pgLatLon

changeset 16:e319679cefbd

Added more operators for clusters (including polygons); Bugfix regarding missing entries in ecluster_ops operator class (index was not used for nearest-neighbor searches)
author jbe
date Fri Sep 09 19:22:30 2016 +0200 (2016-09-09)
parents 95f185a648a4
children c790cf162e04
files GNUmakefile README.html README.mkd latlon--0.1--0.2.sql latlon--0.2--0.3.sql latlon--0.3--0.4.sql latlon--0.3.sql latlon--0.4.sql latlon-v0003.c latlon-v0004.c latlon.control
line diff
     1.1 --- a/GNUmakefile	Sat Sep 03 16:00:22 2016 +0200
     1.2 +++ b/GNUmakefile	Fri Sep 09 19:22:30 2016 +0200
     1.3 @@ -1,6 +1,6 @@
     1.4  EXTENSION = latlon
     1.5 -DATA = latlon--0.1--0.2.sql latlon--0.2--0.3.sql latlon--0.3.sql latlon--0.4.sql
     1.6 -MODULES = latlon-v0003 latlon-v0004
     1.7 +DATA = latlon--0.3--0.4.sql latlon--0.4.sql
     1.8 +MODULES = latlon-v0004
     1.9  
    1.10  PG_CONFIG = pg_config
    1.11  PGXS := $(shell $(PG_CONFIG) --pgxs)
     2.1 --- a/README.html	Sat Sep 03 16:00:22 2016 +0200
     2.2 +++ b/README.html	Fri Sep 09 19:22:30 2016 +0200
     2.3 @@ -1,5 +1,5 @@
     2.4 -<html><head><title>pgLatLon v0.3 documentation</title></head><body>
     2.5 -<h1>pgLatLon v0.3 documentation</h1>
     2.6 +<html><head><title>pgLatLon v0.4 documentation</title></head><body>
     2.7 +<h1>pgLatLon v0.4 documentation</h1>
     2.8  
     2.9  <p>pgLatLon is a spatial database extension for the PostgreSQL object-relational
    2.10  database management system providing geographic data types and spatial indexing
    2.11 @@ -59,6 +59,39 @@
    2.12  test_database=# CREATE EXTENSION latlon;
    2.13  </code></pre>
    2.14  
    2.15 +<h3>Updating</h3>
    2.16 +
    2.17 +<p>Before updating your database cluster to a new version of pgLatLon, you may
    2.18 +want to uninstall the old by calling "<code>make uninstall</code>" in the unpacked source
    2.19 +code directory of your old pgLatLon version. You may also manually delete the
    2.20 +<code>latlon-v????.so</code> files from your PostgreSQL library directory and the
    2.21 +<code>latlon.control</code> and <code>latlon--*.sql</code> files from your PostgreSQL extension
    2.22 +directory.</p>
    2.23 +
    2.24 +<p>The new version can be installed as described above. For altering an existing
    2.25 +database to use the installed new version (mandatory if you removed the old
    2.26 +version), execute the following SQL command in the respective databases:</p>
    2.27 +
    2.28 +<pre><code>ALTER EXTENSION latlon UPDATE;
    2.29 +</code></pre>
    2.30 +
    2.31 +<p>If the update contains modifications to operator classes, it may be necessary
    2.32 +to drop all indices on geographic data types first (you will get an error
    2.33 +message in this case). These indices can be re-created after the update.</p>
    2.34 +
    2.35 +<p>Note that taking several update steps at once (e.g. updating from version 0.2
    2.36 +directly to version 0.4) requires the intermediate versions to be installed
    2.37 +(i.e. in this example version 0.3 would need to be installed). Whenever you
    2.38 +install or uninstall an intermediate or old version, make sure to afterwards
    2.39 +re-install the latest pgLatLon version to ensure that the <code>latlon.control</code> file
    2.40 +is available and points to the latest version.</p>
    2.41 +
    2.42 +<p>If the update contains modifications to the internal data representation
    2.43 +format, an update path might not be available. In this case, create a dump of
    2.44 +your database, delete your database, and restore it from your dump.</p>
    2.45 +
    2.46 +<p>Be sure to always keep backups of all your data before attempting to update.</p>
    2.47 +
    2.48  <h2>Reference</h2>
    2.49  
    2.50  <h3>1. Types</h3>
    2.51 @@ -245,8 +278,11 @@
    2.52  <li><code>epoint &amp;&amp; ecircle</code></li>
    2.53  <li><code>epoint &amp;&amp; ecluster</code></li>
    2.54  <li><code>ebox &amp;&amp; ebox</code></li>
    2.55 +<li><code>ebox &amp;&amp; ecircle</code></li>
    2.56 +<li><code>ebox &amp;&amp; ecluster</code></li>
    2.57  <li><code>ecircle &amp;&amp; ecircle</code></li>
    2.58  <li><code>ecircle &amp;&amp; ecluster</code></li>
    2.59 +<li><code>ecluster &amp;&amp; ecluster</code></li>
    2.60  </ul>
    2.61  
    2.62  <p>The <code>&amp;&amp;</code> operator is commutative, i.e. <code>a &amp;&amp; b</code> is the same as <code>b &amp;&amp; a</code>. Each
    2.63 @@ -266,12 +302,28 @@
    2.64  <li><code>ecluster &amp;&amp;+ ecluster</code></li>
    2.65  </ul>
    2.66  
    2.67 -<p>The <code>&amp;&amp;+</code> operator is commutative, i.e. <code>a &amp;&amp;+ b</code> is the same as <code>b &amp;&amp;+ a</code>. Each
    2.68 -commutation is supported as well.</p>
    2.69 +<p>The <code>&amp;&amp;+</code> operator is commutative, i.e. <code>a &amp;&amp;+ b</code> is the same as <code>b &amp;&amp;+ a</code>.
    2.70 +Each commutation is supported as well.</p>
    2.71  
    2.72  <p>Where two data types support both the <code>&amp;&amp;</code> and the <code>&amp;&amp;+</code> operator, the <code>&amp;&amp;+</code>
    2.73  operator computes faster.</p>
    2.74  
    2.75 +<h4>Contains operator <code>@&gt;</code></h4>
    2.76 +
    2.77 +<p>Tests if the object right of the operator is contained in the object left of
    2.78 +the operator. Currently implemented for:</p>
    2.79 +
    2.80 +<ul>
    2.81 +<li><code>ebox @&gt; epoint</code> (alias for <code>&amp;&amp;</code>)</li>
    2.82 +<li><code>ebox @&gt; ecluster</code></li>
    2.83 +<li><code>ecluster @&gt; epoint</code> (alias for <code>&amp;&amp;</code>)</li>
    2.84 +<li><code>ecluster @&gt; ebox</code></li>
    2.85 +<li><code>ecluster @&gt; ecluster</code></li>
    2.86 +</ul>
    2.87 +
    2.88 +<p>The commutator of <code>@&gt;</code> ("contains") is <code>&lt;@</code> ("is contained in"), i.e. <code>a @&gt; b</code>
    2.89 +is the same as <code>b &lt;@ a</code>.</p>
    2.90 +
    2.91  <h4>Distance operator <code>&lt;-&gt;</code></h4>
    2.92  
    2.93  <p>Calculates the shortest distance between two geographic objects in meters (zero
    2.94 @@ -279,10 +331,15 @@
    2.95  
    2.96  <ul>
    2.97  <li><code>epoint &lt;-&gt; epoint</code></li>
    2.98 +<li><code>epoint &lt;-&gt; ebox</code></li>
    2.99  <li><code>epoint &lt;-&gt; ecircle</code></li>
   2.100  <li><code>epoint &lt;-&gt; ecluster</code></li>
   2.101 +<li><code>ebox &lt;-&gt; ebox</code></li>
   2.102 +<li><code>ebox &lt;-&gt; ecircle</code></li>
   2.103 +<li><code>ebox &lt;-&gt; ecluster</code></li>
   2.104  <li><code>ecircle &lt;-&gt; ecircle</code></li>
   2.105  <li><code>ecircle &lt;-&gt; ecluster</code></li>
   2.106 +<li><code>ecluster &lt;-&gt; ecluster</code></li>
   2.107  </ul>
   2.108  
   2.109  <p>The <code>&lt;-&gt;</code> operator is commutative, i.e. <code>a &lt;-&gt; b</code> is the same as <code>b &lt;-&gt; a</code>.
     3.1 --- a/README.mkd	Sat Sep 03 16:00:22 2016 +0200
     3.2 +++ b/README.mkd	Fri Sep 09 19:22:30 2016 +0200
     3.3 @@ -1,4 +1,4 @@
     3.4 -pgLatLon v0.3 documentation
     3.5 +pgLatLon v0.4 documentation
     3.6  ===========================
     3.7  
     3.8  pgLatLon is a spatial database extension for the PostgreSQL object-relational
     3.9 @@ -56,6 +56,38 @@
    3.10  
    3.11      test_database=# CREATE EXTENSION latlon;
    3.12  
    3.13 +### Updating
    3.14 +
    3.15 +Before updating your database cluster to a new version of pgLatLon, you may
    3.16 +want to uninstall the old by calling "`make uninstall`" in the unpacked source
    3.17 +code directory of your old pgLatLon version. You may also manually delete the
    3.18 +`latlon-v????.so` files from your PostgreSQL library directory and the
    3.19 +`latlon.control` and `latlon--*.sql` files from your PostgreSQL extension
    3.20 +directory.
    3.21 +
    3.22 +The new version can be installed as described above. For altering an existing
    3.23 +database to use the installed new version (mandatory if you removed the old
    3.24 +version), execute the following SQL command in the respective databases:
    3.25 +
    3.26 +    ALTER EXTENSION latlon UPDATE;
    3.27 +
    3.28 +If the update contains modifications to operator classes, it may be necessary
    3.29 +to drop all indices on geographic data types first (you will get an error
    3.30 +message in this case). These indices can be re-created after the update.
    3.31 +
    3.32 +Note that taking several update steps at once (e.g. updating from version 0.2
    3.33 +directly to version 0.4) requires the intermediate versions to be installed
    3.34 +(i.e. in this example version 0.3 would need to be installed). Whenever you
    3.35 +install or uninstall an intermediate or old version, make sure to afterwards
    3.36 +re-install the latest pgLatLon version to ensure that the `latlon.control` file
    3.37 +is available and points to the latest version.
    3.38 +
    3.39 +If the update contains modifications to the internal data representation
    3.40 +format, an update path might not be available. In this case, create a dump of
    3.41 +your database, delete your database, and restore it from your dump.
    3.42 +
    3.43 +Be sure to always keep backups of all your data before attempting to update.
    3.44 +
    3.45  
    3.46  Reference
    3.47  ---------
    3.48 @@ -236,8 +268,11 @@
    3.49  * `epoint && ecircle`
    3.50  * `epoint && ecluster`
    3.51  * `ebox && ebox`
    3.52 +* `ebox && ecircle`
    3.53 +* `ebox && ecluster`
    3.54  * `ecircle && ecircle`
    3.55  * `ecircle && ecluster`
    3.56 +* `ecluster && ecluster`
    3.57  
    3.58  The `&&` operator is commutative, i.e. `a && b` is the same as `b && a`. Each
    3.59  commutation is supported as well.
    3.60 @@ -254,22 +289,41 @@
    3.61  * `ecircle &&+ ecluster`
    3.62  * `ecluster &&+ ecluster`
    3.63  
    3.64 -The `&&+` operator is commutative, i.e. `a &&+ b` is the same as `b &&+ a`. Each
    3.65 -commutation is supported as well.
    3.66 +The `&&+` operator is commutative, i.e. `a &&+ b` is the same as `b &&+ a`.
    3.67 +Each commutation is supported as well.
    3.68  
    3.69  Where two data types support both the `&&` and the `&&+` operator, the `&&+`
    3.70  operator computes faster.
    3.71  
    3.72 +#### Contains operator `@>`
    3.73 +
    3.74 +Tests if the object right of the operator is contained in the object left of
    3.75 +the operator. Currently implemented for:
    3.76 +
    3.77 +* `ebox @> epoint` (alias for `&&`)
    3.78 +* `ebox @> ecluster`
    3.79 +* `ecluster @> epoint` (alias for `&&`)
    3.80 +* `ecluster @> ebox`
    3.81 +* `ecluster @> ecluster`
    3.82 +
    3.83 +The commutator of `@>` ("contains") is `<@` ("is contained in"), i.e. `a @> b`
    3.84 +is the same as `b <@ a`.
    3.85 +
    3.86  #### Distance operator `<->`
    3.87  
    3.88  Calculates the shortest distance between two geographic objects in meters (zero
    3.89  if the objects are overlapping). Currently implemented for:
    3.90  
    3.91  * `epoint <-> epoint`
    3.92 +* `epoint <-> ebox`
    3.93  * `epoint <-> ecircle`
    3.94  * `epoint <-> ecluster`
    3.95 +* `ebox <-> ebox`
    3.96 +* `ebox <-> ecircle`
    3.97 +* `ebox <-> ecluster`
    3.98  * `ecircle <-> ecircle`
    3.99  * `ecircle <-> ecluster`
   3.100 +* `ecluster <-> ecluster`
   3.101  
   3.102  The `<->` operator is commutative, i.e. `a <-> b` is the same as `b <-> a`.
   3.103  Each commutation is supported as well.
     4.1 --- a/latlon--0.1--0.2.sql	Sat Sep 03 16:00:22 2016 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,391 +0,0 @@
     4.4 -
     4.5 -CREATE OR REPLACE FUNCTION ekey_point_in_dummy(cstring)
     4.6 -  RETURNS ekey_point
     4.7 -  LANGUAGE C IMMUTABLE STRICT
     4.8 -  AS '$libdir/latlon-v0002', 'pgl_notimpl';
     4.9 -
    4.10 -CREATE OR REPLACE FUNCTION ekey_point_out_dummy(ekey_point)
    4.11 -  RETURNS cstring
    4.12 -  LANGUAGE C IMMUTABLE STRICT
    4.13 -  AS '$libdir/latlon-v0002', 'pgl_notimpl';
    4.14 -
    4.15 -CREATE OR REPLACE FUNCTION ekey_area_in_dummy(cstring)
    4.16 -  RETURNS ekey_area
    4.17 -  LANGUAGE C IMMUTABLE STRICT
    4.18 -  AS '$libdir/latlon-v0002', 'pgl_notimpl';
    4.19 -
    4.20 -CREATE OR REPLACE FUNCTION ekey_area_out_dummy(ekey_area)
    4.21 -  RETURNS cstring
    4.22 -  LANGUAGE C IMMUTABLE STRICT
    4.23 -  AS '$libdir/latlon-v0002', 'pgl_notimpl';
    4.24 -
    4.25 -CREATE OR REPLACE FUNCTION epoint_in(cstring)
    4.26 -  RETURNS epoint
    4.27 -  LANGUAGE C IMMUTABLE STRICT
    4.28 -  AS '$libdir/latlon-v0002', 'pgl_epoint_in';
    4.29 -
    4.30 -CREATE OR REPLACE FUNCTION ebox_in(cstring)
    4.31 -  RETURNS ebox
    4.32 -  LANGUAGE C IMMUTABLE STRICT
    4.33 -  AS '$libdir/latlon-v0002', 'pgl_ebox_in';
    4.34 -
    4.35 -CREATE OR REPLACE FUNCTION ecircle_in(cstring)
    4.36 -  RETURNS ecircle
    4.37 -  LANGUAGE C IMMUTABLE STRICT
    4.38 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_in';
    4.39 -
    4.40 -CREATE OR REPLACE FUNCTION ecluster_in(cstring)
    4.41 -  RETURNS ecluster
    4.42 -  LANGUAGE C IMMUTABLE STRICT
    4.43 -  AS '$libdir/latlon-v0002', 'pgl_ecluster_in';
    4.44 -
    4.45 -CREATE OR REPLACE FUNCTION epoint_out(epoint)
    4.46 -  RETURNS cstring
    4.47 -  LANGUAGE C IMMUTABLE STRICT
    4.48 -  AS '$libdir/latlon-v0002', 'pgl_epoint_out';
    4.49 -
    4.50 -CREATE OR REPLACE FUNCTION ebox_out(ebox)
    4.51 -  RETURNS cstring
    4.52 -  LANGUAGE C IMMUTABLE STRICT
    4.53 -  AS '$libdir/latlon-v0002', 'pgl_ebox_out';
    4.54 -
    4.55 -CREATE OR REPLACE FUNCTION ecircle_out(ecircle)
    4.56 -  RETURNS cstring
    4.57 -  LANGUAGE C IMMUTABLE STRICT
    4.58 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_out';
    4.59 -
    4.60 -CREATE OR REPLACE FUNCTION ecluster_out(ecluster)
    4.61 -  RETURNS cstring
    4.62 -  LANGUAGE C IMMUTABLE STRICT
    4.63 -  AS '$libdir/latlon-v0002', 'pgl_ecluster_out';
    4.64 -
    4.65 -CREATE OR REPLACE FUNCTION epoint_recv(internal)
    4.66 -  RETURNS epoint
    4.67 -  LANGUAGE C IMMUTABLE STRICT
    4.68 -  AS '$libdir/latlon-v0002', 'pgl_epoint_recv';
    4.69 -
    4.70 -CREATE OR REPLACE FUNCTION ebox_recv(internal)
    4.71 -  RETURNS ebox
    4.72 -  LANGUAGE C IMMUTABLE STRICT
    4.73 -  AS '$libdir/latlon-v0002', 'pgl_ebox_recv';
    4.74 -
    4.75 -CREATE OR REPLACE FUNCTION ecircle_recv(internal)
    4.76 -  RETURNS ecircle
    4.77 -  LANGUAGE C IMMUTABLE STRICT
    4.78 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_recv';
    4.79 -
    4.80 -CREATE OR REPLACE FUNCTION epoint_send(epoint)
    4.81 -  RETURNS bytea
    4.82 -  LANGUAGE C IMMUTABLE STRICT
    4.83 -  AS '$libdir/latlon-v0002', 'pgl_epoint_send';
    4.84 -
    4.85 -CREATE OR REPLACE FUNCTION ebox_send(ebox)
    4.86 -  RETURNS bytea
    4.87 -  LANGUAGE C IMMUTABLE STRICT
    4.88 -  AS '$libdir/latlon-v0002', 'pgl_ebox_send';
    4.89 -
    4.90 -CREATE OR REPLACE FUNCTION ecircle_send(ecircle)
    4.91 -  RETURNS bytea
    4.92 -  LANGUAGE C IMMUTABLE STRICT
    4.93 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_send';
    4.94 -
    4.95 -CREATE OR REPLACE FUNCTION epoint_btree_lt(epoint, epoint)
    4.96 -  RETURNS boolean
    4.97 -  LANGUAGE C IMMUTABLE STRICT
    4.98 -  AS '$libdir/latlon-v0002', 'pgl_btree_epoint_lt';
    4.99 -
   4.100 -CREATE OR REPLACE FUNCTION epoint_btree_le(epoint, epoint)
   4.101 -  RETURNS boolean
   4.102 -  LANGUAGE C IMMUTABLE STRICT
   4.103 -  AS '$libdir/latlon-v0002', 'pgl_btree_epoint_le';
   4.104 -
   4.105 -CREATE OR REPLACE FUNCTION epoint_btree_eq(epoint, epoint)
   4.106 -  RETURNS boolean
   4.107 -  LANGUAGE C IMMUTABLE STRICT
   4.108 -  AS '$libdir/latlon-v0002', 'pgl_btree_epoint_eq';
   4.109 -
   4.110 -CREATE OR REPLACE FUNCTION epoint_btree_ne(epoint, epoint)
   4.111 -  RETURNS boolean
   4.112 -  LANGUAGE C IMMUTABLE STRICT
   4.113 -  AS '$libdir/latlon-v0002', 'pgl_btree_epoint_ne';
   4.114 -
   4.115 -CREATE OR REPLACE FUNCTION epoint_btree_ge(epoint, epoint)
   4.116 -  RETURNS boolean
   4.117 -  LANGUAGE C IMMUTABLE STRICT
   4.118 -  AS '$libdir/latlon-v0002', 'pgl_btree_epoint_ge';
   4.119 -
   4.120 -CREATE OR REPLACE FUNCTION epoint_btree_gt(epoint, epoint)
   4.121 -  RETURNS boolean
   4.122 -  LANGUAGE C IMMUTABLE STRICT
   4.123 -  AS '$libdir/latlon-v0002', 'pgl_btree_epoint_gt';
   4.124 -
   4.125 -CREATE OR REPLACE FUNCTION epoint_btree_cmp(epoint, epoint)
   4.126 -  RETURNS int4
   4.127 -  LANGUAGE C IMMUTABLE STRICT
   4.128 -  AS '$libdir/latlon-v0002', 'pgl_btree_epoint_cmp';
   4.129 -
   4.130 -CREATE OR REPLACE FUNCTION ebox_btree_lt(ebox, ebox)
   4.131 -  RETURNS boolean
   4.132 -  LANGUAGE C IMMUTABLE STRICT
   4.133 -  AS '$libdir/latlon-v0002', 'pgl_btree_ebox_lt';
   4.134 -
   4.135 -CREATE OR REPLACE FUNCTION ebox_btree_le(ebox, ebox)
   4.136 -  RETURNS boolean
   4.137 -  LANGUAGE C IMMUTABLE STRICT
   4.138 -  AS '$libdir/latlon-v0002', 'pgl_btree_ebox_le';
   4.139 -
   4.140 -CREATE OR REPLACE FUNCTION ebox_btree_eq(ebox, ebox)
   4.141 -  RETURNS boolean
   4.142 -  LANGUAGE C IMMUTABLE STRICT
   4.143 -  AS '$libdir/latlon-v0002', 'pgl_btree_ebox_eq';
   4.144 -
   4.145 -CREATE OR REPLACE FUNCTION ebox_btree_ne(ebox, ebox)
   4.146 -  RETURNS boolean
   4.147 -  LANGUAGE C IMMUTABLE STRICT
   4.148 -  AS '$libdir/latlon-v0002', 'pgl_btree_ebox_ne';
   4.149 -
   4.150 -CREATE OR REPLACE FUNCTION ebox_btree_ge(ebox, ebox)
   4.151 -  RETURNS boolean
   4.152 -  LANGUAGE C IMMUTABLE STRICT
   4.153 -  AS '$libdir/latlon-v0002', 'pgl_btree_ebox_ge';
   4.154 -
   4.155 -CREATE OR REPLACE FUNCTION ebox_btree_gt(ebox, ebox)
   4.156 -  RETURNS boolean
   4.157 -  LANGUAGE C IMMUTABLE STRICT
   4.158 -  AS '$libdir/latlon-v0002', 'pgl_btree_ebox_gt';
   4.159 -
   4.160 -CREATE OR REPLACE FUNCTION ebox_btree_cmp(ebox, ebox)
   4.161 -  RETURNS int4
   4.162 -  LANGUAGE C IMMUTABLE STRICT
   4.163 -  AS '$libdir/latlon-v0002', 'pgl_btree_ebox_cmp';
   4.164 -
   4.165 -CREATE OR REPLACE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   4.166 -  RETURNS boolean
   4.167 -  LANGUAGE C IMMUTABLE STRICT
   4.168 -  AS '$libdir/latlon-v0002', 'pgl_btree_ecircle_lt';
   4.169 -
   4.170 -CREATE OR REPLACE FUNCTION ecircle_btree_le(ecircle, ecircle)
   4.171 -  RETURNS boolean
   4.172 -  LANGUAGE C IMMUTABLE STRICT
   4.173 -  AS '$libdir/latlon-v0002', 'pgl_btree_ecircle_le';
   4.174 -
   4.175 -CREATE OR REPLACE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   4.176 -  RETURNS boolean
   4.177 -  LANGUAGE C IMMUTABLE STRICT
   4.178 -  AS '$libdir/latlon-v0002', 'pgl_btree_ecircle_eq';
   4.179 -
   4.180 -CREATE OR REPLACE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   4.181 -  RETURNS boolean
   4.182 -  LANGUAGE C IMMUTABLE STRICT
   4.183 -  AS '$libdir/latlon-v0002', 'pgl_btree_ecircle_ne';
   4.184 -
   4.185 -CREATE OR REPLACE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   4.186 -  RETURNS boolean
   4.187 -  LANGUAGE C IMMUTABLE STRICT
   4.188 -  AS '$libdir/latlon-v0002', 'pgl_btree_ecircle_ge';
   4.189 -
   4.190 -CREATE OR REPLACE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   4.191 -  RETURNS boolean
   4.192 -  LANGUAGE C IMMUTABLE STRICT
   4.193 -  AS '$libdir/latlon-v0002', 'pgl_btree_ecircle_gt';
   4.194 -
   4.195 -CREATE OR REPLACE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   4.196 -  RETURNS int4
   4.197 -  LANGUAGE C IMMUTABLE STRICT
   4.198 -  AS '$libdir/latlon-v0002', 'pgl_btree_ecircle_cmp';
   4.199 -
   4.200 -CREATE OR REPLACE FUNCTION cast_epoint_to_ebox(epoint)
   4.201 -  RETURNS ebox
   4.202 -  LANGUAGE C IMMUTABLE STRICT
   4.203 -  AS '$libdir/latlon-v0002', 'pgl_epoint_to_ebox';
   4.204 -
   4.205 -CREATE OR REPLACE FUNCTION cast_epoint_to_ecircle(epoint)
   4.206 -  RETURNS ecircle
   4.207 -  LANGUAGE C IMMUTABLE STRICT
   4.208 -  AS '$libdir/latlon-v0002', 'pgl_epoint_to_ecircle';
   4.209 -
   4.210 -CREATE OR REPLACE FUNCTION cast_epoint_to_ecluster(epoint)
   4.211 -  RETURNS ecluster
   4.212 -  LANGUAGE C IMMUTABLE STRICT
   4.213 -  AS '$libdir/latlon-v0002', 'pgl_epoint_to_ecluster';
   4.214 -
   4.215 -CREATE OR REPLACE FUNCTION cast_ebox_to_ecluster(ebox)
   4.216 -  RETURNS ecluster
   4.217 -  LANGUAGE C IMMUTABLE STRICT
   4.218 -  AS '$libdir/latlon-v0002', 'pgl_ebox_to_ecluster';
   4.219 -
   4.220 -CREATE OR REPLACE FUNCTION epoint(float8, float8)
   4.221 -  RETURNS epoint
   4.222 -  LANGUAGE C IMMUTABLE STRICT
   4.223 -  AS '$libdir/latlon-v0002', 'pgl_create_epoint';
   4.224 -
   4.225 -CREATE OR REPLACE FUNCTION empty_ebox()
   4.226 -  RETURNS ebox
   4.227 -  LANGUAGE C IMMUTABLE STRICT
   4.228 -  AS '$libdir/latlon-v0002', 'pgl_create_empty_ebox';
   4.229 -
   4.230 -CREATE OR REPLACE FUNCTION ebox(float8, float8, float8, float8)
   4.231 -  RETURNS ebox
   4.232 -  LANGUAGE C IMMUTABLE STRICT
   4.233 -  AS '$libdir/latlon-v0002', 'pgl_create_ebox';
   4.234 -
   4.235 -CREATE OR REPLACE FUNCTION ebox(epoint, epoint)
   4.236 -  RETURNS ebox
   4.237 -  LANGUAGE C IMMUTABLE STRICT
   4.238 -  AS '$libdir/latlon-v0002', 'pgl_create_ebox_from_epoints';
   4.239 -
   4.240 -CREATE OR REPLACE FUNCTION ecircle(float8, float8, float8)
   4.241 -  RETURNS ecircle
   4.242 -  LANGUAGE C IMMUTABLE STRICT
   4.243 -  AS '$libdir/latlon-v0002', 'pgl_create_ecircle';
   4.244 -
   4.245 -CREATE OR REPLACE FUNCTION ecircle(epoint, float8)
   4.246 -  RETURNS ecircle
   4.247 -  LANGUAGE C IMMUTABLE STRICT
   4.248 -  AS '$libdir/latlon-v0002', 'pgl_create_ecircle_from_epoint';
   4.249 -
   4.250 -CREATE OR REPLACE FUNCTION latitude(epoint)
   4.251 -  RETURNS float8
   4.252 -  LANGUAGE C IMMUTABLE STRICT
   4.253 -  AS '$libdir/latlon-v0002', 'pgl_epoint_lat';
   4.254 -
   4.255 -CREATE OR REPLACE FUNCTION longitude(epoint)
   4.256 -  RETURNS float8
   4.257 -  LANGUAGE C IMMUTABLE STRICT
   4.258 -  AS '$libdir/latlon-v0002', 'pgl_epoint_lon';
   4.259 -
   4.260 -CREATE OR REPLACE FUNCTION min_latitude(ebox)
   4.261 -  RETURNS float8
   4.262 -  LANGUAGE C IMMUTABLE STRICT
   4.263 -  AS '$libdir/latlon-v0002', 'pgl_ebox_lat_min';
   4.264 -
   4.265 -CREATE OR REPLACE FUNCTION max_latitude(ebox)
   4.266 -  RETURNS float8
   4.267 -  LANGUAGE C IMMUTABLE STRICT
   4.268 -  AS '$libdir/latlon-v0002', 'pgl_ebox_lat_max';
   4.269 -
   4.270 -CREATE OR REPLACE FUNCTION min_longitude(ebox)
   4.271 -  RETURNS float8
   4.272 -  LANGUAGE C IMMUTABLE STRICT
   4.273 -  AS '$libdir/latlon-v0002', 'pgl_ebox_lon_min';
   4.274 -
   4.275 -CREATE OR REPLACE FUNCTION max_longitude(ebox)
   4.276 -  RETURNS float8
   4.277 -  LANGUAGE C IMMUTABLE STRICT
   4.278 -  AS '$libdir/latlon-v0002', 'pgl_ebox_lon_max';
   4.279 -
   4.280 -CREATE OR REPLACE FUNCTION center(ecircle)
   4.281 -  RETURNS epoint
   4.282 -  LANGUAGE C IMMUTABLE STRICT
   4.283 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_center';
   4.284 -
   4.285 -CREATE OR REPLACE FUNCTION radius(ecircle)
   4.286 -  RETURNS float8
   4.287 -  LANGUAGE C IMMUTABLE STRICT
   4.288 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_radius';
   4.289 -
   4.290 -CREATE OR REPLACE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   4.291 -  RETURNS boolean
   4.292 -  LANGUAGE C IMMUTABLE STRICT
   4.293 -  AS '$libdir/latlon-v0002', 'pgl_epoint_ebox_overlap';
   4.294 -
   4.295 -CREATE OR REPLACE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   4.296 -  RETURNS boolean
   4.297 -  LANGUAGE C IMMUTABLE STRICT
   4.298 -  AS '$libdir/latlon-v0002', 'pgl_epoint_ecircle_overlap';
   4.299 -
   4.300 -CREATE OR REPLACE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   4.301 -  RETURNS boolean
   4.302 -  LANGUAGE C IMMUTABLE STRICT
   4.303 -  AS '$libdir/latlon-v0002', 'pgl_epoint_ecluster_overlap';
   4.304 -
   4.305 -CREATE OR REPLACE FUNCTION ebox_overlap_proc(ebox, ebox)
   4.306 -  RETURNS boolean
   4.307 -  LANGUAGE C IMMUTABLE STRICT
   4.308 -  AS '$libdir/latlon-v0002', 'pgl_ebox_overlap';
   4.309 -
   4.310 -CREATE OR REPLACE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   4.311 -  RETURNS boolean
   4.312 -  LANGUAGE C IMMUTABLE STRICT
   4.313 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_overlap';
   4.314 -
   4.315 -CREATE OR REPLACE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   4.316 -  RETURNS boolean
   4.317 -  LANGUAGE C IMMUTABLE STRICT
   4.318 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_ecluster_overlap';
   4.319 -
   4.320 -CREATE OR REPLACE FUNCTION epoint_distance_proc(epoint, epoint)
   4.321 -  RETURNS float8
   4.322 -  LANGUAGE C IMMUTABLE STRICT
   4.323 -  AS '$libdir/latlon-v0002', 'pgl_epoint_distance';
   4.324 -
   4.325 -CREATE OR REPLACE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   4.326 -  RETURNS float8
   4.327 -  LANGUAGE C IMMUTABLE STRICT
   4.328 -  AS '$libdir/latlon-v0002', 'pgl_epoint_ecircle_distance';
   4.329 -
   4.330 -CREATE OR REPLACE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   4.331 -  RETURNS float8
   4.332 -  LANGUAGE C IMMUTABLE STRICT
   4.333 -  AS '$libdir/latlon-v0002', 'pgl_epoint_ecluster_distance';
   4.334 -
   4.335 -CREATE OR REPLACE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   4.336 -  RETURNS float8
   4.337 -  LANGUAGE C IMMUTABLE STRICT
   4.338 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_distance';
   4.339 -
   4.340 -CREATE OR REPLACE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   4.341 -  RETURNS float8
   4.342 -  LANGUAGE C IMMUTABLE STRICT
   4.343 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_ecluster_distance';
   4.344 -
   4.345 -CREATE OR REPLACE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
   4.346 -  RETURNS boolean
   4.347 -  LANGUAGE C STRICT
   4.348 -  AS '$libdir/latlon-v0002', 'pgl_gist_consistent';
   4.349 -
   4.350 -CREATE OR REPLACE FUNCTION pgl_gist_union(internal, internal)
   4.351 -  RETURNS internal
   4.352 -  LANGUAGE C STRICT
   4.353 -  AS '$libdir/latlon-v0002', 'pgl_gist_union';
   4.354 -
   4.355 -CREATE OR REPLACE FUNCTION pgl_gist_compress_epoint(internal)
   4.356 -  RETURNS internal
   4.357 -  LANGUAGE C STRICT
   4.358 -  AS '$libdir/latlon-v0002', 'pgl_gist_compress_epoint';
   4.359 -
   4.360 -CREATE OR REPLACE FUNCTION pgl_gist_compress_ecircle(internal)
   4.361 -  RETURNS internal
   4.362 -  LANGUAGE C STRICT
   4.363 -  AS '$libdir/latlon-v0002', 'pgl_gist_compress_ecircle';
   4.364 -
   4.365 -CREATE OR REPLACE FUNCTION pgl_gist_compress_ecluster(internal)
   4.366 -  RETURNS internal
   4.367 -  LANGUAGE C STRICT
   4.368 -  AS '$libdir/latlon-v0002', 'pgl_gist_compress_ecluster';
   4.369 -
   4.370 -CREATE OR REPLACE FUNCTION pgl_gist_decompress(internal)
   4.371 -  RETURNS internal
   4.372 -  LANGUAGE C STRICT
   4.373 -  AS '$libdir/latlon-v0002', 'pgl_gist_decompress';
   4.374 -
   4.375 -CREATE OR REPLACE FUNCTION pgl_gist_penalty(internal, internal, internal)
   4.376 -  RETURNS internal
   4.377 -  LANGUAGE C STRICT
   4.378 -  AS '$libdir/latlon-v0002', 'pgl_gist_penalty';
   4.379 -
   4.380 -CREATE OR REPLACE FUNCTION pgl_gist_picksplit(internal, internal)
   4.381 -  RETURNS internal
   4.382 -  LANGUAGE C STRICT
   4.383 -  AS '$libdir/latlon-v0002', 'pgl_gist_picksplit';
   4.384 -
   4.385 -CREATE OR REPLACE FUNCTION pgl_gist_same(internal, internal, internal)
   4.386 -  RETURNS internal
   4.387 -  LANGUAGE C STRICT
   4.388 -  AS '$libdir/latlon-v0002', 'pgl_gist_same';
   4.389 -
   4.390 -CREATE OR REPLACE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
   4.391 -  RETURNS internal
   4.392 -  LANGUAGE C STRICT
   4.393 -  AS '$libdir/latlon-v0002', 'pgl_gist_distance';
   4.394 -
     5.1 --- a/latlon--0.2--0.3.sql	Sat Sep 03 16:00:22 2016 +0200
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,575 +0,0 @@
     5.4 -
     5.5 -CREATE OR REPLACE FUNCTION ekey_point_in_dummy(cstring)
     5.6 -  RETURNS ekey_point
     5.7 -  LANGUAGE C IMMUTABLE STRICT
     5.8 -  AS '$libdir/latlon-v0003', 'pgl_notimpl';
     5.9 -
    5.10 -CREATE OR REPLACE FUNCTION ekey_point_out_dummy(ekey_point)
    5.11 -  RETURNS cstring
    5.12 -  LANGUAGE C IMMUTABLE STRICT
    5.13 -  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    5.14 -
    5.15 -CREATE OR REPLACE FUNCTION ekey_area_in_dummy(cstring)
    5.16 -  RETURNS ekey_area
    5.17 -  LANGUAGE C IMMUTABLE STRICT
    5.18 -  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    5.19 -
    5.20 -CREATE OR REPLACE FUNCTION ekey_area_out_dummy(ekey_area)
    5.21 -  RETURNS cstring
    5.22 -  LANGUAGE C IMMUTABLE STRICT
    5.23 -  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    5.24 -
    5.25 -CREATE OR REPLACE FUNCTION epoint_in(cstring)
    5.26 -  RETURNS epoint
    5.27 -  LANGUAGE C IMMUTABLE STRICT
    5.28 -  AS '$libdir/latlon-v0003', 'pgl_epoint_in';
    5.29 -
    5.30 -CREATE OR REPLACE FUNCTION ebox_in(cstring)
    5.31 -  RETURNS ebox
    5.32 -  LANGUAGE C IMMUTABLE STRICT
    5.33 -  AS '$libdir/latlon-v0003', 'pgl_ebox_in';
    5.34 -
    5.35 -CREATE OR REPLACE FUNCTION ecircle_in(cstring)
    5.36 -  RETURNS ecircle
    5.37 -  LANGUAGE C IMMUTABLE STRICT
    5.38 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_in';
    5.39 -
    5.40 -CREATE OR REPLACE FUNCTION ecluster_in(cstring)
    5.41 -  RETURNS ecluster
    5.42 -  LANGUAGE C IMMUTABLE STRICT
    5.43 -  AS '$libdir/latlon-v0003', 'pgl_ecluster_in';
    5.44 -
    5.45 -CREATE OR REPLACE FUNCTION epoint_out(epoint)
    5.46 -  RETURNS cstring
    5.47 -  LANGUAGE C IMMUTABLE STRICT
    5.48 -  AS '$libdir/latlon-v0003', 'pgl_epoint_out';
    5.49 -
    5.50 -CREATE OR REPLACE FUNCTION ebox_out(ebox)
    5.51 -  RETURNS cstring
    5.52 -  LANGUAGE C IMMUTABLE STRICT
    5.53 -  AS '$libdir/latlon-v0003', 'pgl_ebox_out';
    5.54 -
    5.55 -CREATE OR REPLACE FUNCTION ecircle_out(ecircle)
    5.56 -  RETURNS cstring
    5.57 -  LANGUAGE C IMMUTABLE STRICT
    5.58 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_out';
    5.59 -
    5.60 -CREATE OR REPLACE FUNCTION ecluster_out(ecluster)
    5.61 -  RETURNS cstring
    5.62 -  LANGUAGE C IMMUTABLE STRICT
    5.63 -  AS '$libdir/latlon-v0003', 'pgl_ecluster_out';
    5.64 -
    5.65 -CREATE OR REPLACE FUNCTION epoint_recv(internal)
    5.66 -  RETURNS epoint
    5.67 -  LANGUAGE C IMMUTABLE STRICT
    5.68 -  AS '$libdir/latlon-v0003', 'pgl_epoint_recv';
    5.69 -
    5.70 -CREATE OR REPLACE FUNCTION ebox_recv(internal)
    5.71 -  RETURNS ebox
    5.72 -  LANGUAGE C IMMUTABLE STRICT
    5.73 -  AS '$libdir/latlon-v0003', 'pgl_ebox_recv';
    5.74 -
    5.75 -CREATE OR REPLACE FUNCTION ecircle_recv(internal)
    5.76 -  RETURNS ecircle
    5.77 -  LANGUAGE C IMMUTABLE STRICT
    5.78 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_recv';
    5.79 -
    5.80 -CREATE OR REPLACE FUNCTION epoint_send(epoint)
    5.81 -  RETURNS bytea
    5.82 -  LANGUAGE C IMMUTABLE STRICT
    5.83 -  AS '$libdir/latlon-v0003', 'pgl_epoint_send';
    5.84 -
    5.85 -CREATE OR REPLACE FUNCTION ebox_send(ebox)
    5.86 -  RETURNS bytea
    5.87 -  LANGUAGE C IMMUTABLE STRICT
    5.88 -  AS '$libdir/latlon-v0003', 'pgl_ebox_send';
    5.89 -
    5.90 -CREATE OR REPLACE FUNCTION ecircle_send(ecircle)
    5.91 -  RETURNS bytea
    5.92 -  LANGUAGE C IMMUTABLE STRICT
    5.93 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_send';
    5.94 -
    5.95 -CREATE OR REPLACE FUNCTION epoint_btree_lt(epoint, epoint)
    5.96 -  RETURNS boolean
    5.97 -  LANGUAGE C IMMUTABLE STRICT
    5.98 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_lt';
    5.99 -
   5.100 -CREATE OR REPLACE FUNCTION epoint_btree_le(epoint, epoint)
   5.101 -  RETURNS boolean
   5.102 -  LANGUAGE C IMMUTABLE STRICT
   5.103 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_le';
   5.104 -
   5.105 -CREATE OR REPLACE FUNCTION epoint_btree_eq(epoint, epoint)
   5.106 -  RETURNS boolean
   5.107 -  LANGUAGE C IMMUTABLE STRICT
   5.108 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_eq';
   5.109 -
   5.110 -CREATE OR REPLACE FUNCTION epoint_btree_ne(epoint, epoint)
   5.111 -  RETURNS boolean
   5.112 -  LANGUAGE C IMMUTABLE STRICT
   5.113 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_ne';
   5.114 -
   5.115 -CREATE OR REPLACE FUNCTION epoint_btree_ge(epoint, epoint)
   5.116 -  RETURNS boolean
   5.117 -  LANGUAGE C IMMUTABLE STRICT
   5.118 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_ge';
   5.119 -
   5.120 -CREATE OR REPLACE FUNCTION epoint_btree_gt(epoint, epoint)
   5.121 -  RETURNS boolean
   5.122 -  LANGUAGE C IMMUTABLE STRICT
   5.123 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_gt';
   5.124 -
   5.125 -CREATE OR REPLACE FUNCTION epoint_btree_cmp(epoint, epoint)
   5.126 -  RETURNS int4
   5.127 -  LANGUAGE C IMMUTABLE STRICT
   5.128 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_cmp';
   5.129 -
   5.130 -CREATE OR REPLACE FUNCTION ebox_btree_lt(ebox, ebox)
   5.131 -  RETURNS boolean
   5.132 -  LANGUAGE C IMMUTABLE STRICT
   5.133 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_lt';
   5.134 -
   5.135 -CREATE OR REPLACE FUNCTION ebox_btree_le(ebox, ebox)
   5.136 -  RETURNS boolean
   5.137 -  LANGUAGE C IMMUTABLE STRICT
   5.138 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_le';
   5.139 -
   5.140 -CREATE OR REPLACE FUNCTION ebox_btree_eq(ebox, ebox)
   5.141 -  RETURNS boolean
   5.142 -  LANGUAGE C IMMUTABLE STRICT
   5.143 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_eq';
   5.144 -
   5.145 -CREATE OR REPLACE FUNCTION ebox_btree_ne(ebox, ebox)
   5.146 -  RETURNS boolean
   5.147 -  LANGUAGE C IMMUTABLE STRICT
   5.148 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_ne';
   5.149 -
   5.150 -CREATE OR REPLACE FUNCTION ebox_btree_ge(ebox, ebox)
   5.151 -  RETURNS boolean
   5.152 -  LANGUAGE C IMMUTABLE STRICT
   5.153 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_ge';
   5.154 -
   5.155 -CREATE OR REPLACE FUNCTION ebox_btree_gt(ebox, ebox)
   5.156 -  RETURNS boolean
   5.157 -  LANGUAGE C IMMUTABLE STRICT
   5.158 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_gt';
   5.159 -
   5.160 -CREATE OR REPLACE FUNCTION ebox_btree_cmp(ebox, ebox)
   5.161 -  RETURNS int4
   5.162 -  LANGUAGE C IMMUTABLE STRICT
   5.163 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_cmp';
   5.164 -
   5.165 -CREATE OR REPLACE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   5.166 -  RETURNS boolean
   5.167 -  LANGUAGE C IMMUTABLE STRICT
   5.168 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_lt';
   5.169 -
   5.170 -CREATE OR REPLACE FUNCTION ecircle_btree_le(ecircle, ecircle)
   5.171 -  RETURNS boolean
   5.172 -  LANGUAGE C IMMUTABLE STRICT
   5.173 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_le';
   5.174 -
   5.175 -CREATE OR REPLACE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   5.176 -  RETURNS boolean
   5.177 -  LANGUAGE C IMMUTABLE STRICT
   5.178 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_eq';
   5.179 -
   5.180 -CREATE OR REPLACE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   5.181 -  RETURNS boolean
   5.182 -  LANGUAGE C IMMUTABLE STRICT
   5.183 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_ne';
   5.184 -
   5.185 -CREATE OR REPLACE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   5.186 -  RETURNS boolean
   5.187 -  LANGUAGE C IMMUTABLE STRICT
   5.188 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_ge';
   5.189 -
   5.190 -CREATE OR REPLACE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   5.191 -  RETURNS boolean
   5.192 -  LANGUAGE C IMMUTABLE STRICT
   5.193 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_gt';
   5.194 -
   5.195 -CREATE OR REPLACE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   5.196 -  RETURNS int4
   5.197 -  LANGUAGE C IMMUTABLE STRICT
   5.198 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_cmp';
   5.199 -
   5.200 -CREATE OR REPLACE FUNCTION cast_epoint_to_ebox(epoint)
   5.201 -  RETURNS ebox
   5.202 -  LANGUAGE C IMMUTABLE STRICT
   5.203 -  AS '$libdir/latlon-v0003', 'pgl_epoint_to_ebox';
   5.204 -
   5.205 -CREATE OR REPLACE FUNCTION cast_epoint_to_ecircle(epoint)
   5.206 -  RETURNS ecircle
   5.207 -  LANGUAGE C IMMUTABLE STRICT
   5.208 -  AS '$libdir/latlon-v0003', 'pgl_epoint_to_ecircle';
   5.209 -
   5.210 -CREATE OR REPLACE FUNCTION cast_epoint_to_ecluster(epoint)
   5.211 -  RETURNS ecluster
   5.212 -  LANGUAGE C IMMUTABLE STRICT
   5.213 -  AS '$libdir/latlon-v0003', 'pgl_epoint_to_ecluster';
   5.214 -
   5.215 -CREATE OR REPLACE FUNCTION cast_ebox_to_ecluster(ebox)
   5.216 -  RETURNS ecluster
   5.217 -  LANGUAGE C IMMUTABLE STRICT
   5.218 -  AS '$libdir/latlon-v0003', 'pgl_ebox_to_ecluster';
   5.219 -
   5.220 -CREATE OR REPLACE FUNCTION epoint(float8, float8)
   5.221 -  RETURNS epoint
   5.222 -  LANGUAGE C IMMUTABLE STRICT
   5.223 -  AS '$libdir/latlon-v0003', 'pgl_create_epoint';
   5.224 -
   5.225 -CREATE OR REPLACE FUNCTION empty_ebox()
   5.226 -  RETURNS ebox
   5.227 -  LANGUAGE C IMMUTABLE STRICT
   5.228 -  AS '$libdir/latlon-v0003', 'pgl_create_empty_ebox';
   5.229 -
   5.230 -CREATE OR REPLACE FUNCTION ebox(float8, float8, float8, float8)
   5.231 -  RETURNS ebox
   5.232 -  LANGUAGE C IMMUTABLE STRICT
   5.233 -  AS '$libdir/latlon-v0003', 'pgl_create_ebox';
   5.234 -
   5.235 -CREATE OR REPLACE FUNCTION ebox(epoint, epoint)
   5.236 -  RETURNS ebox
   5.237 -  LANGUAGE C IMMUTABLE STRICT
   5.238 -  AS '$libdir/latlon-v0003', 'pgl_create_ebox_from_epoints';
   5.239 -
   5.240 -CREATE OR REPLACE FUNCTION ecircle(float8, float8, float8)
   5.241 -  RETURNS ecircle
   5.242 -  LANGUAGE C IMMUTABLE STRICT
   5.243 -  AS '$libdir/latlon-v0003', 'pgl_create_ecircle';
   5.244 -
   5.245 -CREATE OR REPLACE FUNCTION ecircle(epoint, float8)
   5.246 -  RETURNS ecircle
   5.247 -  LANGUAGE C IMMUTABLE STRICT
   5.248 -  AS '$libdir/latlon-v0003', 'pgl_create_ecircle_from_epoint';
   5.249 -
   5.250 -CREATE OR REPLACE FUNCTION latitude(epoint)
   5.251 -  RETURNS float8
   5.252 -  LANGUAGE C IMMUTABLE STRICT
   5.253 -  AS '$libdir/latlon-v0003', 'pgl_epoint_lat';
   5.254 -
   5.255 -CREATE OR REPLACE FUNCTION longitude(epoint)
   5.256 -  RETURNS float8
   5.257 -  LANGUAGE C IMMUTABLE STRICT
   5.258 -  AS '$libdir/latlon-v0003', 'pgl_epoint_lon';
   5.259 -
   5.260 -CREATE OR REPLACE FUNCTION min_latitude(ebox)
   5.261 -  RETURNS float8
   5.262 -  LANGUAGE C IMMUTABLE STRICT
   5.263 -  AS '$libdir/latlon-v0003', 'pgl_ebox_lat_min';
   5.264 -
   5.265 -CREATE OR REPLACE FUNCTION max_latitude(ebox)
   5.266 -  RETURNS float8
   5.267 -  LANGUAGE C IMMUTABLE STRICT
   5.268 -  AS '$libdir/latlon-v0003', 'pgl_ebox_lat_max';
   5.269 -
   5.270 -CREATE OR REPLACE FUNCTION min_longitude(ebox)
   5.271 -  RETURNS float8
   5.272 -  LANGUAGE C IMMUTABLE STRICT
   5.273 -  AS '$libdir/latlon-v0003', 'pgl_ebox_lon_min';
   5.274 -
   5.275 -CREATE OR REPLACE FUNCTION max_longitude(ebox)
   5.276 -  RETURNS float8
   5.277 -  LANGUAGE C IMMUTABLE STRICT
   5.278 -  AS '$libdir/latlon-v0003', 'pgl_ebox_lon_max';
   5.279 -
   5.280 -CREATE OR REPLACE FUNCTION center(ecircle)
   5.281 -  RETURNS epoint
   5.282 -  LANGUAGE C IMMUTABLE STRICT
   5.283 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_center';
   5.284 -
   5.285 -CREATE OR REPLACE FUNCTION radius(ecircle)
   5.286 -  RETURNS float8
   5.287 -  LANGUAGE C IMMUTABLE STRICT
   5.288 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_radius';
   5.289 -
   5.290 -CREATE OR REPLACE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   5.291 -  RETURNS boolean
   5.292 -  LANGUAGE C IMMUTABLE STRICT
   5.293 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ebox_overlap';
   5.294 -
   5.295 -CREATE OR REPLACE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   5.296 -  RETURNS boolean
   5.297 -  LANGUAGE C IMMUTABLE STRICT
   5.298 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecircle_overlap';
   5.299 -
   5.300 -CREATE OR REPLACE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   5.301 -  RETURNS boolean
   5.302 -  LANGUAGE C IMMUTABLE STRICT
   5.303 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecluster_overlap';
   5.304 -
   5.305 -CREATE OR REPLACE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
   5.306 -  RETURNS boolean
   5.307 -  LANGUAGE C IMMUTABLE STRICT
   5.308 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecluster_may_overlap';
   5.309 -
   5.310 -CREATE OR REPLACE FUNCTION ebox_overlap_proc(ebox, ebox)
   5.311 -  RETURNS boolean
   5.312 -  LANGUAGE C IMMUTABLE STRICT
   5.313 -  AS '$libdir/latlon-v0003', 'pgl_ebox_overlap';
   5.314 -
   5.315 -CREATE OR REPLACE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
   5.316 -  RETURNS boolean
   5.317 -  LANGUAGE C IMMUTABLE STRICT
   5.318 -  AS '$libdir/latlon-v0003', 'pgl_ebox_ecircle_may_overlap';
   5.319 -
   5.320 -CREATE OR REPLACE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
   5.321 -  RETURNS boolean
   5.322 -  LANGUAGE C IMMUTABLE STRICT
   5.323 -  AS '$libdir/latlon-v0003', 'pgl_ebox_ecluster_may_overlap';
   5.324 -
   5.325 -CREATE OR REPLACE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   5.326 -  RETURNS boolean
   5.327 -  LANGUAGE C IMMUTABLE STRICT
   5.328 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_overlap';
   5.329 -
   5.330 -CREATE OR REPLACE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   5.331 -  RETURNS boolean
   5.332 -  LANGUAGE C IMMUTABLE STRICT
   5.333 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_ecluster_overlap';
   5.334 -
   5.335 -CREATE OR REPLACE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
   5.336 -  RETURNS boolean
   5.337 -  LANGUAGE C IMMUTABLE STRICT
   5.338 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_ecluster_may_overlap';
   5.339 -
   5.340 -CREATE OR REPLACE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
   5.341 -  RETURNS boolean
   5.342 -  LANGUAGE C IMMUTABLE STRICT
   5.343 -  AS '$libdir/latlon-v0003', 'pgl_ecluster_may_overlap';
   5.344 -
   5.345 -CREATE OPERATOR &&+ (
   5.346 -  leftarg = epoint,
   5.347 -  rightarg = ecluster,
   5.348 -  procedure = epoint_ecluster_may_overlap_proc,
   5.349 -  commutator = &&+,
   5.350 -  restrict = areasel,
   5.351 -  join = areajoinsel
   5.352 -);
   5.353 -
   5.354 -CREATE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint)
   5.355 -  RETURNS boolean
   5.356 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   5.357 -
   5.358 -CREATE OPERATOR &&+ (
   5.359 -  leftarg = ecluster,
   5.360 -  rightarg = epoint,
   5.361 -  procedure = epoint_ecluster_may_overlap_commutator,
   5.362 -  commutator = &&+,
   5.363 -  restrict = areasel,
   5.364 -  join = areajoinsel
   5.365 -);
   5.366 -
   5.367 -CREATE OPERATOR &&+ (
   5.368 -  leftarg = ebox,
   5.369 -  rightarg = ecircle,
   5.370 -  procedure = ebox_ecircle_may_overlap_proc,
   5.371 -  commutator = &&+,
   5.372 -  restrict = areasel,
   5.373 -  join = areajoinsel
   5.374 -);
   5.375 -
   5.376 -CREATE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox)
   5.377 -  RETURNS boolean
   5.378 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   5.379 -
   5.380 -CREATE OPERATOR &&+ (
   5.381 -  leftarg = ecircle,
   5.382 -  rightarg = ebox,
   5.383 -  procedure = ebox_ecircle_may_overlap_commutator,
   5.384 -  commutator = &&+,
   5.385 -  restrict = areasel,
   5.386 -  join = areajoinsel
   5.387 -);
   5.388 -
   5.389 -CREATE OPERATOR &&+ (
   5.390 -  leftarg = ebox,
   5.391 -  rightarg = ecluster,
   5.392 -  procedure = ebox_ecluster_may_overlap_proc,
   5.393 -  commutator = &&+,
   5.394 -  restrict = areasel,
   5.395 -  join = areajoinsel
   5.396 -);
   5.397 -
   5.398 -CREATE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox)
   5.399 -  RETURNS boolean
   5.400 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   5.401 -
   5.402 -CREATE OPERATOR &&+ (
   5.403 -  leftarg = ecluster,
   5.404 -  rightarg = ebox,
   5.405 -  procedure = ebox_ecluster_may_overlap_commutator,
   5.406 -  commutator = &&+,
   5.407 -  restrict = areasel,
   5.408 -  join = areajoinsel
   5.409 -);
   5.410 -
   5.411 -CREATE OPERATOR &&+ (
   5.412 -  leftarg = ecircle,
   5.413 -  rightarg = ecluster,
   5.414 -  procedure = ecircle_ecluster_may_overlap_proc,
   5.415 -  commutator = &&+,
   5.416 -  restrict = areasel,
   5.417 -  join = areajoinsel
   5.418 -);
   5.419 -
   5.420 -CREATE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle)
   5.421 -  RETURNS boolean
   5.422 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   5.423 -
   5.424 -CREATE OPERATOR &&+ (
   5.425 -  leftarg = ecluster,
   5.426 -  rightarg = ecircle,
   5.427 -  procedure = ecircle_ecluster_may_overlap_commutator,
   5.428 -  commutator = &&+,
   5.429 -  restrict = areasel,
   5.430 -  join = areajoinsel
   5.431 -);
   5.432 -
   5.433 -CREATE OPERATOR &&+ (
   5.434 -  leftarg = ecluster,
   5.435 -  rightarg = ecluster,
   5.436 -  procedure = ecluster_may_overlap_proc,
   5.437 -  commutator = &&+,
   5.438 -  restrict = areasel,
   5.439 -  join = areajoinsel
   5.440 -);
   5.441 -
   5.442 -CREATE OR REPLACE FUNCTION epoint_distance_proc(epoint, epoint)
   5.443 -  RETURNS float8
   5.444 -  LANGUAGE C IMMUTABLE STRICT
   5.445 -  AS '$libdir/latlon-v0003', 'pgl_epoint_distance';
   5.446 -
   5.447 -CREATE OR REPLACE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   5.448 -  RETURNS float8
   5.449 -  LANGUAGE C IMMUTABLE STRICT
   5.450 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecircle_distance';
   5.451 -
   5.452 -CREATE OR REPLACE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   5.453 -  RETURNS float8
   5.454 -  LANGUAGE C IMMUTABLE STRICT
   5.455 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecluster_distance';
   5.456 -
   5.457 -CREATE OR REPLACE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   5.458 -  RETURNS float8
   5.459 -  LANGUAGE C IMMUTABLE STRICT
   5.460 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_distance';
   5.461 -
   5.462 -CREATE OR REPLACE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   5.463 -  RETURNS float8
   5.464 -  LANGUAGE C IMMUTABLE STRICT
   5.465 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_ecluster_distance';
   5.466 -
   5.467 -CREATE OR REPLACE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
   5.468 -  RETURNS boolean
   5.469 -  LANGUAGE C STRICT
   5.470 -  AS '$libdir/latlon-v0003', 'pgl_gist_consistent';
   5.471 -
   5.472 -CREATE OR REPLACE FUNCTION pgl_gist_union(internal, internal)
   5.473 -  RETURNS internal
   5.474 -  LANGUAGE C STRICT
   5.475 -  AS '$libdir/latlon-v0003', 'pgl_gist_union';
   5.476 -
   5.477 -CREATE OR REPLACE FUNCTION pgl_gist_compress_epoint(internal)
   5.478 -  RETURNS internal
   5.479 -  LANGUAGE C STRICT
   5.480 -  AS '$libdir/latlon-v0003', 'pgl_gist_compress_epoint';
   5.481 -
   5.482 -CREATE OR REPLACE FUNCTION pgl_gist_compress_ecircle(internal)
   5.483 -  RETURNS internal
   5.484 -  LANGUAGE C STRICT
   5.485 -  AS '$libdir/latlon-v0003', 'pgl_gist_compress_ecircle';
   5.486 -
   5.487 -CREATE OR REPLACE FUNCTION pgl_gist_compress_ecluster(internal)
   5.488 -  RETURNS internal
   5.489 -  LANGUAGE C STRICT
   5.490 -  AS '$libdir/latlon-v0003', 'pgl_gist_compress_ecluster';
   5.491 -
   5.492 -CREATE OR REPLACE FUNCTION pgl_gist_decompress(internal)
   5.493 -  RETURNS internal
   5.494 -  LANGUAGE C STRICT
   5.495 -  AS '$libdir/latlon-v0003', 'pgl_gist_decompress';
   5.496 -
   5.497 -CREATE OR REPLACE FUNCTION pgl_gist_penalty(internal, internal, internal)
   5.498 -  RETURNS internal
   5.499 -  LANGUAGE C STRICT
   5.500 -  AS '$libdir/latlon-v0003', 'pgl_gist_penalty';
   5.501 -
   5.502 -CREATE OR REPLACE FUNCTION pgl_gist_picksplit(internal, internal)
   5.503 -  RETURNS internal
   5.504 -  LANGUAGE C STRICT
   5.505 -  AS '$libdir/latlon-v0003', 'pgl_gist_picksplit';
   5.506 -
   5.507 -CREATE OR REPLACE FUNCTION pgl_gist_same(internal, internal, internal)
   5.508 -  RETURNS internal
   5.509 -  LANGUAGE C STRICT
   5.510 -  AS '$libdir/latlon-v0003', 'pgl_gist_same';
   5.511 -
   5.512 -CREATE OR REPLACE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
   5.513 -  RETURNS internal
   5.514 -  LANGUAGE C STRICT
   5.515 -  AS '$libdir/latlon-v0003', 'pgl_gist_distance';
   5.516 -
   5.517 -DROP OPERATOR CLASS epoint_ops USING gist;
   5.518 -CREATE OPERATOR CLASS epoint_ops
   5.519 -  DEFAULT FOR TYPE epoint USING gist AS
   5.520 -  OPERATOR  11 = ,
   5.521 -  OPERATOR  22 &&  (epoint, ebox),
   5.522 -  OPERATOR  23 &&  (epoint, ecircle),
   5.523 -  OPERATOR  24 &&  (epoint, ecluster),
   5.524 -  OPERATOR 124 &&+ (epoint, ecluster),
   5.525 -  OPERATOR  31 <-> (epoint, epoint) FOR ORDER BY float_ops,
   5.526 -  OPERATOR  33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
   5.527 -  OPERATOR  34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
   5.528 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
   5.529 -  FUNCTION 2 pgl_gist_union(internal, internal),
   5.530 -  FUNCTION 3 pgl_gist_compress_epoint(internal),
   5.531 -  FUNCTION 4 pgl_gist_decompress(internal),
   5.532 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
   5.533 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
   5.534 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
   5.535 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
   5.536 -  STORAGE ekey_point;
   5.537 -
   5.538 -DROP OPERATOR CLASS ecircle_ops USING gist;
   5.539 -CREATE OPERATOR CLASS ecircle_ops
   5.540 -  DEFAULT FOR TYPE ecircle USING gist AS
   5.541 -  OPERATOR  13 = ,
   5.542 -  OPERATOR  21 &&  (ecircle, epoint),
   5.543 -  OPERATOR 122 &&+ (ecircle, ebox),
   5.544 -  OPERATOR  23 &&  (ecircle, ecircle),
   5.545 -  OPERATOR  24 &&  (ecircle, ecluster),
   5.546 -  OPERATOR 124 &&+ (ecircle, ecluster),
   5.547 -  OPERATOR  31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
   5.548 -  OPERATOR  33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
   5.549 -  OPERATOR  34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
   5.550 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
   5.551 -  FUNCTION 2 pgl_gist_union(internal, internal),
   5.552 -  FUNCTION 3 pgl_gist_compress_ecircle(internal),
   5.553 -  FUNCTION 4 pgl_gist_decompress(internal),
   5.554 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
   5.555 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
   5.556 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
   5.557 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
   5.558 -  STORAGE ekey_area;
   5.559 -
   5.560 -DROP OPERATOR CLASS ecluster_ops USING gist;
   5.561 -CREATE OPERATOR CLASS ecluster_ops
   5.562 -  DEFAULT FOR TYPE ecluster USING gist AS
   5.563 -  OPERATOR  21 &&  (ecluster, epoint),
   5.564 -  OPERATOR 121 &&+ (ecluster, epoint),
   5.565 -  OPERATOR 122 &&+ (ecluster, ebox),
   5.566 -  OPERATOR  23 &&  (ecluster, ecircle),
   5.567 -  OPERATOR 123 &&+ (ecluster, ecircle),
   5.568 -  OPERATOR 124 &&+ (ecluster, ecluster),
   5.569 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
   5.570 -  FUNCTION 2 pgl_gist_union(internal, internal),
   5.571 -  FUNCTION 3 pgl_gist_compress_ecluster(internal),
   5.572 -  FUNCTION 4 pgl_gist_decompress(internal),
   5.573 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
   5.574 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
   5.575 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
   5.576 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
   5.577 -  STORAGE ekey_area;
   5.578 -
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/latlon--0.3--0.4.sql	Fri Sep 09 19:22:30 2016 +0200
     6.3 @@ -0,0 +1,767 @@
     6.4 +
     6.5 +CREATE OR REPLACE FUNCTION ekey_point_in_dummy(cstring)
     6.6 +  RETURNS ekey_point
     6.7 +  LANGUAGE C IMMUTABLE STRICT
     6.8 +  AS '$libdir/latlon-v0004', 'pgl_notimpl';
     6.9 +
    6.10 +CREATE OR REPLACE FUNCTION ekey_point_out_dummy(ekey_point)
    6.11 +  RETURNS cstring
    6.12 +  LANGUAGE C IMMUTABLE STRICT
    6.13 +  AS '$libdir/latlon-v0004', 'pgl_notimpl';
    6.14 +
    6.15 +CREATE OR REPLACE FUNCTION ekey_area_in_dummy(cstring)
    6.16 +  RETURNS ekey_area
    6.17 +  LANGUAGE C IMMUTABLE STRICT
    6.18 +  AS '$libdir/latlon-v0004', 'pgl_notimpl';
    6.19 +
    6.20 +CREATE OR REPLACE FUNCTION ekey_area_out_dummy(ekey_area)
    6.21 +  RETURNS cstring
    6.22 +  LANGUAGE C IMMUTABLE STRICT
    6.23 +  AS '$libdir/latlon-v0004', 'pgl_notimpl';
    6.24 +
    6.25 +CREATE OR REPLACE FUNCTION epoint_in(cstring)
    6.26 +  RETURNS epoint
    6.27 +  LANGUAGE C IMMUTABLE STRICT
    6.28 +  AS '$libdir/latlon-v0004', 'pgl_epoint_in';
    6.29 +
    6.30 +CREATE OR REPLACE FUNCTION ebox_in(cstring)
    6.31 +  RETURNS ebox
    6.32 +  LANGUAGE C IMMUTABLE STRICT
    6.33 +  AS '$libdir/latlon-v0004', 'pgl_ebox_in';
    6.34 +
    6.35 +CREATE OR REPLACE FUNCTION ecircle_in(cstring)
    6.36 +  RETURNS ecircle
    6.37 +  LANGUAGE C IMMUTABLE STRICT
    6.38 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_in';
    6.39 +
    6.40 +CREATE OR REPLACE FUNCTION ecluster_in(cstring)
    6.41 +  RETURNS ecluster
    6.42 +  LANGUAGE C IMMUTABLE STRICT
    6.43 +  AS '$libdir/latlon-v0004', 'pgl_ecluster_in';
    6.44 +
    6.45 +CREATE OR REPLACE FUNCTION epoint_out(epoint)
    6.46 +  RETURNS cstring
    6.47 +  LANGUAGE C IMMUTABLE STRICT
    6.48 +  AS '$libdir/latlon-v0004', 'pgl_epoint_out';
    6.49 +
    6.50 +CREATE OR REPLACE FUNCTION ebox_out(ebox)
    6.51 +  RETURNS cstring
    6.52 +  LANGUAGE C IMMUTABLE STRICT
    6.53 +  AS '$libdir/latlon-v0004', 'pgl_ebox_out';
    6.54 +
    6.55 +CREATE OR REPLACE FUNCTION ecircle_out(ecircle)
    6.56 +  RETURNS cstring
    6.57 +  LANGUAGE C IMMUTABLE STRICT
    6.58 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_out';
    6.59 +
    6.60 +CREATE OR REPLACE FUNCTION ecluster_out(ecluster)
    6.61 +  RETURNS cstring
    6.62 +  LANGUAGE C IMMUTABLE STRICT
    6.63 +  AS '$libdir/latlon-v0004', 'pgl_ecluster_out';
    6.64 +
    6.65 +CREATE OR REPLACE FUNCTION epoint_recv(internal)
    6.66 +  RETURNS epoint
    6.67 +  LANGUAGE C IMMUTABLE STRICT
    6.68 +  AS '$libdir/latlon-v0004', 'pgl_epoint_recv';
    6.69 +
    6.70 +CREATE OR REPLACE FUNCTION ebox_recv(internal)
    6.71 +  RETURNS ebox
    6.72 +  LANGUAGE C IMMUTABLE STRICT
    6.73 +  AS '$libdir/latlon-v0004', 'pgl_ebox_recv';
    6.74 +
    6.75 +CREATE OR REPLACE FUNCTION ecircle_recv(internal)
    6.76 +  RETURNS ecircle
    6.77 +  LANGUAGE C IMMUTABLE STRICT
    6.78 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_recv';
    6.79 +
    6.80 +CREATE OR REPLACE FUNCTION epoint_send(epoint)
    6.81 +  RETURNS bytea
    6.82 +  LANGUAGE C IMMUTABLE STRICT
    6.83 +  AS '$libdir/latlon-v0004', 'pgl_epoint_send';
    6.84 +
    6.85 +CREATE OR REPLACE FUNCTION ebox_send(ebox)
    6.86 +  RETURNS bytea
    6.87 +  LANGUAGE C IMMUTABLE STRICT
    6.88 +  AS '$libdir/latlon-v0004', 'pgl_ebox_send';
    6.89 +
    6.90 +CREATE OR REPLACE FUNCTION ecircle_send(ecircle)
    6.91 +  RETURNS bytea
    6.92 +  LANGUAGE C IMMUTABLE STRICT
    6.93 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_send';
    6.94 +
    6.95 +CREATE OR REPLACE FUNCTION epoint_btree_lt(epoint, epoint)
    6.96 +  RETURNS boolean
    6.97 +  LANGUAGE C IMMUTABLE STRICT
    6.98 +  AS '$libdir/latlon-v0004', 'pgl_btree_epoint_lt';
    6.99 +
   6.100 +CREATE OR REPLACE FUNCTION epoint_btree_le(epoint, epoint)
   6.101 +  RETURNS boolean
   6.102 +  LANGUAGE C IMMUTABLE STRICT
   6.103 +  AS '$libdir/latlon-v0004', 'pgl_btree_epoint_le';
   6.104 +
   6.105 +CREATE OR REPLACE FUNCTION epoint_btree_eq(epoint, epoint)
   6.106 +  RETURNS boolean
   6.107 +  LANGUAGE C IMMUTABLE STRICT
   6.108 +  AS '$libdir/latlon-v0004', 'pgl_btree_epoint_eq';
   6.109 +
   6.110 +CREATE OR REPLACE FUNCTION epoint_btree_ne(epoint, epoint)
   6.111 +  RETURNS boolean
   6.112 +  LANGUAGE C IMMUTABLE STRICT
   6.113 +  AS '$libdir/latlon-v0004', 'pgl_btree_epoint_ne';
   6.114 +
   6.115 +CREATE OR REPLACE FUNCTION epoint_btree_ge(epoint, epoint)
   6.116 +  RETURNS boolean
   6.117 +  LANGUAGE C IMMUTABLE STRICT
   6.118 +  AS '$libdir/latlon-v0004', 'pgl_btree_epoint_ge';
   6.119 +
   6.120 +CREATE OR REPLACE FUNCTION epoint_btree_gt(epoint, epoint)
   6.121 +  RETURNS boolean
   6.122 +  LANGUAGE C IMMUTABLE STRICT
   6.123 +  AS '$libdir/latlon-v0004', 'pgl_btree_epoint_gt';
   6.124 +
   6.125 +CREATE OR REPLACE FUNCTION epoint_btree_cmp(epoint, epoint)
   6.126 +  RETURNS int4
   6.127 +  LANGUAGE C IMMUTABLE STRICT
   6.128 +  AS '$libdir/latlon-v0004', 'pgl_btree_epoint_cmp';
   6.129 +
   6.130 +CREATE OR REPLACE FUNCTION ebox_btree_lt(ebox, ebox)
   6.131 +  RETURNS boolean
   6.132 +  LANGUAGE C IMMUTABLE STRICT
   6.133 +  AS '$libdir/latlon-v0004', 'pgl_btree_ebox_lt';
   6.134 +
   6.135 +CREATE OR REPLACE FUNCTION ebox_btree_le(ebox, ebox)
   6.136 +  RETURNS boolean
   6.137 +  LANGUAGE C IMMUTABLE STRICT
   6.138 +  AS '$libdir/latlon-v0004', 'pgl_btree_ebox_le';
   6.139 +
   6.140 +CREATE OR REPLACE FUNCTION ebox_btree_eq(ebox, ebox)
   6.141 +  RETURNS boolean
   6.142 +  LANGUAGE C IMMUTABLE STRICT
   6.143 +  AS '$libdir/latlon-v0004', 'pgl_btree_ebox_eq';
   6.144 +
   6.145 +CREATE OR REPLACE FUNCTION ebox_btree_ne(ebox, ebox)
   6.146 +  RETURNS boolean
   6.147 +  LANGUAGE C IMMUTABLE STRICT
   6.148 +  AS '$libdir/latlon-v0004', 'pgl_btree_ebox_ne';
   6.149 +
   6.150 +CREATE OR REPLACE FUNCTION ebox_btree_ge(ebox, ebox)
   6.151 +  RETURNS boolean
   6.152 +  LANGUAGE C IMMUTABLE STRICT
   6.153 +  AS '$libdir/latlon-v0004', 'pgl_btree_ebox_ge';
   6.154 +
   6.155 +CREATE OR REPLACE FUNCTION ebox_btree_gt(ebox, ebox)
   6.156 +  RETURNS boolean
   6.157 +  LANGUAGE C IMMUTABLE STRICT
   6.158 +  AS '$libdir/latlon-v0004', 'pgl_btree_ebox_gt';
   6.159 +
   6.160 +CREATE OR REPLACE FUNCTION ebox_btree_cmp(ebox, ebox)
   6.161 +  RETURNS int4
   6.162 +  LANGUAGE C IMMUTABLE STRICT
   6.163 +  AS '$libdir/latlon-v0004', 'pgl_btree_ebox_cmp';
   6.164 +
   6.165 +CREATE OR REPLACE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   6.166 +  RETURNS boolean
   6.167 +  LANGUAGE C IMMUTABLE STRICT
   6.168 +  AS '$libdir/latlon-v0004', 'pgl_btree_ecircle_lt';
   6.169 +
   6.170 +CREATE OR REPLACE FUNCTION ecircle_btree_le(ecircle, ecircle)
   6.171 +  RETURNS boolean
   6.172 +  LANGUAGE C IMMUTABLE STRICT
   6.173 +  AS '$libdir/latlon-v0004', 'pgl_btree_ecircle_le';
   6.174 +
   6.175 +CREATE OR REPLACE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   6.176 +  RETURNS boolean
   6.177 +  LANGUAGE C IMMUTABLE STRICT
   6.178 +  AS '$libdir/latlon-v0004', 'pgl_btree_ecircle_eq';
   6.179 +
   6.180 +CREATE OR REPLACE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   6.181 +  RETURNS boolean
   6.182 +  LANGUAGE C IMMUTABLE STRICT
   6.183 +  AS '$libdir/latlon-v0004', 'pgl_btree_ecircle_ne';
   6.184 +
   6.185 +CREATE OR REPLACE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   6.186 +  RETURNS boolean
   6.187 +  LANGUAGE C IMMUTABLE STRICT
   6.188 +  AS '$libdir/latlon-v0004', 'pgl_btree_ecircle_ge';
   6.189 +
   6.190 +CREATE OR REPLACE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   6.191 +  RETURNS boolean
   6.192 +  LANGUAGE C IMMUTABLE STRICT
   6.193 +  AS '$libdir/latlon-v0004', 'pgl_btree_ecircle_gt';
   6.194 +
   6.195 +CREATE OR REPLACE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   6.196 +  RETURNS int4
   6.197 +  LANGUAGE C IMMUTABLE STRICT
   6.198 +  AS '$libdir/latlon-v0004', 'pgl_btree_ecircle_cmp';
   6.199 +
   6.200 +CREATE OR REPLACE FUNCTION cast_epoint_to_ebox(epoint)
   6.201 +  RETURNS ebox
   6.202 +  LANGUAGE C IMMUTABLE STRICT
   6.203 +  AS '$libdir/latlon-v0004', 'pgl_epoint_to_ebox';
   6.204 +
   6.205 +CREATE OR REPLACE FUNCTION cast_epoint_to_ecircle(epoint)
   6.206 +  RETURNS ecircle
   6.207 +  LANGUAGE C IMMUTABLE STRICT
   6.208 +  AS '$libdir/latlon-v0004', 'pgl_epoint_to_ecircle';
   6.209 +
   6.210 +CREATE OR REPLACE FUNCTION cast_epoint_to_ecluster(epoint)
   6.211 +  RETURNS ecluster
   6.212 +  LANGUAGE C IMMUTABLE STRICT
   6.213 +  AS '$libdir/latlon-v0004', 'pgl_epoint_to_ecluster';
   6.214 +
   6.215 +CREATE OR REPLACE FUNCTION cast_ebox_to_ecluster(ebox)
   6.216 +  RETURNS ecluster
   6.217 +  LANGUAGE C IMMUTABLE STRICT
   6.218 +  AS '$libdir/latlon-v0004', 'pgl_ebox_to_ecluster';
   6.219 +
   6.220 +CREATE OR REPLACE FUNCTION epoint(float8, float8)
   6.221 +  RETURNS epoint
   6.222 +  LANGUAGE C IMMUTABLE STRICT
   6.223 +  AS '$libdir/latlon-v0004', 'pgl_create_epoint';
   6.224 +
   6.225 +CREATE OR REPLACE FUNCTION empty_ebox()
   6.226 +  RETURNS ebox
   6.227 +  LANGUAGE C IMMUTABLE STRICT
   6.228 +  AS '$libdir/latlon-v0004', 'pgl_create_empty_ebox';
   6.229 +
   6.230 +CREATE OR REPLACE FUNCTION ebox(float8, float8, float8, float8)
   6.231 +  RETURNS ebox
   6.232 +  LANGUAGE C IMMUTABLE STRICT
   6.233 +  AS '$libdir/latlon-v0004', 'pgl_create_ebox';
   6.234 +
   6.235 +CREATE OR REPLACE FUNCTION ebox(epoint, epoint)
   6.236 +  RETURNS ebox
   6.237 +  LANGUAGE C IMMUTABLE STRICT
   6.238 +  AS '$libdir/latlon-v0004', 'pgl_create_ebox_from_epoints';
   6.239 +
   6.240 +CREATE OR REPLACE FUNCTION ecircle(float8, float8, float8)
   6.241 +  RETURNS ecircle
   6.242 +  LANGUAGE C IMMUTABLE STRICT
   6.243 +  AS '$libdir/latlon-v0004', 'pgl_create_ecircle';
   6.244 +
   6.245 +CREATE OR REPLACE FUNCTION ecircle(epoint, float8)
   6.246 +  RETURNS ecircle
   6.247 +  LANGUAGE C IMMUTABLE STRICT
   6.248 +  AS '$libdir/latlon-v0004', 'pgl_create_ecircle_from_epoint';
   6.249 +
   6.250 +CREATE OR REPLACE FUNCTION latitude(epoint)
   6.251 +  RETURNS float8
   6.252 +  LANGUAGE C IMMUTABLE STRICT
   6.253 +  AS '$libdir/latlon-v0004', 'pgl_epoint_lat';
   6.254 +
   6.255 +CREATE OR REPLACE FUNCTION longitude(epoint)
   6.256 +  RETURNS float8
   6.257 +  LANGUAGE C IMMUTABLE STRICT
   6.258 +  AS '$libdir/latlon-v0004', 'pgl_epoint_lon';
   6.259 +
   6.260 +CREATE OR REPLACE FUNCTION min_latitude(ebox)
   6.261 +  RETURNS float8
   6.262 +  LANGUAGE C IMMUTABLE STRICT
   6.263 +  AS '$libdir/latlon-v0004', 'pgl_ebox_lat_min';
   6.264 +
   6.265 +CREATE OR REPLACE FUNCTION max_latitude(ebox)
   6.266 +  RETURNS float8
   6.267 +  LANGUAGE C IMMUTABLE STRICT
   6.268 +  AS '$libdir/latlon-v0004', 'pgl_ebox_lat_max';
   6.269 +
   6.270 +CREATE OR REPLACE FUNCTION min_longitude(ebox)
   6.271 +  RETURNS float8
   6.272 +  LANGUAGE C IMMUTABLE STRICT
   6.273 +  AS '$libdir/latlon-v0004', 'pgl_ebox_lon_min';
   6.274 +
   6.275 +CREATE OR REPLACE FUNCTION max_longitude(ebox)
   6.276 +  RETURNS float8
   6.277 +  LANGUAGE C IMMUTABLE STRICT
   6.278 +  AS '$libdir/latlon-v0004', 'pgl_ebox_lon_max';
   6.279 +
   6.280 +CREATE OR REPLACE FUNCTION center(ecircle)
   6.281 +  RETURNS epoint
   6.282 +  LANGUAGE C IMMUTABLE STRICT
   6.283 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_center';
   6.284 +
   6.285 +CREATE OR REPLACE FUNCTION radius(ecircle)
   6.286 +  RETURNS float8
   6.287 +  LANGUAGE C IMMUTABLE STRICT
   6.288 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_radius';
   6.289 +
   6.290 +CREATE OR REPLACE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   6.291 +  RETURNS boolean
   6.292 +  LANGUAGE C IMMUTABLE STRICT
   6.293 +  AS '$libdir/latlon-v0004', 'pgl_epoint_ebox_overlap';
   6.294 +
   6.295 +CREATE OR REPLACE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   6.296 +  RETURNS boolean
   6.297 +  LANGUAGE C IMMUTABLE STRICT
   6.298 +  AS '$libdir/latlon-v0004', 'pgl_epoint_ecircle_overlap';
   6.299 +
   6.300 +CREATE OR REPLACE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   6.301 +  RETURNS boolean
   6.302 +  LANGUAGE C IMMUTABLE STRICT
   6.303 +  AS '$libdir/latlon-v0004', 'pgl_epoint_ecluster_overlap';
   6.304 +
   6.305 +CREATE OR REPLACE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
   6.306 +  RETURNS boolean
   6.307 +  LANGUAGE C IMMUTABLE STRICT
   6.308 +  AS '$libdir/latlon-v0004', 'pgl_epoint_ecluster_may_overlap';
   6.309 +
   6.310 +CREATE OR REPLACE FUNCTION ebox_overlap_proc(ebox, ebox)
   6.311 +  RETURNS boolean
   6.312 +  LANGUAGE C IMMUTABLE STRICT
   6.313 +  AS '$libdir/latlon-v0004', 'pgl_ebox_overlap';
   6.314 +
   6.315 +CREATE OR REPLACE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
   6.316 +  RETURNS boolean
   6.317 +  LANGUAGE C IMMUTABLE STRICT
   6.318 +  AS '$libdir/latlon-v0004', 'pgl_ebox_ecircle_may_overlap';
   6.319 +
   6.320 +CREATE OR REPLACE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
   6.321 +  RETURNS boolean
   6.322 +  LANGUAGE C IMMUTABLE STRICT
   6.323 +  AS '$libdir/latlon-v0004', 'pgl_ebox_ecluster_may_overlap';
   6.324 +
   6.325 +CREATE OR REPLACE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   6.326 +  RETURNS boolean
   6.327 +  LANGUAGE C IMMUTABLE STRICT
   6.328 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_overlap';
   6.329 +
   6.330 +CREATE OR REPLACE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   6.331 +  RETURNS boolean
   6.332 +  LANGUAGE C IMMUTABLE STRICT
   6.333 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_ecluster_overlap';
   6.334 +
   6.335 +CREATE OR REPLACE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
   6.336 +  RETURNS boolean
   6.337 +  LANGUAGE C IMMUTABLE STRICT
   6.338 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_ecluster_may_overlap';
   6.339 +
   6.340 +CREATE FUNCTION ecluster_overlap_proc(ecluster, ecluster)
   6.341 +  RETURNS boolean
   6.342 +  LANGUAGE C IMMUTABLE STRICT
   6.343 +  AS '$libdir/latlon-v0004', 'pgl_ecluster_overlap';
   6.344 +
   6.345 +CREATE OR REPLACE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
   6.346 +  RETURNS boolean
   6.347 +  LANGUAGE C IMMUTABLE STRICT
   6.348 +  AS '$libdir/latlon-v0004', 'pgl_ecluster_may_overlap';
   6.349 +
   6.350 +CREATE FUNCTION ecluster_contains_proc(ecluster, ecluster)
   6.351 +  RETURNS boolean
   6.352 +  LANGUAGE C IMMUTABLE STRICT
   6.353 +  AS '$libdir/latlon-v0004', 'pgl_ecluster_contains';
   6.354 +
   6.355 +CREATE OR REPLACE FUNCTION epoint_distance_proc(epoint, epoint)
   6.356 +  RETURNS float8
   6.357 +  LANGUAGE C IMMUTABLE STRICT
   6.358 +  AS '$libdir/latlon-v0004', 'pgl_epoint_distance';
   6.359 +
   6.360 +CREATE OR REPLACE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   6.361 +  RETURNS float8
   6.362 +  LANGUAGE C IMMUTABLE STRICT
   6.363 +  AS '$libdir/latlon-v0004', 'pgl_epoint_ecircle_distance';
   6.364 +
   6.365 +CREATE OR REPLACE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   6.366 +  RETURNS float8
   6.367 +  LANGUAGE C IMMUTABLE STRICT
   6.368 +  AS '$libdir/latlon-v0004', 'pgl_epoint_ecluster_distance';
   6.369 +
   6.370 +CREATE OR REPLACE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   6.371 +  RETURNS float8
   6.372 +  LANGUAGE C IMMUTABLE STRICT
   6.373 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_distance';
   6.374 +
   6.375 +CREATE OR REPLACE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   6.376 +  RETURNS float8
   6.377 +  LANGUAGE C IMMUTABLE STRICT
   6.378 +  AS '$libdir/latlon-v0004', 'pgl_ecircle_ecluster_distance';
   6.379 +
   6.380 +CREATE FUNCTION ecluster_distance_proc(ecluster, ecluster)
   6.381 +  RETURNS float8
   6.382 +  LANGUAGE C IMMUTABLE STRICT
   6.383 +  AS '$libdir/latlon-v0004', 'pgl_ecluster_distance';
   6.384 +
   6.385 +CREATE OPERATOR && (
   6.386 +  leftarg = ecluster,
   6.387 +  rightarg = ecluster,
   6.388 +  procedure = ecluster_overlap_proc,
   6.389 +  commutator = &&,
   6.390 +  restrict = areasel,
   6.391 +  join = areajoinsel
   6.392 +);
   6.393 +
   6.394 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle)
   6.395 +  RETURNS boolean
   6.396 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   6.397 +
   6.398 +CREATE OPERATOR && (
   6.399 +  leftarg = ebox,
   6.400 +  rightarg = ecircle,
   6.401 +  procedure = ebox_ecircle_overlap_castwrap,
   6.402 +  commutator = &&,
   6.403 +  restrict = areasel,
   6.404 +  join = areajoinsel
   6.405 +);
   6.406 +
   6.407 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox)
   6.408 +  RETURNS boolean
   6.409 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
   6.410 +
   6.411 +CREATE OPERATOR && (
   6.412 +  leftarg = ecircle,
   6.413 +  rightarg = ebox,
   6.414 +  procedure = ebox_ecircle_overlap_castwrap,
   6.415 +  commutator = &&,
   6.416 +  restrict = areasel,
   6.417 +  join = areajoinsel
   6.418 +);
   6.419 +
   6.420 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster)
   6.421 +  RETURNS boolean
   6.422 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   6.423 +
   6.424 +CREATE OPERATOR && (
   6.425 +  leftarg = ebox,
   6.426 +  rightarg = ecluster,
   6.427 +  procedure = ebox_ecluster_overlap_castwrap,
   6.428 +  commutator = &&,
   6.429 +  restrict = areasel,
   6.430 +  join = areajoinsel
   6.431 +);
   6.432 +
   6.433 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox)
   6.434 +  RETURNS boolean
   6.435 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
   6.436 +
   6.437 +CREATE OPERATOR && (
   6.438 +  leftarg = ecluster,
   6.439 +  rightarg = ebox,
   6.440 +  procedure = ebox_ecluster_overlap_castwrap,
   6.441 +  commutator = &&,
   6.442 +  restrict = areasel,
   6.443 +  join = areajoinsel
   6.444 +);
   6.445 +
   6.446 +
   6.447 +CREATE OPERATOR @> (
   6.448 +  leftarg = ebox,
   6.449 +  rightarg = epoint,
   6.450 +  procedure = epoint_ebox_overlap_commutator,
   6.451 +  commutator = <@,
   6.452 +  restrict = areasel,
   6.453 +  join = areajoinsel
   6.454 +);
   6.455 +
   6.456 +CREATE OPERATOR <@ (
   6.457 +  leftarg = epoint,
   6.458 +  rightarg = ebox,
   6.459 +  procedure = epoint_ebox_overlap_proc,
   6.460 +  commutator = @>,
   6.461 +  restrict = areasel,
   6.462 +  join = areajoinsel
   6.463 +);
   6.464 +
   6.465 +CREATE OPERATOR @> (
   6.466 +  leftarg = ecluster,
   6.467 +  rightarg = epoint,
   6.468 +  procedure = epoint_ecluster_overlap_commutator,
   6.469 +  commutator = <@,
   6.470 +  restrict = areasel,
   6.471 +  join = areajoinsel
   6.472 +);
   6.473 +
   6.474 +CREATE OPERATOR <@ (
   6.475 +  leftarg = epoint,
   6.476 +  rightarg = ecluster,
   6.477 +  procedure = epoint_ecluster_overlap_proc,
   6.478 +  commutator = <@,
   6.479 +  restrict = areasel,
   6.480 +  join = areajoinsel
   6.481 +);
   6.482 +
   6.483 +CREATE OPERATOR @> (
   6.484 +  leftarg = ecluster,
   6.485 +  rightarg = ecluster,
   6.486 +  procedure = ecluster_contains_proc,
   6.487 +  commutator = <@,
   6.488 +  restrict = areasel,
   6.489 +  join = areajoinsel
   6.490 +);
   6.491 +
   6.492 +CREATE FUNCTION ecluster_contains_commutator(ecluster, ecluster)
   6.493 +  RETURNS boolean
   6.494 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1';
   6.495 +
   6.496 +CREATE OPERATOR <@ (
   6.497 +  leftarg = ecluster,
   6.498 +  rightarg = ecluster,
   6.499 +  procedure = ecluster_contains_commutator,
   6.500 +  commutator = @>,
   6.501 +  restrict = areasel,
   6.502 +  join = areajoinsel
   6.503 +);
   6.504 +
   6.505 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster)
   6.506 +  RETURNS boolean
   6.507 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2';
   6.508 +
   6.509 +CREATE OPERATOR @> (
   6.510 +  leftarg = ebox,
   6.511 +  rightarg = ecluster,
   6.512 +  procedure = ebox_ecluster_contains_castwrap,
   6.513 +  commutator = <@,
   6.514 +  restrict = areasel,
   6.515 +  join = areajoinsel
   6.516 +);
   6.517 +
   6.518 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox)
   6.519 +  RETURNS boolean
   6.520 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1';
   6.521 +
   6.522 +CREATE OPERATOR <@ (
   6.523 +  leftarg = ecluster,
   6.524 +  rightarg = ebox,
   6.525 +  procedure = ebox_ecluster_contains_castwrap,
   6.526 +  commutator = @>,
   6.527 +  restrict = areasel,
   6.528 +  join = areajoinsel
   6.529 +);
   6.530 +
   6.531 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox)
   6.532 +  RETURNS boolean
   6.533 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 @> $2::ecluster';
   6.534 +
   6.535 +CREATE OPERATOR @> (
   6.536 +  leftarg = ecluster,
   6.537 +  rightarg = ebox,
   6.538 +  procedure = ecluster_ebox_contains_castwrap,
   6.539 +  commutator = <@,
   6.540 +  restrict = areasel,
   6.541 +  join = areajoinsel
   6.542 +);
   6.543 +
   6.544 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster)
   6.545 +  RETURNS boolean
   6.546 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1::ecluster';
   6.547 +
   6.548 +CREATE OPERATOR <@ (
   6.549 +  leftarg = ebox,
   6.550 +  rightarg = ecluster,
   6.551 +  procedure = ecluster_ebox_contains_castwrap,
   6.552 +  commutator = @>,
   6.553 +  restrict = areasel,
   6.554 +  join = areajoinsel
   6.555 +);
   6.556 +
   6.557 +
   6.558 +CREATE OPERATOR <-> (
   6.559 +  leftarg = ecluster,
   6.560 +  rightarg = ecluster,
   6.561 +  procedure = ecluster_distance_proc,
   6.562 +  commutator = <->
   6.563 +);
   6.564 +
   6.565 +CREATE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox)
   6.566 +  RETURNS float8
   6.567 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
   6.568 +
   6.569 +CREATE OPERATOR <-> (
   6.570 +  leftarg = epoint,
   6.571 +  rightarg = ebox,
   6.572 +  procedure = epoint_ebox_distance_castwrap,
   6.573 +  commutator = <->
   6.574 +);
   6.575 +
   6.576 +CREATE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint)
   6.577 +  RETURNS float8
   6.578 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
   6.579 +
   6.580 +CREATE OPERATOR <-> (
   6.581 +  leftarg = ebox,
   6.582 +  rightarg = epoint,
   6.583 +  procedure = epoint_ebox_distance_castwrap,
   6.584 +  commutator = <->
   6.585 +);
   6.586 +
   6.587 +CREATE FUNCTION ebox_distance_castwrap(ebox, ebox)
   6.588 +  RETURNS float8
   6.589 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2::ecluster';
   6.590 +
   6.591 +CREATE OPERATOR <-> (
   6.592 +  leftarg = ebox,
   6.593 +  rightarg = ebox,
   6.594 +  procedure = ebox_distance_castwrap,
   6.595 +  commutator = <->
   6.596 +);
   6.597 +
   6.598 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle)
   6.599 +  RETURNS float8
   6.600 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
   6.601 +
   6.602 +CREATE OPERATOR <-> (
   6.603 +  leftarg = ebox,
   6.604 +  rightarg = ecircle,
   6.605 +  procedure = ebox_ecircle_distance_castwrap,
   6.606 +  commutator = <->
   6.607 +);
   6.608 +
   6.609 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox)
   6.610 +  RETURNS float8
   6.611 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
   6.612 +
   6.613 +CREATE OPERATOR <-> (
   6.614 +  leftarg = ecircle,
   6.615 +  rightarg = ebox,
   6.616 +  procedure = ebox_ecircle_distance_castwrap,
   6.617 +  commutator = <->
   6.618 +);
   6.619 +
   6.620 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster)
   6.621 +  RETURNS float8
   6.622 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
   6.623 +
   6.624 +CREATE OPERATOR <-> (
   6.625 +  leftarg = ebox,
   6.626 +  rightarg = ecluster,
   6.627 +  procedure = ebox_ecluster_distance_castwrap,
   6.628 +  commutator = <->
   6.629 +);
   6.630 +
   6.631 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox)
   6.632 +  RETURNS float8
   6.633 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
   6.634 +
   6.635 +CREATE OPERATOR <-> (
   6.636 +  leftarg = ecluster,
   6.637 +  rightarg = ebox,
   6.638 +  procedure = ebox_ecluster_distance_castwrap,
   6.639 +  commutator = <->
   6.640 +);
   6.641 +
   6.642 +DROP OPERATOR CLASS epoint_ops USING gist;
   6.643 +DROP OPERATOR CLASS ecircle_ops USING gist;
   6.644 +DROP OPERATOR CLASS ecluster_ops USING gist;
   6.645 +
   6.646 +CREATE OR REPLACE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
   6.647 +  RETURNS boolean
   6.648 +  LANGUAGE C STRICT
   6.649 +  AS '$libdir/latlon-v0004', 'pgl_gist_consistent';
   6.650 +
   6.651 +CREATE OR REPLACE FUNCTION pgl_gist_union(internal, internal)
   6.652 +  RETURNS internal
   6.653 +  LANGUAGE C STRICT
   6.654 +  AS '$libdir/latlon-v0004', 'pgl_gist_union';
   6.655 +
   6.656 +CREATE OR REPLACE FUNCTION pgl_gist_compress_epoint(internal)
   6.657 +  RETURNS internal
   6.658 +  LANGUAGE C STRICT
   6.659 +  AS '$libdir/latlon-v0004', 'pgl_gist_compress_epoint';
   6.660 +
   6.661 +CREATE OR REPLACE FUNCTION pgl_gist_compress_ecircle(internal)
   6.662 +  RETURNS internal
   6.663 +  LANGUAGE C STRICT
   6.664 +  AS '$libdir/latlon-v0004', 'pgl_gist_compress_ecircle';
   6.665 +
   6.666 +CREATE OR REPLACE FUNCTION pgl_gist_compress_ecluster(internal)
   6.667 +  RETURNS internal
   6.668 +  LANGUAGE C STRICT
   6.669 +  AS '$libdir/latlon-v0004', 'pgl_gist_compress_ecluster';
   6.670 +
   6.671 +CREATE OR REPLACE FUNCTION pgl_gist_decompress(internal)
   6.672 +  RETURNS internal
   6.673 +  LANGUAGE C STRICT
   6.674 +  AS '$libdir/latlon-v0004', 'pgl_gist_decompress';
   6.675 +
   6.676 +CREATE OR REPLACE FUNCTION pgl_gist_penalty(internal, internal, internal)
   6.677 +  RETURNS internal
   6.678 +  LANGUAGE C STRICT
   6.679 +  AS '$libdir/latlon-v0004', 'pgl_gist_penalty';
   6.680 +
   6.681 +CREATE OR REPLACE FUNCTION pgl_gist_picksplit(internal, internal)
   6.682 +  RETURNS internal
   6.683 +  LANGUAGE C STRICT
   6.684 +  AS '$libdir/latlon-v0004', 'pgl_gist_picksplit';
   6.685 +
   6.686 +CREATE OR REPLACE FUNCTION pgl_gist_same(internal, internal, internal)
   6.687 +  RETURNS internal
   6.688 +  LANGUAGE C STRICT
   6.689 +  AS '$libdir/latlon-v0004', 'pgl_gist_same';
   6.690 +
   6.691 +CREATE OR REPLACE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
   6.692 +  RETURNS internal
   6.693 +  LANGUAGE C STRICT
   6.694 +  AS '$libdir/latlon-v0004', 'pgl_gist_distance';
   6.695 +
   6.696 +CREATE OPERATOR CLASS epoint_ops
   6.697 +  DEFAULT FOR TYPE epoint USING gist AS
   6.698 +  OPERATOR  11 = ,
   6.699 +  OPERATOR  22 &&  (epoint, ebox),
   6.700 +  OPERATOR 222 <@  (epoint, ebox),
   6.701 +  OPERATOR  23 &&  (epoint, ecircle),
   6.702 +  OPERATOR  24 &&  (epoint, ecluster),
   6.703 +  OPERATOR 124 &&+ (epoint, ecluster),
   6.704 +  OPERATOR 224 <@  (epoint, ecluster),
   6.705 +  OPERATOR  31 <-> (epoint, epoint) FOR ORDER BY float_ops,
   6.706 +  OPERATOR  32 <-> (epoint, ebox) FOR ORDER BY float_ops,
   6.707 +  OPERATOR  33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
   6.708 +  OPERATOR  34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
   6.709 +  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
   6.710 +  FUNCTION 2 pgl_gist_union(internal, internal),
   6.711 +  FUNCTION 3 pgl_gist_compress_epoint(internal),
   6.712 +  FUNCTION 4 pgl_gist_decompress(internal),
   6.713 +  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
   6.714 +  FUNCTION 6 pgl_gist_picksplit(internal, internal),
   6.715 +  FUNCTION 7 pgl_gist_same(internal, internal, internal),
   6.716 +  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
   6.717 +  STORAGE ekey_point;
   6.718 +
   6.719 +CREATE OPERATOR CLASS ecircle_ops
   6.720 +  DEFAULT FOR TYPE ecircle USING gist AS
   6.721 +  OPERATOR  13 = ,
   6.722 +  OPERATOR  21 &&  (ecircle, epoint),
   6.723 +  OPERATOR  22 &&  (ecircle, ebox),
   6.724 +  OPERATOR 122 &&+ (ecircle, ebox),
   6.725 +  OPERATOR  23 &&  (ecircle, ecircle),
   6.726 +  OPERATOR  24 &&  (ecircle, ecluster),
   6.727 +  OPERATOR 124 &&+ (ecircle, ecluster),
   6.728 +  OPERATOR  31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
   6.729 +  OPERATOR  32 <-> (ecircle, ebox) FOR ORDER BY float_ops,
   6.730 +  OPERATOR  33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
   6.731 +  OPERATOR  34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
   6.732 +  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
   6.733 +  FUNCTION 2 pgl_gist_union(internal, internal),
   6.734 +  FUNCTION 3 pgl_gist_compress_ecircle(internal),
   6.735 +  FUNCTION 4 pgl_gist_decompress(internal),
   6.736 +  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
   6.737 +  FUNCTION 6 pgl_gist_picksplit(internal, internal),
   6.738 +  FUNCTION 7 pgl_gist_same(internal, internal, internal),
   6.739 +  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
   6.740 +  STORAGE ekey_area;
   6.741 +
   6.742 +CREATE OPERATOR CLASS ecluster_ops
   6.743 +  DEFAULT FOR TYPE ecluster USING gist AS
   6.744 +  OPERATOR  21 &&  (ecluster, epoint),
   6.745 +  OPERATOR 121 &&+ (ecluster, epoint),
   6.746 +  OPERATOR 221 @>  (ecluster, epoint),
   6.747 +  OPERATOR  22 &&  (ecluster, ebox),
   6.748 +  OPERATOR 122 &&+ (ecluster, ebox),
   6.749 +  OPERATOR 222 @>  (ecluster, ebox),
   6.750 +  OPERATOR 322 <@  (ecluster, ebox),
   6.751 +  OPERATOR  23 &&  (ecluster, ecircle),
   6.752 +  OPERATOR 123 &&+ (ecluster, ecircle),
   6.753 +  OPERATOR  24 &&  (ecluster, ecluster),
   6.754 +  OPERATOR 124 &&+ (ecluster, ecluster),
   6.755 +  OPERATOR 224 @>  (ecluster, ecluster),
   6.756 +  OPERATOR 324 <@  (ecluster, ecluster),
   6.757 +  OPERATOR  31 <-> (ecluster, epoint) FOR ORDER BY float_ops,
   6.758 +  OPERATOR  32 <-> (ecluster, ebox) FOR ORDER BY float_ops,
   6.759 +  OPERATOR  33 <-> (ecluster, ecircle) FOR ORDER BY float_ops,
   6.760 +  OPERATOR  34 <-> (ecluster, ecluster) FOR ORDER BY float_ops,
   6.761 +  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
   6.762 +  FUNCTION 2 pgl_gist_union(internal, internal),
   6.763 +  FUNCTION 3 pgl_gist_compress_ecluster(internal),
   6.764 +  FUNCTION 4 pgl_gist_decompress(internal),
   6.765 +  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
   6.766 +  FUNCTION 6 pgl_gist_picksplit(internal, internal),
   6.767 +  FUNCTION 7 pgl_gist_same(internal, internal, internal),
   6.768 +  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
   6.769 +  STORAGE ekey_area;
   6.770 +
     7.1 --- a/latlon--0.3.sql	Sat Sep 03 16:00:22 2016 +0200
     7.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.3 @@ -1,1335 +0,0 @@
     7.4 -
     7.5 -----------------------------------------
     7.6 --- forward declarations (shell types) --
     7.7 -----------------------------------------
     7.8 -
     7.9 -CREATE TYPE epoint;
    7.10 -CREATE TYPE ebox;
    7.11 -CREATE TYPE ecircle;
    7.12 -CREATE TYPE ecluster;
    7.13 -
    7.14 -
    7.15 -------------------------------------------------------------
    7.16 --- dummy input/output functions for dummy index key types --
    7.17 -------------------------------------------------------------
    7.18 -
    7.19 -CREATE FUNCTION ekey_point_in_dummy(cstring)
    7.20 -  RETURNS ekey_point
    7.21 -  LANGUAGE C IMMUTABLE STRICT
    7.22 -  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    7.23 -
    7.24 -CREATE FUNCTION ekey_point_out_dummy(ekey_point)
    7.25 -  RETURNS cstring
    7.26 -  LANGUAGE C IMMUTABLE STRICT
    7.27 -  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    7.28 -
    7.29 -CREATE FUNCTION ekey_area_in_dummy(cstring)
    7.30 -  RETURNS ekey_area
    7.31 -  LANGUAGE C IMMUTABLE STRICT
    7.32 -  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    7.33 -
    7.34 -CREATE FUNCTION ekey_area_out_dummy(ekey_area)
    7.35 -  RETURNS cstring
    7.36 -  LANGUAGE C IMMUTABLE STRICT
    7.37 -  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    7.38 -
    7.39 -
    7.40 ---------------------------
    7.41 --- text input functions --
    7.42 ---------------------------
    7.43 -
    7.44 -CREATE FUNCTION epoint_in(cstring)
    7.45 -  RETURNS epoint
    7.46 -  LANGUAGE C IMMUTABLE STRICT
    7.47 -  AS '$libdir/latlon-v0003', 'pgl_epoint_in';
    7.48 -
    7.49 -CREATE FUNCTION ebox_in(cstring)
    7.50 -  RETURNS ebox
    7.51 -  LANGUAGE C IMMUTABLE STRICT
    7.52 -  AS '$libdir/latlon-v0003', 'pgl_ebox_in';
    7.53 -
    7.54 -CREATE FUNCTION ecircle_in(cstring)
    7.55 -  RETURNS ecircle
    7.56 -  LANGUAGE C IMMUTABLE STRICT
    7.57 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_in';
    7.58 -
    7.59 -CREATE FUNCTION ecluster_in(cstring)
    7.60 -  RETURNS ecluster
    7.61 -  LANGUAGE C IMMUTABLE STRICT
    7.62 -  AS '$libdir/latlon-v0003', 'pgl_ecluster_in';
    7.63 -
    7.64 -
    7.65 ----------------------------
    7.66 --- text output functions --
    7.67 ----------------------------
    7.68 -
    7.69 -CREATE FUNCTION epoint_out(epoint)
    7.70 -  RETURNS cstring
    7.71 -  LANGUAGE C IMMUTABLE STRICT
    7.72 -  AS '$libdir/latlon-v0003', 'pgl_epoint_out';
    7.73 -
    7.74 -CREATE FUNCTION ebox_out(ebox)
    7.75 -  RETURNS cstring
    7.76 -  LANGUAGE C IMMUTABLE STRICT
    7.77 -  AS '$libdir/latlon-v0003', 'pgl_ebox_out';
    7.78 -
    7.79 -CREATE FUNCTION ecircle_out(ecircle)
    7.80 -  RETURNS cstring
    7.81 -  LANGUAGE C IMMUTABLE STRICT
    7.82 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_out';
    7.83 -
    7.84 -CREATE FUNCTION ecluster_out(ecluster)
    7.85 -  RETURNS cstring
    7.86 -  LANGUAGE C IMMUTABLE STRICT
    7.87 -  AS '$libdir/latlon-v0003', 'pgl_ecluster_out';
    7.88 -
    7.89 -
    7.90 ---------------------------
    7.91 --- binary I/O functions --
    7.92 ---------------------------
    7.93 -
    7.94 -CREATE FUNCTION epoint_recv(internal)
    7.95 -  RETURNS epoint
    7.96 -  LANGUAGE C IMMUTABLE STRICT
    7.97 -  AS '$libdir/latlon-v0003', 'pgl_epoint_recv';
    7.98 -
    7.99 -CREATE FUNCTION ebox_recv(internal)
   7.100 -  RETURNS ebox
   7.101 -  LANGUAGE C IMMUTABLE STRICT
   7.102 -  AS '$libdir/latlon-v0003', 'pgl_ebox_recv';
   7.103 -
   7.104 -CREATE FUNCTION ecircle_recv(internal)
   7.105 -  RETURNS ecircle
   7.106 -  LANGUAGE C IMMUTABLE STRICT
   7.107 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_recv';
   7.108 -
   7.109 -CREATE FUNCTION epoint_send(epoint)
   7.110 -  RETURNS bytea
   7.111 -  LANGUAGE C IMMUTABLE STRICT
   7.112 -  AS '$libdir/latlon-v0003', 'pgl_epoint_send';
   7.113 -
   7.114 -CREATE FUNCTION ebox_send(ebox)
   7.115 -  RETURNS bytea
   7.116 -  LANGUAGE C IMMUTABLE STRICT
   7.117 -  AS '$libdir/latlon-v0003', 'pgl_ebox_send';
   7.118 -
   7.119 -CREATE FUNCTION ecircle_send(ecircle)
   7.120 -  RETURNS bytea
   7.121 -  LANGUAGE C IMMUTABLE STRICT
   7.122 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_send';
   7.123 -
   7.124 -
   7.125 ------------------------------------------------
   7.126 --- type definitions of dummy index key types --
   7.127 ------------------------------------------------
   7.128 -
   7.129 -CREATE TYPE ekey_point (
   7.130 -  internallength = 8,
   7.131 -  input = ekey_point_in_dummy,
   7.132 -  output = ekey_point_out_dummy,
   7.133 -  alignment = char );
   7.134 -
   7.135 -CREATE TYPE ekey_area (
   7.136 -  internallength = 9,
   7.137 -  input = ekey_area_in_dummy,
   7.138 -  output = ekey_area_out_dummy,
   7.139 -  alignment = char );
   7.140 -
   7.141 -
   7.142 -------------------------------------------
   7.143 --- definitions of geographic data types --
   7.144 -------------------------------------------
   7.145 -
   7.146 -CREATE TYPE epoint (
   7.147 -  internallength = 16,
   7.148 -  input = epoint_in,
   7.149 -  output = epoint_out,
   7.150 -  receive = epoint_recv,
   7.151 -  send = epoint_send,
   7.152 -  alignment = double );
   7.153 -
   7.154 -CREATE TYPE ebox (
   7.155 -  internallength = 32,
   7.156 -  input = ebox_in,
   7.157 -  output = ebox_out,
   7.158 -  receive = ebox_recv,
   7.159 -  send = ebox_send,
   7.160 -  alignment = double );
   7.161 -
   7.162 -CREATE TYPE ecircle (
   7.163 -  internallength = 24,
   7.164 -  input = ecircle_in,
   7.165 -  output = ecircle_out,
   7.166 -  receive = ecircle_recv,
   7.167 -  send = ecircle_send,
   7.168 -  alignment = double );
   7.169 -
   7.170 -CREATE TYPE ecluster (
   7.171 -  internallength = VARIABLE,
   7.172 -  input = ecluster_in,
   7.173 -  output = ecluster_out,
   7.174 -  alignment = double,
   7.175 -  storage = external );
   7.176 -
   7.177 -
   7.178 ---------------------
   7.179 --- B-tree support --
   7.180 ---------------------
   7.181 -
   7.182 --- begin of B-tree support for epoint
   7.183 -
   7.184 -CREATE FUNCTION epoint_btree_lt(epoint, epoint)
   7.185 -  RETURNS boolean
   7.186 -  LANGUAGE C IMMUTABLE STRICT
   7.187 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_lt';
   7.188 -
   7.189 -CREATE FUNCTION epoint_btree_le(epoint, epoint)
   7.190 -  RETURNS boolean
   7.191 -  LANGUAGE C IMMUTABLE STRICT
   7.192 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_le';
   7.193 -
   7.194 -CREATE FUNCTION epoint_btree_eq(epoint, epoint)
   7.195 -  RETURNS boolean
   7.196 -  LANGUAGE C IMMUTABLE STRICT
   7.197 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_eq';
   7.198 -
   7.199 -CREATE FUNCTION epoint_btree_ne(epoint, epoint)
   7.200 -  RETURNS boolean
   7.201 -  LANGUAGE C IMMUTABLE STRICT
   7.202 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_ne';
   7.203 -
   7.204 -CREATE FUNCTION epoint_btree_ge(epoint, epoint)
   7.205 -  RETURNS boolean
   7.206 -  LANGUAGE C IMMUTABLE STRICT
   7.207 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_ge';
   7.208 -
   7.209 -CREATE FUNCTION epoint_btree_gt(epoint, epoint)
   7.210 -  RETURNS boolean
   7.211 -  LANGUAGE C IMMUTABLE STRICT
   7.212 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_gt';
   7.213 -
   7.214 -CREATE OPERATOR <<< (
   7.215 -  leftarg = epoint,
   7.216 -  rightarg = epoint,
   7.217 -  procedure = epoint_btree_lt,
   7.218 -  commutator = >>>,
   7.219 -  negator = >>>=,
   7.220 -  restrict = scalarltsel,
   7.221 -  join = scalarltjoinsel
   7.222 -);
   7.223 -
   7.224 -CREATE OPERATOR <<<= (
   7.225 -  leftarg = epoint,
   7.226 -  rightarg = epoint,
   7.227 -  procedure = epoint_btree_le,
   7.228 -  commutator = >>>=,
   7.229 -  negator = >>>,
   7.230 -  restrict = scalarltsel,
   7.231 -  join = scalarltjoinsel
   7.232 -);
   7.233 -
   7.234 -CREATE OPERATOR = (
   7.235 -  leftarg = epoint,
   7.236 -  rightarg = epoint,
   7.237 -  procedure = epoint_btree_eq,
   7.238 -  commutator = =,
   7.239 -  negator = <>,
   7.240 -  restrict = eqsel,
   7.241 -  join = eqjoinsel,
   7.242 -  merges
   7.243 -);
   7.244 -
   7.245 -CREATE OPERATOR <> (
   7.246 -  leftarg = epoint,
   7.247 -  rightarg = epoint,
   7.248 -  procedure = epoint_btree_eq,
   7.249 -  commutator = <>,
   7.250 -  negator = =,
   7.251 -  restrict = neqsel,
   7.252 -  join = neqjoinsel
   7.253 -);
   7.254 -
   7.255 -CREATE OPERATOR >>>= (
   7.256 -  leftarg = epoint,
   7.257 -  rightarg = epoint,
   7.258 -  procedure = epoint_btree_ge,
   7.259 -  commutator = <<<=,
   7.260 -  negator = <<<,
   7.261 -  restrict = scalargtsel,
   7.262 -  join = scalargtjoinsel
   7.263 -);
   7.264 -
   7.265 -CREATE OPERATOR >>> (
   7.266 -  leftarg = epoint,
   7.267 -  rightarg = epoint,
   7.268 -  procedure = epoint_btree_gt,
   7.269 -  commutator = <<<,
   7.270 -  negator = <<<=,
   7.271 -  restrict = scalargtsel,
   7.272 -  join = scalargtjoinsel
   7.273 -);
   7.274 -
   7.275 -CREATE FUNCTION epoint_btree_cmp(epoint, epoint)
   7.276 -  RETURNS int4
   7.277 -  LANGUAGE C IMMUTABLE STRICT
   7.278 -  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_cmp';
   7.279 -
   7.280 -CREATE OPERATOR CLASS epoint_btree_ops
   7.281 -  DEFAULT FOR TYPE epoint USING btree AS
   7.282 -  OPERATOR 1 <<< ,
   7.283 -  OPERATOR 2 <<<= ,
   7.284 -  OPERATOR 3 = ,
   7.285 -  OPERATOR 4 >>>= ,
   7.286 -  OPERATOR 5 >>> ,
   7.287 -  FUNCTION 1 epoint_btree_cmp(epoint, epoint);
   7.288 -
   7.289 --- end of B-tree support for epoint
   7.290 -
   7.291 --- begin of B-tree support for ebox
   7.292 -
   7.293 -CREATE FUNCTION ebox_btree_lt(ebox, ebox)
   7.294 -  RETURNS boolean
   7.295 -  LANGUAGE C IMMUTABLE STRICT
   7.296 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_lt';
   7.297 -
   7.298 -CREATE FUNCTION ebox_btree_le(ebox, ebox)
   7.299 -  RETURNS boolean
   7.300 -  LANGUAGE C IMMUTABLE STRICT
   7.301 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_le';
   7.302 -
   7.303 -CREATE FUNCTION ebox_btree_eq(ebox, ebox)
   7.304 -  RETURNS boolean
   7.305 -  LANGUAGE C IMMUTABLE STRICT
   7.306 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_eq';
   7.307 -
   7.308 -CREATE FUNCTION ebox_btree_ne(ebox, ebox)
   7.309 -  RETURNS boolean
   7.310 -  LANGUAGE C IMMUTABLE STRICT
   7.311 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_ne';
   7.312 -
   7.313 -CREATE FUNCTION ebox_btree_ge(ebox, ebox)
   7.314 -  RETURNS boolean
   7.315 -  LANGUAGE C IMMUTABLE STRICT
   7.316 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_ge';
   7.317 -
   7.318 -CREATE FUNCTION ebox_btree_gt(ebox, ebox)
   7.319 -  RETURNS boolean
   7.320 -  LANGUAGE C IMMUTABLE STRICT
   7.321 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_gt';
   7.322 -
   7.323 -CREATE OPERATOR <<< (
   7.324 -  leftarg = ebox,
   7.325 -  rightarg = ebox,
   7.326 -  procedure = ebox_btree_lt,
   7.327 -  commutator = >>>,
   7.328 -  negator = >>>=,
   7.329 -  restrict = scalarltsel,
   7.330 -  join = scalarltjoinsel
   7.331 -);
   7.332 -
   7.333 -CREATE OPERATOR <<<= (
   7.334 -  leftarg = ebox,
   7.335 -  rightarg = ebox,
   7.336 -  procedure = ebox_btree_le,
   7.337 -  commutator = >>>=,
   7.338 -  negator = >>>,
   7.339 -  restrict = scalarltsel,
   7.340 -  join = scalarltjoinsel
   7.341 -);
   7.342 -
   7.343 -CREATE OPERATOR = (
   7.344 -  leftarg = ebox,
   7.345 -  rightarg = ebox,
   7.346 -  procedure = ebox_btree_eq,
   7.347 -  commutator = =,
   7.348 -  negator = <>,
   7.349 -  restrict = eqsel,
   7.350 -  join = eqjoinsel,
   7.351 -  merges
   7.352 -);
   7.353 -
   7.354 -CREATE OPERATOR <> (
   7.355 -  leftarg = ebox,
   7.356 -  rightarg = ebox,
   7.357 -  procedure = ebox_btree_eq,
   7.358 -  commutator = <>,
   7.359 -  negator = =,
   7.360 -  restrict = neqsel,
   7.361 -  join = neqjoinsel
   7.362 -);
   7.363 -
   7.364 -CREATE OPERATOR >>>= (
   7.365 -  leftarg = ebox,
   7.366 -  rightarg = ebox,
   7.367 -  procedure = ebox_btree_ge,
   7.368 -  commutator = <<<=,
   7.369 -  negator = <<<,
   7.370 -  restrict = scalargtsel,
   7.371 -  join = scalargtjoinsel
   7.372 -);
   7.373 -
   7.374 -CREATE OPERATOR >>> (
   7.375 -  leftarg = ebox,
   7.376 -  rightarg = ebox,
   7.377 -  procedure = ebox_btree_gt,
   7.378 -  commutator = <<<,
   7.379 -  negator = <<<=,
   7.380 -  restrict = scalargtsel,
   7.381 -  join = scalargtjoinsel
   7.382 -);
   7.383 -
   7.384 -CREATE FUNCTION ebox_btree_cmp(ebox, ebox)
   7.385 -  RETURNS int4
   7.386 -  LANGUAGE C IMMUTABLE STRICT
   7.387 -  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_cmp';
   7.388 -
   7.389 -CREATE OPERATOR CLASS ebox_btree_ops
   7.390 -  DEFAULT FOR TYPE ebox USING btree AS
   7.391 -  OPERATOR 1 <<< ,
   7.392 -  OPERATOR 2 <<<= ,
   7.393 -  OPERATOR 3 = ,
   7.394 -  OPERATOR 4 >>>= ,
   7.395 -  OPERATOR 5 >>> ,
   7.396 -  FUNCTION 1 ebox_btree_cmp(ebox, ebox);
   7.397 -
   7.398 --- end of B-tree support for ebox
   7.399 -
   7.400 --- begin of B-tree support for ecircle
   7.401 -
   7.402 -CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   7.403 -  RETURNS boolean
   7.404 -  LANGUAGE C IMMUTABLE STRICT
   7.405 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_lt';
   7.406 -
   7.407 -CREATE FUNCTION ecircle_btree_le(ecircle, ecircle)
   7.408 -  RETURNS boolean
   7.409 -  LANGUAGE C IMMUTABLE STRICT
   7.410 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_le';
   7.411 -
   7.412 -CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   7.413 -  RETURNS boolean
   7.414 -  LANGUAGE C IMMUTABLE STRICT
   7.415 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_eq';
   7.416 -
   7.417 -CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   7.418 -  RETURNS boolean
   7.419 -  LANGUAGE C IMMUTABLE STRICT
   7.420 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_ne';
   7.421 -
   7.422 -CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   7.423 -  RETURNS boolean
   7.424 -  LANGUAGE C IMMUTABLE STRICT
   7.425 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_ge';
   7.426 -
   7.427 -CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   7.428 -  RETURNS boolean
   7.429 -  LANGUAGE C IMMUTABLE STRICT
   7.430 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_gt';
   7.431 -
   7.432 -CREATE OPERATOR <<< (
   7.433 -  leftarg = ecircle,
   7.434 -  rightarg = ecircle,
   7.435 -  procedure = ecircle_btree_lt,
   7.436 -  commutator = >>>,
   7.437 -  negator = >>>=,
   7.438 -  restrict = scalarltsel,
   7.439 -  join = scalarltjoinsel
   7.440 -);
   7.441 -
   7.442 -CREATE OPERATOR <<<= (
   7.443 -  leftarg = ecircle,
   7.444 -  rightarg = ecircle,
   7.445 -  procedure = ecircle_btree_le,
   7.446 -  commutator = >>>=,
   7.447 -  negator = >>>,
   7.448 -  restrict = scalarltsel,
   7.449 -  join = scalarltjoinsel
   7.450 -);
   7.451 -
   7.452 -CREATE OPERATOR = (
   7.453 -  leftarg = ecircle,
   7.454 -  rightarg = ecircle,
   7.455 -  procedure = ecircle_btree_eq,
   7.456 -  commutator = =,
   7.457 -  negator = <>,
   7.458 -  restrict = eqsel,
   7.459 -  join = eqjoinsel,
   7.460 -  merges
   7.461 -);
   7.462 -
   7.463 -CREATE OPERATOR <> (
   7.464 -  leftarg = ecircle,
   7.465 -  rightarg = ecircle,
   7.466 -  procedure = ecircle_btree_eq,
   7.467 -  commutator = <>,
   7.468 -  negator = =,
   7.469 -  restrict = neqsel,
   7.470 -  join = neqjoinsel
   7.471 -);
   7.472 -
   7.473 -CREATE OPERATOR >>>= (
   7.474 -  leftarg = ecircle,
   7.475 -  rightarg = ecircle,
   7.476 -  procedure = ecircle_btree_ge,
   7.477 -  commutator = <<<=,
   7.478 -  negator = <<<,
   7.479 -  restrict = scalargtsel,
   7.480 -  join = scalargtjoinsel
   7.481 -);
   7.482 -
   7.483 -CREATE OPERATOR >>> (
   7.484 -  leftarg = ecircle,
   7.485 -  rightarg = ecircle,
   7.486 -  procedure = ecircle_btree_gt,
   7.487 -  commutator = <<<,
   7.488 -  negator = <<<=,
   7.489 -  restrict = scalargtsel,
   7.490 -  join = scalargtjoinsel
   7.491 -);
   7.492 -
   7.493 -CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   7.494 -  RETURNS int4
   7.495 -  LANGUAGE C IMMUTABLE STRICT
   7.496 -  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_cmp';
   7.497 -
   7.498 -CREATE OPERATOR CLASS ecircle_btree_ops
   7.499 -  DEFAULT FOR TYPE ecircle USING btree AS
   7.500 -  OPERATOR 1 <<< ,
   7.501 -  OPERATOR 2 <<<= ,
   7.502 -  OPERATOR 3 = ,
   7.503 -  OPERATOR 4 >>>= ,
   7.504 -  OPERATOR 5 >>> ,
   7.505 -  FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle);
   7.506 -
   7.507 --- end of B-tree support for ecircle
   7.508 -
   7.509 -
   7.510 -----------------
   7.511 --- type casts --
   7.512 -----------------
   7.513 -
   7.514 -CREATE FUNCTION cast_epoint_to_ebox(epoint)
   7.515 -  RETURNS ebox
   7.516 -  LANGUAGE C IMMUTABLE STRICT
   7.517 -  AS '$libdir/latlon-v0003', 'pgl_epoint_to_ebox';
   7.518 -
   7.519 -CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint);
   7.520 -
   7.521 -CREATE FUNCTION cast_epoint_to_ecircle(epoint)
   7.522 -  RETURNS ecircle
   7.523 -  LANGUAGE C IMMUTABLE STRICT
   7.524 -  AS '$libdir/latlon-v0003', 'pgl_epoint_to_ecircle';
   7.525 -
   7.526 -CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint);
   7.527 -
   7.528 -CREATE FUNCTION cast_epoint_to_ecluster(epoint)
   7.529 -  RETURNS ecluster
   7.530 -  LANGUAGE C IMMUTABLE STRICT
   7.531 -  AS '$libdir/latlon-v0003', 'pgl_epoint_to_ecluster';
   7.532 -
   7.533 -CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint);
   7.534 -
   7.535 -CREATE FUNCTION cast_ebox_to_ecluster(ebox)
   7.536 -  RETURNS ecluster
   7.537 -  LANGUAGE C IMMUTABLE STRICT
   7.538 -  AS '$libdir/latlon-v0003', 'pgl_ebox_to_ecluster';
   7.539 -
   7.540 -CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox);
   7.541 -
   7.542 -
   7.543 ----------------------------
   7.544 --- constructor functions --
   7.545 ----------------------------
   7.546 -
   7.547 -CREATE FUNCTION epoint(float8, float8)
   7.548 -  RETURNS epoint
   7.549 -  LANGUAGE C IMMUTABLE STRICT
   7.550 -  AS '$libdir/latlon-v0003', 'pgl_create_epoint';
   7.551 -
   7.552 -CREATE FUNCTION epoint_latlon(float8, float8)
   7.553 -  RETURNS epoint
   7.554 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   7.555 -    SELECT epoint($1, $2)
   7.556 -  $$;
   7.557 -
   7.558 -CREATE FUNCTION epoint_lonlat(float8, float8)
   7.559 -  RETURNS epoint
   7.560 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   7.561 -    SELECT epoint($2, $1)
   7.562 -  $$;
   7.563 -
   7.564 -CREATE FUNCTION empty_ebox()
   7.565 -  RETURNS ebox
   7.566 -  LANGUAGE C IMMUTABLE STRICT
   7.567 -  AS '$libdir/latlon-v0003', 'pgl_create_empty_ebox';
   7.568 -
   7.569 -CREATE FUNCTION ebox(float8, float8, float8, float8)
   7.570 -  RETURNS ebox
   7.571 -  LANGUAGE C IMMUTABLE STRICT
   7.572 -  AS '$libdir/latlon-v0003', 'pgl_create_ebox';
   7.573 -
   7.574 -CREATE FUNCTION ebox(epoint, epoint)
   7.575 -  RETURNS ebox
   7.576 -  LANGUAGE C IMMUTABLE STRICT
   7.577 -  AS '$libdir/latlon-v0003', 'pgl_create_ebox_from_epoints';
   7.578 -
   7.579 -CREATE FUNCTION ecircle(float8, float8, float8)
   7.580 -  RETURNS ecircle
   7.581 -  LANGUAGE C IMMUTABLE STRICT
   7.582 -  AS '$libdir/latlon-v0003', 'pgl_create_ecircle';
   7.583 -
   7.584 -CREATE FUNCTION ecircle(epoint, float8)
   7.585 -  RETURNS ecircle
   7.586 -  LANGUAGE C IMMUTABLE STRICT
   7.587 -  AS '$libdir/latlon-v0003', 'pgl_create_ecircle_from_epoint';
   7.588 -
   7.589 -CREATE FUNCTION ecluster_concat(ecluster[])
   7.590 -  RETURNS ecluster
   7.591 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.592 -    SELECT array_to_string($1, ' ')::ecluster
   7.593 -  $$;
   7.594 -
   7.595 -CREATE FUNCTION ecluster_concat(ecluster, ecluster)
   7.596 -  RETURNS ecluster
   7.597 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.598 -    SELECT ($1::text || ' ' || $2::text)::ecluster
   7.599 -  $$;
   7.600 -
   7.601 -CREATE FUNCTION ecluster_create_multipoint(epoint[])
   7.602 -  RETURNS ecluster
   7.603 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.604 -    SELECT
   7.605 -      array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster
   7.606 -    FROM unnest($1)
   7.607 -  $$;
   7.608 -
   7.609 -CREATE FUNCTION ecluster_create_path(epoint[])
   7.610 -  RETURNS ecluster
   7.611 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.612 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   7.613 -      ('path (' || array_to_string($1, ' ') || ')')::ecluster
   7.614 -    END
   7.615 -    FROM array_to_string($1, ' ') AS "str"
   7.616 -  $$;
   7.617 -
   7.618 -CREATE FUNCTION ecluster_create_outline(epoint[])
   7.619 -  RETURNS ecluster
   7.620 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.621 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   7.622 -      ('outline (' || array_to_string($1, ' ') || ')')::ecluster
   7.623 -    END
   7.624 -    FROM array_to_string($1, ' ') AS "str"
   7.625 -  $$;
   7.626 -
   7.627 -CREATE FUNCTION ecluster_create_polygon(epoint[])
   7.628 -  RETURNS ecluster
   7.629 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.630 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   7.631 -      ('polygon (' || array_to_string($1, ' ') || ')')::ecluster
   7.632 -    END
   7.633 -    FROM array_to_string($1, ' ') AS "str"
   7.634 -  $$;
   7.635 -
   7.636 -
   7.637 -----------------------
   7.638 --- getter functions --
   7.639 -----------------------
   7.640 -
   7.641 -CREATE FUNCTION latitude(epoint)
   7.642 -  RETURNS float8
   7.643 -  LANGUAGE C IMMUTABLE STRICT
   7.644 -  AS '$libdir/latlon-v0003', 'pgl_epoint_lat';
   7.645 -
   7.646 -CREATE FUNCTION longitude(epoint)
   7.647 -  RETURNS float8
   7.648 -  LANGUAGE C IMMUTABLE STRICT
   7.649 -  AS '$libdir/latlon-v0003', 'pgl_epoint_lon';
   7.650 -
   7.651 -CREATE FUNCTION min_latitude(ebox)
   7.652 -  RETURNS float8
   7.653 -  LANGUAGE C IMMUTABLE STRICT
   7.654 -  AS '$libdir/latlon-v0003', 'pgl_ebox_lat_min';
   7.655 -
   7.656 -CREATE FUNCTION max_latitude(ebox)
   7.657 -  RETURNS float8
   7.658 -  LANGUAGE C IMMUTABLE STRICT
   7.659 -  AS '$libdir/latlon-v0003', 'pgl_ebox_lat_max';
   7.660 -
   7.661 -CREATE FUNCTION min_longitude(ebox)
   7.662 -  RETURNS float8
   7.663 -  LANGUAGE C IMMUTABLE STRICT
   7.664 -  AS '$libdir/latlon-v0003', 'pgl_ebox_lon_min';
   7.665 -
   7.666 -CREATE FUNCTION max_longitude(ebox)
   7.667 -  RETURNS float8
   7.668 -  LANGUAGE C IMMUTABLE STRICT
   7.669 -  AS '$libdir/latlon-v0003', 'pgl_ebox_lon_max';
   7.670 -
   7.671 -CREATE FUNCTION center(ecircle)
   7.672 -  RETURNS epoint
   7.673 -  LANGUAGE C IMMUTABLE STRICT
   7.674 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_center';
   7.675 -
   7.676 -CREATE FUNCTION radius(ecircle)
   7.677 -  RETURNS float8
   7.678 -  LANGUAGE C IMMUTABLE STRICT
   7.679 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_radius';
   7.680 -
   7.681 -CREATE FUNCTION ecluster_extract_points(ecluster)
   7.682 -  RETURNS SETOF epoint
   7.683 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.684 -    SELECT "match"[2]::epoint
   7.685 -    FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match"
   7.686 -  $$;
   7.687 -
   7.688 -CREATE FUNCTION ecluster_extract_paths(ecluster)
   7.689 -  RETURNS SETOF epoint[]
   7.690 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.691 -    SELECT (
   7.692 -      SELECT array_agg("m2"[1]::epoint)
   7.693 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   7.694 -    )
   7.695 -    FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1"
   7.696 -  $$;
   7.697 -
   7.698 -CREATE FUNCTION ecluster_extract_outlines(ecluster)
   7.699 -  RETURNS SETOF epoint[]
   7.700 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.701 -    SELECT (
   7.702 -      SELECT array_agg("m2"[1]::epoint)
   7.703 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   7.704 -    )
   7.705 -    FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1"
   7.706 -  $$;
   7.707 -
   7.708 -CREATE FUNCTION ecluster_extract_polygons(ecluster)
   7.709 -  RETURNS SETOF epoint[]
   7.710 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   7.711 -    SELECT (
   7.712 -      SELECT array_agg("m2"[1]::epoint)
   7.713 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   7.714 -    )
   7.715 -    FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1"
   7.716 -  $$;
   7.717 -
   7.718 -
   7.719 ----------------
   7.720 --- operators --
   7.721 ----------------
   7.722 -
   7.723 -CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   7.724 -  RETURNS boolean
   7.725 -  LANGUAGE C IMMUTABLE STRICT
   7.726 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ebox_overlap';
   7.727 -
   7.728 -CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   7.729 -  RETURNS boolean
   7.730 -  LANGUAGE C IMMUTABLE STRICT
   7.731 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecircle_overlap';
   7.732 -
   7.733 -CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   7.734 -  RETURNS boolean
   7.735 -  LANGUAGE C IMMUTABLE STRICT
   7.736 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecluster_overlap';
   7.737 -
   7.738 -CREATE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
   7.739 -  RETURNS boolean
   7.740 -  LANGUAGE C IMMUTABLE STRICT
   7.741 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecluster_may_overlap';
   7.742 -
   7.743 -CREATE FUNCTION ebox_overlap_proc(ebox, ebox)
   7.744 -  RETURNS boolean
   7.745 -  LANGUAGE C IMMUTABLE STRICT
   7.746 -  AS '$libdir/latlon-v0003', 'pgl_ebox_overlap';
   7.747 -
   7.748 -CREATE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
   7.749 -  RETURNS boolean
   7.750 -  LANGUAGE C IMMUTABLE STRICT
   7.751 -  AS '$libdir/latlon-v0003', 'pgl_ebox_ecircle_may_overlap';
   7.752 -
   7.753 -CREATE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
   7.754 -  RETURNS boolean
   7.755 -  LANGUAGE C IMMUTABLE STRICT
   7.756 -  AS '$libdir/latlon-v0003', 'pgl_ebox_ecluster_may_overlap';
   7.757 -
   7.758 -CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   7.759 -  RETURNS boolean
   7.760 -  LANGUAGE C IMMUTABLE STRICT
   7.761 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_overlap';
   7.762 -
   7.763 -CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   7.764 -  RETURNS boolean
   7.765 -  LANGUAGE C IMMUTABLE STRICT
   7.766 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_ecluster_overlap';
   7.767 -
   7.768 -CREATE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
   7.769 -  RETURNS boolean
   7.770 -  LANGUAGE C IMMUTABLE STRICT
   7.771 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_ecluster_may_overlap';
   7.772 -
   7.773 -CREATE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
   7.774 -  RETURNS boolean
   7.775 -  LANGUAGE C IMMUTABLE STRICT
   7.776 -  AS '$libdir/latlon-v0003', 'pgl_ecluster_may_overlap';
   7.777 -
   7.778 -CREATE FUNCTION epoint_distance_proc(epoint, epoint)
   7.779 -  RETURNS float8
   7.780 -  LANGUAGE C IMMUTABLE STRICT
   7.781 -  AS '$libdir/latlon-v0003', 'pgl_epoint_distance';
   7.782 -
   7.783 -CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   7.784 -  RETURNS float8
   7.785 -  LANGUAGE C IMMUTABLE STRICT
   7.786 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecircle_distance';
   7.787 -
   7.788 -CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   7.789 -  RETURNS float8
   7.790 -  LANGUAGE C IMMUTABLE STRICT
   7.791 -  AS '$libdir/latlon-v0003', 'pgl_epoint_ecluster_distance';
   7.792 -
   7.793 -CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   7.794 -  RETURNS float8
   7.795 -  LANGUAGE C IMMUTABLE STRICT
   7.796 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_distance';
   7.797 -
   7.798 -CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   7.799 -  RETURNS float8
   7.800 -  LANGUAGE C IMMUTABLE STRICT
   7.801 -  AS '$libdir/latlon-v0003', 'pgl_ecircle_ecluster_distance';
   7.802 -
   7.803 -CREATE OPERATOR && (
   7.804 -  leftarg = epoint,
   7.805 -  rightarg = ebox,
   7.806 -  procedure = epoint_ebox_overlap_proc,
   7.807 -  commutator = &&,
   7.808 -  restrict = areasel,
   7.809 -  join = areajoinsel
   7.810 -);
   7.811 -
   7.812 -CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint)
   7.813 -  RETURNS boolean
   7.814 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   7.815 -
   7.816 -CREATE OPERATOR && (
   7.817 -  leftarg = ebox,
   7.818 -  rightarg = epoint,
   7.819 -  procedure = epoint_ebox_overlap_commutator,
   7.820 -  commutator = &&,
   7.821 -  restrict = areasel,
   7.822 -  join = areajoinsel
   7.823 -);
   7.824 -
   7.825 -CREATE OPERATOR && (
   7.826 -  leftarg = epoint,
   7.827 -  rightarg = ecircle,
   7.828 -  procedure = epoint_ecircle_overlap_proc,
   7.829 -  commutator = &&,
   7.830 -  restrict = areasel,
   7.831 -  join = areajoinsel
   7.832 -);
   7.833 -
   7.834 -CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint)
   7.835 -  RETURNS boolean
   7.836 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   7.837 -
   7.838 -CREATE OPERATOR && (
   7.839 -  leftarg = ecircle,
   7.840 -  rightarg = epoint,
   7.841 -  procedure = epoint_ecircle_overlap_commutator,
   7.842 -  commutator = &&,
   7.843 -  restrict = areasel,
   7.844 -  join = areajoinsel
   7.845 -);
   7.846 -
   7.847 -CREATE OPERATOR && (
   7.848 -  leftarg = epoint,
   7.849 -  rightarg = ecluster,
   7.850 -  procedure = epoint_ecluster_overlap_proc,
   7.851 -  commutator = &&,
   7.852 -  restrict = areasel,
   7.853 -  join = areajoinsel
   7.854 -);
   7.855 -
   7.856 -CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint)
   7.857 -  RETURNS boolean
   7.858 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   7.859 -
   7.860 -CREATE OPERATOR && (
   7.861 -  leftarg = ecluster,
   7.862 -  rightarg = epoint,
   7.863 -  procedure = epoint_ecluster_overlap_commutator,
   7.864 -  commutator = &&,
   7.865 -  restrict = areasel,
   7.866 -  join = areajoinsel
   7.867 -);
   7.868 -
   7.869 -CREATE OPERATOR && (
   7.870 -  leftarg = ebox,
   7.871 -  rightarg = ebox,
   7.872 -  procedure = ebox_overlap_proc,
   7.873 -  commutator = &&,
   7.874 -  restrict = areasel,
   7.875 -  join = areajoinsel
   7.876 -);
   7.877 -
   7.878 -CREATE OPERATOR && (
   7.879 -  leftarg = ecircle,
   7.880 -  rightarg = ecircle,
   7.881 -  procedure = ecircle_overlap_proc,
   7.882 -  commutator = &&,
   7.883 -  restrict = areasel,
   7.884 -  join = areajoinsel
   7.885 -);
   7.886 -
   7.887 -CREATE OPERATOR && (
   7.888 -  leftarg = ecircle,
   7.889 -  rightarg = ecluster,
   7.890 -  procedure = ecircle_ecluster_overlap_proc,
   7.891 -  commutator = &&,
   7.892 -  restrict = areasel,
   7.893 -  join = areajoinsel
   7.894 -);
   7.895 -
   7.896 -CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle)
   7.897 -  RETURNS boolean
   7.898 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   7.899 -
   7.900 -CREATE OPERATOR && (
   7.901 -  leftarg = ecluster,
   7.902 -  rightarg = ecircle,
   7.903 -  procedure = ecircle_ecluster_overlap_commutator,
   7.904 -  commutator = &&,
   7.905 -  restrict = areasel,
   7.906 -  join = areajoinsel
   7.907 -);
   7.908 -
   7.909 -CREATE OPERATOR &&+ (
   7.910 -  leftarg = epoint,
   7.911 -  rightarg = ecluster,
   7.912 -  procedure = epoint_ecluster_may_overlap_proc,
   7.913 -  commutator = &&+,
   7.914 -  restrict = areasel,
   7.915 -  join = areajoinsel
   7.916 -);
   7.917 -
   7.918 -CREATE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint)
   7.919 -  RETURNS boolean
   7.920 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   7.921 -
   7.922 -CREATE OPERATOR &&+ (
   7.923 -  leftarg = ecluster,
   7.924 -  rightarg = epoint,
   7.925 -  procedure = epoint_ecluster_may_overlap_commutator,
   7.926 -  commutator = &&+,
   7.927 -  restrict = areasel,
   7.928 -  join = areajoinsel
   7.929 -);
   7.930 -
   7.931 -CREATE OPERATOR &&+ (
   7.932 -  leftarg = ebox,
   7.933 -  rightarg = ecircle,
   7.934 -  procedure = ebox_ecircle_may_overlap_proc,
   7.935 -  commutator = &&+,
   7.936 -  restrict = areasel,
   7.937 -  join = areajoinsel
   7.938 -);
   7.939 -
   7.940 -CREATE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox)
   7.941 -  RETURNS boolean
   7.942 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   7.943 -
   7.944 -CREATE OPERATOR &&+ (
   7.945 -  leftarg = ecircle,
   7.946 -  rightarg = ebox,
   7.947 -  procedure = ebox_ecircle_may_overlap_commutator,
   7.948 -  commutator = &&+,
   7.949 -  restrict = areasel,
   7.950 -  join = areajoinsel
   7.951 -);
   7.952 -
   7.953 -CREATE OPERATOR &&+ (
   7.954 -  leftarg = ebox,
   7.955 -  rightarg = ecluster,
   7.956 -  procedure = ebox_ecluster_may_overlap_proc,
   7.957 -  commutator = &&+,
   7.958 -  restrict = areasel,
   7.959 -  join = areajoinsel
   7.960 -);
   7.961 -
   7.962 -CREATE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox)
   7.963 -  RETURNS boolean
   7.964 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   7.965 -
   7.966 -CREATE OPERATOR &&+ (
   7.967 -  leftarg = ecluster,
   7.968 -  rightarg = ebox,
   7.969 -  procedure = ebox_ecluster_may_overlap_commutator,
   7.970 -  commutator = &&+,
   7.971 -  restrict = areasel,
   7.972 -  join = areajoinsel
   7.973 -);
   7.974 -
   7.975 -CREATE OPERATOR &&+ (
   7.976 -  leftarg = ecircle,
   7.977 -  rightarg = ecluster,
   7.978 -  procedure = ecircle_ecluster_may_overlap_proc,
   7.979 -  commutator = &&+,
   7.980 -  restrict = areasel,
   7.981 -  join = areajoinsel
   7.982 -);
   7.983 -
   7.984 -CREATE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle)
   7.985 -  RETURNS boolean
   7.986 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   7.987 -
   7.988 -CREATE OPERATOR &&+ (
   7.989 -  leftarg = ecluster,
   7.990 -  rightarg = ecircle,
   7.991 -  procedure = ecircle_ecluster_may_overlap_commutator,
   7.992 -  commutator = &&+,
   7.993 -  restrict = areasel,
   7.994 -  join = areajoinsel
   7.995 -);
   7.996 -
   7.997 -CREATE OPERATOR &&+ (
   7.998 -  leftarg = ecluster,
   7.999 -  rightarg = ecluster,
  7.1000 -  procedure = ecluster_may_overlap_proc,
  7.1001 -  commutator = &&+,
  7.1002 -  restrict = areasel,
  7.1003 -  join = areajoinsel
  7.1004 -);
  7.1005 -
  7.1006 -CREATE OPERATOR <-> (
  7.1007 -  leftarg = epoint,
  7.1008 -  rightarg = epoint,
  7.1009 -  procedure = epoint_distance_proc,
  7.1010 -  commutator = <->
  7.1011 -);
  7.1012 -
  7.1013 -CREATE OPERATOR <-> (
  7.1014 -  leftarg = epoint,
  7.1015 -  rightarg = ecircle,
  7.1016 -  procedure = epoint_ecircle_distance_proc,
  7.1017 -  commutator = <->
  7.1018 -);
  7.1019 -
  7.1020 -CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint)
  7.1021 -  RETURNS float8
  7.1022 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  7.1023 -
  7.1024 -CREATE OPERATOR <-> (
  7.1025 -  leftarg = ecircle,
  7.1026 -  rightarg = epoint,
  7.1027 -  procedure = epoint_ecircle_distance_commutator,
  7.1028 -  commutator = <->
  7.1029 -);
  7.1030 -
  7.1031 -CREATE OPERATOR <-> (
  7.1032 -  leftarg = epoint,
  7.1033 -  rightarg = ecluster,
  7.1034 -  procedure = epoint_ecluster_distance_proc,
  7.1035 -  commutator = <->
  7.1036 -);
  7.1037 -
  7.1038 -CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint)
  7.1039 -  RETURNS float8
  7.1040 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  7.1041 -
  7.1042 -CREATE OPERATOR <-> (
  7.1043 -  leftarg = ecluster,
  7.1044 -  rightarg = epoint,
  7.1045 -  procedure = epoint_ecluster_distance_commutator,
  7.1046 -  commutator = <->
  7.1047 -);
  7.1048 -
  7.1049 -CREATE OPERATOR <-> (
  7.1050 -  leftarg = ecircle,
  7.1051 -  rightarg = ecircle,
  7.1052 -  procedure = ecircle_distance_proc,
  7.1053 -  commutator = <->
  7.1054 -);
  7.1055 -
  7.1056 -CREATE OPERATOR <-> (
  7.1057 -  leftarg = ecircle,
  7.1058 -  rightarg = ecluster,
  7.1059 -  procedure = ecircle_ecluster_distance_proc,
  7.1060 -  commutator = <->
  7.1061 -);
  7.1062 -
  7.1063 -CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle)
  7.1064 -  RETURNS float8
  7.1065 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  7.1066 -
  7.1067 -CREATE OPERATOR <-> (
  7.1068 -  leftarg = ecluster,
  7.1069 -  rightarg = ecircle,
  7.1070 -  procedure = ecircle_ecluster_distance_commutator,
  7.1071 -  commutator = <->
  7.1072 -);
  7.1073 -
  7.1074 -
  7.1075 -----------------
  7.1076 --- GiST index --
  7.1077 -----------------
  7.1078 -
  7.1079 -CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
  7.1080 -  RETURNS boolean
  7.1081 -  LANGUAGE C STRICT
  7.1082 -  AS '$libdir/latlon-v0003', 'pgl_gist_consistent';
  7.1083 -
  7.1084 -CREATE FUNCTION pgl_gist_union(internal, internal)
  7.1085 -  RETURNS internal
  7.1086 -  LANGUAGE C STRICT
  7.1087 -  AS '$libdir/latlon-v0003', 'pgl_gist_union';
  7.1088 -
  7.1089 -CREATE FUNCTION pgl_gist_compress_epoint(internal)
  7.1090 -  RETURNS internal
  7.1091 -  LANGUAGE C STRICT
  7.1092 -  AS '$libdir/latlon-v0003', 'pgl_gist_compress_epoint';
  7.1093 -
  7.1094 -CREATE FUNCTION pgl_gist_compress_ecircle(internal)
  7.1095 -  RETURNS internal
  7.1096 -  LANGUAGE C STRICT
  7.1097 -  AS '$libdir/latlon-v0003', 'pgl_gist_compress_ecircle';
  7.1098 -
  7.1099 -CREATE FUNCTION pgl_gist_compress_ecluster(internal)
  7.1100 -  RETURNS internal
  7.1101 -  LANGUAGE C STRICT
  7.1102 -  AS '$libdir/latlon-v0003', 'pgl_gist_compress_ecluster';
  7.1103 -
  7.1104 -CREATE FUNCTION pgl_gist_decompress(internal)
  7.1105 -  RETURNS internal
  7.1106 -  LANGUAGE C STRICT
  7.1107 -  AS '$libdir/latlon-v0003', 'pgl_gist_decompress';
  7.1108 -
  7.1109 -CREATE FUNCTION pgl_gist_penalty(internal, internal, internal)
  7.1110 -  RETURNS internal
  7.1111 -  LANGUAGE C STRICT
  7.1112 -  AS '$libdir/latlon-v0003', 'pgl_gist_penalty';
  7.1113 -
  7.1114 -CREATE FUNCTION pgl_gist_picksplit(internal, internal)
  7.1115 -  RETURNS internal
  7.1116 -  LANGUAGE C STRICT
  7.1117 -  AS '$libdir/latlon-v0003', 'pgl_gist_picksplit';
  7.1118 -
  7.1119 -CREATE FUNCTION pgl_gist_same(internal, internal, internal)
  7.1120 -  RETURNS internal
  7.1121 -  LANGUAGE C STRICT
  7.1122 -  AS '$libdir/latlon-v0003', 'pgl_gist_same';
  7.1123 -
  7.1124 -CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
  7.1125 -  RETURNS internal
  7.1126 -  LANGUAGE C STRICT
  7.1127 -  AS '$libdir/latlon-v0003', 'pgl_gist_distance';
  7.1128 -
  7.1129 -CREATE OPERATOR CLASS epoint_ops
  7.1130 -  DEFAULT FOR TYPE epoint USING gist AS
  7.1131 -  OPERATOR  11 = ,
  7.1132 -  OPERATOR  22 &&  (epoint, ebox),
  7.1133 -  OPERATOR  23 &&  (epoint, ecircle),
  7.1134 -  OPERATOR  24 &&  (epoint, ecluster),
  7.1135 -  OPERATOR 124 &&+ (epoint, ecluster),
  7.1136 -  OPERATOR  31 <-> (epoint, epoint) FOR ORDER BY float_ops,
  7.1137 -  OPERATOR  33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
  7.1138 -  OPERATOR  34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
  7.1139 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  7.1140 -  FUNCTION 2 pgl_gist_union(internal, internal),
  7.1141 -  FUNCTION 3 pgl_gist_compress_epoint(internal),
  7.1142 -  FUNCTION 4 pgl_gist_decompress(internal),
  7.1143 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  7.1144 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  7.1145 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  7.1146 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  7.1147 -  STORAGE ekey_point;
  7.1148 -
  7.1149 -CREATE OPERATOR CLASS ecircle_ops
  7.1150 -  DEFAULT FOR TYPE ecircle USING gist AS
  7.1151 -  OPERATOR  13 = ,
  7.1152 -  OPERATOR  21 &&  (ecircle, epoint),
  7.1153 -  OPERATOR 122 &&+ (ecircle, ebox),
  7.1154 -  OPERATOR  23 &&  (ecircle, ecircle),
  7.1155 -  OPERATOR  24 &&  (ecircle, ecluster),
  7.1156 -  OPERATOR 124 &&+ (ecircle, ecluster),
  7.1157 -  OPERATOR  31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
  7.1158 -  OPERATOR  33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
  7.1159 -  OPERATOR  34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
  7.1160 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  7.1161 -  FUNCTION 2 pgl_gist_union(internal, internal),
  7.1162 -  FUNCTION 3 pgl_gist_compress_ecircle(internal),
  7.1163 -  FUNCTION 4 pgl_gist_decompress(internal),
  7.1164 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  7.1165 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  7.1166 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  7.1167 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  7.1168 -  STORAGE ekey_area;
  7.1169 -
  7.1170 -CREATE OPERATOR CLASS ecluster_ops
  7.1171 -  DEFAULT FOR TYPE ecluster USING gist AS
  7.1172 -  OPERATOR  21 &&  (ecluster, epoint),
  7.1173 -  OPERATOR 121 &&+ (ecluster, epoint),
  7.1174 -  OPERATOR 122 &&+ (ecluster, ebox),
  7.1175 -  OPERATOR  23 &&  (ecluster, ecircle),
  7.1176 -  OPERATOR 123 &&+ (ecluster, ecircle),
  7.1177 -  OPERATOR 124 &&+ (ecluster, ecluster),
  7.1178 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  7.1179 -  FUNCTION 2 pgl_gist_union(internal, internal),
  7.1180 -  FUNCTION 3 pgl_gist_compress_ecluster(internal),
  7.1181 -  FUNCTION 4 pgl_gist_decompress(internal),
  7.1182 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  7.1183 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  7.1184 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  7.1185 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  7.1186 -  STORAGE ekey_area;
  7.1187 -
  7.1188 -
  7.1189 ----------------------
  7.1190 --- alias functions --
  7.1191 ----------------------
  7.1192 -
  7.1193 -CREATE FUNCTION distance(epoint, epoint)
  7.1194 -  RETURNS float8
  7.1195 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  7.1196 -
  7.1197 -CREATE FUNCTION distance(ecluster, epoint)
  7.1198 -  RETURNS float8
  7.1199 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  7.1200 -
  7.1201 -CREATE FUNCTION distance_within(epoint, epoint, float8)
  7.1202 -  RETURNS boolean
  7.1203 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  7.1204 -
  7.1205 -CREATE FUNCTION distance_within(ecluster, epoint, float8)
  7.1206 -  RETURNS boolean
  7.1207 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  7.1208 -
  7.1209 -
  7.1210 ---------------------------------
  7.1211 --- other data storage formats --
  7.1212 ---------------------------------
  7.1213 -
  7.1214 -CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint_lonlat')
  7.1215 -  RETURNS epoint
  7.1216 -  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
  7.1217 -    DECLARE
  7.1218 -      "result" epoint;
  7.1219 -    BEGIN
  7.1220 -      IF $3 = 'epoint_lonlat' THEN
  7.1221 -        -- avoid dynamic command execution for better performance
  7.1222 -        RETURN epoint($2, $1);
  7.1223 -      END IF;
  7.1224 -      IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN
  7.1225 -        -- avoid dynamic command execution for better performance
  7.1226 -        RETURN epoint($1, $2);
  7.1227 -      END IF;
  7.1228 -      EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2;
  7.1229 -      RETURN "result";
  7.1230 -    END;
  7.1231 -  $$;
  7.1232 -
  7.1233 -CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat')
  7.1234 -  RETURNS epoint
  7.1235 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  7.1236 -    SELECT CASE
  7.1237 -    WHEN $1->>'type' = 'Point' THEN
  7.1238 -      coords_to_epoint(
  7.1239 -        ($1->'coordinates'->>1)::float8,
  7.1240 -        ($1->'coordinates'->>0)::float8,
  7.1241 -        $2
  7.1242 -      )
  7.1243 -    WHEN $1->>'type' = 'Feature' THEN
  7.1244 -      GeoJSON_to_epoint($1->'geometry', $2)
  7.1245 -    ELSE
  7.1246 -      NULL
  7.1247 -    END
  7.1248 -  $$;
  7.1249 -
  7.1250 -CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat')
  7.1251 -  RETURNS ecluster
  7.1252 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  7.1253 -    SELECT CASE $1->>'type'
  7.1254 -    WHEN 'Point' THEN
  7.1255 -      coords_to_epoint(
  7.1256 -        ($1->'coordinates'->>1)::float8,
  7.1257 -        ($1->'coordinates'->>0)::float8,
  7.1258 -        $2
  7.1259 -      )::ecluster
  7.1260 -    WHEN 'MultiPoint' THEN
  7.1261 -      ( SELECT ecluster_create_multipoint(array_agg(
  7.1262 -          coords_to_epoint(
  7.1263 -            ("coord"->>1)::float8,
  7.1264 -            ("coord"->>0)::float8,
  7.1265 -            $2
  7.1266 -          )
  7.1267 -        ))
  7.1268 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  7.1269 -      )
  7.1270 -    WHEN 'LineString' THEN
  7.1271 -      ( SELECT ecluster_create_path(array_agg(
  7.1272 -          coords_to_epoint(
  7.1273 -            ("coord"->>1)::float8,
  7.1274 -            ("coord"->>0)::float8,
  7.1275 -            $2
  7.1276 -          )
  7.1277 -        ))
  7.1278 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  7.1279 -      )
  7.1280 -    WHEN 'MultiLineString' THEN
  7.1281 -      ( SELECT ecluster_concat(array_agg(
  7.1282 -          ( SELECT ecluster_create_path(array_agg(
  7.1283 -              coords_to_epoint(
  7.1284 -                ("coord"->>1)::float8,
  7.1285 -                ("coord"->>0)::float8,
  7.1286 -                $2
  7.1287 -              )
  7.1288 -            ))
  7.1289 -            FROM jsonb_array_elements("coord_array") AS "coord"
  7.1290 -          )
  7.1291 -        ))
  7.1292 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  7.1293 -      )
  7.1294 -    WHEN 'Polygon' THEN
  7.1295 -      ( SELECT ecluster_concat(array_agg(
  7.1296 -          ( SELECT ecluster_create_polygon(array_agg(
  7.1297 -              coords_to_epoint(
  7.1298 -                ("coord"->>1)::float8,
  7.1299 -                ("coord"->>0)::float8,
  7.1300 -                $2
  7.1301 -              )
  7.1302 -            ))
  7.1303 -            FROM jsonb_array_elements("coord_array") AS "coord"
  7.1304 -          )
  7.1305 -        ))
  7.1306 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  7.1307 -      )
  7.1308 -    WHEN 'MultiPolygon' THEN
  7.1309 -      ( SELECT ecluster_concat(array_agg(
  7.1310 -          ( SELECT ecluster_concat(array_agg(
  7.1311 -              ( SELECT ecluster_create_polygon(array_agg(
  7.1312 -                  coords_to_epoint(
  7.1313 -                    ("coord"->>1)::float8,
  7.1314 -                    ("coord"->>0)::float8,
  7.1315 -                    $2
  7.1316 -                  )
  7.1317 -                ))
  7.1318 -                FROM jsonb_array_elements("coord_array") AS "coord"
  7.1319 -              )
  7.1320 -            ))
  7.1321 -            FROM jsonb_array_elements("coord_array_array") AS "coord_array"
  7.1322 -          )
  7.1323 -        ))
  7.1324 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array"
  7.1325 -      )
  7.1326 -    WHEN 'Feature' THEN
  7.1327 -      GeoJSON_to_ecluster($1->'geometry', $2)
  7.1328 -    WHEN 'FeatureCollection' THEN
  7.1329 -      ( SELECT ecluster_concat(array_agg(
  7.1330 -          GeoJSON_to_ecluster("feature", $2)
  7.1331 -        ))
  7.1332 -        FROM jsonb_array_elements($1->'features') AS "feature"
  7.1333 -      )
  7.1334 -    ELSE
  7.1335 -      NULL
  7.1336 -    END
  7.1337 -  $$;
  7.1338 -
     8.1 --- a/latlon--0.4.sql	Sat Sep 03 16:00:22 2016 +0200
     8.2 +++ b/latlon--0.4.sql	Fri Sep 09 19:22:30 2016 +0200
     8.3 @@ -767,11 +767,21 @@
     8.4    LANGUAGE C IMMUTABLE STRICT
     8.5    AS '$libdir/latlon-v0004', 'pgl_ecircle_ecluster_may_overlap';
     8.6  
     8.7 +CREATE FUNCTION ecluster_overlap_proc(ecluster, ecluster)
     8.8 +  RETURNS boolean
     8.9 +  LANGUAGE C IMMUTABLE STRICT
    8.10 +  AS '$libdir/latlon-v0004', 'pgl_ecluster_overlap';
    8.11 +
    8.12  CREATE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
    8.13    RETURNS boolean
    8.14    LANGUAGE C IMMUTABLE STRICT
    8.15    AS '$libdir/latlon-v0004', 'pgl_ecluster_may_overlap';
    8.16  
    8.17 +CREATE FUNCTION ecluster_contains_proc(ecluster, ecluster)
    8.18 +  RETURNS boolean
    8.19 +  LANGUAGE C IMMUTABLE STRICT
    8.20 +  AS '$libdir/latlon-v0004', 'pgl_ecluster_contains';
    8.21 +
    8.22  CREATE FUNCTION epoint_distance_proc(epoint, epoint)
    8.23    RETURNS float8
    8.24    LANGUAGE C IMMUTABLE STRICT
    8.25 @@ -797,6 +807,11 @@
    8.26    LANGUAGE C IMMUTABLE STRICT
    8.27    AS '$libdir/latlon-v0004', 'pgl_ecircle_ecluster_distance';
    8.28  
    8.29 +CREATE FUNCTION ecluster_distance_proc(ecluster, ecluster)
    8.30 +  RETURNS float8
    8.31 +  LANGUAGE C IMMUTABLE STRICT
    8.32 +  AS '$libdir/latlon-v0004', 'pgl_ecluster_distance';
    8.33 +
    8.34  CREATE OPERATOR && (
    8.35    leftarg = epoint,
    8.36    rightarg = ebox,
    8.37 @@ -903,6 +918,67 @@
    8.38    join = areajoinsel
    8.39  );
    8.40  
    8.41 +CREATE OPERATOR && (
    8.42 +  leftarg = ecluster,
    8.43 +  rightarg = ecluster,
    8.44 +  procedure = ecluster_overlap_proc,
    8.45 +  commutator = &&,
    8.46 +  restrict = areasel,
    8.47 +  join = areajoinsel
    8.48 +);
    8.49 +
    8.50 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle)
    8.51 +  RETURNS boolean
    8.52 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
    8.53 +
    8.54 +CREATE OPERATOR && (
    8.55 +  leftarg = ebox,
    8.56 +  rightarg = ecircle,
    8.57 +  procedure = ebox_ecircle_overlap_castwrap,
    8.58 +  commutator = &&,
    8.59 +  restrict = areasel,
    8.60 +  join = areajoinsel
    8.61 +);
    8.62 +
    8.63 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox)
    8.64 +  RETURNS boolean
    8.65 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
    8.66 +
    8.67 +CREATE OPERATOR && (
    8.68 +  leftarg = ecircle,
    8.69 +  rightarg = ebox,
    8.70 +  procedure = ebox_ecircle_overlap_castwrap,
    8.71 +  commutator = &&,
    8.72 +  restrict = areasel,
    8.73 +  join = areajoinsel
    8.74 +);
    8.75 +
    8.76 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster)
    8.77 +  RETURNS boolean
    8.78 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
    8.79 +
    8.80 +CREATE OPERATOR && (
    8.81 +  leftarg = ebox,
    8.82 +  rightarg = ecluster,
    8.83 +  procedure = ebox_ecluster_overlap_castwrap,
    8.84 +  commutator = &&,
    8.85 +  restrict = areasel,
    8.86 +  join = areajoinsel
    8.87 +);
    8.88 +
    8.89 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox)
    8.90 +  RETURNS boolean
    8.91 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
    8.92 +
    8.93 +CREATE OPERATOR && (
    8.94 +  leftarg = ecluster,
    8.95 +  rightarg = ebox,
    8.96 +  procedure = ebox_ecluster_overlap_castwrap,
    8.97 +  commutator = &&,
    8.98 +  restrict = areasel,
    8.99 +  join = areajoinsel
   8.100 +);
   8.101 +
   8.102  CREATE OPERATOR &&+ (
   8.103    leftarg = epoint,
   8.104    rightarg = ecluster,
   8.105 @@ -1000,6 +1076,116 @@
   8.106    join = areajoinsel
   8.107  );
   8.108  
   8.109 +CREATE OPERATOR @> (
   8.110 +  leftarg = ebox,
   8.111 +  rightarg = epoint,
   8.112 +  procedure = epoint_ebox_overlap_commutator,
   8.113 +  commutator = <@,
   8.114 +  restrict = areasel,
   8.115 +  join = areajoinsel
   8.116 +);
   8.117 +
   8.118 +CREATE OPERATOR <@ (
   8.119 +  leftarg = epoint,
   8.120 +  rightarg = ebox,
   8.121 +  procedure = epoint_ebox_overlap_proc,
   8.122 +  commutator = @>,
   8.123 +  restrict = areasel,
   8.124 +  join = areajoinsel
   8.125 +);
   8.126 +
   8.127 +CREATE OPERATOR @> (
   8.128 +  leftarg = ecluster,
   8.129 +  rightarg = epoint,
   8.130 +  procedure = epoint_ecluster_overlap_commutator,
   8.131 +  commutator = <@,
   8.132 +  restrict = areasel,
   8.133 +  join = areajoinsel
   8.134 +);
   8.135 +
   8.136 +CREATE OPERATOR <@ (
   8.137 +  leftarg = epoint,
   8.138 +  rightarg = ecluster,
   8.139 +  procedure = epoint_ecluster_overlap_proc,
   8.140 +  commutator = <@,
   8.141 +  restrict = areasel,
   8.142 +  join = areajoinsel
   8.143 +);
   8.144 +
   8.145 +CREATE OPERATOR @> (
   8.146 +  leftarg = ecluster,
   8.147 +  rightarg = ecluster,
   8.148 +  procedure = ecluster_contains_proc,
   8.149 +  commutator = <@,
   8.150 +  restrict = areasel,
   8.151 +  join = areajoinsel
   8.152 +);
   8.153 +
   8.154 +CREATE FUNCTION ecluster_contains_commutator(ecluster, ecluster)
   8.155 +  RETURNS boolean
   8.156 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1';
   8.157 +
   8.158 +CREATE OPERATOR <@ (
   8.159 +  leftarg = ecluster,
   8.160 +  rightarg = ecluster,
   8.161 +  procedure = ecluster_contains_commutator,
   8.162 +  commutator = @>,
   8.163 +  restrict = areasel,
   8.164 +  join = areajoinsel
   8.165 +);
   8.166 +
   8.167 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster)
   8.168 +  RETURNS boolean
   8.169 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2';
   8.170 +
   8.171 +CREATE OPERATOR @> (
   8.172 +  leftarg = ebox,
   8.173 +  rightarg = ecluster,
   8.174 +  procedure = ebox_ecluster_contains_castwrap,
   8.175 +  commutator = <@,
   8.176 +  restrict = areasel,
   8.177 +  join = areajoinsel
   8.178 +);
   8.179 +
   8.180 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox)
   8.181 +  RETURNS boolean
   8.182 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1';
   8.183 +
   8.184 +CREATE OPERATOR <@ (
   8.185 +  leftarg = ecluster,
   8.186 +  rightarg = ebox,
   8.187 +  procedure = ebox_ecluster_contains_castwrap,
   8.188 +  commutator = @>,
   8.189 +  restrict = areasel,
   8.190 +  join = areajoinsel
   8.191 +);
   8.192 +
   8.193 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox)
   8.194 +  RETURNS boolean
   8.195 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 @> $2::ecluster';
   8.196 +
   8.197 +CREATE OPERATOR @> (
   8.198 +  leftarg = ecluster,
   8.199 +  rightarg = ebox,
   8.200 +  procedure = ecluster_ebox_contains_castwrap,
   8.201 +  commutator = <@,
   8.202 +  restrict = areasel,
   8.203 +  join = areajoinsel
   8.204 +);
   8.205 +
   8.206 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster)
   8.207 +  RETURNS boolean
   8.208 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1::ecluster';
   8.209 +
   8.210 +CREATE OPERATOR <@ (
   8.211 +  leftarg = ebox,
   8.212 +  rightarg = ecluster,
   8.213 +  procedure = ecluster_ebox_contains_castwrap,
   8.214 +  commutator = @>,
   8.215 +  restrict = areasel,
   8.216 +  join = areajoinsel
   8.217 +);
   8.218 +
   8.219  CREATE OPERATOR <-> (
   8.220    leftarg = epoint,
   8.221    rightarg = epoint,
   8.222 @@ -1068,6 +1254,90 @@
   8.223    commutator = <->
   8.224  );
   8.225  
   8.226 +CREATE OPERATOR <-> (
   8.227 +  leftarg = ecluster,
   8.228 +  rightarg = ecluster,
   8.229 +  procedure = ecluster_distance_proc,
   8.230 +  commutator = <->
   8.231 +);
   8.232 +
   8.233 +CREATE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox)
   8.234 +  RETURNS float8
   8.235 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
   8.236 +
   8.237 +CREATE OPERATOR <-> (
   8.238 +  leftarg = epoint,
   8.239 +  rightarg = ebox,
   8.240 +  procedure = epoint_ebox_distance_castwrap,
   8.241 +  commutator = <->
   8.242 +);
   8.243 +
   8.244 +CREATE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint)
   8.245 +  RETURNS float8
   8.246 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
   8.247 +
   8.248 +CREATE OPERATOR <-> (
   8.249 +  leftarg = ebox,
   8.250 +  rightarg = epoint,
   8.251 +  procedure = epoint_ebox_distance_castwrap,
   8.252 +  commutator = <->
   8.253 +);
   8.254 +
   8.255 +CREATE FUNCTION ebox_distance_castwrap(ebox, ebox)
   8.256 +  RETURNS float8
   8.257 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2::ecluster';
   8.258 +
   8.259 +CREATE OPERATOR <-> (
   8.260 +  leftarg = ebox,
   8.261 +  rightarg = ebox,
   8.262 +  procedure = ebox_distance_castwrap,
   8.263 +  commutator = <->
   8.264 +);
   8.265 +
   8.266 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle)
   8.267 +  RETURNS float8
   8.268 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
   8.269 +
   8.270 +CREATE OPERATOR <-> (
   8.271 +  leftarg = ebox,
   8.272 +  rightarg = ecircle,
   8.273 +  procedure = ebox_ecircle_distance_castwrap,
   8.274 +  commutator = <->
   8.275 +);
   8.276 +
   8.277 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox)
   8.278 +  RETURNS float8
   8.279 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
   8.280 +
   8.281 +CREATE OPERATOR <-> (
   8.282 +  leftarg = ecircle,
   8.283 +  rightarg = ebox,
   8.284 +  procedure = ebox_ecircle_distance_castwrap,
   8.285 +  commutator = <->
   8.286 +);
   8.287 +
   8.288 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster)
   8.289 +  RETURNS float8
   8.290 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
   8.291 +
   8.292 +CREATE OPERATOR <-> (
   8.293 +  leftarg = ebox,
   8.294 +  rightarg = ecluster,
   8.295 +  procedure = ebox_ecluster_distance_castwrap,
   8.296 +  commutator = <->
   8.297 +);
   8.298 +
   8.299 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox)
   8.300 +  RETURNS float8
   8.301 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
   8.302 +
   8.303 +CREATE OPERATOR <-> (
   8.304 +  leftarg = ecluster,
   8.305 +  rightarg = ebox,
   8.306 +  procedure = ebox_ecluster_distance_castwrap,
   8.307 +  commutator = <->
   8.308 +);
   8.309 +
   8.310  
   8.311  ----------------
   8.312  -- GiST index --
   8.313 @@ -1127,10 +1397,13 @@
   8.314    DEFAULT FOR TYPE epoint USING gist AS
   8.315    OPERATOR  11 = ,
   8.316    OPERATOR  22 &&  (epoint, ebox),
   8.317 +  OPERATOR 222 <@  (epoint, ebox),
   8.318    OPERATOR  23 &&  (epoint, ecircle),
   8.319    OPERATOR  24 &&  (epoint, ecluster),
   8.320    OPERATOR 124 &&+ (epoint, ecluster),
   8.321 +  OPERATOR 224 <@  (epoint, ecluster),
   8.322    OPERATOR  31 <-> (epoint, epoint) FOR ORDER BY float_ops,
   8.323 +  OPERATOR  32 <-> (epoint, ebox) FOR ORDER BY float_ops,
   8.324    OPERATOR  33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
   8.325    OPERATOR  34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
   8.326    FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
   8.327 @@ -1147,11 +1420,13 @@
   8.328    DEFAULT FOR TYPE ecircle USING gist AS
   8.329    OPERATOR  13 = ,
   8.330    OPERATOR  21 &&  (ecircle, epoint),
   8.331 +  OPERATOR  22 &&  (ecircle, ebox),
   8.332    OPERATOR 122 &&+ (ecircle, ebox),
   8.333    OPERATOR  23 &&  (ecircle, ecircle),
   8.334    OPERATOR  24 &&  (ecircle, ecluster),
   8.335    OPERATOR 124 &&+ (ecircle, ecluster),
   8.336    OPERATOR  31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
   8.337 +  OPERATOR  32 <-> (ecircle, ebox) FOR ORDER BY float_ops,
   8.338    OPERATOR  33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
   8.339    OPERATOR  34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
   8.340    FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
   8.341 @@ -1168,10 +1443,21 @@
   8.342    DEFAULT FOR TYPE ecluster USING gist AS
   8.343    OPERATOR  21 &&  (ecluster, epoint),
   8.344    OPERATOR 121 &&+ (ecluster, epoint),
   8.345 +  OPERATOR 221 @>  (ecluster, epoint),
   8.346 +  OPERATOR  22 &&  (ecluster, ebox),
   8.347    OPERATOR 122 &&+ (ecluster, ebox),
   8.348 +  OPERATOR 222 @>  (ecluster, ebox),
   8.349 +  OPERATOR 322 <@  (ecluster, ebox),
   8.350    OPERATOR  23 &&  (ecluster, ecircle),
   8.351    OPERATOR 123 &&+ (ecluster, ecircle),
   8.352 +  OPERATOR  24 &&  (ecluster, ecluster),
   8.353    OPERATOR 124 &&+ (ecluster, ecluster),
   8.354 +  OPERATOR 224 @>  (ecluster, ecluster),
   8.355 +  OPERATOR 324 <@  (ecluster, ecluster),
   8.356 +  OPERATOR  31 <-> (ecluster, epoint) FOR ORDER BY float_ops,
   8.357 +  OPERATOR  32 <-> (ecluster, ebox) FOR ORDER BY float_ops,
   8.358 +  OPERATOR  33 <-> (ecluster, ecircle) FOR ORDER BY float_ops,
   8.359 +  OPERATOR  34 <-> (ecluster, ecluster) FOR ORDER BY float_ops,
   8.360    FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
   8.361    FUNCTION 2 pgl_gist_union(internal, internal),
   8.362    FUNCTION 3 pgl_gist_compress_ecluster(internal),
     9.1 --- a/latlon-v0003.c	Sat Sep 03 16:00:22 2016 +0200
     9.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.3 @@ -1,2768 +0,0 @@
     9.4 -
     9.5 -/*-------------*
     9.6 - *  C prelude  *
     9.7 - *-------------*/
     9.8 -
     9.9 -#include "postgres.h"
    9.10 -#include "fmgr.h"
    9.11 -#include "libpq/pqformat.h"
    9.12 -#include "access/gist.h"
    9.13 -#include "access/stratnum.h"
    9.14 -#include "utils/array.h"
    9.15 -#include <math.h>
    9.16 -
    9.17 -#ifdef PG_MODULE_MAGIC
    9.18 -PG_MODULE_MAGIC;
    9.19 -#endif
    9.20 -
    9.21 -#if INT_MAX < 2147483647
    9.22 -#error Expected int type to be at least 32 bit wide
    9.23 -#endif
    9.24 -
    9.25 -
    9.26 -/*---------------------------------*
    9.27 - *  distance calculation on earth  *
    9.28 - *  (using WGS-84 spheroid)        *
    9.29 - *---------------------------------*/
    9.30 -
    9.31 -/*  WGS-84 spheroid with following parameters:
    9.32 -    semi-major axis  a = 6378137
    9.33 -    semi-minor axis  b = a * (1 - 1/298.257223563)
    9.34 -    estimated diameter = 2 * (2*a+b)/3
    9.35 -*/
    9.36 -#define PGL_SPHEROID_A 6378137.0            /* semi major axis */
    9.37 -#define PGL_SPHEROID_F (1.0/298.257223563)  /* flattening */
    9.38 -#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F))
    9.39 -#define PGL_EPS2       ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \
    9.40 -                           PGL_SPHEROID_B * PGL_SPHEROID_B ) / \
    9.41 -                         ( PGL_SPHEROID_A * PGL_SPHEROID_A ) )
    9.42 -#define PGL_SUBEPS2    (1.0-PGL_EPS2)
    9.43 -#define PGL_DIAMETER   ((4.0*PGL_SPHEROID_A + 2.0*PGL_SPHEROID_B) / 3.0)
    9.44 -#define PGL_SCALE      (PGL_SPHEROID_A / PGL_DIAMETER)  /* semi-major ref. */
    9.45 -#define PGL_FADELIMIT  (PGL_DIAMETER * M_PI / 6.0)      /* 1/6 circumference */
    9.46 -#define PGL_MAXDIST    (PGL_DIAMETER * M_PI / 2.0)      /* maximum distance */
    9.47 -
    9.48 -/* calculate distance between two points on earth (given in degrees) */
    9.49 -static inline double pgl_distance(
    9.50 -  double lat1, double lon1, double lat2, double lon2
    9.51 -) {
    9.52 -  float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin;
    9.53 -  float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t;
    9.54 -  /* normalize delta longitude (lon2 > 0 && lon1 = 0) */
    9.55 -  /* lon1 = 0 (not used anymore) */
    9.56 -  lon2 = fabs(lon2-lon1);
    9.57 -  /* convert to radians (first divide, then multiply) */
    9.58 -  lat1 = (lat1 / 180.0) * M_PI;
    9.59 -  lat2 = (lat2 / 180.0) * M_PI;
    9.60 -  lon2 = (lon2 / 180.0) * M_PI;
    9.61 -  /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point
    9.62 -     operations (lon2 >= lon1 is already ensured in a previous step) */
    9.63 -  if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; }
    9.64 -  /* calculate 3d coordinates on scaled ellipsoid which has an average diameter
    9.65 -     of 1.0 */
    9.66 -  lat1cos = cos(lat1); lat1sin = sin(lat1);
    9.67 -  lat2cos = cos(lat2); lat2sin = sin(lat2);
    9.68 -  lon2cos = cos(lon2); lon2sin = sin(lon2);
    9.69 -  nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin);
    9.70 -  nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
    9.71 -  x1 = nphi1 * lat1cos;
    9.72 -  z1 = nphi1 * PGL_SUBEPS2 * lat1sin;
    9.73 -  x2 = nphi2 * lat2cos * lon2cos;
    9.74 -  y2 = nphi2 * lat2cos * lon2sin;
    9.75 -  z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
    9.76 -  /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */
    9.77 -  g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
    9.78 -  /* convert tunnel distance through scaled ellipsoid to approximated surface
    9.79 -     distance on original ellipsoid */
    9.80 -  if (g > 1.0) g = 1.0;
    9.81 -  s = PGL_DIAMETER * asin(g);
    9.82 -  /* return result only if small enough to be precise (less than 1/3 of
    9.83 -     maximum possible distance) */
    9.84 -  if (s <= PGL_FADELIMIT) return s;
    9.85 -  /* calculate tunnel distance to antipodal point through scaled ellipsoid */
    9.86 -  g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1));
    9.87 -  /* convert tunnel distance to antipodal point through scaled ellipsoid to
    9.88 -     approximated surface distance to antipodal point on original ellipsoid */
    9.89 -  if (g > 1.0) g = 1.0;
    9.90 -  t = PGL_DIAMETER * asin(g);
    9.91 -  /* surface distance between original points can now be approximated by
    9.92 -     substracting antipodal distance from maximum possible distance;
    9.93 -     return result only if small enough (less than 1/3 of maximum possible
    9.94 -     distance) */
    9.95 -  if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t;
    9.96 -  /* otherwise crossfade direct and antipodal result to ensure monotonicity */
    9.97 -  return (
    9.98 -    (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) /
    9.99 -    (s + t - 2*PGL_FADELIMIT)
   9.100 -  );
   9.101 -}
   9.102 -
   9.103 -/* finite distance that can not be reached on earth */
   9.104 -#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST)
   9.105 -
   9.106 -
   9.107 -/*--------------------------------*
   9.108 - *  simple geographic data types  *
   9.109 - *--------------------------------*/
   9.110 -
   9.111 -/* point on earth given by latitude and longitude in degrees */
   9.112 -/* (type "epoint" in SQL) */
   9.113 -typedef struct {
   9.114 -  double lat;  /* between  -90 and  90 (both inclusive) */
   9.115 -  double lon;  /* between -180 and 180 (both inclusive) */
   9.116 -} pgl_point;
   9.117 -
   9.118 -/* box delimited by two parallels and two meridians (all in degrees) */
   9.119 -/* (type "ebox" in SQL) */
   9.120 -typedef struct {
   9.121 -  double lat_min;  /* between  -90 and  90 (both inclusive) */
   9.122 -  double lat_max;  /* between  -90 and  90 (both inclusive) */
   9.123 -  double lon_min;  /* between -180 and 180 (both inclusive) */
   9.124 -  double lon_max;  /* between -180 and 180 (both inclusive) */
   9.125 -  /* if lat_min > lat_max, then box is empty */
   9.126 -  /* if lon_min > lon_max, then 180th meridian is crossed */
   9.127 -} pgl_box;
   9.128 -
   9.129 -/* circle on earth surface (for radial searches with fixed radius) */
   9.130 -/* (type "ecircle" in SQL) */
   9.131 -typedef struct {
   9.132 -  pgl_point center;
   9.133 -  double radius; /* positive (including +0 but excluding -0), or -INFINITY */
   9.134 -  /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point),
   9.135 -     zero radius (0) denotes a single point,
   9.136 -     a finite radius (0 < radius < INFINITY) denotes a filled circle, and
   9.137 -     a radius of INFINITY is valid and means complete coverage of earth. */
   9.138 -} pgl_circle;
   9.139 -
   9.140 -
   9.141 -/*----------------------------------*
   9.142 - *  geographic "cluster" data type  *
   9.143 - *----------------------------------*/
   9.144 -
   9.145 -/* A cluster is a collection of points, paths, outlines, and polygons. If two
   9.146 -   polygons in a cluster overlap, the area covered by both polygons does not
   9.147 -   belong to the cluster. This way, a cluster can be used to describe complex
   9.148 -   shapes like polygons with holes. Outlines are non-filled polygons. Paths are
   9.149 -   open by default (i.e. the last point in the list is not connected with the
   9.150 -   first point in the list). Note that each outline or polygon in a cluster
   9.151 -   must cover a longitude range of less than 180 degrees to avoid ambiguities.
   9.152 -   Areas which are larger may be split into multiple polygons. */
   9.153 -
   9.154 -/* maximum number of points in a cluster */
   9.155 -/* (limited to avoid integer overflows, e.g. when allocating memory) */
   9.156 -#define PGL_CLUSTER_MAXPOINTS 16777216
   9.157 -
   9.158 -/* types of cluster entries */
   9.159 -#define PGL_ENTRY_POINT   1  /* a point */
   9.160 -#define PGL_ENTRY_PATH    2  /* a path from first point to last point */
   9.161 -#define PGL_ENTRY_OUTLINE 3  /* a non-filled polygon with given vertices */
   9.162 -#define PGL_ENTRY_POLYGON 4  /* a filled polygon with given vertices */
   9.163 -
   9.164 -/* Entries of a cluster are described by two different structs: pgl_newentry
   9.165 -   and pgl_entry. The first is used only during construction of a cluster, the
   9.166 -   second is used in all other cases (e.g. when reading clusters from the
   9.167 -   database, performing operations, etc). */
   9.168 -
   9.169 -/* entry for new geographic cluster during construction of that cluster */
   9.170 -typedef struct {
   9.171 -  int32_t entrytype;
   9.172 -  int32_t npoints;
   9.173 -  pgl_point *points;  /* pointer to an array of points (pgl_point) */
   9.174 -} pgl_newentry;
   9.175 -
   9.176 -/* entry of geographic cluster */
   9.177 -typedef struct {
   9.178 -  int32_t entrytype;  /* type of entry: point, path, outline, polygon */
   9.179 -  int32_t npoints;    /* number of stored points (set to 1 for point entry) */
   9.180 -  int32_t offset;     /* offset of pgl_point array from cluster base address */
   9.181 -  /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */
   9.182 -} pgl_entry;
   9.183 -
   9.184 -/* geographic cluster which is a collection of points, (open) paths, polygons,
   9.185 -   and outlines (non-filled polygons) */
   9.186 -typedef struct {
   9.187 -  char header[VARHDRSZ];  /* PostgreSQL header for variable size data types */
   9.188 -  int32_t nentries;       /* number of stored points */
   9.189 -  pgl_circle bounding;    /* bounding circle */
   9.190 -  /* Note: bounding circle ensures alignment of pgl_cluster for points */
   9.191 -  pgl_entry entries[FLEXIBLE_ARRAY_MEMBER];  /* var-length data */
   9.192 -} pgl_cluster;
   9.193 -
   9.194 -/* macro to determine memory alignment of points */
   9.195 -/* (needed to store pgl_point array after entries in pgl_cluster) */
   9.196 -typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment;
   9.197 -#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned)
   9.198 -
   9.199 -/* macro to extract a pointer to the array of points of a cluster entry */
   9.200 -#define PGL_ENTRY_POINTS(cluster, idx) \
   9.201 -  ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset))
   9.202 -
   9.203 -/* convert pgl_newentry array to pgl_cluster */
   9.204 -static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) {
   9.205 -  int i;              /* index of current entry */
   9.206 -  int npoints = 0;    /* number of points in whole cluster */
   9.207 -  int entry_npoints;  /* number of points in current entry */
   9.208 -  int points_offset = PGL_POINT_ALIGNMENT * (
   9.209 -    ( offsetof(pgl_cluster, entries) +
   9.210 -      nentries * sizeof(pgl_entry) +
   9.211 -      PGL_POINT_ALIGNMENT - 1
   9.212 -    ) / PGL_POINT_ALIGNMENT
   9.213 -  );  /* offset of pgl_point array from base address (considering alignment) */
   9.214 -  pgl_cluster *cluster;  /* new cluster to be returned */
   9.215 -  /* determine total number of points */
   9.216 -  for (i=0; i<nentries; i++) npoints += entries[i].npoints;
   9.217 -  /* allocate memory for cluster (including entries and points) */
   9.218 -  cluster = palloc(points_offset + npoints * sizeof(pgl_point));
   9.219 -  /* re-count total number of points to determine offset for each entry */
   9.220 -  npoints = 0;
   9.221 -  /* copy entries and points */
   9.222 -  for (i=0; i<nentries; i++) {
   9.223 -    /* determine number of points in entry */
   9.224 -    entry_npoints = entries[i].npoints;
   9.225 -    /* copy entry */
   9.226 -    cluster->entries[i].entrytype = entries[i].entrytype;
   9.227 -    cluster->entries[i].npoints = entry_npoints;
   9.228 -    /* calculate offset (in bytes) of pgl_point array */
   9.229 -    cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point);
   9.230 -    /* copy points */
   9.231 -    memcpy(
   9.232 -      PGL_ENTRY_POINTS(cluster, i),
   9.233 -      entries[i].points,
   9.234 -      entry_npoints * sizeof(pgl_point)
   9.235 -    );
   9.236 -    /* update total number of points processed */
   9.237 -    npoints += entry_npoints;
   9.238 -  }
   9.239 -  /* set number of entries in cluster */
   9.240 -  cluster->nentries = nentries;
   9.241 -  /* set PostgreSQL header for variable sized data */
   9.242 -  SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point));
   9.243 -  /* return newly created cluster */
   9.244 -  return cluster;
   9.245 -}
   9.246 -
   9.247 -
   9.248 -/*----------------------------------------*
   9.249 - *  C functions on geographic data types  *
   9.250 - *----------------------------------------*/
   9.251 -
   9.252 -/* round latitude or longitude to 12 digits after decimal point */
   9.253 -static inline double pgl_round(double val) {
   9.254 -  return round(val * 1e12) / 1e12;
   9.255 -}
   9.256 -
   9.257 -/* compare two points */
   9.258 -/* (equality when same point on earth is described, otherwise an arbitrary
   9.259 -   linear order) */
   9.260 -static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) {
   9.261 -  double lon1, lon2;  /* modified longitudes for special cases */
   9.262 -  /* use latitude as first ordering criterion */
   9.263 -  if (point1->lat < point2->lat) return -1;
   9.264 -  if (point1->lat > point2->lat) return 1;
   9.265 -  /* determine modified longitudes (considering special case of poles and
   9.266 -     180th meridian which can be described as W180 or E180) */
   9.267 -  if (point1->lat == -90 || point1->lat == 90) lon1 = 0;
   9.268 -  else if (point1->lon == 180) lon1 = -180;
   9.269 -  else lon1 = point1->lon;
   9.270 -  if (point2->lat == -90 || point2->lat == 90) lon2 = 0;
   9.271 -  else if (point2->lon == 180) lon2 = -180;
   9.272 -  else lon2 = point2->lon;
   9.273 -  /* use (modified) longitude as secondary ordering criterion */
   9.274 -  if (lon1 < lon2) return -1;
   9.275 -  if (lon1 > lon2) return 1;
   9.276 -  /* no difference found, points are equal */
   9.277 -  return 0;
   9.278 -}
   9.279 -
   9.280 -/* compare two boxes */
   9.281 -/* (equality when same box on earth is described, otherwise an arbitrary linear
   9.282 -   order) */
   9.283 -static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) {
   9.284 -  /* two empty boxes are equal, and an empty box is always considered "less
   9.285 -     than" a non-empty box */
   9.286 -  if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1;
   9.287 -  if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0;
   9.288 -  if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1;
   9.289 -  /* use southern border as first ordering criterion */
   9.290 -  if (box1->lat_min < box2->lat_min) return -1;
   9.291 -  if (box1->lat_min > box2->lat_min) return 1;
   9.292 -  /* use northern border as second ordering criterion */
   9.293 -  if (box1->lat_max < box2->lat_max) return -1;
   9.294 -  if (box1->lat_max > box2->lat_max) return 1;
   9.295 -  /* use western border as third ordering criterion */
   9.296 -  if (box1->lon_min < box2->lon_min) return -1;
   9.297 -  if (box1->lon_min > box2->lon_min) return 1;
   9.298 -  /* use eastern border as fourth ordering criterion */
   9.299 -  if (box1->lon_max < box2->lon_max) return -1;
   9.300 -  if (box1->lon_max > box2->lon_max) return 1;
   9.301 -  /* no difference found, boxes are equal */
   9.302 -  return 0;
   9.303 -}
   9.304 -
   9.305 -/* compare two circles */
   9.306 -/* (equality when same circle on earth is described, otherwise an arbitrary
   9.307 -   linear order) */
   9.308 -static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) {
   9.309 -  /* two circles with same infinite radius (positive or negative infinity) are
   9.310 -     considered equal independently of center point */
   9.311 -  if (
   9.312 -    !isfinite(circle1->radius) && !isfinite(circle2->radius) &&
   9.313 -    circle1->radius == circle2->radius
   9.314 -  ) return 0;
   9.315 -  /* use radius as first ordering criterion */
   9.316 -  if (circle1->radius < circle2->radius) return -1;
   9.317 -  if (circle1->radius > circle2->radius) return 1;
   9.318 -  /* use center point as secondary ordering criterion */
   9.319 -  return pgl_point_cmp(&(circle1->center), &(circle2->center));
   9.320 -}
   9.321 -
   9.322 -/* set box to empty box*/
   9.323 -static void pgl_box_set_empty(pgl_box *box) {
   9.324 -  box->lat_min = INFINITY;
   9.325 -  box->lat_max = -INFINITY;
   9.326 -  box->lon_min = 0;
   9.327 -  box->lon_max = 0;
   9.328 -}
   9.329 -
   9.330 -/* check if point is inside a box */
   9.331 -static bool pgl_point_in_box(pgl_point *point, pgl_box *box) {
   9.332 -  return (
   9.333 -    point->lat >= box->lat_min && point->lat <= box->lat_max && (
   9.334 -      (box->lon_min > box->lon_max) ? (
   9.335 -        /* box crosses 180th meridian */
   9.336 -        point->lon >= box->lon_min || point->lon <= box->lon_max
   9.337 -      ) : (
   9.338 -        /* box does not cross the 180th meridian */
   9.339 -        point->lon >= box->lon_min && point->lon <= box->lon_max
   9.340 -      )
   9.341 -    )
   9.342 -  );
   9.343 -}
   9.344 -
   9.345 -/* check if two boxes overlap */
   9.346 -static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) {
   9.347 -  return (
   9.348 -    box2->lat_max >= box2->lat_min &&  /* ensure box2 is not empty */
   9.349 -    ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) &&
   9.350 -    ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && (
   9.351 -      (
   9.352 -        /* check if one and only one box crosses the 180th meridian */
   9.353 -        ((box1->lon_min > box1->lon_max) ? 1 : 0) ^
   9.354 -        ((box2->lon_min > box2->lon_max) ? 1 : 0)
   9.355 -      ) ? (
   9.356 -        /* exactly one box crosses the 180th meridian */
   9.357 -        box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min ||
   9.358 -        box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max
   9.359 -      ) : (
   9.360 -        /* no box or both boxes cross the 180th meridian */
   9.361 -        (
   9.362 -          (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) &&
   9.363 -          (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max)
   9.364 -        ) ||
   9.365 -        /* handle W180 == E180 */
   9.366 -        ( box1->lon_min == -180 && box2->lon_max == 180 ) ||
   9.367 -        ( box2->lon_min == -180 && box1->lon_max == 180 )
   9.368 -      )
   9.369 -    )
   9.370 -  );
   9.371 -}
   9.372 -
   9.373 -/* check unambiguousness of east/west orientation of cluster entries and set
   9.374 -   bounding circle of cluster */
   9.375 -static bool pgl_finalize_cluster(pgl_cluster *cluster) {
   9.376 -  int i, j;                 /* i: index of entry, j: index of point in entry */
   9.377 -  int npoints;              /* number of points in entry */
   9.378 -  int total_npoints = 0;    /* total number of points in cluster */
   9.379 -  pgl_point *points;        /* points in entry */
   9.380 -  int lon_dir;              /* first point of entry west (-1) or east (+1) */
   9.381 -  double lon_break = 0;     /* antipodal longitude of first point in entry */
   9.382 -  double lon_min, lon_max;  /* covered longitude range of entry */
   9.383 -  double value;             /* temporary variable */
   9.384 -  /* reset bounding circle center to empty circle at 0/0 coordinates */
   9.385 -  cluster->bounding.center.lat = 0;
   9.386 -  cluster->bounding.center.lon = 0;
   9.387 -  cluster->bounding.radius = -INFINITY;
   9.388 -  /* if cluster is not empty */
   9.389 -  if (cluster->nentries != 0) {
   9.390 -    /* iterate over all cluster entries and ensure they each cover a longitude
   9.391 -       range less than 180 degrees */
   9.392 -    for (i=0; i<cluster->nentries; i++) {
   9.393 -      /* get properties of entry */
   9.394 -      npoints = cluster->entries[i].npoints;
   9.395 -      points = PGL_ENTRY_POINTS(cluster, i);
   9.396 -      /* get longitude of first point of entry */
   9.397 -      value = points[0].lon;
   9.398 -      /* initialize lon_min and lon_max with longitude of first point */
   9.399 -      lon_min = value;
   9.400 -      lon_max = value;
   9.401 -      /* determine east/west orientation of first point and calculate antipodal
   9.402 -         longitude (Note: rounding required here) */
   9.403 -      if      (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); }
   9.404 -      else if (value > 0) { lon_dir =  1; lon_break = pgl_round(value - 180); }
   9.405 -      else lon_dir = 0;
   9.406 -      /* iterate over all other points in entry */
   9.407 -      for (j=1; j<npoints; j++) {
   9.408 -        /* consider longitude wrap-around */
   9.409 -        value = points[j].lon;
   9.410 -        if      (lon_dir<0 && value>lon_break) value = pgl_round(value - 360);
   9.411 -        else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360);
   9.412 -        /* update lon_min and lon_max */
   9.413 -        if      (value < lon_min) lon_min = value;
   9.414 -        else if (value > lon_max) lon_max = value;
   9.415 -        /* return false if 180 degrees or more are covered */
   9.416 -        if (lon_max - lon_min >= 180) return false;
   9.417 -      }
   9.418 -    }
   9.419 -    /* iterate over all points of all entries and calculate arbitrary center
   9.420 -       point for bounding circle (best if center point minimizes the radius,
   9.421 -       but some error is allowed here) */
   9.422 -    for (i=0; i<cluster->nentries; i++) {
   9.423 -      /* get properties of entry */
   9.424 -      npoints = cluster->entries[i].npoints;
   9.425 -      points = PGL_ENTRY_POINTS(cluster, i);
   9.426 -      /* check if first entry */
   9.427 -      if (i==0) {
   9.428 -        /* get longitude of first point of first entry in whole cluster */
   9.429 -        value = points[0].lon;
   9.430 -        /* initialize lon_min and lon_max with longitude of first point of
   9.431 -           first entry in whole cluster (used to determine if whole cluster
   9.432 -           covers a longitude range of 180 degrees or more) */
   9.433 -        lon_min = value;
   9.434 -        lon_max = value;
   9.435 -        /* determine east/west orientation of first point and calculate
   9.436 -           antipodal longitude (Note: rounding not necessary here) */
   9.437 -        if      (value < 0) { lon_dir = -1; lon_break = value + 180; }
   9.438 -        else if (value > 0) { lon_dir =  1; lon_break = value - 180; }
   9.439 -        else lon_dir = 0;
   9.440 -      }
   9.441 -      /* iterate over all points in entry */
   9.442 -      for (j=0; j<npoints; j++) {
   9.443 -        /* longitude wrap-around (Note: rounding not necessary here) */
   9.444 -        value = points[j].lon;
   9.445 -        if      (lon_dir < 0 && value > lon_break) value -= 360;
   9.446 -        else if (lon_dir > 0 && value < lon_break) value += 360;
   9.447 -        if      (value < lon_min) lon_min = value;
   9.448 -        else if (value > lon_max) lon_max = value;
   9.449 -        /* set bounding circle to cover whole earth if more than 180 degrees
   9.450 -           are covered */
   9.451 -        if (lon_max - lon_min >= 180) {
   9.452 -          cluster->bounding.center.lat = 0;
   9.453 -          cluster->bounding.center.lon = 0;
   9.454 -          cluster->bounding.radius = INFINITY;
   9.455 -          return true;
   9.456 -        }
   9.457 -        /* add point to bounding circle center (for average calculation) */
   9.458 -        cluster->bounding.center.lat += points[j].lat;
   9.459 -        cluster->bounding.center.lon += value;
   9.460 -      }
   9.461 -      /* count total number of points */
   9.462 -      total_npoints += npoints;
   9.463 -    }
   9.464 -    /* determine average latitude and longitude of cluster */
   9.465 -    cluster->bounding.center.lat /= total_npoints;
   9.466 -    cluster->bounding.center.lon /= total_npoints;
   9.467 -    /* normalize longitude of center of cluster bounding circle */
   9.468 -    if (cluster->bounding.center.lon < -180) {
   9.469 -      cluster->bounding.center.lon += 360;
   9.470 -    }
   9.471 -    else if (cluster->bounding.center.lon > 180) {
   9.472 -      cluster->bounding.center.lon -= 360;
   9.473 -    }
   9.474 -    /* round bounding circle center (useful if it is used by other functions) */
   9.475 -    cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat);
   9.476 -    cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon);
   9.477 -    /* calculate radius of bounding circle */
   9.478 -    for (i=0; i<cluster->nentries; i++) {
   9.479 -      npoints = cluster->entries[i].npoints;
   9.480 -      points = PGL_ENTRY_POINTS(cluster, i);
   9.481 -      for (j=0; j<npoints; j++) {
   9.482 -        value = pgl_distance(
   9.483 -          cluster->bounding.center.lat, cluster->bounding.center.lon,
   9.484 -          points[j].lat, points[j].lon
   9.485 -        );
   9.486 -        if (value > cluster->bounding.radius) cluster->bounding.radius = value;
   9.487 -      }
   9.488 -    }
   9.489 -  }
   9.490 -  /* return true (east/west orientation is unambiguous) */
   9.491 -  return true;
   9.492 -}
   9.493 -
   9.494 -/* check if point is inside cluster */
   9.495 -static bool pgl_point_in_cluster(pgl_point *point, pgl_cluster *cluster) {
   9.496 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   9.497 -  int entrytype;         /* type of entry */
   9.498 -  int npoints;           /* number of points in entry */
   9.499 -  pgl_point *points;     /* array of points in entry */
   9.500 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   9.501 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   9.502 -  double lat0 = point->lat;  /* latitude of point */
   9.503 -  double lon0;           /* (adjusted) longitude of point */
   9.504 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   9.505 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   9.506 -  double lon;            /* longitude of intersection */
   9.507 -  int counter = 0;       /* counter for intersections east of point */
   9.508 -  /* points outside bounding circle are always assumed to be non-overlapping */
   9.509 -  /* (necessary for consistent table and index scans) */
   9.510 -  if (
   9.511 -    pgl_distance(
   9.512 -      point->lat, point->lon,
   9.513 -      cluster->bounding.center.lat, cluster->bounding.center.lon
   9.514 -    ) > cluster->bounding.radius
   9.515 -  ) return false;
   9.516 -  /* iterate over all entries */
   9.517 -  for (i=0; i<cluster->nentries; i++) {
   9.518 -    /* get properties of entry */
   9.519 -    entrytype = cluster->entries[i].entrytype;
   9.520 -    npoints = cluster->entries[i].npoints;
   9.521 -    points = PGL_ENTRY_POINTS(cluster, i);
   9.522 -    /* determine east/west orientation of first point of entry and calculate
   9.523 -       antipodal longitude */
   9.524 -    lon_break = points[0].lon;
   9.525 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   9.526 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   9.527 -    else lon_dir = 0;
   9.528 -    /* get longitude of point */
   9.529 -    lon0 = point->lon;
   9.530 -    /* consider longitude wrap-around for point */
   9.531 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360);
   9.532 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360);
   9.533 -    /* iterate over all edges and vertices */
   9.534 -    for (j=0; j<npoints; j++) {
   9.535 -      /* return true if point is on vertex of polygon */
   9.536 -      if (pgl_point_cmp(point, &(points[j])) == 0) return true;
   9.537 -      /* calculate index of next vertex */
   9.538 -      k = (j+1) % npoints;
   9.539 -      /* skip last edge unless entry is (closed) outline or polygon */
   9.540 -      if (
   9.541 -        k == 0 &&
   9.542 -        entrytype != PGL_ENTRY_OUTLINE &&
   9.543 -        entrytype != PGL_ENTRY_POLYGON
   9.544 -      ) continue;
   9.545 -      /* get latitude and longitude values of edge */
   9.546 -      lat1 = points[j].lat;
   9.547 -      lat2 = points[k].lat;
   9.548 -      lon1 = points[j].lon;
   9.549 -      lon2 = points[k].lon;
   9.550 -      /* consider longitude wrap-around for edge */
   9.551 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   9.552 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   9.553 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
   9.554 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
   9.555 -      /* return true if point is on horizontal (west to east) edge of polygon */
   9.556 -      if (
   9.557 -        lat0 == lat1 && lat0 == lat2 &&
   9.558 -        ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) )
   9.559 -      ) return true;
   9.560 -      /* check if edge crosses east/west line of point */
   9.561 -      if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) {
   9.562 -        /* calculate longitude of intersection */
   9.563 -        lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1);
   9.564 -        /* return true if intersection goes (approximately) through point */
   9.565 -        if (pgl_round(lon) == lon0) return true;
   9.566 -        /* count intersection if east of point and entry is polygon*/
   9.567 -        if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++;
   9.568 -      }
   9.569 -    }
   9.570 -  }
   9.571 -  /* return true if number of intersections is odd */
   9.572 -  return counter & 1;
   9.573 -}
   9.574 -
   9.575 -/* calculate (approximate) distance between point and cluster */
   9.576 -static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
   9.577 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   9.578 -  int entrytype;         /* type of entry */
   9.579 -  int npoints;           /* number of points in entry */
   9.580 -  pgl_point *points;     /* array of points in entry */
   9.581 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   9.582 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   9.583 -  double lon_min = 0;    /* minimum (adjusted) longitude of entry vertices */
   9.584 -  double lon_max = 0;    /* maximum (adjusted) longitude of entry vertices */
   9.585 -  double lat0 = point->lat;  /* latitude of point */
   9.586 -  double lon0;           /* (adjusted) longitude of point */
   9.587 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   9.588 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   9.589 -  double s;              /* scalar for vector calculations */
   9.590 -  double dist;           /* distance calculated in one step */
   9.591 -  double min_dist = INFINITY;   /* minimum distance */
   9.592 -  /* distance is zero if point is contained in cluster */
   9.593 -  if (pgl_point_in_cluster(point, cluster)) return 0;
   9.594 -  /* iterate over all entries */
   9.595 -  for (i=0; i<cluster->nentries; i++) {
   9.596 -    /* get properties of entry */
   9.597 -    entrytype = cluster->entries[i].entrytype;
   9.598 -    npoints = cluster->entries[i].npoints;
   9.599 -    points = PGL_ENTRY_POINTS(cluster, i);
   9.600 -    /* determine east/west orientation of first point of entry and calculate
   9.601 -       antipodal longitude */
   9.602 -    lon_break = points[0].lon;
   9.603 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   9.604 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   9.605 -    else lon_dir = 0;
   9.606 -    /* determine covered longitude range */
   9.607 -    for (j=0; j<npoints; j++) {
   9.608 -      /* get longitude of vertex */
   9.609 -      lon1 = points[j].lon;
   9.610 -      /* adjust longitude to fix potential wrap-around */
   9.611 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   9.612 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   9.613 -      /* update minimum and maximum longitude of polygon */
   9.614 -      if (j == 0 || lon1 < lon_min) lon_min = lon1;
   9.615 -      if (j == 0 || lon1 > lon_max) lon_max = lon1;
   9.616 -    }
   9.617 -    /* adjust longitude wrap-around according to full longitude range */
   9.618 -    lon_break = (lon_max + lon_min) / 2;
   9.619 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   9.620 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   9.621 -    /* get longitude of point */
   9.622 -    lon0 = point->lon;
   9.623 -    /* consider longitude wrap-around for point */
   9.624 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 -= 360;
   9.625 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
   9.626 -    /* iterate over all edges and vertices */
   9.627 -    for (j=0; j<npoints; j++) {
   9.628 -      /* get latitude and longitude values of current point */
   9.629 -      lat1 = points[j].lat;
   9.630 -      lon1 = points[j].lon;
   9.631 -      /* consider longitude wrap-around for current point */
   9.632 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   9.633 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   9.634 -      /* calculate distance to vertex */
   9.635 -      dist = pgl_distance(lat0, lon0, lat1, lon1);
   9.636 -      /* store calculated distance if smallest */
   9.637 -      if (dist < min_dist) min_dist = dist;
   9.638 -      /* calculate index of next vertex */
   9.639 -      k = (j+1) % npoints;
   9.640 -      /* skip last edge unless entry is (closed) outline or polygon */
   9.641 -      if (
   9.642 -        k == 0 &&
   9.643 -        entrytype != PGL_ENTRY_OUTLINE &&
   9.644 -        entrytype != PGL_ENTRY_POLYGON
   9.645 -      ) continue;
   9.646 -      /* get latitude and longitude values of next point */
   9.647 -      lat2 = points[k].lat;
   9.648 -      lon2 = points[k].lon;
   9.649 -      /* consider longitude wrap-around for next point */
   9.650 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
   9.651 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
   9.652 -      /* go to next vertex and edge if edge is degenerated */
   9.653 -      if (lat1 == lat2 && lon1 == lon2) continue;
   9.654 -      /* otherwise test if point can be projected onto edge of polygon */
   9.655 -      s = (
   9.656 -        ((lat0-lat1) * (lat2-lat1) + (lon0-lon1) * (lon2-lon1)) /
   9.657 -        ((lat2-lat1) * (lat2-lat1) + (lon2-lon1) * (lon2-lon1))
   9.658 -      );
   9.659 -      /* go to next vertex and edge if point cannot be projected */
   9.660 -      if (!(s > 0 && s < 1)) continue;
   9.661 -      /* calculate distance from original point to projected point */
   9.662 -      dist = pgl_distance(
   9.663 -        lat0, lon0,
   9.664 -        lat1 + s * (lat2-lat1),
   9.665 -        lon1 + s * (lon2-lon1)
   9.666 -      );
   9.667 -      /* store calculated distance if smallest */
   9.668 -      if (dist < min_dist) min_dist = dist;
   9.669 -    }
   9.670 -  }
   9.671 -  /* return minimum distance */
   9.672 -  return min_dist;
   9.673 -}
   9.674 -
   9.675 -/* estimator function for distance between box and point */
   9.676 -/* allowed to return smaller values than actually correct */
   9.677 -static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
   9.678 -  double dlon;  /* longitude range of box (delta longitude) */
   9.679 -  double h;     /* half of distance along meridian */
   9.680 -  double d;     /* distance between both southern or both northern points */
   9.681 -  double cur_dist;  /* calculated distance */
   9.682 -  double min_dist;  /* minimum distance calculated */
   9.683 -  /* return infinity if bounding box is empty */
   9.684 -  if (box->lat_min > box->lat_max) return INFINITY;
   9.685 -  /* return zero if point is inside bounding box */
   9.686 -  if (pgl_point_in_box(point, box)) return 0;
   9.687 -  /* calculate delta longitude */
   9.688 -  dlon = box->lon_max - box->lon_min;
   9.689 -  if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
   9.690 -  /* if delta longitude is greater than 180 degrees, perform safe fall-back */
   9.691 -  if (dlon > 180) return 0;
   9.692 -  /* calculate half of distance along meridian */
   9.693 -  h = pgl_distance(box->lat_min, 0, box->lat_max, 0) / 2;
   9.694 -  /* calculate full distance between southern points */
   9.695 -  d = pgl_distance(box->lat_min, 0, box->lat_min, dlon);
   9.696 -  /* calculate maximum of full distance and half distance */
   9.697 -  if (h > d) d = h;
   9.698 -  /* calculate distance from point to first southern vertex and substract
   9.699 -     maximum error */
   9.700 -  min_dist = pgl_distance(
   9.701 -    point->lat, point->lon, box->lat_min, box->lon_min
   9.702 -  ) - d;
   9.703 -  /* return zero if estimated distance is smaller than zero */
   9.704 -  if (min_dist <= 0) return 0;
   9.705 -  /* repeat procedure with second southern vertex */
   9.706 -  cur_dist = pgl_distance(
   9.707 -    point->lat, point->lon, box->lat_min, box->lon_max
   9.708 -  ) - d;
   9.709 -  if (cur_dist <= 0) return 0;
   9.710 -  if (cur_dist < min_dist) min_dist = cur_dist;
   9.711 -  /* calculate full distance between northern points */
   9.712 -  d = pgl_distance(box->lat_max, 0, box->lat_max, dlon);
   9.713 -  /* calculate maximum of full distance and half distance */
   9.714 -  if (h > d) d = h;
   9.715 -  /* repeat procedure with northern vertices */
   9.716 -  cur_dist = pgl_distance(
   9.717 -    point->lat, point->lon, box->lat_max, box->lon_max
   9.718 -  ) - d;
   9.719 -  if (cur_dist <= 0) return 0;
   9.720 -  if (cur_dist < min_dist) min_dist = cur_dist;
   9.721 -  cur_dist = pgl_distance(
   9.722 -    point->lat, point->lon, box->lat_max, box->lon_min
   9.723 -  ) - d;
   9.724 -  if (cur_dist <= 0) return 0;
   9.725 -  if (cur_dist < min_dist) min_dist = cur_dist;
   9.726 -  /* return smallest value (unless already returned zero) */
   9.727 -  return min_dist;
   9.728 -}
   9.729 -
   9.730 -
   9.731 -/*----------------------------*
   9.732 - *  fractal geographic index  *
   9.733 - *----------------------------*/
   9.734 -
   9.735 -/* number of bytes used for geographic (center) position in keys */
   9.736 -#define PGL_KEY_LATLON_BYTELEN 7
   9.737 -
   9.738 -/* maximum reference value for logarithmic size of geographic objects */
   9.739 -#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
   9.740 -
   9.741 -/* safety margin to avoid floating point errors in distance estimation */
   9.742 -#define PGL_FPE_SAFETY (1.0+1e-14)  /* slightly greater than 1.0 */
   9.743 -
   9.744 -/* pointer to index key (either pgl_pointkey or pgl_areakey) */
   9.745 -typedef unsigned char *pgl_keyptr;
   9.746 -
   9.747 -/* index key for points (objects with zero area) on the spheroid */
   9.748 -/* bit  0..55: interspersed bits of latitude and longitude,
   9.749 -   bit 56..57: always zero,
   9.750 -   bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */
   9.751 -typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1];
   9.752 -
   9.753 -/* index key for geographic objects on spheroid with area greater than zero */
   9.754 -/* bit  0..55: interspersed bits of latitude and longitude of center point,
   9.755 -   bit     56: always set to 1,
   9.756 -   bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.),
   9.757 -   bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to
   9.758 -               PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth
   9.759 -               = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL
   9.760 -               (with interspersed bits = 0 and node depth = 0) for keys which
   9.761 -               cover both empty and non-empty objects */
   9.762 -
   9.763 -typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2];
   9.764 -
   9.765 -/* helper macros for reading/writing index keys */
   9.766 -#define PGL_KEY_NODEDEPTH_OFFSET  PGL_KEY_LATLON_BYTELEN
   9.767 -#define PGL_KEY_OBJSIZE_OFFSET    (PGL_KEY_NODEDEPTH_OFFSET+1)
   9.768 -#define PGL_POINTKEY_MAXDEPTH     (PGL_KEY_LATLON_BYTELEN*8)
   9.769 -#define PGL_AREAKEY_MAXDEPTH      (2*PGL_POINTKEY_MAXDEPTH+1)
   9.770 -#define PGL_AREAKEY_MAXOBJSIZE    (PGL_POINTKEY_MAXDEPTH+1)
   9.771 -#define PGL_AREAKEY_TYPEMASK      0x80
   9.772 -#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8)))
   9.773 -#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \
   9.774 -                                  ( PGL_KEY_LATLONBIT(key1, n) ^ \
   9.775 -                                    PGL_KEY_LATLONBIT(key2, n) )
   9.776 -#define PGL_KEY_IS_AREAKEY(key)   ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
   9.777 -                                    PGL_AREAKEY_TYPEMASK)
   9.778 -#define PGL_KEY_NODEDEPTH(key)    ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
   9.779 -                                    (PGL_AREAKEY_TYPEMASK-1))
   9.780 -#define PGL_KEY_OBJSIZE(key)      ((key)[PGL_KEY_OBJSIZE_OFFSET])
   9.781 -#define PGL_KEY_OBJSIZE_EMPTY     126
   9.782 -#define PGL_KEY_OBJSIZE_UNIVERSAL 127
   9.783 -#define PGL_KEY_IS_EMPTY(key)     ( PGL_KEY_IS_AREAKEY(key) && \
   9.784 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
   9.785 -                                    PGL_KEY_OBJSIZE_EMPTY )
   9.786 -#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \
   9.787 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
   9.788 -                                    PGL_KEY_OBJSIZE_UNIVERSAL )
   9.789 -
   9.790 -/* set area key to match empty objects only */
   9.791 -static void pgl_key_set_empty(pgl_keyptr key) {
   9.792 -  memset(key, 0, sizeof(pgl_areakey));
   9.793 -  /* Note: setting node depth to maximum is required for picksplit function */
   9.794 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
   9.795 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY;
   9.796 -}
   9.797 -
   9.798 -/* set area key to match any object (including empty objects) */
   9.799 -static void pgl_key_set_universal(pgl_keyptr key) {
   9.800 -  memset(key, 0, sizeof(pgl_areakey));
   9.801 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK;
   9.802 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL;
   9.803 -}
   9.804 -
   9.805 -/* convert a point on earth into a max-depth key to be used in index */
   9.806 -static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) {
   9.807 -  double lat = point->lat;
   9.808 -  double lon = point->lon;
   9.809 -  int i;
   9.810 -  /* clear latitude and longitude bits */
   9.811 -  memset(key, 0, PGL_KEY_LATLON_BYTELEN);
   9.812 -  /* set node depth to maximum and type bit to zero */
   9.813 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH;
   9.814 -  /* iterate over all latitude/longitude bit pairs */
   9.815 -  for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) {
   9.816 -    /* determine latitude bit */
   9.817 -    if (lat >= 0) {
   9.818 -      key[i/4] |= 0x80 >> (2*(i%4));
   9.819 -      lat *= 2; lat -= 90;
   9.820 -    } else {
   9.821 -      lat *= 2; lat += 90;
   9.822 -    }
   9.823 -    /* determine longitude bit */
   9.824 -    if (lon >= 0) {
   9.825 -      key[i/4] |= 0x80 >> (2*(i%4)+1);
   9.826 -      lon *= 2; lon -= 180;
   9.827 -    } else {
   9.828 -      lon *= 2; lon += 180;
   9.829 -    }
   9.830 -  }
   9.831 -}
   9.832 -
   9.833 -/* convert a circle on earth into a max-depth key to be used in an index */
   9.834 -static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) {
   9.835 -  /* handle special case of empty circle */
   9.836 -  if (circle->radius < 0) {
   9.837 -    pgl_key_set_empty(key);
   9.838 -    return;
   9.839 -  }
   9.840 -  /* perform same action as for point keys */
   9.841 -  pgl_point_to_key(&(circle->center), key);
   9.842 -  /* but overwrite type and node depth to fit area index key */
   9.843 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
   9.844 -  /* check if radius is greater than (or equal to) reference size */
   9.845 -  /* (treat equal values as greater values for numerical safety) */
   9.846 -  if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) {
   9.847 -    /* if yes, set logarithmic size to zero */
   9.848 -    key[PGL_KEY_OBJSIZE_OFFSET] = 0;
   9.849 -  } else {
   9.850 -    /* otherwise, determine logarithmic size iteratively */
   9.851 -    /* (one step is equivalent to a factor of sqrt(2)) */
   9.852 -    double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2;
   9.853 -    int objsize = 1;
   9.854 -    while (objsize < PGL_AREAKEY_MAXOBJSIZE) {
   9.855 -      /* stop when radius is greater than (or equal to) adjusted reference */
   9.856 -      /* (treat equal values as greater values for numerical safety) */
   9.857 -      if (circle->radius >= reference) break;
   9.858 -      reference /= M_SQRT2;
   9.859 -      objsize++;
   9.860 -    }
   9.861 -    /* set logarithmic size to determined value */
   9.862 -    key[PGL_KEY_OBJSIZE_OFFSET] = objsize;
   9.863 -  }
   9.864 -}
   9.865 -
   9.866 -/* check if one key is subkey of another key or vice versa */
   9.867 -static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) {
   9.868 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
   9.869 -  /* determine smallest depth */
   9.870 -  int depth1 = PGL_KEY_NODEDEPTH(key1);
   9.871 -  int depth2 = PGL_KEY_NODEDEPTH(key2);
   9.872 -  int depth = (depth1 < depth2) ? depth1 : depth2;
   9.873 -  /* check if keys are area keys (assuming that both keys have same type) */
   9.874 -  if (PGL_KEY_IS_AREAKEY(key1)) {
   9.875 -    int j = 0;  /* bit offset for logarithmic object size bits */
   9.876 -    int k = 0;  /* bit offset for latitude and longitude */
   9.877 -    /* fetch logarithmic object size information */
   9.878 -    int objsize1 = PGL_KEY_OBJSIZE(key1);
   9.879 -    int objsize2 = PGL_KEY_OBJSIZE(key2);
   9.880 -    /* handle special cases for empty objects (universal and empty keys) */
   9.881 -    if (
   9.882 -      objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL ||
   9.883 -      objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL
   9.884 -    ) return true;
   9.885 -    if (
   9.886 -      objsize1 == PGL_KEY_OBJSIZE_EMPTY ||
   9.887 -      objsize2 == PGL_KEY_OBJSIZE_EMPTY
   9.888 -    ) return objsize1 == objsize2;
   9.889 -    /* iterate through key bits */
   9.890 -    for (i=0; i<depth; i++) {
   9.891 -      /* every second bit is a bit describing the object size */
   9.892 -      if (i%2 == 0) {
   9.893 -        /* check if object size bit is different in both keys (objsize1 and
   9.894 -           objsize2 describe the minimum index when object size bit is set) */
   9.895 -        if (
   9.896 -          (objsize1 <= j && objsize2 > j) ||
   9.897 -          (objsize2 <= j && objsize1 > j)
   9.898 -        ) {
   9.899 -          /* bit differs, therefore keys are in separate branches */
   9.900 -          return false;
   9.901 -        }
   9.902 -        /* increase bit counter for object size bits */
   9.903 -        j++;
   9.904 -      }
   9.905 -      /* all other bits describe latitude and longitude */
   9.906 -      else {
   9.907 -        /* check if bit differs in both keys */
   9.908 -        if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) {
   9.909 -          /* bit differs, therefore keys are in separate branches */
   9.910 -          return false;
   9.911 -        }
   9.912 -        /* increase bit counter for latitude/longitude bits */
   9.913 -        k++;
   9.914 -      }
   9.915 -    }
   9.916 -  }
   9.917 -  /* if not, keys are point keys */
   9.918 -  else {
   9.919 -    /* iterate through key bits */
   9.920 -    for (i=0; i<depth; i++) {
   9.921 -      /* check if bit differs in both keys */
   9.922 -      if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) {
   9.923 -        /* bit differs, therefore keys are in separate branches */
   9.924 -        return false;
   9.925 -      }
   9.926 -    }
   9.927 -  }
   9.928 -  /* return true because keys are in the same branch */
   9.929 -  return true;
   9.930 -}
   9.931 -
   9.932 -/* combine two keys into new key which covers both original keys */
   9.933 -/* (result stored in first argument) */
   9.934 -static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) {
   9.935 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
   9.936 -  /* determine smallest depth */
   9.937 -  int depth1 = PGL_KEY_NODEDEPTH(dst);
   9.938 -  int depth2 = PGL_KEY_NODEDEPTH(src);
   9.939 -  int depth = (depth1 < depth2) ? depth1 : depth2;
   9.940 -  /* check if keys are area keys (assuming that both keys have same type) */
   9.941 -  if (PGL_KEY_IS_AREAKEY(dst)) {
   9.942 -    pgl_areakey dstbuf = { 0, };  /* destination buffer (cleared) */
   9.943 -    int j = 0;  /* bit offset for logarithmic object size bits */
   9.944 -    int k = 0;  /* bit offset for latitude and longitude */
   9.945 -    /* fetch logarithmic object size information */
   9.946 -    int objsize1 = PGL_KEY_OBJSIZE(dst);
   9.947 -    int objsize2 = PGL_KEY_OBJSIZE(src);
   9.948 -    /* handle special cases for empty objects (universal and empty keys) */
   9.949 -    if (
   9.950 -      objsize1 > PGL_AREAKEY_MAXOBJSIZE ||
   9.951 -      objsize2 > PGL_AREAKEY_MAXOBJSIZE
   9.952 -    ) {
   9.953 -      if (
   9.954 -        objsize1 == PGL_KEY_OBJSIZE_EMPTY &&
   9.955 -        objsize2 == PGL_KEY_OBJSIZE_EMPTY
   9.956 -      ) pgl_key_set_empty(dst);
   9.957 -      else pgl_key_set_universal(dst);
   9.958 -      return;
   9.959 -    }
   9.960 -    /* iterate through key bits */
   9.961 -    for (i=0; i<depth; i++) {
   9.962 -      /* every second bit is a bit describing the object size */
   9.963 -      if (i%2 == 0) {
   9.964 -        /* increase bit counter for object size bits first */
   9.965 -        /* (handy when setting objsize variable) */
   9.966 -        j++;
   9.967 -        /* check if object size bit is set in neither key */
   9.968 -        if (objsize1 >= j && objsize2 >= j) {
   9.969 -          /* set objsize in destination buffer to indicate that size bit is
   9.970 -             unset in destination buffer at the current bit position */
   9.971 -          dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j;
   9.972 -        }
   9.973 -        /* break if object size bit is set in one key only */
   9.974 -        else if (objsize1 >= j || objsize2 >= j) break;
   9.975 -      }
   9.976 -      /* all other bits describe latitude and longitude */
   9.977 -      else {
   9.978 -        /* break if bit differs in both keys */
   9.979 -        if (PGL_KEY_LATLONBIT(dst, k)) {
   9.980 -          if (!PGL_KEY_LATLONBIT(src, k)) break;
   9.981 -          /* but set bit in destination buffer if bit is set in both keys */
   9.982 -          dstbuf[k/8] |= 0x80 >> (k%8);
   9.983 -        } else if (PGL_KEY_LATLONBIT(src, k)) break;
   9.984 -        /* increase bit counter for latitude/longitude bits */
   9.985 -        k++;
   9.986 -      }
   9.987 -    }
   9.988 -    /* set common node depth and type bit (type bit = 1) */
   9.989 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i;
   9.990 -    /* copy contents of destination buffer to first key */
   9.991 -    memcpy(dst, dstbuf, sizeof(pgl_areakey));
   9.992 -  }
   9.993 -  /* if not, keys are point keys */
   9.994 -  else {
   9.995 -    pgl_pointkey dstbuf = { 0, };  /* destination buffer (cleared) */
   9.996 -    /* iterate through key bits */
   9.997 -    for (i=0; i<depth; i++) {
   9.998 -      /* break if bit differs in both keys */
   9.999 -      if (PGL_KEY_LATLONBIT(dst, i)) {
  9.1000 -        if (!PGL_KEY_LATLONBIT(src, i)) break;
  9.1001 -        /* but set bit in destination buffer if bit is set in both keys */
  9.1002 -        dstbuf[i/8] |= 0x80 >> (i%8);
  9.1003 -      } else if (PGL_KEY_LATLONBIT(src, i)) break;
  9.1004 -    }
  9.1005 -    /* set common node depth (type bit = 0) */
  9.1006 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i;
  9.1007 -    /* copy contents of destination buffer to first key */
  9.1008 -    memcpy(dst, dstbuf, sizeof(pgl_pointkey));
  9.1009 -  }
  9.1010 -}
  9.1011 -
  9.1012 -/* determine center(!) boundaries and radius estimation of index key */
  9.1013 -static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) {
  9.1014 -  int i;
  9.1015 -  /* determine node depth */
  9.1016 -  int depth = PGL_KEY_NODEDEPTH(key);
  9.1017 -  /* center point of possible result */
  9.1018 -  double lat = 0;
  9.1019 -  double lon = 0;
  9.1020 -  /* maximum distance of real center point from key center */
  9.1021 -  double dlat = 90;
  9.1022 -  double dlon = 180;
  9.1023 -  /* maximum radius of contained objects */
  9.1024 -  double radius = 0;  /* always return zero for point index keys */
  9.1025 -  /* check if key is area key */
  9.1026 -  if (PGL_KEY_IS_AREAKEY(key)) {
  9.1027 -    /* get logarithmic object size */
  9.1028 -    int objsize = PGL_KEY_OBJSIZE(key);
  9.1029 -    /* handle special cases for empty objects (universal and empty keys) */
  9.1030 -    if (objsize == PGL_KEY_OBJSIZE_EMPTY) {
  9.1031 -      pgl_box_set_empty(box);
  9.1032 -      return 0;
  9.1033 -    } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) {
  9.1034 -      box->lat_min = -90;
  9.1035 -      box->lat_max =  90;
  9.1036 -      box->lon_min = -180;
  9.1037 -      box->lon_max =  180;
  9.1038 -      return 0;  /* any value >= 0 would do */
  9.1039 -    }
  9.1040 -    /* calculate maximum possible radius of objects covered by the given key */
  9.1041 -    if (objsize == 0) radius = INFINITY;
  9.1042 -    else {
  9.1043 -      radius = PGL_AREAKEY_REFOBJSIZE;
  9.1044 -      while (--objsize) radius /= M_SQRT2;
  9.1045 -    }
  9.1046 -    /* iterate over latitude and longitude bits in key */
  9.1047 -    /* (every second bit is a latitude or longitude bit) */
  9.1048 -    for (i=0; i<depth/2; i++) {
  9.1049 -      /* check if latitude bit */
  9.1050 -      if (i%2 == 0) {
  9.1051 -        /* cut latitude dimension in half */
  9.1052 -        dlat /= 2;
  9.1053 -        /* increase center latitude if bit is 1, otherwise decrease */
  9.1054 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  9.1055 -        else lat -= dlat;
  9.1056 -      }
  9.1057 -      /* otherwise longitude bit */
  9.1058 -      else {
  9.1059 -        /* cut longitude dimension in half */
  9.1060 -        dlon /= 2;
  9.1061 -        /* increase center longitude if bit is 1, otherwise decrease */
  9.1062 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  9.1063 -        else lon -= dlon;
  9.1064 -      }
  9.1065 -    }
  9.1066 -  }
  9.1067 -  /* if not, keys are point keys */
  9.1068 -  else {
  9.1069 -    /* iterate over all bits in key */
  9.1070 -    for (i=0; i<depth; i++) {
  9.1071 -      /* check if latitude bit */
  9.1072 -      if (i%2 == 0) {
  9.1073 -        /* cut latitude dimension in half */
  9.1074 -        dlat /= 2;
  9.1075 -        /* increase center latitude if bit is 1, otherwise decrease */
  9.1076 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  9.1077 -        else lat -= dlat;
  9.1078 -      }
  9.1079 -      /* otherwise longitude bit */
  9.1080 -      else {
  9.1081 -        /* cut longitude dimension in half */
  9.1082 -        dlon /= 2;
  9.1083 -        /* increase center longitude if bit is 1, otherwise decrease */
  9.1084 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  9.1085 -        else lon -= dlon;
  9.1086 -      }
  9.1087 -    }
  9.1088 -  }
  9.1089 -  /* calculate boundaries from center point and remaining dlat and dlon */
  9.1090 -  /* (return values through pointer to box) */
  9.1091 -  box->lat_min = lat - dlat;
  9.1092 -  box->lat_max = lat + dlat;
  9.1093 -  box->lon_min = lon - dlon;
  9.1094 -  box->lon_max = lon + dlon;
  9.1095 -  /* return radius (as a function return value) */
  9.1096 -  return radius;
  9.1097 -}
  9.1098 -
  9.1099 -/* estimator function for distance between point and index key */
  9.1100 -/* allowed to return smaller values than actually correct */
  9.1101 -static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
  9.1102 -  pgl_box box;  /* center(!) bounding box of area index key */
  9.1103 -  /* calculate center(!) bounding box and maximum radius of objects covered
  9.1104 -     by area index key (radius is zero for point index keys) */
  9.1105 -  double distance = pgl_key_to_box(key, &box);
  9.1106 -  /* calculate estimated distance between bounding box of center point of
  9.1107 -     indexed object and point passed as second argument, then substract maximum
  9.1108 -     radius of objects covered by index key */
  9.1109 -  /* (use PGL_FPE_SAFETY factor to cope with minor floating point errors) */
  9.1110 -  distance = (
  9.1111 -    pgl_estimate_point_box_distance(point, &box) / PGL_FPE_SAFETY -
  9.1112 -    distance * PGL_FPE_SAFETY
  9.1113 -  );
  9.1114 -  /* truncate negative results to zero */
  9.1115 -  if (distance <= 0) distance = 0;
  9.1116 -  /* return result */
  9.1117 -  return distance;
  9.1118 -}
  9.1119 -
  9.1120 -
  9.1121 -/*---------------------------------*
  9.1122 - *  helper functions for text I/O  *
  9.1123 - *---------------------------------*/
  9.1124 -
  9.1125 -#define PGL_NUMBUFLEN 64  /* buffer size for number to string conversion */
  9.1126 -
  9.1127 -/* convert floating point number to string (round-trip safe) */
  9.1128 -static void pgl_print_float(char *buf, double flt) {
  9.1129 -  /* check if number is integral */
  9.1130 -  if (trunc(flt) == flt) {
  9.1131 -    /* for integral floats use maximum precision */
  9.1132 -    snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  9.1133 -  } else {
  9.1134 -    /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */
  9.1135 -    snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt);
  9.1136 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt);
  9.1137 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  9.1138 -  }
  9.1139 -}
  9.1140 -
  9.1141 -/* convert latitude floating point number (in degrees) to string */
  9.1142 -static void pgl_print_lat(char *buf, double lat) {
  9.1143 -  if (signbit(lat)) {
  9.1144 -    /* treat negative latitudes (including -0) as south */
  9.1145 -    snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat);
  9.1146 -  } else {
  9.1147 -    /* treat positive latitudes (including +0) as north */
  9.1148 -    snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat);
  9.1149 -  }
  9.1150 -}
  9.1151 -
  9.1152 -/* convert longitude floating point number (in degrees) to string */
  9.1153 -static void pgl_print_lon(char *buf, double lon) {
  9.1154 -  if (signbit(lon)) {
  9.1155 -    /* treat negative longitudes (including -0) as west */
  9.1156 -    snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon);
  9.1157 -  } else {
  9.1158 -    /* treat positive longitudes (including +0) as east */
  9.1159 -    snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon);
  9.1160 -  }
  9.1161 -}
  9.1162 -
  9.1163 -/* bit masks used as return value of pgl_scan() function */
  9.1164 -#define PGL_SCAN_NONE 0      /* no value has been parsed */
  9.1165 -#define PGL_SCAN_LAT (1<<0)  /* latitude has been parsed */
  9.1166 -#define PGL_SCAN_LON (1<<1)  /* longitude has been parsed */
  9.1167 -#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON)  /* bitwise OR of both */
  9.1168 -
  9.1169 -/* parse a coordinate (can be latitude or longitude) */
  9.1170 -static int pgl_scan(char **str, double *lat, double *lon) {
  9.1171 -  double val;
  9.1172 -  int len;
  9.1173 -  if (
  9.1174 -    sscanf(*str, " N %lf %n", &val, &len) ||
  9.1175 -    sscanf(*str, " n %lf %n", &val, &len)
  9.1176 -  ) {
  9.1177 -    *str += len; *lat = val; return PGL_SCAN_LAT;
  9.1178 -  }
  9.1179 -  if (
  9.1180 -    sscanf(*str, " S %lf %n", &val, &len) ||
  9.1181 -    sscanf(*str, " s %lf %n", &val, &len)
  9.1182 -  ) {
  9.1183 -    *str += len; *lat = -val; return PGL_SCAN_LAT;
  9.1184 -  }
  9.1185 -  if (
  9.1186 -    sscanf(*str, " E %lf %n", &val, &len) ||
  9.1187 -    sscanf(*str, " e %lf %n", &val, &len)
  9.1188 -  ) {
  9.1189 -    *str += len; *lon = val; return PGL_SCAN_LON;
  9.1190 -  }
  9.1191 -  if (
  9.1192 -    sscanf(*str, " W %lf %n", &val, &len) ||
  9.1193 -    sscanf(*str, " w %lf %n", &val, &len)
  9.1194 -  ) {
  9.1195 -    *str += len; *lon = -val; return PGL_SCAN_LON;
  9.1196 -  }
  9.1197 -  return PGL_SCAN_NONE;
  9.1198 -}
  9.1199 -
  9.1200 -
  9.1201 -/*-----------------*
  9.1202 - *  SQL functions  *
  9.1203 - *-----------------*/
  9.1204 -
  9.1205 -/* Note: These function names use "epoint", "ebox", etc. notation here instead
  9.1206 -   of "point", "box", etc. in order to distinguish them from any previously
  9.1207 -   defined functions. */
  9.1208 -
  9.1209 -/* function needed for dummy types and/or not implemented features */
  9.1210 -PG_FUNCTION_INFO_V1(pgl_notimpl);
  9.1211 -Datum pgl_notimpl(PG_FUNCTION_ARGS) {
  9.1212 -  ereport(ERROR, (errmsg("not implemented by pgLatLon")));
  9.1213 -}
  9.1214 -
  9.1215 -/* set point to latitude and longitude (including checks) */
  9.1216 -static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) {
  9.1217 -  /* reject infinite or NaN values */
  9.1218 -  if (!isfinite(lat) || !isfinite(lon)) {
  9.1219 -    ereport(ERROR, (
  9.1220 -      errcode(ERRCODE_DATA_EXCEPTION),
  9.1221 -      errmsg("epoint requires finite coordinates")
  9.1222 -    ));
  9.1223 -  }
  9.1224 -  /* check latitude bounds */
  9.1225 -  if (lat < -90) {
  9.1226 -    ereport(WARNING, (errmsg("latitude exceeds south pole")));
  9.1227 -    lat = -90;
  9.1228 -  } else if (lat > 90) {
  9.1229 -    ereport(WARNING, (errmsg("latitude exceeds north pole")));
  9.1230 -    lat = 90;
  9.1231 -  }
  9.1232 -  /* check longitude bounds */
  9.1233 -  if (lon < -180) {
  9.1234 -    ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized")));
  9.1235 -    lon += 360 - trunc(lon / 360) * 360;
  9.1236 -  } else if (lon > 180) {
  9.1237 -    ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized")));
  9.1238 -    lon -= 360 + trunc(lon / 360) * 360;
  9.1239 -  }
  9.1240 -  /* store rounded latitude/longitude values for round-trip safety */
  9.1241 -  point->lat = pgl_round(lat);
  9.1242 -  point->lon = pgl_round(lon);
  9.1243 -}
  9.1244 -
  9.1245 -/* create point ("epoint" in SQL) from latitude and longitude */
  9.1246 -PG_FUNCTION_INFO_V1(pgl_create_epoint);
  9.1247 -Datum pgl_create_epoint(PG_FUNCTION_ARGS) {
  9.1248 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  9.1249 -  pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1));
  9.1250 -  PG_RETURN_POINTER(point);
  9.1251 -}
  9.1252 -
  9.1253 -/* parse point ("epoint" in SQL) */
  9.1254 -/* format: '[NS]<float> [EW]<float>' */
  9.1255 -PG_FUNCTION_INFO_V1(pgl_epoint_in);
  9.1256 -Datum pgl_epoint_in(PG_FUNCTION_ARGS) {
  9.1257 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  9.1258 -  char *strptr = str;  /* current position within string */
  9.1259 -  int done = 0;        /* bit mask storing if latitude or longitude was read */
  9.1260 -  double lat, lon;     /* parsed values as double precision floats */
  9.1261 -  pgl_point *point;    /* return value (to be palloc'ed) */
  9.1262 -  /* parse two floats (each latitude or longitude) separated by white-space */
  9.1263 -  done |= pgl_scan(&strptr, &lat, &lon);
  9.1264 -  if (strptr != str && isspace(strptr[-1])) {
  9.1265 -    done |= pgl_scan(&strptr, &lat, &lon);
  9.1266 -  }
  9.1267 -  /* require end of string, and latitude and longitude parsed successfully */
  9.1268 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  9.1269 -    ereport(ERROR, (
  9.1270 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  9.1271 -      errmsg("invalid input syntax for type epoint: \"%s\"", str)
  9.1272 -    ));
  9.1273 -  }
  9.1274 -  /* allocate memory for result */
  9.1275 -  point = (pgl_point *)palloc(sizeof(pgl_point));
  9.1276 -  /* set latitude and longitude (and perform checks) */
  9.1277 -  pgl_epoint_set_latlon(point, lat, lon);
  9.1278 -  /* return result */
  9.1279 -  PG_RETURN_POINTER(point);
  9.1280 -}
  9.1281 -
  9.1282 -/* create box ("ebox" in SQL) that is empty */
  9.1283 -PG_FUNCTION_INFO_V1(pgl_create_empty_ebox);
  9.1284 -Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) {
  9.1285 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  9.1286 -  pgl_box_set_empty(box);
  9.1287 -  PG_RETURN_POINTER(box);
  9.1288 -}
  9.1289 -
  9.1290 -/* set box to given boundaries (including checks) */
  9.1291 -static void pgl_ebox_set_boundaries(
  9.1292 -  pgl_box *box,
  9.1293 -  double lat_min, double lat_max, double lon_min, double lon_max
  9.1294 -) {
  9.1295 -  /* if minimum latitude is greater than maximum latitude, return empty box */
  9.1296 -  if (lat_min > lat_max) {
  9.1297 -    pgl_box_set_empty(box);
  9.1298 -    return;
  9.1299 -  }
  9.1300 -  /* otherwise reject infinite or NaN values */
  9.1301 -  if (
  9.1302 -    !isfinite(lat_min) || !isfinite(lat_max) ||
  9.1303 -    !isfinite(lon_min) || !isfinite(lon_max)
  9.1304 -  ) {
  9.1305 -    ereport(ERROR, (
  9.1306 -      errcode(ERRCODE_DATA_EXCEPTION),
  9.1307 -      errmsg("ebox requires finite coordinates")
  9.1308 -    ));
  9.1309 -  }
  9.1310 -  /* check latitude bounds */
  9.1311 -  if (lat_max < -90) {
  9.1312 -    ereport(WARNING, (errmsg("northern latitude exceeds south pole")));
  9.1313 -    lat_max = -90;
  9.1314 -  } else if (lat_max > 90) {
  9.1315 -    ereport(WARNING, (errmsg("northern latitude exceeds north pole")));
  9.1316 -    lat_max = 90;
  9.1317 -  }
  9.1318 -  if (lat_min < -90) {
  9.1319 -    ereport(WARNING, (errmsg("southern latitude exceeds south pole")));
  9.1320 -    lat_min = -90;
  9.1321 -  } else if (lat_min > 90) {
  9.1322 -    ereport(WARNING, (errmsg("southern latitude exceeds north pole")));
  9.1323 -    lat_min = 90;
  9.1324 -  }
  9.1325 -  /* check if all longitudes are included */
  9.1326 -  if (lon_max - lon_min >= 360) {
  9.1327 -    if (lon_max - lon_min > 360) ereport(WARNING, (
  9.1328 -      errmsg("longitude coverage greater than 360 degrees")
  9.1329 -    ));
  9.1330 -    lon_min = -180;
  9.1331 -    lon_max = 180;
  9.1332 -  } else {
  9.1333 -    /* normalize longitude bounds */
  9.1334 -    if      (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360;
  9.1335 -    else if (lon_min >  180) lon_min -= 360 + trunc(lon_min / 360) * 360;
  9.1336 -    if      (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360;
  9.1337 -    else if (lon_max >  180) lon_max -= 360 + trunc(lon_max / 360) * 360;
  9.1338 -  }
  9.1339 -  /* store rounded latitude/longitude values for round-trip safety */
  9.1340 -  box->lat_min = pgl_round(lat_min);
  9.1341 -  box->lat_max = pgl_round(lat_max);
  9.1342 -  box->lon_min = pgl_round(lon_min);
  9.1343 -  box->lon_max = pgl_round(lon_max);
  9.1344 -  /* ensure that rounding does not change orientation */
  9.1345 -  if (lon_min > lon_max && box->lon_min == box->lon_max) {
  9.1346 -    box->lon_min = -180;
  9.1347 -    box->lon_max = 180;
  9.1348 -  }
  9.1349 -}
  9.1350 -
  9.1351 -/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */
  9.1352 -PG_FUNCTION_INFO_V1(pgl_create_ebox);
  9.1353 -Datum pgl_create_ebox(PG_FUNCTION_ARGS) {
  9.1354 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  9.1355 -  pgl_ebox_set_boundaries(
  9.1356 -    box,
  9.1357 -    PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1),
  9.1358 -    PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3)
  9.1359 -  );
  9.1360 -  PG_RETURN_POINTER(box);
  9.1361 -}
  9.1362 -
  9.1363 -/* create box ("ebox" in SQL) from two points ("epoint"s) */
  9.1364 -/* (can not be used to cover a longitude range of more than 120 degrees) */
  9.1365 -PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints);
  9.1366 -Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) {
  9.1367 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  9.1368 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  9.1369 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  9.1370 -  double lat_min, lat_max, lon_min, lon_max;
  9.1371 -  double dlon;  /* longitude range (delta longitude) */
  9.1372 -  /* order latitude and longitude boundaries */
  9.1373 -  if (point2->lat < point1->lat) {
  9.1374 -    lat_min = point2->lat;
  9.1375 -    lat_max = point1->lat;
  9.1376 -  } else {
  9.1377 -    lat_min = point1->lat;
  9.1378 -    lat_max = point2->lat;
  9.1379 -  }
  9.1380 -  if (point2->lon < point1->lon) {
  9.1381 -    lon_min = point2->lon;
  9.1382 -    lon_max = point1->lon;
  9.1383 -  } else {
  9.1384 -    lon_min = point1->lon;
  9.1385 -    lon_max = point2->lon;
  9.1386 -  }
  9.1387 -  /* calculate longitude range (round to avoid floating point errors) */
  9.1388 -  dlon = pgl_round(lon_max - lon_min);
  9.1389 -  /* determine east-west direction */
  9.1390 -  if (dlon >= 240) {
  9.1391 -    /* assume that 180th meridian is crossed and swap min/max longitude */
  9.1392 -    double swap = lon_min; lon_min = lon_max; lon_max = swap;
  9.1393 -  } else if (dlon > 120) {
  9.1394 -    /* unclear orientation since delta longitude > 120 */
  9.1395 -    ereport(ERROR, (
  9.1396 -      errcode(ERRCODE_DATA_EXCEPTION),
  9.1397 -      errmsg("can not determine east/west orientation for ebox")
  9.1398 -    ));
  9.1399 -  }
  9.1400 -  /* use boundaries to setup box (and perform checks) */
  9.1401 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  9.1402 -  /* return result */
  9.1403 -  PG_RETURN_POINTER(box);
  9.1404 -}
  9.1405 -
  9.1406 -/* parse box ("ebox" in SQL) */
  9.1407 -/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>'
  9.1408 -       or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */
  9.1409 -PG_FUNCTION_INFO_V1(pgl_ebox_in);
  9.1410 -Datum pgl_ebox_in(PG_FUNCTION_ARGS) {
  9.1411 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  9.1412 -  char *str_lower;     /* lower case version of input string */
  9.1413 -  char *strptr;        /* current position within string */
  9.1414 -  int valid;           /* number of valid chars */
  9.1415 -  int done;            /* specifies if latitude or longitude was read */
  9.1416 -  double val;          /* temporary variable */
  9.1417 -  int lat_count = 0;   /* count of latitude values parsed */
  9.1418 -  int lon_count = 0;   /* count of longitufde values parsed */
  9.1419 -  double lat_min, lat_max, lon_min, lon_max;  /* see pgl_box struct */
  9.1420 -  pgl_box *box;        /* return value (to be palloc'ed) */
  9.1421 -  /* lowercase input */
  9.1422 -  str_lower = psprintf("%s", str);
  9.1423 -  for (strptr=str_lower; *strptr; strptr++) {
  9.1424 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  9.1425 -  }
  9.1426 -  /* reset reading position to start of (lowercase) string */
  9.1427 -  strptr = str_lower;
  9.1428 -  /* check if empty box */
  9.1429 -  valid = 0;
  9.1430 -  sscanf(strptr, " empty %n", &valid);
  9.1431 -  if (valid && strptr[valid] == 0) {
  9.1432 -    /* allocate and return empty box */
  9.1433 -    box = (pgl_box *)palloc(sizeof(pgl_box));
  9.1434 -    pgl_box_set_empty(box);
  9.1435 -    PG_RETURN_POINTER(box);
  9.1436 -  }
  9.1437 -  /* demand four blocks separated by whitespace */
  9.1438 -  valid = 0;
  9.1439 -  sscanf(strptr, " %*s %*s %*s %*s %n", &valid);
  9.1440 -  /* if four blocks separated by whitespace exist, parse those blocks */
  9.1441 -  if (strptr[valid] == 0) while (strptr[0]) {
  9.1442 -    /* parse either latitude or longitude (whichever found in input string) */
  9.1443 -    done = pgl_scan(&strptr, &val, &val);
  9.1444 -    /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */
  9.1445 -    if (done == PGL_SCAN_LAT) {
  9.1446 -      if (!lat_count) lat_min = val; else lat_max = val;
  9.1447 -      lat_count++;
  9.1448 -    } else if (done == PGL_SCAN_LON) {
  9.1449 -      if (!lon_count) lon_min = val; else lon_max = val;
  9.1450 -      lon_count++;
  9.1451 -    } else {
  9.1452 -      break;
  9.1453 -    }
  9.1454 -  }
  9.1455 -  /* require end of string, and two latitude and two longitude values */
  9.1456 -  if (strptr[0] || lat_count != 2 || lon_count != 2) {
  9.1457 -    ereport(ERROR, (
  9.1458 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  9.1459 -      errmsg("invalid input syntax for type ebox: \"%s\"", str)
  9.1460 -    ));
  9.1461 -  }
  9.1462 -  /* free lower case string */
  9.1463 -  pfree(str_lower);
  9.1464 -  /* order boundaries (maximum greater than minimum) */
  9.1465 -  if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; }
  9.1466 -  if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; }
  9.1467 -  /* allocate memory for result */
  9.1468 -  box = (pgl_box *)palloc(sizeof(pgl_box));
  9.1469 -  /* set boundaries (and perform checks) */
  9.1470 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  9.1471 -  /* return result */
  9.1472 -  PG_RETURN_POINTER(box);
  9.1473 -}
  9.1474 -
  9.1475 -/* set circle to given latitude, longitude, and radius (including checks) */
  9.1476 -static void pgl_ecircle_set_latlon_radius(
  9.1477 -  pgl_circle *circle, double lat, double lon, double radius
  9.1478 -) {
  9.1479 -  /* set center point (including checks) */
  9.1480 -  pgl_epoint_set_latlon(&(circle->center), lat, lon);
  9.1481 -  /* handle non-positive radius */
  9.1482 -  if (isnan(radius)) {
  9.1483 -    ereport(ERROR, (
  9.1484 -      errcode(ERRCODE_DATA_EXCEPTION),
  9.1485 -      errmsg("invalid radius for ecircle")
  9.1486 -    ));
  9.1487 -  }
  9.1488 -  if (radius == 0) radius = 0;  /* avoids -0 */
  9.1489 -  else if (radius < 0) {
  9.1490 -    if (isfinite(radius)) {
  9.1491 -      ereport(NOTICE, (errmsg("negative radius converted to minus infinity")));
  9.1492 -    }
  9.1493 -    radius = -INFINITY;
  9.1494 -  }
  9.1495 -  /* store radius (round-trip safety is ensured by pgl_print_float) */
  9.1496 -  circle->radius = radius;
  9.1497 -}
  9.1498 -
  9.1499 -/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */
  9.1500 -PG_FUNCTION_INFO_V1(pgl_create_ecircle);
  9.1501 -Datum pgl_create_ecircle(PG_FUNCTION_ARGS) {
  9.1502 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  9.1503 -  pgl_ecircle_set_latlon_radius(
  9.1504 -    circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2)
  9.1505 -  );
  9.1506 -  PG_RETURN_POINTER(circle);
  9.1507 -}
  9.1508 -
  9.1509 -/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */
  9.1510 -PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint);
  9.1511 -Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) {
  9.1512 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.1513 -  double radius = PG_GETARG_FLOAT8(1);
  9.1514 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  9.1515 -  /* set latitude, longitude, radius (and perform checks) */
  9.1516 -  pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius);
  9.1517 -  /* return result */
  9.1518 -  PG_RETURN_POINTER(circle);
  9.1519 -}
  9.1520 -
  9.1521 -/* parse circle ("ecircle" in SQL) */
  9.1522 -/* format: '[NS]<float> [EW]<float> <float>' */
  9.1523 -PG_FUNCTION_INFO_V1(pgl_ecircle_in);
  9.1524 -Datum pgl_ecircle_in(PG_FUNCTION_ARGS) {
  9.1525 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  9.1526 -  char *strptr = str;       /* current position within string */
  9.1527 -  double lat, lon, radius;  /* parsed values as double precision flaots */
  9.1528 -  int valid = 0;            /* number of valid chars */
  9.1529 -  int done = 0;             /* stores if latitude and/or longitude was read */
  9.1530 -  pgl_circle *circle;       /* return value (to be palloc'ed) */
  9.1531 -  /* demand three blocks separated by whitespace */
  9.1532 -  sscanf(strptr, " %*s %*s %*s %n", &valid);
  9.1533 -  /* if three blocks separated by whitespace exist, parse those blocks */
  9.1534 -  if (strptr[valid] == 0) {
  9.1535 -    /* parse latitude and longitude */
  9.1536 -    done |= pgl_scan(&strptr, &lat, &lon);
  9.1537 -    done |= pgl_scan(&strptr, &lat, &lon);
  9.1538 -    /* parse radius (while incrementing strptr by number of bytes parsed) */
  9.1539 -    valid = 0;
  9.1540 -    if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid;
  9.1541 -  }
  9.1542 -  /* require end of string and both latitude and longitude being parsed */
  9.1543 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  9.1544 -    ereport(ERROR, (
  9.1545 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  9.1546 -      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  9.1547 -    ));
  9.1548 -  }
  9.1549 -  /* allocate memory for result */
  9.1550 -  circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  9.1551 -  /* set latitude, longitude, radius (and perform checks) */
  9.1552 -  pgl_ecircle_set_latlon_radius(circle, lat, lon, radius);
  9.1553 -  /* return result */
  9.1554 -  PG_RETURN_POINTER(circle);
  9.1555 -}
  9.1556 -
  9.1557 -/* parse cluster ("ecluster" in SQL) */
  9.1558 -PG_FUNCTION_INFO_V1(pgl_ecluster_in);
  9.1559 -Datum pgl_ecluster_in(PG_FUNCTION_ARGS) {
  9.1560 -  int i;
  9.1561 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  9.1562 -  char *str_lower;         /* lower case version of input string */
  9.1563 -  char *strptr;            /* pointer to current reading position of input */
  9.1564 -  int npoints_total = 0;   /* total number of points in cluster */
  9.1565 -  int nentries = 0;        /* total number of entries */
  9.1566 -  pgl_newentry *entries;   /* array of pgl_newentry to create pgl_cluster */
  9.1567 -  int entries_buflen = 4;  /* maximum number of elements in entries array */
  9.1568 -  int valid;               /* number of valid chars processed */
  9.1569 -  double lat, lon;         /* latitude and longitude of parsed point */
  9.1570 -  int entrytype;           /* current entry type */
  9.1571 -  int npoints;             /* number of points in current entry */
  9.1572 -  pgl_point *points;       /* array of pgl_point for pgl_newentry */
  9.1573 -  int points_buflen;       /* maximum number of elements in points array */
  9.1574 -  int done;                /* return value of pgl_scan function */
  9.1575 -  pgl_cluster *cluster;    /* created cluster */
  9.1576 -  /* lowercase input */
  9.1577 -  str_lower = psprintf("%s", str);
  9.1578 -  for (strptr=str_lower; *strptr; strptr++) {
  9.1579 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  9.1580 -  }
  9.1581 -  /* reset reading position to start of (lowercase) string */
  9.1582 -  strptr = str_lower;
  9.1583 -  /* allocate initial buffer for entries */
  9.1584 -  entries = palloc(entries_buflen * sizeof(pgl_newentry));
  9.1585 -  /* parse until end of string */
  9.1586 -  while (strptr[0]) {
  9.1587 -    /* require previous white-space or closing parenthesis before next token */
  9.1588 -    if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') {
  9.1589 -      goto pgl_ecluster_in_error;
  9.1590 -    }
  9.1591 -    /* ignore token "empty" */
  9.1592 -    valid = 0; sscanf(strptr, " empty %n", &valid);
  9.1593 -    if (valid) { strptr += valid; continue; }
  9.1594 -    /* test for "point" token */
  9.1595 -    valid = 0; sscanf(strptr, " point ( %n", &valid);
  9.1596 -    if (valid) {
  9.1597 -      strptr += valid;
  9.1598 -      entrytype = PGL_ENTRY_POINT;
  9.1599 -      goto pgl_ecluster_in_type_ok;
  9.1600 -    }
  9.1601 -    /* test for "path" token */
  9.1602 -    valid = 0; sscanf(strptr, " path ( %n", &valid);
  9.1603 -    if (valid) {
  9.1604 -      strptr += valid;
  9.1605 -      entrytype = PGL_ENTRY_PATH;
  9.1606 -      goto pgl_ecluster_in_type_ok;
  9.1607 -    }
  9.1608 -    /* test for "outline" token */
  9.1609 -    valid = 0; sscanf(strptr, " outline ( %n", &valid);
  9.1610 -    if (valid) {
  9.1611 -      strptr += valid;
  9.1612 -      entrytype = PGL_ENTRY_OUTLINE;
  9.1613 -      goto pgl_ecluster_in_type_ok;
  9.1614 -    }
  9.1615 -    /* test for "polygon" token */
  9.1616 -    valid = 0; sscanf(strptr, " polygon ( %n", &valid);
  9.1617 -    if (valid) {
  9.1618 -      strptr += valid;
  9.1619 -      entrytype = PGL_ENTRY_POLYGON;
  9.1620 -      goto pgl_ecluster_in_type_ok;
  9.1621 -    }
  9.1622 -    /* error if no valid token found */
  9.1623 -    goto pgl_ecluster_in_error;
  9.1624 -    pgl_ecluster_in_type_ok:
  9.1625 -    /* check if pgl_newentry array needs to grow */
  9.1626 -    if (nentries == entries_buflen) {
  9.1627 -      pgl_newentry *newbuf;
  9.1628 -      entries_buflen *= 2;
  9.1629 -      newbuf = palloc(entries_buflen * sizeof(pgl_newentry));
  9.1630 -      memcpy(newbuf, entries, nentries * sizeof(pgl_newentry));
  9.1631 -      pfree(entries);
  9.1632 -      entries = newbuf;
  9.1633 -    }
  9.1634 -    /* reset number of points for current entry */
  9.1635 -    npoints = 0;
  9.1636 -    /* allocate array for points */
  9.1637 -    points_buflen = 4;
  9.1638 -    points = palloc(points_buflen * sizeof(pgl_point));
  9.1639 -    /* parse until closing parenthesis */
  9.1640 -    while (strptr[0] != ')') {
  9.1641 -      /* error on unexpected end of string */
  9.1642 -      if (strptr[0] == 0) goto pgl_ecluster_in_error;
  9.1643 -      /* mark neither latitude nor longitude as read */
  9.1644 -      done = PGL_SCAN_NONE;
  9.1645 -      /* require white-space before second, third, etc. point */
  9.1646 -      if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  9.1647 -      /* scan latitude (or longitude) */
  9.1648 -      done |= pgl_scan(&strptr, &lat, &lon);
  9.1649 -      /* require white-space before second coordinate */
  9.1650 -      if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  9.1651 -      /* scan longitude (or latitude) */
  9.1652 -      done |= pgl_scan(&strptr, &lat, &lon);
  9.1653 -      /* error unless both latitude and longitude were parsed */
  9.1654 -      if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error;
  9.1655 -      /* throw error if number of points is too high */
  9.1656 -      if (npoints_total == PGL_CLUSTER_MAXPOINTS) {
  9.1657 -        ereport(ERROR, (
  9.1658 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  9.1659 -          errmsg(
  9.1660 -            "too many points for ecluster entry (maximum %i)",
  9.1661 -            PGL_CLUSTER_MAXPOINTS
  9.1662 -          )
  9.1663 -        ));
  9.1664 -      }
  9.1665 -      /* check if pgl_point array needs to grow */
  9.1666 -      if (npoints == points_buflen) {
  9.1667 -        pgl_point *newbuf;
  9.1668 -        points_buflen *= 2;
  9.1669 -        newbuf = palloc(points_buflen * sizeof(pgl_point));
  9.1670 -        memcpy(newbuf, points, npoints * sizeof(pgl_point));
  9.1671 -        pfree(points);
  9.1672 -        points = newbuf;
  9.1673 -      }
  9.1674 -      /* append point to pgl_point array (includes checks) */
  9.1675 -      pgl_epoint_set_latlon(&(points[npoints++]), lat, lon);
  9.1676 -      /* increase total number of points */
  9.1677 -      npoints_total++;
  9.1678 -    }
  9.1679 -    /* error if entry has no points */
  9.1680 -    if (!npoints) goto pgl_ecluster_in_error;
  9.1681 -    /* entries with one point are automatically of type "point" */
  9.1682 -    if (npoints == 1) entrytype = PGL_ENTRY_POINT;
  9.1683 -    /* if entries have more than one point */
  9.1684 -    else {
  9.1685 -      /* throw error if entry type is "point" */
  9.1686 -      if (entrytype == PGL_ENTRY_POINT) {
  9.1687 -        ereport(ERROR, (
  9.1688 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  9.1689 -          errmsg("invalid input syntax for type ecluster (point entry with more than one point)")
  9.1690 -        ));
  9.1691 -      }
  9.1692 -      /* coerce outlines and polygons with more than 2 points to be a path */
  9.1693 -      if (npoints == 2) entrytype = PGL_ENTRY_PATH;
  9.1694 -    }
  9.1695 -    /* append entry to pgl_newentry array */
  9.1696 -    entries[nentries].entrytype = entrytype;
  9.1697 -    entries[nentries].npoints = npoints;
  9.1698 -    entries[nentries].points = points;
  9.1699 -    nentries++;
  9.1700 -    /* consume closing parenthesis */
  9.1701 -    strptr++;
  9.1702 -    /* consume white-space */
  9.1703 -    while (isspace(strptr[0])) strptr++;
  9.1704 -  }
  9.1705 -  /* free lower case string */
  9.1706 -  pfree(str_lower);
  9.1707 -  /* create cluster from pgl_newentry array */
  9.1708 -  cluster = pgl_new_cluster(nentries, entries);
  9.1709 -  /* free pgl_newentry array */
  9.1710 -  for (i=0; i<nentries; i++) pfree(entries[i].points);
  9.1711 -  pfree(entries);
  9.1712 -  /* set bounding circle of cluster and check east/west orientation */
  9.1713 -  if (!pgl_finalize_cluster(cluster)) {
  9.1714 -    ereport(ERROR, (
  9.1715 -      errcode(ERRCODE_DATA_EXCEPTION),
  9.1716 -      errmsg("can not determine east/west orientation for ecluster"),
  9.1717 -      errhint("Ensure that each entry has a longitude span of less than 180 degrees.")
  9.1718 -    ));
  9.1719 -  }
  9.1720 -  /* return cluster */
  9.1721 -  PG_RETURN_POINTER(cluster);
  9.1722 -  /* code to throw error */
  9.1723 -  pgl_ecluster_in_error:
  9.1724 -  ereport(ERROR, (
  9.1725 -    errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  9.1726 -    errmsg("invalid input syntax for type ecluster: \"%s\"", str)
  9.1727 -  ));
  9.1728 -}
  9.1729 -
  9.1730 -/* convert point ("epoint") to string representation */
  9.1731 -PG_FUNCTION_INFO_V1(pgl_epoint_out);
  9.1732 -Datum pgl_epoint_out(PG_FUNCTION_ARGS) {
  9.1733 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.1734 -  char latstr[PGL_NUMBUFLEN];
  9.1735 -  char lonstr[PGL_NUMBUFLEN];
  9.1736 -  pgl_print_lat(latstr, point->lat);
  9.1737 -  pgl_print_lon(lonstr, point->lon);
  9.1738 -  PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr));
  9.1739 -}
  9.1740 -
  9.1741 -/* convert box ("ebox") to string representation */
  9.1742 -PG_FUNCTION_INFO_V1(pgl_ebox_out);
  9.1743 -Datum pgl_ebox_out(PG_FUNCTION_ARGS) {
  9.1744 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  9.1745 -  double lon_min = box->lon_min;
  9.1746 -  double lon_max = box->lon_max;
  9.1747 -  char lat_min_str[PGL_NUMBUFLEN];
  9.1748 -  char lat_max_str[PGL_NUMBUFLEN];
  9.1749 -  char lon_min_str[PGL_NUMBUFLEN];
  9.1750 -  char lon_max_str[PGL_NUMBUFLEN];
  9.1751 -  /* return string "empty" if box is set to be empty */
  9.1752 -  if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty");
  9.1753 -  /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */
  9.1754 -  /* (required since pgl_box_in orders the longitude boundaries) */
  9.1755 -  if (lon_min > lon_max) {
  9.1756 -    if (lon_min + lon_max >= 0) lon_min -= 360;
  9.1757 -    else lon_max += 360;
  9.1758 -  }
  9.1759 -  /* format and return result */
  9.1760 -  pgl_print_lat(lat_min_str, box->lat_min);
  9.1761 -  pgl_print_lat(lat_max_str, box->lat_max);
  9.1762 -  pgl_print_lon(lon_min_str, lon_min);
  9.1763 -  pgl_print_lon(lon_max_str, lon_max);
  9.1764 -  PG_RETURN_CSTRING(psprintf(
  9.1765 -    "%s %s %s %s",
  9.1766 -    lat_min_str, lon_min_str, lat_max_str, lon_max_str
  9.1767 -  ));
  9.1768 -}
  9.1769 -
  9.1770 -/* convert circle ("ecircle") to string representation */
  9.1771 -PG_FUNCTION_INFO_V1(pgl_ecircle_out);
  9.1772 -Datum pgl_ecircle_out(PG_FUNCTION_ARGS) {
  9.1773 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  9.1774 -  char latstr[PGL_NUMBUFLEN];
  9.1775 -  char lonstr[PGL_NUMBUFLEN];
  9.1776 -  char radstr[PGL_NUMBUFLEN];
  9.1777 -  pgl_print_lat(latstr, circle->center.lat);
  9.1778 -  pgl_print_lon(lonstr, circle->center.lon);
  9.1779 -  pgl_print_float(radstr, circle->radius);
  9.1780 -  PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr));
  9.1781 -}
  9.1782 -
  9.1783 -/* convert cluster ("ecluster") to string representation */
  9.1784 -PG_FUNCTION_INFO_V1(pgl_ecluster_out);
  9.1785 -Datum pgl_ecluster_out(PG_FUNCTION_ARGS) {
  9.1786 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  9.1787 -  char latstr[PGL_NUMBUFLEN];  /* string buffer for latitude */
  9.1788 -  char lonstr[PGL_NUMBUFLEN];  /* string buffer for longitude */
  9.1789 -  char ***strings;     /* array of array of strings */
  9.1790 -  char *string;        /* string of current token */
  9.1791 -  char *res, *resptr;  /* result and pointer to current write position */
  9.1792 -  size_t reslen = 1;   /* length of result (init with 1 for terminator) */
  9.1793 -  int npoints;         /* number of points of current entry */
  9.1794 -  int i, j;            /* i: entry, j: point in entry */
  9.1795 -  /* handle empty clusters */
  9.1796 -  if (cluster->nentries == 0) {
  9.1797 -    /* free detoasted cluster (if copy) */
  9.1798 -    PG_FREE_IF_COPY(cluster, 0);
  9.1799 -    /* return static result */
  9.1800 -    PG_RETURN_CSTRING("empty");
  9.1801 -  }
  9.1802 -  /* allocate array of array of strings */
  9.1803 -  strings = palloc(cluster->nentries * sizeof(char **));
  9.1804 -  /* iterate over all entries in cluster */
  9.1805 -  for (i=0; i<cluster->nentries; i++) {
  9.1806 -    /* get number of points in entry */
  9.1807 -    npoints = cluster->entries[i].npoints;
  9.1808 -    /* allocate array of strings (one string for each point plus two extra) */
  9.1809 -    strings[i] = palloc((2 + npoints) * sizeof(char *));
  9.1810 -    /* determine opening string */
  9.1811 -    switch (cluster->entries[i].entrytype) {
  9.1812 -      case PGL_ENTRY_POINT:   string = (i==0)?"point ("  :" point (";   break;
  9.1813 -      case PGL_ENTRY_PATH:    string = (i==0)?"path ("   :" path (";    break;
  9.1814 -      case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break;
  9.1815 -      case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break;
  9.1816 -      default:                string = (i==0)?"unknown"  :" unknown";
  9.1817 -    }
  9.1818 -    /* use opening string as first string in array */
  9.1819 -    strings[i][0] = string;
  9.1820 -    /* update result length (for allocating result string later) */
  9.1821 -    reslen += strlen(string);
  9.1822 -    /* iterate over all points */
  9.1823 -    for (j=0; j<npoints; j++) {
  9.1824 -      /* create string representation of point */
  9.1825 -      pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat);
  9.1826 -      pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon);
  9.1827 -      string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr);
  9.1828 -      /* copy string pointer to string array */
  9.1829 -      strings[i][j+1] = string;
  9.1830 -      /* update result length (for allocating result string later) */
  9.1831 -      reslen += strlen(string);
  9.1832 -    }
  9.1833 -    /* use closing parenthesis as last string in array */
  9.1834 -    strings[i][npoints+1] = ")";
  9.1835 -    /* update result length (for allocating result string later) */
  9.1836 -    reslen++;
  9.1837 -  }
  9.1838 -  /* allocate result string */
  9.1839 -  res = palloc(reslen);
  9.1840 -  /* set write pointer to begin of result string */
  9.1841 -  resptr = res;
  9.1842 -  /* copy strings into result string */
  9.1843 -  for (i=0; i<cluster->nentries; i++) {
  9.1844 -    npoints = cluster->entries[i].npoints;
  9.1845 -    for (j=0; j<npoints+2; j++) {
  9.1846 -      string = strings[i][j];
  9.1847 -      strcpy(resptr, string);
  9.1848 -      resptr += strlen(string);
  9.1849 -      /* free strings allocated by psprintf */
  9.1850 -      if (j != 0 && j != npoints+1) pfree(string);
  9.1851 -    }
  9.1852 -    /* free array of strings */
  9.1853 -    pfree(strings[i]);
  9.1854 -  }
  9.1855 -  /* free array of array of strings */
  9.1856 -  pfree(strings);
  9.1857 -  /* free detoasted cluster (if copy) */
  9.1858 -  PG_FREE_IF_COPY(cluster, 0);
  9.1859 -  /* return result */
  9.1860 -  PG_RETURN_CSTRING(res);
  9.1861 -}
  9.1862 -
  9.1863 -/* binary input function for point ("epoint") */
  9.1864 -PG_FUNCTION_INFO_V1(pgl_epoint_recv);
  9.1865 -Datum pgl_epoint_recv(PG_FUNCTION_ARGS) {
  9.1866 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  9.1867 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  9.1868 -  point->lat = pq_getmsgfloat8(buf);
  9.1869 -  point->lon = pq_getmsgfloat8(buf);
  9.1870 -  PG_RETURN_POINTER(point);
  9.1871 -}
  9.1872 -
  9.1873 -/* binary input function for box ("ebox") */
  9.1874 -PG_FUNCTION_INFO_V1(pgl_ebox_recv);
  9.1875 -Datum pgl_ebox_recv(PG_FUNCTION_ARGS) {
  9.1876 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  9.1877 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  9.1878 -  box->lat_min = pq_getmsgfloat8(buf);
  9.1879 -  box->lat_max = pq_getmsgfloat8(buf);
  9.1880 -  box->lon_min = pq_getmsgfloat8(buf);
  9.1881 -  box->lon_max = pq_getmsgfloat8(buf);
  9.1882 -  PG_RETURN_POINTER(box);
  9.1883 -}
  9.1884 -
  9.1885 -/* binary input function for circle ("ecircle") */
  9.1886 -PG_FUNCTION_INFO_V1(pgl_ecircle_recv);
  9.1887 -Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) {
  9.1888 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  9.1889 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  9.1890 -  circle->center.lat = pq_getmsgfloat8(buf);
  9.1891 -  circle->center.lon = pq_getmsgfloat8(buf);
  9.1892 -  circle->radius = pq_getmsgfloat8(buf);
  9.1893 -  PG_RETURN_POINTER(circle);
  9.1894 -}
  9.1895 -
  9.1896 -/* TODO: binary receive function for cluster */
  9.1897 -
  9.1898 -/* binary output function for point ("epoint") */
  9.1899 -PG_FUNCTION_INFO_V1(pgl_epoint_send);
  9.1900 -Datum pgl_epoint_send(PG_FUNCTION_ARGS) {
  9.1901 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.1902 -  StringInfoData buf;
  9.1903 -  pq_begintypsend(&buf);
  9.1904 -  pq_sendfloat8(&buf, point->lat);
  9.1905 -  pq_sendfloat8(&buf, point->lon);
  9.1906 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  9.1907 -}
  9.1908 -
  9.1909 -/* binary output function for box ("ebox") */
  9.1910 -PG_FUNCTION_INFO_V1(pgl_ebox_send);
  9.1911 -Datum pgl_ebox_send(PG_FUNCTION_ARGS) {
  9.1912 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  9.1913 -  StringInfoData buf;
  9.1914 -  pq_begintypsend(&buf);
  9.1915 -  pq_sendfloat8(&buf, box->lat_min);
  9.1916 -  pq_sendfloat8(&buf, box->lat_max);
  9.1917 -  pq_sendfloat8(&buf, box->lon_min);
  9.1918 -  pq_sendfloat8(&buf, box->lon_max);
  9.1919 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  9.1920 -}
  9.1921 -
  9.1922 -/* binary output function for circle ("ecircle") */
  9.1923 -PG_FUNCTION_INFO_V1(pgl_ecircle_send);
  9.1924 -Datum pgl_ecircle_send(PG_FUNCTION_ARGS) {
  9.1925 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  9.1926 -  StringInfoData buf;
  9.1927 -  pq_begintypsend(&buf);
  9.1928 -  pq_sendfloat8(&buf, circle->center.lat);
  9.1929 -  pq_sendfloat8(&buf, circle->center.lon);
  9.1930 -  pq_sendfloat8(&buf, circle->radius);
  9.1931 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  9.1932 -}
  9.1933 -
  9.1934 -/* TODO: binary send functions for cluster */
  9.1935 -
  9.1936 -/* cast point ("epoint") to box ("ebox") */
  9.1937 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox);
  9.1938 -Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) {
  9.1939 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.1940 -  pgl_box *box = palloc(sizeof(pgl_box));
  9.1941 -  box->lat_min = point->lat;
  9.1942 -  box->lat_max = point->lat;
  9.1943 -  box->lon_min = point->lon;
  9.1944 -  box->lon_max = point->lon;
  9.1945 -  PG_RETURN_POINTER(box);
  9.1946 -}
  9.1947 -
  9.1948 -/* cast point ("epoint") to circle ("ecircle") */
  9.1949 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle);
  9.1950 -Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) {
  9.1951 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.1952 -  pgl_circle *circle = palloc(sizeof(pgl_box));
  9.1953 -  circle->center = *point;
  9.1954 -  circle->radius = 0;
  9.1955 -  PG_RETURN_POINTER(circle);
  9.1956 -}
  9.1957 -
  9.1958 -/* cast point ("epoint") to cluster ("ecluster") */
  9.1959 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster);
  9.1960 -Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) {
  9.1961 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.1962 -  pgl_newentry entry;
  9.1963 -  entry.entrytype = PGL_ENTRY_POINT;
  9.1964 -  entry.npoints = 1;
  9.1965 -  entry.points = point;
  9.1966 -  PG_RETURN_POINTER(pgl_new_cluster(1, &entry));
  9.1967 -}
  9.1968 -
  9.1969 -/* cast box ("ebox") to cluster ("ecluster") */
  9.1970 -#define pgl_ebox_to_ecluster_macro(i, a, b) \
  9.1971 -  entries[i].entrytype = PGL_ENTRY_POLYGON; \
  9.1972 -  entries[i].npoints = 4; \
  9.1973 -  entries[i].points = points[i]; \
  9.1974 -  points[i][0].lat = box->lat_min; \
  9.1975 -  points[i][0].lon = (a); \
  9.1976 -  points[i][1].lat = box->lat_min; \
  9.1977 -  points[i][1].lon = (b); \
  9.1978 -  points[i][2].lat = box->lat_max; \
  9.1979 -  points[i][2].lon = (b); \
  9.1980 -  points[i][3].lat = box->lat_max; \
  9.1981 -  points[i][3].lon = (a);
  9.1982 -PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster);
  9.1983 -Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) {
  9.1984 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  9.1985 -  double lon, dlon;
  9.1986 -  int nentries;
  9.1987 -  pgl_newentry entries[3];
  9.1988 -  pgl_point points[3][4];
  9.1989 -  if (box->lat_min > box->lat_max) {
  9.1990 -    nentries = 0;
  9.1991 -  } else if (box->lon_min > box->lon_max) {
  9.1992 -    if (box->lon_min < 0) {
  9.1993 -      lon = pgl_round((box->lon_min + 180) / 2.0);
  9.1994 -      nentries = 3;
  9.1995 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  9.1996 -      pgl_ebox_to_ecluster_macro(1, lon, 180);
  9.1997 -      pgl_ebox_to_ecluster_macro(2, -180, box->lon_max);
  9.1998 -    } else if (box->lon_max > 0) {
  9.1999 -      lon = pgl_round((box->lon_max - 180) / 2.0);
  9.2000 -      nentries = 3;
  9.2001 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  9.2002 -      pgl_ebox_to_ecluster_macro(1, -180, lon);
  9.2003 -      pgl_ebox_to_ecluster_macro(2, lon, box->lon_max);
  9.2004 -    } else {
  9.2005 -      nentries = 2;
  9.2006 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  9.2007 -      pgl_ebox_to_ecluster_macro(1, -180, box->lon_max);
  9.2008 -    }
  9.2009 -  } else {
  9.2010 -    dlon = pgl_round(box->lon_max - box->lon_min);
  9.2011 -    if (dlon < 180) {
  9.2012 -      nentries = 1;
  9.2013 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max);
  9.2014 -    } else {
  9.2015 -      lon = pgl_round((box->lon_min + box->lon_max) / 2.0);
  9.2016 -      if (
  9.2017 -        pgl_round(lon - box->lon_min) < 180 &&
  9.2018 -        pgl_round(box->lon_max - lon) < 180
  9.2019 -      ) {
  9.2020 -        nentries = 2;
  9.2021 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  9.2022 -        pgl_ebox_to_ecluster_macro(1, lon, box->lon_max);
  9.2023 -      } else {
  9.2024 -        nentries = 3;
  9.2025 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, -60);
  9.2026 -        pgl_ebox_to_ecluster_macro(1, -60, 60);
  9.2027 -        pgl_ebox_to_ecluster_macro(2, 60, box->lon_max);
  9.2028 -      }
  9.2029 -    }
  9.2030 -  }
  9.2031 -  PG_RETURN_POINTER(pgl_new_cluster(nentries, entries));
  9.2032 -}
  9.2033 -
  9.2034 -/* extract latitude from point ("epoint") */
  9.2035 -PG_FUNCTION_INFO_V1(pgl_epoint_lat);
  9.2036 -Datum pgl_epoint_lat(PG_FUNCTION_ARGS) {
  9.2037 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat);
  9.2038 -}
  9.2039 -
  9.2040 -/* extract longitude from point ("epoint") */
  9.2041 -PG_FUNCTION_INFO_V1(pgl_epoint_lon);
  9.2042 -Datum pgl_epoint_lon(PG_FUNCTION_ARGS) {
  9.2043 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon);
  9.2044 -}
  9.2045 -
  9.2046 -/* extract minimum latitude from box ("ebox") */
  9.2047 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_min);
  9.2048 -Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) {
  9.2049 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min);
  9.2050 -}
  9.2051 -
  9.2052 -/* extract maximum latitude from box ("ebox") */
  9.2053 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_max);
  9.2054 -Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) {
  9.2055 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max);
  9.2056 -}
  9.2057 -
  9.2058 -/* extract minimum longitude from box ("ebox") */
  9.2059 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_min);
  9.2060 -Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) {
  9.2061 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min);
  9.2062 -}
  9.2063 -
  9.2064 -/* extract maximum longitude from box ("ebox") */
  9.2065 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_max);
  9.2066 -Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) {
  9.2067 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max);
  9.2068 -}
  9.2069 -
  9.2070 -/* extract center point from circle ("ecircle") */
  9.2071 -PG_FUNCTION_INFO_V1(pgl_ecircle_center);
  9.2072 -Datum pgl_ecircle_center(PG_FUNCTION_ARGS) {
  9.2073 -  PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center));
  9.2074 -}
  9.2075 -
  9.2076 -/* extract radius from circle ("ecircle") */
  9.2077 -PG_FUNCTION_INFO_V1(pgl_ecircle_radius);
  9.2078 -Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) {
  9.2079 -  PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius);
  9.2080 -}
  9.2081 -
  9.2082 -/* check if point is inside box (overlap operator "&&") in SQL */
  9.2083 -PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap);
  9.2084 -Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) {
  9.2085 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.2086 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1);
  9.2087 -  PG_RETURN_BOOL(pgl_point_in_box(point, box));
  9.2088 -}
  9.2089 -
  9.2090 -/* check if point is inside circle (overlap operator "&&") in SQL */
  9.2091 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap);
  9.2092 -Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) {
  9.2093 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.2094 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  9.2095 -  PG_RETURN_BOOL(
  9.2096 -    pgl_distance(
  9.2097 -      point->lat, point->lon,
  9.2098 -      circle->center.lat, circle->center.lon
  9.2099 -    ) <= circle->radius
  9.2100 -  );
  9.2101 -}
  9.2102 -
  9.2103 -/* check if point is inside cluster (overlap operator "&&") in SQL */
  9.2104 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap);
  9.2105 -Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
  9.2106 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.2107 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2108 -  bool retval = pgl_point_in_cluster(point, cluster);
  9.2109 -  PG_FREE_IF_COPY(cluster, 1);
  9.2110 -  PG_RETURN_BOOL(retval);
  9.2111 -}
  9.2112 -
  9.2113 -/* check if point may be inside cluster (lossy overl. operator "&&+") in SQL */
  9.2114 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_may_overlap);
  9.2115 -Datum pgl_epoint_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  9.2116 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.2117 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2118 -  bool retval = pgl_distance(
  9.2119 -    point->lat, point->lon,
  9.2120 -    cluster->bounding.center.lat, cluster->bounding.center.lon
  9.2121 -  ) <= cluster->bounding.radius;
  9.2122 -  PG_FREE_IF_COPY(cluster, 1);
  9.2123 -  PG_RETURN_BOOL(retval);
  9.2124 -}
  9.2125 -
  9.2126 -/* check if two boxes overlap (overlap operator "&&") in SQL */
  9.2127 -PG_FUNCTION_INFO_V1(pgl_ebox_overlap);
  9.2128 -Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) {
  9.2129 -  pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0);
  9.2130 -  pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1);
  9.2131 -  PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2));
  9.2132 -}
  9.2133 -
  9.2134 -/* check if box and circle may overlap (lossy overl. operator "&&+") in SQL */
  9.2135 -PG_FUNCTION_INFO_V1(pgl_ebox_ecircle_may_overlap);
  9.2136 -Datum pgl_ebox_ecircle_may_overlap(PG_FUNCTION_ARGS) {
  9.2137 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  9.2138 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  9.2139 -  PG_RETURN_BOOL(
  9.2140 -    pgl_estimate_point_box_distance(&circle->center, box) <= circle->radius
  9.2141 -  );
  9.2142 -}
  9.2143 -
  9.2144 -/* check if box and cluster may overlap (lossy overl. operator "&&+") in SQL */
  9.2145 -PG_FUNCTION_INFO_V1(pgl_ebox_ecluster_may_overlap);
  9.2146 -Datum pgl_ebox_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  9.2147 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  9.2148 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2149 -  bool retval = pgl_estimate_point_box_distance(
  9.2150 -    &cluster->bounding.center,
  9.2151 -    box
  9.2152 -  ) <= cluster->bounding.radius;
  9.2153 -  PG_FREE_IF_COPY(cluster, 1);
  9.2154 -  PG_RETURN_BOOL(retval);
  9.2155 -}
  9.2156 -
  9.2157 -/* check if two circles overlap (overlap operator "&&") in SQL */
  9.2158 -PG_FUNCTION_INFO_V1(pgl_ecircle_overlap);
  9.2159 -Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) {
  9.2160 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  9.2161 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  9.2162 -  PG_RETURN_BOOL(
  9.2163 -    pgl_distance(
  9.2164 -      circle1->center.lat, circle1->center.lon,
  9.2165 -      circle2->center.lat, circle2->center.lon
  9.2166 -    ) <= circle1->radius + circle2->radius
  9.2167 -  );
  9.2168 -}
  9.2169 -
  9.2170 -/* check if circle and cluster overlap (overlap operator "&&") in SQL */
  9.2171 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap);
  9.2172 -Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) {
  9.2173 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  9.2174 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2175 -  bool retval = (
  9.2176 -    pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius
  9.2177 -  );
  9.2178 -  PG_FREE_IF_COPY(cluster, 1);
  9.2179 -  PG_RETURN_BOOL(retval);
  9.2180 -}
  9.2181 -
  9.2182 -/* check if circle and cluster may be overlap (l. ov. operator "&&+") in SQL */
  9.2183 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_may_overlap);
  9.2184 -Datum pgl_ecircle_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  9.2185 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  9.2186 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2187 -  bool retval = pgl_distance(
  9.2188 -    circle->center.lat, circle->center.lon,
  9.2189 -    cluster->bounding.center.lat, cluster->bounding.center.lon
  9.2190 -  ) <= circle->radius + cluster->bounding.radius;
  9.2191 -  PG_FREE_IF_COPY(cluster, 1);
  9.2192 -  PG_RETURN_BOOL(retval);
  9.2193 -}
  9.2194 -
  9.2195 -/* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */
  9.2196 -PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap);
  9.2197 -Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  9.2198 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  9.2199 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2200 -  bool retval = pgl_distance(
  9.2201 -    cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  9.2202 -    cluster2->bounding.center.lat, cluster2->bounding.center.lon
  9.2203 -  ) <= cluster1->bounding.radius + cluster2->bounding.radius;
  9.2204 -  PG_FREE_IF_COPY(cluster1, 0);
  9.2205 -  PG_FREE_IF_COPY(cluster2, 1);
  9.2206 -  PG_RETURN_BOOL(retval);
  9.2207 -}
  9.2208 -
  9.2209 -/* calculate distance between two points ("<->" operator) in SQL */
  9.2210 -PG_FUNCTION_INFO_V1(pgl_epoint_distance);
  9.2211 -Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
  9.2212 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  9.2213 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  9.2214 -  PG_RETURN_FLOAT8(pgl_distance(
  9.2215 -    point1->lat, point1->lon, point2->lat, point2->lon
  9.2216 -  ));
  9.2217 -}
  9.2218 -
  9.2219 -/* calculate point to circle distance ("<->" operator) in SQL */
  9.2220 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance);
  9.2221 -Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) {
  9.2222 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.2223 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  9.2224 -  double distance = pgl_distance(
  9.2225 -    point->lat, point->lon, circle->center.lat, circle->center.lon
  9.2226 -  ) - circle->radius;
  9.2227 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  9.2228 -}
  9.2229 -
  9.2230 -/* calculate point to cluster distance ("<->" operator) in SQL */
  9.2231 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance);
  9.2232 -Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) {
  9.2233 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  9.2234 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2235 -  double distance = pgl_point_cluster_distance(point, cluster);
  9.2236 -  PG_FREE_IF_COPY(cluster, 1);
  9.2237 -  PG_RETURN_FLOAT8(distance);
  9.2238 -}
  9.2239 -
  9.2240 -/* calculate distance between two circles ("<->" operator) in SQL */
  9.2241 -PG_FUNCTION_INFO_V1(pgl_ecircle_distance);
  9.2242 -Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) {
  9.2243 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  9.2244 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  9.2245 -  double distance = pgl_distance(
  9.2246 -    circle1->center.lat, circle1->center.lon,
  9.2247 -    circle2->center.lat, circle2->center.lon
  9.2248 -  ) - (circle1->radius + circle2->radius);
  9.2249 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  9.2250 -}
  9.2251 -
  9.2252 -/* calculate circle to cluster distance ("<->" operator) in SQL */
  9.2253 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance);
  9.2254 -Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) {
  9.2255 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  9.2256 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2257 -  double distance = (
  9.2258 -    pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius
  9.2259 -  );
  9.2260 -  PG_FREE_IF_COPY(cluster, 1);
  9.2261 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  9.2262 -}
  9.2263 -
  9.2264 -
  9.2265 -/*-----------------------------------------------------------*
  9.2266 - *  B-tree comparison operators and index support functions  *
  9.2267 - *-----------------------------------------------------------*/
  9.2268 -
  9.2269 -/* macro for a B-tree operator (without detoasting) */
  9.2270 -#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \
  9.2271 -  PG_FUNCTION_INFO_V1(func); \
  9.2272 -  Datum func(PG_FUNCTION_ARGS) { \
  9.2273 -    type *a = (type *)PG_GETARG_POINTER(0); \
  9.2274 -    type *b = (type *)PG_GETARG_POINTER(1); \
  9.2275 -    PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \
  9.2276 -  }
  9.2277 -
  9.2278 -/* macro for a B-tree comparison function (without detoasting) */
  9.2279 -#define PGL_BTREE_CMP(func, type, cmpfunc) \
  9.2280 -  PG_FUNCTION_INFO_V1(func); \
  9.2281 -  Datum func(PG_FUNCTION_ARGS) { \
  9.2282 -    type *a = (type *)PG_GETARG_POINTER(0); \
  9.2283 -    type *b = (type *)PG_GETARG_POINTER(1); \
  9.2284 -    PG_RETURN_INT32(cmpfunc(a, b)); \
  9.2285 -  }
  9.2286 -
  9.2287 -/* macro for a B-tree operator (with detoasting) */
  9.2288 -#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \
  9.2289 -  PG_FUNCTION_INFO_V1(func); \
  9.2290 -  Datum func(PG_FUNCTION_ARGS) { \
  9.2291 -    bool res; \
  9.2292 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  9.2293 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  9.2294 -    res = cmpfunc(a, b) oper 0; \
  9.2295 -    PG_FREE_IF_COPY(a, 0); \
  9.2296 -    PG_FREE_IF_COPY(b, 1); \
  9.2297 -    PG_RETURN_BOOL(res); \
  9.2298 -  }
  9.2299 -
  9.2300 -/* macro for a B-tree comparison function (with detoasting) */
  9.2301 -#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \
  9.2302 -  PG_FUNCTION_INFO_V1(func); \
  9.2303 -  Datum func(PG_FUNCTION_ARGS) { \
  9.2304 -    int32_t res; \
  9.2305 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  9.2306 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  9.2307 -    res = cmpfunc(a, b); \
  9.2308 -    PG_FREE_IF_COPY(a, 0); \
  9.2309 -    PG_FREE_IF_COPY(b, 1); \
  9.2310 -    PG_RETURN_INT32(res); \
  9.2311 -  }
  9.2312 -
  9.2313 -/* B-tree operators and comparison function for point */
  9.2314 -PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <)
  9.2315 -PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=)
  9.2316 -PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==)
  9.2317 -PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=)
  9.2318 -PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=)
  9.2319 -PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >)
  9.2320 -PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp)
  9.2321 -
  9.2322 -/* B-tree operators and comparison function for box */
  9.2323 -PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <)
  9.2324 -PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=)
  9.2325 -PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==)
  9.2326 -PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=)
  9.2327 -PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=)
  9.2328 -PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >)
  9.2329 -PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp)
  9.2330 -
  9.2331 -/* B-tree operators and comparison function for circle */
  9.2332 -PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <)
  9.2333 -PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=)
  9.2334 -PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==)
  9.2335 -PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=)
  9.2336 -PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=)
  9.2337 -PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >)
  9.2338 -PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp)
  9.2339 -
  9.2340 -
  9.2341 -/*--------------------------------*
  9.2342 - *  GiST index support functions  *
  9.2343 - *--------------------------------*/
  9.2344 -
  9.2345 -/* GiST "consistent" support function */
  9.2346 -PG_FUNCTION_INFO_V1(pgl_gist_consistent);
  9.2347 -Datum pgl_gist_consistent(PG_FUNCTION_ARGS) {
  9.2348 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  9.2349 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  9.2350 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  9.2351 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  9.2352 -  /* demand recheck because index and query methods are lossy */
  9.2353 -  *recheck = true;
  9.2354 -  /* strategy number aliases for different operators using the same strategy */
  9.2355 -  strategy %= 100;
  9.2356 -  /* strategy number 11: equality of two points */
  9.2357 -  if (strategy == 11) {
  9.2358 -    /* query datum is another point */
  9.2359 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  9.2360 -    /* convert other point to key */
  9.2361 -    pgl_pointkey querykey;
  9.2362 -    pgl_point_to_key(query, querykey);
  9.2363 -    /* return true if both keys overlap */
  9.2364 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  9.2365 -  }
  9.2366 -  /* strategy number 13: equality of two circles */
  9.2367 -  if (strategy == 13) {
  9.2368 -    /* query datum is another circle */
  9.2369 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  9.2370 -    /* convert other circle to key */
  9.2371 -    pgl_areakey querykey;
  9.2372 -    pgl_circle_to_key(query, querykey);
  9.2373 -    /* return true if both keys overlap */
  9.2374 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  9.2375 -  }
  9.2376 -  /* for all remaining strategies, keys on empty objects produce no match */
  9.2377 -  /* (check necessary because query radius may be infinite) */
  9.2378 -  if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false);
  9.2379 -  /* strategy number 21: overlapping with point */
  9.2380 -  if (strategy == 21) {
  9.2381 -    /* query datum is a point */
  9.2382 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  9.2383 -    /* return true if estimated distance (allowed to be smaller than real
  9.2384 -       distance) between index key and point is zero */
  9.2385 -    PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0);
  9.2386 -  }
  9.2387 -  /* strategy number 22: (point) overlapping with box */
  9.2388 -  if (strategy == 22) {
  9.2389 -    /* query datum is a box */
  9.2390 -    pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1);
  9.2391 -    /* determine bounding box of indexed key */
  9.2392 -    pgl_box keybox;
  9.2393 -    pgl_key_to_box(key, &keybox);
  9.2394 -    /* return true if query box overlaps with bounding box of indexed key */
  9.2395 -    PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox));
  9.2396 -  }
  9.2397 -  /* strategy number 23: overlapping with circle */
  9.2398 -  if (strategy == 23) {
  9.2399 -    /* query datum is a circle */
  9.2400 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  9.2401 -    /* return true if estimated distance (allowed to be smaller than real
  9.2402 -       distance) between index key and circle center is smaller than radius */
  9.2403 -    PG_RETURN_BOOL(
  9.2404 -      pgl_estimate_key_distance(key, &(query->center)) <= query->radius
  9.2405 -    );
  9.2406 -  }
  9.2407 -  /* strategy number 24: overlapping with cluster */
  9.2408 -  if (strategy == 24) {
  9.2409 -    bool retval;  /* return value */
  9.2410 -    /* query datum is a cluster */
  9.2411 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2412 -    /* return true if estimated distance (allowed to be smaller than real
  9.2413 -       distance) between index key and circle center is smaller than radius */
  9.2414 -    retval = (
  9.2415 -      pgl_estimate_key_distance(key, &(query->bounding.center)) <=
  9.2416 -      query->bounding.radius
  9.2417 -    );
  9.2418 -    PG_FREE_IF_COPY(query, 1);  /* free detoasted cluster (if copy) */
  9.2419 -    PG_RETURN_BOOL(retval);
  9.2420 -  }
  9.2421 -  /* throw error for any unknown strategy number */
  9.2422 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  9.2423 -}
  9.2424 -
  9.2425 -/* GiST "union" support function */
  9.2426 -PG_FUNCTION_INFO_V1(pgl_gist_union);
  9.2427 -Datum pgl_gist_union(PG_FUNCTION_ARGS) {
  9.2428 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  9.2429 -  pgl_keyptr out;  /* return value (to be palloc'ed) */
  9.2430 -  int i;
  9.2431 -  /* determine key size */
  9.2432 -  size_t keysize = PGL_KEY_IS_AREAKEY(
  9.2433 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key)
  9.2434 -  ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey);
  9.2435 -  /* begin with first key as result */
  9.2436 -  out = palloc(keysize);
  9.2437 -  memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize);
  9.2438 -  /* unite current result with second, third, etc. key */
  9.2439 -  for (i=1; i<entryvec->n; i++) {
  9.2440 -    pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key));
  9.2441 -  }
  9.2442 -  /* return result */
  9.2443 -  PG_RETURN_POINTER(out);
  9.2444 -}
  9.2445 -
  9.2446 -/* GiST "compress" support function for indicis on points */
  9.2447 -PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint);
  9.2448 -Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) {
  9.2449 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  9.2450 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  9.2451 -  /* only transform new leaves */
  9.2452 -  if (entry->leafkey) {
  9.2453 -    /* get point to be transformed */
  9.2454 -    pgl_point *point = (pgl_point *)DatumGetPointer(entry->key);
  9.2455 -    /* allocate memory for key */
  9.2456 -    pgl_keyptr key = palloc(sizeof(pgl_pointkey));
  9.2457 -    /* transform point to key */
  9.2458 -    pgl_point_to_key(point, key);
  9.2459 -    /* create new GISTENTRY structure as return value */
  9.2460 -    retval = palloc(sizeof(GISTENTRY));
  9.2461 -    gistentryinit(
  9.2462 -      *retval, PointerGetDatum(key),
  9.2463 -      entry->rel, entry->page, entry->offset, FALSE
  9.2464 -    );
  9.2465 -  } else {
  9.2466 -    /* inner nodes have already been transformed */
  9.2467 -    retval = entry;
  9.2468 -  }
  9.2469 -  /* return pointer to old or new GISTENTRY structure */
  9.2470 -  PG_RETURN_POINTER(retval);
  9.2471 -}
  9.2472 -
  9.2473 -/* GiST "compress" support function for indicis on circles */
  9.2474 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle);
  9.2475 -Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) {
  9.2476 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  9.2477 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  9.2478 -  /* only transform new leaves */
  9.2479 -  if (entry->leafkey) {
  9.2480 -    /* get circle to be transformed */
  9.2481 -    pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key);
  9.2482 -    /* allocate memory for key */
  9.2483 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  9.2484 -    /* transform circle to key */
  9.2485 -    pgl_circle_to_key(circle, key);
  9.2486 -    /* create new GISTENTRY structure as return value */
  9.2487 -    retval = palloc(sizeof(GISTENTRY));
  9.2488 -    gistentryinit(
  9.2489 -      *retval, PointerGetDatum(key),
  9.2490 -      entry->rel, entry->page, entry->offset, FALSE
  9.2491 -    );
  9.2492 -  } else {
  9.2493 -    /* inner nodes have already been transformed */
  9.2494 -    retval = entry;
  9.2495 -  }
  9.2496 -  /* return pointer to old or new GISTENTRY structure */
  9.2497 -  PG_RETURN_POINTER(retval);
  9.2498 -}
  9.2499 -
  9.2500 -/* GiST "compress" support function for indices on clusters */
  9.2501 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster);
  9.2502 -Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) {
  9.2503 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  9.2504 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  9.2505 -  /* only transform new leaves */
  9.2506 -  if (entry->leafkey) {
  9.2507 -    /* get cluster to be transformed (detoasting necessary!) */
  9.2508 -    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key);
  9.2509 -    /* allocate memory for key */
  9.2510 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  9.2511 -    /* transform cluster to key */
  9.2512 -    pgl_circle_to_key(&(cluster->bounding), key);
  9.2513 -    /* create new GISTENTRY structure as return value */
  9.2514 -    retval = palloc(sizeof(GISTENTRY));
  9.2515 -    gistentryinit(
  9.2516 -      *retval, PointerGetDatum(key),
  9.2517 -      entry->rel, entry->page, entry->offset, FALSE
  9.2518 -    );
  9.2519 -    /* free detoasted datum */
  9.2520 -    if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster);
  9.2521 -  } else {
  9.2522 -    /* inner nodes have already been transformed */
  9.2523 -    retval = entry;
  9.2524 -  }
  9.2525 -  /* return pointer to old or new GISTENTRY structure */
  9.2526 -  PG_RETURN_POINTER(retval);
  9.2527 -}
  9.2528 -
  9.2529 -/* GiST "decompress" support function for indices */
  9.2530 -PG_FUNCTION_INFO_V1(pgl_gist_decompress);
  9.2531 -Datum pgl_gist_decompress(PG_FUNCTION_ARGS) {
  9.2532 -  /* return passed pointer without transformation */
  9.2533 -  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
  9.2534 -}
  9.2535 -
  9.2536 -/* GiST "penalty" support function */
  9.2537 -PG_FUNCTION_INFO_V1(pgl_gist_penalty);
  9.2538 -Datum pgl_gist_penalty(PG_FUNCTION_ARGS) {
  9.2539 -  GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0);
  9.2540 -  GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1);
  9.2541 -  float *penalty = (float *)PG_GETARG_POINTER(2);
  9.2542 -  /* get original key and key to insert */
  9.2543 -  pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key);
  9.2544 -  pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key);
  9.2545 -  /* copy original key */
  9.2546 -  union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key;
  9.2547 -  if (PGL_KEY_IS_AREAKEY(orig)) {
  9.2548 -    memcpy(union_key.areakey, orig, sizeof(union_key.areakey));
  9.2549 -  } else {
  9.2550 -    memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey));
  9.2551 -  }
  9.2552 -  /* calculate union of both keys */
  9.2553 -  pgl_unite_keys((pgl_keyptr)&union_key, new);
  9.2554 -  /* penalty equal to reduction of key length (logarithm of added area) */
  9.2555 -  /* (return value by setting referenced value and returning pointer) */
  9.2556 -  *penalty = (
  9.2557 -    PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key)
  9.2558 -  );
  9.2559 -  PG_RETURN_POINTER(penalty);
  9.2560 -}
  9.2561 -
  9.2562 -/* GiST "picksplit" support function */
  9.2563 -PG_FUNCTION_INFO_V1(pgl_gist_picksplit);
  9.2564 -Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) {
  9.2565 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  9.2566 -  GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1);
  9.2567 -  OffsetNumber i;  /* between FirstOffsetNumber and entryvec->n (inclusive) */
  9.2568 -  union {
  9.2569 -    pgl_pointkey pointkey;
  9.2570 -    pgl_areakey areakey;
  9.2571 -  } union_all;  /* union of all keys (to be calculated from scratch)
  9.2572 -                   (later cut in half) */
  9.2573 -  int is_areakey = PGL_KEY_IS_AREAKEY(
  9.2574 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key)
  9.2575 -  );
  9.2576 -  int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey);
  9.2577 -  pgl_keyptr unionL = palloc(keysize);  /* union of keys that go left */
  9.2578 -  pgl_keyptr unionR = palloc(keysize);  /* union of keys that go right */
  9.2579 -  pgl_keyptr key;  /* current key to be processed */
  9.2580 -  /* allocate memory for array of left and right keys, set counts to zero */
  9.2581 -  v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  9.2582 -  v->spl_nleft = 0;
  9.2583 -  v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  9.2584 -  v->spl_nright = 0;
  9.2585 -  /* calculate union of all keys from scratch */
  9.2586 -  memcpy(
  9.2587 -    (pgl_keyptr)&union_all,
  9.2588 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key),
  9.2589 -    keysize
  9.2590 -  );
  9.2591 -  for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) {
  9.2592 -    pgl_unite_keys(
  9.2593 -      (pgl_keyptr)&union_all,
  9.2594 -      (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)
  9.2595 -    );
  9.2596 -  }
  9.2597 -  /* check if trivial split is necessary due to exhausted key length */
  9.2598 -  /* (Note: keys for empty objects must have node depth set to maximum) */
  9.2599 -  if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == (
  9.2600 -    is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH
  9.2601 -  )) {
  9.2602 -    /* half of all keys go left */
  9.2603 -    for (
  9.2604 -      i=FirstOffsetNumber;
  9.2605 -      i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  9.2606 -      i=OffsetNumberNext(i)
  9.2607 -    ) {
  9.2608 -      /* pointer to current key */
  9.2609 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  9.2610 -      /* update unionL */
  9.2611 -      /* check if key is first key that goes left */
  9.2612 -      if (!v->spl_nleft) {
  9.2613 -        /* first key that goes left is just copied to unionL */
  9.2614 -        memcpy(unionL, key, keysize);
  9.2615 -      } else {
  9.2616 -        /* unite current value and next key */
  9.2617 -        pgl_unite_keys(unionL, key);
  9.2618 -      }
  9.2619 -      /* append offset number to list of keys that go left */
  9.2620 -      v->spl_left[v->spl_nleft++] = i;
  9.2621 -    }
  9.2622 -    /* other half goes right */
  9.2623 -    for (
  9.2624 -      i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  9.2625 -      i<entryvec->n;
  9.2626 -      i=OffsetNumberNext(i)
  9.2627 -    ) {
  9.2628 -      /* pointer to current key */
  9.2629 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  9.2630 -      /* update unionR */
  9.2631 -      /* check if key is first key that goes right */
  9.2632 -      if (!v->spl_nright) {
  9.2633 -        /* first key that goes right is just copied to unionR */
  9.2634 -        memcpy(unionR, key, keysize);
  9.2635 -      } else {
  9.2636 -        /* unite current value and next key */
  9.2637 -        pgl_unite_keys(unionR, key);
  9.2638 -      }
  9.2639 -      /* append offset number to list of keys that go right */
  9.2640 -      v->spl_right[v->spl_nright++] = i;
  9.2641 -    }
  9.2642 -  }
  9.2643 -  /* otherwise, a non-trivial split is possible */
  9.2644 -  else {
  9.2645 -    /* cut covered area in half */
  9.2646 -    /* (union_all then refers to area of keys that go left) */
  9.2647 -    /* check if union of all keys covers empty and non-empty objects */
  9.2648 -    if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) {
  9.2649 -      /* if yes, split into empty and non-empty objects */
  9.2650 -      pgl_key_set_empty((pgl_keyptr)&union_all);
  9.2651 -    } else {
  9.2652 -      /* otherwise split by next bit */
  9.2653 -      ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++;
  9.2654 -      /* NOTE: type bit conserved */
  9.2655 -    }
  9.2656 -    /* determine for each key if it goes left or right */
  9.2657 -    for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) {
  9.2658 -      /* pointer to current key */
  9.2659 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  9.2660 -      /* keys within one half of the area go left */
  9.2661 -      if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) {
  9.2662 -        /* update unionL */
  9.2663 -        /* check if key is first key that goes left */
  9.2664 -        if (!v->spl_nleft) {
  9.2665 -          /* first key that goes left is just copied to unionL */
  9.2666 -          memcpy(unionL, key, keysize);
  9.2667 -        } else {
  9.2668 -          /* unite current value of unionL and processed key */
  9.2669 -          pgl_unite_keys(unionL, key);
  9.2670 -        }
  9.2671 -        /* append offset number to list of keys that go left */
  9.2672 -        v->spl_left[v->spl_nleft++] = i;
  9.2673 -      }
  9.2674 -      /* the other keys go right */
  9.2675 -      else {
  9.2676 -        /* update unionR */
  9.2677 -        /* check if key is first key that goes right */
  9.2678 -        if (!v->spl_nright) {
  9.2679 -          /* first key that goes right is just copied to unionR */
  9.2680 -          memcpy(unionR, key, keysize);
  9.2681 -        } else {
  9.2682 -          /* unite current value of unionR and processed key */
  9.2683 -          pgl_unite_keys(unionR, key);
  9.2684 -        }
  9.2685 -        /* append offset number to list of keys that go right */
  9.2686 -        v->spl_right[v->spl_nright++] = i;
  9.2687 -      }
  9.2688 -    }
  9.2689 -  }
  9.2690 -  /* store unions in return value */
  9.2691 -  v->spl_ldatum = PointerGetDatum(unionL);
  9.2692 -  v->spl_rdatum = PointerGetDatum(unionR);
  9.2693 -  /* return all results */
  9.2694 -  PG_RETURN_POINTER(v);
  9.2695 -}
  9.2696 -
  9.2697 -/* GiST "same"/"equal" support function */
  9.2698 -PG_FUNCTION_INFO_V1(pgl_gist_same);
  9.2699 -Datum pgl_gist_same(PG_FUNCTION_ARGS) {
  9.2700 -  pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0);
  9.2701 -  pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1);
  9.2702 -  bool *boolptr = (bool *)PG_GETARG_POINTER(2);
  9.2703 -  /* two keys are equal if they are binary equal */
  9.2704 -  /* (return result by setting referenced boolean and returning pointer) */
  9.2705 -  *boolptr = !memcmp(
  9.2706 -    key1,
  9.2707 -    key2,
  9.2708 -    PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey)
  9.2709 -  );
  9.2710 -  PG_RETURN_POINTER(boolptr);
  9.2711 -}
  9.2712 -
  9.2713 -/* GiST "distance" support function */
  9.2714 -PG_FUNCTION_INFO_V1(pgl_gist_distance);
  9.2715 -Datum pgl_gist_distance(PG_FUNCTION_ARGS) {
  9.2716 -  GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
  9.2717 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  9.2718 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  9.2719 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  9.2720 -  double distance;  /* return value */
  9.2721 -  /* demand recheck because distance is just an estimation */
  9.2722 -  /* (real distance may be bigger) */
  9.2723 -  *recheck = true;
  9.2724 -  /* strategy number aliases for different operators using the same strategy */
  9.2725 -  strategy %= 100;
  9.2726 -  /* strategy number 31: distance to point */
  9.2727 -  if (strategy == 31) {
  9.2728 -    /* query datum is a point */
  9.2729 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  9.2730 -    /* use pgl_estimate_pointkey_distance() function to compute result */
  9.2731 -    distance = pgl_estimate_key_distance(key, query);
  9.2732 -    /* avoid infinity (reserved!) */
  9.2733 -    if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  9.2734 -    /* return result */
  9.2735 -    PG_RETURN_FLOAT8(distance);
  9.2736 -  }
  9.2737 -  /* strategy number 33: distance to circle */
  9.2738 -  if (strategy == 33) {
  9.2739 -    /* query datum is a circle */
  9.2740 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  9.2741 -    /* estimate distance to circle center and substract circle radius */
  9.2742 -    distance = (
  9.2743 -      pgl_estimate_key_distance(key, &(query->center)) - query->radius
  9.2744 -    );
  9.2745 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  9.2746 -    if (distance <= 0) distance = 0;
  9.2747 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  9.2748 -    /* return result */
  9.2749 -    PG_RETURN_FLOAT8(distance);
  9.2750 -  }
  9.2751 -  /* strategy number 34: distance to cluster */
  9.2752 -  if (strategy == 34) {
  9.2753 -    /* query datum is a cluster */
  9.2754 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  9.2755 -    /* estimate distance to bounding center and substract bounding radius */
  9.2756 -    distance = (
  9.2757 -      pgl_estimate_key_distance(key, &(query->bounding.center)) -
  9.2758 -      query->bounding.radius
  9.2759 -    );
  9.2760 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  9.2761 -    if (distance <= 0) distance = 0;
  9.2762 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  9.2763 -    /* free detoasted cluster (if copy) */
  9.2764 -    PG_FREE_IF_COPY(query, 1);
  9.2765 -    /* return result */
  9.2766 -    PG_RETURN_FLOAT8(distance);
  9.2767 -  }
  9.2768 -  /* throw error for any unknown strategy number */
  9.2769 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  9.2770 -}
  9.2771 -
    10.1 --- a/latlon-v0004.c	Sat Sep 03 16:00:22 2016 +0200
    10.2 +++ b/latlon-v0004.c	Fri Sep 09 19:22:30 2016 +0200
    10.3 @@ -502,14 +502,6 @@
    10.4    double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
    10.5    double lon;            /* longitude of intersection */
    10.6    int counter = 0;       /* counter for intersections east of point */
    10.7 -  /* points outside bounding circle are always assumed to be non-overlapping */
    10.8 -  /* (necessary for consistent table and index scans) */
    10.9 -  if (
   10.10 -    pgl_distance(
   10.11 -      point->lat, point->lon,
   10.12 -      cluster->bounding.center.lat, cluster->bounding.center.lon
   10.13 -    ) > cluster->bounding.radius
   10.14 -  ) return false;
   10.15    /* iterate over all entries */
   10.16    for (i=0; i<cluster->nentries; i++) {
   10.17      /* get properties of entry */
   10.18 @@ -539,14 +531,22 @@
   10.19          entrytype != PGL_ENTRY_OUTLINE &&
   10.20          entrytype != PGL_ENTRY_POLYGON
   10.21        ) continue;
   10.22 -      /* get latitude and longitude values of edge */
   10.23 -      lat1 = points[j].lat;
   10.24 +      /* use previously calculated values for lat1 and lon1 if possible */
   10.25 +      if (j) {
   10.26 +        lat1 = lat2;
   10.27 +        lon1 = lon2;
   10.28 +      } else {
   10.29 +        /* otherwise get latitude and longitude values of first vertex */
   10.30 +        lat1 = points[0].lat;
   10.31 +        lon1 = points[0].lon;
   10.32 +        /* and consider longitude wrap-around for first vertex */
   10.33 +        if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   10.34 +        else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   10.35 +      }
   10.36 +      /* get latitude and longitude of next vertex */
   10.37        lat2 = points[k].lat;
   10.38 -      lon1 = points[j].lon;
   10.39        lon2 = points[k].lon;
   10.40 -      /* consider longitude wrap-around for edge */
   10.41 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   10.42 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   10.43 +      /* consider longitude wrap-around for next vertex */
   10.44        if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
   10.45        else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
   10.46        /* return true if point is on horizontal (west to east) edge of polygon */
   10.47 @@ -569,6 +569,231 @@
   10.48    return counter & 1;
   10.49  }
   10.50  
   10.51 +/* check if all points of the second cluster are inside the first cluster */
   10.52 +static inline bool pgl_all_cluster_points_in_cluster(
   10.53 +  pgl_cluster *outer, pgl_cluster *inner
   10.54 +) {
   10.55 +  int i, j;           /* i: entry, j: point in entry */
   10.56 +  int npoints;        /* number of points in entry */
   10.57 +  pgl_point *points;  /* array of points in entry */
   10.58 +  /* iterate over all entries of "inner" cluster */
   10.59 +  for (i=0; i<inner->nentries; i++) {
   10.60 +    /* get properties of entry */
   10.61 +    npoints = inner->entries[i].npoints;
   10.62 +    points = PGL_ENTRY_POINTS(inner, i);
   10.63 +    /* iterate over all points in entry of "inner" cluster */
   10.64 +    for (j=0; j<npoints; j++) {
   10.65 +      /* return false if one point of inner cluster is not in outer cluster */
   10.66 +      if (!pgl_point_in_cluster(points+j, outer)) return false;
   10.67 +    }
   10.68 +  }
   10.69 +  /* otherwise return true */
   10.70 +  return true;
   10.71 +}
   10.72 +
   10.73 +/* check if any point the second cluster is inside the first cluster */
   10.74 +static inline bool pgl_any_cluster_points_in_cluster(
   10.75 +  pgl_cluster *outer, pgl_cluster *inner
   10.76 +) {
   10.77 +  int i, j;           /* i: entry, j: point in entry */
   10.78 +  int npoints;        /* number of points in entry */
   10.79 +  pgl_point *points;  /* array of points in entry */
   10.80 +  /* iterate over all entries of "inner" cluster */
   10.81 +  for (i=0; i<inner->nentries; i++) {
   10.82 +    /* get properties of entry */
   10.83 +    npoints = inner->entries[i].npoints;
   10.84 +    points = PGL_ENTRY_POINTS(inner, i);
   10.85 +    /* iterate over all points in entry of "inner" cluster */
   10.86 +    for (j=0; j<npoints; j++) {
   10.87 +      /* return true if one point of inner cluster is in outer cluster */
   10.88 +      if (pgl_point_in_cluster(points+j, outer)) return true;
   10.89 +    }
   10.90 +  }
   10.91 +  /* otherwise return false */
   10.92 +  return false;
   10.93 +}
   10.94 +
   10.95 +/* check if line segment crosses line */
   10.96 +/* returns -1 if yes, 1 if no, and 0 in corner cases */
   10.97 +/* NOTE: each line (segment) must have a length greater than zero */
   10.98 +static inline double pgl_lseg_crosses_line(
   10.99 +  double seg_x1,  double seg_y1,  double seg_x2,  double seg_y2,
  10.100 +  double line_x1, double line_y1, double line_x2, double line_y2,
  10.101 +  bool strict
  10.102 +) {
  10.103 +  double value = (
  10.104 +    (seg_x1-line_x1) * (line_y2-line_y1) -
  10.105 +    (seg_y1-line_y1) * (line_x2-line_x1)
  10.106 +  ) * (
  10.107 +    (seg_x2-line_x1) * (line_y2-line_y1) -
  10.108 +    (seg_y2-line_y1) * (line_x2-line_x1)
  10.109 +  );
  10.110 +  if (strict) return value < 0;
  10.111 +  else return value <= 0;
  10.112 +}
  10.113 +
  10.114 +/* check if paths and outlines of two clusters overlap */
  10.115 +/* (set strict to true to disregard corner cases) */
  10.116 +static bool pgl_outlines_overlap(
  10.117 +  pgl_cluster *cluster1, pgl_cluster *cluster2, bool strict
  10.118 +) {
  10.119 +  int i1, j1, k1;  /* i: entry, j: point in entry, k: next point in entry */
  10.120 +  int i2, j2, k2;
  10.121 +  int entrytype1, entrytype2;     /* type of entry */
  10.122 +  int npoints1, npoints2;         /* number of points in entry */
  10.123 +  pgl_point *points1;             /* array of points in entry of cluster1 */
  10.124 +  pgl_point *points2;             /* array of points in entry of cluster2 */
  10.125 +  int lon_dir1, lon_dir2;         /* first vertex west (-1) or east (+1) */
  10.126 +  double lon_break1, lon_break2;  /* antipodal longitude of first vertex */
  10.127 +  double lat11, lon11;  /* latitude and (adjusted) longitude of vertex */
  10.128 +  double lat12, lon12;  /* latitude and (adjusted) longitude of next vertex */
  10.129 +  double lat21, lon21;  /* latitude and (adjusted) longitudes for cluster2 */
  10.130 +  double lat22, lon22;
  10.131 +  double wrapvalue;     /* temporary helper value to adjust wrap-around */  
  10.132 +  /* iterate over all entries of cluster1 */
  10.133 +  for (i1=0; i1<cluster1->nentries; i1++) {
  10.134 +    /* get properties of entry in cluster1 and skip points */
  10.135 +    npoints1 = cluster1->entries[i1].npoints;
  10.136 +    if (npoints1 < 2) continue;
  10.137 +    entrytype1 = cluster1->entries[i1].entrytype;
  10.138 +    points1 = PGL_ENTRY_POINTS(cluster1, i1);
  10.139 +    /* determine east/west orientation of first point and calculate antipodal
  10.140 +       longitude */
  10.141 +    lon_break1 = points1[0].lon;
  10.142 +    if (lon_break1 < 0) {
  10.143 +      lon_dir1   = -1;
  10.144 +      lon_break1 = pgl_round(lon_break1 + 180);
  10.145 +    } else if (lon_break1 > 0) {
  10.146 +      lon_dir1   = 1;
  10.147 +      lon_break1 = pgl_round(lon_break1 - 180);
  10.148 +    } else lon_dir1 = 0;
  10.149 +    /* iterate over all edges and vertices in cluster1 */
  10.150 +    for (j1=0; j1<npoints1; j1++) {
  10.151 +      /* calculate index of next vertex */
  10.152 +      k1 = (j1+1) % npoints1;
  10.153 +      /* skip last edge unless entry is (closed) outline or polygon */
  10.154 +      if (
  10.155 +        k1 == 0 &&
  10.156 +        entrytype1 != PGL_ENTRY_OUTLINE &&
  10.157 +        entrytype1 != PGL_ENTRY_POLYGON
  10.158 +      ) continue;
  10.159 +      /* use previously calculated values for lat1 and lon1 if possible */
  10.160 +      if (j1) {
  10.161 +        lat11 = lat12;
  10.162 +        lon11 = lon12;
  10.163 +      } else {
  10.164 +        /* otherwise get latitude and longitude values of first vertex */
  10.165 +        lat11 = points1[0].lat;
  10.166 +        lon11 = points1[0].lon;
  10.167 +        /* and consider longitude wrap-around for first vertex */
  10.168 +        if      (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360);
  10.169 +        else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360);
  10.170 +      }
  10.171 +      /* get latitude and longitude of next vertex */
  10.172 +      lat12 = points1[k1].lat;
  10.173 +      lon12 = points1[k1].lon;
  10.174 +      /* consider longitude wrap-around for next vertex */
  10.175 +      if      (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360);
  10.176 +      else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360);
  10.177 +      /* skip degenerated edges */
  10.178 +      if (lat11 == lat12 && lon11 == lon12) continue;
  10.179 +      /* iterate over all entries of cluster2 */
  10.180 +      for (i2=0; i2<cluster2->nentries; i2++) {
  10.181 +        /* get points and number of points of entry in cluster2 */
  10.182 +        npoints2 = cluster2->entries[i2].npoints;
  10.183 +        if (npoints2 < 2) continue;
  10.184 +        entrytype2 = cluster2->entries[i2].entrytype;
  10.185 +        points2 = PGL_ENTRY_POINTS(cluster2, i2);
  10.186 +        /* determine east/west orientation of first point and calculate antipodal
  10.187 +           longitude */
  10.188 +        lon_break2 = points2[0].lon;
  10.189 +        if (lon_break2 < 0) {
  10.190 +          lon_dir2   = -1;
  10.191 +          lon_break2 = pgl_round(lon_break2 + 180);
  10.192 +        } else if (lon_break2 > 0) {
  10.193 +          lon_dir2   = 1;
  10.194 +          lon_break2 = pgl_round(lon_break2 - 180);
  10.195 +        } else lon_dir2 = 0;
  10.196 +        /* iterate over all edges and vertices in cluster2 */
  10.197 +        for (j2=0; j2<npoints2; j2++) {
  10.198 +          /* calculate index of next vertex */
  10.199 +          k2 = (j2+1) % npoints2;
  10.200 +          /* skip last edge unless entry is (closed) outline or polygon */
  10.201 +          if (
  10.202 +            k2 == 0 &&
  10.203 +            entrytype2 != PGL_ENTRY_OUTLINE &&
  10.204 +            entrytype2 != PGL_ENTRY_POLYGON
  10.205 +          ) continue;
  10.206 +          /* use previously calculated values for lat1 and lon1 if possible */
  10.207 +          if (j2) {
  10.208 +            lat21 = lat22;
  10.209 +            lon21 = lon22;
  10.210 +          } else {
  10.211 +            /* otherwise get latitude and longitude values of first vertex */
  10.212 +            lat21 = points2[0].lat;
  10.213 +            lon21 = points2[0].lon;
  10.214 +            /* and consider longitude wrap-around for first vertex */
  10.215 +            if      (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360);
  10.216 +            else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360);
  10.217 +          }
  10.218 +          /* get latitude and longitude of next vertex */
  10.219 +          lat22 = points2[k2].lat;
  10.220 +          lon22 = points2[k2].lon;
  10.221 +          /* consider longitude wrap-around for next vertex */
  10.222 +          if      (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360);
  10.223 +          else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360);
  10.224 +          /* skip degenerated edges */
  10.225 +          if (lat21 == lat22 && lon21 == lon22) continue;
  10.226 +          /* perform another wrap-around where necessary */
  10.227 +          /* TODO: improve performance of whole wrap-around mechanism */
  10.228 +          wrapvalue = (lon21 + lon22) - (lon11 + lon12);
  10.229 +          if (wrapvalue > 360) {
  10.230 +            lon21 = pgl_round(lon21 - 360);
  10.231 +            lon22 = pgl_round(lon22 - 360);
  10.232 +          } else if (wrapvalue < -360) {
  10.233 +            lon21 = pgl_round(lon21 + 360);
  10.234 +            lon22 = pgl_round(lon22 + 360);
  10.235 +          }
  10.236 +          /* return true if segments overlap */
  10.237 +          if (
  10.238 +            pgl_lseg_crosses_line(
  10.239 +              lat11, lon11, lat12, lon12,
  10.240 +              lat21, lon21, lat22, lon22,
  10.241 +              strict
  10.242 +            ) && pgl_lseg_crosses_line(
  10.243 +              lat21, lon21, lat22, lon22,
  10.244 +              lat11, lon11, lat12, lon12,
  10.245 +              strict
  10.246 +            )
  10.247 +          ) {
  10.248 +            return true;
  10.249 +          }
  10.250 +        }
  10.251 +      }
  10.252 +    }
  10.253 +  }
  10.254 +  /* otherwise return false */
  10.255 +  return false;
  10.256 +}
  10.257 +
  10.258 +/* check if second cluster is completely contained in first cluster */
  10.259 +static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) {
  10.260 +  if (!pgl_all_cluster_points_in_cluster(outer, inner)) return false;
  10.261 +  if (pgl_outlines_overlap(outer, inner, true)) return false;
  10.262 +  return true;
  10.263 +}
  10.264 +
  10.265 +/* check if two clusters overlap */
  10.266 +static bool pgl_clusters_overlap(
  10.267 +  pgl_cluster *cluster1, pgl_cluster *cluster2
  10.268 +) {
  10.269 +  if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true;
  10.270 +  if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true;
  10.271 +  if (pgl_outlines_overlap(cluster1, cluster2, false)) return true;
  10.272 +  return false;
  10.273 +}
  10.274 +
  10.275 +
  10.276  /* calculate (approximate) distance between point and cluster */
  10.277  static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
  10.278    int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
  10.279 @@ -622,12 +847,18 @@
  10.280      else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
  10.281      /* iterate over all edges and vertices */
  10.282      for (j=0; j<npoints; j++) {
  10.283 -      /* get latitude and longitude values of current point */
  10.284 -      lat1 = points[j].lat;
  10.285 -      lon1 = points[j].lon;
  10.286 -      /* consider longitude wrap-around for current point */
  10.287 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
  10.288 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
  10.289 +      /* use previously calculated values for lat1 and lon1 if possible */
  10.290 +      if (j) {
  10.291 +        lat1 = lat2;
  10.292 +        lon1 = lon2;
  10.293 +      } else {
  10.294 +        /* otherwise get latitude and longitude values of first vertex */
  10.295 +        lat1 = points[0].lat;
  10.296 +        lon1 = points[0].lon;
  10.297 +        /* and consider longitude wrap-around for first vertex */
  10.298 +        if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
  10.299 +        else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
  10.300 +      }
  10.301        /* calculate distance to vertex */
  10.302        dist = pgl_distance(lat0, lon0, lat1, lon1);
  10.303        /* store calculated distance if smallest */
  10.304 @@ -640,10 +871,10 @@
  10.305          entrytype != PGL_ENTRY_OUTLINE &&
  10.306          entrytype != PGL_ENTRY_POLYGON
  10.307        ) continue;
  10.308 -      /* get latitude and longitude values of next point */
  10.309 +      /* get latitude and longitude of next vertex */
  10.310        lat2 = points[k].lat;
  10.311        lon2 = points[k].lon;
  10.312 -      /* consider longitude wrap-around for next point */
  10.313 +      /* consider longitude wrap-around for next vertex */
  10.314        if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
  10.315        else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
  10.316        /* go to next vertex and edge if edge is degenerated */
  10.317 @@ -669,65 +900,73 @@
  10.318    return min_dist;
  10.319  }
  10.320  
  10.321 +/* calculate (approximate) distance between two clusters */
  10.322 +static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) {
  10.323 +  int i, j;                    /* i: entry, j: point in entry */
  10.324 +  int npoints;                 /* number of points in entry */
  10.325 +  pgl_point *points;           /* array of points in entry */
  10.326 +  double dist;                 /* distance calculated in one step */
  10.327 +  double min_dist = INFINITY;  /* minimum distance */
  10.328 +  /* consider distance from each point in one cluster to the whole other */
  10.329 +  for (i=0; i<cluster1->nentries; i++) {
  10.330 +    npoints = cluster1->entries[i].npoints;
  10.331 +    points = PGL_ENTRY_POINTS(cluster1, i);
  10.332 +    for (j=0; j<npoints; j++) {
  10.333 +      dist = pgl_point_cluster_distance(points+j, cluster2);
  10.334 +      if (dist == 0) return dist;
  10.335 +      if (dist < min_dist) min_dist = dist;
  10.336 +    }
  10.337 +  }
  10.338 +  /* consider distance from each point in other cluster to the first cluster */
  10.339 +  for (i=0; i<cluster2->nentries; i++) {
  10.340 +    npoints = cluster2->entries[i].npoints;
  10.341 +    points = PGL_ENTRY_POINTS(cluster2, i);
  10.342 +    for (j=0; j<npoints; j++) {
  10.343 +      dist = pgl_point_cluster_distance(points+j, cluster1);
  10.344 +      if (dist == 0) return dist;
  10.345 +      if (dist < min_dist) min_dist = dist;
  10.346 +    }
  10.347 +  }
  10.348 +  return min_dist;
  10.349 +}
  10.350 +
  10.351  /* estimator function for distance between box and point */
  10.352 -/* allowed to return smaller values than actually correct */
  10.353 +/* always returns a smaller value than actually correct or zero */
  10.354  static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
  10.355 -  double dlon;  /* longitude range of box (delta longitude) */
  10.356 -  double h;     /* half of distance along meridian */
  10.357 -  double d;     /* distance between both southern or both northern points */
  10.358 -  double cur_dist;  /* calculated distance */
  10.359 -  double min_dist;  /* minimum distance calculated */
  10.360 -  /* return infinity if bounding box is empty */
  10.361 +  double dlon;      /* longitude range of box (delta longitude) */
  10.362 +  double distance;  /* return value */
  10.363 +  /* return infinity if box is empty */
  10.364    if (box->lat_min > box->lat_max) return INFINITY;
  10.365 -  /* return zero if point is inside bounding box */
  10.366 +  /* return zero if point is inside box */
  10.367    if (pgl_point_in_box(point, box)) return 0;
  10.368    /* calculate delta longitude */
  10.369    dlon = box->lon_max - box->lon_min;
  10.370    if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
  10.371 -  /* if delta longitude is greater than 180 degrees, perform safe fall-back */
  10.372 -  if (dlon > 180) return 0;
  10.373 -  /* calculate half of distance along meridian */
  10.374 -  h = pgl_distance(box->lat_min, 0, box->lat_max, 0) / 2;
  10.375 -  /* calculate full distance between southern points */
  10.376 -  d = pgl_distance(box->lat_min, 0, box->lat_min, dlon);
  10.377 -  /* calculate maximum of full distance and half distance */
  10.378 -  if (h > d) d = h;
  10.379 -  /* calculate distance from point to first southern vertex and substract
  10.380 -     maximum error */
  10.381 -  min_dist = pgl_distance(
  10.382 -    point->lat, point->lon, box->lat_min, box->lon_min
  10.383 -  ) - d;
  10.384 -  /* return zero if estimated distance is smaller than zero */
  10.385 -  if (min_dist <= 0) return 0;
  10.386 -  /* repeat procedure with second southern vertex */
  10.387 -  cur_dist = pgl_distance(
  10.388 -    point->lat, point->lon, box->lat_min, box->lon_max
  10.389 -  ) - d;
  10.390 -  if (cur_dist <= 0) return 0;
  10.391 -  if (cur_dist < min_dist) min_dist = cur_dist;
  10.392 -  /* calculate full distance between northern points */
  10.393 -  d = pgl_distance(box->lat_max, 0, box->lat_max, dlon);
  10.394 -  /* calculate maximum of full distance and half distance */
  10.395 -  if (h > d) d = h;
  10.396 -  /* repeat procedure with northern vertices */
  10.397 -  cur_dist = pgl_distance(
  10.398 -    point->lat, point->lon, box->lat_max, box->lon_max
  10.399 -  ) - d;
  10.400 -  if (cur_dist <= 0) return 0;
  10.401 -  if (cur_dist < min_dist) min_dist = cur_dist;
  10.402 -  cur_dist = pgl_distance(
  10.403 -    point->lat, point->lon, box->lat_max, box->lon_min
  10.404 -  ) - d;
  10.405 -  if (cur_dist <= 0) return 0;
  10.406 -  if (cur_dist < min_dist) min_dist = cur_dist;
  10.407 -  /* return smallest value (unless already returned zero) */
  10.408 -  return min_dist;
  10.409 +  /* if delta longitude is greater than 150 degrees, perform safe fall-back */
  10.410 +  if (dlon > 150) return 0;
  10.411 +  /* calculate lower limit for distance (formula below requires dlon <= 150) */
  10.412 +  /* TODO: provide better estimation function to improve performance */
  10.413 +  distance = (
  10.414 +    (1.0-1e-14) * pgl_distance(
  10.415 +      point->lat,
  10.416 +      point->lon,
  10.417 +      (box->lat_min + box->lat_max) / 2,
  10.418 +      box->lon_min + dlon/2
  10.419 +    ) - pgl_distance(
  10.420 +      box->lat_min, box->lon_min,
  10.421 +      box->lat_max, box->lon_max
  10.422 +    )
  10.423 +  );
  10.424 +  /* truncate negative results to zero */
  10.425 +  if (distance <= 0) distance = 0;
  10.426 +  /* return result */
  10.427 +  return distance;
  10.428  }
  10.429  
  10.430  
  10.431 -/*----------------------------*
  10.432 - *  fractal geographic index  *
  10.433 - *----------------------------*/
  10.434 +/*-------------------------------------------------*
  10.435 + *  geographic index based on space-filling curve  *
  10.436 + *-------------------------------------------------*/
  10.437  
  10.438  /* number of bytes used for geographic (center) position in keys */
  10.439  #define PGL_KEY_LATLON_BYTELEN 7
  10.440 @@ -735,9 +974,6 @@
  10.441  /* maximum reference value for logarithmic size of geographic objects */
  10.442  #define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
  10.443  
  10.444 -/* safety margin to avoid floating point errors in distance estimation */
  10.445 -#define PGL_FPE_SAFETY (1.0+1e-14)  /* slightly greater than 1.0 */
  10.446 -
  10.447  /* pointer to index key (either pgl_pointkey or pgl_areakey) */
  10.448  typedef unsigned char *pgl_keyptr;
  10.449  
  10.450 @@ -1094,7 +1330,7 @@
  10.451  }
  10.452  
  10.453  /* estimator function for distance between point and index key */
  10.454 -/* allowed to return smaller values than actually correct */
  10.455 +/* always returns a smaller value than actually correct or zero */
  10.456  static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
  10.457    pgl_box box;  /* center(!) bounding box of area index key */
  10.458    /* calculate center(!) bounding box and maximum radius of objects covered
  10.459 @@ -1103,11 +1339,7 @@
  10.460    /* calculate estimated distance between bounding box of center point of
  10.461       indexed object and point passed as second argument, then substract maximum
  10.462       radius of objects covered by index key */
  10.463 -  /* (use PGL_FPE_SAFETY factor to cope with minor floating point errors) */
  10.464 -  distance = (
  10.465 -    pgl_estimate_point_box_distance(point, &box) / PGL_FPE_SAFETY -
  10.466 -    distance * PGL_FPE_SAFETY
  10.467 -  );
  10.468 +  distance = pgl_estimate_point_box_distance(point, &box) - distance;
  10.469    /* truncate negative results to zero */
  10.470    if (distance <= 0) distance = 0;
  10.471    /* return result */
  10.472 @@ -2102,7 +2334,16 @@
  10.473  Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
  10.474    pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  10.475    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  10.476 -  bool retval = pgl_point_in_cluster(point, cluster);
  10.477 +  bool retval;
  10.478 +  /* points outside bounding circle are always assumed to be non-overlapping
  10.479 +     (necessary for consistent table and index scans) */
  10.480 +  if (
  10.481 +    pgl_distance(
  10.482 +      point->lat, point->lon,
  10.483 +      cluster->bounding.center.lat, cluster->bounding.center.lon
  10.484 +    ) > cluster->bounding.radius
  10.485 +  ) retval = false;
  10.486 +  else retval = pgl_point_in_cluster(point, cluster);
  10.487    PG_FREE_IF_COPY(cluster, 1);
  10.488    PG_RETURN_BOOL(retval);
  10.489  }
  10.490 @@ -2189,6 +2430,27 @@
  10.491    PG_RETURN_BOOL(retval);
  10.492  }
  10.493  
  10.494 +/* check if two clusters overlap (overlap operator "&&") in SQL */
  10.495 +PG_FUNCTION_INFO_V1(pgl_ecluster_overlap);
  10.496 +Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) {
  10.497 +  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  10.498 +  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  10.499 +  bool retval;
  10.500 +  /* clusters with non-touching bounding circles are always assumed to be
  10.501 +     non-overlapping (improves performance and is necessary for consistent
  10.502 +     table and index scans) */
  10.503 +  if (
  10.504 +    pgl_distance(
  10.505 +      cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  10.506 +      cluster2->bounding.center.lat, cluster2->bounding.center.lon
  10.507 +    ) > cluster1->bounding.radius + cluster2->bounding.radius
  10.508 +  ) retval = false;
  10.509 +  else retval = pgl_clusters_overlap(cluster1, cluster2);
  10.510 +  PG_FREE_IF_COPY(cluster1, 0);
  10.511 +  PG_FREE_IF_COPY(cluster2, 1);
  10.512 +  PG_RETURN_BOOL(retval);
  10.513 +}
  10.514 +
  10.515  /* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */
  10.516  PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap);
  10.517  Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  10.518 @@ -2203,6 +2465,27 @@
  10.519    PG_RETURN_BOOL(retval);
  10.520  }
  10.521  
  10.522 +/* check if second cluster is in first cluster (cont. operator "@>) in SQL */
  10.523 +PG_FUNCTION_INFO_V1(pgl_ecluster_contains);
  10.524 +Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) {
  10.525 +  pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  10.526 +  pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  10.527 +  bool retval;
  10.528 +  /* clusters with non-touching bounding circles are always assumed to be
  10.529 +     non-overlapping (improves performance and is necessary for consistent
  10.530 +     table and index scans) */
  10.531 +  if (
  10.532 +    pgl_distance(
  10.533 +      outer->bounding.center.lat, outer->bounding.center.lon,
  10.534 +      inner->bounding.center.lat, inner->bounding.center.lon
  10.535 +    ) > outer->bounding.radius + inner->bounding.radius
  10.536 +  ) retval = false;
  10.537 +  else retval = pgl_cluster_in_cluster(outer, inner);
  10.538 +  PG_FREE_IF_COPY(outer, 0);
  10.539 +  PG_FREE_IF_COPY(inner, 1);
  10.540 +  PG_RETURN_BOOL(retval);
  10.541 +}
  10.542 +
  10.543  /* calculate distance between two points ("<->" operator) in SQL */
  10.544  PG_FUNCTION_INFO_V1(pgl_epoint_distance);
  10.545  Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
  10.546 @@ -2258,6 +2541,17 @@
  10.547    PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  10.548  }
  10.549  
  10.550 +/* calculate distance between two clusters ("<->" operator) in SQL */
  10.551 +PG_FUNCTION_INFO_V1(pgl_ecluster_distance);
  10.552 +Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) {
  10.553 +  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  10.554 +  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  10.555 +  double retval = pgl_cluster_distance(cluster1, cluster2);
  10.556 +  PG_FREE_IF_COPY(cluster1, 0);
  10.557 +  PG_FREE_IF_COPY(cluster2, 1);
  10.558 +  PG_RETURN_FLOAT8(retval);
  10.559 +}
  10.560 +
  10.561  
  10.562  /*-----------------------------------------------------------*
  10.563   *  B-tree comparison operators and index support functions  *
    11.1 --- a/latlon.control	Sat Sep 03 16:00:22 2016 +0200
    11.2 +++ b/latlon.control	Fri Sep 09 19:22:30 2016 +0200
    11.3 @@ -1,3 +1,3 @@
    11.4 -comment = 'geospatial support'
    11.5 -default_version = '0.3'
    11.6 +comment = 'Geographic data types and spatial indexing for the WGS-84 spheroid'
    11.7 +default_version = '0.4'
    11.8  relocatable = true

Impressum / About Us