├── .github └── workflows │ ├── _extension_deploy.yml │ └── test.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── extension_config.cmake ├── src ├── h3_common.cpp ├── h3_directededge.cpp ├── h3_extension.cpp ├── h3_hierarchy.cpp ├── h3_indexing.cpp ├── h3_inspection.cpp ├── h3_misc.cpp ├── h3_regions.cpp ├── h3_traversal.cpp ├── h3_vertex.cpp └── include │ ├── h3_common.hpp │ ├── h3_extension.hpp │ └── h3_functions.hpp ├── test.sql ├── test ├── data │ └── simple.parquet └── sql │ └── h3 │ ├── h3_functions.test │ ├── h3_functions_directededge.test │ ├── h3_functions_hierarchy.test │ ├── h3_functions_indexing.test │ ├── h3_functions_inspection.test │ ├── h3_functions_misc.test │ ├── h3_functions_regions.test │ ├── h3_functions_regions_old_order.test │ ├── h3_functions_traversal.test │ ├── h3_functions_vertex.test │ └── h3_parquet.test └── vcpkg.json /.github/workflows/_extension_deploy.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Reusable workflow that deploys the artifacts produced by github.com/duckdb/duckdb/.github/workflows/_extension_distribution.yml 3 | # 4 | # note: this workflow needs to be located in the extension repository, as it requires secrets to be passed to the 5 | # deploy script. However, it should generally not be necessary to modify this workflow in your extension repository, as 6 | # this workflow can be configured to use a custom deploy script. 7 | 8 | 9 | name: Extension Deployment 10 | on: 11 | workflow_call: 12 | inputs: 13 | # The name of the extension 14 | extension_name: 15 | required: true 16 | type: string 17 | # DuckDB version to build against 18 | duckdb_version: 19 | required: true 20 | type: string 21 | # ';' separated list of architectures to exclude, for example: 'linux_amd64;osx_arm64' 22 | exclude_archs: 23 | required: false 24 | type: string 25 | default: "" 26 | # Whether to upload this deployment as the latest. This may overwrite a previous deployment. 27 | deploy_latest: 28 | required: false 29 | type: boolean 30 | default: false 31 | # Whether to upload this deployment under a versioned path. These will not be deleted automatically 32 | deploy_versioned: 33 | required: false 34 | type: boolean 35 | default: false 36 | # Postfix added to artifact names. Can be used to guarantee unique names when this workflow is called multiple times 37 | artifact_postfix: 38 | required: false 39 | type: string 40 | default: "" 41 | # Override the default deploy script with a custom script 42 | deploy_script: 43 | required: false 44 | type: string 45 | default: "./duckdb/scripts/extension-upload-single.sh" 46 | # Override the default matrix parse script with a custom script 47 | matrix_parse_script: 48 | required: false 49 | type: string 50 | default: "./duckdb/scripts/modify_distribution_matrix.py" 51 | 52 | jobs: 53 | generate_matrix: 54 | name: Generate matrix 55 | runs-on: ubuntu-latest 56 | outputs: 57 | deploy_matrix: ${{ steps.parse-matrices.outputs.deploy_matrix }} 58 | steps: 59 | - uses: actions/checkout@v3 60 | with: 61 | fetch-depth: 0 62 | submodules: 'true' 63 | 64 | - name: Checkout DuckDB to version 65 | run: | 66 | cd duckdb 67 | git checkout ${{ inputs.duckdb_version }} 68 | 69 | - id: parse-matrices 70 | run: | 71 | python3 ${{ inputs.matrix_parse_script }} --input ./duckdb/.github/config/distribution_matrix.json --deploy_matrix --output deploy_matrix.json --exclude "${{ inputs.exclude_archs }}" --pretty 72 | deploy_matrix="`cat deploy_matrix.json`" 73 | echo deploy_matrix=$deploy_matrix >> $GITHUB_OUTPUT 74 | echo `cat $GITHUB_OUTPUT` 75 | 76 | deploy: 77 | name: Deploy 78 | runs-on: ubuntu-latest 79 | needs: generate_matrix 80 | if: ${{ needs.generate_matrix.outputs.deploy_matrix != '{}' && needs.generate_matrix.outputs.deploy_matrix != '' }} 81 | strategy: 82 | matrix: ${{fromJson(needs.generate_matrix.outputs.deploy_matrix)}} 83 | 84 | steps: 85 | - uses: actions/checkout@v3 86 | with: 87 | fetch-depth: 0 88 | submodules: 'true' 89 | 90 | - name: Checkout DuckDB to version 91 | run: | 92 | cd duckdb 93 | git checkout ${{ inputs.duckdb_version }} 94 | 95 | - uses: actions/download-artifact@v4 96 | with: 97 | name: ${{ inputs.extension_name }}-${{ inputs.duckdb_version }}-extension-${{matrix.duckdb_arch}}${{inputs.artifact_postfix}}${{startsWith(matrix.duckdb, 'wasm') && '.wasm' || ''}} 98 | path: | 99 | /tmp/extension 100 | 101 | - name: Deploy 102 | shell: bash 103 | env: 104 | AWS_ACCESS_KEY_ID: ${{ secrets.S3_DUCKDB_ORG_DEPLOY_ID }} 105 | AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_DUCKDB_ORG_DEPLOY_KEY }} 106 | AWS_DEFAULT_REGION: ${{ secrets.S3_DUCKDB_ORG_REGION }} 107 | AWS_ENDPOINT_URL: ${{ secrets.S3_DUCKDB_ORG_ENDPOINT }} 108 | BUCKET_NAME: ${{ secrets.S3_DUCKDB_ORG_BUCKET }} 109 | DUCKDB_EXTENSION_SIGNING_PK: ${{ secrets.S3_DUCKDB_ORG_EXTENSION_SIGNING_PK }} 110 | DUCKDB_DEPLOY_SCRIPT_MODE: for_real 111 | run: | 112 | pwd 113 | python3 -m pip install pip awscli 114 | git config --global --add safe.directory '*' 115 | cd duckdb 116 | git fetch --tags 117 | export DUCKDB_VERSION=`git tag --points-at HEAD` 118 | export DUCKDB_VERSION=${DUCKDB_VERSION:=`git log -1 --format=%h`} 119 | cd .. 120 | git fetch --tags 121 | export EXT_VERSION=`git tag --points-at HEAD` 122 | export EXT_VERSION=${EXT_VERSION:=`git log -1 --format=%h`} 123 | ${{ inputs.deploy_script }} ${{ inputs.extension_name }} $EXT_VERSION $DUCKDB_VERSION ${{ matrix.duckdb_arch }} $BUCKET_NAME ${{inputs.deploy_latest || 'true' && 'false'}} ${{inputs.deploy_versioned || 'true' && 'false'}} 124 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This workflow calls the main distribution pipeline from DuckDB to build, test and (optionally) release the extension 3 | # 4 | name: Main Extension Distribution Pipeline 5 | on: 6 | push: 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || '' }}-${{ github.base_ref || '' }}-${{ github.ref != 'refs/heads/main' || github.sha }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | duckdb-next-build: 16 | name: Build extension binaries 17 | uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@main 18 | with: 19 | duckdb_version: main 20 | ci_tools_version: main 21 | extension_name: h3 22 | 23 | duckdb-stable-build: 24 | name: Build extension binaries 25 | uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v1.3.0 26 | with: 27 | duckdb_version: v1.3.0 28 | ci_tools_version: v1.3.0 29 | extension_name: h3 30 | 31 | duckdb-stable-deploy: 32 | name: Deploy extension binaries 33 | needs: duckdb-stable-build 34 | uses: ./.github/workflows/_extension_deploy.yml 35 | secrets: inherit 36 | with: 37 | duckdb_version: v1.3.0 38 | extension_name: h3 39 | deploy_latest: ${{ startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' }} 40 | deploy_versioned: ${{ startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' }} 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | cmake_install.cmake 7 | install_manifest.txt 8 | compile_commands.json 9 | CTestTestfile.cmake 10 | _deps 11 | /cmake/ 12 | build/ 13 | .vscode/ 14 | 15 | .*.swp 16 | .DS_Store 17 | data/ 18 | .env/ 19 | env/ 20 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "duckdb"] 2 | path = duckdb 3 | url = https://github.com/duckdb/duckdb.git 4 | [submodule "h3"] 5 | path = h3 6 | url = https://github.com/uber/h3.git 7 | [submodule "extension-ci-tools"] 8 | path = extension-ci-tools 9 | url = https://github.com/duckdb/extension-ci-tools.git 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5...3.29) 2 | set(TARGET_NAME h3) 3 | set(EXTENSION_NAME ${TARGET_NAME}_extension) 4 | set(LOADABLE_EXTENSION_NAME ${TARGET_NAME}_loadable_extension) 5 | 6 | project(${TARGET_NAME}) 7 | 8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 9 | set(CMAKE_C_STANDARD_REQUIRED 1) 10 | set(CMAKE_C_STANDARD 11) 11 | if(WIN32) 12 | set(CMAKE_CXX_STANDARD_REQUIRED 1) 13 | set(CMAKE_CXX_STANDARD 20) 14 | endif() 15 | 16 | # Avoid building tooling we won't need for release 17 | set(BUILD_BENCHMARKS 18 | OFF 19 | CACHE BOOL "" FORCE) 20 | set(BUILD_FILTERS 21 | OFF 22 | CACHE BOOL "" FORCE) 23 | set(BUILD_GENERATORS 24 | OFF 25 | CACHE BOOL "" FORCE) 26 | set(BUILD_TESTING 27 | OFF 28 | CACHE BOOL "" FORCE) 29 | set(BUILD_FUZZERS 30 | OFF 31 | CACHE BOOL "" FORCE) 32 | set(ENABLE_DOCS 33 | OFF 34 | CACHE BOOL "" FORCE) 35 | set(ENABLE_TESTING 36 | OFF 37 | CACHE BOOL "" FORCE) 38 | set(ENABLE_LINTING 39 | OFF 40 | CACHE BOOL "" FORCE) 41 | set(ENABLE_FORMAT 42 | OFF 43 | CACHE BOOL "" FORCE) 44 | 45 | # Build the core library as static TODO: Is this needed? Consider restoring 46 | # correctly 47 | set(BUILD_SHARED_LIBS OFF) 48 | add_subdirectory(h3) 49 | 50 | # Build the rest (other than the core library dependency) as shared 51 | set(BUILD_SHARED_LIBS ON) 52 | 53 | set(EXTENSION_SOURCES 54 | src/h3_extension.cpp 55 | src/h3_common.cpp 56 | src/h3_indexing.cpp 57 | src/h3_inspection.cpp 58 | src/h3_hierarchy.cpp 59 | src/h3_traversal.cpp 60 | src/h3_vertex.cpp 61 | src/h3_directededge.cpp 62 | src/h3_misc.cpp 63 | src/h3_regions.cpp) 64 | set(LIB_HEADER_FILES src/include/h3_common.hpp src/include/h3_functions.hpp 65 | src/include/h3_extension.hpp) 66 | set(ALL_SOURCE_FILES ${EXTENSION_SOURCES} ${LIB_HEADER_FILES}) 67 | 68 | add_library(${EXTENSION_NAME} STATIC ${EXTENSION_SOURCES}) 69 | 70 | # Note this must be the INSTALL target name. See 71 | # https://stackoverflow.com/a/71080574 72 | target_link_libraries(${EXTENSION_NAME} h3) 73 | 74 | include_directories(src/include) 75 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/h3/src/h3lib/include") 76 | 77 | build_loadable_extension(${TARGET_NAME} " " ${EXTENSION_SOURCES}) 78 | target_link_libraries(${LOADABLE_EXTENSION_NAME} h3) 79 | 80 | install( 81 | TARGETS ${EXTENSION_NAME} h3 82 | EXPORT "${DUCKDB_EXPORT_SET}" 83 | LIBRARY DESTINATION "${INSTALL_LIB_DIR}" 84 | ARCHIVE DESTINATION "${INSTALL_LIB_DIR}") 85 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJ_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) 2 | 3 | # Configuration of extension 4 | EXT_NAME=h3 5 | EXT_CONFIG=${PROJ_DIR}extension_config.cmake 6 | 7 | # Include the Makefile from extension-ci-tools 8 | include extension-ci-tools/makefiles/duckdb_extension.Makefile 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Extension Test](https://github.com/isaacbrodsky/h3-duckdb/actions/workflows/test.yml/badge.svg)](https://github.com/isaacbrodsky/h3-duckdb/actions/workflows/test.yml) 2 | [![DuckDB Version](https://img.shields.io/static/v1?label=duckdb&message=v1.3.0&color=blue)](https://github.com/duckdb/duckdb/releases/tag/v1.3.0) 3 | [![H3 Version](https://img.shields.io/static/v1?label=h3&message=v4.2.1&color=blue)](https://github.com/uber/h3/releases/tag/v4.2.1) 4 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) 5 | 6 | This is a [DuckDB](https://duckdb.org) extension that adds support for the [H3 discrete global grid system](https://github.com/uber/h3/), so you can index points and geometries to hexagons in SQL. 7 | 8 | # Get started 9 | 10 | Load from the [community extensions repository](https://community-extensions.duckdb.org/extensions/h3.html): 11 | ```SQL 12 | INSTALL h3 FROM community; 13 | LOAD h3; 14 | ``` 15 | 16 | Test running an H3 function: 17 | ```SQL 18 | SELECT h3_cell_to_latlng('822d57fffffffff'); 19 | ``` 20 | 21 | Or, using the integer API, which generally has better performance: 22 | ```SQL 23 | SELECT h3_cell_to_latlng(586265647244115967); 24 | ``` 25 | 26 | # Implemented functions 27 | 28 | This extension implements the entire [H3 API](https://h3geo.org/docs/api/indexing). The full list of functions is below. 29 | 30 | All functions support H3 indexes specified as `UBIGINT` (`uint64`) or `BIGINT` (`int64`), 31 | but the unsigned one is preferred and is returned when the extension can't detect which 32 | one to use. The unsigned and signed APIs are identical. All functions also support 33 | `VARCHAR` H3 index input and output. 34 | 35 | ### Full list of functions 36 | 37 | | Function | Description 38 | | --: | --- 39 | | `h3_latlng_to_cell` | Convert latitude/longitude coordinate to cell ID 40 | | `h3_latlng_to_cell_string` | Convert latitude/longitude coordinate to cell ID (returns VARCHAR) 41 | | `h3_cell_to_lat` | Convert cell ID to latitude 42 | | `h3_cell_to_lng` | Convert cell ID to longitude 43 | | `h3_cell_to_latlng` | Convert cell ID to latitude/longitude 44 | | `h3_cell_to_boundary_wkt` | Convert cell ID to cell boundary 45 | | `h3_get_resolution` | Get resolution number of cell ID 46 | | `h3_get_base_cell_number` | Get base cell number of cell ID 47 | | `h3_string_to_h3` | Convert VARCHAR cell ID to UBIGINT 48 | | `h3_h3_to_string` | Convert BIGINT or UBIGINT cell ID to VARCHAR 49 | | `h3_is_valid_cell` | True if this is a valid cell ID 50 | | `h3_is_res_class_iii` | True if the cell's resolution is class III 51 | | `h3_is_pentagon` | True if the cell is a pentagon 52 | | `h3_get_icosahedron_faces` | List of icosahedron face IDs the cell is on 53 | | `h3_cell_to_parent` | Get coarser cell for a cell 54 | | `h3_cell_to_children` | Get finer cells for a cell 55 | | `h3_cell_to_center_child` | Get the center finer cell for a cell 56 | | `h3_cell_to_child_pos` | Get a sub-indexing number for a cell inside a parent 57 | | `h3_child_pos_to_cell` | Convert parent and sub-indexing number to a cell ID 58 | | `h3_compact_cells` | Convert a set of single-resolution cells to the minimal mixed-resolution set 59 | | `h3_uncompact_cells` | Convert a mixed-resolution set to a single-resolution set of cells 60 | | `h3_grid_disk` | Find cells within a grid distance 61 | | `h3_grid_disk_distances` | Find cells within a grid distance, sorted by distance 62 | | `h3_grid_disk_unsafe` | Find cells within a grid distance, with no pentagon distortion 63 | | `h3_grid_disk_distances_unsafe` | Find cells within a grid distance, sorted by distance, with no pentagon distortion 64 | | `h3_grid_disk_distances_safe` | Find cells within a grid distance, sorted by distance 65 | | `h3_grid_ring_unsafe` | Find cells exactly a grid distance away, with no pentagon distortion 66 | | `h3_grid_path_cells` | Find a grid path to connect two cells 67 | | `h3_grid_distance` | Find the grid distance between two cells 68 | | `h3_cell_to_local_ij` | Convert a cell ID to a local I,J coordinate space 69 | | `h3_local_ij_to_cell` | Convert a local I,J coordinate to a cell ID 70 | | `h3_cell_to_vertex` | Get the vertex ID for a cell ID and vertex number 71 | | `h3_cell_to_vertexes` | Get all vertex IDs for a cell ID 72 | | `h3_vertex_to_lat` | Convert a vertex ID to latitude 73 | | `h3_vertex_to_lng` | Convert a vertex ID to longitude 74 | | `h3_vertex_to_latlng` | Convert a vertex ID to latitude/longitude coordinate 75 | | `h3_is_valid_vertex` | True if passed a valid vertex ID 76 | | `h3_is_valid_directed_edge` | True if passed a valid directed edge ID 77 | | `h3_origin_to_directed_edges` | Get all directed edge IDs for a cell ID 78 | | `h3_directed_edge_to_cells` | Convert a directed edge ID to origin/destination cell IDs 79 | | `h3_get_directed_edge_origin` | Convert a directed edge ID to origin cell ID 80 | | `h3_get_directed_edge_destination` | Convert a directed edge ID to destination cell ID 81 | | `h3_cells_to_directed_edge` | Convert an origin/destination pair to directed edge ID 82 | | `h3_are_neighbor_cells` | True if the two cell IDs are directly adjacent 83 | | `h3_directed_edge_to_boundary_wkt` | Convert directed edge ID to linestring WKT 84 | | `h3_get_hexagon_area_avg` | Get average area of a hexagon cell at resolution 85 | | `h3_cell_area` | Get the area of a cell ID 86 | | `h3_get_hexagon_edge_length_avg` | Average hexagon edge length at resolution 87 | | `h3_edge_length` | Get the length of a directed edge ID 88 | | `h3_get_num_cells` | Get the number of cells at a resolution 89 | | `h3_get_res0_cells` | Get all resolution 0 cells 90 | | `h3_get_res0_cells_string` | Get all resolution 0 cells (returns VARCHAR) 91 | | `h3_get_pentagons` | Get all pentagons at a resolution 92 | | `h3_get_pentagons_string` | Get all pentagons at a resolution (returns VARCHAR) 93 | | `h3_great_circle_distance` | Compute the great circle distance between two points (haversine) 94 | | `h3_cells_to_multi_polygon_wkt` | Convert a set of cells to multipolygon WKT 95 | | `h3_polygon_wkt_to_cells` | Convert polygon WKT to a set of cells 96 | | `h3_polygon_wkt_to_cells_string` | Convert polygon WKT to a set of cells (returns VARCHAR) 97 | | `h3_polygon_wkt_to_cells_experimental` | Convert polygon WKT to a set of cells, new algorithm 98 | | `h3_polygon_wkt_to_cells_experimental_string` | Convert polygon WKT to a set of cells, new algorithm (returns VARCHAR) 99 | 100 | # Alternative download / install 101 | 102 | If you'd like to install the H3 extension from the version published here, rather than the community extension version, you will need to run DuckDB with the unsigned option: 103 | ```sh 104 | duckdb -unsigned 105 | ``` 106 | 107 | Load the extension: 108 | ```SQL 109 | INSTALL h3 FROM 'https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev'; 110 | LOAD h3; 111 | ``` 112 | 113 | If you want to directly download the latest version of the extension: [Linux AMD64](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/linux_amd64/h3.duckdb_extension.gz) [Linux AMD64 GCC4](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/linux_amd64_gcc4/h3.duckdb_extension.gz) [Linux Arm64](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/linux_arm64/h3.duckdb_extension.gz) [OSX AMD64](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/osx_amd64/h3.duckdb_extension.gz) [OSX Arm64](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/osx_arm64/h3.duckdb_extension.gz) [wasm eh](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/wasm_eh/h3.duckdb_extension.wasm) [wasm mvp](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/wasm_mvp/h3.duckdb_extension.wasm) [wasm threads](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/wasm_threads/h3.duckdb_extension.wasm) [Windows AMD64](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/windows_amd64/h3.duckdb_extension.gz) [Windows AMD64 MinGW](https://pub-cc26a6fd5d8240078bd0c2e0623393a5.r2.dev/v1.3.0/windows_amd64_mingw/h3.duckdb_extension.gz) 114 | 115 | # Development 116 | 117 | To build, type: 118 | ```sh 119 | git submodule update --init 120 | GEN=ninja make release 121 | ``` 122 | 123 | You will need Git, CMake, and a C compiler. The build instructions suggest using `ninja` 124 | because it enables parallelism by default. Using `make` instead is fine, but you will want 125 | to enable the following parallelism option, because building DuckDB can take a very long 126 | time (>=1 hour is not unusual). Run the below replacing `4` with the number of CPU cores 127 | on your machine. 128 | 129 | ```sh 130 | CMAKE_BUILD_PARALLEL_LEVEL=4 make duckdb_release release 131 | ``` 132 | 133 | To run, run the bundled `duckdb` shell: 134 | 135 | ```sh 136 | ./build/release/duckdb -unsigned 137 | ``` 138 | 139 | Load the extension: 140 | 141 | ```SQL 142 | load 'build/release/extension/h3/h3.duckdb_extension'; 143 | ``` 144 | 145 | To run tests: 146 | 147 | ```sh 148 | make test 149 | ``` 150 | 151 | To update the submodules to latest upstream, run: 152 | 153 | ```sh 154 | make update_deps 155 | ``` 156 | 157 | # License 158 | 159 | h3-duckdb Copyright 2022 Isaac Brodsky. Licensed under the [Apache 2.0 License](./LICENSE). 160 | 161 | [H3](https://github.com/uber/h3) Copyright 2018 Uber Technologies Inc. (Apache 2.0 License) 162 | 163 | DGGRID Copyright (c) 2015 Southern Oregon University 164 | 165 | [DuckDB](https://github.com/duckdb/duckdb) Copyright 2018-2022 Stichting DuckDB Foundation (MIT License) 166 | 167 | [DuckDB extension-template](https://github.com/duckdb/extension-template) Copyright 2018-2022 DuckDB Labs BV (MIT License) 168 | -------------------------------------------------------------------------------- /extension_config.cmake: -------------------------------------------------------------------------------- 1 | # This file is included by DuckDB's build system. It specifies which extension to load 2 | 3 | # Extension from this repo 4 | duckdb_extension_load(h3 5 | SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR} 6 | LINKED_LIBS "h3/lib/libh3.a" 7 | LOAD_TESTS 8 | ) 9 | 10 | # Any extra extensions that should be built 11 | # e.g.: duckdb_extension_load(json) 12 | -------------------------------------------------------------------------------- /src/h3_common.cpp: -------------------------------------------------------------------------------- 1 | #include "h3_common.hpp" 2 | 3 | namespace duckdb { 4 | 5 | void ThrowH3Error(H3Error err) { 6 | if (err) { 7 | throw InvalidInputException(StringUtil::Format("H3 error: '%d'", err)); 8 | } 9 | } 10 | 11 | } // namespace duckdb 12 | -------------------------------------------------------------------------------- /src/h3_directededge.cpp: -------------------------------------------------------------------------------- 1 | #include "h3_common.hpp" 2 | #include "h3_functions.hpp" 3 | 4 | namespace duckdb { 5 | 6 | static void DirectedEdgeToCellsFunction(DataChunk &args, ExpressionState &state, 7 | Vector &result) { 8 | auto result_data = FlatVector::GetData(result); 9 | for (idx_t i = 0; i < args.size(); i++) { 10 | result_data[i].offset = ListVector::GetListSize(result); 11 | 12 | uint64_t edge = args.GetValue(0, i) 13 | .DefaultCastAs(LogicalType::UBIGINT) 14 | .GetValue(); 15 | std::vector out(2); 16 | H3Error err = directedEdgeToCells(edge, out.data()); 17 | if (err) { 18 | result.SetValue(i, Value(LogicalType::SQLNULL)); 19 | } else { 20 | ListVector::PushBack(result, Value::UBIGINT(out[0])); 21 | ListVector::PushBack(result, Value::UBIGINT(out[1])); 22 | 23 | result_data[i].length = 2; 24 | } 25 | } 26 | result.Verify(args.size()); 27 | } 28 | 29 | static void DirectedEdgeToCellsVarcharFunction(DataChunk &args, 30 | ExpressionState &state, 31 | Vector &result) { 32 | auto result_data = FlatVector::GetData(result); 33 | for (idx_t i = 0; i < args.size(); i++) { 34 | result_data[i].offset = ListVector::GetListSize(result); 35 | 36 | string edgeInput = args.GetValue(0, i) 37 | .DefaultCastAs(LogicalType::VARCHAR) 38 | .GetValue(); 39 | 40 | H3Index edge; 41 | H3Error err0 = stringToH3(edgeInput.c_str(), &edge); 42 | if (err0) { 43 | result.SetValue(i, Value(LogicalType::SQLNULL)); 44 | } else { 45 | std::vector out(2); 46 | H3Error err1 = directedEdgeToCells(edge, out.data()); 47 | if (err1) { 48 | result.SetValue(i, Value(LogicalType::SQLNULL)); 49 | } else { 50 | auto str0 = StringUtil::Format("%llx", out[0]); 51 | string_t strAsStr0 = string_t(strdup(str0.c_str()), str0.size()); 52 | ListVector::PushBack(result, strAsStr0); 53 | auto str1 = StringUtil::Format("%llx", out[1]); 54 | string_t strAsStr1 = string_t(strdup(str1.c_str()), str1.size()); 55 | ListVector::PushBack(result, strAsStr1); 56 | 57 | result_data[i].length = 2; 58 | } 59 | } 60 | } 61 | result.Verify(args.size()); 62 | } 63 | 64 | static void OriginToDirectedEdgesFunction(DataChunk &args, 65 | ExpressionState &state, 66 | Vector &result) { 67 | D_ASSERT(result.GetType().id() == LogicalTypeId::LIST); 68 | 69 | auto result_data = FlatVector::GetData(result); 70 | for (idx_t i = 0; i < args.size(); i++) { 71 | result_data[i].offset = ListVector::GetListSize(result); 72 | 73 | uint64_t origin = args.GetValue(0, i) 74 | .DefaultCastAs(LogicalType::UBIGINT) 75 | .GetValue(); 76 | int64_t actual = 0; 77 | std::vector out(6); 78 | H3Error err = originToDirectedEdges(origin, out.data()); 79 | if (err) { 80 | result.SetValue(i, Value(LogicalType::SQLNULL)); 81 | } else { 82 | for (auto val : out) { 83 | if (val != H3_NULL) { 84 | ListVector::PushBack(result, Value::UBIGINT(val)); 85 | actual++; 86 | } 87 | } 88 | 89 | result_data[i].length = actual; 90 | } 91 | } 92 | result.Verify(args.size()); 93 | } 94 | 95 | static void OriginToDirectedEdgesVarcharFunction(DataChunk &args, 96 | ExpressionState &state, 97 | Vector &result) { 98 | D_ASSERT(result.GetType().id() == LogicalTypeId::LIST); 99 | 100 | auto result_data = FlatVector::GetData(result); 101 | for (idx_t i = 0; i < args.size(); i++) { 102 | result_data[i].offset = ListVector::GetListSize(result); 103 | 104 | string originStr = args.GetValue(0, i) 105 | .DefaultCastAs(LogicalType::VARCHAR) 106 | .GetValue(); 107 | 108 | H3Index origin; 109 | H3Error err0 = stringToH3(originStr.c_str(), &origin); 110 | if (err0) { 111 | result.SetValue(i, Value(LogicalType::SQLNULL)); 112 | } else { 113 | int64_t actual = 0; 114 | std::vector out(6); 115 | H3Error err1 = originToDirectedEdges(origin, out.data()); 116 | if (err1) { 117 | result.SetValue(i, Value(LogicalType::SQLNULL)); 118 | } else { 119 | for (auto val : out) { 120 | if (val != H3_NULL) { 121 | auto str = StringUtil::Format("%llx", val); 122 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 123 | ListVector::PushBack(result, strAsStr); 124 | actual++; 125 | } 126 | } 127 | 128 | result_data[i].length = actual; 129 | } 130 | } 131 | } 132 | result.Verify(args.size()); 133 | } 134 | 135 | static void GetDirectedEdgeOriginFunction(DataChunk &args, 136 | ExpressionState &state, 137 | Vector &result) { 138 | auto &inputs = args.data[0]; 139 | UnaryExecutor::ExecuteWithNulls( 140 | inputs, result, args.size(), 141 | [&](uint64_t input, ValidityMask &mask, idx_t idx) { 142 | H3Index out; 143 | H3Error err = getDirectedEdgeOrigin(input, &out); 144 | if (err) { 145 | mask.SetInvalid(idx); 146 | return H3Index(H3_NULL); 147 | } else { 148 | return out; 149 | } 150 | }); 151 | } 152 | 153 | static void GetDirectedEdgeOriginVarcharFunction(DataChunk &args, 154 | ExpressionState &state, 155 | Vector &result) { 156 | auto &inputs = args.data[0]; 157 | UnaryExecutor::ExecuteWithNulls( 158 | inputs, result, args.size(), 159 | [&](string_t inputStr, ValidityMask &mask, idx_t idx) { 160 | H3Index input; 161 | H3Error err0 = stringToH3(inputStr.GetString().c_str(), &input); 162 | if (err0) { 163 | mask.SetInvalid(idx); 164 | return StringVector::EmptyString(result, 0); 165 | } else { 166 | H3Index out; 167 | H3Error err1 = getDirectedEdgeOrigin(input, &out); 168 | if (err1) { 169 | mask.SetInvalid(idx); 170 | return StringVector::EmptyString(result, 0); 171 | } else { 172 | auto str = StringUtil::Format("%llx", out); 173 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 174 | return StringVector::AddString(result, strAsStr); 175 | } 176 | } 177 | }); 178 | } 179 | 180 | static void GetDirectedEdgeDestinationFunction(DataChunk &args, 181 | ExpressionState &state, 182 | Vector &result) { 183 | auto &inputs = args.data[0]; 184 | UnaryExecutor::ExecuteWithNulls( 185 | inputs, result, args.size(), 186 | [&](uint64_t input, ValidityMask &mask, idx_t idx) { 187 | H3Index out; 188 | H3Error err = getDirectedEdgeDestination(input, &out); 189 | if (err) { 190 | mask.SetInvalid(idx); 191 | return H3Index(H3_NULL); 192 | } else { 193 | return out; 194 | } 195 | }); 196 | } 197 | 198 | static void GetDirectedEdgeDestinationVarcharFunction(DataChunk &args, 199 | ExpressionState &state, 200 | Vector &result) { 201 | auto &inputs = args.data[0]; 202 | UnaryExecutor::ExecuteWithNulls( 203 | inputs, result, args.size(), 204 | [&](string_t inputStr, ValidityMask &mask, idx_t idx) { 205 | H3Index input; 206 | H3Error err0 = stringToH3(inputStr.GetString().c_str(), &input); 207 | if (err0) { 208 | mask.SetInvalid(idx); 209 | return StringVector::EmptyString(result, 0); 210 | } else { 211 | H3Index out; 212 | H3Error err1 = getDirectedEdgeDestination(input, &out); 213 | if (err1) { 214 | mask.SetInvalid(idx); 215 | return StringVector::EmptyString(result, 0); 216 | } else { 217 | auto str = StringUtil::Format("%llx", out); 218 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 219 | return StringVector::AddString(result, strAsStr); 220 | } 221 | } 222 | }); 223 | } 224 | 225 | static void CellsToDirectedEdgeFunction(DataChunk &args, ExpressionState &state, 226 | Vector &result) { 227 | auto &inputs = args.data[0]; 228 | auto &inputs2 = args.data[1]; 229 | BinaryExecutor::ExecuteWithNulls( 230 | inputs, inputs2, result, args.size(), 231 | [&](uint64_t input, uint64_t input2, ValidityMask &mask, idx_t idx) { 232 | H3Index out; 233 | H3Error err = cellsToDirectedEdge(input, input2, &out); 234 | if (err) { 235 | mask.SetInvalid(idx); 236 | return H3Index(H3_NULL); 237 | } else { 238 | return out; 239 | } 240 | }); 241 | } 242 | 243 | static void CellsToDirectedEdgeVarcharFunction(DataChunk &args, 244 | ExpressionState &state, 245 | Vector &result) { 246 | auto &inputs = args.data[0]; 247 | auto &inputs2 = args.data[1]; 248 | BinaryExecutor::ExecuteWithNulls( 249 | inputs, inputs2, result, args.size(), 250 | [&](string_t inputStr, string_t inputStr2, ValidityMask &mask, 251 | idx_t idx) { 252 | H3Index input, input2; 253 | H3Error err0 = stringToH3(inputStr.GetString().c_str(), &input); 254 | H3Error err1 = stringToH3(inputStr2.GetString().c_str(), &input2); 255 | if (err0 || err1) { 256 | mask.SetInvalid(idx); 257 | return StringVector::EmptyString(result, 0); 258 | } else { 259 | H3Index out; 260 | H3Error err = cellsToDirectedEdge(input, input2, &out); 261 | if (err) { 262 | mask.SetInvalid(idx); 263 | return StringVector::EmptyString(result, 0); 264 | } else { 265 | auto str = StringUtil::Format("%llx", out); 266 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 267 | return StringVector::AddString(result, strAsStr); 268 | } 269 | } 270 | }); 271 | } 272 | 273 | static void AreNeighborCellsFunction(DataChunk &args, ExpressionState &state, 274 | Vector &result) { 275 | auto &inputs = args.data[0]; 276 | auto &inputs2 = args.data[1]; 277 | BinaryExecutor::ExecuteWithNulls( 278 | inputs, inputs2, result, args.size(), 279 | [&](uint64_t input, uint64_t input2, ValidityMask &mask, idx_t idx) { 280 | int out; 281 | H3Error err = areNeighborCells(input, input2, &out); 282 | if (err) { 283 | mask.SetInvalid(idx); 284 | return bool(false); 285 | } else { 286 | return bool(out); 287 | } 288 | }); 289 | } 290 | 291 | static void AreNeighborCellsVarcharFunction(DataChunk &args, 292 | ExpressionState &state, 293 | Vector &result) { 294 | auto &inputs = args.data[0]; 295 | auto &inputs2 = args.data[1]; 296 | BinaryExecutor::ExecuteWithNulls( 297 | inputs, inputs2, result, args.size(), 298 | [&](string_t inputStr, string_t inputStr2, ValidityMask &mask, 299 | idx_t idx) { 300 | H3Index input, input2; 301 | H3Error err0 = stringToH3(inputStr.GetString().c_str(), &input); 302 | H3Error err1 = stringToH3(inputStr2.GetString().c_str(), &input2); 303 | if (err0 || err1) { 304 | mask.SetInvalid(idx); 305 | return bool(false); 306 | } else { 307 | int out; 308 | H3Error err2 = areNeighborCells(input, input2, &out); 309 | if (err2) { 310 | mask.SetInvalid(idx); 311 | return bool(false); 312 | } else { 313 | return bool(out); 314 | } 315 | } 316 | }); 317 | } 318 | 319 | static void IsValidDirectedEdgeVarcharFunction(DataChunk &args, 320 | ExpressionState &state, 321 | Vector &result) { 322 | auto &inputs = args.data[0]; 323 | UnaryExecutor::Execute( 324 | inputs, result, args.size(), [&](string_t input) { 325 | H3Index h; 326 | H3Error err = stringToH3(input.GetString().c_str(), &h); 327 | if (err) { 328 | return false; 329 | } 330 | return bool(isValidDirectedEdge(h)); 331 | }); 332 | } 333 | 334 | static void IsValidDirectedEdgeFunction(DataChunk &args, ExpressionState &state, 335 | Vector &result) { 336 | auto &inputs = args.data[0]; 337 | UnaryExecutor::Execute( 338 | inputs, result, args.size(), 339 | [&](H3Index input) { return bool(isValidDirectedEdge(input)); }); 340 | } 341 | 342 | struct DirectedEdgeToBoundaryOperator { 343 | template 344 | static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) { 345 | CellBoundary boundary; 346 | H3Error err = directedEdgeToBoundary(input, &boundary); 347 | 348 | if (err) { 349 | // TODO: Is it possible to return null here instead? 350 | return StringVector::EmptyString(result, 0); 351 | } else { 352 | std::string str = "LINESTRING (("; 353 | for (int i = 0; i <= boundary.numVerts; i++) { 354 | std::string sep = (i == 0) ? "" : ", "; 355 | int vertIndex = (i == boundary.numVerts) ? 0 : i; 356 | str += StringUtil::Format("%s%f %f", sep, 357 | radsToDegs(boundary.verts[vertIndex].lng), 358 | radsToDegs(boundary.verts[vertIndex].lat)); 359 | } 360 | str += "))"; 361 | 362 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 363 | return StringVector::AddString(result, strAsStr); 364 | } 365 | } 366 | }; 367 | 368 | static void DirectedEdgeToBoundaryWktFunction(DataChunk &args, 369 | ExpressionState &state, 370 | Vector &result) { 371 | UnaryExecutor::ExecuteString( 373 | args.data[0], result, args.size()); 374 | } 375 | 376 | struct DirectedEdgeToBoundaryVarcharOperator { 377 | template 378 | static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) { 379 | H3Index h; 380 | H3Error err = stringToH3(input.GetString().c_str(), &h); 381 | if (err) { 382 | return StringVector::EmptyString(result, 0); 383 | } else { 384 | return DirectedEdgeToBoundaryOperator().Operation( 385 | h, result); 386 | } 387 | } 388 | }; 389 | 390 | static void DirectedEdgeToBoundaryWktVarcharFunction(DataChunk &args, 391 | ExpressionState &state, 392 | Vector &result) { 393 | UnaryExecutor::ExecuteString( 395 | args.data[0], result, args.size()); 396 | } 397 | 398 | CreateScalarFunctionInfo H3Functions::GetDirectedEdgeToCellsFunction() { 399 | ScalarFunctionSet funcs("h3_directed_edge_to_cells"); 400 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, 401 | LogicalType::LIST(LogicalType::UBIGINT), 402 | DirectedEdgeToCellsFunction)); 403 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, 404 | LogicalType::LIST(LogicalType::UBIGINT), 405 | DirectedEdgeToCellsFunction)); 406 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, 407 | LogicalType::LIST(LogicalType::VARCHAR), 408 | DirectedEdgeToCellsVarcharFunction)); 409 | return CreateScalarFunctionInfo(funcs); 410 | } 411 | 412 | CreateScalarFunctionInfo H3Functions::GetOriginToDirectedEdgesFunction() { 413 | ScalarFunctionSet funcs("h3_origin_to_directed_edges"); 414 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, 415 | LogicalType::LIST(LogicalType::UBIGINT), 416 | OriginToDirectedEdgesFunction)); 417 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, 418 | LogicalType::LIST(LogicalType::UBIGINT), 419 | OriginToDirectedEdgesFunction)); 420 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, 421 | LogicalType::LIST(LogicalType::VARCHAR), 422 | OriginToDirectedEdgesVarcharFunction)); 423 | return CreateScalarFunctionInfo(funcs); 424 | } 425 | 426 | CreateScalarFunctionInfo H3Functions::GetGetDirectedEdgeOriginFunction() { 427 | ScalarFunctionSet funcs("h3_get_directed_edge_origin"); 428 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::UBIGINT, 429 | GetDirectedEdgeOriginFunction)); 430 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::BIGINT, 431 | GetDirectedEdgeOriginFunction)); 432 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::VARCHAR, 433 | GetDirectedEdgeOriginVarcharFunction)); 434 | return CreateScalarFunctionInfo(funcs); 435 | } 436 | 437 | CreateScalarFunctionInfo H3Functions::GetGetDirectedEdgeDestinationFunction() { 438 | ScalarFunctionSet funcs("h3_get_directed_edge_destination"); 439 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::UBIGINT, 440 | GetDirectedEdgeDestinationFunction)); 441 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::BIGINT, 442 | GetDirectedEdgeDestinationFunction)); 443 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::VARCHAR, 444 | GetDirectedEdgeDestinationVarcharFunction)); 445 | return CreateScalarFunctionInfo(funcs); 446 | } 447 | 448 | CreateScalarFunctionInfo H3Functions::GetCellsToDirectedEdgeFunction() { 449 | ScalarFunctionSet funcs("h3_cells_to_directed_edge"); 450 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT, LogicalType::UBIGINT}, 451 | LogicalType::UBIGINT, 452 | CellsToDirectedEdgeFunction)); 453 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT, LogicalType::BIGINT}, 454 | LogicalType::BIGINT, 455 | CellsToDirectedEdgeFunction)); 456 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR, LogicalType::VARCHAR}, 457 | LogicalType::VARCHAR, 458 | CellsToDirectedEdgeVarcharFunction)); 459 | return CreateScalarFunctionInfo(funcs); 460 | } 461 | 462 | CreateScalarFunctionInfo H3Functions::GetAreNeighborCellsFunction() { 463 | ScalarFunctionSet funcs("h3_are_neighbor_cells"); 464 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT, LogicalType::UBIGINT}, 465 | LogicalType::BOOLEAN, 466 | AreNeighborCellsFunction)); 467 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT, LogicalType::BIGINT}, 468 | LogicalType::BOOLEAN, 469 | AreNeighborCellsFunction)); 470 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR, LogicalType::VARCHAR}, 471 | LogicalType::BOOLEAN, 472 | AreNeighborCellsVarcharFunction)); 473 | return CreateScalarFunctionInfo(funcs); 474 | } 475 | 476 | CreateScalarFunctionInfo H3Functions::GetIsValidDirectedEdgeFunctions() { 477 | ScalarFunctionSet funcs("h3_is_valid_directed_edge"); 478 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::BOOLEAN, 479 | IsValidDirectedEdgeVarcharFunction)); 480 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::BOOLEAN, 481 | IsValidDirectedEdgeFunction)); 482 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::BOOLEAN, 483 | IsValidDirectedEdgeFunction)); 484 | return CreateScalarFunctionInfo(funcs); 485 | } 486 | 487 | CreateScalarFunctionInfo H3Functions::GetDirectedEdgeToBoundaryWktFunction() { 488 | ScalarFunctionSet funcs("h3_directed_edge_to_boundary_wkt"); 489 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::VARCHAR, 490 | DirectedEdgeToBoundaryWktVarcharFunction)); 491 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::VARCHAR, 492 | DirectedEdgeToBoundaryWktFunction)); 493 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::VARCHAR, 494 | DirectedEdgeToBoundaryWktFunction)); 495 | return CreateScalarFunctionInfo(funcs); 496 | } 497 | 498 | } // namespace duckdb 499 | -------------------------------------------------------------------------------- /src/h3_extension.cpp: -------------------------------------------------------------------------------- 1 | #define DUCKDB_EXTENSION_MAIN 2 | #include "h3_extension.hpp" 3 | 4 | #include "duckdb/main/extension_util.hpp" 5 | #include "h3_functions.hpp" 6 | #include "h3api.h" 7 | 8 | namespace duckdb { 9 | 10 | void H3Extension::Load(DuckDB &db) { 11 | std::string description = 12 | StringUtil::Format("H3 hierarchical hexagonal indexing system for " 13 | "geospatial data, v%d.%d.%d", 14 | H3_VERSION_MAJOR, H3_VERSION_MINOR, H3_VERSION_PATCH); 15 | ExtensionUtil::RegisterExtension(*db.instance, "h3", {description}); 16 | 17 | Connection con(db); 18 | con.BeginTransaction(); 19 | 20 | auto &catalog = Catalog::GetSystemCatalog(*con.context); 21 | for (auto &fun : H3Functions::GetFunctions()) { 22 | catalog.CreateFunction(*con.context, fun); 23 | } 24 | 25 | con.Commit(); 26 | } 27 | 28 | std::string H3Extension::Name() { return "h3"; } 29 | 30 | } // namespace duckdb 31 | 32 | extern "C" { 33 | 34 | DUCKDB_EXTENSION_API void h3_init(duckdb::DatabaseInstance &db) { 35 | duckdb::DuckDB db_wrapper(db); 36 | db_wrapper.LoadExtension(); 37 | } 38 | 39 | DUCKDB_EXTENSION_API const char *h3_version() { 40 | return duckdb::DuckDB::LibraryVersion(); 41 | } 42 | } 43 | 44 | #ifndef DUCKDB_EXTENSION_MAIN 45 | #error DUCKDB_EXTENSION_MAIN not defined 46 | #endif 47 | -------------------------------------------------------------------------------- /src/h3_indexing.cpp: -------------------------------------------------------------------------------- 1 | #include "h3_common.hpp" 2 | #include "h3_functions.hpp" 3 | 4 | namespace duckdb { 5 | 6 | static void LatLngToCellFunction(DataChunk &args, ExpressionState &state, 7 | Vector &result) { 8 | auto &inputs = args.data[0]; 9 | auto &inputs2 = args.data[1]; 10 | auto &inputs3 = args.data[2]; 11 | TernaryExecutor::ExecuteWithNulls( 12 | inputs, inputs2, inputs3, result, args.size(), 13 | [&](double lat, double lng, int res, ValidityMask &mask, idx_t idx) { 14 | H3Index cell; 15 | LatLng latLng = {.lat = degsToRads(lat), .lng = degsToRads(lng)}; 16 | H3Error err = latLngToCell(&latLng, res, &cell); 17 | if (err) { 18 | mask.SetInvalid(idx); 19 | return H3Index(H3_NULL); 20 | } else { 21 | return cell; 22 | } 23 | }); 24 | } 25 | 26 | static void LatLngToCellVarcharFunction(DataChunk &args, ExpressionState &state, 27 | Vector &result) { 28 | auto &inputs = args.data[0]; 29 | auto &inputs2 = args.data[1]; 30 | auto &inputs3 = args.data[2]; 31 | TernaryExecutor::ExecuteWithNulls( 32 | inputs, inputs2, inputs3, result, args.size(), 33 | [&](double lat, double lng, int res, ValidityMask &mask, idx_t idx) { 34 | H3Index cell; 35 | LatLng latLng = {.lat = degsToRads(lat), .lng = degsToRads(lng)}; 36 | H3Error err = latLngToCell(&latLng, res, &cell); 37 | if (err) { 38 | mask.SetInvalid(idx); 39 | return StringVector::EmptyString(result, 0); 40 | } else { 41 | auto str = StringUtil::Format("%llx", cell); 42 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 43 | return StringVector::AddString(result, strAsStr); 44 | } 45 | }); 46 | } 47 | 48 | static void CellToLatFunction(DataChunk &args, ExpressionState &state, 49 | Vector &result) { 50 | auto &inputs = args.data[0]; 51 | UnaryExecutor::ExecuteWithNulls( 52 | inputs, result, args.size(), 53 | [&](H3Index cell, ValidityMask &mask, idx_t idx) { 54 | LatLng latLng = {.lat = 0, .lng = 0}; 55 | H3Error err = cellToLatLng(cell, &latLng); 56 | if (err) { 57 | mask.SetInvalid(idx); 58 | return .0; 59 | } else { 60 | return radsToDegs(latLng.lat); 61 | } 62 | }); 63 | } 64 | 65 | static void CellToLatVarcharFunction(DataChunk &args, ExpressionState &state, 66 | Vector &result) { 67 | auto &inputs = args.data[0]; 68 | UnaryExecutor::ExecuteWithNulls( 69 | inputs, result, args.size(), 70 | [&](string_t cellAddress, ValidityMask &mask, idx_t idx) { 71 | H3Index cell; 72 | H3Error err0 = stringToH3(cellAddress.GetString().c_str(), &cell); 73 | if (err0) { 74 | mask.SetInvalid(idx); 75 | return .0; 76 | } else { 77 | LatLng latLng = {.lat = 0, .lng = 0}; 78 | H3Error err = cellToLatLng(cell, &latLng); 79 | if (err) { 80 | mask.SetInvalid(idx); 81 | return .0; 82 | } else { 83 | return radsToDegs(latLng.lat); 84 | } 85 | } 86 | }); 87 | } 88 | 89 | static void CellToLngFunction(DataChunk &args, ExpressionState &state, 90 | Vector &result) { 91 | auto &inputs = args.data[0]; 92 | UnaryExecutor::ExecuteWithNulls( 93 | inputs, result, args.size(), 94 | [&](H3Index cell, ValidityMask &mask, idx_t idx) { 95 | LatLng latLng = {.lat = 0, .lng = 0}; 96 | H3Error err = cellToLatLng(cell, &latLng); 97 | if (err) { 98 | mask.SetInvalid(idx); 99 | return .0; 100 | } else { 101 | return radsToDegs(latLng.lng); 102 | } 103 | }); 104 | } 105 | 106 | static void CellToLngVarcharFunction(DataChunk &args, ExpressionState &state, 107 | Vector &result) { 108 | auto &inputs = args.data[0]; 109 | UnaryExecutor::ExecuteWithNulls( 110 | inputs, result, args.size(), 111 | [&](string_t cellAddress, ValidityMask &mask, idx_t idx) { 112 | H3Index cell; 113 | H3Error err0 = stringToH3(cellAddress.GetString().c_str(), &cell); 114 | if (err0) { 115 | mask.SetInvalid(idx); 116 | return .0; 117 | } else { 118 | LatLng latLng = {.lat = 0, .lng = 0}; 119 | H3Error err = cellToLatLng(cell, &latLng); 120 | if (err) { 121 | mask.SetInvalid(idx); 122 | return .0; 123 | } else { 124 | return radsToDegs(latLng.lng); 125 | } 126 | } 127 | }); 128 | } 129 | 130 | static void CellToLatLngFunction(DataChunk &args, ExpressionState &state, 131 | Vector &result) { 132 | auto result_data = FlatVector::GetData(result); 133 | for (idx_t i = 0; i < args.size(); i++) { 134 | result_data[i].offset = ListVector::GetListSize(result); 135 | 136 | uint64_t cell = args.GetValue(0, i) 137 | .DefaultCastAs(LogicalType::UBIGINT) 138 | .GetValue(); 139 | LatLng latLng; 140 | H3Error err = cellToLatLng(cell, &latLng); 141 | if (err) { 142 | result.SetValue(i, Value(LogicalType::SQLNULL)); 143 | } else { 144 | ListVector::PushBack(result, radsToDegs(latLng.lat)); 145 | ListVector::PushBack(result, radsToDegs(latLng.lng)); 146 | result_data[i].length = 2; 147 | } 148 | } 149 | result.Verify(args.size()); 150 | } 151 | 152 | static void CellToLatLngVarcharFunction(DataChunk &args, ExpressionState &state, 153 | Vector &result) { 154 | UnifiedVectorFormat vdata; 155 | args.data[0].ToUnifiedFormat(args.size(), vdata); 156 | 157 | auto ldata = UnifiedVectorFormat::GetData(vdata); 158 | 159 | result.SetVectorType(VectorType::FLAT_VECTOR); 160 | auto result_data = FlatVector::GetData(result); 161 | for (idx_t i = 0; i < args.size(); i++) { 162 | result_data[i].offset = ListVector::GetListSize(result); 163 | 164 | string_t cellAddress = ldata[i]; 165 | H3Index cell; 166 | H3Error err0 = stringToH3(cellAddress.GetString().c_str(), &cell); 167 | if (err0) { 168 | result.SetValue(i, Value(LogicalType::SQLNULL)); 169 | } else { 170 | LatLng latLng; 171 | H3Error err = cellToLatLng(cell, &latLng); 172 | if (err) { 173 | result.SetValue(i, Value(LogicalType::SQLNULL)); 174 | } else { 175 | ListVector::PushBack(result, radsToDegs(latLng.lat)); 176 | ListVector::PushBack(result, radsToDegs(latLng.lng)); 177 | result_data[i].length = 2; 178 | } 179 | } 180 | } 181 | result.Verify(args.size()); 182 | } 183 | 184 | struct CellToBoundaryOperator { 185 | template 186 | static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) { 187 | CellBoundary boundary; 188 | H3Error err = cellToBoundary(input, &boundary); 189 | 190 | if (err) { 191 | // TODO: Is it possible to return null here instead? 192 | return StringVector::EmptyString(result, 0); 193 | } else { 194 | std::string str = "POLYGON (("; 195 | for (int i = 0; i <= boundary.numVerts; i++) { 196 | std::string sep = (i == 0) ? "" : ", "; 197 | // Add an extra vertex onto the end to close the polygon 198 | int vertIndex = (i == boundary.numVerts) ? 0 : i; 199 | str += StringUtil::Format("%s%f %f", sep, 200 | radsToDegs(boundary.verts[vertIndex].lng), 201 | radsToDegs(boundary.verts[vertIndex].lat)); 202 | } 203 | str += "))"; 204 | 205 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 206 | return StringVector::AddString(result, strAsStr); 207 | } 208 | } 209 | }; 210 | 211 | static void CellToBoundaryWktFunction(DataChunk &args, ExpressionState &state, 212 | Vector &result) { 213 | UnaryExecutor::ExecuteString( 214 | args.data[0], result, args.size()); 215 | } 216 | 217 | struct CellToBoundaryVarcharOperator { 218 | template 219 | static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) { 220 | H3Index h; 221 | H3Error err = stringToH3(input.GetString().c_str(), &h); 222 | if (err) { 223 | return StringVector::EmptyString(result, 0); 224 | } else { 225 | return CellToBoundaryOperator().Operation(h, 226 | result); 227 | } 228 | } 229 | }; 230 | 231 | static void CellToBoundaryWktVarcharFunction(DataChunk &args, 232 | ExpressionState &state, 233 | Vector &result) { 234 | UnaryExecutor::ExecuteString( 236 | args.data[0], result, args.size()); 237 | } 238 | 239 | CreateScalarFunctionInfo H3Functions::GetLatLngToCellFunction() { 240 | return CreateScalarFunctionInfo(ScalarFunction( 241 | "h3_latlng_to_cell", 242 | {LogicalType::DOUBLE, LogicalType::DOUBLE, LogicalType::INTEGER}, 243 | LogicalType::UBIGINT, LatLngToCellFunction)); 244 | } 245 | 246 | CreateScalarFunctionInfo H3Functions::GetLatLngToCellVarcharFunction() { 247 | return CreateScalarFunctionInfo(ScalarFunction( 248 | "h3_latlng_to_cell_string", 249 | {LogicalType::DOUBLE, LogicalType::DOUBLE, LogicalType::INTEGER}, 250 | LogicalType::VARCHAR, LatLngToCellVarcharFunction)); 251 | } 252 | 253 | CreateScalarFunctionInfo H3Functions::GetCellToLatFunction() { 254 | ScalarFunctionSet funcs("h3_cell_to_lat"); 255 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::DOUBLE, 256 | CellToLatVarcharFunction)); 257 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::DOUBLE, 258 | CellToLatFunction)); 259 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::DOUBLE, 260 | CellToLatFunction)); 261 | return CreateScalarFunctionInfo(funcs); 262 | } 263 | 264 | CreateScalarFunctionInfo H3Functions::GetCellToLngFunction() { 265 | ScalarFunctionSet funcs("h3_cell_to_lng"); 266 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::DOUBLE, 267 | CellToLngVarcharFunction)); 268 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::DOUBLE, 269 | CellToLngFunction)); 270 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::DOUBLE, 271 | CellToLngFunction)); 272 | return CreateScalarFunctionInfo(funcs); 273 | } 274 | 275 | CreateScalarFunctionInfo H3Functions::GetCellToLatLngFunction() { 276 | ScalarFunctionSet funcs("h3_cell_to_latlng"); 277 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, 278 | LogicalType::LIST(LogicalType::DOUBLE), 279 | CellToLatLngVarcharFunction)); 280 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, 281 | LogicalType::LIST(LogicalType::DOUBLE), 282 | CellToLatLngFunction)); 283 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, 284 | LogicalType::LIST(LogicalType::DOUBLE), 285 | CellToLatLngFunction)); 286 | return CreateScalarFunctionInfo(funcs); 287 | } 288 | 289 | CreateScalarFunctionInfo H3Functions::GetCellToBoundaryWktFunction() { 290 | ScalarFunctionSet funcs("h3_cell_to_boundary_wkt"); 291 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::VARCHAR, 292 | CellToBoundaryWktVarcharFunction)); 293 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::VARCHAR, 294 | CellToBoundaryWktFunction)); 295 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::VARCHAR, 296 | CellToBoundaryWktFunction)); 297 | return CreateScalarFunctionInfo(funcs); 298 | } 299 | 300 | } // namespace duckdb 301 | -------------------------------------------------------------------------------- /src/h3_inspection.cpp: -------------------------------------------------------------------------------- 1 | #include "h3_common.hpp" 2 | #include "h3_functions.hpp" 3 | 4 | namespace duckdb { 5 | 6 | static void GetResolutionFunction(DataChunk &args, ExpressionState &state, 7 | Vector &result) { 8 | auto &inputs = args.data[0]; 9 | UnaryExecutor::Execute( 10 | inputs, result, args.size(), 11 | [&](H3Index cell) { return getResolution(cell); }); 12 | } 13 | 14 | static void GetResolutionVarcharFunction(DataChunk &args, 15 | ExpressionState &state, 16 | Vector &result) { 17 | auto &inputs = args.data[0]; 18 | UnaryExecutor::ExecuteWithNulls( 19 | inputs, result, args.size(), 20 | [&](string_t cellAddress, ValidityMask &mask, idx_t idx) { 21 | H3Index cell; 22 | H3Error err0 = stringToH3(cellAddress.GetString().c_str(), &cell); 23 | if (err0) { 24 | mask.SetInvalid(idx); 25 | return 0; 26 | } else { 27 | return getResolution(cell); 28 | } 29 | }); 30 | } 31 | 32 | static void GetBaseCellNumberFunction(DataChunk &args, ExpressionState &state, 33 | Vector &result) { 34 | auto &inputs = args.data[0]; 35 | UnaryExecutor::Execute( 36 | inputs, result, args.size(), 37 | [&](H3Index cell) { return getBaseCellNumber(cell); }); 38 | } 39 | 40 | static void GetBaseCellNumberVarcharFunction(DataChunk &args, 41 | ExpressionState &state, 42 | Vector &result) { 43 | auto &inputs = args.data[0]; 44 | UnaryExecutor::ExecuteWithNulls( 45 | inputs, result, args.size(), 46 | [&](string_t cellAddress, ValidityMask &mask, idx_t idx) { 47 | H3Index cell; 48 | H3Error err0 = stringToH3(cellAddress.GetString().c_str(), &cell); 49 | if (err0) { 50 | mask.SetInvalid(idx); 51 | return 0; 52 | } else { 53 | return getBaseCellNumber(cell); 54 | } 55 | }); 56 | } 57 | 58 | static void StringToH3Function(DataChunk &args, ExpressionState &state, 59 | Vector &result) { 60 | auto &inputs = args.data[0]; 61 | UnaryExecutor::ExecuteWithNulls( 62 | inputs, result, args.size(), 63 | [&](string_t input, ValidityMask &mask, idx_t idx) { 64 | H3Index h; 65 | H3Error err = stringToH3(input.GetString().c_str(), &h); 66 | if (err) { 67 | mask.SetInvalid(idx); 68 | return H3Index(H3_NULL); 69 | } else { 70 | return h; 71 | } 72 | }); 73 | } 74 | 75 | struct H3ToStringOperator { 76 | template 77 | static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) { 78 | auto str = StringUtil::Format("%llx", input); 79 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 80 | return StringVector::AddString(result, strAsStr); 81 | } 82 | }; 83 | 84 | static void H3ToStringFunction(DataChunk &args, ExpressionState &state, 85 | Vector &result) { 86 | UnaryExecutor::ExecuteString( 87 | args.data[0], result, args.size()); 88 | } 89 | 90 | static void IsValidCellVarcharFunction(DataChunk &args, ExpressionState &state, 91 | Vector &result) { 92 | auto &inputs = args.data[0]; 93 | UnaryExecutor::Execute( 94 | inputs, result, args.size(), [&](string_t input) { 95 | H3Index h; 96 | H3Error err = stringToH3(input.GetString().c_str(), &h); 97 | if (err) { 98 | return false; 99 | } 100 | return bool(isValidCell(h)); 101 | }); 102 | } 103 | 104 | static void IsValidCellFunction(DataChunk &args, ExpressionState &state, 105 | Vector &result) { 106 | auto &inputs = args.data[0]; 107 | UnaryExecutor::Execute( 108 | inputs, result, args.size(), 109 | [&](H3Index input) { return bool(isValidCell(input)); }); 110 | } 111 | 112 | static void IsResClassIIIFunction(DataChunk &args, ExpressionState &state, 113 | Vector &result) { 114 | auto &inputs = args.data[0]; 115 | UnaryExecutor::Execute( 116 | inputs, result, args.size(), 117 | [&](uint64_t cell) { return bool(isResClassIII(cell)); }); 118 | } 119 | 120 | static void IsResClassIIIVarcharFunction(DataChunk &args, 121 | ExpressionState &state, 122 | Vector &result) { 123 | auto &inputs = args.data[0]; 124 | UnaryExecutor::ExecuteWithNulls( 125 | inputs, result, args.size(), 126 | [&](string_t cellAddress, ValidityMask &mask, idx_t idx) { 127 | H3Index cell; 128 | H3Error err0 = stringToH3(cellAddress.GetString().c_str(), &cell); 129 | if (err0) { 130 | mask.SetInvalid(idx); 131 | return false; 132 | } else { 133 | return bool(isResClassIII(cell)); 134 | } 135 | }); 136 | } 137 | 138 | static void IsPentagonFunction(DataChunk &args, ExpressionState &state, 139 | Vector &result) { 140 | auto &inputs = args.data[0]; 141 | UnaryExecutor::Execute( 142 | inputs, result, args.size(), 143 | [&](uint64_t cell) { return bool(isPentagon(cell)); }); 144 | } 145 | 146 | static void IsPentagonVarcharFunction(DataChunk &args, ExpressionState &state, 147 | Vector &result) { 148 | auto &inputs = args.data[0]; 149 | UnaryExecutor::ExecuteWithNulls( 150 | inputs, result, args.size(), 151 | [&](string_t cellAddress, ValidityMask &mask, idx_t idx) { 152 | H3Index cell; 153 | H3Error err0 = stringToH3(cellAddress.GetString().c_str(), &cell); 154 | if (err0) { 155 | mask.SetInvalid(idx); 156 | return false; 157 | } else { 158 | return bool(isPentagon(cell)); 159 | } 160 | }); 161 | } 162 | 163 | static void GetIcosahedronFacesFunction(DataChunk &args, ExpressionState &state, 164 | Vector &result) { 165 | auto result_data = FlatVector::GetData(result); 166 | for (idx_t i = 0; i < args.size(); i++) { 167 | result_data[i].offset = ListVector::GetListSize(result); 168 | 169 | uint64_t cell = args.GetValue(0, i) 170 | .DefaultCastAs(LogicalType::UBIGINT) 171 | .GetValue(); 172 | int faceCount; 173 | int64_t actual = 0; 174 | H3Error err1 = maxFaceCount(cell, &faceCount); 175 | if (err1) { 176 | result.SetValue(i, Value(LogicalType::SQLNULL)); 177 | } else { 178 | std::vector out(faceCount); 179 | H3Error err2 = getIcosahedronFaces(cell, out.data()); 180 | if (err2) { 181 | result.SetValue(i, Value(LogicalType::SQLNULL)); 182 | } else { 183 | for (auto val : out) { 184 | if (val != -1) { 185 | ListVector::PushBack(result, Value::INTEGER(val)); 186 | actual++; 187 | } 188 | } 189 | } 190 | } 191 | 192 | result_data[i].length = actual; 193 | } 194 | result.Verify(args.size()); 195 | } 196 | 197 | static void GetIcosahedronFacesVarcharFunction(DataChunk &args, 198 | ExpressionState &state, 199 | Vector &result) { 200 | UnifiedVectorFormat vdata; 201 | args.data[0].ToUnifiedFormat(args.size(), vdata); 202 | 203 | auto ldata = UnifiedVectorFormat::GetData(vdata); 204 | 205 | result.SetVectorType(VectorType::FLAT_VECTOR); 206 | auto result_data = FlatVector::GetData(result); 207 | for (idx_t i = 0; i < args.size(); i++) { 208 | result_data[i].offset = ListVector::GetListSize(result); 209 | 210 | int faceCount; 211 | int64_t actual = 0; 212 | string_t cellAddress = ldata[i]; 213 | H3Index cell; 214 | H3Error err0 = stringToH3(cellAddress.GetString().c_str(), &cell); 215 | if (err0) { 216 | result.SetValue(i, Value(LogicalType::SQLNULL)); 217 | } else { 218 | H3Error err1 = maxFaceCount(cell, &faceCount); 219 | if (err1) { 220 | result.SetValue(i, Value(LogicalType::SQLNULL)); 221 | } else { 222 | std::vector out(faceCount); 223 | H3Error err2 = getIcosahedronFaces(cell, out.data()); 224 | if (err2) { 225 | result.SetValue(i, Value(LogicalType::SQLNULL)); 226 | } else { 227 | for (auto val : out) { 228 | if (val != -1) { 229 | ListVector::PushBack(result, Value::INTEGER(val)); 230 | actual++; 231 | } 232 | } 233 | } 234 | } 235 | } 236 | 237 | result_data[i].length = actual; 238 | } 239 | result.Verify(args.size()); 240 | } 241 | 242 | CreateScalarFunctionInfo H3Functions::GetGetResolutionFunction() { 243 | ScalarFunctionSet funcs("h3_get_resolution"); 244 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::INTEGER, 245 | GetResolutionFunction)); 246 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::INTEGER, 247 | GetResolutionFunction)); 248 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::INTEGER, 249 | GetResolutionVarcharFunction)); 250 | return CreateScalarFunctionInfo(funcs); 251 | } 252 | 253 | CreateScalarFunctionInfo H3Functions::GetGetBaseCellNumberFunction() { 254 | ScalarFunctionSet funcs("h3_get_base_cell_number"); 255 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::INTEGER, 256 | GetBaseCellNumberFunction)); 257 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::INTEGER, 258 | GetBaseCellNumberFunction)); 259 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::INTEGER, 260 | GetBaseCellNumberFunction)); 261 | return CreateScalarFunctionInfo(funcs); 262 | } 263 | 264 | CreateScalarFunctionInfo H3Functions::GetStringToH3Function() { 265 | return CreateScalarFunctionInfo( 266 | ScalarFunction("h3_string_to_h3", {LogicalType::VARCHAR}, 267 | LogicalType::UBIGINT, StringToH3Function)); 268 | } 269 | 270 | CreateScalarFunctionInfo H3Functions::GetH3ToStringFunction() { 271 | ScalarFunctionSet funcs("h3_h3_to_string"); 272 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::VARCHAR, 273 | H3ToStringFunction)); 274 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::VARCHAR, 275 | H3ToStringFunction)); 276 | return CreateScalarFunctionInfo(funcs); 277 | } 278 | 279 | CreateScalarFunctionInfo H3Functions::GetIsValidCellFunctions() { 280 | ScalarFunctionSet funcs("h3_is_valid_cell"); 281 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::BOOLEAN, 282 | IsValidCellVarcharFunction)); 283 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::BOOLEAN, 284 | IsValidCellFunction)); 285 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::BOOLEAN, 286 | IsValidCellFunction)); 287 | return CreateScalarFunctionInfo(funcs); 288 | } 289 | 290 | CreateScalarFunctionInfo H3Functions::GetIsResClassIIIFunction() { 291 | ScalarFunctionSet funcs("h3_is_res_class_iii"); 292 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::BOOLEAN, 293 | IsResClassIIIFunction)); 294 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::BOOLEAN, 295 | IsResClassIIIFunction)); 296 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::BOOLEAN, 297 | IsResClassIIIFunction)); 298 | return CreateScalarFunctionInfo(funcs); 299 | } 300 | 301 | CreateScalarFunctionInfo H3Functions::GetIsPentagonFunction() { 302 | ScalarFunctionSet funcs("h3_is_pentagon"); 303 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::BOOLEAN, 304 | IsPentagonFunction)); 305 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::BOOLEAN, 306 | IsPentagonFunction)); 307 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::BOOLEAN, 308 | IsPentagonFunction)); 309 | return CreateScalarFunctionInfo(funcs); 310 | } 311 | 312 | CreateScalarFunctionInfo H3Functions::GetGetIcosahedronFacesFunction() { 313 | ScalarFunctionSet funcs("h3_get_icosahedron_faces"); 314 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, 315 | LogicalType::LIST(LogicalType::INTEGER), 316 | GetIcosahedronFacesFunction)); 317 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, 318 | LogicalType::LIST(LogicalType::INTEGER), 319 | GetIcosahedronFacesFunction)); 320 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, 321 | LogicalType::LIST(LogicalType::INTEGER), 322 | GetIcosahedronFacesVarcharFunction)); 323 | return CreateScalarFunctionInfo(funcs); 324 | } 325 | 326 | } // namespace duckdb 327 | -------------------------------------------------------------------------------- /src/h3_misc.cpp: -------------------------------------------------------------------------------- 1 | #include "h3_common.hpp" 2 | #include "h3_functions.hpp" 3 | 4 | namespace duckdb { 5 | 6 | // TODO: Consider using enums for (km, m, rads) here, instead of VARCHAR 7 | // (string) 8 | 9 | static void GetHexagonAreaAvgFunction(DataChunk &args, ExpressionState &state, 10 | Vector &result) { 11 | auto &inputs = args.data[0]; 12 | auto &inputs2 = args.data[1]; 13 | BinaryExecutor::ExecuteWithNulls( 14 | inputs, inputs2, result, args.size(), 15 | [&](int res, string_t unit, ValidityMask &mask, idx_t idx) { 16 | double out; 17 | H3Error err = E_OPTION_INVALID; 18 | if (unit == "km^2") { 19 | err = getHexagonAreaAvgKm2(res, &out); 20 | } else if (unit == "m^2") { 21 | err = getHexagonAreaAvgM2(res, &out); 22 | } 23 | if (err) { 24 | mask.SetInvalid(idx); 25 | return 0.0; 26 | } else { 27 | return out; 28 | } 29 | }); 30 | } 31 | 32 | static double CellAreaFunctionInternal(H3Index cell, string_t unit, 33 | ValidityMask &mask, idx_t idx) { 34 | double out; 35 | H3Error err = E_OPTION_INVALID; 36 | if (unit == "rads^2") { 37 | err = cellAreaRads2(cell, &out); 38 | } else if (unit == "km^2") { 39 | err = cellAreaKm2(cell, &out); 40 | } else if (unit == "m^2") { 41 | err = cellAreaM2(cell, &out); 42 | } 43 | if (err) { 44 | mask.SetInvalid(idx); 45 | return 0.0; 46 | } else { 47 | return out; 48 | } 49 | } 50 | 51 | static void CellAreaVarcharFunction(DataChunk &args, ExpressionState &state, 52 | Vector &result) { 53 | auto &inputs = args.data[0]; 54 | auto &inputs2 = args.data[1]; 55 | BinaryExecutor::ExecuteWithNulls( 56 | inputs, inputs2, result, args.size(), 57 | [&](string_t cell, string_t unit, ValidityMask &mask, idx_t idx) { 58 | H3Index h; 59 | H3Error err = stringToH3(cell.GetString().c_str(), &h); 60 | if (err) { 61 | mask.SetInvalid(idx); 62 | return 0.0; 63 | } 64 | return CellAreaFunctionInternal(h, unit, mask, idx); 65 | }); 66 | } 67 | 68 | static void CellAreaFunction(DataChunk &args, ExpressionState &state, 69 | Vector &result) { 70 | auto &inputs = args.data[0]; 71 | auto &inputs2 = args.data[1]; 72 | BinaryExecutor::ExecuteWithNulls( 73 | inputs, inputs2, result, args.size(), CellAreaFunctionInternal); 74 | } 75 | 76 | static void GetHexagonEdgeLengthAvgFunction(DataChunk &args, 77 | ExpressionState &state, 78 | Vector &result) { 79 | auto &inputs = args.data[0]; 80 | auto &inputs2 = args.data[1]; 81 | BinaryExecutor::ExecuteWithNulls( 82 | inputs, inputs2, result, args.size(), 83 | [&](int res, string_t unit, ValidityMask &mask, idx_t idx) { 84 | double out; 85 | H3Error err = E_OPTION_INVALID; 86 | if (unit == "km") { 87 | err = getHexagonEdgeLengthAvgKm(res, &out); 88 | } else if (unit == "m") { 89 | err = getHexagonEdgeLengthAvgM(res, &out); 90 | } 91 | if (err) { 92 | mask.SetInvalid(idx); 93 | return 0.0; 94 | } else { 95 | return out; 96 | } 97 | }); 98 | } 99 | 100 | static double EdgeLengthFunctionInternal(H3Index edge, string_t unit, 101 | ValidityMask &mask, idx_t idx) { 102 | double out; 103 | H3Error err = E_OPTION_INVALID; 104 | if (unit == "rads") { 105 | err = edgeLengthRads(edge, &out); 106 | } else if (unit == "km") { 107 | err = edgeLengthKm(edge, &out); 108 | } else if (unit == "m") { 109 | err = edgeLengthM(edge, &out); 110 | } 111 | if (err) { 112 | mask.SetInvalid(idx); 113 | return 0.0; 114 | } else { 115 | return out; 116 | } 117 | } 118 | 119 | static void EdgeLengthVarcharFunction(DataChunk &args, ExpressionState &state, 120 | Vector &result) { 121 | auto &inputs = args.data[0]; 122 | auto &inputs2 = args.data[1]; 123 | BinaryExecutor::ExecuteWithNulls( 124 | inputs, inputs2, result, args.size(), 125 | [&](string_t edge, string_t unit, ValidityMask &mask, idx_t idx) { 126 | H3Index h; 127 | H3Error err = stringToH3(edge.GetString().c_str(), &h); 128 | if (err) { 129 | mask.SetInvalid(idx); 130 | return 0.0; 131 | } 132 | return EdgeLengthFunctionInternal(h, unit, mask, idx); 133 | }); 134 | } 135 | 136 | static void EdgeLengthFunction(DataChunk &args, ExpressionState &state, 137 | Vector &result) { 138 | auto &inputs = args.data[0]; 139 | auto &inputs2 = args.data[1]; 140 | BinaryExecutor::ExecuteWithNulls( 141 | inputs, inputs2, result, args.size(), EdgeLengthFunctionInternal); 142 | } 143 | 144 | static void GetNumCellsFunction(DataChunk &args, ExpressionState &state, 145 | Vector &result) { 146 | auto &inputs = args.data[0]; 147 | UnaryExecutor::ExecuteWithNulls( 148 | inputs, result, args.size(), [&](int res, ValidityMask &mask, idx_t idx) { 149 | int64_t out; 150 | H3Error err = getNumCells(res, &out); 151 | if (err) { 152 | mask.SetInvalid(idx); 153 | return int64_t(0); 154 | } 155 | return out; 156 | }); 157 | } 158 | 159 | static void GetRes0CellsFunction(DataChunk &args, ExpressionState &state, 160 | Vector &result) { 161 | auto result_data = FlatVector::GetData(result); 162 | 163 | int sz = res0CellCount(); 164 | 165 | for (idx_t i = 0; i < args.size(); i++) { 166 | result_data[i].offset = ListVector::GetListSize(result); 167 | 168 | std::vector out(sz); 169 | H3Error err1 = getRes0Cells(out.data()); 170 | if (err1) { 171 | // This should be unreachable 172 | result.SetValue(i, Value(LogicalType::SQLNULL)); 173 | } else { 174 | int64_t actual = 0; 175 | for (auto val : out) { 176 | if (val != H3_NULL) { 177 | ListVector::PushBack(result, Value::UBIGINT(val)); 178 | actual++; 179 | } 180 | } 181 | // actual should always be 122 182 | 183 | result_data[i].length = actual; 184 | } 185 | } 186 | result.Verify(args.size()); 187 | result.SetVectorType(VectorType::CONSTANT_VECTOR); 188 | } 189 | 190 | static void GetRes0CellsVarcharFunction(DataChunk &args, ExpressionState &state, 191 | Vector &result) { 192 | auto result_data = FlatVector::GetData(result); 193 | 194 | int sz = res0CellCount(); 195 | 196 | for (idx_t i = 0; i < args.size(); i++) { 197 | result_data[i].offset = ListVector::GetListSize(result); 198 | 199 | std::vector out(sz); 200 | H3Error err1 = getRes0Cells(out.data()); 201 | if (err1) { 202 | // This should be unreachable 203 | result.SetValue(i, Value(LogicalType::SQLNULL)); 204 | } else { 205 | int64_t actual = 0; 206 | for (auto val : out) { 207 | if (val != H3_NULL) { 208 | auto str = StringUtil::Format("%llx", val); 209 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 210 | ListVector::PushBack(result, strAsStr); 211 | actual++; 212 | } 213 | } 214 | // actual should always be 122 215 | 216 | result_data[i].length = actual; 217 | } 218 | } 219 | result.Verify(args.size()); 220 | result.SetVectorType(VectorType::CONSTANT_VECTOR); 221 | } 222 | 223 | static void GetPentagonsFunction(DataChunk &args, ExpressionState &state, 224 | Vector &result) { 225 | auto result_data = FlatVector::GetData(result); 226 | 227 | int sz = pentagonCount(); 228 | 229 | for (idx_t i = 0; i < args.size(); i++) { 230 | result_data[i].offset = ListVector::GetListSize(result); 231 | 232 | int32_t res = args.GetValue(0, i) 233 | .DefaultCastAs(LogicalType::INTEGER) 234 | .GetValue(); 235 | 236 | std::vector out(sz); 237 | H3Error err1 = getPentagons(res, out.data()); 238 | if (err1) { 239 | result.SetValue(i, Value(LogicalType::SQLNULL)); 240 | } else { 241 | int64_t actual = 0; 242 | for (auto val : out) { 243 | if (val != H3_NULL) { 244 | ListVector::PushBack(result, Value::UBIGINT(val)); 245 | actual++; 246 | } 247 | } 248 | // actual should always be 12 249 | 250 | result_data[i].length = actual; 251 | } 252 | } 253 | result.Verify(args.size()); 254 | } 255 | 256 | static void GetPentagonsVarcharFunction(DataChunk &args, ExpressionState &state, 257 | Vector &result) { 258 | auto result_data = FlatVector::GetData(result); 259 | 260 | int sz = pentagonCount(); 261 | 262 | for (idx_t i = 0; i < args.size(); i++) { 263 | result_data[i].offset = ListVector::GetListSize(result); 264 | 265 | int32_t res = args.GetValue(0, i) 266 | .DefaultCastAs(LogicalType::INTEGER) 267 | .GetValue(); 268 | 269 | std::vector out(sz); 270 | H3Error err1 = getPentagons(res, out.data()); 271 | if (err1) { 272 | result.SetValue(i, Value(LogicalType::SQLNULL)); 273 | } else { 274 | int64_t actual = 0; 275 | for (auto val : out) { 276 | if (val != H3_NULL) { 277 | auto str = StringUtil::Format("%llx", val); 278 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 279 | ListVector::PushBack(result, strAsStr); 280 | actual++; 281 | } 282 | } 283 | // actual should always be 12 284 | 285 | result_data[i].length = actual; 286 | } 287 | } 288 | result.Verify(args.size()); 289 | } 290 | 291 | static void GreatCircleDistanceFunction(DataChunk &args, ExpressionState &state, 292 | Vector &result) { 293 | auto result_data = FlatVector::GetData(result); 294 | 295 | UnifiedVectorFormat unitData; 296 | 297 | args.data[4].ToUnifiedFormat(args.size(), unitData); 298 | 299 | for (idx_t i = 0; i < args.size(); i++) { 300 | double dist = 0.0; 301 | bool isValid = true; 302 | 303 | if (unitData.validity.RowIsValid(i)) { 304 | double lat0 = args.GetValue(0, i) 305 | .DefaultCastAs(LogicalType::DOUBLE) 306 | .GetValue(); 307 | double lng0 = args.GetValue(1, i) 308 | .DefaultCastAs(LogicalType::DOUBLE) 309 | .GetValue(); 310 | double lat1 = args.GetValue(2, i) 311 | .DefaultCastAs(LogicalType::DOUBLE) 312 | .GetValue(); 313 | double lng1 = args.GetValue(3, i) 314 | .DefaultCastAs(LogicalType::DOUBLE) 315 | .GetValue(); 316 | string_t unit = args.GetValue(4, i).ToString(); 317 | 318 | LatLng latLng0 = {.lat = degsToRads(lat0), .lng = degsToRads(lng0)}; 319 | LatLng latLng1 = {.lat = degsToRads(lat1), .lng = degsToRads(lng1)}; 320 | 321 | if (unit == "rads") { 322 | dist = greatCircleDistanceRads(&latLng0, &latLng1); 323 | } else if (unit == "km") { 324 | dist = greatCircleDistanceKm(&latLng0, &latLng1); 325 | } else if (unit == "m") { 326 | dist = greatCircleDistanceM(&latLng0, &latLng1); 327 | } else { 328 | isValid = false; 329 | } 330 | } else { 331 | isValid = false; 332 | } 333 | 334 | if (isValid) { 335 | result.SetValue(i, Value(dist)); 336 | } else { 337 | result.SetValue(i, Value(LogicalType::SQLNULL)); 338 | } 339 | } 340 | } 341 | 342 | CreateScalarFunctionInfo H3Functions::GetGetHexagonAreaAvgFunction() { 343 | return CreateScalarFunctionInfo(ScalarFunction( 344 | "h3_get_hexagon_area_avg", {LogicalType::INTEGER, LogicalType::VARCHAR}, 345 | LogicalType::DOUBLE, GetHexagonAreaAvgFunction)); 346 | } 347 | 348 | CreateScalarFunctionInfo H3Functions::GetCellAreaFunction() { 349 | ScalarFunctionSet funcs("h3_cell_area"); 350 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR, LogicalType::VARCHAR}, 351 | LogicalType::DOUBLE, 352 | CellAreaVarcharFunction)); 353 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT, LogicalType::VARCHAR}, 354 | LogicalType::DOUBLE, CellAreaFunction)); 355 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT, LogicalType::VARCHAR}, 356 | LogicalType::DOUBLE, CellAreaFunction)); 357 | return CreateScalarFunctionInfo(funcs); 358 | } 359 | 360 | CreateScalarFunctionInfo H3Functions::GetGetHexagonEdgeLengthAvgFunction() { 361 | return CreateScalarFunctionInfo( 362 | ScalarFunction("h3_get_hexagon_edge_length_avg", 363 | {LogicalType::INTEGER, LogicalType::VARCHAR}, 364 | LogicalType::DOUBLE, GetHexagonEdgeLengthAvgFunction)); 365 | } 366 | 367 | CreateScalarFunctionInfo H3Functions::GetEdgeLengthFunction() { 368 | ScalarFunctionSet funcs("h3_edge_length"); 369 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR, LogicalType::VARCHAR}, 370 | LogicalType::DOUBLE, 371 | EdgeLengthVarcharFunction)); 372 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT, LogicalType::VARCHAR}, 373 | LogicalType::DOUBLE, EdgeLengthFunction)); 374 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT, LogicalType::VARCHAR}, 375 | LogicalType::DOUBLE, EdgeLengthFunction)); 376 | return CreateScalarFunctionInfo(funcs); 377 | } 378 | 379 | CreateScalarFunctionInfo H3Functions::GetGetNumCellsFunction() { 380 | return CreateScalarFunctionInfo( 381 | ScalarFunction("h3_get_num_cells", {LogicalType::INTEGER}, 382 | LogicalType::BIGINT, GetNumCellsFunction)); 383 | } 384 | 385 | CreateScalarFunctionInfo H3Functions::GetGetRes0CellsFunction() { 386 | return CreateScalarFunctionInfo(ScalarFunction( 387 | "h3_get_res0_cells", {}, LogicalType::LIST(LogicalType::UBIGINT), 388 | GetRes0CellsFunction)); 389 | } 390 | 391 | CreateScalarFunctionInfo H3Functions::GetGetRes0CellsVarcharFunction() { 392 | return CreateScalarFunctionInfo(ScalarFunction( 393 | "h3_get_res0_cells_string", {}, LogicalType::LIST(LogicalType::VARCHAR), 394 | GetRes0CellsVarcharFunction)); 395 | } 396 | 397 | CreateScalarFunctionInfo H3Functions::GetGetPentagonsFunction() { 398 | return CreateScalarFunctionInfo(ScalarFunction( 399 | "h3_get_pentagons", {LogicalType::INTEGER}, 400 | LogicalType::LIST(LogicalType::UBIGINT), GetPentagonsFunction)); 401 | } 402 | 403 | CreateScalarFunctionInfo H3Functions::GetGetPentagonsVarcharFunction() { 404 | return CreateScalarFunctionInfo(ScalarFunction( 405 | "h3_get_pentagons_string", {LogicalType::INTEGER}, 406 | LogicalType::LIST(LogicalType::VARCHAR), GetPentagonsVarcharFunction)); 407 | } 408 | 409 | CreateScalarFunctionInfo H3Functions::GetGreatCircleDistanceFunction() { 410 | return CreateScalarFunctionInfo(ScalarFunction( 411 | "h3_great_circle_distance", 412 | {LogicalType::DOUBLE, LogicalType::DOUBLE, LogicalType::DOUBLE, 413 | LogicalType::DOUBLE, LogicalType::VARCHAR}, 414 | LogicalType::DOUBLE, GreatCircleDistanceFunction)); 415 | } 416 | 417 | } // namespace duckdb 418 | -------------------------------------------------------------------------------- /src/h3_regions.cpp: -------------------------------------------------------------------------------- 1 | #include "h3_common.hpp" 2 | #include "h3_functions.hpp" 3 | 4 | #include "duckdb/common/helper.hpp" 5 | 6 | namespace duckdb { 7 | 8 | static const std::string POLYGON = "POLYGON"; 9 | static const std::string EMPTY = "EMPTY"; 10 | 11 | // TODO: For convenience, 0 is returned instead of throwing. However, this may 12 | // actually be interpreted by cellsToMultiPolygon as the index referring to base 13 | // cell 0. 14 | struct CellsToMultiPolygonWktInputOperator { 15 | static H3Index Get(const UnifiedVectorFormat &child_data, 16 | const size_t offset) { 17 | return ((H3Index *)child_data.data)[child_data.sel->get_index(offset)]; 18 | } 19 | }; 20 | 21 | struct CellsToMultiPolygonWktVarcharInputOperator { 22 | static H3Index Get(const UnifiedVectorFormat &child_data, 23 | const size_t offset) { 24 | auto str = ((string_t *)child_data.data)[child_data.sel->get_index(offset)] 25 | .GetString(); 26 | H3Index cell; 27 | H3Error err = stringToH3(str.c_str(), &cell); 28 | if (err) { 29 | return 0; 30 | } else { 31 | return cell; 32 | } 33 | } 34 | }; 35 | 36 | template 37 | static void CellsToMultiPolygonWktFunction(DataChunk &args, 38 | ExpressionState &state, 39 | Vector &result) { 40 | D_ASSERT(args.ColumnCount() == 1); 41 | auto count = args.size(); 42 | 43 | Vector &lhs = args.data[0]; 44 | if (lhs.GetType().id() == LogicalTypeId::SQLNULL) { 45 | result.Reference(lhs); 46 | return; 47 | } 48 | 49 | auto lists_size = ListVector::GetListSize(lhs); 50 | auto &child_vector = ListVector::GetEntry(lhs); 51 | child_vector.Flatten(lists_size); 52 | 53 | UnifiedVectorFormat child_data; 54 | child_vector.ToUnifiedFormat(lists_size, child_data); 55 | 56 | UnifiedVectorFormat lists_data; 57 | lhs.ToUnifiedFormat(count, lists_data); 58 | auto list_entries = UnifiedVectorFormat::GetData(lists_data); 59 | 60 | result.SetVectorType(VectorType::FLAT_VECTOR); 61 | auto result_entries = FlatVector::GetData(result); 62 | auto &result_validity = FlatVector::Validity(result); 63 | 64 | idx_t offset = 0; 65 | for (idx_t i = 0; i < count; i++) { 66 | result_entries[i].offset = offset; 67 | result_entries[i].length = 0; 68 | auto list_index = lists_data.sel->get_index(i); 69 | 70 | if (!lists_data.validity.RowIsValid(list_index)) { 71 | result_validity.SetInvalid(i); 72 | continue; 73 | } 74 | 75 | vector input_set(list_entries[i].length); 76 | for (size_t j = 0; j < list_entries[i].length; j++) { 77 | if (child_data.validity.RowIsValid( 78 | child_data.sel->get_index(list_entries[i].offset + j))) { 79 | input_set[j] = 80 | InputOperator::Get(child_data, list_entries[i].offset + j); 81 | } 82 | } 83 | LinkedGeoPolygon first_lgp; 84 | H3Error err = cellsToLinkedMultiPolygon(input_set.data(), input_set.size(), 85 | &first_lgp); 86 | 87 | if (err) { 88 | result_validity.SetInvalid(i); 89 | } else { 90 | std::string str = "MULTIPOLYGON "; 91 | 92 | if (first_lgp.first) { 93 | str += "("; 94 | std::string lgp_sep = ""; 95 | LinkedGeoPolygon *lgp = &first_lgp; 96 | while (lgp) { 97 | LinkedGeoLoop *loop = lgp->first; 98 | std::string loop_sep = ""; 99 | str += lgp_sep + "("; 100 | while (loop) { 101 | LinkedLatLng *lat_lng = loop->first; 102 | std::string lat_lng_sep = ""; 103 | str += loop_sep + "("; 104 | while (lat_lng) { 105 | str += StringUtil::Format("%s%f %f", lat_lng_sep, 106 | radsToDegs(lat_lng->vertex.lng), 107 | radsToDegs(lat_lng->vertex.lat)); 108 | 109 | lat_lng_sep = ", "; 110 | lat_lng = lat_lng->next; 111 | } 112 | 113 | if (loop->first) { 114 | // Duplicate first vertex, to close the polygon 115 | str += StringUtil::Format(", %f %f", 116 | radsToDegs(loop->first->vertex.lng), 117 | radsToDegs(loop->first->vertex.lat)); 118 | } 119 | 120 | str += ")"; 121 | loop_sep = ", "; 122 | loop = loop->next; 123 | } 124 | str += ")"; 125 | lgp_sep = ", "; 126 | lgp = lgp->next; 127 | } 128 | str += ")"; 129 | } else { 130 | str += "EMPTY"; 131 | } 132 | 133 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 134 | result.SetValue(i, StringVector::AddString(result, strAsStr)); 135 | 136 | destroyLinkedMultiPolygon(&first_lgp); 137 | } 138 | } 139 | 140 | result.Verify(args.size()); 141 | 142 | if (lhs.GetVectorType() == VectorType::CONSTANT_VECTOR) { 143 | result.SetVectorType(VectorType::CONSTANT_VECTOR); 144 | } 145 | } 146 | 147 | static size_t whitespace(const std::string &str, size_t offset) { 148 | while (str[offset] == ' ') { 149 | offset++; 150 | } 151 | return offset; 152 | } 153 | 154 | static size_t readNumber(const std::string &str, size_t offset, double &num) { 155 | size_t start = offset; 156 | while (str[offset] != ' ' && str[offset] != ')' && str[offset] != ',') { 157 | offset++; 158 | } 159 | std::string part = str.substr(start, offset - start); 160 | 161 | try { 162 | num = std::stod(part); 163 | return offset; 164 | } catch (std::invalid_argument const &ex) { 165 | throw InvalidInputException( 166 | StringUtil::Format("Invalid number around %lu, %lu", start, offset)); 167 | } 168 | } 169 | 170 | static size_t readGeoLoop(const std::string &str, size_t offset, 171 | duckdb::shared_ptr> verts, 172 | GeoLoop &loop) { 173 | if (str[offset] != '(') { 174 | throw InvalidInputException( 175 | StringUtil::Format("Expected ( at pos %lu", offset)); 176 | } 177 | 178 | offset++; 179 | offset = whitespace(str, offset); 180 | 181 | while (str[offset] != ')') { 182 | double x, y; 183 | offset = readNumber(str, offset, x); 184 | offset = whitespace(str, offset); 185 | offset = readNumber(str, offset, y); 186 | offset = whitespace(str, offset); 187 | verts->push_back({.lat = degsToRads(y), .lng = degsToRads(x)}); 188 | 189 | if (str[offset] == ',') { 190 | offset++; 191 | offset = whitespace(str, offset); 192 | } 193 | } 194 | // Consume the ) 195 | offset++; 196 | 197 | loop.numVerts = verts->size(); 198 | loop.verts = verts->data(); 199 | 200 | offset = whitespace(str, offset); 201 | return offset; 202 | } 203 | 204 | static void PolygonWktToCellsFunction(DataChunk &args, ExpressionState &state, 205 | Vector &result) { 206 | // TODO: Note this function is not fully noexcept -- some invalid WKT strings 207 | // will throw, others will return empty lists. 208 | BinaryExecutor::Execute( 209 | args.data[0], args.data[1], result, args.size(), 210 | [&](string_t input, int res) { 211 | GeoPolygon polygon; 212 | int32_t flags = 0; 213 | 214 | std::string str = input.GetString(); 215 | 216 | uint64_t offset = ListVector::GetListSize(result); 217 | if (str.rfind(POLYGON, 0) != 0) { 218 | return list_entry_t(offset, 0); 219 | } 220 | 221 | size_t strIndex = POLYGON.length(); 222 | strIndex = whitespace(str, strIndex); 223 | 224 | if (str.rfind(EMPTY, strIndex) == strIndex) { 225 | return list_entry_t(offset, 0); 226 | } 227 | 228 | if (str[strIndex] == '(') { 229 | strIndex++; 230 | strIndex = whitespace(str, strIndex); 231 | 232 | auto outerVerts = duckdb::make_shared_ptr>(); 233 | strIndex = readGeoLoop(str, strIndex, outerVerts, polygon.geoloop); 234 | 235 | std::vector holes; 236 | std::vector>> holesVerts; 237 | while (strIndex < str.length() && str[strIndex] == ',') { 238 | strIndex++; 239 | strIndex = whitespace(str, strIndex); 240 | if (str[strIndex] == '(') { 241 | GeoLoop hole; 242 | auto verts = duckdb::make_shared_ptr>(); 243 | strIndex = readGeoLoop(str, strIndex, verts, hole); 244 | holes.push_back(hole); 245 | holesVerts.push_back(verts); 246 | } else { 247 | throw InvalidInputException(StringUtil::Format( 248 | "Invalid WKT: expected a hole loop '(' after ',' at pos %lu", 249 | strIndex)); 250 | } 251 | } 252 | if (str[strIndex] != ')') { 253 | throw InvalidInputException(StringUtil::Format( 254 | "Invalid WKT: expected a hole loop ',' or final ')' at pos %lu", 255 | strIndex)); 256 | } 257 | 258 | polygon.numHoles = holes.size(); 259 | polygon.holes = holes.data(); 260 | 261 | int64_t numCells = 0; 262 | H3Error err = maxPolygonToCellsSize(&polygon, res, flags, &numCells); 263 | if (err) { 264 | return list_entry_t(offset, 0); 265 | } else { 266 | std::vector out(numCells); 267 | H3Error err2 = polygonToCells(&polygon, res, flags, out.data()); 268 | if (err2) { 269 | return list_entry_t(offset, 0); 270 | } else { 271 | uint64_t actual = 0; 272 | for (H3Index outCell : out) { 273 | if (outCell != H3_NULL) { 274 | ListVector::PushBack(result, Value::UBIGINT(outCell)); 275 | actual++; 276 | } 277 | } 278 | return list_entry_t(offset, actual); 279 | } 280 | } 281 | } 282 | return list_entry_t(offset, 0); 283 | }); 284 | } 285 | 286 | static void PolygonWktToCellsVarcharFunction(DataChunk &args, 287 | ExpressionState &state, 288 | Vector &result) { 289 | // TODO: Note this function is not fully noexcept -- some invalid WKT strings 290 | // will throw, others will return empty lists. 291 | BinaryExecutor::Execute( 292 | args.data[0], args.data[1], result, args.size(), 293 | [&](string_t input, int res) { 294 | GeoPolygon polygon; 295 | int32_t flags = 0; 296 | 297 | std::string str = input.GetString(); 298 | 299 | uint64_t offset = ListVector::GetListSize(result); 300 | if (str.rfind(POLYGON, 0) != 0) { 301 | return list_entry_t(offset, 0); 302 | } 303 | 304 | size_t strIndex = POLYGON.length(); 305 | strIndex = whitespace(str, strIndex); 306 | 307 | if (str.rfind(EMPTY, strIndex) == strIndex) { 308 | return list_entry_t(offset, 0); 309 | } 310 | 311 | if (str[strIndex] == '(') { 312 | strIndex++; 313 | strIndex = whitespace(str, strIndex); 314 | 315 | auto outerVerts = duckdb::make_shared_ptr>(); 316 | strIndex = readGeoLoop(str, strIndex, outerVerts, polygon.geoloop); 317 | 318 | std::vector holes; 319 | std::vector>> holesVerts; 320 | while (strIndex < str.length() && str[strIndex] == ',') { 321 | strIndex++; 322 | strIndex = whitespace(str, strIndex); 323 | if (str[strIndex] == '(') { 324 | GeoLoop hole; 325 | auto verts = duckdb::make_shared_ptr>(); 326 | strIndex = readGeoLoop(str, strIndex, verts, hole); 327 | holes.push_back(hole); 328 | holesVerts.push_back(verts); 329 | } else { 330 | throw InvalidInputException(StringUtil::Format( 331 | "Invalid WKT: expected a hole loop '(' after ',' at pos %lu", 332 | strIndex)); 333 | } 334 | } 335 | if (str[strIndex] != ')') { 336 | throw InvalidInputException(StringUtil::Format( 337 | "Invalid WKT: expected a hole loop ',' or final ')' at pos %lu", 338 | strIndex)); 339 | } 340 | 341 | polygon.numHoles = holes.size(); 342 | polygon.holes = holes.data(); 343 | 344 | int64_t numCells = 0; 345 | H3Error err = maxPolygonToCellsSize(&polygon, res, flags, &numCells); 346 | if (err) { 347 | return list_entry_t(offset, 0); 348 | } else { 349 | std::vector out(numCells); 350 | H3Error err2 = polygonToCells(&polygon, res, flags, out.data()); 351 | if (err2) { 352 | return list_entry_t(offset, 0); 353 | } else { 354 | uint64_t actual = 0; 355 | for (H3Index outCell : out) { 356 | if (outCell != H3_NULL) { 357 | auto str = StringUtil::Format("%llx", outCell); 358 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 359 | ListVector::PushBack(result, strAsStr); 360 | actual++; 361 | } 362 | } 363 | return list_entry_t(offset, actual); 364 | } 365 | } 366 | } 367 | return list_entry_t(offset, 0); 368 | }); 369 | } 370 | 371 | static list_entry_t 372 | PolygonWktToCellsExperimentalInnerFunction(string_t input, int res, 373 | string_t flagsStr, Vector &result) { 374 | // TODO: Note this function is not fully noexcept -- some invalid WKT strings 375 | // will throw, others will return empty lists. 376 | GeoPolygon polygon; 377 | int32_t flags = 0; 378 | 379 | std::string str = input.GetString(); 380 | 381 | uint64_t offset = ListVector::GetListSize(result); 382 | 383 | // TODO: Make flags easier to work with 384 | if (flagsStr == "CONTAINMENT_CENTER" || flagsStr == "center") { 385 | flags = 0; 386 | } else if (flagsStr == "CONTAINMENT_FULL" || flagsStr == "full") { 387 | flags = 1; 388 | } else if (flagsStr == "CONTAINMENT_OVERLAPPING" || flagsStr == "overlap") { 389 | flags = 2; 390 | } else if (flagsStr == "CONTAINMENT_OVERLAPPING_BBOX" || 391 | flagsStr == "overlap_bbox") { 392 | flags = 3; 393 | } else { 394 | // Invalid flags input 395 | return list_entry_t(offset, 0); 396 | } 397 | 398 | if (str.rfind(POLYGON, 0) != 0) { 399 | return list_entry_t(offset, 0); 400 | } 401 | 402 | size_t strIndex = POLYGON.length(); 403 | strIndex = whitespace(str, strIndex); 404 | 405 | if (str.rfind(EMPTY, strIndex) == strIndex) { 406 | return list_entry_t(offset, 0); 407 | } 408 | 409 | if (str[strIndex] == '(') { 410 | strIndex++; 411 | strIndex = whitespace(str, strIndex); 412 | 413 | auto outerVerts = duckdb::make_shared_ptr>(); 414 | strIndex = readGeoLoop(str, strIndex, outerVerts, polygon.geoloop); 415 | 416 | std::vector holes; 417 | std::vector>> holesVerts; 418 | while (strIndex < str.length() && str[strIndex] == ',') { 419 | strIndex++; 420 | strIndex = whitespace(str, strIndex); 421 | if (str[strIndex] == '(') { 422 | GeoLoop hole; 423 | auto verts = duckdb::make_shared_ptr>(); 424 | strIndex = readGeoLoop(str, strIndex, verts, hole); 425 | holes.push_back(hole); 426 | holesVerts.push_back(verts); 427 | } else { 428 | throw InvalidInputException(StringUtil::Format( 429 | "Invalid WKT: expected a hole loop '(' after ',' at pos %lu", 430 | strIndex)); 431 | } 432 | } 433 | if (str[strIndex] != ')') { 434 | throw InvalidInputException(StringUtil::Format( 435 | "Invalid WKT: expected a hole loop ',' or final ')' at pos %lu", 436 | strIndex)); 437 | } 438 | 439 | polygon.numHoles = holes.size(); 440 | polygon.holes = holes.data(); 441 | 442 | int64_t numCells = 0; 443 | H3Error err = 444 | maxPolygonToCellsSizeExperimental(&polygon, res, flags, &numCells); 445 | if (err) { 446 | return list_entry_t(offset, 0); 447 | } else { 448 | std::vector out(numCells); 449 | H3Error err2 = polygonToCellsExperimental(&polygon, res, flags, numCells, 450 | out.data()); 451 | if (err2) { 452 | return list_entry_t(offset, 0); 453 | } else { 454 | uint64_t actual = 0; 455 | for (H3Index outCell : out) { 456 | if (outCell != H3_NULL) { 457 | ListVector::PushBack(result, Value::UBIGINT(outCell)); 458 | actual++; 459 | } 460 | } 461 | return list_entry_t(offset, actual); 462 | } 463 | } 464 | } 465 | return list_entry_t(offset, 0); 466 | } 467 | 468 | static void PolygonWktToCellsExperimentalFunction(DataChunk &args, 469 | ExpressionState &state, 470 | Vector &result) { 471 | TernaryExecutor::Execute( 472 | args.data[0], args.data[1], args.data[2], result, args.size(), 473 | [&](string_t input, int res, string_t flagsStr) { 474 | return PolygonWktToCellsExperimentalInnerFunction(input, res, flagsStr, 475 | result); 476 | }); 477 | } 478 | 479 | static void PolygonWktToCellsExperimentalFunctionSwapped(DataChunk &args, 480 | ExpressionState &state, 481 | Vector &result) { 482 | TernaryExecutor::Execute( 483 | args.data[0], args.data[1], args.data[2], result, args.size(), 484 | [&](string_t input, string_t flagsStr, int res) { 485 | return PolygonWktToCellsExperimentalInnerFunction(input, res, flagsStr, 486 | result); 487 | }); 488 | } 489 | 490 | static list_entry_t PolygonWktToCellsExperimentalVarcharInnerFunction( 491 | string_t input, int res, string_t flagsStr, Vector &result) { 492 | // TODO: Note this function is not fully noexcept -- some invalid WKT strings 493 | // will throw, others will return empty lists. 494 | GeoPolygon polygon; 495 | int32_t flags = 0; 496 | 497 | std::string str = input.GetString(); 498 | 499 | uint64_t offset = ListVector::GetListSize(result); 500 | 501 | // TODO: Make flags easier to work with 502 | if (flagsStr == "CONTAINMENT_CENTER" || flagsStr == "center") { 503 | flags = 0; 504 | } else if (flagsStr == "CONTAINMENT_FULL" || flagsStr == "full") { 505 | flags = 1; 506 | } else if (flagsStr == "CONTAINMENT_OVERLAPPING" || flagsStr == "overlap") { 507 | flags = 2; 508 | } else if (flagsStr == "CONTAINMENT_OVERLAPPING_BBOX" || 509 | flagsStr == "overlap_bbox") { 510 | flags = 3; 511 | } else { 512 | // Invalid flags input 513 | return list_entry_t(offset, 0); 514 | } 515 | 516 | if (str.rfind(POLYGON, 0) != 0) { 517 | return list_entry_t(offset, 0); 518 | } 519 | 520 | size_t strIndex = POLYGON.length(); 521 | strIndex = whitespace(str, strIndex); 522 | 523 | if (str.rfind(EMPTY, strIndex) == strIndex) { 524 | return list_entry_t(offset, 0); 525 | } 526 | 527 | if (str[strIndex] == '(') { 528 | strIndex++; 529 | strIndex = whitespace(str, strIndex); 530 | 531 | auto outerVerts = duckdb::make_shared_ptr>(); 532 | strIndex = readGeoLoop(str, strIndex, outerVerts, polygon.geoloop); 533 | 534 | std::vector holes; 535 | std::vector>> holesVerts; 536 | while (strIndex < str.length() && str[strIndex] == ',') { 537 | strIndex++; 538 | strIndex = whitespace(str, strIndex); 539 | if (str[strIndex] == '(') { 540 | GeoLoop hole; 541 | auto verts = duckdb::make_shared_ptr>(); 542 | strIndex = readGeoLoop(str, strIndex, verts, hole); 543 | holes.push_back(hole); 544 | holesVerts.push_back(verts); 545 | } else { 546 | throw InvalidInputException(StringUtil::Format( 547 | "Invalid WKT: expected a hole loop '(' after ',' at pos %lu", 548 | strIndex)); 549 | } 550 | } 551 | if (str[strIndex] != ')') { 552 | throw InvalidInputException(StringUtil::Format( 553 | "Invalid WKT: expected a hole loop ',' or final ')' at pos %lu", 554 | strIndex)); 555 | } 556 | 557 | polygon.numHoles = holes.size(); 558 | polygon.holes = holes.data(); 559 | 560 | int64_t numCells = 0; 561 | H3Error err = 562 | maxPolygonToCellsSizeExperimental(&polygon, res, flags, &numCells); 563 | if (err) { 564 | return list_entry_t(offset, 0); 565 | } else { 566 | std::vector out(numCells); 567 | H3Error err2 = polygonToCellsExperimental(&polygon, res, flags, numCells, 568 | out.data()); 569 | if (err2) { 570 | return list_entry_t(offset, 0); 571 | } else { 572 | uint64_t actual = 0; 573 | for (H3Index outCell : out) { 574 | if (outCell != H3_NULL) { 575 | auto str = StringUtil::Format("%llx", outCell); 576 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 577 | ListVector::PushBack(result, strAsStr); 578 | actual++; 579 | } 580 | } 581 | return list_entry_t(offset, actual); 582 | } 583 | } 584 | } 585 | return list_entry_t(offset, 0); 586 | } 587 | 588 | static void PolygonWktToCellsExperimentalVarcharFunction(DataChunk &args, 589 | ExpressionState &state, 590 | Vector &result) { 591 | TernaryExecutor::Execute( 592 | args.data[0], args.data[1], args.data[2], result, args.size(), 593 | [&](string_t input, int res, string_t flagsStr) { 594 | return PolygonWktToCellsExperimentalVarcharInnerFunction( 595 | input, res, flagsStr, result); 596 | }); 597 | } 598 | 599 | static void PolygonWktToCellsExperimentalVarcharFunctionSwapped( 600 | DataChunk &args, ExpressionState &state, Vector &result) { 601 | TernaryExecutor::Execute( 602 | args.data[0], args.data[1], args.data[2], result, args.size(), 603 | [&](string_t input, string_t flagsStr, int res) { 604 | return PolygonWktToCellsExperimentalVarcharInnerFunction( 605 | input, res, flagsStr, result); 606 | }); 607 | } 608 | 609 | CreateScalarFunctionInfo H3Functions::GetCellsToMultiPolygonWktFunction() { 610 | ScalarFunctionSet funcs("h3_cells_to_multi_polygon_wkt"); 611 | funcs.AddFunction(ScalarFunction( 612 | {LogicalType::LIST(LogicalType::VARCHAR)}, LogicalType::VARCHAR, 613 | CellsToMultiPolygonWktFunction< 614 | LogicalType::VARCHAR, CellsToMultiPolygonWktVarcharInputOperator>)); 615 | funcs.AddFunction(ScalarFunction( 616 | {LogicalType::LIST(LogicalType::UBIGINT)}, LogicalType::VARCHAR, 617 | CellsToMultiPolygonWktFunction)); 619 | funcs.AddFunction(ScalarFunction( 620 | {LogicalType::LIST(LogicalType::BIGINT)}, LogicalType::VARCHAR, 621 | CellsToMultiPolygonWktFunction)); 623 | return CreateScalarFunctionInfo(funcs); 624 | } 625 | 626 | CreateScalarFunctionInfo H3Functions::GetPolygonWktToCellsFunction() { 627 | // TODO: Expose flags 628 | return CreateScalarFunctionInfo(ScalarFunction( 629 | "h3_polygon_wkt_to_cells", {LogicalType::VARCHAR, LogicalType::INTEGER}, 630 | LogicalType::LIST(LogicalType::UBIGINT), PolygonWktToCellsFunction)); 631 | } 632 | 633 | CreateScalarFunctionInfo H3Functions::GetPolygonWktToCellsVarcharFunction() { 634 | // TODO: Expose flags 635 | return CreateScalarFunctionInfo( 636 | ScalarFunction("h3_polygon_wkt_to_cells_string", 637 | {LogicalType::VARCHAR, LogicalType::INTEGER}, 638 | LogicalType::LIST(LogicalType::VARCHAR), 639 | PolygonWktToCellsVarcharFunction)); 640 | } 641 | 642 | CreateScalarFunctionInfo 643 | H3Functions::GetPolygonWktToCellsExperimentalFunction() { 644 | ScalarFunctionSet funcs("h3_polygon_wkt_to_cells_experimental"); 645 | funcs.AddFunction(ScalarFunction( 646 | {LogicalType::VARCHAR, LogicalType::INTEGER, LogicalType::VARCHAR}, 647 | LogicalType::LIST(LogicalType::UBIGINT), 648 | PolygonWktToCellsExperimentalFunction)); 649 | funcs.AddFunction(ScalarFunction( 650 | {LogicalType::VARCHAR, LogicalType::VARCHAR, LogicalType::INTEGER}, 651 | LogicalType::LIST(LogicalType::UBIGINT), 652 | PolygonWktToCellsExperimentalFunctionSwapped)); 653 | return CreateScalarFunctionInfo(funcs); 654 | } 655 | 656 | CreateScalarFunctionInfo 657 | H3Functions::GetPolygonWktToCellsExperimentalVarcharFunction() { 658 | ScalarFunctionSet funcs("h3_polygon_wkt_to_cells_experimental_string"); 659 | funcs.AddFunction(ScalarFunction( 660 | {LogicalType::VARCHAR, LogicalType::INTEGER, LogicalType::VARCHAR}, 661 | LogicalType::LIST(LogicalType::VARCHAR), 662 | PolygonWktToCellsExperimentalVarcharFunction)); 663 | funcs.AddFunction(ScalarFunction( 664 | {LogicalType::VARCHAR, LogicalType::VARCHAR, LogicalType::INTEGER}, 665 | LogicalType::LIST(LogicalType::VARCHAR), 666 | PolygonWktToCellsExperimentalVarcharFunctionSwapped)); 667 | return CreateScalarFunctionInfo(funcs); 668 | } 669 | 670 | } // namespace duckdb 671 | -------------------------------------------------------------------------------- /src/h3_vertex.cpp: -------------------------------------------------------------------------------- 1 | #include "h3_common.hpp" 2 | #include "h3_functions.hpp" 3 | 4 | namespace duckdb { 5 | 6 | static void CellToVertexFunction(DataChunk &args, ExpressionState &state, 7 | Vector &result) { 8 | auto &inputs = args.data[0]; 9 | auto &inputs2 = args.data[1]; 10 | BinaryExecutor::ExecuteWithNulls( 11 | inputs, inputs2, result, args.size(), 12 | [&](H3Index cell, int32_t vertexNum, ValidityMask &mask, idx_t idx) { 13 | H3Index vertex; 14 | H3Error err = cellToVertex(cell, vertexNum, &vertex); 15 | if (err) { 16 | mask.SetInvalid(idx); 17 | return H3Index(H3_NULL); 18 | } else { 19 | return vertex; 20 | } 21 | }); 22 | } 23 | 24 | static void CellToVertexVarcharFunction(DataChunk &args, ExpressionState &state, 25 | Vector &result) { 26 | auto &inputs = args.data[0]; 27 | auto &inputs2 = args.data[1]; 28 | BinaryExecutor::ExecuteWithNulls( 29 | inputs, inputs2, result, args.size(), 30 | [&](string_t cellInput, int32_t vertexNum, ValidityMask &mask, 31 | idx_t idx) { 32 | H3Index cell; 33 | H3Error err0 = stringToH3(cellInput.GetString().c_str(), &cell); 34 | if (err0) { 35 | mask.SetInvalid(idx); 36 | return StringVector::EmptyString(result, 0); 37 | } else { 38 | H3Index vertex; 39 | H3Error err1 = cellToVertex(cell, vertexNum, &vertex); 40 | if (err1) { 41 | mask.SetInvalid(idx); 42 | return StringVector::EmptyString(result, 0); 43 | } else { 44 | auto str = StringUtil::Format("%llx", vertex); 45 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 46 | return StringVector::AddString(result, strAsStr); 47 | } 48 | } 49 | }); 50 | } 51 | 52 | static void CellToVertexesFunction(DataChunk &args, ExpressionState &state, 53 | Vector &result) { 54 | result.SetVectorType(VectorType::FLAT_VECTOR); 55 | auto &result_validity = FlatVector::Validity(result); 56 | auto result_data = FlatVector::GetData(result); 57 | idx_t offset = 0; 58 | for (idx_t i = 0; i < args.size(); i++) { 59 | result_data[i].offset = offset; 60 | 61 | uint64_t cell = args.GetValue(0, i) 62 | .DefaultCastAs(LogicalType::UBIGINT) 63 | .GetValue(); 64 | 65 | int64_t actual = 0; 66 | std::vector out(6); 67 | H3Error err = cellToVertexes(cell, out.data()); 68 | if (err) { 69 | result_validity.SetInvalid(i); 70 | result_data[i].length = 0; 71 | } else { 72 | for (auto val : out) { 73 | if (val != H3_NULL) { 74 | auto result_val = Value::UBIGINT(val); 75 | ListVector::PushBack(result, result_val); 76 | actual++; 77 | } 78 | } 79 | 80 | result_data[i].length = actual; 81 | } 82 | offset += actual; 83 | } 84 | result.Verify(args.size()); 85 | 86 | if (args.AllConstant()) { 87 | result.SetVectorType(VectorType::CONSTANT_VECTOR); 88 | } 89 | } 90 | 91 | static void CellToVertexesVarcharFunction(DataChunk &args, 92 | ExpressionState &state, 93 | Vector &result) { 94 | result.SetVectorType(VectorType::FLAT_VECTOR); 95 | auto &result_validity = FlatVector::Validity(result); 96 | auto result_data = FlatVector::GetData(result); 97 | idx_t offset = 0; 98 | for (idx_t i = 0; i < args.size(); i++) { 99 | result_data[i].offset = offset; 100 | 101 | string cellInput = args.GetValue(0, i) 102 | .DefaultCastAs(LogicalType::VARCHAR) 103 | .GetValue(); 104 | 105 | H3Index cell; 106 | H3Error err0 = stringToH3(cellInput.c_str(), &cell); 107 | if (err0) { 108 | result_validity.SetInvalid(i); 109 | result_data[i].length = 0; 110 | } else { 111 | int64_t actual = 0; 112 | std::vector out(6); 113 | H3Error err = cellToVertexes(cell, out.data()); 114 | if (err) { 115 | result_validity.SetInvalid(i); 116 | result_data[i].length = 0; 117 | } else { 118 | for (auto val : out) { 119 | if (val != H3_NULL) { 120 | auto str = StringUtil::Format("%llx", val); 121 | string_t strAsStr = string_t(strdup(str.c_str()), str.size()); 122 | ListVector::PushBack(result, strAsStr); 123 | actual++; 124 | } 125 | } 126 | 127 | result_data[i].length = actual; 128 | } 129 | offset += actual; 130 | } 131 | } 132 | result.Verify(args.size()); 133 | 134 | if (args.AllConstant()) { 135 | result.SetVectorType(VectorType::CONSTANT_VECTOR); 136 | } 137 | } 138 | 139 | static void VertexToLatFunction(DataChunk &args, ExpressionState &state, 140 | Vector &result) { 141 | auto &inputs = args.data[0]; 142 | UnaryExecutor::ExecuteWithNulls( 143 | inputs, result, args.size(), 144 | [&](H3Index vertex, ValidityMask &mask, idx_t idx) { 145 | LatLng latLng = {.lat = 0, .lng = 0}; 146 | H3Error err = vertexToLatLng(vertex, &latLng); 147 | if (err) { 148 | mask.SetInvalid(idx); 149 | return .0; 150 | } else { 151 | return radsToDegs(latLng.lat); 152 | } 153 | }); 154 | } 155 | 156 | static void VertexToLatVarcharFunction(DataChunk &args, ExpressionState &state, 157 | Vector &result) { 158 | auto &inputs = args.data[0]; 159 | UnaryExecutor::ExecuteWithNulls( 160 | inputs, result, args.size(), 161 | [&](string_t vertexInput, ValidityMask &mask, idx_t idx) { 162 | H3Index vertex; 163 | H3Error err0 = stringToH3(vertexInput.GetString().c_str(), &vertex); 164 | if (err0) { 165 | mask.SetInvalid(idx); 166 | return .0; 167 | } else { 168 | LatLng latLng = {.lat = 0, .lng = 0}; 169 | H3Error err1 = vertexToLatLng(vertex, &latLng); 170 | if (err1) { 171 | mask.SetInvalid(idx); 172 | return .0; 173 | } else { 174 | return radsToDegs(latLng.lat); 175 | } 176 | } 177 | }); 178 | } 179 | 180 | static void VertexToLngFunction(DataChunk &args, ExpressionState &state, 181 | Vector &result) { 182 | auto &inputs = args.data[0]; 183 | UnaryExecutor::ExecuteWithNulls( 184 | inputs, result, args.size(), 185 | [&](H3Index vertex, ValidityMask &mask, idx_t idx) { 186 | LatLng latLng = {.lat = 0, .lng = 0}; 187 | H3Error err = vertexToLatLng(vertex, &latLng); 188 | if (err) { 189 | mask.SetInvalid(idx); 190 | return .0; 191 | } else { 192 | return radsToDegs(latLng.lng); 193 | } 194 | }); 195 | } 196 | 197 | static void VertexToLngVarcharFunction(DataChunk &args, ExpressionState &state, 198 | Vector &result) { 199 | auto &inputs = args.data[0]; 200 | UnaryExecutor::ExecuteWithNulls( 201 | inputs, result, args.size(), 202 | [&](string_t vertexInput, ValidityMask &mask, idx_t idx) { 203 | H3Index vertex; 204 | H3Error err0 = stringToH3(vertexInput.GetString().c_str(), &vertex); 205 | if (err0) { 206 | mask.SetInvalid(idx); 207 | return .0; 208 | } else { 209 | LatLng latLng = {.lat = 0, .lng = 0}; 210 | H3Error err1 = vertexToLatLng(vertex, &latLng); 211 | if (err1) { 212 | mask.SetInvalid(idx); 213 | return .0; 214 | } else { 215 | return radsToDegs(latLng.lng); 216 | } 217 | } 218 | }); 219 | } 220 | 221 | static void VertexToLatLngFunction(DataChunk &args, ExpressionState &state, 222 | Vector &result) { 223 | auto result_data = FlatVector::GetData(result); 224 | for (idx_t i = 0; i < args.size(); i++) { 225 | result_data[i].offset = ListVector::GetListSize(result); 226 | 227 | uint64_t vertex = args.GetValue(0, i) 228 | .DefaultCastAs(LogicalType::UBIGINT) 229 | .GetValue(); 230 | LatLng latLng; 231 | H3Error err = vertexToLatLng(vertex, &latLng); 232 | ThrowH3Error(err); 233 | 234 | ListVector::PushBack(result, radsToDegs(latLng.lat)); 235 | ListVector::PushBack(result, radsToDegs(latLng.lng)); 236 | result_data[i].length = 2; 237 | } 238 | result.Verify(args.size()); 239 | } 240 | 241 | static void VertexToLatLngVarcharFunction(DataChunk &args, 242 | ExpressionState &state, 243 | Vector &result) { 244 | auto result_data = FlatVector::GetData(result); 245 | for (idx_t i = 0; i < args.size(); i++) { 246 | result_data[i].offset = ListVector::GetListSize(result); 247 | 248 | string vertexInput = args.GetValue(0, i) 249 | .DefaultCastAs(LogicalType::VARCHAR) 250 | .GetValue(); 251 | H3Index vertex; 252 | H3Error err0 = stringToH3(vertexInput.c_str(), &vertex); 253 | if (err0) { 254 | result.SetValue(i, Value(LogicalType::SQLNULL)); 255 | } else { 256 | LatLng latLng; 257 | H3Error err1 = vertexToLatLng(vertex, &latLng); 258 | if (err1) { 259 | result.SetValue(i, Value(LogicalType::SQLNULL)); 260 | } else { 261 | ListVector::PushBack(result, radsToDegs(latLng.lat)); 262 | ListVector::PushBack(result, radsToDegs(latLng.lng)); 263 | result_data[i].length = 2; 264 | } 265 | } 266 | } 267 | result.Verify(args.size()); 268 | } 269 | 270 | static void IsValidVertexVarcharFunction(DataChunk &args, 271 | ExpressionState &state, 272 | Vector &result) { 273 | auto &inputs = args.data[0]; 274 | UnaryExecutor::Execute( 275 | inputs, result, args.size(), [&](string_t input) { 276 | H3Index h; 277 | H3Error err = stringToH3(input.GetString().c_str(), &h); 278 | if (err) { 279 | return false; 280 | } 281 | return bool(isValidVertex(h)); 282 | }); 283 | } 284 | 285 | static void IsValidVertexFunction(DataChunk &args, ExpressionState &state, 286 | Vector &result) { 287 | auto &inputs = args.data[0]; 288 | UnaryExecutor::Execute( 289 | inputs, result, args.size(), 290 | [&](H3Index input) { return bool(isValidVertex(input)); }); 291 | } 292 | 293 | CreateScalarFunctionInfo H3Functions::GetCellToVertexFunction() { 294 | ScalarFunctionSet funcs("h3_cell_to_vertex"); 295 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT, LogicalType::INTEGER}, 296 | LogicalType::UBIGINT, CellToVertexFunction)); 297 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT, LogicalType::INTEGER}, 298 | LogicalType::BIGINT, CellToVertexFunction)); 299 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR, LogicalType::INTEGER}, 300 | LogicalType::VARCHAR, 301 | CellToVertexVarcharFunction)); 302 | return CreateScalarFunctionInfo(funcs); 303 | } 304 | 305 | CreateScalarFunctionInfo H3Functions::GetCellToVertexesFunction() { 306 | ScalarFunctionSet funcs("h3_cell_to_vertexes"); 307 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, 308 | LogicalType::LIST(LogicalType::UBIGINT), 309 | CellToVertexesFunction)); 310 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, 311 | LogicalType::LIST(LogicalType::BIGINT), 312 | CellToVertexesFunction)); 313 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, 314 | LogicalType::LIST(LogicalType::VARCHAR), 315 | CellToVertexesVarcharFunction)); 316 | return CreateScalarFunctionInfo(funcs); 317 | } 318 | 319 | CreateScalarFunctionInfo H3Functions::GetVertexToLatFunction() { 320 | ScalarFunctionSet funcs("h3_vertex_to_lat"); 321 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::DOUBLE, 322 | VertexToLatFunction)); 323 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::DOUBLE, 324 | VertexToLatFunction)); 325 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::DOUBLE, 326 | VertexToLatVarcharFunction)); 327 | return CreateScalarFunctionInfo(funcs); 328 | } 329 | 330 | CreateScalarFunctionInfo H3Functions::GetVertexToLngFunction() { 331 | ScalarFunctionSet funcs("h3_vertex_to_lng"); 332 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::DOUBLE, 333 | VertexToLngFunction)); 334 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::DOUBLE, 335 | VertexToLngFunction)); 336 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::DOUBLE, 337 | VertexToLngVarcharFunction)); 338 | return CreateScalarFunctionInfo(funcs); 339 | } 340 | 341 | CreateScalarFunctionInfo H3Functions::GetVertexToLatLngFunction() { 342 | ScalarFunctionSet funcs("h3_vertex_to_latlng"); 343 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, 344 | LogicalType::LIST(LogicalType::DOUBLE), 345 | VertexToLatLngFunction)); 346 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, 347 | LogicalType::LIST(LogicalType::DOUBLE), 348 | VertexToLatLngFunction)); 349 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, 350 | LogicalType::LIST(LogicalType::DOUBLE), 351 | VertexToLatLngVarcharFunction)); 352 | return CreateScalarFunctionInfo(funcs); 353 | } 354 | 355 | CreateScalarFunctionInfo H3Functions::GetIsValidVertexFunctions() { 356 | ScalarFunctionSet funcs("h3_is_valid_vertex"); 357 | funcs.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::BOOLEAN, 358 | IsValidVertexVarcharFunction)); 359 | funcs.AddFunction(ScalarFunction({LogicalType::UBIGINT}, LogicalType::BOOLEAN, 360 | IsValidVertexFunction)); 361 | funcs.AddFunction(ScalarFunction({LogicalType::BIGINT}, LogicalType::BOOLEAN, 362 | IsValidVertexFunction)); 363 | return CreateScalarFunctionInfo(funcs); 364 | } 365 | 366 | } // namespace duckdb 367 | -------------------------------------------------------------------------------- /src/include/h3_common.hpp: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // DuckDB 3 | // 4 | // h3_common.hpp 5 | // 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #pragma once 10 | 11 | #include "duckdb/common/operator/cast_operators.hpp" 12 | #include "duckdb/common/operator/decimal_cast_operators.hpp" 13 | #include "duckdb/common/operator/string_cast.hpp" 14 | #include "duckdb/execution/expression_executor.hpp" 15 | #include "duckdb/planner/expression/bound_function_expression.hpp" 16 | #include "h3api.h" 17 | 18 | namespace duckdb { 19 | 20 | void ThrowH3Error(H3Error err); 21 | 22 | } // namespace duckdb 23 | -------------------------------------------------------------------------------- /src/include/h3_extension.hpp: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // DuckDB 3 | // 4 | // h3_extension.hpp 5 | // 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #pragma once 10 | 11 | #include "duckdb.hpp" 12 | 13 | namespace duckdb { 14 | 15 | class H3Extension : public Extension { 16 | public: 17 | void Load(DuckDB &db) override; 18 | std::string Name() override; 19 | }; 20 | 21 | } // namespace duckdb 22 | -------------------------------------------------------------------------------- /src/include/h3_functions.hpp: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // DuckDB 3 | // 4 | // h3_functions.hpp 5 | // 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #pragma once 10 | 11 | #include "duckdb/parser/parsed_data/create_scalar_function_info.hpp" 12 | 13 | namespace duckdb { 14 | 15 | class H3Functions { 16 | public: 17 | static vector GetFunctions() { 18 | vector functions; 19 | 20 | // Indexing 21 | functions.push_back(GetLatLngToCellFunction()); 22 | functions.push_back(GetLatLngToCellVarcharFunction()); 23 | functions.push_back(GetCellToLatFunction()); 24 | functions.push_back(GetCellToLngFunction()); 25 | functions.push_back(GetCellToLatLngFunction()); 26 | functions.push_back(GetCellToBoundaryWktFunction()); 27 | 28 | // Inspection 29 | functions.push_back(GetGetResolutionFunction()); 30 | functions.push_back(GetGetBaseCellNumberFunction()); 31 | functions.push_back(GetStringToH3Function()); 32 | functions.push_back(GetH3ToStringFunction()); 33 | functions.push_back(GetIsValidCellFunctions()); 34 | functions.push_back(GetIsResClassIIIFunction()); 35 | functions.push_back(GetIsPentagonFunction()); 36 | functions.push_back(GetGetIcosahedronFacesFunction()); 37 | 38 | // Hierarchy 39 | functions.push_back(GetCellToParentFunction()); 40 | functions.push_back(GetCellToChildrenFunction()); 41 | functions.push_back(GetCellToCenterChildFunction()); 42 | functions.push_back(GetCellToChildPosFunction()); 43 | functions.push_back(GetChildPosToCellFunction()); 44 | functions.push_back(GetCompactCellsFunction()); 45 | functions.push_back(GetUncompactCellsFunction()); 46 | 47 | // Traversal 48 | functions.push_back(GetGridDiskFunction()); 49 | functions.push_back(GetGridDiskDistancesFunction()); 50 | functions.push_back(GetGridDiskUnsafeFunction()); 51 | functions.push_back(GetGridDiskDistancesUnsafeFunction()); 52 | functions.push_back(GetGridDiskDistancesSafeFunction()); 53 | functions.push_back(GetGridRingUnsafeFunction()); 54 | functions.push_back(GetGridPathCellsFunction()); 55 | functions.push_back(GetGridDistanceFunction()); 56 | functions.push_back(GetCellToLocalIjFunction()); 57 | functions.push_back(GetLocalIjToCellFunction()); 58 | 59 | // Directed edge 60 | functions.push_back(GetAreNeighborCellsFunction()); 61 | functions.push_back(GetCellsToDirectedEdgeFunction()); 62 | functions.push_back(GetIsValidDirectedEdgeFunctions()); 63 | functions.push_back(GetGetDirectedEdgeOriginFunction()); 64 | functions.push_back(GetGetDirectedEdgeDestinationFunction()); 65 | functions.push_back(GetDirectedEdgeToCellsFunction()); 66 | functions.push_back(GetOriginToDirectedEdgesFunction()); 67 | functions.push_back(GetDirectedEdgeToBoundaryWktFunction()); 68 | 69 | // Vertex 70 | functions.push_back(GetCellToVertexFunction()); 71 | functions.push_back(GetCellToVertexesFunction()); 72 | functions.push_back(GetVertexToLatFunction()); 73 | functions.push_back(GetVertexToLngFunction()); 74 | functions.push_back(GetVertexToLatLngFunction()); 75 | functions.push_back(GetIsValidVertexFunctions()); 76 | 77 | // Misc 78 | functions.push_back(GetGetHexagonAreaAvgFunction()); 79 | functions.push_back(GetCellAreaFunction()); 80 | functions.push_back(GetGetHexagonEdgeLengthAvgFunction()); 81 | functions.push_back(GetEdgeLengthFunction()); 82 | functions.push_back(GetGetNumCellsFunction()); 83 | functions.push_back(GetGetRes0CellsFunction()); 84 | functions.push_back(GetGetRes0CellsVarcharFunction()); 85 | functions.push_back(GetGetPentagonsFunction()); 86 | functions.push_back(GetGetPentagonsVarcharFunction()); 87 | functions.push_back(GetGreatCircleDistanceFunction()); 88 | 89 | // Regions 90 | functions.push_back(GetCellsToMultiPolygonWktFunction()); 91 | functions.push_back(GetPolygonWktToCellsFunction()); 92 | functions.push_back(GetPolygonWktToCellsVarcharFunction()); 93 | functions.push_back(GetPolygonWktToCellsExperimentalFunction()); 94 | functions.push_back(GetPolygonWktToCellsExperimentalVarcharFunction()); 95 | 96 | return functions; 97 | } 98 | 99 | private: 100 | // Indexing 101 | static CreateScalarFunctionInfo GetLatLngToCellFunction(); 102 | static CreateScalarFunctionInfo GetLatLngToCellVarcharFunction(); 103 | static CreateScalarFunctionInfo GetCellToLatFunction(); 104 | static CreateScalarFunctionInfo GetCellToLngFunction(); 105 | static CreateScalarFunctionInfo GetCellToLatLngFunction(); 106 | static CreateScalarFunctionInfo GetCellToBoundaryWktFunction(); 107 | 108 | // Inspection 109 | static CreateScalarFunctionInfo GetGetResolutionFunction(); 110 | static CreateScalarFunctionInfo GetGetBaseCellNumberFunction(); 111 | static CreateScalarFunctionInfo GetStringToH3Function(); 112 | static CreateScalarFunctionInfo GetH3ToStringFunction(); 113 | static CreateScalarFunctionInfo GetIsValidCellFunctions(); 114 | static CreateScalarFunctionInfo GetIsResClassIIIFunction(); 115 | static CreateScalarFunctionInfo GetIsPentagonFunction(); 116 | static CreateScalarFunctionInfo GetGetIcosahedronFacesFunction(); 117 | 118 | // Hierarchy 119 | static CreateScalarFunctionInfo GetCellToParentFunction(); 120 | static CreateScalarFunctionInfo GetCellToChildrenFunction(); 121 | static CreateScalarFunctionInfo GetCellToCenterChildFunction(); 122 | static CreateScalarFunctionInfo GetCellToChildPosFunction(); 123 | static CreateScalarFunctionInfo GetChildPosToCellFunction(); 124 | static CreateScalarFunctionInfo GetCompactCellsFunction(); 125 | static CreateScalarFunctionInfo GetUncompactCellsFunction(); 126 | 127 | // Traversal 128 | static CreateScalarFunctionInfo GetGridDiskFunction(); 129 | static CreateScalarFunctionInfo GetGridDiskDistancesFunction(); 130 | static CreateScalarFunctionInfo GetGridDiskUnsafeFunction(); 131 | static CreateScalarFunctionInfo GetGridDiskDistancesUnsafeFunction(); 132 | static CreateScalarFunctionInfo GetGridDiskDistancesSafeFunction(); 133 | static CreateScalarFunctionInfo GetGridRingUnsafeFunction(); 134 | static CreateScalarFunctionInfo GetGridPathCellsFunction(); 135 | static CreateScalarFunctionInfo GetGridDistanceFunction(); 136 | static CreateScalarFunctionInfo GetCellToLocalIjFunction(); 137 | static CreateScalarFunctionInfo GetLocalIjToCellFunction(); 138 | 139 | // Directed edge 140 | static CreateScalarFunctionInfo GetAreNeighborCellsFunction(); 141 | static CreateScalarFunctionInfo GetCellsToDirectedEdgeFunction(); 142 | static CreateScalarFunctionInfo GetIsValidDirectedEdgeFunctions(); 143 | static CreateScalarFunctionInfo GetGetDirectedEdgeOriginFunction(); 144 | static CreateScalarFunctionInfo GetGetDirectedEdgeDestinationFunction(); 145 | static CreateScalarFunctionInfo GetDirectedEdgeToCellsFunction(); 146 | static CreateScalarFunctionInfo GetOriginToDirectedEdgesFunction(); 147 | static CreateScalarFunctionInfo GetDirectedEdgeToBoundaryWktFunction(); 148 | 149 | // Vertex 150 | static CreateScalarFunctionInfo GetCellToVertexFunction(); 151 | static CreateScalarFunctionInfo GetCellToVertexesFunction(); 152 | static CreateScalarFunctionInfo GetVertexToLatFunction(); 153 | static CreateScalarFunctionInfo GetVertexToLngFunction(); 154 | static CreateScalarFunctionInfo GetVertexToLatLngFunction(); 155 | static CreateScalarFunctionInfo GetIsValidVertexFunctions(); 156 | 157 | // Misc 158 | static CreateScalarFunctionInfo GetGetHexagonAreaAvgFunction(); 159 | static CreateScalarFunctionInfo GetCellAreaFunction(); 160 | static CreateScalarFunctionInfo GetGetHexagonEdgeLengthAvgFunction(); 161 | static CreateScalarFunctionInfo GetEdgeLengthFunction(); 162 | static CreateScalarFunctionInfo GetGetNumCellsFunction(); 163 | static CreateScalarFunctionInfo GetGetRes0CellsFunction(); 164 | static CreateScalarFunctionInfo GetGetRes0CellsVarcharFunction(); 165 | static CreateScalarFunctionInfo GetGetPentagonsFunction(); 166 | static CreateScalarFunctionInfo GetGetPentagonsVarcharFunction(); 167 | static CreateScalarFunctionInfo GetGreatCircleDistanceFunction(); 168 | 169 | // Regions 170 | static CreateScalarFunctionInfo GetCellsToMultiPolygonWktFunction(); 171 | static CreateScalarFunctionInfo GetPolygonWktToCellsFunction(); 172 | static CreateScalarFunctionInfo GetPolygonWktToCellsVarcharFunction(); 173 | static CreateScalarFunctionInfo GetPolygonWktToCellsExperimentalFunction(); 174 | static CreateScalarFunctionInfo 175 | GetPolygonWktToCellsExperimentalVarcharFunction(); 176 | 177 | static void AddAliases(vector names, CreateScalarFunctionInfo fun, 178 | vector &functions) { 179 | for (auto &name : names) { 180 | fun.name = name; 181 | functions.push_back(fun); 182 | } 183 | } 184 | }; 185 | 186 | } // namespace duckdb 187 | -------------------------------------------------------------------------------- /test.sql: -------------------------------------------------------------------------------- 1 | LOAD 'build/release/h3.duckdb_extension'; 2 | -------------------------------------------------------------------------------- /test/data/simple.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaacbrodsky/h3-duckdb/752d8e4ebe4ea74caf06b21ee27cec9168485cb3/test/data/simple.parquet -------------------------------------------------------------------------------- /test/sql/h3/h3_functions.test: -------------------------------------------------------------------------------- 1 | require h3 2 | 3 | query I 4 | SELECT h3_latlng_to_cell(0, 0, 1); 5 | ---- 6 | 583031433791012863 7 | 8 | query II 9 | SELECT h3_cell_to_lat(h3_string_to_h3('85283473fffffff')), h3_cell_to_lng(h3_string_to_h3('85283473fffffff')) 10 | ---- 11 | 37.34579337536848 -121.9763759725512 12 | 13 | query I 14 | SELECT list_transform(h3_cell_to_latlng(h3_string_to_h3('85283473fffffff')), x-> round(x, 12)); 15 | ---- 16 | [37.345793375368, -121.976375972551] 17 | -------------------------------------------------------------------------------- /test/sql/h3/h3_functions_directededge.test: -------------------------------------------------------------------------------- 1 | require h3 2 | 3 | query I 4 | SELECT h3_is_valid_directed_edge('2222597fffffffff'); 5 | ---- 6 | false 7 | 8 | query I 9 | SELECT h3_is_valid_directed_edge(cast(0 as ubigint)); 10 | ---- 11 | false 12 | 13 | query I 14 | SELECT h3_is_valid_directed_edge('115283473fffffff'); 15 | ---- 16 | true 17 | 18 | query I 19 | SELECT h3_is_valid_directed_edge(1248204388774707199::ubigint); 20 | ---- 21 | true 22 | 23 | query I 24 | SELECT h3_origin_to_directed_edges(599686042433355775::ubigint) 25 | ---- 26 | [1248204388774707199, 1320261982812635135, 1392319576850563071, 1464377170888491007, 1536434764926418943, 1608492358964346879] 27 | 28 | query I 29 | SELECT h3_origin_to_directed_edges(599686042433355775::bigint) 30 | ---- 31 | [1248204388774707199, 1320261982812635135, 1392319576850563071, 1464377170888491007, 1536434764926418943, 1608492358964346879] 32 | 33 | query I 34 | SELECT h3_directed_edge_to_cells(1608492358964346879::ubigint) 35 | ---- 36 | [599686042433355775, 599686030622195711] 37 | 38 | query I 39 | SELECT h3_directed_edge_to_cells(1608492358964346879::bigint) 40 | ---- 41 | [599686042433355775, 599686030622195711] 42 | 43 | query I 44 | SELECT h3_directed_edge_to_cells(0::ubigint) 45 | ---- 46 | NULL 47 | 48 | query I 49 | SELECT h3_directed_edge_to_cells(0::bigint) 50 | ---- 51 | NULL 52 | 53 | query I 54 | SELECT h3_get_directed_edge_origin(1608492358964346879::ubigint) 55 | ---- 56 | 599686042433355775 57 | 58 | query I 59 | SELECT h3_get_directed_edge_origin(1608492358964346879::bigint) 60 | ---- 61 | 599686042433355775 62 | 63 | query I 64 | SELECT h3_get_directed_edge_destination(1608492358964346879::ubigint) 65 | ---- 66 | 599686030622195711 67 | 68 | query I 69 | SELECT h3_get_directed_edge_destination(1608492358964346879::bigint) 70 | ---- 71 | 599686030622195711 72 | 73 | query I 74 | SELECT h3_cells_to_directed_edge(599686042433355775::ubigint, 599686030622195711::ubigint) 75 | ---- 76 | 1608492358964346879 77 | 78 | query I 79 | SELECT h3_cells_to_directed_edge(599686042433355775::bigint, 599686030622195711::bigint) 80 | ---- 81 | 1608492358964346879 82 | 83 | query I 84 | SELECT h3_are_neighbor_cells(599686042433355775::ubigint, 599686030622195711::ubigint) 85 | ---- 86 | true 87 | 88 | query I 89 | SELECT h3_are_neighbor_cells(599686042433355775::bigint, 599686030622195711::bigint) 90 | ---- 91 | true 92 | 93 | query I 94 | SELECT h3_are_neighbor_cells(599686042433355775::ubigint, 599686029548453887::ubigint) 95 | ---- 96 | false 97 | 98 | query I 99 | SELECT h3_are_neighbor_cells(599686042433355775::bigint, 599686029548453887::bigint) 100 | ---- 101 | false 102 | 103 | query I 104 | SELECT strlen(h3_directed_edge_to_boundary_wkt(0::ubigint)) 105 | ---- 106 | 0 107 | 108 | query I 109 | SELECT strlen(h3_directed_edge_to_boundary_wkt(0::bigint)) 110 | ---- 111 | 0 112 | 113 | query I 114 | SELECT strlen(h3_directed_edge_to_boundary_wkt('0')) 115 | ---- 116 | 0 117 | 118 | query I 119 | SELECT h3_directed_edge_to_boundary_wkt(1608492358964346879::ubigint) 120 | ---- 121 | LINESTRING ((-121.915080 37.271356, -121.862223 37.353926, -121.915080 37.271356)) 122 | 123 | query I 124 | SELECT h3_directed_edge_to_boundary_wkt(1608492358964346879::bigint) 125 | ---- 126 | LINESTRING ((-121.915080 37.271356, -121.862223 37.353926, -121.915080 37.271356)) 127 | 128 | query I 129 | SELECT h3_directed_edge_to_boundary_wkt('115283473fffffff') 130 | ---- 131 | LINESTRING ((-122.037735 37.420129, -122.090429 37.337556, -122.037735 37.420129)) 132 | 133 | query I 134 | SELECT h3_origin_to_directed_edges('85283473fffffff') 135 | ---- 136 | [115283473fffffff, 125283473fffffff, 135283473fffffff, 145283473fffffff, 155283473fffffff, 165283473fffffff] 137 | 138 | query I 139 | SELECT h3_directed_edge_to_cells('165283473fffffff') 140 | ---- 141 | [85283473fffffff, 85283447fffffff] 142 | 143 | query I 144 | SELECT h3_directed_edge_to_cells('0') 145 | ---- 146 | NULL 147 | 148 | query I 149 | SELECT h3_get_directed_edge_origin('165283473fffffff') 150 | ---- 151 | 85283473fffffff 152 | 153 | query I 154 | SELECT h3_get_directed_edge_destination('165283473fffffff') 155 | ---- 156 | 85283447fffffff 157 | 158 | query I 159 | SELECT h3_cells_to_directed_edge('85283473fffffff', '85283447fffffff') 160 | ---- 161 | 165283473fffffff 162 | 163 | query I 164 | SELECT h3_are_neighbor_cells('85283473fffffff', '85283447fffffff') 165 | ---- 166 | true 167 | 168 | query I 169 | SELECT h3_are_neighbor_cells('85283473fffffff', '85283443fffffff') 170 | ---- 171 | false 172 | -------------------------------------------------------------------------------- /test/sql/h3/h3_functions_hierarchy.test: -------------------------------------------------------------------------------- 1 | require h3 2 | 3 | query I 4 | SELECT h3_cell_to_parent(cast(586265647244115967 as ubigint), 1); 5 | ---- 6 | 581764796395814911 7 | 8 | query I 9 | SELECT h3_cell_to_parent(cast(586265647244115967 as bigint), 1); 10 | ---- 11 | 581764796395814911 12 | 13 | query I 14 | SELECT h3_cell_to_parent('822d57fffffffff', 1); 15 | ---- 16 | 812d7ffffffffff 17 | 18 | query I 19 | SELECT h3_cell_to_parent(cast(586265647244115967 as ubigint), -1); 20 | ---- 21 | NULL 22 | 23 | query I 24 | SELECT h3_cell_to_parent(cast(586265647244115967 as bigint), -1); 25 | ---- 26 | NULL 27 | 28 | query I 29 | SELECT h3_cell_to_parent('822d57fffffffff', -1); 30 | ---- 31 | NULL 32 | 33 | query I 34 | SELECT h3_cell_to_parent(NULL, 0); 35 | ---- 36 | NULL 37 | 38 | query I 39 | SELECT h3_cell_to_center_child(cast(586265647244115967 as ubigint), 4); 40 | ---- 41 | 595272305332977663 42 | 43 | query I 44 | SELECT h3_cell_to_center_child(cast(586265647244115967 as bigint), 4); 45 | ---- 46 | 595272305332977663 47 | 48 | query I 49 | SELECT h3_cell_to_center_child('822d57fffffffff', 4); 50 | ---- 51 | 842d501ffffffff 52 | 53 | query I 54 | SELECT h3_cell_to_center_child(cast(586265647244115967 as ubigint), 0); 55 | ---- 56 | NULL 57 | 58 | query I 59 | SELECT h3_cell_to_center_child(cast(586265647244115967 as bigint), 0); 60 | ---- 61 | NULL 62 | 63 | query I 64 | SELECT h3_cell_to_center_child('822d57fffffffff', 0); 65 | ---- 66 | NULL 67 | 68 | query I 69 | select h3_cell_to_children(586265647244115967::ubigint, 3); 70 | ---- 71 | [590768765835149311, 590768834554626047, 590768903274102783, 590768971993579519, 590769040713056255, 590769109432532991, 590769178152009727] 72 | 73 | query I 74 | select h3_cell_to_children(586265647244115967::bigint, 3); 75 | ---- 76 | [590768765835149311, 590768834554626047, 590768903274102783, 590768971993579519, 590769040713056255, 590769109432532991, 590769178152009727] 77 | 78 | query I 79 | select h3_cell_to_children('822d57fffffffff', 3); 80 | ---- 81 | [832d50fffffffff, 832d51fffffffff, 832d52fffffffff, 832d53fffffffff, 832d54fffffffff, 832d55fffffffff, 832d56fffffffff] 82 | 83 | query I 84 | select h3_cell_to_children(586265647244115967::ubigint, 30); 85 | ---- 86 | NULL 87 | 88 | query I 89 | select h3_cell_to_children(586265647244115967::bigint, 30); 90 | ---- 91 | NULL 92 | 93 | query I 94 | select h3_cell_to_children('822d57fffffffff', 30); 95 | ---- 96 | NULL 97 | 98 | query I 99 | select h3_cell_to_child_pos(597563343967879167::ubigint, 1); 100 | ---- 101 | 70 102 | 103 | query I 104 | select h3_cell_to_child_pos(597563343967879167::bigint, 1); 105 | ---- 106 | 70 107 | 108 | query I 109 | select h3_cell_to_child_pos('84af8b1ffffffff', 1); 110 | ---- 111 | 70 112 | 113 | query I 114 | select h3_cell_to_child_pos(597563343967879167::ubigint, 100); 115 | ---- 116 | NULL 117 | 118 | query I 119 | select h3_cell_to_child_pos(597563343967879167::bigint, 100); 120 | ---- 121 | NULL 122 | 123 | query I 124 | select h3_cell_to_child_pos('84af8b1ffffffff', 100); 125 | ---- 126 | NULL 127 | 128 | query I 129 | select h3_child_pos_to_cell(70::bigint, 584056178628100095::ubigint, 4); 130 | ---- 131 | 597563343967879167 132 | 133 | query I 134 | select h3_child_pos_to_cell(70::bigint, 584056178628100095::bigint, 4); 135 | ---- 136 | 597563343967879167 137 | 138 | query I 139 | select h3_child_pos_to_cell(70::bigint, '81afbffffffffff', 4); 140 | ---- 141 | 84af8b1ffffffff 142 | 143 | query I 144 | select h3_child_pos_to_cell(70::bigint, 584056178628100095::ubigint, -1); 145 | ---- 146 | NULL 147 | 148 | query I 149 | select h3_child_pos_to_cell(70::bigint, 584056178628100095::bigint, -1); 150 | ---- 151 | NULL 152 | 153 | query I 154 | select h3_child_pos_to_cell(70, '81afbffffffffff', -1); 155 | ---- 156 | NULL 157 | 158 | query I 159 | select h3_compact_cells([586265647244115967::ubigint, 586260699441790975::ubigint, 586244756523188223::ubigint, 586245306279002111::ubigint, 586266196999929855::ubigint, 586264547732488191::ubigint, 586267846267371519::ubigint]) 160 | ---- 161 | [586265647244115967, 586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519] 162 | 163 | query I 164 | select h3_compact_cells([586265647244115967::bigint, 586260699441790975::bigint, 586244756523188223::bigint, 586245306279002111::bigint, 586266196999929855::bigint, 586264547732488191::bigint, 586267846267371519::bigint]) 165 | ---- 166 | [586265647244115967, 586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519] 167 | 168 | query I 169 | select h3_compact_cells([586266746755743743::ubigint, 586266196999929855::ubigint, 586265097488302079::ubigint, 586265647244115967::ubigint, 586267846267371519::ubigint, 586267296511557631::ubigint, 586264547732488191::ubigint]) 170 | ---- 171 | [581764796395814911] 172 | 173 | query I 174 | select h3_compact_cells([586266746755743743::bigint, 586266196999929855::bigint, 586265097488302079::bigint, 586265647244115967::bigint, 586267846267371519::bigint, 586267296511557631::bigint, 586264547732488191::bigint]) 175 | ---- 176 | [581764796395814911] 177 | 178 | query I 179 | select h3_uncompact_cells([581764796395814911::ubigint], 2) 180 | ---- 181 | [586264547732488191, 586265097488302079, 586265647244115967, 586266196999929855, 586266746755743743, 586267296511557631, 586267846267371519] 182 | 183 | query I 184 | select h3_uncompact_cells([581764796395814911::bigint], 2) 185 | ---- 186 | [586264547732488191, 586265097488302079, 586265647244115967, 586266196999929855, 586266746755743743, 586267296511557631, 586267846267371519] 187 | 188 | 189 | query I 190 | select h3_compact_cells(['822d57fffffffff', '822d0ffffffffff', '822c27fffffffff', '822c2ffffffffff', '822d5ffffffffff', '822d47fffffffff', '822d77fffffffff']) 191 | ---- 192 | [822d57fffffffff, 822d0ffffffffff, 822c27fffffffff, 822c2ffffffffff, 822d5ffffffffff, 822d47fffffffff, 822d77fffffffff] 193 | 194 | query I 195 | select h3_compact_cells(['822d67fffffffff', '822d5ffffffffff', '822d4ffffffffff', '822d57fffffffff', '822d77fffffffff', '822d6ffffffffff', '822d47fffffffff']) 196 | ---- 197 | [812d7ffffffffff] 198 | 199 | query I 200 | select h3_compact_cells(['X', '822d5ffffffffff', '822d4ffffffffff', '822d57fffffffff', '822d77fffffffff', '822d6ffffffffff', '822d47fffffffff']) 201 | ---- 202 | NULL 203 | 204 | query I 205 | select h3_compact_cells([]) 206 | ---- 207 | [] 208 | 209 | query I 210 | select h3_uncompact_cells(['812d7ffffffffff'], 2) 211 | ---- 212 | [822d47fffffffff, 822d4ffffffffff, 822d57fffffffff, 822d5ffffffffff, 822d67fffffffff, 822d6ffffffffff, 822d77fffffffff] 213 | 214 | query I 215 | select h3_uncompact_cells(['X'], 2) 216 | ---- 217 | NULL 218 | 219 | query I 220 | select h3_uncompact_cells([], 2) 221 | ---- 222 | [] 223 | 224 | statement ok 225 | -- https://github.com/isaacbrodsky/h3-duckdb/issues/140 226 | CREATE TABLE h3_child_cells AS 227 | WITH h3BaseCell AS ( SELECT h3_latlng_to_cell(51.477928, -0.001545, 5) AS base_cell_id) 228 | , kRing AS ( select UNNEST ( h3_cell_to_children(base_cell_id, 6) ) AS h3_child_id FROM h3BaseCell ) 229 | SELECT h3_child_id, h3_cell_to_boundary_wkt(h3_child_id) AS wkt 230 | FROM kRing; 231 | 232 | statement ok 233 | CREATE TABLE h3_child_cells_polyfilled AS SELECT h3_child_id, h3_polygon_wkt_to_cells(wkt, 7) AS polyfilled FROM h3_child_cells; 234 | 235 | query II 236 | SELECT h3_child_id, h3_compact_cells(polyfilled) FROM h3_child_cells_polyfilled; 237 | ---- 238 | 603927295928827903 [603927295928827903] 239 | 603927296063045631 [603927296063045631] 240 | 603927296197263359 [603927296197263359] 241 | 603927296331481087 [603927296331481087] 242 | 603927296465698815 [603927296465698815] 243 | 603927296599916543 [603927296599916543] 244 | 603927296734134271 [603927296734134271] 245 | 246 | query II 247 | SELECT h3_child_id, h3_uncompact_cells(polyfilled, 7) FROM h3_child_cells_polyfilled; 248 | ---- 249 | 603927295928827903 [608430895438757887, 608430895472312319, 608430895505866751, 608430895539421183, 608430895455535103, 608430895489089535, 608430895522643967] 250 | 603927296063045631 [608430895589752831, 608430895623307263, 608430895656861695, 608430895572975615, 608430895606530047, 608430895640084479, 608430895673638911] 251 | 603927296197263359 [608430895723970559, 608430895757524991, 608430895791079423, 608430895707193343, 608430895740747775, 608430895774302207, 608430895807856639] 252 | 603927296331481087 [608430895942074367, 608430895858188287, 608430895891742719, 608430895925297151, 608430895841411071, 608430895874965503, 608430895908519935] 253 | 603927296465698815 [608430895975628799, 608430896009183231, 608430896042737663, 608430896076292095, 608430895992406015, 608430896025960447, 608430896059514879] 254 | 603927296599916543 [608430896109846527, 608430896143400959, 608430896176955391, 608430896210509823, 608430896126623743, 608430896160178175, 608430896193732607] 255 | 603927296734134271 [608430896294395903, 608430896327950335, 608430896244064255, 608430896277618687, 608430896311173119, 608430896344727551, 608430896260841471] 256 | 257 | query II 258 | SELECT h3_child_id, h3_compact_cells(array_apply(polyfilled, (x) -> h3_h3_to_string(x))) FROM h3_child_cells_polyfilled; 259 | ---- 260 | 603927295928827903 [86194ad07ffffff] 261 | 603927296063045631 [86194ad0fffffff] 262 | 603927296197263359 [86194ad17ffffff] 263 | 603927296331481087 [86194ad1fffffff] 264 | 603927296465698815 [86194ad27ffffff] 265 | 603927296599916543 [86194ad2fffffff] 266 | 603927296734134271 [86194ad37ffffff] 267 | 268 | query II 269 | SELECT h3_child_id, h3_uncompact_cells(array_apply(polyfilled, (x) -> h3_h3_to_string(x)), 7) FROM h3_child_cells_polyfilled; 270 | ---- 271 | 603927295928827903 [87194ad00ffffff, 87194ad02ffffff, 87194ad04ffffff, 87194ad06ffffff, 87194ad01ffffff, 87194ad03ffffff, 87194ad05ffffff] 272 | 603927296063045631 [87194ad09ffffff, 87194ad0bffffff, 87194ad0dffffff, 87194ad08ffffff, 87194ad0affffff, 87194ad0cffffff, 87194ad0effffff] 273 | 603927296197263359 [87194ad11ffffff, 87194ad13ffffff, 87194ad15ffffff, 87194ad10ffffff, 87194ad12ffffff, 87194ad14ffffff, 87194ad16ffffff] 274 | 603927296331481087 [87194ad1effffff, 87194ad19ffffff, 87194ad1bffffff, 87194ad1dffffff, 87194ad18ffffff, 87194ad1affffff, 87194ad1cffffff] 275 | 603927296465698815 [87194ad20ffffff, 87194ad22ffffff, 87194ad24ffffff, 87194ad26ffffff, 87194ad21ffffff, 87194ad23ffffff, 87194ad25ffffff] 276 | 603927296599916543 [87194ad28ffffff, 87194ad2affffff, 87194ad2cffffff, 87194ad2effffff, 87194ad29ffffff, 87194ad2bffffff, 87194ad2dffffff] 277 | 603927296734134271 [87194ad33ffffff, 87194ad35ffffff, 87194ad30ffffff, 87194ad32ffffff, 87194ad34ffffff, 87194ad36ffffff, 87194ad31ffffff] 278 | -------------------------------------------------------------------------------- /test/sql/h3/h3_functions_indexing.test: -------------------------------------------------------------------------------- 1 | require h3 2 | 3 | query I 4 | SELECT h3_latlng_to_cell(0, 0, -1); 5 | ---- 6 | NULL 7 | 8 | query I 9 | SELECT h3_latlng_to_cell(0, 0, 1); 10 | ---- 11 | 583031433791012863 12 | 13 | query I 14 | SELECT h3_latlng_to_cell(37.7752702151959, NULL, 9); 15 | ---- 16 | NULL 17 | 18 | query I 19 | SELECT printf('%x', h3_latlng_to_cell(37.7752702151959, -122.418307270836, 9)::bigint); 20 | ---- 21 | 8928308280fffff 22 | 23 | query I 24 | SELECT h3_latlng_to_cell_string(0, 0, -1); 25 | ---- 26 | NULL 27 | 28 | query I 29 | SELECT h3_latlng_to_cell_string(0, 0, 1); 30 | ---- 31 | 81757ffffffffff 32 | 33 | query I 34 | SELECT h3_latlng_to_cell_string(37.7752702151959, NULL, 9); 35 | ---- 36 | NULL 37 | 38 | query I 39 | SELECT h3_latlng_to_cell_string(37.7752702151959, -122.418307270836, 9); 40 | ---- 41 | 8928308280fffff 42 | 43 | query II 44 | SELECT h3_cell_to_lat(h3_string_to_h3('85283473fffffff')), h3_cell_to_lng(h3_string_to_h3('85283473fffffff')) 45 | ---- 46 | 37.34579337536848 -121.9763759725512 47 | 48 | query II 49 | SELECT h3_cell_to_lat(cast(h3_string_to_h3('85283473fffffff') as bigint)), h3_cell_to_lng(cast(h3_string_to_h3('85283473fffffff') as bigint)) 50 | ---- 51 | 37.34579337536848 -121.9763759725512 52 | 53 | query II 54 | SELECT h3_cell_to_lat('85283473fffffff'), h3_cell_to_lng('85283473fffffff') 55 | ---- 56 | 37.34579337536848 -121.9763759725512 57 | 58 | query II 59 | SELECT h3_cell_to_lat(h3_string_to_h3('ffffffffffffffff')), h3_cell_to_lng(h3_string_to_h3('ffffffffffffffff')) 60 | ---- 61 | NULL NULL 62 | 63 | query I 64 | SELECT list_transform(h3_cell_to_latlng(h3_string_to_h3('85283473fffffff')), x-> round(x, 12)); 65 | ---- 66 | [37.345793375368, -121.976375972551] 67 | 68 | query I 69 | SELECT list_transform(h3_cell_to_latlng(cast(h3_string_to_h3('85283473fffffff') as bigint)), x-> round(x, 12)); 70 | ---- 71 | [37.345793375368, -121.976375972551] 72 | 73 | query I 74 | SELECT list_transform(h3_cell_to_latlng('85283473fffffff'), x-> round(x, 12)); 75 | ---- 76 | [37.345793375368, -121.976375972551] 77 | 78 | query I 79 | SELECT h3_cell_to_latlng(h3_string_to_h3('ffffffffffffffff')); 80 | ---- 81 | NULL 82 | 83 | query I 84 | SELECT h3_cell_to_boundary_wkt(h3_string_to_h3('822d57fffffffff')); 85 | ---- 86 | POLYGON ((38.777546 44.198571, 39.938746 42.736298, 42.150674 42.631271, 43.258395 44.047542, 42.146575 45.539505, 39.897167 45.559577, 38.777546 44.198571)) 87 | 88 | query I 89 | SELECT h3_cell_to_boundary_wkt(cast(h3_string_to_h3('822d57fffffffff') as bigint)); 90 | ---- 91 | POLYGON ((38.777546 44.198571, 39.938746 42.736298, 42.150674 42.631271, 43.258395 44.047542, 42.146575 45.539505, 39.897167 45.559577, 38.777546 44.198571)) 92 | 93 | query I 94 | SELECT strlen(h3_cell_to_boundary_wkt(h3_string_to_h3('fffffffffffffff'))); 95 | ---- 96 | 0 97 | 98 | query I 99 | SELECT h3_cell_to_boundary_wkt('822d57fffffffff'); 100 | ---- 101 | POLYGON ((38.777546 44.198571, 39.938746 42.736298, 42.150674 42.631271, 43.258395 44.047542, 42.146575 45.539505, 39.897167 45.559577, 38.777546 44.198571)) 102 | 103 | query I 104 | SELECT strlen(h3_cell_to_boundary_wkt('zzz')); 105 | ---- 106 | 0 107 | -------------------------------------------------------------------------------- /test/sql/h3/h3_functions_inspection.test: -------------------------------------------------------------------------------- 1 | require h3 2 | 3 | query I 4 | SELECT h3_get_resolution(cast(586265647244115967 as ubigint)); 5 | ---- 6 | 2 7 | 8 | query I 9 | SELECT h3_get_resolution(cast(586265647244115967 as bigint)); 10 | ---- 11 | 2 12 | 13 | query I 14 | SELECT h3_get_resolution('822d57fffffffff'); 15 | ---- 16 | 2 17 | 18 | query I 19 | SELECT h3_is_valid_cell(cast(586265647244115967 as ubigint)); 20 | ---- 21 | true 22 | 23 | query I 24 | SELECT h3_is_valid_cell(cast(586265647244115967 as bigint)); 25 | ---- 26 | true 27 | 28 | query I 29 | SELECT h3_is_valid_cell('85283473fffffff'); 30 | ---- 31 | true 32 | 33 | query I 34 | SELECT h3_is_valid_cell(cast(1234 as ubigint)); 35 | ---- 36 | false 37 | 38 | query I 39 | SELECT h3_is_valid_cell(cast(1234 as bigint)); 40 | ---- 41 | false 42 | 43 | query I 44 | SELECT h3_is_valid_cell('1234'); 45 | ---- 46 | false 47 | 48 | query I 49 | SELECT h3_string_to_h3(',,,,,') 50 | ---- 51 | NULL 52 | 53 | query II 54 | SELECT h3_h3_to_string(cast(10000000000000000 as ubigint)), h3_h3_to_string(cast(1 as ubigint)) 55 | ---- 56 | 2386f26fc10000 1 57 | 58 | query II 59 | SELECT h3_h3_to_string(cast(10000000000000000 as bigint)), h3_h3_to_string(cast(1 as bigint)) 60 | ---- 61 | 2386f26fc10000 1 62 | 63 | query II 64 | select h3_get_icosahedron_faces(599686042433355775::ubigint), h3_get_icosahedron_faces(576988517884755967::ubigint) 65 | ---- 66 | [7] [1, 6, 11, 7, 2] 67 | 68 | query II 69 | select h3_get_icosahedron_faces(599686042433355775::bigint), h3_get_icosahedron_faces(576988517884755967::bigint) 70 | ---- 71 | [7] [1, 6, 11, 7, 2] 72 | 73 | query II 74 | select h3_get_icosahedron_faces('85283473fffffff'), h3_get_icosahedron_faces('801dfffffffffff') 75 | ---- 76 | [7] [1, 6, 11, 7, 2] 77 | 78 | query I 79 | select h3_get_icosahedron_faces(18446744073709551615::ubigint) 80 | ---- 81 | NULL 82 | 83 | query I 84 | select h3_get_icosahedron_faces(9223372036854775807::bigint) 85 | ---- 86 | NULL 87 | 88 | query I 89 | select h3_get_icosahedron_faces('7fffffffffffffff') 90 | ---- 91 | NULL 92 | -------------------------------------------------------------------------------- /test/sql/h3/h3_functions_misc.test: -------------------------------------------------------------------------------- 1 | require h3 2 | 3 | query I 4 | SELECT h3_get_hexagon_area_avg(0, 'km^2'); 5 | ---- 6 | 4357449.416078383 7 | 8 | query I 9 | SELECT h3_get_hexagon_area_avg(1, 'km^2'); 10 | ---- 11 | 609788.4417941332 12 | 13 | query I 14 | SELECT h3_get_hexagon_area_avg(-1, 'km^2'); 15 | ---- 16 | NULL 17 | 18 | query I 19 | SELECT h3_get_hexagon_area_avg(0, 'm^2'); 20 | ---- 21 | 4357449416078.39 22 | 23 | query I 24 | SELECT h3_get_hexagon_area_avg(1, 'm^2'); 25 | ---- 26 | 609788441794.1339 27 | 28 | query I 29 | SELECT h3_get_hexagon_area_avg(-1, 'm^2'); 30 | ---- 31 | NULL 32 | 33 | query I 34 | SELECT h3_cell_area('8928308280fffff', 'km^2') 35 | ---- 36 | 0.1093981886464832 37 | 38 | query I 39 | SELECT h3_cell_area('8928308280fffff', 'm^2') 40 | ---- 41 | 109398.18864648319 42 | 43 | query I 44 | SELECT h3_cell_area(cast(586265647244115967 as ubigint), 'km^2') 45 | ---- 46 | 85321.69572540345 47 | 48 | query I 49 | SELECT h3_cell_area(cast(586265647244115967 as bigint), 'km^2') 50 | ---- 51 | 85321.69572540345 52 | 53 | query I 54 | SELECT h3_cell_area('fffffffffffffff', 'km^2') 55 | ---- 56 | NULL 57 | 58 | query I 59 | SELECT h3_cell_area('8928308280fffff', 'km') 60 | ---- 61 | NULL 62 | 63 | query I 64 | SELECT h3_edge_length('115283473fffffff', 'km') 65 | ---- 66 | 10.294736086198531 67 | 68 | query I 69 | SELECT h3_edge_length('115283473fffffff', 'm') 70 | ---- 71 | 10294.73608619853 72 | 73 | query I 74 | SELECT h3_edge_length(cast(1608492358964346879 as ubigint), 'km') 75 | ---- 76 | 10.302930275179133 77 | 78 | query I 79 | SELECT h3_edge_length(cast(1608492358964346879 as bigint), 'km') 80 | ---- 81 | 10.302930275179133 82 | 83 | query I 84 | SELECT h3_edge_length('fffffffffffffff', 'km') 85 | ---- 86 | NULL 87 | 88 | query I 89 | SELECT h3_edge_length('115283473fffffff', 'km^2') 90 | ---- 91 | NULL 92 | 93 | query I 94 | SELECT h3_get_num_cells(0) 95 | ---- 96 | 122 97 | 98 | query I 99 | SELECT h3_get_num_cells(5) 100 | ---- 101 | 2016842 102 | 103 | query I 104 | SELECT h3_get_num_cells(-1) 105 | ---- 106 | NULL 107 | 108 | query I 109 | SELECT h3_get_res0_cells() 110 | ---- 111 | [576495936675512319, 576531121047601151, 576566305419689983, 576601489791778815, 576636674163867647, 576671858535956479, 576707042908045311, 576742227280134143, 576777411652222975, 576812596024311807, 576847780396400639, 576882964768489471, 576918149140578303, 576953333512667135, 576988517884755967, 577023702256844799, 577058886628933631, 577094071001022463, 577129255373111295, 577164439745200127, 577199624117288959, 577234808489377791, 577269992861466623, 577305177233555455, 577340361605644287, 577375545977733119, 577410730349821951, 577445914721910783, 577481099093999615, 577516283466088447, 577551467838177279, 577586652210266111, 577621836582354943, 577657020954443775, 577692205326532607, 577727389698621439, 577762574070710271, 577797758442799103, 577832942814887935, 577868127186976767, 577903311559065599, 577938495931154431, 577973680303243263, 578008864675332095, 578044049047420927, 578079233419509759, 578114417791598591, 578149602163687423, 578184786535776255, 578219970907865087, 578255155279953919, 578290339652042751, 578325524024131583, 578360708396220415, 578395892768309247, 578431077140398079, 578466261512486911, 578501445884575743, 578536630256664575, 578571814628753407, 578606999000842239, 578642183372931071, 578677367745019903, 578712552117108735, 578747736489197567, 578782920861286399, 578818105233375231, 578853289605464063, 578888473977552895, 578923658349641727, 578958842721730559, 578994027093819391, 579029211465908223, 579064395837997055, 579099580210085887, 579134764582174719, 579169948954263551, 579205133326352383, 579240317698441215, 579275502070530047, 579310686442618879, 579345870814707711, 579381055186796543, 579416239558885375, 579451423930974207, 579486608303063039, 579521792675151871, 579556977047240703, 579592161419329535, 579627345791418367, 579662530163507199, 579697714535596031, 579732898907684863, 579768083279773695, 579803267651862527, 579838452023951359, 579873636396040191, 579908820768129023, 579944005140217855, 579979189512306687, 580014373884395519, 580049558256484351, 580084742628573183, 580119927000662015, 580155111372750847, 580190295744839679, 580225480116928511, 580260664489017343, 580295848861106175, 580331033233195007, 580366217605283839, 580401401977372671, 580436586349461503, 580471770721550335, 580506955093639167, 580542139465727999, 580577323837816831, 580612508209905663, 580647692581994495, 580682876954083327, 580718061326172159, 580753245698260991] 112 | 113 | query I 114 | SELECT h3_get_res0_cells_string() 115 | ---- 116 | [8001fffffffffff, 8003fffffffffff, 8005fffffffffff, 8007fffffffffff, 8009fffffffffff, 800bfffffffffff, 800dfffffffffff, 800ffffffffffff, 8011fffffffffff, 8013fffffffffff, 8015fffffffffff, 8017fffffffffff, 8019fffffffffff, 801bfffffffffff, 801dfffffffffff, 801ffffffffffff, 8021fffffffffff, 8023fffffffffff, 8025fffffffffff, 8027fffffffffff, 8029fffffffffff, 802bfffffffffff, 802dfffffffffff, 802ffffffffffff, 8031fffffffffff, 8033fffffffffff, 8035fffffffffff, 8037fffffffffff, 8039fffffffffff, 803bfffffffffff, 803dfffffffffff, 803ffffffffffff, 8041fffffffffff, 8043fffffffffff, 8045fffffffffff, 8047fffffffffff, 8049fffffffffff, 804bfffffffffff, 804dfffffffffff, 804ffffffffffff, 8051fffffffffff, 8053fffffffffff, 8055fffffffffff, 8057fffffffffff, 8059fffffffffff, 805bfffffffffff, 805dfffffffffff, 805ffffffffffff, 8061fffffffffff, 8063fffffffffff, 8065fffffffffff, 8067fffffffffff, 8069fffffffffff, 806bfffffffffff, 806dfffffffffff, 806ffffffffffff, 8071fffffffffff, 8073fffffffffff, 8075fffffffffff, 8077fffffffffff, 8079fffffffffff, 807bfffffffffff, 807dfffffffffff, 807ffffffffffff, 8081fffffffffff, 8083fffffffffff, 8085fffffffffff, 8087fffffffffff, 8089fffffffffff, 808bfffffffffff, 808dfffffffffff, 808ffffffffffff, 8091fffffffffff, 8093fffffffffff, 8095fffffffffff, 8097fffffffffff, 8099fffffffffff, 809bfffffffffff, 809dfffffffffff, 809ffffffffffff, 80a1fffffffffff, 80a3fffffffffff, 80a5fffffffffff, 80a7fffffffffff, 80a9fffffffffff, 80abfffffffffff, 80adfffffffffff, 80affffffffffff, 80b1fffffffffff, 80b3fffffffffff, 80b5fffffffffff, 80b7fffffffffff, 80b9fffffffffff, 80bbfffffffffff, 80bdfffffffffff, 80bffffffffffff, 80c1fffffffffff, 80c3fffffffffff, 80c5fffffffffff, 80c7fffffffffff, 80c9fffffffffff, 80cbfffffffffff, 80cdfffffffffff, 80cffffffffffff, 80d1fffffffffff, 80d3fffffffffff, 80d5fffffffffff, 80d7fffffffffff, 80d9fffffffffff, 80dbfffffffffff, 80ddfffffffffff, 80dffffffffffff, 80e1fffffffffff, 80e3fffffffffff, 80e5fffffffffff, 80e7fffffffffff, 80e9fffffffffff, 80ebfffffffffff, 80edfffffffffff, 80effffffffffff, 80f1fffffffffff, 80f3fffffffffff] 117 | 118 | query I 119 | SELECT h3_get_pentagons(-1) 120 | ---- 121 | NULL 122 | 123 | query I 124 | SELECT h3_get_pentagons(16) 125 | ---- 126 | NULL 127 | 128 | query I 129 | SELECT h3_get_pentagons(0) 130 | ---- 131 | [576636674163867647, 576988517884755967, 577340361605644287, 577832942814887935, 578219970907865087, 578536630256664575, 578712552117108735, 579029211465908223, 579416239558885375, 579908820768129023, 580260664489017343, 580612508209905663] 132 | 133 | query I 134 | SELECT h3_get_pentagons(5) 135 | ---- 136 | [599119489002373119, 599471332723261439, 599823176444149759, 600315757653393407, 600702785746370559, 601019445095170047, 601195366955614207, 601512026304413695, 601899054397390847, 602391635606634495, 602743479327522815, 603095323048411135] 137 | 138 | query I 139 | SELECT h3_get_pentagons_string(-1) 140 | ---- 141 | NULL 142 | 143 | query I 144 | SELECT h3_get_pentagons_string(16) 145 | ---- 146 | NULL 147 | 148 | query I 149 | SELECT h3_get_pentagons_string(0) 150 | ---- 151 | [8009fffffffffff, 801dfffffffffff, 8031fffffffffff, 804dfffffffffff, 8063fffffffffff, 8075fffffffffff, 807ffffffffffff, 8091fffffffffff, 80a7fffffffffff, 80c3fffffffffff, 80d7fffffffffff, 80ebfffffffffff] 152 | 153 | query I 154 | SELECT h3_get_pentagons_string(5) 155 | ---- 156 | [85080003fffffff, 851c0003fffffff, 85300003fffffff, 854c0003fffffff, 85620003fffffff, 85740003fffffff, 857e0003fffffff, 85900003fffffff, 85a60003fffffff, 85c20003fffffff, 85d60003fffffff, 85ea0003fffffff] 157 | 158 | query I 159 | SELECT h3_great_circle_distance(5, 5, 5, 5, 'km') 160 | ---- 161 | 0 162 | 163 | query I 164 | SELECT h3_great_circle_distance(5, 5, 15, 15, 'rads') 165 | ---- 166 | 0.2447868223787244 167 | 168 | query I 169 | SELECT h3_great_circle_distance(5, 5, 15, 15, 'km') 170 | ---- 171 | 1559.5386031690684 172 | 173 | query I 174 | SELECT h3_great_circle_distance(5, 5, -15, -15, 'm') 175 | ---- 176 | 3130865.2809374747 177 | 178 | query I 179 | SELECT h3_great_circle_distance(5, 5, -15, -15, 'invalid') 180 | ---- 181 | NULL 182 | 183 | query I 184 | SELECT h3_great_circle_distance(5, 5, -15, -15, NULL) 185 | ---- 186 | NULL 187 | -------------------------------------------------------------------------------- /test/sql/h3/h3_functions_regions_old_order.test: -------------------------------------------------------------------------------- 1 | require h3 2 | 3 | query I 4 | select length(h3_polygon_wkt_to_cells_experimental('POLYGON', 'CONTAINMENT_CENTER', 9)); 5 | ---- 6 | 0 7 | 8 | query I 9 | select length(h3_polygon_wkt_to_cells_experimental('POLYGON', 'center', 9)); 10 | ---- 11 | 0 12 | 13 | query I 14 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'AAA', 9); 15 | ---- 16 | [] 17 | 18 | query I 19 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_CENTER', 5) 20 | ---- 21 | [] 22 | 23 | query I 24 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_FULL', 5) 25 | ---- 26 | [] 27 | 28 | query I 29 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING', 5) 30 | ---- 31 | [599685771850416127, 599685772924157951, 599685776145383423, 599685777219125247] 32 | 33 | query I 34 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING_BBOX', 5) 35 | ---- 36 | [599685771850416127, 599685772924157951, 599685773997899775, 599685775071641599, 599685776145383423, 599685777219125247, 599685784735318015, 599686100415414271, 599686104710381567] 37 | 38 | query I 39 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_CENTER', 5) 40 | ---- 41 | [] 42 | 43 | query I 44 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_FULL', 5) 45 | ---- 46 | [] 47 | 48 | query I 49 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING', 5) 50 | ---- 51 | [85283083fffffff, 85283087fffffff, 85283093fffffff, 85283097fffffff] 52 | 53 | query I 54 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING_BBOX', 5) 55 | ---- 56 | [85283083fffffff, 85283087fffffff, 8528308bfffffff, 8528308ffffffff, 85283093fffffff, 85283097fffffff, 852830b3fffffff, 8528354bfffffff, 8528355bfffffff] 57 | 58 | query I 59 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_FULL', 5) 60 | ---- 61 | [] 62 | 63 | query I 64 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING', 5) 65 | ---- 66 | [599685771850416127, 599685772924157951, 599685776145383423, 599685777219125247] 67 | 68 | query I 69 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING_BBOX', 5) 70 | ---- 71 | [599685771850416127, 599685772924157951, 599685773997899775, 599685775071641599, 599685776145383423, 599685777219125247, 599685784735318015, 599686100415414271, 599686104710381567] 72 | 73 | query I 74 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_CENTER', 6) 75 | ---- 76 | [604189371075133439, 604189371209351167, 604189372417310719, 604189376175407103, 604189376309624831] 77 | 78 | query I 79 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_FULL', 6) 80 | ---- 81 | [604189371209351167, 604189376309624831] 82 | 83 | query I 84 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING', 6) 85 | ---- 86 | [604189370538262527, 604189370672480255, 604189371075133439, 604189371209351167, 604189372148875263, 604189372417310719, 604189374967447551, 604189375235883007, 604189375906971647, 604189376041189375, 604189376175407103, 604189376309624831] 87 | 88 | query I 89 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING_BBOX', 6) 90 | ---- 91 | [604189370538262527, 604189370672480255, 604189370940915711, 604189371075133439, 604189371209351167, 604189371343568895, 604189371612004351, 604189372148875263, 604189372283092991, 604189372417310719, 604189374967447551, 604189375235883007, 604189375906971647, 604189376041189375, 604189376175407103, 604189376309624831, 604189376578060287, 604189376712278015] 92 | 93 | query I 94 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_CENTER', 6) 95 | ---- 96 | [862830827ffffff, 86283082fffffff, 862830877ffffff, 862830957ffffff, 86283095fffffff] 97 | 98 | query I 99 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_FULL', 6) 100 | ---- 101 | [86283082fffffff, 86283095fffffff] 102 | 103 | query I 104 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING', 6) 105 | ---- 106 | [862830807ffffff, 86283080fffffff, 862830827ffffff, 86283082fffffff, 862830867ffffff, 862830877ffffff, 86283090fffffff, 86283091fffffff, 862830947ffffff, 86283094fffffff, 862830957ffffff, 86283095fffffff] 107 | 108 | query I 109 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'CONTAINMENT_OVERLAPPING_BBOX', 6) 110 | ---- 111 | [862830807ffffff, 86283080fffffff, 86283081fffffff, 862830827ffffff, 86283082fffffff, 862830837ffffff, 862830847ffffff, 862830867ffffff, 86283086fffffff, 862830877ffffff, 86283090fffffff, 86283091fffffff, 862830947ffffff, 86283094fffffff, 862830957ffffff, 86283095fffffff, 86283096fffffff, 862830977ffffff] 112 | 113 | query I 114 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'center', 5) 115 | ---- 116 | [] 117 | 118 | query I 119 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'full', 5) 120 | ---- 121 | [] 122 | 123 | query I 124 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap', 5) 125 | ---- 126 | [599685771850416127, 599685772924157951, 599685776145383423, 599685777219125247] 127 | 128 | query I 129 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap_bbox', 5) 130 | ---- 131 | [599685771850416127, 599685772924157951, 599685773997899775, 599685775071641599, 599685776145383423, 599685777219125247, 599685784735318015, 599686100415414271, 599686104710381567] 132 | 133 | query I 134 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'center', 5) 135 | ---- 136 | [] 137 | 138 | query I 139 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'full', 5) 140 | ---- 141 | [] 142 | 143 | query I 144 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap', 5) 145 | ---- 146 | [85283083fffffff, 85283087fffffff, 85283093fffffff, 85283097fffffff] 147 | 148 | query I 149 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap_bbox', 5) 150 | ---- 151 | [85283083fffffff, 85283087fffffff, 8528308bfffffff, 8528308ffffffff, 85283093fffffff, 85283097fffffff, 852830b3fffffff, 8528354bfffffff, 8528355bfffffff] 152 | 153 | query I 154 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'full', 5) 155 | ---- 156 | [] 157 | 158 | query I 159 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap', 5) 160 | ---- 161 | [599685771850416127, 599685772924157951, 599685776145383423, 599685777219125247] 162 | 163 | query I 164 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap_bbox', 5) 165 | ---- 166 | [599685771850416127, 599685772924157951, 599685773997899775, 599685775071641599, 599685776145383423, 599685777219125247, 599685784735318015, 599686100415414271, 599686104710381567] 167 | 168 | query I 169 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'center', 6) 170 | ---- 171 | [604189371075133439, 604189371209351167, 604189372417310719, 604189376175407103, 604189376309624831] 172 | 173 | query I 174 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'full', 6) 175 | ---- 176 | [604189371209351167, 604189376309624831] 177 | 178 | query I 179 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap', 6) 180 | ---- 181 | [604189370538262527, 604189370672480255, 604189371075133439, 604189371209351167, 604189372148875263, 604189372417310719, 604189374967447551, 604189375235883007, 604189375906971647, 604189376041189375, 604189376175407103, 604189376309624831] 182 | 183 | query I 184 | select h3_polygon_wkt_to_cells_experimental('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap_bbox', 6) 185 | ---- 186 | [604189370538262527, 604189370672480255, 604189370940915711, 604189371075133439, 604189371209351167, 604189371343568895, 604189371612004351, 604189372148875263, 604189372283092991, 604189372417310719, 604189374967447551, 604189375235883007, 604189375906971647, 604189376041189375, 604189376175407103, 604189376309624831, 604189376578060287, 604189376712278015] 187 | 188 | query I 189 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'center', 6) 190 | ---- 191 | [862830827ffffff, 86283082fffffff, 862830877ffffff, 862830957ffffff, 86283095fffffff] 192 | 193 | query I 194 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'full', 6) 195 | ---- 196 | [86283082fffffff, 86283095fffffff] 197 | 198 | query I 199 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap', 6) 200 | ---- 201 | [862830807ffffff, 86283080fffffff, 862830827ffffff, 86283082fffffff, 862830867ffffff, 862830877ffffff, 86283090fffffff, 86283091fffffff, 862830947ffffff, 86283094fffffff, 862830957ffffff, 86283095fffffff] 202 | 203 | query I 204 | select h3_polygon_wkt_to_cells_experimental_string('POLYGON ((-122.53401215374411 37.81666158907579, -122.53401215374411 37.70454536656959, -122.3479361380842 37.70454536656959, -122.3479361380842 37.81666158907579, -122.53401215374411 37.81666158907579))', 'overlap_bbox', 6) 205 | ---- 206 | [862830807ffffff, 86283080fffffff, 86283081fffffff, 862830827ffffff, 86283082fffffff, 862830837ffffff, 862830847ffffff, 862830867ffffff, 86283086fffffff, 862830877ffffff, 86283090fffffff, 86283091fffffff, 862830947ffffff, 86283094fffffff, 862830957ffffff, 86283095fffffff, 86283096fffffff, 862830977ffffff] 207 | -------------------------------------------------------------------------------- /test/sql/h3/h3_functions_traversal.test: -------------------------------------------------------------------------------- 1 | require h3 2 | 3 | query I 4 | select h3_grid_disk(586265647244115967::ubigint, 1); 5 | ---- 6 | [586265647244115967, 586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519] 7 | 8 | query I 9 | select h3_grid_disk(586265647244115967::bigint, 1); 10 | ---- 11 | [586265647244115967, 586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519] 12 | 13 | query I 14 | select h3_grid_disk_distances(586265647244115967::ubigint, 1); 15 | ---- 16 | [[586265647244115967], [586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519]] 17 | 18 | query I 19 | select h3_grid_disk_distances(586265647244115967::bigint, 1); 20 | ---- 21 | [[586265647244115967], [586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519]] 22 | 23 | query I 24 | select h3_grid_disk(594615896891195391::ubigint, 1); 25 | ---- 26 | [594615939840868351, 594615948430802943, 594615896891195391, 594615914071064575, 594615922660999167, 594615931250933759] 27 | 28 | query I 29 | select h3_grid_disk(594615896891195391::bigint, 1); 30 | ---- 31 | [594615939840868351, 594615948430802943, 594615896891195391, 594615914071064575, 594615922660999167, 594615931250933759] 32 | 33 | query I 34 | select h3_grid_disk(594615896891195391::ubigint, -1); 35 | ---- 36 | NULL 37 | 38 | query I 39 | select h3_grid_disk(594615896891195391::bigint, -1); 40 | ---- 41 | NULL 42 | 43 | query I 44 | select h3_grid_disk_distances(594615896891195391::ubigint, 1); 45 | ---- 46 | [[594615896891195391], [594615939840868351, 594615948430802943, 594615914071064575, 594615922660999167, 594615931250933759]] 47 | 48 | query I 49 | select h3_grid_disk_distances(594615896891195391::bigint, 1); 50 | ---- 51 | [[594615896891195391], [594615939840868351, 594615948430802943, 594615914071064575, 594615922660999167, 594615931250933759]] 52 | 53 | query I 54 | select h3_grid_disk_unsafe(586265647244115967::ubigint, 1); 55 | ---- 56 | [586265647244115967, 586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519] 57 | 58 | query I 59 | select h3_grid_disk_unsafe(586265647244115967::bigint, 1); 60 | ---- 61 | [586265647244115967, 586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519] 62 | 63 | query I 64 | select h3_grid_disk_distances_unsafe(586265647244115967::ubigint, 1); 65 | ---- 66 | [[586265647244115967], [586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519]] 67 | 68 | query I 69 | select h3_grid_disk_distances_unsafe(586265647244115967::bigint, 1); 70 | ---- 71 | [[586265647244115967], [586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191, 586267846267371519]] 72 | 73 | query I 74 | select h3_grid_disk_unsafe(594615896891195391::ubigint, 1); 75 | ---- 76 | NULL 77 | 78 | query I 79 | select h3_grid_disk_unsafe(594615896891195391::bigint, 1); 80 | ---- 81 | NULL 82 | 83 | query I 84 | select h3_grid_disk_distances_unsafe(594615896891195391::ubigint, 2); 85 | ---- 86 | NULL 87 | 88 | query I 89 | select h3_grid_disk_distances_unsafe(594615896891195391::bigint, 2); 90 | ---- 91 | NULL 92 | 93 | query I 94 | select h3_grid_disk_distances_safe(594615896891195391::ubigint, 2); 95 | ---- 96 | [[594615896891195391], [594615922660999167, 594615939840868351, 594615914071064575, 594615931250933759, 594615948430802943], [594616317797990399, 594616154589233151, 594616197538906111, 594616266258382847, 594616077279821823, 594616352157728767, 594616257668448255, 594616068689887231, 594616137409363967, 594616180359036927]] 97 | 98 | query I 99 | select h3_grid_disk_distances_safe(594615896891195391::bigint, 2); 100 | ---- 101 | [[594615896891195391], [594615922660999167, 594615939840868351, 594615914071064575, 594615931250933759, 594615948430802943], [594616317797990399, 594616154589233151, 594616197538906111, 594616266258382847, 594616077279821823, 594616352157728767, 594616257668448255, 594616068689887231, 594616137409363967, 594616180359036927]] 102 | 103 | query I 104 | select h3_grid_ring_unsafe(594615896891195391::ubigint, 1); 105 | ---- 106 | NULL 107 | 108 | query I 109 | select h3_grid_ring_unsafe(594615896891195391::bigint, 1); 110 | ---- 111 | NULL 112 | 113 | query I 114 | select h3_grid_ring_unsafe(586265647244115967::ubigint, 1); 115 | ---- 116 | [586267846267371519, 586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191] 117 | 118 | query I 119 | select h3_grid_ring_unsafe(586265647244115967::bigint, 1); 120 | ---- 121 | [586267846267371519, 586260699441790975, 586244756523188223, 586245306279002111, 586266196999929855, 586264547732488191] 122 | 123 | query I 124 | select h3_grid_disk('822d57fffffffff', 1); 125 | ---- 126 | [822d57fffffffff, 822d0ffffffffff, 822c27fffffffff, 822c2ffffffffff, 822d5ffffffffff, 822d47fffffffff, 822d77fffffffff] 127 | 128 | query I 129 | select h3_grid_disk_distances('822d57fffffffff', 1); 130 | ---- 131 | [[822d57fffffffff], [822d0ffffffffff, 822c27fffffffff, 822c2ffffffffff, 822d5ffffffffff, 822d47fffffffff, 822d77fffffffff]] 132 | 133 | query I 134 | select h3_grid_disk('8408001ffffffff', 1); 135 | ---- 136 | [840800bffffffff, 840800dffffffff, 8408001ffffffff, 8408005ffffffff, 8408007ffffffff, 8408009ffffffff] 137 | 138 | query I 139 | select h3_grid_disk('8408001ffffffff', -1); 140 | ---- 141 | NULL 142 | 143 | query I 144 | select h3_grid_disk_distances('8408001ffffffff', 1); 145 | ---- 146 | [[8408001ffffffff], [840800bffffffff, 840800dffffffff, 8408005ffffffff, 8408007ffffffff, 8408009ffffffff]] 147 | 148 | query I 149 | select h3_grid_disk_unsafe('822d57fffffffff', 1); 150 | ---- 151 | [822d57fffffffff, 822d0ffffffffff, 822c27fffffffff, 822c2ffffffffff, 822d5ffffffffff, 822d47fffffffff, 822d77fffffffff] 152 | 153 | query I 154 | select h3_grid_disk_distances_unsafe('822d57fffffffff', 1); 155 | ---- 156 | [[822d57fffffffff], [822d0ffffffffff, 822c27fffffffff, 822c2ffffffffff, 822d5ffffffffff, 822d47fffffffff, 822d77fffffffff]] 157 | 158 | query I 159 | select h3_grid_disk_unsafe('8408001ffffffff', 1); 160 | ---- 161 | NULL 162 | 163 | query I 164 | select h3_grid_disk_distances_unsafe('8408001ffffffff', 2); 165 | ---- 166 | NULL 167 | 168 | query I 169 | select h3_grid_disk_distances_safe('8408001ffffffff', 2); 170 | ---- 171 | [[8408001ffffffff], [8408007ffffffff, 840800bffffffff, 8408005ffffffff, 8408009ffffffff, 840800dffffffff], [8408063ffffffff, 840803dffffffff, 8408047ffffffff, 8408057ffffffff, 840802bffffffff, 840806bffffffff, 8408055ffffffff, 8408029ffffffff, 8408039ffffffff, 8408043ffffffff]] 172 | 173 | query I 174 | select h3_grid_ring_unsafe('8408001ffffffff', 1); 175 | ---- 176 | NULL 177 | 178 | query I 179 | select h3_grid_ring_unsafe('822d57fffffffff', 1); 180 | ---- 181 | [822d77fffffffff, 822d0ffffffffff, 822c27fffffffff, 822c2ffffffffff, 822d5ffffffffff, 822d47fffffffff] 182 | 183 | query I 184 | select h3_grid_path_cells(605035864166236159::ubigint, 605034941150920703::ubigint); 185 | ---- 186 | [605035864166236159, 605035861750317055, 605035861347663871, 605035862018752511, 605034941419356159, 605034941150920703] 187 | 188 | query I 189 | select h3_grid_path_cells(605035864166236159::bigint, 605034941150920703::bigint); 190 | ---- 191 | [605035864166236159, 605035861750317055, 605035861347663871, 605035862018752511, 605034941419356159, 605034941150920703] 192 | 193 | query I 194 | select h3_grid_path_cells(605035864166236159::ubigint, 0::ubigint); 195 | ---- 196 | NULL 197 | 198 | query I 199 | select h3_grid_path_cells(605035864166236159::bigint, 0::bigint); 200 | ---- 201 | NULL 202 | 203 | query I 204 | select h3_grid_path_cells('86584e9afffffff', '8658412c7ffffff'); 205 | ---- 206 | [86584e9afffffff, 86584e91fffffff, 86584e907ffffff, 86584e92fffffff, 8658412d7ffffff, 8658412c7ffffff] 207 | 208 | query I 209 | select h3_grid_path_cells('86584e9afffffff', '0'); 210 | ---- 211 | NULL 212 | 213 | query I 214 | select h3_grid_path_cells('0', '86584e9afffffff'); 215 | ---- 216 | NULL 217 | 218 | query I 219 | select h3_grid_distance(605035864166236159::ubigint, 605034941150920703::ubigint); 220 | ---- 221 | 5 222 | 223 | query I 224 | select h3_grid_distance(605035864166236159::bigint, 605034941150920703::bigint); 225 | ---- 226 | 5 227 | 228 | query I 229 | select h3_grid_distance(605035864166236159::ubigint, 0::ubigint); 230 | ---- 231 | NULL 232 | 233 | query I 234 | select h3_grid_distance(605035864166236159::bigint, 0::bigint); 235 | ---- 236 | NULL 237 | 238 | query I 239 | select h3_grid_distance('86584e9afffffff', '8658412c7ffffff'); 240 | ---- 241 | 5 242 | 243 | query I 244 | select h3_grid_distance('86584e9afffffff', '0'); 245 | ---- 246 | NULL 247 | 248 | query I 249 | select h3_grid_distance('0', '86584e9afffffff'); 250 | ---- 251 | NULL 252 | 253 | query I 254 | select h3_cell_to_local_ij(605034941285138431::ubigint, 605034941285138431::ubigint); 255 | ---- 256 | [-123, -177] 257 | 258 | query I 259 | select h3_cell_to_local_ij(605034941285138431::bigint, 605034941285138431::bigint); 260 | ---- 261 | [-123, -177] 262 | 263 | query I 264 | select h3_cell_to_local_ij(605034941285138431::ubigint, 0::ubigint); 265 | ---- 266 | NULL 267 | 268 | query I 269 | select h3_cell_to_local_ij(605034941285138431::bigint, 0::bigint); 270 | ---- 271 | NULL 272 | 273 | query I 274 | select h3_local_ij_to_cell(605034941285138431::ubigint, -123, -177); 275 | ---- 276 | 605034941285138431 277 | 278 | query I 279 | select h3_local_ij_to_cell(605034941285138431::bigint, -123, -177); 280 | ---- 281 | 605034941285138431 282 | 283 | query I 284 | select h3_local_ij_to_cell(605034941285138431::ubigint, -1230000, -177); 285 | ---- 286 | NULL 287 | 288 | query I 289 | select h3_local_ij_to_cell(605034941285138431::bigint, -1230000, -177); 290 | ---- 291 | NULL 292 | 293 | query I 294 | select h3_cell_to_local_ij('8658412cfffffff', '8658412cfffffff'); 295 | ---- 296 | [-123, -177] 297 | 298 | query I 299 | select h3_cell_to_local_ij('8658412cfffffff', '0'); 300 | ---- 301 | NULL 302 | 303 | query I 304 | select h3_cell_to_local_ij('8658412cfffffff', 'abc'); 305 | ---- 306 | NULL 307 | 308 | query I 309 | select h3_local_ij_to_cell('8658412cfffffff', -123, -177); 310 | ---- 311 | 8658412cfffffff 312 | 313 | query I 314 | select h3_local_ij_to_cell('8658412cfffffff', -1230000, -177); 315 | ---- 316 | NULL 317 | -------------------------------------------------------------------------------- /test/sql/h3/h3_functions_vertex.test: -------------------------------------------------------------------------------- 1 | require h3 2 | 3 | query I 4 | SELECT h3_is_valid_vertex('2222597fffffffff'); 5 | ---- 6 | true 7 | 8 | query I 9 | SELECT h3_is_valid_vertex('823d6ffffffffff'); 10 | ---- 11 | false 12 | 13 | query I 14 | SELECT h3_is_valid_vertex(h3_string_to_h3('2222597fffffffff')); 15 | ---- 16 | true 17 | 18 | query I 19 | SELECT h3_is_valid_vertex(h3_string_to_h3('823d6ffffffffff')); 20 | ---- 21 | false 22 | 23 | query I 24 | SELECT h3_is_valid_vertex(cast(h3_string_to_h3('2222597fffffffff') as bigint)); 25 | ---- 26 | true 27 | 28 | query I 29 | SELECT h3_is_valid_vertex(cast(h3_string_to_h3('823d6ffffffffff') as bigint)); 30 | ---- 31 | false 32 | 33 | query I 34 | SELECT h3_cell_to_vertexes(h3_string_to_h3('823d6ffffffffff')); 35 | ---- 36 | [2459626752788398079, 2676216249809108991, 2604158655771181055, 2387553765587681279, 2315496171549753343, 2531684346826326015] 37 | 38 | query I 39 | SELECT h3_cell_to_vertexes(cast(h3_string_to_h3('823d6ffffffffff') as bigint)); 40 | ---- 41 | [2459626752788398079, 2676216249809108991, 2604158655771181055, 2387553765587681279, 2315496171549753343, 2531684346826326015] 42 | 43 | query I 44 | SELECT h3_cell_to_vertexes(h3_string_to_h3('fffffffffffffff')); 45 | ---- 46 | NULL 47 | 48 | query I 49 | SELECT h3_cell_to_vertexes(cast(h3_string_to_h3('fffffffffffffff') as bigint)); 50 | ---- 51 | NULL 52 | 53 | query I 54 | SELECT h3_cell_to_vertex(h3_string_to_h3('823d6ffffffffff'), 0); 55 | ---- 56 | 2459626752788398079 57 | 58 | query I 59 | SELECT h3_cell_to_vertex(cast(h3_string_to_h3('823d6ffffffffff') as bigint), 0); 60 | ---- 61 | 2459626752788398079 62 | 63 | query I 64 | SELECT h3_cell_to_vertex(h3_string_to_h3('823d6ffffffffff'), -1); 65 | ---- 66 | NULL 67 | 68 | query I 69 | SELECT h3_cell_to_vertex(cast(h3_string_to_h3('823d6ffffffffff') as bigint), -1); 70 | ---- 71 | NULL 72 | 73 | query I 74 | SELECT list_transform(h3_vertex_to_latlng(h3_string_to_h3('2222597fffffffff')), x -> round(x, 12)); 75 | ---- 76 | [39.380842841818, 88.574962137855] 77 | 78 | query I 79 | SELECT list_transform(h3_vertex_to_latlng(cast(h3_string_to_h3('2222597fffffffff') as bigint)), x -> round(x, 12)); 80 | ---- 81 | [39.380842841818, 88.574962137855] 82 | 83 | query I 84 | SELECT round(h3_vertex_to_lat(h3_string_to_h3('2222597fffffffff')), 12); 85 | ---- 86 | 39.380842841818 87 | 88 | query I 89 | SELECT round(h3_vertex_to_lat(cast(h3_string_to_h3('2222597fffffffff') as bigint)), 12); 90 | ---- 91 | 39.380842841818 92 | 93 | query I 94 | SELECT round(h3_vertex_to_lng(h3_string_to_h3('2222597fffffffff')), 12); 95 | ---- 96 | 88.574962137855 97 | 98 | query I 99 | SELECT round(h3_vertex_to_lng(cast(h3_string_to_h3('2222597fffffffff') as bigint)), 12); 100 | ---- 101 | 88.574962137855 102 | 103 | query I 104 | SELECT h3_cell_to_vertexes('823d6ffffffffff'); 105 | ---- 106 | [2222597fffffffff, 2523d47fffffffff, 2423d47fffffffff, 21224b7fffffffff, 20224b7fffffffff, 2322597fffffffff] 107 | 108 | query I 109 | SELECT h3_cell_to_vertexes('fffffffffffffff'); 110 | ---- 111 | NULL 112 | 113 | query I 114 | SELECT h3_cell_to_vertex('823d6ffffffffff', 0); 115 | ---- 116 | 2222597fffffffff 117 | 118 | query I 119 | SELECT h3_cell_to_vertex('823d6ffffffffff', -1); 120 | ---- 121 | NULL 122 | 123 | query I 124 | SELECT list_transform(h3_vertex_to_latlng('2222597fffffffff'), x -> round(x, 12)); 125 | ---- 126 | [39.380842841818, 88.574962137855] 127 | 128 | query I 129 | SELECT round(h3_vertex_to_lat('2222597fffffffff'), 12); 130 | ---- 131 | 39.380842841818 132 | 133 | query I 134 | SELECT round(h3_vertex_to_lng('2222597fffffffff'), 12); 135 | ---- 136 | 88.574962137855 137 | -------------------------------------------------------------------------------- /test/sql/h3/h3_parquet.test: -------------------------------------------------------------------------------- 1 | require parquet 2 | 3 | require h3 4 | 5 | query I 6 | SELECT * from read_parquet('test/data/simple.parquet'); 7 | ---- 8 | 0 9 | 1 10 | 2 11 | 3 12 | 4 13 | 646078419604526808 14 | 15 | query I 16 | SELECT h3_h3_to_string(a) from read_parquet('test/data/simple.parquet'); 17 | ---- 18 | 0 19 | 1 20 | 2 21 | 3 22 | 4 23 | 8f754e64992d6d8 24 | 25 | query I 26 | select h3_compact_cells([a]) from read_parquet('test/data/simple.parquet'); 27 | ---- 28 | [] 29 | [1] 30 | [2] 31 | [3] 32 | [4] 33 | [646078419604526808] 34 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | {} 2 | --------------------------------------------------------------------------------