ext/h3/src/src/h3lib/lib/bbox.c in h3-3.6.1 vs ext/h3/src/src/h3lib/lib/bbox.c in h3-3.6.2
- old
+ new
@@ -86,61 +86,40 @@
H3_EXPORT(h3ToGeoBoundary)(h3Index, &h3Boundary);
return _geoDistKm(&h3Center, h3Boundary.verts);
}
/**
- * bboxHexEstimate returns an estimated number of hexagons that fit
- * within the cartesian-projected bounding box
- *
- * @param bbox the bounding box to estimate the hexagon fill level
- * @param res the resolution of the H3 hexagons to fill the bounding box
- * @return the estimated number of hexagons to fill the bounding box
+ * Get the radius of the bbox in hexagons - i.e. the radius of a k-ring centered
+ * on the bbox center and covering the entire bbox.
+ * @param bbox Bounding box to measure
+ * @param res Resolution of hexagons to use in measurement
+ * @return Radius in hexagons
*/
-int bboxHexEstimate(const BBox* bbox, int res) {
- // Get the area of the pentagon as the maximally-distorted area possible
- H3Index pentagons[12] = {0};
- H3_EXPORT(getPentagonIndexes)(res, pentagons);
- double pentagonRadiusKm = _hexRadiusKm(pentagons[0]);
- // Area of a regular hexagon is 3/2*sqrt(3) * r * r
- // The pentagon has the most distortion (smallest edges) and shares its
- // edges with hexagons, so the most-distorted hexagons have this area
- double pentagonAreaKm2 =
- 2.59807621135 * pentagonRadiusKm * pentagonRadiusKm;
+int bboxHexRadius(const BBox* bbox, int res) {
+ // Determine the center of the bounding box
+ GeoCoord center;
+ bboxCenter(bbox, ¢er);
- // Then get the area of the bounding box of the geofence in question
- GeoCoord p1, p2;
- p1.lat = bbox->north;
- p1.lon = bbox->east;
- p2.lat = bbox->south;
- p2.lon = bbox->east;
- double h = _geoDistKm(&p1, &p2);
- p2.lat = bbox->north;
- p2.lon = bbox->west;
- double w = _geoDistKm(&p1, &p2);
+ // Use a vertex on the side closest to the equator, to ensure the longest
+ // radius in cases with significant distortion. East/west is arbitrary.
+ double lat =
+ fabs(bbox->north) > fabs(bbox->south) ? bbox->south : bbox->north;
+ GeoCoord vertex = {lat, bbox->east};
- // Divide the two to get an estimate of the number of hexagons needed
- int estimate = (int)ceil(w * h / pentagonAreaKm2);
- if (estimate == 0) estimate = 1;
- return estimate;
-}
+ // Determine the length of the bounding box "radius" to then use
+ // as a circle on the earth that the k-rings must be greater than
+ double bboxRadiusKm = _geoDistKm(¢er, &vertex);
-/**
- * lineHexEstimate returns an estimated number of hexagons that trace
- * the cartesian-projected line
- *
- * @param origin the origin coordinates
- * @param destination the destination coordinates
- * @param res the resolution of the H3 hexagons to trace the line
- * @return the estimated number of hexagons required to trace the line
- */
-int lineHexEstimate(const GeoCoord* origin, const GeoCoord* destination,
- int res) {
- // Get the area of the pentagon as the maximally-distorted area possible
- H3Index pentagons[12] = {0};
- H3_EXPORT(getPentagonIndexes)(res, pentagons);
- double pentagonRadiusKm = _hexRadiusKm(pentagons[0]);
+ // Determine the radius of the center hexagon
+ double centerHexRadiusKm = _hexRadiusKm(H3_EXPORT(geoToH3)(¢er, res));
- double dist = _geoDistKm(origin, destination);
- int estimate = (int)ceil(dist / (2 * pentagonRadiusKm));
- if (estimate == 0) estimate = 1;
- return estimate;
+ // We use centerHexRadiusKm un-scaled and rounded *up* to guarantee
+ // containment ot the bbox. Ideal, undistorted hexagons could scale
+ // centerHexRadiusKm by a factor of up to 1.5, reducing bboxHexRadius.
+ // This is because the closest point along an undistorted hexagon drawn
+ // through the center points of a k-ring aggregation is exactly 1.5 radii
+ // of the hexagon. But there is distortion near pentagons, and for those
+ // cases, the scaling needs to be less than 1.5. Using the un-scaled value
+ // conservatively guarantees containment for all cases, at the expense of a
+ // larger bboxHexRadius.
+ return (int)ceil(bboxRadiusKm / centerHexRadiusKm);
}