<!-- Converted by db4-upgrade version 1.1 -->
<section xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="Clustering_Functions">
    <title>Clustering Functions</title><info>
    <abstract>
    <para>These functions implement clustering algorithms for sets of geometries.</para>
    </abstract>
    </info>


    <refentry xml:id="ST_ClusterDBSCAN">
	  <refnamediv>
		<refname>ST_ClusterDBSCAN</refname>

        <refpurpose>Window function that returns a cluster id for each input geometry using the DBSCAN algorithm.</refpurpose>
    </refnamediv>

	  <refsynopsisdiv>
		<funcsynopsis>
		  <funcprototype>
			<funcdef>integer <function>ST_ClusterDBSCAN</function></funcdef>

			<paramdef><type>geometry winset </type>
			<parameter>geom</parameter></paramdef>

			<paramdef><type>float8 </type>
			<parameter>eps</parameter></paramdef>

			<paramdef><type>integer </type>
			<parameter>minpoints</parameter></paramdef>
		  </funcprototype>
		</funcsynopsis>
	  </refsynopsisdiv>

	  <refsection>
      <title>Description</title>

	  <para>
		  A window function that returns a cluster number for each input geometry, using the 2D
          <link xlink:href="https://en.wikipedia.org/wiki/DBSCAN">Density-based spatial clustering of applications with noise (DBSCAN)</link>
		  algorithm.  Unlike <xref linkend="ST_ClusterKMeans"/>, it does not require the number of clusters to be specified, but instead
		  uses the desired <link linkend="ST_Distance">distance</link> (<varname>eps</varname>) and density (<varname>minpoints</varname>) parameters to determine each cluster.
	  </para>

	  <para>
		  An input geometry is added to a cluster if it is either:
		  <itemizedlist>
              <listitem>
                  <para>
                      A "core" geometry, that is within <varname>eps</varname> <link linkend="ST_Distance">distance</link> of at least <varname>minpoints</varname> input geometries (including itself); or
                  </para>
			  </listitem>
			  <listitem>
                  <para>
                      A "border" geometry, that is within <varname>eps</varname> <link linkend="ST_Distance">distance</link> of a core geometry.
                  </para>
			  </listitem>
		  </itemizedlist>
		</para>

		<para>
		  Note that border geometries may be within <varname>eps</varname> distance of core geometries in more than one cluster.
      Either assignment would be correct, so the border geometry will be arbitrarily assigned to one of the available clusters.
		  In this situation it is possible for a correct cluster to be generated with fewer than <varname>minpoints</varname> geometries.
		  To ensure deterministic assignment of border geometries
      (so that repeated calls to ST_ClusterDBSCAN will produce identical results)
      use an <code>ORDER BY</code> clause in the window definition.
      Ambiguous cluster assignments may differ from other DBSCAN implementations.
	  </para>

	  <note><para>
		  Geometries that do not meet the criteria to join any cluster are assigned a cluster number of NULL.
	  </para></note>

      <para role="availability" conformance="2.3.0">Availability: 2.3.0</para>
      <para>&curve_support;</para>
    </refsection>

    <refsection>
      <title>Examples</title>
	      <para>Cluster polygons within 50 units of each other, requiring at least two polygons per cluster. The isolated polygon is reported as noise.</para>
	      <programlisting language="sql">WITH input(name, geom) AS (
  VALUES
    ('A1', ST_MakeEnvelope(0, 0, 10, 10)),
    ('A2', ST_MakeEnvelope(30, 0, 40, 10)),
    ('B1', ST_MakeEnvelope(100, 0, 110, 10)),
    ('B2', ST_MakeEnvelope(130, 0, 140, 10)),
    ('noise', ST_MakeEnvelope(250, 0, 260, 10))
), clustered AS (
  SELECT name,
         geom,
         ST_ClusterDBSCAN(geom, eps =&gt; 50, minpoints =&gt; 2)
           OVER (ORDER BY name) AS cid
  FROM input
)
SELECT COALESCE(cid::text, 'noise') AS cluster,
       string_agg(name, ',' ORDER BY name) AS members,
       ST_Collect(geom ORDER BY name) AS cluster_geom
FROM clustered
GROUP BY cid
ORDER BY cid NULLS LAST;</programlisting>
	      <screen role="visual-primary text-primary">0 | A1,A2 | MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((30 0,30 10,40 10,40 0,30 0)))
1 | B1,B2 | MULTIPOLYGON(((100 0,100 10,110 10,110 0,100 0)),((130 0,130 10,140 10,140 0,130 0)))
noise | noise | MULTIPOLYGON(((250 0,250 10,260 10,260 0,250 0)))</screen>


        <para>
            A example showing combining parcels with the same cluster number into geometry collections.
        </para>
		    <programlisting language="sql">
SELECT cid, ST_Collect(geom) AS cluster_geom, array_agg(parcel_id) AS ids_in_cluster FROM (
    SELECT parcel_id, ST_ClusterDBSCAN(geom, eps =&gt; 0.5, minpoints =&gt; 5) over () AS cid, geom
    FROM parcels) sq
GROUP BY cid;
    </programlisting>
    </refsection>

    <refsection>
		  <title>See Also</title>
          <para><xref linkend="ST_DWithin"/>,
              <xref linkend="ST_ClusterKMeans"/>,
              <xref linkend="ST_ClusterIntersecting"/>,
              <xref linkend="ST_ClusterIntersectingWin"/>,
              <xref linkend="ST_ClusterWithin"/>,
              <xref linkend="ST_ClusterWithinWin"/>
          </para>
	  </refsection>
    </refentry>


    <refentry xml:id="ST_ClusterIntersecting">
      <refnamediv>
        <refname>ST_ClusterIntersecting</refname>

        <refpurpose>Aggregate function that clusters input geometries into connected sets.</refpurpose>
      </refnamediv>

      <refsynopsisdiv>
        <funcsynopsis>
          <funcprototype>
            <funcdef>geometry[] <function>ST_ClusterIntersecting</function></funcdef>
            <paramdef><type>geometry set</type> <parameter>g</parameter></paramdef>
          </funcprototype>
        </funcsynopsis>
      </refsynopsisdiv>

      <refsection>
        <title>Description</title>

        <para>An aggregate function that returns an array of GeometryCollections
        partitioning the input geometries into connected clusters that are disjoint.
        Each geometry in a cluster intersects at least one other geometry in the cluster,
        and does not intersect any geometry in other clusters.
        </para>

        <para role="availability" conformance="2.2.0">Availability: 2.2.0</para>
      </refsection>

      <refsection>
        <title>Examples</title>
        <programlisting language="sql">
WITH testdata AS
  (SELECT unnest(
           ARRAY['LINESTRING (0 0,1 1)'::geometry,
           'LINESTRING (5 5,4 4)'::geometry,
           'LINESTRING (6 6,7 7)'::geometry,
           'LINESTRING (0 0,-1 -1)'::geometry,
           'POLYGON ((0 0,4 0,4 4,0 4,0 0))'::geometry]) AS geom)

SELECT unnest(ST_ClusterIntersecting(geom)) FROM testdata;
</programlisting>
<screen role="visual-primary text-primary">GEOMETRYCOLLECTION(LINESTRING(0 0,1 1),LINESTRING(5 5,4 4),LINESTRING(0 0,-1 -1),POLYGON((0 0,4 0,4 4,0 4,0 0)))
GEOMETRYCOLLECTION(LINESTRING(6 6,7 7))</screen>
      </refsection>
      <refsection>
        <title>See Also</title>
        <para>
            <xref linkend="ST_ClusterIntersectingWin"/>,
            <xref linkend="ST_ClusterWithin"/>,
            <xref linkend="ST_ClusterWithinWin"/>
        </para>
      </refsection>

    </refentry>


    <refentry xml:id="ST_ClusterIntersectingWin">
      <refnamediv>
        <refname>ST_ClusterIntersectingWin</refname>

        <refpurpose>Window function that returns a cluster id for each input geometry, clustering input geometries into connected sets.</refpurpose>
      </refnamediv>

      <refsynopsisdiv>
        <funcsynopsis>
          <funcprototype>
            <funcdef>integer <function>ST_ClusterIntersectingWin</function></funcdef>
            <paramdef><type>geometry winset </type> <parameter>geom</parameter></paramdef>
          </funcprototype>
        </funcsynopsis>
      </refsynopsisdiv>

      <refsection>
        <title>Description</title>

        <para>A window function that builds connected clusters of geometries that intersect. It is possible to traverse all geometries in a cluster without leaving the cluster. The return value is the cluster number that the geometry argument participates in, or null for null inputs.</para>

        <para role="availability" conformance="3.4.0">Availability: 3.4.0</para>
      </refsection>

      <refsection>
        <title>Examples</title>
        <para>Return an explicit cluster id for each input geometry. Using the conventional <varname>cid</varname> alias keeps the geometry column ready for color-by-cluster rendering.</para>
        <programlisting language="sql">WITH testdata(id, geom) AS (
  VALUES (1, 'LINESTRING (0 0,1 1)'::geometry),
         (2, 'LINESTRING (5 5,4 4)'::geometry),
         (3, 'LINESTRING (6 6,7 7)'::geometry),
         (4, 'LINESTRING (0 0,-1 -1)'::geometry),
         (5, 'POLYGON ((0 0,4 0,4 4,0 4,0 0))'::geometry)
), clustered AS (
  SELECT id,
         geom,
         ST_ClusterIntersectingWin(geom) OVER () AS cid
  FROM testdata
)
SELECT id,
       geom AS geom_wkt,
       cid
FROM clustered
ORDER BY id;</programlisting>
<screen role="visual-primary text-primary"> id |            geom_wkt            | cid
----+--------------------------------+-----
  1 | LINESTRING(0 0,1 1)            |   0
  2 | LINESTRING(5 5,4 4)            |   0
  3 | LINESTRING(6 6,7 7)            |   1
  4 | LINESTRING(0 0,-1 -1)          |   0
  5 | POLYGON((0 0,4 0,4 4,0 4,0 0)) |   0</screen>
      </refsection>
      <refsection>
        <title>See Also</title>
        <para>
            <xref linkend="ST_ClusterIntersecting"/>,
            <xref linkend="ST_ClusterWithin"/>,
            <xref linkend="ST_ClusterWithinWin"/>
        </para>
      </refsection>

    </refentry>



    <refentry xml:id="ST_ClusterRelateWin">
      <refnamediv>
        <refname>ST_ClusterRelateWin</refname>

        <refpurpose>Window function that returns a cluster id for each input geometry, clustering input geometries into connected sets using the relate pattern to determine whether the geometries are connected.</refpurpose>
      </refnamediv>

      <refsynopsisdiv>
        <funcsynopsis>
          <funcprototype>
            <funcdef>integer <function>ST_ClusterRelateWin</function></funcdef>
            <paramdef><type>geometry winset </type> <parameter>geom</parameter></paramdef>
            <paramdef><type>text </type> <parameter>relate_matrix</parameter></paramdef>
          </funcprototype>
        </funcsynopsis>
      </refsynopsisdiv>

      <refsection>
        <title>Description</title>

        <para>A window function that builds connected clusters of geometries that intersect. Geometries are added to a cluster if they share a pairwise DE9IM relationship with another member of the cluster. With this function it is possible to build a cluster of all objects that touch at boundaries, but exclude those that merely overlap.</para>

        <para role="availability" conformance="3.7.0">Availability: 3.7.0</para>
      </refsection>

      <refsection>
        <title>Examples</title>
        <para>This collection of line strings would form a single cluster using <xref linkend="ST_ClusterIntersectingWin"/>, but using <xref linkend="ST_ClusterRelateWin"/> can be clustered into three groups that connect only via their end points.</para>

        <programlisting language="sql">WITH data(id, geom) AS (
  VALUES (1, 'LINESTRING(0 0,1 1)'::geometry),
         (2, 'LINESTRING(2 2,1 1)'::geometry),
         (3, 'LINESTRING(0 1,1 0)'::geometry),
         (4, 'LINESTRING(0 1,0 4)'::geometry),
         (5, 'LINESTRING(2 2,2 4)'::geometry),
         (6, 'LINESTRING(1.5 2.5,2.5 3.5)'::geometry)
), clustered AS (
  SELECT id, geom,
         ST_ClusterRelateWin(geom, '****0****') OVER () AS cluster
  FROM data
)
SELECT cluster,
       string_agg(id::text, ',' ORDER BY id) AS members,
       ST_Collect(geom ORDER BY id) AS cluster_geom
FROM clustered
GROUP BY cluster
ORDER BY cluster;</programlisting>
<screen role="visual-primary">0 | 1,2,5 | MULTILINESTRING((0 0,1 1),(2 2,1 1),(2 2,2 4))
1 | 3,4 | MULTILINESTRING((0 1,1 0),(0 1,0 4))
2 | 6 | MULTILINESTRING((1.5 2.5,2.5 3.5))</screen>
      </refsection>
      <refsection>
        <title>See Also</title>
        <para>
            <xref linkend="ST_Relate"/>,
            <xref linkend="ST_ClusterIntersectingWin"/>,
            <xref linkend="ST_ClusterWithinWin"/>
        </para>
      </refsection>

    </refentry>


    <refentry xml:id="ST_MinimumSpanningTree">
      <refnamediv>
        <refname>ST_MinimumSpanningTree</refname>

        <refpurpose>Window function that returns a tree id for each input geometry, clustering input geometries into connected trees using the Minimum Spanning Tree algorithm.</refpurpose>
      </refnamediv>

      <refsynopsisdiv>
        <funcsynopsis>
          <funcprototype>
            <funcdef>integer <function>ST_MinimumSpanningTree</function></funcdef>
            <paramdef><type>geometry winset </type> <parameter>geom</parameter></paramdef>
          </funcprototype>
        </funcsynopsis>
      </refsynopsisdiv>

      <refsection>
        <title>Description</title>

        <para>A window function that builds connected graphs of line strings based on the Minimum Spanning Tree (MST) of the input geometries. The return value is the cluster number that the geometry argument participates in, or zero if it is not part of the minimum tree.</para>
        <para>The <link xlink:href="https://en.wikipedia.org/wiki/Minimum_spanning_tree">Minimum Spanning Tree</link> connects all geometries in the window partition such that the total length of the connecting lines is minimized. If the graph is not fully connected (e.g. infinite distance between some geometries), it produces a Minimum Spanning Forest, and each connected component is assigned a unique tree ID.</para>

        <para role="availability" conformance="3.7.0">Availability: 3.7.0</para>
        <para role="geos_requirement" conformance="3.15.0">Requires GEOS &gt;= 3.15.0</para>
      </refsection>

      <refsection>
        <title>Examples</title>
        <para>These six edges describe a square with both diagonals. The minimum spanning tree keeps three edges and excludes the rest.</para>
<programlisting language="sql" role="requires-geos-3.15">WITH edges(id, geom) AS (
  VALUES
    (1, 'LINESTRING(0 0,1 0)'::geometry),
    (2, 'LINESTRING(0 0,0 1)'::geometry),
    (3, 'LINESTRING(1 1,0 1)'::geometry),
    (4, 'LINESTRING(1 1,1 0)'::geometry),
    (5, 'LINESTRING(0 0,1 1)'::geometry),
    (6, 'LINESTRING(1 0,0 1)'::geometry)
), marked AS (
  SELECT id,
         geom,
         ST_MinimumSpanningTree(geom) OVER (ORDER BY id) AS tree_id
  FROM edges
)
SELECT string_agg(id::text, ',' ORDER BY id)
         FILTER (WHERE tree_id &gt; 0) AS tree_edge_ids,
       ST_Collect(geom ORDER BY id)
         FILTER (WHERE tree_id &gt; 0) AS tree_edges,
       string_agg(id::text, ',' ORDER BY id)
         FILTER (WHERE tree_id = 0) AS excluded_edge_ids,
       ST_Collect(geom ORDER BY id)
         FILTER (WHERE tree_id = 0) AS excluded_edges
FROM marked;</programlisting>
<screen role="visual-primary visual-overlay text-primary">-[ RECORD 1 ]-----
tree_edge_ids     | 1,2,3
tree_edges        | MULTILINESTRING((0 0,1 0),(0 0,0 1),(1 1,0 1))
excluded_edge_ids | 4,5,6
excluded_edges    | MULTILINESTRING((1 1,1 0),(0 0,1 1),(1 0,0 1))</screen>
      </refsection>
      <refsection>
        <title>See Also</title>
        <para>
            <xref linkend="ST_ClusterDBSCAN"/>,
            <xref linkend="ST_ClusterKMeans"/>,
            <xref linkend="ST_ClusterIntersectingWin"/>
        </para>
      </refsection>

    </refentry>



	<refentry xml:id="ST_ClusterKMeans">
	  <refnamediv>
		<refname>ST_ClusterKMeans</refname>

		<refpurpose>Window function that returns a cluster id for each input geometry using the K-means algorithm.</refpurpose>
	  </refnamediv>

	  <refsynopsisdiv>
		<funcsynopsis>
		  <funcprototype>
            <funcdef>integer <function>ST_ClusterKMeans</function></funcdef>
    
            <paramdef>
              <type>geometry winset </type>
              <parameter>geom</parameter>
            </paramdef>
    
            <paramdef>
              <type>integer </type>
              <parameter>k</parameter>
            </paramdef>
    
            <paramdef>
              <type>float8 </type>
              <parameter>max_radius</parameter>
            </paramdef>
		  </funcprototype>
		</funcsynopsis>
	  </refsynopsisdiv>

	  <refsection>
      <title>Description</title>

      <para>Returns <link xlink:href="https://en.wikipedia.org/wiki/K-means_clustering">K-means</link>
        cluster number for each input geometry. The distance used for clustering is the
        distance between the centroids for 2D geometries, and distance between bounding box centers for 3D geometries.
        For POINT inputs, M coordinate will be treated as weight of input and has to be larger than 0.
      </para>
      <para><varname>max_radius</varname>, if set, will cause ST_ClusterKMeans to generate more clusters than
        <varname>k</varname> ensuring that no cluster in output has radius larger than <varname>max_radius</varname>.
        This is useful in reachability analysis. </para>
      <para role="enhanced" conformance="3.2.0">Enhanced: 3.2.0 Support for <varname>max_radius</varname></para>
      <para role="enhanced" conformance="3.1.0">Enhanced: 3.1.0 Support for 3D geometries and weights</para>
      <para role="availability" conformance="2.3.0">Availability: 2.3.0</para>
    </refsection>

    <refsection>
      <title>Examples</title>
		<para>Define the parcel geometries once in a CTE and reuse them across the executable examples.</para>

        <para>Parcels color-coded by cluster number (<varname>cid</varname>).</para>
<programlisting language="sql" role="example-test">WITH parcels(parcel_id, geom) AS (
  VALUES
    ('A1', ST_MakeEnvelope(0, 0, 10, 10)),
    ('A2', ST_MakeEnvelope(10, 0, 20, 10)),
    ('B1', ST_MakeEnvelope(100, 0, 110, 10)),
    ('B2', ST_MakeEnvelope(110, 0, 120, 10)),
    ('C1', ST_MakeEnvelope(200, 0, 210, 10)),
    ('C2', ST_MakeEnvelope(210, 0, 220, 10))
), clustered AS (
  SELECT parcel_id,
         geom,
         ST_ClusterKMeans(geom, 3) OVER (ORDER BY parcel_id) AS cid
  FROM parcels
)
SELECT cid,
       string_agg(parcel_id, ',' ORDER BY parcel_id) AS parcels,
       ST_Union(geom) AS cluster_geom
FROM clustered
GROUP BY cid
ORDER BY cid;</programlisting>
<screen role="visual-primary visual-overlay text-primary">0 | C1,C2 | POLYGON((200 10,210 10,220 10,220 0,210 0,200 0,200 10))
1 | A1,A2 | POLYGON((0 10,10 10,20 10,20 0,10 0,0 0,0 10))
2 | B1,B2 | POLYGON((100 10,110 10,120 10,120 0,110 0,100 0,100 10))</screen>

        <para>Partitioning parcel clusters by type:</para>
<programlisting language="sql" role="example-test">WITH parcel_geoms AS (
  SELECT geom
  FROM ST_Subdivide(
    ST_Buffer('SRID=3857;LINESTRING(40 100,98 100,100 150,60 90)'::geometry,
              40, 'endcap=square'),
    12) AS geom
), parcels AS (
  SELECT lpad(row_number() OVER (ORDER BY ST_YMin(geom), ST_XMin(geom))::text, 3, '0') AS parcel_id,
         geom,
         ('{residential,commercial}'::text[])[1 + mod(row_number() OVER (ORDER BY ST_YMin(geom), ST_XMin(geom)), 2)] AS type
  FROM parcel_geoms
)
SELECT ST_ClusterKMeans(geom, 3) over (PARTITION BY type) AS cid,
       parcel_id,
       type,
       ST_SnapToGrid(geom, 1) AS parcel_geom
FROM parcels
ORDER BY type, parcel_id;</programlisting>
<screen role="visual-primary text-primary visual-output-only"> cid | parcel_id |    type     | parcel_geom
-----+-----------+-------------+--------------------------------------------------------------
   0 | 001       | commercial  | POLYGON((33 60,0 60,0 98,71 98,71 35,33 60))
   2 | 003       | commercial  | POLYGON((106 98,138 98,137 91,134 84,131 77,126 71,120 66,113 63,106 61,106 98))
   2 | 005       | commercial  | POLYGON((140 148,138 98,72 98,72 148,140 148))
   1 | 007       | commercial  | POLYGON((109 189,116 187,123 183,129 178,134 171,137 164,139 156,140 148,109 148,109 189))
   0 | 002       | residential | POLYGON((71 98,106 98,106 61,98 60,88 60,71 35,71 98))
   2 | 004       | residential | POLYGON((0 98,0 140,45 140,67 172,72 178,72 98,0 98))
   1 | 006       | residential | POLYGON((72 178,78 183,85 187,93 189,101 190,109 189,109 148,72 148,72 178))
</screen>

<para>Example: Clustering a preaggregated planetary-scale data population dataset
using 3D clustering and weighting. Identify at least 20 regions based on
<link xlink:href="https://data.humdata.org/dataset/kontur-population-dataset">Kontur Population Data</link>
that do not span more than 3000 km from their center:</para>
<programlisting language="sql" role="requires-external-state">CREATE TABLE kontur_population_3000km_clusters AS
SELECT
    geom,
    ST_ClusterKMeans(
        ST_Force4D(
            ST_Transform(ST_Force3D(geom), 4978),
            mvalue =&gt; population
        ),
        20,
        max_radius =&gt; 3000000
    ) OVER () AS cid
FROM kontur_population;</programlisting>
<para>The clustering produces 46 regions. Clusters are centered at well-populated
regions such as New York and Moscow. Greenland forms one cluster, several island
clusters span the antimeridian, and cluster edges follow the Earth's curvature.</para>
<informalfigure>
    <mediaobject>
        <imageobject>
            <imagedata fileref="images/st_clusterkmeans03.png"/>
        </imageobject>
        <caption><para>Kontur population clustered with 3000 km maximum radius.</para></caption>
    </mediaobject>
</informalfigure>

	<para>Cluster weighted city populations on the Earth in geocentric coordinates. The maximum radius is specified in meters, while the returned geometries stay in WGS 84 for display.</para>
	<programlisting language="sql" role="example-test">WITH population(place, population, geom) AS (
  VALUES
    ('Boston', 5.0, ST_SetSRID(ST_Point(-71.06, 42.36), 4326)),
    ('New York', 20.0, ST_SetSRID(ST_Point(-74.01, 40.71), 4326)),
    ('London', 15.0, ST_SetSRID(ST_Point(-0.13, 51.51), 4326)),
    ('Paris', 11.0, ST_SetSRID(ST_Point(2.35, 48.86), 4326)),
    ('Osaka', 19.0, ST_SetSRID(ST_Point(135.50, 34.69), 4326)),
    ('Tokyo', 37.0, ST_SetSRID(ST_Point(139.69, 35.68), 4326)),
    ('Melbourne', 5.0, ST_SetSRID(ST_Point(144.96, -37.81), 4326)),
    ('Sydney', 5.0, ST_SetSRID(ST_Point(151.21, -33.87), 4326))
), clustered AS (
  SELECT place,
         geom,
         ST_ClusterKMeans(
           ST_Force4D(
             ST_Transform(ST_Force3D(geom), 4978),
             mvalue =&gt; population
           ),
           4,
           max_radius =&gt; 3000000
         ) OVER (ORDER BY place) AS cid
  FROM population
)
SELECT cid,
       string_agg(place, ', ' ORDER BY place) AS places,
       ST_Collect(geom ORDER BY place) AS cluster_geom
FROM clustered
GROUP BY cid
ORDER BY cid;</programlisting>
	<screen role="visual-primary text-primary visual-output-only">0 | Melbourne, Sydney | MULTIPOINT((144.96 -37.81),(151.21 -33.87))
1 | Boston, New York | MULTIPOINT((-71.06 42.36),(-74.01 40.71))
2 | Osaka, Tokyo | MULTIPOINT((135.5 34.69),(139.69 35.68))
3 | London, Paris | MULTIPOINT((-0.13 51.51),(2.35 48.86))</screen>

    </refsection>

    <refsection>
		  <title>See Also</title>
          <para>
              <xref linkend="ST_ClusterDBSCAN"/>,
              <xref linkend="ST_ClusterIntersectingWin"/>,
              <xref linkend="ST_ClusterWithinWin"/>,
              <xref linkend="ST_ClusterIntersecting"/>,
              <xref linkend="ST_ClusterWithin"/>,
              <xref linkend="ST_Subdivide"/>,
              <xref linkend="ST_Force_3D"/>,
              <xref linkend="ST_Force_4D"/>,
          </para>
	  </refsection>
	</refentry>

	<refentry xml:id="ST_ClusterWithin">
      <refnamediv>
        <refname>ST_ClusterWithin</refname>

        <refpurpose>Aggregate function that clusters geometries by separation distance.</refpurpose>
      </refnamediv>

      <refsynopsisdiv>
        <funcsynopsis>
          <funcprototype>
            <funcdef>geometry[] <function>ST_ClusterWithin</function></funcdef>
            <paramdef><type>geometry set </type> <parameter>g</parameter></paramdef>
            <paramdef><type>float8 </type> <parameter>distance</parameter></paramdef>
          </funcprototype>
        </funcsynopsis>
      </refsynopsisdiv>

      <refsection>
        <title>Description</title>

        <para>An aggregate function that returns an array of GeometryCollections,
        where each collection is a cluster containing some input geometries.
        Clustering partitions the input geometries into sets
        in which each geometry is within the specified <parameter>distance</parameter>
        of at least one other geometry in the same cluster.
        Distances are Cartesian distances in the units of the SRID.
        </para>
        <para>ST_ClusterWithin is equivalent to running <xref linkend="ST_ClusterDBSCAN"/> with <code>minpoints =&gt; 0</code>.</para>

        <para role="availability" conformance="2.2.0">Availability: 2.2.0</para>
        <para>&curve_support;</para>
      </refsection>

      <refsection>
        <title>Examples</title>
        <programlisting language="sql">
WITH testdata AS
  (SELECT unnest(ARRAY['LINESTRING (0 0,1 1)'::geometry,
               'LINESTRING (5 5,4 4)'::geometry,
               'LINESTRING (6 6,7 7)'::geometry,
               'LINESTRING (0 0,-1 -1)'::geometry,
               'POLYGON ((0 0,4 0,4 4,0 4,0 0))'::geometry]) AS geom)

SELECT unnest(ST_ClusterWithin(geom, 1.4)) FROM testdata;
</programlisting>
<screen role="visual-primary text-primary">GEOMETRYCOLLECTION(LINESTRING(0 0,1 1),LINESTRING(5 5,4 4),LINESTRING(0 0,-1 -1),POLYGON((0 0,4 0,4 4,0 4,0 0)))
GEOMETRYCOLLECTION(LINESTRING(6 6,7 7))</screen>
      </refsection>
      <refsection>
        <title>See Also</title>
        <para>
          <xref linkend="ST_ClusterWithinWin"/>,
          <xref linkend="ST_ClusterDBSCAN"/>,
          <xref linkend="ST_ClusterIntersecting"/>,
          <xref linkend="ST_ClusterIntersectingWin"/>
        </para>
      </refsection>

    </refentry>

  <refentry xml:id="ST_ClusterWithinWin">
      <refnamediv>
        <refname>ST_ClusterWithinWin</refname>

        <refpurpose>Window function that returns a cluster id for each input geometry, clustering using separation distance.</refpurpose>
      </refnamediv>

      <refsynopsisdiv>
        <funcsynopsis>
          <funcprototype>
            <funcdef>integer <function>ST_ClusterWithinWin</function></funcdef>
            <paramdef><type>geometry winset </type> <parameter>geom</parameter></paramdef>
            <paramdef><type>float8 </type> <parameter>distance</parameter></paramdef>
          </funcprototype>
        </funcsynopsis>
      </refsynopsisdiv>

      <refsection>
        <title>Description</title>

        <para>A window function that returns a cluster number for each input geometry.
        Clustering partitions the geometries into sets
        in which each geometry is within the specified <varname>distance</varname>
        of at least one other geometry in the same cluster.
        Distances are Cartesian distances in the units of the SRID.
        </para>
        <para>ST_ClusterWithinWin is equivalent to running <xref linkend="ST_ClusterDBSCAN"/> with <code>minpoints =&gt; 0</code>.</para>
        <para role="availability" conformance="3.4.0">Availability: 3.4.0</para>
        <para>&curve_support;</para>

      </refsection>

      <refsection>
        <title>Examples</title>
        <programlisting language="sql">
WITH testdata AS (
  SELECT id, geom::geometry FROM (
  VALUES  (1, 'LINESTRING (0 0,1 1)'),
          (2, 'LINESTRING (5 5,4 4)'),
          (3, 'LINESTRING (6 6,7 7)'),
          (4, 'LINESTRING (0 0,-1 -1)'),
          (5, 'POLYGON ((0 0,4 0,4 4,0 4,0 0))')) AS t(id, geom)
)
SELECT id,
  geom,
  ST_ClusterWithinWin(geom, 1.4) OVER () AS cluster
FROM testdata;</programlisting>
<screen role="visual-primary text-primary"> id |           st_astext            | cluster
----+--------------------------------+---------
  1 | LINESTRING(0 0,1 1)            |       0
  2 | LINESTRING(5 5,4 4)            |       0
  3 | LINESTRING(6 6,7 7)            |       1
  4 | LINESTRING(0 0,-1 -1)          |       0
  5 | POLYGON((0 0,4 0,4 4,0 4,0 0)) |       0</screen>
      </refsection>
      <refsection>
        <title>See Also</title>
        <para>
          <xref linkend="ST_ClusterWithin"/>,
          <xref linkend="ST_ClusterDBSCAN"/>,
          <xref linkend="ST_ClusterIntersecting"/>,
          <xref linkend="ST_ClusterIntersectingWin"/>,
        </para>
      </refsection>

    </refentry>

</section>
