pgLatLon

changeset 26:f0c2535a8465

Removed files for version 0.5
author jbe
date Thu Sep 22 23:31:29 2016 +0200 (2016-09-22)
parents d8ac13006162
children a7ba8335e541
files GNUmakefile latlon--0.4--0.5.sql latlon--0.5.sql latlon-v0005.c latlon.control
line diff
     1.1 --- a/GNUmakefile	Thu Sep 22 21:57:03 2016 +0200
     1.2 +++ b/GNUmakefile	Thu Sep 22 23:31:29 2016 +0200
     1.3 @@ -1,6 +1,6 @@
     1.4  EXTENSION = latlon
     1.5 -DATA = latlon--0.4--0.5.sql latlon--0.5.sql latlon--0.5--0.6.sql latlon--0.6.sql
     1.6 -MODULES = latlon-v0005 latlon-v0006
     1.7 +DATA = latlon--0.5--0.6.sql latlon--0.6.sql
     1.8 +MODULES = latlon-v0006
     1.9  
    1.10  PG_CONFIG = pg_config
    1.11  PGXS := $(shell $(PG_CONFIG) --pgxs)
     2.1 --- a/latlon--0.4--0.5.sql	Thu Sep 22 21:57:03 2016 +0200
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,457 +0,0 @@
     2.4 -
     2.5 -CREATE OR REPLACE FUNCTION ekey_point_in_dummy(cstring)
     2.6 -  RETURNS ekey_point
     2.7 -  LANGUAGE C IMMUTABLE STRICT
     2.8 -  AS '$libdir/latlon-v0005', 'pgl_notimpl';
     2.9 -
    2.10 -CREATE OR REPLACE FUNCTION ekey_point_out_dummy(ekey_point)
    2.11 -  RETURNS cstring
    2.12 -  LANGUAGE C IMMUTABLE STRICT
    2.13 -  AS '$libdir/latlon-v0005', 'pgl_notimpl';
    2.14 -
    2.15 -CREATE OR REPLACE FUNCTION ekey_area_in_dummy(cstring)
    2.16 -  RETURNS ekey_area
    2.17 -  LANGUAGE C IMMUTABLE STRICT
    2.18 -  AS '$libdir/latlon-v0005', 'pgl_notimpl';
    2.19 -
    2.20 -CREATE OR REPLACE FUNCTION ekey_area_out_dummy(ekey_area)
    2.21 -  RETURNS cstring
    2.22 -  LANGUAGE C IMMUTABLE STRICT
    2.23 -  AS '$libdir/latlon-v0005', 'pgl_notimpl';
    2.24 -
    2.25 -CREATE OR REPLACE FUNCTION epoint_in(cstring)
    2.26 -  RETURNS epoint
    2.27 -  LANGUAGE C IMMUTABLE STRICT
    2.28 -  AS '$libdir/latlon-v0005', 'pgl_epoint_in';
    2.29 -
    2.30 -CREATE OR REPLACE FUNCTION ebox_in(cstring)
    2.31 -  RETURNS ebox
    2.32 -  LANGUAGE C IMMUTABLE STRICT
    2.33 -  AS '$libdir/latlon-v0005', 'pgl_ebox_in';
    2.34 -
    2.35 -CREATE OR REPLACE FUNCTION ecircle_in(cstring)
    2.36 -  RETURNS ecircle
    2.37 -  LANGUAGE C IMMUTABLE STRICT
    2.38 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_in';
    2.39 -
    2.40 -CREATE OR REPLACE FUNCTION ecluster_in(cstring)
    2.41 -  RETURNS ecluster
    2.42 -  LANGUAGE C IMMUTABLE STRICT
    2.43 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_in';
    2.44 -
    2.45 -CREATE OR REPLACE FUNCTION epoint_out(epoint)
    2.46 -  RETURNS cstring
    2.47 -  LANGUAGE C IMMUTABLE STRICT
    2.48 -  AS '$libdir/latlon-v0005', 'pgl_epoint_out';
    2.49 -
    2.50 -CREATE OR REPLACE FUNCTION ebox_out(ebox)
    2.51 -  RETURNS cstring
    2.52 -  LANGUAGE C IMMUTABLE STRICT
    2.53 -  AS '$libdir/latlon-v0005', 'pgl_ebox_out';
    2.54 -
    2.55 -CREATE OR REPLACE FUNCTION ecircle_out(ecircle)
    2.56 -  RETURNS cstring
    2.57 -  LANGUAGE C IMMUTABLE STRICT
    2.58 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_out';
    2.59 -
    2.60 -CREATE OR REPLACE FUNCTION ecluster_out(ecluster)
    2.61 -  RETURNS cstring
    2.62 -  LANGUAGE C IMMUTABLE STRICT
    2.63 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_out';
    2.64 -
    2.65 -CREATE OR REPLACE FUNCTION epoint_recv(internal)
    2.66 -  RETURNS epoint
    2.67 -  LANGUAGE C IMMUTABLE STRICT
    2.68 -  AS '$libdir/latlon-v0005', 'pgl_epoint_recv';
    2.69 -
    2.70 -CREATE OR REPLACE FUNCTION ebox_recv(internal)
    2.71 -  RETURNS ebox
    2.72 -  LANGUAGE C IMMUTABLE STRICT
    2.73 -  AS '$libdir/latlon-v0005', 'pgl_ebox_recv';
    2.74 -
    2.75 -CREATE OR REPLACE FUNCTION ecircle_recv(internal)
    2.76 -  RETURNS ecircle
    2.77 -  LANGUAGE C IMMUTABLE STRICT
    2.78 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_recv';
    2.79 -
    2.80 -CREATE OR REPLACE FUNCTION epoint_send(epoint)
    2.81 -  RETURNS bytea
    2.82 -  LANGUAGE C IMMUTABLE STRICT
    2.83 -  AS '$libdir/latlon-v0005', 'pgl_epoint_send';
    2.84 -
    2.85 -CREATE OR REPLACE FUNCTION ebox_send(ebox)
    2.86 -  RETURNS bytea
    2.87 -  LANGUAGE C IMMUTABLE STRICT
    2.88 -  AS '$libdir/latlon-v0005', 'pgl_ebox_send';
    2.89 -
    2.90 -CREATE OR REPLACE FUNCTION ecircle_send(ecircle)
    2.91 -  RETURNS bytea
    2.92 -  LANGUAGE C IMMUTABLE STRICT
    2.93 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_send';
    2.94 -
    2.95 -CREATE OR REPLACE FUNCTION epoint_btree_lt(epoint, epoint)
    2.96 -  RETURNS boolean
    2.97 -  LANGUAGE C IMMUTABLE STRICT
    2.98 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_lt';
    2.99 -
   2.100 -CREATE OR REPLACE FUNCTION epoint_btree_le(epoint, epoint)
   2.101 -  RETURNS boolean
   2.102 -  LANGUAGE C IMMUTABLE STRICT
   2.103 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_le';
   2.104 -
   2.105 -CREATE OR REPLACE FUNCTION epoint_btree_eq(epoint, epoint)
   2.106 -  RETURNS boolean
   2.107 -  LANGUAGE C IMMUTABLE STRICT
   2.108 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_eq';
   2.109 -
   2.110 -CREATE OR REPLACE FUNCTION epoint_btree_ne(epoint, epoint)
   2.111 -  RETURNS boolean
   2.112 -  LANGUAGE C IMMUTABLE STRICT
   2.113 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_ne';
   2.114 -
   2.115 -CREATE OR REPLACE FUNCTION epoint_btree_ge(epoint, epoint)
   2.116 -  RETURNS boolean
   2.117 -  LANGUAGE C IMMUTABLE STRICT
   2.118 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_ge';
   2.119 -
   2.120 -CREATE OR REPLACE FUNCTION epoint_btree_gt(epoint, epoint)
   2.121 -  RETURNS boolean
   2.122 -  LANGUAGE C IMMUTABLE STRICT
   2.123 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_gt';
   2.124 -
   2.125 -CREATE OR REPLACE FUNCTION epoint_btree_cmp(epoint, epoint)
   2.126 -  RETURNS int4
   2.127 -  LANGUAGE C IMMUTABLE STRICT
   2.128 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_cmp';
   2.129 -
   2.130 -CREATE OR REPLACE FUNCTION ebox_btree_lt(ebox, ebox)
   2.131 -  RETURNS boolean
   2.132 -  LANGUAGE C IMMUTABLE STRICT
   2.133 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_lt';
   2.134 -
   2.135 -CREATE OR REPLACE FUNCTION ebox_btree_le(ebox, ebox)
   2.136 -  RETURNS boolean
   2.137 -  LANGUAGE C IMMUTABLE STRICT
   2.138 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_le';
   2.139 -
   2.140 -CREATE OR REPLACE FUNCTION ebox_btree_eq(ebox, ebox)
   2.141 -  RETURNS boolean
   2.142 -  LANGUAGE C IMMUTABLE STRICT
   2.143 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_eq';
   2.144 -
   2.145 -CREATE OR REPLACE FUNCTION ebox_btree_ne(ebox, ebox)
   2.146 -  RETURNS boolean
   2.147 -  LANGUAGE C IMMUTABLE STRICT
   2.148 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_ne';
   2.149 -
   2.150 -CREATE OR REPLACE FUNCTION ebox_btree_ge(ebox, ebox)
   2.151 -  RETURNS boolean
   2.152 -  LANGUAGE C IMMUTABLE STRICT
   2.153 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_ge';
   2.154 -
   2.155 -CREATE OR REPLACE FUNCTION ebox_btree_gt(ebox, ebox)
   2.156 -  RETURNS boolean
   2.157 -  LANGUAGE C IMMUTABLE STRICT
   2.158 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_gt';
   2.159 -
   2.160 -CREATE OR REPLACE FUNCTION ebox_btree_cmp(ebox, ebox)
   2.161 -  RETURNS int4
   2.162 -  LANGUAGE C IMMUTABLE STRICT
   2.163 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_cmp';
   2.164 -
   2.165 -CREATE OR REPLACE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   2.166 -  RETURNS boolean
   2.167 -  LANGUAGE C IMMUTABLE STRICT
   2.168 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_lt';
   2.169 -
   2.170 -CREATE OR REPLACE FUNCTION ecircle_btree_le(ecircle, ecircle)
   2.171 -  RETURNS boolean
   2.172 -  LANGUAGE C IMMUTABLE STRICT
   2.173 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_le';
   2.174 -
   2.175 -CREATE OR REPLACE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   2.176 -  RETURNS boolean
   2.177 -  LANGUAGE C IMMUTABLE STRICT
   2.178 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_eq';
   2.179 -
   2.180 -CREATE OR REPLACE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   2.181 -  RETURNS boolean
   2.182 -  LANGUAGE C IMMUTABLE STRICT
   2.183 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_ne';
   2.184 -
   2.185 -CREATE OR REPLACE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   2.186 -  RETURNS boolean
   2.187 -  LANGUAGE C IMMUTABLE STRICT
   2.188 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_ge';
   2.189 -
   2.190 -CREATE OR REPLACE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   2.191 -  RETURNS boolean
   2.192 -  LANGUAGE C IMMUTABLE STRICT
   2.193 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_gt';
   2.194 -
   2.195 -CREATE OR REPLACE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   2.196 -  RETURNS int4
   2.197 -  LANGUAGE C IMMUTABLE STRICT
   2.198 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_cmp';
   2.199 -
   2.200 -CREATE OR REPLACE FUNCTION cast_epoint_to_ebox(epoint)
   2.201 -  RETURNS ebox
   2.202 -  LANGUAGE C IMMUTABLE STRICT
   2.203 -  AS '$libdir/latlon-v0005', 'pgl_epoint_to_ebox';
   2.204 -
   2.205 -CREATE OR REPLACE FUNCTION cast_epoint_to_ecircle(epoint)
   2.206 -  RETURNS ecircle
   2.207 -  LANGUAGE C IMMUTABLE STRICT
   2.208 -  AS '$libdir/latlon-v0005', 'pgl_epoint_to_ecircle';
   2.209 -
   2.210 -CREATE OR REPLACE FUNCTION cast_epoint_to_ecluster(epoint)
   2.211 -  RETURNS ecluster
   2.212 -  LANGUAGE C IMMUTABLE STRICT
   2.213 -  AS '$libdir/latlon-v0005', 'pgl_epoint_to_ecluster';
   2.214 -
   2.215 -CREATE OR REPLACE FUNCTION cast_ebox_to_ecluster(ebox)
   2.216 -  RETURNS ecluster
   2.217 -  LANGUAGE C IMMUTABLE STRICT
   2.218 -  AS '$libdir/latlon-v0005', 'pgl_ebox_to_ecluster';
   2.219 -
   2.220 -CREATE OR REPLACE FUNCTION epoint(float8, float8)
   2.221 -  RETURNS epoint
   2.222 -  LANGUAGE C IMMUTABLE STRICT
   2.223 -  AS '$libdir/latlon-v0005', 'pgl_create_epoint';
   2.224 -
   2.225 -CREATE OR REPLACE FUNCTION empty_ebox()
   2.226 -  RETURNS ebox
   2.227 -  LANGUAGE C IMMUTABLE STRICT
   2.228 -  AS '$libdir/latlon-v0005', 'pgl_create_empty_ebox';
   2.229 -
   2.230 -CREATE OR REPLACE FUNCTION ebox(float8, float8, float8, float8)
   2.231 -  RETURNS ebox
   2.232 -  LANGUAGE C IMMUTABLE STRICT
   2.233 -  AS '$libdir/latlon-v0005', 'pgl_create_ebox';
   2.234 -
   2.235 -CREATE OR REPLACE FUNCTION ebox(epoint, epoint)
   2.236 -  RETURNS ebox
   2.237 -  LANGUAGE C IMMUTABLE STRICT
   2.238 -  AS '$libdir/latlon-v0005', 'pgl_create_ebox_from_epoints';
   2.239 -
   2.240 -CREATE OR REPLACE FUNCTION ecircle(float8, float8, float8)
   2.241 -  RETURNS ecircle
   2.242 -  LANGUAGE C IMMUTABLE STRICT
   2.243 -  AS '$libdir/latlon-v0005', 'pgl_create_ecircle';
   2.244 -
   2.245 -CREATE OR REPLACE FUNCTION ecircle(epoint, float8)
   2.246 -  RETURNS ecircle
   2.247 -  LANGUAGE C IMMUTABLE STRICT
   2.248 -  AS '$libdir/latlon-v0005', 'pgl_create_ecircle_from_epoint';
   2.249 -
   2.250 -CREATE OR REPLACE FUNCTION latitude(epoint)
   2.251 -  RETURNS float8
   2.252 -  LANGUAGE C IMMUTABLE STRICT
   2.253 -  AS '$libdir/latlon-v0005', 'pgl_epoint_lat';
   2.254 -
   2.255 -CREATE OR REPLACE FUNCTION longitude(epoint)
   2.256 -  RETURNS float8
   2.257 -  LANGUAGE C IMMUTABLE STRICT
   2.258 -  AS '$libdir/latlon-v0005', 'pgl_epoint_lon';
   2.259 -
   2.260 -CREATE OR REPLACE FUNCTION min_latitude(ebox)
   2.261 -  RETURNS float8
   2.262 -  LANGUAGE C IMMUTABLE STRICT
   2.263 -  AS '$libdir/latlon-v0005', 'pgl_ebox_lat_min';
   2.264 -
   2.265 -CREATE OR REPLACE FUNCTION max_latitude(ebox)
   2.266 -  RETURNS float8
   2.267 -  LANGUAGE C IMMUTABLE STRICT
   2.268 -  AS '$libdir/latlon-v0005', 'pgl_ebox_lat_max';
   2.269 -
   2.270 -CREATE OR REPLACE FUNCTION min_longitude(ebox)
   2.271 -  RETURNS float8
   2.272 -  LANGUAGE C IMMUTABLE STRICT
   2.273 -  AS '$libdir/latlon-v0005', 'pgl_ebox_lon_min';
   2.274 -
   2.275 -CREATE OR REPLACE FUNCTION max_longitude(ebox)
   2.276 -  RETURNS float8
   2.277 -  LANGUAGE C IMMUTABLE STRICT
   2.278 -  AS '$libdir/latlon-v0005', 'pgl_ebox_lon_max';
   2.279 -
   2.280 -CREATE OR REPLACE FUNCTION center(ecircle)
   2.281 -  RETURNS epoint
   2.282 -  LANGUAGE C IMMUTABLE STRICT
   2.283 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_center';
   2.284 -
   2.285 -CREATE OR REPLACE FUNCTION radius(ecircle)
   2.286 -  RETURNS float8
   2.287 -  LANGUAGE C IMMUTABLE STRICT
   2.288 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_radius';
   2.289 -
   2.290 -CREATE OR REPLACE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   2.291 -  RETURNS boolean
   2.292 -  LANGUAGE C IMMUTABLE STRICT
   2.293 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ebox_overlap';
   2.294 -
   2.295 -CREATE OR REPLACE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   2.296 -  RETURNS boolean
   2.297 -  LANGUAGE C IMMUTABLE STRICT
   2.298 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecircle_overlap';
   2.299 -
   2.300 -CREATE OR REPLACE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   2.301 -  RETURNS boolean
   2.302 -  LANGUAGE C IMMUTABLE STRICT
   2.303 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecluster_overlap';
   2.304 -
   2.305 -CREATE OR REPLACE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
   2.306 -  RETURNS boolean
   2.307 -  LANGUAGE C IMMUTABLE STRICT
   2.308 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecluster_may_overlap';
   2.309 -
   2.310 -CREATE OR REPLACE FUNCTION ebox_overlap_proc(ebox, ebox)
   2.311 -  RETURNS boolean
   2.312 -  LANGUAGE C IMMUTABLE STRICT
   2.313 -  AS '$libdir/latlon-v0005', 'pgl_ebox_overlap';
   2.314 -
   2.315 -CREATE OR REPLACE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
   2.316 -  RETURNS boolean
   2.317 -  LANGUAGE C IMMUTABLE STRICT
   2.318 -  AS '$libdir/latlon-v0005', 'pgl_ebox_ecircle_may_overlap';
   2.319 -
   2.320 -CREATE OR REPLACE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
   2.321 -  RETURNS boolean
   2.322 -  LANGUAGE C IMMUTABLE STRICT
   2.323 -  AS '$libdir/latlon-v0005', 'pgl_ebox_ecluster_may_overlap';
   2.324 -
   2.325 -CREATE OR REPLACE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   2.326 -  RETURNS boolean
   2.327 -  LANGUAGE C IMMUTABLE STRICT
   2.328 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_overlap';
   2.329 -
   2.330 -CREATE OR REPLACE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   2.331 -  RETURNS boolean
   2.332 -  LANGUAGE C IMMUTABLE STRICT
   2.333 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_ecluster_overlap';
   2.334 -
   2.335 -CREATE OR REPLACE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
   2.336 -  RETURNS boolean
   2.337 -  LANGUAGE C IMMUTABLE STRICT
   2.338 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_ecluster_may_overlap';
   2.339 -
   2.340 -CREATE OR REPLACE FUNCTION ecluster_overlap_proc(ecluster, ecluster)
   2.341 -  RETURNS boolean
   2.342 -  LANGUAGE C IMMUTABLE STRICT
   2.343 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_overlap';
   2.344 -
   2.345 -CREATE OR REPLACE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
   2.346 -  RETURNS boolean
   2.347 -  LANGUAGE C IMMUTABLE STRICT
   2.348 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_may_overlap';
   2.349 -
   2.350 -CREATE OR REPLACE FUNCTION ecluster_contains_proc(ecluster, ecluster)
   2.351 -  RETURNS boolean
   2.352 -  LANGUAGE C IMMUTABLE STRICT
   2.353 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_contains';
   2.354 -
   2.355 -CREATE OR REPLACE FUNCTION epoint_distance_proc(epoint, epoint)
   2.356 -  RETURNS float8
   2.357 -  LANGUAGE C IMMUTABLE STRICT
   2.358 -  AS '$libdir/latlon-v0005', 'pgl_epoint_distance';
   2.359 -
   2.360 -CREATE OR REPLACE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   2.361 -  RETURNS float8
   2.362 -  LANGUAGE C IMMUTABLE STRICT
   2.363 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecircle_distance';
   2.364 -
   2.365 -CREATE OR REPLACE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   2.366 -  RETURNS float8
   2.367 -  LANGUAGE C IMMUTABLE STRICT
   2.368 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecluster_distance';
   2.369 -
   2.370 -CREATE OR REPLACE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   2.371 -  RETURNS float8
   2.372 -  LANGUAGE C IMMUTABLE STRICT
   2.373 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_distance';
   2.374 -
   2.375 -CREATE OR REPLACE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   2.376 -  RETURNS float8
   2.377 -  LANGUAGE C IMMUTABLE STRICT
   2.378 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_ecluster_distance';
   2.379 -
   2.380 -CREATE OR REPLACE FUNCTION ecluster_distance_proc(ecluster, ecluster)
   2.381 -  RETURNS float8
   2.382 -  LANGUAGE C IMMUTABLE STRICT
   2.383 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_distance';
   2.384 -
   2.385 -CREATE FUNCTION ebox_contains_castwrap(ebox, ebox)
   2.386 -  RETURNS boolean
   2.387 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2::ecluster';
   2.388 -
   2.389 -CREATE OPERATOR @> (
   2.390 -  leftarg = ebox,
   2.391 -  rightarg = ebox,
   2.392 -  procedure = ebox_contains_castwrap,
   2.393 -  commutator = <@,
   2.394 -  restrict = areasel,
   2.395 -  join = areajoinsel
   2.396 -);
   2.397 -
   2.398 -CREATE FUNCTION ebox_contains_swapped_castwrap(ebox, ebox)
   2.399 -  RETURNS boolean
   2.400 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1::ecluster';
   2.401 -
   2.402 -CREATE OPERATOR <@ (
   2.403 -  leftarg = ebox,
   2.404 -  rightarg = ebox,
   2.405 -  procedure = ebox_contains_swapped_castwrap,
   2.406 -  commutator = @>,
   2.407 -  restrict = areasel,
   2.408 -  join = areajoinsel
   2.409 -);
   2.410 -
   2.411 -CREATE OR REPLACE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
   2.412 -  RETURNS boolean
   2.413 -  LANGUAGE C STRICT
   2.414 -  AS '$libdir/latlon-v0005', 'pgl_gist_consistent';
   2.415 -
   2.416 -CREATE OR REPLACE FUNCTION pgl_gist_union(internal, internal)
   2.417 -  RETURNS internal
   2.418 -  LANGUAGE C STRICT
   2.419 -  AS '$libdir/latlon-v0005', 'pgl_gist_union';
   2.420 -
   2.421 -CREATE OR REPLACE FUNCTION pgl_gist_compress_epoint(internal)
   2.422 -  RETURNS internal
   2.423 -  LANGUAGE C STRICT
   2.424 -  AS '$libdir/latlon-v0005', 'pgl_gist_compress_epoint';
   2.425 -
   2.426 -CREATE OR REPLACE FUNCTION pgl_gist_compress_ecircle(internal)
   2.427 -  RETURNS internal
   2.428 -  LANGUAGE C STRICT
   2.429 -  AS '$libdir/latlon-v0005', 'pgl_gist_compress_ecircle';
   2.430 -
   2.431 -CREATE OR REPLACE FUNCTION pgl_gist_compress_ecluster(internal)
   2.432 -  RETURNS internal
   2.433 -  LANGUAGE C STRICT
   2.434 -  AS '$libdir/latlon-v0005', 'pgl_gist_compress_ecluster';
   2.435 -
   2.436 -CREATE OR REPLACE FUNCTION pgl_gist_decompress(internal)
   2.437 -  RETURNS internal
   2.438 -  LANGUAGE C STRICT
   2.439 -  AS '$libdir/latlon-v0005', 'pgl_gist_decompress';
   2.440 -
   2.441 -CREATE OR REPLACE FUNCTION pgl_gist_penalty(internal, internal, internal)
   2.442 -  RETURNS internal
   2.443 -  LANGUAGE C STRICT
   2.444 -  AS '$libdir/latlon-v0005', 'pgl_gist_penalty';
   2.445 -
   2.446 -CREATE OR REPLACE FUNCTION pgl_gist_picksplit(internal, internal)
   2.447 -  RETURNS internal
   2.448 -  LANGUAGE C STRICT
   2.449 -  AS '$libdir/latlon-v0005', 'pgl_gist_picksplit';
   2.450 -
   2.451 -CREATE OR REPLACE FUNCTION pgl_gist_same(internal, internal, internal)
   2.452 -  RETURNS internal
   2.453 -  LANGUAGE C STRICT
   2.454 -  AS '$libdir/latlon-v0005', 'pgl_gist_same';
   2.455 -
   2.456 -CREATE OR REPLACE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
   2.457 -  RETURNS internal
   2.458 -  LANGUAGE C STRICT
   2.459 -  AS '$libdir/latlon-v0005', 'pgl_gist_distance';
   2.460 -
     3.1 --- a/latlon--0.5.sql	Thu Sep 22 21:57:03 2016 +0200
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,1647 +0,0 @@
     3.4 -
     3.5 -----------------------------------------
     3.6 --- forward declarations (shell types) --
     3.7 -----------------------------------------
     3.8 -
     3.9 -CREATE TYPE epoint;
    3.10 -CREATE TYPE ebox;
    3.11 -CREATE TYPE ecircle;
    3.12 -CREATE TYPE ecluster;
    3.13 -
    3.14 -
    3.15 -------------------------------------------------------------
    3.16 --- dummy input/output functions for dummy index key types --
    3.17 -------------------------------------------------------------
    3.18 -
    3.19 -CREATE FUNCTION ekey_point_in_dummy(cstring)
    3.20 -  RETURNS ekey_point
    3.21 -  LANGUAGE C IMMUTABLE STRICT
    3.22 -  AS '$libdir/latlon-v0005', 'pgl_notimpl';
    3.23 -
    3.24 -CREATE FUNCTION ekey_point_out_dummy(ekey_point)
    3.25 -  RETURNS cstring
    3.26 -  LANGUAGE C IMMUTABLE STRICT
    3.27 -  AS '$libdir/latlon-v0005', 'pgl_notimpl';
    3.28 -
    3.29 -CREATE FUNCTION ekey_area_in_dummy(cstring)
    3.30 -  RETURNS ekey_area
    3.31 -  LANGUAGE C IMMUTABLE STRICT
    3.32 -  AS '$libdir/latlon-v0005', 'pgl_notimpl';
    3.33 -
    3.34 -CREATE FUNCTION ekey_area_out_dummy(ekey_area)
    3.35 -  RETURNS cstring
    3.36 -  LANGUAGE C IMMUTABLE STRICT
    3.37 -  AS '$libdir/latlon-v0005', 'pgl_notimpl';
    3.38 -
    3.39 -
    3.40 ---------------------------
    3.41 --- text input functions --
    3.42 ---------------------------
    3.43 -
    3.44 -CREATE FUNCTION epoint_in(cstring)
    3.45 -  RETURNS epoint
    3.46 -  LANGUAGE C IMMUTABLE STRICT
    3.47 -  AS '$libdir/latlon-v0005', 'pgl_epoint_in';
    3.48 -
    3.49 -CREATE FUNCTION ebox_in(cstring)
    3.50 -  RETURNS ebox
    3.51 -  LANGUAGE C IMMUTABLE STRICT
    3.52 -  AS '$libdir/latlon-v0005', 'pgl_ebox_in';
    3.53 -
    3.54 -CREATE FUNCTION ecircle_in(cstring)
    3.55 -  RETURNS ecircle
    3.56 -  LANGUAGE C IMMUTABLE STRICT
    3.57 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_in';
    3.58 -
    3.59 -CREATE FUNCTION ecluster_in(cstring)
    3.60 -  RETURNS ecluster
    3.61 -  LANGUAGE C IMMUTABLE STRICT
    3.62 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_in';
    3.63 -
    3.64 -
    3.65 ----------------------------
    3.66 --- text output functions --
    3.67 ----------------------------
    3.68 -
    3.69 -CREATE FUNCTION epoint_out(epoint)
    3.70 -  RETURNS cstring
    3.71 -  LANGUAGE C IMMUTABLE STRICT
    3.72 -  AS '$libdir/latlon-v0005', 'pgl_epoint_out';
    3.73 -
    3.74 -CREATE FUNCTION ebox_out(ebox)
    3.75 -  RETURNS cstring
    3.76 -  LANGUAGE C IMMUTABLE STRICT
    3.77 -  AS '$libdir/latlon-v0005', 'pgl_ebox_out';
    3.78 -
    3.79 -CREATE FUNCTION ecircle_out(ecircle)
    3.80 -  RETURNS cstring
    3.81 -  LANGUAGE C IMMUTABLE STRICT
    3.82 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_out';
    3.83 -
    3.84 -CREATE FUNCTION ecluster_out(ecluster)
    3.85 -  RETURNS cstring
    3.86 -  LANGUAGE C IMMUTABLE STRICT
    3.87 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_out';
    3.88 -
    3.89 -
    3.90 ---------------------------
    3.91 --- binary I/O functions --
    3.92 ---------------------------
    3.93 -
    3.94 -CREATE FUNCTION epoint_recv(internal)
    3.95 -  RETURNS epoint
    3.96 -  LANGUAGE C IMMUTABLE STRICT
    3.97 -  AS '$libdir/latlon-v0005', 'pgl_epoint_recv';
    3.98 -
    3.99 -CREATE FUNCTION ebox_recv(internal)
   3.100 -  RETURNS ebox
   3.101 -  LANGUAGE C IMMUTABLE STRICT
   3.102 -  AS '$libdir/latlon-v0005', 'pgl_ebox_recv';
   3.103 -
   3.104 -CREATE FUNCTION ecircle_recv(internal)
   3.105 -  RETURNS ecircle
   3.106 -  LANGUAGE C IMMUTABLE STRICT
   3.107 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_recv';
   3.108 -
   3.109 -CREATE FUNCTION epoint_send(epoint)
   3.110 -  RETURNS bytea
   3.111 -  LANGUAGE C IMMUTABLE STRICT
   3.112 -  AS '$libdir/latlon-v0005', 'pgl_epoint_send';
   3.113 -
   3.114 -CREATE FUNCTION ebox_send(ebox)
   3.115 -  RETURNS bytea
   3.116 -  LANGUAGE C IMMUTABLE STRICT
   3.117 -  AS '$libdir/latlon-v0005', 'pgl_ebox_send';
   3.118 -
   3.119 -CREATE FUNCTION ecircle_send(ecircle)
   3.120 -  RETURNS bytea
   3.121 -  LANGUAGE C IMMUTABLE STRICT
   3.122 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_send';
   3.123 -
   3.124 -
   3.125 ------------------------------------------------
   3.126 --- type definitions of dummy index key types --
   3.127 ------------------------------------------------
   3.128 -
   3.129 -CREATE TYPE ekey_point (
   3.130 -  internallength = 8,
   3.131 -  input = ekey_point_in_dummy,
   3.132 -  output = ekey_point_out_dummy,
   3.133 -  alignment = char );
   3.134 -
   3.135 -CREATE TYPE ekey_area (
   3.136 -  internallength = 9,
   3.137 -  input = ekey_area_in_dummy,
   3.138 -  output = ekey_area_out_dummy,
   3.139 -  alignment = char );
   3.140 -
   3.141 -
   3.142 -------------------------------------------
   3.143 --- definitions of geographic data types --
   3.144 -------------------------------------------
   3.145 -
   3.146 -CREATE TYPE epoint (
   3.147 -  internallength = 16,
   3.148 -  input = epoint_in,
   3.149 -  output = epoint_out,
   3.150 -  receive = epoint_recv,
   3.151 -  send = epoint_send,
   3.152 -  alignment = double );
   3.153 -
   3.154 -CREATE TYPE ebox (
   3.155 -  internallength = 32,
   3.156 -  input = ebox_in,
   3.157 -  output = ebox_out,
   3.158 -  receive = ebox_recv,
   3.159 -  send = ebox_send,
   3.160 -  alignment = double );
   3.161 -
   3.162 -CREATE TYPE ecircle (
   3.163 -  internallength = 24,
   3.164 -  input = ecircle_in,
   3.165 -  output = ecircle_out,
   3.166 -  receive = ecircle_recv,
   3.167 -  send = ecircle_send,
   3.168 -  alignment = double );
   3.169 -
   3.170 -CREATE TYPE ecluster (
   3.171 -  internallength = VARIABLE,
   3.172 -  input = ecluster_in,
   3.173 -  output = ecluster_out,
   3.174 -  alignment = double,
   3.175 -  storage = external );
   3.176 -
   3.177 -
   3.178 ---------------------
   3.179 --- B-tree support --
   3.180 ---------------------
   3.181 -
   3.182 --- begin of B-tree support for epoint
   3.183 -
   3.184 -CREATE FUNCTION epoint_btree_lt(epoint, epoint)
   3.185 -  RETURNS boolean
   3.186 -  LANGUAGE C IMMUTABLE STRICT
   3.187 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_lt';
   3.188 -
   3.189 -CREATE FUNCTION epoint_btree_le(epoint, epoint)
   3.190 -  RETURNS boolean
   3.191 -  LANGUAGE C IMMUTABLE STRICT
   3.192 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_le';
   3.193 -
   3.194 -CREATE FUNCTION epoint_btree_eq(epoint, epoint)
   3.195 -  RETURNS boolean
   3.196 -  LANGUAGE C IMMUTABLE STRICT
   3.197 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_eq';
   3.198 -
   3.199 -CREATE FUNCTION epoint_btree_ne(epoint, epoint)
   3.200 -  RETURNS boolean
   3.201 -  LANGUAGE C IMMUTABLE STRICT
   3.202 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_ne';
   3.203 -
   3.204 -CREATE FUNCTION epoint_btree_ge(epoint, epoint)
   3.205 -  RETURNS boolean
   3.206 -  LANGUAGE C IMMUTABLE STRICT
   3.207 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_ge';
   3.208 -
   3.209 -CREATE FUNCTION epoint_btree_gt(epoint, epoint)
   3.210 -  RETURNS boolean
   3.211 -  LANGUAGE C IMMUTABLE STRICT
   3.212 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_gt';
   3.213 -
   3.214 -CREATE OPERATOR <<< (
   3.215 -  leftarg = epoint,
   3.216 -  rightarg = epoint,
   3.217 -  procedure = epoint_btree_lt,
   3.218 -  commutator = >>>,
   3.219 -  negator = >>>=,
   3.220 -  restrict = scalarltsel,
   3.221 -  join = scalarltjoinsel
   3.222 -);
   3.223 -
   3.224 -CREATE OPERATOR <<<= (
   3.225 -  leftarg = epoint,
   3.226 -  rightarg = epoint,
   3.227 -  procedure = epoint_btree_le,
   3.228 -  commutator = >>>=,
   3.229 -  negator = >>>,
   3.230 -  restrict = scalarltsel,
   3.231 -  join = scalarltjoinsel
   3.232 -);
   3.233 -
   3.234 -CREATE OPERATOR = (
   3.235 -  leftarg = epoint,
   3.236 -  rightarg = epoint,
   3.237 -  procedure = epoint_btree_eq,
   3.238 -  commutator = =,
   3.239 -  negator = <>,
   3.240 -  restrict = eqsel,
   3.241 -  join = eqjoinsel,
   3.242 -  merges
   3.243 -);
   3.244 -
   3.245 -CREATE OPERATOR <> (
   3.246 -  leftarg = epoint,
   3.247 -  rightarg = epoint,
   3.248 -  procedure = epoint_btree_eq,
   3.249 -  commutator = <>,
   3.250 -  negator = =,
   3.251 -  restrict = neqsel,
   3.252 -  join = neqjoinsel
   3.253 -);
   3.254 -
   3.255 -CREATE OPERATOR >>>= (
   3.256 -  leftarg = epoint,
   3.257 -  rightarg = epoint,
   3.258 -  procedure = epoint_btree_ge,
   3.259 -  commutator = <<<=,
   3.260 -  negator = <<<,
   3.261 -  restrict = scalargtsel,
   3.262 -  join = scalargtjoinsel
   3.263 -);
   3.264 -
   3.265 -CREATE OPERATOR >>> (
   3.266 -  leftarg = epoint,
   3.267 -  rightarg = epoint,
   3.268 -  procedure = epoint_btree_gt,
   3.269 -  commutator = <<<,
   3.270 -  negator = <<<=,
   3.271 -  restrict = scalargtsel,
   3.272 -  join = scalargtjoinsel
   3.273 -);
   3.274 -
   3.275 -CREATE FUNCTION epoint_btree_cmp(epoint, epoint)
   3.276 -  RETURNS int4
   3.277 -  LANGUAGE C IMMUTABLE STRICT
   3.278 -  AS '$libdir/latlon-v0005', 'pgl_btree_epoint_cmp';
   3.279 -
   3.280 -CREATE OPERATOR CLASS epoint_btree_ops
   3.281 -  DEFAULT FOR TYPE epoint USING btree AS
   3.282 -  OPERATOR 1 <<< ,
   3.283 -  OPERATOR 2 <<<= ,
   3.284 -  OPERATOR 3 = ,
   3.285 -  OPERATOR 4 >>>= ,
   3.286 -  OPERATOR 5 >>> ,
   3.287 -  FUNCTION 1 epoint_btree_cmp(epoint, epoint);
   3.288 -
   3.289 --- end of B-tree support for epoint
   3.290 -
   3.291 --- begin of B-tree support for ebox
   3.292 -
   3.293 -CREATE FUNCTION ebox_btree_lt(ebox, ebox)
   3.294 -  RETURNS boolean
   3.295 -  LANGUAGE C IMMUTABLE STRICT
   3.296 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_lt';
   3.297 -
   3.298 -CREATE FUNCTION ebox_btree_le(ebox, ebox)
   3.299 -  RETURNS boolean
   3.300 -  LANGUAGE C IMMUTABLE STRICT
   3.301 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_le';
   3.302 -
   3.303 -CREATE FUNCTION ebox_btree_eq(ebox, ebox)
   3.304 -  RETURNS boolean
   3.305 -  LANGUAGE C IMMUTABLE STRICT
   3.306 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_eq';
   3.307 -
   3.308 -CREATE FUNCTION ebox_btree_ne(ebox, ebox)
   3.309 -  RETURNS boolean
   3.310 -  LANGUAGE C IMMUTABLE STRICT
   3.311 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_ne';
   3.312 -
   3.313 -CREATE FUNCTION ebox_btree_ge(ebox, ebox)
   3.314 -  RETURNS boolean
   3.315 -  LANGUAGE C IMMUTABLE STRICT
   3.316 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_ge';
   3.317 -
   3.318 -CREATE FUNCTION ebox_btree_gt(ebox, ebox)
   3.319 -  RETURNS boolean
   3.320 -  LANGUAGE C IMMUTABLE STRICT
   3.321 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_gt';
   3.322 -
   3.323 -CREATE OPERATOR <<< (
   3.324 -  leftarg = ebox,
   3.325 -  rightarg = ebox,
   3.326 -  procedure = ebox_btree_lt,
   3.327 -  commutator = >>>,
   3.328 -  negator = >>>=,
   3.329 -  restrict = scalarltsel,
   3.330 -  join = scalarltjoinsel
   3.331 -);
   3.332 -
   3.333 -CREATE OPERATOR <<<= (
   3.334 -  leftarg = ebox,
   3.335 -  rightarg = ebox,
   3.336 -  procedure = ebox_btree_le,
   3.337 -  commutator = >>>=,
   3.338 -  negator = >>>,
   3.339 -  restrict = scalarltsel,
   3.340 -  join = scalarltjoinsel
   3.341 -);
   3.342 -
   3.343 -CREATE OPERATOR = (
   3.344 -  leftarg = ebox,
   3.345 -  rightarg = ebox,
   3.346 -  procedure = ebox_btree_eq,
   3.347 -  commutator = =,
   3.348 -  negator = <>,
   3.349 -  restrict = eqsel,
   3.350 -  join = eqjoinsel,
   3.351 -  merges
   3.352 -);
   3.353 -
   3.354 -CREATE OPERATOR <> (
   3.355 -  leftarg = ebox,
   3.356 -  rightarg = ebox,
   3.357 -  procedure = ebox_btree_eq,
   3.358 -  commutator = <>,
   3.359 -  negator = =,
   3.360 -  restrict = neqsel,
   3.361 -  join = neqjoinsel
   3.362 -);
   3.363 -
   3.364 -CREATE OPERATOR >>>= (
   3.365 -  leftarg = ebox,
   3.366 -  rightarg = ebox,
   3.367 -  procedure = ebox_btree_ge,
   3.368 -  commutator = <<<=,
   3.369 -  negator = <<<,
   3.370 -  restrict = scalargtsel,
   3.371 -  join = scalargtjoinsel
   3.372 -);
   3.373 -
   3.374 -CREATE OPERATOR >>> (
   3.375 -  leftarg = ebox,
   3.376 -  rightarg = ebox,
   3.377 -  procedure = ebox_btree_gt,
   3.378 -  commutator = <<<,
   3.379 -  negator = <<<=,
   3.380 -  restrict = scalargtsel,
   3.381 -  join = scalargtjoinsel
   3.382 -);
   3.383 -
   3.384 -CREATE FUNCTION ebox_btree_cmp(ebox, ebox)
   3.385 -  RETURNS int4
   3.386 -  LANGUAGE C IMMUTABLE STRICT
   3.387 -  AS '$libdir/latlon-v0005', 'pgl_btree_ebox_cmp';
   3.388 -
   3.389 -CREATE OPERATOR CLASS ebox_btree_ops
   3.390 -  DEFAULT FOR TYPE ebox USING btree AS
   3.391 -  OPERATOR 1 <<< ,
   3.392 -  OPERATOR 2 <<<= ,
   3.393 -  OPERATOR 3 = ,
   3.394 -  OPERATOR 4 >>>= ,
   3.395 -  OPERATOR 5 >>> ,
   3.396 -  FUNCTION 1 ebox_btree_cmp(ebox, ebox);
   3.397 -
   3.398 --- end of B-tree support for ebox
   3.399 -
   3.400 --- begin of B-tree support for ecircle
   3.401 -
   3.402 -CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle)
   3.403 -  RETURNS boolean
   3.404 -  LANGUAGE C IMMUTABLE STRICT
   3.405 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_lt';
   3.406 -
   3.407 -CREATE FUNCTION ecircle_btree_le(ecircle, ecircle)
   3.408 -  RETURNS boolean
   3.409 -  LANGUAGE C IMMUTABLE STRICT
   3.410 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_le';
   3.411 -
   3.412 -CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle)
   3.413 -  RETURNS boolean
   3.414 -  LANGUAGE C IMMUTABLE STRICT
   3.415 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_eq';
   3.416 -
   3.417 -CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle)
   3.418 -  RETURNS boolean
   3.419 -  LANGUAGE C IMMUTABLE STRICT
   3.420 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_ne';
   3.421 -
   3.422 -CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle)
   3.423 -  RETURNS boolean
   3.424 -  LANGUAGE C IMMUTABLE STRICT
   3.425 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_ge';
   3.426 -
   3.427 -CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle)
   3.428 -  RETURNS boolean
   3.429 -  LANGUAGE C IMMUTABLE STRICT
   3.430 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_gt';
   3.431 -
   3.432 -CREATE OPERATOR <<< (
   3.433 -  leftarg = ecircle,
   3.434 -  rightarg = ecircle,
   3.435 -  procedure = ecircle_btree_lt,
   3.436 -  commutator = >>>,
   3.437 -  negator = >>>=,
   3.438 -  restrict = scalarltsel,
   3.439 -  join = scalarltjoinsel
   3.440 -);
   3.441 -
   3.442 -CREATE OPERATOR <<<= (
   3.443 -  leftarg = ecircle,
   3.444 -  rightarg = ecircle,
   3.445 -  procedure = ecircle_btree_le,
   3.446 -  commutator = >>>=,
   3.447 -  negator = >>>,
   3.448 -  restrict = scalarltsel,
   3.449 -  join = scalarltjoinsel
   3.450 -);
   3.451 -
   3.452 -CREATE OPERATOR = (
   3.453 -  leftarg = ecircle,
   3.454 -  rightarg = ecircle,
   3.455 -  procedure = ecircle_btree_eq,
   3.456 -  commutator = =,
   3.457 -  negator = <>,
   3.458 -  restrict = eqsel,
   3.459 -  join = eqjoinsel,
   3.460 -  merges
   3.461 -);
   3.462 -
   3.463 -CREATE OPERATOR <> (
   3.464 -  leftarg = ecircle,
   3.465 -  rightarg = ecircle,
   3.466 -  procedure = ecircle_btree_eq,
   3.467 -  commutator = <>,
   3.468 -  negator = =,
   3.469 -  restrict = neqsel,
   3.470 -  join = neqjoinsel
   3.471 -);
   3.472 -
   3.473 -CREATE OPERATOR >>>= (
   3.474 -  leftarg = ecircle,
   3.475 -  rightarg = ecircle,
   3.476 -  procedure = ecircle_btree_ge,
   3.477 -  commutator = <<<=,
   3.478 -  negator = <<<,
   3.479 -  restrict = scalargtsel,
   3.480 -  join = scalargtjoinsel
   3.481 -);
   3.482 -
   3.483 -CREATE OPERATOR >>> (
   3.484 -  leftarg = ecircle,
   3.485 -  rightarg = ecircle,
   3.486 -  procedure = ecircle_btree_gt,
   3.487 -  commutator = <<<,
   3.488 -  negator = <<<=,
   3.489 -  restrict = scalargtsel,
   3.490 -  join = scalargtjoinsel
   3.491 -);
   3.492 -
   3.493 -CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
   3.494 -  RETURNS int4
   3.495 -  LANGUAGE C IMMUTABLE STRICT
   3.496 -  AS '$libdir/latlon-v0005', 'pgl_btree_ecircle_cmp';
   3.497 -
   3.498 -CREATE OPERATOR CLASS ecircle_btree_ops
   3.499 -  DEFAULT FOR TYPE ecircle USING btree AS
   3.500 -  OPERATOR 1 <<< ,
   3.501 -  OPERATOR 2 <<<= ,
   3.502 -  OPERATOR 3 = ,
   3.503 -  OPERATOR 4 >>>= ,
   3.504 -  OPERATOR 5 >>> ,
   3.505 -  FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle);
   3.506 -
   3.507 --- end of B-tree support for ecircle
   3.508 -
   3.509 -
   3.510 -----------------
   3.511 --- type casts --
   3.512 -----------------
   3.513 -
   3.514 -CREATE FUNCTION cast_epoint_to_ebox(epoint)
   3.515 -  RETURNS ebox
   3.516 -  LANGUAGE C IMMUTABLE STRICT
   3.517 -  AS '$libdir/latlon-v0005', 'pgl_epoint_to_ebox';
   3.518 -
   3.519 -CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint);
   3.520 -
   3.521 -CREATE FUNCTION cast_epoint_to_ecircle(epoint)
   3.522 -  RETURNS ecircle
   3.523 -  LANGUAGE C IMMUTABLE STRICT
   3.524 -  AS '$libdir/latlon-v0005', 'pgl_epoint_to_ecircle';
   3.525 -
   3.526 -CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint);
   3.527 -
   3.528 -CREATE FUNCTION cast_epoint_to_ecluster(epoint)
   3.529 -  RETURNS ecluster
   3.530 -  LANGUAGE C IMMUTABLE STRICT
   3.531 -  AS '$libdir/latlon-v0005', 'pgl_epoint_to_ecluster';
   3.532 -
   3.533 -CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint);
   3.534 -
   3.535 -CREATE FUNCTION cast_ebox_to_ecluster(ebox)
   3.536 -  RETURNS ecluster
   3.537 -  LANGUAGE C IMMUTABLE STRICT
   3.538 -  AS '$libdir/latlon-v0005', 'pgl_ebox_to_ecluster';
   3.539 -
   3.540 -CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox);
   3.541 -
   3.542 -
   3.543 ----------------------------
   3.544 --- constructor functions --
   3.545 ----------------------------
   3.546 -
   3.547 -CREATE FUNCTION epoint(float8, float8)
   3.548 -  RETURNS epoint
   3.549 -  LANGUAGE C IMMUTABLE STRICT
   3.550 -  AS '$libdir/latlon-v0005', 'pgl_create_epoint';
   3.551 -
   3.552 -CREATE FUNCTION epoint_latlon(float8, float8)
   3.553 -  RETURNS epoint
   3.554 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   3.555 -    SELECT epoint($1, $2)
   3.556 -  $$;
   3.557 -
   3.558 -CREATE FUNCTION epoint_lonlat(float8, float8)
   3.559 -  RETURNS epoint
   3.560 -  LANGUAGE SQL IMMUTABLE STRICT AS $$
   3.561 -    SELECT epoint($2, $1)
   3.562 -  $$;
   3.563 -
   3.564 -CREATE FUNCTION empty_ebox()
   3.565 -  RETURNS ebox
   3.566 -  LANGUAGE C IMMUTABLE STRICT
   3.567 -  AS '$libdir/latlon-v0005', 'pgl_create_empty_ebox';
   3.568 -
   3.569 -CREATE FUNCTION ebox(float8, float8, float8, float8)
   3.570 -  RETURNS ebox
   3.571 -  LANGUAGE C IMMUTABLE STRICT
   3.572 -  AS '$libdir/latlon-v0005', 'pgl_create_ebox';
   3.573 -
   3.574 -CREATE FUNCTION ebox(epoint, epoint)
   3.575 -  RETURNS ebox
   3.576 -  LANGUAGE C IMMUTABLE STRICT
   3.577 -  AS '$libdir/latlon-v0005', 'pgl_create_ebox_from_epoints';
   3.578 -
   3.579 -CREATE FUNCTION ecircle(float8, float8, float8)
   3.580 -  RETURNS ecircle
   3.581 -  LANGUAGE C IMMUTABLE STRICT
   3.582 -  AS '$libdir/latlon-v0005', 'pgl_create_ecircle';
   3.583 -
   3.584 -CREATE FUNCTION ecircle(epoint, float8)
   3.585 -  RETURNS ecircle
   3.586 -  LANGUAGE C IMMUTABLE STRICT
   3.587 -  AS '$libdir/latlon-v0005', 'pgl_create_ecircle_from_epoint';
   3.588 -
   3.589 -CREATE FUNCTION ecluster_concat(ecluster[])
   3.590 -  RETURNS ecluster
   3.591 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.592 -    SELECT array_to_string($1, ' ')::ecluster
   3.593 -  $$;
   3.594 -
   3.595 -CREATE FUNCTION ecluster_concat(ecluster, ecluster)
   3.596 -  RETURNS ecluster
   3.597 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.598 -    SELECT ($1::text || ' ' || $2::text)::ecluster
   3.599 -  $$;
   3.600 -
   3.601 -CREATE FUNCTION ecluster_create_multipoint(epoint[])
   3.602 -  RETURNS ecluster
   3.603 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.604 -    SELECT
   3.605 -      array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster
   3.606 -    FROM unnest($1)
   3.607 -  $$;
   3.608 -
   3.609 -CREATE FUNCTION ecluster_create_path(epoint[])
   3.610 -  RETURNS ecluster
   3.611 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.612 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   3.613 -      ('path (' || array_to_string($1, ' ') || ')')::ecluster
   3.614 -    END
   3.615 -    FROM array_to_string($1, ' ') AS "str"
   3.616 -  $$;
   3.617 -
   3.618 -CREATE FUNCTION ecluster_create_outline(epoint[])
   3.619 -  RETURNS ecluster
   3.620 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.621 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   3.622 -      ('outline (' || array_to_string($1, ' ') || ')')::ecluster
   3.623 -    END
   3.624 -    FROM array_to_string($1, ' ') AS "str"
   3.625 -  $$;
   3.626 -
   3.627 -CREATE FUNCTION ecluster_create_polygon(epoint[])
   3.628 -  RETURNS ecluster
   3.629 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.630 -    SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE
   3.631 -      ('polygon (' || array_to_string($1, ' ') || ')')::ecluster
   3.632 -    END
   3.633 -    FROM array_to_string($1, ' ') AS "str"
   3.634 -  $$;
   3.635 -
   3.636 -
   3.637 -----------------------
   3.638 --- getter functions --
   3.639 -----------------------
   3.640 -
   3.641 -CREATE FUNCTION latitude(epoint)
   3.642 -  RETURNS float8
   3.643 -  LANGUAGE C IMMUTABLE STRICT
   3.644 -  AS '$libdir/latlon-v0005', 'pgl_epoint_lat';
   3.645 -
   3.646 -CREATE FUNCTION longitude(epoint)
   3.647 -  RETURNS float8
   3.648 -  LANGUAGE C IMMUTABLE STRICT
   3.649 -  AS '$libdir/latlon-v0005', 'pgl_epoint_lon';
   3.650 -
   3.651 -CREATE FUNCTION min_latitude(ebox)
   3.652 -  RETURNS float8
   3.653 -  LANGUAGE C IMMUTABLE STRICT
   3.654 -  AS '$libdir/latlon-v0005', 'pgl_ebox_lat_min';
   3.655 -
   3.656 -CREATE FUNCTION max_latitude(ebox)
   3.657 -  RETURNS float8
   3.658 -  LANGUAGE C IMMUTABLE STRICT
   3.659 -  AS '$libdir/latlon-v0005', 'pgl_ebox_lat_max';
   3.660 -
   3.661 -CREATE FUNCTION min_longitude(ebox)
   3.662 -  RETURNS float8
   3.663 -  LANGUAGE C IMMUTABLE STRICT
   3.664 -  AS '$libdir/latlon-v0005', 'pgl_ebox_lon_min';
   3.665 -
   3.666 -CREATE FUNCTION max_longitude(ebox)
   3.667 -  RETURNS float8
   3.668 -  LANGUAGE C IMMUTABLE STRICT
   3.669 -  AS '$libdir/latlon-v0005', 'pgl_ebox_lon_max';
   3.670 -
   3.671 -CREATE FUNCTION center(ecircle)
   3.672 -  RETURNS epoint
   3.673 -  LANGUAGE C IMMUTABLE STRICT
   3.674 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_center';
   3.675 -
   3.676 -CREATE FUNCTION radius(ecircle)
   3.677 -  RETURNS float8
   3.678 -  LANGUAGE C IMMUTABLE STRICT
   3.679 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_radius';
   3.680 -
   3.681 -CREATE FUNCTION ecluster_extract_points(ecluster)
   3.682 -  RETURNS SETOF epoint
   3.683 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.684 -    SELECT "match"[2]::epoint
   3.685 -    FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match"
   3.686 -  $$;
   3.687 -
   3.688 -CREATE FUNCTION ecluster_extract_paths(ecluster)
   3.689 -  RETURNS SETOF epoint[]
   3.690 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.691 -    SELECT (
   3.692 -      SELECT array_agg("m2"[1]::epoint)
   3.693 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   3.694 -    )
   3.695 -    FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1"
   3.696 -  $$;
   3.697 -
   3.698 -CREATE FUNCTION ecluster_extract_outlines(ecluster)
   3.699 -  RETURNS SETOF epoint[]
   3.700 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.701 -    SELECT (
   3.702 -      SELECT array_agg("m2"[1]::epoint)
   3.703 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   3.704 -    )
   3.705 -    FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1"
   3.706 -  $$;
   3.707 -
   3.708 -CREATE FUNCTION ecluster_extract_polygons(ecluster)
   3.709 -  RETURNS SETOF epoint[]
   3.710 -  LANGUAGE sql IMMUTABLE STRICT AS $$
   3.711 -    SELECT (
   3.712 -      SELECT array_agg("m2"[1]::epoint)
   3.713 -      FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
   3.714 -    )
   3.715 -    FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1"
   3.716 -  $$;
   3.717 -
   3.718 -
   3.719 ----------------
   3.720 --- operators --
   3.721 ----------------
   3.722 -
   3.723 -CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
   3.724 -  RETURNS boolean
   3.725 -  LANGUAGE C IMMUTABLE STRICT
   3.726 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ebox_overlap';
   3.727 -
   3.728 -CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
   3.729 -  RETURNS boolean
   3.730 -  LANGUAGE C IMMUTABLE STRICT
   3.731 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecircle_overlap';
   3.732 -
   3.733 -CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
   3.734 -  RETURNS boolean
   3.735 -  LANGUAGE C IMMUTABLE STRICT
   3.736 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecluster_overlap';
   3.737 -
   3.738 -CREATE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
   3.739 -  RETURNS boolean
   3.740 -  LANGUAGE C IMMUTABLE STRICT
   3.741 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecluster_may_overlap';
   3.742 -
   3.743 -CREATE FUNCTION ebox_overlap_proc(ebox, ebox)
   3.744 -  RETURNS boolean
   3.745 -  LANGUAGE C IMMUTABLE STRICT
   3.746 -  AS '$libdir/latlon-v0005', 'pgl_ebox_overlap';
   3.747 -
   3.748 -CREATE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
   3.749 -  RETURNS boolean
   3.750 -  LANGUAGE C IMMUTABLE STRICT
   3.751 -  AS '$libdir/latlon-v0005', 'pgl_ebox_ecircle_may_overlap';
   3.752 -
   3.753 -CREATE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
   3.754 -  RETURNS boolean
   3.755 -  LANGUAGE C IMMUTABLE STRICT
   3.756 -  AS '$libdir/latlon-v0005', 'pgl_ebox_ecluster_may_overlap';
   3.757 -
   3.758 -CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
   3.759 -  RETURNS boolean
   3.760 -  LANGUAGE C IMMUTABLE STRICT
   3.761 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_overlap';
   3.762 -
   3.763 -CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
   3.764 -  RETURNS boolean
   3.765 -  LANGUAGE C IMMUTABLE STRICT
   3.766 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_ecluster_overlap';
   3.767 -
   3.768 -CREATE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
   3.769 -  RETURNS boolean
   3.770 -  LANGUAGE C IMMUTABLE STRICT
   3.771 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_ecluster_may_overlap';
   3.772 -
   3.773 -CREATE FUNCTION ecluster_overlap_proc(ecluster, ecluster)
   3.774 -  RETURNS boolean
   3.775 -  LANGUAGE C IMMUTABLE STRICT
   3.776 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_overlap';
   3.777 -
   3.778 -CREATE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
   3.779 -  RETURNS boolean
   3.780 -  LANGUAGE C IMMUTABLE STRICT
   3.781 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_may_overlap';
   3.782 -
   3.783 -CREATE FUNCTION ecluster_contains_proc(ecluster, ecluster)
   3.784 -  RETURNS boolean
   3.785 -  LANGUAGE C IMMUTABLE STRICT
   3.786 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_contains';
   3.787 -
   3.788 -CREATE FUNCTION epoint_distance_proc(epoint, epoint)
   3.789 -  RETURNS float8
   3.790 -  LANGUAGE C IMMUTABLE STRICT
   3.791 -  AS '$libdir/latlon-v0005', 'pgl_epoint_distance';
   3.792 -
   3.793 -CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
   3.794 -  RETURNS float8
   3.795 -  LANGUAGE C IMMUTABLE STRICT
   3.796 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecircle_distance';
   3.797 -
   3.798 -CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
   3.799 -  RETURNS float8
   3.800 -  LANGUAGE C IMMUTABLE STRICT
   3.801 -  AS '$libdir/latlon-v0005', 'pgl_epoint_ecluster_distance';
   3.802 -
   3.803 -CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle)
   3.804 -  RETURNS float8
   3.805 -  LANGUAGE C IMMUTABLE STRICT
   3.806 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_distance';
   3.807 -
   3.808 -CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
   3.809 -  RETURNS float8
   3.810 -  LANGUAGE C IMMUTABLE STRICT
   3.811 -  AS '$libdir/latlon-v0005', 'pgl_ecircle_ecluster_distance';
   3.812 -
   3.813 -CREATE FUNCTION ecluster_distance_proc(ecluster, ecluster)
   3.814 -  RETURNS float8
   3.815 -  LANGUAGE C IMMUTABLE STRICT
   3.816 -  AS '$libdir/latlon-v0005', 'pgl_ecluster_distance';
   3.817 -
   3.818 -CREATE OPERATOR && (
   3.819 -  leftarg = epoint,
   3.820 -  rightarg = ebox,
   3.821 -  procedure = epoint_ebox_overlap_proc,
   3.822 -  commutator = &&,
   3.823 -  restrict = areasel,
   3.824 -  join = areajoinsel
   3.825 -);
   3.826 -
   3.827 -CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint)
   3.828 -  RETURNS boolean
   3.829 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   3.830 -
   3.831 -CREATE OPERATOR && (
   3.832 -  leftarg = ebox,
   3.833 -  rightarg = epoint,
   3.834 -  procedure = epoint_ebox_overlap_commutator,
   3.835 -  commutator = &&,
   3.836 -  restrict = areasel,
   3.837 -  join = areajoinsel
   3.838 -);
   3.839 -
   3.840 -CREATE OPERATOR && (
   3.841 -  leftarg = epoint,
   3.842 -  rightarg = ecircle,
   3.843 -  procedure = epoint_ecircle_overlap_proc,
   3.844 -  commutator = &&,
   3.845 -  restrict = areasel,
   3.846 -  join = areajoinsel
   3.847 -);
   3.848 -
   3.849 -CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint)
   3.850 -  RETURNS boolean
   3.851 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   3.852 -
   3.853 -CREATE OPERATOR && (
   3.854 -  leftarg = ecircle,
   3.855 -  rightarg = epoint,
   3.856 -  procedure = epoint_ecircle_overlap_commutator,
   3.857 -  commutator = &&,
   3.858 -  restrict = areasel,
   3.859 -  join = areajoinsel
   3.860 -);
   3.861 -
   3.862 -CREATE OPERATOR && (
   3.863 -  leftarg = epoint,
   3.864 -  rightarg = ecluster,
   3.865 -  procedure = epoint_ecluster_overlap_proc,
   3.866 -  commutator = &&,
   3.867 -  restrict = areasel,
   3.868 -  join = areajoinsel
   3.869 -);
   3.870 -
   3.871 -CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint)
   3.872 -  RETURNS boolean
   3.873 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   3.874 -
   3.875 -CREATE OPERATOR && (
   3.876 -  leftarg = ecluster,
   3.877 -  rightarg = epoint,
   3.878 -  procedure = epoint_ecluster_overlap_commutator,
   3.879 -  commutator = &&,
   3.880 -  restrict = areasel,
   3.881 -  join = areajoinsel
   3.882 -);
   3.883 -
   3.884 -CREATE OPERATOR && (
   3.885 -  leftarg = ebox,
   3.886 -  rightarg = ebox,
   3.887 -  procedure = ebox_overlap_proc,
   3.888 -  commutator = &&,
   3.889 -  restrict = areasel,
   3.890 -  join = areajoinsel
   3.891 -);
   3.892 -
   3.893 -CREATE OPERATOR && (
   3.894 -  leftarg = ecircle,
   3.895 -  rightarg = ecircle,
   3.896 -  procedure = ecircle_overlap_proc,
   3.897 -  commutator = &&,
   3.898 -  restrict = areasel,
   3.899 -  join = areajoinsel
   3.900 -);
   3.901 -
   3.902 -CREATE OPERATOR && (
   3.903 -  leftarg = ecircle,
   3.904 -  rightarg = ecluster,
   3.905 -  procedure = ecircle_ecluster_overlap_proc,
   3.906 -  commutator = &&,
   3.907 -  restrict = areasel,
   3.908 -  join = areajoinsel
   3.909 -);
   3.910 -
   3.911 -CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle)
   3.912 -  RETURNS boolean
   3.913 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1';
   3.914 -
   3.915 -CREATE OPERATOR && (
   3.916 -  leftarg = ecluster,
   3.917 -  rightarg = ecircle,
   3.918 -  procedure = ecircle_ecluster_overlap_commutator,
   3.919 -  commutator = &&,
   3.920 -  restrict = areasel,
   3.921 -  join = areajoinsel
   3.922 -);
   3.923 -
   3.924 -CREATE OPERATOR && (
   3.925 -  leftarg = ecluster,
   3.926 -  rightarg = ecluster,
   3.927 -  procedure = ecluster_overlap_proc,
   3.928 -  commutator = &&,
   3.929 -  restrict = areasel,
   3.930 -  join = areajoinsel
   3.931 -);
   3.932 -
   3.933 -CREATE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle)
   3.934 -  RETURNS boolean
   3.935 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   3.936 -
   3.937 -CREATE OPERATOR && (
   3.938 -  leftarg = ebox,
   3.939 -  rightarg = ecircle,
   3.940 -  procedure = ebox_ecircle_overlap_castwrap,
   3.941 -  commutator = &&,
   3.942 -  restrict = areasel,
   3.943 -  join = areajoinsel
   3.944 -);
   3.945 -
   3.946 -CREATE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox)
   3.947 -  RETURNS boolean
   3.948 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
   3.949 -
   3.950 -CREATE OPERATOR && (
   3.951 -  leftarg = ecircle,
   3.952 -  rightarg = ebox,
   3.953 -  procedure = ebox_ecircle_overlap_castwrap,
   3.954 -  commutator = &&,
   3.955 -  restrict = areasel,
   3.956 -  join = areajoinsel
   3.957 -);
   3.958 -
   3.959 -CREATE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster)
   3.960 -  RETURNS boolean
   3.961 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2';
   3.962 -
   3.963 -CREATE OPERATOR && (
   3.964 -  leftarg = ebox,
   3.965 -  rightarg = ecluster,
   3.966 -  procedure = ebox_ecluster_overlap_castwrap,
   3.967 -  commutator = &&,
   3.968 -  restrict = areasel,
   3.969 -  join = areajoinsel
   3.970 -);
   3.971 -
   3.972 -CREATE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox)
   3.973 -  RETURNS boolean
   3.974 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster';
   3.975 -
   3.976 -CREATE OPERATOR && (
   3.977 -  leftarg = ecluster,
   3.978 -  rightarg = ebox,
   3.979 -  procedure = ebox_ecluster_overlap_castwrap,
   3.980 -  commutator = &&,
   3.981 -  restrict = areasel,
   3.982 -  join = areajoinsel
   3.983 -);
   3.984 -
   3.985 -CREATE OPERATOR &&+ (
   3.986 -  leftarg = epoint,
   3.987 -  rightarg = ecluster,
   3.988 -  procedure = epoint_ecluster_may_overlap_proc,
   3.989 -  commutator = &&+,
   3.990 -  restrict = areasel,
   3.991 -  join = areajoinsel
   3.992 -);
   3.993 -
   3.994 -CREATE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint)
   3.995 -  RETURNS boolean
   3.996 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
   3.997 -
   3.998 -CREATE OPERATOR &&+ (
   3.999 -  leftarg = ecluster,
  3.1000 -  rightarg = epoint,
  3.1001 -  procedure = epoint_ecluster_may_overlap_commutator,
  3.1002 -  commutator = &&+,
  3.1003 -  restrict = areasel,
  3.1004 -  join = areajoinsel
  3.1005 -);
  3.1006 -
  3.1007 -CREATE OPERATOR &&+ (
  3.1008 -  leftarg = ebox,
  3.1009 -  rightarg = ecircle,
  3.1010 -  procedure = ebox_ecircle_may_overlap_proc,
  3.1011 -  commutator = &&+,
  3.1012 -  restrict = areasel,
  3.1013 -  join = areajoinsel
  3.1014 -);
  3.1015 -
  3.1016 -CREATE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox)
  3.1017 -  RETURNS boolean
  3.1018 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  3.1019 -
  3.1020 -CREATE OPERATOR &&+ (
  3.1021 -  leftarg = ecircle,
  3.1022 -  rightarg = ebox,
  3.1023 -  procedure = ebox_ecircle_may_overlap_commutator,
  3.1024 -  commutator = &&+,
  3.1025 -  restrict = areasel,
  3.1026 -  join = areajoinsel
  3.1027 -);
  3.1028 -
  3.1029 -CREATE OPERATOR &&+ (
  3.1030 -  leftarg = ebox,
  3.1031 -  rightarg = ecluster,
  3.1032 -  procedure = ebox_ecluster_may_overlap_proc,
  3.1033 -  commutator = &&+,
  3.1034 -  restrict = areasel,
  3.1035 -  join = areajoinsel
  3.1036 -);
  3.1037 -
  3.1038 -CREATE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox)
  3.1039 -  RETURNS boolean
  3.1040 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  3.1041 -
  3.1042 -CREATE OPERATOR &&+ (
  3.1043 -  leftarg = ecluster,
  3.1044 -  rightarg = ebox,
  3.1045 -  procedure = ebox_ecluster_may_overlap_commutator,
  3.1046 -  commutator = &&+,
  3.1047 -  restrict = areasel,
  3.1048 -  join = areajoinsel
  3.1049 -);
  3.1050 -
  3.1051 -CREATE OPERATOR &&+ (
  3.1052 -  leftarg = ecircle,
  3.1053 -  rightarg = ecluster,
  3.1054 -  procedure = ecircle_ecluster_may_overlap_proc,
  3.1055 -  commutator = &&+,
  3.1056 -  restrict = areasel,
  3.1057 -  join = areajoinsel
  3.1058 -);
  3.1059 -
  3.1060 -CREATE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle)
  3.1061 -  RETURNS boolean
  3.1062 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1';
  3.1063 -
  3.1064 -CREATE OPERATOR &&+ (
  3.1065 -  leftarg = ecluster,
  3.1066 -  rightarg = ecircle,
  3.1067 -  procedure = ecircle_ecluster_may_overlap_commutator,
  3.1068 -  commutator = &&+,
  3.1069 -  restrict = areasel,
  3.1070 -  join = areajoinsel
  3.1071 -);
  3.1072 -
  3.1073 -CREATE OPERATOR &&+ (
  3.1074 -  leftarg = ecluster,
  3.1075 -  rightarg = ecluster,
  3.1076 -  procedure = ecluster_may_overlap_proc,
  3.1077 -  commutator = &&+,
  3.1078 -  restrict = areasel,
  3.1079 -  join = areajoinsel
  3.1080 -);
  3.1081 -
  3.1082 -CREATE OPERATOR @> (
  3.1083 -  leftarg = ebox,
  3.1084 -  rightarg = epoint,
  3.1085 -  procedure = epoint_ebox_overlap_commutator,
  3.1086 -  commutator = <@,
  3.1087 -  restrict = areasel,
  3.1088 -  join = areajoinsel
  3.1089 -);
  3.1090 -
  3.1091 -CREATE OPERATOR <@ (
  3.1092 -  leftarg = epoint,
  3.1093 -  rightarg = ebox,
  3.1094 -  procedure = epoint_ebox_overlap_proc,
  3.1095 -  commutator = @>,
  3.1096 -  restrict = areasel,
  3.1097 -  join = areajoinsel
  3.1098 -);
  3.1099 -
  3.1100 -CREATE OPERATOR @> (
  3.1101 -  leftarg = ecluster,
  3.1102 -  rightarg = epoint,
  3.1103 -  procedure = epoint_ecluster_overlap_commutator,
  3.1104 -  commutator = <@,
  3.1105 -  restrict = areasel,
  3.1106 -  join = areajoinsel
  3.1107 -);
  3.1108 -
  3.1109 -CREATE OPERATOR <@ (
  3.1110 -  leftarg = epoint,
  3.1111 -  rightarg = ecluster,
  3.1112 -  procedure = epoint_ecluster_overlap_proc,
  3.1113 -  commutator = <@,
  3.1114 -  restrict = areasel,
  3.1115 -  join = areajoinsel
  3.1116 -);
  3.1117 -
  3.1118 -CREATE OPERATOR @> (
  3.1119 -  leftarg = ecluster,
  3.1120 -  rightarg = ecluster,
  3.1121 -  procedure = ecluster_contains_proc,
  3.1122 -  commutator = <@,
  3.1123 -  restrict = areasel,
  3.1124 -  join = areajoinsel
  3.1125 -);
  3.1126 -
  3.1127 -CREATE FUNCTION ecluster_contains_commutator(ecluster, ecluster)
  3.1128 -  RETURNS boolean
  3.1129 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1';
  3.1130 -
  3.1131 -CREATE OPERATOR <@ (
  3.1132 -  leftarg = ecluster,
  3.1133 -  rightarg = ecluster,
  3.1134 -  procedure = ecluster_contains_commutator,
  3.1135 -  commutator = @>,
  3.1136 -  restrict = areasel,
  3.1137 -  join = areajoinsel
  3.1138 -);
  3.1139 -
  3.1140 -CREATE FUNCTION ebox_contains_castwrap(ebox, ebox)
  3.1141 -  RETURNS boolean
  3.1142 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2::ecluster';
  3.1143 -
  3.1144 -CREATE OPERATOR @> (
  3.1145 -  leftarg = ebox,
  3.1146 -  rightarg = ebox,
  3.1147 -  procedure = ebox_contains_castwrap,
  3.1148 -  commutator = <@,
  3.1149 -  restrict = areasel,
  3.1150 -  join = areajoinsel
  3.1151 -);
  3.1152 -
  3.1153 -CREATE FUNCTION ebox_contains_swapped_castwrap(ebox, ebox)
  3.1154 -  RETURNS boolean
  3.1155 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1::ecluster';
  3.1156 -
  3.1157 -CREATE OPERATOR <@ (
  3.1158 -  leftarg = ebox,
  3.1159 -  rightarg = ebox,
  3.1160 -  procedure = ebox_contains_swapped_castwrap,
  3.1161 -  commutator = @>,
  3.1162 -  restrict = areasel,
  3.1163 -  join = areajoinsel
  3.1164 -);
  3.1165 -
  3.1166 -CREATE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster)
  3.1167 -  RETURNS boolean
  3.1168 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2';
  3.1169 -
  3.1170 -CREATE OPERATOR @> (
  3.1171 -  leftarg = ebox,
  3.1172 -  rightarg = ecluster,
  3.1173 -  procedure = ebox_ecluster_contains_castwrap,
  3.1174 -  commutator = <@,
  3.1175 -  restrict = areasel,
  3.1176 -  join = areajoinsel
  3.1177 -);
  3.1178 -
  3.1179 -CREATE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox)
  3.1180 -  RETURNS boolean
  3.1181 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1';
  3.1182 -
  3.1183 -CREATE OPERATOR <@ (
  3.1184 -  leftarg = ecluster,
  3.1185 -  rightarg = ebox,
  3.1186 -  procedure = ebox_ecluster_contains_castwrap,
  3.1187 -  commutator = @>,
  3.1188 -  restrict = areasel,
  3.1189 -  join = areajoinsel
  3.1190 -);
  3.1191 -
  3.1192 -CREATE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox)
  3.1193 -  RETURNS boolean
  3.1194 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 @> $2::ecluster';
  3.1195 -
  3.1196 -CREATE OPERATOR @> (
  3.1197 -  leftarg = ecluster,
  3.1198 -  rightarg = ebox,
  3.1199 -  procedure = ecluster_ebox_contains_castwrap,
  3.1200 -  commutator = <@,
  3.1201 -  restrict = areasel,
  3.1202 -  join = areajoinsel
  3.1203 -);
  3.1204 -
  3.1205 -CREATE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster)
  3.1206 -  RETURNS boolean
  3.1207 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1::ecluster';
  3.1208 -
  3.1209 -CREATE OPERATOR <@ (
  3.1210 -  leftarg = ebox,
  3.1211 -  rightarg = ecluster,
  3.1212 -  procedure = ecluster_ebox_contains_castwrap,
  3.1213 -  commutator = @>,
  3.1214 -  restrict = areasel,
  3.1215 -  join = areajoinsel
  3.1216 -);
  3.1217 -
  3.1218 -CREATE OPERATOR <-> (
  3.1219 -  leftarg = epoint,
  3.1220 -  rightarg = epoint,
  3.1221 -  procedure = epoint_distance_proc,
  3.1222 -  commutator = <->
  3.1223 -);
  3.1224 -
  3.1225 -CREATE OPERATOR <-> (
  3.1226 -  leftarg = epoint,
  3.1227 -  rightarg = ecircle,
  3.1228 -  procedure = epoint_ecircle_distance_proc,
  3.1229 -  commutator = <->
  3.1230 -);
  3.1231 -
  3.1232 -CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint)
  3.1233 -  RETURNS float8
  3.1234 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  3.1235 -
  3.1236 -CREATE OPERATOR <-> (
  3.1237 -  leftarg = ecircle,
  3.1238 -  rightarg = epoint,
  3.1239 -  procedure = epoint_ecircle_distance_commutator,
  3.1240 -  commutator = <->
  3.1241 -);
  3.1242 -
  3.1243 -CREATE OPERATOR <-> (
  3.1244 -  leftarg = epoint,
  3.1245 -  rightarg = ecluster,
  3.1246 -  procedure = epoint_ecluster_distance_proc,
  3.1247 -  commutator = <->
  3.1248 -);
  3.1249 -
  3.1250 -CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint)
  3.1251 -  RETURNS float8
  3.1252 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  3.1253 -
  3.1254 -CREATE OPERATOR <-> (
  3.1255 -  leftarg = ecluster,
  3.1256 -  rightarg = epoint,
  3.1257 -  procedure = epoint_ecluster_distance_commutator,
  3.1258 -  commutator = <->
  3.1259 -);
  3.1260 -
  3.1261 -CREATE OPERATOR <-> (
  3.1262 -  leftarg = ecircle,
  3.1263 -  rightarg = ecircle,
  3.1264 -  procedure = ecircle_distance_proc,
  3.1265 -  commutator = <->
  3.1266 -);
  3.1267 -
  3.1268 -CREATE OPERATOR <-> (
  3.1269 -  leftarg = ecircle,
  3.1270 -  rightarg = ecluster,
  3.1271 -  procedure = ecircle_ecluster_distance_proc,
  3.1272 -  commutator = <->
  3.1273 -);
  3.1274 -
  3.1275 -CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle)
  3.1276 -  RETURNS float8
  3.1277 -  LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1';
  3.1278 -
  3.1279 -CREATE OPERATOR <-> (
  3.1280 -  leftarg = ecluster,
  3.1281 -  rightarg = ecircle,
  3.1282 -  procedure = ecircle_ecluster_distance_commutator,
  3.1283 -  commutator = <->
  3.1284 -);
  3.1285 -
  3.1286 -CREATE OPERATOR <-> (
  3.1287 -  leftarg = ecluster,
  3.1288 -  rightarg = ecluster,
  3.1289 -  procedure = ecluster_distance_proc,
  3.1290 -  commutator = <->
  3.1291 -);
  3.1292 -
  3.1293 -CREATE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox)
  3.1294 -  RETURNS float8
  3.1295 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  3.1296 -
  3.1297 -CREATE OPERATOR <-> (
  3.1298 -  leftarg = epoint,
  3.1299 -  rightarg = ebox,
  3.1300 -  procedure = epoint_ebox_distance_castwrap,
  3.1301 -  commutator = <->
  3.1302 -);
  3.1303 -
  3.1304 -CREATE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint)
  3.1305 -  RETURNS float8
  3.1306 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  3.1307 -
  3.1308 -CREATE OPERATOR <-> (
  3.1309 -  leftarg = ebox,
  3.1310 -  rightarg = epoint,
  3.1311 -  procedure = epoint_ebox_distance_castwrap,
  3.1312 -  commutator = <->
  3.1313 -);
  3.1314 -
  3.1315 -CREATE FUNCTION ebox_distance_castwrap(ebox, ebox)
  3.1316 -  RETURNS float8
  3.1317 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2::ecluster';
  3.1318 -
  3.1319 -CREATE OPERATOR <-> (
  3.1320 -  leftarg = ebox,
  3.1321 -  rightarg = ebox,
  3.1322 -  procedure = ebox_distance_castwrap,
  3.1323 -  commutator = <->
  3.1324 -);
  3.1325 -
  3.1326 -CREATE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle)
  3.1327 -  RETURNS float8
  3.1328 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  3.1329 -
  3.1330 -CREATE OPERATOR <-> (
  3.1331 -  leftarg = ebox,
  3.1332 -  rightarg = ecircle,
  3.1333 -  procedure = ebox_ecircle_distance_castwrap,
  3.1334 -  commutator = <->
  3.1335 -);
  3.1336 -
  3.1337 -CREATE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox)
  3.1338 -  RETURNS float8
  3.1339 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  3.1340 -
  3.1341 -CREATE OPERATOR <-> (
  3.1342 -  leftarg = ecircle,
  3.1343 -  rightarg = ebox,
  3.1344 -  procedure = ebox_ecircle_distance_castwrap,
  3.1345 -  commutator = <->
  3.1346 -);
  3.1347 -
  3.1348 -CREATE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster)
  3.1349 -  RETURNS float8
  3.1350 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2';
  3.1351 -
  3.1352 -CREATE OPERATOR <-> (
  3.1353 -  leftarg = ebox,
  3.1354 -  rightarg = ecluster,
  3.1355 -  procedure = ebox_ecluster_distance_castwrap,
  3.1356 -  commutator = <->
  3.1357 -);
  3.1358 -
  3.1359 -CREATE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox)
  3.1360 -  RETURNS float8
  3.1361 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster';
  3.1362 -
  3.1363 -CREATE OPERATOR <-> (
  3.1364 -  leftarg = ecluster,
  3.1365 -  rightarg = ebox,
  3.1366 -  procedure = ebox_ecluster_distance_castwrap,
  3.1367 -  commutator = <->
  3.1368 -);
  3.1369 -
  3.1370 -
  3.1371 -----------------
  3.1372 --- GiST index --
  3.1373 -----------------
  3.1374 -
  3.1375 -CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
  3.1376 -  RETURNS boolean
  3.1377 -  LANGUAGE C STRICT
  3.1378 -  AS '$libdir/latlon-v0005', 'pgl_gist_consistent';
  3.1379 -
  3.1380 -CREATE FUNCTION pgl_gist_union(internal, internal)
  3.1381 -  RETURNS internal
  3.1382 -  LANGUAGE C STRICT
  3.1383 -  AS '$libdir/latlon-v0005', 'pgl_gist_union';
  3.1384 -
  3.1385 -CREATE FUNCTION pgl_gist_compress_epoint(internal)
  3.1386 -  RETURNS internal
  3.1387 -  LANGUAGE C STRICT
  3.1388 -  AS '$libdir/latlon-v0005', 'pgl_gist_compress_epoint';
  3.1389 -
  3.1390 -CREATE FUNCTION pgl_gist_compress_ecircle(internal)
  3.1391 -  RETURNS internal
  3.1392 -  LANGUAGE C STRICT
  3.1393 -  AS '$libdir/latlon-v0005', 'pgl_gist_compress_ecircle';
  3.1394 -
  3.1395 -CREATE FUNCTION pgl_gist_compress_ecluster(internal)
  3.1396 -  RETURNS internal
  3.1397 -  LANGUAGE C STRICT
  3.1398 -  AS '$libdir/latlon-v0005', 'pgl_gist_compress_ecluster';
  3.1399 -
  3.1400 -CREATE FUNCTION pgl_gist_decompress(internal)
  3.1401 -  RETURNS internal
  3.1402 -  LANGUAGE C STRICT
  3.1403 -  AS '$libdir/latlon-v0005', 'pgl_gist_decompress';
  3.1404 -
  3.1405 -CREATE FUNCTION pgl_gist_penalty(internal, internal, internal)
  3.1406 -  RETURNS internal
  3.1407 -  LANGUAGE C STRICT
  3.1408 -  AS '$libdir/latlon-v0005', 'pgl_gist_penalty';
  3.1409 -
  3.1410 -CREATE FUNCTION pgl_gist_picksplit(internal, internal)
  3.1411 -  RETURNS internal
  3.1412 -  LANGUAGE C STRICT
  3.1413 -  AS '$libdir/latlon-v0005', 'pgl_gist_picksplit';
  3.1414 -
  3.1415 -CREATE FUNCTION pgl_gist_same(internal, internal, internal)
  3.1416 -  RETURNS internal
  3.1417 -  LANGUAGE C STRICT
  3.1418 -  AS '$libdir/latlon-v0005', 'pgl_gist_same';
  3.1419 -
  3.1420 -CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
  3.1421 -  RETURNS internal
  3.1422 -  LANGUAGE C STRICT
  3.1423 -  AS '$libdir/latlon-v0005', 'pgl_gist_distance';
  3.1424 -
  3.1425 -CREATE OPERATOR CLASS epoint_ops
  3.1426 -  DEFAULT FOR TYPE epoint USING gist AS
  3.1427 -  OPERATOR  11 = ,
  3.1428 -  OPERATOR  22 &&  (epoint, ebox),
  3.1429 -  OPERATOR 222 <@  (epoint, ebox),
  3.1430 -  OPERATOR  23 &&  (epoint, ecircle),
  3.1431 -  OPERATOR  24 &&  (epoint, ecluster),
  3.1432 -  OPERATOR 124 &&+ (epoint, ecluster),
  3.1433 -  OPERATOR 224 <@  (epoint, ecluster),
  3.1434 -  OPERATOR  31 <-> (epoint, epoint) FOR ORDER BY float_ops,
  3.1435 -  OPERATOR  32 <-> (epoint, ebox) FOR ORDER BY float_ops,
  3.1436 -  OPERATOR  33 <-> (epoint, ecircle) FOR ORDER BY float_ops,
  3.1437 -  OPERATOR  34 <-> (epoint, ecluster) FOR ORDER BY float_ops,
  3.1438 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  3.1439 -  FUNCTION 2 pgl_gist_union(internal, internal),
  3.1440 -  FUNCTION 3 pgl_gist_compress_epoint(internal),
  3.1441 -  FUNCTION 4 pgl_gist_decompress(internal),
  3.1442 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  3.1443 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  3.1444 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  3.1445 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  3.1446 -  STORAGE ekey_point;
  3.1447 -
  3.1448 -CREATE OPERATOR CLASS ecircle_ops
  3.1449 -  DEFAULT FOR TYPE ecircle USING gist AS
  3.1450 -  OPERATOR  13 = ,
  3.1451 -  OPERATOR  21 &&  (ecircle, epoint),
  3.1452 -  OPERATOR  22 &&  (ecircle, ebox),
  3.1453 -  OPERATOR 122 &&+ (ecircle, ebox),
  3.1454 -  OPERATOR  23 &&  (ecircle, ecircle),
  3.1455 -  OPERATOR  24 &&  (ecircle, ecluster),
  3.1456 -  OPERATOR 124 &&+ (ecircle, ecluster),
  3.1457 -  OPERATOR  31 <-> (ecircle, epoint) FOR ORDER BY float_ops,
  3.1458 -  OPERATOR  32 <-> (ecircle, ebox) FOR ORDER BY float_ops,
  3.1459 -  OPERATOR  33 <-> (ecircle, ecircle) FOR ORDER BY float_ops,
  3.1460 -  OPERATOR  34 <-> (ecircle, ecluster) FOR ORDER BY float_ops,
  3.1461 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  3.1462 -  FUNCTION 2 pgl_gist_union(internal, internal),
  3.1463 -  FUNCTION 3 pgl_gist_compress_ecircle(internal),
  3.1464 -  FUNCTION 4 pgl_gist_decompress(internal),
  3.1465 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  3.1466 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  3.1467 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  3.1468 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  3.1469 -  STORAGE ekey_area;
  3.1470 -
  3.1471 -CREATE OPERATOR CLASS ecluster_ops
  3.1472 -  DEFAULT FOR TYPE ecluster USING gist AS
  3.1473 -  OPERATOR  21 &&  (ecluster, epoint),
  3.1474 -  OPERATOR 121 &&+ (ecluster, epoint),
  3.1475 -  OPERATOR 221 @>  (ecluster, epoint),
  3.1476 -  OPERATOR  22 &&  (ecluster, ebox),
  3.1477 -  OPERATOR 122 &&+ (ecluster, ebox),
  3.1478 -  OPERATOR 222 @>  (ecluster, ebox),
  3.1479 -  OPERATOR 322 <@  (ecluster, ebox),
  3.1480 -  OPERATOR  23 &&  (ecluster, ecircle),
  3.1481 -  OPERATOR 123 &&+ (ecluster, ecircle),
  3.1482 -  OPERATOR  24 &&  (ecluster, ecluster),
  3.1483 -  OPERATOR 124 &&+ (ecluster, ecluster),
  3.1484 -  OPERATOR 224 @>  (ecluster, ecluster),
  3.1485 -  OPERATOR 324 <@  (ecluster, ecluster),
  3.1486 -  OPERATOR  31 <-> (ecluster, epoint) FOR ORDER BY float_ops,
  3.1487 -  OPERATOR  32 <-> (ecluster, ebox) FOR ORDER BY float_ops,
  3.1488 -  OPERATOR  33 <-> (ecluster, ecircle) FOR ORDER BY float_ops,
  3.1489 -  OPERATOR  34 <-> (ecluster, ecluster) FOR ORDER BY float_ops,
  3.1490 -  FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal),
  3.1491 -  FUNCTION 2 pgl_gist_union(internal, internal),
  3.1492 -  FUNCTION 3 pgl_gist_compress_ecluster(internal),
  3.1493 -  FUNCTION 4 pgl_gist_decompress(internal),
  3.1494 -  FUNCTION 5 pgl_gist_penalty(internal, internal, internal),
  3.1495 -  FUNCTION 6 pgl_gist_picksplit(internal, internal),
  3.1496 -  FUNCTION 7 pgl_gist_same(internal, internal, internal),
  3.1497 -  FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid),
  3.1498 -  STORAGE ekey_area;
  3.1499 -
  3.1500 -
  3.1501 ----------------------
  3.1502 --- alias functions --
  3.1503 ----------------------
  3.1504 -
  3.1505 -CREATE FUNCTION distance(epoint, epoint)
  3.1506 -  RETURNS float8
  3.1507 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  3.1508 -
  3.1509 -CREATE FUNCTION distance(ecluster, epoint)
  3.1510 -  RETURNS float8
  3.1511 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2';
  3.1512 -
  3.1513 -CREATE FUNCTION distance_within(epoint, epoint, float8)
  3.1514 -  RETURNS boolean
  3.1515 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  3.1516 -
  3.1517 -CREATE FUNCTION distance_within(ecluster, epoint, float8)
  3.1518 -  RETURNS boolean
  3.1519 -  LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)';
  3.1520 -
  3.1521 -
  3.1522 ---------------------------------
  3.1523 --- other data storage formats --
  3.1524 ---------------------------------
  3.1525 -
  3.1526 -CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint_lonlat')
  3.1527 -  RETURNS epoint
  3.1528 -  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
  3.1529 -    DECLARE
  3.1530 -      "result" epoint;
  3.1531 -    BEGIN
  3.1532 -      IF $3 = 'epoint_lonlat' THEN
  3.1533 -        -- avoid dynamic command execution for better performance
  3.1534 -        RETURN epoint($2, $1);
  3.1535 -      END IF;
  3.1536 -      IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN
  3.1537 -        -- avoid dynamic command execution for better performance
  3.1538 -        RETURN epoint($1, $2);
  3.1539 -      END IF;
  3.1540 -      EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2;
  3.1541 -      RETURN "result";
  3.1542 -    END;
  3.1543 -  $$;
  3.1544 -
  3.1545 -CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat')
  3.1546 -  RETURNS epoint
  3.1547 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  3.1548 -    SELECT CASE
  3.1549 -    WHEN $1->>'type' = 'Point' THEN
  3.1550 -      coords_to_epoint(
  3.1551 -        ($1->'coordinates'->>1)::float8,
  3.1552 -        ($1->'coordinates'->>0)::float8,
  3.1553 -        $2
  3.1554 -      )
  3.1555 -    WHEN $1->>'type' = 'Feature' THEN
  3.1556 -      GeoJSON_to_epoint($1->'geometry', $2)
  3.1557 -    ELSE
  3.1558 -      NULL
  3.1559 -    END
  3.1560 -  $$;
  3.1561 -
  3.1562 -CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat')
  3.1563 -  RETURNS ecluster
  3.1564 -  LANGUAGE sql IMMUTABLE STRICT AS $$
  3.1565 -    SELECT CASE $1->>'type'
  3.1566 -    WHEN 'Point' THEN
  3.1567 -      coords_to_epoint(
  3.1568 -        ($1->'coordinates'->>1)::float8,
  3.1569 -        ($1->'coordinates'->>0)::float8,
  3.1570 -        $2
  3.1571 -      )::ecluster
  3.1572 -    WHEN 'MultiPoint' THEN
  3.1573 -      ( SELECT ecluster_create_multipoint(array_agg(
  3.1574 -          coords_to_epoint(
  3.1575 -            ("coord"->>1)::float8,
  3.1576 -            ("coord"->>0)::float8,
  3.1577 -            $2
  3.1578 -          )
  3.1579 -        ))
  3.1580 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  3.1581 -      )
  3.1582 -    WHEN 'LineString' THEN
  3.1583 -      ( SELECT ecluster_create_path(array_agg(
  3.1584 -          coords_to_epoint(
  3.1585 -            ("coord"->>1)::float8,
  3.1586 -            ("coord"->>0)::float8,
  3.1587 -            $2
  3.1588 -          )
  3.1589 -        ))
  3.1590 -        FROM jsonb_array_elements($1->'coordinates') AS "coord"
  3.1591 -      )
  3.1592 -    WHEN 'MultiLineString' THEN
  3.1593 -      ( SELECT ecluster_concat(array_agg(
  3.1594 -          ( SELECT ecluster_create_path(array_agg(
  3.1595 -              coords_to_epoint(
  3.1596 -                ("coord"->>1)::float8,
  3.1597 -                ("coord"->>0)::float8,
  3.1598 -                $2
  3.1599 -              )
  3.1600 -            ))
  3.1601 -            FROM jsonb_array_elements("coord_array") AS "coord"
  3.1602 -          )
  3.1603 -        ))
  3.1604 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  3.1605 -      )
  3.1606 -    WHEN 'Polygon' THEN
  3.1607 -      ( SELECT ecluster_concat(array_agg(
  3.1608 -          ( SELECT ecluster_create_polygon(array_agg(
  3.1609 -              coords_to_epoint(
  3.1610 -                ("coord"->>1)::float8,
  3.1611 -                ("coord"->>0)::float8,
  3.1612 -                $2
  3.1613 -              )
  3.1614 -            ))
  3.1615 -            FROM jsonb_array_elements("coord_array") AS "coord"
  3.1616 -          )
  3.1617 -        ))
  3.1618 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array"
  3.1619 -      )
  3.1620 -    WHEN 'MultiPolygon' THEN
  3.1621 -      ( SELECT ecluster_concat(array_agg(
  3.1622 -          ( SELECT ecluster_concat(array_agg(
  3.1623 -              ( SELECT ecluster_create_polygon(array_agg(
  3.1624 -                  coords_to_epoint(
  3.1625 -                    ("coord"->>1)::float8,
  3.1626 -                    ("coord"->>0)::float8,
  3.1627 -                    $2
  3.1628 -                  )
  3.1629 -                ))
  3.1630 -                FROM jsonb_array_elements("coord_array") AS "coord"
  3.1631 -              )
  3.1632 -            ))
  3.1633 -            FROM jsonb_array_elements("coord_array_array") AS "coord_array"
  3.1634 -          )
  3.1635 -        ))
  3.1636 -        FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array"
  3.1637 -      )
  3.1638 -    WHEN 'Feature' THEN
  3.1639 -      GeoJSON_to_ecluster($1->'geometry', $2)
  3.1640 -    WHEN 'FeatureCollection' THEN
  3.1641 -      ( SELECT ecluster_concat(array_agg(
  3.1642 -          GeoJSON_to_ecluster("feature", $2)
  3.1643 -        ))
  3.1644 -        FROM jsonb_array_elements($1->'features') AS "feature"
  3.1645 -      )
  3.1646 -    ELSE
  3.1647 -      NULL
  3.1648 -    END
  3.1649 -  $$;
  3.1650 -
     4.1 --- a/latlon-v0005.c	Thu Sep 22 21:57:03 2016 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,3068 +0,0 @@
     4.4 -
     4.5 -/*-------------*
     4.6 - *  C prelude  *
     4.7 - *-------------*/
     4.8 -
     4.9 -#include "postgres.h"
    4.10 -#include "fmgr.h"
    4.11 -#include "libpq/pqformat.h"
    4.12 -#include "access/gist.h"
    4.13 -#include "access/stratnum.h"
    4.14 -#include "utils/array.h"
    4.15 -#include <math.h>
    4.16 -
    4.17 -#ifdef PG_MODULE_MAGIC
    4.18 -PG_MODULE_MAGIC;
    4.19 -#endif
    4.20 -
    4.21 -#if INT_MAX < 2147483647
    4.22 -#error Expected int type to be at least 32 bit wide
    4.23 -#endif
    4.24 -
    4.25 -
    4.26 -/*---------------------------------*
    4.27 - *  distance calculation on earth  *
    4.28 - *  (using WGS-84 spheroid)        *
    4.29 - *---------------------------------*/
    4.30 -
    4.31 -/*  WGS-84 spheroid with following parameters:
    4.32 -    semi-major axis  a = 6378137
    4.33 -    semi-minor axis  b = a * (1 - 1/298.257223563)
    4.34 -    estimated diameter = 2 * (2*a+b)/3
    4.35 -*/
    4.36 -#define PGL_SPHEROID_A 6378137.0            /* semi major axis */
    4.37 -#define PGL_SPHEROID_F (1.0/298.257223563)  /* flattening */
    4.38 -#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F))
    4.39 -#define PGL_EPS2       ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \
    4.40 -                           PGL_SPHEROID_B * PGL_SPHEROID_B ) / \
    4.41 -                         ( PGL_SPHEROID_A * PGL_SPHEROID_A ) )
    4.42 -#define PGL_SUBEPS2    (1.0-PGL_EPS2)
    4.43 -#define PGL_DIAMETER   ((4.0*PGL_SPHEROID_A + 2.0*PGL_SPHEROID_B) / 3.0)
    4.44 -#define PGL_SCALE      (PGL_SPHEROID_A / PGL_DIAMETER)  /* semi-major ref. */
    4.45 -#define PGL_FADELIMIT  (PGL_DIAMETER * M_PI / 6.0)      /* 1/6 circumference */
    4.46 -#define PGL_MAXDIST    (PGL_DIAMETER * M_PI / 2.0)      /* maximum distance */
    4.47 -
    4.48 -/* calculate distance between two points on earth (given in degrees) */
    4.49 -static inline double pgl_distance(
    4.50 -  double lat1, double lon1, double lat2, double lon2
    4.51 -) {
    4.52 -  float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin;
    4.53 -  float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t;
    4.54 -  /* normalize delta longitude (lon2 > 0 && lon1 = 0) */
    4.55 -  /* lon1 = 0 (not used anymore) */
    4.56 -  lon2 = fabs(lon2-lon1);
    4.57 -  /* convert to radians (first divide, then multiply) */
    4.58 -  lat1 = (lat1 / 180.0) * M_PI;
    4.59 -  lat2 = (lat2 / 180.0) * M_PI;
    4.60 -  lon2 = (lon2 / 180.0) * M_PI;
    4.61 -  /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point
    4.62 -     operations (lon2 >= lon1 is already ensured in a previous step) */
    4.63 -  if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; }
    4.64 -  /* calculate 3d coordinates on scaled ellipsoid which has an average diameter
    4.65 -     of 1.0 */
    4.66 -  lat1cos = cos(lat1); lat1sin = sin(lat1);
    4.67 -  lat2cos = cos(lat2); lat2sin = sin(lat2);
    4.68 -  lon2cos = cos(lon2); lon2sin = sin(lon2);
    4.69 -  nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin);
    4.70 -  nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
    4.71 -  x1 = nphi1 * lat1cos;
    4.72 -  z1 = nphi1 * PGL_SUBEPS2 * lat1sin;
    4.73 -  x2 = nphi2 * lat2cos * lon2cos;
    4.74 -  y2 = nphi2 * lat2cos * lon2sin;
    4.75 -  z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
    4.76 -  /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */
    4.77 -  g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
    4.78 -  /* convert tunnel distance through scaled ellipsoid to approximated surface
    4.79 -     distance on original ellipsoid */
    4.80 -  if (g > 1.0) g = 1.0;
    4.81 -  s = PGL_DIAMETER * asin(g);
    4.82 -  /* return result only if small enough to be precise (less than 1/3 of
    4.83 -     maximum possible distance) */
    4.84 -  if (s <= PGL_FADELIMIT) return s;
    4.85 -  /* calculate tunnel distance to antipodal point through scaled ellipsoid */
    4.86 -  g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1));
    4.87 -  /* convert tunnel distance to antipodal point through scaled ellipsoid to
    4.88 -     approximated surface distance to antipodal point on original ellipsoid */
    4.89 -  if (g > 1.0) g = 1.0;
    4.90 -  t = PGL_DIAMETER * asin(g);
    4.91 -  /* surface distance between original points can now be approximated by
    4.92 -     substracting antipodal distance from maximum possible distance;
    4.93 -     return result only if small enough (less than 1/3 of maximum possible
    4.94 -     distance) */
    4.95 -  if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t;
    4.96 -  /* otherwise crossfade direct and antipodal result to ensure monotonicity */
    4.97 -  return (
    4.98 -    (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) /
    4.99 -    (s + t - 2*PGL_FADELIMIT)
   4.100 -  );
   4.101 -}
   4.102 -
   4.103 -/* finite distance that can not be reached on earth */
   4.104 -#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST)
   4.105 -
   4.106 -
   4.107 -/*--------------------------------*
   4.108 - *  simple geographic data types  *
   4.109 - *--------------------------------*/
   4.110 -
   4.111 -/* point on earth given by latitude and longitude in degrees */
   4.112 -/* (type "epoint" in SQL) */
   4.113 -typedef struct {
   4.114 -  double lat;  /* between  -90 and  90 (both inclusive) */
   4.115 -  double lon;  /* between -180 and 180 (both inclusive) */
   4.116 -} pgl_point;
   4.117 -
   4.118 -/* box delimited by two parallels and two meridians (all in degrees) */
   4.119 -/* (type "ebox" in SQL) */
   4.120 -typedef struct {
   4.121 -  double lat_min;  /* between  -90 and  90 (both inclusive) */
   4.122 -  double lat_max;  /* between  -90 and  90 (both inclusive) */
   4.123 -  double lon_min;  /* between -180 and 180 (both inclusive) */
   4.124 -  double lon_max;  /* between -180 and 180 (both inclusive) */
   4.125 -  /* if lat_min > lat_max, then box is empty */
   4.126 -  /* if lon_min > lon_max, then 180th meridian is crossed */
   4.127 -} pgl_box;
   4.128 -
   4.129 -/* circle on earth surface (for radial searches with fixed radius) */
   4.130 -/* (type "ecircle" in SQL) */
   4.131 -typedef struct {
   4.132 -  pgl_point center;
   4.133 -  double radius; /* positive (including +0 but excluding -0), or -INFINITY */
   4.134 -  /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point),
   4.135 -     zero radius (0) denotes a single point,
   4.136 -     a finite radius (0 < radius < INFINITY) denotes a filled circle, and
   4.137 -     a radius of INFINITY is valid and means complete coverage of earth. */
   4.138 -} pgl_circle;
   4.139 -
   4.140 -
   4.141 -/*----------------------------------*
   4.142 - *  geographic "cluster" data type  *
   4.143 - *----------------------------------*/
   4.144 -
   4.145 -/* A cluster is a collection of points, paths, outlines, and polygons. If two
   4.146 -   polygons in a cluster overlap, the area covered by both polygons does not
   4.147 -   belong to the cluster. This way, a cluster can be used to describe complex
   4.148 -   shapes like polygons with holes. Outlines are non-filled polygons. Paths are
   4.149 -   open by default (i.e. the last point in the list is not connected with the
   4.150 -   first point in the list). Note that each outline or polygon in a cluster
   4.151 -   must cover a longitude range of less than 180 degrees to avoid ambiguities.
   4.152 -   Areas which are larger may be split into multiple polygons. */
   4.153 -
   4.154 -/* maximum number of points in a cluster */
   4.155 -/* (limited to avoid integer overflows, e.g. when allocating memory) */
   4.156 -#define PGL_CLUSTER_MAXPOINTS 16777216
   4.157 -
   4.158 -/* types of cluster entries */
   4.159 -#define PGL_ENTRY_POINT   1  /* a point */
   4.160 -#define PGL_ENTRY_PATH    2  /* a path from first point to last point */
   4.161 -#define PGL_ENTRY_OUTLINE 3  /* a non-filled polygon with given vertices */
   4.162 -#define PGL_ENTRY_POLYGON 4  /* a filled polygon with given vertices */
   4.163 -
   4.164 -/* Entries of a cluster are described by two different structs: pgl_newentry
   4.165 -   and pgl_entry. The first is used only during construction of a cluster, the
   4.166 -   second is used in all other cases (e.g. when reading clusters from the
   4.167 -   database, performing operations, etc). */
   4.168 -
   4.169 -/* entry for new geographic cluster during construction of that cluster */
   4.170 -typedef struct {
   4.171 -  int32_t entrytype;
   4.172 -  int32_t npoints;
   4.173 -  pgl_point *points;  /* pointer to an array of points (pgl_point) */
   4.174 -} pgl_newentry;
   4.175 -
   4.176 -/* entry of geographic cluster */
   4.177 -typedef struct {
   4.178 -  int32_t entrytype;  /* type of entry: point, path, outline, polygon */
   4.179 -  int32_t npoints;    /* number of stored points (set to 1 for point entry) */
   4.180 -  int32_t offset;     /* offset of pgl_point array from cluster base address */
   4.181 -  /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */
   4.182 -} pgl_entry;
   4.183 -
   4.184 -/* geographic cluster which is a collection of points, (open) paths, polygons,
   4.185 -   and outlines (non-filled polygons) */
   4.186 -typedef struct {
   4.187 -  char header[VARHDRSZ];  /* PostgreSQL header for variable size data types */
   4.188 -  int32_t nentries;       /* number of stored points */
   4.189 -  pgl_circle bounding;    /* bounding circle */
   4.190 -  /* Note: bounding circle ensures alignment of pgl_cluster for points */
   4.191 -  pgl_entry entries[FLEXIBLE_ARRAY_MEMBER];  /* var-length data */
   4.192 -} pgl_cluster;
   4.193 -
   4.194 -/* macro to determine memory alignment of points */
   4.195 -/* (needed to store pgl_point array after entries in pgl_cluster) */
   4.196 -typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment;
   4.197 -#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned)
   4.198 -
   4.199 -/* macro to extract a pointer to the array of points of a cluster entry */
   4.200 -#define PGL_ENTRY_POINTS(cluster, idx) \
   4.201 -  ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset))
   4.202 -
   4.203 -/* convert pgl_newentry array to pgl_cluster */
   4.204 -static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) {
   4.205 -  int i;              /* index of current entry */
   4.206 -  int npoints = 0;    /* number of points in whole cluster */
   4.207 -  int entry_npoints;  /* number of points in current entry */
   4.208 -  int points_offset = PGL_POINT_ALIGNMENT * (
   4.209 -    ( offsetof(pgl_cluster, entries) +
   4.210 -      nentries * sizeof(pgl_entry) +
   4.211 -      PGL_POINT_ALIGNMENT - 1
   4.212 -    ) / PGL_POINT_ALIGNMENT
   4.213 -  );  /* offset of pgl_point array from base address (considering alignment) */
   4.214 -  pgl_cluster *cluster;  /* new cluster to be returned */
   4.215 -  /* determine total number of points */
   4.216 -  for (i=0; i<nentries; i++) npoints += entries[i].npoints;
   4.217 -  /* allocate memory for cluster (including entries and points) */
   4.218 -  cluster = palloc(points_offset + npoints * sizeof(pgl_point));
   4.219 -  /* re-count total number of points to determine offset for each entry */
   4.220 -  npoints = 0;
   4.221 -  /* copy entries and points */
   4.222 -  for (i=0; i<nentries; i++) {
   4.223 -    /* determine number of points in entry */
   4.224 -    entry_npoints = entries[i].npoints;
   4.225 -    /* copy entry */
   4.226 -    cluster->entries[i].entrytype = entries[i].entrytype;
   4.227 -    cluster->entries[i].npoints = entry_npoints;
   4.228 -    /* calculate offset (in bytes) of pgl_point array */
   4.229 -    cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point);
   4.230 -    /* copy points */
   4.231 -    memcpy(
   4.232 -      PGL_ENTRY_POINTS(cluster, i),
   4.233 -      entries[i].points,
   4.234 -      entry_npoints * sizeof(pgl_point)
   4.235 -    );
   4.236 -    /* update total number of points processed */
   4.237 -    npoints += entry_npoints;
   4.238 -  }
   4.239 -  /* set number of entries in cluster */
   4.240 -  cluster->nentries = nentries;
   4.241 -  /* set PostgreSQL header for variable sized data */
   4.242 -  SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point));
   4.243 -  /* return newly created cluster */
   4.244 -  return cluster;
   4.245 -}
   4.246 -
   4.247 -
   4.248 -/*----------------------------------------*
   4.249 - *  C functions on geographic data types  *
   4.250 - *----------------------------------------*/
   4.251 -
   4.252 -/* round latitude or longitude to 12 digits after decimal point */
   4.253 -static inline double pgl_round(double val) {
   4.254 -  return round(val * 1e12) / 1e12;
   4.255 -}
   4.256 -
   4.257 -/* compare two points */
   4.258 -/* (equality when same point on earth is described, otherwise an arbitrary
   4.259 -   linear order) */
   4.260 -static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) {
   4.261 -  double lon1, lon2;  /* modified longitudes for special cases */
   4.262 -  /* use latitude as first ordering criterion */
   4.263 -  if (point1->lat < point2->lat) return -1;
   4.264 -  if (point1->lat > point2->lat) return 1;
   4.265 -  /* determine modified longitudes (considering special case of poles and
   4.266 -     180th meridian which can be described as W180 or E180) */
   4.267 -  if (point1->lat == -90 || point1->lat == 90) lon1 = 0;
   4.268 -  else if (point1->lon == 180) lon1 = -180;
   4.269 -  else lon1 = point1->lon;
   4.270 -  if (point2->lat == -90 || point2->lat == 90) lon2 = 0;
   4.271 -  else if (point2->lon == 180) lon2 = -180;
   4.272 -  else lon2 = point2->lon;
   4.273 -  /* use (modified) longitude as secondary ordering criterion */
   4.274 -  if (lon1 < lon2) return -1;
   4.275 -  if (lon1 > lon2) return 1;
   4.276 -  /* no difference found, points are equal */
   4.277 -  return 0;
   4.278 -}
   4.279 -
   4.280 -/* compare two boxes */
   4.281 -/* (equality when same box on earth is described, otherwise an arbitrary linear
   4.282 -   order) */
   4.283 -static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) {
   4.284 -  /* two empty boxes are equal, and an empty box is always considered "less
   4.285 -     than" a non-empty box */
   4.286 -  if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1;
   4.287 -  if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0;
   4.288 -  if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1;
   4.289 -  /* use southern border as first ordering criterion */
   4.290 -  if (box1->lat_min < box2->lat_min) return -1;
   4.291 -  if (box1->lat_min > box2->lat_min) return 1;
   4.292 -  /* use northern border as second ordering criterion */
   4.293 -  if (box1->lat_max < box2->lat_max) return -1;
   4.294 -  if (box1->lat_max > box2->lat_max) return 1;
   4.295 -  /* use western border as third ordering criterion */
   4.296 -  if (box1->lon_min < box2->lon_min) return -1;
   4.297 -  if (box1->lon_min > box2->lon_min) return 1;
   4.298 -  /* use eastern border as fourth ordering criterion */
   4.299 -  if (box1->lon_max < box2->lon_max) return -1;
   4.300 -  if (box1->lon_max > box2->lon_max) return 1;
   4.301 -  /* no difference found, boxes are equal */
   4.302 -  return 0;
   4.303 -}
   4.304 -
   4.305 -/* compare two circles */
   4.306 -/* (equality when same circle on earth is described, otherwise an arbitrary
   4.307 -   linear order) */
   4.308 -static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) {
   4.309 -  /* two circles with same infinite radius (positive or negative infinity) are
   4.310 -     considered equal independently of center point */
   4.311 -  if (
   4.312 -    !isfinite(circle1->radius) && !isfinite(circle2->radius) &&
   4.313 -    circle1->radius == circle2->radius
   4.314 -  ) return 0;
   4.315 -  /* use radius as first ordering criterion */
   4.316 -  if (circle1->radius < circle2->radius) return -1;
   4.317 -  if (circle1->radius > circle2->radius) return 1;
   4.318 -  /* use center point as secondary ordering criterion */
   4.319 -  return pgl_point_cmp(&(circle1->center), &(circle2->center));
   4.320 -}
   4.321 -
   4.322 -/* set box to empty box*/
   4.323 -static void pgl_box_set_empty(pgl_box *box) {
   4.324 -  box->lat_min = INFINITY;
   4.325 -  box->lat_max = -INFINITY;
   4.326 -  box->lon_min = 0;
   4.327 -  box->lon_max = 0;
   4.328 -}
   4.329 -
   4.330 -/* check if point is inside a box */
   4.331 -static bool pgl_point_in_box(pgl_point *point, pgl_box *box) {
   4.332 -  return (
   4.333 -    point->lat >= box->lat_min && point->lat <= box->lat_max && (
   4.334 -      (box->lon_min > box->lon_max) ? (
   4.335 -        /* box crosses 180th meridian */
   4.336 -        point->lon >= box->lon_min || point->lon <= box->lon_max
   4.337 -      ) : (
   4.338 -        /* box does not cross the 180th meridian */
   4.339 -        point->lon >= box->lon_min && point->lon <= box->lon_max
   4.340 -      )
   4.341 -    )
   4.342 -  );
   4.343 -}
   4.344 -
   4.345 -/* check if two boxes overlap */
   4.346 -static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) {
   4.347 -  return (
   4.348 -    box2->lat_max >= box2->lat_min &&  /* ensure box2 is not empty */
   4.349 -    ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) &&
   4.350 -    ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && (
   4.351 -      (
   4.352 -        /* check if one and only one box crosses the 180th meridian */
   4.353 -        ((box1->lon_min > box1->lon_max) ? 1 : 0) ^
   4.354 -        ((box2->lon_min > box2->lon_max) ? 1 : 0)
   4.355 -      ) ? (
   4.356 -        /* exactly one box crosses the 180th meridian */
   4.357 -        box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min ||
   4.358 -        box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max
   4.359 -      ) : (
   4.360 -        /* no box or both boxes cross the 180th meridian */
   4.361 -        (
   4.362 -          (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) &&
   4.363 -          (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max)
   4.364 -        ) ||
   4.365 -        /* handle W180 == E180 */
   4.366 -        ( box1->lon_min == -180 && box2->lon_max == 180 ) ||
   4.367 -        ( box2->lon_min == -180 && box1->lon_max == 180 )
   4.368 -      )
   4.369 -    )
   4.370 -  );
   4.371 -}
   4.372 -
   4.373 -/* check unambiguousness of east/west orientation of cluster entries and set
   4.374 -   bounding circle of cluster */
   4.375 -static bool pgl_finalize_cluster(pgl_cluster *cluster) {
   4.376 -  int i, j;                 /* i: index of entry, j: index of point in entry */
   4.377 -  int npoints;              /* number of points in entry */
   4.378 -  int total_npoints = 0;    /* total number of points in cluster */
   4.379 -  pgl_point *points;        /* points in entry */
   4.380 -  int lon_dir;              /* first point of entry west (-1) or east (+1) */
   4.381 -  double lon_break = 0;     /* antipodal longitude of first point in entry */
   4.382 -  double lon_min, lon_max;  /* covered longitude range of entry */
   4.383 -  double value;             /* temporary variable */
   4.384 -  /* reset bounding circle center to empty circle at 0/0 coordinates */
   4.385 -  cluster->bounding.center.lat = 0;
   4.386 -  cluster->bounding.center.lon = 0;
   4.387 -  cluster->bounding.radius = -INFINITY;
   4.388 -  /* if cluster is not empty */
   4.389 -  if (cluster->nentries != 0) {
   4.390 -    /* iterate over all cluster entries and ensure they each cover a longitude
   4.391 -       range less than 180 degrees */
   4.392 -    for (i=0; i<cluster->nentries; i++) {
   4.393 -      /* get properties of entry */
   4.394 -      npoints = cluster->entries[i].npoints;
   4.395 -      points = PGL_ENTRY_POINTS(cluster, i);
   4.396 -      /* get longitude of first point of entry */
   4.397 -      value = points[0].lon;
   4.398 -      /* initialize lon_min and lon_max with longitude of first point */
   4.399 -      lon_min = value;
   4.400 -      lon_max = value;
   4.401 -      /* determine east/west orientation of first point and calculate antipodal
   4.402 -         longitude (Note: rounding required here) */
   4.403 -      if      (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); }
   4.404 -      else if (value > 0) { lon_dir =  1; lon_break = pgl_round(value - 180); }
   4.405 -      else lon_dir = 0;
   4.406 -      /* iterate over all other points in entry */
   4.407 -      for (j=1; j<npoints; j++) {
   4.408 -        /* consider longitude wrap-around */
   4.409 -        value = points[j].lon;
   4.410 -        if      (lon_dir<0 && value>lon_break) value = pgl_round(value - 360);
   4.411 -        else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360);
   4.412 -        /* update lon_min and lon_max */
   4.413 -        if      (value < lon_min) lon_min = value;
   4.414 -        else if (value > lon_max) lon_max = value;
   4.415 -        /* return false if 180 degrees or more are covered */
   4.416 -        if (lon_max - lon_min >= 180) return false;
   4.417 -      }
   4.418 -    }
   4.419 -    /* iterate over all points of all entries and calculate arbitrary center
   4.420 -       point for bounding circle (best if center point minimizes the radius,
   4.421 -       but some error is allowed here) */
   4.422 -    for (i=0; i<cluster->nentries; i++) {
   4.423 -      /* get properties of entry */
   4.424 -      npoints = cluster->entries[i].npoints;
   4.425 -      points = PGL_ENTRY_POINTS(cluster, i);
   4.426 -      /* check if first entry */
   4.427 -      if (i==0) {
   4.428 -        /* get longitude of first point of first entry in whole cluster */
   4.429 -        value = points[0].lon;
   4.430 -        /* initialize lon_min and lon_max with longitude of first point of
   4.431 -           first entry in whole cluster (used to determine if whole cluster
   4.432 -           covers a longitude range of 180 degrees or more) */
   4.433 -        lon_min = value;
   4.434 -        lon_max = value;
   4.435 -        /* determine east/west orientation of first point and calculate
   4.436 -           antipodal longitude (Note: rounding not necessary here) */
   4.437 -        if      (value < 0) { lon_dir = -1; lon_break = value + 180; }
   4.438 -        else if (value > 0) { lon_dir =  1; lon_break = value - 180; }
   4.439 -        else lon_dir = 0;
   4.440 -      }
   4.441 -      /* iterate over all points in entry */
   4.442 -      for (j=0; j<npoints; j++) {
   4.443 -        /* longitude wrap-around (Note: rounding not necessary here) */
   4.444 -        value = points[j].lon;
   4.445 -        if      (lon_dir < 0 && value > lon_break) value -= 360;
   4.446 -        else if (lon_dir > 0 && value < lon_break) value += 360;
   4.447 -        if      (value < lon_min) lon_min = value;
   4.448 -        else if (value > lon_max) lon_max = value;
   4.449 -        /* set bounding circle to cover whole earth if more than 180 degrees
   4.450 -           are covered */
   4.451 -        if (lon_max - lon_min >= 180) {
   4.452 -          cluster->bounding.center.lat = 0;
   4.453 -          cluster->bounding.center.lon = 0;
   4.454 -          cluster->bounding.radius = INFINITY;
   4.455 -          return true;
   4.456 -        }
   4.457 -        /* add point to bounding circle center (for average calculation) */
   4.458 -        cluster->bounding.center.lat += points[j].lat;
   4.459 -        cluster->bounding.center.lon += value;
   4.460 -      }
   4.461 -      /* count total number of points */
   4.462 -      total_npoints += npoints;
   4.463 -    }
   4.464 -    /* determine average latitude and longitude of cluster */
   4.465 -    cluster->bounding.center.lat /= total_npoints;
   4.466 -    cluster->bounding.center.lon /= total_npoints;
   4.467 -    /* normalize longitude of center of cluster bounding circle */
   4.468 -    if (cluster->bounding.center.lon < -180) {
   4.469 -      cluster->bounding.center.lon += 360;
   4.470 -    }
   4.471 -    else if (cluster->bounding.center.lon > 180) {
   4.472 -      cluster->bounding.center.lon -= 360;
   4.473 -    }
   4.474 -    /* round bounding circle center (useful if it is used by other functions) */
   4.475 -    cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat);
   4.476 -    cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon);
   4.477 -    /* calculate radius of bounding circle */
   4.478 -    for (i=0; i<cluster->nentries; i++) {
   4.479 -      npoints = cluster->entries[i].npoints;
   4.480 -      points = PGL_ENTRY_POINTS(cluster, i);
   4.481 -      for (j=0; j<npoints; j++) {
   4.482 -        value = pgl_distance(
   4.483 -          cluster->bounding.center.lat, cluster->bounding.center.lon,
   4.484 -          points[j].lat, points[j].lon
   4.485 -        );
   4.486 -        if (value > cluster->bounding.radius) cluster->bounding.radius = value;
   4.487 -      }
   4.488 -    }
   4.489 -  }
   4.490 -  /* return true (east/west orientation is unambiguous) */
   4.491 -  return true;
   4.492 -}
   4.493 -
   4.494 -/* check if point is inside cluster */
   4.495 -/* (if point is on perimeter, then true is returned if and only if
   4.496 -   strict == false) */
   4.497 -static bool pgl_point_in_cluster(
   4.498 -  pgl_point *point,
   4.499 -  pgl_cluster *cluster,
   4.500 -  bool strict
   4.501 -) {
   4.502 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   4.503 -  int entrytype;         /* type of entry */
   4.504 -  int npoints;           /* number of points in entry */
   4.505 -  pgl_point *points;     /* array of points in entry */
   4.506 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   4.507 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   4.508 -  double lat0 = point->lat;  /* latitude of point */
   4.509 -  double lon0;           /* (adjusted) longitude of point */
   4.510 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   4.511 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   4.512 -  double lon;            /* longitude of intersection */
   4.513 -  int counter = 0;       /* counter for intersections east of point */
   4.514 -  /* iterate over all entries */
   4.515 -  for (i=0; i<cluster->nentries; i++) {
   4.516 -    /* get type of entry */
   4.517 -    entrytype = cluster->entries[i].entrytype;
   4.518 -    /* skip all entries but polygons if perimeters are excluded */
   4.519 -    if (strict && entrytype != PGL_ENTRY_POLYGON) continue;
   4.520 -    /* get points of entry */
   4.521 -    npoints = cluster->entries[i].npoints;
   4.522 -    points = PGL_ENTRY_POINTS(cluster, i);
   4.523 -    /* determine east/west orientation of first point of entry and calculate
   4.524 -       antipodal longitude */
   4.525 -    lon_break = points[0].lon;
   4.526 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   4.527 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   4.528 -    else lon_dir = 0;
   4.529 -    /* get longitude of point */
   4.530 -    lon0 = point->lon;
   4.531 -    /* consider longitude wrap-around for point */
   4.532 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360);
   4.533 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360);
   4.534 -    /* iterate over all edges and vertices */
   4.535 -    for (j=0; j<npoints; j++) {
   4.536 -      /* return if point is on vertex of polygon */
   4.537 -      if (pgl_point_cmp(point, &(points[j])) == 0) return !strict;
   4.538 -      /* calculate index of next vertex */
   4.539 -      k = (j+1) % npoints;
   4.540 -      /* skip last edge unless entry is (closed) outline or polygon */
   4.541 -      if (
   4.542 -        k == 0 &&
   4.543 -        entrytype != PGL_ENTRY_OUTLINE &&
   4.544 -        entrytype != PGL_ENTRY_POLYGON
   4.545 -      ) continue;
   4.546 -      /* use previously calculated values for lat1 and lon1 if possible */
   4.547 -      if (j) {
   4.548 -        lat1 = lat2;
   4.549 -        lon1 = lon2;
   4.550 -      } else {
   4.551 -        /* otherwise get latitude and longitude values of first vertex */
   4.552 -        lat1 = points[0].lat;
   4.553 -        lon1 = points[0].lon;
   4.554 -        /* and consider longitude wrap-around for first vertex */
   4.555 -        if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
   4.556 -        else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
   4.557 -      }
   4.558 -      /* get latitude and longitude of next vertex */
   4.559 -      lat2 = points[k].lat;
   4.560 -      lon2 = points[k].lon;
   4.561 -      /* consider longitude wrap-around for next vertex */
   4.562 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
   4.563 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
   4.564 -      /* return if point is on horizontal (west to east) edge of polygon */
   4.565 -      if (
   4.566 -        lat0 == lat1 && lat0 == lat2 &&
   4.567 -        ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) )
   4.568 -      ) return !strict;
   4.569 -      /* check if edge crosses east/west line of point */
   4.570 -      if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) {
   4.571 -        /* calculate longitude of intersection */
   4.572 -        lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1);
   4.573 -        /* return if intersection goes (approximately) through point */
   4.574 -        if (pgl_round(lon) == lon0) return !strict;
   4.575 -        /* count intersection if east of point and entry is polygon*/
   4.576 -        if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++;
   4.577 -      }
   4.578 -    }
   4.579 -  }
   4.580 -  /* return true if number of intersections is odd */
   4.581 -  return counter & 1;
   4.582 -}
   4.583 -
   4.584 -/* check if all points of the second cluster are strictly inside the first
   4.585 -   cluster */
   4.586 -static inline bool pgl_all_cluster_points_strictly_in_cluster(
   4.587 -  pgl_cluster *outer, pgl_cluster *inner
   4.588 -) {
   4.589 -  int i, j;           /* i: entry, j: point in entry */
   4.590 -  int npoints;        /* number of points in entry */
   4.591 -  pgl_point *points;  /* array of points in entry */
   4.592 -  /* iterate over all entries of "inner" cluster */
   4.593 -  for (i=0; i<inner->nentries; i++) {
   4.594 -    /* get properties of entry */
   4.595 -    npoints = inner->entries[i].npoints;
   4.596 -    points = PGL_ENTRY_POINTS(inner, i);
   4.597 -    /* iterate over all points in entry of "inner" cluster */
   4.598 -    for (j=0; j<npoints; j++) {
   4.599 -      /* return false if one point of inner cluster is not in outer cluster */
   4.600 -      if (!pgl_point_in_cluster(points+j, outer, true)) return false;
   4.601 -    }
   4.602 -  }
   4.603 -  /* otherwise return true */
   4.604 -  return true;
   4.605 -}
   4.606 -
   4.607 -/* check if any point the second cluster is inside the first cluster */
   4.608 -static inline bool pgl_any_cluster_points_in_cluster(
   4.609 -  pgl_cluster *outer, pgl_cluster *inner
   4.610 -) {
   4.611 -  int i, j;           /* i: entry, j: point in entry */
   4.612 -  int npoints;        /* number of points in entry */
   4.613 -  pgl_point *points;  /* array of points in entry */
   4.614 -  /* iterate over all entries of "inner" cluster */
   4.615 -  for (i=0; i<inner->nentries; i++) {
   4.616 -    /* get properties of entry */
   4.617 -    npoints = inner->entries[i].npoints;
   4.618 -    points = PGL_ENTRY_POINTS(inner, i);
   4.619 -    /* iterate over all points in entry of "inner" cluster */
   4.620 -    for (j=0; j<npoints; j++) {
   4.621 -      /* return true if one point of inner cluster is in outer cluster */
   4.622 -      if (pgl_point_in_cluster(points+j, outer, false)) return true;
   4.623 -    }
   4.624 -  }
   4.625 -  /* otherwise return false */
   4.626 -  return false;
   4.627 -}
   4.628 -
   4.629 -/* check if line segment strictly crosses line (not just touching) */
   4.630 -static inline bool pgl_lseg_crosses_line(
   4.631 -  double seg_x1,  double seg_y1,  double seg_x2,  double seg_y2,
   4.632 -  double line_x1, double line_y1, double line_x2, double line_y2
   4.633 -) {
   4.634 -  return (
   4.635 -    (
   4.636 -      (seg_x1-line_x1) * (line_y2-line_y1) -
   4.637 -      (seg_y1-line_y1) * (line_x2-line_x1)
   4.638 -    ) * (
   4.639 -      (seg_x2-line_x1) * (line_y2-line_y1) -
   4.640 -      (seg_y2-line_y1) * (line_x2-line_x1)
   4.641 -    )
   4.642 -  ) < 0;
   4.643 -}
   4.644 -
   4.645 -/* check if paths and outlines of two clusters strictly overlap (not just
   4.646 -   touching) */
   4.647 -static bool pgl_outlines_overlap(
   4.648 -  pgl_cluster *cluster1, pgl_cluster *cluster2
   4.649 -) {
   4.650 -  int i1, j1, k1;  /* i: entry, j: point in entry, k: next point in entry */
   4.651 -  int i2, j2, k2;
   4.652 -  int entrytype1, entrytype2;     /* type of entry */
   4.653 -  int npoints1, npoints2;         /* number of points in entry */
   4.654 -  pgl_point *points1;             /* array of points in entry of cluster1 */
   4.655 -  pgl_point *points2;             /* array of points in entry of cluster2 */
   4.656 -  int lon_dir1, lon_dir2;         /* first vertex west (-1) or east (+1) */
   4.657 -  double lon_break1, lon_break2;  /* antipodal longitude of first vertex */
   4.658 -  double lat11, lon11;  /* latitude and (adjusted) longitude of vertex */
   4.659 -  double lat12, lon12;  /* latitude and (adjusted) longitude of next vertex */
   4.660 -  double lat21, lon21;  /* latitude and (adjusted) longitudes for cluster2 */
   4.661 -  double lat22, lon22;
   4.662 -  double wrapvalue;     /* temporary helper value to adjust wrap-around */  
   4.663 -  /* iterate over all entries of cluster1 */
   4.664 -  for (i1=0; i1<cluster1->nentries; i1++) {
   4.665 -    /* get properties of entry in cluster1 and skip points */
   4.666 -    npoints1 = cluster1->entries[i1].npoints;
   4.667 -    if (npoints1 < 2) continue;
   4.668 -    entrytype1 = cluster1->entries[i1].entrytype;
   4.669 -    points1 = PGL_ENTRY_POINTS(cluster1, i1);
   4.670 -    /* determine east/west orientation of first point and calculate antipodal
   4.671 -       longitude */
   4.672 -    lon_break1 = points1[0].lon;
   4.673 -    if (lon_break1 < 0) {
   4.674 -      lon_dir1   = -1;
   4.675 -      lon_break1 = pgl_round(lon_break1 + 180);
   4.676 -    } else if (lon_break1 > 0) {
   4.677 -      lon_dir1   = 1;
   4.678 -      lon_break1 = pgl_round(lon_break1 - 180);
   4.679 -    } else lon_dir1 = 0;
   4.680 -    /* iterate over all edges and vertices in cluster1 */
   4.681 -    for (j1=0; j1<npoints1; j1++) {
   4.682 -      /* calculate index of next vertex */
   4.683 -      k1 = (j1+1) % npoints1;
   4.684 -      /* skip last edge unless entry is (closed) outline or polygon */
   4.685 -      if (
   4.686 -        k1 == 0 &&
   4.687 -        entrytype1 != PGL_ENTRY_OUTLINE &&
   4.688 -        entrytype1 != PGL_ENTRY_POLYGON
   4.689 -      ) continue;
   4.690 -      /* use previously calculated values for lat1 and lon1 if possible */
   4.691 -      if (j1) {
   4.692 -        lat11 = lat12;
   4.693 -        lon11 = lon12;
   4.694 -      } else {
   4.695 -        /* otherwise get latitude and longitude values of first vertex */
   4.696 -        lat11 = points1[0].lat;
   4.697 -        lon11 = points1[0].lon;
   4.698 -        /* and consider longitude wrap-around for first vertex */
   4.699 -        if      (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360);
   4.700 -        else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360);
   4.701 -      }
   4.702 -      /* get latitude and longitude of next vertex */
   4.703 -      lat12 = points1[k1].lat;
   4.704 -      lon12 = points1[k1].lon;
   4.705 -      /* consider longitude wrap-around for next vertex */
   4.706 -      if      (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360);
   4.707 -      else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360);
   4.708 -      /* skip degenerated edges */
   4.709 -      if (lat11 == lat12 && lon11 == lon12) continue;
   4.710 -      /* iterate over all entries of cluster2 */
   4.711 -      for (i2=0; i2<cluster2->nentries; i2++) {
   4.712 -        /* get points and number of points of entry in cluster2 */
   4.713 -        npoints2 = cluster2->entries[i2].npoints;
   4.714 -        if (npoints2 < 2) continue;
   4.715 -        entrytype2 = cluster2->entries[i2].entrytype;
   4.716 -        points2 = PGL_ENTRY_POINTS(cluster2, i2);
   4.717 -        /* determine east/west orientation of first point and calculate antipodal
   4.718 -           longitude */
   4.719 -        lon_break2 = points2[0].lon;
   4.720 -        if (lon_break2 < 0) {
   4.721 -          lon_dir2   = -1;
   4.722 -          lon_break2 = pgl_round(lon_break2 + 180);
   4.723 -        } else if (lon_break2 > 0) {
   4.724 -          lon_dir2   = 1;
   4.725 -          lon_break2 = pgl_round(lon_break2 - 180);
   4.726 -        } else lon_dir2 = 0;
   4.727 -        /* iterate over all edges and vertices in cluster2 */
   4.728 -        for (j2=0; j2<npoints2; j2++) {
   4.729 -          /* calculate index of next vertex */
   4.730 -          k2 = (j2+1) % npoints2;
   4.731 -          /* skip last edge unless entry is (closed) outline or polygon */
   4.732 -          if (
   4.733 -            k2 == 0 &&
   4.734 -            entrytype2 != PGL_ENTRY_OUTLINE &&
   4.735 -            entrytype2 != PGL_ENTRY_POLYGON
   4.736 -          ) continue;
   4.737 -          /* use previously calculated values for lat1 and lon1 if possible */
   4.738 -          if (j2) {
   4.739 -            lat21 = lat22;
   4.740 -            lon21 = lon22;
   4.741 -          } else {
   4.742 -            /* otherwise get latitude and longitude values of first vertex */
   4.743 -            lat21 = points2[0].lat;
   4.744 -            lon21 = points2[0].lon;
   4.745 -            /* and consider longitude wrap-around for first vertex */
   4.746 -            if      (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360);
   4.747 -            else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360);
   4.748 -          }
   4.749 -          /* get latitude and longitude of next vertex */
   4.750 -          lat22 = points2[k2].lat;
   4.751 -          lon22 = points2[k2].lon;
   4.752 -          /* consider longitude wrap-around for next vertex */
   4.753 -          if      (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360);
   4.754 -          else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360);
   4.755 -          /* skip degenerated edges */
   4.756 -          if (lat21 == lat22 && lon21 == lon22) continue;
   4.757 -          /* perform another wrap-around where necessary */
   4.758 -          /* TODO: improve performance of whole wrap-around mechanism */
   4.759 -          wrapvalue = (lon21 + lon22) - (lon11 + lon12);
   4.760 -          if (wrapvalue > 360) {
   4.761 -            lon21 = pgl_round(lon21 - 360);
   4.762 -            lon22 = pgl_round(lon22 - 360);
   4.763 -          } else if (wrapvalue < -360) {
   4.764 -            lon21 = pgl_round(lon21 + 360);
   4.765 -            lon22 = pgl_round(lon22 + 360);
   4.766 -          }
   4.767 -          /* return true if segments overlap */
   4.768 -          if (
   4.769 -            pgl_lseg_crosses_line(
   4.770 -              lat11, lon11, lat12, lon12,
   4.771 -              lat21, lon21, lat22, lon22
   4.772 -            ) && pgl_lseg_crosses_line(
   4.773 -              lat21, lon21, lat22, lon22,
   4.774 -              lat11, lon11, lat12, lon12
   4.775 -            )
   4.776 -          ) {
   4.777 -            return true;
   4.778 -          }
   4.779 -        }
   4.780 -      }
   4.781 -    }
   4.782 -  }
   4.783 -  /* otherwise return false */
   4.784 -  return false;
   4.785 -}
   4.786 -
   4.787 -/* check if second cluster is completely contained in first cluster */
   4.788 -static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) {
   4.789 -  if (!pgl_all_cluster_points_strictly_in_cluster(outer, inner)) return false;
   4.790 -  if (pgl_any_cluster_points_in_cluster(inner, outer)) return false;
   4.791 -  if (pgl_outlines_overlap(outer, inner)) return false;
   4.792 -  return true;
   4.793 -}
   4.794 -
   4.795 -/* check if two clusters overlap */
   4.796 -static bool pgl_clusters_overlap(
   4.797 -  pgl_cluster *cluster1, pgl_cluster *cluster2
   4.798 -) {
   4.799 -  if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true;
   4.800 -  if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true;
   4.801 -  if (pgl_outlines_overlap(cluster1, cluster2)) return true;
   4.802 -  return false;
   4.803 -}
   4.804 -
   4.805 -
   4.806 -/* calculate (approximate) distance between point and cluster */
   4.807 -static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
   4.808 -  int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   4.809 -  int entrytype;         /* type of entry */
   4.810 -  int npoints;           /* number of points in entry */
   4.811 -  pgl_point *points;     /* array of points in entry */
   4.812 -  int lon_dir = 0;       /* first vertex west (-1) or east (+1) */
   4.813 -  double lon_break = 0;  /* antipodal longitude of first vertex */
   4.814 -  double lon_min = 0;    /* minimum (adjusted) longitude of entry vertices */
   4.815 -  double lon_max = 0;    /* maximum (adjusted) longitude of entry vertices */
   4.816 -  double lat0 = point->lat;  /* latitude of point */
   4.817 -  double lon0;           /* (adjusted) longitude of point */
   4.818 -  double lat1, lon1;     /* latitude and (adjusted) longitude of vertex */
   4.819 -  double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
   4.820 -  double s;              /* scalar for vector calculations */
   4.821 -  double dist;           /* distance calculated in one step */
   4.822 -  double min_dist = INFINITY;   /* minimum distance */
   4.823 -  /* distance is zero if point is contained in cluster */
   4.824 -  if (pgl_point_in_cluster(point, cluster, false)) return 0;
   4.825 -  /* iterate over all entries */
   4.826 -  for (i=0; i<cluster->nentries; i++) {
   4.827 -    /* get properties of entry */
   4.828 -    entrytype = cluster->entries[i].entrytype;
   4.829 -    npoints = cluster->entries[i].npoints;
   4.830 -    points = PGL_ENTRY_POINTS(cluster, i);
   4.831 -    /* determine east/west orientation of first point of entry and calculate
   4.832 -       antipodal longitude */
   4.833 -    lon_break = points[0].lon;
   4.834 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   4.835 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   4.836 -    else lon_dir = 0;
   4.837 -    /* determine covered longitude range */
   4.838 -    for (j=0; j<npoints; j++) {
   4.839 -      /* get longitude of vertex */
   4.840 -      lon1 = points[j].lon;
   4.841 -      /* adjust longitude to fix potential wrap-around */
   4.842 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   4.843 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   4.844 -      /* update minimum and maximum longitude of polygon */
   4.845 -      if (j == 0 || lon1 < lon_min) lon_min = lon1;
   4.846 -      if (j == 0 || lon1 > lon_max) lon_max = lon1;
   4.847 -    }
   4.848 -    /* adjust longitude wrap-around according to full longitude range */
   4.849 -    lon_break = (lon_max + lon_min) / 2;
   4.850 -    if      (lon_break < 0) { lon_dir = -1; lon_break += 180; }
   4.851 -    else if (lon_break > 0) { lon_dir =  1; lon_break -= 180; }
   4.852 -    /* get longitude of point */
   4.853 -    lon0 = point->lon;
   4.854 -    /* consider longitude wrap-around for point */
   4.855 -    if      (lon_dir < 0 && lon0 > lon_break) lon0 -= 360;
   4.856 -    else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
   4.857 -    /* iterate over all edges and vertices */
   4.858 -    for (j=0; j<npoints; j++) {
   4.859 -      /* use previously calculated values for lat1 and lon1 if possible */
   4.860 -      if (j) {
   4.861 -        lat1 = lat2;
   4.862 -        lon1 = lon2;
   4.863 -      } else {
   4.864 -        /* otherwise get latitude and longitude values of first vertex */
   4.865 -        lat1 = points[0].lat;
   4.866 -        lon1 = points[0].lon;
   4.867 -        /* and consider longitude wrap-around for first vertex */
   4.868 -        if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   4.869 -        else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   4.870 -      }
   4.871 -      /* calculate distance to vertex */
   4.872 -      dist = pgl_distance(lat0, lon0, lat1, lon1);
   4.873 -      /* store calculated distance if smallest */
   4.874 -      if (dist < min_dist) min_dist = dist;
   4.875 -      /* calculate index of next vertex */
   4.876 -      k = (j+1) % npoints;
   4.877 -      /* skip last edge unless entry is (closed) outline or polygon */
   4.878 -      if (
   4.879 -        k == 0 &&
   4.880 -        entrytype != PGL_ENTRY_OUTLINE &&
   4.881 -        entrytype != PGL_ENTRY_POLYGON
   4.882 -      ) continue;
   4.883 -      /* get latitude and longitude of next vertex */
   4.884 -      lat2 = points[k].lat;
   4.885 -      lon2 = points[k].lon;
   4.886 -      /* consider longitude wrap-around for next vertex */
   4.887 -      if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
   4.888 -      else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
   4.889 -      /* go to next vertex and edge if edge is degenerated */
   4.890 -      if (lat1 == lat2 && lon1 == lon2) continue;
   4.891 -      /* otherwise test if point can be projected onto edge of polygon */
   4.892 -      s = (
   4.893 -        ((lat0-lat1) * (lat2-lat1) + (lon0-lon1) * (lon2-lon1)) /
   4.894 -        ((lat2-lat1) * (lat2-lat1) + (lon2-lon1) * (lon2-lon1))
   4.895 -      );
   4.896 -      /* go to next vertex and edge if point cannot be projected */
   4.897 -      if (!(s > 0 && s < 1)) continue;
   4.898 -      /* calculate distance from original point to projected point */
   4.899 -      dist = pgl_distance(
   4.900 -        lat0, lon0,
   4.901 -        lat1 + s * (lat2-lat1),
   4.902 -        lon1 + s * (lon2-lon1)
   4.903 -      );
   4.904 -      /* store calculated distance if smallest */
   4.905 -      if (dist < min_dist) min_dist = dist;
   4.906 -    }
   4.907 -  }
   4.908 -  /* return minimum distance */
   4.909 -  return min_dist;
   4.910 -}
   4.911 -
   4.912 -/* calculate (approximate) distance between two clusters */
   4.913 -static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) {
   4.914 -  int i, j;                    /* i: entry, j: point in entry */
   4.915 -  int npoints;                 /* number of points in entry */
   4.916 -  pgl_point *points;           /* array of points in entry */
   4.917 -  double dist;                 /* distance calculated in one step */
   4.918 -  double min_dist = INFINITY;  /* minimum distance */
   4.919 -  /* consider distance from each point in one cluster to the whole other */
   4.920 -  for (i=0; i<cluster1->nentries; i++) {
   4.921 -    npoints = cluster1->entries[i].npoints;
   4.922 -    points = PGL_ENTRY_POINTS(cluster1, i);
   4.923 -    for (j=0; j<npoints; j++) {
   4.924 -      dist = pgl_point_cluster_distance(points+j, cluster2);
   4.925 -      if (dist == 0) return dist;
   4.926 -      if (dist < min_dist) min_dist = dist;
   4.927 -    }
   4.928 -  }
   4.929 -  /* consider distance from each point in other cluster to the first cluster */
   4.930 -  for (i=0; i<cluster2->nentries; i++) {
   4.931 -    npoints = cluster2->entries[i].npoints;
   4.932 -    points = PGL_ENTRY_POINTS(cluster2, i);
   4.933 -    for (j=0; j<npoints; j++) {
   4.934 -      dist = pgl_point_cluster_distance(points+j, cluster1);
   4.935 -      if (dist == 0) return dist;
   4.936 -      if (dist < min_dist) min_dist = dist;
   4.937 -    }
   4.938 -  }
   4.939 -  return min_dist;
   4.940 -}
   4.941 -
   4.942 -/* estimator function for distance between box and point */
   4.943 -/* always returns a smaller value than actually correct or zero */
   4.944 -static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
   4.945 -  double dlon;      /* longitude range of box (delta longitude) */
   4.946 -  double distance;  /* return value */
   4.947 -  /* return infinity if box is empty */
   4.948 -  if (box->lat_min > box->lat_max) return INFINITY;
   4.949 -  /* return zero if point is inside box */
   4.950 -  if (pgl_point_in_box(point, box)) return 0;
   4.951 -  /* calculate delta longitude */
   4.952 -  dlon = box->lon_max - box->lon_min;
   4.953 -  if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
   4.954 -  /* if delta longitude is greater than 150 degrees, perform safe fall-back */
   4.955 -  if (dlon > 150) return 0;
   4.956 -  /* calculate lower limit for distance (formula below requires dlon <= 150) */
   4.957 -  /* TODO: provide better estimation function to improve performance */
   4.958 -  distance = (
   4.959 -    (1.0-1e-14) * pgl_distance(
   4.960 -      point->lat,
   4.961 -      point->lon,
   4.962 -      (box->lat_min + box->lat_max) / 2,
   4.963 -      box->lon_min + dlon/2
   4.964 -    ) - pgl_distance(
   4.965 -      box->lat_min, box->lon_min,
   4.966 -      box->lat_max, box->lon_max
   4.967 -    )
   4.968 -  );
   4.969 -  /* truncate negative results to zero */
   4.970 -  if (distance <= 0) distance = 0;
   4.971 -  /* return result */
   4.972 -  return distance;
   4.973 -}
   4.974 -
   4.975 -
   4.976 -/*-------------------------------------------------*
   4.977 - *  geographic index based on space-filling curve  *
   4.978 - *-------------------------------------------------*/
   4.979 -
   4.980 -/* number of bytes used for geographic (center) position in keys */
   4.981 -#define PGL_KEY_LATLON_BYTELEN 7
   4.982 -
   4.983 -/* maximum reference value for logarithmic size of geographic objects */
   4.984 -#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
   4.985 -
   4.986 -/* pointer to index key (either pgl_pointkey or pgl_areakey) */
   4.987 -typedef unsigned char *pgl_keyptr;
   4.988 -
   4.989 -/* index key for points (objects with zero area) on the spheroid */
   4.990 -/* bit  0..55: interspersed bits of latitude and longitude,
   4.991 -   bit 56..57: always zero,
   4.992 -   bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */
   4.993 -typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1];
   4.994 -
   4.995 -/* index key for geographic objects on spheroid with area greater than zero */
   4.996 -/* bit  0..55: interspersed bits of latitude and longitude of center point,
   4.997 -   bit     56: always set to 1,
   4.998 -   bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.),
   4.999 -   bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to
  4.1000 -               PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth
  4.1001 -               = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL
  4.1002 -               (with interspersed bits = 0 and node depth = 0) for keys which
  4.1003 -               cover both empty and non-empty objects */
  4.1004 -
  4.1005 -typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2];
  4.1006 -
  4.1007 -/* helper macros for reading/writing index keys */
  4.1008 -#define PGL_KEY_NODEDEPTH_OFFSET  PGL_KEY_LATLON_BYTELEN
  4.1009 -#define PGL_KEY_OBJSIZE_OFFSET    (PGL_KEY_NODEDEPTH_OFFSET+1)
  4.1010 -#define PGL_POINTKEY_MAXDEPTH     (PGL_KEY_LATLON_BYTELEN*8)
  4.1011 -#define PGL_AREAKEY_MAXDEPTH      (2*PGL_POINTKEY_MAXDEPTH+1)
  4.1012 -#define PGL_AREAKEY_MAXOBJSIZE    (PGL_POINTKEY_MAXDEPTH+1)
  4.1013 -#define PGL_AREAKEY_TYPEMASK      0x80
  4.1014 -#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8)))
  4.1015 -#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \
  4.1016 -                                  ( PGL_KEY_LATLONBIT(key1, n) ^ \
  4.1017 -                                    PGL_KEY_LATLONBIT(key2, n) )
  4.1018 -#define PGL_KEY_IS_AREAKEY(key)   ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
  4.1019 -                                    PGL_AREAKEY_TYPEMASK)
  4.1020 -#define PGL_KEY_NODEDEPTH(key)    ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
  4.1021 -                                    (PGL_AREAKEY_TYPEMASK-1))
  4.1022 -#define PGL_KEY_OBJSIZE(key)      ((key)[PGL_KEY_OBJSIZE_OFFSET])
  4.1023 -#define PGL_KEY_OBJSIZE_EMPTY     126
  4.1024 -#define PGL_KEY_OBJSIZE_UNIVERSAL 127
  4.1025 -#define PGL_KEY_IS_EMPTY(key)     ( PGL_KEY_IS_AREAKEY(key) && \
  4.1026 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
  4.1027 -                                    PGL_KEY_OBJSIZE_EMPTY )
  4.1028 -#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \
  4.1029 -                                    (key)[PGL_KEY_OBJSIZE_OFFSET] == \
  4.1030 -                                    PGL_KEY_OBJSIZE_UNIVERSAL )
  4.1031 -
  4.1032 -/* set area key to match empty objects only */
  4.1033 -static void pgl_key_set_empty(pgl_keyptr key) {
  4.1034 -  memset(key, 0, sizeof(pgl_areakey));
  4.1035 -  /* Note: setting node depth to maximum is required for picksplit function */
  4.1036 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
  4.1037 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY;
  4.1038 -}
  4.1039 -
  4.1040 -/* set area key to match any object (including empty objects) */
  4.1041 -static void pgl_key_set_universal(pgl_keyptr key) {
  4.1042 -  memset(key, 0, sizeof(pgl_areakey));
  4.1043 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK;
  4.1044 -  key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL;
  4.1045 -}
  4.1046 -
  4.1047 -/* convert a point on earth into a max-depth key to be used in index */
  4.1048 -static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) {
  4.1049 -  double lat = point->lat;
  4.1050 -  double lon = point->lon;
  4.1051 -  int i;
  4.1052 -  /* clear latitude and longitude bits */
  4.1053 -  memset(key, 0, PGL_KEY_LATLON_BYTELEN);
  4.1054 -  /* set node depth to maximum and type bit to zero */
  4.1055 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH;
  4.1056 -  /* iterate over all latitude/longitude bit pairs */
  4.1057 -  for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) {
  4.1058 -    /* determine latitude bit */
  4.1059 -    if (lat >= 0) {
  4.1060 -      key[i/4] |= 0x80 >> (2*(i%4));
  4.1061 -      lat *= 2; lat -= 90;
  4.1062 -    } else {
  4.1063 -      lat *= 2; lat += 90;
  4.1064 -    }
  4.1065 -    /* determine longitude bit */
  4.1066 -    if (lon >= 0) {
  4.1067 -      key[i/4] |= 0x80 >> (2*(i%4)+1);
  4.1068 -      lon *= 2; lon -= 180;
  4.1069 -    } else {
  4.1070 -      lon *= 2; lon += 180;
  4.1071 -    }
  4.1072 -  }
  4.1073 -}
  4.1074 -
  4.1075 -/* convert a circle on earth into a max-depth key to be used in an index */
  4.1076 -static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) {
  4.1077 -  /* handle special case of empty circle */
  4.1078 -  if (circle->radius < 0) {
  4.1079 -    pgl_key_set_empty(key);
  4.1080 -    return;
  4.1081 -  }
  4.1082 -  /* perform same action as for point keys */
  4.1083 -  pgl_point_to_key(&(circle->center), key);
  4.1084 -  /* but overwrite type and node depth to fit area index key */
  4.1085 -  key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
  4.1086 -  /* check if radius is greater than (or equal to) reference size */
  4.1087 -  /* (treat equal values as greater values for numerical safety) */
  4.1088 -  if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) {
  4.1089 -    /* if yes, set logarithmic size to zero */
  4.1090 -    key[PGL_KEY_OBJSIZE_OFFSET] = 0;
  4.1091 -  } else {
  4.1092 -    /* otherwise, determine logarithmic size iteratively */
  4.1093 -    /* (one step is equivalent to a factor of sqrt(2)) */
  4.1094 -    double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2;
  4.1095 -    int objsize = 1;
  4.1096 -    while (objsize < PGL_AREAKEY_MAXOBJSIZE) {
  4.1097 -      /* stop when radius is greater than (or equal to) adjusted reference */
  4.1098 -      /* (treat equal values as greater values for numerical safety) */
  4.1099 -      if (circle->radius >= reference) break;
  4.1100 -      reference /= M_SQRT2;
  4.1101 -      objsize++;
  4.1102 -    }
  4.1103 -    /* set logarithmic size to determined value */
  4.1104 -    key[PGL_KEY_OBJSIZE_OFFSET] = objsize;
  4.1105 -  }
  4.1106 -}
  4.1107 -
  4.1108 -/* check if one key is subkey of another key or vice versa */
  4.1109 -static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) {
  4.1110 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
  4.1111 -  /* determine smallest depth */
  4.1112 -  int depth1 = PGL_KEY_NODEDEPTH(key1);
  4.1113 -  int depth2 = PGL_KEY_NODEDEPTH(key2);
  4.1114 -  int depth = (depth1 < depth2) ? depth1 : depth2;
  4.1115 -  /* check if keys are area keys (assuming that both keys have same type) */
  4.1116 -  if (PGL_KEY_IS_AREAKEY(key1)) {
  4.1117 -    int j = 0;  /* bit offset for logarithmic object size bits */
  4.1118 -    int k = 0;  /* bit offset for latitude and longitude */
  4.1119 -    /* fetch logarithmic object size information */
  4.1120 -    int objsize1 = PGL_KEY_OBJSIZE(key1);
  4.1121 -    int objsize2 = PGL_KEY_OBJSIZE(key2);
  4.1122 -    /* handle special cases for empty objects (universal and empty keys) */
  4.1123 -    if (
  4.1124 -      objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL ||
  4.1125 -      objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL
  4.1126 -    ) return true;
  4.1127 -    if (
  4.1128 -      objsize1 == PGL_KEY_OBJSIZE_EMPTY ||
  4.1129 -      objsize2 == PGL_KEY_OBJSIZE_EMPTY
  4.1130 -    ) return objsize1 == objsize2;
  4.1131 -    /* iterate through key bits */
  4.1132 -    for (i=0; i<depth; i++) {
  4.1133 -      /* every second bit is a bit describing the object size */
  4.1134 -      if (i%2 == 0) {
  4.1135 -        /* check if object size bit is different in both keys (objsize1 and
  4.1136 -           objsize2 describe the minimum index when object size bit is set) */
  4.1137 -        if (
  4.1138 -          (objsize1 <= j && objsize2 > j) ||
  4.1139 -          (objsize2 <= j && objsize1 > j)
  4.1140 -        ) {
  4.1141 -          /* bit differs, therefore keys are in separate branches */
  4.1142 -          return false;
  4.1143 -        }
  4.1144 -        /* increase bit counter for object size bits */
  4.1145 -        j++;
  4.1146 -      }
  4.1147 -      /* all other bits describe latitude and longitude */
  4.1148 -      else {
  4.1149 -        /* check if bit differs in both keys */
  4.1150 -        if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) {
  4.1151 -          /* bit differs, therefore keys are in separate branches */
  4.1152 -          return false;
  4.1153 -        }
  4.1154 -        /* increase bit counter for latitude/longitude bits */
  4.1155 -        k++;
  4.1156 -      }
  4.1157 -    }
  4.1158 -  }
  4.1159 -  /* if not, keys are point keys */
  4.1160 -  else {
  4.1161 -    /* iterate through key bits */
  4.1162 -    for (i=0; i<depth; i++) {
  4.1163 -      /* check if bit differs in both keys */
  4.1164 -      if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) {
  4.1165 -        /* bit differs, therefore keys are in separate branches */
  4.1166 -        return false;
  4.1167 -      }
  4.1168 -    }
  4.1169 -  }
  4.1170 -  /* return true because keys are in the same branch */
  4.1171 -  return true;
  4.1172 -}
  4.1173 -
  4.1174 -/* combine two keys into new key which covers both original keys */
  4.1175 -/* (result stored in first argument) */
  4.1176 -static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) {
  4.1177 -  int i;  /* key bit offset (includes both lat/lon and log. obj. size bits) */
  4.1178 -  /* determine smallest depth */
  4.1179 -  int depth1 = PGL_KEY_NODEDEPTH(dst);
  4.1180 -  int depth2 = PGL_KEY_NODEDEPTH(src);
  4.1181 -  int depth = (depth1 < depth2) ? depth1 : depth2;
  4.1182 -  /* check if keys are area keys (assuming that both keys have same type) */
  4.1183 -  if (PGL_KEY_IS_AREAKEY(dst)) {
  4.1184 -    pgl_areakey dstbuf = { 0, };  /* destination buffer (cleared) */
  4.1185 -    int j = 0;  /* bit offset for logarithmic object size bits */
  4.1186 -    int k = 0;  /* bit offset for latitude and longitude */
  4.1187 -    /* fetch logarithmic object size information */
  4.1188 -    int objsize1 = PGL_KEY_OBJSIZE(dst);
  4.1189 -    int objsize2 = PGL_KEY_OBJSIZE(src);
  4.1190 -    /* handle special cases for empty objects (universal and empty keys) */
  4.1191 -    if (
  4.1192 -      objsize1 > PGL_AREAKEY_MAXOBJSIZE ||
  4.1193 -      objsize2 > PGL_AREAKEY_MAXOBJSIZE
  4.1194 -    ) {
  4.1195 -      if (
  4.1196 -        objsize1 == PGL_KEY_OBJSIZE_EMPTY &&
  4.1197 -        objsize2 == PGL_KEY_OBJSIZE_EMPTY
  4.1198 -      ) pgl_key_set_empty(dst);
  4.1199 -      else pgl_key_set_universal(dst);
  4.1200 -      return;
  4.1201 -    }
  4.1202 -    /* iterate through key bits */
  4.1203 -    for (i=0; i<depth; i++) {
  4.1204 -      /* every second bit is a bit describing the object size */
  4.1205 -      if (i%2 == 0) {
  4.1206 -        /* increase bit counter for object size bits first */
  4.1207 -        /* (handy when setting objsize variable) */
  4.1208 -        j++;
  4.1209 -        /* check if object size bit is set in neither key */
  4.1210 -        if (objsize1 >= j && objsize2 >= j) {
  4.1211 -          /* set objsize in destination buffer to indicate that size bit is
  4.1212 -             unset in destination buffer at the current bit position */
  4.1213 -          dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j;
  4.1214 -        }
  4.1215 -        /* break if object size bit is set in one key only */
  4.1216 -        else if (objsize1 >= j || objsize2 >= j) break;
  4.1217 -      }
  4.1218 -      /* all other bits describe latitude and longitude */
  4.1219 -      else {
  4.1220 -        /* break if bit differs in both keys */
  4.1221 -        if (PGL_KEY_LATLONBIT(dst, k)) {
  4.1222 -          if (!PGL_KEY_LATLONBIT(src, k)) break;
  4.1223 -          /* but set bit in destination buffer if bit is set in both keys */
  4.1224 -          dstbuf[k/8] |= 0x80 >> (k%8);
  4.1225 -        } else if (PGL_KEY_LATLONBIT(src, k)) break;
  4.1226 -        /* increase bit counter for latitude/longitude bits */
  4.1227 -        k++;
  4.1228 -      }
  4.1229 -    }
  4.1230 -    /* set common node depth and type bit (type bit = 1) */
  4.1231 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i;
  4.1232 -    /* copy contents of destination buffer to first key */
  4.1233 -    memcpy(dst, dstbuf, sizeof(pgl_areakey));
  4.1234 -  }
  4.1235 -  /* if not, keys are point keys */
  4.1236 -  else {
  4.1237 -    pgl_pointkey dstbuf = { 0, };  /* destination buffer (cleared) */
  4.1238 -    /* iterate through key bits */
  4.1239 -    for (i=0; i<depth; i++) {
  4.1240 -      /* break if bit differs in both keys */
  4.1241 -      if (PGL_KEY_LATLONBIT(dst, i)) {
  4.1242 -        if (!PGL_KEY_LATLONBIT(src, i)) break;
  4.1243 -        /* but set bit in destination buffer if bit is set in both keys */
  4.1244 -        dstbuf[i/8] |= 0x80 >> (i%8);
  4.1245 -      } else if (PGL_KEY_LATLONBIT(src, i)) break;
  4.1246 -    }
  4.1247 -    /* set common node depth (type bit = 0) */
  4.1248 -    dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i;
  4.1249 -    /* copy contents of destination buffer to first key */
  4.1250 -    memcpy(dst, dstbuf, sizeof(pgl_pointkey));
  4.1251 -  }
  4.1252 -}
  4.1253 -
  4.1254 -/* determine center(!) boundaries and radius estimation of index key */
  4.1255 -static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) {
  4.1256 -  int i;
  4.1257 -  /* determine node depth */
  4.1258 -  int depth = PGL_KEY_NODEDEPTH(key);
  4.1259 -  /* center point of possible result */
  4.1260 -  double lat = 0;
  4.1261 -  double lon = 0;
  4.1262 -  /* maximum distance of real center point from key center */
  4.1263 -  double dlat = 90;
  4.1264 -  double dlon = 180;
  4.1265 -  /* maximum radius of contained objects */
  4.1266 -  double radius = 0;  /* always return zero for point index keys */
  4.1267 -  /* check if key is area key */
  4.1268 -  if (PGL_KEY_IS_AREAKEY(key)) {
  4.1269 -    /* get logarithmic object size */
  4.1270 -    int objsize = PGL_KEY_OBJSIZE(key);
  4.1271 -    /* handle special cases for empty objects (universal and empty keys) */
  4.1272 -    if (objsize == PGL_KEY_OBJSIZE_EMPTY) {
  4.1273 -      pgl_box_set_empty(box);
  4.1274 -      return 0;
  4.1275 -    } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) {
  4.1276 -      box->lat_min = -90;
  4.1277 -      box->lat_max =  90;
  4.1278 -      box->lon_min = -180;
  4.1279 -      box->lon_max =  180;
  4.1280 -      return 0;  /* any value >= 0 would do */
  4.1281 -    }
  4.1282 -    /* calculate maximum possible radius of objects covered by the given key */
  4.1283 -    if (objsize == 0) radius = INFINITY;
  4.1284 -    else {
  4.1285 -      radius = PGL_AREAKEY_REFOBJSIZE;
  4.1286 -      while (--objsize) radius /= M_SQRT2;
  4.1287 -    }
  4.1288 -    /* iterate over latitude and longitude bits in key */
  4.1289 -    /* (every second bit is a latitude or longitude bit) */
  4.1290 -    for (i=0; i<depth/2; i++) {
  4.1291 -      /* check if latitude bit */
  4.1292 -      if (i%2 == 0) {
  4.1293 -        /* cut latitude dimension in half */
  4.1294 -        dlat /= 2;
  4.1295 -        /* increase center latitude if bit is 1, otherwise decrease */
  4.1296 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  4.1297 -        else lat -= dlat;
  4.1298 -      }
  4.1299 -      /* otherwise longitude bit */
  4.1300 -      else {
  4.1301 -        /* cut longitude dimension in half */
  4.1302 -        dlon /= 2;
  4.1303 -        /* increase center longitude if bit is 1, otherwise decrease */
  4.1304 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  4.1305 -        else lon -= dlon;
  4.1306 -      }
  4.1307 -    }
  4.1308 -  }
  4.1309 -  /* if not, keys are point keys */
  4.1310 -  else {
  4.1311 -    /* iterate over all bits in key */
  4.1312 -    for (i=0; i<depth; i++) {
  4.1313 -      /* check if latitude bit */
  4.1314 -      if (i%2 == 0) {
  4.1315 -        /* cut latitude dimension in half */
  4.1316 -        dlat /= 2;
  4.1317 -        /* increase center latitude if bit is 1, otherwise decrease */
  4.1318 -        if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
  4.1319 -        else lat -= dlat;
  4.1320 -      }
  4.1321 -      /* otherwise longitude bit */
  4.1322 -      else {
  4.1323 -        /* cut longitude dimension in half */
  4.1324 -        dlon /= 2;
  4.1325 -        /* increase center longitude if bit is 1, otherwise decrease */
  4.1326 -        if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
  4.1327 -        else lon -= dlon;
  4.1328 -      }
  4.1329 -    }
  4.1330 -  }
  4.1331 -  /* calculate boundaries from center point and remaining dlat and dlon */
  4.1332 -  /* (return values through pointer to box) */
  4.1333 -  box->lat_min = lat - dlat;
  4.1334 -  box->lat_max = lat + dlat;
  4.1335 -  box->lon_min = lon - dlon;
  4.1336 -  box->lon_max = lon + dlon;
  4.1337 -  /* return radius (as a function return value) */
  4.1338 -  return radius;
  4.1339 -}
  4.1340 -
  4.1341 -/* estimator function for distance between point and index key */
  4.1342 -/* always returns a smaller value than actually correct or zero */
  4.1343 -static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
  4.1344 -  pgl_box box;  /* center(!) bounding box of area index key */
  4.1345 -  /* calculate center(!) bounding box and maximum radius of objects covered
  4.1346 -     by area index key (radius is zero for point index keys) */
  4.1347 -  double distance = pgl_key_to_box(key, &box);
  4.1348 -  /* calculate estimated distance between bounding box of center point of
  4.1349 -     indexed object and point passed as second argument, then substract maximum
  4.1350 -     radius of objects covered by index key */
  4.1351 -  distance = pgl_estimate_point_box_distance(point, &box) - distance;
  4.1352 -  /* truncate negative results to zero */
  4.1353 -  if (distance <= 0) distance = 0;
  4.1354 -  /* return result */
  4.1355 -  return distance;
  4.1356 -}
  4.1357 -
  4.1358 -
  4.1359 -/*---------------------------------*
  4.1360 - *  helper functions for text I/O  *
  4.1361 - *---------------------------------*/
  4.1362 -
  4.1363 -#define PGL_NUMBUFLEN 64  /* buffer size for number to string conversion */
  4.1364 -
  4.1365 -/* convert floating point number to string (round-trip safe) */
  4.1366 -static void pgl_print_float(char *buf, double flt) {
  4.1367 -  /* check if number is integral */
  4.1368 -  if (trunc(flt) == flt) {
  4.1369 -    /* for integral floats use maximum precision */
  4.1370 -    snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  4.1371 -  } else {
  4.1372 -    /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */
  4.1373 -    snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt);
  4.1374 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt);
  4.1375 -    if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
  4.1376 -  }
  4.1377 -}
  4.1378 -
  4.1379 -/* convert latitude floating point number (in degrees) to string */
  4.1380 -static void pgl_print_lat(char *buf, double lat) {
  4.1381 -  if (signbit(lat)) {
  4.1382 -    /* treat negative latitudes (including -0) as south */
  4.1383 -    snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat);
  4.1384 -  } else {
  4.1385 -    /* treat positive latitudes (including +0) as north */
  4.1386 -    snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat);
  4.1387 -  }
  4.1388 -}
  4.1389 -
  4.1390 -/* convert longitude floating point number (in degrees) to string */
  4.1391 -static void pgl_print_lon(char *buf, double lon) {
  4.1392 -  if (signbit(lon)) {
  4.1393 -    /* treat negative longitudes (including -0) as west */
  4.1394 -    snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon);
  4.1395 -  } else {
  4.1396 -    /* treat positive longitudes (including +0) as east */
  4.1397 -    snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon);
  4.1398 -  }
  4.1399 -}
  4.1400 -
  4.1401 -/* bit masks used as return value of pgl_scan() function */
  4.1402 -#define PGL_SCAN_NONE 0      /* no value has been parsed */
  4.1403 -#define PGL_SCAN_LAT (1<<0)  /* latitude has been parsed */
  4.1404 -#define PGL_SCAN_LON (1<<1)  /* longitude has been parsed */
  4.1405 -#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON)  /* bitwise OR of both */
  4.1406 -
  4.1407 -/* parse a coordinate (can be latitude or longitude) */
  4.1408 -static int pgl_scan(char **str, double *lat, double *lon) {
  4.1409 -  double val;
  4.1410 -  int len;
  4.1411 -  if (
  4.1412 -    sscanf(*str, " N %lf %n", &val, &len) ||
  4.1413 -    sscanf(*str, " n %lf %n", &val, &len)
  4.1414 -  ) {
  4.1415 -    *str += len; *lat = val; return PGL_SCAN_LAT;
  4.1416 -  }
  4.1417 -  if (
  4.1418 -    sscanf(*str, " S %lf %n", &val, &len) ||
  4.1419 -    sscanf(*str, " s %lf %n", &val, &len)
  4.1420 -  ) {
  4.1421 -    *str += len; *lat = -val; return PGL_SCAN_LAT;
  4.1422 -  }
  4.1423 -  if (
  4.1424 -    sscanf(*str, " E %lf %n", &val, &len) ||
  4.1425 -    sscanf(*str, " e %lf %n", &val, &len)
  4.1426 -  ) {
  4.1427 -    *str += len; *lon = val; return PGL_SCAN_LON;
  4.1428 -  }
  4.1429 -  if (
  4.1430 -    sscanf(*str, " W %lf %n", &val, &len) ||
  4.1431 -    sscanf(*str, " w %lf %n", &val, &len)
  4.1432 -  ) {
  4.1433 -    *str += len; *lon = -val; return PGL_SCAN_LON;
  4.1434 -  }
  4.1435 -  return PGL_SCAN_NONE;
  4.1436 -}
  4.1437 -
  4.1438 -
  4.1439 -/*-----------------*
  4.1440 - *  SQL functions  *
  4.1441 - *-----------------*/
  4.1442 -
  4.1443 -/* Note: These function names use "epoint", "ebox", etc. notation here instead
  4.1444 -   of "point", "box", etc. in order to distinguish them from any previously
  4.1445 -   defined functions. */
  4.1446 -
  4.1447 -/* function needed for dummy types and/or not implemented features */
  4.1448 -PG_FUNCTION_INFO_V1(pgl_notimpl);
  4.1449 -Datum pgl_notimpl(PG_FUNCTION_ARGS) {
  4.1450 -  ereport(ERROR, (errmsg("not implemented by pgLatLon")));
  4.1451 -}
  4.1452 -
  4.1453 -/* set point to latitude and longitude (including checks) */
  4.1454 -static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) {
  4.1455 -  /* reject infinite or NaN values */
  4.1456 -  if (!isfinite(lat) || !isfinite(lon)) {
  4.1457 -    ereport(ERROR, (
  4.1458 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1459 -      errmsg("epoint requires finite coordinates")
  4.1460 -    ));
  4.1461 -  }
  4.1462 -  /* check latitude bounds */
  4.1463 -  if (lat < -90) {
  4.1464 -    ereport(WARNING, (errmsg("latitude exceeds south pole")));
  4.1465 -    lat = -90;
  4.1466 -  } else if (lat > 90) {
  4.1467 -    ereport(WARNING, (errmsg("latitude exceeds north pole")));
  4.1468 -    lat = 90;
  4.1469 -  }
  4.1470 -  /* check longitude bounds */
  4.1471 -  if (lon < -180) {
  4.1472 -    ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized")));
  4.1473 -    lon += 360 - trunc(lon / 360) * 360;
  4.1474 -  } else if (lon > 180) {
  4.1475 -    ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized")));
  4.1476 -    lon -= 360 + trunc(lon / 360) * 360;
  4.1477 -  }
  4.1478 -  /* store rounded latitude/longitude values for round-trip safety */
  4.1479 -  point->lat = pgl_round(lat);
  4.1480 -  point->lon = pgl_round(lon);
  4.1481 -}
  4.1482 -
  4.1483 -/* create point ("epoint" in SQL) from latitude and longitude */
  4.1484 -PG_FUNCTION_INFO_V1(pgl_create_epoint);
  4.1485 -Datum pgl_create_epoint(PG_FUNCTION_ARGS) {
  4.1486 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  4.1487 -  pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1));
  4.1488 -  PG_RETURN_POINTER(point);
  4.1489 -}
  4.1490 -
  4.1491 -/* parse point ("epoint" in SQL) */
  4.1492 -/* format: '[NS]<float> [EW]<float>' */
  4.1493 -PG_FUNCTION_INFO_V1(pgl_epoint_in);
  4.1494 -Datum pgl_epoint_in(PG_FUNCTION_ARGS) {
  4.1495 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  4.1496 -  char *strptr = str;  /* current position within string */
  4.1497 -  int done = 0;        /* bit mask storing if latitude or longitude was read */
  4.1498 -  double lat, lon;     /* parsed values as double precision floats */
  4.1499 -  pgl_point *point;    /* return value (to be palloc'ed) */
  4.1500 -  /* parse two floats (each latitude or longitude) separated by white-space */
  4.1501 -  done |= pgl_scan(&strptr, &lat, &lon);
  4.1502 -  if (strptr != str && isspace(strptr[-1])) {
  4.1503 -    done |= pgl_scan(&strptr, &lat, &lon);
  4.1504 -  }
  4.1505 -  /* require end of string, and latitude and longitude parsed successfully */
  4.1506 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  4.1507 -    ereport(ERROR, (
  4.1508 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1509 -      errmsg("invalid input syntax for type epoint: \"%s\"", str)
  4.1510 -    ));
  4.1511 -  }
  4.1512 -  /* allocate memory for result */
  4.1513 -  point = (pgl_point *)palloc(sizeof(pgl_point));
  4.1514 -  /* set latitude and longitude (and perform checks) */
  4.1515 -  pgl_epoint_set_latlon(point, lat, lon);
  4.1516 -  /* return result */
  4.1517 -  PG_RETURN_POINTER(point);
  4.1518 -}
  4.1519 -
  4.1520 -/* create box ("ebox" in SQL) that is empty */
  4.1521 -PG_FUNCTION_INFO_V1(pgl_create_empty_ebox);
  4.1522 -Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) {
  4.1523 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1524 -  pgl_box_set_empty(box);
  4.1525 -  PG_RETURN_POINTER(box);
  4.1526 -}
  4.1527 -
  4.1528 -/* set box to given boundaries (including checks) */
  4.1529 -static void pgl_ebox_set_boundaries(
  4.1530 -  pgl_box *box,
  4.1531 -  double lat_min, double lat_max, double lon_min, double lon_max
  4.1532 -) {
  4.1533 -  /* if minimum latitude is greater than maximum latitude, return empty box */
  4.1534 -  if (lat_min > lat_max) {
  4.1535 -    pgl_box_set_empty(box);
  4.1536 -    return;
  4.1537 -  }
  4.1538 -  /* otherwise reject infinite or NaN values */
  4.1539 -  if (
  4.1540 -    !isfinite(lat_min) || !isfinite(lat_max) ||
  4.1541 -    !isfinite(lon_min) || !isfinite(lon_max)
  4.1542 -  ) {
  4.1543 -    ereport(ERROR, (
  4.1544 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1545 -      errmsg("ebox requires finite coordinates")
  4.1546 -    ));
  4.1547 -  }
  4.1548 -  /* check latitude bounds */
  4.1549 -  if (lat_max < -90) {
  4.1550 -    ereport(WARNING, (errmsg("northern latitude exceeds south pole")));
  4.1551 -    lat_max = -90;
  4.1552 -  } else if (lat_max > 90) {
  4.1553 -    ereport(WARNING, (errmsg("northern latitude exceeds north pole")));
  4.1554 -    lat_max = 90;
  4.1555 -  }
  4.1556 -  if (lat_min < -90) {
  4.1557 -    ereport(WARNING, (errmsg("southern latitude exceeds south pole")));
  4.1558 -    lat_min = -90;
  4.1559 -  } else if (lat_min > 90) {
  4.1560 -    ereport(WARNING, (errmsg("southern latitude exceeds north pole")));
  4.1561 -    lat_min = 90;
  4.1562 -  }
  4.1563 -  /* check if all longitudes are included */
  4.1564 -  if (lon_max - lon_min >= 360) {
  4.1565 -    if (lon_max - lon_min > 360) ereport(WARNING, (
  4.1566 -      errmsg("longitude coverage greater than 360 degrees")
  4.1567 -    ));
  4.1568 -    lon_min = -180;
  4.1569 -    lon_max = 180;
  4.1570 -  } else {
  4.1571 -    /* normalize longitude bounds */
  4.1572 -    if      (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360;
  4.1573 -    else if (lon_min >  180) lon_min -= 360 + trunc(lon_min / 360) * 360;
  4.1574 -    if      (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360;
  4.1575 -    else if (lon_max >  180) lon_max -= 360 + trunc(lon_max / 360) * 360;
  4.1576 -  }
  4.1577 -  /* store rounded latitude/longitude values for round-trip safety */
  4.1578 -  box->lat_min = pgl_round(lat_min);
  4.1579 -  box->lat_max = pgl_round(lat_max);
  4.1580 -  box->lon_min = pgl_round(lon_min);
  4.1581 -  box->lon_max = pgl_round(lon_max);
  4.1582 -  /* ensure that rounding does not change orientation */
  4.1583 -  if (lon_min > lon_max && box->lon_min == box->lon_max) {
  4.1584 -    box->lon_min = -180;
  4.1585 -    box->lon_max = 180;
  4.1586 -  }
  4.1587 -}
  4.1588 -
  4.1589 -/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */
  4.1590 -PG_FUNCTION_INFO_V1(pgl_create_ebox);
  4.1591 -Datum pgl_create_ebox(PG_FUNCTION_ARGS) {
  4.1592 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1593 -  pgl_ebox_set_boundaries(
  4.1594 -    box,
  4.1595 -    PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1),
  4.1596 -    PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3)
  4.1597 -  );
  4.1598 -  PG_RETURN_POINTER(box);
  4.1599 -}
  4.1600 -
  4.1601 -/* create box ("ebox" in SQL) from two points ("epoint"s) */
  4.1602 -/* (can not be used to cover a longitude range of more than 120 degrees) */
  4.1603 -PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints);
  4.1604 -Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) {
  4.1605 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  4.1606 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  4.1607 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1608 -  double lat_min, lat_max, lon_min, lon_max;
  4.1609 -  double dlon;  /* longitude range (delta longitude) */
  4.1610 -  /* order latitude and longitude boundaries */
  4.1611 -  if (point2->lat < point1->lat) {
  4.1612 -    lat_min = point2->lat;
  4.1613 -    lat_max = point1->lat;
  4.1614 -  } else {
  4.1615 -    lat_min = point1->lat;
  4.1616 -    lat_max = point2->lat;
  4.1617 -  }
  4.1618 -  if (point2->lon < point1->lon) {
  4.1619 -    lon_min = point2->lon;
  4.1620 -    lon_max = point1->lon;
  4.1621 -  } else {
  4.1622 -    lon_min = point1->lon;
  4.1623 -    lon_max = point2->lon;
  4.1624 -  }
  4.1625 -  /* calculate longitude range (round to avoid floating point errors) */
  4.1626 -  dlon = pgl_round(lon_max - lon_min);
  4.1627 -  /* determine east-west direction */
  4.1628 -  if (dlon >= 240) {
  4.1629 -    /* assume that 180th meridian is crossed and swap min/max longitude */
  4.1630 -    double swap = lon_min; lon_min = lon_max; lon_max = swap;
  4.1631 -  } else if (dlon > 120) {
  4.1632 -    /* unclear orientation since delta longitude > 120 */
  4.1633 -    ereport(ERROR, (
  4.1634 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1635 -      errmsg("can not determine east/west orientation for ebox")
  4.1636 -    ));
  4.1637 -  }
  4.1638 -  /* use boundaries to setup box (and perform checks) */
  4.1639 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  4.1640 -  /* return result */
  4.1641 -  PG_RETURN_POINTER(box);
  4.1642 -}
  4.1643 -
  4.1644 -/* parse box ("ebox" in SQL) */
  4.1645 -/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>'
  4.1646 -       or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */
  4.1647 -PG_FUNCTION_INFO_V1(pgl_ebox_in);
  4.1648 -Datum pgl_ebox_in(PG_FUNCTION_ARGS) {
  4.1649 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  4.1650 -  char *str_lower;     /* lower case version of input string */
  4.1651 -  char *strptr;        /* current position within string */
  4.1652 -  int valid;           /* number of valid chars */
  4.1653 -  int done;            /* specifies if latitude or longitude was read */
  4.1654 -  double val;          /* temporary variable */
  4.1655 -  int lat_count = 0;   /* count of latitude values parsed */
  4.1656 -  int lon_count = 0;   /* count of longitufde values parsed */
  4.1657 -  double lat_min, lat_max, lon_min, lon_max;  /* see pgl_box struct */
  4.1658 -  pgl_box *box;        /* return value (to be palloc'ed) */
  4.1659 -  /* lowercase input */
  4.1660 -  str_lower = psprintf("%s", str);
  4.1661 -  for (strptr=str_lower; *strptr; strptr++) {
  4.1662 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  4.1663 -  }
  4.1664 -  /* reset reading position to start of (lowercase) string */
  4.1665 -  strptr = str_lower;
  4.1666 -  /* check if empty box */
  4.1667 -  valid = 0;
  4.1668 -  sscanf(strptr, " empty %n", &valid);
  4.1669 -  if (valid && strptr[valid] == 0) {
  4.1670 -    /* allocate and return empty box */
  4.1671 -    box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1672 -    pgl_box_set_empty(box);
  4.1673 -    PG_RETURN_POINTER(box);
  4.1674 -  }
  4.1675 -  /* demand four blocks separated by whitespace */
  4.1676 -  valid = 0;
  4.1677 -  sscanf(strptr, " %*s %*s %*s %*s %n", &valid);
  4.1678 -  /* if four blocks separated by whitespace exist, parse those blocks */
  4.1679 -  if (strptr[valid] == 0) while (strptr[0]) {
  4.1680 -    /* parse either latitude or longitude (whichever found in input string) */
  4.1681 -    done = pgl_scan(&strptr, &val, &val);
  4.1682 -    /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */
  4.1683 -    if (done == PGL_SCAN_LAT) {
  4.1684 -      if (!lat_count) lat_min = val; else lat_max = val;
  4.1685 -      lat_count++;
  4.1686 -    } else if (done == PGL_SCAN_LON) {
  4.1687 -      if (!lon_count) lon_min = val; else lon_max = val;
  4.1688 -      lon_count++;
  4.1689 -    } else {
  4.1690 -      break;
  4.1691 -    }
  4.1692 -  }
  4.1693 -  /* require end of string, and two latitude and two longitude values */
  4.1694 -  if (strptr[0] || lat_count != 2 || lon_count != 2) {
  4.1695 -    ereport(ERROR, (
  4.1696 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1697 -      errmsg("invalid input syntax for type ebox: \"%s\"", str)
  4.1698 -    ));
  4.1699 -  }
  4.1700 -  /* free lower case string */
  4.1701 -  pfree(str_lower);
  4.1702 -  /* order boundaries (maximum greater than minimum) */
  4.1703 -  if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; }
  4.1704 -  if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; }
  4.1705 -  /* allocate memory for result */
  4.1706 -  box = (pgl_box *)palloc(sizeof(pgl_box));
  4.1707 -  /* set boundaries (and perform checks) */
  4.1708 -  pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
  4.1709 -  /* return result */
  4.1710 -  PG_RETURN_POINTER(box);
  4.1711 -}
  4.1712 -
  4.1713 -/* set circle to given latitude, longitude, and radius (including checks) */
  4.1714 -static void pgl_ecircle_set_latlon_radius(
  4.1715 -  pgl_circle *circle, double lat, double lon, double radius
  4.1716 -) {
  4.1717 -  /* set center point (including checks) */
  4.1718 -  pgl_epoint_set_latlon(&(circle->center), lat, lon);
  4.1719 -  /* handle non-positive radius */
  4.1720 -  if (isnan(radius)) {
  4.1721 -    ereport(ERROR, (
  4.1722 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1723 -      errmsg("invalid radius for ecircle")
  4.1724 -    ));
  4.1725 -  }
  4.1726 -  if (radius == 0) radius = 0;  /* avoids -0 */
  4.1727 -  else if (radius < 0) {
  4.1728 -    if (isfinite(radius)) {
  4.1729 -      ereport(NOTICE, (errmsg("negative radius converted to minus infinity")));
  4.1730 -    }
  4.1731 -    radius = -INFINITY;
  4.1732 -  }
  4.1733 -  /* store radius (round-trip safety is ensured by pgl_print_float) */
  4.1734 -  circle->radius = radius;
  4.1735 -}
  4.1736 -
  4.1737 -/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */
  4.1738 -PG_FUNCTION_INFO_V1(pgl_create_ecircle);
  4.1739 -Datum pgl_create_ecircle(PG_FUNCTION_ARGS) {
  4.1740 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  4.1741 -  pgl_ecircle_set_latlon_radius(
  4.1742 -    circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2)
  4.1743 -  );
  4.1744 -  PG_RETURN_POINTER(circle);
  4.1745 -}
  4.1746 -
  4.1747 -/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */
  4.1748 -PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint);
  4.1749 -Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) {
  4.1750 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.1751 -  double radius = PG_GETARG_FLOAT8(1);
  4.1752 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  4.1753 -  /* set latitude, longitude, radius (and perform checks) */
  4.1754 -  pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius);
  4.1755 -  /* return result */
  4.1756 -  PG_RETURN_POINTER(circle);
  4.1757 -}
  4.1758 -
  4.1759 -/* parse circle ("ecircle" in SQL) */
  4.1760 -/* format: '[NS]<float> [EW]<float> <float>' */
  4.1761 -PG_FUNCTION_INFO_V1(pgl_ecircle_in);
  4.1762 -Datum pgl_ecircle_in(PG_FUNCTION_ARGS) {
  4.1763 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  4.1764 -  char *strptr = str;       /* current position within string */
  4.1765 -  double lat, lon, radius;  /* parsed values as double precision flaots */
  4.1766 -  int valid = 0;            /* number of valid chars */
  4.1767 -  int done = 0;             /* stores if latitude and/or longitude was read */
  4.1768 -  pgl_circle *circle;       /* return value (to be palloc'ed) */
  4.1769 -  /* demand three blocks separated by whitespace */
  4.1770 -  sscanf(strptr, " %*s %*s %*s %n", &valid);
  4.1771 -  /* if three blocks separated by whitespace exist, parse those blocks */
  4.1772 -  if (strptr[valid] == 0) {
  4.1773 -    /* parse latitude and longitude */
  4.1774 -    done |= pgl_scan(&strptr, &lat, &lon);
  4.1775 -    done |= pgl_scan(&strptr, &lat, &lon);
  4.1776 -    /* parse radius (while incrementing strptr by number of bytes parsed) */
  4.1777 -    valid = 0;
  4.1778 -    if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid;
  4.1779 -  }
  4.1780 -  /* require end of string and both latitude and longitude being parsed */
  4.1781 -  if (strptr[0] || done != PGL_SCAN_LATLON) {
  4.1782 -    ereport(ERROR, (
  4.1783 -      errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1784 -      errmsg("invalid input syntax for type ecircle: \"%s\"", str)
  4.1785 -    ));
  4.1786 -  }
  4.1787 -  /* allocate memory for result */
  4.1788 -  circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  4.1789 -  /* set latitude, longitude, radius (and perform checks) */
  4.1790 -  pgl_ecircle_set_latlon_radius(circle, lat, lon, radius);
  4.1791 -  /* return result */
  4.1792 -  PG_RETURN_POINTER(circle);
  4.1793 -}
  4.1794 -
  4.1795 -/* parse cluster ("ecluster" in SQL) */
  4.1796 -PG_FUNCTION_INFO_V1(pgl_ecluster_in);
  4.1797 -Datum pgl_ecluster_in(PG_FUNCTION_ARGS) {
  4.1798 -  int i;
  4.1799 -  char *str = PG_GETARG_CSTRING(0);  /* input string */
  4.1800 -  char *str_lower;         /* lower case version of input string */
  4.1801 -  char *strptr;            /* pointer to current reading position of input */
  4.1802 -  int npoints_total = 0;   /* total number of points in cluster */
  4.1803 -  int nentries = 0;        /* total number of entries */
  4.1804 -  pgl_newentry *entries;   /* array of pgl_newentry to create pgl_cluster */
  4.1805 -  int entries_buflen = 4;  /* maximum number of elements in entries array */
  4.1806 -  int valid;               /* number of valid chars processed */
  4.1807 -  double lat, lon;         /* latitude and longitude of parsed point */
  4.1808 -  int entrytype;           /* current entry type */
  4.1809 -  int npoints;             /* number of points in current entry */
  4.1810 -  pgl_point *points;       /* array of pgl_point for pgl_newentry */
  4.1811 -  int points_buflen;       /* maximum number of elements in points array */
  4.1812 -  int done;                /* return value of pgl_scan function */
  4.1813 -  pgl_cluster *cluster;    /* created cluster */
  4.1814 -  /* lowercase input */
  4.1815 -  str_lower = psprintf("%s", str);
  4.1816 -  for (strptr=str_lower; *strptr; strptr++) {
  4.1817 -    if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
  4.1818 -  }
  4.1819 -  /* reset reading position to start of (lowercase) string */
  4.1820 -  strptr = str_lower;
  4.1821 -  /* allocate initial buffer for entries */
  4.1822 -  entries = palloc(entries_buflen * sizeof(pgl_newentry));
  4.1823 -  /* parse until end of string */
  4.1824 -  while (strptr[0]) {
  4.1825 -    /* require previous white-space or closing parenthesis before next token */
  4.1826 -    if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') {
  4.1827 -      goto pgl_ecluster_in_error;
  4.1828 -    }
  4.1829 -    /* ignore token "empty" */
  4.1830 -    valid = 0; sscanf(strptr, " empty %n", &valid);
  4.1831 -    if (valid) { strptr += valid; continue; }
  4.1832 -    /* test for "point" token */
  4.1833 -    valid = 0; sscanf(strptr, " point ( %n", &valid);
  4.1834 -    if (valid) {
  4.1835 -      strptr += valid;
  4.1836 -      entrytype = PGL_ENTRY_POINT;
  4.1837 -      goto pgl_ecluster_in_type_ok;
  4.1838 -    }
  4.1839 -    /* test for "path" token */
  4.1840 -    valid = 0; sscanf(strptr, " path ( %n", &valid);
  4.1841 -    if (valid) {
  4.1842 -      strptr += valid;
  4.1843 -      entrytype = PGL_ENTRY_PATH;
  4.1844 -      goto pgl_ecluster_in_type_ok;
  4.1845 -    }
  4.1846 -    /* test for "outline" token */
  4.1847 -    valid = 0; sscanf(strptr, " outline ( %n", &valid);
  4.1848 -    if (valid) {
  4.1849 -      strptr += valid;
  4.1850 -      entrytype = PGL_ENTRY_OUTLINE;
  4.1851 -      goto pgl_ecluster_in_type_ok;
  4.1852 -    }
  4.1853 -    /* test for "polygon" token */
  4.1854 -    valid = 0; sscanf(strptr, " polygon ( %n", &valid);
  4.1855 -    if (valid) {
  4.1856 -      strptr += valid;
  4.1857 -      entrytype = PGL_ENTRY_POLYGON;
  4.1858 -      goto pgl_ecluster_in_type_ok;
  4.1859 -    }
  4.1860 -    /* error if no valid token found */
  4.1861 -    goto pgl_ecluster_in_error;
  4.1862 -    pgl_ecluster_in_type_ok:
  4.1863 -    /* check if pgl_newentry array needs to grow */
  4.1864 -    if (nentries == entries_buflen) {
  4.1865 -      pgl_newentry *newbuf;
  4.1866 -      entries_buflen *= 2;
  4.1867 -      newbuf = palloc(entries_buflen * sizeof(pgl_newentry));
  4.1868 -      memcpy(newbuf, entries, nentries * sizeof(pgl_newentry));
  4.1869 -      pfree(entries);
  4.1870 -      entries = newbuf;
  4.1871 -    }
  4.1872 -    /* reset number of points for current entry */
  4.1873 -    npoints = 0;
  4.1874 -    /* allocate array for points */
  4.1875 -    points_buflen = 4;
  4.1876 -    points = palloc(points_buflen * sizeof(pgl_point));
  4.1877 -    /* parse until closing parenthesis */
  4.1878 -    while (strptr[0] != ')') {
  4.1879 -      /* error on unexpected end of string */
  4.1880 -      if (strptr[0] == 0) goto pgl_ecluster_in_error;
  4.1881 -      /* mark neither latitude nor longitude as read */
  4.1882 -      done = PGL_SCAN_NONE;
  4.1883 -      /* require white-space before second, third, etc. point */
  4.1884 -      if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  4.1885 -      /* scan latitude (or longitude) */
  4.1886 -      done |= pgl_scan(&strptr, &lat, &lon);
  4.1887 -      /* require white-space before second coordinate */
  4.1888 -      if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
  4.1889 -      /* scan longitude (or latitude) */
  4.1890 -      done |= pgl_scan(&strptr, &lat, &lon);
  4.1891 -      /* error unless both latitude and longitude were parsed */
  4.1892 -      if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error;
  4.1893 -      /* throw error if number of points is too high */
  4.1894 -      if (npoints_total == PGL_CLUSTER_MAXPOINTS) {
  4.1895 -        ereport(ERROR, (
  4.1896 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1897 -          errmsg(
  4.1898 -            "too many points for ecluster entry (maximum %i)",
  4.1899 -            PGL_CLUSTER_MAXPOINTS
  4.1900 -          )
  4.1901 -        ));
  4.1902 -      }
  4.1903 -      /* check if pgl_point array needs to grow */
  4.1904 -      if (npoints == points_buflen) {
  4.1905 -        pgl_point *newbuf;
  4.1906 -        points_buflen *= 2;
  4.1907 -        newbuf = palloc(points_buflen * sizeof(pgl_point));
  4.1908 -        memcpy(newbuf, points, npoints * sizeof(pgl_point));
  4.1909 -        pfree(points);
  4.1910 -        points = newbuf;
  4.1911 -      }
  4.1912 -      /* append point to pgl_point array (includes checks) */
  4.1913 -      pgl_epoint_set_latlon(&(points[npoints++]), lat, lon);
  4.1914 -      /* increase total number of points */
  4.1915 -      npoints_total++;
  4.1916 -    }
  4.1917 -    /* error if entry has no points */
  4.1918 -    if (!npoints) goto pgl_ecluster_in_error;
  4.1919 -    /* entries with one point are automatically of type "point" */
  4.1920 -    if (npoints == 1) entrytype = PGL_ENTRY_POINT;
  4.1921 -    /* if entries have more than one point */
  4.1922 -    else {
  4.1923 -      /* throw error if entry type is "point" */
  4.1924 -      if (entrytype == PGL_ENTRY_POINT) {
  4.1925 -        ereport(ERROR, (
  4.1926 -          errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1927 -          errmsg("invalid input syntax for type ecluster (point entry with more than one point)")
  4.1928 -        ));
  4.1929 -      }
  4.1930 -      /* coerce outlines and polygons with more than 2 points to be a path */
  4.1931 -      if (npoints == 2) entrytype = PGL_ENTRY_PATH;
  4.1932 -    }
  4.1933 -    /* append entry to pgl_newentry array */
  4.1934 -    entries[nentries].entrytype = entrytype;
  4.1935 -    entries[nentries].npoints = npoints;
  4.1936 -    entries[nentries].points = points;
  4.1937 -    nentries++;
  4.1938 -    /* consume closing parenthesis */
  4.1939 -    strptr++;
  4.1940 -    /* consume white-space */
  4.1941 -    while (isspace(strptr[0])) strptr++;
  4.1942 -  }
  4.1943 -  /* free lower case string */
  4.1944 -  pfree(str_lower);
  4.1945 -  /* create cluster from pgl_newentry array */
  4.1946 -  cluster = pgl_new_cluster(nentries, entries);
  4.1947 -  /* free pgl_newentry array */
  4.1948 -  for (i=0; i<nentries; i++) pfree(entries[i].points);
  4.1949 -  pfree(entries);
  4.1950 -  /* set bounding circle of cluster and check east/west orientation */
  4.1951 -  if (!pgl_finalize_cluster(cluster)) {
  4.1952 -    ereport(ERROR, (
  4.1953 -      errcode(ERRCODE_DATA_EXCEPTION),
  4.1954 -      errmsg("can not determine east/west orientation for ecluster"),
  4.1955 -      errhint("Ensure that each entry has a longitude span of less than 180 degrees.")
  4.1956 -    ));
  4.1957 -  }
  4.1958 -  /* return cluster */
  4.1959 -  PG_RETURN_POINTER(cluster);
  4.1960 -  /* code to throw error */
  4.1961 -  pgl_ecluster_in_error:
  4.1962 -  ereport(ERROR, (
  4.1963 -    errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
  4.1964 -    errmsg("invalid input syntax for type ecluster: \"%s\"", str)
  4.1965 -  ));
  4.1966 -}
  4.1967 -
  4.1968 -/* convert point ("epoint") to string representation */
  4.1969 -PG_FUNCTION_INFO_V1(pgl_epoint_out);
  4.1970 -Datum pgl_epoint_out(PG_FUNCTION_ARGS) {
  4.1971 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.1972 -  char latstr[PGL_NUMBUFLEN];
  4.1973 -  char lonstr[PGL_NUMBUFLEN];
  4.1974 -  pgl_print_lat(latstr, point->lat);
  4.1975 -  pgl_print_lon(lonstr, point->lon);
  4.1976 -  PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr));
  4.1977 -}
  4.1978 -
  4.1979 -/* convert box ("ebox") to string representation */
  4.1980 -PG_FUNCTION_INFO_V1(pgl_ebox_out);
  4.1981 -Datum pgl_ebox_out(PG_FUNCTION_ARGS) {
  4.1982 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  4.1983 -  double lon_min = box->lon_min;
  4.1984 -  double lon_max = box->lon_max;
  4.1985 -  char lat_min_str[PGL_NUMBUFLEN];
  4.1986 -  char lat_max_str[PGL_NUMBUFLEN];
  4.1987 -  char lon_min_str[PGL_NUMBUFLEN];
  4.1988 -  char lon_max_str[PGL_NUMBUFLEN];
  4.1989 -  /* return string "empty" if box is set to be empty */
  4.1990 -  if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty");
  4.1991 -  /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */
  4.1992 -  /* (required since pgl_box_in orders the longitude boundaries) */
  4.1993 -  if (lon_min > lon_max) {
  4.1994 -    if (lon_min + lon_max >= 0) lon_min -= 360;
  4.1995 -    else lon_max += 360;
  4.1996 -  }
  4.1997 -  /* format and return result */
  4.1998 -  pgl_print_lat(lat_min_str, box->lat_min);
  4.1999 -  pgl_print_lat(lat_max_str, box->lat_max);
  4.2000 -  pgl_print_lon(lon_min_str, lon_min);
  4.2001 -  pgl_print_lon(lon_max_str, lon_max);
  4.2002 -  PG_RETURN_CSTRING(psprintf(
  4.2003 -    "%s %s %s %s",
  4.2004 -    lat_min_str, lon_min_str, lat_max_str, lon_max_str
  4.2005 -  ));
  4.2006 -}
  4.2007 -
  4.2008 -/* convert circle ("ecircle") to string representation */
  4.2009 -PG_FUNCTION_INFO_V1(pgl_ecircle_out);
  4.2010 -Datum pgl_ecircle_out(PG_FUNCTION_ARGS) {
  4.2011 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2012 -  char latstr[PGL_NUMBUFLEN];
  4.2013 -  char lonstr[PGL_NUMBUFLEN];
  4.2014 -  char radstr[PGL_NUMBUFLEN];
  4.2015 -  pgl_print_lat(latstr, circle->center.lat);
  4.2016 -  pgl_print_lon(lonstr, circle->center.lon);
  4.2017 -  pgl_print_float(radstr, circle->radius);
  4.2018 -  PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr));
  4.2019 -}
  4.2020 -
  4.2021 -/* convert cluster ("ecluster") to string representation */
  4.2022 -PG_FUNCTION_INFO_V1(pgl_ecluster_out);
  4.2023 -Datum pgl_ecluster_out(PG_FUNCTION_ARGS) {
  4.2024 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  4.2025 -  char latstr[PGL_NUMBUFLEN];  /* string buffer for latitude */
  4.2026 -  char lonstr[PGL_NUMBUFLEN];  /* string buffer for longitude */
  4.2027 -  char ***strings;     /* array of array of strings */
  4.2028 -  char *string;        /* string of current token */
  4.2029 -  char *res, *resptr;  /* result and pointer to current write position */
  4.2030 -  size_t reslen = 1;   /* length of result (init with 1 for terminator) */
  4.2031 -  int npoints;         /* number of points of current entry */
  4.2032 -  int i, j;            /* i: entry, j: point in entry */
  4.2033 -  /* handle empty clusters */
  4.2034 -  if (cluster->nentries == 0) {
  4.2035 -    /* free detoasted cluster (if copy) */
  4.2036 -    PG_FREE_IF_COPY(cluster, 0);
  4.2037 -    /* return static result */
  4.2038 -    PG_RETURN_CSTRING("empty");
  4.2039 -  }
  4.2040 -  /* allocate array of array of strings */
  4.2041 -  strings = palloc(cluster->nentries * sizeof(char **));
  4.2042 -  /* iterate over all entries in cluster */
  4.2043 -  for (i=0; i<cluster->nentries; i++) {
  4.2044 -    /* get number of points in entry */
  4.2045 -    npoints = cluster->entries[i].npoints;
  4.2046 -    /* allocate array of strings (one string for each point plus two extra) */
  4.2047 -    strings[i] = palloc((2 + npoints) * sizeof(char *));
  4.2048 -    /* determine opening string */
  4.2049 -    switch (cluster->entries[i].entrytype) {
  4.2050 -      case PGL_ENTRY_POINT:   string = (i==0)?"point ("  :" point (";   break;
  4.2051 -      case PGL_ENTRY_PATH:    string = (i==0)?"path ("   :" path (";    break;
  4.2052 -      case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break;
  4.2053 -      case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break;
  4.2054 -      default:                string = (i==0)?"unknown"  :" unknown";
  4.2055 -    }
  4.2056 -    /* use opening string as first string in array */
  4.2057 -    strings[i][0] = string;
  4.2058 -    /* update result length (for allocating result string later) */
  4.2059 -    reslen += strlen(string);
  4.2060 -    /* iterate over all points */
  4.2061 -    for (j=0; j<npoints; j++) {
  4.2062 -      /* create string representation of point */
  4.2063 -      pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat);
  4.2064 -      pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon);
  4.2065 -      string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr);
  4.2066 -      /* copy string pointer to string array */
  4.2067 -      strings[i][j+1] = string;
  4.2068 -      /* update result length (for allocating result string later) */
  4.2069 -      reslen += strlen(string);
  4.2070 -    }
  4.2071 -    /* use closing parenthesis as last string in array */
  4.2072 -    strings[i][npoints+1] = ")";
  4.2073 -    /* update result length (for allocating result string later) */
  4.2074 -    reslen++;
  4.2075 -  }
  4.2076 -  /* allocate result string */
  4.2077 -  res = palloc(reslen);
  4.2078 -  /* set write pointer to begin of result string */
  4.2079 -  resptr = res;
  4.2080 -  /* copy strings into result string */
  4.2081 -  for (i=0; i<cluster->nentries; i++) {
  4.2082 -    npoints = cluster->entries[i].npoints;
  4.2083 -    for (j=0; j<npoints+2; j++) {
  4.2084 -      string = strings[i][j];
  4.2085 -      strcpy(resptr, string);
  4.2086 -      resptr += strlen(string);
  4.2087 -      /* free strings allocated by psprintf */
  4.2088 -      if (j != 0 && j != npoints+1) pfree(string);
  4.2089 -    }
  4.2090 -    /* free array of strings */
  4.2091 -    pfree(strings[i]);
  4.2092 -  }
  4.2093 -  /* free array of array of strings */
  4.2094 -  pfree(strings);
  4.2095 -  /* free detoasted cluster (if copy) */
  4.2096 -  PG_FREE_IF_COPY(cluster, 0);
  4.2097 -  /* return result */
  4.2098 -  PG_RETURN_CSTRING(res);
  4.2099 -}
  4.2100 -
  4.2101 -/* binary input function for point ("epoint") */
  4.2102 -PG_FUNCTION_INFO_V1(pgl_epoint_recv);
  4.2103 -Datum pgl_epoint_recv(PG_FUNCTION_ARGS) {
  4.2104 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  4.2105 -  pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
  4.2106 -  point->lat = pq_getmsgfloat8(buf);
  4.2107 -  point->lon = pq_getmsgfloat8(buf);
  4.2108 -  PG_RETURN_POINTER(point);
  4.2109 -}
  4.2110 -
  4.2111 -/* binary input function for box ("ebox") */
  4.2112 -PG_FUNCTION_INFO_V1(pgl_ebox_recv);
  4.2113 -Datum pgl_ebox_recv(PG_FUNCTION_ARGS) {
  4.2114 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  4.2115 -  pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
  4.2116 -  box->lat_min = pq_getmsgfloat8(buf);
  4.2117 -  box->lat_max = pq_getmsgfloat8(buf);
  4.2118 -  box->lon_min = pq_getmsgfloat8(buf);
  4.2119 -  box->lon_max = pq_getmsgfloat8(buf);
  4.2120 -  PG_RETURN_POINTER(box);
  4.2121 -}
  4.2122 -
  4.2123 -/* binary input function for circle ("ecircle") */
  4.2124 -PG_FUNCTION_INFO_V1(pgl_ecircle_recv);
  4.2125 -Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) {
  4.2126 -  StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
  4.2127 -  pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
  4.2128 -  circle->center.lat = pq_getmsgfloat8(buf);
  4.2129 -  circle->center.lon = pq_getmsgfloat8(buf);
  4.2130 -  circle->radius = pq_getmsgfloat8(buf);
  4.2131 -  PG_RETURN_POINTER(circle);
  4.2132 -}
  4.2133 -
  4.2134 -/* TODO: binary receive function for cluster */
  4.2135 -
  4.2136 -/* binary output function for point ("epoint") */
  4.2137 -PG_FUNCTION_INFO_V1(pgl_epoint_send);
  4.2138 -Datum pgl_epoint_send(PG_FUNCTION_ARGS) {
  4.2139 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2140 -  StringInfoData buf;
  4.2141 -  pq_begintypsend(&buf);
  4.2142 -  pq_sendfloat8(&buf, point->lat);
  4.2143 -  pq_sendfloat8(&buf, point->lon);
  4.2144 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  4.2145 -}
  4.2146 -
  4.2147 -/* binary output function for box ("ebox") */
  4.2148 -PG_FUNCTION_INFO_V1(pgl_ebox_send);
  4.2149 -Datum pgl_ebox_send(PG_FUNCTION_ARGS) {
  4.2150 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  4.2151 -  StringInfoData buf;
  4.2152 -  pq_begintypsend(&buf);
  4.2153 -  pq_sendfloat8(&buf, box->lat_min);
  4.2154 -  pq_sendfloat8(&buf, box->lat_max);
  4.2155 -  pq_sendfloat8(&buf, box->lon_min);
  4.2156 -  pq_sendfloat8(&buf, box->lon_max);
  4.2157 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  4.2158 -}
  4.2159 -
  4.2160 -/* binary output function for circle ("ecircle") */
  4.2161 -PG_FUNCTION_INFO_V1(pgl_ecircle_send);
  4.2162 -Datum pgl_ecircle_send(PG_FUNCTION_ARGS) {
  4.2163 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2164 -  StringInfoData buf;
  4.2165 -  pq_begintypsend(&buf);
  4.2166 -  pq_sendfloat8(&buf, circle->center.lat);
  4.2167 -  pq_sendfloat8(&buf, circle->center.lon);
  4.2168 -  pq_sendfloat8(&buf, circle->radius);
  4.2169 -  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  4.2170 -}
  4.2171 -
  4.2172 -/* TODO: binary send functions for cluster */
  4.2173 -
  4.2174 -/* cast point ("epoint") to box ("ebox") */
  4.2175 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox);
  4.2176 -Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) {
  4.2177 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2178 -  pgl_box *box = palloc(sizeof(pgl_box));
  4.2179 -  box->lat_min = point->lat;
  4.2180 -  box->lat_max = point->lat;
  4.2181 -  box->lon_min = point->lon;
  4.2182 -  box->lon_max = point->lon;
  4.2183 -  PG_RETURN_POINTER(box);
  4.2184 -}
  4.2185 -
  4.2186 -/* cast point ("epoint") to circle ("ecircle") */
  4.2187 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle);
  4.2188 -Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) {
  4.2189 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2190 -  pgl_circle *circle = palloc(sizeof(pgl_box));
  4.2191 -  circle->center = *point;
  4.2192 -  circle->radius = 0;
  4.2193 -  PG_RETURN_POINTER(circle);
  4.2194 -}
  4.2195 -
  4.2196 -/* cast point ("epoint") to cluster ("ecluster") */
  4.2197 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster);
  4.2198 -Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) {
  4.2199 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2200 -  pgl_newentry entry;
  4.2201 -  entry.entrytype = PGL_ENTRY_POINT;
  4.2202 -  entry.npoints = 1;
  4.2203 -  entry.points = point;
  4.2204 -  PG_RETURN_POINTER(pgl_new_cluster(1, &entry));
  4.2205 -}
  4.2206 -
  4.2207 -/* cast box ("ebox") to cluster ("ecluster") */
  4.2208 -#define pgl_ebox_to_ecluster_macro(i, a, b) \
  4.2209 -  entries[i].entrytype = PGL_ENTRY_POLYGON; \
  4.2210 -  entries[i].npoints = 4; \
  4.2211 -  entries[i].points = points[i]; \
  4.2212 -  points[i][0].lat = box->lat_min; \
  4.2213 -  points[i][0].lon = (a); \
  4.2214 -  points[i][1].lat = box->lat_min; \
  4.2215 -  points[i][1].lon = (b); \
  4.2216 -  points[i][2].lat = box->lat_max; \
  4.2217 -  points[i][2].lon = (b); \
  4.2218 -  points[i][3].lat = box->lat_max; \
  4.2219 -  points[i][3].lon = (a);
  4.2220 -PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster);
  4.2221 -Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) {
  4.2222 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  4.2223 -  double lon, dlon;
  4.2224 -  int nentries;
  4.2225 -  pgl_newentry entries[3];
  4.2226 -  pgl_point points[3][4];
  4.2227 -  if (box->lat_min > box->lat_max) {
  4.2228 -    nentries = 0;
  4.2229 -  } else if (box->lon_min > box->lon_max) {
  4.2230 -    if (box->lon_min < 0) {
  4.2231 -      lon = pgl_round((box->lon_min + 180) / 2.0);
  4.2232 -      nentries = 3;
  4.2233 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  4.2234 -      pgl_ebox_to_ecluster_macro(1, lon, 180);
  4.2235 -      pgl_ebox_to_ecluster_macro(2, -180, box->lon_max);
  4.2236 -    } else if (box->lon_max > 0) {
  4.2237 -      lon = pgl_round((box->lon_max - 180) / 2.0);
  4.2238 -      nentries = 3;
  4.2239 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  4.2240 -      pgl_ebox_to_ecluster_macro(1, -180, lon);
  4.2241 -      pgl_ebox_to_ecluster_macro(2, lon, box->lon_max);
  4.2242 -    } else {
  4.2243 -      nentries = 2;
  4.2244 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
  4.2245 -      pgl_ebox_to_ecluster_macro(1, -180, box->lon_max);
  4.2246 -    }
  4.2247 -  } else {
  4.2248 -    dlon = pgl_round(box->lon_max - box->lon_min);
  4.2249 -    if (dlon < 180) {
  4.2250 -      nentries = 1;
  4.2251 -      pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max);
  4.2252 -    } else {
  4.2253 -      lon = pgl_round((box->lon_min + box->lon_max) / 2.0);
  4.2254 -      if (
  4.2255 -        pgl_round(lon - box->lon_min) < 180 &&
  4.2256 -        pgl_round(box->lon_max - lon) < 180
  4.2257 -      ) {
  4.2258 -        nentries = 2;
  4.2259 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
  4.2260 -        pgl_ebox_to_ecluster_macro(1, lon, box->lon_max);
  4.2261 -      } else {
  4.2262 -        nentries = 3;
  4.2263 -        pgl_ebox_to_ecluster_macro(0, box->lon_min, -60);
  4.2264 -        pgl_ebox_to_ecluster_macro(1, -60, 60);
  4.2265 -        pgl_ebox_to_ecluster_macro(2, 60, box->lon_max);
  4.2266 -      }
  4.2267 -    }
  4.2268 -  }
  4.2269 -  PG_RETURN_POINTER(pgl_new_cluster(nentries, entries));
  4.2270 -}
  4.2271 -
  4.2272 -/* extract latitude from point ("epoint") */
  4.2273 -PG_FUNCTION_INFO_V1(pgl_epoint_lat);
  4.2274 -Datum pgl_epoint_lat(PG_FUNCTION_ARGS) {
  4.2275 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat);
  4.2276 -}
  4.2277 -
  4.2278 -/* extract longitude from point ("epoint") */
  4.2279 -PG_FUNCTION_INFO_V1(pgl_epoint_lon);
  4.2280 -Datum pgl_epoint_lon(PG_FUNCTION_ARGS) {
  4.2281 -  PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon);
  4.2282 -}
  4.2283 -
  4.2284 -/* extract minimum latitude from box ("ebox") */
  4.2285 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_min);
  4.2286 -Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) {
  4.2287 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min);
  4.2288 -}
  4.2289 -
  4.2290 -/* extract maximum latitude from box ("ebox") */
  4.2291 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_max);
  4.2292 -Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) {
  4.2293 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max);
  4.2294 -}
  4.2295 -
  4.2296 -/* extract minimum longitude from box ("ebox") */
  4.2297 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_min);
  4.2298 -Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) {
  4.2299 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min);
  4.2300 -}
  4.2301 -
  4.2302 -/* extract maximum longitude from box ("ebox") */
  4.2303 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_max);
  4.2304 -Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) {
  4.2305 -  PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max);
  4.2306 -}
  4.2307 -
  4.2308 -/* extract center point from circle ("ecircle") */
  4.2309 -PG_FUNCTION_INFO_V1(pgl_ecircle_center);
  4.2310 -Datum pgl_ecircle_center(PG_FUNCTION_ARGS) {
  4.2311 -  PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center));
  4.2312 -}
  4.2313 -
  4.2314 -/* extract radius from circle ("ecircle") */
  4.2315 -PG_FUNCTION_INFO_V1(pgl_ecircle_radius);
  4.2316 -Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) {
  4.2317 -  PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius);
  4.2318 -}
  4.2319 -
  4.2320 -/* check if point is inside box (overlap operator "&&") in SQL */
  4.2321 -PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap);
  4.2322 -Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) {
  4.2323 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2324 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1);
  4.2325 -  PG_RETURN_BOOL(pgl_point_in_box(point, box));
  4.2326 -}
  4.2327 -
  4.2328 -/* check if point is inside circle (overlap operator "&&") in SQL */
  4.2329 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap);
  4.2330 -Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) {
  4.2331 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2332 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2333 -  PG_RETURN_BOOL(
  4.2334 -    pgl_distance(
  4.2335 -      point->lat, point->lon,
  4.2336 -      circle->center.lat, circle->center.lon
  4.2337 -    ) <= circle->radius
  4.2338 -  );
  4.2339 -}
  4.2340 -
  4.2341 -/* check if point is inside cluster (overlap operator "&&") in SQL */
  4.2342 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap);
  4.2343 -Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
  4.2344 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2345 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2346 -  bool retval;
  4.2347 -  /* points outside bounding circle are always assumed to be non-overlapping
  4.2348 -     (necessary for consistent table and index scans) */
  4.2349 -  if (
  4.2350 -    pgl_distance(
  4.2351 -      point->lat, point->lon,
  4.2352 -      cluster->bounding.center.lat, cluster->bounding.center.lon
  4.2353 -    ) > cluster->bounding.radius
  4.2354 -  ) retval = false;
  4.2355 -  else retval = pgl_point_in_cluster(point, cluster, false);
  4.2356 -  PG_FREE_IF_COPY(cluster, 1);
  4.2357 -  PG_RETURN_BOOL(retval);
  4.2358 -}
  4.2359 -
  4.2360 -/* check if point may be inside cluster (lossy overl. operator "&&+") in SQL */
  4.2361 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_may_overlap);
  4.2362 -Datum pgl_epoint_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  4.2363 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2364 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2365 -  bool retval = pgl_distance(
  4.2366 -    point->lat, point->lon,
  4.2367 -    cluster->bounding.center.lat, cluster->bounding.center.lon
  4.2368 -  ) <= cluster->bounding.radius;
  4.2369 -  PG_FREE_IF_COPY(cluster, 1);
  4.2370 -  PG_RETURN_BOOL(retval);
  4.2371 -}
  4.2372 -
  4.2373 -/* check if two boxes overlap (overlap operator "&&") in SQL */
  4.2374 -PG_FUNCTION_INFO_V1(pgl_ebox_overlap);
  4.2375 -Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) {
  4.2376 -  pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0);
  4.2377 -  pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1);
  4.2378 -  PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2));
  4.2379 -}
  4.2380 -
  4.2381 -/* check if box and circle may overlap (lossy overl. operator "&&+") in SQL */
  4.2382 -PG_FUNCTION_INFO_V1(pgl_ebox_ecircle_may_overlap);
  4.2383 -Datum pgl_ebox_ecircle_may_overlap(PG_FUNCTION_ARGS) {
  4.2384 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  4.2385 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2386 -  PG_RETURN_BOOL(
  4.2387 -    pgl_estimate_point_box_distance(&circle->center, box) <= circle->radius
  4.2388 -  );
  4.2389 -}
  4.2390 -
  4.2391 -/* check if box and cluster may overlap (lossy overl. operator "&&+") in SQL */
  4.2392 -PG_FUNCTION_INFO_V1(pgl_ebox_ecluster_may_overlap);
  4.2393 -Datum pgl_ebox_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  4.2394 -  pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
  4.2395 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2396 -  bool retval = pgl_estimate_point_box_distance(
  4.2397 -    &cluster->bounding.center,
  4.2398 -    box
  4.2399 -  ) <= cluster->bounding.radius;
  4.2400 -  PG_FREE_IF_COPY(cluster, 1);
  4.2401 -  PG_RETURN_BOOL(retval);
  4.2402 -}
  4.2403 -
  4.2404 -/* check if two circles overlap (overlap operator "&&") in SQL */
  4.2405 -PG_FUNCTION_INFO_V1(pgl_ecircle_overlap);
  4.2406 -Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) {
  4.2407 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2408 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2409 -  PG_RETURN_BOOL(
  4.2410 -    pgl_distance(
  4.2411 -      circle1->center.lat, circle1->center.lon,
  4.2412 -      circle2->center.lat, circle2->center.lon
  4.2413 -    ) <= circle1->radius + circle2->radius
  4.2414 -  );
  4.2415 -}
  4.2416 -
  4.2417 -/* check if circle and cluster overlap (overlap operator "&&") in SQL */
  4.2418 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap);
  4.2419 -Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) {
  4.2420 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2421 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2422 -  bool retval = (
  4.2423 -    pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius
  4.2424 -  );
  4.2425 -  PG_FREE_IF_COPY(cluster, 1);
  4.2426 -  PG_RETURN_BOOL(retval);
  4.2427 -}
  4.2428 -
  4.2429 -/* check if circle and cluster may overlap (l. ov. operator "&&+") in SQL */
  4.2430 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_may_overlap);
  4.2431 -Datum pgl_ecircle_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  4.2432 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2433 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2434 -  bool retval = pgl_distance(
  4.2435 -    circle->center.lat, circle->center.lon,
  4.2436 -    cluster->bounding.center.lat, cluster->bounding.center.lon
  4.2437 -  ) <= circle->radius + cluster->bounding.radius;
  4.2438 -  PG_FREE_IF_COPY(cluster, 1);
  4.2439 -  PG_RETURN_BOOL(retval);
  4.2440 -}
  4.2441 -
  4.2442 -/* check if two clusters overlap (overlap operator "&&") in SQL */
  4.2443 -PG_FUNCTION_INFO_V1(pgl_ecluster_overlap);
  4.2444 -Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) {
  4.2445 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  4.2446 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2447 -  bool retval;
  4.2448 -  /* clusters with non-touching bounding circles are always assumed to be
  4.2449 -     non-overlapping (improves performance and is necessary for consistent
  4.2450 -     table and index scans) */
  4.2451 -  if (
  4.2452 -    pgl_distance(
  4.2453 -      cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  4.2454 -      cluster2->bounding.center.lat, cluster2->bounding.center.lon
  4.2455 -    ) > cluster1->bounding.radius + cluster2->bounding.radius
  4.2456 -  ) retval = false;
  4.2457 -  else retval = pgl_clusters_overlap(cluster1, cluster2);
  4.2458 -  PG_FREE_IF_COPY(cluster1, 0);
  4.2459 -  PG_FREE_IF_COPY(cluster2, 1);
  4.2460 -  PG_RETURN_BOOL(retval);
  4.2461 -}
  4.2462 -
  4.2463 -/* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */
  4.2464 -PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap);
  4.2465 -Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) {
  4.2466 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  4.2467 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2468 -  bool retval = pgl_distance(
  4.2469 -    cluster1->bounding.center.lat, cluster1->bounding.center.lon,
  4.2470 -    cluster2->bounding.center.lat, cluster2->bounding.center.lon
  4.2471 -  ) <= cluster1->bounding.radius + cluster2->bounding.radius;
  4.2472 -  PG_FREE_IF_COPY(cluster1, 0);
  4.2473 -  PG_FREE_IF_COPY(cluster2, 1);
  4.2474 -  PG_RETURN_BOOL(retval);
  4.2475 -}
  4.2476 -
  4.2477 -/* check if second cluster is in first cluster (cont. operator "@>) in SQL */
  4.2478 -PG_FUNCTION_INFO_V1(pgl_ecluster_contains);
  4.2479 -Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) {
  4.2480 -  pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  4.2481 -  pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2482 -  bool retval;
  4.2483 -  /* clusters with non-touching bounding circles are always assumed to be
  4.2484 -     non-overlapping (improves performance and is necessary for consistent
  4.2485 -     table and index scans) */
  4.2486 -  if (
  4.2487 -    pgl_distance(
  4.2488 -      outer->bounding.center.lat, outer->bounding.center.lon,
  4.2489 -      inner->bounding.center.lat, inner->bounding.center.lon
  4.2490 -    ) > outer->bounding.radius + inner->bounding.radius
  4.2491 -  ) retval = false;
  4.2492 -  else retval = pgl_cluster_in_cluster(outer, inner);
  4.2493 -  PG_FREE_IF_COPY(outer, 0);
  4.2494 -  PG_FREE_IF_COPY(inner, 1);
  4.2495 -  PG_RETURN_BOOL(retval);
  4.2496 -}
  4.2497 -
  4.2498 -/* calculate distance between two points ("<->" operator) in SQL */
  4.2499 -PG_FUNCTION_INFO_V1(pgl_epoint_distance);
  4.2500 -Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
  4.2501 -  pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
  4.2502 -  pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
  4.2503 -  PG_RETURN_FLOAT8(pgl_distance(
  4.2504 -    point1->lat, point1->lon, point2->lat, point2->lon
  4.2505 -  ));
  4.2506 -}
  4.2507 -
  4.2508 -/* calculate point to circle distance ("<->" operator) in SQL */
  4.2509 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance);
  4.2510 -Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) {
  4.2511 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2512 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2513 -  double distance = pgl_distance(
  4.2514 -    point->lat, point->lon, circle->center.lat, circle->center.lon
  4.2515 -  ) - circle->radius;
  4.2516 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  4.2517 -}
  4.2518 -
  4.2519 -/* calculate point to cluster distance ("<->" operator) in SQL */
  4.2520 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance);
  4.2521 -Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) {
  4.2522 -  pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
  4.2523 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2524 -  double distance = pgl_point_cluster_distance(point, cluster);
  4.2525 -  PG_FREE_IF_COPY(cluster, 1);
  4.2526 -  PG_RETURN_FLOAT8(distance);
  4.2527 -}
  4.2528 -
  4.2529 -/* calculate distance between two circles ("<->" operator) in SQL */
  4.2530 -PG_FUNCTION_INFO_V1(pgl_ecircle_distance);
  4.2531 -Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) {
  4.2532 -  pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2533 -  pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2534 -  double distance = pgl_distance(
  4.2535 -    circle1->center.lat, circle1->center.lon,
  4.2536 -    circle2->center.lat, circle2->center.lon
  4.2537 -  ) - (circle1->radius + circle2->radius);
  4.2538 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  4.2539 -}
  4.2540 -
  4.2541 -/* calculate circle to cluster distance ("<->" operator) in SQL */
  4.2542 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance);
  4.2543 -Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) {
  4.2544 -  pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
  4.2545 -  pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2546 -  double distance = (
  4.2547 -    pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius
  4.2548 -  );
  4.2549 -  PG_FREE_IF_COPY(cluster, 1);
  4.2550 -  PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
  4.2551 -}
  4.2552 -
  4.2553 -/* calculate distance between two clusters ("<->" operator) in SQL */
  4.2554 -PG_FUNCTION_INFO_V1(pgl_ecluster_distance);
  4.2555 -Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) {
  4.2556 -  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
  4.2557 -  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2558 -  double retval = pgl_cluster_distance(cluster1, cluster2);
  4.2559 -  PG_FREE_IF_COPY(cluster1, 0);
  4.2560 -  PG_FREE_IF_COPY(cluster2, 1);
  4.2561 -  PG_RETURN_FLOAT8(retval);
  4.2562 -}
  4.2563 -
  4.2564 -
  4.2565 -/*-----------------------------------------------------------*
  4.2566 - *  B-tree comparison operators and index support functions  *
  4.2567 - *-----------------------------------------------------------*/
  4.2568 -
  4.2569 -/* macro for a B-tree operator (without detoasting) */
  4.2570 -#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \
  4.2571 -  PG_FUNCTION_INFO_V1(func); \
  4.2572 -  Datum func(PG_FUNCTION_ARGS) { \
  4.2573 -    type *a = (type *)PG_GETARG_POINTER(0); \
  4.2574 -    type *b = (type *)PG_GETARG_POINTER(1); \
  4.2575 -    PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \
  4.2576 -  }
  4.2577 -
  4.2578 -/* macro for a B-tree comparison function (without detoasting) */
  4.2579 -#define PGL_BTREE_CMP(func, type, cmpfunc) \
  4.2580 -  PG_FUNCTION_INFO_V1(func); \
  4.2581 -  Datum func(PG_FUNCTION_ARGS) { \
  4.2582 -    type *a = (type *)PG_GETARG_POINTER(0); \
  4.2583 -    type *b = (type *)PG_GETARG_POINTER(1); \
  4.2584 -    PG_RETURN_INT32(cmpfunc(a, b)); \
  4.2585 -  }
  4.2586 -
  4.2587 -/* macro for a B-tree operator (with detoasting) */
  4.2588 -#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \
  4.2589 -  PG_FUNCTION_INFO_V1(func); \
  4.2590 -  Datum func(PG_FUNCTION_ARGS) { \
  4.2591 -    bool res; \
  4.2592 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  4.2593 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  4.2594 -    res = cmpfunc(a, b) oper 0; \
  4.2595 -    PG_FREE_IF_COPY(a, 0); \
  4.2596 -    PG_FREE_IF_COPY(b, 1); \
  4.2597 -    PG_RETURN_BOOL(res); \
  4.2598 -  }
  4.2599 -
  4.2600 -/* macro for a B-tree comparison function (with detoasting) */
  4.2601 -#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \
  4.2602 -  PG_FUNCTION_INFO_V1(func); \
  4.2603 -  Datum func(PG_FUNCTION_ARGS) { \
  4.2604 -    int32_t res; \
  4.2605 -    type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
  4.2606 -    type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
  4.2607 -    res = cmpfunc(a, b); \
  4.2608 -    PG_FREE_IF_COPY(a, 0); \
  4.2609 -    PG_FREE_IF_COPY(b, 1); \
  4.2610 -    PG_RETURN_INT32(res); \
  4.2611 -  }
  4.2612 -
  4.2613 -/* B-tree operators and comparison function for point */
  4.2614 -PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <)
  4.2615 -PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=)
  4.2616 -PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==)
  4.2617 -PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=)
  4.2618 -PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=)
  4.2619 -PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >)
  4.2620 -PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp)
  4.2621 -
  4.2622 -/* B-tree operators and comparison function for box */
  4.2623 -PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <)
  4.2624 -PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=)
  4.2625 -PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==)
  4.2626 -PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=)
  4.2627 -PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=)
  4.2628 -PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >)
  4.2629 -PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp)
  4.2630 -
  4.2631 -/* B-tree operators and comparison function for circle */
  4.2632 -PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <)
  4.2633 -PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=)
  4.2634 -PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==)
  4.2635 -PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=)
  4.2636 -PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=)
  4.2637 -PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >)
  4.2638 -PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp)
  4.2639 -
  4.2640 -
  4.2641 -/*--------------------------------*
  4.2642 - *  GiST index support functions  *
  4.2643 - *--------------------------------*/
  4.2644 -
  4.2645 -/* GiST "consistent" support function */
  4.2646 -PG_FUNCTION_INFO_V1(pgl_gist_consistent);
  4.2647 -Datum pgl_gist_consistent(PG_FUNCTION_ARGS) {
  4.2648 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  4.2649 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  4.2650 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  4.2651 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  4.2652 -  /* demand recheck because index and query methods are lossy */
  4.2653 -  *recheck = true;
  4.2654 -  /* strategy number aliases for different operators using the same strategy */
  4.2655 -  strategy %= 100;
  4.2656 -  /* strategy number 11: equality of two points */
  4.2657 -  if (strategy == 11) {
  4.2658 -    /* query datum is another point */
  4.2659 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  4.2660 -    /* convert other point to key */
  4.2661 -    pgl_pointkey querykey;
  4.2662 -    pgl_point_to_key(query, querykey);
  4.2663 -    /* return true if both keys overlap */
  4.2664 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  4.2665 -  }
  4.2666 -  /* strategy number 13: equality of two circles */
  4.2667 -  if (strategy == 13) {
  4.2668 -    /* query datum is another circle */
  4.2669 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2670 -    /* convert other circle to key */
  4.2671 -    pgl_areakey querykey;
  4.2672 -    pgl_circle_to_key(query, querykey);
  4.2673 -    /* return true if both keys overlap */
  4.2674 -    PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
  4.2675 -  }
  4.2676 -  /* for all remaining strategies, keys on empty objects produce no match */
  4.2677 -  /* (check necessary because query radius may be infinite) */
  4.2678 -  if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false);
  4.2679 -  /* strategy number 21: overlapping with point */
  4.2680 -  if (strategy == 21) {
  4.2681 -    /* query datum is a point */
  4.2682 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  4.2683 -    /* return true if estimated distance (allowed to be smaller than real
  4.2684 -       distance) between index key and point is zero */
  4.2685 -    PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0);
  4.2686 -  }
  4.2687 -  /* strategy number 22: (point) overlapping with box */
  4.2688 -  if (strategy == 22) {
  4.2689 -    /* query datum is a box */
  4.2690 -    pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1);
  4.2691 -    /* determine bounding box of indexed key */
  4.2692 -    pgl_box keybox;
  4.2693 -    pgl_key_to_box(key, &keybox);
  4.2694 -    /* return true if query box overlaps with bounding box of indexed key */
  4.2695 -    PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox));
  4.2696 -  }
  4.2697 -  /* strategy number 23: overlapping with circle */
  4.2698 -  if (strategy == 23) {
  4.2699 -    /* query datum is a circle */
  4.2700 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  4.2701 -    /* return true if estimated distance (allowed to be smaller than real
  4.2702 -       distance) between index key and circle center is smaller than radius */
  4.2703 -    PG_RETURN_BOOL(
  4.2704 -      pgl_estimate_key_distance(key, &(query->center)) <= query->radius
  4.2705 -    );
  4.2706 -  }
  4.2707 -  /* strategy number 24: overlapping with cluster */
  4.2708 -  if (strategy == 24) {
  4.2709 -    bool retval;  /* return value */
  4.2710 -    /* query datum is a cluster */
  4.2711 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.2712 -    /* return true if estimated distance (allowed to be smaller than real
  4.2713 -       distance) between index key and circle center is smaller than radius */
  4.2714 -    retval = (
  4.2715 -      pgl_estimate_key_distance(key, &(query->bounding.center)) <=
  4.2716 -      query->bounding.radius
  4.2717 -    );
  4.2718 -    PG_FREE_IF_COPY(query, 1);  /* free detoasted cluster (if copy) */
  4.2719 -    PG_RETURN_BOOL(retval);
  4.2720 -  }
  4.2721 -  /* throw error for any unknown strategy number */
  4.2722 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  4.2723 -}
  4.2724 -
  4.2725 -/* GiST "union" support function */
  4.2726 -PG_FUNCTION_INFO_V1(pgl_gist_union);
  4.2727 -Datum pgl_gist_union(PG_FUNCTION_ARGS) {
  4.2728 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  4.2729 -  pgl_keyptr out;  /* return value (to be palloc'ed) */
  4.2730 -  int i;
  4.2731 -  /* determine key size */
  4.2732 -  size_t keysize = PGL_KEY_IS_AREAKEY(
  4.2733 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key)
  4.2734 -  ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey);
  4.2735 -  /* begin with first key as result */
  4.2736 -  out = palloc(keysize);
  4.2737 -  memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize);
  4.2738 -  /* unite current result with second, third, etc. key */
  4.2739 -  for (i=1; i<entryvec->n; i++) {
  4.2740 -    pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key));
  4.2741 -  }
  4.2742 -  /* return result */
  4.2743 -  PG_RETURN_POINTER(out);
  4.2744 -}
  4.2745 -
  4.2746 -/* GiST "compress" support function for indicis on points */
  4.2747 -PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint);
  4.2748 -Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) {
  4.2749 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  4.2750 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  4.2751 -  /* only transform new leaves */
  4.2752 -  if (entry->leafkey) {
  4.2753 -    /* get point to be transformed */
  4.2754 -    pgl_point *point = (pgl_point *)DatumGetPointer(entry->key);
  4.2755 -    /* allocate memory for key */
  4.2756 -    pgl_keyptr key = palloc(sizeof(pgl_pointkey));
  4.2757 -    /* transform point to key */
  4.2758 -    pgl_point_to_key(point, key);
  4.2759 -    /* create new GISTENTRY structure as return value */
  4.2760 -    retval = palloc(sizeof(GISTENTRY));
  4.2761 -    gistentryinit(
  4.2762 -      *retval, PointerGetDatum(key),
  4.2763 -      entry->rel, entry->page, entry->offset, FALSE
  4.2764 -    );
  4.2765 -  } else {
  4.2766 -    /* inner nodes have already been transformed */
  4.2767 -    retval = entry;
  4.2768 -  }
  4.2769 -  /* return pointer to old or new GISTENTRY structure */
  4.2770 -  PG_RETURN_POINTER(retval);
  4.2771 -}
  4.2772 -
  4.2773 -/* GiST "compress" support function for indicis on circles */
  4.2774 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle);
  4.2775 -Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) {
  4.2776 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  4.2777 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  4.2778 -  /* only transform new leaves */
  4.2779 -  if (entry->leafkey) {
  4.2780 -    /* get circle to be transformed */
  4.2781 -    pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key);
  4.2782 -    /* allocate memory for key */
  4.2783 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  4.2784 -    /* transform circle to key */
  4.2785 -    pgl_circle_to_key(circle, key);
  4.2786 -    /* create new GISTENTRY structure as return value */
  4.2787 -    retval = palloc(sizeof(GISTENTRY));
  4.2788 -    gistentryinit(
  4.2789 -      *retval, PointerGetDatum(key),
  4.2790 -      entry->rel, entry->page, entry->offset, FALSE
  4.2791 -    );
  4.2792 -  } else {
  4.2793 -    /* inner nodes have already been transformed */
  4.2794 -    retval = entry;
  4.2795 -  }
  4.2796 -  /* return pointer to old or new GISTENTRY structure */
  4.2797 -  PG_RETURN_POINTER(retval);
  4.2798 -}
  4.2799 -
  4.2800 -/* GiST "compress" support function for indices on clusters */
  4.2801 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster);
  4.2802 -Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) {
  4.2803 -  GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
  4.2804 -  GISTENTRY *retval;  /* return value (to be palloc'ed unless set to entry) */
  4.2805 -  /* only transform new leaves */
  4.2806 -  if (entry->leafkey) {
  4.2807 -    /* get cluster to be transformed (detoasting necessary!) */
  4.2808 -    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key);
  4.2809 -    /* allocate memory for key */
  4.2810 -    pgl_keyptr key = palloc(sizeof(pgl_areakey));
  4.2811 -    /* transform cluster to key */
  4.2812 -    pgl_circle_to_key(&(cluster->bounding), key);
  4.2813 -    /* create new GISTENTRY structure as return value */
  4.2814 -    retval = palloc(sizeof(GISTENTRY));
  4.2815 -    gistentryinit(
  4.2816 -      *retval, PointerGetDatum(key),
  4.2817 -      entry->rel, entry->page, entry->offset, FALSE
  4.2818 -    );
  4.2819 -    /* free detoasted datum */
  4.2820 -    if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster);
  4.2821 -  } else {
  4.2822 -    /* inner nodes have already been transformed */
  4.2823 -    retval = entry;
  4.2824 -  }
  4.2825 -  /* return pointer to old or new GISTENTRY structure */
  4.2826 -  PG_RETURN_POINTER(retval);
  4.2827 -}
  4.2828 -
  4.2829 -/* GiST "decompress" support function for indices */
  4.2830 -PG_FUNCTION_INFO_V1(pgl_gist_decompress);
  4.2831 -Datum pgl_gist_decompress(PG_FUNCTION_ARGS) {
  4.2832 -  /* return passed pointer without transformation */
  4.2833 -  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
  4.2834 -}
  4.2835 -
  4.2836 -/* GiST "penalty" support function */
  4.2837 -PG_FUNCTION_INFO_V1(pgl_gist_penalty);
  4.2838 -Datum pgl_gist_penalty(PG_FUNCTION_ARGS) {
  4.2839 -  GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0);
  4.2840 -  GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1);
  4.2841 -  float *penalty = (float *)PG_GETARG_POINTER(2);
  4.2842 -  /* get original key and key to insert */
  4.2843 -  pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key);
  4.2844 -  pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key);
  4.2845 -  /* copy original key */
  4.2846 -  union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key;
  4.2847 -  if (PGL_KEY_IS_AREAKEY(orig)) {
  4.2848 -    memcpy(union_key.areakey, orig, sizeof(union_key.areakey));
  4.2849 -  } else {
  4.2850 -    memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey));
  4.2851 -  }
  4.2852 -  /* calculate union of both keys */
  4.2853 -  pgl_unite_keys((pgl_keyptr)&union_key, new);
  4.2854 -  /* penalty equal to reduction of key length (logarithm of added area) */
  4.2855 -  /* (return value by setting referenced value and returning pointer) */
  4.2856 -  *penalty = (
  4.2857 -    PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key)
  4.2858 -  );
  4.2859 -  PG_RETURN_POINTER(penalty);
  4.2860 -}
  4.2861 -
  4.2862 -/* GiST "picksplit" support function */
  4.2863 -PG_FUNCTION_INFO_V1(pgl_gist_picksplit);
  4.2864 -Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) {
  4.2865 -  GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
  4.2866 -  GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1);
  4.2867 -  OffsetNumber i;  /* between FirstOffsetNumber and entryvec->n (inclusive) */
  4.2868 -  union {
  4.2869 -    pgl_pointkey pointkey;
  4.2870 -    pgl_areakey areakey;
  4.2871 -  } union_all;  /* union of all keys (to be calculated from scratch)
  4.2872 -                   (later cut in half) */
  4.2873 -  int is_areakey = PGL_KEY_IS_AREAKEY(
  4.2874 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key)
  4.2875 -  );
  4.2876 -  int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey);
  4.2877 -  pgl_keyptr unionL = palloc(keysize);  /* union of keys that go left */
  4.2878 -  pgl_keyptr unionR = palloc(keysize);  /* union of keys that go right */
  4.2879 -  pgl_keyptr key;  /* current key to be processed */
  4.2880 -  /* allocate memory for array of left and right keys, set counts to zero */
  4.2881 -  v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  4.2882 -  v->spl_nleft = 0;
  4.2883 -  v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
  4.2884 -  v->spl_nright = 0;
  4.2885 -  /* calculate union of all keys from scratch */
  4.2886 -  memcpy(
  4.2887 -    (pgl_keyptr)&union_all,
  4.2888 -    (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key),
  4.2889 -    keysize
  4.2890 -  );
  4.2891 -  for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) {
  4.2892 -    pgl_unite_keys(
  4.2893 -      (pgl_keyptr)&union_all,
  4.2894 -      (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)
  4.2895 -    );
  4.2896 -  }
  4.2897 -  /* check if trivial split is necessary due to exhausted key length */
  4.2898 -  /* (Note: keys for empty objects must have node depth set to maximum) */
  4.2899 -  if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == (
  4.2900 -    is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH
  4.2901 -  )) {
  4.2902 -    /* half of all keys go left */
  4.2903 -    for (
  4.2904 -      i=FirstOffsetNumber;
  4.2905 -      i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  4.2906 -      i=OffsetNumberNext(i)
  4.2907 -    ) {
  4.2908 -      /* pointer to current key */
  4.2909 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  4.2910 -      /* update unionL */
  4.2911 -      /* check if key is first key that goes left */
  4.2912 -      if (!v->spl_nleft) {
  4.2913 -        /* first key that goes left is just copied to unionL */
  4.2914 -        memcpy(unionL, key, keysize);
  4.2915 -      } else {
  4.2916 -        /* unite current value and next key */
  4.2917 -        pgl_unite_keys(unionL, key);
  4.2918 -      }
  4.2919 -      /* append offset number to list of keys that go left */
  4.2920 -      v->spl_left[v->spl_nleft++] = i;
  4.2921 -    }
  4.2922 -    /* other half goes right */
  4.2923 -    for (
  4.2924 -      i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
  4.2925 -      i<entryvec->n;
  4.2926 -      i=OffsetNumberNext(i)
  4.2927 -    ) {
  4.2928 -      /* pointer to current key */
  4.2929 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  4.2930 -      /* update unionR */
  4.2931 -      /* check if key is first key that goes right */
  4.2932 -      if (!v->spl_nright) {
  4.2933 -        /* first key that goes right is just copied to unionR */
  4.2934 -        memcpy(unionR, key, keysize);
  4.2935 -      } else {
  4.2936 -        /* unite current value and next key */
  4.2937 -        pgl_unite_keys(unionR, key);
  4.2938 -      }
  4.2939 -      /* append offset number to list of keys that go right */
  4.2940 -      v->spl_right[v->spl_nright++] = i;
  4.2941 -    }
  4.2942 -  }
  4.2943 -  /* otherwise, a non-trivial split is possible */
  4.2944 -  else {
  4.2945 -    /* cut covered area in half */
  4.2946 -    /* (union_all then refers to area of keys that go left) */
  4.2947 -    /* check if union of all keys covers empty and non-empty objects */
  4.2948 -    if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) {
  4.2949 -      /* if yes, split into empty and non-empty objects */
  4.2950 -      pgl_key_set_empty((pgl_keyptr)&union_all);
  4.2951 -    } else {
  4.2952 -      /* otherwise split by next bit */
  4.2953 -      ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++;
  4.2954 -      /* NOTE: type bit conserved */
  4.2955 -    }
  4.2956 -    /* determine for each key if it goes left or right */
  4.2957 -    for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) {
  4.2958 -      /* pointer to current key */
  4.2959 -      key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
  4.2960 -      /* keys within one half of the area go left */
  4.2961 -      if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) {
  4.2962 -        /* update unionL */
  4.2963 -        /* check if key is first key that goes left */
  4.2964 -        if (!v->spl_nleft) {
  4.2965 -          /* first key that goes left is just copied to unionL */
  4.2966 -          memcpy(unionL, key, keysize);
  4.2967 -        } else {
  4.2968 -          /* unite current value of unionL and processed key */
  4.2969 -          pgl_unite_keys(unionL, key);
  4.2970 -        }
  4.2971 -        /* append offset number to list of keys that go left */
  4.2972 -        v->spl_left[v->spl_nleft++] = i;
  4.2973 -      }
  4.2974 -      /* the other keys go right */
  4.2975 -      else {
  4.2976 -        /* update unionR */
  4.2977 -        /* check if key is first key that goes right */
  4.2978 -        if (!v->spl_nright) {
  4.2979 -          /* first key that goes right is just copied to unionR */
  4.2980 -          memcpy(unionR, key, keysize);
  4.2981 -        } else {
  4.2982 -          /* unite current value of unionR and processed key */
  4.2983 -          pgl_unite_keys(unionR, key);
  4.2984 -        }
  4.2985 -        /* append offset number to list of keys that go right */
  4.2986 -        v->spl_right[v->spl_nright++] = i;
  4.2987 -      }
  4.2988 -    }
  4.2989 -  }
  4.2990 -  /* store unions in return value */
  4.2991 -  v->spl_ldatum = PointerGetDatum(unionL);
  4.2992 -  v->spl_rdatum = PointerGetDatum(unionR);
  4.2993 -  /* return all results */
  4.2994 -  PG_RETURN_POINTER(v);
  4.2995 -}
  4.2996 -
  4.2997 -/* GiST "same"/"equal" support function */
  4.2998 -PG_FUNCTION_INFO_V1(pgl_gist_same);
  4.2999 -Datum pgl_gist_same(PG_FUNCTION_ARGS) {
  4.3000 -  pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0);
  4.3001 -  pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1);
  4.3002 -  bool *boolptr = (bool *)PG_GETARG_POINTER(2);
  4.3003 -  /* two keys are equal if they are binary equal */
  4.3004 -  /* (return result by setting referenced boolean and returning pointer) */
  4.3005 -  *boolptr = !memcmp(
  4.3006 -    key1,
  4.3007 -    key2,
  4.3008 -    PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey)
  4.3009 -  );
  4.3010 -  PG_RETURN_POINTER(boolptr);
  4.3011 -}
  4.3012 -
  4.3013 -/* GiST "distance" support function */
  4.3014 -PG_FUNCTION_INFO_V1(pgl_gist_distance);
  4.3015 -Datum pgl_gist_distance(PG_FUNCTION_ARGS) {
  4.3016 -  GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
  4.3017 -  pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
  4.3018 -  StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
  4.3019 -  bool *recheck = (bool *)PG_GETARG_POINTER(4);
  4.3020 -  double distance;  /* return value */
  4.3021 -  /* demand recheck because distance is just an estimation */
  4.3022 -  /* (real distance may be bigger) */
  4.3023 -  *recheck = true;
  4.3024 -  /* strategy number aliases for different operators using the same strategy */
  4.3025 -  strategy %= 100;
  4.3026 -  /* strategy number 31: distance to point */
  4.3027 -  if (strategy == 31) {
  4.3028 -    /* query datum is a point */
  4.3029 -    pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
  4.3030 -    /* use pgl_estimate_pointkey_distance() function to compute result */
  4.3031 -    distance = pgl_estimate_key_distance(key, query);
  4.3032 -    /* avoid infinity (reserved!) */
  4.3033 -    if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  4.3034 -    /* return result */
  4.3035 -    PG_RETURN_FLOAT8(distance);
  4.3036 -  }
  4.3037 -  /* strategy number 33: distance to circle */
  4.3038 -  if (strategy == 33) {
  4.3039 -    /* query datum is a circle */
  4.3040 -    pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
  4.3041 -    /* estimate distance to circle center and substract circle radius */
  4.3042 -    distance = (
  4.3043 -      pgl_estimate_key_distance(key, &(query->center)) - query->radius
  4.3044 -    );
  4.3045 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  4.3046 -    if (distance <= 0) distance = 0;
  4.3047 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  4.3048 -    /* return result */
  4.3049 -    PG_RETURN_FLOAT8(distance);
  4.3050 -  }
  4.3051 -  /* strategy number 34: distance to cluster */
  4.3052 -  if (strategy == 34) {
  4.3053 -    /* query datum is a cluster */
  4.3054 -    pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
  4.3055 -    /* estimate distance to bounding center and substract bounding radius */
  4.3056 -    distance = (
  4.3057 -      pgl_estimate_key_distance(key, &(query->bounding.center)) -
  4.3058 -      query->bounding.radius
  4.3059 -    );
  4.3060 -    /* convert non-positive values to zero and avoid infinity (reserved!) */
  4.3061 -    if (distance <= 0) distance = 0;
  4.3062 -    else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
  4.3063 -    /* free detoasted cluster (if copy) */
  4.3064 -    PG_FREE_IF_COPY(query, 1);
  4.3065 -    /* return result */
  4.3066 -    PG_RETURN_FLOAT8(distance);
  4.3067 -  }
  4.3068 -  /* throw error for any unknown strategy number */
  4.3069 -  elog(ERROR, "unrecognized strategy number: %d", strategy);
  4.3070 -}
  4.3071 -
     5.1 --- a/latlon.control	Thu Sep 22 21:57:03 2016 +0200
     5.2 +++ b/latlon.control	Thu Sep 22 23:31:29 2016 +0200
     5.3 @@ -1,3 +1,3 @@
     5.4  comment = 'Geographic data types and spatial indexing for the WGS-84 spheroid'
     5.5 -default_version = '0.5'
     5.6 +default_version = '0.6'
     5.7  relocatable = true

Impressum / About Us