ext/h3/src/src/h3lib/lib/bbox.c in h3-3.6.2 vs ext/h3/src/src/h3lib/lib/bbox.c in h3-3.7.1
- old
+ new
@@ -1,7 +1,7 @@
/*
- * Copyright 2016-2017 Uber Technologies, Inc.
+ * Copyright 2016-2020 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
@@ -16,13 +16,15 @@
/** @file bbox.c
* @brief Geographic bounding box functions
*/
#include "bbox.h"
+
#include <float.h>
#include <math.h>
#include <stdbool.h>
+
#include "constants.h"
#include "geoCoord.h"
#include "h3Index.h"
/**
@@ -82,44 +84,67 @@
// hexagon, but this way is conceptually simple
GeoCoord h3Center;
GeoBoundary h3Boundary;
H3_EXPORT(h3ToGeo)(h3Index, &h3Center);
H3_EXPORT(h3ToGeoBoundary)(h3Index, &h3Boundary);
- return _geoDistKm(&h3Center, h3Boundary.verts);
+ return H3_EXPORT(pointDistKm)(&h3Center, h3Boundary.verts);
}
/**
- * 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
+ * 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
*/
-int bboxHexRadius(const BBox* bbox, int res) {
- // Determine the center of the bounding box
- GeoCoord center;
- bboxCenter(bbox, ¢er);
+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,
+ // shrunk by 20% off chance that the bounding box perfectly bounds a
+ // pentagon.
+ double pentagonAreaKm2 =
+ 0.8 * (2.59807621135 * pentagonRadiusKm * pentagonRadiusKm);
- // 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};
+ // 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->west;
+ double d = H3_EXPORT(pointDistKm)(&p1, &p2);
+ // Derived constant based on: https://math.stackexchange.com/a/1921940
+ // Clamped to 3 as higher values tend to rapidly drag the estimate to zero.
+ double a = d * d / fmin(3.0, fabs((p1.lon - p2.lon) / (p1.lat - p2.lat)));
- // 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);
+ // Divide the two to get an estimate of the number of hexagons needed
+ int estimate = (int)ceil(a / pentagonAreaKm2);
+ if (estimate == 0) estimate = 1;
+ return estimate;
+}
- // Determine the radius of the center hexagon
- double centerHexRadiusKm = _hexRadiusKm(H3_EXPORT(geoToH3)(¢er, res));
+/**
+ * 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]);
- // 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);
+ double dist = H3_EXPORT(pointDistKm)(origin, destination);
+ int estimate = (int)ceil(dist / (2 * pentagonRadiusKm));
+ if (estimate == 0) estimate = 1;
+ return estimate;
}