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  }