h3 3.6.0 → 3.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby_ci.yml +30 -0
- data/.rubocop.yml +1 -1
- data/.ruby-version +1 -0
- data/CHANGELOG.md +39 -0
- data/Gemfile.lock +6 -24
- data/LICENSE.md +1 -1
- data/README.md +2 -3
- 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 +20 -42
- data/ext/h3/src/CHANGELOG.md +57 -0
- data/ext/h3/src/CMakeLists.txt +135 -33
- data/ext/h3/src/CONTRIBUTING.md +1 -1
- data/ext/h3/src/README.md +61 -11
- 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 +10 -0
- 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 +109 -2
- data/ext/h3/src/src/apps/testapps/testCoordIj.c +1 -0
- data/ext/h3/src/src/apps/testapps/testGeoCoord.c +47 -8
- 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 +12 -6
- 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 +19 -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 +27 -1
- 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 +304 -76
- data/ext/h3/src/src/h3lib/lib/baseCells.c +26 -4
- data/ext/h3/src/src/h3lib/lib/bbox.c +56 -27
- 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 +83 -42
- 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/h3.gemspec +2 -2
- data/lib/h3/bindings/base.rb +14 -4
- data/lib/h3/bindings/private.rb +12 -9
- data/lib/h3/hierarchy.rb +0 -18
- data/lib/h3/indexing.rb +0 -18
- data/lib/h3/inspection.rb +3 -59
- data/lib/h3/miscellaneous.rb +119 -14
- data/lib/h3/regions.rb +3 -0
- data/lib/h3/traversal.rb +0 -18
- data/lib/h3/unidirectional_edges.rb +5 -60
- data/lib/h3/version.rb +1 -1
- data/spec/geo_json_spec.rb +8 -0
- data/spec/miscellaneous_spec.rb +117 -0
- data/spec/{region_spec.rb → regions_spec.rb} +1 -1
- data/spec/spec_helper.rb +2 -2
- metadata +44 -36
- data/.travis.yml +0 -11
- data/ext/h3/src/.ycm_extra_conf.py +0 -92
- data/ext/h3/src/appveyor.yml +0 -50
- 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,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
|
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
|
-
// 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
|
}
|
@@ -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
|
+
}
|