pgLatLon

changeset 47:20482d4309b5 v0.10

Removed files for old version 0.9
author jbe
date Mon Oct 31 13:10:25 2016 +0100 (2016-10-31)
parents 3cbce515387f
children fa3d19a7218b
files GNUmakefile latlon--0.9.sql latlon-v0008.c
line diff
     1.1 --- a/GNUmakefile	Mon Oct 31 13:06:31 2016 +0100
     1.2 +++ b/GNUmakefile	Mon Oct 31 13:10:25 2016 +0100
     1.3 @@ -1,6 +1,6 @@
     1.4  EXTENSION = latlon
     1.5 -DATA = latlon--0.9.sql latlon--0.9--0.10.sql latlon--0.10.sql
     1.6 -MODULES = latlon-v0008 latlon-v0009
     1.7 +DATA = latlon--0.9--0.10.sql latlon--0.10.sql
     1.8 +MODULES = latlon-v0009
     1.9  
    1.10  PG_CONFIG = pg_config
    1.11  PGXS := $(shell $(PG_CONFIG) --pgxs)
     2.1 --- a/latlon--0.9.sql	Mon Oct 31 13:06:31 2016 +0100
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,1692 +0,0 @@
     2.4 -
     2.5 -----------------------------------------
     2.6 --- forward declarations (shell types) --
     2.7 -----------------------------------------
     2.8 -
     2.9 -CREATE TYPE epoint;
    2.10 -CREATE TYPE ebox;
    2.11 -CREATE TYPE ecircle;
    2.12 -CREATE TYPE ecluster;
    2.13 -
    2.14 -
    2.15 -------------------------------------------------------------
    2.16 --- dummy input/output functions for dummy index key types --
    2.17 -------------------------------------------------------------
    2.18 -
    2.19 -CREATE FUNCTION ekey_point_in_dummy(cstring)
    2.20 -  RETURNS ekey_point
    2.21 -  LANGUAGE C IMMUTABLE STRICT
    2.22 -  AS '$libdir/latlon-v0008', 'pgl_notimpl';
    2.23 -
    2.24 -CREATE FUNCTION ekey_point_out_dummy(ekey_point)
    2.25 -  RETURNS cstring
    2.26 -  LANGUAGE C IMMUTABLE STRICT
    2.27 -  AS '$libdir/latlon-v0008', 'pgl_notimpl';
    2.28 -
    2.29 -CREATE FUNCTION ekey_area_in_dummy(cstring)
    2.30 -  RETURNS ekey_area
    2.31 -  LANGUAGE C IMMUTABLE STRICT
    2.32 -  AS '$libdir/latlon-v0008', 'pgl_notimpl';
    2.33 -
    2.34 -CREATE FUNCTION ekey_area_out_dummy(ekey_area)
    2.35 -  RETURNS cstring
    2.36 -  LANGUAGE C IMMUTABLE STRICT
    2.37 -  AS '$libdir/latlon-v0008', 'pgl_notimpl';
    2.38 -
    2.39 -
    2.40 ---------------------------
    2.41 --- text input functions --
    2.42 ---------------------------
    2.43 -
    2.44 -CREATE FUNCTION epoint_in(cstring)
    2.45 -  RETURNS epoint
    2.46 -  LANGUAGE C IMMUTABLE STRICT
    2.47 -  AS '$libdir/latlon-v0008', 'pgl_epoint_in';
    2.48 -
    2.49 -CREATE FUNCTION ebox_in(cstring)
    2.50 -  RETURNS ebox
    2.51 -  LANGUAGE C IMMUTABLE STRICT
    2.52 -  AS '$libdir/latlon-v0008', 'pgl_ebox_in';
    2.53 -
    2.54 -CREATE FUNCTION ecircle_in(cstring)
    2.55 -  RETURNS ecircle
    2.56 -  LANGUAGE C IMMUTABLE STRICT
    2.57 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_in';
    2.58 -
    2.59 -CREATE FUNCTION ecluster_in(cstring)
    2.60 -  RETURNS ecluster
    2.61 -  LANGUAGE C IMMUTABLE STRICT
    2.62 -  AS '$libdir/latlon-v0008', 'pgl_ecluster_in';
    2.63 -
    2.64 -
    2.65 ----------------------------
    2.66 --- text output functions --
    2.67 ----------------------------
    2.68 -
    2.69 -CREATE FUNCTION epoint_out(epoint)
    2.70 -  RETURNS cstring
    2.71 -  LANGUAGE C IMMUTABLE STRICT
    2.72 -  AS '$libdir/latlon-v0008', 'pgl_epoint_out';
    2.73 -
    2.74 -CREATE FUNCTION ebox_out(ebox)
    2.75 -  RETURNS cstring
    2.76 -  LANGUAGE C IMMUTABLE STRICT
    2.77 -  AS '$libdir/latlon-v0008', 'pgl_ebox_out';
    2.78 -
    2.79 -CREATE FUNCTION ecircle_out(ecircle)
    2.80 -  RETURNS cstring
    2.81 -  LANGUAGE C IMMUTABLE STRICT
    2.82 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_out';
    2.83 -
    2.84 -CREATE FUNCTION ecluster_out(ecluster)
    2.85 -  RETURNS cstring
    2.86 -  LANGUAGE C IMMUTABLE STRICT
    2.87 -  AS '$libdir/latlon-v0008', 'pgl_ecluster_out';
    2.88 -
    2.89 -
    2.90 ---------------------------
    2.91 --- binary I/O functions --
    2.92 ---------------------------
    2.93 -
    2.94 -CREATE FUNCTION epoint_recv(internal)
    2.95 -  RETURNS epoint
    2.96 -  LANGUAGE C IMMUTABLE STRICT
    2.97 -  AS '$libdir/latlon-v0008', 'pgl_epoint_recv';
    2.98 -
    2.99 -CREATE FUNCTION ebox_recv(internal)
   2.100 -  RETURNS ebox
   2.101 -  LANGUAGE C IMMUTABLE STRICT
   2.102 -  AS '$libdir/latlon-v0008', 'pgl_ebox_recv';
   2.103 -
   2.104 -CREATE FUNCTION ecircle_recv(internal)
   2.105 -  RETURNS ecircle
   2.106 -  LANGUAGE C IMMUTABLE STRICT
   2.107 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_recv';
   2.108 -
   2.109 -CREATE FUNCTION epoint_send(epoint)
   2.110 -  RETURNS bytea
   2.111 -  LANGUAGE C IMMUTABLE STRICT
   2.112 -  AS '$libdir/latlon-v0008', 'pgl_epoint_send';
   2.113 -
   2.114 -CREATE FUNCTION ebox_send(ebox)
   2.115 -  RETURNS bytea
   2.116 -  LANGUAGE C IMMUTABLE STRICT
   2.117 -  AS '$libdir/latlon-v0008', 'pgl_ebox_send';
   2.118 -
   2.119 -CREATE FUNCTION ecircle_send(ecircle)
   2.120 -  RETURNS bytea
   2.121 -  LANGUAGE C IMMUTABLE STRICT
   2.122 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_send';
   2.123 -
   2.124 -
   2.125 ------------------------------------------------
   2.126 --- type definitions of dummy index key types --
   2.127 ------------------------------------------------
   2.128 -
   2.129 -CREATE TYPE ekey_point (
   2.130 -  internallength = 8,
   2.131 -  input = ekey_point_in_dummy,
   2.132 -  output = ekey_point_out_dummy,
   2.133 -  alignment = char );
   2.134 -
   2.135 -CREATE TYPE ekey_area (
   2.136 -  internallength = 9,
   2.137 -  input = ekey_area_in_dummy,
   2.138 -  output = ekey_area_out_dummy,
   2.139 -  alignment = char );
   2.140 -
   2.141 -
   2.142 -------------------------------------------
   2.143 --- definitions of geographic data types --
   2.144 -------------------------------------------
   2.145 -
   2.146 -CREATE TYPE epoint (
   2.147 -  internallength = 16,
   2.148 -  input = epoint_in,
   2.149 -  output = epoint_out,
   2.150 -  receive = epoint_recv,
   2.151 -  send = epoint_send,
   2.152 -  alignment = double );
   2.153 -
   2.154 -CREATE TYPE ebox (
   2.155 -  internallength = 32,
   2.156 -  input = ebox_in,
   2.157 -  output = ebox_out,
   2.158 -  receive = ebox_recv,
   2.159 -  send = ebox_send,
   2.160 -  alignment = double );
   2.161 -
   2.162 -CREATE TYPE ecircle (
   2.163 -  internallength = 24,
   2.164 -  input = ecircle_in,
   2.165 -  output = ecircle_out,
   2.166 -  receive = ecircle_recv,
   2.167 -  send = ecircle_send,
   2.168 -  alignment = double );
   2.169 -
   2.170 -CREATE TYPE ecluster (
   2.171 -  internallength = VARIABLE,
   2.172 -  input = ecluster_in,
   2.173 -  output = ecluster_out,
   2.174 -  alignment = double,
   2.175 -  storage = external );
   2.176 -
   2.177 -
   2.178 ---------------------
   2.179 --- B-tree support --
   2.180 ---------------------
   2.181 -
   2.182 --- begin of B-tree support for epoint
   2.183 -
   2.184 -CREATE FUNCTION epoint_btree_lt(epoint, epoint)
   2.185 -  RETURNS boolean
   2.186 -  LANGUAGE C IMMUTABLE STRICT
   2.187 -  AS '$libdir/latlon-v0008', 'pgl_btree_epoint_lt';
   2.188 -
   2.189 -CREATE FUNCTION epoint_btree_le(epoint, epoint)
   2.190 -  RETURNS boolean
   2.191 -  LANGUAGE C IMMUTABLE STRICT
   2.192 -  AS '$libdir/latlon-v0008', 'pgl_btree_epoint_le';
   2.193 -
   2.194 -CREATE FUNCTION epoint_btree_eq(epoint, epoint)
   2.195 -  RETURNS boolean
   2.196 -  LANGUAGE C IMMUTABLE STRICT
   2.197 -  AS '$libdir/latlon-v0008', 'pgl_btree_epoint_eq';
   2.198 -
   2.199 -CREATE FUNCTION epoint_btree_ne(epoint, epoint)
   2.200 -  RETURNS boolean
   2.201 -  LANGUAGE C IMMUTABLE STRICT
   2.202 -  AS '$libdir/latlon-v0008', 'pgl_btree_epoint_ne';
   2.203 -
   2.204 -CREATE FUNCTION epoint_btree_ge(epoint, epoint)
   2.205 -  RETURNS boolean
   2.206 -  LANGUAGE C IMMUTABLE STRICT
   2.207 -  AS '$libdir/latlon-v0008', 'pgl_btree_epoint_ge';
   2.208 -
   2.209 -CREATE FUNCTION epoint_btree_gt(epoint, epoint)
   2.210 -  RETURNS boolean
   2.211 -  LANGUAGE C IMMUTABLE STRICT
   2.212 -  AS '$libdir/latlon-v0008', 'pgl_btree_epoint_gt';
   2.213 -
   2.214 -CREATE OPERATOR <<< (
   2.215 -  leftarg = epoint,
   2.216 -  rightarg = epoint,
   2.217 -  procedure = epoint_btree_lt,
   2.218 -  commutator = >>>,
   2.219 -  negator = >>>=,
   2.220 -  restrict = scalarltsel,
   2.221 -  join = scalarltjoinsel
   2.222 -);
   2.223 -
   2.224 -CREATE OPERATOR <<<= (
   2.225 -  leftarg = epoint,
   2.226 -  rightarg = epoint,
   2.227 -  procedure = epoint_btree_le,
   2.228 -  commutator = >>>=,
   2.229 -  negator = >>>,
   2.230 -  restrict = scalarltsel,
   2.231 -  join = scalarltjoinsel
   2.232 -);
   2.233 -
   2.234 -CREATE OPERATOR = (
   2.235 -  leftarg = epoint,
   2.236 -  rightarg = epoint,
   2.237 -  procedure = epoint_btree_eq,
   2.238 -  commutator = =,
   2.239 -  negator = <>,
   2.240 -  restrict = eqsel,
   2.241 -  join = eqjoinsel,
   2.242 -  merges
   2.243 -);
   2.244 -
   2.245 -CREATE OPERATOR <> (
   2.246 -  leftarg = epoint,
   2.247 -  rightarg = epoint,
   2.248 -  procedure = epoint_btree_eq,
   2.249 -  commutator = <>,
   2.250 -  negator = =,
   2.251 -  restrict = neqsel,
   2.252 -  join = neqjoinsel
   2.253 -);
   2.254 -
   2.255 -CREATE OPERATOR >>>= (
   2.256 -  leftarg = epoint,
   2.257 -  rightarg = epoint,
   2.258 -  procedure = epoint_btree_ge,
   2.259 -  commutator = <<<=,
   2.260 -  negator = <<<,
   2.261 -  restrict = scalargtsel,
   2.262 -  join = scalargtjoinsel
   2.263 -);
   2.264 -
   2.265 -CREATE OPERATOR >>> (
   2.266 -  leftarg = epoint,
   2.267 -  rightarg = epoint,
   2.268 -  procedure = epoint_btree_gt,
   2.269 -  commutator = <<<,
   2.270 -  negator = <<<=,
   2.271 -  restrict = scalargtsel,
   2.272 -  join = scalargtjoinsel
   2.273 -);
   2.274 -
   2.275 -CREATE FUNCTION epoint_btree_cmp(epoint, epoint)
   2.276 -  RETURNS int4
   2.277 -  LANGUAGE C IMMUTABLE STRICT
   2.278 -  AS '$libdir/latlon-v0008', 'pgl_btree_epoint_cmp';
   2.279 -
   2.280 -CREATE OPERATOR CLASS epoint_btree_ops
   2.281 -  DEFAULT FOR TYPE epoint USING btree AS
   2.282 -  OPERATOR 1 <<< ,
   2.283 -  OPERATOR 2 <<<= ,
   2.284 -  OPERATOR 3 = ,
   2.285 -  OPERATOR 4 >>>= ,
   2.286 -  OPERATOR 5 >>> ,
   2.287 -  FUNCTION 1 epoint_btree_cmp(epoint, epoint);
   2.288 -
   2.289 --- end of B-tree support for epoint
   2.290 -
   2.291 --- begin of B-tree support for ebox
   2.292 -
   2.293 -CREATE FUNCTION ebox_btree_lt(ebox, ebox)
   2.294 -  RETURNS boolean
   2.295 -  LANGUAGE C IMMUTABLE STRICT
   2.296 -  AS '$libdir/latlon-v0008', 'pgl_btree_ebox_lt';
   2.297 -
   2.298 -CREATE FUNCTION ebox_btree_le(ebox, ebox)
   2.299 -  RETURNS boolean
   2.300 -  LANGUAGE C IMMUTABLE STRICT
   2.301 -  AS '$libdir/latlon-v0008', 'pgl_btree_ebox_le';
   2.302 -
   2.303 -CREATE FUNCTION ebox_btree_eq(ebox, ebox)
   2.304 -  RETURNS boolean
   2.305 -  LANGUAGE C IMMUTABLE STRICT
   2.306 -  AS '$libdir/latlon-v0008', 'pgl_btree_ebox_eq';
   2.307 -
   2.308 -CREATE FUNCTION ebox_btree_ne(ebox, ebox)
   2.309 -  RETURNS boolean
   2.310 -  LANGUAGE C IMMUTABLE STRICT
   2.311 -  AS '$libdir/latlon-v0008', 'pgl_btree_ebox_ne';
   2.312 -
   2.313 -CREATE FUNCTION ebox_btree_ge(ebox, ebox)
   2.314 -  RETURNS boolean
   2.315 -  LANGUAGE C IMMUTABLE STRICT
   2.316 -  AS '$libdir/latlon-v0008', 'pgl_btree_ebox_ge';
   2.317 -
   2.318 -CREATE FUNCTION ebox_btree_gt(ebox, ebox)
   2.319 -  RETURNS boolean
   2.320 -  LANGUAGE C IMMUTABLE STRICT
   2.321 -  AS '$libdir/latlon-v0008', 'pgl_btree_ebox_gt';
   2.322 -
   2.323 -CREATE OPERATOR <<< (
   2.324 -  leftarg = ebox,
   2.325 -  rightarg = ebox,
   2.326 -  procedure = ebox_btree_lt,
   2.327 -  commutator = >>>,
   2.328 -  negator = >>>=,
   2.329 -  restrict = scalarltsel,
   2.330 -  join = scalarltjoinsel
   2.331 -);
   2.332 -
   2.333 -CREATE OPERATOR <<<= (
   2.334 -  leftarg = ebox,
   2.335 -  rightarg = ebox,
   2.336 -  procedure = ebox_btree_le,
   2.337 -  commutator = >>>=,
   2.338 -  negator = >>>,
   2.339 -  restrict = scalarltsel,
   2.340 -  join = scalarltjoinsel
   2.341 -);
   2.342 -
   2.343 -CREATE OPERATOR = (
   2.344 -  leftarg = ebox,
   2.345 -  rightarg = ebox,
   2.346 -  procedure = ebox_btree_eq,
   2.347 -  commutator = =,
   2.348 -  negator = <>,
   2.349 -  restrict = eqsel,
   2.350 -  join = eqjoinsel,
   2.351 -  merges
   2.352 -);
   2.353 -
   2.354 -CREATE OPERATOR <> (
   2.355 -  leftarg = ebox,
   2.356 -  rightarg = ebox,
   2.357 -  procedure = ebox_btree_eq,
   2.358 -  commutator = <>,
   2.359 -  negator = =,
   2.360 -  restrict = neqsel,
   2.361 -  join = neqjoinsel
   2.362 -);
   2.363 -
   2.364 -CREATE OPERATOR >>>= (
   2.365 -  leftarg = ebox,
   2.366 -  rightarg = ebox,
   2.367 -  procedure = ebox_btree_ge,
   2.368 -  commutator = <<<=,
   2.369 -  negator = <<<,
   2.370 -  restrict = scalargtsel,
   2.371 -  join = scalargtjoinsel
   2.372 -);
   2.373 -
   2.374 -CREATE OPERATOR >>> (
   2.375 -  leftarg = ebox,
   2.376 -  rightarg = ebox,
   2.377 -  procedure = ebox_btree_gt,
   2.378 -  commutator = <<<,
   2.379 -  negator = <<<=,
   2.380 -  restrict = scalargtsel,
   2.381 -  join = scalargtjoinsel
   2.382 -);
   2.383 -
   2.384 -CREATE FUNCTION ebox_btree_cmp(ebox, ebox)
   2.385 -  RETURNS int4
   2.386 -  LANGUAGE C IMMUTABLE STRICT
   2.387 -  AS '$libdir/latlon-v0008', 'pgl_btree_ebox_cmp';
   2.388 -
   2.389 -CREATE OPERATOR CLASS ebox_btree_ops
   2.390 -  DEFAULT FOR TYPE ebox USING btree AS
   2.391 -  OPERATOR 1 <<< ,
   2.392 -  OPERATOR 2 <<<= ,
   2.393 -  OPERATOR 3 = ,
   2.394 -  OPERATOR 4 >>>= ,
   2.395 -  OPERATOR 5 >>> ,
   2.396 -  FUNCTION 1 ebox_btree_cmp(ebox, ebox);
   2.397 -
   2.398 --- end of B-tree support for ebox
   2.399 -
   2.400 --- begin of B-tree support for ecircle
   2.401 -
   2.402 -CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   2.403 -  RETURNS boolean
   2.404 -  LANGUAGE C IMMUTABLE STRICT
   2.405 -  AS '$libdir/latlon-v0008', 'pgl_btree_ecircle_lt';
   2.406 -
   2.407 -CREATE FUNCTION ecircle_btree_le(ecircle, ecircle)
   2.408 -  RETURNS boolean
   2.409 -  LANGUAGE C IMMUTABLE STRICT
   2.410 -  AS '$libdir/latlon-v0008', 'pgl_btree_ecircle_le';
   2.411 -
   2.412 -CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   2.413 -  RETURNS boolean
   2.414 -  LANGUAGE C IMMUTABLE STRICT
   2.415 -  AS '$libdir/latlon-v0008', 'pgl_btree_ecircle_eq';
   2.416 -
   2.417 -CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   2.418 -  RETURNS boolean
   2.419 -  LANGUAGE C IMMUTABLE STRICT
   2.420 -  AS '$libdir/latlon-v0008', 'pgl_btree_ecircle_ne';
   2.421 -
   2.422 -CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   2.423 -  RETURNS boolean
   2.424 -  LANGUAGE C IMMUTABLE STRICT
   2.425 -  AS '$libdir/latlon-v0008', 'pgl_btree_ecircle_ge';
   2.426 -
   2.427 -CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   2.428 -  RETURNS boolean
   2.429 -  LANGUAGE C IMMUTABLE STRICT
   2.430 -  AS '$libdir/latlon-v0008', 'pgl_btree_ecircle_gt';
   2.431 -
   2.432 -CREATE OPERATOR <<< (
   2.433 -  leftarg = ecircle,
   2.434 -  rightarg = ecircle,
   2.435 -  procedure = ecircle_btree_lt,
   2.436 -  commutator = >>>,
   2.437 -  negator = >>>=,
   2.438 -  restrict = scalarltsel,
   2.439 -  join = scalarltjoinsel
   2.440 -);
   2.441 -
   2.442 -CREATE OPERATOR <<<= (
   2.443 -  leftarg = ecircle,
   2.444 -  rightarg = ecircle,
   2.445 -  procedure = ecircle_btree_le,
   2.446 -  commutator = >>>=,
   2.447 -  negator = >>>,
   2.448 -  restrict = scalarltsel,
   2.449 -  join = scalarltjoinsel
   2.450 -);
   2.451 -
   2.452 -CREATE OPERATOR = (
   2.453 -  leftarg = ecircle,
   2.454 -  rightarg = ecircle,
   2.455 -  procedure = ecircle_btree_eq,
   2.456 -  commutator = =,
   2.457 -  negator = <>,
   2.458 -  restrict = eqsel,
   2.459 -  join = eqjoinsel,
   2.460 -  merges
   2.461 -);
   2.462 -
   2.463 -CREATE OPERATOR <> (
   2.464 -  leftarg = ecircle,
   2.465 -  rightarg = ecircle,
   2.466 -  procedure = ecircle_btree_eq,
   2.467 -  commutator = <>,
   2.468 -  negator = =,
   2.469 -  restrict = neqsel,
   2.470 -  join = neqjoinsel
   2.471 -);
   2.472 -
   2.473 -CREATE OPERATOR >>>= (
   2.474 -  leftarg = ecircle,
   2.475 -  rightarg = ecircle,
   2.476 -  procedure = ecircle_btree_ge,
   2.477 -  commutator = <<<=,
   2.478 -  negator = <<<,
   2.479 -  restrict = scalargtsel,
   2.480 -  join = scalargtjoinsel
   2.481 -);
   2.482 -
   2.483 -CREATE OPERATOR >>> (
   2.484 -  leftarg = ecircle,
   2.485 -  rightarg = ecircle,
   2.486 -  procedure = ecircle_btree_gt,
   2.487 -  commutator = <<<,
   2.488 -  negator = <<<=,
   2.489 -  restrict = scalargtsel,
   2.490 -  join = scalargtjoinsel
   2.491 -);
   2.492 -
   2.493 -CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   2.494 -  RETURNS int4
   2.495 -  LANGUAGE C IMMUTABLE STRICT
   2.496 -  AS '$libdir/latlon-v0008', 'pgl_btree_ecircle_cmp';
   2.497 -
   2.498 -CREATE OPERATOR CLASS ecircle_btree_ops
   2.499 -  DEFAULT FOR TYPE ecircle USING btree AS
   2.500 -  OPERATOR 1 <<< ,
   2.501 -  OPERATOR 2 <<<= ,
   2.502 -  OPERATOR 3 = ,
   2.503 -  OPERATOR 4 >>>= ,
   2.504 -  OPERATOR 5 >>> ,
   2.505 -  FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle);
   2.506 -
   2.507 --- end of B-tree support for ecircle
   2.508 -
   2.509 -
   2.510 -----------------
   2.511 --- type casts --
   2.512 -----------------
   2.513 -
   2.514 -CREATE FUNCTION cast_epoint_to_ebox(epoint)
   2.515 -  RETURNS ebox
   2.516 -  LANGUAGE C IMMUTABLE STRICT
   2.517 -  AS '$libdir/latlon-v0008', 'pgl_epoint_to_ebox';
   2.518 -
   2.519 -CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint);
   2.520 -
   2.521 -CREATE FUNCTION cast_epoint_to_ecircle(epoint)
   2.522 -  RETURNS ecircle
   2.523 -  LANGUAGE C IMMUTABLE STRICT
   2.524 -  AS '$libdir/latlon-v0008', 'pgl_epoint_to_ecircle';
   2.525 -
   2.526 -CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint);
   2.527 -
   2.528 -CREATE FUNCTION cast_epoint_to_ecluster(epoint)
   2.529 -  RETURNS ecluster
   2.530 -  LANGUAGE C IMMUTABLE STRICT
   2.531 -  AS '$libdir/latlon-v0008', 'pgl_epoint_to_ecluster';
   2.532 -
   2.533 -CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint);
   2.534 -
   2.535 -CREATE FUNCTION cast_ebox_to_ecluster(ebox)
   2.536 -  RETURNS ecluster
   2.537 -  LANGUAGE C IMMUTABLE STRICT
   2.538 -  AS '$libdir/latlon-v0008', 'pgl_ebox_to_ecluster';
   2.539 -
   2.540 -CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox);
   2.541 -
   2.542 -
   2.543 ----------------------------
   2.544 --- constructor functions --
   2.545 ----------------------------
   2.546 -
   2.547 -CREATE FUNCTION epoint(float8, float8)
   2.548 -  RETURNS epoint
   2.549 -  LANGUAGE C IMMUTABLE STRICT
   2.550 -  AS '$libdir/latlon-v0008', 'pgl_create_epoint';
   2.551 -
   2.552 -CREATE FUNCTION epoint_latlon(float8, float8)
   2.553 -  RETURNS epoint
   2.554 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   2.555 -    SELECT epoint($1, $2)
   2.556 -  $$;
   2.557 -
   2.558 -CREATE FUNCTION epoint_lonlat(float8, float8)
   2.559 -  RETURNS epoint
   2.560 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   2.561 -    SELECT epoint($2, $1)
   2.562 -  $$;
   2.563 -
   2.564 -CREATE FUNCTION empty_ebox()
   2.565 -  RETURNS ebox
   2.566 -  LANGUAGE C IMMUTABLE STRICT
   2.567 -  AS '$libdir/latlon-v0008', 'pgl_create_empty_ebox';
   2.568 -
   2.569 -CREATE FUNCTION ebox(float8, float8, float8, float8)
   2.570 -  RETURNS ebox
   2.571 -  LANGUAGE C IMMUTABLE STRICT
   2.572 -  AS '$libdir/latlon-v0008', 'pgl_create_ebox';
   2.573 -
   2.574 -CREATE FUNCTION ebox(epoint, epoint)
   2.575 -  RETURNS ebox
   2.576 -  LANGUAGE C IMMUTABLE STRICT
   2.577 -  AS '$libdir/latlon-v0008', 'pgl_create_ebox_from_epoints';
   2.578 -
   2.579 -CREATE FUNCTION ecircle(float8, float8, float8)
   2.580 -  RETURNS ecircle
   2.581 -  LANGUAGE C IMMUTABLE STRICT
   2.582 -  AS '$libdir/latlon-v0008', 'pgl_create_ecircle';
   2.583 -
   2.584 -CREATE FUNCTION ecircle(epoint, float8)
   2.585 -  RETURNS ecircle
   2.586 -  LANGUAGE C IMMUTABLE STRICT
   2.587 -  AS '$libdir/latlon-v0008', 'pgl_create_ecircle_from_epoint';
   2.588 -
   2.589 -CREATE FUNCTION ecluster_concat(ecluster[])
   2.590 -  RETURNS ecluster
   2.591 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.592 -    SELECT array_to_string($1, ' ')::ecluster
   2.593 -  $$;
   2.594 -
   2.595 -CREATE FUNCTION ecluster_concat(ecluster, ecluster)
   2.596 -  RETURNS ecluster
   2.597 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.598 -    SELECT ($1::text || ' ' || $2::text)::ecluster
   2.599 -  $$;
   2.600 -
   2.601 -CREATE FUNCTION ecluster_create_multipoint(epoint[])
   2.602 -  RETURNS ecluster
   2.603 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.604 -    SELECT
   2.605 -      array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster
   2.606 -    FROM unnest($1)
   2.607 -  $$;
   2.608 -
   2.609 -CREATE FUNCTION ecluster_create_path(epoint[])
   2.610 -  RETURNS ecluster
   2.611 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.612 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   2.613 -      ('path (' || array_to_string($1, ' ') || ')')::ecluster
   2.614 -    END
   2.615 -    FROM array_to_string($1, ' ') AS "str"
   2.616 -  $$;
   2.617 -
   2.618 -CREATE FUNCTION ecluster_create_outline(epoint[])
   2.619 -  RETURNS ecluster
   2.620 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.621 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   2.622 -      ('outline (' || array_to_string($1, ' ') || ')')::ecluster
   2.623 -    END
   2.624 -    FROM array_to_string($1, ' ') AS "str"
   2.625 -  $$;
   2.626 -
   2.627 -CREATE FUNCTION ecluster_create_polygon(epoint[])
   2.628 -  RETURNS ecluster
   2.629 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.630 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   2.631 -      ('polygon (' || array_to_string($1, ' ') || ')')::ecluster
   2.632 -    END
   2.633 -    FROM array_to_string($1, ' ') AS "str"
   2.634 -  $$;
   2.635 -
   2.636 -
   2.637 -----------------------
   2.638 --- getter functions --
   2.639 -----------------------
   2.640 -
   2.641 -CREATE FUNCTION latitude(epoint)
   2.642 -  RETURNS float8
   2.643 -  LANGUAGE C IMMUTABLE STRICT
   2.644 -  AS '$libdir/latlon-v0008', 'pgl_epoint_lat';
   2.645 -
   2.646 -CREATE FUNCTION longitude(epoint)
   2.647 -  RETURNS float8
   2.648 -  LANGUAGE C IMMUTABLE STRICT
   2.649 -  AS '$libdir/latlon-v0008', 'pgl_epoint_lon';
   2.650 -
   2.651 -CREATE FUNCTION min_latitude(ebox)
   2.652 -  RETURNS float8
   2.653 -  LANGUAGE C IMMUTABLE STRICT
   2.654 -  AS '$libdir/latlon-v0008', 'pgl_ebox_lat_min';
   2.655 -
   2.656 -CREATE FUNCTION max_latitude(ebox)
   2.657 -  RETURNS float8
   2.658 -  LANGUAGE C IMMUTABLE STRICT
   2.659 -  AS '$libdir/latlon-v0008', 'pgl_ebox_lat_max';
   2.660 -
   2.661 -CREATE FUNCTION min_longitude(ebox)
   2.662 -  RETURNS float8
   2.663 -  LANGUAGE C IMMUTABLE STRICT
   2.664 -  AS '$libdir/latlon-v0008', 'pgl_ebox_lon_min';
   2.665 -
   2.666 -CREATE FUNCTION max_longitude(ebox)
   2.667 -  RETURNS float8
   2.668 -  LANGUAGE C IMMUTABLE STRICT
   2.669 -  AS '$libdir/latlon-v0008', 'pgl_ebox_lon_max';
   2.670 -
   2.671 -CREATE FUNCTION center(ecircle)
   2.672 -  RETURNS epoint
   2.673 -  LANGUAGE C IMMUTABLE STRICT
   2.674 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_center';
   2.675 -
   2.676 -CREATE FUNCTION radius(ecircle)
   2.677 -  RETURNS float8
   2.678 -  LANGUAGE C IMMUTABLE STRICT
   2.679 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_radius';
   2.680 -
   2.681 -CREATE FUNCTION ecluster_extract_points(ecluster)
   2.682 -  RETURNS SETOF epoint
   2.683 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.684 -    SELECT "match"[2]::epoint
   2.685 -    FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match"
   2.686 -  $$;
   2.687 -
   2.688 -CREATE FUNCTION ecluster_extract_paths(ecluster)
   2.689 -  RETURNS SETOF epoint[]
   2.690 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.691 -    SELECT (
   2.692 -      SELECT array_agg("m2"[1]::epoint)
   2.693 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   2.694 -    )
   2.695 -    FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1"
   2.696 -  $$;
   2.697 -
   2.698 -CREATE FUNCTION ecluster_extract_outlines(ecluster)
   2.699 -  RETURNS SETOF epoint[]
   2.700 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.701 -    SELECT (
   2.702 -      SELECT array_agg("m2"[1]::epoint)
   2.703 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   2.704 -    )
   2.705 -    FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1"
   2.706 -  $$;
   2.707 -
   2.708 -CREATE FUNCTION ecluster_extract_polygons(ecluster)
   2.709 -  RETURNS SETOF epoint[]
   2.710 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   2.711 -    SELECT (
   2.712 -      SELECT array_agg("m2"[1]::epoint)
   2.713 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   2.714 -    )
   2.715 -    FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1"
   2.716 -  $$;
   2.717 -
   2.718 -
   2.719 ----------------
   2.720 --- operators --
   2.721 ----------------
   2.722 -
   2.723 -CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   2.724 -  RETURNS boolean
   2.725 -  LANGUAGE C IMMUTABLE STRICT
   2.726 -  AS '$libdir/latlon-v0008', 'pgl_epoint_ebox_overlap';
   2.727 -
   2.728 -CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   2.729 -  RETURNS boolean
   2.730 -  LANGUAGE C IMMUTABLE STRICT
   2.731 -  AS '$libdir/latlon-v0008', 'pgl_epoint_ecircle_overlap';
   2.732 -
   2.733 -CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   2.734 -  RETURNS boolean
   2.735 -  LANGUAGE C IMMUTABLE STRICT
   2.736 -  AS '$libdir/latlon-v0008', 'pgl_epoint_ecluster_overlap';
   2.737 -
   2.738 -CREATE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
   2.739 -  RETURNS boolean
   2.740 -  LANGUAGE C IMMUTABLE STRICT
   2.741 -  AS '$libdir/latlon-v0008', 'pgl_epoint_ecluster_may_overlap';
   2.742 -
   2.743 -CREATE FUNCTION ebox_overlap_proc(ebox, ebox)
   2.744 -  RETURNS boolean
   2.745 -  LANGUAGE C IMMUTABLE STRICT
   2.746 -  AS '$libdir/latlon-v0008', 'pgl_ebox_overlap';
   2.747 -
   2.748 -CREATE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
   2.749 -  RETURNS boolean
   2.750 -  LANGUAGE C IMMUTABLE STRICT
   2.751 -  AS '$libdir/latlon-v0008', 'pgl_ebox_ecircle_may_overlap';
   2.752 -
   2.753 -CREATE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
   2.754 -  RETURNS boolean
   2.755 -  LANGUAGE C IMMUTABLE STRICT
   2.756 -  AS '$libdir/latlon-v0008', 'pgl_ebox_ecluster_may_overlap';
   2.757 -
   2.758 -CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   2.759 -  RETURNS boolean
   2.760 -  LANGUAGE C IMMUTABLE STRICT
   2.761 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_overlap';
   2.762 -
   2.763 -CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   2.764 -  RETURNS boolean
   2.765 -  LANGUAGE C IMMUTABLE STRICT
   2.766 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_ecluster_overlap';
   2.767 -
   2.768 -CREATE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
   2.769 -  RETURNS boolean
   2.770 -  LANGUAGE C IMMUTABLE STRICT
   2.771 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_ecluster_may_overlap';
   2.772 -
   2.773 -CREATE FUNCTION ecluster_overlap_proc(ecluster, ecluster)
   2.774 -  RETURNS boolean
   2.775 -  LANGUAGE C IMMUTABLE STRICT
   2.776 -  AS '$libdir/latlon-v0008', 'pgl_ecluster_overlap';
   2.777 -
   2.778 -CREATE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
   2.779 -  RETURNS boolean
   2.780 -  LANGUAGE C IMMUTABLE STRICT
   2.781 -  AS '$libdir/latlon-v0008', 'pgl_ecluster_may_overlap';
   2.782 -
   2.783 -CREATE FUNCTION ecluster_contains_proc(ecluster, ecluster)
   2.784 -  RETURNS boolean
   2.785 -  LANGUAGE C IMMUTABLE STRICT
   2.786 -  AS '$libdir/latlon-v0008', 'pgl_ecluster_contains';
   2.787 -
   2.788 -CREATE FUNCTION epoint_distance_proc(epoint, epoint)
   2.789 -  RETURNS float8
   2.790 -  LANGUAGE C IMMUTABLE STRICT
   2.791 -  AS '$libdir/latlon-v0008', 'pgl_epoint_distance';
   2.792 -
   2.793 -CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   2.794 -  RETURNS float8
   2.795 -  LANGUAGE C IMMUTABLE STRICT
   2.796 -  AS '$libdir/latlon-v0008', 'pgl_epoint_ecircle_distance';
   2.797 -
   2.798 -CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   2.799 -  RETURNS float8
   2.800 -  LANGUAGE C IMMUTABLE STRICT
   2.801 -  AS '$libdir/latlon-v0008', 'pgl_epoint_ecluster_distance';
   2.802 -
   2.803 -CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   2.804 -  RETURNS float8
   2.805 -  LANGUAGE C IMMUTABLE STRICT
   2.806 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_distance';
   2.807 -
   2.808 -CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   2.809 -  RETURNS float8
   2.810 -  LANGUAGE C IMMUTABLE STRICT
   2.811 -  AS '$libdir/latlon-v0008', 'pgl_ecircle_ecluster_distance';
   2.812 -
   2.813 -CREATE FUNCTION ecluster_distance_proc(ecluster, ecluster)
   2.814 -  RETURNS float8
   2.815 -  LANGUAGE C IMMUTABLE STRICT
   2.816 -  AS '$libdir/latlon-v0008', 'pgl_ecluster_distance';
   2.817 -
   2.818 -CREATE OPERATOR && (
   2.819 -  leftarg = epoint,
   2.820 -  rightarg = ebox,
   2.821 -  procedure = epoint_ebox_overlap_proc,
   2.822 -  commutator = &&,
   2.823 -  restrict = areasel,
   2.824 -  join = areajoinsel
   2.825 -);
   2.826 -
   2.827 -CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint)
   2.828 -  RETURNS boolean
   2.829 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   2.830 -
   2.831 -CREATE OPERATOR && (
   2.832 -  leftarg = ebox,
   2.833 -  rightarg = epoint,
   2.834 -  procedure = epoint_ebox_overlap_commutator,
   2.835 -  commutator = &&,
   2.836 -  restrict = areasel,
   2.837 -  join = areajoinsel
   2.838 -);
   2.839 -
   2.840 -CREATE OPERATOR && (
   2.841 -  leftarg = epoint,
   2.842 -  rightarg = ecircle,
   2.843 -  procedure = epoint_ecircle_overlap_proc,
   2.844 -  commutator = &&,
   2.845 -  restrict = areasel,
   2.846 -  join = areajoinsel
   2.847 -);
   2.848 -
   2.849 -CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint)
   2.850 -  RETURNS boolean
   2.851 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   2.852 -
   2.853 -CREATE OPERATOR && (
   2.854 -  leftarg = ecircle,
   2.855 -  rightarg = epoint,
   2.856 -  procedure = epoint_ecircle_overlap_commutator,
   2.857 -  commutator = &&,
   2.858 -  restrict = areasel,
   2.859 -  join = areajoinsel
   2.860 -);
   2.861 -
   2.862 -CREATE OPERATOR && (
   2.863 -  leftarg = epoint,
   2.864 -  rightarg = ecluster,
   2.865 -  procedure = epoint_ecluster_overlap_proc,
   2.866 -  commutator = &&,
   2.867 -  restrict = areasel,
   2.868 -  join = areajoinsel
   2.869 -);
   2.870 -
   2.871 -CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint)
   2.872 -  RETURNS boolean
   2.873 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   2.874 -
   2.875 -CREATE OPERATOR && (
   2.876 -  leftarg = ecluster,
   2.877 -  rightarg = epoint,
   2.878 -  procedure = epoint_ecluster_overlap_commutator,
   2.879 -  commutator = &&,
   2.880 -  restrict = areasel,
   2.881 -  join = areajoinsel
   2.882 -);
   2.883 -
   2.884 -CREATE OPERATOR && (
   2.885 -  leftarg = ebox,
   2.886 -  rightarg = ebox,
   2.887 -  procedure = ebox_overlap_proc,
   2.888 -  commutator = &&,
   2.889 -  restrict = areasel,
   2.890 -  join = areajoinsel
   2.891 -);
   2.892 -
   2.893 -CREATE OPERATOR && (
   2.894 -  leftarg = ecircle,
   2.895 -  rightarg = ecircle,
   2.896 -  procedure = ecircle_overlap_proc,
   2.897 -  commutator = &&,
   2.898 -  restrict = areasel,
   2.899 -  join = areajoinsel
   2.900 -);
   2.901 -
   2.902 -CREATE OPERATOR && (
   2.903 -  leftarg = ecircle,
   2.904 -  rightarg = ecluster,
   2.905 -  procedure = ecircle_ecluster_overlap_proc,
   2.906 -  commutator = &&,
   2.907 -  restrict = areasel,
   2.908 -  join = areajoinsel
   2.909 -);
   2.910 -
   2.911 -CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle)
   2.912 -  RETURNS boolean
   2.913 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   2.914 -
   2.915 -CREATE OPERATOR && (
   2.916 -  leftarg = ecluster,
   2.917 -  rightarg = ecircle,
   2.918 -  procedure = ecircle_ecluster_overlap_commutator,
   2.919 -  commutator = &&,
   2.920 -  restrict = areasel,
   2.921 -  join = areajoinsel
   2.922 -);
   2.923 -
   2.924 -CREATE OPERATOR && (
   2.925 -  leftarg = ecluster,
   2.926 -  rightarg = ecluster,
   2.927 -  procedure = ecluster_overlap_proc,
   2.928 -  commutator = &&,
   2.929 -  restrict = areasel,
   2.930 -  join = areajoinsel
   2.931 -);
   2.932 -
   2.933 -CREATE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle)
   2.934 -  RETURNS boolean
   2.935 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   2.936 -
   2.937 -CREATE OPERATOR && (
   2.938 -  leftarg = ebox,
   2.939 -  rightarg = ecircle,
   2.940 -  procedure = ebox_ecircle_overlap_castwrap,
   2.941 -  commutator = &&,
   2.942 -  restrict = areasel,
   2.943 -  join = areajoinsel
   2.944 -);
   2.945 -
   2.946 -CREATE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox)
   2.947 -  RETURNS boolean
   2.948 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
   2.949 -
   2.950 -CREATE OPERATOR && (
   2.951 -  leftarg = ecircle,
   2.952 -  rightarg = ebox,
   2.953 -  procedure = ebox_ecircle_overlap_castwrap,
   2.954 -  commutator = &&,
   2.955 -  restrict = areasel,
   2.956 -  join = areajoinsel
   2.957 -);
   2.958 -
   2.959 -CREATE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster)
   2.960 -  RETURNS boolean
   2.961 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   2.962 -
   2.963 -CREATE OPERATOR && (
   2.964 -  leftarg = ebox,
   2.965 -  rightarg = ecluster,
   2.966 -  procedure = ebox_ecluster_overlap_castwrap,
   2.967 -  commutator = &&,
   2.968 -  restrict = areasel,
   2.969 -  join = areajoinsel
   2.970 -);
   2.971 -
   2.972 -CREATE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox)
   2.973 -  RETURNS boolean
   2.974 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
   2.975 -
   2.976 -CREATE OPERATOR && (
   2.977 -  leftarg = ecluster,
   2.978 -  rightarg = ebox,
   2.979 -  procedure = ebox_ecluster_overlap_castwrap,
   2.980 -  commutator = &&,
   2.981 -  restrict = areasel,
   2.982 -  join = areajoinsel
   2.983 -);
   2.984 -
   2.985 -CREATE OPERATOR &&+ (
   2.986 -  leftarg = epoint,
   2.987 -  rightarg = ecluster,
   2.988 -  procedure = epoint_ecluster_may_overlap_proc,
   2.989 -  commutator = &&+,
   2.990 -  restrict = areasel,
   2.991 -  join = areajoinsel
   2.992 -);
   2.993 -
   2.994 -CREATE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint)
   2.995 -  RETURNS boolean
   2.996 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   2.997 -
   2.998 -CREATE OPERATOR &&+ (
   2.999 -  leftarg = ecluster,
  2.1000 -  rightarg = epoint,
  2.1001 -  procedure = epoint_ecluster_may_overlap_commutator,
  2.1002 -  commutator = &&+,
  2.1003 -  restrict = areasel,
  2.1004 -  join = areajoinsel
  2.1005 -);
  2.1006 -
  2.1007 -CREATE OPERATOR &&+ (
  2.1008 -  leftarg = ebox,
  2.1009 -  rightarg = ecircle,
  2.1010 -  procedure = ebox_ecircle_may_overlap_proc,
  2.1011 -  commutator = &&+,
  2.1012 -  restrict = areasel,
  2.1013 -  join = areajoinsel
  2.1014 -);
  2.1015 -
  2.1016 -CREATE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox)
  2.1017 -  RETURNS boolean
  2.1018 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  2.1019 -
  2.1020 -CREATE OPERATOR &&+ (
  2.1021 -  leftarg = ecircle,
  2.1022 -  rightarg = ebox,
  2.1023 -  procedure = ebox_ecircle_may_overlap_commutator,
  2.1024 -  commutator = &&+,
  2.1025 -  restrict = areasel,
  2.1026 -  join = areajoinsel
  2.1027 -);
  2.1028 -
  2.1029 -CREATE OPERATOR &&+ (
  2.1030 -  leftarg = ebox,
  2.1031 -  rightarg = ecluster,
  2.1032 -  procedure = ebox_ecluster_may_overlap_proc,
  2.1033 -  commutator = &&+,
  2.1034 -  restrict = areasel,
  2.1035 -  join = areajoinsel
  2.1036 -);
  2.1037 -
  2.1038 -CREATE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox)
  2.1039 -  RETURNS boolean
  2.1040 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  2.1041 -
  2.1042 -CREATE OPERATOR &&+ (
  2.1043 -  leftarg = ecluster,
  2.1044 -  rightarg = ebox,
  2.1045 -  procedure = ebox_ecluster_may_overlap_commutator,
  2.1046 -  commutator = &&+,
  2.1047 -  restrict = areasel,
  2.1048 -  join = areajoinsel
  2.1049 -);
  2.1050 -
  2.1051 -CREATE OPERATOR &&+ (
  2.1052 -  leftarg = ecircle,
  2.1053 -  rightarg = ecluster,
  2.1054 -  procedure = ecircle_ecluster_may_overlap_proc,
  2.1055 -  commutator = &&+,
  2.1056 -  restrict = areasel,
  2.1057 -  join = areajoinsel
  2.1058 -);
  2.1059 -
  2.1060 -CREATE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle)
  2.1061 -  RETURNS boolean
  2.1062 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  2.1063 -
  2.1064 -CREATE OPERATOR &&+ (
  2.1065 -  leftarg = ecluster,
  2.1066 -  rightarg = ecircle,
  2.1067 -  procedure = ecircle_ecluster_may_overlap_commutator,
  2.1068 -  commutator = &&+,
  2.1069 -  restrict = areasel,
  2.1070 -  join = areajoinsel
  2.1071 -);
  2.1072 -
  2.1073 -CREATE OPERATOR &&+ (
  2.1074 -  leftarg = ecluster,
  2.1075 -  rightarg = ecluster,
  2.1076 -  procedure = ecluster_may_overlap_proc,
  2.1077 -  commutator = &&+,
  2.1078 -  restrict = areasel,
  2.1079 -  join = areajoinsel
  2.1080 -);
  2.1081 -
  2.1082 -CREATE OPERATOR @> (
  2.1083 -  leftarg = ebox,
  2.1084 -  rightarg = epoint,
  2.1085 -  procedure = epoint_ebox_overlap_commutator,
  2.1086 -  commutator = <@,
  2.1087 -  restrict = areasel,
  2.1088 -  join = areajoinsel
  2.1089 -);
  2.1090 -
  2.1091 -CREATE OPERATOR <@ (
  2.1092 -  leftarg = epoint,
  2.1093 -  rightarg = ebox,
  2.1094 -  procedure = epoint_ebox_overlap_proc,
  2.1095 -  commutator = @>,
  2.1096 -  restrict = areasel,
  2.1097 -  join = areajoinsel
  2.1098 -);
  2.1099 -
  2.1100 -CREATE OPERATOR @> (
  2.1101 -  leftarg = ecluster,
  2.1102 -  rightarg = epoint,
  2.1103 -  procedure = epoint_ecluster_overlap_commutator,
  2.1104 -  commutator = <@,
  2.1105 -  restrict = areasel,
  2.1106 -  join = areajoinsel
  2.1107 -);
  2.1108 -
  2.1109 -CREATE OPERATOR <@ (
  2.1110 -  leftarg = epoint,
  2.1111 -  rightarg = ecluster,
  2.1112 -  procedure = epoint_ecluster_overlap_proc,
  2.1113 -  commutator = <@,
  2.1114 -  restrict = areasel,
  2.1115 -  join = areajoinsel
  2.1116 -);
  2.1117 -
  2.1118 -CREATE OPERATOR @> (
  2.1119 -  leftarg = ecluster,
  2.1120 -  rightarg = ecluster,
  2.1121 -  procedure = ecluster_contains_proc,
  2.1122 -  commutator = <@,
  2.1123 -  restrict = areasel,
  2.1124 -  join = areajoinsel
  2.1125 -);
  2.1126 -
  2.1127 -CREATE FUNCTION ecluster_contains_commutator(ecluster, ecluster)
  2.1128 -  RETURNS boolean
  2.1129 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1';
  2.1130 -
  2.1131 -CREATE OPERATOR <@ (
  2.1132 -  leftarg = ecluster,
  2.1133 -  rightarg = ecluster,
  2.1134 -  procedure = ecluster_contains_commutator,
  2.1135 -  commutator = @>,
  2.1136 -  restrict = areasel,
  2.1137 -  join = areajoinsel
  2.1138 -);
  2.1139 -
  2.1140 -CREATE FUNCTION ebox_contains_castwrap(ebox, ebox)
  2.1141 -  RETURNS boolean
  2.1142 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2::ecluster';
  2.1143 -
  2.1144 -CREATE OPERATOR @> (
  2.1145 -  leftarg = ebox,
  2.1146 -  rightarg = ebox,
  2.1147 -  procedure = ebox_contains_castwrap,
  2.1148 -  commutator = <@,
  2.1149 -  restrict = areasel,
  2.1150 -  join = areajoinsel
  2.1151 -);
  2.1152 -
  2.1153 -CREATE FUNCTION ebox_contains_swapped_castwrap(ebox, ebox)
  2.1154 -  RETURNS boolean
  2.1155 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1::ecluster';
  2.1156 -
  2.1157 -CREATE OPERATOR <@ (
  2.1158 -  leftarg = ebox,
  2.1159 -  rightarg = ebox,
  2.1160 -  procedure = ebox_contains_swapped_castwrap,
  2.1161 -  commutator = @>,
  2.1162 -  restrict = areasel,
  2.1163 -  join = areajoinsel
  2.1164 -);
  2.1165 -
  2.1166 -CREATE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster)
  2.1167 -  RETURNS boolean
  2.1168 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2';
  2.1169 -
  2.1170 -CREATE OPERATOR @> (
  2.1171 -  leftarg = ebox,
  2.1172 -  rightarg = ecluster,
  2.1173 -  procedure = ebox_ecluster_contains_castwrap,
  2.1174 -  commutator = <@,
  2.1175 -  restrict = areasel,
  2.1176 -  join = areajoinsel
  2.1177 -);
  2.1178 -
  2.1179 -CREATE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox)
  2.1180 -  RETURNS boolean
  2.1181 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1';
  2.1182 -
  2.1183 -CREATE OPERATOR <@ (
  2.1184 -  leftarg = ecluster,
  2.1185 -  rightarg = ebox,
  2.1186 -  procedure = ebox_ecluster_contains_castwrap,
  2.1187 -  commutator = @>,
  2.1188 -  restrict = areasel,
  2.1189 -  join = areajoinsel
  2.1190 -);
  2.1191 -
  2.1192 -CREATE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox)
  2.1193 -  RETURNS boolean
  2.1194 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 @> $2::ecluster';
  2.1195 -
  2.1196 -CREATE OPERATOR @> (
  2.1197 -  leftarg = ecluster,
  2.1198 -  rightarg = ebox,
  2.1199 -  procedure = ecluster_ebox_contains_castwrap,
  2.1200 -  commutator = <@,
  2.1201 -  restrict = areasel,
  2.1202 -  join = areajoinsel
  2.1203 -);
  2.1204 -
  2.1205 -CREATE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster)
  2.1206 -  RETURNS boolean
  2.1207 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1::ecluster';
  2.1208 -
  2.1209 -CREATE OPERATOR <@ (
  2.1210 -  leftarg = ebox,
  2.1211 -  rightarg = ecluster,
  2.1212 -  procedure = ecluster_ebox_contains_castwrap,
  2.1213 -  commutator = @>,
  2.1214 -  restrict = areasel,
  2.1215 -  join = areajoinsel
  2.1216 -);
  2.1217 -
  2.1218 -CREATE OPERATOR <-> (
  2.1219 -  leftarg = epoint,
  2.1220 -  rightarg = epoint,
  2.1221 -  procedure = epoint_distance_proc,
  2.1222 -  commutator = <->
  2.1223 -);
  2.1224 -
  2.1225 -CREATE OPERATOR <-> (
  2.1226 -  leftarg = epoint,
  2.1227 -  rightarg = ecircle,
  2.1228 -  procedure = epoint_ecircle_distance_proc,
  2.1229 -  commutator = <->
  2.1230 -);
  2.1231 -
  2.1232 -CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint)
  2.1233 -  RETURNS float8
  2.1234 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  2.1235 -
  2.1236 -CREATE OPERATOR <-> (
  2.1237 -  leftarg = ecircle,
  2.1238 -  rightarg = epoint,
  2.1239 -  procedure = epoint_ecircle_distance_commutator,
  2.1240 -  commutator = <->
  2.1241 -);
  2.1242 -
  2.1243 -CREATE OPERATOR <-> (
  2.1244 -  leftarg = epoint,
  2.1245 -  rightarg = ecluster,
  2.1246 -  procedure = epoint_ecluster_distance_proc,
  2.1247 -  commutator = <->
  2.1248 -);
  2.1249 -
  2.1250 -CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint)
  2.1251 -  RETURNS float8
  2.1252 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  2.1253 -
  2.1254 -CREATE OPERATOR <-> (
  2.1255 -  leftarg = ecluster,
  2.1256 -  rightarg = epoint,
  2.1257 -  procedure = epoint_ecluster_distance_commutator,
  2.1258 -  commutator = <->
  2.1259 -);
  2.1260 -
  2.1261 -CREATE OPERATOR <-> (
  2.1262 -  leftarg = ecircle,
  2.1263 -  rightarg = ecircle,
  2.1264 -  procedure = ecircle_distance_proc,
  2.1265 -  commutator = <->
  2.1266 -);
  2.1267 -
  2.1268 -CREATE OPERATOR <-> (
  2.1269 -  leftarg = ecircle,
  2.1270 -  rightarg = ecluster,
  2.1271 -  procedure = ecircle_ecluster_distance_proc,
  2.1272 -  commutator = <->
  2.1273 -);
  2.1274 -
  2.1275 -CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle)
  2.1276 -  RETURNS float8
  2.1277 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  2.1278 -
  2.1279 -CREATE OPERATOR <-> (
  2.1280 -  leftarg = ecluster,
  2.1281 -  rightarg = ecircle,
  2.1282 -  procedure = ecircle_ecluster_distance_commutator,
  2.1283 -  commutator = <->
  2.1284 -);
  2.1285 -
  2.1286 -CREATE OPERATOR <-> (
  2.1287 -  leftarg = ecluster,
  2.1288 -  rightarg = ecluster,
  2.1289 -  procedure = ecluster_distance_proc,
  2.1290 -  commutator = <->
  2.1291 -);
  2.1292 -
  2.1293 -CREATE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox)
  2.1294 -  RETURNS float8
  2.1295 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  2.1296 -
  2.1297 -CREATE OPERATOR <-> (
  2.1298 -  leftarg = epoint,
  2.1299 -  rightarg = ebox,
  2.1300 -  procedure = epoint_ebox_distance_castwrap,
  2.1301 -  commutator = <->
  2.1302 -);
  2.1303 -
  2.1304 -CREATE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint)
  2.1305 -  RETURNS float8
  2.1306 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  2.1307 -
  2.1308 -CREATE OPERATOR <-> (
  2.1309 -  leftarg = ebox,
  2.1310 -  rightarg = epoint,
  2.1311 -  procedure = epoint_ebox_distance_castwrap,
  2.1312 -  commutator = <->
  2.1313 -);
  2.1314 -
  2.1315 -CREATE FUNCTION ebox_distance_castwrap(ebox, ebox)
  2.1316 -  RETURNS float8
  2.1317 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2::ecluster';
  2.1318 -
  2.1319 -CREATE OPERATOR <-> (
  2.1320 -  leftarg = ebox,
  2.1321 -  rightarg = ebox,
  2.1322 -  procedure = ebox_distance_castwrap,
  2.1323 -  commutator = <->
  2.1324 -);
  2.1325 -
  2.1326 -CREATE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle)
  2.1327 -  RETURNS float8
  2.1328 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  2.1329 -
  2.1330 -CREATE OPERATOR <-> (
  2.1331 -  leftarg = ebox,
  2.1332 -  rightarg = ecircle,
  2.1333 -  procedure = ebox_ecircle_distance_castwrap,
  2.1334 -  commutator = <->
  2.1335 -);
  2.1336 -
  2.1337 -CREATE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox)
  2.1338 -  RETURNS float8
  2.1339 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  2.1340 -
  2.1341 -CREATE OPERATOR <-> (
  2.1342 -  leftarg = ecircle,
  2.1343 -  rightarg = ebox,
  2.1344 -  procedure = ebox_ecircle_distance_castwrap,
  2.1345 -  commutator = <->
  2.1346 -);
  2.1347 -
  2.1348 -CREATE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster)
  2.1349 -  RETURNS float8
  2.1350 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  2.1351 -
  2.1352 -CREATE OPERATOR <-> (
  2.1353 -  leftarg = ebox,
  2.1354 -  rightarg = ecluster,
  2.1355 -  procedure = ebox_ecluster_distance_castwrap,
  2.1356 -  commutator = <->
  2.1357 -);
  2.1358 -
  2.1359 -CREATE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox)
  2.1360 -  RETURNS float8
  2.1361 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  2.1362 -
  2.1363 -CREATE OPERATOR <-> (
  2.1364 -  leftarg = ecluster,
  2.1365 -  rightarg = ebox,
  2.1366 -  procedure = ebox_ecluster_distance_castwrap,
  2.1367 -  commutator = <->
  2.1368 -);
  2.1369 -
  2.1370 -
  2.1371 ----------------------
  2.1372 --- other functions --
  2.1373 ----------------------
  2.1374 -
  2.1375 -CREATE FUNCTION monte_carlo_area(ecluster, int4 = 10000)
  2.1376 -  RETURNS float8
  2.1377 -  LANGUAGE C STRICT
  2.1378 -  AS '$libdir/latlon-v0008', 'pgl_ecluster_monte_carlo_area';
  2.1379 -
  2.1380 -CREATE FUNCTION fair_distance(ecluster, epoint, int4 = 10000)
  2.1381 -  RETURNS float8
  2.1382 -  LANGUAGE C STRICT
  2.1383 -  AS '$libdir/latlon-v0008', 'pgl_ecluster_epoint_fair_distance';
  2.1384 -
  2.1385 -
  2.1386 -----------------
  2.1387 --- GiST index --
  2.1388 -----------------
  2.1389 -
  2.1390 -CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
  2.1391 -  RETURNS boolean
  2.1392 -  LANGUAGE C STRICT
  2.1393 -  AS '$libdir/latlon-v0008', 'pgl_gist_consistent';
  2.1394 -
  2.1395 -CREATE FUNCTION pgl_gist_union(internal, internal)
  2.1396 -  RETURNS internal
  2.1397 -  LANGUAGE C STRICT
  2.1398 -  AS '$libdir/latlon-v0008', 'pgl_gist_union';
  2.1399 -
  2.1400 -CREATE FUNCTION pgl_gist_compress_epoint(internal)
  2.1401 -  RETURNS internal
  2.1402 -  LANGUAGE C STRICT
  2.1403 -  AS '$libdir/latlon-v0008', 'pgl_gist_compress_epoint';
  2.1404 -
  2.1405 -CREATE FUNCTION pgl_gist_compress_ecircle(internal)
  2.1406 -  RETURNS internal
  2.1407 -  LANGUAGE C STRICT
  2.1408 -  AS '$libdir/latlon-v0008', 'pgl_gist_compress_ecircle';
  2.1409 -
  2.1410 -CREATE FUNCTION pgl_gist_compress_ecluster(internal)
  2.1411 -  RETURNS internal
  2.1412 -  LANGUAGE C STRICT
  2.1413 -  AS '$libdir/latlon-v0008', 'pgl_gist_compress_ecluster';
  2.1414 -
  2.1415 -CREATE FUNCTION pgl_gist_decompress(internal)
  2.1416 -  RETURNS internal
  2.1417 -  LANGUAGE C STRICT
  2.1418 -  AS '$libdir/latlon-v0008', 'pgl_gist_decompress';
  2.1419 -
  2.1420 -CREATE FUNCTION pgl_gist_penalty(internal, internal, internal)
  2.1421 -  RETURNS internal
  2.1422 -  LANGUAGE C STRICT
  2.1423 -  AS '$libdir/latlon-v0008', 'pgl_gist_penalty';
  2.1424 -
  2.1425 -CREATE FUNCTION pgl_gist_picksplit(internal, internal)
  2.1426 -  RETURNS internal
  2.1427 -  LANGUAGE C STRICT
  2.1428 -  AS '$libdir/latlon-v0008', 'pgl_gist_picksplit';
  2.1429 -
  2.1430 -CREATE FUNCTION pgl_gist_same(internal, internal, internal)
  2.1431 -  RETURNS internal
  2.1432 -  LANGUAGE C STRICT
  2.1433 -  AS '$libdir/latlon-v0008', 'pgl_gist_same';
  2.1434 -
  2.1435 -CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
  2.1436 -  RETURNS internal
  2.1437 -  LANGUAGE C STRICT
  2.1438 -  AS '$libdir/latlon-v0008', 'pgl_gist_distance';
  2.1439 -
  2.1440 -CREATE OPERATOR CLASS epoint_ops
  2.1441 -  DEFAULT FOR TYPE epoint USING gist AS
  2.1442 -  OPERATOR  11 = ,
  2.1443 -  OPERATOR  22 &&  (epoint, ebox),
  2.1444 -  OPERATOR 222 <@  (epoint, ebox),
  2.1445 -  OPERATOR  23 &&  (epoint, ecircle),
  2.1446 -  OPERATOR  24 &&  (epoint, ecluster),
  2.1447 -  OPERATOR 124 &&+ (epoint, ecluster),
  2.1448 -  OPERATOR 224 <@  (epoint, ecluster),
  2.1449 -  OPERATOR  31 <-> (epoint, epoint) FOR ORDER BY float_ops,
  2.1450 -  OPERATOR  32 <-> (epoint, ebox) FOR ORDER BY float_ops,
  2.1451 -  OPERATOR  33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
  2.1452 -  OPERATOR  34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
  2.1453 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  2.1454 -  FUNCTION 2 pgl_gist_union(internal, internal),
  2.1455 -  FUNCTION 3 pgl_gist_compress_epoint(internal),
  2.1456 -  FUNCTION 4 pgl_gist_decompress(internal),
  2.1457 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  2.1458 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  2.1459 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  2.1460 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  2.1461 -  STORAGE ekey_point;
  2.1462 -
  2.1463 -CREATE OPERATOR CLASS ecircle_ops
  2.1464 -  DEFAULT FOR TYPE ecircle USING gist AS
  2.1465 -  OPERATOR  13 = ,
  2.1466 -  OPERATOR  21 &&  (ecircle, epoint),
  2.1467 -  OPERATOR  22 &&  (ecircle, ebox),
  2.1468 -  OPERATOR 122 &&+ (ecircle, ebox),
  2.1469 -  OPERATOR  23 &&  (ecircle, ecircle),
  2.1470 -  OPERATOR  24 &&  (ecircle, ecluster),
  2.1471 -  OPERATOR 124 &&+ (ecircle, ecluster),
  2.1472 -  OPERATOR  31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
  2.1473 -  OPERATOR  32 <-> (ecircle, ebox) FOR ORDER BY float_ops,
  2.1474 -  OPERATOR  33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
  2.1475 -  OPERATOR  34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
  2.1476 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  2.1477 -  FUNCTION 2 pgl_gist_union(internal, internal),
  2.1478 -  FUNCTION 3 pgl_gist_compress_ecircle(internal),
  2.1479 -  FUNCTION 4 pgl_gist_decompress(internal),
  2.1480 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  2.1481 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  2.1482 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  2.1483 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  2.1484 -  STORAGE ekey_area;
  2.1485 -
  2.1486 -CREATE OPERATOR CLASS ecluster_ops
  2.1487 -  DEFAULT FOR TYPE ecluster USING gist AS
  2.1488 -  OPERATOR  21 &&  (ecluster, epoint),
  2.1489 -  OPERATOR 121 &&+ (ecluster, epoint),
  2.1490 -  OPERATOR 221 @>  (ecluster, epoint),
  2.1491 -  OPERATOR  22 &&  (ecluster, ebox),
  2.1492 -  OPERATOR 122 &&+ (ecluster, ebox),
  2.1493 -  OPERATOR 222 @>  (ecluster, ebox),
  2.1494 -  OPERATOR 322 <@  (ecluster, ebox),
  2.1495 -  OPERATOR  23 &&  (ecluster, ecircle),
  2.1496 -  OPERATOR 123 &&+ (ecluster, ecircle),
  2.1497 -  OPERATOR  24 &&  (ecluster, ecluster),
  2.1498 -  OPERATOR 124 &&+ (ecluster, ecluster),
  2.1499 -  OPERATOR 224 @>  (ecluster, ecluster),
  2.1500 -  OPERATOR 324 <@  (ecluster, ecluster),
  2.1501 -  OPERATOR  31 <-> (ecluster, epoint) FOR ORDER BY float_ops,
  2.1502 -  OPERATOR  32 <-> (ecluster, ebox) FOR ORDER BY float_ops,
  2.1503 -  OPERATOR  33 <-> (ecluster, ecircle) FOR ORDER BY float_ops,
  2.1504 -  OPERATOR  34 <-> (ecluster, ecluster) FOR ORDER BY float_ops,
  2.1505 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  2.1506 -  FUNCTION 2 pgl_gist_union(internal, internal),
  2.1507 -  FUNCTION 3 pgl_gist_compress_ecluster(internal),
  2.1508 -  FUNCTION 4 pgl_gist_decompress(internal),
  2.1509 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  2.1510 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  2.1511 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  2.1512 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  2.1513 -  STORAGE ekey_area;
  2.1514 -
  2.1515 -
  2.1516 ----------------------
  2.1517 --- alias functions --
  2.1518 ----------------------
  2.1519 -
  2.1520 -CREATE FUNCTION distance(epoint, epoint)
  2.1521 -  RETURNS float8
  2.1522 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  2.1523 -
  2.1524 -CREATE FUNCTION distance(ecluster, epoint)
  2.1525 -  RETURNS float8
  2.1526 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  2.1527 -
  2.1528 -CREATE FUNCTION distance_within(epoint, epoint, float8)
  2.1529 -  RETURNS boolean
  2.1530 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  2.1531 -
  2.1532 -CREATE FUNCTION distance_within(ecluster, epoint, float8)
  2.1533 -  RETURNS boolean
  2.1534 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  2.1535 -
  2.1536 -
  2.1537 ---------------------------------
  2.1538 --- other data storage formats --
  2.1539 ---------------------------------
  2.1540 -
  2.1541 -CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint')
  2.1542 -  RETURNS epoint
  2.1543 -  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
  2.1544 -    DECLARE
  2.1545 -      "result" epoint;
  2.1546 -    BEGIN
  2.1547 -      IF $3 = 'epoint_lonlat' THEN
  2.1548 -        -- avoid dynamic command execution for better performance
  2.1549 -        RETURN epoint($2, $1);
  2.1550 -      END IF;
  2.1551 -      IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN
  2.1552 -        -- avoid dynamic command execution for better performance
  2.1553 -        RETURN epoint($1, $2);
  2.1554 -      END IF;
  2.1555 -      EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2;
  2.1556 -      RETURN "result";
  2.1557 -    END;
  2.1558 -  $$;
  2.1559 -
  2.1560 -CREATE FUNCTION GeoJSON_LinearRing_vertices(jsonb, text = 'epoint_lonlat')
  2.1561 -  RETURNS SETOF jsonb
  2.1562 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  2.1563 -    SELECT "result" FROM
  2.1564 -      ( SELECT jsonb_array_length($1) - 1 ) AS "lastindex_row" ("lastindex")
  2.1565 -      CROSS JOIN LATERAL jsonb_array_elements(
  2.1566 -        CASE WHEN
  2.1567 -          coords_to_epoint(
  2.1568 -            ($1->0->>0)::float8,
  2.1569 -            ($1->0->>1)::float8,
  2.1570 -            $2
  2.1571 -          ) = coords_to_epoint(
  2.1572 -            ($1->"lastindex"->>0)::float8,
  2.1573 -            ($1->"lastindex"->>1)::float8,
  2.1574 -            $2
  2.1575 -          )
  2.1576 -        THEN
  2.1577 -          $1 - "lastindex"
  2.1578 -        ELSE
  2.1579 -          $1
  2.1580 -        END
  2.1581 -      ) AS "result_row" ("result")
  2.1582 -  $$;
  2.1583 -
  2.1584 -CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat')
  2.1585 -  RETURNS epoint
  2.1586 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  2.1587 -    SELECT CASE
  2.1588 -    WHEN $1->>'type' = 'Point' THEN
  2.1589 -      coords_to_epoint(
  2.1590 -        ($1->'coordinates'->>0)::float8,
  2.1591 -        ($1->'coordinates'->>1)::float8,
  2.1592 -        $2
  2.1593 -      )
  2.1594 -    WHEN $1->>'type' = 'Feature' THEN
  2.1595 -      GeoJSON_to_epoint($1->'geometry', $2)
  2.1596 -    ELSE
  2.1597 -      NULL
  2.1598 -    END
  2.1599 -  $$;
  2.1600 -
  2.1601 -CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat')
  2.1602 -  RETURNS ecluster
  2.1603 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  2.1604 -    SELECT CASE $1->>'type'
  2.1605 -    WHEN 'Point' THEN
  2.1606 -      coords_to_epoint(
  2.1607 -        ($1->'coordinates'->>0)::float8,
  2.1608 -        ($1->'coordinates'->>1)::float8,
  2.1609 -        $2
  2.1610 -      )::ecluster
  2.1611 -    WHEN 'MultiPoint' THEN
  2.1612 -      ( SELECT ecluster_create_multipoint(array_agg(
  2.1613 -          coords_to_epoint(
  2.1614 -            ("coord"->>0)::float8,
  2.1615 -            ("coord"->>1)::float8,
  2.1616 -            $2
  2.1617 -          )
  2.1618 -        ))
  2.1619 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  2.1620 -      )
  2.1621 -    WHEN 'LineString' THEN
  2.1622 -      ( SELECT ecluster_create_path(array_agg(
  2.1623 -          coords_to_epoint(
  2.1624 -            ("coord"->>0)::float8,
  2.1625 -            ("coord"->>1)::float8,
  2.1626 -            $2
  2.1627 -          )
  2.1628 -        ))
  2.1629 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  2.1630 -      )
  2.1631 -    WHEN 'MultiLineString' THEN
  2.1632 -      ( SELECT ecluster_concat(array_agg(
  2.1633 -          ( SELECT ecluster_create_path(array_agg(
  2.1634 -              coords_to_epoint(
  2.1635 -                ("coord"->>0)::float8,
  2.1636 -                ("coord"->>1)::float8,
  2.1637 -                $2
  2.1638 -              )
  2.1639 -            ))
  2.1640 -            FROM jsonb_array_elements("coord_array") AS "coord"
  2.1641 -          )
  2.1642 -        ))
  2.1643 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  2.1644 -      )
  2.1645 -    WHEN 'Polygon' THEN
  2.1646 -      ( SELECT ecluster_concat(array_agg(
  2.1647 -          ( SELECT ecluster_create_polygon(array_agg(
  2.1648 -              coords_to_epoint(
  2.1649 -                ("coord"->>0)::float8,
  2.1650 -                ("coord"->>1)::float8,
  2.1651 -                $2
  2.1652 -              )
  2.1653 -            ))
  2.1654 -            FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord"
  2.1655 -          )
  2.1656 -        ))
  2.1657 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  2.1658 -      )
  2.1659 -    WHEN 'MultiPolygon' THEN
  2.1660 -      ( SELECT ecluster_concat(array_agg(
  2.1661 -          ( SELECT ecluster_concat(array_agg(
  2.1662 -              ( SELECT ecluster_create_polygon(array_agg(
  2.1663 -                  coords_to_epoint(
  2.1664 -                    ("coord"->>0)::float8,
  2.1665 -                    ("coord"->>1)::float8,
  2.1666 -                    $2
  2.1667 -                  )
  2.1668 -                ))
  2.1669 -                FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord"
  2.1670 -              )
  2.1671 -            ))
  2.1672 -            FROM jsonb_array_elements("coord_array_array") AS "coord_array"
  2.1673 -          )
  2.1674 -        ))
  2.1675 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array"
  2.1676 -      )
  2.1677 -    WHEN 'GeometryCollection' THEN
  2.1678 -      ( SELECT ecluster_concat(array_agg(
  2.1679 -          GeoJSON_to_ecluster("geometry", $2)
  2.1680 -        ))
  2.1681 -        FROM jsonb_array_elements($1->'geometries') AS "geometry"
  2.1682 -      )
  2.1683 -    WHEN 'Feature' THEN
  2.1684 -      GeoJSON_to_ecluster($1->'geometry', $2)
  2.1685 -    WHEN 'FeatureCollection' THEN
  2.1686 -      ( SELECT ecluster_concat(array_agg(
  2.1687 -          GeoJSON_to_ecluster("feature", $2)
  2.1688 -        ))
  2.1689 -        FROM jsonb_array_elements($1->'features') AS "feature"
  2.1690 -      )
  2.1691 -    ELSE
  2.1692 -      NULL
  2.1693 -    END
  2.1694 -  $$;
  2.1695 -
     3.1 --- a/latlon-v0008.c	Mon Oct 31 13:06:31 2016 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,3285 +0,0 @@
     3.4 -
     3.5 -/*-------------*
     3.6 - *  C prelude  *
     3.7 - *-------------*/
     3.8 -
     3.9 -#include "postgres.h"
    3.10 -#include "fmgr.h"
    3.11 -#include "libpq/pqformat.h"
    3.12 -#include "access/gist.h"
    3.13 -#include "access/stratnum.h"
    3.14 -#include "utils/array.h"
    3.15 -#include <math.h>
    3.16 -
    3.17 -#ifdef PG_MODULE_MAGIC
    3.18 -PG_MODULE_MAGIC;
    3.19 -#endif
    3.20 -
    3.21 -#if INT_MAX < 2147483647
    3.22 -#error Expected int type to be at least 32 bit wide
    3.23 -#endif
    3.24 -
    3.25 -
    3.26 -/*---------------------------------*
    3.27 - *  distance calculation on earth  *
    3.28 - *  (using WGS-84 spheroid)        *
    3.29 - *---------------------------------*/
    3.30 -
    3.31 -/*  WGS-84 spheroid with following parameters:
    3.32 -    semi-major axis  a = 6378137
    3.33 -    semi-minor axis  b = a * (1 - 1/298.257223563)
    3.34 -    estimated diameter = 2 * (2*a+b)/3
    3.35 -*/
    3.36 -#define PGL_SPHEROID_A 6378137.0            /* semi major axis */
    3.37 -#define PGL_SPHEROID_F (1.0/298.257223563)  /* flattening */
    3.38 -#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F))
    3.39 -#define PGL_EPS2       ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \
    3.40 -                           PGL_SPHEROID_B * PGL_SPHEROID_B ) / \
    3.41 -                         ( PGL_SPHEROID_A * PGL_SPHEROID_A ) )
    3.42 -#define PGL_SUBEPS2    (1.0-PGL_EPS2)
    3.43 -#define PGL_RADIUS     ((2.0*PGL_SPHEROID_A + PGL_SPHEROID_B) / 3.0)
    3.44 -#define PGL_DIAMETER   (2.0 * PGL_RADIUS)
    3.45 -#define PGL_SCALE      (PGL_SPHEROID_A / PGL_DIAMETER)  /* semi-major ref. */
    3.46 -#define PGL_MAXDIST    (PGL_RADIUS * M_PI)              /* maximum distance */
    3.47 -#define PGL_FADELIMIT  (PGL_MAXDIST / 3.0)              /* 1/6 circumference */
    3.48 -
    3.49 -/* calculate distance between two points on earth (given in degrees) */
    3.50 -static inline double pgl_distance(
    3.51 -  double lat1, double lon1, double lat2, double lon2
    3.52 -) {
    3.53 -  float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin;
    3.54 -  float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t;
    3.55 -  /* normalize delta longitude (lon2 > 0 && lon1 = 0) */
    3.56 -  /* lon1 = 0 (not used anymore) */
    3.57 -  lon2 = fabs(lon2-lon1);
    3.58 -  /* convert to radians (first divide, then multiply) */
    3.59 -  lat1 = (lat1 / 180.0) * M_PI;
    3.60 -  lat2 = (lat2 / 180.0) * M_PI;
    3.61 -  lon2 = (lon2 / 180.0) * M_PI;
    3.62 -  /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point
    3.63 -     operations (lon2 >= lon1 is already ensured in a previous step) */
    3.64 -  if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; }
    3.65 -  /* calculate 3d coordinates on scaled ellipsoid which has an average diameter
    3.66 -     of 1.0 */
    3.67 -  lat1cos = cos(lat1); lat1sin = sin(lat1);
    3.68 -  lat2cos = cos(lat2); lat2sin = sin(lat2);
    3.69 -  lon2cos = cos(lon2); lon2sin = sin(lon2);
    3.70 -  nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin);
    3.71 -  nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
    3.72 -  x1 = nphi1 * lat1cos;
    3.73 -  z1 = nphi1 * PGL_SUBEPS2 * lat1sin;
    3.74 -  x2 = nphi2 * lat2cos * lon2cos;
    3.75 -  y2 = nphi2 * lat2cos * lon2sin;
    3.76 -  z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
    3.77 -  /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */
    3.78 -  g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
    3.79 -  /* convert tunnel distance through scaled ellipsoid to approximated surface
    3.80 -     distance on original ellipsoid */
    3.81 -  if (g > 1.0) g = 1.0;
    3.82 -  s = PGL_DIAMETER * asin(g);
    3.83 -  /* return result only if small enough to be precise (less than 1/3 of
    3.84 -     maximum possible distance) */
    3.85 -  if (s <= PGL_FADELIMIT) return s;
    3.86 -  /* calculate tunnel distance to antipodal point through scaled ellipsoid */
    3.87 -  g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1));
    3.88 -  /* convert tunnel distance to antipodal point through scaled ellipsoid to
    3.89 -     approximated surface distance to antipodal point on original ellipsoid */
    3.90 -  if (g > 1.0) g = 1.0;
    3.91 -  t = PGL_DIAMETER * asin(g);
    3.92 -  /* surface distance between original points can now be approximated by
    3.93 -     substracting antipodal distance from maximum possible distance;
    3.94 -     return result only if small enough (less than 1/3 of maximum possible
    3.95 -     distance) */
    3.96 -  if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t;
    3.97 -  /* otherwise crossfade direct and antipodal result to ensure monotonicity */
    3.98 -  return (
    3.99 -    (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) /
   3.100 -    (s + t - 2*PGL_FADELIMIT)
   3.101 -  );
   3.102 -}
   3.103 -
   3.104 -/* finite distance that can not be reached on earth */
   3.105 -#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST)
   3.106 -
   3.107 -
   3.108 -/*--------------------------------*
   3.109 - *  simple geographic data types  *
   3.110 - *--------------------------------*/
   3.111 -
   3.112 -/* point on earth given by latitude and longitude in degrees */
   3.113 -/* (type "epoint" in SQL) */
   3.114 -typedef struct {
   3.115 -  double lat;  /* between  -90 and  90 (both inclusive) */
   3.116 -  double lon;  /* between -180 and 180 (both inclusive) */
   3.117 -} pgl_point;
   3.118 -
   3.119 -/* box delimited by two parallels and two meridians (all in degrees) */
   3.120 -/* (type "ebox" in SQL) */
   3.121 -typedef struct {
   3.122 -  double lat_min;  /* between  -90 and  90 (both inclusive) */
   3.123 -  double lat_max;  /* between  -90 and  90 (both inclusive) */
   3.124 -  double lon_min;  /* between -180 and 180 (both inclusive) */
   3.125 -  double lon_max;  /* between -180 and 180 (both inclusive) */
   3.126 -  /* if lat_min > lat_max, then box is empty */
   3.127 -  /* if lon_min > lon_max, then 180th meridian is crossed */
   3.128 -} pgl_box;
   3.129 -
   3.130 -/* circle on earth surface (for radial searches with fixed radius) */
   3.131 -/* (type "ecircle" in SQL) */
   3.132 -typedef struct {
   3.133 -  pgl_point center;
   3.134 -  double radius; /* positive (including +0 but excluding -0), or -INFINITY */
   3.135 -  /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point),
   3.136 -     zero radius (0) denotes a single point,
   3.137 -     a finite radius (0 < radius < INFINITY) denotes a filled circle, and
   3.138 -     a radius of INFINITY is valid and means complete coverage of earth. */
   3.139 -} pgl_circle;
   3.140 -
   3.141 -
   3.142 -/*----------------------------------*
   3.143 - *  geographic "cluster" data type  *
   3.144 - *----------------------------------*/
   3.145 -
   3.146 -/* A cluster is a collection of points, paths, outlines, and polygons. If two
   3.147 -   polygons in a cluster overlap, the area covered by both polygons does not
   3.148 -   belong to the cluster. This way, a cluster can be used to describe complex
   3.149 -   shapes like polygons with holes. Outlines are non-filled polygons. Paths are
   3.150 -   open by default (i.e. the last point in the list is not connected with the
   3.151 -   first point in the list). Note that each outline or polygon in a cluster
   3.152 -   must cover a longitude range of less than 180 degrees to avoid ambiguities.
   3.153 -   Areas which are larger may be split into multiple polygons. */
   3.154 -
   3.155 -/* maximum number of points in a cluster */
   3.156 -/* (limited to avoid integer overflows, e.g. when allocating memory) */
   3.157 -#define PGL_CLUSTER_MAXPOINTS 16777216
   3.158 -
   3.159 -/* types of cluster entries */
   3.160 -#define PGL_ENTRY_POINT   1  /* a point */
   3.161 -#define PGL_ENTRY_PATH    2  /* a path from first point to last point */
   3.162 -#define PGL_ENTRY_OUTLINE 3  /* a non-filled polygon with given vertices */
   3.163 -#define PGL_ENTRY_POLYGON 4  /* a filled polygon with given vertices */
   3.164 -
   3.165 -/* Entries of a cluster are described by two different structs: pgl_newentry
   3.166 -   and pgl_entry. The first is used only during construction of a cluster, the
   3.167 -   second is used in all other cases (e.g. when reading clusters from the
   3.168 -   database, performing operations, etc). */
   3.169 -
   3.170 -/* entry for new geographic cluster during construction of that cluster */
   3.171 -typedef struct {
   3.172 -  int32_t entrytype;
   3.173 -  int32_t npoints;
   3.174 -  pgl_point *points;  /* pointer to an array of points (pgl_point) */
   3.175 -} pgl_newentry;
   3.176 -
   3.177 -/* entry of geographic cluster */
   3.178 -typedef struct {
   3.179 -  int32_t entrytype;  /* type of entry: point, path, outline, polygon */
   3.180 -  int32_t npoints;    /* number of stored points (set to 1 for point entry) */
   3.181 -  int32_t offset;     /* offset of pgl_point array from cluster base address */
   3.182 -  /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */
   3.183 -} pgl_entry;
   3.184 -
   3.185 -/* geographic cluster which is a collection of points, (open) paths, polygons,
   3.186 -   and outlines (non-filled polygons) */
   3.187 -typedef struct {
   3.188 -  char header[VARHDRSZ];  /* PostgreSQL header for variable size data types */
   3.189 -  int32_t nentries;       /* number of stored points */
   3.190 -  pgl_circle bounding;    /* bounding circle */
   3.191 -  /* Note: bounding circle ensures alignment of pgl_cluster for points */
   3.192 -  pgl_entry entries[FLEXIBLE_ARRAY_MEMBER];  /* var-length data */
   3.193 -} pgl_cluster;
   3.194 -
   3.195 -/* macro to determine memory alignment of points */
   3.196 -/* (needed to store pgl_point array after entries in pgl_cluster) */
   3.197 -typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment;
   3.198 -#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned)
   3.199 -
   3.200 -/* macro to extract a pointer to the array of points of a cluster entry */
   3.201 -#define PGL_ENTRY_POINTS(cluster, idx) \
   3.202 -  ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset))
   3.203 -
   3.204 -/* convert pgl_newentry array to pgl_cluster */
   3.205 -/* NOTE: requires pgl_finalize_cluster to be called to finalize result */
   3.206 -static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) {
   3.207 -  int i;              /* index of current entry */
   3.208 -  int npoints = 0;    /* number of points in whole cluster */
   3.209 -  int entry_npoints;  /* number of points in current entry */
   3.210 -  int points_offset = PGL_POINT_ALIGNMENT * (
   3.211 -    ( offsetof(pgl_cluster, entries) +
   3.212 -      nentries * sizeof(pgl_entry) +
   3.213 -      PGL_POINT_ALIGNMENT - 1
   3.214 -    ) / PGL_POINT_ALIGNMENT
   3.215 -  );  /* offset of pgl_point array from base address (considering alignment) */
   3.216 -  pgl_cluster *cluster;  /* new cluster to be returned */
   3.217 -  /* determine total number of points */
   3.218 -  for (i=0; i<nentries; i++) npoints += entries[i].npoints;
   3.219 -  /* allocate memory for cluster (including entries and points) */
   3.220 -  cluster = palloc(points_offset + npoints * sizeof(pgl_point));
   3.221 -  /* re-count total number of points to determine offset for each entry */
   3.222 -  npoints = 0;
   3.223 -  /* copy entries and points */
   3.224 -  for (i=0; i<nentries; i++) {
   3.225 -    /* determine number of points in entry */
   3.226 -    entry_npoints = entries[i].npoints;
   3.227 -    /* copy entry */
   3.228 -    cluster->entries[i].entrytype = entries[i].entrytype;
   3.229 -    cluster->entries[i].npoints = entry_npoints;
   3.230 -    /* calculate offset (in bytes) of pgl_point array */
   3.231 -    cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point);
   3.232 -    /* copy points */
   3.233 -    memcpy(
   3.234 -      PGL_ENTRY_POINTS(cluster, i),
   3.235 -      entries[i].points,
   3.236 -      entry_npoints * sizeof(pgl_point)
   3.237 -    );
   3.238 -    /* update total number of points processed */
   3.239 -    npoints += entry_npoints;
   3.240 -  }
   3.241 -  /* set number of entries in cluster */
   3.242 -  cluster->nentries = nentries;
   3.243 -  /* set PostgreSQL header for variable sized data */
   3.244 -  SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point));
   3.245 -  /* return newly created cluster */
   3.246 -  return cluster;
   3.247 -}
   3.248 -
   3.249 -
   3.250 -/*----------------------------------------*
   3.251 - *  C functions on geographic data types  *
   3.252 - *----------------------------------------*/
   3.253 -
   3.254 -/* round latitude or longitude to 12 digits after decimal point */
   3.255 -static inline double pgl_round(double val) {
   3.256 -  return round(val * 1e12) / 1e12;
   3.257 -}
   3.258 -
   3.259 -/* compare two points */
   3.260 -/* (equality when same point on earth is described, otherwise an arbitrary
   3.261 -   linear order) */
   3.262 -static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) {
   3.263 -  double lon1, lon2;  /* modified longitudes for special cases */
   3.264 -  /* use latitude as first ordering criterion */
   3.265 -  if (point1->lat < point2->lat) return -1;
   3.266 -  if (point1->lat > point2->lat) return 1;
   3.267 -  /* determine modified longitudes (considering special case of poles and
   3.268 -     180th meridian which can be described as W180 or E180) */
   3.269 -  if (point1->lat == -90 || point1->lat == 90) lon1 = 0;
   3.270 -  else if (point1->lon == 180) lon1 = -180;
   3.271 -  else lon1 = point1->lon;
   3.272 -  if (point2->lat == -90 || point2->lat == 90) lon2 = 0;
   3.273 -  else if (point2->lon == 180) lon2 = -180;
   3.274 -  else lon2 = point2->lon;
   3.275 -  /* use (modified) longitude as secondary ordering criterion */
   3.276 -  if (lon1 < lon2) return -1;
   3.277 -  if (lon1 > lon2) return 1;
   3.278 -  /* no difference found, points are equal */
   3.279 -  return 0;
   3.280 -}
   3.281 -
   3.282 -/* compare two boxes */
   3.283 -/* (equality when same box on earth is described, otherwise an arbitrary linear
   3.284 -   order) */
   3.285 -static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) {
   3.286 -  /* two empty boxes are equal, and an empty box is always considered "less
   3.287 -     than" a non-empty box */
   3.288 -  if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1;
   3.289 -  if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0;
   3.290 -  if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1;
   3.291 -  /* use southern border as first ordering criterion */
   3.292 -  if (box1->lat_min < box2->lat_min) return -1;
   3.293 -  if (box1->lat_min > box2->lat_min) return 1;
   3.294 -  /* use northern border as second ordering criterion */
   3.295 -  if (box1->lat_max < box2->lat_max) return -1;
   3.296 -  if (box1->lat_max > box2->lat_max) return 1;
   3.297 -  /* use western border as third ordering criterion */
   3.298 -  if (box1->lon_min < box2->lon_min) return -1;
   3.299 -  if (box1->lon_min > box2->lon_min) return 1;
   3.300 -  /* use eastern border as fourth ordering criterion */
   3.301 -  if (box1->lon_max < box2->lon_max) return -1;
   3.302 -  if (box1->lon_max > box2->lon_max) return 1;
   3.303 -  /* no difference found, boxes are equal */
   3.304 -  return 0;
   3.305 -}
   3.306 -
   3.307 -/* compare two circles */
   3.308 -/* (equality when same circle on earth is described, otherwise an arbitrary
   3.309 -   linear order) */
   3.310 -static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) {
   3.311 -  /* two circles with same infinite radius (positive or negative infinity) are
   3.312 -     considered equal independently of center point */
   3.313 -  if (
   3.314 -    !isfinite(circle1->radius) && !isfinite(circle2->radius) &&
   3.315 -    circle1->radius == circle2->radius
   3.316 -  ) return 0;
   3.317 -  /* use radius as first ordering criterion */
   3.318 -  if (circle1->radius < circle2->radius) return -1;
   3.319 -  if (circle1->radius > circle2->radius) return 1;
   3.320 -  /* use center point as secondary ordering criterion */
   3.321 -  return pgl_point_cmp(&(circle1->center), &(circle2->center));
   3.322 -}
   3.323 -
   3.324 -/* set box to empty box*/
   3.325 -static void pgl_box_set_empty(pgl_box *box) {
   3.326 -  box->lat_min = INFINITY;
   3.327 -  box->lat_max = -INFINITY;
   3.328 -  box->lon_min = 0;
   3.329 -  box->lon_max = 0;
   3.330 -}
   3.331 -
   3.332 -/* check if point is inside a box */
   3.333 -static bool pgl_point_in_box(pgl_point *point, pgl_box *box) {
   3.334 -  return (
   3.335 -    point->lat >= box->lat_min && point->lat <= box->lat_max && (
   3.336 -      (box->lon_min > box->lon_max) ? (
   3.337 -        /* box crosses 180th meridian */
   3.338 -        point->lon >= box->lon_min || point->lon <= box->lon_max
   3.339 -      ) : (
   3.340 -        /* box does not cross the 180th meridian */
   3.341 -        point->lon >= box->lon_min && point->lon <= box->lon_max
   3.342 -      )
   3.343 -    )
   3.344 -  );
   3.345 -}
   3.346 -
   3.347 -/* check if two boxes overlap */
   3.348 -static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) {
   3.349 -  return (
   3.350 -    box2->lat_max >= box2->lat_min &&  /* ensure box2 is not empty */
   3.351 -    ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) &&
   3.352 -    ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && (
   3.353 -      (
   3.354 -        /* check if one and only one box crosses the 180th meridian */
   3.355 -        ((box1->lon_min > box1->lon_max) ? 1 : 0) ^
   3.356 -        ((box2->lon_min > box2->lon_max) ? 1 : 0)
   3.357 -      ) ? (
   3.358 -        /* exactly one box crosses the 180th meridian */
   3.359 -        box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min ||
   3.360 -        box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max
   3.361 -      ) : (
   3.362 -        /* no box or both boxes cross the 180th meridian */
   3.363 -        (
   3.364 -          (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) &&
   3.365 -          (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max)
   3.366 -        ) ||
   3.367 -        /* handle W180 == E180 */
   3.368 -        ( box1->lon_min == -180 && box2->lon_max == 180 ) ||
   3.369 -        ( box2->lon_min == -180 && box1->lon_max == 180 )
   3.370 -      )
   3.371 -    )
   3.372 -  );
   3.373 -}
   3.374 -
   3.375 -/* check unambiguousness of east/west orientation of cluster entries and set
   3.376 -   bounding circle of cluster */
   3.377 -static bool pgl_finalize_cluster(pgl_cluster *cluster) {
   3.378 -  int i, j;                 /* i: index of entry, j: index of point in entry */
   3.379 -  int npoints;              /* number of points in entry */
   3.380 -  int total_npoints = 0;    /* total number of points in cluster */
   3.381 -  pgl_point *points;        /* points in entry */
   3.382 -  int lon_dir;              /* first point of entry west (-1) or east (+1) */
   3.383 -  double lon_break = 0;     /* antipodal longitude of first point in entry */
   3.384 -  double lon_min, lon_max;  /* covered longitude range of entry */
   3.385 -  double value;             /* temporary variable */
   3.386 -  /* reset bounding circle center to empty circle at 0/0 coordinates */
   3.387 -  cluster->bounding.center.lat = 0;
   3.388 -  cluster->bounding.center.lon = 0;
   3.389 -  cluster->bounding.radius = -INFINITY;
   3.390 -  /* if cluster is not empty */
   3.391 -  if (cluster->nentries != 0) {
   3.392 -    /* iterate over all cluster entries and ensure they each cover a longitude
   3.393 -       range less than 180 degrees */
   3.394 -    for (i=0; i<cluster->nentries; i++) {
   3.395 -      /* get properties of entry */
   3.396 -      npoints = cluster->entries[i].npoints;
   3.397 -      points = PGL_ENTRY_POINTS(cluster, i);
   3.398 -      /* get longitude of first point of entry */
   3.399 -      value = points[0].lon;
   3.400 -      /* initialize lon_min and lon_max with longitude of first point */
   3.401 -      lon_min = value;
   3.402 -      lon_max = value;
   3.403 -      /* determine east/west orientation of first point and calculate antipodal
   3.404 -         longitude (Note: rounding required here) */
   3.405 -      if      (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); }
   3.406 -      else if (value > 0) { lon_dir =  1; lon_break = pgl_round(value - 180); }
   3.407 -      else lon_dir = 0;
   3.408 -      /* iterate over all other points in entry */
   3.409 -      for (j=1; j<npoints; j++) {
   3.410 -        /* consider longitude wrap-around */
   3.411 -        value = points[j].lon;
   3.412 -        if      (lon_dir<0 && value>lon_break) value = pgl_round(value - 360);
   3.413 -        else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360);
   3.414 -        /* update lon_min and lon_max */
   3.415 -        if      (value < lon_min) lon_min = value;
   3.416 -        else if (value > lon_max) lon_max = value;
   3.417 -        /* return false if 180 degrees or more are covered */
   3.418 -        if (lon_max - lon_min >= 180) return false;
   3.419 -      }
   3.420 -    }
   3.421 -    /* iterate over all points of all entries and calculate arbitrary center
   3.422 -       point for bounding circle (best if center point minimizes the radius,
   3.423 -       but some error is allowed here) */
   3.424 -    for (i=0; i<cluster->nentries; i++) {
   3.425 -      /* get properties of entry */
   3.426 -      npoints = cluster->entries[i].npoints;
   3.427 -      points = PGL_ENTRY_POINTS(cluster, i);
   3.428 -      /* check if first entry */
   3.429 -      if (i==0) {
   3.430 -        /* get longitude of first point of first entry in whole cluster */
   3.431 -        value = points[0].lon;
   3.432 -        /* initialize lon_min and lon_max with longitude of first point of
   3.433 -           first entry in whole cluster (used to determine if whole cluster
   3.434 -           covers a longitude range of 180 degrees or more) */
   3.435 -        lon_min = value;
   3.436 -        lon_max = value;
   3.437 -        /* determine east/west orientation of first point and calculate
   3.438 -           antipodal longitude (Note: rounding not necessary here) */
   3.439 -        if      (value < 0) { lon_dir = -1; lon_break = value + 180; }
   3.440 -        else if (value > 0) { lon_dir =  1; lon_break = value - 180; }
   3.441 -        else lon_dir = 0;
   3.442 -      }
   3.443 -      /* iterate over all points in entry */
   3.444 -      for (j=0; j<npoints; j++) {
   3.445 -        /* longitude wrap-around (Note: rounding not necessary here) */
   3.446 -        value = points[j].lon;
   3.447 -        if      (lon_dir < 0 && value > lon_break) value -= 360;
   3.448 -        else if (lon_dir > 0 && value < lon_break) value += 360;
   3.449 -        if      (value < lon_min) lon_min = value;
   3.450 -        else if (value > lon_max) lon_max = value;
   3.451 -        /* set bounding circle to cover whole earth if more than 180 degrees
   3.452 -           are covered */
   3.453 -        if (lon_max - lon_min >= 180) {
   3.454 -          cluster->bounding.center.lat = 0;
   3.455 -          cluster->bounding.center.lon = 0;
   3.456 -          cluster->bounding.radius = INFINITY;
   3.457 -          return true;
   3.458 -        }
   3.459 -        /* add point to bounding circle center (for average calculation) */
   3.460 -        cluster->bounding.center.lat += points[j].lat;
   3.461 -        cluster->bounding.center.lon += value;
   3.462 -      }
   3.463 -      /* count total number of points */
   3.464 -      total_npoints += npoints;
   3.465 -    }
   3.466 -    /* determine average latitude and longitude of cluster */
   3.467 -    cluster->bounding.center.lat /= total_npoints;
   3.468 -    cluster->bounding.center.lon /= total_npoints;
   3.469 -    /* normalize longitude of center of cluster bounding circle */
   3.470 -    if (cluster->bounding.center.lon < -180) {
   3.471 -      cluster->bounding.center.lon += 360;
   3.472 -    }
   3.473 -    else if (cluster->bounding.center.lon > 180) {
   3.474 -      cluster->bounding.center.lon -= 360;
   3.475 -    }
   3.476 -    /* round bounding circle center (useful if it is used by other functions) */
   3.477 -    cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat);
   3.478 -    cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon);
   3.479 -    /* calculate radius of bounding circle */
   3.480 -    for (i=0; i<cluster->nentries; i++) {
   3.481 -      npoints = cluster->entries[i].npoints;
   3.482 -      points = PGL_ENTRY_POINTS(cluster, i);
   3.483 -      for (j=0; j<npoints; j++) {
   3.484 -        value = pgl_distance(
   3.485 -          cluster->bounding.center.lat, cluster->bounding.center.lon,
   3.486 -          points[j].lat, points[j].lon
   3.487 -        );
   3.488 -        if (value > cluster->bounding.radius) cluster->bounding.radius = value;
   3.489 -      }
   3.490 -    }
   3.491 -  }
   3.492 -  /* return true (east/west orientation is unambiguous) */
   3.493 -  return true;
   3.494 -}
   3.495 -
   3.496 -/* check if point is inside cluster */
   3.497 -/* (if point is on perimeter, then true is returned if and only if
   3.498 -   strict == false) */
   3.499 -static bool pgl_point_in_cluster(
   3.500 -  pgl_point *point,
   3.501 -  pgl_cluster *cluster,
   3.502 -  bool strict
   3.503 -) {
   3.504 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   3.505 -  int entrytype;         /* type of entry */
   3.506 -  int npoints;           /* number of points in entry */
   3.507 -  pgl_point *points;     /* array of points in entry */
   3.508 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   3.509 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   3.510 -  double lat0 = point->lat;  /* latitude of point */
   3.511 -  double lon0;           /* (adjusted) longitude of point */
   3.512 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   3.513 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   3.514 -  double lon;            /* longitude of intersection */
   3.515 -  int counter = 0;       /* counter for intersections east of point */
   3.516 -  /* iterate over all entries */
   3.517 -  for (i=0; i<cluster->nentries; i++) {
   3.518 -    /* get type of entry */
   3.519 -    entrytype = cluster->entries[i].entrytype;
   3.520 -    /* skip all entries but polygons if perimeters are excluded */
   3.521 -    if (strict && entrytype != PGL_ENTRY_POLYGON) continue;
   3.522 -    /* get points of entry */
   3.523 -    npoints = cluster->entries[i].npoints;
   3.524 -    points = PGL_ENTRY_POINTS(cluster, i);
   3.525 -    /* determine east/west orientation of first point of entry and calculate
   3.526 -       antipodal longitude */
   3.527 -    lon_break = points[0].lon;
   3.528 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   3.529 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   3.530 -    else lon_dir = 0;
   3.531 -    /* get longitude of point */
   3.532 -    lon0 = point->lon;
   3.533 -    /* consider longitude wrap-around for point */
   3.534 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360);
   3.535 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360);
   3.536 -    /* iterate over all edges and vertices */
   3.537 -    for (j=0; j<npoints; j++) {
   3.538 -      /* return if point is on vertex of polygon */
   3.539 -      if (pgl_point_cmp(point, &(points[j])) == 0) return !strict;
   3.540 -      /* calculate index of next vertex */
   3.541 -      k = (j+1) % npoints;
   3.542 -      /* skip last edge unless entry is (closed) outline or polygon */
   3.543 -      if (
   3.544 -        k == 0 &&
   3.545 -        entrytype != PGL_ENTRY_OUTLINE &&
   3.546 -        entrytype != PGL_ENTRY_POLYGON
   3.547 -      ) continue;
   3.548 -      /* use previously calculated values for lat1 and lon1 if possible */
   3.549 -      if (j) {
   3.550 -        lat1 = lat2;
   3.551 -        lon1 = lon2;
   3.552 -      } else {
   3.553 -        /* otherwise get latitude and longitude values of first vertex */
   3.554 -        lat1 = points[0].lat;
   3.555 -        lon1 = points[0].lon;
   3.556 -        /* and consider longitude wrap-around for first vertex */
   3.557 -        if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   3.558 -        else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   3.559 -      }
   3.560 -      /* get latitude and longitude of next vertex */
   3.561 -      lat2 = points[k].lat;
   3.562 -      lon2 = points[k].lon;
   3.563 -      /* consider longitude wrap-around for next vertex */
   3.564 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
   3.565 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
   3.566 -      /* return if point is on horizontal (west to east) edge of polygon */
   3.567 -      if (
   3.568 -        lat0 == lat1 && lat0 == lat2 &&
   3.569 -        ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) )
   3.570 -      ) return !strict;
   3.571 -      /* check if edge crosses east/west line of point */
   3.572 -      if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) {
   3.573 -        /* calculate longitude of intersection */
   3.574 -        lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1);
   3.575 -        /* return if intersection goes (approximately) through point */
   3.576 -        if (pgl_round(lon) == lon0) return !strict;
   3.577 -        /* count intersection if east of point and entry is polygon*/
   3.578 -        if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++;
   3.579 -      }
   3.580 -    }
   3.581 -  }
   3.582 -  /* return true if number of intersections is odd */
   3.583 -  return counter & 1;
   3.584 -}
   3.585 -
   3.586 -/* check if all points of the second cluster are strictly inside the first
   3.587 -   cluster */
   3.588 -static inline bool pgl_all_cluster_points_strictly_in_cluster(
   3.589 -  pgl_cluster *outer, pgl_cluster *inner
   3.590 -) {
   3.591 -  int i, j;           /* i: entry, j: point in entry */
   3.592 -  int npoints;        /* number of points in entry */
   3.593 -  pgl_point *points;  /* array of points in entry */
   3.594 -  /* iterate over all entries of "inner" cluster */
   3.595 -  for (i=0; i<inner->nentries; i++) {
   3.596 -    /* get properties of entry */
   3.597 -    npoints = inner->entries[i].npoints;
   3.598 -    points = PGL_ENTRY_POINTS(inner, i);
   3.599 -    /* iterate over all points in entry of "inner" cluster */
   3.600 -    for (j=0; j<npoints; j++) {
   3.601 -      /* return false if one point of inner cluster is not in outer cluster */
   3.602 -      if (!pgl_point_in_cluster(points+j, outer, true)) return false;
   3.603 -    }
   3.604 -  }
   3.605 -  /* otherwise return true */
   3.606 -  return true;
   3.607 -}
   3.608 -
   3.609 -/* check if any point the second cluster is inside the first cluster */
   3.610 -static inline bool pgl_any_cluster_points_in_cluster(
   3.611 -  pgl_cluster *outer, pgl_cluster *inner
   3.612 -) {
   3.613 -  int i, j;           /* i: entry, j: point in entry */
   3.614 -  int npoints;        /* number of points in entry */
   3.615 -  pgl_point *points;  /* array of points in entry */
   3.616 -  /* iterate over all entries of "inner" cluster */
   3.617 -  for (i=0; i<inner->nentries; i++) {
   3.618 -    /* get properties of entry */
   3.619 -    npoints = inner->entries[i].npoints;
   3.620 -    points = PGL_ENTRY_POINTS(inner, i);
   3.621 -    /* iterate over all points in entry of "inner" cluster */
   3.622 -    for (j=0; j<npoints; j++) {
   3.623 -      /* return true if one point of inner cluster is in outer cluster */
   3.624 -      if (pgl_point_in_cluster(points+j, outer, false)) return true;
   3.625 -    }
   3.626 -  }
   3.627 -  /* otherwise return false */
   3.628 -  return false;
   3.629 -}
   3.630 -
   3.631 -/* check if line segment strictly crosses line (not just touching) */
   3.632 -static inline bool pgl_lseg_crosses_line(
   3.633 -  double seg_x1,  double seg_y1,  double seg_x2,  double seg_y2,
   3.634 -  double line_x1, double line_y1, double line_x2, double line_y2
   3.635 -) {
   3.636 -  return (
   3.637 -    (
   3.638 -      (seg_x1-line_x1) * (line_y2-line_y1) -
   3.639 -      (seg_y1-line_y1) * (line_x2-line_x1)
   3.640 -    ) * (
   3.641 -      (seg_x2-line_x1) * (line_y2-line_y1) -
   3.642 -      (seg_y2-line_y1) * (line_x2-line_x1)
   3.643 -    )
   3.644 -  ) < 0;
   3.645 -}
   3.646 -
   3.647 -/* check if paths and outlines of two clusters strictly overlap (not just
   3.648 -   touching) */
   3.649 -static bool pgl_outlines_overlap(
   3.650 -  pgl_cluster *cluster1, pgl_cluster *cluster2
   3.651 -) {
   3.652 -  int i1, j1, k1;  /* i: entry, j: point in entry, k: next point in entry */
   3.653 -  int i2, j2, k2;
   3.654 -  int entrytype1, entrytype2;     /* type of entry */
   3.655 -  int npoints1, npoints2;         /* number of points in entry */
   3.656 -  pgl_point *points1;             /* array of points in entry of cluster1 */
   3.657 -  pgl_point *points2;             /* array of points in entry of cluster2 */
   3.658 -  int lon_dir1, lon_dir2;         /* first vertex west (-1) or east (+1) */
   3.659 -  double lon_break1, lon_break2;  /* antipodal longitude of first vertex */
   3.660 -  double lat11, lon11;  /* latitude and (adjusted) longitude of vertex */
   3.661 -  double lat12, lon12;  /* latitude and (adjusted) longitude of next vertex */
   3.662 -  double lat21, lon21;  /* latitude and (adjusted) longitudes for cluster2 */
   3.663 -  double lat22, lon22;
   3.664 -  double wrapvalue;     /* temporary helper value to adjust wrap-around */  
   3.665 -  /* iterate over all entries of cluster1 */
   3.666 -  for (i1=0; i1<cluster1->nentries; i1++) {
   3.667 -    /* get properties of entry in cluster1 and skip points */
   3.668 -    npoints1 = cluster1->entries[i1].npoints;
   3.669 -    if (npoints1 < 2) continue;
   3.670 -    entrytype1 = cluster1->entries[i1].entrytype;
   3.671 -    points1 = PGL_ENTRY_POINTS(cluster1, i1);
   3.672 -    /* determine east/west orientation of first point and calculate antipodal
   3.673 -       longitude */
   3.674 -    lon_break1 = points1[0].lon;
   3.675 -    if (lon_break1 < 0) {
   3.676 -      lon_dir1   = -1;
   3.677 -      lon_break1 = pgl_round(lon_break1 + 180);
   3.678 -    } else if (lon_break1 > 0) {
   3.679 -      lon_dir1   = 1;
   3.680 -      lon_break1 = pgl_round(lon_break1 - 180);
   3.681 -    } else lon_dir1 = 0;
   3.682 -    /* iterate over all edges and vertices in cluster1 */
   3.683 -    for (j1=0; j1<npoints1; j1++) {
   3.684 -      /* calculate index of next vertex */
   3.685 -      k1 = (j1+1) % npoints1;
   3.686 -      /* skip last edge unless entry is (closed) outline or polygon */
   3.687 -      if (
   3.688 -        k1 == 0 &&
   3.689 -        entrytype1 != PGL_ENTRY_OUTLINE &&
   3.690 -        entrytype1 != PGL_ENTRY_POLYGON
   3.691 -      ) continue;
   3.692 -      /* use previously calculated values for lat1 and lon1 if possible */
   3.693 -      if (j1) {
   3.694 -        lat11 = lat12;
   3.695 -        lon11 = lon12;
   3.696 -      } else {
   3.697 -        /* otherwise get latitude and longitude values of first vertex */
   3.698 -        lat11 = points1[0].lat;
   3.699 -        lon11 = points1[0].lon;
   3.700 -        /* and consider longitude wrap-around for first vertex */
   3.701 -        if      (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360);
   3.702 -        else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360);
   3.703 -      }
   3.704 -      /* get latitude and longitude of next vertex */
   3.705 -      lat12 = points1[k1].lat;
   3.706 -      lon12 = points1[k1].lon;
   3.707 -      /* consider longitude wrap-around for next vertex */
   3.708 -      if      (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360);
   3.709 -      else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360);
   3.710 -      /* skip degenerated edges */
   3.711 -      if (lat11 == lat12 && lon11 == lon12) continue;
   3.712 -      /* iterate over all entries of cluster2 */
   3.713 -      for (i2=0; i2<cluster2->nentries; i2++) {
   3.714 -        /* get points and number of points of entry in cluster2 */
   3.715 -        npoints2 = cluster2->entries[i2].npoints;
   3.716 -        if (npoints2 < 2) continue;
   3.717 -        entrytype2 = cluster2->entries[i2].entrytype;
   3.718 -        points2 = PGL_ENTRY_POINTS(cluster2, i2);
   3.719 -        /* determine east/west orientation of first point and calculate antipodal
   3.720 -           longitude */
   3.721 -        lon_break2 = points2[0].lon;
   3.722 -        if (lon_break2 < 0) {
   3.723 -          lon_dir2   = -1;
   3.724 -          lon_break2 = pgl_round(lon_break2 + 180);
   3.725 -        } else if (lon_break2 > 0) {
   3.726 -          lon_dir2   = 1;
   3.727 -          lon_break2 = pgl_round(lon_break2 - 180);
   3.728 -        } else lon_dir2 = 0;
   3.729 -        /* iterate over all edges and vertices in cluster2 */
   3.730 -        for (j2=0; j2<npoints2; j2++) {
   3.731 -          /* calculate index of next vertex */
   3.732 -          k2 = (j2+1) % npoints2;
   3.733 -          /* skip last edge unless entry is (closed) outline or polygon */
   3.734 -          if (
   3.735 -            k2 == 0 &&
   3.736 -            entrytype2 != PGL_ENTRY_OUTLINE &&
   3.737 -            entrytype2 != PGL_ENTRY_POLYGON
   3.738 -          ) continue;
   3.739 -          /* use previously calculated values for lat1 and lon1 if possible */
   3.740 -          if (j2) {
   3.741 -            lat21 = lat22;
   3.742 -            lon21 = lon22;
   3.743 -          } else {
   3.744 -            /* otherwise get latitude and longitude values of first vertex */
   3.745 -            lat21 = points2[0].lat;
   3.746 -            lon21 = points2[0].lon;
   3.747 -            /* and consider longitude wrap-around for first vertex */
   3.748 -            if      (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360);
   3.749 -            else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360);
   3.750 -          }
   3.751 -          /* get latitude and longitude of next vertex */
   3.752 -          lat22 = points2[k2].lat;
   3.753 -          lon22 = points2[k2].lon;
   3.754 -          /* consider longitude wrap-around for next vertex */
   3.755 -          if      (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360);
   3.756 -          else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360);
   3.757 -          /* skip degenerated edges */
   3.758 -          if (lat21 == lat22 && lon21 == lon22) continue;
   3.759 -          /* perform another wrap-around where necessary */
   3.760 -          /* TODO: improve performance of whole wrap-around mechanism */
   3.761 -          wrapvalue = (lon21 + lon22) - (lon11 + lon12);
   3.762 -          if (wrapvalue > 360) {
   3.763 -            lon21 = pgl_round(lon21 - 360);
   3.764 -            lon22 = pgl_round(lon22 - 360);
   3.765 -          } else if (wrapvalue < -360) {
   3.766 -            lon21 = pgl_round(lon21 + 360);
   3.767 -            lon22 = pgl_round(lon22 + 360);
   3.768 -          }
   3.769 -          /* return true if segments overlap */
   3.770 -          if (
   3.771 -            pgl_lseg_crosses_line(
   3.772 -              lat11, lon11, lat12, lon12,
   3.773 -              lat21, lon21, lat22, lon22
   3.774 -            ) && pgl_lseg_crosses_line(
   3.775 -              lat21, lon21, lat22, lon22,
   3.776 -              lat11, lon11, lat12, lon12
   3.777 -            )
   3.778 -          ) {
   3.779 -            return true;
   3.780 -          }
   3.781 -        }
   3.782 -      }
   3.783 -    }
   3.784 -  }
   3.785 -  /* otherwise return false */
   3.786 -  return false;
   3.787 -}
   3.788 -
   3.789 -/* check if second cluster is completely contained in first cluster */
   3.790 -static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) {
   3.791 -  if (!pgl_all_cluster_points_strictly_in_cluster(outer, inner)) return false;
   3.792 -  if (pgl_any_cluster_points_in_cluster(inner, outer)) return false;
   3.793 -  if (pgl_outlines_overlap(outer, inner)) return false;
   3.794 -  return true;
   3.795 -}
   3.796 -
   3.797 -/* check if two clusters overlap */
   3.798 -static bool pgl_clusters_overlap(
   3.799 -  pgl_cluster *cluster1, pgl_cluster *cluster2
   3.800 -) {
   3.801 -  if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true;
   3.802 -  if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true;
   3.803 -  if (pgl_outlines_overlap(cluster1, cluster2)) return true;
   3.804 -  return false;
   3.805 -}
   3.806 -
   3.807 -
   3.808 -/* calculate (approximate) distance between point and cluster */
   3.809 -static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
   3.810 -  double comp;           /* square of compression of meridians */
   3.811 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   3.812 -  int entrytype;         /* type of entry */
   3.813 -  int npoints;           /* number of points in entry */
   3.814 -  pgl_point *points;     /* array of points in entry */
   3.815 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   3.816 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   3.817 -  double lon_min = 0;    /* minimum (adjusted) longitude of entry vertices */
   3.818 -  double lon_max = 0;    /* maximum (adjusted) longitude of entry vertices */
   3.819 -  double lat0 = point->lat;  /* latitude of point */
   3.820 -  double lon0;           /* (adjusted) longitude of point */
   3.821 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   3.822 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   3.823 -  double s;              /* scalar for vector calculations */
   3.824 -  double dist;           /* distance calculated in one step */
   3.825 -  double min_dist = INFINITY;   /* minimum distance */
   3.826 -  /* distance is zero if point is contained in cluster */
   3.827 -  if (pgl_point_in_cluster(point, cluster, false)) return 0;
   3.828 -  /* calculate approximate square compression of meridians */
   3.829 -  comp = cos((lat0 / 180.0) * M_PI);
   3.830 -  comp *= comp;
   3.831 -  /* calculate exact square compression of meridians */
   3.832 -  comp *= (
   3.833 -    (1.0 - PGL_EPS2 * (1.0-comp)) *
   3.834 -    (1.0 - PGL_EPS2 * (1.0-comp)) /
   3.835 -    (PGL_SUBEPS2 * PGL_SUBEPS2)
   3.836 -  );
   3.837 -  /* iterate over all entries */
   3.838 -  for (i=0; i<cluster->nentries; i++) {
   3.839 -    /* get properties of entry */
   3.840 -    entrytype = cluster->entries[i].entrytype;
   3.841 -    npoints = cluster->entries[i].npoints;
   3.842 -    points = PGL_ENTRY_POINTS(cluster, i);
   3.843 -    /* determine east/west orientation of first point of entry and calculate
   3.844 -       antipodal longitude */
   3.845 -    lon_break = points[0].lon;
   3.846 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   3.847 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   3.848 -    else lon_dir = 0;
   3.849 -    /* determine covered longitude range */
   3.850 -    for (j=0; j<npoints; j++) {
   3.851 -      /* get longitude of vertex */
   3.852 -      lon1 = points[j].lon;
   3.853 -      /* adjust longitude to fix potential wrap-around */
   3.854 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   3.855 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   3.856 -      /* update minimum and maximum longitude of polygon */
   3.857 -      if (j == 0 || lon1 < lon_min) lon_min = lon1;
   3.858 -      if (j == 0 || lon1 > lon_max) lon_max = lon1;
   3.859 -    }
   3.860 -    /* adjust longitude wrap-around according to full longitude range */
   3.861 -    lon_break = (lon_max + lon_min) / 2;
   3.862 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   3.863 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   3.864 -    /* get longitude of point */
   3.865 -    lon0 = point->lon;
   3.866 -    /* consider longitude wrap-around for point */
   3.867 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 -= 360;
   3.868 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
   3.869 -    /* iterate over all edges and vertices */
   3.870 -    for (j=0; j<npoints; j++) {
   3.871 -      /* use previously calculated values for lat1 and lon1 if possible */
   3.872 -      if (j) {
   3.873 -        lat1 = lat2;
   3.874 -        lon1 = lon2;
   3.875 -      } else {
   3.876 -        /* otherwise get latitude and longitude values of first vertex */
   3.877 -        lat1 = points[0].lat;
   3.878 -        lon1 = points[0].lon;
   3.879 -        /* and consider longitude wrap-around for first vertex */
   3.880 -        if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   3.881 -        else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   3.882 -      }
   3.883 -      /* calculate distance to vertex */
   3.884 -      dist = pgl_distance(lat0, lon0, lat1, lon1);
   3.885 -      /* store calculated distance if smallest */
   3.886 -      if (dist < min_dist) min_dist = dist;
   3.887 -      /* calculate index of next vertex */
   3.888 -      k = (j+1) % npoints;
   3.889 -      /* skip last edge unless entry is (closed) outline or polygon */
   3.890 -      if (
   3.891 -        k == 0 &&
   3.892 -        entrytype != PGL_ENTRY_OUTLINE &&
   3.893 -        entrytype != PGL_ENTRY_POLYGON
   3.894 -      ) continue;
   3.895 -      /* get latitude and longitude of next vertex */
   3.896 -      lat2 = points[k].lat;
   3.897 -      lon2 = points[k].lon;
   3.898 -      /* consider longitude wrap-around for next vertex */
   3.899 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
   3.900 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
   3.901 -      /* go to next vertex and edge if edge is degenerated */
   3.902 -      if (lat1 == lat2 && lon1 == lon2) continue;
   3.903 -      /* otherwise test if point can be projected onto edge of polygon */
   3.904 -      s = (
   3.905 -        ((lat0-lat1) * (lat2-lat1) + comp * (lon0-lon1) * (lon2-lon1)) /
   3.906 -        ((lat2-lat1) * (lat2-lat1) + comp * (lon2-lon1) * (lon2-lon1))
   3.907 -      );
   3.908 -      /* go to next vertex and edge if point cannot be projected */
   3.909 -      if (!(s > 0 && s < 1)) continue;
   3.910 -      /* calculate distance from original point to projected point */
   3.911 -      dist = pgl_distance(
   3.912 -        lat0, lon0,
   3.913 -        lat1 + s * (lat2-lat1),
   3.914 -        lon1 + s * (lon2-lon1)
   3.915 -      );
   3.916 -      /* store calculated distance if smallest */
   3.917 -      if (dist < min_dist) min_dist = dist;
   3.918 -    }
   3.919 -  }
   3.920 -  /* return minimum distance */
   3.921 -  return min_dist;
   3.922 -}
   3.923 -
   3.924 -/* calculate (approximate) distance between two clusters */
   3.925 -static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) {
   3.926 -  int i, j;                    /* i: entry, j: point in entry */
   3.927 -  int npoints;                 /* number of points in entry */
   3.928 -  pgl_point *points;           /* array of points in entry */
   3.929 -  double dist;                 /* distance calculated in one step */
   3.930 -  double min_dist = INFINITY;  /* minimum distance */
   3.931 -  /* consider distance from each point in one cluster to the whole other */
   3.932 -  for (i=0; i<cluster1->nentries; i++) {
   3.933 -    npoints = cluster1->entries[i].npoints;
   3.934 -    points = PGL_ENTRY_POINTS(cluster1, i);
   3.935 -    for (j=0; j<npoints; j++) {
   3.936 -      dist = pgl_point_cluster_distance(points+j, cluster2);
   3.937 -      if (dist == 0) return dist;
   3.938 -      if (dist < min_dist) min_dist = dist;
   3.939 -    }
   3.940 -  }
   3.941 -  /* consider distance from each point in other cluster to the first cluster */
   3.942 -  for (i=0; i<cluster2->nentries; i++) {
   3.943 -    npoints = cluster2->entries[i].npoints;
   3.944 -    points = PGL_ENTRY_POINTS(cluster2, i);
   3.945 -    for (j=0; j<npoints; j++) {
   3.946 -      dist = pgl_point_cluster_distance(points+j, cluster1);
   3.947 -      if (dist == 0) return dist;
   3.948 -      if (dist < min_dist) min_dist = dist;
   3.949 -    }
   3.950 -  }
   3.951 -  return min_dist;
   3.952 -}
   3.953 -
   3.954 -/* estimator function for distance between box and point */
   3.955 -/* always returns a smaller value than actually correct or zero */
   3.956 -static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
   3.957 -  double dlon;      /* longitude range of box (delta longitude) */
   3.958 -  double distance;  /* return value */
   3.959 -  /* return infinity if box is empty */
   3.960 -  if (box->lat_min > box->lat_max) return INFINITY;
   3.961 -  /* return zero if point is inside box */
   3.962 -  if (pgl_point_in_box(point, box)) return 0;
   3.963 -  /* calculate delta longitude */
   3.964 -  dlon = box->lon_max - box->lon_min;
   3.965 -  if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
   3.966 -  /* if delta longitude is greater than 150 degrees, perform safe fall-back */
   3.967 -  if (dlon > 150) return 0;
   3.968 -  /* calculate lower limit for distance (formula below requires dlon <= 150) */
   3.969 -  /* TODO: provide better estimation function to improve performance */
   3.970 -  distance = (
   3.971 -    (1.0-1e-14) * pgl_distance(
   3.972 -      point->lat,
   3.973 -      point->lon,
   3.974 -      (box->lat_min + box->lat_max) / 2,
   3.975 -      box->lon_min + dlon/2
   3.976 -    ) - pgl_distance(
   3.977 -      box->lat_min, box->lon_min,
   3.978 -      box->lat_max, box->lon_max
   3.979 -    )
   3.980 -  );
   3.981 -  /* truncate negative results to zero */
   3.982 -  if (distance <= 0) distance = 0;
   3.983 -  /* return result */
   3.984 -  return distance;
   3.985 -}
   3.986 -
   3.987 -
   3.988 -/*------------------------------------------------------------*
   3.989 - *  Functions using numerical integration (Monte Carlo like)  *
   3.990 - *------------------------------------------------------------*/
   3.991 -
   3.992 -/* half of (spherical) earth's surface area */
   3.993 -#define PGL_HALF_SURFACE (PGL_RADIUS * PGL_DIAMETER * M_PI)
   3.994 -
   3.995 -/* golden angle in radians */
   3.996 -#define PGL_GOLDEN_ANGLE (M_PI * (sqrt(5) - 1.0))
   3.997 -
   3.998 -/* create a list of sample points covering a bounding circle 
   3.999 -   and return covered area */
  3.1000 -static double pgl_sample_points(
  3.1001 -  pgl_point *center,  /* center of bounding circle */
  3.1002 -  double radius,      /* radius of bounding circle */
  3.1003 -  int samples,        /* number of sample points */
  3.1004 -  pgl_point *result   /* pointer to result array */
  3.1005 -) {
  3.1006 -  double double_share = 2.0;  /* double of covered share of earth's surface */
  3.1007 -  double double_share_div_samples;  /* double_share divided by sample count */
  3.1008 -  int i;
  3.1009 -  double t;  /* parameter of spiral laid on (spherical) earth's surface */
  3.1010 -  double x, y, z;  /* normalized coordinates of point on non-rotated spiral */
  3.1011 -  double sin_phi;  /* sine of sph. coordinate of point of non-rotated spiral */
  3.1012 -  double lambda;   /* other sph. coordinate of point of non-rotated spiral */
  3.1013 -  double rot = (0.5 - center->lat / 180.0) * M_PI;  /* needed rot. (in rad) */
  3.1014 -  double cos_rot = cos(rot);  /* cosine of rotation by latitude */
  3.1015 -  double sin_rot = sin(rot);  /* sine of rotation by latitude */
  3.1016 -  double x_rot, z_rot;  /* normalized coordinates of point on rotated spiral */
  3.1017 -  double center_lon = center->lon;  /* second rotation in degree */
  3.1018 -  /* add safety margin to bounding circle because of spherical approximation */
  3.1019 -  radius *= PGL_SPHEROID_A / PGL_RADIUS;
  3.1020 -  /* if whole earth is covered, use initialized value, otherwise calculate
  3.1021 -     share of covered area (multiplied by 2) */
  3.1022 -  if (radius < PGL_MAXDIST) double_share = 1.0 - cos(radius / PGL_RADIUS);
  3.1023 -  /* divide double_share by sample count for later calculations */
  3.1024 -  double_share_div_samples = double_share / samples;
  3.1025 -  /* generate sample points */
  3.1026 -  for (i=0; i<samples; i++) {
  3.1027 -    /* use an offset of 1/2 to avoid too dense clustering at spiral center */
  3.1028 -    t = 0.5 + i;
  3.1029 -    /* calculate normalized coordinates of point on non-rotated spiral */
  3.1030 -    z = 1.0 - double_share_div_samples * t;
  3.1031 -    sin_phi = sqrt(1.0 - z*z);
  3.1032 -    lambda = t * PGL_GOLDEN_ANGLE;
  3.1033 -    x = sin_phi * cos(lambda);
  3.1034 -    y = sin_phi * sin(lambda);
  3.1035 -    /* rotate spiral by latitude value of bounding circle */
  3.1036 -    x_rot = cos_rot * x + sin_rot * z;
  3.1037 -    z_rot = cos_rot * z - sin_rot * x;
  3.1038 -    /* set resulting sample point in result array */
  3.1039 -    /* (while performing second rotation by bounding circle longitude) */
  3.1040 -    result[i].lat = 180.0 * (atan(z_rot / fabs(x_rot)) / M_PI);
  3.1041 -    result[i].lon = center_lon + 180.0 * (atan2(y, x_rot) / M_PI);
  3.1042 -  }
  3.1043 -  /* return covered area */
  3.1044 -  return PGL_HALF_SURFACE * double_share;
  3.1045 -}
  3.1046 -
  3.1047 -/* calculate covered area using Monte Carlo like method */
  3.1048 -/* TODO: inefficient, should be replaced by different method */
  3.1049 -static double pgl_monte_carlo_area(pgl_cluster *cluster, int samples) {
  3.1050 -  pgl_point *points;
  3.1051 -  double area;
  3.1052 -  int i;
  3.1053 -  int matches = 0;
  3.1054 -  if (samples > PGL_CLUSTER_MAXPOINTS) {
  3.1055 -    ereport(ERROR, (
  3.1056 -      errcode(ERRCODE_DATA_EXCEPTION),
  3.1057 -      errmsg(
  3.1058 -        "too many sample points for Monte Carlo method (maximum %i)",
  3.1059 -        PGL_CLUSTER_MAXPOINTS
  3.1060 -      )
  3.1061 -    ));
  3.1062 -  }
  3.1063 -  if (!(cluster->bounding.radius > 0)) return 0;
  3.1064 -  for (i=0; ; i++) {
  3.1065 -    if (i == cluster->nentries) return 0;
  3.1066 -    if (cluster->entries[i].entrytype == PGL_ENTRY_POLYGON) break;
  3.1067 -  }
  3.1068 -  points = palloc(samples * sizeof(pgl_point));
  3.1069 -  area = pgl_sample_points(
  3.1070 -    &cluster->bounding.center,
  3.1071 -    cluster->bounding.radius,
  3.1072 -    samples,
  3.1073 -    points
  3.1074 -  );
  3.1075 -  for (i=0; i<samples; i++) {
  3.1076 -    if (pgl_point_in_cluster(points+i, cluster, true)) matches++;
  3.1077 -  }
  3.1078 -  pfree(points);
  3.1079 -  return area * ((double)matches / samples);
  3.1080 -}
  3.1081 -
  3.1082 -/* fair distance between point and cluster (see README file for explanation) */
  3.1083 -static double pgl_fair_distance(
  3.1084 -  pgl_point *point, pgl_cluster *cluster, int samples
  3.1085 -) {
  3.1086 -  double distance;       /* shortest distance from point to cluster */
  3.1087 -  pgl_point *points;     /* sample points for Monte Carlo method */
  3.1088 -  double area;           /* area covered by sample points */
  3.1089 -  int i;
  3.1090 -  int matches = 0;       /* number of points within enlarged area (multiplied
  3.1091 -                            by two to be able to store half matches) */
  3.1092 -  double result;         /* result determined by Monte Carlo method */
  3.1093 -  /* limit sample count to avoid integer overflows on memory allocation */
  3.1094 -  if (samples > PGL_CLUSTER_MAXPOINTS) {
  3.1095 -    ereport(ERROR, (
  3.1096 -      errcode(ERRCODE_DATA_EXCEPTION),
  3.1097 -      errmsg(
  3.1098 -        "too many sample points for Monte Carlo method (maximum %i)",
  3.1099 -        PGL_CLUSTER_MAXPOINTS
  3.1100 -      )
  3.1101 -    ));
  3.1102 -  }
  3.1103 -  /* calculate shortest distance from point to cluster */
  3.1104 -  distance = pgl_point_cluster_distance(point, cluster);
  3.1105 -  /* if cluster consists of a single point or has no bounding circle with
  3.1106 -      positive radius, simply return distance */
  3.1107 -  if (
  3.1108 -    (cluster->nentries==1 && cluster->entries[0].entrytype==PGL_ENTRY_POINT) ||
  3.1109 -    !(cluster->bounding.radius > 0)
  3.1110 -  ) return distance;
  3.1111 -  /* if cluster consists of two points which are twice as far apart, return
  3.1112 -     distance between point and cluster multiplied by square root of two */
  3.1113 -  if (
  3.1114 -    cluster->nentries == 2 &&
  3.1115 -    cluster->entries[0].entrytype == PGL_ENTRY_POINT &&
  3.1116 -    cluster->entries[1].entrytype == PGL_ENTRY_POINT &&
  3.1117 -    pgl_distance(
  3.1118 -      PGL_ENTRY_POINTS(cluster, 0)[0].lat,
  3.1119 -      PGL_ENTRY_POINTS(cluster, 0)[0].lon,
  3.1120 -      PGL_ENTRY_POINTS(cluster, 1)[0].lat,
  3.1121 -      PGL_ENTRY_POINTS(cluster, 1)[0].lon
  3.1122 -    ) >= 2.0 * distance
  3.1123 -  ) {
  3.1124 -    return distance * M_SQRT2;
  3.1125 -  }
  3.1126 -  /* otherwise create sample points for Monte Carlo method and determine area
  3.1127 -     covered by sample points */
  3.1128 -  points = palloc(samples * sizeof(pgl_point));
  3.1129 -  area = pgl_sample_points(
  3.1130 -    &cluster->bounding.center,
  3.1131 -    cluster->bounding.radius + distance,  /* pad bounding circle by distance */
  3.1132 -    samples,
  3.1133 -    points
  3.1134 -  );
  3.1135 -  /* perform Monte Carlo method */
  3.1136 -  if (distance > 0) {
  3.1137 -    /* point is outside cluster */
  3.1138 -    for (i=0; i<samples; i++) {
  3.1139 -      /* count sample poitns within cluster as half match */
  3.1140 -      if (pgl_point_in_cluster(points+i, cluster, true)) matches += 1;
  3.1141 -      /* count sample points outside of cluster but within cluster grown by
  3.1142 -         distance as full match */
  3.1143 -      else if (
  3.1144 -        pgl_point_cluster_distance(points+i, cluster) < distance
  3.1145 -      ) matches += 2;
  3.1146 -    }
  3.1147 -  } else {
  3.1148 -    /* if point is within cluster,
  3.1149 -       just count sample points within cluster as half match */
  3.1150 -    for (i=0; i<samples; i++) {
  3.1151 -      if (pgl_point_in_cluster(points+i, cluster, true)) matches += 1;
  3.1152 -    }
  3.1153 -  }
  3.1154 -  /* release memory for sample points needed by Monte Carlo method */
  3.1155 -  pfree(points);
  3.1156 -  /* convert area determined by Monte Carlo method into distance
  3.1157 -     (i.e. radius of circle with the same area) */
  3.1158 -  result = sqrt(area * ((double)matches / 2.0 / samples) / M_PI);
  3.1159 -  /* return result only if it is greater than the distance between point and
  3.1160 -     cluster to avoid unexpected results because of errors due to limited
  3.1161 -     precision */
  3.1162 -  if (result > distance) return result;
  3.1163 -  /* otherwise return distance between point and cluster */
  3.1164 -  else return distance;
  3.1165 -}
  3.1166 -
  3.1167 -
  3.1168 -/*-------------------------------------------------*
  3.1169 - *  geographic index based on space-filling curve  *
  3.1170 - *-------------------------------------------------*/
  3.1171 -
  3.1172 -/* number of bytes used for geographic (center) position in keys */
  3.1173 -#define PGL_KEY_LATLON_BYTELEN 7
  3.1174 -
  3.1175 -/* maximum reference value for logarithmic size of geographic objects */
  3.1176 -#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
  3.1177 -
  3.1178 -/* pointer to index key (either pgl_pointkey or pgl_areakey) */
  3.1179 -typedef unsigned char *pgl_keyptr;
  3.1180 -
  3.1181 -/* index key for points (objects with zero area) on the spheroid */
  3.1182 -/* bit  0..55: interspersed bits of latitude and longitude,
  3.1183 -   bit 56..57: always zero,
  3.1184 -   bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */
  3.1185 -typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1];
  3.1186 -
  3.1187 -/* index key for geographic objects on spheroid with area greater than zero */
  3.1188 -/* bit  0..55: interspersed bits of latitude and longitude of center point,
  3.1189 -   bit     56: always set to 1,
  3.1190 -   bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.),
  3.1191 -   bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to
  3.1192 -               PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth
  3.1193 -               = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL
  3.1194 -               (with interspersed bits = 0 and node depth = 0) for keys which
  3.1195 -               cover both empty and non-empty objects */
  3.1196 -
  3.1197 -typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2];
  3.1198 -
  3.1199 -/* helper macros for reading/writing index keys */
  3.1200 -#define PGL_KEY_NODEDEPTH_OFFSET  PGL_KEY_LATLON_BYTELEN
  3.1201 -#define PGL_KEY_OBJSIZE_OFFSET    (PGL_KEY_NODEDEPTH_OFFSET+1)
  3.1202 -#define PGL_POINTKEY_MAXDEPTH     (PGL_KEY_LATLON_BYTELEN*8)
  3.1203 -#define PGL_AREAKEY_MAXDEPTH      (2*PGL_POINTKEY_MAXDEPTH+1)
  3.1204 -#define PGL_AREAKEY_MAXOBJSIZE    (PGL_POINTKEY_MAXDEPTH+1)
  3.1205 -#define PGL_AREAKEY_TYPEMASK      0x80
  3.1206 -#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8)))
  3.1207 -#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \
  3.1208 -                                  ( PGL_KEY_LATLONBIT(key1, n) ^ \
  3.1209 -                                    PGL_KEY_LATLONBIT(key2, n) )
  3.1210 -#define PGL_KEY_IS_AREAKEY(key)   ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
  3.1211 -                                    PGL_AREAKEY_TYPEMASK)
  3.1212 -#define PGL_KEY_NODEDEPTH(key)    ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
  3.1213 -                                    (PGL_AREAKEY_TYPEMASK-1))
  3.1214 -#define PGL_KEY_OBJSIZE(key)      ((key)[PGL_KEY_OBJSIZE_OFFSET])
  3.1215 -#define PGL_KEY_OBJSIZE_EMPTY     126
  3.1216 -#define PGL_KEY_OBJSIZE_UNIVERSAL 127
  3.1217 -#define PGL_KEY_IS_EMPTY(key)     ( PGL_KEY_IS_AREAKEY(key) && \
  3.1218 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
  3.1219 -                                    PGL_KEY_OBJSIZE_EMPTY )
  3.1220 -#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \
  3.1221 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
  3.1222 -                                    PGL_KEY_OBJSIZE_UNIVERSAL )
  3.1223 -
  3.1224 -/* set area key to match empty objects only */
  3.1225 -static void pgl_key_set_empty(pgl_keyptr key) {
  3.1226 -  memset(key, 0, sizeof(pgl_areakey));
  3.1227 -  /* Note: setting node depth to maximum is required for picksplit function */
  3.1228 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
  3.1229 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY;
  3.1230 -}
  3.1231 -
  3.1232 -/* set area key to match any object (including empty objects) */
  3.1233 -static void pgl_key_set_universal(pgl_keyptr key) {
  3.1234 -  memset(key, 0, sizeof(pgl_areakey));
  3.1235 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK;
  3.1236 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL;
  3.1237 -}
  3.1238 -
  3.1239 -/* convert a point on earth into a max-depth key to be used in index */
  3.1240 -static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) {
  3.1241 -  double lat = point->lat;
  3.1242 -  double lon = point->lon;
  3.1243 -  int i;
  3.1244 -  /* clear latitude and longitude bits */
  3.1245 -  memset(key, 0, PGL_KEY_LATLON_BYTELEN);
  3.1246 -  /* set node depth to maximum and type bit to zero */
  3.1247 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH;
  3.1248 -  /* iterate over all latitude/longitude bit pairs */
  3.1249 -  for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) {
  3.1250 -    /* determine latitude bit */
  3.1251 -    if (lat >= 0) {
  3.1252 -      key[i/4] |= 0x80 >> (2*(i%4));
  3.1253 -      lat *= 2; lat -= 90;
  3.1254 -    } else {
  3.1255 -      lat *= 2; lat += 90;
  3.1256 -    }
  3.1257 -    /* determine longitude bit */
  3.1258 -    if (lon >= 0) {
  3.1259 -      key[i/4] |= 0x80 >> (2*(i%4)+1);
  3.1260 -      lon *= 2; lon -= 180;
  3.1261 -    } else {
  3.1262 -      lon *= 2; lon += 180;
  3.1263 -    }
  3.1264 -  }
  3.1265 -}
  3.1266 -
  3.1267 -/* convert a circle on earth into a max-depth key to be used in an index */
  3.1268 -static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) {
  3.1269 -  /* handle special case of empty circle */
  3.1270 -  if (circle->radius < 0) {
  3.1271 -    pgl_key_set_empty(key);
  3.1272 -    return;
  3.1273 -  }
  3.1274 -  /* perform same action as for point keys */
  3.1275 -  pgl_point_to_key(&(circle->center), key);
  3.1276 -  /* but overwrite type and node depth to fit area index key */
  3.1277 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
  3.1278 -  /* check if radius is greater than (or equal to) reference size */
  3.1279 -  /* (treat equal values as greater values for numerical safety) */
  3.1280 -  if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) {
  3.1281 -    /* if yes, set logarithmic size to zero */
  3.1282 -    key[PGL_KEY_OBJSIZE_OFFSET] = 0;
  3.1283 -  } else {
  3.1284 -    /* otherwise, determine logarithmic size iteratively */
  3.1285 -    /* (one step is equivalent to a factor of sqrt(2)) */
  3.1286 -    double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2;
  3.1287 -    int objsize = 1;
  3.1288 -    while (objsize < PGL_AREAKEY_MAXOBJSIZE) {
  3.1289 -      /* stop when radius is greater than (or equal to) adjusted reference */
  3.1290 -      /* (treat equal values as greater values for numerical safety) */
  3.1291 -      if (circle->radius >= reference) break;
  3.1292 -      reference /= M_SQRT2;
  3.1293 -      objsize++;
  3.1294 -    }
  3.1295 -    /* set logarithmic size to determined value */
  3.1296 -    key[PGL_KEY_OBJSIZE_OFFSET] = objsize;
  3.1297 -  }
  3.1298 -}
  3.1299 -
  3.1300 -/* check if one key is subkey of another key or vice versa */
  3.1301 -static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) {
  3.1302 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
  3.1303 -  /* determine smallest depth */
  3.1304 -  int depth1 = PGL_KEY_NODEDEPTH(key1);
  3.1305 -  int depth2 = PGL_KEY_NODEDEPTH(key2);
  3.1306 -  int depth = (depth1 < depth2) ? depth1 : depth2;
  3.1307 -  /* check if keys are area keys (assuming that both keys have same type) */
  3.1308 -  if (PGL_KEY_IS_AREAKEY(key1)) {
  3.1309 -    int j = 0;  /* bit offset for logarithmic object size bits */
  3.1310 -    int k = 0;  /* bit offset for latitude and longitude */
  3.1311 -    /* fetch logarithmic object size information */
  3.1312 -    int objsize1 = PGL_KEY_OBJSIZE(key1);
  3.1313 -    int objsize2 = PGL_KEY_OBJSIZE(key2);
  3.1314 -    /* handle special cases for empty objects (universal and empty keys) */
  3.1315 -    if (
  3.1316 -      objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL ||
  3.1317 -      objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL
  3.1318 -    ) return true;
  3.1319 -    if (
  3.1320 -      objsize1 == PGL_KEY_OBJSIZE_EMPTY ||
  3.1321 -      objsize2 == PGL_KEY_OBJSIZE_EMPTY
  3.1322 -    ) return objsize1 == objsize2;
  3.1323 -    /* iterate through key bits */
  3.1324 -    for (i=0; i<depth; i++) {
  3.1325 -      /* every second bit is a bit describing the object size */
  3.1326 -      if (i%2 == 0) {
  3.1327 -        /* check if object size bit is different in both keys (objsize1 and
  3.1328 -           objsize2 describe the minimum index when object size bit is set) */
  3.1329 -        if (
  3.1330 -          (objsize1 <= j && objsize2 > j) ||
  3.1331 -          (objsize2 <= j && objsize1 > j)
  3.1332 -        ) {
  3.1333 -          /* bit differs, therefore keys are in separate branches */
  3.1334 -          return false;
  3.1335 -        }
  3.1336 -        /* increase bit counter for object size bits */
  3.1337 -        j++;
  3.1338 -      }
  3.1339 -      /* all other bits describe latitude and longitude */
  3.1340 -      else {
  3.1341 -        /* check if bit differs in both keys */
  3.1342 -        if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) {
  3.1343 -          /* bit differs, therefore keys are in separate branches */
  3.1344 -          return false;
  3.1345 -        }
  3.1346 -        /* increase bit counter for latitude/longitude bits */
  3.1347 -        k++;
  3.1348 -      }
  3.1349 -    }
  3.1350 -  }
  3.1351 -  /* if not, keys are point keys */
  3.1352 -  else {
  3.1353 -    /* iterate through key bits */
  3.1354 -    for (i=0; i<depth; i++) {
  3.1355 -      /* check if bit differs in both keys */
  3.1356 -      if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) {
  3.1357 -        /* bit differs, therefore keys are in separate branches */
  3.1358 -        return false;
  3.1359 -      }
  3.1360 -    }
  3.1361 -  }
  3.1362 -  /* return true because keys are in the same branch */
  3.1363 -  return true;
  3.1364 -}
  3.1365 -
  3.1366 -/* combine two keys into new key which covers both original keys */
  3.1367 -/* (result stored in first argument) */
  3.1368 -static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) {
  3.1369 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
  3.1370 -  /* determine smallest depth */
  3.1371 -  int depth1 = PGL_KEY_NODEDEPTH(dst);
  3.1372 -  int depth2 = PGL_KEY_NODEDEPTH(src);
  3.1373 -  int depth = (depth1 < depth2) ? depth1 : depth2;
  3.1374 -  /* check if keys are area keys (assuming that both keys have same type) */
  3.1375 -  if (PGL_KEY_IS_AREAKEY(dst)) {
  3.1376 -    pgl_areakey dstbuf = { 0, };  /* destination buffer (cleared) */
  3.1377 -    int j = 0;  /* bit offset for logarithmic object size bits */
  3.1378 -    int k = 0;  /* bit offset for latitude and longitude */
  3.1379 -    /* fetch logarithmic object size information */
  3.1380 -    int objsize1 = PGL_KEY_OBJSIZE(dst);
  3.1381 -    int objsize2 = PGL_KEY_OBJSIZE(src);
  3.1382 -    /* handle special cases for empty objects (universal and empty keys) */
  3.1383 -    if (
  3.1384 -      objsize1 > PGL_AREAKEY_MAXOBJSIZE ||
  3.1385 -      objsize2 > PGL_AREAKEY_MAXOBJSIZE
  3.1386 -    ) {
  3.1387 -      if (
  3.1388 -        objsize1 == PGL_KEY_OBJSIZE_EMPTY &&
  3.1389 -        objsize2 == PGL_KEY_OBJSIZE_EMPTY
  3.1390 -      ) pgl_key_set_empty(dst);
  3.1391 -      else pgl_key_set_universal(dst);
  3.1392 -      return;
  3.1393 -    }
  3.1394 -    /* iterate through key bits */
  3.1395 -    for (i=0; i<depth; i++) {
  3.1396 -      /* every second bit is a bit describing the object size */
  3.1397 -      if (i%2 == 0) {
  3.1398 -        /* increase bit counter for object size bits first */
  3.1399 -        /* (handy when setting objsize variable) */
  3.1400 -        j++;
  3.1401 -        /* check if object size bit is set in neither key */
  3.1402 -        if (objsize1 >= j && objsize2 >= j) {
  3.1403 -          /* set objsize in destination buffer to indicate that size bit is
  3.1404 -             unset in destination buffer at the current bit position */
  3.1405 -          dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j;
  3.1406 -        }
  3.1407 -        /* break if object size bit is set in one key only */
  3.1408 -        else if (objsize1 >= j || objsize2 >= j) break;
  3.1409 -      }
  3.1410 -      /* all other bits describe latitude and longitude */
  3.1411 -      else {
  3.1412 -        /* break if bit differs in both keys */
  3.1413 -        if (PGL_KEY_LATLONBIT(dst, k)) {
  3.1414 -          if (!PGL_KEY_LATLONBIT(src, k)) break;
  3.1415 -          /* but set bit in destination buffer if bit is set in both keys */
  3.1416 -          dstbuf[k/8] |= 0x80 >> (k%8);
  3.1417 -        } else if (PGL_KEY_LATLONBIT(src, k)) break;
  3.1418 -        /* increase bit counter for latitude/longitude bits */
  3.1419 -        k++;
  3.1420 -      }
  3.1421 -    }
  3.1422 -    /* set common node depth and type bit (type bit = 1) */
  3.1423 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i;
  3.1424 -    /* copy contents of destination buffer to first key */
  3.1425 -    memcpy(dst, dstbuf, sizeof(pgl_areakey));
  3.1426 -  }
  3.1427 -  /* if not, keys are point keys */
  3.1428 -  else {
  3.1429 -    pgl_pointkey dstbuf = { 0, };  /* destination buffer (cleared) */
  3.1430 -    /* iterate through key bits */
  3.1431 -    for (i=0; i<depth; i++) {
  3.1432 -      /* break if bit differs in both keys */
  3.1433 -      if (PGL_KEY_LATLONBIT(dst, i)) {
  3.1434 -        if (!PGL_KEY_LATLONBIT(src, i)) break;
  3.1435 -        /* but set bit in destination buffer if bit is set in both keys */
  3.1436 -        dstbuf[i/8] |= 0x80 >> (i%8);
  3.1437 -      } else if (PGL_KEY_LATLONBIT(src, i)) break;
  3.1438 -    }
  3.1439 -    /* set common node depth (type bit = 0) */
  3.1440 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i;
  3.1441 -    /* copy contents of destination buffer to first key */
  3.1442 -    memcpy(dst, dstbuf, sizeof(pgl_pointkey));
  3.1443 -  }
  3.1444 -}
  3.1445 -
  3.1446 -/* determine center(!) boundaries and radius estimation of index key */
  3.1447 -static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) {
  3.1448 -  int i;
  3.1449 -  /* determine node depth */
  3.1450 -  int depth = PGL_KEY_NODEDEPTH(key);
  3.1451 -  /* center point of possible result */
  3.1452 -  double lat = 0;
  3.1453 -  double lon = 0;
  3.1454 -  /* maximum distance of real center point from key center */
  3.1455 -  double dlat = 90;
  3.1456 -  double dlon = 180;
  3.1457 -  /* maximum radius of contained objects */
  3.1458 -  double radius = 0;  /* always return zero for point index keys */
  3.1459 -  /* check if key is area key */
  3.1460 -  if (PGL_KEY_IS_AREAKEY(key)) {
  3.1461 -    /* get logarithmic object size */
  3.1462 -    int objsize = PGL_KEY_OBJSIZE(key);
  3.1463 -    /* handle special cases for empty objects (universal and empty keys) */
  3.1464 -    if (objsize == PGL_KEY_OBJSIZE_EMPTY) {
  3.1465 -      pgl_box_set_empty(box);
  3.1466 -      return 0;
  3.1467 -    } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) {
  3.1468 -      box->lat_min = -90;
  3.1469 -      box->lat_max =  90;
  3.1470 -      box->lon_min = -180;
  3.1471 -      box->lon_max =  180;
  3.1472 -      return 0;  /* any value >= 0 would do */
  3.1473 -    }
  3.1474 -    /* calculate maximum possible radius of objects covered by the given key */
  3.1475 -    if (objsize == 0) radius = INFINITY;
  3.1476 -    else {
  3.1477 -      radius = PGL_AREAKEY_REFOBJSIZE;
  3.1478 -      while (--objsize) radius /= M_SQRT2;
  3.1479 -    }
  3.1480 -    /* iterate over latitude and longitude bits in key */
  3.1481 -    /* (every second bit is a latitude or longitude bit) */
  3.1482 -    for (i=0; i<depth/2; i++) {
  3.1483 -      /* check if latitude bit */
  3.1484 -      if (i%2 == 0) {
  3.1485 -        /* cut latitude dimension in half */
  3.1486 -        dlat /= 2;
  3.1487 -        /* increase center latitude if bit is 1, otherwise decrease */
  3.1488 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  3.1489 -        else lat -= dlat;
  3.1490 -      }
  3.1491 -      /* otherwise longitude bit */
  3.1492 -      else {
  3.1493 -        /* cut longitude dimension in half */
  3.1494 -        dlon /= 2;
  3.1495 -        /* increase center longitude if bit is 1, otherwise decrease */
  3.1496 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  3.1497 -        else lon -= dlon;
  3.1498 -      }
  3.1499 -    }
  3.1500 -  }
  3.1501 -  /* if not, keys are point keys */
  3.1502 -  else {
  3.1503 -    /* iterate over all bits in key */
  3.1504 -    for (i=0; i<depth; i++) {
  3.1505 -      /* check if latitude bit */
  3.1506 -      if (i%2 == 0) {
  3.1507 -        /* cut latitude dimension in half */
  3.1508 -        dlat /= 2;
  3.1509 -        /* increase center latitude if bit is 1, otherwise decrease */
  3.1510 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  3.1511 -        else lat -= dlat;
  3.1512 -      }
  3.1513 -      /* otherwise longitude bit */
  3.1514 -      else {
  3.1515 -        /* cut longitude dimension in half */
  3.1516 -        dlon /= 2;
  3.1517 -        /* increase center longitude if bit is 1, otherwise decrease */
  3.1518 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  3.1519 -        else lon -= dlon;
  3.1520 -      }
  3.1521 -    }
  3.1522 -  }
  3.1523 -  /* calculate boundaries from center point and remaining dlat and dlon */
  3.1524 -  /* (return values through pointer to box) */
  3.1525 -  box->lat_min = lat - dlat;
  3.1526 -  box->lat_max = lat + dlat;
  3.1527 -  box->lon_min = lon - dlon;
  3.1528 -  box->lon_max = lon + dlon;
  3.1529 -  /* return radius (as a function return value) */
  3.1530 -  return radius;
  3.1531 -}
  3.1532 -
  3.1533 -/* estimator function for distance between point and index key */
  3.1534 -/* always returns a smaller value than actually correct or zero */
  3.1535 -static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
  3.1536 -  pgl_box box;  /* center(!) bounding box of area index key */
  3.1537 -  /* calculate center(!) bounding box and maximum radius of objects covered
  3.1538 -     by area index key (radius is zero for point index keys) */
  3.1539 -  double distance = pgl_key_to_box(key, &box);
  3.1540 -  /* calculate estimated distance between bounding box of center point of
  3.1541 -     indexed object and point passed as second argument, then substract maximum
  3.1542 -     radius of objects covered by index key */
  3.1543 -  distance = pgl_estimate_point_box_distance(point, &box) - distance;
  3.1544 -  /* truncate negative results to zero */
  3.1545 -  if (distance <= 0) distance = 0;
  3.1546 -  /* return result */
  3.1547 -  return distance;
  3.1548 -}
  3.1549 -
  3.1550 -
  3.1551 -/*---------------------------------*
  3.1552 - *  helper functions for text I/O  *
  3.1553 - *---------------------------------*/
  3.1554 -
  3.1555 -#define PGL_NUMBUFLEN 64  /* buffer size for number to string conversion */
  3.1556 -
  3.1557 -/* convert floating point number to string (round-trip safe) */
  3.1558 -static void pgl_print_float(char *buf, double flt) {
  3.1559 -  /* check if number is integral */
  3.1560 -  if (trunc(flt) == flt) {
  3.1561 -    /* for integral floats use maximum precision */
  3.1562 -    snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  3.1563 -  } else {
  3.1564 -    /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */
  3.1565 -    snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt);
  3.1566 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt);
  3.1567 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  3.1568 -  }
  3.1569 -}
  3.1570 -
  3.1571 -/* convert latitude floating point number (in degrees) to string */
  3.1572 -static void pgl_print_lat(char *buf, double lat) {
  3.1573 -  if (signbit(lat)) {
  3.1574 -    /* treat negative latitudes (including -0) as south */
  3.1575 -    snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat);
  3.1576 -  } else {
  3.1577 -    /* treat positive latitudes (including +0) as north */
  3.1578 -    snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat);
  3.1579 -  }
  3.1580 -}
  3.1581 -
  3.1582 -/* convert longitude floating point number (in degrees) to string */
  3.1583 -static void pgl_print_lon(char *buf, double lon) {
  3.1584 -  if (signbit(lon)) {
  3.1585 -    /* treat negative longitudes (including -0) as west */
  3.1586 -    snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon);
  3.1587 -  } else {
  3.1588 -    /* treat positive longitudes (including +0) as east */
  3.1589 -    snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon);
  3.1590 -  }
  3.1591 -}
  3.1592 -
  3.1593 -/* bit masks used as return value of pgl_scan() function */
  3.1594 -#define PGL_SCAN_NONE 0      /* no value has been parsed */
  3.1595 -#define PGL_SCAN_LAT (1<<0)  /* latitude has been parsed */
  3.1596 -#define PGL_SCAN_LON (1<<1)  /* longitude has been parsed */
  3.1597 -#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON)  /* bitwise OR of both */
  3.1598 -
  3.1599 -/* parse a coordinate (can be latitude or longitude) */
  3.1600 -static int pgl_scan(char **str, double *lat, double *lon) {
  3.1601 -  double val;
  3.1602 -  int len;
  3.1603 -  if (
  3.1604 -    sscanf(*str, " N %lf %n", &val, &len) ||
  3.1605 -    sscanf(*str, " n %lf %n", &val, &len)
  3.1606 -  ) {
  3.1607 -    *str += len; *lat = val; return PGL_SCAN_LAT;
  3.1608 -  }
  3.1609 -  if (
  3.1610 -    sscanf(*str, " S %lf %n", &val, &len) ||
  3.1611 -    sscanf(*str, " s %lf %n", &val, &len)
  3.1612 -  ) {
  3.1613 -    *str += len; *lat = -val; return PGL_SCAN_LAT;
  3.1614 -  }
  3.1615 -  if (
  3.1616 -    sscanf(*str, " E %lf %n", &val, &len) ||
  3.1617 -    sscanf(*str, " e %lf %n", &val, &len)
  3.1618 -  ) {
  3.1619 -    *str += len; *lon = val; return PGL_SCAN_LON;
  3.1620 -  }
  3.1621 -  if (
  3.1622 -    sscanf(*str, " W %lf %n", &val, &len) ||
  3.1623 -    sscanf(*str, " w %lf %n", &val, &len)
  3.1624 -  ) {
  3.1625 -    *str += len; *lon = -val; return PGL_SCAN_LON;
  3.1626 -  }
  3.1627 -  return PGL_SCAN_NONE;
  3.1628 -}
  3.1629 -
  3.1630 -
  3.1631 -/*-----------------*
  3.1632 - *  SQL functions  *
  3.1633 - *-----------------*/
  3.1634 -
  3.1635 -/* Note: These function names use "epoint", "ebox", etc. notation here instead
  3.1636 -   of "point", "box", etc. in order to distinguish them from any previously
  3.1637 -   defined functions. */
  3.1638 -
  3.1639 -/* function needed for dummy types and/or not implemented features */
  3.1640 -PG_FUNCTION_INFO_V1(pgl_notimpl);
  3.1641 -Datum pgl_notimpl(PG_FUNCTION_ARGS) {
  3.1642 -  ereport(ERROR, (errmsg("not implemented by pgLatLon")));
  3.1643 -}
  3.1644 -
  3.1645 -/* set point to latitude and longitude (including checks) */
  3.1646 -static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) {
  3.1647 -  /* reject infinite or NaN values */
  3.1648 -  if (!isfinite(lat) || !isfinite(lon)) {
  3.1649 -    ereport(ERROR, (
  3.1650 -      errcode(ERRCODE_DATA_EXCEPTION),
  3.1651 -      errmsg("epoint requires finite coordinates")
  3.1652 -    ));
  3.1653 -  }
  3.1654 -  /* check latitude bounds */
  3.1655 -  if (lat < -90) {
  3.1656 -    ereport(WARNING, (errmsg("latitude exceeds south pole")));
  3.1657 -    lat = -90;
  3.1658 -  } else if (lat > 90) {
  3.1659 -    ereport(WARNING, (errmsg("latitude exceeds north pole")));
  3.1660 -    lat = 90;
  3.1661 -  }
  3.1662 -  /* check longitude bounds */
  3.1663 -  if (lon < -180) {
  3.1664 -    ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized")));
  3.1665 -    lon += 360 - trunc(lon / 360) * 360;
  3.1666 -  } else if (lon > 180) {
  3.1667 -    ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized")));
  3.1668 -    lon -= 360 + trunc(lon / 360) * 360;
  3.1669 -  }
  3.1670 -  /* store rounded latitude/longitude values for round-trip safety */
  3.1671 -  point->lat = pgl_round(lat);
  3.1672 -  point->lon = pgl_round(lon);
  3.1673 -}
  3.1674 -
  3.1675 -/* create point ("epoint" in SQL) from latitude and longitude */
  3.1676 -PG_FUNCTION_INFO_V1(pgl_create_epoint);
  3.1677 -Datum pgl_create_epoint(PG_FUNCTION_ARGS) {
  3.1678 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  3.1679 -  pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1));
  3.1680 -  PG_RETURN_POINTER(point);
  3.1681 -}
  3.1682 -
  3.1683 -/* parse point ("epoint" in SQL) */
  3.1684 -/* format: '[NS]<float> [EW]<float>' */
  3.1685 -PG_FUNCTION_INFO_V1(pgl_epoint_in);
  3.1686 -Datum pgl_epoint_in(PG_FUNCTION_ARGS) {
  3.1687 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  3.1688 -  char *strptr = str;  /* current position within string */
  3.1689 -  int done = 0;        /* bit mask storing if latitude or longitude was read */
  3.1690 -  double lat, lon;     /* parsed values as double precision floats */
  3.1691 -  pgl_point *point;    /* return value (to be palloc'ed) */
  3.1692 -  /* parse two floats (each latitude or longitude) separated by white-space */
  3.1693 -  done |= pgl_scan(&strptr, &lat, &lon);
  3.1694 -  if (strptr != str && isspace(strptr[-1])) {
  3.1695 -    done |= pgl_scan(&strptr, &lat, &lon);
  3.1696 -  }
  3.1697 -  /* require end of string, and latitude and longitude parsed successfully */
  3.1698 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  3.1699 -    ereport(ERROR, (
  3.1700 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  3.1701 -      errmsg("invalid input syntax for type epoint: \"%s\"", str)
  3.1702 -    ));
  3.1703 -  }
  3.1704 -  /* allocate memory for result */
  3.1705 -  point = (pgl_point *)palloc(sizeof(pgl_point));
  3.1706 -  /* set latitude and longitude (and perform checks) */
  3.1707 -  pgl_epoint_set_latlon(point, lat, lon);
  3.1708 -  /* return result */
  3.1709 -  PG_RETURN_POINTER(point);
  3.1710 -}
  3.1711 -
  3.1712 -/* create box ("ebox" in SQL) that is empty */
  3.1713 -PG_FUNCTION_INFO_V1(pgl_create_empty_ebox);
  3.1714 -Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) {
  3.1715 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  3.1716 -  pgl_box_set_empty(box);
  3.1717 -  PG_RETURN_POINTER(box);
  3.1718 -}
  3.1719 -
  3.1720 -/* set box to given boundaries (including checks) */
  3.1721 -static void pgl_ebox_set_boundaries(
  3.1722 -  pgl_box *box,
  3.1723 -  double lat_min, double lat_max, double lon_min, double lon_max
  3.1724 -) {
  3.1725 -  /* if minimum latitude is greater than maximum latitude, return empty box */
  3.1726 -  if (lat_min > lat_max) {
  3.1727 -    pgl_box_set_empty(box);
  3.1728 -    return;
  3.1729 -  }
  3.1730 -  /* otherwise reject infinite or NaN values */
  3.1731 -  if (
  3.1732 -    !isfinite(lat_min) || !isfinite(lat_max) ||
  3.1733 -    !isfinite(lon_min) || !isfinite(lon_max)
  3.1734 -  ) {
  3.1735 -    ereport(ERROR, (
  3.1736 -      errcode(ERRCODE_DATA_EXCEPTION),
  3.1737 -      errmsg("ebox requires finite coordinates")
  3.1738 -    ));
  3.1739 -  }
  3.1740 -  /* check latitude bounds */
  3.1741 -  if (lat_max < -90) {
  3.1742 -    ereport(WARNING, (errmsg("northern latitude exceeds south pole")));
  3.1743 -    lat_max = -90;
  3.1744 -  } else if (lat_max > 90) {
  3.1745 -    ereport(WARNING, (errmsg("northern latitude exceeds north pole")));
  3.1746 -    lat_max = 90;
  3.1747 -  }
  3.1748 -  if (lat_min < -90) {
  3.1749 -    ereport(WARNING, (errmsg("southern latitude exceeds south pole")));
  3.1750 -    lat_min = -90;
  3.1751 -  } else if (lat_min > 90) {
  3.1752 -    ereport(WARNING, (errmsg("southern latitude exceeds north pole")));
  3.1753 -    lat_min = 90;
  3.1754 -  }
  3.1755 -  /* check if all longitudes are included */
  3.1756 -  if (lon_max - lon_min >= 360) {
  3.1757 -    if (lon_max - lon_min > 360) ereport(WARNING, (
  3.1758 -      errmsg("longitude coverage greater than 360 degrees")
  3.1759 -    ));
  3.1760 -    lon_min = -180;
  3.1761 -    lon_max = 180;
  3.1762 -  } else {
  3.1763 -    /* normalize longitude bounds */
  3.1764 -    if      (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360;
  3.1765 -    else if (lon_min >  180) lon_min -= 360 + trunc(lon_min / 360) * 360;
  3.1766 -    if      (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360;
  3.1767 -    else if (lon_max >  180) lon_max -= 360 + trunc(lon_max / 360) * 360;
  3.1768 -  }
  3.1769 -  /* store rounded latitude/longitude values for round-trip safety */
  3.1770 -  box->lat_min = pgl_round(lat_min);
  3.1771 -  box->lat_max = pgl_round(lat_max);
  3.1772 -  box->lon_min = pgl_round(lon_min);
  3.1773 -  box->lon_max = pgl_round(lon_max);
  3.1774 -  /* ensure that rounding does not change orientation */
  3.1775 -  if (lon_min > lon_max && box->lon_min == box->lon_max) {
  3.1776 -    box->lon_min = -180;
  3.1777 -    box->lon_max = 180;
  3.1778 -  }
  3.1779 -}
  3.1780 -
  3.1781 -/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */
  3.1782 -PG_FUNCTION_INFO_V1(pgl_create_ebox);
  3.1783 -Datum pgl_create_ebox(PG_FUNCTION_ARGS) {
  3.1784 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  3.1785 -  pgl_ebox_set_boundaries(
  3.1786 -    box,
  3.1787 -    PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1),
  3.1788 -    PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3)
  3.1789 -  );
  3.1790 -  PG_RETURN_POINTER(box);
  3.1791 -}
  3.1792 -
  3.1793 -/* create box ("ebox" in SQL) from two points ("epoint"s) */
  3.1794 -/* (can not be used to cover a longitude range of more than 120 degrees) */
  3.1795 -PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints);
  3.1796 -Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) {
  3.1797 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  3.1798 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  3.1799 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  3.1800 -  double lat_min, lat_max, lon_min, lon_max;
  3.1801 -  double dlon;  /* longitude range (delta longitude) */
  3.1802 -  /* order latitude and longitude boundaries */
  3.1803 -  if (point2->lat < point1->lat) {
  3.1804 -    lat_min = point2->lat;
  3.1805 -    lat_max = point1->lat;
  3.1806 -  } else {
  3.1807 -    lat_min = point1->lat;
  3.1808 -    lat_max = point2->lat;
  3.1809 -  }
  3.1810 -  if (point2->lon < point1->lon) {
  3.1811 -    lon_min = point2->lon;
  3.1812 -    lon_max = point1->lon;
  3.1813 -  } else {
  3.1814 -    lon_min = point1->lon;
  3.1815 -    lon_max = point2->lon;
  3.1816 -  }
  3.1817 -  /* calculate longitude range (round to avoid floating point errors) */
  3.1818 -  dlon = pgl_round(lon_max - lon_min);
  3.1819 -  /* determine east-west direction */
  3.1820 -  if (dlon >= 240) {
  3.1821 -    /* assume that 180th meridian is crossed and swap min/max longitude */
  3.1822 -    double swap = lon_min; lon_min = lon_max; lon_max = swap;
  3.1823 -  } else if (dlon > 120) {
  3.1824 -    /* unclear orientation since delta longitude > 120 */
  3.1825 -    ereport(ERROR, (
  3.1826 -      errcode(ERRCODE_DATA_EXCEPTION),
  3.1827 -      errmsg("can not determine east/west orientation for ebox")
  3.1828 -    ));
  3.1829 -  }
  3.1830 -  /* use boundaries to setup box (and perform checks) */
  3.1831 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  3.1832 -  /* return result */
  3.1833 -  PG_RETURN_POINTER(box);
  3.1834 -}
  3.1835 -
  3.1836 -/* parse box ("ebox" in SQL) */
  3.1837 -/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>'
  3.1838 -       or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */
  3.1839 -PG_FUNCTION_INFO_V1(pgl_ebox_in);
  3.1840 -Datum pgl_ebox_in(PG_FUNCTION_ARGS) {
  3.1841 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  3.1842 -  char *str_lower;     /* lower case version of input string */
  3.1843 -  char *strptr;        /* current position within string */
  3.1844 -  int valid;           /* number of valid chars */
  3.1845 -  int done;            /* specifies if latitude or longitude was read */
  3.1846 -  double val;          /* temporary variable */
  3.1847 -  int lat_count = 0;   /* count of latitude values parsed */
  3.1848 -  int lon_count = 0;   /* count of longitufde values parsed */
  3.1849 -  double lat_min, lat_max, lon_min, lon_max;  /* see pgl_box struct */
  3.1850 -  pgl_box *box;        /* return value (to be palloc'ed) */
  3.1851 -  /* lowercase input */
  3.1852 -  str_lower = psprintf("%s", str);
  3.1853 -  for (strptr=str_lower; *strptr; strptr++) {
  3.1854 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  3.1855 -  }
  3.1856 -  /* reset reading position to start of (lowercase) string */
  3.1857 -  strptr = str_lower;
  3.1858 -  /* check if empty box */
  3.1859 -  valid = 0;
  3.1860 -  sscanf(strptr, " empty %n", &valid);
  3.1861 -  if (valid && strptr[valid] == 0) {
  3.1862 -    /* allocate and return empty box */
  3.1863 -    box = (pgl_box *)palloc(sizeof(pgl_box));
  3.1864 -    pgl_box_set_empty(box);
  3.1865 -    PG_RETURN_POINTER(box);
  3.1866 -  }
  3.1867 -  /* demand four blocks separated by whitespace */
  3.1868 -  valid = 0;
  3.1869 -  sscanf(strptr, " %*s %*s %*s %*s %n", &valid);
  3.1870 -  /* if four blocks separated by whitespace exist, parse those blocks */
  3.1871 -  if (strptr[valid] == 0) while (strptr[0]) {
  3.1872 -    /* parse either latitude or longitude (whichever found in input string) */
  3.1873 -    done = pgl_scan(&strptr, &val, &val);
  3.1874 -    /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */
  3.1875 -    if (done == PGL_SCAN_LAT) {
  3.1876 -      if (!lat_count) lat_min = val; else lat_max = val;
  3.1877 -      lat_count++;
  3.1878 -    } else if (done == PGL_SCAN_LON) {
  3.1879 -      if (!lon_count) lon_min = val; else lon_max = val;
  3.1880 -      lon_count++;
  3.1881 -    } else {
  3.1882 -      break;
  3.1883 -    }
  3.1884 -  }
  3.1885 -  /* require end of string, and two latitude and two longitude values */
  3.1886 -  if (strptr[0] || lat_count != 2 || lon_count != 2) {
  3.1887 -    ereport(ERROR, (
  3.1888 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  3.1889 -      errmsg("invalid input syntax for type ebox: \"%s\"", str)
  3.1890 -    ));
  3.1891 -  }
  3.1892 -  /* free lower case string */
  3.1893 -  pfree(str_lower);
  3.1894 -  /* order boundaries (maximum greater than minimum) */
  3.1895 -  if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; }
  3.1896 -  if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; }
  3.1897 -  /* allocate memory for result */
  3.1898 -  box = (pgl_box *)palloc(sizeof(pgl_box));
  3.1899 -  /* set boundaries (and perform checks) */
  3.1900 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  3.1901 -  /* return result */
  3.1902 -  PG_RETURN_POINTER(box);
  3.1903 -}
  3.1904 -
  3.1905 -/* set circle to given latitude, longitude, and radius (including checks) */
  3.1906 -static void pgl_ecircle_set_latlon_radius(
  3.1907 -  pgl_circle *circle, double lat, double lon, double radius
  3.1908 -) {
  3.1909 -  /* set center point (including checks) */
  3.1910 -  pgl_epoint_set_latlon(&(circle->center), lat, lon);
  3.1911 -  /* handle non-positive radius */
  3.1912 -  if (isnan(radius)) {
  3.1913 -    ereport(ERROR, (
  3.1914 -      errcode(ERRCODE_DATA_EXCEPTION),
  3.1915 -      errmsg("invalid radius for ecircle")
  3.1916 -    ));
  3.1917 -  }
  3.1918 -  if (radius == 0) radius = 0;  /* avoids -0 */
  3.1919 -  else if (radius < 0) {
  3.1920 -    if (isfinite(radius)) {
  3.1921 -      ereport(NOTICE, (errmsg("negative radius converted to minus infinity")));
  3.1922 -    }
  3.1923 -    radius = -INFINITY;
  3.1924 -  }
  3.1925 -  /* store radius (round-trip safety is ensured by pgl_print_float) */
  3.1926 -  circle->radius = radius;
  3.1927 -}
  3.1928 -
  3.1929 -/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */
  3.1930 -PG_FUNCTION_INFO_V1(pgl_create_ecircle);
  3.1931 -Datum pgl_create_ecircle(PG_FUNCTION_ARGS) {
  3.1932 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  3.1933 -  pgl_ecircle_set_latlon_radius(
  3.1934 -    circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2)
  3.1935 -  );
  3.1936 -  PG_RETURN_POINTER(circle);
  3.1937 -}
  3.1938 -
  3.1939 -/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */
  3.1940 -PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint);
  3.1941 -Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) {
  3.1942 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.1943 -  double radius = PG_GETARG_FLOAT8(1);
  3.1944 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  3.1945 -  /* set latitude, longitude, radius (and perform checks) */
  3.1946 -  pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius);
  3.1947 -  /* return result */
  3.1948 -  PG_RETURN_POINTER(circle);
  3.1949 -}
  3.1950 -
  3.1951 -/* parse circle ("ecircle" in SQL) */
  3.1952 -/* format: '[NS]<float> [EW]<float> <float>' */
  3.1953 -PG_FUNCTION_INFO_V1(pgl_ecircle_in);
  3.1954 -Datum pgl_ecircle_in(PG_FUNCTION_ARGS) {
  3.1955 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  3.1956 -  char *strptr = str;       /* current position within string */
  3.1957 -  double lat, lon, radius;  /* parsed values as double precision flaots */
  3.1958 -  int valid = 0;            /* number of valid chars */
  3.1959 -  int done = 0;             /* stores if latitude and/or longitude was read */
  3.1960 -  pgl_circle *circle;       /* return value (to be palloc'ed) */
  3.1961 -  /* demand three blocks separated by whitespace */
  3.1962 -  sscanf(strptr, " %*s %*s %*s %n", &valid);
  3.1963 -  /* if three blocks separated by whitespace exist, parse those blocks */
  3.1964 -  if (strptr[valid] == 0) {
  3.1965 -    /* parse latitude and longitude */
  3.1966 -    done |= pgl_scan(&strptr, &lat, &lon);
  3.1967 -    done |= pgl_scan(&strptr, &lat, &lon);
  3.1968 -    /* parse radius (while incrementing strptr by number of bytes parsed) */
  3.1969 -    valid = 0;
  3.1970 -    if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid;
  3.1971 -  }
  3.1972 -  /* require end of string and both latitude and longitude being parsed */
  3.1973 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  3.1974 -    ereport(ERROR, (
  3.1975 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  3.1976 -      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  3.1977 -    ));
  3.1978 -  }
  3.1979 -  /* allocate memory for result */
  3.1980 -  circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  3.1981 -  /* set latitude, longitude, radius (and perform checks) */
  3.1982 -  pgl_ecircle_set_latlon_radius(circle, lat, lon, radius);
  3.1983 -  /* return result */
  3.1984 -  PG_RETURN_POINTER(circle);
  3.1985 -}
  3.1986 -
  3.1987 -/* parse cluster ("ecluster" in SQL) */
  3.1988 -PG_FUNCTION_INFO_V1(pgl_ecluster_in);
  3.1989 -Datum pgl_ecluster_in(PG_FUNCTION_ARGS) {
  3.1990 -  int i;
  3.1991 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  3.1992 -  char *str_lower;         /* lower case version of input string */
  3.1993 -  char *strptr;            /* pointer to current reading position of input */
  3.1994 -  int npoints_total = 0;   /* total number of points in cluster */
  3.1995 -  int nentries = 0;        /* total number of entries */
  3.1996 -  pgl_newentry *entries;   /* array of pgl_newentry to create pgl_cluster */
  3.1997 -  int entries_buflen = 4;  /* maximum number of elements in entries array */
  3.1998 -  int valid;               /* number of valid chars processed */
  3.1999 -  double lat, lon;         /* latitude and longitude of parsed point */
  3.2000 -  int entrytype;           /* current entry type */
  3.2001 -  int npoints;             /* number of points in current entry */
  3.2002 -  pgl_point *points;       /* array of pgl_point for pgl_newentry */
  3.2003 -  int points_buflen;       /* maximum number of elements in points array */
  3.2004 -  int done;                /* return value of pgl_scan function */
  3.2005 -  pgl_cluster *cluster;    /* created cluster */
  3.2006 -  /* lowercase input */
  3.2007 -  str_lower = psprintf("%s", str);
  3.2008 -  for (strptr=str_lower; *strptr; strptr++) {
  3.2009 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  3.2010 -  }
  3.2011 -  /* reset reading position to start of (lowercase) string */
  3.2012 -  strptr = str_lower;
  3.2013 -  /* allocate initial buffer for entries */
  3.2014 -  entries = palloc(entries_buflen * sizeof(pgl_newentry));
  3.2015 -  /* parse until end of string */
  3.2016 -  while (strptr[0]) {
  3.2017 -    /* require previous white-space or closing parenthesis before next token */
  3.2018 -    if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') {
  3.2019 -      goto pgl_ecluster_in_error;
  3.2020 -    }
  3.2021 -    /* ignore token "empty" */
  3.2022 -    valid = 0; sscanf(strptr, " empty %n", &valid);
  3.2023 -    if (valid) { strptr += valid; continue; }
  3.2024 -    /* test for "point" token */
  3.2025 -    valid = 0; sscanf(strptr, " point ( %n", &valid);
  3.2026 -    if (valid) {
  3.2027 -      strptr += valid;
  3.2028 -      entrytype = PGL_ENTRY_POINT;
  3.2029 -      goto pgl_ecluster_in_type_ok;
  3.2030 -    }
  3.2031 -    /* test for "path" token */
  3.2032 -    valid = 0; sscanf(strptr, " path ( %n", &valid);
  3.2033 -    if (valid) {
  3.2034 -      strptr += valid;
  3.2035 -      entrytype = PGL_ENTRY_PATH;
  3.2036 -      goto pgl_ecluster_in_type_ok;
  3.2037 -    }
  3.2038 -    /* test for "outline" token */
  3.2039 -    valid = 0; sscanf(strptr, " outline ( %n", &valid);
  3.2040 -    if (valid) {
  3.2041 -      strptr += valid;
  3.2042 -      entrytype = PGL_ENTRY_OUTLINE;
  3.2043 -      goto pgl_ecluster_in_type_ok;
  3.2044 -    }
  3.2045 -    /* test for "polygon" token */
  3.2046 -    valid = 0; sscanf(strptr, " polygon ( %n", &valid);
  3.2047 -    if (valid) {
  3.2048 -      strptr += valid;
  3.2049 -      entrytype = PGL_ENTRY_POLYGON;
  3.2050 -      goto pgl_ecluster_in_type_ok;
  3.2051 -    }
  3.2052 -    /* error if no valid token found */
  3.2053 -    goto pgl_ecluster_in_error;
  3.2054 -    pgl_ecluster_in_type_ok:
  3.2055 -    /* check if pgl_newentry array needs to grow */
  3.2056 -    if (nentries == entries_buflen) {
  3.2057 -      pgl_newentry *newbuf;
  3.2058 -      entries_buflen *= 2;
  3.2059 -      newbuf = palloc(entries_buflen * sizeof(pgl_newentry));
  3.2060 -      memcpy(newbuf, entries, nentries * sizeof(pgl_newentry));
  3.2061 -      pfree(entries);
  3.2062 -      entries = newbuf;
  3.2063 -    }
  3.2064 -    /* reset number of points for current entry */
  3.2065 -    npoints = 0;
  3.2066 -    /* allocate array for points */
  3.2067 -    points_buflen = 4;
  3.2068 -    points = palloc(points_buflen * sizeof(pgl_point));
  3.2069 -    /* parse until closing parenthesis */
  3.2070 -    while (strptr[0] != ')') {
  3.2071 -      /* error on unexpected end of string */
  3.2072 -      if (strptr[0] == 0) goto pgl_ecluster_in_error;
  3.2073 -      /* mark neither latitude nor longitude as read */
  3.2074 -      done = PGL_SCAN_NONE;
  3.2075 -      /* require white-space before second, third, etc. point */
  3.2076 -      if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  3.2077 -      /* scan latitude (or longitude) */
  3.2078 -      done |= pgl_scan(&strptr, &lat, &lon);
  3.2079 -      /* require white-space before second coordinate */
  3.2080 -      if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  3.2081 -      /* scan longitude (or latitude) */
  3.2082 -      done |= pgl_scan(&strptr, &lat, &lon);
  3.2083 -      /* error unless both latitude and longitude were parsed */
  3.2084 -      if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error;
  3.2085 -      /* throw error if number of points is too high */
  3.2086 -      if (npoints_total == PGL_CLUSTER_MAXPOINTS) {
  3.2087 -        ereport(ERROR, (
  3.2088 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  3.2089 -          errmsg(
  3.2090 -            "too many points for ecluster entry (maximum %i)",
  3.2091 -            PGL_CLUSTER_MAXPOINTS
  3.2092 -          )
  3.2093 -        ));
  3.2094 -      }
  3.2095 -      /* check if pgl_point array needs to grow */
  3.2096 -      if (npoints == points_buflen) {
  3.2097 -        pgl_point *newbuf;
  3.2098 -        points_buflen *= 2;
  3.2099 -        newbuf = palloc(points_buflen * sizeof(pgl_point));
  3.2100 -        memcpy(newbuf, points, npoints * sizeof(pgl_point));
  3.2101 -        pfree(points);
  3.2102 -        points = newbuf;
  3.2103 -      }
  3.2104 -      /* append point to pgl_point array (includes checks) */
  3.2105 -      pgl_epoint_set_latlon(&(points[npoints++]), lat, lon);
  3.2106 -      /* increase total number of points */
  3.2107 -      npoints_total++;
  3.2108 -    }
  3.2109 -    /* error if entry has no points */
  3.2110 -    if (!npoints) goto pgl_ecluster_in_error;
  3.2111 -    /* entries with one point are automatically of type "point" */
  3.2112 -    if (npoints == 1) entrytype = PGL_ENTRY_POINT;
  3.2113 -    /* if entries have more than one point */
  3.2114 -    else {
  3.2115 -      /* throw error if entry type is "point" */
  3.2116 -      if (entrytype == PGL_ENTRY_POINT) {
  3.2117 -        ereport(ERROR, (
  3.2118 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  3.2119 -          errmsg("invalid input syntax for type ecluster (point entry with more than one point)")
  3.2120 -        ));
  3.2121 -      }
  3.2122 -      /* coerce outlines and polygons with more than 2 points to be a path */
  3.2123 -      if (npoints == 2) entrytype = PGL_ENTRY_PATH;
  3.2124 -    }
  3.2125 -    /* append entry to pgl_newentry array */
  3.2126 -    entries[nentries].entrytype = entrytype;
  3.2127 -    entries[nentries].npoints = npoints;
  3.2128 -    entries[nentries].points = points;
  3.2129 -    nentries++;
  3.2130 -    /* consume closing parenthesis */
  3.2131 -    strptr++;
  3.2132 -    /* consume white-space */
  3.2133 -    while (isspace(strptr[0])) strptr++;
  3.2134 -  }
  3.2135 -  /* free lower case string */
  3.2136 -  pfree(str_lower);
  3.2137 -  /* create cluster from pgl_newentry array */
  3.2138 -  cluster = pgl_new_cluster(nentries, entries);
  3.2139 -  /* free pgl_newentry array */
  3.2140 -  for (i=0; i<nentries; i++) pfree(entries[i].points);
  3.2141 -  pfree(entries);
  3.2142 -  /* set bounding circle of cluster and check east/west orientation */
  3.2143 -  if (!pgl_finalize_cluster(cluster)) {
  3.2144 -    ereport(ERROR, (
  3.2145 -      errcode(ERRCODE_DATA_EXCEPTION),
  3.2146 -      errmsg("can not determine east/west orientation for ecluster"),
  3.2147 -      errhint("Ensure that each entry has a longitude span of less than 180 degrees.")
  3.2148 -    ));
  3.2149 -  }
  3.2150 -  /* return cluster */
  3.2151 -  PG_RETURN_POINTER(cluster);
  3.2152 -  /* code to throw error */
  3.2153 -  pgl_ecluster_in_error:
  3.2154 -  ereport(ERROR, (
  3.2155 -    errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  3.2156 -    errmsg("invalid input syntax for type ecluster: \"%s\"", str)
  3.2157 -  ));
  3.2158 -}
  3.2159 -
  3.2160 -/* convert point ("epoint") to string representation */
  3.2161 -PG_FUNCTION_INFO_V1(pgl_epoint_out);
  3.2162 -Datum pgl_epoint_out(PG_FUNCTION_ARGS) {
  3.2163 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2164 -  char latstr[PGL_NUMBUFLEN];
  3.2165 -  char lonstr[PGL_NUMBUFLEN];
  3.2166 -  pgl_print_lat(latstr, point->lat);
  3.2167 -  pgl_print_lon(lonstr, point->lon);
  3.2168 -  PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr));
  3.2169 -}
  3.2170 -
  3.2171 -/* convert box ("ebox") to string representation */
  3.2172 -PG_FUNCTION_INFO_V1(pgl_ebox_out);
  3.2173 -Datum pgl_ebox_out(PG_FUNCTION_ARGS) {
  3.2174 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  3.2175 -  double lon_min = box->lon_min;
  3.2176 -  double lon_max = box->lon_max;
  3.2177 -  char lat_min_str[PGL_NUMBUFLEN];
  3.2178 -  char lat_max_str[PGL_NUMBUFLEN];
  3.2179 -  char lon_min_str[PGL_NUMBUFLEN];
  3.2180 -  char lon_max_str[PGL_NUMBUFLEN];
  3.2181 -  /* return string "empty" if box is set to be empty */
  3.2182 -  if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty");
  3.2183 -  /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */
  3.2184 -  /* (required since pgl_box_in orders the longitude boundaries) */
  3.2185 -  if (lon_min > lon_max) {
  3.2186 -    if (lon_min + lon_max >= 0) lon_min -= 360;
  3.2187 -    else lon_max += 360;
  3.2188 -  }
  3.2189 -  /* format and return result */
  3.2190 -  pgl_print_lat(lat_min_str, box->lat_min);
  3.2191 -  pgl_print_lat(lat_max_str, box->lat_max);
  3.2192 -  pgl_print_lon(lon_min_str, lon_min);
  3.2193 -  pgl_print_lon(lon_max_str, lon_max);
  3.2194 -  PG_RETURN_CSTRING(psprintf(
  3.2195 -    "%s %s %s %s",
  3.2196 -    lat_min_str, lon_min_str, lat_max_str, lon_max_str
  3.2197 -  ));
  3.2198 -}
  3.2199 -
  3.2200 -/* convert circle ("ecircle") to string representation */
  3.2201 -PG_FUNCTION_INFO_V1(pgl_ecircle_out);
  3.2202 -Datum pgl_ecircle_out(PG_FUNCTION_ARGS) {
  3.2203 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  3.2204 -  char latstr[PGL_NUMBUFLEN];
  3.2205 -  char lonstr[PGL_NUMBUFLEN];
  3.2206 -  char radstr[PGL_NUMBUFLEN];
  3.2207 -  pgl_print_lat(latstr, circle->center.lat);
  3.2208 -  pgl_print_lon(lonstr, circle->center.lon);
  3.2209 -  pgl_print_float(radstr, circle->radius);
  3.2210 -  PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr));
  3.2211 -}
  3.2212 -
  3.2213 -/* convert cluster ("ecluster") to string representation */
  3.2214 -PG_FUNCTION_INFO_V1(pgl_ecluster_out);
  3.2215 -Datum pgl_ecluster_out(PG_FUNCTION_ARGS) {
  3.2216 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  3.2217 -  char latstr[PGL_NUMBUFLEN];  /* string buffer for latitude */
  3.2218 -  char lonstr[PGL_NUMBUFLEN];  /* string buffer for longitude */
  3.2219 -  char ***strings;     /* array of array of strings */
  3.2220 -  char *string;        /* string of current token */
  3.2221 -  char *res, *resptr;  /* result and pointer to current write position */
  3.2222 -  size_t reslen = 1;   /* length of result (init with 1 for terminator) */
  3.2223 -  int npoints;         /* number of points of current entry */
  3.2224 -  int i, j;            /* i: entry, j: point in entry */
  3.2225 -  /* handle empty clusters */
  3.2226 -  if (cluster->nentries == 0) {
  3.2227 -    /* free detoasted cluster (if copy) */
  3.2228 -    PG_FREE_IF_COPY(cluster, 0);
  3.2229 -    /* return static result */
  3.2230 -    PG_RETURN_CSTRING("empty");
  3.2231 -  }
  3.2232 -  /* allocate array of array of strings */
  3.2233 -  strings = palloc(cluster->nentries * sizeof(char **));
  3.2234 -  /* iterate over all entries in cluster */
  3.2235 -  for (i=0; i<cluster->nentries; i++) {
  3.2236 -    /* get number of points in entry */
  3.2237 -    npoints = cluster->entries[i].npoints;
  3.2238 -    /* allocate array of strings (one string for each point plus two extra) */
  3.2239 -    strings[i] = palloc((2 + npoints) * sizeof(char *));
  3.2240 -    /* determine opening string */
  3.2241 -    switch (cluster->entries[i].entrytype) {
  3.2242 -      case PGL_ENTRY_POINT:   string = (i==0)?"point ("  :" point (";   break;
  3.2243 -      case PGL_ENTRY_PATH:    string = (i==0)?"path ("   :" path (";    break;
  3.2244 -      case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break;
  3.2245 -      case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break;
  3.2246 -      default:                string = (i==0)?"unknown"  :" unknown";
  3.2247 -    }
  3.2248 -    /* use opening string as first string in array */
  3.2249 -    strings[i][0] = string;
  3.2250 -    /* update result length (for allocating result string later) */
  3.2251 -    reslen += strlen(string);
  3.2252 -    /* iterate over all points */
  3.2253 -    for (j=0; j<npoints; j++) {
  3.2254 -      /* create string representation of point */
  3.2255 -      pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat);
  3.2256 -      pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon);
  3.2257 -      string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr);
  3.2258 -      /* copy string pointer to string array */
  3.2259 -      strings[i][j+1] = string;
  3.2260 -      /* update result length (for allocating result string later) */
  3.2261 -      reslen += strlen(string);
  3.2262 -    }
  3.2263 -    /* use closing parenthesis as last string in array */
  3.2264 -    strings[i][npoints+1] = ")";
  3.2265 -    /* update result length (for allocating result string later) */
  3.2266 -    reslen++;
  3.2267 -  }
  3.2268 -  /* allocate result string */
  3.2269 -  res = palloc(reslen);
  3.2270 -  /* set write pointer to begin of result string */
  3.2271 -  resptr = res;
  3.2272 -  /* copy strings into result string */
  3.2273 -  for (i=0; i<cluster->nentries; i++) {
  3.2274 -    npoints = cluster->entries[i].npoints;
  3.2275 -    for (j=0; j<npoints+2; j++) {
  3.2276 -      string = strings[i][j];
  3.2277 -      strcpy(resptr, string);
  3.2278 -      resptr += strlen(string);
  3.2279 -      /* free strings allocated by psprintf */
  3.2280 -      if (j != 0 && j != npoints+1) pfree(string);
  3.2281 -    }
  3.2282 -    /* free array of strings */
  3.2283 -    pfree(strings[i]);
  3.2284 -  }
  3.2285 -  /* free array of array of strings */
  3.2286 -  pfree(strings);
  3.2287 -  /* free detoasted cluster (if copy) */
  3.2288 -  PG_FREE_IF_COPY(cluster, 0);
  3.2289 -  /* return result */
  3.2290 -  PG_RETURN_CSTRING(res);
  3.2291 -}
  3.2292 -
  3.2293 -/* binary input function for point ("epoint") */
  3.2294 -PG_FUNCTION_INFO_V1(pgl_epoint_recv);
  3.2295 -Datum pgl_epoint_recv(PG_FUNCTION_ARGS) {
  3.2296 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  3.2297 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  3.2298 -  point->lat = pq_getmsgfloat8(buf);
  3.2299 -  point->lon = pq_getmsgfloat8(buf);
  3.2300 -  PG_RETURN_POINTER(point);
  3.2301 -}
  3.2302 -
  3.2303 -/* binary input function for box ("ebox") */
  3.2304 -PG_FUNCTION_INFO_V1(pgl_ebox_recv);
  3.2305 -Datum pgl_ebox_recv(PG_FUNCTION_ARGS) {
  3.2306 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  3.2307 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  3.2308 -  box->lat_min = pq_getmsgfloat8(buf);
  3.2309 -  box->lat_max = pq_getmsgfloat8(buf);
  3.2310 -  box->lon_min = pq_getmsgfloat8(buf);
  3.2311 -  box->lon_max = pq_getmsgfloat8(buf);
  3.2312 -  PG_RETURN_POINTER(box);
  3.2313 -}
  3.2314 -
  3.2315 -/* binary input function for circle ("ecircle") */
  3.2316 -PG_FUNCTION_INFO_V1(pgl_ecircle_recv);
  3.2317 -Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) {
  3.2318 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  3.2319 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  3.2320 -  circle->center.lat = pq_getmsgfloat8(buf);
  3.2321 -  circle->center.lon = pq_getmsgfloat8(buf);
  3.2322 -  circle->radius = pq_getmsgfloat8(buf);
  3.2323 -  PG_RETURN_POINTER(circle);
  3.2324 -}
  3.2325 -
  3.2326 -/* TODO: binary receive function for cluster */
  3.2327 -
  3.2328 -/* binary output function for point ("epoint") */
  3.2329 -PG_FUNCTION_INFO_V1(pgl_epoint_send);
  3.2330 -Datum pgl_epoint_send(PG_FUNCTION_ARGS) {
  3.2331 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2332 -  StringInfoData buf;
  3.2333 -  pq_begintypsend(&buf);
  3.2334 -  pq_sendfloat8(&buf, point->lat);
  3.2335 -  pq_sendfloat8(&buf, point->lon);
  3.2336 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  3.2337 -}
  3.2338 -
  3.2339 -/* binary output function for box ("ebox") */
  3.2340 -PG_FUNCTION_INFO_V1(pgl_ebox_send);
  3.2341 -Datum pgl_ebox_send(PG_FUNCTION_ARGS) {
  3.2342 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  3.2343 -  StringInfoData buf;
  3.2344 -  pq_begintypsend(&buf);
  3.2345 -  pq_sendfloat8(&buf, box->lat_min);
  3.2346 -  pq_sendfloat8(&buf, box->lat_max);
  3.2347 -  pq_sendfloat8(&buf, box->lon_min);
  3.2348 -  pq_sendfloat8(&buf, box->lon_max);
  3.2349 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  3.2350 -}
  3.2351 -
  3.2352 -/* binary output function for circle ("ecircle") */
  3.2353 -PG_FUNCTION_INFO_V1(pgl_ecircle_send);
  3.2354 -Datum pgl_ecircle_send(PG_FUNCTION_ARGS) {
  3.2355 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  3.2356 -  StringInfoData buf;
  3.2357 -  pq_begintypsend(&buf);
  3.2358 -  pq_sendfloat8(&buf, circle->center.lat);
  3.2359 -  pq_sendfloat8(&buf, circle->center.lon);
  3.2360 -  pq_sendfloat8(&buf, circle->radius);
  3.2361 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  3.2362 -}
  3.2363 -
  3.2364 -/* TODO: binary send functions for cluster */
  3.2365 -
  3.2366 -/* cast point ("epoint") to box ("ebox") */
  3.2367 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox);
  3.2368 -Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) {
  3.2369 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2370 -  pgl_box *box = palloc(sizeof(pgl_box));
  3.2371 -  box->lat_min = point->lat;
  3.2372 -  box->lat_max = point->lat;
  3.2373 -  box->lon_min = point->lon;
  3.2374 -  box->lon_max = point->lon;
  3.2375 -  PG_RETURN_POINTER(box);
  3.2376 -}
  3.2377 -
  3.2378 -/* cast point ("epoint") to circle ("ecircle") */
  3.2379 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle);
  3.2380 -Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) {
  3.2381 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2382 -  pgl_circle *circle = palloc(sizeof(pgl_box));
  3.2383 -  circle->center = *point;
  3.2384 -  circle->radius = 0;
  3.2385 -  PG_RETURN_POINTER(circle);
  3.2386 -}
  3.2387 -
  3.2388 -/* cast point ("epoint") to cluster ("ecluster") */
  3.2389 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster);
  3.2390 -Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) {
  3.2391 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2392 -  pgl_newentry entry;
  3.2393 -  pgl_cluster *cluster;
  3.2394 -  entry.entrytype = PGL_ENTRY_POINT;
  3.2395 -  entry.npoints = 1;
  3.2396 -  entry.points = point;
  3.2397 -  cluster = pgl_new_cluster(1, &entry);
  3.2398 -  pgl_finalize_cluster(cluster);  /* NOTE: should not fail */
  3.2399 -  PG_RETURN_POINTER(cluster);
  3.2400 -}
  3.2401 -
  3.2402 -/* cast box ("ebox") to cluster ("ecluster") */
  3.2403 -#define pgl_ebox_to_ecluster_macro(i, a, b) \
  3.2404 -  entries[i].entrytype = PGL_ENTRY_POLYGON; \
  3.2405 -  entries[i].npoints = 4; \
  3.2406 -  entries[i].points = points[i]; \
  3.2407 -  points[i][0].lat = box->lat_min; \
  3.2408 -  points[i][0].lon = (a); \
  3.2409 -  points[i][1].lat = box->lat_min; \
  3.2410 -  points[i][1].lon = (b); \
  3.2411 -  points[i][2].lat = box->lat_max; \
  3.2412 -  points[i][2].lon = (b); \
  3.2413 -  points[i][3].lat = box->lat_max; \
  3.2414 -  points[i][3].lon = (a);
  3.2415 -PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster);
  3.2416 -Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) {
  3.2417 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  3.2418 -  double lon, dlon;
  3.2419 -  int nentries;
  3.2420 -  pgl_newentry entries[3];
  3.2421 -  pgl_point points[3][4];
  3.2422 -  pgl_cluster *cluster;
  3.2423 -  if (box->lat_min > box->lat_max) {
  3.2424 -    nentries = 0;
  3.2425 -  } else if (box->lon_min > box->lon_max) {
  3.2426 -    if (box->lon_min < 0) {
  3.2427 -      lon = pgl_round((box->lon_min + 180) / 2.0);
  3.2428 -      nentries = 3;
  3.2429 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  3.2430 -      pgl_ebox_to_ecluster_macro(1, lon, 180);
  3.2431 -      pgl_ebox_to_ecluster_macro(2, -180, box->lon_max);
  3.2432 -    } else if (box->lon_max > 0) {
  3.2433 -      lon = pgl_round((box->lon_max - 180) / 2.0);
  3.2434 -      nentries = 3;
  3.2435 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  3.2436 -      pgl_ebox_to_ecluster_macro(1, -180, lon);
  3.2437 -      pgl_ebox_to_ecluster_macro(2, lon, box->lon_max);
  3.2438 -    } else {
  3.2439 -      nentries = 2;
  3.2440 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  3.2441 -      pgl_ebox_to_ecluster_macro(1, -180, box->lon_max);
  3.2442 -    }
  3.2443 -  } else {
  3.2444 -    dlon = pgl_round(box->lon_max - box->lon_min);
  3.2445 -    if (dlon < 180) {
  3.2446 -      nentries = 1;
  3.2447 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max);
  3.2448 -    } else {
  3.2449 -      lon = pgl_round((box->lon_min + box->lon_max) / 2.0);
  3.2450 -      if (
  3.2451 -        pgl_round(lon - box->lon_min) < 180 &&
  3.2452 -        pgl_round(box->lon_max - lon) < 180
  3.2453 -      ) {
  3.2454 -        nentries = 2;
  3.2455 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  3.2456 -        pgl_ebox_to_ecluster_macro(1, lon, box->lon_max);
  3.2457 -      } else {
  3.2458 -        nentries = 3;
  3.2459 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, -60);
  3.2460 -        pgl_ebox_to_ecluster_macro(1, -60, 60);
  3.2461 -        pgl_ebox_to_ecluster_macro(2, 60, box->lon_max);
  3.2462 -      }
  3.2463 -    }
  3.2464 -  }
  3.2465 -  cluster = pgl_new_cluster(nentries, entries);
  3.2466 -  pgl_finalize_cluster(cluster);  /* NOTE: should not fail */
  3.2467 -  PG_RETURN_POINTER(cluster);
  3.2468 -}
  3.2469 -
  3.2470 -/* extract latitude from point ("epoint") */
  3.2471 -PG_FUNCTION_INFO_V1(pgl_epoint_lat);
  3.2472 -Datum pgl_epoint_lat(PG_FUNCTION_ARGS) {
  3.2473 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat);
  3.2474 -}
  3.2475 -
  3.2476 -/* extract longitude from point ("epoint") */
  3.2477 -PG_FUNCTION_INFO_V1(pgl_epoint_lon);
  3.2478 -Datum pgl_epoint_lon(PG_FUNCTION_ARGS) {
  3.2479 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon);
  3.2480 -}
  3.2481 -
  3.2482 -/* extract minimum latitude from box ("ebox") */
  3.2483 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_min);
  3.2484 -Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) {
  3.2485 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min);
  3.2486 -}
  3.2487 -
  3.2488 -/* extract maximum latitude from box ("ebox") */
  3.2489 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_max);
  3.2490 -Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) {
  3.2491 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max);
  3.2492 -}
  3.2493 -
  3.2494 -/* extract minimum longitude from box ("ebox") */
  3.2495 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_min);
  3.2496 -Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) {
  3.2497 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min);
  3.2498 -}
  3.2499 -
  3.2500 -/* extract maximum longitude from box ("ebox") */
  3.2501 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_max);
  3.2502 -Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) {
  3.2503 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max);
  3.2504 -}
  3.2505 -
  3.2506 -/* extract center point from circle ("ecircle") */
  3.2507 -PG_FUNCTION_INFO_V1(pgl_ecircle_center);
  3.2508 -Datum pgl_ecircle_center(PG_FUNCTION_ARGS) {
  3.2509 -  PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center));
  3.2510 -}
  3.2511 -
  3.2512 -/* extract radius from circle ("ecircle") */
  3.2513 -PG_FUNCTION_INFO_V1(pgl_ecircle_radius);
  3.2514 -Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) {
  3.2515 -  PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius);
  3.2516 -}
  3.2517 -
  3.2518 -/* check if point is inside box (overlap operator "&&") in SQL */
  3.2519 -PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap);
  3.2520 -Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) {
  3.2521 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2522 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1);
  3.2523 -  PG_RETURN_BOOL(pgl_point_in_box(point, box));
  3.2524 -}
  3.2525 -
  3.2526 -/* check if point is inside circle (overlap operator "&&") in SQL */
  3.2527 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap);
  3.2528 -Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) {
  3.2529 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2530 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  3.2531 -  PG_RETURN_BOOL(
  3.2532 -    pgl_distance(
  3.2533 -      point->lat, point->lon,
  3.2534 -      circle->center.lat, circle->center.lon
  3.2535 -    ) <= circle->radius
  3.2536 -  );
  3.2537 -}
  3.2538 -
  3.2539 -/* check if point is inside cluster (overlap operator "&&") in SQL */
  3.2540 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap);
  3.2541 -Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
  3.2542 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2543 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2544 -  bool retval;
  3.2545 -  /* points outside bounding circle are always assumed to be non-overlapping
  3.2546 -     (necessary for consistent table and index scans) */
  3.2547 -  if (
  3.2548 -    pgl_distance(
  3.2549 -      point->lat, point->lon,
  3.2550 -      cluster->bounding.center.lat, cluster->bounding.center.lon
  3.2551 -    ) > cluster->bounding.radius
  3.2552 -  ) retval = false;
  3.2553 -  else retval = pgl_point_in_cluster(point, cluster, false);
  3.2554 -  PG_FREE_IF_COPY(cluster, 1);
  3.2555 -  PG_RETURN_BOOL(retval);
  3.2556 -}
  3.2557 -
  3.2558 -/* check if point may be inside cluster (lossy overl. operator "&&+") in SQL */
  3.2559 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_may_overlap);
  3.2560 -Datum pgl_epoint_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  3.2561 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2562 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2563 -  bool retval = pgl_distance(
  3.2564 -    point->lat, point->lon,
  3.2565 -    cluster->bounding.center.lat, cluster->bounding.center.lon
  3.2566 -  ) <= cluster->bounding.radius;
  3.2567 -  PG_FREE_IF_COPY(cluster, 1);
  3.2568 -  PG_RETURN_BOOL(retval);
  3.2569 -}
  3.2570 -
  3.2571 -/* check if two boxes overlap (overlap operator "&&") in SQL */
  3.2572 -PG_FUNCTION_INFO_V1(pgl_ebox_overlap);
  3.2573 -Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) {
  3.2574 -  pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0);
  3.2575 -  pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1);
  3.2576 -  PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2));
  3.2577 -}
  3.2578 -
  3.2579 -/* check if box and circle may overlap (lossy overl. operator "&&+") in SQL */
  3.2580 -PG_FUNCTION_INFO_V1(pgl_ebox_ecircle_may_overlap);
  3.2581 -Datum pgl_ebox_ecircle_may_overlap(PG_FUNCTION_ARGS) {
  3.2582 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  3.2583 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  3.2584 -  PG_RETURN_BOOL(
  3.2585 -    pgl_estimate_point_box_distance(&circle->center, box) <= circle->radius
  3.2586 -  );
  3.2587 -}
  3.2588 -
  3.2589 -/* check if box and cluster may overlap (lossy overl. operator "&&+") in SQL */
  3.2590 -PG_FUNCTION_INFO_V1(pgl_ebox_ecluster_may_overlap);
  3.2591 -Datum pgl_ebox_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  3.2592 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  3.2593 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2594 -  bool retval = pgl_estimate_point_box_distance(
  3.2595 -    &cluster->bounding.center,
  3.2596 -    box
  3.2597 -  ) <= cluster->bounding.radius;
  3.2598 -  PG_FREE_IF_COPY(cluster, 1);
  3.2599 -  PG_RETURN_BOOL(retval);
  3.2600 -}
  3.2601 -
  3.2602 -/* check if two circles overlap (overlap operator "&&") in SQL */
  3.2603 -PG_FUNCTION_INFO_V1(pgl_ecircle_overlap);
  3.2604 -Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) {
  3.2605 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  3.2606 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  3.2607 -  PG_RETURN_BOOL(
  3.2608 -    pgl_distance(
  3.2609 -      circle1->center.lat, circle1->center.lon,
  3.2610 -      circle2->center.lat, circle2->center.lon
  3.2611 -    ) <= circle1->radius + circle2->radius
  3.2612 -  );
  3.2613 -}
  3.2614 -
  3.2615 -/* check if circle and cluster overlap (overlap operator "&&") in SQL */
  3.2616 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap);
  3.2617 -Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) {
  3.2618 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  3.2619 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2620 -  bool retval = (
  3.2621 -    pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius
  3.2622 -  );
  3.2623 -  PG_FREE_IF_COPY(cluster, 1);
  3.2624 -  PG_RETURN_BOOL(retval);
  3.2625 -}
  3.2626 -
  3.2627 -/* check if circle and cluster may overlap (l. ov. operator "&&+") in SQL */
  3.2628 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_may_overlap);
  3.2629 -Datum pgl_ecircle_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  3.2630 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  3.2631 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2632 -  bool retval = pgl_distance(
  3.2633 -    circle->center.lat, circle->center.lon,
  3.2634 -    cluster->bounding.center.lat, cluster->bounding.center.lon
  3.2635 -  ) <= circle->radius + cluster->bounding.radius;
  3.2636 -  PG_FREE_IF_COPY(cluster, 1);
  3.2637 -  PG_RETURN_BOOL(retval);
  3.2638 -}
  3.2639 -
  3.2640 -/* check if two clusters overlap (overlap operator "&&") in SQL */
  3.2641 -PG_FUNCTION_INFO_V1(pgl_ecluster_overlap);
  3.2642 -Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) {
  3.2643 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  3.2644 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2645 -  bool retval;
  3.2646 -  /* clusters with non-touching bounding circles are always assumed to be
  3.2647 -     non-overlapping (improves performance and is necessary for consistent
  3.2648 -     table and index scans) */
  3.2649 -  if (
  3.2650 -    pgl_distance(
  3.2651 -      cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  3.2652 -      cluster2->bounding.center.lat, cluster2->bounding.center.lon
  3.2653 -    ) > cluster1->bounding.radius + cluster2->bounding.radius
  3.2654 -  ) retval = false;
  3.2655 -  else retval = pgl_clusters_overlap(cluster1, cluster2);
  3.2656 -  PG_FREE_IF_COPY(cluster1, 0);
  3.2657 -  PG_FREE_IF_COPY(cluster2, 1);
  3.2658 -  PG_RETURN_BOOL(retval);
  3.2659 -}
  3.2660 -
  3.2661 -/* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */
  3.2662 -PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap);
  3.2663 -Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  3.2664 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  3.2665 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2666 -  bool retval = pgl_distance(
  3.2667 -    cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  3.2668 -    cluster2->bounding.center.lat, cluster2->bounding.center.lon
  3.2669 -  ) <= cluster1->bounding.radius + cluster2->bounding.radius;
  3.2670 -  PG_FREE_IF_COPY(cluster1, 0);
  3.2671 -  PG_FREE_IF_COPY(cluster2, 1);
  3.2672 -  PG_RETURN_BOOL(retval);
  3.2673 -}
  3.2674 -
  3.2675 -/* check if second cluster is in first cluster (cont. operator "@>) in SQL */
  3.2676 -PG_FUNCTION_INFO_V1(pgl_ecluster_contains);
  3.2677 -Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) {
  3.2678 -  pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  3.2679 -  pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2680 -  bool retval;
  3.2681 -  /* clusters with non-touching bounding circles are always assumed to be
  3.2682 -     non-overlapping (improves performance and is necessary for consistent
  3.2683 -     table and index scans) */
  3.2684 -  if (
  3.2685 -    pgl_distance(
  3.2686 -      outer->bounding.center.lat, outer->bounding.center.lon,
  3.2687 -      inner->bounding.center.lat, inner->bounding.center.lon
  3.2688 -    ) > outer->bounding.radius + inner->bounding.radius
  3.2689 -  ) retval = false;
  3.2690 -  else retval = pgl_cluster_in_cluster(outer, inner);
  3.2691 -  PG_FREE_IF_COPY(outer, 0);
  3.2692 -  PG_FREE_IF_COPY(inner, 1);
  3.2693 -  PG_RETURN_BOOL(retval);
  3.2694 -}
  3.2695 -
  3.2696 -/* calculate distance between two points ("<->" operator) in SQL */
  3.2697 -PG_FUNCTION_INFO_V1(pgl_epoint_distance);
  3.2698 -Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
  3.2699 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  3.2700 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  3.2701 -  PG_RETURN_FLOAT8(pgl_distance(
  3.2702 -    point1->lat, point1->lon, point2->lat, point2->lon
  3.2703 -  ));
  3.2704 -}
  3.2705 -
  3.2706 -/* calculate point to circle distance ("<->" operator) in SQL */
  3.2707 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance);
  3.2708 -Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) {
  3.2709 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2710 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  3.2711 -  double distance = pgl_distance(
  3.2712 -    point->lat, point->lon, circle->center.lat, circle->center.lon
  3.2713 -  ) - circle->radius;
  3.2714 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  3.2715 -}
  3.2716 -
  3.2717 -/* calculate point to cluster distance ("<->" operator) in SQL */
  3.2718 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance);
  3.2719 -Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) {
  3.2720 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  3.2721 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2722 -  double distance = pgl_point_cluster_distance(point, cluster);
  3.2723 -  PG_FREE_IF_COPY(cluster, 1);
  3.2724 -  PG_RETURN_FLOAT8(distance);
  3.2725 -}
  3.2726 -
  3.2727 -/* calculate distance between two circles ("<->" operator) in SQL */
  3.2728 -PG_FUNCTION_INFO_V1(pgl_ecircle_distance);
  3.2729 -Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) {
  3.2730 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  3.2731 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  3.2732 -  double distance = pgl_distance(
  3.2733 -    circle1->center.lat, circle1->center.lon,
  3.2734 -    circle2->center.lat, circle2->center.lon
  3.2735 -  ) - (circle1->radius + circle2->radius);
  3.2736 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  3.2737 -}
  3.2738 -
  3.2739 -/* calculate circle to cluster distance ("<->" operator) in SQL */
  3.2740 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance);
  3.2741 -Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) {
  3.2742 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  3.2743 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2744 -  double distance = (
  3.2745 -    pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius
  3.2746 -  );
  3.2747 -  PG_FREE_IF_COPY(cluster, 1);
  3.2748 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  3.2749 -}
  3.2750 -
  3.2751 -/* calculate distance between two clusters ("<->" operator) in SQL */
  3.2752 -PG_FUNCTION_INFO_V1(pgl_ecluster_distance);
  3.2753 -Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) {
  3.2754 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  3.2755 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2756 -  double retval = pgl_cluster_distance(cluster1, cluster2);
  3.2757 -  PG_FREE_IF_COPY(cluster1, 0);
  3.2758 -  PG_FREE_IF_COPY(cluster2, 1);
  3.2759 -  PG_RETURN_FLOAT8(retval);
  3.2760 -}
  3.2761 -
  3.2762 -PG_FUNCTION_INFO_V1(pgl_ecluster_monte_carlo_area);
  3.2763 -Datum pgl_ecluster_monte_carlo_area(PG_FUNCTION_ARGS) {
  3.2764 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  3.2765 -  int samples = PG_GETARG_INT32(1);
  3.2766 -  double retval = pgl_monte_carlo_area(cluster, samples);
  3.2767 -  PG_FREE_IF_COPY(cluster, 0);
  3.2768 -  PG_RETURN_FLOAT8(retval);
  3.2769 -}
  3.2770 -
  3.2771 -PG_FUNCTION_INFO_V1(pgl_ecluster_epoint_fair_distance);
  3.2772 -Datum pgl_ecluster_epoint_fair_distance(PG_FUNCTION_ARGS) {
  3.2773 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  3.2774 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(1);
  3.2775 -  int samples = PG_GETARG_INT32(2);
  3.2776 -  double retval = pgl_fair_distance(point, cluster, samples);
  3.2777 -  PG_FREE_IF_COPY(cluster, 0);
  3.2778 -  PG_RETURN_FLOAT8(retval);
  3.2779 -}
  3.2780 -
  3.2781 -
  3.2782 -/*-----------------------------------------------------------*
  3.2783 - *  B-tree comparison operators and index support functions  *
  3.2784 - *-----------------------------------------------------------*/
  3.2785 -
  3.2786 -/* macro for a B-tree operator (without detoasting) */
  3.2787 -#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \
  3.2788 -  PG_FUNCTION_INFO_V1(func); \
  3.2789 -  Datum func(PG_FUNCTION_ARGS) { \
  3.2790 -    type *a = (type *)PG_GETARG_POINTER(0); \
  3.2791 -    type *b = (type *)PG_GETARG_POINTER(1); \
  3.2792 -    PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \
  3.2793 -  }
  3.2794 -
  3.2795 -/* macro for a B-tree comparison function (without detoasting) */
  3.2796 -#define PGL_BTREE_CMP(func, type, cmpfunc) \
  3.2797 -  PG_FUNCTION_INFO_V1(func); \
  3.2798 -  Datum func(PG_FUNCTION_ARGS) { \
  3.2799 -    type *a = (type *)PG_GETARG_POINTER(0); \
  3.2800 -    type *b = (type *)PG_GETARG_POINTER(1); \
  3.2801 -    PG_RETURN_INT32(cmpfunc(a, b)); \
  3.2802 -  }
  3.2803 -
  3.2804 -/* macro for a B-tree operator (with detoasting) */
  3.2805 -#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \
  3.2806 -  PG_FUNCTION_INFO_V1(func); \
  3.2807 -  Datum func(PG_FUNCTION_ARGS) { \
  3.2808 -    bool res; \
  3.2809 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  3.2810 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  3.2811 -    res = cmpfunc(a, b) oper 0; \
  3.2812 -    PG_FREE_IF_COPY(a, 0); \
  3.2813 -    PG_FREE_IF_COPY(b, 1); \
  3.2814 -    PG_RETURN_BOOL(res); \
  3.2815 -  }
  3.2816 -
  3.2817 -/* macro for a B-tree comparison function (with detoasting) */
  3.2818 -#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \
  3.2819 -  PG_FUNCTION_INFO_V1(func); \
  3.2820 -  Datum func(PG_FUNCTION_ARGS) { \
  3.2821 -    int32_t res; \
  3.2822 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  3.2823 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  3.2824 -    res = cmpfunc(a, b); \
  3.2825 -    PG_FREE_IF_COPY(a, 0); \
  3.2826 -    PG_FREE_IF_COPY(b, 1); \
  3.2827 -    PG_RETURN_INT32(res); \
  3.2828 -  }
  3.2829 -
  3.2830 -/* B-tree operators and comparison function for point */
  3.2831 -PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <)
  3.2832 -PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=)
  3.2833 -PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==)
  3.2834 -PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=)
  3.2835 -PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=)
  3.2836 -PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >)
  3.2837 -PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp)
  3.2838 -
  3.2839 -/* B-tree operators and comparison function for box */
  3.2840 -PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <)
  3.2841 -PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=)
  3.2842 -PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==)
  3.2843 -PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=)
  3.2844 -PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=)
  3.2845 -PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >)
  3.2846 -PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp)
  3.2847 -
  3.2848 -/* B-tree operators and comparison function for circle */
  3.2849 -PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <)
  3.2850 -PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=)
  3.2851 -PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==)
  3.2852 -PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=)
  3.2853 -PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=)
  3.2854 -PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >)
  3.2855 -PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp)
  3.2856 -
  3.2857 -
  3.2858 -/*--------------------------------*
  3.2859 - *  GiST index support functions  *
  3.2860 - *--------------------------------*/
  3.2861 -
  3.2862 -/* GiST "consistent" support function */
  3.2863 -PG_FUNCTION_INFO_V1(pgl_gist_consistent);
  3.2864 -Datum pgl_gist_consistent(PG_FUNCTION_ARGS) {
  3.2865 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  3.2866 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  3.2867 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  3.2868 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  3.2869 -  /* demand recheck because index and query methods are lossy */
  3.2870 -  *recheck = true;
  3.2871 -  /* strategy number aliases for different operators using the same strategy */
  3.2872 -  strategy %= 100;
  3.2873 -  /* strategy number 11: equality of two points */
  3.2874 -  if (strategy == 11) {
  3.2875 -    /* query datum is another point */
  3.2876 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  3.2877 -    /* convert other point to key */
  3.2878 -    pgl_pointkey querykey;
  3.2879 -    pgl_point_to_key(query, querykey);
  3.2880 -    /* return true if both keys overlap */
  3.2881 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  3.2882 -  }
  3.2883 -  /* strategy number 13: equality of two circles */
  3.2884 -  if (strategy == 13) {
  3.2885 -    /* query datum is another circle */
  3.2886 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  3.2887 -    /* convert other circle to key */
  3.2888 -    pgl_areakey querykey;
  3.2889 -    pgl_circle_to_key(query, querykey);
  3.2890 -    /* return true if both keys overlap */
  3.2891 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  3.2892 -  }
  3.2893 -  /* for all remaining strategies, keys on empty objects produce no match */
  3.2894 -  /* (check necessary because query radius may be infinite) */
  3.2895 -  if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false);
  3.2896 -  /* strategy number 21: overlapping with point */
  3.2897 -  if (strategy == 21) {
  3.2898 -    /* query datum is a point */
  3.2899 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  3.2900 -    /* return true if estimated distance (allowed to be smaller than real
  3.2901 -       distance) between index key and point is zero */
  3.2902 -    PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0);
  3.2903 -  }
  3.2904 -  /* strategy number 22: (point) overlapping with box */
  3.2905 -  if (strategy == 22) {
  3.2906 -    /* query datum is a box */
  3.2907 -    pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1);
  3.2908 -    /* determine bounding box of indexed key */
  3.2909 -    pgl_box keybox;
  3.2910 -    pgl_key_to_box(key, &keybox);
  3.2911 -    /* return true if query box overlaps with bounding box of indexed key */
  3.2912 -    PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox));
  3.2913 -  }
  3.2914 -  /* strategy number 23: overlapping with circle */
  3.2915 -  if (strategy == 23) {
  3.2916 -    /* query datum is a circle */
  3.2917 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  3.2918 -    /* return true if estimated distance (allowed to be smaller than real
  3.2919 -       distance) between index key and circle center is smaller than radius */
  3.2920 -    PG_RETURN_BOOL(
  3.2921 -      pgl_estimate_key_distance(key, &(query->center)) <= query->radius
  3.2922 -    );
  3.2923 -  }
  3.2924 -  /* strategy number 24: overlapping with cluster */
  3.2925 -  if (strategy == 24) {
  3.2926 -    bool retval;  /* return value */
  3.2927 -    /* query datum is a cluster */
  3.2928 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.2929 -    /* return true if estimated distance (allowed to be smaller than real
  3.2930 -       distance) between index key and circle center is smaller than radius */
  3.2931 -    retval = (
  3.2932 -      pgl_estimate_key_distance(key, &(query->bounding.center)) <=
  3.2933 -      query->bounding.radius
  3.2934 -    );
  3.2935 -    PG_FREE_IF_COPY(query, 1);  /* free detoasted cluster (if copy) */
  3.2936 -    PG_RETURN_BOOL(retval);
  3.2937 -  }
  3.2938 -  /* throw error for any unknown strategy number */
  3.2939 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  3.2940 -}
  3.2941 -
  3.2942 -/* GiST "union" support function */
  3.2943 -PG_FUNCTION_INFO_V1(pgl_gist_union);
  3.2944 -Datum pgl_gist_union(PG_FUNCTION_ARGS) {
  3.2945 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  3.2946 -  pgl_keyptr out;  /* return value (to be palloc'ed) */
  3.2947 -  int i;
  3.2948 -  /* determine key size */
  3.2949 -  size_t keysize = PGL_KEY_IS_AREAKEY(
  3.2950 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key)
  3.2951 -  ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey);
  3.2952 -  /* begin with first key as result */
  3.2953 -  out = palloc(keysize);
  3.2954 -  memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize);
  3.2955 -  /* unite current result with second, third, etc. key */
  3.2956 -  for (i=1; i<entryvec->n; i++) {
  3.2957 -    pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key));
  3.2958 -  }
  3.2959 -  /* return result */
  3.2960 -  PG_RETURN_POINTER(out);
  3.2961 -}
  3.2962 -
  3.2963 -/* GiST "compress" support function for indicis on points */
  3.2964 -PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint);
  3.2965 -Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) {
  3.2966 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  3.2967 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  3.2968 -  /* only transform new leaves */
  3.2969 -  if (entry->leafkey) {
  3.2970 -    /* get point to be transformed */
  3.2971 -    pgl_point *point = (pgl_point *)DatumGetPointer(entry->key);
  3.2972 -    /* allocate memory for key */
  3.2973 -    pgl_keyptr key = palloc(sizeof(pgl_pointkey));
  3.2974 -    /* transform point to key */
  3.2975 -    pgl_point_to_key(point, key);
  3.2976 -    /* create new GISTENTRY structure as return value */
  3.2977 -    retval = palloc(sizeof(GISTENTRY));
  3.2978 -    gistentryinit(
  3.2979 -      *retval, PointerGetDatum(key),
  3.2980 -      entry->rel, entry->page, entry->offset, FALSE
  3.2981 -    );
  3.2982 -  } else {
  3.2983 -    /* inner nodes have already been transformed */
  3.2984 -    retval = entry;
  3.2985 -  }
  3.2986 -  /* return pointer to old or new GISTENTRY structure */
  3.2987 -  PG_RETURN_POINTER(retval);
  3.2988 -}
  3.2989 -
  3.2990 -/* GiST "compress" support function for indicis on circles */
  3.2991 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle);
  3.2992 -Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) {
  3.2993 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  3.2994 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  3.2995 -  /* only transform new leaves */
  3.2996 -  if (entry->leafkey) {
  3.2997 -    /* get circle to be transformed */
  3.2998 -    pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key);
  3.2999 -    /* allocate memory for key */
  3.3000 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  3.3001 -    /* transform circle to key */
  3.3002 -    pgl_circle_to_key(circle, key);
  3.3003 -    /* create new GISTENTRY structure as return value */
  3.3004 -    retval = palloc(sizeof(GISTENTRY));
  3.3005 -    gistentryinit(
  3.3006 -      *retval, PointerGetDatum(key),
  3.3007 -      entry->rel, entry->page, entry->offset, FALSE
  3.3008 -    );
  3.3009 -  } else {
  3.3010 -    /* inner nodes have already been transformed */
  3.3011 -    retval = entry;
  3.3012 -  }
  3.3013 -  /* return pointer to old or new GISTENTRY structure */
  3.3014 -  PG_RETURN_POINTER(retval);
  3.3015 -}
  3.3016 -
  3.3017 -/* GiST "compress" support function for indices on clusters */
  3.3018 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster);
  3.3019 -Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) {
  3.3020 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  3.3021 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  3.3022 -  /* only transform new leaves */
  3.3023 -  if (entry->leafkey) {
  3.3024 -    /* get cluster to be transformed (detoasting necessary!) */
  3.3025 -    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key);
  3.3026 -    /* allocate memory for key */
  3.3027 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  3.3028 -    /* transform cluster to key */
  3.3029 -    pgl_circle_to_key(&(cluster->bounding), key);
  3.3030 -    /* create new GISTENTRY structure as return value */
  3.3031 -    retval = palloc(sizeof(GISTENTRY));
  3.3032 -    gistentryinit(
  3.3033 -      *retval, PointerGetDatum(key),
  3.3034 -      entry->rel, entry->page, entry->offset, FALSE
  3.3035 -    );
  3.3036 -    /* free detoasted datum */
  3.3037 -    if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster);
  3.3038 -  } else {
  3.3039 -    /* inner nodes have already been transformed */
  3.3040 -    retval = entry;
  3.3041 -  }
  3.3042 -  /* return pointer to old or new GISTENTRY structure */
  3.3043 -  PG_RETURN_POINTER(retval);
  3.3044 -}
  3.3045 -
  3.3046 -/* GiST "decompress" support function for indices */
  3.3047 -PG_FUNCTION_INFO_V1(pgl_gist_decompress);
  3.3048 -Datum pgl_gist_decompress(PG_FUNCTION_ARGS) {
  3.3049 -  /* return passed pointer without transformation */
  3.3050 -  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
  3.3051 -}
  3.3052 -
  3.3053 -/* GiST "penalty" support function */
  3.3054 -PG_FUNCTION_INFO_V1(pgl_gist_penalty);
  3.3055 -Datum pgl_gist_penalty(PG_FUNCTION_ARGS) {
  3.3056 -  GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0);
  3.3057 -  GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1);
  3.3058 -  float *penalty = (float *)PG_GETARG_POINTER(2);
  3.3059 -  /* get original key and key to insert */
  3.3060 -  pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key);
  3.3061 -  pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key);
  3.3062 -  /* copy original key */
  3.3063 -  union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key;
  3.3064 -  if (PGL_KEY_IS_AREAKEY(orig)) {
  3.3065 -    memcpy(union_key.areakey, orig, sizeof(union_key.areakey));
  3.3066 -  } else {
  3.3067 -    memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey));
  3.3068 -  }
  3.3069 -  /* calculate union of both keys */
  3.3070 -  pgl_unite_keys((pgl_keyptr)&union_key, new);
  3.3071 -  /* penalty equal to reduction of key length (logarithm of added area) */
  3.3072 -  /* (return value by setting referenced value and returning pointer) */
  3.3073 -  *penalty = (
  3.3074 -    PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key)
  3.3075 -  );
  3.3076 -  PG_RETURN_POINTER(penalty);
  3.3077 -}
  3.3078 -
  3.3079 -/* GiST "picksplit" support function */
  3.3080 -PG_FUNCTION_INFO_V1(pgl_gist_picksplit);
  3.3081 -Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) {
  3.3082 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  3.3083 -  GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1);
  3.3084 -  OffsetNumber i;  /* between FirstOffsetNumber and entryvec->n (inclusive) */
  3.3085 -  union {
  3.3086 -    pgl_pointkey pointkey;
  3.3087 -    pgl_areakey areakey;
  3.3088 -  } union_all;  /* union of all keys (to be calculated from scratch)
  3.3089 -                   (later cut in half) */
  3.3090 -  int is_areakey = PGL_KEY_IS_AREAKEY(
  3.3091 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key)
  3.3092 -  );
  3.3093 -  int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey);
  3.3094 -  pgl_keyptr unionL = palloc(keysize);  /* union of keys that go left */
  3.3095 -  pgl_keyptr unionR = palloc(keysize);  /* union of keys that go right */
  3.3096 -  pgl_keyptr key;  /* current key to be processed */
  3.3097 -  /* allocate memory for array of left and right keys, set counts to zero */
  3.3098 -  v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  3.3099 -  v->spl_nleft = 0;
  3.3100 -  v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  3.3101 -  v->spl_nright = 0;
  3.3102 -  /* calculate union of all keys from scratch */
  3.3103 -  memcpy(
  3.3104 -    (pgl_keyptr)&union_all,
  3.3105 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key),
  3.3106 -    keysize
  3.3107 -  );
  3.3108 -  for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) {
  3.3109 -    pgl_unite_keys(
  3.3110 -      (pgl_keyptr)&union_all,
  3.3111 -      (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)
  3.3112 -    );
  3.3113 -  }
  3.3114 -  /* check if trivial split is necessary due to exhausted key length */
  3.3115 -  /* (Note: keys for empty objects must have node depth set to maximum) */
  3.3116 -  if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == (
  3.3117 -    is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH
  3.3118 -  )) {
  3.3119 -    /* half of all keys go left */
  3.3120 -    for (
  3.3121 -      i=FirstOffsetNumber;
  3.3122 -      i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  3.3123 -      i=OffsetNumberNext(i)
  3.3124 -    ) {
  3.3125 -      /* pointer to current key */
  3.3126 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  3.3127 -      /* update unionL */
  3.3128 -      /* check if key is first key that goes left */
  3.3129 -      if (!v->spl_nleft) {
  3.3130 -        /* first key that goes left is just copied to unionL */
  3.3131 -        memcpy(unionL, key, keysize);
  3.3132 -      } else {
  3.3133 -        /* unite current value and next key */
  3.3134 -        pgl_unite_keys(unionL, key);
  3.3135 -      }
  3.3136 -      /* append offset number to list of keys that go left */
  3.3137 -      v->spl_left[v->spl_nleft++] = i;
  3.3138 -    }
  3.3139 -    /* other half goes right */
  3.3140 -    for (
  3.3141 -      i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  3.3142 -      i<entryvec->n;
  3.3143 -      i=OffsetNumberNext(i)
  3.3144 -    ) {
  3.3145 -      /* pointer to current key */
  3.3146 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  3.3147 -      /* update unionR */
  3.3148 -      /* check if key is first key that goes right */
  3.3149 -      if (!v->spl_nright) {
  3.3150 -        /* first key that goes right is just copied to unionR */
  3.3151 -        memcpy(unionR, key, keysize);
  3.3152 -      } else {
  3.3153 -        /* unite current value and next key */
  3.3154 -        pgl_unite_keys(unionR, key);
  3.3155 -      }
  3.3156 -      /* append offset number to list of keys that go right */
  3.3157 -      v->spl_right[v->spl_nright++] = i;
  3.3158 -    }
  3.3159 -  }
  3.3160 -  /* otherwise, a non-trivial split is possible */
  3.3161 -  else {
  3.3162 -    /* cut covered area in half */
  3.3163 -    /* (union_all then refers to area of keys that go left) */
  3.3164 -    /* check if union of all keys covers empty and non-empty objects */
  3.3165 -    if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) {
  3.3166 -      /* if yes, split into empty and non-empty objects */
  3.3167 -      pgl_key_set_empty((pgl_keyptr)&union_all);
  3.3168 -    } else {
  3.3169 -      /* otherwise split by next bit */
  3.3170 -      ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++;
  3.3171 -      /* NOTE: type bit conserved */
  3.3172 -    }
  3.3173 -    /* determine for each key if it goes left or right */
  3.3174 -    for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) {
  3.3175 -      /* pointer to current key */
  3.3176 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  3.3177 -      /* keys within one half of the area go left */
  3.3178 -      if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) {
  3.3179 -        /* update unionL */
  3.3180 -        /* check if key is first key that goes left */
  3.3181 -        if (!v->spl_nleft) {
  3.3182 -          /* first key that goes left is just copied to unionL */
  3.3183 -          memcpy(unionL, key, keysize);
  3.3184 -        } else {
  3.3185 -          /* unite current value of unionL and processed key */
  3.3186 -          pgl_unite_keys(unionL, key);
  3.3187 -        }
  3.3188 -        /* append offset number to list of keys that go left */
  3.3189 -        v->spl_left[v->spl_nleft++] = i;
  3.3190 -      }
  3.3191 -      /* the other keys go right */
  3.3192 -      else {
  3.3193 -        /* update unionR */
  3.3194 -        /* check if key is first key that goes right */
  3.3195 -        if (!v->spl_nright) {
  3.3196 -          /* first key that goes right is just copied to unionR */
  3.3197 -          memcpy(unionR, key, keysize);
  3.3198 -        } else {
  3.3199 -          /* unite current value of unionR and processed key */
  3.3200 -          pgl_unite_keys(unionR, key);
  3.3201 -        }
  3.3202 -        /* append offset number to list of keys that go right */
  3.3203 -        v->spl_right[v->spl_nright++] = i;
  3.3204 -      }
  3.3205 -    }
  3.3206 -  }
  3.3207 -  /* store unions in return value */
  3.3208 -  v->spl_ldatum = PointerGetDatum(unionL);
  3.3209 -  v->spl_rdatum = PointerGetDatum(unionR);
  3.3210 -  /* return all results */
  3.3211 -  PG_RETURN_POINTER(v);
  3.3212 -}
  3.3213 -
  3.3214 -/* GiST "same"/"equal" support function */
  3.3215 -PG_FUNCTION_INFO_V1(pgl_gist_same);
  3.3216 -Datum pgl_gist_same(PG_FUNCTION_ARGS) {
  3.3217 -  pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0);
  3.3218 -  pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1);
  3.3219 -  bool *boolptr = (bool *)PG_GETARG_POINTER(2);
  3.3220 -  /* two keys are equal if they are binary equal */
  3.3221 -  /* (return result by setting referenced boolean and returning pointer) */
  3.3222 -  *boolptr = !memcmp(
  3.3223 -    key1,
  3.3224 -    key2,
  3.3225 -    PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey)
  3.3226 -  );
  3.3227 -  PG_RETURN_POINTER(boolptr);
  3.3228 -}
  3.3229 -
  3.3230 -/* GiST "distance" support function */
  3.3231 -PG_FUNCTION_INFO_V1(pgl_gist_distance);
  3.3232 -Datum pgl_gist_distance(PG_FUNCTION_ARGS) {
  3.3233 -  GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
  3.3234 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  3.3235 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  3.3236 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  3.3237 -  double distance;  /* return value */
  3.3238 -  /* demand recheck because distance is just an estimation */
  3.3239 -  /* (real distance may be bigger) */
  3.3240 -  *recheck = true;
  3.3241 -  /* strategy number aliases for different operators using the same strategy */
  3.3242 -  strategy %= 100;
  3.3243 -  /* strategy number 31: distance to point */
  3.3244 -  if (strategy == 31) {
  3.3245 -    /* query datum is a point */
  3.3246 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  3.3247 -    /* use pgl_estimate_pointkey_distance() function to compute result */
  3.3248 -    distance = pgl_estimate_key_distance(key, query);
  3.3249 -    /* avoid infinity (reserved!) */
  3.3250 -    if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  3.3251 -    /* return result */
  3.3252 -    PG_RETURN_FLOAT8(distance);
  3.3253 -  }
  3.3254 -  /* strategy number 33: distance to circle */
  3.3255 -  if (strategy == 33) {
  3.3256 -    /* query datum is a circle */
  3.3257 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  3.3258 -    /* estimate distance to circle center and substract circle radius */
  3.3259 -    distance = (
  3.3260 -      pgl_estimate_key_distance(key, &(query->center)) - query->radius
  3.3261 -    );
  3.3262 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  3.3263 -    if (distance <= 0) distance = 0;
  3.3264 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  3.3265 -    /* return result */
  3.3266 -    PG_RETURN_FLOAT8(distance);
  3.3267 -  }
  3.3268 -  /* strategy number 34: distance to cluster */
  3.3269 -  if (strategy == 34) {
  3.3270 -    /* query datum is a cluster */
  3.3271 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  3.3272 -    /* estimate distance to bounding center and substract bounding radius */
  3.3273 -    distance = (
  3.3274 -      pgl_estimate_key_distance(key, &(query->bounding.center)) -
  3.3275 -      query->bounding.radius
  3.3276 -    );
  3.3277 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  3.3278 -    if (distance <= 0) distance = 0;
  3.3279 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  3.3280 -    /* free detoasted cluster (if copy) */
  3.3281 -    PG_FREE_IF_COPY(query, 1);
  3.3282 -    /* return result */
  3.3283 -    PG_RETURN_FLOAT8(distance);
  3.3284 -  }
  3.3285 -  /* throw error for any unknown strategy number */
  3.3286 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  3.3287 -}
  3.3288 -

Impressum / About Us