rev |
line source |
jbe@0
|
1
|
jbe@0
|
2 /*-------------*
|
jbe@0
|
3 * C prelude *
|
jbe@0
|
4 *-------------*/
|
jbe@0
|
5
|
jbe@0
|
6 #include "postgres.h"
|
jbe@0
|
7 #include "fmgr.h"
|
jbe@0
|
8 #include "libpq/pqformat.h"
|
jbe@0
|
9 #include "access/gist.h"
|
jbe@0
|
10 #include "access/stratnum.h"
|
jbe@0
|
11 #include "utils/array.h"
|
jbe@0
|
12 #include <math.h>
|
jbe@0
|
13
|
jbe@0
|
14 #ifdef PG_MODULE_MAGIC
|
jbe@0
|
15 PG_MODULE_MAGIC;
|
jbe@0
|
16 #endif
|
jbe@0
|
17
|
jbe@0
|
18 #if INT_MAX < 2147483647
|
jbe@0
|
19 #error Expected int type to be at least 32 bit wide
|
jbe@0
|
20 #endif
|
jbe@0
|
21
|
jbe@0
|
22
|
jbe@0
|
23 /*---------------------------------*
|
jbe@0
|
24 * distance calculation on earth *
|
jbe@0
|
25 * (using WGS-84 spheroid) *
|
jbe@0
|
26 *---------------------------------*/
|
jbe@0
|
27
|
jbe@0
|
28 /* WGS-84 spheroid with following parameters:
|
jbe@0
|
29 semi-major axis a = 6378137
|
jbe@0
|
30 semi-minor axis b = a * (1 - 1/298.257223563)
|
jbe@0
|
31 estimated diameter = 2 * (2*a+b)/3
|
jbe@0
|
32 */
|
jbe@0
|
33 #define PGL_SPHEROID_A 6378137.0 /* semi major axis */
|
jbe@0
|
34 #define PGL_SPHEROID_F (1.0/298.257223563) /* flattening */
|
jbe@0
|
35 #define PGL_SPHEROID_B (PGL_SPHEROID_A * (1.0-PGL_SPHEROID_F))
|
jbe@0
|
36 #define PGL_EPS2 ( ( PGL_SPHEROID_A * PGL_SPHEROID_A - \
|
jbe@0
|
37 PGL_SPHEROID_B * PGL_SPHEROID_B ) / \
|
jbe@0
|
38 ( PGL_SPHEROID_A * PGL_SPHEROID_A ) )
|
jbe@0
|
39 #define PGL_SUBEPS2 (1.0-PGL_EPS2)
|
jbe@0
|
40 #define PGL_DIAMETER ((4.0*PGL_SPHEROID_A + 2.0*PGL_SPHEROID_B) / 3.0)
|
jbe@0
|
41 #define PGL_SCALE (PGL_SPHEROID_A / PGL_DIAMETER) /* semi-major ref. */
|
jbe@0
|
42 #define PGL_FADELIMIT (PGL_DIAMETER * M_PI / 6.0) /* 1/6 circumference */
|
jbe@0
|
43 #define PGL_MAXDIST (PGL_DIAMETER * M_PI / 2.0) /* maximum distance */
|
jbe@0
|
44
|
jbe@0
|
45 /* calculate distance between two points on earth (given in degrees) */
|
jbe@0
|
46 static inline double pgl_distance(
|
jbe@0
|
47 double lat1, double lon1, double lat2, double lon2
|
jbe@0
|
48 ) {
|
jbe@0
|
49 float8 lat1cos, lat1sin, lat2cos, lat2sin, lon2cos, lon2sin;
|
jbe@0
|
50 float8 nphi1, nphi2, x1, z1, x2, y2, z2, g, s, t;
|
jbe@0
|
51 /* normalize delta longitude (lon2 > 0 && lon1 = 0) */
|
jbe@0
|
52 /* lon1 = 0 (not used anymore) */
|
jbe@0
|
53 lon2 = fabs(lon2-lon1);
|
jbe@0
|
54 /* convert to radians (first divide, then multiply) */
|
jbe@0
|
55 lat1 = (lat1 / 180.0) * M_PI;
|
jbe@0
|
56 lat2 = (lat2 / 180.0) * M_PI;
|
jbe@0
|
57 lon2 = (lon2 / 180.0) * M_PI;
|
jbe@0
|
58 /* make lat2 >= lat1 to ensure reversal-symmetry despite floating point
|
jbe@0
|
59 operations (lon2 >= lon1 is already ensured in a previous step) */
|
jbe@0
|
60 if (lat2 < lat1) { float8 swap = lat1; lat1 = lat2; lat2 = swap; }
|
jbe@0
|
61 /* calculate 3d coordinates on scaled ellipsoid which has an average diameter
|
jbe@0
|
62 of 1.0 */
|
jbe@0
|
63 lat1cos = cos(lat1); lat1sin = sin(lat1);
|
jbe@0
|
64 lat2cos = cos(lat2); lat2sin = sin(lat2);
|
jbe@0
|
65 lon2cos = cos(lon2); lon2sin = sin(lon2);
|
jbe@0
|
66 nphi1 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat1sin * lat1sin);
|
jbe@0
|
67 nphi2 = PGL_SCALE / sqrt(1 - PGL_EPS2 * lat2sin * lat2sin);
|
jbe@0
|
68 x1 = nphi1 * lat1cos;
|
jbe@0
|
69 z1 = nphi1 * PGL_SUBEPS2 * lat1sin;
|
jbe@0
|
70 x2 = nphi2 * lat2cos * lon2cos;
|
jbe@0
|
71 y2 = nphi2 * lat2cos * lon2sin;
|
jbe@0
|
72 z2 = nphi2 * PGL_SUBEPS2 * lat2sin;
|
jbe@0
|
73 /* calculate tunnel distance through scaled (diameter 1.0) ellipsoid */
|
jbe@0
|
74 g = sqrt((x2-x1)*(x2-x1) + y2*y2 + (z2-z1)*(z2-z1));
|
jbe@0
|
75 /* convert tunnel distance through scaled ellipsoid to approximated surface
|
jbe@0
|
76 distance on original ellipsoid */
|
jbe@0
|
77 if (g > 1.0) g = 1.0;
|
jbe@0
|
78 s = PGL_DIAMETER * asin(g);
|
jbe@0
|
79 /* return result only if small enough to be precise (less than 1/3 of
|
jbe@0
|
80 maximum possible distance) */
|
jbe@0
|
81 if (s <= PGL_FADELIMIT) return s;
|
jbe@0
|
82 /* calculate tunnel distance to antipodal point through scaled ellipsoid */
|
jbe@3
|
83 g = sqrt((x2+x1)*(x2+x1) + y2*y2 + (z2+z1)*(z2+z1));
|
jbe@0
|
84 /* convert tunnel distance to antipodal point through scaled ellipsoid to
|
jbe@0
|
85 approximated surface distance to antipodal point on original ellipsoid */
|
jbe@0
|
86 if (g > 1.0) g = 1.0;
|
jbe@0
|
87 t = PGL_DIAMETER * asin(g);
|
jbe@0
|
88 /* surface distance between original points can now be approximated by
|
jbe@0
|
89 substracting antipodal distance from maximum possible distance;
|
jbe@0
|
90 return result only if small enough (less than 1/3 of maximum possible
|
jbe@0
|
91 distance) */
|
jbe@0
|
92 if (t <= PGL_FADELIMIT) return PGL_MAXDIST-t;
|
jbe@0
|
93 /* otherwise crossfade direct and antipodal result to ensure monotonicity */
|
jbe@0
|
94 return (
|
jbe@0
|
95 (s * (t-PGL_FADELIMIT) + (PGL_MAXDIST-t) * (s-PGL_FADELIMIT)) /
|
jbe@0
|
96 (s + t - 2*PGL_FADELIMIT)
|
jbe@0
|
97 );
|
jbe@0
|
98 }
|
jbe@0
|
99
|
jbe@0
|
100 /* finite distance that can not be reached on earth */
|
jbe@0
|
101 #define PGL_ULTRA_DISTANCE (3 * PGL_MAXDIST)
|
jbe@0
|
102
|
jbe@0
|
103
|
jbe@0
|
104 /*--------------------------------*
|
jbe@0
|
105 * simple geographic data types *
|
jbe@0
|
106 *--------------------------------*/
|
jbe@0
|
107
|
jbe@0
|
108 /* point on earth given by latitude and longitude in degrees */
|
jbe@0
|
109 /* (type "epoint" in SQL) */
|
jbe@0
|
110 typedef struct {
|
jbe@0
|
111 double lat; /* between -90 and 90 (both inclusive) */
|
jbe@0
|
112 double lon; /* between -180 and 180 (both inclusive) */
|
jbe@0
|
113 } pgl_point;
|
jbe@0
|
114
|
jbe@0
|
115 /* box delimited by two parallels and two meridians (all in degrees) */
|
jbe@0
|
116 /* (type "ebox" in SQL) */
|
jbe@0
|
117 typedef struct {
|
jbe@0
|
118 double lat_min; /* between -90 and 90 (both inclusive) */
|
jbe@0
|
119 double lat_max; /* between -90 and 90 (both inclusive) */
|
jbe@0
|
120 double lon_min; /* between -180 and 180 (both inclusive) */
|
jbe@0
|
121 double lon_max; /* between -180 and 180 (both inclusive) */
|
jbe@0
|
122 /* if lat_min > lat_max, then box is empty */
|
jbe@0
|
123 /* if lon_min > lon_max, then 180th meridian is crossed */
|
jbe@0
|
124 } pgl_box;
|
jbe@0
|
125
|
jbe@0
|
126 /* circle on earth surface (for radial searches with fixed radius) */
|
jbe@0
|
127 /* (type "ecircle" in SQL) */
|
jbe@0
|
128 typedef struct {
|
jbe@0
|
129 pgl_point center;
|
jbe@0
|
130 double radius; /* positive (including +0 but excluding -0), or -INFINITY */
|
jbe@0
|
131 /* A negative radius (i.e. -INFINITY) denotes nothing (i.e. no point),
|
jbe@0
|
132 zero radius (0) denotes a single point,
|
jbe@0
|
133 a finite radius (0 < radius < INFINITY) denotes a filled circle, and
|
jbe@0
|
134 a radius of INFINITY is valid and means complete coverage of earth. */
|
jbe@0
|
135 } pgl_circle;
|
jbe@0
|
136
|
jbe@0
|
137
|
jbe@0
|
138 /*----------------------------------*
|
jbe@0
|
139 * geographic "cluster" data type *
|
jbe@0
|
140 *----------------------------------*/
|
jbe@0
|
141
|
jbe@0
|
142 /* A cluster is a collection of points, paths, outlines, and polygons. If two
|
jbe@0
|
143 polygons in a cluster overlap, the area covered by both polygons does not
|
jbe@0
|
144 belong to the cluster. This way, a cluster can be used to describe complex
|
jbe@0
|
145 shapes like polygons with holes. Outlines are non-filled polygons. Paths are
|
jbe@0
|
146 open by default (i.e. the last point in the list is not connected with the
|
jbe@0
|
147 first point in the list). Note that each outline or polygon in a cluster
|
jbe@0
|
148 must cover a longitude range of less than 180 degrees to avoid ambiguities.
|
jbe@0
|
149 Areas which are larger may be split into multiple polygons. */
|
jbe@0
|
150
|
jbe@0
|
151 /* maximum number of points in a cluster */
|
jbe@0
|
152 /* (limited to avoid integer overflows, e.g. when allocating memory) */
|
jbe@0
|
153 #define PGL_CLUSTER_MAXPOINTS 16777216
|
jbe@0
|
154
|
jbe@0
|
155 /* types of cluster entries */
|
jbe@0
|
156 #define PGL_ENTRY_POINT 1 /* a point */
|
jbe@0
|
157 #define PGL_ENTRY_PATH 2 /* a path from first point to last point */
|
jbe@0
|
158 #define PGL_ENTRY_OUTLINE 3 /* a non-filled polygon with given vertices */
|
jbe@0
|
159 #define PGL_ENTRY_POLYGON 4 /* a filled polygon with given vertices */
|
jbe@0
|
160
|
jbe@0
|
161 /* Entries of a cluster are described by two different structs: pgl_newentry
|
jbe@0
|
162 and pgl_entry. The first is used only during construction of a cluster, the
|
jbe@0
|
163 second is used in all other cases (e.g. when reading clusters from the
|
jbe@0
|
164 database, performing operations, etc). */
|
jbe@0
|
165
|
jbe@0
|
166 /* entry for new geographic cluster during construction of that cluster */
|
jbe@0
|
167 typedef struct {
|
jbe@0
|
168 int32_t entrytype;
|
jbe@0
|
169 int32_t npoints;
|
jbe@0
|
170 pgl_point *points; /* pointer to an array of points (pgl_point) */
|
jbe@0
|
171 } pgl_newentry;
|
jbe@0
|
172
|
jbe@0
|
173 /* entry of geographic cluster */
|
jbe@0
|
174 typedef struct {
|
jbe@0
|
175 int32_t entrytype; /* type of entry: point, path, outline, polygon */
|
jbe@0
|
176 int32_t npoints; /* number of stored points (set to 1 for point entry) */
|
jbe@0
|
177 int32_t offset; /* offset of pgl_point array from cluster base address */
|
jbe@0
|
178 /* use macro PGL_ENTRY_POINTS to obtain a pointer to the array of points */
|
jbe@0
|
179 } pgl_entry;
|
jbe@0
|
180
|
jbe@0
|
181 /* geographic cluster which is a collection of points, (open) paths, polygons,
|
jbe@0
|
182 and outlines (non-filled polygons) */
|
jbe@0
|
183 typedef struct {
|
jbe@0
|
184 char header[VARHDRSZ]; /* PostgreSQL header for variable size data types */
|
jbe@0
|
185 int32_t nentries; /* number of stored points */
|
jbe@0
|
186 pgl_circle bounding; /* bounding circle */
|
jbe@0
|
187 /* Note: bounding circle ensures alignment of pgl_cluster for points */
|
jbe@0
|
188 pgl_entry entries[FLEXIBLE_ARRAY_MEMBER]; /* var-length data */
|
jbe@0
|
189 } pgl_cluster;
|
jbe@0
|
190
|
jbe@0
|
191 /* macro to determine memory alignment of points */
|
jbe@0
|
192 /* (needed to store pgl_point array after entries in pgl_cluster) */
|
jbe@0
|
193 typedef struct { char dummy; pgl_point aligned; } pgl_point_alignment;
|
jbe@0
|
194 #define PGL_POINT_ALIGNMENT offsetof(pgl_point_alignment, aligned)
|
jbe@0
|
195
|
jbe@0
|
196 /* macro to extract a pointer to the array of points of a cluster entry */
|
jbe@0
|
197 #define PGL_ENTRY_POINTS(cluster, idx) \
|
jbe@0
|
198 ((pgl_point *)(((intptr_t)cluster)+(cluster)->entries[idx].offset))
|
jbe@0
|
199
|
jbe@0
|
200 /* convert pgl_newentry array to pgl_cluster */
|
jbe@0
|
201 static pgl_cluster *pgl_new_cluster(int nentries, pgl_newentry *entries) {
|
jbe@0
|
202 int i; /* index of current entry */
|
jbe@0
|
203 int npoints = 0; /* number of points in whole cluster */
|
jbe@0
|
204 int entry_npoints; /* number of points in current entry */
|
jbe@0
|
205 int points_offset = PGL_POINT_ALIGNMENT * (
|
jbe@0
|
206 ( offsetof(pgl_cluster, entries) +
|
jbe@0
|
207 nentries * sizeof(pgl_entry) +
|
jbe@0
|
208 PGL_POINT_ALIGNMENT - 1
|
jbe@0
|
209 ) / PGL_POINT_ALIGNMENT
|
jbe@0
|
210 ); /* offset of pgl_point array from base address (considering alignment) */
|
jbe@0
|
211 pgl_cluster *cluster; /* new cluster to be returned */
|
jbe@0
|
212 /* determine total number of points */
|
jbe@0
|
213 for (i=0; i<nentries; i++) npoints += entries[i].npoints;
|
jbe@0
|
214 /* allocate memory for cluster (including entries and points) */
|
jbe@0
|
215 cluster = palloc(points_offset + npoints * sizeof(pgl_point));
|
jbe@0
|
216 /* re-count total number of points to determine offset for each entry */
|
jbe@0
|
217 npoints = 0;
|
jbe@0
|
218 /* copy entries and points */
|
jbe@0
|
219 for (i=0; i<nentries; i++) {
|
jbe@0
|
220 /* determine number of points in entry */
|
jbe@0
|
221 entry_npoints = entries[i].npoints;
|
jbe@0
|
222 /* copy entry */
|
jbe@0
|
223 cluster->entries[i].entrytype = entries[i].entrytype;
|
jbe@0
|
224 cluster->entries[i].npoints = entry_npoints;
|
jbe@0
|
225 /* calculate offset (in bytes) of pgl_point array */
|
jbe@0
|
226 cluster->entries[i].offset = points_offset + npoints * sizeof(pgl_point);
|
jbe@0
|
227 /* copy points */
|
jbe@0
|
228 memcpy(
|
jbe@0
|
229 PGL_ENTRY_POINTS(cluster, i),
|
jbe@0
|
230 entries[i].points,
|
jbe@0
|
231 entry_npoints * sizeof(pgl_point)
|
jbe@0
|
232 );
|
jbe@0
|
233 /* update total number of points processed */
|
jbe@0
|
234 npoints += entry_npoints;
|
jbe@0
|
235 }
|
jbe@0
|
236 /* set number of entries in cluster */
|
jbe@0
|
237 cluster->nentries = nentries;
|
jbe@0
|
238 /* set PostgreSQL header for variable sized data */
|
jbe@0
|
239 SET_VARSIZE(cluster, points_offset + npoints * sizeof(pgl_point));
|
jbe@0
|
240 /* return newly created cluster */
|
jbe@0
|
241 return cluster;
|
jbe@0
|
242 }
|
jbe@0
|
243
|
jbe@0
|
244
|
jbe@0
|
245 /*----------------------------------------*
|
jbe@0
|
246 * C functions on geographic data types *
|
jbe@0
|
247 *----------------------------------------*/
|
jbe@0
|
248
|
jbe@0
|
249 /* round latitude or longitude to 12 digits after decimal point */
|
jbe@0
|
250 static inline double pgl_round(double val) {
|
jbe@0
|
251 return round(val * 1e12) / 1e12;
|
jbe@0
|
252 }
|
jbe@0
|
253
|
jbe@0
|
254 /* compare two points */
|
jbe@0
|
255 /* (equality when same point on earth is described, otherwise an arbitrary
|
jbe@0
|
256 linear order) */
|
jbe@0
|
257 static int pgl_point_cmp(pgl_point *point1, pgl_point *point2) {
|
jbe@0
|
258 double lon1, lon2; /* modified longitudes for special cases */
|
jbe@0
|
259 /* use latitude as first ordering criterion */
|
jbe@0
|
260 if (point1->lat < point2->lat) return -1;
|
jbe@0
|
261 if (point1->lat > point2->lat) return 1;
|
jbe@0
|
262 /* determine modified longitudes (considering special case of poles and
|
jbe@0
|
263 180th meridian which can be described as W180 or E180) */
|
jbe@0
|
264 if (point1->lat == -90 || point1->lat == 90) lon1 = 0;
|
jbe@0
|
265 else if (point1->lon == 180) lon1 = -180;
|
jbe@0
|
266 else lon1 = point1->lon;
|
jbe@0
|
267 if (point2->lat == -90 || point2->lat == 90) lon2 = 0;
|
jbe@0
|
268 else if (point2->lon == 180) lon2 = -180;
|
jbe@0
|
269 else lon2 = point2->lon;
|
jbe@0
|
270 /* use (modified) longitude as secondary ordering criterion */
|
jbe@0
|
271 if (lon1 < lon2) return -1;
|
jbe@0
|
272 if (lon1 > lon2) return 1;
|
jbe@0
|
273 /* no difference found, points are equal */
|
jbe@0
|
274 return 0;
|
jbe@0
|
275 }
|
jbe@0
|
276
|
jbe@0
|
277 /* compare two boxes */
|
jbe@0
|
278 /* (equality when same box on earth is described, otherwise an arbitrary linear
|
jbe@0
|
279 order) */
|
jbe@0
|
280 static int pgl_box_cmp(pgl_box *box1, pgl_box *box2) {
|
jbe@0
|
281 /* two empty boxes are equal, and an empty box is always considered "less
|
jbe@0
|
282 than" a non-empty box */
|
jbe@0
|
283 if (box1->lat_min> box1->lat_max && box2->lat_min<=box2->lat_max) return -1;
|
jbe@0
|
284 if (box1->lat_min> box1->lat_max && box2->lat_min> box2->lat_max) return 0;
|
jbe@0
|
285 if (box1->lat_min<=box1->lat_max && box2->lat_min> box2->lat_max) return 1;
|
jbe@0
|
286 /* use southern border as first ordering criterion */
|
jbe@0
|
287 if (box1->lat_min < box2->lat_min) return -1;
|
jbe@0
|
288 if (box1->lat_min > box2->lat_min) return 1;
|
jbe@0
|
289 /* use northern border as second ordering criterion */
|
jbe@0
|
290 if (box1->lat_max < box2->lat_max) return -1;
|
jbe@0
|
291 if (box1->lat_max > box2->lat_max) return 1;
|
jbe@0
|
292 /* use western border as third ordering criterion */
|
jbe@0
|
293 if (box1->lon_min < box2->lon_min) return -1;
|
jbe@0
|
294 if (box1->lon_min > box2->lon_min) return 1;
|
jbe@0
|
295 /* use eastern border as fourth ordering criterion */
|
jbe@0
|
296 if (box1->lon_max < box2->lon_max) return -1;
|
jbe@0
|
297 if (box1->lon_max > box2->lon_max) return 1;
|
jbe@0
|
298 /* no difference found, boxes are equal */
|
jbe@0
|
299 return 0;
|
jbe@0
|
300 }
|
jbe@0
|
301
|
jbe@0
|
302 /* compare two circles */
|
jbe@0
|
303 /* (equality when same circle on earth is described, otherwise an arbitrary
|
jbe@0
|
304 linear order) */
|
jbe@0
|
305 static int pgl_circle_cmp(pgl_circle *circle1, pgl_circle *circle2) {
|
jbe@0
|
306 /* two circles with same infinite radius (positive or negative infinity) are
|
jbe@0
|
307 considered equal independently of center point */
|
jbe@0
|
308 if (
|
jbe@0
|
309 !isfinite(circle1->radius) && !isfinite(circle2->radius) &&
|
jbe@0
|
310 circle1->radius == circle2->radius
|
jbe@0
|
311 ) return 0;
|
jbe@0
|
312 /* use radius as first ordering criterion */
|
jbe@0
|
313 if (circle1->radius < circle2->radius) return -1;
|
jbe@0
|
314 if (circle1->radius > circle2->radius) return 1;
|
jbe@0
|
315 /* use center point as secondary ordering criterion */
|
jbe@0
|
316 return pgl_point_cmp(&(circle1->center), &(circle2->center));
|
jbe@0
|
317 }
|
jbe@0
|
318
|
jbe@0
|
319 /* set box to empty box*/
|
jbe@0
|
320 static void pgl_box_set_empty(pgl_box *box) {
|
jbe@0
|
321 box->lat_min = INFINITY;
|
jbe@0
|
322 box->lat_max = -INFINITY;
|
jbe@0
|
323 box->lon_min = 0;
|
jbe@0
|
324 box->lon_max = 0;
|
jbe@0
|
325 }
|
jbe@0
|
326
|
jbe@0
|
327 /* check if point is inside a box */
|
jbe@0
|
328 static bool pgl_point_in_box(pgl_point *point, pgl_box *box) {
|
jbe@0
|
329 return (
|
jbe@0
|
330 point->lat >= box->lat_min && point->lat <= box->lat_max && (
|
jbe@0
|
331 (box->lon_min > box->lon_max) ? (
|
jbe@0
|
332 /* box crosses 180th meridian */
|
jbe@0
|
333 point->lon >= box->lon_min || point->lon <= box->lon_max
|
jbe@0
|
334 ) : (
|
jbe@0
|
335 /* box does not cross the 180th meridian */
|
jbe@0
|
336 point->lon >= box->lon_min && point->lon <= box->lon_max
|
jbe@0
|
337 )
|
jbe@0
|
338 )
|
jbe@0
|
339 );
|
jbe@0
|
340 }
|
jbe@0
|
341
|
jbe@0
|
342 /* check if two boxes overlap */
|
jbe@0
|
343 static bool pgl_boxes_overlap(pgl_box *box1, pgl_box *box2) {
|
jbe@0
|
344 return (
|
jbe@0
|
345 box2->lat_max >= box2->lat_min && /* ensure box2 is not empty */
|
jbe@0
|
346 ( box2->lat_min >= box1->lat_min || box2->lat_max >= box1->lat_min ) &&
|
jbe@0
|
347 ( box2->lat_min <= box1->lat_max || box2->lat_max <= box1->lat_max ) && (
|
jbe@0
|
348 (
|
jbe@0
|
349 /* check if one and only one box crosses the 180th meridian */
|
jbe@0
|
350 ((box1->lon_min > box1->lon_max) ? 1 : 0) ^
|
jbe@0
|
351 ((box2->lon_min > box2->lon_max) ? 1 : 0)
|
jbe@0
|
352 ) ? (
|
jbe@0
|
353 /* exactly one box crosses the 180th meridian */
|
jbe@0
|
354 box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min ||
|
jbe@0
|
355 box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max
|
jbe@0
|
356 ) : (
|
jbe@0
|
357 /* no box or both boxes cross the 180th meridian */
|
jbe@0
|
358 (
|
jbe@0
|
359 (box2->lon_min >= box1->lon_min || box2->lon_max >= box1->lon_min) &&
|
jbe@0
|
360 (box2->lon_min <= box1->lon_max || box2->lon_max <= box1->lon_max)
|
jbe@0
|
361 ) ||
|
jbe@0
|
362 /* handle W180 == E180 */
|
jbe@0
|
363 ( box1->lon_min == -180 && box2->lon_max == 180 ) ||
|
jbe@0
|
364 ( box2->lon_min == -180 && box1->lon_max == 180 )
|
jbe@0
|
365 )
|
jbe@0
|
366 )
|
jbe@0
|
367 );
|
jbe@0
|
368 }
|
jbe@0
|
369
|
jbe@0
|
370 /* check unambiguousness of east/west orientation of cluster entries and set
|
jbe@0
|
371 bounding circle of cluster */
|
jbe@0
|
372 static bool pgl_finalize_cluster(pgl_cluster *cluster) {
|
jbe@0
|
373 int i, j; /* i: index of entry, j: index of point in entry */
|
jbe@0
|
374 int npoints; /* number of points in entry */
|
jbe@0
|
375 int total_npoints = 0; /* total number of points in cluster */
|
jbe@0
|
376 pgl_point *points; /* points in entry */
|
jbe@0
|
377 int lon_dir; /* first point of entry west (-1) or east (+1) */
|
jbe@0
|
378 double lon_break = 0; /* antipodal longitude of first point in entry */
|
jbe@0
|
379 double lon_min, lon_max; /* covered longitude range of entry */
|
jbe@0
|
380 double value; /* temporary variable */
|
jbe@0
|
381 /* reset bounding circle center to empty circle at 0/0 coordinates */
|
jbe@0
|
382 cluster->bounding.center.lat = 0;
|
jbe@0
|
383 cluster->bounding.center.lon = 0;
|
jbe@0
|
384 cluster->bounding.radius = -INFINITY;
|
jbe@0
|
385 /* if cluster is not empty */
|
jbe@0
|
386 if (cluster->nentries != 0) {
|
jbe@0
|
387 /* iterate over all cluster entries and ensure they each cover a longitude
|
jbe@0
|
388 range less than 180 degrees */
|
jbe@0
|
389 for (i=0; i<cluster->nentries; i++) {
|
jbe@0
|
390 /* get properties of entry */
|
jbe@0
|
391 npoints = cluster->entries[i].npoints;
|
jbe@0
|
392 points = PGL_ENTRY_POINTS(cluster, i);
|
jbe@0
|
393 /* get longitude of first point of entry */
|
jbe@0
|
394 value = points[0].lon;
|
jbe@0
|
395 /* initialize lon_min and lon_max with longitude of first point */
|
jbe@0
|
396 lon_min = value;
|
jbe@0
|
397 lon_max = value;
|
jbe@0
|
398 /* determine east/west orientation of first point and calculate antipodal
|
jbe@0
|
399 longitude (Note: rounding required here) */
|
jbe@0
|
400 if (value < 0) { lon_dir = -1; lon_break = pgl_round(value + 180); }
|
jbe@0
|
401 else if (value > 0) { lon_dir = 1; lon_break = pgl_round(value - 180); }
|
jbe@0
|
402 else lon_dir = 0;
|
jbe@0
|
403 /* iterate over all other points in entry */
|
jbe@0
|
404 for (j=1; j<npoints; j++) {
|
jbe@0
|
405 /* consider longitude wrap-around */
|
jbe@0
|
406 value = points[j].lon;
|
jbe@0
|
407 if (lon_dir<0 && value>lon_break) value = pgl_round(value - 360);
|
jbe@0
|
408 else if (lon_dir>0 && value<lon_break) value = pgl_round(value + 360);
|
jbe@0
|
409 /* update lon_min and lon_max */
|
jbe@0
|
410 if (value < lon_min) lon_min = value;
|
jbe@0
|
411 else if (value > lon_max) lon_max = value;
|
jbe@0
|
412 /* return false if 180 degrees or more are covered */
|
jbe@0
|
413 if (lon_max - lon_min >= 180) return false;
|
jbe@0
|
414 }
|
jbe@0
|
415 }
|
jbe@0
|
416 /* iterate over all points of all entries and calculate arbitrary center
|
jbe@0
|
417 point for bounding circle (best if center point minimizes the radius,
|
jbe@0
|
418 but some error is allowed here) */
|
jbe@0
|
419 for (i=0; i<cluster->nentries; i++) {
|
jbe@0
|
420 /* get properties of entry */
|
jbe@0
|
421 npoints = cluster->entries[i].npoints;
|
jbe@0
|
422 points = PGL_ENTRY_POINTS(cluster, i);
|
jbe@0
|
423 /* check if first entry */
|
jbe@0
|
424 if (i==0) {
|
jbe@0
|
425 /* get longitude of first point of first entry in whole cluster */
|
jbe@0
|
426 value = points[0].lon;
|
jbe@0
|
427 /* initialize lon_min and lon_max with longitude of first point of
|
jbe@0
|
428 first entry in whole cluster (used to determine if whole cluster
|
jbe@0
|
429 covers a longitude range of 180 degrees or more) */
|
jbe@0
|
430 lon_min = value;
|
jbe@0
|
431 lon_max = value;
|
jbe@0
|
432 /* determine east/west orientation of first point and calculate
|
jbe@0
|
433 antipodal longitude (Note: rounding not necessary here) */
|
jbe@0
|
434 if (value < 0) { lon_dir = -1; lon_break = value + 180; }
|
jbe@0
|
435 else if (value > 0) { lon_dir = 1; lon_break = value - 180; }
|
jbe@0
|
436 else lon_dir = 0;
|
jbe@0
|
437 }
|
jbe@0
|
438 /* iterate over all points in entry */
|
jbe@0
|
439 for (j=0; j<npoints; j++) {
|
jbe@0
|
440 /* longitude wrap-around (Note: rounding not necessary here) */
|
jbe@0
|
441 value = points[j].lon;
|
jbe@0
|
442 if (lon_dir < 0 && value > lon_break) value -= 360;
|
jbe@0
|
443 else if (lon_dir > 0 && value < lon_break) value += 360;
|
jbe@0
|
444 if (value < lon_min) lon_min = value;
|
jbe@0
|
445 else if (value > lon_max) lon_max = value;
|
jbe@0
|
446 /* set bounding circle to cover whole earth if more than 180 degrees
|
jbe@0
|
447 are covered */
|
jbe@0
|
448 if (lon_max - lon_min >= 180) {
|
jbe@0
|
449 cluster->bounding.center.lat = 0;
|
jbe@0
|
450 cluster->bounding.center.lon = 0;
|
jbe@0
|
451 cluster->bounding.radius = INFINITY;
|
jbe@0
|
452 return true;
|
jbe@0
|
453 }
|
jbe@0
|
454 /* add point to bounding circle center (for average calculation) */
|
jbe@0
|
455 cluster->bounding.center.lat += points[j].lat;
|
jbe@0
|
456 cluster->bounding.center.lon += value;
|
jbe@0
|
457 }
|
jbe@0
|
458 /* count total number of points */
|
jbe@0
|
459 total_npoints += npoints;
|
jbe@0
|
460 }
|
jbe@0
|
461 /* determine average latitude and longitude of cluster */
|
jbe@0
|
462 cluster->bounding.center.lat /= total_npoints;
|
jbe@0
|
463 cluster->bounding.center.lon /= total_npoints;
|
jbe@0
|
464 /* normalize longitude of center of cluster bounding circle */
|
jbe@0
|
465 if (cluster->bounding.center.lon < -180) {
|
jbe@0
|
466 cluster->bounding.center.lon += 360;
|
jbe@0
|
467 }
|
jbe@0
|
468 else if (cluster->bounding.center.lon > 180) {
|
jbe@0
|
469 cluster->bounding.center.lon -= 360;
|
jbe@0
|
470 }
|
jbe@0
|
471 /* round bounding circle center (useful if it is used by other functions) */
|
jbe@0
|
472 cluster->bounding.center.lat = pgl_round(cluster->bounding.center.lat);
|
jbe@0
|
473 cluster->bounding.center.lon = pgl_round(cluster->bounding.center.lon);
|
jbe@0
|
474 /* calculate radius of bounding circle */
|
jbe@0
|
475 for (i=0; i<cluster->nentries; i++) {
|
jbe@0
|
476 npoints = cluster->entries[i].npoints;
|
jbe@0
|
477 points = PGL_ENTRY_POINTS(cluster, i);
|
jbe@0
|
478 for (j=0; j<npoints; j++) {
|
jbe@0
|
479 value = pgl_distance(
|
jbe@0
|
480 cluster->bounding.center.lat, cluster->bounding.center.lon,
|
jbe@0
|
481 points[j].lat, points[j].lon
|
jbe@0
|
482 );
|
jbe@0
|
483 if (value > cluster->bounding.radius) cluster->bounding.radius = value;
|
jbe@0
|
484 }
|
jbe@0
|
485 }
|
jbe@0
|
486 }
|
jbe@0
|
487 /* return true (east/west orientation is unambiguous) */
|
jbe@0
|
488 return true;
|
jbe@0
|
489 }
|
jbe@0
|
490
|
jbe@0
|
491 /* check if point is inside cluster */
|
jbe@20
|
492 /* (if point is on perimeter, then true is returned if and only if
|
jbe@20
|
493 strict == false) */
|
jbe@20
|
494 static bool pgl_point_in_cluster(
|
jbe@20
|
495 pgl_point *point,
|
jbe@20
|
496 pgl_cluster *cluster,
|
jbe@20
|
497 bool strict
|
jbe@20
|
498 ) {
|
jbe@0
|
499 int i, j, k; /* i: entry, j: point in entry, k: next point in entry */
|
jbe@0
|
500 int entrytype; /* type of entry */
|
jbe@0
|
501 int npoints; /* number of points in entry */
|
jbe@0
|
502 pgl_point *points; /* array of points in entry */
|
jbe@0
|
503 int lon_dir = 0; /* first vertex west (-1) or east (+1) */
|
jbe@0
|
504 double lon_break = 0; /* antipodal longitude of first vertex */
|
jbe@0
|
505 double lat0 = point->lat; /* latitude of point */
|
jbe@0
|
506 double lon0; /* (adjusted) longitude of point */
|
jbe@0
|
507 double lat1, lon1; /* latitude and (adjusted) longitude of vertex */
|
jbe@0
|
508 double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */
|
jbe@0
|
509 double lon; /* longitude of intersection */
|
jbe@0
|
510 int counter = 0; /* counter for intersections east of point */
|
jbe@0
|
511 /* iterate over all entries */
|
jbe@0
|
512 for (i=0; i<cluster->nentries; i++) {
|
jbe@20
|
513 /* get type of entry */
|
jbe@0
|
514 entrytype = cluster->entries[i].entrytype;
|
jbe@20
|
515 /* skip all entries but polygons if perimeters are excluded */
|
jbe@20
|
516 if (strict && entrytype != PGL_ENTRY_POLYGON) continue;
|
jbe@20
|
517 /* get points of entry */
|
jbe@0
|
518 npoints = cluster->entries[i].npoints;
|
jbe@0
|
519 points = PGL_ENTRY_POINTS(cluster, i);
|
jbe@0
|
520 /* determine east/west orientation of first point of entry and calculate
|
jbe@0
|
521 antipodal longitude */
|
jbe@0
|
522 lon_break = points[0].lon;
|
jbe@0
|
523 if (lon_break < 0) { lon_dir = -1; lon_break += 180; }
|
jbe@0
|
524 else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; }
|
jbe@0
|
525 else lon_dir = 0;
|
jbe@0
|
526 /* get longitude of point */
|
jbe@0
|
527 lon0 = point->lon;
|
jbe@0
|
528 /* consider longitude wrap-around for point */
|
jbe@0
|
529 if (lon_dir < 0 && lon0 > lon_break) lon0 = pgl_round(lon0 - 360);
|
jbe@0
|
530 else if (lon_dir > 0 && lon0 < lon_break) lon0 = pgl_round(lon0 + 360);
|
jbe@0
|
531 /* iterate over all edges and vertices */
|
jbe@0
|
532 for (j=0; j<npoints; j++) {
|
jbe@20
|
533 /* return if point is on vertex of polygon */
|
jbe@20
|
534 if (pgl_point_cmp(point, &(points[j])) == 0) return !strict;
|
jbe@0
|
535 /* calculate index of next vertex */
|
jbe@0
|
536 k = (j+1) % npoints;
|
jbe@0
|
537 /* skip last edge unless entry is (closed) outline or polygon */
|
jbe@0
|
538 if (
|
jbe@0
|
539 k == 0 &&
|
jbe@0
|
540 entrytype != PGL_ENTRY_OUTLINE &&
|
jbe@0
|
541 entrytype != PGL_ENTRY_POLYGON
|
jbe@0
|
542 ) continue;
|
jbe@16
|
543 /* use previously calculated values for lat1 and lon1 if possible */
|
jbe@16
|
544 if (j) {
|
jbe@16
|
545 lat1 = lat2;
|
jbe@16
|
546 lon1 = lon2;
|
jbe@16
|
547 } else {
|
jbe@16
|
548 /* otherwise get latitude and longitude values of first vertex */
|
jbe@16
|
549 lat1 = points[0].lat;
|
jbe@16
|
550 lon1 = points[0].lon;
|
jbe@16
|
551 /* and consider longitude wrap-around for first vertex */
|
jbe@16
|
552 if (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
|
jbe@16
|
553 else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
|
jbe@16
|
554 }
|
jbe@16
|
555 /* get latitude and longitude of next vertex */
|
jbe@0
|
556 lat2 = points[k].lat;
|
jbe@0
|
557 lon2 = points[k].lon;
|
jbe@16
|
558 /* consider longitude wrap-around for next vertex */
|
jbe@0
|
559 if (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
|
jbe@0
|
560 else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
|
jbe@20
|
561 /* return if point is on horizontal (west to east) edge of polygon */
|
jbe@0
|
562 if (
|
jbe@0
|
563 lat0 == lat1 && lat0 == lat2 &&
|
jbe@0
|
564 ( (lon0 >= lon1 && lon0 <= lon2) || (lon0 >= lon2 && lon0 <= lon1) )
|
jbe@20
|
565 ) return !strict;
|
jbe@0
|
566 /* check if edge crosses east/west line of point */
|
jbe@0
|
567 if ((lat1 < lat0 && lat2 >= lat0) || (lat2 < lat0 && lat1 >= lat0)) {
|
jbe@0
|
568 /* calculate longitude of intersection */
|
jbe@0
|
569 lon = (lon1 * (lat2-lat0) + lon2 * (lat0-lat1)) / (lat2-lat1);
|
jbe@20
|
570 /* return if intersection goes (approximately) through point */
|
jbe@20
|
571 if (pgl_round(lon) == lon0) return !strict;
|
jbe@0
|
572 /* count intersection if east of point and entry is polygon*/
|
jbe@0
|
573 if (entrytype == PGL_ENTRY_POLYGON && lon > lon0) counter++;
|
jbe@0
|
574 }
|
jbe@0
|
575 }
|
jbe@0
|
576 }
|
jbe@0
|
577 /* return true if number of intersections is odd */
|
jbe@0
|
578 return counter & 1;
|
jbe@0
|
579 }
|
jbe@0
|
580
|
jbe@20
|
581 /* check if all points of the second cluster are strictly inside the first
|
jbe@20
|
582 cluster */
|
jbe@20
|
583 static inline bool pgl_all_cluster_points_strictly_in_cluster(
|
jbe@16
|
584 pgl_cluster *outer, pgl_cluster *inner
|
jbe@16
|
585 ) {
|
jbe@16
|
586 int i, j; /* i: entry, j: point in entry */
|
jbe@16
|
587 int npoints; /* number of points in entry */
|
jbe@16
|
588 pgl_point *points; /* array of points in entry */
|
jbe@16
|
589 /* iterate over all entries of "inner" cluster */
|
jbe@16
|
590 for (i=0; i<inner->nentries; i++) {
|
jbe@16
|
591 /* get properties of entry */
|
jbe@16
|
592 npoints = inner->entries[i].npoints;
|
jbe@16
|
593 points = PGL_ENTRY_POINTS(inner, i);
|
jbe@16
|
594 /* iterate over all points in entry of "inner" cluster */
|
jbe@16
|
595 for (j=0; j<npoints; j++) {
|
jbe@16
|
596 /* return false if one point of inner cluster is not in outer cluster */
|
jbe@20
|
597 if (!pgl_point_in_cluster(points+j, outer, true)) return false;
|
jbe@16
|
598 }
|
jbe@16
|
599 }
|
jbe@16
|
600 /* otherwise return true */
|
jbe@16
|
601 return true;
|
jbe@16
|
602 }
|
jbe@16
|
603
|
jbe@16
|
604 /* check if any point the second cluster is inside the first cluster */
|
jbe@16
|
605 static inline bool pgl_any_cluster_points_in_cluster(
|
jbe@16
|
606 pgl_cluster *outer, pgl_cluster *inner
|
jbe@16
|
607 ) {
|
jbe@16
|
608 int i, j; /* i: entry, j: point in entry */
|
jbe@16
|
609 int npoints; /* number of points in entry */
|
jbe@16
|
610 pgl_point *points; /* array of points in entry */
|
jbe@16
|
611 /* iterate over all entries of "inner" cluster */
|
jbe@16
|
612 for (i=0; i<inner->nentries; i++) {
|
jbe@16
|
613 /* get properties of entry */
|
jbe@16
|
614 npoints = inner->entries[i].npoints;
|
jbe@16
|
615 points = PGL_ENTRY_POINTS(inner, i);
|
jbe@16
|
616 /* iterate over all points in entry of "inner" cluster */
|
jbe@16
|
617 for (j=0; j<npoints; j++) {
|
jbe@16
|
618 /* return true if one point of inner cluster is in outer cluster */
|
jbe@20
|
619 if (pgl_point_in_cluster(points+j, outer, false)) return true;
|
jbe@16
|
620 }
|
jbe@16
|
621 }
|
jbe@16
|
622 /* otherwise return false */
|
jbe@16
|
623 return false;
|
jbe@16
|
624 }
|
jbe@16
|
625
|
jbe@20
|
626 /* check if line segment strictly crosses line (not just touching) */
|
jbe@20
|
627 static inline bool pgl_lseg_crosses_line(
|
jbe@16
|
628 double seg_x1, double seg_y1, double seg_x2, double seg_y2,
|
jbe@20
|
629 double line_x1, double line_y1, double line_x2, double line_y2
|
jbe@16
|
630 ) {
|
jbe@20
|
631 return (
|
jbe@20
|
632 (
|
jbe@20
|
633 (seg_x1-line_x1) * (line_y2-line_y1) -
|
jbe@20
|
634 (seg_y1-line_y1) * (line_x2-line_x1)
|
jbe@20
|
635 ) * (
|
jbe@20
|
636 (seg_x2-line_x1) * (line_y2-line_y1) -
|
jbe@20
|
637 (seg_y2-line_y1) * (line_x2-line_x1)
|
jbe@20
|
638 )
|
jbe@20
|
639 ) < 0;
|
jbe@16
|
640 }
|
jbe@16
|
641
|
jbe@20
|
642 /* check if paths and outlines of two clusters strictly overlap (not just
|
jbe@20
|
643 touching) */
|
jbe@16
|
644 static bool pgl_outlines_overlap(
|
jbe@20
|
645 pgl_cluster *cluster1, pgl_cluster *cluster2
|
jbe@16
|
646 ) {
|
jbe@16
|
647 int i1, j1, k1; /* i: entry, j: point in entry, k: next point in entry */
|
jbe@16
|
648 int i2, j2, k2;
|
jbe@16
|
649 int entrytype1, entrytype2; /* type of entry */
|
jbe@16
|
650 int npoints1, npoints2; /* number of points in entry */
|
jbe@16
|
651 pgl_point *points1; /* array of points in entry of cluster1 */
|
jbe@16
|
652 pgl_point *points2; /* array of points in entry of cluster2 */
|
jbe@16
|
653 int lon_dir1, lon_dir2; /* first vertex west (-1) or east (+1) */
|
jbe@16
|
654 double lon_break1, lon_break2; /* antipodal longitude of first vertex */
|
jbe@16
|
655 double lat11, lon11; /* latitude and (adjusted) longitude of vertex */
|
jbe@16
|
656 double lat12, lon12; /* latitude and (adjusted) longitude of next vertex */
|
jbe@16
|
657 double lat21, lon21; /* latitude and (adjusted) longitudes for cluster2 */
|
jbe@16
|
658 double lat22, lon22;
|
jbe@16
|
659 double wrapvalue; /* temporary helper value to adjust wrap-around */
|
jbe@16
|
660 /* iterate over all entries of cluster1 */
|
jbe@16
|
661 for (i1=0; i1<cluster1->nentries; i1++) {
|
jbe@16
|
662 /* get properties of entry in cluster1 and skip points */
|
jbe@16
|
663 npoints1 = cluster1->entries[i1].npoints;
|
jbe@16
|
664 if (npoints1 < 2) continue;
|
jbe@16
|
665 entrytype1 = cluster1->entries[i1].entrytype;
|
jbe@16
|
666 points1 = PGL_ENTRY_POINTS(cluster1, i1);
|
jbe@16
|
667 /* determine east/west orientation of first point and calculate antipodal
|
jbe@16
|
668 longitude */
|
jbe@16
|
669 lon_break1 = points1[0].lon;
|
jbe@16
|
670 if (lon_break1 < 0) {
|
jbe@16
|
671 lon_dir1 = -1;
|
jbe@16
|
672 lon_break1 = pgl_round(lon_break1 + 180);
|
jbe@16
|
673 } else if (lon_break1 > 0) {
|
jbe@16
|
674 lon_dir1 = 1;
|
jbe@16
|
675 lon_break1 = pgl_round(lon_break1 - 180);
|
jbe@16
|
676 } else lon_dir1 = 0;
|
jbe@16
|
677 /* iterate over all edges and vertices in cluster1 */
|
jbe@16
|
678 for (j1=0; j1<npoints1; j1++) {
|
jbe@16
|
679 /* calculate index of next vertex */
|
jbe@16
|
680 k1 = (j1+1) % npoints1;
|
jbe@16
|
681 /* skip last edge unless entry is (closed) outline or polygon */
|
jbe@16
|
682 if (
|
jbe@16
|
683 k1 == 0 &&
|
jbe@16
|
684 entrytype1 != PGL_ENTRY_OUTLINE &&
|
jbe@16
|
685 entrytype1 != PGL_ENTRY_POLYGON
|
jbe@16
|
686 ) continue;
|
jbe@16
|
687 /* use previously calculated values for lat1 and lon1 if possible */
|
jbe@16
|
688 if (j1) {
|
jbe@16
|
689 lat11 = lat12;
|
jbe@16
|
690 lon11 = lon12;
|
jbe@16
|
691 } else {
|
jbe@16
|
692 /* otherwise get latitude and longitude values of first vertex */
|
jbe@16
|
693 lat11 = points1[0].lat;
|
jbe@16
|
694 lon11 = points1[0].lon;
|
jbe@16
|
695 /* and consider longitude wrap-around for first vertex */
|
jbe@16
|
696 if (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360);
|
jbe@16
|
697 else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360);
|
jbe@16
|
698 }
|
jbe@16
|
699 /* get latitude and longitude of next vertex */
|
jbe@16
|
700 lat12 = points1[k1].lat;
|
jbe@16
|
701 lon12 = points1[k1].lon;
|
jbe@16
|
702 /* consider longitude wrap-around for next vertex */
|
jbe@16
|
703 if (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360);
|
jbe@16
|
704 else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360);
|
jbe@16
|
705 /* skip degenerated edges */
|
jbe@16
|
706 if (lat11 == lat12 && lon11 == lon12) continue;
|
jbe@16
|
707 /* iterate over all entries of cluster2 */
|
jbe@16
|
708 for (i2=0; i2<cluster2->nentries; i2++) {
|
jbe@16
|
709 /* get points and number of points of entry in cluster2 */
|
jbe@16
|
710 npoints2 = cluster2->entries[i2].npoints;
|
jbe@16
|
711 if (npoints2 < 2) continue;
|
jbe@16
|
712 entrytype2 = cluster2->entries[i2].entrytype;
|
jbe@16
|
713 points2 = PGL_ENTRY_POINTS(cluster2, i2);
|
jbe@16
|
714 /* determine east/west orientation of first point and calculate antipodal
|
jbe@16
|
715 longitude */
|
jbe@16
|
716 lon_break2 = points2[0].lon;
|
jbe@16
|
717 if (lon_break2 < 0) {
|
jbe@16
|
718 lon_dir2 = -1;
|
jbe@16
|
719 lon_break2 = pgl_round(lon_break2 + 180);
|
jbe@16
|
720 } else if (lon_break2 > 0) {
|
jbe@16
|
721 lon_dir2 = 1;
|
jbe@16
|
722 lon_break2 = pgl_round(lon_break2 - 180);
|
jbe@16
|
723 } else lon_dir2 = 0;
|
jbe@16
|
724 /* iterate over all edges and vertices in cluster2 */
|
jbe@16
|
725 for (j2=0; j2<npoints2; j2++) {
|
jbe@16
|
726 /* calculate index of next vertex */
|
jbe@16
|
727 k2 = (j2+1) % npoints2;
|
jbe@16
|
728 /* skip last edge unless entry is (closed) outline or polygon */
|
jbe@16
|
729 if (
|
jbe@16
|
730 k2 == 0 &&
|
jbe@16
|
731 entrytype2 != PGL_ENTRY_OUTLINE &&
|
jbe@16
|
732 entrytype2 != PGL_ENTRY_POLYGON
|
jbe@16
|
733 ) continue;
|
jbe@16
|
734 /* use previously calculated values for lat1 and lon1 if possible */
|
jbe@16
|
735 if (j2) {
|
jbe@16
|
736 lat21 = lat22;
|
jbe@16
|
737 lon21 = lon22;
|
jbe@16
|
738 } else {
|
jbe@16
|
739 /* otherwise get latitude and longitude values of first vertex */
|
jbe@16
|
740 lat21 = points2[0].lat;
|
jbe@16
|
741 lon21 = points2[0].lon;
|
jbe@16
|
742 /* and consider longitude wrap-around for first vertex */
|
jbe@16
|
743 if (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360);
|
jbe@16
|
744 else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360);
|
jbe@16
|
745 }
|
jbe@16
|
746 /* get latitude and longitude of next vertex */
|
jbe@16
|
747 lat22 = points2[k2].lat;
|
jbe@16
|
748 lon22 = points2[k2].lon;
|
jbe@16
|
749 /* consider longitude wrap-around for next vertex */
|
jbe@16
|
750 if (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360);
|
jbe@16
|
751 else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360);
|
jbe@16
|
752 /* skip degenerated edges */
|
jbe@16
|
753 if (lat21 == lat22 && lon21 == lon22) continue;
|
jbe@16
|
754 /* perform another wrap-around where necessary */
|
jbe@16
|
755 /* TODO: improve performance of whole wrap-around mechanism */
|
jbe@16
|
756 wrapvalue = (lon21 + lon22) - (lon11 + lon12);
|
jbe@16
|
757 if (wrapvalue > 360) {
|
jbe@16
|
758 lon21 = pgl_round(lon21 - 360);
|
jbe@16
|
759 lon22 = pgl_round(lon22 - 360);
|
jbe@16
|
760 } else if (wrapvalue < -360) {
|
jbe@16
|
761 lon21 = pgl_round(lon21 + 360);
|
jbe@16
|
762 lon22 = pgl_round(lon22 + 360);
|
jbe@16
|
763 }
|
jbe@16
|
764 /* return true if segments overlap */
|
jbe@16
|
765 if (
|
jbe@16
|
766 pgl_lseg_crosses_line(
|
jbe@16
|
767 lat11, lon11, lat12, lon12,
|
jbe@20
|
768 lat21, lon21, lat22, lon22
|
jbe@16
|
769 ) && pgl_lseg_crosses_line(
|
jbe@16
|
770 lat21, lon21, lat22, lon22,
|
jbe@20
|
771 lat11, lon11, lat12, lon12
|
jbe@16
|
772 )
|
jbe@16
|
773 ) {
|
jbe@16
|
774 return true;
|
jbe@16
|
775 }
|
jbe@16
|
776 }
|
jbe@16
|
777 }
|
jbe@16
|
778 }
|
jbe@16
|
779 }
|
jbe@16
|
780 /* otherwise return false */
|
jbe@16
|
781 return false;
|
jbe@16
|
782 }
|
jbe@16
|
783
|
jbe@16
|
784 /* check if second cluster is completely contained in first cluster */
|
jbe@16
|
785 static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) {
|
jbe@20
|
786 if (!pgl_all_cluster_points_strictly_in_cluster(outer, inner)) return false;
|
jbe@20
|
787 if (pgl_any_cluster_points_in_cluster(inner, outer)) return false;
|
jbe@20
|
788 if (pgl_outlines_overlap(outer, inner)) return false;
|
jbe@16
|
789 return true;
|
jbe@16
|
790 }
|
jbe@16
|
791
|
jbe@16
|
792 /* check if two clusters overlap */
|
jbe@16
|
793 static bool pgl_clusters_overlap(
|
jbe@16
|
794 pgl_cluster *cluster1, pgl_cluster *cluster2
|
jbe@16
|
795 ) {
|
jbe@16
|
796 if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true;
|
jbe@16
|
797 if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true;
|
jbe@20
|
798 if (pgl_outlines_overlap(cluster1, cluster2)) return true;
|
jbe@16
|
799 return false;
|
jbe@16
|
800 }
|
jbe@16
|
801
|
jbe@16
|
802
|
jbe@0
|
803 /* calculate (approximate) distance between point and cluster */
|
jbe@0
|
804 static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
|
jbe@0
|
805 int i, j, k; /* i: entry, j: point in entry, k: next point in entry */
|
jbe@0
|
806 int entrytype; /* type of entry */
|
jbe@0
|
807 int npoints; /* number of points in entry */
|
jbe@0
|
808 pgl_point *points; /* array of points in entry */
|
jbe@0
|
809 int lon_dir = 0; /* first vertex west (-1) or east (+1) */
|
jbe@0
|
810 double lon_break = 0; /* antipodal longitude of first vertex */
|
jbe@0
|
811 double lon_min = 0; /* minimum (adjusted) longitude of entry vertices */
|
jbe@0
|
812 double lon_max = 0; /* maximum (adjusted) longitude of entry vertices */
|
jbe@0
|
813 double lat0 = point->lat; /* latitude of point */
|
jbe@0
|
814 double lon0; /* (adjusted) longitude of point */
|
jbe@0
|
815 double lat1, lon1; /* latitude and (adjusted) longitude of vertex */
|
jbe@0
|
816 double lat2, lon2; /* latitude and (adjusted) longitude of next vertex */
|
jbe@0
|
817 double s; /* scalar for vector calculations */
|
jbe@0
|
818 double dist; /* distance calculated in one step */
|
jbe@0
|
819 double min_dist = INFINITY; /* minimum distance */
|
jbe@0
|
820 /* distance is zero if point is contained in cluster */
|
jbe@20
|
821 if (pgl_point_in_cluster(point, cluster, false)) return 0;
|
jbe@0
|
822 /* iterate over all entries */
|
jbe@0
|
823 for (i=0; i<cluster->nentries; i++) {
|
jbe@0
|
824 /* get properties of entry */
|
jbe@0
|
825 entrytype = cluster->entries[i].entrytype;
|
jbe@0
|
826 npoints = cluster->entries[i].npoints;
|
jbe@0
|
827 points = PGL_ENTRY_POINTS(cluster, i);
|
jbe@0
|
828 /* determine east/west orientation of first point of entry and calculate
|
jbe@0
|
829 antipodal longitude */
|
jbe@0
|
830 lon_break = points[0].lon;
|
jbe@0
|
831 if (lon_break < 0) { lon_dir = -1; lon_break += 180; }
|
jbe@0
|
832 else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; }
|
jbe@0
|
833 else lon_dir = 0;
|
jbe@0
|
834 /* determine covered longitude range */
|
jbe@0
|
835 for (j=0; j<npoints; j++) {
|
jbe@0
|
836 /* get longitude of vertex */
|
jbe@0
|
837 lon1 = points[j].lon;
|
jbe@0
|
838 /* adjust longitude to fix potential wrap-around */
|
jbe@0
|
839 if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
|
jbe@0
|
840 else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
|
jbe@0
|
841 /* update minimum and maximum longitude of polygon */
|
jbe@0
|
842 if (j == 0 || lon1 < lon_min) lon_min = lon1;
|
jbe@0
|
843 if (j == 0 || lon1 > lon_max) lon_max = lon1;
|
jbe@0
|
844 }
|
jbe@0
|
845 /* adjust longitude wrap-around according to full longitude range */
|
jbe@0
|
846 lon_break = (lon_max + lon_min) / 2;
|
jbe@0
|
847 if (lon_break < 0) { lon_dir = -1; lon_break += 180; }
|
jbe@0
|
848 else if (lon_break > 0) { lon_dir = 1; lon_break -= 180; }
|
jbe@0
|
849 /* get longitude of point */
|
jbe@0
|
850 lon0 = point->lon;
|
jbe@0
|
851 /* consider longitude wrap-around for point */
|
jbe@0
|
852 if (lon_dir < 0 && lon0 > lon_break) lon0 -= 360;
|
jbe@0
|
853 else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
|
jbe@0
|
854 /* iterate over all edges and vertices */
|
jbe@0
|
855 for (j=0; j<npoints; j++) {
|
jbe@16
|
856 /* use previously calculated values for lat1 and lon1 if possible */
|
jbe@16
|
857 if (j) {
|
jbe@16
|
858 lat1 = lat2;
|
jbe@16
|
859 lon1 = lon2;
|
jbe@16
|
860 } else {
|
jbe@16
|
861 /* otherwise get latitude and longitude values of first vertex */
|
jbe@16
|
862 lat1 = points[0].lat;
|
jbe@16
|
863 lon1 = points[0].lon;
|
jbe@16
|
864 /* and consider longitude wrap-around for first vertex */
|
jbe@16
|
865 if (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
|
jbe@16
|
866 else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
|
jbe@16
|
867 }
|
jbe@0
|
868 /* calculate distance to vertex */
|
jbe@0
|
869 dist = pgl_distance(lat0, lon0, lat1, lon1);
|
jbe@0
|
870 /* store calculated distance if smallest */
|
jbe@0
|
871 if (dist < min_dist) min_dist = dist;
|
jbe@0
|
872 /* calculate index of next vertex */
|
jbe@0
|
873 k = (j+1) % npoints;
|
jbe@0
|
874 /* skip last edge unless entry is (closed) outline or polygon */
|
jbe@0
|
875 if (
|
jbe@0
|
876 k == 0 &&
|
jbe@0
|
877 entrytype != PGL_ENTRY_OUTLINE &&
|
jbe@0
|
878 entrytype != PGL_ENTRY_POLYGON
|
jbe@0
|
879 ) continue;
|
jbe@16
|
880 /* get latitude and longitude of next vertex */
|
jbe@0
|
881 lat2 = points[k].lat;
|
jbe@0
|
882 lon2 = points[k].lon;
|
jbe@16
|
883 /* consider longitude wrap-around for next vertex */
|
jbe@0
|
884 if (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
|
jbe@0
|
885 else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
|
jbe@0
|
886 /* go to next vertex and edge if edge is degenerated */
|
jbe@0
|
887 if (lat1 == lat2 && lon1 == lon2) continue;
|
jbe@0
|
888 /* otherwise test if point can be projected onto edge of polygon */
|
jbe@0
|
889 s = (
|
jbe@0
|
890 ((lat0-lat1) * (lat2-lat1) + (lon0-lon1) * (lon2-lon1)) /
|
jbe@0
|
891 ((lat2-lat1) * (lat2-lat1) + (lon2-lon1) * (lon2-lon1))
|
jbe@0
|
892 );
|
jbe@0
|
893 /* go to next vertex and edge if point cannot be projected */
|
jbe@0
|
894 if (!(s > 0 && s < 1)) continue;
|
jbe@0
|
895 /* calculate distance from original point to projected point */
|
jbe@0
|
896 dist = pgl_distance(
|
jbe@0
|
897 lat0, lon0,
|
jbe@0
|
898 lat1 + s * (lat2-lat1),
|
jbe@0
|
899 lon1 + s * (lon2-lon1)
|
jbe@0
|
900 );
|
jbe@0
|
901 /* store calculated distance if smallest */
|
jbe@0
|
902 if (dist < min_dist) min_dist = dist;
|
jbe@0
|
903 }
|
jbe@0
|
904 }
|
jbe@0
|
905 /* return minimum distance */
|
jbe@0
|
906 return min_dist;
|
jbe@0
|
907 }
|
jbe@0
|
908
|
jbe@16
|
909 /* calculate (approximate) distance between two clusters */
|
jbe@16
|
910 static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) {
|
jbe@16
|
911 int i, j; /* i: entry, j: point in entry */
|
jbe@16
|
912 int npoints; /* number of points in entry */
|
jbe@16
|
913 pgl_point *points; /* array of points in entry */
|
jbe@16
|
914 double dist; /* distance calculated in one step */
|
jbe@16
|
915 double min_dist = INFINITY; /* minimum distance */
|
jbe@16
|
916 /* consider distance from each point in one cluster to the whole other */
|
jbe@16
|
917 for (i=0; i<cluster1->nentries; i++) {
|
jbe@16
|
918 npoints = cluster1->entries[i].npoints;
|
jbe@16
|
919 points = PGL_ENTRY_POINTS(cluster1, i);
|
jbe@16
|
920 for (j=0; j<npoints; j++) {
|
jbe@16
|
921 dist = pgl_point_cluster_distance(points+j, cluster2);
|
jbe@16
|
922 if (dist == 0) return dist;
|
jbe@16
|
923 if (dist < min_dist) min_dist = dist;
|
jbe@16
|
924 }
|
jbe@16
|
925 }
|
jbe@16
|
926 /* consider distance from each point in other cluster to the first cluster */
|
jbe@16
|
927 for (i=0; i<cluster2->nentries; i++) {
|
jbe@16
|
928 npoints = cluster2->entries[i].npoints;
|
jbe@16
|
929 points = PGL_ENTRY_POINTS(cluster2, i);
|
jbe@16
|
930 for (j=0; j<npoints; j++) {
|
jbe@16
|
931 dist = pgl_point_cluster_distance(points+j, cluster1);
|
jbe@16
|
932 if (dist == 0) return dist;
|
jbe@16
|
933 if (dist < min_dist) min_dist = dist;
|
jbe@16
|
934 }
|
jbe@16
|
935 }
|
jbe@16
|
936 return min_dist;
|
jbe@16
|
937 }
|
jbe@16
|
938
|
jbe@0
|
939 /* estimator function for distance between box and point */
|
jbe@16
|
940 /* always returns a smaller value than actually correct or zero */
|
jbe@0
|
941 static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
|
jbe@16
|
942 double dlon; /* longitude range of box (delta longitude) */
|
jbe@16
|
943 double distance; /* return value */
|
jbe@16
|
944 /* return infinity if box is empty */
|
jbe@0
|
945 if (box->lat_min > box->lat_max) return INFINITY;
|
jbe@16
|
946 /* return zero if point is inside box */
|
jbe@0
|
947 if (pgl_point_in_box(point, box)) return 0;
|
jbe@0
|
948 /* calculate delta longitude */
|
jbe@0
|
949 dlon = box->lon_max - box->lon_min;
|
jbe@0
|
950 if (dlon < 0) dlon += 360; /* 180th meridian crossed */
|
jbe@16
|
951 /* if delta longitude is greater than 150 degrees, perform safe fall-back */
|
jbe@16
|
952 if (dlon > 150) return 0;
|
jbe@16
|
953 /* calculate lower limit for distance (formula below requires dlon <= 150) */
|
jbe@16
|
954 /* TODO: provide better estimation function to improve performance */
|
jbe@16
|
955 distance = (
|
jbe@16
|
956 (1.0-1e-14) * pgl_distance(
|
jbe@16
|
957 point->lat,
|
jbe@16
|
958 point->lon,
|
jbe@16
|
959 (box->lat_min + box->lat_max) / 2,
|
jbe@16
|
960 box->lon_min + dlon/2
|
jbe@16
|
961 ) - pgl_distance(
|
jbe@16
|
962 box->lat_min, box->lon_min,
|
jbe@16
|
963 box->lat_max, box->lon_max
|
jbe@16
|
964 )
|
jbe@16
|
965 );
|
jbe@16
|
966 /* truncate negative results to zero */
|
jbe@16
|
967 if (distance <= 0) distance = 0;
|
jbe@16
|
968 /* return result */
|
jbe@16
|
969 return distance;
|
jbe@0
|
970 }
|
jbe@0
|
971
|
jbe@0
|
972
|
jbe@16
|
973 /*-------------------------------------------------*
|
jbe@16
|
974 * geographic index based on space-filling curve *
|
jbe@16
|
975 *-------------------------------------------------*/
|
jbe@0
|
976
|
jbe@0
|
977 /* number of bytes used for geographic (center) position in keys */
|
jbe@0
|
978 #define PGL_KEY_LATLON_BYTELEN 7
|
jbe@0
|
979
|
jbe@0
|
980 /* maximum reference value for logarithmic size of geographic objects */
|
jbe@0
|
981 #define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0) /* can be tweaked */
|
jbe@0
|
982
|
jbe@0
|
983 /* pointer to index key (either pgl_pointkey or pgl_areakey) */
|
jbe@0
|
984 typedef unsigned char *pgl_keyptr;
|
jbe@0
|
985
|
jbe@0
|
986 /* index key for points (objects with zero area) on the spheroid */
|
jbe@0
|
987 /* bit 0..55: interspersed bits of latitude and longitude,
|
jbe@0
|
988 bit 56..57: always zero,
|
jbe@0
|
989 bit 58..63: node depth in hypothetic (full) tree from 0 to 56 (incl.) */
|
jbe@0
|
990 typedef unsigned char pgl_pointkey[PGL_KEY_LATLON_BYTELEN+1];
|
jbe@0
|
991
|
jbe@0
|
992 /* index key for geographic objects on spheroid with area greater than zero */
|
jbe@0
|
993 /* bit 0..55: interspersed bits of latitude and longitude of center point,
|
jbe@0
|
994 bit 56: always set to 1,
|
jbe@0
|
995 bit 57..63: node depth in hypothetic (full) tree from 0 to (2*56)+1 (incl.),
|
jbe@0
|
996 bit 64..71: logarithmic object size from 0 to 56+1 = 57 (incl.), but set to
|
jbe@0
|
997 PGL_KEY_OBJSIZE_EMPTY (with interspersed bits = 0 and node depth
|
jbe@0
|
998 = 113) for empty objects, and set to PGL_KEY_OBJSIZE_UNIVERSAL
|
jbe@0
|
999 (with interspersed bits = 0 and node depth = 0) for keys which
|
jbe@0
|
1000 cover both empty and non-empty objects */
|
jbe@0
|
1001
|
jbe@0
|
1002 typedef unsigned char pgl_areakey[PGL_KEY_LATLON_BYTELEN+2];
|
jbe@0
|
1003
|
jbe@0
|
1004 /* helper macros for reading/writing index keys */
|
jbe@0
|
1005 #define PGL_KEY_NODEDEPTH_OFFSET PGL_KEY_LATLON_BYTELEN
|
jbe@0
|
1006 #define PGL_KEY_OBJSIZE_OFFSET (PGL_KEY_NODEDEPTH_OFFSET+1)
|
jbe@0
|
1007 #define PGL_POINTKEY_MAXDEPTH (PGL_KEY_LATLON_BYTELEN*8)
|
jbe@0
|
1008 #define PGL_AREAKEY_MAXDEPTH (2*PGL_POINTKEY_MAXDEPTH+1)
|
jbe@0
|
1009 #define PGL_AREAKEY_MAXOBJSIZE (PGL_POINTKEY_MAXDEPTH+1)
|
jbe@0
|
1010 #define PGL_AREAKEY_TYPEMASK 0x80
|
jbe@0
|
1011 #define PGL_KEY_LATLONBIT(key, n) ((key)[(n)/8] & (0x80 >> ((n)%8)))
|
jbe@0
|
1012 #define PGL_KEY_LATLONBIT_DIFF(key1, key2, n) \
|
jbe@0
|
1013 ( PGL_KEY_LATLONBIT(key1, n) ^ \
|
jbe@0
|
1014 PGL_KEY_LATLONBIT(key2, n) )
|
jbe@0
|
1015 #define PGL_KEY_IS_AREAKEY(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
|
jbe@0
|
1016 PGL_AREAKEY_TYPEMASK)
|
jbe@0
|
1017 #define PGL_KEY_NODEDEPTH(key) ((key)[PGL_KEY_NODEDEPTH_OFFSET] & \
|
jbe@0
|
1018 (PGL_AREAKEY_TYPEMASK-1))
|
jbe@0
|
1019 #define PGL_KEY_OBJSIZE(key) ((key)[PGL_KEY_OBJSIZE_OFFSET])
|
jbe@0
|
1020 #define PGL_KEY_OBJSIZE_EMPTY 126
|
jbe@0
|
1021 #define PGL_KEY_OBJSIZE_UNIVERSAL 127
|
jbe@0
|
1022 #define PGL_KEY_IS_EMPTY(key) ( PGL_KEY_IS_AREAKEY(key) && \
|
jbe@0
|
1023 (key)[PGL_KEY_OBJSIZE_OFFSET] == \
|
jbe@0
|
1024 PGL_KEY_OBJSIZE_EMPTY )
|
jbe@0
|
1025 #define PGL_KEY_IS_UNIVERSAL(key) ( PGL_KEY_IS_AREAKEY(key) && \
|
jbe@0
|
1026 (key)[PGL_KEY_OBJSIZE_OFFSET] == \
|
jbe@0
|
1027 PGL_KEY_OBJSIZE_UNIVERSAL )
|
jbe@0
|
1028
|
jbe@0
|
1029 /* set area key to match empty objects only */
|
jbe@0
|
1030 static void pgl_key_set_empty(pgl_keyptr key) {
|
jbe@0
|
1031 memset(key, 0, sizeof(pgl_areakey));
|
jbe@0
|
1032 /* Note: setting node depth to maximum is required for picksplit function */
|
jbe@0
|
1033 key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
|
jbe@0
|
1034 key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_EMPTY;
|
jbe@0
|
1035 }
|
jbe@0
|
1036
|
jbe@0
|
1037 /* set area key to match any object (including empty objects) */
|
jbe@0
|
1038 static void pgl_key_set_universal(pgl_keyptr key) {
|
jbe@0
|
1039 memset(key, 0, sizeof(pgl_areakey));
|
jbe@0
|
1040 key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK;
|
jbe@0
|
1041 key[PGL_KEY_OBJSIZE_OFFSET] = PGL_KEY_OBJSIZE_UNIVERSAL;
|
jbe@0
|
1042 }
|
jbe@0
|
1043
|
jbe@0
|
1044 /* convert a point on earth into a max-depth key to be used in index */
|
jbe@0
|
1045 static void pgl_point_to_key(pgl_point *point, pgl_keyptr key) {
|
jbe@0
|
1046 double lat = point->lat;
|
jbe@0
|
1047 double lon = point->lon;
|
jbe@0
|
1048 int i;
|
jbe@0
|
1049 /* clear latitude and longitude bits */
|
jbe@0
|
1050 memset(key, 0, PGL_KEY_LATLON_BYTELEN);
|
jbe@0
|
1051 /* set node depth to maximum and type bit to zero */
|
jbe@0
|
1052 key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_POINTKEY_MAXDEPTH;
|
jbe@0
|
1053 /* iterate over all latitude/longitude bit pairs */
|
jbe@0
|
1054 for (i=0; i<PGL_POINTKEY_MAXDEPTH/2; i++) {
|
jbe@0
|
1055 /* determine latitude bit */
|
jbe@0
|
1056 if (lat >= 0) {
|
jbe@0
|
1057 key[i/4] |= 0x80 >> (2*(i%4));
|
jbe@0
|
1058 lat *= 2; lat -= 90;
|
jbe@0
|
1059 } else {
|
jbe@0
|
1060 lat *= 2; lat += 90;
|
jbe@0
|
1061 }
|
jbe@0
|
1062 /* determine longitude bit */
|
jbe@0
|
1063 if (lon >= 0) {
|
jbe@0
|
1064 key[i/4] |= 0x80 >> (2*(i%4)+1);
|
jbe@0
|
1065 lon *= 2; lon -= 180;
|
jbe@0
|
1066 } else {
|
jbe@0
|
1067 lon *= 2; lon += 180;
|
jbe@0
|
1068 }
|
jbe@0
|
1069 }
|
jbe@0
|
1070 }
|
jbe@0
|
1071
|
jbe@0
|
1072 /* convert a circle on earth into a max-depth key to be used in an index */
|
jbe@0
|
1073 static void pgl_circle_to_key(pgl_circle *circle, pgl_keyptr key) {
|
jbe@0
|
1074 /* handle special case of empty circle */
|
jbe@0
|
1075 if (circle->radius < 0) {
|
jbe@0
|
1076 pgl_key_set_empty(key);
|
jbe@0
|
1077 return;
|
jbe@0
|
1078 }
|
jbe@0
|
1079 /* perform same action as for point keys */
|
jbe@0
|
1080 pgl_point_to_key(&(circle->center), key);
|
jbe@0
|
1081 /* but overwrite type and node depth to fit area index key */
|
jbe@0
|
1082 key[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | PGL_AREAKEY_MAXDEPTH;
|
jbe@0
|
1083 /* check if radius is greater than (or equal to) reference size */
|
jbe@0
|
1084 /* (treat equal values as greater values for numerical safety) */
|
jbe@0
|
1085 if (circle->radius >= PGL_AREAKEY_REFOBJSIZE) {
|
jbe@0
|
1086 /* if yes, set logarithmic size to zero */
|
jbe@0
|
1087 key[PGL_KEY_OBJSIZE_OFFSET] = 0;
|
jbe@0
|
1088 } else {
|
jbe@0
|
1089 /* otherwise, determine logarithmic size iteratively */
|
jbe@0
|
1090 /* (one step is equivalent to a factor of sqrt(2)) */
|
jbe@0
|
1091 double reference = PGL_AREAKEY_REFOBJSIZE / M_SQRT2;
|
jbe@0
|
1092 int objsize = 1;
|
jbe@0
|
1093 while (objsize < PGL_AREAKEY_MAXOBJSIZE) {
|
jbe@0
|
1094 /* stop when radius is greater than (or equal to) adjusted reference */
|
jbe@0
|
1095 /* (treat equal values as greater values for numerical safety) */
|
jbe@0
|
1096 if (circle->radius >= reference) break;
|
jbe@0
|
1097 reference /= M_SQRT2;
|
jbe@0
|
1098 objsize++;
|
jbe@0
|
1099 }
|
jbe@0
|
1100 /* set logarithmic size to determined value */
|
jbe@0
|
1101 key[PGL_KEY_OBJSIZE_OFFSET] = objsize;
|
jbe@0
|
1102 }
|
jbe@0
|
1103 }
|
jbe@0
|
1104
|
jbe@0
|
1105 /* check if one key is subkey of another key or vice versa */
|
jbe@0
|
1106 static bool pgl_keys_overlap(pgl_keyptr key1, pgl_keyptr key2) {
|
jbe@0
|
1107 int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */
|
jbe@0
|
1108 /* determine smallest depth */
|
jbe@0
|
1109 int depth1 = PGL_KEY_NODEDEPTH(key1);
|
jbe@0
|
1110 int depth2 = PGL_KEY_NODEDEPTH(key2);
|
jbe@0
|
1111 int depth = (depth1 < depth2) ? depth1 : depth2;
|
jbe@0
|
1112 /* check if keys are area keys (assuming that both keys have same type) */
|
jbe@0
|
1113 if (PGL_KEY_IS_AREAKEY(key1)) {
|
jbe@0
|
1114 int j = 0; /* bit offset for logarithmic object size bits */
|
jbe@0
|
1115 int k = 0; /* bit offset for latitude and longitude */
|
jbe@0
|
1116 /* fetch logarithmic object size information */
|
jbe@0
|
1117 int objsize1 = PGL_KEY_OBJSIZE(key1);
|
jbe@0
|
1118 int objsize2 = PGL_KEY_OBJSIZE(key2);
|
jbe@0
|
1119 /* handle special cases for empty objects (universal and empty keys) */
|
jbe@0
|
1120 if (
|
jbe@0
|
1121 objsize1 == PGL_KEY_OBJSIZE_UNIVERSAL ||
|
jbe@0
|
1122 objsize2 == PGL_KEY_OBJSIZE_UNIVERSAL
|
jbe@0
|
1123 ) return true;
|
jbe@0
|
1124 if (
|
jbe@0
|
1125 objsize1 == PGL_KEY_OBJSIZE_EMPTY ||
|
jbe@0
|
1126 objsize2 == PGL_KEY_OBJSIZE_EMPTY
|
jbe@0
|
1127 ) return objsize1 == objsize2;
|
jbe@0
|
1128 /* iterate through key bits */
|
jbe@0
|
1129 for (i=0; i<depth; i++) {
|
jbe@0
|
1130 /* every second bit is a bit describing the object size */
|
jbe@0
|
1131 if (i%2 == 0) {
|
jbe@0
|
1132 /* check if object size bit is different in both keys (objsize1 and
|
jbe@0
|
1133 objsize2 describe the minimum index when object size bit is set) */
|
jbe@0
|
1134 if (
|
jbe@0
|
1135 (objsize1 <= j && objsize2 > j) ||
|
jbe@0
|
1136 (objsize2 <= j && objsize1 > j)
|
jbe@0
|
1137 ) {
|
jbe@0
|
1138 /* bit differs, therefore keys are in separate branches */
|
jbe@0
|
1139 return false;
|
jbe@0
|
1140 }
|
jbe@0
|
1141 /* increase bit counter for object size bits */
|
jbe@0
|
1142 j++;
|
jbe@0
|
1143 }
|
jbe@0
|
1144 /* all other bits describe latitude and longitude */
|
jbe@0
|
1145 else {
|
jbe@0
|
1146 /* check if bit differs in both keys */
|
jbe@0
|
1147 if (PGL_KEY_LATLONBIT_DIFF(key1, key2, k)) {
|
jbe@0
|
1148 /* bit differs, therefore keys are in separate branches */
|
jbe@0
|
1149 return false;
|
jbe@0
|
1150 }
|
jbe@0
|
1151 /* increase bit counter for latitude/longitude bits */
|
jbe@0
|
1152 k++;
|
jbe@0
|
1153 }
|
jbe@0
|
1154 }
|
jbe@0
|
1155 }
|
jbe@0
|
1156 /* if not, keys are point keys */
|
jbe@0
|
1157 else {
|
jbe@0
|
1158 /* iterate through key bits */
|
jbe@0
|
1159 for (i=0; i<depth; i++) {
|
jbe@0
|
1160 /* check if bit differs in both keys */
|
jbe@0
|
1161 if (PGL_KEY_LATLONBIT_DIFF(key1, key2, i)) {
|
jbe@0
|
1162 /* bit differs, therefore keys are in separate branches */
|
jbe@0
|
1163 return false;
|
jbe@0
|
1164 }
|
jbe@0
|
1165 }
|
jbe@0
|
1166 }
|
jbe@0
|
1167 /* return true because keys are in the same branch */
|
jbe@0
|
1168 return true;
|
jbe@0
|
1169 }
|
jbe@0
|
1170
|
jbe@0
|
1171 /* combine two keys into new key which covers both original keys */
|
jbe@0
|
1172 /* (result stored in first argument) */
|
jbe@0
|
1173 static void pgl_unite_keys(pgl_keyptr dst, pgl_keyptr src) {
|
jbe@0
|
1174 int i; /* key bit offset (includes both lat/lon and log. obj. size bits) */
|
jbe@0
|
1175 /* determine smallest depth */
|
jbe@0
|
1176 int depth1 = PGL_KEY_NODEDEPTH(dst);
|
jbe@0
|
1177 int depth2 = PGL_KEY_NODEDEPTH(src);
|
jbe@0
|
1178 int depth = (depth1 < depth2) ? depth1 : depth2;
|
jbe@0
|
1179 /* check if keys are area keys (assuming that both keys have same type) */
|
jbe@0
|
1180 if (PGL_KEY_IS_AREAKEY(dst)) {
|
jbe@0
|
1181 pgl_areakey dstbuf = { 0, }; /* destination buffer (cleared) */
|
jbe@0
|
1182 int j = 0; /* bit offset for logarithmic object size bits */
|
jbe@0
|
1183 int k = 0; /* bit offset for latitude and longitude */
|
jbe@0
|
1184 /* fetch logarithmic object size information */
|
jbe@0
|
1185 int objsize1 = PGL_KEY_OBJSIZE(dst);
|
jbe@0
|
1186 int objsize2 = PGL_KEY_OBJSIZE(src);
|
jbe@0
|
1187 /* handle special cases for empty objects (universal and empty keys) */
|
jbe@0
|
1188 if (
|
jbe@0
|
1189 objsize1 > PGL_AREAKEY_MAXOBJSIZE ||
|
jbe@0
|
1190 objsize2 > PGL_AREAKEY_MAXOBJSIZE
|
jbe@0
|
1191 ) {
|
jbe@0
|
1192 if (
|
jbe@0
|
1193 objsize1 == PGL_KEY_OBJSIZE_EMPTY &&
|
jbe@0
|
1194 objsize2 == PGL_KEY_OBJSIZE_EMPTY
|
jbe@0
|
1195 ) pgl_key_set_empty(dst);
|
jbe@0
|
1196 else pgl_key_set_universal(dst);
|
jbe@0
|
1197 return;
|
jbe@0
|
1198 }
|
jbe@0
|
1199 /* iterate through key bits */
|
jbe@0
|
1200 for (i=0; i<depth; i++) {
|
jbe@0
|
1201 /* every second bit is a bit describing the object size */
|
jbe@0
|
1202 if (i%2 == 0) {
|
jbe@0
|
1203 /* increase bit counter for object size bits first */
|
jbe@0
|
1204 /* (handy when setting objsize variable) */
|
jbe@0
|
1205 j++;
|
jbe@0
|
1206 /* check if object size bit is set in neither key */
|
jbe@0
|
1207 if (objsize1 >= j && objsize2 >= j) {
|
jbe@0
|
1208 /* set objsize in destination buffer to indicate that size bit is
|
jbe@0
|
1209 unset in destination buffer at the current bit position */
|
jbe@0
|
1210 dstbuf[PGL_KEY_OBJSIZE_OFFSET] = j;
|
jbe@0
|
1211 }
|
jbe@0
|
1212 /* break if object size bit is set in one key only */
|
jbe@0
|
1213 else if (objsize1 >= j || objsize2 >= j) break;
|
jbe@0
|
1214 }
|
jbe@0
|
1215 /* all other bits describe latitude and longitude */
|
jbe@0
|
1216 else {
|
jbe@0
|
1217 /* break if bit differs in both keys */
|
jbe@0
|
1218 if (PGL_KEY_LATLONBIT(dst, k)) {
|
jbe@0
|
1219 if (!PGL_KEY_LATLONBIT(src, k)) break;
|
jbe@0
|
1220 /* but set bit in destination buffer if bit is set in both keys */
|
jbe@0
|
1221 dstbuf[k/8] |= 0x80 >> (k%8);
|
jbe@0
|
1222 } else if (PGL_KEY_LATLONBIT(src, k)) break;
|
jbe@0
|
1223 /* increase bit counter for latitude/longitude bits */
|
jbe@0
|
1224 k++;
|
jbe@0
|
1225 }
|
jbe@0
|
1226 }
|
jbe@0
|
1227 /* set common node depth and type bit (type bit = 1) */
|
jbe@0
|
1228 dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = PGL_AREAKEY_TYPEMASK | i;
|
jbe@0
|
1229 /* copy contents of destination buffer to first key */
|
jbe@0
|
1230 memcpy(dst, dstbuf, sizeof(pgl_areakey));
|
jbe@0
|
1231 }
|
jbe@0
|
1232 /* if not, keys are point keys */
|
jbe@0
|
1233 else {
|
jbe@0
|
1234 pgl_pointkey dstbuf = { 0, }; /* destination buffer (cleared) */
|
jbe@0
|
1235 /* iterate through key bits */
|
jbe@0
|
1236 for (i=0; i<depth; i++) {
|
jbe@0
|
1237 /* break if bit differs in both keys */
|
jbe@0
|
1238 if (PGL_KEY_LATLONBIT(dst, i)) {
|
jbe@0
|
1239 if (!PGL_KEY_LATLONBIT(src, i)) break;
|
jbe@0
|
1240 /* but set bit in destination buffer if bit is set in both keys */
|
jbe@0
|
1241 dstbuf[i/8] |= 0x80 >> (i%8);
|
jbe@0
|
1242 } else if (PGL_KEY_LATLONBIT(src, i)) break;
|
jbe@0
|
1243 }
|
jbe@0
|
1244 /* set common node depth (type bit = 0) */
|
jbe@0
|
1245 dstbuf[PGL_KEY_NODEDEPTH_OFFSET] = i;
|
jbe@0
|
1246 /* copy contents of destination buffer to first key */
|
jbe@0
|
1247 memcpy(dst, dstbuf, sizeof(pgl_pointkey));
|
jbe@0
|
1248 }
|
jbe@0
|
1249 }
|
jbe@0
|
1250
|
jbe@0
|
1251 /* determine center(!) boundaries and radius estimation of index key */
|
jbe@0
|
1252 static double pgl_key_to_box(pgl_keyptr key, pgl_box *box) {
|
jbe@0
|
1253 int i;
|
jbe@0
|
1254 /* determine node depth */
|
jbe@0
|
1255 int depth = PGL_KEY_NODEDEPTH(key);
|
jbe@0
|
1256 /* center point of possible result */
|
jbe@0
|
1257 double lat = 0;
|
jbe@0
|
1258 double lon = 0;
|
jbe@0
|
1259 /* maximum distance of real center point from key center */
|
jbe@0
|
1260 double dlat = 90;
|
jbe@0
|
1261 double dlon = 180;
|
jbe@0
|
1262 /* maximum radius of contained objects */
|
jbe@0
|
1263 double radius = 0; /* always return zero for point index keys */
|
jbe@0
|
1264 /* check if key is area key */
|
jbe@0
|
1265 if (PGL_KEY_IS_AREAKEY(key)) {
|
jbe@0
|
1266 /* get logarithmic object size */
|
jbe@0
|
1267 int objsize = PGL_KEY_OBJSIZE(key);
|
jbe@0
|
1268 /* handle special cases for empty objects (universal and empty keys) */
|
jbe@0
|
1269 if (objsize == PGL_KEY_OBJSIZE_EMPTY) {
|
jbe@0
|
1270 pgl_box_set_empty(box);
|
jbe@0
|
1271 return 0;
|
jbe@0
|
1272 } else if (objsize == PGL_KEY_OBJSIZE_UNIVERSAL) {
|
jbe@0
|
1273 box->lat_min = -90;
|
jbe@0
|
1274 box->lat_max = 90;
|
jbe@0
|
1275 box->lon_min = -180;
|
jbe@0
|
1276 box->lon_max = 180;
|
jbe@0
|
1277 return 0; /* any value >= 0 would do */
|
jbe@0
|
1278 }
|
jbe@0
|
1279 /* calculate maximum possible radius of objects covered by the given key */
|
jbe@0
|
1280 if (objsize == 0) radius = INFINITY;
|
jbe@0
|
1281 else {
|
jbe@0
|
1282 radius = PGL_AREAKEY_REFOBJSIZE;
|
jbe@0
|
1283 while (--objsize) radius /= M_SQRT2;
|
jbe@0
|
1284 }
|
jbe@0
|
1285 /* iterate over latitude and longitude bits in key */
|
jbe@0
|
1286 /* (every second bit is a latitude or longitude bit) */
|
jbe@0
|
1287 for (i=0; i<depth/2; i++) {
|
jbe@0
|
1288 /* check if latitude bit */
|
jbe@0
|
1289 if (i%2 == 0) {
|
jbe@0
|
1290 /* cut latitude dimension in half */
|
jbe@0
|
1291 dlat /= 2;
|
jbe@0
|
1292 /* increase center latitude if bit is 1, otherwise decrease */
|
jbe@0
|
1293 if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
|
jbe@0
|
1294 else lat -= dlat;
|
jbe@0
|
1295 }
|
jbe@0
|
1296 /* otherwise longitude bit */
|
jbe@0
|
1297 else {
|
jbe@0
|
1298 /* cut longitude dimension in half */
|
jbe@0
|
1299 dlon /= 2;
|
jbe@0
|
1300 /* increase center longitude if bit is 1, otherwise decrease */
|
jbe@0
|
1301 if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
|
jbe@0
|
1302 else lon -= dlon;
|
jbe@0
|
1303 }
|
jbe@0
|
1304 }
|
jbe@0
|
1305 }
|
jbe@0
|
1306 /* if not, keys are point keys */
|
jbe@0
|
1307 else {
|
jbe@0
|
1308 /* iterate over all bits in key */
|
jbe@0
|
1309 for (i=0; i<depth; i++) {
|
jbe@0
|
1310 /* check if latitude bit */
|
jbe@0
|
1311 if (i%2 == 0) {
|
jbe@0
|
1312 /* cut latitude dimension in half */
|
jbe@0
|
1313 dlat /= 2;
|
jbe@0
|
1314 /* increase center latitude if bit is 1, otherwise decrease */
|
jbe@0
|
1315 if (PGL_KEY_LATLONBIT(key, i)) lat += dlat;
|
jbe@0
|
1316 else lat -= dlat;
|
jbe@0
|
1317 }
|
jbe@0
|
1318 /* otherwise longitude bit */
|
jbe@0
|
1319 else {
|
jbe@0
|
1320 /* cut longitude dimension in half */
|
jbe@0
|
1321 dlon /= 2;
|
jbe@0
|
1322 /* increase center longitude if bit is 1, otherwise decrease */
|
jbe@0
|
1323 if (PGL_KEY_LATLONBIT(key, i)) lon += dlon;
|
jbe@0
|
1324 else lon -= dlon;
|
jbe@0
|
1325 }
|
jbe@0
|
1326 }
|
jbe@0
|
1327 }
|
jbe@0
|
1328 /* calculate boundaries from center point and remaining dlat and dlon */
|
jbe@0
|
1329 /* (return values through pointer to box) */
|
jbe@0
|
1330 box->lat_min = lat - dlat;
|
jbe@0
|
1331 box->lat_max = lat + dlat;
|
jbe@0
|
1332 box->lon_min = lon - dlon;
|
jbe@0
|
1333 box->lon_max = lon + dlon;
|
jbe@0
|
1334 /* return radius (as a function return value) */
|
jbe@0
|
1335 return radius;
|
jbe@0
|
1336 }
|
jbe@0
|
1337
|
jbe@0
|
1338 /* estimator function for distance between point and index key */
|
jbe@16
|
1339 /* always returns a smaller value than actually correct or zero */
|
jbe@0
|
1340 static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
|
jbe@0
|
1341 pgl_box box; /* center(!) bounding box of area index key */
|
jbe@0
|
1342 /* calculate center(!) bounding box and maximum radius of objects covered
|
jbe@0
|
1343 by area index key (radius is zero for point index keys) */
|
jbe@0
|
1344 double distance = pgl_key_to_box(key, &box);
|
jbe@0
|
1345 /* calculate estimated distance between bounding box of center point of
|
jbe@0
|
1346 indexed object and point passed as second argument, then substract maximum
|
jbe@0
|
1347 radius of objects covered by index key */
|
jbe@16
|
1348 distance = pgl_estimate_point_box_distance(point, &box) - distance;
|
jbe@0
|
1349 /* truncate negative results to zero */
|
jbe@0
|
1350 if (distance <= 0) distance = 0;
|
jbe@0
|
1351 /* return result */
|
jbe@0
|
1352 return distance;
|
jbe@0
|
1353 }
|
jbe@0
|
1354
|
jbe@0
|
1355
|
jbe@0
|
1356 /*---------------------------------*
|
jbe@0
|
1357 * helper functions for text I/O *
|
jbe@0
|
1358 *---------------------------------*/
|
jbe@0
|
1359
|
jbe@0
|
1360 #define PGL_NUMBUFLEN 64 /* buffer size for number to string conversion */
|
jbe@0
|
1361
|
jbe@0
|
1362 /* convert floating point number to string (round-trip safe) */
|
jbe@0
|
1363 static void pgl_print_float(char *buf, double flt) {
|
jbe@0
|
1364 /* check if number is integral */
|
jbe@0
|
1365 if (trunc(flt) == flt) {
|
jbe@0
|
1366 /* for integral floats use maximum precision */
|
jbe@0
|
1367 snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
|
jbe@0
|
1368 } else {
|
jbe@0
|
1369 /* otherwise check if 15, 16, or 17 digits needed (round-trip safety) */
|
jbe@0
|
1370 snprintf(buf, PGL_NUMBUFLEN, "%.15g", flt);
|
jbe@0
|
1371 if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.16g", flt);
|
jbe@0
|
1372 if (strtod(buf, NULL) != flt) snprintf(buf, PGL_NUMBUFLEN, "%.17g", flt);
|
jbe@0
|
1373 }
|
jbe@0
|
1374 }
|
jbe@0
|
1375
|
jbe@0
|
1376 /* convert latitude floating point number (in degrees) to string */
|
jbe@0
|
1377 static void pgl_print_lat(char *buf, double lat) {
|
jbe@0
|
1378 if (signbit(lat)) {
|
jbe@0
|
1379 /* treat negative latitudes (including -0) as south */
|
jbe@0
|
1380 snprintf(buf, PGL_NUMBUFLEN, "S%015.12f", -lat);
|
jbe@0
|
1381 } else {
|
jbe@0
|
1382 /* treat positive latitudes (including +0) as north */
|
jbe@0
|
1383 snprintf(buf, PGL_NUMBUFLEN, "N%015.12f", lat);
|
jbe@0
|
1384 }
|
jbe@0
|
1385 }
|
jbe@0
|
1386
|
jbe@0
|
1387 /* convert longitude floating point number (in degrees) to string */
|
jbe@0
|
1388 static void pgl_print_lon(char *buf, double lon) {
|
jbe@0
|
1389 if (signbit(lon)) {
|
jbe@0
|
1390 /* treat negative longitudes (including -0) as west */
|
jbe@0
|
1391 snprintf(buf, PGL_NUMBUFLEN, "W%016.12f", -lon);
|
jbe@0
|
1392 } else {
|
jbe@0
|
1393 /* treat positive longitudes (including +0) as east */
|
jbe@0
|
1394 snprintf(buf, PGL_NUMBUFLEN, "E%016.12f", lon);
|
jbe@0
|
1395 }
|
jbe@0
|
1396 }
|
jbe@0
|
1397
|
jbe@0
|
1398 /* bit masks used as return value of pgl_scan() function */
|
jbe@0
|
1399 #define PGL_SCAN_NONE 0 /* no value has been parsed */
|
jbe@0
|
1400 #define PGL_SCAN_LAT (1<<0) /* latitude has been parsed */
|
jbe@0
|
1401 #define PGL_SCAN_LON (1<<1) /* longitude has been parsed */
|
jbe@0
|
1402 #define PGL_SCAN_LATLON (PGL_SCAN_LAT | PGL_SCAN_LON) /* bitwise OR of both */
|
jbe@0
|
1403
|
jbe@0
|
1404 /* parse a coordinate (can be latitude or longitude) */
|
jbe@0
|
1405 static int pgl_scan(char **str, double *lat, double *lon) {
|
jbe@0
|
1406 double val;
|
jbe@0
|
1407 int len;
|
jbe@0
|
1408 if (
|
jbe@0
|
1409 sscanf(*str, " N %lf %n", &val, &len) ||
|
jbe@0
|
1410 sscanf(*str, " n %lf %n", &val, &len)
|
jbe@0
|
1411 ) {
|
jbe@0
|
1412 *str += len; *lat = val; return PGL_SCAN_LAT;
|
jbe@0
|
1413 }
|
jbe@0
|
1414 if (
|
jbe@0
|
1415 sscanf(*str, " S %lf %n", &val, &len) ||
|
jbe@0
|
1416 sscanf(*str, " s %lf %n", &val, &len)
|
jbe@0
|
1417 ) {
|
jbe@0
|
1418 *str += len; *lat = -val; return PGL_SCAN_LAT;
|
jbe@0
|
1419 }
|
jbe@0
|
1420 if (
|
jbe@0
|
1421 sscanf(*str, " E %lf %n", &val, &len) ||
|
jbe@0
|
1422 sscanf(*str, " e %lf %n", &val, &len)
|
jbe@0
|
1423 ) {
|
jbe@0
|
1424 *str += len; *lon = val; return PGL_SCAN_LON;
|
jbe@0
|
1425 }
|
jbe@0
|
1426 if (
|
jbe@0
|
1427 sscanf(*str, " W %lf %n", &val, &len) ||
|
jbe@0
|
1428 sscanf(*str, " w %lf %n", &val, &len)
|
jbe@0
|
1429 ) {
|
jbe@0
|
1430 *str += len; *lon = -val; return PGL_SCAN_LON;
|
jbe@0
|
1431 }
|
jbe@0
|
1432 return PGL_SCAN_NONE;
|
jbe@0
|
1433 }
|
jbe@0
|
1434
|
jbe@0
|
1435
|
jbe@0
|
1436 /*-----------------*
|
jbe@0
|
1437 * SQL functions *
|
jbe@0
|
1438 *-----------------*/
|
jbe@0
|
1439
|
jbe@0
|
1440 /* Note: These function names use "epoint", "ebox", etc. notation here instead
|
jbe@0
|
1441 of "point", "box", etc. in order to distinguish them from any previously
|
jbe@0
|
1442 defined functions. */
|
jbe@0
|
1443
|
jbe@0
|
1444 /* function needed for dummy types and/or not implemented features */
|
jbe@0
|
1445 PG_FUNCTION_INFO_V1(pgl_notimpl);
|
jbe@0
|
1446 Datum pgl_notimpl(PG_FUNCTION_ARGS) {
|
jbe@0
|
1447 ereport(ERROR, (errmsg("not implemented by pgLatLon")));
|
jbe@0
|
1448 }
|
jbe@0
|
1449
|
jbe@0
|
1450 /* set point to latitude and longitude (including checks) */
|
jbe@0
|
1451 static void pgl_epoint_set_latlon(pgl_point *point, double lat, double lon) {
|
jbe@0
|
1452 /* reject infinite or NaN values */
|
jbe@0
|
1453 if (!isfinite(lat) || !isfinite(lon)) {
|
jbe@0
|
1454 ereport(ERROR, (
|
jbe@0
|
1455 errcode(ERRCODE_DATA_EXCEPTION),
|
jbe@0
|
1456 errmsg("epoint requires finite coordinates")
|
jbe@0
|
1457 ));
|
jbe@0
|
1458 }
|
jbe@0
|
1459 /* check latitude bounds */
|
jbe@0
|
1460 if (lat < -90) {
|
jbe@0
|
1461 ereport(WARNING, (errmsg("latitude exceeds south pole")));
|
jbe@0
|
1462 lat = -90;
|
jbe@0
|
1463 } else if (lat > 90) {
|
jbe@0
|
1464 ereport(WARNING, (errmsg("latitude exceeds north pole")));
|
jbe@0
|
1465 lat = 90;
|
jbe@0
|
1466 }
|
jbe@0
|
1467 /* check longitude bounds */
|
jbe@0
|
1468 if (lon < -180) {
|
jbe@0
|
1469 ereport(NOTICE, (errmsg("longitude west of 180th meridian normalized")));
|
jbe@0
|
1470 lon += 360 - trunc(lon / 360) * 360;
|
jbe@0
|
1471 } else if (lon > 180) {
|
jbe@0
|
1472 ereport(NOTICE, (errmsg("longitude east of 180th meridian normalized")));
|
jbe@0
|
1473 lon -= 360 + trunc(lon / 360) * 360;
|
jbe@0
|
1474 }
|
jbe@0
|
1475 /* store rounded latitude/longitude values for round-trip safety */
|
jbe@0
|
1476 point->lat = pgl_round(lat);
|
jbe@0
|
1477 point->lon = pgl_round(lon);
|
jbe@0
|
1478 }
|
jbe@0
|
1479
|
jbe@0
|
1480 /* create point ("epoint" in SQL) from latitude and longitude */
|
jbe@0
|
1481 PG_FUNCTION_INFO_V1(pgl_create_epoint);
|
jbe@0
|
1482 Datum pgl_create_epoint(PG_FUNCTION_ARGS) {
|
jbe@0
|
1483 pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
|
jbe@0
|
1484 pgl_epoint_set_latlon(point, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1));
|
jbe@0
|
1485 PG_RETURN_POINTER(point);
|
jbe@0
|
1486 }
|
jbe@0
|
1487
|
jbe@0
|
1488 /* parse point ("epoint" in SQL) */
|
jbe@0
|
1489 /* format: '[NS]<float> [EW]<float>' */
|
jbe@0
|
1490 PG_FUNCTION_INFO_V1(pgl_epoint_in);
|
jbe@0
|
1491 Datum pgl_epoint_in(PG_FUNCTION_ARGS) {
|
jbe@0
|
1492 char *str = PG_GETARG_CSTRING(0); /* input string */
|
jbe@0
|
1493 char *strptr = str; /* current position within string */
|
jbe@0
|
1494 int done = 0; /* bit mask storing if latitude or longitude was read */
|
jbe@0
|
1495 double lat, lon; /* parsed values as double precision floats */
|
jbe@0
|
1496 pgl_point *point; /* return value (to be palloc'ed) */
|
jbe@0
|
1497 /* parse two floats (each latitude or longitude) separated by white-space */
|
jbe@0
|
1498 done |= pgl_scan(&strptr, &lat, &lon);
|
jbe@0
|
1499 if (strptr != str && isspace(strptr[-1])) {
|
jbe@0
|
1500 done |= pgl_scan(&strptr, &lat, &lon);
|
jbe@0
|
1501 }
|
jbe@0
|
1502 /* require end of string, and latitude and longitude parsed successfully */
|
jbe@0
|
1503 if (strptr[0] || done != PGL_SCAN_LATLON) {
|
jbe@0
|
1504 ereport(ERROR, (
|
jbe@0
|
1505 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
jbe@0
|
1506 errmsg("invalid input syntax for type epoint: \"%s\"", str)
|
jbe@0
|
1507 ));
|
jbe@0
|
1508 }
|
jbe@0
|
1509 /* allocate memory for result */
|
jbe@0
|
1510 point = (pgl_point *)palloc(sizeof(pgl_point));
|
jbe@0
|
1511 /* set latitude and longitude (and perform checks) */
|
jbe@0
|
1512 pgl_epoint_set_latlon(point, lat, lon);
|
jbe@0
|
1513 /* return result */
|
jbe@0
|
1514 PG_RETURN_POINTER(point);
|
jbe@0
|
1515 }
|
jbe@0
|
1516
|
jbe@0
|
1517 /* create box ("ebox" in SQL) that is empty */
|
jbe@0
|
1518 PG_FUNCTION_INFO_V1(pgl_create_empty_ebox);
|
jbe@0
|
1519 Datum pgl_create_empty_ebox(PG_FUNCTION_ARGS) {
|
jbe@0
|
1520 pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
|
jbe@0
|
1521 pgl_box_set_empty(box);
|
jbe@0
|
1522 PG_RETURN_POINTER(box);
|
jbe@0
|
1523 }
|
jbe@0
|
1524
|
jbe@0
|
1525 /* set box to given boundaries (including checks) */
|
jbe@0
|
1526 static void pgl_ebox_set_boundaries(
|
jbe@0
|
1527 pgl_box *box,
|
jbe@0
|
1528 double lat_min, double lat_max, double lon_min, double lon_max
|
jbe@0
|
1529 ) {
|
jbe@0
|
1530 /* if minimum latitude is greater than maximum latitude, return empty box */
|
jbe@0
|
1531 if (lat_min > lat_max) {
|
jbe@0
|
1532 pgl_box_set_empty(box);
|
jbe@0
|
1533 return;
|
jbe@0
|
1534 }
|
jbe@0
|
1535 /* otherwise reject infinite or NaN values */
|
jbe@0
|
1536 if (
|
jbe@0
|
1537 !isfinite(lat_min) || !isfinite(lat_max) ||
|
jbe@0
|
1538 !isfinite(lon_min) || !isfinite(lon_max)
|
jbe@0
|
1539 ) {
|
jbe@0
|
1540 ereport(ERROR, (
|
jbe@0
|
1541 errcode(ERRCODE_DATA_EXCEPTION),
|
jbe@0
|
1542 errmsg("ebox requires finite coordinates")
|
jbe@0
|
1543 ));
|
jbe@0
|
1544 }
|
jbe@0
|
1545 /* check latitude bounds */
|
jbe@0
|
1546 if (lat_max < -90) {
|
jbe@0
|
1547 ereport(WARNING, (errmsg("northern latitude exceeds south pole")));
|
jbe@0
|
1548 lat_max = -90;
|
jbe@0
|
1549 } else if (lat_max > 90) {
|
jbe@0
|
1550 ereport(WARNING, (errmsg("northern latitude exceeds north pole")));
|
jbe@0
|
1551 lat_max = 90;
|
jbe@0
|
1552 }
|
jbe@0
|
1553 if (lat_min < -90) {
|
jbe@0
|
1554 ereport(WARNING, (errmsg("southern latitude exceeds south pole")));
|
jbe@0
|
1555 lat_min = -90;
|
jbe@0
|
1556 } else if (lat_min > 90) {
|
jbe@0
|
1557 ereport(WARNING, (errmsg("southern latitude exceeds north pole")));
|
jbe@0
|
1558 lat_min = 90;
|
jbe@0
|
1559 }
|
jbe@0
|
1560 /* check if all longitudes are included */
|
jbe@0
|
1561 if (lon_max - lon_min >= 360) {
|
jbe@0
|
1562 if (lon_max - lon_min > 360) ereport(WARNING, (
|
jbe@0
|
1563 errmsg("longitude coverage greater than 360 degrees")
|
jbe@0
|
1564 ));
|
jbe@0
|
1565 lon_min = -180;
|
jbe@0
|
1566 lon_max = 180;
|
jbe@0
|
1567 } else {
|
jbe@0
|
1568 /* normalize longitude bounds */
|
jbe@0
|
1569 if (lon_min < -180) lon_min += 360 - trunc(lon_min / 360) * 360;
|
jbe@0
|
1570 else if (lon_min > 180) lon_min -= 360 + trunc(lon_min / 360) * 360;
|
jbe@0
|
1571 if (lon_max < -180) lon_max += 360 - trunc(lon_max / 360) * 360;
|
jbe@0
|
1572 else if (lon_max > 180) lon_max -= 360 + trunc(lon_max / 360) * 360;
|
jbe@0
|
1573 }
|
jbe@0
|
1574 /* store rounded latitude/longitude values for round-trip safety */
|
jbe@0
|
1575 box->lat_min = pgl_round(lat_min);
|
jbe@0
|
1576 box->lat_max = pgl_round(lat_max);
|
jbe@0
|
1577 box->lon_min = pgl_round(lon_min);
|
jbe@0
|
1578 box->lon_max = pgl_round(lon_max);
|
jbe@0
|
1579 /* ensure that rounding does not change orientation */
|
jbe@0
|
1580 if (lon_min > lon_max && box->lon_min == box->lon_max) {
|
jbe@0
|
1581 box->lon_min = -180;
|
jbe@0
|
1582 box->lon_max = 180;
|
jbe@0
|
1583 }
|
jbe@0
|
1584 }
|
jbe@0
|
1585
|
jbe@0
|
1586 /* create box ("ebox" in SQL) from min/max latitude and min/max longitude */
|
jbe@0
|
1587 PG_FUNCTION_INFO_V1(pgl_create_ebox);
|
jbe@0
|
1588 Datum pgl_create_ebox(PG_FUNCTION_ARGS) {
|
jbe@0
|
1589 pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
|
jbe@0
|
1590 pgl_ebox_set_boundaries(
|
jbe@0
|
1591 box,
|
jbe@0
|
1592 PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1),
|
jbe@0
|
1593 PG_GETARG_FLOAT8(2), PG_GETARG_FLOAT8(3)
|
jbe@0
|
1594 );
|
jbe@0
|
1595 PG_RETURN_POINTER(box);
|
jbe@0
|
1596 }
|
jbe@0
|
1597
|
jbe@0
|
1598 /* create box ("ebox" in SQL) from two points ("epoint"s) */
|
jbe@0
|
1599 /* (can not be used to cover a longitude range of more than 120 degrees) */
|
jbe@0
|
1600 PG_FUNCTION_INFO_V1(pgl_create_ebox_from_epoints);
|
jbe@0
|
1601 Datum pgl_create_ebox_from_epoints(PG_FUNCTION_ARGS) {
|
jbe@0
|
1602 pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
1603 pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
|
jbe@0
|
1604 pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
|
jbe@0
|
1605 double lat_min, lat_max, lon_min, lon_max;
|
jbe@0
|
1606 double dlon; /* longitude range (delta longitude) */
|
jbe@0
|
1607 /* order latitude and longitude boundaries */
|
jbe@0
|
1608 if (point2->lat < point1->lat) {
|
jbe@0
|
1609 lat_min = point2->lat;
|
jbe@0
|
1610 lat_max = point1->lat;
|
jbe@0
|
1611 } else {
|
jbe@0
|
1612 lat_min = point1->lat;
|
jbe@0
|
1613 lat_max = point2->lat;
|
jbe@0
|
1614 }
|
jbe@0
|
1615 if (point2->lon < point1->lon) {
|
jbe@0
|
1616 lon_min = point2->lon;
|
jbe@0
|
1617 lon_max = point1->lon;
|
jbe@0
|
1618 } else {
|
jbe@0
|
1619 lon_min = point1->lon;
|
jbe@0
|
1620 lon_max = point2->lon;
|
jbe@0
|
1621 }
|
jbe@0
|
1622 /* calculate longitude range (round to avoid floating point errors) */
|
jbe@0
|
1623 dlon = pgl_round(lon_max - lon_min);
|
jbe@0
|
1624 /* determine east-west direction */
|
jbe@0
|
1625 if (dlon >= 240) {
|
jbe@0
|
1626 /* assume that 180th meridian is crossed and swap min/max longitude */
|
jbe@0
|
1627 double swap = lon_min; lon_min = lon_max; lon_max = swap;
|
jbe@0
|
1628 } else if (dlon > 120) {
|
jbe@0
|
1629 /* unclear orientation since delta longitude > 120 */
|
jbe@0
|
1630 ereport(ERROR, (
|
jbe@0
|
1631 errcode(ERRCODE_DATA_EXCEPTION),
|
jbe@0
|
1632 errmsg("can not determine east/west orientation for ebox")
|
jbe@0
|
1633 ));
|
jbe@0
|
1634 }
|
jbe@0
|
1635 /* use boundaries to setup box (and perform checks) */
|
jbe@0
|
1636 pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
|
jbe@0
|
1637 /* return result */
|
jbe@0
|
1638 PG_RETURN_POINTER(box);
|
jbe@0
|
1639 }
|
jbe@0
|
1640
|
jbe@0
|
1641 /* parse box ("ebox" in SQL) */
|
jbe@0
|
1642 /* format: '[NS]<float> [EW]<float> [NS]<float> [EW]<float>'
|
jbe@0
|
1643 or: '[NS]<float> [NS]<float> [EW]<float> [EW]<float>' */
|
jbe@0
|
1644 PG_FUNCTION_INFO_V1(pgl_ebox_in);
|
jbe@0
|
1645 Datum pgl_ebox_in(PG_FUNCTION_ARGS) {
|
jbe@0
|
1646 char *str = PG_GETARG_CSTRING(0); /* input string */
|
jbe@0
|
1647 char *str_lower; /* lower case version of input string */
|
jbe@0
|
1648 char *strptr; /* current position within string */
|
jbe@0
|
1649 int valid; /* number of valid chars */
|
jbe@0
|
1650 int done; /* specifies if latitude or longitude was read */
|
jbe@0
|
1651 double val; /* temporary variable */
|
jbe@0
|
1652 int lat_count = 0; /* count of latitude values parsed */
|
jbe@0
|
1653 int lon_count = 0; /* count of longitufde values parsed */
|
jbe@0
|
1654 double lat_min, lat_max, lon_min, lon_max; /* see pgl_box struct */
|
jbe@0
|
1655 pgl_box *box; /* return value (to be palloc'ed) */
|
jbe@0
|
1656 /* lowercase input */
|
jbe@0
|
1657 str_lower = psprintf("%s", str);
|
jbe@0
|
1658 for (strptr=str_lower; *strptr; strptr++) {
|
jbe@0
|
1659 if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
|
jbe@0
|
1660 }
|
jbe@0
|
1661 /* reset reading position to start of (lowercase) string */
|
jbe@0
|
1662 strptr = str_lower;
|
jbe@0
|
1663 /* check if empty box */
|
jbe@0
|
1664 valid = 0;
|
jbe@0
|
1665 sscanf(strptr, " empty %n", &valid);
|
jbe@0
|
1666 if (valid && strptr[valid] == 0) {
|
jbe@0
|
1667 /* allocate and return empty box */
|
jbe@0
|
1668 box = (pgl_box *)palloc(sizeof(pgl_box));
|
jbe@0
|
1669 pgl_box_set_empty(box);
|
jbe@0
|
1670 PG_RETURN_POINTER(box);
|
jbe@0
|
1671 }
|
jbe@0
|
1672 /* demand four blocks separated by whitespace */
|
jbe@0
|
1673 valid = 0;
|
jbe@0
|
1674 sscanf(strptr, " %*s %*s %*s %*s %n", &valid);
|
jbe@0
|
1675 /* if four blocks separated by whitespace exist, parse those blocks */
|
jbe@0
|
1676 if (strptr[valid] == 0) while (strptr[0]) {
|
jbe@0
|
1677 /* parse either latitude or longitude (whichever found in input string) */
|
jbe@0
|
1678 done = pgl_scan(&strptr, &val, &val);
|
jbe@0
|
1679 /* store latitude or longitude in lat_min, lat_max, lon_min, or lon_max */
|
jbe@0
|
1680 if (done == PGL_SCAN_LAT) {
|
jbe@0
|
1681 if (!lat_count) lat_min = val; else lat_max = val;
|
jbe@0
|
1682 lat_count++;
|
jbe@0
|
1683 } else if (done == PGL_SCAN_LON) {
|
jbe@0
|
1684 if (!lon_count) lon_min = val; else lon_max = val;
|
jbe@0
|
1685 lon_count++;
|
jbe@0
|
1686 } else {
|
jbe@0
|
1687 break;
|
jbe@0
|
1688 }
|
jbe@0
|
1689 }
|
jbe@0
|
1690 /* require end of string, and two latitude and two longitude values */
|
jbe@0
|
1691 if (strptr[0] || lat_count != 2 || lon_count != 2) {
|
jbe@0
|
1692 ereport(ERROR, (
|
jbe@0
|
1693 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
jbe@0
|
1694 errmsg("invalid input syntax for type ebox: \"%s\"", str)
|
jbe@0
|
1695 ));
|
jbe@0
|
1696 }
|
jbe@0
|
1697 /* free lower case string */
|
jbe@0
|
1698 pfree(str_lower);
|
jbe@0
|
1699 /* order boundaries (maximum greater than minimum) */
|
jbe@0
|
1700 if (lat_min > lat_max) { val = lat_min; lat_min = lat_max; lat_max = val; }
|
jbe@0
|
1701 if (lon_min > lon_max) { val = lon_min; lon_min = lon_max; lon_max = val; }
|
jbe@0
|
1702 /* allocate memory for result */
|
jbe@0
|
1703 box = (pgl_box *)palloc(sizeof(pgl_box));
|
jbe@0
|
1704 /* set boundaries (and perform checks) */
|
jbe@0
|
1705 pgl_ebox_set_boundaries(box, lat_min, lat_max, lon_min, lon_max);
|
jbe@0
|
1706 /* return result */
|
jbe@0
|
1707 PG_RETURN_POINTER(box);
|
jbe@0
|
1708 }
|
jbe@0
|
1709
|
jbe@0
|
1710 /* set circle to given latitude, longitude, and radius (including checks) */
|
jbe@0
|
1711 static void pgl_ecircle_set_latlon_radius(
|
jbe@0
|
1712 pgl_circle *circle, double lat, double lon, double radius
|
jbe@0
|
1713 ) {
|
jbe@0
|
1714 /* set center point (including checks) */
|
jbe@0
|
1715 pgl_epoint_set_latlon(&(circle->center), lat, lon);
|
jbe@0
|
1716 /* handle non-positive radius */
|
jbe@0
|
1717 if (isnan(radius)) {
|
jbe@0
|
1718 ereport(ERROR, (
|
jbe@0
|
1719 errcode(ERRCODE_DATA_EXCEPTION),
|
jbe@0
|
1720 errmsg("invalid radius for ecircle")
|
jbe@0
|
1721 ));
|
jbe@0
|
1722 }
|
jbe@0
|
1723 if (radius == 0) radius = 0; /* avoids -0 */
|
jbe@0
|
1724 else if (radius < 0) {
|
jbe@0
|
1725 if (isfinite(radius)) {
|
jbe@0
|
1726 ereport(NOTICE, (errmsg("negative radius converted to minus infinity")));
|
jbe@0
|
1727 }
|
jbe@0
|
1728 radius = -INFINITY;
|
jbe@0
|
1729 }
|
jbe@0
|
1730 /* store radius (round-trip safety is ensured by pgl_print_float) */
|
jbe@0
|
1731 circle->radius = radius;
|
jbe@0
|
1732 }
|
jbe@0
|
1733
|
jbe@0
|
1734 /* create circle ("ecircle" in SQL) from latitude, longitude, and radius */
|
jbe@0
|
1735 PG_FUNCTION_INFO_V1(pgl_create_ecircle);
|
jbe@0
|
1736 Datum pgl_create_ecircle(PG_FUNCTION_ARGS) {
|
jbe@0
|
1737 pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
|
jbe@0
|
1738 pgl_ecircle_set_latlon_radius(
|
jbe@0
|
1739 circle, PG_GETARG_FLOAT8(0), PG_GETARG_FLOAT8(1), PG_GETARG_FLOAT8(2)
|
jbe@0
|
1740 );
|
jbe@0
|
1741 PG_RETURN_POINTER(circle);
|
jbe@0
|
1742 }
|
jbe@0
|
1743
|
jbe@0
|
1744 /* create circle ("ecircle" in SQL) from point ("epoint"), and radius */
|
jbe@0
|
1745 PG_FUNCTION_INFO_V1(pgl_create_ecircle_from_epoint);
|
jbe@0
|
1746 Datum pgl_create_ecircle_from_epoint(PG_FUNCTION_ARGS) {
|
jbe@0
|
1747 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
1748 double radius = PG_GETARG_FLOAT8(1);
|
jbe@0
|
1749 pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
|
jbe@0
|
1750 /* set latitude, longitude, radius (and perform checks) */
|
jbe@0
|
1751 pgl_ecircle_set_latlon_radius(circle, point->lat, point->lon, radius);
|
jbe@0
|
1752 /* return result */
|
jbe@0
|
1753 PG_RETURN_POINTER(circle);
|
jbe@0
|
1754 }
|
jbe@0
|
1755
|
jbe@0
|
1756 /* parse circle ("ecircle" in SQL) */
|
jbe@0
|
1757 /* format: '[NS]<float> [EW]<float> <float>' */
|
jbe@0
|
1758 PG_FUNCTION_INFO_V1(pgl_ecircle_in);
|
jbe@0
|
1759 Datum pgl_ecircle_in(PG_FUNCTION_ARGS) {
|
jbe@0
|
1760 char *str = PG_GETARG_CSTRING(0); /* input string */
|
jbe@0
|
1761 char *strptr = str; /* current position within string */
|
jbe@0
|
1762 double lat, lon, radius; /* parsed values as double precision flaots */
|
jbe@0
|
1763 int valid = 0; /* number of valid chars */
|
jbe@0
|
1764 int done = 0; /* stores if latitude and/or longitude was read */
|
jbe@0
|
1765 pgl_circle *circle; /* return value (to be palloc'ed) */
|
jbe@0
|
1766 /* demand three blocks separated by whitespace */
|
jbe@0
|
1767 sscanf(strptr, " %*s %*s %*s %n", &valid);
|
jbe@0
|
1768 /* if three blocks separated by whitespace exist, parse those blocks */
|
jbe@0
|
1769 if (strptr[valid] == 0) {
|
jbe@0
|
1770 /* parse latitude and longitude */
|
jbe@0
|
1771 done |= pgl_scan(&strptr, &lat, &lon);
|
jbe@0
|
1772 done |= pgl_scan(&strptr, &lat, &lon);
|
jbe@0
|
1773 /* parse radius (while incrementing strptr by number of bytes parsed) */
|
jbe@0
|
1774 valid = 0;
|
jbe@0
|
1775 if (sscanf(strptr, " %lf %n", &radius, &valid) == 1) strptr += valid;
|
jbe@0
|
1776 }
|
jbe@0
|
1777 /* require end of string and both latitude and longitude being parsed */
|
jbe@0
|
1778 if (strptr[0] || done != PGL_SCAN_LATLON) {
|
jbe@0
|
1779 ereport(ERROR, (
|
jbe@0
|
1780 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
jbe@0
|
1781 errmsg("invalid input syntax for type ecircle: \"%s\"", str)
|
jbe@0
|
1782 ));
|
jbe@0
|
1783 }
|
jbe@0
|
1784 /* allocate memory for result */
|
jbe@0
|
1785 circle = (pgl_circle *)palloc(sizeof(pgl_circle));
|
jbe@0
|
1786 /* set latitude, longitude, radius (and perform checks) */
|
jbe@0
|
1787 pgl_ecircle_set_latlon_radius(circle, lat, lon, radius);
|
jbe@0
|
1788 /* return result */
|
jbe@0
|
1789 PG_RETURN_POINTER(circle);
|
jbe@0
|
1790 }
|
jbe@0
|
1791
|
jbe@0
|
1792 /* parse cluster ("ecluster" in SQL) */
|
jbe@0
|
1793 PG_FUNCTION_INFO_V1(pgl_ecluster_in);
|
jbe@0
|
1794 Datum pgl_ecluster_in(PG_FUNCTION_ARGS) {
|
jbe@0
|
1795 int i;
|
jbe@0
|
1796 char *str = PG_GETARG_CSTRING(0); /* input string */
|
jbe@0
|
1797 char *str_lower; /* lower case version of input string */
|
jbe@0
|
1798 char *strptr; /* pointer to current reading position of input */
|
jbe@0
|
1799 int npoints_total = 0; /* total number of points in cluster */
|
jbe@0
|
1800 int nentries = 0; /* total number of entries */
|
jbe@0
|
1801 pgl_newentry *entries; /* array of pgl_newentry to create pgl_cluster */
|
jbe@0
|
1802 int entries_buflen = 4; /* maximum number of elements in entries array */
|
jbe@0
|
1803 int valid; /* number of valid chars processed */
|
jbe@0
|
1804 double lat, lon; /* latitude and longitude of parsed point */
|
jbe@0
|
1805 int entrytype; /* current entry type */
|
jbe@0
|
1806 int npoints; /* number of points in current entry */
|
jbe@0
|
1807 pgl_point *points; /* array of pgl_point for pgl_newentry */
|
jbe@0
|
1808 int points_buflen; /* maximum number of elements in points array */
|
jbe@0
|
1809 int done; /* return value of pgl_scan function */
|
jbe@0
|
1810 pgl_cluster *cluster; /* created cluster */
|
jbe@0
|
1811 /* lowercase input */
|
jbe@0
|
1812 str_lower = psprintf("%s", str);
|
jbe@0
|
1813 for (strptr=str_lower; *strptr; strptr++) {
|
jbe@0
|
1814 if (*strptr >= 'A' && *strptr <= 'Z') *strptr += 'a' - 'A';
|
jbe@0
|
1815 }
|
jbe@0
|
1816 /* reset reading position to start of (lowercase) string */
|
jbe@0
|
1817 strptr = str_lower;
|
jbe@0
|
1818 /* allocate initial buffer for entries */
|
jbe@0
|
1819 entries = palloc(entries_buflen * sizeof(pgl_newentry));
|
jbe@0
|
1820 /* parse until end of string */
|
jbe@0
|
1821 while (strptr[0]) {
|
jbe@0
|
1822 /* require previous white-space or closing parenthesis before next token */
|
jbe@0
|
1823 if (strptr != str_lower && !isspace(strptr[-1]) && strptr[-1] != ')') {
|
jbe@0
|
1824 goto pgl_ecluster_in_error;
|
jbe@0
|
1825 }
|
jbe@0
|
1826 /* ignore token "empty" */
|
jbe@0
|
1827 valid = 0; sscanf(strptr, " empty %n", &valid);
|
jbe@0
|
1828 if (valid) { strptr += valid; continue; }
|
jbe@0
|
1829 /* test for "point" token */
|
jbe@0
|
1830 valid = 0; sscanf(strptr, " point ( %n", &valid);
|
jbe@0
|
1831 if (valid) {
|
jbe@0
|
1832 strptr += valid;
|
jbe@0
|
1833 entrytype = PGL_ENTRY_POINT;
|
jbe@0
|
1834 goto pgl_ecluster_in_type_ok;
|
jbe@0
|
1835 }
|
jbe@0
|
1836 /* test for "path" token */
|
jbe@0
|
1837 valid = 0; sscanf(strptr, " path ( %n", &valid);
|
jbe@0
|
1838 if (valid) {
|
jbe@0
|
1839 strptr += valid;
|
jbe@0
|
1840 entrytype = PGL_ENTRY_PATH;
|
jbe@0
|
1841 goto pgl_ecluster_in_type_ok;
|
jbe@0
|
1842 }
|
jbe@0
|
1843 /* test for "outline" token */
|
jbe@0
|
1844 valid = 0; sscanf(strptr, " outline ( %n", &valid);
|
jbe@0
|
1845 if (valid) {
|
jbe@0
|
1846 strptr += valid;
|
jbe@0
|
1847 entrytype = PGL_ENTRY_OUTLINE;
|
jbe@0
|
1848 goto pgl_ecluster_in_type_ok;
|
jbe@0
|
1849 }
|
jbe@0
|
1850 /* test for "polygon" token */
|
jbe@0
|
1851 valid = 0; sscanf(strptr, " polygon ( %n", &valid);
|
jbe@0
|
1852 if (valid) {
|
jbe@0
|
1853 strptr += valid;
|
jbe@0
|
1854 entrytype = PGL_ENTRY_POLYGON;
|
jbe@0
|
1855 goto pgl_ecluster_in_type_ok;
|
jbe@0
|
1856 }
|
jbe@0
|
1857 /* error if no valid token found */
|
jbe@0
|
1858 goto pgl_ecluster_in_error;
|
jbe@0
|
1859 pgl_ecluster_in_type_ok:
|
jbe@0
|
1860 /* check if pgl_newentry array needs to grow */
|
jbe@0
|
1861 if (nentries == entries_buflen) {
|
jbe@0
|
1862 pgl_newentry *newbuf;
|
jbe@0
|
1863 entries_buflen *= 2;
|
jbe@0
|
1864 newbuf = palloc(entries_buflen * sizeof(pgl_newentry));
|
jbe@0
|
1865 memcpy(newbuf, entries, nentries * sizeof(pgl_newentry));
|
jbe@0
|
1866 pfree(entries);
|
jbe@0
|
1867 entries = newbuf;
|
jbe@0
|
1868 }
|
jbe@0
|
1869 /* reset number of points for current entry */
|
jbe@0
|
1870 npoints = 0;
|
jbe@0
|
1871 /* allocate array for points */
|
jbe@0
|
1872 points_buflen = 4;
|
jbe@0
|
1873 points = palloc(points_buflen * sizeof(pgl_point));
|
jbe@0
|
1874 /* parse until closing parenthesis */
|
jbe@0
|
1875 while (strptr[0] != ')') {
|
jbe@0
|
1876 /* error on unexpected end of string */
|
jbe@0
|
1877 if (strptr[0] == 0) goto pgl_ecluster_in_error;
|
jbe@0
|
1878 /* mark neither latitude nor longitude as read */
|
jbe@0
|
1879 done = PGL_SCAN_NONE;
|
jbe@0
|
1880 /* require white-space before second, third, etc. point */
|
jbe@0
|
1881 if (npoints != 0 && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
|
jbe@0
|
1882 /* scan latitude (or longitude) */
|
jbe@0
|
1883 done |= pgl_scan(&strptr, &lat, &lon);
|
jbe@0
|
1884 /* require white-space before second coordinate */
|
jbe@0
|
1885 if (strptr != str && !isspace(strptr[-1])) goto pgl_ecluster_in_error;
|
jbe@0
|
1886 /* scan longitude (or latitude) */
|
jbe@0
|
1887 done |= pgl_scan(&strptr, &lat, &lon);
|
jbe@0
|
1888 /* error unless both latitude and longitude were parsed */
|
jbe@0
|
1889 if (done != PGL_SCAN_LATLON) goto pgl_ecluster_in_error;
|
jbe@0
|
1890 /* throw error if number of points is too high */
|
jbe@0
|
1891 if (npoints_total == PGL_CLUSTER_MAXPOINTS) {
|
jbe@0
|
1892 ereport(ERROR, (
|
jbe@0
|
1893 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
jbe@0
|
1894 errmsg(
|
jbe@0
|
1895 "too many points for ecluster entry (maximum %i)",
|
jbe@0
|
1896 PGL_CLUSTER_MAXPOINTS
|
jbe@0
|
1897 )
|
jbe@0
|
1898 ));
|
jbe@0
|
1899 }
|
jbe@0
|
1900 /* check if pgl_point array needs to grow */
|
jbe@0
|
1901 if (npoints == points_buflen) {
|
jbe@0
|
1902 pgl_point *newbuf;
|
jbe@0
|
1903 points_buflen *= 2;
|
jbe@0
|
1904 newbuf = palloc(points_buflen * sizeof(pgl_point));
|
jbe@0
|
1905 memcpy(newbuf, points, npoints * sizeof(pgl_point));
|
jbe@0
|
1906 pfree(points);
|
jbe@0
|
1907 points = newbuf;
|
jbe@0
|
1908 }
|
jbe@0
|
1909 /* append point to pgl_point array (includes checks) */
|
jbe@0
|
1910 pgl_epoint_set_latlon(&(points[npoints++]), lat, lon);
|
jbe@0
|
1911 /* increase total number of points */
|
jbe@0
|
1912 npoints_total++;
|
jbe@0
|
1913 }
|
jbe@0
|
1914 /* error if entry has no points */
|
jbe@0
|
1915 if (!npoints) goto pgl_ecluster_in_error;
|
jbe@0
|
1916 /* entries with one point are automatically of type "point" */
|
jbe@0
|
1917 if (npoints == 1) entrytype = PGL_ENTRY_POINT;
|
jbe@0
|
1918 /* if entries have more than one point */
|
jbe@0
|
1919 else {
|
jbe@0
|
1920 /* throw error if entry type is "point" */
|
jbe@0
|
1921 if (entrytype == PGL_ENTRY_POINT) {
|
jbe@0
|
1922 ereport(ERROR, (
|
jbe@0
|
1923 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
jbe@0
|
1924 errmsg("invalid input syntax for type ecluster (point entry with more than one point)")
|
jbe@0
|
1925 ));
|
jbe@0
|
1926 }
|
jbe@0
|
1927 /* coerce outlines and polygons with more than 2 points to be a path */
|
jbe@0
|
1928 if (npoints == 2) entrytype = PGL_ENTRY_PATH;
|
jbe@0
|
1929 }
|
jbe@0
|
1930 /* append entry to pgl_newentry array */
|
jbe@0
|
1931 entries[nentries].entrytype = entrytype;
|
jbe@0
|
1932 entries[nentries].npoints = npoints;
|
jbe@0
|
1933 entries[nentries].points = points;
|
jbe@0
|
1934 nentries++;
|
jbe@0
|
1935 /* consume closing parenthesis */
|
jbe@0
|
1936 strptr++;
|
jbe@0
|
1937 /* consume white-space */
|
jbe@0
|
1938 while (isspace(strptr[0])) strptr++;
|
jbe@0
|
1939 }
|
jbe@0
|
1940 /* free lower case string */
|
jbe@0
|
1941 pfree(str_lower);
|
jbe@0
|
1942 /* create cluster from pgl_newentry array */
|
jbe@0
|
1943 cluster = pgl_new_cluster(nentries, entries);
|
jbe@0
|
1944 /* free pgl_newentry array */
|
jbe@0
|
1945 for (i=0; i<nentries; i++) pfree(entries[i].points);
|
jbe@0
|
1946 pfree(entries);
|
jbe@0
|
1947 /* set bounding circle of cluster and check east/west orientation */
|
jbe@0
|
1948 if (!pgl_finalize_cluster(cluster)) {
|
jbe@0
|
1949 ereport(ERROR, (
|
jbe@0
|
1950 errcode(ERRCODE_DATA_EXCEPTION),
|
jbe@0
|
1951 errmsg("can not determine east/west orientation for ecluster"),
|
jbe@0
|
1952 errhint("Ensure that each entry has a longitude span of less than 180 degrees.")
|
jbe@0
|
1953 ));
|
jbe@0
|
1954 }
|
jbe@0
|
1955 /* return cluster */
|
jbe@0
|
1956 PG_RETURN_POINTER(cluster);
|
jbe@0
|
1957 /* code to throw error */
|
jbe@0
|
1958 pgl_ecluster_in_error:
|
jbe@0
|
1959 ereport(ERROR, (
|
jbe@0
|
1960 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
jbe@0
|
1961 errmsg("invalid input syntax for type ecluster: \"%s\"", str)
|
jbe@0
|
1962 ));
|
jbe@0
|
1963 }
|
jbe@0
|
1964
|
jbe@0
|
1965 /* convert point ("epoint") to string representation */
|
jbe@0
|
1966 PG_FUNCTION_INFO_V1(pgl_epoint_out);
|
jbe@0
|
1967 Datum pgl_epoint_out(PG_FUNCTION_ARGS) {
|
jbe@0
|
1968 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
1969 char latstr[PGL_NUMBUFLEN];
|
jbe@0
|
1970 char lonstr[PGL_NUMBUFLEN];
|
jbe@0
|
1971 pgl_print_lat(latstr, point->lat);
|
jbe@0
|
1972 pgl_print_lon(lonstr, point->lon);
|
jbe@0
|
1973 PG_RETURN_CSTRING(psprintf("%s %s", latstr, lonstr));
|
jbe@0
|
1974 }
|
jbe@0
|
1975
|
jbe@0
|
1976 /* convert box ("ebox") to string representation */
|
jbe@0
|
1977 PG_FUNCTION_INFO_V1(pgl_ebox_out);
|
jbe@0
|
1978 Datum pgl_ebox_out(PG_FUNCTION_ARGS) {
|
jbe@0
|
1979 pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
|
jbe@0
|
1980 double lon_min = box->lon_min;
|
jbe@0
|
1981 double lon_max = box->lon_max;
|
jbe@0
|
1982 char lat_min_str[PGL_NUMBUFLEN];
|
jbe@0
|
1983 char lat_max_str[PGL_NUMBUFLEN];
|
jbe@0
|
1984 char lon_min_str[PGL_NUMBUFLEN];
|
jbe@0
|
1985 char lon_max_str[PGL_NUMBUFLEN];
|
jbe@0
|
1986 /* return string "empty" if box is set to be empty */
|
jbe@0
|
1987 if (box->lat_min > box->lat_max) PG_RETURN_CSTRING("empty");
|
jbe@0
|
1988 /* use boundaries exceeding W180 or E180 if 180th meridian is enclosed */
|
jbe@0
|
1989 /* (required since pgl_box_in orders the longitude boundaries) */
|
jbe@0
|
1990 if (lon_min > lon_max) {
|
jbe@0
|
1991 if (lon_min + lon_max >= 0) lon_min -= 360;
|
jbe@0
|
1992 else lon_max += 360;
|
jbe@0
|
1993 }
|
jbe@0
|
1994 /* format and return result */
|
jbe@0
|
1995 pgl_print_lat(lat_min_str, box->lat_min);
|
jbe@0
|
1996 pgl_print_lat(lat_max_str, box->lat_max);
|
jbe@0
|
1997 pgl_print_lon(lon_min_str, lon_min);
|
jbe@0
|
1998 pgl_print_lon(lon_max_str, lon_max);
|
jbe@0
|
1999 PG_RETURN_CSTRING(psprintf(
|
jbe@0
|
2000 "%s %s %s %s",
|
jbe@0
|
2001 lat_min_str, lon_min_str, lat_max_str, lon_max_str
|
jbe@0
|
2002 ));
|
jbe@0
|
2003 }
|
jbe@0
|
2004
|
jbe@0
|
2005 /* convert circle ("ecircle") to string representation */
|
jbe@0
|
2006 PG_FUNCTION_INFO_V1(pgl_ecircle_out);
|
jbe@0
|
2007 Datum pgl_ecircle_out(PG_FUNCTION_ARGS) {
|
jbe@0
|
2008 pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
|
jbe@0
|
2009 char latstr[PGL_NUMBUFLEN];
|
jbe@0
|
2010 char lonstr[PGL_NUMBUFLEN];
|
jbe@0
|
2011 char radstr[PGL_NUMBUFLEN];
|
jbe@0
|
2012 pgl_print_lat(latstr, circle->center.lat);
|
jbe@0
|
2013 pgl_print_lon(lonstr, circle->center.lon);
|
jbe@0
|
2014 pgl_print_float(radstr, circle->radius);
|
jbe@0
|
2015 PG_RETURN_CSTRING(psprintf("%s %s %s", latstr, lonstr, radstr));
|
jbe@0
|
2016 }
|
jbe@0
|
2017
|
jbe@0
|
2018 /* convert cluster ("ecluster") to string representation */
|
jbe@0
|
2019 PG_FUNCTION_INFO_V1(pgl_ecluster_out);
|
jbe@0
|
2020 Datum pgl_ecluster_out(PG_FUNCTION_ARGS) {
|
jbe@0
|
2021 pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
jbe@0
|
2022 char latstr[PGL_NUMBUFLEN]; /* string buffer for latitude */
|
jbe@0
|
2023 char lonstr[PGL_NUMBUFLEN]; /* string buffer for longitude */
|
jbe@0
|
2024 char ***strings; /* array of array of strings */
|
jbe@0
|
2025 char *string; /* string of current token */
|
jbe@0
|
2026 char *res, *resptr; /* result and pointer to current write position */
|
jbe@0
|
2027 size_t reslen = 1; /* length of result (init with 1 for terminator) */
|
jbe@0
|
2028 int npoints; /* number of points of current entry */
|
jbe@0
|
2029 int i, j; /* i: entry, j: point in entry */
|
jbe@0
|
2030 /* handle empty clusters */
|
jbe@0
|
2031 if (cluster->nentries == 0) {
|
jbe@0
|
2032 /* free detoasted cluster (if copy) */
|
jbe@0
|
2033 PG_FREE_IF_COPY(cluster, 0);
|
jbe@0
|
2034 /* return static result */
|
jbe@0
|
2035 PG_RETURN_CSTRING("empty");
|
jbe@0
|
2036 }
|
jbe@0
|
2037 /* allocate array of array of strings */
|
jbe@0
|
2038 strings = palloc(cluster->nentries * sizeof(char **));
|
jbe@0
|
2039 /* iterate over all entries in cluster */
|
jbe@0
|
2040 for (i=0; i<cluster->nentries; i++) {
|
jbe@0
|
2041 /* get number of points in entry */
|
jbe@0
|
2042 npoints = cluster->entries[i].npoints;
|
jbe@0
|
2043 /* allocate array of strings (one string for each point plus two extra) */
|
jbe@0
|
2044 strings[i] = palloc((2 + npoints) * sizeof(char *));
|
jbe@0
|
2045 /* determine opening string */
|
jbe@0
|
2046 switch (cluster->entries[i].entrytype) {
|
jbe@0
|
2047 case PGL_ENTRY_POINT: string = (i==0)?"point (" :" point ("; break;
|
jbe@0
|
2048 case PGL_ENTRY_PATH: string = (i==0)?"path (" :" path ("; break;
|
jbe@0
|
2049 case PGL_ENTRY_OUTLINE: string = (i==0)?"outline (":" outline ("; break;
|
jbe@0
|
2050 case PGL_ENTRY_POLYGON: string = (i==0)?"polygon (":" polygon ("; break;
|
jbe@0
|
2051 default: string = (i==0)?"unknown" :" unknown";
|
jbe@0
|
2052 }
|
jbe@0
|
2053 /* use opening string as first string in array */
|
jbe@0
|
2054 strings[i][0] = string;
|
jbe@0
|
2055 /* update result length (for allocating result string later) */
|
jbe@0
|
2056 reslen += strlen(string);
|
jbe@0
|
2057 /* iterate over all points */
|
jbe@0
|
2058 for (j=0; j<npoints; j++) {
|
jbe@0
|
2059 /* create string representation of point */
|
jbe@0
|
2060 pgl_print_lat(latstr, PGL_ENTRY_POINTS(cluster, i)[j].lat);
|
jbe@0
|
2061 pgl_print_lon(lonstr, PGL_ENTRY_POINTS(cluster, i)[j].lon);
|
jbe@0
|
2062 string = psprintf((j == 0) ? "%s %s" : " %s %s", latstr, lonstr);
|
jbe@0
|
2063 /* copy string pointer to string array */
|
jbe@0
|
2064 strings[i][j+1] = string;
|
jbe@0
|
2065 /* update result length (for allocating result string later) */
|
jbe@0
|
2066 reslen += strlen(string);
|
jbe@0
|
2067 }
|
jbe@0
|
2068 /* use closing parenthesis as last string in array */
|
jbe@0
|
2069 strings[i][npoints+1] = ")";
|
jbe@0
|
2070 /* update result length (for allocating result string later) */
|
jbe@0
|
2071 reslen++;
|
jbe@0
|
2072 }
|
jbe@0
|
2073 /* allocate result string */
|
jbe@0
|
2074 res = palloc(reslen);
|
jbe@0
|
2075 /* set write pointer to begin of result string */
|
jbe@0
|
2076 resptr = res;
|
jbe@0
|
2077 /* copy strings into result string */
|
jbe@0
|
2078 for (i=0; i<cluster->nentries; i++) {
|
jbe@0
|
2079 npoints = cluster->entries[i].npoints;
|
jbe@0
|
2080 for (j=0; j<npoints+2; j++) {
|
jbe@0
|
2081 string = strings[i][j];
|
jbe@0
|
2082 strcpy(resptr, string);
|
jbe@0
|
2083 resptr += strlen(string);
|
jbe@0
|
2084 /* free strings allocated by psprintf */
|
jbe@0
|
2085 if (j != 0 && j != npoints+1) pfree(string);
|
jbe@0
|
2086 }
|
jbe@0
|
2087 /* free array of strings */
|
jbe@0
|
2088 pfree(strings[i]);
|
jbe@0
|
2089 }
|
jbe@0
|
2090 /* free array of array of strings */
|
jbe@0
|
2091 pfree(strings);
|
jbe@0
|
2092 /* free detoasted cluster (if copy) */
|
jbe@0
|
2093 PG_FREE_IF_COPY(cluster, 0);
|
jbe@0
|
2094 /* return result */
|
jbe@0
|
2095 PG_RETURN_CSTRING(res);
|
jbe@0
|
2096 }
|
jbe@0
|
2097
|
jbe@0
|
2098 /* binary input function for point ("epoint") */
|
jbe@0
|
2099 PG_FUNCTION_INFO_V1(pgl_epoint_recv);
|
jbe@0
|
2100 Datum pgl_epoint_recv(PG_FUNCTION_ARGS) {
|
jbe@0
|
2101 StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
|
jbe@0
|
2102 pgl_point *point = (pgl_point *)palloc(sizeof(pgl_point));
|
jbe@0
|
2103 point->lat = pq_getmsgfloat8(buf);
|
jbe@0
|
2104 point->lon = pq_getmsgfloat8(buf);
|
jbe@0
|
2105 PG_RETURN_POINTER(point);
|
jbe@0
|
2106 }
|
jbe@0
|
2107
|
jbe@0
|
2108 /* binary input function for box ("ebox") */
|
jbe@0
|
2109 PG_FUNCTION_INFO_V1(pgl_ebox_recv);
|
jbe@0
|
2110 Datum pgl_ebox_recv(PG_FUNCTION_ARGS) {
|
jbe@0
|
2111 StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
|
jbe@0
|
2112 pgl_box *box = (pgl_box *)palloc(sizeof(pgl_box));
|
jbe@0
|
2113 box->lat_min = pq_getmsgfloat8(buf);
|
jbe@0
|
2114 box->lat_max = pq_getmsgfloat8(buf);
|
jbe@0
|
2115 box->lon_min = pq_getmsgfloat8(buf);
|
jbe@0
|
2116 box->lon_max = pq_getmsgfloat8(buf);
|
jbe@0
|
2117 PG_RETURN_POINTER(box);
|
jbe@0
|
2118 }
|
jbe@0
|
2119
|
jbe@0
|
2120 /* binary input function for circle ("ecircle") */
|
jbe@0
|
2121 PG_FUNCTION_INFO_V1(pgl_ecircle_recv);
|
jbe@0
|
2122 Datum pgl_ecircle_recv(PG_FUNCTION_ARGS) {
|
jbe@0
|
2123 StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
|
jbe@0
|
2124 pgl_circle *circle = (pgl_circle *)palloc(sizeof(pgl_circle));
|
jbe@0
|
2125 circle->center.lat = pq_getmsgfloat8(buf);
|
jbe@0
|
2126 circle->center.lon = pq_getmsgfloat8(buf);
|
jbe@0
|
2127 circle->radius = pq_getmsgfloat8(buf);
|
jbe@0
|
2128 PG_RETURN_POINTER(circle);
|
jbe@0
|
2129 }
|
jbe@0
|
2130
|
jbe@0
|
2131 /* TODO: binary receive function for cluster */
|
jbe@0
|
2132
|
jbe@0
|
2133 /* binary output function for point ("epoint") */
|
jbe@0
|
2134 PG_FUNCTION_INFO_V1(pgl_epoint_send);
|
jbe@0
|
2135 Datum pgl_epoint_send(PG_FUNCTION_ARGS) {
|
jbe@0
|
2136 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2137 StringInfoData buf;
|
jbe@0
|
2138 pq_begintypsend(&buf);
|
jbe@0
|
2139 pq_sendfloat8(&buf, point->lat);
|
jbe@0
|
2140 pq_sendfloat8(&buf, point->lon);
|
jbe@0
|
2141 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
jbe@0
|
2142 }
|
jbe@0
|
2143
|
jbe@0
|
2144 /* binary output function for box ("ebox") */
|
jbe@0
|
2145 PG_FUNCTION_INFO_V1(pgl_ebox_send);
|
jbe@0
|
2146 Datum pgl_ebox_send(PG_FUNCTION_ARGS) {
|
jbe@0
|
2147 pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
|
jbe@0
|
2148 StringInfoData buf;
|
jbe@0
|
2149 pq_begintypsend(&buf);
|
jbe@0
|
2150 pq_sendfloat8(&buf, box->lat_min);
|
jbe@0
|
2151 pq_sendfloat8(&buf, box->lat_max);
|
jbe@0
|
2152 pq_sendfloat8(&buf, box->lon_min);
|
jbe@0
|
2153 pq_sendfloat8(&buf, box->lon_max);
|
jbe@0
|
2154 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
jbe@0
|
2155 }
|
jbe@0
|
2156
|
jbe@0
|
2157 /* binary output function for circle ("ecircle") */
|
jbe@0
|
2158 PG_FUNCTION_INFO_V1(pgl_ecircle_send);
|
jbe@0
|
2159 Datum pgl_ecircle_send(PG_FUNCTION_ARGS) {
|
jbe@0
|
2160 pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
|
jbe@0
|
2161 StringInfoData buf;
|
jbe@0
|
2162 pq_begintypsend(&buf);
|
jbe@0
|
2163 pq_sendfloat8(&buf, circle->center.lat);
|
jbe@0
|
2164 pq_sendfloat8(&buf, circle->center.lon);
|
jbe@0
|
2165 pq_sendfloat8(&buf, circle->radius);
|
jbe@0
|
2166 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
jbe@0
|
2167 }
|
jbe@0
|
2168
|
jbe@0
|
2169 /* TODO: binary send functions for cluster */
|
jbe@0
|
2170
|
jbe@0
|
2171 /* cast point ("epoint") to box ("ebox") */
|
jbe@0
|
2172 PG_FUNCTION_INFO_V1(pgl_epoint_to_ebox);
|
jbe@0
|
2173 Datum pgl_epoint_to_ebox(PG_FUNCTION_ARGS) {
|
jbe@0
|
2174 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2175 pgl_box *box = palloc(sizeof(pgl_box));
|
jbe@0
|
2176 box->lat_min = point->lat;
|
jbe@0
|
2177 box->lat_max = point->lat;
|
jbe@0
|
2178 box->lon_min = point->lon;
|
jbe@0
|
2179 box->lon_max = point->lon;
|
jbe@0
|
2180 PG_RETURN_POINTER(box);
|
jbe@0
|
2181 }
|
jbe@0
|
2182
|
jbe@0
|
2183 /* cast point ("epoint") to circle ("ecircle") */
|
jbe@0
|
2184 PG_FUNCTION_INFO_V1(pgl_epoint_to_ecircle);
|
jbe@0
|
2185 Datum pgl_epoint_to_ecircle(PG_FUNCTION_ARGS) {
|
jbe@0
|
2186 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2187 pgl_circle *circle = palloc(sizeof(pgl_box));
|
jbe@0
|
2188 circle->center = *point;
|
jbe@0
|
2189 circle->radius = 0;
|
jbe@0
|
2190 PG_RETURN_POINTER(circle);
|
jbe@0
|
2191 }
|
jbe@0
|
2192
|
jbe@0
|
2193 /* cast point ("epoint") to cluster ("ecluster") */
|
jbe@0
|
2194 PG_FUNCTION_INFO_V1(pgl_epoint_to_ecluster);
|
jbe@0
|
2195 Datum pgl_epoint_to_ecluster(PG_FUNCTION_ARGS) {
|
jbe@0
|
2196 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2197 pgl_newentry entry;
|
jbe@0
|
2198 entry.entrytype = PGL_ENTRY_POINT;
|
jbe@0
|
2199 entry.npoints = 1;
|
jbe@0
|
2200 entry.points = point;
|
jbe@0
|
2201 PG_RETURN_POINTER(pgl_new_cluster(1, &entry));
|
jbe@0
|
2202 }
|
jbe@0
|
2203
|
jbe@0
|
2204 /* cast box ("ebox") to cluster ("ecluster") */
|
jbe@0
|
2205 #define pgl_ebox_to_ecluster_macro(i, a, b) \
|
jbe@0
|
2206 entries[i].entrytype = PGL_ENTRY_POLYGON; \
|
jbe@0
|
2207 entries[i].npoints = 4; \
|
jbe@0
|
2208 entries[i].points = points[i]; \
|
jbe@0
|
2209 points[i][0].lat = box->lat_min; \
|
jbe@0
|
2210 points[i][0].lon = (a); \
|
jbe@0
|
2211 points[i][1].lat = box->lat_min; \
|
jbe@0
|
2212 points[i][1].lon = (b); \
|
jbe@0
|
2213 points[i][2].lat = box->lat_max; \
|
jbe@0
|
2214 points[i][2].lon = (b); \
|
jbe@0
|
2215 points[i][3].lat = box->lat_max; \
|
jbe@0
|
2216 points[i][3].lon = (a);
|
jbe@0
|
2217 PG_FUNCTION_INFO_V1(pgl_ebox_to_ecluster);
|
jbe@0
|
2218 Datum pgl_ebox_to_ecluster(PG_FUNCTION_ARGS) {
|
jbe@0
|
2219 pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
|
jbe@0
|
2220 double lon, dlon;
|
jbe@0
|
2221 int nentries;
|
jbe@0
|
2222 pgl_newentry entries[3];
|
jbe@0
|
2223 pgl_point points[3][4];
|
jbe@0
|
2224 if (box->lat_min > box->lat_max) {
|
jbe@0
|
2225 nentries = 0;
|
jbe@0
|
2226 } else if (box->lon_min > box->lon_max) {
|
jbe@0
|
2227 if (box->lon_min < 0) {
|
jbe@0
|
2228 lon = pgl_round((box->lon_min + 180) / 2.0);
|
jbe@0
|
2229 nentries = 3;
|
jbe@0
|
2230 pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
|
jbe@0
|
2231 pgl_ebox_to_ecluster_macro(1, lon, 180);
|
jbe@0
|
2232 pgl_ebox_to_ecluster_macro(2, -180, box->lon_max);
|
jbe@0
|
2233 } else if (box->lon_max > 0) {
|
jbe@0
|
2234 lon = pgl_round((box->lon_max - 180) / 2.0);
|
jbe@0
|
2235 nentries = 3;
|
jbe@0
|
2236 pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
|
jbe@0
|
2237 pgl_ebox_to_ecluster_macro(1, -180, lon);
|
jbe@0
|
2238 pgl_ebox_to_ecluster_macro(2, lon, box->lon_max);
|
jbe@0
|
2239 } else {
|
jbe@0
|
2240 nentries = 2;
|
jbe@0
|
2241 pgl_ebox_to_ecluster_macro(0, box->lon_min, 180);
|
jbe@0
|
2242 pgl_ebox_to_ecluster_macro(1, -180, box->lon_max);
|
jbe@0
|
2243 }
|
jbe@0
|
2244 } else {
|
jbe@0
|
2245 dlon = pgl_round(box->lon_max - box->lon_min);
|
jbe@0
|
2246 if (dlon < 180) {
|
jbe@0
|
2247 nentries = 1;
|
jbe@0
|
2248 pgl_ebox_to_ecluster_macro(0, box->lon_min, box->lon_max);
|
jbe@0
|
2249 } else {
|
jbe@0
|
2250 lon = pgl_round((box->lon_min + box->lon_max) / 2.0);
|
jbe@0
|
2251 if (
|
jbe@0
|
2252 pgl_round(lon - box->lon_min) < 180 &&
|
jbe@0
|
2253 pgl_round(box->lon_max - lon) < 180
|
jbe@0
|
2254 ) {
|
jbe@0
|
2255 nentries = 2;
|
jbe@0
|
2256 pgl_ebox_to_ecluster_macro(0, box->lon_min, lon);
|
jbe@0
|
2257 pgl_ebox_to_ecluster_macro(1, lon, box->lon_max);
|
jbe@0
|
2258 } else {
|
jbe@0
|
2259 nentries = 3;
|
jbe@0
|
2260 pgl_ebox_to_ecluster_macro(0, box->lon_min, -60);
|
jbe@0
|
2261 pgl_ebox_to_ecluster_macro(1, -60, 60);
|
jbe@0
|
2262 pgl_ebox_to_ecluster_macro(2, 60, box->lon_max);
|
jbe@0
|
2263 }
|
jbe@0
|
2264 }
|
jbe@0
|
2265 }
|
jbe@0
|
2266 PG_RETURN_POINTER(pgl_new_cluster(nentries, entries));
|
jbe@0
|
2267 }
|
jbe@0
|
2268
|
jbe@0
|
2269 /* extract latitude from point ("epoint") */
|
jbe@0
|
2270 PG_FUNCTION_INFO_V1(pgl_epoint_lat);
|
jbe@0
|
2271 Datum pgl_epoint_lat(PG_FUNCTION_ARGS) {
|
jbe@0
|
2272 PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lat);
|
jbe@0
|
2273 }
|
jbe@0
|
2274
|
jbe@0
|
2275 /* extract longitude from point ("epoint") */
|
jbe@0
|
2276 PG_FUNCTION_INFO_V1(pgl_epoint_lon);
|
jbe@0
|
2277 Datum pgl_epoint_lon(PG_FUNCTION_ARGS) {
|
jbe@0
|
2278 PG_RETURN_FLOAT8(((pgl_point *)PG_GETARG_POINTER(0))->lon);
|
jbe@0
|
2279 }
|
jbe@0
|
2280
|
jbe@0
|
2281 /* extract minimum latitude from box ("ebox") */
|
jbe@0
|
2282 PG_FUNCTION_INFO_V1(pgl_ebox_lat_min);
|
jbe@0
|
2283 Datum pgl_ebox_lat_min(PG_FUNCTION_ARGS) {
|
jbe@0
|
2284 PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_min);
|
jbe@0
|
2285 }
|
jbe@0
|
2286
|
jbe@0
|
2287 /* extract maximum latitude from box ("ebox") */
|
jbe@0
|
2288 PG_FUNCTION_INFO_V1(pgl_ebox_lat_max);
|
jbe@0
|
2289 Datum pgl_ebox_lat_max(PG_FUNCTION_ARGS) {
|
jbe@0
|
2290 PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lat_max);
|
jbe@0
|
2291 }
|
jbe@0
|
2292
|
jbe@0
|
2293 /* extract minimum longitude from box ("ebox") */
|
jbe@0
|
2294 PG_FUNCTION_INFO_V1(pgl_ebox_lon_min);
|
jbe@0
|
2295 Datum pgl_ebox_lon_min(PG_FUNCTION_ARGS) {
|
jbe@0
|
2296 PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_min);
|
jbe@0
|
2297 }
|
jbe@0
|
2298
|
jbe@0
|
2299 /* extract maximum longitude from box ("ebox") */
|
jbe@0
|
2300 PG_FUNCTION_INFO_V1(pgl_ebox_lon_max);
|
jbe@0
|
2301 Datum pgl_ebox_lon_max(PG_FUNCTION_ARGS) {
|
jbe@0
|
2302 PG_RETURN_FLOAT8(((pgl_box *)PG_GETARG_POINTER(0))->lon_max);
|
jbe@0
|
2303 }
|
jbe@0
|
2304
|
jbe@0
|
2305 /* extract center point from circle ("ecircle") */
|
jbe@0
|
2306 PG_FUNCTION_INFO_V1(pgl_ecircle_center);
|
jbe@0
|
2307 Datum pgl_ecircle_center(PG_FUNCTION_ARGS) {
|
jbe@0
|
2308 PG_RETURN_POINTER(&(((pgl_circle *)PG_GETARG_POINTER(0))->center));
|
jbe@0
|
2309 }
|
jbe@0
|
2310
|
jbe@0
|
2311 /* extract radius from circle ("ecircle") */
|
jbe@0
|
2312 PG_FUNCTION_INFO_V1(pgl_ecircle_radius);
|
jbe@0
|
2313 Datum pgl_ecircle_radius(PG_FUNCTION_ARGS) {
|
jbe@0
|
2314 PG_RETURN_FLOAT8(((pgl_circle *)PG_GETARG_POINTER(0))->radius);
|
jbe@0
|
2315 }
|
jbe@0
|
2316
|
jbe@0
|
2317 /* check if point is inside box (overlap operator "&&") in SQL */
|
jbe@0
|
2318 PG_FUNCTION_INFO_V1(pgl_epoint_ebox_overlap);
|
jbe@0
|
2319 Datum pgl_epoint_ebox_overlap(PG_FUNCTION_ARGS) {
|
jbe@0
|
2320 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2321 pgl_box *box = (pgl_box *)PG_GETARG_POINTER(1);
|
jbe@0
|
2322 PG_RETURN_BOOL(pgl_point_in_box(point, box));
|
jbe@0
|
2323 }
|
jbe@0
|
2324
|
jbe@0
|
2325 /* check if point is inside circle (overlap operator "&&") in SQL */
|
jbe@0
|
2326 PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_overlap);
|
jbe@0
|
2327 Datum pgl_epoint_ecircle_overlap(PG_FUNCTION_ARGS) {
|
jbe@0
|
2328 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2329 pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
|
jbe@0
|
2330 PG_RETURN_BOOL(
|
jbe@0
|
2331 pgl_distance(
|
jbe@0
|
2332 point->lat, point->lon,
|
jbe@0
|
2333 circle->center.lat, circle->center.lon
|
jbe@0
|
2334 ) <= circle->radius
|
jbe@0
|
2335 );
|
jbe@0
|
2336 }
|
jbe@0
|
2337
|
jbe@0
|
2338 /* check if point is inside cluster (overlap operator "&&") in SQL */
|
jbe@0
|
2339 PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_overlap);
|
jbe@0
|
2340 Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
|
jbe@0
|
2341 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2342 pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@16
|
2343 bool retval;
|
jbe@16
|
2344 /* points outside bounding circle are always assumed to be non-overlapping
|
jbe@16
|
2345 (necessary for consistent table and index scans) */
|
jbe@16
|
2346 if (
|
jbe@16
|
2347 pgl_distance(
|
jbe@16
|
2348 point->lat, point->lon,
|
jbe@16
|
2349 cluster->bounding.center.lat, cluster->bounding.center.lon
|
jbe@16
|
2350 ) > cluster->bounding.radius
|
jbe@16
|
2351 ) retval = false;
|
jbe@20
|
2352 else retval = pgl_point_in_cluster(point, cluster, false);
|
jbe@0
|
2353 PG_FREE_IF_COPY(cluster, 1);
|
jbe@0
|
2354 PG_RETURN_BOOL(retval);
|
jbe@0
|
2355 }
|
jbe@0
|
2356
|
jbe@10
|
2357 /* check if point may be inside cluster (lossy overl. operator "&&+") in SQL */
|
jbe@10
|
2358 PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_may_overlap);
|
jbe@10
|
2359 Datum pgl_epoint_ecluster_may_overlap(PG_FUNCTION_ARGS) {
|
jbe@10
|
2360 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@10
|
2361 pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@10
|
2362 bool retval = pgl_distance(
|
jbe@10
|
2363 point->lat, point->lon,
|
jbe@10
|
2364 cluster->bounding.center.lat, cluster->bounding.center.lon
|
jbe@10
|
2365 ) <= cluster->bounding.radius;
|
jbe@10
|
2366 PG_FREE_IF_COPY(cluster, 1);
|
jbe@10
|
2367 PG_RETURN_BOOL(retval);
|
jbe@10
|
2368 }
|
jbe@10
|
2369
|
jbe@0
|
2370 /* check if two boxes overlap (overlap operator "&&") in SQL */
|
jbe@0
|
2371 PG_FUNCTION_INFO_V1(pgl_ebox_overlap);
|
jbe@0
|
2372 Datum pgl_ebox_overlap(PG_FUNCTION_ARGS) {
|
jbe@0
|
2373 pgl_box *box1 = (pgl_box *)PG_GETARG_POINTER(0);
|
jbe@0
|
2374 pgl_box *box2 = (pgl_box *)PG_GETARG_POINTER(1);
|
jbe@0
|
2375 PG_RETURN_BOOL(pgl_boxes_overlap(box1, box2));
|
jbe@0
|
2376 }
|
jbe@0
|
2377
|
jbe@10
|
2378 /* check if box and circle may overlap (lossy overl. operator "&&+") in SQL */
|
jbe@10
|
2379 PG_FUNCTION_INFO_V1(pgl_ebox_ecircle_may_overlap);
|
jbe@10
|
2380 Datum pgl_ebox_ecircle_may_overlap(PG_FUNCTION_ARGS) {
|
jbe@10
|
2381 pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
|
jbe@10
|
2382 pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
|
jbe@10
|
2383 PG_RETURN_BOOL(
|
jbe@10
|
2384 pgl_estimate_point_box_distance(&circle->center, box) <= circle->radius
|
jbe@10
|
2385 );
|
jbe@10
|
2386 }
|
jbe@10
|
2387
|
jbe@10
|
2388 /* check if box and cluster may overlap (lossy overl. operator "&&+") in SQL */
|
jbe@10
|
2389 PG_FUNCTION_INFO_V1(pgl_ebox_ecluster_may_overlap);
|
jbe@10
|
2390 Datum pgl_ebox_ecluster_may_overlap(PG_FUNCTION_ARGS) {
|
jbe@10
|
2391 pgl_box *box = (pgl_box *)PG_GETARG_POINTER(0);
|
jbe@10
|
2392 pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@10
|
2393 bool retval = pgl_estimate_point_box_distance(
|
jbe@10
|
2394 &cluster->bounding.center,
|
jbe@10
|
2395 box
|
jbe@10
|
2396 ) <= cluster->bounding.radius;
|
jbe@10
|
2397 PG_FREE_IF_COPY(cluster, 1);
|
jbe@10
|
2398 PG_RETURN_BOOL(retval);
|
jbe@10
|
2399 }
|
jbe@10
|
2400
|
jbe@0
|
2401 /* check if two circles overlap (overlap operator "&&") in SQL */
|
jbe@0
|
2402 PG_FUNCTION_INFO_V1(pgl_ecircle_overlap);
|
jbe@0
|
2403 Datum pgl_ecircle_overlap(PG_FUNCTION_ARGS) {
|
jbe@0
|
2404 pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
|
jbe@0
|
2405 pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
|
jbe@0
|
2406 PG_RETURN_BOOL(
|
jbe@0
|
2407 pgl_distance(
|
jbe@0
|
2408 circle1->center.lat, circle1->center.lon,
|
jbe@0
|
2409 circle2->center.lat, circle2->center.lon
|
jbe@0
|
2410 ) <= circle1->radius + circle2->radius
|
jbe@0
|
2411 );
|
jbe@0
|
2412 }
|
jbe@0
|
2413
|
jbe@0
|
2414 /* check if circle and cluster overlap (overlap operator "&&") in SQL */
|
jbe@0
|
2415 PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_overlap);
|
jbe@0
|
2416 Datum pgl_ecircle_ecluster_overlap(PG_FUNCTION_ARGS) {
|
jbe@0
|
2417 pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
|
jbe@0
|
2418 pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@0
|
2419 bool retval = (
|
jbe@0
|
2420 pgl_point_cluster_distance(&(circle->center), cluster) <= circle->radius
|
jbe@0
|
2421 );
|
jbe@0
|
2422 PG_FREE_IF_COPY(cluster, 1);
|
jbe@0
|
2423 PG_RETURN_BOOL(retval);
|
jbe@0
|
2424 }
|
jbe@0
|
2425
|
jbe@17
|
2426 /* check if circle and cluster may overlap (l. ov. operator "&&+") in SQL */
|
jbe@10
|
2427 PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_may_overlap);
|
jbe@10
|
2428 Datum pgl_ecircle_ecluster_may_overlap(PG_FUNCTION_ARGS) {
|
jbe@10
|
2429 pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
|
jbe@10
|
2430 pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@10
|
2431 bool retval = pgl_distance(
|
jbe@10
|
2432 circle->center.lat, circle->center.lon,
|
jbe@10
|
2433 cluster->bounding.center.lat, cluster->bounding.center.lon
|
jbe@10
|
2434 ) <= circle->radius + cluster->bounding.radius;
|
jbe@10
|
2435 PG_FREE_IF_COPY(cluster, 1);
|
jbe@10
|
2436 PG_RETURN_BOOL(retval);
|
jbe@10
|
2437 }
|
jbe@10
|
2438
|
jbe@16
|
2439 /* check if two clusters overlap (overlap operator "&&") in SQL */
|
jbe@16
|
2440 PG_FUNCTION_INFO_V1(pgl_ecluster_overlap);
|
jbe@16
|
2441 Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) {
|
jbe@16
|
2442 pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
jbe@16
|
2443 pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@16
|
2444 bool retval;
|
jbe@16
|
2445 /* clusters with non-touching bounding circles are always assumed to be
|
jbe@16
|
2446 non-overlapping (improves performance and is necessary for consistent
|
jbe@16
|
2447 table and index scans) */
|
jbe@16
|
2448 if (
|
jbe@16
|
2449 pgl_distance(
|
jbe@16
|
2450 cluster1->bounding.center.lat, cluster1->bounding.center.lon,
|
jbe@16
|
2451 cluster2->bounding.center.lat, cluster2->bounding.center.lon
|
jbe@16
|
2452 ) > cluster1->bounding.radius + cluster2->bounding.radius
|
jbe@16
|
2453 ) retval = false;
|
jbe@16
|
2454 else retval = pgl_clusters_overlap(cluster1, cluster2);
|
jbe@16
|
2455 PG_FREE_IF_COPY(cluster1, 0);
|
jbe@16
|
2456 PG_FREE_IF_COPY(cluster2, 1);
|
jbe@16
|
2457 PG_RETURN_BOOL(retval);
|
jbe@16
|
2458 }
|
jbe@16
|
2459
|
jbe@10
|
2460 /* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */
|
jbe@10
|
2461 PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap);
|
jbe@10
|
2462 Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) {
|
jbe@10
|
2463 pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
jbe@10
|
2464 pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@10
|
2465 bool retval = pgl_distance(
|
jbe@10
|
2466 cluster1->bounding.center.lat, cluster1->bounding.center.lon,
|
jbe@10
|
2467 cluster2->bounding.center.lat, cluster2->bounding.center.lon
|
jbe@10
|
2468 ) <= cluster1->bounding.radius + cluster2->bounding.radius;
|
jbe@10
|
2469 PG_FREE_IF_COPY(cluster1, 0);
|
jbe@10
|
2470 PG_FREE_IF_COPY(cluster2, 1);
|
jbe@10
|
2471 PG_RETURN_BOOL(retval);
|
jbe@10
|
2472 }
|
jbe@10
|
2473
|
jbe@16
|
2474 /* check if second cluster is in first cluster (cont. operator "@>) in SQL */
|
jbe@16
|
2475 PG_FUNCTION_INFO_V1(pgl_ecluster_contains);
|
jbe@16
|
2476 Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) {
|
jbe@16
|
2477 pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
jbe@16
|
2478 pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@16
|
2479 bool retval;
|
jbe@16
|
2480 /* clusters with non-touching bounding circles are always assumed to be
|
jbe@16
|
2481 non-overlapping (improves performance and is necessary for consistent
|
jbe@16
|
2482 table and index scans) */
|
jbe@16
|
2483 if (
|
jbe@16
|
2484 pgl_distance(
|
jbe@16
|
2485 outer->bounding.center.lat, outer->bounding.center.lon,
|
jbe@16
|
2486 inner->bounding.center.lat, inner->bounding.center.lon
|
jbe@16
|
2487 ) > outer->bounding.radius + inner->bounding.radius
|
jbe@16
|
2488 ) retval = false;
|
jbe@16
|
2489 else retval = pgl_cluster_in_cluster(outer, inner);
|
jbe@16
|
2490 PG_FREE_IF_COPY(outer, 0);
|
jbe@16
|
2491 PG_FREE_IF_COPY(inner, 1);
|
jbe@16
|
2492 PG_RETURN_BOOL(retval);
|
jbe@16
|
2493 }
|
jbe@16
|
2494
|
jbe@0
|
2495 /* calculate distance between two points ("<->" operator) in SQL */
|
jbe@0
|
2496 PG_FUNCTION_INFO_V1(pgl_epoint_distance);
|
jbe@0
|
2497 Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
|
jbe@0
|
2498 pgl_point *point1 = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2499 pgl_point *point2 = (pgl_point *)PG_GETARG_POINTER(1);
|
jbe@0
|
2500 PG_RETURN_FLOAT8(pgl_distance(
|
jbe@0
|
2501 point1->lat, point1->lon, point2->lat, point2->lon
|
jbe@0
|
2502 ));
|
jbe@0
|
2503 }
|
jbe@0
|
2504
|
jbe@0
|
2505 /* calculate point to circle distance ("<->" operator) in SQL */
|
jbe@0
|
2506 PG_FUNCTION_INFO_V1(pgl_epoint_ecircle_distance);
|
jbe@0
|
2507 Datum pgl_epoint_ecircle_distance(PG_FUNCTION_ARGS) {
|
jbe@0
|
2508 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2509 pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(1);
|
jbe@0
|
2510 double distance = pgl_distance(
|
jbe@0
|
2511 point->lat, point->lon, circle->center.lat, circle->center.lon
|
jbe@0
|
2512 ) - circle->radius;
|
jbe@0
|
2513 PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
|
jbe@0
|
2514 }
|
jbe@0
|
2515
|
jbe@0
|
2516 /* calculate point to cluster distance ("<->" operator) in SQL */
|
jbe@0
|
2517 PG_FUNCTION_INFO_V1(pgl_epoint_ecluster_distance);
|
jbe@0
|
2518 Datum pgl_epoint_ecluster_distance(PG_FUNCTION_ARGS) {
|
jbe@0
|
2519 pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
|
jbe@0
|
2520 pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@0
|
2521 double distance = pgl_point_cluster_distance(point, cluster);
|
jbe@0
|
2522 PG_FREE_IF_COPY(cluster, 1);
|
jbe@0
|
2523 PG_RETURN_FLOAT8(distance);
|
jbe@0
|
2524 }
|
jbe@0
|
2525
|
jbe@0
|
2526 /* calculate distance between two circles ("<->" operator) in SQL */
|
jbe@0
|
2527 PG_FUNCTION_INFO_V1(pgl_ecircle_distance);
|
jbe@0
|
2528 Datum pgl_ecircle_distance(PG_FUNCTION_ARGS) {
|
jbe@0
|
2529 pgl_circle *circle1 = (pgl_circle *)PG_GETARG_POINTER(0);
|
jbe@0
|
2530 pgl_circle *circle2 = (pgl_circle *)PG_GETARG_POINTER(1);
|
jbe@0
|
2531 double distance = pgl_distance(
|
jbe@0
|
2532 circle1->center.lat, circle1->center.lon,
|
jbe@0
|
2533 circle2->center.lat, circle2->center.lon
|
jbe@0
|
2534 ) - (circle1->radius + circle2->radius);
|
jbe@0
|
2535 PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
|
jbe@0
|
2536 }
|
jbe@0
|
2537
|
jbe@0
|
2538 /* calculate circle to cluster distance ("<->" operator) in SQL */
|
jbe@0
|
2539 PG_FUNCTION_INFO_V1(pgl_ecircle_ecluster_distance);
|
jbe@0
|
2540 Datum pgl_ecircle_ecluster_distance(PG_FUNCTION_ARGS) {
|
jbe@0
|
2541 pgl_circle *circle = (pgl_circle *)PG_GETARG_POINTER(0);
|
jbe@0
|
2542 pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@0
|
2543 double distance = (
|
jbe@0
|
2544 pgl_point_cluster_distance(&(circle->center), cluster) - circle->radius
|
jbe@0
|
2545 );
|
jbe@0
|
2546 PG_FREE_IF_COPY(cluster, 1);
|
jbe@0
|
2547 PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
|
jbe@0
|
2548 }
|
jbe@0
|
2549
|
jbe@16
|
2550 /* calculate distance between two clusters ("<->" operator) in SQL */
|
jbe@16
|
2551 PG_FUNCTION_INFO_V1(pgl_ecluster_distance);
|
jbe@16
|
2552 Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) {
|
jbe@16
|
2553 pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
jbe@16
|
2554 pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@16
|
2555 double retval = pgl_cluster_distance(cluster1, cluster2);
|
jbe@16
|
2556 PG_FREE_IF_COPY(cluster1, 0);
|
jbe@16
|
2557 PG_FREE_IF_COPY(cluster2, 1);
|
jbe@16
|
2558 PG_RETURN_FLOAT8(retval);
|
jbe@16
|
2559 }
|
jbe@16
|
2560
|
jbe@0
|
2561
|
jbe@0
|
2562 /*-----------------------------------------------------------*
|
jbe@0
|
2563 * B-tree comparison operators and index support functions *
|
jbe@0
|
2564 *-----------------------------------------------------------*/
|
jbe@0
|
2565
|
jbe@0
|
2566 /* macro for a B-tree operator (without detoasting) */
|
jbe@0
|
2567 #define PGL_BTREE_OPER(func, type, cmpfunc, oper) \
|
jbe@0
|
2568 PG_FUNCTION_INFO_V1(func); \
|
jbe@0
|
2569 Datum func(PG_FUNCTION_ARGS) { \
|
jbe@0
|
2570 type *a = (type *)PG_GETARG_POINTER(0); \
|
jbe@0
|
2571 type *b = (type *)PG_GETARG_POINTER(1); \
|
jbe@0
|
2572 PG_RETURN_BOOL(cmpfunc(a, b) oper 0); \
|
jbe@0
|
2573 }
|
jbe@0
|
2574
|
jbe@0
|
2575 /* macro for a B-tree comparison function (without detoasting) */
|
jbe@0
|
2576 #define PGL_BTREE_CMP(func, type, cmpfunc) \
|
jbe@0
|
2577 PG_FUNCTION_INFO_V1(func); \
|
jbe@0
|
2578 Datum func(PG_FUNCTION_ARGS) { \
|
jbe@0
|
2579 type *a = (type *)PG_GETARG_POINTER(0); \
|
jbe@0
|
2580 type *b = (type *)PG_GETARG_POINTER(1); \
|
jbe@0
|
2581 PG_RETURN_INT32(cmpfunc(a, b)); \
|
jbe@0
|
2582 }
|
jbe@0
|
2583
|
jbe@0
|
2584 /* macro for a B-tree operator (with detoasting) */
|
jbe@0
|
2585 #define PGL_BTREE_OPER_DETOAST(func, type, cmpfunc, oper) \
|
jbe@0
|
2586 PG_FUNCTION_INFO_V1(func); \
|
jbe@0
|
2587 Datum func(PG_FUNCTION_ARGS) { \
|
jbe@0
|
2588 bool res; \
|
jbe@0
|
2589 type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
|
jbe@0
|
2590 type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
|
jbe@0
|
2591 res = cmpfunc(a, b) oper 0; \
|
jbe@0
|
2592 PG_FREE_IF_COPY(a, 0); \
|
jbe@0
|
2593 PG_FREE_IF_COPY(b, 1); \
|
jbe@0
|
2594 PG_RETURN_BOOL(res); \
|
jbe@0
|
2595 }
|
jbe@0
|
2596
|
jbe@0
|
2597 /* macro for a B-tree comparison function (with detoasting) */
|
jbe@0
|
2598 #define PGL_BTREE_CMP_DETOAST(func, type, cmpfunc) \
|
jbe@0
|
2599 PG_FUNCTION_INFO_V1(func); \
|
jbe@0
|
2600 Datum func(PG_FUNCTION_ARGS) { \
|
jbe@0
|
2601 int32_t res; \
|
jbe@0
|
2602 type *a = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); \
|
jbe@0
|
2603 type *b = (type *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); \
|
jbe@0
|
2604 res = cmpfunc(a, b); \
|
jbe@0
|
2605 PG_FREE_IF_COPY(a, 0); \
|
jbe@0
|
2606 PG_FREE_IF_COPY(b, 1); \
|
jbe@0
|
2607 PG_RETURN_INT32(res); \
|
jbe@0
|
2608 }
|
jbe@0
|
2609
|
jbe@0
|
2610 /* B-tree operators and comparison function for point */
|
jbe@0
|
2611 PGL_BTREE_OPER(pgl_btree_epoint_lt, pgl_point, pgl_point_cmp, <)
|
jbe@0
|
2612 PGL_BTREE_OPER(pgl_btree_epoint_le, pgl_point, pgl_point_cmp, <=)
|
jbe@0
|
2613 PGL_BTREE_OPER(pgl_btree_epoint_eq, pgl_point, pgl_point_cmp, ==)
|
jbe@0
|
2614 PGL_BTREE_OPER(pgl_btree_epoint_ne, pgl_point, pgl_point_cmp, !=)
|
jbe@0
|
2615 PGL_BTREE_OPER(pgl_btree_epoint_ge, pgl_point, pgl_point_cmp, >=)
|
jbe@0
|
2616 PGL_BTREE_OPER(pgl_btree_epoint_gt, pgl_point, pgl_point_cmp, >)
|
jbe@0
|
2617 PGL_BTREE_CMP(pgl_btree_epoint_cmp, pgl_point, pgl_point_cmp)
|
jbe@0
|
2618
|
jbe@0
|
2619 /* B-tree operators and comparison function for box */
|
jbe@0
|
2620 PGL_BTREE_OPER(pgl_btree_ebox_lt, pgl_box, pgl_box_cmp, <)
|
jbe@0
|
2621 PGL_BTREE_OPER(pgl_btree_ebox_le, pgl_box, pgl_box_cmp, <=)
|
jbe@0
|
2622 PGL_BTREE_OPER(pgl_btree_ebox_eq, pgl_box, pgl_box_cmp, ==)
|
jbe@0
|
2623 PGL_BTREE_OPER(pgl_btree_ebox_ne, pgl_box, pgl_box_cmp, !=)
|
jbe@0
|
2624 PGL_BTREE_OPER(pgl_btree_ebox_ge, pgl_box, pgl_box_cmp, >=)
|
jbe@0
|
2625 PGL_BTREE_OPER(pgl_btree_ebox_gt, pgl_box, pgl_box_cmp, >)
|
jbe@0
|
2626 PGL_BTREE_CMP(pgl_btree_ebox_cmp, pgl_box, pgl_box_cmp)
|
jbe@0
|
2627
|
jbe@0
|
2628 /* B-tree operators and comparison function for circle */
|
jbe@0
|
2629 PGL_BTREE_OPER(pgl_btree_ecircle_lt, pgl_circle, pgl_circle_cmp, <)
|
jbe@0
|
2630 PGL_BTREE_OPER(pgl_btree_ecircle_le, pgl_circle, pgl_circle_cmp, <=)
|
jbe@0
|
2631 PGL_BTREE_OPER(pgl_btree_ecircle_eq, pgl_circle, pgl_circle_cmp, ==)
|
jbe@0
|
2632 PGL_BTREE_OPER(pgl_btree_ecircle_ne, pgl_circle, pgl_circle_cmp, !=)
|
jbe@0
|
2633 PGL_BTREE_OPER(pgl_btree_ecircle_ge, pgl_circle, pgl_circle_cmp, >=)
|
jbe@0
|
2634 PGL_BTREE_OPER(pgl_btree_ecircle_gt, pgl_circle, pgl_circle_cmp, >)
|
jbe@0
|
2635 PGL_BTREE_CMP(pgl_btree_ecircle_cmp, pgl_circle, pgl_circle_cmp)
|
jbe@0
|
2636
|
jbe@0
|
2637
|
jbe@0
|
2638 /*--------------------------------*
|
jbe@0
|
2639 * GiST index support functions *
|
jbe@0
|
2640 *--------------------------------*/
|
jbe@0
|
2641
|
jbe@0
|
2642 /* GiST "consistent" support function */
|
jbe@0
|
2643 PG_FUNCTION_INFO_V1(pgl_gist_consistent);
|
jbe@0
|
2644 Datum pgl_gist_consistent(PG_FUNCTION_ARGS) {
|
jbe@0
|
2645 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
jbe@0
|
2646 pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
|
jbe@0
|
2647 StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
jbe@0
|
2648 bool *recheck = (bool *)PG_GETARG_POINTER(4);
|
jbe@0
|
2649 /* demand recheck because index and query methods are lossy */
|
jbe@0
|
2650 *recheck = true;
|
jbe@10
|
2651 /* strategy number aliases for different operators using the same strategy */
|
jbe@10
|
2652 strategy %= 100;
|
jbe@0
|
2653 /* strategy number 11: equality of two points */
|
jbe@0
|
2654 if (strategy == 11) {
|
jbe@0
|
2655 /* query datum is another point */
|
jbe@0
|
2656 pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
|
jbe@0
|
2657 /* convert other point to key */
|
jbe@0
|
2658 pgl_pointkey querykey;
|
jbe@0
|
2659 pgl_point_to_key(query, querykey);
|
jbe@0
|
2660 /* return true if both keys overlap */
|
jbe@0
|
2661 PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
|
jbe@0
|
2662 }
|
jbe@0
|
2663 /* strategy number 13: equality of two circles */
|
jbe@0
|
2664 if (strategy == 13) {
|
jbe@0
|
2665 /* query datum is another circle */
|
jbe@0
|
2666 pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
|
jbe@0
|
2667 /* convert other circle to key */
|
jbe@0
|
2668 pgl_areakey querykey;
|
jbe@0
|
2669 pgl_circle_to_key(query, querykey);
|
jbe@0
|
2670 /* return true if both keys overlap */
|
jbe@0
|
2671 PG_RETURN_BOOL(pgl_keys_overlap(key, querykey));
|
jbe@0
|
2672 }
|
jbe@0
|
2673 /* for all remaining strategies, keys on empty objects produce no match */
|
jbe@0
|
2674 /* (check necessary because query radius may be infinite) */
|
jbe@0
|
2675 if (PGL_KEY_IS_EMPTY(key)) PG_RETURN_BOOL(false);
|
jbe@0
|
2676 /* strategy number 21: overlapping with point */
|
jbe@0
|
2677 if (strategy == 21) {
|
jbe@0
|
2678 /* query datum is a point */
|
jbe@0
|
2679 pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
|
jbe@0
|
2680 /* return true if estimated distance (allowed to be smaller than real
|
jbe@0
|
2681 distance) between index key and point is zero */
|
jbe@0
|
2682 PG_RETURN_BOOL(pgl_estimate_key_distance(key, query) == 0);
|
jbe@0
|
2683 }
|
jbe@0
|
2684 /* strategy number 22: (point) overlapping with box */
|
jbe@0
|
2685 if (strategy == 22) {
|
jbe@0
|
2686 /* query datum is a box */
|
jbe@0
|
2687 pgl_box *query = (pgl_box *)PG_GETARG_POINTER(1);
|
jbe@0
|
2688 /* determine bounding box of indexed key */
|
jbe@0
|
2689 pgl_box keybox;
|
jbe@0
|
2690 pgl_key_to_box(key, &keybox);
|
jbe@0
|
2691 /* return true if query box overlaps with bounding box of indexed key */
|
jbe@0
|
2692 PG_RETURN_BOOL(pgl_boxes_overlap(query, &keybox));
|
jbe@0
|
2693 }
|
jbe@0
|
2694 /* strategy number 23: overlapping with circle */
|
jbe@0
|
2695 if (strategy == 23) {
|
jbe@0
|
2696 /* query datum is a circle */
|
jbe@0
|
2697 pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
|
jbe@0
|
2698 /* return true if estimated distance (allowed to be smaller than real
|
jbe@0
|
2699 distance) between index key and circle center is smaller than radius */
|
jbe@0
|
2700 PG_RETURN_BOOL(
|
jbe@0
|
2701 pgl_estimate_key_distance(key, &(query->center)) <= query->radius
|
jbe@0
|
2702 );
|
jbe@0
|
2703 }
|
jbe@0
|
2704 /* strategy number 24: overlapping with cluster */
|
jbe@0
|
2705 if (strategy == 24) {
|
jbe@0
|
2706 bool retval; /* return value */
|
jbe@0
|
2707 /* query datum is a cluster */
|
jbe@0
|
2708 pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@0
|
2709 /* return true if estimated distance (allowed to be smaller than real
|
jbe@0
|
2710 distance) between index key and circle center is smaller than radius */
|
jbe@0
|
2711 retval = (
|
jbe@0
|
2712 pgl_estimate_key_distance(key, &(query->bounding.center)) <=
|
jbe@0
|
2713 query->bounding.radius
|
jbe@0
|
2714 );
|
jbe@0
|
2715 PG_FREE_IF_COPY(query, 1); /* free detoasted cluster (if copy) */
|
jbe@0
|
2716 PG_RETURN_BOOL(retval);
|
jbe@0
|
2717 }
|
jbe@0
|
2718 /* throw error for any unknown strategy number */
|
jbe@0
|
2719 elog(ERROR, "unrecognized strategy number: %d", strategy);
|
jbe@0
|
2720 }
|
jbe@0
|
2721
|
jbe@0
|
2722 /* GiST "union" support function */
|
jbe@0
|
2723 PG_FUNCTION_INFO_V1(pgl_gist_union);
|
jbe@0
|
2724 Datum pgl_gist_union(PG_FUNCTION_ARGS) {
|
jbe@0
|
2725 GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
|
jbe@0
|
2726 pgl_keyptr out; /* return value (to be palloc'ed) */
|
jbe@0
|
2727 int i;
|
jbe@0
|
2728 /* determine key size */
|
jbe@0
|
2729 size_t keysize = PGL_KEY_IS_AREAKEY(
|
jbe@0
|
2730 (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key)
|
jbe@0
|
2731 ) ? sizeof (pgl_areakey) : sizeof(pgl_pointkey);
|
jbe@0
|
2732 /* begin with first key as result */
|
jbe@0
|
2733 out = palloc(keysize);
|
jbe@0
|
2734 memcpy(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[0].key), keysize);
|
jbe@0
|
2735 /* unite current result with second, third, etc. key */
|
jbe@0
|
2736 for (i=1; i<entryvec->n; i++) {
|
jbe@0
|
2737 pgl_unite_keys(out, (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key));
|
jbe@0
|
2738 }
|
jbe@0
|
2739 /* return result */
|
jbe@0
|
2740 PG_RETURN_POINTER(out);
|
jbe@0
|
2741 }
|
jbe@0
|
2742
|
jbe@0
|
2743 /* GiST "compress" support function for indicis on points */
|
jbe@0
|
2744 PG_FUNCTION_INFO_V1(pgl_gist_compress_epoint);
|
jbe@0
|
2745 Datum pgl_gist_compress_epoint(PG_FUNCTION_ARGS) {
|
jbe@0
|
2746 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
jbe@0
|
2747 GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */
|
jbe@0
|
2748 /* only transform new leaves */
|
jbe@0
|
2749 if (entry->leafkey) {
|
jbe@0
|
2750 /* get point to be transformed */
|
jbe@0
|
2751 pgl_point *point = (pgl_point *)DatumGetPointer(entry->key);
|
jbe@0
|
2752 /* allocate memory for key */
|
jbe@0
|
2753 pgl_keyptr key = palloc(sizeof(pgl_pointkey));
|
jbe@0
|
2754 /* transform point to key */
|
jbe@0
|
2755 pgl_point_to_key(point, key);
|
jbe@0
|
2756 /* create new GISTENTRY structure as return value */
|
jbe@0
|
2757 retval = palloc(sizeof(GISTENTRY));
|
jbe@0
|
2758 gistentryinit(
|
jbe@0
|
2759 *retval, PointerGetDatum(key),
|
jbe@0
|
2760 entry->rel, entry->page, entry->offset, FALSE
|
jbe@0
|
2761 );
|
jbe@0
|
2762 } else {
|
jbe@0
|
2763 /* inner nodes have already been transformed */
|
jbe@0
|
2764 retval = entry;
|
jbe@0
|
2765 }
|
jbe@0
|
2766 /* return pointer to old or new GISTENTRY structure */
|
jbe@0
|
2767 PG_RETURN_POINTER(retval);
|
jbe@0
|
2768 }
|
jbe@0
|
2769
|
jbe@0
|
2770 /* GiST "compress" support function for indicis on circles */
|
jbe@0
|
2771 PG_FUNCTION_INFO_V1(pgl_gist_compress_ecircle);
|
jbe@0
|
2772 Datum pgl_gist_compress_ecircle(PG_FUNCTION_ARGS) {
|
jbe@0
|
2773 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
jbe@0
|
2774 GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */
|
jbe@0
|
2775 /* only transform new leaves */
|
jbe@0
|
2776 if (entry->leafkey) {
|
jbe@0
|
2777 /* get circle to be transformed */
|
jbe@0
|
2778 pgl_circle *circle = (pgl_circle *)DatumGetPointer(entry->key);
|
jbe@0
|
2779 /* allocate memory for key */
|
jbe@0
|
2780 pgl_keyptr key = palloc(sizeof(pgl_areakey));
|
jbe@0
|
2781 /* transform circle to key */
|
jbe@0
|
2782 pgl_circle_to_key(circle, key);
|
jbe@0
|
2783 /* create new GISTENTRY structure as return value */
|
jbe@0
|
2784 retval = palloc(sizeof(GISTENTRY));
|
jbe@0
|
2785 gistentryinit(
|
jbe@0
|
2786 *retval, PointerGetDatum(key),
|
jbe@0
|
2787 entry->rel, entry->page, entry->offset, FALSE
|
jbe@0
|
2788 );
|
jbe@0
|
2789 } else {
|
jbe@0
|
2790 /* inner nodes have already been transformed */
|
jbe@0
|
2791 retval = entry;
|
jbe@0
|
2792 }
|
jbe@0
|
2793 /* return pointer to old or new GISTENTRY structure */
|
jbe@0
|
2794 PG_RETURN_POINTER(retval);
|
jbe@0
|
2795 }
|
jbe@0
|
2796
|
jbe@0
|
2797 /* GiST "compress" support function for indices on clusters */
|
jbe@0
|
2798 PG_FUNCTION_INFO_V1(pgl_gist_compress_ecluster);
|
jbe@0
|
2799 Datum pgl_gist_compress_ecluster(PG_FUNCTION_ARGS) {
|
jbe@0
|
2800 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
jbe@0
|
2801 GISTENTRY *retval; /* return value (to be palloc'ed unless set to entry) */
|
jbe@0
|
2802 /* only transform new leaves */
|
jbe@0
|
2803 if (entry->leafkey) {
|
jbe@0
|
2804 /* get cluster to be transformed (detoasting necessary!) */
|
jbe@0
|
2805 pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(entry->key);
|
jbe@0
|
2806 /* allocate memory for key */
|
jbe@0
|
2807 pgl_keyptr key = palloc(sizeof(pgl_areakey));
|
jbe@0
|
2808 /* transform cluster to key */
|
jbe@0
|
2809 pgl_circle_to_key(&(cluster->bounding), key);
|
jbe@0
|
2810 /* create new GISTENTRY structure as return value */
|
jbe@0
|
2811 retval = palloc(sizeof(GISTENTRY));
|
jbe@0
|
2812 gistentryinit(
|
jbe@0
|
2813 *retval, PointerGetDatum(key),
|
jbe@0
|
2814 entry->rel, entry->page, entry->offset, FALSE
|
jbe@0
|
2815 );
|
jbe@0
|
2816 /* free detoasted datum */
|
jbe@0
|
2817 if ((void *)cluster != (void *)DatumGetPointer(entry->key)) pfree(cluster);
|
jbe@0
|
2818 } else {
|
jbe@0
|
2819 /* inner nodes have already been transformed */
|
jbe@0
|
2820 retval = entry;
|
jbe@0
|
2821 }
|
jbe@0
|
2822 /* return pointer to old or new GISTENTRY structure */
|
jbe@0
|
2823 PG_RETURN_POINTER(retval);
|
jbe@0
|
2824 }
|
jbe@0
|
2825
|
jbe@0
|
2826 /* GiST "decompress" support function for indices */
|
jbe@0
|
2827 PG_FUNCTION_INFO_V1(pgl_gist_decompress);
|
jbe@0
|
2828 Datum pgl_gist_decompress(PG_FUNCTION_ARGS) {
|
jbe@0
|
2829 /* return passed pointer without transformation */
|
jbe@0
|
2830 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
|
jbe@0
|
2831 }
|
jbe@0
|
2832
|
jbe@0
|
2833 /* GiST "penalty" support function */
|
jbe@0
|
2834 PG_FUNCTION_INFO_V1(pgl_gist_penalty);
|
jbe@0
|
2835 Datum pgl_gist_penalty(PG_FUNCTION_ARGS) {
|
jbe@0
|
2836 GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0);
|
jbe@0
|
2837 GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1);
|
jbe@0
|
2838 float *penalty = (float *)PG_GETARG_POINTER(2);
|
jbe@0
|
2839 /* get original key and key to insert */
|
jbe@0
|
2840 pgl_keyptr orig = (pgl_keyptr)DatumGetPointer(origentry->key);
|
jbe@0
|
2841 pgl_keyptr new = (pgl_keyptr)DatumGetPointer(newentry->key);
|
jbe@0
|
2842 /* copy original key */
|
jbe@0
|
2843 union { pgl_pointkey pointkey; pgl_areakey areakey; } union_key;
|
jbe@0
|
2844 if (PGL_KEY_IS_AREAKEY(orig)) {
|
jbe@0
|
2845 memcpy(union_key.areakey, orig, sizeof(union_key.areakey));
|
jbe@0
|
2846 } else {
|
jbe@0
|
2847 memcpy(union_key.pointkey, orig, sizeof(union_key.pointkey));
|
jbe@0
|
2848 }
|
jbe@0
|
2849 /* calculate union of both keys */
|
jbe@0
|
2850 pgl_unite_keys((pgl_keyptr)&union_key, new);
|
jbe@0
|
2851 /* penalty equal to reduction of key length (logarithm of added area) */
|
jbe@0
|
2852 /* (return value by setting referenced value and returning pointer) */
|
jbe@0
|
2853 *penalty = (
|
jbe@0
|
2854 PGL_KEY_NODEDEPTH(orig) - PGL_KEY_NODEDEPTH((pgl_keyptr)&union_key)
|
jbe@0
|
2855 );
|
jbe@0
|
2856 PG_RETURN_POINTER(penalty);
|
jbe@0
|
2857 }
|
jbe@0
|
2858
|
jbe@0
|
2859 /* GiST "picksplit" support function */
|
jbe@0
|
2860 PG_FUNCTION_INFO_V1(pgl_gist_picksplit);
|
jbe@0
|
2861 Datum pgl_gist_picksplit(PG_FUNCTION_ARGS) {
|
jbe@0
|
2862 GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0);
|
jbe@0
|
2863 GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1);
|
jbe@0
|
2864 OffsetNumber i; /* between FirstOffsetNumber and entryvec->n (inclusive) */
|
jbe@0
|
2865 union {
|
jbe@0
|
2866 pgl_pointkey pointkey;
|
jbe@0
|
2867 pgl_areakey areakey;
|
jbe@0
|
2868 } union_all; /* union of all keys (to be calculated from scratch)
|
jbe@0
|
2869 (later cut in half) */
|
jbe@0
|
2870 int is_areakey = PGL_KEY_IS_AREAKEY(
|
jbe@0
|
2871 (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key)
|
jbe@0
|
2872 );
|
jbe@0
|
2873 int keysize = is_areakey ? sizeof(pgl_areakey) : sizeof(pgl_pointkey);
|
jbe@0
|
2874 pgl_keyptr unionL = palloc(keysize); /* union of keys that go left */
|
jbe@0
|
2875 pgl_keyptr unionR = palloc(keysize); /* union of keys that go right */
|
jbe@0
|
2876 pgl_keyptr key; /* current key to be processed */
|
jbe@0
|
2877 /* allocate memory for array of left and right keys, set counts to zero */
|
jbe@0
|
2878 v->spl_left = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
|
jbe@0
|
2879 v->spl_nleft = 0;
|
jbe@0
|
2880 v->spl_right = (OffsetNumber *)palloc(entryvec->n * sizeof(OffsetNumber));
|
jbe@0
|
2881 v->spl_nright = 0;
|
jbe@0
|
2882 /* calculate union of all keys from scratch */
|
jbe@0
|
2883 memcpy(
|
jbe@0
|
2884 (pgl_keyptr)&union_all,
|
jbe@0
|
2885 (pgl_keyptr)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key),
|
jbe@0
|
2886 keysize
|
jbe@0
|
2887 );
|
jbe@0
|
2888 for (i=FirstOffsetNumber+1; i<entryvec->n; i=OffsetNumberNext(i)) {
|
jbe@0
|
2889 pgl_unite_keys(
|
jbe@0
|
2890 (pgl_keyptr)&union_all,
|
jbe@0
|
2891 (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key)
|
jbe@0
|
2892 );
|
jbe@0
|
2893 }
|
jbe@0
|
2894 /* check if trivial split is necessary due to exhausted key length */
|
jbe@0
|
2895 /* (Note: keys for empty objects must have node depth set to maximum) */
|
jbe@0
|
2896 if (PGL_KEY_NODEDEPTH((pgl_keyptr)&union_all) == (
|
jbe@0
|
2897 is_areakey ? PGL_AREAKEY_MAXDEPTH : PGL_POINTKEY_MAXDEPTH
|
jbe@0
|
2898 )) {
|
jbe@0
|
2899 /* half of all keys go left */
|
jbe@0
|
2900 for (
|
jbe@0
|
2901 i=FirstOffsetNumber;
|
jbe@0
|
2902 i<FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
|
jbe@0
|
2903 i=OffsetNumberNext(i)
|
jbe@0
|
2904 ) {
|
jbe@0
|
2905 /* pointer to current key */
|
jbe@0
|
2906 key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
|
jbe@0
|
2907 /* update unionL */
|
jbe@0
|
2908 /* check if key is first key that goes left */
|
jbe@0
|
2909 if (!v->spl_nleft) {
|
jbe@0
|
2910 /* first key that goes left is just copied to unionL */
|
jbe@0
|
2911 memcpy(unionL, key, keysize);
|
jbe@0
|
2912 } else {
|
jbe@0
|
2913 /* unite current value and next key */
|
jbe@0
|
2914 pgl_unite_keys(unionL, key);
|
jbe@0
|
2915 }
|
jbe@0
|
2916 /* append offset number to list of keys that go left */
|
jbe@0
|
2917 v->spl_left[v->spl_nleft++] = i;
|
jbe@0
|
2918 }
|
jbe@0
|
2919 /* other half goes right */
|
jbe@0
|
2920 for (
|
jbe@0
|
2921 i=FirstOffsetNumber+(entryvec->n - FirstOffsetNumber)/2;
|
jbe@0
|
2922 i<entryvec->n;
|
jbe@0
|
2923 i=OffsetNumberNext(i)
|
jbe@0
|
2924 ) {
|
jbe@0
|
2925 /* pointer to current key */
|
jbe@0
|
2926 key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
|
jbe@0
|
2927 /* update unionR */
|
jbe@0
|
2928 /* check if key is first key that goes right */
|
jbe@0
|
2929 if (!v->spl_nright) {
|
jbe@0
|
2930 /* first key that goes right is just copied to unionR */
|
jbe@0
|
2931 memcpy(unionR, key, keysize);
|
jbe@0
|
2932 } else {
|
jbe@0
|
2933 /* unite current value and next key */
|
jbe@0
|
2934 pgl_unite_keys(unionR, key);
|
jbe@0
|
2935 }
|
jbe@0
|
2936 /* append offset number to list of keys that go right */
|
jbe@0
|
2937 v->spl_right[v->spl_nright++] = i;
|
jbe@0
|
2938 }
|
jbe@0
|
2939 }
|
jbe@0
|
2940 /* otherwise, a non-trivial split is possible */
|
jbe@0
|
2941 else {
|
jbe@0
|
2942 /* cut covered area in half */
|
jbe@0
|
2943 /* (union_all then refers to area of keys that go left) */
|
jbe@0
|
2944 /* check if union of all keys covers empty and non-empty objects */
|
jbe@0
|
2945 if (PGL_KEY_IS_UNIVERSAL((pgl_keyptr)&union_all)) {
|
jbe@0
|
2946 /* if yes, split into empty and non-empty objects */
|
jbe@0
|
2947 pgl_key_set_empty((pgl_keyptr)&union_all);
|
jbe@0
|
2948 } else {
|
jbe@0
|
2949 /* otherwise split by next bit */
|
jbe@0
|
2950 ((pgl_keyptr)&union_all)[PGL_KEY_NODEDEPTH_OFFSET]++;
|
jbe@0
|
2951 /* NOTE: type bit conserved */
|
jbe@0
|
2952 }
|
jbe@0
|
2953 /* determine for each key if it goes left or right */
|
jbe@0
|
2954 for (i=FirstOffsetNumber; i<entryvec->n; i=OffsetNumberNext(i)) {
|
jbe@0
|
2955 /* pointer to current key */
|
jbe@0
|
2956 key = (pgl_keyptr)DatumGetPointer(entryvec->vector[i].key);
|
jbe@0
|
2957 /* keys within one half of the area go left */
|
jbe@0
|
2958 if (pgl_keys_overlap((pgl_keyptr)&union_all, key)) {
|
jbe@0
|
2959 /* update unionL */
|
jbe@0
|
2960 /* check if key is first key that goes left */
|
jbe@0
|
2961 if (!v->spl_nleft) {
|
jbe@0
|
2962 /* first key that goes left is just copied to unionL */
|
jbe@0
|
2963 memcpy(unionL, key, keysize);
|
jbe@0
|
2964 } else {
|
jbe@0
|
2965 /* unite current value of unionL and processed key */
|
jbe@0
|
2966 pgl_unite_keys(unionL, key);
|
jbe@0
|
2967 }
|
jbe@0
|
2968 /* append offset number to list of keys that go left */
|
jbe@0
|
2969 v->spl_left[v->spl_nleft++] = i;
|
jbe@0
|
2970 }
|
jbe@0
|
2971 /* the other keys go right */
|
jbe@0
|
2972 else {
|
jbe@0
|
2973 /* update unionR */
|
jbe@0
|
2974 /* check if key is first key that goes right */
|
jbe@0
|
2975 if (!v->spl_nright) {
|
jbe@0
|
2976 /* first key that goes right is just copied to unionR */
|
jbe@0
|
2977 memcpy(unionR, key, keysize);
|
jbe@0
|
2978 } else {
|
jbe@0
|
2979 /* unite current value of unionR and processed key */
|
jbe@0
|
2980 pgl_unite_keys(unionR, key);
|
jbe@0
|
2981 }
|
jbe@0
|
2982 /* append offset number to list of keys that go right */
|
jbe@0
|
2983 v->spl_right[v->spl_nright++] = i;
|
jbe@0
|
2984 }
|
jbe@0
|
2985 }
|
jbe@0
|
2986 }
|
jbe@0
|
2987 /* store unions in return value */
|
jbe@0
|
2988 v->spl_ldatum = PointerGetDatum(unionL);
|
jbe@0
|
2989 v->spl_rdatum = PointerGetDatum(unionR);
|
jbe@0
|
2990 /* return all results */
|
jbe@0
|
2991 PG_RETURN_POINTER(v);
|
jbe@0
|
2992 }
|
jbe@0
|
2993
|
jbe@0
|
2994 /* GiST "same"/"equal" support function */
|
jbe@0
|
2995 PG_FUNCTION_INFO_V1(pgl_gist_same);
|
jbe@0
|
2996 Datum pgl_gist_same(PG_FUNCTION_ARGS) {
|
jbe@0
|
2997 pgl_keyptr key1 = (pgl_keyptr)PG_GETARG_POINTER(0);
|
jbe@0
|
2998 pgl_keyptr key2 = (pgl_keyptr)PG_GETARG_POINTER(1);
|
jbe@0
|
2999 bool *boolptr = (bool *)PG_GETARG_POINTER(2);
|
jbe@0
|
3000 /* two keys are equal if they are binary equal */
|
jbe@0
|
3001 /* (return result by setting referenced boolean and returning pointer) */
|
jbe@0
|
3002 *boolptr = !memcmp(
|
jbe@0
|
3003 key1,
|
jbe@0
|
3004 key2,
|
jbe@0
|
3005 PGL_KEY_IS_AREAKEY(key1) ? sizeof(pgl_areakey) : sizeof(pgl_pointkey)
|
jbe@0
|
3006 );
|
jbe@0
|
3007 PG_RETURN_POINTER(boolptr);
|
jbe@0
|
3008 }
|
jbe@0
|
3009
|
jbe@0
|
3010 /* GiST "distance" support function */
|
jbe@0
|
3011 PG_FUNCTION_INFO_V1(pgl_gist_distance);
|
jbe@0
|
3012 Datum pgl_gist_distance(PG_FUNCTION_ARGS) {
|
jbe@0
|
3013 GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
|
jbe@0
|
3014 pgl_keyptr key = (pgl_keyptr)DatumGetPointer(entry->key);
|
jbe@0
|
3015 StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
jbe@0
|
3016 bool *recheck = (bool *)PG_GETARG_POINTER(4);
|
jbe@0
|
3017 double distance; /* return value */
|
jbe@0
|
3018 /* demand recheck because distance is just an estimation */
|
jbe@0
|
3019 /* (real distance may be bigger) */
|
jbe@0
|
3020 *recheck = true;
|
jbe@10
|
3021 /* strategy number aliases for different operators using the same strategy */
|
jbe@10
|
3022 strategy %= 100;
|
jbe@0
|
3023 /* strategy number 31: distance to point */
|
jbe@0
|
3024 if (strategy == 31) {
|
jbe@0
|
3025 /* query datum is a point */
|
jbe@0
|
3026 pgl_point *query = (pgl_point *)PG_GETARG_POINTER(1);
|
jbe@0
|
3027 /* use pgl_estimate_pointkey_distance() function to compute result */
|
jbe@0
|
3028 distance = pgl_estimate_key_distance(key, query);
|
jbe@0
|
3029 /* avoid infinity (reserved!) */
|
jbe@0
|
3030 if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
|
jbe@0
|
3031 /* return result */
|
jbe@0
|
3032 PG_RETURN_FLOAT8(distance);
|
jbe@0
|
3033 }
|
jbe@0
|
3034 /* strategy number 33: distance to circle */
|
jbe@0
|
3035 if (strategy == 33) {
|
jbe@0
|
3036 /* query datum is a circle */
|
jbe@0
|
3037 pgl_circle *query = (pgl_circle *)PG_GETARG_POINTER(1);
|
jbe@0
|
3038 /* estimate distance to circle center and substract circle radius */
|
jbe@0
|
3039 distance = (
|
jbe@0
|
3040 pgl_estimate_key_distance(key, &(query->center)) - query->radius
|
jbe@0
|
3041 );
|
jbe@0
|
3042 /* convert non-positive values to zero and avoid infinity (reserved!) */
|
jbe@0
|
3043 if (distance <= 0) distance = 0;
|
jbe@0
|
3044 else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
|
jbe@0
|
3045 /* return result */
|
jbe@0
|
3046 PG_RETURN_FLOAT8(distance);
|
jbe@0
|
3047 }
|
jbe@0
|
3048 /* strategy number 34: distance to cluster */
|
jbe@0
|
3049 if (strategy == 34) {
|
jbe@0
|
3050 /* query datum is a cluster */
|
jbe@0
|
3051 pgl_cluster *query = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
jbe@0
|
3052 /* estimate distance to bounding center and substract bounding radius */
|
jbe@0
|
3053 distance = (
|
jbe@0
|
3054 pgl_estimate_key_distance(key, &(query->bounding.center)) -
|
jbe@0
|
3055 query->bounding.radius
|
jbe@0
|
3056 );
|
jbe@0
|
3057 /* convert non-positive values to zero and avoid infinity (reserved!) */
|
jbe@0
|
3058 if (distance <= 0) distance = 0;
|
jbe@0
|
3059 else if (!isfinite(distance)) distance = PGL_ULTRA_DISTANCE;
|
jbe@0
|
3060 /* free detoasted cluster (if copy) */
|
jbe@0
|
3061 PG_FREE_IF_COPY(query, 1);
|
jbe@0
|
3062 /* return result */
|
jbe@0
|
3063 PG_RETURN_FLOAT8(distance);
|
jbe@0
|
3064 }
|
jbe@0
|
3065 /* throw error for any unknown strategy number */
|
jbe@0
|
3066 elog(ERROR, "unrecognized strategy number: %d", strategy);
|
jbe@0
|
3067 }
|
jbe@0
|
3068
|