h3 3.6.2 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +23 -0
  3. data/Gemfile.lock +6 -6
  4. data/README.md +1 -1
  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 +16 -46
  11. data/ext/h3/src/CHANGELOG.md +43 -0
  12. data/ext/h3/src/CMakeLists.txt +133 -33
  13. data/ext/h3/src/CONTRIBUTING.md +1 -1
  14. data/ext/h3/src/README.md +60 -10
  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/misc.md +76 -0
  28. data/ext/h3/src/docs/community/applications.md +1 -0
  29. data/ext/h3/src/docs/community/bindings.md +7 -1
  30. data/ext/h3/src/docs/community/tutorials.md +8 -3
  31. data/ext/h3/src/docs/core-library/coordsystems.md +5 -4
  32. data/ext/h3/src/docs/core-library/filters.md +8 -9
  33. data/ext/h3/src/docs/core-library/geoToH3desc.md +2 -3
  34. data/ext/h3/src/docs/core-library/h3ToGeoBoundaryDesc.md +4 -5
  35. data/ext/h3/src/docs/core-library/h3ToGeoDesc.md +3 -4
  36. data/ext/h3/src/docs/core-library/h3indexing.md +26 -17
  37. data/ext/h3/src/docs/core-library/overview.md +2 -3
  38. data/ext/h3/src/docs/core-library/restable.md +1 -2
  39. data/ext/h3/src/docs/core-library/usage.md +1 -2
  40. data/ext/h3/src/docs/table-of-contents.json +47 -0
  41. data/ext/h3/src/docs/{overview/usecases.md → usecases.md} +6 -11
  42. data/ext/h3/src/scripts/binding_functions.sh +1 -1
  43. data/ext/h3/src/scripts/coverage.sh.in +1 -1
  44. data/ext/h3/src/scripts/update_version.sh +2 -2
  45. data/ext/h3/src/src/apps/applib/include/args.h +1 -0
  46. data/ext/h3/src/src/apps/applib/include/test.h +1 -0
  47. data/ext/h3/src/src/apps/applib/include/utility.h +7 -1
  48. data/ext/h3/src/src/apps/applib/lib/args.c +2 -0
  49. data/ext/h3/src/src/apps/applib/lib/kml.c +2 -0
  50. data/ext/h3/src/src/apps/applib/lib/test.c +1 -0
  51. data/ext/h3/src/src/apps/applib/lib/utility.c +133 -2
  52. data/ext/h3/src/src/apps/benchmarks/benchmarkH3Api.c +1 -1
  53. data/ext/h3/src/{website/html.config.js → src/apps/benchmarks/benchmarkH3UniEdge.c} +15 -12
  54. data/ext/h3/src/src/apps/filters/h3ToComponents.c +1 -0
  55. data/ext/h3/src/src/apps/filters/h3ToGeo.c +1 -0
  56. data/ext/h3/src/src/apps/filters/h3ToGeoBoundary.c +1 -0
  57. data/ext/h3/src/src/apps/filters/h3ToLocalIj.c +1 -0
  58. data/ext/h3/src/src/apps/filters/hexRange.c +1 -0
  59. data/ext/h3/src/src/apps/filters/kRing.c +1 -0
  60. data/ext/h3/src/src/apps/filters/localIjToH3.c +1 -0
  61. data/ext/h3/src/src/apps/miscapps/generateFaceCenterPoint.c +1 -0
  62. data/ext/h3/src/src/apps/miscapps/generateNumHexagons.c +1 -0
  63. data/ext/h3/src/src/apps/miscapps/generatePentagonDirectionFaces.c +67 -0
  64. data/ext/h3/src/src/apps/miscapps/h3ToGeoBoundaryHier.c +1 -0
  65. data/ext/h3/src/src/apps/miscapps/h3ToGeoHier.c +1 -0
  66. data/ext/h3/src/src/apps/miscapps/h3ToHier.c +1 -0
  67. data/ext/h3/src/src/apps/testapps/mkRandGeo.c +1 -0
  68. data/ext/h3/src/src/apps/testapps/mkRandGeoBoundary.c +1 -0
  69. data/ext/h3/src/src/apps/testapps/testBBox.c +1 -0
  70. data/ext/h3/src/src/apps/testapps/testBaseCells.c +15 -1
  71. data/ext/h3/src/src/apps/testapps/testCompact.c +75 -0
  72. data/ext/h3/src/src/apps/testapps/testCoordIj.c +1 -0
  73. data/ext/h3/src/src/apps/testapps/testGeoCoord.c +40 -13
  74. data/ext/h3/src/src/apps/testapps/testGeoToH3.c +1 -0
  75. data/ext/h3/src/src/apps/testapps/testH3Api.c +1 -0
  76. data/ext/h3/src/src/apps/testapps/testH3CellArea.c +47 -0
  77. data/ext/h3/src/src/apps/testapps/testH3CellAreaExhaustive.c +180 -0
  78. data/ext/h3/src/src/apps/testapps/testH3Distance.c +1 -0
  79. data/ext/h3/src/src/apps/testapps/testH3DistanceExhaustive.c +1 -0
  80. data/ext/h3/src/src/apps/testapps/testH3GetFaces.c +1 -0
  81. data/ext/h3/src/src/apps/testapps/testH3Index.c +33 -3
  82. data/ext/h3/src/src/apps/testapps/testH3Line.c +1 -0
  83. data/ext/h3/src/src/apps/testapps/testH3LineExhaustive.c +1 -0
  84. data/ext/h3/src/src/apps/testapps/testH3Memory.c +175 -0
  85. data/ext/h3/src/src/apps/testapps/testH3NeighborRotations.c +1 -0
  86. data/ext/h3/src/src/apps/testapps/testH3SetToLinkedGeo.c +1 -0
  87. data/ext/h3/src/src/apps/testapps/testH3SetToVertexGraph.c +1 -0
  88. data/ext/h3/src/src/apps/testapps/testH3ToCenterChild.c +1 -0
  89. data/ext/h3/src/src/apps/testapps/testH3ToChildren.c +1 -0
  90. data/ext/h3/src/src/apps/testapps/testH3ToGeo.c +1 -0
  91. data/ext/h3/src/src/apps/testapps/testH3ToGeoBoundary.c +1 -0
  92. data/ext/h3/src/src/apps/testapps/testH3ToLocalIj.c +9 -5
  93. data/ext/h3/src/src/apps/testapps/testH3ToLocalIjExhaustive.c +1 -0
  94. data/ext/h3/src/src/apps/testapps/testH3ToParent.c +1 -0
  95. data/ext/h3/src/src/apps/testapps/testH3UniEdge.c +45 -16
  96. data/ext/h3/src/src/apps/testapps/testH3UniEdgeExhaustive.c +111 -0
  97. data/ext/h3/src/src/apps/testapps/testHexRanges.c +1 -0
  98. data/ext/h3/src/src/apps/testapps/testHexRing.c +1 -0
  99. data/ext/h3/src/src/apps/testapps/testKRing.c +1 -0
  100. data/ext/h3/src/src/apps/testapps/testLinkedGeo.c +1 -0
  101. data/ext/h3/src/src/apps/testapps/testMaxH3ToChildrenSize.c +1 -0
  102. data/ext/h3/src/src/apps/testapps/testPentagonIndexes.c +1 -0
  103. data/ext/h3/src/src/apps/testapps/testPolyfill.c +72 -9
  104. data/ext/h3/src/src/apps/testapps/testPolyfillReported.c +157 -0
  105. data/ext/h3/src/src/apps/testapps/testPolygon.c +1 -0
  106. data/ext/h3/src/src/apps/testapps/testVec2d.c +1 -0
  107. data/ext/h3/src/src/apps/testapps/testVec3d.c +1 -0
  108. data/ext/h3/src/src/apps/testapps/testVertex.c +66 -0
  109. data/ext/h3/src/src/apps/testapps/testVertexGraph.c +1 -0
  110. data/ext/h3/src/src/h3lib/include/algos.h +8 -0
  111. data/ext/h3/src/src/h3lib/include/alloc.h +40 -0
  112. data/ext/h3/src/src/h3lib/include/baseCells.h +4 -0
  113. data/ext/h3/src/src/h3lib/include/bbox.h +4 -1
  114. data/ext/h3/src/src/h3lib/include/faceijk.h +3 -2
  115. data/ext/h3/src/src/h3lib/include/geoCoord.h +2 -3
  116. data/ext/h3/src/src/h3lib/include/h3Index.h +37 -4
  117. data/ext/h3/src/src/h3lib/include/h3api.h.in +65 -17
  118. data/ext/h3/src/src/h3lib/include/linkedGeo.h +1 -0
  119. data/ext/h3/src/src/h3lib/include/polygon.h +1 -0
  120. data/ext/h3/src/src/h3lib/include/polygonAlgos.h +1 -0
  121. data/ext/h3/src/src/h3lib/include/vertex.h +44 -0
  122. data/ext/h3/src/src/h3lib/include/vertexGraph.h +1 -0
  123. data/ext/h3/src/src/h3lib/lib/algos.c +300 -75
  124. data/ext/h3/src/src/h3lib/lib/baseCells.c +26 -4
  125. data/ext/h3/src/src/h3lib/lib/bbox.c +56 -31
  126. data/ext/h3/src/src/h3lib/lib/coordijk.c +2 -0
  127. data/ext/h3/src/src/h3lib/lib/faceijk.c +32 -21
  128. data/ext/h3/src/src/h3lib/lib/geoCoord.c +162 -44
  129. data/ext/h3/src/src/h3lib/lib/h3Index.c +81 -43
  130. data/ext/h3/src/src/h3lib/lib/h3UniEdge.c +42 -57
  131. data/ext/h3/src/src/h3lib/lib/linkedGeo.c +20 -15
  132. data/ext/h3/src/src/h3lib/lib/localij.c +1 -1
  133. data/ext/h3/src/src/h3lib/lib/polygon.c +2 -0
  134. data/ext/h3/src/src/h3lib/lib/vec2d.c +1 -0
  135. data/ext/h3/src/src/h3lib/lib/vec3d.c +1 -0
  136. data/ext/h3/src/src/h3lib/lib/vertex.c +134 -0
  137. data/ext/h3/src/src/h3lib/lib/vertexGraph.c +8 -5
  138. data/ext/h3/src/website/.eslintignore +2 -0
  139. data/ext/h3/src/website/.gitignore +57 -0
  140. data/ext/h3/src/website/.nvmrc +1 -0
  141. data/ext/h3/src/website/README.md +8 -6
  142. data/ext/h3/src/website/gatsby-config.js +83 -0
  143. data/ext/h3/src/website/package.json +20 -12
  144. data/ext/h3/src/website/scripts/build-to-gh-pages.sh +7 -5
  145. data/ext/h3/src/website/src/.gitkeep +0 -0
  146. data/ext/h3/src/website/templates/documentation.jsx +129 -0
  147. data/ext/h3/src/website/yarn.lock +13723 -0
  148. data/lib/h3/bindings/private.rb +3 -0
  149. data/lib/h3/miscellaneous.rb +123 -0
  150. data/lib/h3/version.rb +1 -1
  151. data/spec/miscellaneous_spec.rb +117 -0
  152. data/spec/regions_spec.rb +1 -1
  153. metadata +35 -14
  154. data/ext/h3/src/.ycm_extra_conf.py +0 -92
  155. data/ext/h3/src/appveyor.yml +0 -50
  156. data/ext/h3/src/src/apps/testapps/testPolyfill_GH136.c +0 -58
  157. data/ext/h3/src/website/src/config.js +0 -46
  158. data/ext/h3/src/website/src/mdRoutes.js +0 -151
  159. data/ext/h3/src/website/src/styles/_variables.scss +0 -16
  160. data/ext/h3/src/website/src/styles/index.scss +0 -3
  161. data/ext/h3/src/website/static/index.html +0 -15
@@ -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,42 +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
- // We use centerHexRadiusKm un-scaled and rounded *up* to guarantee
116
- // containment ot the bbox. Ideal, undistorted hexagons could scale
117
- // centerHexRadiusKm by a factor of up to 1.5, reducing bboxHexRadius.
118
- // This is because the closest point along an undistorted hexagon drawn
119
- // through the center points of a k-ring aggregation is exactly 1.5 radii
120
- // of the hexagon. But there is distortion near pentagons, and for those
121
- // cases, the scaling needs to be less than 1.5. Using the un-scaled value
122
- // conservatively guarantees containment for all cases, at the expense of a
123
- // larger bboxHexRadius.
124
- return (int)ceil(bboxRadiusKm / 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;
125
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,33 +674,34 @@ 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
697
  Overage lastOverage = NO_OVERAGE;
688
- for (int vert = 0; vert < NUM_HEX_VERTS + 1; vert++) {
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;
704
+ const int pentLeading4 = 0;
694
705
  Overage overage = _adjustOverageClassII(&fijk, adjRes, pentLeading4, 1);
695
706
 
696
707
  /*
@@ -702,7 +713,7 @@ 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 &&
716
+ if (isResClassIII(res) && vert > start && fijk.face != lastFace &&
706
717
  lastOverage != FACE_EDGE) {
707
718
  // find hex2d of the two vertexes on original face
708
719
  int lastV = (v + 5) % NUM_HEX_VERTS;
@@ -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
+ }