pgLatLon
changeset 46:3cbce515387f
Added safety margins for distance calculation on index lookups (fixes bug that caused nearest-neighbor search to fail in certain cases); Improved "fair_distance" function
author | jbe |
---|---|
date | Mon Oct 31 13:06:31 2016 +0100 (2016-10-31) |
parents | 10640afbe2ea |
children | 20482d4309b5 |
files | GNUmakefile README.html README.mkd create_test_db.data.sql create_test_db.schema.sql latlon--0.10.sql latlon--0.9--0.10.sql latlon-v0009.c latlon.control |
line diff
1.1 --- a/GNUmakefile Tue Oct 25 22:15:17 2016 +0200 1.2 +++ b/GNUmakefile Mon Oct 31 13:06:31 2016 +0100 1.3 @@ -1,6 +1,6 @@ 1.4 EXTENSION = latlon 1.5 -DATA = latlon--0.9.sql 1.6 -MODULES = latlon-v0008 1.7 +DATA = latlon--0.9.sql latlon--0.9--0.10.sql latlon--0.10.sql 1.8 +MODULES = latlon-v0008 latlon-v0009 1.9 1.10 PG_CONFIG = pg_config 1.11 PGXS := $(shell $(PG_CONFIG) --pgxs)
2.1 --- a/README.html Tue Oct 25 22:15:17 2016 +0200 2.2 +++ b/README.html Mon Oct 31 13:06:31 2016 +0100 2.3 @@ -1,5 +1,5 @@ 2.4 -<html><head><title>pgLatLon v0.9 documentation</title></head><body> 2.5 -<h1>pgLatLon v0.9 documentation</h1> 2.6 +<html><head><title>pgLatLon v0.10 documentation</title></head><body> 2.7 +<h1>pgLatLon v0.10 documentation</h1> 2.8 2.9 <p>pgLatLon is a spatial database extension for the PostgreSQL object-relational 2.10 database management system providing geographic data types and spatial indexing 2.11 @@ -40,8 +40,8 @@ 2.12 <p>It is also possible to compile and install the extension without GNU Make as 2.13 follows:</p> 2.14 2.15 -<pre><code>cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0008.so latlon-v0008.c 2.16 -cp latlon-v0008.so `pg_config --pkglibdir` 2.17 +<pre><code>cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0009.so latlon-v0009.c 2.18 +cp latlon-v0009.so `pg_config --pkglibdir` 2.19 cp latlon.control `pg_config --sharedir`/extension/ 2.20 cp latlon--*.sql `pg_config --sharedir`/extension/ 2.21 </code></pre> 2.22 @@ -502,23 +502,41 @@ 2.23 following properties:</p> 2.24 2.25 <ul> 2.26 -<li>For search points far away from the <code>ecluster</code> (i.e. distance approaching 2.27 -infinity), the penalty approaches zero (i.e. <code>fair_distance</code> behaves the same 2.28 -as <code>distance</code>).</li> 2.29 +<li>The penalty function is continuous (except noise created by numerical 2.30 +integration, see paragraph after this list) as long as no objects are added 2.31 +to or removed from the <code>ecluster</code>. That particularly means: small changes in 2.32 +the search point (second argument) cause only small changes in the result.</li> 2.33 +<li>For search points far away from the <code>ecluster</code> (i.e. large distances compared 2.34 +to the dimensions of the <code>ecluster</code>), the penalty approaches zero, i.e. the 2.35 +behavior of the <code>fair_distance</code> function approaches the behavior of the 2.36 +<code>distance</code> function.</li> 2.37 <li>If the <code>ecluster</code> consists of a set of points, the penalty for a search point 2.38 -close to one of that points (closer than half of the minimum distance between 2.39 -each pair of points in the <code>ecluster</code>) is chosen in such a way that the 2.40 -adjusted distance is equal to the distance from the search point to the 2.41 +close to one of those points (closer than half of the minimum distance 2.42 +between each pair of points in the <code>ecluster</code>) is chosen in such a way that 2.43 +the adjusted distance is equal to the distance from the search point to the 2.44 closest point in the <code>ecluster</code> multiplied by the square root of the count of 2.45 points in the <code>ecluster</code>.</li> 2.46 +<li>If the <code>ecluster</code> does not cover any area (i.e. only consists of points, 2.47 +paths, and/or outlines), and if the search point (second argument) overlaps 2.48 +with the <code>ecluster</code>, then the penalty (and thus the result) is zero.</li> 2.49 +<li>The integral (or average) of the square of the fair distance value (result of 2.50 +this function) over all possible search points is independent of the 2.51 +<code>ecluster</code> as long as the <code>ecluster</code> does not cover more than a half of 2.52 +earth's surface.</li> 2.53 </ul> 2.54 2.55 -<p>The function interally uses numerical integration (Monte Carlo like) to compute 2.56 -the result. The third parameter (which defaults to 10000) can be used to adjust 2.57 -the number of samples taken. It is ensured that the penalty is always positive, 2.58 -i.e. results returned by the <code>fair_distance</code> function are always equal to or 2.59 -greater than the results returned by the <code>distance</code> function regardless of 2.60 -stochastic effects.</p> 2.61 +<p>The function uses numerical integration to compute the result. The third 2.62 +parameter (which defaults to 10000) can be used to adjust the number of samples 2.63 +taken. A higher sample count increases precision as well as execution time of 2.64 +the function. Because this function internally uses a spherical model of earth 2.65 +for certain steps of the calculation, the precision cannot be increased 2.66 +unboundedly.</p> 2.67 + 2.68 +<p>Despite the limitations explained above, it is ensured that the penalty is 2.69 +always positive, i.e. results returned by the <code>fair_distance</code> function are 2.70 +always equal to or greater than the results returned by the <code>distance</code> 2.71 +function regardless of stochastic effects. Furthermore, all results are 2.72 +deterministic and reproducible with the same version of pgLatLon.</p> 2.73 2.74 <h4><code>GeoJSON_to_epoint(jsonb, text)</code></h4> 2.75
3.1 --- a/README.mkd Tue Oct 25 22:15:17 2016 +0200 3.2 +++ b/README.mkd Mon Oct 31 13:06:31 2016 +0100 3.3 @@ -1,4 +1,4 @@ 3.4 -pgLatLon v0.9 documentation 3.5 +pgLatLon v0.10 documentation 3.6 =========================== 3.7 3.8 pgLatLon is a spatial database extension for the PostgreSQL object-relational 3.9 @@ -39,8 +39,8 @@ 3.10 It is also possible to compile and install the extension without GNU Make as 3.11 follows: 3.12 3.13 - cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0008.so latlon-v0008.c 3.14 - cp latlon-v0008.so `pg_config --pkglibdir` 3.15 + cc -Wall -O2 -fPIC -shared -I `pg_config --includedir-server` -o latlon-v0009.so latlon-v0009.c 3.16 + cp latlon-v0009.so `pg_config --pkglibdir` 3.17 cp latlon.control `pg_config --sharedir`/extension/ 3.18 cp latlon--*.sql `pg_config --sharedir`/extension/ 3.19 3.20 @@ -484,22 +484,40 @@ 3.21 The penalty by which the returned distance is increased fulfills (at least) the 3.22 following properties: 3.23 3.24 -* For search points far away from the `ecluster` (i.e. distance approaching 3.25 - infinity), the penalty approaches zero (i.e. `fair_distance` behaves the same 3.26 - as `distance`). 3.27 +* The penalty function is continuous (except noise created by numerical 3.28 + integration, see paragraph after this list) as long as no objects are added 3.29 + to or removed from the `ecluster`. That particularly means: small changes in 3.30 + the search point (second argument) cause only small changes in the result. 3.31 +* For search points far away from the `ecluster` (i.e. large distances compared 3.32 + to the dimensions of the `ecluster`), the penalty approaches zero, i.e. the 3.33 + behavior of the `fair_distance` function approaches the behavior of the 3.34 + `distance` function. 3.35 * If the `ecluster` consists of a set of points, the penalty for a search point 3.36 - close to one of that points (closer than half of the minimum distance between 3.37 - each pair of points in the `ecluster`) is chosen in such a way that the 3.38 - adjusted distance is equal to the distance from the search point to the 3.39 + close to one of those points (closer than half of the minimum distance 3.40 + between each pair of points in the `ecluster`) is chosen in such a way that 3.41 + the adjusted distance is equal to the distance from the search point to the 3.42 closest point in the `ecluster` multiplied by the square root of the count of 3.43 points in the `ecluster`. 3.44 +* If the `ecluster` does not cover any area (i.e. only consists of points, 3.45 + paths, and/or outlines), and if the search point (second argument) overlaps 3.46 + with the `ecluster`, then the penalty (and thus the result) is zero. 3.47 +* The integral (or average) of the square of the fair distance value (result of 3.48 + this function) over all possible search points is independent of the 3.49 + `ecluster` as long as the `ecluster` does not cover more than a half of 3.50 + earth's surface. 3.51 3.52 -The function interally uses numerical integration (Monte Carlo like) to compute 3.53 -the result. The third parameter (which defaults to 10000) can be used to adjust 3.54 -the number of samples taken. It is ensured that the penalty is always positive, 3.55 -i.e. results returned by the `fair_distance` function are always equal to or 3.56 -greater than the results returned by the `distance` function regardless of 3.57 -stochastic effects. 3.58 +The function uses numerical integration to compute the result. The third 3.59 +parameter (which defaults to 10000) can be used to adjust the number of samples 3.60 +taken. A higher sample count increases precision as well as execution time of 3.61 +the function. Because this function internally uses a spherical model of earth 3.62 +for certain steps of the calculation, the precision cannot be increased 3.63 +unboundedly. 3.64 + 3.65 +Despite the limitations explained above, it is ensured that the penalty is 3.66 +always positive, i.e. results returned by the `fair_distance` function are 3.67 +always equal to or greater than the results returned by the `distance` 3.68 +function regardless of stochastic effects. Furthermore, all results are 3.69 +deterministic and reproducible with the same version of pgLatLon. 3.70 3.71 #### `GeoJSON_to_epoint(jsonb, text)` 3.72
4.1 --- a/create_test_db.data.sql Tue Oct 25 22:15:17 2016 +0200 4.2 +++ b/create_test_db.data.sql Mon Oct 31 13:06:31 2016 +0100 4.3 @@ -14,11 +14,13 @@ 4.4 END; 4.5 $$; 4.6 4.7 -INSERT INTO "test" ("location", "surrounding", "multipoint", "triangle") SELECT 4.8 +INSERT INTO "test" ("location", "surrounding", "multipoint", "triangle", "votes") SELECT 4.9 epoint((asin(2*random()-1) / pi()) * 180, (2*random()-1) * 180), 4.10 ecircle((asin(2*random()-1) / pi()) * 180, (2*random()-1) * 180, -ln(1-random()) * 1000), 4.11 ecluster_create_multipoint(tmp_three_points()), 4.12 - ecluster_create_polygon(tmp_three_points()) 4.13 + ecluster_create_polygon(tmp_three_points()), 4.14 + floor(random()*101) 4.15 FROM generate_series(1, 10000); 4.16 4.17 DROP FUNCTION tmp_three_points(); 4.18 +
5.1 --- a/create_test_db.schema.sql Tue Oct 25 22:15:17 2016 +0200 5.2 +++ b/create_test_db.schema.sql Mon Oct 31 13:06:31 2016 +0100 5.3 @@ -6,10 +6,81 @@ 5.4 "location" EPOINT NOT NULL, 5.5 "surrounding" ECIRCLE NOT NULL, 5.6 "multipoint" ECLUSTER NOT NULL, 5.7 - "triangle" ECLUSTER NOT NULL ); 5.8 + "triangle" ECLUSTER NOT NULL, 5.9 + "votes" INT4 NOT NULL ); 5.10 5.11 CREATE INDEX "test_location_key" ON "test" USING gist ("location"); 5.12 CREATE INDEX "test_surrounding_key" ON "test" USING gist ("surrounding"); 5.13 CREATE INDEX "test_multipoint_key" ON "test" USING gist ("multipoint"); 5.14 CREATE INDEX "test_triangle_key" ON "test" USING gist ("triangle"); 5.15 +CREATE INDEX "test_vote_key" ON "test" ("votes"); 5.16 5.17 + 5.18 +-- Below follows an example of how to perform a nearest-neighbor search with 5.19 +-- weighted geometric objects (distance scaled anti-proportionally through 5.20 +-- "votes" column). 5.21 +-- 5.22 +-- NOTE: The approach may be speeded up by providing new data types like 5.23 +-- "weighted_ecluster" with corresponding GiST index support in future 5.24 +-- versions of pgLatLon. 5.25 + 5.26 +CREATE TYPE "test_with_relevance" AS ( 5.27 + "id" INT4, 5.28 + "location" EPOINT, 5.29 + "surrounding" ECIRCLE, 5.30 + "multipoint" ECLUSTER, 5.31 + "triangle" ECLUSTER, 5.32 + "votes" INT4, 5.33 + "relevance" FLOAT8 ); 5.34 + 5.35 +CREATE FUNCTION "get_by_relevance" (epoint, int4 = 1, int4 = 10000) 5.36 + RETURNS SETOF "test_with_relevance" 5.37 + LANGUAGE plpgsql STABLE AS $$ 5.38 + DECLARE 5.39 + "max_votes" INT4; 5.40 + "tries" INT4 = 2; 5.41 + "all" INT4; 5.42 + "matches" INT4; 5.43 + BEGIN 5.44 + SELECT "votes" INTO "max_votes" FROM "test" ORDER BY "votes" DESC LIMIT 1; 5.45 + IF "max_votes" > 0 THEN 5.46 + LOOP 5.47 + RAISE DEBUG 'Considering % entries', "tries"; 5.48 + SELECT 5.49 + count(1), 5.50 + count(CASE WHEN "relevance" < "worst_case" THEN 1 ELSE NULL END) 5.51 + INTO "all", "matches" 5.52 + FROM ( 5.53 + SELECT 5.54 + CASE 5.55 + WHEN "votes" = 0 5.56 + THEN 'inf'::FLOAT8 5.57 + ELSE "fair_distance" / "votes" 5.58 + END AS "relevance", 5.59 + max("fair_distance") OVER () / "max_votes" AS "worst_case" 5.60 + FROM ( 5.61 + SELECT fair_distance("triangle", $1, $3), "votes" FROM "test" 5.62 + ORDER BY fair_distance 5.63 + LIMIT "tries" 5.64 + ) AS "subquery1" 5.65 + ) AS "subquery2"; 5.66 + EXIT WHEN "matches" >= $2 OR "all" < "tries"; 5.67 + "tries" := "tries" * 2; 5.68 + END LOOP; 5.69 + RETURN QUERY SELECT * FROM ( 5.70 + SELECT 5.71 + *, 5.72 + CASE 5.73 + WHEN "votes" = 0 5.74 + THEN 'inf'::FLOAT8 5.75 + ELSE fair_distance("triangle", $1, $3) / "votes" 5.76 + END AS "relevance" 5.77 + FROM "test" ORDER BY fair_distance("triangle", $1, $3) LIMIT "tries" 5.78 + ) AS "subquery" ORDER BY "relevance", "id" LIMIT $2; 5.79 + ELSE 5.80 + RETURN QUERY SELECT * FROM "test" ORDER BY "id" LIMIT $2; 5.81 + END IF; 5.82 + RETURN; 5.83 + END; 5.84 + $$; 5.85 +
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/latlon--0.10.sql Mon Oct 31 13:06:31 2016 +0100 6.3 @@ -0,0 +1,1717 @@ 6.4 + 6.5 +---------------------------------------- 6.6 +-- forward declarations (shell types) -- 6.7 +---------------------------------------- 6.8 + 6.9 +CREATE TYPE ekey_point; 6.10 +CREATE TYPE ekey_area; 6.11 +CREATE TYPE epoint; 6.12 +CREATE TYPE epoint_with_sample_count; 6.13 +CREATE TYPE ebox; 6.14 +CREATE TYPE ecircle; 6.15 +CREATE TYPE ecluster; 6.16 + 6.17 + 6.18 +------------------------------------------------------------ 6.19 +-- dummy input/output functions for dummy index key types -- 6.20 +------------------------------------------------------------ 6.21 + 6.22 +CREATE FUNCTION ekey_point_in_dummy(cstring) 6.23 + RETURNS ekey_point 6.24 + LANGUAGE C IMMUTABLE STRICT 6.25 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 6.26 + 6.27 +CREATE FUNCTION ekey_point_out_dummy(ekey_point) 6.28 + RETURNS cstring 6.29 + LANGUAGE C IMMUTABLE STRICT 6.30 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 6.31 + 6.32 +CREATE FUNCTION ekey_area_in_dummy(cstring) 6.33 + RETURNS ekey_area 6.34 + LANGUAGE C IMMUTABLE STRICT 6.35 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 6.36 + 6.37 +CREATE FUNCTION ekey_area_out_dummy(ekey_area) 6.38 + RETURNS cstring 6.39 + LANGUAGE C IMMUTABLE STRICT 6.40 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 6.41 + 6.42 + 6.43 +-------------------------- 6.44 +-- text input functions -- 6.45 +-------------------------- 6.46 + 6.47 +CREATE FUNCTION epoint_in(cstring) 6.48 + RETURNS epoint 6.49 + LANGUAGE C IMMUTABLE STRICT 6.50 + AS '$libdir/latlon-v0009', 'pgl_epoint_in'; 6.51 + 6.52 +CREATE FUNCTION epoint_with_sample_count_in(cstring) 6.53 + RETURNS epoint_with_sample_count 6.54 + LANGUAGE C IMMUTABLE STRICT 6.55 + AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_in'; 6.56 + 6.57 +CREATE FUNCTION ebox_in(cstring) 6.58 + RETURNS ebox 6.59 + LANGUAGE C IMMUTABLE STRICT 6.60 + AS '$libdir/latlon-v0009', 'pgl_ebox_in'; 6.61 + 6.62 +CREATE FUNCTION ecircle_in(cstring) 6.63 + RETURNS ecircle 6.64 + LANGUAGE C IMMUTABLE STRICT 6.65 + AS '$libdir/latlon-v0009', 'pgl_ecircle_in'; 6.66 + 6.67 +CREATE FUNCTION ecluster_in(cstring) 6.68 + RETURNS ecluster 6.69 + LANGUAGE C IMMUTABLE STRICT 6.70 + AS '$libdir/latlon-v0009', 'pgl_ecluster_in'; 6.71 + 6.72 + 6.73 +--------------------------- 6.74 +-- text output functions -- 6.75 +--------------------------- 6.76 + 6.77 +CREATE FUNCTION epoint_out(epoint) 6.78 + RETURNS cstring 6.79 + LANGUAGE C IMMUTABLE STRICT 6.80 + AS '$libdir/latlon-v0009', 'pgl_epoint_out'; 6.81 + 6.82 +CREATE FUNCTION epoint_with_sample_count_out(epoint_with_sample_count) 6.83 + RETURNS cstring 6.84 + LANGUAGE C IMMUTABLE STRICT 6.85 + AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_out'; 6.86 + 6.87 +CREATE FUNCTION ebox_out(ebox) 6.88 + RETURNS cstring 6.89 + LANGUAGE C IMMUTABLE STRICT 6.90 + AS '$libdir/latlon-v0009', 'pgl_ebox_out'; 6.91 + 6.92 +CREATE FUNCTION ecircle_out(ecircle) 6.93 + RETURNS cstring 6.94 + LANGUAGE C IMMUTABLE STRICT 6.95 + AS '$libdir/latlon-v0009', 'pgl_ecircle_out'; 6.96 + 6.97 +CREATE FUNCTION ecluster_out(ecluster) 6.98 + RETURNS cstring 6.99 + LANGUAGE C IMMUTABLE STRICT 6.100 + AS '$libdir/latlon-v0009', 'pgl_ecluster_out'; 6.101 + 6.102 + 6.103 +-------------------------- 6.104 +-- binary I/O functions -- 6.105 +-------------------------- 6.106 + 6.107 +CREATE FUNCTION epoint_recv(internal) 6.108 + RETURNS epoint 6.109 + LANGUAGE C IMMUTABLE STRICT 6.110 + AS '$libdir/latlon-v0009', 'pgl_epoint_recv'; 6.111 + 6.112 +CREATE FUNCTION ebox_recv(internal) 6.113 + RETURNS ebox 6.114 + LANGUAGE C IMMUTABLE STRICT 6.115 + AS '$libdir/latlon-v0009', 'pgl_ebox_recv'; 6.116 + 6.117 +CREATE FUNCTION ecircle_recv(internal) 6.118 + RETURNS ecircle 6.119 + LANGUAGE C IMMUTABLE STRICT 6.120 + AS '$libdir/latlon-v0009', 'pgl_ecircle_recv'; 6.121 + 6.122 +CREATE FUNCTION epoint_send(epoint) 6.123 + RETURNS bytea 6.124 + LANGUAGE C IMMUTABLE STRICT 6.125 + AS '$libdir/latlon-v0009', 'pgl_epoint_send'; 6.126 + 6.127 +CREATE FUNCTION ebox_send(ebox) 6.128 + RETURNS bytea 6.129 + LANGUAGE C IMMUTABLE STRICT 6.130 + AS '$libdir/latlon-v0009', 'pgl_ebox_send'; 6.131 + 6.132 +CREATE FUNCTION ecircle_send(ecircle) 6.133 + RETURNS bytea 6.134 + LANGUAGE C IMMUTABLE STRICT 6.135 + AS '$libdir/latlon-v0009', 'pgl_ecircle_send'; 6.136 + 6.137 + 6.138 +----------------------------------------------- 6.139 +-- type definitions of dummy index key types -- 6.140 +----------------------------------------------- 6.141 + 6.142 +CREATE TYPE ekey_point ( 6.143 + internallength = 8, 6.144 + input = ekey_point_in_dummy, 6.145 + output = ekey_point_out_dummy, 6.146 + alignment = char ); 6.147 + 6.148 +CREATE TYPE ekey_area ( 6.149 + internallength = 9, 6.150 + input = ekey_area_in_dummy, 6.151 + output = ekey_area_out_dummy, 6.152 + alignment = char ); 6.153 + 6.154 + 6.155 +------------------------------------------ 6.156 +-- definitions of geographic data types -- 6.157 +------------------------------------------ 6.158 + 6.159 +CREATE TYPE epoint ( 6.160 + internallength = 16, 6.161 + input = epoint_in, 6.162 + output = epoint_out, 6.163 + receive = epoint_recv, 6.164 + send = epoint_send, 6.165 + alignment = double ); 6.166 + 6.167 +CREATE TYPE epoint_with_sample_count ( 6.168 + internallength = 20, 6.169 + input = epoint_with_sample_count_in, 6.170 + output = epoint_with_sample_count_out, 6.171 + alignment = double ); 6.172 + 6.173 +CREATE TYPE ebox ( 6.174 + internallength = 32, 6.175 + input = ebox_in, 6.176 + output = ebox_out, 6.177 + receive = ebox_recv, 6.178 + send = ebox_send, 6.179 + alignment = double ); 6.180 + 6.181 +CREATE TYPE ecircle ( 6.182 + internallength = 24, 6.183 + input = ecircle_in, 6.184 + output = ecircle_out, 6.185 + receive = ecircle_recv, 6.186 + send = ecircle_send, 6.187 + alignment = double ); 6.188 + 6.189 +CREATE TYPE ecluster ( 6.190 + internallength = VARIABLE, 6.191 + input = ecluster_in, 6.192 + output = ecluster_out, 6.193 + alignment = double, 6.194 + storage = external ); 6.195 + 6.196 + 6.197 +-------------------- 6.198 +-- B-tree support -- 6.199 +-------------------- 6.200 + 6.201 +-- begin of B-tree support for epoint 6.202 + 6.203 +CREATE FUNCTION epoint_btree_lt(epoint, epoint) 6.204 + RETURNS boolean 6.205 + LANGUAGE C IMMUTABLE STRICT 6.206 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_lt'; 6.207 + 6.208 +CREATE FUNCTION epoint_btree_le(epoint, epoint) 6.209 + RETURNS boolean 6.210 + LANGUAGE C IMMUTABLE STRICT 6.211 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_le'; 6.212 + 6.213 +CREATE FUNCTION epoint_btree_eq(epoint, epoint) 6.214 + RETURNS boolean 6.215 + LANGUAGE C IMMUTABLE STRICT 6.216 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_eq'; 6.217 + 6.218 +CREATE FUNCTION epoint_btree_ne(epoint, epoint) 6.219 + RETURNS boolean 6.220 + LANGUAGE C IMMUTABLE STRICT 6.221 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ne'; 6.222 + 6.223 +CREATE FUNCTION epoint_btree_ge(epoint, epoint) 6.224 + RETURNS boolean 6.225 + LANGUAGE C IMMUTABLE STRICT 6.226 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ge'; 6.227 + 6.228 +CREATE FUNCTION epoint_btree_gt(epoint, epoint) 6.229 + RETURNS boolean 6.230 + LANGUAGE C IMMUTABLE STRICT 6.231 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_gt'; 6.232 + 6.233 +CREATE OPERATOR <<< ( 6.234 + leftarg = epoint, 6.235 + rightarg = epoint, 6.236 + procedure = epoint_btree_lt, 6.237 + commutator = >>>, 6.238 + negator = >>>=, 6.239 + restrict = scalarltsel, 6.240 + join = scalarltjoinsel 6.241 +); 6.242 + 6.243 +CREATE OPERATOR <<<= ( 6.244 + leftarg = epoint, 6.245 + rightarg = epoint, 6.246 + procedure = epoint_btree_le, 6.247 + commutator = >>>=, 6.248 + negator = >>>, 6.249 + restrict = scalarltsel, 6.250 + join = scalarltjoinsel 6.251 +); 6.252 + 6.253 +CREATE OPERATOR = ( 6.254 + leftarg = epoint, 6.255 + rightarg = epoint, 6.256 + procedure = epoint_btree_eq, 6.257 + commutator = =, 6.258 + negator = <>, 6.259 + restrict = eqsel, 6.260 + join = eqjoinsel, 6.261 + merges 6.262 +); 6.263 + 6.264 +CREATE OPERATOR <> ( 6.265 + leftarg = epoint, 6.266 + rightarg = epoint, 6.267 + procedure = epoint_btree_eq, 6.268 + commutator = <>, 6.269 + negator = =, 6.270 + restrict = neqsel, 6.271 + join = neqjoinsel 6.272 +); 6.273 + 6.274 +CREATE OPERATOR >>>= ( 6.275 + leftarg = epoint, 6.276 + rightarg = epoint, 6.277 + procedure = epoint_btree_ge, 6.278 + commutator = <<<=, 6.279 + negator = <<<, 6.280 + restrict = scalargtsel, 6.281 + join = scalargtjoinsel 6.282 +); 6.283 + 6.284 +CREATE OPERATOR >>> ( 6.285 + leftarg = epoint, 6.286 + rightarg = epoint, 6.287 + procedure = epoint_btree_gt, 6.288 + commutator = <<<, 6.289 + negator = <<<=, 6.290 + restrict = scalargtsel, 6.291 + join = scalargtjoinsel 6.292 +); 6.293 + 6.294 +CREATE FUNCTION epoint_btree_cmp(epoint, epoint) 6.295 + RETURNS int4 6.296 + LANGUAGE C IMMUTABLE STRICT 6.297 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_cmp'; 6.298 + 6.299 +CREATE OPERATOR CLASS epoint_btree_ops 6.300 + DEFAULT FOR TYPE epoint USING btree AS 6.301 + OPERATOR 1 <<< , 6.302 + OPERATOR 2 <<<= , 6.303 + OPERATOR 3 = , 6.304 + OPERATOR 4 >>>= , 6.305 + OPERATOR 5 >>> , 6.306 + FUNCTION 1 epoint_btree_cmp(epoint, epoint); 6.307 + 6.308 +-- end of B-tree support for epoint 6.309 + 6.310 +-- begin of B-tree support for ebox 6.311 + 6.312 +CREATE FUNCTION ebox_btree_lt(ebox, ebox) 6.313 + RETURNS boolean 6.314 + LANGUAGE C IMMUTABLE STRICT 6.315 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_lt'; 6.316 + 6.317 +CREATE FUNCTION ebox_btree_le(ebox, ebox) 6.318 + RETURNS boolean 6.319 + LANGUAGE C IMMUTABLE STRICT 6.320 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_le'; 6.321 + 6.322 +CREATE FUNCTION ebox_btree_eq(ebox, ebox) 6.323 + RETURNS boolean 6.324 + LANGUAGE C IMMUTABLE STRICT 6.325 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_eq'; 6.326 + 6.327 +CREATE FUNCTION ebox_btree_ne(ebox, ebox) 6.328 + RETURNS boolean 6.329 + LANGUAGE C IMMUTABLE STRICT 6.330 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ne'; 6.331 + 6.332 +CREATE FUNCTION ebox_btree_ge(ebox, ebox) 6.333 + RETURNS boolean 6.334 + LANGUAGE C IMMUTABLE STRICT 6.335 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ge'; 6.336 + 6.337 +CREATE FUNCTION ebox_btree_gt(ebox, ebox) 6.338 + RETURNS boolean 6.339 + LANGUAGE C IMMUTABLE STRICT 6.340 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_gt'; 6.341 + 6.342 +CREATE OPERATOR <<< ( 6.343 + leftarg = ebox, 6.344 + rightarg = ebox, 6.345 + procedure = ebox_btree_lt, 6.346 + commutator = >>>, 6.347 + negator = >>>=, 6.348 + restrict = scalarltsel, 6.349 + join = scalarltjoinsel 6.350 +); 6.351 + 6.352 +CREATE OPERATOR <<<= ( 6.353 + leftarg = ebox, 6.354 + rightarg = ebox, 6.355 + procedure = ebox_btree_le, 6.356 + commutator = >>>=, 6.357 + negator = >>>, 6.358 + restrict = scalarltsel, 6.359 + join = scalarltjoinsel 6.360 +); 6.361 + 6.362 +CREATE OPERATOR = ( 6.363 + leftarg = ebox, 6.364 + rightarg = ebox, 6.365 + procedure = ebox_btree_eq, 6.366 + commutator = =, 6.367 + negator = <>, 6.368 + restrict = eqsel, 6.369 + join = eqjoinsel, 6.370 + merges 6.371 +); 6.372 + 6.373 +CREATE OPERATOR <> ( 6.374 + leftarg = ebox, 6.375 + rightarg = ebox, 6.376 + procedure = ebox_btree_eq, 6.377 + commutator = <>, 6.378 + negator = =, 6.379 + restrict = neqsel, 6.380 + join = neqjoinsel 6.381 +); 6.382 + 6.383 +CREATE OPERATOR >>>= ( 6.384 + leftarg = ebox, 6.385 + rightarg = ebox, 6.386 + procedure = ebox_btree_ge, 6.387 + commutator = <<<=, 6.388 + negator = <<<, 6.389 + restrict = scalargtsel, 6.390 + join = scalargtjoinsel 6.391 +); 6.392 + 6.393 +CREATE OPERATOR >>> ( 6.394 + leftarg = ebox, 6.395 + rightarg = ebox, 6.396 + procedure = ebox_btree_gt, 6.397 + commutator = <<<, 6.398 + negator = <<<=, 6.399 + restrict = scalargtsel, 6.400 + join = scalargtjoinsel 6.401 +); 6.402 + 6.403 +CREATE FUNCTION ebox_btree_cmp(ebox, ebox) 6.404 + RETURNS int4 6.405 + LANGUAGE C IMMUTABLE STRICT 6.406 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_cmp'; 6.407 + 6.408 +CREATE OPERATOR CLASS ebox_btree_ops 6.409 + DEFAULT FOR TYPE ebox USING btree AS 6.410 + OPERATOR 1 <<< , 6.411 + OPERATOR 2 <<<= , 6.412 + OPERATOR 3 = , 6.413 + OPERATOR 4 >>>= , 6.414 + OPERATOR 5 >>> , 6.415 + FUNCTION 1 ebox_btree_cmp(ebox, ebox); 6.416 + 6.417 +-- end of B-tree support for ebox 6.418 + 6.419 +-- begin of B-tree support for ecircle 6.420 + 6.421 +CREATE FUNCTION ecircle_btree_lt(ecircle, ecircle) 6.422 + RETURNS boolean 6.423 + LANGUAGE C IMMUTABLE STRICT 6.424 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_lt'; 6.425 + 6.426 +CREATE FUNCTION ecircle_btree_le(ecircle, ecircle) 6.427 + RETURNS boolean 6.428 + LANGUAGE C IMMUTABLE STRICT 6.429 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_le'; 6.430 + 6.431 +CREATE FUNCTION ecircle_btree_eq(ecircle, ecircle) 6.432 + RETURNS boolean 6.433 + LANGUAGE C IMMUTABLE STRICT 6.434 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_eq'; 6.435 + 6.436 +CREATE FUNCTION ecircle_btree_ne(ecircle, ecircle) 6.437 + RETURNS boolean 6.438 + LANGUAGE C IMMUTABLE STRICT 6.439 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ne'; 6.440 + 6.441 +CREATE FUNCTION ecircle_btree_ge(ecircle, ecircle) 6.442 + RETURNS boolean 6.443 + LANGUAGE C IMMUTABLE STRICT 6.444 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ge'; 6.445 + 6.446 +CREATE FUNCTION ecircle_btree_gt(ecircle, ecircle) 6.447 + RETURNS boolean 6.448 + LANGUAGE C IMMUTABLE STRICT 6.449 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_gt'; 6.450 + 6.451 +CREATE OPERATOR <<< ( 6.452 + leftarg = ecircle, 6.453 + rightarg = ecircle, 6.454 + procedure = ecircle_btree_lt, 6.455 + commutator = >>>, 6.456 + negator = >>>=, 6.457 + restrict = scalarltsel, 6.458 + join = scalarltjoinsel 6.459 +); 6.460 + 6.461 +CREATE OPERATOR <<<= ( 6.462 + leftarg = ecircle, 6.463 + rightarg = ecircle, 6.464 + procedure = ecircle_btree_le, 6.465 + commutator = >>>=, 6.466 + negator = >>>, 6.467 + restrict = scalarltsel, 6.468 + join = scalarltjoinsel 6.469 +); 6.470 + 6.471 +CREATE OPERATOR = ( 6.472 + leftarg = ecircle, 6.473 + rightarg = ecircle, 6.474 + procedure = ecircle_btree_eq, 6.475 + commutator = =, 6.476 + negator = <>, 6.477 + restrict = eqsel, 6.478 + join = eqjoinsel, 6.479 + merges 6.480 +); 6.481 + 6.482 +CREATE OPERATOR <> ( 6.483 + leftarg = ecircle, 6.484 + rightarg = ecircle, 6.485 + procedure = ecircle_btree_eq, 6.486 + commutator = <>, 6.487 + negator = =, 6.488 + restrict = neqsel, 6.489 + join = neqjoinsel 6.490 +); 6.491 + 6.492 +CREATE OPERATOR >>>= ( 6.493 + leftarg = ecircle, 6.494 + rightarg = ecircle, 6.495 + procedure = ecircle_btree_ge, 6.496 + commutator = <<<=, 6.497 + negator = <<<, 6.498 + restrict = scalargtsel, 6.499 + join = scalargtjoinsel 6.500 +); 6.501 + 6.502 +CREATE OPERATOR >>> ( 6.503 + leftarg = ecircle, 6.504 + rightarg = ecircle, 6.505 + procedure = ecircle_btree_gt, 6.506 + commutator = <<<, 6.507 + negator = <<<=, 6.508 + restrict = scalargtsel, 6.509 + join = scalargtjoinsel 6.510 +); 6.511 + 6.512 +CREATE FUNCTION ecircle_btree_cmp(ecircle, ecircle) 6.513 + RETURNS int4 6.514 + LANGUAGE C IMMUTABLE STRICT 6.515 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_cmp'; 6.516 + 6.517 +CREATE OPERATOR CLASS ecircle_btree_ops 6.518 + DEFAULT FOR TYPE ecircle USING btree AS 6.519 + OPERATOR 1 <<< , 6.520 + OPERATOR 2 <<<= , 6.521 + OPERATOR 3 = , 6.522 + OPERATOR 4 >>>= , 6.523 + OPERATOR 5 >>> , 6.524 + FUNCTION 1 ecircle_btree_cmp(ecircle, ecircle); 6.525 + 6.526 +-- end of B-tree support for ecircle 6.527 + 6.528 + 6.529 +---------------- 6.530 +-- type casts -- 6.531 +---------------- 6.532 + 6.533 +CREATE FUNCTION cast_epoint_to_ebox(epoint) 6.534 + RETURNS ebox 6.535 + LANGUAGE C IMMUTABLE STRICT 6.536 + AS '$libdir/latlon-v0009', 'pgl_epoint_to_ebox'; 6.537 + 6.538 +CREATE CAST (epoint AS ebox) WITH FUNCTION cast_epoint_to_ebox(epoint); 6.539 + 6.540 +CREATE FUNCTION cast_epoint_to_ecircle(epoint) 6.541 + RETURNS ecircle 6.542 + LANGUAGE C IMMUTABLE STRICT 6.543 + AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecircle'; 6.544 + 6.545 +CREATE CAST (epoint AS ecircle) WITH FUNCTION cast_epoint_to_ecircle(epoint); 6.546 + 6.547 +CREATE FUNCTION cast_epoint_to_ecluster(epoint) 6.548 + RETURNS ecluster 6.549 + LANGUAGE C IMMUTABLE STRICT 6.550 + AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecluster'; 6.551 + 6.552 +CREATE CAST (epoint AS ecluster) WITH FUNCTION cast_epoint_to_ecluster(epoint); 6.553 + 6.554 +CREATE FUNCTION cast_ebox_to_ecluster(ebox) 6.555 + RETURNS ecluster 6.556 + LANGUAGE C IMMUTABLE STRICT 6.557 + AS '$libdir/latlon-v0009', 'pgl_ebox_to_ecluster'; 6.558 + 6.559 +CREATE CAST (ebox AS ecluster) WITH FUNCTION cast_ebox_to_ecluster(ebox); 6.560 + 6.561 + 6.562 +--------------------------- 6.563 +-- constructor functions -- 6.564 +--------------------------- 6.565 + 6.566 +CREATE FUNCTION epoint(float8, float8) 6.567 + RETURNS epoint 6.568 + LANGUAGE C IMMUTABLE STRICT 6.569 + AS '$libdir/latlon-v0009', 'pgl_create_epoint'; 6.570 + 6.571 +CREATE FUNCTION epoint_latlon(float8, float8) 6.572 + RETURNS epoint 6.573 + LANGUAGE SQL IMMUTABLE STRICT AS $$ 6.574 + SELECT epoint($1, $2) 6.575 + $$; 6.576 + 6.577 +CREATE FUNCTION epoint_lonlat(float8, float8) 6.578 + RETURNS epoint 6.579 + LANGUAGE SQL IMMUTABLE STRICT AS $$ 6.580 + SELECT epoint($2, $1) 6.581 + $$; 6.582 + 6.583 +CREATE FUNCTION epoint_with_sample_count(epoint, int4) 6.584 + RETURNS epoint_with_sample_count 6.585 + LANGUAGE C IMMUTABLE STRICT 6.586 + AS '$libdir/latlon-v0009', 'pgl_create_epoint_with_sample_count'; 6.587 + 6.588 +CREATE FUNCTION empty_ebox() 6.589 + RETURNS ebox 6.590 + LANGUAGE C IMMUTABLE STRICT 6.591 + AS '$libdir/latlon-v0009', 'pgl_create_empty_ebox'; 6.592 + 6.593 +CREATE FUNCTION ebox(float8, float8, float8, float8) 6.594 + RETURNS ebox 6.595 + LANGUAGE C IMMUTABLE STRICT 6.596 + AS '$libdir/latlon-v0009', 'pgl_create_ebox'; 6.597 + 6.598 +CREATE FUNCTION ebox(epoint, epoint) 6.599 + RETURNS ebox 6.600 + LANGUAGE C IMMUTABLE STRICT 6.601 + AS '$libdir/latlon-v0009', 'pgl_create_ebox_from_epoints'; 6.602 + 6.603 +CREATE FUNCTION ecircle(float8, float8, float8) 6.604 + RETURNS ecircle 6.605 + LANGUAGE C IMMUTABLE STRICT 6.606 + AS '$libdir/latlon-v0009', 'pgl_create_ecircle'; 6.607 + 6.608 +CREATE FUNCTION ecircle(epoint, float8) 6.609 + RETURNS ecircle 6.610 + LANGUAGE C IMMUTABLE STRICT 6.611 + AS '$libdir/latlon-v0009', 'pgl_create_ecircle_from_epoint'; 6.612 + 6.613 +CREATE FUNCTION ecluster_concat(ecluster[]) 6.614 + RETURNS ecluster 6.615 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.616 + SELECT array_to_string($1, ' ')::ecluster 6.617 + $$; 6.618 + 6.619 +CREATE FUNCTION ecluster_concat(ecluster, ecluster) 6.620 + RETURNS ecluster 6.621 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.622 + SELECT ($1::text || ' ' || $2::text)::ecluster 6.623 + $$; 6.624 + 6.625 +CREATE FUNCTION ecluster_create_multipoint(epoint[]) 6.626 + RETURNS ecluster 6.627 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.628 + SELECT 6.629 + array_to_string(array_agg('point (' || unnest || ')'), ' ')::ecluster 6.630 + FROM unnest($1) 6.631 + $$; 6.632 + 6.633 +CREATE FUNCTION ecluster_create_path(epoint[]) 6.634 + RETURNS ecluster 6.635 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.636 + SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 6.637 + ('path (' || array_to_string($1, ' ') || ')')::ecluster 6.638 + END 6.639 + FROM array_to_string($1, ' ') AS "str" 6.640 + $$; 6.641 + 6.642 +CREATE FUNCTION ecluster_create_outline(epoint[]) 6.643 + RETURNS ecluster 6.644 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.645 + SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 6.646 + ('outline (' || array_to_string($1, ' ') || ')')::ecluster 6.647 + END 6.648 + FROM array_to_string($1, ' ') AS "str" 6.649 + $$; 6.650 + 6.651 +CREATE FUNCTION ecluster_create_polygon(epoint[]) 6.652 + RETURNS ecluster 6.653 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.654 + SELECT CASE WHEN "str" = '' THEN 'empty'::ecluster ELSE 6.655 + ('polygon (' || array_to_string($1, ' ') || ')')::ecluster 6.656 + END 6.657 + FROM array_to_string($1, ' ') AS "str" 6.658 + $$; 6.659 + 6.660 + 6.661 +---------------------- 6.662 +-- getter functions -- 6.663 +---------------------- 6.664 + 6.665 +CREATE FUNCTION latitude(epoint) 6.666 + RETURNS float8 6.667 + LANGUAGE C IMMUTABLE STRICT 6.668 + AS '$libdir/latlon-v0009', 'pgl_epoint_lat'; 6.669 + 6.670 +CREATE FUNCTION longitude(epoint) 6.671 + RETURNS float8 6.672 + LANGUAGE C IMMUTABLE STRICT 6.673 + AS '$libdir/latlon-v0009', 'pgl_epoint_lon'; 6.674 + 6.675 +CREATE FUNCTION min_latitude(ebox) 6.676 + RETURNS float8 6.677 + LANGUAGE C IMMUTABLE STRICT 6.678 + AS '$libdir/latlon-v0009', 'pgl_ebox_lat_min'; 6.679 + 6.680 +CREATE FUNCTION max_latitude(ebox) 6.681 + RETURNS float8 6.682 + LANGUAGE C IMMUTABLE STRICT 6.683 + AS '$libdir/latlon-v0009', 'pgl_ebox_lat_max'; 6.684 + 6.685 +CREATE FUNCTION min_longitude(ebox) 6.686 + RETURNS float8 6.687 + LANGUAGE C IMMUTABLE STRICT 6.688 + AS '$libdir/latlon-v0009', 'pgl_ebox_lon_min'; 6.689 + 6.690 +CREATE FUNCTION max_longitude(ebox) 6.691 + RETURNS float8 6.692 + LANGUAGE C IMMUTABLE STRICT 6.693 + AS '$libdir/latlon-v0009', 'pgl_ebox_lon_max'; 6.694 + 6.695 +CREATE FUNCTION center(ecircle) 6.696 + RETURNS epoint 6.697 + LANGUAGE C IMMUTABLE STRICT 6.698 + AS '$libdir/latlon-v0009', 'pgl_ecircle_center'; 6.699 + 6.700 +CREATE FUNCTION radius(ecircle) 6.701 + RETURNS float8 6.702 + LANGUAGE C IMMUTABLE STRICT 6.703 + AS '$libdir/latlon-v0009', 'pgl_ecircle_radius'; 6.704 + 6.705 +CREATE FUNCTION ecluster_extract_points(ecluster) 6.706 + RETURNS SETOF epoint 6.707 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.708 + SELECT "match"[2]::epoint 6.709 + FROM regexp_matches($1::text, e'(^| )point \\(([^)]+)\\)', 'g') AS "match" 6.710 + $$; 6.711 + 6.712 +CREATE FUNCTION ecluster_extract_paths(ecluster) 6.713 + RETURNS SETOF epoint[] 6.714 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.715 + SELECT ( 6.716 + SELECT array_agg("m2"[1]::epoint) 6.717 + FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 6.718 + ) 6.719 + FROM regexp_matches($1::text, e'(^| )path \\(([^)]+)\\)', 'g') AS "m1" 6.720 + $$; 6.721 + 6.722 +CREATE FUNCTION ecluster_extract_outlines(ecluster) 6.723 + RETURNS SETOF epoint[] 6.724 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.725 + SELECT ( 6.726 + SELECT array_agg("m2"[1]::epoint) 6.727 + FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 6.728 + ) 6.729 + FROM regexp_matches($1::text, e'(^| )outline \\(([^)]+)\\)', 'g') AS "m1" 6.730 + $$; 6.731 + 6.732 +CREATE FUNCTION ecluster_extract_polygons(ecluster) 6.733 + RETURNS SETOF epoint[] 6.734 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.735 + SELECT ( 6.736 + SELECT array_agg("m2"[1]::epoint) 6.737 + FROM regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2" 6.738 + ) 6.739 + FROM regexp_matches($1::text, e'(^| )polygon \\(([^)]+)\\)', 'g') AS "m1" 6.740 + $$; 6.741 + 6.742 + 6.743 +--------------- 6.744 +-- operators -- 6.745 +--------------- 6.746 + 6.747 +CREATE FUNCTION epoint_ebox_overlap_proc(epoint, ebox) 6.748 + RETURNS boolean 6.749 + LANGUAGE C IMMUTABLE STRICT 6.750 + AS '$libdir/latlon-v0009', 'pgl_epoint_ebox_overlap'; 6.751 + 6.752 +CREATE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle) 6.753 + RETURNS boolean 6.754 + LANGUAGE C IMMUTABLE STRICT 6.755 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_overlap'; 6.756 + 6.757 +CREATE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster) 6.758 + RETURNS boolean 6.759 + LANGUAGE C IMMUTABLE STRICT 6.760 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_overlap'; 6.761 + 6.762 +CREATE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster) 6.763 + RETURNS boolean 6.764 + LANGUAGE C IMMUTABLE STRICT 6.765 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_may_overlap'; 6.766 + 6.767 +CREATE FUNCTION ebox_overlap_proc(ebox, ebox) 6.768 + RETURNS boolean 6.769 + LANGUAGE C IMMUTABLE STRICT 6.770 + AS '$libdir/latlon-v0009', 'pgl_ebox_overlap'; 6.771 + 6.772 +CREATE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle) 6.773 + RETURNS boolean 6.774 + LANGUAGE C IMMUTABLE STRICT 6.775 + AS '$libdir/latlon-v0009', 'pgl_ebox_ecircle_may_overlap'; 6.776 + 6.777 +CREATE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster) 6.778 + RETURNS boolean 6.779 + LANGUAGE C IMMUTABLE STRICT 6.780 + AS '$libdir/latlon-v0009', 'pgl_ebox_ecluster_may_overlap'; 6.781 + 6.782 +CREATE FUNCTION ecircle_overlap_proc(ecircle, ecircle) 6.783 + RETURNS boolean 6.784 + LANGUAGE C IMMUTABLE STRICT 6.785 + AS '$libdir/latlon-v0009', 'pgl_ecircle_overlap'; 6.786 + 6.787 +CREATE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster) 6.788 + RETURNS boolean 6.789 + LANGUAGE C IMMUTABLE STRICT 6.790 + AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_overlap'; 6.791 + 6.792 +CREATE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster) 6.793 + RETURNS boolean 6.794 + LANGUAGE C IMMUTABLE STRICT 6.795 + AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_may_overlap'; 6.796 + 6.797 +CREATE FUNCTION ecluster_overlap_proc(ecluster, ecluster) 6.798 + RETURNS boolean 6.799 + LANGUAGE C IMMUTABLE STRICT 6.800 + AS '$libdir/latlon-v0009', 'pgl_ecluster_overlap'; 6.801 + 6.802 +CREATE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster) 6.803 + RETURNS boolean 6.804 + LANGUAGE C IMMUTABLE STRICT 6.805 + AS '$libdir/latlon-v0009', 'pgl_ecluster_may_overlap'; 6.806 + 6.807 +CREATE FUNCTION ecluster_contains_proc(ecluster, ecluster) 6.808 + RETURNS boolean 6.809 + LANGUAGE C IMMUTABLE STRICT 6.810 + AS '$libdir/latlon-v0009', 'pgl_ecluster_contains'; 6.811 + 6.812 +CREATE FUNCTION epoint_distance_proc(epoint, epoint) 6.813 + RETURNS float8 6.814 + LANGUAGE C IMMUTABLE STRICT 6.815 + AS '$libdir/latlon-v0009', 'pgl_epoint_distance'; 6.816 + 6.817 +CREATE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle) 6.818 + RETURNS float8 6.819 + LANGUAGE C IMMUTABLE STRICT 6.820 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_distance'; 6.821 + 6.822 +CREATE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster) 6.823 + RETURNS float8 6.824 + LANGUAGE C IMMUTABLE STRICT 6.825 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_distance'; 6.826 + 6.827 +CREATE FUNCTION ecircle_distance_proc(ecircle, ecircle) 6.828 + RETURNS float8 6.829 + LANGUAGE C IMMUTABLE STRICT 6.830 + AS '$libdir/latlon-v0009', 'pgl_ecircle_distance'; 6.831 + 6.832 +CREATE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster) 6.833 + RETURNS float8 6.834 + LANGUAGE C IMMUTABLE STRICT 6.835 + AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_distance'; 6.836 + 6.837 +CREATE FUNCTION ecluster_distance_proc(ecluster, ecluster) 6.838 + RETURNS float8 6.839 + LANGUAGE C IMMUTABLE STRICT 6.840 + AS '$libdir/latlon-v0009', 'pgl_ecluster_distance'; 6.841 + 6.842 +CREATE FUNCTION fair_distance_operator_proc(ecluster, epoint_with_sample_count) 6.843 + RETURNS float8 6.844 + LANGUAGE C IMMUTABLE STRICT 6.845 + AS '$libdir/latlon-v0009', 'pgl_ecluster_epoint_sc_fair_distance'; 6.846 + 6.847 +CREATE OPERATOR && ( 6.848 + leftarg = epoint, 6.849 + rightarg = ebox, 6.850 + procedure = epoint_ebox_overlap_proc, 6.851 + commutator = &&, 6.852 + restrict = areasel, 6.853 + join = areajoinsel 6.854 +); 6.855 + 6.856 +CREATE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint) 6.857 + RETURNS boolean 6.858 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 6.859 + 6.860 +CREATE OPERATOR && ( 6.861 + leftarg = ebox, 6.862 + rightarg = epoint, 6.863 + procedure = epoint_ebox_overlap_commutator, 6.864 + commutator = &&, 6.865 + restrict = areasel, 6.866 + join = areajoinsel 6.867 +); 6.868 + 6.869 +CREATE OPERATOR && ( 6.870 + leftarg = epoint, 6.871 + rightarg = ecircle, 6.872 + procedure = epoint_ecircle_overlap_proc, 6.873 + commutator = &&, 6.874 + restrict = areasel, 6.875 + join = areajoinsel 6.876 +); 6.877 + 6.878 +CREATE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint) 6.879 + RETURNS boolean 6.880 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 6.881 + 6.882 +CREATE OPERATOR && ( 6.883 + leftarg = ecircle, 6.884 + rightarg = epoint, 6.885 + procedure = epoint_ecircle_overlap_commutator, 6.886 + commutator = &&, 6.887 + restrict = areasel, 6.888 + join = areajoinsel 6.889 +); 6.890 + 6.891 +CREATE OPERATOR && ( 6.892 + leftarg = epoint, 6.893 + rightarg = ecluster, 6.894 + procedure = epoint_ecluster_overlap_proc, 6.895 + commutator = &&, 6.896 + restrict = areasel, 6.897 + join = areajoinsel 6.898 +); 6.899 + 6.900 +CREATE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint) 6.901 + RETURNS boolean 6.902 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 6.903 + 6.904 +CREATE OPERATOR && ( 6.905 + leftarg = ecluster, 6.906 + rightarg = epoint, 6.907 + procedure = epoint_ecluster_overlap_commutator, 6.908 + commutator = &&, 6.909 + restrict = areasel, 6.910 + join = areajoinsel 6.911 +); 6.912 + 6.913 +CREATE OPERATOR && ( 6.914 + leftarg = ebox, 6.915 + rightarg = ebox, 6.916 + procedure = ebox_overlap_proc, 6.917 + commutator = &&, 6.918 + restrict = areasel, 6.919 + join = areajoinsel 6.920 +); 6.921 + 6.922 +CREATE OPERATOR && ( 6.923 + leftarg = ecircle, 6.924 + rightarg = ecircle, 6.925 + procedure = ecircle_overlap_proc, 6.926 + commutator = &&, 6.927 + restrict = areasel, 6.928 + join = areajoinsel 6.929 +); 6.930 + 6.931 +CREATE OPERATOR && ( 6.932 + leftarg = ecircle, 6.933 + rightarg = ecluster, 6.934 + procedure = ecircle_ecluster_overlap_proc, 6.935 + commutator = &&, 6.936 + restrict = areasel, 6.937 + join = areajoinsel 6.938 +); 6.939 + 6.940 +CREATE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle) 6.941 + RETURNS boolean 6.942 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 && $1'; 6.943 + 6.944 +CREATE OPERATOR && ( 6.945 + leftarg = ecluster, 6.946 + rightarg = ecircle, 6.947 + procedure = ecircle_ecluster_overlap_commutator, 6.948 + commutator = &&, 6.949 + restrict = areasel, 6.950 + join = areajoinsel 6.951 +); 6.952 + 6.953 +CREATE OPERATOR && ( 6.954 + leftarg = ecluster, 6.955 + rightarg = ecluster, 6.956 + procedure = ecluster_overlap_proc, 6.957 + commutator = &&, 6.958 + restrict = areasel, 6.959 + join = areajoinsel 6.960 +); 6.961 + 6.962 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle) 6.963 + RETURNS boolean 6.964 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2'; 6.965 + 6.966 +CREATE OPERATOR && ( 6.967 + leftarg = ebox, 6.968 + rightarg = ecircle, 6.969 + procedure = ebox_ecircle_overlap_castwrap, 6.970 + commutator = &&, 6.971 + restrict = areasel, 6.972 + join = areajoinsel 6.973 +); 6.974 + 6.975 +CREATE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox) 6.976 + RETURNS boolean 6.977 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster'; 6.978 + 6.979 +CREATE OPERATOR && ( 6.980 + leftarg = ecircle, 6.981 + rightarg = ebox, 6.982 + procedure = ebox_ecircle_overlap_castwrap, 6.983 + commutator = &&, 6.984 + restrict = areasel, 6.985 + join = areajoinsel 6.986 +); 6.987 + 6.988 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster) 6.989 + RETURNS boolean 6.990 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster && $2'; 6.991 + 6.992 +CREATE OPERATOR && ( 6.993 + leftarg = ebox, 6.994 + rightarg = ecluster, 6.995 + procedure = ebox_ecluster_overlap_castwrap, 6.996 + commutator = &&, 6.997 + restrict = areasel, 6.998 + join = areajoinsel 6.999 +); 6.1000 + 6.1001 +CREATE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox) 6.1002 + RETURNS boolean 6.1003 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 && $2::ecluster'; 6.1004 + 6.1005 +CREATE OPERATOR && ( 6.1006 + leftarg = ecluster, 6.1007 + rightarg = ebox, 6.1008 + procedure = ebox_ecluster_overlap_castwrap, 6.1009 + commutator = &&, 6.1010 + restrict = areasel, 6.1011 + join = areajoinsel 6.1012 +); 6.1013 + 6.1014 +CREATE OPERATOR &&+ ( 6.1015 + leftarg = epoint, 6.1016 + rightarg = ecluster, 6.1017 + procedure = epoint_ecluster_may_overlap_proc, 6.1018 + commutator = &&+, 6.1019 + restrict = areasel, 6.1020 + join = areajoinsel 6.1021 +); 6.1022 + 6.1023 +CREATE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint) 6.1024 + RETURNS boolean 6.1025 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 6.1026 + 6.1027 +CREATE OPERATOR &&+ ( 6.1028 + leftarg = ecluster, 6.1029 + rightarg = epoint, 6.1030 + procedure = epoint_ecluster_may_overlap_commutator, 6.1031 + commutator = &&+, 6.1032 + restrict = areasel, 6.1033 + join = areajoinsel 6.1034 +); 6.1035 + 6.1036 +CREATE OPERATOR &&+ ( 6.1037 + leftarg = ebox, 6.1038 + rightarg = ecircle, 6.1039 + procedure = ebox_ecircle_may_overlap_proc, 6.1040 + commutator = &&+, 6.1041 + restrict = areasel, 6.1042 + join = areajoinsel 6.1043 +); 6.1044 + 6.1045 +CREATE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox) 6.1046 + RETURNS boolean 6.1047 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 6.1048 + 6.1049 +CREATE OPERATOR &&+ ( 6.1050 + leftarg = ecircle, 6.1051 + rightarg = ebox, 6.1052 + procedure = ebox_ecircle_may_overlap_commutator, 6.1053 + commutator = &&+, 6.1054 + restrict = areasel, 6.1055 + join = areajoinsel 6.1056 +); 6.1057 + 6.1058 +CREATE OPERATOR &&+ ( 6.1059 + leftarg = ebox, 6.1060 + rightarg = ecluster, 6.1061 + procedure = ebox_ecluster_may_overlap_proc, 6.1062 + commutator = &&+, 6.1063 + restrict = areasel, 6.1064 + join = areajoinsel 6.1065 +); 6.1066 + 6.1067 +CREATE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox) 6.1068 + RETURNS boolean 6.1069 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 6.1070 + 6.1071 +CREATE OPERATOR &&+ ( 6.1072 + leftarg = ecluster, 6.1073 + rightarg = ebox, 6.1074 + procedure = ebox_ecluster_may_overlap_commutator, 6.1075 + commutator = &&+, 6.1076 + restrict = areasel, 6.1077 + join = areajoinsel 6.1078 +); 6.1079 + 6.1080 +CREATE OPERATOR &&+ ( 6.1081 + leftarg = ecircle, 6.1082 + rightarg = ecluster, 6.1083 + procedure = ecircle_ecluster_may_overlap_proc, 6.1084 + commutator = &&+, 6.1085 + restrict = areasel, 6.1086 + join = areajoinsel 6.1087 +); 6.1088 + 6.1089 +CREATE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle) 6.1090 + RETURNS boolean 6.1091 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 &&+ $1'; 6.1092 + 6.1093 +CREATE OPERATOR &&+ ( 6.1094 + leftarg = ecluster, 6.1095 + rightarg = ecircle, 6.1096 + procedure = ecircle_ecluster_may_overlap_commutator, 6.1097 + commutator = &&+, 6.1098 + restrict = areasel, 6.1099 + join = areajoinsel 6.1100 +); 6.1101 + 6.1102 +CREATE OPERATOR &&+ ( 6.1103 + leftarg = ecluster, 6.1104 + rightarg = ecluster, 6.1105 + procedure = ecluster_may_overlap_proc, 6.1106 + commutator = &&+, 6.1107 + restrict = areasel, 6.1108 + join = areajoinsel 6.1109 +); 6.1110 + 6.1111 +CREATE OPERATOR @> ( 6.1112 + leftarg = ebox, 6.1113 + rightarg = epoint, 6.1114 + procedure = epoint_ebox_overlap_commutator, 6.1115 + commutator = <@, 6.1116 + restrict = areasel, 6.1117 + join = areajoinsel 6.1118 +); 6.1119 + 6.1120 +CREATE OPERATOR <@ ( 6.1121 + leftarg = epoint, 6.1122 + rightarg = ebox, 6.1123 + procedure = epoint_ebox_overlap_proc, 6.1124 + commutator = @>, 6.1125 + restrict = areasel, 6.1126 + join = areajoinsel 6.1127 +); 6.1128 + 6.1129 +CREATE OPERATOR @> ( 6.1130 + leftarg = ecluster, 6.1131 + rightarg = epoint, 6.1132 + procedure = epoint_ecluster_overlap_commutator, 6.1133 + commutator = <@, 6.1134 + restrict = areasel, 6.1135 + join = areajoinsel 6.1136 +); 6.1137 + 6.1138 +CREATE OPERATOR <@ ( 6.1139 + leftarg = epoint, 6.1140 + rightarg = ecluster, 6.1141 + procedure = epoint_ecluster_overlap_proc, 6.1142 + commutator = <@, 6.1143 + restrict = areasel, 6.1144 + join = areajoinsel 6.1145 +); 6.1146 + 6.1147 +CREATE OPERATOR @> ( 6.1148 + leftarg = ecluster, 6.1149 + rightarg = ecluster, 6.1150 + procedure = ecluster_contains_proc, 6.1151 + commutator = <@, 6.1152 + restrict = areasel, 6.1153 + join = areajoinsel 6.1154 +); 6.1155 + 6.1156 +CREATE FUNCTION ecluster_contains_commutator(ecluster, ecluster) 6.1157 + RETURNS boolean 6.1158 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1'; 6.1159 + 6.1160 +CREATE OPERATOR <@ ( 6.1161 + leftarg = ecluster, 6.1162 + rightarg = ecluster, 6.1163 + procedure = ecluster_contains_commutator, 6.1164 + commutator = @>, 6.1165 + restrict = areasel, 6.1166 + join = areajoinsel 6.1167 +); 6.1168 + 6.1169 +CREATE FUNCTION ebox_contains_castwrap(ebox, ebox) 6.1170 + RETURNS boolean 6.1171 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2::ecluster'; 6.1172 + 6.1173 +CREATE OPERATOR @> ( 6.1174 + leftarg = ebox, 6.1175 + rightarg = ebox, 6.1176 + procedure = ebox_contains_castwrap, 6.1177 + commutator = <@, 6.1178 + restrict = areasel, 6.1179 + join = areajoinsel 6.1180 +); 6.1181 + 6.1182 +CREATE FUNCTION ebox_contains_swapped_castwrap(ebox, ebox) 6.1183 + RETURNS boolean 6.1184 + LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1::ecluster'; 6.1185 + 6.1186 +CREATE OPERATOR <@ ( 6.1187 + leftarg = ebox, 6.1188 + rightarg = ebox, 6.1189 + procedure = ebox_contains_swapped_castwrap, 6.1190 + commutator = @>, 6.1191 + restrict = areasel, 6.1192 + join = areajoinsel 6.1193 +); 6.1194 + 6.1195 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster) 6.1196 + RETURNS boolean 6.1197 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster @> $2'; 6.1198 + 6.1199 +CREATE OPERATOR @> ( 6.1200 + leftarg = ebox, 6.1201 + rightarg = ecluster, 6.1202 + procedure = ebox_ecluster_contains_castwrap, 6.1203 + commutator = <@, 6.1204 + restrict = areasel, 6.1205 + join = areajoinsel 6.1206 +); 6.1207 + 6.1208 +CREATE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox) 6.1209 + RETURNS boolean 6.1210 + LANGUAGE sql IMMUTABLE AS 'SELECT $2::ecluster @> $1'; 6.1211 + 6.1212 +CREATE OPERATOR <@ ( 6.1213 + leftarg = ecluster, 6.1214 + rightarg = ebox, 6.1215 + procedure = ebox_ecluster_contains_castwrap, 6.1216 + commutator = @>, 6.1217 + restrict = areasel, 6.1218 + join = areajoinsel 6.1219 +); 6.1220 + 6.1221 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox) 6.1222 + RETURNS boolean 6.1223 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 @> $2::ecluster'; 6.1224 + 6.1225 +CREATE OPERATOR @> ( 6.1226 + leftarg = ecluster, 6.1227 + rightarg = ebox, 6.1228 + procedure = ecluster_ebox_contains_castwrap, 6.1229 + commutator = <@, 6.1230 + restrict = areasel, 6.1231 + join = areajoinsel 6.1232 +); 6.1233 + 6.1234 +CREATE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster) 6.1235 + RETURNS boolean 6.1236 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 @> $1::ecluster'; 6.1237 + 6.1238 +CREATE OPERATOR <@ ( 6.1239 + leftarg = ebox, 6.1240 + rightarg = ecluster, 6.1241 + procedure = ecluster_ebox_contains_castwrap, 6.1242 + commutator = @>, 6.1243 + restrict = areasel, 6.1244 + join = areajoinsel 6.1245 +); 6.1246 + 6.1247 +CREATE OPERATOR <-> ( 6.1248 + leftarg = epoint, 6.1249 + rightarg = epoint, 6.1250 + procedure = epoint_distance_proc, 6.1251 + commutator = <-> 6.1252 +); 6.1253 + 6.1254 +CREATE OPERATOR <-> ( 6.1255 + leftarg = epoint, 6.1256 + rightarg = ecircle, 6.1257 + procedure = epoint_ecircle_distance_proc, 6.1258 + commutator = <-> 6.1259 +); 6.1260 + 6.1261 +CREATE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint) 6.1262 + RETURNS float8 6.1263 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 6.1264 + 6.1265 +CREATE OPERATOR <-> ( 6.1266 + leftarg = ecircle, 6.1267 + rightarg = epoint, 6.1268 + procedure = epoint_ecircle_distance_commutator, 6.1269 + commutator = <-> 6.1270 +); 6.1271 + 6.1272 +CREATE OPERATOR <-> ( 6.1273 + leftarg = epoint, 6.1274 + rightarg = ecluster, 6.1275 + procedure = epoint_ecluster_distance_proc, 6.1276 + commutator = <-> 6.1277 +); 6.1278 + 6.1279 +CREATE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint) 6.1280 + RETURNS float8 6.1281 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 6.1282 + 6.1283 +CREATE OPERATOR <-> ( 6.1284 + leftarg = ecluster, 6.1285 + rightarg = epoint, 6.1286 + procedure = epoint_ecluster_distance_commutator, 6.1287 + commutator = <-> 6.1288 +); 6.1289 + 6.1290 +CREATE OPERATOR <-> ( 6.1291 + leftarg = ecircle, 6.1292 + rightarg = ecircle, 6.1293 + procedure = ecircle_distance_proc, 6.1294 + commutator = <-> 6.1295 +); 6.1296 + 6.1297 +CREATE OPERATOR <-> ( 6.1298 + leftarg = ecircle, 6.1299 + rightarg = ecluster, 6.1300 + procedure = ecircle_ecluster_distance_proc, 6.1301 + commutator = <-> 6.1302 +); 6.1303 + 6.1304 +CREATE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle) 6.1305 + RETURNS float8 6.1306 + LANGUAGE sql IMMUTABLE AS 'SELECT $2 <-> $1'; 6.1307 + 6.1308 +CREATE OPERATOR <-> ( 6.1309 + leftarg = ecluster, 6.1310 + rightarg = ecircle, 6.1311 + procedure = ecircle_ecluster_distance_commutator, 6.1312 + commutator = <-> 6.1313 +); 6.1314 + 6.1315 +CREATE OPERATOR <-> ( 6.1316 + leftarg = ecluster, 6.1317 + rightarg = ecluster, 6.1318 + procedure = ecluster_distance_proc, 6.1319 + commutator = <-> 6.1320 +); 6.1321 + 6.1322 +CREATE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox) 6.1323 + RETURNS float8 6.1324 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster'; 6.1325 + 6.1326 +CREATE OPERATOR <-> ( 6.1327 + leftarg = epoint, 6.1328 + rightarg = ebox, 6.1329 + procedure = epoint_ebox_distance_castwrap, 6.1330 + commutator = <-> 6.1331 +); 6.1332 + 6.1333 +CREATE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint) 6.1334 + RETURNS float8 6.1335 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2'; 6.1336 + 6.1337 +CREATE OPERATOR <-> ( 6.1338 + leftarg = ebox, 6.1339 + rightarg = epoint, 6.1340 + procedure = epoint_ebox_distance_castwrap, 6.1341 + commutator = <-> 6.1342 +); 6.1343 + 6.1344 +CREATE FUNCTION ebox_distance_castwrap(ebox, ebox) 6.1345 + RETURNS float8 6.1346 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2::ecluster'; 6.1347 + 6.1348 +CREATE OPERATOR <-> ( 6.1349 + leftarg = ebox, 6.1350 + rightarg = ebox, 6.1351 + procedure = ebox_distance_castwrap, 6.1352 + commutator = <-> 6.1353 +); 6.1354 + 6.1355 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle) 6.1356 + RETURNS float8 6.1357 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2'; 6.1358 + 6.1359 +CREATE OPERATOR <-> ( 6.1360 + leftarg = ebox, 6.1361 + rightarg = ecircle, 6.1362 + procedure = ebox_ecircle_distance_castwrap, 6.1363 + commutator = <-> 6.1364 +); 6.1365 + 6.1366 +CREATE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox) 6.1367 + RETURNS float8 6.1368 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster'; 6.1369 + 6.1370 +CREATE OPERATOR <-> ( 6.1371 + leftarg = ecircle, 6.1372 + rightarg = ebox, 6.1373 + procedure = ebox_ecircle_distance_castwrap, 6.1374 + commutator = <-> 6.1375 +); 6.1376 + 6.1377 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster) 6.1378 + RETURNS float8 6.1379 + LANGUAGE sql IMMUTABLE AS 'SELECT $1::ecluster <-> $2'; 6.1380 + 6.1381 +CREATE OPERATOR <-> ( 6.1382 + leftarg = ebox, 6.1383 + rightarg = ecluster, 6.1384 + procedure = ebox_ecluster_distance_castwrap, 6.1385 + commutator = <-> 6.1386 +); 6.1387 + 6.1388 +CREATE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox) 6.1389 + RETURNS float8 6.1390 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2::ecluster'; 6.1391 + 6.1392 +CREATE OPERATOR <-> ( 6.1393 + leftarg = ecluster, 6.1394 + rightarg = ebox, 6.1395 + procedure = ebox_ecluster_distance_castwrap, 6.1396 + commutator = <-> 6.1397 +); 6.1398 + 6.1399 +CREATE OPERATOR <=> ( 6.1400 + leftarg = ecluster, 6.1401 + rightarg = epoint_with_sample_count, 6.1402 + procedure = fair_distance_operator_proc 6.1403 +); 6.1404 + 6.1405 + 6.1406 +---------------- 6.1407 +-- GiST index -- 6.1408 +---------------- 6.1409 + 6.1410 +CREATE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal) 6.1411 + RETURNS boolean 6.1412 + LANGUAGE C STRICT 6.1413 + AS '$libdir/latlon-v0009', 'pgl_gist_consistent'; 6.1414 + 6.1415 +CREATE FUNCTION pgl_gist_union(internal, internal) 6.1416 + RETURNS internal 6.1417 + LANGUAGE C STRICT 6.1418 + AS '$libdir/latlon-v0009', 'pgl_gist_union'; 6.1419 + 6.1420 +CREATE FUNCTION pgl_gist_compress_epoint(internal) 6.1421 + RETURNS internal 6.1422 + LANGUAGE C STRICT 6.1423 + AS '$libdir/latlon-v0009', 'pgl_gist_compress_epoint'; 6.1424 + 6.1425 +CREATE FUNCTION pgl_gist_compress_ecircle(internal) 6.1426 + RETURNS internal 6.1427 + LANGUAGE C STRICT 6.1428 + AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecircle'; 6.1429 + 6.1430 +CREATE FUNCTION pgl_gist_compress_ecluster(internal) 6.1431 + RETURNS internal 6.1432 + LANGUAGE C STRICT 6.1433 + AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecluster'; 6.1434 + 6.1435 +CREATE FUNCTION pgl_gist_decompress(internal) 6.1436 + RETURNS internal 6.1437 + LANGUAGE C STRICT 6.1438 + AS '$libdir/latlon-v0009', 'pgl_gist_decompress'; 6.1439 + 6.1440 +CREATE FUNCTION pgl_gist_penalty(internal, internal, internal) 6.1441 + RETURNS internal 6.1442 + LANGUAGE C STRICT 6.1443 + AS '$libdir/latlon-v0009', 'pgl_gist_penalty'; 6.1444 + 6.1445 +CREATE FUNCTION pgl_gist_picksplit(internal, internal) 6.1446 + RETURNS internal 6.1447 + LANGUAGE C STRICT 6.1448 + AS '$libdir/latlon-v0009', 'pgl_gist_picksplit'; 6.1449 + 6.1450 +CREATE FUNCTION pgl_gist_same(internal, internal, internal) 6.1451 + RETURNS internal 6.1452 + LANGUAGE C STRICT 6.1453 + AS '$libdir/latlon-v0009', 'pgl_gist_same'; 6.1454 + 6.1455 +CREATE FUNCTION pgl_gist_distance(internal, internal, smallint, oid) 6.1456 + RETURNS internal 6.1457 + LANGUAGE C STRICT 6.1458 + AS '$libdir/latlon-v0009', 'pgl_gist_distance'; 6.1459 + 6.1460 +CREATE OPERATOR CLASS epoint_ops 6.1461 + DEFAULT FOR TYPE epoint USING gist AS 6.1462 + OPERATOR 11 = , 6.1463 + OPERATOR 22 && (epoint, ebox), 6.1464 + OPERATOR 222 <@ (epoint, ebox), 6.1465 + OPERATOR 23 && (epoint, ecircle), 6.1466 + OPERATOR 24 && (epoint, ecluster), 6.1467 + OPERATOR 124 &&+ (epoint, ecluster), 6.1468 + OPERATOR 224 <@ (epoint, ecluster), 6.1469 + OPERATOR 31 <-> (epoint, epoint) FOR ORDER BY float_ops, 6.1470 + OPERATOR 32 <-> (epoint, ebox) FOR ORDER BY float_ops, 6.1471 + OPERATOR 33 <-> (epoint, ecircle) FOR ORDER BY float_ops, 6.1472 + OPERATOR 34 <-> (epoint, ecluster) FOR ORDER BY float_ops, 6.1473 + FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 6.1474 + FUNCTION 2 pgl_gist_union(internal, internal), 6.1475 + FUNCTION 3 pgl_gist_compress_epoint(internal), 6.1476 + FUNCTION 4 pgl_gist_decompress(internal), 6.1477 + FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 6.1478 + FUNCTION 6 pgl_gist_picksplit(internal, internal), 6.1479 + FUNCTION 7 pgl_gist_same(internal, internal, internal), 6.1480 + FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 6.1481 + STORAGE ekey_point; 6.1482 + 6.1483 +CREATE OPERATOR CLASS ecircle_ops 6.1484 + DEFAULT FOR TYPE ecircle USING gist AS 6.1485 + OPERATOR 13 = , 6.1486 + OPERATOR 21 && (ecircle, epoint), 6.1487 + OPERATOR 22 && (ecircle, ebox), 6.1488 + OPERATOR 122 &&+ (ecircle, ebox), 6.1489 + OPERATOR 23 && (ecircle, ecircle), 6.1490 + OPERATOR 24 && (ecircle, ecluster), 6.1491 + OPERATOR 124 &&+ (ecircle, ecluster), 6.1492 + OPERATOR 31 <-> (ecircle, epoint) FOR ORDER BY float_ops, 6.1493 + OPERATOR 32 <-> (ecircle, ebox) FOR ORDER BY float_ops, 6.1494 + OPERATOR 33 <-> (ecircle, ecircle) FOR ORDER BY float_ops, 6.1495 + OPERATOR 34 <-> (ecircle, ecluster) FOR ORDER BY float_ops, 6.1496 + FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 6.1497 + FUNCTION 2 pgl_gist_union(internal, internal), 6.1498 + FUNCTION 3 pgl_gist_compress_ecircle(internal), 6.1499 + FUNCTION 4 pgl_gist_decompress(internal), 6.1500 + FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 6.1501 + FUNCTION 6 pgl_gist_picksplit(internal, internal), 6.1502 + FUNCTION 7 pgl_gist_same(internal, internal, internal), 6.1503 + FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 6.1504 + STORAGE ekey_area; 6.1505 + 6.1506 +CREATE OPERATOR CLASS ecluster_ops 6.1507 + DEFAULT FOR TYPE ecluster USING gist AS 6.1508 + OPERATOR 21 && (ecluster, epoint), 6.1509 + OPERATOR 121 &&+ (ecluster, epoint), 6.1510 + OPERATOR 221 @> (ecluster, epoint), 6.1511 + OPERATOR 22 && (ecluster, ebox), 6.1512 + OPERATOR 122 &&+ (ecluster, ebox), 6.1513 + OPERATOR 222 @> (ecluster, ebox), 6.1514 + OPERATOR 322 <@ (ecluster, ebox), 6.1515 + OPERATOR 23 && (ecluster, ecircle), 6.1516 + OPERATOR 123 &&+ (ecluster, ecircle), 6.1517 + OPERATOR 24 && (ecluster, ecluster), 6.1518 + OPERATOR 124 &&+ (ecluster, ecluster), 6.1519 + OPERATOR 224 @> (ecluster, ecluster), 6.1520 + OPERATOR 324 <@ (ecluster, ecluster), 6.1521 + OPERATOR 31 <-> (ecluster, epoint) FOR ORDER BY float_ops, 6.1522 + OPERATOR 32 <-> (ecluster, ebox) FOR ORDER BY float_ops, 6.1523 + OPERATOR 33 <-> (ecluster, ecircle) FOR ORDER BY float_ops, 6.1524 + OPERATOR 34 <-> (ecluster, ecluster) FOR ORDER BY float_ops, 6.1525 + OPERATOR 131 <=> (ecluster, epoint_with_sample_count) FOR ORDER BY float_ops, 6.1526 + FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 6.1527 + FUNCTION 2 pgl_gist_union(internal, internal), 6.1528 + FUNCTION 3 pgl_gist_compress_ecluster(internal), 6.1529 + FUNCTION 4 pgl_gist_decompress(internal), 6.1530 + FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 6.1531 + FUNCTION 6 pgl_gist_picksplit(internal, internal), 6.1532 + FUNCTION 7 pgl_gist_same(internal, internal, internal), 6.1533 + FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 6.1534 + STORAGE ekey_area; 6.1535 + 6.1536 + 6.1537 +--------------------- 6.1538 +-- alias functions -- 6.1539 +--------------------- 6.1540 + 6.1541 +CREATE FUNCTION distance(epoint, epoint) 6.1542 + RETURNS float8 6.1543 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2'; 6.1544 + 6.1545 +CREATE FUNCTION distance(ecluster, epoint) 6.1546 + RETURNS float8 6.1547 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <-> $2'; 6.1548 + 6.1549 +CREATE FUNCTION distance_within(epoint, epoint, float8) 6.1550 + RETURNS boolean 6.1551 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)'; 6.1552 + 6.1553 +CREATE FUNCTION distance_within(ecluster, epoint, float8) 6.1554 + RETURNS boolean 6.1555 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 && ecircle($2, $3)'; 6.1556 + 6.1557 +CREATE FUNCTION fair_distance(ecluster, epoint, int4 = 10000) 6.1558 + RETURNS float8 6.1559 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <=> epoint_with_sample_count($2, $3)'; 6.1560 + 6.1561 + 6.1562 +-------------------------------- 6.1563 +-- other data storage formats -- 6.1564 +-------------------------------- 6.1565 + 6.1566 +CREATE FUNCTION coords_to_epoint(float8, float8, text = 'epoint') 6.1567 + RETURNS epoint 6.1568 + LANGUAGE plpgsql IMMUTABLE STRICT AS $$ 6.1569 + DECLARE 6.1570 + "result" epoint; 6.1571 + BEGIN 6.1572 + IF $3 = 'epoint_lonlat' THEN 6.1573 + -- avoid dynamic command execution for better performance 6.1574 + RETURN epoint($2, $1); 6.1575 + END IF; 6.1576 + IF $3 = 'epoint' OR $3 = 'epoint_latlon' THEN 6.1577 + -- avoid dynamic command execution for better performance 6.1578 + RETURN epoint($1, $2); 6.1579 + END IF; 6.1580 + EXECUTE 'SELECT ' || $3 || '($1, $2)' INTO STRICT "result" USING $1, $2; 6.1581 + RETURN "result"; 6.1582 + END; 6.1583 + $$; 6.1584 + 6.1585 +CREATE FUNCTION GeoJSON_LinearRing_vertices(jsonb, text = 'epoint_lonlat') 6.1586 + RETURNS SETOF jsonb 6.1587 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.1588 + SELECT "result" FROM 6.1589 + ( SELECT jsonb_array_length($1) - 1 ) AS "lastindex_row" ("lastindex") 6.1590 + CROSS JOIN LATERAL jsonb_array_elements( 6.1591 + CASE WHEN 6.1592 + coords_to_epoint( 6.1593 + ($1->0->>0)::float8, 6.1594 + ($1->0->>1)::float8, 6.1595 + $2 6.1596 + ) = coords_to_epoint( 6.1597 + ($1->"lastindex"->>0)::float8, 6.1598 + ($1->"lastindex"->>1)::float8, 6.1599 + $2 6.1600 + ) 6.1601 + THEN 6.1602 + $1 - "lastindex" 6.1603 + ELSE 6.1604 + $1 6.1605 + END 6.1606 + ) AS "result_row" ("result") 6.1607 + $$; 6.1608 + 6.1609 +CREATE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat') 6.1610 + RETURNS epoint 6.1611 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.1612 + SELECT CASE 6.1613 + WHEN $1->>'type' = 'Point' THEN 6.1614 + coords_to_epoint( 6.1615 + ($1->'coordinates'->>0)::float8, 6.1616 + ($1->'coordinates'->>1)::float8, 6.1617 + $2 6.1618 + ) 6.1619 + WHEN $1->>'type' = 'Feature' THEN 6.1620 + GeoJSON_to_epoint($1->'geometry', $2) 6.1621 + ELSE 6.1622 + NULL 6.1623 + END 6.1624 + $$; 6.1625 + 6.1626 +CREATE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat') 6.1627 + RETURNS ecluster 6.1628 + LANGUAGE sql IMMUTABLE STRICT AS $$ 6.1629 + SELECT CASE $1->>'type' 6.1630 + WHEN 'Point' THEN 6.1631 + coords_to_epoint( 6.1632 + ($1->'coordinates'->>0)::float8, 6.1633 + ($1->'coordinates'->>1)::float8, 6.1634 + $2 6.1635 + )::ecluster 6.1636 + WHEN 'MultiPoint' THEN 6.1637 + ( SELECT ecluster_create_multipoint(array_agg( 6.1638 + coords_to_epoint( 6.1639 + ("coord"->>0)::float8, 6.1640 + ("coord"->>1)::float8, 6.1641 + $2 6.1642 + ) 6.1643 + )) 6.1644 + FROM jsonb_array_elements($1->'coordinates') AS "coord" 6.1645 + ) 6.1646 + WHEN 'LineString' THEN 6.1647 + ( SELECT ecluster_create_path(array_agg( 6.1648 + coords_to_epoint( 6.1649 + ("coord"->>0)::float8, 6.1650 + ("coord"->>1)::float8, 6.1651 + $2 6.1652 + ) 6.1653 + )) 6.1654 + FROM jsonb_array_elements($1->'coordinates') AS "coord" 6.1655 + ) 6.1656 + WHEN 'MultiLineString' THEN 6.1657 + ( SELECT ecluster_concat(array_agg( 6.1658 + ( SELECT ecluster_create_path(array_agg( 6.1659 + coords_to_epoint( 6.1660 + ("coord"->>0)::float8, 6.1661 + ("coord"->>1)::float8, 6.1662 + $2 6.1663 + ) 6.1664 + )) 6.1665 + FROM jsonb_array_elements("coord_array") AS "coord" 6.1666 + ) 6.1667 + )) 6.1668 + FROM jsonb_array_elements($1->'coordinates') AS "coord_array" 6.1669 + ) 6.1670 + WHEN 'Polygon' THEN 6.1671 + ( SELECT ecluster_concat(array_agg( 6.1672 + ( SELECT ecluster_create_polygon(array_agg( 6.1673 + coords_to_epoint( 6.1674 + ("coord"->>0)::float8, 6.1675 + ("coord"->>1)::float8, 6.1676 + $2 6.1677 + ) 6.1678 + )) 6.1679 + FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord" 6.1680 + ) 6.1681 + )) 6.1682 + FROM jsonb_array_elements($1->'coordinates') AS "coord_array" 6.1683 + ) 6.1684 + WHEN 'MultiPolygon' THEN 6.1685 + ( SELECT ecluster_concat(array_agg( 6.1686 + ( SELECT ecluster_concat(array_agg( 6.1687 + ( SELECT ecluster_create_polygon(array_agg( 6.1688 + coords_to_epoint( 6.1689 + ("coord"->>0)::float8, 6.1690 + ("coord"->>1)::float8, 6.1691 + $2 6.1692 + ) 6.1693 + )) 6.1694 + FROM GeoJSON_LinearRing_vertices("coord_array", $2) AS "coord" 6.1695 + ) 6.1696 + )) 6.1697 + FROM jsonb_array_elements("coord_array_array") AS "coord_array" 6.1698 + ) 6.1699 + )) 6.1700 + FROM jsonb_array_elements($1->'coordinates') AS "coord_array_array" 6.1701 + ) 6.1702 + WHEN 'GeometryCollection' THEN 6.1703 + ( SELECT ecluster_concat(array_agg( 6.1704 + GeoJSON_to_ecluster("geometry", $2) 6.1705 + )) 6.1706 + FROM jsonb_array_elements($1->'geometries') AS "geometry" 6.1707 + ) 6.1708 + WHEN 'Feature' THEN 6.1709 + GeoJSON_to_ecluster($1->'geometry', $2) 6.1710 + WHEN 'FeatureCollection' THEN 6.1711 + ( SELECT ecluster_concat(array_agg( 6.1712 + GeoJSON_to_ecluster("feature", $2) 6.1713 + )) 6.1714 + FROM jsonb_array_elements($1->'features') AS "feature" 6.1715 + ) 6.1716 + ELSE 6.1717 + NULL 6.1718 + END 6.1719 + $$; 6.1720 +
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/latlon--0.9--0.10.sql Mon Oct 31 13:06:31 2016 +0100 7.3 @@ -0,0 +1,504 @@ 7.4 + 7.5 +CREATE TYPE epoint_with_sample_count; 7.6 + 7.7 +CREATE OR REPLACE FUNCTION ekey_point_in_dummy(cstring) 7.8 + RETURNS ekey_point 7.9 + LANGUAGE C IMMUTABLE STRICT 7.10 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 7.11 + 7.12 +CREATE OR REPLACE FUNCTION ekey_point_out_dummy(ekey_point) 7.13 + RETURNS cstring 7.14 + LANGUAGE C IMMUTABLE STRICT 7.15 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 7.16 + 7.17 +CREATE OR REPLACE FUNCTION ekey_area_in_dummy(cstring) 7.18 + RETURNS ekey_area 7.19 + LANGUAGE C IMMUTABLE STRICT 7.20 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 7.21 + 7.22 +CREATE OR REPLACE FUNCTION ekey_area_out_dummy(ekey_area) 7.23 + RETURNS cstring 7.24 + LANGUAGE C IMMUTABLE STRICT 7.25 + AS '$libdir/latlon-v0009', 'pgl_notimpl'; 7.26 + 7.27 +CREATE OR REPLACE FUNCTION epoint_in(cstring) 7.28 + RETURNS epoint 7.29 + LANGUAGE C IMMUTABLE STRICT 7.30 + AS '$libdir/latlon-v0009', 'pgl_epoint_in'; 7.31 + 7.32 +CREATE FUNCTION epoint_with_sample_count_in(cstring) 7.33 + RETURNS epoint_with_sample_count 7.34 + LANGUAGE C IMMUTABLE STRICT 7.35 + AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_in'; 7.36 + 7.37 +CREATE OR REPLACE FUNCTION ebox_in(cstring) 7.38 + RETURNS ebox 7.39 + LANGUAGE C IMMUTABLE STRICT 7.40 + AS '$libdir/latlon-v0009', 'pgl_ebox_in'; 7.41 + 7.42 +CREATE OR REPLACE FUNCTION ecircle_in(cstring) 7.43 + RETURNS ecircle 7.44 + LANGUAGE C IMMUTABLE STRICT 7.45 + AS '$libdir/latlon-v0009', 'pgl_ecircle_in'; 7.46 + 7.47 +CREATE OR REPLACE FUNCTION ecluster_in(cstring) 7.48 + RETURNS ecluster 7.49 + LANGUAGE C IMMUTABLE STRICT 7.50 + AS '$libdir/latlon-v0009', 'pgl_ecluster_in'; 7.51 + 7.52 +CREATE OR REPLACE FUNCTION epoint_out(epoint) 7.53 + RETURNS cstring 7.54 + LANGUAGE C IMMUTABLE STRICT 7.55 + AS '$libdir/latlon-v0009', 'pgl_epoint_out'; 7.56 + 7.57 +CREATE FUNCTION epoint_with_sample_count_out(epoint_with_sample_count) 7.58 + RETURNS cstring 7.59 + LANGUAGE C IMMUTABLE STRICT 7.60 + AS '$libdir/latlon-v0009', 'pgl_epoint_with_sample_count_out'; 7.61 + 7.62 +CREATE OR REPLACE FUNCTION ebox_out(ebox) 7.63 + RETURNS cstring 7.64 + LANGUAGE C IMMUTABLE STRICT 7.65 + AS '$libdir/latlon-v0009', 'pgl_ebox_out'; 7.66 + 7.67 +CREATE OR REPLACE FUNCTION ecircle_out(ecircle) 7.68 + RETURNS cstring 7.69 + LANGUAGE C IMMUTABLE STRICT 7.70 + AS '$libdir/latlon-v0009', 'pgl_ecircle_out'; 7.71 + 7.72 +CREATE OR REPLACE FUNCTION ecluster_out(ecluster) 7.73 + RETURNS cstring 7.74 + LANGUAGE C IMMUTABLE STRICT 7.75 + AS '$libdir/latlon-v0009', 'pgl_ecluster_out'; 7.76 + 7.77 +CREATE OR REPLACE FUNCTION epoint_recv(internal) 7.78 + RETURNS epoint 7.79 + LANGUAGE C IMMUTABLE STRICT 7.80 + AS '$libdir/latlon-v0009', 'pgl_epoint_recv'; 7.81 + 7.82 +CREATE OR REPLACE FUNCTION ebox_recv(internal) 7.83 + RETURNS ebox 7.84 + LANGUAGE C IMMUTABLE STRICT 7.85 + AS '$libdir/latlon-v0009', 'pgl_ebox_recv'; 7.86 + 7.87 +CREATE OR REPLACE FUNCTION ecircle_recv(internal) 7.88 + RETURNS ecircle 7.89 + LANGUAGE C IMMUTABLE STRICT 7.90 + AS '$libdir/latlon-v0009', 'pgl_ecircle_recv'; 7.91 + 7.92 +CREATE OR REPLACE FUNCTION epoint_send(epoint) 7.93 + RETURNS bytea 7.94 + LANGUAGE C IMMUTABLE STRICT 7.95 + AS '$libdir/latlon-v0009', 'pgl_epoint_send'; 7.96 + 7.97 +CREATE OR REPLACE FUNCTION ebox_send(ebox) 7.98 + RETURNS bytea 7.99 + LANGUAGE C IMMUTABLE STRICT 7.100 + AS '$libdir/latlon-v0009', 'pgl_ebox_send'; 7.101 + 7.102 +CREATE OR REPLACE FUNCTION ecircle_send(ecircle) 7.103 + RETURNS bytea 7.104 + LANGUAGE C IMMUTABLE STRICT 7.105 + AS '$libdir/latlon-v0009', 'pgl_ecircle_send'; 7.106 + 7.107 +CREATE TYPE epoint_with_sample_count ( 7.108 + internallength = 20, 7.109 + input = epoint_with_sample_count_in, 7.110 + output = epoint_with_sample_count_out, 7.111 + alignment = double ); 7.112 + 7.113 +CREATE OR REPLACE FUNCTION epoint_btree_lt(epoint, epoint) 7.114 + RETURNS boolean 7.115 + LANGUAGE C IMMUTABLE STRICT 7.116 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_lt'; 7.117 + 7.118 +CREATE OR REPLACE FUNCTION epoint_btree_le(epoint, epoint) 7.119 + RETURNS boolean 7.120 + LANGUAGE C IMMUTABLE STRICT 7.121 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_le'; 7.122 + 7.123 +CREATE OR REPLACE FUNCTION epoint_btree_eq(epoint, epoint) 7.124 + RETURNS boolean 7.125 + LANGUAGE C IMMUTABLE STRICT 7.126 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_eq'; 7.127 + 7.128 +CREATE OR REPLACE FUNCTION epoint_btree_ne(epoint, epoint) 7.129 + RETURNS boolean 7.130 + LANGUAGE C IMMUTABLE STRICT 7.131 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ne'; 7.132 + 7.133 +CREATE OR REPLACE FUNCTION epoint_btree_ge(epoint, epoint) 7.134 + RETURNS boolean 7.135 + LANGUAGE C IMMUTABLE STRICT 7.136 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_ge'; 7.137 + 7.138 +CREATE OR REPLACE FUNCTION epoint_btree_gt(epoint, epoint) 7.139 + RETURNS boolean 7.140 + LANGUAGE C IMMUTABLE STRICT 7.141 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_gt'; 7.142 + 7.143 +CREATE OR REPLACE FUNCTION epoint_btree_cmp(epoint, epoint) 7.144 + RETURNS int4 7.145 + LANGUAGE C IMMUTABLE STRICT 7.146 + AS '$libdir/latlon-v0009', 'pgl_btree_epoint_cmp'; 7.147 + 7.148 +CREATE OR REPLACE FUNCTION ebox_btree_lt(ebox, ebox) 7.149 + RETURNS boolean 7.150 + LANGUAGE C IMMUTABLE STRICT 7.151 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_lt'; 7.152 + 7.153 +CREATE OR REPLACE FUNCTION ebox_btree_le(ebox, ebox) 7.154 + RETURNS boolean 7.155 + LANGUAGE C IMMUTABLE STRICT 7.156 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_le'; 7.157 + 7.158 +CREATE OR REPLACE FUNCTION ebox_btree_eq(ebox, ebox) 7.159 + RETURNS boolean 7.160 + LANGUAGE C IMMUTABLE STRICT 7.161 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_eq'; 7.162 + 7.163 +CREATE OR REPLACE FUNCTION ebox_btree_ne(ebox, ebox) 7.164 + RETURNS boolean 7.165 + LANGUAGE C IMMUTABLE STRICT 7.166 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ne'; 7.167 + 7.168 +CREATE OR REPLACE FUNCTION ebox_btree_ge(ebox, ebox) 7.169 + RETURNS boolean 7.170 + LANGUAGE C IMMUTABLE STRICT 7.171 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_ge'; 7.172 + 7.173 +CREATE OR REPLACE FUNCTION ebox_btree_gt(ebox, ebox) 7.174 + RETURNS boolean 7.175 + LANGUAGE C IMMUTABLE STRICT 7.176 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_gt'; 7.177 + 7.178 +CREATE OR REPLACE FUNCTION ebox_btree_cmp(ebox, ebox) 7.179 + RETURNS int4 7.180 + LANGUAGE C IMMUTABLE STRICT 7.181 + AS '$libdir/latlon-v0009', 'pgl_btree_ebox_cmp'; 7.182 + 7.183 +CREATE OR REPLACE FUNCTION ecircle_btree_lt(ecircle, ecircle) 7.184 + RETURNS boolean 7.185 + LANGUAGE C IMMUTABLE STRICT 7.186 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_lt'; 7.187 + 7.188 +CREATE OR REPLACE FUNCTION ecircle_btree_le(ecircle, ecircle) 7.189 + RETURNS boolean 7.190 + LANGUAGE C IMMUTABLE STRICT 7.191 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_le'; 7.192 + 7.193 +CREATE OR REPLACE FUNCTION ecircle_btree_eq(ecircle, ecircle) 7.194 + RETURNS boolean 7.195 + LANGUAGE C IMMUTABLE STRICT 7.196 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_eq'; 7.197 + 7.198 +CREATE OR REPLACE FUNCTION ecircle_btree_ne(ecircle, ecircle) 7.199 + RETURNS boolean 7.200 + LANGUAGE C IMMUTABLE STRICT 7.201 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ne'; 7.202 + 7.203 +CREATE OR REPLACE FUNCTION ecircle_btree_ge(ecircle, ecircle) 7.204 + RETURNS boolean 7.205 + LANGUAGE C IMMUTABLE STRICT 7.206 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_ge'; 7.207 + 7.208 +CREATE OR REPLACE FUNCTION ecircle_btree_gt(ecircle, ecircle) 7.209 + RETURNS boolean 7.210 + LANGUAGE C IMMUTABLE STRICT 7.211 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_gt'; 7.212 + 7.213 +CREATE OR REPLACE FUNCTION ecircle_btree_cmp(ecircle, ecircle) 7.214 + RETURNS int4 7.215 + LANGUAGE C IMMUTABLE STRICT 7.216 + AS '$libdir/latlon-v0009', 'pgl_btree_ecircle_cmp'; 7.217 + 7.218 +CREATE OR REPLACE FUNCTION cast_epoint_to_ebox(epoint) 7.219 + RETURNS ebox 7.220 + LANGUAGE C IMMUTABLE STRICT 7.221 + AS '$libdir/latlon-v0009', 'pgl_epoint_to_ebox'; 7.222 + 7.223 +CREATE OR REPLACE FUNCTION cast_epoint_to_ecircle(epoint) 7.224 + RETURNS ecircle 7.225 + LANGUAGE C IMMUTABLE STRICT 7.226 + AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecircle'; 7.227 + 7.228 +CREATE OR REPLACE FUNCTION cast_epoint_to_ecluster(epoint) 7.229 + RETURNS ecluster 7.230 + LANGUAGE C IMMUTABLE STRICT 7.231 + AS '$libdir/latlon-v0009', 'pgl_epoint_to_ecluster'; 7.232 + 7.233 +CREATE OR REPLACE FUNCTION cast_ebox_to_ecluster(ebox) 7.234 + RETURNS ecluster 7.235 + LANGUAGE C IMMUTABLE STRICT 7.236 + AS '$libdir/latlon-v0009', 'pgl_ebox_to_ecluster'; 7.237 + 7.238 +CREATE OR REPLACE FUNCTION epoint(float8, float8) 7.239 + RETURNS epoint 7.240 + LANGUAGE C IMMUTABLE STRICT 7.241 + AS '$libdir/latlon-v0009', 'pgl_create_epoint'; 7.242 + 7.243 +CREATE FUNCTION epoint_with_sample_count(epoint, int4) 7.244 + RETURNS epoint_with_sample_count 7.245 + LANGUAGE C IMMUTABLE STRICT 7.246 + AS '$libdir/latlon-v0009', 'pgl_create_epoint_with_sample_count'; 7.247 + 7.248 +CREATE OR REPLACE FUNCTION empty_ebox() 7.249 + RETURNS ebox 7.250 + LANGUAGE C IMMUTABLE STRICT 7.251 + AS '$libdir/latlon-v0009', 'pgl_create_empty_ebox'; 7.252 + 7.253 +CREATE OR REPLACE FUNCTION ebox(float8, float8, float8, float8) 7.254 + RETURNS ebox 7.255 + LANGUAGE C IMMUTABLE STRICT 7.256 + AS '$libdir/latlon-v0009', 'pgl_create_ebox'; 7.257 + 7.258 +CREATE OR REPLACE FUNCTION ebox(epoint, epoint) 7.259 + RETURNS ebox 7.260 + LANGUAGE C IMMUTABLE STRICT 7.261 + AS '$libdir/latlon-v0009', 'pgl_create_ebox_from_epoints'; 7.262 + 7.263 +CREATE OR REPLACE FUNCTION ecircle(float8, float8, float8) 7.264 + RETURNS ecircle 7.265 + LANGUAGE C IMMUTABLE STRICT 7.266 + AS '$libdir/latlon-v0009', 'pgl_create_ecircle'; 7.267 + 7.268 +CREATE OR REPLACE FUNCTION ecircle(epoint, float8) 7.269 + RETURNS ecircle 7.270 + LANGUAGE C IMMUTABLE STRICT 7.271 + AS '$libdir/latlon-v0009', 'pgl_create_ecircle_from_epoint'; 7.272 + 7.273 +CREATE OR REPLACE FUNCTION latitude(epoint) 7.274 + RETURNS float8 7.275 + LANGUAGE C IMMUTABLE STRICT 7.276 + AS '$libdir/latlon-v0009', 'pgl_epoint_lat'; 7.277 + 7.278 +CREATE OR REPLACE FUNCTION longitude(epoint) 7.279 + RETURNS float8 7.280 + LANGUAGE C IMMUTABLE STRICT 7.281 + AS '$libdir/latlon-v0009', 'pgl_epoint_lon'; 7.282 + 7.283 +CREATE OR REPLACE FUNCTION min_latitude(ebox) 7.284 + RETURNS float8 7.285 + LANGUAGE C IMMUTABLE STRICT 7.286 + AS '$libdir/latlon-v0009', 'pgl_ebox_lat_min'; 7.287 + 7.288 +CREATE OR REPLACE FUNCTION max_latitude(ebox) 7.289 + RETURNS float8 7.290 + LANGUAGE C IMMUTABLE STRICT 7.291 + AS '$libdir/latlon-v0009', 'pgl_ebox_lat_max'; 7.292 + 7.293 +CREATE OR REPLACE FUNCTION min_longitude(ebox) 7.294 + RETURNS float8 7.295 + LANGUAGE C IMMUTABLE STRICT 7.296 + AS '$libdir/latlon-v0009', 'pgl_ebox_lon_min'; 7.297 + 7.298 +CREATE OR REPLACE FUNCTION max_longitude(ebox) 7.299 + RETURNS float8 7.300 + LANGUAGE C IMMUTABLE STRICT 7.301 + AS '$libdir/latlon-v0009', 'pgl_ebox_lon_max'; 7.302 + 7.303 +CREATE OR REPLACE FUNCTION center(ecircle) 7.304 + RETURNS epoint 7.305 + LANGUAGE C IMMUTABLE STRICT 7.306 + AS '$libdir/latlon-v0009', 'pgl_ecircle_center'; 7.307 + 7.308 +CREATE OR REPLACE FUNCTION radius(ecircle) 7.309 + RETURNS float8 7.310 + LANGUAGE C IMMUTABLE STRICT 7.311 + AS '$libdir/latlon-v0009', 'pgl_ecircle_radius'; 7.312 + 7.313 +CREATE OR REPLACE FUNCTION epoint_ebox_overlap_proc(epoint, ebox) 7.314 + RETURNS boolean 7.315 + LANGUAGE C IMMUTABLE STRICT 7.316 + AS '$libdir/latlon-v0009', 'pgl_epoint_ebox_overlap'; 7.317 + 7.318 +CREATE OR REPLACE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle) 7.319 + RETURNS boolean 7.320 + LANGUAGE C IMMUTABLE STRICT 7.321 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_overlap'; 7.322 + 7.323 +CREATE OR REPLACE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster) 7.324 + RETURNS boolean 7.325 + LANGUAGE C IMMUTABLE STRICT 7.326 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_overlap'; 7.327 + 7.328 +CREATE OR REPLACE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster) 7.329 + RETURNS boolean 7.330 + LANGUAGE C IMMUTABLE STRICT 7.331 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_may_overlap'; 7.332 + 7.333 +CREATE OR REPLACE FUNCTION ebox_overlap_proc(ebox, ebox) 7.334 + RETURNS boolean 7.335 + LANGUAGE C IMMUTABLE STRICT 7.336 + AS '$libdir/latlon-v0009', 'pgl_ebox_overlap'; 7.337 + 7.338 +CREATE OR REPLACE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle) 7.339 + RETURNS boolean 7.340 + LANGUAGE C IMMUTABLE STRICT 7.341 + AS '$libdir/latlon-v0009', 'pgl_ebox_ecircle_may_overlap'; 7.342 + 7.343 +CREATE OR REPLACE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster) 7.344 + RETURNS boolean 7.345 + LANGUAGE C IMMUTABLE STRICT 7.346 + AS '$libdir/latlon-v0009', 'pgl_ebox_ecluster_may_overlap'; 7.347 + 7.348 +CREATE OR REPLACE FUNCTION ecircle_overlap_proc(ecircle, ecircle) 7.349 + RETURNS boolean 7.350 + LANGUAGE C IMMUTABLE STRICT 7.351 + AS '$libdir/latlon-v0009', 'pgl_ecircle_overlap'; 7.352 + 7.353 +CREATE OR REPLACE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster) 7.354 + RETURNS boolean 7.355 + LANGUAGE C IMMUTABLE STRICT 7.356 + AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_overlap'; 7.357 + 7.358 +CREATE OR REPLACE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster) 7.359 + RETURNS boolean 7.360 + LANGUAGE C IMMUTABLE STRICT 7.361 + AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_may_overlap'; 7.362 + 7.363 +CREATE OR REPLACE FUNCTION ecluster_overlap_proc(ecluster, ecluster) 7.364 + RETURNS boolean 7.365 + LANGUAGE C IMMUTABLE STRICT 7.366 + AS '$libdir/latlon-v0009', 'pgl_ecluster_overlap'; 7.367 + 7.368 +CREATE OR REPLACE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster) 7.369 + RETURNS boolean 7.370 + LANGUAGE C IMMUTABLE STRICT 7.371 + AS '$libdir/latlon-v0009', 'pgl_ecluster_may_overlap'; 7.372 + 7.373 +CREATE OR REPLACE FUNCTION ecluster_contains_proc(ecluster, ecluster) 7.374 + RETURNS boolean 7.375 + LANGUAGE C IMMUTABLE STRICT 7.376 + AS '$libdir/latlon-v0009', 'pgl_ecluster_contains'; 7.377 + 7.378 +CREATE OR REPLACE FUNCTION epoint_distance_proc(epoint, epoint) 7.379 + RETURNS float8 7.380 + LANGUAGE C IMMUTABLE STRICT 7.381 + AS '$libdir/latlon-v0009', 'pgl_epoint_distance'; 7.382 + 7.383 +CREATE OR REPLACE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle) 7.384 + RETURNS float8 7.385 + LANGUAGE C IMMUTABLE STRICT 7.386 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecircle_distance'; 7.387 + 7.388 +CREATE OR REPLACE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster) 7.389 + RETURNS float8 7.390 + LANGUAGE C IMMUTABLE STRICT 7.391 + AS '$libdir/latlon-v0009', 'pgl_epoint_ecluster_distance'; 7.392 + 7.393 +CREATE OR REPLACE FUNCTION ecircle_distance_proc(ecircle, ecircle) 7.394 + RETURNS float8 7.395 + LANGUAGE C IMMUTABLE STRICT 7.396 + AS '$libdir/latlon-v0009', 'pgl_ecircle_distance'; 7.397 + 7.398 +CREATE OR REPLACE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster) 7.399 + RETURNS float8 7.400 + LANGUAGE C IMMUTABLE STRICT 7.401 + AS '$libdir/latlon-v0009', 'pgl_ecircle_ecluster_distance'; 7.402 + 7.403 +CREATE OR REPLACE FUNCTION ecluster_distance_proc(ecluster, ecluster) 7.404 + RETURNS float8 7.405 + LANGUAGE C IMMUTABLE STRICT 7.406 + AS '$libdir/latlon-v0009', 'pgl_ecluster_distance'; 7.407 + 7.408 +DROP FUNCTION monte_carlo_area(ecluster, int4); 7.409 + 7.410 +CREATE FUNCTION fair_distance_operator_proc(ecluster, epoint_with_sample_count) 7.411 + RETURNS float8 7.412 + LANGUAGE C IMMUTABLE STRICT 7.413 + AS '$libdir/latlon-v0009', 'pgl_ecluster_epoint_sc_fair_distance'; 7.414 + 7.415 +CREATE OPERATOR <=> ( 7.416 + leftarg = ecluster, 7.417 + rightarg = epoint_with_sample_count, 7.418 + procedure = fair_distance_operator_proc 7.419 +); 7.420 + 7.421 +CREATE OR REPLACE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal) 7.422 + RETURNS boolean 7.423 + LANGUAGE C STRICT 7.424 + AS '$libdir/latlon-v0009', 'pgl_gist_consistent'; 7.425 + 7.426 +CREATE OR REPLACE FUNCTION pgl_gist_union(internal, internal) 7.427 + RETURNS internal 7.428 + LANGUAGE C STRICT 7.429 + AS '$libdir/latlon-v0009', 'pgl_gist_union'; 7.430 + 7.431 +CREATE OR REPLACE FUNCTION pgl_gist_compress_epoint(internal) 7.432 + RETURNS internal 7.433 + LANGUAGE C STRICT 7.434 + AS '$libdir/latlon-v0009', 'pgl_gist_compress_epoint'; 7.435 + 7.436 +CREATE OR REPLACE FUNCTION pgl_gist_compress_ecircle(internal) 7.437 + RETURNS internal 7.438 + LANGUAGE C STRICT 7.439 + AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecircle'; 7.440 + 7.441 +CREATE OR REPLACE FUNCTION pgl_gist_compress_ecluster(internal) 7.442 + RETURNS internal 7.443 + LANGUAGE C STRICT 7.444 + AS '$libdir/latlon-v0009', 'pgl_gist_compress_ecluster'; 7.445 + 7.446 +CREATE OR REPLACE FUNCTION pgl_gist_decompress(internal) 7.447 + RETURNS internal 7.448 + LANGUAGE C STRICT 7.449 + AS '$libdir/latlon-v0009', 'pgl_gist_decompress'; 7.450 + 7.451 +CREATE OR REPLACE FUNCTION pgl_gist_penalty(internal, internal, internal) 7.452 + RETURNS internal 7.453 + LANGUAGE C STRICT 7.454 + AS '$libdir/latlon-v0009', 'pgl_gist_penalty'; 7.455 + 7.456 +CREATE OR REPLACE FUNCTION pgl_gist_picksplit(internal, internal) 7.457 + RETURNS internal 7.458 + LANGUAGE C STRICT 7.459 + AS '$libdir/latlon-v0009', 'pgl_gist_picksplit'; 7.460 + 7.461 +CREATE OR REPLACE FUNCTION pgl_gist_same(internal, internal, internal) 7.462 + RETURNS internal 7.463 + LANGUAGE C STRICT 7.464 + AS '$libdir/latlon-v0009', 'pgl_gist_same'; 7.465 + 7.466 +CREATE OR REPLACE FUNCTION pgl_gist_distance(internal, internal, smallint, oid) 7.467 + RETURNS internal 7.468 + LANGUAGE C STRICT 7.469 + AS '$libdir/latlon-v0009', 'pgl_gist_distance'; 7.470 + 7.471 +DROP OPERATOR CLASS ecluster_ops USING gist; 7.472 + 7.473 +CREATE OPERATOR CLASS ecluster_ops 7.474 + DEFAULT FOR TYPE ecluster USING gist AS 7.475 + OPERATOR 21 && (ecluster, epoint), 7.476 + OPERATOR 121 &&+ (ecluster, epoint), 7.477 + OPERATOR 221 @> (ecluster, epoint), 7.478 + OPERATOR 22 && (ecluster, ebox), 7.479 + OPERATOR 122 &&+ (ecluster, ebox), 7.480 + OPERATOR 222 @> (ecluster, ebox), 7.481 + OPERATOR 322 <@ (ecluster, ebox), 7.482 + OPERATOR 23 && (ecluster, ecircle), 7.483 + OPERATOR 123 &&+ (ecluster, ecircle), 7.484 + OPERATOR 24 && (ecluster, ecluster), 7.485 + OPERATOR 124 &&+ (ecluster, ecluster), 7.486 + OPERATOR 224 @> (ecluster, ecluster), 7.487 + OPERATOR 324 <@ (ecluster, ecluster), 7.488 + OPERATOR 31 <-> (ecluster, epoint) FOR ORDER BY float_ops, 7.489 + OPERATOR 32 <-> (ecluster, ebox) FOR ORDER BY float_ops, 7.490 + OPERATOR 33 <-> (ecluster, ecircle) FOR ORDER BY float_ops, 7.491 + OPERATOR 34 <-> (ecluster, ecluster) FOR ORDER BY float_ops, 7.492 + OPERATOR 131 <=> (ecluster, epoint_with_sample_count) FOR ORDER BY float_ops, 7.493 + FUNCTION 1 pgl_gist_consistent(internal, internal, smallint, oid, internal), 7.494 + FUNCTION 2 pgl_gist_union(internal, internal), 7.495 + FUNCTION 3 pgl_gist_compress_ecluster(internal), 7.496 + FUNCTION 4 pgl_gist_decompress(internal), 7.497 + FUNCTION 5 pgl_gist_penalty(internal, internal, internal), 7.498 + FUNCTION 6 pgl_gist_picksplit(internal, internal), 7.499 + FUNCTION 7 pgl_gist_same(internal, internal, internal), 7.500 + FUNCTION 8 pgl_gist_distance(internal, internal, smallint, oid), 7.501 + STORAGE ekey_area; 7.502 + 7.503 +CREATE OR REPLACE FUNCTION fair_distance(ecluster, epoint, int4 = 10000) 7.504 + RETURNS float8 7.505 + LANGUAGE sql IMMUTABLE AS 'SELECT $1 <=> epoint_with_sample_count($2, $3)'; 7.506 + 7.507 +
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/latlon-v0009.c Mon Oct 31 13:06:31 2016 +0100 8.3 @@ -0,0 +1,3352 @@ 8.4 + 8.5 +/*-------------* 8.6 + * C prelude * 8.7 + *-------------*/ 8.8 + 8.9 +#include "postgres.h" 8.10 +#include "fmgr.h" 8.11 +#include "libpq/pqformat.h" 8.12 +#include "access/gist.h" 8.13 +#include "access/stratnum.h" 8.14 +#include "utils/array.h" 8.15 +#include <math.h> 8.16 + 8.17 +#ifdef PG_MODULE_MAGIC 8.18 +PG_MODULE_MAGIC; 8.19 +#endif 8.20 + 8.21 +#if INT_MAX < 2147483647 8.22 +#error Expected int type to be at least 32 bit wide 8.23 +#endif 8.24 + 8.25 + 8.26 +/*---------------------------------* 8.27 + * distance calculation on earth * 8.28 + * (using WGS-84 spheroid) * 8.29 + *---------------------------------*/ 8.30 + 8.31 +/* WGS-84 spheroid with following parameters: 8.32 + semi-major axis a = 6378137 8.33 + semi-minor axis b = a * (1 - 1/298.257223563) 8.34 + estimated diameter = 2 * (2*a+b)/3 8.35 +*/ 8.36 +#define PGL_SPHEROID_A 6378137.0 /* semi major axis */ 8.37 +#define PGL_SPHEROID_F (1.0/298.257223563) /* flattening */ 8.38 +#define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F)) 8.39 +#define PGL_EPS2 ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \ 8.40 + PGL_SPHEROID_B * PGL_SPHEROID_B ) / \ 8.41 + ( PGL_SPHEROID_A * PGL_SPHEROID_A ) ) 8.42 +#define PGL_SUBEPS2 (1.0-PGL_EPS2) 8.43 +#define PGL_RADIUS ((2.0*PGL_SPHEROID_A + PGL_SPHEROID_B) / 3.0) 8.44 +#define PGL_DIAMETER (2.0 * PGL_RADIUS) 8.45 +#define PGL_SCALE (PGL_SPHEROID_A / PGL_DIAMETER) /* semi-major ref. */ 8.46 +#define PGL_MAXDIST (PGL_RADIUS * M_PI) /* maximum distance */ 8.47 +#define PGL_FADELIMIT (PGL_MAXDIST / 3.0) /* 1/6 circumference */ 8.48 + 8.49 +/* calculate distance between two points on earth (given in degrees) */ 8.50 +static inline double pgl_distance( 8.51 + double lat1, double lon1, double lat2, double lon2 8.52 +) { 8.53 + float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin; 8.54 + float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t; 8.55 + /* normalize delta longitude (lon2 > 0 && lon1 = 0) */ 8.56 + /* lon1 = 0 (not used anymore) */ 8.57 + lon2 = fabs(lon2-lon1); 8.58 + /* convert to radians (first divide, then multiply) */ 8.59 + lat1 = (lat1 / 180.0) * M_PI; 8.60 + lat2 = (lat2 / 180.0) * M_PI; 8.61 + lon2 = (lon2 / 180.0) * M_PI; 8.62 + /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point 8.63 + operations (lon2 >= lon1 is already ensured in a previous step) */ 8.64 + if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; } 8.65 + /* calculate 3d coordinates on scaled ellipsoid which has an average diameter 8.66 + of 1.0 */ 8.67 + lat1cos = cos(lat1); lat1sin = sin(lat1); 8.68 + lat2cos = cos(lat2); lat2sin = sin(lat2); 8.69 + lon2cos = cos(lon2); lon2sin = sin(lon2); 8.70 + nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin); 8.71 + nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin); 8.72 + x1 = nphi1 * lat1cos; 8.73 + z1 = nphi1 * PGL_SUBEPS2 * lat1sin; 8.74 + x2 = nphi2 * lat2cos * lon2cos; 8.75 + y2 = nphi2 * lat2cos * lon2sin; 8.76 + z2 = nphi2 * PGL_SUBEPS2 * lat2sin; 8.77 + /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */ 8.78 + g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1)); 8.79 + /* convert tunnel distance through scaled ellipsoid to approximated surface 8.80 + distance on original ellipsoid */ 8.81 + if (g > 1.0) g = 1.0; 8.82 + s = PGL_DIAMETER * asin(g); 8.83 + /* return result only if small enough to be precise (less than 1/3 of 8.84 + maximum possible distance) */ 8.85 + if (s <= PGL_FADELIMIT) return s; 8.86 + /* calculate tunnel distance to antipodal point through scaled ellipsoid */ 8.87 + g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1)); 8.88 + /* convert tunnel distance to antipodal point through scaled ellipsoid to 8.89 + approximated surface distance to antipodal point on original ellipsoid */ 8.90 + if (g > 1.0) g = 1.0; 8.91 + t = PGL_DIAMETER * asin(g); 8.92 + /* surface distance between original points can now be approximated by 8.93 + substracting antipodal distance from maximum possible distance; 8.94 + return result only if small enough (less than 1/3 of maximum possible 8.95 + distance) */ 8.96 + if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t; 8.97 + /* otherwise crossfade direct and antipodal result to ensure monotonicity */ 8.98 + return ( 8.99 + (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) / 8.100 + (s + t - 2*PGL_FADELIMIT) 8.101 + ); 8.102 +} 8.103 + 8.104 +/* finite distance that can not be reached on earth */ 8.105 +#define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST) 8.106 + 8.107 + 8.108 +/*--------------------------------* 8.109 + * simple geographic data types * 8.110 + *--------------------------------*/ 8.111 + 8.112 +/* point on earth given by latitude and longitude in degrees */ 8.113 +/* (type "epoint" in SQL) */ 8.114 +typedef struct { 8.115 + double lat; /* between -90 and 90 (both inclusive) */ 8.116 + double lon; /* between -180 and 180 (both inclusive) */ 8.117 +} pgl_point; 8.118 + 8.119 +/* box delimited by two parallels and two meridians (all in degrees) */ 8.120 +/* (type "ebox" in SQL) */ 8.121 +typedef struct { 8.122 + double lat_min; /* between -90 and 90 (both inclusive) */ 8.123 + double lat_max; /* between -90 and 90 (both inclusive) */ 8.124 + double lon_min; /* between -180 and 180 (both inclusive) */ 8.125 + double lon_max; /* between -180 and 180 (both inclusive) */ 8.126 + /* if lat_min > lat_max, then box is empty */ 8.127 + /* if lon_min > lon_max, then 180th meridian is crossed */ 8.128 +} pgl_box; 8.129 + 8.130 +/* circle on earth surface (for radial searches with fixed radius) */ 8.131 +/* (type "ecircle" in SQL) */ 8.132 +typedef struct { 8.133 + pgl_point center; 8.134 + double radius; /* positive (including +0 but excluding -0), or -INFINITY */ 8.135 + /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point), 8.136 + zero radius (0) denotes a single point, 8.137 + a finite radius (0 < radius < INFINITY) denotes a filled circle, and 8.138 + a radius of INFINITY is valid and means complete coverage of earth. */ 8.139 +} pgl_circle; 8.140 + 8.141 + 8.142 +/*----------------------------------* 8.143 + * geographic "cluster" data type * 8.144 + *----------------------------------*/ 8.145 + 8.146 +/* A cluster is a collection of points, paths, outlines, and polygons. If two 8.147 + polygons in a cluster overlap, the area covered by both polygons does not 8.148 + belong to the cluster. This way, a cluster can be used to describe complex 8.149 + shapes like polygons with holes. Outlines are non-filled polygons. Paths are 8.150 + open by default (i.e. the last point in the list is not connected with the 8.151 + first point in the list). Note that each outline or polygon in a cluster 8.152 + must cover a longitude range of less than 180 degrees to avoid ambiguities. 8.153 + Areas which are larger may be split into multiple polygons. */ 8.154 + 8.155 +/* maximum number of points in a cluster */ 8.156 +/* (limited to avoid integer overflows, e.g. when allocating memory) */ 8.157 +#define PGL_CLUSTER_MAXPOINTS 16777216 8.158 + 8.159 +/* types of cluster entries */ 8.160 +#define PGL_ENTRY_POINT 1 /* a point */ 8.161 +#define PGL_ENTRY_PATH 2 /* a path from first point to last point */ 8.162 +#define PGL_ENTRY_OUTLINE 3 /* a non-filled polygon with given vertices */ 8.163 +#define PGL_ENTRY_POLYGON 4 /* a filled polygon with given vertices */ 8.164 + 8.165 +/* Entries of a cluster are described by two different structs: pgl_newentry 8.166 + and pgl_entry. The first is used only during construction of a cluster, the 8.167 + second is used in all other cases (e.g. when reading clusters from the 8.168 + database, performing operations, etc). */ 8.169 + 8.170 +/* entry for new geographic cluster during construction of that cluster */ 8.171 +typedef struct { 8.172 + int32_t entrytype; 8.173 + int32_t npoints; 8.174 + pgl_point *points; /* pointer to an array of points (pgl_point) */ 8.175 +} pgl_newentry; 8.176 + 8.177 +/* entry of geographic cluster */ 8.178 +typedef struct { 8.179 + int32_t entrytype; /* type of entry: point, path, outline, polygon */ 8.180 + int32_t npoints; /* number of stored points (set to 1 for point entry) */ 8.181 + int32_t offset; /* offset of pgl_point array from cluster base address */ 8.182 + /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */ 8.183 +} pgl_entry; 8.184 + 8.185 +/* geographic cluster which is a collection of points, (open) paths, polygons, 8.186 + and outlines (non-filled polygons) */ 8.187 +typedef struct { 8.188 + char header[VARHDRSZ]; /* PostgreSQL header for variable size data types */ 8.189 + int32_t nentries; /* number of stored points */ 8.190 + pgl_circle bounding; /* bounding circle */ 8.191 + /* Note: bounding circle ensures alignment of pgl_cluster for points */ 8.192 + pgl_entry entries[FLEXIBLE_ARRAY_MEMBER]; /* var-length data */ 8.193 +} pgl_cluster; 8.194 + 8.195 +/* macro to determine memory alignment of points */ 8.196 +/* (needed to store pgl_point array after entries in pgl_cluster) */ 8.197 +typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment; 8.198 +#define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned) 8.199 + 8.200 +/* macro to extract a pointer to the array of points of a cluster entry */ 8.201 +#define PGL_ENTRY_POINTS(cluster, idx) \ 8.202 + ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset)) 8.203 + 8.204 +/* convert pgl_newentry array to pgl_cluster */ 8.205 +/* NOTE: requires pgl_finalize_cluster to be called to finalize result */ 8.206 +static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) { 8.207 + int i; /* index of current entry */ 8.208 + int npoints = 0; /* number of points in whole cluster */ 8.209 + int entry_npoints; /* number of points in current entry */ 8.210 + int points_offset = PGL_POINT_ALIGNMENT * ( 8.211 + ( offsetof(pgl_cluster, entries) + 8.212 + nentries * sizeof(pgl_entry) + 8.213 + PGL_POINT_ALIGNMENT - 1 8.214 + ) / PGL_POINT_ALIGNMENT 8.215 + ); /* offset of pgl_point array from base address (considering alignment) */ 8.216 + pgl_cluster *cluster; /* new cluster to be returned */ 8.217 + /* determine total number of points */ 8.218 + for (i=0; i<nentries; i++) npoints += entries[i].npoints; 8.219 + /* allocate memory for cluster (including entries and points) */ 8.220 + cluster = palloc(points_offset + npoints * sizeof(pgl_point)); 8.221 + /* re-count total number of points to determine offset for each entry */ 8.222 + npoints = 0; 8.223 + /* copy entries and points */ 8.224 + for (i=0; i<nentries; i++) { 8.225 + /* determine number of points in entry */ 8.226 + entry_npoints = entries[i].npoints; 8.227 + /* copy entry */ 8.228 + cluster->entries[i].entrytype = entries[i].entrytype; 8.229 + cluster->entries[i].npoints = entry_npoints; 8.230 + /* calculate offset (in bytes) of pgl_point array */ 8.231 + cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point); 8.232 + /* copy points */ 8.233 + memcpy( 8.234 + PGL_ENTRY_POINTS(cluster, i), 8.235 + entries[i].points, 8.236 + entry_npoints * sizeof(pgl_point) 8.237 + ); 8.238 + /* update total number of points processed */ 8.239 + npoints += entry_npoints; 8.240 + } 8.241 + /* set number of entries in cluster */ 8.242 + cluster->nentries = nentries; 8.243 + /* set PostgreSQL header for variable sized data */ 8.244 + SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point)); 8.245 + /* return newly created cluster */ 8.246 + return cluster; 8.247 +} 8.248 + 8.249 + 8.250 +/*----------------------------------------------* 8.251 + * Geographic point with integer sample count * 8.252 + * (needed for fair distance calculation) * 8.253 + *----------------------------------------------*/ 8.254 + 8.255 +typedef struct { 8.256 + pgl_point point; /* NOTE: point first to allow C cast to pgl_point */ 8.257 + int32 samples; 8.258 +} pgl_point_sc; 8.259 + 8.260 + 8.261 +/*----------------------------------------* 8.262 + * C functions on geographic data types * 8.263 + *----------------------------------------*/ 8.264 + 8.265 +/* round latitude or longitude to 12 digits after decimal point */ 8.266 +static inline double pgl_round(double val) { 8.267 + return round(val * 1e12) / 1e12; 8.268 +} 8.269 + 8.270 +/* compare two points */ 8.271 +/* (equality when same point on earth is described, otherwise an arbitrary 8.272 + linear order) */ 8.273 +static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) { 8.274 + double lon1, lon2; /* modified longitudes for special cases */ 8.275 + /* use latitude as first ordering criterion */ 8.276 + if (point1->lat < point2->lat) return -1; 8.277 + if (point1->lat > point2->lat) return 1; 8.278 + /* determine modified longitudes (considering special case of poles and 8.279 + 180th meridian which can be described as W180 or E180) */ 8.280 + if (point1->lat == -90 || point1->lat == 90) lon1 = 0; 8.281 + else if (point1->lon == 180) lon1 = -180; 8.282 + else lon1 = point1->lon; 8.283 + if (point2->lat == -90 || point2->lat == 90) lon2 = 0; 8.284 + else if (point2->lon == 180) lon2 = -180; 8.285 + else lon2 = point2->lon; 8.286 + /* use (modified) longitude as secondary ordering criterion */ 8.287 + if (lon1 < lon2) return -1; 8.288 + if (lon1 > lon2) return 1; 8.289 + /* no difference found, points are equal */ 8.290 + return 0; 8.291 +} 8.292 + 8.293 +/* compare two boxes */ 8.294 +/* (equality when same box on earth is described, otherwise an arbitrary linear 8.295 + order) */ 8.296 +static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) { 8.297 + /* two empty boxes are equal, and an empty box is always considered "less 8.298 + than" a non-empty box */ 8.299 + if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1; 8.300 + if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0; 8.301 + if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1; 8.302 + /* use southern border as first ordering criterion */ 8.303 + if (box1->lat_min < box2->lat_min) return -1; 8.304 + if (box1->lat_min > box2->lat_min) return 1; 8.305 + /* use northern border as second ordering criterion */ 8.306 + if (box1->lat_max < box2->lat_max) return -1; 8.307 + if (box1->lat_max > box2->lat_max) return 1; 8.308 + /* use western border as third ordering criterion */ 8.309 + if (box1->lon_min < box2->lon_min) return -1; 8.310 + if (box1->lon_min > box2->lon_min) return 1; 8.311 + /* use eastern border as fourth ordering criterion */ 8.312 + if (box1->lon_max < box2->lon_max) return -1; 8.313 + if (box1->lon_max > box2->lon_max) return 1; 8.314 + /* no difference found, boxes are equal */ 8.315 + return 0; 8.316 +} 8.317 + 8.318 +/* compare two circles */ 8.319 +/* (equality when same circle on earth is described, otherwise an arbitrary 8.320 + linear order) */ 8.321 +static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) { 8.322 + /* two circles with same infinite radius (positive or negative infinity) are 8.323 + considered equal independently of center point */ 8.324 + if ( 8.325 + !isfinite(circle1->radius) && !isfinite(circle2->radius) && 8.326 + circle1->radius == circle2->radius 8.327 + ) return 0; 8.328 + /* use radius as first ordering criterion */ 8.329 + if (circle1->radius < circle2->radius) return -1; 8.330 + if (circle1->radius > circle2->radius) return 1; 8.331 + /* use center point as secondary ordering criterion */ 8.332 + return pgl_point_cmp(&(circle1->center), &(circle2->center)); 8.333 +} 8.334 + 8.335 +/* set box to empty box*/ 8.336 +static void pgl_box_set_empty(pgl_box *box) { 8.337 + box->lat_min = INFINITY; 8.338 + box->lat_max = -INFINITY; 8.339 + box->lon_min = 0; 8.340 + box->lon_max = 0; 8.341 +} 8.342 + 8.343 +/* check if point is inside a box */ 8.344 +static bool pgl_point_in_box(pgl_point *point, pgl_box *box) { 8.345 + return ( 8.346 + point->lat >= box->lat_min && point->lat <= box->lat_max && ( 8.347 + (box->lon_min > box->lon_max) ? ( 8.348 + /* box crosses 180th meridian */ 8.349 + point->lon >= box->lon_min || point->lon <= box->lon_max 8.350 + ) : ( 8.351 + /* box does not cross the 180th meridian */ 8.352 + point->lon >= box->lon_min && point->lon <= box->lon_max 8.353 + ) 8.354 + ) 8.355 + ); 8.356 +} 8.357 + 8.358 +/* check if two boxes overlap */ 8.359 +static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) { 8.360 + return ( 8.361 + box2->lat_max >= box2->lat_min && /* ensure box2 is not empty */ 8.362 + ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) && 8.363 + ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && ( 8.364 + ( 8.365 + /* check if one and only one box crosses the 180th meridian */ 8.366 + ((box1->lon_min > box1->lon_max) ? 1 : 0) ^ 8.367 + ((box2->lon_min > box2->lon_max) ? 1 : 0) 8.368 + ) ? ( 8.369 + /* exactly one box crosses the 180th meridian */ 8.370 + box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min || 8.371 + box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max 8.372 + ) : ( 8.373 + /* no box or both boxes cross the 180th meridian */ 8.374 + ( 8.375 + (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) && 8.376 + (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max) 8.377 + ) || 8.378 + /* handle W180 == E180 */ 8.379 + ( box1->lon_min == -180 && box2->lon_max == 180 ) || 8.380 + ( box2->lon_min == -180 && box1->lon_max == 180 ) 8.381 + ) 8.382 + ) 8.383 + ); 8.384 +} 8.385 + 8.386 +/* check unambiguousness of east/west orientation of cluster entries and set 8.387 + bounding circle of cluster */ 8.388 +static bool pgl_finalize_cluster(pgl_cluster *cluster) { 8.389 + int i, j; /* i: index of entry, j: index of point in entry */ 8.390 + int npoints; /* number of points in entry */ 8.391 + int total_npoints = 0; /* total number of points in cluster */ 8.392 + pgl_point *points; /* points in entry */ 8.393 + int lon_dir; /* first point of entry west (-1) or east (+1) */ 8.394 + double lon_break = 0; /* antipodal longitude of first point in entry */ 8.395 + double lon_min, lon_max; /* covered longitude range of entry */ 8.396 + double value; /* temporary variable */ 8.397 + /* reset bounding circle center to empty circle at 0/0 coordinates */ 8.398 + cluster->bounding.center.lat = 0; 8.399 + cluster->bounding.center.lon = 0; 8.400 + cluster->bounding.radius = -INFINITY; 8.401 + /* if cluster is not empty */ 8.402 + if (cluster->nentries != 0) { 8.403 + /* iterate over all cluster entries and ensure they each cover a longitude 8.404 + range less than 180 degrees */ 8.405 + for (i=0; i<cluster->nentries; i++) { 8.406 + /* get properties of entry */ 8.407 + npoints = cluster->entries[i].npoints; 8.408 + points = PGL_ENTRY_POINTS(cluster, i); 8.409 + /* get longitude of first point of entry */ 8.410 + value = points[0].lon; 8.411 + /* initialize lon_min and lon_max with longitude of first point */ 8.412 + lon_min = value; 8.413 + lon_max = value; 8.414 + /* determine east/west orientation of first point and calculate antipodal 8.415 + longitude (Note: rounding required here) */ 8.416 + if (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); } 8.417 + else if (value > 0) { lon_dir = 1; lon_break = pgl_round(value - 180); } 8.418 + else lon_dir = 0; 8.419 + /* iterate over all other points in entry */ 8.420 + for (j=1; j<npoints; j++) { 8.421 + /* consider longitude wrap-around */ 8.422 + value = points[j].lon; 8.423 + if (lon_dir<0 && value>lon_break) value = pgl_round(value - 360); 8.424 + else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360); 8.425 + /* update lon_min and lon_max */ 8.426 + if (value < lon_min) lon_min = value; 8.427 + else if (value > lon_max) lon_max = value; 8.428 + /* return false if 180 degrees or more are covered */ 8.429 + if (lon_max - lon_min >= 180) return false; 8.430 + } 8.431 + } 8.432 + /* iterate over all points of all entries and calculate arbitrary center 8.433 + point for bounding circle (best if center point minimizes the radius, 8.434 + but some error is allowed here) */ 8.435 + for (i=0; i<cluster->nentries; i++) { 8.436 + /* get properties of entry */ 8.437 + npoints = cluster->entries[i].npoints; 8.438 + points = PGL_ENTRY_POINTS(cluster, i); 8.439 + /* check if first entry */ 8.440 + if (i==0) { 8.441 + /* get longitude of first point of first entry in whole cluster */ 8.442 + value = points[0].lon; 8.443 + /* initialize lon_min and lon_max with longitude of first point of 8.444 + first entry in whole cluster (used to determine if whole cluster 8.445 + covers a longitude range of 180 degrees or more) */ 8.446 + lon_min = value; 8.447 + lon_max = value; 8.448 + /* determine east/west orientation of first point and calculate 8.449 + antipodal longitude (Note: rounding not necessary here) */ 8.450 + if (value < 0) { lon_dir = -1; lon_break = value + 180; } 8.451 + else if (value > 0) { lon_dir = 1; lon_break = value - 180; } 8.452 + else lon_dir = 0; 8.453 + } 8.454 + /* iterate over all points in entry */ 8.455 + for (j=0; j<npoints; j++) { 8.456 + /* longitude wrap-around (Note: rounding not necessary here) */ 8.457 + value = points[j].lon; 8.458 + if (lon_dir < 0 && value > lon_break) value -= 360; 8.459 + else if (lon_dir > 0 && value < lon_break) value += 360; 8.460 + if (value < lon_min) lon_min = value; 8.461 + else if (value > lon_max) lon_max = value; 8.462 + /* set bounding circle to cover whole earth if 180 degrees or more are 8.463 + covered */ 8.464 + if (lon_max - lon_min >= 180) { 8.465 + cluster->bounding.center.lat = 0; 8.466 + cluster->bounding.center.lon = 0; 8.467 + cluster->bounding.radius = INFINITY; 8.468 + return true; 8.469 + } 8.470 + /* add point to bounding circle center (for average calculation) */ 8.471 + cluster->bounding.center.lat += points[j].lat; 8.472 + cluster->bounding.center.lon += value; 8.473 + } 8.474 + /* count total number of points */ 8.475 + total_npoints += npoints; 8.476 + } 8.477 + /* determine average latitude and longitude of cluster */ 8.478 + cluster->bounding.center.lat /= total_npoints; 8.479 + cluster->bounding.center.lon /= total_npoints; 8.480 + /* normalize longitude of center of cluster bounding circle */ 8.481 + if (cluster->bounding.center.lon < -180) { 8.482 + cluster->bounding.center.lon += 360; 8.483 + } 8.484 + else if (cluster->bounding.center.lon > 180) { 8.485 + cluster->bounding.center.lon -= 360; 8.486 + } 8.487 + /* round bounding circle center (useful if it is used by other functions) */ 8.488 + cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat); 8.489 + cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon); 8.490 + /* calculate radius of bounding circle */ 8.491 + for (i=0; i<cluster->nentries; i++) { 8.492 + npoints = cluster->entries[i].npoints; 8.493 + points = PGL_ENTRY_POINTS(cluster, i); 8.494 + for (j=0; j<npoints; j++) { 8.495 + value = pgl_distance( 8.496 + cluster->bounding.center.lat, cluster->bounding.center.lon, 8.497 + points[j].lat, points[j].lon 8.498 + ); 8.499 + if (value > cluster->bounding.radius) cluster->bounding.radius = value; 8.500 + } 8.501 + } 8.502 + } 8.503 + /* return true (east/west orientation is unambiguous) */ 8.504 + return true; 8.505 +} 8.506 + 8.507 +/* check if point is inside cluster */ 8.508 +/* (if point is on perimeter, then true is returned if and only if 8.509 + strict == false) */ 8.510 +static bool pgl_point_in_cluster( 8.511 + pgl_point *point, 8.512 + pgl_cluster *cluster, 8.513 + bool strict 8.514 +) { 8.515 + int i, j, k; /* i: entry, j: point in entry, k: next point in entry */ 8.516 + int entrytype; /* type of entry */ 8.517 + int npoints; /* number of points in entry */ 8.518 + pgl_point *points; /* array of points in entry */ 8.519 + int lon_dir = 0; /* first vertex west (-1) or east (+1) */ 8.520 + double lon_break = 0; /* antipodal longitude of first vertex */ 8.521 + double lat0 = point->lat; /* latitude of point */ 8.522 + double lon0; /* (adjusted) longitude of point */ 8.523 + double lat1, lon1; /* latitude and (adjusted) longitude of vertex */ 8.524 + double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */ 8.525 + double lon; /* longitude of intersection */ 8.526 + int counter = 0; /* counter for intersections east of point */ 8.527 + /* iterate over all entries */ 8.528 + for (i=0; i<cluster->nentries; i++) { 8.529 + /* get type of entry */ 8.530 + entrytype = cluster->entries[i].entrytype; 8.531 + /* skip all entries but polygons if perimeters are excluded */ 8.532 + if (strict && entrytype != PGL_ENTRY_POLYGON) continue; 8.533 + /* get points of entry */ 8.534 + npoints = cluster->entries[i].npoints; 8.535 + points = PGL_ENTRY_POINTS(cluster, i); 8.536 + /* determine east/west orientation of first point of entry and calculate 8.537 + antipodal longitude */ 8.538 + lon_break = points[0].lon; 8.539 + if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 8.540 + else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 8.541 + else lon_dir = 0; 8.542 + /* get longitude of point */ 8.543 + lon0 = point->lon; 8.544 + /* consider longitude wrap-around for point */ 8.545 + if (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360); 8.546 + else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360); 8.547 + /* iterate over all edges and vertices */ 8.548 + for (j=0; j<npoints; j++) { 8.549 + /* return if point is on vertex of polygon */ 8.550 + if (pgl_point_cmp(point, &(points[j])) == 0) return !strict; 8.551 + /* calculate index of next vertex */ 8.552 + k = (j+1) % npoints; 8.553 + /* skip last edge unless entry is (closed) outline or polygon */ 8.554 + if ( 8.555 + k == 0 && 8.556 + entrytype != PGL_ENTRY_OUTLINE && 8.557 + entrytype != PGL_ENTRY_POLYGON 8.558 + ) continue; 8.559 + /* use previously calculated values for lat1 and lon1 if possible */ 8.560 + if (j) { 8.561 + lat1 = lat2; 8.562 + lon1 = lon2; 8.563 + } else { 8.564 + /* otherwise get latitude and longitude values of first vertex */ 8.565 + lat1 = points[0].lat; 8.566 + lon1 = points[0].lon; 8.567 + /* and consider longitude wrap-around for first vertex */ 8.568 + if (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360); 8.569 + else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360); 8.570 + } 8.571 + /* get latitude and longitude of next vertex */ 8.572 + lat2 = points[k].lat; 8.573 + lon2 = points[k].lon; 8.574 + /* consider longitude wrap-around for next vertex */ 8.575 + if (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360); 8.576 + else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360); 8.577 + /* return if point is on horizontal (west to east) edge of polygon */ 8.578 + if ( 8.579 + lat0 == lat1 && lat0 == lat2 && 8.580 + ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) ) 8.581 + ) return !strict; 8.582 + /* check if edge crosses east/west line of point */ 8.583 + if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) { 8.584 + /* calculate longitude of intersection */ 8.585 + lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1); 8.586 + /* return if intersection goes (approximately) through point */ 8.587 + if (pgl_round(lon) == lon0) return !strict; 8.588 + /* count intersection if east of point and entry is polygon*/ 8.589 + if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++; 8.590 + } 8.591 + } 8.592 + } 8.593 + /* return true if number of intersections is odd */ 8.594 + return counter & 1; 8.595 +} 8.596 + 8.597 +/* check if all points of the second cluster are strictly inside the first 8.598 + cluster */ 8.599 +static inline bool pgl_all_cluster_points_strictly_in_cluster( 8.600 + pgl_cluster *outer, pgl_cluster *inner 8.601 +) { 8.602 + int i, j; /* i: entry, j: point in entry */ 8.603 + int npoints; /* number of points in entry */ 8.604 + pgl_point *points; /* array of points in entry */ 8.605 + /* iterate over all entries of "inner" cluster */ 8.606 + for (i=0; i<inner->nentries; i++) { 8.607 + /* get properties of entry */ 8.608 + npoints = inner->entries[i].npoints; 8.609 + points = PGL_ENTRY_POINTS(inner, i); 8.610 + /* iterate over all points in entry of "inner" cluster */ 8.611 + for (j=0; j<npoints; j++) { 8.612 + /* return false if one point of inner cluster is not in outer cluster */ 8.613 + if (!pgl_point_in_cluster(points+j, outer, true)) return false; 8.614 + } 8.615 + } 8.616 + /* otherwise return true */ 8.617 + return true; 8.618 +} 8.619 + 8.620 +/* check if any point the second cluster is inside the first cluster */ 8.621 +static inline bool pgl_any_cluster_points_in_cluster( 8.622 + pgl_cluster *outer, pgl_cluster *inner 8.623 +) { 8.624 + int i, j; /* i: entry, j: point in entry */ 8.625 + int npoints; /* number of points in entry */ 8.626 + pgl_point *points; /* array of points in entry */ 8.627 + /* iterate over all entries of "inner" cluster */ 8.628 + for (i=0; i<inner->nentries; i++) { 8.629 + /* get properties of entry */ 8.630 + npoints = inner->entries[i].npoints; 8.631 + points = PGL_ENTRY_POINTS(inner, i); 8.632 + /* iterate over all points in entry of "inner" cluster */ 8.633 + for (j=0; j<npoints; j++) { 8.634 + /* return true if one point of inner cluster is in outer cluster */ 8.635 + if (pgl_point_in_cluster(points+j, outer, false)) return true; 8.636 + } 8.637 + } 8.638 + /* otherwise return false */ 8.639 + return false; 8.640 +} 8.641 + 8.642 +/* check if line segment strictly crosses line (not just touching) */ 8.643 +static inline bool pgl_lseg_crosses_line( 8.644 + double seg_x1, double seg_y1, double seg_x2, double seg_y2, 8.645 + double line_x1, double line_y1, double line_x2, double line_y2 8.646 +) { 8.647 + return ( 8.648 + ( 8.649 + (seg_x1-line_x1) * (line_y2-line_y1) - 8.650 + (seg_y1-line_y1) * (line_x2-line_x1) 8.651 + ) * ( 8.652 + (seg_x2-line_x1) * (line_y2-line_y1) - 8.653 + (seg_y2-line_y1) * (line_x2-line_x1) 8.654 + ) 8.655 + ) < 0; 8.656 +} 8.657 + 8.658 +/* check if paths and outlines of two clusters strictly overlap (not just 8.659 + touching) */ 8.660 +static bool pgl_outlines_overlap( 8.661 + pgl_cluster *cluster1, pgl_cluster *cluster2 8.662 +) { 8.663 + int i1, j1, k1; /* i: entry, j: point in entry, k: next point in entry */ 8.664 + int i2, j2, k2; 8.665 + int entrytype1, entrytype2; /* type of entry */ 8.666 + int npoints1, npoints2; /* number of points in entry */ 8.667 + pgl_point *points1; /* array of points in entry of cluster1 */ 8.668 + pgl_point *points2; /* array of points in entry of cluster2 */ 8.669 + int lon_dir1, lon_dir2; /* first vertex west (-1) or east (+1) */ 8.670 + double lon_break1, lon_break2; /* antipodal longitude of first vertex */ 8.671 + double lat11, lon11; /* latitude and (adjusted) longitude of vertex */ 8.672 + double lat12, lon12; /* latitude and (adjusted) longitude of next vertex */ 8.673 + double lat21, lon21; /* latitude and (adjusted) longitudes for cluster2 */ 8.674 + double lat22, lon22; 8.675 + double wrapvalue; /* temporary helper value to adjust wrap-around */ 8.676 + /* iterate over all entries of cluster1 */ 8.677 + for (i1=0; i1<cluster1->nentries; i1++) { 8.678 + /* get properties of entry in cluster1 and skip points */ 8.679 + npoints1 = cluster1->entries[i1].npoints; 8.680 + if (npoints1 < 2) continue; 8.681 + entrytype1 = cluster1->entries[i1].entrytype; 8.682 + points1 = PGL_ENTRY_POINTS(cluster1, i1); 8.683 + /* determine east/west orientation of first point and calculate antipodal 8.684 + longitude */ 8.685 + lon_break1 = points1[0].lon; 8.686 + if (lon_break1 < 0) { 8.687 + lon_dir1 = -1; 8.688 + lon_break1 = pgl_round(lon_break1 + 180); 8.689 + } else if (lon_break1 > 0) { 8.690 + lon_dir1 = 1; 8.691 + lon_break1 = pgl_round(lon_break1 - 180); 8.692 + } else lon_dir1 = 0; 8.693 + /* iterate over all edges and vertices in cluster1 */ 8.694 + for (j1=0; j1<npoints1; j1++) { 8.695 + /* calculate index of next vertex */ 8.696 + k1 = (j1+1) % npoints1; 8.697 + /* skip last edge unless entry is (closed) outline or polygon */ 8.698 + if ( 8.699 + k1 == 0 && 8.700 + entrytype1 != PGL_ENTRY_OUTLINE && 8.701 + entrytype1 != PGL_ENTRY_POLYGON 8.702 + ) continue; 8.703 + /* use previously calculated values for lat1 and lon1 if possible */ 8.704 + if (j1) { 8.705 + lat11 = lat12; 8.706 + lon11 = lon12; 8.707 + } else { 8.708 + /* otherwise get latitude and longitude values of first vertex */ 8.709 + lat11 = points1[0].lat; 8.710 + lon11 = points1[0].lon; 8.711 + /* and consider longitude wrap-around for first vertex */ 8.712 + if (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360); 8.713 + else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360); 8.714 + } 8.715 + /* get latitude and longitude of next vertex */ 8.716 + lat12 = points1[k1].lat; 8.717 + lon12 = points1[k1].lon; 8.718 + /* consider longitude wrap-around for next vertex */ 8.719 + if (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360); 8.720 + else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360); 8.721 + /* skip degenerated edges */ 8.722 + if (lat11 == lat12 && lon11 == lon12) continue; 8.723 + /* iterate over all entries of cluster2 */ 8.724 + for (i2=0; i2<cluster2->nentries; i2++) { 8.725 + /* get points and number of points of entry in cluster2 */ 8.726 + npoints2 = cluster2->entries[i2].npoints; 8.727 + if (npoints2 < 2) continue; 8.728 + entrytype2 = cluster2->entries[i2].entrytype; 8.729 + points2 = PGL_ENTRY_POINTS(cluster2, i2); 8.730 + /* determine east/west orientation of first point and calculate antipodal 8.731 + longitude */ 8.732 + lon_break2 = points2[0].lon; 8.733 + if (lon_break2 < 0) { 8.734 + lon_dir2 = -1; 8.735 + lon_break2 = pgl_round(lon_break2 + 180); 8.736 + } else if (lon_break2 > 0) { 8.737 + lon_dir2 = 1; 8.738 + lon_break2 = pgl_round(lon_break2 - 180); 8.739 + } else lon_dir2 = 0; 8.740 + /* iterate over all edges and vertices in cluster2 */ 8.741 + for (j2=0; j2<npoints2; j2++) { 8.742 + /* calculate index of next vertex */ 8.743 + k2 = (j2+1) % npoints2; 8.744 + /* skip last edge unless entry is (closed) outline or polygon */ 8.745 + if ( 8.746 + k2 == 0 && 8.747 + entrytype2 != PGL_ENTRY_OUTLINE && 8.748 + entrytype2 != PGL_ENTRY_POLYGON 8.749 + ) continue; 8.750 + /* use previously calculated values for lat1 and lon1 if possible */ 8.751 + if (j2) { 8.752 + lat21 = lat22; 8.753 + lon21 = lon22; 8.754 + } else { 8.755 + /* otherwise get latitude and longitude values of first vertex */ 8.756 + lat21 = points2[0].lat; 8.757 + lon21 = points2[0].lon; 8.758 + /* and consider longitude wrap-around for first vertex */ 8.759 + if (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360); 8.760 + else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360); 8.761 + } 8.762 + /* get latitude and longitude of next vertex */ 8.763 + lat22 = points2[k2].lat; 8.764 + lon22 = points2[k2].lon; 8.765 + /* consider longitude wrap-around for next vertex */ 8.766 + if (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360); 8.767 + else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360); 8.768 + /* skip degenerated edges */ 8.769 + if (lat21 == lat22 && lon21 == lon22) continue; 8.770 + /* perform another wrap-around where necessary */ 8.771 + /* TODO: improve performance of whole wrap-around mechanism */ 8.772 + wrapvalue = (lon21 + lon22) - (lon11 + lon12); 8.773 + if (wrapvalue > 360) { 8.774 + lon21 = pgl_round(lon21 - 360); 8.775 + lon22 = pgl_round(lon22 - 360); 8.776 + } else if (wrapvalue < -360) { 8.777 + lon21 = pgl_round(lon21 + 360); 8.778 + lon22 = pgl_round(lon22 + 360); 8.779 + } 8.780 + /* return true if segments overlap */ 8.781 + if ( 8.782 + pgl_lseg_crosses_line( 8.783 + lat11, lon11, lat12, lon12, 8.784 + lat21, lon21, lat22, lon22 8.785 + ) && pgl_lseg_crosses_line( 8.786 + lat21, lon21, lat22, lon22, 8.787 + lat11, lon11, lat12, lon12 8.788 + ) 8.789 + ) { 8.790 + return true; 8.791 + } 8.792 + } 8.793 + } 8.794 + } 8.795 + } 8.796 + /* otherwise return false */ 8.797 + return false; 8.798 +} 8.799 + 8.800 +/* check if second cluster is completely contained in first cluster */ 8.801 +static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) { 8.802 + if (!pgl_all_cluster_points_strictly_in_cluster(outer, inner)) return false; 8.803 + if (pgl_any_cluster_points_in_cluster(inner, outer)) return false; 8.804 + if (pgl_outlines_overlap(outer, inner)) return false; 8.805 + return true; 8.806 +} 8.807 + 8.808 +/* check if two clusters overlap */ 8.809 +static bool pgl_clusters_overlap( 8.810 + pgl_cluster *cluster1, pgl_cluster *cluster2 8.811 +) { 8.812 + if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true; 8.813 + if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true; 8.814 + if (pgl_outlines_overlap(cluster1, cluster2)) return true; 8.815 + return false; 8.816 +} 8.817 + 8.818 + 8.819 +/* calculate (approximate) distance between point and cluster */ 8.820 +static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) { 8.821 + double comp; /* square of compression of meridians */ 8.822 + int i, j, k; /* i: entry, j: point in entry, k: next point in entry */ 8.823 + int entrytype; /* type of entry */ 8.824 + int npoints; /* number of points in entry */ 8.825 + pgl_point *points; /* array of points in entry */ 8.826 + int lon_dir = 0; /* first vertex west (-1) or east (+1) */ 8.827 + double lon_break = 0; /* antipodal longitude of first vertex */ 8.828 + double lon_min = 0; /* minimum (adjusted) longitude of entry vertices */ 8.829 + double lon_max = 0; /* maximum (adjusted) longitude of entry vertices */ 8.830 + double lat0 = point->lat; /* latitude of point */ 8.831 + double lon0; /* (adjusted) longitude of point */ 8.832 + double lat1, lon1; /* latitude and (adjusted) longitude of vertex */ 8.833 + double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */ 8.834 + double s; /* scalar for vector calculations */ 8.835 + double dist; /* distance calculated in one step */ 8.836 + double min_dist = INFINITY; /* minimum distance */ 8.837 + /* distance is zero if point is contained in cluster */ 8.838 + if (pgl_point_in_cluster(point, cluster, false)) return 0; 8.839 + /* calculate approximate square compression of meridians */ 8.840 + comp = cos((lat0 / 180.0) * M_PI); 8.841 + comp *= comp; 8.842 + /* calculate exact square compression of meridians */ 8.843 + comp *= ( 8.844 + (1.0 - PGL_EPS2 * (1.0-comp)) * 8.845 + (1.0 - PGL_EPS2 * (1.0-comp)) / 8.846 + (PGL_SUBEPS2 * PGL_SUBEPS2) 8.847 + ); 8.848 + /* iterate over all entries */ 8.849 + for (i=0; i<cluster->nentries; i++) { 8.850 + /* get properties of entry */ 8.851 + entrytype = cluster->entries[i].entrytype; 8.852 + npoints = cluster->entries[i].npoints; 8.853 + points = PGL_ENTRY_POINTS(cluster, i); 8.854 + /* determine east/west orientation of first point of entry and calculate 8.855 + antipodal longitude */ 8.856 + lon_break = points[0].lon; 8.857 + if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 8.858 + else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 8.859 + else lon_dir = 0; 8.860 + /* determine covered longitude range */ 8.861 + for (j=0; j<npoints; j++) { 8.862 + /* get longitude of vertex */ 8.863 + lon1 = points[j].lon; 8.864 + /* adjust longitude to fix potential wrap-around */ 8.865 + if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360; 8.866 + else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360; 8.867 + /* update minimum and maximum longitude of polygon */ 8.868 + if (j == 0 || lon1 < lon_min) lon_min = lon1; 8.869 + if (j == 0 || lon1 > lon_max) lon_max = lon1; 8.870 + } 8.871 + /* adjust longitude wrap-around according to full longitude range */ 8.872 + lon_break = (lon_max + lon_min) / 2; 8.873 + if (lon_break < 0) { lon_dir = -1; lon_break += 180; } 8.874 + else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; } 8.875 + /* get longitude of point */ 8.876 + lon0 = point->lon; 8.877 + /* consider longitude wrap-around for point */ 8.878 + if (lon_dir < 0 && lon0 > lon_break) lon0 -= 360; 8.879 + else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360; 8.880 + /* iterate over all edges and vertices */ 8.881 + for (j=0; j<npoints; j++) { 8.882 + /* use previously calculated values for lat1 and lon1 if possible */ 8.883 + if (j) { 8.884 + lat1 = lat2; 8.885 + lon1 = lon2; 8.886 + } else { 8.887 + /* otherwise get latitude and longitude values of first vertex */ 8.888 + lat1 = points[0].lat; 8.889 + lon1 = points[0].lon; 8.890 + /* and consider longitude wrap-around for first vertex */ 8.891 + if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360; 8.892 + else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360; 8.893 + } 8.894 + /* calculate distance to vertex */ 8.895 + dist = pgl_distance(lat0, lon0, lat1, lon1); 8.896 + /* store calculated distance if smallest */ 8.897 + if (dist < min_dist) min_dist = dist; 8.898 + /* calculate index of next vertex */ 8.899 + k = (j+1) % npoints; 8.900 + /* skip last edge unless entry is (closed) outline or polygon */ 8.901 + if ( 8.902 + k == 0 && 8.903 + entrytype != PGL_ENTRY_OUTLINE && 8.904 + entrytype != PGL_ENTRY_POLYGON 8.905 + ) continue; 8.906 + /* get latitude and longitude of next vertex */ 8.907 + lat2 = points[k].lat; 8.908 + lon2 = points[k].lon; 8.909 + /* consider longitude wrap-around for next vertex */ 8.910 + if (lon_dir < 0 && lon2 > lon_break) lon2 -= 360; 8.911 + else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360; 8.912 + /* go to next vertex and edge if edge is degenerated */ 8.913 + if (lat1 == lat2 && lon1 == lon2) continue; 8.914 + /* otherwise test if point can be projected onto edge of polygon */ 8.915 + s = ( 8.916 + ((lat0-lat1) * (lat2-lat1) + comp * (lon0-lon1) * (lon2-lon1)) / 8.917 + ((lat2-lat1) * (lat2-lat1) + comp * (lon2-lon1) * (lon2-lon1)) 8.918 + ); 8.919 + /* go to next vertex and edge if point cannot be projected */ 8.920 + if (!(s > 0 && s < 1)) continue; 8.921 + /* calculate distance from original point to projected point */ 8.922 + dist = pgl_distance( 8.923 + lat0, lon0, 8.924 + lat1 + s * (lat2-lat1), 8.925 + lon1 + s * (lon2-lon1) 8.926 + ); 8.927 + /* store calculated distance if smallest */ 8.928 + if (dist < min_dist) min_dist = dist; 8.929 + } 8.930 + } 8.931 + /* return minimum distance */ 8.932 + return min_dist; 8.933 +} 8.934 + 8.935 +/* calculate (approximate) distance between two clusters */ 8.936 +static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) { 8.937 + int i, j; /* i: entry, j: point in entry */ 8.938 + int npoints; /* number of points in entry */ 8.939 + pgl_point *points; /* array of points in entry */ 8.940 + double dist; /* distance calculated in one step */ 8.941 + double min_dist = INFINITY; /* minimum distance */ 8.942 + /* consider distance from each point in one cluster to the whole other */ 8.943 + for (i=0; i<cluster1->nentries; i++) { 8.944 + npoints = cluster1->entries[i].npoints; 8.945 + points = PGL_ENTRY_POINTS(cluster1, i); 8.946 + for (j=0; j<npoints; j++) { 8.947 + dist = pgl_point_cluster_distance(points+j, cluster2); 8.948 + if (dist == 0) return dist; 8.949 + if (dist < min_dist) min_dist = dist; 8.950 + } 8.951 + } 8.952 + /* consider distance from each point in other cluster to the first cluster */ 8.953 + for (i=0; i<cluster2->nentries; i++) { 8.954 + npoints = cluster2->entries[i].npoints; 8.955 + points = PGL_ENTRY_POINTS(cluster2, i); 8.956 + for (j=0; j<npoints; j++) { 8.957 + dist = pgl_point_cluster_distance(points+j, cluster1); 8.958 + if (dist == 0) return dist; 8.959 + if (dist < min_dist) min_dist = dist; 8.960 + } 8.961 + } 8.962 + return min_dist; 8.963 +} 8.964 + 8.965 +/* estimator function for distance between box and point */ 8.966 +/* always returns a smaller value than actually correct or zero */ 8.967 +static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) { 8.968 + double dlon; /* longitude range of box (delta longitude) */ 8.969 + double distance; /* return value */ 8.970 + /* return infinity if box is empty */ 8.971 + if (box->lat_min > box->lat_max) return INFINITY; 8.972 + /* return zero if point is inside box */ 8.973 + if (pgl_point_in_box(point, box)) return 0; 8.974 + /* calculate delta longitude */ 8.975 + dlon = box->lon_max - box->lon_min; 8.976 + if (dlon < 0) dlon += 360; /* 180th meridian crossed */ 8.977 + /* if delta longitude is greater than 150 degrees, perform safe fall-back */ 8.978 + if (dlon > 150) return 0; 8.979 + /* calculate lower limit for distance (formula below requires dlon <= 150) */ 8.980 + /* TODO: provide better estimation function to improve performance */ 8.981 + distance = ( 8.982 + (1.0-PGL_SPHEROID_F) * /* safety margin due to flattening and approx. */ 8.983 + pgl_distance( 8.984 + point->lat, 8.985 + point->lon, 8.986 + (box->lat_min + box->lat_max) / 2, 8.987 + box->lon_min + dlon/2 8.988 + ) 8.989 + ) - pgl_distance( 8.990 + box->lat_min, box->lon_min, 8.991 + box->lat_max, box->lon_max 8.992 + ); 8.993 + /* truncate negative results to zero */ 8.994 + if (distance <= 0) distance = 0; 8.995 + /* return result */ 8.996 + return distance; 8.997 +} 8.998 + 8.999 + 8.1000 +/*------------------------------------------------------------* 8.1001 + * Functions using numerical integration (Monte Carlo like) * 8.1002 + *------------------------------------------------------------*/ 8.1003 + 8.1004 +/* half of (spherical) earth's surface area */ 8.1005 +#define PGL_HALF_SURFACE (PGL_RADIUS * PGL_DIAMETER * M_PI) 8.1006 + 8.1007 +/* golden angle in radians */ 8.1008 +#define PGL_GOLDEN_ANGLE (M_PI * (sqrt(5) - 1.0)) 8.1009 + 8.1010 +/* create a list of sample points covering a bounding circle 8.1011 + and return covered area */ 8.1012 +static double pgl_sample_points( 8.1013 + pgl_point *center, /* center of bounding circle */ 8.1014 + double radius, /* radius of bounding circle */ 8.1015 + int samples, /* number of sample points (MUST be positive!) */ 8.1016 + pgl_point *result /* pointer to result array */ 8.1017 +) { 8.1018 + double double_share = 2.0; /* double of covered share of earth's surface */ 8.1019 + double double_share_div_samples; /* double_share divided by sample count */ 8.1020 + int i; 8.1021 + double t; /* parameter of spiral laid on (spherical) earth's surface */ 8.1022 + double x, y, z; /* normalized coordinates of point on non-rotated spiral */ 8.1023 + double sin_phi; /* sine of sph. coordinate of point of non-rotated spiral */ 8.1024 + double lambda; /* other sph. coordinate of point of non-rotated spiral */ 8.1025 + double rot = (0.5 - center->lat / 180.0) * M_PI; /* needed rot. (in rad) */ 8.1026 + double cos_rot = cos(rot); /* cosine of rotation by latitude */ 8.1027 + double sin_rot = sin(rot); /* sine of rotation by latitude */ 8.1028 + double x_rot, z_rot; /* normalized coordinates of point on rotated spiral */ 8.1029 + double center_lon = center->lon; /* second rotation in degree */ 8.1030 + /* add safety margin to bounding circle because of spherical approximation */ 8.1031 + radius *= PGL_SPHEROID_A / PGL_RADIUS; 8.1032 + /* if whole earth is covered, use initialized value, otherwise calculate 8.1033 + share of covered area (multiplied by 2) */ 8.1034 + if (radius < PGL_MAXDIST) double_share = 1.0 - cos(radius / PGL_RADIUS); 8.1035 + /* divide double_share by sample count for later calculations */ 8.1036 + double_share_div_samples = double_share / samples; 8.1037 + /* generate sample points */ 8.1038 + for (i=0; i<samples; i++) { 8.1039 + /* use an offset of 1/2 to avoid too dense clustering at spiral center */ 8.1040 + t = 0.5 + i; 8.1041 + /* calculate normalized coordinates of point on non-rotated spiral */ 8.1042 + z = 1.0 - double_share_div_samples * t; 8.1043 + sin_phi = sqrt(1.0 - z*z); 8.1044 + lambda = t * PGL_GOLDEN_ANGLE; 8.1045 + x = sin_phi * cos(lambda); 8.1046 + y = sin_phi * sin(lambda); 8.1047 + /* rotate spiral by latitude value of bounding circle */ 8.1048 + x_rot = cos_rot * x + sin_rot * z; 8.1049 + z_rot = cos_rot * z - sin_rot * x; 8.1050 + /* set resulting sample point in result array */ 8.1051 + /* (while performing second rotation by bounding circle longitude) */ 8.1052 + result[i].lat = 180.0 * (atan(z_rot / fabs(x_rot)) / M_PI); 8.1053 + result[i].lon = center_lon + 180.0 * (atan2(y, x_rot) / M_PI); 8.1054 + } 8.1055 + /* return covered area */ 8.1056 + return PGL_HALF_SURFACE * double_share; 8.1057 +} 8.1058 + 8.1059 +/* fair distance between point and cluster (see README file for explanation) */ 8.1060 +/* NOTE: sample count passed as third argument MUST be positive */ 8.1061 +static double pgl_fair_distance( 8.1062 + pgl_point *point, pgl_cluster *cluster, int samples 8.1063 +) { 8.1064 + double distance; /* shortest distance from point to cluster */ 8.1065 + pgl_point *points; /* sample points for numerical integration */ 8.1066 + double area; /* area covered by sample points */ 8.1067 + int i; 8.1068 + int inner = 0; /* number of sample points within cluster */ 8.1069 + int outer = 0; /* number of sample points outside cluster but 8.1070 + within cluster enlarged by distance */ 8.1071 + double result; 8.1072 + /* calculate shortest distance from point to cluster */ 8.1073 + distance = pgl_point_cluster_distance(point, cluster); 8.1074 + /* if cluster consists of a single point or has no bounding circle with 8.1075 + positive radius, simply return distance */ 8.1076 + if ( 8.1077 + (cluster->nentries==1 && cluster->entries[0].entrytype==PGL_ENTRY_POINT) || 8.1078 + !(cluster->bounding.radius > 0) 8.1079 + ) return distance; 8.1080 + /* if cluster consists of two points which are twice as far apart, return 8.1081 + distance between point and cluster multiplied by square root of two */ 8.1082 + if ( 8.1083 + cluster->nentries == 2 && 8.1084 + cluster->entries[0].entrytype == PGL_ENTRY_POINT && 8.1085 + cluster->entries[1].entrytype == PGL_ENTRY_POINT && 8.1086 + pgl_distance( 8.1087 + PGL_ENTRY_POINTS(cluster, 0)[0].lat, 8.1088 + PGL_ENTRY_POINTS(cluster, 0)[0].lon, 8.1089 + PGL_ENTRY_POINTS(cluster, 1)[0].lat, 8.1090 + PGL_ENTRY_POINTS(cluster, 1)[0].lon 8.1091 + ) >= 2.0 * distance 8.1092 + ) { 8.1093 + return distance * M_SQRT2; 8.1094 + } 8.1095 + /* otherwise create sample points for numerical integration and determine 8.1096 + area covered by sample points */ 8.1097 + points = palloc(samples * sizeof(pgl_point)); 8.1098 + area = pgl_sample_points( 8.1099 + &cluster->bounding.center, 8.1100 + cluster->bounding.radius + distance, /* pad bounding circle by distance */ 8.1101 + samples, 8.1102 + points 8.1103 + ); 8.1104 + /* perform numerical integration */ 8.1105 + if (distance > 0) { 8.1106 + /* point (that was passed as argument) is outside cluster */ 8.1107 + for (i=0; i<samples; i++) { 8.1108 + /* count sample points within cluster */ 8.1109 + if (pgl_point_in_cluster(points+i, cluster, true)) inner++; 8.1110 + /* count sample points outside of cluster but within cluster enlarged by 8.1111 + distance between point (that was passed as argument) and cluster */ 8.1112 + else if ( 8.1113 + pgl_point_cluster_distance(points+i, cluster) < distance 8.1114 + ) outer++; 8.1115 + } 8.1116 + } else { 8.1117 + /* if point is within cluster, just count sample points within cluster */ 8.1118 + for (i=0; i<samples; i++) { 8.1119 + if (pgl_point_in_cluster(points+i, cluster, true)) inner++; 8.1120 + } 8.1121 + } 8.1122 + /* release memory for sample points needed for numerical integration */ 8.1123 + pfree(points); 8.1124 + /* if enlargement was less than doubling the area, then combine inner and 8.1125 + outer sample point counts with different weighting */ 8.1126 + /* (ensures fairness in such a way that the integral of the squared result 8.1127 + over all possible point parameters is independent of the cluster) */ 8.1128 + if (outer < inner) result = (2*inner + 4*outer) / 3.0; 8.1129 + /* otherwise weigh inner and outer points the same */ 8.1130 + else result = inner + outer; 8.1131 + /* convert area into distance (i.e. radius of a circle with the same area) */ 8.1132 + result = sqrt(area * (result / samples) / M_PI); 8.1133 + /* return result only if it is greater than the distance between point and 8.1134 + cluster to avoid unexpected results because of errors due to limited 8.1135 + precision */ 8.1136 + if (result > distance) return result; 8.1137 + /* otherwise return distance between point and cluster */ 8.1138 + else return distance; 8.1139 +} 8.1140 + 8.1141 + 8.1142 +/*-------------------------------------------------* 8.1143 + * geographic index based on space-filling curve * 8.1144 + *-------------------------------------------------*/ 8.1145 + 8.1146 +/* number of bytes used for geographic (center) position in keys */ 8.1147 +#define PGL_KEY_LATLON_BYTELEN 7 8.1148 + 8.1149 +/* maximum reference value for logarithmic size of geographic objects */ 8.1150 +#define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0) /* can be tweaked */ 8.1151 + 8.1152 +/* pointer to index key (either pgl_pointkey or pgl_areakey) */ 8.1153 +typedef unsigned char *pgl_keyptr; 8.1154 + 8.1155 +/* index key for points (objects with zero area) on the spheroid */ 8.1156 +/* bit 0..55: interspersed bits of latitude and longitude, 8.1157 + bit 56..57: always zero, 8.1158 + bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */ 8.1159 +typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1]; 8.1160 + 8.1161 +/* index key for geographic objects on spheroid with area greater than zero */ 8.1162 +/* bit 0..55: interspersed bits of latitude and longitude of center point, 8.1163 + bit 56: always set to 1, 8.1164 + bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.), 8.1165 + bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to 8.1166 + PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth 8.1167 + = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL 8.1168 + (with interspersed bits = 0 and node depth = 0) for keys which 8.1169 + cover both empty and non-empty objects */ 8.1170 + 8.1171 +typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2]; 8.1172 + 8.1173 +/* helper macros for reading/writing index keys */ 8.1174 +#define PGL_KEY_NODEDEPTH_OFFSET PGL_KEY_LATLON_BYTELEN 8.1175 +#define PGL_KEY_OBJSIZE_OFFSET (PGL_KEY_NODEDEPTH_OFFSET+1) 8.1176 +#define PGL_POINTKEY_MAXDEPTH (PGL_KEY_LATLON_BYTELEN*8) 8.1177 +#define PGL_AREAKEY_MAXDEPTH (2*PGL_POINTKEY_MAXDEPTH+1) 8.1178 +#define PGL_AREAKEY_MAXOBJSIZE (PGL_POINTKEY_MAXDEPTH+1) 8.1179 +#define PGL_AREAKEY_TYPEMASK 0x80 8.1180 +#define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8))) 8.1181 +#define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \ 8.1182 + ( PGL_KEY_LATLONBIT(key1, n) ^ \ 8.1183 + PGL_KEY_LATLONBIT(key2, n) ) 8.1184 +#define PGL_KEY_IS_AREAKEY(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \ 8.1185 + PGL_AREAKEY_TYPEMASK) 8.1186 +#define PGL_KEY_NODEDEPTH(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \ 8.1187 + (PGL_AREAKEY_TYPEMASK-1)) 8.1188 +#define PGL_KEY_OBJSIZE(key) ((key)[PGL_KEY_OBJSIZE_OFFSET]) 8.1189 +#define PGL_KEY_OBJSIZE_EMPTY 126 8.1190 +#define PGL_KEY_OBJSIZE_UNIVERSAL 127 8.1191 +#define PGL_KEY_IS_EMPTY(key) ( PGL_KEY_IS_AREAKEY(key) && \ 8.1192 + (key)[PGL_KEY_OBJSIZE_OFFSET] == \ 8.1193 + PGL_KEY_OBJSIZE_EMPTY ) 8.1194 +#define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \ 8.1195 + (key)[PGL_KEY_OBJSIZE_OFFSET] == \ 8.1196 + PGL_KEY_OBJSIZE_UNIVERSAL ) 8.1197 + 8.1198 +/* set area key to match empty objects only */ 8.1199 +static void pgl_key_set_empty(pgl_keyptr key) { 8.1200 + memset(key, 0, sizeof(pgl_areakey)); 8.1201 + /* Note: setting node depth to maximum is required for picksplit function */ 8.1202 + key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH; 8.1203 + key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY; 8.1204 +} 8.1205 + 8.1206 +/* set area key to match any object (including empty objects) */ 8.1207 +static void pgl_key_set_universal(pgl_keyptr key) { 8.1208 + memset(key, 0, sizeof(pgl_areakey)); 8.1209 + key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK; 8.1210 + key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL; 8.1211 +} 8.1212 + 8.1213 +/* convert a point on earth into a max-depth key to be used in index */ 8.1214 +static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) { 8.1215 + double lat = point->lat; 8.1216 + double lon = point->lon; 8.1217 + int i; 8.1218 + /* clear latitude and longitude bits */ 8.1219 + memset(key, 0, PGL_KEY_LATLON_BYTELEN); 8.1220 + /* set node depth to maximum and type bit to zero */ 8.1221 + key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH; 8.1222 + /* iterate over all latitude/longitude bit pairs */ 8.1223 + for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) { 8.1224 + /* determine latitude bit */ 8.1225 + if (lat >= 0) { 8.1226 + key[i/4] |= 0x80 >> (2*(i%4)); 8.1227 + lat *= 2; lat -= 90; 8.1228 + } else { 8.1229 + lat *= 2; lat += 90; 8.1230 + } 8.1231 + /* determine longitude bit */ 8.1232 + if (lon >= 0) { 8.1233 + key[i/4] |= 0x80 >> (2*(i%4)+1); 8.1234 + lon *= 2; lon -= 180; 8.1235 + } else { 8.1236 + lon *= 2; lon += 180; 8.1237 + } 8.1238 + } 8.1239 +} 8.1240 + 8.1241 +/* convert a circle on earth into a max-depth key to be used in an index */ 8.1242 +static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) { 8.1243 + /* handle special case of empty circle */ 8.1244 + if (circle->radius < 0) { 8.1245 + pgl_key_set_empty(key); 8.1246 + return; 8.1247 + } 8.1248 + /* perform same action as for point keys */ 8.1249 + pgl_point_to_key(&(circle->center), key); 8.1250 + /* but overwrite type and node depth to fit area index key */ 8.1251 + key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH; 8.1252 + /* check if radius is greater than (or equal to) reference size */ 8.1253 + /* (treat equal values as greater values for numerical safety) */ 8.1254 + if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) { 8.1255 + /* if yes, set logarithmic size to zero */ 8.1256 + key[PGL_KEY_OBJSIZE_OFFSET] = 0; 8.1257 + } else { 8.1258 + /* otherwise, determine logarithmic size iteratively */ 8.1259 + /* (one step is equivalent to a factor of sqrt(2)) */ 8.1260 + double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2; 8.1261 + int objsize = 1; 8.1262 + while (objsize < PGL_AREAKEY_MAXOBJSIZE) { 8.1263 + /* stop when radius is greater than (or equal to) adjusted reference */ 8.1264 + /* (treat equal values as greater values for numerical safety) */ 8.1265 + if (circle->radius >= reference) break; 8.1266 + reference /= M_SQRT2; 8.1267 + objsize++; 8.1268 + } 8.1269 + /* set logarithmic size to determined value */ 8.1270 + key[PGL_KEY_OBJSIZE_OFFSET] = objsize; 8.1271 + } 8.1272 +} 8.1273 + 8.1274 +/* check if one key is subkey of another key or vice versa */ 8.1275 +static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) { 8.1276 + int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */ 8.1277 + /* determine smallest depth */ 8.1278 + int depth1 = PGL_KEY_NODEDEPTH(key1); 8.1279 + int depth2 = PGL_KEY_NODEDEPTH(key2); 8.1280 + int depth = (depth1 < depth2) ? depth1 : depth2; 8.1281 + /* check if keys are area keys (assuming that both keys have same type) */ 8.1282 + if (PGL_KEY_IS_AREAKEY(key1)) { 8.1283 + int j = 0; /* bit offset for logarithmic object size bits */ 8.1284 + int k = 0; /* bit offset for latitude and longitude */ 8.1285 + /* fetch logarithmic object size information */ 8.1286 + int objsize1 = PGL_KEY_OBJSIZE(key1); 8.1287 + int objsize2 = PGL_KEY_OBJSIZE(key2); 8.1288 + /* handle special cases for empty objects (universal and empty keys) */ 8.1289 + if ( 8.1290 + objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL || 8.1291 + objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL 8.1292 + ) return true; 8.1293 + if ( 8.1294 + objsize1 == PGL_KEY_OBJSIZE_EMPTY || 8.1295 + objsize2 == PGL_KEY_OBJSIZE_EMPTY 8.1296 + ) return objsize1 == objsize2; 8.1297 + /* iterate through key bits */ 8.1298 + for (i=0; i<depth; i++) { 8.1299 + /* every second bit is a bit describing the object size */ 8.1300 + if (i%2 == 0) { 8.1301 + /* check if object size bit is different in both keys (objsize1 and 8.1302 + objsize2 describe the minimum index when object size bit is set) */ 8.1303 + if ( 8.1304 + (objsize1 <= j && objsize2 > j) || 8.1305 + (objsize2 <= j && objsize1 > j) 8.1306 + ) { 8.1307 + /* bit differs, therefore keys are in separate branches */ 8.1308 + return false; 8.1309 + } 8.1310 + /* increase bit counter for object size bits */ 8.1311 + j++; 8.1312 + } 8.1313 + /* all other bits describe latitude and longitude */ 8.1314 + else { 8.1315 + /* check if bit differs in both keys */ 8.1316 + if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) { 8.1317 + /* bit differs, therefore keys are in separate branches */ 8.1318 + return false; 8.1319 + } 8.1320 + /* increase bit counter for latitude/longitude bits */ 8.1321 + k++; 8.1322 + } 8.1323 + } 8.1324 + } 8.1325 + /* if not, keys are point keys */ 8.1326 + else { 8.1327 + /* iterate through key bits */ 8.1328 + for (i=0; i<depth; i++) { 8.1329 + /* check if bit differs in both keys */ 8.1330 + if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) { 8.1331 + /* bit differs, therefore keys are in separate branches */ 8.1332 + return false; 8.1333 + } 8.1334 + } 8.1335 + } 8.1336 + /* return true because keys are in the same branch */ 8.1337 + return true; 8.1338 +} 8.1339 + 8.1340 +/* combine two keys into new key which covers both original keys */ 8.1341 +/* (result stored in first argument) */ 8.1342 +static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) { 8.1343 + int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */ 8.1344 + /* determine smallest depth */ 8.1345 + int depth1 = PGL_KEY_NODEDEPTH(dst); 8.1346 + int depth2 = PGL_KEY_NODEDEPTH(src); 8.1347 + int depth = (depth1 < depth2) ? depth1 : depth2; 8.1348 + /* check if keys are area keys (assuming that both keys have same type) */ 8.1349 + if (PGL_KEY_IS_AREAKEY(dst)) { 8.1350 + pgl_areakey dstbuf = { 0, }; /* destination buffer (cleared) */ 8.1351 + int j = 0; /* bit offset for logarithmic object size bits */ 8.1352 + int k = 0; /* bit offset for latitude and longitude */ 8.1353 + /* fetch logarithmic object size information */ 8.1354 + int objsize1 = PGL_KEY_OBJSIZE(dst); 8.1355 + int objsize2 = PGL_KEY_OBJSIZE(src); 8.1356 + /* handle special cases for empty objects (universal and empty keys) */ 8.1357 + if ( 8.1358 + objsize1 > PGL_AREAKEY_MAXOBJSIZE || 8.1359 + objsize2 > PGL_AREAKEY_MAXOBJSIZE 8.1360 + ) { 8.1361 + if ( 8.1362 + objsize1 == PGL_KEY_OBJSIZE_EMPTY && 8.1363 + objsize2 == PGL_KEY_OBJSIZE_EMPTY 8.1364 + ) pgl_key_set_empty(dst); 8.1365 + else pgl_key_set_universal(dst); 8.1366 + return; 8.1367 + } 8.1368 + /* iterate through key bits */ 8.1369 + for (i=0; i<depth; i++) { 8.1370 + /* every second bit is a bit describing the object size */ 8.1371 + if (i%2 == 0) { 8.1372 + /* increase bit counter for object size bits first */ 8.1373 + /* (handy when setting objsize variable) */ 8.1374 + j++; 8.1375 + /* check if object size bit is set in neither key */ 8.1376 + if (objsize1 >= j && objsize2 >= j) { 8.1377 + /* set objsize in destination buffer to indicate that size bit is 8.1378 + unset in destination buffer at the current bit position */ 8.1379 + dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j; 8.1380 + } 8.1381 + /* break if object size bit is set in one key only */ 8.1382 + else if (objsize1 >= j || objsize2 >= j) break; 8.1383 + } 8.1384 + /* all other bits describe latitude and longitude */ 8.1385 + else { 8.1386 + /* break if bit differs in both keys */ 8.1387 + if (PGL_KEY_LATLONBIT(dst, k)) { 8.1388 + if (!PGL_KEY_LATLONBIT(src, k)) break; 8.1389 + /* but set bit in destination buffer if bit is set in both keys */ 8.1390 + dstbuf[k/8] |= 0x80 >> (k%8); 8.1391 + } else if (PGL_KEY_LATLONBIT(src, k)) break; 8.1392 + /* increase bit counter for latitude/longitude bits */ 8.1393 + k++; 8.1394 + } 8.1395 + } 8.1396 + /* set common node depth and type bit (type bit = 1) */ 8.1397 + dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i; 8.1398 + /* copy contents of destination buffer to first key */ 8.1399 + memcpy(dst, dstbuf, sizeof(pgl_areakey)); 8.1400 + } 8.1401 + /* if not, keys are point keys */ 8.1402 + else { 8.1403 + pgl_pointkey dstbuf = { 0, }; /* destination buffer (cleared) */ 8.1404 + /* iterate through key bits */ 8.1405 + for (i=0; i<depth; i++) { 8.1406 + /* break if bit differs in both keys */ 8.1407 + if (PGL_KEY_LATLONBIT(dst, i)) { 8.1408 + if (!PGL_KEY_LATLONBIT(src, i)) break; 8.1409 + /* but set bit in destination buffer if bit is set in both keys */ 8.1410 + dstbuf[i/8] |= 0x80 >> (i%8); 8.1411 + } else if (PGL_KEY_LATLONBIT(src, i)) break; 8.1412 + } 8.1413 + /* set common node depth (type bit = 0) */ 8.1414 + dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i; 8.1415 + /* copy contents of destination buffer to first key */ 8.1416 + memcpy(dst, dstbuf, sizeof(pgl_pointkey)); 8.1417 + } 8.1418 +} 8.1419 + 8.1420 +/* determine center(!) boundaries and radius estimation of index key */ 8.1421 +static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) { 8.1422 + int i; 8.1423 + /* determine node depth */ 8.1424 + int depth = PGL_KEY_NODEDEPTH(key); 8.1425 + /* center point of possible result */ 8.1426 + double lat = 0; 8.1427 + double lon = 0; 8.1428 + /* maximum distance of real center point from key center */ 8.1429 + double dlat = 90; 8.1430 + double dlon = 180; 8.1431 + /* maximum radius of contained objects */ 8.1432 + double radius = 0; /* always return zero for point index keys */ 8.1433 + /* check if key is area key */ 8.1434 + if (PGL_KEY_IS_AREAKEY(key)) { 8.1435 + /* get logarithmic object size */ 8.1436 + int objsize = PGL_KEY_OBJSIZE(key); 8.1437 + /* handle special cases for empty objects (universal and empty keys) */ 8.1438 + if (objsize == PGL_KEY_OBJSIZE_EMPTY) { 8.1439 + pgl_box_set_empty(box); 8.1440 + return 0; 8.1441 + } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) { 8.1442 + box->lat_min = -90; 8.1443 + box->lat_max = 90; 8.1444 + box->lon_min = -180; 8.1445 + box->lon_max = 180; 8.1446 + return 0; /* any value >= 0 would do */ 8.1447 + } 8.1448 + /* calculate maximum possible radius of objects covered by the given key */ 8.1449 + if (objsize == 0) radius = INFINITY; 8.1450 + else { 8.1451 + radius = PGL_AREAKEY_REFOBJSIZE; 8.1452 + while (--objsize) radius /= M_SQRT2; 8.1453 + } 8.1454 + /* iterate over latitude and longitude bits in key */ 8.1455 + /* (every second bit is a latitude or longitude bit) */ 8.1456 + for (i=0; i<depth/2; i++) { 8.1457 + /* check if latitude bit */ 8.1458 + if (i%2 == 0) { 8.1459 + /* cut latitude dimension in half */ 8.1460 + dlat /= 2; 8.1461 + /* increase center latitude if bit is 1, otherwise decrease */ 8.1462 + if (PGL_KEY_LATLONBIT(key, i)) lat += dlat; 8.1463 + else lat -= dlat; 8.1464 + } 8.1465 + /* otherwise longitude bit */ 8.1466 + else { 8.1467 + /* cut longitude dimension in half */ 8.1468 + dlon /= 2; 8.1469 + /* increase center longitude if bit is 1, otherwise decrease */ 8.1470 + if (PGL_KEY_LATLONBIT(key, i)) lon += dlon; 8.1471 + else lon -= dlon; 8.1472 + } 8.1473 + } 8.1474 + } 8.1475 + /* if not, keys are point keys */ 8.1476 + else { 8.1477 + /* iterate over all bits in key */ 8.1478 + for (i=0; i<depth; i++) { 8.1479 + /* check if latitude bit */ 8.1480 + if (i%2 == 0) { 8.1481 + /* cut latitude dimension in half */ 8.1482 + dlat /= 2; 8.1483 + /* increase center latitude if bit is 1, otherwise decrease */ 8.1484 + if (PGL_KEY_LATLONBIT(key, i)) lat += dlat; 8.1485 + else lat -= dlat; 8.1486 + } 8.1487 + /* otherwise longitude bit */ 8.1488 + else { 8.1489 + /* cut longitude dimension in half */ 8.1490 + dlon /= 2; 8.1491 + /* increase center longitude if bit is 1, otherwise decrease */ 8.1492 + if (PGL_KEY_LATLONBIT(key, i)) lon += dlon; 8.1493 + else lon -= dlon; 8.1494 + } 8.1495 + } 8.1496 + } 8.1497 + /* calculate boundaries from center point and remaining dlat and dlon */ 8.1498 + /* (return values through pointer to box) */ 8.1499 + box->lat_min = lat - dlat; 8.1500 + box->lat_max = lat + dlat; 8.1501 + box->lon_min = lon - dlon; 8.1502 + box->lon_max = lon + dlon; 8.1503 + /* return radius (as a function return value) */ 8.1504 + return radius; 8.1505 +} 8.1506 + 8.1507 +/* estimator function for distance between point and index key */ 8.1508 +/* always returns a smaller value than actually correct or zero */ 8.1509 +static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) { 8.1510 + pgl_box box; /* center(!) bounding box of area index key */ 8.1511 + /* calculate center(!) bounding box and maximum radius of objects covered 8.1512 + by area index key (radius is zero for point index keys) */ 8.1513 + double distance = pgl_key_to_box(key, &box); 8.1514 + /* calculate estimated distance between bounding box of center point of 8.1515 + indexed object and point passed as second argument, then substract maximum 8.1516 + radius of objects covered by index key */ 8.1517 + distance = pgl_estimate_point_box_distance(point, &box) - distance; 8.1518 + /* truncate negative results to zero */ 8.1519 + if (distance <= 0) distance = 0; 8.1520 + /* return result */ 8.1521 + return distance; 8.1522 +} 8.1523 + 8.1524 + 8.1525 +/*---------------------------------* 8.1526 + * helper functions for text I/O * 8.1527 + *---------------------------------*/ 8.1528 + 8.1529 +#define PGL_NUMBUFLEN 64 /* buffer size for number to string conversion */ 8.1530 + 8.1531 +/* convert floating point number to string (round-trip safe) */ 8.1532 +static void pgl_print_float(char *buf, double flt) { 8.1533 + /* check if number is integral */ 8.1534 + if (trunc(flt) == flt) { 8.1535 + /* for integral floats use maximum precision */ 8.1536 + snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt); 8.1537 + } else { 8.1538 + /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */ 8.1539 + snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt); 8.1540 + if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt); 8.1541 + if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt); 8.1542 + } 8.1543 +} 8.1544 + 8.1545 +/* convert latitude floating point number (in degrees) to string */ 8.1546 +static void pgl_print_lat(char *buf, double lat) { 8.1547 + if (signbit(lat)) { 8.1548 + /* treat negative latitudes (including -0) as south */ 8.1549 + snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat); 8.1550 + } else { 8.1551 + /* treat positive latitudes (including +0) as north */ 8.1552 + snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat); 8.1553 + } 8.1554 +} 8.1555 + 8.1556 +/* convert longitude floating point number (in degrees) to string */ 8.1557 +static void pgl_print_lon(char *buf, double lon) { 8.1558 + if (signbit(lon)) { 8.1559 + /* treat negative longitudes (including -0) as west */ 8.1560 + snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon); 8.1561 + } else { 8.1562 + /* treat positive longitudes (including +0) as east */ 8.1563 + snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon); 8.1564 + } 8.1565 +} 8.1566 + 8.1567 +/* bit masks used as return value of pgl_scan() function */ 8.1568 +#define PGL_SCAN_NONE 0 /* no value has been parsed */ 8.1569 +#define PGL_SCAN_LAT (1<<0) /* latitude has been parsed */ 8.1570 +#define PGL_SCAN_LON (1<<1) /* longitude has been parsed */ 8.1571 +#define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON) /* bitwise OR of both */ 8.1572 + 8.1573 +/* parse a coordinate (can be latitude or longitude) */ 8.1574 +static int pgl_scan(char **str, double *lat, double *lon) { 8.1575 + double val; 8.1576 + int len; 8.1577 + if ( 8.1578 + sscanf(*str, " N %lf %n", &val, &len) || 8.1579 + sscanf(*str, " n %lf %n", &val, &len) 8.1580 + ) { 8.1581 + *str += len; *lat = val; return PGL_SCAN_LAT; 8.1582 + } 8.1583 + if ( 8.1584 + sscanf(*str, " S %lf %n", &val, &len) || 8.1585 + sscanf(*str, " s %lf %n", &val, &len) 8.1586 + ) { 8.1587 + *str += len; *lat = -val; return PGL_SCAN_LAT; 8.1588 + } 8.1589 + if ( 8.1590 + sscanf(*str, " E %lf %n", &val, &len) || 8.1591 + sscanf(*str, " e %lf %n", &val, &len) 8.1592 + ) { 8.1593 + *str += len; *lon = val; return PGL_SCAN_LON; 8.1594 + } 8.1595 + if ( 8.1596 + sscanf(*str, " W %lf %n", &val, &len) || 8.1597 + sscanf(*str, " w %lf %n", &val, &len) 8.1598 + ) { 8.1599 + *str += len; *lon = -val; return PGL_SCAN_LON; 8.1600 + } 8.1601 + return PGL_SCAN_NONE; 8.1602 +} 8.1603 + 8.1604 + 8.1605 +/*-----------------* 8.1606 + * SQL functions * 8.1607 + *-----------------*/ 8.1608 + 8.1609 +/* Note: These function names use "epoint", "ebox", etc. notation here instead 8.1610 + of "point", "box", etc. in order to distinguish them from any previously 8.1611 + defined functions. */ 8.1612 + 8.1613 +/* function needed for dummy types and/or not implemented features */ 8.1614 +PG_FUNCTION_INFO_V1(pgl_notimpl); 8.1615 +Datum pgl_notimpl(PG_FUNCTION_ARGS) { 8.1616 + ereport(ERROR, (errmsg("not implemented by pgLatLon"))); 8.1617 +} 8.1618 + 8.1619 +/* set point to latitude and longitude (including checks) */ 8.1620 +static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) { 8.1621 + /* reject infinite or NaN values */ 8.1622 + if (!isfinite(lat) || !isfinite(lon)) { 8.1623 + ereport(ERROR, ( 8.1624 + errcode(ERRCODE_DATA_EXCEPTION), 8.1625 + errmsg("epoint requires finite coordinates") 8.1626 + )); 8.1627 + } 8.1628 + /* check latitude bounds */ 8.1629 + if (lat < -90) { 8.1630 + ereport(WARNING, (errmsg("latitude exceeds south pole"))); 8.1631 + lat = -90; 8.1632 + } else if (lat > 90) { 8.1633 + ereport(WARNING, (errmsg("latitude exceeds north pole"))); 8.1634 + lat = 90; 8.1635 + } 8.1636 + /* check longitude bounds */ 8.1637 + if (lon < -180) { 8.1638 + ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized"))); 8.1639 + lon += 360 - trunc(lon / 360) * 360; 8.1640 + } else if (lon > 180) { 8.1641 + ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized"))); 8.1642 + lon -= 360 + trunc(lon / 360) * 360; 8.1643 + } 8.1644 + /* store rounded latitude/longitude values for round-trip safety */ 8.1645 + point->lat = pgl_round(lat); 8.1646 + point->lon = pgl_round(lon); 8.1647 +} 8.1648 + 8.1649 +/* create point ("epoint" in SQL) from latitude and longitude */ 8.1650 +PG_FUNCTION_INFO_V1(pgl_create_epoint); 8.1651 +Datum pgl_create_epoint(PG_FUNCTION_ARGS) { 8.1652 + pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point)); 8.1653 + pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1)); 8.1654 + PG_RETURN_POINTER(point); 8.1655 +} 8.1656 + 8.1657 +/* parse point ("epoint" in SQL) */ 8.1658 +/* format: '[NS]<float> [EW]<float>' */ 8.1659 +PG_FUNCTION_INFO_V1(pgl_epoint_in); 8.1660 +Datum pgl_epoint_in(PG_FUNCTION_ARGS) { 8.1661 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.1662 + char *strptr = str; /* current position within string */ 8.1663 + int done = 0; /* bit mask storing if latitude or longitude was read */ 8.1664 + double lat, lon; /* parsed values as double precision floats */ 8.1665 + pgl_point *point; /* return value (to be palloc'ed) */ 8.1666 + /* parse two floats (each latitude or longitude) separated by white-space */ 8.1667 + done |= pgl_scan(&strptr, &lat, &lon); 8.1668 + if (strptr != str && isspace(strptr[-1])) { 8.1669 + done |= pgl_scan(&strptr, &lat, &lon); 8.1670 + } 8.1671 + /* require end of string, and latitude and longitude parsed successfully */ 8.1672 + if (strptr[0] || done != PGL_SCAN_LATLON) { 8.1673 + ereport(ERROR, ( 8.1674 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.1675 + errmsg("invalid input syntax for type epoint: \"%s\"", str) 8.1676 + )); 8.1677 + } 8.1678 + /* allocate memory for result */ 8.1679 + point = (pgl_point *)palloc(sizeof(pgl_point)); 8.1680 + /* set latitude and longitude (and perform checks) */ 8.1681 + pgl_epoint_set_latlon(point, lat, lon); 8.1682 + /* return result */ 8.1683 + PG_RETURN_POINTER(point); 8.1684 +} 8.1685 + 8.1686 +/* set sample count for numerical integration (including checks) */ 8.1687 +static void pgl_epoint_set_sample_count(pgl_point_sc *search, int32 samples) { 8.1688 + /* require minimum of 6 samples */ 8.1689 + if (samples < 6) { 8.1690 + ereport(ERROR, ( 8.1691 + errcode(ERRCODE_DATA_EXCEPTION), 8.1692 + errmsg("too few sample points for numerical integration (minimum 6)") 8.1693 + )); 8.1694 + } 8.1695 + /* limit sample count to avoid integer overflows on memory allocation */ 8.1696 + if (samples > PGL_CLUSTER_MAXPOINTS) { 8.1697 + ereport(ERROR, ( 8.1698 + errcode(ERRCODE_DATA_EXCEPTION), 8.1699 + errmsg( 8.1700 + "too many sample points for numerical integration (maximum %i)", 8.1701 + PGL_CLUSTER_MAXPOINTS 8.1702 + ) 8.1703 + )); 8.1704 + } 8.1705 + search->samples = samples; 8.1706 +} 8.1707 + 8.1708 +/* create point with sample count for fair distance calculation 8.1709 + ("epoint_with_sample_count" in SQL) from epoint and integer */ 8.1710 +PG_FUNCTION_INFO_V1(pgl_create_epoint_with_sample_count); 8.1711 +Datum pgl_create_epoint_with_sample_count(PG_FUNCTION_ARGS) { 8.1712 + pgl_point_sc *search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc)); 8.1713 + search->point = *(pgl_point *)PG_GETARG_POINTER(0); 8.1714 + pgl_epoint_set_sample_count(search, PG_GETARG_INT32(1)); 8.1715 + PG_RETURN_POINTER(search); 8.1716 +} 8.1717 + 8.1718 +/* parse point with sample count ("epoint_with_sample_count" in SQL) */ 8.1719 +/* format: '[NS]<float> [EW]<float> <integer>' */ 8.1720 +PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_in); 8.1721 +Datum pgl_epoint_with_sample_count_in(PG_FUNCTION_ARGS) { 8.1722 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.1723 + char *strptr = str; /* current position within string */ 8.1724 + double lat, lon; /* parsed values for latitude and longitude */ 8.1725 + int samples; /* parsed value for sample count */ 8.1726 + int valid = 0; /* number of valid chars */ 8.1727 + int done = 0; /* stores if latitude and/or longitude was read */ 8.1728 + pgl_point_sc *search; /* return value (to be palloc'ed) */ 8.1729 + /* demand three blocks separated by whitespace */ 8.1730 + sscanf(strptr, " %*s %*s %*s %n", &valid); 8.1731 + /* if three blocks separated by whitespace exist, parse those blocks */ 8.1732 + if (strptr[valid] == 0) { 8.1733 + /* parse latitude and longitude */ 8.1734 + done |= pgl_scan(&strptr, &lat, &lon); 8.1735 + done |= pgl_scan(&strptr, &lat, &lon); 8.1736 + /* parse sample count (while incr. strptr by number of bytes parsed) */ 8.1737 + valid = 0; 8.1738 + if (sscanf(strptr, " %d %n", &samples, &valid) == 1) strptr += valid; 8.1739 + } 8.1740 + /* require end of string and both latitude and longitude being parsed */ 8.1741 + if (strptr[0] || done != PGL_SCAN_LATLON) { 8.1742 + ereport(ERROR, ( 8.1743 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.1744 + errmsg("invalid input syntax for type ecircle: \"%s\"", str) 8.1745 + )); 8.1746 + } 8.1747 + /* allocate memory for result */ 8.1748 + search = (pgl_point_sc *)palloc(sizeof(pgl_point_sc)); 8.1749 + /* set latitude, longitude, and sample count (while performing checks) */ 8.1750 + pgl_epoint_set_latlon(&search->point, lat, lon); 8.1751 + pgl_epoint_set_sample_count(search, samples); 8.1752 + /* return result */ 8.1753 + PG_RETURN_POINTER(search); 8.1754 +} 8.1755 + 8.1756 +/* create box ("ebox" in SQL) that is empty */ 8.1757 +PG_FUNCTION_INFO_V1(pgl_create_empty_ebox); 8.1758 +Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) { 8.1759 + pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1760 + pgl_box_set_empty(box); 8.1761 + PG_RETURN_POINTER(box); 8.1762 +} 8.1763 + 8.1764 +/* set box to given boundaries (including checks) */ 8.1765 +static void pgl_ebox_set_boundaries( 8.1766 + pgl_box *box, 8.1767 + double lat_min, double lat_max, double lon_min, double lon_max 8.1768 +) { 8.1769 + /* if minimum latitude is greater than maximum latitude, return empty box */ 8.1770 + if (lat_min > lat_max) { 8.1771 + pgl_box_set_empty(box); 8.1772 + return; 8.1773 + } 8.1774 + /* otherwise reject infinite or NaN values */ 8.1775 + if ( 8.1776 + !isfinite(lat_min) || !isfinite(lat_max) || 8.1777 + !isfinite(lon_min) || !isfinite(lon_max) 8.1778 + ) { 8.1779 + ereport(ERROR, ( 8.1780 + errcode(ERRCODE_DATA_EXCEPTION), 8.1781 + errmsg("ebox requires finite coordinates") 8.1782 + )); 8.1783 + } 8.1784 + /* check latitude bounds */ 8.1785 + if (lat_max < -90) { 8.1786 + ereport(WARNING, (errmsg("northern latitude exceeds south pole"))); 8.1787 + lat_max = -90; 8.1788 + } else if (lat_max > 90) { 8.1789 + ereport(WARNING, (errmsg("northern latitude exceeds north pole"))); 8.1790 + lat_max = 90; 8.1791 + } 8.1792 + if (lat_min < -90) { 8.1793 + ereport(WARNING, (errmsg("southern latitude exceeds south pole"))); 8.1794 + lat_min = -90; 8.1795 + } else if (lat_min > 90) { 8.1796 + ereport(WARNING, (errmsg("southern latitude exceeds north pole"))); 8.1797 + lat_min = 90; 8.1798 + } 8.1799 + /* check if all longitudes are included */ 8.1800 + if (lon_max - lon_min >= 360) { 8.1801 + if (lon_max - lon_min > 360) ereport(WARNING, ( 8.1802 + errmsg("longitude coverage greater than 360 degrees") 8.1803 + )); 8.1804 + lon_min = -180; 8.1805 + lon_max = 180; 8.1806 + } else { 8.1807 + /* normalize longitude bounds */ 8.1808 + if (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360; 8.1809 + else if (lon_min > 180) lon_min -= 360 + trunc(lon_min / 360) * 360; 8.1810 + if (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360; 8.1811 + else if (lon_max > 180) lon_max -= 360 + trunc(lon_max / 360) * 360; 8.1812 + } 8.1813 + /* store rounded latitude/longitude values for round-trip safety */ 8.1814 + box->lat_min = pgl_round(lat_min); 8.1815 + box->lat_max = pgl_round(lat_max); 8.1816 + box->lon_min = pgl_round(lon_min); 8.1817 + box->lon_max = pgl_round(lon_max); 8.1818 + /* ensure that rounding does not change orientation */ 8.1819 + if (lon_min > lon_max && box->lon_min == box->lon_max) { 8.1820 + box->lon_min = -180; 8.1821 + box->lon_max = 180; 8.1822 + } 8.1823 +} 8.1824 + 8.1825 +/* create box ("ebox" in SQL) from min/max latitude and min/max longitude */ 8.1826 +PG_FUNCTION_INFO_V1(pgl_create_ebox); 8.1827 +Datum pgl_create_ebox(PG_FUNCTION_ARGS) { 8.1828 + pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1829 + pgl_ebox_set_boundaries( 8.1830 + box, 8.1831 + PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), 8.1832 + PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3) 8.1833 + ); 8.1834 + PG_RETURN_POINTER(box); 8.1835 +} 8.1836 + 8.1837 +/* create box ("ebox" in SQL) from two points ("epoint"s) */ 8.1838 +/* (can not be used to cover a longitude range of more than 120 degrees) */ 8.1839 +PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints); 8.1840 +Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) { 8.1841 + pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0); 8.1842 + pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1); 8.1843 + pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1844 + double lat_min, lat_max, lon_min, lon_max; 8.1845 + double dlon; /* longitude range (delta longitude) */ 8.1846 + /* order latitude and longitude boundaries */ 8.1847 + if (point2->lat < point1->lat) { 8.1848 + lat_min = point2->lat; 8.1849 + lat_max = point1->lat; 8.1850 + } else { 8.1851 + lat_min = point1->lat; 8.1852 + lat_max = point2->lat; 8.1853 + } 8.1854 + if (point2->lon < point1->lon) { 8.1855 + lon_min = point2->lon; 8.1856 + lon_max = point1->lon; 8.1857 + } else { 8.1858 + lon_min = point1->lon; 8.1859 + lon_max = point2->lon; 8.1860 + } 8.1861 + /* calculate longitude range (round to avoid floating point errors) */ 8.1862 + dlon = pgl_round(lon_max - lon_min); 8.1863 + /* determine east-west direction */ 8.1864 + if (dlon >= 240) { 8.1865 + /* assume that 180th meridian is crossed and swap min/max longitude */ 8.1866 + double swap = lon_min; lon_min = lon_max; lon_max = swap; 8.1867 + } else if (dlon > 120) { 8.1868 + /* unclear orientation since delta longitude > 120 */ 8.1869 + ereport(ERROR, ( 8.1870 + errcode(ERRCODE_DATA_EXCEPTION), 8.1871 + errmsg("can not determine east/west orientation for ebox") 8.1872 + )); 8.1873 + } 8.1874 + /* use boundaries to setup box (and perform checks) */ 8.1875 + pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max); 8.1876 + /* return result */ 8.1877 + PG_RETURN_POINTER(box); 8.1878 +} 8.1879 + 8.1880 +/* parse box ("ebox" in SQL) */ 8.1881 +/* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>' 8.1882 + or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */ 8.1883 +PG_FUNCTION_INFO_V1(pgl_ebox_in); 8.1884 +Datum pgl_ebox_in(PG_FUNCTION_ARGS) { 8.1885 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.1886 + char *str_lower; /* lower case version of input string */ 8.1887 + char *strptr; /* current position within string */ 8.1888 + int valid; /* number of valid chars */ 8.1889 + int done; /* specifies if latitude or longitude was read */ 8.1890 + double val; /* temporary variable */ 8.1891 + int lat_count = 0; /* count of latitude values parsed */ 8.1892 + int lon_count = 0; /* count of longitufde values parsed */ 8.1893 + double lat_min, lat_max, lon_min, lon_max; /* see pgl_box struct */ 8.1894 + pgl_box *box; /* return value (to be palloc'ed) */ 8.1895 + /* lowercase input */ 8.1896 + str_lower = psprintf("%s", str); 8.1897 + for (strptr=str_lower; *strptr; strptr++) { 8.1898 + if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A'; 8.1899 + } 8.1900 + /* reset reading position to start of (lowercase) string */ 8.1901 + strptr = str_lower; 8.1902 + /* check if empty box */ 8.1903 + valid = 0; 8.1904 + sscanf(strptr, " empty %n", &valid); 8.1905 + if (valid && strptr[valid] == 0) { 8.1906 + /* allocate and return empty box */ 8.1907 + box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1908 + pgl_box_set_empty(box); 8.1909 + PG_RETURN_POINTER(box); 8.1910 + } 8.1911 + /* demand four blocks separated by whitespace */ 8.1912 + valid = 0; 8.1913 + sscanf(strptr, " %*s %*s %*s %*s %n", &valid); 8.1914 + /* if four blocks separated by whitespace exist, parse those blocks */ 8.1915 + if (strptr[valid] == 0) while (strptr[0]) { 8.1916 + /* parse either latitude or longitude (whichever found in input string) */ 8.1917 + done = pgl_scan(&strptr, &val, &val); 8.1918 + /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */ 8.1919 + if (done == PGL_SCAN_LAT) { 8.1920 + if (!lat_count) lat_min = val; else lat_max = val; 8.1921 + lat_count++; 8.1922 + } else if (done == PGL_SCAN_LON) { 8.1923 + if (!lon_count) lon_min = val; else lon_max = val; 8.1924 + lon_count++; 8.1925 + } else { 8.1926 + break; 8.1927 + } 8.1928 + } 8.1929 + /* require end of string, and two latitude and two longitude values */ 8.1930 + if (strptr[0] || lat_count != 2 || lon_count != 2) { 8.1931 + ereport(ERROR, ( 8.1932 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.1933 + errmsg("invalid input syntax for type ebox: \"%s\"", str) 8.1934 + )); 8.1935 + } 8.1936 + /* free lower case string */ 8.1937 + pfree(str_lower); 8.1938 + /* order boundaries (maximum greater than minimum) */ 8.1939 + if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; } 8.1940 + if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; } 8.1941 + /* allocate memory for result */ 8.1942 + box = (pgl_box *)palloc(sizeof(pgl_box)); 8.1943 + /* set boundaries (and perform checks) */ 8.1944 + pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max); 8.1945 + /* return result */ 8.1946 + PG_RETURN_POINTER(box); 8.1947 +} 8.1948 + 8.1949 +/* set circle to given latitude, longitude, and radius (including checks) */ 8.1950 +static void pgl_ecircle_set_latlon_radius( 8.1951 + pgl_circle *circle, double lat, double lon, double radius 8.1952 +) { 8.1953 + /* set center point (including checks) */ 8.1954 + pgl_epoint_set_latlon(&(circle->center), lat, lon); 8.1955 + /* handle non-positive radius */ 8.1956 + if (isnan(radius)) { 8.1957 + ereport(ERROR, ( 8.1958 + errcode(ERRCODE_DATA_EXCEPTION), 8.1959 + errmsg("invalid radius for ecircle") 8.1960 + )); 8.1961 + } 8.1962 + if (radius == 0) radius = 0; /* avoids -0 */ 8.1963 + else if (radius < 0) { 8.1964 + if (isfinite(radius)) { 8.1965 + ereport(NOTICE, (errmsg("negative radius converted to minus infinity"))); 8.1966 + } 8.1967 + radius = -INFINITY; 8.1968 + } 8.1969 + /* store radius (round-trip safety is ensured by pgl_print_float) */ 8.1970 + circle->radius = radius; 8.1971 +} 8.1972 + 8.1973 +/* create circle ("ecircle" in SQL) from latitude, longitude, and radius */ 8.1974 +PG_FUNCTION_INFO_V1(pgl_create_ecircle); 8.1975 +Datum pgl_create_ecircle(PG_FUNCTION_ARGS) { 8.1976 + pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 8.1977 + pgl_ecircle_set_latlon_radius( 8.1978 + circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2) 8.1979 + ); 8.1980 + PG_RETURN_POINTER(circle); 8.1981 +} 8.1982 + 8.1983 +/* create circle ("ecircle" in SQL) from point ("epoint"), and radius */ 8.1984 +PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint); 8.1985 +Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) { 8.1986 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.1987 + double radius = PG_GETARG_FLOAT8(1); 8.1988 + pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 8.1989 + /* set latitude, longitude, radius (and perform checks) */ 8.1990 + pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius); 8.1991 + /* return result */ 8.1992 + PG_RETURN_POINTER(circle); 8.1993 +} 8.1994 + 8.1995 +/* parse circle ("ecircle" in SQL) */ 8.1996 +/* format: '[NS]<float> [EW]<float> <float>' */ 8.1997 +PG_FUNCTION_INFO_V1(pgl_ecircle_in); 8.1998 +Datum pgl_ecircle_in(PG_FUNCTION_ARGS) { 8.1999 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.2000 + char *strptr = str; /* current position within string */ 8.2001 + double lat, lon, radius; /* parsed values as double precision floats */ 8.2002 + int valid = 0; /* number of valid chars */ 8.2003 + int done = 0; /* stores if latitude and/or longitude was read */ 8.2004 + pgl_circle *circle; /* return value (to be palloc'ed) */ 8.2005 + /* demand three blocks separated by whitespace */ 8.2006 + sscanf(strptr, " %*s %*s %*s %n", &valid); 8.2007 + /* if three blocks separated by whitespace exist, parse those blocks */ 8.2008 + if (strptr[valid] == 0) { 8.2009 + /* parse latitude and longitude */ 8.2010 + done |= pgl_scan(&strptr, &lat, &lon); 8.2011 + done |= pgl_scan(&strptr, &lat, &lon); 8.2012 + /* parse radius (while incrementing strptr by number of bytes parsed) */ 8.2013 + valid = 0; 8.2014 + if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid; 8.2015 + } 8.2016 + /* require end of string and both latitude and longitude being parsed */ 8.2017 + if (strptr[0] || done != PGL_SCAN_LATLON) { 8.2018 + ereport(ERROR, ( 8.2019 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.2020 + errmsg("invalid input syntax for type ecircle: \"%s\"", str) 8.2021 + )); 8.2022 + } 8.2023 + /* allocate memory for result */ 8.2024 + circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 8.2025 + /* set latitude, longitude, radius (and perform checks) */ 8.2026 + pgl_ecircle_set_latlon_radius(circle, lat, lon, radius); 8.2027 + /* return result */ 8.2028 + PG_RETURN_POINTER(circle); 8.2029 +} 8.2030 + 8.2031 +/* parse cluster ("ecluster" in SQL) */ 8.2032 +PG_FUNCTION_INFO_V1(pgl_ecluster_in); 8.2033 +Datum pgl_ecluster_in(PG_FUNCTION_ARGS) { 8.2034 + int i; 8.2035 + char *str = PG_GETARG_CSTRING(0); /* input string */ 8.2036 + char *str_lower; /* lower case version of input string */ 8.2037 + char *strptr; /* pointer to current reading position of input */ 8.2038 + int npoints_total = 0; /* total number of points in cluster */ 8.2039 + int nentries = 0; /* total number of entries */ 8.2040 + pgl_newentry *entries; /* array of pgl_newentry to create pgl_cluster */ 8.2041 + int entries_buflen = 4; /* maximum number of elements in entries array */ 8.2042 + int valid; /* number of valid chars processed */ 8.2043 + double lat, lon; /* latitude and longitude of parsed point */ 8.2044 + int entrytype; /* current entry type */ 8.2045 + int npoints; /* number of points in current entry */ 8.2046 + pgl_point *points; /* array of pgl_point for pgl_newentry */ 8.2047 + int points_buflen; /* maximum number of elements in points array */ 8.2048 + int done; /* return value of pgl_scan function */ 8.2049 + pgl_cluster *cluster; /* created cluster */ 8.2050 + /* lowercase input */ 8.2051 + str_lower = psprintf("%s", str); 8.2052 + for (strptr=str_lower; *strptr; strptr++) { 8.2053 + if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A'; 8.2054 + } 8.2055 + /* reset reading position to start of (lowercase) string */ 8.2056 + strptr = str_lower; 8.2057 + /* allocate initial buffer for entries */ 8.2058 + entries = palloc(entries_buflen * sizeof(pgl_newentry)); 8.2059 + /* parse until end of string */ 8.2060 + while (strptr[0]) { 8.2061 + /* require previous white-space or closing parenthesis before next token */ 8.2062 + if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') { 8.2063 + goto pgl_ecluster_in_error; 8.2064 + } 8.2065 + /* ignore token "empty" */ 8.2066 + valid = 0; sscanf(strptr, " empty %n", &valid); 8.2067 + if (valid) { strptr += valid; continue; } 8.2068 + /* test for "point" token */ 8.2069 + valid = 0; sscanf(strptr, " point ( %n", &valid); 8.2070 + if (valid) { 8.2071 + strptr += valid; 8.2072 + entrytype = PGL_ENTRY_POINT; 8.2073 + goto pgl_ecluster_in_type_ok; 8.2074 + } 8.2075 + /* test for "path" token */ 8.2076 + valid = 0; sscanf(strptr, " path ( %n", &valid); 8.2077 + if (valid) { 8.2078 + strptr += valid; 8.2079 + entrytype = PGL_ENTRY_PATH; 8.2080 + goto pgl_ecluster_in_type_ok; 8.2081 + } 8.2082 + /* test for "outline" token */ 8.2083 + valid = 0; sscanf(strptr, " outline ( %n", &valid); 8.2084 + if (valid) { 8.2085 + strptr += valid; 8.2086 + entrytype = PGL_ENTRY_OUTLINE; 8.2087 + goto pgl_ecluster_in_type_ok; 8.2088 + } 8.2089 + /* test for "polygon" token */ 8.2090 + valid = 0; sscanf(strptr, " polygon ( %n", &valid); 8.2091 + if (valid) { 8.2092 + strptr += valid; 8.2093 + entrytype = PGL_ENTRY_POLYGON; 8.2094 + goto pgl_ecluster_in_type_ok; 8.2095 + } 8.2096 + /* error if no valid token found */ 8.2097 + goto pgl_ecluster_in_error; 8.2098 + pgl_ecluster_in_type_ok: 8.2099 + /* check if pgl_newentry array needs to grow */ 8.2100 + if (nentries == entries_buflen) { 8.2101 + pgl_newentry *newbuf; 8.2102 + entries_buflen *= 2; 8.2103 + newbuf = palloc(entries_buflen * sizeof(pgl_newentry)); 8.2104 + memcpy(newbuf, entries, nentries * sizeof(pgl_newentry)); 8.2105 + pfree(entries); 8.2106 + entries = newbuf; 8.2107 + } 8.2108 + /* reset number of points for current entry */ 8.2109 + npoints = 0; 8.2110 + /* allocate array for points */ 8.2111 + points_buflen = 4; 8.2112 + points = palloc(points_buflen * sizeof(pgl_point)); 8.2113 + /* parse until closing parenthesis */ 8.2114 + while (strptr[0] != ')') { 8.2115 + /* error on unexpected end of string */ 8.2116 + if (strptr[0] == 0) goto pgl_ecluster_in_error; 8.2117 + /* mark neither latitude nor longitude as read */ 8.2118 + done = PGL_SCAN_NONE; 8.2119 + /* require white-space before second, third, etc. point */ 8.2120 + if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error; 8.2121 + /* scan latitude (or longitude) */ 8.2122 + done |= pgl_scan(&strptr, &lat, &lon); 8.2123 + /* require white-space before second coordinate */ 8.2124 + if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error; 8.2125 + /* scan longitude (or latitude) */ 8.2126 + done |= pgl_scan(&strptr, &lat, &lon); 8.2127 + /* error unless both latitude and longitude were parsed */ 8.2128 + if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error; 8.2129 + /* throw error if number of points is too high */ 8.2130 + if (npoints_total == PGL_CLUSTER_MAXPOINTS) { 8.2131 + ereport(ERROR, ( 8.2132 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.2133 + errmsg( 8.2134 + "too many points for ecluster entry (maximum %i)", 8.2135 + PGL_CLUSTER_MAXPOINTS 8.2136 + ) 8.2137 + )); 8.2138 + } 8.2139 + /* check if pgl_point array needs to grow */ 8.2140 + if (npoints == points_buflen) { 8.2141 + pgl_point *newbuf; 8.2142 + points_buflen *= 2; 8.2143 + newbuf = palloc(points_buflen * sizeof(pgl_point)); 8.2144 + memcpy(newbuf, points, npoints * sizeof(pgl_point)); 8.2145 + pfree(points); 8.2146 + points = newbuf; 8.2147 + } 8.2148 + /* append point to pgl_point array (includes checks) */ 8.2149 + pgl_epoint_set_latlon(&(points[npoints++]), lat, lon); 8.2150 + /* increase total number of points */ 8.2151 + npoints_total++; 8.2152 + } 8.2153 + /* error if entry has no points */ 8.2154 + if (!npoints) goto pgl_ecluster_in_error; 8.2155 + /* entries with one point are automatically of type "point" */ 8.2156 + if (npoints == 1) entrytype = PGL_ENTRY_POINT; 8.2157 + /* if entries have more than one point */ 8.2158 + else { 8.2159 + /* throw error if entry type is "point" */ 8.2160 + if (entrytype == PGL_ENTRY_POINT) { 8.2161 + ereport(ERROR, ( 8.2162 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.2163 + errmsg("invalid input syntax for type ecluster (point entry with more than one point)") 8.2164 + )); 8.2165 + } 8.2166 + /* coerce outlines and polygons with more than 2 points to be a path */ 8.2167 + if (npoints == 2) entrytype = PGL_ENTRY_PATH; 8.2168 + } 8.2169 + /* append entry to pgl_newentry array */ 8.2170 + entries[nentries].entrytype = entrytype; 8.2171 + entries[nentries].npoints = npoints; 8.2172 + entries[nentries].points = points; 8.2173 + nentries++; 8.2174 + /* consume closing parenthesis */ 8.2175 + strptr++; 8.2176 + /* consume white-space */ 8.2177 + while (isspace(strptr[0])) strptr++; 8.2178 + } 8.2179 + /* free lower case string */ 8.2180 + pfree(str_lower); 8.2181 + /* create cluster from pgl_newentry array */ 8.2182 + cluster = pgl_new_cluster(nentries, entries); 8.2183 + /* free pgl_newentry array */ 8.2184 + for (i=0; i<nentries; i++) pfree(entries[i].points); 8.2185 + pfree(entries); 8.2186 + /* set bounding circle of cluster and check east/west orientation */ 8.2187 + if (!pgl_finalize_cluster(cluster)) { 8.2188 + ereport(ERROR, ( 8.2189 + errcode(ERRCODE_DATA_EXCEPTION), 8.2190 + errmsg("can not determine east/west orientation for ecluster"), 8.2191 + errhint("Ensure that each entry has a longitude span of less than 180 degrees.") 8.2192 + )); 8.2193 + } 8.2194 + /* return cluster */ 8.2195 + PG_RETURN_POINTER(cluster); 8.2196 + /* code to throw error */ 8.2197 + pgl_ecluster_in_error: 8.2198 + ereport(ERROR, ( 8.2199 + errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 8.2200 + errmsg("invalid input syntax for type ecluster: \"%s\"", str) 8.2201 + )); 8.2202 +} 8.2203 + 8.2204 +/* convert point ("epoint") to string representation */ 8.2205 +PG_FUNCTION_INFO_V1(pgl_epoint_out); 8.2206 +Datum pgl_epoint_out(PG_FUNCTION_ARGS) { 8.2207 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2208 + char latstr[PGL_NUMBUFLEN]; 8.2209 + char lonstr[PGL_NUMBUFLEN]; 8.2210 + pgl_print_lat(latstr, point->lat); 8.2211 + pgl_print_lon(lonstr, point->lon); 8.2212 + PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr)); 8.2213 +} 8.2214 + 8.2215 +/* convert point with sample count ("epoint_with_sample_count") to str. rep. */ 8.2216 +PG_FUNCTION_INFO_V1(pgl_epoint_with_sample_count_out); 8.2217 +Datum pgl_epoint_with_sample_count_out(PG_FUNCTION_ARGS) { 8.2218 + pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(0); 8.2219 + char latstr[PGL_NUMBUFLEN]; 8.2220 + char lonstr[PGL_NUMBUFLEN]; 8.2221 + pgl_print_lat(latstr, search->point.lat); 8.2222 + pgl_print_lon(lonstr, search->point.lon); 8.2223 + PG_RETURN_CSTRING( 8.2224 + psprintf("%s %s %i", latstr, lonstr, (int)search->samples) 8.2225 + ); 8.2226 +} 8.2227 + 8.2228 +/* convert box ("ebox") to string representation */ 8.2229 +PG_FUNCTION_INFO_V1(pgl_ebox_out); 8.2230 +Datum pgl_ebox_out(PG_FUNCTION_ARGS) { 8.2231 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2232 + double lon_min = box->lon_min; 8.2233 + double lon_max = box->lon_max; 8.2234 + char lat_min_str[PGL_NUMBUFLEN]; 8.2235 + char lat_max_str[PGL_NUMBUFLEN]; 8.2236 + char lon_min_str[PGL_NUMBUFLEN]; 8.2237 + char lon_max_str[PGL_NUMBUFLEN]; 8.2238 + /* return string "empty" if box is set to be empty */ 8.2239 + if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty"); 8.2240 + /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */ 8.2241 + /* (required since pgl_box_in orders the longitude boundaries) */ 8.2242 + if (lon_min > lon_max) { 8.2243 + if (lon_min + lon_max >= 0) lon_min -= 360; 8.2244 + else lon_max += 360; 8.2245 + } 8.2246 + /* format and return result */ 8.2247 + pgl_print_lat(lat_min_str, box->lat_min); 8.2248 + pgl_print_lat(lat_max_str, box->lat_max); 8.2249 + pgl_print_lon(lon_min_str, lon_min); 8.2250 + pgl_print_lon(lon_max_str, lon_max); 8.2251 + PG_RETURN_CSTRING(psprintf( 8.2252 + "%s %s %s %s", 8.2253 + lat_min_str, lon_min_str, lat_max_str, lon_max_str 8.2254 + )); 8.2255 +} 8.2256 + 8.2257 +/* convert circle ("ecircle") to string representation */ 8.2258 +PG_FUNCTION_INFO_V1(pgl_ecircle_out); 8.2259 +Datum pgl_ecircle_out(PG_FUNCTION_ARGS) { 8.2260 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2261 + char latstr[PGL_NUMBUFLEN]; 8.2262 + char lonstr[PGL_NUMBUFLEN]; 8.2263 + char radstr[PGL_NUMBUFLEN]; 8.2264 + pgl_print_lat(latstr, circle->center.lat); 8.2265 + pgl_print_lon(lonstr, circle->center.lon); 8.2266 + pgl_print_float(radstr, circle->radius); 8.2267 + PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr)); 8.2268 +} 8.2269 + 8.2270 +/* convert cluster ("ecluster") to string representation */ 8.2271 +PG_FUNCTION_INFO_V1(pgl_ecluster_out); 8.2272 +Datum pgl_ecluster_out(PG_FUNCTION_ARGS) { 8.2273 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2274 + char latstr[PGL_NUMBUFLEN]; /* string buffer for latitude */ 8.2275 + char lonstr[PGL_NUMBUFLEN]; /* string buffer for longitude */ 8.2276 + char ***strings; /* array of array of strings */ 8.2277 + char *string; /* string of current token */ 8.2278 + char *res, *resptr; /* result and pointer to current write position */ 8.2279 + size_t reslen = 1; /* length of result (init with 1 for terminator) */ 8.2280 + int npoints; /* number of points of current entry */ 8.2281 + int i, j; /* i: entry, j: point in entry */ 8.2282 + /* handle empty clusters */ 8.2283 + if (cluster->nentries == 0) { 8.2284 + /* free detoasted cluster (if copy) */ 8.2285 + PG_FREE_IF_COPY(cluster, 0); 8.2286 + /* return static result */ 8.2287 + PG_RETURN_CSTRING("empty"); 8.2288 + } 8.2289 + /* allocate array of array of strings */ 8.2290 + strings = palloc(cluster->nentries * sizeof(char **)); 8.2291 + /* iterate over all entries in cluster */ 8.2292 + for (i=0; i<cluster->nentries; i++) { 8.2293 + /* get number of points in entry */ 8.2294 + npoints = cluster->entries[i].npoints; 8.2295 + /* allocate array of strings (one string for each point plus two extra) */ 8.2296 + strings[i] = palloc((2 + npoints) * sizeof(char *)); 8.2297 + /* determine opening string */ 8.2298 + switch (cluster->entries[i].entrytype) { 8.2299 + case PGL_ENTRY_POINT: string = (i==0)?"point (" :" point ("; break; 8.2300 + case PGL_ENTRY_PATH: string = (i==0)?"path (" :" path ("; break; 8.2301 + case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break; 8.2302 + case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break; 8.2303 + default: string = (i==0)?"unknown" :" unknown"; 8.2304 + } 8.2305 + /* use opening string as first string in array */ 8.2306 + strings[i][0] = string; 8.2307 + /* update result length (for allocating result string later) */ 8.2308 + reslen += strlen(string); 8.2309 + /* iterate over all points */ 8.2310 + for (j=0; j<npoints; j++) { 8.2311 + /* create string representation of point */ 8.2312 + pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat); 8.2313 + pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon); 8.2314 + string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr); 8.2315 + /* copy string pointer to string array */ 8.2316 + strings[i][j+1] = string; 8.2317 + /* update result length (for allocating result string later) */ 8.2318 + reslen += strlen(string); 8.2319 + } 8.2320 + /* use closing parenthesis as last string in array */ 8.2321 + strings[i][npoints+1] = ")"; 8.2322 + /* update result length (for allocating result string later) */ 8.2323 + reslen++; 8.2324 + } 8.2325 + /* allocate result string */ 8.2326 + res = palloc(reslen); 8.2327 + /* set write pointer to begin of result string */ 8.2328 + resptr = res; 8.2329 + /* copy strings into result string */ 8.2330 + for (i=0; i<cluster->nentries; i++) { 8.2331 + npoints = cluster->entries[i].npoints; 8.2332 + for (j=0; j<npoints+2; j++) { 8.2333 + string = strings[i][j]; 8.2334 + strcpy(resptr, string); 8.2335 + resptr += strlen(string); 8.2336 + /* free strings allocated by psprintf */ 8.2337 + if (j != 0 && j != npoints+1) pfree(string); 8.2338 + } 8.2339 + /* free array of strings */ 8.2340 + pfree(strings[i]); 8.2341 + } 8.2342 + /* free array of array of strings */ 8.2343 + pfree(strings); 8.2344 + /* free detoasted cluster (if copy) */ 8.2345 + PG_FREE_IF_COPY(cluster, 0); 8.2346 + /* return result */ 8.2347 + PG_RETURN_CSTRING(res); 8.2348 +} 8.2349 + 8.2350 +/* binary input function for point ("epoint") */ 8.2351 +PG_FUNCTION_INFO_V1(pgl_epoint_recv); 8.2352 +Datum pgl_epoint_recv(PG_FUNCTION_ARGS) { 8.2353 + StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 8.2354 + pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point)); 8.2355 + point->lat = pq_getmsgfloat8(buf); 8.2356 + point->lon = pq_getmsgfloat8(buf); 8.2357 + PG_RETURN_POINTER(point); 8.2358 +} 8.2359 + 8.2360 +/* binary input function for box ("ebox") */ 8.2361 +PG_FUNCTION_INFO_V1(pgl_ebox_recv); 8.2362 +Datum pgl_ebox_recv(PG_FUNCTION_ARGS) { 8.2363 + StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 8.2364 + pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box)); 8.2365 + box->lat_min = pq_getmsgfloat8(buf); 8.2366 + box->lat_max = pq_getmsgfloat8(buf); 8.2367 + box->lon_min = pq_getmsgfloat8(buf); 8.2368 + box->lon_max = pq_getmsgfloat8(buf); 8.2369 + PG_RETURN_POINTER(box); 8.2370 +} 8.2371 + 8.2372 +/* binary input function for circle ("ecircle") */ 8.2373 +PG_FUNCTION_INFO_V1(pgl_ecircle_recv); 8.2374 +Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) { 8.2375 + StringInfo buf = (StringInfo)PG_GETARG_POINTER(0); 8.2376 + pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle)); 8.2377 + circle->center.lat = pq_getmsgfloat8(buf); 8.2378 + circle->center.lon = pq_getmsgfloat8(buf); 8.2379 + circle->radius = pq_getmsgfloat8(buf); 8.2380 + PG_RETURN_POINTER(circle); 8.2381 +} 8.2382 + 8.2383 +/* TODO: binary receive function for cluster */ 8.2384 + 8.2385 +/* binary output function for point ("epoint") */ 8.2386 +PG_FUNCTION_INFO_V1(pgl_epoint_send); 8.2387 +Datum pgl_epoint_send(PG_FUNCTION_ARGS) { 8.2388 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2389 + StringInfoData buf; 8.2390 + pq_begintypsend(&buf); 8.2391 + pq_sendfloat8(&buf, point->lat); 8.2392 + pq_sendfloat8(&buf, point->lon); 8.2393 + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 8.2394 +} 8.2395 + 8.2396 +/* binary output function for box ("ebox") */ 8.2397 +PG_FUNCTION_INFO_V1(pgl_ebox_send); 8.2398 +Datum pgl_ebox_send(PG_FUNCTION_ARGS) { 8.2399 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2400 + StringInfoData buf; 8.2401 + pq_begintypsend(&buf); 8.2402 + pq_sendfloat8(&buf, box->lat_min); 8.2403 + pq_sendfloat8(&buf, box->lat_max); 8.2404 + pq_sendfloat8(&buf, box->lon_min); 8.2405 + pq_sendfloat8(&buf, box->lon_max); 8.2406 + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 8.2407 +} 8.2408 + 8.2409 +/* binary output function for circle ("ecircle") */ 8.2410 +PG_FUNCTION_INFO_V1(pgl_ecircle_send); 8.2411 +Datum pgl_ecircle_send(PG_FUNCTION_ARGS) { 8.2412 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2413 + StringInfoData buf; 8.2414 + pq_begintypsend(&buf); 8.2415 + pq_sendfloat8(&buf, circle->center.lat); 8.2416 + pq_sendfloat8(&buf, circle->center.lon); 8.2417 + pq_sendfloat8(&buf, circle->radius); 8.2418 + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 8.2419 +} 8.2420 + 8.2421 +/* TODO: binary send functions for cluster */ 8.2422 + 8.2423 +/* cast point ("epoint") to box ("ebox") */ 8.2424 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox); 8.2425 +Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) { 8.2426 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2427 + pgl_box *box = palloc(sizeof(pgl_box)); 8.2428 + box->lat_min = point->lat; 8.2429 + box->lat_max = point->lat; 8.2430 + box->lon_min = point->lon; 8.2431 + box->lon_max = point->lon; 8.2432 + PG_RETURN_POINTER(box); 8.2433 +} 8.2434 + 8.2435 +/* cast point ("epoint") to circle ("ecircle") */ 8.2436 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle); 8.2437 +Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) { 8.2438 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2439 + pgl_circle *circle = palloc(sizeof(pgl_box)); 8.2440 + circle->center = *point; 8.2441 + circle->radius = 0; 8.2442 + PG_RETURN_POINTER(circle); 8.2443 +} 8.2444 + 8.2445 +/* cast point ("epoint") to cluster ("ecluster") */ 8.2446 +PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster); 8.2447 +Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) { 8.2448 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2449 + pgl_newentry entry; 8.2450 + pgl_cluster *cluster; 8.2451 + entry.entrytype = PGL_ENTRY_POINT; 8.2452 + entry.npoints = 1; 8.2453 + entry.points = point; 8.2454 + cluster = pgl_new_cluster(1, &entry); 8.2455 + pgl_finalize_cluster(cluster); /* NOTE: should not fail */ 8.2456 + PG_RETURN_POINTER(cluster); 8.2457 +} 8.2458 + 8.2459 +/* cast box ("ebox") to cluster ("ecluster") */ 8.2460 +#define pgl_ebox_to_ecluster_macro(i, a, b) \ 8.2461 + entries[i].entrytype = PGL_ENTRY_POLYGON; \ 8.2462 + entries[i].npoints = 4; \ 8.2463 + entries[i].points = points[i]; \ 8.2464 + points[i][0].lat = box->lat_min; \ 8.2465 + points[i][0].lon = (a); \ 8.2466 + points[i][1].lat = box->lat_min; \ 8.2467 + points[i][1].lon = (b); \ 8.2468 + points[i][2].lat = box->lat_max; \ 8.2469 + points[i][2].lon = (b); \ 8.2470 + points[i][3].lat = box->lat_max; \ 8.2471 + points[i][3].lon = (a); 8.2472 +PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster); 8.2473 +Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) { 8.2474 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2475 + double lon, dlon; 8.2476 + int nentries; 8.2477 + pgl_newentry entries[3]; 8.2478 + pgl_point points[3][4]; 8.2479 + pgl_cluster *cluster; 8.2480 + if (box->lat_min > box->lat_max) { 8.2481 + nentries = 0; 8.2482 + } else if (box->lon_min > box->lon_max) { 8.2483 + if (box->lon_min < 0) { 8.2484 + lon = pgl_round((box->lon_min + 180) / 2.0); 8.2485 + nentries = 3; 8.2486 + pgl_ebox_to_ecluster_macro(0, box->lon_min, lon); 8.2487 + pgl_ebox_to_ecluster_macro(1, lon, 180); 8.2488 + pgl_ebox_to_ecluster_macro(2, -180, box->lon_max); 8.2489 + } else if (box->lon_max > 0) { 8.2490 + lon = pgl_round((box->lon_max - 180) / 2.0); 8.2491 + nentries = 3; 8.2492 + pgl_ebox_to_ecluster_macro(0, box->lon_min, 180); 8.2493 + pgl_ebox_to_ecluster_macro(1, -180, lon); 8.2494 + pgl_ebox_to_ecluster_macro(2, lon, box->lon_max); 8.2495 + } else { 8.2496 + nentries = 2; 8.2497 + pgl_ebox_to_ecluster_macro(0, box->lon_min, 180); 8.2498 + pgl_ebox_to_ecluster_macro(1, -180, box->lon_max); 8.2499 + } 8.2500 + } else { 8.2501 + dlon = pgl_round(box->lon_max - box->lon_min); 8.2502 + if (dlon < 180) { 8.2503 + nentries = 1; 8.2504 + pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max); 8.2505 + } else { 8.2506 + lon = pgl_round((box->lon_min + box->lon_max) / 2.0); 8.2507 + if ( 8.2508 + pgl_round(lon - box->lon_min) < 180 && 8.2509 + pgl_round(box->lon_max - lon) < 180 8.2510 + ) { 8.2511 + nentries = 2; 8.2512 + pgl_ebox_to_ecluster_macro(0, box->lon_min, lon); 8.2513 + pgl_ebox_to_ecluster_macro(1, lon, box->lon_max); 8.2514 + } else { 8.2515 + nentries = 3; 8.2516 + pgl_ebox_to_ecluster_macro(0, box->lon_min, -60); 8.2517 + pgl_ebox_to_ecluster_macro(1, -60, 60); 8.2518 + pgl_ebox_to_ecluster_macro(2, 60, box->lon_max); 8.2519 + } 8.2520 + } 8.2521 + } 8.2522 + cluster = pgl_new_cluster(nentries, entries); 8.2523 + pgl_finalize_cluster(cluster); /* NOTE: should not fail */ 8.2524 + PG_RETURN_POINTER(cluster); 8.2525 +} 8.2526 + 8.2527 +/* extract latitude from point ("epoint") */ 8.2528 +PG_FUNCTION_INFO_V1(pgl_epoint_lat); 8.2529 +Datum pgl_epoint_lat(PG_FUNCTION_ARGS) { 8.2530 + PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat); 8.2531 +} 8.2532 + 8.2533 +/* extract longitude from point ("epoint") */ 8.2534 +PG_FUNCTION_INFO_V1(pgl_epoint_lon); 8.2535 +Datum pgl_epoint_lon(PG_FUNCTION_ARGS) { 8.2536 + PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon); 8.2537 +} 8.2538 + 8.2539 +/* extract minimum latitude from box ("ebox") */ 8.2540 +PG_FUNCTION_INFO_V1(pgl_ebox_lat_min); 8.2541 +Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) { 8.2542 + PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min); 8.2543 +} 8.2544 + 8.2545 +/* extract maximum latitude from box ("ebox") */ 8.2546 +PG_FUNCTION_INFO_V1(pgl_ebox_lat_max); 8.2547 +Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) { 8.2548 + PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max); 8.2549 +} 8.2550 + 8.2551 +/* extract minimum longitude from box ("ebox") */ 8.2552 +PG_FUNCTION_INFO_V1(pgl_ebox_lon_min); 8.2553 +Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) { 8.2554 + PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min); 8.2555 +} 8.2556 + 8.2557 +/* extract maximum longitude from box ("ebox") */ 8.2558 +PG_FUNCTION_INFO_V1(pgl_ebox_lon_max); 8.2559 +Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) { 8.2560 + PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max); 8.2561 +} 8.2562 + 8.2563 +/* extract center point from circle ("ecircle") */ 8.2564 +PG_FUNCTION_INFO_V1(pgl_ecircle_center); 8.2565 +Datum pgl_ecircle_center(PG_FUNCTION_ARGS) { 8.2566 + PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center)); 8.2567 +} 8.2568 + 8.2569 +/* extract radius from circle ("ecircle") */ 8.2570 +PG_FUNCTION_INFO_V1(pgl_ecircle_radius); 8.2571 +Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) { 8.2572 + PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius); 8.2573 +} 8.2574 + 8.2575 +/* check if point is inside box (overlap operator "&&") in SQL */ 8.2576 +PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap); 8.2577 +Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) { 8.2578 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2579 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1); 8.2580 + PG_RETURN_BOOL(pgl_point_in_box(point, box)); 8.2581 +} 8.2582 + 8.2583 +/* check if point is inside circle (overlap operator "&&") in SQL */ 8.2584 +PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap); 8.2585 +Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) { 8.2586 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2587 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 8.2588 + PG_RETURN_BOOL( 8.2589 + pgl_distance( 8.2590 + point->lat, point->lon, 8.2591 + circle->center.lat, circle->center.lon 8.2592 + ) <= circle->radius 8.2593 + ); 8.2594 +} 8.2595 + 8.2596 +/* check if point is inside cluster (overlap operator "&&") in SQL */ 8.2597 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap); 8.2598 +Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) { 8.2599 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2600 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2601 + bool retval; 8.2602 + /* points outside bounding circle are always assumed to be non-overlapping */ 8.2603 + if ( 8.2604 + pgl_distance( 8.2605 + point->lat, point->lon, 8.2606 + cluster->bounding.center.lat, cluster->bounding.center.lon 8.2607 + ) > cluster->bounding.radius 8.2608 + ) retval = false; 8.2609 + else retval = pgl_point_in_cluster(point, cluster, false); 8.2610 + PG_FREE_IF_COPY(cluster, 1); 8.2611 + PG_RETURN_BOOL(retval); 8.2612 +} 8.2613 + 8.2614 +/* check if point may be inside cluster (lossy overl. operator "&&+") in SQL */ 8.2615 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_may_overlap); 8.2616 +Datum pgl_epoint_ecluster_may_overlap(PG_FUNCTION_ARGS) { 8.2617 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2618 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2619 + bool retval = pgl_distance( 8.2620 + point->lat, point->lon, 8.2621 + cluster->bounding.center.lat, cluster->bounding.center.lon 8.2622 + ) <= cluster->bounding.radius; 8.2623 + PG_FREE_IF_COPY(cluster, 1); 8.2624 + PG_RETURN_BOOL(retval); 8.2625 +} 8.2626 + 8.2627 +/* check if two boxes overlap (overlap operator "&&") in SQL */ 8.2628 +PG_FUNCTION_INFO_V1(pgl_ebox_overlap); 8.2629 +Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) { 8.2630 + pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0); 8.2631 + pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1); 8.2632 + PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2)); 8.2633 +} 8.2634 + 8.2635 +/* check if box and circle may overlap (lossy overl. operator "&&+") in SQL */ 8.2636 +PG_FUNCTION_INFO_V1(pgl_ebox_ecircle_may_overlap); 8.2637 +Datum pgl_ebox_ecircle_may_overlap(PG_FUNCTION_ARGS) { 8.2638 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2639 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 8.2640 + PG_RETURN_BOOL( 8.2641 + pgl_estimate_point_box_distance(&circle->center, box) <= circle->radius 8.2642 + ); 8.2643 +} 8.2644 + 8.2645 +/* check if box and cluster may overlap (lossy overl. operator "&&+") in SQL */ 8.2646 +PG_FUNCTION_INFO_V1(pgl_ebox_ecluster_may_overlap); 8.2647 +Datum pgl_ebox_ecluster_may_overlap(PG_FUNCTION_ARGS) { 8.2648 + pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0); 8.2649 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2650 + bool retval = pgl_estimate_point_box_distance( 8.2651 + &cluster->bounding.center, 8.2652 + box 8.2653 + ) <= cluster->bounding.radius; 8.2654 + PG_FREE_IF_COPY(cluster, 1); 8.2655 + PG_RETURN_BOOL(retval); 8.2656 +} 8.2657 + 8.2658 +/* check if two circles overlap (overlap operator "&&") in SQL */ 8.2659 +PG_FUNCTION_INFO_V1(pgl_ecircle_overlap); 8.2660 +Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) { 8.2661 + pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0); 8.2662 + pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1); 8.2663 + PG_RETURN_BOOL( 8.2664 + pgl_distance( 8.2665 + circle1->center.lat, circle1->center.lon, 8.2666 + circle2->center.lat, circle2->center.lon 8.2667 + ) <= circle1->radius + circle2->radius 8.2668 + ); 8.2669 +} 8.2670 + 8.2671 +/* check if circle and cluster overlap (overlap operator "&&") in SQL */ 8.2672 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap); 8.2673 +Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) { 8.2674 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2675 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2676 + bool retval; 8.2677 + /* points outside bounding circle (with margin for flattening) are always 8.2678 + assumed to be non-overlapping */ 8.2679 + if ( 8.2680 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2681 + pgl_distance( 8.2682 + circle->center.lat, circle->center.lon, 8.2683 + cluster->bounding.center.lat, cluster->bounding.center.lon 8.2684 + ) > circle->radius + cluster->bounding.radius 8.2685 + ) retval = false; 8.2686 + else retval = pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius; 8.2687 + PG_FREE_IF_COPY(cluster, 1); 8.2688 + PG_RETURN_BOOL(retval); 8.2689 +} 8.2690 + 8.2691 +/* check if circle and cluster may overlap (l. ov. operator "&&+") in SQL */ 8.2692 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_may_overlap); 8.2693 +Datum pgl_ecircle_ecluster_may_overlap(PG_FUNCTION_ARGS) { 8.2694 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2695 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2696 + bool retval = ( 8.2697 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2698 + pgl_distance( 8.2699 + circle->center.lat, circle->center.lon, 8.2700 + cluster->bounding.center.lat, cluster->bounding.center.lon 8.2701 + ) 8.2702 + ) <= circle->radius + cluster->bounding.radius; 8.2703 + PG_FREE_IF_COPY(cluster, 1); 8.2704 + PG_RETURN_BOOL(retval); 8.2705 +} 8.2706 + 8.2707 +/* check if two clusters overlap (overlap operator "&&") in SQL */ 8.2708 +PG_FUNCTION_INFO_V1(pgl_ecluster_overlap); 8.2709 +Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) { 8.2710 + pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2711 + pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2712 + bool retval; 8.2713 + /* clusters with non-touching bounding circles (with margin for flattening) 8.2714 + are always assumed to be non-overlapping */ 8.2715 + if ( 8.2716 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2717 + pgl_distance( 8.2718 + cluster1->bounding.center.lat, cluster1->bounding.center.lon, 8.2719 + cluster2->bounding.center.lat, cluster2->bounding.center.lon 8.2720 + ) > cluster1->bounding.radius + cluster2->bounding.radius 8.2721 + ) retval = false; 8.2722 + else retval = pgl_clusters_overlap(cluster1, cluster2); 8.2723 + PG_FREE_IF_COPY(cluster1, 0); 8.2724 + PG_FREE_IF_COPY(cluster2, 1); 8.2725 + PG_RETURN_BOOL(retval); 8.2726 +} 8.2727 + 8.2728 +/* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */ 8.2729 +PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap); 8.2730 +Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) { 8.2731 + pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2732 + pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2733 + bool retval = ( 8.2734 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2735 + pgl_distance( 8.2736 + cluster1->bounding.center.lat, cluster1->bounding.center.lon, 8.2737 + cluster2->bounding.center.lat, cluster2->bounding.center.lon 8.2738 + ) 8.2739 + ) <= cluster1->bounding.radius + cluster2->bounding.radius; 8.2740 + PG_FREE_IF_COPY(cluster1, 0); 8.2741 + PG_FREE_IF_COPY(cluster2, 1); 8.2742 + PG_RETURN_BOOL(retval); 8.2743 +} 8.2744 + 8.2745 +/* check if second cluster is in first cluster (cont. operator "@>) in SQL */ 8.2746 +PG_FUNCTION_INFO_V1(pgl_ecluster_contains); 8.2747 +Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) { 8.2748 + pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2749 + pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2750 + bool retval; 8.2751 + /* clusters with non-touching bounding circles are always assumed to be 8.2752 + non-overlapping */ 8.2753 + if ( 8.2754 + (1.0-PGL_SPHEROID_F) * /* margin for flattening and approximation */ 8.2755 + pgl_distance( 8.2756 + outer->bounding.center.lat, outer->bounding.center.lon, 8.2757 + inner->bounding.center.lat, inner->bounding.center.lon 8.2758 + ) > outer->bounding.radius + inner->bounding.radius 8.2759 + ) retval = false; 8.2760 + else retval = pgl_cluster_in_cluster(outer, inner); 8.2761 + PG_FREE_IF_COPY(outer, 0); 8.2762 + PG_FREE_IF_COPY(inner, 1); 8.2763 + PG_RETURN_BOOL(retval); 8.2764 +} 8.2765 + 8.2766 +/* calculate distance between two points ("<->" operator) in SQL */ 8.2767 +PG_FUNCTION_INFO_V1(pgl_epoint_distance); 8.2768 +Datum pgl_epoint_distance(PG_FUNCTION_ARGS) { 8.2769 + pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0); 8.2770 + pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1); 8.2771 + PG_RETURN_FLOAT8(pgl_distance( 8.2772 + point1->lat, point1->lon, point2->lat, point2->lon 8.2773 + )); 8.2774 +} 8.2775 + 8.2776 +/* calculate point to circle distance ("<->" operator) in SQL */ 8.2777 +PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance); 8.2778 +Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) { 8.2779 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2780 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1); 8.2781 + double distance = pgl_distance( 8.2782 + point->lat, point->lon, circle->center.lat, circle->center.lon 8.2783 + ) - circle->radius; 8.2784 + PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 8.2785 +} 8.2786 + 8.2787 +/* calculate point to cluster distance ("<->" operator) in SQL */ 8.2788 +PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance); 8.2789 +Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) { 8.2790 + pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0); 8.2791 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2792 + double distance = pgl_point_cluster_distance(point, cluster); 8.2793 + PG_FREE_IF_COPY(cluster, 1); 8.2794 + PG_RETURN_FLOAT8(distance); 8.2795 +} 8.2796 + 8.2797 +/* calculate distance between two circles ("<->" operator) in SQL */ 8.2798 +PG_FUNCTION_INFO_V1(pgl_ecircle_distance); 8.2799 +Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) { 8.2800 + pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0); 8.2801 + pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1); 8.2802 + double distance = pgl_distance( 8.2803 + circle1->center.lat, circle1->center.lon, 8.2804 + circle2->center.lat, circle2->center.lon 8.2805 + ) - (circle1->radius + circle2->radius); 8.2806 + PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 8.2807 +} 8.2808 + 8.2809 +/* calculate circle to cluster distance ("<->" operator) in SQL */ 8.2810 +PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance); 8.2811 +Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) { 8.2812 + pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0); 8.2813 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2814 + double distance = ( 8.2815 + pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius 8.2816 + ); 8.2817 + PG_FREE_IF_COPY(cluster, 1); 8.2818 + PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance); 8.2819 +} 8.2820 + 8.2821 +/* calculate distance between two clusters ("<->" operator) in SQL */ 8.2822 +PG_FUNCTION_INFO_V1(pgl_ecluster_distance); 8.2823 +Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) { 8.2824 + pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2825 + pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2826 + double retval = pgl_cluster_distance(cluster1, cluster2); 8.2827 + PG_FREE_IF_COPY(cluster1, 0); 8.2828 + PG_FREE_IF_COPY(cluster2, 1); 8.2829 + PG_RETURN_FLOAT8(retval); 8.2830 +} 8.2831 + 8.2832 +/* calculate fair distance (see README) between cluster and point with 8.2833 + precision denoted by sample count ("<=> operator) in SQL */ 8.2834 +PG_FUNCTION_INFO_V1(pgl_ecluster_epoint_sc_fair_distance); 8.2835 +Datum pgl_ecluster_epoint_sc_fair_distance(PG_FUNCTION_ARGS) { 8.2836 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 8.2837 + pgl_point_sc *search = (pgl_point_sc *)PG_GETARG_POINTER(1); 8.2838 + double retval = pgl_fair_distance( 8.2839 + &search->point, cluster, search->samples 8.2840 + ); 8.2841 + PG_FREE_IF_COPY(cluster, 0); 8.2842 + PG_RETURN_FLOAT8(retval); 8.2843 +} 8.2844 + 8.2845 + 8.2846 +/*-----------------------------------------------------------* 8.2847 + * B-tree comparison operators and index support functions * 8.2848 + *-----------------------------------------------------------*/ 8.2849 + 8.2850 +/* macro for a B-tree operator (without detoasting) */ 8.2851 +#define PGL_BTREE_OPER(func, type, cmpfunc, oper) \ 8.2852 + PG_FUNCTION_INFO_V1(func); \ 8.2853 + Datum func(PG_FUNCTION_ARGS) { \ 8.2854 + type *a = (type *)PG_GETARG_POINTER(0); \ 8.2855 + type *b = (type *)PG_GETARG_POINTER(1); \ 8.2856 + PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \ 8.2857 + } 8.2858 + 8.2859 +/* macro for a B-tree comparison function (without detoasting) */ 8.2860 +#define PGL_BTREE_CMP(func, type, cmpfunc) \ 8.2861 + PG_FUNCTION_INFO_V1(func); \ 8.2862 + Datum func(PG_FUNCTION_ARGS) { \ 8.2863 + type *a = (type *)PG_GETARG_POINTER(0); \ 8.2864 + type *b = (type *)PG_GETARG_POINTER(1); \ 8.2865 + PG_RETURN_INT32(cmpfunc(a, b)); \ 8.2866 + } 8.2867 + 8.2868 +/* macro for a B-tree operator (with detoasting) */ 8.2869 +#define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \ 8.2870 + PG_FUNCTION_INFO_V1(func); \ 8.2871 + Datum func(PG_FUNCTION_ARGS) { \ 8.2872 + bool res; \ 8.2873 + type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \ 8.2874 + type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \ 8.2875 + res = cmpfunc(a, b) oper 0; \ 8.2876 + PG_FREE_IF_COPY(a, 0); \ 8.2877 + PG_FREE_IF_COPY(b, 1); \ 8.2878 + PG_RETURN_BOOL(res); \ 8.2879 + } 8.2880 + 8.2881 +/* macro for a B-tree comparison function (with detoasting) */ 8.2882 +#define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \ 8.2883 + PG_FUNCTION_INFO_V1(func); \ 8.2884 + Datum func(PG_FUNCTION_ARGS) { \ 8.2885 + int32_t res; \ 8.2886 + type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \ 8.2887 + type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \ 8.2888 + res = cmpfunc(a, b); \ 8.2889 + PG_FREE_IF_COPY(a, 0); \ 8.2890 + PG_FREE_IF_COPY(b, 1); \ 8.2891 + PG_RETURN_INT32(res); \ 8.2892 + } 8.2893 + 8.2894 +/* B-tree operators and comparison function for point */ 8.2895 +PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <) 8.2896 +PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=) 8.2897 +PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==) 8.2898 +PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=) 8.2899 +PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=) 8.2900 +PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >) 8.2901 +PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp) 8.2902 + 8.2903 +/* B-tree operators and comparison function for box */ 8.2904 +PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <) 8.2905 +PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=) 8.2906 +PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==) 8.2907 +PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=) 8.2908 +PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=) 8.2909 +PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >) 8.2910 +PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp) 8.2911 + 8.2912 +/* B-tree operators and comparison function for circle */ 8.2913 +PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <) 8.2914 +PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=) 8.2915 +PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==) 8.2916 +PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=) 8.2917 +PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=) 8.2918 +PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >) 8.2919 +PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp) 8.2920 + 8.2921 + 8.2922 +/*--------------------------------* 8.2923 + * GiST index support functions * 8.2924 + *--------------------------------*/ 8.2925 + 8.2926 +/* GiST "consistent" support function */ 8.2927 +PG_FUNCTION_INFO_V1(pgl_gist_consistent); 8.2928 +Datum pgl_gist_consistent(PG_FUNCTION_ARGS) { 8.2929 + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 8.2930 + pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key); 8.2931 + StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); 8.2932 + bool *recheck = (bool *)PG_GETARG_POINTER(4); 8.2933 + /* demand recheck because index and query methods are lossy */ 8.2934 + *recheck = true; 8.2935 + /* strategy number aliases for different operators using the same strategy */ 8.2936 + strategy %= 100; 8.2937 + /* strategy number 11: equality of two points */ 8.2938 + if (strategy == 11) { 8.2939 + /* query datum is another point */ 8.2940 + pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 8.2941 + /* convert other point to key */ 8.2942 + pgl_pointkey querykey; 8.2943 + pgl_point_to_key(query, querykey); 8.2944 + /* return true if both keys overlap */ 8.2945 + PG_RETURN_BOOL(pgl_keys_overlap(key, querykey)); 8.2946 + } 8.2947 + /* strategy number 13: equality of two circles */ 8.2948 + if (strategy == 13) { 8.2949 + /* query datum is another circle */ 8.2950 + pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 8.2951 + /* convert other circle to key */ 8.2952 + pgl_areakey querykey; 8.2953 + pgl_circle_to_key(query, querykey); 8.2954 + /* return true if both keys overlap */ 8.2955 + PG_RETURN_BOOL(pgl_keys_overlap(key, querykey)); 8.2956 + } 8.2957 + /* for all remaining strategies, keys on empty objects produce no match */ 8.2958 + /* (check necessary because query radius may be infinite) */ 8.2959 + if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false); 8.2960 + /* strategy number 21: overlapping with point */ 8.2961 + if (strategy == 21) { 8.2962 + /* query datum is a point */ 8.2963 + pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 8.2964 + /* return true if estimated distance (allowed to be smaller than real 8.2965 + distance) between index key and point is zero */ 8.2966 + PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0); 8.2967 + } 8.2968 + /* strategy number 22: (point) overlapping with box */ 8.2969 + if (strategy == 22) { 8.2970 + /* query datum is a box */ 8.2971 + pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1); 8.2972 + /* determine bounding box of indexed key */ 8.2973 + pgl_box keybox; 8.2974 + pgl_key_to_box(key, &keybox); 8.2975 + /* return true if query box overlaps with bounding box of indexed key */ 8.2976 + PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox)); 8.2977 + } 8.2978 + /* strategy number 23: overlapping with circle */ 8.2979 + if (strategy == 23) { 8.2980 + /* query datum is a circle */ 8.2981 + pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 8.2982 + /* return true if estimated distance (allowed to be smaller than real 8.2983 + distance) between index key and circle center is smaller than radius */ 8.2984 + PG_RETURN_BOOL( 8.2985 + (1.0-PGL_SPHEROID_F) * /* safety margin for lossy operator */ 8.2986 + pgl_estimate_key_distance(key, &(query->center)) 8.2987 + <= query->radius 8.2988 + ); 8.2989 + } 8.2990 + /* strategy number 24: overlapping with cluster */ 8.2991 + if (strategy == 24) { 8.2992 + bool retval; /* return value */ 8.2993 + /* query datum is a cluster */ 8.2994 + pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.2995 + /* return true if estimated distance (allowed to be smaller than real 8.2996 + distance) between index key and circle center is smaller than radius */ 8.2997 + retval = ( 8.2998 + (1.0-PGL_SPHEROID_F) * /* safety margin for lossy operator */ 8.2999 + pgl_estimate_key_distance(key, &(query->bounding.center)) 8.3000 + <= query->bounding.radius 8.3001 + ); 8.3002 + PG_FREE_IF_COPY(query, 1); /* free detoasted cluster (if copy) */ 8.3003 + PG_RETURN_BOOL(retval); 8.3004 + } 8.3005 + /* throw error for any unknown strategy number */ 8.3006 + elog(ERROR, "unrecognized strategy number: %d", strategy); 8.3007 +} 8.3008 + 8.3009 +/* GiST "union" support function */ 8.3010 +PG_FUNCTION_INFO_V1(pgl_gist_union); 8.3011 +Datum pgl_gist_union(PG_FUNCTION_ARGS) { 8.3012 + GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); 8.3013 + pgl_keyptr out; /* return value (to be palloc'ed) */ 8.3014 + int i; 8.3015 + /* determine key size */ 8.3016 + size_t keysize = PGL_KEY_IS_AREAKEY( 8.3017 + (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key) 8.3018 + ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey); 8.3019 + /* begin with first key as result */ 8.3020 + out = palloc(keysize); 8.3021 + memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize); 8.3022 + /* unite current result with second, third, etc. key */ 8.3023 + for (i=1; i<entryvec->n; i++) { 8.3024 + pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)); 8.3025 + } 8.3026 + /* return result */ 8.3027 + PG_RETURN_POINTER(out); 8.3028 +} 8.3029 + 8.3030 +/* GiST "compress" support function for indicis on points */ 8.3031 +PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint); 8.3032 +Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) { 8.3033 + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 8.3034 + GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 8.3035 + /* only transform new leaves */ 8.3036 + if (entry->leafkey) { 8.3037 + /* get point to be transformed */ 8.3038 + pgl_point *point = (pgl_point *)DatumGetPointer(entry->key); 8.3039 + /* allocate memory for key */ 8.3040 + pgl_keyptr key = palloc(sizeof(pgl_pointkey)); 8.3041 + /* transform point to key */ 8.3042 + pgl_point_to_key(point, key); 8.3043 + /* create new GISTENTRY structure as return value */ 8.3044 + retval = palloc(sizeof(GISTENTRY)); 8.3045 + gistentryinit( 8.3046 + *retval, PointerGetDatum(key), 8.3047 + entry->rel, entry->page, entry->offset, FALSE 8.3048 + ); 8.3049 + } else { 8.3050 + /* inner nodes have already been transformed */ 8.3051 + retval = entry; 8.3052 + } 8.3053 + /* return pointer to old or new GISTENTRY structure */ 8.3054 + PG_RETURN_POINTER(retval); 8.3055 +} 8.3056 + 8.3057 +/* GiST "compress" support function for indicis on circles */ 8.3058 +PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle); 8.3059 +Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) { 8.3060 + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 8.3061 + GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 8.3062 + /* only transform new leaves */ 8.3063 + if (entry->leafkey) { 8.3064 + /* get circle to be transformed */ 8.3065 + pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key); 8.3066 + /* allocate memory for key */ 8.3067 + pgl_keyptr key = palloc(sizeof(pgl_areakey)); 8.3068 + /* transform circle to key */ 8.3069 + pgl_circle_to_key(circle, key); 8.3070 + /* create new GISTENTRY structure as return value */ 8.3071 + retval = palloc(sizeof(GISTENTRY)); 8.3072 + gistentryinit( 8.3073 + *retval, PointerGetDatum(key), 8.3074 + entry->rel, entry->page, entry->offset, FALSE 8.3075 + ); 8.3076 + } else { 8.3077 + /* inner nodes have already been transformed */ 8.3078 + retval = entry; 8.3079 + } 8.3080 + /* return pointer to old or new GISTENTRY structure */ 8.3081 + PG_RETURN_POINTER(retval); 8.3082 +} 8.3083 + 8.3084 +/* GiST "compress" support function for indices on clusters */ 8.3085 +PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster); 8.3086 +Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) { 8.3087 + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); 8.3088 + GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */ 8.3089 + /* only transform new leaves */ 8.3090 + if (entry->leafkey) { 8.3091 + /* get cluster to be transformed (detoasting necessary!) */ 8.3092 + pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key); 8.3093 + /* allocate memory for key */ 8.3094 + pgl_keyptr key = palloc(sizeof(pgl_areakey)); 8.3095 + /* transform cluster to key */ 8.3096 + pgl_circle_to_key(&(cluster->bounding), key); 8.3097 + /* create new GISTENTRY structure as return value */ 8.3098 + retval = palloc(sizeof(GISTENTRY)); 8.3099 + gistentryinit( 8.3100 + *retval, PointerGetDatum(key), 8.3101 + entry->rel, entry->page, entry->offset, FALSE 8.3102 + ); 8.3103 + /* free detoasted datum */ 8.3104 + if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster); 8.3105 + } else { 8.3106 + /* inner nodes have already been transformed */ 8.3107 + retval = entry; 8.3108 + } 8.3109 + /* return pointer to old or new GISTENTRY structure */ 8.3110 + PG_RETURN_POINTER(retval); 8.3111 +} 8.3112 + 8.3113 +/* GiST "decompress" support function for indices */ 8.3114 +PG_FUNCTION_INFO_V1(pgl_gist_decompress); 8.3115 +Datum pgl_gist_decompress(PG_FUNCTION_ARGS) { 8.3116 + /* return passed pointer without transformation */ 8.3117 + PG_RETURN_POINTER(PG_GETARG_POINTER(0)); 8.3118 +} 8.3119 + 8.3120 +/* GiST "penalty" support function */ 8.3121 +PG_FUNCTION_INFO_V1(pgl_gist_penalty); 8.3122 +Datum pgl_gist_penalty(PG_FUNCTION_ARGS) { 8.3123 + GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0); 8.3124 + GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1); 8.3125 + float *penalty = (float *)PG_GETARG_POINTER(2); 8.3126 + /* get original key and key to insert */ 8.3127 + pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key); 8.3128 + pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key); 8.3129 + /* copy original key */ 8.3130 + union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key; 8.3131 + if (PGL_KEY_IS_AREAKEY(orig)) { 8.3132 + memcpy(union_key.areakey, orig, sizeof(union_key.areakey)); 8.3133 + } else { 8.3134 + memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey)); 8.3135 + } 8.3136 + /* calculate union of both keys */ 8.3137 + pgl_unite_keys((pgl_keyptr)&union_key, new); 8.3138 + /* penalty equal to reduction of key length (logarithm of added area) */ 8.3139 + /* (return value by setting referenced value and returning pointer) */ 8.3140 + *penalty = ( 8.3141 + PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key) 8.3142 + ); 8.3143 + PG_RETURN_POINTER(penalty); 8.3144 +} 8.3145 + 8.3146 +/* GiST "picksplit" support function */ 8.3147 +PG_FUNCTION_INFO_V1(pgl_gist_picksplit); 8.3148 +Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) { 8.3149 + GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); 8.3150 + GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1); 8.3151 + OffsetNumber i; /* between FirstOffsetNumber and entryvec->n (inclusive) */ 8.3152 + union { 8.3153 + pgl_pointkey pointkey; 8.3154 + pgl_areakey areakey; 8.3155 + } union_all; /* union of all keys (to be calculated from scratch) 8.3156 + (later cut in half) */ 8.3157 + int is_areakey = PGL_KEY_IS_AREAKEY( 8.3158 + (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key) 8.3159 + ); 8.3160 + int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey); 8.3161 + pgl_keyptr unionL = palloc(keysize); /* union of keys that go left */ 8.3162 + pgl_keyptr unionR = palloc(keysize); /* union of keys that go right */ 8.3163 + pgl_keyptr key; /* current key to be processed */ 8.3164 + /* allocate memory for array of left and right keys, set counts to zero */ 8.3165 + v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber)); 8.3166 + v->spl_nleft = 0; 8.3167 + v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber)); 8.3168 + v->spl_nright = 0; 8.3169 + /* calculate union of all keys from scratch */ 8.3170 + memcpy( 8.3171 + (pgl_keyptr)&union_all, 8.3172 + (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key), 8.3173 + keysize 8.3174 + ); 8.3175 + for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) { 8.3176 + pgl_unite_keys( 8.3177 + (pgl_keyptr)&union_all, 8.3178 + (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key) 8.3179 + ); 8.3180 + } 8.3181 + /* check if trivial split is necessary due to exhausted key length */ 8.3182 + /* (Note: keys for empty objects must have node depth set to maximum) */ 8.3183 + if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == ( 8.3184 + is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH 8.3185 + )) { 8.3186 + /* half of all keys go left */ 8.3187 + for ( 8.3188 + i=FirstOffsetNumber; 8.3189 + i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2; 8.3190 + i=OffsetNumberNext(i) 8.3191 + ) { 8.3192 + /* pointer to current key */ 8.3193 + key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 8.3194 + /* update unionL */ 8.3195 + /* check if key is first key that goes left */ 8.3196 + if (!v->spl_nleft) { 8.3197 + /* first key that goes left is just copied to unionL */ 8.3198 + memcpy(unionL, key, keysize); 8.3199 + } else { 8.3200 + /* unite current value and next key */ 8.3201 + pgl_unite_keys(unionL, key); 8.3202 + } 8.3203 + /* append offset number to list of keys that go left */ 8.3204 + v->spl_left[v->spl_nleft++] = i; 8.3205 + } 8.3206 + /* other half goes right */ 8.3207 + for ( 8.3208 + i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2; 8.3209 + i<entryvec->n; 8.3210 + i=OffsetNumberNext(i) 8.3211 + ) { 8.3212 + /* pointer to current key */ 8.3213 + key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 8.3214 + /* update unionR */ 8.3215 + /* check if key is first key that goes right */ 8.3216 + if (!v->spl_nright) { 8.3217 + /* first key that goes right is just copied to unionR */ 8.3218 + memcpy(unionR, key, keysize); 8.3219 + } else { 8.3220 + /* unite current value and next key */ 8.3221 + pgl_unite_keys(unionR, key); 8.3222 + } 8.3223 + /* append offset number to list of keys that go right */ 8.3224 + v->spl_right[v->spl_nright++] = i; 8.3225 + } 8.3226 + } 8.3227 + /* otherwise, a non-trivial split is possible */ 8.3228 + else { 8.3229 + /* cut covered area in half */ 8.3230 + /* (union_all then refers to area of keys that go left) */ 8.3231 + /* check if union of all keys covers empty and non-empty objects */ 8.3232 + if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) { 8.3233 + /* if yes, split into empty and non-empty objects */ 8.3234 + pgl_key_set_empty((pgl_keyptr)&union_all); 8.3235 + } else { 8.3236 + /* otherwise split by next bit */ 8.3237 + ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++; 8.3238 + /* NOTE: type bit conserved */ 8.3239 + } 8.3240 + /* determine for each key if it goes left or right */ 8.3241 + for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) { 8.3242 + /* pointer to current key */ 8.3243 + key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key); 8.3244 + /* keys within one half of the area go left */ 8.3245 + if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) { 8.3246 + /* update unionL */ 8.3247 + /* check if key is first key that goes left */ 8.3248 + if (!v->spl_nleft) { 8.3249 + /* first key that goes left is just copied to unionL */ 8.3250 + memcpy(unionL, key, keysize); 8.3251 + } else { 8.3252 + /* unite current value of unionL and processed key */ 8.3253 + pgl_unite_keys(unionL, key); 8.3254 + } 8.3255 + /* append offset number to list of keys that go left */ 8.3256 + v->spl_left[v->spl_nleft++] = i; 8.3257 + } 8.3258 + /* the other keys go right */ 8.3259 + else { 8.3260 + /* update unionR */ 8.3261 + /* check if key is first key that goes right */ 8.3262 + if (!v->spl_nright) { 8.3263 + /* first key that goes right is just copied to unionR */ 8.3264 + memcpy(unionR, key, keysize); 8.3265 + } else { 8.3266 + /* unite current value of unionR and processed key */ 8.3267 + pgl_unite_keys(unionR, key); 8.3268 + } 8.3269 + /* append offset number to list of keys that go right */ 8.3270 + v->spl_right[v->spl_nright++] = i; 8.3271 + } 8.3272 + } 8.3273 + } 8.3274 + /* store unions in return value */ 8.3275 + v->spl_ldatum = PointerGetDatum(unionL); 8.3276 + v->spl_rdatum = PointerGetDatum(unionR); 8.3277 + /* return all results */ 8.3278 + PG_RETURN_POINTER(v); 8.3279 +} 8.3280 + 8.3281 +/* GiST "same"/"equal" support function */ 8.3282 +PG_FUNCTION_INFO_V1(pgl_gist_same); 8.3283 +Datum pgl_gist_same(PG_FUNCTION_ARGS) { 8.3284 + pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0); 8.3285 + pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1); 8.3286 + bool *boolptr = (bool *)PG_GETARG_POINTER(2); 8.3287 + /* two keys are equal if they are binary equal */ 8.3288 + /* (return result by setting referenced boolean and returning pointer) */ 8.3289 + *boolptr = !memcmp( 8.3290 + key1, 8.3291 + key2, 8.3292 + PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey) 8.3293 + ); 8.3294 + PG_RETURN_POINTER(boolptr); 8.3295 +} 8.3296 + 8.3297 +/* GiST "distance" support function */ 8.3298 +PG_FUNCTION_INFO_V1(pgl_gist_distance); 8.3299 +Datum pgl_gist_distance(PG_FUNCTION_ARGS) { 8.3300 + GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); 8.3301 + pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key); 8.3302 + StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); 8.3303 + bool *recheck = (bool *)PG_GETARG_POINTER(4); 8.3304 + double distance; /* return value */ 8.3305 + /* demand recheck because distance is just an estimation */ 8.3306 + /* (real distance may be bigger) */ 8.3307 + *recheck = true; 8.3308 + /* strategy number aliases for different operators using the same strategy */ 8.3309 + strategy %= 100; 8.3310 + /* strategy number 31: distance to point */ 8.3311 + if (strategy == 31) { 8.3312 + /* query datum is a point */ 8.3313 + pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1); 8.3314 + /* use pgl_estimate_pointkey_distance() function to compute result */ 8.3315 + distance = pgl_estimate_key_distance(key, query); 8.3316 + /* avoid infinity (reserved!) */ 8.3317 + if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 8.3318 + /* return result */ 8.3319 + PG_RETURN_FLOAT8(distance); 8.3320 + } 8.3321 + /* strategy number 33: distance to circle */ 8.3322 + if (strategy == 33) { 8.3323 + /* query datum is a circle */ 8.3324 + pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1); 8.3325 + /* estimate distance to circle center and substract circle radius */ 8.3326 + distance = ( 8.3327 + pgl_estimate_key_distance(key, &(query->center)) - query->radius 8.3328 + ); 8.3329 + /* convert non-positive values to zero and avoid infinity (reserved!) */ 8.3330 + if (distance <= 0) distance = 0; 8.3331 + else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 8.3332 + /* return result */ 8.3333 + PG_RETURN_FLOAT8(distance); 8.3334 + } 8.3335 + /* strategy number 34: distance to cluster */ 8.3336 + if (strategy == 34) { 8.3337 + /* query datum is a cluster */ 8.3338 + pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); 8.3339 + /* estimate distance to bounding center and substract bounding radius */ 8.3340 + distance = ( 8.3341 + pgl_estimate_key_distance(key, &(query->bounding.center)) - 8.3342 + query->bounding.radius 8.3343 + ); 8.3344 + /* convert non-positive values to zero and avoid infinity (reserved!) */ 8.3345 + if (distance <= 0) distance = 0; 8.3346 + else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE; 8.3347 + /* free detoasted cluster (if copy) */ 8.3348 + PG_FREE_IF_COPY(query, 1); 8.3349 + /* return result */ 8.3350 + PG_RETURN_FLOAT8(distance); 8.3351 + } 8.3352 + /* throw error for any unknown strategy number */ 8.3353 + elog(ERROR, "unrecognized strategy number: %d", strategy); 8.3354 +} 8.3355 +