pgLatLon

changeset 9:dc8da756b761

Changed version to 0.3
author jbe
date Fri Sep 02 14:04:04 2016 +0200 (2016-09-02)
parents cf73c01d4de0
children 684a78d2f9f0
files GNUmakefile latlon--0.2.sql latlon--0.3.sql latlon-v0002.c latlon-v0003.c latlon.control
line diff
     1.1 --- a/GNUmakefile	Mon Aug 22 22:10:44 2016 +0200
     1.2 +++ b/GNUmakefile	Fri Sep 02 14:04:04 2016 +0200
     1.3 @@ -1,6 +1,6 @@
     1.4  EXTENSION = latlon
     1.5 -DATA = latlon--0.1--0.2.sql latlon--0.2.sql
     1.6 -MODULES = latlon-v0002
     1.7 +DATA = latlon--0.1--0.2.sql latlon--0.3.sql
     1.8 +MODULES = latlon-v0003
     1.9  
    1.10  PG_CONFIG = pg_config
    1.11  PGXS := $(shell $(PG_CONFIG) --pgxs)
     2.1 --- a/latlon--0.2.sql	Mon Aug 22 22:10:44 2016 +0200
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,1205 +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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', '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-v0002', 'pgl_epoint_ecluster_overlap';
   2.737 -
   2.738 -CREATE FUNCTION ebox_overlap_proc(ebox, ebox)
   2.739 -  RETURNS boolean
   2.740 -  LANGUAGE C IMMUTABLE STRICT
   2.741 -  AS '$libdir/latlon-v0002', 'pgl_ebox_overlap';
   2.742 -
   2.743 -CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   2.744 -  RETURNS boolean
   2.745 -  LANGUAGE C IMMUTABLE STRICT
   2.746 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_overlap';
   2.747 -
   2.748 -CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   2.749 -  RETURNS boolean
   2.750 -  LANGUAGE C IMMUTABLE STRICT
   2.751 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_ecluster_overlap';
   2.752 -
   2.753 -CREATE FUNCTION epoint_distance_proc(epoint, epoint)
   2.754 -  RETURNS float8
   2.755 -  LANGUAGE C IMMUTABLE STRICT
   2.756 -  AS '$libdir/latlon-v0002', 'pgl_epoint_distance';
   2.757 -
   2.758 -CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   2.759 -  RETURNS float8
   2.760 -  LANGUAGE C IMMUTABLE STRICT
   2.761 -  AS '$libdir/latlon-v0002', 'pgl_epoint_ecircle_distance';
   2.762 -
   2.763 -CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   2.764 -  RETURNS float8
   2.765 -  LANGUAGE C IMMUTABLE STRICT
   2.766 -  AS '$libdir/latlon-v0002', 'pgl_epoint_ecluster_distance';
   2.767 -
   2.768 -CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   2.769 -  RETURNS float8
   2.770 -  LANGUAGE C IMMUTABLE STRICT
   2.771 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_distance';
   2.772 -
   2.773 -CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   2.774 -  RETURNS float8
   2.775 -  LANGUAGE C IMMUTABLE STRICT
   2.776 -  AS '$libdir/latlon-v0002', 'pgl_ecircle_ecluster_distance';
   2.777 -
   2.778 -CREATE OPERATOR && (
   2.779 -  leftarg = epoint,
   2.780 -  rightarg = ebox,
   2.781 -  procedure = epoint_ebox_overlap_proc,
   2.782 -  commutator = &&,
   2.783 -  restrict = areasel,
   2.784 -  join = areajoinsel
   2.785 -);
   2.786 -
   2.787 -CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint)
   2.788 -  RETURNS boolean
   2.789 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   2.790 -
   2.791 -CREATE OPERATOR && (
   2.792 -  leftarg = ebox,
   2.793 -  rightarg = epoint,
   2.794 -  procedure = epoint_ebox_overlap_commutator,
   2.795 -  commutator = &&,
   2.796 -  restrict = areasel,
   2.797 -  join = areajoinsel
   2.798 -);
   2.799 -
   2.800 -CREATE OPERATOR && (
   2.801 -  leftarg = epoint,
   2.802 -  rightarg = ecircle,
   2.803 -  procedure = epoint_ecircle_overlap_proc,
   2.804 -  commutator = &&,
   2.805 -  restrict = areasel,
   2.806 -  join = areajoinsel
   2.807 -);
   2.808 -
   2.809 -CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint)
   2.810 -  RETURNS boolean
   2.811 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   2.812 -
   2.813 -CREATE OPERATOR && (
   2.814 -  leftarg = ecircle,
   2.815 -  rightarg = epoint,
   2.816 -  procedure = epoint_ecircle_overlap_commutator,
   2.817 -  commutator = &&,
   2.818 -  restrict = areasel,
   2.819 -  join = areajoinsel
   2.820 -);
   2.821 -
   2.822 -CREATE OPERATOR && (
   2.823 -  leftarg = epoint,
   2.824 -  rightarg = ecluster,
   2.825 -  procedure = epoint_ecluster_overlap_proc,
   2.826 -  commutator = &&,
   2.827 -  restrict = areasel,
   2.828 -  join = areajoinsel
   2.829 -);
   2.830 -
   2.831 -CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint)
   2.832 -  RETURNS boolean
   2.833 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   2.834 -
   2.835 -CREATE OPERATOR && (
   2.836 -  leftarg = ecluster,
   2.837 -  rightarg = epoint,
   2.838 -  procedure = epoint_ecluster_overlap_commutator,
   2.839 -  commutator = &&,
   2.840 -  restrict = areasel,
   2.841 -  join = areajoinsel
   2.842 -);
   2.843 -
   2.844 -CREATE OPERATOR && (
   2.845 -  leftarg = ebox,
   2.846 -  rightarg = ebox,
   2.847 -  procedure = ebox_overlap_proc,
   2.848 -  commutator = &&,
   2.849 -  restrict = areasel,
   2.850 -  join = areajoinsel
   2.851 -);
   2.852 -
   2.853 -CREATE OPERATOR && (
   2.854 -  leftarg = ecircle,
   2.855 -  rightarg = ecircle,
   2.856 -  procedure = ecircle_overlap_proc,
   2.857 -  commutator = &&,
   2.858 -  restrict = areasel,
   2.859 -  join = areajoinsel
   2.860 -);
   2.861 -
   2.862 -CREATE OPERATOR && (
   2.863 -  leftarg = ecircle,
   2.864 -  rightarg = ecluster,
   2.865 -  procedure = ecircle_ecluster_overlap_proc,
   2.866 -  commutator = &&,
   2.867 -  restrict = areasel,
   2.868 -  join = areajoinsel
   2.869 -);
   2.870 -
   2.871 -CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle)
   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 = ecircle,
   2.878 -  procedure = ecircle_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 = epoint,
   2.886 -  rightarg = epoint,
   2.887 -  procedure = epoint_distance_proc,
   2.888 -  commutator = <->
   2.889 -);
   2.890 -
   2.891 -CREATE OPERATOR <-> (
   2.892 -  leftarg = epoint,
   2.893 -  rightarg = ecircle,
   2.894 -  procedure = epoint_ecircle_distance_proc,
   2.895 -  commutator = <->
   2.896 -);
   2.897 -
   2.898 -CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint)
   2.899 -  RETURNS float8
   2.900 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
   2.901 -
   2.902 -CREATE OPERATOR <-> (
   2.903 -  leftarg = ecircle,
   2.904 -  rightarg = epoint,
   2.905 -  procedure = epoint_ecircle_distance_commutator,
   2.906 -  commutator = <->
   2.907 -);
   2.908 -
   2.909 -CREATE OPERATOR <-> (
   2.910 -  leftarg = epoint,
   2.911 -  rightarg = ecluster,
   2.912 -  procedure = epoint_ecluster_distance_proc,
   2.913 -  commutator = <->
   2.914 -);
   2.915 -
   2.916 -CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint)
   2.917 -  RETURNS float8
   2.918 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
   2.919 -
   2.920 -CREATE OPERATOR <-> (
   2.921 -  leftarg = ecluster,
   2.922 -  rightarg = epoint,
   2.923 -  procedure = epoint_ecluster_distance_commutator,
   2.924 -  commutator = <->
   2.925 -);
   2.926 -
   2.927 -CREATE OPERATOR <-> (
   2.928 -  leftarg = ecircle,
   2.929 -  rightarg = ecircle,
   2.930 -  procedure = ecircle_distance_proc,
   2.931 -  commutator = <->
   2.932 -);
   2.933 -
   2.934 -CREATE OPERATOR <-> (
   2.935 -  leftarg = ecircle,
   2.936 -  rightarg = ecluster,
   2.937 -  procedure = ecircle_ecluster_distance_proc,
   2.938 -  commutator = <->
   2.939 -);
   2.940 -
   2.941 -CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle)
   2.942 -  RETURNS float8
   2.943 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
   2.944 -
   2.945 -CREATE OPERATOR <-> (
   2.946 -  leftarg = ecluster,
   2.947 -  rightarg = ecircle,
   2.948 -  procedure = ecircle_ecluster_distance_commutator,
   2.949 -  commutator = <->
   2.950 -);
   2.951 -
   2.952 -
   2.953 -----------------
   2.954 --- GiST index --
   2.955 -----------------
   2.956 -
   2.957 -CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
   2.958 -  RETURNS boolean
   2.959 -  LANGUAGE C STRICT
   2.960 -  AS '$libdir/latlon-v0002', 'pgl_gist_consistent';
   2.961 -
   2.962 -CREATE FUNCTION pgl_gist_union(internal, internal)
   2.963 -  RETURNS internal
   2.964 -  LANGUAGE C STRICT
   2.965 -  AS '$libdir/latlon-v0002', 'pgl_gist_union';
   2.966 -
   2.967 -CREATE FUNCTION pgl_gist_compress_epoint(internal)
   2.968 -  RETURNS internal
   2.969 -  LANGUAGE C STRICT
   2.970 -  AS '$libdir/latlon-v0002', 'pgl_gist_compress_epoint';
   2.971 -
   2.972 -CREATE FUNCTION pgl_gist_compress_ecircle(internal)
   2.973 -  RETURNS internal
   2.974 -  LANGUAGE C STRICT
   2.975 -  AS '$libdir/latlon-v0002', 'pgl_gist_compress_ecircle';
   2.976 -
   2.977 -CREATE FUNCTION pgl_gist_compress_ecluster(internal)
   2.978 -  RETURNS internal
   2.979 -  LANGUAGE C STRICT
   2.980 -  AS '$libdir/latlon-v0002', 'pgl_gist_compress_ecluster';
   2.981 -
   2.982 -CREATE FUNCTION pgl_gist_decompress(internal)
   2.983 -  RETURNS internal
   2.984 -  LANGUAGE C STRICT
   2.985 -  AS '$libdir/latlon-v0002', 'pgl_gist_decompress';
   2.986 -
   2.987 -CREATE FUNCTION pgl_gist_penalty(internal, internal, internal)
   2.988 -  RETURNS internal
   2.989 -  LANGUAGE C STRICT
   2.990 -  AS '$libdir/latlon-v0002', 'pgl_gist_penalty';
   2.991 -
   2.992 -CREATE FUNCTION pgl_gist_picksplit(internal, internal)
   2.993 -  RETURNS internal
   2.994 -  LANGUAGE C STRICT
   2.995 -  AS '$libdir/latlon-v0002', 'pgl_gist_picksplit';
   2.996 -
   2.997 -CREATE FUNCTION pgl_gist_same(internal, internal, internal)
   2.998 -  RETURNS internal
   2.999 -  LANGUAGE C STRICT
  2.1000 -  AS '$libdir/latlon-v0002', 'pgl_gist_same';
  2.1001 -
  2.1002 -CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
  2.1003 -  RETURNS internal
  2.1004 -  LANGUAGE C STRICT
  2.1005 -  AS '$libdir/latlon-v0002', 'pgl_gist_distance';
  2.1006 -
  2.1007 -CREATE OPERATOR CLASS epoint_ops
  2.1008 -  DEFAULT FOR TYPE epoint USING gist AS
  2.1009 -  OPERATOR 11 = ,
  2.1010 -  OPERATOR 22 && (epoint, ebox),
  2.1011 -  OPERATOR 23 && (epoint, ecircle),
  2.1012 -  OPERATOR 24 && (epoint, ecluster),
  2.1013 -  OPERATOR 31 <-> (epoint, epoint) FOR ORDER BY float_ops,
  2.1014 -  OPERATOR 33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
  2.1015 -  OPERATOR 34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
  2.1016 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  2.1017 -  FUNCTION 2 pgl_gist_union(internal, internal),
  2.1018 -  FUNCTION 3 pgl_gist_compress_epoint(internal),
  2.1019 -  FUNCTION 4 pgl_gist_decompress(internal),
  2.1020 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  2.1021 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  2.1022 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  2.1023 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  2.1024 -  STORAGE ekey_point;
  2.1025 -
  2.1026 -CREATE OPERATOR CLASS ecircle_ops
  2.1027 -  DEFAULT FOR TYPE ecircle USING gist AS
  2.1028 -  OPERATOR 13 = ,
  2.1029 -  OPERATOR 21 && (ecircle, epoint),
  2.1030 -  OPERATOR 23 && (ecircle, ecircle),
  2.1031 -  OPERATOR 24 && (ecircle, ecluster),
  2.1032 -  OPERATOR 31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
  2.1033 -  OPERATOR 33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
  2.1034 -  OPERATOR 34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
  2.1035 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  2.1036 -  FUNCTION 2 pgl_gist_union(internal, internal),
  2.1037 -  FUNCTION 3 pgl_gist_compress_ecircle(internal),
  2.1038 -  FUNCTION 4 pgl_gist_decompress(internal),
  2.1039 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  2.1040 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  2.1041 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  2.1042 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  2.1043 -  STORAGE ekey_area;
  2.1044 -
  2.1045 -CREATE OPERATOR CLASS ecluster_ops
  2.1046 -  DEFAULT FOR TYPE ecluster USING gist AS
  2.1047 -  OPERATOR 21 && (ecluster, epoint),
  2.1048 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  2.1049 -  FUNCTION 2 pgl_gist_union(internal, internal),
  2.1050 -  FUNCTION 3 pgl_gist_compress_ecluster(internal),
  2.1051 -  FUNCTION 4 pgl_gist_decompress(internal),
  2.1052 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  2.1053 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  2.1054 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  2.1055 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  2.1056 -  STORAGE ekey_area;
  2.1057 -
  2.1058 -
  2.1059 ----------------------
  2.1060 --- alias functions --
  2.1061 ----------------------
  2.1062 -
  2.1063 -CREATE FUNCTION distance(epoint, epoint)
  2.1064 -  RETURNS float8
  2.1065 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  2.1066 -
  2.1067 -CREATE FUNCTION distance(ecluster, epoint)
  2.1068 -  RETURNS float8
  2.1069 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  2.1070 -
  2.1071 -CREATE FUNCTION distance_within(epoint, epoint, float8)
  2.1072 -  RETURNS boolean
  2.1073 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  2.1074 -
  2.1075 -CREATE FUNCTION distance_within(ecluster, epoint, float8)
  2.1076 -  RETURNS boolean
  2.1077 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  2.1078 -
  2.1079 -
  2.1080 ---------------------------------
  2.1081 --- other data storage formats --
  2.1082 ---------------------------------
  2.1083 -
  2.1084 -CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint_lonlat')
  2.1085 -  RETURNS epoint
  2.1086 -  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
  2.1087 -    DECLARE
  2.1088 -      "result" epoint;
  2.1089 -    BEGIN
  2.1090 -      IF $3 = 'epoint_lonlat' THEN
  2.1091 -        -- avoid dynamic command execution for better performance
  2.1092 -        RETURN epoint($2, $1);
  2.1093 -      END IF;
  2.1094 -      IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN
  2.1095 -        -- avoid dynamic command execution for better performance
  2.1096 -        RETURN epoint($1, $2);
  2.1097 -      END IF;
  2.1098 -      EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2;
  2.1099 -      RETURN "result";
  2.1100 -    END;
  2.1101 -  $$;
  2.1102 -
  2.1103 -CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat')
  2.1104 -  RETURNS epoint
  2.1105 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  2.1106 -    SELECT CASE
  2.1107 -    WHEN $1->>'type' = 'Point' THEN
  2.1108 -      coords_to_epoint(
  2.1109 -        ($1->'coordinates'->>1)::float8,
  2.1110 -        ($1->'coordinates'->>0)::float8,
  2.1111 -        $2
  2.1112 -      )
  2.1113 -    WHEN $1->>'type' = 'Feature' THEN
  2.1114 -      GeoJSON_to_epoint($1->'geometry', $2)
  2.1115 -    ELSE
  2.1116 -      NULL
  2.1117 -    END
  2.1118 -  $$;
  2.1119 -
  2.1120 -CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat')
  2.1121 -  RETURNS ecluster
  2.1122 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  2.1123 -    SELECT CASE $1->>'type'
  2.1124 -    WHEN 'Point' THEN
  2.1125 -      coords_to_epoint(
  2.1126 -        ($1->'coordinates'->>1)::float8,
  2.1127 -        ($1->'coordinates'->>0)::float8,
  2.1128 -        $2
  2.1129 -      )::ecluster
  2.1130 -    WHEN 'MultiPoint' THEN
  2.1131 -      ( SELECT ecluster_create_multipoint(array_agg(
  2.1132 -          coords_to_epoint(
  2.1133 -            ("coord"->>1)::float8,
  2.1134 -            ("coord"->>0)::float8,
  2.1135 -            $2
  2.1136 -          )
  2.1137 -        ))
  2.1138 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  2.1139 -      )
  2.1140 -    WHEN 'LineString' THEN
  2.1141 -      ( SELECT ecluster_create_path(array_agg(
  2.1142 -          coords_to_epoint(
  2.1143 -            ("coord"->>1)::float8,
  2.1144 -            ("coord"->>0)::float8,
  2.1145 -            $2
  2.1146 -          )
  2.1147 -        ))
  2.1148 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  2.1149 -      )
  2.1150 -    WHEN 'MultiLineString' THEN
  2.1151 -      ( SELECT ecluster_concat(array_agg(
  2.1152 -          ( SELECT ecluster_create_path(array_agg(
  2.1153 -              coords_to_epoint(
  2.1154 -                ("coord"->>1)::float8,
  2.1155 -                ("coord"->>0)::float8,
  2.1156 -                $2
  2.1157 -              )
  2.1158 -            ))
  2.1159 -            FROM jsonb_array_elements("coord_array") AS "coord"
  2.1160 -          )
  2.1161 -        ))
  2.1162 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  2.1163 -      )
  2.1164 -    WHEN 'Polygon' THEN
  2.1165 -      ( SELECT ecluster_concat(array_agg(
  2.1166 -          ( SELECT ecluster_create_polygon(array_agg(
  2.1167 -              coords_to_epoint(
  2.1168 -                ("coord"->>1)::float8,
  2.1169 -                ("coord"->>0)::float8,
  2.1170 -                $2
  2.1171 -              )
  2.1172 -            ))
  2.1173 -            FROM jsonb_array_elements("coord_array") AS "coord"
  2.1174 -          )
  2.1175 -        ))
  2.1176 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  2.1177 -      )
  2.1178 -    WHEN 'MultiPolygon' THEN
  2.1179 -      ( SELECT ecluster_concat(array_agg(
  2.1180 -          ( SELECT ecluster_concat(array_agg(
  2.1181 -              ( SELECT ecluster_create_polygon(array_agg(
  2.1182 -                  coords_to_epoint(
  2.1183 -                    ("coord"->>1)::float8,
  2.1184 -                    ("coord"->>0)::float8,
  2.1185 -                    $2
  2.1186 -                  )
  2.1187 -                ))
  2.1188 -                FROM jsonb_array_elements("coord_array") AS "coord"
  2.1189 -              )
  2.1190 -            ))
  2.1191 -            FROM jsonb_array_elements("coord_array_array") AS "coord_array"
  2.1192 -          )
  2.1193 -        ))
  2.1194 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array"
  2.1195 -      )
  2.1196 -    WHEN 'Feature' THEN
  2.1197 -      GeoJSON_to_ecluster($1->'geometry', $2)
  2.1198 -    WHEN 'FeatureCollection' THEN
  2.1199 -      ( SELECT ecluster_concat(array_agg(
  2.1200 -          GeoJSON_to_ecluster("feature", $2)
  2.1201 -        ))
  2.1202 -        FROM jsonb_array_elements($1->'features') AS "feature"
  2.1203 -      )
  2.1204 -    ELSE
  2.1205 -      NULL
  2.1206 -    END
  2.1207 -  $$;
  2.1208 -
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/latlon--0.3.sql	Fri Sep 02 14:04:04 2016 +0200
     3.3 @@ -0,0 +1,1205 @@
     3.4 +
     3.5 +----------------------------------------
     3.6 +-- forward declarations (shell types) --
     3.7 +----------------------------------------
     3.8 +
     3.9 +CREATE TYPE epoint;
    3.10 +CREATE TYPE ebox;
    3.11 +CREATE TYPE ecircle;
    3.12 +CREATE TYPE ecluster;
    3.13 +
    3.14 +
    3.15 +------------------------------------------------------------
    3.16 +-- dummy input/output functions for dummy index key types --
    3.17 +------------------------------------------------------------
    3.18 +
    3.19 +CREATE FUNCTION ekey_point_in_dummy(cstring)
    3.20 +  RETURNS ekey_point
    3.21 +  LANGUAGE C IMMUTABLE STRICT
    3.22 +  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    3.23 +
    3.24 +CREATE FUNCTION ekey_point_out_dummy(ekey_point)
    3.25 +  RETURNS cstring
    3.26 +  LANGUAGE C IMMUTABLE STRICT
    3.27 +  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    3.28 +
    3.29 +CREATE FUNCTION ekey_area_in_dummy(cstring)
    3.30 +  RETURNS ekey_area
    3.31 +  LANGUAGE C IMMUTABLE STRICT
    3.32 +  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    3.33 +
    3.34 +CREATE FUNCTION ekey_area_out_dummy(ekey_area)
    3.35 +  RETURNS cstring
    3.36 +  LANGUAGE C IMMUTABLE STRICT
    3.37 +  AS '$libdir/latlon-v0003', 'pgl_notimpl';
    3.38 +
    3.39 +
    3.40 +--------------------------
    3.41 +-- text input functions --
    3.42 +--------------------------
    3.43 +
    3.44 +CREATE FUNCTION epoint_in(cstring)
    3.45 +  RETURNS epoint
    3.46 +  LANGUAGE C IMMUTABLE STRICT
    3.47 +  AS '$libdir/latlon-v0003', 'pgl_epoint_in';
    3.48 +
    3.49 +CREATE FUNCTION ebox_in(cstring)
    3.50 +  RETURNS ebox
    3.51 +  LANGUAGE C IMMUTABLE STRICT
    3.52 +  AS '$libdir/latlon-v0003', 'pgl_ebox_in';
    3.53 +
    3.54 +CREATE FUNCTION ecircle_in(cstring)
    3.55 +  RETURNS ecircle
    3.56 +  LANGUAGE C IMMUTABLE STRICT
    3.57 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_in';
    3.58 +
    3.59 +CREATE FUNCTION ecluster_in(cstring)
    3.60 +  RETURNS ecluster
    3.61 +  LANGUAGE C IMMUTABLE STRICT
    3.62 +  AS '$libdir/latlon-v0003', 'pgl_ecluster_in';
    3.63 +
    3.64 +
    3.65 +---------------------------
    3.66 +-- text output functions --
    3.67 +---------------------------
    3.68 +
    3.69 +CREATE FUNCTION epoint_out(epoint)
    3.70 +  RETURNS cstring
    3.71 +  LANGUAGE C IMMUTABLE STRICT
    3.72 +  AS '$libdir/latlon-v0003', 'pgl_epoint_out';
    3.73 +
    3.74 +CREATE FUNCTION ebox_out(ebox)
    3.75 +  RETURNS cstring
    3.76 +  LANGUAGE C IMMUTABLE STRICT
    3.77 +  AS '$libdir/latlon-v0003', 'pgl_ebox_out';
    3.78 +
    3.79 +CREATE FUNCTION ecircle_out(ecircle)
    3.80 +  RETURNS cstring
    3.81 +  LANGUAGE C IMMUTABLE STRICT
    3.82 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_out';
    3.83 +
    3.84 +CREATE FUNCTION ecluster_out(ecluster)
    3.85 +  RETURNS cstring
    3.86 +  LANGUAGE C IMMUTABLE STRICT
    3.87 +  AS '$libdir/latlon-v0003', 'pgl_ecluster_out';
    3.88 +
    3.89 +
    3.90 +--------------------------
    3.91 +-- binary I/O functions --
    3.92 +--------------------------
    3.93 +
    3.94 +CREATE FUNCTION epoint_recv(internal)
    3.95 +  RETURNS epoint
    3.96 +  LANGUAGE C IMMUTABLE STRICT
    3.97 +  AS '$libdir/latlon-v0003', 'pgl_epoint_recv';
    3.98 +
    3.99 +CREATE FUNCTION ebox_recv(internal)
   3.100 +  RETURNS ebox
   3.101 +  LANGUAGE C IMMUTABLE STRICT
   3.102 +  AS '$libdir/latlon-v0003', 'pgl_ebox_recv';
   3.103 +
   3.104 +CREATE FUNCTION ecircle_recv(internal)
   3.105 +  RETURNS ecircle
   3.106 +  LANGUAGE C IMMUTABLE STRICT
   3.107 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_recv';
   3.108 +
   3.109 +CREATE FUNCTION epoint_send(epoint)
   3.110 +  RETURNS bytea
   3.111 +  LANGUAGE C IMMUTABLE STRICT
   3.112 +  AS '$libdir/latlon-v0003', 'pgl_epoint_send';
   3.113 +
   3.114 +CREATE FUNCTION ebox_send(ebox)
   3.115 +  RETURNS bytea
   3.116 +  LANGUAGE C IMMUTABLE STRICT
   3.117 +  AS '$libdir/latlon-v0003', 'pgl_ebox_send';
   3.118 +
   3.119 +CREATE FUNCTION ecircle_send(ecircle)
   3.120 +  RETURNS bytea
   3.121 +  LANGUAGE C IMMUTABLE STRICT
   3.122 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_send';
   3.123 +
   3.124 +
   3.125 +-----------------------------------------------
   3.126 +-- type definitions of dummy index key types --
   3.127 +-----------------------------------------------
   3.128 +
   3.129 +CREATE TYPE ekey_point (
   3.130 +  internallength = 8,
   3.131 +  input = ekey_point_in_dummy,
   3.132 +  output = ekey_point_out_dummy,
   3.133 +  alignment = char );
   3.134 +
   3.135 +CREATE TYPE ekey_area (
   3.136 +  internallength = 9,
   3.137 +  input = ekey_area_in_dummy,
   3.138 +  output = ekey_area_out_dummy,
   3.139 +  alignment = char );
   3.140 +
   3.141 +
   3.142 +------------------------------------------
   3.143 +-- definitions of geographic data types --
   3.144 +------------------------------------------
   3.145 +
   3.146 +CREATE TYPE epoint (
   3.147 +  internallength = 16,
   3.148 +  input = epoint_in,
   3.149 +  output = epoint_out,
   3.150 +  receive = epoint_recv,
   3.151 +  send = epoint_send,
   3.152 +  alignment = double );
   3.153 +
   3.154 +CREATE TYPE ebox (
   3.155 +  internallength = 32,
   3.156 +  input = ebox_in,
   3.157 +  output = ebox_out,
   3.158 +  receive = ebox_recv,
   3.159 +  send = ebox_send,
   3.160 +  alignment = double );
   3.161 +
   3.162 +CREATE TYPE ecircle (
   3.163 +  internallength = 24,
   3.164 +  input = ecircle_in,
   3.165 +  output = ecircle_out,
   3.166 +  receive = ecircle_recv,
   3.167 +  send = ecircle_send,
   3.168 +  alignment = double );
   3.169 +
   3.170 +CREATE TYPE ecluster (
   3.171 +  internallength = VARIABLE,
   3.172 +  input = ecluster_in,
   3.173 +  output = ecluster_out,
   3.174 +  alignment = double,
   3.175 +  storage = external );
   3.176 +
   3.177 +
   3.178 +--------------------
   3.179 +-- B-tree support --
   3.180 +--------------------
   3.181 +
   3.182 +-- begin of B-tree support for epoint
   3.183 +
   3.184 +CREATE FUNCTION epoint_btree_lt(epoint, epoint)
   3.185 +  RETURNS boolean
   3.186 +  LANGUAGE C IMMUTABLE STRICT
   3.187 +  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_lt';
   3.188 +
   3.189 +CREATE FUNCTION epoint_btree_le(epoint, epoint)
   3.190 +  RETURNS boolean
   3.191 +  LANGUAGE C IMMUTABLE STRICT
   3.192 +  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_le';
   3.193 +
   3.194 +CREATE FUNCTION epoint_btree_eq(epoint, epoint)
   3.195 +  RETURNS boolean
   3.196 +  LANGUAGE C IMMUTABLE STRICT
   3.197 +  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_eq';
   3.198 +
   3.199 +CREATE FUNCTION epoint_btree_ne(epoint, epoint)
   3.200 +  RETURNS boolean
   3.201 +  LANGUAGE C IMMUTABLE STRICT
   3.202 +  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_ne';
   3.203 +
   3.204 +CREATE FUNCTION epoint_btree_ge(epoint, epoint)
   3.205 +  RETURNS boolean
   3.206 +  LANGUAGE C IMMUTABLE STRICT
   3.207 +  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_ge';
   3.208 +
   3.209 +CREATE FUNCTION epoint_btree_gt(epoint, epoint)
   3.210 +  RETURNS boolean
   3.211 +  LANGUAGE C IMMUTABLE STRICT
   3.212 +  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_gt';
   3.213 +
   3.214 +CREATE OPERATOR <<< (
   3.215 +  leftarg = epoint,
   3.216 +  rightarg = epoint,
   3.217 +  procedure = epoint_btree_lt,
   3.218 +  commutator = >>>,
   3.219 +  negator = >>>=,
   3.220 +  restrict = scalarltsel,
   3.221 +  join = scalarltjoinsel
   3.222 +);
   3.223 +
   3.224 +CREATE OPERATOR <<<= (
   3.225 +  leftarg = epoint,
   3.226 +  rightarg = epoint,
   3.227 +  procedure = epoint_btree_le,
   3.228 +  commutator = >>>=,
   3.229 +  negator = >>>,
   3.230 +  restrict = scalarltsel,
   3.231 +  join = scalarltjoinsel
   3.232 +);
   3.233 +
   3.234 +CREATE OPERATOR = (
   3.235 +  leftarg = epoint,
   3.236 +  rightarg = epoint,
   3.237 +  procedure = epoint_btree_eq,
   3.238 +  commutator = =,
   3.239 +  negator = <>,
   3.240 +  restrict = eqsel,
   3.241 +  join = eqjoinsel,
   3.242 +  merges
   3.243 +);
   3.244 +
   3.245 +CREATE OPERATOR <> (
   3.246 +  leftarg = epoint,
   3.247 +  rightarg = epoint,
   3.248 +  procedure = epoint_btree_eq,
   3.249 +  commutator = <>,
   3.250 +  negator = =,
   3.251 +  restrict = neqsel,
   3.252 +  join = neqjoinsel
   3.253 +);
   3.254 +
   3.255 +CREATE OPERATOR >>>= (
   3.256 +  leftarg = epoint,
   3.257 +  rightarg = epoint,
   3.258 +  procedure = epoint_btree_ge,
   3.259 +  commutator = <<<=,
   3.260 +  negator = <<<,
   3.261 +  restrict = scalargtsel,
   3.262 +  join = scalargtjoinsel
   3.263 +);
   3.264 +
   3.265 +CREATE OPERATOR >>> (
   3.266 +  leftarg = epoint,
   3.267 +  rightarg = epoint,
   3.268 +  procedure = epoint_btree_gt,
   3.269 +  commutator = <<<,
   3.270 +  negator = <<<=,
   3.271 +  restrict = scalargtsel,
   3.272 +  join = scalargtjoinsel
   3.273 +);
   3.274 +
   3.275 +CREATE FUNCTION epoint_btree_cmp(epoint, epoint)
   3.276 +  RETURNS int4
   3.277 +  LANGUAGE C IMMUTABLE STRICT
   3.278 +  AS '$libdir/latlon-v0003', 'pgl_btree_epoint_cmp';
   3.279 +
   3.280 +CREATE OPERATOR CLASS epoint_btree_ops
   3.281 +  DEFAULT FOR TYPE epoint USING btree AS
   3.282 +  OPERATOR 1 <<< ,
   3.283 +  OPERATOR 2 <<<= ,
   3.284 +  OPERATOR 3 = ,
   3.285 +  OPERATOR 4 >>>= ,
   3.286 +  OPERATOR 5 >>> ,
   3.287 +  FUNCTION 1 epoint_btree_cmp(epoint, epoint);
   3.288 +
   3.289 +-- end of B-tree support for epoint
   3.290 +
   3.291 +-- begin of B-tree support for ebox
   3.292 +
   3.293 +CREATE FUNCTION ebox_btree_lt(ebox, ebox)
   3.294 +  RETURNS boolean
   3.295 +  LANGUAGE C IMMUTABLE STRICT
   3.296 +  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_lt';
   3.297 +
   3.298 +CREATE FUNCTION ebox_btree_le(ebox, ebox)
   3.299 +  RETURNS boolean
   3.300 +  LANGUAGE C IMMUTABLE STRICT
   3.301 +  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_le';
   3.302 +
   3.303 +CREATE FUNCTION ebox_btree_eq(ebox, ebox)
   3.304 +  RETURNS boolean
   3.305 +  LANGUAGE C IMMUTABLE STRICT
   3.306 +  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_eq';
   3.307 +
   3.308 +CREATE FUNCTION ebox_btree_ne(ebox, ebox)
   3.309 +  RETURNS boolean
   3.310 +  LANGUAGE C IMMUTABLE STRICT
   3.311 +  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_ne';
   3.312 +
   3.313 +CREATE FUNCTION ebox_btree_ge(ebox, ebox)
   3.314 +  RETURNS boolean
   3.315 +  LANGUAGE C IMMUTABLE STRICT
   3.316 +  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_ge';
   3.317 +
   3.318 +CREATE FUNCTION ebox_btree_gt(ebox, ebox)
   3.319 +  RETURNS boolean
   3.320 +  LANGUAGE C IMMUTABLE STRICT
   3.321 +  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_gt';
   3.322 +
   3.323 +CREATE OPERATOR <<< (
   3.324 +  leftarg = ebox,
   3.325 +  rightarg = ebox,
   3.326 +  procedure = ebox_btree_lt,
   3.327 +  commutator = >>>,
   3.328 +  negator = >>>=,
   3.329 +  restrict = scalarltsel,
   3.330 +  join = scalarltjoinsel
   3.331 +);
   3.332 +
   3.333 +CREATE OPERATOR <<<= (
   3.334 +  leftarg = ebox,
   3.335 +  rightarg = ebox,
   3.336 +  procedure = ebox_btree_le,
   3.337 +  commutator = >>>=,
   3.338 +  negator = >>>,
   3.339 +  restrict = scalarltsel,
   3.340 +  join = scalarltjoinsel
   3.341 +);
   3.342 +
   3.343 +CREATE OPERATOR = (
   3.344 +  leftarg = ebox,
   3.345 +  rightarg = ebox,
   3.346 +  procedure = ebox_btree_eq,
   3.347 +  commutator = =,
   3.348 +  negator = <>,
   3.349 +  restrict = eqsel,
   3.350 +  join = eqjoinsel,
   3.351 +  merges
   3.352 +);
   3.353 +
   3.354 +CREATE OPERATOR <> (
   3.355 +  leftarg = ebox,
   3.356 +  rightarg = ebox,
   3.357 +  procedure = ebox_btree_eq,
   3.358 +  commutator = <>,
   3.359 +  negator = =,
   3.360 +  restrict = neqsel,
   3.361 +  join = neqjoinsel
   3.362 +);
   3.363 +
   3.364 +CREATE OPERATOR >>>= (
   3.365 +  leftarg = ebox,
   3.366 +  rightarg = ebox,
   3.367 +  procedure = ebox_btree_ge,
   3.368 +  commutator = <<<=,
   3.369 +  negator = <<<,
   3.370 +  restrict = scalargtsel,
   3.371 +  join = scalargtjoinsel
   3.372 +);
   3.373 +
   3.374 +CREATE OPERATOR >>> (
   3.375 +  leftarg = ebox,
   3.376 +  rightarg = ebox,
   3.377 +  procedure = ebox_btree_gt,
   3.378 +  commutator = <<<,
   3.379 +  negator = <<<=,
   3.380 +  restrict = scalargtsel,
   3.381 +  join = scalargtjoinsel
   3.382 +);
   3.383 +
   3.384 +CREATE FUNCTION ebox_btree_cmp(ebox, ebox)
   3.385 +  RETURNS int4
   3.386 +  LANGUAGE C IMMUTABLE STRICT
   3.387 +  AS '$libdir/latlon-v0003', 'pgl_btree_ebox_cmp';
   3.388 +
   3.389 +CREATE OPERATOR CLASS ebox_btree_ops
   3.390 +  DEFAULT FOR TYPE ebox USING btree AS
   3.391 +  OPERATOR 1 <<< ,
   3.392 +  OPERATOR 2 <<<= ,
   3.393 +  OPERATOR 3 = ,
   3.394 +  OPERATOR 4 >>>= ,
   3.395 +  OPERATOR 5 >>> ,
   3.396 +  FUNCTION 1 ebox_btree_cmp(ebox, ebox);
   3.397 +
   3.398 +-- end of B-tree support for ebox
   3.399 +
   3.400 +-- begin of B-tree support for ecircle
   3.401 +
   3.402 +CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   3.403 +  RETURNS boolean
   3.404 +  LANGUAGE C IMMUTABLE STRICT
   3.405 +  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_lt';
   3.406 +
   3.407 +CREATE FUNCTION ecircle_btree_le(ecircle, ecircle)
   3.408 +  RETURNS boolean
   3.409 +  LANGUAGE C IMMUTABLE STRICT
   3.410 +  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_le';
   3.411 +
   3.412 +CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   3.413 +  RETURNS boolean
   3.414 +  LANGUAGE C IMMUTABLE STRICT
   3.415 +  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_eq';
   3.416 +
   3.417 +CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   3.418 +  RETURNS boolean
   3.419 +  LANGUAGE C IMMUTABLE STRICT
   3.420 +  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_ne';
   3.421 +
   3.422 +CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   3.423 +  RETURNS boolean
   3.424 +  LANGUAGE C IMMUTABLE STRICT
   3.425 +  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_ge';
   3.426 +
   3.427 +CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   3.428 +  RETURNS boolean
   3.429 +  LANGUAGE C IMMUTABLE STRICT
   3.430 +  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_gt';
   3.431 +
   3.432 +CREATE OPERATOR <<< (
   3.433 +  leftarg = ecircle,
   3.434 +  rightarg = ecircle,
   3.435 +  procedure = ecircle_btree_lt,
   3.436 +  commutator = >>>,
   3.437 +  negator = >>>=,
   3.438 +  restrict = scalarltsel,
   3.439 +  join = scalarltjoinsel
   3.440 +);
   3.441 +
   3.442 +CREATE OPERATOR <<<= (
   3.443 +  leftarg = ecircle,
   3.444 +  rightarg = ecircle,
   3.445 +  procedure = ecircle_btree_le,
   3.446 +  commutator = >>>=,
   3.447 +  negator = >>>,
   3.448 +  restrict = scalarltsel,
   3.449 +  join = scalarltjoinsel
   3.450 +);
   3.451 +
   3.452 +CREATE OPERATOR = (
   3.453 +  leftarg = ecircle,
   3.454 +  rightarg = ecircle,
   3.455 +  procedure = ecircle_btree_eq,
   3.456 +  commutator = =,
   3.457 +  negator = <>,
   3.458 +  restrict = eqsel,
   3.459 +  join = eqjoinsel,
   3.460 +  merges
   3.461 +);
   3.462 +
   3.463 +CREATE OPERATOR <> (
   3.464 +  leftarg = ecircle,
   3.465 +  rightarg = ecircle,
   3.466 +  procedure = ecircle_btree_eq,
   3.467 +  commutator = <>,
   3.468 +  negator = =,
   3.469 +  restrict = neqsel,
   3.470 +  join = neqjoinsel
   3.471 +);
   3.472 +
   3.473 +CREATE OPERATOR >>>= (
   3.474 +  leftarg = ecircle,
   3.475 +  rightarg = ecircle,
   3.476 +  procedure = ecircle_btree_ge,
   3.477 +  commutator = <<<=,
   3.478 +  negator = <<<,
   3.479 +  restrict = scalargtsel,
   3.480 +  join = scalargtjoinsel
   3.481 +);
   3.482 +
   3.483 +CREATE OPERATOR >>> (
   3.484 +  leftarg = ecircle,
   3.485 +  rightarg = ecircle,
   3.486 +  procedure = ecircle_btree_gt,
   3.487 +  commutator = <<<,
   3.488 +  negator = <<<=,
   3.489 +  restrict = scalargtsel,
   3.490 +  join = scalargtjoinsel
   3.491 +);
   3.492 +
   3.493 +CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   3.494 +  RETURNS int4
   3.495 +  LANGUAGE C IMMUTABLE STRICT
   3.496 +  AS '$libdir/latlon-v0003', 'pgl_btree_ecircle_cmp';
   3.497 +
   3.498 +CREATE OPERATOR CLASS ecircle_btree_ops
   3.499 +  DEFAULT FOR TYPE ecircle USING btree AS
   3.500 +  OPERATOR 1 <<< ,
   3.501 +  OPERATOR 2 <<<= ,
   3.502 +  OPERATOR 3 = ,
   3.503 +  OPERATOR 4 >>>= ,
   3.504 +  OPERATOR 5 >>> ,
   3.505 +  FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle);
   3.506 +
   3.507 +-- end of B-tree support for ecircle
   3.508 +
   3.509 +
   3.510 +----------------
   3.511 +-- type casts --
   3.512 +----------------
   3.513 +
   3.514 +CREATE FUNCTION cast_epoint_to_ebox(epoint)
   3.515 +  RETURNS ebox
   3.516 +  LANGUAGE C IMMUTABLE STRICT
   3.517 +  AS '$libdir/latlon-v0003', 'pgl_epoint_to_ebox';
   3.518 +
   3.519 +CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint);
   3.520 +
   3.521 +CREATE FUNCTION cast_epoint_to_ecircle(epoint)
   3.522 +  RETURNS ecircle
   3.523 +  LANGUAGE C IMMUTABLE STRICT
   3.524 +  AS '$libdir/latlon-v0003', 'pgl_epoint_to_ecircle';
   3.525 +
   3.526 +CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint);
   3.527 +
   3.528 +CREATE FUNCTION cast_epoint_to_ecluster(epoint)
   3.529 +  RETURNS ecluster
   3.530 +  LANGUAGE C IMMUTABLE STRICT
   3.531 +  AS '$libdir/latlon-v0003', 'pgl_epoint_to_ecluster';
   3.532 +
   3.533 +CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint);
   3.534 +
   3.535 +CREATE FUNCTION cast_ebox_to_ecluster(ebox)
   3.536 +  RETURNS ecluster
   3.537 +  LANGUAGE C IMMUTABLE STRICT
   3.538 +  AS '$libdir/latlon-v0003', 'pgl_ebox_to_ecluster';
   3.539 +
   3.540 +CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox);
   3.541 +
   3.542 +
   3.543 +---------------------------
   3.544 +-- constructor functions --
   3.545 +---------------------------
   3.546 +
   3.547 +CREATE FUNCTION epoint(float8, float8)
   3.548 +  RETURNS epoint
   3.549 +  LANGUAGE C IMMUTABLE STRICT
   3.550 +  AS '$libdir/latlon-v0003', 'pgl_create_epoint';
   3.551 +
   3.552 +CREATE FUNCTION epoint_latlon(float8, float8)
   3.553 +  RETURNS epoint
   3.554 +  LANGUAGE SQL IMMUTABLE STRICT AS $$
   3.555 +    SELECT epoint($1, $2)
   3.556 +  $$;
   3.557 +
   3.558 +CREATE FUNCTION epoint_lonlat(float8, float8)
   3.559 +  RETURNS epoint
   3.560 +  LANGUAGE SQL IMMUTABLE STRICT AS $$
   3.561 +    SELECT epoint($2, $1)
   3.562 +  $$;
   3.563 +
   3.564 +CREATE FUNCTION empty_ebox()
   3.565 +  RETURNS ebox
   3.566 +  LANGUAGE C IMMUTABLE STRICT
   3.567 +  AS '$libdir/latlon-v0003', 'pgl_create_empty_ebox';
   3.568 +
   3.569 +CREATE FUNCTION ebox(float8, float8, float8, float8)
   3.570 +  RETURNS ebox
   3.571 +  LANGUAGE C IMMUTABLE STRICT
   3.572 +  AS '$libdir/latlon-v0003', 'pgl_create_ebox';
   3.573 +
   3.574 +CREATE FUNCTION ebox(epoint, epoint)
   3.575 +  RETURNS ebox
   3.576 +  LANGUAGE C IMMUTABLE STRICT
   3.577 +  AS '$libdir/latlon-v0003', 'pgl_create_ebox_from_epoints';
   3.578 +
   3.579 +CREATE FUNCTION ecircle(float8, float8, float8)
   3.580 +  RETURNS ecircle
   3.581 +  LANGUAGE C IMMUTABLE STRICT
   3.582 +  AS '$libdir/latlon-v0003', 'pgl_create_ecircle';
   3.583 +
   3.584 +CREATE FUNCTION ecircle(epoint, float8)
   3.585 +  RETURNS ecircle
   3.586 +  LANGUAGE C IMMUTABLE STRICT
   3.587 +  AS '$libdir/latlon-v0003', 'pgl_create_ecircle_from_epoint';
   3.588 +
   3.589 +CREATE FUNCTION ecluster_concat(ecluster[])
   3.590 +  RETURNS ecluster
   3.591 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.592 +    SELECT array_to_string($1, ' ')::ecluster
   3.593 +  $$;
   3.594 +
   3.595 +CREATE FUNCTION ecluster_concat(ecluster, ecluster)
   3.596 +  RETURNS ecluster
   3.597 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.598 +    SELECT ($1::text || ' ' || $2::text)::ecluster
   3.599 +  $$;
   3.600 +
   3.601 +CREATE FUNCTION ecluster_create_multipoint(epoint[])
   3.602 +  RETURNS ecluster
   3.603 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.604 +    SELECT
   3.605 +      array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster
   3.606 +    FROM unnest($1)
   3.607 +  $$;
   3.608 +
   3.609 +CREATE FUNCTION ecluster_create_path(epoint[])
   3.610 +  RETURNS ecluster
   3.611 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.612 +    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   3.613 +      ('path (' || array_to_string($1, ' ') || ')')::ecluster
   3.614 +    END
   3.615 +    FROM array_to_string($1, ' ') AS "str"
   3.616 +  $$;
   3.617 +
   3.618 +CREATE FUNCTION ecluster_create_outline(epoint[])
   3.619 +  RETURNS ecluster
   3.620 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.621 +    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   3.622 +      ('outline (' || array_to_string($1, ' ') || ')')::ecluster
   3.623 +    END
   3.624 +    FROM array_to_string($1, ' ') AS "str"
   3.625 +  $$;
   3.626 +
   3.627 +CREATE FUNCTION ecluster_create_polygon(epoint[])
   3.628 +  RETURNS ecluster
   3.629 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.630 +    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   3.631 +      ('polygon (' || array_to_string($1, ' ') || ')')::ecluster
   3.632 +    END
   3.633 +    FROM array_to_string($1, ' ') AS "str"
   3.634 +  $$;
   3.635 +
   3.636 +
   3.637 +----------------------
   3.638 +-- getter functions --
   3.639 +----------------------
   3.640 +
   3.641 +CREATE FUNCTION latitude(epoint)
   3.642 +  RETURNS float8
   3.643 +  LANGUAGE C IMMUTABLE STRICT
   3.644 +  AS '$libdir/latlon-v0003', 'pgl_epoint_lat';
   3.645 +
   3.646 +CREATE FUNCTION longitude(epoint)
   3.647 +  RETURNS float8
   3.648 +  LANGUAGE C IMMUTABLE STRICT
   3.649 +  AS '$libdir/latlon-v0003', 'pgl_epoint_lon';
   3.650 +
   3.651 +CREATE FUNCTION min_latitude(ebox)
   3.652 +  RETURNS float8
   3.653 +  LANGUAGE C IMMUTABLE STRICT
   3.654 +  AS '$libdir/latlon-v0003', 'pgl_ebox_lat_min';
   3.655 +
   3.656 +CREATE FUNCTION max_latitude(ebox)
   3.657 +  RETURNS float8
   3.658 +  LANGUAGE C IMMUTABLE STRICT
   3.659 +  AS '$libdir/latlon-v0003', 'pgl_ebox_lat_max';
   3.660 +
   3.661 +CREATE FUNCTION min_longitude(ebox)
   3.662 +  RETURNS float8
   3.663 +  LANGUAGE C IMMUTABLE STRICT
   3.664 +  AS '$libdir/latlon-v0003', 'pgl_ebox_lon_min';
   3.665 +
   3.666 +CREATE FUNCTION max_longitude(ebox)
   3.667 +  RETURNS float8
   3.668 +  LANGUAGE C IMMUTABLE STRICT
   3.669 +  AS '$libdir/latlon-v0003', 'pgl_ebox_lon_max';
   3.670 +
   3.671 +CREATE FUNCTION center(ecircle)
   3.672 +  RETURNS epoint
   3.673 +  LANGUAGE C IMMUTABLE STRICT
   3.674 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_center';
   3.675 +
   3.676 +CREATE FUNCTION radius(ecircle)
   3.677 +  RETURNS float8
   3.678 +  LANGUAGE C IMMUTABLE STRICT
   3.679 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_radius';
   3.680 +
   3.681 +CREATE FUNCTION ecluster_extract_points(ecluster)
   3.682 +  RETURNS SETOF epoint
   3.683 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.684 +    SELECT "match"[2]::epoint
   3.685 +    FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match"
   3.686 +  $$;
   3.687 +
   3.688 +CREATE FUNCTION ecluster_extract_paths(ecluster)
   3.689 +  RETURNS SETOF epoint[]
   3.690 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.691 +    SELECT (
   3.692 +      SELECT array_agg("m2"[1]::epoint)
   3.693 +      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   3.694 +    )
   3.695 +    FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1"
   3.696 +  $$;
   3.697 +
   3.698 +CREATE FUNCTION ecluster_extract_outlines(ecluster)
   3.699 +  RETURNS SETOF epoint[]
   3.700 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.701 +    SELECT (
   3.702 +      SELECT array_agg("m2"[1]::epoint)
   3.703 +      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   3.704 +    )
   3.705 +    FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1"
   3.706 +  $$;
   3.707 +
   3.708 +CREATE FUNCTION ecluster_extract_polygons(ecluster)
   3.709 +  RETURNS SETOF epoint[]
   3.710 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.711 +    SELECT (
   3.712 +      SELECT array_agg("m2"[1]::epoint)
   3.713 +      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   3.714 +    )
   3.715 +    FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1"
   3.716 +  $$;
   3.717 +
   3.718 +
   3.719 +---------------
   3.720 +-- operators --
   3.721 +---------------
   3.722 +
   3.723 +CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   3.724 +  RETURNS boolean
   3.725 +  LANGUAGE C IMMUTABLE STRICT
   3.726 +  AS '$libdir/latlon-v0003', 'pgl_epoint_ebox_overlap';
   3.727 +
   3.728 +CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   3.729 +  RETURNS boolean
   3.730 +  LANGUAGE C IMMUTABLE STRICT
   3.731 +  AS '$libdir/latlon-v0003', 'pgl_epoint_ecircle_overlap';
   3.732 +
   3.733 +CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   3.734 +  RETURNS boolean
   3.735 +  LANGUAGE C IMMUTABLE STRICT
   3.736 +  AS '$libdir/latlon-v0003', 'pgl_epoint_ecluster_overlap';
   3.737 +
   3.738 +CREATE FUNCTION ebox_overlap_proc(ebox, ebox)
   3.739 +  RETURNS boolean
   3.740 +  LANGUAGE C IMMUTABLE STRICT
   3.741 +  AS '$libdir/latlon-v0003', 'pgl_ebox_overlap';
   3.742 +
   3.743 +CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   3.744 +  RETURNS boolean
   3.745 +  LANGUAGE C IMMUTABLE STRICT
   3.746 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_overlap';
   3.747 +
   3.748 +CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   3.749 +  RETURNS boolean
   3.750 +  LANGUAGE C IMMUTABLE STRICT
   3.751 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_ecluster_overlap';
   3.752 +
   3.753 +CREATE FUNCTION epoint_distance_proc(epoint, epoint)
   3.754 +  RETURNS float8
   3.755 +  LANGUAGE C IMMUTABLE STRICT
   3.756 +  AS '$libdir/latlon-v0003', 'pgl_epoint_distance';
   3.757 +
   3.758 +CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   3.759 +  RETURNS float8
   3.760 +  LANGUAGE C IMMUTABLE STRICT
   3.761 +  AS '$libdir/latlon-v0003', 'pgl_epoint_ecircle_distance';
   3.762 +
   3.763 +CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   3.764 +  RETURNS float8
   3.765 +  LANGUAGE C IMMUTABLE STRICT
   3.766 +  AS '$libdir/latlon-v0003', 'pgl_epoint_ecluster_distance';
   3.767 +
   3.768 +CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   3.769 +  RETURNS float8
   3.770 +  LANGUAGE C IMMUTABLE STRICT
   3.771 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_distance';
   3.772 +
   3.773 +CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   3.774 +  RETURNS float8
   3.775 +  LANGUAGE C IMMUTABLE STRICT
   3.776 +  AS '$libdir/latlon-v0003', 'pgl_ecircle_ecluster_distance';
   3.777 +
   3.778 +CREATE OPERATOR && (
   3.779 +  leftarg = epoint,
   3.780 +  rightarg = ebox,
   3.781 +  procedure = epoint_ebox_overlap_proc,
   3.782 +  commutator = &&,
   3.783 +  restrict = areasel,
   3.784 +  join = areajoinsel
   3.785 +);
   3.786 +
   3.787 +CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint)
   3.788 +  RETURNS boolean
   3.789 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   3.790 +
   3.791 +CREATE OPERATOR && (
   3.792 +  leftarg = ebox,
   3.793 +  rightarg = epoint,
   3.794 +  procedure = epoint_ebox_overlap_commutator,
   3.795 +  commutator = &&,
   3.796 +  restrict = areasel,
   3.797 +  join = areajoinsel
   3.798 +);
   3.799 +
   3.800 +CREATE OPERATOR && (
   3.801 +  leftarg = epoint,
   3.802 +  rightarg = ecircle,
   3.803 +  procedure = epoint_ecircle_overlap_proc,
   3.804 +  commutator = &&,
   3.805 +  restrict = areasel,
   3.806 +  join = areajoinsel
   3.807 +);
   3.808 +
   3.809 +CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint)
   3.810 +  RETURNS boolean
   3.811 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   3.812 +
   3.813 +CREATE OPERATOR && (
   3.814 +  leftarg = ecircle,
   3.815 +  rightarg = epoint,
   3.816 +  procedure = epoint_ecircle_overlap_commutator,
   3.817 +  commutator = &&,
   3.818 +  restrict = areasel,
   3.819 +  join = areajoinsel
   3.820 +);
   3.821 +
   3.822 +CREATE OPERATOR && (
   3.823 +  leftarg = epoint,
   3.824 +  rightarg = ecluster,
   3.825 +  procedure = epoint_ecluster_overlap_proc,
   3.826 +  commutator = &&,
   3.827 +  restrict = areasel,
   3.828 +  join = areajoinsel
   3.829 +);
   3.830 +
   3.831 +CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint)
   3.832 +  RETURNS boolean
   3.833 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   3.834 +
   3.835 +CREATE OPERATOR && (
   3.836 +  leftarg = ecluster,
   3.837 +  rightarg = epoint,
   3.838 +  procedure = epoint_ecluster_overlap_commutator,
   3.839 +  commutator = &&,
   3.840 +  restrict = areasel,
   3.841 +  join = areajoinsel
   3.842 +);
   3.843 +
   3.844 +CREATE OPERATOR && (
   3.845 +  leftarg = ebox,
   3.846 +  rightarg = ebox,
   3.847 +  procedure = ebox_overlap_proc,
   3.848 +  commutator = &&,
   3.849 +  restrict = areasel,
   3.850 +  join = areajoinsel
   3.851 +);
   3.852 +
   3.853 +CREATE OPERATOR && (
   3.854 +  leftarg = ecircle,
   3.855 +  rightarg = ecircle,
   3.856 +  procedure = ecircle_overlap_proc,
   3.857 +  commutator = &&,
   3.858 +  restrict = areasel,
   3.859 +  join = areajoinsel
   3.860 +);
   3.861 +
   3.862 +CREATE OPERATOR && (
   3.863 +  leftarg = ecircle,
   3.864 +  rightarg = ecluster,
   3.865 +  procedure = ecircle_ecluster_overlap_proc,
   3.866 +  commutator = &&,
   3.867 +  restrict = areasel,
   3.868 +  join = areajoinsel
   3.869 +);
   3.870 +
   3.871 +CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle)
   3.872 +  RETURNS boolean
   3.873 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   3.874 +
   3.875 +CREATE OPERATOR && (
   3.876 +  leftarg = ecluster,
   3.877 +  rightarg = ecircle,
   3.878 +  procedure = ecircle_ecluster_overlap_commutator,
   3.879 +  commutator = &&,
   3.880 +  restrict = areasel,
   3.881 +  join = areajoinsel
   3.882 +);
   3.883 +
   3.884 +CREATE OPERATOR <-> (
   3.885 +  leftarg = epoint,
   3.886 +  rightarg = epoint,
   3.887 +  procedure = epoint_distance_proc,
   3.888 +  commutator = <->
   3.889 +);
   3.890 +
   3.891 +CREATE OPERATOR <-> (
   3.892 +  leftarg = epoint,
   3.893 +  rightarg = ecircle,
   3.894 +  procedure = epoint_ecircle_distance_proc,
   3.895 +  commutator = <->
   3.896 +);
   3.897 +
   3.898 +CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint)
   3.899 +  RETURNS float8
   3.900 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
   3.901 +
   3.902 +CREATE OPERATOR <-> (
   3.903 +  leftarg = ecircle,
   3.904 +  rightarg = epoint,
   3.905 +  procedure = epoint_ecircle_distance_commutator,
   3.906 +  commutator = <->
   3.907 +);
   3.908 +
   3.909 +CREATE OPERATOR <-> (
   3.910 +  leftarg = epoint,
   3.911 +  rightarg = ecluster,
   3.912 +  procedure = epoint_ecluster_distance_proc,
   3.913 +  commutator = <->
   3.914 +);
   3.915 +
   3.916 +CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint)
   3.917 +  RETURNS float8
   3.918 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
   3.919 +
   3.920 +CREATE OPERATOR <-> (
   3.921 +  leftarg = ecluster,
   3.922 +  rightarg = epoint,
   3.923 +  procedure = epoint_ecluster_distance_commutator,
   3.924 +  commutator = <->
   3.925 +);
   3.926 +
   3.927 +CREATE OPERATOR <-> (
   3.928 +  leftarg = ecircle,
   3.929 +  rightarg = ecircle,
   3.930 +  procedure = ecircle_distance_proc,
   3.931 +  commutator = <->
   3.932 +);
   3.933 +
   3.934 +CREATE OPERATOR <-> (
   3.935 +  leftarg = ecircle,
   3.936 +  rightarg = ecluster,
   3.937 +  procedure = ecircle_ecluster_distance_proc,
   3.938 +  commutator = <->
   3.939 +);
   3.940 +
   3.941 +CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle)
   3.942 +  RETURNS float8
   3.943 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
   3.944 +
   3.945 +CREATE OPERATOR <-> (
   3.946 +  leftarg = ecluster,
   3.947 +  rightarg = ecircle,
   3.948 +  procedure = ecircle_ecluster_distance_commutator,
   3.949 +  commutator = <->
   3.950 +);
   3.951 +
   3.952 +
   3.953 +----------------
   3.954 +-- GiST index --
   3.955 +----------------
   3.956 +
   3.957 +CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
   3.958 +  RETURNS boolean
   3.959 +  LANGUAGE C STRICT
   3.960 +  AS '$libdir/latlon-v0003', 'pgl_gist_consistent';
   3.961 +
   3.962 +CREATE FUNCTION pgl_gist_union(internal, internal)
   3.963 +  RETURNS internal
   3.964 +  LANGUAGE C STRICT
   3.965 +  AS '$libdir/latlon-v0003', 'pgl_gist_union';
   3.966 +
   3.967 +CREATE FUNCTION pgl_gist_compress_epoint(internal)
   3.968 +  RETURNS internal
   3.969 +  LANGUAGE C STRICT
   3.970 +  AS '$libdir/latlon-v0003', 'pgl_gist_compress_epoint';
   3.971 +
   3.972 +CREATE FUNCTION pgl_gist_compress_ecircle(internal)
   3.973 +  RETURNS internal
   3.974 +  LANGUAGE C STRICT
   3.975 +  AS '$libdir/latlon-v0003', 'pgl_gist_compress_ecircle';
   3.976 +
   3.977 +CREATE FUNCTION pgl_gist_compress_ecluster(internal)
   3.978 +  RETURNS internal
   3.979 +  LANGUAGE C STRICT
   3.980 +  AS '$libdir/latlon-v0003', 'pgl_gist_compress_ecluster';
   3.981 +
   3.982 +CREATE FUNCTION pgl_gist_decompress(internal)
   3.983 +  RETURNS internal
   3.984 +  LANGUAGE C STRICT
   3.985 +  AS '$libdir/latlon-v0003', 'pgl_gist_decompress';
   3.986 +
   3.987 +CREATE FUNCTION pgl_gist_penalty(internal, internal, internal)
   3.988 +  RETURNS internal
   3.989 +  LANGUAGE C STRICT
   3.990 +  AS '$libdir/latlon-v0003', 'pgl_gist_penalty';
   3.991 +
   3.992 +CREATE FUNCTION pgl_gist_picksplit(internal, internal)
   3.993 +  RETURNS internal
   3.994 +  LANGUAGE C STRICT
   3.995 +  AS '$libdir/latlon-v0003', 'pgl_gist_picksplit';
   3.996 +
   3.997 +CREATE FUNCTION pgl_gist_same(internal, internal, internal)
   3.998 +  RETURNS internal
   3.999 +  LANGUAGE C STRICT
  3.1000 +  AS '$libdir/latlon-v0003', 'pgl_gist_same';
  3.1001 +
  3.1002 +CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
  3.1003 +  RETURNS internal
  3.1004 +  LANGUAGE C STRICT
  3.1005 +  AS '$libdir/latlon-v0003', 'pgl_gist_distance';
  3.1006 +
  3.1007 +CREATE OPERATOR CLASS epoint_ops
  3.1008 +  DEFAULT FOR TYPE epoint USING gist AS
  3.1009 +  OPERATOR 11 = ,
  3.1010 +  OPERATOR 22 && (epoint, ebox),
  3.1011 +  OPERATOR 23 && (epoint, ecircle),
  3.1012 +  OPERATOR 24 && (epoint, ecluster),
  3.1013 +  OPERATOR 31 <-> (epoint, epoint) FOR ORDER BY float_ops,
  3.1014 +  OPERATOR 33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
  3.1015 +  OPERATOR 34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
  3.1016 +  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  3.1017 +  FUNCTION 2 pgl_gist_union(internal, internal),
  3.1018 +  FUNCTION 3 pgl_gist_compress_epoint(internal),
  3.1019 +  FUNCTION 4 pgl_gist_decompress(internal),
  3.1020 +  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  3.1021 +  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  3.1022 +  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  3.1023 +  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  3.1024 +  STORAGE ekey_point;
  3.1025 +
  3.1026 +CREATE OPERATOR CLASS ecircle_ops
  3.1027 +  DEFAULT FOR TYPE ecircle USING gist AS
  3.1028 +  OPERATOR 13 = ,
  3.1029 +  OPERATOR 21 && (ecircle, epoint),
  3.1030 +  OPERATOR 23 && (ecircle, ecircle),
  3.1031 +  OPERATOR 24 && (ecircle, ecluster),
  3.1032 +  OPERATOR 31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
  3.1033 +  OPERATOR 33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
  3.1034 +  OPERATOR 34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
  3.1035 +  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  3.1036 +  FUNCTION 2 pgl_gist_union(internal, internal),
  3.1037 +  FUNCTION 3 pgl_gist_compress_ecircle(internal),
  3.1038 +  FUNCTION 4 pgl_gist_decompress(internal),
  3.1039 +  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  3.1040 +  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  3.1041 +  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  3.1042 +  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  3.1043 +  STORAGE ekey_area;
  3.1044 +
  3.1045 +CREATE OPERATOR CLASS ecluster_ops
  3.1046 +  DEFAULT FOR TYPE ecluster USING gist AS
  3.1047 +  OPERATOR 21 && (ecluster, epoint),
  3.1048 +  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  3.1049 +  FUNCTION 2 pgl_gist_union(internal, internal),
  3.1050 +  FUNCTION 3 pgl_gist_compress_ecluster(internal),
  3.1051 +  FUNCTION 4 pgl_gist_decompress(internal),
  3.1052 +  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  3.1053 +  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  3.1054 +  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  3.1055 +  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  3.1056 +  STORAGE ekey_area;
  3.1057 +
  3.1058 +
  3.1059 +---------------------
  3.1060 +-- alias functions --
  3.1061 +---------------------
  3.1062 +
  3.1063 +CREATE FUNCTION distance(epoint, epoint)
  3.1064 +  RETURNS float8
  3.1065 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  3.1066 +
  3.1067 +CREATE FUNCTION distance(ecluster, epoint)
  3.1068 +  RETURNS float8
  3.1069 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  3.1070 +
  3.1071 +CREATE FUNCTION distance_within(epoint, epoint, float8)
  3.1072 +  RETURNS boolean
  3.1073 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  3.1074 +
  3.1075 +CREATE FUNCTION distance_within(ecluster, epoint, float8)
  3.1076 +  RETURNS boolean
  3.1077 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  3.1078 +
  3.1079 +
  3.1080 +--------------------------------
  3.1081 +-- other data storage formats --
  3.1082 +--------------------------------
  3.1083 +
  3.1084 +CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint_lonlat')
  3.1085 +  RETURNS epoint
  3.1086 +  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
  3.1087 +    DECLARE
  3.1088 +      "result" epoint;
  3.1089 +    BEGIN
  3.1090 +      IF $3 = 'epoint_lonlat' THEN
  3.1091 +        -- avoid dynamic command execution for better performance
  3.1092 +        RETURN epoint($2, $1);
  3.1093 +      END IF;
  3.1094 +      IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN
  3.1095 +        -- avoid dynamic command execution for better performance
  3.1096 +        RETURN epoint($1, $2);
  3.1097 +      END IF;
  3.1098 +      EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2;
  3.1099 +      RETURN "result";
  3.1100 +    END;
  3.1101 +  $$;
  3.1102 +
  3.1103 +CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat')
  3.1104 +  RETURNS epoint
  3.1105 +  LANGUAGE sql IMMUTABLE STRICT AS $$
  3.1106 +    SELECT CASE
  3.1107 +    WHEN $1->>'type' = 'Point' THEN
  3.1108 +      coords_to_epoint(
  3.1109 +        ($1->'coordinates'->>1)::float8,
  3.1110 +        ($1->'coordinates'->>0)::float8,
  3.1111 +        $2
  3.1112 +      )
  3.1113 +    WHEN $1->>'type' = 'Feature' THEN
  3.1114 +      GeoJSON_to_epoint($1->'geometry', $2)
  3.1115 +    ELSE
  3.1116 +      NULL
  3.1117 +    END
  3.1118 +  $$;
  3.1119 +
  3.1120 +CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat')
  3.1121 +  RETURNS ecluster
  3.1122 +  LANGUAGE sql IMMUTABLE STRICT AS $$
  3.1123 +    SELECT CASE $1->>'type'
  3.1124 +    WHEN 'Point' THEN
  3.1125 +      coords_to_epoint(
  3.1126 +        ($1->'coordinates'->>1)::float8,
  3.1127 +        ($1->'coordinates'->>0)::float8,
  3.1128 +        $2
  3.1129 +      )::ecluster
  3.1130 +    WHEN 'MultiPoint' THEN
  3.1131 +      ( SELECT ecluster_create_multipoint(array_agg(
  3.1132 +          coords_to_epoint(
  3.1133 +            ("coord"->>1)::float8,
  3.1134 +            ("coord"->>0)::float8,
  3.1135 +            $2
  3.1136 +          )
  3.1137 +        ))
  3.1138 +        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  3.1139 +      )
  3.1140 +    WHEN 'LineString' THEN
  3.1141 +      ( SELECT ecluster_create_path(array_agg(
  3.1142 +          coords_to_epoint(
  3.1143 +            ("coord"->>1)::float8,
  3.1144 +            ("coord"->>0)::float8,
  3.1145 +            $2
  3.1146 +          )
  3.1147 +        ))
  3.1148 +        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  3.1149 +      )
  3.1150 +    WHEN 'MultiLineString' THEN
  3.1151 +      ( SELECT ecluster_concat(array_agg(
  3.1152 +          ( SELECT ecluster_create_path(array_agg(
  3.1153 +              coords_to_epoint(
  3.1154 +                ("coord"->>1)::float8,
  3.1155 +                ("coord"->>0)::float8,
  3.1156 +                $2
  3.1157 +              )
  3.1158 +            ))
  3.1159 +            FROM jsonb_array_elements("coord_array") AS "coord"
  3.1160 +          )
  3.1161 +        ))
  3.1162 +        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  3.1163 +      )
  3.1164 +    WHEN 'Polygon' THEN
  3.1165 +      ( SELECT ecluster_concat(array_agg(
  3.1166 +          ( SELECT ecluster_create_polygon(array_agg(
  3.1167 +              coords_to_epoint(
  3.1168 +                ("coord"->>1)::float8,
  3.1169 +                ("coord"->>0)::float8,
  3.1170 +                $2
  3.1171 +              )
  3.1172 +            ))
  3.1173 +            FROM jsonb_array_elements("coord_array") AS "coord"
  3.1174 +          )
  3.1175 +        ))
  3.1176 +        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  3.1177 +      )
  3.1178 +    WHEN 'MultiPolygon' THEN
  3.1179 +      ( SELECT ecluster_concat(array_agg(
  3.1180 +          ( SELECT ecluster_concat(array_agg(
  3.1181 +              ( SELECT ecluster_create_polygon(array_agg(
  3.1182 +                  coords_to_epoint(
  3.1183 +                    ("coord"->>1)::float8,
  3.1184 +                    ("coord"->>0)::float8,
  3.1185 +                    $2
  3.1186 +                  )
  3.1187 +                ))
  3.1188 +                FROM jsonb_array_elements("coord_array") AS "coord"
  3.1189 +              )
  3.1190 +            ))
  3.1191 +            FROM jsonb_array_elements("coord_array_array") AS "coord_array"
  3.1192 +          )
  3.1193 +        ))
  3.1194 +        FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array"
  3.1195 +      )
  3.1196 +    WHEN 'Feature' THEN
  3.1197 +      GeoJSON_to_ecluster($1->'geometry', $2)
  3.1198 +    WHEN 'FeatureCollection' THEN
  3.1199 +      ( SELECT ecluster_concat(array_agg(
  3.1200 +          GeoJSON_to_ecluster("feature", $2)
  3.1201 +        ))
  3.1202 +        FROM jsonb_array_elements($1->'features') AS "feature"
  3.1203 +      )
  3.1204 +    ELSE
  3.1205 +      NULL
  3.1206 +    END
  3.1207 +  $$;
  3.1208 +
     4.1 --- a/latlon-v0002.c	Mon Aug 22 22:10:44 2016 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,2701 +0,0 @@
     4.4 -
     4.5 -/*-------------*
     4.6 - *  C prelude  *
     4.7 - *-------------*/
     4.8 -
     4.9 -#include "postgres.h"
    4.10 -#include "fmgr.h"
    4.11 -#include "libpq/pqformat.h"
    4.12 -#include "access/gist.h"
    4.13 -#include "access/stratnum.h"
    4.14 -#include "utils/array.h"
    4.15 -#include <math.h>
    4.16 -
    4.17 -#ifdef PG_MODULE_MAGIC
    4.18 -PG_MODULE_MAGIC;
    4.19 -#endif
    4.20 -
    4.21 -#if INT_MAX < 2147483647
    4.22 -#error Expected int type to be at least 32 bit wide
    4.23 -#endif
    4.24 -
    4.25 -
    4.26 -/*---------------------------------*
    4.27 - *  distance calculation on earth  *
    4.28 - *  (using WGS-84 spheroid)        *
    4.29 - *---------------------------------*/
    4.30 -
    4.31 -/*  WGS-84 spheroid with following parameters:
    4.32 -    semi-major axis  a = 6378137
    4.33 -    semi-minor axis  b = a * (1 - 1/298.257223563)
    4.34 -    estimated diameter = 2 * (2*a+b)/3
    4.35 -*/
    4.36 -#define PGL_SPHEROID_A 6378137.0            /* semi major axis */
    4.37 -#define PGL_SPHEROID_F (1.0/298.257223563)  /* flattening */
    4.38 -#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F))
    4.39 -#define PGL_EPS2       ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \
    4.40 -                           PGL_SPHEROID_B * PGL_SPHEROID_B ) / \
    4.41 -                         ( PGL_SPHEROID_A * PGL_SPHEROID_A ) )
    4.42 -#define PGL_SUBEPS2    (1.0-PGL_EPS2)
    4.43 -#define PGL_DIAMETER   ((4.0*PGL_SPHEROID_A + 2.0*PGL_SPHEROID_B) / 3.0)
    4.44 -#define PGL_SCALE      (PGL_SPHEROID_A / PGL_DIAMETER)  /* semi-major ref. */
    4.45 -#define PGL_FADELIMIT  (PGL_DIAMETER * M_PI / 6.0)      /* 1/6 circumference */
    4.46 -#define PGL_MAXDIST    (PGL_DIAMETER * M_PI / 2.0)      /* maximum distance */
    4.47 -
    4.48 -/* calculate distance between two points on earth (given in degrees) */
    4.49 -static inline double pgl_distance(
    4.50 -  double lat1, double lon1, double lat2, double lon2
    4.51 -) {
    4.52 -  float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin;
    4.53 -  float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t;
    4.54 -  /* normalize delta longitude (lon2 > 0 && lon1 = 0) */
    4.55 -  /* lon1 = 0 (not used anymore) */
    4.56 -  lon2 = fabs(lon2-lon1);
    4.57 -  /* convert to radians (first divide, then multiply) */
    4.58 -  lat1 = (lat1 / 180.0) * M_PI;
    4.59 -  lat2 = (lat2 / 180.0) * M_PI;
    4.60 -  lon2 = (lon2 / 180.0) * M_PI;
    4.61 -  /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point
    4.62 -     operations (lon2 >= lon1 is already ensured in a previous step) */
    4.63 -  if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; }
    4.64 -  /* calculate 3d coordinates on scaled ellipsoid which has an average diameter
    4.65 -     of 1.0 */
    4.66 -  lat1cos = cos(lat1); lat1sin = sin(lat1);
    4.67 -  lat2cos = cos(lat2); lat2sin = sin(lat2);
    4.68 -  lon2cos = cos(lon2); lon2sin = sin(lon2);
    4.69 -  nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin);
    4.70 -  nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
    4.71 -  x1 = nphi1 * lat1cos;
    4.72 -  z1 = nphi1 * PGL_SUBEPS2 * lat1sin;
    4.73 -  x2 = nphi2 * lat2cos * lon2cos;
    4.74 -  y2 = nphi2 * lat2cos * lon2sin;
    4.75 -  z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
    4.76 -  /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */
    4.77 -  g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
    4.78 -  /* convert tunnel distance through scaled ellipsoid to approximated surface
    4.79 -     distance on original ellipsoid */
    4.80 -  if (g > 1.0) g = 1.0;
    4.81 -  s = PGL_DIAMETER * asin(g);
    4.82 -  /* return result only if small enough to be precise (less than 1/3 of
    4.83 -     maximum possible distance) */
    4.84 -  if (s <= PGL_FADELIMIT) return s;
    4.85 -  /* calculate tunnel distance to antipodal point through scaled ellipsoid */
    4.86 -  g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1));
    4.87 -  /* convert tunnel distance to antipodal point through scaled ellipsoid to
    4.88 -     approximated surface distance to antipodal point on original ellipsoid */
    4.89 -  if (g > 1.0) g = 1.0;
    4.90 -  t = PGL_DIAMETER * asin(g);
    4.91 -  /* surface distance between original points can now be approximated by
    4.92 -     substracting antipodal distance from maximum possible distance;
    4.93 -     return result only if small enough (less than 1/3 of maximum possible
    4.94 -     distance) */
    4.95 -  if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t;
    4.96 -  /* otherwise crossfade direct and antipodal result to ensure monotonicity */
    4.97 -  return (
    4.98 -    (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) /
    4.99 -    (s + t - 2*PGL_FADELIMIT)
   4.100 -  );
   4.101 -}
   4.102 -
   4.103 -/* finite distance that can not be reached on earth */
   4.104 -#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST)
   4.105 -
   4.106 -
   4.107 -/*--------------------------------*
   4.108 - *  simple geographic data types  *
   4.109 - *--------------------------------*/
   4.110 -
   4.111 -/* point on earth given by latitude and longitude in degrees */
   4.112 -/* (type "epoint" in SQL) */
   4.113 -typedef struct {
   4.114 -  double lat;  /* between  -90 and  90 (both inclusive) */
   4.115 -  double lon;  /* between -180 and 180 (both inclusive) */
   4.116 -} pgl_point;
   4.117 -
   4.118 -/* box delimited by two parallels and two meridians (all in degrees) */
   4.119 -/* (type "ebox" in SQL) */
   4.120 -typedef struct {
   4.121 -  double lat_min;  /* between  -90 and  90 (both inclusive) */
   4.122 -  double lat_max;  /* between  -90 and  90 (both inclusive) */
   4.123 -  double lon_min;  /* between -180 and 180 (both inclusive) */
   4.124 -  double lon_max;  /* between -180 and 180 (both inclusive) */
   4.125 -  /* if lat_min > lat_max, then box is empty */
   4.126 -  /* if lon_min > lon_max, then 180th meridian is crossed */
   4.127 -} pgl_box;
   4.128 -
   4.129 -/* circle on earth surface (for radial searches with fixed radius) */
   4.130 -/* (type "ecircle" in SQL) */
   4.131 -typedef struct {
   4.132 -  pgl_point center;
   4.133 -  double radius; /* positive (including +0 but excluding -0), or -INFINITY */
   4.134 -  /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point),
   4.135 -     zero radius (0) denotes a single point,
   4.136 -     a finite radius (0 < radius < INFINITY) denotes a filled circle, and
   4.137 -     a radius of INFINITY is valid and means complete coverage of earth. */
   4.138 -} pgl_circle;
   4.139 -
   4.140 -
   4.141 -/*----------------------------------*
   4.142 - *  geographic "cluster" data type  *
   4.143 - *----------------------------------*/
   4.144 -
   4.145 -/* A cluster is a collection of points, paths, outlines, and polygons. If two
   4.146 -   polygons in a cluster overlap, the area covered by both polygons does not
   4.147 -   belong to the cluster. This way, a cluster can be used to describe complex
   4.148 -   shapes like polygons with holes. Outlines are non-filled polygons. Paths are
   4.149 -   open by default (i.e. the last point in the list is not connected with the
   4.150 -   first point in the list). Note that each outline or polygon in a cluster
   4.151 -   must cover a longitude range of less than 180 degrees to avoid ambiguities.
   4.152 -   Areas which are larger may be split into multiple polygons. */
   4.153 -
   4.154 -/* maximum number of points in a cluster */
   4.155 -/* (limited to avoid integer overflows, e.g. when allocating memory) */
   4.156 -#define PGL_CLUSTER_MAXPOINTS 16777216
   4.157 -
   4.158 -/* types of cluster entries */
   4.159 -#define PGL_ENTRY_POINT   1  /* a point */
   4.160 -#define PGL_ENTRY_PATH    2  /* a path from first point to last point */
   4.161 -#define PGL_ENTRY_OUTLINE 3  /* a non-filled polygon with given vertices */
   4.162 -#define PGL_ENTRY_POLYGON 4  /* a filled polygon with given vertices */
   4.163 -
   4.164 -/* Entries of a cluster are described by two different structs: pgl_newentry
   4.165 -   and pgl_entry. The first is used only during construction of a cluster, the
   4.166 -   second is used in all other cases (e.g. when reading clusters from the
   4.167 -   database, performing operations, etc). */
   4.168 -
   4.169 -/* entry for new geographic cluster during construction of that cluster */
   4.170 -typedef struct {
   4.171 -  int32_t entrytype;
   4.172 -  int32_t npoints;
   4.173 -  pgl_point *points;  /* pointer to an array of points (pgl_point) */
   4.174 -} pgl_newentry;
   4.175 -
   4.176 -/* entry of geographic cluster */
   4.177 -typedef struct {
   4.178 -  int32_t entrytype;  /* type of entry: point, path, outline, polygon */
   4.179 -  int32_t npoints;    /* number of stored points (set to 1 for point entry) */
   4.180 -  int32_t offset;     /* offset of pgl_point array from cluster base address */
   4.181 -  /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */
   4.182 -} pgl_entry;
   4.183 -
   4.184 -/* geographic cluster which is a collection of points, (open) paths, polygons,
   4.185 -   and outlines (non-filled polygons) */
   4.186 -typedef struct {
   4.187 -  char header[VARHDRSZ];  /* PostgreSQL header for variable size data types */
   4.188 -  int32_t nentries;       /* number of stored points */
   4.189 -  pgl_circle bounding;    /* bounding circle */
   4.190 -  /* Note: bounding circle ensures alignment of pgl_cluster for points */
   4.191 -  pgl_entry entries[FLEXIBLE_ARRAY_MEMBER];  /* var-length data */
   4.192 -} pgl_cluster;
   4.193 -
   4.194 -/* macro to determine memory alignment of points */
   4.195 -/* (needed to store pgl_point array after entries in pgl_cluster) */
   4.196 -typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment;
   4.197 -#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned)
   4.198 -
   4.199 -/* macro to extract a pointer to the array of points of a cluster entry */
   4.200 -#define PGL_ENTRY_POINTS(cluster, idx) \
   4.201 -  ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset))
   4.202 -
   4.203 -/* convert pgl_newentry array to pgl_cluster */
   4.204 -static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) {
   4.205 -  int i;              /* index of current entry */
   4.206 -  int npoints = 0;    /* number of points in whole cluster */
   4.207 -  int entry_npoints;  /* number of points in current entry */
   4.208 -  int points_offset = PGL_POINT_ALIGNMENT * (
   4.209 -    ( offsetof(pgl_cluster, entries) +
   4.210 -      nentries * sizeof(pgl_entry) +
   4.211 -      PGL_POINT_ALIGNMENT - 1
   4.212 -    ) / PGL_POINT_ALIGNMENT
   4.213 -  );  /* offset of pgl_point array from base address (considering alignment) */
   4.214 -  pgl_cluster *cluster;  /* new cluster to be returned */
   4.215 -  /* determine total number of points */
   4.216 -  for (i=0; i<nentries; i++) npoints += entries[i].npoints;
   4.217 -  /* allocate memory for cluster (including entries and points) */
   4.218 -  cluster = palloc(points_offset + npoints * sizeof(pgl_point));
   4.219 -  /* re-count total number of points to determine offset for each entry */
   4.220 -  npoints = 0;
   4.221 -  /* copy entries and points */
   4.222 -  for (i=0; i<nentries; i++) {
   4.223 -    /* determine number of points in entry */
   4.224 -    entry_npoints = entries[i].npoints;
   4.225 -    /* copy entry */
   4.226 -    cluster->entries[i].entrytype = entries[i].entrytype;
   4.227 -    cluster->entries[i].npoints = entry_npoints;
   4.228 -    /* calculate offset (in bytes) of pgl_point array */
   4.229 -    cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point);
   4.230 -    /* copy points */
   4.231 -    memcpy(
   4.232 -      PGL_ENTRY_POINTS(cluster, i),
   4.233 -      entries[i].points,
   4.234 -      entry_npoints * sizeof(pgl_point)
   4.235 -    );
   4.236 -    /* update total number of points processed */
   4.237 -    npoints += entry_npoints;
   4.238 -  }
   4.239 -  /* set number of entries in cluster */
   4.240 -  cluster->nentries = nentries;
   4.241 -  /* set PostgreSQL header for variable sized data */
   4.242 -  SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point));
   4.243 -  /* return newly created cluster */
   4.244 -  return cluster;
   4.245 -}
   4.246 -
   4.247 -
   4.248 -/*----------------------------------------*
   4.249 - *  C functions on geographic data types  *
   4.250 - *----------------------------------------*/
   4.251 -
   4.252 -/* round latitude or longitude to 12 digits after decimal point */
   4.253 -static inline double pgl_round(double val) {
   4.254 -  return round(val * 1e12) / 1e12;
   4.255 -}
   4.256 -
   4.257 -/* compare two points */
   4.258 -/* (equality when same point on earth is described, otherwise an arbitrary
   4.259 -   linear order) */
   4.260 -static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) {
   4.261 -  double lon1, lon2;  /* modified longitudes for special cases */
   4.262 -  /* use latitude as first ordering criterion */
   4.263 -  if (point1->lat < point2->lat) return -1;
   4.264 -  if (point1->lat > point2->lat) return 1;
   4.265 -  /* determine modified longitudes (considering special case of poles and
   4.266 -     180th meridian which can be described as W180 or E180) */
   4.267 -  if (point1->lat == -90 || point1->lat == 90) lon1 = 0;
   4.268 -  else if (point1->lon == 180) lon1 = -180;
   4.269 -  else lon1 = point1->lon;
   4.270 -  if (point2->lat == -90 || point2->lat == 90) lon2 = 0;
   4.271 -  else if (point2->lon == 180) lon2 = -180;
   4.272 -  else lon2 = point2->lon;
   4.273 -  /* use (modified) longitude as secondary ordering criterion */
   4.274 -  if (lon1 < lon2) return -1;
   4.275 -  if (lon1 > lon2) return 1;
   4.276 -  /* no difference found, points are equal */
   4.277 -  return 0;
   4.278 -}
   4.279 -
   4.280 -/* compare two boxes */
   4.281 -/* (equality when same box on earth is described, otherwise an arbitrary linear
   4.282 -   order) */
   4.283 -static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) {
   4.284 -  /* two empty boxes are equal, and an empty box is always considered "less
   4.285 -     than" a non-empty box */
   4.286 -  if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1;
   4.287 -  if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0;
   4.288 -  if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1;
   4.289 -  /* use southern border as first ordering criterion */
   4.290 -  if (box1->lat_min < box2->lat_min) return -1;
   4.291 -  if (box1->lat_min > box2->lat_min) return 1;
   4.292 -  /* use northern border as second ordering criterion */
   4.293 -  if (box1->lat_max < box2->lat_max) return -1;
   4.294 -  if (box1->lat_max > box2->lat_max) return 1;
   4.295 -  /* use western border as third ordering criterion */
   4.296 -  if (box1->lon_min < box2->lon_min) return -1;
   4.297 -  if (box1->lon_min > box2->lon_min) return 1;
   4.298 -  /* use eastern border as fourth ordering criterion */
   4.299 -  if (box1->lon_max < box2->lon_max) return -1;
   4.300 -  if (box1->lon_max > box2->lon_max) return 1;
   4.301 -  /* no difference found, boxes are equal */
   4.302 -  return 0;
   4.303 -}
   4.304 -
   4.305 -/* compare two circles */
   4.306 -/* (equality when same circle on earth is described, otherwise an arbitrary
   4.307 -   linear order) */
   4.308 -static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) {
   4.309 -  /* two circles with same infinite radius (positive or negative infinity) are
   4.310 -     considered equal independently of center point */
   4.311 -  if (
   4.312 -    !isfinite(circle1->radius) && !isfinite(circle2->radius) &&
   4.313 -    circle1->radius == circle2->radius
   4.314 -  ) return 0;
   4.315 -  /* use radius as first ordering criterion */
   4.316 -  if (circle1->radius < circle2->radius) return -1;
   4.317 -  if (circle1->radius > circle2->radius) return 1;
   4.318 -  /* use center point as secondary ordering criterion */
   4.319 -  return pgl_point_cmp(&(circle1->center), &(circle2->center));
   4.320 -}
   4.321 -
   4.322 -/* set box to empty box*/
   4.323 -static void pgl_box_set_empty(pgl_box *box) {
   4.324 -  box->lat_min = INFINITY;
   4.325 -  box->lat_max = -INFINITY;
   4.326 -  box->lon_min = 0;
   4.327 -  box->lon_max = 0;
   4.328 -}
   4.329 -
   4.330 -/* check if point is inside a box */
   4.331 -static bool pgl_point_in_box(pgl_point *point, pgl_box *box) {
   4.332 -  return (
   4.333 -    point->lat >= box->lat_min && point->lat <= box->lat_max && (
   4.334 -      (box->lon_min > box->lon_max) ? (
   4.335 -        /* box crosses 180th meridian */
   4.336 -        point->lon >= box->lon_min || point->lon <= box->lon_max
   4.337 -      ) : (
   4.338 -        /* box does not cross the 180th meridian */
   4.339 -        point->lon >= box->lon_min && point->lon <= box->lon_max
   4.340 -      )
   4.341 -    )
   4.342 -  );
   4.343 -}
   4.344 -
   4.345 -/* check if two boxes overlap */
   4.346 -static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) {
   4.347 -  return (
   4.348 -    box2->lat_max >= box2->lat_min &&  /* ensure box2 is not empty */
   4.349 -    ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) &&
   4.350 -    ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && (
   4.351 -      (
   4.352 -        /* check if one and only one box crosses the 180th meridian */
   4.353 -        ((box1->lon_min > box1->lon_max) ? 1 : 0) ^
   4.354 -        ((box2->lon_min > box2->lon_max) ? 1 : 0)
   4.355 -      ) ? (
   4.356 -        /* exactly one box crosses the 180th meridian */
   4.357 -        box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min ||
   4.358 -        box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max
   4.359 -      ) : (
   4.360 -        /* no box or both boxes cross the 180th meridian */
   4.361 -        (
   4.362 -          (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) &&
   4.363 -          (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max)
   4.364 -        ) ||
   4.365 -        /* handle W180 == E180 */
   4.366 -        ( box1->lon_min == -180 && box2->lon_max == 180 ) ||
   4.367 -        ( box2->lon_min == -180 && box1->lon_max == 180 )
   4.368 -      )
   4.369 -    )
   4.370 -  );
   4.371 -}
   4.372 -
   4.373 -/* check unambiguousness of east/west orientation of cluster entries and set
   4.374 -   bounding circle of cluster */
   4.375 -static bool pgl_finalize_cluster(pgl_cluster *cluster) {
   4.376 -  int i, j;                 /* i: index of entry, j: index of point in entry */
   4.377 -  int npoints;              /* number of points in entry */
   4.378 -  int total_npoints = 0;    /* total number of points in cluster */
   4.379 -  pgl_point *points;        /* points in entry */
   4.380 -  int lon_dir;              /* first point of entry west (-1) or east (+1) */
   4.381 -  double lon_break = 0;     /* antipodal longitude of first point in entry */
   4.382 -  double lon_min, lon_max;  /* covered longitude range of entry */
   4.383 -  double value;             /* temporary variable */
   4.384 -  /* reset bounding circle center to empty circle at 0/0 coordinates */
   4.385 -  cluster->bounding.center.lat = 0;
   4.386 -  cluster->bounding.center.lon = 0;
   4.387 -  cluster->bounding.radius = -INFINITY;
   4.388 -  /* if cluster is not empty */
   4.389 -  if (cluster->nentries != 0) {
   4.390 -    /* iterate over all cluster entries and ensure they each cover a longitude
   4.391 -       range less than 180 degrees */
   4.392 -    for (i=0; i<cluster->nentries; i++) {
   4.393 -      /* get properties of entry */
   4.394 -      npoints = cluster->entries[i].npoints;
   4.395 -      points = PGL_ENTRY_POINTS(cluster, i);
   4.396 -      /* get longitude of first point of entry */
   4.397 -      value = points[0].lon;
   4.398 -      /* initialize lon_min and lon_max with longitude of first point */
   4.399 -      lon_min = value;
   4.400 -      lon_max = value;
   4.401 -      /* determine east/west orientation of first point and calculate antipodal
   4.402 -         longitude (Note: rounding required here) */
   4.403 -      if      (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); }
   4.404 -      else if (value > 0) { lon_dir =  1; lon_break = pgl_round(value - 180); }
   4.405 -      else lon_dir = 0;
   4.406 -      /* iterate over all other points in entry */
   4.407 -      for (j=1; j<npoints; j++) {
   4.408 -        /* consider longitude wrap-around */
   4.409 -        value = points[j].lon;
   4.410 -        if      (lon_dir<0 && value>lon_break) value = pgl_round(value - 360);
   4.411 -        else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360);
   4.412 -        /* update lon_min and lon_max */
   4.413 -        if      (value < lon_min) lon_min = value;
   4.414 -        else if (value > lon_max) lon_max = value;
   4.415 -        /* return false if 180 degrees or more are covered */
   4.416 -        if (lon_max - lon_min >= 180) return false;
   4.417 -      }
   4.418 -    }
   4.419 -    /* iterate over all points of all entries and calculate arbitrary center
   4.420 -       point for bounding circle (best if center point minimizes the radius,
   4.421 -       but some error is allowed here) */
   4.422 -    for (i=0; i<cluster->nentries; i++) {
   4.423 -      /* get properties of entry */
   4.424 -      npoints = cluster->entries[i].npoints;
   4.425 -      points = PGL_ENTRY_POINTS(cluster, i);
   4.426 -      /* check if first entry */
   4.427 -      if (i==0) {
   4.428 -        /* get longitude of first point of first entry in whole cluster */
   4.429 -        value = points[0].lon;
   4.430 -        /* initialize lon_min and lon_max with longitude of first point of
   4.431 -           first entry in whole cluster (used to determine if whole cluster
   4.432 -           covers a longitude range of 180 degrees or more) */
   4.433 -        lon_min = value;
   4.434 -        lon_max = value;
   4.435 -        /* determine east/west orientation of first point and calculate
   4.436 -           antipodal longitude (Note: rounding not necessary here) */
   4.437 -        if      (value < 0) { lon_dir = -1; lon_break = value + 180; }
   4.438 -        else if (value > 0) { lon_dir =  1; lon_break = value - 180; }
   4.439 -        else lon_dir = 0;
   4.440 -      }
   4.441 -      /* iterate over all points in entry */
   4.442 -      for (j=0; j<npoints; j++) {
   4.443 -        /* longitude wrap-around (Note: rounding not necessary here) */
   4.444 -        value = points[j].lon;
   4.445 -        if      (lon_dir < 0 && value > lon_break) value -= 360;
   4.446 -        else if (lon_dir > 0 && value < lon_break) value += 360;
   4.447 -        if      (value < lon_min) lon_min = value;
   4.448 -        else if (value > lon_max) lon_max = value;
   4.449 -        /* set bounding circle to cover whole earth if more than 180 degrees
   4.450 -           are covered */
   4.451 -        if (lon_max - lon_min >= 180) {
   4.452 -          cluster->bounding.center.lat = 0;
   4.453 -          cluster->bounding.center.lon = 0;
   4.454 -          cluster->bounding.radius = INFINITY;
   4.455 -          return true;
   4.456 -        }
   4.457 -        /* add point to bounding circle center (for average calculation) */
   4.458 -        cluster->bounding.center.lat += points[j].lat;
   4.459 -        cluster->bounding.center.lon += value;
   4.460 -      }
   4.461 -      /* count total number of points */
   4.462 -      total_npoints += npoints;
   4.463 -    }
   4.464 -    /* determine average latitude and longitude of cluster */
   4.465 -    cluster->bounding.center.lat /= total_npoints;
   4.466 -    cluster->bounding.center.lon /= total_npoints;
   4.467 -    /* normalize longitude of center of cluster bounding circle */
   4.468 -    if (cluster->bounding.center.lon < -180) {
   4.469 -      cluster->bounding.center.lon += 360;
   4.470 -    }
   4.471 -    else if (cluster->bounding.center.lon > 180) {
   4.472 -      cluster->bounding.center.lon -= 360;
   4.473 -    }
   4.474 -    /* round bounding circle center (useful if it is used by other functions) */
   4.475 -    cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat);
   4.476 -    cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon);
   4.477 -    /* calculate radius of bounding circle */
   4.478 -    for (i=0; i<cluster->nentries; i++) {
   4.479 -      npoints = cluster->entries[i].npoints;
   4.480 -      points = PGL_ENTRY_POINTS(cluster, i);
   4.481 -      for (j=0; j<npoints; j++) {
   4.482 -        value = pgl_distance(
   4.483 -          cluster->bounding.center.lat, cluster->bounding.center.lon,
   4.484 -          points[j].lat, points[j].lon
   4.485 -        );
   4.486 -        if (value > cluster->bounding.radius) cluster->bounding.radius = value;
   4.487 -      }
   4.488 -    }
   4.489 -  }
   4.490 -  /* return true (east/west orientation is unambiguous) */
   4.491 -  return true;
   4.492 -}
   4.493 -
   4.494 -/* check if point is inside cluster */
   4.495 -static bool pgl_point_in_cluster(pgl_point *point, pgl_cluster *cluster) {
   4.496 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   4.497 -  int entrytype;         /* type of entry */
   4.498 -  int npoints;           /* number of points in entry */
   4.499 -  pgl_point *points;     /* array of points in entry */
   4.500 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   4.501 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   4.502 -  double lat0 = point->lat;  /* latitude of point */
   4.503 -  double lon0;           /* (adjusted) longitude of point */
   4.504 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   4.505 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   4.506 -  double lon;            /* longitude of intersection */
   4.507 -  int counter = 0;       /* counter for intersections east of point */
   4.508 -  /* points outside bounding circle are always assumed to be non-overlapping */
   4.509 -  /* (necessary for consistent table and index scans) */
   4.510 -  if (
   4.511 -    pgl_distance(
   4.512 -      point->lat, point->lon,
   4.513 -      cluster->bounding.center.lat, cluster->bounding.center.lon
   4.514 -    ) > cluster->bounding.radius
   4.515 -  ) return false;
   4.516 -  /* iterate over all entries */
   4.517 -  for (i=0; i<cluster->nentries; i++) {
   4.518 -    /* get properties of entry */
   4.519 -    entrytype = cluster->entries[i].entrytype;
   4.520 -    npoints = cluster->entries[i].npoints;
   4.521 -    points = PGL_ENTRY_POINTS(cluster, i);
   4.522 -    /* determine east/west orientation of first point of entry and calculate
   4.523 -       antipodal longitude */
   4.524 -    lon_break = points[0].lon;
   4.525 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   4.526 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   4.527 -    else lon_dir = 0;
   4.528 -    /* get longitude of point */
   4.529 -    lon0 = point->lon;
   4.530 -    /* consider longitude wrap-around for point */
   4.531 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360);
   4.532 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360);
   4.533 -    /* iterate over all edges and vertices */
   4.534 -    for (j=0; j<npoints; j++) {
   4.535 -      /* return true if point is on vertex of polygon */
   4.536 -      if (pgl_point_cmp(point, &(points[j])) == 0) return true;
   4.537 -      /* calculate index of next vertex */
   4.538 -      k = (j+1) % npoints;
   4.539 -      /* skip last edge unless entry is (closed) outline or polygon */
   4.540 -      if (
   4.541 -        k == 0 &&
   4.542 -        entrytype != PGL_ENTRY_OUTLINE &&
   4.543 -        entrytype != PGL_ENTRY_POLYGON
   4.544 -      ) continue;
   4.545 -      /* get latitude and longitude values of edge */
   4.546 -      lat1 = points[j].lat;
   4.547 -      lat2 = points[k].lat;
   4.548 -      lon1 = points[j].lon;
   4.549 -      lon2 = points[k].lon;
   4.550 -      /* consider longitude wrap-around for edge */
   4.551 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   4.552 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   4.553 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
   4.554 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
   4.555 -      /* return true if point is on horizontal (west to east) edge of polygon */
   4.556 -      if (
   4.557 -        lat0 == lat1 && lat0 == lat2 &&
   4.558 -        ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) )
   4.559 -      ) return true;
   4.560 -      /* check if edge crosses east/west line of point */
   4.561 -      if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) {
   4.562 -        /* calculate longitude of intersection */
   4.563 -        lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1);
   4.564 -        /* return true if intersection goes (approximately) through point */
   4.565 -        if (pgl_round(lon) == lon0) return true;
   4.566 -        /* count intersection if east of point and entry is polygon*/
   4.567 -        if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++;
   4.568 -      }
   4.569 -    }
   4.570 -  }
   4.571 -  /* return true if number of intersections is odd */
   4.572 -  return counter & 1;
   4.573 -}
   4.574 -
   4.575 -/* calculate (approximate) distance between point and cluster */
   4.576 -static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
   4.577 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   4.578 -  int entrytype;         /* type of entry */
   4.579 -  int npoints;           /* number of points in entry */
   4.580 -  pgl_point *points;     /* array of points in entry */
   4.581 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   4.582 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   4.583 -  double lon_min = 0;    /* minimum (adjusted) longitude of entry vertices */
   4.584 -  double lon_max = 0;    /* maximum (adjusted) longitude of entry vertices */
   4.585 -  double lat0 = point->lat;  /* latitude of point */
   4.586 -  double lon0;           /* (adjusted) longitude of point */
   4.587 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   4.588 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   4.589 -  double s;              /* scalar for vector calculations */
   4.590 -  double dist;           /* distance calculated in one step */
   4.591 -  double min_dist = INFINITY;   /* minimum distance */
   4.592 -  /* distance is zero if point is contained in cluster */
   4.593 -  if (pgl_point_in_cluster(point, cluster)) return 0;
   4.594 -  /* iterate over all entries */
   4.595 -  for (i=0; i<cluster->nentries; i++) {
   4.596 -    /* get properties of entry */
   4.597 -    entrytype = cluster->entries[i].entrytype;
   4.598 -    npoints = cluster->entries[i].npoints;
   4.599 -    points = PGL_ENTRY_POINTS(cluster, i);
   4.600 -    /* determine east/west orientation of first point of entry and calculate
   4.601 -       antipodal longitude */
   4.602 -    lon_break = points[0].lon;
   4.603 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   4.604 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   4.605 -    else lon_dir = 0;
   4.606 -    /* determine covered longitude range */
   4.607 -    for (j=0; j<npoints; j++) {
   4.608 -      /* get longitude of vertex */
   4.609 -      lon1 = points[j].lon;
   4.610 -      /* adjust longitude to fix potential wrap-around */
   4.611 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   4.612 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   4.613 -      /* update minimum and maximum longitude of polygon */
   4.614 -      if (j == 0 || lon1 < lon_min) lon_min = lon1;
   4.615 -      if (j == 0 || lon1 > lon_max) lon_max = lon1;
   4.616 -    }
   4.617 -    /* adjust longitude wrap-around according to full longitude range */
   4.618 -    lon_break = (lon_max + lon_min) / 2;
   4.619 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   4.620 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   4.621 -    /* get longitude of point */
   4.622 -    lon0 = point->lon;
   4.623 -    /* consider longitude wrap-around for point */
   4.624 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 -= 360;
   4.625 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
   4.626 -    /* iterate over all edges and vertices */
   4.627 -    for (j=0; j<npoints; j++) {
   4.628 -      /* get latitude and longitude values of current point */
   4.629 -      lat1 = points[j].lat;
   4.630 -      lon1 = points[j].lon;
   4.631 -      /* consider longitude wrap-around for current point */
   4.632 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   4.633 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   4.634 -      /* calculate distance to vertex */
   4.635 -      dist = pgl_distance(lat0, lon0, lat1, lon1);
   4.636 -      /* store calculated distance if smallest */
   4.637 -      if (dist < min_dist) min_dist = dist;
   4.638 -      /* calculate index of next vertex */
   4.639 -      k = (j+1) % npoints;
   4.640 -      /* skip last edge unless entry is (closed) outline or polygon */
   4.641 -      if (
   4.642 -        k == 0 &&
   4.643 -        entrytype != PGL_ENTRY_OUTLINE &&
   4.644 -        entrytype != PGL_ENTRY_POLYGON
   4.645 -      ) continue;
   4.646 -      /* get latitude and longitude values of next point */
   4.647 -      lat2 = points[k].lat;
   4.648 -      lon2 = points[k].lon;
   4.649 -      /* consider longitude wrap-around for next point */
   4.650 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
   4.651 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
   4.652 -      /* go to next vertex and edge if edge is degenerated */
   4.653 -      if (lat1 == lat2 && lon1 == lon2) continue;
   4.654 -      /* otherwise test if point can be projected onto edge of polygon */
   4.655 -      s = (
   4.656 -        ((lat0-lat1) * (lat2-lat1) + (lon0-lon1) * (lon2-lon1)) /
   4.657 -        ((lat2-lat1) * (lat2-lat1) + (lon2-lon1) * (lon2-lon1))
   4.658 -      );
   4.659 -      /* go to next vertex and edge if point cannot be projected */
   4.660 -      if (!(s > 0 && s < 1)) continue;
   4.661 -      /* calculate distance from original point to projected point */
   4.662 -      dist = pgl_distance(
   4.663 -        lat0, lon0,
   4.664 -        lat1 + s * (lat2-lat1),
   4.665 -        lon1 + s * (lon2-lon1)
   4.666 -      );
   4.667 -      /* store calculated distance if smallest */
   4.668 -      if (dist < min_dist) min_dist = dist;
   4.669 -    }
   4.670 -  }
   4.671 -  /* return minimum distance */
   4.672 -  return min_dist;
   4.673 -}
   4.674 -
   4.675 -/* estimator function for distance between box and point */
   4.676 -/* allowed to return smaller values than actually correct */
   4.677 -static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
   4.678 -  double dlon;  /* longitude range of box (delta longitude) */
   4.679 -  double h;     /* half of distance along meridian */
   4.680 -  double d;     /* distance between both southern or both northern points */
   4.681 -  double cur_dist;  /* calculated distance */
   4.682 -  double min_dist;  /* minimum distance calculated */
   4.683 -  /* return infinity if bounding box is empty */
   4.684 -  if (box->lat_min > box->lat_max) return INFINITY;
   4.685 -  /* return zero if point is inside bounding box */
   4.686 -  if (pgl_point_in_box(point, box)) return 0;
   4.687 -  /* calculate delta longitude */
   4.688 -  dlon = box->lon_max - box->lon_min;
   4.689 -  if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
   4.690 -  /* if delta longitude is greater than 180 degrees, perform safe fall-back */
   4.691 -  if (dlon > 180) return 0;
   4.692 -  /* calculate half of distance along meridian */
   4.693 -  h = pgl_distance(box->lat_min, 0, box->lat_max, 0) / 2;
   4.694 -  /* calculate full distance between southern points */
   4.695 -  d = pgl_distance(box->lat_min, 0, box->lat_min, dlon);
   4.696 -  /* calculate maximum of full distance and half distance */
   4.697 -  if (h > d) d = h;
   4.698 -  /* calculate distance from point to first southern vertex and substract
   4.699 -     maximum error */
   4.700 -  min_dist = pgl_distance(
   4.701 -    point->lat, point->lon, box->lat_min, box->lon_min
   4.702 -  ) - d;
   4.703 -  /* return zero if estimated distance is smaller than zero */
   4.704 -  if (min_dist <= 0) return 0;
   4.705 -  /* repeat procedure with second southern vertex */
   4.706 -  cur_dist = pgl_distance(
   4.707 -    point->lat, point->lon, box->lat_min, box->lon_max
   4.708 -  ) - d;
   4.709 -  if (cur_dist <= 0) return 0;
   4.710 -  if (cur_dist < min_dist) min_dist = cur_dist;
   4.711 -  /* calculate full distance between northern points */
   4.712 -  d = pgl_distance(box->lat_max, 0, box->lat_max, dlon);
   4.713 -  /* calculate maximum of full distance and half distance */
   4.714 -  if (h > d) d = h;
   4.715 -  /* repeat procedure with northern vertices */
   4.716 -  cur_dist = pgl_distance(
   4.717 -    point->lat, point->lon, box->lat_max, box->lon_max
   4.718 -  ) - d;
   4.719 -  if (cur_dist <= 0) return 0;
   4.720 -  if (cur_dist < min_dist) min_dist = cur_dist;
   4.721 -  cur_dist = pgl_distance(
   4.722 -    point->lat, point->lon, box->lat_max, box->lon_min
   4.723 -  ) - d;
   4.724 -  if (cur_dist <= 0) return 0;
   4.725 -  if (cur_dist < min_dist) min_dist = cur_dist;
   4.726 -  /* return smallest value (unless already returned zero) */
   4.727 -  return min_dist;
   4.728 -}
   4.729 -
   4.730 -
   4.731 -/*----------------------------*
   4.732 - *  fractal geographic index  *
   4.733 - *----------------------------*/
   4.734 -
   4.735 -/* number of bytes used for geographic (center) position in keys */
   4.736 -#define PGL_KEY_LATLON_BYTELEN 7
   4.737 -
   4.738 -/* maximum reference value for logarithmic size of geographic objects */
   4.739 -#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
   4.740 -
   4.741 -/* safety margin to avoid floating point errors in distance estimation */
   4.742 -#define PGL_FPE_SAFETY (1.0+1e-14)  /* slightly greater than 1.0 */
   4.743 -
   4.744 -/* pointer to index key (either pgl_pointkey or pgl_areakey) */
   4.745 -typedef unsigned char *pgl_keyptr;
   4.746 -
   4.747 -/* index key for points (objects with zero area) on the spheroid */
   4.748 -/* bit  0..55: interspersed bits of latitude and longitude,
   4.749 -   bit 56..57: always zero,
   4.750 -   bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */
   4.751 -typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1];
   4.752 -
   4.753 -/* index key for geographic objects on spheroid with area greater than zero */
   4.754 -/* bit  0..55: interspersed bits of latitude and longitude of center point,
   4.755 -   bit     56: always set to 1,
   4.756 -   bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.),
   4.757 -   bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to
   4.758 -               PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth
   4.759 -               = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL
   4.760 -               (with interspersed bits = 0 and node depth = 0) for keys which
   4.761 -               cover both empty and non-empty objects */
   4.762 -
   4.763 -typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2];
   4.764 -
   4.765 -/* helper macros for reading/writing index keys */
   4.766 -#define PGL_KEY_NODEDEPTH_OFFSET  PGL_KEY_LATLON_BYTELEN
   4.767 -#define PGL_KEY_OBJSIZE_OFFSET    (PGL_KEY_NODEDEPTH_OFFSET+1)
   4.768 -#define PGL_POINTKEY_MAXDEPTH     (PGL_KEY_LATLON_BYTELEN*8)
   4.769 -#define PGL_AREAKEY_MAXDEPTH      (2*PGL_POINTKEY_MAXDEPTH+1)
   4.770 -#define PGL_AREAKEY_MAXOBJSIZE    (PGL_POINTKEY_MAXDEPTH+1)
   4.771 -#define PGL_AREAKEY_TYPEMASK      0x80
   4.772 -#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8)))
   4.773 -#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \
   4.774 -                                  ( PGL_KEY_LATLONBIT(key1, n) ^ \
   4.775 -                                    PGL_KEY_LATLONBIT(key2, n) )
   4.776 -#define PGL_KEY_IS_AREAKEY(key)   ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
   4.777 -                                    PGL_AREAKEY_TYPEMASK)
   4.778 -#define PGL_KEY_NODEDEPTH(key)    ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
   4.779 -                                    (PGL_AREAKEY_TYPEMASK-1))
   4.780 -#define PGL_KEY_OBJSIZE(key)      ((key)[PGL_KEY_OBJSIZE_OFFSET])
   4.781 -#define PGL_KEY_OBJSIZE_EMPTY     126
   4.782 -#define PGL_KEY_OBJSIZE_UNIVERSAL 127
   4.783 -#define PGL_KEY_IS_EMPTY(key)     ( PGL_KEY_IS_AREAKEY(key) && \
   4.784 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
   4.785 -                                    PGL_KEY_OBJSIZE_EMPTY )
   4.786 -#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \
   4.787 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
   4.788 -                                    PGL_KEY_OBJSIZE_UNIVERSAL )
   4.789 -
   4.790 -/* set area key to match empty objects only */
   4.791 -static void pgl_key_set_empty(pgl_keyptr key) {
   4.792 -  memset(key, 0, sizeof(pgl_areakey));
   4.793 -  /* Note: setting node depth to maximum is required for picksplit function */
   4.794 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
   4.795 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY;
   4.796 -}
   4.797 -
   4.798 -/* set area key to match any object (including empty objects) */
   4.799 -static void pgl_key_set_universal(pgl_keyptr key) {
   4.800 -  memset(key, 0, sizeof(pgl_areakey));
   4.801 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK;
   4.802 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL;
   4.803 -}
   4.804 -
   4.805 -/* convert a point on earth into a max-depth key to be used in index */
   4.806 -static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) {
   4.807 -  double lat = point->lat;
   4.808 -  double lon = point->lon;
   4.809 -  int i;
   4.810 -  /* clear latitude and longitude bits */
   4.811 -  memset(key, 0, PGL_KEY_LATLON_BYTELEN);
   4.812 -  /* set node depth to maximum and type bit to zero */
   4.813 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH;
   4.814 -  /* iterate over all latitude/longitude bit pairs */
   4.815 -  for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) {
   4.816 -    /* determine latitude bit */
   4.817 -    if (lat >= 0) {
   4.818 -      key[i/4] |= 0x80 >> (2*(i%4));
   4.819 -      lat *= 2; lat -= 90;
   4.820 -    } else {
   4.821 -      lat *= 2; lat += 90;
   4.822 -    }
   4.823 -    /* determine longitude bit */
   4.824 -    if (lon >= 0) {
   4.825 -      key[i/4] |= 0x80 >> (2*(i%4)+1);
   4.826 -      lon *= 2; lon -= 180;
   4.827 -    } else {
   4.828 -      lon *= 2; lon += 180;
   4.829 -    }
   4.830 -  }
   4.831 -}
   4.832 -
   4.833 -/* convert a circle on earth into a max-depth key to be used in an index */
   4.834 -static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) {
   4.835 -  /* handle special case of empty circle */
   4.836 -  if (circle->radius < 0) {
   4.837 -    pgl_key_set_empty(key);
   4.838 -    return;
   4.839 -  }
   4.840 -  /* perform same action as for point keys */
   4.841 -  pgl_point_to_key(&(circle->center), key);
   4.842 -  /* but overwrite type and node depth to fit area index key */
   4.843 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
   4.844 -  /* check if radius is greater than (or equal to) reference size */
   4.845 -  /* (treat equal values as greater values for numerical safety) */
   4.846 -  if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) {
   4.847 -    /* if yes, set logarithmic size to zero */
   4.848 -    key[PGL_KEY_OBJSIZE_OFFSET] = 0;
   4.849 -  } else {
   4.850 -    /* otherwise, determine logarithmic size iteratively */
   4.851 -    /* (one step is equivalent to a factor of sqrt(2)) */
   4.852 -    double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2;
   4.853 -    int objsize = 1;
   4.854 -    while (objsize < PGL_AREAKEY_MAXOBJSIZE) {
   4.855 -      /* stop when radius is greater than (or equal to) adjusted reference */
   4.856 -      /* (treat equal values as greater values for numerical safety) */
   4.857 -      if (circle->radius >= reference) break;
   4.858 -      reference /= M_SQRT2;
   4.859 -      objsize++;
   4.860 -    }
   4.861 -    /* set logarithmic size to determined value */
   4.862 -    key[PGL_KEY_OBJSIZE_OFFSET] = objsize;
   4.863 -  }
   4.864 -}
   4.865 -
   4.866 -/* check if one key is subkey of another key or vice versa */
   4.867 -static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) {
   4.868 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
   4.869 -  /* determine smallest depth */
   4.870 -  int depth1 = PGL_KEY_NODEDEPTH(key1);
   4.871 -  int depth2 = PGL_KEY_NODEDEPTH(key2);
   4.872 -  int depth = (depth1 < depth2) ? depth1 : depth2;
   4.873 -  /* check if keys are area keys (assuming that both keys have same type) */
   4.874 -  if (PGL_KEY_IS_AREAKEY(key1)) {
   4.875 -    int j = 0;  /* bit offset for logarithmic object size bits */
   4.876 -    int k = 0;  /* bit offset for latitude and longitude */
   4.877 -    /* fetch logarithmic object size information */
   4.878 -    int objsize1 = PGL_KEY_OBJSIZE(key1);
   4.879 -    int objsize2 = PGL_KEY_OBJSIZE(key2);
   4.880 -    /* handle special cases for empty objects (universal and empty keys) */
   4.881 -    if (
   4.882 -      objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL ||
   4.883 -      objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL
   4.884 -    ) return true;
   4.885 -    if (
   4.886 -      objsize1 == PGL_KEY_OBJSIZE_EMPTY ||
   4.887 -      objsize2 == PGL_KEY_OBJSIZE_EMPTY
   4.888 -    ) return objsize1 == objsize2;
   4.889 -    /* iterate through key bits */
   4.890 -    for (i=0; i<depth; i++) {
   4.891 -      /* every second bit is a bit describing the object size */
   4.892 -      if (i%2 == 0) {
   4.893 -        /* check if object size bit is different in both keys (objsize1 and
   4.894 -           objsize2 describe the minimum index when object size bit is set) */
   4.895 -        if (
   4.896 -          (objsize1 <= j && objsize2 > j) ||
   4.897 -          (objsize2 <= j && objsize1 > j)
   4.898 -        ) {
   4.899 -          /* bit differs, therefore keys are in separate branches */
   4.900 -          return false;
   4.901 -        }
   4.902 -        /* increase bit counter for object size bits */
   4.903 -        j++;
   4.904 -      }
   4.905 -      /* all other bits describe latitude and longitude */
   4.906 -      else {
   4.907 -        /* check if bit differs in both keys */
   4.908 -        if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) {
   4.909 -          /* bit differs, therefore keys are in separate branches */
   4.910 -          return false;
   4.911 -        }
   4.912 -        /* increase bit counter for latitude/longitude bits */
   4.913 -        k++;
   4.914 -      }
   4.915 -    }
   4.916 -  }
   4.917 -  /* if not, keys are point keys */
   4.918 -  else {
   4.919 -    /* iterate through key bits */
   4.920 -    for (i=0; i<depth; i++) {
   4.921 -      /* check if bit differs in both keys */
   4.922 -      if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) {
   4.923 -        /* bit differs, therefore keys are in separate branches */
   4.924 -        return false;
   4.925 -      }
   4.926 -    }
   4.927 -  }
   4.928 -  /* return true because keys are in the same branch */
   4.929 -  return true;
   4.930 -}
   4.931 -
   4.932 -/* combine two keys into new key which covers both original keys */
   4.933 -/* (result stored in first argument) */
   4.934 -static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) {
   4.935 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
   4.936 -  /* determine smallest depth */
   4.937 -  int depth1 = PGL_KEY_NODEDEPTH(dst);
   4.938 -  int depth2 = PGL_KEY_NODEDEPTH(src);
   4.939 -  int depth = (depth1 < depth2) ? depth1 : depth2;
   4.940 -  /* check if keys are area keys (assuming that both keys have same type) */
   4.941 -  if (PGL_KEY_IS_AREAKEY(dst)) {
   4.942 -    pgl_areakey dstbuf = { 0, };  /* destination buffer (cleared) */
   4.943 -    int j = 0;  /* bit offset for logarithmic object size bits */
   4.944 -    int k = 0;  /* bit offset for latitude and longitude */
   4.945 -    /* fetch logarithmic object size information */
   4.946 -    int objsize1 = PGL_KEY_OBJSIZE(dst);
   4.947 -    int objsize2 = PGL_KEY_OBJSIZE(src);
   4.948 -    /* handle special cases for empty objects (universal and empty keys) */
   4.949 -    if (
   4.950 -      objsize1 > PGL_AREAKEY_MAXOBJSIZE ||
   4.951 -      objsize2 > PGL_AREAKEY_MAXOBJSIZE
   4.952 -    ) {
   4.953 -      if (
   4.954 -        objsize1 == PGL_KEY_OBJSIZE_EMPTY &&
   4.955 -        objsize2 == PGL_KEY_OBJSIZE_EMPTY
   4.956 -      ) pgl_key_set_empty(dst);
   4.957 -      else pgl_key_set_universal(dst);
   4.958 -      return;
   4.959 -    }
   4.960 -    /* iterate through key bits */
   4.961 -    for (i=0; i<depth; i++) {
   4.962 -      /* every second bit is a bit describing the object size */
   4.963 -      if (i%2 == 0) {
   4.964 -        /* increase bit counter for object size bits first */
   4.965 -        /* (handy when setting objsize variable) */
   4.966 -        j++;
   4.967 -        /* check if object size bit is set in neither key */
   4.968 -        if (objsize1 >= j && objsize2 >= j) {
   4.969 -          /* set objsize in destination buffer to indicate that size bit is
   4.970 -             unset in destination buffer at the current bit position */
   4.971 -          dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j;
   4.972 -        }
   4.973 -        /* break if object size bit is set in one key only */
   4.974 -        else if (objsize1 >= j || objsize2 >= j) break;
   4.975 -      }
   4.976 -      /* all other bits describe latitude and longitude */
   4.977 -      else {
   4.978 -        /* break if bit differs in both keys */
   4.979 -        if (PGL_KEY_LATLONBIT(dst, k)) {
   4.980 -          if (!PGL_KEY_LATLONBIT(src, k)) break;
   4.981 -          /* but set bit in destination buffer if bit is set in both keys */
   4.982 -          dstbuf[k/8] |= 0x80 >> (k%8);
   4.983 -        } else if (PGL_KEY_LATLONBIT(src, k)) break;
   4.984 -        /* increase bit counter for latitude/longitude bits */
   4.985 -        k++;
   4.986 -      }
   4.987 -    }
   4.988 -    /* set common node depth and type bit (type bit = 1) */
   4.989 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i;
   4.990 -    /* copy contents of destination buffer to first key */
   4.991 -    memcpy(dst, dstbuf, sizeof(pgl_areakey));
   4.992 -  }
   4.993 -  /* if not, keys are point keys */
   4.994 -  else {
   4.995 -    pgl_pointkey dstbuf = { 0, };  /* destination buffer (cleared) */
   4.996 -    /* iterate through key bits */
   4.997 -    for (i=0; i<depth; i++) {
   4.998 -      /* break if bit differs in both keys */
   4.999 -      if (PGL_KEY_LATLONBIT(dst, i)) {
  4.1000 -        if (!PGL_KEY_LATLONBIT(src, i)) break;
  4.1001 -        /* but set bit in destination buffer if bit is set in both keys */
  4.1002 -        dstbuf[i/8] |= 0x80 >> (i%8);
  4.1003 -      } else if (PGL_KEY_LATLONBIT(src, i)) break;
  4.1004 -    }
  4.1005 -    /* set common node depth (type bit = 0) */
  4.1006 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i;
  4.1007 -    /* copy contents of destination buffer to first key */
  4.1008 -    memcpy(dst, dstbuf, sizeof(pgl_pointkey));
  4.1009 -  }
  4.1010 -}
  4.1011 -
  4.1012 -/* determine center(!) boundaries and radius estimation of index key */
  4.1013 -static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) {
  4.1014 -  int i;
  4.1015 -  /* determine node depth */
  4.1016 -  int depth = PGL_KEY_NODEDEPTH(key);
  4.1017 -  /* center point of possible result */
  4.1018 -  double lat = 0;
  4.1019 -  double lon = 0;
  4.1020 -  /* maximum distance of real center point from key center */
  4.1021 -  double dlat = 90;
  4.1022 -  double dlon = 180;
  4.1023 -  /* maximum radius of contained objects */
  4.1024 -  double radius = 0;  /* always return zero for point index keys */
  4.1025 -  /* check if key is area key */
  4.1026 -  if (PGL_KEY_IS_AREAKEY(key)) {
  4.1027 -    /* get logarithmic object size */
  4.1028 -    int objsize = PGL_KEY_OBJSIZE(key);
  4.1029 -    /* handle special cases for empty objects (universal and empty keys) */
  4.1030 -    if (objsize == PGL_KEY_OBJSIZE_EMPTY) {
  4.1031 -      pgl_box_set_empty(box);
  4.1032 -      return 0;
  4.1033 -    } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) {
  4.1034 -      box->lat_min = -90;
  4.1035 -      box->lat_max =  90;
  4.1036 -      box->lon_min = -180;
  4.1037 -      box->lon_max =  180;
  4.1038 -      return 0;  /* any value >= 0 would do */
  4.1039 -    }
  4.1040 -    /* calculate maximum possible radius of objects covered by the given key */
  4.1041 -    if (objsize == 0) radius = INFINITY;
  4.1042 -    else {
  4.1043 -      radius = PGL_AREAKEY_REFOBJSIZE;
  4.1044 -      while (--objsize) radius /= M_SQRT2;
  4.1045 -    }
  4.1046 -    /* iterate over latitude and longitude bits in key */
  4.1047 -    /* (every second bit is a latitude or longitude bit) */
  4.1048 -    for (i=0; i<depth/2; i++) {
  4.1049 -      /* check if latitude bit */
  4.1050 -      if (i%2 == 0) {
  4.1051 -        /* cut latitude dimension in half */
  4.1052 -        dlat /= 2;
  4.1053 -        /* increase center latitude if bit is 1, otherwise decrease */
  4.1054 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  4.1055 -        else lat -= dlat;
  4.1056 -      }
  4.1057 -      /* otherwise longitude bit */
  4.1058 -      else {
  4.1059 -        /* cut longitude dimension in half */
  4.1060 -        dlon /= 2;
  4.1061 -        /* increase center longitude if bit is 1, otherwise decrease */
  4.1062 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  4.1063 -        else lon -= dlon;
  4.1064 -      }
  4.1065 -    }
  4.1066 -  }
  4.1067 -  /* if not, keys are point keys */
  4.1068 -  else {
  4.1069 -    /* iterate over all bits in key */
  4.1070 -    for (i=0; i<depth; i++) {
  4.1071 -      /* check if latitude bit */
  4.1072 -      if (i%2 == 0) {
  4.1073 -        /* cut latitude dimension in half */
  4.1074 -        dlat /= 2;
  4.1075 -        /* increase center latitude if bit is 1, otherwise decrease */
  4.1076 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  4.1077 -        else lat -= dlat;
  4.1078 -      }
  4.1079 -      /* otherwise longitude bit */
  4.1080 -      else {
  4.1081 -        /* cut longitude dimension in half */
  4.1082 -        dlon /= 2;
  4.1083 -        /* increase center longitude if bit is 1, otherwise decrease */
  4.1084 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  4.1085 -        else lon -= dlon;
  4.1086 -      }
  4.1087 -    }
  4.1088 -  }
  4.1089 -  /* calculate boundaries from center point and remaining dlat and dlon */
  4.1090 -  /* (return values through pointer to box) */
  4.1091 -  box->lat_min = lat - dlat;
  4.1092 -  box->lat_max = lat + dlat;
  4.1093 -  box->lon_min = lon - dlon;
  4.1094 -  box->lon_max = lon + dlon;
  4.1095 -  /* return radius (as a function return value) */
  4.1096 -  return radius;
  4.1097 -}
  4.1098 -
  4.1099 -/* estimator function for distance between point and index key */
  4.1100 -/* allowed to return smaller values than actually correct */
  4.1101 -static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
  4.1102 -  pgl_box box;  /* center(!) bounding box of area index key */
  4.1103 -  /* calculate center(!) bounding box and maximum radius of objects covered
  4.1104 -     by area index key (radius is zero for point index keys) */
  4.1105 -  double distance = pgl_key_to_box(key, &box);
  4.1106 -  /* calculate estimated distance between bounding box of center point of
  4.1107 -     indexed object and point passed as second argument, then substract maximum
  4.1108 -     radius of objects covered by index key */
  4.1109 -  /* (use PGL_FPE_SAFETY factor to cope with minor floating point errors) */
  4.1110 -  distance = (
  4.1111 -    pgl_estimate_point_box_distance(point, &box) / PGL_FPE_SAFETY -
  4.1112 -    distance * PGL_FPE_SAFETY
  4.1113 -  );
  4.1114 -  /* truncate negative results to zero */
  4.1115 -  if (distance <= 0) distance = 0;
  4.1116 -  /* return result */
  4.1117 -  return distance;
  4.1118 -}
  4.1119 -
  4.1120 -
  4.1121 -/*---------------------------------*
  4.1122 - *  helper functions for text I/O  *
  4.1123 - *---------------------------------*/
  4.1124 -
  4.1125 -#define PGL_NUMBUFLEN 64  /* buffer size for number to string conversion */
  4.1126 -
  4.1127 -/* convert floating point number to string (round-trip safe) */
  4.1128 -static void pgl_print_float(char *buf, double flt) {
  4.1129 -  /* check if number is integral */
  4.1130 -  if (trunc(flt) == flt) {
  4.1131 -    /* for integral floats use maximum precision */
  4.1132 -    snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  4.1133 -  } else {
  4.1134 -    /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */
  4.1135 -    snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt);
  4.1136 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt);
  4.1137 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  4.1138 -  }
  4.1139 -}
  4.1140 -
  4.1141 -/* convert latitude floating point number (in degrees) to string */
  4.1142 -static void pgl_print_lat(char *buf, double lat) {
  4.1143 -  if (signbit(lat)) {
  4.1144 -    /* treat negative latitudes (including -0) as south */
  4.1145 -    snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat);
  4.1146 -  } else {
  4.1147 -    /* treat positive latitudes (including +0) as north */
  4.1148 -    snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat);
  4.1149 -  }
  4.1150 -}
  4.1151 -
  4.1152 -/* convert longitude floating point number (in degrees) to string */
  4.1153 -static void pgl_print_lon(char *buf, double lon) {
  4.1154 -  if (signbit(lon)) {
  4.1155 -    /* treat negative longitudes (including -0) as west */
  4.1156 -    snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon);
  4.1157 -  } else {
  4.1158 -    /* treat positive longitudes (including +0) as east */
  4.1159 -    snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon);
  4.1160 -  }
  4.1161 -}
  4.1162 -
  4.1163 -/* bit masks used as return value of pgl_scan() function */
  4.1164 -#define PGL_SCAN_NONE 0      /* no value has been parsed */
  4.1165 -#define PGL_SCAN_LAT (1<<0)  /* latitude has been parsed */
  4.1166 -#define PGL_SCAN_LON (1<<1)  /* longitude has been parsed */
  4.1167 -#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON)  /* bitwise OR of both */
  4.1168 -
  4.1169 -/* parse a coordinate (can be latitude or longitude) */
  4.1170 -static int pgl_scan(char **str, double *lat, double *lon) {
  4.1171 -  double val;
  4.1172 -  int len;
  4.1173 -  if (
  4.1174 -    sscanf(*str, " N %lf %n", &val, &len) ||
  4.1175 -    sscanf(*str, " n %lf %n", &val, &len)
  4.1176 -  ) {
  4.1177 -    *str += len; *lat = val; return PGL_SCAN_LAT;
  4.1178 -  }
  4.1179 -  if (
  4.1180 -    sscanf(*str, " S %lf %n", &val, &len) ||
  4.1181 -    sscanf(*str, " s %lf %n", &val, &len)
  4.1182 -  ) {
  4.1183 -    *str += len; *lat = -val; return PGL_SCAN_LAT;
  4.1184 -  }
  4.1185 -  if (
  4.1186 -    sscanf(*str, " E %lf %n", &val, &len) ||
  4.1187 -    sscanf(*str, " e %lf %n", &val, &len)
  4.1188 -  ) {
  4.1189 -    *str += len; *lon = val; return PGL_SCAN_LON;
  4.1190 -  }
  4.1191 -  if (
  4.1192 -    sscanf(*str, " W %lf %n", &val, &len) ||
  4.1193 -    sscanf(*str, " w %lf %n", &val, &len)
  4.1194 -  ) {
  4.1195 -    *str += len; *lon = -val; return PGL_SCAN_LON;
  4.1196 -  }
  4.1197 -  return PGL_SCAN_NONE;
  4.1198 -}
  4.1199 -
  4.1200 -
  4.1201 -/*-----------------*
  4.1202 - *  SQL functions  *
  4.1203 - *-----------------*/
  4.1204 -
  4.1205 -/* Note: These function names use "epoint", "ebox", etc. notation here instead
  4.1206 -   of "point", "box", etc. in order to distinguish them from any previously
  4.1207 -   defined functions. */
  4.1208 -
  4.1209 -/* function needed for dummy types and/or not implemented features */
  4.1210 -PG_FUNCTION_INFO_V1(pgl_notimpl);
  4.1211 -Datum pgl_notimpl(PG_FUNCTION_ARGS) {
  4.1212 -  ereport(ERROR, (errmsg("not implemented by pgLatLon")));
  4.1213 -}
  4.1214 -
  4.1215 -/* set point to latitude and longitude (including checks) */
  4.1216 -static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) {
  4.1217 -  /* reject infinite or NaN values */
  4.1218 -  if (!isfinite(lat) || !isfinite(lon)) {
  4.1219 -    ereport(ERROR, (
  4.1220 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1221 -      errmsg("epoint requires finite coordinates")
  4.1222 -    ));
  4.1223 -  }
  4.1224 -  /* check latitude bounds */
  4.1225 -  if (lat < -90) {
  4.1226 -    ereport(WARNING, (errmsg("latitude exceeds south pole")));
  4.1227 -    lat = -90;
  4.1228 -  } else if (lat > 90) {
  4.1229 -    ereport(WARNING, (errmsg("latitude exceeds north pole")));
  4.1230 -    lat = 90;
  4.1231 -  }
  4.1232 -  /* check longitude bounds */
  4.1233 -  if (lon < -180) {
  4.1234 -    ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized")));
  4.1235 -    lon += 360 - trunc(lon / 360) * 360;
  4.1236 -  } else if (lon > 180) {
  4.1237 -    ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized")));
  4.1238 -    lon -= 360 + trunc(lon / 360) * 360;
  4.1239 -  }
  4.1240 -  /* store rounded latitude/longitude values for round-trip safety */
  4.1241 -  point->lat = pgl_round(lat);
  4.1242 -  point->lon = pgl_round(lon);
  4.1243 -}
  4.1244 -
  4.1245 -/* create point ("epoint" in SQL) from latitude and longitude */
  4.1246 -PG_FUNCTION_INFO_V1(pgl_create_epoint);
  4.1247 -Datum pgl_create_epoint(PG_FUNCTION_ARGS) {
  4.1248 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  4.1249 -  pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1));
  4.1250 -  PG_RETURN_POINTER(point);
  4.1251 -}
  4.1252 -
  4.1253 -/* parse point ("epoint" in SQL) */
  4.1254 -/* format: '[NS]<float> [EW]<float>' */
  4.1255 -PG_FUNCTION_INFO_V1(pgl_epoint_in);
  4.1256 -Datum pgl_epoint_in(PG_FUNCTION_ARGS) {
  4.1257 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  4.1258 -  char *strptr = str;  /* current position within string */
  4.1259 -  int done = 0;        /* bit mask storing if latitude or longitude was read */
  4.1260 -  double lat, lon;     /* parsed values as double precision floats */
  4.1261 -  pgl_point *point;    /* return value (to be palloc'ed) */
  4.1262 -  /* parse two floats (each latitude or longitude) separated by white-space */
  4.1263 -  done |= pgl_scan(&strptr, &lat, &lon);
  4.1264 -  if (strptr != str && isspace(strptr[-1])) {
  4.1265 -    done |= pgl_scan(&strptr, &lat, &lon);
  4.1266 -  }
  4.1267 -  /* require end of string, and latitude and longitude parsed successfully */
  4.1268 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  4.1269 -    ereport(ERROR, (
  4.1270 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1271 -      errmsg("invalid input syntax for type epoint: \"%s\"", str)
  4.1272 -    ));
  4.1273 -  }
  4.1274 -  /* allocate memory for result */
  4.1275 -  point = (pgl_point *)palloc(sizeof(pgl_point));
  4.1276 -  /* set latitude and longitude (and perform checks) */
  4.1277 -  pgl_epoint_set_latlon(point, lat, lon);
  4.1278 -  /* return result */
  4.1279 -  PG_RETURN_POINTER(point);
  4.1280 -}
  4.1281 -
  4.1282 -/* create box ("ebox" in SQL) that is empty */
  4.1283 -PG_FUNCTION_INFO_V1(pgl_create_empty_ebox);
  4.1284 -Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) {
  4.1285 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1286 -  pgl_box_set_empty(box);
  4.1287 -  PG_RETURN_POINTER(box);
  4.1288 -}
  4.1289 -
  4.1290 -/* set box to given boundaries (including checks) */
  4.1291 -static void pgl_ebox_set_boundaries(
  4.1292 -  pgl_box *box,
  4.1293 -  double lat_min, double lat_max, double lon_min, double lon_max
  4.1294 -) {
  4.1295 -  /* if minimum latitude is greater than maximum latitude, return empty box */
  4.1296 -  if (lat_min > lat_max) {
  4.1297 -    pgl_box_set_empty(box);
  4.1298 -    return;
  4.1299 -  }
  4.1300 -  /* otherwise reject infinite or NaN values */
  4.1301 -  if (
  4.1302 -    !isfinite(lat_min) || !isfinite(lat_max) ||
  4.1303 -    !isfinite(lon_min) || !isfinite(lon_max)
  4.1304 -  ) {
  4.1305 -    ereport(ERROR, (
  4.1306 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1307 -      errmsg("ebox requires finite coordinates")
  4.1308 -    ));
  4.1309 -  }
  4.1310 -  /* check latitude bounds */
  4.1311 -  if (lat_max < -90) {
  4.1312 -    ereport(WARNING, (errmsg("northern latitude exceeds south pole")));
  4.1313 -    lat_max = -90;
  4.1314 -  } else if (lat_max > 90) {
  4.1315 -    ereport(WARNING, (errmsg("northern latitude exceeds north pole")));
  4.1316 -    lat_max = 90;
  4.1317 -  }
  4.1318 -  if (lat_min < -90) {
  4.1319 -    ereport(WARNING, (errmsg("southern latitude exceeds south pole")));
  4.1320 -    lat_min = -90;
  4.1321 -  } else if (lat_min > 90) {
  4.1322 -    ereport(WARNING, (errmsg("southern latitude exceeds north pole")));
  4.1323 -    lat_min = 90;
  4.1324 -  }
  4.1325 -  /* check if all longitudes are included */
  4.1326 -  if (lon_max - lon_min >= 360) {
  4.1327 -    if (lon_max - lon_min > 360) ereport(WARNING, (
  4.1328 -      errmsg("longitude coverage greater than 360 degrees")
  4.1329 -    ));
  4.1330 -    lon_min = -180;
  4.1331 -    lon_max = 180;
  4.1332 -  } else {
  4.1333 -    /* normalize longitude bounds */
  4.1334 -    if      (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360;
  4.1335 -    else if (lon_min >  180) lon_min -= 360 + trunc(lon_min / 360) * 360;
  4.1336 -    if      (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360;
  4.1337 -    else if (lon_max >  180) lon_max -= 360 + trunc(lon_max / 360) * 360;
  4.1338 -  }
  4.1339 -  /* store rounded latitude/longitude values for round-trip safety */
  4.1340 -  box->lat_min = pgl_round(lat_min);
  4.1341 -  box->lat_max = pgl_round(lat_max);
  4.1342 -  box->lon_min = pgl_round(lon_min);
  4.1343 -  box->lon_max = pgl_round(lon_max);
  4.1344 -  /* ensure that rounding does not change orientation */
  4.1345 -  if (lon_min > lon_max && box->lon_min == box->lon_max) {
  4.1346 -    box->lon_min = -180;
  4.1347 -    box->lon_max = 180;
  4.1348 -  }
  4.1349 -}
  4.1350 -
  4.1351 -/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */
  4.1352 -PG_FUNCTION_INFO_V1(pgl_create_ebox);
  4.1353 -Datum pgl_create_ebox(PG_FUNCTION_ARGS) {
  4.1354 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1355 -  pgl_ebox_set_boundaries(
  4.1356 -    box,
  4.1357 -    PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1),
  4.1358 -    PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3)
  4.1359 -  );
  4.1360 -  PG_RETURN_POINTER(box);
  4.1361 -}
  4.1362 -
  4.1363 -/* create box ("ebox" in SQL) from two points ("epoint"s) */
  4.1364 -/* (can not be used to cover a longitude range of more than 120 degrees) */
  4.1365 -PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints);
  4.1366 -Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) {
  4.1367 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  4.1368 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  4.1369 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1370 -  double lat_min, lat_max, lon_min, lon_max;
  4.1371 -  double dlon;  /* longitude range (delta longitude) */
  4.1372 -  /* order latitude and longitude boundaries */
  4.1373 -  if (point2->lat < point1->lat) {
  4.1374 -    lat_min = point2->lat;
  4.1375 -    lat_max = point1->lat;
  4.1376 -  } else {
  4.1377 -    lat_min = point1->lat;
  4.1378 -    lat_max = point2->lat;
  4.1379 -  }
  4.1380 -  if (point2->lon < point1->lon) {
  4.1381 -    lon_min = point2->lon;
  4.1382 -    lon_max = point1->lon;
  4.1383 -  } else {
  4.1384 -    lon_min = point1->lon;
  4.1385 -    lon_max = point2->lon;
  4.1386 -  }
  4.1387 -  /* calculate longitude range (round to avoid floating point errors) */
  4.1388 -  dlon = pgl_round(lon_max - lon_min);
  4.1389 -  /* determine east-west direction */
  4.1390 -  if (dlon >= 240) {
  4.1391 -    /* assume that 180th meridian is crossed and swap min/max longitude */
  4.1392 -    double swap = lon_min; lon_min = lon_max; lon_max = swap;
  4.1393 -  } else if (dlon > 120) {
  4.1394 -    /* unclear orientation since delta longitude > 120 */
  4.1395 -    ereport(ERROR, (
  4.1396 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1397 -      errmsg("can not determine east/west orientation for ebox")
  4.1398 -    ));
  4.1399 -  }
  4.1400 -  /* use boundaries to setup box (and perform checks) */
  4.1401 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  4.1402 -  /* return result */
  4.1403 -  PG_RETURN_POINTER(box);
  4.1404 -}
  4.1405 -
  4.1406 -/* parse box ("ebox" in SQL) */
  4.1407 -/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>'
  4.1408 -       or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */
  4.1409 -PG_FUNCTION_INFO_V1(pgl_ebox_in);
  4.1410 -Datum pgl_ebox_in(PG_FUNCTION_ARGS) {
  4.1411 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  4.1412 -  char *str_lower;     /* lower case version of input string */
  4.1413 -  char *strptr;        /* current position within string */
  4.1414 -  int valid;           /* number of valid chars */
  4.1415 -  int done;            /* specifies if latitude or longitude was read */
  4.1416 -  double val;          /* temporary variable */
  4.1417 -  int lat_count = 0;   /* count of latitude values parsed */
  4.1418 -  int lon_count = 0;   /* count of longitufde values parsed */
  4.1419 -  double lat_min, lat_max, lon_min, lon_max;  /* see pgl_box struct */
  4.1420 -  pgl_box *box;        /* return value (to be palloc'ed) */
  4.1421 -  /* lowercase input */
  4.1422 -  str_lower = psprintf("%s", str);
  4.1423 -  for (strptr=str_lower; *strptr; strptr++) {
  4.1424 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  4.1425 -  }
  4.1426 -  /* reset reading position to start of (lowercase) string */
  4.1427 -  strptr = str_lower;
  4.1428 -  /* check if empty box */
  4.1429 -  valid = 0;
  4.1430 -  sscanf(strptr, " empty %n", &valid);
  4.1431 -  if (valid && strptr[valid] == 0) {
  4.1432 -    /* allocate and return empty box */
  4.1433 -    box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1434 -    pgl_box_set_empty(box);
  4.1435 -    PG_RETURN_POINTER(box);
  4.1436 -  }
  4.1437 -  /* demand four blocks separated by whitespace */
  4.1438 -  valid = 0;
  4.1439 -  sscanf(strptr, " %*s %*s %*s %*s %n", &valid);
  4.1440 -  /* if four blocks separated by whitespace exist, parse those blocks */
  4.1441 -  if (strptr[valid] == 0) while (strptr[0]) {
  4.1442 -    /* parse either latitude or longitude (whichever found in input string) */
  4.1443 -    done = pgl_scan(&strptr, &val, &val);
  4.1444 -    /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */
  4.1445 -    if (done == PGL_SCAN_LAT) {
  4.1446 -      if (!lat_count) lat_min = val; else lat_max = val;
  4.1447 -      lat_count++;
  4.1448 -    } else if (done == PGL_SCAN_LON) {
  4.1449 -      if (!lon_count) lon_min = val; else lon_max = val;
  4.1450 -      lon_count++;
  4.1451 -    } else {
  4.1452 -      break;
  4.1453 -    }
  4.1454 -  }
  4.1455 -  /* require end of string, and two latitude and two longitude values */
  4.1456 -  if (strptr[0] || lat_count != 2 || lon_count != 2) {
  4.1457 -    ereport(ERROR, (
  4.1458 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1459 -      errmsg("invalid input syntax for type ebox: \"%s\"", str)
  4.1460 -    ));
  4.1461 -  }
  4.1462 -  /* free lower case string */
  4.1463 -  pfree(str_lower);
  4.1464 -  /* order boundaries (maximum greater than minimum) */
  4.1465 -  if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; }
  4.1466 -  if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; }
  4.1467 -  /* allocate memory for result */
  4.1468 -  box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1469 -  /* set boundaries (and perform checks) */
  4.1470 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  4.1471 -  /* return result */
  4.1472 -  PG_RETURN_POINTER(box);
  4.1473 -}
  4.1474 -
  4.1475 -/* set circle to given latitude, longitude, and radius (including checks) */
  4.1476 -static void pgl_ecircle_set_latlon_radius(
  4.1477 -  pgl_circle *circle, double lat, double lon, double radius
  4.1478 -) {
  4.1479 -  /* set center point (including checks) */
  4.1480 -  pgl_epoint_set_latlon(&(circle->center), lat, lon);
  4.1481 -  /* handle non-positive radius */
  4.1482 -  if (isnan(radius)) {
  4.1483 -    ereport(ERROR, (
  4.1484 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1485 -      errmsg("invalid radius for ecircle")
  4.1486 -    ));
  4.1487 -  }
  4.1488 -  if (radius == 0) radius = 0;  /* avoids -0 */
  4.1489 -  else if (radius < 0) {
  4.1490 -    if (isfinite(radius)) {
  4.1491 -      ereport(NOTICE, (errmsg("negative radius converted to minus infinity")));
  4.1492 -    }
  4.1493 -    radius = -INFINITY;
  4.1494 -  }
  4.1495 -  /* store radius (round-trip safety is ensured by pgl_print_float) */
  4.1496 -  circle->radius = radius;
  4.1497 -}
  4.1498 -
  4.1499 -/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */
  4.1500 -PG_FUNCTION_INFO_V1(pgl_create_ecircle);
  4.1501 -Datum pgl_create_ecircle(PG_FUNCTION_ARGS) {
  4.1502 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  4.1503 -  pgl_ecircle_set_latlon_radius(
  4.1504 -    circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2)
  4.1505 -  );
  4.1506 -  PG_RETURN_POINTER(circle);
  4.1507 -}
  4.1508 -
  4.1509 -/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */
  4.1510 -PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint);
  4.1511 -Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) {
  4.1512 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.1513 -  double radius = PG_GETARG_FLOAT8(1);
  4.1514 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  4.1515 -  /* set latitude, longitude, radius (and perform checks) */
  4.1516 -  pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius);
  4.1517 -  /* return result */
  4.1518 -  PG_RETURN_POINTER(circle);
  4.1519 -}
  4.1520 -
  4.1521 -/* parse circle ("ecircle" in SQL) */
  4.1522 -/* format: '[NS]<float> [EW]<float> <float>' */
  4.1523 -PG_FUNCTION_INFO_V1(pgl_ecircle_in);
  4.1524 -Datum pgl_ecircle_in(PG_FUNCTION_ARGS) {
  4.1525 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  4.1526 -  char *strptr = str;       /* current position within string */
  4.1527 -  double lat, lon, radius;  /* parsed values as double precision flaots */
  4.1528 -  int valid = 0;            /* number of valid chars */
  4.1529 -  int done = 0;             /* stores if latitude and/or longitude was read */
  4.1530 -  pgl_circle *circle;       /* return value (to be palloc'ed) */
  4.1531 -  /* demand three blocks separated by whitespace */
  4.1532 -  sscanf(strptr, " %*s %*s %*s %n", &valid);
  4.1533 -  /* if three blocks separated by whitespace exist, parse those blocks */
  4.1534 -  if (strptr[valid] == 0) {
  4.1535 -    /* parse latitude and longitude */
  4.1536 -    done |= pgl_scan(&strptr, &lat, &lon);
  4.1537 -    done |= pgl_scan(&strptr, &lat, &lon);
  4.1538 -    /* parse radius (while incrementing strptr by number of bytes parsed) */
  4.1539 -    valid = 0;
  4.1540 -    if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid;
  4.1541 -  }
  4.1542 -  /* require end of string and both latitude and longitude being parsed */
  4.1543 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  4.1544 -    ereport(ERROR, (
  4.1545 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1546 -      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  4.1547 -    ));
  4.1548 -  }
  4.1549 -  /* allocate memory for result */
  4.1550 -  circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  4.1551 -  /* set latitude, longitude, radius (and perform checks) */
  4.1552 -  pgl_ecircle_set_latlon_radius(circle, lat, lon, radius);
  4.1553 -  /* return result */
  4.1554 -  PG_RETURN_POINTER(circle);
  4.1555 -}
  4.1556 -
  4.1557 -/* parse cluster ("ecluster" in SQL) */
  4.1558 -PG_FUNCTION_INFO_V1(pgl_ecluster_in);
  4.1559 -Datum pgl_ecluster_in(PG_FUNCTION_ARGS) {
  4.1560 -  int i;
  4.1561 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  4.1562 -  char *str_lower;         /* lower case version of input string */
  4.1563 -  char *strptr;            /* pointer to current reading position of input */
  4.1564 -  int npoints_total = 0;   /* total number of points in cluster */
  4.1565 -  int nentries = 0;        /* total number of entries */
  4.1566 -  pgl_newentry *entries;   /* array of pgl_newentry to create pgl_cluster */
  4.1567 -  int entries_buflen = 4;  /* maximum number of elements in entries array */
  4.1568 -  int valid;               /* number of valid chars processed */
  4.1569 -  double lat, lon;         /* latitude and longitude of parsed point */
  4.1570 -  int entrytype;           /* current entry type */
  4.1571 -  int npoints;             /* number of points in current entry */
  4.1572 -  pgl_point *points;       /* array of pgl_point for pgl_newentry */
  4.1573 -  int points_buflen;       /* maximum number of elements in points array */
  4.1574 -  int done;                /* return value of pgl_scan function */
  4.1575 -  pgl_cluster *cluster;    /* created cluster */
  4.1576 -  /* lowercase input */
  4.1577 -  str_lower = psprintf("%s", str);
  4.1578 -  for (strptr=str_lower; *strptr; strptr++) {
  4.1579 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  4.1580 -  }
  4.1581 -  /* reset reading position to start of (lowercase) string */
  4.1582 -  strptr = str_lower;
  4.1583 -  /* allocate initial buffer for entries */
  4.1584 -  entries = palloc(entries_buflen * sizeof(pgl_newentry));
  4.1585 -  /* parse until end of string */
  4.1586 -  while (strptr[0]) {
  4.1587 -    /* require previous white-space or closing parenthesis before next token */
  4.1588 -    if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') {
  4.1589 -      goto pgl_ecluster_in_error;
  4.1590 -    }
  4.1591 -    /* ignore token "empty" */
  4.1592 -    valid = 0; sscanf(strptr, " empty %n", &valid);
  4.1593 -    if (valid) { strptr += valid; continue; }
  4.1594 -    /* test for "point" token */
  4.1595 -    valid = 0; sscanf(strptr, " point ( %n", &valid);
  4.1596 -    if (valid) {
  4.1597 -      strptr += valid;
  4.1598 -      entrytype = PGL_ENTRY_POINT;
  4.1599 -      goto pgl_ecluster_in_type_ok;
  4.1600 -    }
  4.1601 -    /* test for "path" token */
  4.1602 -    valid = 0; sscanf(strptr, " path ( %n", &valid);
  4.1603 -    if (valid) {
  4.1604 -      strptr += valid;
  4.1605 -      entrytype = PGL_ENTRY_PATH;
  4.1606 -      goto pgl_ecluster_in_type_ok;
  4.1607 -    }
  4.1608 -    /* test for "outline" token */
  4.1609 -    valid = 0; sscanf(strptr, " outline ( %n", &valid);
  4.1610 -    if (valid) {
  4.1611 -      strptr += valid;
  4.1612 -      entrytype = PGL_ENTRY_OUTLINE;
  4.1613 -      goto pgl_ecluster_in_type_ok;
  4.1614 -    }
  4.1615 -    /* test for "polygon" token */
  4.1616 -    valid = 0; sscanf(strptr, " polygon ( %n", &valid);
  4.1617 -    if (valid) {
  4.1618 -      strptr += valid;
  4.1619 -      entrytype = PGL_ENTRY_POLYGON;
  4.1620 -      goto pgl_ecluster_in_type_ok;
  4.1621 -    }
  4.1622 -    /* error if no valid token found */
  4.1623 -    goto pgl_ecluster_in_error;
  4.1624 -    pgl_ecluster_in_type_ok:
  4.1625 -    /* check if pgl_newentry array needs to grow */
  4.1626 -    if (nentries == entries_buflen) {
  4.1627 -      pgl_newentry *newbuf;
  4.1628 -      entries_buflen *= 2;
  4.1629 -      newbuf = palloc(entries_buflen * sizeof(pgl_newentry));
  4.1630 -      memcpy(newbuf, entries, nentries * sizeof(pgl_newentry));
  4.1631 -      pfree(entries);
  4.1632 -      entries = newbuf;
  4.1633 -    }
  4.1634 -    /* reset number of points for current entry */
  4.1635 -    npoints = 0;
  4.1636 -    /* allocate array for points */
  4.1637 -    points_buflen = 4;
  4.1638 -    points = palloc(points_buflen * sizeof(pgl_point));
  4.1639 -    /* parse until closing parenthesis */
  4.1640 -    while (strptr[0] != ')') {
  4.1641 -      /* error on unexpected end of string */
  4.1642 -      if (strptr[0] == 0) goto pgl_ecluster_in_error;
  4.1643 -      /* mark neither latitude nor longitude as read */
  4.1644 -      done = PGL_SCAN_NONE;
  4.1645 -      /* require white-space before second, third, etc. point */
  4.1646 -      if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  4.1647 -      /* scan latitude (or longitude) */
  4.1648 -      done |= pgl_scan(&strptr, &lat, &lon);
  4.1649 -      /* require white-space before second coordinate */
  4.1650 -      if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  4.1651 -      /* scan longitude (or latitude) */
  4.1652 -      done |= pgl_scan(&strptr, &lat, &lon);
  4.1653 -      /* error unless both latitude and longitude were parsed */
  4.1654 -      if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error;
  4.1655 -      /* throw error if number of points is too high */
  4.1656 -      if (npoints_total == PGL_CLUSTER_MAXPOINTS) {
  4.1657 -        ereport(ERROR, (
  4.1658 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1659 -          errmsg(
  4.1660 -            "too many points for ecluster entry (maximum %i)",
  4.1661 -            PGL_CLUSTER_MAXPOINTS
  4.1662 -          )
  4.1663 -        ));
  4.1664 -      }
  4.1665 -      /* check if pgl_point array needs to grow */
  4.1666 -      if (npoints == points_buflen) {
  4.1667 -        pgl_point *newbuf;
  4.1668 -        points_buflen *= 2;
  4.1669 -        newbuf = palloc(points_buflen * sizeof(pgl_point));
  4.1670 -        memcpy(newbuf, points, npoints * sizeof(pgl_point));
  4.1671 -        pfree(points);
  4.1672 -        points = newbuf;
  4.1673 -      }
  4.1674 -      /* append point to pgl_point array (includes checks) */
  4.1675 -      pgl_epoint_set_latlon(&(points[npoints++]), lat, lon);
  4.1676 -      /* increase total number of points */
  4.1677 -      npoints_total++;
  4.1678 -    }
  4.1679 -    /* error if entry has no points */
  4.1680 -    if (!npoints) goto pgl_ecluster_in_error;
  4.1681 -    /* entries with one point are automatically of type "point" */
  4.1682 -    if (npoints == 1) entrytype = PGL_ENTRY_POINT;
  4.1683 -    /* if entries have more than one point */
  4.1684 -    else {
  4.1685 -      /* throw error if entry type is "point" */
  4.1686 -      if (entrytype == PGL_ENTRY_POINT) {
  4.1687 -        ereport(ERROR, (
  4.1688 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1689 -          errmsg("invalid input syntax for type ecluster (point entry with more than one point)")
  4.1690 -        ));
  4.1691 -      }
  4.1692 -      /* coerce outlines and polygons with more than 2 points to be a path */
  4.1693 -      if (npoints == 2) entrytype = PGL_ENTRY_PATH;
  4.1694 -    }
  4.1695 -    /* append entry to pgl_newentry array */
  4.1696 -    entries[nentries].entrytype = entrytype;
  4.1697 -    entries[nentries].npoints = npoints;
  4.1698 -    entries[nentries].points = points;
  4.1699 -    nentries++;
  4.1700 -    /* consume closing parenthesis */
  4.1701 -    strptr++;
  4.1702 -    /* consume white-space */
  4.1703 -    while (isspace(strptr[0])) strptr++;
  4.1704 -  }
  4.1705 -  /* free lower case string */
  4.1706 -  pfree(str_lower);
  4.1707 -  /* create cluster from pgl_newentry array */
  4.1708 -  cluster = pgl_new_cluster(nentries, entries);
  4.1709 -  /* free pgl_newentry array */
  4.1710 -  for (i=0; i<nentries; i++) pfree(entries[i].points);
  4.1711 -  pfree(entries);
  4.1712 -  /* set bounding circle of cluster and check east/west orientation */
  4.1713 -  if (!pgl_finalize_cluster(cluster)) {
  4.1714 -    ereport(ERROR, (
  4.1715 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1716 -      errmsg("can not determine east/west orientation for ecluster"),
  4.1717 -      errhint("Ensure that each entry has a longitude span of less than 180 degrees.")
  4.1718 -    ));
  4.1719 -  }
  4.1720 -  /* return cluster */
  4.1721 -  PG_RETURN_POINTER(cluster);
  4.1722 -  /* code to throw error */
  4.1723 -  pgl_ecluster_in_error:
  4.1724 -  ereport(ERROR, (
  4.1725 -    errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1726 -    errmsg("invalid input syntax for type ecluster: \"%s\"", str)
  4.1727 -  ));
  4.1728 -}
  4.1729 -
  4.1730 -/* convert point ("epoint") to string representation */
  4.1731 -PG_FUNCTION_INFO_V1(pgl_epoint_out);
  4.1732 -Datum pgl_epoint_out(PG_FUNCTION_ARGS) {
  4.1733 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.1734 -  char latstr[PGL_NUMBUFLEN];
  4.1735 -  char lonstr[PGL_NUMBUFLEN];
  4.1736 -  pgl_print_lat(latstr, point->lat);
  4.1737 -  pgl_print_lon(lonstr, point->lon);
  4.1738 -  PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr));
  4.1739 -}
  4.1740 -
  4.1741 -/* convert box ("ebox") to string representation */
  4.1742 -PG_FUNCTION_INFO_V1(pgl_ebox_out);
  4.1743 -Datum pgl_ebox_out(PG_FUNCTION_ARGS) {
  4.1744 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  4.1745 -  double lon_min = box->lon_min;
  4.1746 -  double lon_max = box->lon_max;
  4.1747 -  char lat_min_str[PGL_NUMBUFLEN];
  4.1748 -  char lat_max_str[PGL_NUMBUFLEN];
  4.1749 -  char lon_min_str[PGL_NUMBUFLEN];
  4.1750 -  char lon_max_str[PGL_NUMBUFLEN];
  4.1751 -  /* return string "empty" if box is set to be empty */
  4.1752 -  if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty");
  4.1753 -  /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */
  4.1754 -  /* (required since pgl_box_in orders the longitude boundaries) */
  4.1755 -  if (lon_min > lon_max) {
  4.1756 -    if (lon_min + lon_max >= 0) lon_min -= 360;
  4.1757 -    else lon_max += 360;
  4.1758 -  }
  4.1759 -  /* format and return result */
  4.1760 -  pgl_print_lat(lat_min_str, box->lat_min);
  4.1761 -  pgl_print_lat(lat_max_str, box->lat_max);
  4.1762 -  pgl_print_lon(lon_min_str, lon_min);
  4.1763 -  pgl_print_lon(lon_max_str, lon_max);
  4.1764 -  PG_RETURN_CSTRING(psprintf(
  4.1765 -    "%s %s %s %s",
  4.1766 -    lat_min_str, lon_min_str, lat_max_str, lon_max_str
  4.1767 -  ));
  4.1768 -}
  4.1769 -
  4.1770 -/* convert circle ("ecircle") to string representation */
  4.1771 -PG_FUNCTION_INFO_V1(pgl_ecircle_out);
  4.1772 -Datum pgl_ecircle_out(PG_FUNCTION_ARGS) {
  4.1773 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  4.1774 -  char latstr[PGL_NUMBUFLEN];
  4.1775 -  char lonstr[PGL_NUMBUFLEN];
  4.1776 -  char radstr[PGL_NUMBUFLEN];
  4.1777 -  pgl_print_lat(latstr, circle->center.lat);
  4.1778 -  pgl_print_lon(lonstr, circle->center.lon);
  4.1779 -  pgl_print_float(radstr, circle->radius);
  4.1780 -  PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr));
  4.1781 -}
  4.1782 -
  4.1783 -/* convert cluster ("ecluster") to string representation */
  4.1784 -PG_FUNCTION_INFO_V1(pgl_ecluster_out);
  4.1785 -Datum pgl_ecluster_out(PG_FUNCTION_ARGS) {
  4.1786 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  4.1787 -  char latstr[PGL_NUMBUFLEN];  /* string buffer for latitude */
  4.1788 -  char lonstr[PGL_NUMBUFLEN];  /* string buffer for longitude */
  4.1789 -  char ***strings;     /* array of array of strings */
  4.1790 -  char *string;        /* string of current token */
  4.1791 -  char *res, *resptr;  /* result and pointer to current write position */
  4.1792 -  size_t reslen = 1;   /* length of result (init with 1 for terminator) */
  4.1793 -  int npoints;         /* number of points of current entry */
  4.1794 -  int i, j;            /* i: entry, j: point in entry */
  4.1795 -  /* handle empty clusters */
  4.1796 -  if (cluster->nentries == 0) {
  4.1797 -    /* free detoasted cluster (if copy) */
  4.1798 -    PG_FREE_IF_COPY(cluster, 0);
  4.1799 -    /* return static result */
  4.1800 -    PG_RETURN_CSTRING("empty");
  4.1801 -  }
  4.1802 -  /* allocate array of array of strings */
  4.1803 -  strings = palloc(cluster->nentries * sizeof(char **));
  4.1804 -  /* iterate over all entries in cluster */
  4.1805 -  for (i=0; i<cluster->nentries; i++) {
  4.1806 -    /* get number of points in entry */
  4.1807 -    npoints = cluster->entries[i].npoints;
  4.1808 -    /* allocate array of strings (one string for each point plus two extra) */
  4.1809 -    strings[i] = palloc((2 + npoints) * sizeof(char *));
  4.1810 -    /* determine opening string */
  4.1811 -    switch (cluster->entries[i].entrytype) {
  4.1812 -      case PGL_ENTRY_POINT:   string = (i==0)?"point ("  :" point (";   break;
  4.1813 -      case PGL_ENTRY_PATH:    string = (i==0)?"path ("   :" path (";    break;
  4.1814 -      case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break;
  4.1815 -      case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break;
  4.1816 -      default:                string = (i==0)?"unknown"  :" unknown";
  4.1817 -    }
  4.1818 -    /* use opening string as first string in array */
  4.1819 -    strings[i][0] = string;
  4.1820 -    /* update result length (for allocating result string later) */
  4.1821 -    reslen += strlen(string);
  4.1822 -    /* iterate over all points */
  4.1823 -    for (j=0; j<npoints; j++) {
  4.1824 -      /* create string representation of point */
  4.1825 -      pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat);
  4.1826 -      pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon);
  4.1827 -      string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr);
  4.1828 -      /* copy string pointer to string array */
  4.1829 -      strings[i][j+1] = string;
  4.1830 -      /* update result length (for allocating result string later) */
  4.1831 -      reslen += strlen(string);
  4.1832 -    }
  4.1833 -    /* use closing parenthesis as last string in array */
  4.1834 -    strings[i][npoints+1] = ")";
  4.1835 -    /* update result length (for allocating result string later) */
  4.1836 -    reslen++;
  4.1837 -  }
  4.1838 -  /* allocate result string */
  4.1839 -  res = palloc(reslen);
  4.1840 -  /* set write pointer to begin of result string */
  4.1841 -  resptr = res;
  4.1842 -  /* copy strings into result string */
  4.1843 -  for (i=0; i<cluster->nentries; i++) {
  4.1844 -    npoints = cluster->entries[i].npoints;
  4.1845 -    for (j=0; j<npoints+2; j++) {
  4.1846 -      string = strings[i][j];
  4.1847 -      strcpy(resptr, string);
  4.1848 -      resptr += strlen(string);
  4.1849 -      /* free strings allocated by psprintf */
  4.1850 -      if (j != 0 && j != npoints+1) pfree(string);
  4.1851 -    }
  4.1852 -    /* free array of strings */
  4.1853 -    pfree(strings[i]);
  4.1854 -  }
  4.1855 -  /* free array of array of strings */
  4.1856 -  pfree(strings);
  4.1857 -  /* free detoasted cluster (if copy) */
  4.1858 -  PG_FREE_IF_COPY(cluster, 0);
  4.1859 -  /* return result */
  4.1860 -  PG_RETURN_CSTRING(res);
  4.1861 -}
  4.1862 -
  4.1863 -/* binary input function for point ("epoint") */
  4.1864 -PG_FUNCTION_INFO_V1(pgl_epoint_recv);
  4.1865 -Datum pgl_epoint_recv(PG_FUNCTION_ARGS) {
  4.1866 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  4.1867 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  4.1868 -  point->lat = pq_getmsgfloat8(buf);
  4.1869 -  point->lon = pq_getmsgfloat8(buf);
  4.1870 -  PG_RETURN_POINTER(point);
  4.1871 -}
  4.1872 -
  4.1873 -/* binary input function for box ("ebox") */
  4.1874 -PG_FUNCTION_INFO_V1(pgl_ebox_recv);
  4.1875 -Datum pgl_ebox_recv(PG_FUNCTION_ARGS) {
  4.1876 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  4.1877 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1878 -  box->lat_min = pq_getmsgfloat8(buf);
  4.1879 -  box->lat_max = pq_getmsgfloat8(buf);
  4.1880 -  box->lon_min = pq_getmsgfloat8(buf);
  4.1881 -  box->lon_max = pq_getmsgfloat8(buf);
  4.1882 -  PG_RETURN_POINTER(box);
  4.1883 -}
  4.1884 -
  4.1885 -/* binary input function for circle ("ecircle") */
  4.1886 -PG_FUNCTION_INFO_V1(pgl_ecircle_recv);
  4.1887 -Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) {
  4.1888 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  4.1889 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  4.1890 -  circle->center.lat = pq_getmsgfloat8(buf);
  4.1891 -  circle->center.lon = pq_getmsgfloat8(buf);
  4.1892 -  circle->radius = pq_getmsgfloat8(buf);
  4.1893 -  PG_RETURN_POINTER(circle);
  4.1894 -}
  4.1895 -
  4.1896 -/* TODO: binary receive function for cluster */
  4.1897 -
  4.1898 -/* binary output function for point ("epoint") */
  4.1899 -PG_FUNCTION_INFO_V1(pgl_epoint_send);
  4.1900 -Datum pgl_epoint_send(PG_FUNCTION_ARGS) {
  4.1901 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.1902 -  StringInfoData buf;
  4.1903 -  pq_begintypsend(&buf);
  4.1904 -  pq_sendfloat8(&buf, point->lat);
  4.1905 -  pq_sendfloat8(&buf, point->lon);
  4.1906 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  4.1907 -}
  4.1908 -
  4.1909 -/* binary output function for box ("ebox") */
  4.1910 -PG_FUNCTION_INFO_V1(pgl_ebox_send);
  4.1911 -Datum pgl_ebox_send(PG_FUNCTION_ARGS) {
  4.1912 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  4.1913 -  StringInfoData buf;
  4.1914 -  pq_begintypsend(&buf);
  4.1915 -  pq_sendfloat8(&buf, box->lat_min);
  4.1916 -  pq_sendfloat8(&buf, box->lat_max);
  4.1917 -  pq_sendfloat8(&buf, box->lon_min);
  4.1918 -  pq_sendfloat8(&buf, box->lon_max);
  4.1919 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  4.1920 -}
  4.1921 -
  4.1922 -/* binary output function for circle ("ecircle") */
  4.1923 -PG_FUNCTION_INFO_V1(pgl_ecircle_send);
  4.1924 -Datum pgl_ecircle_send(PG_FUNCTION_ARGS) {
  4.1925 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  4.1926 -  StringInfoData buf;
  4.1927 -  pq_begintypsend(&buf);
  4.1928 -  pq_sendfloat8(&buf, circle->center.lat);
  4.1929 -  pq_sendfloat8(&buf, circle->center.lon);
  4.1930 -  pq_sendfloat8(&buf, circle->radius);
  4.1931 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  4.1932 -}
  4.1933 -
  4.1934 -/* TODO: binary send functions for cluster */
  4.1935 -
  4.1936 -/* cast point ("epoint") to box ("ebox") */
  4.1937 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox);
  4.1938 -Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) {
  4.1939 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.1940 -  pgl_box *box = palloc(sizeof(pgl_box));
  4.1941 -  box->lat_min = point->lat;
  4.1942 -  box->lat_max = point->lat;
  4.1943 -  box->lon_min = point->lon;
  4.1944 -  box->lon_max = point->lon;
  4.1945 -  PG_RETURN_POINTER(box);
  4.1946 -}
  4.1947 -
  4.1948 -/* cast point ("epoint") to circle ("ecircle") */
  4.1949 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle);
  4.1950 -Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) {
  4.1951 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.1952 -  pgl_circle *circle = palloc(sizeof(pgl_box));
  4.1953 -  circle->center = *point;
  4.1954 -  circle->radius = 0;
  4.1955 -  PG_RETURN_POINTER(circle);
  4.1956 -}
  4.1957 -
  4.1958 -/* cast point ("epoint") to cluster ("ecluster") */
  4.1959 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster);
  4.1960 -Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) {
  4.1961 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.1962 -  pgl_newentry entry;
  4.1963 -  entry.entrytype = PGL_ENTRY_POINT;
  4.1964 -  entry.npoints = 1;
  4.1965 -  entry.points = point;
  4.1966 -  PG_RETURN_POINTER(pgl_new_cluster(1, &entry));
  4.1967 -}
  4.1968 -
  4.1969 -/* cast box ("ebox") to cluster ("ecluster") */
  4.1970 -#define pgl_ebox_to_ecluster_macro(i, a, b) \
  4.1971 -  entries[i].entrytype = PGL_ENTRY_POLYGON; \
  4.1972 -  entries[i].npoints = 4; \
  4.1973 -  entries[i].points = points[i]; \
  4.1974 -  points[i][0].lat = box->lat_min; \
  4.1975 -  points[i][0].lon = (a); \
  4.1976 -  points[i][1].lat = box->lat_min; \
  4.1977 -  points[i][1].lon = (b); \
  4.1978 -  points[i][2].lat = box->lat_max; \
  4.1979 -  points[i][2].lon = (b); \
  4.1980 -  points[i][3].lat = box->lat_max; \
  4.1981 -  points[i][3].lon = (a);
  4.1982 -PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster);
  4.1983 -Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) {
  4.1984 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  4.1985 -  double lon, dlon;
  4.1986 -  int nentries;
  4.1987 -  pgl_newentry entries[3];
  4.1988 -  pgl_point points[3][4];
  4.1989 -  if (box->lat_min > box->lat_max) {
  4.1990 -    nentries = 0;
  4.1991 -  } else if (box->lon_min > box->lon_max) {
  4.1992 -    if (box->lon_min < 0) {
  4.1993 -      lon = pgl_round((box->lon_min + 180) / 2.0);
  4.1994 -      nentries = 3;
  4.1995 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  4.1996 -      pgl_ebox_to_ecluster_macro(1, lon, 180);
  4.1997 -      pgl_ebox_to_ecluster_macro(2, -180, box->lon_max);
  4.1998 -    } else if (box->lon_max > 0) {
  4.1999 -      lon = pgl_round((box->lon_max - 180) / 2.0);
  4.2000 -      nentries = 3;
  4.2001 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  4.2002 -      pgl_ebox_to_ecluster_macro(1, -180, lon);
  4.2003 -      pgl_ebox_to_ecluster_macro(2, lon, box->lon_max);
  4.2004 -    } else {
  4.2005 -      nentries = 2;
  4.2006 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  4.2007 -      pgl_ebox_to_ecluster_macro(1, -180, box->lon_max);
  4.2008 -    }
  4.2009 -  } else {
  4.2010 -    dlon = pgl_round(box->lon_max - box->lon_min);
  4.2011 -    if (dlon < 180) {
  4.2012 -      nentries = 1;
  4.2013 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max);
  4.2014 -    } else {
  4.2015 -      lon = pgl_round((box->lon_min + box->lon_max) / 2.0);
  4.2016 -      if (
  4.2017 -        pgl_round(lon - box->lon_min) < 180 &&
  4.2018 -        pgl_round(box->lon_max - lon) < 180
  4.2019 -      ) {
  4.2020 -        nentries = 2;
  4.2021 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  4.2022 -        pgl_ebox_to_ecluster_macro(1, lon, box->lon_max);
  4.2023 -      } else {
  4.2024 -        nentries = 3;
  4.2025 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, -60);
  4.2026 -        pgl_ebox_to_ecluster_macro(1, -60, 60);
  4.2027 -        pgl_ebox_to_ecluster_macro(2, 60, box->lon_max);
  4.2028 -      }
  4.2029 -    }
  4.2030 -  }
  4.2031 -  PG_RETURN_POINTER(pgl_new_cluster(nentries, entries));
  4.2032 -}
  4.2033 -
  4.2034 -/* extract latitude from point ("epoint") */
  4.2035 -PG_FUNCTION_INFO_V1(pgl_epoint_lat);
  4.2036 -Datum pgl_epoint_lat(PG_FUNCTION_ARGS) {
  4.2037 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat);
  4.2038 -}
  4.2039 -
  4.2040 -/* extract longitude from point ("epoint") */
  4.2041 -PG_FUNCTION_INFO_V1(pgl_epoint_lon);
  4.2042 -Datum pgl_epoint_lon(PG_FUNCTION_ARGS) {
  4.2043 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon);
  4.2044 -}
  4.2045 -
  4.2046 -/* extract minimum latitude from box ("ebox") */
  4.2047 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_min);
  4.2048 -Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) {
  4.2049 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min);
  4.2050 -}
  4.2051 -
  4.2052 -/* extract maximum latitude from box ("ebox") */
  4.2053 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_max);
  4.2054 -Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) {
  4.2055 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max);
  4.2056 -}
  4.2057 -
  4.2058 -/* extract minimum longitude from box ("ebox") */
  4.2059 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_min);
  4.2060 -Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) {
  4.2061 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min);
  4.2062 -}
  4.2063 -
  4.2064 -/* extract maximum longitude from box ("ebox") */
  4.2065 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_max);
  4.2066 -Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) {
  4.2067 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max);
  4.2068 -}
  4.2069 -
  4.2070 -/* extract center point from circle ("ecircle") */
  4.2071 -PG_FUNCTION_INFO_V1(pgl_ecircle_center);
  4.2072 -Datum pgl_ecircle_center(PG_FUNCTION_ARGS) {
  4.2073 -  PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center));
  4.2074 -}
  4.2075 -
  4.2076 -/* extract radius from circle ("ecircle") */
  4.2077 -PG_FUNCTION_INFO_V1(pgl_ecircle_radius);
  4.2078 -Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) {
  4.2079 -  PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius);
  4.2080 -}
  4.2081 -
  4.2082 -/* check if point is inside box (overlap operator "&&") in SQL */
  4.2083 -PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap);
  4.2084 -Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) {
  4.2085 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2086 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1);
  4.2087 -  PG_RETURN_BOOL(pgl_point_in_box(point, box));
  4.2088 -}
  4.2089 -
  4.2090 -/* check if point is inside circle (overlap operator "&&") in SQL */
  4.2091 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap);
  4.2092 -Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) {
  4.2093 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2094 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2095 -  PG_RETURN_BOOL(
  4.2096 -    pgl_distance(
  4.2097 -      point->lat, point->lon,
  4.2098 -      circle->center.lat, circle->center.lon
  4.2099 -    ) <= circle->radius
  4.2100 -  );
  4.2101 -}
  4.2102 -
  4.2103 -/* check if point is inside cluster (overlap operator "&&") in SQL */
  4.2104 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap);
  4.2105 -Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
  4.2106 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2107 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2108 -  bool retval = pgl_point_in_cluster(point, cluster);
  4.2109 -  PG_FREE_IF_COPY(cluster, 1);
  4.2110 -  PG_RETURN_BOOL(retval);
  4.2111 -}
  4.2112 -
  4.2113 -/* check if two boxes overlap (overlap operator "&&") in SQL */
  4.2114 -PG_FUNCTION_INFO_V1(pgl_ebox_overlap);
  4.2115 -Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) {
  4.2116 -  pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0);
  4.2117 -  pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1);
  4.2118 -  PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2));
  4.2119 -}
  4.2120 -
  4.2121 -/* check if two circles overlap (overlap operator "&&") in SQL */
  4.2122 -PG_FUNCTION_INFO_V1(pgl_ecircle_overlap);
  4.2123 -Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) {
  4.2124 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2125 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2126 -  PG_RETURN_BOOL(
  4.2127 -    pgl_distance(
  4.2128 -      circle1->center.lat, circle1->center.lon,
  4.2129 -      circle2->center.lat, circle2->center.lon
  4.2130 -    ) <= circle1->radius + circle2->radius
  4.2131 -  );
  4.2132 -}
  4.2133 -
  4.2134 -/* check if circle and cluster overlap (overlap operator "&&") in SQL */
  4.2135 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap);
  4.2136 -Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) {
  4.2137 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2138 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2139 -  bool retval = (
  4.2140 -    pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius
  4.2141 -  );
  4.2142 -  PG_FREE_IF_COPY(cluster, 1);
  4.2143 -  PG_RETURN_BOOL(retval);
  4.2144 -}
  4.2145 -
  4.2146 -/* calculate distance between two points ("<->" operator) in SQL */
  4.2147 -PG_FUNCTION_INFO_V1(pgl_epoint_distance);
  4.2148 -Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
  4.2149 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  4.2150 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  4.2151 -  PG_RETURN_FLOAT8(pgl_distance(
  4.2152 -    point1->lat, point1->lon, point2->lat, point2->lon
  4.2153 -  ));
  4.2154 -}
  4.2155 -
  4.2156 -/* calculate point to circle distance ("<->" operator) in SQL */
  4.2157 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance);
  4.2158 -Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) {
  4.2159 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2160 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2161 -  double distance = pgl_distance(
  4.2162 -    point->lat, point->lon, circle->center.lat, circle->center.lon
  4.2163 -  ) - circle->radius;
  4.2164 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  4.2165 -}
  4.2166 -
  4.2167 -/* calculate point to cluster distance ("<->" operator) in SQL */
  4.2168 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance);
  4.2169 -Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) {
  4.2170 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2171 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2172 -  double distance = pgl_point_cluster_distance(point, cluster);
  4.2173 -  PG_FREE_IF_COPY(cluster, 1);
  4.2174 -  PG_RETURN_FLOAT8(distance);
  4.2175 -}
  4.2176 -
  4.2177 -/* calculate distance between two circles ("<->" operator) in SQL */
  4.2178 -PG_FUNCTION_INFO_V1(pgl_ecircle_distance);
  4.2179 -Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) {
  4.2180 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2181 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2182 -  double distance = pgl_distance(
  4.2183 -    circle1->center.lat, circle1->center.lon,
  4.2184 -    circle2->center.lat, circle2->center.lon
  4.2185 -  ) - (circle1->radius + circle2->radius);
  4.2186 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  4.2187 -}
  4.2188 -
  4.2189 -/* calculate circle to cluster distance ("<->" operator) in SQL */
  4.2190 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance);
  4.2191 -Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) {
  4.2192 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2193 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2194 -  double distance = (
  4.2195 -    pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius
  4.2196 -  );
  4.2197 -  PG_FREE_IF_COPY(cluster, 1);
  4.2198 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  4.2199 -}
  4.2200 -
  4.2201 -
  4.2202 -/*-----------------------------------------------------------*
  4.2203 - *  B-tree comparison operators and index support functions  *
  4.2204 - *-----------------------------------------------------------*/
  4.2205 -
  4.2206 -/* macro for a B-tree operator (without detoasting) */
  4.2207 -#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \
  4.2208 -  PG_FUNCTION_INFO_V1(func); \
  4.2209 -  Datum func(PG_FUNCTION_ARGS) { \
  4.2210 -    type *a = (type *)PG_GETARG_POINTER(0); \
  4.2211 -    type *b = (type *)PG_GETARG_POINTER(1); \
  4.2212 -    PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \
  4.2213 -  }
  4.2214 -
  4.2215 -/* macro for a B-tree comparison function (without detoasting) */
  4.2216 -#define PGL_BTREE_CMP(func, type, cmpfunc) \
  4.2217 -  PG_FUNCTION_INFO_V1(func); \
  4.2218 -  Datum func(PG_FUNCTION_ARGS) { \
  4.2219 -    type *a = (type *)PG_GETARG_POINTER(0); \
  4.2220 -    type *b = (type *)PG_GETARG_POINTER(1); \
  4.2221 -    PG_RETURN_INT32(cmpfunc(a, b)); \
  4.2222 -  }
  4.2223 -
  4.2224 -/* macro for a B-tree operator (with detoasting) */
  4.2225 -#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \
  4.2226 -  PG_FUNCTION_INFO_V1(func); \
  4.2227 -  Datum func(PG_FUNCTION_ARGS) { \
  4.2228 -    bool res; \
  4.2229 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  4.2230 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  4.2231 -    res = cmpfunc(a, b) oper 0; \
  4.2232 -    PG_FREE_IF_COPY(a, 0); \
  4.2233 -    PG_FREE_IF_COPY(b, 1); \
  4.2234 -    PG_RETURN_BOOL(res); \
  4.2235 -  }
  4.2236 -
  4.2237 -/* macro for a B-tree comparison function (with detoasting) */
  4.2238 -#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \
  4.2239 -  PG_FUNCTION_INFO_V1(func); \
  4.2240 -  Datum func(PG_FUNCTION_ARGS) { \
  4.2241 -    int32_t res; \
  4.2242 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  4.2243 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  4.2244 -    res = cmpfunc(a, b); \
  4.2245 -    PG_FREE_IF_COPY(a, 0); \
  4.2246 -    PG_FREE_IF_COPY(b, 1); \
  4.2247 -    PG_RETURN_INT32(res); \
  4.2248 -  }
  4.2249 -
  4.2250 -/* B-tree operators and comparison function for point */
  4.2251 -PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <)
  4.2252 -PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=)
  4.2253 -PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==)
  4.2254 -PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=)
  4.2255 -PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=)
  4.2256 -PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >)
  4.2257 -PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp)
  4.2258 -
  4.2259 -/* B-tree operators and comparison function for box */
  4.2260 -PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <)
  4.2261 -PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=)
  4.2262 -PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==)
  4.2263 -PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=)
  4.2264 -PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=)
  4.2265 -PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >)
  4.2266 -PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp)
  4.2267 -
  4.2268 -/* B-tree operators and comparison function for circle */
  4.2269 -PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <)
  4.2270 -PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=)
  4.2271 -PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==)
  4.2272 -PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=)
  4.2273 -PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=)
  4.2274 -PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >)
  4.2275 -PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp)
  4.2276 -
  4.2277 -
  4.2278 -/*--------------------------------*
  4.2279 - *  GiST index support functions  *
  4.2280 - *--------------------------------*/
  4.2281 -
  4.2282 -/* GiST "consistent" support function */
  4.2283 -PG_FUNCTION_INFO_V1(pgl_gist_consistent);
  4.2284 -Datum pgl_gist_consistent(PG_FUNCTION_ARGS) {
  4.2285 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  4.2286 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  4.2287 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  4.2288 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  4.2289 -  /* demand recheck because index and query methods are lossy */
  4.2290 -  *recheck = true;
  4.2291 -  /* strategy number 11: equality of two points */
  4.2292 -  if (strategy == 11) {
  4.2293 -    /* query datum is another point */
  4.2294 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  4.2295 -    /* convert other point to key */
  4.2296 -    pgl_pointkey querykey;
  4.2297 -    pgl_point_to_key(query, querykey);
  4.2298 -    /* return true if both keys overlap */
  4.2299 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  4.2300 -  }
  4.2301 -  /* strategy number 13: equality of two circles */
  4.2302 -  if (strategy == 13) {
  4.2303 -    /* query datum is another circle */
  4.2304 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2305 -    /* convert other circle to key */
  4.2306 -    pgl_areakey querykey;
  4.2307 -    pgl_circle_to_key(query, querykey);
  4.2308 -    /* return true if both keys overlap */
  4.2309 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  4.2310 -  }
  4.2311 -  /* for all remaining strategies, keys on empty objects produce no match */
  4.2312 -  /* (check necessary because query radius may be infinite) */
  4.2313 -  if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false);
  4.2314 -  /* strategy number 21: overlapping with point */
  4.2315 -  if (strategy == 21) {
  4.2316 -    /* query datum is a point */
  4.2317 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  4.2318 -    /* return true if estimated distance (allowed to be smaller than real
  4.2319 -       distance) between index key and point is zero */
  4.2320 -    PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0);
  4.2321 -  }
  4.2322 -  /* strategy number 22: (point) overlapping with box */
  4.2323 -  if (strategy == 22) {
  4.2324 -    /* query datum is a box */
  4.2325 -    pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1);
  4.2326 -    /* determine bounding box of indexed key */
  4.2327 -    pgl_box keybox;
  4.2328 -    pgl_key_to_box(key, &keybox);
  4.2329 -    /* return true if query box overlaps with bounding box of indexed key */
  4.2330 -    PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox));
  4.2331 -  }
  4.2332 -  /* strategy number 23: overlapping with circle */
  4.2333 -  if (strategy == 23) {
  4.2334 -    /* query datum is a circle */
  4.2335 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2336 -    /* return true if estimated distance (allowed to be smaller than real
  4.2337 -       distance) between index key and circle center is smaller than radius */
  4.2338 -    PG_RETURN_BOOL(
  4.2339 -      pgl_estimate_key_distance(key, &(query->center)) <= query->radius
  4.2340 -    );
  4.2341 -  }
  4.2342 -  /* strategy number 24: overlapping with cluster */
  4.2343 -  if (strategy == 24) {
  4.2344 -    bool retval;  /* return value */
  4.2345 -    /* query datum is a cluster */
  4.2346 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2347 -    /* return true if estimated distance (allowed to be smaller than real
  4.2348 -       distance) between index key and circle center is smaller than radius */
  4.2349 -    retval = (
  4.2350 -      pgl_estimate_key_distance(key, &(query->bounding.center)) <=
  4.2351 -      query->bounding.radius
  4.2352 -    );
  4.2353 -    PG_FREE_IF_COPY(query, 1);  /* free detoasted cluster (if copy) */
  4.2354 -    PG_RETURN_BOOL(retval);
  4.2355 -  }
  4.2356 -  /* throw error for any unknown strategy number */
  4.2357 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  4.2358 -}
  4.2359 -
  4.2360 -/* GiST "union" support function */
  4.2361 -PG_FUNCTION_INFO_V1(pgl_gist_union);
  4.2362 -Datum pgl_gist_union(PG_FUNCTION_ARGS) {
  4.2363 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  4.2364 -  pgl_keyptr out;  /* return value (to be palloc'ed) */
  4.2365 -  int i;
  4.2366 -  /* determine key size */
  4.2367 -  size_t keysize = PGL_KEY_IS_AREAKEY(
  4.2368 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key)
  4.2369 -  ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey);
  4.2370 -  /* begin with first key as result */
  4.2371 -  out = palloc(keysize);
  4.2372 -  memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize);
  4.2373 -  /* unite current result with second, third, etc. key */
  4.2374 -  for (i=1; i<entryvec->n; i++) {
  4.2375 -    pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key));
  4.2376 -  }
  4.2377 -  /* return result */
  4.2378 -  PG_RETURN_POINTER(out);
  4.2379 -}
  4.2380 -
  4.2381 -/* GiST "compress" support function for indicis on points */
  4.2382 -PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint);
  4.2383 -Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) {
  4.2384 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  4.2385 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  4.2386 -  /* only transform new leaves */
  4.2387 -  if (entry->leafkey) {
  4.2388 -    /* get point to be transformed */
  4.2389 -    pgl_point *point = (pgl_point *)DatumGetPointer(entry->key);
  4.2390 -    /* allocate memory for key */
  4.2391 -    pgl_keyptr key = palloc(sizeof(pgl_pointkey));
  4.2392 -    /* transform point to key */
  4.2393 -    pgl_point_to_key(point, key);
  4.2394 -    /* create new GISTENTRY structure as return value */
  4.2395 -    retval = palloc(sizeof(GISTENTRY));
  4.2396 -    gistentryinit(
  4.2397 -      *retval, PointerGetDatum(key),
  4.2398 -      entry->rel, entry->page, entry->offset, FALSE
  4.2399 -    );
  4.2400 -  } else {
  4.2401 -    /* inner nodes have already been transformed */
  4.2402 -    retval = entry;
  4.2403 -  }
  4.2404 -  /* return pointer to old or new GISTENTRY structure */
  4.2405 -  PG_RETURN_POINTER(retval);
  4.2406 -}
  4.2407 -
  4.2408 -/* GiST "compress" support function for indicis on circles */
  4.2409 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle);
  4.2410 -Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) {
  4.2411 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  4.2412 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  4.2413 -  /* only transform new leaves */
  4.2414 -  if (entry->leafkey) {
  4.2415 -    /* get circle to be transformed */
  4.2416 -    pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key);
  4.2417 -    /* allocate memory for key */
  4.2418 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  4.2419 -    /* transform circle to key */
  4.2420 -    pgl_circle_to_key(circle, key);
  4.2421 -    /* create new GISTENTRY structure as return value */
  4.2422 -    retval = palloc(sizeof(GISTENTRY));
  4.2423 -    gistentryinit(
  4.2424 -      *retval, PointerGetDatum(key),
  4.2425 -      entry->rel, entry->page, entry->offset, FALSE
  4.2426 -    );
  4.2427 -  } else {
  4.2428 -    /* inner nodes have already been transformed */
  4.2429 -    retval = entry;
  4.2430 -  }
  4.2431 -  /* return pointer to old or new GISTENTRY structure */
  4.2432 -  PG_RETURN_POINTER(retval);
  4.2433 -}
  4.2434 -
  4.2435 -/* GiST "compress" support function for indices on clusters */
  4.2436 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster);
  4.2437 -Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) {
  4.2438 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  4.2439 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  4.2440 -  /* only transform new leaves */
  4.2441 -  if (entry->leafkey) {
  4.2442 -    /* get cluster to be transformed (detoasting necessary!) */
  4.2443 -    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key);
  4.2444 -    /* allocate memory for key */
  4.2445 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  4.2446 -    /* transform cluster to key */
  4.2447 -    pgl_circle_to_key(&(cluster->bounding), key);
  4.2448 -    /* create new GISTENTRY structure as return value */
  4.2449 -    retval = palloc(sizeof(GISTENTRY));
  4.2450 -    gistentryinit(
  4.2451 -      *retval, PointerGetDatum(key),
  4.2452 -      entry->rel, entry->page, entry->offset, FALSE
  4.2453 -    );
  4.2454 -    /* free detoasted datum */
  4.2455 -    if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster);
  4.2456 -  } else {
  4.2457 -    /* inner nodes have already been transformed */
  4.2458 -    retval = entry;
  4.2459 -  }
  4.2460 -  /* return pointer to old or new GISTENTRY structure */
  4.2461 -  PG_RETURN_POINTER(retval);
  4.2462 -}
  4.2463 -
  4.2464 -/* GiST "decompress" support function for indices */
  4.2465 -PG_FUNCTION_INFO_V1(pgl_gist_decompress);
  4.2466 -Datum pgl_gist_decompress(PG_FUNCTION_ARGS) {
  4.2467 -  /* return passed pointer without transformation */
  4.2468 -  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
  4.2469 -}
  4.2470 -
  4.2471 -/* GiST "penalty" support function */
  4.2472 -PG_FUNCTION_INFO_V1(pgl_gist_penalty);
  4.2473 -Datum pgl_gist_penalty(PG_FUNCTION_ARGS) {
  4.2474 -  GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0);
  4.2475 -  GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1);
  4.2476 -  float *penalty = (float *)PG_GETARG_POINTER(2);
  4.2477 -  /* get original key and key to insert */
  4.2478 -  pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key);
  4.2479 -  pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key);
  4.2480 -  /* copy original key */
  4.2481 -  union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key;
  4.2482 -  if (PGL_KEY_IS_AREAKEY(orig)) {
  4.2483 -    memcpy(union_key.areakey, orig, sizeof(union_key.areakey));
  4.2484 -  } else {
  4.2485 -    memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey));
  4.2486 -  }
  4.2487 -  /* calculate union of both keys */
  4.2488 -  pgl_unite_keys((pgl_keyptr)&union_key, new);
  4.2489 -  /* penalty equal to reduction of key length (logarithm of added area) */
  4.2490 -  /* (return value by setting referenced value and returning pointer) */
  4.2491 -  *penalty = (
  4.2492 -    PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key)
  4.2493 -  );
  4.2494 -  PG_RETURN_POINTER(penalty);
  4.2495 -}
  4.2496 -
  4.2497 -/* GiST "picksplit" support function */
  4.2498 -PG_FUNCTION_INFO_V1(pgl_gist_picksplit);
  4.2499 -Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) {
  4.2500 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  4.2501 -  GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1);
  4.2502 -  OffsetNumber i;  /* between FirstOffsetNumber and entryvec->n (inclusive) */
  4.2503 -  union {
  4.2504 -    pgl_pointkey pointkey;
  4.2505 -    pgl_areakey areakey;
  4.2506 -  } union_all;  /* union of all keys (to be calculated from scratch)
  4.2507 -                   (later cut in half) */
  4.2508 -  int is_areakey = PGL_KEY_IS_AREAKEY(
  4.2509 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key)
  4.2510 -  );
  4.2511 -  int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey);
  4.2512 -  pgl_keyptr unionL = palloc(keysize);  /* union of keys that go left */
  4.2513 -  pgl_keyptr unionR = palloc(keysize);  /* union of keys that go right */
  4.2514 -  pgl_keyptr key;  /* current key to be processed */
  4.2515 -  /* allocate memory for array of left and right keys, set counts to zero */
  4.2516 -  v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  4.2517 -  v->spl_nleft = 0;
  4.2518 -  v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  4.2519 -  v->spl_nright = 0;
  4.2520 -  /* calculate union of all keys from scratch */
  4.2521 -  memcpy(
  4.2522 -    (pgl_keyptr)&union_all,
  4.2523 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key),
  4.2524 -    keysize
  4.2525 -  );
  4.2526 -  for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) {
  4.2527 -    pgl_unite_keys(
  4.2528 -      (pgl_keyptr)&union_all,
  4.2529 -      (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)
  4.2530 -    );
  4.2531 -  }
  4.2532 -  /* check if trivial split is necessary due to exhausted key length */
  4.2533 -  /* (Note: keys for empty objects must have node depth set to maximum) */
  4.2534 -  if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == (
  4.2535 -    is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH
  4.2536 -  )) {
  4.2537 -    /* half of all keys go left */
  4.2538 -    for (
  4.2539 -      i=FirstOffsetNumber;
  4.2540 -      i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  4.2541 -      i=OffsetNumberNext(i)
  4.2542 -    ) {
  4.2543 -      /* pointer to current key */
  4.2544 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  4.2545 -      /* update unionL */
  4.2546 -      /* check if key is first key that goes left */
  4.2547 -      if (!v->spl_nleft) {
  4.2548 -        /* first key that goes left is just copied to unionL */
  4.2549 -        memcpy(unionL, key, keysize);
  4.2550 -      } else {
  4.2551 -        /* unite current value and next key */
  4.2552 -        pgl_unite_keys(unionL, key);
  4.2553 -      }
  4.2554 -      /* append offset number to list of keys that go left */
  4.2555 -      v->spl_left[v->spl_nleft++] = i;
  4.2556 -    }
  4.2557 -    /* other half goes right */
  4.2558 -    for (
  4.2559 -      i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  4.2560 -      i<entryvec->n;
  4.2561 -      i=OffsetNumberNext(i)
  4.2562 -    ) {
  4.2563 -      /* pointer to current key */
  4.2564 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  4.2565 -      /* update unionR */
  4.2566 -      /* check if key is first key that goes right */
  4.2567 -      if (!v->spl_nright) {
  4.2568 -        /* first key that goes right is just copied to unionR */
  4.2569 -        memcpy(unionR, key, keysize);
  4.2570 -      } else {
  4.2571 -        /* unite current value and next key */
  4.2572 -        pgl_unite_keys(unionR, key);
  4.2573 -      }
  4.2574 -      /* append offset number to list of keys that go right */
  4.2575 -      v->spl_right[v->spl_nright++] = i;
  4.2576 -    }
  4.2577 -  }
  4.2578 -  /* otherwise, a non-trivial split is possible */
  4.2579 -  else {
  4.2580 -    /* cut covered area in half */
  4.2581 -    /* (union_all then refers to area of keys that go left) */
  4.2582 -    /* check if union of all keys covers empty and non-empty objects */
  4.2583 -    if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) {
  4.2584 -      /* if yes, split into empty and non-empty objects */
  4.2585 -      pgl_key_set_empty((pgl_keyptr)&union_all);
  4.2586 -    } else {
  4.2587 -      /* otherwise split by next bit */
  4.2588 -      ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++;
  4.2589 -      /* NOTE: type bit conserved */
  4.2590 -    }
  4.2591 -    /* determine for each key if it goes left or right */
  4.2592 -    for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) {
  4.2593 -      /* pointer to current key */
  4.2594 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  4.2595 -      /* keys within one half of the area go left */
  4.2596 -      if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) {
  4.2597 -        /* update unionL */
  4.2598 -        /* check if key is first key that goes left */
  4.2599 -        if (!v->spl_nleft) {
  4.2600 -          /* first key that goes left is just copied to unionL */
  4.2601 -          memcpy(unionL, key, keysize);
  4.2602 -        } else {
  4.2603 -          /* unite current value of unionL and processed key */
  4.2604 -          pgl_unite_keys(unionL, key);
  4.2605 -        }
  4.2606 -        /* append offset number to list of keys that go left */
  4.2607 -        v->spl_left[v->spl_nleft++] = i;
  4.2608 -      }
  4.2609 -      /* the other keys go right */
  4.2610 -      else {
  4.2611 -        /* update unionR */
  4.2612 -        /* check if key is first key that goes right */
  4.2613 -        if (!v->spl_nright) {
  4.2614 -          /* first key that goes right is just copied to unionR */
  4.2615 -          memcpy(unionR, key, keysize);
  4.2616 -        } else {
  4.2617 -          /* unite current value of unionR and processed key */
  4.2618 -          pgl_unite_keys(unionR, key);
  4.2619 -        }
  4.2620 -        /* append offset number to list of keys that go right */
  4.2621 -        v->spl_right[v->spl_nright++] = i;
  4.2622 -      }
  4.2623 -    }
  4.2624 -  }
  4.2625 -  /* store unions in return value */
  4.2626 -  v->spl_ldatum = PointerGetDatum(unionL);
  4.2627 -  v->spl_rdatum = PointerGetDatum(unionR);
  4.2628 -  /* return all results */
  4.2629 -  PG_RETURN_POINTER(v);
  4.2630 -}
  4.2631 -
  4.2632 -/* GiST "same"/"equal" support function */
  4.2633 -PG_FUNCTION_INFO_V1(pgl_gist_same);
  4.2634 -Datum pgl_gist_same(PG_FUNCTION_ARGS) {
  4.2635 -  pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0);
  4.2636 -  pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1);
  4.2637 -  bool *boolptr = (bool *)PG_GETARG_POINTER(2);
  4.2638 -  /* two keys are equal if they are binary equal */
  4.2639 -  /* (return result by setting referenced boolean and returning pointer) */
  4.2640 -  *boolptr = !memcmp(
  4.2641 -    key1,
  4.2642 -    key2,
  4.2643 -    PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey)
  4.2644 -  );
  4.2645 -  PG_RETURN_POINTER(boolptr);
  4.2646 -}
  4.2647 -
  4.2648 -/* GiST "distance" support function */
  4.2649 -PG_FUNCTION_INFO_V1(pgl_gist_distance);
  4.2650 -Datum pgl_gist_distance(PG_FUNCTION_ARGS) {
  4.2651 -  GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
  4.2652 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  4.2653 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  4.2654 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  4.2655 -  double distance;  /* return value */
  4.2656 -  /* demand recheck because distance is just an estimation */
  4.2657 -  /* (real distance may be bigger) */
  4.2658 -  *recheck = true;
  4.2659 -  /* strategy number 31: distance to point */
  4.2660 -  if (strategy == 31) {
  4.2661 -    /* query datum is a point */
  4.2662 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  4.2663 -    /* use pgl_estimate_pointkey_distance() function to compute result */
  4.2664 -    distance = pgl_estimate_key_distance(key, query);
  4.2665 -    /* avoid infinity (reserved!) */
  4.2666 -    if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  4.2667 -    /* return result */
  4.2668 -    PG_RETURN_FLOAT8(distance);
  4.2669 -  }
  4.2670 -  /* strategy number 33: distance to circle */
  4.2671 -  if (strategy == 33) {
  4.2672 -    /* query datum is a circle */
  4.2673 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2674 -    /* estimate distance to circle center and substract circle radius */
  4.2675 -    distance = (
  4.2676 -      pgl_estimate_key_distance(key, &(query->center)) - query->radius
  4.2677 -    );
  4.2678 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  4.2679 -    if (distance <= 0) distance = 0;
  4.2680 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  4.2681 -    /* return result */
  4.2682 -    PG_RETURN_FLOAT8(distance);
  4.2683 -  }
  4.2684 -  /* strategy number 34: distance to cluster */
  4.2685 -  if (strategy == 34) {
  4.2686 -    /* query datum is a cluster */
  4.2687 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2688 -    /* estimate distance to bounding center and substract bounding radius */
  4.2689 -    distance = (
  4.2690 -      pgl_estimate_key_distance(key, &(query->bounding.center)) -
  4.2691 -      query->bounding.radius
  4.2692 -    );
  4.2693 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  4.2694 -    if (distance <= 0) distance = 0;
  4.2695 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  4.2696 -    /* free detoasted cluster (if copy) */
  4.2697 -    PG_FREE_IF_COPY(query, 1);
  4.2698 -    /* return result */
  4.2699 -    PG_RETURN_FLOAT8(distance);
  4.2700 -  }
  4.2701 -  /* throw error for any unknown strategy number */
  4.2702 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  4.2703 -}
  4.2704 -
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/latlon-v0003.c	Fri Sep 02 14:04:04 2016 +0200
     5.3 @@ -0,0 +1,2701 @@
     5.4 +
     5.5 +/*-------------*
     5.6 + *  C prelude  *
     5.7 + *-------------*/
     5.8 +
     5.9 +#include "postgres.h"
    5.10 +#include "fmgr.h"
    5.11 +#include "libpq/pqformat.h"
    5.12 +#include "access/gist.h"
    5.13 +#include "access/stratnum.h"
    5.14 +#include "utils/array.h"
    5.15 +#include <math.h>
    5.16 +
    5.17 +#ifdef PG_MODULE_MAGIC
    5.18 +PG_MODULE_MAGIC;
    5.19 +#endif
    5.20 +
    5.21 +#if INT_MAX < 2147483647
    5.22 +#error Expected int type to be at least 32 bit wide
    5.23 +#endif
    5.24 +
    5.25 +
    5.26 +/*---------------------------------*
    5.27 + *  distance calculation on earth  *
    5.28 + *  (using WGS-84 spheroid)        *
    5.29 + *---------------------------------*/
    5.30 +
    5.31 +/*  WGS-84 spheroid with following parameters:
    5.32 +    semi-major axis  a = 6378137
    5.33 +    semi-minor axis  b = a * (1 - 1/298.257223563)
    5.34 +    estimated diameter = 2 * (2*a+b)/3
    5.35 +*/
    5.36 +#define PGL_SPHEROID_A 6378137.0            /* semi major axis */
    5.37 +#define PGL_SPHEROID_F (1.0/298.257223563)  /* flattening */
    5.38 +#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F))
    5.39 +#define PGL_EPS2       ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \
    5.40 +                           PGL_SPHEROID_B * PGL_SPHEROID_B ) / \
    5.41 +                         ( PGL_SPHEROID_A * PGL_SPHEROID_A ) )
    5.42 +#define PGL_SUBEPS2    (1.0-PGL_EPS2)
    5.43 +#define PGL_DIAMETER   ((4.0*PGL_SPHEROID_A + 2.0*PGL_SPHEROID_B) / 3.0)
    5.44 +#define PGL_SCALE      (PGL_SPHEROID_A / PGL_DIAMETER)  /* semi-major ref. */
    5.45 +#define PGL_FADELIMIT  (PGL_DIAMETER * M_PI / 6.0)      /* 1/6 circumference */
    5.46 +#define PGL_MAXDIST    (PGL_DIAMETER * M_PI / 2.0)      /* maximum distance */
    5.47 +
    5.48 +/* calculate distance between two points on earth (given in degrees) */
    5.49 +static inline double pgl_distance(
    5.50 +  double lat1, double lon1, double lat2, double lon2
    5.51 +) {
    5.52 +  float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin;
    5.53 +  float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t;
    5.54 +  /* normalize delta longitude (lon2 > 0 && lon1 = 0) */
    5.55 +  /* lon1 = 0 (not used anymore) */
    5.56 +  lon2 = fabs(lon2-lon1);
    5.57 +  /* convert to radians (first divide, then multiply) */
    5.58 +  lat1 = (lat1 / 180.0) * M_PI;
    5.59 +  lat2 = (lat2 / 180.0) * M_PI;
    5.60 +  lon2 = (lon2 / 180.0) * M_PI;
    5.61 +  /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point
    5.62 +     operations (lon2 >= lon1 is already ensured in a previous step) */
    5.63 +  if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; }
    5.64 +  /* calculate 3d coordinates on scaled ellipsoid which has an average diameter
    5.65 +     of 1.0 */
    5.66 +  lat1cos = cos(lat1); lat1sin = sin(lat1);
    5.67 +  lat2cos = cos(lat2); lat2sin = sin(lat2);
    5.68 +  lon2cos = cos(lon2); lon2sin = sin(lon2);
    5.69 +  nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin);
    5.70 +  nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
    5.71 +  x1 = nphi1 * lat1cos;
    5.72 +  z1 = nphi1 * PGL_SUBEPS2 * lat1sin;
    5.73 +  x2 = nphi2 * lat2cos * lon2cos;
    5.74 +  y2 = nphi2 * lat2cos * lon2sin;
    5.75 +  z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
    5.76 +  /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */
    5.77 +  g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
    5.78 +  /* convert tunnel distance through scaled ellipsoid to approximated surface
    5.79 +     distance on original ellipsoid */
    5.80 +  if (g > 1.0) g = 1.0;
    5.81 +  s = PGL_DIAMETER * asin(g);
    5.82 +  /* return result only if small enough to be precise (less than 1/3 of
    5.83 +     maximum possible distance) */
    5.84 +  if (s <= PGL_FADELIMIT) return s;
    5.85 +  /* calculate tunnel distance to antipodal point through scaled ellipsoid */
    5.86 +  g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1));
    5.87 +  /* convert tunnel distance to antipodal point through scaled ellipsoid to
    5.88 +     approximated surface distance to antipodal point on original ellipsoid */
    5.89 +  if (g > 1.0) g = 1.0;
    5.90 +  t = PGL_DIAMETER * asin(g);
    5.91 +  /* surface distance between original points can now be approximated by
    5.92 +     substracting antipodal distance from maximum possible distance;
    5.93 +     return result only if small enough (less than 1/3 of maximum possible
    5.94 +     distance) */
    5.95 +  if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t;
    5.96 +  /* otherwise crossfade direct and antipodal result to ensure monotonicity */
    5.97 +  return (
    5.98 +    (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) /
    5.99 +    (s + t - 2*PGL_FADELIMIT)
   5.100 +  );
   5.101 +}
   5.102 +
   5.103 +/* finite distance that can not be reached on earth */
   5.104 +#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST)
   5.105 +
   5.106 +
   5.107 +/*--------------------------------*
   5.108 + *  simple geographic data types  *
   5.109 + *--------------------------------*/
   5.110 +
   5.111 +/* point on earth given by latitude and longitude in degrees */
   5.112 +/* (type "epoint" in SQL) */
   5.113 +typedef struct {
   5.114 +  double lat;  /* between  -90 and  90 (both inclusive) */
   5.115 +  double lon;  /* between -180 and 180 (both inclusive) */
   5.116 +} pgl_point;
   5.117 +
   5.118 +/* box delimited by two parallels and two meridians (all in degrees) */
   5.119 +/* (type "ebox" in SQL) */
   5.120 +typedef struct {
   5.121 +  double lat_min;  /* between  -90 and  90 (both inclusive) */
   5.122 +  double lat_max;  /* between  -90 and  90 (both inclusive) */
   5.123 +  double lon_min;  /* between -180 and 180 (both inclusive) */
   5.124 +  double lon_max;  /* between -180 and 180 (both inclusive) */
   5.125 +  /* if lat_min > lat_max, then box is empty */
   5.126 +  /* if lon_min > lon_max, then 180th meridian is crossed */
   5.127 +} pgl_box;
   5.128 +
   5.129 +/* circle on earth surface (for radial searches with fixed radius) */
   5.130 +/* (type "ecircle" in SQL) */
   5.131 +typedef struct {
   5.132 +  pgl_point center;
   5.133 +  double radius; /* positive (including +0 but excluding -0), or -INFINITY */
   5.134 +  /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point),
   5.135 +     zero radius (0) denotes a single point,
   5.136 +     a finite radius (0 < radius < INFINITY) denotes a filled circle, and
   5.137 +     a radius of INFINITY is valid and means complete coverage of earth. */
   5.138 +} pgl_circle;
   5.139 +
   5.140 +
   5.141 +/*----------------------------------*
   5.142 + *  geographic "cluster" data type  *
   5.143 + *----------------------------------*/
   5.144 +
   5.145 +/* A cluster is a collection of points, paths, outlines, and polygons. If two
   5.146 +   polygons in a cluster overlap, the area covered by both polygons does not
   5.147 +   belong to the cluster. This way, a cluster can be used to describe complex
   5.148 +   shapes like polygons with holes. Outlines are non-filled polygons. Paths are
   5.149 +   open by default (i.e. the last point in the list is not connected with the
   5.150 +   first point in the list). Note that each outline or polygon in a cluster
   5.151 +   must cover a longitude range of less than 180 degrees to avoid ambiguities.
   5.152 +   Areas which are larger may be split into multiple polygons. */
   5.153 +
   5.154 +/* maximum number of points in a cluster */
   5.155 +/* (limited to avoid integer overflows, e.g. when allocating memory) */
   5.156 +#define PGL_CLUSTER_MAXPOINTS 16777216
   5.157 +
   5.158 +/* types of cluster entries */
   5.159 +#define PGL_ENTRY_POINT   1  /* a point */
   5.160 +#define PGL_ENTRY_PATH    2  /* a path from first point to last point */
   5.161 +#define PGL_ENTRY_OUTLINE 3  /* a non-filled polygon with given vertices */
   5.162 +#define PGL_ENTRY_POLYGON 4  /* a filled polygon with given vertices */
   5.163 +
   5.164 +/* Entries of a cluster are described by two different structs: pgl_newentry
   5.165 +   and pgl_entry. The first is used only during construction of a cluster, the
   5.166 +   second is used in all other cases (e.g. when reading clusters from the
   5.167 +   database, performing operations, etc). */
   5.168 +
   5.169 +/* entry for new geographic cluster during construction of that cluster */
   5.170 +typedef struct {
   5.171 +  int32_t entrytype;
   5.172 +  int32_t npoints;
   5.173 +  pgl_point *points;  /* pointer to an array of points (pgl_point) */
   5.174 +} pgl_newentry;
   5.175 +
   5.176 +/* entry of geographic cluster */
   5.177 +typedef struct {
   5.178 +  int32_t entrytype;  /* type of entry: point, path, outline, polygon */
   5.179 +  int32_t npoints;    /* number of stored points (set to 1 for point entry) */
   5.180 +  int32_t offset;     /* offset of pgl_point array from cluster base address */
   5.181 +  /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */
   5.182 +} pgl_entry;
   5.183 +
   5.184 +/* geographic cluster which is a collection of points, (open) paths, polygons,
   5.185 +   and outlines (non-filled polygons) */
   5.186 +typedef struct {
   5.187 +  char header[VARHDRSZ];  /* PostgreSQL header for variable size data types */
   5.188 +  int32_t nentries;       /* number of stored points */
   5.189 +  pgl_circle bounding;    /* bounding circle */
   5.190 +  /* Note: bounding circle ensures alignment of pgl_cluster for points */
   5.191 +  pgl_entry entries[FLEXIBLE_ARRAY_MEMBER];  /* var-length data */
   5.192 +} pgl_cluster;
   5.193 +
   5.194 +/* macro to determine memory alignment of points */
   5.195 +/* (needed to store pgl_point array after entries in pgl_cluster) */
   5.196 +typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment;
   5.197 +#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned)
   5.198 +
   5.199 +/* macro to extract a pointer to the array of points of a cluster entry */
   5.200 +#define PGL_ENTRY_POINTS(cluster, idx) \
   5.201 +  ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset))
   5.202 +
   5.203 +/* convert pgl_newentry array to pgl_cluster */
   5.204 +static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) {
   5.205 +  int i;              /* index of current entry */
   5.206 +  int npoints = 0;    /* number of points in whole cluster */
   5.207 +  int entry_npoints;  /* number of points in current entry */
   5.208 +  int points_offset = PGL_POINT_ALIGNMENT * (
   5.209 +    ( offsetof(pgl_cluster, entries) +
   5.210 +      nentries * sizeof(pgl_entry) +
   5.211 +      PGL_POINT_ALIGNMENT - 1
   5.212 +    ) / PGL_POINT_ALIGNMENT
   5.213 +  );  /* offset of pgl_point array from base address (considering alignment) */
   5.214 +  pgl_cluster *cluster;  /* new cluster to be returned */
   5.215 +  /* determine total number of points */
   5.216 +  for (i=0; i<nentries; i++) npoints += entries[i].npoints;
   5.217 +  /* allocate memory for cluster (including entries and points) */
   5.218 +  cluster = palloc(points_offset + npoints * sizeof(pgl_point));
   5.219 +  /* re-count total number of points to determine offset for each entry */
   5.220 +  npoints = 0;
   5.221 +  /* copy entries and points */
   5.222 +  for (i=0; i<nentries; i++) {
   5.223 +    /* determine number of points in entry */
   5.224 +    entry_npoints = entries[i].npoints;
   5.225 +    /* copy entry */
   5.226 +    cluster->entries[i].entrytype = entries[i].entrytype;
   5.227 +    cluster->entries[i].npoints = entry_npoints;
   5.228 +    /* calculate offset (in bytes) of pgl_point array */
   5.229 +    cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point);
   5.230 +    /* copy points */
   5.231 +    memcpy(
   5.232 +      PGL_ENTRY_POINTS(cluster, i),
   5.233 +      entries[i].points,
   5.234 +      entry_npoints * sizeof(pgl_point)
   5.235 +    );
   5.236 +    /* update total number of points processed */
   5.237 +    npoints += entry_npoints;
   5.238 +  }
   5.239 +  /* set number of entries in cluster */
   5.240 +  cluster->nentries = nentries;
   5.241 +  /* set PostgreSQL header for variable sized data */
   5.242 +  SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point));
   5.243 +  /* return newly created cluster */
   5.244 +  return cluster;
   5.245 +}
   5.246 +
   5.247 +
   5.248 +/*----------------------------------------*
   5.249 + *  C functions on geographic data types  *
   5.250 + *----------------------------------------*/
   5.251 +
   5.252 +/* round latitude or longitude to 12 digits after decimal point */
   5.253 +static inline double pgl_round(double val) {
   5.254 +  return round(val * 1e12) / 1e12;
   5.255 +}
   5.256 +
   5.257 +/* compare two points */
   5.258 +/* (equality when same point on earth is described, otherwise an arbitrary
   5.259 +   linear order) */
   5.260 +static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) {
   5.261 +  double lon1, lon2;  /* modified longitudes for special cases */
   5.262 +  /* use latitude as first ordering criterion */
   5.263 +  if (point1->lat < point2->lat) return -1;
   5.264 +  if (point1->lat > point2->lat) return 1;
   5.265 +  /* determine modified longitudes (considering special case of poles and
   5.266 +     180th meridian which can be described as W180 or E180) */
   5.267 +  if (point1->lat == -90 || point1->lat == 90) lon1 = 0;
   5.268 +  else if (point1->lon == 180) lon1 = -180;
   5.269 +  else lon1 = point1->lon;
   5.270 +  if (point2->lat == -90 || point2->lat == 90) lon2 = 0;
   5.271 +  else if (point2->lon == 180) lon2 = -180;
   5.272 +  else lon2 = point2->lon;
   5.273 +  /* use (modified) longitude as secondary ordering criterion */
   5.274 +  if (lon1 < lon2) return -1;
   5.275 +  if (lon1 > lon2) return 1;
   5.276 +  /* no difference found, points are equal */
   5.277 +  return 0;
   5.278 +}
   5.279 +
   5.280 +/* compare two boxes */
   5.281 +/* (equality when same box on earth is described, otherwise an arbitrary linear
   5.282 +   order) */
   5.283 +static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) {
   5.284 +  /* two empty boxes are equal, and an empty box is always considered "less
   5.285 +     than" a non-empty box */
   5.286 +  if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1;
   5.287 +  if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0;
   5.288 +  if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1;
   5.289 +  /* use southern border as first ordering criterion */
   5.290 +  if (box1->lat_min < box2->lat_min) return -1;
   5.291 +  if (box1->lat_min > box2->lat_min) return 1;
   5.292 +  /* use northern border as second ordering criterion */
   5.293 +  if (box1->lat_max < box2->lat_max) return -1;
   5.294 +  if (box1->lat_max > box2->lat_max) return 1;
   5.295 +  /* use western border as third ordering criterion */
   5.296 +  if (box1->lon_min < box2->lon_min) return -1;
   5.297 +  if (box1->lon_min > box2->lon_min) return 1;
   5.298 +  /* use eastern border as fourth ordering criterion */
   5.299 +  if (box1->lon_max < box2->lon_max) return -1;
   5.300 +  if (box1->lon_max > box2->lon_max) return 1;
   5.301 +  /* no difference found, boxes are equal */
   5.302 +  return 0;
   5.303 +}
   5.304 +
   5.305 +/* compare two circles */
   5.306 +/* (equality when same circle on earth is described, otherwise an arbitrary
   5.307 +   linear order) */
   5.308 +static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) {
   5.309 +  /* two circles with same infinite radius (positive or negative infinity) are
   5.310 +     considered equal independently of center point */
   5.311 +  if (
   5.312 +    !isfinite(circle1->radius) && !isfinite(circle2->radius) &&
   5.313 +    circle1->radius == circle2->radius
   5.314 +  ) return 0;
   5.315 +  /* use radius as first ordering criterion */
   5.316 +  if (circle1->radius < circle2->radius) return -1;
   5.317 +  if (circle1->radius > circle2->radius) return 1;
   5.318 +  /* use center point as secondary ordering criterion */
   5.319 +  return pgl_point_cmp(&(circle1->center), &(circle2->center));
   5.320 +}
   5.321 +
   5.322 +/* set box to empty box*/
   5.323 +static void pgl_box_set_empty(pgl_box *box) {
   5.324 +  box->lat_min = INFINITY;
   5.325 +  box->lat_max = -INFINITY;
   5.326 +  box->lon_min = 0;
   5.327 +  box->lon_max = 0;
   5.328 +}
   5.329 +
   5.330 +/* check if point is inside a box */
   5.331 +static bool pgl_point_in_box(pgl_point *point, pgl_box *box) {
   5.332 +  return (
   5.333 +    point->lat >= box->lat_min && point->lat <= box->lat_max && (
   5.334 +      (box->lon_min > box->lon_max) ? (
   5.335 +        /* box crosses 180th meridian */
   5.336 +        point->lon >= box->lon_min || point->lon <= box->lon_max
   5.337 +      ) : (
   5.338 +        /* box does not cross the 180th meridian */
   5.339 +        point->lon >= box->lon_min && point->lon <= box->lon_max
   5.340 +      )
   5.341 +    )
   5.342 +  );
   5.343 +}
   5.344 +
   5.345 +/* check if two boxes overlap */
   5.346 +static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) {
   5.347 +  return (
   5.348 +    box2->lat_max >= box2->lat_min &&  /* ensure box2 is not empty */
   5.349 +    ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) &&
   5.350 +    ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && (
   5.351 +      (
   5.352 +        /* check if one and only one box crosses the 180th meridian */
   5.353 +        ((box1->lon_min > box1->lon_max) ? 1 : 0) ^
   5.354 +        ((box2->lon_min > box2->lon_max) ? 1 : 0)
   5.355 +      ) ? (
   5.356 +        /* exactly one box crosses the 180th meridian */
   5.357 +        box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min ||
   5.358 +        box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max
   5.359 +      ) : (
   5.360 +        /* no box or both boxes cross the 180th meridian */
   5.361 +        (
   5.362 +          (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) &&
   5.363 +          (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max)
   5.364 +        ) ||
   5.365 +        /* handle W180 == E180 */
   5.366 +        ( box1->lon_min == -180 && box2->lon_max == 180 ) ||
   5.367 +        ( box2->lon_min == -180 && box1->lon_max == 180 )
   5.368 +      )
   5.369 +    )
   5.370 +  );
   5.371 +}
   5.372 +
   5.373 +/* check unambiguousness of east/west orientation of cluster entries and set
   5.374 +   bounding circle of cluster */
   5.375 +static bool pgl_finalize_cluster(pgl_cluster *cluster) {
   5.376 +  int i, j;                 /* i: index of entry, j: index of point in entry */
   5.377 +  int npoints;              /* number of points in entry */
   5.378 +  int total_npoints = 0;    /* total number of points in cluster */
   5.379 +  pgl_point *points;        /* points in entry */
   5.380 +  int lon_dir;              /* first point of entry west (-1) or east (+1) */
   5.381 +  double lon_break = 0;     /* antipodal longitude of first point in entry */
   5.382 +  double lon_min, lon_max;  /* covered longitude range of entry */
   5.383 +  double value;             /* temporary variable */
   5.384 +  /* reset bounding circle center to empty circle at 0/0 coordinates */
   5.385 +  cluster->bounding.center.lat = 0;
   5.386 +  cluster->bounding.center.lon = 0;
   5.387 +  cluster->bounding.radius = -INFINITY;
   5.388 +  /* if cluster is not empty */
   5.389 +  if (cluster->nentries != 0) {
   5.390 +    /* iterate over all cluster entries and ensure they each cover a longitude
   5.391 +       range less than 180 degrees */
   5.392 +    for (i=0; i<cluster->nentries; i++) {
   5.393 +      /* get properties of entry */
   5.394 +      npoints = cluster->entries[i].npoints;
   5.395 +      points = PGL_ENTRY_POINTS(cluster, i);
   5.396 +      /* get longitude of first point of entry */
   5.397 +      value = points[0].lon;
   5.398 +      /* initialize lon_min and lon_max with longitude of first point */
   5.399 +      lon_min = value;
   5.400 +      lon_max = value;
   5.401 +      /* determine east/west orientation of first point and calculate antipodal
   5.402 +         longitude (Note: rounding required here) */
   5.403 +      if      (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); }
   5.404 +      else if (value > 0) { lon_dir =  1; lon_break = pgl_round(value - 180); }
   5.405 +      else lon_dir = 0;
   5.406 +      /* iterate over all other points in entry */
   5.407 +      for (j=1; j<npoints; j++) {
   5.408 +        /* consider longitude wrap-around */
   5.409 +        value = points[j].lon;
   5.410 +        if      (lon_dir<0 && value>lon_break) value = pgl_round(value - 360);
   5.411 +        else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360);
   5.412 +        /* update lon_min and lon_max */
   5.413 +        if      (value < lon_min) lon_min = value;
   5.414 +        else if (value > lon_max) lon_max = value;
   5.415 +        /* return false if 180 degrees or more are covered */
   5.416 +        if (lon_max - lon_min >= 180) return false;
   5.417 +      }
   5.418 +    }
   5.419 +    /* iterate over all points of all entries and calculate arbitrary center
   5.420 +       point for bounding circle (best if center point minimizes the radius,
   5.421 +       but some error is allowed here) */
   5.422 +    for (i=0; i<cluster->nentries; i++) {
   5.423 +      /* get properties of entry */
   5.424 +      npoints = cluster->entries[i].npoints;
   5.425 +      points = PGL_ENTRY_POINTS(cluster, i);
   5.426 +      /* check if first entry */
   5.427 +      if (i==0) {
   5.428 +        /* get longitude of first point of first entry in whole cluster */
   5.429 +        value = points[0].lon;
   5.430 +        /* initialize lon_min and lon_max with longitude of first point of
   5.431 +           first entry in whole cluster (used to determine if whole cluster
   5.432 +           covers a longitude range of 180 degrees or more) */
   5.433 +        lon_min = value;
   5.434 +        lon_max = value;
   5.435 +        /* determine east/west orientation of first point and calculate
   5.436 +           antipodal longitude (Note: rounding not necessary here) */
   5.437 +        if      (value < 0) { lon_dir = -1; lon_break = value + 180; }
   5.438 +        else if (value > 0) { lon_dir =  1; lon_break = value - 180; }
   5.439 +        else lon_dir = 0;
   5.440 +      }
   5.441 +      /* iterate over all points in entry */
   5.442 +      for (j=0; j<npoints; j++) {
   5.443 +        /* longitude wrap-around (Note: rounding not necessary here) */
   5.444 +        value = points[j].lon;
   5.445 +        if      (lon_dir < 0 && value > lon_break) value -= 360;
   5.446 +        else if (lon_dir > 0 && value < lon_break) value += 360;
   5.447 +        if      (value < lon_min) lon_min = value;
   5.448 +        else if (value > lon_max) lon_max = value;
   5.449 +        /* set bounding circle to cover whole earth if more than 180 degrees
   5.450 +           are covered */
   5.451 +        if (lon_max - lon_min >= 180) {
   5.452 +          cluster->bounding.center.lat = 0;
   5.453 +          cluster->bounding.center.lon = 0;
   5.454 +          cluster->bounding.radius = INFINITY;
   5.455 +          return true;
   5.456 +        }
   5.457 +        /* add point to bounding circle center (for average calculation) */
   5.458 +        cluster->bounding.center.lat += points[j].lat;
   5.459 +        cluster->bounding.center.lon += value;
   5.460 +      }
   5.461 +      /* count total number of points */
   5.462 +      total_npoints += npoints;
   5.463 +    }
   5.464 +    /* determine average latitude and longitude of cluster */
   5.465 +    cluster->bounding.center.lat /= total_npoints;
   5.466 +    cluster->bounding.center.lon /= total_npoints;
   5.467 +    /* normalize longitude of center of cluster bounding circle */
   5.468 +    if (cluster->bounding.center.lon < -180) {
   5.469 +      cluster->bounding.center.lon += 360;
   5.470 +    }
   5.471 +    else if (cluster->bounding.center.lon > 180) {
   5.472 +      cluster->bounding.center.lon -= 360;
   5.473 +    }
   5.474 +    /* round bounding circle center (useful if it is used by other functions) */
   5.475 +    cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat);
   5.476 +    cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon);
   5.477 +    /* calculate radius of bounding circle */
   5.478 +    for (i=0; i<cluster->nentries; i++) {
   5.479 +      npoints = cluster->entries[i].npoints;
   5.480 +      points = PGL_ENTRY_POINTS(cluster, i);
   5.481 +      for (j=0; j<npoints; j++) {
   5.482 +        value = pgl_distance(
   5.483 +          cluster->bounding.center.lat, cluster->bounding.center.lon,
   5.484 +          points[j].lat, points[j].lon
   5.485 +        );
   5.486 +        if (value > cluster->bounding.radius) cluster->bounding.radius = value;
   5.487 +      }
   5.488 +    }
   5.489 +  }
   5.490 +  /* return true (east/west orientation is unambiguous) */
   5.491 +  return true;
   5.492 +}
   5.493 +
   5.494 +/* check if point is inside cluster */
   5.495 +static bool pgl_point_in_cluster(pgl_point *point, pgl_cluster *cluster) {
   5.496 +  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   5.497 +  int entrytype;         /* type of entry */
   5.498 +  int npoints;           /* number of points in entry */
   5.499 +  pgl_point *points;     /* array of points in entry */
   5.500 +  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   5.501 +  double lon_break = 0;  /* antipodal longitude of first vertex */
   5.502 +  double lat0 = point->lat;  /* latitude of point */
   5.503 +  double lon0;           /* (adjusted) longitude of point */
   5.504 +  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   5.505 +  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   5.506 +  double lon;            /* longitude of intersection */
   5.507 +  int counter = 0;       /* counter for intersections east of point */
   5.508 +  /* points outside bounding circle are always assumed to be non-overlapping */
   5.509 +  /* (necessary for consistent table and index scans) */
   5.510 +  if (
   5.511 +    pgl_distance(
   5.512 +      point->lat, point->lon,
   5.513 +      cluster->bounding.center.lat, cluster->bounding.center.lon
   5.514 +    ) > cluster->bounding.radius
   5.515 +  ) return false;
   5.516 +  /* iterate over all entries */
   5.517 +  for (i=0; i<cluster->nentries; i++) {
   5.518 +    /* get properties of entry */
   5.519 +    entrytype = cluster->entries[i].entrytype;
   5.520 +    npoints = cluster->entries[i].npoints;
   5.521 +    points = PGL_ENTRY_POINTS(cluster, i);
   5.522 +    /* determine east/west orientation of first point of entry and calculate
   5.523 +       antipodal longitude */
   5.524 +    lon_break = points[0].lon;
   5.525 +    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   5.526 +    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   5.527 +    else lon_dir = 0;
   5.528 +    /* get longitude of point */
   5.529 +    lon0 = point->lon;
   5.530 +    /* consider longitude wrap-around for point */
   5.531 +    if      (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360);
   5.532 +    else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360);
   5.533 +    /* iterate over all edges and vertices */
   5.534 +    for (j=0; j<npoints; j++) {
   5.535 +      /* return true if point is on vertex of polygon */
   5.536 +      if (pgl_point_cmp(point, &(points[j])) == 0) return true;
   5.537 +      /* calculate index of next vertex */
   5.538 +      k = (j+1) % npoints;
   5.539 +      /* skip last edge unless entry is (closed) outline or polygon */
   5.540 +      if (
   5.541 +        k == 0 &&
   5.542 +        entrytype != PGL_ENTRY_OUTLINE &&
   5.543 +        entrytype != PGL_ENTRY_POLYGON
   5.544 +      ) continue;
   5.545 +      /* get latitude and longitude values of edge */
   5.546 +      lat1 = points[j].lat;
   5.547 +      lat2 = points[k].lat;
   5.548 +      lon1 = points[j].lon;
   5.549 +      lon2 = points[k].lon;
   5.550 +      /* consider longitude wrap-around for edge */
   5.551 +      if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   5.552 +      else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   5.553 +      if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
   5.554 +      else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
   5.555 +      /* return true if point is on horizontal (west to east) edge of polygon */
   5.556 +      if (
   5.557 +        lat0 == lat1 && lat0 == lat2 &&
   5.558 +        ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) )
   5.559 +      ) return true;
   5.560 +      /* check if edge crosses east/west line of point */
   5.561 +      if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) {
   5.562 +        /* calculate longitude of intersection */
   5.563 +        lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1);
   5.564 +        /* return true if intersection goes (approximately) through point */
   5.565 +        if (pgl_round(lon) == lon0) return true;
   5.566 +        /* count intersection if east of point and entry is polygon*/
   5.567 +        if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++;
   5.568 +      }
   5.569 +    }
   5.570 +  }
   5.571 +  /* return true if number of intersections is odd */
   5.572 +  return counter & 1;
   5.573 +}
   5.574 +
   5.575 +/* calculate (approximate) distance between point and cluster */
   5.576 +static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
   5.577 +  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   5.578 +  int entrytype;         /* type of entry */
   5.579 +  int npoints;           /* number of points in entry */
   5.580 +  pgl_point *points;     /* array of points in entry */
   5.581 +  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   5.582 +  double lon_break = 0;  /* antipodal longitude of first vertex */
   5.583 +  double lon_min = 0;    /* minimum (adjusted) longitude of entry vertices */
   5.584 +  double lon_max = 0;    /* maximum (adjusted) longitude of entry vertices */
   5.585 +  double lat0 = point->lat;  /* latitude of point */
   5.586 +  double lon0;           /* (adjusted) longitude of point */
   5.587 +  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   5.588 +  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   5.589 +  double s;              /* scalar for vector calculations */
   5.590 +  double dist;           /* distance calculated in one step */
   5.591 +  double min_dist = INFINITY;   /* minimum distance */
   5.592 +  /* distance is zero if point is contained in cluster */
   5.593 +  if (pgl_point_in_cluster(point, cluster)) return 0;
   5.594 +  /* iterate over all entries */
   5.595 +  for (i=0; i<cluster->nentries; i++) {
   5.596 +    /* get properties of entry */
   5.597 +    entrytype = cluster->entries[i].entrytype;
   5.598 +    npoints = cluster->entries[i].npoints;
   5.599 +    points = PGL_ENTRY_POINTS(cluster, i);
   5.600 +    /* determine east/west orientation of first point of entry and calculate
   5.601 +       antipodal longitude */
   5.602 +    lon_break = points[0].lon;
   5.603 +    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   5.604 +    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   5.605 +    else lon_dir = 0;
   5.606 +    /* determine covered longitude range */
   5.607 +    for (j=0; j<npoints; j++) {
   5.608 +      /* get longitude of vertex */
   5.609 +      lon1 = points[j].lon;
   5.610 +      /* adjust longitude to fix potential wrap-around */
   5.611 +      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   5.612 +      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   5.613 +      /* update minimum and maximum longitude of polygon */
   5.614 +      if (j == 0 || lon1 < lon_min) lon_min = lon1;
   5.615 +      if (j == 0 || lon1 > lon_max) lon_max = lon1;
   5.616 +    }
   5.617 +    /* adjust longitude wrap-around according to full longitude range */
   5.618 +    lon_break = (lon_max + lon_min) / 2;
   5.619 +    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   5.620 +    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   5.621 +    /* get longitude of point */
   5.622 +    lon0 = point->lon;
   5.623 +    /* consider longitude wrap-around for point */
   5.624 +    if      (lon_dir < 0 && lon0 > lon_break) lon0 -= 360;
   5.625 +    else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
   5.626 +    /* iterate over all edges and vertices */
   5.627 +    for (j=0; j<npoints; j++) {
   5.628 +      /* get latitude and longitude values of current point */
   5.629 +      lat1 = points[j].lat;
   5.630 +      lon1 = points[j].lon;
   5.631 +      /* consider longitude wrap-around for current point */
   5.632 +      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   5.633 +      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   5.634 +      /* calculate distance to vertex */
   5.635 +      dist = pgl_distance(lat0, lon0, lat1, lon1);
   5.636 +      /* store calculated distance if smallest */
   5.637 +      if (dist < min_dist) min_dist = dist;
   5.638 +      /* calculate index of next vertex */
   5.639 +      k = (j+1) % npoints;
   5.640 +      /* skip last edge unless entry is (closed) outline or polygon */
   5.641 +      if (
   5.642 +        k == 0 &&
   5.643 +        entrytype != PGL_ENTRY_OUTLINE &&
   5.644 +        entrytype != PGL_ENTRY_POLYGON
   5.645 +      ) continue;
   5.646 +      /* get latitude and longitude values of next point */
   5.647 +      lat2 = points[k].lat;
   5.648 +      lon2 = points[k].lon;
   5.649 +      /* consider longitude wrap-around for next point */
   5.650 +      if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
   5.651 +      else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
   5.652 +      /* go to next vertex and edge if edge is degenerated */
   5.653 +      if (lat1 == lat2 && lon1 == lon2) continue;
   5.654 +      /* otherwise test if point can be projected onto edge of polygon */
   5.655 +      s = (
   5.656 +        ((lat0-lat1) * (lat2-lat1) + (lon0-lon1) * (lon2-lon1)) /
   5.657 +        ((lat2-lat1) * (lat2-lat1) + (lon2-lon1) * (lon2-lon1))
   5.658 +      );
   5.659 +      /* go to next vertex and edge if point cannot be projected */
   5.660 +      if (!(s > 0 && s < 1)) continue;
   5.661 +      /* calculate distance from original point to projected point */
   5.662 +      dist = pgl_distance(
   5.663 +        lat0, lon0,
   5.664 +        lat1 + s * (lat2-lat1),
   5.665 +        lon1 + s * (lon2-lon1)
   5.666 +      );
   5.667 +      /* store calculated distance if smallest */
   5.668 +      if (dist < min_dist) min_dist = dist;
   5.669 +    }
   5.670 +  }
   5.671 +  /* return minimum distance */
   5.672 +  return min_dist;
   5.673 +}
   5.674 +
   5.675 +/* estimator function for distance between box and point */
   5.676 +/* allowed to return smaller values than actually correct */
   5.677 +static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
   5.678 +  double dlon;  /* longitude range of box (delta longitude) */
   5.679 +  double h;     /* half of distance along meridian */
   5.680 +  double d;     /* distance between both southern or both northern points */
   5.681 +  double cur_dist;  /* calculated distance */
   5.682 +  double min_dist;  /* minimum distance calculated */
   5.683 +  /* return infinity if bounding box is empty */
   5.684 +  if (box->lat_min > box->lat_max) return INFINITY;
   5.685 +  /* return zero if point is inside bounding box */
   5.686 +  if (pgl_point_in_box(point, box)) return 0;
   5.687 +  /* calculate delta longitude */
   5.688 +  dlon = box->lon_max - box->lon_min;
   5.689 +  if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
   5.690 +  /* if delta longitude is greater than 180 degrees, perform safe fall-back */
   5.691 +  if (dlon > 180) return 0;
   5.692 +  /* calculate half of distance along meridian */
   5.693 +  h = pgl_distance(box->lat_min, 0, box->lat_max, 0) / 2;
   5.694 +  /* calculate full distance between southern points */
   5.695 +  d = pgl_distance(box->lat_min, 0, box->lat_min, dlon);
   5.696 +  /* calculate maximum of full distance and half distance */
   5.697 +  if (h > d) d = h;
   5.698 +  /* calculate distance from point to first southern vertex and substract
   5.699 +     maximum error */
   5.700 +  min_dist = pgl_distance(
   5.701 +    point->lat, point->lon, box->lat_min, box->lon_min
   5.702 +  ) - d;
   5.703 +  /* return zero if estimated distance is smaller than zero */
   5.704 +  if (min_dist <= 0) return 0;
   5.705 +  /* repeat procedure with second southern vertex */
   5.706 +  cur_dist = pgl_distance(
   5.707 +    point->lat, point->lon, box->lat_min, box->lon_max
   5.708 +  ) - d;
   5.709 +  if (cur_dist <= 0) return 0;
   5.710 +  if (cur_dist < min_dist) min_dist = cur_dist;
   5.711 +  /* calculate full distance between northern points */
   5.712 +  d = pgl_distance(box->lat_max, 0, box->lat_max, dlon);
   5.713 +  /* calculate maximum of full distance and half distance */
   5.714 +  if (h > d) d = h;
   5.715 +  /* repeat procedure with northern vertices */
   5.716 +  cur_dist = pgl_distance(
   5.717 +    point->lat, point->lon, box->lat_max, box->lon_max
   5.718 +  ) - d;
   5.719 +  if (cur_dist <= 0) return 0;
   5.720 +  if (cur_dist < min_dist) min_dist = cur_dist;
   5.721 +  cur_dist = pgl_distance(
   5.722 +    point->lat, point->lon, box->lat_max, box->lon_min
   5.723 +  ) - d;
   5.724 +  if (cur_dist <= 0) return 0;
   5.725 +  if (cur_dist < min_dist) min_dist = cur_dist;
   5.726 +  /* return smallest value (unless already returned zero) */
   5.727 +  return min_dist;
   5.728 +}
   5.729 +
   5.730 +
   5.731 +/*----------------------------*
   5.732 + *  fractal geographic index  *
   5.733 + *----------------------------*/
   5.734 +
   5.735 +/* number of bytes used for geographic (center) position in keys */
   5.736 +#define PGL_KEY_LATLON_BYTELEN 7
   5.737 +
   5.738 +/* maximum reference value for logarithmic size of geographic objects */
   5.739 +#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
   5.740 +
   5.741 +/* safety margin to avoid floating point errors in distance estimation */
   5.742 +#define PGL_FPE_SAFETY (1.0+1e-14)  /* slightly greater than 1.0 */
   5.743 +
   5.744 +/* pointer to index key (either pgl_pointkey or pgl_areakey) */
   5.745 +typedef unsigned char *pgl_keyptr;
   5.746 +
   5.747 +/* index key for points (objects with zero area) on the spheroid */
   5.748 +/* bit  0..55: interspersed bits of latitude and longitude,
   5.749 +   bit 56..57: always zero,
   5.750 +   bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */
   5.751 +typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1];
   5.752 +
   5.753 +/* index key for geographic objects on spheroid with area greater than zero */
   5.754 +/* bit  0..55: interspersed bits of latitude and longitude of center point,
   5.755 +   bit     56: always set to 1,
   5.756 +   bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.),
   5.757 +   bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to
   5.758 +               PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth
   5.759 +               = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL
   5.760 +               (with interspersed bits = 0 and node depth = 0) for keys which
   5.761 +               cover both empty and non-empty objects */
   5.762 +
   5.763 +typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2];
   5.764 +
   5.765 +/* helper macros for reading/writing index keys */
   5.766 +#define PGL_KEY_NODEDEPTH_OFFSET  PGL_KEY_LATLON_BYTELEN
   5.767 +#define PGL_KEY_OBJSIZE_OFFSET    (PGL_KEY_NODEDEPTH_OFFSET+1)
   5.768 +#define PGL_POINTKEY_MAXDEPTH     (PGL_KEY_LATLON_BYTELEN*8)
   5.769 +#define PGL_AREAKEY_MAXDEPTH      (2*PGL_POINTKEY_MAXDEPTH+1)
   5.770 +#define PGL_AREAKEY_MAXOBJSIZE    (PGL_POINTKEY_MAXDEPTH+1)
   5.771 +#define PGL_AREAKEY_TYPEMASK      0x80
   5.772 +#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8)))
   5.773 +#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \
   5.774 +                                  ( PGL_KEY_LATLONBIT(key1, n) ^ \
   5.775 +                                    PGL_KEY_LATLONBIT(key2, n) )
   5.776 +#define PGL_KEY_IS_AREAKEY(key)   ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
   5.777 +                                    PGL_AREAKEY_TYPEMASK)
   5.778 +#define PGL_KEY_NODEDEPTH(key)    ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
   5.779 +                                    (PGL_AREAKEY_TYPEMASK-1))
   5.780 +#define PGL_KEY_OBJSIZE(key)      ((key)[PGL_KEY_OBJSIZE_OFFSET])
   5.781 +#define PGL_KEY_OBJSIZE_EMPTY     126
   5.782 +#define PGL_KEY_OBJSIZE_UNIVERSAL 127
   5.783 +#define PGL_KEY_IS_EMPTY(key)     ( PGL_KEY_IS_AREAKEY(key) && \
   5.784 +                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
   5.785 +                                    PGL_KEY_OBJSIZE_EMPTY )
   5.786 +#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \
   5.787 +                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
   5.788 +                                    PGL_KEY_OBJSIZE_UNIVERSAL )
   5.789 +
   5.790 +/* set area key to match empty objects only */
   5.791 +static void pgl_key_set_empty(pgl_keyptr key) {
   5.792 +  memset(key, 0, sizeof(pgl_areakey));
   5.793 +  /* Note: setting node depth to maximum is required for picksplit function */
   5.794 +  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
   5.795 +  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY;
   5.796 +}
   5.797 +
   5.798 +/* set area key to match any object (including empty objects) */
   5.799 +static void pgl_key_set_universal(pgl_keyptr key) {
   5.800 +  memset(key, 0, sizeof(pgl_areakey));
   5.801 +  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK;
   5.802 +  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL;
   5.803 +}
   5.804 +
   5.805 +/* convert a point on earth into a max-depth key to be used in index */
   5.806 +static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) {
   5.807 +  double lat = point->lat;
   5.808 +  double lon = point->lon;
   5.809 +  int i;
   5.810 +  /* clear latitude and longitude bits */
   5.811 +  memset(key, 0, PGL_KEY_LATLON_BYTELEN);
   5.812 +  /* set node depth to maximum and type bit to zero */
   5.813 +  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH;
   5.814 +  /* iterate over all latitude/longitude bit pairs */
   5.815 +  for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) {
   5.816 +    /* determine latitude bit */
   5.817 +    if (lat >= 0) {
   5.818 +      key[i/4] |= 0x80 >> (2*(i%4));
   5.819 +      lat *= 2; lat -= 90;
   5.820 +    } else {
   5.821 +      lat *= 2; lat += 90;
   5.822 +    }
   5.823 +    /* determine longitude bit */
   5.824 +    if (lon >= 0) {
   5.825 +      key[i/4] |= 0x80 >> (2*(i%4)+1);
   5.826 +      lon *= 2; lon -= 180;
   5.827 +    } else {
   5.828 +      lon *= 2; lon += 180;
   5.829 +    }
   5.830 +  }
   5.831 +}
   5.832 +
   5.833 +/* convert a circle on earth into a max-depth key to be used in an index */
   5.834 +static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) {
   5.835 +  /* handle special case of empty circle */
   5.836 +  if (circle->radius < 0) {
   5.837 +    pgl_key_set_empty(key);
   5.838 +    return;
   5.839 +  }
   5.840 +  /* perform same action as for point keys */
   5.841 +  pgl_point_to_key(&(circle->center), key);
   5.842 +  /* but overwrite type and node depth to fit area index key */
   5.843 +  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
   5.844 +  /* check if radius is greater than (or equal to) reference size */
   5.845 +  /* (treat equal values as greater values for numerical safety) */
   5.846 +  if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) {
   5.847 +    /* if yes, set logarithmic size to zero */
   5.848 +    key[PGL_KEY_OBJSIZE_OFFSET] = 0;
   5.849 +  } else {
   5.850 +    /* otherwise, determine logarithmic size iteratively */
   5.851 +    /* (one step is equivalent to a factor of sqrt(2)) */
   5.852 +    double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2;
   5.853 +    int objsize = 1;
   5.854 +    while (objsize < PGL_AREAKEY_MAXOBJSIZE) {
   5.855 +      /* stop when radius is greater than (or equal to) adjusted reference */
   5.856 +      /* (treat equal values as greater values for numerical safety) */
   5.857 +      if (circle->radius >= reference) break;
   5.858 +      reference /= M_SQRT2;
   5.859 +      objsize++;
   5.860 +    }
   5.861 +    /* set logarithmic size to determined value */
   5.862 +    key[PGL_KEY_OBJSIZE_OFFSET] = objsize;
   5.863 +  }
   5.864 +}
   5.865 +
   5.866 +/* check if one key is subkey of another key or vice versa */
   5.867 +static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) {
   5.868 +  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
   5.869 +  /* determine smallest depth */
   5.870 +  int depth1 = PGL_KEY_NODEDEPTH(key1);
   5.871 +  int depth2 = PGL_KEY_NODEDEPTH(key2);
   5.872 +  int depth = (depth1 < depth2) ? depth1 : depth2;
   5.873 +  /* check if keys are area keys (assuming that both keys have same type) */
   5.874 +  if (PGL_KEY_IS_AREAKEY(key1)) {
   5.875 +    int j = 0;  /* bit offset for logarithmic object size bits */
   5.876 +    int k = 0;  /* bit offset for latitude and longitude */
   5.877 +    /* fetch logarithmic object size information */
   5.878 +    int objsize1 = PGL_KEY_OBJSIZE(key1);
   5.879 +    int objsize2 = PGL_KEY_OBJSIZE(key2);
   5.880 +    /* handle special cases for empty objects (universal and empty keys) */
   5.881 +    if (
   5.882 +      objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL ||
   5.883 +      objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL
   5.884 +    ) return true;
   5.885 +    if (
   5.886 +      objsize1 == PGL_KEY_OBJSIZE_EMPTY ||
   5.887 +      objsize2 == PGL_KEY_OBJSIZE_EMPTY
   5.888 +    ) return objsize1 == objsize2;
   5.889 +    /* iterate through key bits */
   5.890 +    for (i=0; i<depth; i++) {
   5.891 +      /* every second bit is a bit describing the object size */
   5.892 +      if (i%2 == 0) {
   5.893 +        /* check if object size bit is different in both keys (objsize1 and
   5.894 +           objsize2 describe the minimum index when object size bit is set) */
   5.895 +        if (
   5.896 +          (objsize1 <= j && objsize2 > j) ||
   5.897 +          (objsize2 <= j && objsize1 > j)
   5.898 +        ) {
   5.899 +          /* bit differs, therefore keys are in separate branches */
   5.900 +          return false;
   5.901 +        }
   5.902 +        /* increase bit counter for object size bits */
   5.903 +        j++;
   5.904 +      }
   5.905 +      /* all other bits describe latitude and longitude */
   5.906 +      else {
   5.907 +        /* check if bit differs in both keys */
   5.908 +        if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) {
   5.909 +          /* bit differs, therefore keys are in separate branches */
   5.910 +          return false;
   5.911 +        }
   5.912 +        /* increase bit counter for latitude/longitude bits */
   5.913 +        k++;
   5.914 +      }
   5.915 +    }
   5.916 +  }
   5.917 +  /* if not, keys are point keys */
   5.918 +  else {
   5.919 +    /* iterate through key bits */
   5.920 +    for (i=0; i<depth; i++) {
   5.921 +      /* check if bit differs in both keys */
   5.922 +      if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) {
   5.923 +        /* bit differs, therefore keys are in separate branches */
   5.924 +        return false;
   5.925 +      }
   5.926 +    }
   5.927 +  }
   5.928 +  /* return true because keys are in the same branch */
   5.929 +  return true;
   5.930 +}
   5.931 +
   5.932 +/* combine two keys into new key which covers both original keys */
   5.933 +/* (result stored in first argument) */
   5.934 +static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) {
   5.935 +  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
   5.936 +  /* determine smallest depth */
   5.937 +  int depth1 = PGL_KEY_NODEDEPTH(dst);
   5.938 +  int depth2 = PGL_KEY_NODEDEPTH(src);
   5.939 +  int depth = (depth1 < depth2) ? depth1 : depth2;
   5.940 +  /* check if keys are area keys (assuming that both keys have same type) */
   5.941 +  if (PGL_KEY_IS_AREAKEY(dst)) {
   5.942 +    pgl_areakey dstbuf = { 0, };  /* destination buffer (cleared) */
   5.943 +    int j = 0;  /* bit offset for logarithmic object size bits */
   5.944 +    int k = 0;  /* bit offset for latitude and longitude */
   5.945 +    /* fetch logarithmic object size information */
   5.946 +    int objsize1 = PGL_KEY_OBJSIZE(dst);
   5.947 +    int objsize2 = PGL_KEY_OBJSIZE(src);
   5.948 +    /* handle special cases for empty objects (universal and empty keys) */
   5.949 +    if (
   5.950 +      objsize1 > PGL_AREAKEY_MAXOBJSIZE ||
   5.951 +      objsize2 > PGL_AREAKEY_MAXOBJSIZE
   5.952 +    ) {
   5.953 +      if (
   5.954 +        objsize1 == PGL_KEY_OBJSIZE_EMPTY &&
   5.955 +        objsize2 == PGL_KEY_OBJSIZE_EMPTY
   5.956 +      ) pgl_key_set_empty(dst);
   5.957 +      else pgl_key_set_universal(dst);
   5.958 +      return;
   5.959 +    }
   5.960 +    /* iterate through key bits */
   5.961 +    for (i=0; i<depth; i++) {
   5.962 +      /* every second bit is a bit describing the object size */
   5.963 +      if (i%2 == 0) {
   5.964 +        /* increase bit counter for object size bits first */
   5.965 +        /* (handy when setting objsize variable) */
   5.966 +        j++;
   5.967 +        /* check if object size bit is set in neither key */
   5.968 +        if (objsize1 >= j && objsize2 >= j) {
   5.969 +          /* set objsize in destination buffer to indicate that size bit is
   5.970 +             unset in destination buffer at the current bit position */
   5.971 +          dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j;
   5.972 +        }
   5.973 +        /* break if object size bit is set in one key only */
   5.974 +        else if (objsize1 >= j || objsize2 >= j) break;
   5.975 +      }
   5.976 +      /* all other bits describe latitude and longitude */
   5.977 +      else {
   5.978 +        /* break if bit differs in both keys */
   5.979 +        if (PGL_KEY_LATLONBIT(dst, k)) {
   5.980 +          if (!PGL_KEY_LATLONBIT(src, k)) break;
   5.981 +          /* but set bit in destination buffer if bit is set in both keys */
   5.982 +          dstbuf[k/8] |= 0x80 >> (k%8);
   5.983 +        } else if (PGL_KEY_LATLONBIT(src, k)) break;
   5.984 +        /* increase bit counter for latitude/longitude bits */
   5.985 +        k++;
   5.986 +      }
   5.987 +    }
   5.988 +    /* set common node depth and type bit (type bit = 1) */
   5.989 +    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i;
   5.990 +    /* copy contents of destination buffer to first key */
   5.991 +    memcpy(dst, dstbuf, sizeof(pgl_areakey));
   5.992 +  }
   5.993 +  /* if not, keys are point keys */
   5.994 +  else {
   5.995 +    pgl_pointkey dstbuf = { 0, };  /* destination buffer (cleared) */
   5.996 +    /* iterate through key bits */
   5.997 +    for (i=0; i<depth; i++) {
   5.998 +      /* break if bit differs in both keys */
   5.999 +      if (PGL_KEY_LATLONBIT(dst, i)) {
  5.1000 +        if (!PGL_KEY_LATLONBIT(src, i)) break;
  5.1001 +        /* but set bit in destination buffer if bit is set in both keys */
  5.1002 +        dstbuf[i/8] |= 0x80 >> (i%8);
  5.1003 +      } else if (PGL_KEY_LATLONBIT(src, i)) break;
  5.1004 +    }
  5.1005 +    /* set common node depth (type bit = 0) */
  5.1006 +    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i;
  5.1007 +    /* copy contents of destination buffer to first key */
  5.1008 +    memcpy(dst, dstbuf, sizeof(pgl_pointkey));
  5.1009 +  }
  5.1010 +}
  5.1011 +
  5.1012 +/* determine center(!) boundaries and radius estimation of index key */
  5.1013 +static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) {
  5.1014 +  int i;
  5.1015 +  /* determine node depth */
  5.1016 +  int depth = PGL_KEY_NODEDEPTH(key);
  5.1017 +  /* center point of possible result */
  5.1018 +  double lat = 0;
  5.1019 +  double lon = 0;
  5.1020 +  /* maximum distance of real center point from key center */
  5.1021 +  double dlat = 90;
  5.1022 +  double dlon = 180;
  5.1023 +  /* maximum radius of contained objects */
  5.1024 +  double radius = 0;  /* always return zero for point index keys */
  5.1025 +  /* check if key is area key */
  5.1026 +  if (PGL_KEY_IS_AREAKEY(key)) {
  5.1027 +    /* get logarithmic object size */
  5.1028 +    int objsize = PGL_KEY_OBJSIZE(key);
  5.1029 +    /* handle special cases for empty objects (universal and empty keys) */
  5.1030 +    if (objsize == PGL_KEY_OBJSIZE_EMPTY) {
  5.1031 +      pgl_box_set_empty(box);
  5.1032 +      return 0;
  5.1033 +    } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) {
  5.1034 +      box->lat_min = -90;
  5.1035 +      box->lat_max =  90;
  5.1036 +      box->lon_min = -180;
  5.1037 +      box->lon_max =  180;
  5.1038 +      return 0;  /* any value >= 0 would do */
  5.1039 +    }
  5.1040 +    /* calculate maximum possible radius of objects covered by the given key */
  5.1041 +    if (objsize == 0) radius = INFINITY;
  5.1042 +    else {
  5.1043 +      radius = PGL_AREAKEY_REFOBJSIZE;
  5.1044 +      while (--objsize) radius /= M_SQRT2;
  5.1045 +    }
  5.1046 +    /* iterate over latitude and longitude bits in key */
  5.1047 +    /* (every second bit is a latitude or longitude bit) */
  5.1048 +    for (i=0; i<depth/2; i++) {
  5.1049 +      /* check if latitude bit */
  5.1050 +      if (i%2 == 0) {
  5.1051 +        /* cut latitude dimension in half */
  5.1052 +        dlat /= 2;
  5.1053 +        /* increase center latitude if bit is 1, otherwise decrease */
  5.1054 +        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  5.1055 +        else lat -= dlat;
  5.1056 +      }
  5.1057 +      /* otherwise longitude bit */
  5.1058 +      else {
  5.1059 +        /* cut longitude dimension in half */
  5.1060 +        dlon /= 2;
  5.1061 +        /* increase center longitude if bit is 1, otherwise decrease */
  5.1062 +        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  5.1063 +        else lon -= dlon;
  5.1064 +      }
  5.1065 +    }
  5.1066 +  }
  5.1067 +  /* if not, keys are point keys */
  5.1068 +  else {
  5.1069 +    /* iterate over all bits in key */
  5.1070 +    for (i=0; i<depth; i++) {
  5.1071 +      /* check if latitude bit */
  5.1072 +      if (i%2 == 0) {
  5.1073 +        /* cut latitude dimension in half */
  5.1074 +        dlat /= 2;
  5.1075 +        /* increase center latitude if bit is 1, otherwise decrease */
  5.1076 +        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  5.1077 +        else lat -= dlat;
  5.1078 +      }
  5.1079 +      /* otherwise longitude bit */
  5.1080 +      else {
  5.1081 +        /* cut longitude dimension in half */
  5.1082 +        dlon /= 2;
  5.1083 +        /* increase center longitude if bit is 1, otherwise decrease */
  5.1084 +        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  5.1085 +        else lon -= dlon;
  5.1086 +      }
  5.1087 +    }
  5.1088 +  }
  5.1089 +  /* calculate boundaries from center point and remaining dlat and dlon */
  5.1090 +  /* (return values through pointer to box) */
  5.1091 +  box->lat_min = lat - dlat;
  5.1092 +  box->lat_max = lat + dlat;
  5.1093 +  box->lon_min = lon - dlon;
  5.1094 +  box->lon_max = lon + dlon;
  5.1095 +  /* return radius (as a function return value) */
  5.1096 +  return radius;
  5.1097 +}
  5.1098 +
  5.1099 +/* estimator function for distance between point and index key */
  5.1100 +/* allowed to return smaller values than actually correct */
  5.1101 +static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
  5.1102 +  pgl_box box;  /* center(!) bounding box of area index key */
  5.1103 +  /* calculate center(!) bounding box and maximum radius of objects covered
  5.1104 +     by area index key (radius is zero for point index keys) */
  5.1105 +  double distance = pgl_key_to_box(key, &box);
  5.1106 +  /* calculate estimated distance between bounding box of center point of
  5.1107 +     indexed object and point passed as second argument, then substract maximum
  5.1108 +     radius of objects covered by index key */
  5.1109 +  /* (use PGL_FPE_SAFETY factor to cope with minor floating point errors) */
  5.1110 +  distance = (
  5.1111 +    pgl_estimate_point_box_distance(point, &box) / PGL_FPE_SAFETY -
  5.1112 +    distance * PGL_FPE_SAFETY
  5.1113 +  );
  5.1114 +  /* truncate negative results to zero */
  5.1115 +  if (distance <= 0) distance = 0;
  5.1116 +  /* return result */
  5.1117 +  return distance;
  5.1118 +}
  5.1119 +
  5.1120 +
  5.1121 +/*---------------------------------*
  5.1122 + *  helper functions for text I/O  *
  5.1123 + *---------------------------------*/
  5.1124 +
  5.1125 +#define PGL_NUMBUFLEN 64  /* buffer size for number to string conversion */
  5.1126 +
  5.1127 +/* convert floating point number to string (round-trip safe) */
  5.1128 +static void pgl_print_float(char *buf, double flt) {
  5.1129 +  /* check if number is integral */
  5.1130 +  if (trunc(flt) == flt) {
  5.1131 +    /* for integral floats use maximum precision */
  5.1132 +    snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  5.1133 +  } else {
  5.1134 +    /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */
  5.1135 +    snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt);
  5.1136 +    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt);
  5.1137 +    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  5.1138 +  }
  5.1139 +}
  5.1140 +
  5.1141 +/* convert latitude floating point number (in degrees) to string */
  5.1142 +static void pgl_print_lat(char *buf, double lat) {
  5.1143 +  if (signbit(lat)) {
  5.1144 +    /* treat negative latitudes (including -0) as south */
  5.1145 +    snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat);
  5.1146 +  } else {
  5.1147 +    /* treat positive latitudes (including +0) as north */
  5.1148 +    snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat);
  5.1149 +  }
  5.1150 +}
  5.1151 +
  5.1152 +/* convert longitude floating point number (in degrees) to string */
  5.1153 +static void pgl_print_lon(char *buf, double lon) {
  5.1154 +  if (signbit(lon)) {
  5.1155 +    /* treat negative longitudes (including -0) as west */
  5.1156 +    snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon);
  5.1157 +  } else {
  5.1158 +    /* treat positive longitudes (including +0) as east */
  5.1159 +    snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon);
  5.1160 +  }
  5.1161 +}
  5.1162 +
  5.1163 +/* bit masks used as return value of pgl_scan() function */
  5.1164 +#define PGL_SCAN_NONE 0      /* no value has been parsed */
  5.1165 +#define PGL_SCAN_LAT (1<<0)  /* latitude has been parsed */
  5.1166 +#define PGL_SCAN_LON (1<<1)  /* longitude has been parsed */
  5.1167 +#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON)  /* bitwise OR of both */
  5.1168 +
  5.1169 +/* parse a coordinate (can be latitude or longitude) */
  5.1170 +static int pgl_scan(char **str, double *lat, double *lon) {
  5.1171 +  double val;
  5.1172 +  int len;
  5.1173 +  if (
  5.1174 +    sscanf(*str, " N %lf %n", &val, &len) ||
  5.1175 +    sscanf(*str, " n %lf %n", &val, &len)
  5.1176 +  ) {
  5.1177 +    *str += len; *lat = val; return PGL_SCAN_LAT;
  5.1178 +  }
  5.1179 +  if (
  5.1180 +    sscanf(*str, " S %lf %n", &val, &len) ||
  5.1181 +    sscanf(*str, " s %lf %n", &val, &len)
  5.1182 +  ) {
  5.1183 +    *str += len; *lat = -val; return PGL_SCAN_LAT;
  5.1184 +  }
  5.1185 +  if (
  5.1186 +    sscanf(*str, " E %lf %n", &val, &len) ||
  5.1187 +    sscanf(*str, " e %lf %n", &val, &len)
  5.1188 +  ) {
  5.1189 +    *str += len; *lon = val; return PGL_SCAN_LON;
  5.1190 +  }
  5.1191 +  if (
  5.1192 +    sscanf(*str, " W %lf %n", &val, &len) ||
  5.1193 +    sscanf(*str, " w %lf %n", &val, &len)
  5.1194 +  ) {
  5.1195 +    *str += len; *lon = -val; return PGL_SCAN_LON;
  5.1196 +  }
  5.1197 +  return PGL_SCAN_NONE;
  5.1198 +}
  5.1199 +
  5.1200 +
  5.1201 +/*-----------------*
  5.1202 + *  SQL functions  *
  5.1203 + *-----------------*/
  5.1204 +
  5.1205 +/* Note: These function names use "epoint", "ebox", etc. notation here instead
  5.1206 +   of "point", "box", etc. in order to distinguish them from any previously
  5.1207 +   defined functions. */
  5.1208 +
  5.1209 +/* function needed for dummy types and/or not implemented features */
  5.1210 +PG_FUNCTION_INFO_V1(pgl_notimpl);
  5.1211 +Datum pgl_notimpl(PG_FUNCTION_ARGS) {
  5.1212 +  ereport(ERROR, (errmsg("not implemented by pgLatLon")));
  5.1213 +}
  5.1214 +
  5.1215 +/* set point to latitude and longitude (including checks) */
  5.1216 +static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) {
  5.1217 +  /* reject infinite or NaN values */
  5.1218 +  if (!isfinite(lat) || !isfinite(lon)) {
  5.1219 +    ereport(ERROR, (
  5.1220 +      errcode(ERRCODE_DATA_EXCEPTION),
  5.1221 +      errmsg("epoint requires finite coordinates")
  5.1222 +    ));
  5.1223 +  }
  5.1224 +  /* check latitude bounds */
  5.1225 +  if (lat < -90) {
  5.1226 +    ereport(WARNING, (errmsg("latitude exceeds south pole")));
  5.1227 +    lat = -90;
  5.1228 +  } else if (lat > 90) {
  5.1229 +    ereport(WARNING, (errmsg("latitude exceeds north pole")));
  5.1230 +    lat = 90;
  5.1231 +  }
  5.1232 +  /* check longitude bounds */
  5.1233 +  if (lon < -180) {
  5.1234 +    ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized")));
  5.1235 +    lon += 360 - trunc(lon / 360) * 360;
  5.1236 +  } else if (lon > 180) {
  5.1237 +    ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized")));
  5.1238 +    lon -= 360 + trunc(lon / 360) * 360;
  5.1239 +  }
  5.1240 +  /* store rounded latitude/longitude values for round-trip safety */
  5.1241 +  point->lat = pgl_round(lat);
  5.1242 +  point->lon = pgl_round(lon);
  5.1243 +}
  5.1244 +
  5.1245 +/* create point ("epoint" in SQL) from latitude and longitude */
  5.1246 +PG_FUNCTION_INFO_V1(pgl_create_epoint);
  5.1247 +Datum pgl_create_epoint(PG_FUNCTION_ARGS) {
  5.1248 +  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  5.1249 +  pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1));
  5.1250 +  PG_RETURN_POINTER(point);
  5.1251 +}
  5.1252 +
  5.1253 +/* parse point ("epoint" in SQL) */
  5.1254 +/* format: '[NS]<float> [EW]<float>' */
  5.1255 +PG_FUNCTION_INFO_V1(pgl_epoint_in);
  5.1256 +Datum pgl_epoint_in(PG_FUNCTION_ARGS) {
  5.1257 +  char *str = PG_GETARG_CSTRING(0);  /* input string */
  5.1258 +  char *strptr = str;  /* current position within string */
  5.1259 +  int done = 0;        /* bit mask storing if latitude or longitude was read */
  5.1260 +  double lat, lon;     /* parsed values as double precision floats */
  5.1261 +  pgl_point *point;    /* return value (to be palloc'ed) */
  5.1262 +  /* parse two floats (each latitude or longitude) separated by white-space */
  5.1263 +  done |= pgl_scan(&strptr, &lat, &lon);
  5.1264 +  if (strptr != str && isspace(strptr[-1])) {
  5.1265 +    done |= pgl_scan(&strptr, &lat, &lon);
  5.1266 +  }
  5.1267 +  /* require end of string, and latitude and longitude parsed successfully */
  5.1268 +  if (strptr[0] || done != PGL_SCAN_LATLON) {
  5.1269 +    ereport(ERROR, (
  5.1270 +      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1271 +      errmsg("invalid input syntax for type epoint: \"%s\"", str)
  5.1272 +    ));
  5.1273 +  }
  5.1274 +  /* allocate memory for result */
  5.1275 +  point = (pgl_point *)palloc(sizeof(pgl_point));
  5.1276 +  /* set latitude and longitude (and perform checks) */
  5.1277 +  pgl_epoint_set_latlon(point, lat, lon);
  5.1278 +  /* return result */
  5.1279 +  PG_RETURN_POINTER(point);
  5.1280 +}
  5.1281 +
  5.1282 +/* create box ("ebox" in SQL) that is empty */
  5.1283 +PG_FUNCTION_INFO_V1(pgl_create_empty_ebox);
  5.1284 +Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) {
  5.1285 +  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1286 +  pgl_box_set_empty(box);
  5.1287 +  PG_RETURN_POINTER(box);
  5.1288 +}
  5.1289 +
  5.1290 +/* set box to given boundaries (including checks) */
  5.1291 +static void pgl_ebox_set_boundaries(
  5.1292 +  pgl_box *box,
  5.1293 +  double lat_min, double lat_max, double lon_min, double lon_max
  5.1294 +) {
  5.1295 +  /* if minimum latitude is greater than maximum latitude, return empty box */
  5.1296 +  if (lat_min > lat_max) {
  5.1297 +    pgl_box_set_empty(box);
  5.1298 +    return;
  5.1299 +  }
  5.1300 +  /* otherwise reject infinite or NaN values */
  5.1301 +  if (
  5.1302 +    !isfinite(lat_min) || !isfinite(lat_max) ||
  5.1303 +    !isfinite(lon_min) || !isfinite(lon_max)
  5.1304 +  ) {
  5.1305 +    ereport(ERROR, (
  5.1306 +      errcode(ERRCODE_DATA_EXCEPTION),
  5.1307 +      errmsg("ebox requires finite coordinates")
  5.1308 +    ));
  5.1309 +  }
  5.1310 +  /* check latitude bounds */
  5.1311 +  if (lat_max < -90) {
  5.1312 +    ereport(WARNING, (errmsg("northern latitude exceeds south pole")));
  5.1313 +    lat_max = -90;
  5.1314 +  } else if (lat_max > 90) {
  5.1315 +    ereport(WARNING, (errmsg("northern latitude exceeds north pole")));
  5.1316 +    lat_max = 90;
  5.1317 +  }
  5.1318 +  if (lat_min < -90) {
  5.1319 +    ereport(WARNING, (errmsg("southern latitude exceeds south pole")));
  5.1320 +    lat_min = -90;
  5.1321 +  } else if (lat_min > 90) {
  5.1322 +    ereport(WARNING, (errmsg("southern latitude exceeds north pole")));
  5.1323 +    lat_min = 90;
  5.1324 +  }
  5.1325 +  /* check if all longitudes are included */
  5.1326 +  if (lon_max - lon_min >= 360) {
  5.1327 +    if (lon_max - lon_min > 360) ereport(WARNING, (
  5.1328 +      errmsg("longitude coverage greater than 360 degrees")
  5.1329 +    ));
  5.1330 +    lon_min = -180;
  5.1331 +    lon_max = 180;
  5.1332 +  } else {
  5.1333 +    /* normalize longitude bounds */
  5.1334 +    if      (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360;
  5.1335 +    else if (lon_min >  180) lon_min -= 360 + trunc(lon_min / 360) * 360;
  5.1336 +    if      (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360;
  5.1337 +    else if (lon_max >  180) lon_max -= 360 + trunc(lon_max / 360) * 360;
  5.1338 +  }
  5.1339 +  /* store rounded latitude/longitude values for round-trip safety */
  5.1340 +  box->lat_min = pgl_round(lat_min);
  5.1341 +  box->lat_max = pgl_round(lat_max);
  5.1342 +  box->lon_min = pgl_round(lon_min);
  5.1343 +  box->lon_max = pgl_round(lon_max);
  5.1344 +  /* ensure that rounding does not change orientation */
  5.1345 +  if (lon_min > lon_max && box->lon_min == box->lon_max) {
  5.1346 +    box->lon_min = -180;
  5.1347 +    box->lon_max = 180;
  5.1348 +  }
  5.1349 +}
  5.1350 +
  5.1351 +/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */
  5.1352 +PG_FUNCTION_INFO_V1(pgl_create_ebox);
  5.1353 +Datum pgl_create_ebox(PG_FUNCTION_ARGS) {
  5.1354 +  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1355 +  pgl_ebox_set_boundaries(
  5.1356 +    box,
  5.1357 +    PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1),
  5.1358 +    PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3)
  5.1359 +  );
  5.1360 +  PG_RETURN_POINTER(box);
  5.1361 +}
  5.1362 +
  5.1363 +/* create box ("ebox" in SQL) from two points ("epoint"s) */
  5.1364 +/* (can not be used to cover a longitude range of more than 120 degrees) */
  5.1365 +PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints);
  5.1366 +Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) {
  5.1367 +  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  5.1368 +  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  5.1369 +  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1370 +  double lat_min, lat_max, lon_min, lon_max;
  5.1371 +  double dlon;  /* longitude range (delta longitude) */
  5.1372 +  /* order latitude and longitude boundaries */
  5.1373 +  if (point2->lat < point1->lat) {
  5.1374 +    lat_min = point2->lat;
  5.1375 +    lat_max = point1->lat;
  5.1376 +  } else {
  5.1377 +    lat_min = point1->lat;
  5.1378 +    lat_max = point2->lat;
  5.1379 +  }
  5.1380 +  if (point2->lon < point1->lon) {
  5.1381 +    lon_min = point2->lon;
  5.1382 +    lon_max = point1->lon;
  5.1383 +  } else {
  5.1384 +    lon_min = point1->lon;
  5.1385 +    lon_max = point2->lon;
  5.1386 +  }
  5.1387 +  /* calculate longitude range (round to avoid floating point errors) */
  5.1388 +  dlon = pgl_round(lon_max - lon_min);
  5.1389 +  /* determine east-west direction */
  5.1390 +  if (dlon >= 240) {
  5.1391 +    /* assume that 180th meridian is crossed and swap min/max longitude */
  5.1392 +    double swap = lon_min; lon_min = lon_max; lon_max = swap;
  5.1393 +  } else if (dlon > 120) {
  5.1394 +    /* unclear orientation since delta longitude > 120 */
  5.1395 +    ereport(ERROR, (
  5.1396 +      errcode(ERRCODE_DATA_EXCEPTION),
  5.1397 +      errmsg("can not determine east/west orientation for ebox")
  5.1398 +    ));
  5.1399 +  }
  5.1400 +  /* use boundaries to setup box (and perform checks) */
  5.1401 +  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  5.1402 +  /* return result */
  5.1403 +  PG_RETURN_POINTER(box);
  5.1404 +}
  5.1405 +
  5.1406 +/* parse box ("ebox" in SQL) */
  5.1407 +/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>'
  5.1408 +       or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */
  5.1409 +PG_FUNCTION_INFO_V1(pgl_ebox_in);
  5.1410 +Datum pgl_ebox_in(PG_FUNCTION_ARGS) {
  5.1411 +  char *str = PG_GETARG_CSTRING(0);  /* input string */
  5.1412 +  char *str_lower;     /* lower case version of input string */
  5.1413 +  char *strptr;        /* current position within string */
  5.1414 +  int valid;           /* number of valid chars */
  5.1415 +  int done;            /* specifies if latitude or longitude was read */
  5.1416 +  double val;          /* temporary variable */
  5.1417 +  int lat_count = 0;   /* count of latitude values parsed */
  5.1418 +  int lon_count = 0;   /* count of longitufde values parsed */
  5.1419 +  double lat_min, lat_max, lon_min, lon_max;  /* see pgl_box struct */
  5.1420 +  pgl_box *box;        /* return value (to be palloc'ed) */
  5.1421 +  /* lowercase input */
  5.1422 +  str_lower = psprintf("%s", str);
  5.1423 +  for (strptr=str_lower; *strptr; strptr++) {
  5.1424 +    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  5.1425 +  }
  5.1426 +  /* reset reading position to start of (lowercase) string */
  5.1427 +  strptr = str_lower;
  5.1428 +  /* check if empty box */
  5.1429 +  valid = 0;
  5.1430 +  sscanf(strptr, " empty %n", &valid);
  5.1431 +  if (valid && strptr[valid] == 0) {
  5.1432 +    /* allocate and return empty box */
  5.1433 +    box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1434 +    pgl_box_set_empty(box);
  5.1435 +    PG_RETURN_POINTER(box);
  5.1436 +  }
  5.1437 +  /* demand four blocks separated by whitespace */
  5.1438 +  valid = 0;
  5.1439 +  sscanf(strptr, " %*s %*s %*s %*s %n", &valid);
  5.1440 +  /* if four blocks separated by whitespace exist, parse those blocks */
  5.1441 +  if (strptr[valid] == 0) while (strptr[0]) {
  5.1442 +    /* parse either latitude or longitude (whichever found in input string) */
  5.1443 +    done = pgl_scan(&strptr, &val, &val);
  5.1444 +    /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */
  5.1445 +    if (done == PGL_SCAN_LAT) {
  5.1446 +      if (!lat_count) lat_min = val; else lat_max = val;
  5.1447 +      lat_count++;
  5.1448 +    } else if (done == PGL_SCAN_LON) {
  5.1449 +      if (!lon_count) lon_min = val; else lon_max = val;
  5.1450 +      lon_count++;
  5.1451 +    } else {
  5.1452 +      break;
  5.1453 +    }
  5.1454 +  }
  5.1455 +  /* require end of string, and two latitude and two longitude values */
  5.1456 +  if (strptr[0] || lat_count != 2 || lon_count != 2) {
  5.1457 +    ereport(ERROR, (
  5.1458 +      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1459 +      errmsg("invalid input syntax for type ebox: \"%s\"", str)
  5.1460 +    ));
  5.1461 +  }
  5.1462 +  /* free lower case string */
  5.1463 +  pfree(str_lower);
  5.1464 +  /* order boundaries (maximum greater than minimum) */
  5.1465 +  if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; }
  5.1466 +  if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; }
  5.1467 +  /* allocate memory for result */
  5.1468 +  box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1469 +  /* set boundaries (and perform checks) */
  5.1470 +  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  5.1471 +  /* return result */
  5.1472 +  PG_RETURN_POINTER(box);
  5.1473 +}
  5.1474 +
  5.1475 +/* set circle to given latitude, longitude, and radius (including checks) */
  5.1476 +static void pgl_ecircle_set_latlon_radius(
  5.1477 +  pgl_circle *circle, double lat, double lon, double radius
  5.1478 +) {
  5.1479 +  /* set center point (including checks) */
  5.1480 +  pgl_epoint_set_latlon(&(circle->center), lat, lon);
  5.1481 +  /* handle non-positive radius */
  5.1482 +  if (isnan(radius)) {
  5.1483 +    ereport(ERROR, (
  5.1484 +      errcode(ERRCODE_DATA_EXCEPTION),
  5.1485 +      errmsg("invalid radius for ecircle")
  5.1486 +    ));
  5.1487 +  }
  5.1488 +  if (radius == 0) radius = 0;  /* avoids -0 */
  5.1489 +  else if (radius < 0) {
  5.1490 +    if (isfinite(radius)) {
  5.1491 +      ereport(NOTICE, (errmsg("negative radius converted to minus infinity")));
  5.1492 +    }
  5.1493 +    radius = -INFINITY;
  5.1494 +  }
  5.1495 +  /* store radius (round-trip safety is ensured by pgl_print_float) */
  5.1496 +  circle->radius = radius;
  5.1497 +}
  5.1498 +
  5.1499 +/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */
  5.1500 +PG_FUNCTION_INFO_V1(pgl_create_ecircle);
  5.1501 +Datum pgl_create_ecircle(PG_FUNCTION_ARGS) {
  5.1502 +  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  5.1503 +  pgl_ecircle_set_latlon_radius(
  5.1504 +    circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2)
  5.1505 +  );
  5.1506 +  PG_RETURN_POINTER(circle);
  5.1507 +}
  5.1508 +
  5.1509 +/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */
  5.1510 +PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint);
  5.1511 +Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) {
  5.1512 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1513 +  double radius = PG_GETARG_FLOAT8(1);
  5.1514 +  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  5.1515 +  /* set latitude, longitude, radius (and perform checks) */
  5.1516 +  pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius);
  5.1517 +  /* return result */
  5.1518 +  PG_RETURN_POINTER(circle);
  5.1519 +}
  5.1520 +
  5.1521 +/* parse circle ("ecircle" in SQL) */
  5.1522 +/* format: '[NS]<float> [EW]<float> <float>' */
  5.1523 +PG_FUNCTION_INFO_V1(pgl_ecircle_in);
  5.1524 +Datum pgl_ecircle_in(PG_FUNCTION_ARGS) {
  5.1525 +  char *str = PG_GETARG_CSTRING(0);  /* input string */
  5.1526 +  char *strptr = str;       /* current position within string */
  5.1527 +  double lat, lon, radius;  /* parsed values as double precision flaots */
  5.1528 +  int valid = 0;            /* number of valid chars */
  5.1529 +  int done = 0;             /* stores if latitude and/or longitude was read */
  5.1530 +  pgl_circle *circle;       /* return value (to be palloc'ed) */
  5.1531 +  /* demand three blocks separated by whitespace */
  5.1532 +  sscanf(strptr, " %*s %*s %*s %n", &valid);
  5.1533 +  /* if three blocks separated by whitespace exist, parse those blocks */
  5.1534 +  if (strptr[valid] == 0) {
  5.1535 +    /* parse latitude and longitude */
  5.1536 +    done |= pgl_scan(&strptr, &lat, &lon);
  5.1537 +    done |= pgl_scan(&strptr, &lat, &lon);
  5.1538 +    /* parse radius (while incrementing strptr by number of bytes parsed) */
  5.1539 +    valid = 0;
  5.1540 +    if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid;
  5.1541 +  }
  5.1542 +  /* require end of string and both latitude and longitude being parsed */
  5.1543 +  if (strptr[0] || done != PGL_SCAN_LATLON) {
  5.1544 +    ereport(ERROR, (
  5.1545 +      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1546 +      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  5.1547 +    ));
  5.1548 +  }
  5.1549 +  /* allocate memory for result */
  5.1550 +  circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  5.1551 +  /* set latitude, longitude, radius (and perform checks) */
  5.1552 +  pgl_ecircle_set_latlon_radius(circle, lat, lon, radius);
  5.1553 +  /* return result */
  5.1554 +  PG_RETURN_POINTER(circle);
  5.1555 +}
  5.1556 +
  5.1557 +/* parse cluster ("ecluster" in SQL) */
  5.1558 +PG_FUNCTION_INFO_V1(pgl_ecluster_in);
  5.1559 +Datum pgl_ecluster_in(PG_FUNCTION_ARGS) {
  5.1560 +  int i;
  5.1561 +  char *str = PG_GETARG_CSTRING(0);  /* input string */
  5.1562 +  char *str_lower;         /* lower case version of input string */
  5.1563 +  char *strptr;            /* pointer to current reading position of input */
  5.1564 +  int npoints_total = 0;   /* total number of points in cluster */
  5.1565 +  int nentries = 0;        /* total number of entries */
  5.1566 +  pgl_newentry *entries;   /* array of pgl_newentry to create pgl_cluster */
  5.1567 +  int entries_buflen = 4;  /* maximum number of elements in entries array */
  5.1568 +  int valid;               /* number of valid chars processed */
  5.1569 +  double lat, lon;         /* latitude and longitude of parsed point */
  5.1570 +  int entrytype;           /* current entry type */
  5.1571 +  int npoints;             /* number of points in current entry */
  5.1572 +  pgl_point *points;       /* array of pgl_point for pgl_newentry */
  5.1573 +  int points_buflen;       /* maximum number of elements in points array */
  5.1574 +  int done;                /* return value of pgl_scan function */
  5.1575 +  pgl_cluster *cluster;    /* created cluster */
  5.1576 +  /* lowercase input */
  5.1577 +  str_lower = psprintf("%s", str);
  5.1578 +  for (strptr=str_lower; *strptr; strptr++) {
  5.1579 +    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  5.1580 +  }
  5.1581 +  /* reset reading position to start of (lowercase) string */
  5.1582 +  strptr = str_lower;
  5.1583 +  /* allocate initial buffer for entries */
  5.1584 +  entries = palloc(entries_buflen * sizeof(pgl_newentry));
  5.1585 +  /* parse until end of string */
  5.1586 +  while (strptr[0]) {
  5.1587 +    /* require previous white-space or closing parenthesis before next token */
  5.1588 +    if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') {
  5.1589 +      goto pgl_ecluster_in_error;
  5.1590 +    }
  5.1591 +    /* ignore token "empty" */
  5.1592 +    valid = 0; sscanf(strptr, " empty %n", &valid);
  5.1593 +    if (valid) { strptr += valid; continue; }
  5.1594 +    /* test for "point" token */
  5.1595 +    valid = 0; sscanf(strptr, " point ( %n", &valid);
  5.1596 +    if (valid) {
  5.1597 +      strptr += valid;
  5.1598 +      entrytype = PGL_ENTRY_POINT;
  5.1599 +      goto pgl_ecluster_in_type_ok;
  5.1600 +    }
  5.1601 +    /* test for "path" token */
  5.1602 +    valid = 0; sscanf(strptr, " path ( %n", &valid);
  5.1603 +    if (valid) {
  5.1604 +      strptr += valid;
  5.1605 +      entrytype = PGL_ENTRY_PATH;
  5.1606 +      goto pgl_ecluster_in_type_ok;
  5.1607 +    }
  5.1608 +    /* test for "outline" token */
  5.1609 +    valid = 0; sscanf(strptr, " outline ( %n", &valid);
  5.1610 +    if (valid) {
  5.1611 +      strptr += valid;
  5.1612 +      entrytype = PGL_ENTRY_OUTLINE;
  5.1613 +      goto pgl_ecluster_in_type_ok;
  5.1614 +    }
  5.1615 +    /* test for "polygon" token */
  5.1616 +    valid = 0; sscanf(strptr, " polygon ( %n", &valid);
  5.1617 +    if (valid) {
  5.1618 +      strptr += valid;
  5.1619 +      entrytype = PGL_ENTRY_POLYGON;
  5.1620 +      goto pgl_ecluster_in_type_ok;
  5.1621 +    }
  5.1622 +    /* error if no valid token found */
  5.1623 +    goto pgl_ecluster_in_error;
  5.1624 +    pgl_ecluster_in_type_ok:
  5.1625 +    /* check if pgl_newentry array needs to grow */
  5.1626 +    if (nentries == entries_buflen) {
  5.1627 +      pgl_newentry *newbuf;
  5.1628 +      entries_buflen *= 2;
  5.1629 +      newbuf = palloc(entries_buflen * sizeof(pgl_newentry));
  5.1630 +      memcpy(newbuf, entries, nentries * sizeof(pgl_newentry));
  5.1631 +      pfree(entries);
  5.1632 +      entries = newbuf;
  5.1633 +    }
  5.1634 +    /* reset number of points for current entry */
  5.1635 +    npoints = 0;
  5.1636 +    /* allocate array for points */
  5.1637 +    points_buflen = 4;
  5.1638 +    points = palloc(points_buflen * sizeof(pgl_point));
  5.1639 +    /* parse until closing parenthesis */
  5.1640 +    while (strptr[0] != ')') {
  5.1641 +      /* error on unexpected end of string */
  5.1642 +      if (strptr[0] == 0) goto pgl_ecluster_in_error;
  5.1643 +      /* mark neither latitude nor longitude as read */
  5.1644 +      done = PGL_SCAN_NONE;
  5.1645 +      /* require white-space before second, third, etc. point */
  5.1646 +      if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  5.1647 +      /* scan latitude (or longitude) */
  5.1648 +      done |= pgl_scan(&strptr, &lat, &lon);
  5.1649 +      /* require white-space before second coordinate */
  5.1650 +      if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  5.1651 +      /* scan longitude (or latitude) */
  5.1652 +      done |= pgl_scan(&strptr, &lat, &lon);
  5.1653 +      /* error unless both latitude and longitude were parsed */
  5.1654 +      if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error;
  5.1655 +      /* throw error if number of points is too high */
  5.1656 +      if (npoints_total == PGL_CLUSTER_MAXPOINTS) {
  5.1657 +        ereport(ERROR, (
  5.1658 +          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1659 +          errmsg(
  5.1660 +            "too many points for ecluster entry (maximum %i)",
  5.1661 +            PGL_CLUSTER_MAXPOINTS
  5.1662 +          )
  5.1663 +        ));
  5.1664 +      }
  5.1665 +      /* check if pgl_point array needs to grow */
  5.1666 +      if (npoints == points_buflen) {
  5.1667 +        pgl_point *newbuf;
  5.1668 +        points_buflen *= 2;
  5.1669 +        newbuf = palloc(points_buflen * sizeof(pgl_point));
  5.1670 +        memcpy(newbuf, points, npoints * sizeof(pgl_point));
  5.1671 +        pfree(points);
  5.1672 +        points = newbuf;
  5.1673 +      }
  5.1674 +      /* append point to pgl_point array (includes checks) */
  5.1675 +      pgl_epoint_set_latlon(&(points[npoints++]), lat, lon);
  5.1676 +      /* increase total number of points */
  5.1677 +      npoints_total++;
  5.1678 +    }
  5.1679 +    /* error if entry has no points */
  5.1680 +    if (!npoints) goto pgl_ecluster_in_error;
  5.1681 +    /* entries with one point are automatically of type "point" */
  5.1682 +    if (npoints == 1) entrytype = PGL_ENTRY_POINT;
  5.1683 +    /* if entries have more than one point */
  5.1684 +    else {
  5.1685 +      /* throw error if entry type is "point" */
  5.1686 +      if (entrytype == PGL_ENTRY_POINT) {
  5.1687 +        ereport(ERROR, (
  5.1688 +          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1689 +          errmsg("invalid input syntax for type ecluster (point entry with more than one point)")
  5.1690 +        ));
  5.1691 +      }
  5.1692 +      /* coerce outlines and polygons with more than 2 points to be a path */
  5.1693 +      if (npoints == 2) entrytype = PGL_ENTRY_PATH;
  5.1694 +    }
  5.1695 +    /* append entry to pgl_newentry array */
  5.1696 +    entries[nentries].entrytype = entrytype;
  5.1697 +    entries[nentries].npoints = npoints;
  5.1698 +    entries[nentries].points = points;
  5.1699 +    nentries++;
  5.1700 +    /* consume closing parenthesis */
  5.1701 +    strptr++;
  5.1702 +    /* consume white-space */
  5.1703 +    while (isspace(strptr[0])) strptr++;
  5.1704 +  }
  5.1705 +  /* free lower case string */
  5.1706 +  pfree(str_lower);
  5.1707 +  /* create cluster from pgl_newentry array */
  5.1708 +  cluster = pgl_new_cluster(nentries, entries);
  5.1709 +  /* free pgl_newentry array */
  5.1710 +  for (i=0; i<nentries; i++) pfree(entries[i].points);
  5.1711 +  pfree(entries);
  5.1712 +  /* set bounding circle of cluster and check east/west orientation */
  5.1713 +  if (!pgl_finalize_cluster(cluster)) {
  5.1714 +    ereport(ERROR, (
  5.1715 +      errcode(ERRCODE_DATA_EXCEPTION),
  5.1716 +      errmsg("can not determine east/west orientation for ecluster"),
  5.1717 +      errhint("Ensure that each entry has a longitude span of less than 180 degrees.")
  5.1718 +    ));
  5.1719 +  }
  5.1720 +  /* return cluster */
  5.1721 +  PG_RETURN_POINTER(cluster);
  5.1722 +  /* code to throw error */
  5.1723 +  pgl_ecluster_in_error:
  5.1724 +  ereport(ERROR, (
  5.1725 +    errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  5.1726 +    errmsg("invalid input syntax for type ecluster: \"%s\"", str)
  5.1727 +  ));
  5.1728 +}
  5.1729 +
  5.1730 +/* convert point ("epoint") to string representation */
  5.1731 +PG_FUNCTION_INFO_V1(pgl_epoint_out);
  5.1732 +Datum pgl_epoint_out(PG_FUNCTION_ARGS) {
  5.1733 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1734 +  char latstr[PGL_NUMBUFLEN];
  5.1735 +  char lonstr[PGL_NUMBUFLEN];
  5.1736 +  pgl_print_lat(latstr, point->lat);
  5.1737 +  pgl_print_lon(lonstr, point->lon);
  5.1738 +  PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr));
  5.1739 +}
  5.1740 +
  5.1741 +/* convert box ("ebox") to string representation */
  5.1742 +PG_FUNCTION_INFO_V1(pgl_ebox_out);
  5.1743 +Datum pgl_ebox_out(PG_FUNCTION_ARGS) {
  5.1744 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  5.1745 +  double lon_min = box->lon_min;
  5.1746 +  double lon_max = box->lon_max;
  5.1747 +  char lat_min_str[PGL_NUMBUFLEN];
  5.1748 +  char lat_max_str[PGL_NUMBUFLEN];
  5.1749 +  char lon_min_str[PGL_NUMBUFLEN];
  5.1750 +  char lon_max_str[PGL_NUMBUFLEN];
  5.1751 +  /* return string "empty" if box is set to be empty */
  5.1752 +  if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty");
  5.1753 +  /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */
  5.1754 +  /* (required since pgl_box_in orders the longitude boundaries) */
  5.1755 +  if (lon_min > lon_max) {
  5.1756 +    if (lon_min + lon_max >= 0) lon_min -= 360;
  5.1757 +    else lon_max += 360;
  5.1758 +  }
  5.1759 +  /* format and return result */
  5.1760 +  pgl_print_lat(lat_min_str, box->lat_min);
  5.1761 +  pgl_print_lat(lat_max_str, box->lat_max);
  5.1762 +  pgl_print_lon(lon_min_str, lon_min);
  5.1763 +  pgl_print_lon(lon_max_str, lon_max);
  5.1764 +  PG_RETURN_CSTRING(psprintf(
  5.1765 +    "%s %s %s %s",
  5.1766 +    lat_min_str, lon_min_str, lat_max_str, lon_max_str
  5.1767 +  ));
  5.1768 +}
  5.1769 +
  5.1770 +/* convert circle ("ecircle") to string representation */
  5.1771 +PG_FUNCTION_INFO_V1(pgl_ecircle_out);
  5.1772 +Datum pgl_ecircle_out(PG_FUNCTION_ARGS) {
  5.1773 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  5.1774 +  char latstr[PGL_NUMBUFLEN];
  5.1775 +  char lonstr[PGL_NUMBUFLEN];
  5.1776 +  char radstr[PGL_NUMBUFLEN];
  5.1777 +  pgl_print_lat(latstr, circle->center.lat);
  5.1778 +  pgl_print_lon(lonstr, circle->center.lon);
  5.1779 +  pgl_print_float(radstr, circle->radius);
  5.1780 +  PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr));
  5.1781 +}
  5.1782 +
  5.1783 +/* convert cluster ("ecluster") to string representation */
  5.1784 +PG_FUNCTION_INFO_V1(pgl_ecluster_out);
  5.1785 +Datum pgl_ecluster_out(PG_FUNCTION_ARGS) {
  5.1786 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  5.1787 +  char latstr[PGL_NUMBUFLEN];  /* string buffer for latitude */
  5.1788 +  char lonstr[PGL_NUMBUFLEN];  /* string buffer for longitude */
  5.1789 +  char ***strings;     /* array of array of strings */
  5.1790 +  char *string;        /* string of current token */
  5.1791 +  char *res, *resptr;  /* result and pointer to current write position */
  5.1792 +  size_t reslen = 1;   /* length of result (init with 1 for terminator) */
  5.1793 +  int npoints;         /* number of points of current entry */
  5.1794 +  int i, j;            /* i: entry, j: point in entry */
  5.1795 +  /* handle empty clusters */
  5.1796 +  if (cluster->nentries == 0) {
  5.1797 +    /* free detoasted cluster (if copy) */
  5.1798 +    PG_FREE_IF_COPY(cluster, 0);
  5.1799 +    /* return static result */
  5.1800 +    PG_RETURN_CSTRING("empty");
  5.1801 +  }
  5.1802 +  /* allocate array of array of strings */
  5.1803 +  strings = palloc(cluster->nentries * sizeof(char **));
  5.1804 +  /* iterate over all entries in cluster */
  5.1805 +  for (i=0; i<cluster->nentries; i++) {
  5.1806 +    /* get number of points in entry */
  5.1807 +    npoints = cluster->entries[i].npoints;
  5.1808 +    /* allocate array of strings (one string for each point plus two extra) */
  5.1809 +    strings[i] = palloc((2 + npoints) * sizeof(char *));
  5.1810 +    /* determine opening string */
  5.1811 +    switch (cluster->entries[i].entrytype) {
  5.1812 +      case PGL_ENTRY_POINT:   string = (i==0)?"point ("  :" point (";   break;
  5.1813 +      case PGL_ENTRY_PATH:    string = (i==0)?"path ("   :" path (";    break;
  5.1814 +      case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break;
  5.1815 +      case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break;
  5.1816 +      default:                string = (i==0)?"unknown"  :" unknown";
  5.1817 +    }
  5.1818 +    /* use opening string as first string in array */
  5.1819 +    strings[i][0] = string;
  5.1820 +    /* update result length (for allocating result string later) */
  5.1821 +    reslen += strlen(string);
  5.1822 +    /* iterate over all points */
  5.1823 +    for (j=0; j<npoints; j++) {
  5.1824 +      /* create string representation of point */
  5.1825 +      pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat);
  5.1826 +      pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon);
  5.1827 +      string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr);
  5.1828 +      /* copy string pointer to string array */
  5.1829 +      strings[i][j+1] = string;
  5.1830 +      /* update result length (for allocating result string later) */
  5.1831 +      reslen += strlen(string);
  5.1832 +    }
  5.1833 +    /* use closing parenthesis as last string in array */
  5.1834 +    strings[i][npoints+1] = ")";
  5.1835 +    /* update result length (for allocating result string later) */
  5.1836 +    reslen++;
  5.1837 +  }
  5.1838 +  /* allocate result string */
  5.1839 +  res = palloc(reslen);
  5.1840 +  /* set write pointer to begin of result string */
  5.1841 +  resptr = res;
  5.1842 +  /* copy strings into result string */
  5.1843 +  for (i=0; i<cluster->nentries; i++) {
  5.1844 +    npoints = cluster->entries[i].npoints;
  5.1845 +    for (j=0; j<npoints+2; j++) {
  5.1846 +      string = strings[i][j];
  5.1847 +      strcpy(resptr, string);
  5.1848 +      resptr += strlen(string);
  5.1849 +      /* free strings allocated by psprintf */
  5.1850 +      if (j != 0 && j != npoints+1) pfree(string);
  5.1851 +    }
  5.1852 +    /* free array of strings */
  5.1853 +    pfree(strings[i]);
  5.1854 +  }
  5.1855 +  /* free array of array of strings */
  5.1856 +  pfree(strings);
  5.1857 +  /* free detoasted cluster (if copy) */
  5.1858 +  PG_FREE_IF_COPY(cluster, 0);
  5.1859 +  /* return result */
  5.1860 +  PG_RETURN_CSTRING(res);
  5.1861 +}
  5.1862 +
  5.1863 +/* binary input function for point ("epoint") */
  5.1864 +PG_FUNCTION_INFO_V1(pgl_epoint_recv);
  5.1865 +Datum pgl_epoint_recv(PG_FUNCTION_ARGS) {
  5.1866 +  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  5.1867 +  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  5.1868 +  point->lat = pq_getmsgfloat8(buf);
  5.1869 +  point->lon = pq_getmsgfloat8(buf);
  5.1870 +  PG_RETURN_POINTER(point);
  5.1871 +}
  5.1872 +
  5.1873 +/* binary input function for box ("ebox") */
  5.1874 +PG_FUNCTION_INFO_V1(pgl_ebox_recv);
  5.1875 +Datum pgl_ebox_recv(PG_FUNCTION_ARGS) {
  5.1876 +  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  5.1877 +  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  5.1878 +  box->lat_min = pq_getmsgfloat8(buf);
  5.1879 +  box->lat_max = pq_getmsgfloat8(buf);
  5.1880 +  box->lon_min = pq_getmsgfloat8(buf);
  5.1881 +  box->lon_max = pq_getmsgfloat8(buf);
  5.1882 +  PG_RETURN_POINTER(box);
  5.1883 +}
  5.1884 +
  5.1885 +/* binary input function for circle ("ecircle") */
  5.1886 +PG_FUNCTION_INFO_V1(pgl_ecircle_recv);
  5.1887 +Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) {
  5.1888 +  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  5.1889 +  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  5.1890 +  circle->center.lat = pq_getmsgfloat8(buf);
  5.1891 +  circle->center.lon = pq_getmsgfloat8(buf);
  5.1892 +  circle->radius = pq_getmsgfloat8(buf);
  5.1893 +  PG_RETURN_POINTER(circle);
  5.1894 +}
  5.1895 +
  5.1896 +/* TODO: binary receive function for cluster */
  5.1897 +
  5.1898 +/* binary output function for point ("epoint") */
  5.1899 +PG_FUNCTION_INFO_V1(pgl_epoint_send);
  5.1900 +Datum pgl_epoint_send(PG_FUNCTION_ARGS) {
  5.1901 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1902 +  StringInfoData buf;
  5.1903 +  pq_begintypsend(&buf);
  5.1904 +  pq_sendfloat8(&buf, point->lat);
  5.1905 +  pq_sendfloat8(&buf, point->lon);
  5.1906 +  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  5.1907 +}
  5.1908 +
  5.1909 +/* binary output function for box ("ebox") */
  5.1910 +PG_FUNCTION_INFO_V1(pgl_ebox_send);
  5.1911 +Datum pgl_ebox_send(PG_FUNCTION_ARGS) {
  5.1912 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  5.1913 +  StringInfoData buf;
  5.1914 +  pq_begintypsend(&buf);
  5.1915 +  pq_sendfloat8(&buf, box->lat_min);
  5.1916 +  pq_sendfloat8(&buf, box->lat_max);
  5.1917 +  pq_sendfloat8(&buf, box->lon_min);
  5.1918 +  pq_sendfloat8(&buf, box->lon_max);
  5.1919 +  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  5.1920 +}
  5.1921 +
  5.1922 +/* binary output function for circle ("ecircle") */
  5.1923 +PG_FUNCTION_INFO_V1(pgl_ecircle_send);
  5.1924 +Datum pgl_ecircle_send(PG_FUNCTION_ARGS) {
  5.1925 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  5.1926 +  StringInfoData buf;
  5.1927 +  pq_begintypsend(&buf);
  5.1928 +  pq_sendfloat8(&buf, circle->center.lat);
  5.1929 +  pq_sendfloat8(&buf, circle->center.lon);
  5.1930 +  pq_sendfloat8(&buf, circle->radius);
  5.1931 +  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  5.1932 +}
  5.1933 +
  5.1934 +/* TODO: binary send functions for cluster */
  5.1935 +
  5.1936 +/* cast point ("epoint") to box ("ebox") */
  5.1937 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox);
  5.1938 +Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) {
  5.1939 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1940 +  pgl_box *box = palloc(sizeof(pgl_box));
  5.1941 +  box->lat_min = point->lat;
  5.1942 +  box->lat_max = point->lat;
  5.1943 +  box->lon_min = point->lon;
  5.1944 +  box->lon_max = point->lon;
  5.1945 +  PG_RETURN_POINTER(box);
  5.1946 +}
  5.1947 +
  5.1948 +/* cast point ("epoint") to circle ("ecircle") */
  5.1949 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle);
  5.1950 +Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) {
  5.1951 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1952 +  pgl_circle *circle = palloc(sizeof(pgl_box));
  5.1953 +  circle->center = *point;
  5.1954 +  circle->radius = 0;
  5.1955 +  PG_RETURN_POINTER(circle);
  5.1956 +}
  5.1957 +
  5.1958 +/* cast point ("epoint") to cluster ("ecluster") */
  5.1959 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster);
  5.1960 +Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) {
  5.1961 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.1962 +  pgl_newentry entry;
  5.1963 +  entry.entrytype = PGL_ENTRY_POINT;
  5.1964 +  entry.npoints = 1;
  5.1965 +  entry.points = point;
  5.1966 +  PG_RETURN_POINTER(pgl_new_cluster(1, &entry));
  5.1967 +}
  5.1968 +
  5.1969 +/* cast box ("ebox") to cluster ("ecluster") */
  5.1970 +#define pgl_ebox_to_ecluster_macro(i, a, b) \
  5.1971 +  entries[i].entrytype = PGL_ENTRY_POLYGON; \
  5.1972 +  entries[i].npoints = 4; \
  5.1973 +  entries[i].points = points[i]; \
  5.1974 +  points[i][0].lat = box->lat_min; \
  5.1975 +  points[i][0].lon = (a); \
  5.1976 +  points[i][1].lat = box->lat_min; \
  5.1977 +  points[i][1].lon = (b); \
  5.1978 +  points[i][2].lat = box->lat_max; \
  5.1979 +  points[i][2].lon = (b); \
  5.1980 +  points[i][3].lat = box->lat_max; \
  5.1981 +  points[i][3].lon = (a);
  5.1982 +PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster);
  5.1983 +Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) {
  5.1984 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  5.1985 +  double lon, dlon;
  5.1986 +  int nentries;
  5.1987 +  pgl_newentry entries[3];
  5.1988 +  pgl_point points[3][4];
  5.1989 +  if (box->lat_min > box->lat_max) {
  5.1990 +    nentries = 0;
  5.1991 +  } else if (box->lon_min > box->lon_max) {
  5.1992 +    if (box->lon_min < 0) {
  5.1993 +      lon = pgl_round((box->lon_min + 180) / 2.0);
  5.1994 +      nentries = 3;
  5.1995 +      pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  5.1996 +      pgl_ebox_to_ecluster_macro(1, lon, 180);
  5.1997 +      pgl_ebox_to_ecluster_macro(2, -180, box->lon_max);
  5.1998 +    } else if (box->lon_max > 0) {
  5.1999 +      lon = pgl_round((box->lon_max - 180) / 2.0);
  5.2000 +      nentries = 3;
  5.2001 +      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  5.2002 +      pgl_ebox_to_ecluster_macro(1, -180, lon);
  5.2003 +      pgl_ebox_to_ecluster_macro(2, lon, box->lon_max);
  5.2004 +    } else {
  5.2005 +      nentries = 2;
  5.2006 +      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  5.2007 +      pgl_ebox_to_ecluster_macro(1, -180, box->lon_max);
  5.2008 +    }
  5.2009 +  } else {
  5.2010 +    dlon = pgl_round(box->lon_max - box->lon_min);
  5.2011 +    if (dlon < 180) {
  5.2012 +      nentries = 1;
  5.2013 +      pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max);
  5.2014 +    } else {
  5.2015 +      lon = pgl_round((box->lon_min + box->lon_max) / 2.0);
  5.2016 +      if (
  5.2017 +        pgl_round(lon - box->lon_min) < 180 &&
  5.2018 +        pgl_round(box->lon_max - lon) < 180
  5.2019 +      ) {
  5.2020 +        nentries = 2;
  5.2021 +        pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  5.2022 +        pgl_ebox_to_ecluster_macro(1, lon, box->lon_max);
  5.2023 +      } else {
  5.2024 +        nentries = 3;
  5.2025 +        pgl_ebox_to_ecluster_macro(0, box->lon_min, -60);
  5.2026 +        pgl_ebox_to_ecluster_macro(1, -60, 60);
  5.2027 +        pgl_ebox_to_ecluster_macro(2, 60, box->lon_max);
  5.2028 +      }
  5.2029 +    }
  5.2030 +  }
  5.2031 +  PG_RETURN_POINTER(pgl_new_cluster(nentries, entries));
  5.2032 +}
  5.2033 +
  5.2034 +/* extract latitude from point ("epoint") */
  5.2035 +PG_FUNCTION_INFO_V1(pgl_epoint_lat);
  5.2036 +Datum pgl_epoint_lat(PG_FUNCTION_ARGS) {
  5.2037 +  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat);
  5.2038 +}
  5.2039 +
  5.2040 +/* extract longitude from point ("epoint") */
  5.2041 +PG_FUNCTION_INFO_V1(pgl_epoint_lon);
  5.2042 +Datum pgl_epoint_lon(PG_FUNCTION_ARGS) {
  5.2043 +  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon);
  5.2044 +}
  5.2045 +
  5.2046 +/* extract minimum latitude from box ("ebox") */
  5.2047 +PG_FUNCTION_INFO_V1(pgl_ebox_lat_min);
  5.2048 +Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) {
  5.2049 +  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min);
  5.2050 +}
  5.2051 +
  5.2052 +/* extract maximum latitude from box ("ebox") */
  5.2053 +PG_FUNCTION_INFO_V1(pgl_ebox_lat_max);
  5.2054 +Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) {
  5.2055 +  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max);
  5.2056 +}
  5.2057 +
  5.2058 +/* extract minimum longitude from box ("ebox") */
  5.2059 +PG_FUNCTION_INFO_V1(pgl_ebox_lon_min);
  5.2060 +Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) {
  5.2061 +  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min);
  5.2062 +}
  5.2063 +
  5.2064 +/* extract maximum longitude from box ("ebox") */
  5.2065 +PG_FUNCTION_INFO_V1(pgl_ebox_lon_max);
  5.2066 +Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) {
  5.2067 +  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max);
  5.2068 +}
  5.2069 +
  5.2070 +/* extract center point from circle ("ecircle") */
  5.2071 +PG_FUNCTION_INFO_V1(pgl_ecircle_center);
  5.2072 +Datum pgl_ecircle_center(PG_FUNCTION_ARGS) {
  5.2073 +  PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center));
  5.2074 +}
  5.2075 +
  5.2076 +/* extract radius from circle ("ecircle") */
  5.2077 +PG_FUNCTION_INFO_V1(pgl_ecircle_radius);
  5.2078 +Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) {
  5.2079 +  PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius);
  5.2080 +}
  5.2081 +
  5.2082 +/* check if point is inside box (overlap operator "&&") in SQL */
  5.2083 +PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap);
  5.2084 +Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) {
  5.2085 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2086 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1);
  5.2087 +  PG_RETURN_BOOL(pgl_point_in_box(point, box));
  5.2088 +}
  5.2089 +
  5.2090 +/* check if point is inside circle (overlap operator "&&") in SQL */
  5.2091 +PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap);
  5.2092 +Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) {
  5.2093 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2094 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2095 +  PG_RETURN_BOOL(
  5.2096 +    pgl_distance(
  5.2097 +      point->lat, point->lon,
  5.2098 +      circle->center.lat, circle->center.lon
  5.2099 +    ) <= circle->radius
  5.2100 +  );
  5.2101 +}
  5.2102 +
  5.2103 +/* check if point is inside cluster (overlap operator "&&") in SQL */
  5.2104 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap);
  5.2105 +Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
  5.2106 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2107 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2108 +  bool retval = pgl_point_in_cluster(point, cluster);
  5.2109 +  PG_FREE_IF_COPY(cluster, 1);
  5.2110 +  PG_RETURN_BOOL(retval);
  5.2111 +}
  5.2112 +
  5.2113 +/* check if two boxes overlap (overlap operator "&&") in SQL */
  5.2114 +PG_FUNCTION_INFO_V1(pgl_ebox_overlap);
  5.2115 +Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) {
  5.2116 +  pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0);
  5.2117 +  pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1);
  5.2118 +  PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2));
  5.2119 +}
  5.2120 +
  5.2121 +/* check if two circles overlap (overlap operator "&&") in SQL */
  5.2122 +PG_FUNCTION_INFO_V1(pgl_ecircle_overlap);
  5.2123 +Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) {
  5.2124 +  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  5.2125 +  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2126 +  PG_RETURN_BOOL(
  5.2127 +    pgl_distance(
  5.2128 +      circle1->center.lat, circle1->center.lon,
  5.2129 +      circle2->center.lat, circle2->center.lon
  5.2130 +    ) <= circle1->radius + circle2->radius
  5.2131 +  );
  5.2132 +}
  5.2133 +
  5.2134 +/* check if circle and cluster overlap (overlap operator "&&") in SQL */
  5.2135 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap);
  5.2136 +Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) {
  5.2137 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  5.2138 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2139 +  bool retval = (
  5.2140 +    pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius
  5.2141 +  );
  5.2142 +  PG_FREE_IF_COPY(cluster, 1);
  5.2143 +  PG_RETURN_BOOL(retval);
  5.2144 +}
  5.2145 +
  5.2146 +/* calculate distance between two points ("<->" operator) in SQL */
  5.2147 +PG_FUNCTION_INFO_V1(pgl_epoint_distance);
  5.2148 +Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
  5.2149 +  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  5.2150 +  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  5.2151 +  PG_RETURN_FLOAT8(pgl_distance(
  5.2152 +    point1->lat, point1->lon, point2->lat, point2->lon
  5.2153 +  ));
  5.2154 +}
  5.2155 +
  5.2156 +/* calculate point to circle distance ("<->" operator) in SQL */
  5.2157 +PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance);
  5.2158 +Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) {
  5.2159 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2160 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2161 +  double distance = pgl_distance(
  5.2162 +    point->lat, point->lon, circle->center.lat, circle->center.lon
  5.2163 +  ) - circle->radius;
  5.2164 +  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  5.2165 +}
  5.2166 +
  5.2167 +/* calculate point to cluster distance ("<->" operator) in SQL */
  5.2168 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance);
  5.2169 +Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) {
  5.2170 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  5.2171 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2172 +  double distance = pgl_point_cluster_distance(point, cluster);
  5.2173 +  PG_FREE_IF_COPY(cluster, 1);
  5.2174 +  PG_RETURN_FLOAT8(distance);
  5.2175 +}
  5.2176 +
  5.2177 +/* calculate distance between two circles ("<->" operator) in SQL */
  5.2178 +PG_FUNCTION_INFO_V1(pgl_ecircle_distance);
  5.2179 +Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) {
  5.2180 +  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  5.2181 +  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2182 +  double distance = pgl_distance(
  5.2183 +    circle1->center.lat, circle1->center.lon,
  5.2184 +    circle2->center.lat, circle2->center.lon
  5.2185 +  ) - (circle1->radius + circle2->radius);
  5.2186 +  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  5.2187 +}
  5.2188 +
  5.2189 +/* calculate circle to cluster distance ("<->" operator) in SQL */
  5.2190 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance);
  5.2191 +Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) {
  5.2192 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  5.2193 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2194 +  double distance = (
  5.2195 +    pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius
  5.2196 +  );
  5.2197 +  PG_FREE_IF_COPY(cluster, 1);
  5.2198 +  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  5.2199 +}
  5.2200 +
  5.2201 +
  5.2202 +/*-----------------------------------------------------------*
  5.2203 + *  B-tree comparison operators and index support functions  *
  5.2204 + *-----------------------------------------------------------*/
  5.2205 +
  5.2206 +/* macro for a B-tree operator (without detoasting) */
  5.2207 +#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \
  5.2208 +  PG_FUNCTION_INFO_V1(func); \
  5.2209 +  Datum func(PG_FUNCTION_ARGS) { \
  5.2210 +    type *a = (type *)PG_GETARG_POINTER(0); \
  5.2211 +    type *b = (type *)PG_GETARG_POINTER(1); \
  5.2212 +    PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \
  5.2213 +  }
  5.2214 +
  5.2215 +/* macro for a B-tree comparison function (without detoasting) */
  5.2216 +#define PGL_BTREE_CMP(func, type, cmpfunc) \
  5.2217 +  PG_FUNCTION_INFO_V1(func); \
  5.2218 +  Datum func(PG_FUNCTION_ARGS) { \
  5.2219 +    type *a = (type *)PG_GETARG_POINTER(0); \
  5.2220 +    type *b = (type *)PG_GETARG_POINTER(1); \
  5.2221 +    PG_RETURN_INT32(cmpfunc(a, b)); \
  5.2222 +  }
  5.2223 +
  5.2224 +/* macro for a B-tree operator (with detoasting) */
  5.2225 +#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \
  5.2226 +  PG_FUNCTION_INFO_V1(func); \
  5.2227 +  Datum func(PG_FUNCTION_ARGS) { \
  5.2228 +    bool res; \
  5.2229 +    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  5.2230 +    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  5.2231 +    res = cmpfunc(a, b) oper 0; \
  5.2232 +    PG_FREE_IF_COPY(a, 0); \
  5.2233 +    PG_FREE_IF_COPY(b, 1); \
  5.2234 +    PG_RETURN_BOOL(res); \
  5.2235 +  }
  5.2236 +
  5.2237 +/* macro for a B-tree comparison function (with detoasting) */
  5.2238 +#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \
  5.2239 +  PG_FUNCTION_INFO_V1(func); \
  5.2240 +  Datum func(PG_FUNCTION_ARGS) { \
  5.2241 +    int32_t res; \
  5.2242 +    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  5.2243 +    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  5.2244 +    res = cmpfunc(a, b); \
  5.2245 +    PG_FREE_IF_COPY(a, 0); \
  5.2246 +    PG_FREE_IF_COPY(b, 1); \
  5.2247 +    PG_RETURN_INT32(res); \
  5.2248 +  }
  5.2249 +
  5.2250 +/* B-tree operators and comparison function for point */
  5.2251 +PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <)
  5.2252 +PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=)
  5.2253 +PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==)
  5.2254 +PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=)
  5.2255 +PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=)
  5.2256 +PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >)
  5.2257 +PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp)
  5.2258 +
  5.2259 +/* B-tree operators and comparison function for box */
  5.2260 +PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <)
  5.2261 +PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=)
  5.2262 +PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==)
  5.2263 +PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=)
  5.2264 +PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=)
  5.2265 +PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >)
  5.2266 +PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp)
  5.2267 +
  5.2268 +/* B-tree operators and comparison function for circle */
  5.2269 +PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <)
  5.2270 +PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=)
  5.2271 +PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==)
  5.2272 +PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=)
  5.2273 +PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=)
  5.2274 +PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >)
  5.2275 +PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp)
  5.2276 +
  5.2277 +
  5.2278 +/*--------------------------------*
  5.2279 + *  GiST index support functions  *
  5.2280 + *--------------------------------*/
  5.2281 +
  5.2282 +/* GiST "consistent" support function */
  5.2283 +PG_FUNCTION_INFO_V1(pgl_gist_consistent);
  5.2284 +Datum pgl_gist_consistent(PG_FUNCTION_ARGS) {
  5.2285 +  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  5.2286 +  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  5.2287 +  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  5.2288 +  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  5.2289 +  /* demand recheck because index and query methods are lossy */
  5.2290 +  *recheck = true;
  5.2291 +  /* strategy number 11: equality of two points */
  5.2292 +  if (strategy == 11) {
  5.2293 +    /* query datum is another point */
  5.2294 +    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  5.2295 +    /* convert other point to key */
  5.2296 +    pgl_pointkey querykey;
  5.2297 +    pgl_point_to_key(query, querykey);
  5.2298 +    /* return true if both keys overlap */
  5.2299 +    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  5.2300 +  }
  5.2301 +  /* strategy number 13: equality of two circles */
  5.2302 +  if (strategy == 13) {
  5.2303 +    /* query datum is another circle */
  5.2304 +    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2305 +    /* convert other circle to key */
  5.2306 +    pgl_areakey querykey;
  5.2307 +    pgl_circle_to_key(query, querykey);
  5.2308 +    /* return true if both keys overlap */
  5.2309 +    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  5.2310 +  }
  5.2311 +  /* for all remaining strategies, keys on empty objects produce no match */
  5.2312 +  /* (check necessary because query radius may be infinite) */
  5.2313 +  if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false);
  5.2314 +  /* strategy number 21: overlapping with point */
  5.2315 +  if (strategy == 21) {
  5.2316 +    /* query datum is a point */
  5.2317 +    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  5.2318 +    /* return true if estimated distance (allowed to be smaller than real
  5.2319 +       distance) between index key and point is zero */
  5.2320 +    PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0);
  5.2321 +  }
  5.2322 +  /* strategy number 22: (point) overlapping with box */
  5.2323 +  if (strategy == 22) {
  5.2324 +    /* query datum is a box */
  5.2325 +    pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1);
  5.2326 +    /* determine bounding box of indexed key */
  5.2327 +    pgl_box keybox;
  5.2328 +    pgl_key_to_box(key, &keybox);
  5.2329 +    /* return true if query box overlaps with bounding box of indexed key */
  5.2330 +    PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox));
  5.2331 +  }
  5.2332 +  /* strategy number 23: overlapping with circle */
  5.2333 +  if (strategy == 23) {
  5.2334 +    /* query datum is a circle */
  5.2335 +    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2336 +    /* return true if estimated distance (allowed to be smaller than real
  5.2337 +       distance) between index key and circle center is smaller than radius */
  5.2338 +    PG_RETURN_BOOL(
  5.2339 +      pgl_estimate_key_distance(key, &(query->center)) <= query->radius
  5.2340 +    );
  5.2341 +  }
  5.2342 +  /* strategy number 24: overlapping with cluster */
  5.2343 +  if (strategy == 24) {
  5.2344 +    bool retval;  /* return value */
  5.2345 +    /* query datum is a cluster */
  5.2346 +    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2347 +    /* return true if estimated distance (allowed to be smaller than real
  5.2348 +       distance) between index key and circle center is smaller than radius */
  5.2349 +    retval = (
  5.2350 +      pgl_estimate_key_distance(key, &(query->bounding.center)) <=
  5.2351 +      query->bounding.radius
  5.2352 +    );
  5.2353 +    PG_FREE_IF_COPY(query, 1);  /* free detoasted cluster (if copy) */
  5.2354 +    PG_RETURN_BOOL(retval);
  5.2355 +  }
  5.2356 +  /* throw error for any unknown strategy number */
  5.2357 +  elog(ERROR, "unrecognized strategy number: %d", strategy);
  5.2358 +}
  5.2359 +
  5.2360 +/* GiST "union" support function */
  5.2361 +PG_FUNCTION_INFO_V1(pgl_gist_union);
  5.2362 +Datum pgl_gist_union(PG_FUNCTION_ARGS) {
  5.2363 +  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  5.2364 +  pgl_keyptr out;  /* return value (to be palloc'ed) */
  5.2365 +  int i;
  5.2366 +  /* determine key size */
  5.2367 +  size_t keysize = PGL_KEY_IS_AREAKEY(
  5.2368 +    (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key)
  5.2369 +  ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey);
  5.2370 +  /* begin with first key as result */
  5.2371 +  out = palloc(keysize);
  5.2372 +  memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize);
  5.2373 +  /* unite current result with second, third, etc. key */
  5.2374 +  for (i=1; i<entryvec->n; i++) {
  5.2375 +    pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key));
  5.2376 +  }
  5.2377 +  /* return result */
  5.2378 +  PG_RETURN_POINTER(out);
  5.2379 +}
  5.2380 +
  5.2381 +/* GiST "compress" support function for indicis on points */
  5.2382 +PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint);
  5.2383 +Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) {
  5.2384 +  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  5.2385 +  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  5.2386 +  /* only transform new leaves */
  5.2387 +  if (entry->leafkey) {
  5.2388 +    /* get point to be transformed */
  5.2389 +    pgl_point *point = (pgl_point *)DatumGetPointer(entry->key);
  5.2390 +    /* allocate memory for key */
  5.2391 +    pgl_keyptr key = palloc(sizeof(pgl_pointkey));
  5.2392 +    /* transform point to key */
  5.2393 +    pgl_point_to_key(point, key);
  5.2394 +    /* create new GISTENTRY structure as return value */
  5.2395 +    retval = palloc(sizeof(GISTENTRY));
  5.2396 +    gistentryinit(
  5.2397 +      *retval, PointerGetDatum(key),
  5.2398 +      entry->rel, entry->page, entry->offset, FALSE
  5.2399 +    );
  5.2400 +  } else {
  5.2401 +    /* inner nodes have already been transformed */
  5.2402 +    retval = entry;
  5.2403 +  }
  5.2404 +  /* return pointer to old or new GISTENTRY structure */
  5.2405 +  PG_RETURN_POINTER(retval);
  5.2406 +}
  5.2407 +
  5.2408 +/* GiST "compress" support function for indicis on circles */
  5.2409 +PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle);
  5.2410 +Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) {
  5.2411 +  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  5.2412 +  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  5.2413 +  /* only transform new leaves */
  5.2414 +  if (entry->leafkey) {
  5.2415 +    /* get circle to be transformed */
  5.2416 +    pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key);
  5.2417 +    /* allocate memory for key */
  5.2418 +    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  5.2419 +    /* transform circle to key */
  5.2420 +    pgl_circle_to_key(circle, key);
  5.2421 +    /* create new GISTENTRY structure as return value */
  5.2422 +    retval = palloc(sizeof(GISTENTRY));
  5.2423 +    gistentryinit(
  5.2424 +      *retval, PointerGetDatum(key),
  5.2425 +      entry->rel, entry->page, entry->offset, FALSE
  5.2426 +    );
  5.2427 +  } else {
  5.2428 +    /* inner nodes have already been transformed */
  5.2429 +    retval = entry;
  5.2430 +  }
  5.2431 +  /* return pointer to old or new GISTENTRY structure */
  5.2432 +  PG_RETURN_POINTER(retval);
  5.2433 +}
  5.2434 +
  5.2435 +/* GiST "compress" support function for indices on clusters */
  5.2436 +PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster);
  5.2437 +Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) {
  5.2438 +  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  5.2439 +  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  5.2440 +  /* only transform new leaves */
  5.2441 +  if (entry->leafkey) {
  5.2442 +    /* get cluster to be transformed (detoasting necessary!) */
  5.2443 +    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key);
  5.2444 +    /* allocate memory for key */
  5.2445 +    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  5.2446 +    /* transform cluster to key */
  5.2447 +    pgl_circle_to_key(&(cluster->bounding), key);
  5.2448 +    /* create new GISTENTRY structure as return value */
  5.2449 +    retval = palloc(sizeof(GISTENTRY));
  5.2450 +    gistentryinit(
  5.2451 +      *retval, PointerGetDatum(key),
  5.2452 +      entry->rel, entry->page, entry->offset, FALSE
  5.2453 +    );
  5.2454 +    /* free detoasted datum */
  5.2455 +    if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster);
  5.2456 +  } else {
  5.2457 +    /* inner nodes have already been transformed */
  5.2458 +    retval = entry;
  5.2459 +  }
  5.2460 +  /* return pointer to old or new GISTENTRY structure */
  5.2461 +  PG_RETURN_POINTER(retval);
  5.2462 +}
  5.2463 +
  5.2464 +/* GiST "decompress" support function for indices */
  5.2465 +PG_FUNCTION_INFO_V1(pgl_gist_decompress);
  5.2466 +Datum pgl_gist_decompress(PG_FUNCTION_ARGS) {
  5.2467 +  /* return passed pointer without transformation */
  5.2468 +  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
  5.2469 +}
  5.2470 +
  5.2471 +/* GiST "penalty" support function */
  5.2472 +PG_FUNCTION_INFO_V1(pgl_gist_penalty);
  5.2473 +Datum pgl_gist_penalty(PG_FUNCTION_ARGS) {
  5.2474 +  GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0);
  5.2475 +  GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1);
  5.2476 +  float *penalty = (float *)PG_GETARG_POINTER(2);
  5.2477 +  /* get original key and key to insert */
  5.2478 +  pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key);
  5.2479 +  pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key);
  5.2480 +  /* copy original key */
  5.2481 +  union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key;
  5.2482 +  if (PGL_KEY_IS_AREAKEY(orig)) {
  5.2483 +    memcpy(union_key.areakey, orig, sizeof(union_key.areakey));
  5.2484 +  } else {
  5.2485 +    memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey));
  5.2486 +  }
  5.2487 +  /* calculate union of both keys */
  5.2488 +  pgl_unite_keys((pgl_keyptr)&union_key, new);
  5.2489 +  /* penalty equal to reduction of key length (logarithm of added area) */
  5.2490 +  /* (return value by setting referenced value and returning pointer) */
  5.2491 +  *penalty = (
  5.2492 +    PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key)
  5.2493 +  );
  5.2494 +  PG_RETURN_POINTER(penalty);
  5.2495 +}
  5.2496 +
  5.2497 +/* GiST "picksplit" support function */
  5.2498 +PG_FUNCTION_INFO_V1(pgl_gist_picksplit);
  5.2499 +Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) {
  5.2500 +  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  5.2501 +  GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1);
  5.2502 +  OffsetNumber i;  /* between FirstOffsetNumber and entryvec->n (inclusive) */
  5.2503 +  union {
  5.2504 +    pgl_pointkey pointkey;
  5.2505 +    pgl_areakey areakey;
  5.2506 +  } union_all;  /* union of all keys (to be calculated from scratch)
  5.2507 +                   (later cut in half) */
  5.2508 +  int is_areakey = PGL_KEY_IS_AREAKEY(
  5.2509 +    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key)
  5.2510 +  );
  5.2511 +  int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey);
  5.2512 +  pgl_keyptr unionL = palloc(keysize);  /* union of keys that go left */
  5.2513 +  pgl_keyptr unionR = palloc(keysize);  /* union of keys that go right */
  5.2514 +  pgl_keyptr key;  /* current key to be processed */
  5.2515 +  /* allocate memory for array of left and right keys, set counts to zero */
  5.2516 +  v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  5.2517 +  v->spl_nleft = 0;
  5.2518 +  v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  5.2519 +  v->spl_nright = 0;
  5.2520 +  /* calculate union of all keys from scratch */
  5.2521 +  memcpy(
  5.2522 +    (pgl_keyptr)&union_all,
  5.2523 +    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key),
  5.2524 +    keysize
  5.2525 +  );
  5.2526 +  for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) {
  5.2527 +    pgl_unite_keys(
  5.2528 +      (pgl_keyptr)&union_all,
  5.2529 +      (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)
  5.2530 +    );
  5.2531 +  }
  5.2532 +  /* check if trivial split is necessary due to exhausted key length */
  5.2533 +  /* (Note: keys for empty objects must have node depth set to maximum) */
  5.2534 +  if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == (
  5.2535 +    is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH
  5.2536 +  )) {
  5.2537 +    /* half of all keys go left */
  5.2538 +    for (
  5.2539 +      i=FirstOffsetNumber;
  5.2540 +      i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  5.2541 +      i=OffsetNumberNext(i)
  5.2542 +    ) {
  5.2543 +      /* pointer to current key */
  5.2544 +      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  5.2545 +      /* update unionL */
  5.2546 +      /* check if key is first key that goes left */
  5.2547 +      if (!v->spl_nleft) {
  5.2548 +        /* first key that goes left is just copied to unionL */
  5.2549 +        memcpy(unionL, key, keysize);
  5.2550 +      } else {
  5.2551 +        /* unite current value and next key */
  5.2552 +        pgl_unite_keys(unionL, key);
  5.2553 +      }
  5.2554 +      /* append offset number to list of keys that go left */
  5.2555 +      v->spl_left[v->spl_nleft++] = i;
  5.2556 +    }
  5.2557 +    /* other half goes right */
  5.2558 +    for (
  5.2559 +      i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  5.2560 +      i<entryvec->n;
  5.2561 +      i=OffsetNumberNext(i)
  5.2562 +    ) {
  5.2563 +      /* pointer to current key */
  5.2564 +      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  5.2565 +      /* update unionR */
  5.2566 +      /* check if key is first key that goes right */
  5.2567 +      if (!v->spl_nright) {
  5.2568 +        /* first key that goes right is just copied to unionR */
  5.2569 +        memcpy(unionR, key, keysize);
  5.2570 +      } else {
  5.2571 +        /* unite current value and next key */
  5.2572 +        pgl_unite_keys(unionR, key);
  5.2573 +      }
  5.2574 +      /* append offset number to list of keys that go right */
  5.2575 +      v->spl_right[v->spl_nright++] = i;
  5.2576 +    }
  5.2577 +  }
  5.2578 +  /* otherwise, a non-trivial split is possible */
  5.2579 +  else {
  5.2580 +    /* cut covered area in half */
  5.2581 +    /* (union_all then refers to area of keys that go left) */
  5.2582 +    /* check if union of all keys covers empty and non-empty objects */
  5.2583 +    if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) {
  5.2584 +      /* if yes, split into empty and non-empty objects */
  5.2585 +      pgl_key_set_empty((pgl_keyptr)&union_all);
  5.2586 +    } else {
  5.2587 +      /* otherwise split by next bit */
  5.2588 +      ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++;
  5.2589 +      /* NOTE: type bit conserved */
  5.2590 +    }
  5.2591 +    /* determine for each key if it goes left or right */
  5.2592 +    for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) {
  5.2593 +      /* pointer to current key */
  5.2594 +      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  5.2595 +      /* keys within one half of the area go left */
  5.2596 +      if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) {
  5.2597 +        /* update unionL */
  5.2598 +        /* check if key is first key that goes left */
  5.2599 +        if (!v->spl_nleft) {
  5.2600 +          /* first key that goes left is just copied to unionL */
  5.2601 +          memcpy(unionL, key, keysize);
  5.2602 +        } else {
  5.2603 +          /* unite current value of unionL and processed key */
  5.2604 +          pgl_unite_keys(unionL, key);
  5.2605 +        }
  5.2606 +        /* append offset number to list of keys that go left */
  5.2607 +        v->spl_left[v->spl_nleft++] = i;
  5.2608 +      }
  5.2609 +      /* the other keys go right */
  5.2610 +      else {
  5.2611 +        /* update unionR */
  5.2612 +        /* check if key is first key that goes right */
  5.2613 +        if (!v->spl_nright) {
  5.2614 +          /* first key that goes right is just copied to unionR */
  5.2615 +          memcpy(unionR, key, keysize);
  5.2616 +        } else {
  5.2617 +          /* unite current value of unionR and processed key */
  5.2618 +          pgl_unite_keys(unionR, key);
  5.2619 +        }
  5.2620 +        /* append offset number to list of keys that go right */
  5.2621 +        v->spl_right[v->spl_nright++] = i;
  5.2622 +      }
  5.2623 +    }
  5.2624 +  }
  5.2625 +  /* store unions in return value */
  5.2626 +  v->spl_ldatum = PointerGetDatum(unionL);
  5.2627 +  v->spl_rdatum = PointerGetDatum(unionR);
  5.2628 +  /* return all results */
  5.2629 +  PG_RETURN_POINTER(v);
  5.2630 +}
  5.2631 +
  5.2632 +/* GiST "same"/"equal" support function */
  5.2633 +PG_FUNCTION_INFO_V1(pgl_gist_same);
  5.2634 +Datum pgl_gist_same(PG_FUNCTION_ARGS) {
  5.2635 +  pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0);
  5.2636 +  pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1);
  5.2637 +  bool *boolptr = (bool *)PG_GETARG_POINTER(2);
  5.2638 +  /* two keys are equal if they are binary equal */
  5.2639 +  /* (return result by setting referenced boolean and returning pointer) */
  5.2640 +  *boolptr = !memcmp(
  5.2641 +    key1,
  5.2642 +    key2,
  5.2643 +    PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey)
  5.2644 +  );
  5.2645 +  PG_RETURN_POINTER(boolptr);
  5.2646 +}
  5.2647 +
  5.2648 +/* GiST "distance" support function */
  5.2649 +PG_FUNCTION_INFO_V1(pgl_gist_distance);
  5.2650 +Datum pgl_gist_distance(PG_FUNCTION_ARGS) {
  5.2651 +  GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
  5.2652 +  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  5.2653 +  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  5.2654 +  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  5.2655 +  double distance;  /* return value */
  5.2656 +  /* demand recheck because distance is just an estimation */
  5.2657 +  /* (real distance may be bigger) */
  5.2658 +  *recheck = true;
  5.2659 +  /* strategy number 31: distance to point */
  5.2660 +  if (strategy == 31) {
  5.2661 +    /* query datum is a point */
  5.2662 +    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  5.2663 +    /* use pgl_estimate_pointkey_distance() function to compute result */
  5.2664 +    distance = pgl_estimate_key_distance(key, query);
  5.2665 +    /* avoid infinity (reserved!) */
  5.2666 +    if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  5.2667 +    /* return result */
  5.2668 +    PG_RETURN_FLOAT8(distance);
  5.2669 +  }
  5.2670 +  /* strategy number 33: distance to circle */
  5.2671 +  if (strategy == 33) {
  5.2672 +    /* query datum is a circle */
  5.2673 +    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  5.2674 +    /* estimate distance to circle center and substract circle radius */
  5.2675 +    distance = (
  5.2676 +      pgl_estimate_key_distance(key, &(query->center)) - query->radius
  5.2677 +    );
  5.2678 +    /* convert non-positive values to zero and avoid infinity (reserved!) */
  5.2679 +    if (distance <= 0) distance = 0;
  5.2680 +    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  5.2681 +    /* return result */
  5.2682 +    PG_RETURN_FLOAT8(distance);
  5.2683 +  }
  5.2684 +  /* strategy number 34: distance to cluster */
  5.2685 +  if (strategy == 34) {
  5.2686 +    /* query datum is a cluster */
  5.2687 +    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  5.2688 +    /* estimate distance to bounding center and substract bounding radius */
  5.2689 +    distance = (
  5.2690 +      pgl_estimate_key_distance(key, &(query->bounding.center)) -
  5.2691 +      query->bounding.radius
  5.2692 +    );
  5.2693 +    /* convert non-positive values to zero and avoid infinity (reserved!) */
  5.2694 +    if (distance <= 0) distance = 0;
  5.2695 +    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  5.2696 +    /* free detoasted cluster (if copy) */
  5.2697 +    PG_FREE_IF_COPY(query, 1);
  5.2698 +    /* return result */
  5.2699 +    PG_RETURN_FLOAT8(distance);
  5.2700 +  }
  5.2701 +  /* throw error for any unknown strategy number */
  5.2702 +  elog(ERROR, "unrecognized strategy number: %d", strategy);
  5.2703 +}
  5.2704 +
     6.1 --- a/latlon.control	Mon Aug 22 22:10:44 2016 +0200
     6.2 +++ b/latlon.control	Fri Sep 02 14:04:04 2016 +0200
     6.3 @@ -1,3 +1,3 @@
     6.4  comment = 'geospatial support'
     6.5 -default_version = '0.2'
     6.6 +default_version = '0.3'
     6.7  relocatable = true

Impressum / About Us