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