pgLatLon
changeset 6:2b7aea022117
Remove version 0.1 files (but keep update script from 0.1 to 0.2); Updated version number in manual installation instructions and include extension update script in manual instructions
author | jbe |
---|---|
date | Mon Aug 22 22:00:21 2016 +0200 (2016-08-22) |
parents | 13d26f330e20 |
children | 9e11e9b2c0d9 |
files | GNUmakefile README.html README.mkd latlon--0.1.sql latlon-v0001.c |
line diff
1.1 --- a/GNUmakefile Mon Aug 22 21:52:32 2016 +0200 1.2 +++ b/GNUmakefile Mon Aug 22 22:00:21 2016 +0200 1.3 @@ -1,6 +1,6 @@ 1.4 EXTENSION = latlon 1.5 -DATA = latlon--0.1.sql latlon--0.1--0.2.sql latlon--0.2.sql 1.6 -MODULES = latlon-v0001 latlon-v0002 1.7 +DATA = latlon--0.1--0.2.sql latlon--0.2.sql 1.8 +MODULES = latlon-v0002 1.9 1.10 PG_CONFIG = pg_config 1.11 PGXS := $(shell $(PG_CONFIG) --pgxs)
2.1 --- a/README.html Mon Aug 22 21:52:32 2016 +0200 2.2 +++ b/README.html Mon Aug 22 22:00:21 2016 +0200 2.3 @@ -37,10 +37,10 @@ 2.4 <p>It is also possible to compile and install the extension without GNU Make as 2.5 follows:</p> 2.6 2.7 -<pre><code>cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0001.so latlon-v0001.c 2.8 -cp latlon-v0001.so `pg_config --pkglibdir` 2.9 +<pre><code>cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0002.so latlon-v0002.c 2.10 +cp latlon-v0002.so `pg_config --pkglibdir` 2.11 cp latlon.control `pg_config --sharedir`/extension/ 2.12 -cp latlon--0.1.sql `pg_config --sharedir`/extension/ 2.13 +cp latlon--0.1--0.2.sql latlon--0.2.sql `pg_config --sharedir`/extension/ 2.14 </code></pre> 2.15 2.16 <h3>Loading the extension</h3>
3.1 --- a/README.mkd Mon Aug 22 21:52:32 2016 +0200 3.2 +++ b/README.mkd Mon Aug 22 22:00:21 2016 +0200 3.3 @@ -36,10 +36,10 @@ 3.4 It is also possible to compile and install the extension without GNU Make as 3.5 follows: 3.6 3.7 - cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0001.so latlon-v0001.c 3.8 - cp latlon-v0001.so `pg_config --pkglibdir` 3.9 + cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0002.so latlon-v0002.c 3.10 + cp latlon-v0002.so `pg_config --pkglibdir` 3.11 cp latlon.control `pg_config --sharedir`/extension/ 3.12 - cp latlon--0.1.sql `pg_config --sharedir`/extension/ 3.13 + cp latlon--0.1--0.2.sql latlon--0.2.sql `pg_config --sharedir`/extension/ 3.14 3.15 ### Loading the extension 3.16
4.1 --- a/latlon--0.1.sql Mon Aug 22 21:52:32 2016 +0200 4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 4.3 @@ -1,1205 +0,0 @@ 4.4 - 4.5 ----------------------------------------- 4.6 --- forward declarations (shell types) -- 4.7 ----------------------------------------- 4.8 - 4.9 -CREATE TYPE epoint; 4.10 -CREATE TYPE ebox; 4.11 -CREATE TYPE ecircle; 4.12 -CREATE TYPE ecluster; 4.13 - 4.14 - 4.15 ------------------------------------------------------------- 4.16 --- dummy input/output functions for dummy index key types -- 4.17 ------------------------------------------------------------- 4.18 - 4.19 -CREATE FUNCTION ekey_point_in_dummy(cstring) 4.20 - RETURNS ekey_point 4.21 - LANGUAGE C IMMUTABLE STRICT 4.22 - AS '$libdir/latlon-v0001', 'pgl_notimpl'; 4.23 - 4.24 -CREATE FUNCTION ekey_point_out_dummy(ekey_point) 4.25 - RETURNS cstring 4.26 - LANGUAGE C IMMUTABLE STRICT 4.27 - AS '$libdir/latlon-v0001', 'pgl_notimpl'; 4.28 - 4.29 -CREATE FUNCTION ekey_area_in_dummy(cstring) 4.30 - RETURNS ekey_area 4.31 - LANGUAGE C IMMUTABLE STRICT 4.32 - AS '$libdir/latlon-v0001', 'pgl_notimpl'; 4.33 - 4.34 -CREATE FUNCTION ekey_area_out_dummy(ekey_area) 4.35 - RETURNS cstring 4.36 - LANGUAGE C IMMUTABLE STRICT 4.37 - AS '$libdir/latlon-v0001', 'pgl_notimpl'; 4.38 - 4.39 - 4.40 --------------------------- 4.41 --- text input functions -- 4.42 --------------------------- 4.43 - 4.44 -CREATE FUNCTION epoint_in(cstring) 4.45 - RETURNS epoint 4.46 - LANGUAGE C IMMUTABLE STRICT 4.47 - AS '$libdir/latlon-v0001', 'pgl_epoint_in'; 4.48 - 4.49 -CREATE FUNCTION ebox_in(cstring) 4.50 - RETURNS ebox 4.51 - LANGUAGE C IMMUTABLE STRICT 4.52 - AS '$libdir/latlon-v0001', 'pgl_ebox_in'; 4.53 - 4.54 -CREATE FUNCTION ecircle_in(cstring) 4.55 - RETURNS ecircle 4.56 - LANGUAGE C IMMUTABLE STRICT 4.57 - AS '$libdir/latlon-v0001', 'pgl_ecircle_in'; 4.58 - 4.59 -CREATE FUNCTION ecluster_in(cstring) 4.60 - RETURNS ecluster 4.61 - LANGUAGE C IMMUTABLE STRICT 4.62 - AS '$libdir/latlon-v0001', 'pgl_ecluster_in'; 4.63 - 4.64 - 4.65 ---------------------------- 4.66 --- text output functions -- 4.67 ---------------------------- 4.68 - 4.69 -CREATE FUNCTION epoint_out(epoint) 4.70 - RETURNS cstring 4.71 - LANGUAGE C IMMUTABLE STRICT 4.72 - AS '$libdir/latlon-v0001', 'pgl_epoint_out'; 4.73 - 4.74 -CREATE FUNCTION ebox_out(ebox) 4.75 - RETURNS cstring 4.76 - LANGUAGE C IMMUTABLE STRICT 4.77 - AS '$libdir/latlon-v0001', 'pgl_ebox_out'; 4.78 - 4.79 -CREATE FUNCTION ecircle_out(ecircle) 4.80 - RETURNS cstring 4.81 - LANGUAGE C IMMUTABLE STRICT 4.82 - AS '$libdir/latlon-v0001', 'pgl_ecircle_out'; 4.83 - 4.84 -CREATE FUNCTION ecluster_out(ecluster) 4.85 - RETURNS cstring 4.86 - LANGUAGE C IMMUTABLE STRICT 4.87 - AS '$libdir/latlon-v0001', 'pgl_ecluster_out'; 4.88 - 4.89 - 4.90 --------------------------- 4.91 --- binary I/O functions -- 4.92 --------------------------- 4.93 - 4.94 -CREATE FUNCTION epoint_recv(internal) 4.95 - RETURNS epoint 4.96 - LANGUAGE C IMMUTABLE STRICT 4.97 - AS '$libdir/latlon-v0001', 'pgl_epoint_recv'; 4.98 - 4.99 -CREATE FUNCTION ebox_recv(internal) 4.100 - RETURNS ebox 4.101 - LANGUAGE C IMMUTABLE STRICT 4.102 - AS '$libdir/latlon-v0001', 'pgl_ebox_recv'; 4.103 - 4.104 -CREATE FUNCTION ecircle_recv(internal) 4.105 - RETURNS ecircle 4.106 - LANGUAGE C IMMUTABLE STRICT 4.107 - AS '$libdir/latlon-v0001', 'pgl_ecircle_recv'; 4.108 - 4.109 -CREATE FUNCTION epoint_send(epoint) 4.110 - RETURNS bytea 4.111 - LANGUAGE C IMMUTABLE STRICT 4.112 - AS '$libdir/latlon-v0001', 'pgl_epoint_send'; 4.113 - 4.114 -CREATE FUNCTION ebox_send(ebox) 4.115 - RETURNS bytea 4.116 - LANGUAGE C IMMUTABLE STRICT 4.117 - AS '$libdir/latlon-v0001', 'pgl_ebox_send'; 4.118 - 4.119 -CREATE FUNCTION ecircle_send(ecircle) 4.120 - RETURNS bytea 4.121 - LANGUAGE C IMMUTABLE STRICT 4.122 - AS '$libdir/latlon-v0001', 'pgl_ecircle_send'; 4.123 - 4.124 - 4.125 ------------------------------------------------ 4.126 --- type definitions of dummy index key types -- 4.127 ------------------------------------------------ 4.128 - 4.129 -CREATE TYPE ekey_point ( 4.130 - internallength = 8, 4.131 - input = ekey_point_in_dummy, 4.132 - output = ekey_point_out_dummy, 4.133 - alignment = char ); 4.134 - 4.135 -CREATE TYPE ekey_area ( 4.136 - internallength = 9, 4.137 - input = ekey_area_in_dummy, 4.138 - output = ekey_area_out_dummy, 4.139 - alignment = char ); 4.140 - 4.141 - 4.142 ------------------------------------------- 4.143 --- definitions of geographic data types -- 4.144 ------------------------------------------- 4.145 - 4.146 -CREATE TYPE epoint ( 4.147 - internallength = 16, 4.148 - input = epoint_in, 4.149 - output = epoint_out, 4.150 - receive = epoint_recv, 4.151 - send = epoint_send, 4.152 - alignment = double ); 4.153 - 4.154 -CREATE TYPE ebox ( 4.155 - internallength = 32, 4.156 - input = ebox_in, 4.157 - output = ebox_out, 4.158 - receive = ebox_recv, 4.159 - send = ebox_send, 4.160 - alignment = double ); 4.161 - 4.162 -CREATE TYPE ecircle ( 4.163 - internallength = 24, 4.164 - input = ecircle_in, 4.165 - output = ecircle_out, 4.166 - receive = ecircle_recv, 4.167 - send = ecircle_send, 4.168 - alignment = double ); 4.169 - 4.170 -CREATE TYPE ecluster ( 4.171 - internallength = VARIABLE, 4.172 - input = ecluster_in, 4.173 - output = ecluster_out, 4.174 - alignment = double, 4.175 - storage = external ); 4.176 - 4.177 - 4.178 --------------------- 4.179 --- B-tree support -- 4.180 --------------------- 4.181 - 4.182 --- begin of B-tree support for epoint 4.183 - 4.184 -CREATE FUNCTION epoint_btree_lt(epoint, epoint) 4.185 - RETURNS boolean 4.186 - LANGUAGE C IMMUTABLE STRICT 4.187 - AS '$libdir/latlon-v0001', 'pgl_btree_epoint_lt'; 4.188 - 4.189 -CREATE FUNCTION epoint_btree_le(epoint, epoint) 4.190 - RETURNS boolean 4.191 - LANGUAGE C IMMUTABLE STRICT 4.192 - AS '$libdir/latlon-v0001', 'pgl_btree_epoint_le'; 4.193 - 4.194 -CREATE FUNCTION epoint_btree_eq(epoint, epoint) 4.195 - RETURNS boolean 4.196 - LANGUAGE C IMMUTABLE STRICT 4.197 - AS '$libdir/latlon-v0001', 'pgl_btree_epoint_eq'; 4.198 - 4.199 -CREATE FUNCTION epoint_btree_ne(epoint, epoint) 4.200 - RETURNS boolean 4.201 - LANGUAGE C IMMUTABLE STRICT 4.202 - AS '$libdir/latlon-v0001', 'pgl_btree_epoint_ne'; 4.203 - 4.204 -CREATE FUNCTION epoint_btree_ge(epoint, epoint) 4.205 - RETURNS boolean 4.206 - LANGUAGE C IMMUTABLE STRICT 4.207 - AS '$libdir/latlon-v0001', 'pgl_btree_epoint_ge'; 4.208 - 4.209 -CREATE FUNCTION epoint_btree_gt(epoint, epoint) 4.210 - RETURNS boolean 4.211 - LANGUAGE C IMMUTABLE STRICT 4.212 - AS '$libdir/latlon-v0001', 'pgl_btree_epoint_gt'; 4.213 - 4.214 -CREATE OPERATOR <<< ( 4.215 - leftarg = epoint, 4.216 - rightarg = epoint, 4.217 - procedure = epoint_btree_lt, 4.218 - commutator = >>>, 4.219 - negator = >>>=, 4.220 - restrict = scalarltsel, 4.221 - join = scalarltjoinsel 4.222 -); 4.223 - 4.224 -CREATE OPERATOR <<<= ( 4.225 - leftarg = epoint, 4.226 - rightarg = epoint, 4.227 - procedure = epoint_btree_le, 4.228 - commutator = >>>=, 4.229 - negator = >>>, 4.230 - restrict = scalarltsel, 4.231 - join = scalarltjoinsel 4.232 -); 4.233 - 4.234 -CREATE OPERATOR = ( 4.235 - leftarg = epoint, 4.236 - rightarg = epoint, 4.237 - procedure = epoint_btree_eq, 4.238 - commutator = =, 4.239 - negator = <>, 4.240 - restrict = eqsel, 4.241 - join = eqjoinsel, 4.242 - merges 4.243 -); 4.244 - 4.245 -CREATE OPERATOR <> ( 4.246 - leftarg = epoint, 4.247 - rightarg = epoint, 4.248 - procedure = epoint_btree_eq, 4.249 - commutator = <>, 4.250 - negator = =, 4.251 - restrict = neqsel, 4.252 - join = neqjoinsel 4.253 -); 4.254 - 4.255 -CREATE OPERATOR >>>= ( 4.256 - leftarg = epoint, 4.257 - rightarg = epoint, 4.258 - procedure = epoint_btree_ge, 4.259 - commutator = <<<=, 4.260 - negator = <<<, 4.261 - restrict = scalargtsel, 4.262 - join = scalargtjoinsel 4.263 -); 4.264 - 4.265 -CREATE OPERATOR >>> ( 4.266 - leftarg = epoint, 4.267 - rightarg = epoint, 4.268 - procedure = epoint_btree_gt, 4.269 - commutator = <<<, 4.270 - negator = <<<=, 4.271 - restrict = scalargtsel, 4.272 - join = scalargtjoinsel 4.273 -); 4.274 - 4.275 -CREATE FUNCTION epoint_btree_cmp(epoint, epoint) 4.276 - RETURNS int4 4.277 - LANGUAGE C IMMUTABLE STRICT 4.278 - AS '$libdir/latlon-v0001', 'pgl_btree_epoint_cmp'; 4.279 - 4.280 -CREATE OPERATOR CLASS epoint_btree_ops 4.281 - DEFAULT FOR TYPE epoint USING btree AS 4.282 - OPERATOR 1 <<< , 4.283 - OPERATOR 2 <<<= , 4.284 - OPERATOR 3 = , 4.285 - OPERATOR 4 >>>= , 4.286 - OPERATOR 5 >>> , 4.287 - FUNCTION 1 epoint_btree_cmp(epoint, epoint); 4.288 - 4.289 --- end of B-tree support for epoint 4.290 - 4.291 --- begin of B-tree support for ebox 4.292 - 4.293 -CREATE FUNCTION ebox_btree_lt(ebox, ebox) 4.294 - RETURNS boolean 4.295 - LANGUAGE C IMMUTABLE STRICT 4.296 - AS '$libdir/latlon-v0001', 'pgl_btree_ebox_lt'; 4.297 - 4.298 -CREATE FUNCTION ebox_btree_le(ebox, ebox) 4.299 - RETURNS boolean 4.300 - LANGUAGE C IMMUTABLE STRICT 4.301 - AS '$libdir/latlon-v0001', 'pgl_btree_ebox_le'; 4.302 - 4.303 -CREATE FUNCTION ebox_btree_eq(ebox, ebox) 4.304 - RETURNS boolean 4.305 - LANGUAGE C IMMUTABLE STRICT 4.306 - AS '$libdir/latlon-v0001', 'pgl_btree_ebox_eq'; 4.307 - 4.308 -CREATE FUNCTION ebox_btree_ne(ebox, ebox) 4.309 - RETURNS boolean 4.310 - LANGUAGE C IMMUTABLE STRICT 4.311 - AS '$libdir/latlon-v0001', 'pgl_btree_ebox_ne'; 4.312 - 4.313 -CREATE FUNCTION ebox_btree_ge(ebox, ebox) 4.314 - RETURNS boolean 4.315 - LANGUAGE C IMMUTABLE STRICT 4.316 - AS '$libdir/latlon-v0001', 'pgl_btree_ebox_ge'; 4.317 - 4.318 -CREATE FUNCTION ebox_btree_gt(ebox, ebox) 4.319 - RETURNS boolean 4.320 - LANGUAGE C IMMUTABLE STRICT 4.321 - AS '$libdir/latlon-v0001', 'pgl_btree_ebox_gt'; 4.322 - 4.323 -CREATE OPERATOR <<< ( 4.324 - leftarg = ebox, 4.325 - rightarg = ebox, 4.326 - procedure = ebox_btree_lt, 4.327 - commutator = >>>, 4.328 - negator = >>>=, 4.329 - restrict = scalarltsel, 4.330 - join = scalarltjoinsel 4.331 -); 4.332 - 4.333 -CREATE OPERATOR <<<= ( 4.334 - leftarg = ebox, 4.335 - rightarg = ebox, 4.336 - procedure = ebox_btree_le, 4.337 - commutator = >>>=, 4.338 - negator = >>>, 4.339 - restrict = scalarltsel, 4.340 - join = scalarltjoinsel 4.341 -); 4.342 - 4.343 -CREATE OPERATOR = ( 4.344 - leftarg = ebox, 4.345 - rightarg = ebox, 4.346 - procedure = ebox_btree_eq, 4.347 - commutator = =, 4.348 - negator = <>, 4.349 - restrict = eqsel, 4.350 - join = eqjoinsel, 4.351 - merges 4.352 -); 4.353 - 4.354 -CREATE OPERATOR <> ( 4.355 - leftarg = ebox, 4.356 - rightarg = ebox, 4.357 - procedure = ebox_btree_eq, 4.358 - commutator = <>, 4.359 - negator = =, 4.360 - restrict = neqsel, 4.361 - join = neqjoinsel 4.362 -); 4.363 - 4.364 -CREATE OPERATOR >>>= ( 4.365 - leftarg = ebox, 4.366 - rightarg = ebox, 4.367 - procedure = ebox_btree_ge, 4.368 - commutator = <<<=, 4.369 - negator = <<<, 4.370 - restrict = scalargtsel, 4.371 - join = scalargtjoinsel 4.372 -); 4.373 - 4.374 -CREATE OPERATOR >>> ( 4.375 - leftarg = ebox, 4.376 - rightarg = ebox, 4.377 - procedure = ebox_btree_gt, 4.378 - commutator = <<<, 4.379 - negator = <<<=, 4.380 - restrict = scalargtsel, 4.381 - join = scalargtjoinsel 4.382 -); 4.383 - 4.384 -CREATE FUNCTION ebox_btree_cmp(ebox, ebox) 4.385 - RETURNS int4 4.386 - LANGUAGE C IMMUTABLE STRICT 4.387 - AS '$libdir/latlon-v0001', 'pgl_btree_ebox_cmp'; 4.388 - 4.389 -CREATE OPERATOR CLASS ebox_btree_ops 4.390 - DEFAULT FOR TYPE ebox USING btree AS 4.391 - OPERATOR 1 <<< , 4.392 - OPERATOR 2 <<<= , 4.393 - OPERATOR 3 = , 4.394 - OPERATOR 4 >>>= , 4.395 - OPERATOR 5 >>> , 4.396 - FUNCTION 1 ebox_btree_cmp(ebox, ebox); 4.397 - 4.398 --- end of B-tree support for ebox 4.399 - 4.400 --- begin of B-tree support for ecircle 4.401 - 4.402 -CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle) 4.403 - RETURNS boolean 4.404 - LANGUAGE C IMMUTABLE STRICT 4.405 - AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_lt'; 4.406 - 4.407 -CREATE FUNCTION ecircle_btree_le(ecircle, ecircle) 4.408 - RETURNS boolean 4.409 - LANGUAGE C IMMUTABLE STRICT 4.410 - AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_le'; 4.411 - 4.412 -CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle) 4.413 - RETURNS boolean 4.414 - LANGUAGE C IMMUTABLE STRICT 4.415 - AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_eq'; 4.416 - 4.417 -CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle) 4.418 - RETURNS boolean 4.419 - LANGUAGE C IMMUTABLE STRICT 4.420 - AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_ne'; 4.421 - 4.422 -CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle) 4.423 - RETURNS boolean 4.424 - LANGUAGE C IMMUTABLE STRICT 4.425 - AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_ge'; 4.426 - 4.427 -CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle) 4.428 - RETURNS boolean 4.429 - LANGUAGE C IMMUTABLE STRICT 4.430 - AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_gt'; 4.431 - 4.432 -CREATE OPERATOR <<< ( 4.433 - leftarg = ecircle, 4.434 - rightarg = ecircle, 4.435 - procedure = ecircle_btree_lt, 4.436 - commutator = >>>, 4.437 - negator = >>>=, 4.438 - restrict = scalarltsel, 4.439 - join = scalarltjoinsel 4.440 -); 4.441 - 4.442 -CREATE OPERATOR <<<= ( 4.443 - leftarg = ecircle, 4.444 - rightarg = ecircle, 4.445 - procedure = ecircle_btree_le, 4.446 - commutator = >>>=, 4.447 - negator = >>>, 4.448 - restrict = scalarltsel, 4.449 - join = scalarltjoinsel 4.450 -); 4.451 - 4.452 -CREATE OPERATOR = ( 4.453 - leftarg = ecircle, 4.454 - rightarg = ecircle, 4.455 - procedure = ecircle_btree_eq, 4.456 - commutator = =, 4.457 - negator = <>, 4.458 - restrict = eqsel, 4.459 - join = eqjoinsel, 4.460 - merges 4.461 -); 4.462 - 4.463 -CREATE OPERATOR <> ( 4.464 - leftarg = ecircle, 4.465 - rightarg = ecircle, 4.466 - procedure = ecircle_btree_eq, 4.467 - commutator = <>, 4.468 - negator = =, 4.469 - restrict = neqsel, 4.470 - join = neqjoinsel 4.471 -); 4.472 - 4.473 -CREATE OPERATOR >>>= ( 4.474 - leftarg = ecircle, 4.475 - rightarg = ecircle, 4.476 - procedure = ecircle_btree_ge, 4.477 - commutator = <<<=, 4.478 - negator = <<<, 4.479 - restrict = scalargtsel, 4.480 - join = scalargtjoinsel 4.481 -); 4.482 - 4.483 -CREATE OPERATOR >>> ( 4.484 - leftarg = ecircle, 4.485 - rightarg = ecircle, 4.486 - procedure = ecircle_btree_gt, 4.487 - commutator = <<<, 4.488 - negator = <<<=, 4.489 - restrict = scalargtsel, 4.490 - join = scalargtjoinsel 4.491 -); 4.492 - 4.493 -CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle) 4.494 - RETURNS int4 4.495 - LANGUAGE C IMMUTABLE STRICT 4.496 - AS '$libdir/latlon-v0001', 'pgl_btree_ecircle_cmp'; 4.497 - 4.498 -CREATE OPERATOR CLASS ecircle_btree_ops 4.499 - DEFAULT FOR TYPE ecircle USING btree AS 4.500 - OPERATOR 1 <<< , 4.501 - OPERATOR 2 <<<= , 4.502 - OPERATOR 3 = , 4.503 - OPERATOR 4 >>>= , 4.504 - OPERATOR 5 >>> , 4.505 - FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle); 4.506 - 4.507 --- end of B-tree support for ecircle 4.508 - 4.509 - 4.510 ----------------- 4.511 --- type casts -- 4.512 ----------------- 4.513 - 4.514 -CREATE FUNCTION cast_epoint_to_ebox(epoint) 4.515 - RETURNS ebox 4.516 - LANGUAGE C IMMUTABLE STRICT 4.517 - AS '$libdir/latlon-v0001', 'pgl_epoint_to_ebox'; 4.518 - 4.519 -CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint); 4.520 - 4.521 -CREATE FUNCTION cast_epoint_to_ecircle(epoint) 4.522 - RETURNS ecircle 4.523 - LANGUAGE C IMMUTABLE STRICT 4.524 - AS '$libdir/latlon-v0001', 'pgl_epoint_to_ecircle'; 4.525 - 4.526 -CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint); 4.527 - 4.528 -CREATE FUNCTION cast_epoint_to_ecluster(epoint) 4.529 - RETURNS ecluster 4.530 - LANGUAGE C IMMUTABLE STRICT 4.531 - AS '$libdir/latlon-v0001', 'pgl_epoint_to_ecluster'; 4.532 - 4.533 -CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint); 4.534 - 4.535 -CREATE FUNCTION cast_ebox_to_ecluster(ebox) 4.536 - RETURNS ecluster 4.537 - LANGUAGE C IMMUTABLE STRICT 4.538 - AS '$libdir/latlon-v0001', 'pgl_ebox_to_ecluster'; 4.539 - 4.540 -CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox); 4.541 - 4.542 - 4.543 ---------------------------- 4.544 --- constructor functions -- 4.545 ---------------------------- 4.546 - 4.547 -CREATE FUNCTION epoint(float8, float8) 4.548 - RETURNS epoint 4.549 - LANGUAGE C IMMUTABLE STRICT 4.550 - AS '$libdir/latlon-v0001', 'pgl_create_epoint'; 4.551 - 4.552 -CREATE FUNCTION epoint_latlon(float8, float8) 4.553 - RETURNS epoint 4.554 - LANGUAGE SQL IMMUTABLE STRICT AS $$ 4.555 - SELECT epoint($1, $2) 4.556 - $$; 4.557 - 4.558 -CREATE FUNCTION epoint_lonlat(float8, float8) 4.559 - RETURNS epoint 4.560 - LANGUAGE SQL IMMUTABLE STRICT AS $$ 4.561 - SELECT epoint($2, $1) 4.562 - $$; 4.563 - 4.564 -CREATE FUNCTION empty_ebox() 4.565 - RETURNS ebox 4.566 - LANGUAGE C IMMUTABLE STRICT 4.567 - AS '$libdir/latlon-v0001', 'pgl_create_empty_ebox'; 4.568 - 4.569 -CREATE FUNCTION ebox(float8, float8, float8, float8) 4.570 - RETURNS ebox 4.571 - LANGUAGE C IMMUTABLE STRICT 4.572 - AS '$libdir/latlon-v0001', 'pgl_create_ebox'; 4.573 - 4.574 -CREATE FUNCTION ebox(epoint, epoint) 4.575 - RETURNS ebox 4.576 - LANGUAGE C IMMUTABLE STRICT 4.577 - AS '$libdir/latlon-v0001', 'pgl_create_ebox_from_epoints'; 4.578 - 4.579 -CREATE FUNCTION ecircle(float8, float8, float8) 4.580 - RETURNS ecircle 4.581 - LANGUAGE C IMMUTABLE STRICT 4.582 - AS '$libdir/latlon-v0001', 'pgl_create_ecircle'; 4.583 - 4.584 -CREATE FUNCTION ecircle(epoint, float8) 4.585 - RETURNS ecircle 4.586 - LANGUAGE C IMMUTABLE STRICT 4.587 - AS '$libdir/latlon-v0001', 'pgl_create_ecircle_from_epoint'; 4.588 - 4.589 -CREATE FUNCTION ecluster_concat(ecluster[]) 4.590 - RETURNS ecluster 4.591 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.592 - SELECT array_to_string($1, ' ')::ecluster 4.593 - $$; 4.594 - 4.595 -CREATE FUNCTION ecluster_concat(ecluster, ecluster) 4.596 - RETURNS ecluster 4.597 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.598 - SELECT ($1::text || ' ' || $2::text)::ecluster 4.599 - $$; 4.600 - 4.601 -CREATE FUNCTION ecluster_create_multipoint(epoint[]) 4.602 - RETURNS ecluster 4.603 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.604 - SELECT 4.605 - array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster 4.606 - FROM unnest($1) 4.607 - $$; 4.608 - 4.609 -CREATE FUNCTION ecluster_create_path(epoint[]) 4.610 - RETURNS ecluster 4.611 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.612 - SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 4.613 - ('path (' || array_to_string($1, ' ') || ')')::ecluster 4.614 - END 4.615 - FROM array_to_string($1, ' ') AS "str" 4.616 - $$; 4.617 - 4.618 -CREATE FUNCTION ecluster_create_outline(epoint[]) 4.619 - RETURNS ecluster 4.620 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.621 - SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 4.622 - ('outline (' || array_to_string($1, ' ') || ')')::ecluster 4.623 - END 4.624 - FROM array_to_string($1, ' ') AS "str" 4.625 - $$; 4.626 - 4.627 -CREATE FUNCTION ecluster_create_polygon(epoint[]) 4.628 - RETURNS ecluster 4.629 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.630 - SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 4.631 - ('polygon (' || array_to_string($1, ' ') || ')')::ecluster 4.632 - END 4.633 - FROM array_to_string($1, ' ') AS "str" 4.634 - $$; 4.635 - 4.636 - 4.637 ----------------------- 4.638 --- getter functions -- 4.639 ----------------------- 4.640 - 4.641 -CREATE FUNCTION latitude(epoint) 4.642 - RETURNS float8 4.643 - LANGUAGE C IMMUTABLE STRICT 4.644 - AS '$libdir/latlon-v0001', 'pgl_epoint_lat'; 4.645 - 4.646 -CREATE FUNCTION longitude(epoint) 4.647 - RETURNS float8 4.648 - LANGUAGE C IMMUTABLE STRICT 4.649 - AS '$libdir/latlon-v0001', 'pgl_epoint_lon'; 4.650 - 4.651 -CREATE FUNCTION min_latitude(ebox) 4.652 - RETURNS float8 4.653 - LANGUAGE C IMMUTABLE STRICT 4.654 - AS '$libdir/latlon-v0001', 'pgl_ebox_lat_min'; 4.655 - 4.656 -CREATE FUNCTION max_latitude(ebox) 4.657 - RETURNS float8 4.658 - LANGUAGE C IMMUTABLE STRICT 4.659 - AS '$libdir/latlon-v0001', 'pgl_ebox_lat_max'; 4.660 - 4.661 -CREATE FUNCTION min_longitude(ebox) 4.662 - RETURNS float8 4.663 - LANGUAGE C IMMUTABLE STRICT 4.664 - AS '$libdir/latlon-v0001', 'pgl_ebox_lon_min'; 4.665 - 4.666 -CREATE FUNCTION max_longitude(ebox) 4.667 - RETURNS float8 4.668 - LANGUAGE C IMMUTABLE STRICT 4.669 - AS '$libdir/latlon-v0001', 'pgl_ebox_lon_max'; 4.670 - 4.671 -CREATE FUNCTION center(ecircle) 4.672 - RETURNS epoint 4.673 - LANGUAGE C IMMUTABLE STRICT 4.674 - AS '$libdir/latlon-v0001', 'pgl_ecircle_center'; 4.675 - 4.676 -CREATE FUNCTION radius(ecircle) 4.677 - RETURNS float8 4.678 - LANGUAGE C IMMUTABLE STRICT 4.679 - AS '$libdir/latlon-v0001', 'pgl_ecircle_radius'; 4.680 - 4.681 -CREATE FUNCTION ecluster_extract_points(ecluster) 4.682 - RETURNS SETOF epoint 4.683 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.684 - SELECT "match"[2]::epoint 4.685 - FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match" 4.686 - $$; 4.687 - 4.688 -CREATE FUNCTION ecluster_extract_paths(ecluster) 4.689 - RETURNS SETOF epoint[] 4.690 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.691 - SELECT ( 4.692 - SELECT array_agg("m2"[1]::epoint) 4.693 - FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 4.694 - ) 4.695 - FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1" 4.696 - $$; 4.697 - 4.698 -CREATE FUNCTION ecluster_extract_outlines(ecluster) 4.699 - RETURNS SETOF epoint[] 4.700 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.701 - SELECT ( 4.702 - SELECT array_agg("m2"[1]::epoint) 4.703 - FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 4.704 - ) 4.705 - FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1" 4.706 - $$; 4.707 - 4.708 -CREATE FUNCTION ecluster_extract_polygons(ecluster) 4.709 - RETURNS SETOF epoint[] 4.710 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.711 - SELECT ( 4.712 - SELECT array_agg("m2"[1]::epoint) 4.713 - FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 4.714 - ) 4.715 - FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1" 4.716 - $$; 4.717 - 4.718 - 4.719 ---------------- 4.720 --- operators -- 4.721 ---------------- 4.722 - 4.723 -CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox) 4.724 - RETURNS boolean 4.725 - LANGUAGE C IMMUTABLE STRICT 4.726 - AS '$libdir/latlon-v0001', 'pgl_epoint_ebox_overlap'; 4.727 - 4.728 -CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle) 4.729 - RETURNS boolean 4.730 - LANGUAGE C IMMUTABLE STRICT 4.731 - AS '$libdir/latlon-v0001', 'pgl_epoint_ecircle_overlap'; 4.732 - 4.733 -CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster) 4.734 - RETURNS boolean 4.735 - LANGUAGE C IMMUTABLE STRICT 4.736 - AS '$libdir/latlon-v0001', 'pgl_epoint_ecluster_overlap'; 4.737 - 4.738 -CREATE FUNCTION ebox_overlap_proc(ebox, ebox) 4.739 - RETURNS boolean 4.740 - LANGUAGE C IMMUTABLE STRICT 4.741 - AS '$libdir/latlon-v0001', 'pgl_ebox_overlap'; 4.742 - 4.743 -CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle) 4.744 - RETURNS boolean 4.745 - LANGUAGE C IMMUTABLE STRICT 4.746 - AS '$libdir/latlon-v0001', 'pgl_ecircle_overlap'; 4.747 - 4.748 -CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster) 4.749 - RETURNS boolean 4.750 - LANGUAGE C IMMUTABLE STRICT 4.751 - AS '$libdir/latlon-v0001', 'pgl_ecircle_ecluster_overlap'; 4.752 - 4.753 -CREATE FUNCTION epoint_distance_proc(epoint, epoint) 4.754 - RETURNS float8 4.755 - LANGUAGE C IMMUTABLE STRICT 4.756 - AS '$libdir/latlon-v0001', 'pgl_epoint_distance'; 4.757 - 4.758 -CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle) 4.759 - RETURNS float8 4.760 - LANGUAGE C IMMUTABLE STRICT 4.761 - AS '$libdir/latlon-v0001', 'pgl_epoint_ecircle_distance'; 4.762 - 4.763 -CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster) 4.764 - RETURNS float8 4.765 - LANGUAGE C IMMUTABLE STRICT 4.766 - AS '$libdir/latlon-v0001', 'pgl_epoint_ecluster_distance'; 4.767 - 4.768 -CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle) 4.769 - RETURNS float8 4.770 - LANGUAGE C IMMUTABLE STRICT 4.771 - AS '$libdir/latlon-v0001', 'pgl_ecircle_distance'; 4.772 - 4.773 -CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster) 4.774 - RETURNS float8 4.775 - LANGUAGE C IMMUTABLE STRICT 4.776 - AS '$libdir/latlon-v0001', 'pgl_ecircle_ecluster_distance'; 4.777 - 4.778 -CREATE OPERATOR && ( 4.779 - leftarg = epoint, 4.780 - rightarg = ebox, 4.781 - procedure = epoint_ebox_overlap_proc, 4.782 - commutator = &&, 4.783 - restrict = areasel, 4.784 - join = areajoinsel 4.785 -); 4.786 - 4.787 -CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint) 4.788 - RETURNS boolean 4.789 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 4.790 - 4.791 -CREATE OPERATOR && ( 4.792 - leftarg = ebox, 4.793 - rightarg = epoint, 4.794 - procedure = epoint_ebox_overlap_commutator, 4.795 - commutator = &&, 4.796 - restrict = areasel, 4.797 - join = areajoinsel 4.798 -); 4.799 - 4.800 -CREATE OPERATOR && ( 4.801 - leftarg = epoint, 4.802 - rightarg = ecircle, 4.803 - procedure = epoint_ecircle_overlap_proc, 4.804 - commutator = &&, 4.805 - restrict = areasel, 4.806 - join = areajoinsel 4.807 -); 4.808 - 4.809 -CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint) 4.810 - RETURNS boolean 4.811 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 4.812 - 4.813 -CREATE OPERATOR && ( 4.814 - leftarg = ecircle, 4.815 - rightarg = epoint, 4.816 - procedure = epoint_ecircle_overlap_commutator, 4.817 - commutator = &&, 4.818 - restrict = areasel, 4.819 - join = areajoinsel 4.820 -); 4.821 - 4.822 -CREATE OPERATOR && ( 4.823 - leftarg = epoint, 4.824 - rightarg = ecluster, 4.825 - procedure = epoint_ecluster_overlap_proc, 4.826 - commutator = &&, 4.827 - restrict = areasel, 4.828 - join = areajoinsel 4.829 -); 4.830 - 4.831 -CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint) 4.832 - RETURNS boolean 4.833 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 4.834 - 4.835 -CREATE OPERATOR && ( 4.836 - leftarg = ecluster, 4.837 - rightarg = epoint, 4.838 - procedure = epoint_ecluster_overlap_commutator, 4.839 - commutator = &&, 4.840 - restrict = areasel, 4.841 - join = areajoinsel 4.842 -); 4.843 - 4.844 -CREATE OPERATOR && ( 4.845 - leftarg = ebox, 4.846 - rightarg = ebox, 4.847 - procedure = ebox_overlap_proc, 4.848 - commutator = &&, 4.849 - restrict = areasel, 4.850 - join = areajoinsel 4.851 -); 4.852 - 4.853 -CREATE OPERATOR && ( 4.854 - leftarg = ecircle, 4.855 - rightarg = ecircle, 4.856 - procedure = ecircle_overlap_proc, 4.857 - commutator = &&, 4.858 - restrict = areasel, 4.859 - join = areajoinsel 4.860 -); 4.861 - 4.862 -CREATE OPERATOR && ( 4.863 - leftarg = ecircle, 4.864 - rightarg = ecluster, 4.865 - procedure = ecircle_ecluster_overlap_proc, 4.866 - commutator = &&, 4.867 - restrict = areasel, 4.868 - join = areajoinsel 4.869 -); 4.870 - 4.871 -CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle) 4.872 - RETURNS boolean 4.873 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 4.874 - 4.875 -CREATE OPERATOR && ( 4.876 - leftarg = ecluster, 4.877 - rightarg = ecircle, 4.878 - procedure = ecircle_ecluster_overlap_commutator, 4.879 - commutator = &&, 4.880 - restrict = areasel, 4.881 - join = areajoinsel 4.882 -); 4.883 - 4.884 -CREATE OPERATOR <-> ( 4.885 - leftarg = epoint, 4.886 - rightarg = epoint, 4.887 - procedure = epoint_distance_proc, 4.888 - commutator = <-> 4.889 -); 4.890 - 4.891 -CREATE OPERATOR <-> ( 4.892 - leftarg = epoint, 4.893 - rightarg = ecircle, 4.894 - procedure = epoint_ecircle_distance_proc, 4.895 - commutator = <-> 4.896 -); 4.897 - 4.898 -CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint) 4.899 - RETURNS float8 4.900 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 4.901 - 4.902 -CREATE OPERATOR <-> ( 4.903 - leftarg = ecircle, 4.904 - rightarg = epoint, 4.905 - procedure = epoint_ecircle_distance_commutator, 4.906 - commutator = <-> 4.907 -); 4.908 - 4.909 -CREATE OPERATOR <-> ( 4.910 - leftarg = epoint, 4.911 - rightarg = ecluster, 4.912 - procedure = epoint_ecluster_distance_proc, 4.913 - commutator = <-> 4.914 -); 4.915 - 4.916 -CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint) 4.917 - RETURNS float8 4.918 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 4.919 - 4.920 -CREATE OPERATOR <-> ( 4.921 - leftarg = ecluster, 4.922 - rightarg = epoint, 4.923 - procedure = epoint_ecluster_distance_commutator, 4.924 - commutator = <-> 4.925 -); 4.926 - 4.927 -CREATE OPERATOR <-> ( 4.928 - leftarg = ecircle, 4.929 - rightarg = ecircle, 4.930 - procedure = ecircle_distance_proc, 4.931 - commutator = <-> 4.932 -); 4.933 - 4.934 -CREATE OPERATOR <-> ( 4.935 - leftarg = ecircle, 4.936 - rightarg = ecluster, 4.937 - procedure = ecircle_ecluster_distance_proc, 4.938 - commutator = <-> 4.939 -); 4.940 - 4.941 -CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle) 4.942 - RETURNS float8 4.943 - LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 4.944 - 4.945 -CREATE OPERATOR <-> ( 4.946 - leftarg = ecluster, 4.947 - rightarg = ecircle, 4.948 - procedure = ecircle_ecluster_distance_commutator, 4.949 - commutator = <-> 4.950 -); 4.951 - 4.952 - 4.953 ----------------- 4.954 --- GiST index -- 4.955 ----------------- 4.956 - 4.957 -CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal) 4.958 - RETURNS boolean 4.959 - LANGUAGE C STRICT 4.960 - AS '$libdir/latlon-v0001', 'pgl_gist_consistent'; 4.961 - 4.962 -CREATE FUNCTION pgl_gist_union(internal, internal) 4.963 - RETURNS internal 4.964 - LANGUAGE C STRICT 4.965 - AS '$libdir/latlon-v0001', 'pgl_gist_union'; 4.966 - 4.967 -CREATE FUNCTION pgl_gist_compress_epoint(internal) 4.968 - RETURNS internal 4.969 - LANGUAGE C STRICT 4.970 - AS '$libdir/latlon-v0001', 'pgl_gist_compress_epoint'; 4.971 - 4.972 -CREATE FUNCTION pgl_gist_compress_ecircle(internal) 4.973 - RETURNS internal 4.974 - LANGUAGE C STRICT 4.975 - AS '$libdir/latlon-v0001', 'pgl_gist_compress_ecircle'; 4.976 - 4.977 -CREATE FUNCTION pgl_gist_compress_ecluster(internal) 4.978 - RETURNS internal 4.979 - LANGUAGE C STRICT 4.980 - AS '$libdir/latlon-v0001', 'pgl_gist_compress_ecluster'; 4.981 - 4.982 -CREATE FUNCTION pgl_gist_decompress(internal) 4.983 - RETURNS internal 4.984 - LANGUAGE C STRICT 4.985 - AS '$libdir/latlon-v0001', 'pgl_gist_decompress'; 4.986 - 4.987 -CREATE FUNCTION pgl_gist_penalty(internal, internal, internal) 4.988 - RETURNS internal 4.989 - LANGUAGE C STRICT 4.990 - AS '$libdir/latlon-v0001', 'pgl_gist_penalty'; 4.991 - 4.992 -CREATE FUNCTION pgl_gist_picksplit(internal, internal) 4.993 - RETURNS internal 4.994 - LANGUAGE C STRICT 4.995 - AS '$libdir/latlon-v0001', 'pgl_gist_picksplit'; 4.996 - 4.997 -CREATE FUNCTION pgl_gist_same(internal, internal, internal) 4.998 - RETURNS internal 4.999 - LANGUAGE C STRICT 4.1000 - AS '$libdir/latlon-v0001', 'pgl_gist_same'; 4.1001 - 4.1002 -CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid) 4.1003 - RETURNS internal 4.1004 - LANGUAGE C STRICT 4.1005 - AS '$libdir/latlon-v0001', 'pgl_gist_distance'; 4.1006 - 4.1007 -CREATE OPERATOR CLASS epoint_ops 4.1008 - DEFAULT FOR TYPE epoint USING gist AS 4.1009 - OPERATOR 11 = , 4.1010 - OPERATOR 22 && (epoint, ebox), 4.1011 - OPERATOR 23 && (epoint, ecircle), 4.1012 - OPERATOR 24 && (epoint, ecluster), 4.1013 - OPERATOR 31 <-> (epoint, epoint) FOR ORDER BY float_ops, 4.1014 - OPERATOR 33 <-> (epoint, ecircle) FOR ORDER BY float_ops, 4.1015 - OPERATOR 34 <-> (epoint, ecluster) FOR ORDER BY float_ops, 4.1016 - FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 4.1017 - FUNCTION 2 pgl_gist_union(internal, internal), 4.1018 - FUNCTION 3 pgl_gist_compress_epoint(internal), 4.1019 - FUNCTION 4 pgl_gist_decompress(internal), 4.1020 - FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 4.1021 - FUNCTION 6 pgl_gist_picksplit(internal, internal), 4.1022 - FUNCTION 7 pgl_gist_same(internal, internal, internal), 4.1023 - FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 4.1024 - STORAGE ekey_point; 4.1025 - 4.1026 -CREATE OPERATOR CLASS ecircle_ops 4.1027 - DEFAULT FOR TYPE ecircle USING gist AS 4.1028 - OPERATOR 13 = , 4.1029 - OPERATOR 21 && (ecircle, epoint), 4.1030 - OPERATOR 23 && (ecircle, ecircle), 4.1031 - OPERATOR 24 && (ecircle, ecluster), 4.1032 - OPERATOR 31 <-> (ecircle, epoint) FOR ORDER BY float_ops, 4.1033 - OPERATOR 33 <-> (ecircle, ecircle) FOR ORDER BY float_ops, 4.1034 - OPERATOR 34 <-> (ecircle, ecluster) FOR ORDER BY float_ops, 4.1035 - FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 4.1036 - FUNCTION 2 pgl_gist_union(internal, internal), 4.1037 - FUNCTION 3 pgl_gist_compress_ecircle(internal), 4.1038 - FUNCTION 4 pgl_gist_decompress(internal), 4.1039 - FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 4.1040 - FUNCTION 6 pgl_gist_picksplit(internal, internal), 4.1041 - FUNCTION 7 pgl_gist_same(internal, internal, internal), 4.1042 - FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 4.1043 - STORAGE ekey_area; 4.1044 - 4.1045 -CREATE OPERATOR CLASS ecluster_ops 4.1046 - DEFAULT FOR TYPE ecluster USING gist AS 4.1047 - OPERATOR 21 && (ecluster, epoint), 4.1048 - FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 4.1049 - FUNCTION 2 pgl_gist_union(internal, internal), 4.1050 - FUNCTION 3 pgl_gist_compress_ecluster(internal), 4.1051 - FUNCTION 4 pgl_gist_decompress(internal), 4.1052 - FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 4.1053 - FUNCTION 6 pgl_gist_picksplit(internal, internal), 4.1054 - FUNCTION 7 pgl_gist_same(internal, internal, internal), 4.1055 - FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 4.1056 - STORAGE ekey_area; 4.1057 - 4.1058 - 4.1059 ---------------------- 4.1060 --- alias functions -- 4.1061 ---------------------- 4.1062 - 4.1063 -CREATE FUNCTION distance(epoint, epoint) 4.1064 - RETURNS float8 4.1065 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2'; 4.1066 - 4.1067 -CREATE FUNCTION distance(ecluster, epoint) 4.1068 - RETURNS float8 4.1069 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2'; 4.1070 - 4.1071 -CREATE FUNCTION distance_within(epoint, epoint, float8) 4.1072 - RETURNS boolean 4.1073 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)'; 4.1074 - 4.1075 -CREATE FUNCTION distance_within(ecluster, epoint, float8) 4.1076 - RETURNS boolean 4.1077 - LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)'; 4.1078 - 4.1079 - 4.1080 --------------------------------- 4.1081 --- other data storage formats -- 4.1082 --------------------------------- 4.1083 - 4.1084 -CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint_lonlat') 4.1085 - RETURNS epoint 4.1086 - LANGUAGE plpgsql IMMUTABLE STRICT AS $$ 4.1087 - DECLARE 4.1088 - "result" epoint; 4.1089 - BEGIN 4.1090 - IF $3 = 'epoint_lonlat' THEN 4.1091 - -- avoid dynamic command execution for better performance 4.1092 - RETURN epoint($2, $1); 4.1093 - END IF; 4.1094 - IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN 4.1095 - -- avoid dynamic command execution for better performance 4.1096 - RETURN epoint($1, $2); 4.1097 - END IF; 4.1098 - EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2; 4.1099 - RETURN "result"; 4.1100 - END; 4.1101 - $$; 4.1102 - 4.1103 -CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat') 4.1104 - RETURNS epoint 4.1105 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.1106 - SELECT CASE 4.1107 - WHEN $1->>'type' = 'Point' THEN 4.1108 - coords_to_epoint( 4.1109 - ($1->'coordinates'->>1)::float8, 4.1110 - ($1->'coordinates'->>0)::float8, 4.1111 - $2 4.1112 - ) 4.1113 - WHEN $1->>'type' = 'Feature' THEN 4.1114 - GeoJSON_to_epoint($1->'geometry', $2) 4.1115 - ELSE 4.1116 - NULL 4.1117 - END 4.1118 - $$; 4.1119 - 4.1120 -CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat') 4.1121 - RETURNS ecluster 4.1122 - LANGUAGE sql IMMUTABLE STRICT AS $$ 4.1123 - SELECT CASE $1->>'type' 4.1124 - WHEN 'Point' THEN 4.1125 - coords_to_epoint( 4.1126 - ($1->'coordinates'->>1)::float8, 4.1127 - ($1->'coordinates'->>0)::float8, 4.1128 - $2 4.1129 - )::ecluster 4.1130 - WHEN 'MultiPoint' THEN 4.1131 - ( SELECT ecluster_create_multipoint(array_agg( 4.1132 - coords_to_epoint( 4.1133 - ("coord"->>1)::float8, 4.1134 - ("coord"->>0)::float8, 4.1135 - $2 4.1136 - ) 4.1137 - )) 4.1138 - FROM jsonb_array_elements($1->'coordinates') AS "coord" 4.1139 - ) 4.1140 - WHEN 'LineString' THEN 4.1141 - ( SELECT ecluster_create_path(array_agg( 4.1142 - coords_to_epoint( 4.1143 - ("coord"->>1)::float8, 4.1144 - ("coord"->>0)::float8, 4.1145 - $2 4.1146 - ) 4.1147 - )) 4.1148 - FROM jsonb_array_elements($1->'coordinates') AS "coord" 4.1149 - ) 4.1150 - WHEN 'MultiLineString' THEN 4.1151 - ( SELECT ecluster_concat(array_agg( 4.1152 - ( SELECT ecluster_create_path(array_agg( 4.1153 - coords_to_epoint( 4.1154 - ("coord"->>1)::float8, 4.1155 - ("coord"->>0)::float8, 4.1156 - $2 4.1157 - ) 4.1158 - )) 4.1159 - FROM jsonb_array_elements("coord_array") AS "coord" 4.1160 - ) 4.1161 - )) 4.1162 - FROM jsonb_array_elements($1->'coordinates') AS "coord_array" 4.1163 - ) 4.1164 - WHEN 'Polygon' THEN 4.1165 - ( SELECT ecluster_concat(array_agg( 4.1166 - ( SELECT ecluster_create_polygon(array_agg( 4.1167 - coords_to_epoint( 4.1168 - ("coord"->>1)::float8, 4.1169 - ("coord"->>0)::float8, 4.1170 - $2 4.1171 - ) 4.1172 - )) 4.1173 - FROM jsonb_array_elements("coord_array") AS "coord" 4.1174 - ) 4.1175 - )) 4.1176 - FROM jsonb_array_elements($1->'coordinates') AS "coord_array" 4.1177 - ) 4.1178 - WHEN 'MultiPolygon' THEN 4.1179 - ( SELECT ecluster_concat(array_agg( 4.1180 - ( SELECT ecluster_concat(array_agg( 4.1181 - ( SELECT ecluster_create_polygon(array_agg( 4.1182 - coords_to_epoint( 4.1183 - ("coord"->>1)::float8, 4.1184 - ("coord"->>0)::float8, 4.1185 - $2 4.1186 - ) 4.1187 - )) 4.1188 - FROM jsonb_array_elements("coord_array") AS "coord" 4.1189 - ) 4.1190 - )) 4.1191 - FROM jsonb_array_elements("coord_array_array") AS "coord_array" 4.1192 - ) 4.1193 - )) 4.1194 - FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array" 4.1195 - ) 4.1196 - WHEN 'Feature' THEN 4.1197 - GeoJSON_to_ecluster($1->'geometry', $2) 4.1198 - WHEN 'FeatureCollection' THEN 4.1199 - ( SELECT ecluster_concat(array_agg( 4.1200 - GeoJSON_to_ecluster("feature", $2) 4.1201 - )) 4.1202 - FROM jsonb_array_elements($1->'features') AS "feature" 4.1203 - ) 4.1204 - ELSE 4.1205 - NULL 4.1206 - END 4.1207 - $$; 4.1208 -
5.1 --- a/latlon-v0001.c Mon Aug 22 21:52:32 2016 +0200 5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 5.3 @@ -1,2710 +0,0 @@ 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 - /* determine antipodal point of second point (i.e. mirror second point) */ 5.86 - lat2 = -lat2; lon2 = lon2 - M_PI; 5.87 - lat2cos = cos(lat2); lat2sin = sin(lat2); 5.88 - lon2cos = cos(lon2); lon2sin = sin(lon2); 5.89 - /* calculate 3d coordinates of antipodal point on scaled ellipsoid */ 5.90 - nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin); 5.91 - x2 = nphi2 * lat2cos * lon2cos; 5.92 - y2 = nphi2 * lat2cos * lon2sin; 5.93 - z2 = nphi2 * PGL_SUBEPS2 * lat2sin; 5.94 - /* calculate tunnel distance to antipodal point through scaled ellipsoid */ 5.95 - g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1)); 5.96 - /* convert tunnel distance to antipodal point through scaled ellipsoid to 5.97 - approximated surface distance to antipodal point on original ellipsoid */ 5.98 - if (g > 1.0) g = 1.0; 5.99 - t = PGL_DIAMETER * asin(g); 5.100 - /* surface distance between original points can now be approximated by 5.101 - substracting antipodal distance from maximum possible distance; 5.102 - return result only if small enough (less than 1/3 of maximum possible 5.103 - distance) */ 5.104 - if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t; 5.105 - /* otherwise crossfade direct and antipodal result to ensure monotonicity */ 5.106 - return ( 5.107 - (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) / 5.108 - (s + t - 2*PGL_FADELIMIT) 5.109 - ); 5.110 -} 5.111 - 5.112 -/* finite distance that can not be reached on earth */ 5.113 -#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST) 5.114 - 5.115 - 5.116 -/*--------------------------------* 5.117 - * simple geographic data types * 5.118 - *--------------------------------*/ 5.119 - 5.120 -/* point on earth given by latitude and longitude in degrees */ 5.121 -/* (type "epoint" in SQL) */ 5.122 -typedef struct { 5.123 - double lat; /* between -90 and 90 (both inclusive) */ 5.124 - double lon; /* between -180 and 180 (both inclusive) */ 5.125 -} pgl_point; 5.126 - 5.127 -/* box delimited by two parallels and two meridians (all in degrees) */ 5.128 -/* (type "ebox" in SQL) */ 5.129 -typedef struct { 5.130 - double lat_min; /* between -90 and 90 (both inclusive) */ 5.131 - double lat_max; /* between -90 and 90 (both inclusive) */ 5.132 - double lon_min; /* between -180 and 180 (both inclusive) */ 5.133 - double lon_max; /* between -180 and 180 (both inclusive) */ 5.134 - /* if lat_min > lat_max, then box is empty */ 5.135 - /* if lon_min > lon_max, then 180th meridian is crossed */ 5.136 -} pgl_box; 5.137 - 5.138 -/* circle on earth surface (for radial searches with fixed radius) */ 5.139 -/* (type "ecircle" in SQL) */ 5.140 -typedef struct { 5.141 - pgl_point center; 5.142 - double radius; /* positive (including +0 but excluding -0), or -INFINITY */ 5.143 - /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point), 5.144 - zero radius (0) denotes a single point, 5.145 - a finite radius (0 < radius < INFINITY) denotes a filled circle, and 5.146 - a radius of INFINITY is valid and means complete coverage of earth. */ 5.147 -} pgl_circle; 5.148 - 5.149 - 5.150 -/*----------------------------------* 5.151 - * geographic "cluster" data type * 5.152 - *----------------------------------*/ 5.153 - 5.154 -/* A cluster is a collection of points, paths, outlines, and polygons. If two 5.155 - polygons in a cluster overlap, the area covered by both polygons does not 5.156 - belong to the cluster. This way, a cluster can be used to describe complex 5.157 - shapes like polygons with holes. Outlines are non-filled polygons. Paths are 5.158 - open by default (i.e. the last point in the list is not connected with the 5.159 - first point in the list). Note that each outline or polygon in a cluster 5.160 - must cover a longitude range of less than 180 degrees to avoid ambiguities. 5.161 - Areas which are larger may be split into multiple polygons. */ 5.162 - 5.163 -/* maximum number of points in a cluster */ 5.164 -/* (limited to avoid integer overflows, e.g. when allocating memory) */ 5.165 -#define PGL_CLUSTER_MAXPOINTS 16777216 5.166 - 5.167 -/* types of cluster entries */ 5.168 -#define PGL_ENTRY_POINT 1 /* a point */ 5.169 -#define PGL_ENTRY_PATH 2 /* a path from first point to last point */ 5.170 -#define PGL_ENTRY_OUTLINE 3 /* a non-filled polygon with given vertices */ 5.171 -#define PGL_ENTRY_POLYGON 4 /* a filled polygon with given vertices */ 5.172 - 5.173 -/* Entries of a cluster are described by two different structs: pgl_newentry 5.174 - and pgl_entry. The first is used only during construction of a cluster, the 5.175 - second is used in all other cases (e.g. when reading clusters from the 5.176 - database, performing operations, etc). */ 5.177 - 5.178 -/* entry for new geographic cluster during construction of that cluster */ 5.179 -typedef struct { 5.180 - int32_t entrytype; 5.181 - int32_t npoints; 5.182 - pgl_point *points; /* pointer to an array of points (pgl_point) */ 5.183 -} pgl_newentry; 5.184 - 5.185 -/* entry of geographic cluster */ 5.186 -typedef struct { 5.187 - int32_t entrytype; /* type of entry: point, path, outline, polygon */ 5.188 - int32_t npoints; /* number of stored points (set to 1 for point entry) */ 5.189 - int32_t offset; /* offset of pgl_point array from cluster base address */ 5.190 - /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */ 5.191 -} pgl_entry; 5.192 - 5.193 -/* geographic cluster which is a collection of points, (open) paths, polygons, 5.194 - and outlines (non-filled polygons) */ 5.195 -typedef struct { 5.196 - char header[VARHDRSZ]; /* PostgreSQL header for variable size data types */ 5.197 - int32_t nentries; /* number of stored points */ 5.198 - pgl_circle bounding; /* bounding circle */ 5.199 - /* Note: bounding circle ensures alignment of pgl_cluster for points */ 5.200 - pgl_entry entries[FLEXIBLE_ARRAY_MEMBER]; /* var-length data */ 5.201 -} pgl_cluster; 5.202 - 5.203 -/* macro to determine memory alignment of points */ 5.204 -/* (needed to store pgl_point array after entries in pgl_cluster) */ 5.205 -typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment; 5.206 -#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned) 5.207 - 5.208 -/* macro to extract a pointer to the array of points of a cluster entry */ 5.209 -#define PGL_ENTRY_POINTS(cluster, idx) \ 5.210 - ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset)) 5.211 - 5.212 -/* convert pgl_newentry array to pgl_cluster */ 5.213 -static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) { 5.214 - int i; /* index of current entry */ 5.215 - int npoints = 0; /* number of points in whole cluster */ 5.216 - int entry_npoints; /* number of points in current entry */ 5.217 - int points_offset = PGL_POINT_ALIGNMENT * ( 5.218 - ( offsetof(pgl_cluster, entries) + 5.219 - nentries * sizeof(pgl_entry) + 5.220 - PGL_POINT_ALIGNMENT - 1 5.221 - ) / PGL_POINT_ALIGNMENT 5.222 - ); /* offset of pgl_point array from base address (considering alignment) */ 5.223 - pgl_cluster *cluster; /* new cluster to be returned */ 5.224 - /* determine total number of points */ 5.225 - for (i=0; i<nentries; i++) npoints += entries[i].npoints; 5.226 - /* allocate memory for cluster (including entries and points) */ 5.227 - cluster = palloc(points_offset + npoints * sizeof(pgl_point)); 5.228 - /* re-count total number of points to determine offset for each entry */ 5.229 - npoints = 0; 5.230 - /* copy entries and points */ 5.231 - for (i=0; i<nentries; i++) { 5.232 - /* determine number of points in entry */ 5.233 - entry_npoints = entries[i].npoints; 5.234 - /* copy entry */ 5.235 - cluster->entries[i].entrytype = entries[i].entrytype; 5.236 - cluster->entries[i].npoints = entry_npoints; 5.237 - /* calculate offset (in bytes) of pgl_point array */ 5.238 - cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point); 5.239 - /* copy points */ 5.240 - memcpy( 5.241 - PGL_ENTRY_POINTS(cluster, i), 5.242 - entries[i].points, 5.243 - entry_npoints * sizeof(pgl_point) 5.244 - ); 5.245 - /* update total number of points processed */ 5.246 - npoints += entry_npoints; 5.247 - } 5.248 - /* set number of entries in cluster */ 5.249 - cluster->nentries = nentries; 5.250 - /* set PostgreSQL header for variable sized data */ 5.251 - SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point)); 5.252 - /* return newly created cluster */ 5.253 - return cluster; 5.254 -} 5.255 - 5.256 - 5.257 -/*----------------------------------------* 5.258 - * C functions on geographic data types * 5.259 - *----------------------------------------*/ 5.260 - 5.261 -/* round latitude or longitude to 12 digits after decimal point */ 5.262 -static inline double pgl_round(double val) { 5.263 - return round(val * 1e12) / 1e12; 5.264 -} 5.265 - 5.266 -/* compare two points */ 5.267 -/* (equality when same point on earth is described, otherwise an arbitrary 5.268 - linear order) */ 5.269 -static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) { 5.270 - double lon1, lon2; /* modified longitudes for special cases */ 5.271 - /* use latitude as first ordering criterion */ 5.272 - if (point1->lat < point2->lat) return -1; 5.273 - if (point1->lat > point2->lat) return 1; 5.274 - /* determine modified longitudes (considering special case of poles and 5.275 - 180th meridian which can be described as W180 or E180) */ 5.276 - if (point1->lat == -90 || point1->lat == 90) lon1 = 0; 5.277 - else if (point1->lon == 180) lon1 = -180; 5.278 - else lon1 = point1->lon; 5.279 - if (point2->lat == -90 || point2->lat == 90) lon2 = 0; 5.280 - else if (point2->lon == 180) lon2 = -180; 5.281 - else lon2 = point2->lon; 5.282 - /* use (modified) longitude as secondary ordering criterion */ 5.283 - if (lon1 < lon2) return -1; 5.284 - if (lon1 > lon2) return 1; 5.285 - /* no difference found, points are equal */ 5.286 - return 0; 5.287 -} 5.288 - 5.289 -/* compare two boxes */ 5.290 -/* (equality when same box on earth is described, otherwise an arbitrary linear 5.291 - order) */ 5.292 -static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) { 5.293 - /* two empty boxes are equal, and an empty box is always considered "less 5.294 - than" a non-empty box */ 5.295 - if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1; 5.296 - if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0; 5.297 - if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1; 5.298 - /* use southern border as first ordering criterion */ 5.299 - if (box1->lat_min < box2->lat_min) return -1; 5.300 - if (box1->lat_min > box2->lat_min) return 1; 5.301 - /* use northern border as second ordering criterion */ 5.302 - if (box1->lat_max < box2->lat_max) return -1; 5.303 - if (box1->lat_max > box2->lat_max) return 1; 5.304 - /* use western border as third ordering criterion */ 5.305 - if (box1->lon_min < box2->lon_min) return -1; 5.306 - if (box1->lon_min > box2->lon_min) return 1; 5.307 - /* use eastern border as fourth ordering criterion */ 5.308 - if (box1->lon_max < box2->lon_max) return -1; 5.309 - if (box1->lon_max > box2->lon_max) return 1; 5.310 - /* no difference found, boxes are equal */ 5.311 - return 0; 5.312 -} 5.313 - 5.314 -/* compare two circles */ 5.315 -/* (equality when same circle on earth is described, otherwise an arbitrary 5.316 - linear order) */ 5.317 -static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) { 5.318 - /* two circles with same infinite radius (positive or negative infinity) are 5.319 - considered equal independently of center point */ 5.320 - if ( 5.321 - !isfinite(circle1->radius) && !isfinite(circle2->radius) && 5.322 - circle1->radius == circle2->radius 5.323 - ) return 0; 5.324 - /* use radius as first ordering criterion */ 5.325 - if (circle1->radius < circle2->radius) return -1; 5.326 - if (circle1->radius > circle2->radius) return 1; 5.327 - /* use center point as secondary ordering criterion */ 5.328 - return pgl_point_cmp(&(circle1->center), &(circle2->center)); 5.329 -} 5.330 - 5.331 -/* set box to empty box*/ 5.332 -static void pgl_box_set_empty(pgl_box *box) { 5.333 - box->lat_min = INFINITY; 5.334 - box->lat_max = -INFINITY; 5.335 - box->lon_min = 0; 5.336 - box->lon_max = 0; 5.337 -} 5.338 - 5.339 -/* check if point is inside a box */ 5.340 -static bool pgl_point_in_box(pgl_point *point, pgl_box *box) { 5.341 - return ( 5.342 - point->lat >= box->lat_min && point->lat <= box->lat_max && ( 5.343 - (box->lon_min > box->lon_max) ? ( 5.344 - /* box crosses 180th meridian */ 5.345 - point->lon >= box->lon_min || point->lon <= box->lon_max 5.346 - ) : ( 5.347 - /* box does not cross the 180th meridian */ 5.348 - point->lon >= box->lon_min && point->lon <= box->lon_max 5.349 - ) 5.350 - ) 5.351 - ); 5.352 -} 5.353 - 5.354 -/* check if two boxes overlap */ 5.355 -static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) { 5.356 - return ( 5.357 - box2->lat_max >= box2->lat_min && /* ensure box2 is not empty */ 5.358 - ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) && 5.359 - ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && ( 5.360 - ( 5.361 - /* check if one and only one box crosses the 180th meridian */ 5.362 - ((box1->lon_min > box1->lon_max) ? 1 : 0) ^ 5.363 - ((box2->lon_min > box2->lon_max) ? 1 : 0) 5.364 - ) ? ( 5.365 - /* exactly one box crosses the 180th meridian */ 5.366 - box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min || 5.367 - box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max 5.368 - ) : ( 5.369 - /* no box or both boxes cross the 180th meridian */ 5.370 - ( 5.371 - (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) && 5.372 - (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max) 5.373 - ) || 5.374 - /* handle W180 == E180 */ 5.375 - ( box1->lon_min == -180 && box2->lon_max == 180 ) || 5.376 - ( box2->lon_min == -180 && box1->lon_max == 180 ) 5.377 - ) 5.378 - ) 5.379 - ); 5.380 -} 5.381 - 5.382 -/* check unambiguousness of east/west orientation of cluster entries and set 5.383 - bounding circle of cluster */ 5.384 -static bool pgl_finalize_cluster(pgl_cluster *cluster) { 5.385 - int i, j; /* i: index of entry, j: index of point in entry */ 5.386 - int npoints; /* number of points in entry */ 5.387 - int total_npoints = 0; /* total number of points in cluster */ 5.388 - pgl_point *points; /* points in entry */ 5.389 - int lon_dir; /* first point of entry west (-1) or east (+1) */ 5.390 - double lon_break = 0; /* antipodal longitude of first point in entry */ 5.391 - double lon_min, lon_max; /* covered longitude range of entry */ 5.392 - double value; /* temporary variable */ 5.393 - /* reset bounding circle center to empty circle at 0/0 coordinates */ 5.394 - cluster->bounding.center.lat = 0; 5.395 - cluster->bounding.center.lon = 0; 5.396 - cluster->bounding.radius = -INFINITY; 5.397 - /* if cluster is not empty */ 5.398 - if (cluster->nentries != 0) { 5.399 - /* iterate over all cluster entries and ensure they each cover a longitude 5.400 - range less than 180 degrees */ 5.401 - for (i=0; i<cluster->nentries; i++) { 5.402 - /* get properties of entry */ 5.403 - npoints = cluster->entries[i].npoints; 5.404 - points = PGL_ENTRY_POINTS(cluster, i); 5.405 - /* get longitude of first point of entry */ 5.406 - value = points[0].lon; 5.407 - /* initialize lon_min and lon_max with longitude of first point */ 5.408 - lon_min = value; 5.409 - lon_max = value; 5.410 - /* determine east/west orientation of first point and calculate antipodal 5.411 - longitude (Note: rounding required here) */ 5.412 - if (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); } 5.413 - else if (value > 0) { lon_dir = 1; lon_break = pgl_round(value - 180); } 5.414 - else lon_dir = 0; 5.415 - /* iterate over all other points in entry */ 5.416 - for (j=1; j<npoints; j++) { 5.417 - /* consider longitude wrap-around */ 5.418 - value = points[j].lon; 5.419 - if (lon_dir<0 && value>lon_break) value = pgl_round(value - 360); 5.420 - else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360); 5.421 - /* update lon_min and lon_max */ 5.422 - if (value < lon_min) lon_min = value; 5.423 - else if (value > lon_max) lon_max = value; 5.424 - /* return false if 180 degrees or more are covered */ 5.425 - if (lon_max - lon_min >= 180) return false; 5.426 - } 5.427 - } 5.428 - /* iterate over all points of all entries and calculate arbitrary center 5.429 - point for bounding circle (best if center point minimizes the radius, 5.430 - but some error is allowed here) */ 5.431 - for (i=0; i<cluster->nentries; i++) { 5.432 - /* get properties of entry */ 5.433 - npoints = cluster->entries[i].npoints; 5.434 - points = PGL_ENTRY_POINTS(cluster, i); 5.435 - /* check if first entry */ 5.436 - if (i==0) { 5.437 - /* get longitude of first point of first entry in whole cluster */ 5.438 - value = points[0].lon; 5.439 - /* initialize lon_min and lon_max with longitude of first point of 5.440 - first entry in whole cluster (used to determine if whole cluster 5.441 - covers a longitude range of 180 degrees or more) */ 5.442 - lon_min = value; 5.443 - lon_max = value; 5.444 - /* determine east/west orientation of first point and calculate 5.445 - antipodal longitude (Note: rounding not necessary here) */ 5.446 - if (value < 0) { lon_dir = -1; lon_break = value + 180; } 5.447 - else if (value > 0) { lon_dir = 1; lon_break = value - 180; } 5.448 - else lon_dir = 0; 5.449 - } 5.450 - /* iterate over all points in entry */ 5.451 - for (j=0; j<npoints; j++) { 5.452 - /* longitude wrap-around (Note: rounding not necessary here) */ 5.453 - value = points[j].lon; 5.454 - if (lon_dir < 0 && value > lon_break) value -= 360; 5.455 - else if (lon_dir > 0 && value < lon_break) value += 360; 5.456 - if (value < lon_min) lon_min = value; 5.457 - else if (value > lon_max) lon_max = value; 5.458 - /* set bounding circle to cover whole earth if more than 180 degrees 5.459 - are covered */ 5.460 - if (lon_max - lon_min >= 180) { 5.461 - cluster->bounding.center.lat = 0; 5.462 - cluster->bounding.center.lon = 0; 5.463 - cluster->bounding.radius = INFINITY; 5.464 - return true; 5.465 - } 5.466 - /* add point to bounding circle center (for average calculation) */ 5.467 - cluster->bounding.center.lat += points[j].lat; 5.468 - cluster->bounding.center.lon += value; 5.469 - } 5.470 - /* count total number of points */ 5.471 - total_npoints += npoints; 5.472 - } 5.473 - /* determine average latitude and longitude of cluster */ 5.474 - cluster->bounding.center.lat /= total_npoints; 5.475 - cluster->bounding.center.lon /= total_npoints; 5.476 - /* normalize longitude of center of cluster bounding circle */ 5.477 - if (cluster->bounding.center.lon < -180) { 5.478 - cluster->bounding.center.lon += 360; 5.479 - } 5.480 - else if (cluster->bounding.center.lon > 180) { 5.481 - cluster->bounding.center.lon -= 360; 5.482 - } 5.483 - /* round bounding circle center (useful if it is used by other functions) */ 5.484 - cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat); 5.485 - cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon); 5.486 - /* calculate radius of bounding circle */ 5.487 - for (i=0; i<cluster->nentries; i++) { 5.488 - npoints = cluster->entries[i].npoints; 5.489 - points = PGL_ENTRY_POINTS(cluster, i); 5.490 - for (j=0; j<npoints; j++) { 5.491 - value = pgl_distance( 5.492 - cluster->bounding.center.lat, cluster->bounding.center.lon, 5.493 - points[j].lat, points[j].lon 5.494 - ); 5.495 - if (value > cluster->bounding.radius) cluster->bounding.radius = value; 5.496 - } 5.497 - } 5.498 - } 5.499 - /* return true (east/west orientation is unambiguous) */ 5.500 - return true; 5.501 -} 5.502 - 5.503 -/* check if point is inside cluster */ 5.504 -static bool pgl_point_in_cluster(pgl_point *point, pgl_cluster *cluster) { 5.505 - int i, j, k; /* i: entry, j: point in entry, k: next point in entry */ 5.506 - int entrytype; /* type of entry */ 5.507 - int npoints; /* number of points in entry */ 5.508 - pgl_point *points; /* array of points in entry */ 5.509 - int lon_dir = 0; /* first vertex west (-1) or east (+1) */ 5.510 - double lon_break = 0; /* antipodal longitude of first vertex */ 5.511 - double lat0 = point->lat; /* latitude of point */ 5.512 - double lon0; /* (adjusted) longitude of point */ 5.513 - double lat1, lon1; /* latitude and (adjusted) longitude of vertex */ 5.514 - double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */ 5.515 - double lon; /* longitude of intersection */ 5.516 - int counter = 0; /* counter for intersections east of point */ 5.517 - /* points outside bounding circle are always assumed to be non-overlapping */ 5.518 - /* (necessary for consistent table and index scans) */ 5.519 - if ( 5.520 - pgl_distance( 5.521 - point->lat, point->lon, 5.522 - cluster->bounding.center.lat, cluster->bounding.center.lon 5.523 - ) > cluster->bounding.radius 5.524 - ) return false; 5.525 - /* iterate over all entries */ 5.526 - for (i=0; i<cluster->nentries; i++) { 5.527 - /* get properties of entry */ 5.528 - entrytype = cluster->entries[i].entrytype; 5.529 - npoints = cluster->entries[i].npoints; 5.530 - points = PGL_ENTRY_POINTS(cluster, i); 5.531 - /* determine east/west orientation of first point of entry and calculate 5.532 - antipodal longitude */ 5.533 - lon_break = points[0].lon; 5.534 - if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 5.535 - else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 5.536 - else lon_dir = 0; 5.537 - /* get longitude of point */ 5.538 - lon0 = point->lon; 5.539 - /* consider longitude wrap-around for point */ 5.540 - if (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360); 5.541 - else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360); 5.542 - /* iterate over all edges and vertices */ 5.543 - for (j=0; j<npoints; j++) { 5.544 - /* return true if point is on vertex of polygon */ 5.545 - if (pgl_point_cmp(point, &(points[j])) == 0) return true; 5.546 - /* calculate index of next vertex */ 5.547 - k = (j+1) % npoints; 5.548 - /* skip last edge unless entry is (closed) outline or polygon */ 5.549 - if ( 5.550 - k == 0 && 5.551 - entrytype != PGL_ENTRY_OUTLINE && 5.552 - entrytype != PGL_ENTRY_POLYGON 5.553 - ) continue; 5.554 - /* get latitude and longitude values of edge */ 5.555 - lat1 = points[j].lat; 5.556 - lat2 = points[k].lat; 5.557 - lon1 = points[j].lon; 5.558 - lon2 = points[k].lon; 5.559 - /* consider longitude wrap-around for edge */ 5.560 - if (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360); 5.561 - else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360); 5.562 - if (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360); 5.563 - else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360); 5.564 - /* return true if point is on horizontal (west to east) edge of polygon */ 5.565 - if ( 5.566 - lat0 == lat1 && lat0 == lat2 && 5.567 - ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) ) 5.568 - ) return true; 5.569 - /* check if edge crosses east/west line of point */ 5.570 - if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) { 5.571 - /* calculate longitude of intersection */ 5.572 - lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1); 5.573 - /* return true if intersection goes (approximately) through point */ 5.574 - if (pgl_round(lon) == lon0) return true; 5.575 - /* count intersection if east of point and entry is polygon*/ 5.576 - if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++; 5.577 - } 5.578 - } 5.579 - } 5.580 - /* return true if number of intersections is odd */ 5.581 - return counter & 1; 5.582 -} 5.583 - 5.584 -/* calculate (approximate) distance between point and cluster */ 5.585 -static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) { 5.586 - int i, j, k; /* i: entry, j: point in entry, k: next point in entry */ 5.587 - int entrytype; /* type of entry */ 5.588 - int npoints; /* number of points in entry */ 5.589 - pgl_point *points; /* array of points in entry */ 5.590 - int lon_dir = 0; /* first vertex west (-1) or east (+1) */ 5.591 - double lon_break = 0; /* antipodal longitude of first vertex */ 5.592 - double lon_min = 0; /* minimum (adjusted) longitude of entry vertices */ 5.593 - double lon_max = 0; /* maximum (adjusted) longitude of entry vertices */ 5.594 - double lat0 = point->lat; /* latitude of point */ 5.595 - double lon0; /* (adjusted) longitude of point */ 5.596 - double lat1, lon1; /* latitude and (adjusted) longitude of vertex */ 5.597 - double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */ 5.598 - double s; /* scalar for vector calculations */ 5.599 - double dist; /* distance calculated in one step */ 5.600 - double min_dist = INFINITY; /* minimum distance */ 5.601 - /* distance is zero if point is contained in cluster */ 5.602 - if (pgl_point_in_cluster(point, cluster)) return 0; 5.603 - /* iterate over all entries */ 5.604 - for (i=0; i<cluster->nentries; i++) { 5.605 - /* get properties of entry */ 5.606 - entrytype = cluster->entries[i].entrytype; 5.607 - npoints = cluster->entries[i].npoints; 5.608 - points = PGL_ENTRY_POINTS(cluster, i); 5.609 - /* determine east/west orientation of first point of entry and calculate 5.610 - antipodal longitude */ 5.611 - lon_break = points[0].lon; 5.612 - if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 5.613 - else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 5.614 - else lon_dir = 0; 5.615 - /* determine covered longitude range */ 5.616 - for (j=0; j<npoints; j++) { 5.617 - /* get longitude of vertex */ 5.618 - lon1 = points[j].lon; 5.619 - /* adjust longitude to fix potential wrap-around */ 5.620 - if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360; 5.621 - else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360; 5.622 - /* update minimum and maximum longitude of polygon */ 5.623 - if (j == 0 || lon1 < lon_min) lon_min = lon1; 5.624 - if (j == 0 || lon1 > lon_max) lon_max = lon1; 5.625 - } 5.626 - /* adjust longitude wrap-around according to full longitude range */ 5.627 - lon_break = (lon_max + lon_min) / 2; 5.628 - if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 5.629 - else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 5.630 - /* get longitude of point */ 5.631 - lon0 = point->lon; 5.632 - /* consider longitude wrap-around for point */ 5.633 - if (lon_dir < 0 && lon0 > lon_break) lon0 -= 360; 5.634 - else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360; 5.635 - /* iterate over all edges and vertices */ 5.636 - for (j=0; j<npoints; j++) { 5.637 - /* get latitude and longitude values of current point */ 5.638 - lat1 = points[j].lat; 5.639 - lon1 = points[j].lon; 5.640 - /* consider longitude wrap-around for current point */ 5.641 - if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360; 5.642 - else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360; 5.643 - /* calculate distance to vertex */ 5.644 - dist = pgl_distance(lat0, lon0, lat1, lon1); 5.645 - /* store calculated distance if smallest */ 5.646 - if (dist < min_dist) min_dist = dist; 5.647 - /* calculate index of next vertex */ 5.648 - k = (j+1) % npoints; 5.649 - /* skip last edge unless entry is (closed) outline or polygon */ 5.650 - if ( 5.651 - k == 0 && 5.652 - entrytype != PGL_ENTRY_OUTLINE && 5.653 - entrytype != PGL_ENTRY_POLYGON 5.654 - ) continue; 5.655 - /* get latitude and longitude values of next point */ 5.656 - lat2 = points[k].lat; 5.657 - lon2 = points[k].lon; 5.658 - /* consider longitude wrap-around for next point */ 5.659 - if (lon_dir < 0 && lon2 > lon_break) lon2 -= 360; 5.660 - else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360; 5.661 - /* go to next vertex and edge if edge is degenerated */ 5.662 - if (lat1 == lat2 && lon1 == lon2) continue; 5.663 - /* otherwise test if point can be projected onto edge of polygon */ 5.664 - s = ( 5.665 - ((lat0-lat1) * (lat2-lat1) + (lon0-lon1) * (lon2-lon1)) / 5.666 - ((lat2-lat1) * (lat2-lat1) + (lon2-lon1) * (lon2-lon1)) 5.667 - ); 5.668 - /* go to next vertex and edge if point cannot be projected */ 5.669 - if (!(s > 0 && s < 1)) continue; 5.670 - /* calculate distance from original point to projected point */ 5.671 - dist = pgl_distance( 5.672 - lat0, lon0, 5.673 - lat1 + s * (lat2-lat1), 5.674 - lon1 + s * (lon2-lon1) 5.675 - ); 5.676 - /* store calculated distance if smallest */ 5.677 - if (dist < min_dist) min_dist = dist; 5.678 - } 5.679 - } 5.680 - /* return minimum distance */ 5.681 - return min_dist; 5.682 -} 5.683 - 5.684 -/* estimator function for distance between box and point */ 5.685 -/* allowed to return smaller values than actually correct */ 5.686 -static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) { 5.687 - double dlon; /* longitude range of box (delta longitude) */ 5.688 - double h; /* half of distance along meridian */ 5.689 - double d; /* distance between both southern or both northern points */ 5.690 - double cur_dist; /* calculated distance */ 5.691 - double min_dist; /* minimum distance calculated */ 5.692 - /* return infinity if bounding box is empty */ 5.693 - if (box->lat_min > box->lat_max) return INFINITY; 5.694 - /* return zero if point is inside bounding box */ 5.695 - if (pgl_point_in_box(point, box)) return 0; 5.696 - /* calculate delta longitude */ 5.697 - dlon = box->lon_max - box->lon_min; 5.698 - if (dlon < 0) dlon += 360; /* 180th meridian crossed */ 5.699 - /* if delta longitude is greater than 180 degrees, perform safe fall-back */ 5.700 - if (dlon > 180) return 0; 5.701 - /* calculate half of distance along meridian */ 5.702 - h = pgl_distance(box->lat_min, 0, box->lat_max, 0) / 2; 5.703 - /* calculate full distance between southern points */ 5.704 - d = pgl_distance(box->lat_min, 0, box->lat_min, dlon); 5.705 - /* calculate maximum of full distance and half distance */ 5.706 - if (h > d) d = h; 5.707 - /* calculate distance from point to first southern vertex and substract 5.708 - maximum error */ 5.709 - min_dist = pgl_distance( 5.710 - point->lat, point->lon, box->lat_min, box->lon_min 5.711 - ) - d; 5.712 - /* return zero if estimated distance is smaller than zero */ 5.713 - if (min_dist <= 0) return 0; 5.714 - /* repeat procedure with second southern vertex */ 5.715 - cur_dist = pgl_distance( 5.716 - point->lat, point->lon, box->lat_min, box->lon_max 5.717 - ) - d; 5.718 - if (cur_dist <= 0) return 0; 5.719 - if (cur_dist < min_dist) min_dist = cur_dist; 5.720 - /* calculate full distance between northern points */ 5.721 - d = pgl_distance(box->lat_max, 0, box->lat_max, dlon); 5.722 - /* calculate maximum of full distance and half distance */ 5.723 - if (h > d) d = h; 5.724 - /* repeat procedure with northern vertices */ 5.725 - cur_dist = pgl_distance( 5.726 - point->lat, point->lon, box->lat_max, box->lon_max 5.727 - ) - d; 5.728 - if (cur_dist <= 0) return 0; 5.729 - if (cur_dist < min_dist) min_dist = cur_dist; 5.730 - cur_dist = pgl_distance( 5.731 - point->lat, point->lon, box->lat_max, box->lon_min 5.732 - ) - d; 5.733 - if (cur_dist <= 0) return 0; 5.734 - if (cur_dist < min_dist) min_dist = cur_dist; 5.735 - /* return smallest value (unless already returned zero) */ 5.736 - return min_dist; 5.737 -} 5.738 - 5.739 - 5.740 -/*----------------------------* 5.741 - * fractal geographic index * 5.742 - *----------------------------*/ 5.743 - 5.744 -/* number of bytes used for geographic (center) position in keys */ 5.745 -#define PGL_KEY_LATLON_BYTELEN 7 5.746 - 5.747 -/* maximum reference value for logarithmic size of geographic objects */ 5.748 -#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0) /* can be tweaked */ 5.749 - 5.750 -/* safety margin to avoid floating point errors in distance estimation */ 5.751 -#define PGL_FPE_SAFETY (1.0+1e-14) /* slightly greater than 1.0 */ 5.752 - 5.753 -/* pointer to index key (either pgl_pointkey or pgl_areakey) */ 5.754 -typedef unsigned char *pgl_keyptr; 5.755 - 5.756 -/* index key for points (objects with zero area) on the spheroid */ 5.757 -/* bit 0..55: interspersed bits of latitude and longitude, 5.758 - bit 56..57: always zero, 5.759 - bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */ 5.760 -typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1]; 5.761 - 5.762 -/* index key for geographic objects on spheroid with area greater than zero */ 5.763 -/* bit 0..55: interspersed bits of latitude and longitude of center point, 5.764 - bit 56: always set to 1, 5.765 - bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.), 5.766 - bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to 5.767 - PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth 5.768 - = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL 5.769 - (with interspersed bits = 0 and node depth = 0) for keys which 5.770 - cover both empty and non-empty objects */ 5.771 - 5.772 -typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2]; 5.773 - 5.774 -/* helper macros for reading/writing index keys */ 5.775 -#define PGL_KEY_NODEDEPTH_OFFSET PGL_KEY_LATLON_BYTELEN 5.776 -#define PGL_KEY_OBJSIZE_OFFSET (PGL_KEY_NODEDEPTH_OFFSET+1) 5.777 -#define PGL_POINTKEY_MAXDEPTH (PGL_KEY_LATLON_BYTELEN*8) 5.778 -#define PGL_AREAKEY_MAXDEPTH (2*PGL_POINTKEY_MAXDEPTH+1) 5.779 -#define PGL_AREAKEY_MAXOBJSIZE (PGL_POINTKEY_MAXDEPTH+1) 5.780 -#define PGL_AREAKEY_TYPEMASK 0x80 5.781 -#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8))) 5.782 -#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \ 5.783 - ( PGL_KEY_LATLONBIT(key1, n) ^ \ 5.784 - PGL_KEY_LATLONBIT(key2, n) ) 5.785 -#define PGL_KEY_IS_AREAKEY(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \ 5.786 - PGL_AREAKEY_TYPEMASK) 5.787 -#define PGL_KEY_NODEDEPTH(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \ 5.788 - (PGL_AREAKEY_TYPEMASK-1)) 5.789 -#define PGL_KEY_OBJSIZE(key) ((key)[PGL_KEY_OBJSIZE_OFFSET]) 5.790 -#define PGL_KEY_OBJSIZE_EMPTY 126 5.791 -#define PGL_KEY_OBJSIZE_UNIVERSAL 127 5.792 -#define PGL_KEY_IS_EMPTY(key) ( PGL_KEY_IS_AREAKEY(key) && \ 5.793 - (key)[PGL_KEY_OBJSIZE_OFFSET] == \ 5.794 - PGL_KEY_OBJSIZE_EMPTY ) 5.795 -#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \ 5.796 - (key)[PGL_KEY_OBJSIZE_OFFSET] == \ 5.797 - PGL_KEY_OBJSIZE_UNIVERSAL ) 5.798 - 5.799 -/* set area key to match empty objects only */ 5.800 -static void pgl_key_set_empty(pgl_keyptr key) { 5.801 - memset(key, 0, sizeof(pgl_areakey)); 5.802 - /* Note: setting node depth to maximum is required for picksplit function */ 5.803 - key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH; 5.804 - key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY; 5.805 -} 5.806 - 5.807 -/* set area key to match any object (including empty objects) */ 5.808 -static void pgl_key_set_universal(pgl_keyptr key) { 5.809 - memset(key, 0, sizeof(pgl_areakey)); 5.810 - key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK; 5.811 - key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL; 5.812 -} 5.813 - 5.814 -/* convert a point on earth into a max-depth key to be used in index */ 5.815 -static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) { 5.816 - double lat = point->lat; 5.817 - double lon = point->lon; 5.818 - int i; 5.819 - /* clear latitude and longitude bits */ 5.820 - memset(key, 0, PGL_KEY_LATLON_BYTELEN); 5.821 - /* set node depth to maximum and type bit to zero */ 5.822 - key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH; 5.823 - /* iterate over all latitude/longitude bit pairs */ 5.824 - for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) { 5.825 - /* determine latitude bit */ 5.826 - if (lat >= 0) { 5.827 - key[i/4] |= 0x80 >> (2*(i%4)); 5.828 - lat *= 2; lat -= 90; 5.829 - } else { 5.830 - lat *= 2; lat += 90; 5.831 - } 5.832 - /* determine longitude bit */ 5.833 - if (lon >= 0) { 5.834 - key[i/4] |= 0x80 >> (2*(i%4)+1); 5.835 - lon *= 2; lon -= 180; 5.836 - } else { 5.837 - lon *= 2; lon += 180; 5.838 - } 5.839 - } 5.840 -} 5.841 - 5.842 -/* convert a circle on earth into a max-depth key to be used in an index */ 5.843 -static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) { 5.844 - /* handle special case of empty circle */ 5.845 - if (circle->radius < 0) { 5.846 - pgl_key_set_empty(key); 5.847 - return; 5.848 - } 5.849 - /* perform same action as for point keys */ 5.850 - pgl_point_to_key(&(circle->center), key); 5.851 - /* but overwrite type and node depth to fit area index key */ 5.852 - key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH; 5.853 - /* check if radius is greater than (or equal to) reference size */ 5.854 - /* (treat equal values as greater values for numerical safety) */ 5.855 - if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) { 5.856 - /* if yes, set logarithmic size to zero */ 5.857 - key[PGL_KEY_OBJSIZE_OFFSET] = 0; 5.858 - } else { 5.859 - /* otherwise, determine logarithmic size iteratively */ 5.860 - /* (one step is equivalent to a factor of sqrt(2)) */ 5.861 - double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2; 5.862 - int objsize = 1; 5.863 - while (objsize < PGL_AREAKEY_MAXOBJSIZE) { 5.864 - /* stop when radius is greater than (or equal to) adjusted reference */ 5.865 - /* (treat equal values as greater values for numerical safety) */ 5.866 - if (circle->radius >= reference) break; 5.867 - reference /= M_SQRT2; 5.868 - objsize++; 5.869 - } 5.870 - /* set logarithmic size to determined value */ 5.871 - key[PGL_KEY_OBJSIZE_OFFSET] = objsize; 5.872 - } 5.873 -} 5.874 - 5.875 -/* check if one key is subkey of another key or vice versa */ 5.876 -static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) { 5.877 - int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */ 5.878 - /* determine smallest depth */ 5.879 - int depth1 = PGL_KEY_NODEDEPTH(key1); 5.880 - int depth2 = PGL_KEY_NODEDEPTH(key2); 5.881 - int depth = (depth1 < depth2) ? depth1 : depth2; 5.882 - /* check if keys are area keys (assuming that both keys have same type) */ 5.883 - if (PGL_KEY_IS_AREAKEY(key1)) { 5.884 - int j = 0; /* bit offset for logarithmic object size bits */ 5.885 - int k = 0; /* bit offset for latitude and longitude */ 5.886 - /* fetch logarithmic object size information */ 5.887 - int objsize1 = PGL_KEY_OBJSIZE(key1); 5.888 - int objsize2 = PGL_KEY_OBJSIZE(key2); 5.889 - /* handle special cases for empty objects (universal and empty keys) */ 5.890 - if ( 5.891 - objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL || 5.892 - objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL 5.893 - ) return true; 5.894 - if ( 5.895 - objsize1 == PGL_KEY_OBJSIZE_EMPTY || 5.896 - objsize2 == PGL_KEY_OBJSIZE_EMPTY 5.897 - ) return objsize1 == objsize2; 5.898 - /* iterate through key bits */ 5.899 - for (i=0; i<depth; i++) { 5.900 - /* every second bit is a bit describing the object size */ 5.901 - if (i%2 == 0) { 5.902 - /* check if object size bit is different in both keys (objsize1 and 5.903 - objsize2 describe the minimum index when object size bit is set) */ 5.904 - if ( 5.905 - (objsize1 <= j && objsize2 > j) || 5.906 - (objsize2 <= j && objsize1 > j) 5.907 - ) { 5.908 - /* bit differs, therefore keys are in separate branches */ 5.909 - return false; 5.910 - } 5.911 - /* increase bit counter for object size bits */ 5.912 - j++; 5.913 - } 5.914 - /* all other bits describe latitude and longitude */ 5.915 - else { 5.916 - /* check if bit differs in both keys */ 5.917 - if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) { 5.918 - /* bit differs, therefore keys are in separate branches */ 5.919 - return false; 5.920 - } 5.921 - /* increase bit counter for latitude/longitude bits */ 5.922 - k++; 5.923 - } 5.924 - } 5.925 - } 5.926 - /* if not, keys are point keys */ 5.927 - else { 5.928 - /* iterate through key bits */ 5.929 - for (i=0; i<depth; i++) { 5.930 - /* check if bit differs in both keys */ 5.931 - if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) { 5.932 - /* bit differs, therefore keys are in separate branches */ 5.933 - return false; 5.934 - } 5.935 - } 5.936 - } 5.937 - /* return true because keys are in the same branch */ 5.938 - return true; 5.939 -} 5.940 - 5.941 -/* combine two keys into new key which covers both original keys */ 5.942 -/* (result stored in first argument) */ 5.943 -static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) { 5.944 - int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */ 5.945 - /* determine smallest depth */ 5.946 - int depth1 = PGL_KEY_NODEDEPTH(dst); 5.947 - int depth2 = PGL_KEY_NODEDEPTH(src); 5.948 - int depth = (depth1 < depth2) ? depth1 : depth2; 5.949 - /* check if keys are area keys (assuming that both keys have same type) */ 5.950 - if (PGL_KEY_IS_AREAKEY(dst)) { 5.951 - pgl_areakey dstbuf = { 0, }; /* destination buffer (cleared) */ 5.952 - int j = 0; /* bit offset for logarithmic object size bits */ 5.953 - int k = 0; /* bit offset for latitude and longitude */ 5.954 - /* fetch logarithmic object size information */ 5.955 - int objsize1 = PGL_KEY_OBJSIZE(dst); 5.956 - int objsize2 = PGL_KEY_OBJSIZE(src); 5.957 - /* handle special cases for empty objects (universal and empty keys) */ 5.958 - if ( 5.959 - objsize1 > PGL_AREAKEY_MAXOBJSIZE || 5.960 - objsize2 > PGL_AREAKEY_MAXOBJSIZE 5.961 - ) { 5.962 - if ( 5.963 - objsize1 == PGL_KEY_OBJSIZE_EMPTY && 5.964 - objsize2 == PGL_KEY_OBJSIZE_EMPTY 5.965 - ) pgl_key_set_empty(dst); 5.966 - else pgl_key_set_universal(dst); 5.967 - return; 5.968 - } 5.969 - /* iterate through key bits */ 5.970 - for (i=0; i<depth; i++) { 5.971 - /* every second bit is a bit describing the object size */ 5.972 - if (i%2 == 0) { 5.973 - /* increase bit counter for object size bits first */ 5.974 - /* (handy when setting objsize variable) */ 5.975 - j++; 5.976 - /* check if object size bit is set in neither key */ 5.977 - if (objsize1 >= j && objsize2 >= j) { 5.978 - /* set objsize in destination buffer to indicate that size bit is 5.979 - unset in destination buffer at the current bit position */ 5.980 - dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j; 5.981 - } 5.982 - /* break if object size bit is set in one key only */ 5.983 - else if (objsize1 >= j || objsize2 >= j) break; 5.984 - } 5.985 - /* all other bits describe latitude and longitude */ 5.986 - else { 5.987 - /* break if bit differs in both keys */ 5.988 - if (PGL_KEY_LATLONBIT(dst, k)) { 5.989 - if (!PGL_KEY_LATLONBIT(src, k)) break; 5.990 - /* but set bit in destination buffer if bit is set in both keys */ 5.991 - dstbuf[k/8] |= 0x80 >> (k%8); 5.992 - } else if (PGL_KEY_LATLONBIT(src, k)) break; 5.993 - /* increase bit counter for latitude/longitude bits */ 5.994 - k++; 5.995 - } 5.996 - } 5.997 - /* set common node depth and type bit (type bit = 1) */ 5.998 - dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i; 5.999 - /* copy contents of destination buffer to first key */ 5.1000 - memcpy(dst, dstbuf, sizeof(pgl_areakey)); 5.1001 - } 5.1002 - /* if not, keys are point keys */ 5.1003 - else { 5.1004 - pgl_pointkey dstbuf = { 0, }; /* destination buffer (cleared) */ 5.1005 - /* iterate through key bits */ 5.1006 - for (i=0; i<depth; i++) { 5.1007 - /* break if bit differs in both keys */ 5.1008 - if (PGL_KEY_LATLONBIT(dst, i)) { 5.1009 - if (!PGL_KEY_LATLONBIT(src, i)) break; 5.1010 - /* but set bit in destination buffer if bit is set in both keys */ 5.1011 - dstbuf[i/8] |= 0x80 >> (i%8); 5.1012 - } else if (PGL_KEY_LATLONBIT(src, i)) break; 5.1013 - } 5.1014 - /* set common node depth (type bit = 0) */ 5.1015 - dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i; 5.1016 - /* copy contents of destination buffer to first key */ 5.1017 - memcpy(dst, dstbuf, sizeof(pgl_pointkey)); 5.1018 - } 5.1019 -} 5.1020 - 5.1021 -/* determine center(!) boundaries and radius estimation of index key */ 5.1022 -static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) { 5.1023 - int i; 5.1024 - /* determine node depth */ 5.1025 - int depth = PGL_KEY_NODEDEPTH(key); 5.1026 - /* center point of possible result */ 5.1027 - double lat = 0; 5.1028 - double lon = 0; 5.1029 - /* maximum distance of real center point from key center */ 5.1030 - double dlat = 90; 5.1031 - double dlon = 180; 5.1032 - /* maximum radius of contained objects */ 5.1033 - double radius = 0; /* always return zero for point index keys */ 5.1034 - /* check if key is area key */ 5.1035 - if (PGL_KEY_IS_AREAKEY(key)) { 5.1036 - /* get logarithmic object size */ 5.1037 - int objsize = PGL_KEY_OBJSIZE(key); 5.1038 - /* handle special cases for empty objects (universal and empty keys) */ 5.1039 - if (objsize == PGL_KEY_OBJSIZE_EMPTY) { 5.1040 - pgl_box_set_empty(box); 5.1041 - return 0; 5.1042 - } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) { 5.1043 - box->lat_min = -90; 5.1044 - box->lat_max = 90; 5.1045 - box->lon_min = -180; 5.1046 - box->lon_max = 180; 5.1047 - return 0; /* any value >= 0 would do */ 5.1048 - } 5.1049 - /* calculate maximum possible radius of objects covered by the given key */ 5.1050 - if (objsize == 0) radius = INFINITY; 5.1051 - else { 5.1052 - radius = PGL_AREAKEY_REFOBJSIZE; 5.1053 - while (--objsize) radius /= M_SQRT2; 5.1054 - } 5.1055 - /* iterate over latitude and longitude bits in key */ 5.1056 - /* (every second bit is a latitude or longitude bit) */ 5.1057 - for (i=0; i<depth/2; i++) { 5.1058 - /* check if latitude bit */ 5.1059 - if (i%2 == 0) { 5.1060 - /* cut latitude dimension in half */ 5.1061 - dlat /= 2; 5.1062 - /* increase center latitude if bit is 1, otherwise decrease */ 5.1063 - if (PGL_KEY_LATLONBIT(key, i)) lat += dlat; 5.1064 - else lat -= dlat; 5.1065 - } 5.1066 - /* otherwise longitude bit */ 5.1067 - else { 5.1068 - /* cut longitude dimension in half */ 5.1069 - dlon /= 2; 5.1070 - /* increase center longitude if bit is 1, otherwise decrease */ 5.1071 - if (PGL_KEY_LATLONBIT(key, i)) lon += dlon; 5.1072 - else lon -= dlon; 5.1073 - } 5.1074 - } 5.1075 - } 5.1076 - /* if not, keys are point keys */ 5.1077 - else { 5.1078 - /* iterate over all bits in key */ 5.1079 - for (i=0; i<depth; i++) { 5.1080 - /* check if latitude bit */ 5.1081 - if (i%2 == 0) { 5.1082 - /* cut latitude dimension in half */ 5.1083 - dlat /= 2; 5.1084 - /* increase center latitude if bit is 1, otherwise decrease */ 5.1085 - if (PGL_KEY_LATLONBIT(key, i)) lat += dlat; 5.1086 - else lat -= dlat; 5.1087 - } 5.1088 - /* otherwise longitude bit */ 5.1089 - else { 5.1090 - /* cut longitude dimension in half */ 5.1091 - dlon /= 2; 5.1092 - /* increase center longitude if bit is 1, otherwise decrease */ 5.1093 - if (PGL_KEY_LATLONBIT(key, i)) lon += dlon; 5.1094 - else lon -= dlon; 5.1095 - } 5.1096 - } 5.1097 - } 5.1098 - /* calculate boundaries from center point and remaining dlat and dlon */ 5.1099 - /* (return values through pointer to box) */ 5.1100 - box->lat_min = lat - dlat; 5.1101 - box->lat_max = lat + dlat; 5.1102 - box->lon_min = lon - dlon; 5.1103 - box->lon_max = lon + dlon; 5.1104 - /* return radius (as a function return value) */ 5.1105 - return radius; 5.1106 -} 5.1107 - 5.1108 -/* estimator function for distance between point and index key */ 5.1109 -/* allowed to return smaller values than actually correct */ 5.1110 -static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) { 5.1111 - pgl_box box; /* center(!) bounding box of area index key */ 5.1112 - /* calculate center(!) bounding box and maximum radius of objects covered 5.1113 - by area index key (radius is zero for point index keys) */ 5.1114 - double distance = pgl_key_to_box(key, &box); 5.1115 - /* calculate estimated distance between bounding box of center point of 5.1116 - indexed object and point passed as second argument, then substract maximum 5.1117 - radius of objects covered by index key */ 5.1118 - /* (use PGL_FPE_SAFETY factor to cope with minor floating point errors) */ 5.1119 - distance = ( 5.1120 - pgl_estimate_point_box_distance(point, &box) / PGL_FPE_SAFETY - 5.1121 - distance * PGL_FPE_SAFETY 5.1122 - ); 5.1123 - /* truncate negative results to zero */ 5.1124 - if (distance <= 0) distance = 0; 5.1125 - /* return result */ 5.1126 - return distance; 5.1127 -} 5.1128 - 5.1129 - 5.1130 -/*---------------------------------* 5.1131 - * helper functions for text I/O * 5.1132 - *---------------------------------*/ 5.1133 - 5.1134 -#define PGL_NUMBUFLEN 64 /* buffer size for number to string conversion */ 5.1135 - 5.1136 -/* convert floating point number to string (round-trip safe) */ 5.1137 -static void pgl_print_float(char *buf, double flt) { 5.1138 - /* check if number is integral */ 5.1139 - if (trunc(flt) == flt) { 5.1140 - /* for integral floats use maximum precision */ 5.1141 - snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt); 5.1142 - } else { 5.1143 - /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */ 5.1144 - snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt); 5.1145 - if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt); 5.1146 - if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt); 5.1147 - } 5.1148 -} 5.1149 - 5.1150 -/* convert latitude floating point number (in degrees) to string */ 5.1151 -static void pgl_print_lat(char *buf, double lat) { 5.1152 - if (signbit(lat)) { 5.1153 - /* treat negative latitudes (including -0) as south */ 5.1154 - snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat); 5.1155 - } else { 5.1156 - /* treat positive latitudes (including +0) as north */ 5.1157 - snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat); 5.1158 - } 5.1159 -} 5.1160 - 5.1161 -/* convert longitude floating point number (in degrees) to string */ 5.1162 -static void pgl_print_lon(char *buf, double lon) { 5.1163 - if (signbit(lon)) { 5.1164 - /* treat negative longitudes (including -0) as west */ 5.1165 - snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon); 5.1166 - } else { 5.1167 - /* treat positive longitudes (including +0) as east */ 5.1168 - snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon); 5.1169 - } 5.1170 -} 5.1171 - 5.1172 -/* bit masks used as return value of pgl_scan() function */ 5.1173 -#define PGL_SCAN_NONE 0 /* no value has been parsed */ 5.1174 -#define PGL_SCAN_LAT (1<<0) /* latitude has been parsed */ 5.1175 -#define PGL_SCAN_LON (1<<1) /* longitude has been parsed */ 5.1176 -#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON) /* bitwise OR of both */ 5.1177 - 5.1178 -/* parse a coordinate (can be latitude or longitude) */ 5.1179 -static int pgl_scan(char **str, double *lat, double *lon) { 5.1180 - double val; 5.1181 - int len; 5.1182 - if ( 5.1183 - sscanf(*str, " N %lf %n", &val, &len) || 5.1184 - sscanf(*str, " n %lf %n", &val, &len) 5.1185 - ) { 5.1186 - *str += len; *lat = val; return PGL_SCAN_LAT; 5.1187 - } 5.1188 - if ( 5.1189 - sscanf(*str, " S %lf %n", &val, &len) || 5.1190 - sscanf(*str, " s %lf %n", &val, &len) 5.1191 - ) { 5.1192 - *str += len; *lat = -val; return PGL_SCAN_LAT; 5.1193 - } 5.1194 - if ( 5.1195 - sscanf(*str, " E %lf %n", &val, &len) || 5.1196 - sscanf(*str, " e %lf %n", &val, &len) 5.1197 - ) { 5.1198 - *str += len; *lon = val; return PGL_SCAN_LON; 5.1199 - } 5.1200 - if ( 5.1201 - sscanf(*str, " W %lf %n", &val, &len) || 5.1202 - sscanf(*str, " w %lf %n", &val, &len) 5.1203 - ) { 5.1204 - *str += len; *lon = -val; return PGL_SCAN_LON; 5.1205 - } 5.1206 - return PGL_SCAN_NONE; 5.1207 -} 5.1208 - 5.1209 - 5.1210 -/*-----------------* 5.1211 - * SQL functions * 5.1212 - *-----------------*/ 5.1213 - 5.1214 -/* Note: These function names use "epoint", "ebox", etc. notation here instead 5.1215 - of "point", "box", etc. in order to distinguish them from any previously 5.1216 - defined functions. */ 5.1217 - 5.1218 -/* function needed for dummy types and/or not implemented features */ 5.1219 -PG_FUNCTION_INFO_V1(pgl_notimpl); 5.1220 -Datum pgl_notimpl(PG_FUNCTION_ARGS) { 5.1221 - ereport(ERROR, (errmsg("not implemented by pgLatLon"))); 5.1222 -} 5.1223 - 5.1224 -/* set point to latitude and longitude (including checks) */ 5.1225 -static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) { 5.1226 - /* reject infinite or NaN values */ 5.1227 - if (!isfinite(lat) || !isfinite(lon)) { 5.1228 - ereport(ERROR, ( 5.1229 - errcode(ERRCODE_DATA_EXCEPTION), 5.1230 - errmsg("epoint requires finite coordinates") 5.1231 - )); 5.1232 - } 5.1233 - /* check latitude bounds */ 5.1234 - if (lat < -90) { 5.1235 - ereport(WARNING, (errmsg("latitude exceeds south pole"))); 5.1236 - lat = -90; 5.1237 - } else if (lat > 90) { 5.1238 - ereport(WARNING, (errmsg("latitude exceeds north pole"))); 5.1239 - lat = 90; 5.1240 - } 5.1241 - /* check longitude bounds */ 5.1242 - if (lon < -180) { 5.1243 - ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized"))); 5.1244 - lon += 360 - trunc(lon / 360) * 360; 5.1245 - } else if (lon > 180) { 5.1246 - ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized"))); 5.1247 - lon -= 360 + trunc(lon / 360) * 360; 5.1248 - } 5.1249 - /* store rounded latitude/longitude values for round-trip safety */ 5.1250 - point->lat = pgl_round(lat); 5.1251 - point->lon = pgl_round(lon); 5.1252 -} 5.1253 - 5.1254 -/* create point ("epoint" in SQL) from latitude and longitude */ 5.1255 -PG_FUNCTION_INFO_V1(pgl_create_epoint); 5.1256 -Datum pgl_create_epoint(PG_FUNCTION_ARGS) { 5.1257 - pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point)); 5.1258 - pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1)); 5.1259 - PG_RETURN_POINTER(point); 5.1260 -} 5.1261 - 5.1262 -/* parse point ("epoint" in SQL) */ 5.1263 -/* format: '[NS]<float> [EW]<float>' */ 5.1264 -PG_FUNCTION_INFO_V1(pgl_epoint_in); 5.1265 -Datum pgl_epoint_in(PG_FUNCTION_ARGS) { 5.1266 - char *str = PG_GETARG_CSTRING(0); /* input string */ 5.1267 - char *strptr = str; /* current position within string */ 5.1268 - int done = 0; /* bit mask storing if latitude or longitude was read */ 5.1269 - double lat, lon; /* parsed values as double precision floats */ 5.1270 - pgl_point *point; /* return value (to be palloc'ed) */ 5.1271 - /* parse two floats (each latitude or longitude) separated by white-space */ 5.1272 - done |= pgl_scan(&strptr, &lat, &lon); 5.1273 - if (strptr != str && isspace(strptr[-1])) { 5.1274 - done |= pgl_scan(&strptr, &lat, &lon); 5.1275 - } 5.1276 - /* require end of string, and latitude and longitude parsed successfully */ 5.1277 - if (strptr[0] || done != PGL_SCAN_LATLON) { 5.1278 - ereport(ERROR, ( 5.1279 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 5.1280 - errmsg("invalid input syntax for type epoint: \"%s\"", str) 5.1281 - )); 5.1282 - } 5.1283 - /* allocate memory for result */ 5.1284 - point = (pgl_point *)palloc(sizeof(pgl_point)); 5.1285 - /* set latitude and longitude (and perform checks) */ 5.1286 - pgl_epoint_set_latlon(point, lat, lon); 5.1287 - /* return result */ 5.1288 - PG_RETURN_POINTER(point); 5.1289 -} 5.1290 - 5.1291 -/* create box ("ebox" in SQL) that is empty */ 5.1292 -PG_FUNCTION_INFO_V1(pgl_create_empty_ebox); 5.1293 -Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) { 5.1294 - pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 5.1295 - pgl_box_set_empty(box); 5.1296 - PG_RETURN_POINTER(box); 5.1297 -} 5.1298 - 5.1299 -/* set box to given boundaries (including checks) */ 5.1300 -static void pgl_ebox_set_boundaries( 5.1301 - pgl_box *box, 5.1302 - double lat_min, double lat_max, double lon_min, double lon_max 5.1303 -) { 5.1304 - /* if minimum latitude is greater than maximum latitude, return empty box */ 5.1305 - if (lat_min > lat_max) { 5.1306 - pgl_box_set_empty(box); 5.1307 - return; 5.1308 - } 5.1309 - /* otherwise reject infinite or NaN values */ 5.1310 - if ( 5.1311 - !isfinite(lat_min) || !isfinite(lat_max) || 5.1312 - !isfinite(lon_min) || !isfinite(lon_max) 5.1313 - ) { 5.1314 - ereport(ERROR, ( 5.1315 - errcode(ERRCODE_DATA_EXCEPTION), 5.1316 - errmsg("ebox requires finite coordinates") 5.1317 - )); 5.1318 - } 5.1319 - /* check latitude bounds */ 5.1320 - if (lat_max < -90) { 5.1321 - ereport(WARNING, (errmsg("northern latitude exceeds south pole"))); 5.1322 - lat_max = -90; 5.1323 - } else if (lat_max > 90) { 5.1324 - ereport(WARNING, (errmsg("northern latitude exceeds north pole"))); 5.1325 - lat_max = 90; 5.1326 - } 5.1327 - if (lat_min < -90) { 5.1328 - ereport(WARNING, (errmsg("southern latitude exceeds south pole"))); 5.1329 - lat_min = -90; 5.1330 - } else if (lat_min > 90) { 5.1331 - ereport(WARNING, (errmsg("southern latitude exceeds north pole"))); 5.1332 - lat_min = 90; 5.1333 - } 5.1334 - /* check if all longitudes are included */ 5.1335 - if (lon_max - lon_min >= 360) { 5.1336 - if (lon_max - lon_min > 360) ereport(WARNING, ( 5.1337 - errmsg("longitude coverage greater than 360 degrees") 5.1338 - )); 5.1339 - lon_min = -180; 5.1340 - lon_max = 180; 5.1341 - } else { 5.1342 - /* normalize longitude bounds */ 5.1343 - if (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360; 5.1344 - else if (lon_min > 180) lon_min -= 360 + trunc(lon_min / 360) * 360; 5.1345 - if (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360; 5.1346 - else if (lon_max > 180) lon_max -= 360 + trunc(lon_max / 360) * 360; 5.1347 - } 5.1348 - /* store rounded latitude/longitude values for round-trip safety */ 5.1349 - box->lat_min = pgl_round(lat_min); 5.1350 - box->lat_max = pgl_round(lat_max); 5.1351 - box->lon_min = pgl_round(lon_min); 5.1352 - box->lon_max = pgl_round(lon_max); 5.1353 - /* ensure that rounding does not change orientation */ 5.1354 - if (lon_min > lon_max && box->lon_min == box->lon_max) { 5.1355 - box->lon_min = -180; 5.1356 - box->lon_max = 180; 5.1357 - } 5.1358 -} 5.1359 - 5.1360 -/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */ 5.1361 -PG_FUNCTION_INFO_V1(pgl_create_ebox); 5.1362 -Datum pgl_create_ebox(PG_FUNCTION_ARGS) { 5.1363 - pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 5.1364 - pgl_ebox_set_boundaries( 5.1365 - box, 5.1366 - PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), 5.1367 - PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3) 5.1368 - ); 5.1369 - PG_RETURN_POINTER(box); 5.1370 -} 5.1371 - 5.1372 -/* create box ("ebox" in SQL) from two points ("epoint"s) */ 5.1373 -/* (can not be used to cover a longitude range of more than 120 degrees) */ 5.1374 -PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints); 5.1375 -Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) { 5.1376 - pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0); 5.1377 - pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1); 5.1378 - pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 5.1379 - double lat_min, lat_max, lon_min, lon_max; 5.1380 - double dlon; /* longitude range (delta longitude) */ 5.1381 - /* order latitude and longitude boundaries */ 5.1382 - if (point2->lat < point1->lat) { 5.1383 - lat_min = point2->lat; 5.1384 - lat_max = point1->lat; 5.1385 - } else { 5.1386 - lat_min = point1->lat; 5.1387 - lat_max = point2->lat; 5.1388 - } 5.1389 - if (point2->lon < point1->lon) { 5.1390 - lon_min = point2->lon; 5.1391 - lon_max = point1->lon; 5.1392 - } else { 5.1393 - lon_min = point1->lon; 5.1394 - lon_max = point2->lon; 5.1395 - } 5.1396 - /* calculate longitude range (round to avoid floating point errors) */ 5.1397 - dlon = pgl_round(lon_max - lon_min); 5.1398 - /* determine east-west direction */ 5.1399 - if (dlon >= 240) { 5.1400 - /* assume that 180th meridian is crossed and swap min/max longitude */ 5.1401 - double swap = lon_min; lon_min = lon_max; lon_max = swap; 5.1402 - } else if (dlon > 120) { 5.1403 - /* unclear orientation since delta longitude > 120 */ 5.1404 - ereport(ERROR, ( 5.1405 - errcode(ERRCODE_DATA_EXCEPTION), 5.1406 - errmsg("can not determine east/west orientation for ebox") 5.1407 - )); 5.1408 - } 5.1409 - /* use boundaries to setup box (and perform checks) */ 5.1410 - pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max); 5.1411 - /* return result */ 5.1412 - PG_RETURN_POINTER(box); 5.1413 -} 5.1414 - 5.1415 -/* parse box ("ebox" in SQL) */ 5.1416 -/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>' 5.1417 - or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */ 5.1418 -PG_FUNCTION_INFO_V1(pgl_ebox_in); 5.1419 -Datum pgl_ebox_in(PG_FUNCTION_ARGS) { 5.1420 - char *str = PG_GETARG_CSTRING(0); /* input string */ 5.1421 - char *str_lower; /* lower case version of input string */ 5.1422 - char *strptr; /* current position within string */ 5.1423 - int valid; /* number of valid chars */ 5.1424 - int done; /* specifies if latitude or longitude was read */ 5.1425 - double val; /* temporary variable */ 5.1426 - int lat_count = 0; /* count of latitude values parsed */ 5.1427 - int lon_count = 0; /* count of longitufde values parsed */ 5.1428 - double lat_min, lat_max, lon_min, lon_max; /* see pgl_box struct */ 5.1429 - pgl_box *box; /* return value (to be palloc'ed) */ 5.1430 - /* lowercase input */ 5.1431 - str_lower = psprintf("%s", str); 5.1432 - for (strptr=str_lower; *strptr; strptr++) { 5.1433 - if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A'; 5.1434 - } 5.1435 - /* reset reading position to start of (lowercase) string */ 5.1436 - strptr = str_lower; 5.1437 - /* check if empty box */ 5.1438 - valid = 0; 5.1439 - sscanf(strptr, " empty %n", &valid); 5.1440 - if (valid && strptr[valid] == 0) { 5.1441 - /* allocate and return empty box */ 5.1442 - box = (pgl_box *)palloc(sizeof(pgl_box)); 5.1443 - pgl_box_set_empty(box); 5.1444 - PG_RETURN_POINTER(box); 5.1445 - } 5.1446 - /* demand four blocks separated by whitespace */ 5.1447 - valid = 0; 5.1448 - sscanf(strptr, " %*s %*s %*s %*s %n", &valid); 5.1449 - /* if four blocks separated by whitespace exist, parse those blocks */ 5.1450 - if (strptr[valid] == 0) while (strptr[0]) { 5.1451 - /* parse either latitude or longitude (whichever found in input string) */ 5.1452 - done = pgl_scan(&strptr, &val, &val); 5.1453 - /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */ 5.1454 - if (done == PGL_SCAN_LAT) { 5.1455 - if (!lat_count) lat_min = val; else lat_max = val; 5.1456 - lat_count++; 5.1457 - } else if (done == PGL_SCAN_LON) { 5.1458 - if (!lon_count) lon_min = val; else lon_max = val; 5.1459 - lon_count++; 5.1460 - } else { 5.1461 - break; 5.1462 - } 5.1463 - } 5.1464 - /* require end of string, and two latitude and two longitude values */ 5.1465 - if (strptr[0] || lat_count != 2 || lon_count != 2) { 5.1466 - ereport(ERROR, ( 5.1467 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 5.1468 - errmsg("invalid input syntax for type ebox: \"%s\"", str) 5.1469 - )); 5.1470 - } 5.1471 - /* free lower case string */ 5.1472 - pfree(str_lower); 5.1473 - /* order boundaries (maximum greater than minimum) */ 5.1474 - if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; } 5.1475 - if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; } 5.1476 - /* allocate memory for result */ 5.1477 - box = (pgl_box *)palloc(sizeof(pgl_box)); 5.1478 - /* set boundaries (and perform checks) */ 5.1479 - pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max); 5.1480 - /* return result */ 5.1481 - PG_RETURN_POINTER(box); 5.1482 -} 5.1483 - 5.1484 -/* set circle to given latitude, longitude, and radius (including checks) */ 5.1485 -static void pgl_ecircle_set_latlon_radius( 5.1486 - pgl_circle *circle, double lat, double lon, double radius 5.1487 -) { 5.1488 - /* set center point (including checks) */ 5.1489 - pgl_epoint_set_latlon(&(circle->center), lat, lon); 5.1490 - /* handle non-positive radius */ 5.1491 - if (isnan(radius)) { 5.1492 - ereport(ERROR, ( 5.1493 - errcode(ERRCODE_DATA_EXCEPTION), 5.1494 - errmsg("invalid radius for ecircle") 5.1495 - )); 5.1496 - } 5.1497 - if (radius == 0) radius = 0; /* avoids -0 */ 5.1498 - else if (radius < 0) { 5.1499 - if (isfinite(radius)) { 5.1500 - ereport(NOTICE, (errmsg("negative radius converted to minus infinity"))); 5.1501 - } 5.1502 - radius = -INFINITY; 5.1503 - } 5.1504 - /* store radius (round-trip safety is ensured by pgl_print_float) */ 5.1505 - circle->radius = radius; 5.1506 -} 5.1507 - 5.1508 -/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */ 5.1509 -PG_FUNCTION_INFO_V1(pgl_create_ecircle); 5.1510 -Datum pgl_create_ecircle(PG_FUNCTION_ARGS) { 5.1511 - pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 5.1512 - pgl_ecircle_set_latlon_radius( 5.1513 - circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2) 5.1514 - ); 5.1515 - PG_RETURN_POINTER(circle); 5.1516 -} 5.1517 - 5.1518 -/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */ 5.1519 -PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint); 5.1520 -Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) { 5.1521 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.1522 - double radius = PG_GETARG_FLOAT8(1); 5.1523 - pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 5.1524 - /* set latitude, longitude, radius (and perform checks) */ 5.1525 - pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius); 5.1526 - /* return result */ 5.1527 - PG_RETURN_POINTER(circle); 5.1528 -} 5.1529 - 5.1530 -/* parse circle ("ecircle" in SQL) */ 5.1531 -/* format: '[NS]<float> [EW]<float> <float>' */ 5.1532 -PG_FUNCTION_INFO_V1(pgl_ecircle_in); 5.1533 -Datum pgl_ecircle_in(PG_FUNCTION_ARGS) { 5.1534 - char *str = PG_GETARG_CSTRING(0); /* input string */ 5.1535 - char *strptr = str; /* current position within string */ 5.1536 - double lat, lon, radius; /* parsed values as double precision flaots */ 5.1537 - int valid = 0; /* number of valid chars */ 5.1538 - int done = 0; /* stores if latitude and/or longitude was read */ 5.1539 - pgl_circle *circle; /* return value (to be palloc'ed) */ 5.1540 - /* demand three blocks separated by whitespace */ 5.1541 - sscanf(strptr, " %*s %*s %*s %n", &valid); 5.1542 - /* if three blocks separated by whitespace exist, parse those blocks */ 5.1543 - if (strptr[valid] == 0) { 5.1544 - /* parse latitude and longitude */ 5.1545 - done |= pgl_scan(&strptr, &lat, &lon); 5.1546 - done |= pgl_scan(&strptr, &lat, &lon); 5.1547 - /* parse radius (while incrementing strptr by number of bytes parsed) */ 5.1548 - valid = 0; 5.1549 - if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid; 5.1550 - } 5.1551 - /* require end of string and both latitude and longitude being parsed */ 5.1552 - if (strptr[0] || done != PGL_SCAN_LATLON) { 5.1553 - ereport(ERROR, ( 5.1554 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 5.1555 - errmsg("invalid input syntax for type ecircle: \"%s\"", str) 5.1556 - )); 5.1557 - } 5.1558 - /* allocate memory for result */ 5.1559 - circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 5.1560 - /* set latitude, longitude, radius (and perform checks) */ 5.1561 - pgl_ecircle_set_latlon_radius(circle, lat, lon, radius); 5.1562 - /* return result */ 5.1563 - PG_RETURN_POINTER(circle); 5.1564 -} 5.1565 - 5.1566 -/* parse cluster ("ecluster" in SQL) */ 5.1567 -PG_FUNCTION_INFO_V1(pgl_ecluster_in); 5.1568 -Datum pgl_ecluster_in(PG_FUNCTION_ARGS) { 5.1569 - int i; 5.1570 - char *str = PG_GETARG_CSTRING(0); /* input string */ 5.1571 - char *str_lower; /* lower case version of input string */ 5.1572 - char *strptr; /* pointer to current reading position of input */ 5.1573 - int npoints_total = 0; /* total number of points in cluster */ 5.1574 - int nentries = 0; /* total number of entries */ 5.1575 - pgl_newentry *entries; /* array of pgl_newentry to create pgl_cluster */ 5.1576 - int entries_buflen = 4; /* maximum number of elements in entries array */ 5.1577 - int valid; /* number of valid chars processed */ 5.1578 - double lat, lon; /* latitude and longitude of parsed point */ 5.1579 - int entrytype; /* current entry type */ 5.1580 - int npoints; /* number of points in current entry */ 5.1581 - pgl_point *points; /* array of pgl_point for pgl_newentry */ 5.1582 - int points_buflen; /* maximum number of elements in points array */ 5.1583 - int done; /* return value of pgl_scan function */ 5.1584 - pgl_cluster *cluster; /* created cluster */ 5.1585 - /* lowercase input */ 5.1586 - str_lower = psprintf("%s", str); 5.1587 - for (strptr=str_lower; *strptr; strptr++) { 5.1588 - if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A'; 5.1589 - } 5.1590 - /* reset reading position to start of (lowercase) string */ 5.1591 - strptr = str_lower; 5.1592 - /* allocate initial buffer for entries */ 5.1593 - entries = palloc(entries_buflen * sizeof(pgl_newentry)); 5.1594 - /* parse until end of string */ 5.1595 - while (strptr[0]) { 5.1596 - /* require previous white-space or closing parenthesis before next token */ 5.1597 - if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') { 5.1598 - goto pgl_ecluster_in_error; 5.1599 - } 5.1600 - /* ignore token "empty" */ 5.1601 - valid = 0; sscanf(strptr, " empty %n", &valid); 5.1602 - if (valid) { strptr += valid; continue; } 5.1603 - /* test for "point" token */ 5.1604 - valid = 0; sscanf(strptr, " point ( %n", &valid); 5.1605 - if (valid) { 5.1606 - strptr += valid; 5.1607 - entrytype = PGL_ENTRY_POINT; 5.1608 - goto pgl_ecluster_in_type_ok; 5.1609 - } 5.1610 - /* test for "path" token */ 5.1611 - valid = 0; sscanf(strptr, " path ( %n", &valid); 5.1612 - if (valid) { 5.1613 - strptr += valid; 5.1614 - entrytype = PGL_ENTRY_PATH; 5.1615 - goto pgl_ecluster_in_type_ok; 5.1616 - } 5.1617 - /* test for "outline" token */ 5.1618 - valid = 0; sscanf(strptr, " outline ( %n", &valid); 5.1619 - if (valid) { 5.1620 - strptr += valid; 5.1621 - entrytype = PGL_ENTRY_OUTLINE; 5.1622 - goto pgl_ecluster_in_type_ok; 5.1623 - } 5.1624 - /* test for "polygon" token */ 5.1625 - valid = 0; sscanf(strptr, " polygon ( %n", &valid); 5.1626 - if (valid) { 5.1627 - strptr += valid; 5.1628 - entrytype = PGL_ENTRY_POLYGON; 5.1629 - goto pgl_ecluster_in_type_ok; 5.1630 - } 5.1631 - /* error if no valid token found */ 5.1632 - goto pgl_ecluster_in_error; 5.1633 - pgl_ecluster_in_type_ok: 5.1634 - /* check if pgl_newentry array needs to grow */ 5.1635 - if (nentries == entries_buflen) { 5.1636 - pgl_newentry *newbuf; 5.1637 - entries_buflen *= 2; 5.1638 - newbuf = palloc(entries_buflen * sizeof(pgl_newentry)); 5.1639 - memcpy(newbuf, entries, nentries * sizeof(pgl_newentry)); 5.1640 - pfree(entries); 5.1641 - entries = newbuf; 5.1642 - } 5.1643 - /* reset number of points for current entry */ 5.1644 - npoints = 0; 5.1645 - /* allocate array for points */ 5.1646 - points_buflen = 4; 5.1647 - points = palloc(points_buflen * sizeof(pgl_point)); 5.1648 - /* parse until closing parenthesis */ 5.1649 - while (strptr[0] != ')') { 5.1650 - /* error on unexpected end of string */ 5.1651 - if (strptr[0] == 0) goto pgl_ecluster_in_error; 5.1652 - /* mark neither latitude nor longitude as read */ 5.1653 - done = PGL_SCAN_NONE; 5.1654 - /* require white-space before second, third, etc. point */ 5.1655 - if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error; 5.1656 - /* scan latitude (or longitude) */ 5.1657 - done |= pgl_scan(&strptr, &lat, &lon); 5.1658 - /* require white-space before second coordinate */ 5.1659 - if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error; 5.1660 - /* scan longitude (or latitude) */ 5.1661 - done |= pgl_scan(&strptr, &lat, &lon); 5.1662 - /* error unless both latitude and longitude were parsed */ 5.1663 - if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error; 5.1664 - /* throw error if number of points is too high */ 5.1665 - if (npoints_total == PGL_CLUSTER_MAXPOINTS) { 5.1666 - ereport(ERROR, ( 5.1667 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 5.1668 - errmsg( 5.1669 - "too many points for ecluster entry (maximum %i)", 5.1670 - PGL_CLUSTER_MAXPOINTS 5.1671 - ) 5.1672 - )); 5.1673 - } 5.1674 - /* check if pgl_point array needs to grow */ 5.1675 - if (npoints == points_buflen) { 5.1676 - pgl_point *newbuf; 5.1677 - points_buflen *= 2; 5.1678 - newbuf = palloc(points_buflen * sizeof(pgl_point)); 5.1679 - memcpy(newbuf, points, npoints * sizeof(pgl_point)); 5.1680 - pfree(points); 5.1681 - points = newbuf; 5.1682 - } 5.1683 - /* append point to pgl_point array (includes checks) */ 5.1684 - pgl_epoint_set_latlon(&(points[npoints++]), lat, lon); 5.1685 - /* increase total number of points */ 5.1686 - npoints_total++; 5.1687 - } 5.1688 - /* error if entry has no points */ 5.1689 - if (!npoints) goto pgl_ecluster_in_error; 5.1690 - /* entries with one point are automatically of type "point" */ 5.1691 - if (npoints == 1) entrytype = PGL_ENTRY_POINT; 5.1692 - /* if entries have more than one point */ 5.1693 - else { 5.1694 - /* throw error if entry type is "point" */ 5.1695 - if (entrytype == PGL_ENTRY_POINT) { 5.1696 - ereport(ERROR, ( 5.1697 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 5.1698 - errmsg("invalid input syntax for type ecluster (point entry with more than one point)") 5.1699 - )); 5.1700 - } 5.1701 - /* coerce outlines and polygons with more than 2 points to be a path */ 5.1702 - if (npoints == 2) entrytype = PGL_ENTRY_PATH; 5.1703 - } 5.1704 - /* append entry to pgl_newentry array */ 5.1705 - entries[nentries].entrytype = entrytype; 5.1706 - entries[nentries].npoints = npoints; 5.1707 - entries[nentries].points = points; 5.1708 - nentries++; 5.1709 - /* consume closing parenthesis */ 5.1710 - strptr++; 5.1711 - /* consume white-space */ 5.1712 - while (isspace(strptr[0])) strptr++; 5.1713 - } 5.1714 - /* free lower case string */ 5.1715 - pfree(str_lower); 5.1716 - /* create cluster from pgl_newentry array */ 5.1717 - cluster = pgl_new_cluster(nentries, entries); 5.1718 - /* free pgl_newentry array */ 5.1719 - for (i=0; i<nentries; i++) pfree(entries[i].points); 5.1720 - pfree(entries); 5.1721 - /* set bounding circle of cluster and check east/west orientation */ 5.1722 - if (!pgl_finalize_cluster(cluster)) { 5.1723 - ereport(ERROR, ( 5.1724 - errcode(ERRCODE_DATA_EXCEPTION), 5.1725 - errmsg("can not determine east/west orientation for ecluster"), 5.1726 - errhint("Ensure that each entry has a longitude span of less than 180 degrees.") 5.1727 - )); 5.1728 - } 5.1729 - /* return cluster */ 5.1730 - PG_RETURN_POINTER(cluster); 5.1731 - /* code to throw error */ 5.1732 - pgl_ecluster_in_error: 5.1733 - ereport(ERROR, ( 5.1734 - errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 5.1735 - errmsg("invalid input syntax for type ecluster: \"%s\"", str) 5.1736 - )); 5.1737 -} 5.1738 - 5.1739 -/* convert point ("epoint") to string representation */ 5.1740 -PG_FUNCTION_INFO_V1(pgl_epoint_out); 5.1741 -Datum pgl_epoint_out(PG_FUNCTION_ARGS) { 5.1742 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.1743 - char latstr[PGL_NUMBUFLEN]; 5.1744 - char lonstr[PGL_NUMBUFLEN]; 5.1745 - pgl_print_lat(latstr, point->lat); 5.1746 - pgl_print_lon(lonstr, point->lon); 5.1747 - PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr)); 5.1748 -} 5.1749 - 5.1750 -/* convert box ("ebox") to string representation */ 5.1751 -PG_FUNCTION_INFO_V1(pgl_ebox_out); 5.1752 -Datum pgl_ebox_out(PG_FUNCTION_ARGS) { 5.1753 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 5.1754 - double lon_min = box->lon_min; 5.1755 - double lon_max = box->lon_max; 5.1756 - char lat_min_str[PGL_NUMBUFLEN]; 5.1757 - char lat_max_str[PGL_NUMBUFLEN]; 5.1758 - char lon_min_str[PGL_NUMBUFLEN]; 5.1759 - char lon_max_str[PGL_NUMBUFLEN]; 5.1760 - /* return string "empty" if box is set to be empty */ 5.1761 - if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty"); 5.1762 - /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */ 5.1763 - /* (required since pgl_box_in orders the longitude boundaries) */ 5.1764 - if (lon_min > lon_max) { 5.1765 - if (lon_min + lon_max >= 0) lon_min -= 360; 5.1766 - else lon_max += 360; 5.1767 - } 5.1768 - /* format and return result */ 5.1769 - pgl_print_lat(lat_min_str, box->lat_min); 5.1770 - pgl_print_lat(lat_max_str, box->lat_max); 5.1771 - pgl_print_lon(lon_min_str, lon_min); 5.1772 - pgl_print_lon(lon_max_str, lon_max); 5.1773 - PG_RETURN_CSTRING(psprintf( 5.1774 - "%s %s %s %s", 5.1775 - lat_min_str, lon_min_str, lat_max_str, lon_max_str 5.1776 - )); 5.1777 -} 5.1778 - 5.1779 -/* convert circle ("ecircle") to string representation */ 5.1780 -PG_FUNCTION_INFO_V1(pgl_ecircle_out); 5.1781 -Datum pgl_ecircle_out(PG_FUNCTION_ARGS) { 5.1782 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 5.1783 - char latstr[PGL_NUMBUFLEN]; 5.1784 - char lonstr[PGL_NUMBUFLEN]; 5.1785 - char radstr[PGL_NUMBUFLEN]; 5.1786 - pgl_print_lat(latstr, circle->center.lat); 5.1787 - pgl_print_lon(lonstr, circle->center.lon); 5.1788 - pgl_print_float(radstr, circle->radius); 5.1789 - PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr)); 5.1790 -} 5.1791 - 5.1792 -/* convert cluster ("ecluster") to string representation */ 5.1793 -PG_FUNCTION_INFO_V1(pgl_ecluster_out); 5.1794 -Datum pgl_ecluster_out(PG_FUNCTION_ARGS) { 5.1795 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 5.1796 - char latstr[PGL_NUMBUFLEN]; /* string buffer for latitude */ 5.1797 - char lonstr[PGL_NUMBUFLEN]; /* string buffer for longitude */ 5.1798 - char ***strings; /* array of array of strings */ 5.1799 - char *string; /* string of current token */ 5.1800 - char *res, *resptr; /* result and pointer to current write position */ 5.1801 - size_t reslen = 1; /* length of result (init with 1 for terminator) */ 5.1802 - int npoints; /* number of points of current entry */ 5.1803 - int i, j; /* i: entry, j: point in entry */ 5.1804 - /* handle empty clusters */ 5.1805 - if (cluster->nentries == 0) { 5.1806 - /* free detoasted cluster (if copy) */ 5.1807 - PG_FREE_IF_COPY(cluster, 0); 5.1808 - /* return static result */ 5.1809 - PG_RETURN_CSTRING("empty"); 5.1810 - } 5.1811 - /* allocate array of array of strings */ 5.1812 - strings = palloc(cluster->nentries * sizeof(char **)); 5.1813 - /* iterate over all entries in cluster */ 5.1814 - for (i=0; i<cluster->nentries; i++) { 5.1815 - /* get number of points in entry */ 5.1816 - npoints = cluster->entries[i].npoints; 5.1817 - /* allocate array of strings (one string for each point plus two extra) */ 5.1818 - strings[i] = palloc((2 + npoints) * sizeof(char *)); 5.1819 - /* determine opening string */ 5.1820 - switch (cluster->entries[i].entrytype) { 5.1821 - case PGL_ENTRY_POINT: string = (i==0)?"point (" :" point ("; break; 5.1822 - case PGL_ENTRY_PATH: string = (i==0)?"path (" :" path ("; break; 5.1823 - case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break; 5.1824 - case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break; 5.1825 - default: string = (i==0)?"unknown" :" unknown"; 5.1826 - } 5.1827 - /* use opening string as first string in array */ 5.1828 - strings[i][0] = string; 5.1829 - /* update result length (for allocating result string later) */ 5.1830 - reslen += strlen(string); 5.1831 - /* iterate over all points */ 5.1832 - for (j=0; j<npoints; j++) { 5.1833 - /* create string representation of point */ 5.1834 - pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat); 5.1835 - pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon); 5.1836 - string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr); 5.1837 - /* copy string pointer to string array */ 5.1838 - strings[i][j+1] = string; 5.1839 - /* update result length (for allocating result string later) */ 5.1840 - reslen += strlen(string); 5.1841 - } 5.1842 - /* use closing parenthesis as last string in array */ 5.1843 - strings[i][npoints+1] = ")"; 5.1844 - /* update result length (for allocating result string later) */ 5.1845 - reslen++; 5.1846 - } 5.1847 - /* allocate result string */ 5.1848 - res = palloc(reslen); 5.1849 - /* set write pointer to begin of result string */ 5.1850 - resptr = res; 5.1851 - /* copy strings into result string */ 5.1852 - for (i=0; i<cluster->nentries; i++) { 5.1853 - npoints = cluster->entries[i].npoints; 5.1854 - for (j=0; j<npoints+2; j++) { 5.1855 - string = strings[i][j]; 5.1856 - strcpy(resptr, string); 5.1857 - resptr += strlen(string); 5.1858 - /* free strings allocated by psprintf */ 5.1859 - if (j != 0 && j != npoints+1) pfree(string); 5.1860 - } 5.1861 - /* free array of strings */ 5.1862 - pfree(strings[i]); 5.1863 - } 5.1864 - /* free array of array of strings */ 5.1865 - pfree(strings); 5.1866 - /* free detoasted cluster (if copy) */ 5.1867 - PG_FREE_IF_COPY(cluster, 0); 5.1868 - /* return result */ 5.1869 - PG_RETURN_CSTRING(res); 5.1870 -} 5.1871 - 5.1872 -/* binary input function for point ("epoint") */ 5.1873 -PG_FUNCTION_INFO_V1(pgl_epoint_recv); 5.1874 -Datum pgl_epoint_recv(PG_FUNCTION_ARGS) { 5.1875 - StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 5.1876 - pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point)); 5.1877 - point->lat = pq_getmsgfloat8(buf); 5.1878 - point->lon = pq_getmsgfloat8(buf); 5.1879 - PG_RETURN_POINTER(point); 5.1880 -} 5.1881 - 5.1882 -/* binary input function for box ("ebox") */ 5.1883 -PG_FUNCTION_INFO_V1(pgl_ebox_recv); 5.1884 -Datum pgl_ebox_recv(PG_FUNCTION_ARGS) { 5.1885 - StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 5.1886 - pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 5.1887 - box->lat_min = pq_getmsgfloat8(buf); 5.1888 - box->lat_max = pq_getmsgfloat8(buf); 5.1889 - box->lon_min = pq_getmsgfloat8(buf); 5.1890 - box->lon_max = pq_getmsgfloat8(buf); 5.1891 - PG_RETURN_POINTER(box); 5.1892 -} 5.1893 - 5.1894 -/* binary input function for circle ("ecircle") */ 5.1895 -PG_FUNCTION_INFO_V1(pgl_ecircle_recv); 5.1896 -Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) { 5.1897 - StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 5.1898 - pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 5.1899 - circle->center.lat = pq_getmsgfloat8(buf); 5.1900 - circle->center.lon = pq_getmsgfloat8(buf); 5.1901 - circle->radius = pq_getmsgfloat8(buf); 5.1902 - PG_RETURN_POINTER(circle); 5.1903 -} 5.1904 - 5.1905 -/* TODO: binary receive function for cluster */ 5.1906 - 5.1907 -/* binary output function for point ("epoint") */ 5.1908 -PG_FUNCTION_INFO_V1(pgl_epoint_send); 5.1909 -Datum pgl_epoint_send(PG_FUNCTION_ARGS) { 5.1910 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.1911 - StringInfoData buf; 5.1912 - pq_begintypsend(&buf); 5.1913 - pq_sendfloat8(&buf, point->lat); 5.1914 - pq_sendfloat8(&buf, point->lon); 5.1915 - PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 5.1916 -} 5.1917 - 5.1918 -/* binary output function for box ("ebox") */ 5.1919 -PG_FUNCTION_INFO_V1(pgl_ebox_send); 5.1920 -Datum pgl_ebox_send(PG_FUNCTION_ARGS) { 5.1921 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 5.1922 - StringInfoData buf; 5.1923 - pq_begintypsend(&buf); 5.1924 - pq_sendfloat8(&buf, box->lat_min); 5.1925 - pq_sendfloat8(&buf, box->lat_max); 5.1926 - pq_sendfloat8(&buf, box->lon_min); 5.1927 - pq_sendfloat8(&buf, box->lon_max); 5.1928 - PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 5.1929 -} 5.1930 - 5.1931 -/* binary output function for circle ("ecircle") */ 5.1932 -PG_FUNCTION_INFO_V1(pgl_ecircle_send); 5.1933 -Datum pgl_ecircle_send(PG_FUNCTION_ARGS) { 5.1934 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 5.1935 - StringInfoData buf; 5.1936 - pq_begintypsend(&buf); 5.1937 - pq_sendfloat8(&buf, circle->center.lat); 5.1938 - pq_sendfloat8(&buf, circle->center.lon); 5.1939 - pq_sendfloat8(&buf, circle->radius); 5.1940 - PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 5.1941 -} 5.1942 - 5.1943 -/* TODO: binary send functions for cluster */ 5.1944 - 5.1945 -/* cast point ("epoint") to box ("ebox") */ 5.1946 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox); 5.1947 -Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) { 5.1948 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.1949 - pgl_box *box = palloc(sizeof(pgl_box)); 5.1950 - box->lat_min = point->lat; 5.1951 - box->lat_max = point->lat; 5.1952 - box->lon_min = point->lon; 5.1953 - box->lon_max = point->lon; 5.1954 - PG_RETURN_POINTER(box); 5.1955 -} 5.1956 - 5.1957 -/* cast point ("epoint") to circle ("ecircle") */ 5.1958 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle); 5.1959 -Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) { 5.1960 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.1961 - pgl_circle *circle = palloc(sizeof(pgl_box)); 5.1962 - circle->center = *point; 5.1963 - circle->radius = 0; 5.1964 - PG_RETURN_POINTER(circle); 5.1965 -} 5.1966 - 5.1967 -/* cast point ("epoint") to cluster ("ecluster") */ 5.1968 -PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster); 5.1969 -Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) { 5.1970 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.1971 - pgl_newentry entry; 5.1972 - entry.entrytype = PGL_ENTRY_POINT; 5.1973 - entry.npoints = 1; 5.1974 - entry.points = point; 5.1975 - PG_RETURN_POINTER(pgl_new_cluster(1, &entry)); 5.1976 -} 5.1977 - 5.1978 -/* cast box ("ebox") to cluster ("ecluster") */ 5.1979 -#define pgl_ebox_to_ecluster_macro(i, a, b) \ 5.1980 - entries[i].entrytype = PGL_ENTRY_POLYGON; \ 5.1981 - entries[i].npoints = 4; \ 5.1982 - entries[i].points = points[i]; \ 5.1983 - points[i][0].lat = box->lat_min; \ 5.1984 - points[i][0].lon = (a); \ 5.1985 - points[i][1].lat = box->lat_min; \ 5.1986 - points[i][1].lon = (b); \ 5.1987 - points[i][2].lat = box->lat_max; \ 5.1988 - points[i][2].lon = (b); \ 5.1989 - points[i][3].lat = box->lat_max; \ 5.1990 - points[i][3].lon = (a); 5.1991 -PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster); 5.1992 -Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) { 5.1993 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 5.1994 - double lon, dlon; 5.1995 - int nentries; 5.1996 - pgl_newentry entries[3]; 5.1997 - pgl_point points[3][4]; 5.1998 - if (box->lat_min > box->lat_max) { 5.1999 - nentries = 0; 5.2000 - } else if (box->lon_min > box->lon_max) { 5.2001 - if (box->lon_min < 0) { 5.2002 - lon = pgl_round((box->lon_min + 180) / 2.0); 5.2003 - nentries = 3; 5.2004 - pgl_ebox_to_ecluster_macro(0, box->lon_min, lon); 5.2005 - pgl_ebox_to_ecluster_macro(1, lon, 180); 5.2006 - pgl_ebox_to_ecluster_macro(2, -180, box->lon_max); 5.2007 - } else if (box->lon_max > 0) { 5.2008 - lon = pgl_round((box->lon_max - 180) / 2.0); 5.2009 - nentries = 3; 5.2010 - pgl_ebox_to_ecluster_macro(0, box->lon_min, 180); 5.2011 - pgl_ebox_to_ecluster_macro(1, -180, lon); 5.2012 - pgl_ebox_to_ecluster_macro(2, lon, box->lon_max); 5.2013 - } else { 5.2014 - nentries = 2; 5.2015 - pgl_ebox_to_ecluster_macro(0, box->lon_min, 180); 5.2016 - pgl_ebox_to_ecluster_macro(1, -180, box->lon_max); 5.2017 - } 5.2018 - } else { 5.2019 - dlon = pgl_round(box->lon_max - box->lon_min); 5.2020 - if (dlon < 180) { 5.2021 - nentries = 1; 5.2022 - pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max); 5.2023 - } else { 5.2024 - lon = pgl_round((box->lon_min + box->lon_max) / 2.0); 5.2025 - if ( 5.2026 - pgl_round(lon - box->lon_min) < 180 && 5.2027 - pgl_round(box->lon_max - lon) < 180 5.2028 - ) { 5.2029 - nentries = 2; 5.2030 - pgl_ebox_to_ecluster_macro(0, box->lon_min, lon); 5.2031 - pgl_ebox_to_ecluster_macro(1, lon, box->lon_max); 5.2032 - } else { 5.2033 - nentries = 3; 5.2034 - pgl_ebox_to_ecluster_macro(0, box->lon_min, -60); 5.2035 - pgl_ebox_to_ecluster_macro(1, -60, 60); 5.2036 - pgl_ebox_to_ecluster_macro(2, 60, box->lon_max); 5.2037 - } 5.2038 - } 5.2039 - } 5.2040 - PG_RETURN_POINTER(pgl_new_cluster(nentries, entries)); 5.2041 -} 5.2042 - 5.2043 -/* extract latitude from point ("epoint") */ 5.2044 -PG_FUNCTION_INFO_V1(pgl_epoint_lat); 5.2045 -Datum pgl_epoint_lat(PG_FUNCTION_ARGS) { 5.2046 - PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat); 5.2047 -} 5.2048 - 5.2049 -/* extract longitude from point ("epoint") */ 5.2050 -PG_FUNCTION_INFO_V1(pgl_epoint_lon); 5.2051 -Datum pgl_epoint_lon(PG_FUNCTION_ARGS) { 5.2052 - PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon); 5.2053 -} 5.2054 - 5.2055 -/* extract minimum latitude from box ("ebox") */ 5.2056 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_min); 5.2057 -Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) { 5.2058 - PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min); 5.2059 -} 5.2060 - 5.2061 -/* extract maximum latitude from box ("ebox") */ 5.2062 -PG_FUNCTION_INFO_V1(pgl_ebox_lat_max); 5.2063 -Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) { 5.2064 - PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max); 5.2065 -} 5.2066 - 5.2067 -/* extract minimum longitude from box ("ebox") */ 5.2068 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_min); 5.2069 -Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) { 5.2070 - PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min); 5.2071 -} 5.2072 - 5.2073 -/* extract maximum longitude from box ("ebox") */ 5.2074 -PG_FUNCTION_INFO_V1(pgl_ebox_lon_max); 5.2075 -Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) { 5.2076 - PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max); 5.2077 -} 5.2078 - 5.2079 -/* extract center point from circle ("ecircle") */ 5.2080 -PG_FUNCTION_INFO_V1(pgl_ecircle_center); 5.2081 -Datum pgl_ecircle_center(PG_FUNCTION_ARGS) { 5.2082 - PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center)); 5.2083 -} 5.2084 - 5.2085 -/* extract radius from circle ("ecircle") */ 5.2086 -PG_FUNCTION_INFO_V1(pgl_ecircle_radius); 5.2087 -Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) { 5.2088 - PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius); 5.2089 -} 5.2090 - 5.2091 -/* check if point is inside box (overlap operator "&&") in SQL */ 5.2092 -PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap); 5.2093 -Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) { 5.2094 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.2095 - pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1); 5.2096 - PG_RETURN_BOOL(pgl_point_in_box(point, box)); 5.2097 -} 5.2098 - 5.2099 -/* check if point is inside circle (overlap operator "&&") in SQL */ 5.2100 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap); 5.2101 -Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) { 5.2102 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.2103 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 5.2104 - PG_RETURN_BOOL( 5.2105 - pgl_distance( 5.2106 - point->lat, point->lon, 5.2107 - circle->center.lat, circle->center.lon 5.2108 - ) <= circle->radius 5.2109 - ); 5.2110 -} 5.2111 - 5.2112 -/* check if point is inside cluster (overlap operator "&&") in SQL */ 5.2113 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap); 5.2114 -Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) { 5.2115 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.2116 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 5.2117 - bool retval = pgl_point_in_cluster(point, cluster); 5.2118 - PG_FREE_IF_COPY(cluster, 1); 5.2119 - PG_RETURN_BOOL(retval); 5.2120 -} 5.2121 - 5.2122 -/* check if two boxes overlap (overlap operator "&&") in SQL */ 5.2123 -PG_FUNCTION_INFO_V1(pgl_ebox_overlap); 5.2124 -Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) { 5.2125 - pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0); 5.2126 - pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1); 5.2127 - PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2)); 5.2128 -} 5.2129 - 5.2130 -/* check if two circles overlap (overlap operator "&&") in SQL */ 5.2131 -PG_FUNCTION_INFO_V1(pgl_ecircle_overlap); 5.2132 -Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) { 5.2133 - pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0); 5.2134 - pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1); 5.2135 - PG_RETURN_BOOL( 5.2136 - pgl_distance( 5.2137 - circle1->center.lat, circle1->center.lon, 5.2138 - circle2->center.lat, circle2->center.lon 5.2139 - ) <= circle1->radius + circle2->radius 5.2140 - ); 5.2141 -} 5.2142 - 5.2143 -/* check if circle and cluster overlap (overlap operator "&&") in SQL */ 5.2144 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap); 5.2145 -Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) { 5.2146 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 5.2147 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 5.2148 - bool retval = ( 5.2149 - pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius 5.2150 - ); 5.2151 - PG_FREE_IF_COPY(cluster, 1); 5.2152 - PG_RETURN_BOOL(retval); 5.2153 -} 5.2154 - 5.2155 -/* calculate distance between two points ("<->" operator) in SQL */ 5.2156 -PG_FUNCTION_INFO_V1(pgl_epoint_distance); 5.2157 -Datum pgl_epoint_distance(PG_FUNCTION_ARGS) { 5.2158 - pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0); 5.2159 - pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1); 5.2160 - PG_RETURN_FLOAT8(pgl_distance( 5.2161 - point1->lat, point1->lon, point2->lat, point2->lon 5.2162 - )); 5.2163 -} 5.2164 - 5.2165 -/* calculate point to circle distance ("<->" operator) in SQL */ 5.2166 -PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance); 5.2167 -Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) { 5.2168 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.2169 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 5.2170 - double distance = pgl_distance( 5.2171 - point->lat, point->lon, circle->center.lat, circle->center.lon 5.2172 - ) - circle->radius; 5.2173 - PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 5.2174 -} 5.2175 - 5.2176 -/* calculate point to cluster distance ("<->" operator) in SQL */ 5.2177 -PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance); 5.2178 -Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) { 5.2179 - pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 5.2180 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 5.2181 - double distance = pgl_point_cluster_distance(point, cluster); 5.2182 - PG_FREE_IF_COPY(cluster, 1); 5.2183 - PG_RETURN_FLOAT8(distance); 5.2184 -} 5.2185 - 5.2186 -/* calculate distance between two circles ("<->" operator) in SQL */ 5.2187 -PG_FUNCTION_INFO_V1(pgl_ecircle_distance); 5.2188 -Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) { 5.2189 - pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0); 5.2190 - pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1); 5.2191 - double distance = pgl_distance( 5.2192 - circle1->center.lat, circle1->center.lon, 5.2193 - circle2->center.lat, circle2->center.lon 5.2194 - ) - (circle1->radius + circle2->radius); 5.2195 - PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 5.2196 -} 5.2197 - 5.2198 -/* calculate circle to cluster distance ("<->" operator) in SQL */ 5.2199 -PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance); 5.2200 -Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) { 5.2201 - pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 5.2202 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 5.2203 - double distance = ( 5.2204 - pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius 5.2205 - ); 5.2206 - PG_FREE_IF_COPY(cluster, 1); 5.2207 - PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 5.2208 -} 5.2209 - 5.2210 - 5.2211 -/*-----------------------------------------------------------* 5.2212 - * B-tree comparison operators and index support functions * 5.2213 - *-----------------------------------------------------------*/ 5.2214 - 5.2215 -/* macro for a B-tree operator (without detoasting) */ 5.2216 -#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \ 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_BOOL(cmpfunc(a, b) oper 0); \ 5.2222 - } 5.2223 - 5.2224 -/* macro for a B-tree comparison function (without detoasting) */ 5.2225 -#define PGL_BTREE_CMP(func, type, cmpfunc) \ 5.2226 - PG_FUNCTION_INFO_V1(func); \ 5.2227 - Datum func(PG_FUNCTION_ARGS) { \ 5.2228 - type *a = (type *)PG_GETARG_POINTER(0); \ 5.2229 - type *b = (type *)PG_GETARG_POINTER(1); \ 5.2230 - PG_RETURN_INT32(cmpfunc(a, b)); \ 5.2231 - } 5.2232 - 5.2233 -/* macro for a B-tree operator (with detoasting) */ 5.2234 -#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \ 5.2235 - PG_FUNCTION_INFO_V1(func); \ 5.2236 - Datum func(PG_FUNCTION_ARGS) { \ 5.2237 - bool res; \ 5.2238 - type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \ 5.2239 - type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \ 5.2240 - res = cmpfunc(a, b) oper 0; \ 5.2241 - PG_FREE_IF_COPY(a, 0); \ 5.2242 - PG_FREE_IF_COPY(b, 1); \ 5.2243 - PG_RETURN_BOOL(res); \ 5.2244 - } 5.2245 - 5.2246 -/* macro for a B-tree comparison function (with detoasting) */ 5.2247 -#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \ 5.2248 - PG_FUNCTION_INFO_V1(func); \ 5.2249 - Datum func(PG_FUNCTION_ARGS) { \ 5.2250 - int32_t res; \ 5.2251 - type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \ 5.2252 - type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \ 5.2253 - res = cmpfunc(a, b); \ 5.2254 - PG_FREE_IF_COPY(a, 0); \ 5.2255 - PG_FREE_IF_COPY(b, 1); \ 5.2256 - PG_RETURN_INT32(res); \ 5.2257 - } 5.2258 - 5.2259 -/* B-tree operators and comparison function for point */ 5.2260 -PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <) 5.2261 -PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=) 5.2262 -PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==) 5.2263 -PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=) 5.2264 -PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=) 5.2265 -PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >) 5.2266 -PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp) 5.2267 - 5.2268 -/* B-tree operators and comparison function for box */ 5.2269 -PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <) 5.2270 -PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=) 5.2271 -PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==) 5.2272 -PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=) 5.2273 -PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=) 5.2274 -PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >) 5.2275 -PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp) 5.2276 - 5.2277 -/* B-tree operators and comparison function for circle */ 5.2278 -PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <) 5.2279 -PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=) 5.2280 -PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==) 5.2281 -PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=) 5.2282 -PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=) 5.2283 -PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >) 5.2284 -PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp) 5.2285 - 5.2286 - 5.2287 -/*--------------------------------* 5.2288 - * GiST index support functions * 5.2289 - *--------------------------------*/ 5.2290 - 5.2291 -/* GiST "consistent" support function */ 5.2292 -PG_FUNCTION_INFO_V1(pgl_gist_consistent); 5.2293 -Datum pgl_gist_consistent(PG_FUNCTION_ARGS) { 5.2294 - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 5.2295 - pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key); 5.2296 - StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); 5.2297 - bool *recheck = (bool *)PG_GETARG_POINTER(4); 5.2298 - /* demand recheck because index and query methods are lossy */ 5.2299 - *recheck = true; 5.2300 - /* strategy number 11: equality of two points */ 5.2301 - if (strategy == 11) { 5.2302 - /* query datum is another point */ 5.2303 - pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 5.2304 - /* convert other point to key */ 5.2305 - pgl_pointkey querykey; 5.2306 - pgl_point_to_key(query, querykey); 5.2307 - /* return true if both keys overlap */ 5.2308 - PG_RETURN_BOOL(pgl_keys_overlap(key, querykey)); 5.2309 - } 5.2310 - /* strategy number 13: equality of two circles */ 5.2311 - if (strategy == 13) { 5.2312 - /* query datum is another circle */ 5.2313 - pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 5.2314 - /* convert other circle to key */ 5.2315 - pgl_areakey querykey; 5.2316 - pgl_circle_to_key(query, querykey); 5.2317 - /* return true if both keys overlap */ 5.2318 - PG_RETURN_BOOL(pgl_keys_overlap(key, querykey)); 5.2319 - } 5.2320 - /* for all remaining strategies, keys on empty objects produce no match */ 5.2321 - /* (check necessary because query radius may be infinite) */ 5.2322 - if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false); 5.2323 - /* strategy number 21: overlapping with point */ 5.2324 - if (strategy == 21) { 5.2325 - /* query datum is a point */ 5.2326 - pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 5.2327 - /* return true if estimated distance (allowed to be smaller than real 5.2328 - distance) between index key and point is zero */ 5.2329 - PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0); 5.2330 - } 5.2331 - /* strategy number 22: (point) overlapping with box */ 5.2332 - if (strategy == 22) { 5.2333 - /* query datum is a box */ 5.2334 - pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1); 5.2335 - /* determine bounding box of indexed key */ 5.2336 - pgl_box keybox; 5.2337 - pgl_key_to_box(key, &keybox); 5.2338 - /* return true if query box overlaps with bounding box of indexed key */ 5.2339 - PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox)); 5.2340 - } 5.2341 - /* strategy number 23: overlapping with circle */ 5.2342 - if (strategy == 23) { 5.2343 - /* query datum is a circle */ 5.2344 - pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 5.2345 - /* return true if estimated distance (allowed to be smaller than real 5.2346 - distance) between index key and circle center is smaller than radius */ 5.2347 - PG_RETURN_BOOL( 5.2348 - pgl_estimate_key_distance(key, &(query->center)) <= query->radius 5.2349 - ); 5.2350 - } 5.2351 - /* strategy number 24: overlapping with cluster */ 5.2352 - if (strategy == 24) { 5.2353 - bool retval; /* return value */ 5.2354 - /* query datum is a cluster */ 5.2355 - pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 5.2356 - /* return true if estimated distance (allowed to be smaller than real 5.2357 - distance) between index key and circle center is smaller than radius */ 5.2358 - retval = ( 5.2359 - pgl_estimate_key_distance(key, &(query->bounding.center)) <= 5.2360 - query->bounding.radius 5.2361 - ); 5.2362 - PG_FREE_IF_COPY(query, 1); /* free detoasted cluster (if copy) */ 5.2363 - PG_RETURN_BOOL(retval); 5.2364 - } 5.2365 - /* throw error for any unknown strategy number */ 5.2366 - elog(ERROR, "unrecognized strategy number: %d", strategy); 5.2367 -} 5.2368 - 5.2369 -/* GiST "union" support function */ 5.2370 -PG_FUNCTION_INFO_V1(pgl_gist_union); 5.2371 -Datum pgl_gist_union(PG_FUNCTION_ARGS) { 5.2372 - GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); 5.2373 - pgl_keyptr out; /* return value (to be palloc'ed) */ 5.2374 - int i; 5.2375 - /* determine key size */ 5.2376 - size_t keysize = PGL_KEY_IS_AREAKEY( 5.2377 - (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key) 5.2378 - ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey); 5.2379 - /* begin with first key as result */ 5.2380 - out = palloc(keysize); 5.2381 - memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize); 5.2382 - /* unite current result with second, third, etc. key */ 5.2383 - for (i=1; i<entryvec->n; i++) { 5.2384 - pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)); 5.2385 - } 5.2386 - /* return result */ 5.2387 - PG_RETURN_POINTER(out); 5.2388 -} 5.2389 - 5.2390 -/* GiST "compress" support function for indicis on points */ 5.2391 -PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint); 5.2392 -Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) { 5.2393 - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 5.2394 - GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 5.2395 - /* only transform new leaves */ 5.2396 - if (entry->leafkey) { 5.2397 - /* get point to be transformed */ 5.2398 - pgl_point *point = (pgl_point *)DatumGetPointer(entry->key); 5.2399 - /* allocate memory for key */ 5.2400 - pgl_keyptr key = palloc(sizeof(pgl_pointkey)); 5.2401 - /* transform point to key */ 5.2402 - pgl_point_to_key(point, key); 5.2403 - /* create new GISTENTRY structure as return value */ 5.2404 - retval = palloc(sizeof(GISTENTRY)); 5.2405 - gistentryinit( 5.2406 - *retval, PointerGetDatum(key), 5.2407 - entry->rel, entry->page, entry->offset, FALSE 5.2408 - ); 5.2409 - } else { 5.2410 - /* inner nodes have already been transformed */ 5.2411 - retval = entry; 5.2412 - } 5.2413 - /* return pointer to old or new GISTENTRY structure */ 5.2414 - PG_RETURN_POINTER(retval); 5.2415 -} 5.2416 - 5.2417 -/* GiST "compress" support function for indicis on circles */ 5.2418 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle); 5.2419 -Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) { 5.2420 - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 5.2421 - GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 5.2422 - /* only transform new leaves */ 5.2423 - if (entry->leafkey) { 5.2424 - /* get circle to be transformed */ 5.2425 - pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key); 5.2426 - /* allocate memory for key */ 5.2427 - pgl_keyptr key = palloc(sizeof(pgl_areakey)); 5.2428 - /* transform circle to key */ 5.2429 - pgl_circle_to_key(circle, key); 5.2430 - /* create new GISTENTRY structure as return value */ 5.2431 - retval = palloc(sizeof(GISTENTRY)); 5.2432 - gistentryinit( 5.2433 - *retval, PointerGetDatum(key), 5.2434 - entry->rel, entry->page, entry->offset, FALSE 5.2435 - ); 5.2436 - } else { 5.2437 - /* inner nodes have already been transformed */ 5.2438 - retval = entry; 5.2439 - } 5.2440 - /* return pointer to old or new GISTENTRY structure */ 5.2441 - PG_RETURN_POINTER(retval); 5.2442 -} 5.2443 - 5.2444 -/* GiST "compress" support function for indices on clusters */ 5.2445 -PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster); 5.2446 -Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) { 5.2447 - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 5.2448 - GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 5.2449 - /* only transform new leaves */ 5.2450 - if (entry->leafkey) { 5.2451 - /* get cluster to be transformed (detoasting necessary!) */ 5.2452 - pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key); 5.2453 - /* allocate memory for key */ 5.2454 - pgl_keyptr key = palloc(sizeof(pgl_areakey)); 5.2455 - /* transform cluster to key */ 5.2456 - pgl_circle_to_key(&(cluster->bounding), key); 5.2457 - /* create new GISTENTRY structure as return value */ 5.2458 - retval = palloc(sizeof(GISTENTRY)); 5.2459 - gistentryinit( 5.2460 - *retval, PointerGetDatum(key), 5.2461 - entry->rel, entry->page, entry->offset, FALSE 5.2462 - ); 5.2463 - /* free detoasted datum */ 5.2464 - if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster); 5.2465 - } else { 5.2466 - /* inner nodes have already been transformed */ 5.2467 - retval = entry; 5.2468 - } 5.2469 - /* return pointer to old or new GISTENTRY structure */ 5.2470 - PG_RETURN_POINTER(retval); 5.2471 -} 5.2472 - 5.2473 -/* GiST "decompress" support function for indices */ 5.2474 -PG_FUNCTION_INFO_V1(pgl_gist_decompress); 5.2475 -Datum pgl_gist_decompress(PG_FUNCTION_ARGS) { 5.2476 - /* return passed pointer without transformation */ 5.2477 - PG_RETURN_POINTER(PG_GETARG_POINTER(0)); 5.2478 -} 5.2479 - 5.2480 -/* GiST "penalty" support function */ 5.2481 -PG_FUNCTION_INFO_V1(pgl_gist_penalty); 5.2482 -Datum pgl_gist_penalty(PG_FUNCTION_ARGS) { 5.2483 - GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0); 5.2484 - GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1); 5.2485 - float *penalty = (float *)PG_GETARG_POINTER(2); 5.2486 - /* get original key and key to insert */ 5.2487 - pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key); 5.2488 - pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key); 5.2489 - /* copy original key */ 5.2490 - union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key; 5.2491 - if (PGL_KEY_IS_AREAKEY(orig)) { 5.2492 - memcpy(union_key.areakey, orig, sizeof(union_key.areakey)); 5.2493 - } else { 5.2494 - memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey)); 5.2495 - } 5.2496 - /* calculate union of both keys */ 5.2497 - pgl_unite_keys((pgl_keyptr)&union_key, new); 5.2498 - /* penalty equal to reduction of key length (logarithm of added area) */ 5.2499 - /* (return value by setting referenced value and returning pointer) */ 5.2500 - *penalty = ( 5.2501 - PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key) 5.2502 - ); 5.2503 - PG_RETURN_POINTER(penalty); 5.2504 -} 5.2505 - 5.2506 -/* GiST "picksplit" support function */ 5.2507 -PG_FUNCTION_INFO_V1(pgl_gist_picksplit); 5.2508 -Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) { 5.2509 - GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); 5.2510 - GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1); 5.2511 - OffsetNumber i; /* between FirstOffsetNumber and entryvec->n (inclusive) */ 5.2512 - union { 5.2513 - pgl_pointkey pointkey; 5.2514 - pgl_areakey areakey; 5.2515 - } union_all; /* union of all keys (to be calculated from scratch) 5.2516 - (later cut in half) */ 5.2517 - int is_areakey = PGL_KEY_IS_AREAKEY( 5.2518 - (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key) 5.2519 - ); 5.2520 - int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey); 5.2521 - pgl_keyptr unionL = palloc(keysize); /* union of keys that go left */ 5.2522 - pgl_keyptr unionR = palloc(keysize); /* union of keys that go right */ 5.2523 - pgl_keyptr key; /* current key to be processed */ 5.2524 - /* allocate memory for array of left and right keys, set counts to zero */ 5.2525 - v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber)); 5.2526 - v->spl_nleft = 0; 5.2527 - v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber)); 5.2528 - v->spl_nright = 0; 5.2529 - /* calculate union of all keys from scratch */ 5.2530 - memcpy( 5.2531 - (pgl_keyptr)&union_all, 5.2532 - (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key), 5.2533 - keysize 5.2534 - ); 5.2535 - for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) { 5.2536 - pgl_unite_keys( 5.2537 - (pgl_keyptr)&union_all, 5.2538 - (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key) 5.2539 - ); 5.2540 - } 5.2541 - /* check if trivial split is necessary due to exhausted key length */ 5.2542 - /* (Note: keys for empty objects must have node depth set to maximum) */ 5.2543 - if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == ( 5.2544 - is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH 5.2545 - )) { 5.2546 - /* half of all keys go left */ 5.2547 - for ( 5.2548 - i=FirstOffsetNumber; 5.2549 - i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2; 5.2550 - i=OffsetNumberNext(i) 5.2551 - ) { 5.2552 - /* pointer to current key */ 5.2553 - key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 5.2554 - /* update unionL */ 5.2555 - /* check if key is first key that goes left */ 5.2556 - if (!v->spl_nleft) { 5.2557 - /* first key that goes left is just copied to unionL */ 5.2558 - memcpy(unionL, key, keysize); 5.2559 - } else { 5.2560 - /* unite current value and next key */ 5.2561 - pgl_unite_keys(unionL, key); 5.2562 - } 5.2563 - /* append offset number to list of keys that go left */ 5.2564 - v->spl_left[v->spl_nleft++] = i; 5.2565 - } 5.2566 - /* other half goes right */ 5.2567 - for ( 5.2568 - i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2; 5.2569 - i<entryvec->n; 5.2570 - i=OffsetNumberNext(i) 5.2571 - ) { 5.2572 - /* pointer to current key */ 5.2573 - key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 5.2574 - /* update unionR */ 5.2575 - /* check if key is first key that goes right */ 5.2576 - if (!v->spl_nright) { 5.2577 - /* first key that goes right is just copied to unionR */ 5.2578 - memcpy(unionR, key, keysize); 5.2579 - } else { 5.2580 - /* unite current value and next key */ 5.2581 - pgl_unite_keys(unionR, key); 5.2582 - } 5.2583 - /* append offset number to list of keys that go right */ 5.2584 - v->spl_right[v->spl_nright++] = i; 5.2585 - } 5.2586 - } 5.2587 - /* otherwise, a non-trivial split is possible */ 5.2588 - else { 5.2589 - /* cut covered area in half */ 5.2590 - /* (union_all then refers to area of keys that go left) */ 5.2591 - /* check if union of all keys covers empty and non-empty objects */ 5.2592 - if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) { 5.2593 - /* if yes, split into empty and non-empty objects */ 5.2594 - pgl_key_set_empty((pgl_keyptr)&union_all); 5.2595 - } else { 5.2596 - /* otherwise split by next bit */ 5.2597 - ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++; 5.2598 - /* NOTE: type bit conserved */ 5.2599 - } 5.2600 - /* determine for each key if it goes left or right */ 5.2601 - for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) { 5.2602 - /* pointer to current key */ 5.2603 - key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 5.2604 - /* keys within one half of the area go left */ 5.2605 - if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) { 5.2606 - /* update unionL */ 5.2607 - /* check if key is first key that goes left */ 5.2608 - if (!v->spl_nleft) { 5.2609 - /* first key that goes left is just copied to unionL */ 5.2610 - memcpy(unionL, key, keysize); 5.2611 - } else { 5.2612 - /* unite current value of unionL and processed key */ 5.2613 - pgl_unite_keys(unionL, key); 5.2614 - } 5.2615 - /* append offset number to list of keys that go left */ 5.2616 - v->spl_left[v->spl_nleft++] = i; 5.2617 - } 5.2618 - /* the other keys go right */ 5.2619 - else { 5.2620 - /* update unionR */ 5.2621 - /* check if key is first key that goes right */ 5.2622 - if (!v->spl_nright) { 5.2623 - /* first key that goes right is just copied to unionR */ 5.2624 - memcpy(unionR, key, keysize); 5.2625 - } else { 5.2626 - /* unite current value of unionR and processed key */ 5.2627 - pgl_unite_keys(unionR, key); 5.2628 - } 5.2629 - /* append offset number to list of keys that go right */ 5.2630 - v->spl_right[v->spl_nright++] = i; 5.2631 - } 5.2632 - } 5.2633 - } 5.2634 - /* store unions in return value */ 5.2635 - v->spl_ldatum = PointerGetDatum(unionL); 5.2636 - v->spl_rdatum = PointerGetDatum(unionR); 5.2637 - /* return all results */ 5.2638 - PG_RETURN_POINTER(v); 5.2639 -} 5.2640 - 5.2641 -/* GiST "same"/"equal" support function */ 5.2642 -PG_FUNCTION_INFO_V1(pgl_gist_same); 5.2643 -Datum pgl_gist_same(PG_FUNCTION_ARGS) { 5.2644 - pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0); 5.2645 - pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1); 5.2646 - bool *boolptr = (bool *)PG_GETARG_POINTER(2); 5.2647 - /* two keys are equal if they are binary equal */ 5.2648 - /* (return result by setting referenced boolean and returning pointer) */ 5.2649 - *boolptr = !memcmp( 5.2650 - key1, 5.2651 - key2, 5.2652 - PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey) 5.2653 - ); 5.2654 - PG_RETURN_POINTER(boolptr); 5.2655 -} 5.2656 - 5.2657 -/* GiST "distance" support function */ 5.2658 -PG_FUNCTION_INFO_V1(pgl_gist_distance); 5.2659 -Datum pgl_gist_distance(PG_FUNCTION_ARGS) { 5.2660 - GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); 5.2661 - pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key); 5.2662 - StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); 5.2663 - bool *recheck = (bool *)PG_GETARG_POINTER(4); 5.2664 - double distance; /* return value */ 5.2665 - /* demand recheck because distance is just an estimation */ 5.2666 - /* (real distance may be bigger) */ 5.2667 - *recheck = true; 5.2668 - /* strategy number 31: distance to point */ 5.2669 - if (strategy == 31) { 5.2670 - /* query datum is a point */ 5.2671 - pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 5.2672 - /* use pgl_estimate_pointkey_distance() function to compute result */ 5.2673 - distance = pgl_estimate_key_distance(key, query); 5.2674 - /* avoid infinity (reserved!) */ 5.2675 - if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 5.2676 - /* return result */ 5.2677 - PG_RETURN_FLOAT8(distance); 5.2678 - } 5.2679 - /* strategy number 33: distance to circle */ 5.2680 - if (strategy == 33) { 5.2681 - /* query datum is a circle */ 5.2682 - pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 5.2683 - /* estimate distance to circle center and substract circle radius */ 5.2684 - distance = ( 5.2685 - pgl_estimate_key_distance(key, &(query->center)) - query->radius 5.2686 - ); 5.2687 - /* convert non-positive values to zero and avoid infinity (reserved!) */ 5.2688 - if (distance <= 0) distance = 0; 5.2689 - else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 5.2690 - /* return result */ 5.2691 - PG_RETURN_FLOAT8(distance); 5.2692 - } 5.2693 - /* strategy number 34: distance to cluster */ 5.2694 - if (strategy == 34) { 5.2695 - /* query datum is a cluster */ 5.2696 - pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 5.2697 - /* estimate distance to bounding center and substract bounding radius */ 5.2698 - distance = ( 5.2699 - pgl_estimate_key_distance(key, &(query->bounding.center)) - 5.2700 - query->bounding.radius 5.2701 - ); 5.2702 - /* convert non-positive values to zero and avoid infinity (reserved!) */ 5.2703 - if (distance <= 0) distance = 0; 5.2704 - else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 5.2705 - /* free detoasted cluster (if copy) */ 5.2706 - PG_FREE_IF_COPY(query, 1); 5.2707 - /* return result */ 5.2708 - PG_RETURN_FLOAT8(distance); 5.2709 - } 5.2710 - /* throw error for any unknown strategy number */ 5.2711 - elog(ERROR, "unrecognized strategy number: %d", strategy); 5.2712 -} 5.2713 -