pgLatLon
changeset 69:882b77aee653
Prepared file names for version 0.15
author | jbe |
---|---|
date | Wed Feb 12 11:08:37 2020 +0100 (2020-02-12) |
parents | 9683f651cbd9 |
children | d06b066fb8ad |
files | GNUmakefile latlon--0.12--0.13.sql latlon--0.13--0.14.sql latlon--0.14--0.15.sql latlon--0.14.sql latlon--0.15.sql latlon-v0009.c latlon-v0010.c latlon.control |
line diff
1.1 --- a/GNUmakefile Tue Feb 11 02:35:16 2020 +0100 1.2 +++ b/GNUmakefile Wed Feb 12 11:08:37 2020 +0100 1.3 @@ -1,6 +1,6 @@ 1.4 EXTENSION = latlon 1.5 -DATA = latlon--0.12--0.13.sql latlon--0.13--0.14.sql latlon--0.14.sql 1.6 -MODULES = latlon-v0009 1.7 +DATA = latlon--0.14--0.15.sql latlon--0.15.sql 1.8 +MODULES = latlon-v0010 1.9 1.10 PG_CONFIG = pg_config 1.11 PGXS := $(shell $(PG_CONFIG) --pgxs)
2.1 --- a/latlon--0.12--0.13.sql Tue Feb 11 02:35:16 2020 +0100 2.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 2.3 @@ -1,2 +0,0 @@ 2.4 --- Nothing to do here, as version 0.13 only contained compatibility changes to 2.5 --- allow compilation with recent PostgreSQL versions.
3.1 --- a/latlon--0.13--0.14.sql Tue Feb 11 02:35:16 2020 +0100 3.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 3.3 @@ -1,67 +0,0 @@ 3.4 -DROP OPERATOR CLASS epoint_ops USING gist; 3.5 -DROP OPERATOR CLASS ecluster_ops USING gist; 3.6 - 3.7 -DROP OPERATOR <@ (epoint, ecluster); 3.8 - 3.9 -CREATE OPERATOR <@ ( 3.10 - leftarg = epoint, 3.11 - rightarg = ecluster, 3.12 - procedure = epoint_ecluster_overlap_proc, 3.13 - commutator = @>, 3.14 - restrict = areasel, 3.15 - join = areajoinsel 3.16 -); 3.17 - 3.18 -CREATE OPERATOR CLASS epoint_ops 3.19 - DEFAULT FOR TYPE epoint USING gist AS 3.20 - OPERATOR 11 = , 3.21 - OPERATOR 22 && (epoint, ebox), 3.22 - OPERATOR 222 <@ (epoint, ebox), 3.23 - OPERATOR 23 && (epoint, ecircle), 3.24 - OPERATOR 24 && (epoint, ecluster), 3.25 - OPERATOR 124 &&+ (epoint, ecluster), 3.26 - OPERATOR 224 <@ (epoint, ecluster), 3.27 - OPERATOR 31 <-> (epoint, epoint) FOR ORDER BY float_ops, 3.28 - OPERATOR 32 <-> (epoint, ebox) FOR ORDER BY float_ops, 3.29 - OPERATOR 33 <-> (epoint, ecircle) FOR ORDER BY float_ops, 3.30 - OPERATOR 34 <-> (epoint, ecluster) FOR ORDER BY float_ops, 3.31 - FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 3.32 - FUNCTION 2 pgl_gist_union(internal, internal), 3.33 - FUNCTION 3 pgl_gist_compress_epoint(internal), 3.34 - FUNCTION 4 pgl_gist_decompress(internal), 3.35 - FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 3.36 - FUNCTION 6 pgl_gist_picksplit(internal, internal), 3.37 - FUNCTION 7 pgl_gist_same(internal, internal, internal), 3.38 - FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 3.39 - STORAGE ekey_point; 3.40 - 3.41 -CREATE OPERATOR CLASS ecluster_ops 3.42 - DEFAULT FOR TYPE ecluster USING gist AS 3.43 - OPERATOR 21 && (ecluster, epoint), 3.44 - OPERATOR 121 &&+ (ecluster, epoint), 3.45 - OPERATOR 221 @> (ecluster, epoint), 3.46 - OPERATOR 22 && (ecluster, ebox), 3.47 - OPERATOR 122 &&+ (ecluster, ebox), 3.48 - OPERATOR 222 @> (ecluster, ebox), 3.49 - OPERATOR 322 <@ (ecluster, ebox), 3.50 - OPERATOR 23 && (ecluster, ecircle), 3.51 - OPERATOR 123 &&+ (ecluster, ecircle), 3.52 - OPERATOR 24 && (ecluster, ecluster), 3.53 - OPERATOR 124 &&+ (ecluster, ecluster), 3.54 - OPERATOR 224 @> (ecluster, ecluster), 3.55 - OPERATOR 324 <@ (ecluster, ecluster), 3.56 - OPERATOR 31 <-> (ecluster, epoint) FOR ORDER BY float_ops, 3.57 - OPERATOR 32 <-> (ecluster, ebox) FOR ORDER BY float_ops, 3.58 - OPERATOR 33 <-> (ecluster, ecircle) FOR ORDER BY float_ops, 3.59 - OPERATOR 34 <-> (ecluster, ecluster) FOR ORDER BY float_ops, 3.60 - OPERATOR 131 <=> (ecluster, epoint_with_sample_count) FOR ORDER BY float_ops, 3.61 - FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 3.62 - FUNCTION 2 pgl_gist_union(internal, internal), 3.63 - FUNCTION 3 pgl_gist_compress_ecluster(internal), 3.64 - FUNCTION 4 pgl_gist_decompress(internal), 3.65 - FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 3.66 - FUNCTION 6 pgl_gist_picksplit(internal, internal), 3.67 - FUNCTION 7 pgl_gist_same(internal, internal, internal), 3.68 - FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 3.69 - STORAGE ekey_area; 3.70 -
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/latlon--0.14--0.15.sql Wed Feb 12 11:08:37 2020 +0100 4.3 @@ -0,0 +1,512 @@ 4.4 + 4.5 +------------------------------------------------------------ 4.6 +-- dummy input/output functions for dummy index key types -- 4.7 +------------------------------------------------------------ 4.8 + 4.9 +CREATE OR REPLACE FUNCTION ekey_point_in_dummy(cstring) 4.10 + RETURNS ekey_point 4.11 + LANGUAGE C IMMUTABLE STRICT 4.12 + AS '$libdir/latlon-v0010', 'pgl_notimpl'; 4.13 + 4.14 +CREATE OR REPLACE FUNCTION ekey_point_out_dummy(ekey_point) 4.15 + RETURNS cstring 4.16 + LANGUAGE C IMMUTABLE STRICT 4.17 + AS '$libdir/latlon-v0010', 'pgl_notimpl'; 4.18 + 4.19 +CREATE OR REPLACE FUNCTION ekey_area_in_dummy(cstring) 4.20 + RETURNS ekey_area 4.21 + LANGUAGE C IMMUTABLE STRICT 4.22 + AS '$libdir/latlon-v0010', 'pgl_notimpl'; 4.23 + 4.24 +CREATE OR REPLACE FUNCTION ekey_area_out_dummy(ekey_area) 4.25 + RETURNS cstring 4.26 + LANGUAGE C IMMUTABLE STRICT 4.27 + AS '$libdir/latlon-v0010', 'pgl_notimpl'; 4.28 + 4.29 + 4.30 +-------------------------- 4.31 +-- text input functions -- 4.32 +-------------------------- 4.33 + 4.34 +CREATE OR REPLACE FUNCTION epoint_in(cstring) 4.35 + RETURNS epoint 4.36 + LANGUAGE C IMMUTABLE STRICT 4.37 + AS '$libdir/latlon-v0010', 'pgl_epoint_in'; 4.38 + 4.39 +CREATE OR REPLACE FUNCTION epoint_with_sample_count_in(cstring) 4.40 + RETURNS epoint_with_sample_count 4.41 + LANGUAGE C IMMUTABLE STRICT 4.42 + AS '$libdir/latlon-v0010', 'pgl_epoint_with_sample_count_in'; 4.43 + 4.44 +CREATE OR REPLACE FUNCTION ebox_in(cstring) 4.45 + RETURNS ebox 4.46 + LANGUAGE C IMMUTABLE STRICT 4.47 + AS '$libdir/latlon-v0010', 'pgl_ebox_in'; 4.48 + 4.49 +CREATE OR REPLACE FUNCTION ecircle_in(cstring) 4.50 + RETURNS ecircle 4.51 + LANGUAGE C IMMUTABLE STRICT 4.52 + AS '$libdir/latlon-v0010', 'pgl_ecircle_in'; 4.53 + 4.54 +CREATE OR REPLACE FUNCTION ecluster_in(cstring) 4.55 + RETURNS ecluster 4.56 + LANGUAGE C IMMUTABLE STRICT 4.57 + AS '$libdir/latlon-v0010', 'pgl_ecluster_in'; 4.58 + 4.59 + 4.60 +--------------------------- 4.61 +-- text output functions -- 4.62 +--------------------------- 4.63 + 4.64 +CREATE OR REPLACE FUNCTION epoint_out(epoint) 4.65 + RETURNS cstring 4.66 + LANGUAGE C IMMUTABLE STRICT 4.67 + AS '$libdir/latlon-v0010', 'pgl_epoint_out'; 4.68 + 4.69 +CREATE OR REPLACE FUNCTION epoint_with_sample_count_out(epoint_with_sample_count) 4.70 + RETURNS cstring 4.71 + LANGUAGE C IMMUTABLE STRICT 4.72 + AS '$libdir/latlon-v0010', 'pgl_epoint_with_sample_count_out'; 4.73 + 4.74 +CREATE OR REPLACE FUNCTION ebox_out(ebox) 4.75 + RETURNS cstring 4.76 + LANGUAGE C IMMUTABLE STRICT 4.77 + AS '$libdir/latlon-v0010', 'pgl_ebox_out'; 4.78 + 4.79 +CREATE OR REPLACE FUNCTION ecircle_out(ecircle) 4.80 + RETURNS cstring 4.81 + LANGUAGE C IMMUTABLE STRICT 4.82 + AS '$libdir/latlon-v0010', 'pgl_ecircle_out'; 4.83 + 4.84 +CREATE OR REPLACE FUNCTION ecluster_out(ecluster) 4.85 + RETURNS cstring 4.86 + LANGUAGE C IMMUTABLE STRICT 4.87 + AS '$libdir/latlon-v0010', 'pgl_ecluster_out'; 4.88 + 4.89 + 4.90 +-------------------------- 4.91 +-- binary I/O functions -- 4.92 +-------------------------- 4.93 + 4.94 +CREATE OR REPLACE FUNCTION epoint_recv(internal) 4.95 + RETURNS epoint 4.96 + LANGUAGE C IMMUTABLE STRICT 4.97 + AS '$libdir/latlon-v0010', 'pgl_epoint_recv'; 4.98 + 4.99 +CREATE OR REPLACE FUNCTION ebox_recv(internal) 4.100 + RETURNS ebox 4.101 + LANGUAGE C IMMUTABLE STRICT 4.102 + AS '$libdir/latlon-v0010', 'pgl_ebox_recv'; 4.103 + 4.104 +CREATE OR REPLACE FUNCTION ecircle_recv(internal) 4.105 + RETURNS ecircle 4.106 + LANGUAGE C IMMUTABLE STRICT 4.107 + AS '$libdir/latlon-v0010', 'pgl_ecircle_recv'; 4.108 + 4.109 +CREATE OR REPLACE FUNCTION epoint_send(epoint) 4.110 + RETURNS bytea 4.111 + LANGUAGE C IMMUTABLE STRICT 4.112 + AS '$libdir/latlon-v0010', 'pgl_epoint_send'; 4.113 + 4.114 +CREATE OR REPLACE FUNCTION ebox_send(ebox) 4.115 + RETURNS bytea 4.116 + LANGUAGE C IMMUTABLE STRICT 4.117 + AS '$libdir/latlon-v0010', 'pgl_ebox_send'; 4.118 + 4.119 +CREATE OR REPLACE FUNCTION ecircle_send(ecircle) 4.120 + RETURNS bytea 4.121 + LANGUAGE C IMMUTABLE STRICT 4.122 + AS '$libdir/latlon-v0010', 'pgl_ecircle_send'; 4.123 + 4.124 + 4.125 +-------------------- 4.126 +-- B-tree support -- 4.127 +-------------------- 4.128 + 4.129 +-- begin of B-tree support for epoint 4.130 + 4.131 +CREATE OR REPLACE FUNCTION epoint_btree_lt(epoint, epoint) 4.132 + RETURNS boolean 4.133 + LANGUAGE C IMMUTABLE STRICT 4.134 + AS '$libdir/latlon-v0010', 'pgl_btree_epoint_lt'; 4.135 + 4.136 +CREATE OR REPLACE FUNCTION epoint_btree_le(epoint, epoint) 4.137 + RETURNS boolean 4.138 + LANGUAGE C IMMUTABLE STRICT 4.139 + AS '$libdir/latlon-v0010', 'pgl_btree_epoint_le'; 4.140 + 4.141 +CREATE OR REPLACE FUNCTION epoint_btree_eq(epoint, epoint) 4.142 + RETURNS boolean 4.143 + LANGUAGE C IMMUTABLE STRICT 4.144 + AS '$libdir/latlon-v0010', 'pgl_btree_epoint_eq'; 4.145 + 4.146 +CREATE OR REPLACE FUNCTION epoint_btree_ne(epoint, epoint) 4.147 + RETURNS boolean 4.148 + LANGUAGE C IMMUTABLE STRICT 4.149 + AS '$libdir/latlon-v0010', 'pgl_btree_epoint_ne'; 4.150 + 4.151 +CREATE OR REPLACE FUNCTION epoint_btree_ge(epoint, epoint) 4.152 + RETURNS boolean 4.153 + LANGUAGE C IMMUTABLE STRICT 4.154 + AS '$libdir/latlon-v0010', 'pgl_btree_epoint_ge'; 4.155 + 4.156 +CREATE OR REPLACE FUNCTION epoint_btree_gt(epoint, epoint) 4.157 + RETURNS boolean 4.158 + LANGUAGE C IMMUTABLE STRICT 4.159 + AS '$libdir/latlon-v0010', 'pgl_btree_epoint_gt'; 4.160 + 4.161 +CREATE OR REPLACE FUNCTION epoint_btree_cmp(epoint, epoint) 4.162 + RETURNS int4 4.163 + LANGUAGE C IMMUTABLE STRICT 4.164 + AS '$libdir/latlon-v0010', 'pgl_btree_epoint_cmp'; 4.165 + 4.166 +-- end of B-tree support for epoint 4.167 + 4.168 +-- begin of B-tree support for ebox 4.169 + 4.170 +CREATE OR REPLACE FUNCTION ebox_btree_lt(ebox, ebox) 4.171 + RETURNS boolean 4.172 + LANGUAGE C IMMUTABLE STRICT 4.173 + AS '$libdir/latlon-v0010', 'pgl_btree_ebox_lt'; 4.174 + 4.175 +CREATE OR REPLACE FUNCTION ebox_btree_le(ebox, ebox) 4.176 + RETURNS boolean 4.177 + LANGUAGE C IMMUTABLE STRICT 4.178 + AS '$libdir/latlon-v0010', 'pgl_btree_ebox_le'; 4.179 + 4.180 +CREATE OR REPLACE FUNCTION ebox_btree_eq(ebox, ebox) 4.181 + RETURNS boolean 4.182 + LANGUAGE C IMMUTABLE STRICT 4.183 + AS '$libdir/latlon-v0010', 'pgl_btree_ebox_eq'; 4.184 + 4.185 +CREATE OR REPLACE FUNCTION ebox_btree_ne(ebox, ebox) 4.186 + RETURNS boolean 4.187 + LANGUAGE C IMMUTABLE STRICT 4.188 + AS '$libdir/latlon-v0010', 'pgl_btree_ebox_ne'; 4.189 + 4.190 +CREATE OR REPLACE FUNCTION ebox_btree_ge(ebox, ebox) 4.191 + RETURNS boolean 4.192 + LANGUAGE C IMMUTABLE STRICT 4.193 + AS '$libdir/latlon-v0010', 'pgl_btree_ebox_ge'; 4.194 + 4.195 +CREATE OR REPLACE FUNCTION ebox_btree_gt(ebox, ebox) 4.196 + RETURNS boolean 4.197 + LANGUAGE C IMMUTABLE STRICT 4.198 + AS '$libdir/latlon-v0010', 'pgl_btree_ebox_gt'; 4.199 + 4.200 +CREATE OR REPLACE FUNCTION ebox_btree_cmp(ebox, ebox) 4.201 + RETURNS int4 4.202 + LANGUAGE C IMMUTABLE STRICT 4.203 + AS '$libdir/latlon-v0010', 'pgl_btree_ebox_cmp'; 4.204 + 4.205 +-- end of B-tree support for ebox 4.206 + 4.207 +-- begin of B-tree support for ecircle 4.208 + 4.209 +CREATE OR REPLACE FUNCTION ecircle_btree_lt(ecircle, ecircle) 4.210 + RETURNS boolean 4.211 + LANGUAGE C IMMUTABLE STRICT 4.212 + AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_lt'; 4.213 + 4.214 +CREATE OR REPLACE FUNCTION ecircle_btree_le(ecircle, ecircle) 4.215 + RETURNS boolean 4.216 + LANGUAGE C IMMUTABLE STRICT 4.217 + AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_le'; 4.218 + 4.219 +CREATE OR REPLACE FUNCTION ecircle_btree_eq(ecircle, ecircle) 4.220 + RETURNS boolean 4.221 + LANGUAGE C IMMUTABLE STRICT 4.222 + AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_eq'; 4.223 + 4.224 +CREATE OR REPLACE FUNCTION ecircle_btree_ne(ecircle, ecircle) 4.225 + RETURNS boolean 4.226 + LANGUAGE C IMMUTABLE STRICT 4.227 + AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_ne'; 4.228 + 4.229 +CREATE OR REPLACE FUNCTION ecircle_btree_ge(ecircle, ecircle) 4.230 + RETURNS boolean 4.231 + LANGUAGE C IMMUTABLE STRICT 4.232 + AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_ge'; 4.233 + 4.234 +CREATE OR REPLACE FUNCTION ecircle_btree_gt(ecircle, ecircle) 4.235 + RETURNS boolean 4.236 + LANGUAGE C IMMUTABLE STRICT 4.237 + AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_gt'; 4.238 + 4.239 +CREATE OR REPLACE FUNCTION ecircle_btree_cmp(ecircle, ecircle) 4.240 + RETURNS int4 4.241 + LANGUAGE C IMMUTABLE STRICT 4.242 + AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_cmp'; 4.243 + 4.244 +-- end of B-tree support for ecircle 4.245 + 4.246 + 4.247 +---------------- 4.248 +-- type casts -- 4.249 +---------------- 4.250 + 4.251 +CREATE OR REPLACE FUNCTION cast_epoint_to_ebox(epoint) 4.252 + RETURNS ebox 4.253 + LANGUAGE C IMMUTABLE STRICT 4.254 + AS '$libdir/latlon-v0010', 'pgl_epoint_to_ebox'; 4.255 + 4.256 +CREATE OR REPLACE FUNCTION cast_epoint_to_ecircle(epoint) 4.257 + RETURNS ecircle 4.258 + LANGUAGE C IMMUTABLE STRICT 4.259 + AS '$libdir/latlon-v0010', 'pgl_epoint_to_ecircle'; 4.260 + 4.261 +CREATE OR REPLACE FUNCTION cast_epoint_to_ecluster(epoint) 4.262 + RETURNS ecluster 4.263 + LANGUAGE C IMMUTABLE STRICT 4.264 + AS '$libdir/latlon-v0010', 'pgl_epoint_to_ecluster'; 4.265 + 4.266 +CREATE OR REPLACE FUNCTION cast_ebox_to_ecluster(ebox) 4.267 + RETURNS ecluster 4.268 + LANGUAGE C IMMUTABLE STRICT 4.269 + AS '$libdir/latlon-v0010', 'pgl_ebox_to_ecluster'; 4.270 + 4.271 + 4.272 +--------------------------- 4.273 +-- constructor functions -- 4.274 +--------------------------- 4.275 + 4.276 +CREATE OR REPLACE FUNCTION epoint(float8, float8) 4.277 + RETURNS epoint 4.278 + LANGUAGE C IMMUTABLE STRICT 4.279 + AS '$libdir/latlon-v0010', 'pgl_create_epoint'; 4.280 + 4.281 +CREATE OR REPLACE FUNCTION epoint_with_sample_count(epoint, int4) 4.282 + RETURNS epoint_with_sample_count 4.283 + LANGUAGE C IMMUTABLE STRICT 4.284 + AS '$libdir/latlon-v0010', 'pgl_create_epoint_with_sample_count'; 4.285 + 4.286 +CREATE OR REPLACE FUNCTION empty_ebox() 4.287 + RETURNS ebox 4.288 + LANGUAGE C IMMUTABLE STRICT 4.289 + AS '$libdir/latlon-v0010', 'pgl_create_empty_ebox'; 4.290 + 4.291 +CREATE OR REPLACE FUNCTION ebox(float8, float8, float8, float8) 4.292 + RETURNS ebox 4.293 + LANGUAGE C IMMUTABLE STRICT 4.294 + AS '$libdir/latlon-v0010', 'pgl_create_ebox'; 4.295 + 4.296 +CREATE OR REPLACE FUNCTION ebox(epoint, epoint) 4.297 + RETURNS ebox 4.298 + LANGUAGE C IMMUTABLE STRICT 4.299 + AS '$libdir/latlon-v0010', 'pgl_create_ebox_from_epoints'; 4.300 + 4.301 +CREATE OR REPLACE FUNCTION ecircle(float8, float8, float8) 4.302 + RETURNS ecircle 4.303 + LANGUAGE C IMMUTABLE STRICT 4.304 + AS '$libdir/latlon-v0010', 'pgl_create_ecircle'; 4.305 + 4.306 +CREATE OR REPLACE FUNCTION ecircle(epoint, float8) 4.307 + RETURNS ecircle 4.308 + LANGUAGE C IMMUTABLE STRICT 4.309 + AS '$libdir/latlon-v0010', 'pgl_create_ecircle_from_epoint'; 4.310 + 4.311 + 4.312 +---------------------- 4.313 +-- getter functions -- 4.314 +---------------------- 4.315 + 4.316 +CREATE OR REPLACE FUNCTION latitude(epoint) 4.317 + RETURNS float8 4.318 + LANGUAGE C IMMUTABLE STRICT 4.319 + AS '$libdir/latlon-v0010', 'pgl_epoint_lat'; 4.320 + 4.321 +CREATE OR REPLACE FUNCTION longitude(epoint) 4.322 + RETURNS float8 4.323 + LANGUAGE C IMMUTABLE STRICT 4.324 + AS '$libdir/latlon-v0010', 'pgl_epoint_lon'; 4.325 + 4.326 +CREATE OR REPLACE FUNCTION min_latitude(ebox) 4.327 + RETURNS float8 4.328 + LANGUAGE C IMMUTABLE STRICT 4.329 + AS '$libdir/latlon-v0010', 'pgl_ebox_lat_min'; 4.330 + 4.331 +CREATE OR REPLACE FUNCTION max_latitude(ebox) 4.332 + RETURNS float8 4.333 + LANGUAGE C IMMUTABLE STRICT 4.334 + AS '$libdir/latlon-v0010', 'pgl_ebox_lat_max'; 4.335 + 4.336 +CREATE OR REPLACE FUNCTION min_longitude(ebox) 4.337 + RETURNS float8 4.338 + LANGUAGE C IMMUTABLE STRICT 4.339 + AS '$libdir/latlon-v0010', 'pgl_ebox_lon_min'; 4.340 + 4.341 +CREATE OR REPLACE FUNCTION max_longitude(ebox) 4.342 + RETURNS float8 4.343 + LANGUAGE C IMMUTABLE STRICT 4.344 + AS '$libdir/latlon-v0010', 'pgl_ebox_lon_max'; 4.345 + 4.346 +CREATE OR REPLACE FUNCTION center(ecircle) 4.347 + RETURNS epoint 4.348 + LANGUAGE C IMMUTABLE STRICT 4.349 + AS '$libdir/latlon-v0010', 'pgl_ecircle_center'; 4.350 + 4.351 +CREATE OR REPLACE FUNCTION radius(ecircle) 4.352 + RETURNS float8 4.353 + LANGUAGE C IMMUTABLE STRICT 4.354 + AS '$libdir/latlon-v0010', 'pgl_ecircle_radius'; 4.355 + 4.356 + 4.357 +--------------- 4.358 +-- operators -- 4.359 +--------------- 4.360 + 4.361 +CREATE OR REPLACE FUNCTION epoint_ebox_overlap_proc(epoint, ebox) 4.362 + RETURNS boolean 4.363 + LANGUAGE C IMMUTABLE STRICT 4.364 + AS '$libdir/latlon-v0010', 'pgl_epoint_ebox_overlap'; 4.365 + 4.366 +CREATE OR REPLACE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle) 4.367 + RETURNS boolean 4.368 + LANGUAGE C IMMUTABLE STRICT 4.369 + AS '$libdir/latlon-v0010', 'pgl_epoint_ecircle_overlap'; 4.370 + 4.371 +CREATE OR REPLACE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster) 4.372 + RETURNS boolean 4.373 + LANGUAGE C IMMUTABLE STRICT 4.374 + AS '$libdir/latlon-v0010', 'pgl_epoint_ecluster_overlap'; 4.375 + 4.376 +CREATE OR REPLACE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster) 4.377 + RETURNS boolean 4.378 + LANGUAGE C IMMUTABLE STRICT 4.379 + AS '$libdir/latlon-v0010', 'pgl_epoint_ecluster_may_overlap'; 4.380 + 4.381 +CREATE OR REPLACE FUNCTION ebox_overlap_proc(ebox, ebox) 4.382 + RETURNS boolean 4.383 + LANGUAGE C IMMUTABLE STRICT 4.384 + AS '$libdir/latlon-v0010', 'pgl_ebox_overlap'; 4.385 + 4.386 +CREATE OR REPLACE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle) 4.387 + RETURNS boolean 4.388 + LANGUAGE C IMMUTABLE STRICT 4.389 + AS '$libdir/latlon-v0010', 'pgl_ebox_ecircle_may_overlap'; 4.390 + 4.391 +CREATE OR REPLACE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster) 4.392 + RETURNS boolean 4.393 + LANGUAGE C IMMUTABLE STRICT 4.394 + AS '$libdir/latlon-v0010', 'pgl_ebox_ecluster_may_overlap'; 4.395 + 4.396 +CREATE OR REPLACE FUNCTION ecircle_overlap_proc(ecircle, ecircle) 4.397 + RETURNS boolean 4.398 + LANGUAGE C IMMUTABLE STRICT 4.399 + AS '$libdir/latlon-v0010', 'pgl_ecircle_overlap'; 4.400 + 4.401 +CREATE OR REPLACE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster) 4.402 + RETURNS boolean 4.403 + LANGUAGE C IMMUTABLE STRICT 4.404 + AS '$libdir/latlon-v0010', 'pgl_ecircle_ecluster_overlap'; 4.405 + 4.406 +CREATE OR REPLACE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster) 4.407 + RETURNS boolean 4.408 + LANGUAGE C IMMUTABLE STRICT 4.409 + AS '$libdir/latlon-v0010', 'pgl_ecircle_ecluster_may_overlap'; 4.410 + 4.411 +CREATE OR REPLACE FUNCTION ecluster_overlap_proc(ecluster, ecluster) 4.412 + RETURNS boolean 4.413 + LANGUAGE C IMMUTABLE STRICT 4.414 + AS '$libdir/latlon-v0010', 'pgl_ecluster_overlap'; 4.415 + 4.416 +CREATE OR REPLACE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster) 4.417 + RETURNS boolean 4.418 + LANGUAGE C IMMUTABLE STRICT 4.419 + AS '$libdir/latlon-v0010', 'pgl_ecluster_may_overlap'; 4.420 + 4.421 +CREATE OR REPLACE FUNCTION ecluster_contains_proc(ecluster, ecluster) 4.422 + RETURNS boolean 4.423 + LANGUAGE C IMMUTABLE STRICT 4.424 + AS '$libdir/latlon-v0010', 'pgl_ecluster_contains'; 4.425 + 4.426 +CREATE OR REPLACE FUNCTION epoint_distance_proc(epoint, epoint) 4.427 + RETURNS float8 4.428 + LANGUAGE C IMMUTABLE STRICT 4.429 + AS '$libdir/latlon-v0010', 'pgl_epoint_distance'; 4.430 + 4.431 +CREATE OR REPLACE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle) 4.432 + RETURNS float8 4.433 + LANGUAGE C IMMUTABLE STRICT 4.434 + AS '$libdir/latlon-v0010', 'pgl_epoint_ecircle_distance'; 4.435 + 4.436 +CREATE OR REPLACE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster) 4.437 + RETURNS float8 4.438 + LANGUAGE C IMMUTABLE STRICT 4.439 + AS '$libdir/latlon-v0010', 'pgl_epoint_ecluster_distance'; 4.440 + 4.441 +CREATE OR REPLACE FUNCTION ecircle_distance_proc(ecircle, ecircle) 4.442 + RETURNS float8 4.443 + LANGUAGE C IMMUTABLE STRICT 4.444 + AS '$libdir/latlon-v0010', 'pgl_ecircle_distance'; 4.445 + 4.446 +CREATE OR REPLACE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster) 4.447 + RETURNS float8 4.448 + LANGUAGE C IMMUTABLE STRICT 4.449 + AS '$libdir/latlon-v0010', 'pgl_ecircle_ecluster_distance'; 4.450 + 4.451 +CREATE OR REPLACE FUNCTION ecluster_distance_proc(ecluster, ecluster) 4.452 + RETURNS float8 4.453 + LANGUAGE C IMMUTABLE STRICT 4.454 + AS '$libdir/latlon-v0010', 'pgl_ecluster_distance'; 4.455 + 4.456 +CREATE OR REPLACE FUNCTION fair_distance_operator_proc(ecluster, epoint_with_sample_count) 4.457 + RETURNS float8 4.458 + LANGUAGE C IMMUTABLE STRICT 4.459 + AS '$libdir/latlon-v0010', 'pgl_ecluster_epoint_sc_fair_distance'; 4.460 + 4.461 + 4.462 +---------------- 4.463 +-- GiST index -- 4.464 +---------------- 4.465 + 4.466 +CREATE OR REPLACE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal) 4.467 + RETURNS boolean 4.468 + LANGUAGE C STRICT 4.469 + AS '$libdir/latlon-v0010', 'pgl_gist_consistent'; 4.470 + 4.471 +CREATE OR REPLACE FUNCTION pgl_gist_union(internal, internal) 4.472 + RETURNS internal 4.473 + LANGUAGE C STRICT 4.474 + AS '$libdir/latlon-v0010', 'pgl_gist_union'; 4.475 + 4.476 +CREATE OR REPLACE FUNCTION pgl_gist_compress_epoint(internal) 4.477 + RETURNS internal 4.478 + LANGUAGE C STRICT 4.479 + AS '$libdir/latlon-v0010', 'pgl_gist_compress_epoint'; 4.480 + 4.481 +CREATE OR REPLACE FUNCTION pgl_gist_compress_ecircle(internal) 4.482 + RETURNS internal 4.483 + LANGUAGE C STRICT 4.484 + AS '$libdir/latlon-v0010', 'pgl_gist_compress_ecircle'; 4.485 + 4.486 +CREATE OR REPLACE FUNCTION pgl_gist_compress_ecluster(internal) 4.487 + RETURNS internal 4.488 + LANGUAGE C STRICT 4.489 + AS '$libdir/latlon-v0010', 'pgl_gist_compress_ecluster'; 4.490 + 4.491 +CREATE OR REPLACE FUNCTION pgl_gist_decompress(internal) 4.492 + RETURNS internal 4.493 + LANGUAGE C STRICT 4.494 + AS '$libdir/latlon-v0010', 'pgl_gist_decompress'; 4.495 + 4.496 +CREATE OR REPLACE FUNCTION pgl_gist_penalty(internal, internal, internal) 4.497 + RETURNS internal 4.498 + LANGUAGE C STRICT 4.499 + AS '$libdir/latlon-v0010', 'pgl_gist_penalty'; 4.500 + 4.501 +CREATE OR REPLACE FUNCTION pgl_gist_picksplit(internal, internal) 4.502 + RETURNS internal 4.503 + LANGUAGE C STRICT 4.504 + AS '$libdir/latlon-v0010', 'pgl_gist_picksplit'; 4.505 + 4.506 +CREATE OR REPLACE FUNCTION pgl_gist_same(internal, internal, internal) 4.507 + RETURNS internal 4.508 + LANGUAGE C STRICT 4.509 + AS '$libdir/latlon-v0010', 'pgl_gist_same'; 4.510 + 4.511 +CREATE OR REPLACE FUNCTION pgl_gist_distance(internal, internal, smallint, oid) 4.512 + RETURNS internal 4.513 + LANGUAGE C STRICT 4.514 + AS '$libdir/latlon-v0010', 'pgl_gist_distance'; 4.515 +
5.1 --- a/latlon--0.14.sql Tue Feb 11 02:35:16 2020 +0100 5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 5.3 @@ -1,1717 +0,0 @@ 5.4 - 5.5 ----------------------------------------- 5.6 --- forward declarations (shell types) -- 5.7 ----------------------------------------- 5.8 - 5.9 -CREATE TYPE ekey_point; 5.10 -CREATE TYPE ekey_area; 5.11 -CREATE TYPE epoint; 5.12 -CREATE TYPE epoint_with_sample_count; 5.13 -CREATE TYPE ebox; 5.14 -CREATE TYPE ecircle; 5.15 -CREATE TYPE ecluster; 5.16 - 5.17 - 5.18 ------------------------------------------------------------- 5.19 --- dummy input/output functions for dummy index key types -- 5.20 ------------------------------------------------------------- 5.21 - 5.22 -CREATE FUNCTION ekey_point_in_dummy(cstring) 5.23 - RETURNS ekey_point 5.24 - LANGUAGE C IMMUTABLE STRICT 5.25 - AS '$libdir/latlon-v0009', 'pgl_notimpl'; 5.26 - 5.27 -CREATE FUNCTION ekey_point_out_dummy(ekey_point) 5.28 - RETURNS cstring 5.29 - LANGUAGE C IMMUTABLE STRICT 5.30 - AS '$libdir/latlon-v0009', 'pgl_notimpl'; 5.31 - 5.32 -CREATE FUNCTION ekey_area_in_dummy(cstring) 5.33 - RETURNS ekey_area 5.34 - LANGUAGE C IMMUTABLE STRICT 5.35 - AS '$libdir/latlon-v0009', 'pgl_notimpl'; 5.36 - 5.37 -CREATE FUNCTION ekey_area_out_dummy(ekey_area) 5.38 - RETURNS cstring 5.39 - LANGUAGE C IMMUTABLE STRICT 5.40 - AS '$libdir/latlon-v0009', 'pgl_notimpl'; 5.41 - 5.42 - 5.43 --------------------------- 5.44 --- text input functions -- 5.45 --------------------------- 5.46 - 5.47 -CREATE FUNCTION epoint_in(cstring) 5.48 - RETURNS epoint 5.49 - LANGUAGE C IMMUTABLE STRICT 5.50 - AS '$libdir/latlon-v0009', 'pgl_epoint_in'; 5.51 - 5.52 -CREATE FUNCTION epoint_with_sample_count_in(cstring) 5.53 - RETURNS epoint_with_sample_count 5.54 - LANGUAGE C IMMUTABLE STRICT 5.55 - AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_in'; 5.56 - 5.57 -CREATE FUNCTION ebox_in(cstring) 5.58 - RETURNS ebox 5.59 - LANGUAGE C IMMUTABLE STRICT 5.60 - AS '$libdir/latlon-v0009', 'pgl_ebox_in'; 5.61 - 5.62 -CREATE FUNCTION ecircle_in(cstring) 5.63 - RETURNS ecircle 5.64 - LANGUAGE C IMMUTABLE STRICT 5.65 - AS '$libdir/latlon-v0009', 'pgl_ecircle_in'; 5.66 - 5.67 -CREATE FUNCTION ecluster_in(cstring) 5.68 - RETURNS ecluster 5.69 - LANGUAGE C IMMUTABLE STRICT 5.70 - AS '$libdir/latlon-v0009', 'pgl_ecluster_in'; 5.71 - 5.72 - 5.73 ---------------------------- 5.74 --- text output functions -- 5.75 ---------------------------- 5.76 - 5.77 -CREATE FUNCTION epoint_out(epoint) 5.78 - RETURNS cstring 5.79 - LANGUAGE C IMMUTABLE STRICT 5.80 - AS '$libdir/latlon-v0009', 'pgl_epoint_out'; 5.81 - 5.82 -CREATE FUNCTION epoint_with_sample_count_out(epoint_with_sample_count) 5.83 - RETURNS cstring 5.84 - LANGUAGE C IMMUTABLE STRICT 5.85 - AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_out'; 5.86 - 5.87 -CREATE FUNCTION ebox_out(ebox) 5.88 - RETURNS cstring 5.89 - LANGUAGE C IMMUTABLE STRICT 5.90 - AS '$libdir/latlon-v0009', 'pgl_ebox_out'; 5.91 - 5.92 -CREATE FUNCTION ecircle_out(ecircle) 5.93 - RETURNS cstring 5.94 - LANGUAGE C IMMUTABLE STRICT 5.95 - AS '$libdir/latlon-v0009', 'pgl_ecircle_out'; 5.96 - 5.97 -CREATE FUNCTION ecluster_out(ecluster) 5.98 - RETURNS cstring 5.99 - LANGUAGE C IMMUTABLE STRICT 5.100 - AS '$libdir/latlon-v0009', 'pgl_ecluster_out'; 5.101 - 5.102 - 5.103 --------------------------- 5.104 --- binary I/O functions -- 5.105 --------------------------- 5.106 - 5.107 -CREATE FUNCTION epoint_recv(internal) 5.108 - RETURNS epoint 5.109 - LANGUAGE C IMMUTABLE STRICT 5.110 - AS '$libdir/latlon-v0009', 'pgl_epoint_recv'; 5.111 - 5.112 -CREATE FUNCTION ebox_recv(internal) 5.113 - RETURNS ebox 5.114 - LANGUAGE C IMMUTABLE STRICT 5.115 - AS '$libdir/latlon-v0009', 'pgl_ebox_recv'; 5.116 - 5.117 -CREATE FUNCTION ecircle_recv(internal) 5.118 - RETURNS ecircle 5.119 - LANGUAGE C IMMUTABLE STRICT 5.120 - AS '$libdir/latlon-v0009', 'pgl_ecircle_recv'; 5.121 - 5.122 -CREATE FUNCTION epoint_send(epoint) 5.123 - RETURNS bytea 5.124 - LANGUAGE C IMMUTABLE STRICT 5.125 - AS '$libdir/latlon-v0009', 'pgl_epoint_send'; 5.126 - 5.127 -CREATE FUNCTION ebox_send(ebox) 5.128 - RETURNS bytea 5.129 - LANGUAGE C IMMUTABLE STRICT 5.130 - AS '$libdir/latlon-v0009', 'pgl_ebox_send'; 5.131 - 5.132 -CREATE FUNCTION ecircle_send(ecircle) 5.133 - RETURNS bytea 5.134 - LANGUAGE C IMMUTABLE STRICT 5.135 - AS '$libdir/latlon-v0009', 'pgl_ecircle_send'; 5.136 - 5.137 - 5.138 ------------------------------------------------ 5.139 --- type definitions of dummy index key types -- 5.140 ------------------------------------------------ 5.141 - 5.142 -CREATE TYPE ekey_point ( 5.143 - internallength = 8, 5.144 - input = ekey_point_in_dummy, 5.145 - output = ekey_point_out_dummy, 5.146 - alignment = char ); 5.147 - 5.148 -CREATE TYPE ekey_area ( 5.149 - internallength = 9, 5.150 - input = ekey_area_in_dummy, 5.151 - output = ekey_area_out_dummy, 5.152 - alignment = char ); 5.153 - 5.154 - 5.155 ------------------------------------------- 5.156 --- definitions of geographic data types -- 5.157 ------------------------------------------- 5.158 - 5.159 -CREATE TYPE epoint ( 5.160 - internallength = 16, 5.161 - input = epoint_in, 5.162 - output = epoint_out, 5.163 - receive = epoint_recv, 5.164 - send = epoint_send, 5.165 - alignment = double ); 5.166 - 5.167 -CREATE TYPE epoint_with_sample_count ( 5.168 - internallength = 20, 5.169 - input = epoint_with_sample_count_in, 5.170 - output = epoint_with_sample_count_out, 5.171 - alignment = double ); 5.172 - 5.173 -CREATE TYPE ebox ( 5.174 - internallength = 32, 5.175 - input = ebox_in, 5.176 - output = ebox_out, 5.177 - receive = ebox_recv, 5.178 - send = ebox_send, 5.179 - alignment = double ); 5.180 - 5.181 -CREATE TYPE ecircle ( 5.182 - internallength = 24, 5.183 - input = ecircle_in, 5.184 - output = ecircle_out, 5.185 - receive = ecircle_recv, 5.186 - send = ecircle_send, 5.187 - alignment = double ); 5.188 - 5.189 -CREATE TYPE ecluster ( 5.190 - internallength = VARIABLE, 5.191 - input = ecluster_in, 5.192 - output = ecluster_out, 5.193 - alignment = double, 5.194 - storage = external ); 5.195 - 5.196 - 5.197 --------------------- 5.198 --- B-tree support -- 5.199 --------------------- 5.200 - 5.201 --- begin of B-tree support for epoint 5.202 - 5.203 -CREATE FUNCTION epoint_btree_lt(epoint, epoint) 5.204 - RETURNS boolean 5.205 - LANGUAGE C IMMUTABLE STRICT 5.206 - AS '$libdir/latlon-v0009', 'pgl_btree_epoint_lt'; 5.207 - 5.208 -CREATE FUNCTION epoint_btree_le(epoint, epoint) 5.209 - RETURNS boolean 5.210 - LANGUAGE C IMMUTABLE STRICT 5.211 - AS '$libdir/latlon-v0009', 'pgl_btree_epoint_le'; 5.212 - 5.213 -CREATE FUNCTION epoint_btree_eq(epoint, epoint) 5.214 - RETURNS boolean 5.215 - LANGUAGE C IMMUTABLE STRICT 5.216 - AS '$libdir/latlon-v0009', 'pgl_btree_epoint_eq'; 5.217 - 5.218 -CREATE FUNCTION epoint_btree_ne(epoint, epoint) 5.219 - RETURNS boolean 5.220 - LANGUAGE C IMMUTABLE STRICT 5.221 - AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ne'; 5.222 - 5.223 -CREATE FUNCTION epoint_btree_ge(epoint, epoint) 5.224 - RETURNS boolean 5.225 - LANGUAGE C IMMUTABLE STRICT 5.226 - AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ge'; 5.227 - 5.228 -CREATE FUNCTION epoint_btree_gt(epoint, epoint) 5.229 - RETURNS boolean 5.230 - LANGUAGE C IMMUTABLE STRICT 5.231 - AS '$libdir/latlon-v0009', 'pgl_btree_epoint_gt'; 5.232 - 5.233 -CREATE OPERATOR <<< ( 5.234 - leftarg = epoint, 5.235 - rightarg = epoint, 5.236 - procedure = epoint_btree_lt, 5.237 - commutator = >>>, 5.238 - negator = >>>=, 5.239 - restrict = scalarltsel, 5.240 - join = scalarltjoinsel 5.241 -); 5.242 - 5.243 -CREATE OPERATOR <<<= ( 5.244 - leftarg = epoint, 5.245 - rightarg = epoint, 5.246 - procedure = epoint_btree_le, 5.247 - commutator = >>>=, 5.248 - negator = >>>, 5.249 - restrict = scalarltsel, 5.250 - join = scalarltjoinsel 5.251 -); 5.252 - 5.253 -CREATE OPERATOR = ( 5.254 - leftarg = epoint, 5.255 - rightarg = epoint, 5.256 - procedure = epoint_btree_eq, 5.257 - commutator = =, 5.258 - negator = <>, 5.259 - restrict = eqsel, 5.260 - join = eqjoinsel, 5.261 - merges 5.262 -); 5.263 - 5.264 -CREATE OPERATOR <> ( 5.265 - leftarg = epoint, 5.266 - rightarg = epoint, 5.267 - procedure = epoint_btree_ne, 5.268 - commutator = <>, 5.269 - negator = =, 5.270 - restrict = neqsel, 5.271 - join = neqjoinsel 5.272 -); 5.273 - 5.274 -CREATE OPERATOR >>>= ( 5.275 - leftarg = epoint, 5.276 - rightarg = epoint, 5.277 - procedure = epoint_btree_ge, 5.278 - commutator = <<<=, 5.279 - negator = <<<, 5.280 - restrict = scalargtsel, 5.281 - join = scalargtjoinsel 5.282 -); 5.283 - 5.284 -CREATE OPERATOR >>> ( 5.285 - leftarg = epoint, 5.286 - rightarg = epoint, 5.287 - procedure = epoint_btree_gt, 5.288 - commutator = <<<, 5.289 - negator = <<<=, 5.290 - restrict = scalargtsel, 5.291 - join = scalargtjoinsel 5.292 -); 5.293 - 5.294 -CREATE FUNCTION epoint_btree_cmp(epoint, epoint) 5.295 - RETURNS int4 5.296 - LANGUAGE C IMMUTABLE STRICT 5.297 - AS '$libdir/latlon-v0009', 'pgl_btree_epoint_cmp'; 5.298 - 5.299 -CREATE OPERATOR CLASS epoint_btree_ops 5.300 - DEFAULT FOR TYPE epoint USING btree AS 5.301 - OPERATOR 1 <<< , 5.302 - OPERATOR 2 <<<= , 5.303 - OPERATOR 3 = , 5.304 - OPERATOR 4 >>>= , 5.305 - OPERATOR 5 >>> , 5.306 - FUNCTION 1 epoint_btree_cmp(epoint, epoint); 5.307 - 5.308 --- end of B-tree support for epoint 5.309 - 5.310 --- begin of B-tree support for ebox 5.311 - 5.312 -CREATE FUNCTION ebox_btree_lt(ebox, ebox) 5.313 - RETURNS boolean 5.314 - LANGUAGE C IMMUTABLE STRICT 5.315 - AS '$libdir/latlon-v0009', 'pgl_btree_ebox_lt'; 5.316 - 5.317 -CREATE FUNCTION ebox_btree_le(ebox, ebox) 5.318 - RETURNS boolean 5.319 - LANGUAGE C IMMUTABLE STRICT 5.320 - AS '$libdir/latlon-v0009', 'pgl_btree_ebox_le'; 5.321 - 5.322 -CREATE FUNCTION ebox_btree_eq(ebox, ebox) 5.323 - RETURNS boolean 5.324 - LANGUAGE C IMMUTABLE STRICT 5.325 - AS '$libdir/latlon-v0009', 'pgl_btree_ebox_eq'; 5.326 - 5.327 -CREATE FUNCTION ebox_btree_ne(ebox, ebox) 5.328 - RETURNS boolean 5.329 - LANGUAGE C IMMUTABLE STRICT 5.330 - AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ne'; 5.331 - 5.332 -CREATE FUNCTION ebox_btree_ge(ebox, ebox) 5.333 - RETURNS boolean 5.334 - LANGUAGE C IMMUTABLE STRICT 5.335 - AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ge'; 5.336 - 5.337 -CREATE FUNCTION ebox_btree_gt(ebox, ebox) 5.338 - RETURNS boolean 5.339 - LANGUAGE C IMMUTABLE STRICT 5.340 - AS '$libdir/latlon-v0009', 'pgl_btree_ebox_gt'; 5.341 - 5.342 -CREATE OPERATOR <<< ( 5.343 - leftarg = ebox, 5.344 - rightarg = ebox, 5.345 - procedure = ebox_btree_lt, 5.346 - commutator = >>>, 5.347 - negator = >>>=, 5.348 - restrict = scalarltsel, 5.349 - join = scalarltjoinsel 5.350 -); 5.351 - 5.352 -CREATE OPERATOR <<<= ( 5.353 - leftarg = ebox, 5.354 - rightarg = ebox, 5.355 - procedure = ebox_btree_le, 5.356 - commutator = >>>=, 5.357 - negator = >>>, 5.358 - restrict = scalarltsel, 5.359 - join = scalarltjoinsel 5.360 -); 5.361 - 5.362 -CREATE OPERATOR = ( 5.363 - leftarg = ebox, 5.364 - rightarg = ebox, 5.365 - procedure = ebox_btree_eq, 5.366 - commutator = =, 5.367 - negator = <>, 5.368 - restrict = eqsel, 5.369 - join = eqjoinsel, 5.370 - merges 5.371 -); 5.372 - 5.373 -CREATE OPERATOR <> ( 5.374 - leftarg = ebox, 5.375 - rightarg = ebox, 5.376 - procedure = ebox_btree_ne, 5.377 - commutator = <>, 5.378 - negator = =, 5.379 - restrict = neqsel, 5.380 - join = neqjoinsel 5.381 -); 5.382 - 5.383 -CREATE OPERATOR >>>= ( 5.384 - leftarg = ebox, 5.385 - rightarg = ebox, 5.386 - procedure = ebox_btree_ge, 5.387 - commutator = <<<=, 5.388 - negator = <<<, 5.389 - restrict = scalargtsel, 5.390 - join = scalargtjoinsel 5.391 -); 5.392 - 5.393 -CREATE OPERATOR >>> ( 5.394 - leftarg = ebox, 5.395 - rightarg = ebox, 5.396 - procedure = ebox_btree_gt, 5.397 - commutator = <<<, 5.398 - negator = <<<=, 5.399 - restrict = scalargtsel, 5.400 - join = scalargtjoinsel 5.401 -); 5.402 - 5.403 -CREATE FUNCTION ebox_btree_cmp(ebox, ebox) 5.404 - RETURNS int4 5.405 - LANGUAGE C IMMUTABLE STRICT 5.406 - AS '$libdir/latlon-v0009', 'pgl_btree_ebox_cmp'; 5.407 - 5.408 -CREATE OPERATOR CLASS ebox_btree_ops 5.409 - DEFAULT FOR TYPE ebox USING btree AS 5.410 - OPERATOR 1 <<< , 5.411 - OPERATOR 2 <<<= , 5.412 - OPERATOR 3 = , 5.413 - OPERATOR 4 >>>= , 5.414 - OPERATOR 5 >>> , 5.415 - FUNCTION 1 ebox_btree_cmp(ebox, ebox); 5.416 - 5.417 --- end of B-tree support for ebox 5.418 - 5.419 --- begin of B-tree support for ecircle 5.420 - 5.421 -CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle) 5.422 - RETURNS boolean 5.423 - LANGUAGE C IMMUTABLE STRICT 5.424 - AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_lt'; 5.425 - 5.426 -CREATE FUNCTION ecircle_btree_le(ecircle, ecircle) 5.427 - RETURNS boolean 5.428 - LANGUAGE C IMMUTABLE STRICT 5.429 - AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_le'; 5.430 - 5.431 -CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle) 5.432 - RETURNS boolean 5.433 - LANGUAGE C IMMUTABLE STRICT 5.434 - AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_eq'; 5.435 - 5.436 -CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle) 5.437 - RETURNS boolean 5.438 - LANGUAGE C IMMUTABLE STRICT 5.439 - AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ne'; 5.440 - 5.441 -CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle) 5.442 - RETURNS boolean 5.443 - LANGUAGE C IMMUTABLE STRICT 5.444 - AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ge'; 5.445 - 5.446 -CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle) 5.447 - RETURNS boolean 5.448 - LANGUAGE C IMMUTABLE STRICT 5.449 - AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_gt'; 5.450 - 5.451 -CREATE OPERATOR <<< ( 5.452 - leftarg = ecircle, 5.453 - rightarg = ecircle, 5.454 - procedure = ecircle_btree_lt, 5.455 - commutator = >>>, 5.456 - negator = >>>=, 5.457 - restrict = scalarltsel, 5.458 - join = scalarltjoinsel 5.459 -); 5.460 - 5.461 -CREATE OPERATOR <<<= ( 5.462 - leftarg = ecircle, 5.463 - rightarg = ecircle, 5.464 - procedure = ecircle_btree_le, 5.465 - commutator = >>>=, 5.466 - negator = >>>, 5.467 - restrict = scalarltsel, 5.468 - join = scalarltjoinsel 5.469 -); 5.470 - 5.471 -CREATE OPERATOR = ( 5.472 - leftarg = ecircle, 5.473 - rightarg = ecircle, 5.474 - procedure = ecircle_btree_eq, 5.475 - commutator = =, 5.476 - negator = <>, 5.477 - restrict = eqsel, 5.478 - join = eqjoinsel, 5.479 - merges 5.480 -); 5.481 - 5.482 -CREATE OPERATOR <> ( 5.483 - leftarg = ecircle, 5.484 - rightarg = ecircle, 5.485 - procedure = ecircle_btree_ne, 5.486 - commutator = <>, 5.487 - negator = =, 5.488 - restrict = neqsel, 5.489 - join = neqjoinsel 5.490 -); 5.491 - 5.492 -CREATE OPERATOR >>>= ( 5.493 - leftarg = ecircle, 5.494 - rightarg = ecircle, 5.495 - procedure = ecircle_btree_ge, 5.496 - commutator = <<<=, 5.497 - negator = <<<, 5.498 - restrict = scalargtsel, 5.499 - join = scalargtjoinsel 5.500 -); 5.501 - 5.502 -CREATE OPERATOR >>> ( 5.503 - leftarg = ecircle, 5.504 - rightarg = ecircle, 5.505 - procedure = ecircle_btree_gt, 5.506 - commutator = <<<, 5.507 - negator = <<<=, 5.508 - restrict = scalargtsel, 5.509 - join = scalargtjoinsel 5.510 -); 5.511 - 5.512 -CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle) 5.513 - RETURNS int4 5.514 - LANGUAGE C IMMUTABLE STRICT 5.515 - AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_cmp'; 5.516 - 5.517 -CREATE OPERATOR CLASS ecircle_btree_ops 5.518 - DEFAULT FOR TYPE ecircle USING btree AS 5.519 - OPERATOR 1 <<< , 5.520 - OPERATOR 2 <<<= , 5.521 - OPERATOR 3 = , 5.522 - OPERATOR 4 >>>= , 5.523 - OPERATOR 5 >>> , 5.524 - FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle); 5.525 - 5.526 --- end of B-tree support for ecircle 5.527 - 5.528 - 5.529 ----------------- 5.530 --- type casts -- 5.531 ----------------- 5.532 - 5.533 -CREATE FUNCTION cast_epoint_to_ebox(epoint) 5.534 - RETURNS ebox 5.535 - LANGUAGE C IMMUTABLE STRICT 5.536 - AS '$libdir/latlon-v0009', 'pgl_epoint_to_ebox'; 5.537 - 5.538 -CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint); 5.539 - 5.540 -CREATE FUNCTION cast_epoint_to_ecircle(epoint) 5.541 - RETURNS ecircle 5.542 - LANGUAGE C IMMUTABLE STRICT 5.543 - AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecircle'; 5.544 - 5.545 -CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint); 5.546 - 5.547 -CREATE FUNCTION cast_epoint_to_ecluster(epoint) 5.548 - RETURNS ecluster 5.549 - LANGUAGE C IMMUTABLE STRICT 5.550 - AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecluster'; 5.551 - 5.552 -CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint); 5.553 - 5.554 -CREATE FUNCTION cast_ebox_to_ecluster(ebox) 5.555 - RETURNS ecluster 5.556 - LANGUAGE C IMMUTABLE STRICT 5.557 - AS '$libdir/latlon-v0009', 'pgl_ebox_to_ecluster'; 5.558 - 5.559 -CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox); 5.560 - 5.561 - 5.562 ---------------------------- 5.563 --- constructor functions -- 5.564 ---------------------------- 5.565 - 5.566 -CREATE FUNCTION epoint(float8, float8) 5.567 - RETURNS epoint 5.568 - LANGUAGE C IMMUTABLE STRICT 5.569 - AS '$libdir/latlon-v0009', 'pgl_create_epoint'; 5.570 - 5.571 -CREATE FUNCTION epoint_latlon(float8, float8) 5.572 - RETURNS epoint 5.573 - LANGUAGE SQL IMMUTABLE STRICT AS $$ 5.574 - SELECT epoint($1, $2) 5.575 - $$; 5.576 - 5.577 -CREATE FUNCTION epoint_lonlat(float8, float8) 5.578 - RETURNS epoint 5.579 - LANGUAGE SQL IMMUTABLE STRICT AS $$ 5.580 - SELECT epoint($2, $1) 5.581 - $$; 5.582 - 5.583 -CREATE FUNCTION epoint_with_sample_count(epoint, int4) 5.584 - RETURNS epoint_with_sample_count 5.585 - LANGUAGE C IMMUTABLE STRICT 5.586 - AS '$libdir/latlon-v0009', 'pgl_create_epoint_with_sample_count'; 5.587 - 5.588 -CREATE FUNCTION empty_ebox() 5.589 - RETURNS ebox 5.590 - LANGUAGE C IMMUTABLE STRICT 5.591 - AS '$libdir/latlon-v0009', 'pgl_create_empty_ebox'; 5.592 - 5.593 -CREATE FUNCTION ebox(float8, float8, float8, float8) 5.594 - RETURNS ebox 5.595 - LANGUAGE C IMMUTABLE STRICT 5.596 - AS '$libdir/latlon-v0009', 'pgl_create_ebox'; 5.597 - 5.598 -CREATE FUNCTION ebox(epoint, epoint) 5.599 - RETURNS ebox 5.600 - LANGUAGE C IMMUTABLE STRICT 5.601 - AS '$libdir/latlon-v0009', 'pgl_create_ebox_from_epoints'; 5.602 - 5.603 -CREATE FUNCTION ecircle(float8, float8, float8) 5.604 - RETURNS ecircle 5.605 - LANGUAGE C IMMUTABLE STRICT 5.606 - AS '$libdir/latlon-v0009', 'pgl_create_ecircle'; 5.607 - 5.608 -CREATE FUNCTION ecircle(epoint, float8) 5.609 - RETURNS ecircle 5.610 - LANGUAGE C IMMUTABLE STRICT 5.611 - AS '$libdir/latlon-v0009', 'pgl_create_ecircle_from_epoint'; 5.612 - 5.613 -CREATE FUNCTION ecluster_concat(ecluster[]) 5.614 - RETURNS ecluster 5.615 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.616 - SELECT array_to_string($1, ' ')::ecluster 5.617 - $$; 5.618 - 5.619 -CREATE FUNCTION ecluster_concat(ecluster, ecluster) 5.620 - RETURNS ecluster 5.621 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.622 - SELECT ($1::text || ' ' || $2::text)::ecluster 5.623 - $$; 5.624 - 5.625 -CREATE FUNCTION ecluster_create_multipoint(epoint[]) 5.626 - RETURNS ecluster 5.627 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.628 - SELECT 5.629 - array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster 5.630 - FROM unnest($1) 5.631 - $$; 5.632 - 5.633 -CREATE FUNCTION ecluster_create_path(epoint[]) 5.634 - RETURNS ecluster 5.635 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.636 - SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 5.637 - ('path (' || array_to_string($1, ' ') || ')')::ecluster 5.638 - END 5.639 - FROM array_to_string($1, ' ') AS "str" 5.640 - $$; 5.641 - 5.642 -CREATE FUNCTION ecluster_create_outline(epoint[]) 5.643 - RETURNS ecluster 5.644 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.645 - SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 5.646 - ('outline (' || array_to_string($1, ' ') || ')')::ecluster 5.647 - END 5.648 - FROM array_to_string($1, ' ') AS "str" 5.649 - $$; 5.650 - 5.651 -CREATE FUNCTION ecluster_create_polygon(epoint[]) 5.652 - RETURNS ecluster 5.653 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.654 - SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 5.655 - ('polygon (' || array_to_string($1, ' ') || ')')::ecluster 5.656 - END 5.657 - FROM array_to_string($1, ' ') AS "str" 5.658 - $$; 5.659 - 5.660 - 5.661 ----------------------- 5.662 --- getter functions -- 5.663 ----------------------- 5.664 - 5.665 -CREATE FUNCTION latitude(epoint) 5.666 - RETURNS float8 5.667 - LANGUAGE C IMMUTABLE STRICT 5.668 - AS '$libdir/latlon-v0009', 'pgl_epoint_lat'; 5.669 - 5.670 -CREATE FUNCTION longitude(epoint) 5.671 - RETURNS float8 5.672 - LANGUAGE C IMMUTABLE STRICT 5.673 - AS '$libdir/latlon-v0009', 'pgl_epoint_lon'; 5.674 - 5.675 -CREATE FUNCTION min_latitude(ebox) 5.676 - RETURNS float8 5.677 - LANGUAGE C IMMUTABLE STRICT 5.678 - AS '$libdir/latlon-v0009', 'pgl_ebox_lat_min'; 5.679 - 5.680 -CREATE FUNCTION max_latitude(ebox) 5.681 - RETURNS float8 5.682 - LANGUAGE C IMMUTABLE STRICT 5.683 - AS '$libdir/latlon-v0009', 'pgl_ebox_lat_max'; 5.684 - 5.685 -CREATE FUNCTION min_longitude(ebox) 5.686 - RETURNS float8 5.687 - LANGUAGE C IMMUTABLE STRICT 5.688 - AS '$libdir/latlon-v0009', 'pgl_ebox_lon_min'; 5.689 - 5.690 -CREATE FUNCTION max_longitude(ebox) 5.691 - RETURNS float8 5.692 - LANGUAGE C IMMUTABLE STRICT 5.693 - AS '$libdir/latlon-v0009', 'pgl_ebox_lon_max'; 5.694 - 5.695 -CREATE FUNCTION center(ecircle) 5.696 - RETURNS epoint 5.697 - LANGUAGE C IMMUTABLE STRICT 5.698 - AS '$libdir/latlon-v0009', 'pgl_ecircle_center'; 5.699 - 5.700 -CREATE FUNCTION radius(ecircle) 5.701 - RETURNS float8 5.702 - LANGUAGE C IMMUTABLE STRICT 5.703 - AS '$libdir/latlon-v0009', 'pgl_ecircle_radius'; 5.704 - 5.705 -CREATE FUNCTION ecluster_extract_points(ecluster) 5.706 - RETURNS SETOF epoint 5.707 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.708 - SELECT "match"[2]::epoint 5.709 - FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match" 5.710 - $$; 5.711 - 5.712 -CREATE FUNCTION ecluster_extract_paths(ecluster) 5.713 - RETURNS SETOF epoint[] 5.714 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.715 - SELECT ( 5.716 - SELECT array_agg("m2"[1]::epoint) 5.717 - FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 5.718 - ) 5.719 - FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1" 5.720 - $$; 5.721 - 5.722 -CREATE FUNCTION ecluster_extract_outlines(ecluster) 5.723 - RETURNS SETOF epoint[] 5.724 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.725 - SELECT ( 5.726 - SELECT array_agg("m2"[1]::epoint) 5.727 - FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 5.728 - ) 5.729 - FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1" 5.730 - $$; 5.731 - 5.732 -CREATE FUNCTION ecluster_extract_polygons(ecluster) 5.733 - RETURNS SETOF epoint[] 5.734 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.735 - SELECT ( 5.736 - SELECT array_agg("m2"[1]::epoint) 5.737 - FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 5.738 - ) 5.739 - FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1" 5.740 - $$; 5.741 - 5.742 - 5.743 ---------------- 5.744 --- operators -- 5.745 ---------------- 5.746 - 5.747 -CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox) 5.748 - RETURNS boolean 5.749 - LANGUAGE C IMMUTABLE STRICT 5.750 - AS '$libdir/latlon-v0009', 'pgl_epoint_ebox_overlap'; 5.751 - 5.752 -CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle) 5.753 - RETURNS boolean 5.754 - LANGUAGE C IMMUTABLE STRICT 5.755 - AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_overlap'; 5.756 - 5.757 -CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster) 5.758 - RETURNS boolean 5.759 - LANGUAGE C IMMUTABLE STRICT 5.760 - AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_overlap'; 5.761 - 5.762 -CREATE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster) 5.763 - RETURNS boolean 5.764 - LANGUAGE C IMMUTABLE STRICT 5.765 - AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_may_overlap'; 5.766 - 5.767 -CREATE FUNCTION ebox_overlap_proc(ebox, ebox) 5.768 - RETURNS boolean 5.769 - LANGUAGE C IMMUTABLE STRICT 5.770 - AS '$libdir/latlon-v0009', 'pgl_ebox_overlap'; 5.771 - 5.772 -CREATE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle) 5.773 - RETURNS boolean 5.774 - LANGUAGE C IMMUTABLE STRICT 5.775 - AS '$libdir/latlon-v0009', 'pgl_ebox_ecircle_may_overlap'; 5.776 - 5.777 -CREATE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster) 5.778 - RETURNS boolean 5.779 - LANGUAGE C IMMUTABLE STRICT 5.780 - AS '$libdir/latlon-v0009', 'pgl_ebox_ecluster_may_overlap'; 5.781 - 5.782 -CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle) 5.783 - RETURNS boolean 5.784 - LANGUAGE C IMMUTABLE STRICT 5.785 - AS '$libdir/latlon-v0009', 'pgl_ecircle_overlap'; 5.786 - 5.787 -CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster) 5.788 - RETURNS boolean 5.789 - LANGUAGE C IMMUTABLE STRICT 5.790 - AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_overlap'; 5.791 - 5.792 -CREATE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster) 5.793 - RETURNS boolean 5.794 - LANGUAGE C IMMUTABLE STRICT 5.795 - AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_may_overlap'; 5.796 - 5.797 -CREATE FUNCTION ecluster_overlap_proc(ecluster, ecluster) 5.798 - RETURNS boolean 5.799 - LANGUAGE C IMMUTABLE STRICT 5.800 - AS '$libdir/latlon-v0009', 'pgl_ecluster_overlap'; 5.801 - 5.802 -CREATE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster) 5.803 - RETURNS boolean 5.804 - LANGUAGE C IMMUTABLE STRICT 5.805 - AS '$libdir/latlon-v0009', 'pgl_ecluster_may_overlap'; 5.806 - 5.807 -CREATE FUNCTION ecluster_contains_proc(ecluster, ecluster) 5.808 - RETURNS boolean 5.809 - LANGUAGE C IMMUTABLE STRICT 5.810 - AS '$libdir/latlon-v0009', 'pgl_ecluster_contains'; 5.811 - 5.812 -CREATE FUNCTION epoint_distance_proc(epoint, epoint) 5.813 - RETURNS float8 5.814 - LANGUAGE C IMMUTABLE STRICT 5.815 - AS '$libdir/latlon-v0009', 'pgl_epoint_distance'; 5.816 - 5.817 -CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle) 5.818 - RETURNS float8 5.819 - LANGUAGE C IMMUTABLE STRICT 5.820 - AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_distance'; 5.821 - 5.822 -CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster) 5.823 - RETURNS float8 5.824 - LANGUAGE C IMMUTABLE STRICT 5.825 - AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_distance'; 5.826 - 5.827 -CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle) 5.828 - RETURNS float8 5.829 - LANGUAGE C IMMUTABLE STRICT 5.830 - AS '$libdir/latlon-v0009', 'pgl_ecircle_distance'; 5.831 - 5.832 -CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster) 5.833 - RETURNS float8 5.834 - LANGUAGE C IMMUTABLE STRICT 5.835 - AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_distance'; 5.836 - 5.837 -CREATE FUNCTION ecluster_distance_proc(ecluster, ecluster) 5.838 - RETURNS float8 5.839 - LANGUAGE C IMMUTABLE STRICT 5.840 - AS '$libdir/latlon-v0009', 'pgl_ecluster_distance'; 5.841 - 5.842 -CREATE FUNCTION fair_distance_operator_proc(ecluster, epoint_with_sample_count) 5.843 - RETURNS float8 5.844 - LANGUAGE C IMMUTABLE STRICT 5.845 - AS '$libdir/latlon-v0009', 'pgl_ecluster_epoint_sc_fair_distance'; 5.846 - 5.847 -CREATE OPERATOR && ( 5.848 - leftarg = epoint, 5.849 - rightarg = ebox, 5.850 - procedure = epoint_ebox_overlap_proc, 5.851 - commutator = &&, 5.852 - restrict = areasel, 5.853 - join = areajoinsel 5.854 -); 5.855 - 5.856 -CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint) 5.857 - RETURNS boolean 5.858 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 5.859 - 5.860 -CREATE OPERATOR && ( 5.861 - leftarg = ebox, 5.862 - rightarg = epoint, 5.863 - procedure = epoint_ebox_overlap_commutator, 5.864 - commutator = &&, 5.865 - restrict = areasel, 5.866 - join = areajoinsel 5.867 -); 5.868 - 5.869 -CREATE OPERATOR && ( 5.870 - leftarg = epoint, 5.871 - rightarg = ecircle, 5.872 - procedure = epoint_ecircle_overlap_proc, 5.873 - commutator = &&, 5.874 - restrict = areasel, 5.875 - join = areajoinsel 5.876 -); 5.877 - 5.878 -CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint) 5.879 - RETURNS boolean 5.880 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 5.881 - 5.882 -CREATE OPERATOR && ( 5.883 - leftarg = ecircle, 5.884 - rightarg = epoint, 5.885 - procedure = epoint_ecircle_overlap_commutator, 5.886 - commutator = &&, 5.887 - restrict = areasel, 5.888 - join = areajoinsel 5.889 -); 5.890 - 5.891 -CREATE OPERATOR && ( 5.892 - leftarg = epoint, 5.893 - rightarg = ecluster, 5.894 - procedure = epoint_ecluster_overlap_proc, 5.895 - commutator = &&, 5.896 - restrict = areasel, 5.897 - join = areajoinsel 5.898 -); 5.899 - 5.900 -CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint) 5.901 - RETURNS boolean 5.902 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 5.903 - 5.904 -CREATE OPERATOR && ( 5.905 - leftarg = ecluster, 5.906 - rightarg = epoint, 5.907 - procedure = epoint_ecluster_overlap_commutator, 5.908 - commutator = &&, 5.909 - restrict = areasel, 5.910 - join = areajoinsel 5.911 -); 5.912 - 5.913 -CREATE OPERATOR && ( 5.914 - leftarg = ebox, 5.915 - rightarg = ebox, 5.916 - procedure = ebox_overlap_proc, 5.917 - commutator = &&, 5.918 - restrict = areasel, 5.919 - join = areajoinsel 5.920 -); 5.921 - 5.922 -CREATE OPERATOR && ( 5.923 - leftarg = ecircle, 5.924 - rightarg = ecircle, 5.925 - procedure = ecircle_overlap_proc, 5.926 - commutator = &&, 5.927 - restrict = areasel, 5.928 - join = areajoinsel 5.929 -); 5.930 - 5.931 -CREATE OPERATOR && ( 5.932 - leftarg = ecircle, 5.933 - rightarg = ecluster, 5.934 - procedure = ecircle_ecluster_overlap_proc, 5.935 - commutator = &&, 5.936 - restrict = areasel, 5.937 - join = areajoinsel 5.938 -); 5.939 - 5.940 -CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle) 5.941 - RETURNS boolean 5.942 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 5.943 - 5.944 -CREATE OPERATOR && ( 5.945 - leftarg = ecluster, 5.946 - rightarg = ecircle, 5.947 - procedure = ecircle_ecluster_overlap_commutator, 5.948 - commutator = &&, 5.949 - restrict = areasel, 5.950 - join = areajoinsel 5.951 -); 5.952 - 5.953 -CREATE OPERATOR && ( 5.954 - leftarg = ecluster, 5.955 - rightarg = ecluster, 5.956 - procedure = ecluster_overlap_proc, 5.957 - commutator = &&, 5.958 - restrict = areasel, 5.959 - join = areajoinsel 5.960 -); 5.961 - 5.962 -CREATE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle) 5.963 - RETURNS boolean 5.964 - LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2'; 5.965 - 5.966 -CREATE OPERATOR && ( 5.967 - leftarg = ebox, 5.968 - rightarg = ecircle, 5.969 - procedure = ebox_ecircle_overlap_castwrap, 5.970 - commutator = &&, 5.971 - restrict = areasel, 5.972 - join = areajoinsel 5.973 -); 5.974 - 5.975 -CREATE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox) 5.976 - RETURNS boolean 5.977 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster'; 5.978 - 5.979 -CREATE OPERATOR && ( 5.980 - leftarg = ecircle, 5.981 - rightarg = ebox, 5.982 - procedure = ebox_ecircle_overlap_castwrap, 5.983 - commutator = &&, 5.984 - restrict = areasel, 5.985 - join = areajoinsel 5.986 -); 5.987 - 5.988 -CREATE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster) 5.989 - RETURNS boolean 5.990 - LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2'; 5.991 - 5.992 -CREATE OPERATOR && ( 5.993 - leftarg = ebox, 5.994 - rightarg = ecluster, 5.995 - procedure = ebox_ecluster_overlap_castwrap, 5.996 - commutator = &&, 5.997 - restrict = areasel, 5.998 - join = areajoinsel 5.999 -); 5.1000 - 5.1001 -CREATE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox) 5.1002 - RETURNS boolean 5.1003 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster'; 5.1004 - 5.1005 -CREATE OPERATOR && ( 5.1006 - leftarg = ecluster, 5.1007 - rightarg = ebox, 5.1008 - procedure = ebox_ecluster_overlap_castwrap, 5.1009 - commutator = &&, 5.1010 - restrict = areasel, 5.1011 - join = areajoinsel 5.1012 -); 5.1013 - 5.1014 -CREATE OPERATOR &&+ ( 5.1015 - leftarg = epoint, 5.1016 - rightarg = ecluster, 5.1017 - procedure = epoint_ecluster_may_overlap_proc, 5.1018 - commutator = &&+, 5.1019 - restrict = areasel, 5.1020 - join = areajoinsel 5.1021 -); 5.1022 - 5.1023 -CREATE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint) 5.1024 - RETURNS boolean 5.1025 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 5.1026 - 5.1027 -CREATE OPERATOR &&+ ( 5.1028 - leftarg = ecluster, 5.1029 - rightarg = epoint, 5.1030 - procedure = epoint_ecluster_may_overlap_commutator, 5.1031 - commutator = &&+, 5.1032 - restrict = areasel, 5.1033 - join = areajoinsel 5.1034 -); 5.1035 - 5.1036 -CREATE OPERATOR &&+ ( 5.1037 - leftarg = ebox, 5.1038 - rightarg = ecircle, 5.1039 - procedure = ebox_ecircle_may_overlap_proc, 5.1040 - commutator = &&+, 5.1041 - restrict = areasel, 5.1042 - join = areajoinsel 5.1043 -); 5.1044 - 5.1045 -CREATE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox) 5.1046 - RETURNS boolean 5.1047 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 5.1048 - 5.1049 -CREATE OPERATOR &&+ ( 5.1050 - leftarg = ecircle, 5.1051 - rightarg = ebox, 5.1052 - procedure = ebox_ecircle_may_overlap_commutator, 5.1053 - commutator = &&+, 5.1054 - restrict = areasel, 5.1055 - join = areajoinsel 5.1056 -); 5.1057 - 5.1058 -CREATE OPERATOR &&+ ( 5.1059 - leftarg = ebox, 5.1060 - rightarg = ecluster, 5.1061 - procedure = ebox_ecluster_may_overlap_proc, 5.1062 - commutator = &&+, 5.1063 - restrict = areasel, 5.1064 - join = areajoinsel 5.1065 -); 5.1066 - 5.1067 -CREATE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox) 5.1068 - RETURNS boolean 5.1069 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 5.1070 - 5.1071 -CREATE OPERATOR &&+ ( 5.1072 - leftarg = ecluster, 5.1073 - rightarg = ebox, 5.1074 - procedure = ebox_ecluster_may_overlap_commutator, 5.1075 - commutator = &&+, 5.1076 - restrict = areasel, 5.1077 - join = areajoinsel 5.1078 -); 5.1079 - 5.1080 -CREATE OPERATOR &&+ ( 5.1081 - leftarg = ecircle, 5.1082 - rightarg = ecluster, 5.1083 - procedure = ecircle_ecluster_may_overlap_proc, 5.1084 - commutator = &&+, 5.1085 - restrict = areasel, 5.1086 - join = areajoinsel 5.1087 -); 5.1088 - 5.1089 -CREATE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle) 5.1090 - RETURNS boolean 5.1091 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 5.1092 - 5.1093 -CREATE OPERATOR &&+ ( 5.1094 - leftarg = ecluster, 5.1095 - rightarg = ecircle, 5.1096 - procedure = ecircle_ecluster_may_overlap_commutator, 5.1097 - commutator = &&+, 5.1098 - restrict = areasel, 5.1099 - join = areajoinsel 5.1100 -); 5.1101 - 5.1102 -CREATE OPERATOR &&+ ( 5.1103 - leftarg = ecluster, 5.1104 - rightarg = ecluster, 5.1105 - procedure = ecluster_may_overlap_proc, 5.1106 - commutator = &&+, 5.1107 - restrict = areasel, 5.1108 - join = areajoinsel 5.1109 -); 5.1110 - 5.1111 -CREATE OPERATOR @> ( 5.1112 - leftarg = ebox, 5.1113 - rightarg = epoint, 5.1114 - procedure = epoint_ebox_overlap_commutator, 5.1115 - commutator = <@, 5.1116 - restrict = areasel, 5.1117 - join = areajoinsel 5.1118 -); 5.1119 - 5.1120 -CREATE OPERATOR <@ ( 5.1121 - leftarg = epoint, 5.1122 - rightarg = ebox, 5.1123 - procedure = epoint_ebox_overlap_proc, 5.1124 - commutator = @>, 5.1125 - restrict = areasel, 5.1126 - join = areajoinsel 5.1127 -); 5.1128 - 5.1129 -CREATE OPERATOR @> ( 5.1130 - leftarg = ecluster, 5.1131 - rightarg = epoint, 5.1132 - procedure = epoint_ecluster_overlap_commutator, 5.1133 - commutator = <@, 5.1134 - restrict = areasel, 5.1135 - join = areajoinsel 5.1136 -); 5.1137 - 5.1138 -CREATE OPERATOR <@ ( 5.1139 - leftarg = epoint, 5.1140 - rightarg = ecluster, 5.1141 - procedure = epoint_ecluster_overlap_proc, 5.1142 - commutator = @>, 5.1143 - restrict = areasel, 5.1144 - join = areajoinsel 5.1145 -); 5.1146 - 5.1147 -CREATE OPERATOR @> ( 5.1148 - leftarg = ecluster, 5.1149 - rightarg = ecluster, 5.1150 - procedure = ecluster_contains_proc, 5.1151 - commutator = <@, 5.1152 - restrict = areasel, 5.1153 - join = areajoinsel 5.1154 -); 5.1155 - 5.1156 -CREATE FUNCTION ecluster_contains_commutator(ecluster, ecluster) 5.1157 - RETURNS boolean 5.1158 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1'; 5.1159 - 5.1160 -CREATE OPERATOR <@ ( 5.1161 - leftarg = ecluster, 5.1162 - rightarg = ecluster, 5.1163 - procedure = ecluster_contains_commutator, 5.1164 - commutator = @>, 5.1165 - restrict = areasel, 5.1166 - join = areajoinsel 5.1167 -); 5.1168 - 5.1169 -CREATE FUNCTION ebox_contains_castwrap(ebox, ebox) 5.1170 - RETURNS boolean 5.1171 - LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2::ecluster'; 5.1172 - 5.1173 -CREATE OPERATOR @> ( 5.1174 - leftarg = ebox, 5.1175 - rightarg = ebox, 5.1176 - procedure = ebox_contains_castwrap, 5.1177 - commutator = <@, 5.1178 - restrict = areasel, 5.1179 - join = areajoinsel 5.1180 -); 5.1181 - 5.1182 -CREATE FUNCTION ebox_contains_swapped_castwrap(ebox, ebox) 5.1183 - RETURNS boolean 5.1184 - LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1::ecluster'; 5.1185 - 5.1186 -CREATE OPERATOR <@ ( 5.1187 - leftarg = ebox, 5.1188 - rightarg = ebox, 5.1189 - procedure = ebox_contains_swapped_castwrap, 5.1190 - commutator = @>, 5.1191 - restrict = areasel, 5.1192 - join = areajoinsel 5.1193 -); 5.1194 - 5.1195 -CREATE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster) 5.1196 - RETURNS boolean 5.1197 - LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2'; 5.1198 - 5.1199 -CREATE OPERATOR @> ( 5.1200 - leftarg = ebox, 5.1201 - rightarg = ecluster, 5.1202 - procedure = ebox_ecluster_contains_castwrap, 5.1203 - commutator = <@, 5.1204 - restrict = areasel, 5.1205 - join = areajoinsel 5.1206 -); 5.1207 - 5.1208 -CREATE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox) 5.1209 - RETURNS boolean 5.1210 - LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1'; 5.1211 - 5.1212 -CREATE OPERATOR <@ ( 5.1213 - leftarg = ecluster, 5.1214 - rightarg = ebox, 5.1215 - procedure = ebox_ecluster_contains_castwrap, 5.1216 - commutator = @>, 5.1217 - restrict = areasel, 5.1218 - join = areajoinsel 5.1219 -); 5.1220 - 5.1221 -CREATE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox) 5.1222 - RETURNS boolean 5.1223 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 @> $2::ecluster'; 5.1224 - 5.1225 -CREATE OPERATOR @> ( 5.1226 - leftarg = ecluster, 5.1227 - rightarg = ebox, 5.1228 - procedure = ecluster_ebox_contains_castwrap, 5.1229 - commutator = <@, 5.1230 - restrict = areasel, 5.1231 - join = areajoinsel 5.1232 -); 5.1233 - 5.1234 -CREATE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster) 5.1235 - RETURNS boolean 5.1236 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1::ecluster'; 5.1237 - 5.1238 -CREATE OPERATOR <@ ( 5.1239 - leftarg = ebox, 5.1240 - rightarg = ecluster, 5.1241 - procedure = ecluster_ebox_contains_castwrap, 5.1242 - commutator = @>, 5.1243 - restrict = areasel, 5.1244 - join = areajoinsel 5.1245 -); 5.1246 - 5.1247 -CREATE OPERATOR <-> ( 5.1248 - leftarg = epoint, 5.1249 - rightarg = epoint, 5.1250 - procedure = epoint_distance_proc, 5.1251 - commutator = <-> 5.1252 -); 5.1253 - 5.1254 -CREATE OPERATOR <-> ( 5.1255 - leftarg = epoint, 5.1256 - rightarg = ecircle, 5.1257 - procedure = epoint_ecircle_distance_proc, 5.1258 - commutator = <-> 5.1259 -); 5.1260 - 5.1261 -CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint) 5.1262 - RETURNS float8 5.1263 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 5.1264 - 5.1265 -CREATE OPERATOR <-> ( 5.1266 - leftarg = ecircle, 5.1267 - rightarg = epoint, 5.1268 - procedure = epoint_ecircle_distance_commutator, 5.1269 - commutator = <-> 5.1270 -); 5.1271 - 5.1272 -CREATE OPERATOR <-> ( 5.1273 - leftarg = epoint, 5.1274 - rightarg = ecluster, 5.1275 - procedure = epoint_ecluster_distance_proc, 5.1276 - commutator = <-> 5.1277 -); 5.1278 - 5.1279 -CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint) 5.1280 - RETURNS float8 5.1281 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 5.1282 - 5.1283 -CREATE OPERATOR <-> ( 5.1284 - leftarg = ecluster, 5.1285 - rightarg = epoint, 5.1286 - procedure = epoint_ecluster_distance_commutator, 5.1287 - commutator = <-> 5.1288 -); 5.1289 - 5.1290 -CREATE OPERATOR <-> ( 5.1291 - leftarg = ecircle, 5.1292 - rightarg = ecircle, 5.1293 - procedure = ecircle_distance_proc, 5.1294 - commutator = <-> 5.1295 -); 5.1296 - 5.1297 -CREATE OPERATOR <-> ( 5.1298 - leftarg = ecircle, 5.1299 - rightarg = ecluster, 5.1300 - procedure = ecircle_ecluster_distance_proc, 5.1301 - commutator = <-> 5.1302 -); 5.1303 - 5.1304 -CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle) 5.1305 - RETURNS float8 5.1306 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 5.1307 - 5.1308 -CREATE OPERATOR <-> ( 5.1309 - leftarg = ecluster, 5.1310 - rightarg = ecircle, 5.1311 - procedure = ecircle_ecluster_distance_commutator, 5.1312 - commutator = <-> 5.1313 -); 5.1314 - 5.1315 -CREATE OPERATOR <-> ( 5.1316 - leftarg = ecluster, 5.1317 - rightarg = ecluster, 5.1318 - procedure = ecluster_distance_proc, 5.1319 - commutator = <-> 5.1320 -); 5.1321 - 5.1322 -CREATE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox) 5.1323 - RETURNS float8 5.1324 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster'; 5.1325 - 5.1326 -CREATE OPERATOR <-> ( 5.1327 - leftarg = epoint, 5.1328 - rightarg = ebox, 5.1329 - procedure = epoint_ebox_distance_castwrap, 5.1330 - commutator = <-> 5.1331 -); 5.1332 - 5.1333 -CREATE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint) 5.1334 - RETURNS float8 5.1335 - LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2'; 5.1336 - 5.1337 -CREATE OPERATOR <-> ( 5.1338 - leftarg = ebox, 5.1339 - rightarg = epoint, 5.1340 - procedure = epoint_ebox_distance_castwrap, 5.1341 - commutator = <-> 5.1342 -); 5.1343 - 5.1344 -CREATE FUNCTION ebox_distance_castwrap(ebox, ebox) 5.1345 - RETURNS float8 5.1346 - LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2::ecluster'; 5.1347 - 5.1348 -CREATE OPERATOR <-> ( 5.1349 - leftarg = ebox, 5.1350 - rightarg = ebox, 5.1351 - procedure = ebox_distance_castwrap, 5.1352 - commutator = <-> 5.1353 -); 5.1354 - 5.1355 -CREATE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle) 5.1356 - RETURNS float8 5.1357 - LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2'; 5.1358 - 5.1359 -CREATE OPERATOR <-> ( 5.1360 - leftarg = ebox, 5.1361 - rightarg = ecircle, 5.1362 - procedure = ebox_ecircle_distance_castwrap, 5.1363 - commutator = <-> 5.1364 -); 5.1365 - 5.1366 -CREATE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox) 5.1367 - RETURNS float8 5.1368 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster'; 5.1369 - 5.1370 -CREATE OPERATOR <-> ( 5.1371 - leftarg = ecircle, 5.1372 - rightarg = ebox, 5.1373 - procedure = ebox_ecircle_distance_castwrap, 5.1374 - commutator = <-> 5.1375 -); 5.1376 - 5.1377 -CREATE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster) 5.1378 - RETURNS float8 5.1379 - LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2'; 5.1380 - 5.1381 -CREATE OPERATOR <-> ( 5.1382 - leftarg = ebox, 5.1383 - rightarg = ecluster, 5.1384 - procedure = ebox_ecluster_distance_castwrap, 5.1385 - commutator = <-> 5.1386 -); 5.1387 - 5.1388 -CREATE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox) 5.1389 - RETURNS float8 5.1390 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster'; 5.1391 - 5.1392 -CREATE OPERATOR <-> ( 5.1393 - leftarg = ecluster, 5.1394 - rightarg = ebox, 5.1395 - procedure = ebox_ecluster_distance_castwrap, 5.1396 - commutator = <-> 5.1397 -); 5.1398 - 5.1399 -CREATE OPERATOR <=> ( 5.1400 - leftarg = ecluster, 5.1401 - rightarg = epoint_with_sample_count, 5.1402 - procedure = fair_distance_operator_proc 5.1403 -); 5.1404 - 5.1405 - 5.1406 ----------------- 5.1407 --- GiST index -- 5.1408 ----------------- 5.1409 - 5.1410 -CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal) 5.1411 - RETURNS boolean 5.1412 - LANGUAGE C STRICT 5.1413 - AS '$libdir/latlon-v0009', 'pgl_gist_consistent'; 5.1414 - 5.1415 -CREATE FUNCTION pgl_gist_union(internal, internal) 5.1416 - RETURNS internal 5.1417 - LANGUAGE C STRICT 5.1418 - AS '$libdir/latlon-v0009', 'pgl_gist_union'; 5.1419 - 5.1420 -CREATE FUNCTION pgl_gist_compress_epoint(internal) 5.1421 - RETURNS internal 5.1422 - LANGUAGE C STRICT 5.1423 - AS '$libdir/latlon-v0009', 'pgl_gist_compress_epoint'; 5.1424 - 5.1425 -CREATE FUNCTION pgl_gist_compress_ecircle(internal) 5.1426 - RETURNS internal 5.1427 - LANGUAGE C STRICT 5.1428 - AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecircle'; 5.1429 - 5.1430 -CREATE FUNCTION pgl_gist_compress_ecluster(internal) 5.1431 - RETURNS internal 5.1432 - LANGUAGE C STRICT 5.1433 - AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecluster'; 5.1434 - 5.1435 -CREATE FUNCTION pgl_gist_decompress(internal) 5.1436 - RETURNS internal 5.1437 - LANGUAGE C STRICT 5.1438 - AS '$libdir/latlon-v0009', 'pgl_gist_decompress'; 5.1439 - 5.1440 -CREATE FUNCTION pgl_gist_penalty(internal, internal, internal) 5.1441 - RETURNS internal 5.1442 - LANGUAGE C STRICT 5.1443 - AS '$libdir/latlon-v0009', 'pgl_gist_penalty'; 5.1444 - 5.1445 -CREATE FUNCTION pgl_gist_picksplit(internal, internal) 5.1446 - RETURNS internal 5.1447 - LANGUAGE C STRICT 5.1448 - AS '$libdir/latlon-v0009', 'pgl_gist_picksplit'; 5.1449 - 5.1450 -CREATE FUNCTION pgl_gist_same(internal, internal, internal) 5.1451 - RETURNS internal 5.1452 - LANGUAGE C STRICT 5.1453 - AS '$libdir/latlon-v0009', 'pgl_gist_same'; 5.1454 - 5.1455 -CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid) 5.1456 - RETURNS internal 5.1457 - LANGUAGE C STRICT 5.1458 - AS '$libdir/latlon-v0009', 'pgl_gist_distance'; 5.1459 - 5.1460 -CREATE OPERATOR CLASS epoint_ops 5.1461 - DEFAULT FOR TYPE epoint USING gist AS 5.1462 - OPERATOR 11 = , 5.1463 - OPERATOR 22 && (epoint, ebox), 5.1464 - OPERATOR 222 <@ (epoint, ebox), 5.1465 - OPERATOR 23 && (epoint, ecircle), 5.1466 - OPERATOR 24 && (epoint, ecluster), 5.1467 - OPERATOR 124 &&+ (epoint, ecluster), 5.1468 - OPERATOR 224 <@ (epoint, ecluster), 5.1469 - OPERATOR 31 <-> (epoint, epoint) FOR ORDER BY float_ops, 5.1470 - OPERATOR 32 <-> (epoint, ebox) FOR ORDER BY float_ops, 5.1471 - OPERATOR 33 <-> (epoint, ecircle) FOR ORDER BY float_ops, 5.1472 - OPERATOR 34 <-> (epoint, ecluster) FOR ORDER BY float_ops, 5.1473 - FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 5.1474 - FUNCTION 2 pgl_gist_union(internal, internal), 5.1475 - FUNCTION 3 pgl_gist_compress_epoint(internal), 5.1476 - FUNCTION 4 pgl_gist_decompress(internal), 5.1477 - FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 5.1478 - FUNCTION 6 pgl_gist_picksplit(internal, internal), 5.1479 - FUNCTION 7 pgl_gist_same(internal, internal, internal), 5.1480 - FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 5.1481 - STORAGE ekey_point; 5.1482 - 5.1483 -CREATE OPERATOR CLASS ecircle_ops 5.1484 - DEFAULT FOR TYPE ecircle USING gist AS 5.1485 - OPERATOR 13 = , 5.1486 - OPERATOR 21 && (ecircle, epoint), 5.1487 - OPERATOR 22 && (ecircle, ebox), 5.1488 - OPERATOR 122 &&+ (ecircle, ebox), 5.1489 - OPERATOR 23 && (ecircle, ecircle), 5.1490 - OPERATOR 24 && (ecircle, ecluster), 5.1491 - OPERATOR 124 &&+ (ecircle, ecluster), 5.1492 - OPERATOR 31 <-> (ecircle, epoint) FOR ORDER BY float_ops, 5.1493 - OPERATOR 32 <-> (ecircle, ebox) FOR ORDER BY float_ops, 5.1494 - OPERATOR 33 <-> (ecircle, ecircle) FOR ORDER BY float_ops, 5.1495 - OPERATOR 34 <-> (ecircle, ecluster) FOR ORDER BY float_ops, 5.1496 - FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 5.1497 - FUNCTION 2 pgl_gist_union(internal, internal), 5.1498 - FUNCTION 3 pgl_gist_compress_ecircle(internal), 5.1499 - FUNCTION 4 pgl_gist_decompress(internal), 5.1500 - FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 5.1501 - FUNCTION 6 pgl_gist_picksplit(internal, internal), 5.1502 - FUNCTION 7 pgl_gist_same(internal, internal, internal), 5.1503 - FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 5.1504 - STORAGE ekey_area; 5.1505 - 5.1506 -CREATE OPERATOR CLASS ecluster_ops 5.1507 - DEFAULT FOR TYPE ecluster USING gist AS 5.1508 - OPERATOR 21 && (ecluster, epoint), 5.1509 - OPERATOR 121 &&+ (ecluster, epoint), 5.1510 - OPERATOR 221 @> (ecluster, epoint), 5.1511 - OPERATOR 22 && (ecluster, ebox), 5.1512 - OPERATOR 122 &&+ (ecluster, ebox), 5.1513 - OPERATOR 222 @> (ecluster, ebox), 5.1514 - OPERATOR 322 <@ (ecluster, ebox), 5.1515 - OPERATOR 23 && (ecluster, ecircle), 5.1516 - OPERATOR 123 &&+ (ecluster, ecircle), 5.1517 - OPERATOR 24 && (ecluster, ecluster), 5.1518 - OPERATOR 124 &&+ (ecluster, ecluster), 5.1519 - OPERATOR 224 @> (ecluster, ecluster), 5.1520 - OPERATOR 324 <@ (ecluster, ecluster), 5.1521 - OPERATOR 31 <-> (ecluster, epoint) FOR ORDER BY float_ops, 5.1522 - OPERATOR 32 <-> (ecluster, ebox) FOR ORDER BY float_ops, 5.1523 - OPERATOR 33 <-> (ecluster, ecircle) FOR ORDER BY float_ops, 5.1524 - OPERATOR 34 <-> (ecluster, ecluster) FOR ORDER BY float_ops, 5.1525 - OPERATOR 131 <=> (ecluster, epoint_with_sample_count) FOR ORDER BY float_ops, 5.1526 - FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 5.1527 - FUNCTION 2 pgl_gist_union(internal, internal), 5.1528 - FUNCTION 3 pgl_gist_compress_ecluster(internal), 5.1529 - FUNCTION 4 pgl_gist_decompress(internal), 5.1530 - FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 5.1531 - FUNCTION 6 pgl_gist_picksplit(internal, internal), 5.1532 - FUNCTION 7 pgl_gist_same(internal, internal, internal), 5.1533 - FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 5.1534 - STORAGE ekey_area; 5.1535 - 5.1536 - 5.1537 ---------------------- 5.1538 --- alias functions -- 5.1539 ---------------------- 5.1540 - 5.1541 -CREATE FUNCTION distance(epoint, epoint) 5.1542 - RETURNS float8 5.1543 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2'; 5.1544 - 5.1545 -CREATE FUNCTION distance(ecluster, epoint) 5.1546 - RETURNS float8 5.1547 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2'; 5.1548 - 5.1549 -CREATE FUNCTION distance_within(epoint, epoint, float8) 5.1550 - RETURNS boolean 5.1551 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)'; 5.1552 - 5.1553 -CREATE FUNCTION distance_within(ecluster, epoint, float8) 5.1554 - RETURNS boolean 5.1555 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)'; 5.1556 - 5.1557 -CREATE FUNCTION fair_distance(ecluster, epoint, int4 = 10000) 5.1558 - RETURNS float8 5.1559 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 <=> epoint_with_sample_count($2, $3)'; 5.1560 - 5.1561 - 5.1562 --------------------------------- 5.1563 --- other data storage formats -- 5.1564 --------------------------------- 5.1565 - 5.1566 -CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint') 5.1567 - RETURNS epoint 5.1568 - LANGUAGE plpgsql IMMUTABLE STRICT AS $$ 5.1569 - DECLARE 5.1570 - "result" epoint; 5.1571 - BEGIN 5.1572 - IF $3 = 'epoint_lonlat' THEN 5.1573 - -- avoid dynamic command execution for better performance 5.1574 - RETURN epoint($2, $1); 5.1575 - END IF; 5.1576 - IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN 5.1577 - -- avoid dynamic command execution for better performance 5.1578 - RETURN epoint($1, $2); 5.1579 - END IF; 5.1580 - EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2; 5.1581 - RETURN "result"; 5.1582 - END; 5.1583 - $$; 5.1584 - 5.1585 -CREATE FUNCTION GeoJSON_LinearRing_vertices(jsonb, text = 'epoint_lonlat') 5.1586 - RETURNS SETOF jsonb 5.1587 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.1588 - SELECT "result" FROM 5.1589 - ( SELECT jsonb_array_length($1) - 1 ) AS "lastindex_row" ("lastindex") 5.1590 - CROSS JOIN LATERAL jsonb_array_elements( 5.1591 - CASE WHEN 5.1592 - coords_to_epoint( 5.1593 - ($1->0->>0)::float8, 5.1594 - ($1->0->>1)::float8, 5.1595 - $2 5.1596 - ) = coords_to_epoint( 5.1597 - ($1->"lastindex"->>0)::float8, 5.1598 - ($1->"lastindex"->>1)::float8, 5.1599 - $2 5.1600 - ) 5.1601 - THEN 5.1602 - $1 - "lastindex" 5.1603 - ELSE 5.1604 - $1 5.1605 - END 5.1606 - ) AS "result_row" ("result") 5.1607 - $$; 5.1608 - 5.1609 -CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat') 5.1610 - RETURNS epoint 5.1611 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.1612 - SELECT CASE 5.1613 - WHEN $1->>'type' = 'Point' THEN 5.1614 - coords_to_epoint( 5.1615 - ($1->'coordinates'->>0)::float8, 5.1616 - ($1->'coordinates'->>1)::float8, 5.1617 - $2 5.1618 - ) 5.1619 - WHEN $1->>'type' = 'Feature' THEN 5.1620 - GeoJSON_to_epoint($1->'geometry', $2) 5.1621 - ELSE 5.1622 - NULL 5.1623 - END 5.1624 - $$; 5.1625 - 5.1626 -CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat') 5.1627 - RETURNS ecluster 5.1628 - LANGUAGE sql IMMUTABLE STRICT AS $$ 5.1629 - SELECT CASE $1->>'type' 5.1630 - WHEN 'Point' THEN 5.1631 - coords_to_epoint( 5.1632 - ($1->'coordinates'->>0)::float8, 5.1633 - ($1->'coordinates'->>1)::float8, 5.1634 - $2 5.1635 - )::ecluster 5.1636 - WHEN 'MultiPoint' THEN 5.1637 - ( SELECT ecluster_create_multipoint(array_agg( 5.1638 - coords_to_epoint( 5.1639 - ("coord"->>0)::float8, 5.1640 - ("coord"->>1)::float8, 5.1641 - $2 5.1642 - ) 5.1643 - )) 5.1644 - FROM jsonb_array_elements($1->'coordinates') AS "coord" 5.1645 - ) 5.1646 - WHEN 'LineString' THEN 5.1647 - ( SELECT ecluster_create_path(array_agg( 5.1648 - coords_to_epoint( 5.1649 - ("coord"->>0)::float8, 5.1650 - ("coord"->>1)::float8, 5.1651 - $2 5.1652 - ) 5.1653 - )) 5.1654 - FROM jsonb_array_elements($1->'coordinates') AS "coord" 5.1655 - ) 5.1656 - WHEN 'MultiLineString' THEN 5.1657 - ( SELECT ecluster_concat(array_agg( 5.1658 - ( SELECT ecluster_create_path(array_agg( 5.1659 - coords_to_epoint( 5.1660 - ("coord"->>0)::float8, 5.1661 - ("coord"->>1)::float8, 5.1662 - $2 5.1663 - ) 5.1664 - )) 5.1665 - FROM jsonb_array_elements("coord_array") AS "coord" 5.1666 - ) 5.1667 - )) 5.1668 - FROM jsonb_array_elements($1->'coordinates') AS "coord_array" 5.1669 - ) 5.1670 - WHEN 'Polygon' THEN 5.1671 - ( SELECT ecluster_concat(array_agg( 5.1672 - ( SELECT ecluster_create_polygon(array_agg( 5.1673 - coords_to_epoint( 5.1674 - ("coord"->>0)::float8, 5.1675 - ("coord"->>1)::float8, 5.1676 - $2 5.1677 - ) 5.1678 - )) 5.1679 - FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord" 5.1680 - ) 5.1681 - )) 5.1682 - FROM jsonb_array_elements($1->'coordinates') AS "coord_array" 5.1683 - ) 5.1684 - WHEN 'MultiPolygon' THEN 5.1685 - ( SELECT ecluster_concat(array_agg( 5.1686 - ( SELECT ecluster_concat(array_agg( 5.1687 - ( SELECT ecluster_create_polygon(array_agg( 5.1688 - coords_to_epoint( 5.1689 - ("coord"->>0)::float8, 5.1690 - ("coord"->>1)::float8, 5.1691 - $2 5.1692 - ) 5.1693 - )) 5.1694 - FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord" 5.1695 - ) 5.1696 - )) 5.1697 - FROM jsonb_array_elements("coord_array_array") AS "coord_array" 5.1698 - ) 5.1699 - )) 5.1700 - FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array" 5.1701 - ) 5.1702 - WHEN 'GeometryCollection' THEN 5.1703 - ( SELECT ecluster_concat(array_agg( 5.1704 - GeoJSON_to_ecluster("geometry", $2) 5.1705 - )) 5.1706 - FROM jsonb_array_elements($1->'geometries') AS "geometry" 5.1707 - ) 5.1708 - WHEN 'Feature' THEN 5.1709 - GeoJSON_to_ecluster($1->'geometry', $2) 5.1710 - WHEN 'FeatureCollection' THEN 5.1711 - ( SELECT ecluster_concat(array_agg( 5.1712 - GeoJSON_to_ecluster("feature", $2) 5.1713 - )) 5.1714 - FROM jsonb_array_elements($1->'features') AS "feature" 5.1715 - ) 5.1716 - ELSE 5.1717 - NULL 5.1718 - END 5.1719 - $$; 5.1720 -
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/latlon--0.15.sql Wed Feb 12 11:08:37 2020 +0100 6.3 @@ -0,0 +1,1717 @@ 6.4 + 6.5 +---------------------------------------- 6.6 +-- forward declarations (shell types) -- 6.7 +---------------------------------------- 6.8 + 6.9 +CREATE TYPE ekey_point; 6.10 +CREATE TYPE ekey_area; 6.11 +CREATE TYPE epoint; 6.12 +CREATE TYPE epoint_with_sample_count; 6.13 +CREATE TYPE ebox; 6.14 +CREATE TYPE ecircle; 6.15 +CREATE TYPE ecluster; 6.16 + 6.17 + 6.18 +------------------------------------------------------------ 6.19 +-- dummy input/output functions for dummy index key types -- 6.20 +------------------------------------------------------------ 6.21 + 6.22 +CREATE FUNCTION ekey_point_in_dummy(cstring) 6.23 + RETURNS ekey_point 6.24 + LANGUAGE C IMMUTABLE STRICT 6.25 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 6.26 + 6.27 +CREATE FUNCTION ekey_point_out_dummy(ekey_point) 6.28 + RETURNS cstring 6.29 + LANGUAGE C IMMUTABLE STRICT 6.30 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 6.31 + 6.32 +CREATE FUNCTION ekey_area_in_dummy(cstring) 6.33 + RETURNS ekey_area 6.34 + LANGUAGE C IMMUTABLE STRICT 6.35 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 6.36 + 6.37 +CREATE FUNCTION ekey_area_out_dummy(ekey_area) 6.38 + RETURNS cstring 6.39 + LANGUAGE C IMMUTABLE STRICT 6.40 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 6.41 + 6.42 + 6.43 +-------------------------- 6.44 +-- text input functions -- 6.45 +-------------------------- 6.46 + 6.47 +CREATE FUNCTION epoint_in(cstring) 6.48 + RETURNS epoint 6.49 + LANGUAGE C IMMUTABLE STRICT 6.50 + AS '$libdir/latlon-v0009', 'pgl_epoint_in'; 6.51 + 6.52 +CREATE FUNCTION epoint_with_sample_count_in(cstring) 6.53 + RETURNS epoint_with_sample_count 6.54 + LANGUAGE C IMMUTABLE STRICT 6.55 + AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_in'; 6.56 + 6.57 +CREATE FUNCTION ebox_in(cstring) 6.58 + RETURNS ebox 6.59 + LANGUAGE C IMMUTABLE STRICT 6.60 + AS '$libdir/latlon-v0009', 'pgl_ebox_in'; 6.61 + 6.62 +CREATE FUNCTION ecircle_in(cstring) 6.63 + RETURNS ecircle 6.64 + LANGUAGE C IMMUTABLE STRICT 6.65 + AS '$libdir/latlon-v0009', 'pgl_ecircle_in'; 6.66 + 6.67 +CREATE FUNCTION ecluster_in(cstring) 6.68 + RETURNS ecluster 6.69 + LANGUAGE C IMMUTABLE STRICT 6.70 + AS '$libdir/latlon-v0009', 'pgl_ecluster_in'; 6.71 + 6.72 + 6.73 +--------------------------- 6.74 +-- text output functions -- 6.75 +--------------------------- 6.76 + 6.77 +CREATE FUNCTION epoint_out(epoint) 6.78 + RETURNS cstring 6.79 + LANGUAGE C IMMUTABLE STRICT 6.80 + AS '$libdir/latlon-v0009', 'pgl_epoint_out'; 6.81 + 6.82 +CREATE FUNCTION epoint_with_sample_count_out(epoint_with_sample_count) 6.83 + RETURNS cstring 6.84 + LANGUAGE C IMMUTABLE STRICT 6.85 + AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_out'; 6.86 + 6.87 +CREATE FUNCTION ebox_out(ebox) 6.88 + RETURNS cstring 6.89 + LANGUAGE C IMMUTABLE STRICT 6.90 + AS '$libdir/latlon-v0009', 'pgl_ebox_out'; 6.91 + 6.92 +CREATE FUNCTION ecircle_out(ecircle) 6.93 + RETURNS cstring 6.94 + LANGUAGE C IMMUTABLE STRICT 6.95 + AS '$libdir/latlon-v0009', 'pgl_ecircle_out'; 6.96 + 6.97 +CREATE FUNCTION ecluster_out(ecluster) 6.98 + RETURNS cstring 6.99 + LANGUAGE C IMMUTABLE STRICT 6.100 + AS '$libdir/latlon-v0009', 'pgl_ecluster_out'; 6.101 + 6.102 + 6.103 +-------------------------- 6.104 +-- binary I/O functions -- 6.105 +-------------------------- 6.106 + 6.107 +CREATE FUNCTION epoint_recv(internal) 6.108 + RETURNS epoint 6.109 + LANGUAGE C IMMUTABLE STRICT 6.110 + AS '$libdir/latlon-v0009', 'pgl_epoint_recv'; 6.111 + 6.112 +CREATE FUNCTION ebox_recv(internal) 6.113 + RETURNS ebox 6.114 + LANGUAGE C IMMUTABLE STRICT 6.115 + AS '$libdir/latlon-v0009', 'pgl_ebox_recv'; 6.116 + 6.117 +CREATE FUNCTION ecircle_recv(internal) 6.118 + RETURNS ecircle 6.119 + LANGUAGE C IMMUTABLE STRICT 6.120 + AS '$libdir/latlon-v0009', 'pgl_ecircle_recv'; 6.121 + 6.122 +CREATE FUNCTION epoint_send(epoint) 6.123 + RETURNS bytea 6.124 + LANGUAGE C IMMUTABLE STRICT 6.125 + AS '$libdir/latlon-v0009', 'pgl_epoint_send'; 6.126 + 6.127 +CREATE FUNCTION ebox_send(ebox) 6.128 + RETURNS bytea 6.129 + LANGUAGE C IMMUTABLE STRICT 6.130 + AS '$libdir/latlon-v0009', 'pgl_ebox_send'; 6.131 + 6.132 +CREATE FUNCTION ecircle_send(ecircle) 6.133 + RETURNS bytea 6.134 + LANGUAGE C IMMUTABLE STRICT 6.135 + AS '$libdir/latlon-v0009', 'pgl_ecircle_send'; 6.136 + 6.137 + 6.138 +----------------------------------------------- 6.139 +-- type definitions of dummy index key types -- 6.140 +----------------------------------------------- 6.141 + 6.142 +CREATE TYPE ekey_point ( 6.143 + internallength = 8, 6.144 + input = ekey_point_in_dummy, 6.145 + output = ekey_point_out_dummy, 6.146 + alignment = char ); 6.147 + 6.148 +CREATE TYPE ekey_area ( 6.149 + internallength = 9, 6.150 + input = ekey_area_in_dummy, 6.151 + output = ekey_area_out_dummy, 6.152 + alignment = char ); 6.153 + 6.154 + 6.155 +------------------------------------------ 6.156 +-- definitions of geographic data types -- 6.157 +------------------------------------------ 6.158 + 6.159 +CREATE TYPE epoint ( 6.160 + internallength = 16, 6.161 + input = epoint_in, 6.162 + output = epoint_out, 6.163 + receive = epoint_recv, 6.164 + send = epoint_send, 6.165 + alignment = double ); 6.166 + 6.167 +CREATE TYPE epoint_with_sample_count ( 6.168 + internallength = 20, 6.169 + input = epoint_with_sample_count_in, 6.170 + output = epoint_with_sample_count_out, 6.171 + alignment = double ); 6.172 + 6.173 +CREATE TYPE ebox ( 6.174 + internallength = 32, 6.175 + input = ebox_in, 6.176 + output = ebox_out, 6.177 + receive = ebox_recv, 6.178 + send = ebox_send, 6.179 + alignment = double ); 6.180 + 6.181 +CREATE TYPE ecircle ( 6.182 + internallength = 24, 6.183 + input = ecircle_in, 6.184 + output = ecircle_out, 6.185 + receive = ecircle_recv, 6.186 + send = ecircle_send, 6.187 + alignment = double ); 6.188 + 6.189 +CREATE TYPE ecluster ( 6.190 + internallength = VARIABLE, 6.191 + input = ecluster_in, 6.192 + output = ecluster_out, 6.193 + alignment = double, 6.194 + storage = external ); 6.195 + 6.196 + 6.197 +-------------------- 6.198 +-- B-tree support -- 6.199 +-------------------- 6.200 + 6.201 +-- begin of B-tree support for epoint 6.202 + 6.203 +CREATE FUNCTION epoint_btree_lt(epoint, epoint) 6.204 + RETURNS boolean 6.205 + LANGUAGE C IMMUTABLE STRICT 6.206 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_lt'; 6.207 + 6.208 +CREATE FUNCTION epoint_btree_le(epoint, epoint) 6.209 + RETURNS boolean 6.210 + LANGUAGE C IMMUTABLE STRICT 6.211 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_le'; 6.212 + 6.213 +CREATE FUNCTION epoint_btree_eq(epoint, epoint) 6.214 + RETURNS boolean 6.215 + LANGUAGE C IMMUTABLE STRICT 6.216 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_eq'; 6.217 + 6.218 +CREATE FUNCTION epoint_btree_ne(epoint, epoint) 6.219 + RETURNS boolean 6.220 + LANGUAGE C IMMUTABLE STRICT 6.221 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ne'; 6.222 + 6.223 +CREATE FUNCTION epoint_btree_ge(epoint, epoint) 6.224 + RETURNS boolean 6.225 + LANGUAGE C IMMUTABLE STRICT 6.226 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ge'; 6.227 + 6.228 +CREATE FUNCTION epoint_btree_gt(epoint, epoint) 6.229 + RETURNS boolean 6.230 + LANGUAGE C IMMUTABLE STRICT 6.231 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_gt'; 6.232 + 6.233 +CREATE OPERATOR <<< ( 6.234 + leftarg = epoint, 6.235 + rightarg = epoint, 6.236 + procedure = epoint_btree_lt, 6.237 + commutator = >>>, 6.238 + negator = >>>=, 6.239 + restrict = scalarltsel, 6.240 + join = scalarltjoinsel 6.241 +); 6.242 + 6.243 +CREATE OPERATOR <<<= ( 6.244 + leftarg = epoint, 6.245 + rightarg = epoint, 6.246 + procedure = epoint_btree_le, 6.247 + commutator = >>>=, 6.248 + negator = >>>, 6.249 + restrict = scalarltsel, 6.250 + join = scalarltjoinsel 6.251 +); 6.252 + 6.253 +CREATE OPERATOR = ( 6.254 + leftarg = epoint, 6.255 + rightarg = epoint, 6.256 + procedure = epoint_btree_eq, 6.257 + commutator = =, 6.258 + negator = <>, 6.259 + restrict = eqsel, 6.260 + join = eqjoinsel, 6.261 + merges 6.262 +); 6.263 + 6.264 +CREATE OPERATOR <> ( 6.265 + leftarg = epoint, 6.266 + rightarg = epoint, 6.267 + procedure = epoint_btree_ne, 6.268 + commutator = <>, 6.269 + negator = =, 6.270 + restrict = neqsel, 6.271 + join = neqjoinsel 6.272 +); 6.273 + 6.274 +CREATE OPERATOR >>>= ( 6.275 + leftarg = epoint, 6.276 + rightarg = epoint, 6.277 + procedure = epoint_btree_ge, 6.278 + commutator = <<<=, 6.279 + negator = <<<, 6.280 + restrict = scalargtsel, 6.281 + join = scalargtjoinsel 6.282 +); 6.283 + 6.284 +CREATE OPERATOR >>> ( 6.285 + leftarg = epoint, 6.286 + rightarg = epoint, 6.287 + procedure = epoint_btree_gt, 6.288 + commutator = <<<, 6.289 + negator = <<<=, 6.290 + restrict = scalargtsel, 6.291 + join = scalargtjoinsel 6.292 +); 6.293 + 6.294 +CREATE FUNCTION epoint_btree_cmp(epoint, epoint) 6.295 + RETURNS int4 6.296 + LANGUAGE C IMMUTABLE STRICT 6.297 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_cmp'; 6.298 + 6.299 +CREATE OPERATOR CLASS epoint_btree_ops 6.300 + DEFAULT FOR TYPE epoint USING btree AS 6.301 + OPERATOR 1 <<< , 6.302 + OPERATOR 2 <<<= , 6.303 + OPERATOR 3 = , 6.304 + OPERATOR 4 >>>= , 6.305 + OPERATOR 5 >>> , 6.306 + FUNCTION 1 epoint_btree_cmp(epoint, epoint); 6.307 + 6.308 +-- end of B-tree support for epoint 6.309 + 6.310 +-- begin of B-tree support for ebox 6.311 + 6.312 +CREATE FUNCTION ebox_btree_lt(ebox, ebox) 6.313 + RETURNS boolean 6.314 + LANGUAGE C IMMUTABLE STRICT 6.315 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_lt'; 6.316 + 6.317 +CREATE FUNCTION ebox_btree_le(ebox, ebox) 6.318 + RETURNS boolean 6.319 + LANGUAGE C IMMUTABLE STRICT 6.320 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_le'; 6.321 + 6.322 +CREATE FUNCTION ebox_btree_eq(ebox, ebox) 6.323 + RETURNS boolean 6.324 + LANGUAGE C IMMUTABLE STRICT 6.325 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_eq'; 6.326 + 6.327 +CREATE FUNCTION ebox_btree_ne(ebox, ebox) 6.328 + RETURNS boolean 6.329 + LANGUAGE C IMMUTABLE STRICT 6.330 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ne'; 6.331 + 6.332 +CREATE FUNCTION ebox_btree_ge(ebox, ebox) 6.333 + RETURNS boolean 6.334 + LANGUAGE C IMMUTABLE STRICT 6.335 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ge'; 6.336 + 6.337 +CREATE FUNCTION ebox_btree_gt(ebox, ebox) 6.338 + RETURNS boolean 6.339 + LANGUAGE C IMMUTABLE STRICT 6.340 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_gt'; 6.341 + 6.342 +CREATE OPERATOR <<< ( 6.343 + leftarg = ebox, 6.344 + rightarg = ebox, 6.345 + procedure = ebox_btree_lt, 6.346 + commutator = >>>, 6.347 + negator = >>>=, 6.348 + restrict = scalarltsel, 6.349 + join = scalarltjoinsel 6.350 +); 6.351 + 6.352 +CREATE OPERATOR <<<= ( 6.353 + leftarg = ebox, 6.354 + rightarg = ebox, 6.355 + procedure = ebox_btree_le, 6.356 + commutator = >>>=, 6.357 + negator = >>>, 6.358 + restrict = scalarltsel, 6.359 + join = scalarltjoinsel 6.360 +); 6.361 + 6.362 +CREATE OPERATOR = ( 6.363 + leftarg = ebox, 6.364 + rightarg = ebox, 6.365 + procedure = ebox_btree_eq, 6.366 + commutator = =, 6.367 + negator = <>, 6.368 + restrict = eqsel, 6.369 + join = eqjoinsel, 6.370 + merges 6.371 +); 6.372 + 6.373 +CREATE OPERATOR <> ( 6.374 + leftarg = ebox, 6.375 + rightarg = ebox, 6.376 + procedure = ebox_btree_ne, 6.377 + commutator = <>, 6.378 + negator = =, 6.379 + restrict = neqsel, 6.380 + join = neqjoinsel 6.381 +); 6.382 + 6.383 +CREATE OPERATOR >>>= ( 6.384 + leftarg = ebox, 6.385 + rightarg = ebox, 6.386 + procedure = ebox_btree_ge, 6.387 + commutator = <<<=, 6.388 + negator = <<<, 6.389 + restrict = scalargtsel, 6.390 + join = scalargtjoinsel 6.391 +); 6.392 + 6.393 +CREATE OPERATOR >>> ( 6.394 + leftarg = ebox, 6.395 + rightarg = ebox, 6.396 + procedure = ebox_btree_gt, 6.397 + commutator = <<<, 6.398 + negator = <<<=, 6.399 + restrict = scalargtsel, 6.400 + join = scalargtjoinsel 6.401 +); 6.402 + 6.403 +CREATE FUNCTION ebox_btree_cmp(ebox, ebox) 6.404 + RETURNS int4 6.405 + LANGUAGE C IMMUTABLE STRICT 6.406 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_cmp'; 6.407 + 6.408 +CREATE OPERATOR CLASS ebox_btree_ops 6.409 + DEFAULT FOR TYPE ebox USING btree AS 6.410 + OPERATOR 1 <<< , 6.411 + OPERATOR 2 <<<= , 6.412 + OPERATOR 3 = , 6.413 + OPERATOR 4 >>>= , 6.414 + OPERATOR 5 >>> , 6.415 + FUNCTION 1 ebox_btree_cmp(ebox, ebox); 6.416 + 6.417 +-- end of B-tree support for ebox 6.418 + 6.419 +-- begin of B-tree support for ecircle 6.420 + 6.421 +CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle) 6.422 + RETURNS boolean 6.423 + LANGUAGE C IMMUTABLE STRICT 6.424 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_lt'; 6.425 + 6.426 +CREATE FUNCTION ecircle_btree_le(ecircle, ecircle) 6.427 + RETURNS boolean 6.428 + LANGUAGE C IMMUTABLE STRICT 6.429 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_le'; 6.430 + 6.431 +CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle) 6.432 + RETURNS boolean 6.433 + LANGUAGE C IMMUTABLE STRICT 6.434 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_eq'; 6.435 + 6.436 +CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle) 6.437 + RETURNS boolean 6.438 + LANGUAGE C IMMUTABLE STRICT 6.439 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ne'; 6.440 + 6.441 +CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle) 6.442 + RETURNS boolean 6.443 + LANGUAGE C IMMUTABLE STRICT 6.444 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ge'; 6.445 + 6.446 +CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle) 6.447 + RETURNS boolean 6.448 + LANGUAGE C IMMUTABLE STRICT 6.449 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_gt'; 6.450 + 6.451 +CREATE OPERATOR <<< ( 6.452 + leftarg = ecircle, 6.453 + rightarg = ecircle, 6.454 + procedure = ecircle_btree_lt, 6.455 + commutator = >>>, 6.456 + negator = >>>=, 6.457 + restrict = scalarltsel, 6.458 + join = scalarltjoinsel 6.459 +); 6.460 + 6.461 +CREATE OPERATOR <<<= ( 6.462 + leftarg = ecircle, 6.463 + rightarg = ecircle, 6.464 + procedure = ecircle_btree_le, 6.465 + commutator = >>>=, 6.466 + negator = >>>, 6.467 + restrict = scalarltsel, 6.468 + join = scalarltjoinsel 6.469 +); 6.470 + 6.471 +CREATE OPERATOR = ( 6.472 + leftarg = ecircle, 6.473 + rightarg = ecircle, 6.474 + procedure = ecircle_btree_eq, 6.475 + commutator = =, 6.476 + negator = <>, 6.477 + restrict = eqsel, 6.478 + join = eqjoinsel, 6.479 + merges 6.480 +); 6.481 + 6.482 +CREATE OPERATOR <> ( 6.483 + leftarg = ecircle, 6.484 + rightarg = ecircle, 6.485 + procedure = ecircle_btree_ne, 6.486 + commutator = <>, 6.487 + negator = =, 6.488 + restrict = neqsel, 6.489 + join = neqjoinsel 6.490 +); 6.491 + 6.492 +CREATE OPERATOR >>>= ( 6.493 + leftarg = ecircle, 6.494 + rightarg = ecircle, 6.495 + procedure = ecircle_btree_ge, 6.496 + commutator = <<<=, 6.497 + negator = <<<, 6.498 + restrict = scalargtsel, 6.499 + join = scalargtjoinsel 6.500 +); 6.501 + 6.502 +CREATE OPERATOR >>> ( 6.503 + leftarg = ecircle, 6.504 + rightarg = ecircle, 6.505 + procedure = ecircle_btree_gt, 6.506 + commutator = <<<, 6.507 + negator = <<<=, 6.508 + restrict = scalargtsel, 6.509 + join = scalargtjoinsel 6.510 +); 6.511 + 6.512 +CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle) 6.513 + RETURNS int4 6.514 + LANGUAGE C IMMUTABLE STRICT 6.515 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_cmp'; 6.516 + 6.517 +CREATE OPERATOR CLASS ecircle_btree_ops 6.518 + DEFAULT FOR TYPE ecircle USING btree AS 6.519 + OPERATOR 1 <<< , 6.520 + OPERATOR 2 <<<= , 6.521 + OPERATOR 3 = , 6.522 + OPERATOR 4 >>>= , 6.523 + OPERATOR 5 >>> , 6.524 + FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle); 6.525 + 6.526 +-- end of B-tree support for ecircle 6.527 + 6.528 + 6.529 +---------------- 6.530 +-- type casts -- 6.531 +---------------- 6.532 + 6.533 +CREATE FUNCTION cast_epoint_to_ebox(epoint) 6.534 + RETURNS ebox 6.535 + LANGUAGE C IMMUTABLE STRICT 6.536 + AS '$libdir/latlon-v0009', 'pgl_epoint_to_ebox'; 6.537 + 6.538 +CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint); 6.539 + 6.540 +CREATE FUNCTION cast_epoint_to_ecircle(epoint) 6.541 + RETURNS ecircle 6.542 + LANGUAGE C IMMUTABLE STRICT 6.543 + AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecircle'; 6.544 + 6.545 +CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint); 6.546 + 6.547 +CREATE FUNCTION cast_epoint_to_ecluster(epoint) 6.548 + RETURNS ecluster 6.549 + LANGUAGE C IMMUTABLE STRICT 6.550 + AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecluster'; 6.551 + 6.552 +CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint); 6.553 + 6.554 +CREATE FUNCTION cast_ebox_to_ecluster(ebox) 6.555 + RETURNS ecluster 6.556 + LANGUAGE C IMMUTABLE STRICT 6.557 + AS '$libdir/latlon-v0009', 'pgl_ebox_to_ecluster'; 6.558 + 6.559 +CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox); 6.560 + 6.561 + 6.562 +--------------------------- 6.563 +-- constructor functions -- 6.564 +--------------------------- 6.565 + 6.566 +CREATE FUNCTION epoint(float8, float8) 6.567 + RETURNS epoint 6.568 + LANGUAGE C IMMUTABLE STRICT 6.569 + AS '$libdir/latlon-v0009', 'pgl_create_epoint'; 6.570 + 6.571 +CREATE FUNCTION epoint_latlon(float8, float8) 6.572 + RETURNS epoint 6.573 + LANGUAGE SQL IMMUTABLE STRICT AS $$ 6.574 + SELECT epoint($1, $2) 6.575 + $$; 6.576 + 6.577 +CREATE FUNCTION epoint_lonlat(float8, float8) 6.578 + RETURNS epoint 6.579 + LANGUAGE SQL IMMUTABLE STRICT AS $$ 6.580 + SELECT epoint($2, $1) 6.581 + $$; 6.582 + 6.583 +CREATE FUNCTION epoint_with_sample_count(epoint, int4) 6.584 + RETURNS epoint_with_sample_count 6.585 + LANGUAGE C IMMUTABLE STRICT 6.586 + AS '$libdir/latlon-v0009', 'pgl_create_epoint_with_sample_count'; 6.587 + 6.588 +CREATE FUNCTION empty_ebox() 6.589 + RETURNS ebox 6.590 + LANGUAGE C IMMUTABLE STRICT 6.591 + AS '$libdir/latlon-v0009', 'pgl_create_empty_ebox'; 6.592 + 6.593 +CREATE FUNCTION ebox(float8, float8, float8, float8) 6.594 + RETURNS ebox 6.595 + LANGUAGE C IMMUTABLE STRICT 6.596 + AS '$libdir/latlon-v0009', 'pgl_create_ebox'; 6.597 + 6.598 +CREATE FUNCTION ebox(epoint, epoint) 6.599 + RETURNS ebox 6.600 + LANGUAGE C IMMUTABLE STRICT 6.601 + AS '$libdir/latlon-v0009', 'pgl_create_ebox_from_epoints'; 6.602 + 6.603 +CREATE FUNCTION ecircle(float8, float8, float8) 6.604 + RETURNS ecircle 6.605 + LANGUAGE C IMMUTABLE STRICT 6.606 + AS '$libdir/latlon-v0009', 'pgl_create_ecircle'; 6.607 + 6.608 +CREATE FUNCTION ecircle(epoint, float8) 6.609 + RETURNS ecircle 6.610 + LANGUAGE C IMMUTABLE STRICT 6.611 + AS '$libdir/latlon-v0009', 'pgl_create_ecircle_from_epoint'; 6.612 + 6.613 +CREATE FUNCTION ecluster_concat(ecluster[]) 6.614 + RETURNS ecluster 6.615 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.616 + SELECT array_to_string($1, ' ')::ecluster 6.617 + $$; 6.618 + 6.619 +CREATE FUNCTION ecluster_concat(ecluster, ecluster) 6.620 + RETURNS ecluster 6.621 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.622 + SELECT ($1::text || ' ' || $2::text)::ecluster 6.623 + $$; 6.624 + 6.625 +CREATE FUNCTION ecluster_create_multipoint(epoint[]) 6.626 + RETURNS ecluster 6.627 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.628 + SELECT 6.629 + array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster 6.630 + FROM unnest($1) 6.631 + $$; 6.632 + 6.633 +CREATE FUNCTION ecluster_create_path(epoint[]) 6.634 + RETURNS ecluster 6.635 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.636 + SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 6.637 + ('path (' || array_to_string($1, ' ') || ')')::ecluster 6.638 + END 6.639 + FROM array_to_string($1, ' ') AS "str" 6.640 + $$; 6.641 + 6.642 +CREATE FUNCTION ecluster_create_outline(epoint[]) 6.643 + RETURNS ecluster 6.644 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.645 + SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 6.646 + ('outline (' || array_to_string($1, ' ') || ')')::ecluster 6.647 + END 6.648 + FROM array_to_string($1, ' ') AS "str" 6.649 + $$; 6.650 + 6.651 +CREATE FUNCTION ecluster_create_polygon(epoint[]) 6.652 + RETURNS ecluster 6.653 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.654 + SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 6.655 + ('polygon (' || array_to_string($1, ' ') || ')')::ecluster 6.656 + END 6.657 + FROM array_to_string($1, ' ') AS "str" 6.658 + $$; 6.659 + 6.660 + 6.661 +---------------------- 6.662 +-- getter functions -- 6.663 +---------------------- 6.664 + 6.665 +CREATE FUNCTION latitude(epoint) 6.666 + RETURNS float8 6.667 + LANGUAGE C IMMUTABLE STRICT 6.668 + AS '$libdir/latlon-v0009', 'pgl_epoint_lat'; 6.669 + 6.670 +CREATE FUNCTION longitude(epoint) 6.671 + RETURNS float8 6.672 + LANGUAGE C IMMUTABLE STRICT 6.673 + AS '$libdir/latlon-v0009', 'pgl_epoint_lon'; 6.674 + 6.675 +CREATE FUNCTION min_latitude(ebox) 6.676 + RETURNS float8 6.677 + LANGUAGE C IMMUTABLE STRICT 6.678 + AS '$libdir/latlon-v0009', 'pgl_ebox_lat_min'; 6.679 + 6.680 +CREATE FUNCTION max_latitude(ebox) 6.681 + RETURNS float8 6.682 + LANGUAGE C IMMUTABLE STRICT 6.683 + AS '$libdir/latlon-v0009', 'pgl_ebox_lat_max'; 6.684 + 6.685 +CREATE FUNCTION min_longitude(ebox) 6.686 + RETURNS float8 6.687 + LANGUAGE C IMMUTABLE STRICT 6.688 + AS '$libdir/latlon-v0009', 'pgl_ebox_lon_min'; 6.689 + 6.690 +CREATE FUNCTION max_longitude(ebox) 6.691 + RETURNS float8 6.692 + LANGUAGE C IMMUTABLE STRICT 6.693 + AS '$libdir/latlon-v0009', 'pgl_ebox_lon_max'; 6.694 + 6.695 +CREATE FUNCTION center(ecircle) 6.696 + RETURNS epoint 6.697 + LANGUAGE C IMMUTABLE STRICT 6.698 + AS '$libdir/latlon-v0009', 'pgl_ecircle_center'; 6.699 + 6.700 +CREATE FUNCTION radius(ecircle) 6.701 + RETURNS float8 6.702 + LANGUAGE C IMMUTABLE STRICT 6.703 + AS '$libdir/latlon-v0009', 'pgl_ecircle_radius'; 6.704 + 6.705 +CREATE FUNCTION ecluster_extract_points(ecluster) 6.706 + RETURNS SETOF epoint 6.707 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.708 + SELECT "match"[2]::epoint 6.709 + FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match" 6.710 + $$; 6.711 + 6.712 +CREATE FUNCTION ecluster_extract_paths(ecluster) 6.713 + RETURNS SETOF epoint[] 6.714 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.715 + SELECT ( 6.716 + SELECT array_agg("m2"[1]::epoint) 6.717 + FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 6.718 + ) 6.719 + FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1" 6.720 + $$; 6.721 + 6.722 +CREATE FUNCTION ecluster_extract_outlines(ecluster) 6.723 + RETURNS SETOF epoint[] 6.724 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.725 + SELECT ( 6.726 + SELECT array_agg("m2"[1]::epoint) 6.727 + FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 6.728 + ) 6.729 + FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1" 6.730 + $$; 6.731 + 6.732 +CREATE FUNCTION ecluster_extract_polygons(ecluster) 6.733 + RETURNS SETOF epoint[] 6.734 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.735 + SELECT ( 6.736 + SELECT array_agg("m2"[1]::epoint) 6.737 + FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 6.738 + ) 6.739 + FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1" 6.740 + $$; 6.741 + 6.742 + 6.743 +--------------- 6.744 +-- operators -- 6.745 +--------------- 6.746 + 6.747 +CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox) 6.748 + RETURNS boolean 6.749 + LANGUAGE C IMMUTABLE STRICT 6.750 + AS '$libdir/latlon-v0009', 'pgl_epoint_ebox_overlap'; 6.751 + 6.752 +CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle) 6.753 + RETURNS boolean 6.754 + LANGUAGE C IMMUTABLE STRICT 6.755 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_overlap'; 6.756 + 6.757 +CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster) 6.758 + RETURNS boolean 6.759 + LANGUAGE C IMMUTABLE STRICT 6.760 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_overlap'; 6.761 + 6.762 +CREATE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster) 6.763 + RETURNS boolean 6.764 + LANGUAGE C IMMUTABLE STRICT 6.765 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_may_overlap'; 6.766 + 6.767 +CREATE FUNCTION ebox_overlap_proc(ebox, ebox) 6.768 + RETURNS boolean 6.769 + LANGUAGE C IMMUTABLE STRICT 6.770 + AS '$libdir/latlon-v0009', 'pgl_ebox_overlap'; 6.771 + 6.772 +CREATE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle) 6.773 + RETURNS boolean 6.774 + LANGUAGE C IMMUTABLE STRICT 6.775 + AS '$libdir/latlon-v0009', 'pgl_ebox_ecircle_may_overlap'; 6.776 + 6.777 +CREATE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster) 6.778 + RETURNS boolean 6.779 + LANGUAGE C IMMUTABLE STRICT 6.780 + AS '$libdir/latlon-v0009', 'pgl_ebox_ecluster_may_overlap'; 6.781 + 6.782 +CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle) 6.783 + RETURNS boolean 6.784 + LANGUAGE C IMMUTABLE STRICT 6.785 + AS '$libdir/latlon-v0009', 'pgl_ecircle_overlap'; 6.786 + 6.787 +CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster) 6.788 + RETURNS boolean 6.789 + LANGUAGE C IMMUTABLE STRICT 6.790 + AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_overlap'; 6.791 + 6.792 +CREATE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster) 6.793 + RETURNS boolean 6.794 + LANGUAGE C IMMUTABLE STRICT 6.795 + AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_may_overlap'; 6.796 + 6.797 +CREATE FUNCTION ecluster_overlap_proc(ecluster, ecluster) 6.798 + RETURNS boolean 6.799 + LANGUAGE C IMMUTABLE STRICT 6.800 + AS '$libdir/latlon-v0009', 'pgl_ecluster_overlap'; 6.801 + 6.802 +CREATE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster) 6.803 + RETURNS boolean 6.804 + LANGUAGE C IMMUTABLE STRICT 6.805 + AS '$libdir/latlon-v0009', 'pgl_ecluster_may_overlap'; 6.806 + 6.807 +CREATE FUNCTION ecluster_contains_proc(ecluster, ecluster) 6.808 + RETURNS boolean 6.809 + LANGUAGE C IMMUTABLE STRICT 6.810 + AS '$libdir/latlon-v0009', 'pgl_ecluster_contains'; 6.811 + 6.812 +CREATE FUNCTION epoint_distance_proc(epoint, epoint) 6.813 + RETURNS float8 6.814 + LANGUAGE C IMMUTABLE STRICT 6.815 + AS '$libdir/latlon-v0009', 'pgl_epoint_distance'; 6.816 + 6.817 +CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle) 6.818 + RETURNS float8 6.819 + LANGUAGE C IMMUTABLE STRICT 6.820 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_distance'; 6.821 + 6.822 +CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster) 6.823 + RETURNS float8 6.824 + LANGUAGE C IMMUTABLE STRICT 6.825 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_distance'; 6.826 + 6.827 +CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle) 6.828 + RETURNS float8 6.829 + LANGUAGE C IMMUTABLE STRICT 6.830 + AS '$libdir/latlon-v0009', 'pgl_ecircle_distance'; 6.831 + 6.832 +CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster) 6.833 + RETURNS float8 6.834 + LANGUAGE C IMMUTABLE STRICT 6.835 + AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_distance'; 6.836 + 6.837 +CREATE FUNCTION ecluster_distance_proc(ecluster, ecluster) 6.838 + RETURNS float8 6.839 + LANGUAGE C IMMUTABLE STRICT 6.840 + AS '$libdir/latlon-v0009', 'pgl_ecluster_distance'; 6.841 + 6.842 +CREATE FUNCTION fair_distance_operator_proc(ecluster, epoint_with_sample_count) 6.843 + RETURNS float8 6.844 + LANGUAGE C IMMUTABLE STRICT 6.845 + AS '$libdir/latlon-v0009', 'pgl_ecluster_epoint_sc_fair_distance'; 6.846 + 6.847 +CREATE OPERATOR && ( 6.848 + leftarg = epoint, 6.849 + rightarg = ebox, 6.850 + procedure = epoint_ebox_overlap_proc, 6.851 + commutator = &&, 6.852 + restrict = areasel, 6.853 + join = areajoinsel 6.854 +); 6.855 + 6.856 +CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint) 6.857 + RETURNS boolean 6.858 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 6.859 + 6.860 +CREATE OPERATOR && ( 6.861 + leftarg = ebox, 6.862 + rightarg = epoint, 6.863 + procedure = epoint_ebox_overlap_commutator, 6.864 + commutator = &&, 6.865 + restrict = areasel, 6.866 + join = areajoinsel 6.867 +); 6.868 + 6.869 +CREATE OPERATOR && ( 6.870 + leftarg = epoint, 6.871 + rightarg = ecircle, 6.872 + procedure = epoint_ecircle_overlap_proc, 6.873 + commutator = &&, 6.874 + restrict = areasel, 6.875 + join = areajoinsel 6.876 +); 6.877 + 6.878 +CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint) 6.879 + RETURNS boolean 6.880 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 6.881 + 6.882 +CREATE OPERATOR && ( 6.883 + leftarg = ecircle, 6.884 + rightarg = epoint, 6.885 + procedure = epoint_ecircle_overlap_commutator, 6.886 + commutator = &&, 6.887 + restrict = areasel, 6.888 + join = areajoinsel 6.889 +); 6.890 + 6.891 +CREATE OPERATOR && ( 6.892 + leftarg = epoint, 6.893 + rightarg = ecluster, 6.894 + procedure = epoint_ecluster_overlap_proc, 6.895 + commutator = &&, 6.896 + restrict = areasel, 6.897 + join = areajoinsel 6.898 +); 6.899 + 6.900 +CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint) 6.901 + RETURNS boolean 6.902 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 6.903 + 6.904 +CREATE OPERATOR && ( 6.905 + leftarg = ecluster, 6.906 + rightarg = epoint, 6.907 + procedure = epoint_ecluster_overlap_commutator, 6.908 + commutator = &&, 6.909 + restrict = areasel, 6.910 + join = areajoinsel 6.911 +); 6.912 + 6.913 +CREATE OPERATOR && ( 6.914 + leftarg = ebox, 6.915 + rightarg = ebox, 6.916 + procedure = ebox_overlap_proc, 6.917 + commutator = &&, 6.918 + restrict = areasel, 6.919 + join = areajoinsel 6.920 +); 6.921 + 6.922 +CREATE OPERATOR && ( 6.923 + leftarg = ecircle, 6.924 + rightarg = ecircle, 6.925 + procedure = ecircle_overlap_proc, 6.926 + commutator = &&, 6.927 + restrict = areasel, 6.928 + join = areajoinsel 6.929 +); 6.930 + 6.931 +CREATE OPERATOR && ( 6.932 + leftarg = ecircle, 6.933 + rightarg = ecluster, 6.934 + procedure = ecircle_ecluster_overlap_proc, 6.935 + commutator = &&, 6.936 + restrict = areasel, 6.937 + join = areajoinsel 6.938 +); 6.939 + 6.940 +CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle) 6.941 + RETURNS boolean 6.942 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 6.943 + 6.944 +CREATE OPERATOR && ( 6.945 + leftarg = ecluster, 6.946 + rightarg = ecircle, 6.947 + procedure = ecircle_ecluster_overlap_commutator, 6.948 + commutator = &&, 6.949 + restrict = areasel, 6.950 + join = areajoinsel 6.951 +); 6.952 + 6.953 +CREATE OPERATOR && ( 6.954 + leftarg = ecluster, 6.955 + rightarg = ecluster, 6.956 + procedure = ecluster_overlap_proc, 6.957 + commutator = &&, 6.958 + restrict = areasel, 6.959 + join = areajoinsel 6.960 +); 6.961 + 6.962 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle) 6.963 + RETURNS boolean 6.964 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2'; 6.965 + 6.966 +CREATE OPERATOR && ( 6.967 + leftarg = ebox, 6.968 + rightarg = ecircle, 6.969 + procedure = ebox_ecircle_overlap_castwrap, 6.970 + commutator = &&, 6.971 + restrict = areasel, 6.972 + join = areajoinsel 6.973 +); 6.974 + 6.975 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox) 6.976 + RETURNS boolean 6.977 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster'; 6.978 + 6.979 +CREATE OPERATOR && ( 6.980 + leftarg = ecircle, 6.981 + rightarg = ebox, 6.982 + procedure = ebox_ecircle_overlap_castwrap, 6.983 + commutator = &&, 6.984 + restrict = areasel, 6.985 + join = areajoinsel 6.986 +); 6.987 + 6.988 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster) 6.989 + RETURNS boolean 6.990 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2'; 6.991 + 6.992 +CREATE OPERATOR && ( 6.993 + leftarg = ebox, 6.994 + rightarg = ecluster, 6.995 + procedure = ebox_ecluster_overlap_castwrap, 6.996 + commutator = &&, 6.997 + restrict = areasel, 6.998 + join = areajoinsel 6.999 +); 6.1000 + 6.1001 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox) 6.1002 + RETURNS boolean 6.1003 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster'; 6.1004 + 6.1005 +CREATE OPERATOR && ( 6.1006 + leftarg = ecluster, 6.1007 + rightarg = ebox, 6.1008 + procedure = ebox_ecluster_overlap_castwrap, 6.1009 + commutator = &&, 6.1010 + restrict = areasel, 6.1011 + join = areajoinsel 6.1012 +); 6.1013 + 6.1014 +CREATE OPERATOR &&+ ( 6.1015 + leftarg = epoint, 6.1016 + rightarg = ecluster, 6.1017 + procedure = epoint_ecluster_may_overlap_proc, 6.1018 + commutator = &&+, 6.1019 + restrict = areasel, 6.1020 + join = areajoinsel 6.1021 +); 6.1022 + 6.1023 +CREATE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint) 6.1024 + RETURNS boolean 6.1025 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 6.1026 + 6.1027 +CREATE OPERATOR &&+ ( 6.1028 + leftarg = ecluster, 6.1029 + rightarg = epoint, 6.1030 + procedure = epoint_ecluster_may_overlap_commutator, 6.1031 + commutator = &&+, 6.1032 + restrict = areasel, 6.1033 + join = areajoinsel 6.1034 +); 6.1035 + 6.1036 +CREATE OPERATOR &&+ ( 6.1037 + leftarg = ebox, 6.1038 + rightarg = ecircle, 6.1039 + procedure = ebox_ecircle_may_overlap_proc, 6.1040 + commutator = &&+, 6.1041 + restrict = areasel, 6.1042 + join = areajoinsel 6.1043 +); 6.1044 + 6.1045 +CREATE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox) 6.1046 + RETURNS boolean 6.1047 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 6.1048 + 6.1049 +CREATE OPERATOR &&+ ( 6.1050 + leftarg = ecircle, 6.1051 + rightarg = ebox, 6.1052 + procedure = ebox_ecircle_may_overlap_commutator, 6.1053 + commutator = &&+, 6.1054 + restrict = areasel, 6.1055 + join = areajoinsel 6.1056 +); 6.1057 + 6.1058 +CREATE OPERATOR &&+ ( 6.1059 + leftarg = ebox, 6.1060 + rightarg = ecluster, 6.1061 + procedure = ebox_ecluster_may_overlap_proc, 6.1062 + commutator = &&+, 6.1063 + restrict = areasel, 6.1064 + join = areajoinsel 6.1065 +); 6.1066 + 6.1067 +CREATE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox) 6.1068 + RETURNS boolean 6.1069 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 6.1070 + 6.1071 +CREATE OPERATOR &&+ ( 6.1072 + leftarg = ecluster, 6.1073 + rightarg = ebox, 6.1074 + procedure = ebox_ecluster_may_overlap_commutator, 6.1075 + commutator = &&+, 6.1076 + restrict = areasel, 6.1077 + join = areajoinsel 6.1078 +); 6.1079 + 6.1080 +CREATE OPERATOR &&+ ( 6.1081 + leftarg = ecircle, 6.1082 + rightarg = ecluster, 6.1083 + procedure = ecircle_ecluster_may_overlap_proc, 6.1084 + commutator = &&+, 6.1085 + restrict = areasel, 6.1086 + join = areajoinsel 6.1087 +); 6.1088 + 6.1089 +CREATE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle) 6.1090 + RETURNS boolean 6.1091 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 6.1092 + 6.1093 +CREATE OPERATOR &&+ ( 6.1094 + leftarg = ecluster, 6.1095 + rightarg = ecircle, 6.1096 + procedure = ecircle_ecluster_may_overlap_commutator, 6.1097 + commutator = &&+, 6.1098 + restrict = areasel, 6.1099 + join = areajoinsel 6.1100 +); 6.1101 + 6.1102 +CREATE OPERATOR &&+ ( 6.1103 + leftarg = ecluster, 6.1104 + rightarg = ecluster, 6.1105 + procedure = ecluster_may_overlap_proc, 6.1106 + commutator = &&+, 6.1107 + restrict = areasel, 6.1108 + join = areajoinsel 6.1109 +); 6.1110 + 6.1111 +CREATE OPERATOR @> ( 6.1112 + leftarg = ebox, 6.1113 + rightarg = epoint, 6.1114 + procedure = epoint_ebox_overlap_commutator, 6.1115 + commutator = <@, 6.1116 + restrict = areasel, 6.1117 + join = areajoinsel 6.1118 +); 6.1119 + 6.1120 +CREATE OPERATOR <@ ( 6.1121 + leftarg = epoint, 6.1122 + rightarg = ebox, 6.1123 + procedure = epoint_ebox_overlap_proc, 6.1124 + commutator = @>, 6.1125 + restrict = areasel, 6.1126 + join = areajoinsel 6.1127 +); 6.1128 + 6.1129 +CREATE OPERATOR @> ( 6.1130 + leftarg = ecluster, 6.1131 + rightarg = epoint, 6.1132 + procedure = epoint_ecluster_overlap_commutator, 6.1133 + commutator = <@, 6.1134 + restrict = areasel, 6.1135 + join = areajoinsel 6.1136 +); 6.1137 + 6.1138 +CREATE OPERATOR <@ ( 6.1139 + leftarg = epoint, 6.1140 + rightarg = ecluster, 6.1141 + procedure = epoint_ecluster_overlap_proc, 6.1142 + commutator = @>, 6.1143 + restrict = areasel, 6.1144 + join = areajoinsel 6.1145 +); 6.1146 + 6.1147 +CREATE OPERATOR @> ( 6.1148 + leftarg = ecluster, 6.1149 + rightarg = ecluster, 6.1150 + procedure = ecluster_contains_proc, 6.1151 + commutator = <@, 6.1152 + restrict = areasel, 6.1153 + join = areajoinsel 6.1154 +); 6.1155 + 6.1156 +CREATE FUNCTION ecluster_contains_commutator(ecluster, ecluster) 6.1157 + RETURNS boolean 6.1158 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1'; 6.1159 + 6.1160 +CREATE OPERATOR <@ ( 6.1161 + leftarg = ecluster, 6.1162 + rightarg = ecluster, 6.1163 + procedure = ecluster_contains_commutator, 6.1164 + commutator = @>, 6.1165 + restrict = areasel, 6.1166 + join = areajoinsel 6.1167 +); 6.1168 + 6.1169 +CREATE FUNCTION ebox_contains_castwrap(ebox, ebox) 6.1170 + RETURNS boolean 6.1171 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2::ecluster'; 6.1172 + 6.1173 +CREATE OPERATOR @> ( 6.1174 + leftarg = ebox, 6.1175 + rightarg = ebox, 6.1176 + procedure = ebox_contains_castwrap, 6.1177 + commutator = <@, 6.1178 + restrict = areasel, 6.1179 + join = areajoinsel 6.1180 +); 6.1181 + 6.1182 +CREATE FUNCTION ebox_contains_swapped_castwrap(ebox, ebox) 6.1183 + RETURNS boolean 6.1184 + LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1::ecluster'; 6.1185 + 6.1186 +CREATE OPERATOR <@ ( 6.1187 + leftarg = ebox, 6.1188 + rightarg = ebox, 6.1189 + procedure = ebox_contains_swapped_castwrap, 6.1190 + commutator = @>, 6.1191 + restrict = areasel, 6.1192 + join = areajoinsel 6.1193 +); 6.1194 + 6.1195 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster) 6.1196 + RETURNS boolean 6.1197 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2'; 6.1198 + 6.1199 +CREATE OPERATOR @> ( 6.1200 + leftarg = ebox, 6.1201 + rightarg = ecluster, 6.1202 + procedure = ebox_ecluster_contains_castwrap, 6.1203 + commutator = <@, 6.1204 + restrict = areasel, 6.1205 + join = areajoinsel 6.1206 +); 6.1207 + 6.1208 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox) 6.1209 + RETURNS boolean 6.1210 + LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1'; 6.1211 + 6.1212 +CREATE OPERATOR <@ ( 6.1213 + leftarg = ecluster, 6.1214 + rightarg = ebox, 6.1215 + procedure = ebox_ecluster_contains_castwrap, 6.1216 + commutator = @>, 6.1217 + restrict = areasel, 6.1218 + join = areajoinsel 6.1219 +); 6.1220 + 6.1221 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox) 6.1222 + RETURNS boolean 6.1223 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 @> $2::ecluster'; 6.1224 + 6.1225 +CREATE OPERATOR @> ( 6.1226 + leftarg = ecluster, 6.1227 + rightarg = ebox, 6.1228 + procedure = ecluster_ebox_contains_castwrap, 6.1229 + commutator = <@, 6.1230 + restrict = areasel, 6.1231 + join = areajoinsel 6.1232 +); 6.1233 + 6.1234 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster) 6.1235 + RETURNS boolean 6.1236 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1::ecluster'; 6.1237 + 6.1238 +CREATE OPERATOR <@ ( 6.1239 + leftarg = ebox, 6.1240 + rightarg = ecluster, 6.1241 + procedure = ecluster_ebox_contains_castwrap, 6.1242 + commutator = @>, 6.1243 + restrict = areasel, 6.1244 + join = areajoinsel 6.1245 +); 6.1246 + 6.1247 +CREATE OPERATOR <-> ( 6.1248 + leftarg = epoint, 6.1249 + rightarg = epoint, 6.1250 + procedure = epoint_distance_proc, 6.1251 + commutator = <-> 6.1252 +); 6.1253 + 6.1254 +CREATE OPERATOR <-> ( 6.1255 + leftarg = epoint, 6.1256 + rightarg = ecircle, 6.1257 + procedure = epoint_ecircle_distance_proc, 6.1258 + commutator = <-> 6.1259 +); 6.1260 + 6.1261 +CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint) 6.1262 + RETURNS float8 6.1263 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 6.1264 + 6.1265 +CREATE OPERATOR <-> ( 6.1266 + leftarg = ecircle, 6.1267 + rightarg = epoint, 6.1268 + procedure = epoint_ecircle_distance_commutator, 6.1269 + commutator = <-> 6.1270 +); 6.1271 + 6.1272 +CREATE OPERATOR <-> ( 6.1273 + leftarg = epoint, 6.1274 + rightarg = ecluster, 6.1275 + procedure = epoint_ecluster_distance_proc, 6.1276 + commutator = <-> 6.1277 +); 6.1278 + 6.1279 +CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint) 6.1280 + RETURNS float8 6.1281 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 6.1282 + 6.1283 +CREATE OPERATOR <-> ( 6.1284 + leftarg = ecluster, 6.1285 + rightarg = epoint, 6.1286 + procedure = epoint_ecluster_distance_commutator, 6.1287 + commutator = <-> 6.1288 +); 6.1289 + 6.1290 +CREATE OPERATOR <-> ( 6.1291 + leftarg = ecircle, 6.1292 + rightarg = ecircle, 6.1293 + procedure = ecircle_distance_proc, 6.1294 + commutator = <-> 6.1295 +); 6.1296 + 6.1297 +CREATE OPERATOR <-> ( 6.1298 + leftarg = ecircle, 6.1299 + rightarg = ecluster, 6.1300 + procedure = ecircle_ecluster_distance_proc, 6.1301 + commutator = <-> 6.1302 +); 6.1303 + 6.1304 +CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle) 6.1305 + RETURNS float8 6.1306 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 6.1307 + 6.1308 +CREATE OPERATOR <-> ( 6.1309 + leftarg = ecluster, 6.1310 + rightarg = ecircle, 6.1311 + procedure = ecircle_ecluster_distance_commutator, 6.1312 + commutator = <-> 6.1313 +); 6.1314 + 6.1315 +CREATE OPERATOR <-> ( 6.1316 + leftarg = ecluster, 6.1317 + rightarg = ecluster, 6.1318 + procedure = ecluster_distance_proc, 6.1319 + commutator = <-> 6.1320 +); 6.1321 + 6.1322 +CREATE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox) 6.1323 + RETURNS float8 6.1324 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster'; 6.1325 + 6.1326 +CREATE OPERATOR <-> ( 6.1327 + leftarg = epoint, 6.1328 + rightarg = ebox, 6.1329 + procedure = epoint_ebox_distance_castwrap, 6.1330 + commutator = <-> 6.1331 +); 6.1332 + 6.1333 +CREATE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint) 6.1334 + RETURNS float8 6.1335 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2'; 6.1336 + 6.1337 +CREATE OPERATOR <-> ( 6.1338 + leftarg = ebox, 6.1339 + rightarg = epoint, 6.1340 + procedure = epoint_ebox_distance_castwrap, 6.1341 + commutator = <-> 6.1342 +); 6.1343 + 6.1344 +CREATE FUNCTION ebox_distance_castwrap(ebox, ebox) 6.1345 + RETURNS float8 6.1346 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2::ecluster'; 6.1347 + 6.1348 +CREATE OPERATOR <-> ( 6.1349 + leftarg = ebox, 6.1350 + rightarg = ebox, 6.1351 + procedure = ebox_distance_castwrap, 6.1352 + commutator = <-> 6.1353 +); 6.1354 + 6.1355 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle) 6.1356 + RETURNS float8 6.1357 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2'; 6.1358 + 6.1359 +CREATE OPERATOR <-> ( 6.1360 + leftarg = ebox, 6.1361 + rightarg = ecircle, 6.1362 + procedure = ebox_ecircle_distance_castwrap, 6.1363 + commutator = <-> 6.1364 +); 6.1365 + 6.1366 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox) 6.1367 + RETURNS float8 6.1368 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster'; 6.1369 + 6.1370 +CREATE OPERATOR <-> ( 6.1371 + leftarg = ecircle, 6.1372 + rightarg = ebox, 6.1373 + procedure = ebox_ecircle_distance_castwrap, 6.1374 + commutator = <-> 6.1375 +); 6.1376 + 6.1377 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster) 6.1378 + RETURNS float8 6.1379 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2'; 6.1380 + 6.1381 +CREATE OPERATOR <-> ( 6.1382 + leftarg = ebox, 6.1383 + rightarg = ecluster, 6.1384 + procedure = ebox_ecluster_distance_castwrap, 6.1385 + commutator = <-> 6.1386 +); 6.1387 + 6.1388 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox) 6.1389 + RETURNS float8 6.1390 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster'; 6.1391 + 6.1392 +CREATE OPERATOR <-> ( 6.1393 + leftarg = ecluster, 6.1394 + rightarg = ebox, 6.1395 + procedure = ebox_ecluster_distance_castwrap, 6.1396 + commutator = <-> 6.1397 +); 6.1398 + 6.1399 +CREATE OPERATOR <=> ( 6.1400 + leftarg = ecluster, 6.1401 + rightarg = epoint_with_sample_count, 6.1402 + procedure = fair_distance_operator_proc 6.1403 +); 6.1404 + 6.1405 + 6.1406 +---------------- 6.1407 +-- GiST index -- 6.1408 +---------------- 6.1409 + 6.1410 +CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal) 6.1411 + RETURNS boolean 6.1412 + LANGUAGE C STRICT 6.1413 + AS '$libdir/latlon-v0009', 'pgl_gist_consistent'; 6.1414 + 6.1415 +CREATE FUNCTION pgl_gist_union(internal, internal) 6.1416 + RETURNS internal 6.1417 + LANGUAGE C STRICT 6.1418 + AS '$libdir/latlon-v0009', 'pgl_gist_union'; 6.1419 + 6.1420 +CREATE FUNCTION pgl_gist_compress_epoint(internal) 6.1421 + RETURNS internal 6.1422 + LANGUAGE C STRICT 6.1423 + AS '$libdir/latlon-v0009', 'pgl_gist_compress_epoint'; 6.1424 + 6.1425 +CREATE FUNCTION pgl_gist_compress_ecircle(internal) 6.1426 + RETURNS internal 6.1427 + LANGUAGE C STRICT 6.1428 + AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecircle'; 6.1429 + 6.1430 +CREATE FUNCTION pgl_gist_compress_ecluster(internal) 6.1431 + RETURNS internal 6.1432 + LANGUAGE C STRICT 6.1433 + AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecluster'; 6.1434 + 6.1435 +CREATE FUNCTION pgl_gist_decompress(internal) 6.1436 + RETURNS internal 6.1437 + LANGUAGE C STRICT 6.1438 + AS '$libdir/latlon-v0009', 'pgl_gist_decompress'; 6.1439 + 6.1440 +CREATE FUNCTION pgl_gist_penalty(internal, internal, internal) 6.1441 + RETURNS internal 6.1442 + LANGUAGE C STRICT 6.1443 + AS '$libdir/latlon-v0009', 'pgl_gist_penalty'; 6.1444 + 6.1445 +CREATE FUNCTION pgl_gist_picksplit(internal, internal) 6.1446 + RETURNS internal 6.1447 + LANGUAGE C STRICT 6.1448 + AS '$libdir/latlon-v0009', 'pgl_gist_picksplit'; 6.1449 + 6.1450 +CREATE FUNCTION pgl_gist_same(internal, internal, internal) 6.1451 + RETURNS internal 6.1452 + LANGUAGE C STRICT 6.1453 + AS '$libdir/latlon-v0009', 'pgl_gist_same'; 6.1454 + 6.1455 +CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid) 6.1456 + RETURNS internal 6.1457 + LANGUAGE C STRICT 6.1458 + AS '$libdir/latlon-v0009', 'pgl_gist_distance'; 6.1459 + 6.1460 +CREATE OPERATOR CLASS epoint_ops 6.1461 + DEFAULT FOR TYPE epoint USING gist AS 6.1462 + OPERATOR 11 = , 6.1463 + OPERATOR 22 && (epoint, ebox), 6.1464 + OPERATOR 222 <@ (epoint, ebox), 6.1465 + OPERATOR 23 && (epoint, ecircle), 6.1466 + OPERATOR 24 && (epoint, ecluster), 6.1467 + OPERATOR 124 &&+ (epoint, ecluster), 6.1468 + OPERATOR 224 <@ (epoint, ecluster), 6.1469 + OPERATOR 31 <-> (epoint, epoint) FOR ORDER BY float_ops, 6.1470 + OPERATOR 32 <-> (epoint, ebox) FOR ORDER BY float_ops, 6.1471 + OPERATOR 33 <-> (epoint, ecircle) FOR ORDER BY float_ops, 6.1472 + OPERATOR 34 <-> (epoint, ecluster) FOR ORDER BY float_ops, 6.1473 + FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 6.1474 + FUNCTION 2 pgl_gist_union(internal, internal), 6.1475 + FUNCTION 3 pgl_gist_compress_epoint(internal), 6.1476 + FUNCTION 4 pgl_gist_decompress(internal), 6.1477 + FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 6.1478 + FUNCTION 6 pgl_gist_picksplit(internal, internal), 6.1479 + FUNCTION 7 pgl_gist_same(internal, internal, internal), 6.1480 + FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 6.1481 + STORAGE ekey_point; 6.1482 + 6.1483 +CREATE OPERATOR CLASS ecircle_ops 6.1484 + DEFAULT FOR TYPE ecircle USING gist AS 6.1485 + OPERATOR 13 = , 6.1486 + OPERATOR 21 && (ecircle, epoint), 6.1487 + OPERATOR 22 && (ecircle, ebox), 6.1488 + OPERATOR 122 &&+ (ecircle, ebox), 6.1489 + OPERATOR 23 && (ecircle, ecircle), 6.1490 + OPERATOR 24 && (ecircle, ecluster), 6.1491 + OPERATOR 124 &&+ (ecircle, ecluster), 6.1492 + OPERATOR 31 <-> (ecircle, epoint) FOR ORDER BY float_ops, 6.1493 + OPERATOR 32 <-> (ecircle, ebox) FOR ORDER BY float_ops, 6.1494 + OPERATOR 33 <-> (ecircle, ecircle) FOR ORDER BY float_ops, 6.1495 + OPERATOR 34 <-> (ecircle, ecluster) FOR ORDER BY float_ops, 6.1496 + FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 6.1497 + FUNCTION 2 pgl_gist_union(internal, internal), 6.1498 + FUNCTION 3 pgl_gist_compress_ecircle(internal), 6.1499 + FUNCTION 4 pgl_gist_decompress(internal), 6.1500 + FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 6.1501 + FUNCTION 6 pgl_gist_picksplit(internal, internal), 6.1502 + FUNCTION 7 pgl_gist_same(internal, internal, internal), 6.1503 + FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 6.1504 + STORAGE ekey_area; 6.1505 + 6.1506 +CREATE OPERATOR CLASS ecluster_ops 6.1507 + DEFAULT FOR TYPE ecluster USING gist AS 6.1508 + OPERATOR 21 && (ecluster, epoint), 6.1509 + OPERATOR 121 &&+ (ecluster, epoint), 6.1510 + OPERATOR 221 @> (ecluster, epoint), 6.1511 + OPERATOR 22 && (ecluster, ebox), 6.1512 + OPERATOR 122 &&+ (ecluster, ebox), 6.1513 + OPERATOR 222 @> (ecluster, ebox), 6.1514 + OPERATOR 322 <@ (ecluster, ebox), 6.1515 + OPERATOR 23 && (ecluster, ecircle), 6.1516 + OPERATOR 123 &&+ (ecluster, ecircle), 6.1517 + OPERATOR 24 && (ecluster, ecluster), 6.1518 + OPERATOR 124 &&+ (ecluster, ecluster), 6.1519 + OPERATOR 224 @> (ecluster, ecluster), 6.1520 + OPERATOR 324 <@ (ecluster, ecluster), 6.1521 + OPERATOR 31 <-> (ecluster, epoint) FOR ORDER BY float_ops, 6.1522 + OPERATOR 32 <-> (ecluster, ebox) FOR ORDER BY float_ops, 6.1523 + OPERATOR 33 <-> (ecluster, ecircle) FOR ORDER BY float_ops, 6.1524 + OPERATOR 34 <-> (ecluster, ecluster) FOR ORDER BY float_ops, 6.1525 + OPERATOR 131 <=> (ecluster, epoint_with_sample_count) FOR ORDER BY float_ops, 6.1526 + FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 6.1527 + FUNCTION 2 pgl_gist_union(internal, internal), 6.1528 + FUNCTION 3 pgl_gist_compress_ecluster(internal), 6.1529 + FUNCTION 4 pgl_gist_decompress(internal), 6.1530 + FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 6.1531 + FUNCTION 6 pgl_gist_picksplit(internal, internal), 6.1532 + FUNCTION 7 pgl_gist_same(internal, internal, internal), 6.1533 + FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 6.1534 + STORAGE ekey_area; 6.1535 + 6.1536 + 6.1537 +--------------------- 6.1538 +-- alias functions -- 6.1539 +--------------------- 6.1540 + 6.1541 +CREATE FUNCTION distance(epoint, epoint) 6.1542 + RETURNS float8 6.1543 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2'; 6.1544 + 6.1545 +CREATE FUNCTION distance(ecluster, epoint) 6.1546 + RETURNS float8 6.1547 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2'; 6.1548 + 6.1549 +CREATE FUNCTION distance_within(epoint, epoint, float8) 6.1550 + RETURNS boolean 6.1551 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)'; 6.1552 + 6.1553 +CREATE FUNCTION distance_within(ecluster, epoint, float8) 6.1554 + RETURNS boolean 6.1555 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)'; 6.1556 + 6.1557 +CREATE FUNCTION fair_distance(ecluster, epoint, int4 = 10000) 6.1558 + RETURNS float8 6.1559 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <=> epoint_with_sample_count($2, $3)'; 6.1560 + 6.1561 + 6.1562 +-------------------------------- 6.1563 +-- other data storage formats -- 6.1564 +-------------------------------- 6.1565 + 6.1566 +CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint') 6.1567 + RETURNS epoint 6.1568 + LANGUAGE plpgsql IMMUTABLE STRICT AS $$ 6.1569 + DECLARE 6.1570 + "result" epoint; 6.1571 + BEGIN 6.1572 + IF $3 = 'epoint_lonlat' THEN 6.1573 + -- avoid dynamic command execution for better performance 6.1574 + RETURN epoint($2, $1); 6.1575 + END IF; 6.1576 + IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN 6.1577 + -- avoid dynamic command execution for better performance 6.1578 + RETURN epoint($1, $2); 6.1579 + END IF; 6.1580 + EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2; 6.1581 + RETURN "result"; 6.1582 + END; 6.1583 + $$; 6.1584 + 6.1585 +CREATE FUNCTION GeoJSON_LinearRing_vertices(jsonb, text = 'epoint_lonlat') 6.1586 + RETURNS SETOF jsonb 6.1587 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.1588 + SELECT "result" FROM 6.1589 + ( SELECT jsonb_array_length($1) - 1 ) AS "lastindex_row" ("lastindex") 6.1590 + CROSS JOIN LATERAL jsonb_array_elements( 6.1591 + CASE WHEN 6.1592 + coords_to_epoint( 6.1593 + ($1->0->>0)::float8, 6.1594 + ($1->0->>1)::float8, 6.1595 + $2 6.1596 + ) = coords_to_epoint( 6.1597 + ($1->"lastindex"->>0)::float8, 6.1598 + ($1->"lastindex"->>1)::float8, 6.1599 + $2 6.1600 + ) 6.1601 + THEN 6.1602 + $1 - "lastindex" 6.1603 + ELSE 6.1604 + $1 6.1605 + END 6.1606 + ) AS "result_row" ("result") 6.1607 + $$; 6.1608 + 6.1609 +CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat') 6.1610 + RETURNS epoint 6.1611 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.1612 + SELECT CASE 6.1613 + WHEN $1->>'type' = 'Point' THEN 6.1614 + coords_to_epoint( 6.1615 + ($1->'coordinates'->>0)::float8, 6.1616 + ($1->'coordinates'->>1)::float8, 6.1617 + $2 6.1618 + ) 6.1619 + WHEN $1->>'type' = 'Feature' THEN 6.1620 + GeoJSON_to_epoint($1->'geometry', $2) 6.1621 + ELSE 6.1622 + NULL 6.1623 + END 6.1624 + $$; 6.1625 + 6.1626 +CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat') 6.1627 + RETURNS ecluster 6.1628 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.1629 + SELECT CASE $1->>'type' 6.1630 + WHEN 'Point' THEN 6.1631 + coords_to_epoint( 6.1632 + ($1->'coordinates'->>0)::float8, 6.1633 + ($1->'coordinates'->>1)::float8, 6.1634 + $2 6.1635 + )::ecluster 6.1636 + WHEN 'MultiPoint' THEN 6.1637 + ( SELECT ecluster_create_multipoint(array_agg( 6.1638 + coords_to_epoint( 6.1639 + ("coord"->>0)::float8, 6.1640 + ("coord"->>1)::float8, 6.1641 + $2 6.1642 + ) 6.1643 + )) 6.1644 + FROM jsonb_array_elements($1->'coordinates') AS "coord" 6.1645 + ) 6.1646 + WHEN 'LineString' THEN 6.1647 + ( SELECT ecluster_create_path(array_agg( 6.1648 + coords_to_epoint( 6.1649 + ("coord"->>0)::float8, 6.1650 + ("coord"->>1)::float8, 6.1651 + $2 6.1652 + ) 6.1653 + )) 6.1654 + FROM jsonb_array_elements($1->'coordinates') AS "coord" 6.1655 + ) 6.1656 + WHEN 'MultiLineString' THEN 6.1657 + ( SELECT ecluster_concat(array_agg( 6.1658 + ( SELECT ecluster_create_path(array_agg( 6.1659 + coords_to_epoint( 6.1660 + ("coord"->>0)::float8, 6.1661 + ("coord"->>1)::float8, 6.1662 + $2 6.1663 + ) 6.1664 + )) 6.1665 + FROM jsonb_array_elements("coord_array") AS "coord" 6.1666 + ) 6.1667 + )) 6.1668 + FROM jsonb_array_elements($1->'coordinates') AS "coord_array" 6.1669 + ) 6.1670 + WHEN 'Polygon' THEN 6.1671 + ( SELECT ecluster_concat(array_agg( 6.1672 + ( SELECT ecluster_create_polygon(array_agg( 6.1673 + coords_to_epoint( 6.1674 + ("coord"->>0)::float8, 6.1675 + ("coord"->>1)::float8, 6.1676 + $2 6.1677 + ) 6.1678 + )) 6.1679 + FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord" 6.1680 + ) 6.1681 + )) 6.1682 + FROM jsonb_array_elements($1->'coordinates') AS "coord_array" 6.1683 + ) 6.1684 + WHEN 'MultiPolygon' THEN 6.1685 + ( SELECT ecluster_concat(array_agg( 6.1686 + ( SELECT ecluster_concat(array_agg( 6.1687 + ( SELECT ecluster_create_polygon(array_agg( 6.1688 + coords_to_epoint( 6.1689 + ("coord"->>0)::float8, 6.1690 + ("coord"->>1)::float8, 6.1691 + $2 6.1692 + ) 6.1693 + )) 6.1694 + FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord" 6.1695 + ) 6.1696 + )) 6.1697 + FROM jsonb_array_elements("coord_array_array") AS "coord_array" 6.1698 + ) 6.1699 + )) 6.1700 + FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array" 6.1701 + ) 6.1702 + WHEN 'GeometryCollection' THEN 6.1703 + ( SELECT ecluster_concat(array_agg( 6.1704 + GeoJSON_to_ecluster("geometry", $2) 6.1705 + )) 6.1706 + FROM jsonb_array_elements($1->'geometries') AS "geometry" 6.1707 + ) 6.1708 + WHEN 'Feature' THEN 6.1709 + GeoJSON_to_ecluster($1->'geometry', $2) 6.1710 + WHEN 'FeatureCollection' THEN 6.1711 + ( SELECT ecluster_concat(array_agg( 6.1712 + GeoJSON_to_ecluster("feature", $2) 6.1713 + )) 6.1714 + FROM jsonb_array_elements($1->'features') AS "feature" 6.1715 + ) 6.1716 + ELSE 6.1717 + NULL 6.1718 + END 6.1719 + $$; 6.1720 +
7.1 --- a/latlon-v0009.c Tue Feb 11 02:35:16 2020 +0100 7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 7.3 @@ -1,3352 +0,0 @@ 7.4 - 7.5 -/*-------------* 7.6 - * C prelude * 7.7 - *-------------*/ 7.8 - 7.9 -#include "postgres.h" 7.10 -#include "fmgr.h" 7.11 -#include "libpq/pqformat.h" 7.12 -#include "access/gist.h" 7.13 -#include "access/stratnum.h" 7.14 -#include "utils/array.h" 7.15 -#include <limits.h> 7.16 -#include <math.h> 7.17 - 7.18 -#ifdef PG_MODULE_MAGIC 7.19 -PG_MODULE_MAGIC; 7.20 -#endif 7.21 - 7.22 -#if INT_MAX < 2147483647 7.23 -#error Expected int type to be at least 32 bit wide 7.24 -#endif 7.25 - 7.26 - 7.27 -/*---------------------------------* 7.28 - * distance calculation on earth * 7.29 - * (using WGS-84 spheroid) * 7.30 - *---------------------------------*/ 7.31 - 7.32 -/* WGS-84 spheroid with following parameters: 7.33 - semi-major axis a = 6378137 7.34 - semi-minor axis b = a * (1 - 1/298.257223563) 7.35 - estimated diameter = 2 * (2*a+b)/3 7.36 -*/ 7.37 -#define PGL_SPHEROID_A 6378137.0 /* semi major axis */ 7.38 -#define PGL_SPHEROID_F (1.0/298.257223563) /* flattening */ 7.39 -#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F)) 7.40 -#define PGL_EPS2 ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \ 7.41 - PGL_SPHEROID_B * PGL_SPHEROID_B ) / \ 7.42 - ( PGL_SPHEROID_A * PGL_SPHEROID_A ) ) 7.43 -#define PGL_SUBEPS2 (1.0-PGL_EPS2) 7.44 -#define PGL_RADIUS ((2.0*PGL_SPHEROID_A + PGL_SPHEROID_B) / 3.0) 7.45 -#define PGL_DIAMETER (2.0 * PGL_RADIUS) 7.46 -#define PGL_SCALE (PGL_SPHEROID_A / PGL_DIAMETER) /* semi-major ref. */ 7.47 -#define PGL_MAXDIST (PGL_RADIUS * M_PI) /* maximum distance */ 7.48 -#define PGL_FADELIMIT (PGL_MAXDIST / 3.0) /* 1/6 circumference */ 7.49 - 7.50 -/* calculate distance between two points on earth (given in degrees) */ 7.51 -static inline double pgl_distance( 7.52 - double lat1, double lon1, double lat2, double lon2 7.53 -) { 7.54 - float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin; 7.55 - float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t; 7.56 - /* normalize delta longitude (lon2 > 0 && lon1 = 0) */ 7.57 - /* lon1 = 0 (not used anymore) */ 7.58 - lon2 = fabs(lon2-lon1); 7.59 - /* convert to radians (first divide, then multiply) */ 7.60 - lat1 = (lat1 / 180.0) * M_PI; 7.61 - lat2 = (lat2 / 180.0) * M_PI; 7.62 - lon2 = (lon2 / 180.0) * M_PI; 7.63 - /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point 7.64 - operations (lon2 >= lon1 is already ensured in a previous step) */ 7.65 - if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; } 7.66 - /* calculate 3d coordinates on scaled ellipsoid which has an average diameter 7.67 - of 1.0 */ 7.68 - lat1cos = cos(lat1); lat1sin = sin(lat1); 7.69 - lat2cos = cos(lat2); lat2sin = sin(lat2); 7.70 - lon2cos = cos(lon2); lon2sin = sin(lon2); 7.71 - nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin); 7.72 - nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin); 7.73 - x1 = nphi1 * lat1cos; 7.74 - z1 = nphi1 * PGL_SUBEPS2 * lat1sin; 7.75 - x2 = nphi2 * lat2cos * lon2cos; 7.76 - y2 = nphi2 * lat2cos * lon2sin; 7.77 - z2 = nphi2 * PGL_SUBEPS2 * lat2sin; 7.78 - /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */ 7.79 - g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1)); 7.80 - /* convert tunnel distance through scaled ellipsoid to approximated surface 7.81 - distance on original ellipsoid */ 7.82 - if (g > 1.0) g = 1.0; 7.83 - s = PGL_DIAMETER * asin(g); 7.84 - /* return result only if small enough to be precise (less than 1/3 of 7.85 - maximum possible distance) */ 7.86 - if (s <= PGL_FADELIMIT) return s; 7.87 - /* calculate tunnel distance to antipodal point through scaled ellipsoid */ 7.88 - g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1)); 7.89 - /* convert tunnel distance to antipodal point through scaled ellipsoid to 7.90 - approximated surface distance to antipodal point on original ellipsoid */ 7.91 - if (g > 1.0) g = 1.0; 7.92 - t = PGL_DIAMETER * asin(g); 7.93 - /* surface distance between original points can now be approximated by 7.94 - substracting antipodal distance from maximum possible distance; 7.95 - return result only if small enough (less than 1/3 of maximum possible 7.96 - distance) */ 7.97 - if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t; 7.98 - /* otherwise crossfade direct and antipodal result to ensure monotonicity */ 7.99 - return ( 7.100 - (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) / 7.101 - (s + t - 2*PGL_FADELIMIT) 7.102 - ); 7.103 -} 7.104 - 7.105 -/* finite distance that can not be reached on earth */ 7.106 -#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST) 7.107 - 7.108 - 7.109 -/*--------------------------------* 7.110 - * simple geographic data types * 7.111 - *--------------------------------*/ 7.112 - 7.113 -/* point on earth given by latitude and longitude in degrees */ 7.114 -/* (type "epoint" in SQL) */ 7.115 -typedef struct { 7.116 - double lat; /* between -90 and 90 (both inclusive) */ 7.117 - double lon; /* between -180 and 180 (both inclusive) */ 7.118 -} pgl_point; 7.119 - 7.120 -/* box delimited by two parallels and two meridians (all in degrees) */ 7.121 -/* (type "ebox" in SQL) */ 7.122 -typedef struct { 7.123 - double lat_min; /* between -90 and 90 (both inclusive) */ 7.124 - double lat_max; /* between -90 and 90 (both inclusive) */ 7.125 - double lon_min; /* between -180 and 180 (both inclusive) */ 7.126 - double lon_max; /* between -180 and 180 (both inclusive) */ 7.127 - /* if lat_min > lat_max, then box is empty */ 7.128 - /* if lon_min > lon_max, then 180th meridian is crossed */ 7.129 -} pgl_box; 7.130 - 7.131 -/* circle on earth surface (for radial searches with fixed radius) */ 7.132 -/* (type "ecircle" in SQL) */ 7.133 -typedef struct { 7.134 - pgl_point center; 7.135 - double radius; /* positive (including +0 but excluding -0), or -INFINITY */ 7.136 - /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point), 7.137 - zero radius (0) denotes a single point, 7.138 - a finite radius (0 < radius < INFINITY) denotes a filled circle, and 7.139 - a radius of INFINITY is valid and means complete coverage of earth. */ 7.140 -} pgl_circle; 7.141 - 7.142 - 7.143 -/*----------------------------------* 7.144 - * geographic "cluster" data type * 7.145 - *----------------------------------*/ 7.146 - 7.147 -/* A cluster is a collection of points, paths, outlines, and polygons. If two 7.148 - polygons in a cluster overlap, the area covered by both polygons does not 7.149 - belong to the cluster. This way, a cluster can be used to describe complex 7.150 - shapes like polygons with holes. Outlines are non-filled polygons. Paths are 7.151 - open by default (i.e. the last point in the list is not connected with the 7.152 - first point in the list). Note that each outline or polygon in a cluster 7.153 - must cover a longitude range of less than 180 degrees to avoid ambiguities. 7.154 - Areas which are larger may be split into multiple polygons. */ 7.155 - 7.156 -/* maximum number of points in a cluster */ 7.157 -/* (limited to avoid integer overflows, e.g. when allocating memory) */ 7.158 -#define PGL_CLUSTER_MAXPOINTS 16777216 7.159 - 7.160 -/* types of cluster entries */ 7.161 -#define PGL_ENTRY_POINT 1 /* a point */ 7.162 -#define PGL_ENTRY_PATH 2 /* a path from first point to last point */ 7.163 -#define PGL_ENTRY_OUTLINE 3 /* a non-filled polygon with given vertices */ 7.164 -#define PGL_ENTRY_POLYGON 4 /* a filled polygon with given vertices */ 7.165 - 7.166 -/* Entries of a cluster are described by two different structs: pgl_newentry 7.167 - and pgl_entry. The first is used only during construction of a cluster, the 7.168 - second is used in all other cases (e.g. when reading clusters from the 7.169 - database, performing operations, etc). */ 7.170 - 7.171 -/* entry for new geographic cluster during construction of that cluster */ 7.172 -typedef struct { 7.173 - int32_t entrytype; 7.174 - int32_t npoints; 7.175 - pgl_point *points; /* pointer to an array of points (pgl_point) */ 7.176 -} pgl_newentry; 7.177 - 7.178 -/* entry of geographic cluster */ 7.179 -typedef struct { 7.180 - int32_t entrytype; /* type of entry: point, path, outline, polygon */ 7.181 - int32_t npoints; /* number of stored points (set to 1 for point entry) */ 7.182 - int32_t offset; /* offset of pgl_point array from cluster base address */ 7.183 - /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */ 7.184 -} pgl_entry; 7.185 - 7.186 -/* geographic cluster which is a collection of points, (open) paths, polygons, 7.187 - and outlines (non-filled polygons) */ 7.188 -typedef struct { 7.189 - char header[VARHDRSZ]; /* PostgreSQL header for variable size data types */ 7.190 - int32_t nentries; /* number of stored points */ 7.191 - pgl_circle bounding; /* bounding circle */ 7.192 - /* Note: bounding circle ensures alignment of pgl_cluster for points */ 7.193 - pgl_entry entries[FLEXIBLE_ARRAY_MEMBER]; /* var-length data */ 7.194 -} pgl_cluster; 7.195 - 7.196 -/* macro to determine memory alignment of points */ 7.197 -/* (needed to store pgl_point array after entries in pgl_cluster) */ 7.198 -typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment; 7.199 -#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned) 7.200 - 7.201 -/* macro to extract a pointer to the array of points of a cluster entry */ 7.202 -#define PGL_ENTRY_POINTS(cluster, idx) \ 7.203 - ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset)) 7.204 - 7.205 -/* convert pgl_newentry array to pgl_cluster */ 7.206 -/* NOTE: requires pgl_finalize_cluster to be called to finalize result */ 7.207 -static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) { 7.208 - int i; /* index of current entry */ 7.209 - int npoints = 0; /* number of points in whole cluster */ 7.210 - int entry_npoints; /* number of points in current entry */ 7.211 - int points_offset = PGL_POINT_ALIGNMENT * ( 7.212 - ( offsetof(pgl_cluster, entries) + 7.213 - nentries * sizeof(pgl_entry) + 7.214 - PGL_POINT_ALIGNMENT - 1 7.215 - ) / PGL_POINT_ALIGNMENT 7.216 - ); /* offset of pgl_point array from base address (considering alignment) */ 7.217 - pgl_cluster *cluster; /* new cluster to be returned */ 7.218 - /* determine total number of points */ 7.219 - for (i=0; i<nentries; i++) npoints += entries[i].npoints; 7.220 - /* allocate memory for cluster (including entries and points) */ 7.221 - cluster = palloc(points_offset + npoints * sizeof(pgl_point)); 7.222 - /* re-count total number of points to determine offset for each entry */ 7.223 - npoints = 0; 7.224 - /* copy entries and points */ 7.225 - for (i=0; i<nentries; i++) { 7.226 - /* determine number of points in entry */ 7.227 - entry_npoints = entries[i].npoints; 7.228 - /* copy entry */ 7.229 - cluster->entries[i].entrytype = entries[i].entrytype; 7.230 - cluster->entries[i].npoints = entry_npoints; 7.231 - /* calculate offset (in bytes) of pgl_point array */ 7.232 - cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point); 7.233 - /* copy points */ 7.234 - memcpy( 7.235 - PGL_ENTRY_POINTS(cluster, i), 7.236 - entries[i].points, 7.237 - entry_npoints * sizeof(pgl_point) 7.238 - ); 7.239 - /* update total number of points processed */ 7.240 - npoints += entry_npoints; 7.241 - } 7.242 - /* set number of entries in cluster */ 7.243 - cluster->nentries = nentries; 7.244 - /* set PostgreSQL header for variable sized data */ 7.245 - SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point)); 7.246 - /* return newly created cluster */ 7.247 - return cluster; 7.248 -} 7.249 - 7.250 - 7.251 -/*----------------------------------------------* 7.252 - * Geographic point with integer sample count * 7.253 - * (needed for fair distance calculation) * 7.254 - *----------------------------------------------*/ 7.255 - 7.256 -typedef struct { 7.257 - pgl_point point; /* NOTE: point first to allow C cast to pgl_point */ 7.258 - int32 samples; 7.259 -} pgl_point_sc; 7.260 - 7.261 - 7.262 -/*----------------------------------------* 7.263 - * C functions on geographic data types * 7.264 - *----------------------------------------*/ 7.265 - 7.266 -/* round latitude or longitude to 12 digits after decimal point */ 7.267 -static inline double pgl_round(double val) { 7.268 - return round(val * 1e12) / 1e12; 7.269 -} 7.270 - 7.271 -/* compare two points */ 7.272 -/* (equality when same point on earth is described, otherwise an arbitrary 7.273 - linear order) */ 7.274 -static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) { 7.275 - double lon1, lon2; /* modified longitudes for special cases */ 7.276 - /* use latitude as first ordering criterion */ 7.277 - if (point1->lat < point2->lat) return -1; 7.278 - if (point1->lat > point2->lat) return 1; 7.279 - /* determine modified longitudes (considering special case of poles and 7.280 - 180th meridian which can be described as W180 or E180) */ 7.281 - if (point1->lat == -90 || point1->lat == 90) lon1 = 0; 7.282 - else if (point1->lon == 180) lon1 = -180; 7.283 - else lon1 = point1->lon; 7.284 - if (point2->lat == -90 || point2->lat == 90) lon2 = 0; 7.285 - else if (point2->lon == 180) lon2 = -180; 7.286 - else lon2 = point2->lon; 7.287 - /* use (modified) longitude as secondary ordering criterion */ 7.288 - if (lon1 < lon2) return -1; 7.289 - if (lon1 > lon2) return 1; 7.290 - /* no difference found, points are equal */ 7.291 - return 0; 7.292 -} 7.293 - 7.294 -/* compare two boxes */ 7.295 -/* (equality when same box on earth is described, otherwise an arbitrary linear 7.296 - order) */ 7.297 -static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) { 7.298 - /* two empty boxes are equal, and an empty box is always considered "less 7.299 - than" a non-empty box */ 7.300 - if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1; 7.301 - if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0; 7.302 - if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1; 7.303 - /* use southern border as first ordering criterion */ 7.304 - if (box1->lat_min < box2->lat_min) return -1; 7.305 - if (box1->lat_min > box2->lat_min) return 1; 7.306 - /* use northern border as second ordering criterion */ 7.307 - if (box1->lat_max < box2->lat_max) return -1; 7.308 - if (box1->lat_max > box2->lat_max) return 1; 7.309 - /* use western border as third ordering criterion */ 7.310 - if (box1->lon_min < box2->lon_min) return -1; 7.311 - if (box1->lon_min > box2->lon_min) return 1; 7.312 - /* use eastern border as fourth ordering criterion */ 7.313 - if (box1->lon_max < box2->lon_max) return -1; 7.314 - if (box1->lon_max > box2->lon_max) return 1; 7.315 - /* no difference found, boxes are equal */ 7.316 - return 0; 7.317 -} 7.318 - 7.319 -/* compare two circles */ 7.320 -/* (equality when same circle on earth is described, otherwise an arbitrary 7.321 - linear order) */ 7.322 -static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) { 7.323 - /* two circles with same infinite radius (positive or negative infinity) are 7.324 - considered equal independently of center point */ 7.325 - if ( 7.326 - !isfinite(circle1->radius) && !isfinite(circle2->radius) && 7.327 - circle1->radius == circle2->radius 7.328 - ) return 0; 7.329 - /* use radius as first ordering criterion */ 7.330 - if (circle1->radius < circle2->radius) return -1; 7.331 - if (circle1->radius > circle2->radius) return 1; 7.332 - /* use center point as secondary ordering criterion */ 7.333 - return pgl_point_cmp(&(circle1->center), &(circle2->center)); 7.334 -} 7.335 - 7.336 -/* set box to empty box*/ 7.337 -static void pgl_box_set_empty(pgl_box *box) { 7.338 - box->lat_min = INFINITY; 7.339 - box->lat_max = -INFINITY; 7.340 - box->lon_min = 0; 7.341 - box->lon_max = 0; 7.342 -} 7.343 - 7.344 -/* check if point is inside a box */ 7.345 -static bool pgl_point_in_box(pgl_point *point, pgl_box *box) { 7.346 - return ( 7.347 - point->lat >= box->lat_min && point->lat <= box->lat_max && ( 7.348 - (box->lon_min > box->lon_max) ? ( 7.349 - /* box crosses 180th meridian */ 7.350 - point->lon >= box->lon_min || point->lon <= box->lon_max 7.351 - ) : ( 7.352 - /* box does not cross the 180th meridian */ 7.353 - point->lon >= box->lon_min && point->lon <= box->lon_max 7.354 - ) 7.355 - ) 7.356 - ); 7.357 -} 7.358 - 7.359 -/* check if two boxes overlap */ 7.360 -static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) { 7.361 - return ( 7.362 - box2->lat_max >= box2->lat_min && /* ensure box2 is not empty */ 7.363 - ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) && 7.364 - ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && ( 7.365 - ( 7.366 - /* check if one and only one box crosses the 180th meridian */ 7.367 - ((box1->lon_min > box1->lon_max) ? 1 : 0) ^ 7.368 - ((box2->lon_min > box2->lon_max) ? 1 : 0) 7.369 - ) ? ( 7.370 - /* exactly one box crosses the 180th meridian */ 7.371 - box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min || 7.372 - box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max 7.373 - ) : ( 7.374 - /* no box or both boxes cross the 180th meridian */ 7.375 - ( 7.376 - (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) && 7.377 - (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max) 7.378 - ) || 7.379 - /* handle W180 == E180 */ 7.380 - ( box1->lon_min == -180 && box2->lon_max == 180 ) || 7.381 - ( box2->lon_min == -180 && box1->lon_max == 180 ) 7.382 - ) 7.383 - ) 7.384 - ); 7.385 -} 7.386 - 7.387 -/* check unambiguousness of east/west orientation of cluster entries and set 7.388 - bounding circle of cluster */ 7.389 -static bool pgl_finalize_cluster(pgl_cluster *cluster) { 7.390 - int i, j; /* i: index of entry, j: index of point in entry */ 7.391 - int npoints; /* number of points in entry */ 7.392 - int total_npoints = 0; /* total number of points in cluster */ 7.393 - pgl_point *points; /* points in entry */ 7.394 - int lon_dir; /* first point of entry west (-1) or east (+1) */ 7.395 - double lon_break = 0; /* antipodal longitude of first point in entry */ 7.396 - double lon_min, lon_max; /* covered longitude range of entry */ 7.397 - double value; /* temporary variable */ 7.398 - /* reset bounding circle center to empty circle at 0/0 coordinates */ 7.399 - cluster->bounding.center.lat = 0; 7.400 - cluster->bounding.center.lon = 0; 7.401 - cluster->bounding.radius = -INFINITY; 7.402 - /* if cluster is not empty */ 7.403 - if (cluster->nentries != 0) { 7.404 - /* iterate over all cluster entries and ensure they each cover a longitude 7.405 - range less than 180 degrees */ 7.406 - for (i=0; i<cluster->nentries; i++) { 7.407 - /* get properties of entry */ 7.408 - npoints = cluster->entries[i].npoints; 7.409 - points = PGL_ENTRY_POINTS(cluster, i); 7.410 - /* get longitude of first point of entry */ 7.411 - value = points[0].lon; 7.412 - /* initialize lon_min and lon_max with longitude of first point */ 7.413 - lon_min = value; 7.414 - lon_max = value; 7.415 - /* determine east/west orientation of first point and calculate antipodal 7.416 - longitude (Note: rounding required here) */ 7.417 - if (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); } 7.418 - else if (value > 0) { lon_dir = 1; lon_break = pgl_round(value - 180); } 7.419 - else lon_dir = 0; 7.420 - /* iterate over all other points in entry */ 7.421 - for (j=1; j<npoints; j++) { 7.422 - /* consider longitude wrap-around */ 7.423 - value = points[j].lon; 7.424 - if (lon_dir<0 && value>lon_break) value = pgl_round(value - 360); 7.425 - else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360); 7.426 - /* update lon_min and lon_max */ 7.427 - if (value < lon_min) lon_min = value; 7.428 - else if (value > lon_max) lon_max = value; 7.429 - /* return false if 180 degrees or more are covered */ 7.430 - if (lon_max - lon_min >= 180) return false; 7.431 - } 7.432 - } 7.433 - /* iterate over all points of all entries and calculate arbitrary center 7.434 - point for bounding circle (best if center point minimizes the radius, 7.435 - but some error is allowed here) */ 7.436 - for (i=0; i<cluster->nentries; i++) { 7.437 - /* get properties of entry */ 7.438 - npoints = cluster->entries[i].npoints; 7.439 - points = PGL_ENTRY_POINTS(cluster, i); 7.440 - /* check if first entry */ 7.441 - if (i==0) { 7.442 - /* get longitude of first point of first entry in whole cluster */ 7.443 - value = points[0].lon; 7.444 - /* initialize lon_min and lon_max with longitude of first point of 7.445 - first entry in whole cluster (used to determine if whole cluster 7.446 - covers a longitude range of 180 degrees or more) */ 7.447 - lon_min = value; 7.448 - lon_max = value; 7.449 - /* determine east/west orientation of first point and calculate 7.450 - antipodal longitude (Note: rounding not necessary here) */ 7.451 - if (value < 0) { lon_dir = -1; lon_break = value + 180; } 7.452 - else if (value > 0) { lon_dir = 1; lon_break = value - 180; } 7.453 - else lon_dir = 0; 7.454 - } 7.455 - /* iterate over all points in entry */ 7.456 - for (j=0; j<npoints; j++) { 7.457 - /* longitude wrap-around (Note: rounding not necessary here) */ 7.458 - value = points[j].lon; 7.459 - if (lon_dir < 0 && value > lon_break) value -= 360; 7.460 - else if (lon_dir > 0 && value < lon_break) value += 360; 7.461 - if (value < lon_min) lon_min = value; 7.462 - else if (value > lon_max) lon_max = value; 7.463 - /* set bounding circle to cover whole earth if 180 degrees or more are 7.464 - covered */ 7.465 - if (lon_max - lon_min >= 180) { 7.466 - cluster->bounding.center.lat = 0; 7.467 - cluster->bounding.center.lon = 0; 7.468 - cluster->bounding.radius = INFINITY; 7.469 - return true; 7.470 - } 7.471 - /* add point to bounding circle center (for average calculation) */ 7.472 - cluster->bounding.center.lat += points[j].lat; 7.473 - cluster->bounding.center.lon += value; 7.474 - } 7.475 - /* count total number of points */ 7.476 - total_npoints += npoints; 7.477 - } 7.478 - /* determine average latitude and longitude of cluster */ 7.479 - cluster->bounding.center.lat /= total_npoints; 7.480 - cluster->bounding.center.lon /= total_npoints; 7.481 - /* normalize longitude of center of cluster bounding circle */ 7.482 - if (cluster->bounding.center.lon < -180) { 7.483 - cluster->bounding.center.lon += 360; 7.484 - } 7.485 - else if (cluster->bounding.center.lon > 180) { 7.486 - cluster->bounding.center.lon -= 360; 7.487 - } 7.488 - /* round bounding circle center (useful if it is used by other functions) */ 7.489 - cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat); 7.490 - cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon); 7.491 - /* calculate radius of bounding circle */ 7.492 - for (i=0; i<cluster->nentries; i++) { 7.493 - npoints = cluster->entries[i].npoints; 7.494 - points = PGL_ENTRY_POINTS(cluster, i); 7.495 - for (j=0; j<npoints; j++) { 7.496 - value = pgl_distance( 7.497 - cluster->bounding.center.lat, cluster->bounding.center.lon, 7.498 - points[j].lat, points[j].lon 7.499 - ); 7.500 - if (value > cluster->bounding.radius) cluster->bounding.radius = value; 7.501 - } 7.502 - } 7.503 - } 7.504 - /* return true (east/west orientation is unambiguous) */ 7.505 - return true; 7.506 -} 7.507 - 7.508 -/* check if point is inside cluster */ 7.509 -/* (if point is on perimeter, then true is returned if and only if 7.510 - strict == false) */ 7.511 -static bool pgl_point_in_cluster( 7.512 - pgl_point *point, 7.513 - pgl_cluster *cluster, 7.514 - bool strict 7.515 -) { 7.516 - int i, j, k; /* i: entry, j: point in entry, k: next point in entry */ 7.517 - int entrytype; /* type of entry */ 7.518 - int npoints; /* number of points in entry */ 7.519 - pgl_point *points; /* array of points in entry */ 7.520 - int lon_dir = 0; /* first vertex west (-1) or east (+1) */ 7.521 - double lon_break = 0; /* antipodal longitude of first vertex */ 7.522 - double lat0 = point->lat; /* latitude of point */ 7.523 - double lon0; /* (adjusted) longitude of point */ 7.524 - double lat1, lon1; /* latitude and (adjusted) longitude of vertex */ 7.525 - double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */ 7.526 - double lon; /* longitude of intersection */ 7.527 - int counter = 0; /* counter for intersections east of point */ 7.528 - /* iterate over all entries */ 7.529 - for (i=0; i<cluster->nentries; i++) { 7.530 - /* get type of entry */ 7.531 - entrytype = cluster->entries[i].entrytype; 7.532 - /* skip all entries but polygons if perimeters are excluded */ 7.533 - if (strict && entrytype != PGL_ENTRY_POLYGON) continue; 7.534 - /* get points of entry */ 7.535 - npoints = cluster->entries[i].npoints; 7.536 - points = PGL_ENTRY_POINTS(cluster, i); 7.537 - /* determine east/west orientation of first point of entry and calculate 7.538 - antipodal longitude */ 7.539 - lon_break = points[0].lon; 7.540 - if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 7.541 - else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 7.542 - else lon_dir = 0; 7.543 - /* get longitude of point */ 7.544 - lon0 = point->lon; 7.545 - /* consider longitude wrap-around for point */ 7.546 - if (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360); 7.547 - else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360); 7.548 - /* iterate over all edges and vertices */ 7.549 - for (j=0; j<npoints; j++) { 7.550 - /* return if point is on vertex of polygon */ 7.551 - if (pgl_point_cmp(point, &(points[j])) == 0) return !strict; 7.552 - /* calculate index of next vertex */ 7.553 - k = (j+1) % npoints; 7.554 - /* skip last edge unless entry is (closed) outline or polygon */ 7.555 - if ( 7.556 - k == 0 && 7.557 - entrytype != PGL_ENTRY_OUTLINE && 7.558 - entrytype != PGL_ENTRY_POLYGON 7.559 - ) continue; 7.560 - /* use previously calculated values for lat1 and lon1 if possible */ 7.561 - if (j) { 7.562 - lat1 = lat2; 7.563 - lon1 = lon2; 7.564 - } else { 7.565 - /* otherwise get latitude and longitude values of first vertex */ 7.566 - lat1 = points[0].lat; 7.567 - lon1 = points[0].lon; 7.568 - /* and consider longitude wrap-around for first vertex */ 7.569 - if (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360); 7.570 - else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360); 7.571 - } 7.572 - /* get latitude and longitude of next vertex */ 7.573 - lat2 = points[k].lat; 7.574 - lon2 = points[k].lon; 7.575 - /* consider longitude wrap-around for next vertex */ 7.576 - if (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360); 7.577 - else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360); 7.578 - /* return if point is on horizontal (west to east) edge of polygon */ 7.579 - if ( 7.580 - lat0 == lat1 && lat0 == lat2 && 7.581 - ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) ) 7.582 - ) return !strict; 7.583 - /* check if edge crosses east/west line of point */ 7.584 - if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) { 7.585 - /* calculate longitude of intersection */ 7.586 - lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1); 7.587 - /* return if intersection goes (approximately) through point */ 7.588 - if (pgl_round(lon) == lon0) return !strict; 7.589 - /* count intersection if east of point and entry is polygon*/ 7.590 - if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++; 7.591 - } 7.592 - } 7.593 - } 7.594 - /* return true if number of intersections is odd */ 7.595 - return counter & 1; 7.596 -} 7.597 - 7.598 -/* check if all points of the second cluster are strictly inside the first 7.599 - cluster */ 7.600 -static inline bool pgl_all_cluster_points_strictly_in_cluster( 7.601 - pgl_cluster *outer, pgl_cluster *inner 7.602 -) { 7.603 - int i, j; /* i: entry, j: point in entry */ 7.604 - int npoints; /* number of points in entry */ 7.605 - pgl_point *points; /* array of points in entry */ 7.606 - /* iterate over all entries of "inner" cluster */ 7.607 - for (i=0; i<inner->nentries; i++) { 7.608 - /* get properties of entry */ 7.609 - npoints = inner->entries[i].npoints; 7.610 - points = PGL_ENTRY_POINTS(inner, i); 7.611 - /* iterate over all points in entry of "inner" cluster */ 7.612 - for (j=0; j<npoints; j++) { 7.613 - /* return false if one point of inner cluster is not in outer cluster */ 7.614 - if (!pgl_point_in_cluster(points+j, outer, true)) return false; 7.615 - } 7.616 - } 7.617 - /* otherwise return true */ 7.618 - return true; 7.619 -} 7.620 - 7.621 -/* check if any point the second cluster is inside the first cluster */ 7.622 -static inline bool pgl_any_cluster_points_in_cluster( 7.623 - pgl_cluster *outer, pgl_cluster *inner 7.624 -) { 7.625 - int i, j; /* i: entry, j: point in entry */ 7.626 - int npoints; /* number of points in entry */ 7.627 - pgl_point *points; /* array of points in entry */ 7.628 - /* iterate over all entries of "inner" cluster */ 7.629 - for (i=0; i<inner->nentries; i++) { 7.630 - /* get properties of entry */ 7.631 - npoints = inner->entries[i].npoints; 7.632 - points = PGL_ENTRY_POINTS(inner, i); 7.633 - /* iterate over all points in entry of "inner" cluster */ 7.634 - for (j=0; j<npoints; j++) { 7.635 - /* return true if one point of inner cluster is in outer cluster */ 7.636 - if (pgl_point_in_cluster(points+j, outer, false)) return true; 7.637 - } 7.638 - } 7.639 - /* otherwise return false */ 7.640 - return false; 7.641 -} 7.642 - 7.643 -/* check if line segment strictly crosses line (not just touching) */ 7.644 -static inline bool pgl_lseg_crosses_line( 7.645 - double seg_x1, double seg_y1, double seg_x2, double seg_y2, 7.646 - double line_x1, double line_y1, double line_x2, double line_y2 7.647 -) { 7.648 - return ( 7.649 - ( 7.650 - (seg_x1-line_x1) * (line_y2-line_y1) - 7.651 - (seg_y1-line_y1) * (line_x2-line_x1) 7.652 - ) * ( 7.653 - (seg_x2-line_x1) * (line_y2-line_y1) - 7.654 - (seg_y2-line_y1) * (line_x2-line_x1) 7.655 - ) 7.656 - ) < 0; 7.657 -} 7.658 - 7.659 -/* check if paths and outlines of two clusters strictly overlap (not just 7.660 - touching) */ 7.661 -static bool pgl_outlines_overlap( 7.662 - pgl_cluster *cluster1, pgl_cluster *cluster2 7.663 -) { 7.664 - int i1, j1, k1; /* i: entry, j: point in entry, k: next point in entry */ 7.665 - int i2, j2, k2; 7.666 - int entrytype1, entrytype2; /* type of entry */ 7.667 - int npoints1, npoints2; /* number of points in entry */ 7.668 - pgl_point *points1; /* array of points in entry of cluster1 */ 7.669 - pgl_point *points2; /* array of points in entry of cluster2 */ 7.670 - int lon_dir1, lon_dir2; /* first vertex west (-1) or east (+1) */ 7.671 - double lon_break1, lon_break2; /* antipodal longitude of first vertex */ 7.672 - double lat11, lon11; /* latitude and (adjusted) longitude of vertex */ 7.673 - double lat12, lon12; /* latitude and (adjusted) longitude of next vertex */ 7.674 - double lat21, lon21; /* latitude and (adjusted) longitudes for cluster2 */ 7.675 - double lat22, lon22; 7.676 - double wrapvalue; /* temporary helper value to adjust wrap-around */ 7.677 - /* iterate over all entries of cluster1 */ 7.678 - for (i1=0; i1<cluster1->nentries; i1++) { 7.679 - /* get properties of entry in cluster1 and skip points */ 7.680 - npoints1 = cluster1->entries[i1].npoints; 7.681 - if (npoints1 < 2) continue; 7.682 - entrytype1 = cluster1->entries[i1].entrytype; 7.683 - points1 = PGL_ENTRY_POINTS(cluster1, i1); 7.684 - /* determine east/west orientation of first point and calculate antipodal 7.685 - longitude */ 7.686 - lon_break1 = points1[0].lon; 7.687 - if (lon_break1 < 0) { 7.688 - lon_dir1 = -1; 7.689 - lon_break1 = pgl_round(lon_break1 + 180); 7.690 - } else if (lon_break1 > 0) { 7.691 - lon_dir1 = 1; 7.692 - lon_break1 = pgl_round(lon_break1 - 180); 7.693 - } else lon_dir1 = 0; 7.694 - /* iterate over all edges and vertices in cluster1 */ 7.695 - for (j1=0; j1<npoints1; j1++) { 7.696 - /* calculate index of next vertex */ 7.697 - k1 = (j1+1) % npoints1; 7.698 - /* skip last edge unless entry is (closed) outline or polygon */ 7.699 - if ( 7.700 - k1 == 0 && 7.701 - entrytype1 != PGL_ENTRY_OUTLINE && 7.702 - entrytype1 != PGL_ENTRY_POLYGON 7.703 - ) continue; 7.704 - /* use previously calculated values for lat1 and lon1 if possible */ 7.705 - if (j1) { 7.706 - lat11 = lat12; 7.707 - lon11 = lon12; 7.708 - } else { 7.709 - /* otherwise get latitude and longitude values of first vertex */ 7.710 - lat11 = points1[0].lat; 7.711 - lon11 = points1[0].lon; 7.712 - /* and consider longitude wrap-around for first vertex */ 7.713 - if (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360); 7.714 - else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360); 7.715 - } 7.716 - /* get latitude and longitude of next vertex */ 7.717 - lat12 = points1[k1].lat; 7.718 - lon12 = points1[k1].lon; 7.719 - /* consider longitude wrap-around for next vertex */ 7.720 - if (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360); 7.721 - else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360); 7.722 - /* skip degenerated edges */ 7.723 - if (lat11 == lat12 && lon11 == lon12) continue; 7.724 - /* iterate over all entries of cluster2 */ 7.725 - for (i2=0; i2<cluster2->nentries; i2++) { 7.726 - /* get points and number of points of entry in cluster2 */ 7.727 - npoints2 = cluster2->entries[i2].npoints; 7.728 - if (npoints2 < 2) continue; 7.729 - entrytype2 = cluster2->entries[i2].entrytype; 7.730 - points2 = PGL_ENTRY_POINTS(cluster2, i2); 7.731 - /* determine east/west orientation of first point and calculate antipodal 7.732 - longitude */ 7.733 - lon_break2 = points2[0].lon; 7.734 - if (lon_break2 < 0) { 7.735 - lon_dir2 = -1; 7.736 - lon_break2 = pgl_round(lon_break2 + 180); 7.737 - } else if (lon_break2 > 0) { 7.738 - lon_dir2 = 1; 7.739 - lon_break2 = pgl_round(lon_break2 - 180); 7.740 - } else lon_dir2 = 0; 7.741 - /* iterate over all edges and vertices in cluster2 */ 7.742 - for (j2=0; j2<npoints2; j2++) { 7.743 - /* calculate index of next vertex */ 7.744 - k2 = (j2+1) % npoints2; 7.745 - /* skip last edge unless entry is (closed) outline or polygon */ 7.746 - if ( 7.747 - k2 == 0 && 7.748 - entrytype2 != PGL_ENTRY_OUTLINE && 7.749 - entrytype2 != PGL_ENTRY_POLYGON 7.750 - ) continue; 7.751 - /* use previously calculated values for lat1 and lon1 if possible */ 7.752 - if (j2) { 7.753 - lat21 = lat22; 7.754 - lon21 = lon22; 7.755 - } else { 7.756 - /* otherwise get latitude and longitude values of first vertex */ 7.757 - lat21 = points2[0].lat; 7.758 - lon21 = points2[0].lon; 7.759 - /* and consider longitude wrap-around for first vertex */ 7.760 - if (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360); 7.761 - else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360); 7.762 - } 7.763 - /* get latitude and longitude of next vertex */ 7.764 - lat22 = points2[k2].lat; 7.765 - lon22 = points2[k2].lon; 7.766 - /* consider longitude wrap-around for next vertex */ 7.767 - if (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360); 7.768 - else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360); 7.769 - /* skip degenerated edges */ 7.770 - if (lat21 == lat22 && lon21 == lon22) continue; 7.771 - /* perform another wrap-around where necessary */ 7.772 - /* TODO: improve performance of whole wrap-around mechanism */ 7.773 - wrapvalue = (lon21 + lon22) - (lon11 + lon12); 7.774 - if (wrapvalue > 360) { 7.775 - lon21 = pgl_round(lon21 - 360); 7.776 - lon22 = pgl_round(lon22 - 360); 7.777 - } else if (wrapvalue < -360) { 7.778 - lon21 = pgl_round(lon21 + 360); 7.779 - lon22 = pgl_round(lon22 + 360); 7.780 - } 7.781 - /* return true if segments overlap */ 7.782 - if ( 7.783 - pgl_lseg_crosses_line( 7.784 - lat11, lon11, lat12, lon12, 7.785 - lat21, lon21, lat22, lon22 7.786 - ) && pgl_lseg_crosses_line( 7.787 - lat21, lon21, lat22, lon22, 7.788 - lat11, lon11, lat12, lon12 7.789 - ) 7.790 - ) { 7.791 - return true; 7.792 - } 7.793 - } 7.794 - } 7.795 - } 7.796 - } 7.797 - /* otherwise return false */ 7.798 - return false; 7.799 -} 7.800 - 7.801 -/* check if second cluster is completely contained in first cluster */ 7.802 -static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) { 7.803 - if (!pgl_all_cluster_points_strictly_in_cluster(outer, inner)) return false; 7.804 - if (pgl_any_cluster_points_in_cluster(inner, outer)) return false; 7.805 - if (pgl_outlines_overlap(outer, inner)) return false; 7.806 - return true; 7.807 -} 7.808 - 7.809 -/* check if two clusters overlap */ 7.810 -static bool pgl_clusters_overlap( 7.811 - pgl_cluster *cluster1, pgl_cluster *cluster2 7.812 -) { 7.813 - if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true; 7.814 - if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true; 7.815 - if (pgl_outlines_overlap(cluster1, cluster2)) return true; 7.816 - return false; 7.817 -} 7.818 - 7.819 -/* calculate (approximate) distance between point and cluster */ 7.820 -static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) { 7.821 - double comp; /* square of compression of meridians */ 7.822 - int i, j, k; /* i: entry, j: point in entry, k: next point in entry */ 7.823 - int entrytype; /* type of entry */ 7.824 - int npoints; /* number of points in entry */ 7.825 - pgl_point *points; /* array of points in entry */ 7.826 - int lon_dir = 0; /* first vertex west (-1) or east (+1) */ 7.827 - double lon_break = 0; /* antipodal longitude of first vertex */ 7.828 - double lon_min = 0; /* minimum (adjusted) longitude of entry vertices */ 7.829 - double lon_max = 0; /* maximum (adjusted) longitude of entry vertices */ 7.830 - double lat0 = point->lat; /* latitude of point */ 7.831 - double lon0; /* (adjusted) longitude of point */ 7.832 - double lat1, lon1; /* latitude and (adjusted) longitude of vertex */ 7.833 - double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */ 7.834 - double s; /* scalar for vector calculations */ 7.835 - double dist; /* distance calculated in one step */ 7.836 - double min_dist = INFINITY; /* minimum distance */ 7.837 - /* distance is zero if point is contained in cluster */ 7.838 - if (pgl_point_in_cluster(point, cluster, false)) return 0; 7.839 - /* calculate approximate square compression of meridians */ 7.840 - comp = cos((lat0 / 180.0) * M_PI); 7.841 - comp *= comp; 7.842 - /* calculate exact square compression of meridians */ 7.843 - comp *= ( 7.844 - (1.0 - PGL_EPS2 * (1.0-comp)) * 7.845 - (1.0 - PGL_EPS2 * (1.0-comp)) / 7.846 - (PGL_SUBEPS2 * PGL_SUBEPS2) 7.847 - ); 7.848 - /* iterate over all entries */ 7.849 - for (i=0; i<cluster->nentries; i++) { 7.850 - /* get properties of entry */ 7.851 - entrytype = cluster->entries[i].entrytype; 7.852 - npoints = cluster->entries[i].npoints; 7.853 - points = PGL_ENTRY_POINTS(cluster, i); 7.854 - /* determine east/west orientation of first point of entry and calculate 7.855 - antipodal longitude */ 7.856 - lon_break = points[0].lon; 7.857 - if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 7.858 - else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 7.859 - else lon_dir = 0; 7.860 - /* determine covered longitude range */ 7.861 - for (j=0; j<npoints; j++) { 7.862 - /* get longitude of vertex */ 7.863 - lon1 = points[j].lon; 7.864 - /* adjust longitude to fix potential wrap-around */ 7.865 - if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360; 7.866 - else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360; 7.867 - /* update minimum and maximum longitude of polygon */ 7.868 - if (j == 0 || lon1 < lon_min) lon_min = lon1; 7.869 - if (j == 0 || lon1 > lon_max) lon_max = lon1; 7.870 - } 7.871 - /* adjust longitude wrap-around according to full longitude range */ 7.872 - lon_break = (lon_max + lon_min) / 2; 7.873 - if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 7.874 - else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 7.875 - /* get longitude of point */ 7.876 - lon0 = point->lon; 7.877 - /* consider longitude wrap-around for point */ 7.878 - if (lon_dir < 0 && lon0 > lon_break) lon0 -= 360; 7.879 - else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360; 7.880 - /* iterate over all edges and vertices */ 7.881 - for (j=0; j<npoints; j++) { 7.882 - /* use previously calculated values for lat1 and lon1 if possible */ 7.883 - if (j) { 7.884 - lat1 = lat2; 7.885 - lon1 = lon2; 7.886 - } else { 7.887 - /* otherwise get latitude and longitude values of first vertex */ 7.888 - lat1 = points[0].lat; 7.889 - lon1 = points[0].lon; 7.890 - /* and consider longitude wrap-around for first vertex */ 7.891 - if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360; 7.892 - else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360; 7.893 - } 7.894 - /* calculate distance to vertex */ 7.895 - dist = pgl_distance(lat0, lon0, lat1, lon1); 7.896 - /* store calculated distance if smallest */ 7.897 - if (dist < min_dist) min_dist = dist; 7.898 - /* calculate index of next vertex */ 7.899 - k = (j+1) % npoints; 7.900 - /* skip last edge unless entry is (closed) outline or polygon */ 7.901 - if ( 7.902 - k == 0 && 7.903 - entrytype != PGL_ENTRY_OUTLINE && 7.904 - entrytype != PGL_ENTRY_POLYGON 7.905 - ) continue; 7.906 - /* get latitude and longitude of next vertex */ 7.907 - lat2 = points[k].lat; 7.908 - lon2 = points[k].lon; 7.909 - /* consider longitude wrap-around for next vertex */ 7.910 - if (lon_dir < 0 && lon2 > lon_break) lon2 -= 360; 7.911 - else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360; 7.912 - /* go to next vertex and edge if edge is degenerated */ 7.913 - if (lat1 == lat2 && lon1 == lon2) continue; 7.914 - /* otherwise test if point can be projected onto edge of polygon */ 7.915 - s = ( 7.916 - ((lat0-lat1) * (lat2-lat1) + comp * (lon0-lon1) * (lon2-lon1)) / 7.917 - ((lat2-lat1) * (lat2-lat1) + comp * (lon2-lon1) * (lon2-lon1)) 7.918 - ); 7.919 - /* go to next vertex and edge if point cannot be projected */ 7.920 - if (!(s > 0 && s < 1)) continue; 7.921 - /* calculate distance from original point to projected point */ 7.922 - dist = pgl_distance( 7.923 - lat0, lon0, 7.924 - lat1 + s * (lat2-lat1), 7.925 - lon1 + s * (lon2-lon1) 7.926 - ); 7.927 - /* store calculated distance if smallest */ 7.928 - if (dist < min_dist) min_dist = dist; 7.929 - } 7.930 - } 7.931 - /* return minimum distance */ 7.932 - return min_dist; 7.933 -} 7.934 - 7.935 -/* calculate (approximate) distance between two clusters */ 7.936 -static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) { 7.937 - int i, j; /* i: entry, j: point in entry */ 7.938 - int npoints; /* number of points in entry */ 7.939 - pgl_point *points; /* array of points in entry */ 7.940 - double dist; /* distance calculated in one step */ 7.941 - double min_dist = INFINITY; /* minimum distance */ 7.942 - /* consider distance from each point in one cluster to the whole other */ 7.943 - for (i=0; i<cluster1->nentries; i++) { 7.944 - npoints = cluster1->entries[i].npoints; 7.945 - points = PGL_ENTRY_POINTS(cluster1, i); 7.946 - for (j=0; j<npoints; j++) { 7.947 - dist = pgl_point_cluster_distance(points+j, cluster2); 7.948 - if (dist == 0) return dist; 7.949 - if (dist < min_dist) min_dist = dist; 7.950 - } 7.951 - } 7.952 - /* consider distance from each point in other cluster to the first cluster */ 7.953 - for (i=0; i<cluster2->nentries; i++) { 7.954 - npoints = cluster2->entries[i].npoints; 7.955 - points = PGL_ENTRY_POINTS(cluster2, i); 7.956 - for (j=0; j<npoints; j++) { 7.957 - dist = pgl_point_cluster_distance(points+j, cluster1); 7.958 - if (dist == 0) return dist; 7.959 - if (dist < min_dist) min_dist = dist; 7.960 - } 7.961 - } 7.962 - return min_dist; 7.963 -} 7.964 - 7.965 -/* estimator function for distance between box and point */ 7.966 -/* always returns a smaller value than actually correct or zero */ 7.967 -static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) { 7.968 - double dlon; /* longitude range of box (delta longitude) */ 7.969 - double distance; /* return value */ 7.970 - /* return infinity if box is empty */ 7.971 - if (box->lat_min > box->lat_max) return INFINITY; 7.972 - /* return zero if point is inside box */ 7.973 - if (pgl_point_in_box(point, box)) return 0; 7.974 - /* calculate delta longitude */ 7.975 - dlon = box->lon_max - box->lon_min; 7.976 - if (dlon < 0) dlon += 360; /* 180th meridian crossed */ 7.977 - /* if delta longitude is greater than 150 degrees, perform safe fall-back */ 7.978 - if (dlon > 150) return 0; 7.979 - /* calculate lower limit for distance (formula below requires dlon <= 150) */ 7.980 - /* TODO: provide better estimation function to improve performance */ 7.981 - distance = ( 7.982 - (1.0-PGL_SPHEROID_F) * /* safety margin due to flattening and approx. */ 7.983 - pgl_distance( 7.984 - point->lat, 7.985 - point->lon, 7.986 - (box->lat_min + box->lat_max) / 2, 7.987 - box->lon_min + dlon/2 7.988 - ) 7.989 - ) - pgl_distance( 7.990 - box->lat_min, box->lon_min, 7.991 - box->lat_max, box->lon_max 7.992 - ); 7.993 - /* truncate negative results to zero */ 7.994 - if (distance <= 0) distance = 0; 7.995 - /* return result */ 7.996 - return distance; 7.997 -} 7.998 - 7.999 - 7.1000 -/*------------------------------------------------------------* 7.1001 - * Functions using numerical integration (Monte Carlo like) * 7.1002 - *------------------------------------------------------------*/ 7.1003 - 7.1004 -/* half of (spherical) earth's surface area */ 7.1005 -#define PGL_HALF_SURFACE (PGL_RADIUS * PGL_DIAMETER * M_PI) 7.1006 - 7.1007 -/* golden angle in radians */ 7.1008 -#define PGL_GOLDEN_ANGLE (M_PI * (sqrt(5) - 1.0)) 7.1009 - 7.1010 -/* create a list of sample points covering a bounding circle 7.1011 - and return covered area */ 7.1012 -static double pgl_sample_points( 7.1013 - pgl_point *center, /* center of bounding circle */ 7.1014 - double radius, /* radius of bounding circle */ 7.1015 - int samples, /* number of sample points (MUST be positive!) */ 7.1016 - pgl_point *result /* pointer to result array */ 7.1017 -) { 7.1018 - double double_share = 2.0; /* double of covered share of earth's surface */ 7.1019 - double double_share_div_samples; /* double_share divided by sample count */ 7.1020 - int i; 7.1021 - double t; /* parameter of spiral laid on (spherical) earth's surface */ 7.1022 - double x, y, z; /* normalized coordinates of point on non-rotated spiral */ 7.1023 - double sin_phi; /* sine of sph. coordinate of point of non-rotated spiral */ 7.1024 - double lambda; /* other sph. coordinate of point of non-rotated spiral */ 7.1025 - double rot = (0.5 - center->lat / 180.0) * M_PI; /* needed rot. (in rad) */ 7.1026 - double cos_rot = cos(rot); /* cosine of rotation by latitude */ 7.1027 - double sin_rot = sin(rot); /* sine of rotation by latitude */ 7.1028 - double x_rot, z_rot; /* normalized coordinates of point on rotated spiral */ 7.1029 - double center_lon = center->lon; /* second rotation in degree */ 7.1030 - /* add safety margin to bounding circle because of spherical approximation */ 7.1031 - radius *= PGL_SPHEROID_A / PGL_RADIUS; 7.1032 - /* if whole earth is covered, use initialized value, otherwise calculate 7.1033 - share of covered area (multiplied by 2) */ 7.1034 - if (radius < PGL_MAXDIST) double_share = 1.0 - cos(radius / PGL_RADIUS); 7.1035 - /* divide double_share by sample count for later calculations */ 7.1036 - double_share_div_samples = double_share / samples; 7.1037 - /* generate sample points */ 7.1038 - for (i=0; i<samples; i++) { 7.1039 - /* use an offset of 1/2 to avoid too dense clustering at spiral center */ 7.1040 - t = 0.5 + i; 7.1041 - /* calculate normalized coordinates of point on non-rotated spiral */ 7.1042 - z = 1.0 - double_share_div_samples * t; 7.1043 - sin_phi = sqrt(1.0 - z*z); 7.1044 - lambda = t * PGL_GOLDEN_ANGLE; 7.1045 - x = sin_phi * cos(lambda); 7.1046 - y = sin_phi * sin(lambda); 7.1047 - /* rotate spiral by latitude value of bounding circle */ 7.1048 - x_rot = cos_rot * x + sin_rot * z; 7.1049 - z_rot = cos_rot * z - sin_rot * x; 7.1050 - /* set resulting sample point in result array */ 7.1051 - /* (while performing second rotation by bounding circle longitude) */ 7.1052 - result[i].lat = 180.0 * (atan(z_rot / fabs(x_rot)) / M_PI); 7.1053 - result[i].lon = center_lon + 180.0 * (atan2(y, x_rot) / M_PI); 7.1054 - } 7.1055 - /* return covered area */ 7.1056 - return PGL_HALF_SURFACE * double_share; 7.1057 -} 7.1058 - 7.1059 -/* fair distance between point and cluster (see README file for explanation) */ 7.1060 -/* NOTE: sample count passed as third argument MUST be positive */ 7.1061 -static double pgl_fair_distance( 7.1062 - pgl_point *point, pgl_cluster *cluster, int samples 7.1063 -) { 7.1064 - double distance; /* shortest distance from point to cluster */ 7.1065 - pgl_point *points; /* sample points for numerical integration */ 7.1066 - double area; /* area covered by sample points */ 7.1067 - int i; 7.1068 - int inner = 0; /* number of sample points within cluster */ 7.1069 - int outer = 0; /* number of sample points outside cluster but 7.1070 - within cluster enlarged by distance */ 7.1071 - double result; 7.1072 - /* calculate shortest distance from point to cluster */ 7.1073 - distance = pgl_point_cluster_distance(point, cluster); 7.1074 - /* if cluster consists of a single point or has no bounding circle with 7.1075 - positive radius, simply return distance */ 7.1076 - if ( 7.1077 - (cluster->nentries==1 && cluster->entries[0].entrytype==PGL_ENTRY_POINT) || 7.1078 - !(cluster->bounding.radius > 0) 7.1079 - ) return distance; 7.1080 - /* if cluster consists of two points which are twice as far apart, return 7.1081 - distance between point and cluster multiplied by square root of two */ 7.1082 - if ( 7.1083 - cluster->nentries == 2 && 7.1084 - cluster->entries[0].entrytype == PGL_ENTRY_POINT && 7.1085 - cluster->entries[1].entrytype == PGL_ENTRY_POINT && 7.1086 - pgl_distance( 7.1087 - PGL_ENTRY_POINTS(cluster, 0)[0].lat, 7.1088 - PGL_ENTRY_POINTS(cluster, 0)[0].lon, 7.1089 - PGL_ENTRY_POINTS(cluster, 1)[0].lat, 7.1090 - PGL_ENTRY_POINTS(cluster, 1)[0].lon 7.1091 - ) >= 2.0 * distance 7.1092 - ) { 7.1093 - return distance * M_SQRT2; 7.1094 - } 7.1095 - /* otherwise create sample points for numerical integration and determine 7.1096 - area covered by sample points */ 7.1097 - points = palloc(samples * sizeof(pgl_point)); 7.1098 - area = pgl_sample_points( 7.1099 - &cluster->bounding.center, 7.1100 - cluster->bounding.radius + distance, /* pad bounding circle by distance */ 7.1101 - samples, 7.1102 - points 7.1103 - ); 7.1104 - /* perform numerical integration */ 7.1105 - if (distance > 0) { 7.1106 - /* point (that was passed as argument) is outside cluster */ 7.1107 - for (i=0; i<samples; i++) { 7.1108 - /* count sample points within cluster */ 7.1109 - if (pgl_point_in_cluster(points+i, cluster, true)) inner++; 7.1110 - /* count sample points outside of cluster but within cluster enlarged by 7.1111 - distance between point (that was passed as argument) and cluster */ 7.1112 - else if ( 7.1113 - pgl_point_cluster_distance(points+i, cluster) < distance 7.1114 - ) outer++; 7.1115 - } 7.1116 - } else { 7.1117 - /* if point is within cluster, just count sample points within cluster */ 7.1118 - for (i=0; i<samples; i++) { 7.1119 - if (pgl_point_in_cluster(points+i, cluster, true)) inner++; 7.1120 - } 7.1121 - } 7.1122 - /* release memory for sample points needed for numerical integration */ 7.1123 - pfree(points); 7.1124 - /* if enlargement was less than doubling the area, then combine inner and 7.1125 - outer sample point counts with different weighting */ 7.1126 - /* (ensures fairness in such a way that the integral of the squared result 7.1127 - over all possible point parameters is independent of the cluster) */ 7.1128 - if (outer < inner) result = (2*inner + 4*outer) / 3.0; 7.1129 - /* otherwise weigh inner and outer points the same */ 7.1130 - else result = inner + outer; 7.1131 - /* convert area into distance (i.e. radius of a circle with the same area) */ 7.1132 - result = sqrt(area * (result / samples) / M_PI); 7.1133 - /* return result only if it is greater than the distance between point and 7.1134 - cluster to avoid unexpected results because of errors due to limited 7.1135 - precision */ 7.1136 - if (result > distance) return result; 7.1137 - /* otherwise return distance between point and cluster */ 7.1138 - else return distance; 7.1139 -} 7.1140 - 7.1141 - 7.1142 -/*-------------------------------------------------* 7.1143 - * geographic index based on space-filling curve * 7.1144 - *-------------------------------------------------*/ 7.1145 - 7.1146 -/* number of bytes used for geographic (center) position in keys */ 7.1147 -#define PGL_KEY_LATLON_BYTELEN 7 7.1148 - 7.1149 -/* maximum reference value for logarithmic size of geographic objects */ 7.1150 -#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0) /* can be tweaked */ 7.1151 - 7.1152 -/* pointer to index key (either pgl_pointkey or pgl_areakey) */ 7.1153 -typedef unsigned char *pgl_keyptr; 7.1154 - 7.1155 -/* index key for points (objects with zero area) on the spheroid */ 7.1156 -/* bit 0..55: interspersed bits of latitude and longitude, 7.1157 - bit 56..57: always zero, 7.1158 - bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */ 7.1159 -typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1]; 7.1160 - 7.1161 -/* index key for geographic objects on spheroid with area greater than zero */ 7.1162 -/* bit 0..55: interspersed bits of latitude and longitude of center point, 7.1163 - bit 56: always set to 1, 7.1164 - bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.), 7.1165 - bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to 7.1166 - PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth 7.1167 - = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL 7.1168 - (with interspersed bits = 0 and node depth = 0) for keys which 7.1169 - cover both empty and non-empty objects */ 7.1170 - 7.1171 -typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2]; 7.1172 - 7.1173 -/* helper macros for reading/writing index keys */ 7.1174 -#define PGL_KEY_NODEDEPTH_OFFSET PGL_KEY_LATLON_BYTELEN 7.1175 -#define PGL_KEY_OBJSIZE_OFFSET (PGL_KEY_NODEDEPTH_OFFSET+1) 7.1176 -#define PGL_POINTKEY_MAXDEPTH (PGL_KEY_LATLON_BYTELEN*8) 7.1177 -#define PGL_AREAKEY_MAXDEPTH (2*PGL_POINTKEY_MAXDEPTH+1) 7.1178 -#define PGL_AREAKEY_MAXOBJSIZE (PGL_POINTKEY_MAXDEPTH+1) 7.1179 -#define PGL_AREAKEY_TYPEMASK 0x80 7.1180 -#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8))) 7.1181 -#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \ 7.1182 - ( PGL_KEY_LATLONBIT(key1, n) ^ \ 7.1183 - PGL_KEY_LATLONBIT(key2, n) ) 7.1184 -#define PGL_KEY_IS_AREAKEY(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \ 7.1185 - PGL_AREAKEY_TYPEMASK) 7.1186 -#define PGL_KEY_NODEDEPTH(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \ 7.1187 - (PGL_AREAKEY_TYPEMASK-1)) 7.1188 -#define PGL_KEY_OBJSIZE(key) ((key)[PGL_KEY_OBJSIZE_OFFSET]) 7.1189 -#define PGL_KEY_OBJSIZE_EMPTY 126 7.1190 -#define PGL_KEY_OBJSIZE_UNIVERSAL 127 7.1191 -#define PGL_KEY_IS_EMPTY(key) ( PGL_KEY_IS_AREAKEY(key) && \ 7.1192 - (key)[PGL_KEY_OBJSIZE_OFFSET] == \ 7.1193 - PGL_KEY_OBJSIZE_EMPTY ) 7.1194 -#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \ 7.1195 - (key)[PGL_KEY_OBJSIZE_OFFSET] == \ 7.1196 - PGL_KEY_OBJSIZE_UNIVERSAL ) 7.1197 - 7.1198 -/* set area key to match empty objects only */ 7.1199 -static void pgl_key_set_empty(pgl_keyptr key) { 7.1200 - memset(key, 0, sizeof(pgl_areakey)); 7.1201 - /* Note: setting node depth to maximum is required for picksplit function */ 7.1202 - key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH; 7.1203 - key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY; 7.1204 -} 7.1205 - 7.1206 -/* set area key to match any object (including empty objects) */ 7.1207 -static void pgl_key_set_universal(pgl_keyptr key) { 7.1208 - memset(key, 0, sizeof(pgl_areakey)); 7.1209 - key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK; 7.1210 - key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL; 7.1211 -} 7.1212 - 7.1213 -/* convert a point on earth into a max-depth key to be used in index */ 7.1214 -static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) { 7.1215 - double lat = point->lat; 7.1216 - double lon = point->lon; 7.1217 - int i; 7.1218 - /* clear latitude and longitude bits */ 7.1219 - memset(key, 0, PGL_KEY_LATLON_BYTELEN); 7.1220 - /* set node depth to maximum and type bit to zero */ 7.1221 - key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH; 7.1222 - /* iterate over all latitude/longitude bit pairs */ 7.1223 - for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) { 7.1224 - /* determine latitude bit */ 7.1225 - if (lat >= 0) { 7.1226 - key[i/4] |= 0x80 >> (2*(i%4)); 7.1227 - lat *= 2; lat -= 90; 7.1228 - } else { 7.1229 - lat *= 2; lat += 90; 7.1230 - } 7.1231 - /* determine longitude bit */ 7.1232 - if (lon >= 0) { 7.1233 - key[i/4] |= 0x80 >> (2*(i%4)+1); 7.1234 - lon *= 2; lon -= 180; 7.1235 - } else { 7.1236 - lon *= 2; lon += 180; 7.1237 - } 7.1238 - } 7.1239 -} 7.1240 - 7.1241 -/* convert a circle on earth into a max-depth key to be used in an index */ 7.1242 -static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) { 7.1243 - /* handle special case of empty circle */ 7.1244 - if (circle->radius < 0) { 7.1245 - pgl_key_set_empty(key); 7.1246 - return; 7.1247 - } 7.1248 - /* perform same action as for point keys */ 7.1249 - pgl_point_to_key(&(circle->center), key); 7.1250 - /* but overwrite type and node depth to fit area index key */ 7.1251 - key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH; 7.1252 - /* check if radius is greater than (or equal to) reference size */ 7.1253 - /* (treat equal values as greater values for numerical safety) */ 7.1254 - if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) { 7.1255 - /* if yes, set logarithmic size to zero */ 7.1256 - key[PGL_KEY_OBJSIZE_OFFSET] = 0; 7.1257 - } else { 7.1258 - /* otherwise, determine logarithmic size iteratively */ 7.1259 - /* (one step is equivalent to a factor of sqrt(2)) */ 7.1260 - double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2; 7.1261 - int objsize = 1; 7.1262 - while (objsize < PGL_AREAKEY_MAXOBJSIZE) { 7.1263 - /* stop when radius is greater than (or equal to) adjusted reference */ 7.1264 - /* (treat equal values as greater values for numerical safety) */ 7.1265 - if (circle->radius >= reference) break; 7.1266 - reference /= M_SQRT2; 7.1267 - objsize++; 7.1268 - } 7.1269 - /* set logarithmic size to determined value */ 7.1270 - key[PGL_KEY_OBJSIZE_OFFSET] = objsize; 7.1271 - } 7.1272 -} 7.1273 - 7.1274 -/* check if one key is subkey of another key or vice versa */ 7.1275 -static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) { 7.1276 - int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */ 7.1277 - /* determine smallest depth */ 7.1278 - int depth1 = PGL_KEY_NODEDEPTH(key1); 7.1279 - int depth2 = PGL_KEY_NODEDEPTH(key2); 7.1280 - int depth = (depth1 < depth2) ? depth1 : depth2; 7.1281 - /* check if keys are area keys (assuming that both keys have same type) */ 7.1282 - if (PGL_KEY_IS_AREAKEY(key1)) { 7.1283 - int j = 0; /* bit offset for logarithmic object size bits */ 7.1284 - int k = 0; /* bit offset for latitude and longitude */ 7.1285 - /* fetch logarithmic object size information */ 7.1286 - int objsize1 = PGL_KEY_OBJSIZE(key1); 7.1287 - int objsize2 = PGL_KEY_OBJSIZE(key2); 7.1288 - /* handle special cases for empty objects (universal and empty keys) */ 7.1289 - if ( 7.1290 - objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL || 7.1291 - objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL 7.1292 - ) return true; 7.1293 - if ( 7.1294 - objsize1 == PGL_KEY_OBJSIZE_EMPTY || 7.1295 - objsize2 == PGL_KEY_OBJSIZE_EMPTY 7.1296 - ) return objsize1 == objsize2; 7.1297 - /* iterate through key bits */ 7.1298 - for (i=0; i<depth; i++) { 7.1299 - /* every second bit is a bit describing the object size */ 7.1300 - if (i%2 == 0) { 7.1301 - /* check if object size bit is different in both keys (objsize1 and 7.1302 - objsize2 describe the minimum index when object size bit is set) */ 7.1303 - if ( 7.1304 - (objsize1 <= j && objsize2 > j) || 7.1305 - (objsize2 <= j && objsize1 > j) 7.1306 - ) { 7.1307 - /* bit differs, therefore keys are in separate branches */ 7.1308 - return false; 7.1309 - } 7.1310 - /* increase bit counter for object size bits */ 7.1311 - j++; 7.1312 - } 7.1313 - /* all other bits describe latitude and longitude */ 7.1314 - else { 7.1315 - /* check if bit differs in both keys */ 7.1316 - if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) { 7.1317 - /* bit differs, therefore keys are in separate branches */ 7.1318 - return false; 7.1319 - } 7.1320 - /* increase bit counter for latitude/longitude bits */ 7.1321 - k++; 7.1322 - } 7.1323 - } 7.1324 - } 7.1325 - /* if not, keys are point keys */ 7.1326 - else { 7.1327 - /* iterate through key bits */ 7.1328 - for (i=0; i<depth; i++) { 7.1329 - /* check if bit differs in both keys */ 7.1330 - if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) { 7.1331 - /* bit differs, therefore keys are in separate branches */ 7.1332 - return false; 7.1333 - } 7.1334 - } 7.1335 - } 7.1336 - /* return true because keys are in the same branch */ 7.1337 - return true; 7.1338 -} 7.1339 - 7.1340 -/* combine two keys into new key which covers both original keys */ 7.1341 -/* (result stored in first argument) */ 7.1342 -static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) { 7.1343 - int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */ 7.1344 - /* determine smallest depth */ 7.1345 - int depth1 = PGL_KEY_NODEDEPTH(dst); 7.1346 - int depth2 = PGL_KEY_NODEDEPTH(src); 7.1347 - int depth = (depth1 < depth2) ? depth1 : depth2; 7.1348 - /* check if keys are area keys (assuming that both keys have same type) */ 7.1349 - if (PGL_KEY_IS_AREAKEY(dst)) { 7.1350 - pgl_areakey dstbuf = { 0, }; /* destination buffer (cleared) */ 7.1351 - int j = 0; /* bit offset for logarithmic object size bits */ 7.1352 - int k = 0; /* bit offset for latitude and longitude */ 7.1353 - /* fetch logarithmic object size information */ 7.1354 - int objsize1 = PGL_KEY_OBJSIZE(dst); 7.1355 - int objsize2 = PGL_KEY_OBJSIZE(src); 7.1356 - /* handle special cases for empty objects (universal and empty keys) */ 7.1357 - if ( 7.1358 - objsize1 > PGL_AREAKEY_MAXOBJSIZE || 7.1359 - objsize2 > PGL_AREAKEY_MAXOBJSIZE 7.1360 - ) { 7.1361 - if ( 7.1362 - objsize1 == PGL_KEY_OBJSIZE_EMPTY && 7.1363 - objsize2 == PGL_KEY_OBJSIZE_EMPTY 7.1364 - ) pgl_key_set_empty(dst); 7.1365 - else pgl_key_set_universal(dst); 7.1366 - return; 7.1367 - } 7.1368 - /* iterate through key bits */ 7.1369 - for (i=0; i<depth; i++) { 7.1370 - /* every second bit is a bit describing the object size */ 7.1371 - if (i%2 == 0) { 7.1372 - /* increase bit counter for object size bits first */ 7.1373 - /* (handy when setting objsize variable) */ 7.1374 - j++; 7.1375 - /* check if object size bit is set in neither key */ 7.1376 - if (objsize1 >= j && objsize2 >= j) { 7.1377 - /* set objsize in destination buffer to indicate that size bit is 7.1378 - unset in destination buffer at the current bit position */ 7.1379 - dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j; 7.1380 - } 7.1381 - /* break if object size bit is set in one key only */ 7.1382 - else if (objsize1 >= j || objsize2 >= j) break; 7.1383 - } 7.1384 - /* all other bits describe latitude and longitude */ 7.1385 - else { 7.1386 - /* break if bit differs in both keys */ 7.1387 - if (PGL_KEY_LATLONBIT(dst, k)) { 7.1388 - if (!PGL_KEY_LATLONBIT(src, k)) break; 7.1389 - /* but set bit in destination buffer if bit is set in both keys */ 7.1390 - dstbuf[k/8] |= 0x80 >> (k%8); 7.1391 - } else if (PGL_KEY_LATLONBIT(src, k)) break; 7.1392 - /* increase bit counter for latitude/longitude bits */ 7.1393 - k++; 7.1394 - } 7.1395 - } 7.1396 - /* set common node depth and type bit (type bit = 1) */ 7.1397 - dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i; 7.1398 - /* copy contents of destination buffer to first key */ 7.1399 - memcpy(dst, dstbuf, sizeof(pgl_areakey)); 7.1400 - } 7.1401 - /* if not, keys are point keys */ 7.1402 - else { 7.1403 - pgl_pointkey dstbuf = { 0, }; /* destination buffer (cleared) */ 7.1404 - /* iterate through key bits */ 7.1405 - for (i=0; i<depth; i++) { 7.1406 - /* break if bit differs in both keys */ 7.1407 - if (PGL_KEY_LATLONBIT(dst, i)) { 7.1408 - if (!PGL_KEY_LATLONBIT(src, i)) break; 7.1409 - /* but set bit in destination buffer if bit is set in both keys */ 7.1410 - dstbuf[i/8] |= 0x80 >> (i%8); 7.1411 - } else if (PGL_KEY_LATLONBIT(src, i)) break; 7.1412 - } 7.1413 - /* set common node depth (type bit = 0) */ 7.1414 - dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i; 7.1415 - /* copy contents of destination buffer to first key */ 7.1416 - memcpy(dst, dstbuf, sizeof(pgl_pointkey)); 7.1417 - } 7.1418 -} 7.1419 - 7.1420 -/* determine center(!) boundaries and radius estimation of index key */ 7.1421 -static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) { 7.1422 - int i; 7.1423 - /* determine node depth */ 7.1424 - int depth = PGL_KEY_NODEDEPTH(key); 7.1425 - /* center point of possible result */ 7.1426 - double lat = 0; 7.1427 - double lon = 0; 7.1428 - /* maximum distance of real center point from key center */ 7.1429 - double dlat = 90; 7.1430 - double dlon = 180; 7.1431 - /* maximum radius of contained objects */ 7.1432 - double radius = 0; /* always return zero for point index keys */ 7.1433 - /* check if key is area key */ 7.1434 - if (PGL_KEY_IS_AREAKEY(key)) { 7.1435 - /* get logarithmic object size */ 7.1436 - int objsize = PGL_KEY_OBJSIZE(key); 7.1437 - /* handle special cases for empty objects (universal and empty keys) */ 7.1438 - if (objsize == PGL_KEY_OBJSIZE_EMPTY) { 7.1439 - pgl_box_set_empty(box); 7.1440 - return 0; 7.1441 - } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) { 7.1442 - box->lat_min = -90; 7.1443 - box->lat_max = 90; 7.1444 - box->lon_min = -180; 7.1445 - box->lon_max = 180; 7.1446 - return 0; /* any value >= 0 would do */ 7.1447 - } 7.1448 - /* calculate maximum possible radius of objects covered by the given key */ 7.1449 - if (objsize == 0) radius = INFINITY; 7.1450 - else { 7.1451 - radius = PGL_AREAKEY_REFOBJSIZE; 7.1452 - while (--objsize) radius /= M_SQRT2; 7.1453 - } 7.1454 - /* iterate over latitude and longitude bits in key */ 7.1455 - /* (every second bit is a latitude or longitude bit) */ 7.1456 - for (i=0; i<depth/2; i++) { 7.1457 - /* check if latitude bit */ 7.1458 - if (i%2 == 0) { 7.1459 - /* cut latitude dimension in half */ 7.1460 - dlat /= 2; 7.1461 - /* increase center latitude if bit is 1, otherwise decrease */ 7.1462 - if (PGL_KEY_LATLONBIT(key, i)) lat += dlat; 7.1463 - else lat -= dlat; 7.1464 - } 7.1465 - /* otherwise longitude bit */ 7.1466 - else { 7.1467 - /* cut longitude dimension in half */ 7.1468 - dlon /= 2; 7.1469 - /* increase center longitude if bit is 1, otherwise decrease */ 7.1470 - if (PGL_KEY_LATLONBIT(key, i)) lon += dlon; 7.1471 - else lon -= dlon; 7.1472 - } 7.1473 - } 7.1474 - } 7.1475 - /* if not, keys are point keys */ 7.1476 - else { 7.1477 - /* iterate over all bits in key */ 7.1478 - for (i=0; i<depth; i++) { 7.1479 - /* check if latitude bit */ 7.1480 - if (i%2 == 0) { 7.1481 - /* cut latitude dimension in half */ 7.1482 - dlat /= 2; 7.1483 - /* increase center latitude if bit is 1, otherwise decrease */ 7.1484 - if (PGL_KEY_LATLONBIT(key, i)) lat += dlat; 7.1485 - else lat -= dlat; 7.1486 - } 7.1487 - /* otherwise longitude bit */ 7.1488 - else { 7.1489 - /* cut longitude dimension in half */ 7.1490 - dlon /= 2; 7.1491 - /* increase center longitude if bit is 1, otherwise decrease */ 7.1492 - if (PGL_KEY_LATLONBIT(key, i)) lon += dlon; 7.1493 - else lon -= dlon; 7.1494 - } 7.1495 - } 7.1496 - } 7.1497 - /* calculate boundaries from center point and remaining dlat and dlon */ 7.1498 - /* (return values through pointer to box) */ 7.1499 - box->lat_min = lat - dlat; 7.1500 - box->lat_max = lat + dlat; 7.1501 - box->lon_min = lon - dlon; 7.1502 - box->lon_max = lon + dlon; 7.1503 - /* return radius (as a function return value) */ 7.1504 - return radius; 7.1505 -} 7.1506 - 7.1507 -/* estimator function for distance between point and index key */ 7.1508 -/* always returns a smaller value than actually correct or zero */ 7.1509 -static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) { 7.1510 - pgl_box box; /* center(!) bounding box of area index key */ 7.1511 - /* calculate center(!) bounding box and maximum radius of objects covered 7.1512 - by area index key (radius is zero for point index keys) */ 7.1513 - double distance = pgl_key_to_box(key, &box); 7.1514 - /* calculate estimated distance between bounding box of center point of 7.1515 - indexed object and point passed as second argument, then substract maximum 7.1516 - radius of objects covered by index key */ 7.1517 - distance = pgl_estimate_point_box_distance(point, &box) - distance; 7.1518 - /* truncate negative results to zero */ 7.1519 - if (distance <= 0) distance = 0; 7.1520 - /* return result */ 7.1521 - return distance; 7.1522 -} 7.1523 - 7.1524 - 7.1525 -/*---------------------------------* 7.1526 - * helper functions for text I/O * 7.1527 - *---------------------------------*/ 7.1528 - 7.1529 -#define PGL_NUMBUFLEN 64 /* buffer size for number to string conversion */ 7.1530 - 7.1531 -/* convert floating point number to string (round-trip safe) */ 7.1532 -static void pgl_print_float(char *buf, double flt) { 7.1533 - /* check if number is integral */ 7.1534 - if (trunc(flt) == flt) { 7.1535 - /* for integral floats use maximum precision */ 7.1536 - snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt); 7.1537 - } else { 7.1538 - /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */ 7.1539 - snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt); 7.1540 - if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt); 7.1541 - if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt); 7.1542 - } 7.1543 -} 7.1544 - 7.1545 -/* convert latitude floating point number (in degrees) to string */ 7.1546 -static void pgl_print_lat(char *buf, double lat) { 7.1547 - if (signbit(lat)) { 7.1548 - /* treat negative latitudes (including -0) as south */ 7.1549 - snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat); 7.1550 - } else { 7.1551 - /* treat positive latitudes (including +0) as north */ 7.1552 - snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat); 7.1553 - } 7.1554 -} 7.1555 - 7.1556 -/* convert longitude floating point number (in degrees) to string */ 7.1557 -static void pgl_print_lon(char *buf, double lon) { 7.1558 - if (signbit(lon)) { 7.1559 - /* treat negative longitudes (including -0) as west */ 7.1560 - snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon); 7.1561 - } else { 7.1562 - /* treat positive longitudes (including +0) as east */ 7.1563 - snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon); 7.1564 - } 7.1565 -} 7.1566 - 7.1567 -/* bit masks used as return value of pgl_scan() function */ 7.1568 -#define PGL_SCAN_NONE 0 /* no value has been parsed */ 7.1569 -#define PGL_SCAN_LAT (1<<0) /* latitude has been parsed */ 7.1570 -#define PGL_SCAN_LON (1<<1) /* longitude has been parsed */ 7.1571 -#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON) /* bitwise OR of both */ 7.1572 - 7.1573 -/* parse a coordinate (can be latitude or longitude) */ 7.1574 -static int pgl_scan(char **str, double *lat, double *lon) { 7.1575 - double val; 7.1576 - int len; 7.1577 - if ( 7.1578 - sscanf(*str, " N %lf %n", &val, &len) || 7.1579 - sscanf(*str, " n %lf %n", &val, &len) 7.1580 - ) { 7.1581 - *str += len; *lat = val; return PGL_SCAN_LAT; 7.1582 - } 7.1583 - if ( 7.1584 - sscanf(*str, " S %lf %n", &val, &len) || 7.1585 - sscanf(*str, " s %lf %n", &val, &len) 7.1586 - ) { 7.1587 - *str += len; *lat = -val; return PGL_SCAN_LAT; 7.1588 - } 7.1589 - if ( 7.1590 - sscanf(*str, " E %lf %n", &val, &len) || 7.1591 - sscanf(*str, " e %lf %n", &val, &len) 7.1592 - ) { 7.1593 - *str += len; *lon = val; return PGL_SCAN_LON; 7.1594 - } 7.1595 - if ( 7.1596 - sscanf(*str, " W %lf %n", &val, &len) || 7.1597 - sscanf(*str, " w %lf %n", &val, &len) 7.1598 - ) { 7.1599 - *str += len; *lon = -val; return PGL_SCAN_LON; 7.1600 - } 7.1601 - return PGL_SCAN_NONE; 7.1602 -} 7.1603 - 7.1604 - 7.1605 -/*-----------------* 7.1606 - * SQL functions * 7.1607 - *-----------------*/ 7.1608 - 7.1609 -/* Note: These function names use "epoint", "ebox", etc. notation here instead 7.1610 - of "point", "box", etc. in order to distinguish them from any previously 7.1611 - defined functions. */ 7.1612 - 7.1613 -/* function needed for dummy types and/or not implemented features */ 7.1614 -PG_FUNCTION_INFO_V1(pgl_notimpl); 7.1615 -Datum pgl_notimpl(PG_FUNCTION_ARGS) { 7.1616 - ereport(ERROR, (errmsg("not implemented by pgLatLon"))); 7.1617 -} 7.1618 - 7.1619 -/* set point to latitude and longitude (including checks) */ 7.1620 -static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) { 7.1621 - /* reject infinite or NaN values */ 7.1622 - if (!isfinite(lat) || !isfinite(lon)) { 7.1623 - ereport(ERROR, ( 7.1624 - errcode(ERRCODE_DATA_EXCEPTION), 7.1625 - errmsg("epoint requires finite coordinates") 7.1626 - )); 7.1627 - } 7.1628 - /* check latitude bounds */ 7.1629 - if (lat < -90) { 7.1630 - ereport(WARNING, (errmsg("latitude exceeds south pole"))); 7.1631 - lat = -90; 7.1632 - } else if (lat > 90) { 7.1633 - ereport(WARNING, (errmsg("latitude exceeds north pole"))); 7.1634 - lat = 90; 7.1635 - } 7.1636 - /* check longitude bounds */ 7.1637 - if (lon < -180) { 7.1638 - ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized"))); 7.1639 - lon += 360 - trunc(lon / 360) * 360; 7.1640 - } else if (lon > 180) { 7.1641 - ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized"))); 7.1642 - lon -= 360 + trunc(lon / 360) * 360; 7.1643 - } 7.1644 - /* store rounded latitude/longitude values for round-trip safety */ 7.1645 - point->lat = pgl_round(lat); 7.1646 - point->lon = pgl_round(lon); 7.1647 -} 7.1648 - 7.1649 -/* create point ("epoint" in SQL) from latitude and longitude */ 7.1650 -PG_FUNCTION_INFO_V1(pgl_create_epoint); 7.1651 -Datum pgl_create_epoint(PG_FUNCTION_ARGS) { 7.1652 - pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point)); 7.1653 - pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1)); 7.1654 - PG_RETURN_POINTER(point); 7.1655 -} 7.1656 - 7.1657 -/* parse point ("epoint" in SQL) */ 7.1658 -/* format: '[NS]<float> [EW]<float>' */ 7.1659 -PG_FUNCTION_INFO_V1(pgl_epoint_in); 7.1660 -Datum pgl_epoint_in(PG_FUNCTION_ARGS) { 7.1661 - char *str = PG_GETARG_CSTRING(0); /* input string */ 7.1662 - char *strptr = str; /* current position within string */ 7.1663 - int done = 0; /* bit mask storing if latitude or longitude was read */ 7.1664 - double lat, lon; /* parsed values as double precision floats */ 7.1665 - pgl_point *point; /* return value (to be palloc'ed) */ 7.1666 - /* parse two floats (each latitude or longitude) separated by white-space */ 7.1667 - done |= pgl_scan(&strptr, &lat, &lon); 7.1668 - if (strptr != str && isspace(strptr[-1])) { 7.1669 - done |= pgl_scan(&strptr, &lat, &lon); 7.1670 - } 7.1671 - /* require end of string, and latitude and longitude parsed successfully */ 7.1672 - if (strptr[0] || done != PGL_SCAN_LATLON) { 7.1673 - ereport(ERROR, ( 7.1674 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 7.1675 - errmsg("invalid input syntax for type epoint: \"%s\"", str) 7.1676 - )); 7.1677 - } 7.1678 - /* allocate memory for result */ 7.1679 - point = (pgl_point *)palloc(sizeof(pgl_point)); 7.1680 - /* set latitude and longitude (and perform checks) */ 7.1681 - pgl_epoint_set_latlon(point, lat, lon); 7.1682 - /* return result */ 7.1683 - PG_RETURN_POINTER(point); 7.1684 -} 7.1685 - 7.1686 -/* set sample count for numerical integration (including checks) */ 7.1687 -static void pgl_epoint_set_sample_count(pgl_point_sc *search, int32 samples) { 7.1688 - /* require minimum of 6 samples */ 7.1689 - if (samples < 6) { 7.1690 - ereport(ERROR, ( 7.1691 - errcode(ERRCODE_DATA_EXCEPTION), 7.1692 - errmsg("too few sample points for numerical integration (minimum 6)") 7.1693 - )); 7.1694 - } 7.1695 - /* limit sample count to avoid integer overflows on memory allocation */ 7.1696 - if (samples > PGL_CLUSTER_MAXPOINTS) { 7.1697 - ereport(ERROR, ( 7.1698 - errcode(ERRCODE_DATA_EXCEPTION), 7.1699 - errmsg( 7.1700 - "too many sample points for numerical integration (maximum %i)", 7.1701 - PGL_CLUSTER_MAXPOINTS 7.1702 - ) 7.1703 - )); 7.1704 - } 7.1705 - search->samples = samples; 7.1706 -} 7.1707 - 7.1708 -/* create point with sample count for fair distance calculation 7.1709 - ("epoint_with_sample_count" in SQL) from epoint and integer */ 7.1710 -PG_FUNCTION_INFO_V1(pgl_create_epoint_with_sample_count); 7.1711 -Datum pgl_create_epoint_with_sample_count(PG_FUNCTION_ARGS) { 7.1712 - pgl_point_sc *search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc)); 7.1713 - search->point = *(pgl_point *)PG_GETARG_POINTER(0); 7.1714 - pgl_epoint_set_sample_count(search, PG_GETARG_INT32(1)); 7.1715 - PG_RETURN_POINTER(search); 7.1716 -} 7.1717 - 7.1718 -/* parse point with sample count ("epoint_with_sample_count" in SQL) */ 7.1719 -/* format: '[NS]<float> [EW]<float> <integer>' */ 7.1720 -PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_in); 7.1721 -Datum pgl_epoint_with_sample_count_in(PG_FUNCTION_ARGS) { 7.1722 - char *str = PG_GETARG_CSTRING(0); /* input string */ 7.1723 - char *strptr = str; /* current position within string */ 7.1724 - double lat, lon; /* parsed values for latitude and longitude */ 7.1725 - int samples; /* parsed value for sample count */ 7.1726 - int valid = 0; /* number of valid chars */ 7.1727 - int done = 0; /* stores if latitude and/or longitude was read */ 7.1728 - pgl_point_sc *search; /* return value (to be palloc'ed) */ 7.1729 - /* demand three blocks separated by whitespace */ 7.1730 - sscanf(strptr, " %*s %*s %*s %n", &valid); 7.1731 - /* if three blocks separated by whitespace exist, parse those blocks */ 7.1732 - if (strptr[valid] == 0) { 7.1733 - /* parse latitude and longitude */ 7.1734 - done |= pgl_scan(&strptr, &lat, &lon); 7.1735 - done |= pgl_scan(&strptr, &lat, &lon); 7.1736 - /* parse sample count (while incr. strptr by number of bytes parsed) */ 7.1737 - valid = 0; 7.1738 - if (sscanf(strptr, " %d %n", &samples, &valid) == 1) strptr += valid; 7.1739 - } 7.1740 - /* require end of string and both latitude and longitude being parsed */ 7.1741 - if (strptr[0] || done != PGL_SCAN_LATLON) { 7.1742 - ereport(ERROR, ( 7.1743 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 7.1744 - errmsg("invalid input syntax for type ecircle: \"%s\"", str) 7.1745 - )); 7.1746 - } 7.1747 - /* allocate memory for result */ 7.1748 - search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc)); 7.1749 - /* set latitude, longitude, and sample count (while performing checks) */ 7.1750 - pgl_epoint_set_latlon(&search->point, lat, lon); 7.1751 - pgl_epoint_set_sample_count(search, samples); 7.1752 - /* return result */ 7.1753 - PG_RETURN_POINTER(search); 7.1754 -} 7.1755 - 7.1756 -/* create box ("ebox" in SQL) that is empty */ 7.1757 -PG_FUNCTION_INFO_V1(pgl_create_empty_ebox); 7.1758 -Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) { 7.1759 - pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 7.1760 - pgl_box_set_empty(box); 7.1761 - PG_RETURN_POINTER(box); 7.1762 -} 7.1763 - 7.1764 -/* set box to given boundaries (including checks) */ 7.1765 -static void pgl_ebox_set_boundaries( 7.1766 - pgl_box *box, 7.1767 - double lat_min, double lat_max, double lon_min, double lon_max 7.1768 -) { 7.1769 - /* if minimum latitude is greater than maximum latitude, return empty box */ 7.1770 - if (lat_min > lat_max) { 7.1771 - pgl_box_set_empty(box); 7.1772 - return; 7.1773 - } 7.1774 - /* otherwise reject infinite or NaN values */ 7.1775 - if ( 7.1776 - !isfinite(lat_min) || !isfinite(lat_max) || 7.1777 - !isfinite(lon_min) || !isfinite(lon_max) 7.1778 - ) { 7.1779 - ereport(ERROR, ( 7.1780 - errcode(ERRCODE_DATA_EXCEPTION), 7.1781 - errmsg("ebox requires finite coordinates") 7.1782 - )); 7.1783 - } 7.1784 - /* check latitude bounds */ 7.1785 - if (lat_max < -90) { 7.1786 - ereport(WARNING, (errmsg("northern latitude exceeds south pole"))); 7.1787 - lat_max = -90; 7.1788 - } else if (lat_max > 90) { 7.1789 - ereport(WARNING, (errmsg("northern latitude exceeds north pole"))); 7.1790 - lat_max = 90; 7.1791 - } 7.1792 - if (lat_min < -90) { 7.1793 - ereport(WARNING, (errmsg("southern latitude exceeds south pole"))); 7.1794 - lat_min = -90; 7.1795 - } else if (lat_min > 90) { 7.1796 - ereport(WARNING, (errmsg("southern latitude exceeds north pole"))); 7.1797 - lat_min = 90; 7.1798 - } 7.1799 - /* check if all longitudes are included */ 7.1800 - if (lon_max - lon_min >= 360) { 7.1801 - if (lon_max - lon_min > 360) ereport(WARNING, ( 7.1802 - errmsg("longitude coverage greater than 360 degrees") 7.1803 - )); 7.1804 - lon_min = -180; 7.1805 - lon_max = 180; 7.1806 - } else { 7.1807 - /* normalize longitude bounds */ 7.1808 - if (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360; 7.1809 - else if (lon_min > 180) lon_min -= 360 + trunc(lon_min / 360) * 360; 7.1810 - if (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360; 7.1811 - else if (lon_max > 180) lon_max -= 360 + trunc(lon_max / 360) * 360; 7.1812 - } 7.1813 - /* store rounded latitude/longitude values for round-trip safety */ 7.1814 - box->lat_min = pgl_round(lat_min); 7.1815 - box->lat_max = pgl_round(lat_max); 7.1816 - box->lon_min = pgl_round(lon_min); 7.1817 - box->lon_max = pgl_round(lon_max); 7.1818 - /* ensure that rounding does not change orientation */ 7.1819 - if (lon_min > lon_max && box->lon_min == box->lon_max) { 7.1820 - box->lon_min = -180; 7.1821 - box->lon_max = 180; 7.1822 - } 7.1823 -} 7.1824 - 7.1825 -/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */ 7.1826 -PG_FUNCTION_INFO_V1(pgl_create_ebox); 7.1827 -Datum pgl_create_ebox(PG_FUNCTION_ARGS) { 7.1828 - pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 7.1829 - pgl_ebox_set_boundaries( 7.1830 - box, 7.1831 - PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), 7.1832 - PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3) 7.1833 - ); 7.1834 - PG_RETURN_POINTER(box); 7.1835 -} 7.1836 - 7.1837 -/* create box ("ebox" in SQL) from two points ("epoint"s) */ 7.1838 -/* (can not be used to cover a longitude range of more than 120 degrees) */ 7.1839 -PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints); 7.1840 -Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) { 7.1841 - pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0); 7.1842 - pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1); 7.1843 - pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 7.1844 - double lat_min, lat_max, lon_min, lon_max; 7.1845 - double dlon; /* longitude range (delta longitude) */ 7.1846 - /* order latitude and longitude boundaries */ 7.1847 - if (point2->lat < point1->lat) { 7.1848 - lat_min = point2->lat; 7.1849 - lat_max = point1->lat; 7.1850 - } else { 7.1851 - lat_min = point1->lat; 7.1852 - lat_max = point2->lat; 7.1853 - } 7.1854 - if (point2->lon < point1->lon) { 7.1855 - lon_min = point2->lon; 7.1856 - lon_max = point1->lon; 7.1857 - } else { 7.1858 - lon_min = point1->lon; 7.1859 - lon_max = point2->lon; 7.1860 - } 7.1861 - /* calculate longitude range (round to avoid floating point errors) */ 7.1862 - dlon = pgl_round(lon_max - lon_min); 7.1863 - /* determine east-west direction */ 7.1864 - if (dlon >= 240) { 7.1865 - /* assume that 180th meridian is crossed and swap min/max longitude */ 7.1866 - double swap = lon_min; lon_min = lon_max; lon_max = swap; 7.1867 - } else if (dlon > 120) { 7.1868 - /* unclear orientation since delta longitude > 120 */ 7.1869 - ereport(ERROR, ( 7.1870 - errcode(ERRCODE_DATA_EXCEPTION), 7.1871 - errmsg("can not determine east/west orientation for ebox") 7.1872 - )); 7.1873 - } 7.1874 - /* use boundaries to setup box (and perform checks) */ 7.1875 - pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max); 7.1876 - /* return result */ 7.1877 - PG_RETURN_POINTER(box); 7.1878 -} 7.1879 - 7.1880 -/* parse box ("ebox" in SQL) */ 7.1881 -/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>' 7.1882 - or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */ 7.1883 -PG_FUNCTION_INFO_V1(pgl_ebox_in); 7.1884 -Datum pgl_ebox_in(PG_FUNCTION_ARGS) { 7.1885 - char *str = PG_GETARG_CSTRING(0); /* input string */ 7.1886 - char *str_lower; /* lower case version of input string */ 7.1887 - char *strptr; /* current position within string */ 7.1888 - int valid; /* number of valid chars */ 7.1889 - int done; /* specifies if latitude or longitude was read */ 7.1890 - double val; /* temporary variable */ 7.1891 - int lat_count = 0; /* count of latitude values parsed */ 7.1892 - int lon_count = 0; /* count of longitufde values parsed */ 7.1893 - double lat_min, lat_max, lon_min, lon_max; /* see pgl_box struct */ 7.1894 - pgl_box *box; /* return value (to be palloc'ed) */ 7.1895 - /* lowercase input */ 7.1896 - str_lower = psprintf("%s", str); 7.1897 - for (strptr=str_lower; *strptr; strptr++) { 7.1898 - if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A'; 7.1899 - } 7.1900 - /* reset reading position to start of (lowercase) string */ 7.1901 - strptr = str_lower; 7.1902 - /* check if empty box */ 7.1903 - valid = 0; 7.1904 - sscanf(strptr, " empty %n", &valid); 7.1905 - if (valid && strptr[valid] == 0) { 7.1906 - /* allocate and return empty box */ 7.1907 - box = (pgl_box *)palloc(sizeof(pgl_box)); 7.1908 - pgl_box_set_empty(box); 7.1909 - PG_RETURN_POINTER(box); 7.1910 - } 7.1911 - /* demand four blocks separated by whitespace */ 7.1912 - valid = 0; 7.1913 - sscanf(strptr, " %*s %*s %*s %*s %n", &valid); 7.1914 - /* if four blocks separated by whitespace exist, parse those blocks */ 7.1915 - if (strptr[valid] == 0) while (strptr[0]) { 7.1916 - /* parse either latitude or longitude (whichever found in input string) */ 7.1917 - done = pgl_scan(&strptr, &val, &val); 7.1918 - /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */ 7.1919 - if (done == PGL_SCAN_LAT) { 7.1920 - if (!lat_count) lat_min = val; else lat_max = val; 7.1921 - lat_count++; 7.1922 - } else if (done == PGL_SCAN_LON) { 7.1923 - if (!lon_count) lon_min = val; else lon_max = val; 7.1924 - lon_count++; 7.1925 - } else { 7.1926 - break; 7.1927 - } 7.1928 - } 7.1929 - /* require end of string, and two latitude and two longitude values */ 7.1930 - if (strptr[0] || lat_count != 2 || lon_count != 2) { 7.1931 - ereport(ERROR, ( 7.1932 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 7.1933 - errmsg("invalid input syntax for type ebox: \"%s\"", str) 7.1934 - )); 7.1935 - } 7.1936 - /* free lower case string */ 7.1937 - pfree(str_lower); 7.1938 - /* order boundaries (maximum greater than minimum) */ 7.1939 - if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; } 7.1940 - if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; } 7.1941 - /* allocate memory for result */ 7.1942 - box = (pgl_box *)palloc(sizeof(pgl_box)); 7.1943 - /* set boundaries (and perform checks) */ 7.1944 - pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max); 7.1945 - /* return result */ 7.1946 - PG_RETURN_POINTER(box); 7.1947 -} 7.1948 - 7.1949 -/* set circle to given latitude, longitude, and radius (including checks) */ 7.1950 -static void pgl_ecircle_set_latlon_radius( 7.1951 - pgl_circle *circle, double lat, double lon, double radius 7.1952 -) { 7.1953 - /* set center point (including checks) */ 7.1954 - pgl_epoint_set_latlon(&(circle->center), lat, lon); 7.1955 - /* handle non-positive radius */ 7.1956 - if (isnan(radius)) { 7.1957 - ereport(ERROR, ( 7.1958 - errcode(ERRCODE_DATA_EXCEPTION), 7.1959 - errmsg("invalid radius for ecircle") 7.1960 - )); 7.1961 - } 7.1962 - if (radius == 0) radius = 0; /* avoids -0 */ 7.1963 - else if (radius < 0) { 7.1964 - if (isfinite(radius)) { 7.1965 - ereport(NOTICE, (errmsg("negative radius converted to minus infinity"))); 7.1966 - } 7.1967 - radius = -INFINITY; 7.1968 - } 7.1969 - /* store radius (round-trip safety is ensured by pgl_print_float) */ 7.1970 - circle->radius = radius; 7.1971 -} 7.1972 - 7.1973 -/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */ 7.1974 -PG_FUNCTION_INFO_V1(pgl_create_ecircle); 7.1975 -Datum pgl_create_ecircle(PG_FUNCTION_ARGS) { 7.1976 - pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 7.1977 - pgl_ecircle_set_latlon_radius( 7.1978 - circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2) 7.1979 - ); 7.1980 - PG_RETURN_POINTER(circle); 7.1981 -} 7.1982 - 7.1983 -/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */ 7.1984 -PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint); 7.1985 -Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) { 7.1986 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.1987 - double radius = PG_GETARG_FLOAT8(1); 7.1988 - pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 7.1989 - /* set latitude, longitude, radius (and perform checks) */ 7.1990 - pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius); 7.1991 - /* return result */ 7.1992 - PG_RETURN_POINTER(circle); 7.1993 -} 7.1994 - 7.1995 -/* parse circle ("ecircle" in SQL) */ 7.1996 -/* format: '[NS]<float> [EW]<float> <float>' */ 7.1997 -PG_FUNCTION_INFO_V1(pgl_ecircle_in); 7.1998 -Datum pgl_ecircle_in(PG_FUNCTION_ARGS) { 7.1999 - char *str = PG_GETARG_CSTRING(0); /* input string */ 7.2000 - char *strptr = str; /* current position within string */ 7.2001 - double lat, lon, radius; /* parsed values as double precision floats */ 7.2002 - int valid = 0; /* number of valid chars */ 7.2003 - int done = 0; /* stores if latitude and/or longitude was read */ 7.2004 - pgl_circle *circle; /* return value (to be palloc'ed) */ 7.2005 - /* demand three blocks separated by whitespace */ 7.2006 - sscanf(strptr, " %*s %*s %*s %n", &valid); 7.2007 - /* if three blocks separated by whitespace exist, parse those blocks */ 7.2008 - if (strptr[valid] == 0) { 7.2009 - /* parse latitude and longitude */ 7.2010 - done |= pgl_scan(&strptr, &lat, &lon); 7.2011 - done |= pgl_scan(&strptr, &lat, &lon); 7.2012 - /* parse radius (while incrementing strptr by number of bytes parsed) */ 7.2013 - valid = 0; 7.2014 - if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid; 7.2015 - } 7.2016 - /* require end of string and both latitude and longitude being parsed */ 7.2017 - if (strptr[0] || done != PGL_SCAN_LATLON) { 7.2018 - ereport(ERROR, ( 7.2019 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 7.2020 - errmsg("invalid input syntax for type ecircle: \"%s\"", str) 7.2021 - )); 7.2022 - } 7.2023 - /* allocate memory for result */ 7.2024 - circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 7.2025 - /* set latitude, longitude, radius (and perform checks) */ 7.2026 - pgl_ecircle_set_latlon_radius(circle, lat, lon, radius); 7.2027 - /* return result */ 7.2028 - PG_RETURN_POINTER(circle); 7.2029 -} 7.2030 - 7.2031 -/* parse cluster ("ecluster" in SQL) */ 7.2032 -PG_FUNCTION_INFO_V1(pgl_ecluster_in); 7.2033 -Datum pgl_ecluster_in(PG_FUNCTION_ARGS) { 7.2034 - int i; 7.2035 - char *str = PG_GETARG_CSTRING(0); /* input string */ 7.2036 - char *str_lower; /* lower case version of input string */ 7.2037 - char *strptr; /* pointer to current reading position of input */ 7.2038 - int npoints_total = 0; /* total number of points in cluster */ 7.2039 - int nentries = 0; /* total number of entries */ 7.2040 - pgl_newentry *entries; /* array of pgl_newentry to create pgl_cluster */ 7.2041 - int entries_buflen = 4; /* maximum number of elements in entries array */ 7.2042 - int valid; /* number of valid chars processed */ 7.2043 - double lat, lon; /* latitude and longitude of parsed point */ 7.2044 - int entrytype; /* current entry type */ 7.2045 - int npoints; /* number of points in current entry */ 7.2046 - pgl_point *points; /* array of pgl_point for pgl_newentry */ 7.2047 - int points_buflen; /* maximum number of elements in points array */ 7.2048 - int done; /* return value of pgl_scan function */ 7.2049 - pgl_cluster *cluster; /* created cluster */ 7.2050 - /* lowercase input */ 7.2051 - str_lower = psprintf("%s", str); 7.2052 - for (strptr=str_lower; *strptr; strptr++) { 7.2053 - if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A'; 7.2054 - } 7.2055 - /* reset reading position to start of (lowercase) string */ 7.2056 - strptr = str_lower; 7.2057 - /* allocate initial buffer for entries */ 7.2058 - entries = palloc(entries_buflen * sizeof(pgl_newentry)); 7.2059 - /* parse until end of string */ 7.2060 - while (strptr[0]) { 7.2061 - /* require previous white-space or closing parenthesis before next token */ 7.2062 - if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') { 7.2063 - goto pgl_ecluster_in_error; 7.2064 - } 7.2065 - /* ignore token "empty" */ 7.2066 - valid = 0; sscanf(strptr, " empty %n", &valid); 7.2067 - if (valid) { strptr += valid; continue; } 7.2068 - /* test for "point" token */ 7.2069 - valid = 0; sscanf(strptr, " point ( %n", &valid); 7.2070 - if (valid) { 7.2071 - strptr += valid; 7.2072 - entrytype = PGL_ENTRY_POINT; 7.2073 - goto pgl_ecluster_in_type_ok; 7.2074 - } 7.2075 - /* test for "path" token */ 7.2076 - valid = 0; sscanf(strptr, " path ( %n", &valid); 7.2077 - if (valid) { 7.2078 - strptr += valid; 7.2079 - entrytype = PGL_ENTRY_PATH; 7.2080 - goto pgl_ecluster_in_type_ok; 7.2081 - } 7.2082 - /* test for "outline" token */ 7.2083 - valid = 0; sscanf(strptr, " outline ( %n", &valid); 7.2084 - if (valid) { 7.2085 - strptr += valid; 7.2086 - entrytype = PGL_ENTRY_OUTLINE; 7.2087 - goto pgl_ecluster_in_type_ok; 7.2088 - } 7.2089 - /* test for "polygon" token */ 7.2090 - valid = 0; sscanf(strptr, " polygon ( %n", &valid); 7.2091 - if (valid) { 7.2092 - strptr += valid; 7.2093 - entrytype = PGL_ENTRY_POLYGON; 7.2094 - goto pgl_ecluster_in_type_ok; 7.2095 - } 7.2096 - /* error if no valid token found */ 7.2097 - goto pgl_ecluster_in_error; 7.2098 - pgl_ecluster_in_type_ok: 7.2099 - /* check if pgl_newentry array needs to grow */ 7.2100 - if (nentries == entries_buflen) { 7.2101 - pgl_newentry *newbuf; 7.2102 - entries_buflen *= 2; 7.2103 - newbuf = palloc(entries_buflen * sizeof(pgl_newentry)); 7.2104 - memcpy(newbuf, entries, nentries * sizeof(pgl_newentry)); 7.2105 - pfree(entries); 7.2106 - entries = newbuf; 7.2107 - } 7.2108 - /* reset number of points for current entry */ 7.2109 - npoints = 0; 7.2110 - /* allocate array for points */ 7.2111 - points_buflen = 4; 7.2112 - points = palloc(points_buflen * sizeof(pgl_point)); 7.2113 - /* parse until closing parenthesis */ 7.2114 - while (strptr[0] != ')') { 7.2115 - /* error on unexpected end of string */ 7.2116 - if (strptr[0] == 0) goto pgl_ecluster_in_error; 7.2117 - /* mark neither latitude nor longitude as read */ 7.2118 - done = PGL_SCAN_NONE; 7.2119 - /* require white-space before second, third, etc. point */ 7.2120 - if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error; 7.2121 - /* scan latitude (or longitude) */ 7.2122 - done |= pgl_scan(&strptr, &lat, &lon); 7.2123 - /* require white-space before second coordinate */ 7.2124 - if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error; 7.2125 - /* scan longitude (or latitude) */ 7.2126 - done |= pgl_scan(&strptr, &lat, &lon); 7.2127 - /* error unless both latitude and longitude were parsed */ 7.2128 - if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error; 7.2129 - /* throw error if number of points is too high */ 7.2130 - if (npoints_total == PGL_CLUSTER_MAXPOINTS) { 7.2131 - ereport(ERROR, ( 7.2132 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 7.2133 - errmsg( 7.2134 - "too many points for ecluster entry (maximum %i)", 7.2135 - PGL_CLUSTER_MAXPOINTS 7.2136 - ) 7.2137 - )); 7.2138 - } 7.2139 - /* check if pgl_point array needs to grow */ 7.2140 - if (npoints == points_buflen) { 7.2141 - pgl_point *newbuf; 7.2142 - points_buflen *= 2; 7.2143 - newbuf = palloc(points_buflen * sizeof(pgl_point)); 7.2144 - memcpy(newbuf, points, npoints * sizeof(pgl_point)); 7.2145 - pfree(points); 7.2146 - points = newbuf; 7.2147 - } 7.2148 - /* append point to pgl_point array (includes checks) */ 7.2149 - pgl_epoint_set_latlon(&(points[npoints++]), lat, lon); 7.2150 - /* increase total number of points */ 7.2151 - npoints_total++; 7.2152 - } 7.2153 - /* error if entry has no points */ 7.2154 - if (!npoints) goto pgl_ecluster_in_error; 7.2155 - /* entries with one point are automatically of type "point" */ 7.2156 - if (npoints == 1) entrytype = PGL_ENTRY_POINT; 7.2157 - /* if entries have more than one point */ 7.2158 - else { 7.2159 - /* throw error if entry type is "point" */ 7.2160 - if (entrytype == PGL_ENTRY_POINT) { 7.2161 - ereport(ERROR, ( 7.2162 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 7.2163 - errmsg("invalid input syntax for type ecluster (point entry with more than one point)") 7.2164 - )); 7.2165 - } 7.2166 - /* coerce outlines and polygons with more than 2 points to be a path */ 7.2167 - if (npoints == 2) entrytype = PGL_ENTRY_PATH; 7.2168 - } 7.2169 - /* append entry to pgl_newentry array */ 7.2170 - entries[nentries].entrytype = entrytype; 7.2171 - entries[nentries].npoints = npoints; 7.2172 - entries[nentries].points = points; 7.2173 - nentries++; 7.2174 - /* consume closing parenthesis */ 7.2175 - strptr++; 7.2176 - /* consume white-space */ 7.2177 - while (isspace(strptr[0])) strptr++; 7.2178 - } 7.2179 - /* free lower case string */ 7.2180 - pfree(str_lower); 7.2181 - /* create cluster from pgl_newentry array */ 7.2182 - cluster = pgl_new_cluster(nentries, entries); 7.2183 - /* free pgl_newentry array */ 7.2184 - for (i=0; i<nentries; i++) pfree(entries[i].points); 7.2185 - pfree(entries); 7.2186 - /* set bounding circle of cluster and check east/west orientation */ 7.2187 - if (!pgl_finalize_cluster(cluster)) { 7.2188 - ereport(ERROR, ( 7.2189 - errcode(ERRCODE_DATA_EXCEPTION), 7.2190 - errmsg("can not determine east/west orientation for ecluster"), 7.2191 - errhint("Ensure that each entry has a longitude span of less than 180 degrees.") 7.2192 - )); 7.2193 - } 7.2194 - /* return cluster */ 7.2195 - PG_RETURN_POINTER(cluster); 7.2196 - /* code to throw error */ 7.2197 - pgl_ecluster_in_error: 7.2198 - ereport(ERROR, ( 7.2199 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 7.2200 - errmsg("invalid input syntax for type ecluster: \"%s\"", str) 7.2201 - )); 7.2202 -} 7.2203 - 7.2204 -/* convert point ("epoint") to string representation */ 7.2205 -PG_FUNCTION_INFO_V1(pgl_epoint_out); 7.2206 -Datum pgl_epoint_out(PG_FUNCTION_ARGS) { 7.2207 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2208 - char latstr[PGL_NUMBUFLEN]; 7.2209 - char lonstr[PGL_NUMBUFLEN]; 7.2210 - pgl_print_lat(latstr, point->lat); 7.2211 - pgl_print_lon(lonstr, point->lon); 7.2212 - PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr)); 7.2213 -} 7.2214 - 7.2215 -/* convert point with sample count ("epoint_with_sample_count") to str. rep. */ 7.2216 -PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_out); 7.2217 -Datum pgl_epoint_with_sample_count_out(PG_FUNCTION_ARGS) { 7.2218 - pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(0); 7.2219 - char latstr[PGL_NUMBUFLEN]; 7.2220 - char lonstr[PGL_NUMBUFLEN]; 7.2221 - pgl_print_lat(latstr, search->point.lat); 7.2222 - pgl_print_lon(lonstr, search->point.lon); 7.2223 - PG_RETURN_CSTRING( 7.2224 - psprintf("%s %s %i", latstr, lonstr, (int)search->samples) 7.2225 - ); 7.2226 -} 7.2227 - 7.2228 -/* convert box ("ebox") to string representation */ 7.2229 -PG_FUNCTION_INFO_V1(pgl_ebox_out); 7.2230 -Datum pgl_ebox_out(PG_FUNCTION_ARGS) { 7.2231 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 7.2232 - double lon_min = box->lon_min; 7.2233 - double lon_max = box->lon_max; 7.2234 - char lat_min_str[PGL_NUMBUFLEN]; 7.2235 - char lat_max_str[PGL_NUMBUFLEN]; 7.2236 - char lon_min_str[PGL_NUMBUFLEN]; 7.2237 - char lon_max_str[PGL_NUMBUFLEN]; 7.2238 - /* return string "empty" if box is set to be empty */ 7.2239 - if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty"); 7.2240 - /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */ 7.2241 - /* (required since pgl_box_in orders the longitude boundaries) */ 7.2242 - if (lon_min > lon_max) { 7.2243 - if (lon_min + lon_max >= 0) lon_min -= 360; 7.2244 - else lon_max += 360; 7.2245 - } 7.2246 - /* format and return result */ 7.2247 - pgl_print_lat(lat_min_str, box->lat_min); 7.2248 - pgl_print_lat(lat_max_str, box->lat_max); 7.2249 - pgl_print_lon(lon_min_str, lon_min); 7.2250 - pgl_print_lon(lon_max_str, lon_max); 7.2251 - PG_RETURN_CSTRING(psprintf( 7.2252 - "%s %s %s %s", 7.2253 - lat_min_str, lon_min_str, lat_max_str, lon_max_str 7.2254 - )); 7.2255 -} 7.2256 - 7.2257 -/* convert circle ("ecircle") to string representation */ 7.2258 -PG_FUNCTION_INFO_V1(pgl_ecircle_out); 7.2259 -Datum pgl_ecircle_out(PG_FUNCTION_ARGS) { 7.2260 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 7.2261 - char latstr[PGL_NUMBUFLEN]; 7.2262 - char lonstr[PGL_NUMBUFLEN]; 7.2263 - char radstr[PGL_NUMBUFLEN]; 7.2264 - pgl_print_lat(latstr, circle->center.lat); 7.2265 - pgl_print_lon(lonstr, circle->center.lon); 7.2266 - pgl_print_float(radstr, circle->radius); 7.2267 - PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr)); 7.2268 -} 7.2269 - 7.2270 -/* convert cluster ("ecluster") to string representation */ 7.2271 -PG_FUNCTION_INFO_V1(pgl_ecluster_out); 7.2272 -Datum pgl_ecluster_out(PG_FUNCTION_ARGS) { 7.2273 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 7.2274 - char latstr[PGL_NUMBUFLEN]; /* string buffer for latitude */ 7.2275 - char lonstr[PGL_NUMBUFLEN]; /* string buffer for longitude */ 7.2276 - char ***strings; /* array of array of strings */ 7.2277 - char *string; /* string of current token */ 7.2278 - char *res, *resptr; /* result and pointer to current write position */ 7.2279 - size_t reslen = 1; /* length of result (init with 1 for terminator) */ 7.2280 - int npoints; /* number of points of current entry */ 7.2281 - int i, j; /* i: entry, j: point in entry */ 7.2282 - /* handle empty clusters */ 7.2283 - if (cluster->nentries == 0) { 7.2284 - /* free detoasted cluster (if copy) */ 7.2285 - PG_FREE_IF_COPY(cluster, 0); 7.2286 - /* return static result */ 7.2287 - PG_RETURN_CSTRING("empty"); 7.2288 - } 7.2289 - /* allocate array of array of strings */ 7.2290 - strings = palloc(cluster->nentries * sizeof(char **)); 7.2291 - /* iterate over all entries in cluster */ 7.2292 - for (i=0; i<cluster->nentries; i++) { 7.2293 - /* get number of points in entry */ 7.2294 - npoints = cluster->entries[i].npoints; 7.2295 - /* allocate array of strings (one string for each point plus two extra) */ 7.2296 - strings[i] = palloc((2 + npoints) * sizeof(char *)); 7.2297 - /* determine opening string */ 7.2298 - switch (cluster->entries[i].entrytype) { 7.2299 - case PGL_ENTRY_POINT: string = (i==0)?"point (" :" point ("; break; 7.2300 - case PGL_ENTRY_PATH: string = (i==0)?"path (" :" path ("; break; 7.2301 - case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break; 7.2302 - case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break; 7.2303 - default: string = (i==0)?"unknown" :" unknown"; 7.2304 - } 7.2305 - /* use opening string as first string in array */ 7.2306 - strings[i][0] = string; 7.2307 - /* update result length (for allocating result string later) */ 7.2308 - reslen += strlen(string); 7.2309 - /* iterate over all points */ 7.2310 - for (j=0; j<npoints; j++) { 7.2311 - /* create string representation of point */ 7.2312 - pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat); 7.2313 - pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon); 7.2314 - string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr); 7.2315 - /* copy string pointer to string array */ 7.2316 - strings[i][j+1] = string; 7.2317 - /* update result length (for allocating result string later) */ 7.2318 - reslen += strlen(string); 7.2319 - } 7.2320 - /* use closing parenthesis as last string in array */ 7.2321 - strings[i][npoints+1] = ")"; 7.2322 - /* update result length (for allocating result string later) */ 7.2323 - reslen++; 7.2324 - } 7.2325 - /* allocate result string */ 7.2326 - res = palloc(reslen); 7.2327 - /* set write pointer to begin of result string */ 7.2328 - resptr = res; 7.2329 - /* copy strings into result string */ 7.2330 - for (i=0; i<cluster->nentries; i++) { 7.2331 - npoints = cluster->entries[i].npoints; 7.2332 - for (j=0; j<npoints+2; j++) { 7.2333 - string = strings[i][j]; 7.2334 - strcpy(resptr, string); 7.2335 - resptr += strlen(string); 7.2336 - /* free strings allocated by psprintf */ 7.2337 - if (j != 0 && j != npoints+1) pfree(string); 7.2338 - } 7.2339 - /* free array of strings */ 7.2340 - pfree(strings[i]); 7.2341 - } 7.2342 - /* free array of array of strings */ 7.2343 - pfree(strings); 7.2344 - /* free detoasted cluster (if copy) */ 7.2345 - PG_FREE_IF_COPY(cluster, 0); 7.2346 - /* return result */ 7.2347 - PG_RETURN_CSTRING(res); 7.2348 -} 7.2349 - 7.2350 -/* binary input function for point ("epoint") */ 7.2351 -PG_FUNCTION_INFO_V1(pgl_epoint_recv); 7.2352 -Datum pgl_epoint_recv(PG_FUNCTION_ARGS) { 7.2353 - StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 7.2354 - pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point)); 7.2355 - point->lat = pq_getmsgfloat8(buf); 7.2356 - point->lon = pq_getmsgfloat8(buf); 7.2357 - PG_RETURN_POINTER(point); 7.2358 -} 7.2359 - 7.2360 -/* binary input function for box ("ebox") */ 7.2361 -PG_FUNCTION_INFO_V1(pgl_ebox_recv); 7.2362 -Datum pgl_ebox_recv(PG_FUNCTION_ARGS) { 7.2363 - StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 7.2364 - pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 7.2365 - box->lat_min = pq_getmsgfloat8(buf); 7.2366 - box->lat_max = pq_getmsgfloat8(buf); 7.2367 - box->lon_min = pq_getmsgfloat8(buf); 7.2368 - box->lon_max = pq_getmsgfloat8(buf); 7.2369 - PG_RETURN_POINTER(box); 7.2370 -} 7.2371 - 7.2372 -/* binary input function for circle ("ecircle") */ 7.2373 -PG_FUNCTION_INFO_V1(pgl_ecircle_recv); 7.2374 -Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) { 7.2375 - StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 7.2376 - pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 7.2377 - circle->center.lat = pq_getmsgfloat8(buf); 7.2378 - circle->center.lon = pq_getmsgfloat8(buf); 7.2379 - circle->radius = pq_getmsgfloat8(buf); 7.2380 - PG_RETURN_POINTER(circle); 7.2381 -} 7.2382 - 7.2383 -/* TODO: binary receive function for cluster */ 7.2384 - 7.2385 -/* binary output function for point ("epoint") */ 7.2386 -PG_FUNCTION_INFO_V1(pgl_epoint_send); 7.2387 -Datum pgl_epoint_send(PG_FUNCTION_ARGS) { 7.2388 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2389 - StringInfoData buf; 7.2390 - pq_begintypsend(&buf); 7.2391 - pq_sendfloat8(&buf, point->lat); 7.2392 - pq_sendfloat8(&buf, point->lon); 7.2393 - PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 7.2394 -} 7.2395 - 7.2396 -/* binary output function for box ("ebox") */ 7.2397 -PG_FUNCTION_INFO_V1(pgl_ebox_send); 7.2398 -Datum pgl_ebox_send(PG_FUNCTION_ARGS) { 7.2399 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 7.2400 - StringInfoData buf; 7.2401 - pq_begintypsend(&buf); 7.2402 - pq_sendfloat8(&buf, box->lat_min); 7.2403 - pq_sendfloat8(&buf, box->lat_max); 7.2404 - pq_sendfloat8(&buf, box->lon_min); 7.2405 - pq_sendfloat8(&buf, box->lon_max); 7.2406 - PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 7.2407 -} 7.2408 - 7.2409 -/* binary output function for circle ("ecircle") */ 7.2410 -PG_FUNCTION_INFO_V1(pgl_ecircle_send); 7.2411 -Datum pgl_ecircle_send(PG_FUNCTION_ARGS) { 7.2412 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 7.2413 - StringInfoData buf; 7.2414 - pq_begintypsend(&buf); 7.2415 - pq_sendfloat8(&buf, circle->center.lat); 7.2416 - pq_sendfloat8(&buf, circle->center.lon); 7.2417 - pq_sendfloat8(&buf, circle->radius); 7.2418 - PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 7.2419 -} 7.2420 - 7.2421 -/* TODO: binary send functions for cluster */ 7.2422 - 7.2423 -/* cast point ("epoint") to box ("ebox") */ 7.2424 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox); 7.2425 -Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) { 7.2426 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2427 - pgl_box *box = palloc(sizeof(pgl_box)); 7.2428 - box->lat_min = point->lat; 7.2429 - box->lat_max = point->lat; 7.2430 - box->lon_min = point->lon; 7.2431 - box->lon_max = point->lon; 7.2432 - PG_RETURN_POINTER(box); 7.2433 -} 7.2434 - 7.2435 -/* cast point ("epoint") to circle ("ecircle") */ 7.2436 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle); 7.2437 -Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) { 7.2438 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2439 - pgl_circle *circle = palloc(sizeof(pgl_box)); 7.2440 - circle->center = *point; 7.2441 - circle->radius = 0; 7.2442 - PG_RETURN_POINTER(circle); 7.2443 -} 7.2444 - 7.2445 -/* cast point ("epoint") to cluster ("ecluster") */ 7.2446 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster); 7.2447 -Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) { 7.2448 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2449 - pgl_newentry entry; 7.2450 - pgl_cluster *cluster; 7.2451 - entry.entrytype = PGL_ENTRY_POINT; 7.2452 - entry.npoints = 1; 7.2453 - entry.points = point; 7.2454 - cluster = pgl_new_cluster(1, &entry); 7.2455 - pgl_finalize_cluster(cluster); /* NOTE: should not fail */ 7.2456 - PG_RETURN_POINTER(cluster); 7.2457 -} 7.2458 - 7.2459 -/* cast box ("ebox") to cluster ("ecluster") */ 7.2460 -#define pgl_ebox_to_ecluster_macro(i, a, b) \ 7.2461 - entries[i].entrytype = PGL_ENTRY_POLYGON; \ 7.2462 - entries[i].npoints = 4; \ 7.2463 - entries[i].points = points[i]; \ 7.2464 - points[i][0].lat = box->lat_min; \ 7.2465 - points[i][0].lon = (a); \ 7.2466 - points[i][1].lat = box->lat_min; \ 7.2467 - points[i][1].lon = (b); \ 7.2468 - points[i][2].lat = box->lat_max; \ 7.2469 - points[i][2].lon = (b); \ 7.2470 - points[i][3].lat = box->lat_max; \ 7.2471 - points[i][3].lon = (a); 7.2472 -PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster); 7.2473 -Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) { 7.2474 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 7.2475 - double lon, dlon; 7.2476 - int nentries; 7.2477 - pgl_newentry entries[3]; 7.2478 - pgl_point points[3][4]; 7.2479 - pgl_cluster *cluster; 7.2480 - if (box->lat_min > box->lat_max) { 7.2481 - nentries = 0; 7.2482 - } else if (box->lon_min > box->lon_max) { 7.2483 - if (box->lon_min < 0) { 7.2484 - lon = pgl_round((box->lon_min + 180) / 2.0); 7.2485 - nentries = 3; 7.2486 - pgl_ebox_to_ecluster_macro(0, box->lon_min, lon); 7.2487 - pgl_ebox_to_ecluster_macro(1, lon, 180); 7.2488 - pgl_ebox_to_ecluster_macro(2, -180, box->lon_max); 7.2489 - } else if (box->lon_max > 0) { 7.2490 - lon = pgl_round((box->lon_max - 180) / 2.0); 7.2491 - nentries = 3; 7.2492 - pgl_ebox_to_ecluster_macro(0, box->lon_min, 180); 7.2493 - pgl_ebox_to_ecluster_macro(1, -180, lon); 7.2494 - pgl_ebox_to_ecluster_macro(2, lon, box->lon_max); 7.2495 - } else { 7.2496 - nentries = 2; 7.2497 - pgl_ebox_to_ecluster_macro(0, box->lon_min, 180); 7.2498 - pgl_ebox_to_ecluster_macro(1, -180, box->lon_max); 7.2499 - } 7.2500 - } else { 7.2501 - dlon = pgl_round(box->lon_max - box->lon_min); 7.2502 - if (dlon < 180) { 7.2503 - nentries = 1; 7.2504 - pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max); 7.2505 - } else { 7.2506 - lon = pgl_round((box->lon_min + box->lon_max) / 2.0); 7.2507 - if ( 7.2508 - pgl_round(lon - box->lon_min) < 180 && 7.2509 - pgl_round(box->lon_max - lon) < 180 7.2510 - ) { 7.2511 - nentries = 2; 7.2512 - pgl_ebox_to_ecluster_macro(0, box->lon_min, lon); 7.2513 - pgl_ebox_to_ecluster_macro(1, lon, box->lon_max); 7.2514 - } else { 7.2515 - nentries = 3; 7.2516 - pgl_ebox_to_ecluster_macro(0, box->lon_min, -60); 7.2517 - pgl_ebox_to_ecluster_macro(1, -60, 60); 7.2518 - pgl_ebox_to_ecluster_macro(2, 60, box->lon_max); 7.2519 - } 7.2520 - } 7.2521 - } 7.2522 - cluster = pgl_new_cluster(nentries, entries); 7.2523 - pgl_finalize_cluster(cluster); /* NOTE: should not fail */ 7.2524 - PG_RETURN_POINTER(cluster); 7.2525 -} 7.2526 - 7.2527 -/* extract latitude from point ("epoint") */ 7.2528 -PG_FUNCTION_INFO_V1(pgl_epoint_lat); 7.2529 -Datum pgl_epoint_lat(PG_FUNCTION_ARGS) { 7.2530 - PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat); 7.2531 -} 7.2532 - 7.2533 -/* extract longitude from point ("epoint") */ 7.2534 -PG_FUNCTION_INFO_V1(pgl_epoint_lon); 7.2535 -Datum pgl_epoint_lon(PG_FUNCTION_ARGS) { 7.2536 - PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon); 7.2537 -} 7.2538 - 7.2539 -/* extract minimum latitude from box ("ebox") */ 7.2540 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_min); 7.2541 -Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) { 7.2542 - PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min); 7.2543 -} 7.2544 - 7.2545 -/* extract maximum latitude from box ("ebox") */ 7.2546 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_max); 7.2547 -Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) { 7.2548 - PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max); 7.2549 -} 7.2550 - 7.2551 -/* extract minimum longitude from box ("ebox") */ 7.2552 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_min); 7.2553 -Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) { 7.2554 - PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min); 7.2555 -} 7.2556 - 7.2557 -/* extract maximum longitude from box ("ebox") */ 7.2558 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_max); 7.2559 -Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) { 7.2560 - PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max); 7.2561 -} 7.2562 - 7.2563 -/* extract center point from circle ("ecircle") */ 7.2564 -PG_FUNCTION_INFO_V1(pgl_ecircle_center); 7.2565 -Datum pgl_ecircle_center(PG_FUNCTION_ARGS) { 7.2566 - PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center)); 7.2567 -} 7.2568 - 7.2569 -/* extract radius from circle ("ecircle") */ 7.2570 -PG_FUNCTION_INFO_V1(pgl_ecircle_radius); 7.2571 -Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) { 7.2572 - PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius); 7.2573 -} 7.2574 - 7.2575 -/* check if point is inside box (overlap operator "&&") in SQL */ 7.2576 -PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap); 7.2577 -Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) { 7.2578 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2579 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1); 7.2580 - PG_RETURN_BOOL(pgl_point_in_box(point, box)); 7.2581 -} 7.2582 - 7.2583 -/* check if point is inside circle (overlap operator "&&") in SQL */ 7.2584 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap); 7.2585 -Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) { 7.2586 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2587 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 7.2588 - PG_RETURN_BOOL( 7.2589 - pgl_distance( 7.2590 - point->lat, point->lon, 7.2591 - circle->center.lat, circle->center.lon 7.2592 - ) <= circle->radius 7.2593 - ); 7.2594 -} 7.2595 - 7.2596 -/* check if point is inside cluster (overlap operator "&&") in SQL */ 7.2597 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap); 7.2598 -Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) { 7.2599 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2600 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2601 - bool retval; 7.2602 - /* points outside bounding circle are always assumed to be non-overlapping */ 7.2603 - if ( 7.2604 - pgl_distance( 7.2605 - point->lat, point->lon, 7.2606 - cluster->bounding.center.lat, cluster->bounding.center.lon 7.2607 - ) > cluster->bounding.radius 7.2608 - ) retval = false; 7.2609 - else retval = pgl_point_in_cluster(point, cluster, false); 7.2610 - PG_FREE_IF_COPY(cluster, 1); 7.2611 - PG_RETURN_BOOL(retval); 7.2612 -} 7.2613 - 7.2614 -/* check if point may be inside cluster (lossy overl. operator "&&+") in SQL */ 7.2615 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_may_overlap); 7.2616 -Datum pgl_epoint_ecluster_may_overlap(PG_FUNCTION_ARGS) { 7.2617 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2618 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2619 - bool retval = pgl_distance( 7.2620 - point->lat, point->lon, 7.2621 - cluster->bounding.center.lat, cluster->bounding.center.lon 7.2622 - ) <= cluster->bounding.radius; 7.2623 - PG_FREE_IF_COPY(cluster, 1); 7.2624 - PG_RETURN_BOOL(retval); 7.2625 -} 7.2626 - 7.2627 -/* check if two boxes overlap (overlap operator "&&") in SQL */ 7.2628 -PG_FUNCTION_INFO_V1(pgl_ebox_overlap); 7.2629 -Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) { 7.2630 - pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0); 7.2631 - pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1); 7.2632 - PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2)); 7.2633 -} 7.2634 - 7.2635 -/* check if box and circle may overlap (lossy overl. operator "&&+") in SQL */ 7.2636 -PG_FUNCTION_INFO_V1(pgl_ebox_ecircle_may_overlap); 7.2637 -Datum pgl_ebox_ecircle_may_overlap(PG_FUNCTION_ARGS) { 7.2638 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 7.2639 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 7.2640 - PG_RETURN_BOOL( 7.2641 - pgl_estimate_point_box_distance(&circle->center, box) <= circle->radius 7.2642 - ); 7.2643 -} 7.2644 - 7.2645 -/* check if box and cluster may overlap (lossy overl. operator "&&+") in SQL */ 7.2646 -PG_FUNCTION_INFO_V1(pgl_ebox_ecluster_may_overlap); 7.2647 -Datum pgl_ebox_ecluster_may_overlap(PG_FUNCTION_ARGS) { 7.2648 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 7.2649 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2650 - bool retval = pgl_estimate_point_box_distance( 7.2651 - &cluster->bounding.center, 7.2652 - box 7.2653 - ) <= cluster->bounding.radius; 7.2654 - PG_FREE_IF_COPY(cluster, 1); 7.2655 - PG_RETURN_BOOL(retval); 7.2656 -} 7.2657 - 7.2658 -/* check if two circles overlap (overlap operator "&&") in SQL */ 7.2659 -PG_FUNCTION_INFO_V1(pgl_ecircle_overlap); 7.2660 -Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) { 7.2661 - pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0); 7.2662 - pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1); 7.2663 - PG_RETURN_BOOL( 7.2664 - pgl_distance( 7.2665 - circle1->center.lat, circle1->center.lon, 7.2666 - circle2->center.lat, circle2->center.lon 7.2667 - ) <= circle1->radius + circle2->radius 7.2668 - ); 7.2669 -} 7.2670 - 7.2671 -/* check if circle and cluster overlap (overlap operator "&&") in SQL */ 7.2672 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap); 7.2673 -Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) { 7.2674 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 7.2675 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2676 - bool retval; 7.2677 - /* points outside bounding circle (with margin for flattening) are always 7.2678 - assumed to be non-overlapping */ 7.2679 - if ( 7.2680 - (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 7.2681 - pgl_distance( 7.2682 - circle->center.lat, circle->center.lon, 7.2683 - cluster->bounding.center.lat, cluster->bounding.center.lon 7.2684 - ) > circle->radius + cluster->bounding.radius 7.2685 - ) retval = false; 7.2686 - else retval = pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius; 7.2687 - PG_FREE_IF_COPY(cluster, 1); 7.2688 - PG_RETURN_BOOL(retval); 7.2689 -} 7.2690 - 7.2691 -/* check if circle and cluster may overlap (l. ov. operator "&&+") in SQL */ 7.2692 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_may_overlap); 7.2693 -Datum pgl_ecircle_ecluster_may_overlap(PG_FUNCTION_ARGS) { 7.2694 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 7.2695 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2696 - bool retval = ( 7.2697 - (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 7.2698 - pgl_distance( 7.2699 - circle->center.lat, circle->center.lon, 7.2700 - cluster->bounding.center.lat, cluster->bounding.center.lon 7.2701 - ) 7.2702 - ) <= circle->radius + cluster->bounding.radius; 7.2703 - PG_FREE_IF_COPY(cluster, 1); 7.2704 - PG_RETURN_BOOL(retval); 7.2705 -} 7.2706 - 7.2707 -/* check if two clusters overlap (overlap operator "&&") in SQL */ 7.2708 -PG_FUNCTION_INFO_V1(pgl_ecluster_overlap); 7.2709 -Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) { 7.2710 - pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 7.2711 - pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2712 - bool retval; 7.2713 - /* clusters with non-touching bounding circles (with margin for flattening) 7.2714 - are always assumed to be non-overlapping */ 7.2715 - if ( 7.2716 - (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 7.2717 - pgl_distance( 7.2718 - cluster1->bounding.center.lat, cluster1->bounding.center.lon, 7.2719 - cluster2->bounding.center.lat, cluster2->bounding.center.lon 7.2720 - ) > cluster1->bounding.radius + cluster2->bounding.radius 7.2721 - ) retval = false; 7.2722 - else retval = pgl_clusters_overlap(cluster1, cluster2); 7.2723 - PG_FREE_IF_COPY(cluster1, 0); 7.2724 - PG_FREE_IF_COPY(cluster2, 1); 7.2725 - PG_RETURN_BOOL(retval); 7.2726 -} 7.2727 - 7.2728 -/* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */ 7.2729 -PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap); 7.2730 -Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) { 7.2731 - pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 7.2732 - pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2733 - bool retval = ( 7.2734 - (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 7.2735 - pgl_distance( 7.2736 - cluster1->bounding.center.lat, cluster1->bounding.center.lon, 7.2737 - cluster2->bounding.center.lat, cluster2->bounding.center.lon 7.2738 - ) 7.2739 - ) <= cluster1->bounding.radius + cluster2->bounding.radius; 7.2740 - PG_FREE_IF_COPY(cluster1, 0); 7.2741 - PG_FREE_IF_COPY(cluster2, 1); 7.2742 - PG_RETURN_BOOL(retval); 7.2743 -} 7.2744 - 7.2745 -/* check if second cluster is in first cluster (cont. operator "@>) in SQL */ 7.2746 -PG_FUNCTION_INFO_V1(pgl_ecluster_contains); 7.2747 -Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) { 7.2748 - pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 7.2749 - pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2750 - bool retval; 7.2751 - /* clusters with non-touching bounding circles are always assumed to be 7.2752 - non-overlapping */ 7.2753 - if ( 7.2754 - (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 7.2755 - pgl_distance( 7.2756 - outer->bounding.center.lat, outer->bounding.center.lon, 7.2757 - inner->bounding.center.lat, inner->bounding.center.lon 7.2758 - ) > outer->bounding.radius + inner->bounding.radius 7.2759 - ) retval = false; 7.2760 - else retval = pgl_cluster_in_cluster(outer, inner); 7.2761 - PG_FREE_IF_COPY(outer, 0); 7.2762 - PG_FREE_IF_COPY(inner, 1); 7.2763 - PG_RETURN_BOOL(retval); 7.2764 -} 7.2765 - 7.2766 -/* calculate distance between two points ("<->" operator) in SQL */ 7.2767 -PG_FUNCTION_INFO_V1(pgl_epoint_distance); 7.2768 -Datum pgl_epoint_distance(PG_FUNCTION_ARGS) { 7.2769 - pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0); 7.2770 - pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1); 7.2771 - PG_RETURN_FLOAT8(pgl_distance( 7.2772 - point1->lat, point1->lon, point2->lat, point2->lon 7.2773 - )); 7.2774 -} 7.2775 - 7.2776 -/* calculate point to circle distance ("<->" operator) in SQL */ 7.2777 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance); 7.2778 -Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) { 7.2779 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2780 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 7.2781 - double distance = pgl_distance( 7.2782 - point->lat, point->lon, circle->center.lat, circle->center.lon 7.2783 - ) - circle->radius; 7.2784 - PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 7.2785 -} 7.2786 - 7.2787 -/* calculate point to cluster distance ("<->" operator) in SQL */ 7.2788 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance); 7.2789 -Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) { 7.2790 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 7.2791 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2792 - double distance = pgl_point_cluster_distance(point, cluster); 7.2793 - PG_FREE_IF_COPY(cluster, 1); 7.2794 - PG_RETURN_FLOAT8(distance); 7.2795 -} 7.2796 - 7.2797 -/* calculate distance between two circles ("<->" operator) in SQL */ 7.2798 -PG_FUNCTION_INFO_V1(pgl_ecircle_distance); 7.2799 -Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) { 7.2800 - pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0); 7.2801 - pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1); 7.2802 - double distance = pgl_distance( 7.2803 - circle1->center.lat, circle1->center.lon, 7.2804 - circle2->center.lat, circle2->center.lon 7.2805 - ) - (circle1->radius + circle2->radius); 7.2806 - PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 7.2807 -} 7.2808 - 7.2809 -/* calculate circle to cluster distance ("<->" operator) in SQL */ 7.2810 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance); 7.2811 -Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) { 7.2812 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 7.2813 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2814 - double distance = ( 7.2815 - pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius 7.2816 - ); 7.2817 - PG_FREE_IF_COPY(cluster, 1); 7.2818 - PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 7.2819 -} 7.2820 - 7.2821 -/* calculate distance between two clusters ("<->" operator) in SQL */ 7.2822 -PG_FUNCTION_INFO_V1(pgl_ecluster_distance); 7.2823 -Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) { 7.2824 - pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 7.2825 - pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2826 - double retval = pgl_cluster_distance(cluster1, cluster2); 7.2827 - PG_FREE_IF_COPY(cluster1, 0); 7.2828 - PG_FREE_IF_COPY(cluster2, 1); 7.2829 - PG_RETURN_FLOAT8(retval); 7.2830 -} 7.2831 - 7.2832 -/* calculate fair distance (see README) between cluster and point with 7.2833 - precision denoted by sample count ("<=> operator) in SQL */ 7.2834 -PG_FUNCTION_INFO_V1(pgl_ecluster_epoint_sc_fair_distance); 7.2835 -Datum pgl_ecluster_epoint_sc_fair_distance(PG_FUNCTION_ARGS) { 7.2836 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 7.2837 - pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(1); 7.2838 - double retval = pgl_fair_distance( 7.2839 - &search->point, cluster, search->samples 7.2840 - ); 7.2841 - PG_FREE_IF_COPY(cluster, 0); 7.2842 - PG_RETURN_FLOAT8(retval); 7.2843 -} 7.2844 - 7.2845 - 7.2846 -/*-----------------------------------------------------------* 7.2847 - * B-tree comparison operators and index support functions * 7.2848 - *-----------------------------------------------------------*/ 7.2849 - 7.2850 -/* macro for a B-tree operator (without detoasting) */ 7.2851 -#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \ 7.2852 - PG_FUNCTION_INFO_V1(func); \ 7.2853 - Datum func(PG_FUNCTION_ARGS) { \ 7.2854 - type *a = (type *)PG_GETARG_POINTER(0); \ 7.2855 - type *b = (type *)PG_GETARG_POINTER(1); \ 7.2856 - PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \ 7.2857 - } 7.2858 - 7.2859 -/* macro for a B-tree comparison function (without detoasting) */ 7.2860 -#define PGL_BTREE_CMP(func, type, cmpfunc) \ 7.2861 - PG_FUNCTION_INFO_V1(func); \ 7.2862 - Datum func(PG_FUNCTION_ARGS) { \ 7.2863 - type *a = (type *)PG_GETARG_POINTER(0); \ 7.2864 - type *b = (type *)PG_GETARG_POINTER(1); \ 7.2865 - PG_RETURN_INT32(cmpfunc(a, b)); \ 7.2866 - } 7.2867 - 7.2868 -/* macro for a B-tree operator (with detoasting) */ 7.2869 -#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \ 7.2870 - PG_FUNCTION_INFO_V1(func); \ 7.2871 - Datum func(PG_FUNCTION_ARGS) { \ 7.2872 - bool res; \ 7.2873 - type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \ 7.2874 - type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \ 7.2875 - res = cmpfunc(a, b) oper 0; \ 7.2876 - PG_FREE_IF_COPY(a, 0); \ 7.2877 - PG_FREE_IF_COPY(b, 1); \ 7.2878 - PG_RETURN_BOOL(res); \ 7.2879 - } 7.2880 - 7.2881 -/* macro for a B-tree comparison function (with detoasting) */ 7.2882 -#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \ 7.2883 - PG_FUNCTION_INFO_V1(func); \ 7.2884 - Datum func(PG_FUNCTION_ARGS) { \ 7.2885 - int32_t res; \ 7.2886 - type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \ 7.2887 - type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \ 7.2888 - res = cmpfunc(a, b); \ 7.2889 - PG_FREE_IF_COPY(a, 0); \ 7.2890 - PG_FREE_IF_COPY(b, 1); \ 7.2891 - PG_RETURN_INT32(res); \ 7.2892 - } 7.2893 - 7.2894 -/* B-tree operators and comparison function for point */ 7.2895 -PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <) 7.2896 -PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=) 7.2897 -PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==) 7.2898 -PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=) 7.2899 -PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=) 7.2900 -PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >) 7.2901 -PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp) 7.2902 - 7.2903 -/* B-tree operators and comparison function for box */ 7.2904 -PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <) 7.2905 -PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=) 7.2906 -PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==) 7.2907 -PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=) 7.2908 -PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=) 7.2909 -PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >) 7.2910 -PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp) 7.2911 - 7.2912 -/* B-tree operators and comparison function for circle */ 7.2913 -PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <) 7.2914 -PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=) 7.2915 -PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==) 7.2916 -PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=) 7.2917 -PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=) 7.2918 -PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >) 7.2919 -PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp) 7.2920 - 7.2921 - 7.2922 -/*--------------------------------* 7.2923 - * GiST index support functions * 7.2924 - *--------------------------------*/ 7.2925 - 7.2926 -/* GiST "consistent" support function */ 7.2927 -PG_FUNCTION_INFO_V1(pgl_gist_consistent); 7.2928 -Datum pgl_gist_consistent(PG_FUNCTION_ARGS) { 7.2929 - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 7.2930 - pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key); 7.2931 - StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); 7.2932 - bool *recheck = (bool *)PG_GETARG_POINTER(4); 7.2933 - /* demand recheck because index and query methods are lossy */ 7.2934 - *recheck = true; 7.2935 - /* strategy number aliases for different operators using the same strategy */ 7.2936 - strategy %= 100; 7.2937 - /* strategy number 11: equality of two points */ 7.2938 - if (strategy == 11) { 7.2939 - /* query datum is another point */ 7.2940 - pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 7.2941 - /* convert other point to key */ 7.2942 - pgl_pointkey querykey; 7.2943 - pgl_point_to_key(query, querykey); 7.2944 - /* return true if both keys overlap */ 7.2945 - PG_RETURN_BOOL(pgl_keys_overlap(key, querykey)); 7.2946 - } 7.2947 - /* strategy number 13: equality of two circles */ 7.2948 - if (strategy == 13) { 7.2949 - /* query datum is another circle */ 7.2950 - pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 7.2951 - /* convert other circle to key */ 7.2952 - pgl_areakey querykey; 7.2953 - pgl_circle_to_key(query, querykey); 7.2954 - /* return true if both keys overlap */ 7.2955 - PG_RETURN_BOOL(pgl_keys_overlap(key, querykey)); 7.2956 - } 7.2957 - /* for all remaining strategies, keys on empty objects produce no match */ 7.2958 - /* (check necessary because query radius may be infinite) */ 7.2959 - if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false); 7.2960 - /* strategy number 21: overlapping with point */ 7.2961 - if (strategy == 21) { 7.2962 - /* query datum is a point */ 7.2963 - pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 7.2964 - /* return true if estimated distance (allowed to be smaller than real 7.2965 - distance) between index key and point is zero */ 7.2966 - PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0); 7.2967 - } 7.2968 - /* strategy number 22: (point) overlapping with box */ 7.2969 - if (strategy == 22) { 7.2970 - /* query datum is a box */ 7.2971 - pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1); 7.2972 - /* determine bounding box of indexed key */ 7.2973 - pgl_box keybox; 7.2974 - pgl_key_to_box(key, &keybox); 7.2975 - /* return true if query box overlaps with bounding box of indexed key */ 7.2976 - PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox)); 7.2977 - } 7.2978 - /* strategy number 23: overlapping with circle */ 7.2979 - if (strategy == 23) { 7.2980 - /* query datum is a circle */ 7.2981 - pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 7.2982 - /* return true if estimated distance (allowed to be smaller than real 7.2983 - distance) between index key and circle center is smaller than radius */ 7.2984 - PG_RETURN_BOOL( 7.2985 - (1.0-PGL_SPHEROID_F) * /* safety margin for lossy operator */ 7.2986 - pgl_estimate_key_distance(key, &(query->center)) 7.2987 - <= query->radius 7.2988 - ); 7.2989 - } 7.2990 - /* strategy number 24: overlapping with cluster */ 7.2991 - if (strategy == 24) { 7.2992 - bool retval; /* return value */ 7.2993 - /* query datum is a cluster */ 7.2994 - pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.2995 - /* return true if estimated distance (allowed to be smaller than real 7.2996 - distance) between index key and circle center is smaller than radius */ 7.2997 - retval = ( 7.2998 - (1.0-PGL_SPHEROID_F) * /* safety margin for lossy operator */ 7.2999 - pgl_estimate_key_distance(key, &(query->bounding.center)) 7.3000 - <= query->bounding.radius 7.3001 - ); 7.3002 - PG_FREE_IF_COPY(query, 1); /* free detoasted cluster (if copy) */ 7.3003 - PG_RETURN_BOOL(retval); 7.3004 - } 7.3005 - /* throw error for any unknown strategy number */ 7.3006 - elog(ERROR, "unrecognized strategy number: %d", strategy); 7.3007 -} 7.3008 - 7.3009 -/* GiST "union" support function */ 7.3010 -PG_FUNCTION_INFO_V1(pgl_gist_union); 7.3011 -Datum pgl_gist_union(PG_FUNCTION_ARGS) { 7.3012 - GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); 7.3013 - pgl_keyptr out; /* return value (to be palloc'ed) */ 7.3014 - int i; 7.3015 - /* determine key size */ 7.3016 - size_t keysize = PGL_KEY_IS_AREAKEY( 7.3017 - (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key) 7.3018 - ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey); 7.3019 - /* begin with first key as result */ 7.3020 - out = palloc(keysize); 7.3021 - memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize); 7.3022 - /* unite current result with second, third, etc. key */ 7.3023 - for (i=1; i<entryvec->n; i++) { 7.3024 - pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)); 7.3025 - } 7.3026 - /* return result */ 7.3027 - PG_RETURN_POINTER(out); 7.3028 -} 7.3029 - 7.3030 -/* GiST "compress" support function for indicis on points */ 7.3031 -PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint); 7.3032 -Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) { 7.3033 - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 7.3034 - GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 7.3035 - /* only transform new leaves */ 7.3036 - if (entry->leafkey) { 7.3037 - /* get point to be transformed */ 7.3038 - pgl_point *point = (pgl_point *)DatumGetPointer(entry->key); 7.3039 - /* allocate memory for key */ 7.3040 - pgl_keyptr key = palloc(sizeof(pgl_pointkey)); 7.3041 - /* transform point to key */ 7.3042 - pgl_point_to_key(point, key); 7.3043 - /* create new GISTENTRY structure as return value */ 7.3044 - retval = palloc(sizeof(GISTENTRY)); 7.3045 - gistentryinit( 7.3046 - *retval, PointerGetDatum(key), 7.3047 - entry->rel, entry->page, entry->offset, false 7.3048 - ); 7.3049 - } else { 7.3050 - /* inner nodes have already been transformed */ 7.3051 - retval = entry; 7.3052 - } 7.3053 - /* return pointer to old or new GISTENTRY structure */ 7.3054 - PG_RETURN_POINTER(retval); 7.3055 -} 7.3056 - 7.3057 -/* GiST "compress" support function for indicis on circles */ 7.3058 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle); 7.3059 -Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) { 7.3060 - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 7.3061 - GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 7.3062 - /* only transform new leaves */ 7.3063 - if (entry->leafkey) { 7.3064 - /* get circle to be transformed */ 7.3065 - pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key); 7.3066 - /* allocate memory for key */ 7.3067 - pgl_keyptr key = palloc(sizeof(pgl_areakey)); 7.3068 - /* transform circle to key */ 7.3069 - pgl_circle_to_key(circle, key); 7.3070 - /* create new GISTENTRY structure as return value */ 7.3071 - retval = palloc(sizeof(GISTENTRY)); 7.3072 - gistentryinit( 7.3073 - *retval, PointerGetDatum(key), 7.3074 - entry->rel, entry->page, entry->offset, false 7.3075 - ); 7.3076 - } else { 7.3077 - /* inner nodes have already been transformed */ 7.3078 - retval = entry; 7.3079 - } 7.3080 - /* return pointer to old or new GISTENTRY structure */ 7.3081 - PG_RETURN_POINTER(retval); 7.3082 -} 7.3083 - 7.3084 -/* GiST "compress" support function for indices on clusters */ 7.3085 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster); 7.3086 -Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) { 7.3087 - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 7.3088 - GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 7.3089 - /* only transform new leaves */ 7.3090 - if (entry->leafkey) { 7.3091 - /* get cluster to be transformed (detoasting necessary!) */ 7.3092 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key); 7.3093 - /* allocate memory for key */ 7.3094 - pgl_keyptr key = palloc(sizeof(pgl_areakey)); 7.3095 - /* transform cluster to key */ 7.3096 - pgl_circle_to_key(&(cluster->bounding), key); 7.3097 - /* create new GISTENTRY structure as return value */ 7.3098 - retval = palloc(sizeof(GISTENTRY)); 7.3099 - gistentryinit( 7.3100 - *retval, PointerGetDatum(key), 7.3101 - entry->rel, entry->page, entry->offset, false 7.3102 - ); 7.3103 - /* free detoasted datum */ 7.3104 - if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster); 7.3105 - } else { 7.3106 - /* inner nodes have already been transformed */ 7.3107 - retval = entry; 7.3108 - } 7.3109 - /* return pointer to old or new GISTENTRY structure */ 7.3110 - PG_RETURN_POINTER(retval); 7.3111 -} 7.3112 - 7.3113 -/* GiST "decompress" support function for indices */ 7.3114 -PG_FUNCTION_INFO_V1(pgl_gist_decompress); 7.3115 -Datum pgl_gist_decompress(PG_FUNCTION_ARGS) { 7.3116 - /* return passed pointer without transformation */ 7.3117 - PG_RETURN_POINTER(PG_GETARG_POINTER(0)); 7.3118 -} 7.3119 - 7.3120 -/* GiST "penalty" support function */ 7.3121 -PG_FUNCTION_INFO_V1(pgl_gist_penalty); 7.3122 -Datum pgl_gist_penalty(PG_FUNCTION_ARGS) { 7.3123 - GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0); 7.3124 - GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1); 7.3125 - float *penalty = (float *)PG_GETARG_POINTER(2); 7.3126 - /* get original key and key to insert */ 7.3127 - pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key); 7.3128 - pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key); 7.3129 - /* copy original key */ 7.3130 - union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key; 7.3131 - if (PGL_KEY_IS_AREAKEY(orig)) { 7.3132 - memcpy(union_key.areakey, orig, sizeof(union_key.areakey)); 7.3133 - } else { 7.3134 - memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey)); 7.3135 - } 7.3136 - /* calculate union of both keys */ 7.3137 - pgl_unite_keys((pgl_keyptr)&union_key, new); 7.3138 - /* penalty equal to reduction of key length (logarithm of added area) */ 7.3139 - /* (return value by setting referenced value and returning pointer) */ 7.3140 - *penalty = ( 7.3141 - PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key) 7.3142 - ); 7.3143 - PG_RETURN_POINTER(penalty); 7.3144 -} 7.3145 - 7.3146 -/* GiST "picksplit" support function */ 7.3147 -PG_FUNCTION_INFO_V1(pgl_gist_picksplit); 7.3148 -Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) { 7.3149 - GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); 7.3150 - GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1); 7.3151 - OffsetNumber i; /* between FirstOffsetNumber and entryvec->n (exclusive) */ 7.3152 - union { 7.3153 - pgl_pointkey pointkey; 7.3154 - pgl_areakey areakey; 7.3155 - } union_all; /* union of all keys (to be calculated from scratch) 7.3156 - (later cut in half) */ 7.3157 - int is_areakey = PGL_KEY_IS_AREAKEY( 7.3158 - (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key) 7.3159 - ); 7.3160 - int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey); 7.3161 - pgl_keyptr unionL = palloc(keysize); /* union of keys that go left */ 7.3162 - pgl_keyptr unionR = palloc(keysize); /* union of keys that go right */ 7.3163 - pgl_keyptr key; /* current key to be processed */ 7.3164 - /* allocate memory for array of left and right keys, set counts to zero */ 7.3165 - v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber)); 7.3166 - v->spl_nleft = 0; 7.3167 - v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber)); 7.3168 - v->spl_nright = 0; 7.3169 - /* calculate union of all keys from scratch */ 7.3170 - memcpy( 7.3171 - (pgl_keyptr)&union_all, 7.3172 - (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key), 7.3173 - keysize 7.3174 - ); 7.3175 - for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) { 7.3176 - pgl_unite_keys( 7.3177 - (pgl_keyptr)&union_all, 7.3178 - (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key) 7.3179 - ); 7.3180 - } 7.3181 - /* check if trivial split is necessary due to exhausted key length */ 7.3182 - /* (Note: keys for empty objects must have node depth set to maximum) */ 7.3183 - if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == ( 7.3184 - is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH 7.3185 - )) { 7.3186 - /* half of all keys go left */ 7.3187 - for ( 7.3188 - i=FirstOffsetNumber; 7.3189 - i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2; 7.3190 - i=OffsetNumberNext(i) 7.3191 - ) { 7.3192 - /* pointer to current key */ 7.3193 - key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 7.3194 - /* update unionL */ 7.3195 - /* check if key is first key that goes left */ 7.3196 - if (!v->spl_nleft) { 7.3197 - /* first key that goes left is just copied to unionL */ 7.3198 - memcpy(unionL, key, keysize); 7.3199 - } else { 7.3200 - /* unite current value and next key */ 7.3201 - pgl_unite_keys(unionL, key); 7.3202 - } 7.3203 - /* append offset number to list of keys that go left */ 7.3204 - v->spl_left[v->spl_nleft++] = i; 7.3205 - } 7.3206 - /* other half goes right */ 7.3207 - for ( 7.3208 - i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2; 7.3209 - i<entryvec->n; 7.3210 - i=OffsetNumberNext(i) 7.3211 - ) { 7.3212 - /* pointer to current key */ 7.3213 - key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 7.3214 - /* update unionR */ 7.3215 - /* check if key is first key that goes right */ 7.3216 - if (!v->spl_nright) { 7.3217 - /* first key that goes right is just copied to unionR */ 7.3218 - memcpy(unionR, key, keysize); 7.3219 - } else { 7.3220 - /* unite current value and next key */ 7.3221 - pgl_unite_keys(unionR, key); 7.3222 - } 7.3223 - /* append offset number to list of keys that go right */ 7.3224 - v->spl_right[v->spl_nright++] = i; 7.3225 - } 7.3226 - } 7.3227 - /* otherwise, a non-trivial split is possible */ 7.3228 - else { 7.3229 - /* cut covered area in half */ 7.3230 - /* (union_all then refers to area of keys that go left) */ 7.3231 - /* check if union of all keys covers empty and non-empty objects */ 7.3232 - if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) { 7.3233 - /* if yes, split into empty and non-empty objects */ 7.3234 - pgl_key_set_empty((pgl_keyptr)&union_all); 7.3235 - } else { 7.3236 - /* otherwise split by next bit */ 7.3237 - ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++; 7.3238 - /* NOTE: type bit conserved */ 7.3239 - } 7.3240 - /* determine for each key if it goes left or right */ 7.3241 - for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) { 7.3242 - /* pointer to current key */ 7.3243 - key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 7.3244 - /* keys within one half of the area go left */ 7.3245 - if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) { 7.3246 - /* update unionL */ 7.3247 - /* check if key is first key that goes left */ 7.3248 - if (!v->spl_nleft) { 7.3249 - /* first key that goes left is just copied to unionL */ 7.3250 - memcpy(unionL, key, keysize); 7.3251 - } else { 7.3252 - /* unite current value of unionL and processed key */ 7.3253 - pgl_unite_keys(unionL, key); 7.3254 - } 7.3255 - /* append offset number to list of keys that go left */ 7.3256 - v->spl_left[v->spl_nleft++] = i; 7.3257 - } 7.3258 - /* the other keys go right */ 7.3259 - else { 7.3260 - /* update unionR */ 7.3261 - /* check if key is first key that goes right */ 7.3262 - if (!v->spl_nright) { 7.3263 - /* first key that goes right is just copied to unionR */ 7.3264 - memcpy(unionR, key, keysize); 7.3265 - } else { 7.3266 - /* unite current value of unionR and processed key */ 7.3267 - pgl_unite_keys(unionR, key); 7.3268 - } 7.3269 - /* append offset number to list of keys that go right */ 7.3270 - v->spl_right[v->spl_nright++] = i; 7.3271 - } 7.3272 - } 7.3273 - } 7.3274 - /* store unions in return value */ 7.3275 - v->spl_ldatum = PointerGetDatum(unionL); 7.3276 - v->spl_rdatum = PointerGetDatum(unionR); 7.3277 - /* return all results */ 7.3278 - PG_RETURN_POINTER(v); 7.3279 -} 7.3280 - 7.3281 -/* GiST "same"/"equal" support function */ 7.3282 -PG_FUNCTION_INFO_V1(pgl_gist_same); 7.3283 -Datum pgl_gist_same(PG_FUNCTION_ARGS) { 7.3284 - pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0); 7.3285 - pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1); 7.3286 - bool *boolptr = (bool *)PG_GETARG_POINTER(2); 7.3287 - /* two keys are equal if they are binary equal */ 7.3288 - /* (return result by setting referenced boolean and returning pointer) */ 7.3289 - *boolptr = !memcmp( 7.3290 - key1, 7.3291 - key2, 7.3292 - PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey) 7.3293 - ); 7.3294 - PG_RETURN_POINTER(boolptr); 7.3295 -} 7.3296 - 7.3297 -/* GiST "distance" support function */ 7.3298 -PG_FUNCTION_INFO_V1(pgl_gist_distance); 7.3299 -Datum pgl_gist_distance(PG_FUNCTION_ARGS) { 7.3300 - GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); 7.3301 - pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key); 7.3302 - StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); 7.3303 - bool *recheck = (bool *)PG_GETARG_POINTER(4); 7.3304 - double distance; /* return value */ 7.3305 - /* demand recheck because distance is just an estimation */ 7.3306 - /* (real distance may be bigger) */ 7.3307 - *recheck = true; 7.3308 - /* strategy number aliases for different operators using the same strategy */ 7.3309 - strategy %= 100; 7.3310 - /* strategy number 31: distance to point */ 7.3311 - if (strategy == 31) { 7.3312 - /* query datum is a point */ 7.3313 - pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 7.3314 - /* use pgl_estimate_pointkey_distance() function to compute result */ 7.3315 - distance = pgl_estimate_key_distance(key, query); 7.3316 - /* avoid infinity (reserved!) */ 7.3317 - if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 7.3318 - /* return result */ 7.3319 - PG_RETURN_FLOAT8(distance); 7.3320 - } 7.3321 - /* strategy number 33: distance to circle */ 7.3322 - if (strategy == 33) { 7.3323 - /* query datum is a circle */ 7.3324 - pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 7.3325 - /* estimate distance to circle center and substract circle radius */ 7.3326 - distance = ( 7.3327 - pgl_estimate_key_distance(key, &(query->center)) - query->radius 7.3328 - ); 7.3329 - /* convert non-positive values to zero and avoid infinity (reserved!) */ 7.3330 - if (distance <= 0) distance = 0; 7.3331 - else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 7.3332 - /* return result */ 7.3333 - PG_RETURN_FLOAT8(distance); 7.3334 - } 7.3335 - /* strategy number 34: distance to cluster */ 7.3336 - if (strategy == 34) { 7.3337 - /* query datum is a cluster */ 7.3338 - pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 7.3339 - /* estimate distance to bounding center and substract bounding radius */ 7.3340 - distance = ( 7.3341 - pgl_estimate_key_distance(key, &(query->bounding.center)) - 7.3342 - query->bounding.radius 7.3343 - ); 7.3344 - /* convert non-positive values to zero and avoid infinity (reserved!) */ 7.3345 - if (distance <= 0) distance = 0; 7.3346 - else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 7.3347 - /* free detoasted cluster (if copy) */ 7.3348 - PG_FREE_IF_COPY(query, 1); 7.3349 - /* return result */ 7.3350 - PG_RETURN_FLOAT8(distance); 7.3351 - } 7.3352 - /* throw error for any unknown strategy number */ 7.3353 - elog(ERROR, "unrecognized strategy number: %d", strategy); 7.3354 -} 7.3355 -
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/latlon-v0010.c Wed Feb 12 11:08:37 2020 +0100 8.3 @@ -0,0 +1,3352 @@ 8.4 + 8.5 +/*-------------* 8.6 + * C prelude * 8.7 + *-------------*/ 8.8 + 8.9 +#include "postgres.h" 8.10 +#include "fmgr.h" 8.11 +#include "libpq/pqformat.h" 8.12 +#include "access/gist.h" 8.13 +#include "access/stratnum.h" 8.14 +#include "utils/array.h" 8.15 +#include <limits.h> 8.16 +#include <math.h> 8.17 + 8.18 +#ifdef PG_MODULE_MAGIC 8.19 +PG_MODULE_MAGIC; 8.20 +#endif 8.21 + 8.22 +#if INT_MAX < 2147483647 8.23 +#error Expected int type to be at least 32 bit wide 8.24 +#endif 8.25 + 8.26 + 8.27 +/*---------------------------------* 8.28 + * distance calculation on earth * 8.29 + * (using WGS-84 spheroid) * 8.30 + *---------------------------------*/ 8.31 + 8.32 +/* WGS-84 spheroid with following parameters: 8.33 + semi-major axis a = 6378137 8.34 + semi-minor axis b = a * (1 - 1/298.257223563) 8.35 + estimated diameter = 2 * (2*a+b)/3 8.36 +*/ 8.37 +#define PGL_SPHEROID_A 6378137.0 /* semi major axis */ 8.38 +#define PGL_SPHEROID_F (1.0/298.257223563) /* flattening */ 8.39 +#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F)) 8.40 +#define PGL_EPS2 ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \ 8.41 + PGL_SPHEROID_B * PGL_SPHEROID_B ) / \ 8.42 + ( PGL_SPHEROID_A * PGL_SPHEROID_A ) ) 8.43 +#define PGL_SUBEPS2 (1.0-PGL_EPS2) 8.44 +#define PGL_RADIUS ((2.0*PGL_SPHEROID_A + PGL_SPHEROID_B) / 3.0) 8.45 +#define PGL_DIAMETER (2.0 * PGL_RADIUS) 8.46 +#define PGL_SCALE (PGL_SPHEROID_A / PGL_DIAMETER) /* semi-major ref. */ 8.47 +#define PGL_MAXDIST (PGL_RADIUS * M_PI) /* maximum distance */ 8.48 +#define PGL_FADELIMIT (PGL_MAXDIST / 3.0) /* 1/6 circumference */ 8.49 + 8.50 +/* calculate distance between two points on earth (given in degrees) */ 8.51 +static inline double pgl_distance( 8.52 + double lat1, double lon1, double lat2, double lon2 8.53 +) { 8.54 + float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin; 8.55 + float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t; 8.56 + /* normalize delta longitude (lon2 > 0 && lon1 = 0) */ 8.57 + /* lon1 = 0 (not used anymore) */ 8.58 + lon2 = fabs(lon2-lon1); 8.59 + /* convert to radians (first divide, then multiply) */ 8.60 + lat1 = (lat1 / 180.0) * M_PI; 8.61 + lat2 = (lat2 / 180.0) * M_PI; 8.62 + lon2 = (lon2 / 180.0) * M_PI; 8.63 + /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point 8.64 + operations (lon2 >= lon1 is already ensured in a previous step) */ 8.65 + if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; } 8.66 + /* calculate 3d coordinates on scaled ellipsoid which has an average diameter 8.67 + of 1.0 */ 8.68 + lat1cos = cos(lat1); lat1sin = sin(lat1); 8.69 + lat2cos = cos(lat2); lat2sin = sin(lat2); 8.70 + lon2cos = cos(lon2); lon2sin = sin(lon2); 8.71 + nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin); 8.72 + nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin); 8.73 + x1 = nphi1 * lat1cos; 8.74 + z1 = nphi1 * PGL_SUBEPS2 * lat1sin; 8.75 + x2 = nphi2 * lat2cos * lon2cos; 8.76 + y2 = nphi2 * lat2cos * lon2sin; 8.77 + z2 = nphi2 * PGL_SUBEPS2 * lat2sin; 8.78 + /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */ 8.79 + g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1)); 8.80 + /* convert tunnel distance through scaled ellipsoid to approximated surface 8.81 + distance on original ellipsoid */ 8.82 + if (g > 1.0) g = 1.0; 8.83 + s = PGL_DIAMETER * asin(g); 8.84 + /* return result only if small enough to be precise (less than 1/3 of 8.85 + maximum possible distance) */ 8.86 + if (s <= PGL_FADELIMIT) return s; 8.87 + /* calculate tunnel distance to antipodal point through scaled ellipsoid */ 8.88 + g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1)); 8.89 + /* convert tunnel distance to antipodal point through scaled ellipsoid to 8.90 + approximated surface distance to antipodal point on original ellipsoid */ 8.91 + if (g > 1.0) g = 1.0; 8.92 + t = PGL_DIAMETER * asin(g); 8.93 + /* surface distance between original points can now be approximated by 8.94 + substracting antipodal distance from maximum possible distance; 8.95 + return result only if small enough (less than 1/3 of maximum possible 8.96 + distance) */ 8.97 + if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t; 8.98 + /* otherwise crossfade direct and antipodal result to ensure monotonicity */ 8.99 + return ( 8.100 + (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) / 8.101 + (s + t - 2*PGL_FADELIMIT) 8.102 + ); 8.103 +} 8.104 + 8.105 +/* finite distance that can not be reached on earth */ 8.106 +#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST) 8.107 + 8.108 + 8.109 +/*--------------------------------* 8.110 + * simple geographic data types * 8.111 + *--------------------------------*/ 8.112 + 8.113 +/* point on earth given by latitude and longitude in degrees */ 8.114 +/* (type "epoint" in SQL) */ 8.115 +typedef struct { 8.116 + double lat; /* between -90 and 90 (both inclusive) */ 8.117 + double lon; /* between -180 and 180 (both inclusive) */ 8.118 +} pgl_point; 8.119 + 8.120 +/* box delimited by two parallels and two meridians (all in degrees) */ 8.121 +/* (type "ebox" in SQL) */ 8.122 +typedef struct { 8.123 + double lat_min; /* between -90 and 90 (both inclusive) */ 8.124 + double lat_max; /* between -90 and 90 (both inclusive) */ 8.125 + double lon_min; /* between -180 and 180 (both inclusive) */ 8.126 + double lon_max; /* between -180 and 180 (both inclusive) */ 8.127 + /* if lat_min > lat_max, then box is empty */ 8.128 + /* if lon_min > lon_max, then 180th meridian is crossed */ 8.129 +} pgl_box; 8.130 + 8.131 +/* circle on earth surface (for radial searches with fixed radius) */ 8.132 +/* (type "ecircle" in SQL) */ 8.133 +typedef struct { 8.134 + pgl_point center; 8.135 + double radius; /* positive (including +0 but excluding -0), or -INFINITY */ 8.136 + /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point), 8.137 + zero radius (0) denotes a single point, 8.138 + a finite radius (0 < radius < INFINITY) denotes a filled circle, and 8.139 + a radius of INFINITY is valid and means complete coverage of earth. */ 8.140 +} pgl_circle; 8.141 + 8.142 + 8.143 +/*----------------------------------* 8.144 + * geographic "cluster" data type * 8.145 + *----------------------------------*/ 8.146 + 8.147 +/* A cluster is a collection of points, paths, outlines, and polygons. If two 8.148 + polygons in a cluster overlap, the area covered by both polygons does not 8.149 + belong to the cluster. This way, a cluster can be used to describe complex 8.150 + shapes like polygons with holes. Outlines are non-filled polygons. Paths are 8.151 + open by default (i.e. the last point in the list is not connected with the 8.152 + first point in the list). Note that each outline or polygon in a cluster 8.153 + must cover a longitude range of less than 180 degrees to avoid ambiguities. 8.154 + Areas which are larger may be split into multiple polygons. */ 8.155 + 8.156 +/* maximum number of points in a cluster */ 8.157 +/* (limited to avoid integer overflows, e.g. when allocating memory) */ 8.158 +#define PGL_CLUSTER_MAXPOINTS 16777216 8.159 + 8.160 +/* types of cluster entries */ 8.161 +#define PGL_ENTRY_POINT 1 /* a point */ 8.162 +#define PGL_ENTRY_PATH 2 /* a path from first point to last point */ 8.163 +#define PGL_ENTRY_OUTLINE 3 /* a non-filled polygon with given vertices */ 8.164 +#define PGL_ENTRY_POLYGON 4 /* a filled polygon with given vertices */ 8.165 + 8.166 +/* Entries of a cluster are described by two different structs: pgl_newentry 8.167 + and pgl_entry. The first is used only during construction of a cluster, the 8.168 + second is used in all other cases (e.g. when reading clusters from the 8.169 + database, performing operations, etc). */ 8.170 + 8.171 +/* entry for new geographic cluster during construction of that cluster */ 8.172 +typedef struct { 8.173 + int32_t entrytype; 8.174 + int32_t npoints; 8.175 + pgl_point *points; /* pointer to an array of points (pgl_point) */ 8.176 +} pgl_newentry; 8.177 + 8.178 +/* entry of geographic cluster */ 8.179 +typedef struct { 8.180 + int32_t entrytype; /* type of entry: point, path, outline, polygon */ 8.181 + int32_t npoints; /* number of stored points (set to 1 for point entry) */ 8.182 + int32_t offset; /* offset of pgl_point array from cluster base address */ 8.183 + /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */ 8.184 +} pgl_entry; 8.185 + 8.186 +/* geographic cluster which is a collection of points, (open) paths, polygons, 8.187 + and outlines (non-filled polygons) */ 8.188 +typedef struct { 8.189 + char header[VARHDRSZ]; /* PostgreSQL header for variable size data types */ 8.190 + int32_t nentries; /* number of stored points */ 8.191 + pgl_circle bounding; /* bounding circle */ 8.192 + /* Note: bounding circle ensures alignment of pgl_cluster for points */ 8.193 + pgl_entry entries[FLEXIBLE_ARRAY_MEMBER]; /* var-length data */ 8.194 +} pgl_cluster; 8.195 + 8.196 +/* macro to determine memory alignment of points */ 8.197 +/* (needed to store pgl_point array after entries in pgl_cluster) */ 8.198 +typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment; 8.199 +#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned) 8.200 + 8.201 +/* macro to extract a pointer to the array of points of a cluster entry */ 8.202 +#define PGL_ENTRY_POINTS(cluster, idx) \ 8.203 + ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset)) 8.204 + 8.205 +/* convert pgl_newentry array to pgl_cluster */ 8.206 +/* NOTE: requires pgl_finalize_cluster to be called to finalize result */ 8.207 +static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) { 8.208 + int i; /* index of current entry */ 8.209 + int npoints = 0; /* number of points in whole cluster */ 8.210 + int entry_npoints; /* number of points in current entry */ 8.211 + int points_offset = PGL_POINT_ALIGNMENT * ( 8.212 + ( offsetof(pgl_cluster, entries) + 8.213 + nentries * sizeof(pgl_entry) + 8.214 + PGL_POINT_ALIGNMENT - 1 8.215 + ) / PGL_POINT_ALIGNMENT 8.216 + ); /* offset of pgl_point array from base address (considering alignment) */ 8.217 + pgl_cluster *cluster; /* new cluster to be returned */ 8.218 + /* determine total number of points */ 8.219 + for (i=0; i<nentries; i++) npoints += entries[i].npoints; 8.220 + /* allocate memory for cluster (including entries and points) */ 8.221 + cluster = palloc(points_offset + npoints * sizeof(pgl_point)); 8.222 + /* re-count total number of points to determine offset for each entry */ 8.223 + npoints = 0; 8.224 + /* copy entries and points */ 8.225 + for (i=0; i<nentries; i++) { 8.226 + /* determine number of points in entry */ 8.227 + entry_npoints = entries[i].npoints; 8.228 + /* copy entry */ 8.229 + cluster->entries[i].entrytype = entries[i].entrytype; 8.230 + cluster->entries[i].npoints = entry_npoints; 8.231 + /* calculate offset (in bytes) of pgl_point array */ 8.232 + cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point); 8.233 + /* copy points */ 8.234 + memcpy( 8.235 + PGL_ENTRY_POINTS(cluster, i), 8.236 + entries[i].points, 8.237 + entry_npoints * sizeof(pgl_point) 8.238 + ); 8.239 + /* update total number of points processed */ 8.240 + npoints += entry_npoints; 8.241 + } 8.242 + /* set number of entries in cluster */ 8.243 + cluster->nentries = nentries; 8.244 + /* set PostgreSQL header for variable sized data */ 8.245 + SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point)); 8.246 + /* return newly created cluster */ 8.247 + return cluster; 8.248 +} 8.249 + 8.250 + 8.251 +/*----------------------------------------------* 8.252 + * Geographic point with integer sample count * 8.253 + * (needed for fair distance calculation) * 8.254 + *----------------------------------------------*/ 8.255 + 8.256 +typedef struct { 8.257 + pgl_point point; /* NOTE: point first to allow C cast to pgl_point */ 8.258 + int32 samples; 8.259 +} pgl_point_sc; 8.260 + 8.261 + 8.262 +/*----------------------------------------* 8.263 + * C functions on geographic data types * 8.264 + *----------------------------------------*/ 8.265 + 8.266 +/* round latitude or longitude to 12 digits after decimal point */ 8.267 +static inline double pgl_round(double val) { 8.268 + return round(val * 1e12) / 1e12; 8.269 +} 8.270 + 8.271 +/* compare two points */ 8.272 +/* (equality when same point on earth is described, otherwise an arbitrary 8.273 + linear order) */ 8.274 +static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) { 8.275 + double lon1, lon2; /* modified longitudes for special cases */ 8.276 + /* use latitude as first ordering criterion */ 8.277 + if (point1->lat < point2->lat) return -1; 8.278 + if (point1->lat > point2->lat) return 1; 8.279 + /* determine modified longitudes (considering special case of poles and 8.280 + 180th meridian which can be described as W180 or E180) */ 8.281 + if (point1->lat == -90 || point1->lat == 90) lon1 = 0; 8.282 + else if (point1->lon == 180) lon1 = -180; 8.283 + else lon1 = point1->lon; 8.284 + if (point2->lat == -90 || point2->lat == 90) lon2 = 0; 8.285 + else if (point2->lon == 180) lon2 = -180; 8.286 + else lon2 = point2->lon; 8.287 + /* use (modified) longitude as secondary ordering criterion */ 8.288 + if (lon1 < lon2) return -1; 8.289 + if (lon1 > lon2) return 1; 8.290 + /* no difference found, points are equal */ 8.291 + return 0; 8.292 +} 8.293 + 8.294 +/* compare two boxes */ 8.295 +/* (equality when same box on earth is described, otherwise an arbitrary linear 8.296 + order) */ 8.297 +static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) { 8.298 + /* two empty boxes are equal, and an empty box is always considered "less 8.299 + than" a non-empty box */ 8.300 + if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1; 8.301 + if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0; 8.302 + if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1; 8.303 + /* use southern border as first ordering criterion */ 8.304 + if (box1->lat_min < box2->lat_min) return -1; 8.305 + if (box1->lat_min > box2->lat_min) return 1; 8.306 + /* use northern border as second ordering criterion */ 8.307 + if (box1->lat_max < box2->lat_max) return -1; 8.308 + if (box1->lat_max > box2->lat_max) return 1; 8.309 + /* use western border as third ordering criterion */ 8.310 + if (box1->lon_min < box2->lon_min) return -1; 8.311 + if (box1->lon_min > box2->lon_min) return 1; 8.312 + /* use eastern border as fourth ordering criterion */ 8.313 + if (box1->lon_max < box2->lon_max) return -1; 8.314 + if (box1->lon_max > box2->lon_max) return 1; 8.315 + /* no difference found, boxes are equal */ 8.316 + return 0; 8.317 +} 8.318 + 8.319 +/* compare two circles */ 8.320 +/* (equality when same circle on earth is described, otherwise an arbitrary 8.321 + linear order) */ 8.322 +static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) { 8.323 + /* two circles with same infinite radius (positive or negative infinity) are 8.324 + considered equal independently of center point */ 8.325 + if ( 8.326 + !isfinite(circle1->radius) && !isfinite(circle2->radius) && 8.327 + circle1->radius == circle2->radius 8.328 + ) return 0; 8.329 + /* use radius as first ordering criterion */ 8.330 + if (circle1->radius < circle2->radius) return -1; 8.331 + if (circle1->radius > circle2->radius) return 1; 8.332 + /* use center point as secondary ordering criterion */ 8.333 + return pgl_point_cmp(&(circle1->center), &(circle2->center)); 8.334 +} 8.335 + 8.336 +/* set box to empty box*/ 8.337 +static void pgl_box_set_empty(pgl_box *box) { 8.338 + box->lat_min = INFINITY; 8.339 + box->lat_max = -INFINITY; 8.340 + box->lon_min = 0; 8.341 + box->lon_max = 0; 8.342 +} 8.343 + 8.344 +/* check if point is inside a box */ 8.345 +static bool pgl_point_in_box(pgl_point *point, pgl_box *box) { 8.346 + return ( 8.347 + point->lat >= box->lat_min && point->lat <= box->lat_max && ( 8.348 + (box->lon_min > box->lon_max) ? ( 8.349 + /* box crosses 180th meridian */ 8.350 + point->lon >= box->lon_min || point->lon <= box->lon_max 8.351 + ) : ( 8.352 + /* box does not cross the 180th meridian */ 8.353 + point->lon >= box->lon_min && point->lon <= box->lon_max 8.354 + ) 8.355 + ) 8.356 + ); 8.357 +} 8.358 + 8.359 +/* check if two boxes overlap */ 8.360 +static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) { 8.361 + return ( 8.362 + box2->lat_max >= box2->lat_min && /* ensure box2 is not empty */ 8.363 + ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) && 8.364 + ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && ( 8.365 + ( 8.366 + /* check if one and only one box crosses the 180th meridian */ 8.367 + ((box1->lon_min > box1->lon_max) ? 1 : 0) ^ 8.368 + ((box2->lon_min > box2->lon_max) ? 1 : 0) 8.369 + ) ? ( 8.370 + /* exactly one box crosses the 180th meridian */ 8.371 + box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min || 8.372 + box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max 8.373 + ) : ( 8.374 + /* no box or both boxes cross the 180th meridian */ 8.375 + ( 8.376 + (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) && 8.377 + (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max) 8.378 + ) || 8.379 + /* handle W180 == E180 */ 8.380 + ( box1->lon_min == -180 && box2->lon_max == 180 ) || 8.381 + ( box2->lon_min == -180 && box1->lon_max == 180 ) 8.382 + ) 8.383 + ) 8.384 + ); 8.385 +} 8.386 + 8.387 +/* check unambiguousness of east/west orientation of cluster entries and set 8.388 + bounding circle of cluster */ 8.389 +static bool pgl_finalize_cluster(pgl_cluster *cluster) { 8.390 + int i, j; /* i: index of entry, j: index of point in entry */ 8.391 + int npoints; /* number of points in entry */ 8.392 + int total_npoints = 0; /* total number of points in cluster */ 8.393 + pgl_point *points; /* points in entry */ 8.394 + int lon_dir; /* first point of entry west (-1) or east (+1) */ 8.395 + double lon_break = 0; /* antipodal longitude of first point in entry */ 8.396 + double lon_min, lon_max; /* covered longitude range of entry */ 8.397 + double value; /* temporary variable */ 8.398 + /* reset bounding circle center to empty circle at 0/0 coordinates */ 8.399 + cluster->bounding.center.lat = 0; 8.400 + cluster->bounding.center.lon = 0; 8.401 + cluster->bounding.radius = -INFINITY; 8.402 + /* if cluster is not empty */ 8.403 + if (cluster->nentries != 0) { 8.404 + /* iterate over all cluster entries and ensure they each cover a longitude 8.405 + range less than 180 degrees */ 8.406 + for (i=0; i<cluster->nentries; i++) { 8.407 + /* get properties of entry */ 8.408 + npoints = cluster->entries[i].npoints; 8.409 + points = PGL_ENTRY_POINTS(cluster, i); 8.410 + /* get longitude of first point of entry */ 8.411 + value = points[0].lon; 8.412 + /* initialize lon_min and lon_max with longitude of first point */ 8.413 + lon_min = value; 8.414 + lon_max = value; 8.415 + /* determine east/west orientation of first point and calculate antipodal 8.416 + longitude (Note: rounding required here) */ 8.417 + if (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); } 8.418 + else if (value > 0) { lon_dir = 1; lon_break = pgl_round(value - 180); } 8.419 + else lon_dir = 0; 8.420 + /* iterate over all other points in entry */ 8.421 + for (j=1; j<npoints; j++) { 8.422 + /* consider longitude wrap-around */ 8.423 + value = points[j].lon; 8.424 + if (lon_dir<0 && value>lon_break) value = pgl_round(value - 360); 8.425 + else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360); 8.426 + /* update lon_min and lon_max */ 8.427 + if (value < lon_min) lon_min = value; 8.428 + else if (value > lon_max) lon_max = value; 8.429 + /* return false if 180 degrees or more are covered */ 8.430 + if (lon_max - lon_min >= 180) return false; 8.431 + } 8.432 + } 8.433 + /* iterate over all points of all entries and calculate arbitrary center 8.434 + point for bounding circle (best if center point minimizes the radius, 8.435 + but some error is allowed here) */ 8.436 + for (i=0; i<cluster->nentries; i++) { 8.437 + /* get properties of entry */ 8.438 + npoints = cluster->entries[i].npoints; 8.439 + points = PGL_ENTRY_POINTS(cluster, i); 8.440 + /* check if first entry */ 8.441 + if (i==0) { 8.442 + /* get longitude of first point of first entry in whole cluster */ 8.443 + value = points[0].lon; 8.444 + /* initialize lon_min and lon_max with longitude of first point of 8.445 + first entry in whole cluster (used to determine if whole cluster 8.446 + covers a longitude range of 180 degrees or more) */ 8.447 + lon_min = value; 8.448 + lon_max = value; 8.449 + /* determine east/west orientation of first point and calculate 8.450 + antipodal longitude (Note: rounding not necessary here) */ 8.451 + if (value < 0) { lon_dir = -1; lon_break = value + 180; } 8.452 + else if (value > 0) { lon_dir = 1; lon_break = value - 180; } 8.453 + else lon_dir = 0; 8.454 + } 8.455 + /* iterate over all points in entry */ 8.456 + for (j=0; j<npoints; j++) { 8.457 + /* longitude wrap-around (Note: rounding not necessary here) */ 8.458 + value = points[j].lon; 8.459 + if (lon_dir < 0 && value > lon_break) value -= 360; 8.460 + else if (lon_dir > 0 && value < lon_break) value += 360; 8.461 + if (value < lon_min) lon_min = value; 8.462 + else if (value > lon_max) lon_max = value; 8.463 + /* set bounding circle to cover whole earth if 180 degrees or more are 8.464 + covered */ 8.465 + if (lon_max - lon_min >= 180) { 8.466 + cluster->bounding.center.lat = 0; 8.467 + cluster->bounding.center.lon = 0; 8.468 + cluster->bounding.radius = INFINITY; 8.469 + return true; 8.470 + } 8.471 + /* add point to bounding circle center (for average calculation) */ 8.472 + cluster->bounding.center.lat += points[j].lat; 8.473 + cluster->bounding.center.lon += value; 8.474 + } 8.475 + /* count total number of points */ 8.476 + total_npoints += npoints; 8.477 + } 8.478 + /* determine average latitude and longitude of cluster */ 8.479 + cluster->bounding.center.lat /= total_npoints; 8.480 + cluster->bounding.center.lon /= total_npoints; 8.481 + /* normalize longitude of center of cluster bounding circle */ 8.482 + if (cluster->bounding.center.lon < -180) { 8.483 + cluster->bounding.center.lon += 360; 8.484 + } 8.485 + else if (cluster->bounding.center.lon > 180) { 8.486 + cluster->bounding.center.lon -= 360; 8.487 + } 8.488 + /* round bounding circle center (useful if it is used by other functions) */ 8.489 + cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat); 8.490 + cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon); 8.491 + /* calculate radius of bounding circle */ 8.492 + for (i=0; i<cluster->nentries; i++) { 8.493 + npoints = cluster->entries[i].npoints; 8.494 + points = PGL_ENTRY_POINTS(cluster, i); 8.495 + for (j=0; j<npoints; j++) { 8.496 + value = pgl_distance( 8.497 + cluster->bounding.center.lat, cluster->bounding.center.lon, 8.498 + points[j].lat, points[j].lon 8.499 + ); 8.500 + if (value > cluster->bounding.radius) cluster->bounding.radius = value; 8.501 + } 8.502 + } 8.503 + } 8.504 + /* return true (east/west orientation is unambiguous) */ 8.505 + return true; 8.506 +} 8.507 + 8.508 +/* check if point is inside cluster */ 8.509 +/* (if point is on perimeter, then true is returned if and only if 8.510 + strict == false) */ 8.511 +static bool pgl_point_in_cluster( 8.512 + pgl_point *point, 8.513 + pgl_cluster *cluster, 8.514 + bool strict 8.515 +) { 8.516 + int i, j, k; /* i: entry, j: point in entry, k: next point in entry */ 8.517 + int entrytype; /* type of entry */ 8.518 + int npoints; /* number of points in entry */ 8.519 + pgl_point *points; /* array of points in entry */ 8.520 + int lon_dir = 0; /* first vertex west (-1) or east (+1) */ 8.521 + double lon_break = 0; /* antipodal longitude of first vertex */ 8.522 + double lat0 = point->lat; /* latitude of point */ 8.523 + double lon0; /* (adjusted) longitude of point */ 8.524 + double lat1, lon1; /* latitude and (adjusted) longitude of vertex */ 8.525 + double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */ 8.526 + double lon; /* longitude of intersection */ 8.527 + int counter = 0; /* counter for intersections east of point */ 8.528 + /* iterate over all entries */ 8.529 + for (i=0; i<cluster->nentries; i++) { 8.530 + /* get type of entry */ 8.531 + entrytype = cluster->entries[i].entrytype; 8.532 + /* skip all entries but polygons if perimeters are excluded */ 8.533 + if (strict && entrytype != PGL_ENTRY_POLYGON) continue; 8.534 + /* get points of entry */ 8.535 + npoints = cluster->entries[i].npoints; 8.536 + points = PGL_ENTRY_POINTS(cluster, i); 8.537 + /* determine east/west orientation of first point of entry and calculate 8.538 + antipodal longitude */ 8.539 + lon_break = points[0].lon; 8.540 + if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 8.541 + else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 8.542 + else lon_dir = 0; 8.543 + /* get longitude of point */ 8.544 + lon0 = point->lon; 8.545 + /* consider longitude wrap-around for point */ 8.546 + if (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360); 8.547 + else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360); 8.548 + /* iterate over all edges and vertices */ 8.549 + for (j=0; j<npoints; j++) { 8.550 + /* return if point is on vertex of polygon */ 8.551 + if (pgl_point_cmp(point, &(points[j])) == 0) return !strict; 8.552 + /* calculate index of next vertex */ 8.553 + k = (j+1) % npoints; 8.554 + /* skip last edge unless entry is (closed) outline or polygon */ 8.555 + if ( 8.556 + k == 0 && 8.557 + entrytype != PGL_ENTRY_OUTLINE && 8.558 + entrytype != PGL_ENTRY_POLYGON 8.559 + ) continue; 8.560 + /* use previously calculated values for lat1 and lon1 if possible */ 8.561 + if (j) { 8.562 + lat1 = lat2; 8.563 + lon1 = lon2; 8.564 + } else { 8.565 + /* otherwise get latitude and longitude values of first vertex */ 8.566 + lat1 = points[0].lat; 8.567 + lon1 = points[0].lon; 8.568 + /* and consider longitude wrap-around for first vertex */ 8.569 + if (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360); 8.570 + else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360); 8.571 + } 8.572 + /* get latitude and longitude of next vertex */ 8.573 + lat2 = points[k].lat; 8.574 + lon2 = points[k].lon; 8.575 + /* consider longitude wrap-around for next vertex */ 8.576 + if (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360); 8.577 + else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360); 8.578 + /* return if point is on horizontal (west to east) edge of polygon */ 8.579 + if ( 8.580 + lat0 == lat1 && lat0 == lat2 && 8.581 + ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) ) 8.582 + ) return !strict; 8.583 + /* check if edge crosses east/west line of point */ 8.584 + if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) { 8.585 + /* calculate longitude of intersection */ 8.586 + lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1); 8.587 + /* return if intersection goes (approximately) through point */ 8.588 + if (pgl_round(lon) == lon0) return !strict; 8.589 + /* count intersection if east of point and entry is polygon*/ 8.590 + if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++; 8.591 + } 8.592 + } 8.593 + } 8.594 + /* return true if number of intersections is odd */ 8.595 + return counter & 1; 8.596 +} 8.597 + 8.598 +/* check if all points of the second cluster are strictly inside the first 8.599 + cluster */ 8.600 +static inline bool pgl_all_cluster_points_strictly_in_cluster( 8.601 + pgl_cluster *outer, pgl_cluster *inner 8.602 +) { 8.603 + int i, j; /* i: entry, j: point in entry */ 8.604 + int npoints; /* number of points in entry */ 8.605 + pgl_point *points; /* array of points in entry */ 8.606 + /* iterate over all entries of "inner" cluster */ 8.607 + for (i=0; i<inner->nentries; i++) { 8.608 + /* get properties of entry */ 8.609 + npoints = inner->entries[i].npoints; 8.610 + points = PGL_ENTRY_POINTS(inner, i); 8.611 + /* iterate over all points in entry of "inner" cluster */ 8.612 + for (j=0; j<npoints; j++) { 8.613 + /* return false if one point of inner cluster is not in outer cluster */ 8.614 + if (!pgl_point_in_cluster(points+j, outer, true)) return false; 8.615 + } 8.616 + } 8.617 + /* otherwise return true */ 8.618 + return true; 8.619 +} 8.620 + 8.621 +/* check if any point the second cluster is inside the first cluster */ 8.622 +static inline bool pgl_any_cluster_points_in_cluster( 8.623 + pgl_cluster *outer, pgl_cluster *inner 8.624 +) { 8.625 + int i, j; /* i: entry, j: point in entry */ 8.626 + int npoints; /* number of points in entry */ 8.627 + pgl_point *points; /* array of points in entry */ 8.628 + /* iterate over all entries of "inner" cluster */ 8.629 + for (i=0; i<inner->nentries; i++) { 8.630 + /* get properties of entry */ 8.631 + npoints = inner->entries[i].npoints; 8.632 + points = PGL_ENTRY_POINTS(inner, i); 8.633 + /* iterate over all points in entry of "inner" cluster */ 8.634 + for (j=0; j<npoints; j++) { 8.635 + /* return true if one point of inner cluster is in outer cluster */ 8.636 + if (pgl_point_in_cluster(points+j, outer, false)) return true; 8.637 + } 8.638 + } 8.639 + /* otherwise return false */ 8.640 + return false; 8.641 +} 8.642 + 8.643 +/* check if line segment strictly crosses line (not just touching) */ 8.644 +static inline bool pgl_lseg_crosses_line( 8.645 + double seg_x1, double seg_y1, double seg_x2, double seg_y2, 8.646 + double line_x1, double line_y1, double line_x2, double line_y2 8.647 +) { 8.648 + return ( 8.649 + ( 8.650 + (seg_x1-line_x1) * (line_y2-line_y1) - 8.651 + (seg_y1-line_y1) * (line_x2-line_x1) 8.652 + ) * ( 8.653 + (seg_x2-line_x1) * (line_y2-line_y1) - 8.654 + (seg_y2-line_y1) * (line_x2-line_x1) 8.655 + ) 8.656 + ) < 0; 8.657 +} 8.658 + 8.659 +/* check if paths and outlines of two clusters strictly overlap (not just 8.660 + touching) */ 8.661 +static bool pgl_outlines_overlap( 8.662 + pgl_cluster *cluster1, pgl_cluster *cluster2 8.663 +) { 8.664 + int i1, j1, k1; /* i: entry, j: point in entry, k: next point in entry */ 8.665 + int i2, j2, k2; 8.666 + int entrytype1, entrytype2; /* type of entry */ 8.667 + int npoints1, npoints2; /* number of points in entry */ 8.668 + pgl_point *points1; /* array of points in entry of cluster1 */ 8.669 + pgl_point *points2; /* array of points in entry of cluster2 */ 8.670 + int lon_dir1, lon_dir2; /* first vertex west (-1) or east (+1) */ 8.671 + double lon_break1, lon_break2; /* antipodal longitude of first vertex */ 8.672 + double lat11, lon11; /* latitude and (adjusted) longitude of vertex */ 8.673 + double lat12, lon12; /* latitude and (adjusted) longitude of next vertex */ 8.674 + double lat21, lon21; /* latitude and (adjusted) longitudes for cluster2 */ 8.675 + double lat22, lon22; 8.676 + double wrapvalue; /* temporary helper value to adjust wrap-around */ 8.677 + /* iterate over all entries of cluster1 */ 8.678 + for (i1=0; i1<cluster1->nentries; i1++) { 8.679 + /* get properties of entry in cluster1 and skip points */ 8.680 + npoints1 = cluster1->entries[i1].npoints; 8.681 + if (npoints1 < 2) continue; 8.682 + entrytype1 = cluster1->entries[i1].entrytype; 8.683 + points1 = PGL_ENTRY_POINTS(cluster1, i1); 8.684 + /* determine east/west orientation of first point and calculate antipodal 8.685 + longitude */ 8.686 + lon_break1 = points1[0].lon; 8.687 + if (lon_break1 < 0) { 8.688 + lon_dir1 = -1; 8.689 + lon_break1 = pgl_round(lon_break1 + 180); 8.690 + } else if (lon_break1 > 0) { 8.691 + lon_dir1 = 1; 8.692 + lon_break1 = pgl_round(lon_break1 - 180); 8.693 + } else lon_dir1 = 0; 8.694 + /* iterate over all edges and vertices in cluster1 */ 8.695 + for (j1=0; j1<npoints1; j1++) { 8.696 + /* calculate index of next vertex */ 8.697 + k1 = (j1+1) % npoints1; 8.698 + /* skip last edge unless entry is (closed) outline or polygon */ 8.699 + if ( 8.700 + k1 == 0 && 8.701 + entrytype1 != PGL_ENTRY_OUTLINE && 8.702 + entrytype1 != PGL_ENTRY_POLYGON 8.703 + ) continue; 8.704 + /* use previously calculated values for lat1 and lon1 if possible */ 8.705 + if (j1) { 8.706 + lat11 = lat12; 8.707 + lon11 = lon12; 8.708 + } else { 8.709 + /* otherwise get latitude and longitude values of first vertex */ 8.710 + lat11 = points1[0].lat; 8.711 + lon11 = points1[0].lon; 8.712 + /* and consider longitude wrap-around for first vertex */ 8.713 + if (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360); 8.714 + else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360); 8.715 + } 8.716 + /* get latitude and longitude of next vertex */ 8.717 + lat12 = points1[k1].lat; 8.718 + lon12 = points1[k1].lon; 8.719 + /* consider longitude wrap-around for next vertex */ 8.720 + if (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360); 8.721 + else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360); 8.722 + /* skip degenerated edges */ 8.723 + if (lat11 == lat12 && lon11 == lon12) continue; 8.724 + /* iterate over all entries of cluster2 */ 8.725 + for (i2=0; i2<cluster2->nentries; i2++) { 8.726 + /* get points and number of points of entry in cluster2 */ 8.727 + npoints2 = cluster2->entries[i2].npoints; 8.728 + if (npoints2 < 2) continue; 8.729 + entrytype2 = cluster2->entries[i2].entrytype; 8.730 + points2 = PGL_ENTRY_POINTS(cluster2, i2); 8.731 + /* determine east/west orientation of first point and calculate antipodal 8.732 + longitude */ 8.733 + lon_break2 = points2[0].lon; 8.734 + if (lon_break2 < 0) { 8.735 + lon_dir2 = -1; 8.736 + lon_break2 = pgl_round(lon_break2 + 180); 8.737 + } else if (lon_break2 > 0) { 8.738 + lon_dir2 = 1; 8.739 + lon_break2 = pgl_round(lon_break2 - 180); 8.740 + } else lon_dir2 = 0; 8.741 + /* iterate over all edges and vertices in cluster2 */ 8.742 + for (j2=0; j2<npoints2; j2++) { 8.743 + /* calculate index of next vertex */ 8.744 + k2 = (j2+1) % npoints2; 8.745 + /* skip last edge unless entry is (closed) outline or polygon */ 8.746 + if ( 8.747 + k2 == 0 && 8.748 + entrytype2 != PGL_ENTRY_OUTLINE && 8.749 + entrytype2 != PGL_ENTRY_POLYGON 8.750 + ) continue; 8.751 + /* use previously calculated values for lat1 and lon1 if possible */ 8.752 + if (j2) { 8.753 + lat21 = lat22; 8.754 + lon21 = lon22; 8.755 + } else { 8.756 + /* otherwise get latitude and longitude values of first vertex */ 8.757 + lat21 = points2[0].lat; 8.758 + lon21 = points2[0].lon; 8.759 + /* and consider longitude wrap-around for first vertex */ 8.760 + if (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360); 8.761 + else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360); 8.762 + } 8.763 + /* get latitude and longitude of next vertex */ 8.764 + lat22 = points2[k2].lat; 8.765 + lon22 = points2[k2].lon; 8.766 + /* consider longitude wrap-around for next vertex */ 8.767 + if (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360); 8.768 + else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360); 8.769 + /* skip degenerated edges */ 8.770 + if (lat21 == lat22 && lon21 == lon22) continue; 8.771 + /* perform another wrap-around where necessary */ 8.772 + /* TODO: improve performance of whole wrap-around mechanism */ 8.773 + wrapvalue = (lon21 + lon22) - (lon11 + lon12); 8.774 + if (wrapvalue > 360) { 8.775 + lon21 = pgl_round(lon21 - 360); 8.776 + lon22 = pgl_round(lon22 - 360); 8.777 + } else if (wrapvalue < -360) { 8.778 + lon21 = pgl_round(lon21 + 360); 8.779 + lon22 = pgl_round(lon22 + 360); 8.780 + } 8.781 + /* return true if segments overlap */ 8.782 + if ( 8.783 + pgl_lseg_crosses_line( 8.784 + lat11, lon11, lat12, lon12, 8.785 + lat21, lon21, lat22, lon22 8.786 + ) && pgl_lseg_crosses_line( 8.787 + lat21, lon21, lat22, lon22, 8.788 + lat11, lon11, lat12, lon12 8.789 + ) 8.790 + ) { 8.791 + return true; 8.792 + } 8.793 + } 8.794 + } 8.795 + } 8.796 + } 8.797 + /* otherwise return false */ 8.798 + return false; 8.799 +} 8.800 + 8.801 +/* check if second cluster is completely contained in first cluster */ 8.802 +static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) { 8.803 + if (!pgl_all_cluster_points_strictly_in_cluster(outer, inner)) return false; 8.804 + if (pgl_any_cluster_points_in_cluster(inner, outer)) return false; 8.805 + if (pgl_outlines_overlap(outer, inner)) return false; 8.806 + return true; 8.807 +} 8.808 + 8.809 +/* check if two clusters overlap */ 8.810 +static bool pgl_clusters_overlap( 8.811 + pgl_cluster *cluster1, pgl_cluster *cluster2 8.812 +) { 8.813 + if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true; 8.814 + if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true; 8.815 + if (pgl_outlines_overlap(cluster1, cluster2)) return true; 8.816 + return false; 8.817 +} 8.818 + 8.819 +/* calculate (approximate) distance between point and cluster */ 8.820 +static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) { 8.821 + double comp; /* square of compression of meridians */ 8.822 + int i, j, k; /* i: entry, j: point in entry, k: next point in entry */ 8.823 + int entrytype; /* type of entry */ 8.824 + int npoints; /* number of points in entry */ 8.825 + pgl_point *points; /* array of points in entry */ 8.826 + int lon_dir = 0; /* first vertex west (-1) or east (+1) */ 8.827 + double lon_break = 0; /* antipodal longitude of first vertex */ 8.828 + double lon_min = 0; /* minimum (adjusted) longitude of entry vertices */ 8.829 + double lon_max = 0; /* maximum (adjusted) longitude of entry vertices */ 8.830 + double lat0 = point->lat; /* latitude of point */ 8.831 + double lon0; /* (adjusted) longitude of point */ 8.832 + double lat1, lon1; /* latitude and (adjusted) longitude of vertex */ 8.833 + double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */ 8.834 + double s; /* scalar for vector calculations */ 8.835 + double dist; /* distance calculated in one step */ 8.836 + double min_dist = INFINITY; /* minimum distance */ 8.837 + /* distance is zero if point is contained in cluster */ 8.838 + if (pgl_point_in_cluster(point, cluster, false)) return 0; 8.839 + /* calculate approximate square compression of meridians */ 8.840 + comp = cos((lat0 / 180.0) * M_PI); 8.841 + comp *= comp; 8.842 + /* calculate exact square compression of meridians */ 8.843 + comp *= ( 8.844 + (1.0 - PGL_EPS2 * (1.0-comp)) * 8.845 + (1.0 - PGL_EPS2 * (1.0-comp)) / 8.846 + (PGL_SUBEPS2 * PGL_SUBEPS2) 8.847 + ); 8.848 + /* iterate over all entries */ 8.849 + for (i=0; i<cluster->nentries; i++) { 8.850 + /* get properties of entry */ 8.851 + entrytype = cluster->entries[i].entrytype; 8.852 + npoints = cluster->entries[i].npoints; 8.853 + points = PGL_ENTRY_POINTS(cluster, i); 8.854 + /* determine east/west orientation of first point of entry and calculate 8.855 + antipodal longitude */ 8.856 + lon_break = points[0].lon; 8.857 + if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 8.858 + else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 8.859 + else lon_dir = 0; 8.860 + /* determine covered longitude range */ 8.861 + for (j=0; j<npoints; j++) { 8.862 + /* get longitude of vertex */ 8.863 + lon1 = points[j].lon; 8.864 + /* adjust longitude to fix potential wrap-around */ 8.865 + if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360; 8.866 + else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360; 8.867 + /* update minimum and maximum longitude of polygon */ 8.868 + if (j == 0 || lon1 < lon_min) lon_min = lon1; 8.869 + if (j == 0 || lon1 > lon_max) lon_max = lon1; 8.870 + } 8.871 + /* adjust longitude wrap-around according to full longitude range */ 8.872 + lon_break = (lon_max + lon_min) / 2; 8.873 + if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 8.874 + else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 8.875 + /* get longitude of point */ 8.876 + lon0 = point->lon; 8.877 + /* consider longitude wrap-around for point */ 8.878 + if (lon_dir < 0 && lon0 > lon_break) lon0 -= 360; 8.879 + else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360; 8.880 + /* iterate over all edges and vertices */ 8.881 + for (j=0; j<npoints; j++) { 8.882 + /* use previously calculated values for lat1 and lon1 if possible */ 8.883 + if (j) { 8.884 + lat1 = lat2; 8.885 + lon1 = lon2; 8.886 + } else { 8.887 + /* otherwise get latitude and longitude values of first vertex */ 8.888 + lat1 = points[0].lat; 8.889 + lon1 = points[0].lon; 8.890 + /* and consider longitude wrap-around for first vertex */ 8.891 + if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360; 8.892 + else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360; 8.893 + } 8.894 + /* calculate distance to vertex */ 8.895 + dist = pgl_distance(lat0, lon0, lat1, lon1); 8.896 + /* store calculated distance if smallest */ 8.897 + if (dist < min_dist) min_dist = dist; 8.898 + /* calculate index of next vertex */ 8.899 + k = (j+1) % npoints; 8.900 + /* skip last edge unless entry is (closed) outline or polygon */ 8.901 + if ( 8.902 + k == 0 && 8.903 + entrytype != PGL_ENTRY_OUTLINE && 8.904 + entrytype != PGL_ENTRY_POLYGON 8.905 + ) continue; 8.906 + /* get latitude and longitude of next vertex */ 8.907 + lat2 = points[k].lat; 8.908 + lon2 = points[k].lon; 8.909 + /* consider longitude wrap-around for next vertex */ 8.910 + if (lon_dir < 0 && lon2 > lon_break) lon2 -= 360; 8.911 + else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360; 8.912 + /* go to next vertex and edge if edge is degenerated */ 8.913 + if (lat1 == lat2 && lon1 == lon2) continue; 8.914 + /* otherwise test if point can be projected onto edge of polygon */ 8.915 + s = ( 8.916 + ((lat0-lat1) * (lat2-lat1) + comp * (lon0-lon1) * (lon2-lon1)) / 8.917 + ((lat2-lat1) * (lat2-lat1) + comp * (lon2-lon1) * (lon2-lon1)) 8.918 + ); 8.919 + /* go to next vertex and edge if point cannot be projected */ 8.920 + if (!(s > 0 && s < 1)) continue; 8.921 + /* calculate distance from original point to projected point */ 8.922 + dist = pgl_distance( 8.923 + lat0, lon0, 8.924 + lat1 + s * (lat2-lat1), 8.925 + lon1 + s * (lon2-lon1) 8.926 + ); 8.927 + /* store calculated distance if smallest */ 8.928 + if (dist < min_dist) min_dist = dist; 8.929 + } 8.930 + } 8.931 + /* return minimum distance */ 8.932 + return min_dist; 8.933 +} 8.934 + 8.935 +/* calculate (approximate) distance between two clusters */ 8.936 +static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) { 8.937 + int i, j; /* i: entry, j: point in entry */ 8.938 + int npoints; /* number of points in entry */ 8.939 + pgl_point *points; /* array of points in entry */ 8.940 + double dist; /* distance calculated in one step */ 8.941 + double min_dist = INFINITY; /* minimum distance */ 8.942 + /* consider distance from each point in one cluster to the whole other */ 8.943 + for (i=0; i<cluster1->nentries; i++) { 8.944 + npoints = cluster1->entries[i].npoints; 8.945 + points = PGL_ENTRY_POINTS(cluster1, i); 8.946 + for (j=0; j<npoints; j++) { 8.947 + dist = pgl_point_cluster_distance(points+j, cluster2); 8.948 + if (dist == 0) return dist; 8.949 + if (dist < min_dist) min_dist = dist; 8.950 + } 8.951 + } 8.952 + /* consider distance from each point in other cluster to the first cluster */ 8.953 + for (i=0; i<cluster2->nentries; i++) { 8.954 + npoints = cluster2->entries[i].npoints; 8.955 + points = PGL_ENTRY_POINTS(cluster2, i); 8.956 + for (j=0; j<npoints; j++) { 8.957 + dist = pgl_point_cluster_distance(points+j, cluster1); 8.958 + if (dist == 0) return dist; 8.959 + if (dist < min_dist) min_dist = dist; 8.960 + } 8.961 + } 8.962 + return min_dist; 8.963 +} 8.964 + 8.965 +/* estimator function for distance between box and point */ 8.966 +/* always returns a smaller value than actually correct or zero */ 8.967 +static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) { 8.968 + double dlon; /* longitude range of box (delta longitude) */ 8.969 + double distance; /* return value */ 8.970 + /* return infinity if box is empty */ 8.971 + if (box->lat_min > box->lat_max) return INFINITY; 8.972 + /* return zero if point is inside box */ 8.973 + if (pgl_point_in_box(point, box)) return 0; 8.974 + /* calculate delta longitude */ 8.975 + dlon = box->lon_max - box->lon_min; 8.976 + if (dlon < 0) dlon += 360; /* 180th meridian crossed */ 8.977 + /* if delta longitude is greater than 150 degrees, perform safe fall-back */ 8.978 + if (dlon > 150) return 0; 8.979 + /* calculate lower limit for distance (formula below requires dlon <= 150) */ 8.980 + /* TODO: provide better estimation function to improve performance */ 8.981 + distance = ( 8.982 + (1.0-PGL_SPHEROID_F) * /* safety margin due to flattening and approx. */ 8.983 + pgl_distance( 8.984 + point->lat, 8.985 + point->lon, 8.986 + (box->lat_min + box->lat_max) / 2, 8.987 + box->lon_min + dlon/2 8.988 + ) 8.989 + ) - pgl_distance( 8.990 + box->lat_min, box->lon_min, 8.991 + box->lat_max, box->lon_max 8.992 + ); 8.993 + /* truncate negative results to zero */ 8.994 + if (distance <= 0) distance = 0; 8.995 + /* return result */ 8.996 + return distance; 8.997 +} 8.998 + 8.999 + 8.1000 +/*------------------------------------------------------------* 8.1001 + * Functions using numerical integration (Monte Carlo like) * 8.1002 + *------------------------------------------------------------*/ 8.1003 + 8.1004 +/* half of (spherical) earth's surface area */ 8.1005 +#define PGL_HALF_SURFACE (PGL_RADIUS * PGL_DIAMETER * M_PI) 8.1006 + 8.1007 +/* golden angle in radians */ 8.1008 +#define PGL_GOLDEN_ANGLE (M_PI * (sqrt(5) - 1.0)) 8.1009 + 8.1010 +/* create a list of sample points covering a bounding circle 8.1011 + and return covered area */ 8.1012 +static double pgl_sample_points( 8.1013 + pgl_point *center, /* center of bounding circle */ 8.1014 + double radius, /* radius of bounding circle */ 8.1015 + int samples, /* number of sample points (MUST be positive!) */ 8.1016 + pgl_point *result /* pointer to result array */ 8.1017 +) { 8.1018 + double double_share = 2.0; /* double of covered share of earth's surface */ 8.1019 + double double_share_div_samples; /* double_share divided by sample count */ 8.1020 + int i; 8.1021 + double t; /* parameter of spiral laid on (spherical) earth's surface */ 8.1022 + double x, y, z; /* normalized coordinates of point on non-rotated spiral */ 8.1023 + double sin_phi; /* sine of sph. coordinate of point of non-rotated spiral */ 8.1024 + double lambda; /* other sph. coordinate of point of non-rotated spiral */ 8.1025 + double rot = (0.5 - center->lat / 180.0) * M_PI; /* needed rot. (in rad) */ 8.1026 + double cos_rot = cos(rot); /* cosine of rotation by latitude */ 8.1027 + double sin_rot = sin(rot); /* sine of rotation by latitude */ 8.1028 + double x_rot, z_rot; /* normalized coordinates of point on rotated spiral */ 8.1029 + double center_lon = center->lon; /* second rotation in degree */ 8.1030 + /* add safety margin to bounding circle because of spherical approximation */ 8.1031 + radius *= PGL_SPHEROID_A / PGL_RADIUS; 8.1032 + /* if whole earth is covered, use initialized value, otherwise calculate 8.1033 + share of covered area (multiplied by 2) */ 8.1034 + if (radius < PGL_MAXDIST) double_share = 1.0 - cos(radius / PGL_RADIUS); 8.1035 + /* divide double_share by sample count for later calculations */ 8.1036 + double_share_div_samples = double_share / samples; 8.1037 + /* generate sample points */ 8.1038 + for (i=0; i<samples; i++) { 8.1039 + /* use an offset of 1/2 to avoid too dense clustering at spiral center */ 8.1040 + t = 0.5 + i; 8.1041 + /* calculate normalized coordinates of point on non-rotated spiral */ 8.1042 + z = 1.0 - double_share_div_samples * t; 8.1043 + sin_phi = sqrt(1.0 - z*z); 8.1044 + lambda = t * PGL_GOLDEN_ANGLE; 8.1045 + x = sin_phi * cos(lambda); 8.1046 + y = sin_phi * sin(lambda); 8.1047 + /* rotate spiral by latitude value of bounding circle */ 8.1048 + x_rot = cos_rot * x + sin_rot * z; 8.1049 + z_rot = cos_rot * z - sin_rot * x; 8.1050 + /* set resulting sample point in result array */ 8.1051 + /* (while performing second rotation by bounding circle longitude) */ 8.1052 + result[i].lat = 180.0 * (atan(z_rot / fabs(x_rot)) / M_PI); 8.1053 + result[i].lon = center_lon + 180.0 * (atan2(y, x_rot) / M_PI); 8.1054 + } 8.1055 + /* return covered area */ 8.1056 + return PGL_HALF_SURFACE * double_share; 8.1057 +} 8.1058 + 8.1059 +/* fair distance between point and cluster (see README file for explanation) */ 8.1060 +/* NOTE: sample count passed as third argument MUST be positive */ 8.1061 +static double pgl_fair_distance( 8.1062 + pgl_point *point, pgl_cluster *cluster, int samples 8.1063 +) { 8.1064 + double distance; /* shortest distance from point to cluster */ 8.1065 + pgl_point *points; /* sample points for numerical integration */ 8.1066 + double area; /* area covered by sample points */ 8.1067 + int i; 8.1068 + int inner = 0; /* number of sample points within cluster */ 8.1069 + int outer = 0; /* number of sample points outside cluster but 8.1070 + within cluster enlarged by distance */ 8.1071 + double result; 8.1072 + /* calculate shortest distance from point to cluster */ 8.1073 + distance = pgl_point_cluster_distance(point, cluster); 8.1074 + /* if cluster consists of a single point or has no bounding circle with 8.1075 + positive radius, simply return distance */ 8.1076 + if ( 8.1077 + (cluster->nentries==1 && cluster->entries[0].entrytype==PGL_ENTRY_POINT) || 8.1078 + !(cluster->bounding.radius > 0) 8.1079 + ) return distance; 8.1080 + /* if cluster consists of two points which are twice as far apart, return 8.1081 + distance between point and cluster multiplied by square root of two */ 8.1082 + if ( 8.1083 + cluster->nentries == 2 && 8.1084 + cluster->entries[0].entrytype == PGL_ENTRY_POINT && 8.1085 + cluster->entries[1].entrytype == PGL_ENTRY_POINT && 8.1086 + pgl_distance( 8.1087 + PGL_ENTRY_POINTS(cluster, 0)[0].lat, 8.1088 + PGL_ENTRY_POINTS(cluster, 0)[0].lon, 8.1089 + PGL_ENTRY_POINTS(cluster, 1)[0].lat, 8.1090 + PGL_ENTRY_POINTS(cluster, 1)[0].lon 8.1091 + ) >= 2.0 * distance 8.1092 + ) { 8.1093 + return distance * M_SQRT2; 8.1094 + } 8.1095 + /* otherwise create sample points for numerical integration and determine 8.1096 + area covered by sample points */ 8.1097 + points = palloc(samples * sizeof(pgl_point)); 8.1098 + area = pgl_sample_points( 8.1099 + &cluster->bounding.center, 8.1100 + cluster->bounding.radius + distance, /* pad bounding circle by distance */ 8.1101 + samples, 8.1102 + points 8.1103 + ); 8.1104 + /* perform numerical integration */ 8.1105 + if (distance > 0) { 8.1106 + /* point (that was passed as argument) is outside cluster */ 8.1107 + for (i=0; i<samples; i++) { 8.1108 + /* count sample points within cluster */ 8.1109 + if (pgl_point_in_cluster(points+i, cluster, true)) inner++; 8.1110 + /* count sample points outside of cluster but within cluster enlarged by 8.1111 + distance between point (that was passed as argument) and cluster */ 8.1112 + else if ( 8.1113 + pgl_point_cluster_distance(points+i, cluster) < distance 8.1114 + ) outer++; 8.1115 + } 8.1116 + } else { 8.1117 + /* if point is within cluster, just count sample points within cluster */ 8.1118 + for (i=0; i<samples; i++) { 8.1119 + if (pgl_point_in_cluster(points+i, cluster, true)) inner++; 8.1120 + } 8.1121 + } 8.1122 + /* release memory for sample points needed for numerical integration */ 8.1123 + pfree(points); 8.1124 + /* if enlargement was less than doubling the area, then combine inner and 8.1125 + outer sample point counts with different weighting */ 8.1126 + /* (ensures fairness in such a way that the integral of the squared result 8.1127 + over all possible point parameters is independent of the cluster) */ 8.1128 + if (outer < inner) result = (2*inner + 4*outer) / 3.0; 8.1129 + /* otherwise weigh inner and outer points the same */ 8.1130 + else result = inner + outer; 8.1131 + /* convert area into distance (i.e. radius of a circle with the same area) */ 8.1132 + result = sqrt(area * (result / samples) / M_PI); 8.1133 + /* return result only if it is greater than the distance between point and 8.1134 + cluster to avoid unexpected results because of errors due to limited 8.1135 + precision */ 8.1136 + if (result > distance) return result; 8.1137 + /* otherwise return distance between point and cluster */ 8.1138 + else return distance; 8.1139 +} 8.1140 + 8.1141 + 8.1142 +/*-------------------------------------------------* 8.1143 + * geographic index based on space-filling curve * 8.1144 + *-------------------------------------------------*/ 8.1145 + 8.1146 +/* number of bytes used for geographic (center) position in keys */ 8.1147 +#define PGL_KEY_LATLON_BYTELEN 7 8.1148 + 8.1149 +/* maximum reference value for logarithmic size of geographic objects */ 8.1150 +#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0) /* can be tweaked */ 8.1151 + 8.1152 +/* pointer to index key (either pgl_pointkey or pgl_areakey) */ 8.1153 +typedef unsigned char *pgl_keyptr; 8.1154 + 8.1155 +/* index key for points (objects with zero area) on the spheroid */ 8.1156 +/* bit 0..55: interspersed bits of latitude and longitude, 8.1157 + bit 56..57: always zero, 8.1158 + bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */ 8.1159 +typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1]; 8.1160 + 8.1161 +/* index key for geographic objects on spheroid with area greater than zero */ 8.1162 +/* bit 0..55: interspersed bits of latitude and longitude of center point, 8.1163 + bit 56: always set to 1, 8.1164 + bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.), 8.1165 + bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to 8.1166 + PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth 8.1167 + = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL 8.1168 + (with interspersed bits = 0 and node depth = 0) for keys which 8.1169 + cover both empty and non-empty objects */ 8.1170 + 8.1171 +typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2]; 8.1172 + 8.1173 +/* helper macros for reading/writing index keys */ 8.1174 +#define PGL_KEY_NODEDEPTH_OFFSET PGL_KEY_LATLON_BYTELEN 8.1175 +#define PGL_KEY_OBJSIZE_OFFSET (PGL_KEY_NODEDEPTH_OFFSET+1) 8.1176 +#define PGL_POINTKEY_MAXDEPTH (PGL_KEY_LATLON_BYTELEN*8) 8.1177 +#define PGL_AREAKEY_MAXDEPTH (2*PGL_POINTKEY_MAXDEPTH+1) 8.1178 +#define PGL_AREAKEY_MAXOBJSIZE (PGL_POINTKEY_MAXDEPTH+1) 8.1179 +#define PGL_AREAKEY_TYPEMASK 0x80 8.1180 +#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8))) 8.1181 +#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \ 8.1182 + ( PGL_KEY_LATLONBIT(key1, n) ^ \ 8.1183 + PGL_KEY_LATLONBIT(key2, n) ) 8.1184 +#define PGL_KEY_IS_AREAKEY(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \ 8.1185 + PGL_AREAKEY_TYPEMASK) 8.1186 +#define PGL_KEY_NODEDEPTH(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \ 8.1187 + (PGL_AREAKEY_TYPEMASK-1)) 8.1188 +#define PGL_KEY_OBJSIZE(key) ((key)[PGL_KEY_OBJSIZE_OFFSET]) 8.1189 +#define PGL_KEY_OBJSIZE_EMPTY 126 8.1190 +#define PGL_KEY_OBJSIZE_UNIVERSAL 127 8.1191 +#define PGL_KEY_IS_EMPTY(key) ( PGL_KEY_IS_AREAKEY(key) && \ 8.1192 + (key)[PGL_KEY_OBJSIZE_OFFSET] == \ 8.1193 + PGL_KEY_OBJSIZE_EMPTY ) 8.1194 +#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \ 8.1195 + (key)[PGL_KEY_OBJSIZE_OFFSET] == \ 8.1196 + PGL_KEY_OBJSIZE_UNIVERSAL ) 8.1197 + 8.1198 +/* set area key to match empty objects only */ 8.1199 +static void pgl_key_set_empty(pgl_keyptr key) { 8.1200 + memset(key, 0, sizeof(pgl_areakey)); 8.1201 + /* Note: setting node depth to maximum is required for picksplit function */ 8.1202 + key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH; 8.1203 + key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY; 8.1204 +} 8.1205 + 8.1206 +/* set area key to match any object (including empty objects) */ 8.1207 +static void pgl_key_set_universal(pgl_keyptr key) { 8.1208 + memset(key, 0, sizeof(pgl_areakey)); 8.1209 + key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK; 8.1210 + key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL; 8.1211 +} 8.1212 + 8.1213 +/* convert a point on earth into a max-depth key to be used in index */ 8.1214 +static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) { 8.1215 + double lat = point->lat; 8.1216 + double lon = point->lon; 8.1217 + int i; 8.1218 + /* clear latitude and longitude bits */ 8.1219 + memset(key, 0, PGL_KEY_LATLON_BYTELEN); 8.1220 + /* set node depth to maximum and type bit to zero */ 8.1221 + key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH; 8.1222 + /* iterate over all latitude/longitude bit pairs */ 8.1223 + for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) { 8.1224 + /* determine latitude bit */ 8.1225 + if (lat >= 0) { 8.1226 + key[i/4] |= 0x80 >> (2*(i%4)); 8.1227 + lat *= 2; lat -= 90; 8.1228 + } else { 8.1229 + lat *= 2; lat += 90; 8.1230 + } 8.1231 + /* determine longitude bit */ 8.1232 + if (lon >= 0) { 8.1233 + key[i/4] |= 0x80 >> (2*(i%4)+1); 8.1234 + lon *= 2; lon -= 180; 8.1235 + } else { 8.1236 + lon *= 2; lon += 180; 8.1237 + } 8.1238 + } 8.1239 +} 8.1240 + 8.1241 +/* convert a circle on earth into a max-depth key to be used in an index */ 8.1242 +static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) { 8.1243 + /* handle special case of empty circle */ 8.1244 + if (circle->radius < 0) { 8.1245 + pgl_key_set_empty(key); 8.1246 + return; 8.1247 + } 8.1248 + /* perform same action as for point keys */ 8.1249 + pgl_point_to_key(&(circle->center), key); 8.1250 + /* but overwrite type and node depth to fit area index key */ 8.1251 + key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH; 8.1252 + /* check if radius is greater than (or equal to) reference size */ 8.1253 + /* (treat equal values as greater values for numerical safety) */ 8.1254 + if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) { 8.1255 + /* if yes, set logarithmic size to zero */ 8.1256 + key[PGL_KEY_OBJSIZE_OFFSET] = 0; 8.1257 + } else { 8.1258 + /* otherwise, determine logarithmic size iteratively */ 8.1259 + /* (one step is equivalent to a factor of sqrt(2)) */ 8.1260 + double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2; 8.1261 + int objsize = 1; 8.1262 + while (objsize < PGL_AREAKEY_MAXOBJSIZE) { 8.1263 + /* stop when radius is greater than (or equal to) adjusted reference */ 8.1264 + /* (treat equal values as greater values for numerical safety) */ 8.1265 + if (circle->radius >= reference) break; 8.1266 + reference /= M_SQRT2; 8.1267 + objsize++; 8.1268 + } 8.1269 + /* set logarithmic size to determined value */ 8.1270 + key[PGL_KEY_OBJSIZE_OFFSET] = objsize; 8.1271 + } 8.1272 +} 8.1273 + 8.1274 +/* check if one key is subkey of another key or vice versa */ 8.1275 +static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) { 8.1276 + int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */ 8.1277 + /* determine smallest depth */ 8.1278 + int depth1 = PGL_KEY_NODEDEPTH(key1); 8.1279 + int depth2 = PGL_KEY_NODEDEPTH(key2); 8.1280 + int depth = (depth1 < depth2) ? depth1 : depth2; 8.1281 + /* check if keys are area keys (assuming that both keys have same type) */ 8.1282 + if (PGL_KEY_IS_AREAKEY(key1)) { 8.1283 + int j = 0; /* bit offset for logarithmic object size bits */ 8.1284 + int k = 0; /* bit offset for latitude and longitude */ 8.1285 + /* fetch logarithmic object size information */ 8.1286 + int objsize1 = PGL_KEY_OBJSIZE(key1); 8.1287 + int objsize2 = PGL_KEY_OBJSIZE(key2); 8.1288 + /* handle special cases for empty objects (universal and empty keys) */ 8.1289 + if ( 8.1290 + objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL || 8.1291 + objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL 8.1292 + ) return true; 8.1293 + if ( 8.1294 + objsize1 == PGL_KEY_OBJSIZE_EMPTY || 8.1295 + objsize2 == PGL_KEY_OBJSIZE_EMPTY 8.1296 + ) return objsize1 == objsize2; 8.1297 + /* iterate through key bits */ 8.1298 + for (i=0; i<depth; i++) { 8.1299 + /* every second bit is a bit describing the object size */ 8.1300 + if (i%2 == 0) { 8.1301 + /* check if object size bit is different in both keys (objsize1 and 8.1302 + objsize2 describe the minimum index when object size bit is set) */ 8.1303 + if ( 8.1304 + (objsize1 <= j && objsize2 > j) || 8.1305 + (objsize2 <= j && objsize1 > j) 8.1306 + ) { 8.1307 + /* bit differs, therefore keys are in separate branches */ 8.1308 + return false; 8.1309 + } 8.1310 + /* increase bit counter for object size bits */ 8.1311 + j++; 8.1312 + } 8.1313 + /* all other bits describe latitude and longitude */ 8.1314 + else { 8.1315 + /* check if bit differs in both keys */ 8.1316 + if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) { 8.1317 + /* bit differs, therefore keys are in separate branches */ 8.1318 + return false; 8.1319 + } 8.1320 + /* increase bit counter for latitude/longitude bits */ 8.1321 + k++; 8.1322 + } 8.1323 + } 8.1324 + } 8.1325 + /* if not, keys are point keys */ 8.1326 + else { 8.1327 + /* iterate through key bits */ 8.1328 + for (i=0; i<depth; i++) { 8.1329 + /* check if bit differs in both keys */ 8.1330 + if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) { 8.1331 + /* bit differs, therefore keys are in separate branches */ 8.1332 + return false; 8.1333 + } 8.1334 + } 8.1335 + } 8.1336 + /* return true because keys are in the same branch */ 8.1337 + return true; 8.1338 +} 8.1339 + 8.1340 +/* combine two keys into new key which covers both original keys */ 8.1341 +/* (result stored in first argument) */ 8.1342 +static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) { 8.1343 + int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */ 8.1344 + /* determine smallest depth */ 8.1345 + int depth1 = PGL_KEY_NODEDEPTH(dst); 8.1346 + int depth2 = PGL_KEY_NODEDEPTH(src); 8.1347 + int depth = (depth1 < depth2) ? depth1 : depth2; 8.1348 + /* check if keys are area keys (assuming that both keys have same type) */ 8.1349 + if (PGL_KEY_IS_AREAKEY(dst)) { 8.1350 + pgl_areakey dstbuf = { 0, }; /* destination buffer (cleared) */ 8.1351 + int j = 0; /* bit offset for logarithmic object size bits */ 8.1352 + int k = 0; /* bit offset for latitude and longitude */ 8.1353 + /* fetch logarithmic object size information */ 8.1354 + int objsize1 = PGL_KEY_OBJSIZE(dst); 8.1355 + int objsize2 = PGL_KEY_OBJSIZE(src); 8.1356 + /* handle special cases for empty objects (universal and empty keys) */ 8.1357 + if ( 8.1358 + objsize1 > PGL_AREAKEY_MAXOBJSIZE || 8.1359 + objsize2 > PGL_AREAKEY_MAXOBJSIZE 8.1360 + ) { 8.1361 + if ( 8.1362 + objsize1 == PGL_KEY_OBJSIZE_EMPTY && 8.1363 + objsize2 == PGL_KEY_OBJSIZE_EMPTY 8.1364 + ) pgl_key_set_empty(dst); 8.1365 + else pgl_key_set_universal(dst); 8.1366 + return; 8.1367 + } 8.1368 + /* iterate through key bits */ 8.1369 + for (i=0; i<depth; i++) { 8.1370 + /* every second bit is a bit describing the object size */ 8.1371 + if (i%2 == 0) { 8.1372 + /* increase bit counter for object size bits first */ 8.1373 + /* (handy when setting objsize variable) */ 8.1374 + j++; 8.1375 + /* check if object size bit is set in neither key */ 8.1376 + if (objsize1 >= j && objsize2 >= j) { 8.1377 + /* set objsize in destination buffer to indicate that size bit is 8.1378 + unset in destination buffer at the current bit position */ 8.1379 + dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j; 8.1380 + } 8.1381 + /* break if object size bit is set in one key only */ 8.1382 + else if (objsize1 >= j || objsize2 >= j) break; 8.1383 + } 8.1384 + /* all other bits describe latitude and longitude */ 8.1385 + else { 8.1386 + /* break if bit differs in both keys */ 8.1387 + if (PGL_KEY_LATLONBIT(dst, k)) { 8.1388 + if (!PGL_KEY_LATLONBIT(src, k)) break; 8.1389 + /* but set bit in destination buffer if bit is set in both keys */ 8.1390 + dstbuf[k/8] |= 0x80 >> (k%8); 8.1391 + } else if (PGL_KEY_LATLONBIT(src, k)) break; 8.1392 + /* increase bit counter for latitude/longitude bits */ 8.1393 + k++; 8.1394 + } 8.1395 + } 8.1396 + /* set common node depth and type bit (type bit = 1) */ 8.1397 + dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i; 8.1398 + /* copy contents of destination buffer to first key */ 8.1399 + memcpy(dst, dstbuf, sizeof(pgl_areakey)); 8.1400 + } 8.1401 + /* if not, keys are point keys */ 8.1402 + else { 8.1403 + pgl_pointkey dstbuf = { 0, }; /* destination buffer (cleared) */ 8.1404 + /* iterate through key bits */ 8.1405 + for (i=0; i<depth; i++) { 8.1406 + /* break if bit differs in both keys */ 8.1407 + if (PGL_KEY_LATLONBIT(dst, i)) { 8.1408 + if (!PGL_KEY_LATLONBIT(src, i)) break; 8.1409 + /* but set bit in destination buffer if bit is set in both keys */ 8.1410 + dstbuf[i/8] |= 0x80 >> (i%8); 8.1411 + } else if (PGL_KEY_LATLONBIT(src, i)) break; 8.1412 + } 8.1413 + /* set common node depth (type bit = 0) */ 8.1414 + dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i; 8.1415 + /* copy contents of destination buffer to first key */ 8.1416 + memcpy(dst, dstbuf, sizeof(pgl_pointkey)); 8.1417 + } 8.1418 +} 8.1419 + 8.1420 +/* determine center(!) boundaries and radius estimation of index key */ 8.1421 +static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) { 8.1422 + int i; 8.1423 + /* determine node depth */ 8.1424 + int depth = PGL_KEY_NODEDEPTH(key); 8.1425 + /* center point of possible result */ 8.1426 + double lat = 0; 8.1427 + double lon = 0; 8.1428 + /* maximum distance of real center point from key center */ 8.1429 + double dlat = 90; 8.1430 + double dlon = 180; 8.1431 + /* maximum radius of contained objects */ 8.1432 + double radius = 0; /* always return zero for point index keys */ 8.1433 + /* check if key is area key */ 8.1434 + if (PGL_KEY_IS_AREAKEY(key)) { 8.1435 + /* get logarithmic object size */ 8.1436 + int objsize = PGL_KEY_OBJSIZE(key); 8.1437 + /* handle special cases for empty objects (universal and empty keys) */ 8.1438 + if (objsize == PGL_KEY_OBJSIZE_EMPTY) { 8.1439 + pgl_box_set_empty(box); 8.1440 + return 0; 8.1441 + } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) { 8.1442 + box->lat_min = -90; 8.1443 + box->lat_max = 90; 8.1444 + box->lon_min = -180; 8.1445 + box->lon_max = 180; 8.1446 + return 0; /* any value >= 0 would do */ 8.1447 + } 8.1448 + /* calculate maximum possible radius of objects covered by the given key */ 8.1449 + if (objsize == 0) radius = INFINITY; 8.1450 + else { 8.1451 + radius = PGL_AREAKEY_REFOBJSIZE; 8.1452 + while (--objsize) radius /= M_SQRT2; 8.1453 + } 8.1454 + /* iterate over latitude and longitude bits in key */ 8.1455 + /* (every second bit is a latitude or longitude bit) */ 8.1456 + for (i=0; i<depth/2; i++) { 8.1457 + /* check if latitude bit */ 8.1458 + if (i%2 == 0) { 8.1459 + /* cut latitude dimension in half */ 8.1460 + dlat /= 2; 8.1461 + /* increase center latitude if bit is 1, otherwise decrease */ 8.1462 + if (PGL_KEY_LATLONBIT(key, i)) lat += dlat; 8.1463 + else lat -= dlat; 8.1464 + } 8.1465 + /* otherwise longitude bit */ 8.1466 + else { 8.1467 + /* cut longitude dimension in half */ 8.1468 + dlon /= 2; 8.1469 + /* increase center longitude if bit is 1, otherwise decrease */ 8.1470 + if (PGL_KEY_LATLONBIT(key, i)) lon += dlon; 8.1471 + else lon -= dlon; 8.1472 + } 8.1473 + } 8.1474 + } 8.1475 + /* if not, keys are point keys */ 8.1476 + else { 8.1477 + /* iterate over all bits in key */ 8.1478 + for (i=0; i<depth; i++) { 8.1479 + /* check if latitude bit */ 8.1480 + if (i%2 == 0) { 8.1481 + /* cut latitude dimension in half */ 8.1482 + dlat /= 2; 8.1483 + /* increase center latitude if bit is 1, otherwise decrease */ 8.1484 + if (PGL_KEY_LATLONBIT(key, i)) lat += dlat; 8.1485 + else lat -= dlat; 8.1486 + } 8.1487 + /* otherwise longitude bit */ 8.1488 + else { 8.1489 + /* cut longitude dimension in half */ 8.1490 + dlon /= 2; 8.1491 + /* increase center longitude if bit is 1, otherwise decrease */ 8.1492 + if (PGL_KEY_LATLONBIT(key, i)) lon += dlon; 8.1493 + else lon -= dlon; 8.1494 + } 8.1495 + } 8.1496 + } 8.1497 + /* calculate boundaries from center point and remaining dlat and dlon */ 8.1498 + /* (return values through pointer to box) */ 8.1499 + box->lat_min = lat - dlat; 8.1500 + box->lat_max = lat + dlat; 8.1501 + box->lon_min = lon - dlon; 8.1502 + box->lon_max = lon + dlon; 8.1503 + /* return radius (as a function return value) */ 8.1504 + return radius; 8.1505 +} 8.1506 + 8.1507 +/* estimator function for distance between point and index key */ 8.1508 +/* always returns a smaller value than actually correct or zero */ 8.1509 +static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) { 8.1510 + pgl_box box; /* center(!) bounding box of area index key */ 8.1511 + /* calculate center(!) bounding box and maximum radius of objects covered 8.1512 + by area index key (radius is zero for point index keys) */ 8.1513 + double distance = pgl_key_to_box(key, &box); 8.1514 + /* calculate estimated distance between bounding box of center point of 8.1515 + indexed object and point passed as second argument, then substract maximum 8.1516 + radius of objects covered by index key */ 8.1517 + distance = pgl_estimate_point_box_distance(point, &box) - distance; 8.1518 + /* truncate negative results to zero */ 8.1519 + if (distance <= 0) distance = 0; 8.1520 + /* return result */ 8.1521 + return distance; 8.1522 +} 8.1523 + 8.1524 + 8.1525 +/*---------------------------------* 8.1526 + * helper functions for text I/O * 8.1527 + *---------------------------------*/ 8.1528 + 8.1529 +#define PGL_NUMBUFLEN 64 /* buffer size for number to string conversion */ 8.1530 + 8.1531 +/* convert floating point number to string (round-trip safe) */ 8.1532 +static void pgl_print_float(char *buf, double flt) { 8.1533 + /* check if number is integral */ 8.1534 + if (trunc(flt) == flt) { 8.1535 + /* for integral floats use maximum precision */ 8.1536 + snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt); 8.1537 + } else { 8.1538 + /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */ 8.1539 + snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt); 8.1540 + if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt); 8.1541 + if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt); 8.1542 + } 8.1543 +} 8.1544 + 8.1545 +/* convert latitude floating point number (in degrees) to string */ 8.1546 +static void pgl_print_lat(char *buf, double lat) { 8.1547 + if (signbit(lat)) { 8.1548 + /* treat negative latitudes (including -0) as south */ 8.1549 + snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat); 8.1550 + } else { 8.1551 + /* treat positive latitudes (including +0) as north */ 8.1552 + snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat); 8.1553 + } 8.1554 +} 8.1555 + 8.1556 +/* convert longitude floating point number (in degrees) to string */ 8.1557 +static void pgl_print_lon(char *buf, double lon) { 8.1558 + if (signbit(lon)) { 8.1559 + /* treat negative longitudes (including -0) as west */ 8.1560 + snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon); 8.1561 + } else { 8.1562 + /* treat positive longitudes (including +0) as east */ 8.1563 + snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon); 8.1564 + } 8.1565 +} 8.1566 + 8.1567 +/* bit masks used as return value of pgl_scan() function */ 8.1568 +#define PGL_SCAN_NONE 0 /* no value has been parsed */ 8.1569 +#define PGL_SCAN_LAT (1<<0) /* latitude has been parsed */ 8.1570 +#define PGL_SCAN_LON (1<<1) /* longitude has been parsed */ 8.1571 +#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON) /* bitwise OR of both */ 8.1572 + 8.1573 +/* parse a coordinate (can be latitude or longitude) */ 8.1574 +static int pgl_scan(char **str, double *lat, double *lon) { 8.1575 + double val; 8.1576 + int len; 8.1577 + if ( 8.1578 + sscanf(*str, " N %lf %n", &val, &len) || 8.1579 + sscanf(*str, " n %lf %n", &val, &len) 8.1580 + ) { 8.1581 + *str += len; *lat = val; return PGL_SCAN_LAT; 8.1582 + } 8.1583 + if ( 8.1584 + sscanf(*str, " S %lf %n", &val, &len) || 8.1585 + sscanf(*str, " s %lf %n", &val, &len) 8.1586 + ) { 8.1587 + *str += len; *lat = -val; return PGL_SCAN_LAT; 8.1588 + } 8.1589 + if ( 8.1590 + sscanf(*str, " E %lf %n", &val, &len) || 8.1591 + sscanf(*str, " e %lf %n", &val, &len) 8.1592 + ) { 8.1593 + *str += len; *lon = val; return PGL_SCAN_LON; 8.1594 + } 8.1595 + if ( 8.1596 + sscanf(*str, " W %lf %n", &val, &len) || 8.1597 + sscanf(*str, " w %lf %n", &val, &len) 8.1598 + ) { 8.1599 + *str += len; *lon = -val; return PGL_SCAN_LON; 8.1600 + } 8.1601 + return PGL_SCAN_NONE; 8.1602 +} 8.1603 + 8.1604 + 8.1605 +/*-----------------* 8.1606 + * SQL functions * 8.1607 + *-----------------*/ 8.1608 + 8.1609 +/* Note: These function names use "epoint", "ebox", etc. notation here instead 8.1610 + of "point", "box", etc. in order to distinguish them from any previously 8.1611 + defined functions. */ 8.1612 + 8.1613 +/* function needed for dummy types and/or not implemented features */ 8.1614 +PG_FUNCTION_INFO_V1(pgl_notimpl); 8.1615 +Datum pgl_notimpl(PG_FUNCTION_ARGS) { 8.1616 + ereport(ERROR, (errmsg("not implemented by pgLatLon"))); 8.1617 +} 8.1618 + 8.1619 +/* set point to latitude and longitude (including checks) */ 8.1620 +static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) { 8.1621 + /* reject infinite or NaN values */ 8.1622 + if (!isfinite(lat) || !isfinite(lon)) { 8.1623 + ereport(ERROR, ( 8.1624 + errcode(ERRCODE_DATA_EXCEPTION), 8.1625 + errmsg("epoint requires finite coordinates") 8.1626 + )); 8.1627 + } 8.1628 + /* check latitude bounds */ 8.1629 + if (lat < -90) { 8.1630 + ereport(WARNING, (errmsg("latitude exceeds south pole"))); 8.1631 + lat = -90; 8.1632 + } else if (lat > 90) { 8.1633 + ereport(WARNING, (errmsg("latitude exceeds north pole"))); 8.1634 + lat = 90; 8.1635 + } 8.1636 + /* check longitude bounds */ 8.1637 + if (lon < -180) { 8.1638 + ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized"))); 8.1639 + lon += 360 - trunc(lon / 360) * 360; 8.1640 + } else if (lon > 180) { 8.1641 + ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized"))); 8.1642 + lon -= 360 + trunc(lon / 360) * 360; 8.1643 + } 8.1644 + /* store rounded latitude/longitude values for round-trip safety */ 8.1645 + point->lat = pgl_round(lat); 8.1646 + point->lon = pgl_round(lon); 8.1647 +} 8.1648 + 8.1649 +/* create point ("epoint" in SQL) from latitude and longitude */ 8.1650 +PG_FUNCTION_INFO_V1(pgl_create_epoint); 8.1651 +Datum pgl_create_epoint(PG_FUNCTION_ARGS) { 8.1652 + pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point)); 8.1653 + pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1)); 8.1654 + PG_RETURN_POINTER(point); 8.1655 +} 8.1656 + 8.1657 +/* parse point ("epoint" in SQL) */ 8.1658 +/* format: '[NS]<float> [EW]<float>' */ 8.1659 +PG_FUNCTION_INFO_V1(pgl_epoint_in); 8.1660 +Datum pgl_epoint_in(PG_FUNCTION_ARGS) { 8.1661 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.1662 + char *strptr = str; /* current position within string */ 8.1663 + int done = 0; /* bit mask storing if latitude or longitude was read */ 8.1664 + double lat, lon; /* parsed values as double precision floats */ 8.1665 + pgl_point *point; /* return value (to be palloc'ed) */ 8.1666 + /* parse two floats (each latitude or longitude) separated by white-space */ 8.1667 + done |= pgl_scan(&strptr, &lat, &lon); 8.1668 + if (strptr != str && isspace(strptr[-1])) { 8.1669 + done |= pgl_scan(&strptr, &lat, &lon); 8.1670 + } 8.1671 + /* require end of string, and latitude and longitude parsed successfully */ 8.1672 + if (strptr[0] || done != PGL_SCAN_LATLON) { 8.1673 + ereport(ERROR, ( 8.1674 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.1675 + errmsg("invalid input syntax for type epoint: \"%s\"", str) 8.1676 + )); 8.1677 + } 8.1678 + /* allocate memory for result */ 8.1679 + point = (pgl_point *)palloc(sizeof(pgl_point)); 8.1680 + /* set latitude and longitude (and perform checks) */ 8.1681 + pgl_epoint_set_latlon(point, lat, lon); 8.1682 + /* return result */ 8.1683 + PG_RETURN_POINTER(point); 8.1684 +} 8.1685 + 8.1686 +/* set sample count for numerical integration (including checks) */ 8.1687 +static void pgl_epoint_set_sample_count(pgl_point_sc *search, int32 samples) { 8.1688 + /* require minimum of 6 samples */ 8.1689 + if (samples < 6) { 8.1690 + ereport(ERROR, ( 8.1691 + errcode(ERRCODE_DATA_EXCEPTION), 8.1692 + errmsg("too few sample points for numerical integration (minimum 6)") 8.1693 + )); 8.1694 + } 8.1695 + /* limit sample count to avoid integer overflows on memory allocation */ 8.1696 + if (samples > PGL_CLUSTER_MAXPOINTS) { 8.1697 + ereport(ERROR, ( 8.1698 + errcode(ERRCODE_DATA_EXCEPTION), 8.1699 + errmsg( 8.1700 + "too many sample points for numerical integration (maximum %i)", 8.1701 + PGL_CLUSTER_MAXPOINTS 8.1702 + ) 8.1703 + )); 8.1704 + } 8.1705 + search->samples = samples; 8.1706 +} 8.1707 + 8.1708 +/* create point with sample count for fair distance calculation 8.1709 + ("epoint_with_sample_count" in SQL) from epoint and integer */ 8.1710 +PG_FUNCTION_INFO_V1(pgl_create_epoint_with_sample_count); 8.1711 +Datum pgl_create_epoint_with_sample_count(PG_FUNCTION_ARGS) { 8.1712 + pgl_point_sc *search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc)); 8.1713 + search->point = *(pgl_point *)PG_GETARG_POINTER(0); 8.1714 + pgl_epoint_set_sample_count(search, PG_GETARG_INT32(1)); 8.1715 + PG_RETURN_POINTER(search); 8.1716 +} 8.1717 + 8.1718 +/* parse point with sample count ("epoint_with_sample_count" in SQL) */ 8.1719 +/* format: '[NS]<float> [EW]<float> <integer>' */ 8.1720 +PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_in); 8.1721 +Datum pgl_epoint_with_sample_count_in(PG_FUNCTION_ARGS) { 8.1722 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.1723 + char *strptr = str; /* current position within string */ 8.1724 + double lat, lon; /* parsed values for latitude and longitude */ 8.1725 + int samples; /* parsed value for sample count */ 8.1726 + int valid = 0; /* number of valid chars */ 8.1727 + int done = 0; /* stores if latitude and/or longitude was read */ 8.1728 + pgl_point_sc *search; /* return value (to be palloc'ed) */ 8.1729 + /* demand three blocks separated by whitespace */ 8.1730 + sscanf(strptr, " %*s %*s %*s %n", &valid); 8.1731 + /* if three blocks separated by whitespace exist, parse those blocks */ 8.1732 + if (strptr[valid] == 0) { 8.1733 + /* parse latitude and longitude */ 8.1734 + done |= pgl_scan(&strptr, &lat, &lon); 8.1735 + done |= pgl_scan(&strptr, &lat, &lon); 8.1736 + /* parse sample count (while incr. strptr by number of bytes parsed) */ 8.1737 + valid = 0; 8.1738 + if (sscanf(strptr, " %d %n", &samples, &valid) == 1) strptr += valid; 8.1739 + } 8.1740 + /* require end of string and both latitude and longitude being parsed */ 8.1741 + if (strptr[0] || done != PGL_SCAN_LATLON) { 8.1742 + ereport(ERROR, ( 8.1743 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.1744 + errmsg("invalid input syntax for type ecircle: \"%s\"", str) 8.1745 + )); 8.1746 + } 8.1747 + /* allocate memory for result */ 8.1748 + search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc)); 8.1749 + /* set latitude, longitude, and sample count (while performing checks) */ 8.1750 + pgl_epoint_set_latlon(&search->point, lat, lon); 8.1751 + pgl_epoint_set_sample_count(search, samples); 8.1752 + /* return result */ 8.1753 + PG_RETURN_POINTER(search); 8.1754 +} 8.1755 + 8.1756 +/* create box ("ebox" in SQL) that is empty */ 8.1757 +PG_FUNCTION_INFO_V1(pgl_create_empty_ebox); 8.1758 +Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) { 8.1759 + pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1760 + pgl_box_set_empty(box); 8.1761 + PG_RETURN_POINTER(box); 8.1762 +} 8.1763 + 8.1764 +/* set box to given boundaries (including checks) */ 8.1765 +static void pgl_ebox_set_boundaries( 8.1766 + pgl_box *box, 8.1767 + double lat_min, double lat_max, double lon_min, double lon_max 8.1768 +) { 8.1769 + /* if minimum latitude is greater than maximum latitude, return empty box */ 8.1770 + if (lat_min > lat_max) { 8.1771 + pgl_box_set_empty(box); 8.1772 + return; 8.1773 + } 8.1774 + /* otherwise reject infinite or NaN values */ 8.1775 + if ( 8.1776 + !isfinite(lat_min) || !isfinite(lat_max) || 8.1777 + !isfinite(lon_min) || !isfinite(lon_max) 8.1778 + ) { 8.1779 + ereport(ERROR, ( 8.1780 + errcode(ERRCODE_DATA_EXCEPTION), 8.1781 + errmsg("ebox requires finite coordinates") 8.1782 + )); 8.1783 + } 8.1784 + /* check latitude bounds */ 8.1785 + if (lat_max < -90) { 8.1786 + ereport(WARNING, (errmsg("northern latitude exceeds south pole"))); 8.1787 + lat_max = -90; 8.1788 + } else if (lat_max > 90) { 8.1789 + ereport(WARNING, (errmsg("northern latitude exceeds north pole"))); 8.1790 + lat_max = 90; 8.1791 + } 8.1792 + if (lat_min < -90) { 8.1793 + ereport(WARNING, (errmsg("southern latitude exceeds south pole"))); 8.1794 + lat_min = -90; 8.1795 + } else if (lat_min > 90) { 8.1796 + ereport(WARNING, (errmsg("southern latitude exceeds north pole"))); 8.1797 + lat_min = 90; 8.1798 + } 8.1799 + /* check if all longitudes are included */ 8.1800 + if (lon_max - lon_min >= 360) { 8.1801 + if (lon_max - lon_min > 360) ereport(WARNING, ( 8.1802 + errmsg("longitude coverage greater than 360 degrees") 8.1803 + )); 8.1804 + lon_min = -180; 8.1805 + lon_max = 180; 8.1806 + } else { 8.1807 + /* normalize longitude bounds */ 8.1808 + if (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360; 8.1809 + else if (lon_min > 180) lon_min -= 360 + trunc(lon_min / 360) * 360; 8.1810 + if (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360; 8.1811 + else if (lon_max > 180) lon_max -= 360 + trunc(lon_max / 360) * 360; 8.1812 + } 8.1813 + /* store rounded latitude/longitude values for round-trip safety */ 8.1814 + box->lat_min = pgl_round(lat_min); 8.1815 + box->lat_max = pgl_round(lat_max); 8.1816 + box->lon_min = pgl_round(lon_min); 8.1817 + box->lon_max = pgl_round(lon_max); 8.1818 + /* ensure that rounding does not change orientation */ 8.1819 + if (lon_min > lon_max && box->lon_min == box->lon_max) { 8.1820 + box->lon_min = -180; 8.1821 + box->lon_max = 180; 8.1822 + } 8.1823 +} 8.1824 + 8.1825 +/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */ 8.1826 +PG_FUNCTION_INFO_V1(pgl_create_ebox); 8.1827 +Datum pgl_create_ebox(PG_FUNCTION_ARGS) { 8.1828 + pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1829 + pgl_ebox_set_boundaries( 8.1830 + box, 8.1831 + PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), 8.1832 + PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3) 8.1833 + ); 8.1834 + PG_RETURN_POINTER(box); 8.1835 +} 8.1836 + 8.1837 +/* create box ("ebox" in SQL) from two points ("epoint"s) */ 8.1838 +/* (can not be used to cover a longitude range of more than 120 degrees) */ 8.1839 +PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints); 8.1840 +Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) { 8.1841 + pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0); 8.1842 + pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1); 8.1843 + pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1844 + double lat_min, lat_max, lon_min, lon_max; 8.1845 + double dlon; /* longitude range (delta longitude) */ 8.1846 + /* order latitude and longitude boundaries */ 8.1847 + if (point2->lat < point1->lat) { 8.1848 + lat_min = point2->lat; 8.1849 + lat_max = point1->lat; 8.1850 + } else { 8.1851 + lat_min = point1->lat; 8.1852 + lat_max = point2->lat; 8.1853 + } 8.1854 + if (point2->lon < point1->lon) { 8.1855 + lon_min = point2->lon; 8.1856 + lon_max = point1->lon; 8.1857 + } else { 8.1858 + lon_min = point1->lon; 8.1859 + lon_max = point2->lon; 8.1860 + } 8.1861 + /* calculate longitude range (round to avoid floating point errors) */ 8.1862 + dlon = pgl_round(lon_max - lon_min); 8.1863 + /* determine east-west direction */ 8.1864 + if (dlon >= 240) { 8.1865 + /* assume that 180th meridian is crossed and swap min/max longitude */ 8.1866 + double swap = lon_min; lon_min = lon_max; lon_max = swap; 8.1867 + } else if (dlon > 120) { 8.1868 + /* unclear orientation since delta longitude > 120 */ 8.1869 + ereport(ERROR, ( 8.1870 + errcode(ERRCODE_DATA_EXCEPTION), 8.1871 + errmsg("can not determine east/west orientation for ebox") 8.1872 + )); 8.1873 + } 8.1874 + /* use boundaries to setup box (and perform checks) */ 8.1875 + pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max); 8.1876 + /* return result */ 8.1877 + PG_RETURN_POINTER(box); 8.1878 +} 8.1879 + 8.1880 +/* parse box ("ebox" in SQL) */ 8.1881 +/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>' 8.1882 + or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */ 8.1883 +PG_FUNCTION_INFO_V1(pgl_ebox_in); 8.1884 +Datum pgl_ebox_in(PG_FUNCTION_ARGS) { 8.1885 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.1886 + char *str_lower; /* lower case version of input string */ 8.1887 + char *strptr; /* current position within string */ 8.1888 + int valid; /* number of valid chars */ 8.1889 + int done; /* specifies if latitude or longitude was read */ 8.1890 + double val; /* temporary variable */ 8.1891 + int lat_count = 0; /* count of latitude values parsed */ 8.1892 + int lon_count = 0; /* count of longitufde values parsed */ 8.1893 + double lat_min, lat_max, lon_min, lon_max; /* see pgl_box struct */ 8.1894 + pgl_box *box; /* return value (to be palloc'ed) */ 8.1895 + /* lowercase input */ 8.1896 + str_lower = psprintf("%s", str); 8.1897 + for (strptr=str_lower; *strptr; strptr++) { 8.1898 + if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A'; 8.1899 + } 8.1900 + /* reset reading position to start of (lowercase) string */ 8.1901 + strptr = str_lower; 8.1902 + /* check if empty box */ 8.1903 + valid = 0; 8.1904 + sscanf(strptr, " empty %n", &valid); 8.1905 + if (valid && strptr[valid] == 0) { 8.1906 + /* allocate and return empty box */ 8.1907 + box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1908 + pgl_box_set_empty(box); 8.1909 + PG_RETURN_POINTER(box); 8.1910 + } 8.1911 + /* demand four blocks separated by whitespace */ 8.1912 + valid = 0; 8.1913 + sscanf(strptr, " %*s %*s %*s %*s %n", &valid); 8.1914 + /* if four blocks separated by whitespace exist, parse those blocks */ 8.1915 + if (strptr[valid] == 0) while (strptr[0]) { 8.1916 + /* parse either latitude or longitude (whichever found in input string) */ 8.1917 + done = pgl_scan(&strptr, &val, &val); 8.1918 + /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */ 8.1919 + if (done == PGL_SCAN_LAT) { 8.1920 + if (!lat_count) lat_min = val; else lat_max = val; 8.1921 + lat_count++; 8.1922 + } else if (done == PGL_SCAN_LON) { 8.1923 + if (!lon_count) lon_min = val; else lon_max = val; 8.1924 + lon_count++; 8.1925 + } else { 8.1926 + break; 8.1927 + } 8.1928 + } 8.1929 + /* require end of string, and two latitude and two longitude values */ 8.1930 + if (strptr[0] || lat_count != 2 || lon_count != 2) { 8.1931 + ereport(ERROR, ( 8.1932 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.1933 + errmsg("invalid input syntax for type ebox: \"%s\"", str) 8.1934 + )); 8.1935 + } 8.1936 + /* free lower case string */ 8.1937 + pfree(str_lower); 8.1938 + /* order boundaries (maximum greater than minimum) */ 8.1939 + if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; } 8.1940 + if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; } 8.1941 + /* allocate memory for result */ 8.1942 + box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1943 + /* set boundaries (and perform checks) */ 8.1944 + pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max); 8.1945 + /* return result */ 8.1946 + PG_RETURN_POINTER(box); 8.1947 +} 8.1948 + 8.1949 +/* set circle to given latitude, longitude, and radius (including checks) */ 8.1950 +static void pgl_ecircle_set_latlon_radius( 8.1951 + pgl_circle *circle, double lat, double lon, double radius 8.1952 +) { 8.1953 + /* set center point (including checks) */ 8.1954 + pgl_epoint_set_latlon(&(circle->center), lat, lon); 8.1955 + /* handle non-positive radius */ 8.1956 + if (isnan(radius)) { 8.1957 + ereport(ERROR, ( 8.1958 + errcode(ERRCODE_DATA_EXCEPTION), 8.1959 + errmsg("invalid radius for ecircle") 8.1960 + )); 8.1961 + } 8.1962 + if (radius == 0) radius = 0; /* avoids -0 */ 8.1963 + else if (radius < 0) { 8.1964 + if (isfinite(radius)) { 8.1965 + ereport(NOTICE, (errmsg("negative radius converted to minus infinity"))); 8.1966 + } 8.1967 + radius = -INFINITY; 8.1968 + } 8.1969 + /* store radius (round-trip safety is ensured by pgl_print_float) */ 8.1970 + circle->radius = radius; 8.1971 +} 8.1972 + 8.1973 +/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */ 8.1974 +PG_FUNCTION_INFO_V1(pgl_create_ecircle); 8.1975 +Datum pgl_create_ecircle(PG_FUNCTION_ARGS) { 8.1976 + pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 8.1977 + pgl_ecircle_set_latlon_radius( 8.1978 + circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2) 8.1979 + ); 8.1980 + PG_RETURN_POINTER(circle); 8.1981 +} 8.1982 + 8.1983 +/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */ 8.1984 +PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint); 8.1985 +Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) { 8.1986 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.1987 + double radius = PG_GETARG_FLOAT8(1); 8.1988 + pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 8.1989 + /* set latitude, longitude, radius (and perform checks) */ 8.1990 + pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius); 8.1991 + /* return result */ 8.1992 + PG_RETURN_POINTER(circle); 8.1993 +} 8.1994 + 8.1995 +/* parse circle ("ecircle" in SQL) */ 8.1996 +/* format: '[NS]<float> [EW]<float> <float>' */ 8.1997 +PG_FUNCTION_INFO_V1(pgl_ecircle_in); 8.1998 +Datum pgl_ecircle_in(PG_FUNCTION_ARGS) { 8.1999 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.2000 + char *strptr = str; /* current position within string */ 8.2001 + double lat, lon, radius; /* parsed values as double precision floats */ 8.2002 + int valid = 0; /* number of valid chars */ 8.2003 + int done = 0; /* stores if latitude and/or longitude was read */ 8.2004 + pgl_circle *circle; /* return value (to be palloc'ed) */ 8.2005 + /* demand three blocks separated by whitespace */ 8.2006 + sscanf(strptr, " %*s %*s %*s %n", &valid); 8.2007 + /* if three blocks separated by whitespace exist, parse those blocks */ 8.2008 + if (strptr[valid] == 0) { 8.2009 + /* parse latitude and longitude */ 8.2010 + done |= pgl_scan(&strptr, &lat, &lon); 8.2011 + done |= pgl_scan(&strptr, &lat, &lon); 8.2012 + /* parse radius (while incrementing strptr by number of bytes parsed) */ 8.2013 + valid = 0; 8.2014 + if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid; 8.2015 + } 8.2016 + /* require end of string and both latitude and longitude being parsed */ 8.2017 + if (strptr[0] || done != PGL_SCAN_LATLON) { 8.2018 + ereport(ERROR, ( 8.2019 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.2020 + errmsg("invalid input syntax for type ecircle: \"%s\"", str) 8.2021 + )); 8.2022 + } 8.2023 + /* allocate memory for result */ 8.2024 + circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 8.2025 + /* set latitude, longitude, radius (and perform checks) */ 8.2026 + pgl_ecircle_set_latlon_radius(circle, lat, lon, radius); 8.2027 + /* return result */ 8.2028 + PG_RETURN_POINTER(circle); 8.2029 +} 8.2030 + 8.2031 +/* parse cluster ("ecluster" in SQL) */ 8.2032 +PG_FUNCTION_INFO_V1(pgl_ecluster_in); 8.2033 +Datum pgl_ecluster_in(PG_FUNCTION_ARGS) { 8.2034 + int i; 8.2035 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.2036 + char *str_lower; /* lower case version of input string */ 8.2037 + char *strptr; /* pointer to current reading position of input */ 8.2038 + int npoints_total = 0; /* total number of points in cluster */ 8.2039 + int nentries = 0; /* total number of entries */ 8.2040 + pgl_newentry *entries; /* array of pgl_newentry to create pgl_cluster */ 8.2041 + int entries_buflen = 4; /* maximum number of elements in entries array */ 8.2042 + int valid; /* number of valid chars processed */ 8.2043 + double lat, lon; /* latitude and longitude of parsed point */ 8.2044 + int entrytype; /* current entry type */ 8.2045 + int npoints; /* number of points in current entry */ 8.2046 + pgl_point *points; /* array of pgl_point for pgl_newentry */ 8.2047 + int points_buflen; /* maximum number of elements in points array */ 8.2048 + int done; /* return value of pgl_scan function */ 8.2049 + pgl_cluster *cluster; /* created cluster */ 8.2050 + /* lowercase input */ 8.2051 + str_lower = psprintf("%s", str); 8.2052 + for (strptr=str_lower; *strptr; strptr++) { 8.2053 + if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A'; 8.2054 + } 8.2055 + /* reset reading position to start of (lowercase) string */ 8.2056 + strptr = str_lower; 8.2057 + /* allocate initial buffer for entries */ 8.2058 + entries = palloc(entries_buflen * sizeof(pgl_newentry)); 8.2059 + /* parse until end of string */ 8.2060 + while (strptr[0]) { 8.2061 + /* require previous white-space or closing parenthesis before next token */ 8.2062 + if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') { 8.2063 + goto pgl_ecluster_in_error; 8.2064 + } 8.2065 + /* ignore token "empty" */ 8.2066 + valid = 0; sscanf(strptr, " empty %n", &valid); 8.2067 + if (valid) { strptr += valid; continue; } 8.2068 + /* test for "point" token */ 8.2069 + valid = 0; sscanf(strptr, " point ( %n", &valid); 8.2070 + if (valid) { 8.2071 + strptr += valid; 8.2072 + entrytype = PGL_ENTRY_POINT; 8.2073 + goto pgl_ecluster_in_type_ok; 8.2074 + } 8.2075 + /* test for "path" token */ 8.2076 + valid = 0; sscanf(strptr, " path ( %n", &valid); 8.2077 + if (valid) { 8.2078 + strptr += valid; 8.2079 + entrytype = PGL_ENTRY_PATH; 8.2080 + goto pgl_ecluster_in_type_ok; 8.2081 + } 8.2082 + /* test for "outline" token */ 8.2083 + valid = 0; sscanf(strptr, " outline ( %n", &valid); 8.2084 + if (valid) { 8.2085 + strptr += valid; 8.2086 + entrytype = PGL_ENTRY_OUTLINE; 8.2087 + goto pgl_ecluster_in_type_ok; 8.2088 + } 8.2089 + /* test for "polygon" token */ 8.2090 + valid = 0; sscanf(strptr, " polygon ( %n", &valid); 8.2091 + if (valid) { 8.2092 + strptr += valid; 8.2093 + entrytype = PGL_ENTRY_POLYGON; 8.2094 + goto pgl_ecluster_in_type_ok; 8.2095 + } 8.2096 + /* error if no valid token found */ 8.2097 + goto pgl_ecluster_in_error; 8.2098 + pgl_ecluster_in_type_ok: 8.2099 + /* check if pgl_newentry array needs to grow */ 8.2100 + if (nentries == entries_buflen) { 8.2101 + pgl_newentry *newbuf; 8.2102 + entries_buflen *= 2; 8.2103 + newbuf = palloc(entries_buflen * sizeof(pgl_newentry)); 8.2104 + memcpy(newbuf, entries, nentries * sizeof(pgl_newentry)); 8.2105 + pfree(entries); 8.2106 + entries = newbuf; 8.2107 + } 8.2108 + /* reset number of points for current entry */ 8.2109 + npoints = 0; 8.2110 + /* allocate array for points */ 8.2111 + points_buflen = 4; 8.2112 + points = palloc(points_buflen * sizeof(pgl_point)); 8.2113 + /* parse until closing parenthesis */ 8.2114 + while (strptr[0] != ')') { 8.2115 + /* error on unexpected end of string */ 8.2116 + if (strptr[0] == 0) goto pgl_ecluster_in_error; 8.2117 + /* mark neither latitude nor longitude as read */ 8.2118 + done = PGL_SCAN_NONE; 8.2119 + /* require white-space before second, third, etc. point */ 8.2120 + if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error; 8.2121 + /* scan latitude (or longitude) */ 8.2122 + done |= pgl_scan(&strptr, &lat, &lon); 8.2123 + /* require white-space before second coordinate */ 8.2124 + if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error; 8.2125 + /* scan longitude (or latitude) */ 8.2126 + done |= pgl_scan(&strptr, &lat, &lon); 8.2127 + /* error unless both latitude and longitude were parsed */ 8.2128 + if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error; 8.2129 + /* throw error if number of points is too high */ 8.2130 + if (npoints_total == PGL_CLUSTER_MAXPOINTS) { 8.2131 + ereport(ERROR, ( 8.2132 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.2133 + errmsg( 8.2134 + "too many points for ecluster entry (maximum %i)", 8.2135 + PGL_CLUSTER_MAXPOINTS 8.2136 + ) 8.2137 + )); 8.2138 + } 8.2139 + /* check if pgl_point array needs to grow */ 8.2140 + if (npoints == points_buflen) { 8.2141 + pgl_point *newbuf; 8.2142 + points_buflen *= 2; 8.2143 + newbuf = palloc(points_buflen * sizeof(pgl_point)); 8.2144 + memcpy(newbuf, points, npoints * sizeof(pgl_point)); 8.2145 + pfree(points); 8.2146 + points = newbuf; 8.2147 + } 8.2148 + /* append point to pgl_point array (includes checks) */ 8.2149 + pgl_epoint_set_latlon(&(points[npoints++]), lat, lon); 8.2150 + /* increase total number of points */ 8.2151 + npoints_total++; 8.2152 + } 8.2153 + /* error if entry has no points */ 8.2154 + if (!npoints) goto pgl_ecluster_in_error; 8.2155 + /* entries with one point are automatically of type "point" */ 8.2156 + if (npoints == 1) entrytype = PGL_ENTRY_POINT; 8.2157 + /* if entries have more than one point */ 8.2158 + else { 8.2159 + /* throw error if entry type is "point" */ 8.2160 + if (entrytype == PGL_ENTRY_POINT) { 8.2161 + ereport(ERROR, ( 8.2162 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.2163 + errmsg("invalid input syntax for type ecluster (point entry with more than one point)") 8.2164 + )); 8.2165 + } 8.2166 + /* coerce outlines and polygons with more than 2 points to be a path */ 8.2167 + if (npoints == 2) entrytype = PGL_ENTRY_PATH; 8.2168 + } 8.2169 + /* append entry to pgl_newentry array */ 8.2170 + entries[nentries].entrytype = entrytype; 8.2171 + entries[nentries].npoints = npoints; 8.2172 + entries[nentries].points = points; 8.2173 + nentries++; 8.2174 + /* consume closing parenthesis */ 8.2175 + strptr++; 8.2176 + /* consume white-space */ 8.2177 + while (isspace(strptr[0])) strptr++; 8.2178 + } 8.2179 + /* free lower case string */ 8.2180 + pfree(str_lower); 8.2181 + /* create cluster from pgl_newentry array */ 8.2182 + cluster = pgl_new_cluster(nentries, entries); 8.2183 + /* free pgl_newentry array */ 8.2184 + for (i=0; i<nentries; i++) pfree(entries[i].points); 8.2185 + pfree(entries); 8.2186 + /* set bounding circle of cluster and check east/west orientation */ 8.2187 + if (!pgl_finalize_cluster(cluster)) { 8.2188 + ereport(ERROR, ( 8.2189 + errcode(ERRCODE_DATA_EXCEPTION), 8.2190 + errmsg("can not determine east/west orientation for ecluster"), 8.2191 + errhint("Ensure that each entry has a longitude span of less than 180 degrees.") 8.2192 + )); 8.2193 + } 8.2194 + /* return cluster */ 8.2195 + PG_RETURN_POINTER(cluster); 8.2196 + /* code to throw error */ 8.2197 + pgl_ecluster_in_error: 8.2198 + ereport(ERROR, ( 8.2199 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.2200 + errmsg("invalid input syntax for type ecluster: \"%s\"", str) 8.2201 + )); 8.2202 +} 8.2203 + 8.2204 +/* convert point ("epoint") to string representation */ 8.2205 +PG_FUNCTION_INFO_V1(pgl_epoint_out); 8.2206 +Datum pgl_epoint_out(PG_FUNCTION_ARGS) { 8.2207 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2208 + char latstr[PGL_NUMBUFLEN]; 8.2209 + char lonstr[PGL_NUMBUFLEN]; 8.2210 + pgl_print_lat(latstr, point->lat); 8.2211 + pgl_print_lon(lonstr, point->lon); 8.2212 + PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr)); 8.2213 +} 8.2214 + 8.2215 +/* convert point with sample count ("epoint_with_sample_count") to str. rep. */ 8.2216 +PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_out); 8.2217 +Datum pgl_epoint_with_sample_count_out(PG_FUNCTION_ARGS) { 8.2218 + pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(0); 8.2219 + char latstr[PGL_NUMBUFLEN]; 8.2220 + char lonstr[PGL_NUMBUFLEN]; 8.2221 + pgl_print_lat(latstr, search->point.lat); 8.2222 + pgl_print_lon(lonstr, search->point.lon); 8.2223 + PG_RETURN_CSTRING( 8.2224 + psprintf("%s %s %i", latstr, lonstr, (int)search->samples) 8.2225 + ); 8.2226 +} 8.2227 + 8.2228 +/* convert box ("ebox") to string representation */ 8.2229 +PG_FUNCTION_INFO_V1(pgl_ebox_out); 8.2230 +Datum pgl_ebox_out(PG_FUNCTION_ARGS) { 8.2231 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2232 + double lon_min = box->lon_min; 8.2233 + double lon_max = box->lon_max; 8.2234 + char lat_min_str[PGL_NUMBUFLEN]; 8.2235 + char lat_max_str[PGL_NUMBUFLEN]; 8.2236 + char lon_min_str[PGL_NUMBUFLEN]; 8.2237 + char lon_max_str[PGL_NUMBUFLEN]; 8.2238 + /* return string "empty" if box is set to be empty */ 8.2239 + if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty"); 8.2240 + /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */ 8.2241 + /* (required since pgl_box_in orders the longitude boundaries) */ 8.2242 + if (lon_min > lon_max) { 8.2243 + if (lon_min + lon_max >= 0) lon_min -= 360; 8.2244 + else lon_max += 360; 8.2245 + } 8.2246 + /* format and return result */ 8.2247 + pgl_print_lat(lat_min_str, box->lat_min); 8.2248 + pgl_print_lat(lat_max_str, box->lat_max); 8.2249 + pgl_print_lon(lon_min_str, lon_min); 8.2250 + pgl_print_lon(lon_max_str, lon_max); 8.2251 + PG_RETURN_CSTRING(psprintf( 8.2252 + "%s %s %s %s", 8.2253 + lat_min_str, lon_min_str, lat_max_str, lon_max_str 8.2254 + )); 8.2255 +} 8.2256 + 8.2257 +/* convert circle ("ecircle") to string representation */ 8.2258 +PG_FUNCTION_INFO_V1(pgl_ecircle_out); 8.2259 +Datum pgl_ecircle_out(PG_FUNCTION_ARGS) { 8.2260 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2261 + char latstr[PGL_NUMBUFLEN]; 8.2262 + char lonstr[PGL_NUMBUFLEN]; 8.2263 + char radstr[PGL_NUMBUFLEN]; 8.2264 + pgl_print_lat(latstr, circle->center.lat); 8.2265 + pgl_print_lon(lonstr, circle->center.lon); 8.2266 + pgl_print_float(radstr, circle->radius); 8.2267 + PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr)); 8.2268 +} 8.2269 + 8.2270 +/* convert cluster ("ecluster") to string representation */ 8.2271 +PG_FUNCTION_INFO_V1(pgl_ecluster_out); 8.2272 +Datum pgl_ecluster_out(PG_FUNCTION_ARGS) { 8.2273 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2274 + char latstr[PGL_NUMBUFLEN]; /* string buffer for latitude */ 8.2275 + char lonstr[PGL_NUMBUFLEN]; /* string buffer for longitude */ 8.2276 + char ***strings; /* array of array of strings */ 8.2277 + char *string; /* string of current token */ 8.2278 + char *res, *resptr; /* result and pointer to current write position */ 8.2279 + size_t reslen = 1; /* length of result (init with 1 for terminator) */ 8.2280 + int npoints; /* number of points of current entry */ 8.2281 + int i, j; /* i: entry, j: point in entry */ 8.2282 + /* handle empty clusters */ 8.2283 + if (cluster->nentries == 0) { 8.2284 + /* free detoasted cluster (if copy) */ 8.2285 + PG_FREE_IF_COPY(cluster, 0); 8.2286 + /* return static result */ 8.2287 + PG_RETURN_CSTRING("empty"); 8.2288 + } 8.2289 + /* allocate array of array of strings */ 8.2290 + strings = palloc(cluster->nentries * sizeof(char **)); 8.2291 + /* iterate over all entries in cluster */ 8.2292 + for (i=0; i<cluster->nentries; i++) { 8.2293 + /* get number of points in entry */ 8.2294 + npoints = cluster->entries[i].npoints; 8.2295 + /* allocate array of strings (one string for each point plus two extra) */ 8.2296 + strings[i] = palloc((2 + npoints) * sizeof(char *)); 8.2297 + /* determine opening string */ 8.2298 + switch (cluster->entries[i].entrytype) { 8.2299 + case PGL_ENTRY_POINT: string = (i==0)?"point (" :" point ("; break; 8.2300 + case PGL_ENTRY_PATH: string = (i==0)?"path (" :" path ("; break; 8.2301 + case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break; 8.2302 + case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break; 8.2303 + default: string = (i==0)?"unknown" :" unknown"; 8.2304 + } 8.2305 + /* use opening string as first string in array */ 8.2306 + strings[i][0] = string; 8.2307 + /* update result length (for allocating result string later) */ 8.2308 + reslen += strlen(string); 8.2309 + /* iterate over all points */ 8.2310 + for (j=0; j<npoints; j++) { 8.2311 + /* create string representation of point */ 8.2312 + pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat); 8.2313 + pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon); 8.2314 + string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr); 8.2315 + /* copy string pointer to string array */ 8.2316 + strings[i][j+1] = string; 8.2317 + /* update result length (for allocating result string later) */ 8.2318 + reslen += strlen(string); 8.2319 + } 8.2320 + /* use closing parenthesis as last string in array */ 8.2321 + strings[i][npoints+1] = ")"; 8.2322 + /* update result length (for allocating result string later) */ 8.2323 + reslen++; 8.2324 + } 8.2325 + /* allocate result string */ 8.2326 + res = palloc(reslen); 8.2327 + /* set write pointer to begin of result string */ 8.2328 + resptr = res; 8.2329 + /* copy strings into result string */ 8.2330 + for (i=0; i<cluster->nentries; i++) { 8.2331 + npoints = cluster->entries[i].npoints; 8.2332 + for (j=0; j<npoints+2; j++) { 8.2333 + string = strings[i][j]; 8.2334 + strcpy(resptr, string); 8.2335 + resptr += strlen(string); 8.2336 + /* free strings allocated by psprintf */ 8.2337 + if (j != 0 && j != npoints+1) pfree(string); 8.2338 + } 8.2339 + /* free array of strings */ 8.2340 + pfree(strings[i]); 8.2341 + } 8.2342 + /* free array of array of strings */ 8.2343 + pfree(strings); 8.2344 + /* free detoasted cluster (if copy) */ 8.2345 + PG_FREE_IF_COPY(cluster, 0); 8.2346 + /* return result */ 8.2347 + PG_RETURN_CSTRING(res); 8.2348 +} 8.2349 + 8.2350 +/* binary input function for point ("epoint") */ 8.2351 +PG_FUNCTION_INFO_V1(pgl_epoint_recv); 8.2352 +Datum pgl_epoint_recv(PG_FUNCTION_ARGS) { 8.2353 + StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 8.2354 + pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point)); 8.2355 + point->lat = pq_getmsgfloat8(buf); 8.2356 + point->lon = pq_getmsgfloat8(buf); 8.2357 + PG_RETURN_POINTER(point); 8.2358 +} 8.2359 + 8.2360 +/* binary input function for box ("ebox") */ 8.2361 +PG_FUNCTION_INFO_V1(pgl_ebox_recv); 8.2362 +Datum pgl_ebox_recv(PG_FUNCTION_ARGS) { 8.2363 + StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 8.2364 + pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 8.2365 + box->lat_min = pq_getmsgfloat8(buf); 8.2366 + box->lat_max = pq_getmsgfloat8(buf); 8.2367 + box->lon_min = pq_getmsgfloat8(buf); 8.2368 + box->lon_max = pq_getmsgfloat8(buf); 8.2369 + PG_RETURN_POINTER(box); 8.2370 +} 8.2371 + 8.2372 +/* binary input function for circle ("ecircle") */ 8.2373 +PG_FUNCTION_INFO_V1(pgl_ecircle_recv); 8.2374 +Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) { 8.2375 + StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 8.2376 + pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 8.2377 + circle->center.lat = pq_getmsgfloat8(buf); 8.2378 + circle->center.lon = pq_getmsgfloat8(buf); 8.2379 + circle->radius = pq_getmsgfloat8(buf); 8.2380 + PG_RETURN_POINTER(circle); 8.2381 +} 8.2382 + 8.2383 +/* TODO: binary receive function for cluster */ 8.2384 + 8.2385 +/* binary output function for point ("epoint") */ 8.2386 +PG_FUNCTION_INFO_V1(pgl_epoint_send); 8.2387 +Datum pgl_epoint_send(PG_FUNCTION_ARGS) { 8.2388 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2389 + StringInfoData buf; 8.2390 + pq_begintypsend(&buf); 8.2391 + pq_sendfloat8(&buf, point->lat); 8.2392 + pq_sendfloat8(&buf, point->lon); 8.2393 + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 8.2394 +} 8.2395 + 8.2396 +/* binary output function for box ("ebox") */ 8.2397 +PG_FUNCTION_INFO_V1(pgl_ebox_send); 8.2398 +Datum pgl_ebox_send(PG_FUNCTION_ARGS) { 8.2399 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2400 + StringInfoData buf; 8.2401 + pq_begintypsend(&buf); 8.2402 + pq_sendfloat8(&buf, box->lat_min); 8.2403 + pq_sendfloat8(&buf, box->lat_max); 8.2404 + pq_sendfloat8(&buf, box->lon_min); 8.2405 + pq_sendfloat8(&buf, box->lon_max); 8.2406 + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 8.2407 +} 8.2408 + 8.2409 +/* binary output function for circle ("ecircle") */ 8.2410 +PG_FUNCTION_INFO_V1(pgl_ecircle_send); 8.2411 +Datum pgl_ecircle_send(PG_FUNCTION_ARGS) { 8.2412 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2413 + StringInfoData buf; 8.2414 + pq_begintypsend(&buf); 8.2415 + pq_sendfloat8(&buf, circle->center.lat); 8.2416 + pq_sendfloat8(&buf, circle->center.lon); 8.2417 + pq_sendfloat8(&buf, circle->radius); 8.2418 + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 8.2419 +} 8.2420 + 8.2421 +/* TODO: binary send functions for cluster */ 8.2422 + 8.2423 +/* cast point ("epoint") to box ("ebox") */ 8.2424 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox); 8.2425 +Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) { 8.2426 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2427 + pgl_box *box = palloc(sizeof(pgl_box)); 8.2428 + box->lat_min = point->lat; 8.2429 + box->lat_max = point->lat; 8.2430 + box->lon_min = point->lon; 8.2431 + box->lon_max = point->lon; 8.2432 + PG_RETURN_POINTER(box); 8.2433 +} 8.2434 + 8.2435 +/* cast point ("epoint") to circle ("ecircle") */ 8.2436 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle); 8.2437 +Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) { 8.2438 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2439 + pgl_circle *circle = palloc(sizeof(pgl_box)); 8.2440 + circle->center = *point; 8.2441 + circle->radius = 0; 8.2442 + PG_RETURN_POINTER(circle); 8.2443 +} 8.2444 + 8.2445 +/* cast point ("epoint") to cluster ("ecluster") */ 8.2446 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster); 8.2447 +Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) { 8.2448 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2449 + pgl_newentry entry; 8.2450 + pgl_cluster *cluster; 8.2451 + entry.entrytype = PGL_ENTRY_POINT; 8.2452 + entry.npoints = 1; 8.2453 + entry.points = point; 8.2454 + cluster = pgl_new_cluster(1, &entry); 8.2455 + pgl_finalize_cluster(cluster); /* NOTE: should not fail */ 8.2456 + PG_RETURN_POINTER(cluster); 8.2457 +} 8.2458 + 8.2459 +/* cast box ("ebox") to cluster ("ecluster") */ 8.2460 +#define pgl_ebox_to_ecluster_macro(i, a, b) \ 8.2461 + entries[i].entrytype = PGL_ENTRY_POLYGON; \ 8.2462 + entries[i].npoints = 4; \ 8.2463 + entries[i].points = points[i]; \ 8.2464 + points[i][0].lat = box->lat_min; \ 8.2465 + points[i][0].lon = (a); \ 8.2466 + points[i][1].lat = box->lat_min; \ 8.2467 + points[i][1].lon = (b); \ 8.2468 + points[i][2].lat = box->lat_max; \ 8.2469 + points[i][2].lon = (b); \ 8.2470 + points[i][3].lat = box->lat_max; \ 8.2471 + points[i][3].lon = (a); 8.2472 +PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster); 8.2473 +Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) { 8.2474 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2475 + double lon, dlon; 8.2476 + int nentries; 8.2477 + pgl_newentry entries[3]; 8.2478 + pgl_point points[3][4]; 8.2479 + pgl_cluster *cluster; 8.2480 + if (box->lat_min > box->lat_max) { 8.2481 + nentries = 0; 8.2482 + } else if (box->lon_min > box->lon_max) { 8.2483 + if (box->lon_min < 0) { 8.2484 + lon = pgl_round((box->lon_min + 180) / 2.0); 8.2485 + nentries = 3; 8.2486 + pgl_ebox_to_ecluster_macro(0, box->lon_min, lon); 8.2487 + pgl_ebox_to_ecluster_macro(1, lon, 180); 8.2488 + pgl_ebox_to_ecluster_macro(2, -180, box->lon_max); 8.2489 + } else if (box->lon_max > 0) { 8.2490 + lon = pgl_round((box->lon_max - 180) / 2.0); 8.2491 + nentries = 3; 8.2492 + pgl_ebox_to_ecluster_macro(0, box->lon_min, 180); 8.2493 + pgl_ebox_to_ecluster_macro(1, -180, lon); 8.2494 + pgl_ebox_to_ecluster_macro(2, lon, box->lon_max); 8.2495 + } else { 8.2496 + nentries = 2; 8.2497 + pgl_ebox_to_ecluster_macro(0, box->lon_min, 180); 8.2498 + pgl_ebox_to_ecluster_macro(1, -180, box->lon_max); 8.2499 + } 8.2500 + } else { 8.2501 + dlon = pgl_round(box->lon_max - box->lon_min); 8.2502 + if (dlon < 180) { 8.2503 + nentries = 1; 8.2504 + pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max); 8.2505 + } else { 8.2506 + lon = pgl_round((box->lon_min + box->lon_max) / 2.0); 8.2507 + if ( 8.2508 + pgl_round(lon - box->lon_min) < 180 && 8.2509 + pgl_round(box->lon_max - lon) < 180 8.2510 + ) { 8.2511 + nentries = 2; 8.2512 + pgl_ebox_to_ecluster_macro(0, box->lon_min, lon); 8.2513 + pgl_ebox_to_ecluster_macro(1, lon, box->lon_max); 8.2514 + } else { 8.2515 + nentries = 3; 8.2516 + pgl_ebox_to_ecluster_macro(0, box->lon_min, -60); 8.2517 + pgl_ebox_to_ecluster_macro(1, -60, 60); 8.2518 + pgl_ebox_to_ecluster_macro(2, 60, box->lon_max); 8.2519 + } 8.2520 + } 8.2521 + } 8.2522 + cluster = pgl_new_cluster(nentries, entries); 8.2523 + pgl_finalize_cluster(cluster); /* NOTE: should not fail */ 8.2524 + PG_RETURN_POINTER(cluster); 8.2525 +} 8.2526 + 8.2527 +/* extract latitude from point ("epoint") */ 8.2528 +PG_FUNCTION_INFO_V1(pgl_epoint_lat); 8.2529 +Datum pgl_epoint_lat(PG_FUNCTION_ARGS) { 8.2530 + PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat); 8.2531 +} 8.2532 + 8.2533 +/* extract longitude from point ("epoint") */ 8.2534 +PG_FUNCTION_INFO_V1(pgl_epoint_lon); 8.2535 +Datum pgl_epoint_lon(PG_FUNCTION_ARGS) { 8.2536 + PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon); 8.2537 +} 8.2538 + 8.2539 +/* extract minimum latitude from box ("ebox") */ 8.2540 +PG_FUNCTION_INFO_V1(pgl_ebox_lat_min); 8.2541 +Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) { 8.2542 + PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min); 8.2543 +} 8.2544 + 8.2545 +/* extract maximum latitude from box ("ebox") */ 8.2546 +PG_FUNCTION_INFO_V1(pgl_ebox_lat_max); 8.2547 +Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) { 8.2548 + PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max); 8.2549 +} 8.2550 + 8.2551 +/* extract minimum longitude from box ("ebox") */ 8.2552 +PG_FUNCTION_INFO_V1(pgl_ebox_lon_min); 8.2553 +Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) { 8.2554 + PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min); 8.2555 +} 8.2556 + 8.2557 +/* extract maximum longitude from box ("ebox") */ 8.2558 +PG_FUNCTION_INFO_V1(pgl_ebox_lon_max); 8.2559 +Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) { 8.2560 + PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max); 8.2561 +} 8.2562 + 8.2563 +/* extract center point from circle ("ecircle") */ 8.2564 +PG_FUNCTION_INFO_V1(pgl_ecircle_center); 8.2565 +Datum pgl_ecircle_center(PG_FUNCTION_ARGS) { 8.2566 + PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center)); 8.2567 +} 8.2568 + 8.2569 +/* extract radius from circle ("ecircle") */ 8.2570 +PG_FUNCTION_INFO_V1(pgl_ecircle_radius); 8.2571 +Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) { 8.2572 + PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius); 8.2573 +} 8.2574 + 8.2575 +/* check if point is inside box (overlap operator "&&") in SQL */ 8.2576 +PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap); 8.2577 +Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) { 8.2578 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2579 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1); 8.2580 + PG_RETURN_BOOL(pgl_point_in_box(point, box)); 8.2581 +} 8.2582 + 8.2583 +/* check if point is inside circle (overlap operator "&&") in SQL */ 8.2584 +PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap); 8.2585 +Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) { 8.2586 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2587 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 8.2588 + PG_RETURN_BOOL( 8.2589 + pgl_distance( 8.2590 + point->lat, point->lon, 8.2591 + circle->center.lat, circle->center.lon 8.2592 + ) <= circle->radius 8.2593 + ); 8.2594 +} 8.2595 + 8.2596 +/* check if point is inside cluster (overlap operator "&&") in SQL */ 8.2597 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap); 8.2598 +Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) { 8.2599 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2600 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2601 + bool retval; 8.2602 + /* points outside bounding circle are always assumed to be non-overlapping */ 8.2603 + if ( 8.2604 + pgl_distance( 8.2605 + point->lat, point->lon, 8.2606 + cluster->bounding.center.lat, cluster->bounding.center.lon 8.2607 + ) > cluster->bounding.radius 8.2608 + ) retval = false; 8.2609 + else retval = pgl_point_in_cluster(point, cluster, false); 8.2610 + PG_FREE_IF_COPY(cluster, 1); 8.2611 + PG_RETURN_BOOL(retval); 8.2612 +} 8.2613 + 8.2614 +/* check if point may be inside cluster (lossy overl. operator "&&+") in SQL */ 8.2615 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_may_overlap); 8.2616 +Datum pgl_epoint_ecluster_may_overlap(PG_FUNCTION_ARGS) { 8.2617 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2618 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2619 + bool retval = pgl_distance( 8.2620 + point->lat, point->lon, 8.2621 + cluster->bounding.center.lat, cluster->bounding.center.lon 8.2622 + ) <= cluster->bounding.radius; 8.2623 + PG_FREE_IF_COPY(cluster, 1); 8.2624 + PG_RETURN_BOOL(retval); 8.2625 +} 8.2626 + 8.2627 +/* check if two boxes overlap (overlap operator "&&") in SQL */ 8.2628 +PG_FUNCTION_INFO_V1(pgl_ebox_overlap); 8.2629 +Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) { 8.2630 + pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0); 8.2631 + pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1); 8.2632 + PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2)); 8.2633 +} 8.2634 + 8.2635 +/* check if box and circle may overlap (lossy overl. operator "&&+") in SQL */ 8.2636 +PG_FUNCTION_INFO_V1(pgl_ebox_ecircle_may_overlap); 8.2637 +Datum pgl_ebox_ecircle_may_overlap(PG_FUNCTION_ARGS) { 8.2638 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2639 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 8.2640 + PG_RETURN_BOOL( 8.2641 + pgl_estimate_point_box_distance(&circle->center, box) <= circle->radius 8.2642 + ); 8.2643 +} 8.2644 + 8.2645 +/* check if box and cluster may overlap (lossy overl. operator "&&+") in SQL */ 8.2646 +PG_FUNCTION_INFO_V1(pgl_ebox_ecluster_may_overlap); 8.2647 +Datum pgl_ebox_ecluster_may_overlap(PG_FUNCTION_ARGS) { 8.2648 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2649 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2650 + bool retval = pgl_estimate_point_box_distance( 8.2651 + &cluster->bounding.center, 8.2652 + box 8.2653 + ) <= cluster->bounding.radius; 8.2654 + PG_FREE_IF_COPY(cluster, 1); 8.2655 + PG_RETURN_BOOL(retval); 8.2656 +} 8.2657 + 8.2658 +/* check if two circles overlap (overlap operator "&&") in SQL */ 8.2659 +PG_FUNCTION_INFO_V1(pgl_ecircle_overlap); 8.2660 +Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) { 8.2661 + pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0); 8.2662 + pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1); 8.2663 + PG_RETURN_BOOL( 8.2664 + pgl_distance( 8.2665 + circle1->center.lat, circle1->center.lon, 8.2666 + circle2->center.lat, circle2->center.lon 8.2667 + ) <= circle1->radius + circle2->radius 8.2668 + ); 8.2669 +} 8.2670 + 8.2671 +/* check if circle and cluster overlap (overlap operator "&&") in SQL */ 8.2672 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap); 8.2673 +Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) { 8.2674 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2675 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2676 + bool retval; 8.2677 + /* points outside bounding circle (with margin for flattening) are always 8.2678 + assumed to be non-overlapping */ 8.2679 + if ( 8.2680 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2681 + pgl_distance( 8.2682 + circle->center.lat, circle->center.lon, 8.2683 + cluster->bounding.center.lat, cluster->bounding.center.lon 8.2684 + ) > circle->radius + cluster->bounding.radius 8.2685 + ) retval = false; 8.2686 + else retval = pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius; 8.2687 + PG_FREE_IF_COPY(cluster, 1); 8.2688 + PG_RETURN_BOOL(retval); 8.2689 +} 8.2690 + 8.2691 +/* check if circle and cluster may overlap (l. ov. operator "&&+") in SQL */ 8.2692 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_may_overlap); 8.2693 +Datum pgl_ecircle_ecluster_may_overlap(PG_FUNCTION_ARGS) { 8.2694 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2695 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2696 + bool retval = ( 8.2697 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2698 + pgl_distance( 8.2699 + circle->center.lat, circle->center.lon, 8.2700 + cluster->bounding.center.lat, cluster->bounding.center.lon 8.2701 + ) 8.2702 + ) <= circle->radius + cluster->bounding.radius; 8.2703 + PG_FREE_IF_COPY(cluster, 1); 8.2704 + PG_RETURN_BOOL(retval); 8.2705 +} 8.2706 + 8.2707 +/* check if two clusters overlap (overlap operator "&&") in SQL */ 8.2708 +PG_FUNCTION_INFO_V1(pgl_ecluster_overlap); 8.2709 +Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) { 8.2710 + pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2711 + pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2712 + bool retval; 8.2713 + /* clusters with non-touching bounding circles (with margin for flattening) 8.2714 + are always assumed to be non-overlapping */ 8.2715 + if ( 8.2716 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2717 + pgl_distance( 8.2718 + cluster1->bounding.center.lat, cluster1->bounding.center.lon, 8.2719 + cluster2->bounding.center.lat, cluster2->bounding.center.lon 8.2720 + ) > cluster1->bounding.radius + cluster2->bounding.radius 8.2721 + ) retval = false; 8.2722 + else retval = pgl_clusters_overlap(cluster1, cluster2); 8.2723 + PG_FREE_IF_COPY(cluster1, 0); 8.2724 + PG_FREE_IF_COPY(cluster2, 1); 8.2725 + PG_RETURN_BOOL(retval); 8.2726 +} 8.2727 + 8.2728 +/* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */ 8.2729 +PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap); 8.2730 +Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) { 8.2731 + pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2732 + pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2733 + bool retval = ( 8.2734 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2735 + pgl_distance( 8.2736 + cluster1->bounding.center.lat, cluster1->bounding.center.lon, 8.2737 + cluster2->bounding.center.lat, cluster2->bounding.center.lon 8.2738 + ) 8.2739 + ) <= cluster1->bounding.radius + cluster2->bounding.radius; 8.2740 + PG_FREE_IF_COPY(cluster1, 0); 8.2741 + PG_FREE_IF_COPY(cluster2, 1); 8.2742 + PG_RETURN_BOOL(retval); 8.2743 +} 8.2744 + 8.2745 +/* check if second cluster is in first cluster (cont. operator "@>) in SQL */ 8.2746 +PG_FUNCTION_INFO_V1(pgl_ecluster_contains); 8.2747 +Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) { 8.2748 + pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2749 + pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2750 + bool retval; 8.2751 + /* clusters with non-touching bounding circles are always assumed to be 8.2752 + non-overlapping */ 8.2753 + if ( 8.2754 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2755 + pgl_distance( 8.2756 + outer->bounding.center.lat, outer->bounding.center.lon, 8.2757 + inner->bounding.center.lat, inner->bounding.center.lon 8.2758 + ) > outer->bounding.radius + inner->bounding.radius 8.2759 + ) retval = false; 8.2760 + else retval = pgl_cluster_in_cluster(outer, inner); 8.2761 + PG_FREE_IF_COPY(outer, 0); 8.2762 + PG_FREE_IF_COPY(inner, 1); 8.2763 + PG_RETURN_BOOL(retval); 8.2764 +} 8.2765 + 8.2766 +/* calculate distance between two points ("<->" operator) in SQL */ 8.2767 +PG_FUNCTION_INFO_V1(pgl_epoint_distance); 8.2768 +Datum pgl_epoint_distance(PG_FUNCTION_ARGS) { 8.2769 + pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0); 8.2770 + pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1); 8.2771 + PG_RETURN_FLOAT8(pgl_distance( 8.2772 + point1->lat, point1->lon, point2->lat, point2->lon 8.2773 + )); 8.2774 +} 8.2775 + 8.2776 +/* calculate point to circle distance ("<->" operator) in SQL */ 8.2777 +PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance); 8.2778 +Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) { 8.2779 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2780 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 8.2781 + double distance = pgl_distance( 8.2782 + point->lat, point->lon, circle->center.lat, circle->center.lon 8.2783 + ) - circle->radius; 8.2784 + PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 8.2785 +} 8.2786 + 8.2787 +/* calculate point to cluster distance ("<->" operator) in SQL */ 8.2788 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance); 8.2789 +Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) { 8.2790 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2791 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2792 + double distance = pgl_point_cluster_distance(point, cluster); 8.2793 + PG_FREE_IF_COPY(cluster, 1); 8.2794 + PG_RETURN_FLOAT8(distance); 8.2795 +} 8.2796 + 8.2797 +/* calculate distance between two circles ("<->" operator) in SQL */ 8.2798 +PG_FUNCTION_INFO_V1(pgl_ecircle_distance); 8.2799 +Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) { 8.2800 + pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0); 8.2801 + pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1); 8.2802 + double distance = pgl_distance( 8.2803 + circle1->center.lat, circle1->center.lon, 8.2804 + circle2->center.lat, circle2->center.lon 8.2805 + ) - (circle1->radius + circle2->radius); 8.2806 + PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 8.2807 +} 8.2808 + 8.2809 +/* calculate circle to cluster distance ("<->" operator) in SQL */ 8.2810 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance); 8.2811 +Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) { 8.2812 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2813 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2814 + double distance = ( 8.2815 + pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius 8.2816 + ); 8.2817 + PG_FREE_IF_COPY(cluster, 1); 8.2818 + PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 8.2819 +} 8.2820 + 8.2821 +/* calculate distance between two clusters ("<->" operator) in SQL */ 8.2822 +PG_FUNCTION_INFO_V1(pgl_ecluster_distance); 8.2823 +Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) { 8.2824 + pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2825 + pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2826 + double retval = pgl_cluster_distance(cluster1, cluster2); 8.2827 + PG_FREE_IF_COPY(cluster1, 0); 8.2828 + PG_FREE_IF_COPY(cluster2, 1); 8.2829 + PG_RETURN_FLOAT8(retval); 8.2830 +} 8.2831 + 8.2832 +/* calculate fair distance (see README) between cluster and point with 8.2833 + precision denoted by sample count ("<=> operator) in SQL */ 8.2834 +PG_FUNCTION_INFO_V1(pgl_ecluster_epoint_sc_fair_distance); 8.2835 +Datum pgl_ecluster_epoint_sc_fair_distance(PG_FUNCTION_ARGS) { 8.2836 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2837 + pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(1); 8.2838 + double retval = pgl_fair_distance( 8.2839 + &search->point, cluster, search->samples 8.2840 + ); 8.2841 + PG_FREE_IF_COPY(cluster, 0); 8.2842 + PG_RETURN_FLOAT8(retval); 8.2843 +} 8.2844 + 8.2845 + 8.2846 +/*-----------------------------------------------------------* 8.2847 + * B-tree comparison operators and index support functions * 8.2848 + *-----------------------------------------------------------*/ 8.2849 + 8.2850 +/* macro for a B-tree operator (without detoasting) */ 8.2851 +#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \ 8.2852 + PG_FUNCTION_INFO_V1(func); \ 8.2853 + Datum func(PG_FUNCTION_ARGS) { \ 8.2854 + type *a = (type *)PG_GETARG_POINTER(0); \ 8.2855 + type *b = (type *)PG_GETARG_POINTER(1); \ 8.2856 + PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \ 8.2857 + } 8.2858 + 8.2859 +/* macro for a B-tree comparison function (without detoasting) */ 8.2860 +#define PGL_BTREE_CMP(func, type, cmpfunc) \ 8.2861 + PG_FUNCTION_INFO_V1(func); \ 8.2862 + Datum func(PG_FUNCTION_ARGS) { \ 8.2863 + type *a = (type *)PG_GETARG_POINTER(0); \ 8.2864 + type *b = (type *)PG_GETARG_POINTER(1); \ 8.2865 + PG_RETURN_INT32(cmpfunc(a, b)); \ 8.2866 + } 8.2867 + 8.2868 +/* macro for a B-tree operator (with detoasting) */ 8.2869 +#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \ 8.2870 + PG_FUNCTION_INFO_V1(func); \ 8.2871 + Datum func(PG_FUNCTION_ARGS) { \ 8.2872 + bool res; \ 8.2873 + type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \ 8.2874 + type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \ 8.2875 + res = cmpfunc(a, b) oper 0; \ 8.2876 + PG_FREE_IF_COPY(a, 0); \ 8.2877 + PG_FREE_IF_COPY(b, 1); \ 8.2878 + PG_RETURN_BOOL(res); \ 8.2879 + } 8.2880 + 8.2881 +/* macro for a B-tree comparison function (with detoasting) */ 8.2882 +#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \ 8.2883 + PG_FUNCTION_INFO_V1(func); \ 8.2884 + Datum func(PG_FUNCTION_ARGS) { \ 8.2885 + int32_t res; \ 8.2886 + type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \ 8.2887 + type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \ 8.2888 + res = cmpfunc(a, b); \ 8.2889 + PG_FREE_IF_COPY(a, 0); \ 8.2890 + PG_FREE_IF_COPY(b, 1); \ 8.2891 + PG_RETURN_INT32(res); \ 8.2892 + } 8.2893 + 8.2894 +/* B-tree operators and comparison function for point */ 8.2895 +PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <) 8.2896 +PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=) 8.2897 +PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==) 8.2898 +PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=) 8.2899 +PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=) 8.2900 +PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >) 8.2901 +PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp) 8.2902 + 8.2903 +/* B-tree operators and comparison function for box */ 8.2904 +PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <) 8.2905 +PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=) 8.2906 +PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==) 8.2907 +PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=) 8.2908 +PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=) 8.2909 +PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >) 8.2910 +PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp) 8.2911 + 8.2912 +/* B-tree operators and comparison function for circle */ 8.2913 +PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <) 8.2914 +PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=) 8.2915 +PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==) 8.2916 +PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=) 8.2917 +PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=) 8.2918 +PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >) 8.2919 +PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp) 8.2920 + 8.2921 + 8.2922 +/*--------------------------------* 8.2923 + * GiST index support functions * 8.2924 + *--------------------------------*/ 8.2925 + 8.2926 +/* GiST "consistent" support function */ 8.2927 +PG_FUNCTION_INFO_V1(pgl_gist_consistent); 8.2928 +Datum pgl_gist_consistent(PG_FUNCTION_ARGS) { 8.2929 + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 8.2930 + pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key); 8.2931 + StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); 8.2932 + bool *recheck = (bool *)PG_GETARG_POINTER(4); 8.2933 + /* demand recheck because index and query methods are lossy */ 8.2934 + *recheck = true; 8.2935 + /* strategy number aliases for different operators using the same strategy */ 8.2936 + strategy %= 100; 8.2937 + /* strategy number 11: equality of two points */ 8.2938 + if (strategy == 11) { 8.2939 + /* query datum is another point */ 8.2940 + pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 8.2941 + /* convert other point to key */ 8.2942 + pgl_pointkey querykey; 8.2943 + pgl_point_to_key(query, querykey); 8.2944 + /* return true if both keys overlap */ 8.2945 + PG_RETURN_BOOL(pgl_keys_overlap(key, querykey)); 8.2946 + } 8.2947 + /* strategy number 13: equality of two circles */ 8.2948 + if (strategy == 13) { 8.2949 + /* query datum is another circle */ 8.2950 + pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 8.2951 + /* convert other circle to key */ 8.2952 + pgl_areakey querykey; 8.2953 + pgl_circle_to_key(query, querykey); 8.2954 + /* return true if both keys overlap */ 8.2955 + PG_RETURN_BOOL(pgl_keys_overlap(key, querykey)); 8.2956 + } 8.2957 + /* for all remaining strategies, keys on empty objects produce no match */ 8.2958 + /* (check necessary because query radius may be infinite) */ 8.2959 + if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false); 8.2960 + /* strategy number 21: overlapping with point */ 8.2961 + if (strategy == 21) { 8.2962 + /* query datum is a point */ 8.2963 + pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 8.2964 + /* return true if estimated distance (allowed to be smaller than real 8.2965 + distance) between index key and point is zero */ 8.2966 + PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0); 8.2967 + } 8.2968 + /* strategy number 22: (point) overlapping with box */ 8.2969 + if (strategy == 22) { 8.2970 + /* query datum is a box */ 8.2971 + pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1); 8.2972 + /* determine bounding box of indexed key */ 8.2973 + pgl_box keybox; 8.2974 + pgl_key_to_box(key, &keybox); 8.2975 + /* return true if query box overlaps with bounding box of indexed key */ 8.2976 + PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox)); 8.2977 + } 8.2978 + /* strategy number 23: overlapping with circle */ 8.2979 + if (strategy == 23) { 8.2980 + /* query datum is a circle */ 8.2981 + pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 8.2982 + /* return true if estimated distance (allowed to be smaller than real 8.2983 + distance) between index key and circle center is smaller than radius */ 8.2984 + PG_RETURN_BOOL( 8.2985 + (1.0-PGL_SPHEROID_F) * /* safety margin for lossy operator */ 8.2986 + pgl_estimate_key_distance(key, &(query->center)) 8.2987 + <= query->radius 8.2988 + ); 8.2989 + } 8.2990 + /* strategy number 24: overlapping with cluster */ 8.2991 + if (strategy == 24) { 8.2992 + bool retval; /* return value */ 8.2993 + /* query datum is a cluster */ 8.2994 + pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2995 + /* return true if estimated distance (allowed to be smaller than real 8.2996 + distance) between index key and circle center is smaller than radius */ 8.2997 + retval = ( 8.2998 + (1.0-PGL_SPHEROID_F) * /* safety margin for lossy operator */ 8.2999 + pgl_estimate_key_distance(key, &(query->bounding.center)) 8.3000 + <= query->bounding.radius 8.3001 + ); 8.3002 + PG_FREE_IF_COPY(query, 1); /* free detoasted cluster (if copy) */ 8.3003 + PG_RETURN_BOOL(retval); 8.3004 + } 8.3005 + /* throw error for any unknown strategy number */ 8.3006 + elog(ERROR, "unrecognized strategy number: %d", strategy); 8.3007 +} 8.3008 + 8.3009 +/* GiST "union" support function */ 8.3010 +PG_FUNCTION_INFO_V1(pgl_gist_union); 8.3011 +Datum pgl_gist_union(PG_FUNCTION_ARGS) { 8.3012 + GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); 8.3013 + pgl_keyptr out; /* return value (to be palloc'ed) */ 8.3014 + int i; 8.3015 + /* determine key size */ 8.3016 + size_t keysize = PGL_KEY_IS_AREAKEY( 8.3017 + (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key) 8.3018 + ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey); 8.3019 + /* begin with first key as result */ 8.3020 + out = palloc(keysize); 8.3021 + memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize); 8.3022 + /* unite current result with second, third, etc. key */ 8.3023 + for (i=1; i<entryvec->n; i++) { 8.3024 + pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)); 8.3025 + } 8.3026 + /* return result */ 8.3027 + PG_RETURN_POINTER(out); 8.3028 +} 8.3029 + 8.3030 +/* GiST "compress" support function for indicis on points */ 8.3031 +PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint); 8.3032 +Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) { 8.3033 + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 8.3034 + GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 8.3035 + /* only transform new leaves */ 8.3036 + if (entry->leafkey) { 8.3037 + /* get point to be transformed */ 8.3038 + pgl_point *point = (pgl_point *)DatumGetPointer(entry->key); 8.3039 + /* allocate memory for key */ 8.3040 + pgl_keyptr key = palloc(sizeof(pgl_pointkey)); 8.3041 + /* transform point to key */ 8.3042 + pgl_point_to_key(point, key); 8.3043 + /* create new GISTENTRY structure as return value */ 8.3044 + retval = palloc(sizeof(GISTENTRY)); 8.3045 + gistentryinit( 8.3046 + *retval, PointerGetDatum(key), 8.3047 + entry->rel, entry->page, entry->offset, false 8.3048 + ); 8.3049 + } else { 8.3050 + /* inner nodes have already been transformed */ 8.3051 + retval = entry; 8.3052 + } 8.3053 + /* return pointer to old or new GISTENTRY structure */ 8.3054 + PG_RETURN_POINTER(retval); 8.3055 +} 8.3056 + 8.3057 +/* GiST "compress" support function for indicis on circles */ 8.3058 +PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle); 8.3059 +Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) { 8.3060 + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 8.3061 + GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 8.3062 + /* only transform new leaves */ 8.3063 + if (entry->leafkey) { 8.3064 + /* get circle to be transformed */ 8.3065 + pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key); 8.3066 + /* allocate memory for key */ 8.3067 + pgl_keyptr key = palloc(sizeof(pgl_areakey)); 8.3068 + /* transform circle to key */ 8.3069 + pgl_circle_to_key(circle, key); 8.3070 + /* create new GISTENTRY structure as return value */ 8.3071 + retval = palloc(sizeof(GISTENTRY)); 8.3072 + gistentryinit( 8.3073 + *retval, PointerGetDatum(key), 8.3074 + entry->rel, entry->page, entry->offset, false 8.3075 + ); 8.3076 + } else { 8.3077 + /* inner nodes have already been transformed */ 8.3078 + retval = entry; 8.3079 + } 8.3080 + /* return pointer to old or new GISTENTRY structure */ 8.3081 + PG_RETURN_POINTER(retval); 8.3082 +} 8.3083 + 8.3084 +/* GiST "compress" support function for indices on clusters */ 8.3085 +PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster); 8.3086 +Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) { 8.3087 + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 8.3088 + GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 8.3089 + /* only transform new leaves */ 8.3090 + if (entry->leafkey) { 8.3091 + /* get cluster to be transformed (detoasting necessary!) */ 8.3092 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key); 8.3093 + /* allocate memory for key */ 8.3094 + pgl_keyptr key = palloc(sizeof(pgl_areakey)); 8.3095 + /* transform cluster to key */ 8.3096 + pgl_circle_to_key(&(cluster->bounding), key); 8.3097 + /* create new GISTENTRY structure as return value */ 8.3098 + retval = palloc(sizeof(GISTENTRY)); 8.3099 + gistentryinit( 8.3100 + *retval, PointerGetDatum(key), 8.3101 + entry->rel, entry->page, entry->offset, false 8.3102 + ); 8.3103 + /* free detoasted datum */ 8.3104 + if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster); 8.3105 + } else { 8.3106 + /* inner nodes have already been transformed */ 8.3107 + retval = entry; 8.3108 + } 8.3109 + /* return pointer to old or new GISTENTRY structure */ 8.3110 + PG_RETURN_POINTER(retval); 8.3111 +} 8.3112 + 8.3113 +/* GiST "decompress" support function for indices */ 8.3114 +PG_FUNCTION_INFO_V1(pgl_gist_decompress); 8.3115 +Datum pgl_gist_decompress(PG_FUNCTION_ARGS) { 8.3116 + /* return passed pointer without transformation */ 8.3117 + PG_RETURN_POINTER(PG_GETARG_POINTER(0)); 8.3118 +} 8.3119 + 8.3120 +/* GiST "penalty" support function */ 8.3121 +PG_FUNCTION_INFO_V1(pgl_gist_penalty); 8.3122 +Datum pgl_gist_penalty(PG_FUNCTION_ARGS) { 8.3123 + GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0); 8.3124 + GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1); 8.3125 + float *penalty = (float *)PG_GETARG_POINTER(2); 8.3126 + /* get original key and key to insert */ 8.3127 + pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key); 8.3128 + pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key); 8.3129 + /* copy original key */ 8.3130 + union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key; 8.3131 + if (PGL_KEY_IS_AREAKEY(orig)) { 8.3132 + memcpy(union_key.areakey, orig, sizeof(union_key.areakey)); 8.3133 + } else { 8.3134 + memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey)); 8.3135 + } 8.3136 + /* calculate union of both keys */ 8.3137 + pgl_unite_keys((pgl_keyptr)&union_key, new); 8.3138 + /* penalty equal to reduction of key length (logarithm of added area) */ 8.3139 + /* (return value by setting referenced value and returning pointer) */ 8.3140 + *penalty = ( 8.3141 + PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key) 8.3142 + ); 8.3143 + PG_RETURN_POINTER(penalty); 8.3144 +} 8.3145 + 8.3146 +/* GiST "picksplit" support function */ 8.3147 +PG_FUNCTION_INFO_V1(pgl_gist_picksplit); 8.3148 +Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) { 8.3149 + GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); 8.3150 + GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1); 8.3151 + OffsetNumber i; /* between FirstOffsetNumber and entryvec->n (exclusive) */ 8.3152 + union { 8.3153 + pgl_pointkey pointkey; 8.3154 + pgl_areakey areakey; 8.3155 + } union_all; /* union of all keys (to be calculated from scratch) 8.3156 + (later cut in half) */ 8.3157 + int is_areakey = PGL_KEY_IS_AREAKEY( 8.3158 + (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key) 8.3159 + ); 8.3160 + int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey); 8.3161 + pgl_keyptr unionL = palloc(keysize); /* union of keys that go left */ 8.3162 + pgl_keyptr unionR = palloc(keysize); /* union of keys that go right */ 8.3163 + pgl_keyptr key; /* current key to be processed */ 8.3164 + /* allocate memory for array of left and right keys, set counts to zero */ 8.3165 + v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber)); 8.3166 + v->spl_nleft = 0; 8.3167 + v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber)); 8.3168 + v->spl_nright = 0; 8.3169 + /* calculate union of all keys from scratch */ 8.3170 + memcpy( 8.3171 + (pgl_keyptr)&union_all, 8.3172 + (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key), 8.3173 + keysize 8.3174 + ); 8.3175 + for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) { 8.3176 + pgl_unite_keys( 8.3177 + (pgl_keyptr)&union_all, 8.3178 + (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key) 8.3179 + ); 8.3180 + } 8.3181 + /* check if trivial split is necessary due to exhausted key length */ 8.3182 + /* (Note: keys for empty objects must have node depth set to maximum) */ 8.3183 + if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == ( 8.3184 + is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH 8.3185 + )) { 8.3186 + /* half of all keys go left */ 8.3187 + for ( 8.3188 + i=FirstOffsetNumber; 8.3189 + i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2; 8.3190 + i=OffsetNumberNext(i) 8.3191 + ) { 8.3192 + /* pointer to current key */ 8.3193 + key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 8.3194 + /* update unionL */ 8.3195 + /* check if key is first key that goes left */ 8.3196 + if (!v->spl_nleft) { 8.3197 + /* first key that goes left is just copied to unionL */ 8.3198 + memcpy(unionL, key, keysize); 8.3199 + } else { 8.3200 + /* unite current value and next key */ 8.3201 + pgl_unite_keys(unionL, key); 8.3202 + } 8.3203 + /* append offset number to list of keys that go left */ 8.3204 + v->spl_left[v->spl_nleft++] = i; 8.3205 + } 8.3206 + /* other half goes right */ 8.3207 + for ( 8.3208 + i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2; 8.3209 + i<entryvec->n; 8.3210 + i=OffsetNumberNext(i) 8.3211 + ) { 8.3212 + /* pointer to current key */ 8.3213 + key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 8.3214 + /* update unionR */ 8.3215 + /* check if key is first key that goes right */ 8.3216 + if (!v->spl_nright) { 8.3217 + /* first key that goes right is just copied to unionR */ 8.3218 + memcpy(unionR, key, keysize); 8.3219 + } else { 8.3220 + /* unite current value and next key */ 8.3221 + pgl_unite_keys(unionR, key); 8.3222 + } 8.3223 + /* append offset number to list of keys that go right */ 8.3224 + v->spl_right[v->spl_nright++] = i; 8.3225 + } 8.3226 + } 8.3227 + /* otherwise, a non-trivial split is possible */ 8.3228 + else { 8.3229 + /* cut covered area in half */ 8.3230 + /* (union_all then refers to area of keys that go left) */ 8.3231 + /* check if union of all keys covers empty and non-empty objects */ 8.3232 + if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) { 8.3233 + /* if yes, split into empty and non-empty objects */ 8.3234 + pgl_key_set_empty((pgl_keyptr)&union_all); 8.3235 + } else { 8.3236 + /* otherwise split by next bit */ 8.3237 + ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++; 8.3238 + /* NOTE: type bit conserved */ 8.3239 + } 8.3240 + /* determine for each key if it goes left or right */ 8.3241 + for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) { 8.3242 + /* pointer to current key */ 8.3243 + key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 8.3244 + /* keys within one half of the area go left */ 8.3245 + if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) { 8.3246 + /* update unionL */ 8.3247 + /* check if key is first key that goes left */ 8.3248 + if (!v->spl_nleft) { 8.3249 + /* first key that goes left is just copied to unionL */ 8.3250 + memcpy(unionL, key, keysize); 8.3251 + } else { 8.3252 + /* unite current value of unionL and processed key */ 8.3253 + pgl_unite_keys(unionL, key); 8.3254 + } 8.3255 + /* append offset number to list of keys that go left */ 8.3256 + v->spl_left[v->spl_nleft++] = i; 8.3257 + } 8.3258 + /* the other keys go right */ 8.3259 + else { 8.3260 + /* update unionR */ 8.3261 + /* check if key is first key that goes right */ 8.3262 + if (!v->spl_nright) { 8.3263 + /* first key that goes right is just copied to unionR */ 8.3264 + memcpy(unionR, key, keysize); 8.3265 + } else { 8.3266 + /* unite current value of unionR and processed key */ 8.3267 + pgl_unite_keys(unionR, key); 8.3268 + } 8.3269 + /* append offset number to list of keys that go right */ 8.3270 + v->spl_right[v->spl_nright++] = i; 8.3271 + } 8.3272 + } 8.3273 + } 8.3274 + /* store unions in return value */ 8.3275 + v->spl_ldatum = PointerGetDatum(unionL); 8.3276 + v->spl_rdatum = PointerGetDatum(unionR); 8.3277 + /* return all results */ 8.3278 + PG_RETURN_POINTER(v); 8.3279 +} 8.3280 + 8.3281 +/* GiST "same"/"equal" support function */ 8.3282 +PG_FUNCTION_INFO_V1(pgl_gist_same); 8.3283 +Datum pgl_gist_same(PG_FUNCTION_ARGS) { 8.3284 + pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0); 8.3285 + pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1); 8.3286 + bool *boolptr = (bool *)PG_GETARG_POINTER(2); 8.3287 + /* two keys are equal if they are binary equal */ 8.3288 + /* (return result by setting referenced boolean and returning pointer) */ 8.3289 + *boolptr = !memcmp( 8.3290 + key1, 8.3291 + key2, 8.3292 + PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey) 8.3293 + ); 8.3294 + PG_RETURN_POINTER(boolptr); 8.3295 +} 8.3296 + 8.3297 +/* GiST "distance" support function */ 8.3298 +PG_FUNCTION_INFO_V1(pgl_gist_distance); 8.3299 +Datum pgl_gist_distance(PG_FUNCTION_ARGS) { 8.3300 + GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); 8.3301 + pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key); 8.3302 + StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); 8.3303 + bool *recheck = (bool *)PG_GETARG_POINTER(4); 8.3304 + double distance; /* return value */ 8.3305 + /* demand recheck because distance is just an estimation */ 8.3306 + /* (real distance may be bigger) */ 8.3307 + *recheck = true; 8.3308 + /* strategy number aliases for different operators using the same strategy */ 8.3309 + strategy %= 100; 8.3310 + /* strategy number 31: distance to point */ 8.3311 + if (strategy == 31) { 8.3312 + /* query datum is a point */ 8.3313 + pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 8.3314 + /* use pgl_estimate_pointkey_distance() function to compute result */ 8.3315 + distance = pgl_estimate_key_distance(key, query); 8.3316 + /* avoid infinity (reserved!) */ 8.3317 + if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 8.3318 + /* return result */ 8.3319 + PG_RETURN_FLOAT8(distance); 8.3320 + } 8.3321 + /* strategy number 33: distance to circle */ 8.3322 + if (strategy == 33) { 8.3323 + /* query datum is a circle */ 8.3324 + pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 8.3325 + /* estimate distance to circle center and substract circle radius */ 8.3326 + distance = ( 8.3327 + pgl_estimate_key_distance(key, &(query->center)) - query->radius 8.3328 + ); 8.3329 + /* convert non-positive values to zero and avoid infinity (reserved!) */ 8.3330 + if (distance <= 0) distance = 0; 8.3331 + else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 8.3332 + /* return result */ 8.3333 + PG_RETURN_FLOAT8(distance); 8.3334 + } 8.3335 + /* strategy number 34: distance to cluster */ 8.3336 + if (strategy == 34) { 8.3337 + /* query datum is a cluster */ 8.3338 + pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.3339 + /* estimate distance to bounding center and substract bounding radius */ 8.3340 + distance = ( 8.3341 + pgl_estimate_key_distance(key, &(query->bounding.center)) - 8.3342 + query->bounding.radius 8.3343 + ); 8.3344 + /* convert non-positive values to zero and avoid infinity (reserved!) */ 8.3345 + if (distance <= 0) distance = 0; 8.3346 + else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 8.3347 + /* free detoasted cluster (if copy) */ 8.3348 + PG_FREE_IF_COPY(query, 1); 8.3349 + /* return result */ 8.3350 + PG_RETURN_FLOAT8(distance); 8.3351 + } 8.3352 + /* throw error for any unknown strategy number */ 8.3353 + elog(ERROR, "unrecognized strategy number: %d", strategy); 8.3354 +} 8.3355 +