├── .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 | [![version](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](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 | --------------------------------------------------------------------------------