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