├── .gitignore
├── LICENSE
├── makefile
├── notebooks
├── stats_tables
│ ├── avg_area_table.ipynb
│ ├── cell_counts.ipynb
│ └── extreme_hex_area.ipynb
├── time_h3_apis.ipynb
├── unified_data_layers.ipynb
├── urban_analytics.ipynb
└── usage.ipynb
├── readme.md
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | env/
3 | .ipynb_checkpoints/
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 |
3 | Apache License
4 | Version 2.0, January 2004
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction,
12 | and distribution as defined by Sections 1 through 9 of this document.
13 |
14 | "Licensor" shall mean the copyright owner or entity authorized by
15 | the copyright owner that is granting the License.
16 |
17 | "Legal Entity" shall mean the union of the acting entity and all
18 | other entities that control, are controlled by, or are under common
19 | control with that entity. For the purposes of this definition,
20 | "control" means (i) the power, direct or indirect, to cause the
21 | direction or management of such entity, whether by contract or
22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
23 | outstanding shares, or (iii) beneficial ownership of such entity.
24 |
25 | "You" (or "Your") shall mean an individual or Legal Entity
26 | exercising permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications,
29 | including but not limited to software source code, documentation
30 | source, and configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical
33 | transformation or translation of a Source form, including but
34 | not limited to compiled object code, generated documentation,
35 | and conversions to other media types.
36 |
37 | "Work" shall mean the work of authorship, whether in Source or
38 | Object form, made available under the License, as indicated by a
39 | copyright notice that is included in or attached to the work
40 | (an example is provided in the Appendix below).
41 |
42 | "Derivative Works" shall mean any work, whether in Source or Object
43 | form, that is based on (or derived from) the Work and for which the
44 | editorial revisions, annotations, elaborations, or other modifications
45 | represent, as a whole, an original work of authorship. For the purposes
46 | of this License, Derivative Works shall not include works that remain
47 | separable from, or merely link (or bind by name) to the interfaces of,
48 | the Work and Derivative Works thereof.
49 |
50 | "Contribution" shall mean any work of authorship, including
51 | the original version of the Work and any modifications or additions
52 | to that Work or Derivative Works thereof, that is intentionally
53 | submitted to Licensor for inclusion in the Work by the copyright owner
54 | or by an individual or Legal Entity authorized to submit on behalf of
55 | the copyright owner. For the purposes of this definition, "submitted"
56 | means any form of electronic, verbal, or written communication sent
57 | to the Licensor or its representatives, including but not limited to
58 | communication on electronic mailing lists, source code control systems,
59 | and issue tracking systems that are managed by, or on behalf of, the
60 | Licensor for the purpose of discussing and improving the Work, but
61 | excluding communication that is conspicuously marked or otherwise
62 | designated in writing by the copyright owner as "Not a Contribution."
63 |
64 | "Contributor" shall mean Licensor and any individual or Legal Entity
65 | on behalf of whom a Contribution has been received by Licensor and
66 | subsequently incorporated within the Work.
67 |
68 | 2. Grant of Copyright License. Subject to the terms and conditions of
69 | this License, each Contributor hereby grants to You a perpetual,
70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71 | copyright license to reproduce, prepare Derivative Works of,
72 | publicly display, publicly perform, sublicense, and distribute the
73 | Work and such Derivative Works in Source or Object form.
74 |
75 | 3. Grant of Patent License. Subject to the terms and conditions of
76 | this License, each Contributor hereby grants to You a perpetual,
77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78 | (except as stated in this section) patent license to make, have made,
79 | use, offer to sell, sell, import, and otherwise transfer the Work,
80 | where such license applies only to those patent claims licensable
81 | by such Contributor that are necessarily infringed by their
82 | Contribution(s) alone or by combination of their Contribution(s)
83 | with the Work to which such Contribution(s) was submitted. If You
84 | institute patent litigation against any entity (including a
85 | cross-claim or counterclaim in a lawsuit) alleging that the Work
86 | or a Contribution incorporated within the Work constitutes direct
87 | or contributory patent infringement, then any patent licenses
88 | granted to You under this License for that Work shall terminate
89 | as of the date such litigation is filed.
90 |
91 | 4. Redistribution. You may reproduce and distribute copies of the
92 | Work or Derivative Works thereof in any medium, with or without
93 | modifications, and in Source or Object form, provided that You
94 | meet the following conditions:
95 |
96 | (a) You must give any other recipients of the Work or
97 | Derivative Works a copy of this License; and
98 |
99 | (b) You must cause any modified files to carry prominent notices
100 | stating that You changed the files; and
101 |
102 | (c) You must retain, in the Source form of any Derivative Works
103 | that You distribute, all copyright, patent, trademark, and
104 | attribution notices from the Source form of the Work,
105 | excluding those notices that do not pertain to any part of
106 | the Derivative Works; and
107 |
108 | (d) If the Work includes a "NOTICE" text file as part of its
109 | distribution, then any Derivative Works that You distribute must
110 | include a readable copy of the attribution notices contained
111 | within such NOTICE file, excluding those notices that do not
112 | pertain to any part of the Derivative Works, in at least one
113 | of the following places: within a NOTICE text file distributed
114 | as part of the Derivative Works; within the Source form or
115 | documentation, if provided along with the Derivative Works; or,
116 | within a display generated by the Derivative Works, if and
117 | wherever such third-party notices normally appear. The contents
118 | of the NOTICE file are for informational purposes only and
119 | do not modify the License. You may add Your own attribution
120 | notices within Derivative Works that You distribute, alongside
121 | or as an addendum to the NOTICE text from the Work, provided
122 | that such additional attribution notices cannot be construed
123 | as modifying the License.
124 |
125 | You may add Your own copyright statement to Your modifications and
126 | may provide additional or different license terms and conditions
127 | for use, reproduction, or distribution of Your modifications, or
128 | for any such Derivative Works as a whole, provided Your use,
129 | reproduction, and distribution of the Work otherwise complies with
130 | the conditions stated in this License.
131 |
132 | 5. Submission of Contributions. Unless You explicitly state otherwise,
133 | any Contribution intentionally submitted for inclusion in the Work
134 | by You to the Licensor shall be under the terms and conditions of
135 | this License, without any additional terms or conditions.
136 | Notwithstanding the above, nothing herein shall supersede or modify
137 | the terms of any separate license agreement you may have executed
138 | with Licensor regarding such Contributions.
139 |
140 | 6. Trademarks. This License does not grant permission to use the trade
141 | names, trademarks, service marks, or product names of the Licensor,
142 | except as required for reasonable and customary use in describing the
143 | origin of the Work and reproducing the content of the NOTICE file.
144 |
145 | 7. Disclaimer of Warranty. Unless required by applicable law or
146 | agreed to in writing, Licensor provides the Work (and each
147 | Contributor provides its Contributions) on an "AS IS" BASIS,
148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149 | implied, including, without limitation, any warranties or conditions
150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151 | PARTICULAR PURPOSE. You are solely responsible for determining the
152 | appropriateness of using or redistributing the Work and assume any
153 | risks associated with Your exercise of permissions under this License.
154 |
155 | 8. Limitation of Liability. In no event and under no legal theory,
156 | whether in tort (including negligence), contract, or otherwise,
157 | unless required by applicable law (such as deliberate and grossly
158 | negligent acts) or agreed to in writing, shall any Contributor be
159 | liable to You for damages, including any direct, indirect, special,
160 | incidental, or consequential damages of any character arising as a
161 | result of this License or out of the use or inability to use the
162 | Work (including but not limited to damages for loss of goodwill,
163 | work stoppage, computer failure or malfunction, or any and all
164 | other commercial damages or losses), even if such Contributor
165 | has been advised of the possibility of such damages.
166 |
167 | 9. Accepting Warranty or Additional Liability. While redistributing
168 | the Work or Derivative Works thereof, You may choose to offer,
169 | and charge a fee for, acceptance of support, warranty, indemnity,
170 | or other liability obligations and/or rights consistent with this
171 | License. However, in accepting such obligations, You may act only
172 | on Your own behalf and on Your sole responsibility, not on behalf
173 | of any other Contributor, and only if You agree to indemnify,
174 | defend, and hold each Contributor harmless for any liability
175 | incurred by, or claims asserted against, such Contributor by reason
176 | of your accepting any such warranty or additional liability.
177 |
178 | END OF TERMS AND CONDITIONS
179 |
180 | APPENDIX: How to apply the Apache License to your work.
181 |
182 | To apply the Apache License to your work, attach the following
183 | boilerplate notice, with the fields enclosed by brackets "[]"
184 | replaced with your own identifying information. (Don't include
185 | the brackets!) The text should be enclosed in the appropriate
186 | comment syntax for the file format. We also recommend that a
187 | file or class name and description of purpose be included on the
188 | same "printed page" as the copyright notice for easier
189 | identification within third-party archives.
190 |
191 | Copyright [yyyy] [name of copyright owner]
192 |
193 | Licensed under the Apache License, Version 2.0 (the "License");
194 | you may not use this file except in compliance with the License.
195 | You may obtain a copy of the License at
196 |
197 | http://www.apache.org/licenses/LICENSE-2.0
198 |
199 | Unless required by applicable law or agreed to in writing, software
200 | distributed under the License is distributed on an "AS IS" BASIS,
201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202 | See the License for the specific language governing permissions and
203 | limitations under the License.
204 |
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | init: purge
2 | python -m venv env
3 | env/bin/pip install --upgrade pip wheel setuptools
4 | env/bin/pip install -r requirements.txt
5 |
6 | purge:
7 | -@rm -rf env
8 |
9 | lab:
10 | env/bin/jupyter lab
11 |
12 | ipython:
13 | env/bin/ipython
14 |
--------------------------------------------------------------------------------
/notebooks/stats_tables/avg_area_table.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Average H3 cell areas\n",
8 | "\n",
9 | "H3 covers the sphere with both hexagon and pentagon cells.\n",
10 | "Most of the time you'll only encounter hexagon cells because there are only\n",
11 | "12 pentagons at each resolution, and they're all positioned to be in\n",
12 | "the ocean.\n",
13 | "\n",
14 | "Within a resolution, cells vary in size depending on where they are on the globe.\n",
15 | "We'll use this notebook to compute some statistics for the sizes of the hexagon\n",
16 | "and pentagon cells."
17 | ]
18 | },
19 | {
20 | "cell_type": "code",
21 | "execution_count": 1,
22 | "metadata": {},
23 | "outputs": [],
24 | "source": [
25 | "import h3\n",
26 | "from tabulate import tabulate\n",
27 | "\n",
28 | "def num_hexagons(res):\n",
29 | " 'Number of *hexagons* (excluding pentagons) at a resolution'\n",
30 | " return h3.num_hexagons(res) - 12 # function name to be fixed in 4.0 release\n",
31 | "\n",
32 | "def earth_area(unit='km^2'):\n",
33 | " cells = h3.get_res0_indexes()\n",
34 | " \n",
35 | " return sum(h3.cell_area(c, unit) for c in cells)\n",
36 | "\n",
37 | "def pentagon_area_total(res, unit='km^2'):\n",
38 | " 'Area covered by all the pentagons at a resolution'\n",
39 | " pentagons = h3.get_pentagon_indexes(res)\n",
40 | "\n",
41 | " return sum(h3.cell_area(c, unit) for c in pentagons)\n",
42 | "\n",
43 | "def pentagon_area(res, unit='km^2'):\n",
44 | " 'All pentagons at a resolution have the same area'\n",
45 | " return pentagon_area_total(res, unit)/12\n",
46 | "\n",
47 | "def hexagon_area_avg(res, unit='km^2'):\n",
48 | " A = earth_area(unit) - pentagon_area_total(res, unit)\n",
49 | " A = A/num_hexagons(res)\n",
50 | " \n",
51 | " return A\n",
52 | "\n",
53 | "def compute_rows(unit='km^2'):\n",
54 | " \"\"\"\n",
55 | " For each resolution yield:\n",
56 | " - resolution\n",
57 | " - average *hex* area\n",
58 | " - *uniform* pentagon area\n",
59 | " - ratio of pentagon/hex areas\n",
60 | " \"\"\"\n",
61 | " for res in range(16):\n",
62 | " h = hexagon_area_avg(res, unit)\n",
63 | " p = pentagon_area(res, unit)\n",
64 | "\n",
65 | " yield res, h, p, p/h"
66 | ]
67 | },
68 | {
69 | "cell_type": "code",
70 | "execution_count": 2,
71 | "metadata": {},
72 | "outputs": [
73 | {
74 | "data": {
75 | "text/plain": [
76 | "[(0, ' 4,357,449.416078383', ' 2,562,182.162955496', '0.5880'),\n",
77 | " (1, ' 609,788.441794133', ' 328,434.586246469', '0.5386'),\n",
78 | " (2, ' 86,801.780398997', ' 44,930.898497879', '0.5176'),\n",
79 | " (3, ' 12,393.434655088', ' 6,315.472267516', '0.5096'),\n",
80 | " (4, ' 1,770.347654491', ' 896.582383141', '0.5064'),\n",
81 | " (5, ' 252.903858182', ' 127.785583023', '0.5053'),\n",
82 | " (6, ' 36.129062164', ' 18.238749548', '0.5048'),\n",
83 | " (7, ' 5.161293360', ' 2.604669397', '0.5047'),\n",
84 | " (8, ' 0.737327598', ' 0.372048038', '0.5046'),\n",
85 | " (9, ' 0.105332513', ' 0.053147195', '0.5046'),\n",
86 | " (10, ' 0.015047502', ' 0.007592318', '0.5046'),\n",
87 | " (11, ' 0.002149643', ' 0.001084609', '0.5046'),\n",
88 | " (12, ' 0.000307092', ' 0.000154944', '0.5046'),\n",
89 | " (13, ' 0.000043870', ' 0.000022135', '0.5046'),\n",
90 | " (14, ' 0.000006267', ' 0.000003162', '0.5046'),\n",
91 | " (15, ' 0.000000895', ' 0.000000452', '0.5046')]"
92 | ]
93 | },
94 | "execution_count": 2,
95 | "metadata": {},
96 | "output_type": "execute_result"
97 | }
98 | ],
99 | "source": [
100 | "float_fmt = '{:20,.9f}'\n",
101 | "ratio_fmt = '{:.4f}'\n",
102 | "\n",
103 | "def fmt_float(x):\n",
104 | " s = float_fmt\n",
105 | " return s.format(x)\n",
106 | "\n",
107 | "def fmt_ratio(x):\n",
108 | " s = ratio_fmt\n",
109 | " return s.format(x)\n",
110 | "\n",
111 | "fmt_stats = [\n",
112 | " (a, fmt_float(b), fmt_float(c), fmt_ratio(d))\n",
113 | " for a,b,c,d in compute_rows('km^2')\n",
114 | "]\n",
115 | "\n",
116 | "fmt_stats"
117 | ]
118 | },
119 | {
120 | "cell_type": "code",
121 | "execution_count": 3,
122 | "metadata": {},
123 | "outputs": [
124 | {
125 | "name": "stdout",
126 | "output_type": "stream",
127 | "text": [
128 | "| Res | Average Hexagon Area (km2) | Pentagon Area* (km2) | Ratio (P/H) |\n",
129 | "|------:|---------------------------------------------------:|----------------------------------:|--------------:|\n",
130 | "| 0 | 4,357,449.416078383 | 2,562,182.162955496 | 0.5880 |\n",
131 | "| 1 | 609,788.441794133 | 328,434.586246469 | 0.5386 |\n",
132 | "| 2 | 86,801.780398997 | 44,930.898497879 | 0.5176 |\n",
133 | "| 3 | 12,393.434655088 | 6,315.472267516 | 0.5096 |\n",
134 | "| 4 | 1,770.347654491 | 896.582383141 | 0.5064 |\n",
135 | "| 5 | 252.903858182 | 127.785583023 | 0.5053 |\n",
136 | "| 6 | 36.129062164 | 18.238749548 | 0.5048 |\n",
137 | "| 7 | 5.161293360 | 2.604669397 | 0.5047 |\n",
138 | "| 8 | 0.737327598 | 0.372048038 | 0.5046 |\n",
139 | "| 9 | 0.105332513 | 0.053147195 | 0.5046 |\n",
140 | "| 10 | 0.015047502 | 0.007592318 | 0.5046 |\n",
141 | "| 11 | 0.002149643 | 0.001084609 | 0.5046 |\n",
142 | "| 12 | 0.000307092 | 0.000154944 | 0.5046 |\n",
143 | "| 13 | 0.000043870 | 0.000022135 | 0.5046 |\n",
144 | "| 14 | 0.000006267 | 0.000003162 | 0.5046 |\n",
145 | "| 15 | 0.000000895 | 0.000000452 | 0.5046 |\n",
146 | "\n",
147 | "*: Within a given resolution, all pentagons have the same area.\n"
148 | ]
149 | }
150 | ],
151 | "source": [
152 | "headers = [\n",
153 | " 'Res',\n",
154 | " 'Average Hexagon Area (km2)',\n",
155 | " 'Pentagon Area* (km2)',\n",
156 | " 'Ratio (P/H)'\n",
157 | "]\n",
158 | "out = tabulate(fmt_stats, headers=headers, tablefmt='pipe', stralign='right', disable_numparse=True)\n",
159 | "\n",
160 | "out += '\\n\\n*: Within a given resolution, all pentagons have the same area.'\n",
161 | "print(out)"
162 | ]
163 | },
164 | {
165 | "cell_type": "code",
166 | "execution_count": 4,
167 | "metadata": {},
168 | "outputs": [
169 | {
170 | "data": {
171 | "text/markdown": [
172 | "| Res | Average Hexagon Area (km2) | Pentagon Area* (km2) | Ratio (P/H) |\n",
173 | "|------:|---------------------------------------------------:|----------------------------------:|--------------:|\n",
174 | "| 0 | 4,357,449.416078383 | 2,562,182.162955496 | 0.5880 |\n",
175 | "| 1 | 609,788.441794133 | 328,434.586246469 | 0.5386 |\n",
176 | "| 2 | 86,801.780398997 | 44,930.898497879 | 0.5176 |\n",
177 | "| 3 | 12,393.434655088 | 6,315.472267516 | 0.5096 |\n",
178 | "| 4 | 1,770.347654491 | 896.582383141 | 0.5064 |\n",
179 | "| 5 | 252.903858182 | 127.785583023 | 0.5053 |\n",
180 | "| 6 | 36.129062164 | 18.238749548 | 0.5048 |\n",
181 | "| 7 | 5.161293360 | 2.604669397 | 0.5047 |\n",
182 | "| 8 | 0.737327598 | 0.372048038 | 0.5046 |\n",
183 | "| 9 | 0.105332513 | 0.053147195 | 0.5046 |\n",
184 | "| 10 | 0.015047502 | 0.007592318 | 0.5046 |\n",
185 | "| 11 | 0.002149643 | 0.001084609 | 0.5046 |\n",
186 | "| 12 | 0.000307092 | 0.000154944 | 0.5046 |\n",
187 | "| 13 | 0.000043870 | 0.000022135 | 0.5046 |\n",
188 | "| 14 | 0.000006267 | 0.000003162 | 0.5046 |\n",
189 | "| 15 | 0.000000895 | 0.000000452 | 0.5046 |\n",
190 | "\n",
191 | "*: Within a given resolution, all pentagons have the same area."
192 | ],
193 | "text/plain": [
194 | ""
195 | ]
196 | },
197 | "execution_count": 4,
198 | "metadata": {},
199 | "output_type": "execute_result"
200 | }
201 | ],
202 | "source": [
203 | "from IPython.display import Markdown\n",
204 | "Markdown(out)"
205 | ]
206 | },
207 | {
208 | "cell_type": "markdown",
209 | "metadata": {},
210 | "source": [
211 | "# Now in m^2 units"
212 | ]
213 | },
214 | {
215 | "cell_type": "code",
216 | "execution_count": 5,
217 | "metadata": {},
218 | "outputs": [
219 | {
220 | "data": {
221 | "text/plain": [
222 | "[(0, '4,357,449,416,078.392', '2,562,182,162,955.496'),\n",
223 | " (1, ' 609,788,441,794.134', ' 328,434,586,246.469'),\n",
224 | " (2, ' 86,801,780,398.997', ' 44,930,898,497.879'),\n",
225 | " (3, ' 12,393,434,655.088', ' 6,315,472,267.516'),\n",
226 | " (4, ' 1,770,347,654.491', ' 896,582,383.141'),\n",
227 | " (5, ' 252,903,858.182', ' 127,785,583.023'),\n",
228 | " (6, ' 36,129,062.164', ' 18,238,749.548'),\n",
229 | " (7, ' 5,161,293.360', ' 2,604,669.397'),\n",
230 | " (8, ' 737,327.598', ' 372,048.038'),\n",
231 | " (9, ' 105,332.513', ' 53,147.195'),\n",
232 | " (10, ' 15,047.502', ' 7,592.318'),\n",
233 | " (11, ' 2,149.643', ' 1,084.609'),\n",
234 | " (12, ' 307.092', ' 154.944'),\n",
235 | " (13, ' 43.870', ' 22.135'),\n",
236 | " (14, ' 6.267', ' 3.162'),\n",
237 | " (15, ' 0.895', ' 0.452')]"
238 | ]
239 | },
240 | "execution_count": 5,
241 | "metadata": {},
242 | "output_type": "execute_result"
243 | }
244 | ],
245 | "source": [
246 | "float_fmt = '{:20,.3f}'\n",
247 | "\n",
248 | "def fmt_float(x):\n",
249 | " s = float_fmt\n",
250 | " return s.format(x)\n",
251 | "\n",
252 | "fmt_stats = [\n",
253 | " (a, fmt_float(b), fmt_float(c))\n",
254 | " for a,b,c,_ in compute_rows('m^2')\n",
255 | "]\n",
256 | "\n",
257 | "fmt_stats"
258 | ]
259 | },
260 | {
261 | "cell_type": "code",
262 | "execution_count": 6,
263 | "metadata": {},
264 | "outputs": [
265 | {
266 | "name": "stdout",
267 | "output_type": "stream",
268 | "text": [
269 | "| Res | Average Hexagon Area (m2) | Pentagon Area* (m2) |\n",
270 | "|------:|--------------------------------------------------:|---------------------------------:|\n",
271 | "| 0 | 4,357,449,416,078.392 | 2,562,182,162,955.496 |\n",
272 | "| 1 | 609,788,441,794.134 | 328,434,586,246.469 |\n",
273 | "| 2 | 86,801,780,398.997 | 44,930,898,497.879 |\n",
274 | "| 3 | 12,393,434,655.088 | 6,315,472,267.516 |\n",
275 | "| 4 | 1,770,347,654.491 | 896,582,383.141 |\n",
276 | "| 5 | 252,903,858.182 | 127,785,583.023 |\n",
277 | "| 6 | 36,129,062.164 | 18,238,749.548 |\n",
278 | "| 7 | 5,161,293.360 | 2,604,669.397 |\n",
279 | "| 8 | 737,327.598 | 372,048.038 |\n",
280 | "| 9 | 105,332.513 | 53,147.195 |\n",
281 | "| 10 | 15,047.502 | 7,592.318 |\n",
282 | "| 11 | 2,149.643 | 1,084.609 |\n",
283 | "| 12 | 307.092 | 154.944 |\n",
284 | "| 13 | 43.870 | 22.135 |\n",
285 | "| 14 | 6.267 | 3.162 |\n",
286 | "| 15 | 0.895 | 0.452 |\n",
287 | "\n",
288 | "*: Within a given resolution, all pentagons have the same area.\n"
289 | ]
290 | }
291 | ],
292 | "source": [
293 | "headers = [\n",
294 | " 'Res',\n",
295 | " 'Average Hexagon Area (m2)',\n",
296 | " 'Pentagon Area* (m2)'\n",
297 | "]\n",
298 | "out = tabulate(fmt_stats, headers=headers, tablefmt='pipe', stralign='right', disable_numparse=True)\n",
299 | "\n",
300 | "out += '\\n\\n*: Within a given resolution, all pentagons have the same area.'\n",
301 | "print(out)"
302 | ]
303 | },
304 | {
305 | "cell_type": "code",
306 | "execution_count": 7,
307 | "metadata": {},
308 | "outputs": [
309 | {
310 | "data": {
311 | "text/markdown": [
312 | "| Res | Average Hexagon Area (m2) | Pentagon Area* (m2) |\n",
313 | "|------:|--------------------------------------------------:|---------------------------------:|\n",
314 | "| 0 | 4,357,449,416,078.392 | 2,562,182,162,955.496 |\n",
315 | "| 1 | 609,788,441,794.134 | 328,434,586,246.469 |\n",
316 | "| 2 | 86,801,780,398.997 | 44,930,898,497.879 |\n",
317 | "| 3 | 12,393,434,655.088 | 6,315,472,267.516 |\n",
318 | "| 4 | 1,770,347,654.491 | 896,582,383.141 |\n",
319 | "| 5 | 252,903,858.182 | 127,785,583.023 |\n",
320 | "| 6 | 36,129,062.164 | 18,238,749.548 |\n",
321 | "| 7 | 5,161,293.360 | 2,604,669.397 |\n",
322 | "| 8 | 737,327.598 | 372,048.038 |\n",
323 | "| 9 | 105,332.513 | 53,147.195 |\n",
324 | "| 10 | 15,047.502 | 7,592.318 |\n",
325 | "| 11 | 2,149.643 | 1,084.609 |\n",
326 | "| 12 | 307.092 | 154.944 |\n",
327 | "| 13 | 43.870 | 22.135 |\n",
328 | "| 14 | 6.267 | 3.162 |\n",
329 | "| 15 | 0.895 | 0.452 |\n",
330 | "\n",
331 | "*: Within a given resolution, all pentagons have the same area."
332 | ],
333 | "text/plain": [
334 | ""
335 | ]
336 | },
337 | "execution_count": 7,
338 | "metadata": {},
339 | "output_type": "execute_result"
340 | }
341 | ],
342 | "source": [
343 | "Markdown(out)"
344 | ]
345 | },
346 | {
347 | "cell_type": "markdown",
348 | "metadata": {
349 | "tags": []
350 | },
351 | "source": [
352 | "# Pre-computed numerical constants for the C library\n",
353 | "\n",
354 | "These numbers should match [the averages which are given in the C library functions `getHexagonAreaAvgKm2` and `getHexagonAreaAvgM2`](https://github.com/uber/h3/blob/32b6e07f41353e9def5a778fc5a4a7c8cdddd93a/src/h3lib/lib/latLng.c#L262-L278)."
355 | ]
356 | },
357 | {
358 | "cell_type": "code",
359 | "execution_count": 8,
360 | "metadata": {},
361 | "outputs": [
362 | {
363 | "name": "stdout",
364 | "output_type": "stream",
365 | "text": [
366 | "4.357449416078383e+06,\n",
367 | "6.097884417941332e+05,\n",
368 | "8.680178039899720e+04,\n",
369 | "1.239343465508816e+04,\n",
370 | "1.770347654491307e+03,\n",
371 | "2.529038581819449e+02,\n",
372 | "3.612906216441245e+01,\n",
373 | "5.161293359717191e+00,\n",
374 | "7.373275975944177e-01,\n",
375 | "1.053325134272067e-01,\n",
376 | "1.504750190766435e-02,\n",
377 | "2.149643129451879e-03,\n",
378 | "3.070918756316060e-04,\n",
379 | "4.387026794728296e-05,\n",
380 | "6.267181135324313e-06,\n",
381 | "8.953115907605790e-07,\n"
382 | ]
383 | }
384 | ],
385 | "source": [
386 | "for i in range(16):\n",
387 | " s = '{:.15e},'.format(hexagon_area_avg(i, 'km^2'))\n",
388 | " print(s)"
389 | ]
390 | },
391 | {
392 | "cell_type": "code",
393 | "execution_count": 9,
394 | "metadata": {},
395 | "outputs": [
396 | {
397 | "name": "stdout",
398 | "output_type": "stream",
399 | "text": [
400 | "4.357449416078392e+12,\n",
401 | "6.097884417941342e+11,\n",
402 | "8.680178039899734e+10,\n",
403 | "1.239343465508818e+10,\n",
404 | "1.770347654491310e+09,\n",
405 | "2.529038581819453e+08,\n",
406 | "3.612906216441251e+07,\n",
407 | "5.161293359717200e+06,\n",
408 | "7.373275975944190e+05,\n",
409 | "1.053325134272069e+05,\n",
410 | "1.504750190766437e+04,\n",
411 | "2.149643129451882e+03,\n",
412 | "3.070918756316065e+02,\n",
413 | "4.387026794728303e+01,\n",
414 | "6.267181135324324e+00,\n",
415 | "8.953115907605805e-01,\n"
416 | ]
417 | }
418 | ],
419 | "source": [
420 | "for i in range(16):\n",
421 | " s = '{:.15e},'.format(hexagon_area_avg(i, 'm^2'))\n",
422 | " print(s)"
423 | ]
424 | },
425 | {
426 | "cell_type": "code",
427 | "execution_count": null,
428 | "metadata": {},
429 | "outputs": [],
430 | "source": []
431 | }
432 | ],
433 | "metadata": {
434 | "kernelspec": {
435 | "display_name": "Python 3 (ipykernel)",
436 | "language": "python",
437 | "name": "python3"
438 | },
439 | "language_info": {
440 | "codemirror_mode": {
441 | "name": "ipython",
442 | "version": 3
443 | },
444 | "file_extension": ".py",
445 | "mimetype": "text/x-python",
446 | "name": "python",
447 | "nbconvert_exporter": "python",
448 | "pygments_lexer": "ipython3",
449 | "version": "3.7.12"
450 | }
451 | },
452 | "nbformat": 4,
453 | "nbformat_minor": 4
454 | }
455 |
--------------------------------------------------------------------------------
/notebooks/stats_tables/cell_counts.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Cell counts at different resolutions\n",
8 | "\n",
9 | "H3 covers the sphere with both hexagon and pentagon cells.\n",
10 | "Most of the time you'll only encounter hexagon cells because there are only\n",
11 | "12 pentagons at each resolution, and they're all positioned to be in\n",
12 | "the ocean.\n",
13 | "\n",
14 | "Here are the counts of pentagons, hexagons, and all cells at each resolution."
15 | ]
16 | },
17 | {
18 | "cell_type": "code",
19 | "execution_count": 1,
20 | "metadata": {},
21 | "outputs": [
22 | {
23 | "data": {
24 | "text/plain": [
25 | "[('0', '122', '110', '12'),\n",
26 | " ('1', '842', '830', '12'),\n",
27 | " ('2', '5,882', '5,870', '12'),\n",
28 | " ('3', '41,162', '41,150', '12'),\n",
29 | " ('4', '288,122', '288,110', '12'),\n",
30 | " ('5', '2,016,842', '2,016,830', '12'),\n",
31 | " ('6', '14,117,882', '14,117,870', '12'),\n",
32 | " ('7', '98,825,162', '98,825,150', '12'),\n",
33 | " ('8', '691,776,122', '691,776,110', '12'),\n",
34 | " ('9', '4,842,432,842', '4,842,432,830', '12'),\n",
35 | " ('10', '33,897,029,882', '33,897,029,870', '12'),\n",
36 | " ('11', '237,279,209,162', '237,279,209,150', '12'),\n",
37 | " ('12', '1,660,954,464,122', '1,660,954,464,110', '12'),\n",
38 | " ('13', '11,626,681,248,842', '11,626,681,248,830', '12'),\n",
39 | " ('14', '81,386,768,741,882', '81,386,768,741,870', '12'),\n",
40 | " ('15', '569,707,381,193,162', '569,707,381,193,150', '12')]"
41 | ]
42 | },
43 | "execution_count": 1,
44 | "metadata": {},
45 | "output_type": "execute_result"
46 | }
47 | ],
48 | "source": [
49 | "import h3\n",
50 | "from tabulate import tabulate\n",
51 | "\n",
52 | "def num_pentagons(res):\n",
53 | " return 12\n",
54 | "\n",
55 | "def num_hexagons(res):\n",
56 | " 'Number of *hexagons* (excluding pentagons) at a resolution'\n",
57 | " return h3.num_hexagons(res) - 12\n",
58 | "\n",
59 | "def num_cells(res):\n",
60 | " 'Number of *hexagons* (excluding pentagons) at a resolution'\n",
61 | " return h3.num_hexagons(res) # function name to be fixed in 4.0 release\n",
62 | "\n",
63 | "def fmt(num):\n",
64 | " s = '{:,.0f}'\n",
65 | " return s.format(num) \n",
66 | "\n",
67 | "\n",
68 | "counts = [\n",
69 | " (res, num_cells(res), num_hexagons(res), num_pentagons(res))\n",
70 | " for res in range(16)\n",
71 | "]\n",
72 | "\n",
73 | "counts = [\n",
74 | " tuple(map(fmt, row))\n",
75 | " for row in counts\n",
76 | "]\n",
77 | "counts"
78 | ]
79 | },
80 | {
81 | "cell_type": "code",
82 | "execution_count": 2,
83 | "metadata": {},
84 | "outputs": [
85 | {
86 | "name": "stdout",
87 | "output_type": "stream",
88 | "text": [
89 | "| Res | Total number of cells | Number of hexagons | Number of pentagons |\n",
90 | "|------:|------------------------:|---------------------:|----------------------:|\n",
91 | "| 0 | 122 | 110 | 12 |\n",
92 | "| 1 | 842 | 830 | 12 |\n",
93 | "| 2 | 5,882 | 5,870 | 12 |\n",
94 | "| 3 | 41,162 | 41,150 | 12 |\n",
95 | "| 4 | 288,122 | 288,110 | 12 |\n",
96 | "| 5 | 2,016,842 | 2,016,830 | 12 |\n",
97 | "| 6 | 14,117,882 | 14,117,870 | 12 |\n",
98 | "| 7 | 98,825,162 | 98,825,150 | 12 |\n",
99 | "| 8 | 691,776,122 | 691,776,110 | 12 |\n",
100 | "| 9 | 4,842,432,842 | 4,842,432,830 | 12 |\n",
101 | "| 10 | 33,897,029,882 | 33,897,029,870 | 12 |\n",
102 | "| 11 | 237,279,209,162 | 237,279,209,150 | 12 |\n",
103 | "| 12 | 1,660,954,464,122 | 1,660,954,464,110 | 12 |\n",
104 | "| 13 | 11,626,681,248,842 | 11,626,681,248,830 | 12 |\n",
105 | "| 14 | 81,386,768,741,882 | 81,386,768,741,870 | 12 |\n",
106 | "| 15 | 569,707,381,193,162 | 569,707,381,193,150 | 12 |\n"
107 | ]
108 | }
109 | ],
110 | "source": [
111 | "headers = [\n",
112 | " 'Res',\n",
113 | " 'Total number of cells',\n",
114 | " 'Number of hexagons',\n",
115 | " 'Number of pentagons',\n",
116 | "]\n",
117 | "out = tabulate(counts, headers=headers, tablefmt='pipe', stralign='right')\n",
118 | "\n",
119 | "print(out)"
120 | ]
121 | },
122 | {
123 | "cell_type": "code",
124 | "execution_count": 3,
125 | "metadata": {},
126 | "outputs": [
127 | {
128 | "data": {
129 | "text/markdown": [
130 | "| Res | Total number of cells | Number of hexagons | Number of pentagons |\n",
131 | "|------:|------------------------:|---------------------:|----------------------:|\n",
132 | "| 0 | 122 | 110 | 12 |\n",
133 | "| 1 | 842 | 830 | 12 |\n",
134 | "| 2 | 5,882 | 5,870 | 12 |\n",
135 | "| 3 | 41,162 | 41,150 | 12 |\n",
136 | "| 4 | 288,122 | 288,110 | 12 |\n",
137 | "| 5 | 2,016,842 | 2,016,830 | 12 |\n",
138 | "| 6 | 14,117,882 | 14,117,870 | 12 |\n",
139 | "| 7 | 98,825,162 | 98,825,150 | 12 |\n",
140 | "| 8 | 691,776,122 | 691,776,110 | 12 |\n",
141 | "| 9 | 4,842,432,842 | 4,842,432,830 | 12 |\n",
142 | "| 10 | 33,897,029,882 | 33,897,029,870 | 12 |\n",
143 | "| 11 | 237,279,209,162 | 237,279,209,150 | 12 |\n",
144 | "| 12 | 1,660,954,464,122 | 1,660,954,464,110 | 12 |\n",
145 | "| 13 | 11,626,681,248,842 | 11,626,681,248,830 | 12 |\n",
146 | "| 14 | 81,386,768,741,882 | 81,386,768,741,870 | 12 |\n",
147 | "| 15 | 569,707,381,193,162 | 569,707,381,193,150 | 12 |"
148 | ],
149 | "text/plain": [
150 | ""
151 | ]
152 | },
153 | "execution_count": 3,
154 | "metadata": {},
155 | "output_type": "execute_result"
156 | }
157 | ],
158 | "source": [
159 | "from IPython.display import Markdown\n",
160 | "Markdown(out)"
161 | ]
162 | },
163 | {
164 | "cell_type": "code",
165 | "execution_count": null,
166 | "metadata": {},
167 | "outputs": [],
168 | "source": []
169 | }
170 | ],
171 | "metadata": {
172 | "kernelspec": {
173 | "display_name": "Python 3 (ipykernel)",
174 | "language": "python",
175 | "name": "python3"
176 | },
177 | "language_info": {
178 | "codemirror_mode": {
179 | "name": "ipython",
180 | "version": 3
181 | },
182 | "file_extension": ".py",
183 | "mimetype": "text/x-python",
184 | "name": "python",
185 | "nbconvert_exporter": "python",
186 | "pygments_lexer": "ipython3",
187 | "version": "3.7.12"
188 | }
189 | },
190 | "nbformat": 4,
191 | "nbformat_minor": 4
192 | }
193 |
--------------------------------------------------------------------------------
/notebooks/stats_tables/extreme_hex_area.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Hexagon cell area variation\n",
8 | "\n",
9 | "The hexagons vary in size according to their location on the sphere.\n",
10 | "This distortion comes from the gnomonic projection of cells onto the sphere.\n",
11 | "\n",
12 | "This notebook computes the minimum and maximum **hexagon** cell areas on the sphere at each resolution.\n",
13 | "We're excluding pentagons since they're a special case."
14 | ]
15 | },
16 | {
17 | "cell_type": "markdown",
18 | "metadata": {},
19 | "source": [
20 | "## Brute force area calculation\n",
21 | "\n",
22 | "We can find the minimum and maximum area **hexagons** (excluding pentagons) using brute force search over all hexagons\n",
23 | "at each resolution. However, this search becomes impractically slow for finer resolutions."
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": 1,
29 | "metadata": {},
30 | "outputs": [],
31 | "source": [
32 | "import h3\n",
33 | "from tabulate import tabulate\n",
34 | "\n",
35 | "def brute_extreme_area_hex(res, min_or_max=min):\n",
36 | " \"\"\"\n",
37 | " Note: Due to symmetry, the extremal hex is not unique.\n",
38 | " \"\"\"\n",
39 | " cells = h3.get_res0_indexes()\n",
40 | " cells = h3.uncompact(cells, res)\n",
41 | " cells = (c for c in cells if not h3.h3_is_pentagon(c))\n",
42 | " \n",
43 | " h = min_or_max(cells, key=h3.cell_area)\n",
44 | " \n",
45 | " return h"
46 | ]
47 | },
48 | {
49 | "cell_type": "code",
50 | "execution_count": 2,
51 | "metadata": {},
52 | "outputs": [
53 | {
54 | "data": {
55 | "text/plain": [
56 | "1.970457453618385"
57 | ]
58 | },
59 | "execution_count": 2,
60 | "metadata": {},
61 | "output_type": "execute_result"
62 | }
63 | ],
64 | "source": [
65 | "res = 4\n",
66 | "\n",
67 | "h_max = brute_extreme_area_hex(res, max)\n",
68 | "h_min = brute_extreme_area_hex(res, min)\n",
69 | "\n",
70 | "h3.cell_area(h_max)/h3.cell_area(h_min)"
71 | ]
72 | },
73 | {
74 | "cell_type": "markdown",
75 | "metadata": {},
76 | "source": [
77 | "## Using recursive search\n",
78 | "\n",
79 | "To find the hexagons at each resolution with the min/max area, we can use a recursive procedure where we find the extremal hex at the coarser resolution, and then look around a neighborhood of that hex (really, its children) at a finer resolution. Due to the continuous nature of the area distortions, this method should give us exact results."
80 | ]
81 | },
82 | {
83 | "cell_type": "code",
84 | "execution_count": 3,
85 | "metadata": {},
86 | "outputs": [],
87 | "source": [
88 | "def extreme_small_neighborhood(h, min_or_max=min, k=1):\n",
89 | " \"\"\" Take the k-ring of cells around (and including) h,\n",
90 | " take all their children, and find the one who's\n",
91 | " area is min/max of the group.\n",
92 | " \"\"\"\n",
93 | " cells = h3.k_ring(h, k)\n",
94 | " \n",
95 | " res = h3.h3_get_resolution(h) + 1\n",
96 | " cells = h3.uncompact(cells, res)\n",
97 | " \n",
98 | " cells = (c for c in cells if not h3.h3_is_pentagon(c))\n",
99 | "\n",
100 | " h = min_or_max(cells, key=h3.cell_area)\n",
101 | " \n",
102 | " return h\n",
103 | "\n",
104 | "def extreme_area_hex(res, min_or_max=min, k=1):\n",
105 | " \"\"\" Get the extreme min/max area hex at given resolution\n",
106 | " by recursively searching small neighborhoods of whatever\n",
107 | " the coarser resolution's extremal hex was.\n",
108 | " \"\"\"\n",
109 | " if res == 0:\n",
110 | " cells = h3.get_res0_indexes()\n",
111 | " cells = (c for c in cells if not h3.h3_is_pentagon(c))\n",
112 | " return min_or_max(cells, key=h3.cell_area)\n",
113 | " else:\n",
114 | " h = extreme_area_hex(res-1, min_or_max)\n",
115 | " h = extreme_small_neighborhood(h, min_or_max, k=k)\n",
116 | " return h"
117 | ]
118 | },
119 | {
120 | "cell_type": "markdown",
121 | "metadata": {},
122 | "source": [
123 | "## Compare brute force to the recursive search"
124 | ]
125 | },
126 | {
127 | "cell_type": "code",
128 | "execution_count": 4,
129 | "metadata": {
130 | "tags": []
131 | },
132 | "outputs": [
133 | {
134 | "data": {
135 | "text/plain": [
136 | "1.970457453618161"
137 | ]
138 | },
139 | "execution_count": 4,
140 | "metadata": {},
141 | "output_type": "execute_result"
142 | }
143 | ],
144 | "source": [
145 | "res = 4\n",
146 | "\n",
147 | "h_max = extreme_area_hex(res, max)\n",
148 | "h_min = extreme_area_hex(res, min)\n",
149 | "\n",
150 | "h3.cell_area(h_max)/h3.cell_area(h_min)"
151 | ]
152 | },
153 | {
154 | "cell_type": "code",
155 | "execution_count": 5,
156 | "metadata": {},
157 | "outputs": [
158 | {
159 | "data": {
160 | "text/plain": [
161 | "1.970457453618385"
162 | ]
163 | },
164 | "execution_count": 5,
165 | "metadata": {},
166 | "output_type": "execute_result"
167 | }
168 | ],
169 | "source": [
170 | "res = 4\n",
171 | "\n",
172 | "h_max = brute_extreme_area_hex(res, max)\n",
173 | "h_min = brute_extreme_area_hex(res, min)\n",
174 | "\n",
175 | "h3.cell_area(h_max)/h3.cell_area(h_min)"
176 | ]
177 | },
178 | {
179 | "cell_type": "markdown",
180 | "metadata": {},
181 | "source": [
182 | "## Format results in a nice table"
183 | ]
184 | },
185 | {
186 | "cell_type": "code",
187 | "execution_count": 6,
188 | "metadata": {},
189 | "outputs": [],
190 | "source": [
191 | "def stats():\n",
192 | " \"\"\"\n",
193 | " For each resolution yield:\n",
194 | " - resolution\n",
195 | " - maximum *hex* area\n",
196 | " - minimum *hex* area\n",
197 | " - ratio of max/min\n",
198 | " \"\"\"\n",
199 | " for res in range(16):\n",
200 | " h_max = extreme_area_hex(res, max)\n",
201 | " h_min = extreme_area_hex(res, min)\n",
202 | " \n",
203 | " a_max = h3.cell_area(h_max)\n",
204 | " a_min = h3.cell_area(h_min)\n",
205 | "\n",
206 | " yield res, a_min, a_max, a_max/a_min"
207 | ]
208 | },
209 | {
210 | "cell_type": "code",
211 | "execution_count": 7,
212 | "metadata": {},
213 | "outputs": [
214 | {
215 | "data": {
216 | "text/plain": [
217 | "[(0, 4106166.334463915, 4977807.027442012, 1.2122760312124314),\n",
218 | " (1, 447684.2018179402, 729486.8752753442, 1.62946754054101),\n",
219 | " (2, 56786.62288947397, 104599.80721892511, 1.8419797110053862),\n",
220 | " (3, 7725.505769639398, 14950.773301378937, 1.935248480446969),\n",
221 | " (4, 1084.005635362784, 2135.986983964688, 1.970457453618161),\n",
222 | " (5, 153.7662444479533, 305.1443087785733, 1.984468762140174),\n",
223 | " (6, 21.91002101263703, 43.59211168500078, 1.9895969821233024),\n",
224 | " (7, 3.1268360301030746, 6.227445905490173, 1.9916125583613953),\n",
225 | " (8, 0.44652617408295603, 0.8896351574995928, 1.9923471660461176),\n",
226 | " (9, 0.06378022693678723, 0.12709073735993393, 1.9926353897406783),\n",
227 | " (10, 0.009110980969550845, 0.018155819634610822, 1.9927403750801458),\n",
228 | " (11, 0.0013015418135499314, 0.002593688519461668, 1.992781555275147),\n",
229 | " (12, 0.00018593314532801717, 0.00037052693136442757, 1.9927965544322723),\n",
230 | " (13, 2.65617994932581e-05, 5.2932418770310184e-05, 1.9928024373403412),\n",
231 | " (14, 3.794538702988277e-06, 7.5617741108915425e-06, 1.9928045812094342),\n",
232 | " (15, 5.420767288789694e-07, 1.0802534449686401e-06, 1.992805422956702)]"
233 | ]
234 | },
235 | "execution_count": 7,
236 | "metadata": {},
237 | "output_type": "execute_result"
238 | }
239 | ],
240 | "source": [
241 | "list(stats())"
242 | ]
243 | },
244 | {
245 | "cell_type": "code",
246 | "execution_count": 8,
247 | "metadata": {},
248 | "outputs": [
249 | {
250 | "name": "stdout",
251 | "output_type": "stream",
252 | "text": [
253 | " 0 4,977,807.027442012 4,106,166.334463915 1.212276\n",
254 | " 1 729,486.875275344 447,684.201817940 1.629468\n",
255 | " 2 104,599.807218925 56,786.622889474 1.841980\n",
256 | " 3 14,950.773301379 7,725.505769639 1.935248\n",
257 | " 4 2,135.986983965 1,084.005635363 1.970457\n",
258 | " 5 305.144308779 153.766244448 1.984469\n",
259 | " 6 43.592111685 21.910021013 1.989597\n",
260 | " 7 6.227445905 3.126836030 1.991613\n",
261 | " 8 0.889635157 0.446526174 1.992347\n",
262 | " 9 0.127090737 0.063780227 1.992635\n",
263 | "10 0.018155820 0.009110981 1.992740\n",
264 | "11 0.002593689 0.001301542 1.992782\n",
265 | "12 0.000370527 0.000185933 1.992797\n",
266 | "13 0.000052932 0.000026562 1.992802\n",
267 | "14 0.000007562 0.000003795 1.992805\n",
268 | "15 0.000001080 0.000000542 1.992805\n"
269 | ]
270 | }
271 | ],
272 | "source": [
273 | "res_fmt = '{:2d}'\n",
274 | "float_fmt = '{:20,.9f}'\n",
275 | "ratio_fmt = '{:.6f}'\n",
276 | "\n",
277 | "fmt = f'{res_fmt} {float_fmt} {float_fmt} {ratio_fmt}'\n",
278 | "\n",
279 | "for res, a_min, a_max, ratio in stats():\n",
280 | " print(fmt.format(res, a_max, a_min, ratio))"
281 | ]
282 | },
283 | {
284 | "cell_type": "code",
285 | "execution_count": 9,
286 | "metadata": {},
287 | "outputs": [
288 | {
289 | "data": {
290 | "text/plain": [
291 | "[(0, ' 4,106,166.334463915', ' 4,977,807.027442012', '1.212276'),\n",
292 | " (1, ' 447,684.201817940', ' 729,486.875275344', '1.629468'),\n",
293 | " (2, ' 56,786.622889474', ' 104,599.807218925', '1.841980'),\n",
294 | " (3, ' 7,725.505769639', ' 14,950.773301379', '1.935248'),\n",
295 | " (4, ' 1,084.005635363', ' 2,135.986983965', '1.970457'),\n",
296 | " (5, ' 153.766244448', ' 305.144308779', '1.984469'),\n",
297 | " (6, ' 21.910021013', ' 43.592111685', '1.989597'),\n",
298 | " (7, ' 3.126836030', ' 6.227445905', '1.991613'),\n",
299 | " (8, ' 0.446526174', ' 0.889635157', '1.992347'),\n",
300 | " (9, ' 0.063780227', ' 0.127090737', '1.992635'),\n",
301 | " (10, ' 0.009110981', ' 0.018155820', '1.992740'),\n",
302 | " (11, ' 0.001301542', ' 0.002593689', '1.992782'),\n",
303 | " (12, ' 0.000185933', ' 0.000370527', '1.992797'),\n",
304 | " (13, ' 0.000026562', ' 0.000052932', '1.992802'),\n",
305 | " (14, ' 0.000003795', ' 0.000007562', '1.992805'),\n",
306 | " (15, ' 0.000000542', ' 0.000001080', '1.992805')]"
307 | ]
308 | },
309 | "execution_count": 9,
310 | "metadata": {},
311 | "output_type": "execute_result"
312 | }
313 | ],
314 | "source": [
315 | "def fmt_float(x):\n",
316 | " s = float_fmt\n",
317 | " return s.format(x)\n",
318 | "\n",
319 | "def fmt_ratio(x):\n",
320 | " s = ratio_fmt\n",
321 | " return s.format(x)\n",
322 | "\n",
323 | "fmt_stats = [\n",
324 | " (a, fmt_float(b), fmt_float(c), fmt_ratio(d))\n",
325 | " for a,b,c,d in stats()\n",
326 | "]\n",
327 | "\n",
328 | "fmt_stats"
329 | ]
330 | },
331 | {
332 | "cell_type": "code",
333 | "execution_count": 10,
334 | "metadata": {},
335 | "outputs": [
336 | {
337 | "name": "stdout",
338 | "output_type": "stream",
339 | "text": [
340 | "| Res | Min Hexagon Area (km^2) | Max Hexagon Area (km^2) | Ratio (max/min) |\n",
341 | "|------:|-------------------------------------:|-------------------------------------:|------------------:|\n",
342 | "| 0 | 4,106,166.334463915 | 4,977,807.027442012 | 1.212276 |\n",
343 | "| 1 | 447,684.201817940 | 729,486.875275344 | 1.629468 |\n",
344 | "| 2 | 56,786.622889474 | 104,599.807218925 | 1.841980 |\n",
345 | "| 3 | 7,725.505769639 | 14,950.773301379 | 1.935248 |\n",
346 | "| 4 | 1,084.005635363 | 2,135.986983965 | 1.970457 |\n",
347 | "| 5 | 153.766244448 | 305.144308779 | 1.984469 |\n",
348 | "| 6 | 21.910021013 | 43.592111685 | 1.989597 |\n",
349 | "| 7 | 3.126836030 | 6.227445905 | 1.991613 |\n",
350 | "| 8 | 0.446526174 | 0.889635157 | 1.992347 |\n",
351 | "| 9 | 0.063780227 | 0.127090737 | 1.992635 |\n",
352 | "| 10 | 0.009110981 | 0.018155820 | 1.992740 |\n",
353 | "| 11 | 0.001301542 | 0.002593689 | 1.992782 |\n",
354 | "| 12 | 0.000185933 | 0.000370527 | 1.992797 |\n",
355 | "| 13 | 0.000026562 | 0.000052932 | 1.992802 |\n",
356 | "| 14 | 0.000003795 | 0.000007562 | 1.992805 |\n",
357 | "| 15 | 0.000000542 | 0.000001080 | 1.992805 |\n"
358 | ]
359 | }
360 | ],
361 | "source": [
362 | "headers = [\n",
363 | " 'Res',\n",
364 | " 'Min Hexagon Area (km^2)',\n",
365 | " 'Max Hexagon Area (km^2)',\n",
366 | " 'Ratio (max/min)'\n",
367 | "]\n",
368 | "out = tabulate(fmt_stats, headers=headers, tablefmt='pipe', stralign='right', disable_numparse=True)\n",
369 | "\n",
370 | "print(out)"
371 | ]
372 | },
373 | {
374 | "cell_type": "code",
375 | "execution_count": 11,
376 | "metadata": {},
377 | "outputs": [
378 | {
379 | "data": {
380 | "text/markdown": [
381 | "| Res | Min Hexagon Area (km^2) | Max Hexagon Area (km^2) | Ratio (max/min) |\n",
382 | "|------:|-------------------------------------:|-------------------------------------:|------------------:|\n",
383 | "| 0 | 4,106,166.334463915 | 4,977,807.027442012 | 1.212276 |\n",
384 | "| 1 | 447,684.201817940 | 729,486.875275344 | 1.629468 |\n",
385 | "| 2 | 56,786.622889474 | 104,599.807218925 | 1.841980 |\n",
386 | "| 3 | 7,725.505769639 | 14,950.773301379 | 1.935248 |\n",
387 | "| 4 | 1,084.005635363 | 2,135.986983965 | 1.970457 |\n",
388 | "| 5 | 153.766244448 | 305.144308779 | 1.984469 |\n",
389 | "| 6 | 21.910021013 | 43.592111685 | 1.989597 |\n",
390 | "| 7 | 3.126836030 | 6.227445905 | 1.991613 |\n",
391 | "| 8 | 0.446526174 | 0.889635157 | 1.992347 |\n",
392 | "| 9 | 0.063780227 | 0.127090737 | 1.992635 |\n",
393 | "| 10 | 0.009110981 | 0.018155820 | 1.992740 |\n",
394 | "| 11 | 0.001301542 | 0.002593689 | 1.992782 |\n",
395 | "| 12 | 0.000185933 | 0.000370527 | 1.992797 |\n",
396 | "| 13 | 0.000026562 | 0.000052932 | 1.992802 |\n",
397 | "| 14 | 0.000003795 | 0.000007562 | 1.992805 |\n",
398 | "| 15 | 0.000000542 | 0.000001080 | 1.992805 |"
399 | ],
400 | "text/plain": [
401 | ""
402 | ]
403 | },
404 | "execution_count": 11,
405 | "metadata": {},
406 | "output_type": "execute_result"
407 | }
408 | ],
409 | "source": [
410 | "from IPython.display import Markdown\n",
411 | "Markdown(out)"
412 | ]
413 | },
414 | {
415 | "cell_type": "code",
416 | "execution_count": null,
417 | "metadata": {},
418 | "outputs": [],
419 | "source": []
420 | }
421 | ],
422 | "metadata": {
423 | "kernelspec": {
424 | "display_name": "Python 3 (ipykernel)",
425 | "language": "python",
426 | "name": "python3"
427 | },
428 | "language_info": {
429 | "codemirror_mode": {
430 | "name": "ipython",
431 | "version": 3
432 | },
433 | "file_extension": ".py",
434 | "mimetype": "text/x-python",
435 | "name": "python",
436 | "nbconvert_exporter": "python",
437 | "pygments_lexer": "ipython3",
438 | "version": "3.7.12"
439 | }
440 | },
441 | "nbformat": 4,
442 | "nbformat_minor": 4
443 | }
444 |
--------------------------------------------------------------------------------
/notebooks/time_h3_apis.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "95776ece-9628-40ad-a6cb-bf68690167ec",
6 | "metadata": {},
7 | "source": [
8 | "# API Speed Comparison\n",
9 | "\n",
10 | "We time a simple toy computational task across the various APIs.\n",
11 | "\n",
12 | "We also compare the option of doing most of the computation in one of the faster APIs (using the `int` representation of H3 indexes),\n",
13 | "and then converting the results to the more familiar format of Python `str` objects."
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": 1,
19 | "id": "cef5ba28-c8b0-42de-b55f-7a3d92436b32",
20 | "metadata": {},
21 | "outputs": [],
22 | "source": [
23 | "import h3\n",
24 | "import h3.api.numpy_int\n",
25 | "\n",
26 | "\n",
27 | "def compute(h3_lib, N=100):\n",
28 | " h = h3_lib.geo_to_h3(0, 0, 9)\n",
29 | " out = h3_lib.k_ring(h, N)\n",
30 | " out = h3_lib.compact(out)\n",
31 | " \n",
32 | " return out\n",
33 | "\n",
34 | "def compute_and_convert(h3_lib, N=100):\n",
35 | " out = compute(h3_lib, N)\n",
36 | " out = [h3.h3_to_string(h) for h in out]\n",
37 | " \n",
38 | " return out"
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "id": "9c933e85-5a9a-4d07-b4c4-20a88c97fa88",
44 | "metadata": {},
45 | "source": [
46 | "# Compute with each API\n",
47 | "\n",
48 | "**Benchmarking note**: `%%timeit sleep()` executes `sleep` before each **run** of the code to be timed in the remaining body of the cell; this helps to stabilize timing results (at least on my laptop)."
49 | ]
50 | },
51 | {
52 | "cell_type": "code",
53 | "execution_count": 2,
54 | "id": "e636e193-22ff-440f-a5b8-4c073c8f8a57",
55 | "metadata": {},
56 | "outputs": [],
57 | "source": [
58 | "def sleep():\n",
59 | " import time\n",
60 | " time.sleep(2)"
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": 3,
66 | "id": "e143ba5e-618b-47a9-913b-66f25877b240",
67 | "metadata": {},
68 | "outputs": [
69 | {
70 | "name": "stdout",
71 | "output_type": "stream",
72 | "text": [
73 | "59.8 ms ± 511 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
74 | ]
75 | }
76 | ],
77 | "source": [
78 | "%%timeit sleep()\n",
79 | "\n",
80 | "compute(h3.api.basic_str)"
81 | ]
82 | },
83 | {
84 | "cell_type": "code",
85 | "execution_count": 4,
86 | "id": "6d80bca9-c0f5-4aaa-8488-f7bd174c7ee3",
87 | "metadata": {},
88 | "outputs": [
89 | {
90 | "name": "stdout",
91 | "output_type": "stream",
92 | "text": [
93 | "35.4 ms ± 574 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
94 | ]
95 | }
96 | ],
97 | "source": [
98 | "%%timeit sleep()\n",
99 | "\n",
100 | "compute(h3.api.basic_int)"
101 | ]
102 | },
103 | {
104 | "cell_type": "code",
105 | "execution_count": 5,
106 | "id": "ac9eb860-eba1-41ec-8b33-3aa832758097",
107 | "metadata": {},
108 | "outputs": [
109 | {
110 | "name": "stdout",
111 | "output_type": "stream",
112 | "text": [
113 | "7.12 ms ± 52.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
114 | ]
115 | }
116 | ],
117 | "source": [
118 | "%%timeit sleep()\n",
119 | "\n",
120 | "compute(h3.api.memview_int)"
121 | ]
122 | },
123 | {
124 | "cell_type": "code",
125 | "execution_count": 6,
126 | "id": "f2713e84-c4fb-4a60-b913-c1fb7ea7fd4b",
127 | "metadata": {},
128 | "outputs": [
129 | {
130 | "name": "stdout",
131 | "output_type": "stream",
132 | "text": [
133 | "7.06 ms ± 49.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
134 | ]
135 | }
136 | ],
137 | "source": [
138 | "%%timeit sleep()\n",
139 | "\n",
140 | "compute(h3.api.numpy_int)"
141 | ]
142 | },
143 | {
144 | "cell_type": "markdown",
145 | "id": "97e8e885-401b-42aa-a28e-534f828bf003",
146 | "metadata": {},
147 | "source": [
148 | "# Compute with `int` APIs and convert to `str`"
149 | ]
150 | },
151 | {
152 | "cell_type": "code",
153 | "execution_count": 7,
154 | "id": "51c02c9f-0a03-4e84-943d-09969949b83d",
155 | "metadata": {},
156 | "outputs": [
157 | {
158 | "name": "stdout",
159 | "output_type": "stream",
160 | "text": [
161 | "35.6 ms ± 462 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
162 | ]
163 | }
164 | ],
165 | "source": [
166 | "%%timeit sleep()\n",
167 | "\n",
168 | "compute_and_convert(h3.api.basic_int)"
169 | ]
170 | },
171 | {
172 | "cell_type": "code",
173 | "execution_count": 8,
174 | "id": "b11e3d6d-58d3-418f-9798-2a80dc16b133",
175 | "metadata": {},
176 | "outputs": [
177 | {
178 | "name": "stdout",
179 | "output_type": "stream",
180 | "text": [
181 | "7.76 ms ± 88.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
182 | ]
183 | }
184 | ],
185 | "source": [
186 | "%%timeit sleep()\n",
187 | "\n",
188 | "compute_and_convert(h3.api.memview_int)"
189 | ]
190 | },
191 | {
192 | "cell_type": "code",
193 | "execution_count": 9,
194 | "id": "5cf6e8a8-583d-4309-8ed2-5b4c31c8765a",
195 | "metadata": {},
196 | "outputs": [
197 | {
198 | "name": "stdout",
199 | "output_type": "stream",
200 | "text": [
201 | "7.61 ms ± 40.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
202 | ]
203 | }
204 | ],
205 | "source": [
206 | "%%timeit sleep()\n",
207 | "\n",
208 | "compute_and_convert(h3.api.numpy_int)"
209 | ]
210 | },
211 | {
212 | "cell_type": "markdown",
213 | "id": "381e7c85-a70a-4563-8504-29e39639821f",
214 | "metadata": {},
215 | "source": [
216 | "# Speedup\n",
217 | "\n",
218 | "We typically see about a 6--8x speedup between:\n",
219 | "\n",
220 | "- computing with the `h3.api.basic_str` interface\n",
221 | "- computing with the `h3.api.numpy_int` interface, and then converting the results back to `str`"
222 | ]
223 | },
224 | {
225 | "cell_type": "code",
226 | "execution_count": 11,
227 | "id": "188da0e8-efd8-43f5-9f45-b821e085b8a7",
228 | "metadata": {},
229 | "outputs": [
230 | {
231 | "data": {
232 | "text/plain": [
233 | "7.8580814717477"
234 | ]
235 | },
236 | "execution_count": 11,
237 | "metadata": {},
238 | "output_type": "execute_result"
239 | }
240 | ],
241 | "source": [
242 | "### REMEMBER! Update the numbers from the `%%timeit`s above!\n",
243 | "\n",
244 | "c_str = 59.8\n",
245 | "cnc_numpy = 7.61\n",
246 | "\n",
247 | "c_str/cnc_numpy"
248 | ]
249 | },
250 | {
251 | "cell_type": "code",
252 | "execution_count": null,
253 | "id": "326bea04-304f-4b03-81f2-2fc77420e429",
254 | "metadata": {},
255 | "outputs": [],
256 | "source": []
257 | }
258 | ],
259 | "metadata": {
260 | "kernelspec": {
261 | "display_name": "Python 3 (ipykernel)",
262 | "language": "python",
263 | "name": "python3"
264 | },
265 | "language_info": {
266 | "codemirror_mode": {
267 | "name": "ipython",
268 | "version": 3
269 | },
270 | "file_extension": ".py",
271 | "mimetype": "text/x-python",
272 | "name": "python",
273 | "nbconvert_exporter": "python",
274 | "pygments_lexer": "ipython3",
275 | "version": "3.9.1"
276 | }
277 | },
278 | "nbformat": 4,
279 | "nbformat_minor": 5
280 | }
281 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # h3-py Notebooks
2 |
3 | [](LICENSE)
4 |
5 | Welcome to H3 in Jupyter Notebooks!
6 |
7 | [H3](https://h3geo.org/) is a hierarchical hexagonal
8 | geospatial indexing system.
9 |
10 | This repo is a gallery of Jupyter notebooks including H3 Python
11 | API tutorials, examples and visualizations.
12 | You can visualize them on
13 | [NBViewer](https://nbviewer.jupyter.org/github/uber/h3-py-notebooks/tree/master/notebooks/),
14 | [mybinder](https://mybinder.org/v2/gh/uber/h3-py-notebooks/master?filepath=notebooks),
15 | or execute jupyter notebook from your own terminal.
16 | To execute notebooks locally, you need to install
17 | [`h3-py`](https://github.com/uber/h3-py).
18 |
19 |
20 | You are more than welcome to contribute new notebooks.
21 | Before we can merge your changes, you must agree to the
22 | [Uber Contributor License Agreement](https://cla-assistant.io/uber/h3).
23 |
24 | This repo is licensed under [Apache 2.0 License](LICENSE).
25 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | h3>=3.6.1
2 |
3 | jupyterlab
4 | pandas
5 | geopandas
6 | matplotlib
7 | descartes
8 | folium
9 | rtree
10 | xarray
11 | rasterio
12 | tabulate
13 |
--------------------------------------------------------------------------------