pgLatLon

changeset 69:882b77aee653

Prepared file names for version 0.15
author jbe
date Wed Feb 12 11:08:37 2020 +0100 (2020-02-12)
parents 9683f651cbd9
children d06b066fb8ad
files GNUmakefile latlon--0.12--0.13.sql latlon--0.13--0.14.sql latlon--0.14--0.15.sql latlon--0.14.sql latlon--0.15.sql latlon-v0009.c latlon-v0010.c latlon.control
line diff
     1.1 --- a/GNUmakefile	Tue Feb 11 02:35:16 2020 +0100
     1.2 +++ b/GNUmakefile	Wed Feb 12 11:08:37 2020 +0100
     1.3 @@ -1,6 +1,6 @@
     1.4  EXTENSION = latlon
     1.5 -DATA = latlon--0.12--0.13.sql latlon--0.13--0.14.sql latlon--0.14.sql
     1.6 -MODULES = latlon-v0009
     1.7 +DATA = latlon--0.14--0.15.sql latlon--0.15.sql
     1.8 +MODULES = latlon-v0010
     1.9  
    1.10  PG_CONFIG = pg_config
    1.11  PGXS := $(shell $(PG_CONFIG) --pgxs)
     2.1 --- a/latlon--0.12--0.13.sql	Tue Feb 11 02:35:16 2020 +0100
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,2 +0,0 @@
     2.4 --- Nothing to do here, as version 0.13 only contained compatibility changes to
     2.5 --- allow compilation with recent PostgreSQL versions.
     3.1 --- a/latlon--0.13--0.14.sql	Tue Feb 11 02:35:16 2020 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,67 +0,0 @@
     3.4 -DROP OPERATOR CLASS epoint_ops USING gist;
     3.5 -DROP OPERATOR CLASS ecluster_ops USING gist;
     3.6 -
     3.7 -DROP OPERATOR <@ (epoint, ecluster);
     3.8 -
     3.9 -CREATE OPERATOR <@ (
    3.10 -  leftarg = epoint,
    3.11 -  rightarg = ecluster,
    3.12 -  procedure = epoint_ecluster_overlap_proc,
    3.13 -  commutator = @>,
    3.14 -  restrict = areasel,
    3.15 -  join = areajoinsel
    3.16 -);
    3.17 -
    3.18 -CREATE OPERATOR CLASS epoint_ops
    3.19 -  DEFAULT FOR TYPE epoint USING gist AS
    3.20 -  OPERATOR  11 = ,
    3.21 -  OPERATOR  22 &&  (epoint, ebox),
    3.22 -  OPERATOR 222 <@  (epoint, ebox),
    3.23 -  OPERATOR  23 &&  (epoint, ecircle),
    3.24 -  OPERATOR  24 &&  (epoint, ecluster),
    3.25 -  OPERATOR 124 &&+ (epoint, ecluster),
    3.26 -  OPERATOR 224 <@  (epoint, ecluster),
    3.27 -  OPERATOR  31 <-> (epoint, epoint) FOR ORDER BY float_ops,
    3.28 -  OPERATOR  32 <-> (epoint, ebox) FOR ORDER BY float_ops,
    3.29 -  OPERATOR  33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
    3.30 -  OPERATOR  34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
    3.31 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
    3.32 -  FUNCTION 2 pgl_gist_union(internal, internal),
    3.33 -  FUNCTION 3 pgl_gist_compress_epoint(internal),
    3.34 -  FUNCTION 4 pgl_gist_decompress(internal),
    3.35 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
    3.36 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
    3.37 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
    3.38 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
    3.39 -  STORAGE ekey_point;
    3.40 -
    3.41 -CREATE OPERATOR CLASS ecluster_ops
    3.42 -  DEFAULT FOR TYPE ecluster USING gist AS
    3.43 -  OPERATOR  21 &&  (ecluster, epoint),
    3.44 -  OPERATOR 121 &&+ (ecluster, epoint),
    3.45 -  OPERATOR 221 @>  (ecluster, epoint),
    3.46 -  OPERATOR  22 &&  (ecluster, ebox),
    3.47 -  OPERATOR 122 &&+ (ecluster, ebox),
    3.48 -  OPERATOR 222 @>  (ecluster, ebox),
    3.49 -  OPERATOR 322 <@  (ecluster, ebox),
    3.50 -  OPERATOR  23 &&  (ecluster, ecircle),
    3.51 -  OPERATOR 123 &&+ (ecluster, ecircle),
    3.52 -  OPERATOR  24 &&  (ecluster, ecluster),
    3.53 -  OPERATOR 124 &&+ (ecluster, ecluster),
    3.54 -  OPERATOR 224 @>  (ecluster, ecluster),
    3.55 -  OPERATOR 324 <@  (ecluster, ecluster),
    3.56 -  OPERATOR  31 <-> (ecluster, epoint) FOR ORDER BY float_ops,
    3.57 -  OPERATOR  32 <-> (ecluster, ebox) FOR ORDER BY float_ops,
    3.58 -  OPERATOR  33 <-> (ecluster, ecircle) FOR ORDER BY float_ops,
    3.59 -  OPERATOR  34 <-> (ecluster, ecluster) FOR ORDER BY float_ops,
    3.60 -  OPERATOR 131 <=> (ecluster, epoint_with_sample_count) FOR ORDER BY float_ops,
    3.61 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
    3.62 -  FUNCTION 2 pgl_gist_union(internal, internal),
    3.63 -  FUNCTION 3 pgl_gist_compress_ecluster(internal),
    3.64 -  FUNCTION 4 pgl_gist_decompress(internal),
    3.65 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
    3.66 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
    3.67 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
    3.68 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
    3.69 -  STORAGE ekey_area;
    3.70 -
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/latlon--0.14--0.15.sql	Wed Feb 12 11:08:37 2020 +0100
     4.3 @@ -0,0 +1,512 @@
     4.4 +
     4.5 +------------------------------------------------------------
     4.6 +-- dummy input/output functions for dummy index key types --
     4.7 +------------------------------------------------------------
     4.8 +
     4.9 +CREATE OR REPLACE FUNCTION ekey_point_in_dummy(cstring)
    4.10 +  RETURNS ekey_point
    4.11 +  LANGUAGE C IMMUTABLE STRICT
    4.12 +  AS '$libdir/latlon-v0010', 'pgl_notimpl';
    4.13 +
    4.14 +CREATE OR REPLACE FUNCTION ekey_point_out_dummy(ekey_point)
    4.15 +  RETURNS cstring
    4.16 +  LANGUAGE C IMMUTABLE STRICT
    4.17 +  AS '$libdir/latlon-v0010', 'pgl_notimpl';
    4.18 +
    4.19 +CREATE OR REPLACE FUNCTION ekey_area_in_dummy(cstring)
    4.20 +  RETURNS ekey_area
    4.21 +  LANGUAGE C IMMUTABLE STRICT
    4.22 +  AS '$libdir/latlon-v0010', 'pgl_notimpl';
    4.23 +
    4.24 +CREATE OR REPLACE FUNCTION ekey_area_out_dummy(ekey_area)
    4.25 +  RETURNS cstring
    4.26 +  LANGUAGE C IMMUTABLE STRICT
    4.27 +  AS '$libdir/latlon-v0010', 'pgl_notimpl';
    4.28 +
    4.29 +
    4.30 +--------------------------
    4.31 +-- text input functions --
    4.32 +--------------------------
    4.33 +
    4.34 +CREATE OR REPLACE FUNCTION epoint_in(cstring)
    4.35 +  RETURNS epoint
    4.36 +  LANGUAGE C IMMUTABLE STRICT
    4.37 +  AS '$libdir/latlon-v0010', 'pgl_epoint_in';
    4.38 +
    4.39 +CREATE OR REPLACE FUNCTION epoint_with_sample_count_in(cstring)
    4.40 +  RETURNS epoint_with_sample_count
    4.41 +  LANGUAGE C IMMUTABLE STRICT
    4.42 +  AS '$libdir/latlon-v0010', 'pgl_epoint_with_sample_count_in';
    4.43 +
    4.44 +CREATE OR REPLACE FUNCTION ebox_in(cstring)
    4.45 +  RETURNS ebox
    4.46 +  LANGUAGE C IMMUTABLE STRICT
    4.47 +  AS '$libdir/latlon-v0010', 'pgl_ebox_in';
    4.48 +
    4.49 +CREATE OR REPLACE FUNCTION ecircle_in(cstring)
    4.50 +  RETURNS ecircle
    4.51 +  LANGUAGE C IMMUTABLE STRICT
    4.52 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_in';
    4.53 +
    4.54 +CREATE OR REPLACE FUNCTION ecluster_in(cstring)
    4.55 +  RETURNS ecluster
    4.56 +  LANGUAGE C IMMUTABLE STRICT
    4.57 +  AS '$libdir/latlon-v0010', 'pgl_ecluster_in';
    4.58 +
    4.59 +
    4.60 +---------------------------
    4.61 +-- text output functions --
    4.62 +---------------------------
    4.63 +
    4.64 +CREATE OR REPLACE FUNCTION epoint_out(epoint)
    4.65 +  RETURNS cstring
    4.66 +  LANGUAGE C IMMUTABLE STRICT
    4.67 +  AS '$libdir/latlon-v0010', 'pgl_epoint_out';
    4.68 +
    4.69 +CREATE OR REPLACE FUNCTION epoint_with_sample_count_out(epoint_with_sample_count)
    4.70 +  RETURNS cstring
    4.71 +  LANGUAGE C IMMUTABLE STRICT
    4.72 +  AS '$libdir/latlon-v0010', 'pgl_epoint_with_sample_count_out';
    4.73 +
    4.74 +CREATE OR REPLACE FUNCTION ebox_out(ebox)
    4.75 +  RETURNS cstring
    4.76 +  LANGUAGE C IMMUTABLE STRICT
    4.77 +  AS '$libdir/latlon-v0010', 'pgl_ebox_out';
    4.78 +
    4.79 +CREATE OR REPLACE FUNCTION ecircle_out(ecircle)
    4.80 +  RETURNS cstring
    4.81 +  LANGUAGE C IMMUTABLE STRICT
    4.82 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_out';
    4.83 +
    4.84 +CREATE OR REPLACE FUNCTION ecluster_out(ecluster)
    4.85 +  RETURNS cstring
    4.86 +  LANGUAGE C IMMUTABLE STRICT
    4.87 +  AS '$libdir/latlon-v0010', 'pgl_ecluster_out';
    4.88 +
    4.89 +
    4.90 +--------------------------
    4.91 +-- binary I/O functions --
    4.92 +--------------------------
    4.93 +
    4.94 +CREATE OR REPLACE FUNCTION epoint_recv(internal)
    4.95 +  RETURNS epoint
    4.96 +  LANGUAGE C IMMUTABLE STRICT
    4.97 +  AS '$libdir/latlon-v0010', 'pgl_epoint_recv';
    4.98 +
    4.99 +CREATE OR REPLACE FUNCTION ebox_recv(internal)
   4.100 +  RETURNS ebox
   4.101 +  LANGUAGE C IMMUTABLE STRICT
   4.102 +  AS '$libdir/latlon-v0010', 'pgl_ebox_recv';
   4.103 +
   4.104 +CREATE OR REPLACE FUNCTION ecircle_recv(internal)
   4.105 +  RETURNS ecircle
   4.106 +  LANGUAGE C IMMUTABLE STRICT
   4.107 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_recv';
   4.108 +
   4.109 +CREATE OR REPLACE FUNCTION epoint_send(epoint)
   4.110 +  RETURNS bytea
   4.111 +  LANGUAGE C IMMUTABLE STRICT
   4.112 +  AS '$libdir/latlon-v0010', 'pgl_epoint_send';
   4.113 +
   4.114 +CREATE OR REPLACE FUNCTION ebox_send(ebox)
   4.115 +  RETURNS bytea
   4.116 +  LANGUAGE C IMMUTABLE STRICT
   4.117 +  AS '$libdir/latlon-v0010', 'pgl_ebox_send';
   4.118 +
   4.119 +CREATE OR REPLACE FUNCTION ecircle_send(ecircle)
   4.120 +  RETURNS bytea
   4.121 +  LANGUAGE C IMMUTABLE STRICT
   4.122 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_send';
   4.123 +
   4.124 +
   4.125 +--------------------
   4.126 +-- B-tree support --
   4.127 +--------------------
   4.128 +
   4.129 +-- begin of B-tree support for epoint
   4.130 +
   4.131 +CREATE OR REPLACE FUNCTION epoint_btree_lt(epoint, epoint)
   4.132 +  RETURNS boolean
   4.133 +  LANGUAGE C IMMUTABLE STRICT
   4.134 +  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_lt';
   4.135 +
   4.136 +CREATE OR REPLACE FUNCTION epoint_btree_le(epoint, epoint)
   4.137 +  RETURNS boolean
   4.138 +  LANGUAGE C IMMUTABLE STRICT
   4.139 +  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_le';
   4.140 +
   4.141 +CREATE OR REPLACE FUNCTION epoint_btree_eq(epoint, epoint)
   4.142 +  RETURNS boolean
   4.143 +  LANGUAGE C IMMUTABLE STRICT
   4.144 +  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_eq';
   4.145 +
   4.146 +CREATE OR REPLACE FUNCTION epoint_btree_ne(epoint, epoint)
   4.147 +  RETURNS boolean
   4.148 +  LANGUAGE C IMMUTABLE STRICT
   4.149 +  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_ne';
   4.150 +
   4.151 +CREATE OR REPLACE FUNCTION epoint_btree_ge(epoint, epoint)
   4.152 +  RETURNS boolean
   4.153 +  LANGUAGE C IMMUTABLE STRICT
   4.154 +  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_ge';
   4.155 +
   4.156 +CREATE OR REPLACE FUNCTION epoint_btree_gt(epoint, epoint)
   4.157 +  RETURNS boolean
   4.158 +  LANGUAGE C IMMUTABLE STRICT
   4.159 +  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_gt';
   4.160 +
   4.161 +CREATE OR REPLACE FUNCTION epoint_btree_cmp(epoint, epoint)
   4.162 +  RETURNS int4
   4.163 +  LANGUAGE C IMMUTABLE STRICT
   4.164 +  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_cmp';
   4.165 +
   4.166 +-- end of B-tree support for epoint
   4.167 +
   4.168 +-- begin of B-tree support for ebox
   4.169 +
   4.170 +CREATE OR REPLACE FUNCTION ebox_btree_lt(ebox, ebox)
   4.171 +  RETURNS boolean
   4.172 +  LANGUAGE C IMMUTABLE STRICT
   4.173 +  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_lt';
   4.174 +
   4.175 +CREATE OR REPLACE FUNCTION ebox_btree_le(ebox, ebox)
   4.176 +  RETURNS boolean
   4.177 +  LANGUAGE C IMMUTABLE STRICT
   4.178 +  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_le';
   4.179 +
   4.180 +CREATE OR REPLACE FUNCTION ebox_btree_eq(ebox, ebox)
   4.181 +  RETURNS boolean
   4.182 +  LANGUAGE C IMMUTABLE STRICT
   4.183 +  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_eq';
   4.184 +
   4.185 +CREATE OR REPLACE FUNCTION ebox_btree_ne(ebox, ebox)
   4.186 +  RETURNS boolean
   4.187 +  LANGUAGE C IMMUTABLE STRICT
   4.188 +  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_ne';
   4.189 +
   4.190 +CREATE OR REPLACE FUNCTION ebox_btree_ge(ebox, ebox)
   4.191 +  RETURNS boolean
   4.192 +  LANGUAGE C IMMUTABLE STRICT
   4.193 +  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_ge';
   4.194 +
   4.195 +CREATE OR REPLACE FUNCTION ebox_btree_gt(ebox, ebox)
   4.196 +  RETURNS boolean
   4.197 +  LANGUAGE C IMMUTABLE STRICT
   4.198 +  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_gt';
   4.199 +
   4.200 +CREATE OR REPLACE FUNCTION ebox_btree_cmp(ebox, ebox)
   4.201 +  RETURNS int4
   4.202 +  LANGUAGE C IMMUTABLE STRICT
   4.203 +  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_cmp';
   4.204 +
   4.205 +-- end of B-tree support for ebox
   4.206 +
   4.207 +-- begin of B-tree support for ecircle
   4.208 +
   4.209 +CREATE OR REPLACE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   4.210 +  RETURNS boolean
   4.211 +  LANGUAGE C IMMUTABLE STRICT
   4.212 +  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_lt';
   4.213 +
   4.214 +CREATE OR REPLACE FUNCTION ecircle_btree_le(ecircle, ecircle)
   4.215 +  RETURNS boolean
   4.216 +  LANGUAGE C IMMUTABLE STRICT
   4.217 +  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_le';
   4.218 +
   4.219 +CREATE OR REPLACE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   4.220 +  RETURNS boolean
   4.221 +  LANGUAGE C IMMUTABLE STRICT
   4.222 +  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_eq';
   4.223 +
   4.224 +CREATE OR REPLACE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   4.225 +  RETURNS boolean
   4.226 +  LANGUAGE C IMMUTABLE STRICT
   4.227 +  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_ne';
   4.228 +
   4.229 +CREATE OR REPLACE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   4.230 +  RETURNS boolean
   4.231 +  LANGUAGE C IMMUTABLE STRICT
   4.232 +  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_ge';
   4.233 +
   4.234 +CREATE OR REPLACE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   4.235 +  RETURNS boolean
   4.236 +  LANGUAGE C IMMUTABLE STRICT
   4.237 +  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_gt';
   4.238 +
   4.239 +CREATE OR REPLACE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   4.240 +  RETURNS int4
   4.241 +  LANGUAGE C IMMUTABLE STRICT
   4.242 +  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_cmp';
   4.243 +
   4.244 +-- end of B-tree support for ecircle
   4.245 +
   4.246 +
   4.247 +----------------
   4.248 +-- type casts --
   4.249 +----------------
   4.250 +
   4.251 +CREATE OR REPLACE FUNCTION cast_epoint_to_ebox(epoint)
   4.252 +  RETURNS ebox
   4.253 +  LANGUAGE C IMMUTABLE STRICT
   4.254 +  AS '$libdir/latlon-v0010', 'pgl_epoint_to_ebox';
   4.255 +
   4.256 +CREATE OR REPLACE FUNCTION cast_epoint_to_ecircle(epoint)
   4.257 +  RETURNS ecircle
   4.258 +  LANGUAGE C IMMUTABLE STRICT
   4.259 +  AS '$libdir/latlon-v0010', 'pgl_epoint_to_ecircle';
   4.260 +
   4.261 +CREATE OR REPLACE FUNCTION cast_epoint_to_ecluster(epoint)
   4.262 +  RETURNS ecluster
   4.263 +  LANGUAGE C IMMUTABLE STRICT
   4.264 +  AS '$libdir/latlon-v0010', 'pgl_epoint_to_ecluster';
   4.265 +
   4.266 +CREATE OR REPLACE FUNCTION cast_ebox_to_ecluster(ebox)
   4.267 +  RETURNS ecluster
   4.268 +  LANGUAGE C IMMUTABLE STRICT
   4.269 +  AS '$libdir/latlon-v0010', 'pgl_ebox_to_ecluster';
   4.270 +
   4.271 +
   4.272 +---------------------------
   4.273 +-- constructor functions --
   4.274 +---------------------------
   4.275 +
   4.276 +CREATE OR REPLACE FUNCTION epoint(float8, float8)
   4.277 +  RETURNS epoint
   4.278 +  LANGUAGE C IMMUTABLE STRICT
   4.279 +  AS '$libdir/latlon-v0010', 'pgl_create_epoint';
   4.280 +
   4.281 +CREATE OR REPLACE FUNCTION epoint_with_sample_count(epoint, int4)
   4.282 +  RETURNS epoint_with_sample_count
   4.283 +  LANGUAGE C IMMUTABLE STRICT
   4.284 +  AS '$libdir/latlon-v0010', 'pgl_create_epoint_with_sample_count';
   4.285 +
   4.286 +CREATE OR REPLACE FUNCTION empty_ebox()
   4.287 +  RETURNS ebox
   4.288 +  LANGUAGE C IMMUTABLE STRICT
   4.289 +  AS '$libdir/latlon-v0010', 'pgl_create_empty_ebox';
   4.290 +
   4.291 +CREATE OR REPLACE FUNCTION ebox(float8, float8, float8, float8)
   4.292 +  RETURNS ebox
   4.293 +  LANGUAGE C IMMUTABLE STRICT
   4.294 +  AS '$libdir/latlon-v0010', 'pgl_create_ebox';
   4.295 +
   4.296 +CREATE OR REPLACE FUNCTION ebox(epoint, epoint)
   4.297 +  RETURNS ebox
   4.298 +  LANGUAGE C IMMUTABLE STRICT
   4.299 +  AS '$libdir/latlon-v0010', 'pgl_create_ebox_from_epoints';
   4.300 +
   4.301 +CREATE OR REPLACE FUNCTION ecircle(float8, float8, float8)
   4.302 +  RETURNS ecircle
   4.303 +  LANGUAGE C IMMUTABLE STRICT
   4.304 +  AS '$libdir/latlon-v0010', 'pgl_create_ecircle';
   4.305 +
   4.306 +CREATE OR REPLACE FUNCTION ecircle(epoint, float8)
   4.307 +  RETURNS ecircle
   4.308 +  LANGUAGE C IMMUTABLE STRICT
   4.309 +  AS '$libdir/latlon-v0010', 'pgl_create_ecircle_from_epoint';
   4.310 +
   4.311 +
   4.312 +----------------------
   4.313 +-- getter functions --
   4.314 +----------------------
   4.315 +
   4.316 +CREATE OR REPLACE FUNCTION latitude(epoint)
   4.317 +  RETURNS float8
   4.318 +  LANGUAGE C IMMUTABLE STRICT
   4.319 +  AS '$libdir/latlon-v0010', 'pgl_epoint_lat';
   4.320 +
   4.321 +CREATE OR REPLACE FUNCTION longitude(epoint)
   4.322 +  RETURNS float8
   4.323 +  LANGUAGE C IMMUTABLE STRICT
   4.324 +  AS '$libdir/latlon-v0010', 'pgl_epoint_lon';
   4.325 +
   4.326 +CREATE OR REPLACE FUNCTION min_latitude(ebox)
   4.327 +  RETURNS float8
   4.328 +  LANGUAGE C IMMUTABLE STRICT
   4.329 +  AS '$libdir/latlon-v0010', 'pgl_ebox_lat_min';
   4.330 +
   4.331 +CREATE OR REPLACE FUNCTION max_latitude(ebox)
   4.332 +  RETURNS float8
   4.333 +  LANGUAGE C IMMUTABLE STRICT
   4.334 +  AS '$libdir/latlon-v0010', 'pgl_ebox_lat_max';
   4.335 +
   4.336 +CREATE OR REPLACE FUNCTION min_longitude(ebox)
   4.337 +  RETURNS float8
   4.338 +  LANGUAGE C IMMUTABLE STRICT
   4.339 +  AS '$libdir/latlon-v0010', 'pgl_ebox_lon_min';
   4.340 +
   4.341 +CREATE OR REPLACE FUNCTION max_longitude(ebox)
   4.342 +  RETURNS float8
   4.343 +  LANGUAGE C IMMUTABLE STRICT
   4.344 +  AS '$libdir/latlon-v0010', 'pgl_ebox_lon_max';
   4.345 +
   4.346 +CREATE OR REPLACE FUNCTION center(ecircle)
   4.347 +  RETURNS epoint
   4.348 +  LANGUAGE C IMMUTABLE STRICT
   4.349 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_center';
   4.350 +
   4.351 +CREATE OR REPLACE FUNCTION radius(ecircle)
   4.352 +  RETURNS float8
   4.353 +  LANGUAGE C IMMUTABLE STRICT
   4.354 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_radius';
   4.355 +
   4.356 +
   4.357 +---------------
   4.358 +-- operators --
   4.359 +---------------
   4.360 +
   4.361 +CREATE OR REPLACE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   4.362 +  RETURNS boolean
   4.363 +  LANGUAGE C IMMUTABLE STRICT
   4.364 +  AS '$libdir/latlon-v0010', 'pgl_epoint_ebox_overlap';
   4.365 +
   4.366 +CREATE OR REPLACE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   4.367 +  RETURNS boolean
   4.368 +  LANGUAGE C IMMUTABLE STRICT
   4.369 +  AS '$libdir/latlon-v0010', 'pgl_epoint_ecircle_overlap';
   4.370 +
   4.371 +CREATE OR REPLACE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   4.372 +  RETURNS boolean
   4.373 +  LANGUAGE C IMMUTABLE STRICT
   4.374 +  AS '$libdir/latlon-v0010', 'pgl_epoint_ecluster_overlap';
   4.375 +
   4.376 +CREATE OR REPLACE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
   4.377 +  RETURNS boolean
   4.378 +  LANGUAGE C IMMUTABLE STRICT
   4.379 +  AS '$libdir/latlon-v0010', 'pgl_epoint_ecluster_may_overlap';
   4.380 +
   4.381 +CREATE OR REPLACE FUNCTION ebox_overlap_proc(ebox, ebox)
   4.382 +  RETURNS boolean
   4.383 +  LANGUAGE C IMMUTABLE STRICT
   4.384 +  AS '$libdir/latlon-v0010', 'pgl_ebox_overlap';
   4.385 +
   4.386 +CREATE OR REPLACE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
   4.387 +  RETURNS boolean
   4.388 +  LANGUAGE C IMMUTABLE STRICT
   4.389 +  AS '$libdir/latlon-v0010', 'pgl_ebox_ecircle_may_overlap';
   4.390 +
   4.391 +CREATE OR REPLACE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
   4.392 +  RETURNS boolean
   4.393 +  LANGUAGE C IMMUTABLE STRICT
   4.394 +  AS '$libdir/latlon-v0010', 'pgl_ebox_ecluster_may_overlap';
   4.395 +
   4.396 +CREATE OR REPLACE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   4.397 +  RETURNS boolean
   4.398 +  LANGUAGE C IMMUTABLE STRICT
   4.399 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_overlap';
   4.400 +
   4.401 +CREATE OR REPLACE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   4.402 +  RETURNS boolean
   4.403 +  LANGUAGE C IMMUTABLE STRICT
   4.404 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_ecluster_overlap';
   4.405 +
   4.406 +CREATE OR REPLACE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
   4.407 +  RETURNS boolean
   4.408 +  LANGUAGE C IMMUTABLE STRICT
   4.409 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_ecluster_may_overlap';
   4.410 +
   4.411 +CREATE OR REPLACE FUNCTION ecluster_overlap_proc(ecluster, ecluster)
   4.412 +  RETURNS boolean
   4.413 +  LANGUAGE C IMMUTABLE STRICT
   4.414 +  AS '$libdir/latlon-v0010', 'pgl_ecluster_overlap';
   4.415 +
   4.416 +CREATE OR REPLACE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
   4.417 +  RETURNS boolean
   4.418 +  LANGUAGE C IMMUTABLE STRICT
   4.419 +  AS '$libdir/latlon-v0010', 'pgl_ecluster_may_overlap';
   4.420 +
   4.421 +CREATE OR REPLACE FUNCTION ecluster_contains_proc(ecluster, ecluster)
   4.422 +  RETURNS boolean
   4.423 +  LANGUAGE C IMMUTABLE STRICT
   4.424 +  AS '$libdir/latlon-v0010', 'pgl_ecluster_contains';
   4.425 +
   4.426 +CREATE OR REPLACE FUNCTION epoint_distance_proc(epoint, epoint)
   4.427 +  RETURNS float8
   4.428 +  LANGUAGE C IMMUTABLE STRICT
   4.429 +  AS '$libdir/latlon-v0010', 'pgl_epoint_distance';
   4.430 +
   4.431 +CREATE OR REPLACE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   4.432 +  RETURNS float8
   4.433 +  LANGUAGE C IMMUTABLE STRICT
   4.434 +  AS '$libdir/latlon-v0010', 'pgl_epoint_ecircle_distance';
   4.435 +
   4.436 +CREATE OR REPLACE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   4.437 +  RETURNS float8
   4.438 +  LANGUAGE C IMMUTABLE STRICT
   4.439 +  AS '$libdir/latlon-v0010', 'pgl_epoint_ecluster_distance';
   4.440 +
   4.441 +CREATE OR REPLACE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   4.442 +  RETURNS float8
   4.443 +  LANGUAGE C IMMUTABLE STRICT
   4.444 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_distance';
   4.445 +
   4.446 +CREATE OR REPLACE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   4.447 +  RETURNS float8
   4.448 +  LANGUAGE C IMMUTABLE STRICT
   4.449 +  AS '$libdir/latlon-v0010', 'pgl_ecircle_ecluster_distance';
   4.450 +
   4.451 +CREATE OR REPLACE FUNCTION ecluster_distance_proc(ecluster, ecluster)
   4.452 +  RETURNS float8
   4.453 +  LANGUAGE C IMMUTABLE STRICT
   4.454 +  AS '$libdir/latlon-v0010', 'pgl_ecluster_distance';
   4.455 +
   4.456 +CREATE OR REPLACE FUNCTION fair_distance_operator_proc(ecluster, epoint_with_sample_count)
   4.457 +  RETURNS float8
   4.458 +  LANGUAGE C IMMUTABLE STRICT
   4.459 +  AS '$libdir/latlon-v0010', 'pgl_ecluster_epoint_sc_fair_distance';
   4.460 +
   4.461 +
   4.462 +----------------
   4.463 +-- GiST index --
   4.464 +----------------
   4.465 +
   4.466 +CREATE OR REPLACE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
   4.467 +  RETURNS boolean
   4.468 +  LANGUAGE C STRICT
   4.469 +  AS '$libdir/latlon-v0010', 'pgl_gist_consistent';
   4.470 +
   4.471 +CREATE OR REPLACE FUNCTION pgl_gist_union(internal, internal)
   4.472 +  RETURNS internal
   4.473 +  LANGUAGE C STRICT
   4.474 +  AS '$libdir/latlon-v0010', 'pgl_gist_union';
   4.475 +
   4.476 +CREATE OR REPLACE FUNCTION pgl_gist_compress_epoint(internal)
   4.477 +  RETURNS internal
   4.478 +  LANGUAGE C STRICT
   4.479 +  AS '$libdir/latlon-v0010', 'pgl_gist_compress_epoint';
   4.480 +
   4.481 +CREATE OR REPLACE FUNCTION pgl_gist_compress_ecircle(internal)
   4.482 +  RETURNS internal
   4.483 +  LANGUAGE C STRICT
   4.484 +  AS '$libdir/latlon-v0010', 'pgl_gist_compress_ecircle';
   4.485 +
   4.486 +CREATE OR REPLACE FUNCTION pgl_gist_compress_ecluster(internal)
   4.487 +  RETURNS internal
   4.488 +  LANGUAGE C STRICT
   4.489 +  AS '$libdir/latlon-v0010', 'pgl_gist_compress_ecluster';
   4.490 +
   4.491 +CREATE OR REPLACE FUNCTION pgl_gist_decompress(internal)
   4.492 +  RETURNS internal
   4.493 +  LANGUAGE C STRICT
   4.494 +  AS '$libdir/latlon-v0010', 'pgl_gist_decompress';
   4.495 +
   4.496 +CREATE OR REPLACE FUNCTION pgl_gist_penalty(internal, internal, internal)
   4.497 +  RETURNS internal
   4.498 +  LANGUAGE C STRICT
   4.499 +  AS '$libdir/latlon-v0010', 'pgl_gist_penalty';
   4.500 +
   4.501 +CREATE OR REPLACE FUNCTION pgl_gist_picksplit(internal, internal)
   4.502 +  RETURNS internal
   4.503 +  LANGUAGE C STRICT
   4.504 +  AS '$libdir/latlon-v0010', 'pgl_gist_picksplit';
   4.505 +
   4.506 +CREATE OR REPLACE FUNCTION pgl_gist_same(internal, internal, internal)
   4.507 +  RETURNS internal
   4.508 +  LANGUAGE C STRICT
   4.509 +  AS '$libdir/latlon-v0010', 'pgl_gist_same';
   4.510 +
   4.511 +CREATE OR REPLACE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
   4.512 +  RETURNS internal
   4.513 +  LANGUAGE C STRICT
   4.514 +  AS '$libdir/latlon-v0010', 'pgl_gist_distance';
   4.515 +
     5.1 --- a/latlon--0.14.sql	Tue Feb 11 02:35:16 2020 +0100
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,1717 +0,0 @@
     5.4 -
     5.5 -----------------------------------------
     5.6 --- forward declarations (shell types) --
     5.7 -----------------------------------------
     5.8 -
     5.9 -CREATE TYPE ekey_point;
    5.10 -CREATE TYPE ekey_area;
    5.11 -CREATE TYPE epoint;
    5.12 -CREATE TYPE epoint_with_sample_count;
    5.13 -CREATE TYPE ebox;
    5.14 -CREATE TYPE ecircle;
    5.15 -CREATE TYPE ecluster;
    5.16 -
    5.17 -
    5.18 -------------------------------------------------------------
    5.19 --- dummy input/output functions for dummy index key types --
    5.20 -------------------------------------------------------------
    5.21 -
    5.22 -CREATE FUNCTION ekey_point_in_dummy(cstring)
    5.23 -  RETURNS ekey_point
    5.24 -  LANGUAGE C IMMUTABLE STRICT
    5.25 -  AS '$libdir/latlon-v0009', 'pgl_notimpl';
    5.26 -
    5.27 -CREATE FUNCTION ekey_point_out_dummy(ekey_point)
    5.28 -  RETURNS cstring
    5.29 -  LANGUAGE C IMMUTABLE STRICT
    5.30 -  AS '$libdir/latlon-v0009', 'pgl_notimpl';
    5.31 -
    5.32 -CREATE FUNCTION ekey_area_in_dummy(cstring)
    5.33 -  RETURNS ekey_area
    5.34 -  LANGUAGE C IMMUTABLE STRICT
    5.35 -  AS '$libdir/latlon-v0009', 'pgl_notimpl';
    5.36 -
    5.37 -CREATE FUNCTION ekey_area_out_dummy(ekey_area)
    5.38 -  RETURNS cstring
    5.39 -  LANGUAGE C IMMUTABLE STRICT
    5.40 -  AS '$libdir/latlon-v0009', 'pgl_notimpl';
    5.41 -
    5.42 -
    5.43 ---------------------------
    5.44 --- text input functions --
    5.45 ---------------------------
    5.46 -
    5.47 -CREATE FUNCTION epoint_in(cstring)
    5.48 -  RETURNS epoint
    5.49 -  LANGUAGE C IMMUTABLE STRICT
    5.50 -  AS '$libdir/latlon-v0009', 'pgl_epoint_in';
    5.51 -
    5.52 -CREATE FUNCTION epoint_with_sample_count_in(cstring)
    5.53 -  RETURNS epoint_with_sample_count
    5.54 -  LANGUAGE C IMMUTABLE STRICT
    5.55 -  AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_in';
    5.56 -
    5.57 -CREATE FUNCTION ebox_in(cstring)
    5.58 -  RETURNS ebox
    5.59 -  LANGUAGE C IMMUTABLE STRICT
    5.60 -  AS '$libdir/latlon-v0009', 'pgl_ebox_in';
    5.61 -
    5.62 -CREATE FUNCTION ecircle_in(cstring)
    5.63 -  RETURNS ecircle
    5.64 -  LANGUAGE C IMMUTABLE STRICT
    5.65 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_in';
    5.66 -
    5.67 -CREATE FUNCTION ecluster_in(cstring)
    5.68 -  RETURNS ecluster
    5.69 -  LANGUAGE C IMMUTABLE STRICT
    5.70 -  AS '$libdir/latlon-v0009', 'pgl_ecluster_in';
    5.71 -
    5.72 -
    5.73 ----------------------------
    5.74 --- text output functions --
    5.75 ----------------------------
    5.76 -
    5.77 -CREATE FUNCTION epoint_out(epoint)
    5.78 -  RETURNS cstring
    5.79 -  LANGUAGE C IMMUTABLE STRICT
    5.80 -  AS '$libdir/latlon-v0009', 'pgl_epoint_out';
    5.81 -
    5.82 -CREATE FUNCTION epoint_with_sample_count_out(epoint_with_sample_count)
    5.83 -  RETURNS cstring
    5.84 -  LANGUAGE C IMMUTABLE STRICT
    5.85 -  AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_out';
    5.86 -
    5.87 -CREATE FUNCTION ebox_out(ebox)
    5.88 -  RETURNS cstring
    5.89 -  LANGUAGE C IMMUTABLE STRICT
    5.90 -  AS '$libdir/latlon-v0009', 'pgl_ebox_out';
    5.91 -
    5.92 -CREATE FUNCTION ecircle_out(ecircle)
    5.93 -  RETURNS cstring
    5.94 -  LANGUAGE C IMMUTABLE STRICT
    5.95 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_out';
    5.96 -
    5.97 -CREATE FUNCTION ecluster_out(ecluster)
    5.98 -  RETURNS cstring
    5.99 -  LANGUAGE C IMMUTABLE STRICT
   5.100 -  AS '$libdir/latlon-v0009', 'pgl_ecluster_out';
   5.101 -
   5.102 -
   5.103 ---------------------------
   5.104 --- binary I/O functions --
   5.105 ---------------------------
   5.106 -
   5.107 -CREATE FUNCTION epoint_recv(internal)
   5.108 -  RETURNS epoint
   5.109 -  LANGUAGE C IMMUTABLE STRICT
   5.110 -  AS '$libdir/latlon-v0009', 'pgl_epoint_recv';
   5.111 -
   5.112 -CREATE FUNCTION ebox_recv(internal)
   5.113 -  RETURNS ebox
   5.114 -  LANGUAGE C IMMUTABLE STRICT
   5.115 -  AS '$libdir/latlon-v0009', 'pgl_ebox_recv';
   5.116 -
   5.117 -CREATE FUNCTION ecircle_recv(internal)
   5.118 -  RETURNS ecircle
   5.119 -  LANGUAGE C IMMUTABLE STRICT
   5.120 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_recv';
   5.121 -
   5.122 -CREATE FUNCTION epoint_send(epoint)
   5.123 -  RETURNS bytea
   5.124 -  LANGUAGE C IMMUTABLE STRICT
   5.125 -  AS '$libdir/latlon-v0009', 'pgl_epoint_send';
   5.126 -
   5.127 -CREATE FUNCTION ebox_send(ebox)
   5.128 -  RETURNS bytea
   5.129 -  LANGUAGE C IMMUTABLE STRICT
   5.130 -  AS '$libdir/latlon-v0009', 'pgl_ebox_send';
   5.131 -
   5.132 -CREATE FUNCTION ecircle_send(ecircle)
   5.133 -  RETURNS bytea
   5.134 -  LANGUAGE C IMMUTABLE STRICT
   5.135 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_send';
   5.136 -
   5.137 -
   5.138 ------------------------------------------------
   5.139 --- type definitions of dummy index key types --
   5.140 ------------------------------------------------
   5.141 -
   5.142 -CREATE TYPE ekey_point (
   5.143 -  internallength = 8,
   5.144 -  input = ekey_point_in_dummy,
   5.145 -  output = ekey_point_out_dummy,
   5.146 -  alignment = char );
   5.147 -
   5.148 -CREATE TYPE ekey_area (
   5.149 -  internallength = 9,
   5.150 -  input = ekey_area_in_dummy,
   5.151 -  output = ekey_area_out_dummy,
   5.152 -  alignment = char );
   5.153 -
   5.154 -
   5.155 -------------------------------------------
   5.156 --- definitions of geographic data types --
   5.157 -------------------------------------------
   5.158 -
   5.159 -CREATE TYPE epoint (
   5.160 -  internallength = 16,
   5.161 -  input = epoint_in,
   5.162 -  output = epoint_out,
   5.163 -  receive = epoint_recv,
   5.164 -  send = epoint_send,
   5.165 -  alignment = double );
   5.166 -
   5.167 -CREATE TYPE epoint_with_sample_count (
   5.168 -  internallength = 20,
   5.169 -  input = epoint_with_sample_count_in,
   5.170 -  output = epoint_with_sample_count_out,
   5.171 -  alignment = double );
   5.172 -
   5.173 -CREATE TYPE ebox (
   5.174 -  internallength = 32,
   5.175 -  input = ebox_in,
   5.176 -  output = ebox_out,
   5.177 -  receive = ebox_recv,
   5.178 -  send = ebox_send,
   5.179 -  alignment = double );
   5.180 -
   5.181 -CREATE TYPE ecircle (
   5.182 -  internallength = 24,
   5.183 -  input = ecircle_in,
   5.184 -  output = ecircle_out,
   5.185 -  receive = ecircle_recv,
   5.186 -  send = ecircle_send,
   5.187 -  alignment = double );
   5.188 -
   5.189 -CREATE TYPE ecluster (
   5.190 -  internallength = VARIABLE,
   5.191 -  input = ecluster_in,
   5.192 -  output = ecluster_out,
   5.193 -  alignment = double,
   5.194 -  storage = external );
   5.195 -
   5.196 -
   5.197 ---------------------
   5.198 --- B-tree support --
   5.199 ---------------------
   5.200 -
   5.201 --- begin of B-tree support for epoint
   5.202 -
   5.203 -CREATE FUNCTION epoint_btree_lt(epoint, epoint)
   5.204 -  RETURNS boolean
   5.205 -  LANGUAGE C IMMUTABLE STRICT
   5.206 -  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_lt';
   5.207 -
   5.208 -CREATE FUNCTION epoint_btree_le(epoint, epoint)
   5.209 -  RETURNS boolean
   5.210 -  LANGUAGE C IMMUTABLE STRICT
   5.211 -  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_le';
   5.212 -
   5.213 -CREATE FUNCTION epoint_btree_eq(epoint, epoint)
   5.214 -  RETURNS boolean
   5.215 -  LANGUAGE C IMMUTABLE STRICT
   5.216 -  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_eq';
   5.217 -
   5.218 -CREATE FUNCTION epoint_btree_ne(epoint, epoint)
   5.219 -  RETURNS boolean
   5.220 -  LANGUAGE C IMMUTABLE STRICT
   5.221 -  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ne';
   5.222 -
   5.223 -CREATE FUNCTION epoint_btree_ge(epoint, epoint)
   5.224 -  RETURNS boolean
   5.225 -  LANGUAGE C IMMUTABLE STRICT
   5.226 -  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ge';
   5.227 -
   5.228 -CREATE FUNCTION epoint_btree_gt(epoint, epoint)
   5.229 -  RETURNS boolean
   5.230 -  LANGUAGE C IMMUTABLE STRICT
   5.231 -  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_gt';
   5.232 -
   5.233 -CREATE OPERATOR <<< (
   5.234 -  leftarg = epoint,
   5.235 -  rightarg = epoint,
   5.236 -  procedure = epoint_btree_lt,
   5.237 -  commutator = >>>,
   5.238 -  negator = >>>=,
   5.239 -  restrict = scalarltsel,
   5.240 -  join = scalarltjoinsel
   5.241 -);
   5.242 -
   5.243 -CREATE OPERATOR <<<= (
   5.244 -  leftarg = epoint,
   5.245 -  rightarg = epoint,
   5.246 -  procedure = epoint_btree_le,
   5.247 -  commutator = >>>=,
   5.248 -  negator = >>>,
   5.249 -  restrict = scalarltsel,
   5.250 -  join = scalarltjoinsel
   5.251 -);
   5.252 -
   5.253 -CREATE OPERATOR = (
   5.254 -  leftarg = epoint,
   5.255 -  rightarg = epoint,
   5.256 -  procedure = epoint_btree_eq,
   5.257 -  commutator = =,
   5.258 -  negator = <>,
   5.259 -  restrict = eqsel,
   5.260 -  join = eqjoinsel,
   5.261 -  merges
   5.262 -);
   5.263 -
   5.264 -CREATE OPERATOR <> (
   5.265 -  leftarg = epoint,
   5.266 -  rightarg = epoint,
   5.267 -  procedure = epoint_btree_ne,
   5.268 -  commutator = <>,
   5.269 -  negator = =,
   5.270 -  restrict = neqsel,
   5.271 -  join = neqjoinsel
   5.272 -);
   5.273 -
   5.274 -CREATE OPERATOR >>>= (
   5.275 -  leftarg = epoint,
   5.276 -  rightarg = epoint,
   5.277 -  procedure = epoint_btree_ge,
   5.278 -  commutator = <<<=,
   5.279 -  negator = <<<,
   5.280 -  restrict = scalargtsel,
   5.281 -  join = scalargtjoinsel
   5.282 -);
   5.283 -
   5.284 -CREATE OPERATOR >>> (
   5.285 -  leftarg = epoint,
   5.286 -  rightarg = epoint,
   5.287 -  procedure = epoint_btree_gt,
   5.288 -  commutator = <<<,
   5.289 -  negator = <<<=,
   5.290 -  restrict = scalargtsel,
   5.291 -  join = scalargtjoinsel
   5.292 -);
   5.293 -
   5.294 -CREATE FUNCTION epoint_btree_cmp(epoint, epoint)
   5.295 -  RETURNS int4
   5.296 -  LANGUAGE C IMMUTABLE STRICT
   5.297 -  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_cmp';
   5.298 -
   5.299 -CREATE OPERATOR CLASS epoint_btree_ops
   5.300 -  DEFAULT FOR TYPE epoint USING btree AS
   5.301 -  OPERATOR 1 <<< ,
   5.302 -  OPERATOR 2 <<<= ,
   5.303 -  OPERATOR 3 = ,
   5.304 -  OPERATOR 4 >>>= ,
   5.305 -  OPERATOR 5 >>> ,
   5.306 -  FUNCTION 1 epoint_btree_cmp(epoint, epoint);
   5.307 -
   5.308 --- end of B-tree support for epoint
   5.309 -
   5.310 --- begin of B-tree support for ebox
   5.311 -
   5.312 -CREATE FUNCTION ebox_btree_lt(ebox, ebox)
   5.313 -  RETURNS boolean
   5.314 -  LANGUAGE C IMMUTABLE STRICT
   5.315 -  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_lt';
   5.316 -
   5.317 -CREATE FUNCTION ebox_btree_le(ebox, ebox)
   5.318 -  RETURNS boolean
   5.319 -  LANGUAGE C IMMUTABLE STRICT
   5.320 -  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_le';
   5.321 -
   5.322 -CREATE FUNCTION ebox_btree_eq(ebox, ebox)
   5.323 -  RETURNS boolean
   5.324 -  LANGUAGE C IMMUTABLE STRICT
   5.325 -  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_eq';
   5.326 -
   5.327 -CREATE FUNCTION ebox_btree_ne(ebox, ebox)
   5.328 -  RETURNS boolean
   5.329 -  LANGUAGE C IMMUTABLE STRICT
   5.330 -  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ne';
   5.331 -
   5.332 -CREATE FUNCTION ebox_btree_ge(ebox, ebox)
   5.333 -  RETURNS boolean
   5.334 -  LANGUAGE C IMMUTABLE STRICT
   5.335 -  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ge';
   5.336 -
   5.337 -CREATE FUNCTION ebox_btree_gt(ebox, ebox)
   5.338 -  RETURNS boolean
   5.339 -  LANGUAGE C IMMUTABLE STRICT
   5.340 -  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_gt';
   5.341 -
   5.342 -CREATE OPERATOR <<< (
   5.343 -  leftarg = ebox,
   5.344 -  rightarg = ebox,
   5.345 -  procedure = ebox_btree_lt,
   5.346 -  commutator = >>>,
   5.347 -  negator = >>>=,
   5.348 -  restrict = scalarltsel,
   5.349 -  join = scalarltjoinsel
   5.350 -);
   5.351 -
   5.352 -CREATE OPERATOR <<<= (
   5.353 -  leftarg = ebox,
   5.354 -  rightarg = ebox,
   5.355 -  procedure = ebox_btree_le,
   5.356 -  commutator = >>>=,
   5.357 -  negator = >>>,
   5.358 -  restrict = scalarltsel,
   5.359 -  join = scalarltjoinsel
   5.360 -);
   5.361 -
   5.362 -CREATE OPERATOR = (
   5.363 -  leftarg = ebox,
   5.364 -  rightarg = ebox,
   5.365 -  procedure = ebox_btree_eq,
   5.366 -  commutator = =,
   5.367 -  negator = <>,
   5.368 -  restrict = eqsel,
   5.369 -  join = eqjoinsel,
   5.370 -  merges
   5.371 -);
   5.372 -
   5.373 -CREATE OPERATOR <> (
   5.374 -  leftarg = ebox,
   5.375 -  rightarg = ebox,
   5.376 -  procedure = ebox_btree_ne,
   5.377 -  commutator = <>,
   5.378 -  negator = =,
   5.379 -  restrict = neqsel,
   5.380 -  join = neqjoinsel
   5.381 -);
   5.382 -
   5.383 -CREATE OPERATOR >>>= (
   5.384 -  leftarg = ebox,
   5.385 -  rightarg = ebox,
   5.386 -  procedure = ebox_btree_ge,
   5.387 -  commutator = <<<=,
   5.388 -  negator = <<<,
   5.389 -  restrict = scalargtsel,
   5.390 -  join = scalargtjoinsel
   5.391 -);
   5.392 -
   5.393 -CREATE OPERATOR >>> (
   5.394 -  leftarg = ebox,
   5.395 -  rightarg = ebox,
   5.396 -  procedure = ebox_btree_gt,
   5.397 -  commutator = <<<,
   5.398 -  negator = <<<=,
   5.399 -  restrict = scalargtsel,
   5.400 -  join = scalargtjoinsel
   5.401 -);
   5.402 -
   5.403 -CREATE FUNCTION ebox_btree_cmp(ebox, ebox)
   5.404 -  RETURNS int4
   5.405 -  LANGUAGE C IMMUTABLE STRICT
   5.406 -  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_cmp';
   5.407 -
   5.408 -CREATE OPERATOR CLASS ebox_btree_ops
   5.409 -  DEFAULT FOR TYPE ebox USING btree AS
   5.410 -  OPERATOR 1 <<< ,
   5.411 -  OPERATOR 2 <<<= ,
   5.412 -  OPERATOR 3 = ,
   5.413 -  OPERATOR 4 >>>= ,
   5.414 -  OPERATOR 5 >>> ,
   5.415 -  FUNCTION 1 ebox_btree_cmp(ebox, ebox);
   5.416 -
   5.417 --- end of B-tree support for ebox
   5.418 -
   5.419 --- begin of B-tree support for ecircle
   5.420 -
   5.421 -CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   5.422 -  RETURNS boolean
   5.423 -  LANGUAGE C IMMUTABLE STRICT
   5.424 -  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_lt';
   5.425 -
   5.426 -CREATE FUNCTION ecircle_btree_le(ecircle, ecircle)
   5.427 -  RETURNS boolean
   5.428 -  LANGUAGE C IMMUTABLE STRICT
   5.429 -  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_le';
   5.430 -
   5.431 -CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   5.432 -  RETURNS boolean
   5.433 -  LANGUAGE C IMMUTABLE STRICT
   5.434 -  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_eq';
   5.435 -
   5.436 -CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   5.437 -  RETURNS boolean
   5.438 -  LANGUAGE C IMMUTABLE STRICT
   5.439 -  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ne';
   5.440 -
   5.441 -CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   5.442 -  RETURNS boolean
   5.443 -  LANGUAGE C IMMUTABLE STRICT
   5.444 -  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ge';
   5.445 -
   5.446 -CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   5.447 -  RETURNS boolean
   5.448 -  LANGUAGE C IMMUTABLE STRICT
   5.449 -  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_gt';
   5.450 -
   5.451 -CREATE OPERATOR <<< (
   5.452 -  leftarg = ecircle,
   5.453 -  rightarg = ecircle,
   5.454 -  procedure = ecircle_btree_lt,
   5.455 -  commutator = >>>,
   5.456 -  negator = >>>=,
   5.457 -  restrict = scalarltsel,
   5.458 -  join = scalarltjoinsel
   5.459 -);
   5.460 -
   5.461 -CREATE OPERATOR <<<= (
   5.462 -  leftarg = ecircle,
   5.463 -  rightarg = ecircle,
   5.464 -  procedure = ecircle_btree_le,
   5.465 -  commutator = >>>=,
   5.466 -  negator = >>>,
   5.467 -  restrict = scalarltsel,
   5.468 -  join = scalarltjoinsel
   5.469 -);
   5.470 -
   5.471 -CREATE OPERATOR = (
   5.472 -  leftarg = ecircle,
   5.473 -  rightarg = ecircle,
   5.474 -  procedure = ecircle_btree_eq,
   5.475 -  commutator = =,
   5.476 -  negator = <>,
   5.477 -  restrict = eqsel,
   5.478 -  join = eqjoinsel,
   5.479 -  merges
   5.480 -);
   5.481 -
   5.482 -CREATE OPERATOR <> (
   5.483 -  leftarg = ecircle,
   5.484 -  rightarg = ecircle,
   5.485 -  procedure = ecircle_btree_ne,
   5.486 -  commutator = <>,
   5.487 -  negator = =,
   5.488 -  restrict = neqsel,
   5.489 -  join = neqjoinsel
   5.490 -);
   5.491 -
   5.492 -CREATE OPERATOR >>>= (
   5.493 -  leftarg = ecircle,
   5.494 -  rightarg = ecircle,
   5.495 -  procedure = ecircle_btree_ge,
   5.496 -  commutator = <<<=,
   5.497 -  negator = <<<,
   5.498 -  restrict = scalargtsel,
   5.499 -  join = scalargtjoinsel
   5.500 -);
   5.501 -
   5.502 -CREATE OPERATOR >>> (
   5.503 -  leftarg = ecircle,
   5.504 -  rightarg = ecircle,
   5.505 -  procedure = ecircle_btree_gt,
   5.506 -  commutator = <<<,
   5.507 -  negator = <<<=,
   5.508 -  restrict = scalargtsel,
   5.509 -  join = scalargtjoinsel
   5.510 -);
   5.511 -
   5.512 -CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   5.513 -  RETURNS int4
   5.514 -  LANGUAGE C IMMUTABLE STRICT
   5.515 -  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_cmp';
   5.516 -
   5.517 -CREATE OPERATOR CLASS ecircle_btree_ops
   5.518 -  DEFAULT FOR TYPE ecircle USING btree AS
   5.519 -  OPERATOR 1 <<< ,
   5.520 -  OPERATOR 2 <<<= ,
   5.521 -  OPERATOR 3 = ,
   5.522 -  OPERATOR 4 >>>= ,
   5.523 -  OPERATOR 5 >>> ,
   5.524 -  FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle);
   5.525 -
   5.526 --- end of B-tree support for ecircle
   5.527 -
   5.528 -
   5.529 -----------------
   5.530 --- type casts --
   5.531 -----------------
   5.532 -
   5.533 -CREATE FUNCTION cast_epoint_to_ebox(epoint)
   5.534 -  RETURNS ebox
   5.535 -  LANGUAGE C IMMUTABLE STRICT
   5.536 -  AS '$libdir/latlon-v0009', 'pgl_epoint_to_ebox';
   5.537 -
   5.538 -CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint);
   5.539 -
   5.540 -CREATE FUNCTION cast_epoint_to_ecircle(epoint)
   5.541 -  RETURNS ecircle
   5.542 -  LANGUAGE C IMMUTABLE STRICT
   5.543 -  AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecircle';
   5.544 -
   5.545 -CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint);
   5.546 -
   5.547 -CREATE FUNCTION cast_epoint_to_ecluster(epoint)
   5.548 -  RETURNS ecluster
   5.549 -  LANGUAGE C IMMUTABLE STRICT
   5.550 -  AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecluster';
   5.551 -
   5.552 -CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint);
   5.553 -
   5.554 -CREATE FUNCTION cast_ebox_to_ecluster(ebox)
   5.555 -  RETURNS ecluster
   5.556 -  LANGUAGE C IMMUTABLE STRICT
   5.557 -  AS '$libdir/latlon-v0009', 'pgl_ebox_to_ecluster';
   5.558 -
   5.559 -CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox);
   5.560 -
   5.561 -
   5.562 ----------------------------
   5.563 --- constructor functions --
   5.564 ----------------------------
   5.565 -
   5.566 -CREATE FUNCTION epoint(float8, float8)
   5.567 -  RETURNS epoint
   5.568 -  LANGUAGE C IMMUTABLE STRICT
   5.569 -  AS '$libdir/latlon-v0009', 'pgl_create_epoint';
   5.570 -
   5.571 -CREATE FUNCTION epoint_latlon(float8, float8)
   5.572 -  RETURNS epoint
   5.573 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   5.574 -    SELECT epoint($1, $2)
   5.575 -  $$;
   5.576 -
   5.577 -CREATE FUNCTION epoint_lonlat(float8, float8)
   5.578 -  RETURNS epoint
   5.579 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   5.580 -    SELECT epoint($2, $1)
   5.581 -  $$;
   5.582 -
   5.583 -CREATE FUNCTION epoint_with_sample_count(epoint, int4)
   5.584 -  RETURNS epoint_with_sample_count
   5.585 -  LANGUAGE C IMMUTABLE STRICT
   5.586 -  AS '$libdir/latlon-v0009', 'pgl_create_epoint_with_sample_count';
   5.587 -
   5.588 -CREATE FUNCTION empty_ebox()
   5.589 -  RETURNS ebox
   5.590 -  LANGUAGE C IMMUTABLE STRICT
   5.591 -  AS '$libdir/latlon-v0009', 'pgl_create_empty_ebox';
   5.592 -
   5.593 -CREATE FUNCTION ebox(float8, float8, float8, float8)
   5.594 -  RETURNS ebox
   5.595 -  LANGUAGE C IMMUTABLE STRICT
   5.596 -  AS '$libdir/latlon-v0009', 'pgl_create_ebox';
   5.597 -
   5.598 -CREATE FUNCTION ebox(epoint, epoint)
   5.599 -  RETURNS ebox
   5.600 -  LANGUAGE C IMMUTABLE STRICT
   5.601 -  AS '$libdir/latlon-v0009', 'pgl_create_ebox_from_epoints';
   5.602 -
   5.603 -CREATE FUNCTION ecircle(float8, float8, float8)
   5.604 -  RETURNS ecircle
   5.605 -  LANGUAGE C IMMUTABLE STRICT
   5.606 -  AS '$libdir/latlon-v0009', 'pgl_create_ecircle';
   5.607 -
   5.608 -CREATE FUNCTION ecircle(epoint, float8)
   5.609 -  RETURNS ecircle
   5.610 -  LANGUAGE C IMMUTABLE STRICT
   5.611 -  AS '$libdir/latlon-v0009', 'pgl_create_ecircle_from_epoint';
   5.612 -
   5.613 -CREATE FUNCTION ecluster_concat(ecluster[])
   5.614 -  RETURNS ecluster
   5.615 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.616 -    SELECT array_to_string($1, ' ')::ecluster
   5.617 -  $$;
   5.618 -
   5.619 -CREATE FUNCTION ecluster_concat(ecluster, ecluster)
   5.620 -  RETURNS ecluster
   5.621 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.622 -    SELECT ($1::text || ' ' || $2::text)::ecluster
   5.623 -  $$;
   5.624 -
   5.625 -CREATE FUNCTION ecluster_create_multipoint(epoint[])
   5.626 -  RETURNS ecluster
   5.627 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.628 -    SELECT
   5.629 -      array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster
   5.630 -    FROM unnest($1)
   5.631 -  $$;
   5.632 -
   5.633 -CREATE FUNCTION ecluster_create_path(epoint[])
   5.634 -  RETURNS ecluster
   5.635 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.636 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   5.637 -      ('path (' || array_to_string($1, ' ') || ')')::ecluster
   5.638 -    END
   5.639 -    FROM array_to_string($1, ' ') AS "str"
   5.640 -  $$;
   5.641 -
   5.642 -CREATE FUNCTION ecluster_create_outline(epoint[])
   5.643 -  RETURNS ecluster
   5.644 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.645 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   5.646 -      ('outline (' || array_to_string($1, ' ') || ')')::ecluster
   5.647 -    END
   5.648 -    FROM array_to_string($1, ' ') AS "str"
   5.649 -  $$;
   5.650 -
   5.651 -CREATE FUNCTION ecluster_create_polygon(epoint[])
   5.652 -  RETURNS ecluster
   5.653 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.654 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   5.655 -      ('polygon (' || array_to_string($1, ' ') || ')')::ecluster
   5.656 -    END
   5.657 -    FROM array_to_string($1, ' ') AS "str"
   5.658 -  $$;
   5.659 -
   5.660 -
   5.661 -----------------------
   5.662 --- getter functions --
   5.663 -----------------------
   5.664 -
   5.665 -CREATE FUNCTION latitude(epoint)
   5.666 -  RETURNS float8
   5.667 -  LANGUAGE C IMMUTABLE STRICT
   5.668 -  AS '$libdir/latlon-v0009', 'pgl_epoint_lat';
   5.669 -
   5.670 -CREATE FUNCTION longitude(epoint)
   5.671 -  RETURNS float8
   5.672 -  LANGUAGE C IMMUTABLE STRICT
   5.673 -  AS '$libdir/latlon-v0009', 'pgl_epoint_lon';
   5.674 -
   5.675 -CREATE FUNCTION min_latitude(ebox)
   5.676 -  RETURNS float8
   5.677 -  LANGUAGE C IMMUTABLE STRICT
   5.678 -  AS '$libdir/latlon-v0009', 'pgl_ebox_lat_min';
   5.679 -
   5.680 -CREATE FUNCTION max_latitude(ebox)
   5.681 -  RETURNS float8
   5.682 -  LANGUAGE C IMMUTABLE STRICT
   5.683 -  AS '$libdir/latlon-v0009', 'pgl_ebox_lat_max';
   5.684 -
   5.685 -CREATE FUNCTION min_longitude(ebox)
   5.686 -  RETURNS float8
   5.687 -  LANGUAGE C IMMUTABLE STRICT
   5.688 -  AS '$libdir/latlon-v0009', 'pgl_ebox_lon_min';
   5.689 -
   5.690 -CREATE FUNCTION max_longitude(ebox)
   5.691 -  RETURNS float8
   5.692 -  LANGUAGE C IMMUTABLE STRICT
   5.693 -  AS '$libdir/latlon-v0009', 'pgl_ebox_lon_max';
   5.694 -
   5.695 -CREATE FUNCTION center(ecircle)
   5.696 -  RETURNS epoint
   5.697 -  LANGUAGE C IMMUTABLE STRICT
   5.698 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_center';
   5.699 -
   5.700 -CREATE FUNCTION radius(ecircle)
   5.701 -  RETURNS float8
   5.702 -  LANGUAGE C IMMUTABLE STRICT
   5.703 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_radius';
   5.704 -
   5.705 -CREATE FUNCTION ecluster_extract_points(ecluster)
   5.706 -  RETURNS SETOF epoint
   5.707 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.708 -    SELECT "match"[2]::epoint
   5.709 -    FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match"
   5.710 -  $$;
   5.711 -
   5.712 -CREATE FUNCTION ecluster_extract_paths(ecluster)
   5.713 -  RETURNS SETOF epoint[]
   5.714 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.715 -    SELECT (
   5.716 -      SELECT array_agg("m2"[1]::epoint)
   5.717 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   5.718 -    )
   5.719 -    FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1"
   5.720 -  $$;
   5.721 -
   5.722 -CREATE FUNCTION ecluster_extract_outlines(ecluster)
   5.723 -  RETURNS SETOF epoint[]
   5.724 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.725 -    SELECT (
   5.726 -      SELECT array_agg("m2"[1]::epoint)
   5.727 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   5.728 -    )
   5.729 -    FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1"
   5.730 -  $$;
   5.731 -
   5.732 -CREATE FUNCTION ecluster_extract_polygons(ecluster)
   5.733 -  RETURNS SETOF epoint[]
   5.734 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   5.735 -    SELECT (
   5.736 -      SELECT array_agg("m2"[1]::epoint)
   5.737 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   5.738 -    )
   5.739 -    FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1"
   5.740 -  $$;
   5.741 -
   5.742 -
   5.743 ----------------
   5.744 --- operators --
   5.745 ----------------
   5.746 -
   5.747 -CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   5.748 -  RETURNS boolean
   5.749 -  LANGUAGE C IMMUTABLE STRICT
   5.750 -  AS '$libdir/latlon-v0009', 'pgl_epoint_ebox_overlap';
   5.751 -
   5.752 -CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   5.753 -  RETURNS boolean
   5.754 -  LANGUAGE C IMMUTABLE STRICT
   5.755 -  AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_overlap';
   5.756 -
   5.757 -CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   5.758 -  RETURNS boolean
   5.759 -  LANGUAGE C IMMUTABLE STRICT
   5.760 -  AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_overlap';
   5.761 -
   5.762 -CREATE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
   5.763 -  RETURNS boolean
   5.764 -  LANGUAGE C IMMUTABLE STRICT
   5.765 -  AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_may_overlap';
   5.766 -
   5.767 -CREATE FUNCTION ebox_overlap_proc(ebox, ebox)
   5.768 -  RETURNS boolean
   5.769 -  LANGUAGE C IMMUTABLE STRICT
   5.770 -  AS '$libdir/latlon-v0009', 'pgl_ebox_overlap';
   5.771 -
   5.772 -CREATE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
   5.773 -  RETURNS boolean
   5.774 -  LANGUAGE C IMMUTABLE STRICT
   5.775 -  AS '$libdir/latlon-v0009', 'pgl_ebox_ecircle_may_overlap';
   5.776 -
   5.777 -CREATE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
   5.778 -  RETURNS boolean
   5.779 -  LANGUAGE C IMMUTABLE STRICT
   5.780 -  AS '$libdir/latlon-v0009', 'pgl_ebox_ecluster_may_overlap';
   5.781 -
   5.782 -CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   5.783 -  RETURNS boolean
   5.784 -  LANGUAGE C IMMUTABLE STRICT
   5.785 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_overlap';
   5.786 -
   5.787 -CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   5.788 -  RETURNS boolean
   5.789 -  LANGUAGE C IMMUTABLE STRICT
   5.790 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_overlap';
   5.791 -
   5.792 -CREATE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
   5.793 -  RETURNS boolean
   5.794 -  LANGUAGE C IMMUTABLE STRICT
   5.795 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_may_overlap';
   5.796 -
   5.797 -CREATE FUNCTION ecluster_overlap_proc(ecluster, ecluster)
   5.798 -  RETURNS boolean
   5.799 -  LANGUAGE C IMMUTABLE STRICT
   5.800 -  AS '$libdir/latlon-v0009', 'pgl_ecluster_overlap';
   5.801 -
   5.802 -CREATE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
   5.803 -  RETURNS boolean
   5.804 -  LANGUAGE C IMMUTABLE STRICT
   5.805 -  AS '$libdir/latlon-v0009', 'pgl_ecluster_may_overlap';
   5.806 -
   5.807 -CREATE FUNCTION ecluster_contains_proc(ecluster, ecluster)
   5.808 -  RETURNS boolean
   5.809 -  LANGUAGE C IMMUTABLE STRICT
   5.810 -  AS '$libdir/latlon-v0009', 'pgl_ecluster_contains';
   5.811 -
   5.812 -CREATE FUNCTION epoint_distance_proc(epoint, epoint)
   5.813 -  RETURNS float8
   5.814 -  LANGUAGE C IMMUTABLE STRICT
   5.815 -  AS '$libdir/latlon-v0009', 'pgl_epoint_distance';
   5.816 -
   5.817 -CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   5.818 -  RETURNS float8
   5.819 -  LANGUAGE C IMMUTABLE STRICT
   5.820 -  AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_distance';
   5.821 -
   5.822 -CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   5.823 -  RETURNS float8
   5.824 -  LANGUAGE C IMMUTABLE STRICT
   5.825 -  AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_distance';
   5.826 -
   5.827 -CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   5.828 -  RETURNS float8
   5.829 -  LANGUAGE C IMMUTABLE STRICT
   5.830 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_distance';
   5.831 -
   5.832 -CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   5.833 -  RETURNS float8
   5.834 -  LANGUAGE C IMMUTABLE STRICT
   5.835 -  AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_distance';
   5.836 -
   5.837 -CREATE FUNCTION ecluster_distance_proc(ecluster, ecluster)
   5.838 -  RETURNS float8
   5.839 -  LANGUAGE C IMMUTABLE STRICT
   5.840 -  AS '$libdir/latlon-v0009', 'pgl_ecluster_distance';
   5.841 -
   5.842 -CREATE FUNCTION fair_distance_operator_proc(ecluster, epoint_with_sample_count)
   5.843 -  RETURNS float8
   5.844 -  LANGUAGE C IMMUTABLE STRICT
   5.845 -  AS '$libdir/latlon-v0009', 'pgl_ecluster_epoint_sc_fair_distance';
   5.846 -
   5.847 -CREATE OPERATOR && (
   5.848 -  leftarg = epoint,
   5.849 -  rightarg = ebox,
   5.850 -  procedure = epoint_ebox_overlap_proc,
   5.851 -  commutator = &&,
   5.852 -  restrict = areasel,
   5.853 -  join = areajoinsel
   5.854 -);
   5.855 -
   5.856 -CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint)
   5.857 -  RETURNS boolean
   5.858 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   5.859 -
   5.860 -CREATE OPERATOR && (
   5.861 -  leftarg = ebox,
   5.862 -  rightarg = epoint,
   5.863 -  procedure = epoint_ebox_overlap_commutator,
   5.864 -  commutator = &&,
   5.865 -  restrict = areasel,
   5.866 -  join = areajoinsel
   5.867 -);
   5.868 -
   5.869 -CREATE OPERATOR && (
   5.870 -  leftarg = epoint,
   5.871 -  rightarg = ecircle,
   5.872 -  procedure = epoint_ecircle_overlap_proc,
   5.873 -  commutator = &&,
   5.874 -  restrict = areasel,
   5.875 -  join = areajoinsel
   5.876 -);
   5.877 -
   5.878 -CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint)
   5.879 -  RETURNS boolean
   5.880 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   5.881 -
   5.882 -CREATE OPERATOR && (
   5.883 -  leftarg = ecircle,
   5.884 -  rightarg = epoint,
   5.885 -  procedure = epoint_ecircle_overlap_commutator,
   5.886 -  commutator = &&,
   5.887 -  restrict = areasel,
   5.888 -  join = areajoinsel
   5.889 -);
   5.890 -
   5.891 -CREATE OPERATOR && (
   5.892 -  leftarg = epoint,
   5.893 -  rightarg = ecluster,
   5.894 -  procedure = epoint_ecluster_overlap_proc,
   5.895 -  commutator = &&,
   5.896 -  restrict = areasel,
   5.897 -  join = areajoinsel
   5.898 -);
   5.899 -
   5.900 -CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint)
   5.901 -  RETURNS boolean
   5.902 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   5.903 -
   5.904 -CREATE OPERATOR && (
   5.905 -  leftarg = ecluster,
   5.906 -  rightarg = epoint,
   5.907 -  procedure = epoint_ecluster_overlap_commutator,
   5.908 -  commutator = &&,
   5.909 -  restrict = areasel,
   5.910 -  join = areajoinsel
   5.911 -);
   5.912 -
   5.913 -CREATE OPERATOR && (
   5.914 -  leftarg = ebox,
   5.915 -  rightarg = ebox,
   5.916 -  procedure = ebox_overlap_proc,
   5.917 -  commutator = &&,
   5.918 -  restrict = areasel,
   5.919 -  join = areajoinsel
   5.920 -);
   5.921 -
   5.922 -CREATE OPERATOR && (
   5.923 -  leftarg = ecircle,
   5.924 -  rightarg = ecircle,
   5.925 -  procedure = ecircle_overlap_proc,
   5.926 -  commutator = &&,
   5.927 -  restrict = areasel,
   5.928 -  join = areajoinsel
   5.929 -);
   5.930 -
   5.931 -CREATE OPERATOR && (
   5.932 -  leftarg = ecircle,
   5.933 -  rightarg = ecluster,
   5.934 -  procedure = ecircle_ecluster_overlap_proc,
   5.935 -  commutator = &&,
   5.936 -  restrict = areasel,
   5.937 -  join = areajoinsel
   5.938 -);
   5.939 -
   5.940 -CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle)
   5.941 -  RETURNS boolean
   5.942 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   5.943 -
   5.944 -CREATE OPERATOR && (
   5.945 -  leftarg = ecluster,
   5.946 -  rightarg = ecircle,
   5.947 -  procedure = ecircle_ecluster_overlap_commutator,
   5.948 -  commutator = &&,
   5.949 -  restrict = areasel,
   5.950 -  join = areajoinsel
   5.951 -);
   5.952 -
   5.953 -CREATE OPERATOR && (
   5.954 -  leftarg = ecluster,
   5.955 -  rightarg = ecluster,
   5.956 -  procedure = ecluster_overlap_proc,
   5.957 -  commutator = &&,
   5.958 -  restrict = areasel,
   5.959 -  join = areajoinsel
   5.960 -);
   5.961 -
   5.962 -CREATE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle)
   5.963 -  RETURNS boolean
   5.964 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   5.965 -
   5.966 -CREATE OPERATOR && (
   5.967 -  leftarg = ebox,
   5.968 -  rightarg = ecircle,
   5.969 -  procedure = ebox_ecircle_overlap_castwrap,
   5.970 -  commutator = &&,
   5.971 -  restrict = areasel,
   5.972 -  join = areajoinsel
   5.973 -);
   5.974 -
   5.975 -CREATE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox)
   5.976 -  RETURNS boolean
   5.977 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
   5.978 -
   5.979 -CREATE OPERATOR && (
   5.980 -  leftarg = ecircle,
   5.981 -  rightarg = ebox,
   5.982 -  procedure = ebox_ecircle_overlap_castwrap,
   5.983 -  commutator = &&,
   5.984 -  restrict = areasel,
   5.985 -  join = areajoinsel
   5.986 -);
   5.987 -
   5.988 -CREATE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster)
   5.989 -  RETURNS boolean
   5.990 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   5.991 -
   5.992 -CREATE OPERATOR && (
   5.993 -  leftarg = ebox,
   5.994 -  rightarg = ecluster,
   5.995 -  procedure = ebox_ecluster_overlap_castwrap,
   5.996 -  commutator = &&,
   5.997 -  restrict = areasel,
   5.998 -  join = areajoinsel
   5.999 -);
  5.1000 -
  5.1001 -CREATE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox)
  5.1002 -  RETURNS boolean
  5.1003 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
  5.1004 -
  5.1005 -CREATE OPERATOR && (
  5.1006 -  leftarg = ecluster,
  5.1007 -  rightarg = ebox,
  5.1008 -  procedure = ebox_ecluster_overlap_castwrap,
  5.1009 -  commutator = &&,
  5.1010 -  restrict = areasel,
  5.1011 -  join = areajoinsel
  5.1012 -);
  5.1013 -
  5.1014 -CREATE OPERATOR &&+ (
  5.1015 -  leftarg = epoint,
  5.1016 -  rightarg = ecluster,
  5.1017 -  procedure = epoint_ecluster_may_overlap_proc,
  5.1018 -  commutator = &&+,
  5.1019 -  restrict = areasel,
  5.1020 -  join = areajoinsel
  5.1021 -);
  5.1022 -
  5.1023 -CREATE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint)
  5.1024 -  RETURNS boolean
  5.1025 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  5.1026 -
  5.1027 -CREATE OPERATOR &&+ (
  5.1028 -  leftarg = ecluster,
  5.1029 -  rightarg = epoint,
  5.1030 -  procedure = epoint_ecluster_may_overlap_commutator,
  5.1031 -  commutator = &&+,
  5.1032 -  restrict = areasel,
  5.1033 -  join = areajoinsel
  5.1034 -);
  5.1035 -
  5.1036 -CREATE OPERATOR &&+ (
  5.1037 -  leftarg = ebox,
  5.1038 -  rightarg = ecircle,
  5.1039 -  procedure = ebox_ecircle_may_overlap_proc,
  5.1040 -  commutator = &&+,
  5.1041 -  restrict = areasel,
  5.1042 -  join = areajoinsel
  5.1043 -);
  5.1044 -
  5.1045 -CREATE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox)
  5.1046 -  RETURNS boolean
  5.1047 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  5.1048 -
  5.1049 -CREATE OPERATOR &&+ (
  5.1050 -  leftarg = ecircle,
  5.1051 -  rightarg = ebox,
  5.1052 -  procedure = ebox_ecircle_may_overlap_commutator,
  5.1053 -  commutator = &&+,
  5.1054 -  restrict = areasel,
  5.1055 -  join = areajoinsel
  5.1056 -);
  5.1057 -
  5.1058 -CREATE OPERATOR &&+ (
  5.1059 -  leftarg = ebox,
  5.1060 -  rightarg = ecluster,
  5.1061 -  procedure = ebox_ecluster_may_overlap_proc,
  5.1062 -  commutator = &&+,
  5.1063 -  restrict = areasel,
  5.1064 -  join = areajoinsel
  5.1065 -);
  5.1066 -
  5.1067 -CREATE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox)
  5.1068 -  RETURNS boolean
  5.1069 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  5.1070 -
  5.1071 -CREATE OPERATOR &&+ (
  5.1072 -  leftarg = ecluster,
  5.1073 -  rightarg = ebox,
  5.1074 -  procedure = ebox_ecluster_may_overlap_commutator,
  5.1075 -  commutator = &&+,
  5.1076 -  restrict = areasel,
  5.1077 -  join = areajoinsel
  5.1078 -);
  5.1079 -
  5.1080 -CREATE OPERATOR &&+ (
  5.1081 -  leftarg = ecircle,
  5.1082 -  rightarg = ecluster,
  5.1083 -  procedure = ecircle_ecluster_may_overlap_proc,
  5.1084 -  commutator = &&+,
  5.1085 -  restrict = areasel,
  5.1086 -  join = areajoinsel
  5.1087 -);
  5.1088 -
  5.1089 -CREATE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle)
  5.1090 -  RETURNS boolean
  5.1091 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  5.1092 -
  5.1093 -CREATE OPERATOR &&+ (
  5.1094 -  leftarg = ecluster,
  5.1095 -  rightarg = ecircle,
  5.1096 -  procedure = ecircle_ecluster_may_overlap_commutator,
  5.1097 -  commutator = &&+,
  5.1098 -  restrict = areasel,
  5.1099 -  join = areajoinsel
  5.1100 -);
  5.1101 -
  5.1102 -CREATE OPERATOR &&+ (
  5.1103 -  leftarg = ecluster,
  5.1104 -  rightarg = ecluster,
  5.1105 -  procedure = ecluster_may_overlap_proc,
  5.1106 -  commutator = &&+,
  5.1107 -  restrict = areasel,
  5.1108 -  join = areajoinsel
  5.1109 -);
  5.1110 -
  5.1111 -CREATE OPERATOR @> (
  5.1112 -  leftarg = ebox,
  5.1113 -  rightarg = epoint,
  5.1114 -  procedure = epoint_ebox_overlap_commutator,
  5.1115 -  commutator = <@,
  5.1116 -  restrict = areasel,
  5.1117 -  join = areajoinsel
  5.1118 -);
  5.1119 -
  5.1120 -CREATE OPERATOR <@ (
  5.1121 -  leftarg = epoint,
  5.1122 -  rightarg = ebox,
  5.1123 -  procedure = epoint_ebox_overlap_proc,
  5.1124 -  commutator = @>,
  5.1125 -  restrict = areasel,
  5.1126 -  join = areajoinsel
  5.1127 -);
  5.1128 -
  5.1129 -CREATE OPERATOR @> (
  5.1130 -  leftarg = ecluster,
  5.1131 -  rightarg = epoint,
  5.1132 -  procedure = epoint_ecluster_overlap_commutator,
  5.1133 -  commutator = <@,
  5.1134 -  restrict = areasel,
  5.1135 -  join = areajoinsel
  5.1136 -);
  5.1137 -
  5.1138 -CREATE OPERATOR <@ (
  5.1139 -  leftarg = epoint,
  5.1140 -  rightarg = ecluster,
  5.1141 -  procedure = epoint_ecluster_overlap_proc,
  5.1142 -  commutator = @>,
  5.1143 -  restrict = areasel,
  5.1144 -  join = areajoinsel
  5.1145 -);
  5.1146 -
  5.1147 -CREATE OPERATOR @> (
  5.1148 -  leftarg = ecluster,
  5.1149 -  rightarg = ecluster,
  5.1150 -  procedure = ecluster_contains_proc,
  5.1151 -  commutator = <@,
  5.1152 -  restrict = areasel,
  5.1153 -  join = areajoinsel
  5.1154 -);
  5.1155 -
  5.1156 -CREATE FUNCTION ecluster_contains_commutator(ecluster, ecluster)
  5.1157 -  RETURNS boolean
  5.1158 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1';
  5.1159 -
  5.1160 -CREATE OPERATOR <@ (
  5.1161 -  leftarg = ecluster,
  5.1162 -  rightarg = ecluster,
  5.1163 -  procedure = ecluster_contains_commutator,
  5.1164 -  commutator = @>,
  5.1165 -  restrict = areasel,
  5.1166 -  join = areajoinsel
  5.1167 -);
  5.1168 -
  5.1169 -CREATE FUNCTION ebox_contains_castwrap(ebox, ebox)
  5.1170 -  RETURNS boolean
  5.1171 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2::ecluster';
  5.1172 -
  5.1173 -CREATE OPERATOR @> (
  5.1174 -  leftarg = ebox,
  5.1175 -  rightarg = ebox,
  5.1176 -  procedure = ebox_contains_castwrap,
  5.1177 -  commutator = <@,
  5.1178 -  restrict = areasel,
  5.1179 -  join = areajoinsel
  5.1180 -);
  5.1181 -
  5.1182 -CREATE FUNCTION ebox_contains_swapped_castwrap(ebox, ebox)
  5.1183 -  RETURNS boolean
  5.1184 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1::ecluster';
  5.1185 -
  5.1186 -CREATE OPERATOR <@ (
  5.1187 -  leftarg = ebox,
  5.1188 -  rightarg = ebox,
  5.1189 -  procedure = ebox_contains_swapped_castwrap,
  5.1190 -  commutator = @>,
  5.1191 -  restrict = areasel,
  5.1192 -  join = areajoinsel
  5.1193 -);
  5.1194 -
  5.1195 -CREATE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster)
  5.1196 -  RETURNS boolean
  5.1197 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2';
  5.1198 -
  5.1199 -CREATE OPERATOR @> (
  5.1200 -  leftarg = ebox,
  5.1201 -  rightarg = ecluster,
  5.1202 -  procedure = ebox_ecluster_contains_castwrap,
  5.1203 -  commutator = <@,
  5.1204 -  restrict = areasel,
  5.1205 -  join = areajoinsel
  5.1206 -);
  5.1207 -
  5.1208 -CREATE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox)
  5.1209 -  RETURNS boolean
  5.1210 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1';
  5.1211 -
  5.1212 -CREATE OPERATOR <@ (
  5.1213 -  leftarg = ecluster,
  5.1214 -  rightarg = ebox,
  5.1215 -  procedure = ebox_ecluster_contains_castwrap,
  5.1216 -  commutator = @>,
  5.1217 -  restrict = areasel,
  5.1218 -  join = areajoinsel
  5.1219 -);
  5.1220 -
  5.1221 -CREATE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox)
  5.1222 -  RETURNS boolean
  5.1223 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 @> $2::ecluster';
  5.1224 -
  5.1225 -CREATE OPERATOR @> (
  5.1226 -  leftarg = ecluster,
  5.1227 -  rightarg = ebox,
  5.1228 -  procedure = ecluster_ebox_contains_castwrap,
  5.1229 -  commutator = <@,
  5.1230 -  restrict = areasel,
  5.1231 -  join = areajoinsel
  5.1232 -);
  5.1233 -
  5.1234 -CREATE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster)
  5.1235 -  RETURNS boolean
  5.1236 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1::ecluster';
  5.1237 -
  5.1238 -CREATE OPERATOR <@ (
  5.1239 -  leftarg = ebox,
  5.1240 -  rightarg = ecluster,
  5.1241 -  procedure = ecluster_ebox_contains_castwrap,
  5.1242 -  commutator = @>,
  5.1243 -  restrict = areasel,
  5.1244 -  join = areajoinsel
  5.1245 -);
  5.1246 -
  5.1247 -CREATE OPERATOR <-> (
  5.1248 -  leftarg = epoint,
  5.1249 -  rightarg = epoint,
  5.1250 -  procedure = epoint_distance_proc,
  5.1251 -  commutator = <->
  5.1252 -);
  5.1253 -
  5.1254 -CREATE OPERATOR <-> (
  5.1255 -  leftarg = epoint,
  5.1256 -  rightarg = ecircle,
  5.1257 -  procedure = epoint_ecircle_distance_proc,
  5.1258 -  commutator = <->
  5.1259 -);
  5.1260 -
  5.1261 -CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint)
  5.1262 -  RETURNS float8
  5.1263 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  5.1264 -
  5.1265 -CREATE OPERATOR <-> (
  5.1266 -  leftarg = ecircle,
  5.1267 -  rightarg = epoint,
  5.1268 -  procedure = epoint_ecircle_distance_commutator,
  5.1269 -  commutator = <->
  5.1270 -);
  5.1271 -
  5.1272 -CREATE OPERATOR <-> (
  5.1273 -  leftarg = epoint,
  5.1274 -  rightarg = ecluster,
  5.1275 -  procedure = epoint_ecluster_distance_proc,
  5.1276 -  commutator = <->
  5.1277 -);
  5.1278 -
  5.1279 -CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint)
  5.1280 -  RETURNS float8
  5.1281 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  5.1282 -
  5.1283 -CREATE OPERATOR <-> (
  5.1284 -  leftarg = ecluster,
  5.1285 -  rightarg = epoint,
  5.1286 -  procedure = epoint_ecluster_distance_commutator,
  5.1287 -  commutator = <->
  5.1288 -);
  5.1289 -
  5.1290 -CREATE OPERATOR <-> (
  5.1291 -  leftarg = ecircle,
  5.1292 -  rightarg = ecircle,
  5.1293 -  procedure = ecircle_distance_proc,
  5.1294 -  commutator = <->
  5.1295 -);
  5.1296 -
  5.1297 -CREATE OPERATOR <-> (
  5.1298 -  leftarg = ecircle,
  5.1299 -  rightarg = ecluster,
  5.1300 -  procedure = ecircle_ecluster_distance_proc,
  5.1301 -  commutator = <->
  5.1302 -);
  5.1303 -
  5.1304 -CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle)
  5.1305 -  RETURNS float8
  5.1306 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  5.1307 -
  5.1308 -CREATE OPERATOR <-> (
  5.1309 -  leftarg = ecluster,
  5.1310 -  rightarg = ecircle,
  5.1311 -  procedure = ecircle_ecluster_distance_commutator,
  5.1312 -  commutator = <->
  5.1313 -);
  5.1314 -
  5.1315 -CREATE OPERATOR <-> (
  5.1316 -  leftarg = ecluster,
  5.1317 -  rightarg = ecluster,
  5.1318 -  procedure = ecluster_distance_proc,
  5.1319 -  commutator = <->
  5.1320 -);
  5.1321 -
  5.1322 -CREATE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox)
  5.1323 -  RETURNS float8
  5.1324 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  5.1325 -
  5.1326 -CREATE OPERATOR <-> (
  5.1327 -  leftarg = epoint,
  5.1328 -  rightarg = ebox,
  5.1329 -  procedure = epoint_ebox_distance_castwrap,
  5.1330 -  commutator = <->
  5.1331 -);
  5.1332 -
  5.1333 -CREATE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint)
  5.1334 -  RETURNS float8
  5.1335 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  5.1336 -
  5.1337 -CREATE OPERATOR <-> (
  5.1338 -  leftarg = ebox,
  5.1339 -  rightarg = epoint,
  5.1340 -  procedure = epoint_ebox_distance_castwrap,
  5.1341 -  commutator = <->
  5.1342 -);
  5.1343 -
  5.1344 -CREATE FUNCTION ebox_distance_castwrap(ebox, ebox)
  5.1345 -  RETURNS float8
  5.1346 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2::ecluster';
  5.1347 -
  5.1348 -CREATE OPERATOR <-> (
  5.1349 -  leftarg = ebox,
  5.1350 -  rightarg = ebox,
  5.1351 -  procedure = ebox_distance_castwrap,
  5.1352 -  commutator = <->
  5.1353 -);
  5.1354 -
  5.1355 -CREATE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle)
  5.1356 -  RETURNS float8
  5.1357 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  5.1358 -
  5.1359 -CREATE OPERATOR <-> (
  5.1360 -  leftarg = ebox,
  5.1361 -  rightarg = ecircle,
  5.1362 -  procedure = ebox_ecircle_distance_castwrap,
  5.1363 -  commutator = <->
  5.1364 -);
  5.1365 -
  5.1366 -CREATE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox)
  5.1367 -  RETURNS float8
  5.1368 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  5.1369 -
  5.1370 -CREATE OPERATOR <-> (
  5.1371 -  leftarg = ecircle,
  5.1372 -  rightarg = ebox,
  5.1373 -  procedure = ebox_ecircle_distance_castwrap,
  5.1374 -  commutator = <->
  5.1375 -);
  5.1376 -
  5.1377 -CREATE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster)
  5.1378 -  RETURNS float8
  5.1379 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  5.1380 -
  5.1381 -CREATE OPERATOR <-> (
  5.1382 -  leftarg = ebox,
  5.1383 -  rightarg = ecluster,
  5.1384 -  procedure = ebox_ecluster_distance_castwrap,
  5.1385 -  commutator = <->
  5.1386 -);
  5.1387 -
  5.1388 -CREATE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox)
  5.1389 -  RETURNS float8
  5.1390 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  5.1391 -
  5.1392 -CREATE OPERATOR <-> (
  5.1393 -  leftarg = ecluster,
  5.1394 -  rightarg = ebox,
  5.1395 -  procedure = ebox_ecluster_distance_castwrap,
  5.1396 -  commutator = <->
  5.1397 -);
  5.1398 -
  5.1399 -CREATE OPERATOR <=> (
  5.1400 -  leftarg = ecluster,
  5.1401 -  rightarg = epoint_with_sample_count,
  5.1402 -  procedure = fair_distance_operator_proc
  5.1403 -);
  5.1404 -
  5.1405 -
  5.1406 -----------------
  5.1407 --- GiST index --
  5.1408 -----------------
  5.1409 -
  5.1410 -CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
  5.1411 -  RETURNS boolean
  5.1412 -  LANGUAGE C STRICT
  5.1413 -  AS '$libdir/latlon-v0009', 'pgl_gist_consistent';
  5.1414 -
  5.1415 -CREATE FUNCTION pgl_gist_union(internal, internal)
  5.1416 -  RETURNS internal
  5.1417 -  LANGUAGE C STRICT
  5.1418 -  AS '$libdir/latlon-v0009', 'pgl_gist_union';
  5.1419 -
  5.1420 -CREATE FUNCTION pgl_gist_compress_epoint(internal)
  5.1421 -  RETURNS internal
  5.1422 -  LANGUAGE C STRICT
  5.1423 -  AS '$libdir/latlon-v0009', 'pgl_gist_compress_epoint';
  5.1424 -
  5.1425 -CREATE FUNCTION pgl_gist_compress_ecircle(internal)
  5.1426 -  RETURNS internal
  5.1427 -  LANGUAGE C STRICT
  5.1428 -  AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecircle';
  5.1429 -
  5.1430 -CREATE FUNCTION pgl_gist_compress_ecluster(internal)
  5.1431 -  RETURNS internal
  5.1432 -  LANGUAGE C STRICT
  5.1433 -  AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecluster';
  5.1434 -
  5.1435 -CREATE FUNCTION pgl_gist_decompress(internal)
  5.1436 -  RETURNS internal
  5.1437 -  LANGUAGE C STRICT
  5.1438 -  AS '$libdir/latlon-v0009', 'pgl_gist_decompress';
  5.1439 -
  5.1440 -CREATE FUNCTION pgl_gist_penalty(internal, internal, internal)
  5.1441 -  RETURNS internal
  5.1442 -  LANGUAGE C STRICT
  5.1443 -  AS '$libdir/latlon-v0009', 'pgl_gist_penalty';
  5.1444 -
  5.1445 -CREATE FUNCTION pgl_gist_picksplit(internal, internal)
  5.1446 -  RETURNS internal
  5.1447 -  LANGUAGE C STRICT
  5.1448 -  AS '$libdir/latlon-v0009', 'pgl_gist_picksplit';
  5.1449 -
  5.1450 -CREATE FUNCTION pgl_gist_same(internal, internal, internal)
  5.1451 -  RETURNS internal
  5.1452 -  LANGUAGE C STRICT
  5.1453 -  AS '$libdir/latlon-v0009', 'pgl_gist_same';
  5.1454 -
  5.1455 -CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
  5.1456 -  RETURNS internal
  5.1457 -  LANGUAGE C STRICT
  5.1458 -  AS '$libdir/latlon-v0009', 'pgl_gist_distance';
  5.1459 -
  5.1460 -CREATE OPERATOR CLASS epoint_ops
  5.1461 -  DEFAULT FOR TYPE epoint USING gist AS
  5.1462 -  OPERATOR  11 = ,
  5.1463 -  OPERATOR  22 &&  (epoint, ebox),
  5.1464 -  OPERATOR 222 <@  (epoint, ebox),
  5.1465 -  OPERATOR  23 &&  (epoint, ecircle),
  5.1466 -  OPERATOR  24 &&  (epoint, ecluster),
  5.1467 -  OPERATOR 124 &&+ (epoint, ecluster),
  5.1468 -  OPERATOR 224 <@  (epoint, ecluster),
  5.1469 -  OPERATOR  31 <-> (epoint, epoint) FOR ORDER BY float_ops,
  5.1470 -  OPERATOR  32 <-> (epoint, ebox) FOR ORDER BY float_ops,
  5.1471 -  OPERATOR  33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
  5.1472 -  OPERATOR  34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
  5.1473 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  5.1474 -  FUNCTION 2 pgl_gist_union(internal, internal),
  5.1475 -  FUNCTION 3 pgl_gist_compress_epoint(internal),
  5.1476 -  FUNCTION 4 pgl_gist_decompress(internal),
  5.1477 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  5.1478 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  5.1479 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  5.1480 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  5.1481 -  STORAGE ekey_point;
  5.1482 -
  5.1483 -CREATE OPERATOR CLASS ecircle_ops
  5.1484 -  DEFAULT FOR TYPE ecircle USING gist AS
  5.1485 -  OPERATOR  13 = ,
  5.1486 -  OPERATOR  21 &&  (ecircle, epoint),
  5.1487 -  OPERATOR  22 &&  (ecircle, ebox),
  5.1488 -  OPERATOR 122 &&+ (ecircle, ebox),
  5.1489 -  OPERATOR  23 &&  (ecircle, ecircle),
  5.1490 -  OPERATOR  24 &&  (ecircle, ecluster),
  5.1491 -  OPERATOR 124 &&+ (ecircle, ecluster),
  5.1492 -  OPERATOR  31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
  5.1493 -  OPERATOR  32 <-> (ecircle, ebox) FOR ORDER BY float_ops,
  5.1494 -  OPERATOR  33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
  5.1495 -  OPERATOR  34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
  5.1496 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  5.1497 -  FUNCTION 2 pgl_gist_union(internal, internal),
  5.1498 -  FUNCTION 3 pgl_gist_compress_ecircle(internal),
  5.1499 -  FUNCTION 4 pgl_gist_decompress(internal),
  5.1500 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  5.1501 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  5.1502 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  5.1503 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  5.1504 -  STORAGE ekey_area;
  5.1505 -
  5.1506 -CREATE OPERATOR CLASS ecluster_ops
  5.1507 -  DEFAULT FOR TYPE ecluster USING gist AS
  5.1508 -  OPERATOR  21 &&  (ecluster, epoint),
  5.1509 -  OPERATOR 121 &&+ (ecluster, epoint),
  5.1510 -  OPERATOR 221 @>  (ecluster, epoint),
  5.1511 -  OPERATOR  22 &&  (ecluster, ebox),
  5.1512 -  OPERATOR 122 &&+ (ecluster, ebox),
  5.1513 -  OPERATOR 222 @>  (ecluster, ebox),
  5.1514 -  OPERATOR 322 <@  (ecluster, ebox),
  5.1515 -  OPERATOR  23 &&  (ecluster, ecircle),
  5.1516 -  OPERATOR 123 &&+ (ecluster, ecircle),
  5.1517 -  OPERATOR  24 &&  (ecluster, ecluster),
  5.1518 -  OPERATOR 124 &&+ (ecluster, ecluster),
  5.1519 -  OPERATOR 224 @>  (ecluster, ecluster),
  5.1520 -  OPERATOR 324 <@  (ecluster, ecluster),
  5.1521 -  OPERATOR  31 <-> (ecluster, epoint) FOR ORDER BY float_ops,
  5.1522 -  OPERATOR  32 <-> (ecluster, ebox) FOR ORDER BY float_ops,
  5.1523 -  OPERATOR  33 <-> (ecluster, ecircle) FOR ORDER BY float_ops,
  5.1524 -  OPERATOR  34 <-> (ecluster, ecluster) FOR ORDER BY float_ops,
  5.1525 -  OPERATOR 131 <=> (ecluster, epoint_with_sample_count) FOR ORDER BY float_ops,
  5.1526 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  5.1527 -  FUNCTION 2 pgl_gist_union(internal, internal),
  5.1528 -  FUNCTION 3 pgl_gist_compress_ecluster(internal),
  5.1529 -  FUNCTION 4 pgl_gist_decompress(internal),
  5.1530 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  5.1531 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  5.1532 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  5.1533 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  5.1534 -  STORAGE ekey_area;
  5.1535 -
  5.1536 -
  5.1537 ----------------------
  5.1538 --- alias functions --
  5.1539 ----------------------
  5.1540 -
  5.1541 -CREATE FUNCTION distance(epoint, epoint)
  5.1542 -  RETURNS float8
  5.1543 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  5.1544 -
  5.1545 -CREATE FUNCTION distance(ecluster, epoint)
  5.1546 -  RETURNS float8
  5.1547 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  5.1548 -
  5.1549 -CREATE FUNCTION distance_within(epoint, epoint, float8)
  5.1550 -  RETURNS boolean
  5.1551 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  5.1552 -
  5.1553 -CREATE FUNCTION distance_within(ecluster, epoint, float8)
  5.1554 -  RETURNS boolean
  5.1555 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  5.1556 -
  5.1557 -CREATE FUNCTION fair_distance(ecluster, epoint, int4 = 10000)
  5.1558 -  RETURNS float8
  5.1559 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <=> epoint_with_sample_count($2, $3)';
  5.1560 -
  5.1561 -
  5.1562 ---------------------------------
  5.1563 --- other data storage formats --
  5.1564 ---------------------------------
  5.1565 -
  5.1566 -CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint')
  5.1567 -  RETURNS epoint
  5.1568 -  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
  5.1569 -    DECLARE
  5.1570 -      "result" epoint;
  5.1571 -    BEGIN
  5.1572 -      IF $3 = 'epoint_lonlat' THEN
  5.1573 -        -- avoid dynamic command execution for better performance
  5.1574 -        RETURN epoint($2, $1);
  5.1575 -      END IF;
  5.1576 -      IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN
  5.1577 -        -- avoid dynamic command execution for better performance
  5.1578 -        RETURN epoint($1, $2);
  5.1579 -      END IF;
  5.1580 -      EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2;
  5.1581 -      RETURN "result";
  5.1582 -    END;
  5.1583 -  $$;
  5.1584 -
  5.1585 -CREATE FUNCTION GeoJSON_LinearRing_vertices(jsonb, text = 'epoint_lonlat')
  5.1586 -  RETURNS SETOF jsonb
  5.1587 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  5.1588 -    SELECT "result" FROM
  5.1589 -      ( SELECT jsonb_array_length($1) - 1 ) AS "lastindex_row" ("lastindex")
  5.1590 -      CROSS JOIN LATERAL jsonb_array_elements(
  5.1591 -        CASE WHEN
  5.1592 -          coords_to_epoint(
  5.1593 -            ($1->0->>0)::float8,
  5.1594 -            ($1->0->>1)::float8,
  5.1595 -            $2
  5.1596 -          ) = coords_to_epoint(
  5.1597 -            ($1->"lastindex"->>0)::float8,
  5.1598 -            ($1->"lastindex"->>1)::float8,
  5.1599 -            $2
  5.1600 -          )
  5.1601 -        THEN
  5.1602 -          $1 - "lastindex"
  5.1603 -        ELSE
  5.1604 -          $1
  5.1605 -        END
  5.1606 -      ) AS "result_row" ("result")
  5.1607 -  $$;
  5.1608 -
  5.1609 -CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat')
  5.1610 -  RETURNS epoint
  5.1611 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  5.1612 -    SELECT CASE
  5.1613 -    WHEN $1->>'type' = 'Point' THEN
  5.1614 -      coords_to_epoint(
  5.1615 -        ($1->'coordinates'->>0)::float8,
  5.1616 -        ($1->'coordinates'->>1)::float8,
  5.1617 -        $2
  5.1618 -      )
  5.1619 -    WHEN $1->>'type' = 'Feature' THEN
  5.1620 -      GeoJSON_to_epoint($1->'geometry', $2)
  5.1621 -    ELSE
  5.1622 -      NULL
  5.1623 -    END
  5.1624 -  $$;
  5.1625 -
  5.1626 -CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat')
  5.1627 -  RETURNS ecluster
  5.1628 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  5.1629 -    SELECT CASE $1->>'type'
  5.1630 -    WHEN 'Point' THEN
  5.1631 -      coords_to_epoint(
  5.1632 -        ($1->'coordinates'->>0)::float8,
  5.1633 -        ($1->'coordinates'->>1)::float8,
  5.1634 -        $2
  5.1635 -      )::ecluster
  5.1636 -    WHEN 'MultiPoint' THEN
  5.1637 -      ( SELECT ecluster_create_multipoint(array_agg(
  5.1638 -          coords_to_epoint(
  5.1639 -            ("coord"->>0)::float8,
  5.1640 -            ("coord"->>1)::float8,
  5.1641 -            $2
  5.1642 -          )
  5.1643 -        ))
  5.1644 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  5.1645 -      )
  5.1646 -    WHEN 'LineString' THEN
  5.1647 -      ( SELECT ecluster_create_path(array_agg(
  5.1648 -          coords_to_epoint(
  5.1649 -            ("coord"->>0)::float8,
  5.1650 -            ("coord"->>1)::float8,
  5.1651 -            $2
  5.1652 -          )
  5.1653 -        ))
  5.1654 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  5.1655 -      )
  5.1656 -    WHEN 'MultiLineString' THEN
  5.1657 -      ( SELECT ecluster_concat(array_agg(
  5.1658 -          ( SELECT ecluster_create_path(array_agg(
  5.1659 -              coords_to_epoint(
  5.1660 -                ("coord"->>0)::float8,
  5.1661 -                ("coord"->>1)::float8,
  5.1662 -                $2
  5.1663 -              )
  5.1664 -            ))
  5.1665 -            FROM jsonb_array_elements("coord_array") AS "coord"
  5.1666 -          )
  5.1667 -        ))
  5.1668 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  5.1669 -      )
  5.1670 -    WHEN 'Polygon' THEN
  5.1671 -      ( SELECT ecluster_concat(array_agg(
  5.1672 -          ( SELECT ecluster_create_polygon(array_agg(
  5.1673 -              coords_to_epoint(
  5.1674 -                ("coord"->>0)::float8,
  5.1675 -                ("coord"->>1)::float8,
  5.1676 -                $2
  5.1677 -              )
  5.1678 -            ))
  5.1679 -            FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord"
  5.1680 -          )
  5.1681 -        ))
  5.1682 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  5.1683 -      )
  5.1684 -    WHEN 'MultiPolygon' THEN
  5.1685 -      ( SELECT ecluster_concat(array_agg(
  5.1686 -          ( SELECT ecluster_concat(array_agg(
  5.1687 -              ( SELECT ecluster_create_polygon(array_agg(
  5.1688 -                  coords_to_epoint(
  5.1689 -                    ("coord"->>0)::float8,
  5.1690 -                    ("coord"->>1)::float8,
  5.1691 -                    $2
  5.1692 -                  )
  5.1693 -                ))
  5.1694 -                FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord"
  5.1695 -              )
  5.1696 -            ))
  5.1697 -            FROM jsonb_array_elements("coord_array_array") AS "coord_array"
  5.1698 -          )
  5.1699 -        ))
  5.1700 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array"
  5.1701 -      )
  5.1702 -    WHEN 'GeometryCollection' THEN
  5.1703 -      ( SELECT ecluster_concat(array_agg(
  5.1704 -          GeoJSON_to_ecluster("geometry", $2)
  5.1705 -        ))
  5.1706 -        FROM jsonb_array_elements($1->'geometries') AS "geometry"
  5.1707 -      )
  5.1708 -    WHEN 'Feature' THEN
  5.1709 -      GeoJSON_to_ecluster($1->'geometry', $2)
  5.1710 -    WHEN 'FeatureCollection' THEN
  5.1711 -      ( SELECT ecluster_concat(array_agg(
  5.1712 -          GeoJSON_to_ecluster("feature", $2)
  5.1713 -        ))
  5.1714 -        FROM jsonb_array_elements($1->'features') AS "feature"
  5.1715 -      )
  5.1716 -    ELSE
  5.1717 -      NULL
  5.1718 -    END
  5.1719 -  $$;
  5.1720 -
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/latlon--0.15.sql	Wed Feb 12 11:08:37 2020 +0100
     6.3 @@ -0,0 +1,1717 @@
     6.4 +
     6.5 +----------------------------------------
     6.6 +-- forward declarations (shell types) --
     6.7 +----------------------------------------
     6.8 +
     6.9 +CREATE TYPE ekey_point;
    6.10 +CREATE TYPE ekey_area;
    6.11 +CREATE TYPE epoint;
    6.12 +CREATE TYPE epoint_with_sample_count;
    6.13 +CREATE TYPE ebox;
    6.14 +CREATE TYPE ecircle;
    6.15 +CREATE TYPE ecluster;
    6.16 +
    6.17 +
    6.18 +------------------------------------------------------------
    6.19 +-- dummy input/output functions for dummy index key types --
    6.20 +------------------------------------------------------------
    6.21 +
    6.22 +CREATE FUNCTION ekey_point_in_dummy(cstring)
    6.23 +  RETURNS ekey_point
    6.24 +  LANGUAGE C IMMUTABLE STRICT
    6.25 +  AS '$libdir/latlon-v0009', 'pgl_notimpl';
    6.26 +
    6.27 +CREATE FUNCTION ekey_point_out_dummy(ekey_point)
    6.28 +  RETURNS cstring
    6.29 +  LANGUAGE C IMMUTABLE STRICT
    6.30 +  AS '$libdir/latlon-v0009', 'pgl_notimpl';
    6.31 +
    6.32 +CREATE FUNCTION ekey_area_in_dummy(cstring)
    6.33 +  RETURNS ekey_area
    6.34 +  LANGUAGE C IMMUTABLE STRICT
    6.35 +  AS '$libdir/latlon-v0009', 'pgl_notimpl';
    6.36 +
    6.37 +CREATE FUNCTION ekey_area_out_dummy(ekey_area)
    6.38 +  RETURNS cstring
    6.39 +  LANGUAGE C IMMUTABLE STRICT
    6.40 +  AS '$libdir/latlon-v0009', 'pgl_notimpl';
    6.41 +
    6.42 +
    6.43 +--------------------------
    6.44 +-- text input functions --
    6.45 +--------------------------
    6.46 +
    6.47 +CREATE FUNCTION epoint_in(cstring)
    6.48 +  RETURNS epoint
    6.49 +  LANGUAGE C IMMUTABLE STRICT
    6.50 +  AS '$libdir/latlon-v0009', 'pgl_epoint_in';
    6.51 +
    6.52 +CREATE FUNCTION epoint_with_sample_count_in(cstring)
    6.53 +  RETURNS epoint_with_sample_count
    6.54 +  LANGUAGE C IMMUTABLE STRICT
    6.55 +  AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_in';
    6.56 +
    6.57 +CREATE FUNCTION ebox_in(cstring)
    6.58 +  RETURNS ebox
    6.59 +  LANGUAGE C IMMUTABLE STRICT
    6.60 +  AS '$libdir/latlon-v0009', 'pgl_ebox_in';
    6.61 +
    6.62 +CREATE FUNCTION ecircle_in(cstring)
    6.63 +  RETURNS ecircle
    6.64 +  LANGUAGE C IMMUTABLE STRICT
    6.65 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_in';
    6.66 +
    6.67 +CREATE FUNCTION ecluster_in(cstring)
    6.68 +  RETURNS ecluster
    6.69 +  LANGUAGE C IMMUTABLE STRICT
    6.70 +  AS '$libdir/latlon-v0009', 'pgl_ecluster_in';
    6.71 +
    6.72 +
    6.73 +---------------------------
    6.74 +-- text output functions --
    6.75 +---------------------------
    6.76 +
    6.77 +CREATE FUNCTION epoint_out(epoint)
    6.78 +  RETURNS cstring
    6.79 +  LANGUAGE C IMMUTABLE STRICT
    6.80 +  AS '$libdir/latlon-v0009', 'pgl_epoint_out';
    6.81 +
    6.82 +CREATE FUNCTION epoint_with_sample_count_out(epoint_with_sample_count)
    6.83 +  RETURNS cstring
    6.84 +  LANGUAGE C IMMUTABLE STRICT
    6.85 +  AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_out';
    6.86 +
    6.87 +CREATE FUNCTION ebox_out(ebox)
    6.88 +  RETURNS cstring
    6.89 +  LANGUAGE C IMMUTABLE STRICT
    6.90 +  AS '$libdir/latlon-v0009', 'pgl_ebox_out';
    6.91 +
    6.92 +CREATE FUNCTION ecircle_out(ecircle)
    6.93 +  RETURNS cstring
    6.94 +  LANGUAGE C IMMUTABLE STRICT
    6.95 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_out';
    6.96 +
    6.97 +CREATE FUNCTION ecluster_out(ecluster)
    6.98 +  RETURNS cstring
    6.99 +  LANGUAGE C IMMUTABLE STRICT
   6.100 +  AS '$libdir/latlon-v0009', 'pgl_ecluster_out';
   6.101 +
   6.102 +
   6.103 +--------------------------
   6.104 +-- binary I/O functions --
   6.105 +--------------------------
   6.106 +
   6.107 +CREATE FUNCTION epoint_recv(internal)
   6.108 +  RETURNS epoint
   6.109 +  LANGUAGE C IMMUTABLE STRICT
   6.110 +  AS '$libdir/latlon-v0009', 'pgl_epoint_recv';
   6.111 +
   6.112 +CREATE FUNCTION ebox_recv(internal)
   6.113 +  RETURNS ebox
   6.114 +  LANGUAGE C IMMUTABLE STRICT
   6.115 +  AS '$libdir/latlon-v0009', 'pgl_ebox_recv';
   6.116 +
   6.117 +CREATE FUNCTION ecircle_recv(internal)
   6.118 +  RETURNS ecircle
   6.119 +  LANGUAGE C IMMUTABLE STRICT
   6.120 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_recv';
   6.121 +
   6.122 +CREATE FUNCTION epoint_send(epoint)
   6.123 +  RETURNS bytea
   6.124 +  LANGUAGE C IMMUTABLE STRICT
   6.125 +  AS '$libdir/latlon-v0009', 'pgl_epoint_send';
   6.126 +
   6.127 +CREATE FUNCTION ebox_send(ebox)
   6.128 +  RETURNS bytea
   6.129 +  LANGUAGE C IMMUTABLE STRICT
   6.130 +  AS '$libdir/latlon-v0009', 'pgl_ebox_send';
   6.131 +
   6.132 +CREATE FUNCTION ecircle_send(ecircle)
   6.133 +  RETURNS bytea
   6.134 +  LANGUAGE C IMMUTABLE STRICT
   6.135 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_send';
   6.136 +
   6.137 +
   6.138 +-----------------------------------------------
   6.139 +-- type definitions of dummy index key types --
   6.140 +-----------------------------------------------
   6.141 +
   6.142 +CREATE TYPE ekey_point (
   6.143 +  internallength = 8,
   6.144 +  input = ekey_point_in_dummy,
   6.145 +  output = ekey_point_out_dummy,
   6.146 +  alignment = char );
   6.147 +
   6.148 +CREATE TYPE ekey_area (
   6.149 +  internallength = 9,
   6.150 +  input = ekey_area_in_dummy,
   6.151 +  output = ekey_area_out_dummy,
   6.152 +  alignment = char );
   6.153 +
   6.154 +
   6.155 +------------------------------------------
   6.156 +-- definitions of geographic data types --
   6.157 +------------------------------------------
   6.158 +
   6.159 +CREATE TYPE epoint (
   6.160 +  internallength = 16,
   6.161 +  input = epoint_in,
   6.162 +  output = epoint_out,
   6.163 +  receive = epoint_recv,
   6.164 +  send = epoint_send,
   6.165 +  alignment = double );
   6.166 +
   6.167 +CREATE TYPE epoint_with_sample_count (
   6.168 +  internallength = 20,
   6.169 +  input = epoint_with_sample_count_in,
   6.170 +  output = epoint_with_sample_count_out,
   6.171 +  alignment = double );
   6.172 +
   6.173 +CREATE TYPE ebox (
   6.174 +  internallength = 32,
   6.175 +  input = ebox_in,
   6.176 +  output = ebox_out,
   6.177 +  receive = ebox_recv,
   6.178 +  send = ebox_send,
   6.179 +  alignment = double );
   6.180 +
   6.181 +CREATE TYPE ecircle (
   6.182 +  internallength = 24,
   6.183 +  input = ecircle_in,
   6.184 +  output = ecircle_out,
   6.185 +  receive = ecircle_recv,
   6.186 +  send = ecircle_send,
   6.187 +  alignment = double );
   6.188 +
   6.189 +CREATE TYPE ecluster (
   6.190 +  internallength = VARIABLE,
   6.191 +  input = ecluster_in,
   6.192 +  output = ecluster_out,
   6.193 +  alignment = double,
   6.194 +  storage = external );
   6.195 +
   6.196 +
   6.197 +--------------------
   6.198 +-- B-tree support --
   6.199 +--------------------
   6.200 +
   6.201 +-- begin of B-tree support for epoint
   6.202 +
   6.203 +CREATE FUNCTION epoint_btree_lt(epoint, epoint)
   6.204 +  RETURNS boolean
   6.205 +  LANGUAGE C IMMUTABLE STRICT
   6.206 +  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_lt';
   6.207 +
   6.208 +CREATE FUNCTION epoint_btree_le(epoint, epoint)
   6.209 +  RETURNS boolean
   6.210 +  LANGUAGE C IMMUTABLE STRICT
   6.211 +  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_le';
   6.212 +
   6.213 +CREATE FUNCTION epoint_btree_eq(epoint, epoint)
   6.214 +  RETURNS boolean
   6.215 +  LANGUAGE C IMMUTABLE STRICT
   6.216 +  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_eq';
   6.217 +
   6.218 +CREATE FUNCTION epoint_btree_ne(epoint, epoint)
   6.219 +  RETURNS boolean
   6.220 +  LANGUAGE C IMMUTABLE STRICT
   6.221 +  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ne';
   6.222 +
   6.223 +CREATE FUNCTION epoint_btree_ge(epoint, epoint)
   6.224 +  RETURNS boolean
   6.225 +  LANGUAGE C IMMUTABLE STRICT
   6.226 +  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ge';
   6.227 +
   6.228 +CREATE FUNCTION epoint_btree_gt(epoint, epoint)
   6.229 +  RETURNS boolean
   6.230 +  LANGUAGE C IMMUTABLE STRICT
   6.231 +  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_gt';
   6.232 +
   6.233 +CREATE OPERATOR <<< (
   6.234 +  leftarg = epoint,
   6.235 +  rightarg = epoint,
   6.236 +  procedure = epoint_btree_lt,
   6.237 +  commutator = >>>,
   6.238 +  negator = >>>=,
   6.239 +  restrict = scalarltsel,
   6.240 +  join = scalarltjoinsel
   6.241 +);
   6.242 +
   6.243 +CREATE OPERATOR <<<= (
   6.244 +  leftarg = epoint,
   6.245 +  rightarg = epoint,
   6.246 +  procedure = epoint_btree_le,
   6.247 +  commutator = >>>=,
   6.248 +  negator = >>>,
   6.249 +  restrict = scalarltsel,
   6.250 +  join = scalarltjoinsel
   6.251 +);
   6.252 +
   6.253 +CREATE OPERATOR = (
   6.254 +  leftarg = epoint,
   6.255 +  rightarg = epoint,
   6.256 +  procedure = epoint_btree_eq,
   6.257 +  commutator = =,
   6.258 +  negator = <>,
   6.259 +  restrict = eqsel,
   6.260 +  join = eqjoinsel,
   6.261 +  merges
   6.262 +);
   6.263 +
   6.264 +CREATE OPERATOR <> (
   6.265 +  leftarg = epoint,
   6.266 +  rightarg = epoint,
   6.267 +  procedure = epoint_btree_ne,
   6.268 +  commutator = <>,
   6.269 +  negator = =,
   6.270 +  restrict = neqsel,
   6.271 +  join = neqjoinsel
   6.272 +);
   6.273 +
   6.274 +CREATE OPERATOR >>>= (
   6.275 +  leftarg = epoint,
   6.276 +  rightarg = epoint,
   6.277 +  procedure = epoint_btree_ge,
   6.278 +  commutator = <<<=,
   6.279 +  negator = <<<,
   6.280 +  restrict = scalargtsel,
   6.281 +  join = scalargtjoinsel
   6.282 +);
   6.283 +
   6.284 +CREATE OPERATOR >>> (
   6.285 +  leftarg = epoint,
   6.286 +  rightarg = epoint,
   6.287 +  procedure = epoint_btree_gt,
   6.288 +  commutator = <<<,
   6.289 +  negator = <<<=,
   6.290 +  restrict = scalargtsel,
   6.291 +  join = scalargtjoinsel
   6.292 +);
   6.293 +
   6.294 +CREATE FUNCTION epoint_btree_cmp(epoint, epoint)
   6.295 +  RETURNS int4
   6.296 +  LANGUAGE C IMMUTABLE STRICT
   6.297 +  AS '$libdir/latlon-v0009', 'pgl_btree_epoint_cmp';
   6.298 +
   6.299 +CREATE OPERATOR CLASS epoint_btree_ops
   6.300 +  DEFAULT FOR TYPE epoint USING btree AS
   6.301 +  OPERATOR 1 <<< ,
   6.302 +  OPERATOR 2 <<<= ,
   6.303 +  OPERATOR 3 = ,
   6.304 +  OPERATOR 4 >>>= ,
   6.305 +  OPERATOR 5 >>> ,
   6.306 +  FUNCTION 1 epoint_btree_cmp(epoint, epoint);
   6.307 +
   6.308 +-- end of B-tree support for epoint
   6.309 +
   6.310 +-- begin of B-tree support for ebox
   6.311 +
   6.312 +CREATE FUNCTION ebox_btree_lt(ebox, ebox)
   6.313 +  RETURNS boolean
   6.314 +  LANGUAGE C IMMUTABLE STRICT
   6.315 +  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_lt';
   6.316 +
   6.317 +CREATE FUNCTION ebox_btree_le(ebox, ebox)
   6.318 +  RETURNS boolean
   6.319 +  LANGUAGE C IMMUTABLE STRICT
   6.320 +  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_le';
   6.321 +
   6.322 +CREATE FUNCTION ebox_btree_eq(ebox, ebox)
   6.323 +  RETURNS boolean
   6.324 +  LANGUAGE C IMMUTABLE STRICT
   6.325 +  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_eq';
   6.326 +
   6.327 +CREATE FUNCTION ebox_btree_ne(ebox, ebox)
   6.328 +  RETURNS boolean
   6.329 +  LANGUAGE C IMMUTABLE STRICT
   6.330 +  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ne';
   6.331 +
   6.332 +CREATE FUNCTION ebox_btree_ge(ebox, ebox)
   6.333 +  RETURNS boolean
   6.334 +  LANGUAGE C IMMUTABLE STRICT
   6.335 +  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ge';
   6.336 +
   6.337 +CREATE FUNCTION ebox_btree_gt(ebox, ebox)
   6.338 +  RETURNS boolean
   6.339 +  LANGUAGE C IMMUTABLE STRICT
   6.340 +  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_gt';
   6.341 +
   6.342 +CREATE OPERATOR <<< (
   6.343 +  leftarg = ebox,
   6.344 +  rightarg = ebox,
   6.345 +  procedure = ebox_btree_lt,
   6.346 +  commutator = >>>,
   6.347 +  negator = >>>=,
   6.348 +  restrict = scalarltsel,
   6.349 +  join = scalarltjoinsel
   6.350 +);
   6.351 +
   6.352 +CREATE OPERATOR <<<= (
   6.353 +  leftarg = ebox,
   6.354 +  rightarg = ebox,
   6.355 +  procedure = ebox_btree_le,
   6.356 +  commutator = >>>=,
   6.357 +  negator = >>>,
   6.358 +  restrict = scalarltsel,
   6.359 +  join = scalarltjoinsel
   6.360 +);
   6.361 +
   6.362 +CREATE OPERATOR = (
   6.363 +  leftarg = ebox,
   6.364 +  rightarg = ebox,
   6.365 +  procedure = ebox_btree_eq,
   6.366 +  commutator = =,
   6.367 +  negator = <>,
   6.368 +  restrict = eqsel,
   6.369 +  join = eqjoinsel,
   6.370 +  merges
   6.371 +);
   6.372 +
   6.373 +CREATE OPERATOR <> (
   6.374 +  leftarg = ebox,
   6.375 +  rightarg = ebox,
   6.376 +  procedure = ebox_btree_ne,
   6.377 +  commutator = <>,
   6.378 +  negator = =,
   6.379 +  restrict = neqsel,
   6.380 +  join = neqjoinsel
   6.381 +);
   6.382 +
   6.383 +CREATE OPERATOR >>>= (
   6.384 +  leftarg = ebox,
   6.385 +  rightarg = ebox,
   6.386 +  procedure = ebox_btree_ge,
   6.387 +  commutator = <<<=,
   6.388 +  negator = <<<,
   6.389 +  restrict = scalargtsel,
   6.390 +  join = scalargtjoinsel
   6.391 +);
   6.392 +
   6.393 +CREATE OPERATOR >>> (
   6.394 +  leftarg = ebox,
   6.395 +  rightarg = ebox,
   6.396 +  procedure = ebox_btree_gt,
   6.397 +  commutator = <<<,
   6.398 +  negator = <<<=,
   6.399 +  restrict = scalargtsel,
   6.400 +  join = scalargtjoinsel
   6.401 +);
   6.402 +
   6.403 +CREATE FUNCTION ebox_btree_cmp(ebox, ebox)
   6.404 +  RETURNS int4
   6.405 +  LANGUAGE C IMMUTABLE STRICT
   6.406 +  AS '$libdir/latlon-v0009', 'pgl_btree_ebox_cmp';
   6.407 +
   6.408 +CREATE OPERATOR CLASS ebox_btree_ops
   6.409 +  DEFAULT FOR TYPE ebox USING btree AS
   6.410 +  OPERATOR 1 <<< ,
   6.411 +  OPERATOR 2 <<<= ,
   6.412 +  OPERATOR 3 = ,
   6.413 +  OPERATOR 4 >>>= ,
   6.414 +  OPERATOR 5 >>> ,
   6.415 +  FUNCTION 1 ebox_btree_cmp(ebox, ebox);
   6.416 +
   6.417 +-- end of B-tree support for ebox
   6.418 +
   6.419 +-- begin of B-tree support for ecircle
   6.420 +
   6.421 +CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   6.422 +  RETURNS boolean
   6.423 +  LANGUAGE C IMMUTABLE STRICT
   6.424 +  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_lt';
   6.425 +
   6.426 +CREATE FUNCTION ecircle_btree_le(ecircle, ecircle)
   6.427 +  RETURNS boolean
   6.428 +  LANGUAGE C IMMUTABLE STRICT
   6.429 +  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_le';
   6.430 +
   6.431 +CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   6.432 +  RETURNS boolean
   6.433 +  LANGUAGE C IMMUTABLE STRICT
   6.434 +  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_eq';
   6.435 +
   6.436 +CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   6.437 +  RETURNS boolean
   6.438 +  LANGUAGE C IMMUTABLE STRICT
   6.439 +  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ne';
   6.440 +
   6.441 +CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   6.442 +  RETURNS boolean
   6.443 +  LANGUAGE C IMMUTABLE STRICT
   6.444 +  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ge';
   6.445 +
   6.446 +CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   6.447 +  RETURNS boolean
   6.448 +  LANGUAGE C IMMUTABLE STRICT
   6.449 +  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_gt';
   6.450 +
   6.451 +CREATE OPERATOR <<< (
   6.452 +  leftarg = ecircle,
   6.453 +  rightarg = ecircle,
   6.454 +  procedure = ecircle_btree_lt,
   6.455 +  commutator = >>>,
   6.456 +  negator = >>>=,
   6.457 +  restrict = scalarltsel,
   6.458 +  join = scalarltjoinsel
   6.459 +);
   6.460 +
   6.461 +CREATE OPERATOR <<<= (
   6.462 +  leftarg = ecircle,
   6.463 +  rightarg = ecircle,
   6.464 +  procedure = ecircle_btree_le,
   6.465 +  commutator = >>>=,
   6.466 +  negator = >>>,
   6.467 +  restrict = scalarltsel,
   6.468 +  join = scalarltjoinsel
   6.469 +);
   6.470 +
   6.471 +CREATE OPERATOR = (
   6.472 +  leftarg = ecircle,
   6.473 +  rightarg = ecircle,
   6.474 +  procedure = ecircle_btree_eq,
   6.475 +  commutator = =,
   6.476 +  negator = <>,
   6.477 +  restrict = eqsel,
   6.478 +  join = eqjoinsel,
   6.479 +  merges
   6.480 +);
   6.481 +
   6.482 +CREATE OPERATOR <> (
   6.483 +  leftarg = ecircle,
   6.484 +  rightarg = ecircle,
   6.485 +  procedure = ecircle_btree_ne,
   6.486 +  commutator = <>,
   6.487 +  negator = =,
   6.488 +  restrict = neqsel,
   6.489 +  join = neqjoinsel
   6.490 +);
   6.491 +
   6.492 +CREATE OPERATOR >>>= (
   6.493 +  leftarg = ecircle,
   6.494 +  rightarg = ecircle,
   6.495 +  procedure = ecircle_btree_ge,
   6.496 +  commutator = <<<=,
   6.497 +  negator = <<<,
   6.498 +  restrict = scalargtsel,
   6.499 +  join = scalargtjoinsel
   6.500 +);
   6.501 +
   6.502 +CREATE OPERATOR >>> (
   6.503 +  leftarg = ecircle,
   6.504 +  rightarg = ecircle,
   6.505 +  procedure = ecircle_btree_gt,
   6.506 +  commutator = <<<,
   6.507 +  negator = <<<=,
   6.508 +  restrict = scalargtsel,
   6.509 +  join = scalargtjoinsel
   6.510 +);
   6.511 +
   6.512 +CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   6.513 +  RETURNS int4
   6.514 +  LANGUAGE C IMMUTABLE STRICT
   6.515 +  AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_cmp';
   6.516 +
   6.517 +CREATE OPERATOR CLASS ecircle_btree_ops
   6.518 +  DEFAULT FOR TYPE ecircle USING btree AS
   6.519 +  OPERATOR 1 <<< ,
   6.520 +  OPERATOR 2 <<<= ,
   6.521 +  OPERATOR 3 = ,
   6.522 +  OPERATOR 4 >>>= ,
   6.523 +  OPERATOR 5 >>> ,
   6.524 +  FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle);
   6.525 +
   6.526 +-- end of B-tree support for ecircle
   6.527 +
   6.528 +
   6.529 +----------------
   6.530 +-- type casts --
   6.531 +----------------
   6.532 +
   6.533 +CREATE FUNCTION cast_epoint_to_ebox(epoint)
   6.534 +  RETURNS ebox
   6.535 +  LANGUAGE C IMMUTABLE STRICT
   6.536 +  AS '$libdir/latlon-v0009', 'pgl_epoint_to_ebox';
   6.537 +
   6.538 +CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint);
   6.539 +
   6.540 +CREATE FUNCTION cast_epoint_to_ecircle(epoint)
   6.541 +  RETURNS ecircle
   6.542 +  LANGUAGE C IMMUTABLE STRICT
   6.543 +  AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecircle';
   6.544 +
   6.545 +CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint);
   6.546 +
   6.547 +CREATE FUNCTION cast_epoint_to_ecluster(epoint)
   6.548 +  RETURNS ecluster
   6.549 +  LANGUAGE C IMMUTABLE STRICT
   6.550 +  AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecluster';
   6.551 +
   6.552 +CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint);
   6.553 +
   6.554 +CREATE FUNCTION cast_ebox_to_ecluster(ebox)
   6.555 +  RETURNS ecluster
   6.556 +  LANGUAGE C IMMUTABLE STRICT
   6.557 +  AS '$libdir/latlon-v0009', 'pgl_ebox_to_ecluster';
   6.558 +
   6.559 +CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox);
   6.560 +
   6.561 +
   6.562 +---------------------------
   6.563 +-- constructor functions --
   6.564 +---------------------------
   6.565 +
   6.566 +CREATE FUNCTION epoint(float8, float8)
   6.567 +  RETURNS epoint
   6.568 +  LANGUAGE C IMMUTABLE STRICT
   6.569 +  AS '$libdir/latlon-v0009', 'pgl_create_epoint';
   6.570 +
   6.571 +CREATE FUNCTION epoint_latlon(float8, float8)
   6.572 +  RETURNS epoint
   6.573 +  LANGUAGE SQL IMMUTABLE STRICT AS $$
   6.574 +    SELECT epoint($1, $2)
   6.575 +  $$;
   6.576 +
   6.577 +CREATE FUNCTION epoint_lonlat(float8, float8)
   6.578 +  RETURNS epoint
   6.579 +  LANGUAGE SQL IMMUTABLE STRICT AS $$
   6.580 +    SELECT epoint($2, $1)
   6.581 +  $$;
   6.582 +
   6.583 +CREATE FUNCTION epoint_with_sample_count(epoint, int4)
   6.584 +  RETURNS epoint_with_sample_count
   6.585 +  LANGUAGE C IMMUTABLE STRICT
   6.586 +  AS '$libdir/latlon-v0009', 'pgl_create_epoint_with_sample_count';
   6.587 +
   6.588 +CREATE FUNCTION empty_ebox()
   6.589 +  RETURNS ebox
   6.590 +  LANGUAGE C IMMUTABLE STRICT
   6.591 +  AS '$libdir/latlon-v0009', 'pgl_create_empty_ebox';
   6.592 +
   6.593 +CREATE FUNCTION ebox(float8, float8, float8, float8)
   6.594 +  RETURNS ebox
   6.595 +  LANGUAGE C IMMUTABLE STRICT
   6.596 +  AS '$libdir/latlon-v0009', 'pgl_create_ebox';
   6.597 +
   6.598 +CREATE FUNCTION ebox(epoint, epoint)
   6.599 +  RETURNS ebox
   6.600 +  LANGUAGE C IMMUTABLE STRICT
   6.601 +  AS '$libdir/latlon-v0009', 'pgl_create_ebox_from_epoints';
   6.602 +
   6.603 +CREATE FUNCTION ecircle(float8, float8, float8)
   6.604 +  RETURNS ecircle
   6.605 +  LANGUAGE C IMMUTABLE STRICT
   6.606 +  AS '$libdir/latlon-v0009', 'pgl_create_ecircle';
   6.607 +
   6.608 +CREATE FUNCTION ecircle(epoint, float8)
   6.609 +  RETURNS ecircle
   6.610 +  LANGUAGE C IMMUTABLE STRICT
   6.611 +  AS '$libdir/latlon-v0009', 'pgl_create_ecircle_from_epoint';
   6.612 +
   6.613 +CREATE FUNCTION ecluster_concat(ecluster[])
   6.614 +  RETURNS ecluster
   6.615 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.616 +    SELECT array_to_string($1, ' ')::ecluster
   6.617 +  $$;
   6.618 +
   6.619 +CREATE FUNCTION ecluster_concat(ecluster, ecluster)
   6.620 +  RETURNS ecluster
   6.621 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.622 +    SELECT ($1::text || ' ' || $2::text)::ecluster
   6.623 +  $$;
   6.624 +
   6.625 +CREATE FUNCTION ecluster_create_multipoint(epoint[])
   6.626 +  RETURNS ecluster
   6.627 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.628 +    SELECT
   6.629 +      array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster
   6.630 +    FROM unnest($1)
   6.631 +  $$;
   6.632 +
   6.633 +CREATE FUNCTION ecluster_create_path(epoint[])
   6.634 +  RETURNS ecluster
   6.635 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.636 +    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   6.637 +      ('path (' || array_to_string($1, ' ') || ')')::ecluster
   6.638 +    END
   6.639 +    FROM array_to_string($1, ' ') AS "str"
   6.640 +  $$;
   6.641 +
   6.642 +CREATE FUNCTION ecluster_create_outline(epoint[])
   6.643 +  RETURNS ecluster
   6.644 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.645 +    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   6.646 +      ('outline (' || array_to_string($1, ' ') || ')')::ecluster
   6.647 +    END
   6.648 +    FROM array_to_string($1, ' ') AS "str"
   6.649 +  $$;
   6.650 +
   6.651 +CREATE FUNCTION ecluster_create_polygon(epoint[])
   6.652 +  RETURNS ecluster
   6.653 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.654 +    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   6.655 +      ('polygon (' || array_to_string($1, ' ') || ')')::ecluster
   6.656 +    END
   6.657 +    FROM array_to_string($1, ' ') AS "str"
   6.658 +  $$;
   6.659 +
   6.660 +
   6.661 +----------------------
   6.662 +-- getter functions --
   6.663 +----------------------
   6.664 +
   6.665 +CREATE FUNCTION latitude(epoint)
   6.666 +  RETURNS float8
   6.667 +  LANGUAGE C IMMUTABLE STRICT
   6.668 +  AS '$libdir/latlon-v0009', 'pgl_epoint_lat';
   6.669 +
   6.670 +CREATE FUNCTION longitude(epoint)
   6.671 +  RETURNS float8
   6.672 +  LANGUAGE C IMMUTABLE STRICT
   6.673 +  AS '$libdir/latlon-v0009', 'pgl_epoint_lon';
   6.674 +
   6.675 +CREATE FUNCTION min_latitude(ebox)
   6.676 +  RETURNS float8
   6.677 +  LANGUAGE C IMMUTABLE STRICT
   6.678 +  AS '$libdir/latlon-v0009', 'pgl_ebox_lat_min';
   6.679 +
   6.680 +CREATE FUNCTION max_latitude(ebox)
   6.681 +  RETURNS float8
   6.682 +  LANGUAGE C IMMUTABLE STRICT
   6.683 +  AS '$libdir/latlon-v0009', 'pgl_ebox_lat_max';
   6.684 +
   6.685 +CREATE FUNCTION min_longitude(ebox)
   6.686 +  RETURNS float8
   6.687 +  LANGUAGE C IMMUTABLE STRICT
   6.688 +  AS '$libdir/latlon-v0009', 'pgl_ebox_lon_min';
   6.689 +
   6.690 +CREATE FUNCTION max_longitude(ebox)
   6.691 +  RETURNS float8
   6.692 +  LANGUAGE C IMMUTABLE STRICT
   6.693 +  AS '$libdir/latlon-v0009', 'pgl_ebox_lon_max';
   6.694 +
   6.695 +CREATE FUNCTION center(ecircle)
   6.696 +  RETURNS epoint
   6.697 +  LANGUAGE C IMMUTABLE STRICT
   6.698 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_center';
   6.699 +
   6.700 +CREATE FUNCTION radius(ecircle)
   6.701 +  RETURNS float8
   6.702 +  LANGUAGE C IMMUTABLE STRICT
   6.703 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_radius';
   6.704 +
   6.705 +CREATE FUNCTION ecluster_extract_points(ecluster)
   6.706 +  RETURNS SETOF epoint
   6.707 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.708 +    SELECT "match"[2]::epoint
   6.709 +    FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match"
   6.710 +  $$;
   6.711 +
   6.712 +CREATE FUNCTION ecluster_extract_paths(ecluster)
   6.713 +  RETURNS SETOF epoint[]
   6.714 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.715 +    SELECT (
   6.716 +      SELECT array_agg("m2"[1]::epoint)
   6.717 +      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   6.718 +    )
   6.719 +    FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1"
   6.720 +  $$;
   6.721 +
   6.722 +CREATE FUNCTION ecluster_extract_outlines(ecluster)
   6.723 +  RETURNS SETOF epoint[]
   6.724 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.725 +    SELECT (
   6.726 +      SELECT array_agg("m2"[1]::epoint)
   6.727 +      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   6.728 +    )
   6.729 +    FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1"
   6.730 +  $$;
   6.731 +
   6.732 +CREATE FUNCTION ecluster_extract_polygons(ecluster)
   6.733 +  RETURNS SETOF epoint[]
   6.734 +  LANGUAGE sql IMMUTABLE STRICT AS $$
   6.735 +    SELECT (
   6.736 +      SELECT array_agg("m2"[1]::epoint)
   6.737 +      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   6.738 +    )
   6.739 +    FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1"
   6.740 +  $$;
   6.741 +
   6.742 +
   6.743 +---------------
   6.744 +-- operators --
   6.745 +---------------
   6.746 +
   6.747 +CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   6.748 +  RETURNS boolean
   6.749 +  LANGUAGE C IMMUTABLE STRICT
   6.750 +  AS '$libdir/latlon-v0009', 'pgl_epoint_ebox_overlap';
   6.751 +
   6.752 +CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   6.753 +  RETURNS boolean
   6.754 +  LANGUAGE C IMMUTABLE STRICT
   6.755 +  AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_overlap';
   6.756 +
   6.757 +CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   6.758 +  RETURNS boolean
   6.759 +  LANGUAGE C IMMUTABLE STRICT
   6.760 +  AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_overlap';
   6.761 +
   6.762 +CREATE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
   6.763 +  RETURNS boolean
   6.764 +  LANGUAGE C IMMUTABLE STRICT
   6.765 +  AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_may_overlap';
   6.766 +
   6.767 +CREATE FUNCTION ebox_overlap_proc(ebox, ebox)
   6.768 +  RETURNS boolean
   6.769 +  LANGUAGE C IMMUTABLE STRICT
   6.770 +  AS '$libdir/latlon-v0009', 'pgl_ebox_overlap';
   6.771 +
   6.772 +CREATE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
   6.773 +  RETURNS boolean
   6.774 +  LANGUAGE C IMMUTABLE STRICT
   6.775 +  AS '$libdir/latlon-v0009', 'pgl_ebox_ecircle_may_overlap';
   6.776 +
   6.777 +CREATE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
   6.778 +  RETURNS boolean
   6.779 +  LANGUAGE C IMMUTABLE STRICT
   6.780 +  AS '$libdir/latlon-v0009', 'pgl_ebox_ecluster_may_overlap';
   6.781 +
   6.782 +CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   6.783 +  RETURNS boolean
   6.784 +  LANGUAGE C IMMUTABLE STRICT
   6.785 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_overlap';
   6.786 +
   6.787 +CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   6.788 +  RETURNS boolean
   6.789 +  LANGUAGE C IMMUTABLE STRICT
   6.790 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_overlap';
   6.791 +
   6.792 +CREATE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
   6.793 +  RETURNS boolean
   6.794 +  LANGUAGE C IMMUTABLE STRICT
   6.795 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_may_overlap';
   6.796 +
   6.797 +CREATE FUNCTION ecluster_overlap_proc(ecluster, ecluster)
   6.798 +  RETURNS boolean
   6.799 +  LANGUAGE C IMMUTABLE STRICT
   6.800 +  AS '$libdir/latlon-v0009', 'pgl_ecluster_overlap';
   6.801 +
   6.802 +CREATE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
   6.803 +  RETURNS boolean
   6.804 +  LANGUAGE C IMMUTABLE STRICT
   6.805 +  AS '$libdir/latlon-v0009', 'pgl_ecluster_may_overlap';
   6.806 +
   6.807 +CREATE FUNCTION ecluster_contains_proc(ecluster, ecluster)
   6.808 +  RETURNS boolean
   6.809 +  LANGUAGE C IMMUTABLE STRICT
   6.810 +  AS '$libdir/latlon-v0009', 'pgl_ecluster_contains';
   6.811 +
   6.812 +CREATE FUNCTION epoint_distance_proc(epoint, epoint)
   6.813 +  RETURNS float8
   6.814 +  LANGUAGE C IMMUTABLE STRICT
   6.815 +  AS '$libdir/latlon-v0009', 'pgl_epoint_distance';
   6.816 +
   6.817 +CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   6.818 +  RETURNS float8
   6.819 +  LANGUAGE C IMMUTABLE STRICT
   6.820 +  AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_distance';
   6.821 +
   6.822 +CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   6.823 +  RETURNS float8
   6.824 +  LANGUAGE C IMMUTABLE STRICT
   6.825 +  AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_distance';
   6.826 +
   6.827 +CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   6.828 +  RETURNS float8
   6.829 +  LANGUAGE C IMMUTABLE STRICT
   6.830 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_distance';
   6.831 +
   6.832 +CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   6.833 +  RETURNS float8
   6.834 +  LANGUAGE C IMMUTABLE STRICT
   6.835 +  AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_distance';
   6.836 +
   6.837 +CREATE FUNCTION ecluster_distance_proc(ecluster, ecluster)
   6.838 +  RETURNS float8
   6.839 +  LANGUAGE C IMMUTABLE STRICT
   6.840 +  AS '$libdir/latlon-v0009', 'pgl_ecluster_distance';
   6.841 +
   6.842 +CREATE FUNCTION fair_distance_operator_proc(ecluster, epoint_with_sample_count)
   6.843 +  RETURNS float8
   6.844 +  LANGUAGE C IMMUTABLE STRICT
   6.845 +  AS '$libdir/latlon-v0009', 'pgl_ecluster_epoint_sc_fair_distance';
   6.846 +
   6.847 +CREATE OPERATOR && (
   6.848 +  leftarg = epoint,
   6.849 +  rightarg = ebox,
   6.850 +  procedure = epoint_ebox_overlap_proc,
   6.851 +  commutator = &&,
   6.852 +  restrict = areasel,
   6.853 +  join = areajoinsel
   6.854 +);
   6.855 +
   6.856 +CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint)
   6.857 +  RETURNS boolean
   6.858 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   6.859 +
   6.860 +CREATE OPERATOR && (
   6.861 +  leftarg = ebox,
   6.862 +  rightarg = epoint,
   6.863 +  procedure = epoint_ebox_overlap_commutator,
   6.864 +  commutator = &&,
   6.865 +  restrict = areasel,
   6.866 +  join = areajoinsel
   6.867 +);
   6.868 +
   6.869 +CREATE OPERATOR && (
   6.870 +  leftarg = epoint,
   6.871 +  rightarg = ecircle,
   6.872 +  procedure = epoint_ecircle_overlap_proc,
   6.873 +  commutator = &&,
   6.874 +  restrict = areasel,
   6.875 +  join = areajoinsel
   6.876 +);
   6.877 +
   6.878 +CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint)
   6.879 +  RETURNS boolean
   6.880 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   6.881 +
   6.882 +CREATE OPERATOR && (
   6.883 +  leftarg = ecircle,
   6.884 +  rightarg = epoint,
   6.885 +  procedure = epoint_ecircle_overlap_commutator,
   6.886 +  commutator = &&,
   6.887 +  restrict = areasel,
   6.888 +  join = areajoinsel
   6.889 +);
   6.890 +
   6.891 +CREATE OPERATOR && (
   6.892 +  leftarg = epoint,
   6.893 +  rightarg = ecluster,
   6.894 +  procedure = epoint_ecluster_overlap_proc,
   6.895 +  commutator = &&,
   6.896 +  restrict = areasel,
   6.897 +  join = areajoinsel
   6.898 +);
   6.899 +
   6.900 +CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint)
   6.901 +  RETURNS boolean
   6.902 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   6.903 +
   6.904 +CREATE OPERATOR && (
   6.905 +  leftarg = ecluster,
   6.906 +  rightarg = epoint,
   6.907 +  procedure = epoint_ecluster_overlap_commutator,
   6.908 +  commutator = &&,
   6.909 +  restrict = areasel,
   6.910 +  join = areajoinsel
   6.911 +);
   6.912 +
   6.913 +CREATE OPERATOR && (
   6.914 +  leftarg = ebox,
   6.915 +  rightarg = ebox,
   6.916 +  procedure = ebox_overlap_proc,
   6.917 +  commutator = &&,
   6.918 +  restrict = areasel,
   6.919 +  join = areajoinsel
   6.920 +);
   6.921 +
   6.922 +CREATE OPERATOR && (
   6.923 +  leftarg = ecircle,
   6.924 +  rightarg = ecircle,
   6.925 +  procedure = ecircle_overlap_proc,
   6.926 +  commutator = &&,
   6.927 +  restrict = areasel,
   6.928 +  join = areajoinsel
   6.929 +);
   6.930 +
   6.931 +CREATE OPERATOR && (
   6.932 +  leftarg = ecircle,
   6.933 +  rightarg = ecluster,
   6.934 +  procedure = ecircle_ecluster_overlap_proc,
   6.935 +  commutator = &&,
   6.936 +  restrict = areasel,
   6.937 +  join = areajoinsel
   6.938 +);
   6.939 +
   6.940 +CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle)
   6.941 +  RETURNS boolean
   6.942 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   6.943 +
   6.944 +CREATE OPERATOR && (
   6.945 +  leftarg = ecluster,
   6.946 +  rightarg = ecircle,
   6.947 +  procedure = ecircle_ecluster_overlap_commutator,
   6.948 +  commutator = &&,
   6.949 +  restrict = areasel,
   6.950 +  join = areajoinsel
   6.951 +);
   6.952 +
   6.953 +CREATE OPERATOR && (
   6.954 +  leftarg = ecluster,
   6.955 +  rightarg = ecluster,
   6.956 +  procedure = ecluster_overlap_proc,
   6.957 +  commutator = &&,
   6.958 +  restrict = areasel,
   6.959 +  join = areajoinsel
   6.960 +);
   6.961 +
   6.962 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle)
   6.963 +  RETURNS boolean
   6.964 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   6.965 +
   6.966 +CREATE OPERATOR && (
   6.967 +  leftarg = ebox,
   6.968 +  rightarg = ecircle,
   6.969 +  procedure = ebox_ecircle_overlap_castwrap,
   6.970 +  commutator = &&,
   6.971 +  restrict = areasel,
   6.972 +  join = areajoinsel
   6.973 +);
   6.974 +
   6.975 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox)
   6.976 +  RETURNS boolean
   6.977 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
   6.978 +
   6.979 +CREATE OPERATOR && (
   6.980 +  leftarg = ecircle,
   6.981 +  rightarg = ebox,
   6.982 +  procedure = ebox_ecircle_overlap_castwrap,
   6.983 +  commutator = &&,
   6.984 +  restrict = areasel,
   6.985 +  join = areajoinsel
   6.986 +);
   6.987 +
   6.988 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster)
   6.989 +  RETURNS boolean
   6.990 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   6.991 +
   6.992 +CREATE OPERATOR && (
   6.993 +  leftarg = ebox,
   6.994 +  rightarg = ecluster,
   6.995 +  procedure = ebox_ecluster_overlap_castwrap,
   6.996 +  commutator = &&,
   6.997 +  restrict = areasel,
   6.998 +  join = areajoinsel
   6.999 +);
  6.1000 +
  6.1001 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox)
  6.1002 +  RETURNS boolean
  6.1003 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
  6.1004 +
  6.1005 +CREATE OPERATOR && (
  6.1006 +  leftarg = ecluster,
  6.1007 +  rightarg = ebox,
  6.1008 +  procedure = ebox_ecluster_overlap_castwrap,
  6.1009 +  commutator = &&,
  6.1010 +  restrict = areasel,
  6.1011 +  join = areajoinsel
  6.1012 +);
  6.1013 +
  6.1014 +CREATE OPERATOR &&+ (
  6.1015 +  leftarg = epoint,
  6.1016 +  rightarg = ecluster,
  6.1017 +  procedure = epoint_ecluster_may_overlap_proc,
  6.1018 +  commutator = &&+,
  6.1019 +  restrict = areasel,
  6.1020 +  join = areajoinsel
  6.1021 +);
  6.1022 +
  6.1023 +CREATE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint)
  6.1024 +  RETURNS boolean
  6.1025 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  6.1026 +
  6.1027 +CREATE OPERATOR &&+ (
  6.1028 +  leftarg = ecluster,
  6.1029 +  rightarg = epoint,
  6.1030 +  procedure = epoint_ecluster_may_overlap_commutator,
  6.1031 +  commutator = &&+,
  6.1032 +  restrict = areasel,
  6.1033 +  join = areajoinsel
  6.1034 +);
  6.1035 +
  6.1036 +CREATE OPERATOR &&+ (
  6.1037 +  leftarg = ebox,
  6.1038 +  rightarg = ecircle,
  6.1039 +  procedure = ebox_ecircle_may_overlap_proc,
  6.1040 +  commutator = &&+,
  6.1041 +  restrict = areasel,
  6.1042 +  join = areajoinsel
  6.1043 +);
  6.1044 +
  6.1045 +CREATE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox)
  6.1046 +  RETURNS boolean
  6.1047 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  6.1048 +
  6.1049 +CREATE OPERATOR &&+ (
  6.1050 +  leftarg = ecircle,
  6.1051 +  rightarg = ebox,
  6.1052 +  procedure = ebox_ecircle_may_overlap_commutator,
  6.1053 +  commutator = &&+,
  6.1054 +  restrict = areasel,
  6.1055 +  join = areajoinsel
  6.1056 +);
  6.1057 +
  6.1058 +CREATE OPERATOR &&+ (
  6.1059 +  leftarg = ebox,
  6.1060 +  rightarg = ecluster,
  6.1061 +  procedure = ebox_ecluster_may_overlap_proc,
  6.1062 +  commutator = &&+,
  6.1063 +  restrict = areasel,
  6.1064 +  join = areajoinsel
  6.1065 +);
  6.1066 +
  6.1067 +CREATE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox)
  6.1068 +  RETURNS boolean
  6.1069 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  6.1070 +
  6.1071 +CREATE OPERATOR &&+ (
  6.1072 +  leftarg = ecluster,
  6.1073 +  rightarg = ebox,
  6.1074 +  procedure = ebox_ecluster_may_overlap_commutator,
  6.1075 +  commutator = &&+,
  6.1076 +  restrict = areasel,
  6.1077 +  join = areajoinsel
  6.1078 +);
  6.1079 +
  6.1080 +CREATE OPERATOR &&+ (
  6.1081 +  leftarg = ecircle,
  6.1082 +  rightarg = ecluster,
  6.1083 +  procedure = ecircle_ecluster_may_overlap_proc,
  6.1084 +  commutator = &&+,
  6.1085 +  restrict = areasel,
  6.1086 +  join = areajoinsel
  6.1087 +);
  6.1088 +
  6.1089 +CREATE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle)
  6.1090 +  RETURNS boolean
  6.1091 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  6.1092 +
  6.1093 +CREATE OPERATOR &&+ (
  6.1094 +  leftarg = ecluster,
  6.1095 +  rightarg = ecircle,
  6.1096 +  procedure = ecircle_ecluster_may_overlap_commutator,
  6.1097 +  commutator = &&+,
  6.1098 +  restrict = areasel,
  6.1099 +  join = areajoinsel
  6.1100 +);
  6.1101 +
  6.1102 +CREATE OPERATOR &&+ (
  6.1103 +  leftarg = ecluster,
  6.1104 +  rightarg = ecluster,
  6.1105 +  procedure = ecluster_may_overlap_proc,
  6.1106 +  commutator = &&+,
  6.1107 +  restrict = areasel,
  6.1108 +  join = areajoinsel
  6.1109 +);
  6.1110 +
  6.1111 +CREATE OPERATOR @> (
  6.1112 +  leftarg = ebox,
  6.1113 +  rightarg = epoint,
  6.1114 +  procedure = epoint_ebox_overlap_commutator,
  6.1115 +  commutator = <@,
  6.1116 +  restrict = areasel,
  6.1117 +  join = areajoinsel
  6.1118 +);
  6.1119 +
  6.1120 +CREATE OPERATOR <@ (
  6.1121 +  leftarg = epoint,
  6.1122 +  rightarg = ebox,
  6.1123 +  procedure = epoint_ebox_overlap_proc,
  6.1124 +  commutator = @>,
  6.1125 +  restrict = areasel,
  6.1126 +  join = areajoinsel
  6.1127 +);
  6.1128 +
  6.1129 +CREATE OPERATOR @> (
  6.1130 +  leftarg = ecluster,
  6.1131 +  rightarg = epoint,
  6.1132 +  procedure = epoint_ecluster_overlap_commutator,
  6.1133 +  commutator = <@,
  6.1134 +  restrict = areasel,
  6.1135 +  join = areajoinsel
  6.1136 +);
  6.1137 +
  6.1138 +CREATE OPERATOR <@ (
  6.1139 +  leftarg = epoint,
  6.1140 +  rightarg = ecluster,
  6.1141 +  procedure = epoint_ecluster_overlap_proc,
  6.1142 +  commutator = @>,
  6.1143 +  restrict = areasel,
  6.1144 +  join = areajoinsel
  6.1145 +);
  6.1146 +
  6.1147 +CREATE OPERATOR @> (
  6.1148 +  leftarg = ecluster,
  6.1149 +  rightarg = ecluster,
  6.1150 +  procedure = ecluster_contains_proc,
  6.1151 +  commutator = <@,
  6.1152 +  restrict = areasel,
  6.1153 +  join = areajoinsel
  6.1154 +);
  6.1155 +
  6.1156 +CREATE FUNCTION ecluster_contains_commutator(ecluster, ecluster)
  6.1157 +  RETURNS boolean
  6.1158 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1';
  6.1159 +
  6.1160 +CREATE OPERATOR <@ (
  6.1161 +  leftarg = ecluster,
  6.1162 +  rightarg = ecluster,
  6.1163 +  procedure = ecluster_contains_commutator,
  6.1164 +  commutator = @>,
  6.1165 +  restrict = areasel,
  6.1166 +  join = areajoinsel
  6.1167 +);
  6.1168 +
  6.1169 +CREATE FUNCTION ebox_contains_castwrap(ebox, ebox)
  6.1170 +  RETURNS boolean
  6.1171 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2::ecluster';
  6.1172 +
  6.1173 +CREATE OPERATOR @> (
  6.1174 +  leftarg = ebox,
  6.1175 +  rightarg = ebox,
  6.1176 +  procedure = ebox_contains_castwrap,
  6.1177 +  commutator = <@,
  6.1178 +  restrict = areasel,
  6.1179 +  join = areajoinsel
  6.1180 +);
  6.1181 +
  6.1182 +CREATE FUNCTION ebox_contains_swapped_castwrap(ebox, ebox)
  6.1183 +  RETURNS boolean
  6.1184 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1::ecluster';
  6.1185 +
  6.1186 +CREATE OPERATOR <@ (
  6.1187 +  leftarg = ebox,
  6.1188 +  rightarg = ebox,
  6.1189 +  procedure = ebox_contains_swapped_castwrap,
  6.1190 +  commutator = @>,
  6.1191 +  restrict = areasel,
  6.1192 +  join = areajoinsel
  6.1193 +);
  6.1194 +
  6.1195 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster)
  6.1196 +  RETURNS boolean
  6.1197 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2';
  6.1198 +
  6.1199 +CREATE OPERATOR @> (
  6.1200 +  leftarg = ebox,
  6.1201 +  rightarg = ecluster,
  6.1202 +  procedure = ebox_ecluster_contains_castwrap,
  6.1203 +  commutator = <@,
  6.1204 +  restrict = areasel,
  6.1205 +  join = areajoinsel
  6.1206 +);
  6.1207 +
  6.1208 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox)
  6.1209 +  RETURNS boolean
  6.1210 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1';
  6.1211 +
  6.1212 +CREATE OPERATOR <@ (
  6.1213 +  leftarg = ecluster,
  6.1214 +  rightarg = ebox,
  6.1215 +  procedure = ebox_ecluster_contains_castwrap,
  6.1216 +  commutator = @>,
  6.1217 +  restrict = areasel,
  6.1218 +  join = areajoinsel
  6.1219 +);
  6.1220 +
  6.1221 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox)
  6.1222 +  RETURNS boolean
  6.1223 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 @> $2::ecluster';
  6.1224 +
  6.1225 +CREATE OPERATOR @> (
  6.1226 +  leftarg = ecluster,
  6.1227 +  rightarg = ebox,
  6.1228 +  procedure = ecluster_ebox_contains_castwrap,
  6.1229 +  commutator = <@,
  6.1230 +  restrict = areasel,
  6.1231 +  join = areajoinsel
  6.1232 +);
  6.1233 +
  6.1234 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster)
  6.1235 +  RETURNS boolean
  6.1236 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1::ecluster';
  6.1237 +
  6.1238 +CREATE OPERATOR <@ (
  6.1239 +  leftarg = ebox,
  6.1240 +  rightarg = ecluster,
  6.1241 +  procedure = ecluster_ebox_contains_castwrap,
  6.1242 +  commutator = @>,
  6.1243 +  restrict = areasel,
  6.1244 +  join = areajoinsel
  6.1245 +);
  6.1246 +
  6.1247 +CREATE OPERATOR <-> (
  6.1248 +  leftarg = epoint,
  6.1249 +  rightarg = epoint,
  6.1250 +  procedure = epoint_distance_proc,
  6.1251 +  commutator = <->
  6.1252 +);
  6.1253 +
  6.1254 +CREATE OPERATOR <-> (
  6.1255 +  leftarg = epoint,
  6.1256 +  rightarg = ecircle,
  6.1257 +  procedure = epoint_ecircle_distance_proc,
  6.1258 +  commutator = <->
  6.1259 +);
  6.1260 +
  6.1261 +CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint)
  6.1262 +  RETURNS float8
  6.1263 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  6.1264 +
  6.1265 +CREATE OPERATOR <-> (
  6.1266 +  leftarg = ecircle,
  6.1267 +  rightarg = epoint,
  6.1268 +  procedure = epoint_ecircle_distance_commutator,
  6.1269 +  commutator = <->
  6.1270 +);
  6.1271 +
  6.1272 +CREATE OPERATOR <-> (
  6.1273 +  leftarg = epoint,
  6.1274 +  rightarg = ecluster,
  6.1275 +  procedure = epoint_ecluster_distance_proc,
  6.1276 +  commutator = <->
  6.1277 +);
  6.1278 +
  6.1279 +CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint)
  6.1280 +  RETURNS float8
  6.1281 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  6.1282 +
  6.1283 +CREATE OPERATOR <-> (
  6.1284 +  leftarg = ecluster,
  6.1285 +  rightarg = epoint,
  6.1286 +  procedure = epoint_ecluster_distance_commutator,
  6.1287 +  commutator = <->
  6.1288 +);
  6.1289 +
  6.1290 +CREATE OPERATOR <-> (
  6.1291 +  leftarg = ecircle,
  6.1292 +  rightarg = ecircle,
  6.1293 +  procedure = ecircle_distance_proc,
  6.1294 +  commutator = <->
  6.1295 +);
  6.1296 +
  6.1297 +CREATE OPERATOR <-> (
  6.1298 +  leftarg = ecircle,
  6.1299 +  rightarg = ecluster,
  6.1300 +  procedure = ecircle_ecluster_distance_proc,
  6.1301 +  commutator = <->
  6.1302 +);
  6.1303 +
  6.1304 +CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle)
  6.1305 +  RETURNS float8
  6.1306 +  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  6.1307 +
  6.1308 +CREATE OPERATOR <-> (
  6.1309 +  leftarg = ecluster,
  6.1310 +  rightarg = ecircle,
  6.1311 +  procedure = ecircle_ecluster_distance_commutator,
  6.1312 +  commutator = <->
  6.1313 +);
  6.1314 +
  6.1315 +CREATE OPERATOR <-> (
  6.1316 +  leftarg = ecluster,
  6.1317 +  rightarg = ecluster,
  6.1318 +  procedure = ecluster_distance_proc,
  6.1319 +  commutator = <->
  6.1320 +);
  6.1321 +
  6.1322 +CREATE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox)
  6.1323 +  RETURNS float8
  6.1324 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  6.1325 +
  6.1326 +CREATE OPERATOR <-> (
  6.1327 +  leftarg = epoint,
  6.1328 +  rightarg = ebox,
  6.1329 +  procedure = epoint_ebox_distance_castwrap,
  6.1330 +  commutator = <->
  6.1331 +);
  6.1332 +
  6.1333 +CREATE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint)
  6.1334 +  RETURNS float8
  6.1335 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  6.1336 +
  6.1337 +CREATE OPERATOR <-> (
  6.1338 +  leftarg = ebox,
  6.1339 +  rightarg = epoint,
  6.1340 +  procedure = epoint_ebox_distance_castwrap,
  6.1341 +  commutator = <->
  6.1342 +);
  6.1343 +
  6.1344 +CREATE FUNCTION ebox_distance_castwrap(ebox, ebox)
  6.1345 +  RETURNS float8
  6.1346 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2::ecluster';
  6.1347 +
  6.1348 +CREATE OPERATOR <-> (
  6.1349 +  leftarg = ebox,
  6.1350 +  rightarg = ebox,
  6.1351 +  procedure = ebox_distance_castwrap,
  6.1352 +  commutator = <->
  6.1353 +);
  6.1354 +
  6.1355 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle)
  6.1356 +  RETURNS float8
  6.1357 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  6.1358 +
  6.1359 +CREATE OPERATOR <-> (
  6.1360 +  leftarg = ebox,
  6.1361 +  rightarg = ecircle,
  6.1362 +  procedure = ebox_ecircle_distance_castwrap,
  6.1363 +  commutator = <->
  6.1364 +);
  6.1365 +
  6.1366 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox)
  6.1367 +  RETURNS float8
  6.1368 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  6.1369 +
  6.1370 +CREATE OPERATOR <-> (
  6.1371 +  leftarg = ecircle,
  6.1372 +  rightarg = ebox,
  6.1373 +  procedure = ebox_ecircle_distance_castwrap,
  6.1374 +  commutator = <->
  6.1375 +);
  6.1376 +
  6.1377 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster)
  6.1378 +  RETURNS float8
  6.1379 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  6.1380 +
  6.1381 +CREATE OPERATOR <-> (
  6.1382 +  leftarg = ebox,
  6.1383 +  rightarg = ecluster,
  6.1384 +  procedure = ebox_ecluster_distance_castwrap,
  6.1385 +  commutator = <->
  6.1386 +);
  6.1387 +
  6.1388 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox)
  6.1389 +  RETURNS float8
  6.1390 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  6.1391 +
  6.1392 +CREATE OPERATOR <-> (
  6.1393 +  leftarg = ecluster,
  6.1394 +  rightarg = ebox,
  6.1395 +  procedure = ebox_ecluster_distance_castwrap,
  6.1396 +  commutator = <->
  6.1397 +);
  6.1398 +
  6.1399 +CREATE OPERATOR <=> (
  6.1400 +  leftarg = ecluster,
  6.1401 +  rightarg = epoint_with_sample_count,
  6.1402 +  procedure = fair_distance_operator_proc
  6.1403 +);
  6.1404 +
  6.1405 +
  6.1406 +----------------
  6.1407 +-- GiST index --
  6.1408 +----------------
  6.1409 +
  6.1410 +CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
  6.1411 +  RETURNS boolean
  6.1412 +  LANGUAGE C STRICT
  6.1413 +  AS '$libdir/latlon-v0009', 'pgl_gist_consistent';
  6.1414 +
  6.1415 +CREATE FUNCTION pgl_gist_union(internal, internal)
  6.1416 +  RETURNS internal
  6.1417 +  LANGUAGE C STRICT
  6.1418 +  AS '$libdir/latlon-v0009', 'pgl_gist_union';
  6.1419 +
  6.1420 +CREATE FUNCTION pgl_gist_compress_epoint(internal)
  6.1421 +  RETURNS internal
  6.1422 +  LANGUAGE C STRICT
  6.1423 +  AS '$libdir/latlon-v0009', 'pgl_gist_compress_epoint';
  6.1424 +
  6.1425 +CREATE FUNCTION pgl_gist_compress_ecircle(internal)
  6.1426 +  RETURNS internal
  6.1427 +  LANGUAGE C STRICT
  6.1428 +  AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecircle';
  6.1429 +
  6.1430 +CREATE FUNCTION pgl_gist_compress_ecluster(internal)
  6.1431 +  RETURNS internal
  6.1432 +  LANGUAGE C STRICT
  6.1433 +  AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecluster';
  6.1434 +
  6.1435 +CREATE FUNCTION pgl_gist_decompress(internal)
  6.1436 +  RETURNS internal
  6.1437 +  LANGUAGE C STRICT
  6.1438 +  AS '$libdir/latlon-v0009', 'pgl_gist_decompress';
  6.1439 +
  6.1440 +CREATE FUNCTION pgl_gist_penalty(internal, internal, internal)
  6.1441 +  RETURNS internal
  6.1442 +  LANGUAGE C STRICT
  6.1443 +  AS '$libdir/latlon-v0009', 'pgl_gist_penalty';
  6.1444 +
  6.1445 +CREATE FUNCTION pgl_gist_picksplit(internal, internal)
  6.1446 +  RETURNS internal
  6.1447 +  LANGUAGE C STRICT
  6.1448 +  AS '$libdir/latlon-v0009', 'pgl_gist_picksplit';
  6.1449 +
  6.1450 +CREATE FUNCTION pgl_gist_same(internal, internal, internal)
  6.1451 +  RETURNS internal
  6.1452 +  LANGUAGE C STRICT
  6.1453 +  AS '$libdir/latlon-v0009', 'pgl_gist_same';
  6.1454 +
  6.1455 +CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
  6.1456 +  RETURNS internal
  6.1457 +  LANGUAGE C STRICT
  6.1458 +  AS '$libdir/latlon-v0009', 'pgl_gist_distance';
  6.1459 +
  6.1460 +CREATE OPERATOR CLASS epoint_ops
  6.1461 +  DEFAULT FOR TYPE epoint USING gist AS
  6.1462 +  OPERATOR  11 = ,
  6.1463 +  OPERATOR  22 &&  (epoint, ebox),
  6.1464 +  OPERATOR 222 <@  (epoint, ebox),
  6.1465 +  OPERATOR  23 &&  (epoint, ecircle),
  6.1466 +  OPERATOR  24 &&  (epoint, ecluster),
  6.1467 +  OPERATOR 124 &&+ (epoint, ecluster),
  6.1468 +  OPERATOR 224 <@  (epoint, ecluster),
  6.1469 +  OPERATOR  31 <-> (epoint, epoint) FOR ORDER BY float_ops,
  6.1470 +  OPERATOR  32 <-> (epoint, ebox) FOR ORDER BY float_ops,
  6.1471 +  OPERATOR  33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
  6.1472 +  OPERATOR  34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
  6.1473 +  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  6.1474 +  FUNCTION 2 pgl_gist_union(internal, internal),
  6.1475 +  FUNCTION 3 pgl_gist_compress_epoint(internal),
  6.1476 +  FUNCTION 4 pgl_gist_decompress(internal),
  6.1477 +  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  6.1478 +  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  6.1479 +  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  6.1480 +  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  6.1481 +  STORAGE ekey_point;
  6.1482 +
  6.1483 +CREATE OPERATOR CLASS ecircle_ops
  6.1484 +  DEFAULT FOR TYPE ecircle USING gist AS
  6.1485 +  OPERATOR  13 = ,
  6.1486 +  OPERATOR  21 &&  (ecircle, epoint),
  6.1487 +  OPERATOR  22 &&  (ecircle, ebox),
  6.1488 +  OPERATOR 122 &&+ (ecircle, ebox),
  6.1489 +  OPERATOR  23 &&  (ecircle, ecircle),
  6.1490 +  OPERATOR  24 &&  (ecircle, ecluster),
  6.1491 +  OPERATOR 124 &&+ (ecircle, ecluster),
  6.1492 +  OPERATOR  31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
  6.1493 +  OPERATOR  32 <-> (ecircle, ebox) FOR ORDER BY float_ops,
  6.1494 +  OPERATOR  33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
  6.1495 +  OPERATOR  34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
  6.1496 +  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  6.1497 +  FUNCTION 2 pgl_gist_union(internal, internal),
  6.1498 +  FUNCTION 3 pgl_gist_compress_ecircle(internal),
  6.1499 +  FUNCTION 4 pgl_gist_decompress(internal),
  6.1500 +  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  6.1501 +  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  6.1502 +  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  6.1503 +  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  6.1504 +  STORAGE ekey_area;
  6.1505 +
  6.1506 +CREATE OPERATOR CLASS ecluster_ops
  6.1507 +  DEFAULT FOR TYPE ecluster USING gist AS
  6.1508 +  OPERATOR  21 &&  (ecluster, epoint),
  6.1509 +  OPERATOR 121 &&+ (ecluster, epoint),
  6.1510 +  OPERATOR 221 @>  (ecluster, epoint),
  6.1511 +  OPERATOR  22 &&  (ecluster, ebox),
  6.1512 +  OPERATOR 122 &&+ (ecluster, ebox),
  6.1513 +  OPERATOR 222 @>  (ecluster, ebox),
  6.1514 +  OPERATOR 322 <@  (ecluster, ebox),
  6.1515 +  OPERATOR  23 &&  (ecluster, ecircle),
  6.1516 +  OPERATOR 123 &&+ (ecluster, ecircle),
  6.1517 +  OPERATOR  24 &&  (ecluster, ecluster),
  6.1518 +  OPERATOR 124 &&+ (ecluster, ecluster),
  6.1519 +  OPERATOR 224 @>  (ecluster, ecluster),
  6.1520 +  OPERATOR 324 <@  (ecluster, ecluster),
  6.1521 +  OPERATOR  31 <-> (ecluster, epoint) FOR ORDER BY float_ops,
  6.1522 +  OPERATOR  32 <-> (ecluster, ebox) FOR ORDER BY float_ops,
  6.1523 +  OPERATOR  33 <-> (ecluster, ecircle) FOR ORDER BY float_ops,
  6.1524 +  OPERATOR  34 <-> (ecluster, ecluster) FOR ORDER BY float_ops,
  6.1525 +  OPERATOR 131 <=> (ecluster, epoint_with_sample_count) FOR ORDER BY float_ops,
  6.1526 +  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  6.1527 +  FUNCTION 2 pgl_gist_union(internal, internal),
  6.1528 +  FUNCTION 3 pgl_gist_compress_ecluster(internal),
  6.1529 +  FUNCTION 4 pgl_gist_decompress(internal),
  6.1530 +  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  6.1531 +  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  6.1532 +  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  6.1533 +  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  6.1534 +  STORAGE ekey_area;
  6.1535 +
  6.1536 +
  6.1537 +---------------------
  6.1538 +-- alias functions --
  6.1539 +---------------------
  6.1540 +
  6.1541 +CREATE FUNCTION distance(epoint, epoint)
  6.1542 +  RETURNS float8
  6.1543 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  6.1544 +
  6.1545 +CREATE FUNCTION distance(ecluster, epoint)
  6.1546 +  RETURNS float8
  6.1547 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  6.1548 +
  6.1549 +CREATE FUNCTION distance_within(epoint, epoint, float8)
  6.1550 +  RETURNS boolean
  6.1551 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  6.1552 +
  6.1553 +CREATE FUNCTION distance_within(ecluster, epoint, float8)
  6.1554 +  RETURNS boolean
  6.1555 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  6.1556 +
  6.1557 +CREATE FUNCTION fair_distance(ecluster, epoint, int4 = 10000)
  6.1558 +  RETURNS float8
  6.1559 +  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <=> epoint_with_sample_count($2, $3)';
  6.1560 +
  6.1561 +
  6.1562 +--------------------------------
  6.1563 +-- other data storage formats --
  6.1564 +--------------------------------
  6.1565 +
  6.1566 +CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint')
  6.1567 +  RETURNS epoint
  6.1568 +  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
  6.1569 +    DECLARE
  6.1570 +      "result" epoint;
  6.1571 +    BEGIN
  6.1572 +      IF $3 = 'epoint_lonlat' THEN
  6.1573 +        -- avoid dynamic command execution for better performance
  6.1574 +        RETURN epoint($2, $1);
  6.1575 +      END IF;
  6.1576 +      IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN
  6.1577 +        -- avoid dynamic command execution for better performance
  6.1578 +        RETURN epoint($1, $2);
  6.1579 +      END IF;
  6.1580 +      EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2;
  6.1581 +      RETURN "result";
  6.1582 +    END;
  6.1583 +  $$;
  6.1584 +
  6.1585 +CREATE FUNCTION GeoJSON_LinearRing_vertices(jsonb, text = 'epoint_lonlat')
  6.1586 +  RETURNS SETOF jsonb
  6.1587 +  LANGUAGE sql IMMUTABLE STRICT AS $$
  6.1588 +    SELECT "result" FROM
  6.1589 +      ( SELECT jsonb_array_length($1) - 1 ) AS "lastindex_row" ("lastindex")
  6.1590 +      CROSS JOIN LATERAL jsonb_array_elements(
  6.1591 +        CASE WHEN
  6.1592 +          coords_to_epoint(
  6.1593 +            ($1->0->>0)::float8,
  6.1594 +            ($1->0->>1)::float8,
  6.1595 +            $2
  6.1596 +          ) = coords_to_epoint(
  6.1597 +            ($1->"lastindex"->>0)::float8,
  6.1598 +            ($1->"lastindex"->>1)::float8,
  6.1599 +            $2
  6.1600 +          )
  6.1601 +        THEN
  6.1602 +          $1 - "lastindex"
  6.1603 +        ELSE
  6.1604 +          $1
  6.1605 +        END
  6.1606 +      ) AS "result_row" ("result")
  6.1607 +  $$;
  6.1608 +
  6.1609 +CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat')
  6.1610 +  RETURNS epoint
  6.1611 +  LANGUAGE sql IMMUTABLE STRICT AS $$
  6.1612 +    SELECT CASE
  6.1613 +    WHEN $1->>'type' = 'Point' THEN
  6.1614 +      coords_to_epoint(
  6.1615 +        ($1->'coordinates'->>0)::float8,
  6.1616 +        ($1->'coordinates'->>1)::float8,
  6.1617 +        $2
  6.1618 +      )
  6.1619 +    WHEN $1->>'type' = 'Feature' THEN
  6.1620 +      GeoJSON_to_epoint($1->'geometry', $2)
  6.1621 +    ELSE
  6.1622 +      NULL
  6.1623 +    END
  6.1624 +  $$;
  6.1625 +
  6.1626 +CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat')
  6.1627 +  RETURNS ecluster
  6.1628 +  LANGUAGE sql IMMUTABLE STRICT AS $$
  6.1629 +    SELECT CASE $1->>'type'
  6.1630 +    WHEN 'Point' THEN
  6.1631 +      coords_to_epoint(
  6.1632 +        ($1->'coordinates'->>0)::float8,
  6.1633 +        ($1->'coordinates'->>1)::float8,
  6.1634 +        $2
  6.1635 +      )::ecluster
  6.1636 +    WHEN 'MultiPoint' THEN
  6.1637 +      ( SELECT ecluster_create_multipoint(array_agg(
  6.1638 +          coords_to_epoint(
  6.1639 +            ("coord"->>0)::float8,
  6.1640 +            ("coord"->>1)::float8,
  6.1641 +            $2
  6.1642 +          )
  6.1643 +        ))
  6.1644 +        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  6.1645 +      )
  6.1646 +    WHEN 'LineString' THEN
  6.1647 +      ( SELECT ecluster_create_path(array_agg(
  6.1648 +          coords_to_epoint(
  6.1649 +            ("coord"->>0)::float8,
  6.1650 +            ("coord"->>1)::float8,
  6.1651 +            $2
  6.1652 +          )
  6.1653 +        ))
  6.1654 +        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  6.1655 +      )
  6.1656 +    WHEN 'MultiLineString' THEN
  6.1657 +      ( SELECT ecluster_concat(array_agg(
  6.1658 +          ( SELECT ecluster_create_path(array_agg(
  6.1659 +              coords_to_epoint(
  6.1660 +                ("coord"->>0)::float8,
  6.1661 +                ("coord"->>1)::float8,
  6.1662 +                $2
  6.1663 +              )
  6.1664 +            ))
  6.1665 +            FROM jsonb_array_elements("coord_array") AS "coord"
  6.1666 +          )
  6.1667 +        ))
  6.1668 +        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  6.1669 +      )
  6.1670 +    WHEN 'Polygon' THEN
  6.1671 +      ( SELECT ecluster_concat(array_agg(
  6.1672 +          ( SELECT ecluster_create_polygon(array_agg(
  6.1673 +              coords_to_epoint(
  6.1674 +                ("coord"->>0)::float8,
  6.1675 +                ("coord"->>1)::float8,
  6.1676 +                $2
  6.1677 +              )
  6.1678 +            ))
  6.1679 +            FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord"
  6.1680 +          )
  6.1681 +        ))
  6.1682 +        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  6.1683 +      )
  6.1684 +    WHEN 'MultiPolygon' THEN
  6.1685 +      ( SELECT ecluster_concat(array_agg(
  6.1686 +          ( SELECT ecluster_concat(array_agg(
  6.1687 +              ( SELECT ecluster_create_polygon(array_agg(
  6.1688 +                  coords_to_epoint(
  6.1689 +                    ("coord"->>0)::float8,
  6.1690 +                    ("coord"->>1)::float8,
  6.1691 +                    $2
  6.1692 +                  )
  6.1693 +                ))
  6.1694 +                FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord"
  6.1695 +              )
  6.1696 +            ))
  6.1697 +            FROM jsonb_array_elements("coord_array_array") AS "coord_array"
  6.1698 +          )
  6.1699 +        ))
  6.1700 +        FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array"
  6.1701 +      )
  6.1702 +    WHEN 'GeometryCollection' THEN
  6.1703 +      ( SELECT ecluster_concat(array_agg(
  6.1704 +          GeoJSON_to_ecluster("geometry", $2)
  6.1705 +        ))
  6.1706 +        FROM jsonb_array_elements($1->'geometries') AS "geometry"
  6.1707 +      )
  6.1708 +    WHEN 'Feature' THEN
  6.1709 +      GeoJSON_to_ecluster($1->'geometry', $2)
  6.1710 +    WHEN 'FeatureCollection' THEN
  6.1711 +      ( SELECT ecluster_concat(array_agg(
  6.1712 +          GeoJSON_to_ecluster("feature", $2)
  6.1713 +        ))
  6.1714 +        FROM jsonb_array_elements($1->'features') AS "feature"
  6.1715 +      )
  6.1716 +    ELSE
  6.1717 +      NULL
  6.1718 +    END
  6.1719 +  $$;
  6.1720 +
     7.1 --- a/latlon-v0009.c	Tue Feb 11 02:35:16 2020 +0100
     7.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.3 @@ -1,3352 +0,0 @@
     7.4 -
     7.5 -/*-------------*
     7.6 - *  C prelude  *
     7.7 - *-------------*/
     7.8 -
     7.9 -#include "postgres.h"
    7.10 -#include "fmgr.h"
    7.11 -#include "libpq/pqformat.h"
    7.12 -#include "access/gist.h"
    7.13 -#include "access/stratnum.h"
    7.14 -#include "utils/array.h"
    7.15 -#include <limits.h>
    7.16 -#include <math.h>
    7.17 -
    7.18 -#ifdef PG_MODULE_MAGIC
    7.19 -PG_MODULE_MAGIC;
    7.20 -#endif
    7.21 -
    7.22 -#if INT_MAX < 2147483647
    7.23 -#error Expected int type to be at least 32 bit wide
    7.24 -#endif
    7.25 -
    7.26 -
    7.27 -/*---------------------------------*
    7.28 - *  distance calculation on earth  *
    7.29 - *  (using WGS-84 spheroid)        *
    7.30 - *---------------------------------*/
    7.31 -
    7.32 -/*  WGS-84 spheroid with following parameters:
    7.33 -    semi-major axis  a = 6378137
    7.34 -    semi-minor axis  b = a * (1 - 1/298.257223563)
    7.35 -    estimated diameter = 2 * (2*a+b)/3
    7.36 -*/
    7.37 -#define PGL_SPHEROID_A 6378137.0            /* semi major axis */
    7.38 -#define PGL_SPHEROID_F (1.0/298.257223563)  /* flattening */
    7.39 -#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F))
    7.40 -#define PGL_EPS2       ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \
    7.41 -                           PGL_SPHEROID_B * PGL_SPHEROID_B ) / \
    7.42 -                         ( PGL_SPHEROID_A * PGL_SPHEROID_A ) )
    7.43 -#define PGL_SUBEPS2    (1.0-PGL_EPS2)
    7.44 -#define PGL_RADIUS     ((2.0*PGL_SPHEROID_A + PGL_SPHEROID_B) / 3.0)
    7.45 -#define PGL_DIAMETER   (2.0 * PGL_RADIUS)
    7.46 -#define PGL_SCALE      (PGL_SPHEROID_A / PGL_DIAMETER)  /* semi-major ref. */
    7.47 -#define PGL_MAXDIST    (PGL_RADIUS * M_PI)              /* maximum distance */
    7.48 -#define PGL_FADELIMIT  (PGL_MAXDIST / 3.0)              /* 1/6 circumference */
    7.49 -
    7.50 -/* calculate distance between two points on earth (given in degrees) */
    7.51 -static inline double pgl_distance(
    7.52 -  double lat1, double lon1, double lat2, double lon2
    7.53 -) {
    7.54 -  float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin;
    7.55 -  float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t;
    7.56 -  /* normalize delta longitude (lon2 > 0 && lon1 = 0) */
    7.57 -  /* lon1 = 0 (not used anymore) */
    7.58 -  lon2 = fabs(lon2-lon1);
    7.59 -  /* convert to radians (first divide, then multiply) */
    7.60 -  lat1 = (lat1 / 180.0) * M_PI;
    7.61 -  lat2 = (lat2 / 180.0) * M_PI;
    7.62 -  lon2 = (lon2 / 180.0) * M_PI;
    7.63 -  /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point
    7.64 -     operations (lon2 >= lon1 is already ensured in a previous step) */
    7.65 -  if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; }
    7.66 -  /* calculate 3d coordinates on scaled ellipsoid which has an average diameter
    7.67 -     of 1.0 */
    7.68 -  lat1cos = cos(lat1); lat1sin = sin(lat1);
    7.69 -  lat2cos = cos(lat2); lat2sin = sin(lat2);
    7.70 -  lon2cos = cos(lon2); lon2sin = sin(lon2);
    7.71 -  nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin);
    7.72 -  nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
    7.73 -  x1 = nphi1 * lat1cos;
    7.74 -  z1 = nphi1 * PGL_SUBEPS2 * lat1sin;
    7.75 -  x2 = nphi2 * lat2cos * lon2cos;
    7.76 -  y2 = nphi2 * lat2cos * lon2sin;
    7.77 -  z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
    7.78 -  /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */
    7.79 -  g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
    7.80 -  /* convert tunnel distance through scaled ellipsoid to approximated surface
    7.81 -     distance on original ellipsoid */
    7.82 -  if (g > 1.0) g = 1.0;
    7.83 -  s = PGL_DIAMETER * asin(g);
    7.84 -  /* return result only if small enough to be precise (less than 1/3 of
    7.85 -     maximum possible distance) */
    7.86 -  if (s <= PGL_FADELIMIT) return s;
    7.87 -  /* calculate tunnel distance to antipodal point through scaled ellipsoid */
    7.88 -  g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1));
    7.89 -  /* convert tunnel distance to antipodal point through scaled ellipsoid to
    7.90 -     approximated surface distance to antipodal point on original ellipsoid */
    7.91 -  if (g > 1.0) g = 1.0;
    7.92 -  t = PGL_DIAMETER * asin(g);
    7.93 -  /* surface distance between original points can now be approximated by
    7.94 -     substracting antipodal distance from maximum possible distance;
    7.95 -     return result only if small enough (less than 1/3 of maximum possible
    7.96 -     distance) */
    7.97 -  if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t;
    7.98 -  /* otherwise crossfade direct and antipodal result to ensure monotonicity */
    7.99 -  return (
   7.100 -    (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) /
   7.101 -    (s + t - 2*PGL_FADELIMIT)
   7.102 -  );
   7.103 -}
   7.104 -
   7.105 -/* finite distance that can not be reached on earth */
   7.106 -#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST)
   7.107 -
   7.108 -
   7.109 -/*--------------------------------*
   7.110 - *  simple geographic data types  *
   7.111 - *--------------------------------*/
   7.112 -
   7.113 -/* point on earth given by latitude and longitude in degrees */
   7.114 -/* (type "epoint" in SQL) */
   7.115 -typedef struct {
   7.116 -  double lat;  /* between  -90 and  90 (both inclusive) */
   7.117 -  double lon;  /* between -180 and 180 (both inclusive) */
   7.118 -} pgl_point;
   7.119 -
   7.120 -/* box delimited by two parallels and two meridians (all in degrees) */
   7.121 -/* (type "ebox" in SQL) */
   7.122 -typedef struct {
   7.123 -  double lat_min;  /* between  -90 and  90 (both inclusive) */
   7.124 -  double lat_max;  /* between  -90 and  90 (both inclusive) */
   7.125 -  double lon_min;  /* between -180 and 180 (both inclusive) */
   7.126 -  double lon_max;  /* between -180 and 180 (both inclusive) */
   7.127 -  /* if lat_min > lat_max, then box is empty */
   7.128 -  /* if lon_min > lon_max, then 180th meridian is crossed */
   7.129 -} pgl_box;
   7.130 -
   7.131 -/* circle on earth surface (for radial searches with fixed radius) */
   7.132 -/* (type "ecircle" in SQL) */
   7.133 -typedef struct {
   7.134 -  pgl_point center;
   7.135 -  double radius; /* positive (including +0 but excluding -0), or -INFINITY */
   7.136 -  /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point),
   7.137 -     zero radius (0) denotes a single point,
   7.138 -     a finite radius (0 < radius < INFINITY) denotes a filled circle, and
   7.139 -     a radius of INFINITY is valid and means complete coverage of earth. */
   7.140 -} pgl_circle;
   7.141 -
   7.142 -
   7.143 -/*----------------------------------*
   7.144 - *  geographic "cluster" data type  *
   7.145 - *----------------------------------*/
   7.146 -
   7.147 -/* A cluster is a collection of points, paths, outlines, and polygons. If two
   7.148 -   polygons in a cluster overlap, the area covered by both polygons does not
   7.149 -   belong to the cluster. This way, a cluster can be used to describe complex
   7.150 -   shapes like polygons with holes. Outlines are non-filled polygons. Paths are
   7.151 -   open by default (i.e. the last point in the list is not connected with the
   7.152 -   first point in the list). Note that each outline or polygon in a cluster
   7.153 -   must cover a longitude range of less than 180 degrees to avoid ambiguities.
   7.154 -   Areas which are larger may be split into multiple polygons. */
   7.155 -
   7.156 -/* maximum number of points in a cluster */
   7.157 -/* (limited to avoid integer overflows, e.g. when allocating memory) */
   7.158 -#define PGL_CLUSTER_MAXPOINTS 16777216
   7.159 -
   7.160 -/* types of cluster entries */
   7.161 -#define PGL_ENTRY_POINT   1  /* a point */
   7.162 -#define PGL_ENTRY_PATH    2  /* a path from first point to last point */
   7.163 -#define PGL_ENTRY_OUTLINE 3  /* a non-filled polygon with given vertices */
   7.164 -#define PGL_ENTRY_POLYGON 4  /* a filled polygon with given vertices */
   7.165 -
   7.166 -/* Entries of a cluster are described by two different structs: pgl_newentry
   7.167 -   and pgl_entry. The first is used only during construction of a cluster, the
   7.168 -   second is used in all other cases (e.g. when reading clusters from the
   7.169 -   database, performing operations, etc). */
   7.170 -
   7.171 -/* entry for new geographic cluster during construction of that cluster */
   7.172 -typedef struct {
   7.173 -  int32_t entrytype;
   7.174 -  int32_t npoints;
   7.175 -  pgl_point *points;  /* pointer to an array of points (pgl_point) */
   7.176 -} pgl_newentry;
   7.177 -
   7.178 -/* entry of geographic cluster */
   7.179 -typedef struct {
   7.180 -  int32_t entrytype;  /* type of entry: point, path, outline, polygon */
   7.181 -  int32_t npoints;    /* number of stored points (set to 1 for point entry) */
   7.182 -  int32_t offset;     /* offset of pgl_point array from cluster base address */
   7.183 -  /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */
   7.184 -} pgl_entry;
   7.185 -
   7.186 -/* geographic cluster which is a collection of points, (open) paths, polygons,
   7.187 -   and outlines (non-filled polygons) */
   7.188 -typedef struct {
   7.189 -  char header[VARHDRSZ];  /* PostgreSQL header for variable size data types */
   7.190 -  int32_t nentries;       /* number of stored points */
   7.191 -  pgl_circle bounding;    /* bounding circle */
   7.192 -  /* Note: bounding circle ensures alignment of pgl_cluster for points */
   7.193 -  pgl_entry entries[FLEXIBLE_ARRAY_MEMBER];  /* var-length data */
   7.194 -} pgl_cluster;
   7.195 -
   7.196 -/* macro to determine memory alignment of points */
   7.197 -/* (needed to store pgl_point array after entries in pgl_cluster) */
   7.198 -typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment;
   7.199 -#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned)
   7.200 -
   7.201 -/* macro to extract a pointer to the array of points of a cluster entry */
   7.202 -#define PGL_ENTRY_POINTS(cluster, idx) \
   7.203 -  ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset))
   7.204 -
   7.205 -/* convert pgl_newentry array to pgl_cluster */
   7.206 -/* NOTE: requires pgl_finalize_cluster to be called to finalize result */
   7.207 -static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) {
   7.208 -  int i;              /* index of current entry */
   7.209 -  int npoints = 0;    /* number of points in whole cluster */
   7.210 -  int entry_npoints;  /* number of points in current entry */
   7.211 -  int points_offset = PGL_POINT_ALIGNMENT * (
   7.212 -    ( offsetof(pgl_cluster, entries) +
   7.213 -      nentries * sizeof(pgl_entry) +
   7.214 -      PGL_POINT_ALIGNMENT - 1
   7.215 -    ) / PGL_POINT_ALIGNMENT
   7.216 -  );  /* offset of pgl_point array from base address (considering alignment) */
   7.217 -  pgl_cluster *cluster;  /* new cluster to be returned */
   7.218 -  /* determine total number of points */
   7.219 -  for (i=0; i<nentries; i++) npoints += entries[i].npoints;
   7.220 -  /* allocate memory for cluster (including entries and points) */
   7.221 -  cluster = palloc(points_offset + npoints * sizeof(pgl_point));
   7.222 -  /* re-count total number of points to determine offset for each entry */
   7.223 -  npoints = 0;
   7.224 -  /* copy entries and points */
   7.225 -  for (i=0; i<nentries; i++) {
   7.226 -    /* determine number of points in entry */
   7.227 -    entry_npoints = entries[i].npoints;
   7.228 -    /* copy entry */
   7.229 -    cluster->entries[i].entrytype = entries[i].entrytype;
   7.230 -    cluster->entries[i].npoints = entry_npoints;
   7.231 -    /* calculate offset (in bytes) of pgl_point array */
   7.232 -    cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point);
   7.233 -    /* copy points */
   7.234 -    memcpy(
   7.235 -      PGL_ENTRY_POINTS(cluster, i),
   7.236 -      entries[i].points,
   7.237 -      entry_npoints * sizeof(pgl_point)
   7.238 -    );
   7.239 -    /* update total number of points processed */
   7.240 -    npoints += entry_npoints;
   7.241 -  }
   7.242 -  /* set number of entries in cluster */
   7.243 -  cluster->nentries = nentries;
   7.244 -  /* set PostgreSQL header for variable sized data */
   7.245 -  SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point));
   7.246 -  /* return newly created cluster */
   7.247 -  return cluster;
   7.248 -}
   7.249 -
   7.250 -
   7.251 -/*----------------------------------------------*
   7.252 - *  Geographic point with integer sample count  *
   7.253 - *  (needed for fair distance calculation)      *
   7.254 - *----------------------------------------------*/
   7.255 -
   7.256 -typedef struct {
   7.257 -  pgl_point point;  /* NOTE: point first to allow C cast to pgl_point */
   7.258 -  int32 samples;
   7.259 -} pgl_point_sc;
   7.260 -
   7.261 -
   7.262 -/*----------------------------------------*
   7.263 - *  C functions on geographic data types  *
   7.264 - *----------------------------------------*/
   7.265 -
   7.266 -/* round latitude or longitude to 12 digits after decimal point */
   7.267 -static inline double pgl_round(double val) {
   7.268 -  return round(val * 1e12) / 1e12;
   7.269 -}
   7.270 -
   7.271 -/* compare two points */
   7.272 -/* (equality when same point on earth is described, otherwise an arbitrary
   7.273 -   linear order) */
   7.274 -static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) {
   7.275 -  double lon1, lon2;  /* modified longitudes for special cases */
   7.276 -  /* use latitude as first ordering criterion */
   7.277 -  if (point1->lat < point2->lat) return -1;
   7.278 -  if (point1->lat > point2->lat) return 1;
   7.279 -  /* determine modified longitudes (considering special case of poles and
   7.280 -     180th meridian which can be described as W180 or E180) */
   7.281 -  if (point1->lat == -90 || point1->lat == 90) lon1 = 0;
   7.282 -  else if (point1->lon == 180) lon1 = -180;
   7.283 -  else lon1 = point1->lon;
   7.284 -  if (point2->lat == -90 || point2->lat == 90) lon2 = 0;
   7.285 -  else if (point2->lon == 180) lon2 = -180;
   7.286 -  else lon2 = point2->lon;
   7.287 -  /* use (modified) longitude as secondary ordering criterion */
   7.288 -  if (lon1 < lon2) return -1;
   7.289 -  if (lon1 > lon2) return 1;
   7.290 -  /* no difference found, points are equal */
   7.291 -  return 0;
   7.292 -}
   7.293 -
   7.294 -/* compare two boxes */
   7.295 -/* (equality when same box on earth is described, otherwise an arbitrary linear
   7.296 -   order) */
   7.297 -static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) {
   7.298 -  /* two empty boxes are equal, and an empty box is always considered "less
   7.299 -     than" a non-empty box */
   7.300 -  if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1;
   7.301 -  if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0;
   7.302 -  if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1;
   7.303 -  /* use southern border as first ordering criterion */
   7.304 -  if (box1->lat_min < box2->lat_min) return -1;
   7.305 -  if (box1->lat_min > box2->lat_min) return 1;
   7.306 -  /* use northern border as second ordering criterion */
   7.307 -  if (box1->lat_max < box2->lat_max) return -1;
   7.308 -  if (box1->lat_max > box2->lat_max) return 1;
   7.309 -  /* use western border as third ordering criterion */
   7.310 -  if (box1->lon_min < box2->lon_min) return -1;
   7.311 -  if (box1->lon_min > box2->lon_min) return 1;
   7.312 -  /* use eastern border as fourth ordering criterion */
   7.313 -  if (box1->lon_max < box2->lon_max) return -1;
   7.314 -  if (box1->lon_max > box2->lon_max) return 1;
   7.315 -  /* no difference found, boxes are equal */
   7.316 -  return 0;
   7.317 -}
   7.318 -
   7.319 -/* compare two circles */
   7.320 -/* (equality when same circle on earth is described, otherwise an arbitrary
   7.321 -   linear order) */
   7.322 -static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) {
   7.323 -  /* two circles with same infinite radius (positive or negative infinity) are
   7.324 -     considered equal independently of center point */
   7.325 -  if (
   7.326 -    !isfinite(circle1->radius) && !isfinite(circle2->radius) &&
   7.327 -    circle1->radius == circle2->radius
   7.328 -  ) return 0;
   7.329 -  /* use radius as first ordering criterion */
   7.330 -  if (circle1->radius < circle2->radius) return -1;
   7.331 -  if (circle1->radius > circle2->radius) return 1;
   7.332 -  /* use center point as secondary ordering criterion */
   7.333 -  return pgl_point_cmp(&(circle1->center), &(circle2->center));
   7.334 -}
   7.335 -
   7.336 -/* set box to empty box*/
   7.337 -static void pgl_box_set_empty(pgl_box *box) {
   7.338 -  box->lat_min = INFINITY;
   7.339 -  box->lat_max = -INFINITY;
   7.340 -  box->lon_min = 0;
   7.341 -  box->lon_max = 0;
   7.342 -}
   7.343 -
   7.344 -/* check if point is inside a box */
   7.345 -static bool pgl_point_in_box(pgl_point *point, pgl_box *box) {
   7.346 -  return (
   7.347 -    point->lat >= box->lat_min && point->lat <= box->lat_max && (
   7.348 -      (box->lon_min > box->lon_max) ? (
   7.349 -        /* box crosses 180th meridian */
   7.350 -        point->lon >= box->lon_min || point->lon <= box->lon_max
   7.351 -      ) : (
   7.352 -        /* box does not cross the 180th meridian */
   7.353 -        point->lon >= box->lon_min && point->lon <= box->lon_max
   7.354 -      )
   7.355 -    )
   7.356 -  );
   7.357 -}
   7.358 -
   7.359 -/* check if two boxes overlap */
   7.360 -static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) {
   7.361 -  return (
   7.362 -    box2->lat_max >= box2->lat_min &&  /* ensure box2 is not empty */
   7.363 -    ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) &&
   7.364 -    ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && (
   7.365 -      (
   7.366 -        /* check if one and only one box crosses the 180th meridian */
   7.367 -        ((box1->lon_min > box1->lon_max) ? 1 : 0) ^
   7.368 -        ((box2->lon_min > box2->lon_max) ? 1 : 0)
   7.369 -      ) ? (
   7.370 -        /* exactly one box crosses the 180th meridian */
   7.371 -        box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min ||
   7.372 -        box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max
   7.373 -      ) : (
   7.374 -        /* no box or both boxes cross the 180th meridian */
   7.375 -        (
   7.376 -          (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) &&
   7.377 -          (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max)
   7.378 -        ) ||
   7.379 -        /* handle W180 == E180 */
   7.380 -        ( box1->lon_min == -180 && box2->lon_max == 180 ) ||
   7.381 -        ( box2->lon_min == -180 && box1->lon_max == 180 )
   7.382 -      )
   7.383 -    )
   7.384 -  );
   7.385 -}
   7.386 -
   7.387 -/* check unambiguousness of east/west orientation of cluster entries and set
   7.388 -   bounding circle of cluster */
   7.389 -static bool pgl_finalize_cluster(pgl_cluster *cluster) {
   7.390 -  int i, j;                 /* i: index of entry, j: index of point in entry */
   7.391 -  int npoints;              /* number of points in entry */
   7.392 -  int total_npoints = 0;    /* total number of points in cluster */
   7.393 -  pgl_point *points;        /* points in entry */
   7.394 -  int lon_dir;              /* first point of entry west (-1) or east (+1) */
   7.395 -  double lon_break = 0;     /* antipodal longitude of first point in entry */
   7.396 -  double lon_min, lon_max;  /* covered longitude range of entry */
   7.397 -  double value;             /* temporary variable */
   7.398 -  /* reset bounding circle center to empty circle at 0/0 coordinates */
   7.399 -  cluster->bounding.center.lat = 0;
   7.400 -  cluster->bounding.center.lon = 0;
   7.401 -  cluster->bounding.radius = -INFINITY;
   7.402 -  /* if cluster is not empty */
   7.403 -  if (cluster->nentries != 0) {
   7.404 -    /* iterate over all cluster entries and ensure they each cover a longitude
   7.405 -       range less than 180 degrees */
   7.406 -    for (i=0; i<cluster->nentries; i++) {
   7.407 -      /* get properties of entry */
   7.408 -      npoints = cluster->entries[i].npoints;
   7.409 -      points = PGL_ENTRY_POINTS(cluster, i);
   7.410 -      /* get longitude of first point of entry */
   7.411 -      value = points[0].lon;
   7.412 -      /* initialize lon_min and lon_max with longitude of first point */
   7.413 -      lon_min = value;
   7.414 -      lon_max = value;
   7.415 -      /* determine east/west orientation of first point and calculate antipodal
   7.416 -         longitude (Note: rounding required here) */
   7.417 -      if      (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); }
   7.418 -      else if (value > 0) { lon_dir =  1; lon_break = pgl_round(value - 180); }
   7.419 -      else lon_dir = 0;
   7.420 -      /* iterate over all other points in entry */
   7.421 -      for (j=1; j<npoints; j++) {
   7.422 -        /* consider longitude wrap-around */
   7.423 -        value = points[j].lon;
   7.424 -        if      (lon_dir<0 && value>lon_break) value = pgl_round(value - 360);
   7.425 -        else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360);
   7.426 -        /* update lon_min and lon_max */
   7.427 -        if      (value < lon_min) lon_min = value;
   7.428 -        else if (value > lon_max) lon_max = value;
   7.429 -        /* return false if 180 degrees or more are covered */
   7.430 -        if (lon_max - lon_min >= 180) return false;
   7.431 -      }
   7.432 -    }
   7.433 -    /* iterate over all points of all entries and calculate arbitrary center
   7.434 -       point for bounding circle (best if center point minimizes the radius,
   7.435 -       but some error is allowed here) */
   7.436 -    for (i=0; i<cluster->nentries; i++) {
   7.437 -      /* get properties of entry */
   7.438 -      npoints = cluster->entries[i].npoints;
   7.439 -      points = PGL_ENTRY_POINTS(cluster, i);
   7.440 -      /* check if first entry */
   7.441 -      if (i==0) {
   7.442 -        /* get longitude of first point of first entry in whole cluster */
   7.443 -        value = points[0].lon;
   7.444 -        /* initialize lon_min and lon_max with longitude of first point of
   7.445 -           first entry in whole cluster (used to determine if whole cluster
   7.446 -           covers a longitude range of 180 degrees or more) */
   7.447 -        lon_min = value;
   7.448 -        lon_max = value;
   7.449 -        /* determine east/west orientation of first point and calculate
   7.450 -           antipodal longitude (Note: rounding not necessary here) */
   7.451 -        if      (value < 0) { lon_dir = -1; lon_break = value + 180; }
   7.452 -        else if (value > 0) { lon_dir =  1; lon_break = value - 180; }
   7.453 -        else lon_dir = 0;
   7.454 -      }
   7.455 -      /* iterate over all points in entry */
   7.456 -      for (j=0; j<npoints; j++) {
   7.457 -        /* longitude wrap-around (Note: rounding not necessary here) */
   7.458 -        value = points[j].lon;
   7.459 -        if      (lon_dir < 0 && value > lon_break) value -= 360;
   7.460 -        else if (lon_dir > 0 && value < lon_break) value += 360;
   7.461 -        if      (value < lon_min) lon_min = value;
   7.462 -        else if (value > lon_max) lon_max = value;
   7.463 -        /* set bounding circle to cover whole earth if 180 degrees or more are
   7.464 -           covered */
   7.465 -        if (lon_max - lon_min >= 180) {
   7.466 -          cluster->bounding.center.lat = 0;
   7.467 -          cluster->bounding.center.lon = 0;
   7.468 -          cluster->bounding.radius = INFINITY;
   7.469 -          return true;
   7.470 -        }
   7.471 -        /* add point to bounding circle center (for average calculation) */
   7.472 -        cluster->bounding.center.lat += points[j].lat;
   7.473 -        cluster->bounding.center.lon += value;
   7.474 -      }
   7.475 -      /* count total number of points */
   7.476 -      total_npoints += npoints;
   7.477 -    }
   7.478 -    /* determine average latitude and longitude of cluster */
   7.479 -    cluster->bounding.center.lat /= total_npoints;
   7.480 -    cluster->bounding.center.lon /= total_npoints;
   7.481 -    /* normalize longitude of center of cluster bounding circle */
   7.482 -    if (cluster->bounding.center.lon < -180) {
   7.483 -      cluster->bounding.center.lon += 360;
   7.484 -    }
   7.485 -    else if (cluster->bounding.center.lon > 180) {
   7.486 -      cluster->bounding.center.lon -= 360;
   7.487 -    }
   7.488 -    /* round bounding circle center (useful if it is used by other functions) */
   7.489 -    cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat);
   7.490 -    cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon);
   7.491 -    /* calculate radius of bounding circle */
   7.492 -    for (i=0; i<cluster->nentries; i++) {
   7.493 -      npoints = cluster->entries[i].npoints;
   7.494 -      points = PGL_ENTRY_POINTS(cluster, i);
   7.495 -      for (j=0; j<npoints; j++) {
   7.496 -        value = pgl_distance(
   7.497 -          cluster->bounding.center.lat, cluster->bounding.center.lon,
   7.498 -          points[j].lat, points[j].lon
   7.499 -        );
   7.500 -        if (value > cluster->bounding.radius) cluster->bounding.radius = value;
   7.501 -      }
   7.502 -    }
   7.503 -  }
   7.504 -  /* return true (east/west orientation is unambiguous) */
   7.505 -  return true;
   7.506 -}
   7.507 -
   7.508 -/* check if point is inside cluster */
   7.509 -/* (if point is on perimeter, then true is returned if and only if
   7.510 -   strict == false) */
   7.511 -static bool pgl_point_in_cluster(
   7.512 -  pgl_point *point,
   7.513 -  pgl_cluster *cluster,
   7.514 -  bool strict
   7.515 -) {
   7.516 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   7.517 -  int entrytype;         /* type of entry */
   7.518 -  int npoints;           /* number of points in entry */
   7.519 -  pgl_point *points;     /* array of points in entry */
   7.520 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   7.521 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   7.522 -  double lat0 = point->lat;  /* latitude of point */
   7.523 -  double lon0;           /* (adjusted) longitude of point */
   7.524 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   7.525 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   7.526 -  double lon;            /* longitude of intersection */
   7.527 -  int counter = 0;       /* counter for intersections east of point */
   7.528 -  /* iterate over all entries */
   7.529 -  for (i=0; i<cluster->nentries; i++) {
   7.530 -    /* get type of entry */
   7.531 -    entrytype = cluster->entries[i].entrytype;
   7.532 -    /* skip all entries but polygons if perimeters are excluded */
   7.533 -    if (strict && entrytype != PGL_ENTRY_POLYGON) continue;
   7.534 -    /* get points of entry */
   7.535 -    npoints = cluster->entries[i].npoints;
   7.536 -    points = PGL_ENTRY_POINTS(cluster, i);
   7.537 -    /* determine east/west orientation of first point of entry and calculate
   7.538 -       antipodal longitude */
   7.539 -    lon_break = points[0].lon;
   7.540 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   7.541 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   7.542 -    else lon_dir = 0;
   7.543 -    /* get longitude of point */
   7.544 -    lon0 = point->lon;
   7.545 -    /* consider longitude wrap-around for point */
   7.546 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360);
   7.547 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360);
   7.548 -    /* iterate over all edges and vertices */
   7.549 -    for (j=0; j<npoints; j++) {
   7.550 -      /* return if point is on vertex of polygon */
   7.551 -      if (pgl_point_cmp(point, &(points[j])) == 0) return !strict;
   7.552 -      /* calculate index of next vertex */
   7.553 -      k = (j+1) % npoints;
   7.554 -      /* skip last edge unless entry is (closed) outline or polygon */
   7.555 -      if (
   7.556 -        k == 0 &&
   7.557 -        entrytype != PGL_ENTRY_OUTLINE &&
   7.558 -        entrytype != PGL_ENTRY_POLYGON
   7.559 -      ) continue;
   7.560 -      /* use previously calculated values for lat1 and lon1 if possible */
   7.561 -      if (j) {
   7.562 -        lat1 = lat2;
   7.563 -        lon1 = lon2;
   7.564 -      } else {
   7.565 -        /* otherwise get latitude and longitude values of first vertex */
   7.566 -        lat1 = points[0].lat;
   7.567 -        lon1 = points[0].lon;
   7.568 -        /* and consider longitude wrap-around for first vertex */
   7.569 -        if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   7.570 -        else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   7.571 -      }
   7.572 -      /* get latitude and longitude of next vertex */
   7.573 -      lat2 = points[k].lat;
   7.574 -      lon2 = points[k].lon;
   7.575 -      /* consider longitude wrap-around for next vertex */
   7.576 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
   7.577 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
   7.578 -      /* return if point is on horizontal (west to east) edge of polygon */
   7.579 -      if (
   7.580 -        lat0 == lat1 && lat0 == lat2 &&
   7.581 -        ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) )
   7.582 -      ) return !strict;
   7.583 -      /* check if edge crosses east/west line of point */
   7.584 -      if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) {
   7.585 -        /* calculate longitude of intersection */
   7.586 -        lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1);
   7.587 -        /* return if intersection goes (approximately) through point */
   7.588 -        if (pgl_round(lon) == lon0) return !strict;
   7.589 -        /* count intersection if east of point and entry is polygon*/
   7.590 -        if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++;
   7.591 -      }
   7.592 -    }
   7.593 -  }
   7.594 -  /* return true if number of intersections is odd */
   7.595 -  return counter & 1;
   7.596 -}
   7.597 -
   7.598 -/* check if all points of the second cluster are strictly inside the first
   7.599 -   cluster */
   7.600 -static inline bool pgl_all_cluster_points_strictly_in_cluster(
   7.601 -  pgl_cluster *outer, pgl_cluster *inner
   7.602 -) {
   7.603 -  int i, j;           /* i: entry, j: point in entry */
   7.604 -  int npoints;        /* number of points in entry */
   7.605 -  pgl_point *points;  /* array of points in entry */
   7.606 -  /* iterate over all entries of "inner" cluster */
   7.607 -  for (i=0; i<inner->nentries; i++) {
   7.608 -    /* get properties of entry */
   7.609 -    npoints = inner->entries[i].npoints;
   7.610 -    points = PGL_ENTRY_POINTS(inner, i);
   7.611 -    /* iterate over all points in entry of "inner" cluster */
   7.612 -    for (j=0; j<npoints; j++) {
   7.613 -      /* return false if one point of inner cluster is not in outer cluster */
   7.614 -      if (!pgl_point_in_cluster(points+j, outer, true)) return false;
   7.615 -    }
   7.616 -  }
   7.617 -  /* otherwise return true */
   7.618 -  return true;
   7.619 -}
   7.620 -
   7.621 -/* check if any point the second cluster is inside the first cluster */
   7.622 -static inline bool pgl_any_cluster_points_in_cluster(
   7.623 -  pgl_cluster *outer, pgl_cluster *inner
   7.624 -) {
   7.625 -  int i, j;           /* i: entry, j: point in entry */
   7.626 -  int npoints;        /* number of points in entry */
   7.627 -  pgl_point *points;  /* array of points in entry */
   7.628 -  /* iterate over all entries of "inner" cluster */
   7.629 -  for (i=0; i<inner->nentries; i++) {
   7.630 -    /* get properties of entry */
   7.631 -    npoints = inner->entries[i].npoints;
   7.632 -    points = PGL_ENTRY_POINTS(inner, i);
   7.633 -    /* iterate over all points in entry of "inner" cluster */
   7.634 -    for (j=0; j<npoints; j++) {
   7.635 -      /* return true if one point of inner cluster is in outer cluster */
   7.636 -      if (pgl_point_in_cluster(points+j, outer, false)) return true;
   7.637 -    }
   7.638 -  }
   7.639 -  /* otherwise return false */
   7.640 -  return false;
   7.641 -}
   7.642 -
   7.643 -/* check if line segment strictly crosses line (not just touching) */
   7.644 -static inline bool pgl_lseg_crosses_line(
   7.645 -  double seg_x1,  double seg_y1,  double seg_x2,  double seg_y2,
   7.646 -  double line_x1, double line_y1, double line_x2, double line_y2
   7.647 -) {
   7.648 -  return (
   7.649 -    (
   7.650 -      (seg_x1-line_x1) * (line_y2-line_y1) -
   7.651 -      (seg_y1-line_y1) * (line_x2-line_x1)
   7.652 -    ) * (
   7.653 -      (seg_x2-line_x1) * (line_y2-line_y1) -
   7.654 -      (seg_y2-line_y1) * (line_x2-line_x1)
   7.655 -    )
   7.656 -  ) < 0;
   7.657 -}
   7.658 -
   7.659 -/* check if paths and outlines of two clusters strictly overlap (not just
   7.660 -   touching) */
   7.661 -static bool pgl_outlines_overlap(
   7.662 -  pgl_cluster *cluster1, pgl_cluster *cluster2
   7.663 -) {
   7.664 -  int i1, j1, k1;  /* i: entry, j: point in entry, k: next point in entry */
   7.665 -  int i2, j2, k2;
   7.666 -  int entrytype1, entrytype2;     /* type of entry */
   7.667 -  int npoints1, npoints2;         /* number of points in entry */
   7.668 -  pgl_point *points1;             /* array of points in entry of cluster1 */
   7.669 -  pgl_point *points2;             /* array of points in entry of cluster2 */
   7.670 -  int lon_dir1, lon_dir2;         /* first vertex west (-1) or east (+1) */
   7.671 -  double lon_break1, lon_break2;  /* antipodal longitude of first vertex */
   7.672 -  double lat11, lon11;  /* latitude and (adjusted) longitude of vertex */
   7.673 -  double lat12, lon12;  /* latitude and (adjusted) longitude of next vertex */
   7.674 -  double lat21, lon21;  /* latitude and (adjusted) longitudes for cluster2 */
   7.675 -  double lat22, lon22;
   7.676 -  double wrapvalue;     /* temporary helper value to adjust wrap-around */  
   7.677 -  /* iterate over all entries of cluster1 */
   7.678 -  for (i1=0; i1<cluster1->nentries; i1++) {
   7.679 -    /* get properties of entry in cluster1 and skip points */
   7.680 -    npoints1 = cluster1->entries[i1].npoints;
   7.681 -    if (npoints1 < 2) continue;
   7.682 -    entrytype1 = cluster1->entries[i1].entrytype;
   7.683 -    points1 = PGL_ENTRY_POINTS(cluster1, i1);
   7.684 -    /* determine east/west orientation of first point and calculate antipodal
   7.685 -       longitude */
   7.686 -    lon_break1 = points1[0].lon;
   7.687 -    if (lon_break1 < 0) {
   7.688 -      lon_dir1   = -1;
   7.689 -      lon_break1 = pgl_round(lon_break1 + 180);
   7.690 -    } else if (lon_break1 > 0) {
   7.691 -      lon_dir1   = 1;
   7.692 -      lon_break1 = pgl_round(lon_break1 - 180);
   7.693 -    } else lon_dir1 = 0;
   7.694 -    /* iterate over all edges and vertices in cluster1 */
   7.695 -    for (j1=0; j1<npoints1; j1++) {
   7.696 -      /* calculate index of next vertex */
   7.697 -      k1 = (j1+1) % npoints1;
   7.698 -      /* skip last edge unless entry is (closed) outline or polygon */
   7.699 -      if (
   7.700 -        k1 == 0 &&
   7.701 -        entrytype1 != PGL_ENTRY_OUTLINE &&
   7.702 -        entrytype1 != PGL_ENTRY_POLYGON
   7.703 -      ) continue;
   7.704 -      /* use previously calculated values for lat1 and lon1 if possible */
   7.705 -      if (j1) {
   7.706 -        lat11 = lat12;
   7.707 -        lon11 = lon12;
   7.708 -      } else {
   7.709 -        /* otherwise get latitude and longitude values of first vertex */
   7.710 -        lat11 = points1[0].lat;
   7.711 -        lon11 = points1[0].lon;
   7.712 -        /* and consider longitude wrap-around for first vertex */
   7.713 -        if      (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360);
   7.714 -        else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360);
   7.715 -      }
   7.716 -      /* get latitude and longitude of next vertex */
   7.717 -      lat12 = points1[k1].lat;
   7.718 -      lon12 = points1[k1].lon;
   7.719 -      /* consider longitude wrap-around for next vertex */
   7.720 -      if      (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360);
   7.721 -      else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360);
   7.722 -      /* skip degenerated edges */
   7.723 -      if (lat11 == lat12 && lon11 == lon12) continue;
   7.724 -      /* iterate over all entries of cluster2 */
   7.725 -      for (i2=0; i2<cluster2->nentries; i2++) {
   7.726 -        /* get points and number of points of entry in cluster2 */
   7.727 -        npoints2 = cluster2->entries[i2].npoints;
   7.728 -        if (npoints2 < 2) continue;
   7.729 -        entrytype2 = cluster2->entries[i2].entrytype;
   7.730 -        points2 = PGL_ENTRY_POINTS(cluster2, i2);
   7.731 -        /* determine east/west orientation of first point and calculate antipodal
   7.732 -           longitude */
   7.733 -        lon_break2 = points2[0].lon;
   7.734 -        if (lon_break2 < 0) {
   7.735 -          lon_dir2   = -1;
   7.736 -          lon_break2 = pgl_round(lon_break2 + 180);
   7.737 -        } else if (lon_break2 > 0) {
   7.738 -          lon_dir2   = 1;
   7.739 -          lon_break2 = pgl_round(lon_break2 - 180);
   7.740 -        } else lon_dir2 = 0;
   7.741 -        /* iterate over all edges and vertices in cluster2 */
   7.742 -        for (j2=0; j2<npoints2; j2++) {
   7.743 -          /* calculate index of next vertex */
   7.744 -          k2 = (j2+1) % npoints2;
   7.745 -          /* skip last edge unless entry is (closed) outline or polygon */
   7.746 -          if (
   7.747 -            k2 == 0 &&
   7.748 -            entrytype2 != PGL_ENTRY_OUTLINE &&
   7.749 -            entrytype2 != PGL_ENTRY_POLYGON
   7.750 -          ) continue;
   7.751 -          /* use previously calculated values for lat1 and lon1 if possible */
   7.752 -          if (j2) {
   7.753 -            lat21 = lat22;
   7.754 -            lon21 = lon22;
   7.755 -          } else {
   7.756 -            /* otherwise get latitude and longitude values of first vertex */
   7.757 -            lat21 = points2[0].lat;
   7.758 -            lon21 = points2[0].lon;
   7.759 -            /* and consider longitude wrap-around for first vertex */
   7.760 -            if      (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360);
   7.761 -            else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360);
   7.762 -          }
   7.763 -          /* get latitude and longitude of next vertex */
   7.764 -          lat22 = points2[k2].lat;
   7.765 -          lon22 = points2[k2].lon;
   7.766 -          /* consider longitude wrap-around for next vertex */
   7.767 -          if      (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360);
   7.768 -          else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360);
   7.769 -          /* skip degenerated edges */
   7.770 -          if (lat21 == lat22 && lon21 == lon22) continue;
   7.771 -          /* perform another wrap-around where necessary */
   7.772 -          /* TODO: improve performance of whole wrap-around mechanism */
   7.773 -          wrapvalue = (lon21 + lon22) - (lon11 + lon12);
   7.774 -          if (wrapvalue > 360) {
   7.775 -            lon21 = pgl_round(lon21 - 360);
   7.776 -            lon22 = pgl_round(lon22 - 360);
   7.777 -          } else if (wrapvalue < -360) {
   7.778 -            lon21 = pgl_round(lon21 + 360);
   7.779 -            lon22 = pgl_round(lon22 + 360);
   7.780 -          }
   7.781 -          /* return true if segments overlap */
   7.782 -          if (
   7.783 -            pgl_lseg_crosses_line(
   7.784 -              lat11, lon11, lat12, lon12,
   7.785 -              lat21, lon21, lat22, lon22
   7.786 -            ) && pgl_lseg_crosses_line(
   7.787 -              lat21, lon21, lat22, lon22,
   7.788 -              lat11, lon11, lat12, lon12
   7.789 -            )
   7.790 -          ) {
   7.791 -            return true;
   7.792 -          }
   7.793 -        }
   7.794 -      }
   7.795 -    }
   7.796 -  }
   7.797 -  /* otherwise return false */
   7.798 -  return false;
   7.799 -}
   7.800 -
   7.801 -/* check if second cluster is completely contained in first cluster */
   7.802 -static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) {
   7.803 -  if (!pgl_all_cluster_points_strictly_in_cluster(outer, inner)) return false;
   7.804 -  if (pgl_any_cluster_points_in_cluster(inner, outer)) return false;
   7.805 -  if (pgl_outlines_overlap(outer, inner)) return false;
   7.806 -  return true;
   7.807 -}
   7.808 -
   7.809 -/* check if two clusters overlap */
   7.810 -static bool pgl_clusters_overlap(
   7.811 -  pgl_cluster *cluster1, pgl_cluster *cluster2
   7.812 -) {
   7.813 -  if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true;
   7.814 -  if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true;
   7.815 -  if (pgl_outlines_overlap(cluster1, cluster2)) return true;
   7.816 -  return false;
   7.817 -}
   7.818 -
   7.819 -/* calculate (approximate) distance between point and cluster */
   7.820 -static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
   7.821 -  double comp;           /* square of compression of meridians */
   7.822 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   7.823 -  int entrytype;         /* type of entry */
   7.824 -  int npoints;           /* number of points in entry */
   7.825 -  pgl_point *points;     /* array of points in entry */
   7.826 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   7.827 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   7.828 -  double lon_min = 0;    /* minimum (adjusted) longitude of entry vertices */
   7.829 -  double lon_max = 0;    /* maximum (adjusted) longitude of entry vertices */
   7.830 -  double lat0 = point->lat;  /* latitude of point */
   7.831 -  double lon0;           /* (adjusted) longitude of point */
   7.832 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   7.833 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   7.834 -  double s;              /* scalar for vector calculations */
   7.835 -  double dist;           /* distance calculated in one step */
   7.836 -  double min_dist = INFINITY;   /* minimum distance */
   7.837 -  /* distance is zero if point is contained in cluster */
   7.838 -  if (pgl_point_in_cluster(point, cluster, false)) return 0;
   7.839 -  /* calculate approximate square compression of meridians */
   7.840 -  comp = cos((lat0 / 180.0) * M_PI);
   7.841 -  comp *= comp;
   7.842 -  /* calculate exact square compression of meridians */
   7.843 -  comp *= (
   7.844 -    (1.0 - PGL_EPS2 * (1.0-comp)) *
   7.845 -    (1.0 - PGL_EPS2 * (1.0-comp)) /
   7.846 -    (PGL_SUBEPS2 * PGL_SUBEPS2)
   7.847 -  );
   7.848 -  /* iterate over all entries */
   7.849 -  for (i=0; i<cluster->nentries; i++) {
   7.850 -    /* get properties of entry */
   7.851 -    entrytype = cluster->entries[i].entrytype;
   7.852 -    npoints = cluster->entries[i].npoints;
   7.853 -    points = PGL_ENTRY_POINTS(cluster, i);
   7.854 -    /* determine east/west orientation of first point of entry and calculate
   7.855 -       antipodal longitude */
   7.856 -    lon_break = points[0].lon;
   7.857 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   7.858 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   7.859 -    else lon_dir = 0;
   7.860 -    /* determine covered longitude range */
   7.861 -    for (j=0; j<npoints; j++) {
   7.862 -      /* get longitude of vertex */
   7.863 -      lon1 = points[j].lon;
   7.864 -      /* adjust longitude to fix potential wrap-around */
   7.865 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   7.866 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   7.867 -      /* update minimum and maximum longitude of polygon */
   7.868 -      if (j == 0 || lon1 < lon_min) lon_min = lon1;
   7.869 -      if (j == 0 || lon1 > lon_max) lon_max = lon1;
   7.870 -    }
   7.871 -    /* adjust longitude wrap-around according to full longitude range */
   7.872 -    lon_break = (lon_max + lon_min) / 2;
   7.873 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   7.874 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   7.875 -    /* get longitude of point */
   7.876 -    lon0 = point->lon;
   7.877 -    /* consider longitude wrap-around for point */
   7.878 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 -= 360;
   7.879 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
   7.880 -    /* iterate over all edges and vertices */
   7.881 -    for (j=0; j<npoints; j++) {
   7.882 -      /* use previously calculated values for lat1 and lon1 if possible */
   7.883 -      if (j) {
   7.884 -        lat1 = lat2;
   7.885 -        lon1 = lon2;
   7.886 -      } else {
   7.887 -        /* otherwise get latitude and longitude values of first vertex */
   7.888 -        lat1 = points[0].lat;
   7.889 -        lon1 = points[0].lon;
   7.890 -        /* and consider longitude wrap-around for first vertex */
   7.891 -        if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   7.892 -        else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   7.893 -      }
   7.894 -      /* calculate distance to vertex */
   7.895 -      dist = pgl_distance(lat0, lon0, lat1, lon1);
   7.896 -      /* store calculated distance if smallest */
   7.897 -      if (dist < min_dist) min_dist = dist;
   7.898 -      /* calculate index of next vertex */
   7.899 -      k = (j+1) % npoints;
   7.900 -      /* skip last edge unless entry is (closed) outline or polygon */
   7.901 -      if (
   7.902 -        k == 0 &&
   7.903 -        entrytype != PGL_ENTRY_OUTLINE &&
   7.904 -        entrytype != PGL_ENTRY_POLYGON
   7.905 -      ) continue;
   7.906 -      /* get latitude and longitude of next vertex */
   7.907 -      lat2 = points[k].lat;
   7.908 -      lon2 = points[k].lon;
   7.909 -      /* consider longitude wrap-around for next vertex */
   7.910 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
   7.911 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
   7.912 -      /* go to next vertex and edge if edge is degenerated */
   7.913 -      if (lat1 == lat2 && lon1 == lon2) continue;
   7.914 -      /* otherwise test if point can be projected onto edge of polygon */
   7.915 -      s = (
   7.916 -        ((lat0-lat1) * (lat2-lat1) + comp * (lon0-lon1) * (lon2-lon1)) /
   7.917 -        ((lat2-lat1) * (lat2-lat1) + comp * (lon2-lon1) * (lon2-lon1))
   7.918 -      );
   7.919 -      /* go to next vertex and edge if point cannot be projected */
   7.920 -      if (!(s > 0 && s < 1)) continue;
   7.921 -      /* calculate distance from original point to projected point */
   7.922 -      dist = pgl_distance(
   7.923 -        lat0, lon0,
   7.924 -        lat1 + s * (lat2-lat1),
   7.925 -        lon1 + s * (lon2-lon1)
   7.926 -      );
   7.927 -      /* store calculated distance if smallest */
   7.928 -      if (dist < min_dist) min_dist = dist;
   7.929 -    }
   7.930 -  }
   7.931 -  /* return minimum distance */
   7.932 -  return min_dist;
   7.933 -}
   7.934 -
   7.935 -/* calculate (approximate) distance between two clusters */
   7.936 -static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) {
   7.937 -  int i, j;                    /* i: entry, j: point in entry */
   7.938 -  int npoints;                 /* number of points in entry */
   7.939 -  pgl_point *points;           /* array of points in entry */
   7.940 -  double dist;                 /* distance calculated in one step */
   7.941 -  double min_dist = INFINITY;  /* minimum distance */
   7.942 -  /* consider distance from each point in one cluster to the whole other */
   7.943 -  for (i=0; i<cluster1->nentries; i++) {
   7.944 -    npoints = cluster1->entries[i].npoints;
   7.945 -    points = PGL_ENTRY_POINTS(cluster1, i);
   7.946 -    for (j=0; j<npoints; j++) {
   7.947 -      dist = pgl_point_cluster_distance(points+j, cluster2);
   7.948 -      if (dist == 0) return dist;
   7.949 -      if (dist < min_dist) min_dist = dist;
   7.950 -    }
   7.951 -  }
   7.952 -  /* consider distance from each point in other cluster to the first cluster */
   7.953 -  for (i=0; i<cluster2->nentries; i++) {
   7.954 -    npoints = cluster2->entries[i].npoints;
   7.955 -    points = PGL_ENTRY_POINTS(cluster2, i);
   7.956 -    for (j=0; j<npoints; j++) {
   7.957 -      dist = pgl_point_cluster_distance(points+j, cluster1);
   7.958 -      if (dist == 0) return dist;
   7.959 -      if (dist < min_dist) min_dist = dist;
   7.960 -    }
   7.961 -  }
   7.962 -  return min_dist;
   7.963 -}
   7.964 -
   7.965 -/* estimator function for distance between box and point */
   7.966 -/* always returns a smaller value than actually correct or zero */
   7.967 -static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
   7.968 -  double dlon;      /* longitude range of box (delta longitude) */
   7.969 -  double distance;  /* return value */
   7.970 -  /* return infinity if box is empty */
   7.971 -  if (box->lat_min > box->lat_max) return INFINITY;
   7.972 -  /* return zero if point is inside box */
   7.973 -  if (pgl_point_in_box(point, box)) return 0;
   7.974 -  /* calculate delta longitude */
   7.975 -  dlon = box->lon_max - box->lon_min;
   7.976 -  if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
   7.977 -  /* if delta longitude is greater than 150 degrees, perform safe fall-back */
   7.978 -  if (dlon > 150) return 0;
   7.979 -  /* calculate lower limit for distance (formula below requires dlon <= 150) */
   7.980 -  /* TODO: provide better estimation function to improve performance */
   7.981 -  distance = (
   7.982 -    (1.0-PGL_SPHEROID_F) *  /* safety margin due to flattening and approx. */
   7.983 -    pgl_distance(
   7.984 -      point->lat,
   7.985 -      point->lon,
   7.986 -      (box->lat_min + box->lat_max) / 2,
   7.987 -      box->lon_min + dlon/2
   7.988 -    )
   7.989 -  ) - pgl_distance(
   7.990 -    box->lat_min, box->lon_min,
   7.991 -    box->lat_max, box->lon_max
   7.992 -  );
   7.993 -  /* truncate negative results to zero */
   7.994 -  if (distance <= 0) distance = 0;
   7.995 -  /* return result */
   7.996 -  return distance;
   7.997 -}
   7.998 -
   7.999 -
  7.1000 -/*------------------------------------------------------------*
  7.1001 - *  Functions using numerical integration (Monte Carlo like)  *
  7.1002 - *------------------------------------------------------------*/
  7.1003 -
  7.1004 -/* half of (spherical) earth's surface area */
  7.1005 -#define PGL_HALF_SURFACE (PGL_RADIUS * PGL_DIAMETER * M_PI)
  7.1006 -
  7.1007 -/* golden angle in radians */
  7.1008 -#define PGL_GOLDEN_ANGLE (M_PI * (sqrt(5) - 1.0))
  7.1009 -
  7.1010 -/* create a list of sample points covering a bounding circle 
  7.1011 -   and return covered area */
  7.1012 -static double pgl_sample_points(
  7.1013 -  pgl_point *center,  /* center of bounding circle */
  7.1014 -  double radius,      /* radius of bounding circle */
  7.1015 -  int samples,        /* number of sample points (MUST be positive!) */
  7.1016 -  pgl_point *result   /* pointer to result array */
  7.1017 -) {
  7.1018 -  double double_share = 2.0;  /* double of covered share of earth's surface */
  7.1019 -  double double_share_div_samples;  /* double_share divided by sample count */
  7.1020 -  int i;
  7.1021 -  double t;  /* parameter of spiral laid on (spherical) earth's surface */
  7.1022 -  double x, y, z;  /* normalized coordinates of point on non-rotated spiral */
  7.1023 -  double sin_phi;  /* sine of sph. coordinate of point of non-rotated spiral */
  7.1024 -  double lambda;   /* other sph. coordinate of point of non-rotated spiral */
  7.1025 -  double rot = (0.5 - center->lat / 180.0) * M_PI;  /* needed rot. (in rad) */
  7.1026 -  double cos_rot = cos(rot);  /* cosine of rotation by latitude */
  7.1027 -  double sin_rot = sin(rot);  /* sine of rotation by latitude */
  7.1028 -  double x_rot, z_rot;  /* normalized coordinates of point on rotated spiral */
  7.1029 -  double center_lon = center->lon;  /* second rotation in degree */
  7.1030 -  /* add safety margin to bounding circle because of spherical approximation */
  7.1031 -  radius *= PGL_SPHEROID_A / PGL_RADIUS;
  7.1032 -  /* if whole earth is covered, use initialized value, otherwise calculate
  7.1033 -     share of covered area (multiplied by 2) */
  7.1034 -  if (radius < PGL_MAXDIST) double_share = 1.0 - cos(radius / PGL_RADIUS);
  7.1035 -  /* divide double_share by sample count for later calculations */
  7.1036 -  double_share_div_samples = double_share / samples;
  7.1037 -  /* generate sample points */
  7.1038 -  for (i=0; i<samples; i++) {
  7.1039 -    /* use an offset of 1/2 to avoid too dense clustering at spiral center */
  7.1040 -    t = 0.5 + i;
  7.1041 -    /* calculate normalized coordinates of point on non-rotated spiral */
  7.1042 -    z = 1.0 - double_share_div_samples * t;
  7.1043 -    sin_phi = sqrt(1.0 - z*z);
  7.1044 -    lambda = t * PGL_GOLDEN_ANGLE;
  7.1045 -    x = sin_phi * cos(lambda);
  7.1046 -    y = sin_phi * sin(lambda);
  7.1047 -    /* rotate spiral by latitude value of bounding circle */
  7.1048 -    x_rot = cos_rot * x + sin_rot * z;
  7.1049 -    z_rot = cos_rot * z - sin_rot * x;
  7.1050 -    /* set resulting sample point in result array */
  7.1051 -    /* (while performing second rotation by bounding circle longitude) */
  7.1052 -    result[i].lat = 180.0 * (atan(z_rot / fabs(x_rot)) / M_PI);
  7.1053 -    result[i].lon = center_lon + 180.0 * (atan2(y, x_rot) / M_PI);
  7.1054 -  }
  7.1055 -  /* return covered area */
  7.1056 -  return PGL_HALF_SURFACE * double_share;
  7.1057 -}
  7.1058 -
  7.1059 -/* fair distance between point and cluster (see README file for explanation) */
  7.1060 -/* NOTE: sample count passed as third argument MUST be positive */
  7.1061 -static double pgl_fair_distance(
  7.1062 -  pgl_point *point, pgl_cluster *cluster, int samples
  7.1063 -) {
  7.1064 -  double distance;       /* shortest distance from point to cluster */
  7.1065 -  pgl_point *points;     /* sample points for numerical integration */
  7.1066 -  double area;           /* area covered by sample points */
  7.1067 -  int i;
  7.1068 -  int inner = 0;         /* number of sample points within cluster */
  7.1069 -  int outer = 0;         /* number of sample points outside cluster but
  7.1070 -                            within cluster enlarged by distance */
  7.1071 -  double result;
  7.1072 -  /* calculate shortest distance from point to cluster */
  7.1073 -  distance = pgl_point_cluster_distance(point, cluster);
  7.1074 -  /* if cluster consists of a single point or has no bounding circle with
  7.1075 -      positive radius, simply return distance */
  7.1076 -  if (
  7.1077 -    (cluster->nentries==1 && cluster->entries[0].entrytype==PGL_ENTRY_POINT) ||
  7.1078 -    !(cluster->bounding.radius > 0)
  7.1079 -  ) return distance;
  7.1080 -  /* if cluster consists of two points which are twice as far apart, return
  7.1081 -     distance between point and cluster multiplied by square root of two */
  7.1082 -  if (
  7.1083 -    cluster->nentries == 2 &&
  7.1084 -    cluster->entries[0].entrytype == PGL_ENTRY_POINT &&
  7.1085 -    cluster->entries[1].entrytype == PGL_ENTRY_POINT &&
  7.1086 -    pgl_distance(
  7.1087 -      PGL_ENTRY_POINTS(cluster, 0)[0].lat,
  7.1088 -      PGL_ENTRY_POINTS(cluster, 0)[0].lon,
  7.1089 -      PGL_ENTRY_POINTS(cluster, 1)[0].lat,
  7.1090 -      PGL_ENTRY_POINTS(cluster, 1)[0].lon
  7.1091 -    ) >= 2.0 * distance
  7.1092 -  ) {
  7.1093 -    return distance * M_SQRT2;
  7.1094 -  }
  7.1095 -  /* otherwise create sample points for numerical integration and determine
  7.1096 -     area covered by sample points */
  7.1097 -  points = palloc(samples * sizeof(pgl_point));
  7.1098 -  area = pgl_sample_points(
  7.1099 -    &cluster->bounding.center,
  7.1100 -    cluster->bounding.radius + distance,  /* pad bounding circle by distance */
  7.1101 -    samples,
  7.1102 -    points
  7.1103 -  );
  7.1104 -  /* perform numerical integration */
  7.1105 -  if (distance > 0) {
  7.1106 -    /* point (that was passed as argument) is outside cluster */
  7.1107 -    for (i=0; i<samples; i++) {
  7.1108 -      /* count sample points within cluster */
  7.1109 -      if (pgl_point_in_cluster(points+i, cluster, true)) inner++;
  7.1110 -      /* count sample points outside of cluster but within cluster enlarged by
  7.1111 -         distance between point (that was passed as argument) and cluster */
  7.1112 -      else if (
  7.1113 -        pgl_point_cluster_distance(points+i, cluster) < distance
  7.1114 -      ) outer++;
  7.1115 -    }
  7.1116 -  } else {
  7.1117 -    /* if point is within cluster, just count sample points within cluster */
  7.1118 -    for (i=0; i<samples; i++) {
  7.1119 -      if (pgl_point_in_cluster(points+i, cluster, true)) inner++;
  7.1120 -    }
  7.1121 -  }
  7.1122 -  /* release memory for sample points needed for numerical integration */
  7.1123 -  pfree(points);
  7.1124 -  /* if enlargement was less than doubling the area, then combine inner and
  7.1125 -     outer sample point counts with different weighting */
  7.1126 -  /* (ensures fairness in such a way that the integral of the squared result
  7.1127 -     over all possible point parameters is independent of the cluster) */
  7.1128 -  if (outer < inner) result = (2*inner + 4*outer) / 3.0;
  7.1129 -  /* otherwise weigh inner and outer points the same */
  7.1130 -  else result = inner + outer;
  7.1131 -  /* convert area into distance (i.e. radius of a circle with the same area) */
  7.1132 -  result = sqrt(area * (result / samples) / M_PI);
  7.1133 -  /* return result only if it is greater than the distance between point and
  7.1134 -     cluster to avoid unexpected results because of errors due to limited
  7.1135 -     precision */
  7.1136 -  if (result > distance) return result;
  7.1137 -  /* otherwise return distance between point and cluster */
  7.1138 -  else return distance;
  7.1139 -}
  7.1140 -
  7.1141 -
  7.1142 -/*-------------------------------------------------*
  7.1143 - *  geographic index based on space-filling curve  *
  7.1144 - *-------------------------------------------------*/
  7.1145 -
  7.1146 -/* number of bytes used for geographic (center) position in keys */
  7.1147 -#define PGL_KEY_LATLON_BYTELEN 7
  7.1148 -
  7.1149 -/* maximum reference value for logarithmic size of geographic objects */
  7.1150 -#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
  7.1151 -
  7.1152 -/* pointer to index key (either pgl_pointkey or pgl_areakey) */
  7.1153 -typedef unsigned char *pgl_keyptr;
  7.1154 -
  7.1155 -/* index key for points (objects with zero area) on the spheroid */
  7.1156 -/* bit  0..55: interspersed bits of latitude and longitude,
  7.1157 -   bit 56..57: always zero,
  7.1158 -   bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */
  7.1159 -typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1];
  7.1160 -
  7.1161 -/* index key for geographic objects on spheroid with area greater than zero */
  7.1162 -/* bit  0..55: interspersed bits of latitude and longitude of center point,
  7.1163 -   bit     56: always set to 1,
  7.1164 -   bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.),
  7.1165 -   bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to
  7.1166 -               PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth
  7.1167 -               = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL
  7.1168 -               (with interspersed bits = 0 and node depth = 0) for keys which
  7.1169 -               cover both empty and non-empty objects */
  7.1170 -
  7.1171 -typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2];
  7.1172 -
  7.1173 -/* helper macros for reading/writing index keys */
  7.1174 -#define PGL_KEY_NODEDEPTH_OFFSET  PGL_KEY_LATLON_BYTELEN
  7.1175 -#define PGL_KEY_OBJSIZE_OFFSET    (PGL_KEY_NODEDEPTH_OFFSET+1)
  7.1176 -#define PGL_POINTKEY_MAXDEPTH     (PGL_KEY_LATLON_BYTELEN*8)
  7.1177 -#define PGL_AREAKEY_MAXDEPTH      (2*PGL_POINTKEY_MAXDEPTH+1)
  7.1178 -#define PGL_AREAKEY_MAXOBJSIZE    (PGL_POINTKEY_MAXDEPTH+1)
  7.1179 -#define PGL_AREAKEY_TYPEMASK      0x80
  7.1180 -#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8)))
  7.1181 -#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \
  7.1182 -                                  ( PGL_KEY_LATLONBIT(key1, n) ^ \
  7.1183 -                                    PGL_KEY_LATLONBIT(key2, n) )
  7.1184 -#define PGL_KEY_IS_AREAKEY(key)   ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
  7.1185 -                                    PGL_AREAKEY_TYPEMASK)
  7.1186 -#define PGL_KEY_NODEDEPTH(key)    ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
  7.1187 -                                    (PGL_AREAKEY_TYPEMASK-1))
  7.1188 -#define PGL_KEY_OBJSIZE(key)      ((key)[PGL_KEY_OBJSIZE_OFFSET])
  7.1189 -#define PGL_KEY_OBJSIZE_EMPTY     126
  7.1190 -#define PGL_KEY_OBJSIZE_UNIVERSAL 127
  7.1191 -#define PGL_KEY_IS_EMPTY(key)     ( PGL_KEY_IS_AREAKEY(key) && \
  7.1192 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
  7.1193 -                                    PGL_KEY_OBJSIZE_EMPTY )
  7.1194 -#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \
  7.1195 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
  7.1196 -                                    PGL_KEY_OBJSIZE_UNIVERSAL )
  7.1197 -
  7.1198 -/* set area key to match empty objects only */
  7.1199 -static void pgl_key_set_empty(pgl_keyptr key) {
  7.1200 -  memset(key, 0, sizeof(pgl_areakey));
  7.1201 -  /* Note: setting node depth to maximum is required for picksplit function */
  7.1202 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
  7.1203 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY;
  7.1204 -}
  7.1205 -
  7.1206 -/* set area key to match any object (including empty objects) */
  7.1207 -static void pgl_key_set_universal(pgl_keyptr key) {
  7.1208 -  memset(key, 0, sizeof(pgl_areakey));
  7.1209 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK;
  7.1210 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL;
  7.1211 -}
  7.1212 -
  7.1213 -/* convert a point on earth into a max-depth key to be used in index */
  7.1214 -static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) {
  7.1215 -  double lat = point->lat;
  7.1216 -  double lon = point->lon;
  7.1217 -  int i;
  7.1218 -  /* clear latitude and longitude bits */
  7.1219 -  memset(key, 0, PGL_KEY_LATLON_BYTELEN);
  7.1220 -  /* set node depth to maximum and type bit to zero */
  7.1221 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH;
  7.1222 -  /* iterate over all latitude/longitude bit pairs */
  7.1223 -  for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) {
  7.1224 -    /* determine latitude bit */
  7.1225 -    if (lat >= 0) {
  7.1226 -      key[i/4] |= 0x80 >> (2*(i%4));
  7.1227 -      lat *= 2; lat -= 90;
  7.1228 -    } else {
  7.1229 -      lat *= 2; lat += 90;
  7.1230 -    }
  7.1231 -    /* determine longitude bit */
  7.1232 -    if (lon >= 0) {
  7.1233 -      key[i/4] |= 0x80 >> (2*(i%4)+1);
  7.1234 -      lon *= 2; lon -= 180;
  7.1235 -    } else {
  7.1236 -      lon *= 2; lon += 180;
  7.1237 -    }
  7.1238 -  }
  7.1239 -}
  7.1240 -
  7.1241 -/* convert a circle on earth into a max-depth key to be used in an index */
  7.1242 -static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) {
  7.1243 -  /* handle special case of empty circle */
  7.1244 -  if (circle->radius < 0) {
  7.1245 -    pgl_key_set_empty(key);
  7.1246 -    return;
  7.1247 -  }
  7.1248 -  /* perform same action as for point keys */
  7.1249 -  pgl_point_to_key(&(circle->center), key);
  7.1250 -  /* but overwrite type and node depth to fit area index key */
  7.1251 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
  7.1252 -  /* check if radius is greater than (or equal to) reference size */
  7.1253 -  /* (treat equal values as greater values for numerical safety) */
  7.1254 -  if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) {
  7.1255 -    /* if yes, set logarithmic size to zero */
  7.1256 -    key[PGL_KEY_OBJSIZE_OFFSET] = 0;
  7.1257 -  } else {
  7.1258 -    /* otherwise, determine logarithmic size iteratively */
  7.1259 -    /* (one step is equivalent to a factor of sqrt(2)) */
  7.1260 -    double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2;
  7.1261 -    int objsize = 1;
  7.1262 -    while (objsize < PGL_AREAKEY_MAXOBJSIZE) {
  7.1263 -      /* stop when radius is greater than (or equal to) adjusted reference */
  7.1264 -      /* (treat equal values as greater values for numerical safety) */
  7.1265 -      if (circle->radius >= reference) break;
  7.1266 -      reference /= M_SQRT2;
  7.1267 -      objsize++;
  7.1268 -    }
  7.1269 -    /* set logarithmic size to determined value */
  7.1270 -    key[PGL_KEY_OBJSIZE_OFFSET] = objsize;
  7.1271 -  }
  7.1272 -}
  7.1273 -
  7.1274 -/* check if one key is subkey of another key or vice versa */
  7.1275 -static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) {
  7.1276 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
  7.1277 -  /* determine smallest depth */
  7.1278 -  int depth1 = PGL_KEY_NODEDEPTH(key1);
  7.1279 -  int depth2 = PGL_KEY_NODEDEPTH(key2);
  7.1280 -  int depth = (depth1 < depth2) ? depth1 : depth2;
  7.1281 -  /* check if keys are area keys (assuming that both keys have same type) */
  7.1282 -  if (PGL_KEY_IS_AREAKEY(key1)) {
  7.1283 -    int j = 0;  /* bit offset for logarithmic object size bits */
  7.1284 -    int k = 0;  /* bit offset for latitude and longitude */
  7.1285 -    /* fetch logarithmic object size information */
  7.1286 -    int objsize1 = PGL_KEY_OBJSIZE(key1);
  7.1287 -    int objsize2 = PGL_KEY_OBJSIZE(key2);
  7.1288 -    /* handle special cases for empty objects (universal and empty keys) */
  7.1289 -    if (
  7.1290 -      objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL ||
  7.1291 -      objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL
  7.1292 -    ) return true;
  7.1293 -    if (
  7.1294 -      objsize1 == PGL_KEY_OBJSIZE_EMPTY ||
  7.1295 -      objsize2 == PGL_KEY_OBJSIZE_EMPTY
  7.1296 -    ) return objsize1 == objsize2;
  7.1297 -    /* iterate through key bits */
  7.1298 -    for (i=0; i<depth; i++) {
  7.1299 -      /* every second bit is a bit describing the object size */
  7.1300 -      if (i%2 == 0) {
  7.1301 -        /* check if object size bit is different in both keys (objsize1 and
  7.1302 -           objsize2 describe the minimum index when object size bit is set) */
  7.1303 -        if (
  7.1304 -          (objsize1 <= j && objsize2 > j) ||
  7.1305 -          (objsize2 <= j && objsize1 > j)
  7.1306 -        ) {
  7.1307 -          /* bit differs, therefore keys are in separate branches */
  7.1308 -          return false;
  7.1309 -        }
  7.1310 -        /* increase bit counter for object size bits */
  7.1311 -        j++;
  7.1312 -      }
  7.1313 -      /* all other bits describe latitude and longitude */
  7.1314 -      else {
  7.1315 -        /* check if bit differs in both keys */
  7.1316 -        if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) {
  7.1317 -          /* bit differs, therefore keys are in separate branches */
  7.1318 -          return false;
  7.1319 -        }
  7.1320 -        /* increase bit counter for latitude/longitude bits */
  7.1321 -        k++;
  7.1322 -      }
  7.1323 -    }
  7.1324 -  }
  7.1325 -  /* if not, keys are point keys */
  7.1326 -  else {
  7.1327 -    /* iterate through key bits */
  7.1328 -    for (i=0; i<depth; i++) {
  7.1329 -      /* check if bit differs in both keys */
  7.1330 -      if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) {
  7.1331 -        /* bit differs, therefore keys are in separate branches */
  7.1332 -        return false;
  7.1333 -      }
  7.1334 -    }
  7.1335 -  }
  7.1336 -  /* return true because keys are in the same branch */
  7.1337 -  return true;
  7.1338 -}
  7.1339 -
  7.1340 -/* combine two keys into new key which covers both original keys */
  7.1341 -/* (result stored in first argument) */
  7.1342 -static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) {
  7.1343 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
  7.1344 -  /* determine smallest depth */
  7.1345 -  int depth1 = PGL_KEY_NODEDEPTH(dst);
  7.1346 -  int depth2 = PGL_KEY_NODEDEPTH(src);
  7.1347 -  int depth = (depth1 < depth2) ? depth1 : depth2;
  7.1348 -  /* check if keys are area keys (assuming that both keys have same type) */
  7.1349 -  if (PGL_KEY_IS_AREAKEY(dst)) {
  7.1350 -    pgl_areakey dstbuf = { 0, };  /* destination buffer (cleared) */
  7.1351 -    int j = 0;  /* bit offset for logarithmic object size bits */
  7.1352 -    int k = 0;  /* bit offset for latitude and longitude */
  7.1353 -    /* fetch logarithmic object size information */
  7.1354 -    int objsize1 = PGL_KEY_OBJSIZE(dst);
  7.1355 -    int objsize2 = PGL_KEY_OBJSIZE(src);
  7.1356 -    /* handle special cases for empty objects (universal and empty keys) */
  7.1357 -    if (
  7.1358 -      objsize1 > PGL_AREAKEY_MAXOBJSIZE ||
  7.1359 -      objsize2 > PGL_AREAKEY_MAXOBJSIZE
  7.1360 -    ) {
  7.1361 -      if (
  7.1362 -        objsize1 == PGL_KEY_OBJSIZE_EMPTY &&
  7.1363 -        objsize2 == PGL_KEY_OBJSIZE_EMPTY
  7.1364 -      ) pgl_key_set_empty(dst);
  7.1365 -      else pgl_key_set_universal(dst);
  7.1366 -      return;
  7.1367 -    }
  7.1368 -    /* iterate through key bits */
  7.1369 -    for (i=0; i<depth; i++) {
  7.1370 -      /* every second bit is a bit describing the object size */
  7.1371 -      if (i%2 == 0) {
  7.1372 -        /* increase bit counter for object size bits first */
  7.1373 -        /* (handy when setting objsize variable) */
  7.1374 -        j++;
  7.1375 -        /* check if object size bit is set in neither key */
  7.1376 -        if (objsize1 >= j && objsize2 >= j) {
  7.1377 -          /* set objsize in destination buffer to indicate that size bit is
  7.1378 -             unset in destination buffer at the current bit position */
  7.1379 -          dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j;
  7.1380 -        }
  7.1381 -        /* break if object size bit is set in one key only */
  7.1382 -        else if (objsize1 >= j || objsize2 >= j) break;
  7.1383 -      }
  7.1384 -      /* all other bits describe latitude and longitude */
  7.1385 -      else {
  7.1386 -        /* break if bit differs in both keys */
  7.1387 -        if (PGL_KEY_LATLONBIT(dst, k)) {
  7.1388 -          if (!PGL_KEY_LATLONBIT(src, k)) break;
  7.1389 -          /* but set bit in destination buffer if bit is set in both keys */
  7.1390 -          dstbuf[k/8] |= 0x80 >> (k%8);
  7.1391 -        } else if (PGL_KEY_LATLONBIT(src, k)) break;
  7.1392 -        /* increase bit counter for latitude/longitude bits */
  7.1393 -        k++;
  7.1394 -      }
  7.1395 -    }
  7.1396 -    /* set common node depth and type bit (type bit = 1) */
  7.1397 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i;
  7.1398 -    /* copy contents of destination buffer to first key */
  7.1399 -    memcpy(dst, dstbuf, sizeof(pgl_areakey));
  7.1400 -  }
  7.1401 -  /* if not, keys are point keys */
  7.1402 -  else {
  7.1403 -    pgl_pointkey dstbuf = { 0, };  /* destination buffer (cleared) */
  7.1404 -    /* iterate through key bits */
  7.1405 -    for (i=0; i<depth; i++) {
  7.1406 -      /* break if bit differs in both keys */
  7.1407 -      if (PGL_KEY_LATLONBIT(dst, i)) {
  7.1408 -        if (!PGL_KEY_LATLONBIT(src, i)) break;
  7.1409 -        /* but set bit in destination buffer if bit is set in both keys */
  7.1410 -        dstbuf[i/8] |= 0x80 >> (i%8);
  7.1411 -      } else if (PGL_KEY_LATLONBIT(src, i)) break;
  7.1412 -    }
  7.1413 -    /* set common node depth (type bit = 0) */
  7.1414 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i;
  7.1415 -    /* copy contents of destination buffer to first key */
  7.1416 -    memcpy(dst, dstbuf, sizeof(pgl_pointkey));
  7.1417 -  }
  7.1418 -}
  7.1419 -
  7.1420 -/* determine center(!) boundaries and radius estimation of index key */
  7.1421 -static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) {
  7.1422 -  int i;
  7.1423 -  /* determine node depth */
  7.1424 -  int depth = PGL_KEY_NODEDEPTH(key);
  7.1425 -  /* center point of possible result */
  7.1426 -  double lat = 0;
  7.1427 -  double lon = 0;
  7.1428 -  /* maximum distance of real center point from key center */
  7.1429 -  double dlat = 90;
  7.1430 -  double dlon = 180;
  7.1431 -  /* maximum radius of contained objects */
  7.1432 -  double radius = 0;  /* always return zero for point index keys */
  7.1433 -  /* check if key is area key */
  7.1434 -  if (PGL_KEY_IS_AREAKEY(key)) {
  7.1435 -    /* get logarithmic object size */
  7.1436 -    int objsize = PGL_KEY_OBJSIZE(key);
  7.1437 -    /* handle special cases for empty objects (universal and empty keys) */
  7.1438 -    if (objsize == PGL_KEY_OBJSIZE_EMPTY) {
  7.1439 -      pgl_box_set_empty(box);
  7.1440 -      return 0;
  7.1441 -    } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) {
  7.1442 -      box->lat_min = -90;
  7.1443 -      box->lat_max =  90;
  7.1444 -      box->lon_min = -180;
  7.1445 -      box->lon_max =  180;
  7.1446 -      return 0;  /* any value >= 0 would do */
  7.1447 -    }
  7.1448 -    /* calculate maximum possible radius of objects covered by the given key */
  7.1449 -    if (objsize == 0) radius = INFINITY;
  7.1450 -    else {
  7.1451 -      radius = PGL_AREAKEY_REFOBJSIZE;
  7.1452 -      while (--objsize) radius /= M_SQRT2;
  7.1453 -    }
  7.1454 -    /* iterate over latitude and longitude bits in key */
  7.1455 -    /* (every second bit is a latitude or longitude bit) */
  7.1456 -    for (i=0; i<depth/2; i++) {
  7.1457 -      /* check if latitude bit */
  7.1458 -      if (i%2 == 0) {
  7.1459 -        /* cut latitude dimension in half */
  7.1460 -        dlat /= 2;
  7.1461 -        /* increase center latitude if bit is 1, otherwise decrease */
  7.1462 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  7.1463 -        else lat -= dlat;
  7.1464 -      }
  7.1465 -      /* otherwise longitude bit */
  7.1466 -      else {
  7.1467 -        /* cut longitude dimension in half */
  7.1468 -        dlon /= 2;
  7.1469 -        /* increase center longitude if bit is 1, otherwise decrease */
  7.1470 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  7.1471 -        else lon -= dlon;
  7.1472 -      }
  7.1473 -    }
  7.1474 -  }
  7.1475 -  /* if not, keys are point keys */
  7.1476 -  else {
  7.1477 -    /* iterate over all bits in key */
  7.1478 -    for (i=0; i<depth; i++) {
  7.1479 -      /* check if latitude bit */
  7.1480 -      if (i%2 == 0) {
  7.1481 -        /* cut latitude dimension in half */
  7.1482 -        dlat /= 2;
  7.1483 -        /* increase center latitude if bit is 1, otherwise decrease */
  7.1484 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  7.1485 -        else lat -= dlat;
  7.1486 -      }
  7.1487 -      /* otherwise longitude bit */
  7.1488 -      else {
  7.1489 -        /* cut longitude dimension in half */
  7.1490 -        dlon /= 2;
  7.1491 -        /* increase center longitude if bit is 1, otherwise decrease */
  7.1492 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  7.1493 -        else lon -= dlon;
  7.1494 -      }
  7.1495 -    }
  7.1496 -  }
  7.1497 -  /* calculate boundaries from center point and remaining dlat and dlon */
  7.1498 -  /* (return values through pointer to box) */
  7.1499 -  box->lat_min = lat - dlat;
  7.1500 -  box->lat_max = lat + dlat;
  7.1501 -  box->lon_min = lon - dlon;
  7.1502 -  box->lon_max = lon + dlon;
  7.1503 -  /* return radius (as a function return value) */
  7.1504 -  return radius;
  7.1505 -}
  7.1506 -
  7.1507 -/* estimator function for distance between point and index key */
  7.1508 -/* always returns a smaller value than actually correct or zero */
  7.1509 -static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
  7.1510 -  pgl_box box;  /* center(!) bounding box of area index key */
  7.1511 -  /* calculate center(!) bounding box and maximum radius of objects covered
  7.1512 -     by area index key (radius is zero for point index keys) */
  7.1513 -  double distance = pgl_key_to_box(key, &box);
  7.1514 -  /* calculate estimated distance between bounding box of center point of
  7.1515 -     indexed object and point passed as second argument, then substract maximum
  7.1516 -     radius of objects covered by index key */
  7.1517 -  distance = pgl_estimate_point_box_distance(point, &box) - distance;
  7.1518 -  /* truncate negative results to zero */
  7.1519 -  if (distance <= 0) distance = 0;
  7.1520 -  /* return result */
  7.1521 -  return distance;
  7.1522 -}
  7.1523 -
  7.1524 -
  7.1525 -/*---------------------------------*
  7.1526 - *  helper functions for text I/O  *
  7.1527 - *---------------------------------*/
  7.1528 -
  7.1529 -#define PGL_NUMBUFLEN 64  /* buffer size for number to string conversion */
  7.1530 -
  7.1531 -/* convert floating point number to string (round-trip safe) */
  7.1532 -static void pgl_print_float(char *buf, double flt) {
  7.1533 -  /* check if number is integral */
  7.1534 -  if (trunc(flt) == flt) {
  7.1535 -    /* for integral floats use maximum precision */
  7.1536 -    snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  7.1537 -  } else {
  7.1538 -    /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */
  7.1539 -    snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt);
  7.1540 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt);
  7.1541 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  7.1542 -  }
  7.1543 -}
  7.1544 -
  7.1545 -/* convert latitude floating point number (in degrees) to string */
  7.1546 -static void pgl_print_lat(char *buf, double lat) {
  7.1547 -  if (signbit(lat)) {
  7.1548 -    /* treat negative latitudes (including -0) as south */
  7.1549 -    snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat);
  7.1550 -  } else {
  7.1551 -    /* treat positive latitudes (including +0) as north */
  7.1552 -    snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat);
  7.1553 -  }
  7.1554 -}
  7.1555 -
  7.1556 -/* convert longitude floating point number (in degrees) to string */
  7.1557 -static void pgl_print_lon(char *buf, double lon) {
  7.1558 -  if (signbit(lon)) {
  7.1559 -    /* treat negative longitudes (including -0) as west */
  7.1560 -    snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon);
  7.1561 -  } else {
  7.1562 -    /* treat positive longitudes (including +0) as east */
  7.1563 -    snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon);
  7.1564 -  }
  7.1565 -}
  7.1566 -
  7.1567 -/* bit masks used as return value of pgl_scan() function */
  7.1568 -#define PGL_SCAN_NONE 0      /* no value has been parsed */
  7.1569 -#define PGL_SCAN_LAT (1<<0)  /* latitude has been parsed */
  7.1570 -#define PGL_SCAN_LON (1<<1)  /* longitude has been parsed */
  7.1571 -#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON)  /* bitwise OR of both */
  7.1572 -
  7.1573 -/* parse a coordinate (can be latitude or longitude) */
  7.1574 -static int pgl_scan(char **str, double *lat, double *lon) {
  7.1575 -  double val;
  7.1576 -  int len;
  7.1577 -  if (
  7.1578 -    sscanf(*str, " N %lf %n", &val, &len) ||
  7.1579 -    sscanf(*str, " n %lf %n", &val, &len)
  7.1580 -  ) {
  7.1581 -    *str += len; *lat = val; return PGL_SCAN_LAT;
  7.1582 -  }
  7.1583 -  if (
  7.1584 -    sscanf(*str, " S %lf %n", &val, &len) ||
  7.1585 -    sscanf(*str, " s %lf %n", &val, &len)
  7.1586 -  ) {
  7.1587 -    *str += len; *lat = -val; return PGL_SCAN_LAT;
  7.1588 -  }
  7.1589 -  if (
  7.1590 -    sscanf(*str, " E %lf %n", &val, &len) ||
  7.1591 -    sscanf(*str, " e %lf %n", &val, &len)
  7.1592 -  ) {
  7.1593 -    *str += len; *lon = val; return PGL_SCAN_LON;
  7.1594 -  }
  7.1595 -  if (
  7.1596 -    sscanf(*str, " W %lf %n", &val, &len) ||
  7.1597 -    sscanf(*str, " w %lf %n", &val, &len)
  7.1598 -  ) {
  7.1599 -    *str += len; *lon = -val; return PGL_SCAN_LON;
  7.1600 -  }
  7.1601 -  return PGL_SCAN_NONE;
  7.1602 -}
  7.1603 -
  7.1604 -
  7.1605 -/*-----------------*
  7.1606 - *  SQL functions  *
  7.1607 - *-----------------*/
  7.1608 -
  7.1609 -/* Note: These function names use "epoint", "ebox", etc. notation here instead
  7.1610 -   of "point", "box", etc. in order to distinguish them from any previously
  7.1611 -   defined functions. */
  7.1612 -
  7.1613 -/* function needed for dummy types and/or not implemented features */
  7.1614 -PG_FUNCTION_INFO_V1(pgl_notimpl);
  7.1615 -Datum pgl_notimpl(PG_FUNCTION_ARGS) {
  7.1616 -  ereport(ERROR, (errmsg("not implemented by pgLatLon")));
  7.1617 -}
  7.1618 -
  7.1619 -/* set point to latitude and longitude (including checks) */
  7.1620 -static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) {
  7.1621 -  /* reject infinite or NaN values */
  7.1622 -  if (!isfinite(lat) || !isfinite(lon)) {
  7.1623 -    ereport(ERROR, (
  7.1624 -      errcode(ERRCODE_DATA_EXCEPTION),
  7.1625 -      errmsg("epoint requires finite coordinates")
  7.1626 -    ));
  7.1627 -  }
  7.1628 -  /* check latitude bounds */
  7.1629 -  if (lat < -90) {
  7.1630 -    ereport(WARNING, (errmsg("latitude exceeds south pole")));
  7.1631 -    lat = -90;
  7.1632 -  } else if (lat > 90) {
  7.1633 -    ereport(WARNING, (errmsg("latitude exceeds north pole")));
  7.1634 -    lat = 90;
  7.1635 -  }
  7.1636 -  /* check longitude bounds */
  7.1637 -  if (lon < -180) {
  7.1638 -    ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized")));
  7.1639 -    lon += 360 - trunc(lon / 360) * 360;
  7.1640 -  } else if (lon > 180) {
  7.1641 -    ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized")));
  7.1642 -    lon -= 360 + trunc(lon / 360) * 360;
  7.1643 -  }
  7.1644 -  /* store rounded latitude/longitude values for round-trip safety */
  7.1645 -  point->lat = pgl_round(lat);
  7.1646 -  point->lon = pgl_round(lon);
  7.1647 -}
  7.1648 -
  7.1649 -/* create point ("epoint" in SQL) from latitude and longitude */
  7.1650 -PG_FUNCTION_INFO_V1(pgl_create_epoint);
  7.1651 -Datum pgl_create_epoint(PG_FUNCTION_ARGS) {
  7.1652 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  7.1653 -  pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1));
  7.1654 -  PG_RETURN_POINTER(point);
  7.1655 -}
  7.1656 -
  7.1657 -/* parse point ("epoint" in SQL) */
  7.1658 -/* format: '[NS]<float> [EW]<float>' */
  7.1659 -PG_FUNCTION_INFO_V1(pgl_epoint_in);
  7.1660 -Datum pgl_epoint_in(PG_FUNCTION_ARGS) {
  7.1661 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  7.1662 -  char *strptr = str;  /* current position within string */
  7.1663 -  int done = 0;        /* bit mask storing if latitude or longitude was read */
  7.1664 -  double lat, lon;     /* parsed values as double precision floats */
  7.1665 -  pgl_point *point;    /* return value (to be palloc'ed) */
  7.1666 -  /* parse two floats (each latitude or longitude) separated by white-space */
  7.1667 -  done |= pgl_scan(&strptr, &lat, &lon);
  7.1668 -  if (strptr != str && isspace(strptr[-1])) {
  7.1669 -    done |= pgl_scan(&strptr, &lat, &lon);
  7.1670 -  }
  7.1671 -  /* require end of string, and latitude and longitude parsed successfully */
  7.1672 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  7.1673 -    ereport(ERROR, (
  7.1674 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  7.1675 -      errmsg("invalid input syntax for type epoint: \"%s\"", str)
  7.1676 -    ));
  7.1677 -  }
  7.1678 -  /* allocate memory for result */
  7.1679 -  point = (pgl_point *)palloc(sizeof(pgl_point));
  7.1680 -  /* set latitude and longitude (and perform checks) */
  7.1681 -  pgl_epoint_set_latlon(point, lat, lon);
  7.1682 -  /* return result */
  7.1683 -  PG_RETURN_POINTER(point);
  7.1684 -}
  7.1685 -
  7.1686 -/* set sample count for numerical integration (including checks) */
  7.1687 -static void pgl_epoint_set_sample_count(pgl_point_sc *search, int32 samples) {
  7.1688 -  /* require minimum of 6 samples */
  7.1689 -  if (samples < 6) {
  7.1690 -    ereport(ERROR, (
  7.1691 -      errcode(ERRCODE_DATA_EXCEPTION),
  7.1692 -      errmsg("too few sample points for numerical integration (minimum 6)")
  7.1693 -    ));
  7.1694 -  }
  7.1695 -  /* limit sample count to avoid integer overflows on memory allocation */
  7.1696 -  if (samples > PGL_CLUSTER_MAXPOINTS) {
  7.1697 -    ereport(ERROR, (
  7.1698 -      errcode(ERRCODE_DATA_EXCEPTION),
  7.1699 -      errmsg(
  7.1700 -        "too many sample points for numerical integration (maximum %i)",
  7.1701 -        PGL_CLUSTER_MAXPOINTS
  7.1702 -      )
  7.1703 -    ));
  7.1704 -  }
  7.1705 -  search->samples = samples;
  7.1706 -}
  7.1707 -
  7.1708 -/* create point with sample count for fair distance calculation
  7.1709 -   ("epoint_with_sample_count" in SQL) from epoint and integer */
  7.1710 -PG_FUNCTION_INFO_V1(pgl_create_epoint_with_sample_count);
  7.1711 -Datum pgl_create_epoint_with_sample_count(PG_FUNCTION_ARGS) {
  7.1712 -  pgl_point_sc *search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc));
  7.1713 -  search->point = *(pgl_point *)PG_GETARG_POINTER(0);
  7.1714 -  pgl_epoint_set_sample_count(search, PG_GETARG_INT32(1));
  7.1715 -  PG_RETURN_POINTER(search);
  7.1716 -}
  7.1717 -
  7.1718 -/* parse point with sample count ("epoint_with_sample_count" in SQL) */
  7.1719 -/* format: '[NS]<float> [EW]<float> <integer>' */
  7.1720 -PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_in);
  7.1721 -Datum pgl_epoint_with_sample_count_in(PG_FUNCTION_ARGS) {
  7.1722 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  7.1723 -  char *strptr = str;    /* current position within string */
  7.1724 -  double lat, lon;       /* parsed values for latitude and longitude */
  7.1725 -  int samples;           /* parsed value for sample count */
  7.1726 -  int valid = 0;         /* number of valid chars */
  7.1727 -  int done = 0;          /* stores if latitude and/or longitude was read */
  7.1728 -  pgl_point_sc *search;  /* return value (to be palloc'ed) */
  7.1729 -  /* demand three blocks separated by whitespace */
  7.1730 -  sscanf(strptr, " %*s %*s %*s %n", &valid);
  7.1731 -  /* if three blocks separated by whitespace exist, parse those blocks */
  7.1732 -  if (strptr[valid] == 0) {
  7.1733 -    /* parse latitude and longitude */
  7.1734 -    done |= pgl_scan(&strptr, &lat, &lon);
  7.1735 -    done |= pgl_scan(&strptr, &lat, &lon);
  7.1736 -    /* parse sample count (while incr. strptr by number of bytes parsed) */
  7.1737 -    valid = 0;
  7.1738 -    if (sscanf(strptr, " %d %n", &samples, &valid) == 1) strptr += valid;
  7.1739 -  }
  7.1740 -  /* require end of string and both latitude and longitude being parsed */
  7.1741 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  7.1742 -    ereport(ERROR, (
  7.1743 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  7.1744 -      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  7.1745 -    ));
  7.1746 -  }
  7.1747 -  /* allocate memory for result */
  7.1748 -  search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc));
  7.1749 -  /* set latitude, longitude, and sample count (while performing checks) */
  7.1750 -  pgl_epoint_set_latlon(&search->point, lat, lon);
  7.1751 -  pgl_epoint_set_sample_count(search, samples);
  7.1752 -  /* return result */
  7.1753 -  PG_RETURN_POINTER(search);
  7.1754 -}
  7.1755 -
  7.1756 -/* create box ("ebox" in SQL) that is empty */
  7.1757 -PG_FUNCTION_INFO_V1(pgl_create_empty_ebox);
  7.1758 -Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) {
  7.1759 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  7.1760 -  pgl_box_set_empty(box);
  7.1761 -  PG_RETURN_POINTER(box);
  7.1762 -}
  7.1763 -
  7.1764 -/* set box to given boundaries (including checks) */
  7.1765 -static void pgl_ebox_set_boundaries(
  7.1766 -  pgl_box *box,
  7.1767 -  double lat_min, double lat_max, double lon_min, double lon_max
  7.1768 -) {
  7.1769 -  /* if minimum latitude is greater than maximum latitude, return empty box */
  7.1770 -  if (lat_min > lat_max) {
  7.1771 -    pgl_box_set_empty(box);
  7.1772 -    return;
  7.1773 -  }
  7.1774 -  /* otherwise reject infinite or NaN values */
  7.1775 -  if (
  7.1776 -    !isfinite(lat_min) || !isfinite(lat_max) ||
  7.1777 -    !isfinite(lon_min) || !isfinite(lon_max)
  7.1778 -  ) {
  7.1779 -    ereport(ERROR, (
  7.1780 -      errcode(ERRCODE_DATA_EXCEPTION),
  7.1781 -      errmsg("ebox requires finite coordinates")
  7.1782 -    ));
  7.1783 -  }
  7.1784 -  /* check latitude bounds */
  7.1785 -  if (lat_max < -90) {
  7.1786 -    ereport(WARNING, (errmsg("northern latitude exceeds south pole")));
  7.1787 -    lat_max = -90;
  7.1788 -  } else if (lat_max > 90) {
  7.1789 -    ereport(WARNING, (errmsg("northern latitude exceeds north pole")));
  7.1790 -    lat_max = 90;
  7.1791 -  }
  7.1792 -  if (lat_min < -90) {
  7.1793 -    ereport(WARNING, (errmsg("southern latitude exceeds south pole")));
  7.1794 -    lat_min = -90;
  7.1795 -  } else if (lat_min > 90) {
  7.1796 -    ereport(WARNING, (errmsg("southern latitude exceeds north pole")));
  7.1797 -    lat_min = 90;
  7.1798 -  }
  7.1799 -  /* check if all longitudes are included */
  7.1800 -  if (lon_max - lon_min >= 360) {
  7.1801 -    if (lon_max - lon_min > 360) ereport(WARNING, (
  7.1802 -      errmsg("longitude coverage greater than 360 degrees")
  7.1803 -    ));
  7.1804 -    lon_min = -180;
  7.1805 -    lon_max = 180;
  7.1806 -  } else {
  7.1807 -    /* normalize longitude bounds */
  7.1808 -    if      (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360;
  7.1809 -    else if (lon_min >  180) lon_min -= 360 + trunc(lon_min / 360) * 360;
  7.1810 -    if      (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360;
  7.1811 -    else if (lon_max >  180) lon_max -= 360 + trunc(lon_max / 360) * 360;
  7.1812 -  }
  7.1813 -  /* store rounded latitude/longitude values for round-trip safety */
  7.1814 -  box->lat_min = pgl_round(lat_min);
  7.1815 -  box->lat_max = pgl_round(lat_max);
  7.1816 -  box->lon_min = pgl_round(lon_min);
  7.1817 -  box->lon_max = pgl_round(lon_max);
  7.1818 -  /* ensure that rounding does not change orientation */
  7.1819 -  if (lon_min > lon_max && box->lon_min == box->lon_max) {
  7.1820 -    box->lon_min = -180;
  7.1821 -    box->lon_max = 180;
  7.1822 -  }
  7.1823 -}
  7.1824 -
  7.1825 -/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */
  7.1826 -PG_FUNCTION_INFO_V1(pgl_create_ebox);
  7.1827 -Datum pgl_create_ebox(PG_FUNCTION_ARGS) {
  7.1828 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  7.1829 -  pgl_ebox_set_boundaries(
  7.1830 -    box,
  7.1831 -    PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1),
  7.1832 -    PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3)
  7.1833 -  );
  7.1834 -  PG_RETURN_POINTER(box);
  7.1835 -}
  7.1836 -
  7.1837 -/* create box ("ebox" in SQL) from two points ("epoint"s) */
  7.1838 -/* (can not be used to cover a longitude range of more than 120 degrees) */
  7.1839 -PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints);
  7.1840 -Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) {
  7.1841 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  7.1842 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  7.1843 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  7.1844 -  double lat_min, lat_max, lon_min, lon_max;
  7.1845 -  double dlon;  /* longitude range (delta longitude) */
  7.1846 -  /* order latitude and longitude boundaries */
  7.1847 -  if (point2->lat < point1->lat) {
  7.1848 -    lat_min = point2->lat;
  7.1849 -    lat_max = point1->lat;
  7.1850 -  } else {
  7.1851 -    lat_min = point1->lat;
  7.1852 -    lat_max = point2->lat;
  7.1853 -  }
  7.1854 -  if (point2->lon < point1->lon) {
  7.1855 -    lon_min = point2->lon;
  7.1856 -    lon_max = point1->lon;
  7.1857 -  } else {
  7.1858 -    lon_min = point1->lon;
  7.1859 -    lon_max = point2->lon;
  7.1860 -  }
  7.1861 -  /* calculate longitude range (round to avoid floating point errors) */
  7.1862 -  dlon = pgl_round(lon_max - lon_min);
  7.1863 -  /* determine east-west direction */
  7.1864 -  if (dlon >= 240) {
  7.1865 -    /* assume that 180th meridian is crossed and swap min/max longitude */
  7.1866 -    double swap = lon_min; lon_min = lon_max; lon_max = swap;
  7.1867 -  } else if (dlon > 120) {
  7.1868 -    /* unclear orientation since delta longitude > 120 */
  7.1869 -    ereport(ERROR, (
  7.1870 -      errcode(ERRCODE_DATA_EXCEPTION),
  7.1871 -      errmsg("can not determine east/west orientation for ebox")
  7.1872 -    ));
  7.1873 -  }
  7.1874 -  /* use boundaries to setup box (and perform checks) */
  7.1875 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  7.1876 -  /* return result */
  7.1877 -  PG_RETURN_POINTER(box);
  7.1878 -}
  7.1879 -
  7.1880 -/* parse box ("ebox" in SQL) */
  7.1881 -/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>'
  7.1882 -       or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */
  7.1883 -PG_FUNCTION_INFO_V1(pgl_ebox_in);
  7.1884 -Datum pgl_ebox_in(PG_FUNCTION_ARGS) {
  7.1885 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  7.1886 -  char *str_lower;     /* lower case version of input string */
  7.1887 -  char *strptr;        /* current position within string */
  7.1888 -  int valid;           /* number of valid chars */
  7.1889 -  int done;            /* specifies if latitude or longitude was read */
  7.1890 -  double val;          /* temporary variable */
  7.1891 -  int lat_count = 0;   /* count of latitude values parsed */
  7.1892 -  int lon_count = 0;   /* count of longitufde values parsed */
  7.1893 -  double lat_min, lat_max, lon_min, lon_max;  /* see pgl_box struct */
  7.1894 -  pgl_box *box;        /* return value (to be palloc'ed) */
  7.1895 -  /* lowercase input */
  7.1896 -  str_lower = psprintf("%s", str);
  7.1897 -  for (strptr=str_lower; *strptr; strptr++) {
  7.1898 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  7.1899 -  }
  7.1900 -  /* reset reading position to start of (lowercase) string */
  7.1901 -  strptr = str_lower;
  7.1902 -  /* check if empty box */
  7.1903 -  valid = 0;
  7.1904 -  sscanf(strptr, " empty %n", &valid);
  7.1905 -  if (valid && strptr[valid] == 0) {
  7.1906 -    /* allocate and return empty box */
  7.1907 -    box = (pgl_box *)palloc(sizeof(pgl_box));
  7.1908 -    pgl_box_set_empty(box);
  7.1909 -    PG_RETURN_POINTER(box);
  7.1910 -  }
  7.1911 -  /* demand four blocks separated by whitespace */
  7.1912 -  valid = 0;
  7.1913 -  sscanf(strptr, " %*s %*s %*s %*s %n", &valid);
  7.1914 -  /* if four blocks separated by whitespace exist, parse those blocks */
  7.1915 -  if (strptr[valid] == 0) while (strptr[0]) {
  7.1916 -    /* parse either latitude or longitude (whichever found in input string) */
  7.1917 -    done = pgl_scan(&strptr, &val, &val);
  7.1918 -    /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */
  7.1919 -    if (done == PGL_SCAN_LAT) {
  7.1920 -      if (!lat_count) lat_min = val; else lat_max = val;
  7.1921 -      lat_count++;
  7.1922 -    } else if (done == PGL_SCAN_LON) {
  7.1923 -      if (!lon_count) lon_min = val; else lon_max = val;
  7.1924 -      lon_count++;
  7.1925 -    } else {
  7.1926 -      break;
  7.1927 -    }
  7.1928 -  }
  7.1929 -  /* require end of string, and two latitude and two longitude values */
  7.1930 -  if (strptr[0] || lat_count != 2 || lon_count != 2) {
  7.1931 -    ereport(ERROR, (
  7.1932 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  7.1933 -      errmsg("invalid input syntax for type ebox: \"%s\"", str)
  7.1934 -    ));
  7.1935 -  }
  7.1936 -  /* free lower case string */
  7.1937 -  pfree(str_lower);
  7.1938 -  /* order boundaries (maximum greater than minimum) */
  7.1939 -  if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; }
  7.1940 -  if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; }
  7.1941 -  /* allocate memory for result */
  7.1942 -  box = (pgl_box *)palloc(sizeof(pgl_box));
  7.1943 -  /* set boundaries (and perform checks) */
  7.1944 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  7.1945 -  /* return result */
  7.1946 -  PG_RETURN_POINTER(box);
  7.1947 -}
  7.1948 -
  7.1949 -/* set circle to given latitude, longitude, and radius (including checks) */
  7.1950 -static void pgl_ecircle_set_latlon_radius(
  7.1951 -  pgl_circle *circle, double lat, double lon, double radius
  7.1952 -) {
  7.1953 -  /* set center point (including checks) */
  7.1954 -  pgl_epoint_set_latlon(&(circle->center), lat, lon);
  7.1955 -  /* handle non-positive radius */
  7.1956 -  if (isnan(radius)) {
  7.1957 -    ereport(ERROR, (
  7.1958 -      errcode(ERRCODE_DATA_EXCEPTION),
  7.1959 -      errmsg("invalid radius for ecircle")
  7.1960 -    ));
  7.1961 -  }
  7.1962 -  if (radius == 0) radius = 0;  /* avoids -0 */
  7.1963 -  else if (radius < 0) {
  7.1964 -    if (isfinite(radius)) {
  7.1965 -      ereport(NOTICE, (errmsg("negative radius converted to minus infinity")));
  7.1966 -    }
  7.1967 -    radius = -INFINITY;
  7.1968 -  }
  7.1969 -  /* store radius (round-trip safety is ensured by pgl_print_float) */
  7.1970 -  circle->radius = radius;
  7.1971 -}
  7.1972 -
  7.1973 -/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */
  7.1974 -PG_FUNCTION_INFO_V1(pgl_create_ecircle);
  7.1975 -Datum pgl_create_ecircle(PG_FUNCTION_ARGS) {
  7.1976 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  7.1977 -  pgl_ecircle_set_latlon_radius(
  7.1978 -    circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2)
  7.1979 -  );
  7.1980 -  PG_RETURN_POINTER(circle);
  7.1981 -}
  7.1982 -
  7.1983 -/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */
  7.1984 -PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint);
  7.1985 -Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) {
  7.1986 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.1987 -  double radius = PG_GETARG_FLOAT8(1);
  7.1988 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  7.1989 -  /* set latitude, longitude, radius (and perform checks) */
  7.1990 -  pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius);
  7.1991 -  /* return result */
  7.1992 -  PG_RETURN_POINTER(circle);
  7.1993 -}
  7.1994 -
  7.1995 -/* parse circle ("ecircle" in SQL) */
  7.1996 -/* format: '[NS]<float> [EW]<float> <float>' */
  7.1997 -PG_FUNCTION_INFO_V1(pgl_ecircle_in);
  7.1998 -Datum pgl_ecircle_in(PG_FUNCTION_ARGS) {
  7.1999 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  7.2000 -  char *strptr = str;       /* current position within string */
  7.2001 -  double lat, lon, radius;  /* parsed values as double precision floats */
  7.2002 -  int valid = 0;            /* number of valid chars */
  7.2003 -  int done = 0;             /* stores if latitude and/or longitude was read */
  7.2004 -  pgl_circle *circle;       /* return value (to be palloc'ed) */
  7.2005 -  /* demand three blocks separated by whitespace */
  7.2006 -  sscanf(strptr, " %*s %*s %*s %n", &valid);
  7.2007 -  /* if three blocks separated by whitespace exist, parse those blocks */
  7.2008 -  if (strptr[valid] == 0) {
  7.2009 -    /* parse latitude and longitude */
  7.2010 -    done |= pgl_scan(&strptr, &lat, &lon);
  7.2011 -    done |= pgl_scan(&strptr, &lat, &lon);
  7.2012 -    /* parse radius (while incrementing strptr by number of bytes parsed) */
  7.2013 -    valid = 0;
  7.2014 -    if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid;
  7.2015 -  }
  7.2016 -  /* require end of string and both latitude and longitude being parsed */
  7.2017 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  7.2018 -    ereport(ERROR, (
  7.2019 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  7.2020 -      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  7.2021 -    ));
  7.2022 -  }
  7.2023 -  /* allocate memory for result */
  7.2024 -  circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  7.2025 -  /* set latitude, longitude, radius (and perform checks) */
  7.2026 -  pgl_ecircle_set_latlon_radius(circle, lat, lon, radius);
  7.2027 -  /* return result */
  7.2028 -  PG_RETURN_POINTER(circle);
  7.2029 -}
  7.2030 -
  7.2031 -/* parse cluster ("ecluster" in SQL) */
  7.2032 -PG_FUNCTION_INFO_V1(pgl_ecluster_in);
  7.2033 -Datum pgl_ecluster_in(PG_FUNCTION_ARGS) {
  7.2034 -  int i;
  7.2035 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  7.2036 -  char *str_lower;         /* lower case version of input string */
  7.2037 -  char *strptr;            /* pointer to current reading position of input */
  7.2038 -  int npoints_total = 0;   /* total number of points in cluster */
  7.2039 -  int nentries = 0;        /* total number of entries */
  7.2040 -  pgl_newentry *entries;   /* array of pgl_newentry to create pgl_cluster */
  7.2041 -  int entries_buflen = 4;  /* maximum number of elements in entries array */
  7.2042 -  int valid;               /* number of valid chars processed */
  7.2043 -  double lat, lon;         /* latitude and longitude of parsed point */
  7.2044 -  int entrytype;           /* current entry type */
  7.2045 -  int npoints;             /* number of points in current entry */
  7.2046 -  pgl_point *points;       /* array of pgl_point for pgl_newentry */
  7.2047 -  int points_buflen;       /* maximum number of elements in points array */
  7.2048 -  int done;                /* return value of pgl_scan function */
  7.2049 -  pgl_cluster *cluster;    /* created cluster */
  7.2050 -  /* lowercase input */
  7.2051 -  str_lower = psprintf("%s", str);
  7.2052 -  for (strptr=str_lower; *strptr; strptr++) {
  7.2053 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  7.2054 -  }
  7.2055 -  /* reset reading position to start of (lowercase) string */
  7.2056 -  strptr = str_lower;
  7.2057 -  /* allocate initial buffer for entries */
  7.2058 -  entries = palloc(entries_buflen * sizeof(pgl_newentry));
  7.2059 -  /* parse until end of string */
  7.2060 -  while (strptr[0]) {
  7.2061 -    /* require previous white-space or closing parenthesis before next token */
  7.2062 -    if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') {
  7.2063 -      goto pgl_ecluster_in_error;
  7.2064 -    }
  7.2065 -    /* ignore token "empty" */
  7.2066 -    valid = 0; sscanf(strptr, " empty %n", &valid);
  7.2067 -    if (valid) { strptr += valid; continue; }
  7.2068 -    /* test for "point" token */
  7.2069 -    valid = 0; sscanf(strptr, " point ( %n", &valid);
  7.2070 -    if (valid) {
  7.2071 -      strptr += valid;
  7.2072 -      entrytype = PGL_ENTRY_POINT;
  7.2073 -      goto pgl_ecluster_in_type_ok;
  7.2074 -    }
  7.2075 -    /* test for "path" token */
  7.2076 -    valid = 0; sscanf(strptr, " path ( %n", &valid);
  7.2077 -    if (valid) {
  7.2078 -      strptr += valid;
  7.2079 -      entrytype = PGL_ENTRY_PATH;
  7.2080 -      goto pgl_ecluster_in_type_ok;
  7.2081 -    }
  7.2082 -    /* test for "outline" token */
  7.2083 -    valid = 0; sscanf(strptr, " outline ( %n", &valid);
  7.2084 -    if (valid) {
  7.2085 -      strptr += valid;
  7.2086 -      entrytype = PGL_ENTRY_OUTLINE;
  7.2087 -      goto pgl_ecluster_in_type_ok;
  7.2088 -    }
  7.2089 -    /* test for "polygon" token */
  7.2090 -    valid = 0; sscanf(strptr, " polygon ( %n", &valid);
  7.2091 -    if (valid) {
  7.2092 -      strptr += valid;
  7.2093 -      entrytype = PGL_ENTRY_POLYGON;
  7.2094 -      goto pgl_ecluster_in_type_ok;
  7.2095 -    }
  7.2096 -    /* error if no valid token found */
  7.2097 -    goto pgl_ecluster_in_error;
  7.2098 -    pgl_ecluster_in_type_ok:
  7.2099 -    /* check if pgl_newentry array needs to grow */
  7.2100 -    if (nentries == entries_buflen) {
  7.2101 -      pgl_newentry *newbuf;
  7.2102 -      entries_buflen *= 2;
  7.2103 -      newbuf = palloc(entries_buflen * sizeof(pgl_newentry));
  7.2104 -      memcpy(newbuf, entries, nentries * sizeof(pgl_newentry));
  7.2105 -      pfree(entries);
  7.2106 -      entries = newbuf;
  7.2107 -    }
  7.2108 -    /* reset number of points for current entry */
  7.2109 -    npoints = 0;
  7.2110 -    /* allocate array for points */
  7.2111 -    points_buflen = 4;
  7.2112 -    points = palloc(points_buflen * sizeof(pgl_point));
  7.2113 -    /* parse until closing parenthesis */
  7.2114 -    while (strptr[0] != ')') {
  7.2115 -      /* error on unexpected end of string */
  7.2116 -      if (strptr[0] == 0) goto pgl_ecluster_in_error;
  7.2117 -      /* mark neither latitude nor longitude as read */
  7.2118 -      done = PGL_SCAN_NONE;
  7.2119 -      /* require white-space before second, third, etc. point */
  7.2120 -      if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  7.2121 -      /* scan latitude (or longitude) */
  7.2122 -      done |= pgl_scan(&strptr, &lat, &lon);
  7.2123 -      /* require white-space before second coordinate */
  7.2124 -      if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  7.2125 -      /* scan longitude (or latitude) */
  7.2126 -      done |= pgl_scan(&strptr, &lat, &lon);
  7.2127 -      /* error unless both latitude and longitude were parsed */
  7.2128 -      if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error;
  7.2129 -      /* throw error if number of points is too high */
  7.2130 -      if (npoints_total == PGL_CLUSTER_MAXPOINTS) {
  7.2131 -        ereport(ERROR, (
  7.2132 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  7.2133 -          errmsg(
  7.2134 -            "too many points for ecluster entry (maximum %i)",
  7.2135 -            PGL_CLUSTER_MAXPOINTS
  7.2136 -          )
  7.2137 -        ));
  7.2138 -      }
  7.2139 -      /* check if pgl_point array needs to grow */
  7.2140 -      if (npoints == points_buflen) {
  7.2141 -        pgl_point *newbuf;
  7.2142 -        points_buflen *= 2;
  7.2143 -        newbuf = palloc(points_buflen * sizeof(pgl_point));
  7.2144 -        memcpy(newbuf, points, npoints * sizeof(pgl_point));
  7.2145 -        pfree(points);
  7.2146 -        points = newbuf;
  7.2147 -      }
  7.2148 -      /* append point to pgl_point array (includes checks) */
  7.2149 -      pgl_epoint_set_latlon(&(points[npoints++]), lat, lon);
  7.2150 -      /* increase total number of points */
  7.2151 -      npoints_total++;
  7.2152 -    }
  7.2153 -    /* error if entry has no points */
  7.2154 -    if (!npoints) goto pgl_ecluster_in_error;
  7.2155 -    /* entries with one point are automatically of type "point" */
  7.2156 -    if (npoints == 1) entrytype = PGL_ENTRY_POINT;
  7.2157 -    /* if entries have more than one point */
  7.2158 -    else {
  7.2159 -      /* throw error if entry type is "point" */
  7.2160 -      if (entrytype == PGL_ENTRY_POINT) {
  7.2161 -        ereport(ERROR, (
  7.2162 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  7.2163 -          errmsg("invalid input syntax for type ecluster (point entry with more than one point)")
  7.2164 -        ));
  7.2165 -      }
  7.2166 -      /* coerce outlines and polygons with more than 2 points to be a path */
  7.2167 -      if (npoints == 2) entrytype = PGL_ENTRY_PATH;
  7.2168 -    }
  7.2169 -    /* append entry to pgl_newentry array */
  7.2170 -    entries[nentries].entrytype = entrytype;
  7.2171 -    entries[nentries].npoints = npoints;
  7.2172 -    entries[nentries].points = points;
  7.2173 -    nentries++;
  7.2174 -    /* consume closing parenthesis */
  7.2175 -    strptr++;
  7.2176 -    /* consume white-space */
  7.2177 -    while (isspace(strptr[0])) strptr++;
  7.2178 -  }
  7.2179 -  /* free lower case string */
  7.2180 -  pfree(str_lower);
  7.2181 -  /* create cluster from pgl_newentry array */
  7.2182 -  cluster = pgl_new_cluster(nentries, entries);
  7.2183 -  /* free pgl_newentry array */
  7.2184 -  for (i=0; i<nentries; i++) pfree(entries[i].points);
  7.2185 -  pfree(entries);
  7.2186 -  /* set bounding circle of cluster and check east/west orientation */
  7.2187 -  if (!pgl_finalize_cluster(cluster)) {
  7.2188 -    ereport(ERROR, (
  7.2189 -      errcode(ERRCODE_DATA_EXCEPTION),
  7.2190 -      errmsg("can not determine east/west orientation for ecluster"),
  7.2191 -      errhint("Ensure that each entry has a longitude span of less than 180 degrees.")
  7.2192 -    ));
  7.2193 -  }
  7.2194 -  /* return cluster */
  7.2195 -  PG_RETURN_POINTER(cluster);
  7.2196 -  /* code to throw error */
  7.2197 -  pgl_ecluster_in_error:
  7.2198 -  ereport(ERROR, (
  7.2199 -    errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  7.2200 -    errmsg("invalid input syntax for type ecluster: \"%s\"", str)
  7.2201 -  ));
  7.2202 -}
  7.2203 -
  7.2204 -/* convert point ("epoint") to string representation */
  7.2205 -PG_FUNCTION_INFO_V1(pgl_epoint_out);
  7.2206 -Datum pgl_epoint_out(PG_FUNCTION_ARGS) {
  7.2207 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2208 -  char latstr[PGL_NUMBUFLEN];
  7.2209 -  char lonstr[PGL_NUMBUFLEN];
  7.2210 -  pgl_print_lat(latstr, point->lat);
  7.2211 -  pgl_print_lon(lonstr, point->lon);
  7.2212 -  PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr));
  7.2213 -}
  7.2214 -
  7.2215 -/* convert point with sample count ("epoint_with_sample_count") to str. rep. */
  7.2216 -PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_out);
  7.2217 -Datum pgl_epoint_with_sample_count_out(PG_FUNCTION_ARGS) {
  7.2218 -  pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(0);
  7.2219 -  char latstr[PGL_NUMBUFLEN];
  7.2220 -  char lonstr[PGL_NUMBUFLEN];
  7.2221 -  pgl_print_lat(latstr, search->point.lat);
  7.2222 -  pgl_print_lon(lonstr, search->point.lon);
  7.2223 -  PG_RETURN_CSTRING(
  7.2224 -    psprintf("%s %s %i", latstr, lonstr, (int)search->samples)
  7.2225 -  );
  7.2226 -}
  7.2227 -
  7.2228 -/* convert box ("ebox") to string representation */
  7.2229 -PG_FUNCTION_INFO_V1(pgl_ebox_out);
  7.2230 -Datum pgl_ebox_out(PG_FUNCTION_ARGS) {
  7.2231 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  7.2232 -  double lon_min = box->lon_min;
  7.2233 -  double lon_max = box->lon_max;
  7.2234 -  char lat_min_str[PGL_NUMBUFLEN];
  7.2235 -  char lat_max_str[PGL_NUMBUFLEN];
  7.2236 -  char lon_min_str[PGL_NUMBUFLEN];
  7.2237 -  char lon_max_str[PGL_NUMBUFLEN];
  7.2238 -  /* return string "empty" if box is set to be empty */
  7.2239 -  if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty");
  7.2240 -  /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */
  7.2241 -  /* (required since pgl_box_in orders the longitude boundaries) */
  7.2242 -  if (lon_min > lon_max) {
  7.2243 -    if (lon_min + lon_max >= 0) lon_min -= 360;
  7.2244 -    else lon_max += 360;
  7.2245 -  }
  7.2246 -  /* format and return result */
  7.2247 -  pgl_print_lat(lat_min_str, box->lat_min);
  7.2248 -  pgl_print_lat(lat_max_str, box->lat_max);
  7.2249 -  pgl_print_lon(lon_min_str, lon_min);
  7.2250 -  pgl_print_lon(lon_max_str, lon_max);
  7.2251 -  PG_RETURN_CSTRING(psprintf(
  7.2252 -    "%s %s %s %s",
  7.2253 -    lat_min_str, lon_min_str, lat_max_str, lon_max_str
  7.2254 -  ));
  7.2255 -}
  7.2256 -
  7.2257 -/* convert circle ("ecircle") to string representation */
  7.2258 -PG_FUNCTION_INFO_V1(pgl_ecircle_out);
  7.2259 -Datum pgl_ecircle_out(PG_FUNCTION_ARGS) {
  7.2260 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  7.2261 -  char latstr[PGL_NUMBUFLEN];
  7.2262 -  char lonstr[PGL_NUMBUFLEN];
  7.2263 -  char radstr[PGL_NUMBUFLEN];
  7.2264 -  pgl_print_lat(latstr, circle->center.lat);
  7.2265 -  pgl_print_lon(lonstr, circle->center.lon);
  7.2266 -  pgl_print_float(radstr, circle->radius);
  7.2267 -  PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr));
  7.2268 -}
  7.2269 -
  7.2270 -/* convert cluster ("ecluster") to string representation */
  7.2271 -PG_FUNCTION_INFO_V1(pgl_ecluster_out);
  7.2272 -Datum pgl_ecluster_out(PG_FUNCTION_ARGS) {
  7.2273 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  7.2274 -  char latstr[PGL_NUMBUFLEN];  /* string buffer for latitude */
  7.2275 -  char lonstr[PGL_NUMBUFLEN];  /* string buffer for longitude */
  7.2276 -  char ***strings;     /* array of array of strings */
  7.2277 -  char *string;        /* string of current token */
  7.2278 -  char *res, *resptr;  /* result and pointer to current write position */
  7.2279 -  size_t reslen = 1;   /* length of result (init with 1 for terminator) */
  7.2280 -  int npoints;         /* number of points of current entry */
  7.2281 -  int i, j;            /* i: entry, j: point in entry */
  7.2282 -  /* handle empty clusters */
  7.2283 -  if (cluster->nentries == 0) {
  7.2284 -    /* free detoasted cluster (if copy) */
  7.2285 -    PG_FREE_IF_COPY(cluster, 0);
  7.2286 -    /* return static result */
  7.2287 -    PG_RETURN_CSTRING("empty");
  7.2288 -  }
  7.2289 -  /* allocate array of array of strings */
  7.2290 -  strings = palloc(cluster->nentries * sizeof(char **));
  7.2291 -  /* iterate over all entries in cluster */
  7.2292 -  for (i=0; i<cluster->nentries; i++) {
  7.2293 -    /* get number of points in entry */
  7.2294 -    npoints = cluster->entries[i].npoints;
  7.2295 -    /* allocate array of strings (one string for each point plus two extra) */
  7.2296 -    strings[i] = palloc((2 + npoints) * sizeof(char *));
  7.2297 -    /* determine opening string */
  7.2298 -    switch (cluster->entries[i].entrytype) {
  7.2299 -      case PGL_ENTRY_POINT:   string = (i==0)?"point ("  :" point (";   break;
  7.2300 -      case PGL_ENTRY_PATH:    string = (i==0)?"path ("   :" path (";    break;
  7.2301 -      case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break;
  7.2302 -      case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break;
  7.2303 -      default:                string = (i==0)?"unknown"  :" unknown";
  7.2304 -    }
  7.2305 -    /* use opening string as first string in array */
  7.2306 -    strings[i][0] = string;
  7.2307 -    /* update result length (for allocating result string later) */
  7.2308 -    reslen += strlen(string);
  7.2309 -    /* iterate over all points */
  7.2310 -    for (j=0; j<npoints; j++) {
  7.2311 -      /* create string representation of point */
  7.2312 -      pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat);
  7.2313 -      pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon);
  7.2314 -      string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr);
  7.2315 -      /* copy string pointer to string array */
  7.2316 -      strings[i][j+1] = string;
  7.2317 -      /* update result length (for allocating result string later) */
  7.2318 -      reslen += strlen(string);
  7.2319 -    }
  7.2320 -    /* use closing parenthesis as last string in array */
  7.2321 -    strings[i][npoints+1] = ")";
  7.2322 -    /* update result length (for allocating result string later) */
  7.2323 -    reslen++;
  7.2324 -  }
  7.2325 -  /* allocate result string */
  7.2326 -  res = palloc(reslen);
  7.2327 -  /* set write pointer to begin of result string */
  7.2328 -  resptr = res;
  7.2329 -  /* copy strings into result string */
  7.2330 -  for (i=0; i<cluster->nentries; i++) {
  7.2331 -    npoints = cluster->entries[i].npoints;
  7.2332 -    for (j=0; j<npoints+2; j++) {
  7.2333 -      string = strings[i][j];
  7.2334 -      strcpy(resptr, string);
  7.2335 -      resptr += strlen(string);
  7.2336 -      /* free strings allocated by psprintf */
  7.2337 -      if (j != 0 && j != npoints+1) pfree(string);
  7.2338 -    }
  7.2339 -    /* free array of strings */
  7.2340 -    pfree(strings[i]);
  7.2341 -  }
  7.2342 -  /* free array of array of strings */
  7.2343 -  pfree(strings);
  7.2344 -  /* free detoasted cluster (if copy) */
  7.2345 -  PG_FREE_IF_COPY(cluster, 0);
  7.2346 -  /* return result */
  7.2347 -  PG_RETURN_CSTRING(res);
  7.2348 -}
  7.2349 -
  7.2350 -/* binary input function for point ("epoint") */
  7.2351 -PG_FUNCTION_INFO_V1(pgl_epoint_recv);
  7.2352 -Datum pgl_epoint_recv(PG_FUNCTION_ARGS) {
  7.2353 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  7.2354 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  7.2355 -  point->lat = pq_getmsgfloat8(buf);
  7.2356 -  point->lon = pq_getmsgfloat8(buf);
  7.2357 -  PG_RETURN_POINTER(point);
  7.2358 -}
  7.2359 -
  7.2360 -/* binary input function for box ("ebox") */
  7.2361 -PG_FUNCTION_INFO_V1(pgl_ebox_recv);
  7.2362 -Datum pgl_ebox_recv(PG_FUNCTION_ARGS) {
  7.2363 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  7.2364 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  7.2365 -  box->lat_min = pq_getmsgfloat8(buf);
  7.2366 -  box->lat_max = pq_getmsgfloat8(buf);
  7.2367 -  box->lon_min = pq_getmsgfloat8(buf);
  7.2368 -  box->lon_max = pq_getmsgfloat8(buf);
  7.2369 -  PG_RETURN_POINTER(box);
  7.2370 -}
  7.2371 -
  7.2372 -/* binary input function for circle ("ecircle") */
  7.2373 -PG_FUNCTION_INFO_V1(pgl_ecircle_recv);
  7.2374 -Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) {
  7.2375 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  7.2376 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  7.2377 -  circle->center.lat = pq_getmsgfloat8(buf);
  7.2378 -  circle->center.lon = pq_getmsgfloat8(buf);
  7.2379 -  circle->radius = pq_getmsgfloat8(buf);
  7.2380 -  PG_RETURN_POINTER(circle);
  7.2381 -}
  7.2382 -
  7.2383 -/* TODO: binary receive function for cluster */
  7.2384 -
  7.2385 -/* binary output function for point ("epoint") */
  7.2386 -PG_FUNCTION_INFO_V1(pgl_epoint_send);
  7.2387 -Datum pgl_epoint_send(PG_FUNCTION_ARGS) {
  7.2388 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2389 -  StringInfoData buf;
  7.2390 -  pq_begintypsend(&buf);
  7.2391 -  pq_sendfloat8(&buf, point->lat);
  7.2392 -  pq_sendfloat8(&buf, point->lon);
  7.2393 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  7.2394 -}
  7.2395 -
  7.2396 -/* binary output function for box ("ebox") */
  7.2397 -PG_FUNCTION_INFO_V1(pgl_ebox_send);
  7.2398 -Datum pgl_ebox_send(PG_FUNCTION_ARGS) {
  7.2399 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  7.2400 -  StringInfoData buf;
  7.2401 -  pq_begintypsend(&buf);
  7.2402 -  pq_sendfloat8(&buf, box->lat_min);
  7.2403 -  pq_sendfloat8(&buf, box->lat_max);
  7.2404 -  pq_sendfloat8(&buf, box->lon_min);
  7.2405 -  pq_sendfloat8(&buf, box->lon_max);
  7.2406 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  7.2407 -}
  7.2408 -
  7.2409 -/* binary output function for circle ("ecircle") */
  7.2410 -PG_FUNCTION_INFO_V1(pgl_ecircle_send);
  7.2411 -Datum pgl_ecircle_send(PG_FUNCTION_ARGS) {
  7.2412 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  7.2413 -  StringInfoData buf;
  7.2414 -  pq_begintypsend(&buf);
  7.2415 -  pq_sendfloat8(&buf, circle->center.lat);
  7.2416 -  pq_sendfloat8(&buf, circle->center.lon);
  7.2417 -  pq_sendfloat8(&buf, circle->radius);
  7.2418 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  7.2419 -}
  7.2420 -
  7.2421 -/* TODO: binary send functions for cluster */
  7.2422 -
  7.2423 -/* cast point ("epoint") to box ("ebox") */
  7.2424 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox);
  7.2425 -Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) {
  7.2426 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2427 -  pgl_box *box = palloc(sizeof(pgl_box));
  7.2428 -  box->lat_min = point->lat;
  7.2429 -  box->lat_max = point->lat;
  7.2430 -  box->lon_min = point->lon;
  7.2431 -  box->lon_max = point->lon;
  7.2432 -  PG_RETURN_POINTER(box);
  7.2433 -}
  7.2434 -
  7.2435 -/* cast point ("epoint") to circle ("ecircle") */
  7.2436 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle);
  7.2437 -Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) {
  7.2438 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2439 -  pgl_circle *circle = palloc(sizeof(pgl_box));
  7.2440 -  circle->center = *point;
  7.2441 -  circle->radius = 0;
  7.2442 -  PG_RETURN_POINTER(circle);
  7.2443 -}
  7.2444 -
  7.2445 -/* cast point ("epoint") to cluster ("ecluster") */
  7.2446 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster);
  7.2447 -Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) {
  7.2448 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2449 -  pgl_newentry entry;
  7.2450 -  pgl_cluster *cluster;
  7.2451 -  entry.entrytype = PGL_ENTRY_POINT;
  7.2452 -  entry.npoints = 1;
  7.2453 -  entry.points = point;
  7.2454 -  cluster = pgl_new_cluster(1, &entry);
  7.2455 -  pgl_finalize_cluster(cluster);  /* NOTE: should not fail */
  7.2456 -  PG_RETURN_POINTER(cluster);
  7.2457 -}
  7.2458 -
  7.2459 -/* cast box ("ebox") to cluster ("ecluster") */
  7.2460 -#define pgl_ebox_to_ecluster_macro(i, a, b) \
  7.2461 -  entries[i].entrytype = PGL_ENTRY_POLYGON; \
  7.2462 -  entries[i].npoints = 4; \
  7.2463 -  entries[i].points = points[i]; \
  7.2464 -  points[i][0].lat = box->lat_min; \
  7.2465 -  points[i][0].lon = (a); \
  7.2466 -  points[i][1].lat = box->lat_min; \
  7.2467 -  points[i][1].lon = (b); \
  7.2468 -  points[i][2].lat = box->lat_max; \
  7.2469 -  points[i][2].lon = (b); \
  7.2470 -  points[i][3].lat = box->lat_max; \
  7.2471 -  points[i][3].lon = (a);
  7.2472 -PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster);
  7.2473 -Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) {
  7.2474 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  7.2475 -  double lon, dlon;
  7.2476 -  int nentries;
  7.2477 -  pgl_newentry entries[3];
  7.2478 -  pgl_point points[3][4];
  7.2479 -  pgl_cluster *cluster;
  7.2480 -  if (box->lat_min > box->lat_max) {
  7.2481 -    nentries = 0;
  7.2482 -  } else if (box->lon_min > box->lon_max) {
  7.2483 -    if (box->lon_min < 0) {
  7.2484 -      lon = pgl_round((box->lon_min + 180) / 2.0);
  7.2485 -      nentries = 3;
  7.2486 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  7.2487 -      pgl_ebox_to_ecluster_macro(1, lon, 180);
  7.2488 -      pgl_ebox_to_ecluster_macro(2, -180, box->lon_max);
  7.2489 -    } else if (box->lon_max > 0) {
  7.2490 -      lon = pgl_round((box->lon_max - 180) / 2.0);
  7.2491 -      nentries = 3;
  7.2492 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  7.2493 -      pgl_ebox_to_ecluster_macro(1, -180, lon);
  7.2494 -      pgl_ebox_to_ecluster_macro(2, lon, box->lon_max);
  7.2495 -    } else {
  7.2496 -      nentries = 2;
  7.2497 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  7.2498 -      pgl_ebox_to_ecluster_macro(1, -180, box->lon_max);
  7.2499 -    }
  7.2500 -  } else {
  7.2501 -    dlon = pgl_round(box->lon_max - box->lon_min);
  7.2502 -    if (dlon < 180) {
  7.2503 -      nentries = 1;
  7.2504 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max);
  7.2505 -    } else {
  7.2506 -      lon = pgl_round((box->lon_min + box->lon_max) / 2.0);
  7.2507 -      if (
  7.2508 -        pgl_round(lon - box->lon_min) < 180 &&
  7.2509 -        pgl_round(box->lon_max - lon) < 180
  7.2510 -      ) {
  7.2511 -        nentries = 2;
  7.2512 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  7.2513 -        pgl_ebox_to_ecluster_macro(1, lon, box->lon_max);
  7.2514 -      } else {
  7.2515 -        nentries = 3;
  7.2516 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, -60);
  7.2517 -        pgl_ebox_to_ecluster_macro(1, -60, 60);
  7.2518 -        pgl_ebox_to_ecluster_macro(2, 60, box->lon_max);
  7.2519 -      }
  7.2520 -    }
  7.2521 -  }
  7.2522 -  cluster = pgl_new_cluster(nentries, entries);
  7.2523 -  pgl_finalize_cluster(cluster);  /* NOTE: should not fail */
  7.2524 -  PG_RETURN_POINTER(cluster);
  7.2525 -}
  7.2526 -
  7.2527 -/* extract latitude from point ("epoint") */
  7.2528 -PG_FUNCTION_INFO_V1(pgl_epoint_lat);
  7.2529 -Datum pgl_epoint_lat(PG_FUNCTION_ARGS) {
  7.2530 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat);
  7.2531 -}
  7.2532 -
  7.2533 -/* extract longitude from point ("epoint") */
  7.2534 -PG_FUNCTION_INFO_V1(pgl_epoint_lon);
  7.2535 -Datum pgl_epoint_lon(PG_FUNCTION_ARGS) {
  7.2536 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon);
  7.2537 -}
  7.2538 -
  7.2539 -/* extract minimum latitude from box ("ebox") */
  7.2540 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_min);
  7.2541 -Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) {
  7.2542 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min);
  7.2543 -}
  7.2544 -
  7.2545 -/* extract maximum latitude from box ("ebox") */
  7.2546 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_max);
  7.2547 -Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) {
  7.2548 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max);
  7.2549 -}
  7.2550 -
  7.2551 -/* extract minimum longitude from box ("ebox") */
  7.2552 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_min);
  7.2553 -Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) {
  7.2554 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min);
  7.2555 -}
  7.2556 -
  7.2557 -/* extract maximum longitude from box ("ebox") */
  7.2558 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_max);
  7.2559 -Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) {
  7.2560 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max);
  7.2561 -}
  7.2562 -
  7.2563 -/* extract center point from circle ("ecircle") */
  7.2564 -PG_FUNCTION_INFO_V1(pgl_ecircle_center);
  7.2565 -Datum pgl_ecircle_center(PG_FUNCTION_ARGS) {
  7.2566 -  PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center));
  7.2567 -}
  7.2568 -
  7.2569 -/* extract radius from circle ("ecircle") */
  7.2570 -PG_FUNCTION_INFO_V1(pgl_ecircle_radius);
  7.2571 -Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) {
  7.2572 -  PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius);
  7.2573 -}
  7.2574 -
  7.2575 -/* check if point is inside box (overlap operator "&&") in SQL */
  7.2576 -PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap);
  7.2577 -Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) {
  7.2578 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2579 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1);
  7.2580 -  PG_RETURN_BOOL(pgl_point_in_box(point, box));
  7.2581 -}
  7.2582 -
  7.2583 -/* check if point is inside circle (overlap operator "&&") in SQL */
  7.2584 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap);
  7.2585 -Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) {
  7.2586 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2587 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  7.2588 -  PG_RETURN_BOOL(
  7.2589 -    pgl_distance(
  7.2590 -      point->lat, point->lon,
  7.2591 -      circle->center.lat, circle->center.lon
  7.2592 -    ) <= circle->radius
  7.2593 -  );
  7.2594 -}
  7.2595 -
  7.2596 -/* check if point is inside cluster (overlap operator "&&") in SQL */
  7.2597 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap);
  7.2598 -Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
  7.2599 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2600 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2601 -  bool retval;
  7.2602 -  /* points outside bounding circle are always assumed to be non-overlapping */
  7.2603 -  if (
  7.2604 -    pgl_distance(
  7.2605 -      point->lat, point->lon,
  7.2606 -      cluster->bounding.center.lat, cluster->bounding.center.lon
  7.2607 -    ) > cluster->bounding.radius
  7.2608 -  ) retval = false;
  7.2609 -  else retval = pgl_point_in_cluster(point, cluster, false);
  7.2610 -  PG_FREE_IF_COPY(cluster, 1);
  7.2611 -  PG_RETURN_BOOL(retval);
  7.2612 -}
  7.2613 -
  7.2614 -/* check if point may be inside cluster (lossy overl. operator "&&+") in SQL */
  7.2615 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_may_overlap);
  7.2616 -Datum pgl_epoint_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  7.2617 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2618 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2619 -  bool retval = pgl_distance(
  7.2620 -    point->lat, point->lon,
  7.2621 -    cluster->bounding.center.lat, cluster->bounding.center.lon
  7.2622 -  ) <= cluster->bounding.radius;
  7.2623 -  PG_FREE_IF_COPY(cluster, 1);
  7.2624 -  PG_RETURN_BOOL(retval);
  7.2625 -}
  7.2626 -
  7.2627 -/* check if two boxes overlap (overlap operator "&&") in SQL */
  7.2628 -PG_FUNCTION_INFO_V1(pgl_ebox_overlap);
  7.2629 -Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) {
  7.2630 -  pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0);
  7.2631 -  pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1);
  7.2632 -  PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2));
  7.2633 -}
  7.2634 -
  7.2635 -/* check if box and circle may overlap (lossy overl. operator "&&+") in SQL */
  7.2636 -PG_FUNCTION_INFO_V1(pgl_ebox_ecircle_may_overlap);
  7.2637 -Datum pgl_ebox_ecircle_may_overlap(PG_FUNCTION_ARGS) {
  7.2638 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  7.2639 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  7.2640 -  PG_RETURN_BOOL(
  7.2641 -    pgl_estimate_point_box_distance(&circle->center, box) <= circle->radius
  7.2642 -  );
  7.2643 -}
  7.2644 -
  7.2645 -/* check if box and cluster may overlap (lossy overl. operator "&&+") in SQL */
  7.2646 -PG_FUNCTION_INFO_V1(pgl_ebox_ecluster_may_overlap);
  7.2647 -Datum pgl_ebox_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  7.2648 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  7.2649 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2650 -  bool retval = pgl_estimate_point_box_distance(
  7.2651 -    &cluster->bounding.center,
  7.2652 -    box
  7.2653 -  ) <= cluster->bounding.radius;
  7.2654 -  PG_FREE_IF_COPY(cluster, 1);
  7.2655 -  PG_RETURN_BOOL(retval);
  7.2656 -}
  7.2657 -
  7.2658 -/* check if two circles overlap (overlap operator "&&") in SQL */
  7.2659 -PG_FUNCTION_INFO_V1(pgl_ecircle_overlap);
  7.2660 -Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) {
  7.2661 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  7.2662 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  7.2663 -  PG_RETURN_BOOL(
  7.2664 -    pgl_distance(
  7.2665 -      circle1->center.lat, circle1->center.lon,
  7.2666 -      circle2->center.lat, circle2->center.lon
  7.2667 -    ) <= circle1->radius + circle2->radius
  7.2668 -  );
  7.2669 -}
  7.2670 -
  7.2671 -/* check if circle and cluster overlap (overlap operator "&&") in SQL */
  7.2672 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap);
  7.2673 -Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) {
  7.2674 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  7.2675 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2676 -  bool retval;
  7.2677 -  /* points outside bounding circle (with margin for flattening) are always
  7.2678 -     assumed to be non-overlapping */
  7.2679 -  if (
  7.2680 -    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  7.2681 -    pgl_distance(
  7.2682 -      circle->center.lat, circle->center.lon,
  7.2683 -      cluster->bounding.center.lat, cluster->bounding.center.lon
  7.2684 -    ) > circle->radius + cluster->bounding.radius
  7.2685 -  ) retval = false;
  7.2686 -  else retval = pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius;
  7.2687 -  PG_FREE_IF_COPY(cluster, 1);
  7.2688 -  PG_RETURN_BOOL(retval);
  7.2689 -}
  7.2690 -
  7.2691 -/* check if circle and cluster may overlap (l. ov. operator "&&+") in SQL */
  7.2692 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_may_overlap);
  7.2693 -Datum pgl_ecircle_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  7.2694 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  7.2695 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2696 -  bool retval = (
  7.2697 -    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  7.2698 -    pgl_distance(
  7.2699 -      circle->center.lat, circle->center.lon,
  7.2700 -      cluster->bounding.center.lat, cluster->bounding.center.lon
  7.2701 -    )
  7.2702 -  ) <= circle->radius + cluster->bounding.radius;
  7.2703 -  PG_FREE_IF_COPY(cluster, 1);
  7.2704 -  PG_RETURN_BOOL(retval);
  7.2705 -}
  7.2706 -
  7.2707 -/* check if two clusters overlap (overlap operator "&&") in SQL */
  7.2708 -PG_FUNCTION_INFO_V1(pgl_ecluster_overlap);
  7.2709 -Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) {
  7.2710 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  7.2711 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2712 -  bool retval;
  7.2713 -  /* clusters with non-touching bounding circles (with margin for flattening)
  7.2714 -     are always assumed to be non-overlapping */
  7.2715 -  if (
  7.2716 -    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  7.2717 -    pgl_distance(
  7.2718 -      cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  7.2719 -      cluster2->bounding.center.lat, cluster2->bounding.center.lon
  7.2720 -    ) > cluster1->bounding.radius + cluster2->bounding.radius
  7.2721 -  ) retval = false;
  7.2722 -  else retval = pgl_clusters_overlap(cluster1, cluster2);
  7.2723 -  PG_FREE_IF_COPY(cluster1, 0);
  7.2724 -  PG_FREE_IF_COPY(cluster2, 1);
  7.2725 -  PG_RETURN_BOOL(retval);
  7.2726 -}
  7.2727 -
  7.2728 -/* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */
  7.2729 -PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap);
  7.2730 -Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  7.2731 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  7.2732 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2733 -  bool retval = (
  7.2734 -    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  7.2735 -    pgl_distance(
  7.2736 -      cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  7.2737 -      cluster2->bounding.center.lat, cluster2->bounding.center.lon
  7.2738 -    )
  7.2739 -  ) <= cluster1->bounding.radius + cluster2->bounding.radius;
  7.2740 -  PG_FREE_IF_COPY(cluster1, 0);
  7.2741 -  PG_FREE_IF_COPY(cluster2, 1);
  7.2742 -  PG_RETURN_BOOL(retval);
  7.2743 -}
  7.2744 -
  7.2745 -/* check if second cluster is in first cluster (cont. operator "@>) in SQL */
  7.2746 -PG_FUNCTION_INFO_V1(pgl_ecluster_contains);
  7.2747 -Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) {
  7.2748 -  pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  7.2749 -  pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2750 -  bool retval;
  7.2751 -  /* clusters with non-touching bounding circles are always assumed to be
  7.2752 -     non-overlapping */
  7.2753 -  if (
  7.2754 -    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  7.2755 -    pgl_distance(
  7.2756 -      outer->bounding.center.lat, outer->bounding.center.lon,
  7.2757 -      inner->bounding.center.lat, inner->bounding.center.lon
  7.2758 -    ) > outer->bounding.radius + inner->bounding.radius
  7.2759 -  ) retval = false;
  7.2760 -  else retval = pgl_cluster_in_cluster(outer, inner);
  7.2761 -  PG_FREE_IF_COPY(outer, 0);
  7.2762 -  PG_FREE_IF_COPY(inner, 1);
  7.2763 -  PG_RETURN_BOOL(retval);
  7.2764 -}
  7.2765 -
  7.2766 -/* calculate distance between two points ("<->" operator) in SQL */
  7.2767 -PG_FUNCTION_INFO_V1(pgl_epoint_distance);
  7.2768 -Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
  7.2769 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  7.2770 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  7.2771 -  PG_RETURN_FLOAT8(pgl_distance(
  7.2772 -    point1->lat, point1->lon, point2->lat, point2->lon
  7.2773 -  ));
  7.2774 -}
  7.2775 -
  7.2776 -/* calculate point to circle distance ("<->" operator) in SQL */
  7.2777 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance);
  7.2778 -Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) {
  7.2779 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2780 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  7.2781 -  double distance = pgl_distance(
  7.2782 -    point->lat, point->lon, circle->center.lat, circle->center.lon
  7.2783 -  ) - circle->radius;
  7.2784 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  7.2785 -}
  7.2786 -
  7.2787 -/* calculate point to cluster distance ("<->" operator) in SQL */
  7.2788 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance);
  7.2789 -Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) {
  7.2790 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  7.2791 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2792 -  double distance = pgl_point_cluster_distance(point, cluster);
  7.2793 -  PG_FREE_IF_COPY(cluster, 1);
  7.2794 -  PG_RETURN_FLOAT8(distance);
  7.2795 -}
  7.2796 -
  7.2797 -/* calculate distance between two circles ("<->" operator) in SQL */
  7.2798 -PG_FUNCTION_INFO_V1(pgl_ecircle_distance);
  7.2799 -Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) {
  7.2800 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  7.2801 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  7.2802 -  double distance = pgl_distance(
  7.2803 -    circle1->center.lat, circle1->center.lon,
  7.2804 -    circle2->center.lat, circle2->center.lon
  7.2805 -  ) - (circle1->radius + circle2->radius);
  7.2806 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  7.2807 -}
  7.2808 -
  7.2809 -/* calculate circle to cluster distance ("<->" operator) in SQL */
  7.2810 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance);
  7.2811 -Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) {
  7.2812 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  7.2813 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2814 -  double distance = (
  7.2815 -    pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius
  7.2816 -  );
  7.2817 -  PG_FREE_IF_COPY(cluster, 1);
  7.2818 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  7.2819 -}
  7.2820 -
  7.2821 -/* calculate distance between two clusters ("<->" operator) in SQL */
  7.2822 -PG_FUNCTION_INFO_V1(pgl_ecluster_distance);
  7.2823 -Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) {
  7.2824 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  7.2825 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2826 -  double retval = pgl_cluster_distance(cluster1, cluster2);
  7.2827 -  PG_FREE_IF_COPY(cluster1, 0);
  7.2828 -  PG_FREE_IF_COPY(cluster2, 1);
  7.2829 -  PG_RETURN_FLOAT8(retval);
  7.2830 -}
  7.2831 -
  7.2832 -/* calculate fair distance (see README) between cluster and point with
  7.2833 -   precision denoted by sample count ("<=> operator) in SQL */
  7.2834 -PG_FUNCTION_INFO_V1(pgl_ecluster_epoint_sc_fair_distance);
  7.2835 -Datum pgl_ecluster_epoint_sc_fair_distance(PG_FUNCTION_ARGS) {
  7.2836 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  7.2837 -  pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(1);
  7.2838 -  double retval = pgl_fair_distance(
  7.2839 -    &search->point, cluster, search->samples
  7.2840 -  );
  7.2841 -  PG_FREE_IF_COPY(cluster, 0);
  7.2842 -  PG_RETURN_FLOAT8(retval);
  7.2843 -}
  7.2844 -
  7.2845 -
  7.2846 -/*-----------------------------------------------------------*
  7.2847 - *  B-tree comparison operators and index support functions  *
  7.2848 - *-----------------------------------------------------------*/
  7.2849 -
  7.2850 -/* macro for a B-tree operator (without detoasting) */
  7.2851 -#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \
  7.2852 -  PG_FUNCTION_INFO_V1(func); \
  7.2853 -  Datum func(PG_FUNCTION_ARGS) { \
  7.2854 -    type *a = (type *)PG_GETARG_POINTER(0); \
  7.2855 -    type *b = (type *)PG_GETARG_POINTER(1); \
  7.2856 -    PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \
  7.2857 -  }
  7.2858 -
  7.2859 -/* macro for a B-tree comparison function (without detoasting) */
  7.2860 -#define PGL_BTREE_CMP(func, type, cmpfunc) \
  7.2861 -  PG_FUNCTION_INFO_V1(func); \
  7.2862 -  Datum func(PG_FUNCTION_ARGS) { \
  7.2863 -    type *a = (type *)PG_GETARG_POINTER(0); \
  7.2864 -    type *b = (type *)PG_GETARG_POINTER(1); \
  7.2865 -    PG_RETURN_INT32(cmpfunc(a, b)); \
  7.2866 -  }
  7.2867 -
  7.2868 -/* macro for a B-tree operator (with detoasting) */
  7.2869 -#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \
  7.2870 -  PG_FUNCTION_INFO_V1(func); \
  7.2871 -  Datum func(PG_FUNCTION_ARGS) { \
  7.2872 -    bool res; \
  7.2873 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  7.2874 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  7.2875 -    res = cmpfunc(a, b) oper 0; \
  7.2876 -    PG_FREE_IF_COPY(a, 0); \
  7.2877 -    PG_FREE_IF_COPY(b, 1); \
  7.2878 -    PG_RETURN_BOOL(res); \
  7.2879 -  }
  7.2880 -
  7.2881 -/* macro for a B-tree comparison function (with detoasting) */
  7.2882 -#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \
  7.2883 -  PG_FUNCTION_INFO_V1(func); \
  7.2884 -  Datum func(PG_FUNCTION_ARGS) { \
  7.2885 -    int32_t res; \
  7.2886 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  7.2887 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  7.2888 -    res = cmpfunc(a, b); \
  7.2889 -    PG_FREE_IF_COPY(a, 0); \
  7.2890 -    PG_FREE_IF_COPY(b, 1); \
  7.2891 -    PG_RETURN_INT32(res); \
  7.2892 -  }
  7.2893 -
  7.2894 -/* B-tree operators and comparison function for point */
  7.2895 -PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <)
  7.2896 -PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=)
  7.2897 -PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==)
  7.2898 -PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=)
  7.2899 -PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=)
  7.2900 -PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >)
  7.2901 -PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp)
  7.2902 -
  7.2903 -/* B-tree operators and comparison function for box */
  7.2904 -PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <)
  7.2905 -PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=)
  7.2906 -PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==)
  7.2907 -PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=)
  7.2908 -PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=)
  7.2909 -PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >)
  7.2910 -PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp)
  7.2911 -
  7.2912 -/* B-tree operators and comparison function for circle */
  7.2913 -PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <)
  7.2914 -PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=)
  7.2915 -PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==)
  7.2916 -PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=)
  7.2917 -PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=)
  7.2918 -PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >)
  7.2919 -PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp)
  7.2920 -
  7.2921 -
  7.2922 -/*--------------------------------*
  7.2923 - *  GiST index support functions  *
  7.2924 - *--------------------------------*/
  7.2925 -
  7.2926 -/* GiST "consistent" support function */
  7.2927 -PG_FUNCTION_INFO_V1(pgl_gist_consistent);
  7.2928 -Datum pgl_gist_consistent(PG_FUNCTION_ARGS) {
  7.2929 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  7.2930 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  7.2931 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  7.2932 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  7.2933 -  /* demand recheck because index and query methods are lossy */
  7.2934 -  *recheck = true;
  7.2935 -  /* strategy number aliases for different operators using the same strategy */
  7.2936 -  strategy %= 100;
  7.2937 -  /* strategy number 11: equality of two points */
  7.2938 -  if (strategy == 11) {
  7.2939 -    /* query datum is another point */
  7.2940 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  7.2941 -    /* convert other point to key */
  7.2942 -    pgl_pointkey querykey;
  7.2943 -    pgl_point_to_key(query, querykey);
  7.2944 -    /* return true if both keys overlap */
  7.2945 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  7.2946 -  }
  7.2947 -  /* strategy number 13: equality of two circles */
  7.2948 -  if (strategy == 13) {
  7.2949 -    /* query datum is another circle */
  7.2950 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  7.2951 -    /* convert other circle to key */
  7.2952 -    pgl_areakey querykey;
  7.2953 -    pgl_circle_to_key(query, querykey);
  7.2954 -    /* return true if both keys overlap */
  7.2955 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  7.2956 -  }
  7.2957 -  /* for all remaining strategies, keys on empty objects produce no match */
  7.2958 -  /* (check necessary because query radius may be infinite) */
  7.2959 -  if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false);
  7.2960 -  /* strategy number 21: overlapping with point */
  7.2961 -  if (strategy == 21) {
  7.2962 -    /* query datum is a point */
  7.2963 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  7.2964 -    /* return true if estimated distance (allowed to be smaller than real
  7.2965 -       distance) between index key and point is zero */
  7.2966 -    PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0);
  7.2967 -  }
  7.2968 -  /* strategy number 22: (point) overlapping with box */
  7.2969 -  if (strategy == 22) {
  7.2970 -    /* query datum is a box */
  7.2971 -    pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1);
  7.2972 -    /* determine bounding box of indexed key */
  7.2973 -    pgl_box keybox;
  7.2974 -    pgl_key_to_box(key, &keybox);
  7.2975 -    /* return true if query box overlaps with bounding box of indexed key */
  7.2976 -    PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox));
  7.2977 -  }
  7.2978 -  /* strategy number 23: overlapping with circle */
  7.2979 -  if (strategy == 23) {
  7.2980 -    /* query datum is a circle */
  7.2981 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  7.2982 -    /* return true if estimated distance (allowed to be smaller than real
  7.2983 -       distance) between index key and circle center is smaller than radius */
  7.2984 -    PG_RETURN_BOOL(
  7.2985 -      (1.0-PGL_SPHEROID_F) *  /* safety margin for lossy operator */
  7.2986 -      pgl_estimate_key_distance(key, &(query->center))
  7.2987 -      <= query->radius
  7.2988 -    );
  7.2989 -  }
  7.2990 -  /* strategy number 24: overlapping with cluster */
  7.2991 -  if (strategy == 24) {
  7.2992 -    bool retval;  /* return value */
  7.2993 -    /* query datum is a cluster */
  7.2994 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.2995 -    /* return true if estimated distance (allowed to be smaller than real
  7.2996 -       distance) between index key and circle center is smaller than radius */
  7.2997 -    retval = (
  7.2998 -      (1.0-PGL_SPHEROID_F) *  /* safety margin for lossy operator */
  7.2999 -      pgl_estimate_key_distance(key, &(query->bounding.center))
  7.3000 -      <= query->bounding.radius
  7.3001 -    );
  7.3002 -    PG_FREE_IF_COPY(query, 1);  /* free detoasted cluster (if copy) */
  7.3003 -    PG_RETURN_BOOL(retval);
  7.3004 -  }
  7.3005 -  /* throw error for any unknown strategy number */
  7.3006 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  7.3007 -}
  7.3008 -
  7.3009 -/* GiST "union" support function */
  7.3010 -PG_FUNCTION_INFO_V1(pgl_gist_union);
  7.3011 -Datum pgl_gist_union(PG_FUNCTION_ARGS) {
  7.3012 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  7.3013 -  pgl_keyptr out;  /* return value (to be palloc'ed) */
  7.3014 -  int i;
  7.3015 -  /* determine key size */
  7.3016 -  size_t keysize = PGL_KEY_IS_AREAKEY(
  7.3017 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key)
  7.3018 -  ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey);
  7.3019 -  /* begin with first key as result */
  7.3020 -  out = palloc(keysize);
  7.3021 -  memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize);
  7.3022 -  /* unite current result with second, third, etc. key */
  7.3023 -  for (i=1; i<entryvec->n; i++) {
  7.3024 -    pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key));
  7.3025 -  }
  7.3026 -  /* return result */
  7.3027 -  PG_RETURN_POINTER(out);
  7.3028 -}
  7.3029 -
  7.3030 -/* GiST "compress" support function for indicis on points */
  7.3031 -PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint);
  7.3032 -Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) {
  7.3033 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  7.3034 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  7.3035 -  /* only transform new leaves */
  7.3036 -  if (entry->leafkey) {
  7.3037 -    /* get point to be transformed */
  7.3038 -    pgl_point *point = (pgl_point *)DatumGetPointer(entry->key);
  7.3039 -    /* allocate memory for key */
  7.3040 -    pgl_keyptr key = palloc(sizeof(pgl_pointkey));
  7.3041 -    /* transform point to key */
  7.3042 -    pgl_point_to_key(point, key);
  7.3043 -    /* create new GISTENTRY structure as return value */
  7.3044 -    retval = palloc(sizeof(GISTENTRY));
  7.3045 -    gistentryinit(
  7.3046 -      *retval, PointerGetDatum(key),
  7.3047 -      entry->rel, entry->page, entry->offset, false
  7.3048 -    );
  7.3049 -  } else {
  7.3050 -    /* inner nodes have already been transformed */
  7.3051 -    retval = entry;
  7.3052 -  }
  7.3053 -  /* return pointer to old or new GISTENTRY structure */
  7.3054 -  PG_RETURN_POINTER(retval);
  7.3055 -}
  7.3056 -
  7.3057 -/* GiST "compress" support function for indicis on circles */
  7.3058 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle);
  7.3059 -Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) {
  7.3060 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  7.3061 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  7.3062 -  /* only transform new leaves */
  7.3063 -  if (entry->leafkey) {
  7.3064 -    /* get circle to be transformed */
  7.3065 -    pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key);
  7.3066 -    /* allocate memory for key */
  7.3067 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  7.3068 -    /* transform circle to key */
  7.3069 -    pgl_circle_to_key(circle, key);
  7.3070 -    /* create new GISTENTRY structure as return value */
  7.3071 -    retval = palloc(sizeof(GISTENTRY));
  7.3072 -    gistentryinit(
  7.3073 -      *retval, PointerGetDatum(key),
  7.3074 -      entry->rel, entry->page, entry->offset, false
  7.3075 -    );
  7.3076 -  } else {
  7.3077 -    /* inner nodes have already been transformed */
  7.3078 -    retval = entry;
  7.3079 -  }
  7.3080 -  /* return pointer to old or new GISTENTRY structure */
  7.3081 -  PG_RETURN_POINTER(retval);
  7.3082 -}
  7.3083 -
  7.3084 -/* GiST "compress" support function for indices on clusters */
  7.3085 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster);
  7.3086 -Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) {
  7.3087 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  7.3088 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  7.3089 -  /* only transform new leaves */
  7.3090 -  if (entry->leafkey) {
  7.3091 -    /* get cluster to be transformed (detoasting necessary!) */
  7.3092 -    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key);
  7.3093 -    /* allocate memory for key */
  7.3094 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  7.3095 -    /* transform cluster to key */
  7.3096 -    pgl_circle_to_key(&(cluster->bounding), key);
  7.3097 -    /* create new GISTENTRY structure as return value */
  7.3098 -    retval = palloc(sizeof(GISTENTRY));
  7.3099 -    gistentryinit(
  7.3100 -      *retval, PointerGetDatum(key),
  7.3101 -      entry->rel, entry->page, entry->offset, false
  7.3102 -    );
  7.3103 -    /* free detoasted datum */
  7.3104 -    if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster);
  7.3105 -  } else {
  7.3106 -    /* inner nodes have already been transformed */
  7.3107 -    retval = entry;
  7.3108 -  }
  7.3109 -  /* return pointer to old or new GISTENTRY structure */
  7.3110 -  PG_RETURN_POINTER(retval);
  7.3111 -}
  7.3112 -
  7.3113 -/* GiST "decompress" support function for indices */
  7.3114 -PG_FUNCTION_INFO_V1(pgl_gist_decompress);
  7.3115 -Datum pgl_gist_decompress(PG_FUNCTION_ARGS) {
  7.3116 -  /* return passed pointer without transformation */
  7.3117 -  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
  7.3118 -}
  7.3119 -
  7.3120 -/* GiST "penalty" support function */
  7.3121 -PG_FUNCTION_INFO_V1(pgl_gist_penalty);
  7.3122 -Datum pgl_gist_penalty(PG_FUNCTION_ARGS) {
  7.3123 -  GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0);
  7.3124 -  GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1);
  7.3125 -  float *penalty = (float *)PG_GETARG_POINTER(2);
  7.3126 -  /* get original key and key to insert */
  7.3127 -  pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key);
  7.3128 -  pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key);
  7.3129 -  /* copy original key */
  7.3130 -  union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key;
  7.3131 -  if (PGL_KEY_IS_AREAKEY(orig)) {
  7.3132 -    memcpy(union_key.areakey, orig, sizeof(union_key.areakey));
  7.3133 -  } else {
  7.3134 -    memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey));
  7.3135 -  }
  7.3136 -  /* calculate union of both keys */
  7.3137 -  pgl_unite_keys((pgl_keyptr)&union_key, new);
  7.3138 -  /* penalty equal to reduction of key length (logarithm of added area) */
  7.3139 -  /* (return value by setting referenced value and returning pointer) */
  7.3140 -  *penalty = (
  7.3141 -    PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key)
  7.3142 -  );
  7.3143 -  PG_RETURN_POINTER(penalty);
  7.3144 -}
  7.3145 -
  7.3146 -/* GiST "picksplit" support function */
  7.3147 -PG_FUNCTION_INFO_V1(pgl_gist_picksplit);
  7.3148 -Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) {
  7.3149 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  7.3150 -  GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1);
  7.3151 -  OffsetNumber i;  /* between FirstOffsetNumber and entryvec->n (exclusive) */
  7.3152 -  union {
  7.3153 -    pgl_pointkey pointkey;
  7.3154 -    pgl_areakey areakey;
  7.3155 -  } union_all;  /* union of all keys (to be calculated from scratch)
  7.3156 -                   (later cut in half) */
  7.3157 -  int is_areakey = PGL_KEY_IS_AREAKEY(
  7.3158 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key)
  7.3159 -  );
  7.3160 -  int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey);
  7.3161 -  pgl_keyptr unionL = palloc(keysize);  /* union of keys that go left */
  7.3162 -  pgl_keyptr unionR = palloc(keysize);  /* union of keys that go right */
  7.3163 -  pgl_keyptr key;  /* current key to be processed */
  7.3164 -  /* allocate memory for array of left and right keys, set counts to zero */
  7.3165 -  v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  7.3166 -  v->spl_nleft = 0;
  7.3167 -  v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  7.3168 -  v->spl_nright = 0;
  7.3169 -  /* calculate union of all keys from scratch */
  7.3170 -  memcpy(
  7.3171 -    (pgl_keyptr)&union_all,
  7.3172 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key),
  7.3173 -    keysize
  7.3174 -  );
  7.3175 -  for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) {
  7.3176 -    pgl_unite_keys(
  7.3177 -      (pgl_keyptr)&union_all,
  7.3178 -      (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)
  7.3179 -    );
  7.3180 -  }
  7.3181 -  /* check if trivial split is necessary due to exhausted key length */
  7.3182 -  /* (Note: keys for empty objects must have node depth set to maximum) */
  7.3183 -  if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == (
  7.3184 -    is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH
  7.3185 -  )) {
  7.3186 -    /* half of all keys go left */
  7.3187 -    for (
  7.3188 -      i=FirstOffsetNumber;
  7.3189 -      i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  7.3190 -      i=OffsetNumberNext(i)
  7.3191 -    ) {
  7.3192 -      /* pointer to current key */
  7.3193 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  7.3194 -      /* update unionL */
  7.3195 -      /* check if key is first key that goes left */
  7.3196 -      if (!v->spl_nleft) {
  7.3197 -        /* first key that goes left is just copied to unionL */
  7.3198 -        memcpy(unionL, key, keysize);
  7.3199 -      } else {
  7.3200 -        /* unite current value and next key */
  7.3201 -        pgl_unite_keys(unionL, key);
  7.3202 -      }
  7.3203 -      /* append offset number to list of keys that go left */
  7.3204 -      v->spl_left[v->spl_nleft++] = i;
  7.3205 -    }
  7.3206 -    /* other half goes right */
  7.3207 -    for (
  7.3208 -      i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  7.3209 -      i<entryvec->n;
  7.3210 -      i=OffsetNumberNext(i)
  7.3211 -    ) {
  7.3212 -      /* pointer to current key */
  7.3213 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  7.3214 -      /* update unionR */
  7.3215 -      /* check if key is first key that goes right */
  7.3216 -      if (!v->spl_nright) {
  7.3217 -        /* first key that goes right is just copied to unionR */
  7.3218 -        memcpy(unionR, key, keysize);
  7.3219 -      } else {
  7.3220 -        /* unite current value and next key */
  7.3221 -        pgl_unite_keys(unionR, key);
  7.3222 -      }
  7.3223 -      /* append offset number to list of keys that go right */
  7.3224 -      v->spl_right[v->spl_nright++] = i;
  7.3225 -    }
  7.3226 -  }
  7.3227 -  /* otherwise, a non-trivial split is possible */
  7.3228 -  else {
  7.3229 -    /* cut covered area in half */
  7.3230 -    /* (union_all then refers to area of keys that go left) */
  7.3231 -    /* check if union of all keys covers empty and non-empty objects */
  7.3232 -    if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) {
  7.3233 -      /* if yes, split into empty and non-empty objects */
  7.3234 -      pgl_key_set_empty((pgl_keyptr)&union_all);
  7.3235 -    } else {
  7.3236 -      /* otherwise split by next bit */
  7.3237 -      ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++;
  7.3238 -      /* NOTE: type bit conserved */
  7.3239 -    }
  7.3240 -    /* determine for each key if it goes left or right */
  7.3241 -    for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) {
  7.3242 -      /* pointer to current key */
  7.3243 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  7.3244 -      /* keys within one half of the area go left */
  7.3245 -      if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) {
  7.3246 -        /* update unionL */
  7.3247 -        /* check if key is first key that goes left */
  7.3248 -        if (!v->spl_nleft) {
  7.3249 -          /* first key that goes left is just copied to unionL */
  7.3250 -          memcpy(unionL, key, keysize);
  7.3251 -        } else {
  7.3252 -          /* unite current value of unionL and processed key */
  7.3253 -          pgl_unite_keys(unionL, key);
  7.3254 -        }
  7.3255 -        /* append offset number to list of keys that go left */
  7.3256 -        v->spl_left[v->spl_nleft++] = i;
  7.3257 -      }
  7.3258 -      /* the other keys go right */
  7.3259 -      else {
  7.3260 -        /* update unionR */
  7.3261 -        /* check if key is first key that goes right */
  7.3262 -        if (!v->spl_nright) {
  7.3263 -          /* first key that goes right is just copied to unionR */
  7.3264 -          memcpy(unionR, key, keysize);
  7.3265 -        } else {
  7.3266 -          /* unite current value of unionR and processed key */
  7.3267 -          pgl_unite_keys(unionR, key);
  7.3268 -        }
  7.3269 -        /* append offset number to list of keys that go right */
  7.3270 -        v->spl_right[v->spl_nright++] = i;
  7.3271 -      }
  7.3272 -    }
  7.3273 -  }
  7.3274 -  /* store unions in return value */
  7.3275 -  v->spl_ldatum = PointerGetDatum(unionL);
  7.3276 -  v->spl_rdatum = PointerGetDatum(unionR);
  7.3277 -  /* return all results */
  7.3278 -  PG_RETURN_POINTER(v);
  7.3279 -}
  7.3280 -
  7.3281 -/* GiST "same"/"equal" support function */
  7.3282 -PG_FUNCTION_INFO_V1(pgl_gist_same);
  7.3283 -Datum pgl_gist_same(PG_FUNCTION_ARGS) {
  7.3284 -  pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0);
  7.3285 -  pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1);
  7.3286 -  bool *boolptr = (bool *)PG_GETARG_POINTER(2);
  7.3287 -  /* two keys are equal if they are binary equal */
  7.3288 -  /* (return result by setting referenced boolean and returning pointer) */
  7.3289 -  *boolptr = !memcmp(
  7.3290 -    key1,
  7.3291 -    key2,
  7.3292 -    PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey)
  7.3293 -  );
  7.3294 -  PG_RETURN_POINTER(boolptr);
  7.3295 -}
  7.3296 -
  7.3297 -/* GiST "distance" support function */
  7.3298 -PG_FUNCTION_INFO_V1(pgl_gist_distance);
  7.3299 -Datum pgl_gist_distance(PG_FUNCTION_ARGS) {
  7.3300 -  GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
  7.3301 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  7.3302 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  7.3303 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  7.3304 -  double distance;  /* return value */
  7.3305 -  /* demand recheck because distance is just an estimation */
  7.3306 -  /* (real distance may be bigger) */
  7.3307 -  *recheck = true;
  7.3308 -  /* strategy number aliases for different operators using the same strategy */
  7.3309 -  strategy %= 100;
  7.3310 -  /* strategy number 31: distance to point */
  7.3311 -  if (strategy == 31) {
  7.3312 -    /* query datum is a point */
  7.3313 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  7.3314 -    /* use pgl_estimate_pointkey_distance() function to compute result */
  7.3315 -    distance = pgl_estimate_key_distance(key, query);
  7.3316 -    /* avoid infinity (reserved!) */
  7.3317 -    if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  7.3318 -    /* return result */
  7.3319 -    PG_RETURN_FLOAT8(distance);
  7.3320 -  }
  7.3321 -  /* strategy number 33: distance to circle */
  7.3322 -  if (strategy == 33) {
  7.3323 -    /* query datum is a circle */
  7.3324 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  7.3325 -    /* estimate distance to circle center and substract circle radius */
  7.3326 -    distance = (
  7.3327 -      pgl_estimate_key_distance(key, &(query->center)) - query->radius
  7.3328 -    );
  7.3329 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  7.3330 -    if (distance <= 0) distance = 0;
  7.3331 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  7.3332 -    /* return result */
  7.3333 -    PG_RETURN_FLOAT8(distance);
  7.3334 -  }
  7.3335 -  /* strategy number 34: distance to cluster */
  7.3336 -  if (strategy == 34) {
  7.3337 -    /* query datum is a cluster */
  7.3338 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  7.3339 -    /* estimate distance to bounding center and substract bounding radius */
  7.3340 -    distance = (
  7.3341 -      pgl_estimate_key_distance(key, &(query->bounding.center)) -
  7.3342 -      query->bounding.radius
  7.3343 -    );
  7.3344 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  7.3345 -    if (distance <= 0) distance = 0;
  7.3346 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  7.3347 -    /* free detoasted cluster (if copy) */
  7.3348 -    PG_FREE_IF_COPY(query, 1);
  7.3349 -    /* return result */
  7.3350 -    PG_RETURN_FLOAT8(distance);
  7.3351 -  }
  7.3352 -  /* throw error for any unknown strategy number */
  7.3353 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  7.3354 -}
  7.3355 -
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/latlon-v0010.c	Wed Feb 12 11:08:37 2020 +0100
     8.3 @@ -0,0 +1,3352 @@
     8.4 +
     8.5 +/*-------------*
     8.6 + *  C prelude  *
     8.7 + *-------------*/
     8.8 +
     8.9 +#include "postgres.h"
    8.10 +#include "fmgr.h"
    8.11 +#include "libpq/pqformat.h"
    8.12 +#include "access/gist.h"
    8.13 +#include "access/stratnum.h"
    8.14 +#include "utils/array.h"
    8.15 +#include <limits.h>
    8.16 +#include <math.h>
    8.17 +
    8.18 +#ifdef PG_MODULE_MAGIC
    8.19 +PG_MODULE_MAGIC;
    8.20 +#endif
    8.21 +
    8.22 +#if INT_MAX < 2147483647
    8.23 +#error Expected int type to be at least 32 bit wide
    8.24 +#endif
    8.25 +
    8.26 +
    8.27 +/*---------------------------------*
    8.28 + *  distance calculation on earth  *
    8.29 + *  (using WGS-84 spheroid)        *
    8.30 + *---------------------------------*/
    8.31 +
    8.32 +/*  WGS-84 spheroid with following parameters:
    8.33 +    semi-major axis  a = 6378137
    8.34 +    semi-minor axis  b = a * (1 - 1/298.257223563)
    8.35 +    estimated diameter = 2 * (2*a+b)/3
    8.36 +*/
    8.37 +#define PGL_SPHEROID_A 6378137.0            /* semi major axis */
    8.38 +#define PGL_SPHEROID_F (1.0/298.257223563)  /* flattening */
    8.39 +#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F))
    8.40 +#define PGL_EPS2       ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \
    8.41 +                           PGL_SPHEROID_B * PGL_SPHEROID_B ) / \
    8.42 +                         ( PGL_SPHEROID_A * PGL_SPHEROID_A ) )
    8.43 +#define PGL_SUBEPS2    (1.0-PGL_EPS2)
    8.44 +#define PGL_RADIUS     ((2.0*PGL_SPHEROID_A + PGL_SPHEROID_B) / 3.0)
    8.45 +#define PGL_DIAMETER   (2.0 * PGL_RADIUS)
    8.46 +#define PGL_SCALE      (PGL_SPHEROID_A / PGL_DIAMETER)  /* semi-major ref. */
    8.47 +#define PGL_MAXDIST    (PGL_RADIUS * M_PI)              /* maximum distance */
    8.48 +#define PGL_FADELIMIT  (PGL_MAXDIST / 3.0)              /* 1/6 circumference */
    8.49 +
    8.50 +/* calculate distance between two points on earth (given in degrees) */
    8.51 +static inline double pgl_distance(
    8.52 +  double lat1, double lon1, double lat2, double lon2
    8.53 +) {
    8.54 +  float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin;
    8.55 +  float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t;
    8.56 +  /* normalize delta longitude (lon2 > 0 && lon1 = 0) */
    8.57 +  /* lon1 = 0 (not used anymore) */
    8.58 +  lon2 = fabs(lon2-lon1);
    8.59 +  /* convert to radians (first divide, then multiply) */
    8.60 +  lat1 = (lat1 / 180.0) * M_PI;
    8.61 +  lat2 = (lat2 / 180.0) * M_PI;
    8.62 +  lon2 = (lon2 / 180.0) * M_PI;
    8.63 +  /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point
    8.64 +     operations (lon2 >= lon1 is already ensured in a previous step) */
    8.65 +  if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; }
    8.66 +  /* calculate 3d coordinates on scaled ellipsoid which has an average diameter
    8.67 +     of 1.0 */
    8.68 +  lat1cos = cos(lat1); lat1sin = sin(lat1);
    8.69 +  lat2cos = cos(lat2); lat2sin = sin(lat2);
    8.70 +  lon2cos = cos(lon2); lon2sin = sin(lon2);
    8.71 +  nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin);
    8.72 +  nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
    8.73 +  x1 = nphi1 * lat1cos;
    8.74 +  z1 = nphi1 * PGL_SUBEPS2 * lat1sin;
    8.75 +  x2 = nphi2 * lat2cos * lon2cos;
    8.76 +  y2 = nphi2 * lat2cos * lon2sin;
    8.77 +  z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
    8.78 +  /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */
    8.79 +  g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
    8.80 +  /* convert tunnel distance through scaled ellipsoid to approximated surface
    8.81 +     distance on original ellipsoid */
    8.82 +  if (g > 1.0) g = 1.0;
    8.83 +  s = PGL_DIAMETER * asin(g);
    8.84 +  /* return result only if small enough to be precise (less than 1/3 of
    8.85 +     maximum possible distance) */
    8.86 +  if (s <= PGL_FADELIMIT) return s;
    8.87 +  /* calculate tunnel distance to antipodal point through scaled ellipsoid */
    8.88 +  g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1));
    8.89 +  /* convert tunnel distance to antipodal point through scaled ellipsoid to
    8.90 +     approximated surface distance to antipodal point on original ellipsoid */
    8.91 +  if (g > 1.0) g = 1.0;
    8.92 +  t = PGL_DIAMETER * asin(g);
    8.93 +  /* surface distance between original points can now be approximated by
    8.94 +     substracting antipodal distance from maximum possible distance;
    8.95 +     return result only if small enough (less than 1/3 of maximum possible
    8.96 +     distance) */
    8.97 +  if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t;
    8.98 +  /* otherwise crossfade direct and antipodal result to ensure monotonicity */
    8.99 +  return (
   8.100 +    (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) /
   8.101 +    (s + t - 2*PGL_FADELIMIT)
   8.102 +  );
   8.103 +}
   8.104 +
   8.105 +/* finite distance that can not be reached on earth */
   8.106 +#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST)
   8.107 +
   8.108 +
   8.109 +/*--------------------------------*
   8.110 + *  simple geographic data types  *
   8.111 + *--------------------------------*/
   8.112 +
   8.113 +/* point on earth given by latitude and longitude in degrees */
   8.114 +/* (type "epoint" in SQL) */
   8.115 +typedef struct {
   8.116 +  double lat;  /* between  -90 and  90 (both inclusive) */
   8.117 +  double lon;  /* between -180 and 180 (both inclusive) */
   8.118 +} pgl_point;
   8.119 +
   8.120 +/* box delimited by two parallels and two meridians (all in degrees) */
   8.121 +/* (type "ebox" in SQL) */
   8.122 +typedef struct {
   8.123 +  double lat_min;  /* between  -90 and  90 (both inclusive) */
   8.124 +  double lat_max;  /* between  -90 and  90 (both inclusive) */
   8.125 +  double lon_min;  /* between -180 and 180 (both inclusive) */
   8.126 +  double lon_max;  /* between -180 and 180 (both inclusive) */
   8.127 +  /* if lat_min > lat_max, then box is empty */
   8.128 +  /* if lon_min > lon_max, then 180th meridian is crossed */
   8.129 +} pgl_box;
   8.130 +
   8.131 +/* circle on earth surface (for radial searches with fixed radius) */
   8.132 +/* (type "ecircle" in SQL) */
   8.133 +typedef struct {
   8.134 +  pgl_point center;
   8.135 +  double radius; /* positive (including +0 but excluding -0), or -INFINITY */
   8.136 +  /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point),
   8.137 +     zero radius (0) denotes a single point,
   8.138 +     a finite radius (0 < radius < INFINITY) denotes a filled circle, and
   8.139 +     a radius of INFINITY is valid and means complete coverage of earth. */
   8.140 +} pgl_circle;
   8.141 +
   8.142 +
   8.143 +/*----------------------------------*
   8.144 + *  geographic "cluster" data type  *
   8.145 + *----------------------------------*/
   8.146 +
   8.147 +/* A cluster is a collection of points, paths, outlines, and polygons. If two
   8.148 +   polygons in a cluster overlap, the area covered by both polygons does not
   8.149 +   belong to the cluster. This way, a cluster can be used to describe complex
   8.150 +   shapes like polygons with holes. Outlines are non-filled polygons. Paths are
   8.151 +   open by default (i.e. the last point in the list is not connected with the
   8.152 +   first point in the list). Note that each outline or polygon in a cluster
   8.153 +   must cover a longitude range of less than 180 degrees to avoid ambiguities.
   8.154 +   Areas which are larger may be split into multiple polygons. */
   8.155 +
   8.156 +/* maximum number of points in a cluster */
   8.157 +/* (limited to avoid integer overflows, e.g. when allocating memory) */
   8.158 +#define PGL_CLUSTER_MAXPOINTS 16777216
   8.159 +
   8.160 +/* types of cluster entries */
   8.161 +#define PGL_ENTRY_POINT   1  /* a point */
   8.162 +#define PGL_ENTRY_PATH    2  /* a path from first point to last point */
   8.163 +#define PGL_ENTRY_OUTLINE 3  /* a non-filled polygon with given vertices */
   8.164 +#define PGL_ENTRY_POLYGON 4  /* a filled polygon with given vertices */
   8.165 +
   8.166 +/* Entries of a cluster are described by two different structs: pgl_newentry
   8.167 +   and pgl_entry. The first is used only during construction of a cluster, the
   8.168 +   second is used in all other cases (e.g. when reading clusters from the
   8.169 +   database, performing operations, etc). */
   8.170 +
   8.171 +/* entry for new geographic cluster during construction of that cluster */
   8.172 +typedef struct {
   8.173 +  int32_t entrytype;
   8.174 +  int32_t npoints;
   8.175 +  pgl_point *points;  /* pointer to an array of points (pgl_point) */
   8.176 +} pgl_newentry;
   8.177 +
   8.178 +/* entry of geographic cluster */
   8.179 +typedef struct {
   8.180 +  int32_t entrytype;  /* type of entry: point, path, outline, polygon */
   8.181 +  int32_t npoints;    /* number of stored points (set to 1 for point entry) */
   8.182 +  int32_t offset;     /* offset of pgl_point array from cluster base address */
   8.183 +  /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */
   8.184 +} pgl_entry;
   8.185 +
   8.186 +/* geographic cluster which is a collection of points, (open) paths, polygons,
   8.187 +   and outlines (non-filled polygons) */
   8.188 +typedef struct {
   8.189 +  char header[VARHDRSZ];  /* PostgreSQL header for variable size data types */
   8.190 +  int32_t nentries;       /* number of stored points */
   8.191 +  pgl_circle bounding;    /* bounding circle */
   8.192 +  /* Note: bounding circle ensures alignment of pgl_cluster for points */
   8.193 +  pgl_entry entries[FLEXIBLE_ARRAY_MEMBER];  /* var-length data */
   8.194 +} pgl_cluster;
   8.195 +
   8.196 +/* macro to determine memory alignment of points */
   8.197 +/* (needed to store pgl_point array after entries in pgl_cluster) */
   8.198 +typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment;
   8.199 +#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned)
   8.200 +
   8.201 +/* macro to extract a pointer to the array of points of a cluster entry */
   8.202 +#define PGL_ENTRY_POINTS(cluster, idx) \
   8.203 +  ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset))
   8.204 +
   8.205 +/* convert pgl_newentry array to pgl_cluster */
   8.206 +/* NOTE: requires pgl_finalize_cluster to be called to finalize result */
   8.207 +static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) {
   8.208 +  int i;              /* index of current entry */
   8.209 +  int npoints = 0;    /* number of points in whole cluster */
   8.210 +  int entry_npoints;  /* number of points in current entry */
   8.211 +  int points_offset = PGL_POINT_ALIGNMENT * (
   8.212 +    ( offsetof(pgl_cluster, entries) +
   8.213 +      nentries * sizeof(pgl_entry) +
   8.214 +      PGL_POINT_ALIGNMENT - 1
   8.215 +    ) / PGL_POINT_ALIGNMENT
   8.216 +  );  /* offset of pgl_point array from base address (considering alignment) */
   8.217 +  pgl_cluster *cluster;  /* new cluster to be returned */
   8.218 +  /* determine total number of points */
   8.219 +  for (i=0; i<nentries; i++) npoints += entries[i].npoints;
   8.220 +  /* allocate memory for cluster (including entries and points) */
   8.221 +  cluster = palloc(points_offset + npoints * sizeof(pgl_point));
   8.222 +  /* re-count total number of points to determine offset for each entry */
   8.223 +  npoints = 0;
   8.224 +  /* copy entries and points */
   8.225 +  for (i=0; i<nentries; i++) {
   8.226 +    /* determine number of points in entry */
   8.227 +    entry_npoints = entries[i].npoints;
   8.228 +    /* copy entry */
   8.229 +    cluster->entries[i].entrytype = entries[i].entrytype;
   8.230 +    cluster->entries[i].npoints = entry_npoints;
   8.231 +    /* calculate offset (in bytes) of pgl_point array */
   8.232 +    cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point);
   8.233 +    /* copy points */
   8.234 +    memcpy(
   8.235 +      PGL_ENTRY_POINTS(cluster, i),
   8.236 +      entries[i].points,
   8.237 +      entry_npoints * sizeof(pgl_point)
   8.238 +    );
   8.239 +    /* update total number of points processed */
   8.240 +    npoints += entry_npoints;
   8.241 +  }
   8.242 +  /* set number of entries in cluster */
   8.243 +  cluster->nentries = nentries;
   8.244 +  /* set PostgreSQL header for variable sized data */
   8.245 +  SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point));
   8.246 +  /* return newly created cluster */
   8.247 +  return cluster;
   8.248 +}
   8.249 +
   8.250 +
   8.251 +/*----------------------------------------------*
   8.252 + *  Geographic point with integer sample count  *
   8.253 + *  (needed for fair distance calculation)      *
   8.254 + *----------------------------------------------*/
   8.255 +
   8.256 +typedef struct {
   8.257 +  pgl_point point;  /* NOTE: point first to allow C cast to pgl_point */
   8.258 +  int32 samples;
   8.259 +} pgl_point_sc;
   8.260 +
   8.261 +
   8.262 +/*----------------------------------------*
   8.263 + *  C functions on geographic data types  *
   8.264 + *----------------------------------------*/
   8.265 +
   8.266 +/* round latitude or longitude to 12 digits after decimal point */
   8.267 +static inline double pgl_round(double val) {
   8.268 +  return round(val * 1e12) / 1e12;
   8.269 +}
   8.270 +
   8.271 +/* compare two points */
   8.272 +/* (equality when same point on earth is described, otherwise an arbitrary
   8.273 +   linear order) */
   8.274 +static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) {
   8.275 +  double lon1, lon2;  /* modified longitudes for special cases */
   8.276 +  /* use latitude as first ordering criterion */
   8.277 +  if (point1->lat < point2->lat) return -1;
   8.278 +  if (point1->lat > point2->lat) return 1;
   8.279 +  /* determine modified longitudes (considering special case of poles and
   8.280 +     180th meridian which can be described as W180 or E180) */
   8.281 +  if (point1->lat == -90 || point1->lat == 90) lon1 = 0;
   8.282 +  else if (point1->lon == 180) lon1 = -180;
   8.283 +  else lon1 = point1->lon;
   8.284 +  if (point2->lat == -90 || point2->lat == 90) lon2 = 0;
   8.285 +  else if (point2->lon == 180) lon2 = -180;
   8.286 +  else lon2 = point2->lon;
   8.287 +  /* use (modified) longitude as secondary ordering criterion */
   8.288 +  if (lon1 < lon2) return -1;
   8.289 +  if (lon1 > lon2) return 1;
   8.290 +  /* no difference found, points are equal */
   8.291 +  return 0;
   8.292 +}
   8.293 +
   8.294 +/* compare two boxes */
   8.295 +/* (equality when same box on earth is described, otherwise an arbitrary linear
   8.296 +   order) */
   8.297 +static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) {
   8.298 +  /* two empty boxes are equal, and an empty box is always considered "less
   8.299 +     than" a non-empty box */
   8.300 +  if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1;
   8.301 +  if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0;
   8.302 +  if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1;
   8.303 +  /* use southern border as first ordering criterion */
   8.304 +  if (box1->lat_min < box2->lat_min) return -1;
   8.305 +  if (box1->lat_min > box2->lat_min) return 1;
   8.306 +  /* use northern border as second ordering criterion */
   8.307 +  if (box1->lat_max < box2->lat_max) return -1;
   8.308 +  if (box1->lat_max > box2->lat_max) return 1;
   8.309 +  /* use western border as third ordering criterion */
   8.310 +  if (box1->lon_min < box2->lon_min) return -1;
   8.311 +  if (box1->lon_min > box2->lon_min) return 1;
   8.312 +  /* use eastern border as fourth ordering criterion */
   8.313 +  if (box1->lon_max < box2->lon_max) return -1;
   8.314 +  if (box1->lon_max > box2->lon_max) return 1;
   8.315 +  /* no difference found, boxes are equal */
   8.316 +  return 0;
   8.317 +}
   8.318 +
   8.319 +/* compare two circles */
   8.320 +/* (equality when same circle on earth is described, otherwise an arbitrary
   8.321 +   linear order) */
   8.322 +static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) {
   8.323 +  /* two circles with same infinite radius (positive or negative infinity) are
   8.324 +     considered equal independently of center point */
   8.325 +  if (
   8.326 +    !isfinite(circle1->radius) && !isfinite(circle2->radius) &&
   8.327 +    circle1->radius == circle2->radius
   8.328 +  ) return 0;
   8.329 +  /* use radius as first ordering criterion */
   8.330 +  if (circle1->radius < circle2->radius) return -1;
   8.331 +  if (circle1->radius > circle2->radius) return 1;
   8.332 +  /* use center point as secondary ordering criterion */
   8.333 +  return pgl_point_cmp(&(circle1->center), &(circle2->center));
   8.334 +}
   8.335 +
   8.336 +/* set box to empty box*/
   8.337 +static void pgl_box_set_empty(pgl_box *box) {
   8.338 +  box->lat_min = INFINITY;
   8.339 +  box->lat_max = -INFINITY;
   8.340 +  box->lon_min = 0;
   8.341 +  box->lon_max = 0;
   8.342 +}
   8.343 +
   8.344 +/* check if point is inside a box */
   8.345 +static bool pgl_point_in_box(pgl_point *point, pgl_box *box) {
   8.346 +  return (
   8.347 +    point->lat >= box->lat_min && point->lat <= box->lat_max && (
   8.348 +      (box->lon_min > box->lon_max) ? (
   8.349 +        /* box crosses 180th meridian */
   8.350 +        point->lon >= box->lon_min || point->lon <= box->lon_max
   8.351 +      ) : (
   8.352 +        /* box does not cross the 180th meridian */
   8.353 +        point->lon >= box->lon_min && point->lon <= box->lon_max
   8.354 +      )
   8.355 +    )
   8.356 +  );
   8.357 +}
   8.358 +
   8.359 +/* check if two boxes overlap */
   8.360 +static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) {
   8.361 +  return (
   8.362 +    box2->lat_max >= box2->lat_min &&  /* ensure box2 is not empty */
   8.363 +    ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) &&
   8.364 +    ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && (
   8.365 +      (
   8.366 +        /* check if one and only one box crosses the 180th meridian */
   8.367 +        ((box1->lon_min > box1->lon_max) ? 1 : 0) ^
   8.368 +        ((box2->lon_min > box2->lon_max) ? 1 : 0)
   8.369 +      ) ? (
   8.370 +        /* exactly one box crosses the 180th meridian */
   8.371 +        box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min ||
   8.372 +        box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max
   8.373 +      ) : (
   8.374 +        /* no box or both boxes cross the 180th meridian */
   8.375 +        (
   8.376 +          (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) &&
   8.377 +          (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max)
   8.378 +        ) ||
   8.379 +        /* handle W180 == E180 */
   8.380 +        ( box1->lon_min == -180 && box2->lon_max == 180 ) ||
   8.381 +        ( box2->lon_min == -180 && box1->lon_max == 180 )
   8.382 +      )
   8.383 +    )
   8.384 +  );
   8.385 +}
   8.386 +
   8.387 +/* check unambiguousness of east/west orientation of cluster entries and set
   8.388 +   bounding circle of cluster */
   8.389 +static bool pgl_finalize_cluster(pgl_cluster *cluster) {
   8.390 +  int i, j;                 /* i: index of entry, j: index of point in entry */
   8.391 +  int npoints;              /* number of points in entry */
   8.392 +  int total_npoints = 0;    /* total number of points in cluster */
   8.393 +  pgl_point *points;        /* points in entry */
   8.394 +  int lon_dir;              /* first point of entry west (-1) or east (+1) */
   8.395 +  double lon_break = 0;     /* antipodal longitude of first point in entry */
   8.396 +  double lon_min, lon_max;  /* covered longitude range of entry */
   8.397 +  double value;             /* temporary variable */
   8.398 +  /* reset bounding circle center to empty circle at 0/0 coordinates */
   8.399 +  cluster->bounding.center.lat = 0;
   8.400 +  cluster->bounding.center.lon = 0;
   8.401 +  cluster->bounding.radius = -INFINITY;
   8.402 +  /* if cluster is not empty */
   8.403 +  if (cluster->nentries != 0) {
   8.404 +    /* iterate over all cluster entries and ensure they each cover a longitude
   8.405 +       range less than 180 degrees */
   8.406 +    for (i=0; i<cluster->nentries; i++) {
   8.407 +      /* get properties of entry */
   8.408 +      npoints = cluster->entries[i].npoints;
   8.409 +      points = PGL_ENTRY_POINTS(cluster, i);
   8.410 +      /* get longitude of first point of entry */
   8.411 +      value = points[0].lon;
   8.412 +      /* initialize lon_min and lon_max with longitude of first point */
   8.413 +      lon_min = value;
   8.414 +      lon_max = value;
   8.415 +      /* determine east/west orientation of first point and calculate antipodal
   8.416 +         longitude (Note: rounding required here) */
   8.417 +      if      (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); }
   8.418 +      else if (value > 0) { lon_dir =  1; lon_break = pgl_round(value - 180); }
   8.419 +      else lon_dir = 0;
   8.420 +      /* iterate over all other points in entry */
   8.421 +      for (j=1; j<npoints; j++) {
   8.422 +        /* consider longitude wrap-around */
   8.423 +        value = points[j].lon;
   8.424 +        if      (lon_dir<0 && value>lon_break) value = pgl_round(value - 360);
   8.425 +        else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360);
   8.426 +        /* update lon_min and lon_max */
   8.427 +        if      (value < lon_min) lon_min = value;
   8.428 +        else if (value > lon_max) lon_max = value;
   8.429 +        /* return false if 180 degrees or more are covered */
   8.430 +        if (lon_max - lon_min >= 180) return false;
   8.431 +      }
   8.432 +    }
   8.433 +    /* iterate over all points of all entries and calculate arbitrary center
   8.434 +       point for bounding circle (best if center point minimizes the radius,
   8.435 +       but some error is allowed here) */
   8.436 +    for (i=0; i<cluster->nentries; i++) {
   8.437 +      /* get properties of entry */
   8.438 +      npoints = cluster->entries[i].npoints;
   8.439 +      points = PGL_ENTRY_POINTS(cluster, i);
   8.440 +      /* check if first entry */
   8.441 +      if (i==0) {
   8.442 +        /* get longitude of first point of first entry in whole cluster */
   8.443 +        value = points[0].lon;
   8.444 +        /* initialize lon_min and lon_max with longitude of first point of
   8.445 +           first entry in whole cluster (used to determine if whole cluster
   8.446 +           covers a longitude range of 180 degrees or more) */
   8.447 +        lon_min = value;
   8.448 +        lon_max = value;
   8.449 +        /* determine east/west orientation of first point and calculate
   8.450 +           antipodal longitude (Note: rounding not necessary here) */
   8.451 +        if      (value < 0) { lon_dir = -1; lon_break = value + 180; }
   8.452 +        else if (value > 0) { lon_dir =  1; lon_break = value - 180; }
   8.453 +        else lon_dir = 0;
   8.454 +      }
   8.455 +      /* iterate over all points in entry */
   8.456 +      for (j=0; j<npoints; j++) {
   8.457 +        /* longitude wrap-around (Note: rounding not necessary here) */
   8.458 +        value = points[j].lon;
   8.459 +        if      (lon_dir < 0 && value > lon_break) value -= 360;
   8.460 +        else if (lon_dir > 0 && value < lon_break) value += 360;
   8.461 +        if      (value < lon_min) lon_min = value;
   8.462 +        else if (value > lon_max) lon_max = value;
   8.463 +        /* set bounding circle to cover whole earth if 180 degrees or more are
   8.464 +           covered */
   8.465 +        if (lon_max - lon_min >= 180) {
   8.466 +          cluster->bounding.center.lat = 0;
   8.467 +          cluster->bounding.center.lon = 0;
   8.468 +          cluster->bounding.radius = INFINITY;
   8.469 +          return true;
   8.470 +        }
   8.471 +        /* add point to bounding circle center (for average calculation) */
   8.472 +        cluster->bounding.center.lat += points[j].lat;
   8.473 +        cluster->bounding.center.lon += value;
   8.474 +      }
   8.475 +      /* count total number of points */
   8.476 +      total_npoints += npoints;
   8.477 +    }
   8.478 +    /* determine average latitude and longitude of cluster */
   8.479 +    cluster->bounding.center.lat /= total_npoints;
   8.480 +    cluster->bounding.center.lon /= total_npoints;
   8.481 +    /* normalize longitude of center of cluster bounding circle */
   8.482 +    if (cluster->bounding.center.lon < -180) {
   8.483 +      cluster->bounding.center.lon += 360;
   8.484 +    }
   8.485 +    else if (cluster->bounding.center.lon > 180) {
   8.486 +      cluster->bounding.center.lon -= 360;
   8.487 +    }
   8.488 +    /* round bounding circle center (useful if it is used by other functions) */
   8.489 +    cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat);
   8.490 +    cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon);
   8.491 +    /* calculate radius of bounding circle */
   8.492 +    for (i=0; i<cluster->nentries; i++) {
   8.493 +      npoints = cluster->entries[i].npoints;
   8.494 +      points = PGL_ENTRY_POINTS(cluster, i);
   8.495 +      for (j=0; j<npoints; j++) {
   8.496 +        value = pgl_distance(
   8.497 +          cluster->bounding.center.lat, cluster->bounding.center.lon,
   8.498 +          points[j].lat, points[j].lon
   8.499 +        );
   8.500 +        if (value > cluster->bounding.radius) cluster->bounding.radius = value;
   8.501 +      }
   8.502 +    }
   8.503 +  }
   8.504 +  /* return true (east/west orientation is unambiguous) */
   8.505 +  return true;
   8.506 +}
   8.507 +
   8.508 +/* check if point is inside cluster */
   8.509 +/* (if point is on perimeter, then true is returned if and only if
   8.510 +   strict == false) */
   8.511 +static bool pgl_point_in_cluster(
   8.512 +  pgl_point *point,
   8.513 +  pgl_cluster *cluster,
   8.514 +  bool strict
   8.515 +) {
   8.516 +  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   8.517 +  int entrytype;         /* type of entry */
   8.518 +  int npoints;           /* number of points in entry */
   8.519 +  pgl_point *points;     /* array of points in entry */
   8.520 +  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   8.521 +  double lon_break = 0;  /* antipodal longitude of first vertex */
   8.522 +  double lat0 = point->lat;  /* latitude of point */
   8.523 +  double lon0;           /* (adjusted) longitude of point */
   8.524 +  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   8.525 +  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   8.526 +  double lon;            /* longitude of intersection */
   8.527 +  int counter = 0;       /* counter for intersections east of point */
   8.528 +  /* iterate over all entries */
   8.529 +  for (i=0; i<cluster->nentries; i++) {
   8.530 +    /* get type of entry */
   8.531 +    entrytype = cluster->entries[i].entrytype;
   8.532 +    /* skip all entries but polygons if perimeters are excluded */
   8.533 +    if (strict && entrytype != PGL_ENTRY_POLYGON) continue;
   8.534 +    /* get points of entry */
   8.535 +    npoints = cluster->entries[i].npoints;
   8.536 +    points = PGL_ENTRY_POINTS(cluster, i);
   8.537 +    /* determine east/west orientation of first point of entry and calculate
   8.538 +       antipodal longitude */
   8.539 +    lon_break = points[0].lon;
   8.540 +    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   8.541 +    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   8.542 +    else lon_dir = 0;
   8.543 +    /* get longitude of point */
   8.544 +    lon0 = point->lon;
   8.545 +    /* consider longitude wrap-around for point */
   8.546 +    if      (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360);
   8.547 +    else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360);
   8.548 +    /* iterate over all edges and vertices */
   8.549 +    for (j=0; j<npoints; j++) {
   8.550 +      /* return if point is on vertex of polygon */
   8.551 +      if (pgl_point_cmp(point, &(points[j])) == 0) return !strict;
   8.552 +      /* calculate index of next vertex */
   8.553 +      k = (j+1) % npoints;
   8.554 +      /* skip last edge unless entry is (closed) outline or polygon */
   8.555 +      if (
   8.556 +        k == 0 &&
   8.557 +        entrytype != PGL_ENTRY_OUTLINE &&
   8.558 +        entrytype != PGL_ENTRY_POLYGON
   8.559 +      ) continue;
   8.560 +      /* use previously calculated values for lat1 and lon1 if possible */
   8.561 +      if (j) {
   8.562 +        lat1 = lat2;
   8.563 +        lon1 = lon2;
   8.564 +      } else {
   8.565 +        /* otherwise get latitude and longitude values of first vertex */
   8.566 +        lat1 = points[0].lat;
   8.567 +        lon1 = points[0].lon;
   8.568 +        /* and consider longitude wrap-around for first vertex */
   8.569 +        if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   8.570 +        else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   8.571 +      }
   8.572 +      /* get latitude and longitude of next vertex */
   8.573 +      lat2 = points[k].lat;
   8.574 +      lon2 = points[k].lon;
   8.575 +      /* consider longitude wrap-around for next vertex */
   8.576 +      if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
   8.577 +      else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
   8.578 +      /* return if point is on horizontal (west to east) edge of polygon */
   8.579 +      if (
   8.580 +        lat0 == lat1 && lat0 == lat2 &&
   8.581 +        ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) )
   8.582 +      ) return !strict;
   8.583 +      /* check if edge crosses east/west line of point */
   8.584 +      if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) {
   8.585 +        /* calculate longitude of intersection */
   8.586 +        lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1);
   8.587 +        /* return if intersection goes (approximately) through point */
   8.588 +        if (pgl_round(lon) == lon0) return !strict;
   8.589 +        /* count intersection if east of point and entry is polygon*/
   8.590 +        if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++;
   8.591 +      }
   8.592 +    }
   8.593 +  }
   8.594 +  /* return true if number of intersections is odd */
   8.595 +  return counter & 1;
   8.596 +}
   8.597 +
   8.598 +/* check if all points of the second cluster are strictly inside the first
   8.599 +   cluster */
   8.600 +static inline bool pgl_all_cluster_points_strictly_in_cluster(
   8.601 +  pgl_cluster *outer, pgl_cluster *inner
   8.602 +) {
   8.603 +  int i, j;           /* i: entry, j: point in entry */
   8.604 +  int npoints;        /* number of points in entry */
   8.605 +  pgl_point *points;  /* array of points in entry */
   8.606 +  /* iterate over all entries of "inner" cluster */
   8.607 +  for (i=0; i<inner->nentries; i++) {
   8.608 +    /* get properties of entry */
   8.609 +    npoints = inner->entries[i].npoints;
   8.610 +    points = PGL_ENTRY_POINTS(inner, i);
   8.611 +    /* iterate over all points in entry of "inner" cluster */
   8.612 +    for (j=0; j<npoints; j++) {
   8.613 +      /* return false if one point of inner cluster is not in outer cluster */
   8.614 +      if (!pgl_point_in_cluster(points+j, outer, true)) return false;
   8.615 +    }
   8.616 +  }
   8.617 +  /* otherwise return true */
   8.618 +  return true;
   8.619 +}
   8.620 +
   8.621 +/* check if any point the second cluster is inside the first cluster */
   8.622 +static inline bool pgl_any_cluster_points_in_cluster(
   8.623 +  pgl_cluster *outer, pgl_cluster *inner
   8.624 +) {
   8.625 +  int i, j;           /* i: entry, j: point in entry */
   8.626 +  int npoints;        /* number of points in entry */
   8.627 +  pgl_point *points;  /* array of points in entry */
   8.628 +  /* iterate over all entries of "inner" cluster */
   8.629 +  for (i=0; i<inner->nentries; i++) {
   8.630 +    /* get properties of entry */
   8.631 +    npoints = inner->entries[i].npoints;
   8.632 +    points = PGL_ENTRY_POINTS(inner, i);
   8.633 +    /* iterate over all points in entry of "inner" cluster */
   8.634 +    for (j=0; j<npoints; j++) {
   8.635 +      /* return true if one point of inner cluster is in outer cluster */
   8.636 +      if (pgl_point_in_cluster(points+j, outer, false)) return true;
   8.637 +    }
   8.638 +  }
   8.639 +  /* otherwise return false */
   8.640 +  return false;
   8.641 +}
   8.642 +
   8.643 +/* check if line segment strictly crosses line (not just touching) */
   8.644 +static inline bool pgl_lseg_crosses_line(
   8.645 +  double seg_x1,  double seg_y1,  double seg_x2,  double seg_y2,
   8.646 +  double line_x1, double line_y1, double line_x2, double line_y2
   8.647 +) {
   8.648 +  return (
   8.649 +    (
   8.650 +      (seg_x1-line_x1) * (line_y2-line_y1) -
   8.651 +      (seg_y1-line_y1) * (line_x2-line_x1)
   8.652 +    ) * (
   8.653 +      (seg_x2-line_x1) * (line_y2-line_y1) -
   8.654 +      (seg_y2-line_y1) * (line_x2-line_x1)
   8.655 +    )
   8.656 +  ) < 0;
   8.657 +}
   8.658 +
   8.659 +/* check if paths and outlines of two clusters strictly overlap (not just
   8.660 +   touching) */
   8.661 +static bool pgl_outlines_overlap(
   8.662 +  pgl_cluster *cluster1, pgl_cluster *cluster2
   8.663 +) {
   8.664 +  int i1, j1, k1;  /* i: entry, j: point in entry, k: next point in entry */
   8.665 +  int i2, j2, k2;
   8.666 +  int entrytype1, entrytype2;     /* type of entry */
   8.667 +  int npoints1, npoints2;         /* number of points in entry */
   8.668 +  pgl_point *points1;             /* array of points in entry of cluster1 */
   8.669 +  pgl_point *points2;             /* array of points in entry of cluster2 */
   8.670 +  int lon_dir1, lon_dir2;         /* first vertex west (-1) or east (+1) */
   8.671 +  double lon_break1, lon_break2;  /* antipodal longitude of first vertex */
   8.672 +  double lat11, lon11;  /* latitude and (adjusted) longitude of vertex */
   8.673 +  double lat12, lon12;  /* latitude and (adjusted) longitude of next vertex */
   8.674 +  double lat21, lon21;  /* latitude and (adjusted) longitudes for cluster2 */
   8.675 +  double lat22, lon22;
   8.676 +  double wrapvalue;     /* temporary helper value to adjust wrap-around */  
   8.677 +  /* iterate over all entries of cluster1 */
   8.678 +  for (i1=0; i1<cluster1->nentries; i1++) {
   8.679 +    /* get properties of entry in cluster1 and skip points */
   8.680 +    npoints1 = cluster1->entries[i1].npoints;
   8.681 +    if (npoints1 < 2) continue;
   8.682 +    entrytype1 = cluster1->entries[i1].entrytype;
   8.683 +    points1 = PGL_ENTRY_POINTS(cluster1, i1);
   8.684 +    /* determine east/west orientation of first point and calculate antipodal
   8.685 +       longitude */
   8.686 +    lon_break1 = points1[0].lon;
   8.687 +    if (lon_break1 < 0) {
   8.688 +      lon_dir1   = -1;
   8.689 +      lon_break1 = pgl_round(lon_break1 + 180);
   8.690 +    } else if (lon_break1 > 0) {
   8.691 +      lon_dir1   = 1;
   8.692 +      lon_break1 = pgl_round(lon_break1 - 180);
   8.693 +    } else lon_dir1 = 0;
   8.694 +    /* iterate over all edges and vertices in cluster1 */
   8.695 +    for (j1=0; j1<npoints1; j1++) {
   8.696 +      /* calculate index of next vertex */
   8.697 +      k1 = (j1+1) % npoints1;
   8.698 +      /* skip last edge unless entry is (closed) outline or polygon */
   8.699 +      if (
   8.700 +        k1 == 0 &&
   8.701 +        entrytype1 != PGL_ENTRY_OUTLINE &&
   8.702 +        entrytype1 != PGL_ENTRY_POLYGON
   8.703 +      ) continue;
   8.704 +      /* use previously calculated values for lat1 and lon1 if possible */
   8.705 +      if (j1) {
   8.706 +        lat11 = lat12;
   8.707 +        lon11 = lon12;
   8.708 +      } else {
   8.709 +        /* otherwise get latitude and longitude values of first vertex */
   8.710 +        lat11 = points1[0].lat;
   8.711 +        lon11 = points1[0].lon;
   8.712 +        /* and consider longitude wrap-around for first vertex */
   8.713 +        if      (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360);
   8.714 +        else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360);
   8.715 +      }
   8.716 +      /* get latitude and longitude of next vertex */
   8.717 +      lat12 = points1[k1].lat;
   8.718 +      lon12 = points1[k1].lon;
   8.719 +      /* consider longitude wrap-around for next vertex */
   8.720 +      if      (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360);
   8.721 +      else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360);
   8.722 +      /* skip degenerated edges */
   8.723 +      if (lat11 == lat12 && lon11 == lon12) continue;
   8.724 +      /* iterate over all entries of cluster2 */
   8.725 +      for (i2=0; i2<cluster2->nentries; i2++) {
   8.726 +        /* get points and number of points of entry in cluster2 */
   8.727 +        npoints2 = cluster2->entries[i2].npoints;
   8.728 +        if (npoints2 < 2) continue;
   8.729 +        entrytype2 = cluster2->entries[i2].entrytype;
   8.730 +        points2 = PGL_ENTRY_POINTS(cluster2, i2);
   8.731 +        /* determine east/west orientation of first point and calculate antipodal
   8.732 +           longitude */
   8.733 +        lon_break2 = points2[0].lon;
   8.734 +        if (lon_break2 < 0) {
   8.735 +          lon_dir2   = -1;
   8.736 +          lon_break2 = pgl_round(lon_break2 + 180);
   8.737 +        } else if (lon_break2 > 0) {
   8.738 +          lon_dir2   = 1;
   8.739 +          lon_break2 = pgl_round(lon_break2 - 180);
   8.740 +        } else lon_dir2 = 0;
   8.741 +        /* iterate over all edges and vertices in cluster2 */
   8.742 +        for (j2=0; j2<npoints2; j2++) {
   8.743 +          /* calculate index of next vertex */
   8.744 +          k2 = (j2+1) % npoints2;
   8.745 +          /* skip last edge unless entry is (closed) outline or polygon */
   8.746 +          if (
   8.747 +            k2 == 0 &&
   8.748 +            entrytype2 != PGL_ENTRY_OUTLINE &&
   8.749 +            entrytype2 != PGL_ENTRY_POLYGON
   8.750 +          ) continue;
   8.751 +          /* use previously calculated values for lat1 and lon1 if possible */
   8.752 +          if (j2) {
   8.753 +            lat21 = lat22;
   8.754 +            lon21 = lon22;
   8.755 +          } else {
   8.756 +            /* otherwise get latitude and longitude values of first vertex */
   8.757 +            lat21 = points2[0].lat;
   8.758 +            lon21 = points2[0].lon;
   8.759 +            /* and consider longitude wrap-around for first vertex */
   8.760 +            if      (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360);
   8.761 +            else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360);
   8.762 +          }
   8.763 +          /* get latitude and longitude of next vertex */
   8.764 +          lat22 = points2[k2].lat;
   8.765 +          lon22 = points2[k2].lon;
   8.766 +          /* consider longitude wrap-around for next vertex */
   8.767 +          if      (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360);
   8.768 +          else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360);
   8.769 +          /* skip degenerated edges */
   8.770 +          if (lat21 == lat22 && lon21 == lon22) continue;
   8.771 +          /* perform another wrap-around where necessary */
   8.772 +          /* TODO: improve performance of whole wrap-around mechanism */
   8.773 +          wrapvalue = (lon21 + lon22) - (lon11 + lon12);
   8.774 +          if (wrapvalue > 360) {
   8.775 +            lon21 = pgl_round(lon21 - 360);
   8.776 +            lon22 = pgl_round(lon22 - 360);
   8.777 +          } else if (wrapvalue < -360) {
   8.778 +            lon21 = pgl_round(lon21 + 360);
   8.779 +            lon22 = pgl_round(lon22 + 360);
   8.780 +          }
   8.781 +          /* return true if segments overlap */
   8.782 +          if (
   8.783 +            pgl_lseg_crosses_line(
   8.784 +              lat11, lon11, lat12, lon12,
   8.785 +              lat21, lon21, lat22, lon22
   8.786 +            ) && pgl_lseg_crosses_line(
   8.787 +              lat21, lon21, lat22, lon22,
   8.788 +              lat11, lon11, lat12, lon12
   8.789 +            )
   8.790 +          ) {
   8.791 +            return true;
   8.792 +          }
   8.793 +        }
   8.794 +      }
   8.795 +    }
   8.796 +  }
   8.797 +  /* otherwise return false */
   8.798 +  return false;
   8.799 +}
   8.800 +
   8.801 +/* check if second cluster is completely contained in first cluster */
   8.802 +static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) {
   8.803 +  if (!pgl_all_cluster_points_strictly_in_cluster(outer, inner)) return false;
   8.804 +  if (pgl_any_cluster_points_in_cluster(inner, outer)) return false;
   8.805 +  if (pgl_outlines_overlap(outer, inner)) return false;
   8.806 +  return true;
   8.807 +}
   8.808 +
   8.809 +/* check if two clusters overlap */
   8.810 +static bool pgl_clusters_overlap(
   8.811 +  pgl_cluster *cluster1, pgl_cluster *cluster2
   8.812 +) {
   8.813 +  if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true;
   8.814 +  if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true;
   8.815 +  if (pgl_outlines_overlap(cluster1, cluster2)) return true;
   8.816 +  return false;
   8.817 +}
   8.818 +
   8.819 +/* calculate (approximate) distance between point and cluster */
   8.820 +static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
   8.821 +  double comp;           /* square of compression of meridians */
   8.822 +  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   8.823 +  int entrytype;         /* type of entry */
   8.824 +  int npoints;           /* number of points in entry */
   8.825 +  pgl_point *points;     /* array of points in entry */
   8.826 +  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   8.827 +  double lon_break = 0;  /* antipodal longitude of first vertex */
   8.828 +  double lon_min = 0;    /* minimum (adjusted) longitude of entry vertices */
   8.829 +  double lon_max = 0;    /* maximum (adjusted) longitude of entry vertices */
   8.830 +  double lat0 = point->lat;  /* latitude of point */
   8.831 +  double lon0;           /* (adjusted) longitude of point */
   8.832 +  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   8.833 +  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   8.834 +  double s;              /* scalar for vector calculations */
   8.835 +  double dist;           /* distance calculated in one step */
   8.836 +  double min_dist = INFINITY;   /* minimum distance */
   8.837 +  /* distance is zero if point is contained in cluster */
   8.838 +  if (pgl_point_in_cluster(point, cluster, false)) return 0;
   8.839 +  /* calculate approximate square compression of meridians */
   8.840 +  comp = cos((lat0 / 180.0) * M_PI);
   8.841 +  comp *= comp;
   8.842 +  /* calculate exact square compression of meridians */
   8.843 +  comp *= (
   8.844 +    (1.0 - PGL_EPS2 * (1.0-comp)) *
   8.845 +    (1.0 - PGL_EPS2 * (1.0-comp)) /
   8.846 +    (PGL_SUBEPS2 * PGL_SUBEPS2)
   8.847 +  );
   8.848 +  /* iterate over all entries */
   8.849 +  for (i=0; i<cluster->nentries; i++) {
   8.850 +    /* get properties of entry */
   8.851 +    entrytype = cluster->entries[i].entrytype;
   8.852 +    npoints = cluster->entries[i].npoints;
   8.853 +    points = PGL_ENTRY_POINTS(cluster, i);
   8.854 +    /* determine east/west orientation of first point of entry and calculate
   8.855 +       antipodal longitude */
   8.856 +    lon_break = points[0].lon;
   8.857 +    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   8.858 +    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   8.859 +    else lon_dir = 0;
   8.860 +    /* determine covered longitude range */
   8.861 +    for (j=0; j<npoints; j++) {
   8.862 +      /* get longitude of vertex */
   8.863 +      lon1 = points[j].lon;
   8.864 +      /* adjust longitude to fix potential wrap-around */
   8.865 +      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   8.866 +      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   8.867 +      /* update minimum and maximum longitude of polygon */
   8.868 +      if (j == 0 || lon1 < lon_min) lon_min = lon1;
   8.869 +      if (j == 0 || lon1 > lon_max) lon_max = lon1;
   8.870 +    }
   8.871 +    /* adjust longitude wrap-around according to full longitude range */
   8.872 +    lon_break = (lon_max + lon_min) / 2;
   8.873 +    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   8.874 +    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   8.875 +    /* get longitude of point */
   8.876 +    lon0 = point->lon;
   8.877 +    /* consider longitude wrap-around for point */
   8.878 +    if      (lon_dir < 0 && lon0 > lon_break) lon0 -= 360;
   8.879 +    else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
   8.880 +    /* iterate over all edges and vertices */
   8.881 +    for (j=0; j<npoints; j++) {
   8.882 +      /* use previously calculated values for lat1 and lon1 if possible */
   8.883 +      if (j) {
   8.884 +        lat1 = lat2;
   8.885 +        lon1 = lon2;
   8.886 +      } else {
   8.887 +        /* otherwise get latitude and longitude values of first vertex */
   8.888 +        lat1 = points[0].lat;
   8.889 +        lon1 = points[0].lon;
   8.890 +        /* and consider longitude wrap-around for first vertex */
   8.891 +        if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   8.892 +        else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   8.893 +      }
   8.894 +      /* calculate distance to vertex */
   8.895 +      dist = pgl_distance(lat0, lon0, lat1, lon1);
   8.896 +      /* store calculated distance if smallest */
   8.897 +      if (dist < min_dist) min_dist = dist;
   8.898 +      /* calculate index of next vertex */
   8.899 +      k = (j+1) % npoints;
   8.900 +      /* skip last edge unless entry is (closed) outline or polygon */
   8.901 +      if (
   8.902 +        k == 0 &&
   8.903 +        entrytype != PGL_ENTRY_OUTLINE &&
   8.904 +        entrytype != PGL_ENTRY_POLYGON
   8.905 +      ) continue;
   8.906 +      /* get latitude and longitude of next vertex */
   8.907 +      lat2 = points[k].lat;
   8.908 +      lon2 = points[k].lon;
   8.909 +      /* consider longitude wrap-around for next vertex */
   8.910 +      if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
   8.911 +      else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
   8.912 +      /* go to next vertex and edge if edge is degenerated */
   8.913 +      if (lat1 == lat2 && lon1 == lon2) continue;
   8.914 +      /* otherwise test if point can be projected onto edge of polygon */
   8.915 +      s = (
   8.916 +        ((lat0-lat1) * (lat2-lat1) + comp * (lon0-lon1) * (lon2-lon1)) /
   8.917 +        ((lat2-lat1) * (lat2-lat1) + comp * (lon2-lon1) * (lon2-lon1))
   8.918 +      );
   8.919 +      /* go to next vertex and edge if point cannot be projected */
   8.920 +      if (!(s > 0 && s < 1)) continue;
   8.921 +      /* calculate distance from original point to projected point */
   8.922 +      dist = pgl_distance(
   8.923 +        lat0, lon0,
   8.924 +        lat1 + s * (lat2-lat1),
   8.925 +        lon1 + s * (lon2-lon1)
   8.926 +      );
   8.927 +      /* store calculated distance if smallest */
   8.928 +      if (dist < min_dist) min_dist = dist;
   8.929 +    }
   8.930 +  }
   8.931 +  /* return minimum distance */
   8.932 +  return min_dist;
   8.933 +}
   8.934 +
   8.935 +/* calculate (approximate) distance between two clusters */
   8.936 +static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) {
   8.937 +  int i, j;                    /* i: entry, j: point in entry */
   8.938 +  int npoints;                 /* number of points in entry */
   8.939 +  pgl_point *points;           /* array of points in entry */
   8.940 +  double dist;                 /* distance calculated in one step */
   8.941 +  double min_dist = INFINITY;  /* minimum distance */
   8.942 +  /* consider distance from each point in one cluster to the whole other */
   8.943 +  for (i=0; i<cluster1->nentries; i++) {
   8.944 +    npoints = cluster1->entries[i].npoints;
   8.945 +    points = PGL_ENTRY_POINTS(cluster1, i);
   8.946 +    for (j=0; j<npoints; j++) {
   8.947 +      dist = pgl_point_cluster_distance(points+j, cluster2);
   8.948 +      if (dist == 0) return dist;
   8.949 +      if (dist < min_dist) min_dist = dist;
   8.950 +    }
   8.951 +  }
   8.952 +  /* consider distance from each point in other cluster to the first cluster */
   8.953 +  for (i=0; i<cluster2->nentries; i++) {
   8.954 +    npoints = cluster2->entries[i].npoints;
   8.955 +    points = PGL_ENTRY_POINTS(cluster2, i);
   8.956 +    for (j=0; j<npoints; j++) {
   8.957 +      dist = pgl_point_cluster_distance(points+j, cluster1);
   8.958 +      if (dist == 0) return dist;
   8.959 +      if (dist < min_dist) min_dist = dist;
   8.960 +    }
   8.961 +  }
   8.962 +  return min_dist;
   8.963 +}
   8.964 +
   8.965 +/* estimator function for distance between box and point */
   8.966 +/* always returns a smaller value than actually correct or zero */
   8.967 +static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
   8.968 +  double dlon;      /* longitude range of box (delta longitude) */
   8.969 +  double distance;  /* return value */
   8.970 +  /* return infinity if box is empty */
   8.971 +  if (box->lat_min > box->lat_max) return INFINITY;
   8.972 +  /* return zero if point is inside box */
   8.973 +  if (pgl_point_in_box(point, box)) return 0;
   8.974 +  /* calculate delta longitude */
   8.975 +  dlon = box->lon_max - box->lon_min;
   8.976 +  if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
   8.977 +  /* if delta longitude is greater than 150 degrees, perform safe fall-back */
   8.978 +  if (dlon > 150) return 0;
   8.979 +  /* calculate lower limit for distance (formula below requires dlon <= 150) */
   8.980 +  /* TODO: provide better estimation function to improve performance */
   8.981 +  distance = (
   8.982 +    (1.0-PGL_SPHEROID_F) *  /* safety margin due to flattening and approx. */
   8.983 +    pgl_distance(
   8.984 +      point->lat,
   8.985 +      point->lon,
   8.986 +      (box->lat_min + box->lat_max) / 2,
   8.987 +      box->lon_min + dlon/2
   8.988 +    )
   8.989 +  ) - pgl_distance(
   8.990 +    box->lat_min, box->lon_min,
   8.991 +    box->lat_max, box->lon_max
   8.992 +  );
   8.993 +  /* truncate negative results to zero */
   8.994 +  if (distance <= 0) distance = 0;
   8.995 +  /* return result */
   8.996 +  return distance;
   8.997 +}
   8.998 +
   8.999 +
  8.1000 +/*------------------------------------------------------------*
  8.1001 + *  Functions using numerical integration (Monte Carlo like)  *
  8.1002 + *------------------------------------------------------------*/
  8.1003 +
  8.1004 +/* half of (spherical) earth's surface area */
  8.1005 +#define PGL_HALF_SURFACE (PGL_RADIUS * PGL_DIAMETER * M_PI)
  8.1006 +
  8.1007 +/* golden angle in radians */
  8.1008 +#define PGL_GOLDEN_ANGLE (M_PI * (sqrt(5) - 1.0))
  8.1009 +
  8.1010 +/* create a list of sample points covering a bounding circle 
  8.1011 +   and return covered area */
  8.1012 +static double pgl_sample_points(
  8.1013 +  pgl_point *center,  /* center of bounding circle */
  8.1014 +  double radius,      /* radius of bounding circle */
  8.1015 +  int samples,        /* number of sample points (MUST be positive!) */
  8.1016 +  pgl_point *result   /* pointer to result array */
  8.1017 +) {
  8.1018 +  double double_share = 2.0;  /* double of covered share of earth's surface */
  8.1019 +  double double_share_div_samples;  /* double_share divided by sample count */
  8.1020 +  int i;
  8.1021 +  double t;  /* parameter of spiral laid on (spherical) earth's surface */
  8.1022 +  double x, y, z;  /* normalized coordinates of point on non-rotated spiral */
  8.1023 +  double sin_phi;  /* sine of sph. coordinate of point of non-rotated spiral */
  8.1024 +  double lambda;   /* other sph. coordinate of point of non-rotated spiral */
  8.1025 +  double rot = (0.5 - center->lat / 180.0) * M_PI;  /* needed rot. (in rad) */
  8.1026 +  double cos_rot = cos(rot);  /* cosine of rotation by latitude */
  8.1027 +  double sin_rot = sin(rot);  /* sine of rotation by latitude */
  8.1028 +  double x_rot, z_rot;  /* normalized coordinates of point on rotated spiral */
  8.1029 +  double center_lon = center->lon;  /* second rotation in degree */
  8.1030 +  /* add safety margin to bounding circle because of spherical approximation */
  8.1031 +  radius *= PGL_SPHEROID_A / PGL_RADIUS;
  8.1032 +  /* if whole earth is covered, use initialized value, otherwise calculate
  8.1033 +     share of covered area (multiplied by 2) */
  8.1034 +  if (radius < PGL_MAXDIST) double_share = 1.0 - cos(radius / PGL_RADIUS);
  8.1035 +  /* divide double_share by sample count for later calculations */
  8.1036 +  double_share_div_samples = double_share / samples;
  8.1037 +  /* generate sample points */
  8.1038 +  for (i=0; i<samples; i++) {
  8.1039 +    /* use an offset of 1/2 to avoid too dense clustering at spiral center */
  8.1040 +    t = 0.5 + i;
  8.1041 +    /* calculate normalized coordinates of point on non-rotated spiral */
  8.1042 +    z = 1.0 - double_share_div_samples * t;
  8.1043 +    sin_phi = sqrt(1.0 - z*z);
  8.1044 +    lambda = t * PGL_GOLDEN_ANGLE;
  8.1045 +    x = sin_phi * cos(lambda);
  8.1046 +    y = sin_phi * sin(lambda);
  8.1047 +    /* rotate spiral by latitude value of bounding circle */
  8.1048 +    x_rot = cos_rot * x + sin_rot * z;
  8.1049 +    z_rot = cos_rot * z - sin_rot * x;
  8.1050 +    /* set resulting sample point in result array */
  8.1051 +    /* (while performing second rotation by bounding circle longitude) */
  8.1052 +    result[i].lat = 180.0 * (atan(z_rot / fabs(x_rot)) / M_PI);
  8.1053 +    result[i].lon = center_lon + 180.0 * (atan2(y, x_rot) / M_PI);
  8.1054 +  }
  8.1055 +  /* return covered area */
  8.1056 +  return PGL_HALF_SURFACE * double_share;
  8.1057 +}
  8.1058 +
  8.1059 +/* fair distance between point and cluster (see README file for explanation) */
  8.1060 +/* NOTE: sample count passed as third argument MUST be positive */
  8.1061 +static double pgl_fair_distance(
  8.1062 +  pgl_point *point, pgl_cluster *cluster, int samples
  8.1063 +) {
  8.1064 +  double distance;       /* shortest distance from point to cluster */
  8.1065 +  pgl_point *points;     /* sample points for numerical integration */
  8.1066 +  double area;           /* area covered by sample points */
  8.1067 +  int i;
  8.1068 +  int inner = 0;         /* number of sample points within cluster */
  8.1069 +  int outer = 0;         /* number of sample points outside cluster but
  8.1070 +                            within cluster enlarged by distance */
  8.1071 +  double result;
  8.1072 +  /* calculate shortest distance from point to cluster */
  8.1073 +  distance = pgl_point_cluster_distance(point, cluster);
  8.1074 +  /* if cluster consists of a single point or has no bounding circle with
  8.1075 +      positive radius, simply return distance */
  8.1076 +  if (
  8.1077 +    (cluster->nentries==1 && cluster->entries[0].entrytype==PGL_ENTRY_POINT) ||
  8.1078 +    !(cluster->bounding.radius > 0)
  8.1079 +  ) return distance;
  8.1080 +  /* if cluster consists of two points which are twice as far apart, return
  8.1081 +     distance between point and cluster multiplied by square root of two */
  8.1082 +  if (
  8.1083 +    cluster->nentries == 2 &&
  8.1084 +    cluster->entries[0].entrytype == PGL_ENTRY_POINT &&
  8.1085 +    cluster->entries[1].entrytype == PGL_ENTRY_POINT &&
  8.1086 +    pgl_distance(
  8.1087 +      PGL_ENTRY_POINTS(cluster, 0)[0].lat,
  8.1088 +      PGL_ENTRY_POINTS(cluster, 0)[0].lon,
  8.1089 +      PGL_ENTRY_POINTS(cluster, 1)[0].lat,
  8.1090 +      PGL_ENTRY_POINTS(cluster, 1)[0].lon
  8.1091 +    ) >= 2.0 * distance
  8.1092 +  ) {
  8.1093 +    return distance * M_SQRT2;
  8.1094 +  }
  8.1095 +  /* otherwise create sample points for numerical integration and determine
  8.1096 +     area covered by sample points */
  8.1097 +  points = palloc(samples * sizeof(pgl_point));
  8.1098 +  area = pgl_sample_points(
  8.1099 +    &cluster->bounding.center,
  8.1100 +    cluster->bounding.radius + distance,  /* pad bounding circle by distance */
  8.1101 +    samples,
  8.1102 +    points
  8.1103 +  );
  8.1104 +  /* perform numerical integration */
  8.1105 +  if (distance > 0) {
  8.1106 +    /* point (that was passed as argument) is outside cluster */
  8.1107 +    for (i=0; i<samples; i++) {
  8.1108 +      /* count sample points within cluster */
  8.1109 +      if (pgl_point_in_cluster(points+i, cluster, true)) inner++;
  8.1110 +      /* count sample points outside of cluster but within cluster enlarged by
  8.1111 +         distance between point (that was passed as argument) and cluster */
  8.1112 +      else if (
  8.1113 +        pgl_point_cluster_distance(points+i, cluster) < distance
  8.1114 +      ) outer++;
  8.1115 +    }
  8.1116 +  } else {
  8.1117 +    /* if point is within cluster, just count sample points within cluster */
  8.1118 +    for (i=0; i<samples; i++) {
  8.1119 +      if (pgl_point_in_cluster(points+i, cluster, true)) inner++;
  8.1120 +    }
  8.1121 +  }
  8.1122 +  /* release memory for sample points needed for numerical integration */
  8.1123 +  pfree(points);
  8.1124 +  /* if enlargement was less than doubling the area, then combine inner and
  8.1125 +     outer sample point counts with different weighting */
  8.1126 +  /* (ensures fairness in such a way that the integral of the squared result
  8.1127 +     over all possible point parameters is independent of the cluster) */
  8.1128 +  if (outer < inner) result = (2*inner + 4*outer) / 3.0;
  8.1129 +  /* otherwise weigh inner and outer points the same */
  8.1130 +  else result = inner + outer;
  8.1131 +  /* convert area into distance (i.e. radius of a circle with the same area) */
  8.1132 +  result = sqrt(area * (result / samples) / M_PI);
  8.1133 +  /* return result only if it is greater than the distance between point and
  8.1134 +     cluster to avoid unexpected results because of errors due to limited
  8.1135 +     precision */
  8.1136 +  if (result > distance) return result;
  8.1137 +  /* otherwise return distance between point and cluster */
  8.1138 +  else return distance;
  8.1139 +}
  8.1140 +
  8.1141 +
  8.1142 +/*-------------------------------------------------*
  8.1143 + *  geographic index based on space-filling curve  *
  8.1144 + *-------------------------------------------------*/
  8.1145 +
  8.1146 +/* number of bytes used for geographic (center) position in keys */
  8.1147 +#define PGL_KEY_LATLON_BYTELEN 7
  8.1148 +
  8.1149 +/* maximum reference value for logarithmic size of geographic objects */
  8.1150 +#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
  8.1151 +
  8.1152 +/* pointer to index key (either pgl_pointkey or pgl_areakey) */
  8.1153 +typedef unsigned char *pgl_keyptr;
  8.1154 +
  8.1155 +/* index key for points (objects with zero area) on the spheroid */
  8.1156 +/* bit  0..55: interspersed bits of latitude and longitude,
  8.1157 +   bit 56..57: always zero,
  8.1158 +   bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */
  8.1159 +typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1];
  8.1160 +
  8.1161 +/* index key for geographic objects on spheroid with area greater than zero */
  8.1162 +/* bit  0..55: interspersed bits of latitude and longitude of center point,
  8.1163 +   bit     56: always set to 1,
  8.1164 +   bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.),
  8.1165 +   bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to
  8.1166 +               PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth
  8.1167 +               = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL
  8.1168 +               (with interspersed bits = 0 and node depth = 0) for keys which
  8.1169 +               cover both empty and non-empty objects */
  8.1170 +
  8.1171 +typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2];
  8.1172 +
  8.1173 +/* helper macros for reading/writing index keys */
  8.1174 +#define PGL_KEY_NODEDEPTH_OFFSET  PGL_KEY_LATLON_BYTELEN
  8.1175 +#define PGL_KEY_OBJSIZE_OFFSET    (PGL_KEY_NODEDEPTH_OFFSET+1)
  8.1176 +#define PGL_POINTKEY_MAXDEPTH     (PGL_KEY_LATLON_BYTELEN*8)
  8.1177 +#define PGL_AREAKEY_MAXDEPTH      (2*PGL_POINTKEY_MAXDEPTH+1)
  8.1178 +#define PGL_AREAKEY_MAXOBJSIZE    (PGL_POINTKEY_MAXDEPTH+1)
  8.1179 +#define PGL_AREAKEY_TYPEMASK      0x80
  8.1180 +#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8)))
  8.1181 +#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \
  8.1182 +                                  ( PGL_KEY_LATLONBIT(key1, n) ^ \
  8.1183 +                                    PGL_KEY_LATLONBIT(key2, n) )
  8.1184 +#define PGL_KEY_IS_AREAKEY(key)   ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
  8.1185 +                                    PGL_AREAKEY_TYPEMASK)
  8.1186 +#define PGL_KEY_NODEDEPTH(key)    ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
  8.1187 +                                    (PGL_AREAKEY_TYPEMASK-1))
  8.1188 +#define PGL_KEY_OBJSIZE(key)      ((key)[PGL_KEY_OBJSIZE_OFFSET])
  8.1189 +#define PGL_KEY_OBJSIZE_EMPTY     126
  8.1190 +#define PGL_KEY_OBJSIZE_UNIVERSAL 127
  8.1191 +#define PGL_KEY_IS_EMPTY(key)     ( PGL_KEY_IS_AREAKEY(key) && \
  8.1192 +                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
  8.1193 +                                    PGL_KEY_OBJSIZE_EMPTY )
  8.1194 +#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \
  8.1195 +                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
  8.1196 +                                    PGL_KEY_OBJSIZE_UNIVERSAL )
  8.1197 +
  8.1198 +/* set area key to match empty objects only */
  8.1199 +static void pgl_key_set_empty(pgl_keyptr key) {
  8.1200 +  memset(key, 0, sizeof(pgl_areakey));
  8.1201 +  /* Note: setting node depth to maximum is required for picksplit function */
  8.1202 +  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
  8.1203 +  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY;
  8.1204 +}
  8.1205 +
  8.1206 +/* set area key to match any object (including empty objects) */
  8.1207 +static void pgl_key_set_universal(pgl_keyptr key) {
  8.1208 +  memset(key, 0, sizeof(pgl_areakey));
  8.1209 +  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK;
  8.1210 +  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL;
  8.1211 +}
  8.1212 +
  8.1213 +/* convert a point on earth into a max-depth key to be used in index */
  8.1214 +static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) {
  8.1215 +  double lat = point->lat;
  8.1216 +  double lon = point->lon;
  8.1217 +  int i;
  8.1218 +  /* clear latitude and longitude bits */
  8.1219 +  memset(key, 0, PGL_KEY_LATLON_BYTELEN);
  8.1220 +  /* set node depth to maximum and type bit to zero */
  8.1221 +  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH;
  8.1222 +  /* iterate over all latitude/longitude bit pairs */
  8.1223 +  for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) {
  8.1224 +    /* determine latitude bit */
  8.1225 +    if (lat >= 0) {
  8.1226 +      key[i/4] |= 0x80 >> (2*(i%4));
  8.1227 +      lat *= 2; lat -= 90;
  8.1228 +    } else {
  8.1229 +      lat *= 2; lat += 90;
  8.1230 +    }
  8.1231 +    /* determine longitude bit */
  8.1232 +    if (lon >= 0) {
  8.1233 +      key[i/4] |= 0x80 >> (2*(i%4)+1);
  8.1234 +      lon *= 2; lon -= 180;
  8.1235 +    } else {
  8.1236 +      lon *= 2; lon += 180;
  8.1237 +    }
  8.1238 +  }
  8.1239 +}
  8.1240 +
  8.1241 +/* convert a circle on earth into a max-depth key to be used in an index */
  8.1242 +static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) {
  8.1243 +  /* handle special case of empty circle */
  8.1244 +  if (circle->radius < 0) {
  8.1245 +    pgl_key_set_empty(key);
  8.1246 +    return;
  8.1247 +  }
  8.1248 +  /* perform same action as for point keys */
  8.1249 +  pgl_point_to_key(&(circle->center), key);
  8.1250 +  /* but overwrite type and node depth to fit area index key */
  8.1251 +  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
  8.1252 +  /* check if radius is greater than (or equal to) reference size */
  8.1253 +  /* (treat equal values as greater values for numerical safety) */
  8.1254 +  if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) {
  8.1255 +    /* if yes, set logarithmic size to zero */
  8.1256 +    key[PGL_KEY_OBJSIZE_OFFSET] = 0;
  8.1257 +  } else {
  8.1258 +    /* otherwise, determine logarithmic size iteratively */
  8.1259 +    /* (one step is equivalent to a factor of sqrt(2)) */
  8.1260 +    double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2;
  8.1261 +    int objsize = 1;
  8.1262 +    while (objsize < PGL_AREAKEY_MAXOBJSIZE) {
  8.1263 +      /* stop when radius is greater than (or equal to) adjusted reference */
  8.1264 +      /* (treat equal values as greater values for numerical safety) */
  8.1265 +      if (circle->radius >= reference) break;
  8.1266 +      reference /= M_SQRT2;
  8.1267 +      objsize++;
  8.1268 +    }
  8.1269 +    /* set logarithmic size to determined value */
  8.1270 +    key[PGL_KEY_OBJSIZE_OFFSET] = objsize;
  8.1271 +  }
  8.1272 +}
  8.1273 +
  8.1274 +/* check if one key is subkey of another key or vice versa */
  8.1275 +static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) {
  8.1276 +  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
  8.1277 +  /* determine smallest depth */
  8.1278 +  int depth1 = PGL_KEY_NODEDEPTH(key1);
  8.1279 +  int depth2 = PGL_KEY_NODEDEPTH(key2);
  8.1280 +  int depth = (depth1 < depth2) ? depth1 : depth2;
  8.1281 +  /* check if keys are area keys (assuming that both keys have same type) */
  8.1282 +  if (PGL_KEY_IS_AREAKEY(key1)) {
  8.1283 +    int j = 0;  /* bit offset for logarithmic object size bits */
  8.1284 +    int k = 0;  /* bit offset for latitude and longitude */
  8.1285 +    /* fetch logarithmic object size information */
  8.1286 +    int objsize1 = PGL_KEY_OBJSIZE(key1);
  8.1287 +    int objsize2 = PGL_KEY_OBJSIZE(key2);
  8.1288 +    /* handle special cases for empty objects (universal and empty keys) */
  8.1289 +    if (
  8.1290 +      objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL ||
  8.1291 +      objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL
  8.1292 +    ) return true;
  8.1293 +    if (
  8.1294 +      objsize1 == PGL_KEY_OBJSIZE_EMPTY ||
  8.1295 +      objsize2 == PGL_KEY_OBJSIZE_EMPTY
  8.1296 +    ) return objsize1 == objsize2;
  8.1297 +    /* iterate through key bits */
  8.1298 +    for (i=0; i<depth; i++) {
  8.1299 +      /* every second bit is a bit describing the object size */
  8.1300 +      if (i%2 == 0) {
  8.1301 +        /* check if object size bit is different in both keys (objsize1 and
  8.1302 +           objsize2 describe the minimum index when object size bit is set) */
  8.1303 +        if (
  8.1304 +          (objsize1 <= j && objsize2 > j) ||
  8.1305 +          (objsize2 <= j && objsize1 > j)
  8.1306 +        ) {
  8.1307 +          /* bit differs, therefore keys are in separate branches */
  8.1308 +          return false;
  8.1309 +        }
  8.1310 +        /* increase bit counter for object size bits */
  8.1311 +        j++;
  8.1312 +      }
  8.1313 +      /* all other bits describe latitude and longitude */
  8.1314 +      else {
  8.1315 +        /* check if bit differs in both keys */
  8.1316 +        if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) {
  8.1317 +          /* bit differs, therefore keys are in separate branches */
  8.1318 +          return false;
  8.1319 +        }
  8.1320 +        /* increase bit counter for latitude/longitude bits */
  8.1321 +        k++;
  8.1322 +      }
  8.1323 +    }
  8.1324 +  }
  8.1325 +  /* if not, keys are point keys */
  8.1326 +  else {
  8.1327 +    /* iterate through key bits */
  8.1328 +    for (i=0; i<depth; i++) {
  8.1329 +      /* check if bit differs in both keys */
  8.1330 +      if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) {
  8.1331 +        /* bit differs, therefore keys are in separate branches */
  8.1332 +        return false;
  8.1333 +      }
  8.1334 +    }
  8.1335 +  }
  8.1336 +  /* return true because keys are in the same branch */
  8.1337 +  return true;
  8.1338 +}
  8.1339 +
  8.1340 +/* combine two keys into new key which covers both original keys */
  8.1341 +/* (result stored in first argument) */
  8.1342 +static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) {
  8.1343 +  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
  8.1344 +  /* determine smallest depth */
  8.1345 +  int depth1 = PGL_KEY_NODEDEPTH(dst);
  8.1346 +  int depth2 = PGL_KEY_NODEDEPTH(src);
  8.1347 +  int depth = (depth1 < depth2) ? depth1 : depth2;
  8.1348 +  /* check if keys are area keys (assuming that both keys have same type) */
  8.1349 +  if (PGL_KEY_IS_AREAKEY(dst)) {
  8.1350 +    pgl_areakey dstbuf = { 0, };  /* destination buffer (cleared) */
  8.1351 +    int j = 0;  /* bit offset for logarithmic object size bits */
  8.1352 +    int k = 0;  /* bit offset for latitude and longitude */
  8.1353 +    /* fetch logarithmic object size information */
  8.1354 +    int objsize1 = PGL_KEY_OBJSIZE(dst);
  8.1355 +    int objsize2 = PGL_KEY_OBJSIZE(src);
  8.1356 +    /* handle special cases for empty objects (universal and empty keys) */
  8.1357 +    if (
  8.1358 +      objsize1 > PGL_AREAKEY_MAXOBJSIZE ||
  8.1359 +      objsize2 > PGL_AREAKEY_MAXOBJSIZE
  8.1360 +    ) {
  8.1361 +      if (
  8.1362 +        objsize1 == PGL_KEY_OBJSIZE_EMPTY &&
  8.1363 +        objsize2 == PGL_KEY_OBJSIZE_EMPTY
  8.1364 +      ) pgl_key_set_empty(dst);
  8.1365 +      else pgl_key_set_universal(dst);
  8.1366 +      return;
  8.1367 +    }
  8.1368 +    /* iterate through key bits */
  8.1369 +    for (i=0; i<depth; i++) {
  8.1370 +      /* every second bit is a bit describing the object size */
  8.1371 +      if (i%2 == 0) {
  8.1372 +        /* increase bit counter for object size bits first */
  8.1373 +        /* (handy when setting objsize variable) */
  8.1374 +        j++;
  8.1375 +        /* check if object size bit is set in neither key */
  8.1376 +        if (objsize1 >= j && objsize2 >= j) {
  8.1377 +          /* set objsize in destination buffer to indicate that size bit is
  8.1378 +             unset in destination buffer at the current bit position */
  8.1379 +          dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j;
  8.1380 +        }
  8.1381 +        /* break if object size bit is set in one key only */
  8.1382 +        else if (objsize1 >= j || objsize2 >= j) break;
  8.1383 +      }
  8.1384 +      /* all other bits describe latitude and longitude */
  8.1385 +      else {
  8.1386 +        /* break if bit differs in both keys */
  8.1387 +        if (PGL_KEY_LATLONBIT(dst, k)) {
  8.1388 +          if (!PGL_KEY_LATLONBIT(src, k)) break;
  8.1389 +          /* but set bit in destination buffer if bit is set in both keys */
  8.1390 +          dstbuf[k/8] |= 0x80 >> (k%8);
  8.1391 +        } else if (PGL_KEY_LATLONBIT(src, k)) break;
  8.1392 +        /* increase bit counter for latitude/longitude bits */
  8.1393 +        k++;
  8.1394 +      }
  8.1395 +    }
  8.1396 +    /* set common node depth and type bit (type bit = 1) */
  8.1397 +    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i;
  8.1398 +    /* copy contents of destination buffer to first key */
  8.1399 +    memcpy(dst, dstbuf, sizeof(pgl_areakey));
  8.1400 +  }
  8.1401 +  /* if not, keys are point keys */
  8.1402 +  else {
  8.1403 +    pgl_pointkey dstbuf = { 0, };  /* destination buffer (cleared) */
  8.1404 +    /* iterate through key bits */
  8.1405 +    for (i=0; i<depth; i++) {
  8.1406 +      /* break if bit differs in both keys */
  8.1407 +      if (PGL_KEY_LATLONBIT(dst, i)) {
  8.1408 +        if (!PGL_KEY_LATLONBIT(src, i)) break;
  8.1409 +        /* but set bit in destination buffer if bit is set in both keys */
  8.1410 +        dstbuf[i/8] |= 0x80 >> (i%8);
  8.1411 +      } else if (PGL_KEY_LATLONBIT(src, i)) break;
  8.1412 +    }
  8.1413 +    /* set common node depth (type bit = 0) */
  8.1414 +    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i;
  8.1415 +    /* copy contents of destination buffer to first key */
  8.1416 +    memcpy(dst, dstbuf, sizeof(pgl_pointkey));
  8.1417 +  }
  8.1418 +}
  8.1419 +
  8.1420 +/* determine center(!) boundaries and radius estimation of index key */
  8.1421 +static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) {
  8.1422 +  int i;
  8.1423 +  /* determine node depth */
  8.1424 +  int depth = PGL_KEY_NODEDEPTH(key);
  8.1425 +  /* center point of possible result */
  8.1426 +  double lat = 0;
  8.1427 +  double lon = 0;
  8.1428 +  /* maximum distance of real center point from key center */
  8.1429 +  double dlat = 90;
  8.1430 +  double dlon = 180;
  8.1431 +  /* maximum radius of contained objects */
  8.1432 +  double radius = 0;  /* always return zero for point index keys */
  8.1433 +  /* check if key is area key */
  8.1434 +  if (PGL_KEY_IS_AREAKEY(key)) {
  8.1435 +    /* get logarithmic object size */
  8.1436 +    int objsize = PGL_KEY_OBJSIZE(key);
  8.1437 +    /* handle special cases for empty objects (universal and empty keys) */
  8.1438 +    if (objsize == PGL_KEY_OBJSIZE_EMPTY) {
  8.1439 +      pgl_box_set_empty(box);
  8.1440 +      return 0;
  8.1441 +    } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) {
  8.1442 +      box->lat_min = -90;
  8.1443 +      box->lat_max =  90;
  8.1444 +      box->lon_min = -180;
  8.1445 +      box->lon_max =  180;
  8.1446 +      return 0;  /* any value >= 0 would do */
  8.1447 +    }
  8.1448 +    /* calculate maximum possible radius of objects covered by the given key */
  8.1449 +    if (objsize == 0) radius = INFINITY;
  8.1450 +    else {
  8.1451 +      radius = PGL_AREAKEY_REFOBJSIZE;
  8.1452 +      while (--objsize) radius /= M_SQRT2;
  8.1453 +    }
  8.1454 +    /* iterate over latitude and longitude bits in key */
  8.1455 +    /* (every second bit is a latitude or longitude bit) */
  8.1456 +    for (i=0; i<depth/2; i++) {
  8.1457 +      /* check if latitude bit */
  8.1458 +      if (i%2 == 0) {
  8.1459 +        /* cut latitude dimension in half */
  8.1460 +        dlat /= 2;
  8.1461 +        /* increase center latitude if bit is 1, otherwise decrease */
  8.1462 +        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  8.1463 +        else lat -= dlat;
  8.1464 +      }
  8.1465 +      /* otherwise longitude bit */
  8.1466 +      else {
  8.1467 +        /* cut longitude dimension in half */
  8.1468 +        dlon /= 2;
  8.1469 +        /* increase center longitude if bit is 1, otherwise decrease */
  8.1470 +        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  8.1471 +        else lon -= dlon;
  8.1472 +      }
  8.1473 +    }
  8.1474 +  }
  8.1475 +  /* if not, keys are point keys */
  8.1476 +  else {
  8.1477 +    /* iterate over all bits in key */
  8.1478 +    for (i=0; i<depth; i++) {
  8.1479 +      /* check if latitude bit */
  8.1480 +      if (i%2 == 0) {
  8.1481 +        /* cut latitude dimension in half */
  8.1482 +        dlat /= 2;
  8.1483 +        /* increase center latitude if bit is 1, otherwise decrease */
  8.1484 +        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  8.1485 +        else lat -= dlat;
  8.1486 +      }
  8.1487 +      /* otherwise longitude bit */
  8.1488 +      else {
  8.1489 +        /* cut longitude dimension in half */
  8.1490 +        dlon /= 2;
  8.1491 +        /* increase center longitude if bit is 1, otherwise decrease */
  8.1492 +        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  8.1493 +        else lon -= dlon;
  8.1494 +      }
  8.1495 +    }
  8.1496 +  }
  8.1497 +  /* calculate boundaries from center point and remaining dlat and dlon */
  8.1498 +  /* (return values through pointer to box) */
  8.1499 +  box->lat_min = lat - dlat;
  8.1500 +  box->lat_max = lat + dlat;
  8.1501 +  box->lon_min = lon - dlon;
  8.1502 +  box->lon_max = lon + dlon;
  8.1503 +  /* return radius (as a function return value) */
  8.1504 +  return radius;
  8.1505 +}
  8.1506 +
  8.1507 +/* estimator function for distance between point and index key */
  8.1508 +/* always returns a smaller value than actually correct or zero */
  8.1509 +static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
  8.1510 +  pgl_box box;  /* center(!) bounding box of area index key */
  8.1511 +  /* calculate center(!) bounding box and maximum radius of objects covered
  8.1512 +     by area index key (radius is zero for point index keys) */
  8.1513 +  double distance = pgl_key_to_box(key, &box);
  8.1514 +  /* calculate estimated distance between bounding box of center point of
  8.1515 +     indexed object and point passed as second argument, then substract maximum
  8.1516 +     radius of objects covered by index key */
  8.1517 +  distance = pgl_estimate_point_box_distance(point, &box) - distance;
  8.1518 +  /* truncate negative results to zero */
  8.1519 +  if (distance <= 0) distance = 0;
  8.1520 +  /* return result */
  8.1521 +  return distance;
  8.1522 +}
  8.1523 +
  8.1524 +
  8.1525 +/*---------------------------------*
  8.1526 + *  helper functions for text I/O  *
  8.1527 + *---------------------------------*/
  8.1528 +
  8.1529 +#define PGL_NUMBUFLEN 64  /* buffer size for number to string conversion */
  8.1530 +
  8.1531 +/* convert floating point number to string (round-trip safe) */
  8.1532 +static void pgl_print_float(char *buf, double flt) {
  8.1533 +  /* check if number is integral */
  8.1534 +  if (trunc(flt) == flt) {
  8.1535 +    /* for integral floats use maximum precision */
  8.1536 +    snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  8.1537 +  } else {
  8.1538 +    /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */
  8.1539 +    snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt);
  8.1540 +    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt);
  8.1541 +    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  8.1542 +  }
  8.1543 +}
  8.1544 +
  8.1545 +/* convert latitude floating point number (in degrees) to string */
  8.1546 +static void pgl_print_lat(char *buf, double lat) {
  8.1547 +  if (signbit(lat)) {
  8.1548 +    /* treat negative latitudes (including -0) as south */
  8.1549 +    snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat);
  8.1550 +  } else {
  8.1551 +    /* treat positive latitudes (including +0) as north */
  8.1552 +    snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat);
  8.1553 +  }
  8.1554 +}
  8.1555 +
  8.1556 +/* convert longitude floating point number (in degrees) to string */
  8.1557 +static void pgl_print_lon(char *buf, double lon) {
  8.1558 +  if (signbit(lon)) {
  8.1559 +    /* treat negative longitudes (including -0) as west */
  8.1560 +    snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon);
  8.1561 +  } else {
  8.1562 +    /* treat positive longitudes (including +0) as east */
  8.1563 +    snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon);
  8.1564 +  }
  8.1565 +}
  8.1566 +
  8.1567 +/* bit masks used as return value of pgl_scan() function */
  8.1568 +#define PGL_SCAN_NONE 0      /* no value has been parsed */
  8.1569 +#define PGL_SCAN_LAT (1<<0)  /* latitude has been parsed */
  8.1570 +#define PGL_SCAN_LON (1<<1)  /* longitude has been parsed */
  8.1571 +#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON)  /* bitwise OR of both */
  8.1572 +
  8.1573 +/* parse a coordinate (can be latitude or longitude) */
  8.1574 +static int pgl_scan(char **str, double *lat, double *lon) {
  8.1575 +  double val;
  8.1576 +  int len;
  8.1577 +  if (
  8.1578 +    sscanf(*str, " N %lf %n", &val, &len) ||
  8.1579 +    sscanf(*str, " n %lf %n", &val, &len)
  8.1580 +  ) {
  8.1581 +    *str += len; *lat = val; return PGL_SCAN_LAT;
  8.1582 +  }
  8.1583 +  if (
  8.1584 +    sscanf(*str, " S %lf %n", &val, &len) ||
  8.1585 +    sscanf(*str, " s %lf %n", &val, &len)
  8.1586 +  ) {
  8.1587 +    *str += len; *lat = -val; return PGL_SCAN_LAT;
  8.1588 +  }
  8.1589 +  if (
  8.1590 +    sscanf(*str, " E %lf %n", &val, &len) ||
  8.1591 +    sscanf(*str, " e %lf %n", &val, &len)
  8.1592 +  ) {
  8.1593 +    *str += len; *lon = val; return PGL_SCAN_LON;
  8.1594 +  }
  8.1595 +  if (
  8.1596 +    sscanf(*str, " W %lf %n", &val, &len) ||
  8.1597 +    sscanf(*str, " w %lf %n", &val, &len)
  8.1598 +  ) {
  8.1599 +    *str += len; *lon = -val; return PGL_SCAN_LON;
  8.1600 +  }
  8.1601 +  return PGL_SCAN_NONE;
  8.1602 +}
  8.1603 +
  8.1604 +
  8.1605 +/*-----------------*
  8.1606 + *  SQL functions  *
  8.1607 + *-----------------*/
  8.1608 +
  8.1609 +/* Note: These function names use "epoint", "ebox", etc. notation here instead
  8.1610 +   of "point", "box", etc. in order to distinguish them from any previously
  8.1611 +   defined functions. */
  8.1612 +
  8.1613 +/* function needed for dummy types and/or not implemented features */
  8.1614 +PG_FUNCTION_INFO_V1(pgl_notimpl);
  8.1615 +Datum pgl_notimpl(PG_FUNCTION_ARGS) {
  8.1616 +  ereport(ERROR, (errmsg("not implemented by pgLatLon")));
  8.1617 +}
  8.1618 +
  8.1619 +/* set point to latitude and longitude (including checks) */
  8.1620 +static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) {
  8.1621 +  /* reject infinite or NaN values */
  8.1622 +  if (!isfinite(lat) || !isfinite(lon)) {
  8.1623 +    ereport(ERROR, (
  8.1624 +      errcode(ERRCODE_DATA_EXCEPTION),
  8.1625 +      errmsg("epoint requires finite coordinates")
  8.1626 +    ));
  8.1627 +  }
  8.1628 +  /* check latitude bounds */
  8.1629 +  if (lat < -90) {
  8.1630 +    ereport(WARNING, (errmsg("latitude exceeds south pole")));
  8.1631 +    lat = -90;
  8.1632 +  } else if (lat > 90) {
  8.1633 +    ereport(WARNING, (errmsg("latitude exceeds north pole")));
  8.1634 +    lat = 90;
  8.1635 +  }
  8.1636 +  /* check longitude bounds */
  8.1637 +  if (lon < -180) {
  8.1638 +    ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized")));
  8.1639 +    lon += 360 - trunc(lon / 360) * 360;
  8.1640 +  } else if (lon > 180) {
  8.1641 +    ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized")));
  8.1642 +    lon -= 360 + trunc(lon / 360) * 360;
  8.1643 +  }
  8.1644 +  /* store rounded latitude/longitude values for round-trip safety */
  8.1645 +  point->lat = pgl_round(lat);
  8.1646 +  point->lon = pgl_round(lon);
  8.1647 +}
  8.1648 +
  8.1649 +/* create point ("epoint" in SQL) from latitude and longitude */
  8.1650 +PG_FUNCTION_INFO_V1(pgl_create_epoint);
  8.1651 +Datum pgl_create_epoint(PG_FUNCTION_ARGS) {
  8.1652 +  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  8.1653 +  pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1));
  8.1654 +  PG_RETURN_POINTER(point);
  8.1655 +}
  8.1656 +
  8.1657 +/* parse point ("epoint" in SQL) */
  8.1658 +/* format: '[NS]<float> [EW]<float>' */
  8.1659 +PG_FUNCTION_INFO_V1(pgl_epoint_in);
  8.1660 +Datum pgl_epoint_in(PG_FUNCTION_ARGS) {
  8.1661 +  char *str = PG_GETARG_CSTRING(0);  /* input string */
  8.1662 +  char *strptr = str;  /* current position within string */
  8.1663 +  int done = 0;        /* bit mask storing if latitude or longitude was read */
  8.1664 +  double lat, lon;     /* parsed values as double precision floats */
  8.1665 +  pgl_point *point;    /* return value (to be palloc'ed) */
  8.1666 +  /* parse two floats (each latitude or longitude) separated by white-space */
  8.1667 +  done |= pgl_scan(&strptr, &lat, &lon);
  8.1668 +  if (strptr != str && isspace(strptr[-1])) {
  8.1669 +    done |= pgl_scan(&strptr, &lat, &lon);
  8.1670 +  }
  8.1671 +  /* require end of string, and latitude and longitude parsed successfully */
  8.1672 +  if (strptr[0] || done != PGL_SCAN_LATLON) {
  8.1673 +    ereport(ERROR, (
  8.1674 +      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  8.1675 +      errmsg("invalid input syntax for type epoint: \"%s\"", str)
  8.1676 +    ));
  8.1677 +  }
  8.1678 +  /* allocate memory for result */
  8.1679 +  point = (pgl_point *)palloc(sizeof(pgl_point));
  8.1680 +  /* set latitude and longitude (and perform checks) */
  8.1681 +  pgl_epoint_set_latlon(point, lat, lon);
  8.1682 +  /* return result */
  8.1683 +  PG_RETURN_POINTER(point);
  8.1684 +}
  8.1685 +
  8.1686 +/* set sample count for numerical integration (including checks) */
  8.1687 +static void pgl_epoint_set_sample_count(pgl_point_sc *search, int32 samples) {
  8.1688 +  /* require minimum of 6 samples */
  8.1689 +  if (samples < 6) {
  8.1690 +    ereport(ERROR, (
  8.1691 +      errcode(ERRCODE_DATA_EXCEPTION),
  8.1692 +      errmsg("too few sample points for numerical integration (minimum 6)")
  8.1693 +    ));
  8.1694 +  }
  8.1695 +  /* limit sample count to avoid integer overflows on memory allocation */
  8.1696 +  if (samples > PGL_CLUSTER_MAXPOINTS) {
  8.1697 +    ereport(ERROR, (
  8.1698 +      errcode(ERRCODE_DATA_EXCEPTION),
  8.1699 +      errmsg(
  8.1700 +        "too many sample points for numerical integration (maximum %i)",
  8.1701 +        PGL_CLUSTER_MAXPOINTS
  8.1702 +      )
  8.1703 +    ));
  8.1704 +  }
  8.1705 +  search->samples = samples;
  8.1706 +}
  8.1707 +
  8.1708 +/* create point with sample count for fair distance calculation
  8.1709 +   ("epoint_with_sample_count" in SQL) from epoint and integer */
  8.1710 +PG_FUNCTION_INFO_V1(pgl_create_epoint_with_sample_count);
  8.1711 +Datum pgl_create_epoint_with_sample_count(PG_FUNCTION_ARGS) {
  8.1712 +  pgl_point_sc *search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc));
  8.1713 +  search->point = *(pgl_point *)PG_GETARG_POINTER(0);
  8.1714 +  pgl_epoint_set_sample_count(search, PG_GETARG_INT32(1));
  8.1715 +  PG_RETURN_POINTER(search);
  8.1716 +}
  8.1717 +
  8.1718 +/* parse point with sample count ("epoint_with_sample_count" in SQL) */
  8.1719 +/* format: '[NS]<float> [EW]<float> <integer>' */
  8.1720 +PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_in);
  8.1721 +Datum pgl_epoint_with_sample_count_in(PG_FUNCTION_ARGS) {
  8.1722 +  char *str = PG_GETARG_CSTRING(0);  /* input string */
  8.1723 +  char *strptr = str;    /* current position within string */
  8.1724 +  double lat, lon;       /* parsed values for latitude and longitude */
  8.1725 +  int samples;           /* parsed value for sample count */
  8.1726 +  int valid = 0;         /* number of valid chars */
  8.1727 +  int done = 0;          /* stores if latitude and/or longitude was read */
  8.1728 +  pgl_point_sc *search;  /* return value (to be palloc'ed) */
  8.1729 +  /* demand three blocks separated by whitespace */
  8.1730 +  sscanf(strptr, " %*s %*s %*s %n", &valid);
  8.1731 +  /* if three blocks separated by whitespace exist, parse those blocks */
  8.1732 +  if (strptr[valid] == 0) {
  8.1733 +    /* parse latitude and longitude */
  8.1734 +    done |= pgl_scan(&strptr, &lat, &lon);
  8.1735 +    done |= pgl_scan(&strptr, &lat, &lon);
  8.1736 +    /* parse sample count (while incr. strptr by number of bytes parsed) */
  8.1737 +    valid = 0;
  8.1738 +    if (sscanf(strptr, " %d %n", &samples, &valid) == 1) strptr += valid;
  8.1739 +  }
  8.1740 +  /* require end of string and both latitude and longitude being parsed */
  8.1741 +  if (strptr[0] || done != PGL_SCAN_LATLON) {
  8.1742 +    ereport(ERROR, (
  8.1743 +      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  8.1744 +      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  8.1745 +    ));
  8.1746 +  }
  8.1747 +  /* allocate memory for result */
  8.1748 +  search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc));
  8.1749 +  /* set latitude, longitude, and sample count (while performing checks) */
  8.1750 +  pgl_epoint_set_latlon(&search->point, lat, lon);
  8.1751 +  pgl_epoint_set_sample_count(search, samples);
  8.1752 +  /* return result */
  8.1753 +  PG_RETURN_POINTER(search);
  8.1754 +}
  8.1755 +
  8.1756 +/* create box ("ebox" in SQL) that is empty */
  8.1757 +PG_FUNCTION_INFO_V1(pgl_create_empty_ebox);
  8.1758 +Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) {
  8.1759 +  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  8.1760 +  pgl_box_set_empty(box);
  8.1761 +  PG_RETURN_POINTER(box);
  8.1762 +}
  8.1763 +
  8.1764 +/* set box to given boundaries (including checks) */
  8.1765 +static void pgl_ebox_set_boundaries(
  8.1766 +  pgl_box *box,
  8.1767 +  double lat_min, double lat_max, double lon_min, double lon_max
  8.1768 +) {
  8.1769 +  /* if minimum latitude is greater than maximum latitude, return empty box */
  8.1770 +  if (lat_min > lat_max) {
  8.1771 +    pgl_box_set_empty(box);
  8.1772 +    return;
  8.1773 +  }
  8.1774 +  /* otherwise reject infinite or NaN values */
  8.1775 +  if (
  8.1776 +    !isfinite(lat_min) || !isfinite(lat_max) ||
  8.1777 +    !isfinite(lon_min) || !isfinite(lon_max)
  8.1778 +  ) {
  8.1779 +    ereport(ERROR, (
  8.1780 +      errcode(ERRCODE_DATA_EXCEPTION),
  8.1781 +      errmsg("ebox requires finite coordinates")
  8.1782 +    ));
  8.1783 +  }
  8.1784 +  /* check latitude bounds */
  8.1785 +  if (lat_max < -90) {
  8.1786 +    ereport(WARNING, (errmsg("northern latitude exceeds south pole")));
  8.1787 +    lat_max = -90;
  8.1788 +  } else if (lat_max > 90) {
  8.1789 +    ereport(WARNING, (errmsg("northern latitude exceeds north pole")));
  8.1790 +    lat_max = 90;
  8.1791 +  }
  8.1792 +  if (lat_min < -90) {
  8.1793 +    ereport(WARNING, (errmsg("southern latitude exceeds south pole")));
  8.1794 +    lat_min = -90;
  8.1795 +  } else if (lat_min > 90) {
  8.1796 +    ereport(WARNING, (errmsg("southern latitude exceeds north pole")));
  8.1797 +    lat_min = 90;
  8.1798 +  }
  8.1799 +  /* check if all longitudes are included */
  8.1800 +  if (lon_max - lon_min >= 360) {
  8.1801 +    if (lon_max - lon_min > 360) ereport(WARNING, (
  8.1802 +      errmsg("longitude coverage greater than 360 degrees")
  8.1803 +    ));
  8.1804 +    lon_min = -180;
  8.1805 +    lon_max = 180;
  8.1806 +  } else {
  8.1807 +    /* normalize longitude bounds */
  8.1808 +    if      (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360;
  8.1809 +    else if (lon_min >  180) lon_min -= 360 + trunc(lon_min / 360) * 360;
  8.1810 +    if      (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360;
  8.1811 +    else if (lon_max >  180) lon_max -= 360 + trunc(lon_max / 360) * 360;
  8.1812 +  }
  8.1813 +  /* store rounded latitude/longitude values for round-trip safety */
  8.1814 +  box->lat_min = pgl_round(lat_min);
  8.1815 +  box->lat_max = pgl_round(lat_max);
  8.1816 +  box->lon_min = pgl_round(lon_min);
  8.1817 +  box->lon_max = pgl_round(lon_max);
  8.1818 +  /* ensure that rounding does not change orientation */
  8.1819 +  if (lon_min > lon_max && box->lon_min == box->lon_max) {
  8.1820 +    box->lon_min = -180;
  8.1821 +    box->lon_max = 180;
  8.1822 +  }
  8.1823 +}
  8.1824 +
  8.1825 +/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */
  8.1826 +PG_FUNCTION_INFO_V1(pgl_create_ebox);
  8.1827 +Datum pgl_create_ebox(PG_FUNCTION_ARGS) {
  8.1828 +  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  8.1829 +  pgl_ebox_set_boundaries(
  8.1830 +    box,
  8.1831 +    PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1),
  8.1832 +    PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3)
  8.1833 +  );
  8.1834 +  PG_RETURN_POINTER(box);
  8.1835 +}
  8.1836 +
  8.1837 +/* create box ("ebox" in SQL) from two points ("epoint"s) */
  8.1838 +/* (can not be used to cover a longitude range of more than 120 degrees) */
  8.1839 +PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints);
  8.1840 +Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) {
  8.1841 +  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  8.1842 +  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  8.1843 +  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  8.1844 +  double lat_min, lat_max, lon_min, lon_max;
  8.1845 +  double dlon;  /* longitude range (delta longitude) */
  8.1846 +  /* order latitude and longitude boundaries */
  8.1847 +  if (point2->lat < point1->lat) {
  8.1848 +    lat_min = point2->lat;
  8.1849 +    lat_max = point1->lat;
  8.1850 +  } else {
  8.1851 +    lat_min = point1->lat;
  8.1852 +    lat_max = point2->lat;
  8.1853 +  }
  8.1854 +  if (point2->lon < point1->lon) {
  8.1855 +    lon_min = point2->lon;
  8.1856 +    lon_max = point1->lon;
  8.1857 +  } else {
  8.1858 +    lon_min = point1->lon;
  8.1859 +    lon_max = point2->lon;
  8.1860 +  }
  8.1861 +  /* calculate longitude range (round to avoid floating point errors) */
  8.1862 +  dlon = pgl_round(lon_max - lon_min);
  8.1863 +  /* determine east-west direction */
  8.1864 +  if (dlon >= 240) {
  8.1865 +    /* assume that 180th meridian is crossed and swap min/max longitude */
  8.1866 +    double swap = lon_min; lon_min = lon_max; lon_max = swap;
  8.1867 +  } else if (dlon > 120) {
  8.1868 +    /* unclear orientation since delta longitude > 120 */
  8.1869 +    ereport(ERROR, (
  8.1870 +      errcode(ERRCODE_DATA_EXCEPTION),
  8.1871 +      errmsg("can not determine east/west orientation for ebox")
  8.1872 +    ));
  8.1873 +  }
  8.1874 +  /* use boundaries to setup box (and perform checks) */
  8.1875 +  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  8.1876 +  /* return result */
  8.1877 +  PG_RETURN_POINTER(box);
  8.1878 +}
  8.1879 +
  8.1880 +/* parse box ("ebox" in SQL) */
  8.1881 +/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>'
  8.1882 +       or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */
  8.1883 +PG_FUNCTION_INFO_V1(pgl_ebox_in);
  8.1884 +Datum pgl_ebox_in(PG_FUNCTION_ARGS) {
  8.1885 +  char *str = PG_GETARG_CSTRING(0);  /* input string */
  8.1886 +  char *str_lower;     /* lower case version of input string */
  8.1887 +  char *strptr;        /* current position within string */
  8.1888 +  int valid;           /* number of valid chars */
  8.1889 +  int done;            /* specifies if latitude or longitude was read */
  8.1890 +  double val;          /* temporary variable */
  8.1891 +  int lat_count = 0;   /* count of latitude values parsed */
  8.1892 +  int lon_count = 0;   /* count of longitufde values parsed */
  8.1893 +  double lat_min, lat_max, lon_min, lon_max;  /* see pgl_box struct */
  8.1894 +  pgl_box *box;        /* return value (to be palloc'ed) */
  8.1895 +  /* lowercase input */
  8.1896 +  str_lower = psprintf("%s", str);
  8.1897 +  for (strptr=str_lower; *strptr; strptr++) {
  8.1898 +    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  8.1899 +  }
  8.1900 +  /* reset reading position to start of (lowercase) string */
  8.1901 +  strptr = str_lower;
  8.1902 +  /* check if empty box */
  8.1903 +  valid = 0;
  8.1904 +  sscanf(strptr, " empty %n", &valid);
  8.1905 +  if (valid && strptr[valid] == 0) {
  8.1906 +    /* allocate and return empty box */
  8.1907 +    box = (pgl_box *)palloc(sizeof(pgl_box));
  8.1908 +    pgl_box_set_empty(box);
  8.1909 +    PG_RETURN_POINTER(box);
  8.1910 +  }
  8.1911 +  /* demand four blocks separated by whitespace */
  8.1912 +  valid = 0;
  8.1913 +  sscanf(strptr, " %*s %*s %*s %*s %n", &valid);
  8.1914 +  /* if four blocks separated by whitespace exist, parse those blocks */
  8.1915 +  if (strptr[valid] == 0) while (strptr[0]) {
  8.1916 +    /* parse either latitude or longitude (whichever found in input string) */
  8.1917 +    done = pgl_scan(&strptr, &val, &val);
  8.1918 +    /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */
  8.1919 +    if (done == PGL_SCAN_LAT) {
  8.1920 +      if (!lat_count) lat_min = val; else lat_max = val;
  8.1921 +      lat_count++;
  8.1922 +    } else if (done == PGL_SCAN_LON) {
  8.1923 +      if (!lon_count) lon_min = val; else lon_max = val;
  8.1924 +      lon_count++;
  8.1925 +    } else {
  8.1926 +      break;
  8.1927 +    }
  8.1928 +  }
  8.1929 +  /* require end of string, and two latitude and two longitude values */
  8.1930 +  if (strptr[0] || lat_count != 2 || lon_count != 2) {
  8.1931 +    ereport(ERROR, (
  8.1932 +      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  8.1933 +      errmsg("invalid input syntax for type ebox: \"%s\"", str)
  8.1934 +    ));
  8.1935 +  }
  8.1936 +  /* free lower case string */
  8.1937 +  pfree(str_lower);
  8.1938 +  /* order boundaries (maximum greater than minimum) */
  8.1939 +  if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; }
  8.1940 +  if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; }
  8.1941 +  /* allocate memory for result */
  8.1942 +  box = (pgl_box *)palloc(sizeof(pgl_box));
  8.1943 +  /* set boundaries (and perform checks) */
  8.1944 +  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  8.1945 +  /* return result */
  8.1946 +  PG_RETURN_POINTER(box);
  8.1947 +}
  8.1948 +
  8.1949 +/* set circle to given latitude, longitude, and radius (including checks) */
  8.1950 +static void pgl_ecircle_set_latlon_radius(
  8.1951 +  pgl_circle *circle, double lat, double lon, double radius
  8.1952 +) {
  8.1953 +  /* set center point (including checks) */
  8.1954 +  pgl_epoint_set_latlon(&(circle->center), lat, lon);
  8.1955 +  /* handle non-positive radius */
  8.1956 +  if (isnan(radius)) {
  8.1957 +    ereport(ERROR, (
  8.1958 +      errcode(ERRCODE_DATA_EXCEPTION),
  8.1959 +      errmsg("invalid radius for ecircle")
  8.1960 +    ));
  8.1961 +  }
  8.1962 +  if (radius == 0) radius = 0;  /* avoids -0 */
  8.1963 +  else if (radius < 0) {
  8.1964 +    if (isfinite(radius)) {
  8.1965 +      ereport(NOTICE, (errmsg("negative radius converted to minus infinity")));
  8.1966 +    }
  8.1967 +    radius = -INFINITY;
  8.1968 +  }
  8.1969 +  /* store radius (round-trip safety is ensured by pgl_print_float) */
  8.1970 +  circle->radius = radius;
  8.1971 +}
  8.1972 +
  8.1973 +/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */
  8.1974 +PG_FUNCTION_INFO_V1(pgl_create_ecircle);
  8.1975 +Datum pgl_create_ecircle(PG_FUNCTION_ARGS) {
  8.1976 +  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  8.1977 +  pgl_ecircle_set_latlon_radius(
  8.1978 +    circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2)
  8.1979 +  );
  8.1980 +  PG_RETURN_POINTER(circle);
  8.1981 +}
  8.1982 +
  8.1983 +/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */
  8.1984 +PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint);
  8.1985 +Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) {
  8.1986 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.1987 +  double radius = PG_GETARG_FLOAT8(1);
  8.1988 +  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  8.1989 +  /* set latitude, longitude, radius (and perform checks) */
  8.1990 +  pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius);
  8.1991 +  /* return result */
  8.1992 +  PG_RETURN_POINTER(circle);
  8.1993 +}
  8.1994 +
  8.1995 +/* parse circle ("ecircle" in SQL) */
  8.1996 +/* format: '[NS]<float> [EW]<float> <float>' */
  8.1997 +PG_FUNCTION_INFO_V1(pgl_ecircle_in);
  8.1998 +Datum pgl_ecircle_in(PG_FUNCTION_ARGS) {
  8.1999 +  char *str = PG_GETARG_CSTRING(0);  /* input string */
  8.2000 +  char *strptr = str;       /* current position within string */
  8.2001 +  double lat, lon, radius;  /* parsed values as double precision floats */
  8.2002 +  int valid = 0;            /* number of valid chars */
  8.2003 +  int done = 0;             /* stores if latitude and/or longitude was read */
  8.2004 +  pgl_circle *circle;       /* return value (to be palloc'ed) */
  8.2005 +  /* demand three blocks separated by whitespace */
  8.2006 +  sscanf(strptr, " %*s %*s %*s %n", &valid);
  8.2007 +  /* if three blocks separated by whitespace exist, parse those blocks */
  8.2008 +  if (strptr[valid] == 0) {
  8.2009 +    /* parse latitude and longitude */
  8.2010 +    done |= pgl_scan(&strptr, &lat, &lon);
  8.2011 +    done |= pgl_scan(&strptr, &lat, &lon);
  8.2012 +    /* parse radius (while incrementing strptr by number of bytes parsed) */
  8.2013 +    valid = 0;
  8.2014 +    if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid;
  8.2015 +  }
  8.2016 +  /* require end of string and both latitude and longitude being parsed */
  8.2017 +  if (strptr[0] || done != PGL_SCAN_LATLON) {
  8.2018 +    ereport(ERROR, (
  8.2019 +      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  8.2020 +      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  8.2021 +    ));
  8.2022 +  }
  8.2023 +  /* allocate memory for result */
  8.2024 +  circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  8.2025 +  /* set latitude, longitude, radius (and perform checks) */
  8.2026 +  pgl_ecircle_set_latlon_radius(circle, lat, lon, radius);
  8.2027 +  /* return result */
  8.2028 +  PG_RETURN_POINTER(circle);
  8.2029 +}
  8.2030 +
  8.2031 +/* parse cluster ("ecluster" in SQL) */
  8.2032 +PG_FUNCTION_INFO_V1(pgl_ecluster_in);
  8.2033 +Datum pgl_ecluster_in(PG_FUNCTION_ARGS) {
  8.2034 +  int i;
  8.2035 +  char *str = PG_GETARG_CSTRING(0);  /* input string */
  8.2036 +  char *str_lower;         /* lower case version of input string */
  8.2037 +  char *strptr;            /* pointer to current reading position of input */
  8.2038 +  int npoints_total = 0;   /* total number of points in cluster */
  8.2039 +  int nentries = 0;        /* total number of entries */
  8.2040 +  pgl_newentry *entries;   /* array of pgl_newentry to create pgl_cluster */
  8.2041 +  int entries_buflen = 4;  /* maximum number of elements in entries array */
  8.2042 +  int valid;               /* number of valid chars processed */
  8.2043 +  double lat, lon;         /* latitude and longitude of parsed point */
  8.2044 +  int entrytype;           /* current entry type */
  8.2045 +  int npoints;             /* number of points in current entry */
  8.2046 +  pgl_point *points;       /* array of pgl_point for pgl_newentry */
  8.2047 +  int points_buflen;       /* maximum number of elements in points array */
  8.2048 +  int done;                /* return value of pgl_scan function */
  8.2049 +  pgl_cluster *cluster;    /* created cluster */
  8.2050 +  /* lowercase input */
  8.2051 +  str_lower = psprintf("%s", str);
  8.2052 +  for (strptr=str_lower; *strptr; strptr++) {
  8.2053 +    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  8.2054 +  }
  8.2055 +  /* reset reading position to start of (lowercase) string */
  8.2056 +  strptr = str_lower;
  8.2057 +  /* allocate initial buffer for entries */
  8.2058 +  entries = palloc(entries_buflen * sizeof(pgl_newentry));
  8.2059 +  /* parse until end of string */
  8.2060 +  while (strptr[0]) {
  8.2061 +    /* require previous white-space or closing parenthesis before next token */
  8.2062 +    if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') {
  8.2063 +      goto pgl_ecluster_in_error;
  8.2064 +    }
  8.2065 +    /* ignore token "empty" */
  8.2066 +    valid = 0; sscanf(strptr, " empty %n", &valid);
  8.2067 +    if (valid) { strptr += valid; continue; }
  8.2068 +    /* test for "point" token */
  8.2069 +    valid = 0; sscanf(strptr, " point ( %n", &valid);
  8.2070 +    if (valid) {
  8.2071 +      strptr += valid;
  8.2072 +      entrytype = PGL_ENTRY_POINT;
  8.2073 +      goto pgl_ecluster_in_type_ok;
  8.2074 +    }
  8.2075 +    /* test for "path" token */
  8.2076 +    valid = 0; sscanf(strptr, " path ( %n", &valid);
  8.2077 +    if (valid) {
  8.2078 +      strptr += valid;
  8.2079 +      entrytype = PGL_ENTRY_PATH;
  8.2080 +      goto pgl_ecluster_in_type_ok;
  8.2081 +    }
  8.2082 +    /* test for "outline" token */
  8.2083 +    valid = 0; sscanf(strptr, " outline ( %n", &valid);
  8.2084 +    if (valid) {
  8.2085 +      strptr += valid;
  8.2086 +      entrytype = PGL_ENTRY_OUTLINE;
  8.2087 +      goto pgl_ecluster_in_type_ok;
  8.2088 +    }
  8.2089 +    /* test for "polygon" token */
  8.2090 +    valid = 0; sscanf(strptr, " polygon ( %n", &valid);
  8.2091 +    if (valid) {
  8.2092 +      strptr += valid;
  8.2093 +      entrytype = PGL_ENTRY_POLYGON;
  8.2094 +      goto pgl_ecluster_in_type_ok;
  8.2095 +    }
  8.2096 +    /* error if no valid token found */
  8.2097 +    goto pgl_ecluster_in_error;
  8.2098 +    pgl_ecluster_in_type_ok:
  8.2099 +    /* check if pgl_newentry array needs to grow */
  8.2100 +    if (nentries == entries_buflen) {
  8.2101 +      pgl_newentry *newbuf;
  8.2102 +      entries_buflen *= 2;
  8.2103 +      newbuf = palloc(entries_buflen * sizeof(pgl_newentry));
  8.2104 +      memcpy(newbuf, entries, nentries * sizeof(pgl_newentry));
  8.2105 +      pfree(entries);
  8.2106 +      entries = newbuf;
  8.2107 +    }
  8.2108 +    /* reset number of points for current entry */
  8.2109 +    npoints = 0;
  8.2110 +    /* allocate array for points */
  8.2111 +    points_buflen = 4;
  8.2112 +    points = palloc(points_buflen * sizeof(pgl_point));
  8.2113 +    /* parse until closing parenthesis */
  8.2114 +    while (strptr[0] != ')') {
  8.2115 +      /* error on unexpected end of string */
  8.2116 +      if (strptr[0] == 0) goto pgl_ecluster_in_error;
  8.2117 +      /* mark neither latitude nor longitude as read */
  8.2118 +      done = PGL_SCAN_NONE;
  8.2119 +      /* require white-space before second, third, etc. point */
  8.2120 +      if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  8.2121 +      /* scan latitude (or longitude) */
  8.2122 +      done |= pgl_scan(&strptr, &lat, &lon);
  8.2123 +      /* require white-space before second coordinate */
  8.2124 +      if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  8.2125 +      /* scan longitude (or latitude) */
  8.2126 +      done |= pgl_scan(&strptr, &lat, &lon);
  8.2127 +      /* error unless both latitude and longitude were parsed */
  8.2128 +      if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error;
  8.2129 +      /* throw error if number of points is too high */
  8.2130 +      if (npoints_total == PGL_CLUSTER_MAXPOINTS) {
  8.2131 +        ereport(ERROR, (
  8.2132 +          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  8.2133 +          errmsg(
  8.2134 +            "too many points for ecluster entry (maximum %i)",
  8.2135 +            PGL_CLUSTER_MAXPOINTS
  8.2136 +          )
  8.2137 +        ));
  8.2138 +      }
  8.2139 +      /* check if pgl_point array needs to grow */
  8.2140 +      if (npoints == points_buflen) {
  8.2141 +        pgl_point *newbuf;
  8.2142 +        points_buflen *= 2;
  8.2143 +        newbuf = palloc(points_buflen * sizeof(pgl_point));
  8.2144 +        memcpy(newbuf, points, npoints * sizeof(pgl_point));
  8.2145 +        pfree(points);
  8.2146 +        points = newbuf;
  8.2147 +      }
  8.2148 +      /* append point to pgl_point array (includes checks) */
  8.2149 +      pgl_epoint_set_latlon(&(points[npoints++]), lat, lon);
  8.2150 +      /* increase total number of points */
  8.2151 +      npoints_total++;
  8.2152 +    }
  8.2153 +    /* error if entry has no points */
  8.2154 +    if (!npoints) goto pgl_ecluster_in_error;
  8.2155 +    /* entries with one point are automatically of type "point" */
  8.2156 +    if (npoints == 1) entrytype = PGL_ENTRY_POINT;
  8.2157 +    /* if entries have more than one point */
  8.2158 +    else {
  8.2159 +      /* throw error if entry type is "point" */
  8.2160 +      if (entrytype == PGL_ENTRY_POINT) {
  8.2161 +        ereport(ERROR, (
  8.2162 +          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  8.2163 +          errmsg("invalid input syntax for type ecluster (point entry with more than one point)")
  8.2164 +        ));
  8.2165 +      }
  8.2166 +      /* coerce outlines and polygons with more than 2 points to be a path */
  8.2167 +      if (npoints == 2) entrytype = PGL_ENTRY_PATH;
  8.2168 +    }
  8.2169 +    /* append entry to pgl_newentry array */
  8.2170 +    entries[nentries].entrytype = entrytype;
  8.2171 +    entries[nentries].npoints = npoints;
  8.2172 +    entries[nentries].points = points;
  8.2173 +    nentries++;
  8.2174 +    /* consume closing parenthesis */
  8.2175 +    strptr++;
  8.2176 +    /* consume white-space */
  8.2177 +    while (isspace(strptr[0])) strptr++;
  8.2178 +  }
  8.2179 +  /* free lower case string */
  8.2180 +  pfree(str_lower);
  8.2181 +  /* create cluster from pgl_newentry array */
  8.2182 +  cluster = pgl_new_cluster(nentries, entries);
  8.2183 +  /* free pgl_newentry array */
  8.2184 +  for (i=0; i<nentries; i++) pfree(entries[i].points);
  8.2185 +  pfree(entries);
  8.2186 +  /* set bounding circle of cluster and check east/west orientation */
  8.2187 +  if (!pgl_finalize_cluster(cluster)) {
  8.2188 +    ereport(ERROR, (
  8.2189 +      errcode(ERRCODE_DATA_EXCEPTION),
  8.2190 +      errmsg("can not determine east/west orientation for ecluster"),
  8.2191 +      errhint("Ensure that each entry has a longitude span of less than 180 degrees.")
  8.2192 +    ));
  8.2193 +  }
  8.2194 +  /* return cluster */
  8.2195 +  PG_RETURN_POINTER(cluster);
  8.2196 +  /* code to throw error */
  8.2197 +  pgl_ecluster_in_error:
  8.2198 +  ereport(ERROR, (
  8.2199 +    errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  8.2200 +    errmsg("invalid input syntax for type ecluster: \"%s\"", str)
  8.2201 +  ));
  8.2202 +}
  8.2203 +
  8.2204 +/* convert point ("epoint") to string representation */
  8.2205 +PG_FUNCTION_INFO_V1(pgl_epoint_out);
  8.2206 +Datum pgl_epoint_out(PG_FUNCTION_ARGS) {
  8.2207 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2208 +  char latstr[PGL_NUMBUFLEN];
  8.2209 +  char lonstr[PGL_NUMBUFLEN];
  8.2210 +  pgl_print_lat(latstr, point->lat);
  8.2211 +  pgl_print_lon(lonstr, point->lon);
  8.2212 +  PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr));
  8.2213 +}
  8.2214 +
  8.2215 +/* convert point with sample count ("epoint_with_sample_count") to str. rep. */
  8.2216 +PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_out);
  8.2217 +Datum pgl_epoint_with_sample_count_out(PG_FUNCTION_ARGS) {
  8.2218 +  pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(0);
  8.2219 +  char latstr[PGL_NUMBUFLEN];
  8.2220 +  char lonstr[PGL_NUMBUFLEN];
  8.2221 +  pgl_print_lat(latstr, search->point.lat);
  8.2222 +  pgl_print_lon(lonstr, search->point.lon);
  8.2223 +  PG_RETURN_CSTRING(
  8.2224 +    psprintf("%s %s %i", latstr, lonstr, (int)search->samples)
  8.2225 +  );
  8.2226 +}
  8.2227 +
  8.2228 +/* convert box ("ebox") to string representation */
  8.2229 +PG_FUNCTION_INFO_V1(pgl_ebox_out);
  8.2230 +Datum pgl_ebox_out(PG_FUNCTION_ARGS) {
  8.2231 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  8.2232 +  double lon_min = box->lon_min;
  8.2233 +  double lon_max = box->lon_max;
  8.2234 +  char lat_min_str[PGL_NUMBUFLEN];
  8.2235 +  char lat_max_str[PGL_NUMBUFLEN];
  8.2236 +  char lon_min_str[PGL_NUMBUFLEN];
  8.2237 +  char lon_max_str[PGL_NUMBUFLEN];
  8.2238 +  /* return string "empty" if box is set to be empty */
  8.2239 +  if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty");
  8.2240 +  /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */
  8.2241 +  /* (required since pgl_box_in orders the longitude boundaries) */
  8.2242 +  if (lon_min > lon_max) {
  8.2243 +    if (lon_min + lon_max >= 0) lon_min -= 360;
  8.2244 +    else lon_max += 360;
  8.2245 +  }
  8.2246 +  /* format and return result */
  8.2247 +  pgl_print_lat(lat_min_str, box->lat_min);
  8.2248 +  pgl_print_lat(lat_max_str, box->lat_max);
  8.2249 +  pgl_print_lon(lon_min_str, lon_min);
  8.2250 +  pgl_print_lon(lon_max_str, lon_max);
  8.2251 +  PG_RETURN_CSTRING(psprintf(
  8.2252 +    "%s %s %s %s",
  8.2253 +    lat_min_str, lon_min_str, lat_max_str, lon_max_str
  8.2254 +  ));
  8.2255 +}
  8.2256 +
  8.2257 +/* convert circle ("ecircle") to string representation */
  8.2258 +PG_FUNCTION_INFO_V1(pgl_ecircle_out);
  8.2259 +Datum pgl_ecircle_out(PG_FUNCTION_ARGS) {
  8.2260 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  8.2261 +  char latstr[PGL_NUMBUFLEN];
  8.2262 +  char lonstr[PGL_NUMBUFLEN];
  8.2263 +  char radstr[PGL_NUMBUFLEN];
  8.2264 +  pgl_print_lat(latstr, circle->center.lat);
  8.2265 +  pgl_print_lon(lonstr, circle->center.lon);
  8.2266 +  pgl_print_float(radstr, circle->radius);
  8.2267 +  PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr));
  8.2268 +}
  8.2269 +
  8.2270 +/* convert cluster ("ecluster") to string representation */
  8.2271 +PG_FUNCTION_INFO_V1(pgl_ecluster_out);
  8.2272 +Datum pgl_ecluster_out(PG_FUNCTION_ARGS) {
  8.2273 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  8.2274 +  char latstr[PGL_NUMBUFLEN];  /* string buffer for latitude */
  8.2275 +  char lonstr[PGL_NUMBUFLEN];  /* string buffer for longitude */
  8.2276 +  char ***strings;     /* array of array of strings */
  8.2277 +  char *string;        /* string of current token */
  8.2278 +  char *res, *resptr;  /* result and pointer to current write position */
  8.2279 +  size_t reslen = 1;   /* length of result (init with 1 for terminator) */
  8.2280 +  int npoints;         /* number of points of current entry */
  8.2281 +  int i, j;            /* i: entry, j: point in entry */
  8.2282 +  /* handle empty clusters */
  8.2283 +  if (cluster->nentries == 0) {
  8.2284 +    /* free detoasted cluster (if copy) */
  8.2285 +    PG_FREE_IF_COPY(cluster, 0);
  8.2286 +    /* return static result */
  8.2287 +    PG_RETURN_CSTRING("empty");
  8.2288 +  }
  8.2289 +  /* allocate array of array of strings */
  8.2290 +  strings = palloc(cluster->nentries * sizeof(char **));
  8.2291 +  /* iterate over all entries in cluster */
  8.2292 +  for (i=0; i<cluster->nentries; i++) {
  8.2293 +    /* get number of points in entry */
  8.2294 +    npoints = cluster->entries[i].npoints;
  8.2295 +    /* allocate array of strings (one string for each point plus two extra) */
  8.2296 +    strings[i] = palloc((2 + npoints) * sizeof(char *));
  8.2297 +    /* determine opening string */
  8.2298 +    switch (cluster->entries[i].entrytype) {
  8.2299 +      case PGL_ENTRY_POINT:   string = (i==0)?"point ("  :" point (";   break;
  8.2300 +      case PGL_ENTRY_PATH:    string = (i==0)?"path ("   :" path (";    break;
  8.2301 +      case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break;
  8.2302 +      case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break;
  8.2303 +      default:                string = (i==0)?"unknown"  :" unknown";
  8.2304 +    }
  8.2305 +    /* use opening string as first string in array */
  8.2306 +    strings[i][0] = string;
  8.2307 +    /* update result length (for allocating result string later) */
  8.2308 +    reslen += strlen(string);
  8.2309 +    /* iterate over all points */
  8.2310 +    for (j=0; j<npoints; j++) {
  8.2311 +      /* create string representation of point */
  8.2312 +      pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat);
  8.2313 +      pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon);
  8.2314 +      string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr);
  8.2315 +      /* copy string pointer to string array */
  8.2316 +      strings[i][j+1] = string;
  8.2317 +      /* update result length (for allocating result string later) */
  8.2318 +      reslen += strlen(string);
  8.2319 +    }
  8.2320 +    /* use closing parenthesis as last string in array */
  8.2321 +    strings[i][npoints+1] = ")";
  8.2322 +    /* update result length (for allocating result string later) */
  8.2323 +    reslen++;
  8.2324 +  }
  8.2325 +  /* allocate result string */
  8.2326 +  res = palloc(reslen);
  8.2327 +  /* set write pointer to begin of result string */
  8.2328 +  resptr = res;
  8.2329 +  /* copy strings into result string */
  8.2330 +  for (i=0; i<cluster->nentries; i++) {
  8.2331 +    npoints = cluster->entries[i].npoints;
  8.2332 +    for (j=0; j<npoints+2; j++) {
  8.2333 +      string = strings[i][j];
  8.2334 +      strcpy(resptr, string);
  8.2335 +      resptr += strlen(string);
  8.2336 +      /* free strings allocated by psprintf */
  8.2337 +      if (j != 0 && j != npoints+1) pfree(string);
  8.2338 +    }
  8.2339 +    /* free array of strings */
  8.2340 +    pfree(strings[i]);
  8.2341 +  }
  8.2342 +  /* free array of array of strings */
  8.2343 +  pfree(strings);
  8.2344 +  /* free detoasted cluster (if copy) */
  8.2345 +  PG_FREE_IF_COPY(cluster, 0);
  8.2346 +  /* return result */
  8.2347 +  PG_RETURN_CSTRING(res);
  8.2348 +}
  8.2349 +
  8.2350 +/* binary input function for point ("epoint") */
  8.2351 +PG_FUNCTION_INFO_V1(pgl_epoint_recv);
  8.2352 +Datum pgl_epoint_recv(PG_FUNCTION_ARGS) {
  8.2353 +  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  8.2354 +  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  8.2355 +  point->lat = pq_getmsgfloat8(buf);
  8.2356 +  point->lon = pq_getmsgfloat8(buf);
  8.2357 +  PG_RETURN_POINTER(point);
  8.2358 +}
  8.2359 +
  8.2360 +/* binary input function for box ("ebox") */
  8.2361 +PG_FUNCTION_INFO_V1(pgl_ebox_recv);
  8.2362 +Datum pgl_ebox_recv(PG_FUNCTION_ARGS) {
  8.2363 +  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  8.2364 +  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  8.2365 +  box->lat_min = pq_getmsgfloat8(buf);
  8.2366 +  box->lat_max = pq_getmsgfloat8(buf);
  8.2367 +  box->lon_min = pq_getmsgfloat8(buf);
  8.2368 +  box->lon_max = pq_getmsgfloat8(buf);
  8.2369 +  PG_RETURN_POINTER(box);
  8.2370 +}
  8.2371 +
  8.2372 +/* binary input function for circle ("ecircle") */
  8.2373 +PG_FUNCTION_INFO_V1(pgl_ecircle_recv);
  8.2374 +Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) {
  8.2375 +  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  8.2376 +  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  8.2377 +  circle->center.lat = pq_getmsgfloat8(buf);
  8.2378 +  circle->center.lon = pq_getmsgfloat8(buf);
  8.2379 +  circle->radius = pq_getmsgfloat8(buf);
  8.2380 +  PG_RETURN_POINTER(circle);
  8.2381 +}
  8.2382 +
  8.2383 +/* TODO: binary receive function for cluster */
  8.2384 +
  8.2385 +/* binary output function for point ("epoint") */
  8.2386 +PG_FUNCTION_INFO_V1(pgl_epoint_send);
  8.2387 +Datum pgl_epoint_send(PG_FUNCTION_ARGS) {
  8.2388 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2389 +  StringInfoData buf;
  8.2390 +  pq_begintypsend(&buf);
  8.2391 +  pq_sendfloat8(&buf, point->lat);
  8.2392 +  pq_sendfloat8(&buf, point->lon);
  8.2393 +  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  8.2394 +}
  8.2395 +
  8.2396 +/* binary output function for box ("ebox") */
  8.2397 +PG_FUNCTION_INFO_V1(pgl_ebox_send);
  8.2398 +Datum pgl_ebox_send(PG_FUNCTION_ARGS) {
  8.2399 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  8.2400 +  StringInfoData buf;
  8.2401 +  pq_begintypsend(&buf);
  8.2402 +  pq_sendfloat8(&buf, box->lat_min);
  8.2403 +  pq_sendfloat8(&buf, box->lat_max);
  8.2404 +  pq_sendfloat8(&buf, box->lon_min);
  8.2405 +  pq_sendfloat8(&buf, box->lon_max);
  8.2406 +  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  8.2407 +}
  8.2408 +
  8.2409 +/* binary output function for circle ("ecircle") */
  8.2410 +PG_FUNCTION_INFO_V1(pgl_ecircle_send);
  8.2411 +Datum pgl_ecircle_send(PG_FUNCTION_ARGS) {
  8.2412 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  8.2413 +  StringInfoData buf;
  8.2414 +  pq_begintypsend(&buf);
  8.2415 +  pq_sendfloat8(&buf, circle->center.lat);
  8.2416 +  pq_sendfloat8(&buf, circle->center.lon);
  8.2417 +  pq_sendfloat8(&buf, circle->radius);
  8.2418 +  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  8.2419 +}
  8.2420 +
  8.2421 +/* TODO: binary send functions for cluster */
  8.2422 +
  8.2423 +/* cast point ("epoint") to box ("ebox") */
  8.2424 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox);
  8.2425 +Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) {
  8.2426 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2427 +  pgl_box *box = palloc(sizeof(pgl_box));
  8.2428 +  box->lat_min = point->lat;
  8.2429 +  box->lat_max = point->lat;
  8.2430 +  box->lon_min = point->lon;
  8.2431 +  box->lon_max = point->lon;
  8.2432 +  PG_RETURN_POINTER(box);
  8.2433 +}
  8.2434 +
  8.2435 +/* cast point ("epoint") to circle ("ecircle") */
  8.2436 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle);
  8.2437 +Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) {
  8.2438 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2439 +  pgl_circle *circle = palloc(sizeof(pgl_box));
  8.2440 +  circle->center = *point;
  8.2441 +  circle->radius = 0;
  8.2442 +  PG_RETURN_POINTER(circle);
  8.2443 +}
  8.2444 +
  8.2445 +/* cast point ("epoint") to cluster ("ecluster") */
  8.2446 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster);
  8.2447 +Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) {
  8.2448 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2449 +  pgl_newentry entry;
  8.2450 +  pgl_cluster *cluster;
  8.2451 +  entry.entrytype = PGL_ENTRY_POINT;
  8.2452 +  entry.npoints = 1;
  8.2453 +  entry.points = point;
  8.2454 +  cluster = pgl_new_cluster(1, &entry);
  8.2455 +  pgl_finalize_cluster(cluster);  /* NOTE: should not fail */
  8.2456 +  PG_RETURN_POINTER(cluster);
  8.2457 +}
  8.2458 +
  8.2459 +/* cast box ("ebox") to cluster ("ecluster") */
  8.2460 +#define pgl_ebox_to_ecluster_macro(i, a, b) \
  8.2461 +  entries[i].entrytype = PGL_ENTRY_POLYGON; \
  8.2462 +  entries[i].npoints = 4; \
  8.2463 +  entries[i].points = points[i]; \
  8.2464 +  points[i][0].lat = box->lat_min; \
  8.2465 +  points[i][0].lon = (a); \
  8.2466 +  points[i][1].lat = box->lat_min; \
  8.2467 +  points[i][1].lon = (b); \
  8.2468 +  points[i][2].lat = box->lat_max; \
  8.2469 +  points[i][2].lon = (b); \
  8.2470 +  points[i][3].lat = box->lat_max; \
  8.2471 +  points[i][3].lon = (a);
  8.2472 +PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster);
  8.2473 +Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) {
  8.2474 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  8.2475 +  double lon, dlon;
  8.2476 +  int nentries;
  8.2477 +  pgl_newentry entries[3];
  8.2478 +  pgl_point points[3][4];
  8.2479 +  pgl_cluster *cluster;
  8.2480 +  if (box->lat_min > box->lat_max) {
  8.2481 +    nentries = 0;
  8.2482 +  } else if (box->lon_min > box->lon_max) {
  8.2483 +    if (box->lon_min < 0) {
  8.2484 +      lon = pgl_round((box->lon_min + 180) / 2.0);
  8.2485 +      nentries = 3;
  8.2486 +      pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  8.2487 +      pgl_ebox_to_ecluster_macro(1, lon, 180);
  8.2488 +      pgl_ebox_to_ecluster_macro(2, -180, box->lon_max);
  8.2489 +    } else if (box->lon_max > 0) {
  8.2490 +      lon = pgl_round((box->lon_max - 180) / 2.0);
  8.2491 +      nentries = 3;
  8.2492 +      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  8.2493 +      pgl_ebox_to_ecluster_macro(1, -180, lon);
  8.2494 +      pgl_ebox_to_ecluster_macro(2, lon, box->lon_max);
  8.2495 +    } else {
  8.2496 +      nentries = 2;
  8.2497 +      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  8.2498 +      pgl_ebox_to_ecluster_macro(1, -180, box->lon_max);
  8.2499 +    }
  8.2500 +  } else {
  8.2501 +    dlon = pgl_round(box->lon_max - box->lon_min);
  8.2502 +    if (dlon < 180) {
  8.2503 +      nentries = 1;
  8.2504 +      pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max);
  8.2505 +    } else {
  8.2506 +      lon = pgl_round((box->lon_min + box->lon_max) / 2.0);
  8.2507 +      if (
  8.2508 +        pgl_round(lon - box->lon_min) < 180 &&
  8.2509 +        pgl_round(box->lon_max - lon) < 180
  8.2510 +      ) {
  8.2511 +        nentries = 2;
  8.2512 +        pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  8.2513 +        pgl_ebox_to_ecluster_macro(1, lon, box->lon_max);
  8.2514 +      } else {
  8.2515 +        nentries = 3;
  8.2516 +        pgl_ebox_to_ecluster_macro(0, box->lon_min, -60);
  8.2517 +        pgl_ebox_to_ecluster_macro(1, -60, 60);
  8.2518 +        pgl_ebox_to_ecluster_macro(2, 60, box->lon_max);
  8.2519 +      }
  8.2520 +    }
  8.2521 +  }
  8.2522 +  cluster = pgl_new_cluster(nentries, entries);
  8.2523 +  pgl_finalize_cluster(cluster);  /* NOTE: should not fail */
  8.2524 +  PG_RETURN_POINTER(cluster);
  8.2525 +}
  8.2526 +
  8.2527 +/* extract latitude from point ("epoint") */
  8.2528 +PG_FUNCTION_INFO_V1(pgl_epoint_lat);
  8.2529 +Datum pgl_epoint_lat(PG_FUNCTION_ARGS) {
  8.2530 +  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat);
  8.2531 +}
  8.2532 +
  8.2533 +/* extract longitude from point ("epoint") */
  8.2534 +PG_FUNCTION_INFO_V1(pgl_epoint_lon);
  8.2535 +Datum pgl_epoint_lon(PG_FUNCTION_ARGS) {
  8.2536 +  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon);
  8.2537 +}
  8.2538 +
  8.2539 +/* extract minimum latitude from box ("ebox") */
  8.2540 +PG_FUNCTION_INFO_V1(pgl_ebox_lat_min);
  8.2541 +Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) {
  8.2542 +  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min);
  8.2543 +}
  8.2544 +
  8.2545 +/* extract maximum latitude from box ("ebox") */
  8.2546 +PG_FUNCTION_INFO_V1(pgl_ebox_lat_max);
  8.2547 +Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) {
  8.2548 +  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max);
  8.2549 +}
  8.2550 +
  8.2551 +/* extract minimum longitude from box ("ebox") */
  8.2552 +PG_FUNCTION_INFO_V1(pgl_ebox_lon_min);
  8.2553 +Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) {
  8.2554 +  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min);
  8.2555 +}
  8.2556 +
  8.2557 +/* extract maximum longitude from box ("ebox") */
  8.2558 +PG_FUNCTION_INFO_V1(pgl_ebox_lon_max);
  8.2559 +Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) {
  8.2560 +  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max);
  8.2561 +}
  8.2562 +
  8.2563 +/* extract center point from circle ("ecircle") */
  8.2564 +PG_FUNCTION_INFO_V1(pgl_ecircle_center);
  8.2565 +Datum pgl_ecircle_center(PG_FUNCTION_ARGS) {
  8.2566 +  PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center));
  8.2567 +}
  8.2568 +
  8.2569 +/* extract radius from circle ("ecircle") */
  8.2570 +PG_FUNCTION_INFO_V1(pgl_ecircle_radius);
  8.2571 +Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) {
  8.2572 +  PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius);
  8.2573 +}
  8.2574 +
  8.2575 +/* check if point is inside box (overlap operator "&&") in SQL */
  8.2576 +PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap);
  8.2577 +Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) {
  8.2578 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2579 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1);
  8.2580 +  PG_RETURN_BOOL(pgl_point_in_box(point, box));
  8.2581 +}
  8.2582 +
  8.2583 +/* check if point is inside circle (overlap operator "&&") in SQL */
  8.2584 +PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap);
  8.2585 +Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) {
  8.2586 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2587 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  8.2588 +  PG_RETURN_BOOL(
  8.2589 +    pgl_distance(
  8.2590 +      point->lat, point->lon,
  8.2591 +      circle->center.lat, circle->center.lon
  8.2592 +    ) <= circle->radius
  8.2593 +  );
  8.2594 +}
  8.2595 +
  8.2596 +/* check if point is inside cluster (overlap operator "&&") in SQL */
  8.2597 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap);
  8.2598 +Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
  8.2599 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2600 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2601 +  bool retval;
  8.2602 +  /* points outside bounding circle are always assumed to be non-overlapping */
  8.2603 +  if (
  8.2604 +    pgl_distance(
  8.2605 +      point->lat, point->lon,
  8.2606 +      cluster->bounding.center.lat, cluster->bounding.center.lon
  8.2607 +    ) > cluster->bounding.radius
  8.2608 +  ) retval = false;
  8.2609 +  else retval = pgl_point_in_cluster(point, cluster, false);
  8.2610 +  PG_FREE_IF_COPY(cluster, 1);
  8.2611 +  PG_RETURN_BOOL(retval);
  8.2612 +}
  8.2613 +
  8.2614 +/* check if point may be inside cluster (lossy overl. operator "&&+") in SQL */
  8.2615 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_may_overlap);
  8.2616 +Datum pgl_epoint_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  8.2617 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2618 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2619 +  bool retval = pgl_distance(
  8.2620 +    point->lat, point->lon,
  8.2621 +    cluster->bounding.center.lat, cluster->bounding.center.lon
  8.2622 +  ) <= cluster->bounding.radius;
  8.2623 +  PG_FREE_IF_COPY(cluster, 1);
  8.2624 +  PG_RETURN_BOOL(retval);
  8.2625 +}
  8.2626 +
  8.2627 +/* check if two boxes overlap (overlap operator "&&") in SQL */
  8.2628 +PG_FUNCTION_INFO_V1(pgl_ebox_overlap);
  8.2629 +Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) {
  8.2630 +  pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0);
  8.2631 +  pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1);
  8.2632 +  PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2));
  8.2633 +}
  8.2634 +
  8.2635 +/* check if box and circle may overlap (lossy overl. operator "&&+") in SQL */
  8.2636 +PG_FUNCTION_INFO_V1(pgl_ebox_ecircle_may_overlap);
  8.2637 +Datum pgl_ebox_ecircle_may_overlap(PG_FUNCTION_ARGS) {
  8.2638 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  8.2639 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  8.2640 +  PG_RETURN_BOOL(
  8.2641 +    pgl_estimate_point_box_distance(&circle->center, box) <= circle->radius
  8.2642 +  );
  8.2643 +}
  8.2644 +
  8.2645 +/* check if box and cluster may overlap (lossy overl. operator "&&+") in SQL */
  8.2646 +PG_FUNCTION_INFO_V1(pgl_ebox_ecluster_may_overlap);
  8.2647 +Datum pgl_ebox_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  8.2648 +  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  8.2649 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2650 +  bool retval = pgl_estimate_point_box_distance(
  8.2651 +    &cluster->bounding.center,
  8.2652 +    box
  8.2653 +  ) <= cluster->bounding.radius;
  8.2654 +  PG_FREE_IF_COPY(cluster, 1);
  8.2655 +  PG_RETURN_BOOL(retval);
  8.2656 +}
  8.2657 +
  8.2658 +/* check if two circles overlap (overlap operator "&&") in SQL */
  8.2659 +PG_FUNCTION_INFO_V1(pgl_ecircle_overlap);
  8.2660 +Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) {
  8.2661 +  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  8.2662 +  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  8.2663 +  PG_RETURN_BOOL(
  8.2664 +    pgl_distance(
  8.2665 +      circle1->center.lat, circle1->center.lon,
  8.2666 +      circle2->center.lat, circle2->center.lon
  8.2667 +    ) <= circle1->radius + circle2->radius
  8.2668 +  );
  8.2669 +}
  8.2670 +
  8.2671 +/* check if circle and cluster overlap (overlap operator "&&") in SQL */
  8.2672 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap);
  8.2673 +Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) {
  8.2674 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  8.2675 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2676 +  bool retval;
  8.2677 +  /* points outside bounding circle (with margin for flattening) are always
  8.2678 +     assumed to be non-overlapping */
  8.2679 +  if (
  8.2680 +    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  8.2681 +    pgl_distance(
  8.2682 +      circle->center.lat, circle->center.lon,
  8.2683 +      cluster->bounding.center.lat, cluster->bounding.center.lon
  8.2684 +    ) > circle->radius + cluster->bounding.radius
  8.2685 +  ) retval = false;
  8.2686 +  else retval = pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius;
  8.2687 +  PG_FREE_IF_COPY(cluster, 1);
  8.2688 +  PG_RETURN_BOOL(retval);
  8.2689 +}
  8.2690 +
  8.2691 +/* check if circle and cluster may overlap (l. ov. operator "&&+") in SQL */
  8.2692 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_may_overlap);
  8.2693 +Datum pgl_ecircle_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  8.2694 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  8.2695 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2696 +  bool retval = (
  8.2697 +    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  8.2698 +    pgl_distance(
  8.2699 +      circle->center.lat, circle->center.lon,
  8.2700 +      cluster->bounding.center.lat, cluster->bounding.center.lon
  8.2701 +    )
  8.2702 +  ) <= circle->radius + cluster->bounding.radius;
  8.2703 +  PG_FREE_IF_COPY(cluster, 1);
  8.2704 +  PG_RETURN_BOOL(retval);
  8.2705 +}
  8.2706 +
  8.2707 +/* check if two clusters overlap (overlap operator "&&") in SQL */
  8.2708 +PG_FUNCTION_INFO_V1(pgl_ecluster_overlap);
  8.2709 +Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) {
  8.2710 +  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  8.2711 +  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2712 +  bool retval;
  8.2713 +  /* clusters with non-touching bounding circles (with margin for flattening)
  8.2714 +     are always assumed to be non-overlapping */
  8.2715 +  if (
  8.2716 +    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  8.2717 +    pgl_distance(
  8.2718 +      cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  8.2719 +      cluster2->bounding.center.lat, cluster2->bounding.center.lon
  8.2720 +    ) > cluster1->bounding.radius + cluster2->bounding.radius
  8.2721 +  ) retval = false;
  8.2722 +  else retval = pgl_clusters_overlap(cluster1, cluster2);
  8.2723 +  PG_FREE_IF_COPY(cluster1, 0);
  8.2724 +  PG_FREE_IF_COPY(cluster2, 1);
  8.2725 +  PG_RETURN_BOOL(retval);
  8.2726 +}
  8.2727 +
  8.2728 +/* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */
  8.2729 +PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap);
  8.2730 +Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  8.2731 +  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  8.2732 +  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2733 +  bool retval = (
  8.2734 +    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  8.2735 +    pgl_distance(
  8.2736 +      cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  8.2737 +      cluster2->bounding.center.lat, cluster2->bounding.center.lon
  8.2738 +    )
  8.2739 +  ) <= cluster1->bounding.radius + cluster2->bounding.radius;
  8.2740 +  PG_FREE_IF_COPY(cluster1, 0);
  8.2741 +  PG_FREE_IF_COPY(cluster2, 1);
  8.2742 +  PG_RETURN_BOOL(retval);
  8.2743 +}
  8.2744 +
  8.2745 +/* check if second cluster is in first cluster (cont. operator "@>) in SQL */
  8.2746 +PG_FUNCTION_INFO_V1(pgl_ecluster_contains);
  8.2747 +Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) {
  8.2748 +  pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  8.2749 +  pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2750 +  bool retval;
  8.2751 +  /* clusters with non-touching bounding circles are always assumed to be
  8.2752 +     non-overlapping */
  8.2753 +  if (
  8.2754 +    (1.0-PGL_SPHEROID_F) *  /* margin for flattening and approximation */
  8.2755 +    pgl_distance(
  8.2756 +      outer->bounding.center.lat, outer->bounding.center.lon,
  8.2757 +      inner->bounding.center.lat, inner->bounding.center.lon
  8.2758 +    ) > outer->bounding.radius + inner->bounding.radius
  8.2759 +  ) retval = false;
  8.2760 +  else retval = pgl_cluster_in_cluster(outer, inner);
  8.2761 +  PG_FREE_IF_COPY(outer, 0);
  8.2762 +  PG_FREE_IF_COPY(inner, 1);
  8.2763 +  PG_RETURN_BOOL(retval);
  8.2764 +}
  8.2765 +
  8.2766 +/* calculate distance between two points ("<->" operator) in SQL */
  8.2767 +PG_FUNCTION_INFO_V1(pgl_epoint_distance);
  8.2768 +Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
  8.2769 +  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  8.2770 +  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  8.2771 +  PG_RETURN_FLOAT8(pgl_distance(
  8.2772 +    point1->lat, point1->lon, point2->lat, point2->lon
  8.2773 +  ));
  8.2774 +}
  8.2775 +
  8.2776 +/* calculate point to circle distance ("<->" operator) in SQL */
  8.2777 +PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance);
  8.2778 +Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) {
  8.2779 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2780 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  8.2781 +  double distance = pgl_distance(
  8.2782 +    point->lat, point->lon, circle->center.lat, circle->center.lon
  8.2783 +  ) - circle->radius;
  8.2784 +  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  8.2785 +}
  8.2786 +
  8.2787 +/* calculate point to cluster distance ("<->" operator) in SQL */
  8.2788 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance);
  8.2789 +Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) {
  8.2790 +  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  8.2791 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2792 +  double distance = pgl_point_cluster_distance(point, cluster);
  8.2793 +  PG_FREE_IF_COPY(cluster, 1);
  8.2794 +  PG_RETURN_FLOAT8(distance);
  8.2795 +}
  8.2796 +
  8.2797 +/* calculate distance between two circles ("<->" operator) in SQL */
  8.2798 +PG_FUNCTION_INFO_V1(pgl_ecircle_distance);
  8.2799 +Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) {
  8.2800 +  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  8.2801 +  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  8.2802 +  double distance = pgl_distance(
  8.2803 +    circle1->center.lat, circle1->center.lon,
  8.2804 +    circle2->center.lat, circle2->center.lon
  8.2805 +  ) - (circle1->radius + circle2->radius);
  8.2806 +  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  8.2807 +}
  8.2808 +
  8.2809 +/* calculate circle to cluster distance ("<->" operator) in SQL */
  8.2810 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance);
  8.2811 +Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) {
  8.2812 +  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  8.2813 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2814 +  double distance = (
  8.2815 +    pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius
  8.2816 +  );
  8.2817 +  PG_FREE_IF_COPY(cluster, 1);
  8.2818 +  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  8.2819 +}
  8.2820 +
  8.2821 +/* calculate distance between two clusters ("<->" operator) in SQL */
  8.2822 +PG_FUNCTION_INFO_V1(pgl_ecluster_distance);
  8.2823 +Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) {
  8.2824 +  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  8.2825 +  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2826 +  double retval = pgl_cluster_distance(cluster1, cluster2);
  8.2827 +  PG_FREE_IF_COPY(cluster1, 0);
  8.2828 +  PG_FREE_IF_COPY(cluster2, 1);
  8.2829 +  PG_RETURN_FLOAT8(retval);
  8.2830 +}
  8.2831 +
  8.2832 +/* calculate fair distance (see README) between cluster and point with
  8.2833 +   precision denoted by sample count ("<=> operator) in SQL */
  8.2834 +PG_FUNCTION_INFO_V1(pgl_ecluster_epoint_sc_fair_distance);
  8.2835 +Datum pgl_ecluster_epoint_sc_fair_distance(PG_FUNCTION_ARGS) {
  8.2836 +  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  8.2837 +  pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(1);
  8.2838 +  double retval = pgl_fair_distance(
  8.2839 +    &search->point, cluster, search->samples
  8.2840 +  );
  8.2841 +  PG_FREE_IF_COPY(cluster, 0);
  8.2842 +  PG_RETURN_FLOAT8(retval);
  8.2843 +}
  8.2844 +
  8.2845 +
  8.2846 +/*-----------------------------------------------------------*
  8.2847 + *  B-tree comparison operators and index support functions  *
  8.2848 + *-----------------------------------------------------------*/
  8.2849 +
  8.2850 +/* macro for a B-tree operator (without detoasting) */
  8.2851 +#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \
  8.2852 +  PG_FUNCTION_INFO_V1(func); \
  8.2853 +  Datum func(PG_FUNCTION_ARGS) { \
  8.2854 +    type *a = (type *)PG_GETARG_POINTER(0); \
  8.2855 +    type *b = (type *)PG_GETARG_POINTER(1); \
  8.2856 +    PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \
  8.2857 +  }
  8.2858 +
  8.2859 +/* macro for a B-tree comparison function (without detoasting) */
  8.2860 +#define PGL_BTREE_CMP(func, type, cmpfunc) \
  8.2861 +  PG_FUNCTION_INFO_V1(func); \
  8.2862 +  Datum func(PG_FUNCTION_ARGS) { \
  8.2863 +    type *a = (type *)PG_GETARG_POINTER(0); \
  8.2864 +    type *b = (type *)PG_GETARG_POINTER(1); \
  8.2865 +    PG_RETURN_INT32(cmpfunc(a, b)); \
  8.2866 +  }
  8.2867 +
  8.2868 +/* macro for a B-tree operator (with detoasting) */
  8.2869 +#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \
  8.2870 +  PG_FUNCTION_INFO_V1(func); \
  8.2871 +  Datum func(PG_FUNCTION_ARGS) { \
  8.2872 +    bool res; \
  8.2873 +    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  8.2874 +    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  8.2875 +    res = cmpfunc(a, b) oper 0; \
  8.2876 +    PG_FREE_IF_COPY(a, 0); \
  8.2877 +    PG_FREE_IF_COPY(b, 1); \
  8.2878 +    PG_RETURN_BOOL(res); \
  8.2879 +  }
  8.2880 +
  8.2881 +/* macro for a B-tree comparison function (with detoasting) */
  8.2882 +#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \
  8.2883 +  PG_FUNCTION_INFO_V1(func); \
  8.2884 +  Datum func(PG_FUNCTION_ARGS) { \
  8.2885 +    int32_t res; \
  8.2886 +    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  8.2887 +    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  8.2888 +    res = cmpfunc(a, b); \
  8.2889 +    PG_FREE_IF_COPY(a, 0); \
  8.2890 +    PG_FREE_IF_COPY(b, 1); \
  8.2891 +    PG_RETURN_INT32(res); \
  8.2892 +  }
  8.2893 +
  8.2894 +/* B-tree operators and comparison function for point */
  8.2895 +PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <)
  8.2896 +PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=)
  8.2897 +PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==)
  8.2898 +PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=)
  8.2899 +PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=)
  8.2900 +PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >)
  8.2901 +PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp)
  8.2902 +
  8.2903 +/* B-tree operators and comparison function for box */
  8.2904 +PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <)
  8.2905 +PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=)
  8.2906 +PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==)
  8.2907 +PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=)
  8.2908 +PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=)
  8.2909 +PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >)
  8.2910 +PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp)
  8.2911 +
  8.2912 +/* B-tree operators and comparison function for circle */
  8.2913 +PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <)
  8.2914 +PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=)
  8.2915 +PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==)
  8.2916 +PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=)
  8.2917 +PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=)
  8.2918 +PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >)
  8.2919 +PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp)
  8.2920 +
  8.2921 +
  8.2922 +/*--------------------------------*
  8.2923 + *  GiST index support functions  *
  8.2924 + *--------------------------------*/
  8.2925 +
  8.2926 +/* GiST "consistent" support function */
  8.2927 +PG_FUNCTION_INFO_V1(pgl_gist_consistent);
  8.2928 +Datum pgl_gist_consistent(PG_FUNCTION_ARGS) {
  8.2929 +  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  8.2930 +  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  8.2931 +  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  8.2932 +  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  8.2933 +  /* demand recheck because index and query methods are lossy */
  8.2934 +  *recheck = true;
  8.2935 +  /* strategy number aliases for different operators using the same strategy */
  8.2936 +  strategy %= 100;
  8.2937 +  /* strategy number 11: equality of two points */
  8.2938 +  if (strategy == 11) {
  8.2939 +    /* query datum is another point */
  8.2940 +    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  8.2941 +    /* convert other point to key */
  8.2942 +    pgl_pointkey querykey;
  8.2943 +    pgl_point_to_key(query, querykey);
  8.2944 +    /* return true if both keys overlap */
  8.2945 +    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  8.2946 +  }
  8.2947 +  /* strategy number 13: equality of two circles */
  8.2948 +  if (strategy == 13) {
  8.2949 +    /* query datum is another circle */
  8.2950 +    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  8.2951 +    /* convert other circle to key */
  8.2952 +    pgl_areakey querykey;
  8.2953 +    pgl_circle_to_key(query, querykey);
  8.2954 +    /* return true if both keys overlap */
  8.2955 +    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  8.2956 +  }
  8.2957 +  /* for all remaining strategies, keys on empty objects produce no match */
  8.2958 +  /* (check necessary because query radius may be infinite) */
  8.2959 +  if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false);
  8.2960 +  /* strategy number 21: overlapping with point */
  8.2961 +  if (strategy == 21) {
  8.2962 +    /* query datum is a point */
  8.2963 +    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  8.2964 +    /* return true if estimated distance (allowed to be smaller than real
  8.2965 +       distance) between index key and point is zero */
  8.2966 +    PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0);
  8.2967 +  }
  8.2968 +  /* strategy number 22: (point) overlapping with box */
  8.2969 +  if (strategy == 22) {
  8.2970 +    /* query datum is a box */
  8.2971 +    pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1);
  8.2972 +    /* determine bounding box of indexed key */
  8.2973 +    pgl_box keybox;
  8.2974 +    pgl_key_to_box(key, &keybox);
  8.2975 +    /* return true if query box overlaps with bounding box of indexed key */
  8.2976 +    PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox));
  8.2977 +  }
  8.2978 +  /* strategy number 23: overlapping with circle */
  8.2979 +  if (strategy == 23) {
  8.2980 +    /* query datum is a circle */
  8.2981 +    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  8.2982 +    /* return true if estimated distance (allowed to be smaller than real
  8.2983 +       distance) between index key and circle center is smaller than radius */
  8.2984 +    PG_RETURN_BOOL(
  8.2985 +      (1.0-PGL_SPHEROID_F) *  /* safety margin for lossy operator */
  8.2986 +      pgl_estimate_key_distance(key, &(query->center))
  8.2987 +      <= query->radius
  8.2988 +    );
  8.2989 +  }
  8.2990 +  /* strategy number 24: overlapping with cluster */
  8.2991 +  if (strategy == 24) {
  8.2992 +    bool retval;  /* return value */
  8.2993 +    /* query datum is a cluster */
  8.2994 +    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.2995 +    /* return true if estimated distance (allowed to be smaller than real
  8.2996 +       distance) between index key and circle center is smaller than radius */
  8.2997 +    retval = (
  8.2998 +      (1.0-PGL_SPHEROID_F) *  /* safety margin for lossy operator */
  8.2999 +      pgl_estimate_key_distance(key, &(query->bounding.center))
  8.3000 +      <= query->bounding.radius
  8.3001 +    );
  8.3002 +    PG_FREE_IF_COPY(query, 1);  /* free detoasted cluster (if copy) */
  8.3003 +    PG_RETURN_BOOL(retval);
  8.3004 +  }
  8.3005 +  /* throw error for any unknown strategy number */
  8.3006 +  elog(ERROR, "unrecognized strategy number: %d", strategy);
  8.3007 +}
  8.3008 +
  8.3009 +/* GiST "union" support function */
  8.3010 +PG_FUNCTION_INFO_V1(pgl_gist_union);
  8.3011 +Datum pgl_gist_union(PG_FUNCTION_ARGS) {
  8.3012 +  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  8.3013 +  pgl_keyptr out;  /* return value (to be palloc'ed) */
  8.3014 +  int i;
  8.3015 +  /* determine key size */
  8.3016 +  size_t keysize = PGL_KEY_IS_AREAKEY(
  8.3017 +    (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key)
  8.3018 +  ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey);
  8.3019 +  /* begin with first key as result */
  8.3020 +  out = palloc(keysize);
  8.3021 +  memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize);
  8.3022 +  /* unite current result with second, third, etc. key */
  8.3023 +  for (i=1; i<entryvec->n; i++) {
  8.3024 +    pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key));
  8.3025 +  }
  8.3026 +  /* return result */
  8.3027 +  PG_RETURN_POINTER(out);
  8.3028 +}
  8.3029 +
  8.3030 +/* GiST "compress" support function for indicis on points */
  8.3031 +PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint);
  8.3032 +Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) {
  8.3033 +  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  8.3034 +  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  8.3035 +  /* only transform new leaves */
  8.3036 +  if (entry->leafkey) {
  8.3037 +    /* get point to be transformed */
  8.3038 +    pgl_point *point = (pgl_point *)DatumGetPointer(entry->key);
  8.3039 +    /* allocate memory for key */
  8.3040 +    pgl_keyptr key = palloc(sizeof(pgl_pointkey));
  8.3041 +    /* transform point to key */
  8.3042 +    pgl_point_to_key(point, key);
  8.3043 +    /* create new GISTENTRY structure as return value */
  8.3044 +    retval = palloc(sizeof(GISTENTRY));
  8.3045 +    gistentryinit(
  8.3046 +      *retval, PointerGetDatum(key),
  8.3047 +      entry->rel, entry->page, entry->offset, false
  8.3048 +    );
  8.3049 +  } else {
  8.3050 +    /* inner nodes have already been transformed */
  8.3051 +    retval = entry;
  8.3052 +  }
  8.3053 +  /* return pointer to old or new GISTENTRY structure */
  8.3054 +  PG_RETURN_POINTER(retval);
  8.3055 +}
  8.3056 +
  8.3057 +/* GiST "compress" support function for indicis on circles */
  8.3058 +PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle);
  8.3059 +Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) {
  8.3060 +  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  8.3061 +  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  8.3062 +  /* only transform new leaves */
  8.3063 +  if (entry->leafkey) {
  8.3064 +    /* get circle to be transformed */
  8.3065 +    pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key);
  8.3066 +    /* allocate memory for key */
  8.3067 +    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  8.3068 +    /* transform circle to key */
  8.3069 +    pgl_circle_to_key(circle, key);
  8.3070 +    /* create new GISTENTRY structure as return value */
  8.3071 +    retval = palloc(sizeof(GISTENTRY));
  8.3072 +    gistentryinit(
  8.3073 +      *retval, PointerGetDatum(key),
  8.3074 +      entry->rel, entry->page, entry->offset, false
  8.3075 +    );
  8.3076 +  } else {
  8.3077 +    /* inner nodes have already been transformed */
  8.3078 +    retval = entry;
  8.3079 +  }
  8.3080 +  /* return pointer to old or new GISTENTRY structure */
  8.3081 +  PG_RETURN_POINTER(retval);
  8.3082 +}
  8.3083 +
  8.3084 +/* GiST "compress" support function for indices on clusters */
  8.3085 +PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster);
  8.3086 +Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) {
  8.3087 +  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  8.3088 +  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  8.3089 +  /* only transform new leaves */
  8.3090 +  if (entry->leafkey) {
  8.3091 +    /* get cluster to be transformed (detoasting necessary!) */
  8.3092 +    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key);
  8.3093 +    /* allocate memory for key */
  8.3094 +    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  8.3095 +    /* transform cluster to key */
  8.3096 +    pgl_circle_to_key(&(cluster->bounding), key);
  8.3097 +    /* create new GISTENTRY structure as return value */
  8.3098 +    retval = palloc(sizeof(GISTENTRY));
  8.3099 +    gistentryinit(
  8.3100 +      *retval, PointerGetDatum(key),
  8.3101 +      entry->rel, entry->page, entry->offset, false
  8.3102 +    );
  8.3103 +    /* free detoasted datum */
  8.3104 +    if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster);
  8.3105 +  } else {
  8.3106 +    /* inner nodes have already been transformed */
  8.3107 +    retval = entry;
  8.3108 +  }
  8.3109 +  /* return pointer to old or new GISTENTRY structure */
  8.3110 +  PG_RETURN_POINTER(retval);
  8.3111 +}
  8.3112 +
  8.3113 +/* GiST "decompress" support function for indices */
  8.3114 +PG_FUNCTION_INFO_V1(pgl_gist_decompress);
  8.3115 +Datum pgl_gist_decompress(PG_FUNCTION_ARGS) {
  8.3116 +  /* return passed pointer without transformation */
  8.3117 +  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
  8.3118 +}
  8.3119 +
  8.3120 +/* GiST "penalty" support function */
  8.3121 +PG_FUNCTION_INFO_V1(pgl_gist_penalty);
  8.3122 +Datum pgl_gist_penalty(PG_FUNCTION_ARGS) {
  8.3123 +  GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0);
  8.3124 +  GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1);
  8.3125 +  float *penalty = (float *)PG_GETARG_POINTER(2);
  8.3126 +  /* get original key and key to insert */
  8.3127 +  pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key);
  8.3128 +  pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key);
  8.3129 +  /* copy original key */
  8.3130 +  union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key;
  8.3131 +  if (PGL_KEY_IS_AREAKEY(orig)) {
  8.3132 +    memcpy(union_key.areakey, orig, sizeof(union_key.areakey));
  8.3133 +  } else {
  8.3134 +    memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey));
  8.3135 +  }
  8.3136 +  /* calculate union of both keys */
  8.3137 +  pgl_unite_keys((pgl_keyptr)&union_key, new);
  8.3138 +  /* penalty equal to reduction of key length (logarithm of added area) */
  8.3139 +  /* (return value by setting referenced value and returning pointer) */
  8.3140 +  *penalty = (
  8.3141 +    PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key)
  8.3142 +  );
  8.3143 +  PG_RETURN_POINTER(penalty);
  8.3144 +}
  8.3145 +
  8.3146 +/* GiST "picksplit" support function */
  8.3147 +PG_FUNCTION_INFO_V1(pgl_gist_picksplit);
  8.3148 +Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) {
  8.3149 +  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  8.3150 +  GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1);
  8.3151 +  OffsetNumber i;  /* between FirstOffsetNumber and entryvec->n (exclusive) */
  8.3152 +  union {
  8.3153 +    pgl_pointkey pointkey;
  8.3154 +    pgl_areakey areakey;
  8.3155 +  } union_all;  /* union of all keys (to be calculated from scratch)
  8.3156 +                   (later cut in half) */
  8.3157 +  int is_areakey = PGL_KEY_IS_AREAKEY(
  8.3158 +    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key)
  8.3159 +  );
  8.3160 +  int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey);
  8.3161 +  pgl_keyptr unionL = palloc(keysize);  /* union of keys that go left */
  8.3162 +  pgl_keyptr unionR = palloc(keysize);  /* union of keys that go right */
  8.3163 +  pgl_keyptr key;  /* current key to be processed */
  8.3164 +  /* allocate memory for array of left and right keys, set counts to zero */
  8.3165 +  v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  8.3166 +  v->spl_nleft = 0;
  8.3167 +  v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  8.3168 +  v->spl_nright = 0;
  8.3169 +  /* calculate union of all keys from scratch */
  8.3170 +  memcpy(
  8.3171 +    (pgl_keyptr)&union_all,
  8.3172 +    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key),
  8.3173 +    keysize
  8.3174 +  );
  8.3175 +  for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) {
  8.3176 +    pgl_unite_keys(
  8.3177 +      (pgl_keyptr)&union_all,
  8.3178 +      (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)
  8.3179 +    );
  8.3180 +  }
  8.3181 +  /* check if trivial split is necessary due to exhausted key length */
  8.3182 +  /* (Note: keys for empty objects must have node depth set to maximum) */
  8.3183 +  if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == (
  8.3184 +    is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH
  8.3185 +  )) {
  8.3186 +    /* half of all keys go left */
  8.3187 +    for (
  8.3188 +      i=FirstOffsetNumber;
  8.3189 +      i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  8.3190 +      i=OffsetNumberNext(i)
  8.3191 +    ) {
  8.3192 +      /* pointer to current key */
  8.3193 +      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  8.3194 +      /* update unionL */
  8.3195 +      /* check if key is first key that goes left */
  8.3196 +      if (!v->spl_nleft) {
  8.3197 +        /* first key that goes left is just copied to unionL */
  8.3198 +        memcpy(unionL, key, keysize);
  8.3199 +      } else {
  8.3200 +        /* unite current value and next key */
  8.3201 +        pgl_unite_keys(unionL, key);
  8.3202 +      }
  8.3203 +      /* append offset number to list of keys that go left */
  8.3204 +      v->spl_left[v->spl_nleft++] = i;
  8.3205 +    }
  8.3206 +    /* other half goes right */
  8.3207 +    for (
  8.3208 +      i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  8.3209 +      i<entryvec->n;
  8.3210 +      i=OffsetNumberNext(i)
  8.3211 +    ) {
  8.3212 +      /* pointer to current key */
  8.3213 +      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  8.3214 +      /* update unionR */
  8.3215 +      /* check if key is first key that goes right */
  8.3216 +      if (!v->spl_nright) {
  8.3217 +        /* first key that goes right is just copied to unionR */
  8.3218 +        memcpy(unionR, key, keysize);
  8.3219 +      } else {
  8.3220 +        /* unite current value and next key */
  8.3221 +        pgl_unite_keys(unionR, key);
  8.3222 +      }
  8.3223 +      /* append offset number to list of keys that go right */
  8.3224 +      v->spl_right[v->spl_nright++] = i;
  8.3225 +    }
  8.3226 +  }
  8.3227 +  /* otherwise, a non-trivial split is possible */
  8.3228 +  else {
  8.3229 +    /* cut covered area in half */
  8.3230 +    /* (union_all then refers to area of keys that go left) */
  8.3231 +    /* check if union of all keys covers empty and non-empty objects */
  8.3232 +    if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) {
  8.3233 +      /* if yes, split into empty and non-empty objects */
  8.3234 +      pgl_key_set_empty((pgl_keyptr)&union_all);
  8.3235 +    } else {
  8.3236 +      /* otherwise split by next bit */
  8.3237 +      ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++;
  8.3238 +      /* NOTE: type bit conserved */
  8.3239 +    }
  8.3240 +    /* determine for each key if it goes left or right */
  8.3241 +    for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) {
  8.3242 +      /* pointer to current key */
  8.3243 +      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  8.3244 +      /* keys within one half of the area go left */
  8.3245 +      if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) {
  8.3246 +        /* update unionL */
  8.3247 +        /* check if key is first key that goes left */
  8.3248 +        if (!v->spl_nleft) {
  8.3249 +          /* first key that goes left is just copied to unionL */
  8.3250 +          memcpy(unionL, key, keysize);
  8.3251 +        } else {
  8.3252 +          /* unite current value of unionL and processed key */
  8.3253 +          pgl_unite_keys(unionL, key);
  8.3254 +        }
  8.3255 +        /* append offset number to list of keys that go left */
  8.3256 +        v->spl_left[v->spl_nleft++] = i;
  8.3257 +      }
  8.3258 +      /* the other keys go right */
  8.3259 +      else {
  8.3260 +        /* update unionR */
  8.3261 +        /* check if key is first key that goes right */
  8.3262 +        if (!v->spl_nright) {
  8.3263 +          /* first key that goes right is just copied to unionR */
  8.3264 +          memcpy(unionR, key, keysize);
  8.3265 +        } else {
  8.3266 +          /* unite current value of unionR and processed key */
  8.3267 +          pgl_unite_keys(unionR, key);
  8.3268 +        }
  8.3269 +        /* append offset number to list of keys that go right */
  8.3270 +        v->spl_right[v->spl_nright++] = i;
  8.3271 +      }
  8.3272 +    }
  8.3273 +  }
  8.3274 +  /* store unions in return value */
  8.3275 +  v->spl_ldatum = PointerGetDatum(unionL);
  8.3276 +  v->spl_rdatum = PointerGetDatum(unionR);
  8.3277 +  /* return all results */
  8.3278 +  PG_RETURN_POINTER(v);
  8.3279 +}
  8.3280 +
  8.3281 +/* GiST "same"/"equal" support function */
  8.3282 +PG_FUNCTION_INFO_V1(pgl_gist_same);
  8.3283 +Datum pgl_gist_same(PG_FUNCTION_ARGS) {
  8.3284 +  pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0);
  8.3285 +  pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1);
  8.3286 +  bool *boolptr = (bool *)PG_GETARG_POINTER(2);
  8.3287 +  /* two keys are equal if they are binary equal */
  8.3288 +  /* (return result by setting referenced boolean and returning pointer) */
  8.3289 +  *boolptr = !memcmp(
  8.3290 +    key1,
  8.3291 +    key2,
  8.3292 +    PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey)
  8.3293 +  );
  8.3294 +  PG_RETURN_POINTER(boolptr);
  8.3295 +}
  8.3296 +
  8.3297 +/* GiST "distance" support function */
  8.3298 +PG_FUNCTION_INFO_V1(pgl_gist_distance);
  8.3299 +Datum pgl_gist_distance(PG_FUNCTION_ARGS) {
  8.3300 +  GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
  8.3301 +  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  8.3302 +  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  8.3303 +  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  8.3304 +  double distance;  /* return value */
  8.3305 +  /* demand recheck because distance is just an estimation */
  8.3306 +  /* (real distance may be bigger) */
  8.3307 +  *recheck = true;
  8.3308 +  /* strategy number aliases for different operators using the same strategy */
  8.3309 +  strategy %= 100;
  8.3310 +  /* strategy number 31: distance to point */
  8.3311 +  if (strategy == 31) {
  8.3312 +    /* query datum is a point */
  8.3313 +    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  8.3314 +    /* use pgl_estimate_pointkey_distance() function to compute result */
  8.3315 +    distance = pgl_estimate_key_distance(key, query);
  8.3316 +    /* avoid infinity (reserved!) */
  8.3317 +    if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  8.3318 +    /* return result */
  8.3319 +    PG_RETURN_FLOAT8(distance);
  8.3320 +  }
  8.3321 +  /* strategy number 33: distance to circle */
  8.3322 +  if (strategy == 33) {
  8.3323 +    /* query datum is a circle */
  8.3324 +    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  8.3325 +    /* estimate distance to circle center and substract circle radius */
  8.3326 +    distance = (
  8.3327 +      pgl_estimate_key_distance(key, &(query->center)) - query->radius
  8.3328 +    );
  8.3329 +    /* convert non-positive values to zero and avoid infinity (reserved!) */
  8.3330 +    if (distance <= 0) distance = 0;
  8.3331 +    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  8.3332 +    /* return result */
  8.3333 +    PG_RETURN_FLOAT8(distance);
  8.3334 +  }
  8.3335 +  /* strategy number 34: distance to cluster */
  8.3336 +  if (strategy == 34) {
  8.3337 +    /* query datum is a cluster */
  8.3338 +    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  8.3339 +    /* estimate distance to bounding center and substract bounding radius */
  8.3340 +    distance = (
  8.3341 +      pgl_estimate_key_distance(key, &(query->bounding.center)) -
  8.3342 +      query->bounding.radius
  8.3343 +    );
  8.3344 +    /* convert non-positive values to zero and avoid infinity (reserved!) */
  8.3345 +    if (distance <= 0) distance = 0;
  8.3346 +    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  8.3347 +    /* free detoasted cluster (if copy) */
  8.3348 +    PG_FREE_IF_COPY(query, 1);
  8.3349 +    /* return result */
  8.3350 +    PG_RETURN_FLOAT8(distance);
  8.3351 +  }
  8.3352 +  /* throw error for any unknown strategy number */
  8.3353 +  elog(ERROR, "unrecognized strategy number: %d", strategy);
  8.3354 +}
  8.3355 +
     9.1 --- a/latlon.control	Tue Feb 11 02:35:16 2020 +0100
     9.2 +++ b/latlon.control	Wed Feb 12 11:08:37 2020 +0100
     9.3 @@ -1,3 +1,3 @@
     9.4  comment = 'Geographic data types and spatial indexing for the WGS-84 spheroid'
     9.5 -default_version = '0.14'
     9.6 +default_version = '0.15'
     9.7  relocatable = true

Impressum / About Us