h3 3.5.0 → 3.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +45 -0
  3. data/Gemfile.lock +9 -7
  4. data/README.md +8 -8
  5. data/ext/h3/src/.github/workflows/test-linux.yml +118 -0
  6. data/ext/h3/src/.github/workflows/test-macos.yml +42 -0
  7. data/ext/h3/src/.github/workflows/test-website.yml +32 -0
  8. data/ext/h3/src/.github/workflows/test-windows.yml +44 -0
  9. data/ext/h3/src/.gitignore +5 -0
  10. data/ext/h3/src/.travis.yml +21 -32
  11. data/ext/h3/src/CHANGELOG.md +60 -0
  12. data/ext/h3/src/CMakeLists.txt +150 -33
  13. data/ext/h3/src/CONTRIBUTING.md +1 -1
  14. data/ext/h3/src/README.md +65 -16
  15. data/ext/h3/src/RELEASE.md +3 -1
  16. data/ext/h3/src/VERSION +1 -1
  17. data/ext/h3/src/dev-docs/RFCs/rfc-template.md +21 -0
  18. data/ext/h3/src/dev-docs/RFCs/v4.0.0/error-handling-rfc.md +21 -0
  19. data/ext/h3/src/dev-docs/RFCs/v4.0.0/names_for_concepts_types_functions.md +276 -0
  20. data/ext/h3/src/dev-docs/RFCs/v4.0.0/overrideable-allocators-rfc.md +141 -0
  21. data/ext/h3/src/dev-docs/RFCs/v4.0.0/polyfill-modes-rfc.md +21 -0
  22. data/ext/h3/src/dev-docs/RFCs/v4.0.0/vertex-mode-rfc.md +50 -0
  23. data/ext/h3/src/dev-docs/build_windows.md +6 -1
  24. data/ext/h3/src/dev-docs/creating_bindings.md +3 -3
  25. data/ext/h3/src/dev-docs/custom_alloc.md +27 -0
  26. data/ext/h3/src/docs/{overview/mainpage.md → README.md} +2 -3
  27. data/ext/h3/src/docs/api/hierarchy.md +8 -0
  28. data/ext/h3/src/docs/api/misc.md +94 -0
  29. data/ext/h3/src/docs/community/applications.md +1 -0
  30. data/ext/h3/src/docs/community/bindings.md +10 -0
  31. data/ext/h3/src/docs/community/tutorials.md +8 -3
  32. data/ext/h3/src/docs/core-library/coordsystems.md +5 -4
  33. data/ext/h3/src/docs/core-library/filters.md +8 -9
  34. data/ext/h3/src/docs/core-library/geoToH3desc.md +2 -3
  35. data/ext/h3/src/docs/core-library/h3ToGeoBoundaryDesc.md +4 -5
  36. data/ext/h3/src/docs/core-library/h3ToGeoDesc.md +3 -4
  37. data/ext/h3/src/docs/core-library/h3indexing.md +26 -17
  38. data/ext/h3/src/docs/core-library/overview.md +2 -3
  39. data/ext/h3/src/docs/core-library/restable.md +1 -2
  40. data/ext/h3/src/docs/core-library/usage.md +1 -2
  41. data/ext/h3/src/docs/table-of-contents.json +47 -0
  42. data/ext/h3/src/docs/{overview/usecases.md → usecases.md} +6 -11
  43. data/ext/h3/src/scripts/binding_functions.sh +1 -1
  44. data/ext/h3/src/scripts/coverage.sh.in +8 -4
  45. data/ext/h3/src/scripts/update_version.sh +2 -2
  46. data/ext/h3/src/src/apps/applib/include/args.h +1 -0
  47. data/ext/h3/src/src/apps/applib/include/test.h +1 -0
  48. data/ext/h3/src/src/apps/applib/include/utility.h +7 -1
  49. data/ext/h3/src/src/apps/applib/lib/args.c +2 -0
  50. data/ext/h3/src/src/apps/applib/lib/kml.c +2 -0
  51. data/ext/h3/src/src/apps/applib/lib/test.c +1 -0
  52. data/ext/h3/src/src/apps/applib/lib/utility.c +133 -2
  53. data/ext/h3/src/src/apps/benchmarks/benchmarkH3Api.c +1 -1
  54. data/ext/h3/src/{website/html.config.js → src/apps/benchmarks/benchmarkH3UniEdge.c} +15 -12
  55. data/ext/h3/src/src/apps/filters/h3ToComponents.c +1 -0
  56. data/ext/h3/src/src/apps/filters/h3ToGeo.c +1 -0
  57. data/ext/h3/src/src/apps/filters/h3ToGeoBoundary.c +1 -0
  58. data/ext/h3/src/src/apps/filters/h3ToLocalIj.c +1 -0
  59. data/ext/h3/src/src/apps/filters/hexRange.c +1 -0
  60. data/ext/h3/src/src/apps/filters/kRing.c +1 -0
  61. data/ext/h3/src/src/apps/filters/localIjToH3.c +1 -0
  62. data/ext/h3/src/src/apps/miscapps/generateBaseCellNeighbors.c +2 -2
  63. data/ext/h3/src/src/apps/miscapps/generateFaceCenterPoint.c +1 -0
  64. data/ext/h3/src/src/apps/miscapps/generateNumHexagons.c +1 -2
  65. data/ext/h3/src/src/apps/miscapps/generatePentagonDirectionFaces.c +67 -0
  66. data/ext/h3/src/src/apps/miscapps/h3ToGeoBoundaryHier.c +1 -0
  67. data/ext/h3/src/src/apps/miscapps/h3ToGeoHier.c +1 -0
  68. data/ext/h3/src/src/apps/miscapps/h3ToHier.c +1 -0
  69. data/ext/h3/src/src/apps/testapps/mkRandGeo.c +1 -0
  70. data/ext/h3/src/src/apps/testapps/mkRandGeoBoundary.c +1 -0
  71. data/ext/h3/src/src/apps/testapps/testBBox.c +1 -0
  72. data/ext/h3/src/src/apps/testapps/testBaseCells.c +15 -1
  73. data/ext/h3/src/src/apps/testapps/testCompact.c +121 -2
  74. data/ext/h3/src/src/apps/testapps/testCoordIj.c +1 -0
  75. data/ext/h3/src/src/apps/testapps/testGeoCoord.c +47 -8
  76. data/ext/h3/src/src/apps/testapps/testGeoToH3.c +1 -0
  77. data/ext/h3/src/src/apps/testapps/testH3Api.c +1 -0
  78. data/ext/h3/src/src/apps/testapps/testH3CellArea.c +47 -0
  79. data/ext/h3/src/src/apps/testapps/testH3CellAreaExhaustive.c +180 -0
  80. data/ext/h3/src/src/apps/testapps/testH3Distance.c +2 -50
  81. data/ext/h3/src/src/apps/testapps/testH3DistanceExhaustive.c +84 -0
  82. data/ext/h3/src/src/apps/testapps/testH3GetFaces.c +1 -0
  83. data/ext/h3/src/src/apps/testapps/testH3Index.c +33 -3
  84. data/ext/h3/src/src/apps/testapps/testH3Line.c +2 -84
  85. data/ext/h3/src/src/apps/testapps/testH3LineExhaustive.c +115 -0
  86. data/ext/h3/src/src/apps/testapps/testH3Memory.c +175 -0
  87. data/ext/h3/src/src/apps/testapps/testH3NeighborRotations.c +1 -0
  88. data/ext/h3/src/src/apps/testapps/testH3SetToLinkedGeo.c +1 -0
  89. data/ext/h3/src/src/apps/testapps/testH3SetToVertexGraph.c +1 -0
  90. data/ext/h3/src/src/apps/testapps/testH3ToCenterChild.c +68 -0
  91. data/ext/h3/src/src/apps/testapps/testH3ToChildren.c +15 -2
  92. data/ext/h3/src/src/apps/testapps/testH3ToGeo.c +1 -0
  93. data/ext/h3/src/src/apps/testapps/testH3ToGeoBoundary.c +1 -0
  94. data/ext/h3/src/src/apps/testapps/testH3ToLocalIj.c +24 -236
  95. data/ext/h3/src/src/apps/testapps/testH3ToLocalIjExhaustive.c +265 -0
  96. data/ext/h3/src/src/apps/testapps/testH3ToParent.c +1 -0
  97. data/ext/h3/src/src/apps/testapps/testH3UniEdge.c +45 -16
  98. data/ext/h3/src/src/apps/testapps/testH3UniEdgeExhaustive.c +111 -0
  99. data/ext/h3/src/src/apps/testapps/testHexRanges.c +1 -0
  100. data/ext/h3/src/src/apps/testapps/testHexRing.c +1 -0
  101. data/ext/h3/src/src/apps/testapps/testKRing.c +1 -0
  102. data/ext/h3/src/src/apps/testapps/testLinkedGeo.c +1 -0
  103. data/ext/h3/src/src/apps/testapps/testMaxH3ToChildrenSize.c +1 -0
  104. data/ext/h3/src/src/apps/testapps/testPentagonIndexes.c +58 -0
  105. data/ext/h3/src/src/apps/testapps/testPolyfill.c +72 -9
  106. data/ext/h3/src/src/apps/testapps/testPolyfillReported.c +157 -0
  107. data/ext/h3/src/src/apps/testapps/testPolygon.c +27 -1
  108. data/ext/h3/src/src/apps/testapps/testVec2d.c +1 -0
  109. data/ext/h3/src/src/apps/testapps/testVec3d.c +1 -0
  110. data/ext/h3/src/src/apps/testapps/testVertex.c +66 -0
  111. data/ext/h3/src/src/apps/testapps/testVertexGraph.c +1 -0
  112. data/ext/h3/src/src/h3lib/include/algos.h +8 -0
  113. data/ext/h3/src/src/h3lib/include/alloc.h +40 -0
  114. data/ext/h3/src/src/h3lib/include/baseCells.h +4 -0
  115. data/ext/h3/src/src/h3lib/include/bbox.h +4 -1
  116. data/ext/h3/src/src/h3lib/include/constants.h +2 -0
  117. data/ext/h3/src/src/h3lib/include/faceijk.h +3 -2
  118. data/ext/h3/src/src/h3lib/include/geoCoord.h +2 -3
  119. data/ext/h3/src/src/h3lib/include/h3Index.h +37 -4
  120. data/ext/h3/src/src/h3lib/include/h3api.h.in +85 -17
  121. data/ext/h3/src/src/h3lib/include/linkedGeo.h +1 -0
  122. data/ext/h3/src/src/h3lib/include/polygon.h +1 -0
  123. data/ext/h3/src/src/h3lib/include/polygonAlgos.h +1 -0
  124. data/ext/h3/src/src/h3lib/include/vertex.h +44 -0
  125. data/ext/h3/src/src/h3lib/include/vertexGraph.h +1 -0
  126. data/ext/h3/src/src/h3lib/lib/algos.c +305 -80
  127. data/ext/h3/src/src/h3lib/lib/baseCells.c +26 -4
  128. data/ext/h3/src/src/h3lib/lib/bbox.c +56 -27
  129. data/ext/h3/src/src/h3lib/lib/coordijk.c +2 -0
  130. data/ext/h3/src/src/h3lib/lib/faceijk.c +35 -24
  131. data/ext/h3/src/src/h3lib/lib/geoCoord.c +162 -44
  132. data/ext/h3/src/src/h3lib/lib/h3Index.c +150 -46
  133. data/ext/h3/src/src/h3lib/lib/h3UniEdge.c +42 -57
  134. data/ext/h3/src/src/h3lib/lib/linkedGeo.c +20 -15
  135. data/ext/h3/src/src/h3lib/lib/localij.c +5 -5
  136. data/ext/h3/src/src/h3lib/lib/polygon.c +3 -2
  137. data/ext/h3/src/src/h3lib/lib/vec2d.c +1 -0
  138. data/ext/h3/src/src/h3lib/lib/vec3d.c +1 -0
  139. data/ext/h3/src/src/h3lib/lib/vertex.c +134 -0
  140. data/ext/h3/src/src/h3lib/lib/vertexGraph.c +8 -5
  141. data/ext/h3/src/website/.eslintignore +2 -0
  142. data/ext/h3/src/website/.gitignore +57 -0
  143. data/ext/h3/src/website/.nvmrc +1 -0
  144. data/ext/h3/src/website/README.md +8 -6
  145. data/ext/h3/src/website/gatsby-config.js +83 -0
  146. data/ext/h3/src/website/package.json +20 -12
  147. data/ext/h3/src/website/scripts/build-to-gh-pages.sh +7 -5
  148. data/ext/h3/src/website/src/.gitkeep +0 -0
  149. data/ext/h3/src/website/templates/documentation.jsx +129 -0
  150. data/ext/h3/src/website/yarn.lock +13723 -0
  151. data/h3.gemspec +1 -0
  152. data/lib/h3.rb +8 -23
  153. data/lib/h3/bindings/base.rb +15 -4
  154. data/lib/h3/bindings/private.rb +13 -9
  155. data/lib/h3/geo_json.rb +1 -1
  156. data/lib/h3/hierarchy.rb +24 -9
  157. data/lib/h3/indexing.rb +7 -7
  158. data/lib/h3/inspection.rb +22 -26
  159. data/lib/h3/miscellaneous.rb +157 -9
  160. data/lib/h3/regions.rb +3 -0
  161. data/lib/h3/traversal.rb +9 -9
  162. data/lib/h3/unidirectional_edges.rb +18 -18
  163. data/lib/h3/version.rb +1 -1
  164. data/spec/geo_json_spec.rb +8 -0
  165. data/spec/hierarchy_spec.rb +23 -13
  166. data/spec/indexing_spec.rb +15 -15
  167. data/spec/inspection_spec.rb +17 -17
  168. data/spec/miscellaneous_spec.rb +151 -6
  169. data/spec/{region_spec.rb → regions_spec.rb} +1 -1
  170. data/spec/traversal_spec.rb +6 -6
  171. data/spec/unidirectional_edges_spec.rb +18 -18
  172. metadata +55 -15
  173. data/ext/h3/src/.ycm_extra_conf.py +0 -92
  174. data/ext/h3/src/appveyor.yml +0 -50
  175. data/ext/h3/src/website/src/config.js +0 -46
  176. data/ext/h3/src/website/src/mdRoutes.js +0 -151
  177. data/ext/h3/src/website/src/styles/_variables.scss +0 -16
  178. data/ext/h3/src/website/src/styles/index.scss +0 -3
  179. data/ext/h3/src/website/static/index.html +0 -15
  180. data/lib/h3/bindings.rb +0 -12
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2016-2018 Uber Technologies, Inc.
2
+ * Copyright 2016-2020 Uber Technologies, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -18,16 +18,17 @@
18
18
  */
19
19
 
20
20
  #include "baseCells.h"
21
+
21
22
  #include "h3Index.h"
22
23
 
23
- /** @struct BaseCellOrient
24
+ /** @struct BaseCellRotation
24
25
  * @brief base cell at a given ijk and required rotations into its system
25
26
  */
26
27
  typedef struct {
27
28
  int baseCell; ///< base cell number
28
29
  int ccwRot60; ///< number of ccw 60 degree rotations relative to current
29
30
  /// face
30
- } BaseCellOrient;
31
+ } BaseCellRotation;
31
32
 
32
33
  /** @brief Neighboring base cell ID in each IJK direction.
33
34
  *
@@ -304,7 +305,7 @@ const int baseCellNeighbor60CCWRots[NUM_BASE_CELLS][7] = {
304
305
  * This table can be accessed using the functions `_faceIjkToBaseCell` and
305
306
  * `_faceIjkToBaseCellCCWrot60`
306
307
  */
307
- static const BaseCellOrient faceIjkBaseCells[NUM_ICOSA_FACES][3][3][3] = {
308
+ static const BaseCellRotation faceIjkBaseCells[NUM_ICOSA_FACES][3][3][3] = {
308
309
  {// face 0
309
310
  {
310
311
  // i 0
@@ -862,6 +863,27 @@ void _baseCellToFaceIjk(int baseCell, FaceIJK* h) {
862
863
  *h = baseCellData[baseCell].homeFijk;
863
864
  }
864
865
 
866
+ /**
867
+ * @brief Given a base cell and the face it appears on, return
868
+ * the number of 60' ccw rotations for that base cell's
869
+ * coordinate system.
870
+ * @returns The number of rotations, or INVALID_ROTATIONS if the base
871
+ * cell is not found on the given face
872
+ */
873
+ int _baseCellToCCWrot60(int baseCell, int face) {
874
+ if (face < 0 || face > NUM_ICOSA_FACES) return INVALID_ROTATIONS;
875
+ for (int i = 0; i < 3; i++) {
876
+ for (int j = 0; j < 3; j++) {
877
+ for (int k = 0; k < 3; k++) {
878
+ if (faceIjkBaseCells[face][i][j][k].baseCell == baseCell) {
879
+ return faceIjkBaseCells[face][i][j][k].ccwRot60;
880
+ }
881
+ }
882
+ }
883
+ }
884
+ return INVALID_ROTATIONS;
885
+ }
886
+
865
887
  /** @brief Return whether or not the tested face is a cw offset face.
866
888
  */
867
889
  bool _baseCellIsCwOffset(int baseCell, int testFace) {
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2016-2017 Uber Technologies, Inc.
2
+ * Copyright 2016-2020 Uber Technologies, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -18,9 +18,11 @@
18
18
  */
19
19
 
20
20
  #include "bbox.h"
21
+
21
22
  #include <float.h>
22
23
  #include <math.h>
23
24
  #include <stdbool.h>
25
+
24
26
  #include "constants.h"
25
27
  #include "geoCoord.h"
26
28
  #include "h3Index.h"
@@ -84,38 +86,65 @@ double _hexRadiusKm(H3Index h3Index) {
84
86
  GeoBoundary h3Boundary;
85
87
  H3_EXPORT(h3ToGeo)(h3Index, &h3Center);
86
88
  H3_EXPORT(h3ToGeoBoundary)(h3Index, &h3Boundary);
87
- return _geoDistKm(&h3Center, h3Boundary.verts);
89
+ return H3_EXPORT(pointDistKm)(&h3Center, h3Boundary.verts);
88
90
  }
89
91
 
90
92
  /**
91
- * Get the radius of the bbox in hexagons - i.e. the radius of a k-ring centered
92
- * on the bbox center and covering the entire bbox.
93
- * @param bbox Bounding box to measure
94
- * @param res Resolution of hexagons to use in measurement
95
- * @return Radius in hexagons
93
+ * bboxHexEstimate returns an estimated number of hexagons that fit
94
+ * within the cartesian-projected bounding box
95
+ *
96
+ * @param bbox the bounding box to estimate the hexagon fill level
97
+ * @param res the resolution of the H3 hexagons to fill the bounding box
98
+ * @return the estimated number of hexagons to fill the bounding box
96
99
  */
97
- int bboxHexRadius(const BBox* bbox, int res) {
98
- // Determine the center of the bounding box
99
- GeoCoord center;
100
- bboxCenter(bbox, &center);
100
+ int bboxHexEstimate(const BBox* bbox, int res) {
101
+ // Get the area of the pentagon as the maximally-distorted area possible
102
+ H3Index pentagons[12] = {0};
103
+ H3_EXPORT(getPentagonIndexes)(res, pentagons);
104
+ double pentagonRadiusKm = _hexRadiusKm(pentagons[0]);
105
+ // Area of a regular hexagon is 3/2*sqrt(3) * r * r
106
+ // The pentagon has the most distortion (smallest edges) and shares its
107
+ // edges with hexagons, so the most-distorted hexagons have this area,
108
+ // shrunk by 20% off chance that the bounding box perfectly bounds a
109
+ // pentagon.
110
+ double pentagonAreaKm2 =
111
+ 0.8 * (2.59807621135 * pentagonRadiusKm * pentagonRadiusKm);
101
112
 
102
- // Use a vertex on the side closest to the equator, to ensure the longest
103
- // radius in cases with significant distortion. East/west is arbitrary.
104
- double lat =
105
- fabs(bbox->north) > fabs(bbox->south) ? bbox->south : bbox->north;
106
- GeoCoord vertex = {lat, bbox->east};
113
+ // Then get the area of the bounding box of the geofence in question
114
+ GeoCoord p1, p2;
115
+ p1.lat = bbox->north;
116
+ p1.lon = bbox->east;
117
+ p2.lat = bbox->south;
118
+ p2.lon = bbox->west;
119
+ double d = H3_EXPORT(pointDistKm)(&p1, &p2);
120
+ // Derived constant based on: https://math.stackexchange.com/a/1921940
121
+ // Clamped to 3 as higher values tend to rapidly drag the estimate to zero.
122
+ double a = d * d / fmin(3.0, fabs((p1.lon - p2.lon) / (p1.lat - p2.lat)));
107
123
 
108
- // Determine the length of the bounding box "radius" to then use
109
- // as a circle on the earth that the k-rings must be greater than
110
- double bboxRadiusKm = _geoDistKm(&center, &vertex);
124
+ // Divide the two to get an estimate of the number of hexagons needed
125
+ int estimate = (int)ceil(a / pentagonAreaKm2);
126
+ if (estimate == 0) estimate = 1;
127
+ return estimate;
128
+ }
111
129
 
112
- // Determine the radius of the center hexagon
113
- double centerHexRadiusKm = _hexRadiusKm(H3_EXPORT(geoToH3)(&center, res));
130
+ /**
131
+ * lineHexEstimate returns an estimated number of hexagons that trace
132
+ * the cartesian-projected line
133
+ *
134
+ * @param origin the origin coordinates
135
+ * @param destination the destination coordinates
136
+ * @param res the resolution of the H3 hexagons to trace the line
137
+ * @return the estimated number of hexagons required to trace the line
138
+ */
139
+ int lineHexEstimate(const GeoCoord* origin, const GeoCoord* destination,
140
+ int res) {
141
+ // Get the area of the pentagon as the maximally-distorted area possible
142
+ H3Index pentagons[12] = {0};
143
+ H3_EXPORT(getPentagonIndexes)(res, pentagons);
144
+ double pentagonRadiusKm = _hexRadiusKm(pentagons[0]);
114
145
 
115
- // The closest point along a hexagon drawn through the center points
116
- // of a k-ring aggregation is exactly 1.5 radii of the hexagon. For
117
- // any orientation of the GeoJSON encased in a circle defined by the
118
- // bounding box radius and center, it is guaranteed to fit in this k-ring
119
- // Rounded *up* to guarantee containment
120
- return (int)ceil(bboxRadiusKm / (1.5 * centerHexRadiusKm));
146
+ double dist = H3_EXPORT(pointDistKm)(origin, destination);
147
+ int estimate = (int)ceil(dist / (2 * pentagonRadiusKm));
148
+ if (estimate == 0) estimate = 1;
149
+ return estimate;
121
150
  }
@@ -19,10 +19,12 @@
19
19
  */
20
20
 
21
21
  #include "coordijk.h"
22
+
22
23
  #include <math.h>
23
24
  #include <stdio.h>
24
25
  #include <stdlib.h>
25
26
  #include <string.h>
27
+
26
28
  #include "constants.h"
27
29
  #include "geoCoord.h"
28
30
  #include "mathExtensions.h"
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2016-2019 Uber Technologies, Inc.
2
+ * Copyright 2016-2020 Uber Technologies, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -19,11 +19,13 @@
19
19
  */
20
20
 
21
21
  #include "faceijk.h"
22
+
22
23
  #include <assert.h>
23
24
  #include <math.h>
24
25
  #include <stdio.h>
25
26
  #include <stdlib.h>
26
27
  #include <string.h>
28
+
27
29
  #include "constants.h"
28
30
  #include "coordijk.h"
29
31
  #include "geoCoord.h"
@@ -499,20 +501,28 @@ void _faceIjkToGeo(const FaceIJK* h, int res, GeoCoord* g) {
499
501
  *
500
502
  * @param h The FaceIJK address of the pentagonal cell.
501
503
  * @param res The H3 resolution of the cell.
504
+ * @param start The first topological vertex to return.
505
+ * @param length The number of topological vertexes to return.
502
506
  * @param g The spherical coordinates of the cell boundary.
503
507
  */
504
- void _faceIjkPentToGeoBoundary(const FaceIJK* h, int res, GeoBoundary* g) {
508
+ void _faceIjkPentToGeoBoundary(const FaceIJK* h, int res, int start, int length,
509
+ GeoBoundary* g) {
505
510
  int adjRes = res;
506
511
  FaceIJK centerIJK = *h;
507
512
  FaceIJK fijkVerts[NUM_PENT_VERTS];
508
513
  _faceIjkPentToVerts(&centerIJK, &adjRes, fijkVerts);
509
514
 
515
+ // If we're returning the entire loop, we need one more iteration in case
516
+ // of a distortion vertex on the last edge
517
+ int additionalIteration = length == NUM_PENT_VERTS ? 1 : 0;
518
+
510
519
  // convert each vertex to lat/lon
511
520
  // adjust the face of each vertex as appropriate and introduce
512
521
  // edge-crossing vertices as needed
513
522
  g->numVerts = 0;
514
523
  FaceIJK lastFijk;
515
- for (int vert = 0; vert < NUM_PENT_VERTS + 1; vert++) {
524
+ for (int vert = start; vert < start + length + additionalIteration;
525
+ vert++) {
516
526
  int v = vert % NUM_PENT_VERTS;
517
527
 
518
528
  FaceIJK fijk = fijkVerts[v];
@@ -522,7 +532,7 @@ void _faceIjkPentToGeoBoundary(const FaceIJK* h, int res, GeoBoundary* g) {
522
532
  // all Class III pentagon edges cross icosa edges
523
533
  // note that Class II pentagons have vertices on the edge,
524
534
  // not edge intersections
525
- if (isResClassIII(res) && vert > 0) {
535
+ if (isResClassIII(res) && vert > start) {
526
536
  // find hex2d of the two vertexes on the last face
527
537
 
528
538
  FaceIJK tmpFijk = fijk;
@@ -583,9 +593,9 @@ void _faceIjkPentToGeoBoundary(const FaceIJK* h, int res, GeoBoundary* g) {
583
593
  }
584
594
 
585
595
  // convert vertex to lat/lon and add to the result
586
- // vert == NUM_PENT_VERTS is only used to test for possible intersection
587
- // on last edge
588
- if (vert < NUM_PENT_VERTS) {
596
+ // vert == start + NUM_PENT_VERTS is only used to test for possible
597
+ // intersection on last edge
598
+ if (vert < start + NUM_PENT_VERTS) {
589
599
  Vec2d vec;
590
600
  _ijkToHex2d(&fijk.coord, &vec);
591
601
  _hex2dToGeo(&vec, fijk.face, adjRes, 1, &g->verts[g->numVerts]);
@@ -664,34 +674,35 @@ void _faceIjkPentToVerts(FaceIJK* fijk, int* res, FaceIJK* fijkVerts) {
664
674
  *
665
675
  * @param h The FaceIJK address of the cell.
666
676
  * @param res The H3 resolution of the cell.
667
- * @param isPentagon Whether or not the cell is a pentagon.
677
+ * @param start The first topological vertex to return.
678
+ * @param length The number of topological vertexes to return.
668
679
  * @param g The spherical coordinates of the cell boundary.
669
680
  */
670
- void _faceIjkToGeoBoundary(const FaceIJK* h, int res, int isPentagon,
681
+ void _faceIjkToGeoBoundary(const FaceIJK* h, int res, int start, int length,
671
682
  GeoBoundary* g) {
672
- if (isPentagon) {
673
- _faceIjkPentToGeoBoundary(h, res, g);
674
- return;
675
- }
676
-
677
683
  int adjRes = res;
678
684
  FaceIJK centerIJK = *h;
679
685
  FaceIJK fijkVerts[NUM_HEX_VERTS];
680
686
  _faceIjkToVerts(&centerIJK, &adjRes, fijkVerts);
681
687
 
688
+ // If we're returning the entire loop, we need one more iteration in case
689
+ // of a distortion vertex on the last edge
690
+ int additionalIteration = length == NUM_HEX_VERTS ? 1 : 0;
691
+
682
692
  // convert each vertex to lat/lon
683
693
  // adjust the face of each vertex as appropriate and introduce
684
694
  // edge-crossing vertices as needed
685
695
  g->numVerts = 0;
686
696
  int lastFace = -1;
687
- int lastOverage = 0; // 0: none; 1: edge; 2: overage
688
- for (int vert = 0; vert < NUM_HEX_VERTS + 1; vert++) {
697
+ Overage lastOverage = NO_OVERAGE;
698
+ for (int vert = start; vert < start + length + additionalIteration;
699
+ vert++) {
689
700
  int v = vert % NUM_HEX_VERTS;
690
701
 
691
702
  FaceIJK fijk = fijkVerts[v];
692
703
 
693
- int pentLeading4 = 0;
694
- int overage = _adjustOverageClassII(&fijk, adjRes, pentLeading4, 1);
704
+ const int pentLeading4 = 0;
705
+ Overage overage = _adjustOverageClassII(&fijk, adjRes, pentLeading4, 1);
695
706
 
696
707
  /*
697
708
  Check for edge-crossing. Each face of the underlying icosahedron is a
@@ -702,8 +713,8 @@ void _faceIjkToGeoBoundary(const FaceIJK* h, int res, int isPentagon,
702
713
  projection. Note that Class II cell edges have vertices on the face
703
714
  edge, with no edge line intersections.
704
715
  */
705
- if (isResClassIII(res) && vert > 0 && fijk.face != lastFace &&
706
- lastOverage != 1) {
716
+ if (isResClassIII(res) && vert > start && fijk.face != lastFace &&
717
+ lastOverage != FACE_EDGE) {
707
718
  // find hex2d of the two vertexes on original face
708
719
  int lastV = (v + 5) % NUM_HEX_VERTS;
709
720
  Vec2d orig2d0;
@@ -730,7 +741,7 @@ void _faceIjkToGeoBoundary(const FaceIJK* h, int res, int isPentagon,
730
741
  edge0 = &v1;
731
742
  edge1 = &v2;
732
743
  break;
733
- case KI:
744
+ // case KI:
734
745
  default:
735
746
  assert(adjacentFaceDir[centerIJK.face][face2] == KI);
736
747
  edge0 = &v2;
@@ -756,9 +767,9 @@ void _faceIjkToGeoBoundary(const FaceIJK* h, int res, int isPentagon,
756
767
  }
757
768
 
758
769
  // convert vertex to lat/lon and add to the result
759
- // vert == NUM_HEX_VERTS is only used to test for possible intersection
760
- // on last edge
761
- if (vert < NUM_HEX_VERTS) {
770
+ // vert == start + NUM_HEX_VERTS is only used to test for possible
771
+ // intersection on last edge
772
+ if (vert < start + NUM_HEX_VERTS) {
762
773
  Vec2d vec;
763
774
  _ijkToHex2d(&fijk.coord, &vec);
764
775
  _hex2dToGeo(&vec, fijk.face, adjRes, 1, &g->verts[g->numVerts]);
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2016-2017 Uber Technologies, Inc.
2
+ * Copyright 2016-2020 Uber Technologies, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -18,8 +18,10 @@
18
18
  */
19
19
 
20
20
  #include "geoCoord.h"
21
+
21
22
  #include <math.h>
22
23
  #include <stdbool.h>
24
+
23
25
  #include "constants.h"
24
26
  #include "h3api.h"
25
27
 
@@ -45,7 +47,7 @@ double _posAngleRads(double rads) {
45
47
  * @return Whether or not the two coordinates are within the threshold distance
46
48
  * of each other.
47
49
  */
48
- bool geoAlmostEqualThreshold(const GeoCoord* p1, const GeoCoord* p2,
50
+ bool geoAlmostEqualThreshold(const GeoCoord *p1, const GeoCoord *p2,
49
51
  double threshold) {
50
52
  return fabs(p1->lat - p2->lat) < threshold &&
51
53
  fabs(p1->lon - p2->lon) < threshold;
@@ -60,18 +62,18 @@ bool geoAlmostEqualThreshold(const GeoCoord* p1, const GeoCoord* p2,
60
62
  * @return Whether or not the two coordinates are within the epsilon distance
61
63
  * of each other.
62
64
  */
63
- bool geoAlmostEqual(const GeoCoord* p1, const GeoCoord* p2) {
65
+ bool geoAlmostEqual(const GeoCoord *p1, const GeoCoord *p2) {
64
66
  return geoAlmostEqualThreshold(p1, p2, EPSILON_RAD);
65
67
  }
66
68
 
67
69
  /**
68
70
  * Set the components of spherical coordinates in decimal degrees.
69
71
  *
70
- * @param p The spherical coodinates.
71
- * @param latDegs The desired latitidue in decimal degrees.
72
+ * @param p The spherical coordinates.
73
+ * @param latDegs The desired latitude in decimal degrees.
72
74
  * @param lonDegs The desired longitude in decimal degrees.
73
75
  */
74
- void setGeoDegs(GeoCoord* p, double latDegs, double lonDegs) {
76
+ void setGeoDegs(GeoCoord *p, double latDegs, double lonDegs) {
75
77
  _setGeoRads(p, H3_EXPORT(degsToRads)(latDegs),
76
78
  H3_EXPORT(degsToRads)(lonDegs));
77
79
  }
@@ -79,11 +81,11 @@ void setGeoDegs(GeoCoord* p, double latDegs, double lonDegs) {
79
81
  /**
80
82
  * Set the components of spherical coordinates in radians.
81
83
  *
82
- * @param p The spherical coodinates.
83
- * @param latRads The desired latitidue in decimal radians.
84
+ * @param p The spherical coordinates.
85
+ * @param latRads The desired latitude in decimal radians.
84
86
  * @param lonRads The desired longitude in decimal radians.
85
87
  */
86
- void _setGeoRads(GeoCoord* p, double latRads, double lonRads) {
88
+ void _setGeoRads(GeoCoord *p, double latRads, double lonRads) {
87
89
  p->lat = latRads;
88
90
  p->lon = lonRads;
89
91
  }
@@ -134,47 +136,39 @@ double constrainLng(double lng) {
134
136
  }
135
137
 
136
138
  /**
137
- * Find the great circle distance in radians between two spherical coordinates.
139
+ * The great circle distance in radians between two spherical coordinates.
138
140
  *
139
- * @param p1 The first spherical coordinates.
140
- * @param p2 The second spherical coordinates.
141
- * @return The great circle distance in radians between p1 and p2.
141
+ * This function uses the Haversine formula.
142
+ * For math details, see:
143
+ * https://en.wikipedia.org/wiki/Haversine_formula
144
+ * https://www.movable-type.co.uk/scripts/latlong.html
145
+ *
146
+ * @param a the first lat/lng pair (in radians)
147
+ * @param b the second lat/lng pair (in radians)
148
+ *
149
+ * @return the great circle distance in radians between a and b
142
150
  */
143
- double _geoDistRads(const GeoCoord* p1, const GeoCoord* p2) {
144
- // use spherical triangle with p1 at A, p2 at B, and north pole at C
145
- double bigC = fabs(p2->lon - p1->lon);
146
- if (bigC > M_PI) // assume we want the complement
147
- {
148
- // note that in this case they can't both be negative
149
- double lon1 = p1->lon;
150
- if (lon1 < 0.0L) lon1 += 2.0L * M_PI;
151
- double lon2 = p2->lon;
152
- if (lon2 < 0.0L) lon2 += 2.0L * M_PI;
153
-
154
- bigC = fabs(lon2 - lon1);
155
- }
151
+ double H3_EXPORT(pointDistRads)(const GeoCoord *a, const GeoCoord *b) {
152
+ double sinLat = sin((b->lat - a->lat) / 2.0);
153
+ double sinLng = sin((b->lon - a->lon) / 2.0);
156
154
 
157
- double b = M_PI_2 - p1->lat;
158
- double a = M_PI_2 - p2->lat;
155
+ double A = sinLat * sinLat + cos(a->lat) * cos(b->lat) * sinLng * sinLng;
159
156
 
160
- // use law of cosines to find c
161
- double cosc = cos(a) * cos(b) + sin(a) * sin(b) * cos(bigC);
162
- if (cosc > 1.0L) cosc = 1.0L;
163
- if (cosc < -1.0L) cosc = -1.0L;
157
+ return 2 * atan2(sqrt(A), sqrt(1 - A));
158
+ }
164
159
 
165
- return acos(cosc);
160
+ /**
161
+ * The great circle distance in kilometers between two spherical coordinates.
162
+ */
163
+ double H3_EXPORT(pointDistKm)(const GeoCoord *a, const GeoCoord *b) {
164
+ return H3_EXPORT(pointDistRads)(a, b) * EARTH_RADIUS_KM;
166
165
  }
167
166
 
168
167
  /**
169
- * Find the great circle distance in kilometers between two spherical
170
- * coordinates.
171
- *
172
- * @param p1 The first spherical coordinates.
173
- * @param p2 The second spherical coordinates.
174
- * @return The distance in kilometers between p1 and p2.
168
+ * The great circle distance in meters between two spherical coordinates.
175
169
  */
176
- double _geoDistKm(const GeoCoord* p1, const GeoCoord* p2) {
177
- return EARTH_RADIUS_KM * _geoDistRads(p1, p2);
170
+ double H3_EXPORT(pointDistM)(const GeoCoord *a, const GeoCoord *b) {
171
+ return H3_EXPORT(pointDistKm)(a, b) * 1000;
178
172
  }
179
173
 
180
174
  /**
@@ -184,7 +178,7 @@ double _geoDistKm(const GeoCoord* p1, const GeoCoord* p2) {
184
178
  * @param p2 The second spherical coordinates.
185
179
  * @return The azimuth in radians from p1 to p2.
186
180
  */
187
- double _geoAzimuthRads(const GeoCoord* p1, const GeoCoord* p2) {
181
+ double _geoAzimuthRads(const GeoCoord *p1, const GeoCoord *p2) {
188
182
  return atan2(cos(p2->lat) * sin(p2->lon - p1->lon),
189
183
  cos(p1->lat) * sin(p2->lat) -
190
184
  sin(p1->lat) * cos(p2->lat) * cos(p2->lon - p1->lon));
@@ -200,8 +194,8 @@ double _geoAzimuthRads(const GeoCoord* p1, const GeoCoord* p2) {
200
194
  * @param p2 The spherical coordinates at the desired azimuth and distance from
201
195
  * p1.
202
196
  */
203
- void _geoAzDistanceRads(const GeoCoord* p1, double az, double distance,
204
- GeoCoord* p2) {
197
+ void _geoAzDistanceRads(const GeoCoord *p1, double az, double distance,
198
+ GeoCoord *p2) {
205
199
  if (distance < EPSILON) {
206
200
  *p2 = *p1;
207
201
  return;
@@ -302,6 +296,16 @@ double H3_EXPORT(edgeLengthM)(int res) {
302
296
 
303
297
  /** @brief Number of unique valid H3Indexes at given resolution. */
304
298
  int64_t H3_EXPORT(numHexagons)(int res) {
299
+ /**
300
+ * Note: this *actually* returns the number of *cells*
301
+ * (which includes the 12 pentagons) at each resolution.
302
+ *
303
+ * This table comes from the recurrence:
304
+ *
305
+ * num_cells(0) = 122
306
+ * num_cells(i+1) = (num_cells(i)-12)*7 + 12*6
307
+ *
308
+ */
305
309
  static const int64_t nums[] = {122L,
306
310
  842L,
307
311
  5882L,
@@ -320,3 +324,117 @@ int64_t H3_EXPORT(numHexagons)(int res) {
320
324
  569707381193162L};
321
325
  return nums[res];
322
326
  }
327
+
328
+ /**
329
+ * Surface area in radians^2 of spherical triangle on unit sphere.
330
+ *
331
+ * For the math, see:
332
+ * https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess
333
+ *
334
+ * @param a length of triangle side A in radians
335
+ * @param b length of triangle side B in radians
336
+ * @param c length of triangle side C in radians
337
+ *
338
+ * @return area in radians^2 of triangle on unit sphere
339
+ */
340
+ double triangleEdgeLengthsToArea(double a, double b, double c) {
341
+ double s = (a + b + c) / 2;
342
+
343
+ a = (s - a) / 2;
344
+ b = (s - b) / 2;
345
+ c = (s - c) / 2;
346
+ s = s / 2;
347
+
348
+ return 4 * atan(sqrt(tan(s) * tan(a) * tan(b) * tan(c)));
349
+ }
350
+
351
+ /**
352
+ * Compute area in radians^2 of a spherical triangle, given its vertices.
353
+ *
354
+ * @param a vertex lat/lng in radians
355
+ * @param b vertex lat/lng in radians
356
+ * @param c vertex lat/lng in radians
357
+ *
358
+ * @return area of triangle on unit sphere, in radians^2
359
+ */
360
+ double triangleArea(const GeoCoord *a, const GeoCoord *b, const GeoCoord *c) {
361
+ return triangleEdgeLengthsToArea(H3_EXPORT(pointDistRads)(a, b),
362
+ H3_EXPORT(pointDistRads)(b, c),
363
+ H3_EXPORT(pointDistRads)(c, a));
364
+ }
365
+
366
+ /**
367
+ * Area of H3 cell in radians^2.
368
+ *
369
+ * The area is calculated by breaking the cell into spherical triangles and
370
+ * summing up their areas. Note that some H3 cells (hexagons and pentagons)
371
+ * are irregular, and have more than 6 or 5 sides.
372
+ *
373
+ * todo: optimize the computation by re-using the edges shared between triangles
374
+ *
375
+ * @param cell H3 cell
376
+ *
377
+ * @return cell area in radians^2
378
+ */
379
+ double H3_EXPORT(cellAreaRads2)(H3Index cell) {
380
+ GeoCoord c;
381
+ GeoBoundary gb;
382
+ H3_EXPORT(h3ToGeo)(cell, &c);
383
+ H3_EXPORT(h3ToGeoBoundary)(cell, &gb);
384
+
385
+ double area = 0.0;
386
+ for (int i = 0; i < gb.numVerts; i++) {
387
+ int j = (i + 1) % gb.numVerts;
388
+ area += triangleArea(&gb.verts[i], &gb.verts[j], &c);
389
+ }
390
+
391
+ return area;
392
+ }
393
+
394
+ /**
395
+ * Area of H3 cell in kilometers^2.
396
+ */
397
+ double H3_EXPORT(cellAreaKm2)(H3Index h) {
398
+ return H3_EXPORT(cellAreaRads2)(h) * EARTH_RADIUS_KM * EARTH_RADIUS_KM;
399
+ }
400
+
401
+ /**
402
+ * Area of H3 cell in meters^2.
403
+ */
404
+ double H3_EXPORT(cellAreaM2)(H3Index h) {
405
+ return H3_EXPORT(cellAreaKm2)(h) * 1000 * 1000;
406
+ }
407
+
408
+ /**
409
+ * Length of a unidirectional edge in radians.
410
+ *
411
+ * @param edge H3 unidirectional edge
412
+ *
413
+ * @return length in radians
414
+ */
415
+ double H3_EXPORT(exactEdgeLengthRads)(H3Index edge) {
416
+ GeoBoundary gb;
417
+
418
+ H3_EXPORT(getH3UnidirectionalEdgeBoundary)(edge, &gb);
419
+
420
+ double length = 0.0;
421
+ for (int i = 0; i < gb.numVerts - 1; i++) {
422
+ length += H3_EXPORT(pointDistRads)(&gb.verts[i], &gb.verts[i + 1]);
423
+ }
424
+
425
+ return length;
426
+ }
427
+
428
+ /**
429
+ * Length of a unidirectional edge in kilometers.
430
+ */
431
+ double H3_EXPORT(exactEdgeLengthKm)(H3Index edge) {
432
+ return H3_EXPORT(exactEdgeLengthRads)(edge) * EARTH_RADIUS_KM;
433
+ }
434
+
435
+ /**
436
+ * Length of a unidirectional edge in meters.
437
+ */
438
+ double H3_EXPORT(exactEdgeLengthM)(H3Index edge) {
439
+ return H3_EXPORT(exactEdgeLengthKm)(edge) * 1000;
440
+ }