pgLatLon

changeset 6:2b7aea022117

Remove version 0.1 files (but keep update script from 0.1 to 0.2); Updated version number in manual installation instructions and include extension update script in manual instructions
author jbe
date Mon Aug 22 22:00:21 2016 +0200 (2016-08-22)
parents 13d26f330e20
children 9e11e9b2c0d9
files GNUmakefile README.html README.mkd latlon--0.1.sql latlon-v0001.c
line diff
     1.1 --- a/GNUmakefile	Mon Aug 22 21:52:32 2016 +0200
     1.2 +++ b/GNUmakefile	Mon Aug 22 22:00:21 2016 +0200
     1.3 @@ -1,6 +1,6 @@
     1.4  EXTENSION = latlon
     1.5 -DATA = latlon--0.1.sql latlon--0.1--0.2.sql latlon--0.2.sql
     1.6 -MODULES = latlon-v0001 latlon-v0002
     1.7 +DATA = latlon--0.1--0.2.sql latlon--0.2.sql
     1.8 +MODULES = latlon-v0002
     1.9  
    1.10  PG_CONFIG = pg_config
    1.11  PGXS := $(shell $(PG_CONFIG) --pgxs)
     2.1 --- a/README.html	Mon Aug 22 21:52:32 2016 +0200
     2.2 +++ b/README.html	Mon Aug 22 22:00:21 2016 +0200
     2.3 @@ -37,10 +37,10 @@
     2.4  <p>It is also possible to compile and install the extension without GNU Make as
     2.5  follows:</p>
     2.6  
     2.7 -<pre><code>cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0001.so latlon-v0001.c
     2.8 -cp latlon-v0001.so `pg_config --pkglibdir`
     2.9 +<pre><code>cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0002.so latlon-v0002.c
    2.10 +cp latlon-v0002.so `pg_config --pkglibdir`
    2.11  cp latlon.control `pg_config --sharedir`/extension/
    2.12 -cp latlon--0.1.sql `pg_config --sharedir`/extension/
    2.13 +cp latlon--0.1--0.2.sql latlon--0.2.sql `pg_config --sharedir`/extension/
    2.14  </code></pre>
    2.15  
    2.16  <h3>Loading the extension</h3>
     3.1 --- a/README.mkd	Mon Aug 22 21:52:32 2016 +0200
     3.2 +++ b/README.mkd	Mon Aug 22 22:00:21 2016 +0200
     3.3 @@ -36,10 +36,10 @@
     3.4  It is also possible to compile and install the extension without GNU Make as
     3.5  follows:
     3.6  
     3.7 -    cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0001.so latlon-v0001.c
     3.8 -    cp latlon-v0001.so `pg_config --pkglibdir`
     3.9 +    cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0002.so latlon-v0002.c
    3.10 +    cp latlon-v0002.so `pg_config --pkglibdir`
    3.11      cp latlon.control `pg_config --sharedir`/extension/
    3.12 -    cp latlon--0.1.sql `pg_config --sharedir`/extension/
    3.13 +    cp latlon--0.1--0.2.sql latlon--0.2.sql `pg_config --sharedir`/extension/
    3.14  
    3.15  ### Loading the extension
    3.16  
     4.1 --- a/latlon--0.1.sql	Mon Aug 22 21:52:32 2016 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,1205 +0,0 @@
     4.4 -
     4.5 -----------------------------------------
     4.6 --- forward declarations (shell types) --
     4.7 -----------------------------------------
     4.8 -
     4.9 -CREATE TYPE epoint;
    4.10 -CREATE TYPE ebox;
    4.11 -CREATE TYPE ecircle;
    4.12 -CREATE TYPE ecluster;
    4.13 -
    4.14 -
    4.15 -------------------------------------------------------------
    4.16 --- dummy input/output functions for dummy index key types --
    4.17 -------------------------------------------------------------
    4.18 -
    4.19 -CREATE FUNCTION ekey_point_in_dummy(cstring)
    4.20 -  RETURNS ekey_point
    4.21 -  LANGUAGE C IMMUTABLE STRICT
    4.22 -  AS '$libdir/latlon-v0001', 'pgl_notimpl';
    4.23 -
    4.24 -CREATE FUNCTION ekey_point_out_dummy(ekey_point)
    4.25 -  RETURNS cstring
    4.26 -  LANGUAGE C IMMUTABLE STRICT
    4.27 -  AS '$libdir/latlon-v0001', 'pgl_notimpl';
    4.28 -
    4.29 -CREATE FUNCTION ekey_area_in_dummy(cstring)
    4.30 -  RETURNS ekey_area
    4.31 -  LANGUAGE C IMMUTABLE STRICT
    4.32 -  AS '$libdir/latlon-v0001', 'pgl_notimpl';
    4.33 -
    4.34 -CREATE FUNCTION ekey_area_out_dummy(ekey_area)
    4.35 -  RETURNS cstring
    4.36 -  LANGUAGE C IMMUTABLE STRICT
    4.37 -  AS '$libdir/latlon-v0001', 'pgl_notimpl';
    4.38 -
    4.39 -
    4.40 ---------------------------
    4.41 --- text input functions --
    4.42 ---------------------------
    4.43 -
    4.44 -CREATE FUNCTION epoint_in(cstring)
    4.45 -  RETURNS epoint
    4.46 -  LANGUAGE C IMMUTABLE STRICT
    4.47 -  AS '$libdir/latlon-v0001', 'pgl_epoint_in';
    4.48 -
    4.49 -CREATE FUNCTION ebox_in(cstring)
    4.50 -  RETURNS ebox
    4.51 -  LANGUAGE C IMMUTABLE STRICT
    4.52 -  AS '$libdir/latlon-v0001', 'pgl_ebox_in';
    4.53 -
    4.54 -CREATE FUNCTION ecircle_in(cstring)
    4.55 -  RETURNS ecircle
    4.56 -  LANGUAGE C IMMUTABLE STRICT
    4.57 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_in';
    4.58 -
    4.59 -CREATE FUNCTION ecluster_in(cstring)
    4.60 -  RETURNS ecluster
    4.61 -  LANGUAGE C IMMUTABLE STRICT
    4.62 -  AS '$libdir/latlon-v0001', 'pgl_ecluster_in';
    4.63 -
    4.64 -
    4.65 ----------------------------
    4.66 --- text output functions --
    4.67 ----------------------------
    4.68 -
    4.69 -CREATE FUNCTION epoint_out(epoint)
    4.70 -  RETURNS cstring
    4.71 -  LANGUAGE C IMMUTABLE STRICT
    4.72 -  AS '$libdir/latlon-v0001', 'pgl_epoint_out';
    4.73 -
    4.74 -CREATE FUNCTION ebox_out(ebox)
    4.75 -  RETURNS cstring
    4.76 -  LANGUAGE C IMMUTABLE STRICT
    4.77 -  AS '$libdir/latlon-v0001', 'pgl_ebox_out';
    4.78 -
    4.79 -CREATE FUNCTION ecircle_out(ecircle)
    4.80 -  RETURNS cstring
    4.81 -  LANGUAGE C IMMUTABLE STRICT
    4.82 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_out';
    4.83 -
    4.84 -CREATE FUNCTION ecluster_out(ecluster)
    4.85 -  RETURNS cstring
    4.86 -  LANGUAGE C IMMUTABLE STRICT
    4.87 -  AS '$libdir/latlon-v0001', 'pgl_ecluster_out';
    4.88 -
    4.89 -
    4.90 ---------------------------
    4.91 --- binary I/O functions --
    4.92 ---------------------------
    4.93 -
    4.94 -CREATE FUNCTION epoint_recv(internal)
    4.95 -  RETURNS epoint
    4.96 -  LANGUAGE C IMMUTABLE STRICT
    4.97 -  AS '$libdir/latlon-v0001', 'pgl_epoint_recv';
    4.98 -
    4.99 -CREATE FUNCTION ebox_recv(internal)
   4.100 -  RETURNS ebox
   4.101 -  LANGUAGE C IMMUTABLE STRICT
   4.102 -  AS '$libdir/latlon-v0001', 'pgl_ebox_recv';
   4.103 -
   4.104 -CREATE FUNCTION ecircle_recv(internal)
   4.105 -  RETURNS ecircle
   4.106 -  LANGUAGE C IMMUTABLE STRICT
   4.107 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_recv';
   4.108 -
   4.109 -CREATE FUNCTION epoint_send(epoint)
   4.110 -  RETURNS bytea
   4.111 -  LANGUAGE C IMMUTABLE STRICT
   4.112 -  AS '$libdir/latlon-v0001', 'pgl_epoint_send';
   4.113 -
   4.114 -CREATE FUNCTION ebox_send(ebox)
   4.115 -  RETURNS bytea
   4.116 -  LANGUAGE C IMMUTABLE STRICT
   4.117 -  AS '$libdir/latlon-v0001', 'pgl_ebox_send';
   4.118 -
   4.119 -CREATE FUNCTION ecircle_send(ecircle)
   4.120 -  RETURNS bytea
   4.121 -  LANGUAGE C IMMUTABLE STRICT
   4.122 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_send';
   4.123 -
   4.124 -
   4.125 ------------------------------------------------
   4.126 --- type definitions of dummy index key types --
   4.127 ------------------------------------------------
   4.128 -
   4.129 -CREATE TYPE ekey_point (
   4.130 -  internallength = 8,
   4.131 -  input = ekey_point_in_dummy,
   4.132 -  output = ekey_point_out_dummy,
   4.133 -  alignment = char );
   4.134 -
   4.135 -CREATE TYPE ekey_area (
   4.136 -  internallength = 9,
   4.137 -  input = ekey_area_in_dummy,
   4.138 -  output = ekey_area_out_dummy,
   4.139 -  alignment = char );
   4.140 -
   4.141 -
   4.142 -------------------------------------------
   4.143 --- definitions of geographic data types --
   4.144 -------------------------------------------
   4.145 -
   4.146 -CREATE TYPE epoint (
   4.147 -  internallength = 16,
   4.148 -  input = epoint_in,
   4.149 -  output = epoint_out,
   4.150 -  receive = epoint_recv,
   4.151 -  send = epoint_send,
   4.152 -  alignment = double );
   4.153 -
   4.154 -CREATE TYPE ebox (
   4.155 -  internallength = 32,
   4.156 -  input = ebox_in,
   4.157 -  output = ebox_out,
   4.158 -  receive = ebox_recv,
   4.159 -  send = ebox_send,
   4.160 -  alignment = double );
   4.161 -
   4.162 -CREATE TYPE ecircle (
   4.163 -  internallength = 24,
   4.164 -  input = ecircle_in,
   4.165 -  output = ecircle_out,
   4.166 -  receive = ecircle_recv,
   4.167 -  send = ecircle_send,
   4.168 -  alignment = double );
   4.169 -
   4.170 -CREATE TYPE ecluster (
   4.171 -  internallength = VARIABLE,
   4.172 -  input = ecluster_in,
   4.173 -  output = ecluster_out,
   4.174 -  alignment = double,
   4.175 -  storage = external );
   4.176 -
   4.177 -
   4.178 ---------------------
   4.179 --- B-tree support --
   4.180 ---------------------
   4.181 -
   4.182 --- begin of B-tree support for epoint
   4.183 -
   4.184 -CREATE FUNCTION epoint_btree_lt(epoint, epoint)
   4.185 -  RETURNS boolean
   4.186 -  LANGUAGE C IMMUTABLE STRICT
   4.187 -  AS '$libdir/latlon-v0001', 'pgl_btree_epoint_lt';
   4.188 -
   4.189 -CREATE FUNCTION epoint_btree_le(epoint, epoint)
   4.190 -  RETURNS boolean
   4.191 -  LANGUAGE C IMMUTABLE STRICT
   4.192 -  AS '$libdir/latlon-v0001', 'pgl_btree_epoint_le';
   4.193 -
   4.194 -CREATE FUNCTION epoint_btree_eq(epoint, epoint)
   4.195 -  RETURNS boolean
   4.196 -  LANGUAGE C IMMUTABLE STRICT
   4.197 -  AS '$libdir/latlon-v0001', 'pgl_btree_epoint_eq';
   4.198 -
   4.199 -CREATE FUNCTION epoint_btree_ne(epoint, epoint)
   4.200 -  RETURNS boolean
   4.201 -  LANGUAGE C IMMUTABLE STRICT
   4.202 -  AS '$libdir/latlon-v0001', 'pgl_btree_epoint_ne';
   4.203 -
   4.204 -CREATE FUNCTION epoint_btree_ge(epoint, epoint)
   4.205 -  RETURNS boolean
   4.206 -  LANGUAGE C IMMUTABLE STRICT
   4.207 -  AS '$libdir/latlon-v0001', 'pgl_btree_epoint_ge';
   4.208 -
   4.209 -CREATE FUNCTION epoint_btree_gt(epoint, epoint)
   4.210 -  RETURNS boolean
   4.211 -  LANGUAGE C IMMUTABLE STRICT
   4.212 -  AS '$libdir/latlon-v0001', 'pgl_btree_epoint_gt';
   4.213 -
   4.214 -CREATE OPERATOR <<< (
   4.215 -  leftarg = epoint,
   4.216 -  rightarg = epoint,
   4.217 -  procedure = epoint_btree_lt,
   4.218 -  commutator = >>>,
   4.219 -  negator = >>>=,
   4.220 -  restrict = scalarltsel,
   4.221 -  join = scalarltjoinsel
   4.222 -);
   4.223 -
   4.224 -CREATE OPERATOR <<<= (
   4.225 -  leftarg = epoint,
   4.226 -  rightarg = epoint,
   4.227 -  procedure = epoint_btree_le,
   4.228 -  commutator = >>>=,
   4.229 -  negator = >>>,
   4.230 -  restrict = scalarltsel,
   4.231 -  join = scalarltjoinsel
   4.232 -);
   4.233 -
   4.234 -CREATE OPERATOR = (
   4.235 -  leftarg = epoint,
   4.236 -  rightarg = epoint,
   4.237 -  procedure = epoint_btree_eq,
   4.238 -  commutator = =,
   4.239 -  negator = <>,
   4.240 -  restrict = eqsel,
   4.241 -  join = eqjoinsel,
   4.242 -  merges
   4.243 -);
   4.244 -
   4.245 -CREATE OPERATOR <> (
   4.246 -  leftarg = epoint,
   4.247 -  rightarg = epoint,
   4.248 -  procedure = epoint_btree_eq,
   4.249 -  commutator = <>,
   4.250 -  negator = =,
   4.251 -  restrict = neqsel,
   4.252 -  join = neqjoinsel
   4.253 -);
   4.254 -
   4.255 -CREATE OPERATOR >>>= (
   4.256 -  leftarg = epoint,
   4.257 -  rightarg = epoint,
   4.258 -  procedure = epoint_btree_ge,
   4.259 -  commutator = <<<=,
   4.260 -  negator = <<<,
   4.261 -  restrict = scalargtsel,
   4.262 -  join = scalargtjoinsel
   4.263 -);
   4.264 -
   4.265 -CREATE OPERATOR >>> (
   4.266 -  leftarg = epoint,
   4.267 -  rightarg = epoint,
   4.268 -  procedure = epoint_btree_gt,
   4.269 -  commutator = <<<,
   4.270 -  negator = <<<=,
   4.271 -  restrict = scalargtsel,
   4.272 -  join = scalargtjoinsel
   4.273 -);
   4.274 -
   4.275 -CREATE FUNCTION epoint_btree_cmp(epoint, epoint)
   4.276 -  RETURNS int4
   4.277 -  LANGUAGE C IMMUTABLE STRICT
   4.278 -  AS '$libdir/latlon-v0001', 'pgl_btree_epoint_cmp';
   4.279 -
   4.280 -CREATE OPERATOR CLASS epoint_btree_ops
   4.281 -  DEFAULT FOR TYPE epoint USING btree AS
   4.282 -  OPERATOR 1 <<< ,
   4.283 -  OPERATOR 2 <<<= ,
   4.284 -  OPERATOR 3 = ,
   4.285 -  OPERATOR 4 >>>= ,
   4.286 -  OPERATOR 5 >>> ,
   4.287 -  FUNCTION 1 epoint_btree_cmp(epoint, epoint);
   4.288 -
   4.289 --- end of B-tree support for epoint
   4.290 -
   4.291 --- begin of B-tree support for ebox
   4.292 -
   4.293 -CREATE FUNCTION ebox_btree_lt(ebox, ebox)
   4.294 -  RETURNS boolean
   4.295 -  LANGUAGE C IMMUTABLE STRICT
   4.296 -  AS '$libdir/latlon-v0001', 'pgl_btree_ebox_lt';
   4.297 -
   4.298 -CREATE FUNCTION ebox_btree_le(ebox, ebox)
   4.299 -  RETURNS boolean
   4.300 -  LANGUAGE C IMMUTABLE STRICT
   4.301 -  AS '$libdir/latlon-v0001', 'pgl_btree_ebox_le';
   4.302 -
   4.303 -CREATE FUNCTION ebox_btree_eq(ebox, ebox)
   4.304 -  RETURNS boolean
   4.305 -  LANGUAGE C IMMUTABLE STRICT
   4.306 -  AS '$libdir/latlon-v0001', 'pgl_btree_ebox_eq';
   4.307 -
   4.308 -CREATE FUNCTION ebox_btree_ne(ebox, ebox)
   4.309 -  RETURNS boolean
   4.310 -  LANGUAGE C IMMUTABLE STRICT
   4.311 -  AS '$libdir/latlon-v0001', 'pgl_btree_ebox_ne';
   4.312 -
   4.313 -CREATE FUNCTION ebox_btree_ge(ebox, ebox)
   4.314 -  RETURNS boolean
   4.315 -  LANGUAGE C IMMUTABLE STRICT
   4.316 -  AS '$libdir/latlon-v0001', 'pgl_btree_ebox_ge';
   4.317 -
   4.318 -CREATE FUNCTION ebox_btree_gt(ebox, ebox)
   4.319 -  RETURNS boolean
   4.320 -  LANGUAGE C IMMUTABLE STRICT
   4.321 -  AS '$libdir/latlon-v0001', 'pgl_btree_ebox_gt';
   4.322 -
   4.323 -CREATE OPERATOR <<< (
   4.324 -  leftarg = ebox,
   4.325 -  rightarg = ebox,
   4.326 -  procedure = ebox_btree_lt,
   4.327 -  commutator = >>>,
   4.328 -  negator = >>>=,
   4.329 -  restrict = scalarltsel,
   4.330 -  join = scalarltjoinsel
   4.331 -);
   4.332 -
   4.333 -CREATE OPERATOR <<<= (
   4.334 -  leftarg = ebox,
   4.335 -  rightarg = ebox,
   4.336 -  procedure = ebox_btree_le,
   4.337 -  commutator = >>>=,
   4.338 -  negator = >>>,
   4.339 -  restrict = scalarltsel,
   4.340 -  join = scalarltjoinsel
   4.341 -);
   4.342 -
   4.343 -CREATE OPERATOR = (
   4.344 -  leftarg = ebox,
   4.345 -  rightarg = ebox,
   4.346 -  procedure = ebox_btree_eq,
   4.347 -  commutator = =,
   4.348 -  negator = <>,
   4.349 -  restrict = eqsel,
   4.350 -  join = eqjoinsel,
   4.351 -  merges
   4.352 -);
   4.353 -
   4.354 -CREATE OPERATOR <> (
   4.355 -  leftarg = ebox,
   4.356 -  rightarg = ebox,
   4.357 -  procedure = ebox_btree_eq,
   4.358 -  commutator = <>,
   4.359 -  negator = =,
   4.360 -  restrict = neqsel,
   4.361 -  join = neqjoinsel
   4.362 -);
   4.363 -
   4.364 -CREATE OPERATOR >>>= (
   4.365 -  leftarg = ebox,
   4.366 -  rightarg = ebox,
   4.367 -  procedure = ebox_btree_ge,
   4.368 -  commutator = <<<=,
   4.369 -  negator = <<<,
   4.370 -  restrict = scalargtsel,
   4.371 -  join = scalargtjoinsel
   4.372 -);
   4.373 -
   4.374 -CREATE OPERATOR >>> (
   4.375 -  leftarg = ebox,
   4.376 -  rightarg = ebox,
   4.377 -  procedure = ebox_btree_gt,
   4.378 -  commutator = <<<,
   4.379 -  negator = <<<=,
   4.380 -  restrict = scalargtsel,
   4.381 -  join = scalargtjoinsel
   4.382 -);
   4.383 -
   4.384 -CREATE FUNCTION ebox_btree_cmp(ebox, ebox)
   4.385 -  RETURNS int4
   4.386 -  LANGUAGE C IMMUTABLE STRICT
   4.387 -  AS '$libdir/latlon-v0001', 'pgl_btree_ebox_cmp';
   4.388 -
   4.389 -CREATE OPERATOR CLASS ebox_btree_ops
   4.390 -  DEFAULT FOR TYPE ebox USING btree AS
   4.391 -  OPERATOR 1 <<< ,
   4.392 -  OPERATOR 2 <<<= ,
   4.393 -  OPERATOR 3 = ,
   4.394 -  OPERATOR 4 >>>= ,
   4.395 -  OPERATOR 5 >>> ,
   4.396 -  FUNCTION 1 ebox_btree_cmp(ebox, ebox);
   4.397 -
   4.398 --- end of B-tree support for ebox
   4.399 -
   4.400 --- begin of B-tree support for ecircle
   4.401 -
   4.402 -CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   4.403 -  RETURNS boolean
   4.404 -  LANGUAGE C IMMUTABLE STRICT
   4.405 -  AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_lt';
   4.406 -
   4.407 -CREATE FUNCTION ecircle_btree_le(ecircle, ecircle)
   4.408 -  RETURNS boolean
   4.409 -  LANGUAGE C IMMUTABLE STRICT
   4.410 -  AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_le';
   4.411 -
   4.412 -CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   4.413 -  RETURNS boolean
   4.414 -  LANGUAGE C IMMUTABLE STRICT
   4.415 -  AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_eq';
   4.416 -
   4.417 -CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   4.418 -  RETURNS boolean
   4.419 -  LANGUAGE C IMMUTABLE STRICT
   4.420 -  AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_ne';
   4.421 -
   4.422 -CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   4.423 -  RETURNS boolean
   4.424 -  LANGUAGE C IMMUTABLE STRICT
   4.425 -  AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_ge';
   4.426 -
   4.427 -CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   4.428 -  RETURNS boolean
   4.429 -  LANGUAGE C IMMUTABLE STRICT
   4.430 -  AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_gt';
   4.431 -
   4.432 -CREATE OPERATOR <<< (
   4.433 -  leftarg = ecircle,
   4.434 -  rightarg = ecircle,
   4.435 -  procedure = ecircle_btree_lt,
   4.436 -  commutator = >>>,
   4.437 -  negator = >>>=,
   4.438 -  restrict = scalarltsel,
   4.439 -  join = scalarltjoinsel
   4.440 -);
   4.441 -
   4.442 -CREATE OPERATOR <<<= (
   4.443 -  leftarg = ecircle,
   4.444 -  rightarg = ecircle,
   4.445 -  procedure = ecircle_btree_le,
   4.446 -  commutator = >>>=,
   4.447 -  negator = >>>,
   4.448 -  restrict = scalarltsel,
   4.449 -  join = scalarltjoinsel
   4.450 -);
   4.451 -
   4.452 -CREATE OPERATOR = (
   4.453 -  leftarg = ecircle,
   4.454 -  rightarg = ecircle,
   4.455 -  procedure = ecircle_btree_eq,
   4.456 -  commutator = =,
   4.457 -  negator = <>,
   4.458 -  restrict = eqsel,
   4.459 -  join = eqjoinsel,
   4.460 -  merges
   4.461 -);
   4.462 -
   4.463 -CREATE OPERATOR <> (
   4.464 -  leftarg = ecircle,
   4.465 -  rightarg = ecircle,
   4.466 -  procedure = ecircle_btree_eq,
   4.467 -  commutator = <>,
   4.468 -  negator = =,
   4.469 -  restrict = neqsel,
   4.470 -  join = neqjoinsel
   4.471 -);
   4.472 -
   4.473 -CREATE OPERATOR >>>= (
   4.474 -  leftarg = ecircle,
   4.475 -  rightarg = ecircle,
   4.476 -  procedure = ecircle_btree_ge,
   4.477 -  commutator = <<<=,
   4.478 -  negator = <<<,
   4.479 -  restrict = scalargtsel,
   4.480 -  join = scalargtjoinsel
   4.481 -);
   4.482 -
   4.483 -CREATE OPERATOR >>> (
   4.484 -  leftarg = ecircle,
   4.485 -  rightarg = ecircle,
   4.486 -  procedure = ecircle_btree_gt,
   4.487 -  commutator = <<<,
   4.488 -  negator = <<<=,
   4.489 -  restrict = scalargtsel,
   4.490 -  join = scalargtjoinsel
   4.491 -);
   4.492 -
   4.493 -CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   4.494 -  RETURNS int4
   4.495 -  LANGUAGE C IMMUTABLE STRICT
   4.496 -  AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_cmp';
   4.497 -
   4.498 -CREATE OPERATOR CLASS ecircle_btree_ops
   4.499 -  DEFAULT FOR TYPE ecircle USING btree AS
   4.500 -  OPERATOR 1 <<< ,
   4.501 -  OPERATOR 2 <<<= ,
   4.502 -  OPERATOR 3 = ,
   4.503 -  OPERATOR 4 >>>= ,
   4.504 -  OPERATOR 5 >>> ,
   4.505 -  FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle);
   4.506 -
   4.507 --- end of B-tree support for ecircle
   4.508 -
   4.509 -
   4.510 -----------------
   4.511 --- type casts --
   4.512 -----------------
   4.513 -
   4.514 -CREATE FUNCTION cast_epoint_to_ebox(epoint)
   4.515 -  RETURNS ebox
   4.516 -  LANGUAGE C IMMUTABLE STRICT
   4.517 -  AS '$libdir/latlon-v0001', 'pgl_epoint_to_ebox';
   4.518 -
   4.519 -CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint);
   4.520 -
   4.521 -CREATE FUNCTION cast_epoint_to_ecircle(epoint)
   4.522 -  RETURNS ecircle
   4.523 -  LANGUAGE C IMMUTABLE STRICT
   4.524 -  AS '$libdir/latlon-v0001', 'pgl_epoint_to_ecircle';
   4.525 -
   4.526 -CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint);
   4.527 -
   4.528 -CREATE FUNCTION cast_epoint_to_ecluster(epoint)
   4.529 -  RETURNS ecluster
   4.530 -  LANGUAGE C IMMUTABLE STRICT
   4.531 -  AS '$libdir/latlon-v0001', 'pgl_epoint_to_ecluster';
   4.532 -
   4.533 -CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint);
   4.534 -
   4.535 -CREATE FUNCTION cast_ebox_to_ecluster(ebox)
   4.536 -  RETURNS ecluster
   4.537 -  LANGUAGE C IMMUTABLE STRICT
   4.538 -  AS '$libdir/latlon-v0001', 'pgl_ebox_to_ecluster';
   4.539 -
   4.540 -CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox);
   4.541 -
   4.542 -
   4.543 ----------------------------
   4.544 --- constructor functions --
   4.545 ----------------------------
   4.546 -
   4.547 -CREATE FUNCTION epoint(float8, float8)
   4.548 -  RETURNS epoint
   4.549 -  LANGUAGE C IMMUTABLE STRICT
   4.550 -  AS '$libdir/latlon-v0001', 'pgl_create_epoint';
   4.551 -
   4.552 -CREATE FUNCTION epoint_latlon(float8, float8)
   4.553 -  RETURNS epoint
   4.554 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   4.555 -    SELECT epoint($1, $2)
   4.556 -  $$;
   4.557 -
   4.558 -CREATE FUNCTION epoint_lonlat(float8, float8)
   4.559 -  RETURNS epoint
   4.560 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   4.561 -    SELECT epoint($2, $1)
   4.562 -  $$;
   4.563 -
   4.564 -CREATE FUNCTION empty_ebox()
   4.565 -  RETURNS ebox
   4.566 -  LANGUAGE C IMMUTABLE STRICT
   4.567 -  AS '$libdir/latlon-v0001', 'pgl_create_empty_ebox';
   4.568 -
   4.569 -CREATE FUNCTION ebox(float8, float8, float8, float8)
   4.570 -  RETURNS ebox
   4.571 -  LANGUAGE C IMMUTABLE STRICT
   4.572 -  AS '$libdir/latlon-v0001', 'pgl_create_ebox';
   4.573 -
   4.574 -CREATE FUNCTION ebox(epoint, epoint)
   4.575 -  RETURNS ebox
   4.576 -  LANGUAGE C IMMUTABLE STRICT
   4.577 -  AS '$libdir/latlon-v0001', 'pgl_create_ebox_from_epoints';
   4.578 -
   4.579 -CREATE FUNCTION ecircle(float8, float8, float8)
   4.580 -  RETURNS ecircle
   4.581 -  LANGUAGE C IMMUTABLE STRICT
   4.582 -  AS '$libdir/latlon-v0001', 'pgl_create_ecircle';
   4.583 -
   4.584 -CREATE FUNCTION ecircle(epoint, float8)
   4.585 -  RETURNS ecircle
   4.586 -  LANGUAGE C IMMUTABLE STRICT
   4.587 -  AS '$libdir/latlon-v0001', 'pgl_create_ecircle_from_epoint';
   4.588 -
   4.589 -CREATE FUNCTION ecluster_concat(ecluster[])
   4.590 -  RETURNS ecluster
   4.591 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.592 -    SELECT array_to_string($1, ' ')::ecluster
   4.593 -  $$;
   4.594 -
   4.595 -CREATE FUNCTION ecluster_concat(ecluster, ecluster)
   4.596 -  RETURNS ecluster
   4.597 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.598 -    SELECT ($1::text || ' ' || $2::text)::ecluster
   4.599 -  $$;
   4.600 -
   4.601 -CREATE FUNCTION ecluster_create_multipoint(epoint[])
   4.602 -  RETURNS ecluster
   4.603 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.604 -    SELECT
   4.605 -      array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster
   4.606 -    FROM unnest($1)
   4.607 -  $$;
   4.608 -
   4.609 -CREATE FUNCTION ecluster_create_path(epoint[])
   4.610 -  RETURNS ecluster
   4.611 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.612 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   4.613 -      ('path (' || array_to_string($1, ' ') || ')')::ecluster
   4.614 -    END
   4.615 -    FROM array_to_string($1, ' ') AS "str"
   4.616 -  $$;
   4.617 -
   4.618 -CREATE FUNCTION ecluster_create_outline(epoint[])
   4.619 -  RETURNS ecluster
   4.620 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.621 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   4.622 -      ('outline (' || array_to_string($1, ' ') || ')')::ecluster
   4.623 -    END
   4.624 -    FROM array_to_string($1, ' ') AS "str"
   4.625 -  $$;
   4.626 -
   4.627 -CREATE FUNCTION ecluster_create_polygon(epoint[])
   4.628 -  RETURNS ecluster
   4.629 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.630 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   4.631 -      ('polygon (' || array_to_string($1, ' ') || ')')::ecluster
   4.632 -    END
   4.633 -    FROM array_to_string($1, ' ') AS "str"
   4.634 -  $$;
   4.635 -
   4.636 -
   4.637 -----------------------
   4.638 --- getter functions --
   4.639 -----------------------
   4.640 -
   4.641 -CREATE FUNCTION latitude(epoint)
   4.642 -  RETURNS float8
   4.643 -  LANGUAGE C IMMUTABLE STRICT
   4.644 -  AS '$libdir/latlon-v0001', 'pgl_epoint_lat';
   4.645 -
   4.646 -CREATE FUNCTION longitude(epoint)
   4.647 -  RETURNS float8
   4.648 -  LANGUAGE C IMMUTABLE STRICT
   4.649 -  AS '$libdir/latlon-v0001', 'pgl_epoint_lon';
   4.650 -
   4.651 -CREATE FUNCTION min_latitude(ebox)
   4.652 -  RETURNS float8
   4.653 -  LANGUAGE C IMMUTABLE STRICT
   4.654 -  AS '$libdir/latlon-v0001', 'pgl_ebox_lat_min';
   4.655 -
   4.656 -CREATE FUNCTION max_latitude(ebox)
   4.657 -  RETURNS float8
   4.658 -  LANGUAGE C IMMUTABLE STRICT
   4.659 -  AS '$libdir/latlon-v0001', 'pgl_ebox_lat_max';
   4.660 -
   4.661 -CREATE FUNCTION min_longitude(ebox)
   4.662 -  RETURNS float8
   4.663 -  LANGUAGE C IMMUTABLE STRICT
   4.664 -  AS '$libdir/latlon-v0001', 'pgl_ebox_lon_min';
   4.665 -
   4.666 -CREATE FUNCTION max_longitude(ebox)
   4.667 -  RETURNS float8
   4.668 -  LANGUAGE C IMMUTABLE STRICT
   4.669 -  AS '$libdir/latlon-v0001', 'pgl_ebox_lon_max';
   4.670 -
   4.671 -CREATE FUNCTION center(ecircle)
   4.672 -  RETURNS epoint
   4.673 -  LANGUAGE C IMMUTABLE STRICT
   4.674 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_center';
   4.675 -
   4.676 -CREATE FUNCTION radius(ecircle)
   4.677 -  RETURNS float8
   4.678 -  LANGUAGE C IMMUTABLE STRICT
   4.679 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_radius';
   4.680 -
   4.681 -CREATE FUNCTION ecluster_extract_points(ecluster)
   4.682 -  RETURNS SETOF epoint
   4.683 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.684 -    SELECT "match"[2]::epoint
   4.685 -    FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match"
   4.686 -  $$;
   4.687 -
   4.688 -CREATE FUNCTION ecluster_extract_paths(ecluster)
   4.689 -  RETURNS SETOF epoint[]
   4.690 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.691 -    SELECT (
   4.692 -      SELECT array_agg("m2"[1]::epoint)
   4.693 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   4.694 -    )
   4.695 -    FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1"
   4.696 -  $$;
   4.697 -
   4.698 -CREATE FUNCTION ecluster_extract_outlines(ecluster)
   4.699 -  RETURNS SETOF epoint[]
   4.700 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.701 -    SELECT (
   4.702 -      SELECT array_agg("m2"[1]::epoint)
   4.703 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   4.704 -    )
   4.705 -    FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1"
   4.706 -  $$;
   4.707 -
   4.708 -CREATE FUNCTION ecluster_extract_polygons(ecluster)
   4.709 -  RETURNS SETOF epoint[]
   4.710 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   4.711 -    SELECT (
   4.712 -      SELECT array_agg("m2"[1]::epoint)
   4.713 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   4.714 -    )
   4.715 -    FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1"
   4.716 -  $$;
   4.717 -
   4.718 -
   4.719 ----------------
   4.720 --- operators --
   4.721 ----------------
   4.722 -
   4.723 -CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   4.724 -  RETURNS boolean
   4.725 -  LANGUAGE C IMMUTABLE STRICT
   4.726 -  AS '$libdir/latlon-v0001', 'pgl_epoint_ebox_overlap';
   4.727 -
   4.728 -CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   4.729 -  RETURNS boolean
   4.730 -  LANGUAGE C IMMUTABLE STRICT
   4.731 -  AS '$libdir/latlon-v0001', 'pgl_epoint_ecircle_overlap';
   4.732 -
   4.733 -CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   4.734 -  RETURNS boolean
   4.735 -  LANGUAGE C IMMUTABLE STRICT
   4.736 -  AS '$libdir/latlon-v0001', 'pgl_epoint_ecluster_overlap';
   4.737 -
   4.738 -CREATE FUNCTION ebox_overlap_proc(ebox, ebox)
   4.739 -  RETURNS boolean
   4.740 -  LANGUAGE C IMMUTABLE STRICT
   4.741 -  AS '$libdir/latlon-v0001', 'pgl_ebox_overlap';
   4.742 -
   4.743 -CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   4.744 -  RETURNS boolean
   4.745 -  LANGUAGE C IMMUTABLE STRICT
   4.746 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_overlap';
   4.747 -
   4.748 -CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   4.749 -  RETURNS boolean
   4.750 -  LANGUAGE C IMMUTABLE STRICT
   4.751 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_ecluster_overlap';
   4.752 -
   4.753 -CREATE FUNCTION epoint_distance_proc(epoint, epoint)
   4.754 -  RETURNS float8
   4.755 -  LANGUAGE C IMMUTABLE STRICT
   4.756 -  AS '$libdir/latlon-v0001', 'pgl_epoint_distance';
   4.757 -
   4.758 -CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   4.759 -  RETURNS float8
   4.760 -  LANGUAGE C IMMUTABLE STRICT
   4.761 -  AS '$libdir/latlon-v0001', 'pgl_epoint_ecircle_distance';
   4.762 -
   4.763 -CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   4.764 -  RETURNS float8
   4.765 -  LANGUAGE C IMMUTABLE STRICT
   4.766 -  AS '$libdir/latlon-v0001', 'pgl_epoint_ecluster_distance';
   4.767 -
   4.768 -CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   4.769 -  RETURNS float8
   4.770 -  LANGUAGE C IMMUTABLE STRICT
   4.771 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_distance';
   4.772 -
   4.773 -CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   4.774 -  RETURNS float8
   4.775 -  LANGUAGE C IMMUTABLE STRICT
   4.776 -  AS '$libdir/latlon-v0001', 'pgl_ecircle_ecluster_distance';
   4.777 -
   4.778 -CREATE OPERATOR && (
   4.779 -  leftarg = epoint,
   4.780 -  rightarg = ebox,
   4.781 -  procedure = epoint_ebox_overlap_proc,
   4.782 -  commutator = &&,
   4.783 -  restrict = areasel,
   4.784 -  join = areajoinsel
   4.785 -);
   4.786 -
   4.787 -CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint)
   4.788 -  RETURNS boolean
   4.789 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   4.790 -
   4.791 -CREATE OPERATOR && (
   4.792 -  leftarg = ebox,
   4.793 -  rightarg = epoint,
   4.794 -  procedure = epoint_ebox_overlap_commutator,
   4.795 -  commutator = &&,
   4.796 -  restrict = areasel,
   4.797 -  join = areajoinsel
   4.798 -);
   4.799 -
   4.800 -CREATE OPERATOR && (
   4.801 -  leftarg = epoint,
   4.802 -  rightarg = ecircle,
   4.803 -  procedure = epoint_ecircle_overlap_proc,
   4.804 -  commutator = &&,
   4.805 -  restrict = areasel,
   4.806 -  join = areajoinsel
   4.807 -);
   4.808 -
   4.809 -CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint)
   4.810 -  RETURNS boolean
   4.811 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   4.812 -
   4.813 -CREATE OPERATOR && (
   4.814 -  leftarg = ecircle,
   4.815 -  rightarg = epoint,
   4.816 -  procedure = epoint_ecircle_overlap_commutator,
   4.817 -  commutator = &&,
   4.818 -  restrict = areasel,
   4.819 -  join = areajoinsel
   4.820 -);
   4.821 -
   4.822 -CREATE OPERATOR && (
   4.823 -  leftarg = epoint,
   4.824 -  rightarg = ecluster,
   4.825 -  procedure = epoint_ecluster_overlap_proc,
   4.826 -  commutator = &&,
   4.827 -  restrict = areasel,
   4.828 -  join = areajoinsel
   4.829 -);
   4.830 -
   4.831 -CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint)
   4.832 -  RETURNS boolean
   4.833 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   4.834 -
   4.835 -CREATE OPERATOR && (
   4.836 -  leftarg = ecluster,
   4.837 -  rightarg = epoint,
   4.838 -  procedure = epoint_ecluster_overlap_commutator,
   4.839 -  commutator = &&,
   4.840 -  restrict = areasel,
   4.841 -  join = areajoinsel
   4.842 -);
   4.843 -
   4.844 -CREATE OPERATOR && (
   4.845 -  leftarg = ebox,
   4.846 -  rightarg = ebox,
   4.847 -  procedure = ebox_overlap_proc,
   4.848 -  commutator = &&,
   4.849 -  restrict = areasel,
   4.850 -  join = areajoinsel
   4.851 -);
   4.852 -
   4.853 -CREATE OPERATOR && (
   4.854 -  leftarg = ecircle,
   4.855 -  rightarg = ecircle,
   4.856 -  procedure = ecircle_overlap_proc,
   4.857 -  commutator = &&,
   4.858 -  restrict = areasel,
   4.859 -  join = areajoinsel
   4.860 -);
   4.861 -
   4.862 -CREATE OPERATOR && (
   4.863 -  leftarg = ecircle,
   4.864 -  rightarg = ecluster,
   4.865 -  procedure = ecircle_ecluster_overlap_proc,
   4.866 -  commutator = &&,
   4.867 -  restrict = areasel,
   4.868 -  join = areajoinsel
   4.869 -);
   4.870 -
   4.871 -CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle)
   4.872 -  RETURNS boolean
   4.873 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   4.874 -
   4.875 -CREATE OPERATOR && (
   4.876 -  leftarg = ecluster,
   4.877 -  rightarg = ecircle,
   4.878 -  procedure = ecircle_ecluster_overlap_commutator,
   4.879 -  commutator = &&,
   4.880 -  restrict = areasel,
   4.881 -  join = areajoinsel
   4.882 -);
   4.883 -
   4.884 -CREATE OPERATOR <-> (
   4.885 -  leftarg = epoint,
   4.886 -  rightarg = epoint,
   4.887 -  procedure = epoint_distance_proc,
   4.888 -  commutator = <->
   4.889 -);
   4.890 -
   4.891 -CREATE OPERATOR <-> (
   4.892 -  leftarg = epoint,
   4.893 -  rightarg = ecircle,
   4.894 -  procedure = epoint_ecircle_distance_proc,
   4.895 -  commutator = <->
   4.896 -);
   4.897 -
   4.898 -CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint)
   4.899 -  RETURNS float8
   4.900 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
   4.901 -
   4.902 -CREATE OPERATOR <-> (
   4.903 -  leftarg = ecircle,
   4.904 -  rightarg = epoint,
   4.905 -  procedure = epoint_ecircle_distance_commutator,
   4.906 -  commutator = <->
   4.907 -);
   4.908 -
   4.909 -CREATE OPERATOR <-> (
   4.910 -  leftarg = epoint,
   4.911 -  rightarg = ecluster,
   4.912 -  procedure = epoint_ecluster_distance_proc,
   4.913 -  commutator = <->
   4.914 -);
   4.915 -
   4.916 -CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint)
   4.917 -  RETURNS float8
   4.918 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
   4.919 -
   4.920 -CREATE OPERATOR <-> (
   4.921 -  leftarg = ecluster,
   4.922 -  rightarg = epoint,
   4.923 -  procedure = epoint_ecluster_distance_commutator,
   4.924 -  commutator = <->
   4.925 -);
   4.926 -
   4.927 -CREATE OPERATOR <-> (
   4.928 -  leftarg = ecircle,
   4.929 -  rightarg = ecircle,
   4.930 -  procedure = ecircle_distance_proc,
   4.931 -  commutator = <->
   4.932 -);
   4.933 -
   4.934 -CREATE OPERATOR <-> (
   4.935 -  leftarg = ecircle,
   4.936 -  rightarg = ecluster,
   4.937 -  procedure = ecircle_ecluster_distance_proc,
   4.938 -  commutator = <->
   4.939 -);
   4.940 -
   4.941 -CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle)
   4.942 -  RETURNS float8
   4.943 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
   4.944 -
   4.945 -CREATE OPERATOR <-> (
   4.946 -  leftarg = ecluster,
   4.947 -  rightarg = ecircle,
   4.948 -  procedure = ecircle_ecluster_distance_commutator,
   4.949 -  commutator = <->
   4.950 -);
   4.951 -
   4.952 -
   4.953 -----------------
   4.954 --- GiST index --
   4.955 -----------------
   4.956 -
   4.957 -CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
   4.958 -  RETURNS boolean
   4.959 -  LANGUAGE C STRICT
   4.960 -  AS '$libdir/latlon-v0001', 'pgl_gist_consistent';
   4.961 -
   4.962 -CREATE FUNCTION pgl_gist_union(internal, internal)
   4.963 -  RETURNS internal
   4.964 -  LANGUAGE C STRICT
   4.965 -  AS '$libdir/latlon-v0001', 'pgl_gist_union';
   4.966 -
   4.967 -CREATE FUNCTION pgl_gist_compress_epoint(internal)
   4.968 -  RETURNS internal
   4.969 -  LANGUAGE C STRICT
   4.970 -  AS '$libdir/latlon-v0001', 'pgl_gist_compress_epoint';
   4.971 -
   4.972 -CREATE FUNCTION pgl_gist_compress_ecircle(internal)
   4.973 -  RETURNS internal
   4.974 -  LANGUAGE C STRICT
   4.975 -  AS '$libdir/latlon-v0001', 'pgl_gist_compress_ecircle';
   4.976 -
   4.977 -CREATE FUNCTION pgl_gist_compress_ecluster(internal)
   4.978 -  RETURNS internal
   4.979 -  LANGUAGE C STRICT
   4.980 -  AS '$libdir/latlon-v0001', 'pgl_gist_compress_ecluster';
   4.981 -
   4.982 -CREATE FUNCTION pgl_gist_decompress(internal)
   4.983 -  RETURNS internal
   4.984 -  LANGUAGE C STRICT
   4.985 -  AS '$libdir/latlon-v0001', 'pgl_gist_decompress';
   4.986 -
   4.987 -CREATE FUNCTION pgl_gist_penalty(internal, internal, internal)
   4.988 -  RETURNS internal
   4.989 -  LANGUAGE C STRICT
   4.990 -  AS '$libdir/latlon-v0001', 'pgl_gist_penalty';
   4.991 -
   4.992 -CREATE FUNCTION pgl_gist_picksplit(internal, internal)
   4.993 -  RETURNS internal
   4.994 -  LANGUAGE C STRICT
   4.995 -  AS '$libdir/latlon-v0001', 'pgl_gist_picksplit';
   4.996 -
   4.997 -CREATE FUNCTION pgl_gist_same(internal, internal, internal)
   4.998 -  RETURNS internal
   4.999 -  LANGUAGE C STRICT
  4.1000 -  AS '$libdir/latlon-v0001', 'pgl_gist_same';
  4.1001 -
  4.1002 -CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
  4.1003 -  RETURNS internal
  4.1004 -  LANGUAGE C STRICT
  4.1005 -  AS '$libdir/latlon-v0001', 'pgl_gist_distance';
  4.1006 -
  4.1007 -CREATE OPERATOR CLASS epoint_ops
  4.1008 -  DEFAULT FOR TYPE epoint USING gist AS
  4.1009 -  OPERATOR 11 = ,
  4.1010 -  OPERATOR 22 && (epoint, ebox),
  4.1011 -  OPERATOR 23 && (epoint, ecircle),
  4.1012 -  OPERATOR 24 && (epoint, ecluster),
  4.1013 -  OPERATOR 31 <-> (epoint, epoint) FOR ORDER BY float_ops,
  4.1014 -  OPERATOR 33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
  4.1015 -  OPERATOR 34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
  4.1016 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  4.1017 -  FUNCTION 2 pgl_gist_union(internal, internal),
  4.1018 -  FUNCTION 3 pgl_gist_compress_epoint(internal),
  4.1019 -  FUNCTION 4 pgl_gist_decompress(internal),
  4.1020 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  4.1021 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  4.1022 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  4.1023 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  4.1024 -  STORAGE ekey_point;
  4.1025 -
  4.1026 -CREATE OPERATOR CLASS ecircle_ops
  4.1027 -  DEFAULT FOR TYPE ecircle USING gist AS
  4.1028 -  OPERATOR 13 = ,
  4.1029 -  OPERATOR 21 && (ecircle, epoint),
  4.1030 -  OPERATOR 23 && (ecircle, ecircle),
  4.1031 -  OPERATOR 24 && (ecircle, ecluster),
  4.1032 -  OPERATOR 31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
  4.1033 -  OPERATOR 33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
  4.1034 -  OPERATOR 34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
  4.1035 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  4.1036 -  FUNCTION 2 pgl_gist_union(internal, internal),
  4.1037 -  FUNCTION 3 pgl_gist_compress_ecircle(internal),
  4.1038 -  FUNCTION 4 pgl_gist_decompress(internal),
  4.1039 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  4.1040 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  4.1041 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  4.1042 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  4.1043 -  STORAGE ekey_area;
  4.1044 -
  4.1045 -CREATE OPERATOR CLASS ecluster_ops
  4.1046 -  DEFAULT FOR TYPE ecluster USING gist AS
  4.1047 -  OPERATOR 21 && (ecluster, epoint),
  4.1048 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  4.1049 -  FUNCTION 2 pgl_gist_union(internal, internal),
  4.1050 -  FUNCTION 3 pgl_gist_compress_ecluster(internal),
  4.1051 -  FUNCTION 4 pgl_gist_decompress(internal),
  4.1052 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  4.1053 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  4.1054 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  4.1055 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  4.1056 -  STORAGE ekey_area;
  4.1057 -
  4.1058 -
  4.1059 ----------------------
  4.1060 --- alias functions --
  4.1061 ----------------------
  4.1062 -
  4.1063 -CREATE FUNCTION distance(epoint, epoint)
  4.1064 -  RETURNS float8
  4.1065 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  4.1066 -
  4.1067 -CREATE FUNCTION distance(ecluster, epoint)
  4.1068 -  RETURNS float8
  4.1069 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  4.1070 -
  4.1071 -CREATE FUNCTION distance_within(epoint, epoint, float8)
  4.1072 -  RETURNS boolean
  4.1073 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  4.1074 -
  4.1075 -CREATE FUNCTION distance_within(ecluster, epoint, float8)
  4.1076 -  RETURNS boolean
  4.1077 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  4.1078 -
  4.1079 -
  4.1080 ---------------------------------
  4.1081 --- other data storage formats --
  4.1082 ---------------------------------
  4.1083 -
  4.1084 -CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint_lonlat')
  4.1085 -  RETURNS epoint
  4.1086 -  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
  4.1087 -    DECLARE
  4.1088 -      "result" epoint;
  4.1089 -    BEGIN
  4.1090 -      IF $3 = 'epoint_lonlat' THEN
  4.1091 -        -- avoid dynamic command execution for better performance
  4.1092 -        RETURN epoint($2, $1);
  4.1093 -      END IF;
  4.1094 -      IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN
  4.1095 -        -- avoid dynamic command execution for better performance
  4.1096 -        RETURN epoint($1, $2);
  4.1097 -      END IF;
  4.1098 -      EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2;
  4.1099 -      RETURN "result";
  4.1100 -    END;
  4.1101 -  $$;
  4.1102 -
  4.1103 -CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat')
  4.1104 -  RETURNS epoint
  4.1105 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  4.1106 -    SELECT CASE
  4.1107 -    WHEN $1->>'type' = 'Point' THEN
  4.1108 -      coords_to_epoint(
  4.1109 -        ($1->'coordinates'->>1)::float8,
  4.1110 -        ($1->'coordinates'->>0)::float8,
  4.1111 -        $2
  4.1112 -      )
  4.1113 -    WHEN $1->>'type' = 'Feature' THEN
  4.1114 -      GeoJSON_to_epoint($1->'geometry', $2)
  4.1115 -    ELSE
  4.1116 -      NULL
  4.1117 -    END
  4.1118 -  $$;
  4.1119 -
  4.1120 -CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat')
  4.1121 -  RETURNS ecluster
  4.1122 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  4.1123 -    SELECT CASE $1->>'type'
  4.1124 -    WHEN 'Point' THEN
  4.1125 -      coords_to_epoint(
  4.1126 -        ($1->'coordinates'->>1)::float8,
  4.1127 -        ($1->'coordinates'->>0)::float8,
  4.1128 -        $2
  4.1129 -      )::ecluster
  4.1130 -    WHEN 'MultiPoint' THEN
  4.1131 -      ( SELECT ecluster_create_multipoint(array_agg(
  4.1132 -          coords_to_epoint(
  4.1133 -            ("coord"->>1)::float8,
  4.1134 -            ("coord"->>0)::float8,
  4.1135 -            $2
  4.1136 -          )
  4.1137 -        ))
  4.1138 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  4.1139 -      )
  4.1140 -    WHEN 'LineString' THEN
  4.1141 -      ( SELECT ecluster_create_path(array_agg(
  4.1142 -          coords_to_epoint(
  4.1143 -            ("coord"->>1)::float8,
  4.1144 -            ("coord"->>0)::float8,
  4.1145 -            $2
  4.1146 -          )
  4.1147 -        ))
  4.1148 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  4.1149 -      )
  4.1150 -    WHEN 'MultiLineString' THEN
  4.1151 -      ( SELECT ecluster_concat(array_agg(
  4.1152 -          ( SELECT ecluster_create_path(array_agg(
  4.1153 -              coords_to_epoint(
  4.1154 -                ("coord"->>1)::float8,
  4.1155 -                ("coord"->>0)::float8,
  4.1156 -                $2
  4.1157 -              )
  4.1158 -            ))
  4.1159 -            FROM jsonb_array_elements("coord_array") AS "coord"
  4.1160 -          )
  4.1161 -        ))
  4.1162 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  4.1163 -      )
  4.1164 -    WHEN 'Polygon' THEN
  4.1165 -      ( SELECT ecluster_concat(array_agg(
  4.1166 -          ( SELECT ecluster_create_polygon(array_agg(
  4.1167 -              coords_to_epoint(
  4.1168 -                ("coord"->>1)::float8,
  4.1169 -                ("coord"->>0)::float8,
  4.1170 -                $2
  4.1171 -              )
  4.1172 -            ))
  4.1173 -            FROM jsonb_array_elements("coord_array") AS "coord"
  4.1174 -          )
  4.1175 -        ))
  4.1176 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  4.1177 -      )
  4.1178 -    WHEN 'MultiPolygon' THEN
  4.1179 -      ( SELECT ecluster_concat(array_agg(
  4.1180 -          ( SELECT ecluster_concat(array_agg(
  4.1181 -              ( SELECT ecluster_create_polygon(array_agg(
  4.1182 -                  coords_to_epoint(
  4.1183 -                    ("coord"->>1)::float8,
  4.1184 -                    ("coord"->>0)::float8,
  4.1185 -                    $2
  4.1186 -                  )
  4.1187 -                ))
  4.1188 -                FROM jsonb_array_elements("coord_array") AS "coord"
  4.1189 -              )
  4.1190 -            ))
  4.1191 -            FROM jsonb_array_elements("coord_array_array") AS "coord_array"
  4.1192 -          )
  4.1193 -        ))
  4.1194 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array"
  4.1195 -      )
  4.1196 -    WHEN 'Feature' THEN
  4.1197 -      GeoJSON_to_ecluster($1->'geometry', $2)
  4.1198 -    WHEN 'FeatureCollection' THEN
  4.1199 -      ( SELECT ecluster_concat(array_agg(
  4.1200 -          GeoJSON_to_ecluster("feature", $2)
  4.1201 -        ))
  4.1202 -        FROM jsonb_array_elements($1->'features') AS "feature"
  4.1203 -      )
  4.1204 -    ELSE
  4.1205 -      NULL
  4.1206 -    END
  4.1207 -  $$;
  4.1208 -
     5.1 --- a/latlon-v0001.c	Mon Aug 22 21:52:32 2016 +0200
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,2710 +0,0 @@
     5.4 -
     5.5 -/*-------------*
     5.6 - *  C prelude  *
     5.7 - *-------------*/
     5.8 -
     5.9 -#include "postgres.h"
    5.10 -#include "fmgr.h"
    5.11 -#include "libpq/pqformat.h"
    5.12 -#include "access/gist.h"
    5.13 -#include "access/stratnum.h"
    5.14 -#include "utils/array.h"
    5.15 -#include <math.h>
    5.16 -
    5.17 -#ifdef PG_MODULE_MAGIC
    5.18 -PG_MODULE_MAGIC;
    5.19 -#endif
    5.20 -
    5.21 -#if INT_MAX < 2147483647
    5.22 -#error Expected int type to be at least 32 bit wide
    5.23 -#endif
    5.24 -
    5.25 -
    5.26 -/*---------------------------------*
    5.27 - *  distance calculation on earth  *
    5.28 - *  (using WGS-84 spheroid)        *
    5.29 - *---------------------------------*/
    5.30 -
    5.31 -/*  WGS-84 spheroid with following parameters:
    5.32 -    semi-major axis  a = 6378137
    5.33 -    semi-minor axis  b = a * (1 - 1/298.257223563)
    5.34 -    estimated diameter = 2 * (2*a+b)/3
    5.35 -*/
    5.36 -#define PGL_SPHEROID_A 6378137.0            /* semi major axis */
    5.37 -#define PGL_SPHEROID_F (1.0/298.257223563)  /* flattening */
    5.38 -#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F))
    5.39 -#define PGL_EPS2       ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \
    5.40 -                           PGL_SPHEROID_B * PGL_SPHEROID_B ) / \
    5.41 -                         ( PGL_SPHEROID_A * PGL_SPHEROID_A ) )
    5.42 -#define PGL_SUBEPS2    (1.0-PGL_EPS2)
    5.43 -#define PGL_DIAMETER   ((4.0*PGL_SPHEROID_A + 2.0*PGL_SPHEROID_B) / 3.0)
    5.44 -#define PGL_SCALE      (PGL_SPHEROID_A / PGL_DIAMETER)  /* semi-major ref. */
    5.45 -#define PGL_FADELIMIT  (PGL_DIAMETER * M_PI / 6.0)      /* 1/6 circumference */
    5.46 -#define PGL_MAXDIST    (PGL_DIAMETER * M_PI / 2.0)      /* maximum distance */
    5.47 -
    5.48 -/* calculate distance between two points on earth (given in degrees) */
    5.49 -static inline double pgl_distance(
    5.50 -  double lat1, double lon1, double lat2, double lon2
    5.51 -) {
    5.52 -  float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin;
    5.53 -  float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t;
    5.54 -  /* normalize delta longitude (lon2 > 0 && lon1 = 0) */
    5.55 -  /* lon1 = 0 (not used anymore) */
    5.56 -  lon2 = fabs(lon2-lon1);
    5.57 -  /* convert to radians (first divide, then multiply) */
    5.58 -  lat1 = (lat1 / 180.0) * M_PI;
    5.59 -  lat2 = (lat2 / 180.0) * M_PI;
    5.60 -  lon2 = (lon2 / 180.0) * M_PI;
    5.61 -  /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point
    5.62 -     operations (lon2 >= lon1 is already ensured in a previous step) */
    5.63 -  if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; }
    5.64 -  /* calculate 3d coordinates on scaled ellipsoid which has an average diameter
    5.65 -     of 1.0 */
    5.66 -  lat1cos = cos(lat1); lat1sin = sin(lat1);
    5.67 -  lat2cos = cos(lat2); lat2sin = sin(lat2);
    5.68 -  lon2cos = cos(lon2); lon2sin = sin(lon2);
    5.69 -  nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin);
    5.70 -  nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
    5.71 -  x1 = nphi1 * lat1cos;
    5.72 -  z1 = nphi1 * PGL_SUBEPS2 * lat1sin;
    5.73 -  x2 = nphi2 * lat2cos * lon2cos;
    5.74 -  y2 = nphi2 * lat2cos * lon2sin;
    5.75 -  z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
    5.76 -  /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */
    5.77 -  g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
    5.78 -  /* convert tunnel distance through scaled ellipsoid to approximated surface
    5.79 -     distance on original ellipsoid */
    5.80 -  if (g > 1.0) g = 1.0;
    5.81 -  s = PGL_DIAMETER * asin(g);
    5.82 -  /* return result only if small enough to be precise (less than 1/3 of
    5.83 -     maximum possible distance) */
    5.84 -  if (s <= PGL_FADELIMIT) return s;
    5.85 -  /* determine antipodal point of second point (i.e. mirror second point) */
    5.86 -  lat2 = -lat2; lon2 = lon2 - M_PI;
    5.87 -  lat2cos = cos(lat2); lat2sin = sin(lat2);
    5.88 -  lon2cos = cos(lon2); lon2sin = sin(lon2);
    5.89 -  /* calculate 3d coordinates of antipodal point on scaled ellipsoid */
    5.90 -  nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
    5.91 -  x2 = nphi2 * lat2cos * lon2cos;
    5.92 -  y2 = nphi2 * lat2cos * lon2sin;
    5.93 -  z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
    5.94 -  /* calculate tunnel distance to antipodal point through scaled ellipsoid */
    5.95 -  g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
    5.96 -  /* convert tunnel distance to antipodal point through scaled ellipsoid to
    5.97 -     approximated surface distance to antipodal point on original ellipsoid */
    5.98 -  if (g > 1.0) g = 1.0;
    5.99 -  t = PGL_DIAMETER * asin(g);
   5.100 -  /* surface distance between original points can now be approximated by
   5.101 -     substracting antipodal distance from maximum possible distance;
   5.102 -     return result only if small enough (less than 1/3 of maximum possible
   5.103 -     distance) */
   5.104 -  if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t;
   5.105 -  /* otherwise crossfade direct and antipodal result to ensure monotonicity */
   5.106 -  return (
   5.107 -    (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) /
   5.108 -    (s + t - 2*PGL_FADELIMIT)
   5.109 -  );
   5.110 -}
   5.111 -
   5.112 -/* finite distance that can not be reached on earth */
   5.113 -#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST)
   5.114 -
   5.115 -
   5.116 -/*--------------------------------*
   5.117 - *  simple geographic data types  *
   5.118 - *--------------------------------*/
   5.119 -
   5.120 -/* point on earth given by latitude and longitude in degrees */
   5.121 -/* (type "epoint" in SQL) */
   5.122 -typedef struct {
   5.123 -  double lat;  /* between  -90 and  90 (both inclusive) */
   5.124 -  double lon;  /* between -180 and 180 (both inclusive) */
   5.125 -} pgl_point;
   5.126 -
   5.127 -/* box delimited by two parallels and two meridians (all in degrees) */
   5.128 -/* (type "ebox" in SQL) */
   5.129 -typedef struct {
   5.130 -  double lat_min;  /* between  -90 and  90 (both inclusive) */
   5.131 -  double lat_max;  /* between  -90 and  90 (both inclusive) */
   5.132 -  double lon_min;  /* between -180 and 180 (both inclusive) */
   5.133 -  double lon_max;  /* between -180 and 180 (both inclusive) */
   5.134 -  /* if lat_min > lat_max, then box is empty */
   5.135 -  /* if lon_min > lon_max, then 180th meridian is crossed */
   5.136 -} pgl_box;
   5.137 -
   5.138 -/* circle on earth surface (for radial searches with fixed radius) */
   5.139 -/* (type "ecircle" in SQL) */
   5.140 -typedef struct {
   5.141 -  pgl_point center;
   5.142 -  double radius; /* positive (including +0 but excluding -0), or -INFINITY */
   5.143 -  /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point),
   5.144 -     zero radius (0) denotes a single point,
   5.145 -     a finite radius (0 < radius < INFINITY) denotes a filled circle, and
   5.146 -     a radius of INFINITY is valid and means complete coverage of earth. */
   5.147 -} pgl_circle;
   5.148 -
   5.149 -
   5.150 -/*----------------------------------*
   5.151 - *  geographic "cluster" data type  *
   5.152 - *----------------------------------*/
   5.153 -
   5.154 -/* A cluster is a collection of points, paths, outlines, and polygons. If two
   5.155 -   polygons in a cluster overlap, the area covered by both polygons does not
   5.156 -   belong to the cluster. This way, a cluster can be used to describe complex
   5.157 -   shapes like polygons with holes. Outlines are non-filled polygons. Paths are
   5.158 -   open by default (i.e. the last point in the list is not connected with the
   5.159 -   first point in the list). Note that each outline or polygon in a cluster
   5.160 -   must cover a longitude range of less than 180 degrees to avoid ambiguities.
   5.161 -   Areas which are larger may be split into multiple polygons. */
   5.162 -
   5.163 -/* maximum number of points in a cluster */
   5.164 -/* (limited to avoid integer overflows, e.g. when allocating memory) */
   5.165 -#define PGL_CLUSTER_MAXPOINTS 16777216
   5.166 -
   5.167 -/* types of cluster entries */
   5.168 -#define PGL_ENTRY_POINT   1  /* a point */
   5.169 -#define PGL_ENTRY_PATH    2  /* a path from first point to last point */
   5.170 -#define PGL_ENTRY_OUTLINE 3  /* a non-filled polygon with given vertices */
   5.171 -#define PGL_ENTRY_POLYGON 4  /* a filled polygon with given vertices */
   5.172 -
   5.173 -/* Entries of a cluster are described by two different structs: pgl_newentry
   5.174 -   and pgl_entry. The first is used only during construction of a cluster, the
   5.175 -   second is used in all other cases (e.g. when reading clusters from the
   5.176 -   database, performing operations, etc). */
   5.177 -
   5.178 -/* entry for new geographic cluster during construction of that cluster */
   5.179 -typedef struct {
   5.180 -  int32_t entrytype;
   5.181 -  int32_t npoints;
   5.182 -  pgl_point *points;  /* pointer to an array of points (pgl_point) */
   5.183 -} pgl_newentry;
   5.184 -
   5.185 -/* entry of geographic cluster */
   5.186 -typedef struct {
   5.187 -  int32_t entrytype;  /* type of entry: point, path, outline, polygon */
   5.188 -  int32_t npoints;    /* number of stored points (set to 1 for point entry) */
   5.189 -  int32_t offset;     /* offset of pgl_point array from cluster base address */
   5.190 -  /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */
   5.191 -} pgl_entry;
   5.192 -
   5.193 -/* geographic cluster which is a collection of points, (open) paths, polygons,
   5.194 -   and outlines (non-filled polygons) */
   5.195 -typedef struct {
   5.196 -  char header[VARHDRSZ];  /* PostgreSQL header for variable size data types */
   5.197 -  int32_t nentries;       /* number of stored points */
   5.198 -  pgl_circle bounding;    /* bounding circle */
   5.199 -  /* Note: bounding circle ensures alignment of pgl_cluster for points */
   5.200 -  pgl_entry entries[FLEXIBLE_ARRAY_MEMBER];  /* var-length data */
   5.201 -} pgl_cluster;
   5.202 -
   5.203 -/* macro to determine memory alignment of points */
   5.204 -/* (needed to store pgl_point array after entries in pgl_cluster) */
   5.205 -typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment;
   5.206 -#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned)
   5.207 -
   5.208 -/* macro to extract a pointer to the array of points of a cluster entry */
   5.209 -#define PGL_ENTRY_POINTS(cluster, idx) \
   5.210 -  ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset))
   5.211 -
   5.212 -/* convert pgl_newentry array to pgl_cluster */
   5.213 -static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) {
   5.214 -  int i;              /* index of current entry */
   5.215 -  int npoints = 0;    /* number of points in whole cluster */
   5.216 -  int entry_npoints;  /* number of points in current entry */
   5.217 -  int points_offset = PGL_POINT_ALIGNMENT * (
   5.218 -    ( offsetof(pgl_cluster, entries) +
   5.219 -      nentries * sizeof(pgl_entry) +
   5.220 -      PGL_POINT_ALIGNMENT - 1
   5.221 -    ) / PGL_POINT_ALIGNMENT
   5.222 -  );  /* offset of pgl_point array from base address (considering alignment) */
   5.223 -  pgl_cluster *cluster;  /* new cluster to be returned */
   5.224 -  /* determine total number of points */
   5.225 -  for (i=0; i<nentries; i++) npoints += entries[i].npoints;
   5.226 -  /* allocate memory for cluster (including entries and points) */
   5.227 -  cluster = palloc(points_offset + npoints * sizeof(pgl_point));
   5.228 -  /* re-count total number of points to determine offset for each entry */
   5.229 -  npoints = 0;
   5.230 -  /* copy entries and points */
   5.231 -  for (i=0; i<nentries; i++) {
   5.232 -    /* determine number of points in entry */
   5.233 -    entry_npoints = entries[i].npoints;
   5.234 -    /* copy entry */
   5.235 -    cluster->entries[i].entrytype = entries[i].entrytype;
   5.236 -    cluster->entries[i].npoints = entry_npoints;
   5.237 -    /* calculate offset (in bytes) of pgl_point array */
   5.238 -    cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point);
   5.239 -    /* copy points */
   5.240 -    memcpy(
   5.241 -      PGL_ENTRY_POINTS(cluster, i),
   5.242 -      entries[i].points,
   5.243 -      entry_npoints * sizeof(pgl_point)
   5.244 -    );
   5.245 -    /* update total number of points processed */
   5.246 -    npoints += entry_npoints;
   5.247 -  }
   5.248 -  /* set number of entries in cluster */
   5.249 -  cluster->nentries = nentries;
   5.250 -  /* set PostgreSQL header for variable sized data */
   5.251 -  SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point));
   5.252 -  /* return newly created cluster */
   5.253 -  return cluster;
   5.254 -}
   5.255 -
   5.256 -
   5.257 -/*----------------------------------------*
   5.258 - *  C functions on geographic data types  *
   5.259 - *----------------------------------------*/
   5.260 -
   5.261 -/* round latitude or longitude to 12 digits after decimal point */
   5.262 -static inline double pgl_round(double val) {
   5.263 -  return round(val * 1e12) / 1e12;
   5.264 -}
   5.265 -
   5.266 -/* compare two points */
   5.267 -/* (equality when same point on earth is described, otherwise an arbitrary
   5.268 -   linear order) */
   5.269 -static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) {
   5.270 -  double lon1, lon2;  /* modified longitudes for special cases */
   5.271 -  /* use latitude as first ordering criterion */
   5.272 -  if (point1->lat < point2->lat) return -1;
   5.273 -  if (point1->lat > point2->lat) return 1;
   5.274 -  /* determine modified longitudes (considering special case of poles and
   5.275 -     180th meridian which can be described as W180 or E180) */
   5.276 -  if (point1->lat == -90 || point1->lat == 90) lon1 = 0;
   5.277 -  else if (point1->lon == 180) lon1 = -180;
   5.278 -  else lon1 = point1->lon;
   5.279 -  if (point2->lat == -90 || point2->lat == 90) lon2 = 0;
   5.280 -  else if (point2->lon == 180) lon2 = -180;
   5.281 -  else lon2 = point2->lon;
   5.282 -  /* use (modified) longitude as secondary ordering criterion */
   5.283 -  if (lon1 < lon2) return -1;
   5.284 -  if (lon1 > lon2) return 1;
   5.285 -  /* no difference found, points are equal */
   5.286 -  return 0;
   5.287 -}
   5.288 -
   5.289 -/* compare two boxes */
   5.290 -/* (equality when same box on earth is described, otherwise an arbitrary linear
   5.291 -   order) */
   5.292 -static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) {
   5.293 -  /* two empty boxes are equal, and an empty box is always considered "less
   5.294 -     than" a non-empty box */
   5.295 -  if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1;
   5.296 -  if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0;
   5.297 -  if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1;
   5.298 -  /* use southern border as first ordering criterion */
   5.299 -  if (box1->lat_min < box2->lat_min) return -1;
   5.300 -  if (box1->lat_min > box2->lat_min) return 1;
   5.301 -  /* use northern border as second ordering criterion */
   5.302 -  if (box1->lat_max < box2->lat_max) return -1;
   5.303 -  if (box1->lat_max > box2->lat_max) return 1;
   5.304 -  /* use western border as third ordering criterion */
   5.305 -  if (box1->lon_min < box2->lon_min) return -1;
   5.306 -  if (box1->lon_min > box2->lon_min) return 1;
   5.307 -  /* use eastern border as fourth ordering criterion */
   5.308 -  if (box1->lon_max < box2->lon_max) return -1;
   5.309 -  if (box1->lon_max > box2->lon_max) return 1;
   5.310 -  /* no difference found, boxes are equal */
   5.311 -  return 0;
   5.312 -}
   5.313 -
   5.314 -/* compare two circles */
   5.315 -/* (equality when same circle on earth is described, otherwise an arbitrary
   5.316 -   linear order) */
   5.317 -static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) {
   5.318 -  /* two circles with same infinite radius (positive or negative infinity) are
   5.319 -     considered equal independently of center point */
   5.320 -  if (
   5.321 -    !isfinite(circle1->radius) && !isfinite(circle2->radius) &&
   5.322 -    circle1->radius == circle2->radius
   5.323 -  ) return 0;
   5.324 -  /* use radius as first ordering criterion */
   5.325 -  if (circle1->radius < circle2->radius) return -1;
   5.326 -  if (circle1->radius > circle2->radius) return 1;
   5.327 -  /* use center point as secondary ordering criterion */
   5.328 -  return pgl_point_cmp(&(circle1->center), &(circle2->center));
   5.329 -}
   5.330 -
   5.331 -/* set box to empty box*/
   5.332 -static void pgl_box_set_empty(pgl_box *box) {
   5.333 -  box->lat_min = INFINITY;
   5.334 -  box->lat_max = -INFINITY;
   5.335 -  box->lon_min = 0;
   5.336 -  box->lon_max = 0;
   5.337 -}
   5.338 -
   5.339 -/* check if point is inside a box */
   5.340 -static bool pgl_point_in_box(pgl_point *point, pgl_box *box) {
   5.341 -  return (
   5.342 -    point->lat >= box->lat_min && point->lat <= box->lat_max && (
   5.343 -      (box->lon_min > box->lon_max) ? (
   5.344 -        /* box crosses 180th meridian */
   5.345 -        point->lon >= box->lon_min || point->lon <= box->lon_max
   5.346 -      ) : (
   5.347 -        /* box does not cross the 180th meridian */
   5.348 -        point->lon >= box->lon_min && point->lon <= box->lon_max
   5.349 -      )
   5.350 -    )
   5.351 -  );
   5.352 -}
   5.353 -
   5.354 -/* check if two boxes overlap */
   5.355 -static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) {
   5.356 -  return (
   5.357 -    box2->lat_max >= box2->lat_min &&  /* ensure box2 is not empty */
   5.358 -    ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) &&
   5.359 -    ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && (
   5.360 -      (
   5.361 -        /* check if one and only one box crosses the 180th meridian */
   5.362 -        ((box1->lon_min > box1->lon_max) ? 1 : 0) ^
   5.363 -        ((box2->lon_min > box2->lon_max) ? 1 : 0)
   5.364 -      ) ? (
   5.365 -        /* exactly one box crosses the 180th meridian */
   5.366 -        box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min ||
   5.367 -        box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max
   5.368 -      ) : (
   5.369 -        /* no box or both boxes cross the 180th meridian */
   5.370 -        (
   5.371 -          (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) &&
   5.372 -          (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max)
   5.373 -        ) ||
   5.374 -        /* handle W180 == E180 */
   5.375 -        ( box1->lon_min == -180 && box2->lon_max == 180 ) ||
   5.376 -        ( box2->lon_min == -180 && box1->lon_max == 180 )
   5.377 -      )
   5.378 -    )
   5.379 -  );
   5.380 -}
   5.381 -
   5.382 -/* check unambiguousness of east/west orientation of cluster entries and set
   5.383 -   bounding circle of cluster */
   5.384 -static bool pgl_finalize_cluster(pgl_cluster *cluster) {
   5.385 -  int i, j;                 /* i: index of entry, j: index of point in entry */
   5.386 -  int npoints;              /* number of points in entry */
   5.387 -  int total_npoints = 0;    /* total number of points in cluster */
   5.388 -  pgl_point *points;        /* points in entry */
   5.389 -  int lon_dir;              /* first point of entry west (-1) or east (+1) */
   5.390 -  double lon_break = 0;     /* antipodal longitude of first point in entry */
   5.391 -  double lon_min, lon_max;  /* covered longitude range of entry */
   5.392 -  double value;             /* temporary variable */
   5.393 -  /* reset bounding circle center to empty circle at 0/0 coordinates */
   5.394 -  cluster->bounding.center.lat = 0;
   5.395 -  cluster->bounding.center.lon = 0;
   5.396 -  cluster->bounding.radius = -INFINITY;
   5.397 -  /* if cluster is not empty */
   5.398 -  if (cluster->nentries != 0) {
   5.399 -    /* iterate over all cluster entries and ensure they each cover a longitude
   5.400 -       range less than 180 degrees */
   5.401 -    for (i=0; i<cluster->nentries; i++) {
   5.402 -      /* get properties of entry */
   5.403 -      npoints = cluster->entries[i].npoints;
   5.404 -      points = PGL_ENTRY_POINTS(cluster, i);
   5.405 -      /* get longitude of first point of entry */
   5.406 -      value = points[0].lon;
   5.407 -      /* initialize lon_min and lon_max with longitude of first point */
   5.408 -      lon_min = value;
   5.409 -      lon_max = value;
   5.410 -      /* determine east/west orientation of first point and calculate antipodal
   5.411 -         longitude (Note: rounding required here) */
   5.412 -      if      (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); }
   5.413 -      else if (value > 0) { lon_dir =  1; lon_break = pgl_round(value - 180); }
   5.414 -      else lon_dir = 0;
   5.415 -      /* iterate over all other points in entry */
   5.416 -      for (j=1; j<npoints; j++) {
   5.417 -        /* consider longitude wrap-around */
   5.418 -        value = points[j].lon;
   5.419 -        if      (lon_dir<0 && value>lon_break) value = pgl_round(value - 360);
   5.420 -        else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360);
   5.421 -        /* update lon_min and lon_max */
   5.422 -        if      (value < lon_min) lon_min = value;
   5.423 -        else if (value > lon_max) lon_max = value;
   5.424 -        /* return false if 180 degrees or more are covered */
   5.425 -        if (lon_max - lon_min >= 180) return false;
   5.426 -      }
   5.427 -    }
   5.428 -    /* iterate over all points of all entries and calculate arbitrary center
   5.429 -       point for bounding circle (best if center point minimizes the radius,
   5.430 -       but some error is allowed here) */
   5.431 -    for (i=0; i<cluster->nentries; i++) {
   5.432 -      /* get properties of entry */
   5.433 -      npoints = cluster->entries[i].npoints;
   5.434 -      points = PGL_ENTRY_POINTS(cluster, i);
   5.435 -      /* check if first entry */
   5.436 -      if (i==0) {
   5.437 -        /* get longitude of first point of first entry in whole cluster */
   5.438 -        value = points[0].lon;
   5.439 -        /* initialize lon_min and lon_max with longitude of first point of
   5.440 -           first entry in whole cluster (used to determine if whole cluster
   5.441 -           covers a longitude range of 180 degrees or more) */
   5.442 -        lon_min = value;
   5.443 -        lon_max = value;
   5.444 -        /* determine east/west orientation of first point and calculate
   5.445 -           antipodal longitude (Note: rounding not necessary here) */
   5.446 -        if      (value < 0) { lon_dir = -1; lon_break = value + 180; }
   5.447 -        else if (value > 0) { lon_dir =  1; lon_break = value - 180; }
   5.448 -        else lon_dir = 0;
   5.449 -      }
   5.450 -      /* iterate over all points in entry */
   5.451 -      for (j=0; j<npoints; j++) {
   5.452 -        /* longitude wrap-around (Note: rounding not necessary here) */
   5.453 -        value = points[j].lon;
   5.454 -        if      (lon_dir < 0 && value > lon_break) value -= 360;
   5.455 -        else if (lon_dir > 0 && value < lon_break) value += 360;
   5.456 -        if      (value < lon_min) lon_min = value;
   5.457 -        else if (value > lon_max) lon_max = value;
   5.458 -        /* set bounding circle to cover whole earth if more than 180 degrees
   5.459 -           are covered */
   5.460 -        if (lon_max - lon_min >= 180) {
   5.461 -          cluster->bounding.center.lat = 0;
   5.462 -          cluster->bounding.center.lon = 0;
   5.463 -          cluster->bounding.radius = INFINITY;
   5.464 -          return true;
   5.465 -        }
   5.466 -        /* add point to bounding circle center (for average calculation) */
   5.467 -        cluster->bounding.center.lat += points[j].lat;
   5.468 -        cluster->bounding.center.lon += value;
   5.469 -      }
   5.470 -      /* count total number of points */
   5.471 -      total_npoints += npoints;
   5.472 -    }
   5.473 -    /* determine average latitude and longitude of cluster */
   5.474 -    cluster->bounding.center.lat /= total_npoints;
   5.475 -    cluster->bounding.center.lon /= total_npoints;
   5.476 -    /* normalize longitude of center of cluster bounding circle */
   5.477 -    if (cluster->bounding.center.lon < -180) {
   5.478 -      cluster->bounding.center.lon += 360;
   5.479 -    }
   5.480 -    else if (cluster->bounding.center.lon > 180) {
   5.481 -      cluster->bounding.center.lon -= 360;
   5.482 -    }
   5.483 -    /* round bounding circle center (useful if it is used by other functions) */
   5.484 -    cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat);
   5.485 -    cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon);
   5.486 -    /* calculate radius of bounding circle */
   5.487 -    for (i=0; i<cluster->nentries; i++) {
   5.488 -      npoints = cluster->entries[i].npoints;
   5.489 -      points = PGL_ENTRY_POINTS(cluster, i);
   5.490 -      for (j=0; j<npoints; j++) {
   5.491 -        value = pgl_distance(
   5.492 -          cluster->bounding.center.lat, cluster->bounding.center.lon,
   5.493 -          points[j].lat, points[j].lon
   5.494 -        );
   5.495 -        if (value > cluster->bounding.radius) cluster->bounding.radius = value;
   5.496 -      }
   5.497 -    }
   5.498 -  }
   5.499 -  /* return true (east/west orientation is unambiguous) */
   5.500 -  return true;
   5.501 -}
   5.502 -
   5.503 -/* check if point is inside cluster */
   5.504 -static bool pgl_point_in_cluster(pgl_point *point, pgl_cluster *cluster) {
   5.505 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   5.506 -  int entrytype;         /* type of entry */
   5.507 -  int npoints;           /* number of points in entry */
   5.508 -  pgl_point *points;     /* array of points in entry */
   5.509 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   5.510 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   5.511 -  double lat0 = point->lat;  /* latitude of point */
   5.512 -  double lon0;           /* (adjusted) longitude of point */
   5.513 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   5.514 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   5.515 -  double lon;            /* longitude of intersection */
   5.516 -  int counter = 0;       /* counter for intersections east of point */
   5.517 -  /* points outside bounding circle are always assumed to be non-overlapping */
   5.518 -  /* (necessary for consistent table and index scans) */
   5.519 -  if (
   5.520 -    pgl_distance(
   5.521 -      point->lat, point->lon,
   5.522 -      cluster->bounding.center.lat, cluster->bounding.center.lon
   5.523 -    ) > cluster->bounding.radius
   5.524 -  ) return false;
   5.525 -  /* iterate over all entries */
   5.526 -  for (i=0; i<cluster->nentries; i++) {
   5.527 -    /* get properties of entry */
   5.528 -    entrytype = cluster->entries[i].entrytype;
   5.529 -    npoints = cluster->entries[i].npoints;
   5.530 -    points = PGL_ENTRY_POINTS(cluster, i);
   5.531 -    /* determine east/west orientation of first point of entry and calculate
   5.532 -       antipodal longitude */
   5.533 -    lon_break = points[0].lon;
   5.534 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   5.535 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   5.536 -    else lon_dir = 0;
   5.537 -    /* get longitude of point */
   5.538 -    lon0 = point->lon;
   5.539 -    /* consider longitude wrap-around for point */
   5.540 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360);
   5.541 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360);
   5.542 -    /* iterate over all edges and vertices */
   5.543 -    for (j=0; j<npoints; j++) {
   5.544 -      /* return true if point is on vertex of polygon */
   5.545 -      if (pgl_point_cmp(point, &(points[j])) == 0) return true;
   5.546 -      /* calculate index of next vertex */
   5.547 -      k = (j+1) % npoints;
   5.548 -      /* skip last edge unless entry is (closed) outline or polygon */
   5.549 -      if (
   5.550 -        k == 0 &&
   5.551 -        entrytype != PGL_ENTRY_OUTLINE &&
   5.552 -        entrytype != PGL_ENTRY_POLYGON
   5.553 -      ) continue;
   5.554 -      /* get latitude and longitude values of edge */
   5.555 -      lat1 = points[j].lat;
   5.556 -      lat2 = points[k].lat;
   5.557 -      lon1 = points[j].lon;
   5.558 -      lon2 = points[k].lon;
   5.559 -      /* consider longitude wrap-around for edge */
   5.560 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   5.561 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   5.562 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
   5.563 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
   5.564 -      /* return true if point is on horizontal (west to east) edge of polygon */
   5.565 -      if (
   5.566 -        lat0 == lat1 && lat0 == lat2 &&
   5.567 -        ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) )
   5.568 -      ) return true;
   5.569 -      /* check if edge crosses east/west line of point */
   5.570 -      if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) {
   5.571 -        /* calculate longitude of intersection */
   5.572 -        lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1);
   5.573 -        /* return true if intersection goes (approximately) through point */
   5.574 -        if (pgl_round(lon) == lon0) return true;
   5.575 -        /* count intersection if east of point and entry is polygon*/
   5.576 -        if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++;
   5.577 -      }
   5.578 -    }
   5.579 -  }
   5.580 -  /* return true if number of intersections is odd */
   5.581 -  return counter & 1;
   5.582 -}
   5.583 -
   5.584 -/* calculate (approximate) distance between point and cluster */
   5.585 -static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
   5.586 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   5.587 -  int entrytype;         /* type of entry */
   5.588 -  int npoints;           /* number of points in entry */
   5.589 -  pgl_point *points;     /* array of points in entry */
   5.590 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   5.591 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   5.592 -  double lon_min = 0;    /* minimum (adjusted) longitude of entry vertices */
   5.593 -  double lon_max = 0;    /* maximum (adjusted) longitude of entry vertices */
   5.594 -  double lat0 = point->lat;  /* latitude of point */
   5.595 -  double lon0;           /* (adjusted) longitude of point */
   5.596 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   5.597 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   5.598 -  double s;              /* scalar for vector calculations */
   5.599 -  double dist;           /* distance calculated in one step */
   5.600 -  double min_dist = INFINITY;   /* minimum distance */
   5.601 -  /* distance is zero if point is contained in cluster */
   5.602 -  if (pgl_point_in_cluster(point, cluster)) return 0;
   5.603 -  /* iterate over all entries */
   5.604 -  for (i=0; i<cluster->nentries; i++) {
   5.605 -    /* get properties of entry */
   5.606 -    entrytype = cluster->entries[i].entrytype;
   5.607 -    npoints = cluster->entries[i].npoints;
   5.608 -    points = PGL_ENTRY_POINTS(cluster, i);
   5.609 -    /* determine east/west orientation of first point of entry and calculate
   5.610 -       antipodal longitude */
   5.611 -    lon_break = points[0].lon;
   5.612 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   5.613 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   5.614 -    else lon_dir = 0;
   5.615 -    /* determine covered longitude range */
   5.616 -    for (j=0; j<npoints; j++) {
   5.617 -      /* get longitude of vertex */
   5.618 -      lon1 = points[j].lon;
   5.619 -      /* adjust longitude to fix potential wrap-around */
   5.620 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   5.621 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   5.622 -      /* update minimum and maximum longitude of polygon */
   5.623 -      if (j == 0 || lon1 < lon_min) lon_min = lon1;
   5.624 -      if (j == 0 || lon1 > lon_max) lon_max = lon1;
   5.625 -    }
   5.626 -    /* adjust longitude wrap-around according to full longitude range */
   5.627 -    lon_break = (lon_max + lon_min) / 2;
   5.628 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   5.629 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   5.630 -    /* get longitude of point */
   5.631 -    lon0 = point->lon;
   5.632 -    /* consider longitude wrap-around for point */
   5.633 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 -= 360;
   5.634 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
   5.635 -    /* iterate over all edges and vertices */
   5.636 -    for (j=0; j<npoints; j++) {
   5.637 -      /* get latitude and longitude values of current point */
   5.638 -      lat1 = points[j].lat;
   5.639 -      lon1 = points[j].lon;
   5.640 -      /* consider longitude wrap-around for current point */
   5.641 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   5.642 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   5.643 -      /* calculate distance to vertex */
   5.644 -      dist = pgl_distance(lat0, lon0, lat1, lon1);
   5.645 -      /* store calculated distance if smallest */
   5.646 -      if (dist < min_dist) min_dist = dist;
   5.647 -      /* calculate index of next vertex */
   5.648 -      k = (j+1) % npoints;
   5.649 -      /* skip last edge unless entry is (closed) outline or polygon */
   5.650 -      if (
   5.651 -        k == 0 &&
   5.652 -        entrytype != PGL_ENTRY_OUTLINE &&
   5.653 -        entrytype != PGL_ENTRY_POLYGON
   5.654 -      ) continue;
   5.655 -      /* get latitude and longitude values of next point */
   5.656 -      lat2 = points[k].lat;
   5.657 -      lon2 = points[k].lon;
   5.658 -      /* consider longitude wrap-around for next point */
   5.659 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
   5.660 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
   5.661 -      /* go to next vertex and edge if edge is degenerated */
   5.662 -      if (lat1 == lat2 && lon1 == lon2) continue;
   5.663 -      /* otherwise test if point can be projected onto edge of polygon */
   5.664 -      s = (
   5.665 -        ((lat0-lat1) * (lat2-lat1) + (lon0-lon1) * (lon2-lon1)) /
   5.666 -        ((lat2-lat1) * (lat2-lat1) + (lon2-lon1) * (lon2-lon1))
   5.667 -      );
   5.668 -      /* go to next vertex and edge if point cannot be projected */
   5.669 -      if (!(s > 0 && s < 1)) continue;
   5.670 -      /* calculate distance from original point to projected point */
   5.671 -      dist = pgl_distance(
   5.672 -        lat0, lon0,
   5.673 -        lat1 + s * (lat2-lat1),
   5.674 -        lon1 + s * (lon2-lon1)
   5.675 -      );
   5.676 -      /* store calculated distance if smallest */
   5.677 -      if (dist < min_dist) min_dist = dist;
   5.678 -    }
   5.679 -  }
   5.680 -  /* return minimum distance */
   5.681 -  return min_dist;
   5.682 -}
   5.683 -
   5.684 -/* estimator function for distance between box and point */
   5.685 -/* allowed to return smaller values than actually correct */
   5.686 -static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
   5.687 -  double dlon;  /* longitude range of box (delta longitude) */
   5.688 -  double h;     /* half of distance along meridian */
   5.689 -  double d;     /* distance between both southern or both northern points */
   5.690 -  double cur_dist;  /* calculated distance */
   5.691 -  double min_dist;  /* minimum distance calculated */
   5.692 -  /* return infinity if bounding box is empty */
   5.693 -  if (box->lat_min > box->lat_max) return INFINITY;
   5.694 -  /* return zero if point is inside bounding box */
   5.695 -  if (pgl_point_in_box(point, box)) return 0;
   5.696 -  /* calculate delta longitude */
   5.697 -  dlon = box->lon_max - box->lon_min;
   5.698 -  if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
   5.699 -  /* if delta longitude is greater than 180 degrees, perform safe fall-back */
   5.700 -  if (dlon > 180) return 0;
   5.701 -  /* calculate half of distance along meridian */
   5.702 -  h = pgl_distance(box->lat_min, 0, box->lat_max, 0) / 2;
   5.703 -  /* calculate full distance between southern points */
   5.704 -  d = pgl_distance(box->lat_min, 0, box->lat_min, dlon);
   5.705 -  /* calculate maximum of full distance and half distance */
   5.706 -  if (h > d) d = h;
   5.707 -  /* calculate distance from point to first southern vertex and substract
   5.708 -     maximum error */
   5.709 -  min_dist = pgl_distance(
   5.710 -    point->lat, point->lon, box->lat_min, box->lon_min
   5.711 -  ) - d;
   5.712 -  /* return zero if estimated distance is smaller than zero */
   5.713 -  if (min_dist <= 0) return 0;
   5.714 -  /* repeat procedure with second southern vertex */
   5.715 -  cur_dist = pgl_distance(
   5.716 -    point->lat, point->lon, box->lat_min, box->lon_max
   5.717 -  ) - d;
   5.718 -  if (cur_dist <= 0) return 0;
   5.719 -  if (cur_dist < min_dist) min_dist = cur_dist;
   5.720 -  /* calculate full distance between northern points */
   5.721 -  d = pgl_distance(box->lat_max, 0, box->lat_max, dlon);
   5.722 -  /* calculate maximum of full distance and half distance */
   5.723 -  if (h > d) d = h;
   5.724 -  /* repeat procedure with northern vertices */
   5.725 -  cur_dist = pgl_distance(
   5.726 -    point->lat, point->lon, box->lat_max, box->lon_max
   5.727 -  ) - d;
   5.728 -  if (cur_dist <= 0) return 0;
   5.729 -  if (cur_dist < min_dist) min_dist = cur_dist;
   5.730 -  cur_dist = pgl_distance(
   5.731 -    point->lat, point->lon, box->lat_max, box->lon_min
   5.732 -  ) - d;
   5.733 -  if (cur_dist <= 0) return 0;
   5.734 -  if (cur_dist < min_dist) min_dist = cur_dist;
   5.735 -  /* return smallest value (unless already returned zero) */
   5.736 -  return min_dist;
   5.737 -}
   5.738 -
   5.739 -
   5.740 -/*----------------------------*
   5.741 - *  fractal geographic index  *
   5.742 - *----------------------------*/
   5.743 -
   5.744 -/* number of bytes used for geographic (center) position in keys */
   5.745 -#define PGL_KEY_LATLON_BYTELEN 7
   5.746 -
   5.747 -/* maximum reference value for logarithmic size of geographic objects */
   5.748 -#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
   5.749 -
   5.750 -/* safety margin to avoid floating point errors in distance estimation */
   5.751 -#define PGL_FPE_SAFETY (1.0+1e-14)  /* slightly greater than 1.0 */
   5.752 -
   5.753 -/* pointer to index key (either pgl_pointkey or pgl_areakey) */
   5.754 -typedef unsigned char *pgl_keyptr;
   5.755 -
   5.756 -/* index key for points (objects with zero area) on the spheroid */
   5.757 -/* bit  0..55: interspersed bits of latitude and longitude,
   5.758 -   bit 56..57: always zero,
   5.759 -   bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */
   5.760 -typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1];
   5.761 -
   5.762 -/* index key for geographic objects on spheroid with area greater than zero */
   5.763 -/* bit  0..55: interspersed bits of latitude and longitude of center point,
   5.764 -   bit     56: always set to 1,
   5.765 -   bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.),
   5.766 -   bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to
   5.767 -               PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth
   5.768 -               = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL
   5.769 -               (with interspersed bits = 0 and node depth = 0) for keys which
   5.770 -               cover both empty and non-empty objects */
   5.771 -
   5.772 -typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2];
   5.773 -
   5.774 -/* helper macros for reading/writing index keys */
   5.775 -#define PGL_KEY_NODEDEPTH_OFFSET  PGL_KEY_LATLON_BYTELEN
   5.776 -#define PGL_KEY_OBJSIZE_OFFSET    (PGL_KEY_NODEDEPTH_OFFSET+1)
   5.777 -#define PGL_POINTKEY_MAXDEPTH     (PGL_KEY_LATLON_BYTELEN*8)
   5.778 -#define PGL_AREAKEY_MAXDEPTH      (2*PGL_POINTKEY_MAXDEPTH+1)
   5.779 -#define PGL_AREAKEY_MAXOBJSIZE    (PGL_POINTKEY_MAXDEPTH+1)
   5.780 -#define PGL_AREAKEY_TYPEMASK      0x80
   5.781 -#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8)))
   5.782 -#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \
   5.783 -                                  ( PGL_KEY_LATLONBIT(key1, n) ^ \
   5.784 -                                    PGL_KEY_LATLONBIT(key2, n) )
   5.785 -#define PGL_KEY_IS_AREAKEY(key)   ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
   5.786 -                                    PGL_AREAKEY_TYPEMASK)
   5.787 -#define PGL_KEY_NODEDEPTH(key)    ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
   5.788 -                                    (PGL_AREAKEY_TYPEMASK-1))
   5.789 -#define PGL_KEY_OBJSIZE(key)      ((key)[PGL_KEY_OBJSIZE_OFFSET])
   5.790 -#define PGL_KEY_OBJSIZE_EMPTY     126
   5.791 -#define PGL_KEY_OBJSIZE_UNIVERSAL 127
   5.792 -#define PGL_KEY_IS_EMPTY(key)     ( PGL_KEY_IS_AREAKEY(key) && \
   5.793 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
   5.794 -                                    PGL_KEY_OBJSIZE_EMPTY )
   5.795 -#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \
   5.796 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
   5.797 -                                    PGL_KEY_OBJSIZE_UNIVERSAL )
   5.798 -
   5.799 -/* set area key to match empty objects only */
   5.800 -static void pgl_key_set_empty(pgl_keyptr key) {
   5.801 -  memset(key, 0, sizeof(pgl_areakey));
   5.802 -  /* Note: setting node depth to maximum is required for picksplit function */
   5.803 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
   5.804 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY;
   5.805 -}
   5.806 -
   5.807 -/* set area key to match any object (including empty objects) */
   5.808 -static void pgl_key_set_universal(pgl_keyptr key) {
   5.809 -  memset(key, 0, sizeof(pgl_areakey));
   5.810 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK;
   5.811 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL;
   5.812 -}
   5.813 -
   5.814 -/* convert a point on earth into a max-depth key to be used in index */
   5.815 -static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) {
   5.816 -  double lat = point->lat;
   5.817 -  double lon = point->lon;
   5.818 -  int i;
   5.819 -  /* clear latitude and longitude bits */
   5.820 -  memset(key, 0, PGL_KEY_LATLON_BYTELEN);
   5.821 -  /* set node depth to maximum and type bit to zero */
   5.822 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH;
   5.823 -  /* iterate over all latitude/longitude bit pairs */
   5.824 -  for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) {
   5.825 -    /* determine latitude bit */
   5.826 -    if (lat >= 0) {
   5.827 -      key[i/4] |= 0x80 >> (2*(i%4));
   5.828 -      lat *= 2; lat -= 90;
   5.829 -    } else {
   5.830 -      lat *= 2; lat += 90;
   5.831 -    }
   5.832 -    /* determine longitude bit */
   5.833 -    if (lon >= 0) {
   5.834 -      key[i/4] |= 0x80 >> (2*(i%4)+1);
   5.835 -      lon *= 2; lon -= 180;
   5.836 -    } else {
   5.837 -      lon *= 2; lon += 180;
   5.838 -    }
   5.839 -  }
   5.840 -}
   5.841 -
   5.842 -/* convert a circle on earth into a max-depth key to be used in an index */
   5.843 -static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) {
   5.844 -  /* handle special case of empty circle */
   5.845 -  if (circle->radius < 0) {
   5.846 -    pgl_key_set_empty(key);
   5.847 -    return;
   5.848 -  }
   5.849 -  /* perform same action as for point keys */
   5.850 -  pgl_point_to_key(&(circle->center), key);
   5.851 -  /* but overwrite type and node depth to fit area index key */
   5.852 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
   5.853 -  /* check if radius is greater than (or equal to) reference size */
   5.854 -  /* (treat equal values as greater values for numerical safety) */
   5.855 -  if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) {
   5.856 -    /* if yes, set logarithmic size to zero */
   5.857 -    key[PGL_KEY_OBJSIZE_OFFSET] = 0;
   5.858 -  } else {
   5.859 -    /* otherwise, determine logarithmic size iteratively */
   5.860 -    /* (one step is equivalent to a factor of sqrt(2)) */
   5.861 -    double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2;
   5.862 -    int objsize = 1;
   5.863 -    while (objsize < PGL_AREAKEY_MAXOBJSIZE) {
   5.864 -      /* stop when radius is greater than (or equal to) adjusted reference */
   5.865 -      /* (treat equal values as greater values for numerical safety) */
   5.866 -      if (circle->radius >= reference) break;
   5.867 -      reference /= M_SQRT2;
   5.868 -      objsize++;
   5.869 -    }
   5.870 -    /* set logarithmic size to determined value */
   5.871 -    key[PGL_KEY_OBJSIZE_OFFSET] = objsize;
   5.872 -  }
   5.873 -}
   5.874 -
   5.875 -/* check if one key is subkey of another key or vice versa */
   5.876 -static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) {
   5.877 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
   5.878 -  /* determine smallest depth */
   5.879 -  int depth1 = PGL_KEY_NODEDEPTH(key1);
   5.880 -  int depth2 = PGL_KEY_NODEDEPTH(key2);
   5.881 -  int depth = (depth1 < depth2) ? depth1 : depth2;
   5.882 -  /* check if keys are area keys (assuming that both keys have same type) */
   5.883 -  if (PGL_KEY_IS_AREAKEY(key1)) {
   5.884 -    int j = 0;  /* bit offset for logarithmic object size bits */
   5.885 -    int k = 0;  /* bit offset for latitude and longitude */
   5.886 -    /* fetch logarithmic object size information */
   5.887 -    int objsize1 = PGL_KEY_OBJSIZE(key1);
   5.888 -    int objsize2 = PGL_KEY_OBJSIZE(key2);
   5.889 -    /* handle special cases for empty objects (universal and empty keys) */
   5.890 -    if (
   5.891 -      objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL ||
   5.892 -      objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL
   5.893 -    ) return true;
   5.894 -    if (
   5.895 -      objsize1 == PGL_KEY_OBJSIZE_EMPTY ||
   5.896 -      objsize2 == PGL_KEY_OBJSIZE_EMPTY
   5.897 -    ) return objsize1 == objsize2;
   5.898 -    /* iterate through key bits */
   5.899 -    for (i=0; i<depth; i++) {
   5.900 -      /* every second bit is a bit describing the object size */
   5.901 -      if (i%2 == 0) {
   5.902 -        /* check if object size bit is different in both keys (objsize1 and
   5.903 -           objsize2 describe the minimum index when object size bit is set) */
   5.904 -        if (
   5.905 -          (objsize1 <= j && objsize2 > j) ||
   5.906 -          (objsize2 <= j && objsize1 > j)
   5.907 -        ) {
   5.908 -          /* bit differs, therefore keys are in separate branches */
   5.909 -          return false;
   5.910 -        }
   5.911 -        /* increase bit counter for object size bits */
   5.912 -        j++;
   5.913 -      }
   5.914 -      /* all other bits describe latitude and longitude */
   5.915 -      else {
   5.916 -        /* check if bit differs in both keys */
   5.917 -        if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) {
   5.918 -          /* bit differs, therefore keys are in separate branches */
   5.919 -          return false;
   5.920 -        }
   5.921 -        /* increase bit counter for latitude/longitude bits */
   5.922 -        k++;
   5.923 -      }
   5.924 -    }
   5.925 -  }
   5.926 -  /* if not, keys are point keys */
   5.927 -  else {
   5.928 -    /* iterate through key bits */
   5.929 -    for (i=0; i<depth; i++) {
   5.930 -      /* check if bit differs in both keys */
   5.931 -      if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) {
   5.932 -        /* bit differs, therefore keys are in separate branches */
   5.933 -        return false;
   5.934 -      }
   5.935 -    }
   5.936 -  }
   5.937 -  /* return true because keys are in the same branch */
   5.938 -  return true;
   5.939 -}
   5.940 -
   5.941 -/* combine two keys into new key which covers both original keys */
   5.942 -/* (result stored in first argument) */
   5.943 -static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) {
   5.944 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
   5.945 -  /* determine smallest depth */
   5.946 -  int depth1 = PGL_KEY_NODEDEPTH(dst);
   5.947 -  int depth2 = PGL_KEY_NODEDEPTH(src);
   5.948 -  int depth = (depth1 < depth2) ? depth1 : depth2;
   5.949 -  /* check if keys are area keys (assuming that both keys have same type) */
   5.950 -  if (PGL_KEY_IS_AREAKEY(dst)) {
   5.951 -    pgl_areakey dstbuf = { 0, };  /* destination buffer (cleared) */
   5.952 -    int j = 0;  /* bit offset for logarithmic object size bits */
   5.953 -    int k = 0;  /* bit offset for latitude and longitude */
   5.954 -    /* fetch logarithmic object size information */
   5.955 -    int objsize1 = PGL_KEY_OBJSIZE(dst);
   5.956 -    int objsize2 = PGL_KEY_OBJSIZE(src);
   5.957 -    /* handle special cases for empty objects (universal and empty keys) */
   5.958 -    if (
   5.959 -      objsize1 > PGL_AREAKEY_MAXOBJSIZE ||
   5.960 -      objsize2 > PGL_AREAKEY_MAXOBJSIZE
   5.961 -    ) {
   5.962 -      if (
   5.963 -        objsize1 == PGL_KEY_OBJSIZE_EMPTY &&
   5.964 -        objsize2 == PGL_KEY_OBJSIZE_EMPTY
   5.965 -      ) pgl_key_set_empty(dst);
   5.966 -      else pgl_key_set_universal(dst);
   5.967 -      return;
   5.968 -    }
   5.969 -    /* iterate through key bits */
   5.970 -    for (i=0; i<depth; i++) {
   5.971 -      /* every second bit is a bit describing the object size */
   5.972 -      if (i%2 == 0) {
   5.973 -        /* increase bit counter for object size bits first */
   5.974 -        /* (handy when setting objsize variable) */
   5.975 -        j++;
   5.976 -        /* check if object size bit is set in neither key */
   5.977 -        if (objsize1 >= j && objsize2 >= j) {
   5.978 -          /* set objsize in destination buffer to indicate that size bit is
   5.979 -             unset in destination buffer at the current bit position */
   5.980 -          dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j;
   5.981 -        }
   5.982 -        /* break if object size bit is set in one key only */
   5.983 -        else if (objsize1 >= j || objsize2 >= j) break;
   5.984 -      }
   5.985 -      /* all other bits describe latitude and longitude */
   5.986 -      else {
   5.987 -        /* break if bit differs in both keys */
   5.988 -        if (PGL_KEY_LATLONBIT(dst, k)) {
   5.989 -          if (!PGL_KEY_LATLONBIT(src, k)) break;
   5.990 -          /* but set bit in destination buffer if bit is set in both keys */
   5.991 -          dstbuf[k/8] |= 0x80 >> (k%8);
   5.992 -        } else if (PGL_KEY_LATLONBIT(src, k)) break;
   5.993 -        /* increase bit counter for latitude/longitude bits */
   5.994 -        k++;
   5.995 -      }
   5.996 -    }
   5.997 -    /* set common node depth and type bit (type bit = 1) */
   5.998 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i;
   5.999 -    /* copy contents of destination buffer to first key */
  5.1000 -    memcpy(dst, dstbuf, sizeof(pgl_areakey));
  5.1001 -  }
  5.1002 -  /* if not, keys are point keys */
  5.1003 -  else {
  5.1004 -    pgl_pointkey dstbuf = { 0, };  /* destination buffer (cleared) */
  5.1005 -    /* iterate through key bits */
  5.1006 -    for (i=0; i<depth; i++) {
  5.1007 -      /* break if bit differs in both keys */
  5.1008 -      if (PGL_KEY_LATLONBIT(dst, i)) {
  5.1009 -        if (!PGL_KEY_LATLONBIT(src, i)) break;
  5.1010 -        /* but set bit in destination buffer if bit is set in both keys */
  5.1011 -        dstbuf[i/8] |= 0x80 >> (i%8);
  5.1012 -      } else if (PGL_KEY_LATLONBIT(src, i)) break;
  5.1013 -    }
  5.1014 -    /* set common node depth (type bit = 0) */
  5.1015 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i;
  5.1016 -    /* copy contents of destination buffer to first key */
  5.1017 -    memcpy(dst, dstbuf, sizeof(pgl_pointkey));
  5.1018 -  }
  5.1019 -}
  5.1020 -
  5.1021 -/* determine center(!) boundaries and radius estimation of index key */
  5.1022 -static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) {
  5.1023 -  int i;
  5.1024 -  /* determine node depth */
  5.1025 -  int depth = PGL_KEY_NODEDEPTH(key);
  5.1026 -  /* center point of possible result */
  5.1027 -  double lat = 0;
  5.1028 -  double lon = 0;
  5.1029 -  /* maximum distance of real center point from key center */
  5.1030 -  double dlat = 90;
  5.1031 -  double dlon = 180;
  5.1032 -  /* maximum radius of contained objects */
  5.1033 -  double radius = 0;  /* always return zero for point index keys */
  5.1034 -  /* check if key is area key */
  5.1035 -  if (PGL_KEY_IS_AREAKEY(key)) {
  5.1036 -    /* get logarithmic object size */
  5.1037 -    int objsize = PGL_KEY_OBJSIZE(key);
  5.1038 -    /* handle special cases for empty objects (universal and empty keys) */
  5.1039 -    if (objsize == PGL_KEY_OBJSIZE_EMPTY) {
  5.1040 -      pgl_box_set_empty(box);
  5.1041 -      return 0;
  5.1042 -    } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) {
  5.1043 -      box->lat_min = -90;
  5.1044 -      box->lat_max =  90;
  5.1045 -      box->lon_min = -180;
  5.1046 -      box->lon_max =  180;
  5.1047 -      return 0;  /* any value >= 0 would do */
  5.1048 -    }
  5.1049 -    /* calculate maximum possible radius of objects covered by the given key */
  5.1050 -    if (objsize == 0) radius = INFINITY;
  5.1051 -    else {
  5.1052 -      radius = PGL_AREAKEY_REFOBJSIZE;
  5.1053 -      while (--objsize) radius /= M_SQRT2;
  5.1054 -    }
  5.1055 -    /* iterate over latitude and longitude bits in key */
  5.1056 -    /* (every second bit is a latitude or longitude bit) */
  5.1057 -    for (i=0; i<depth/2; i++) {
  5.1058 -      /* check if latitude bit */
  5.1059 -      if (i%2 == 0) {
  5.1060 -        /* cut latitude dimension in half */
  5.1061 -        dlat /= 2;
  5.1062 -        /* increase center latitude if bit is 1, otherwise decrease */
  5.1063 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  5.1064 -        else lat -= dlat;
  5.1065 -      }
  5.1066 -      /* otherwise longitude bit */
  5.1067 -      else {
  5.1068 -        /* cut longitude dimension in half */
  5.1069 -        dlon /= 2;
  5.1070 -        /* increase center longitude if bit is 1, otherwise decrease */
  5.1071 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  5.1072 -        else lon -= dlon;
  5.1073 -      }
  5.1074 -    }
  5.1075 -  }
  5.1076 -  /* if not, keys are point keys */
  5.1077 -  else {
  5.1078 -    /* iterate over all bits in key */
  5.1079 -    for (i=0; i<depth; i++) {
  5.1080 -      /* check if latitude bit */
  5.1081 -      if (i%2 == 0) {
  5.1082 -        /* cut latitude dimension in half */
  5.1083 -        dlat /= 2;
  5.1084 -        /* increase center latitude if bit is 1, otherwise decrease */
  5.1085 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  5.1086 -        else lat -= dlat;
  5.1087 -      }
  5.1088 -      /* otherwise longitude bit */
  5.1089 -      else {
  5.1090 -        /* cut longitude dimension in half */
  5.1091 -        dlon /= 2;
  5.1092 -        /* increase center longitude if bit is 1, otherwise decrease */
  5.1093 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  5.1094 -        else lon -= dlon;
  5.1095 -      }
  5.1096 -    }
  5.1097 -  }
  5.1098 -  /* calculate boundaries from center point and remaining dlat and dlon */
  5.1099 -  /* (return values through pointer to box) */
  5.1100 -  box->lat_min = lat - dlat;
  5.1101 -  box->lat_max = lat + dlat;
  5.1102 -  box->lon_min = lon - dlon;
  5.1103 -  box->lon_max = lon + dlon;
  5.1104 -  /* return radius (as a function return value) */
  5.1105 -  return radius;
  5.1106 -}
  5.1107 -
  5.1108 -/* estimator function for distance between point and index key */
  5.1109 -/* allowed to return smaller values than actually correct */
  5.1110 -static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
  5.1111 -  pgl_box box;  /* center(!) bounding box of area index key */
  5.1112 -  /* calculate center(!) bounding box and maximum radius of objects covered
  5.1113 -     by area index key (radius is zero for point index keys) */
  5.1114 -  double distance = pgl_key_to_box(key, &box);
  5.1115 -  /* calculate estimated distance between bounding box of center point of
  5.1116 -     indexed object and point passed as second argument, then substract maximum
  5.1117 -     radius of objects covered by index key */
  5.1118 -  /* (use PGL_FPE_SAFETY factor to cope with minor floating point errors) */
  5.1119 -  distance = (
  5.1120 -    pgl_estimate_point_box_distance(point, &box) / PGL_FPE_SAFETY -
  5.1121 -    distance * PGL_FPE_SAFETY
  5.1122 -  );
  5.1123 -  /* truncate negative results to zero */
  5.1124 -  if (distance <= 0) distance = 0;
  5.1125 -  /* return result */
  5.1126 -  return distance;
  5.1127 -}
  5.1128 -
  5.1129 -
  5.1130 -/*---------------------------------*
  5.1131 - *  helper functions for text I/O  *
  5.1132 - *---------------------------------*/
  5.1133 -
  5.1134 -#define PGL_NUMBUFLEN 64  /* buffer size for number to string conversion */
  5.1135 -
  5.1136 -/* convert floating point number to string (round-trip safe) */
  5.1137 -static void pgl_print_float(char *buf, double flt) {
  5.1138 -  /* check if number is integral */
  5.1139 -  if (trunc(flt) == flt) {
  5.1140 -    /* for integral floats use maximum precision */
  5.1141 -    snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  5.1142 -  } else {
  5.1143 -    /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */
  5.1144 -    snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt);
  5.1145 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt);
  5.1146 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  5.1147 -  }
  5.1148 -}
  5.1149 -
  5.1150 -/* convert latitude floating point number (in degrees) to string */
  5.1151 -static void pgl_print_lat(char *buf, double lat) {
  5.1152 -  if (signbit(lat)) {
  5.1153 -    /* treat negative latitudes (including -0) as south */
  5.1154 -    snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat);
  5.1155 -  } else {
  5.1156 -    /* treat positive latitudes (including +0) as north */
  5.1157 -    snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat);
  5.1158 -  }
  5.1159 -}
  5.1160 -
  5.1161 -/* convert longitude floating point number (in degrees) to string */
  5.1162 -static void pgl_print_lon(char *buf, double lon) {
  5.1163 -  if (signbit(lon)) {
  5.1164 -    /* treat negative longitudes (including -0) as west */
  5.1165 -    snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon);
  5.1166 -  } else {
  5.1167 -    /* treat positive longitudes (including +0) as east */
  5.1168 -    snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon);
  5.1169 -  }
  5.1170 -}
  5.1171 -
  5.1172 -/* bit masks used as return value of pgl_scan() function */
  5.1173 -#define PGL_SCAN_NONE 0      /* no value has been parsed */
  5.1174 -#define PGL_SCAN_LAT (1<<0)  /* latitude has been parsed */
  5.1175 -#define PGL_SCAN_LON (1<<1)  /* longitude has been parsed */
  5.1176 -#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON)  /* bitwise OR of both */
  5.1177 -
  5.1178 -/* parse a coordinate (can be latitude or longitude) */
  5.1179 -static int pgl_scan(char **str, double *lat, double *lon) {
  5.1180 -  double val;
  5.1181 -  int len;
  5.1182 -  if (
  5.1183 -    sscanf(*str, " N %lf %n", &val, &len) ||
  5.1184 -    sscanf(*str, " n %lf %n", &val, &len)
  5.1185 -  ) {
  5.1186 -    *str += len; *lat = val; return PGL_SCAN_LAT;
  5.1187 -  }
  5.1188 -  if (
  5.1189 -    sscanf(*str, " S %lf %n", &val, &len) ||
  5.1190 -    sscanf(*str, " s %lf %n", &val, &len)
  5.1191 -  ) {
  5.1192 -    *str += len; *lat = -val; return PGL_SCAN_LAT;
  5.1193 -  }
  5.1194 -  if (
  5.1195 -    sscanf(*str, " E %lf %n", &val, &len) ||
  5.1196 -    sscanf(*str, " e %lf %n", &val, &len)
  5.1197 -  ) {
  5.1198 -    *str += len; *lon = val; return PGL_SCAN_LON;
  5.1199 -  }
  5.1200 -  if (
  5.1201 -    sscanf(*str, " W %lf %n", &val, &len) ||
  5.1202 -    sscanf(*str, " w %lf %n", &val, &len)
  5.1203 -  ) {
  5.1204 -    *str += len; *lon = -val; return PGL_SCAN_LON;
  5.1205 -  }
  5.1206 -  return PGL_SCAN_NONE;
  5.1207 -}
  5.1208 -
  5.1209 -
  5.1210 -/*-----------------*
  5.1211 - *  SQL functions  *
  5.1212 - *-----------------*/
  5.1213 -
  5.1214 -/* Note: These function names use "epoint", "ebox", etc. notation here instead
  5.1215 -   of "point", "box", etc. in order to distinguish them from any previously
  5.1216 -   defined functions. */
  5.1217 -
  5.1218 -/* function needed for dummy types and/or not implemented features */
  5.1219 -PG_FUNCTION_INFO_V1(pgl_notimpl);
  5.1220 -Datum pgl_notimpl(PG_FUNCTION_ARGS) {
  5.1221 -  ereport(ERROR, (errmsg("not implemented by pgLatLon")));
  5.1222 -}
  5.1223 -
  5.1224 -/* set point to latitude and longitude (including checks) */
  5.1225 -static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) {
  5.1226 -  /* reject infinite or NaN values */
  5.1227 -  if (!isfinite(lat) || !isfinite(lon)) {
  5.1228 -    ereport(ERROR, (
  5.1229 -      errcode(ERRCODE_DATA_EXCEPTION),
  5.1230 -      errmsg("epoint requires finite coordinates")
  5.1231 -    ));
  5.1232 -  }
  5.1233 -  /* check latitude bounds */
  5.1234 -  if (lat < -90) {
  5.1235 -    ereport(WARNING, (errmsg("latitude exceeds south pole")));
  5.1236 -    lat = -90;
  5.1237 -  } else if (lat > 90) {
  5.1238 -    ereport(WARNING, (errmsg("latitude exceeds north pole")));
  5.1239 -    lat = 90;
  5.1240 -  }
  5.1241 -  /* check longitude bounds */
  5.1242 -  if (lon < -180) {
  5.1243 -    ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized")));
  5.1244 -    lon += 360 - trunc(lon / 360) * 360;
  5.1245 -  } else if (lon > 180) {
  5.1246 -    ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized")));
  5.1247 -    lon -= 360 + trunc(lon / 360) * 360;
  5.1248 -  }
  5.1249 -  /* store rounded latitude/longitude values for round-trip safety */
  5.1250 -  point->lat = pgl_round(lat);
  5.1251 -  point->lon = pgl_round(lon);
  5.1252 -}
  5.1253 -
  5.1254 -/* create point ("epoint" in SQL) from latitude and longitude */
  5.1255 -PG_FUNCTION_INFO_V1(pgl_create_epoint);
  5.1256 -Datum pgl_create_epoint(PG_FUNCTION_ARGS) {
  5.1257 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  5.1258 -  pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1));
  5.1259 -  PG_RETURN_POINTER(point);
  5.1260 -}
  5.1261 -
  5.1262 -/* parse point ("epoint" in SQL) */
  5.1263 -/* format: '[NS]<float> [EW]<float>' */
  5.1264 -PG_FUNCTION_INFO_V1(pgl_epoint_in);
  5.1265 -Datum pgl_epoint_in(PG_FUNCTION_ARGS) {
  5.1266 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  5.1267 -  char *strptr = str;  /* current position within string */
  5.1268 -  int done = 0;        /* bit mask storing if latitude or longitude was read */
  5.1269 -  double lat, lon;     /* parsed values as double precision floats */
  5.1270 -  pgl_point *point;    /* return value (to be palloc'ed) */
  5.1271 -  /* parse two floats (each latitude or longitude) separated by white-space */
  5.1272 -  done |= pgl_scan(&strptr, &lat, &lon);
  5.1273 -  if (strptr != str && isspace(strptr[-1])) {
  5.1274 -    done |= pgl_scan(&strptr, &lat, &lon);
  5.1275 -  }
  5.1276 -  /* require end of string, and latitude and longitude parsed successfully */
  5.1277 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  5.1278 -    ereport(ERROR, (
  5.1279 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1280 -      errmsg("invalid input syntax for type epoint: \"%s\"", str)
  5.1281 -    ));
  5.1282 -  }
  5.1283 -  /* allocate memory for result */
  5.1284 -  point = (pgl_point *)palloc(sizeof(pgl_point));
  5.1285 -  /* set latitude and longitude (and perform checks) */
  5.1286 -  pgl_epoint_set_latlon(point, lat, lon);
  5.1287 -  /* return result */
  5.1288 -  PG_RETURN_POINTER(point);
  5.1289 -}
  5.1290 -
  5.1291 -/* create box ("ebox" in SQL) that is empty */
  5.1292 -PG_FUNCTION_INFO_V1(pgl_create_empty_ebox);
  5.1293 -Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) {
  5.1294 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1295 -  pgl_box_set_empty(box);
  5.1296 -  PG_RETURN_POINTER(box);
  5.1297 -}
  5.1298 -
  5.1299 -/* set box to given boundaries (including checks) */
  5.1300 -static void pgl_ebox_set_boundaries(
  5.1301 -  pgl_box *box,
  5.1302 -  double lat_min, double lat_max, double lon_min, double lon_max
  5.1303 -) {
  5.1304 -  /* if minimum latitude is greater than maximum latitude, return empty box */
  5.1305 -  if (lat_min > lat_max) {
  5.1306 -    pgl_box_set_empty(box);
  5.1307 -    return;
  5.1308 -  }
  5.1309 -  /* otherwise reject infinite or NaN values */
  5.1310 -  if (
  5.1311 -    !isfinite(lat_min) || !isfinite(lat_max) ||
  5.1312 -    !isfinite(lon_min) || !isfinite(lon_max)
  5.1313 -  ) {
  5.1314 -    ereport(ERROR, (
  5.1315 -      errcode(ERRCODE_DATA_EXCEPTION),
  5.1316 -      errmsg("ebox requires finite coordinates")
  5.1317 -    ));
  5.1318 -  }
  5.1319 -  /* check latitude bounds */
  5.1320 -  if (lat_max < -90) {
  5.1321 -    ereport(WARNING, (errmsg("northern latitude exceeds south pole")));
  5.1322 -    lat_max = -90;
  5.1323 -  } else if (lat_max > 90) {
  5.1324 -    ereport(WARNING, (errmsg("northern latitude exceeds north pole")));
  5.1325 -    lat_max = 90;
  5.1326 -  }
  5.1327 -  if (lat_min < -90) {
  5.1328 -    ereport(WARNING, (errmsg("southern latitude exceeds south pole")));
  5.1329 -    lat_min = -90;
  5.1330 -  } else if (lat_min > 90) {
  5.1331 -    ereport(WARNING, (errmsg("southern latitude exceeds north pole")));
  5.1332 -    lat_min = 90;
  5.1333 -  }
  5.1334 -  /* check if all longitudes are included */
  5.1335 -  if (lon_max - lon_min >= 360) {
  5.1336 -    if (lon_max - lon_min > 360) ereport(WARNING, (
  5.1337 -      errmsg("longitude coverage greater than 360 degrees")
  5.1338 -    ));
  5.1339 -    lon_min = -180;
  5.1340 -    lon_max = 180;
  5.1341 -  } else {
  5.1342 -    /* normalize longitude bounds */
  5.1343 -    if      (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360;
  5.1344 -    else if (lon_min >  180) lon_min -= 360 + trunc(lon_min / 360) * 360;
  5.1345 -    if      (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360;
  5.1346 -    else if (lon_max >  180) lon_max -= 360 + trunc(lon_max / 360) * 360;
  5.1347 -  }
  5.1348 -  /* store rounded latitude/longitude values for round-trip safety */
  5.1349 -  box->lat_min = pgl_round(lat_min);
  5.1350 -  box->lat_max = pgl_round(lat_max);
  5.1351 -  box->lon_min = pgl_round(lon_min);
  5.1352 -  box->lon_max = pgl_round(lon_max);
  5.1353 -  /* ensure that rounding does not change orientation */
  5.1354 -  if (lon_min > lon_max && box->lon_min == box->lon_max) {
  5.1355 -    box->lon_min = -180;
  5.1356 -    box->lon_max = 180;
  5.1357 -  }
  5.1358 -}
  5.1359 -
  5.1360 -/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */
  5.1361 -PG_FUNCTION_INFO_V1(pgl_create_ebox);
  5.1362 -Datum pgl_create_ebox(PG_FUNCTION_ARGS) {
  5.1363 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1364 -  pgl_ebox_set_boundaries(
  5.1365 -    box,
  5.1366 -    PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1),
  5.1367 -    PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3)
  5.1368 -  );
  5.1369 -  PG_RETURN_POINTER(box);
  5.1370 -}
  5.1371 -
  5.1372 -/* create box ("ebox" in SQL) from two points ("epoint"s) */
  5.1373 -/* (can not be used to cover a longitude range of more than 120 degrees) */
  5.1374 -PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints);
  5.1375 -Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) {
  5.1376 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  5.1377 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  5.1378 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1379 -  double lat_min, lat_max, lon_min, lon_max;
  5.1380 -  double dlon;  /* longitude range (delta longitude) */
  5.1381 -  /* order latitude and longitude boundaries */
  5.1382 -  if (point2->lat < point1->lat) {
  5.1383 -    lat_min = point2->lat;
  5.1384 -    lat_max = point1->lat;
  5.1385 -  } else {
  5.1386 -    lat_min = point1->lat;
  5.1387 -    lat_max = point2->lat;
  5.1388 -  }
  5.1389 -  if (point2->lon < point1->lon) {
  5.1390 -    lon_min = point2->lon;
  5.1391 -    lon_max = point1->lon;
  5.1392 -  } else {
  5.1393 -    lon_min = point1->lon;
  5.1394 -    lon_max = point2->lon;
  5.1395 -  }
  5.1396 -  /* calculate longitude range (round to avoid floating point errors) */
  5.1397 -  dlon = pgl_round(lon_max - lon_min);
  5.1398 -  /* determine east-west direction */
  5.1399 -  if (dlon >= 240) {
  5.1400 -    /* assume that 180th meridian is crossed and swap min/max longitude */
  5.1401 -    double swap = lon_min; lon_min = lon_max; lon_max = swap;
  5.1402 -  } else if (dlon > 120) {
  5.1403 -    /* unclear orientation since delta longitude > 120 */
  5.1404 -    ereport(ERROR, (
  5.1405 -      errcode(ERRCODE_DATA_EXCEPTION),
  5.1406 -      errmsg("can not determine east/west orientation for ebox")
  5.1407 -    ));
  5.1408 -  }
  5.1409 -  /* use boundaries to setup box (and perform checks) */
  5.1410 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  5.1411 -  /* return result */
  5.1412 -  PG_RETURN_POINTER(box);
  5.1413 -}
  5.1414 -
  5.1415 -/* parse box ("ebox" in SQL) */
  5.1416 -/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>'
  5.1417 -       or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */
  5.1418 -PG_FUNCTION_INFO_V1(pgl_ebox_in);
  5.1419 -Datum pgl_ebox_in(PG_FUNCTION_ARGS) {
  5.1420 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  5.1421 -  char *str_lower;     /* lower case version of input string */
  5.1422 -  char *strptr;        /* current position within string */
  5.1423 -  int valid;           /* number of valid chars */
  5.1424 -  int done;            /* specifies if latitude or longitude was read */
  5.1425 -  double val;          /* temporary variable */
  5.1426 -  int lat_count = 0;   /* count of latitude values parsed */
  5.1427 -  int lon_count = 0;   /* count of longitufde values parsed */
  5.1428 -  double lat_min, lat_max, lon_min, lon_max;  /* see pgl_box struct */
  5.1429 -  pgl_box *box;        /* return value (to be palloc'ed) */
  5.1430 -  /* lowercase input */
  5.1431 -  str_lower = psprintf("%s", str);
  5.1432 -  for (strptr=str_lower; *strptr; strptr++) {
  5.1433 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  5.1434 -  }
  5.1435 -  /* reset reading position to start of (lowercase) string */
  5.1436 -  strptr = str_lower;
  5.1437 -  /* check if empty box */
  5.1438 -  valid = 0;
  5.1439 -  sscanf(strptr, " empty %n", &valid);
  5.1440 -  if (valid && strptr[valid] == 0) {
  5.1441 -    /* allocate and return empty box */
  5.1442 -    box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1443 -    pgl_box_set_empty(box);
  5.1444 -    PG_RETURN_POINTER(box);
  5.1445 -  }
  5.1446 -  /* demand four blocks separated by whitespace */
  5.1447 -  valid = 0;
  5.1448 -  sscanf(strptr, " %*s %*s %*s %*s %n", &valid);
  5.1449 -  /* if four blocks separated by whitespace exist, parse those blocks */
  5.1450 -  if (strptr[valid] == 0) while (strptr[0]) {
  5.1451 -    /* parse either latitude or longitude (whichever found in input string) */
  5.1452 -    done = pgl_scan(&strptr, &val, &val);
  5.1453 -    /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */
  5.1454 -    if (done == PGL_SCAN_LAT) {
  5.1455 -      if (!lat_count) lat_min = val; else lat_max = val;
  5.1456 -      lat_count++;
  5.1457 -    } else if (done == PGL_SCAN_LON) {
  5.1458 -      if (!lon_count) lon_min = val; else lon_max = val;
  5.1459 -      lon_count++;
  5.1460 -    } else {
  5.1461 -      break;
  5.1462 -    }
  5.1463 -  }
  5.1464 -  /* require end of string, and two latitude and two longitude values */
  5.1465 -  if (strptr[0] || lat_count != 2 || lon_count != 2) {
  5.1466 -    ereport(ERROR, (
  5.1467 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1468 -      errmsg("invalid input syntax for type ebox: \"%s\"", str)
  5.1469 -    ));
  5.1470 -  }
  5.1471 -  /* free lower case string */
  5.1472 -  pfree(str_lower);
  5.1473 -  /* order boundaries (maximum greater than minimum) */
  5.1474 -  if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; }
  5.1475 -  if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; }
  5.1476 -  /* allocate memory for result */
  5.1477 -  box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1478 -  /* set boundaries (and perform checks) */
  5.1479 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  5.1480 -  /* return result */
  5.1481 -  PG_RETURN_POINTER(box);
  5.1482 -}
  5.1483 -
  5.1484 -/* set circle to given latitude, longitude, and radius (including checks) */
  5.1485 -static void pgl_ecircle_set_latlon_radius(
  5.1486 -  pgl_circle *circle, double lat, double lon, double radius
  5.1487 -) {
  5.1488 -  /* set center point (including checks) */
  5.1489 -  pgl_epoint_set_latlon(&(circle->center), lat, lon);
  5.1490 -  /* handle non-positive radius */
  5.1491 -  if (isnan(radius)) {
  5.1492 -    ereport(ERROR, (
  5.1493 -      errcode(ERRCODE_DATA_EXCEPTION),
  5.1494 -      errmsg("invalid radius for ecircle")
  5.1495 -    ));
  5.1496 -  }
  5.1497 -  if (radius == 0) radius = 0;  /* avoids -0 */
  5.1498 -  else if (radius < 0) {
  5.1499 -    if (isfinite(radius)) {
  5.1500 -      ereport(NOTICE, (errmsg("negative radius converted to minus infinity")));
  5.1501 -    }
  5.1502 -    radius = -INFINITY;
  5.1503 -  }
  5.1504 -  /* store radius (round-trip safety is ensured by pgl_print_float) */
  5.1505 -  circle->radius = radius;
  5.1506 -}
  5.1507 -
  5.1508 -/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */
  5.1509 -PG_FUNCTION_INFO_V1(pgl_create_ecircle);
  5.1510 -Datum pgl_create_ecircle(PG_FUNCTION_ARGS) {
  5.1511 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  5.1512 -  pgl_ecircle_set_latlon_radius(
  5.1513 -    circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2)
  5.1514 -  );
  5.1515 -  PG_RETURN_POINTER(circle);
  5.1516 -}
  5.1517 -
  5.1518 -/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */
  5.1519 -PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint);
  5.1520 -Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) {
  5.1521 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1522 -  double radius = PG_GETARG_FLOAT8(1);
  5.1523 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  5.1524 -  /* set latitude, longitude, radius (and perform checks) */
  5.1525 -  pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius);
  5.1526 -  /* return result */
  5.1527 -  PG_RETURN_POINTER(circle);
  5.1528 -}
  5.1529 -
  5.1530 -/* parse circle ("ecircle" in SQL) */
  5.1531 -/* format: '[NS]<float> [EW]<float> <float>' */
  5.1532 -PG_FUNCTION_INFO_V1(pgl_ecircle_in);
  5.1533 -Datum pgl_ecircle_in(PG_FUNCTION_ARGS) {
  5.1534 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  5.1535 -  char *strptr = str;       /* current position within string */
  5.1536 -  double lat, lon, radius;  /* parsed values as double precision flaots */
  5.1537 -  int valid = 0;            /* number of valid chars */
  5.1538 -  int done = 0;             /* stores if latitude and/or longitude was read */
  5.1539 -  pgl_circle *circle;       /* return value (to be palloc'ed) */
  5.1540 -  /* demand three blocks separated by whitespace */
  5.1541 -  sscanf(strptr, " %*s %*s %*s %n", &valid);
  5.1542 -  /* if three blocks separated by whitespace exist, parse those blocks */
  5.1543 -  if (strptr[valid] == 0) {
  5.1544 -    /* parse latitude and longitude */
  5.1545 -    done |= pgl_scan(&strptr, &lat, &lon);
  5.1546 -    done |= pgl_scan(&strptr, &lat, &lon);
  5.1547 -    /* parse radius (while incrementing strptr by number of bytes parsed) */
  5.1548 -    valid = 0;
  5.1549 -    if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid;
  5.1550 -  }
  5.1551 -  /* require end of string and both latitude and longitude being parsed */
  5.1552 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  5.1553 -    ereport(ERROR, (
  5.1554 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1555 -      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  5.1556 -    ));
  5.1557 -  }
  5.1558 -  /* allocate memory for result */
  5.1559 -  circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  5.1560 -  /* set latitude, longitude, radius (and perform checks) */
  5.1561 -  pgl_ecircle_set_latlon_radius(circle, lat, lon, radius);
  5.1562 -  /* return result */
  5.1563 -  PG_RETURN_POINTER(circle);
  5.1564 -}
  5.1565 -
  5.1566 -/* parse cluster ("ecluster" in SQL) */
  5.1567 -PG_FUNCTION_INFO_V1(pgl_ecluster_in);
  5.1568 -Datum pgl_ecluster_in(PG_FUNCTION_ARGS) {
  5.1569 -  int i;
  5.1570 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  5.1571 -  char *str_lower;         /* lower case version of input string */
  5.1572 -  char *strptr;            /* pointer to current reading position of input */
  5.1573 -  int npoints_total = 0;   /* total number of points in cluster */
  5.1574 -  int nentries = 0;        /* total number of entries */
  5.1575 -  pgl_newentry *entries;   /* array of pgl_newentry to create pgl_cluster */
  5.1576 -  int entries_buflen = 4;  /* maximum number of elements in entries array */
  5.1577 -  int valid;               /* number of valid chars processed */
  5.1578 -  double lat, lon;         /* latitude and longitude of parsed point */
  5.1579 -  int entrytype;           /* current entry type */
  5.1580 -  int npoints;             /* number of points in current entry */
  5.1581 -  pgl_point *points;       /* array of pgl_point for pgl_newentry */
  5.1582 -  int points_buflen;       /* maximum number of elements in points array */
  5.1583 -  int done;                /* return value of pgl_scan function */
  5.1584 -  pgl_cluster *cluster;    /* created cluster */
  5.1585 -  /* lowercase input */
  5.1586 -  str_lower = psprintf("%s", str);
  5.1587 -  for (strptr=str_lower; *strptr; strptr++) {
  5.1588 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  5.1589 -  }
  5.1590 -  /* reset reading position to start of (lowercase) string */
  5.1591 -  strptr = str_lower;
  5.1592 -  /* allocate initial buffer for entries */
  5.1593 -  entries = palloc(entries_buflen * sizeof(pgl_newentry));
  5.1594 -  /* parse until end of string */
  5.1595 -  while (strptr[0]) {
  5.1596 -    /* require previous white-space or closing parenthesis before next token */
  5.1597 -    if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') {
  5.1598 -      goto pgl_ecluster_in_error;
  5.1599 -    }
  5.1600 -    /* ignore token "empty" */
  5.1601 -    valid = 0; sscanf(strptr, " empty %n", &valid);
  5.1602 -    if (valid) { strptr += valid; continue; }
  5.1603 -    /* test for "point" token */
  5.1604 -    valid = 0; sscanf(strptr, " point ( %n", &valid);
  5.1605 -    if (valid) {
  5.1606 -      strptr += valid;
  5.1607 -      entrytype = PGL_ENTRY_POINT;
  5.1608 -      goto pgl_ecluster_in_type_ok;
  5.1609 -    }
  5.1610 -    /* test for "path" token */
  5.1611 -    valid = 0; sscanf(strptr, " path ( %n", &valid);
  5.1612 -    if (valid) {
  5.1613 -      strptr += valid;
  5.1614 -      entrytype = PGL_ENTRY_PATH;
  5.1615 -      goto pgl_ecluster_in_type_ok;
  5.1616 -    }
  5.1617 -    /* test for "outline" token */
  5.1618 -    valid = 0; sscanf(strptr, " outline ( %n", &valid);
  5.1619 -    if (valid) {
  5.1620 -      strptr += valid;
  5.1621 -      entrytype = PGL_ENTRY_OUTLINE;
  5.1622 -      goto pgl_ecluster_in_type_ok;
  5.1623 -    }
  5.1624 -    /* test for "polygon" token */
  5.1625 -    valid = 0; sscanf(strptr, " polygon ( %n", &valid);
  5.1626 -    if (valid) {
  5.1627 -      strptr += valid;
  5.1628 -      entrytype = PGL_ENTRY_POLYGON;
  5.1629 -      goto pgl_ecluster_in_type_ok;
  5.1630 -    }
  5.1631 -    /* error if no valid token found */
  5.1632 -    goto pgl_ecluster_in_error;
  5.1633 -    pgl_ecluster_in_type_ok:
  5.1634 -    /* check if pgl_newentry array needs to grow */
  5.1635 -    if (nentries == entries_buflen) {
  5.1636 -      pgl_newentry *newbuf;
  5.1637 -      entries_buflen *= 2;
  5.1638 -      newbuf = palloc(entries_buflen * sizeof(pgl_newentry));
  5.1639 -      memcpy(newbuf, entries, nentries * sizeof(pgl_newentry));
  5.1640 -      pfree(entries);
  5.1641 -      entries = newbuf;
  5.1642 -    }
  5.1643 -    /* reset number of points for current entry */
  5.1644 -    npoints = 0;
  5.1645 -    /* allocate array for points */
  5.1646 -    points_buflen = 4;
  5.1647 -    points = palloc(points_buflen * sizeof(pgl_point));
  5.1648 -    /* parse until closing parenthesis */
  5.1649 -    while (strptr[0] != ')') {
  5.1650 -      /* error on unexpected end of string */
  5.1651 -      if (strptr[0] == 0) goto pgl_ecluster_in_error;
  5.1652 -      /* mark neither latitude nor longitude as read */
  5.1653 -      done = PGL_SCAN_NONE;
  5.1654 -      /* require white-space before second, third, etc. point */
  5.1655 -      if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  5.1656 -      /* scan latitude (or longitude) */
  5.1657 -      done |= pgl_scan(&strptr, &lat, &lon);
  5.1658 -      /* require white-space before second coordinate */
  5.1659 -      if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  5.1660 -      /* scan longitude (or latitude) */
  5.1661 -      done |= pgl_scan(&strptr, &lat, &lon);
  5.1662 -      /* error unless both latitude and longitude were parsed */
  5.1663 -      if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error;
  5.1664 -      /* throw error if number of points is too high */
  5.1665 -      if (npoints_total == PGL_CLUSTER_MAXPOINTS) {
  5.1666 -        ereport(ERROR, (
  5.1667 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1668 -          errmsg(
  5.1669 -            "too many points for ecluster entry (maximum %i)",
  5.1670 -            PGL_CLUSTER_MAXPOINTS
  5.1671 -          )
  5.1672 -        ));
  5.1673 -      }
  5.1674 -      /* check if pgl_point array needs to grow */
  5.1675 -      if (npoints == points_buflen) {
  5.1676 -        pgl_point *newbuf;
  5.1677 -        points_buflen *= 2;
  5.1678 -        newbuf = palloc(points_buflen * sizeof(pgl_point));
  5.1679 -        memcpy(newbuf, points, npoints * sizeof(pgl_point));
  5.1680 -        pfree(points);
  5.1681 -        points = newbuf;
  5.1682 -      }
  5.1683 -      /* append point to pgl_point array (includes checks) */
  5.1684 -      pgl_epoint_set_latlon(&(points[npoints++]), lat, lon);
  5.1685 -      /* increase total number of points */
  5.1686 -      npoints_total++;
  5.1687 -    }
  5.1688 -    /* error if entry has no points */
  5.1689 -    if (!npoints) goto pgl_ecluster_in_error;
  5.1690 -    /* entries with one point are automatically of type "point" */
  5.1691 -    if (npoints == 1) entrytype = PGL_ENTRY_POINT;
  5.1692 -    /* if entries have more than one point */
  5.1693 -    else {
  5.1694 -      /* throw error if entry type is "point" */
  5.1695 -      if (entrytype == PGL_ENTRY_POINT) {
  5.1696 -        ereport(ERROR, (
  5.1697 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1698 -          errmsg("invalid input syntax for type ecluster (point entry with more than one point)")
  5.1699 -        ));
  5.1700 -      }
  5.1701 -      /* coerce outlines and polygons with more than 2 points to be a path */
  5.1702 -      if (npoints == 2) entrytype = PGL_ENTRY_PATH;
  5.1703 -    }
  5.1704 -    /* append entry to pgl_newentry array */
  5.1705 -    entries[nentries].entrytype = entrytype;
  5.1706 -    entries[nentries].npoints = npoints;
  5.1707 -    entries[nentries].points = points;
  5.1708 -    nentries++;
  5.1709 -    /* consume closing parenthesis */
  5.1710 -    strptr++;
  5.1711 -    /* consume white-space */
  5.1712 -    while (isspace(strptr[0])) strptr++;
  5.1713 -  }
  5.1714 -  /* free lower case string */
  5.1715 -  pfree(str_lower);
  5.1716 -  /* create cluster from pgl_newentry array */
  5.1717 -  cluster = pgl_new_cluster(nentries, entries);
  5.1718 -  /* free pgl_newentry array */
  5.1719 -  for (i=0; i<nentries; i++) pfree(entries[i].points);
  5.1720 -  pfree(entries);
  5.1721 -  /* set bounding circle of cluster and check east/west orientation */
  5.1722 -  if (!pgl_finalize_cluster(cluster)) {
  5.1723 -    ereport(ERROR, (
  5.1724 -      errcode(ERRCODE_DATA_EXCEPTION),
  5.1725 -      errmsg("can not determine east/west orientation for ecluster"),
  5.1726 -      errhint("Ensure that each entry has a longitude span of less than 180 degrees.")
  5.1727 -    ));
  5.1728 -  }
  5.1729 -  /* return cluster */
  5.1730 -  PG_RETURN_POINTER(cluster);
  5.1731 -  /* code to throw error */
  5.1732 -  pgl_ecluster_in_error:
  5.1733 -  ereport(ERROR, (
  5.1734 -    errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1735 -    errmsg("invalid input syntax for type ecluster: \"%s\"", str)
  5.1736 -  ));
  5.1737 -}
  5.1738 -
  5.1739 -/* convert point ("epoint") to string representation */
  5.1740 -PG_FUNCTION_INFO_V1(pgl_epoint_out);
  5.1741 -Datum pgl_epoint_out(PG_FUNCTION_ARGS) {
  5.1742 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1743 -  char latstr[PGL_NUMBUFLEN];
  5.1744 -  char lonstr[PGL_NUMBUFLEN];
  5.1745 -  pgl_print_lat(latstr, point->lat);
  5.1746 -  pgl_print_lon(lonstr, point->lon);
  5.1747 -  PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr));
  5.1748 -}
  5.1749 -
  5.1750 -/* convert box ("ebox") to string representation */
  5.1751 -PG_FUNCTION_INFO_V1(pgl_ebox_out);
  5.1752 -Datum pgl_ebox_out(PG_FUNCTION_ARGS) {
  5.1753 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  5.1754 -  double lon_min = box->lon_min;
  5.1755 -  double lon_max = box->lon_max;
  5.1756 -  char lat_min_str[PGL_NUMBUFLEN];
  5.1757 -  char lat_max_str[PGL_NUMBUFLEN];
  5.1758 -  char lon_min_str[PGL_NUMBUFLEN];
  5.1759 -  char lon_max_str[PGL_NUMBUFLEN];
  5.1760 -  /* return string "empty" if box is set to be empty */
  5.1761 -  if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty");
  5.1762 -  /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */
  5.1763 -  /* (required since pgl_box_in orders the longitude boundaries) */
  5.1764 -  if (lon_min > lon_max) {
  5.1765 -    if (lon_min + lon_max >= 0) lon_min -= 360;
  5.1766 -    else lon_max += 360;
  5.1767 -  }
  5.1768 -  /* format and return result */
  5.1769 -  pgl_print_lat(lat_min_str, box->lat_min);
  5.1770 -  pgl_print_lat(lat_max_str, box->lat_max);
  5.1771 -  pgl_print_lon(lon_min_str, lon_min);
  5.1772 -  pgl_print_lon(lon_max_str, lon_max);
  5.1773 -  PG_RETURN_CSTRING(psprintf(
  5.1774 -    "%s %s %s %s",
  5.1775 -    lat_min_str, lon_min_str, lat_max_str, lon_max_str
  5.1776 -  ));
  5.1777 -}
  5.1778 -
  5.1779 -/* convert circle ("ecircle") to string representation */
  5.1780 -PG_FUNCTION_INFO_V1(pgl_ecircle_out);
  5.1781 -Datum pgl_ecircle_out(PG_FUNCTION_ARGS) {
  5.1782 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  5.1783 -  char latstr[PGL_NUMBUFLEN];
  5.1784 -  char lonstr[PGL_NUMBUFLEN];
  5.1785 -  char radstr[PGL_NUMBUFLEN];
  5.1786 -  pgl_print_lat(latstr, circle->center.lat);
  5.1787 -  pgl_print_lon(lonstr, circle->center.lon);
  5.1788 -  pgl_print_float(radstr, circle->radius);
  5.1789 -  PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr));
  5.1790 -}
  5.1791 -
  5.1792 -/* convert cluster ("ecluster") to string representation */
  5.1793 -PG_FUNCTION_INFO_V1(pgl_ecluster_out);
  5.1794 -Datum pgl_ecluster_out(PG_FUNCTION_ARGS) {
  5.1795 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  5.1796 -  char latstr[PGL_NUMBUFLEN];  /* string buffer for latitude */
  5.1797 -  char lonstr[PGL_NUMBUFLEN];  /* string buffer for longitude */
  5.1798 -  char ***strings;     /* array of array of strings */
  5.1799 -  char *string;        /* string of current token */
  5.1800 -  char *res, *resptr;  /* result and pointer to current write position */
  5.1801 -  size_t reslen = 1;   /* length of result (init with 1 for terminator) */
  5.1802 -  int npoints;         /* number of points of current entry */
  5.1803 -  int i, j;            /* i: entry, j: point in entry */
  5.1804 -  /* handle empty clusters */
  5.1805 -  if (cluster->nentries == 0) {
  5.1806 -    /* free detoasted cluster (if copy) */
  5.1807 -    PG_FREE_IF_COPY(cluster, 0);
  5.1808 -    /* return static result */
  5.1809 -    PG_RETURN_CSTRING("empty");
  5.1810 -  }
  5.1811 -  /* allocate array of array of strings */
  5.1812 -  strings = palloc(cluster->nentries * sizeof(char **));
  5.1813 -  /* iterate over all entries in cluster */
  5.1814 -  for (i=0; i<cluster->nentries; i++) {
  5.1815 -    /* get number of points in entry */
  5.1816 -    npoints = cluster->entries[i].npoints;
  5.1817 -    /* allocate array of strings (one string for each point plus two extra) */
  5.1818 -    strings[i] = palloc((2 + npoints) * sizeof(char *));
  5.1819 -    /* determine opening string */
  5.1820 -    switch (cluster->entries[i].entrytype) {
  5.1821 -      case PGL_ENTRY_POINT:   string = (i==0)?"point ("  :" point (";   break;
  5.1822 -      case PGL_ENTRY_PATH:    string = (i==0)?"path ("   :" path (";    break;
  5.1823 -      case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break;
  5.1824 -      case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break;
  5.1825 -      default:                string = (i==0)?"unknown"  :" unknown";
  5.1826 -    }
  5.1827 -    /* use opening string as first string in array */
  5.1828 -    strings[i][0] = string;
  5.1829 -    /* update result length (for allocating result string later) */
  5.1830 -    reslen += strlen(string);
  5.1831 -    /* iterate over all points */
  5.1832 -    for (j=0; j<npoints; j++) {
  5.1833 -      /* create string representation of point */
  5.1834 -      pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat);
  5.1835 -      pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon);
  5.1836 -      string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr);
  5.1837 -      /* copy string pointer to string array */
  5.1838 -      strings[i][j+1] = string;
  5.1839 -      /* update result length (for allocating result string later) */
  5.1840 -      reslen += strlen(string);
  5.1841 -    }
  5.1842 -    /* use closing parenthesis as last string in array */
  5.1843 -    strings[i][npoints+1] = ")";
  5.1844 -    /* update result length (for allocating result string later) */
  5.1845 -    reslen++;
  5.1846 -  }
  5.1847 -  /* allocate result string */
  5.1848 -  res = palloc(reslen);
  5.1849 -  /* set write pointer to begin of result string */
  5.1850 -  resptr = res;
  5.1851 -  /* copy strings into result string */
  5.1852 -  for (i=0; i<cluster->nentries; i++) {
  5.1853 -    npoints = cluster->entries[i].npoints;
  5.1854 -    for (j=0; j<npoints+2; j++) {
  5.1855 -      string = strings[i][j];
  5.1856 -      strcpy(resptr, string);
  5.1857 -      resptr += strlen(string);
  5.1858 -      /* free strings allocated by psprintf */
  5.1859 -      if (j != 0 && j != npoints+1) pfree(string);
  5.1860 -    }
  5.1861 -    /* free array of strings */
  5.1862 -    pfree(strings[i]);
  5.1863 -  }
  5.1864 -  /* free array of array of strings */
  5.1865 -  pfree(strings);
  5.1866 -  /* free detoasted cluster (if copy) */
  5.1867 -  PG_FREE_IF_COPY(cluster, 0);
  5.1868 -  /* return result */
  5.1869 -  PG_RETURN_CSTRING(res);
  5.1870 -}
  5.1871 -
  5.1872 -/* binary input function for point ("epoint") */
  5.1873 -PG_FUNCTION_INFO_V1(pgl_epoint_recv);
  5.1874 -Datum pgl_epoint_recv(PG_FUNCTION_ARGS) {
  5.1875 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  5.1876 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  5.1877 -  point->lat = pq_getmsgfloat8(buf);
  5.1878 -  point->lon = pq_getmsgfloat8(buf);
  5.1879 -  PG_RETURN_POINTER(point);
  5.1880 -}
  5.1881 -
  5.1882 -/* binary input function for box ("ebox") */
  5.1883 -PG_FUNCTION_INFO_V1(pgl_ebox_recv);
  5.1884 -Datum pgl_ebox_recv(PG_FUNCTION_ARGS) {
  5.1885 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  5.1886 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1887 -  box->lat_min = pq_getmsgfloat8(buf);
  5.1888 -  box->lat_max = pq_getmsgfloat8(buf);
  5.1889 -  box->lon_min = pq_getmsgfloat8(buf);
  5.1890 -  box->lon_max = pq_getmsgfloat8(buf);
  5.1891 -  PG_RETURN_POINTER(box);
  5.1892 -}
  5.1893 -
  5.1894 -/* binary input function for circle ("ecircle") */
  5.1895 -PG_FUNCTION_INFO_V1(pgl_ecircle_recv);
  5.1896 -Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) {
  5.1897 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  5.1898 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  5.1899 -  circle->center.lat = pq_getmsgfloat8(buf);
  5.1900 -  circle->center.lon = pq_getmsgfloat8(buf);
  5.1901 -  circle->radius = pq_getmsgfloat8(buf);
  5.1902 -  PG_RETURN_POINTER(circle);
  5.1903 -}
  5.1904 -
  5.1905 -/* TODO: binary receive function for cluster */
  5.1906 -
  5.1907 -/* binary output function for point ("epoint") */
  5.1908 -PG_FUNCTION_INFO_V1(pgl_epoint_send);
  5.1909 -Datum pgl_epoint_send(PG_FUNCTION_ARGS) {
  5.1910 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1911 -  StringInfoData buf;
  5.1912 -  pq_begintypsend(&buf);
  5.1913 -  pq_sendfloat8(&buf, point->lat);
  5.1914 -  pq_sendfloat8(&buf, point->lon);
  5.1915 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  5.1916 -}
  5.1917 -
  5.1918 -/* binary output function for box ("ebox") */
  5.1919 -PG_FUNCTION_INFO_V1(pgl_ebox_send);
  5.1920 -Datum pgl_ebox_send(PG_FUNCTION_ARGS) {
  5.1921 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  5.1922 -  StringInfoData buf;
  5.1923 -  pq_begintypsend(&buf);
  5.1924 -  pq_sendfloat8(&buf, box->lat_min);
  5.1925 -  pq_sendfloat8(&buf, box->lat_max);
  5.1926 -  pq_sendfloat8(&buf, box->lon_min);
  5.1927 -  pq_sendfloat8(&buf, box->lon_max);
  5.1928 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  5.1929 -}
  5.1930 -
  5.1931 -/* binary output function for circle ("ecircle") */
  5.1932 -PG_FUNCTION_INFO_V1(pgl_ecircle_send);
  5.1933 -Datum pgl_ecircle_send(PG_FUNCTION_ARGS) {
  5.1934 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  5.1935 -  StringInfoData buf;
  5.1936 -  pq_begintypsend(&buf);
  5.1937 -  pq_sendfloat8(&buf, circle->center.lat);
  5.1938 -  pq_sendfloat8(&buf, circle->center.lon);
  5.1939 -  pq_sendfloat8(&buf, circle->radius);
  5.1940 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  5.1941 -}
  5.1942 -
  5.1943 -/* TODO: binary send functions for cluster */
  5.1944 -
  5.1945 -/* cast point ("epoint") to box ("ebox") */
  5.1946 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox);
  5.1947 -Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) {
  5.1948 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1949 -  pgl_box *box = palloc(sizeof(pgl_box));
  5.1950 -  box->lat_min = point->lat;
  5.1951 -  box->lat_max = point->lat;
  5.1952 -  box->lon_min = point->lon;
  5.1953 -  box->lon_max = point->lon;
  5.1954 -  PG_RETURN_POINTER(box);
  5.1955 -}
  5.1956 -
  5.1957 -/* cast point ("epoint") to circle ("ecircle") */
  5.1958 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle);
  5.1959 -Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) {
  5.1960 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1961 -  pgl_circle *circle = palloc(sizeof(pgl_box));
  5.1962 -  circle->center = *point;
  5.1963 -  circle->radius = 0;
  5.1964 -  PG_RETURN_POINTER(circle);
  5.1965 -}
  5.1966 -
  5.1967 -/* cast point ("epoint") to cluster ("ecluster") */
  5.1968 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster);
  5.1969 -Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) {
  5.1970 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1971 -  pgl_newentry entry;
  5.1972 -  entry.entrytype = PGL_ENTRY_POINT;
  5.1973 -  entry.npoints = 1;
  5.1974 -  entry.points = point;
  5.1975 -  PG_RETURN_POINTER(pgl_new_cluster(1, &entry));
  5.1976 -}
  5.1977 -
  5.1978 -/* cast box ("ebox") to cluster ("ecluster") */
  5.1979 -#define pgl_ebox_to_ecluster_macro(i, a, b) \
  5.1980 -  entries[i].entrytype = PGL_ENTRY_POLYGON; \
  5.1981 -  entries[i].npoints = 4; \
  5.1982 -  entries[i].points = points[i]; \
  5.1983 -  points[i][0].lat = box->lat_min; \
  5.1984 -  points[i][0].lon = (a); \
  5.1985 -  points[i][1].lat = box->lat_min; \
  5.1986 -  points[i][1].lon = (b); \
  5.1987 -  points[i][2].lat = box->lat_max; \
  5.1988 -  points[i][2].lon = (b); \
  5.1989 -  points[i][3].lat = box->lat_max; \
  5.1990 -  points[i][3].lon = (a);
  5.1991 -PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster);
  5.1992 -Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) {
  5.1993 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  5.1994 -  double lon, dlon;
  5.1995 -  int nentries;
  5.1996 -  pgl_newentry entries[3];
  5.1997 -  pgl_point points[3][4];
  5.1998 -  if (box->lat_min > box->lat_max) {
  5.1999 -    nentries = 0;
  5.2000 -  } else if (box->lon_min > box->lon_max) {
  5.2001 -    if (box->lon_min < 0) {
  5.2002 -      lon = pgl_round((box->lon_min + 180) / 2.0);
  5.2003 -      nentries = 3;
  5.2004 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  5.2005 -      pgl_ebox_to_ecluster_macro(1, lon, 180);
  5.2006 -      pgl_ebox_to_ecluster_macro(2, -180, box->lon_max);
  5.2007 -    } else if (box->lon_max > 0) {
  5.2008 -      lon = pgl_round((box->lon_max - 180) / 2.0);
  5.2009 -      nentries = 3;
  5.2010 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  5.2011 -      pgl_ebox_to_ecluster_macro(1, -180, lon);
  5.2012 -      pgl_ebox_to_ecluster_macro(2, lon, box->lon_max);
  5.2013 -    } else {
  5.2014 -      nentries = 2;
  5.2015 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  5.2016 -      pgl_ebox_to_ecluster_macro(1, -180, box->lon_max);
  5.2017 -    }
  5.2018 -  } else {
  5.2019 -    dlon = pgl_round(box->lon_max - box->lon_min);
  5.2020 -    if (dlon < 180) {
  5.2021 -      nentries = 1;
  5.2022 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max);
  5.2023 -    } else {
  5.2024 -      lon = pgl_round((box->lon_min + box->lon_max) / 2.0);
  5.2025 -      if (
  5.2026 -        pgl_round(lon - box->lon_min) < 180 &&
  5.2027 -        pgl_round(box->lon_max - lon) < 180
  5.2028 -      ) {
  5.2029 -        nentries = 2;
  5.2030 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  5.2031 -        pgl_ebox_to_ecluster_macro(1, lon, box->lon_max);
  5.2032 -      } else {
  5.2033 -        nentries = 3;
  5.2034 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, -60);
  5.2035 -        pgl_ebox_to_ecluster_macro(1, -60, 60);
  5.2036 -        pgl_ebox_to_ecluster_macro(2, 60, box->lon_max);
  5.2037 -      }
  5.2038 -    }
  5.2039 -  }
  5.2040 -  PG_RETURN_POINTER(pgl_new_cluster(nentries, entries));
  5.2041 -}
  5.2042 -
  5.2043 -/* extract latitude from point ("epoint") */
  5.2044 -PG_FUNCTION_INFO_V1(pgl_epoint_lat);
  5.2045 -Datum pgl_epoint_lat(PG_FUNCTION_ARGS) {
  5.2046 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat);
  5.2047 -}
  5.2048 -
  5.2049 -/* extract longitude from point ("epoint") */
  5.2050 -PG_FUNCTION_INFO_V1(pgl_epoint_lon);
  5.2051 -Datum pgl_epoint_lon(PG_FUNCTION_ARGS) {
  5.2052 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon);
  5.2053 -}
  5.2054 -
  5.2055 -/* extract minimum latitude from box ("ebox") */
  5.2056 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_min);
  5.2057 -Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) {
  5.2058 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min);
  5.2059 -}
  5.2060 -
  5.2061 -/* extract maximum latitude from box ("ebox") */
  5.2062 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_max);
  5.2063 -Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) {
  5.2064 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max);
  5.2065 -}
  5.2066 -
  5.2067 -/* extract minimum longitude from box ("ebox") */
  5.2068 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_min);
  5.2069 -Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) {
  5.2070 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min);
  5.2071 -}
  5.2072 -
  5.2073 -/* extract maximum longitude from box ("ebox") */
  5.2074 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_max);
  5.2075 -Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) {
  5.2076 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max);
  5.2077 -}
  5.2078 -
  5.2079 -/* extract center point from circle ("ecircle") */
  5.2080 -PG_FUNCTION_INFO_V1(pgl_ecircle_center);
  5.2081 -Datum pgl_ecircle_center(PG_FUNCTION_ARGS) {
  5.2082 -  PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center));
  5.2083 -}
  5.2084 -
  5.2085 -/* extract radius from circle ("ecircle") */
  5.2086 -PG_FUNCTION_INFO_V1(pgl_ecircle_radius);
  5.2087 -Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) {
  5.2088 -  PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius);
  5.2089 -}
  5.2090 -
  5.2091 -/* check if point is inside box (overlap operator "&&") in SQL */
  5.2092 -PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap);
  5.2093 -Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) {
  5.2094 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2095 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1);
  5.2096 -  PG_RETURN_BOOL(pgl_point_in_box(point, box));
  5.2097 -}
  5.2098 -
  5.2099 -/* check if point is inside circle (overlap operator "&&") in SQL */
  5.2100 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap);
  5.2101 -Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) {
  5.2102 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2103 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2104 -  PG_RETURN_BOOL(
  5.2105 -    pgl_distance(
  5.2106 -      point->lat, point->lon,
  5.2107 -      circle->center.lat, circle->center.lon
  5.2108 -    ) <= circle->radius
  5.2109 -  );
  5.2110 -}
  5.2111 -
  5.2112 -/* check if point is inside cluster (overlap operator "&&") in SQL */
  5.2113 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap);
  5.2114 -Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
  5.2115 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2116 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2117 -  bool retval = pgl_point_in_cluster(point, cluster);
  5.2118 -  PG_FREE_IF_COPY(cluster, 1);
  5.2119 -  PG_RETURN_BOOL(retval);
  5.2120 -}
  5.2121 -
  5.2122 -/* check if two boxes overlap (overlap operator "&&") in SQL */
  5.2123 -PG_FUNCTION_INFO_V1(pgl_ebox_overlap);
  5.2124 -Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) {
  5.2125 -  pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0);
  5.2126 -  pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1);
  5.2127 -  PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2));
  5.2128 -}
  5.2129 -
  5.2130 -/* check if two circles overlap (overlap operator "&&") in SQL */
  5.2131 -PG_FUNCTION_INFO_V1(pgl_ecircle_overlap);
  5.2132 -Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) {
  5.2133 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  5.2134 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2135 -  PG_RETURN_BOOL(
  5.2136 -    pgl_distance(
  5.2137 -      circle1->center.lat, circle1->center.lon,
  5.2138 -      circle2->center.lat, circle2->center.lon
  5.2139 -    ) <= circle1->radius + circle2->radius
  5.2140 -  );
  5.2141 -}
  5.2142 -
  5.2143 -/* check if circle and cluster overlap (overlap operator "&&") in SQL */
  5.2144 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap);
  5.2145 -Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) {
  5.2146 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  5.2147 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2148 -  bool retval = (
  5.2149 -    pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius
  5.2150 -  );
  5.2151 -  PG_FREE_IF_COPY(cluster, 1);
  5.2152 -  PG_RETURN_BOOL(retval);
  5.2153 -}
  5.2154 -
  5.2155 -/* calculate distance between two points ("<->" operator) in SQL */
  5.2156 -PG_FUNCTION_INFO_V1(pgl_epoint_distance);
  5.2157 -Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
  5.2158 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  5.2159 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  5.2160 -  PG_RETURN_FLOAT8(pgl_distance(
  5.2161 -    point1->lat, point1->lon, point2->lat, point2->lon
  5.2162 -  ));
  5.2163 -}
  5.2164 -
  5.2165 -/* calculate point to circle distance ("<->" operator) in SQL */
  5.2166 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance);
  5.2167 -Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) {
  5.2168 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2169 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2170 -  double distance = pgl_distance(
  5.2171 -    point->lat, point->lon, circle->center.lat, circle->center.lon
  5.2172 -  ) - circle->radius;
  5.2173 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  5.2174 -}
  5.2175 -
  5.2176 -/* calculate point to cluster distance ("<->" operator) in SQL */
  5.2177 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance);
  5.2178 -Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) {
  5.2179 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2180 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2181 -  double distance = pgl_point_cluster_distance(point, cluster);
  5.2182 -  PG_FREE_IF_COPY(cluster, 1);
  5.2183 -  PG_RETURN_FLOAT8(distance);
  5.2184 -}
  5.2185 -
  5.2186 -/* calculate distance between two circles ("<->" operator) in SQL */
  5.2187 -PG_FUNCTION_INFO_V1(pgl_ecircle_distance);
  5.2188 -Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) {
  5.2189 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  5.2190 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2191 -  double distance = pgl_distance(
  5.2192 -    circle1->center.lat, circle1->center.lon,
  5.2193 -    circle2->center.lat, circle2->center.lon
  5.2194 -  ) - (circle1->radius + circle2->radius);
  5.2195 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  5.2196 -}
  5.2197 -
  5.2198 -/* calculate circle to cluster distance ("<->" operator) in SQL */
  5.2199 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance);
  5.2200 -Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) {
  5.2201 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  5.2202 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2203 -  double distance = (
  5.2204 -    pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius
  5.2205 -  );
  5.2206 -  PG_FREE_IF_COPY(cluster, 1);
  5.2207 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  5.2208 -}
  5.2209 -
  5.2210 -
  5.2211 -/*-----------------------------------------------------------*
  5.2212 - *  B-tree comparison operators and index support functions  *
  5.2213 - *-----------------------------------------------------------*/
  5.2214 -
  5.2215 -/* macro for a B-tree operator (without detoasting) */
  5.2216 -#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \
  5.2217 -  PG_FUNCTION_INFO_V1(func); \
  5.2218 -  Datum func(PG_FUNCTION_ARGS) { \
  5.2219 -    type *a = (type *)PG_GETARG_POINTER(0); \
  5.2220 -    type *b = (type *)PG_GETARG_POINTER(1); \
  5.2221 -    PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \
  5.2222 -  }
  5.2223 -
  5.2224 -/* macro for a B-tree comparison function (without detoasting) */
  5.2225 -#define PGL_BTREE_CMP(func, type, cmpfunc) \
  5.2226 -  PG_FUNCTION_INFO_V1(func); \
  5.2227 -  Datum func(PG_FUNCTION_ARGS) { \
  5.2228 -    type *a = (type *)PG_GETARG_POINTER(0); \
  5.2229 -    type *b = (type *)PG_GETARG_POINTER(1); \
  5.2230 -    PG_RETURN_INT32(cmpfunc(a, b)); \
  5.2231 -  }
  5.2232 -
  5.2233 -/* macro for a B-tree operator (with detoasting) */
  5.2234 -#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \
  5.2235 -  PG_FUNCTION_INFO_V1(func); \
  5.2236 -  Datum func(PG_FUNCTION_ARGS) { \
  5.2237 -    bool res; \
  5.2238 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  5.2239 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  5.2240 -    res = cmpfunc(a, b) oper 0; \
  5.2241 -    PG_FREE_IF_COPY(a, 0); \
  5.2242 -    PG_FREE_IF_COPY(b, 1); \
  5.2243 -    PG_RETURN_BOOL(res); \
  5.2244 -  }
  5.2245 -
  5.2246 -/* macro for a B-tree comparison function (with detoasting) */
  5.2247 -#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \
  5.2248 -  PG_FUNCTION_INFO_V1(func); \
  5.2249 -  Datum func(PG_FUNCTION_ARGS) { \
  5.2250 -    int32_t res; \
  5.2251 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  5.2252 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  5.2253 -    res = cmpfunc(a, b); \
  5.2254 -    PG_FREE_IF_COPY(a, 0); \
  5.2255 -    PG_FREE_IF_COPY(b, 1); \
  5.2256 -    PG_RETURN_INT32(res); \
  5.2257 -  }
  5.2258 -
  5.2259 -/* B-tree operators and comparison function for point */
  5.2260 -PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <)
  5.2261 -PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=)
  5.2262 -PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==)
  5.2263 -PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=)
  5.2264 -PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=)
  5.2265 -PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >)
  5.2266 -PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp)
  5.2267 -
  5.2268 -/* B-tree operators and comparison function for box */
  5.2269 -PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <)
  5.2270 -PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=)
  5.2271 -PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==)
  5.2272 -PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=)
  5.2273 -PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=)
  5.2274 -PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >)
  5.2275 -PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp)
  5.2276 -
  5.2277 -/* B-tree operators and comparison function for circle */
  5.2278 -PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <)
  5.2279 -PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=)
  5.2280 -PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==)
  5.2281 -PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=)
  5.2282 -PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=)
  5.2283 -PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >)
  5.2284 -PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp)
  5.2285 -
  5.2286 -
  5.2287 -/*--------------------------------*
  5.2288 - *  GiST index support functions  *
  5.2289 - *--------------------------------*/
  5.2290 -
  5.2291 -/* GiST "consistent" support function */
  5.2292 -PG_FUNCTION_INFO_V1(pgl_gist_consistent);
  5.2293 -Datum pgl_gist_consistent(PG_FUNCTION_ARGS) {
  5.2294 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  5.2295 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  5.2296 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  5.2297 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  5.2298 -  /* demand recheck because index and query methods are lossy */
  5.2299 -  *recheck = true;
  5.2300 -  /* strategy number 11: equality of two points */
  5.2301 -  if (strategy == 11) {
  5.2302 -    /* query datum is another point */
  5.2303 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  5.2304 -    /* convert other point to key */
  5.2305 -    pgl_pointkey querykey;
  5.2306 -    pgl_point_to_key(query, querykey);
  5.2307 -    /* return true if both keys overlap */
  5.2308 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  5.2309 -  }
  5.2310 -  /* strategy number 13: equality of two circles */
  5.2311 -  if (strategy == 13) {
  5.2312 -    /* query datum is another circle */
  5.2313 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2314 -    /* convert other circle to key */
  5.2315 -    pgl_areakey querykey;
  5.2316 -    pgl_circle_to_key(query, querykey);
  5.2317 -    /* return true if both keys overlap */
  5.2318 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  5.2319 -  }
  5.2320 -  /* for all remaining strategies, keys on empty objects produce no match */
  5.2321 -  /* (check necessary because query radius may be infinite) */
  5.2322 -  if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false);
  5.2323 -  /* strategy number 21: overlapping with point */
  5.2324 -  if (strategy == 21) {
  5.2325 -    /* query datum is a point */
  5.2326 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  5.2327 -    /* return true if estimated distance (allowed to be smaller than real
  5.2328 -       distance) between index key and point is zero */
  5.2329 -    PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0);
  5.2330 -  }
  5.2331 -  /* strategy number 22: (point) overlapping with box */
  5.2332 -  if (strategy == 22) {
  5.2333 -    /* query datum is a box */
  5.2334 -    pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1);
  5.2335 -    /* determine bounding box of indexed key */
  5.2336 -    pgl_box keybox;
  5.2337 -    pgl_key_to_box(key, &keybox);
  5.2338 -    /* return true if query box overlaps with bounding box of indexed key */
  5.2339 -    PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox));
  5.2340 -  }
  5.2341 -  /* strategy number 23: overlapping with circle */
  5.2342 -  if (strategy == 23) {
  5.2343 -    /* query datum is a circle */
  5.2344 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2345 -    /* return true if estimated distance (allowed to be smaller than real
  5.2346 -       distance) between index key and circle center is smaller than radius */
  5.2347 -    PG_RETURN_BOOL(
  5.2348 -      pgl_estimate_key_distance(key, &(query->center)) <= query->radius
  5.2349 -    );
  5.2350 -  }
  5.2351 -  /* strategy number 24: overlapping with cluster */
  5.2352 -  if (strategy == 24) {
  5.2353 -    bool retval;  /* return value */
  5.2354 -    /* query datum is a cluster */
  5.2355 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2356 -    /* return true if estimated distance (allowed to be smaller than real
  5.2357 -       distance) between index key and circle center is smaller than radius */
  5.2358 -    retval = (
  5.2359 -      pgl_estimate_key_distance(key, &(query->bounding.center)) <=
  5.2360 -      query->bounding.radius
  5.2361 -    );
  5.2362 -    PG_FREE_IF_COPY(query, 1);  /* free detoasted cluster (if copy) */
  5.2363 -    PG_RETURN_BOOL(retval);
  5.2364 -  }
  5.2365 -  /* throw error for any unknown strategy number */
  5.2366 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  5.2367 -}
  5.2368 -
  5.2369 -/* GiST "union" support function */
  5.2370 -PG_FUNCTION_INFO_V1(pgl_gist_union);
  5.2371 -Datum pgl_gist_union(PG_FUNCTION_ARGS) {
  5.2372 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  5.2373 -  pgl_keyptr out;  /* return value (to be palloc'ed) */
  5.2374 -  int i;
  5.2375 -  /* determine key size */
  5.2376 -  size_t keysize = PGL_KEY_IS_AREAKEY(
  5.2377 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key)
  5.2378 -  ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey);
  5.2379 -  /* begin with first key as result */
  5.2380 -  out = palloc(keysize);
  5.2381 -  memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize);
  5.2382 -  /* unite current result with second, third, etc. key */
  5.2383 -  for (i=1; i<entryvec->n; i++) {
  5.2384 -    pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key));
  5.2385 -  }
  5.2386 -  /* return result */
  5.2387 -  PG_RETURN_POINTER(out);
  5.2388 -}
  5.2389 -
  5.2390 -/* GiST "compress" support function for indicis on points */
  5.2391 -PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint);
  5.2392 -Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) {
  5.2393 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  5.2394 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  5.2395 -  /* only transform new leaves */
  5.2396 -  if (entry->leafkey) {
  5.2397 -    /* get point to be transformed */
  5.2398 -    pgl_point *point = (pgl_point *)DatumGetPointer(entry->key);
  5.2399 -    /* allocate memory for key */
  5.2400 -    pgl_keyptr key = palloc(sizeof(pgl_pointkey));
  5.2401 -    /* transform point to key */
  5.2402 -    pgl_point_to_key(point, key);
  5.2403 -    /* create new GISTENTRY structure as return value */
  5.2404 -    retval = palloc(sizeof(GISTENTRY));
  5.2405 -    gistentryinit(
  5.2406 -      *retval, PointerGetDatum(key),
  5.2407 -      entry->rel, entry->page, entry->offset, FALSE
  5.2408 -    );
  5.2409 -  } else {
  5.2410 -    /* inner nodes have already been transformed */
  5.2411 -    retval = entry;
  5.2412 -  }
  5.2413 -  /* return pointer to old or new GISTENTRY structure */
  5.2414 -  PG_RETURN_POINTER(retval);
  5.2415 -}
  5.2416 -
  5.2417 -/* GiST "compress" support function for indicis on circles */
  5.2418 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle);
  5.2419 -Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) {
  5.2420 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  5.2421 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  5.2422 -  /* only transform new leaves */
  5.2423 -  if (entry->leafkey) {
  5.2424 -    /* get circle to be transformed */
  5.2425 -    pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key);
  5.2426 -    /* allocate memory for key */
  5.2427 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  5.2428 -    /* transform circle to key */
  5.2429 -    pgl_circle_to_key(circle, key);
  5.2430 -    /* create new GISTENTRY structure as return value */
  5.2431 -    retval = palloc(sizeof(GISTENTRY));
  5.2432 -    gistentryinit(
  5.2433 -      *retval, PointerGetDatum(key),
  5.2434 -      entry->rel, entry->page, entry->offset, FALSE
  5.2435 -    );
  5.2436 -  } else {
  5.2437 -    /* inner nodes have already been transformed */
  5.2438 -    retval = entry;
  5.2439 -  }
  5.2440 -  /* return pointer to old or new GISTENTRY structure */
  5.2441 -  PG_RETURN_POINTER(retval);
  5.2442 -}
  5.2443 -
  5.2444 -/* GiST "compress" support function for indices on clusters */
  5.2445 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster);
  5.2446 -Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) {
  5.2447 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  5.2448 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  5.2449 -  /* only transform new leaves */
  5.2450 -  if (entry->leafkey) {
  5.2451 -    /* get cluster to be transformed (detoasting necessary!) */
  5.2452 -    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key);
  5.2453 -    /* allocate memory for key */
  5.2454 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  5.2455 -    /* transform cluster to key */
  5.2456 -    pgl_circle_to_key(&(cluster->bounding), key);
  5.2457 -    /* create new GISTENTRY structure as return value */
  5.2458 -    retval = palloc(sizeof(GISTENTRY));
  5.2459 -    gistentryinit(
  5.2460 -      *retval, PointerGetDatum(key),
  5.2461 -      entry->rel, entry->page, entry->offset, FALSE
  5.2462 -    );
  5.2463 -    /* free detoasted datum */
  5.2464 -    if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster);
  5.2465 -  } else {
  5.2466 -    /* inner nodes have already been transformed */
  5.2467 -    retval = entry;
  5.2468 -  }
  5.2469 -  /* return pointer to old or new GISTENTRY structure */
  5.2470 -  PG_RETURN_POINTER(retval);
  5.2471 -}
  5.2472 -
  5.2473 -/* GiST "decompress" support function for indices */
  5.2474 -PG_FUNCTION_INFO_V1(pgl_gist_decompress);
  5.2475 -Datum pgl_gist_decompress(PG_FUNCTION_ARGS) {
  5.2476 -  /* return passed pointer without transformation */
  5.2477 -  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
  5.2478 -}
  5.2479 -
  5.2480 -/* GiST "penalty" support function */
  5.2481 -PG_FUNCTION_INFO_V1(pgl_gist_penalty);
  5.2482 -Datum pgl_gist_penalty(PG_FUNCTION_ARGS) {
  5.2483 -  GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0);
  5.2484 -  GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1);
  5.2485 -  float *penalty = (float *)PG_GETARG_POINTER(2);
  5.2486 -  /* get original key and key to insert */
  5.2487 -  pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key);
  5.2488 -  pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key);
  5.2489 -  /* copy original key */
  5.2490 -  union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key;
  5.2491 -  if (PGL_KEY_IS_AREAKEY(orig)) {
  5.2492 -    memcpy(union_key.areakey, orig, sizeof(union_key.areakey));
  5.2493 -  } else {
  5.2494 -    memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey));
  5.2495 -  }
  5.2496 -  /* calculate union of both keys */
  5.2497 -  pgl_unite_keys((pgl_keyptr)&union_key, new);
  5.2498 -  /* penalty equal to reduction of key length (logarithm of added area) */
  5.2499 -  /* (return value by setting referenced value and returning pointer) */
  5.2500 -  *penalty = (
  5.2501 -    PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key)
  5.2502 -  );
  5.2503 -  PG_RETURN_POINTER(penalty);
  5.2504 -}
  5.2505 -
  5.2506 -/* GiST "picksplit" support function */
  5.2507 -PG_FUNCTION_INFO_V1(pgl_gist_picksplit);
  5.2508 -Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) {
  5.2509 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  5.2510 -  GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1);
  5.2511 -  OffsetNumber i;  /* between FirstOffsetNumber and entryvec->n (inclusive) */
  5.2512 -  union {
  5.2513 -    pgl_pointkey pointkey;
  5.2514 -    pgl_areakey areakey;
  5.2515 -  } union_all;  /* union of all keys (to be calculated from scratch)
  5.2516 -                   (later cut in half) */
  5.2517 -  int is_areakey = PGL_KEY_IS_AREAKEY(
  5.2518 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key)
  5.2519 -  );
  5.2520 -  int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey);
  5.2521 -  pgl_keyptr unionL = palloc(keysize);  /* union of keys that go left */
  5.2522 -  pgl_keyptr unionR = palloc(keysize);  /* union of keys that go right */
  5.2523 -  pgl_keyptr key;  /* current key to be processed */
  5.2524 -  /* allocate memory for array of left and right keys, set counts to zero */
  5.2525 -  v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  5.2526 -  v->spl_nleft = 0;
  5.2527 -  v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  5.2528 -  v->spl_nright = 0;
  5.2529 -  /* calculate union of all keys from scratch */
  5.2530 -  memcpy(
  5.2531 -    (pgl_keyptr)&union_all,
  5.2532 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key),
  5.2533 -    keysize
  5.2534 -  );
  5.2535 -  for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) {
  5.2536 -    pgl_unite_keys(
  5.2537 -      (pgl_keyptr)&union_all,
  5.2538 -      (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)
  5.2539 -    );
  5.2540 -  }
  5.2541 -  /* check if trivial split is necessary due to exhausted key length */
  5.2542 -  /* (Note: keys for empty objects must have node depth set to maximum) */
  5.2543 -  if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == (
  5.2544 -    is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH
  5.2545 -  )) {
  5.2546 -    /* half of all keys go left */
  5.2547 -    for (
  5.2548 -      i=FirstOffsetNumber;
  5.2549 -      i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  5.2550 -      i=OffsetNumberNext(i)
  5.2551 -    ) {
  5.2552 -      /* pointer to current key */
  5.2553 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  5.2554 -      /* update unionL */
  5.2555 -      /* check if key is first key that goes left */
  5.2556 -      if (!v->spl_nleft) {
  5.2557 -        /* first key that goes left is just copied to unionL */
  5.2558 -        memcpy(unionL, key, keysize);
  5.2559 -      } else {
  5.2560 -        /* unite current value and next key */
  5.2561 -        pgl_unite_keys(unionL, key);
  5.2562 -      }
  5.2563 -      /* append offset number to list of keys that go left */
  5.2564 -      v->spl_left[v->spl_nleft++] = i;
  5.2565 -    }
  5.2566 -    /* other half goes right */
  5.2567 -    for (
  5.2568 -      i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  5.2569 -      i<entryvec->n;
  5.2570 -      i=OffsetNumberNext(i)
  5.2571 -    ) {
  5.2572 -      /* pointer to current key */
  5.2573 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  5.2574 -      /* update unionR */
  5.2575 -      /* check if key is first key that goes right */
  5.2576 -      if (!v->spl_nright) {
  5.2577 -        /* first key that goes right is just copied to unionR */
  5.2578 -        memcpy(unionR, key, keysize);
  5.2579 -      } else {
  5.2580 -        /* unite current value and next key */
  5.2581 -        pgl_unite_keys(unionR, key);
  5.2582 -      }
  5.2583 -      /* append offset number to list of keys that go right */
  5.2584 -      v->spl_right[v->spl_nright++] = i;
  5.2585 -    }
  5.2586 -  }
  5.2587 -  /* otherwise, a non-trivial split is possible */
  5.2588 -  else {
  5.2589 -    /* cut covered area in half */
  5.2590 -    /* (union_all then refers to area of keys that go left) */
  5.2591 -    /* check if union of all keys covers empty and non-empty objects */
  5.2592 -    if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) {
  5.2593 -      /* if yes, split into empty and non-empty objects */
  5.2594 -      pgl_key_set_empty((pgl_keyptr)&union_all);
  5.2595 -    } else {
  5.2596 -      /* otherwise split by next bit */
  5.2597 -      ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++;
  5.2598 -      /* NOTE: type bit conserved */
  5.2599 -    }
  5.2600 -    /* determine for each key if it goes left or right */
  5.2601 -    for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) {
  5.2602 -      /* pointer to current key */
  5.2603 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  5.2604 -      /* keys within one half of the area go left */
  5.2605 -      if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) {
  5.2606 -        /* update unionL */
  5.2607 -        /* check if key is first key that goes left */
  5.2608 -        if (!v->spl_nleft) {
  5.2609 -          /* first key that goes left is just copied to unionL */
  5.2610 -          memcpy(unionL, key, keysize);
  5.2611 -        } else {
  5.2612 -          /* unite current value of unionL and processed key */
  5.2613 -          pgl_unite_keys(unionL, key);
  5.2614 -        }
  5.2615 -        /* append offset number to list of keys that go left */
  5.2616 -        v->spl_left[v->spl_nleft++] = i;
  5.2617 -      }
  5.2618 -      /* the other keys go right */
  5.2619 -      else {
  5.2620 -        /* update unionR */
  5.2621 -        /* check if key is first key that goes right */
  5.2622 -        if (!v->spl_nright) {
  5.2623 -          /* first key that goes right is just copied to unionR */
  5.2624 -          memcpy(unionR, key, keysize);
  5.2625 -        } else {
  5.2626 -          /* unite current value of unionR and processed key */
  5.2627 -          pgl_unite_keys(unionR, key);
  5.2628 -        }
  5.2629 -        /* append offset number to list of keys that go right */
  5.2630 -        v->spl_right[v->spl_nright++] = i;
  5.2631 -      }
  5.2632 -    }
  5.2633 -  }
  5.2634 -  /* store unions in return value */
  5.2635 -  v->spl_ldatum = PointerGetDatum(unionL);
  5.2636 -  v->spl_rdatum = PointerGetDatum(unionR);
  5.2637 -  /* return all results */
  5.2638 -  PG_RETURN_POINTER(v);
  5.2639 -}
  5.2640 -
  5.2641 -/* GiST "same"/"equal" support function */
  5.2642 -PG_FUNCTION_INFO_V1(pgl_gist_same);
  5.2643 -Datum pgl_gist_same(PG_FUNCTION_ARGS) {
  5.2644 -  pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0);
  5.2645 -  pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1);
  5.2646 -  bool *boolptr = (bool *)PG_GETARG_POINTER(2);
  5.2647 -  /* two keys are equal if they are binary equal */
  5.2648 -  /* (return result by setting referenced boolean and returning pointer) */
  5.2649 -  *boolptr = !memcmp(
  5.2650 -    key1,
  5.2651 -    key2,
  5.2652 -    PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey)
  5.2653 -  );
  5.2654 -  PG_RETURN_POINTER(boolptr);
  5.2655 -}
  5.2656 -
  5.2657 -/* GiST "distance" support function */
  5.2658 -PG_FUNCTION_INFO_V1(pgl_gist_distance);
  5.2659 -Datum pgl_gist_distance(PG_FUNCTION_ARGS) {
  5.2660 -  GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
  5.2661 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  5.2662 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  5.2663 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  5.2664 -  double distance;  /* return value */
  5.2665 -  /* demand recheck because distance is just an estimation */
  5.2666 -  /* (real distance may be bigger) */
  5.2667 -  *recheck = true;
  5.2668 -  /* strategy number 31: distance to point */
  5.2669 -  if (strategy == 31) {
  5.2670 -    /* query datum is a point */
  5.2671 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  5.2672 -    /* use pgl_estimate_pointkey_distance() function to compute result */
  5.2673 -    distance = pgl_estimate_key_distance(key, query);
  5.2674 -    /* avoid infinity (reserved!) */
  5.2675 -    if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  5.2676 -    /* return result */
  5.2677 -    PG_RETURN_FLOAT8(distance);
  5.2678 -  }
  5.2679 -  /* strategy number 33: distance to circle */
  5.2680 -  if (strategy == 33) {
  5.2681 -    /* query datum is a circle */
  5.2682 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2683 -    /* estimate distance to circle center and substract circle radius */
  5.2684 -    distance = (
  5.2685 -      pgl_estimate_key_distance(key, &(query->center)) - query->radius
  5.2686 -    );
  5.2687 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  5.2688 -    if (distance <= 0) distance = 0;
  5.2689 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  5.2690 -    /* return result */
  5.2691 -    PG_RETURN_FLOAT8(distance);
  5.2692 -  }
  5.2693 -  /* strategy number 34: distance to cluster */
  5.2694 -  if (strategy == 34) {
  5.2695 -    /* query datum is a cluster */
  5.2696 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2697 -    /* estimate distance to bounding center and substract bounding radius */
  5.2698 -    distance = (
  5.2699 -      pgl_estimate_key_distance(key, &(query->bounding.center)) -
  5.2700 -      query->bounding.radius
  5.2701 -    );
  5.2702 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  5.2703 -    if (distance <= 0) distance = 0;
  5.2704 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  5.2705 -    /* free detoasted cluster (if copy) */
  5.2706 -    PG_FREE_IF_COPY(query, 1);
  5.2707 -    /* return result */
  5.2708 -    PG_RETURN_FLOAT8(distance);
  5.2709 -  }
  5.2710 -  /* throw error for any unknown strategy number */
  5.2711 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  5.2712 -}
  5.2713 -

Impressum / About Us