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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +23 -0
- data/Gemfile.lock +6 -6
- data/README.md +1 -1
- data/ext/h3/src/.github/workflows/test-linux.yml +118 -0
- data/ext/h3/src/.github/workflows/test-macos.yml +42 -0
- data/ext/h3/src/.github/workflows/test-website.yml +32 -0
- data/ext/h3/src/.github/workflows/test-windows.yml +44 -0
- data/ext/h3/src/.gitignore +5 -0
- data/ext/h3/src/.travis.yml +16 -46
- data/ext/h3/src/CHANGELOG.md +43 -0
- data/ext/h3/src/CMakeLists.txt +133 -33
- data/ext/h3/src/CONTRIBUTING.md +1 -1
- data/ext/h3/src/README.md +60 -10
- data/ext/h3/src/RELEASE.md +3 -1
- data/ext/h3/src/VERSION +1 -1
- data/ext/h3/src/dev-docs/RFCs/rfc-template.md +21 -0
- data/ext/h3/src/dev-docs/RFCs/v4.0.0/error-handling-rfc.md +21 -0
- data/ext/h3/src/dev-docs/RFCs/v4.0.0/names_for_concepts_types_functions.md +276 -0
- data/ext/h3/src/dev-docs/RFCs/v4.0.0/overrideable-allocators-rfc.md +141 -0
- data/ext/h3/src/dev-docs/RFCs/v4.0.0/polyfill-modes-rfc.md +21 -0
- data/ext/h3/src/dev-docs/RFCs/v4.0.0/vertex-mode-rfc.md +50 -0
- data/ext/h3/src/dev-docs/build_windows.md +6 -1
- data/ext/h3/src/dev-docs/creating_bindings.md +3 -3
- data/ext/h3/src/dev-docs/custom_alloc.md +27 -0
- data/ext/h3/src/docs/{overview/mainpage.md → README.md} +2 -3
- data/ext/h3/src/docs/api/misc.md +76 -0
- data/ext/h3/src/docs/community/applications.md +1 -0
- data/ext/h3/src/docs/community/bindings.md +7 -1
- data/ext/h3/src/docs/community/tutorials.md +8 -3
- data/ext/h3/src/docs/core-library/coordsystems.md +5 -4
- data/ext/h3/src/docs/core-library/filters.md +8 -9
- data/ext/h3/src/docs/core-library/geoToH3desc.md +2 -3
- data/ext/h3/src/docs/core-library/h3ToGeoBoundaryDesc.md +4 -5
- data/ext/h3/src/docs/core-library/h3ToGeoDesc.md +3 -4
- data/ext/h3/src/docs/core-library/h3indexing.md +26 -17
- data/ext/h3/src/docs/core-library/overview.md +2 -3
- data/ext/h3/src/docs/core-library/restable.md +1 -2
- data/ext/h3/src/docs/core-library/usage.md +1 -2
- data/ext/h3/src/docs/table-of-contents.json +47 -0
- data/ext/h3/src/docs/{overview/usecases.md → usecases.md} +6 -11
- data/ext/h3/src/scripts/binding_functions.sh +1 -1
- data/ext/h3/src/scripts/coverage.sh.in +1 -1
- data/ext/h3/src/scripts/update_version.sh +2 -2
- data/ext/h3/src/src/apps/applib/include/args.h +1 -0
- data/ext/h3/src/src/apps/applib/include/test.h +1 -0
- data/ext/h3/src/src/apps/applib/include/utility.h +7 -1
- data/ext/h3/src/src/apps/applib/lib/args.c +2 -0
- data/ext/h3/src/src/apps/applib/lib/kml.c +2 -0
- data/ext/h3/src/src/apps/applib/lib/test.c +1 -0
- data/ext/h3/src/src/apps/applib/lib/utility.c +133 -2
- data/ext/h3/src/src/apps/benchmarks/benchmarkH3Api.c +1 -1
- data/ext/h3/src/{website/html.config.js → src/apps/benchmarks/benchmarkH3UniEdge.c} +15 -12
- data/ext/h3/src/src/apps/filters/h3ToComponents.c +1 -0
- data/ext/h3/src/src/apps/filters/h3ToGeo.c +1 -0
- data/ext/h3/src/src/apps/filters/h3ToGeoBoundary.c +1 -0
- data/ext/h3/src/src/apps/filters/h3ToLocalIj.c +1 -0
- data/ext/h3/src/src/apps/filters/hexRange.c +1 -0
- data/ext/h3/src/src/apps/filters/kRing.c +1 -0
- data/ext/h3/src/src/apps/filters/localIjToH3.c +1 -0
- data/ext/h3/src/src/apps/miscapps/generateFaceCenterPoint.c +1 -0
- data/ext/h3/src/src/apps/miscapps/generateNumHexagons.c +1 -0
- data/ext/h3/src/src/apps/miscapps/generatePentagonDirectionFaces.c +67 -0
- data/ext/h3/src/src/apps/miscapps/h3ToGeoBoundaryHier.c +1 -0
- data/ext/h3/src/src/apps/miscapps/h3ToGeoHier.c +1 -0
- data/ext/h3/src/src/apps/miscapps/h3ToHier.c +1 -0
- data/ext/h3/src/src/apps/testapps/mkRandGeo.c +1 -0
- data/ext/h3/src/src/apps/testapps/mkRandGeoBoundary.c +1 -0
- data/ext/h3/src/src/apps/testapps/testBBox.c +1 -0
- data/ext/h3/src/src/apps/testapps/testBaseCells.c +15 -1
- data/ext/h3/src/src/apps/testapps/testCompact.c +75 -0
- data/ext/h3/src/src/apps/testapps/testCoordIj.c +1 -0
- data/ext/h3/src/src/apps/testapps/testGeoCoord.c +40 -13
- data/ext/h3/src/src/apps/testapps/testGeoToH3.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3Api.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3CellArea.c +47 -0
- data/ext/h3/src/src/apps/testapps/testH3CellAreaExhaustive.c +180 -0
- data/ext/h3/src/src/apps/testapps/testH3Distance.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3DistanceExhaustive.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3GetFaces.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3Index.c +33 -3
- data/ext/h3/src/src/apps/testapps/testH3Line.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3LineExhaustive.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3Memory.c +175 -0
- data/ext/h3/src/src/apps/testapps/testH3NeighborRotations.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3SetToLinkedGeo.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3SetToVertexGraph.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3ToCenterChild.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3ToChildren.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3ToGeo.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3ToGeoBoundary.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3ToLocalIj.c +9 -5
- data/ext/h3/src/src/apps/testapps/testH3ToLocalIjExhaustive.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3ToParent.c +1 -0
- data/ext/h3/src/src/apps/testapps/testH3UniEdge.c +45 -16
- data/ext/h3/src/src/apps/testapps/testH3UniEdgeExhaustive.c +111 -0
- data/ext/h3/src/src/apps/testapps/testHexRanges.c +1 -0
- data/ext/h3/src/src/apps/testapps/testHexRing.c +1 -0
- data/ext/h3/src/src/apps/testapps/testKRing.c +1 -0
- data/ext/h3/src/src/apps/testapps/testLinkedGeo.c +1 -0
- data/ext/h3/src/src/apps/testapps/testMaxH3ToChildrenSize.c +1 -0
- data/ext/h3/src/src/apps/testapps/testPentagonIndexes.c +1 -0
- data/ext/h3/src/src/apps/testapps/testPolyfill.c +72 -9
- data/ext/h3/src/src/apps/testapps/testPolyfillReported.c +157 -0
- data/ext/h3/src/src/apps/testapps/testPolygon.c +1 -0
- data/ext/h3/src/src/apps/testapps/testVec2d.c +1 -0
- data/ext/h3/src/src/apps/testapps/testVec3d.c +1 -0
- data/ext/h3/src/src/apps/testapps/testVertex.c +66 -0
- data/ext/h3/src/src/apps/testapps/testVertexGraph.c +1 -0
- data/ext/h3/src/src/h3lib/include/algos.h +8 -0
- data/ext/h3/src/src/h3lib/include/alloc.h +40 -0
- data/ext/h3/src/src/h3lib/include/baseCells.h +4 -0
- data/ext/h3/src/src/h3lib/include/bbox.h +4 -1
- data/ext/h3/src/src/h3lib/include/faceijk.h +3 -2
- data/ext/h3/src/src/h3lib/include/geoCoord.h +2 -3
- data/ext/h3/src/src/h3lib/include/h3Index.h +37 -4
- data/ext/h3/src/src/h3lib/include/h3api.h.in +65 -17
- data/ext/h3/src/src/h3lib/include/linkedGeo.h +1 -0
- data/ext/h3/src/src/h3lib/include/polygon.h +1 -0
- data/ext/h3/src/src/h3lib/include/polygonAlgos.h +1 -0
- data/ext/h3/src/src/h3lib/include/vertex.h +44 -0
- data/ext/h3/src/src/h3lib/include/vertexGraph.h +1 -0
- data/ext/h3/src/src/h3lib/lib/algos.c +300 -75
- data/ext/h3/src/src/h3lib/lib/baseCells.c +26 -4
- data/ext/h3/src/src/h3lib/lib/bbox.c +56 -31
- data/ext/h3/src/src/h3lib/lib/coordijk.c +2 -0
- data/ext/h3/src/src/h3lib/lib/faceijk.c +32 -21
- data/ext/h3/src/src/h3lib/lib/geoCoord.c +162 -44
- data/ext/h3/src/src/h3lib/lib/h3Index.c +81 -43
- data/ext/h3/src/src/h3lib/lib/h3UniEdge.c +42 -57
- data/ext/h3/src/src/h3lib/lib/linkedGeo.c +20 -15
- data/ext/h3/src/src/h3lib/lib/localij.c +1 -1
- data/ext/h3/src/src/h3lib/lib/polygon.c +2 -0
- data/ext/h3/src/src/h3lib/lib/vec2d.c +1 -0
- data/ext/h3/src/src/h3lib/lib/vec3d.c +1 -0
- data/ext/h3/src/src/h3lib/lib/vertex.c +134 -0
- data/ext/h3/src/src/h3lib/lib/vertexGraph.c +8 -5
- data/ext/h3/src/website/.eslintignore +2 -0
- data/ext/h3/src/website/.gitignore +57 -0
- data/ext/h3/src/website/.nvmrc +1 -0
- data/ext/h3/src/website/README.md +8 -6
- data/ext/h3/src/website/gatsby-config.js +83 -0
- data/ext/h3/src/website/package.json +20 -12
- data/ext/h3/src/website/scripts/build-to-gh-pages.sh +7 -5
- data/ext/h3/src/website/src/.gitkeep +0 -0
- data/ext/h3/src/website/templates/documentation.jsx +129 -0
- data/ext/h3/src/website/yarn.lock +13723 -0
- data/lib/h3/bindings/private.rb +3 -0
- data/lib/h3/miscellaneous.rb +123 -0
- data/lib/h3/version.rb +1 -1
- data/spec/miscellaneous_spec.rb +117 -0
- data/spec/regions_spec.rb +1 -1
- metadata +35 -14
- data/ext/h3/src/.ycm_extra_conf.py +0 -92
- data/ext/h3/src/appveyor.yml +0 -50
- data/ext/h3/src/src/apps/testapps/testPolyfill_GH136.c +0 -58
- data/ext/h3/src/website/src/config.js +0 -46
- data/ext/h3/src/website/src/mdRoutes.js +0 -151
- data/ext/h3/src/website/src/styles/_variables.scss +0 -16
- data/ext/h3/src/website/src/styles/index.scss +0 -3
- data/ext/h3/src/website/static/index.html +0 -15
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright 2016-
|
|
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
|
|
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
|
-
}
|
|
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
|
|
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-
|
|
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
|
|
89
|
+
return H3_EXPORT(pointDistKm)(&h3Center, h3Boundary.verts);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* @param
|
|
95
|
-
* @
|
|
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
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
113
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright 2016-
|
|
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,
|
|
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(¢erIJK, &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 =
|
|
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 >
|
|
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
|
|
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
|
|
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
|
|
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(¢erIJK, &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 =
|
|
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 >
|
|
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
|
|
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-
|
|
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*
|
|
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*
|
|
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
|
|
71
|
-
* @param latDegs The desired
|
|
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*
|
|
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
|
|
83
|
-
* @param latRads The desired
|
|
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*
|
|
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
|
-
*
|
|
139
|
+
* The great circle distance in radians between two spherical coordinates.
|
|
138
140
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
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
|
|
144
|
-
|
|
145
|
-
double
|
|
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
|
|
158
|
-
double a = M_PI_2 - p2->lat;
|
|
155
|
+
double A = sinLat * sinLat + cos(a->lat) * cos(b->lat) * sinLng * sinLng;
|
|
159
156
|
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
177
|
-
return
|
|
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*
|
|
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*
|
|
204
|
-
GeoCoord*
|
|
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
|
+
}
|