github.com/liucxer/courier@v1.7.1/h3/bbox.go (about) 1 package h3 2 3 import "math" 4 5 /** 6 * @brief Geographic bounding box with coordinates defined in radians 7 */ 8 type BBox struct { 9 north float64 ///< north latitude 10 south float64 ///< south latitude 11 east float64 ///< east longitude 12 west float64 ///< west longitude 13 } 14 15 /** 16 * Whether the given bounding box crosses the antimeridian 17 * @param bbox Bounding box to inspect 18 * @return is transmeridian 19 */ 20 func bboxIsTransmeridian(bbox *BBox) bool { 21 return bbox.east < bbox.west 22 } 23 24 /** 25 * Get the center of a bounding box 26 * @param bbox Input bounding box 27 * @param center Output center coordinate 28 */ 29 func bboxCenter(bbox *BBox, center *GeoCoord) { 30 center.Lat = (bbox.north + bbox.south) / 2.0 31 // If the bbox crosses the antimeridian, shift east 360 degrees 32 east := bbox.east 33 if bboxIsTransmeridian(bbox) { 34 east = bbox.east + M_2PI 35 } 36 center.Lon = constrainLng((east + bbox.west) / 2.0) 37 } 38 39 /** 40 * Whether the bounding box contains a given point 41 * @param bbox Bounding box 42 * @param point Point to test 43 * @return Whether the point is contained 44 */ 45 func bboxContains(bbox *BBox, point *GeoCoord) bool { 46 return point.Lat >= bbox.south && point.Lat <= bbox.north && func() bool { 47 if bboxIsTransmeridian(bbox) { 48 return point.Lon >= bbox.west || point.Lon <= bbox.east 49 } 50 return point.Lon >= bbox.west && point.Lon <= bbox.east 51 }() 52 } 53 54 /** 55 * Whether two bounding boxes are strictly equal 56 * @param b1 Bounding box 1 57 * @param b2 Bounding box 2 58 * @return Whether the boxes are equal 59 */ 60 func bboxEquals(b1 *BBox, b2 *BBox) bool { 61 return b1.north == b2.north && b1.south == b2.south && 62 b1.east == b2.east && b1.west == b2.west 63 } 64 65 /** 66 * _hexRadiusKm returns the radius of a given hexagon in Km 67 * 68 * @param h3Index the index of the hexagon 69 * @return the radius of the hexagon in Km 70 */ 71 func _hexRadiusKm(h3Index H3Index) float64 { 72 // There is probably a cheaper way to determine the radius of a 73 // hexagon, but this way is conceptually simple 74 var h3Center GeoCoord 75 var h3Boundary GeoBoundary 76 h3ToGeo(h3Index, &h3Center) 77 h3ToGeoBoundary(h3Index, &h3Boundary) 78 return _geoDistKm(&h3Center, &h3Boundary.Verts[0]) 79 } 80 81 /** 82 * bboxHexEstimate returns an estimated number of hexagons that fit 83 * within the cartesian-projected bounding box 84 * 85 * @param bbox the bounding box to estimate the hexagon fill level 86 * @param res the resolution of the H3 hexagons to fill the bounding box 87 * @return the estimated number of hexagons to fill the bounding box 88 */ 89 func bboxHexEstimate(bbox *BBox, res int) int { 90 // Get the area of the pentagon as the maximally-distorted area possible 91 pentagons := make([]H3Index, 0) 92 getPentagonIndexes(res, &pentagons) 93 94 pentagonRadiusKm := _hexRadiusKm(pentagons[0]) 95 // Area of a regular hexagon is 3/2*sqrt(3) * r * r 96 // The pentagon has the most distortion (smallest edges) and shares its 97 // edges with hexagons, so the most-distorted hexagons have this area 98 pentagonAreaKm2 := 2.59807621135 * pentagonRadiusKm * pentagonRadiusKm 99 100 // Then get the area of the bounding box of the geofence in question 101 var p1, p2 GeoCoord 102 p1.Lat = bbox.north 103 p1.Lon = bbox.east 104 p2.Lat = bbox.south 105 p2.Lon = bbox.east 106 h := _geoDistKm(&p1, &p2) 107 p2.Lat = bbox.north 108 p2.Lon = bbox.west 109 w := _geoDistKm(&p1, &p2) 110 111 // Divide the two to get an estimate of the number of hexagons needed 112 estimate := int(math.Ceil(w * h / pentagonAreaKm2)) 113 if estimate == 0 { 114 estimate = 1 115 } 116 return estimate 117 } 118 119 /** 120 * lineHexEstimate returns an estimated number of hexagons that trace 121 * the cartesian-projected line 122 * 123 * @param origin the origin coordinates 124 * @param destination the destination coordinates 125 * @param res the resolution of the H3 hexagons to trace the line 126 * @return the estimated number of hexagons required to trace the line 127 */ 128 func lineHexEstimate(origin *GeoCoord, destination *GeoCoord, res int) int { 129 // Get the area of the pentagon as the maximally-distorted area possible 130 pentagons := make([]H3Index, 0) 131 getPentagonIndexes(res, &pentagons) 132 133 pentagonRadiusKm := _hexRadiusKm(pentagons[0]) 134 135 dist := _geoDistKm(origin, destination) 136 137 estimate := int(math.Ceil(dist / (2 * pentagonRadiusKm))) 138 if estimate == 0 { 139 estimate = 1 140 } 141 return estimate 142 }