pgLatLon

diff latlon-v0004.c @ 16:e319679cefbd

Added more operators for clusters (including polygons); Bugfix regarding missing entries in ecluster_ops operator class (index was not used for nearest-neighbor searches)
author jbe
date Fri Sep 09 19:22:30 2016 +0200 (2016-09-09)
parents 95f185a648a4
children c790cf162e04
line diff
     1.1 --- a/latlon-v0004.c	Sat Sep 03 16:00:22 2016 +0200
     1.2 +++ b/latlon-v0004.c	Fri Sep 09 19:22:30 2016 +0200
     1.3 @@ -502,14 +502,6 @@
     1.4    double lat2, lon2;     /* latitude and (adjusted) longitude of next vertex */
     1.5    double lon;            /* longitude of intersection */
     1.6    int counter = 0;       /* counter for intersections east of point */
     1.7 -  /* points outside bounding circle are always assumed to be non-overlapping */
     1.8 -  /* (necessary for consistent table and index scans) */
     1.9 -  if (
    1.10 -    pgl_distance(
    1.11 -      point->lat, point->lon,
    1.12 -      cluster->bounding.center.lat, cluster->bounding.center.lon
    1.13 -    ) > cluster->bounding.radius
    1.14 -  ) return false;
    1.15    /* iterate over all entries */
    1.16    for (i=0; i<cluster->nentries; i++) {
    1.17      /* get properties of entry */
    1.18 @@ -539,14 +531,22 @@
    1.19          entrytype != PGL_ENTRY_OUTLINE &&
    1.20          entrytype != PGL_ENTRY_POLYGON
    1.21        ) continue;
    1.22 -      /* get latitude and longitude values of edge */
    1.23 -      lat1 = points[j].lat;
    1.24 +      /* use previously calculated values for lat1 and lon1 if possible */
    1.25 +      if (j) {
    1.26 +        lat1 = lat2;
    1.27 +        lon1 = lon2;
    1.28 +      } else {
    1.29 +        /* otherwise get latitude and longitude values of first vertex */
    1.30 +        lat1 = points[0].lat;
    1.31 +        lon1 = points[0].lon;
    1.32 +        /* and consider longitude wrap-around for first vertex */
    1.33 +        if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
    1.34 +        else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
    1.35 +      }
    1.36 +      /* get latitude and longitude of next vertex */
    1.37        lat2 = points[k].lat;
    1.38 -      lon1 = points[j].lon;
    1.39        lon2 = points[k].lon;
    1.40 -      /* consider longitude wrap-around for edge */
    1.41 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 = pgl_round(lon1 - 360);
    1.42 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 = pgl_round(lon1 + 360);
    1.43 +      /* consider longitude wrap-around for next vertex */
    1.44        if      (lon_dir < 0 && lon2 > lon_break) lon2 = pgl_round(lon2 - 360);
    1.45        else if (lon_dir > 0 && lon2 < lon_break) lon2 = pgl_round(lon2 + 360);
    1.46        /* return true if point is on horizontal (west to east) edge of polygon */
    1.47 @@ -569,6 +569,231 @@
    1.48    return counter & 1;
    1.49  }
    1.50  
    1.51 +/* check if all points of the second cluster are inside the first cluster */
    1.52 +static inline bool pgl_all_cluster_points_in_cluster(
    1.53 +  pgl_cluster *outer, pgl_cluster *inner
    1.54 +) {
    1.55 +  int i, j;           /* i: entry, j: point in entry */
    1.56 +  int npoints;        /* number of points in entry */
    1.57 +  pgl_point *points;  /* array of points in entry */
    1.58 +  /* iterate over all entries of "inner" cluster */
    1.59 +  for (i=0; i<inner->nentries; i++) {
    1.60 +    /* get properties of entry */
    1.61 +    npoints = inner->entries[i].npoints;
    1.62 +    points = PGL_ENTRY_POINTS(inner, i);
    1.63 +    /* iterate over all points in entry of "inner" cluster */
    1.64 +    for (j=0; j<npoints; j++) {
    1.65 +      /* return false if one point of inner cluster is not in outer cluster */
    1.66 +      if (!pgl_point_in_cluster(points+j, outer)) return false;
    1.67 +    }
    1.68 +  }
    1.69 +  /* otherwise return true */
    1.70 +  return true;
    1.71 +}
    1.72 +
    1.73 +/* check if any point the second cluster is inside the first cluster */
    1.74 +static inline bool pgl_any_cluster_points_in_cluster(
    1.75 +  pgl_cluster *outer, pgl_cluster *inner
    1.76 +) {
    1.77 +  int i, j;           /* i: entry, j: point in entry */
    1.78 +  int npoints;        /* number of points in entry */
    1.79 +  pgl_point *points;  /* array of points in entry */
    1.80 +  /* iterate over all entries of "inner" cluster */
    1.81 +  for (i=0; i<inner->nentries; i++) {
    1.82 +    /* get properties of entry */
    1.83 +    npoints = inner->entries[i].npoints;
    1.84 +    points = PGL_ENTRY_POINTS(inner, i);
    1.85 +    /* iterate over all points in entry of "inner" cluster */
    1.86 +    for (j=0; j<npoints; j++) {
    1.87 +      /* return true if one point of inner cluster is in outer cluster */
    1.88 +      if (pgl_point_in_cluster(points+j, outer)) return true;
    1.89 +    }
    1.90 +  }
    1.91 +  /* otherwise return false */
    1.92 +  return false;
    1.93 +}
    1.94 +
    1.95 +/* check if line segment crosses line */
    1.96 +/* returns -1 if yes, 1 if no, and 0 in corner cases */
    1.97 +/* NOTE: each line (segment) must have a length greater than zero */
    1.98 +static inline double pgl_lseg_crosses_line(
    1.99 +  double seg_x1,  double seg_y1,  double seg_x2,  double seg_y2,
   1.100 +  double line_x1, double line_y1, double line_x2, double line_y2,
   1.101 +  bool strict
   1.102 +) {
   1.103 +  double value = (
   1.104 +    (seg_x1-line_x1) * (line_y2-line_y1) -
   1.105 +    (seg_y1-line_y1) * (line_x2-line_x1)
   1.106 +  ) * (
   1.107 +    (seg_x2-line_x1) * (line_y2-line_y1) -
   1.108 +    (seg_y2-line_y1) * (line_x2-line_x1)
   1.109 +  );
   1.110 +  if (strict) return value < 0;
   1.111 +  else return value <= 0;
   1.112 +}
   1.113 +
   1.114 +/* check if paths and outlines of two clusters overlap */
   1.115 +/* (set strict to true to disregard corner cases) */
   1.116 +static bool pgl_outlines_overlap(
   1.117 +  pgl_cluster *cluster1, pgl_cluster *cluster2, bool strict
   1.118 +) {
   1.119 +  int i1, j1, k1;  /* i: entry, j: point in entry, k: next point in entry */
   1.120 +  int i2, j2, k2;
   1.121 +  int entrytype1, entrytype2;     /* type of entry */
   1.122 +  int npoints1, npoints2;         /* number of points in entry */
   1.123 +  pgl_point *points1;             /* array of points in entry of cluster1 */
   1.124 +  pgl_point *points2;             /* array of points in entry of cluster2 */
   1.125 +  int lon_dir1, lon_dir2;         /* first vertex west (-1) or east (+1) */
   1.126 +  double lon_break1, lon_break2;  /* antipodal longitude of first vertex */
   1.127 +  double lat11, lon11;  /* latitude and (adjusted) longitude of vertex */
   1.128 +  double lat12, lon12;  /* latitude and (adjusted) longitude of next vertex */
   1.129 +  double lat21, lon21;  /* latitude and (adjusted) longitudes for cluster2 */
   1.130 +  double lat22, lon22;
   1.131 +  double wrapvalue;     /* temporary helper value to adjust wrap-around */  
   1.132 +  /* iterate over all entries of cluster1 */
   1.133 +  for (i1=0; i1<cluster1->nentries; i1++) {
   1.134 +    /* get properties of entry in cluster1 and skip points */
   1.135 +    npoints1 = cluster1->entries[i1].npoints;
   1.136 +    if (npoints1 < 2) continue;
   1.137 +    entrytype1 = cluster1->entries[i1].entrytype;
   1.138 +    points1 = PGL_ENTRY_POINTS(cluster1, i1);
   1.139 +    /* determine east/west orientation of first point and calculate antipodal
   1.140 +       longitude */
   1.141 +    lon_break1 = points1[0].lon;
   1.142 +    if (lon_break1 < 0) {
   1.143 +      lon_dir1   = -1;
   1.144 +      lon_break1 = pgl_round(lon_break1 + 180);
   1.145 +    } else if (lon_break1 > 0) {
   1.146 +      lon_dir1   = 1;
   1.147 +      lon_break1 = pgl_round(lon_break1 - 180);
   1.148 +    } else lon_dir1 = 0;
   1.149 +    /* iterate over all edges and vertices in cluster1 */
   1.150 +    for (j1=0; j1<npoints1; j1++) {
   1.151 +      /* calculate index of next vertex */
   1.152 +      k1 = (j1+1) % npoints1;
   1.153 +      /* skip last edge unless entry is (closed) outline or polygon */
   1.154 +      if (
   1.155 +        k1 == 0 &&
   1.156 +        entrytype1 != PGL_ENTRY_OUTLINE &&
   1.157 +        entrytype1 != PGL_ENTRY_POLYGON
   1.158 +      ) continue;
   1.159 +      /* use previously calculated values for lat1 and lon1 if possible */
   1.160 +      if (j1) {
   1.161 +        lat11 = lat12;
   1.162 +        lon11 = lon12;
   1.163 +      } else {
   1.164 +        /* otherwise get latitude and longitude values of first vertex */
   1.165 +        lat11 = points1[0].lat;
   1.166 +        lon11 = points1[0].lon;
   1.167 +        /* and consider longitude wrap-around for first vertex */
   1.168 +        if      (lon_dir1<0 && lon11>lon_break1) lon11 = pgl_round(lon11-360);
   1.169 +        else if (lon_dir1>0 && lon11<lon_break1) lon11 = pgl_round(lon11+360);
   1.170 +      }
   1.171 +      /* get latitude and longitude of next vertex */
   1.172 +      lat12 = points1[k1].lat;
   1.173 +      lon12 = points1[k1].lon;
   1.174 +      /* consider longitude wrap-around for next vertex */
   1.175 +      if      (lon_dir1<0 && lon12>lon_break1) lon12 = pgl_round(lon12-360);
   1.176 +      else if (lon_dir1>0 && lon12<lon_break1) lon12 = pgl_round(lon12+360);
   1.177 +      /* skip degenerated edges */
   1.178 +      if (lat11 == lat12 && lon11 == lon12) continue;
   1.179 +      /* iterate over all entries of cluster2 */
   1.180 +      for (i2=0; i2<cluster2->nentries; i2++) {
   1.181 +        /* get points and number of points of entry in cluster2 */
   1.182 +        npoints2 = cluster2->entries[i2].npoints;
   1.183 +        if (npoints2 < 2) continue;
   1.184 +        entrytype2 = cluster2->entries[i2].entrytype;
   1.185 +        points2 = PGL_ENTRY_POINTS(cluster2, i2);
   1.186 +        /* determine east/west orientation of first point and calculate antipodal
   1.187 +           longitude */
   1.188 +        lon_break2 = points2[0].lon;
   1.189 +        if (lon_break2 < 0) {
   1.190 +          lon_dir2   = -1;
   1.191 +          lon_break2 = pgl_round(lon_break2 + 180);
   1.192 +        } else if (lon_break2 > 0) {
   1.193 +          lon_dir2   = 1;
   1.194 +          lon_break2 = pgl_round(lon_break2 - 180);
   1.195 +        } else lon_dir2 = 0;
   1.196 +        /* iterate over all edges and vertices in cluster2 */
   1.197 +        for (j2=0; j2<npoints2; j2++) {
   1.198 +          /* calculate index of next vertex */
   1.199 +          k2 = (j2+1) % npoints2;
   1.200 +          /* skip last edge unless entry is (closed) outline or polygon */
   1.201 +          if (
   1.202 +            k2 == 0 &&
   1.203 +            entrytype2 != PGL_ENTRY_OUTLINE &&
   1.204 +            entrytype2 != PGL_ENTRY_POLYGON
   1.205 +          ) continue;
   1.206 +          /* use previously calculated values for lat1 and lon1 if possible */
   1.207 +          if (j2) {
   1.208 +            lat21 = lat22;
   1.209 +            lon21 = lon22;
   1.210 +          } else {
   1.211 +            /* otherwise get latitude and longitude values of first vertex */
   1.212 +            lat21 = points2[0].lat;
   1.213 +            lon21 = points2[0].lon;
   1.214 +            /* and consider longitude wrap-around for first vertex */
   1.215 +            if      (lon_dir2<0 && lon21>lon_break2) lon21 = pgl_round(lon21-360);
   1.216 +            else if (lon_dir2>0 && lon21<lon_break2) lon21 = pgl_round(lon21+360);
   1.217 +          }
   1.218 +          /* get latitude and longitude of next vertex */
   1.219 +          lat22 = points2[k2].lat;
   1.220 +          lon22 = points2[k2].lon;
   1.221 +          /* consider longitude wrap-around for next vertex */
   1.222 +          if      (lon_dir2<0 && lon22>lon_break2) lon22 = pgl_round(lon22-360);
   1.223 +          else if (lon_dir2>0 && lon22<lon_break2) lon22 = pgl_round(lon22+360);
   1.224 +          /* skip degenerated edges */
   1.225 +          if (lat21 == lat22 && lon21 == lon22) continue;
   1.226 +          /* perform another wrap-around where necessary */
   1.227 +          /* TODO: improve performance of whole wrap-around mechanism */
   1.228 +          wrapvalue = (lon21 + lon22) - (lon11 + lon12);
   1.229 +          if (wrapvalue > 360) {
   1.230 +            lon21 = pgl_round(lon21 - 360);
   1.231 +            lon22 = pgl_round(lon22 - 360);
   1.232 +          } else if (wrapvalue < -360) {
   1.233 +            lon21 = pgl_round(lon21 + 360);
   1.234 +            lon22 = pgl_round(lon22 + 360);
   1.235 +          }
   1.236 +          /* return true if segments overlap */
   1.237 +          if (
   1.238 +            pgl_lseg_crosses_line(
   1.239 +              lat11, lon11, lat12, lon12,
   1.240 +              lat21, lon21, lat22, lon22,
   1.241 +              strict
   1.242 +            ) && pgl_lseg_crosses_line(
   1.243 +              lat21, lon21, lat22, lon22,
   1.244 +              lat11, lon11, lat12, lon12,
   1.245 +              strict
   1.246 +            )
   1.247 +          ) {
   1.248 +            return true;
   1.249 +          }
   1.250 +        }
   1.251 +      }
   1.252 +    }
   1.253 +  }
   1.254 +  /* otherwise return false */
   1.255 +  return false;
   1.256 +}
   1.257 +
   1.258 +/* check if second cluster is completely contained in first cluster */
   1.259 +static bool pgl_cluster_in_cluster(pgl_cluster *outer, pgl_cluster *inner) {
   1.260 +  if (!pgl_all_cluster_points_in_cluster(outer, inner)) return false;
   1.261 +  if (pgl_outlines_overlap(outer, inner, true)) return false;
   1.262 +  return true;
   1.263 +}
   1.264 +
   1.265 +/* check if two clusters overlap */
   1.266 +static bool pgl_clusters_overlap(
   1.267 +  pgl_cluster *cluster1, pgl_cluster *cluster2
   1.268 +) {
   1.269 +  if (pgl_any_cluster_points_in_cluster(cluster1, cluster2)) return true;
   1.270 +  if (pgl_any_cluster_points_in_cluster(cluster2, cluster1)) return true;
   1.271 +  if (pgl_outlines_overlap(cluster1, cluster2, false)) return true;
   1.272 +  return false;
   1.273 +}
   1.274 +
   1.275 +
   1.276  /* calculate (approximate) distance between point and cluster */
   1.277  static double pgl_point_cluster_distance(pgl_point *point, pgl_cluster *cluster) {
   1.278    int i, j, k;  /* i: entry, j: point in entry, k: next point in entry */
   1.279 @@ -622,12 +847,18 @@
   1.280      else if (lon_dir > 0 && lon0 < lon_break) lon0 += 360;
   1.281      /* iterate over all edges and vertices */
   1.282      for (j=0; j<npoints; j++) {
   1.283 -      /* get latitude and longitude values of current point */
   1.284 -      lat1 = points[j].lat;
   1.285 -      lon1 = points[j].lon;
   1.286 -      /* consider longitude wrap-around for current point */
   1.287 -      if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   1.288 -      else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   1.289 +      /* use previously calculated values for lat1 and lon1 if possible */
   1.290 +      if (j) {
   1.291 +        lat1 = lat2;
   1.292 +        lon1 = lon2;
   1.293 +      } else {
   1.294 +        /* otherwise get latitude and longitude values of first vertex */
   1.295 +        lat1 = points[0].lat;
   1.296 +        lon1 = points[0].lon;
   1.297 +        /* and consider longitude wrap-around for first vertex */
   1.298 +        if      (lon_dir < 0 && lon1 > lon_break) lon1 -= 360;
   1.299 +        else if (lon_dir > 0 && lon1 < lon_break) lon1 += 360;
   1.300 +      }
   1.301        /* calculate distance to vertex */
   1.302        dist = pgl_distance(lat0, lon0, lat1, lon1);
   1.303        /* store calculated distance if smallest */
   1.304 @@ -640,10 +871,10 @@
   1.305          entrytype != PGL_ENTRY_OUTLINE &&
   1.306          entrytype != PGL_ENTRY_POLYGON
   1.307        ) continue;
   1.308 -      /* get latitude and longitude values of next point */
   1.309 +      /* get latitude and longitude of next vertex */
   1.310        lat2 = points[k].lat;
   1.311        lon2 = points[k].lon;
   1.312 -      /* consider longitude wrap-around for next point */
   1.313 +      /* consider longitude wrap-around for next vertex */
   1.314        if      (lon_dir < 0 && lon2 > lon_break) lon2 -= 360;
   1.315        else if (lon_dir > 0 && lon2 < lon_break) lon2 += 360;
   1.316        /* go to next vertex and edge if edge is degenerated */
   1.317 @@ -669,65 +900,73 @@
   1.318    return min_dist;
   1.319  }
   1.320  
   1.321 +/* calculate (approximate) distance between two clusters */
   1.322 +static double pgl_cluster_distance(pgl_cluster *cluster1, pgl_cluster *cluster2) {
   1.323 +  int i, j;                    /* i: entry, j: point in entry */
   1.324 +  int npoints;                 /* number of points in entry */
   1.325 +  pgl_point *points;           /* array of points in entry */
   1.326 +  double dist;                 /* distance calculated in one step */
   1.327 +  double min_dist = INFINITY;  /* minimum distance */
   1.328 +  /* consider distance from each point in one cluster to the whole other */
   1.329 +  for (i=0; i<cluster1->nentries; i++) {
   1.330 +    npoints = cluster1->entries[i].npoints;
   1.331 +    points = PGL_ENTRY_POINTS(cluster1, i);
   1.332 +    for (j=0; j<npoints; j++) {
   1.333 +      dist = pgl_point_cluster_distance(points+j, cluster2);
   1.334 +      if (dist == 0) return dist;
   1.335 +      if (dist < min_dist) min_dist = dist;
   1.336 +    }
   1.337 +  }
   1.338 +  /* consider distance from each point in other cluster to the first cluster */
   1.339 +  for (i=0; i<cluster2->nentries; i++) {
   1.340 +    npoints = cluster2->entries[i].npoints;
   1.341 +    points = PGL_ENTRY_POINTS(cluster2, i);
   1.342 +    for (j=0; j<npoints; j++) {
   1.343 +      dist = pgl_point_cluster_distance(points+j, cluster1);
   1.344 +      if (dist == 0) return dist;
   1.345 +      if (dist < min_dist) min_dist = dist;
   1.346 +    }
   1.347 +  }
   1.348 +  return min_dist;
   1.349 +}
   1.350 +
   1.351  /* estimator function for distance between box and point */
   1.352 -/* allowed to return smaller values than actually correct */
   1.353 +/* always returns a smaller value than actually correct or zero */
   1.354  static double pgl_estimate_point_box_distance(pgl_point *point, pgl_box *box) {
   1.355 -  double dlon;  /* longitude range of box (delta longitude) */
   1.356 -  double h;     /* half of distance along meridian */
   1.357 -  double d;     /* distance between both southern or both northern points */
   1.358 -  double cur_dist;  /* calculated distance */
   1.359 -  double min_dist;  /* minimum distance calculated */
   1.360 -  /* return infinity if bounding box is empty */
   1.361 +  double dlon;      /* longitude range of box (delta longitude) */
   1.362 +  double distance;  /* return value */
   1.363 +  /* return infinity if box is empty */
   1.364    if (box->lat_min > box->lat_max) return INFINITY;
   1.365 -  /* return zero if point is inside bounding box */
   1.366 +  /* return zero if point is inside box */
   1.367    if (pgl_point_in_box(point, box)) return 0;
   1.368    /* calculate delta longitude */
   1.369    dlon = box->lon_max - box->lon_min;
   1.370    if (dlon < 0) dlon += 360;  /* 180th meridian crossed */
   1.371 -  /* if delta longitude is greater than 180 degrees, perform safe fall-back */
   1.372 -  if (dlon > 180) return 0;
   1.373 -  /* calculate half of distance along meridian */
   1.374 -  h = pgl_distance(box->lat_min, 0, box->lat_max, 0) / 2;
   1.375 -  /* calculate full distance between southern points */
   1.376 -  d = pgl_distance(box->lat_min, 0, box->lat_min, dlon);
   1.377 -  /* calculate maximum of full distance and half distance */
   1.378 -  if (h > d) d = h;
   1.379 -  /* calculate distance from point to first southern vertex and substract
   1.380 -     maximum error */
   1.381 -  min_dist = pgl_distance(
   1.382 -    point->lat, point->lon, box->lat_min, box->lon_min
   1.383 -  ) - d;
   1.384 -  /* return zero if estimated distance is smaller than zero */
   1.385 -  if (min_dist <= 0) return 0;
   1.386 -  /* repeat procedure with second southern vertex */
   1.387 -  cur_dist = pgl_distance(
   1.388 -    point->lat, point->lon, box->lat_min, box->lon_max
   1.389 -  ) - d;
   1.390 -  if (cur_dist <= 0) return 0;
   1.391 -  if (cur_dist < min_dist) min_dist = cur_dist;
   1.392 -  /* calculate full distance between northern points */
   1.393 -  d = pgl_distance(box->lat_max, 0, box->lat_max, dlon);
   1.394 -  /* calculate maximum of full distance and half distance */
   1.395 -  if (h > d) d = h;
   1.396 -  /* repeat procedure with northern vertices */
   1.397 -  cur_dist = pgl_distance(
   1.398 -    point->lat, point->lon, box->lat_max, box->lon_max
   1.399 -  ) - d;
   1.400 -  if (cur_dist <= 0) return 0;
   1.401 -  if (cur_dist < min_dist) min_dist = cur_dist;
   1.402 -  cur_dist = pgl_distance(
   1.403 -    point->lat, point->lon, box->lat_max, box->lon_min
   1.404 -  ) - d;
   1.405 -  if (cur_dist <= 0) return 0;
   1.406 -  if (cur_dist < min_dist) min_dist = cur_dist;
   1.407 -  /* return smallest value (unless already returned zero) */
   1.408 -  return min_dist;
   1.409 +  /* if delta longitude is greater than 150 degrees, perform safe fall-back */
   1.410 +  if (dlon > 150) return 0;
   1.411 +  /* calculate lower limit for distance (formula below requires dlon <= 150) */
   1.412 +  /* TODO: provide better estimation function to improve performance */
   1.413 +  distance = (
   1.414 +    (1.0-1e-14) * pgl_distance(
   1.415 +      point->lat,
   1.416 +      point->lon,
   1.417 +      (box->lat_min + box->lat_max) / 2,
   1.418 +      box->lon_min + dlon/2
   1.419 +    ) - pgl_distance(
   1.420 +      box->lat_min, box->lon_min,
   1.421 +      box->lat_max, box->lon_max
   1.422 +    )
   1.423 +  );
   1.424 +  /* truncate negative results to zero */
   1.425 +  if (distance <= 0) distance = 0;
   1.426 +  /* return result */
   1.427 +  return distance;
   1.428  }
   1.429  
   1.430  
   1.431 -/*----------------------------*
   1.432 - *  fractal geographic index  *
   1.433 - *----------------------------*/
   1.434 +/*-------------------------------------------------*
   1.435 + *  geographic index based on space-filling curve  *
   1.436 + *-------------------------------------------------*/
   1.437  
   1.438  /* number of bytes used for geographic (center) position in keys */
   1.439  #define PGL_KEY_LATLON_BYTELEN 7
   1.440 @@ -735,9 +974,6 @@
   1.441  /* maximum reference value for logarithmic size of geographic objects */
   1.442  #define PGL_AREAKEY_REFOBJSIZE (PGL_DIAMETER/3.0)  /* can be tweaked */
   1.443  
   1.444 -/* safety margin to avoid floating point errors in distance estimation */
   1.445 -#define PGL_FPE_SAFETY (1.0+1e-14)  /* slightly greater than 1.0 */
   1.446 -
   1.447  /* pointer to index key (either pgl_pointkey or pgl_areakey) */
   1.448  typedef unsigned char *pgl_keyptr;
   1.449  
   1.450 @@ -1094,7 +1330,7 @@
   1.451  }
   1.452  
   1.453  /* estimator function for distance between point and index key */
   1.454 -/* allowed to return smaller values than actually correct */
   1.455 +/* always returns a smaller value than actually correct or zero */
   1.456  static double pgl_estimate_key_distance(pgl_keyptr key, pgl_point *point) {
   1.457    pgl_box box;  /* center(!) bounding box of area index key */
   1.458    /* calculate center(!) bounding box and maximum radius of objects covered
   1.459 @@ -1103,11 +1339,7 @@
   1.460    /* calculate estimated distance between bounding box of center point of
   1.461       indexed object and point passed as second argument, then substract maximum
   1.462       radius of objects covered by index key */
   1.463 -  /* (use PGL_FPE_SAFETY factor to cope with minor floating point errors) */
   1.464 -  distance = (
   1.465 -    pgl_estimate_point_box_distance(point, &box) / PGL_FPE_SAFETY -
   1.466 -    distance * PGL_FPE_SAFETY
   1.467 -  );
   1.468 +  distance = pgl_estimate_point_box_distance(point, &box) - distance;
   1.469    /* truncate negative results to zero */
   1.470    if (distance <= 0) distance = 0;
   1.471    /* return result */
   1.472 @@ -2102,7 +2334,16 @@
   1.473  Datum pgl_epoint_ecluster_overlap(PG_FUNCTION_ARGS) {
   1.474    pgl_point *point = (pgl_point *)PG_GETARG_POINTER(0);
   1.475    pgl_cluster *cluster = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
   1.476 -  bool retval = pgl_point_in_cluster(point, cluster);
   1.477 +  bool retval;
   1.478 +  /* points outside bounding circle are always assumed to be non-overlapping
   1.479 +     (necessary for consistent table and index scans) */
   1.480 +  if (
   1.481 +    pgl_distance(
   1.482 +      point->lat, point->lon,
   1.483 +      cluster->bounding.center.lat, cluster->bounding.center.lon
   1.484 +    ) > cluster->bounding.radius
   1.485 +  ) retval = false;
   1.486 +  else retval = pgl_point_in_cluster(point, cluster);
   1.487    PG_FREE_IF_COPY(cluster, 1);
   1.488    PG_RETURN_BOOL(retval);
   1.489  }
   1.490 @@ -2189,6 +2430,27 @@
   1.491    PG_RETURN_BOOL(retval);
   1.492  }
   1.493  
   1.494 +/* check if two clusters overlap (overlap operator "&&") in SQL */
   1.495 +PG_FUNCTION_INFO_V1(pgl_ecluster_overlap);
   1.496 +Datum pgl_ecluster_overlap(PG_FUNCTION_ARGS) {
   1.497 +  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
   1.498 +  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
   1.499 +  bool retval;
   1.500 +  /* clusters with non-touching bounding circles are always assumed to be
   1.501 +     non-overlapping (improves performance and is necessary for consistent
   1.502 +     table and index scans) */
   1.503 +  if (
   1.504 +    pgl_distance(
   1.505 +      cluster1->bounding.center.lat, cluster1->bounding.center.lon,
   1.506 +      cluster2->bounding.center.lat, cluster2->bounding.center.lon
   1.507 +    ) > cluster1->bounding.radius + cluster2->bounding.radius
   1.508 +  ) retval = false;
   1.509 +  else retval = pgl_clusters_overlap(cluster1, cluster2);
   1.510 +  PG_FREE_IF_COPY(cluster1, 0);
   1.511 +  PG_FREE_IF_COPY(cluster2, 1);
   1.512 +  PG_RETURN_BOOL(retval);
   1.513 +}
   1.514 +
   1.515  /* check if two clusters may overlap (lossy overlap operator "&&+") in SQL */
   1.516  PG_FUNCTION_INFO_V1(pgl_ecluster_may_overlap);
   1.517  Datum pgl_ecluster_may_overlap(PG_FUNCTION_ARGS) {
   1.518 @@ -2203,6 +2465,27 @@
   1.519    PG_RETURN_BOOL(retval);
   1.520  }
   1.521  
   1.522 +/* check if second cluster is in first cluster (cont. operator "@>) in SQL */
   1.523 +PG_FUNCTION_INFO_V1(pgl_ecluster_contains);
   1.524 +Datum pgl_ecluster_contains(PG_FUNCTION_ARGS) {
   1.525 +  pgl_cluster *outer = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
   1.526 +  pgl_cluster *inner = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
   1.527 +  bool retval;
   1.528 +  /* clusters with non-touching bounding circles are always assumed to be
   1.529 +     non-overlapping (improves performance and is necessary for consistent
   1.530 +     table and index scans) */
   1.531 +  if (
   1.532 +    pgl_distance(
   1.533 +      outer->bounding.center.lat, outer->bounding.center.lon,
   1.534 +      inner->bounding.center.lat, inner->bounding.center.lon
   1.535 +    ) > outer->bounding.radius + inner->bounding.radius
   1.536 +  ) retval = false;
   1.537 +  else retval = pgl_cluster_in_cluster(outer, inner);
   1.538 +  PG_FREE_IF_COPY(outer, 0);
   1.539 +  PG_FREE_IF_COPY(inner, 1);
   1.540 +  PG_RETURN_BOOL(retval);
   1.541 +}
   1.542 +
   1.543  /* calculate distance between two points ("<->" operator) in SQL */
   1.544  PG_FUNCTION_INFO_V1(pgl_epoint_distance);
   1.545  Datum pgl_epoint_distance(PG_FUNCTION_ARGS) {
   1.546 @@ -2258,6 +2541,17 @@
   1.547    PG_RETURN_FLOAT8((distance <= 0) ? 0 : distance);
   1.548  }
   1.549  
   1.550 +/* calculate distance between two clusters ("<->" operator) in SQL */
   1.551 +PG_FUNCTION_INFO_V1(pgl_ecluster_distance);
   1.552 +Datum pgl_ecluster_distance(PG_FUNCTION_ARGS) {
   1.553 +  pgl_cluster *cluster1 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
   1.554 +  pgl_cluster *cluster2 = (pgl_cluster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
   1.555 +  double retval = pgl_cluster_distance(cluster1, cluster2);
   1.556 +  PG_FREE_IF_COPY(cluster1, 0);
   1.557 +  PG_FREE_IF_COPY(cluster2, 1);
   1.558 +  PG_RETURN_FLOAT8(retval);
   1.559 +}
   1.560 +
   1.561  
   1.562  /*-----------------------------------------------------------*
   1.563   *  B-tree comparison operators and index support functions  *

Impressum / About Us