pax_global_header 0000666 0000000 0000000 00000000064 14752347156 0014530 g ustar 00root root 0000000 0000000 52 comment=f7ae65e69b2f46d5197677b90a81986482fa0293
h3-pg-4.2.2/ 0000775 0000000 0000000 00000000000 14752347156 0012453 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/.github/ 0000775 0000000 0000000 00000000000 14752347156 0014013 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/.github/FUNDING.yml 0000664 0000000 0000000 00000001340 14752347156 0015626 0 ustar 00root root 0000000 0000000 # These are supported funding model platforms
github: [zachasme]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
h3-pg-4.2.2/.github/workflows/ 0000775 0000000 0000000 00000000000 14752347156 0016050 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/.github/workflows/documentation.yml 0000664 0000000 0000000 00000000572 14752347156 0021450 0 ustar 00root root 0000000 0000000 name: documentation
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
documentation:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.10"
- run: pipx install poetry
- run: scripts/documentation
- run: git diff --exit-code docs
h3-pg-4.2.2/.github/workflows/pgxn.yml 0000664 0000000 0000000 00000001605 14752347156 0017551 0 ustar 00root root 0000000 0000000 name: pgxn
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
pgxn:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install PGXN Client
run: sudo pip install pgxnclient
- name: Install PostgreSQL server dependencies
run: |
sudo apt-get update
sudo apt-get install postgresql-server-dev-14 postgis
- name: Prepare PostgreSQL
run: |
sudo systemctl start postgresql.service
sudo -u postgres -i createuser --superuser runner
sudo -u postgres -i createdb runner
psql -c "CREATE EXTENSION postgis; CREATE EXTENSION postgis_raster;"
- name: Bundle
run: scripts/bundle
- name: Install
run: sudo pgxn install ./h3-*.zip
- name: Load
run: pgxn load h3
- name: Check
run: pgxn check h3
h3-pg-4.2.2/.github/workflows/test-linux.yml 0000664 0000000 0000000 00000002732 14752347156 0020713 0 ustar 00root root 0000000 0000000 name: test-linux
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
linux:
strategy:
matrix:
os: [ubuntu-24.04, ubuntu-22.04]
pg: [17, 16, 15, 14, 13]
config: [Release, Debug]
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }} 🐘${{ matrix.pg }} (${{ matrix.config }})
steps:
- uses: actions/checkout@v4
- name: Setup pg_validate_extupgrade
uses: baptiste0928/cargo-install@v3
with:
crate: pg_validate_extupgrade
git: https://github.com/rjuju/pg_validate_extupgrade
- name: Setup PostgreSQL
run: |
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -p -i -v ${{ matrix.pg }}
sudo apt-get -y install postgresql-${{ matrix.pg }}-postgis-3
sudo -u postgres -i createuser --superuser runner
sudo -u postgres -i createdb runner
psql -c "CREATE EXTENSION postgis; CREATE EXTENSION postgis_raster;"
- name: Generate
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config }}
- name: Build
run: cmake --build build
- name: Install
run: sudo cmake --install build --component h3-pg
- name: Test
env:
PGHOST: /var/run/postgresql
run: ctest --test-dir build --output-on-failure --build-config ${{ matrix.config }}
- name: Print regression diffs
if: ${{ failure() }}
run: cat build/*/test/regression.diffs
h3-pg-4.2.2/.github/workflows/test-macos.yml 0000664 0000000 0000000 00000003446 14752347156 0020661 0 ustar 00root root 0000000 0000000 name: test-macos
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
macos:
strategy:
matrix:
os: [macos-13]
pg: [17, 16, 15, 14, 13]
config: [Release, Debug]
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }} 🐘${{ matrix.pg }} (${{ matrix.config }})
steps:
- uses: actions/checkout@v4
- name: Setup pg_validate_extupgrade
uses: baptiste0928/cargo-install@v3
with:
crate: pg_validate_extupgrade
git: https://github.com/rjuju/pg_validate_extupgrade
- name: Setup PostgreSQL
run: |
brew install postgresql@${{ matrix.pg }}
brew unlink postgresql@${{ matrix.pg }}
brew link --overwrite postgresql@${{ matrix.pg }}
brew services run postgresql@${{ matrix.pg }}
- name: Setup PostGIS
run: brew install postgis
if: ${{ matrix.pg == 17 || matrix.pg == 14 }}
- name: Fix gettext linkage for PostgreSQL >= 15
run: brew unlink gettext && brew link --overwrite --force gettext
if: ${{ matrix.pg >= 15 }}
- name: Generate
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config }}
env:
# Fixes missing libintl.h on some runners
CFLAGS: "-I /usr/local/include"
- name: Build
run: cmake --build build --config ${{ matrix.config }}
- name: Install
run: sudo cmake --install build --component h3-pg --config ${{ matrix.config }}
- name: Create test database (for pg_validate_extupgrade)
run: createdb runner
- name: Test
run: ctest --test-dir build --output-on-failure --build-config ${{ matrix.config }}
- name: Print regression diffs
if: ${{ failure() }}
run: cat build/*/regression.diffs
h3-pg-4.2.2/.github/workflows/test-windows.yml 0000664 0000000 0000000 00000001532 14752347156 0021243 0 ustar 00root root 0000000 0000000 name: test-windows
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
windows:
strategy:
matrix:
config: [Release, Debug]
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
- name: Fix default PostgreSQL version on Windows Runner
run: $env:PGBIN >> $env:GITHUB_PATH
- name: Generate
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config }}
- name: Build
run: cmake --build build --config ${{ matrix.config }}
- name: Install
run: cmake --install build --component h3-pg --config ${{ matrix.config }}
- name: Test
run: ctest --test-dir build --output-on-failure --build-config ${{ matrix.config }}
- name: Print regression diffs
if: ${{ failure() }}
run: cat build/*/test/regression.diffs
h3-pg-4.2.2/.gitignore 0000664 0000000 0000000 00000000332 14752347156 0014441 0 ustar 00root root 0000000 0000000 build*/
# C
*.o
*.bc
*.so
# Test results
h3/test/regression.*
h3/test/results
# Generated
/h3*.sql
/h3/test/expected/ci-*.out
/h3/test/sql/ci-*.sql
/libh3-*/
/h3-*.zip
*.BAK
# IDEs
.vscode/*
!.vscode/settings.json
h3-pg-4.2.2/.vscode/ 0000775 0000000 0000000 00000000000 14752347156 0014014 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/.vscode/settings.json 0000664 0000000 0000000 00000000031 14752347156 0016541 0 ustar 00root root 0000000 0000000 {
"editor.tabSize": 4
} h3-pg-4.2.2/CHANGELOG.md 0000664 0000000 0000000 00000030072 14752347156 0014266 0 ustar 00root root 0000000 0000000 # Changelog
All notable changes to this project will be documented in this file.
Critical bugfixes or breaking changes are marked using a warning symbol: ⚠️
_The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)._
## Versioning
The H3 core library adheres to [Semantic Versioning](http://semver.org/).
H3-pg has a `major.minor.patch` version scheme. The major and minor version
numbers of H3-pg are the major and minor version of the bound core library,
respectively. The patch version is incremented independently of the core
library.
Because H3-pg is versioned in lockstep with the H3 core library, please
avoid adding features or APIs which do not map onto the
[H3 core API](https://uber.github.io/h3/#/documentation/api-reference/).
## [Unreleased]
Changes that have landed in master but are not yet released.
Click to see more.
## [4.2.2] - 2025-02-10
- More upstream copy/paste in support of Debian package (see [#169], thanks [@df7cb])
## [4.2.1] - 2025-02-04
- Copy `h3Index.h` from upstream in support of Debian package (see [#169], thanks [@df7cb])
## [4.2.0] - 2025-01-17
- Bump `h3` to `v4.2.0`
- Add `h3_polygon_to_cells_experimental` (see [#159], thanks [@jmealo])
- Add experimental SP-GIST operator class (see [#43], thanks [@BielStela])
- Fix for MacOS in nixpkgs / NixOS (see [#141], thanks [@wolfgangwalther])
## [4.1.4] - 2024-11-06
- Fix library extension on macOS for PostgreSQL >= 16 (see [#140], thanks [@bayandin])
- Add support for binary I/O (see [#160], thanks [@robertozimek])
## [4.1.3] - 2023-07-26
- Add `geometry @ int` and `geography @ int` operators, as shortcuts for `h3_lat_lng_to_cell`.
- Explain PostGIS SRID expectations (see [#131], thanks [@rustprooflabs])
## [4.1.2] - 2023-02-08
- *Actually* fix `h3_postgis` upgrade path (see [#117], thanks [@mngr777])
## [4.1.1] - 2023-02-07
- Add `postgis_raster` integration (see [#112], thanks [@mngr777])
- Fix `h3_postgis` upgrade path (see [#117], thanks [@mngr777])
## [4.1.0] - 2023-01-18
- Bump `h3` to `v4.1.0`
- Add bindings for `h3_cell_to_child_pos` and `h3_child_pos_to_cell`.
- Use CMake for entire build (see [#70])
- Add helper to fix pass-by-value migration (see [#111])
- Allow distance operator `<->` to work for cells at different resolutions (using center child).
## [4.0.3] - 2022-11-04
- Add BRIN operator class (see [#97], thanks [@mngr777])
- Split PostGIS multipolygons by 180th meridian (see [#90], thanks [@mngr777])
- Add aggregate functions for `h3_postgis` (see [#91], thanks [@mngr777])
- Add recursive `h3_grid_path_cells` (see [#93], thanks [@mngr777])
## [4.0.2] - 2022-09-19
- Fix broken function renames (see [#87], thanks [@Kompza], [@mngr777])
## [4.0.1] - 2022-09-19
- Bump `h3` to `v4.0.1` (was locked on `rc5` for the previous release)
- Add compile flag support check (see [#78], thanks [@mngr777])
- Split polygons by 180th meridian (see [#76], thanks [@mngr777])
## [4.0.0] - 2022-08-24
- ⚠️ Update `h3` core library to `v4.0.0`. Almost all functions have been renamed to align with `h3` core `v4`. Please take care when updating. You can see a [list of changed function names](https://h3geo.org/docs/library/migration-3.x/functions) in the core library documentation.
- ⚠️ The PostGIS functions has been extracted into a separate extension `h3_postgis`. Install using `CREATE EXTENSION h3_postgis;`.
- Enable link time optimization (see [#75], thanks [@mngr777])
- Handle non-polygons for `h3_polyfill` (see [#55], thanks [@Lokks])
- Take advantage of the new v4 error codes (fixes [#71], thanks [@kalenikaliaksandr])
## [3.7.2] - 2022-04-13
- Allow `NULL` in `holes` array for `h3_polyfill` (see [#64], thanks [@mngr777])
- Allow >1Gb memory allocations for `h3_polyfill` (see [#65], thanks [@mngr777])
## [3.7.1] - 2021-06-23
- Update `h3` core library to `v3.7.1`
## [3.7.0] - 2020-09-30
- ⚠️ Default unit for `h3_hex_area` and `h3_edge_length` changed to kilometers
- Update `h3` core library to `v3.7.0`
- Add `h3_point_dist` and `h3_exact_edge_length` bindings
- Add distance operator `<->`
- Fix `h3_to_geography` and `h3_to_geometry` refering to removed functions if extension was upgraded from pre-1.0
- Add optional input validation in geoToH3 (see [#41], thanks [@trylinka])
- Support unit as string argument in `h3_hex_area` and `h3_edge_length` (and flag previous version for deprecation)
## [3.6.5] - 2020-08-14
- Add support for partitioning by hash (see [#37], thanks [@abelvm])
- Fix difference in function flags between fresh install and upgrades (see [#38], thanks [@abelvm])
## [3.6.4] - 2020-06-29
- Update `h3` core library to `v3.6.4`
## [3.6.3] - 2020-04-08
- Build `h3` core using release flag for 2x/3x performance (see [#23], thanks [@komzpa])
## [3.6.2] - 2020-04-07
**This update will corrupt your h3indexes unless your are using a 32-bit build of PostgreSQL (see [#31]). If you upgrade to `4.0.4` you can use the function `h3_pg_migrate_pass_by_reference(h3index)` to retrieve your old h3 cells. See [#111].**
- Add parallel safety flags to PostGIS functions (see [#19], thanks [@komzpa])
- Add B-Tree sort support (see [#24], thanks [@komzpa])
- ⚠️ Make type `h3index` pass-by-value on supported systems (see [#22], [#26], thanks [@komzpa])
- Update `h3` core library to `v3.6.3`
## [3.6.1] - 2019-12-09
- Add `&&`, `@>` and `<@` operators for overlaps, contains and contained by respectively
- Fix PostgreSQL 12 build (see [#18], thanks [@komzpa])
- Update `h3` core library to `v3.6.1`
## [3.6.0] - 2019-10-07
- Add support for `bigint` cast (see [#9], thanks [@kmacdough])
- Add `h3_to_center_child` binding
- Add `h3_get_pentagon_indexes` binding
- Update `h3` core library to `v3.6.0`
## [3.5.0] - 2019-08-01
- Add `h3_get_faces` binding
- ⚠️ Replace `h3_hex_area_m2` and `h3_hex_area_km2` with `h3_hex_area`
- ⚠️ Replace `h3_edge_length_m` and `h3_edge_length_km` with `h3_edge_length`
- ⚠️ Remove `hex_range`, `hex_ranges` and `hex_range_distances`
- Remove `h3` core library version check, since we know which version we are linking
- Fix PostgreSQL 12 build (see [#4], thanks [@komzpa])
- Update `h3` core library to `v3.5.0`
## [3.4.1] - 2019-06-14
- Fix `abs` warning
## [3.4.0] - 2019-06-13
- ⚠️ Remove degree/radian conversion helpers (in favor of built-in RADIANS/DEGREES)
## [1.0.6] - 2019-06-03
- Update `h3` core library to `v3.4.4`
## [1.0.5] - 2019-02-15
- Fix update path
## [1.0.4] - 2019-02-15
- Fix `polyfill` for polygon with multiple holes
## [1.0.3] - 2019-01-27
- Fix update path
## [1.0.2] - 2019-01-27
- Remove git tag check in `distribute` makefile target, since it causes error on pgxn install
## [1.0.1] - 2019-01-27
- Remove usage of `FALSE` instead of 0 in conditional in `ASSERT` macro
## [1.0.0] - 2019-01-27
- Add `h3_get_extension_version()`
- Add hash operator class, now `WHERE IN` works
- ⚠️ Replace `h3_basecells` with `h3_get_res_0_indexes`
- ⚠️ Rename all functions with double `h3_h3_` prefix to use single `h3_` prefix
- ⚠️ Remove `h3_haversine_distance` function
- ⚠️ Change Makefile such that the `h3` core library is cloned, built and statically linked
- Test that upgrade path has same result as fresh install
## [0.4.0] - 2019-01-12
- Add `h3_line` binding
- Fix `h3_h3_to_children_slow`
## [0.3.2] - 2019-01-08
- ⚠️ Fix `btree` operator class indexing
## [0.3.1] - 2018-12-17
- Add `extend` flag to `h3_h3_to_geo_boundary` such that polygons are not wrapped at antimeridian
## 0.3.0 - 2018-12-11
- Initial public release
[unreleased]: https://github.com/zachasme/h3-pg/compare/v4.2.2...HEAD
[4.2.2]: https://github.com/zachasme/h3-pg/compare/v4.2.1...v4.2.2
[4.2.1]: https://github.com/zachasme/h3-pg/compare/v4.2.0...v4.2.1
[4.2.0]: https://github.com/zachasme/h3-pg/compare/v4.1.4...v4.2.0
[4.1.4]: https://github.com/zachasme/h3-pg/compare/v4.1.3...v4.1.4
[4.1.3]: https://github.com/zachasme/h3-pg/compare/v4.1.2...v4.1.3
[4.1.2]: https://github.com/zachasme/h3-pg/compare/v4.1.1...v4.1.2
[4.1.1]: https://github.com/zachasme/h3-pg/compare/v4.1.0...v4.1.1
[4.1.0]: https://github.com/zachasme/h3-pg/compare/v4.0.3...v4.1.0
[4.0.3]: https://github.com/zachasme/h3-pg/compare/v4.0.2...v4.0.3
[4.0.2]: https://github.com/zachasme/h3-pg/compare/v4.0.1...v4.0.2
[4.0.1]: https://github.com/zachasme/h3-pg/compare/v4.0.0...v4.0.1
[4.0.0]: https://github.com/zachasme/h3-pg/compare/v3.7.2...v4.0.0
[3.7.2]: https://github.com/zachasme/h3-pg/compare/v3.7.1...v3.7.2
[3.7.1]: https://github.com/zachasme/h3-pg/compare/v3.7.0...v3.7.1
[3.7.0]: https://github.com/zachasme/h3-pg/compare/v3.6.5...v3.7.0
[3.6.5]: https://github.com/zachasme/h3-pg/compare/v3.6.4...v3.6.5
[3.6.4]: https://github.com/zachasme/h3-pg/compare/v3.6.3...v3.6.4
[3.6.3]: https://github.com/zachasme/h3-pg/compare/v3.6.2...v3.6.3
[3.6.2]: https://github.com/zachasme/h3-pg/compare/v3.6.1...v3.6.2
[3.6.1]: https://github.com/zachasme/h3-pg/compare/v3.6.0...v3.6.1
[3.6.0]: https://github.com/zachasme/h3-pg/compare/v3.5.0...v3.6.0
[3.5.0]: https://github.com/zachasme/h3-pg/compare/v3.4.1...v3.5.0
[3.4.1]: https://github.com/zachasme/h3-pg/compare/v3.4.0...v3.4.1
[3.4.0]: https://github.com/zachasme/h3-pg/compare/v1.0.6...v3.4.0
[1.0.6]: https://github.com/zachasme/h3-pg/compare/v1.0.5...v1.0.6
[1.0.5]: https://github.com/zachasme/h3-pg/compare/v1.0.4...v1.0.5
[1.0.4]: https://github.com/zachasme/h3-pg/compare/v1.0.3...v1.0.4
[1.0.3]: https://github.com/zachasme/h3-pg/compare/v1.0.2...v1.0.3
[1.0.2]: https://github.com/zachasme/h3-pg/compare/v1.0.1...v1.0.2
[1.0.1]: https://github.com/zachasme/h3-pg/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/zachasme/h3-pg/compare/v0.4.0...v1.0.0
[0.4.0]: https://github.com/zachasme/h3-pg/compare/v0.3.2...v0.4.0
[0.3.2]: https://github.com/zachasme/h3-pg/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/zachasme/h3-pg/compare/v0.3.0...v0.3.1
[#4]: https://github.com/zachasme/h3-pg/pull/4
[#9]: https://github.com/zachasme/h3-pg/pull/9
[#18]: https://github.com/zachasme/h3-pg/pull/18
[#19]: https://github.com/zachasme/h3-pg/pull/19
[#22]: https://github.com/zachasme/h3-pg/pull/22
[#23]: https://github.com/zachasme/h3-pg/issues/23
[#24]: https://github.com/zachasme/h3-pg/pull/24
[#26]: https://github.com/zachasme/h3-pg/pull/26
[#31]: https://github.com/zachasme/h3-pg/pull/31
[#37]: https://github.com/zachasme/h3-pg/issues/37
[#38]: https://github.com/zachasme/h3-pg/issues/38
[#41]: https://github.com/zachasme/h3-pg/issues/41
[#43]: https://github.com/zachasme/h3-pg/issues/43
[#55]: https://github.com/zachasme/h3-pg/issues/55
[#64]: https://github.com/zachasme/h3-pg/issues/64
[#65]: https://github.com/zachasme/h3-pg/pull/65
[#70]: https://github.com/zachasme/h3-pg/pull/70
[#71]: https://github.com/zachasme/h3-pg/issues/71
[#75]: https://github.com/zachasme/h3-pg/pull/75
[#76]: https://github.com/zachasme/h3-pg/pull/76
[#78]: https://github.com/zachasme/h3-pg/pull/78
[#87]: https://github.com/zachasme/h3-pg/pull/87
[#90]: https://github.com/zachasme/h3-pg/pull/90
[#91]: https://github.com/zachasme/h3-pg/pull/91
[#93]: https://github.com/zachasme/h3-pg/pull/93
[#97]: https://github.com/zachasme/h3-pg/pull/97
[#111]: https://github.com/zachasme/h3-pg/pull/111
[#112]: https://github.com/zachasme/h3-pg/pull/112
[#117]: https://github.com/zachasme/h3-pg/issues/117
[#131]: https://github.com/zachasme/h3-pg/pull/131
[#140]: https://github.com/zachasme/h3-pg/pull/140
[#141]: https://github.com/zachasme/h3-pg/pull/141
[#159]: https://github.com/zachasme/h3-pg/pull/159
[#160]: https://github.com/zachasme/h3-pg/pull/160
[#169]: https://github.com/zachasme/h3-pg/issues/169
[@abelvm]: https://github.com/AbelVM
[@bayandin]: https://github.com/bayandin
[@BielStela]: https://github.com/BielStela
[@df7cb]: https://github.com/df7cb
[@jmealo]: https://github.com/jmealo
[@kalenikaliaksandr]: https://github.com/kalenikaliaksandr
[@kmacdough]: https://github.com/kmacdough
[@komzpa]: https://github.com/Komzpa
[@lokks]: https://github.com/Lokks
[@mngr777]: https://github.com/mngr777
[@robertozimek]: https://github.com/robertozimek
[@rustprooflabs]: https://github.com/rustprooflabs
[@trylinka]: https://github.com/trylinka
[@wolfgangwalther]: https://github.com/wolfgangwalther
h3-pg-4.2.2/CITATION.cff 0000664 0000000 0000000 00000000474 14752347156 0014352 0 ustar 00root root 0000000 0000000 cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Knudsen"
given-names: "Zacharias"
orcid: https://orcid.org/0000-0003-3662-8396
title: "h3-pg"
version: v3.7.2
doi: 10.5281/zenodo.6856596
date-released: 2022-04-13
url: "https://github.com/zachasme/h3-pg" h3-pg-4.2.2/CMakeLists.txt 0000664 0000000 0000000 00000005152 14752347156 0015216 0 ustar 00root root 0000000 0000000 # Copyright 2022 Zacharias Knudsen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Almost all CMake files should start with this
# You should always specify a range with the newest
# and oldest tested versions of CMake. This will ensure
# you pick up the best policies.
# Keep minimum version in sync with:
# https://github.com/uber/h3/blob/master/CMakeLists.txt
cmake_minimum_required(VERSION 3.20..3.24)
if(POLICY CMP0135)
# Set the timestamps of all extracted contents to the time of the extraction
cmake_policy(SET CMP0135 NEW)
endif()
# This is your project statement. You should always list languages;
# Listing the version is nice here since it sets lots of useful variables
project(
h3-pg
VERSION 4.2.2
LANGUAGES C
)
# set this to "${PROJECT_VERSION}" on release
set(INSTALL_VERSION "${PROJECT_VERSION}")
#set(INSTALL_VERSION "unreleased")
set(H3_CORE_VERSION 4.2.0)
set(H3_CORE_SHA256 438db46fc2b388785d2a0d8e26aa5509739240a7b50b2510c416778d871a4e11)
# If you set any CMAKE_ variables, that can go here.
# (But usually don't do this, except maybe for C++ standard)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# Allow forcing PostgreSQL version
set(POSTGRESQL_VERSION $ENV{POSTGRESQL_VERSION} CACHE STRING "PostgreSQL version major")
# Find packages go here.
find_package(PostgreSQL ${POSTGRESQL_VERSION} REQUIRED
OPTIONAL_COMPONENTS PostGIS
)
include(AddPostgreSQLExtension)
add_definitions(-DPOSTGRESQL_VERSION_MAJOR=${PostgreSQL_VERSION_MAJOR})
# https://cliutils.gitlab.io/modern-cmake/chapters/testing.html
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)
endif()
# Include core library
add_subdirectory(cmake/h3)
# Include extensions and their shared code
add_subdirectory(include)
add_subdirectory(h3)
add_subdirectory(h3_postgis)
# Add target that bundles for pgxn
configure_file(META.json.in META.json)
add_custom_target(pgxn
COMMAND git archive --format zip
--prefix="h3-${INSTALL_VERSION}/"
--add-file ${CMAKE_BINARY_DIR}/META.json
-o h3-${INSTALL_VERSION}.zip HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
h3-pg-4.2.2/LICENSE 0000775 0000000 0000000 00000023675 14752347156 0013500 0 ustar 00root root 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
h3-pg-4.2.2/META.json.in 0000664 0000000 0000000 00000002042 14752347156 0014477 0 ustar 00root root 0000000 0000000 {
"name": "h3",
"abstract": "PostgreSQL bindings for H3",
"description": "PostgreSQL bindings for H3, a hierarchical hexagonal geospatial indexing system.",
"version": "@PROJECT_VERSION@",
"maintainer": "Zacharias Knudsen ",
"license": "apache_2_0",
"provides": {
"h3": {
"abstract": "PostgreSQL bindings for H3",
"file": "h3--@PROJECT_VERSION@.sql",
"docfile": "README.md",
"version": "@PROJECT_VERSION@"
},
"h3_postgis": {
"abstract": "H3 PostGIS integration",
"file": "h3_postgis--@PROJECT_VERSION@.sql",
"docfile": "README.md",
"version": "@PROJECT_VERSION@"
}
},
"resources": {
"homepage": "https://github.com/zachasme/h3-pg",
"bugtracker": {
"web": "https://github.com/zachasme/h3-pg/issues"
},
"repository": {
"url": "https://github.com/zachasme/h3-pg.git",
"web": "https://github.com/zachasme/h3-pg",
"type": "git"
}
},
"meta-spec": {
"version": "1.0.0",
"url": "http://pgxn.org/meta/spec.txt"
}
}
h3-pg-4.2.2/Makefile 0000664 0000000 0000000 00000001523 14752347156 0014114 0 ustar 00root root 0000000 0000000 # Copyright 2024 Zacharias Knudsen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# this file only exists to support pgxnclient
.PHONY: all install installcheck
all:
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
install:
cmake --install build --component h3-pg
installcheck:
ctest --output-on-failure --build-config Release
h3-pg-4.2.2/README.md 0000664 0000000 0000000 00000007606 14752347156 0013743 0 ustar 00root root 0000000 0000000 # h3-pg: Uber's H3 Hexagonal Hierarchical Geospatial Indexing System in PostgreSQL
[](https://github.com/zachasme/h3-pg/actions)
[](https://github.com/zachasme/h3-pg/actions/workflows/test-macos.yml)
[](https://github.com/zachasme/h3-pg/actions/workflows/test-windows.yml)
[](LICENSE)
This library provides PostgreSQL bindings for the [H3 Core Library](https://github.com/uber/h3). For API reference, please see the [H3 Documentation](https://uber.github.io/h3).
Developed in collaboration with [Scandinavian Highlands](http://www.scandinavian-highlands.com).
## Binary distributions
These don't require you have the development headers or cmake installed.
### Debian/Ubuntu (Ubuntu 22.04 LTS (Jammy Jellyfish) +)
Replace 16 with your postgresql version.
Refer to [PGDG Ubuntu](https://www.postgresql.org/download/linux/ubuntu/) and [PGDG Debian](https://www.postgresql.org/download/linux/debian/) for installing PostgreSQL.
More details about the various packages at https://apt.postgresql.org
```
sudo apt install postgresql-16-h3
```
### Redhat Derivatives (Rocky / EL 8+ / Fedora 37+)
Replace 16 with your postgresql version
Refer to [PGDG Redhat Derivatives](https://www.postgresql.org/download/linux/redhat/) for installing PostgreSQL.
More details about the various packages at https://yum.postgresql.org
```
sudo yum install h3-pg_16
```
### Windows
Included as part of PostGIS Bundle 3.3+ for PostgreSQL 11-16
Details: [postgis.net windows](https://postgis.net/documentation/getting_started/install_windows/released_versions/)
Works with [PostgreSQL Windows](https://www.postgresql.org/download/windows/) and PostGIS bundle is accessible via the included Application Stackbuilder.
## Compiling Prerequisites
- PostgreSQL 11+ (_including server headers_, e.g. `postgresql-server-dev-14`)
- C compiler (e.g., `gcc`)
- [CMake](https://cmake.org/) 3.20+
- GNU Make
## Quick Overview
If the prerequisites are met you can use the [PGXN Client](docs/pgxnclient.md) to download, build, and install, e.g.:
```shell
$ pgxn install h3
$ pgxn load h3
$ psql
=# SELECT h3_lat_lng_to_cell(POINT('37.3615593,-122.0553238'), 5);
h3_lat_lng_to_cell
-----------------
85e35e73fffffff
(1 row)
```
(You can install a specific version using `pgxn install 'h3=3.7.2'` and `pgxn load 'h3=3.7.2'` for example)
See [Building](#building) for other installation methods.
## Usage
> :tada: **Note:** The following usage docs apply to **H3 v4**, which was released on August 23, 2022.
>
> - For v3 docs, [see the latest v3.x.x release](https://github.com/zachasme/h3-pg/blob/v3.7.2/README.md).
> - For breaking changes in v4, [see the CHANGELOG](./CHANGELOG.md). In particular, most [function names have changed](https://h3geo.org/docs/library/migration-3.x/functions).
Generally, all functions have been renamed from camelCase in H3 to snake\_case in SQL.
See [API reference](https://pgxn.org/dist/h3/docs/api.html) for all provided functions.
## Building
```bash
# Generate native build system
cmake -B build -DCMAKE_BUILD_TYPE=Release
# Build extension(s)
cmake --build build
# Install extensions (might require sudo)
cmake --install build --component h3-pg
```
## Contributing
Pull requests and GitHub issues are welcome. Please include tests for new work. Please note that the purpose of this extension is to expose the API of the H3 Core library, so we will rarely accept new features that are not part of that API. New proposed feature work is more appropriate in the core C library or in a new extension that depends on h3-pg.
See [Development](docs/development.md).
## License
This project is released under the [Apache 2.0 License](LICENSE.md).
h3-pg-4.2.2/cmake/ 0000775 0000000 0000000 00000000000 14752347156 0013533 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/cmake/AddPostgreSQLExtension.cmake 0000664 0000000 0000000 00000006304 14752347156 0021051 0 ustar 00root root 0000000 0000000 # Add pg_regress binary
find_program(PostgreSQL_REGRESS pg_regress
HINTS
"${PostgreSQL_PKG_LIBRARY_DIR}/pgxs/src/test/regress/"
"${PostgreSQL_BIN_DIR}"
)
# Add pg_validate_extupgrade binary
find_program(PostgreSQL_VALIDATE_EXTUPGRADE pg_validate_extupgrade)
# Add pgindent binary
find_program(PostgreSQL_INDENT pgindent)
# Helper command to add extensions
function(PostgreSQL_add_extension LIBRARY_NAME)
set(options RELOCATABLE)
set(oneValueArgs NAME COMMENT VERSION COMPONENT)
set(multiValueArgs REQUIRES SOURCES INSTALLS UPDATES)
cmake_parse_arguments(EXTENSION "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Default extension name to same as library name
if(NOT EXTENSION_NAME)
set(EXTENSION_NAME ${LIBRARY_NAME})
endif()
# Allow extensions without sources
if(EXTENSION_SOURCES)
# Add extension as a dynamically linked library
add_library(${LIBRARY_NAME} MODULE ${EXTENSION_SOURCES})
# Link extension to PostgreSQL
target_link_libraries(${LIBRARY_NAME} PRIVATE PostgreSQL::PostgreSQL)
# Handle macOS specifics
if(APPLE)
# Fix apple missing symbols
set_target_properties(${LIBRARY_NAME} PROPERTIES LINK_FLAGS ${PostgreSQL_LINK_FLAGS})
# Since Postgres 16, the shared library extension on macOS is `dylib`, not `so`.
# Ref https://github.com/postgres/postgres/commit/b55f62abb2c2e07dfae99e19a2b3d7ca9e58dc1a
if (${PostgreSQL_VERSION_MAJOR} VERSION_GREATER_EQUAL "16")
set_target_properties (${LIBRARY_NAME} PROPERTIES SUFFIX ".dylib")
endif()
endif()
# Final touches on output file
set_target_properties(${LIBRARY_NAME} PROPERTIES
OUTPUT_NAME ${EXTENSION_NAME}
INTERPROCEDURAL_OPTIMIZATION TRUE
#C_VISIBILITY_PRESET hidden # @TODO: how to get this working?
PREFIX "" # Avoid lib* prefix on output file
)
# Install .so/.dll to pkglib-dir
install(
TARGETS ${LIBRARY_NAME}
LIBRARY DESTINATION "${PostgreSQL_PKG_LIBRARY_DIR}"
COMPONENT ${EXTENSION_COMPONENT}
)
endif()
# Generate .control file
string(REPLACE ";" ", " EXTENSION_REQUIRES "${EXTENSION_REQUIRES}")
configure_file(
${CMAKE_SOURCE_DIR}/cmake/control.in
${EXTENSION_NAME}.control
)
# Generate .sql install file
set(EXTENSION_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/${EXTENSION_NAME}--${EXTENSION_VERSION}.sql)
file(WRITE "${EXTENSION_INSTALL}.in" "")
foreach(file ${EXTENSION_INSTALLS})
file(READ ${file} CONTENTS)
file(APPEND "${EXTENSION_INSTALL}.in" "${CONTENTS}")
endforeach()
configure_file("${EXTENSION_INSTALL}.in" "${EXTENSION_INSTALL}" COPYONLY)
# Install everything else into share-dir
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${EXTENSION_NAME}.control
${EXTENSION_INSTALL}
${EXTENSION_UPDATES}
DESTINATION "${PostgreSQL_SHARE_DIR}/extension"
COMPONENT ${EXTENSION_COMPONENT}
)
# Setup auto-format
if(PostgreSQL_INDENT)
add_custom_target("format_${EXTENSION_NAME}"
COMMAND ${PostgreSQL_INDENT} ${EXTENSION_SOURCES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Formatting ${EXTENSION_NAME} sources"
)
add_dependencies(${LIBRARY_NAME} "format_${EXTENSION_NAME}")
endif()
endfunction()
h3-pg-4.2.2/cmake/FindPostgreSQL.cmake 0000664 0000000 0000000 00000020227 14752347156 0017344 0 ustar 00root root 0000000 0000000 # Based on:
# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#find-modules
# ----------------------------------------------------------------------------
# https://www.postgresql.org/support/versioning/
set(PostgreSQL_SUPPORTED_VERSIONS ${PostgreSQL_ADDITIONAL_VERSIONS}
"17" "16" "15" "14" "13")
# Use `FIND_VERSION` to locate specific version of pg_config, or fallback to known versions
if(PostgreSQL_FIND_VERSION)
list(PREPEND PostgreSQL_SUPPORTED_VERSIONS "${PostgreSQL_FIND_VERSION_MAJOR}")
endif()
foreach(suffix ${PostgreSQL_SUPPORTED_VERSIONS})
if(WIN32)
list(APPEND PostgreSQL_CONFIG_PATH_SUFFIXES
"PostgreSQL/${suffix}/bin")
endif()
if(UNIX)
list(APPEND PostgreSQL_CONFIG_HINTS
"/usr/lib/postgresql/${suffix}/bin")
endif()
endforeach()
# Configuration will be based on values gathered from `pg_config`
find_program(PostgreSQL_CONFIG pg_config REQUIRED
HINTS ${PostgreSQL_CONFIG_HINTS}
PATH_SUFFIXES ${PostgreSQL_CONFIG_PATH_SUFFIXES}
)
# Grab information about the installed version of PostgreSQL
execute_process(COMMAND ${PostgreSQL_CONFIG} --version OUTPUT_VARIABLE PostgreSQL_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PostgreSQL_CONFIG} --bindir OUTPUT_VARIABLE PostgreSQL_BIN_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PostgreSQL_CONFIG} --sharedir OUTPUT_VARIABLE PostgreSQL_SHARE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PostgreSQL_CONFIG} --includedir OUTPUT_VARIABLE PostgreSQL_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PostgreSQL_CONFIG} --pkgincludedir OUTPUT_VARIABLE PostgreSQL_PKG_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PostgreSQL_CONFIG} --includedir-server OUTPUT_VARIABLE PostgreSQL_SERVER_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PostgreSQL_CONFIG} --libdir OUTPUT_VARIABLE PostgreSQL_LIBRARY_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PostgreSQL_CONFIG} --pkglibdir OUTPUT_VARIABLE PostgreSQL_PKG_LIBRARY_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
# @TODO: Figure out if we need _INCLUDE_DIR and/or _PKG_INCLUDE_DIR
# Create include dirs list
list(APPEND PostgreSQL_INCLUDE_DIRS "${PostgreSQL_INCLUDE_DIR}" "${PostgreSQL_PKG_INCLUDE_DIR}" "${PostgreSQL_SERVER_INCLUDE_DIR}")
list(APPEND PostgreSQL_LIBRARY_DIRS "${PostgreSQL_LIBRARY_DIR}")
# Set library to search for (which is different on WIN32)
set(FIND_LIBRARY pq)
# Platform fixes: Windows
# https://wiki.postgresql.org/wiki/Building_and_Installing_PostgreSQL_Extension_Modules
if(WIN32)
set(FIND_LIBRARY postgres)
list(APPEND PostgreSQL_INCLUDE_DIRS "${PostgreSQL_SERVER_INCLUDE_DIR}/port/win32")
if(MSVC)
list(APPEND PostgreSQL_INCLUDE_DIRS "${PostgreSQL_SERVER_INCLUDE_DIR}/port/win32_msvc")
endif(MSVC)
endif(WIN32)
# Platform fixes: MacOS
# https://github.com/postgres/postgres/blob/master/src/makefiles/Makefile.darwin
set(PostgreSQL_LINK_FLAGS "")
# @TODO: Find out if this can be done in a more cmakey way
if(APPLE)
find_program(PostgreSQL_EXECUTABLE postgres REQUIRED NO_DEFAULT_PATH PATHS ${PostgreSQL_BIN_DIR})
set(PostgreSQL_LINK_FLAGS "-bundle_loader ${PostgreSQL_EXECUTABLE}")
endif()
# ----------------------------------------------------------------------------
# Now we need to find the libraries and include files
# if the library is available with multiple configurations, you can use SelectLibraryConfigurations
# to automatically set the _LIBRARY variable:
# @TODO: Find out if we can actually find any debug libraries on each platform
find_library(PostgreSQL_LIBRARY_RELEASE
NAMES ${FIND_LIBRARY}
PATHS ${PostgreSQL_LIBRARY_DIRS}
)
find_library(PostgreSQL_LIBRARY_DEBUG
NAMES ${FIND_LIBRARY}d # <-- debug
PATHS ${PostgreSQL_LIBRARY_DIRS}
)
include(SelectLibraryConfigurations)
select_library_configurations(PostgreSQL)
# ----------------------------------------------------------------------------
# If you have a good way of getting the version (from a header file, for example),
# you can use that information to set Foo_VERSION (although note that find modules have
# traditionally used Foo_VERSION_STRING, so you may want to set both).
string(REGEX MATCHALL "[0-9]+" PostgreSQL_VERSION_LIST "${PostgreSQL_VERSION}")
list(GET PostgreSQL_VERSION_LIST 0 PostgreSQL_VERSION_MAJOR)
list(GET PostgreSQL_VERSION_LIST 1 PostgreSQL_VERSION_MINOR)
set(PostgreSQL_VERSION "${PostgreSQL_VERSION_MAJOR}.${PostgreSQL_VERSION_MINOR}")
# ----------------------------------------------------------------------------
# Handle additional components
if("PostGIS" IN_LIST PostgreSQL_FIND_COMPONENTS)
find_library(
POSTGIS
NAMES postgis postgis-3 postgis-3.so
PATHS ${PostgreSQL_PKG_LIBRARY_DIR}
)
set(PostgreSQL_PostGIS_FOUND TRUE)
if(NOT POSTGIS)
set(PostgreSQL_PostGIS_FOUND FALSE)
endif()
endif()
# ----------------------------------------------------------------------------
# Now we can use FindPackageHandleStandardArgs to do
# most of the rest of the work for us
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PostgreSQL
REQUIRED_VARS
PostgreSQL_LIBRARY
PostgreSQL_INCLUDE_DIRS
HANDLE_COMPONENTS
VERSION_VAR PostgreSQL_VERSION
)
# ----------------------------------------------------------------------------
# At this point, we have to provide a way for users of the find module to link to the library or libraries that were found.
#
# If the library is available with multiple configurations, the IMPORTED_CONFIGURATIONS target property should also be populated:
if(PostgreSQL_FOUND)
# When providing imported targets, these should be namespaced (hence the Foo:: prefix);
# CMake will recognize that values passed to target_link_libraries() that contain :: in their name
# are supposed to be imported targets (rather than just library names),
if (NOT TARGET PostgreSQL::PostgreSQL)
add_library(PostgreSQL::PostgreSQL UNKNOWN IMPORTED)
endif()
# The RELEASE variant should be listed first in the property so that the variant is chosen
# if the user uses a configuration which is not an exact match for any listed IMPORTED_CONFIGURATIONS.
if (PostgreSQL_LIBRARY_RELEASE)
set_property(TARGET PostgreSQL::PostgreSQL APPEND PROPERTY
IMPORTED_CONFIGURATIONS RELEASE
)
set_target_properties(PostgreSQL::PostgreSQL PROPERTIES
IMPORTED_LOCATION_RELEASE "${PostgreSQL_LIBRARY_RELEASE}"
)
endif()
if (PostgreSQL_LIBRARY_DEBUG)
set_property(TARGET PostgreSQL::PostgreSQL APPEND PROPERTY
IMPORTED_CONFIGURATIONS DEBUG
)
set_target_properties(PostgreSQL::PostgreSQL PROPERTIES
IMPORTED_LOCATION_DEBUG "${PostgreSQL_LIBRARY_DEBUG}"
)
endif()
set_target_properties(PostgreSQL::PostgreSQL PROPERTIES
#INTERFACE_COMPILE_OPTIONS "${PostgreSQL_CFLAGS_OTHER}"
INTERFACE_INCLUDE_DIRECTORIES "${PostgreSQL_INCLUDE_DIRS}"
)
endif()
# Most of the cache variables should be hidden in the `ccmake` interface unless the user explicitly asks to edit them.
mark_as_advanced(
PostgreSQL_INCLUDE_DIRS
PostgreSQL_LIBRARY
)
# ----------------------------------------------------------------------------
# Traditional variables (from pkg-config):
#
# _FOUND has the package been found
# _LIBRARIES ?
# _LINK_LIBRARIES ?
# _LIBRARY_DIRS ?
# _LDFLAGS ?
# _LDFLAGS_OTHER ?
# _INCLUDE_DIRS ?
# _CFLAGS ?
# _CFLAGS_OTHER required non-include-dir CFLAGS to stdout
#
# _VERSION full version of found package
# _PREFIX ?
# _INCLUDEDIR ?
# _LIBDIR ?
# debug
message(STATUS "Find version: ${PostgreSQL_FIND_VERSION}")
message(STATUS "Find major: ${PostgreSQL_FIND_VERSION_MAJOR}")
message(STATUS "Find minor: ${PostgreSQL_FIND_VERSION_MINOR}")
message(STATUS "Found version: ${PostgreSQL_VERSION}")
message(STATUS "Found major: ${PostgreSQL_VERSION_MAJOR}")
message(STATUS "Found minor: ${PostgreSQL_VERSION_MINOR}")
# Get all variables
GET_CMAKE_PROPERTY(vars VARIABLES)
FOREACH(var ${vars})
STRING(REGEX MATCH "^PostgreSQL_" item ${var})
IF(item)
message(STATUS "${var} = ${${var}}")
ENDIF(item)
ENDFOREACH(var)
h3-pg-4.2.2/cmake/control.in 0000664 0000000 0000000 00000000220 14752347156 0015535 0 ustar 00root root 0000000 0000000 comment = '@EXTENSION_COMMENT@'
default_version = '@EXTENSION_VERSION@'
relocatable = @EXTENSION_RELOCATABLE@
requires = '@EXTENSION_REQUIRES@'
h3-pg-4.2.2/cmake/h3/ 0000775 0000000 0000000 00000000000 14752347156 0014045 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/cmake/h3/CMakeLists.txt 0000664 0000000 0000000 00000001174 14752347156 0016610 0 ustar 00root root 0000000 0000000 include(FetchContent)
# Skip core tooling we won't need for extension
set(ENABLE_COVERAGE OFF)
set(BUILD_BENCHMARKS OFF)
set(BUILD_FUZZERS OFF)
set(BUILD_FILTERS OFF)
set(BUILD_GENERATORS OFF)
set(ENABLE_FORMAT OFF)
set(ENABLE_LINTING OFF)
set(ENABLE_DOCS OFF)
FetchContent_Declare(
h3
URL https://github.com/uber/h3/archive/refs/tags/v${H3_CORE_VERSION}.tar.gz
URL_HASH SHA256=${H3_CORE_SHA256}
)
FetchContent_MakeAvailable(h3)
set_target_properties(h3 PROPERTIES
INTERPROCEDURAL_OPTIMIZATION TRUE
C_VISIBILITY_PRESET hidden
)
set(H3_INCLUDE_DIR ${h3_BINARY_DIR}/src/h3lib/include)
h3-pg-4.2.2/docs/ 0000775 0000000 0000000 00000000000 14752347156 0013403 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/docs/api.md 0000664 0000000 0000000 00000056235 14752347156 0014511 0 ustar 00root root 0000000 0000000 # API Reference
The `h3` extension wraps the [H3 Core Library](https://github.com/uber/h3).
The detailed API reference is in the core [H3 Documentation](https://uber.github.io/h3) under the API Reference section.
The `h3` core functions have been renamed from camelCase in H3 core to snake\_case in SQL.
The SQL function name is prefixed with `h3_`.
# Base type
An unsigned 64-bit integer representing any H3 object (hexagon, pentagon, directed edge ...)
represented as a (or 16-character) hexadecimal string, like '8928308280fffff'.
# Indexing functions
These function are used for finding the H3 index containing coordinates,
and for finding the center and boundary of H3 indexes.
### h3_lat_lng_to_cell(latlng `point`, resolution `integer`) ⇒ `h3index`
*Since v4.0.0*
See also: h3_lat_lng_to_cell(`geometry`, `integer`), h3_lat_lng_to_cell(`geography`, `integer`)
Indexes the location at the specified resolution.
### h3_cell_to_lat_lng(cell `h3index`) ⇒ `point`
*Since v4.0.0*
See also: h3_cell_to_geometry(`h3index`), h3_cell_to_geography(`h3index`)
Finds the centroid of the index.
### h3_cell_to_boundary(cell `h3index`) ⇒ `polygon`
*Since v4.0.0*
See also: h3_cell_to_boundary_geometry(`h3index`), h3_cell_to_boundary_geography(`h3index`)
Finds the boundary of the index.
Use `SET h3.extend_antimeridian TO true` to extend coordinates when crossing 180th meridian.
# Index inspection functions
These functions provide metadata about an H3 index, such as its resolution
or base cell, and provide utilities for converting into and out of the
64-bit representation of an H3 index.
### h3_get_resolution(`h3index`) ⇒ `integer`
*Since v1.0.0*
Returns the resolution of the index.
### h3_get_base_cell_number(`h3index`) ⇒ `integer`
*Since v4.0.0*
Returns the base cell number of the index.
### h3_is_valid_cell(`h3index`) ⇒ `boolean`
*Since v1.0.0*
Returns true if the given H3Index is valid.
### h3_is_res_class_iii(`h3index`) ⇒ `boolean`
*Since v1.0.0*
Returns true if this index has a resolution with Class III orientation.
### h3_is_pentagon(`h3index`) ⇒ `boolean`
*Since v1.0.0*
Returns true if this index represents a pentagonal cell.
### h3_get_icosahedron_faces(`h3index`) ⇒ `integer[]`
*Since v4.0.0*
Find all icosahedron faces intersected by a given H3 index.
# Grid traversal functions
Grid traversal allows finding cells in the vicinity of an origin cell, and
determining how to traverse the grid from one cell to another.
### h3_grid_disk(origin `h3index`, [k `integer` = 1]) ⇒ SETOF `h3index`
*Since v4.0.0*
Produces indices within "k" distance of the origin index.
### h3_grid_disk_distances(origin `h3index`, [k `integer` = 1], OUT index `h3index`, OUT distance `int`) ⇒ SETOF `record`
*Since v4.0.0*
Produces indices within "k" distance of the origin index paired with their distance to the origin.
### h3_grid_ring_unsafe(origin `h3index`, [k `integer` = 1]) ⇒ SETOF `h3index`
*Since v4.0.0*
Returns the hollow hexagonal ring centered at origin with distance "k".
### h3_grid_path_cells(origin `h3index`, destination `h3index`) ⇒ SETOF `h3index`
*Since v4.0.0*
See also: h3_grid_path_cells_recursive(`h3index`, `h3index`)
Given two H3 indexes, return the line of indexes between them (inclusive).
This function may fail to find the line between two indexes, for
example if they are very far apart. It may also fail when finding
distances for indexes on opposite sides of a pentagon.
### h3_grid_distance(origin `h3index`, destination `h3index`) ⇒ `bigint`
*Since v4.0.0*
Returns the distance in grid cells between the two indices.
### h3_cell_to_local_ij(origin `h3index`, index `h3index`) ⇒ `point`
*Since v0.2.0*
Produces local IJ coordinates for an H3 index anchored by an origin.
### h3_local_ij_to_cell(origin `h3index`, coord `point`) ⇒ `h3index`
*Since v0.2.0*
Produces an H3 index from local IJ coordinates anchored by an origin.
# Hierarchical grid functions
These functions permit moving between resolutions in the H3 grid system.
The functions produce parent (coarser) or children (finer) cells.
### h3_cell_to_parent(cell `h3index`, resolution `integer`) ⇒ `h3index`
*Since v4.0.0*
Returns the parent of the given index.
### h3_cell_to_children(cell `h3index`, resolution `integer`) ⇒ SETOF `h3index`
*Since v4.0.0*
Returns the set of children of the given index.
### h3_cell_to_center_child(cell `h3index`, resolution `integer`) ⇒ `h3index`
*Since v4.0.0*
Returns the center child (finer) index contained by input index at given resolution.
### h3_compact_cells(cells `h3index[]`) ⇒ SETOF `h3index`
*Since v4.0.0*
Compacts the given array as best as possible.
### h3_cell_to_child_pos(child `h3index`, parentRes `integer`) ⇒ `int8`
*Since v4.1.0*
Returns the position of the child cell within an ordered list of all children of the cells parent at the specified resolution parentRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of childPosToCell.
### h3_child_pos_to_cell(childPos `int8`, parent `h3index`, childRes `int`) ⇒ `h3index`
*Since v4.1.0*
Returns the child cell at a given position within an ordered list of all children of parent at the specified resolution childRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of cellToChildPos.
### h3_uncompact_cells(cells `h3index[]`, resolution `integer`) ⇒ SETOF `h3index`
*Since v4.0.0*
Uncompacts the given array at the given resolution.
### h3_cell_to_parent(cell `h3index`) ⇒ `h3index`
*Since v4.0.0*
Returns the parent of the given index.
### h3_cell_to_children(cell `h3index`) ⇒ SETOF `h3index`
*Since v4.0.0*
Returns the set of children of the given index.
### h3_cell_to_center_child(cell `h3index`) ⇒ `h3index`
*Since v4.0.0*
Returns the center child (finer) index contained by input index at next resolution.
### h3_uncompact_cells(cells `h3index[]`) ⇒ SETOF `h3index`
*Since v4.0.0*
Uncompacts the given array at the resolution one higher than the highest resolution in the set.
### h3_cell_to_children_slow(index `h3index`, resolution `integer`) ⇒ SETOF `h3index`
*Since v4.0.0*
Slower version of H3ToChildren but allocates less memory.
### h3_cell_to_children_slow(index `h3index`) ⇒ SETOF `h3index`
Slower version of H3ToChildren but allocates less memory.
# Region functions
These functions convert H3 indexes to and from polygonal areas.
### h3_polygon_to_cells(exterior `polygon`, holes `polygon[]`, [resolution `integer` = 1]) ⇒ SETOF `h3index`
*Since v4.0.0*
See also: h3_polygon_to_cells(`geometry`, `integer`), h3_polygon_to_cells(`geography`, `integer`)
Takes an exterior polygon [and a set of hole polygon] and returns the set of hexagons that best fit the structure.
### h3_polygon_to_cells_experimental(exterior `polygon`, holes `polygon[]`, [resolution `integer` = 1], [containment_mode `text` = center]) ⇒ SETOF `h3index`
*Since v4.2.0*
Takes an exterior polygon [and a set of hole polygon] and returns the set of hexagons that best fit the structure.
### h3_cells_to_multi_polygon(`h3index[]`, OUT exterior `polygon`, OUT holes `polygon[]`) ⇒ SETOF `record`
*Since v4.0.0*
See also: h3_cells_to_multi_polygon_geometry(`h3index[]`), h3_cells_to_multi_polygon_geography(`h3index[]`), h3_cells_to_multi_polygon_geometry(setof `h3index`), h3_cells_to_multi_polygon_geography(setof `h3index`)
Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons. Polygon outlines will follow GeoJSON MultiPolygon order: Each polygon will have one outer loop, which is first in the list, followed by any holes.
# Unidirectional edge functions
Unidirectional edges allow encoding the directed edge from one cell to a
neighboring cell.
### h3_are_neighbor_cells(origin `h3index`, destination `h3index`) ⇒ `boolean`
*Since v4.0.0*
Returns true if the given indices are neighbors.
### h3_cells_to_directed_edge(origin `h3index`, destination `h3index`) ⇒ `h3index`
*Since v4.0.0*
Returns a unidirectional edge H3 index based on the provided origin and destination.
### h3_is_valid_directed_edge(edge `h3index`) ⇒ `boolean`
*Since v4.0.0*
Returns true if the given edge is valid.
### h3_get_directed_edge_origin(edge `h3index`) ⇒ `h3index`
*Since v4.0.0*
Returns the origin index from the given edge.
### h3_get_directed_edge_destination(edge `h3index`) ⇒ `h3index`
*Since v4.0.0*
Returns the destination index from the given edge.
### h3_directed_edge_to_cells(edge `h3index`, OUT origin `h3index`, OUT destination `h3index`) ⇒ `record`
*Since v4.0.0*
Returns the pair of indices from the given edge.
### h3_origin_to_directed_edges(`h3index`) ⇒ SETOF `h3index`
*Since v4.0.0*
Returns all unidirectional edges with the given index as origin.
### h3_directed_edge_to_boundary(edge `h3index`) ⇒ `polygon`
*Since v4.0.0*
Provides the coordinates defining the unidirectional edge.
# H3 Vertex functions
Functions for working with cell vertexes.
### h3_cell_to_vertex(cell `h3index`, vertexNum `integer`) ⇒ `h3index`
*Since v4.0.0*
Returns a single vertex for a given cell, as an H3 index.
### h3_cell_to_vertexes(cell `h3index`) ⇒ SETOF `h3index`
*Since v4.0.0*
Returns all vertexes for a given cell, as H3 indexes.
### h3_vertex_to_lat_lng(vertex `h3index`) ⇒ `point`
*Since v4.0.0*
Get the geocoordinates of an H3 vertex.
### h3_is_valid_vertex(vertex `h3index`) ⇒ `boolean`
*Since v4.0.0*
Whether the input is a valid H3 vertex.
# Miscellaneous H3 functions
These functions include descriptions of the H3 grid system.
### h3_great_circle_distance(a `point`, b `point`, [unit `text` = km]) ⇒ `double precision`
*Since v4.0.0*
The great circle distance in radians between two spherical coordinates.
### h3_get_hexagon_area_avg(resolution `integer`, [unit `text` = km]) ⇒ `double precision`
*Since v4.0.0*
Average hexagon area in square (kilo)meters at the given resolution.
### h3_cell_area(cell `h3index`, [unit `text` = km^2]) ⇒ `double precision`
*Since v4.0.0*
Exact area for a specific cell (hexagon or pentagon).
### h3_get_hexagon_edge_length_avg(resolution `integer`, [unit `text` = km]) ⇒ `double precision`
*Since v4.0.0*
Average hexagon edge length in (kilo)meters at the given resolution.
### h3_edge_length(edge `h3index`, [unit `text` = km]) ⇒ `double precision`
*Since v4.0.0*
Exact length for a specific unidirectional edge.
### h3_get_num_cells(resolution `integer`) ⇒ `bigint`
*Since v4.0.0*
Number of unique H3 indexes at the given resolution.
### h3_get_res_0_cells() ⇒ SETOF `h3index`
*Since v4.0.0*
Returns all 122 resolution 0 indexes.
### h3_get_pentagons(resolution `integer`) ⇒ SETOF `h3index`
*Since v4.0.0*
All the pentagon H3 indexes at the specified resolution.
# Operators
### Operator: `h3index` <-> `h3index`
*Since v3.7.0*
Returns the distance in grid cells between the two indices (at the lowest resolution of the two).
## B-tree operators
### Operator: `h3index` = `h3index`
*Since v0.1.0*
Returns true if two indexes are the same.
### Operator: `h3index` <> `h3index`
*Since v0.1.0*
## R-tree Operators
### Operator: `h3index` && `h3index`
*Since v3.6.1*
Returns true if the two H3 indexes intersect.
### Operator: `h3index` @> `h3index`
*Since v3.6.1*
Returns true if A contains B.
### Operator: `h3index` <@ `h3index`
*Since v3.6.1*
Returns true if A is contained by B.
## SP-GiST operator class (experimental)
*This is still an experimental feature and may change in future versions.*
Add an SP-GiST index using the `h3index_ops_experimental` operator class:
```sql
-- CREATE INDEX [indexname] ON [tablename] USING spgist([column] h3index_ops_experimental);
CREATE INDEX spgist_idx ON h3_data USING spgist(hex h3index_ops_experimental);
```
# Type casts
### `h3index` :: `bigint`
Convert H3 index to bigint, which is useful when you need a decimal representation.
### `bigint` :: `h3index`
Convert bigint to H3 index.
### `h3index` :: `point`
Convert H3 index to point.
# Extension specific functions
### h3_get_extension_version() ⇒ `text`
*Since v1.0.0*
Get the currently installed version of the extension.
### h3_pg_migrate_pass_by_reference(`h3index`) ⇒ `h3index`
*Since v4.1.0*
Migrate h3index from pass-by-reference to pass-by-value.
# Deprecated functions
### h3_cell_to_boundary(cell `h3index`, extend_antimeridian `boolean`) ⇒ `polygon`
DEPRECATED: Use `SET h3.extend_antimeridian TO true` instead.
# PostGIS Integration
The `GEOMETRY` data passed to `h3-pg` PostGIS functions should
be in SRID 4326. This is an expectation of the core H3 library.
Using other SRIDs, such as 3857, can result in either errors or
invalid data depending on the function.
For example, the `h3_polygon_to_cells()` function will fail with
an error in this scenario while the `h3_lat_lng_to_cell()` function
will return an invalid geometry.
# PostGIS Indexing Functions
### h3_lat_lng_to_cell(`geometry`, resolution `integer`) ⇒ `h3index`
*Since v4.0.0*
Indexes the location at the specified resolution.
### h3_lat_lng_to_cell(`geography`, resolution `integer`) ⇒ `h3index`
*Since v4.0.0*
Indexes the location at the specified resolution.
### h3_cell_to_geometry(`h3index`) ⇒ `geometry`
*Since v4.0.0*
Finds the centroid of the index.
### h3_cell_to_geography(`h3index`) ⇒ `geography`
*Since v4.0.0*
Finds the centroid of the index.
### h3_cell_to_boundary_geometry(`h3index`) ⇒ `geometry`
*Since v4.0.0*
Finds the boundary of the index.
Splits polygons when crossing 180th meridian.
### h3_cell_to_boundary_geography(`h3index`) ⇒ `geography`
*Since v4.0.0*
Finds the boundary of the index.
Splits polygons when crossing 180th meridian.
# PostGIS Grid Traversal Functions
### h3_grid_path_cells_recursive(origin `h3index`, destination `h3index`) ⇒ SETOF `h3index`
*Since v4.1.0*
# PostGIS Region Functions
### h3_polygon_to_cells(multi `geometry`, resolution `integer`) ⇒ SETOF `h3index`
*Since v4.0.0*
### h3_polygon_to_cells(multi `geography`, resolution `integer`) ⇒ SETOF `h3index`
*Since v4.0.0*
### h3_cells_to_multi_polygon_geometry(`h3index[]`) ⇒ `geometry`
*Since v4.1.0*
### h3_cells_to_multi_polygon_geography(`h3index[]`) ⇒ `geography`
*Since v4.1.0*
### h3_cells_to_multi_polygon_geometry(setof `h3index`)
*Since v4.1.0*
### h3_cells_to_multi_polygon_geography(setof `h3index`)
*Since v4.1.0*
### h3_polygon_to_cells_experimental(multi `geometry`, resolution `integer`, [containment_mode `text` = center]) ⇒ SETOF `h3index`
*Since v4.2.0*
### h3_polygon_to_cells_experimental(multi `geography`, resolution `integer`, [containment_mode `text` = center]) ⇒ SETOF `h3index`
*Since v4.2.0*
# PostGIS Operators
### Operator: `geometry` @ `integer`
*Since v4.1.3*
Index geometry at specified resolution.
### Operator: `geography` @ `integer`
*Since v4.1.3*
Index geography at specified resolution.
# PostGIS casts
### `h3index` :: `geometry`
*Since v0.3.0*
### `h3index` :: `geography`
*Since v0.3.0*
# WKB indexing functions
### h3_cell_to_boundary_wkb(cell `h3index`) ⇒ `bytea`
*Since v4.1.0*
Finds the boundary of the index, converts to EWKB.
Splits polygons when crossing 180th meridian.
This function has to return WKB since Postgres does not provide multipolygon type.
# WKB regions functions
### h3_cells_to_multi_polygon_wkb(`h3index[]`) ⇒ `bytea`
*Since v4.1.0*
Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons, converts to EWKB.
Splits polygons when crossing 180th meridian.
# Raster processing functions
## Continuous raster data
For rasters with pixel values representing continuous data (temperature, humidity,
elevation), the data inside H3 cells can be summarized by calculating number of
pixels, sum, mean, standard deviation, min and max for each cell inside a raster
and grouping these stats across multiple rasters by H3 index.
```
SELECT
(summary).h3 AS h3,
(h3_raster_summary_stats_agg((summary).stats)).*
FROM (
SELECT h3_raster_summary(rast, 8) AS summary
FROM rasters
) t
GROUP BY 1;
h3 | count | sum | mean | stddev | min | max
-----------------+-------+--------------------+---------------------+--------------------+-------+------------------
882d638189fffff | 10 | 4.607657432556152 | 0.46076574325561526 | 1.3822972297668457 | 0 | 4.607657432556152
882d64c4d1fffff | 10 | 3.6940908953547478 | 0.3694090895354748 | 1.099336879464068 | 0 | 3.667332887649536
882d607431fffff | 11 | 6.219290263950825 | 0.5653900239955295 | 1.7624673707119065 | 0 | 6.13831996917724
<...>
```
*Since v4.1.1*
### h3_raster_summary_stats_agg(setof `h3_raster_summary_stats`)
*Since v4.1.1*
### h3_raster_summary_clip(rast `raster`, resolution `integer`, [nband `integer` = 1]) ⇒ TABLE (h3 `h3index`, stats `h3_raster_summary_stats`)
*Since v4.1.1*
Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Clips the raster by H3 cell geometries and processes each part separately.
### h3_raster_summary_centroids(rast `raster`, resolution `integer`, [nband `integer` = 1]) ⇒ TABLE (h3 `h3index`, stats `h3_raster_summary_stats`)
*Since v4.1.1*
Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Finds corresponding H3 cell for each pixel, then groups values by H3 index.
### h3_raster_summary_subpixel(rast `raster`, resolution `integer`, [nband `integer` = 1]) ⇒ TABLE (h3 `h3index`, stats `h3_raster_summary_stats`)
*Since v4.1.1*
Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Assumes H3 cell is smaller than a pixel. Finds corresponding pixel for each H3 cell in raster.
### h3_raster_summary(rast `raster`, resolution `integer`, [nband `integer` = 1]) ⇒ TABLE (h3 `h3index`, stats `h3_raster_summary_stats`)
*Since v4.1.1*
Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Attempts to select an appropriate method based on number of pixels per H3 cell.
## Discrete raster data
For rasters where pixels have discrete values corresponding to different classes
of land cover or land use, H3 cell data summary can be represented by a JSON object
with separate fields for each class. First, value, number of pixels and approximate
area are calculated for each H3 cell and value in a raster, then the stats are
grouped across multiple rasters by H3 index and value, and after that stats for
different values in a cell are combined into a single JSON object.
The following example query additionally calculates a fraction of H3 cell pixels
for each value, using a window function to get a total number of pixels:
```
WITH
summary AS (
-- get aggregated summary for each H3 index/value pair
SELECT h3, val, h3_raster_class_summary_item_agg(summary) AS item
FROM
rasters,
h3_raster_class_summary(rast, 8)
GROUP BY 1, 2),
summary_total AS (
-- add total number of pixels per H3 cell
SELECT h3, val, item, sum((item).count) OVER (PARTITION BY h3) AS total
FROM summary)
SELECT
h3,
jsonb_object_agg(
concat('class_', val::text),
h3_raster_class_summary_item_to_jsonb(item) -- val, count, area
|| jsonb_build_object('fraction', (item).count / total) -- add fraction value
ORDER BY val
) AS summary
FROM summary_total
GROUP BY 1;
h3 | summary
----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------
88194e6f3bfffff | {"class_1": {"area": 75855.5748, "count": 46, "value": 1, "fraction": 0.4509}, "class_2": {"area": 92345.9171, "count": 56, "value": 2, "fraction": 0.5490}}
88194e6f37fffff | {"class_1": {"area": 255600.3064, "count": 155, "value": 1, "fraction": 0.5}, "class_2": {"area": 255600.3064, "count": 155, "value": 2, "fraction": 0.5}}
88194e6f33fffff | {"class_1": {"area": 336402.9840, "count": 204, "value": 1, "fraction": 0.5125}, "class_2": {"area": 319912.6416, "count": 194, "value": 2, "fraction": 0.4874}}
<...>
```
Area covered by pixels with the most frequent value in each cell:
```
SELECT DISTINCT ON (h3)
h3, val, (item).area
FROM (
SELECT
h3, val, h3_raster_class_summary_item_agg(summary) AS item
FROM
rasters,
h3_raster_class_summary(rast, 8)
GROUP BY 1, 2
) t
ORDER BY h3, (item).count DESC;
h3 | val | area
-----------------+-----+--------------------
88194e6f3bfffff | 5 | 23238.699360251427
88194e6f37fffff | 9 | 60863.26022922993
88194e6f33fffff | 8 | 76355.72646939754
<...>
```
*Since v4.1.1*
### h3_raster_class_summary_item_to_jsonb(item `h3_raster_class_summary_item`) ⇒ `jsonb`
*Since v4.1.1*
Convert raster summary to JSONB, example: `{"count": 10, "value": 2, "area": 16490.3423}`
### h3_raster_class_summary_item_agg(setof `h3_raster_class_summary_item`)
*Since v4.1.1*
### h3_raster_class_summary_clip(rast `raster`, resolution `integer`, [nband `integer` = 1]) ⇒ TABLE (h3 `h3index`, val `integer`, summary `h3_raster_class_summary_item`)
*Since v4.1.1*
Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Clips the raster by H3 cell geometries and processes each part separately.
### h3_raster_class_summary_centroids(rast `raster`, resolution `integer`, [nband `integer` = 1]) ⇒ TABLE (h3 `h3index`, val `integer`, summary `h3_raster_class_summary_item`)
*Since v4.1.1*
Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Finds corresponding H3 cell for each pixel, then groups by H3 and value.
### h3_raster_class_summary_subpixel(rast `raster`, resolution `integer`, [nband `integer` = 1]) ⇒ TABLE (h3 `h3index`, val `integer`, summary `h3_raster_class_summary_item`)
*Since v4.1.1*
Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Assumes H3 cell is smaller than a pixel. Finds corresponding pixel for each H3 cell in raster.
### h3_raster_class_summary(rast `raster`, resolution `integer`, [nband `integer` = 1]) ⇒ TABLE (h3 `h3index`, val `integer`, summary `h3_raster_class_summary_item`)
*Since v4.1.1*
Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Attempts to select an appropriate method based on number of pixels per H3 cell.
h3-pg-4.2.2/docs/development.md 0000664 0000000 0000000 00000002264 14752347156 0016253 0 ustar 00root root 0000000 0000000 ## Development
In order to build and test your changes, simply run `./scripts/develop`.
Documentation is generated from the sql files, using the script `scripts/documentaion` (requires poetry).
## Release Process
1. Update version number
- Don't follow semver, simply use major and minor from H3 core and increment patch.
- Version number should be changed in root `CMakeLists.txt`.
- Set `INSTALL_VERSION` to "${PROJECT_VERSION}".
- Update files (and cmake references) suffixed `--unreleased` should be renamed.
- Installer `.sql` files should have `@ availability` comments updated.
- Update changelog by moving from `Unreleased` to a new section
- Push and merge changes in `release-x.y.z` branch.
2. Create a release on GitHub
- Draft new release "vX.Y.Z"
- Copy CHANGELOG.md entry into release description
3. Distribute the extension on PGXN
- Run `scripts/bundle` to package the release
- Upload the distribution on [PGXN Manager](https://manager.pgxn.org/)
4. Prepare for development
- Set `INSTALL_VERSION` to `unreleased` in root `CMakeLists.txt`.
- Create new update files with `--unreleased` suffix.
- Add them to relevant `CMakeLists.txt` files.
h3-pg-4.2.2/docs/pgxnclient.md 0000664 0000000 0000000 00000001041 14752347156 0016074 0 ustar 00root root 0000000 0000000 # PGXN Client
> `pgxnclient` does not normally ship with PostgreSQL, so you will probably have to install it manually.
The [PGXN Client](https://pgxn.github.io/pgxnclient/) is a command line tool designed to interact with the [PostgreSQL Extension Network](https://pgxn.org/) allowing searching, compiling, installing, and removing extensions in PostgreSQL databases.
## Ubuntu
On Ubuntu you can install using `apt`
```bash
apt-get install pgxnclient
```
## MacOS
On MacOs you can install using `brew`
```bash
brew install pgxnclient
``` h3-pg-4.2.2/h3/ 0000775 0000000 0000000 00000000000 14752347156 0012765 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3/CMakeLists.txt 0000664 0000000 0000000 00000005412 14752347156 0015527 0 ustar 00root root 0000000 0000000 PostgreSQL_add_extension(postgresql_h3
RELOCATABLE
NAME h3
COMMENT "H3 bindings for PostgreSQL"
VERSION ${INSTALL_VERSION}
COMPONENT ${PROJECT_NAME}
SOURCES
src/binding/edge.c
src/binding/hierarchy.c
src/binding/indexing.c
src/binding/inspection.c
src/binding/miscellaneous.c
src/binding/regions.c
src/binding/traversal.c
src/binding/vertex.c
src/deprecated.c
src/extension.c
src/guc.c
src/init.c
src/opclass_btree.c
src/opclass_hash.c
src/opclass_spgist.c
src/operators.c
src/srf.c
src/type.c
INSTALLS
sql/install/00-type.sql
sql/install/01-indexing.sql
sql/install/02-inspection.sql
sql/install/03-traversal.sql
sql/install/04-hierarchy.sql
sql/install/05-regions.sql
sql/install/06-edge.sql
sql/install/07-vertex.sql
sql/install/08-miscellaneous.sql
sql/install/10-operators.sql
sql/install/11-opclass_btree.sql
sql/install/12-opclass_hash.sql
sql/install/13-opclass_brin.sql
sql/install/14-opclass_spgist.sql
sql/install/20-casts.sql
sql/install/30-extension.sql
sql/install/99-deprecated.sql
UPDATES
sql/updates/h3--0.1.0.sql
sql/updates/h3--0.1.0--0.2.0.sql
sql/updates/h3--0.2.0--0.3.0.sql
sql/updates/h3--0.3.0--0.3.1.sql
sql/updates/h3--0.3.1--0.3.2.sql
sql/updates/h3--0.3.2--0.4.0.sql
sql/updates/h3--0.4.0--1.0.0.sql
sql/updates/h3--1.0.0--1.0.1.sql
sql/updates/h3--1.0.1--1.0.2.sql
sql/updates/h3--1.0.2--1.0.3.sql
sql/updates/h3--1.0.3--1.0.4.sql
sql/updates/h3--1.0.4--1.0.5.sql
sql/updates/h3--1.0.5--1.0.6.sql
sql/updates/h3--1.0.6--3.4.0.sql
sql/updates/h3--3.4.0--3.4.1.sql
sql/updates/h3--3.4.1--3.5.0.sql
sql/updates/h3--3.5.0--3.6.0.sql
sql/updates/h3--3.6.0--3.6.1.sql
sql/updates/h3--3.6.1--3.6.2.sql
sql/updates/h3--3.6.2--3.6.3.sql
sql/updates/h3--3.6.3--3.6.4.sql
sql/updates/h3--3.6.4--3.6.5.sql
sql/updates/h3--3.6.5--3.7.0.sql
sql/updates/h3--3.7.0--3.7.1.sql
sql/updates/h3--3.7.1--3.7.2.sql
sql/updates/h3--3.7.2--4.0.0.sql
sql/updates/h3--4.0.0--4.0.1.sql
sql/updates/h3--4.0.1--4.0.2.sql
sql/updates/h3--4.0.2--4.0.3.sql
sql/updates/h3--4.0.3--4.1.0.sql
sql/updates/h3--4.1.0--4.1.1.sql
sql/updates/h3--4.1.1--4.1.2.sql
sql/updates/h3--4.1.2--4.1.3.sql
sql/updates/h3--4.1.3--4.1.4.sql
sql/updates/h3--4.1.4--4.2.0.sql
sql/updates/h3--4.2.0--4.2.1.sql
sql/updates/h3--4.2.1--4.2.2.sql
)
# configure
configure_file(src/config.h.in src/config.h)
# include
target_include_directories(postgresql_h3 PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/src
src
)
# link
target_link_libraries(postgresql_h3 PRIVATE
postgresql_h3_shared
h3
)
# test
if(BUILD_TESTING)
add_subdirectory(test)
endif()
h3-pg-4.2.2/h3/sql/ 0000775 0000000 0000000 00000000000 14752347156 0013564 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3/sql/install/ 0000775 0000000 0000000 00000000000 14752347156 0015232 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3/sql/install/00-type.sql 0000664 0000000 0000000 00000004424 14752347156 0017155 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION h3" to load this file. \quit
-- ---------- ---------- ---------- ---------- ---------- ---------- ----------
--| The `h3` extension wraps the [H3 Core Library](https://github.com/uber/h3).
--| The detailed API reference is in the core [H3 Documentation](https://uber.github.io/h3) under the API Reference section.
--|
--| The `h3` core functions have been renamed from camelCase in H3 core to snake\_case in SQL.
--| The SQL function name is prefixed with `h3_`.
--|
--| # Base type
--|
--| An unsigned 64-bit integer representing any H3 object (hexagon, pentagon, directed edge ...)
--| represented as a (or 16-character) hexadecimal string, like '8928308280fffff'.
-- ---------- ---------- ---------- ---------- ---------- ---------- ----------
-- declare shell type, allowing us to reference while defining functions
-- before finally providing the full definition of the data type
CREATE TYPE h3index;
--@ internal
CREATE OR REPLACE FUNCTION
h3index_in(cstring) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OR REPLACE FUNCTION
h3index_out(h3index) RETURNS cstring
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OR REPLACE FUNCTION
h3index_recv(internal) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OR REPLACE FUNCTION
h3index_send(h3index) RETURNS bytea
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE TYPE h3index (
INPUT = h3index_in,
OUTPUT = h3index_out,
RECEIVE = h3index_recv,
SEND = h3index_send,
LIKE = int8
);
h3-pg-4.2.2/h3/sql/install/01-indexing.sql 0000664 0000000 0000000 00000003451 14752347156 0020001 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Indexing functions
--|
--| These function are used for finding the H3 index containing coordinates,
--| and for finding the center and boundary of H3 indexes.
--@ availability: 4.0.0
--@ ref: h3_lat_lng_to_cell_geometry, h3_lat_lng_to_cell_geography
CREATE OR REPLACE FUNCTION
h3_lat_lng_to_cell(latlng point, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_lat_lng_to_cell(point, integer)
IS 'Indexes the location at the specified resolution.';
--@ availability: 4.0.0
--@ ref: h3_cell_to_geometry, h3_cell_to_geography
CREATE OR REPLACE FUNCTION
h3_cell_to_lat_lng(cell h3index) RETURNS point
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_lat_lng(h3index)
IS 'Finds the centroid of the index.';
--@ availability: 4.0.0
--@ ref: h3_cell_to_boundary_geometry, h3_cell_to_boundary_geography
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary(cell h3index) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary(h3index)
IS 'Finds the boundary of the index.
Use `SET h3.extend_antimeridian TO true` to extend coordinates when crossing 180th meridian.';
h3-pg-4.2.2/h3/sql/install/02-inspection.sql 0000664 0000000 0000000 00000004560 14752347156 0020352 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Index inspection functions
--|
--| These functions provide metadata about an H3 index, such as its resolution
--| or base cell, and provide utilities for converting into and out of the
--| 64-bit representation of an H3 index.
--@ availability: 1.0.0
CREATE OR REPLACE FUNCTION
h3_get_resolution(h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_resolution(h3index)
IS 'Returns the resolution of the index.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_get_base_cell_number(h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_base_cell_number(h3index)
IS 'Returns the base cell number of the index.';
--@ availability: 1.0.0
CREATE OR REPLACE FUNCTION
h3_is_valid_cell(h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_valid_cell(h3index)
IS 'Returns true if the given H3Index is valid.';
--@ availability: 1.0.0
CREATE OR REPLACE FUNCTION
h3_is_res_class_iii(h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_res_class_iii(h3index)
IS 'Returns true if this index has a resolution with Class III orientation.';
--@ availability: 1.0.0
CREATE OR REPLACE FUNCTION
h3_is_pentagon(h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_pentagon(h3index)
IS 'Returns true if this index represents a pentagonal cell.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_get_icosahedron_faces(h3index) RETURNS integer[]
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_icosahedron_faces(h3index)
IS 'Find all icosahedron faces intersected by a given H3 index.';
h3-pg-4.2.2/h3/sql/install/03-traversal.sql 0000664 0000000 0000000 00000006314 14752347156 0020202 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Grid traversal functions
--|
--| Grid traversal allows finding cells in the vicinity of an origin cell, and
--| determining how to traverse the grid from one cell to another.
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_grid_disk(origin h3index, k integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_disk(h3index, integer)
IS 'Produces indices within "k" distance of the origin index.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_grid_disk_distances(origin h3index, k integer DEFAULT 1, OUT index h3index, OUT distance int) RETURNS SETOF record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_disk_distances(h3index, integer)
IS 'Produces indices within "k" distance of the origin index paired with their distance to the origin.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_grid_ring_unsafe(origin h3index, k integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_ring_unsafe(h3index, integer)
IS 'Returns the hollow hexagonal ring centered at origin with distance "k".';
--@ availability: 4.0.0
--@ ref: h3_grid_path_cells_recursive
CREATE OR REPLACE FUNCTION
h3_grid_path_cells(origin h3index, destination h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_path_cells(h3index, h3index)
IS 'Given two H3 indexes, return the line of indexes between them (inclusive).
This function may fail to find the line between two indexes, for
example if they are very far apart. It may also fail when finding
distances for indexes on opposite sides of a pentagon.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_grid_distance(origin h3index, destination h3index) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_distance(h3index, h3index)
IS 'Returns the distance in grid cells between the two indices.';
--@ availability: 0.2.0
CREATE OR REPLACE FUNCTION
h3_cell_to_local_ij(origin h3index, index h3index) RETURNS point
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_local_ij(h3index, h3index)
IS 'Produces local IJ coordinates for an H3 index anchored by an origin.';
--@ availability: 0.2.0
CREATE OR REPLACE FUNCTION
h3_local_ij_to_cell(origin h3index, coord point) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_local_ij_to_cell(h3index, point)
IS 'Produces an H3 index from local IJ coordinates anchored by an origin.';
h3-pg-4.2.2/h3/sql/install/04-hierarchy.sql 0000664 0000000 0000000 00000013735 14752347156 0020163 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Hierarchical grid functions
--|
--| These functions permit moving between resolutions in the H3 grid system.
--| The functions produce parent (coarser) or children (finer) cells.
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cell_to_parent(cell h3index, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_parent(cell h3index, resolution integer)
IS 'Returns the parent of the given index.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cell_to_children(cell h3index, resolution integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_children(cell h3index, resolution integer)
IS 'Returns the set of children of the given index.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cell_to_center_child(cell h3index, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_center_child(cell h3index, resolution integer)
IS 'Returns the center child (finer) index contained by input index at given resolution.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_compact_cells(cells h3index[]) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_compact_cells(cells h3index[])
IS 'Compacts the given array as best as possible.';
--@ availability: 4.1.0
CREATE OR REPLACE FUNCTION
h3_cell_to_child_pos(child h3index, parentRes integer) RETURNS int8
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_child_pos(child h3index, parentRes integer)
IS 'Returns the position of the child cell within an ordered list of all children of the cells parent at the specified resolution parentRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of childPosToCell.';
--@ availability: 4.1.0
CREATE OR REPLACE FUNCTION
h3_child_pos_to_cell(childPos int8, parent h3index, childRes int) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_child_pos_to_cell(childPos int8, parent h3index, childRes int)
IS 'Returns the child cell at a given position within an ordered list of all children of parent at the specified resolution childRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of cellToChildPos.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_uncompact_cells(cells h3index[], resolution integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_uncompact_cells(cells h3index[], resolution integer)
IS 'Uncompacts the given array at the given resolution.';
-- ---------- ---------- ---------- ---------- ---------- ---------- ----------
-- Custom Funtions
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cell_to_parent(cell h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_parent(cell h3index)
IS 'Returns the parent of the given index.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cell_to_children(cell h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_children(cell h3index)
IS 'Returns the set of children of the given index.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cell_to_center_child(cell h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_center_child(cell h3index)
IS 'Returns the center child (finer) index contained by input index at next resolution.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_uncompact_cells(cells h3index[]) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_uncompact_cells(cells h3index[])
IS 'Uncompacts the given array at the resolution one higher than the highest resolution in the set.';
--@ internal
CREATE OR REPLACE FUNCTION __h3_cell_to_children_aux(index h3index, resolution integer, current integer)
RETURNS SETOF h3index AS $$
DECLARE
retSet h3index[];
r h3index;
BEGIN
IF current = -1 THEN
SELECT h3_get_resolution(index) into current;
END IF;
IF resolution = -1 THEN
SELECT h3_get_resolution(index)+1 into resolution;
END IF;
IF current < resolution THEN
SELECT ARRAY(SELECT h3_cell_to_children(index)) into retSet;
FOREACH r in ARRAY retSet LOOP
RETURN QUERY SELECT __h3_cell_to_children_aux(r, resolution, current + 1);
END LOOP;
ELSE
RETURN NEXT index;
END IF;
END;$$ LANGUAGE plpgsql;
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION h3_cell_to_children_slow(index h3index, resolution integer) RETURNS SETOF h3index
AS $$ SELECT __h3_cell_to_children_aux($1, $2, -1) $$ LANGUAGE SQL;
COMMENT ON FUNCTION h3_cell_to_children_slow(index h3index, resolution integer) IS
'Slower version of H3ToChildren but allocates less memory.';
CREATE OR REPLACE FUNCTION h3_cell_to_children_slow(index h3index) RETURNS SETOF h3index
AS $$ SELECT __h3_cell_to_children_aux($1, -1, -1) $$ LANGUAGE SQL;
COMMENT ON FUNCTION h3_cell_to_children_slow(index h3index) IS
'Slower version of H3ToChildren but allocates less memory.';
h3-pg-4.2.2/h3/sql/install/05-regions.sql 0000664 0000000 0000000 00000004534 14752347156 0017651 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Region functions
--|
--| These functions convert H3 indexes to and from polygonal areas.
--@ availability: 4.0.0
--@ ref: h3_polygon_to_cells_geometry, h3_polygon_to_cells_geography
CREATE OR REPLACE FUNCTION
h3_polygon_to_cells(exterior polygon, holes polygon[], resolution integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE
-- intentionally NOT STRICT
CALLED ON NULL INPUT PARALLEL SAFE; COMMENT ON FUNCTION
h3_polygon_to_cells(polygon, polygon[], integer)
IS 'Takes an exterior polygon [and a set of hole polygon] and returns the set of hexagons that best fit the structure.';
--@ availability: 4.2.0
CREATE OR REPLACE FUNCTION
h3_polygon_to_cells_experimental(exterior polygon, holes polygon[], resolution integer DEFAULT 1, containment_mode text DEFAULT 'center') RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE
-- intentionally NOT STRICT
CALLED ON NULL INPUT PARALLEL SAFE; COMMENT ON FUNCTION
h3_polygon_to_cells_experimental(polygon, polygon[], integer, text)
IS 'Takes an exterior polygon [and a set of hole polygon] and returns the set of hexagons that best fit the structure.';
--@ availability: 4.0.0
--@ ref: h3_cells_to_multi_polygon_geometry, h3_cells_to_multi_polygon_geography, h3_cells_to_multi_polygon_geometry_agg, h3_cells_to_multi_polygon_geography_agg
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon(h3index[], OUT exterior polygon, OUT holes polygon[]) RETURNS SETOF record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cells_to_multi_polygon(h3index[])
IS 'Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons. Polygon outlines will follow GeoJSON MultiPolygon order: Each polygon will have one outer loop, which is first in the list, followed by any holes.';
h3-pg-4.2.2/h3/sql/install/06-edge.sql 0000664 0000000 0000000 00000006202 14752347156 0017102 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Unidirectional edge functions
--|
--| Unidirectional edges allow encoding the directed edge from one cell to a
--| neighboring cell.
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_are_neighbor_cells(origin h3index, destination h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_are_neighbor_cells(origin h3index, destination h3index)
IS 'Returns true if the given indices are neighbors.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cells_to_directed_edge(origin h3index, destination h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cells_to_directed_edge(origin h3index, destination h3index)
IS 'Returns a unidirectional edge H3 index based on the provided origin and destination.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_is_valid_directed_edge(edge h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_valid_directed_edge(edge h3index)
IS 'Returns true if the given edge is valid.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_get_directed_edge_origin(edge h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_directed_edge_origin(edge h3index)
IS 'Returns the origin index from the given edge.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_get_directed_edge_destination(edge h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_directed_edge_destination(edge h3index)
IS 'Returns the destination index from the given edge.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_directed_edge_to_cells(edge h3index, OUT origin h3index, OUT destination h3index) RETURNS record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_directed_edge_to_cells(edge h3index)
IS 'Returns the pair of indices from the given edge.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_origin_to_directed_edges(h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_origin_to_directed_edges(h3index)
IS 'Returns all unidirectional edges with the given index as origin.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_directed_edge_to_boundary(edge h3index) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_directed_edge_to_boundary(edge h3index)
IS 'Provides the coordinates defining the unidirectional edge.';
h3-pg-4.2.2/h3/sql/install/07-vertex.sql 0000664 0000000 0000000 00000003410 14752347156 0017512 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # H3 Vertex functions
--|
--| Functions for working with cell vertexes.
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cell_to_vertex(cell h3index, vertexNum integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_vertex(cell h3index, vertexNum integer)
IS 'Returns a single vertex for a given cell, as an H3 index.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cell_to_vertexes(cell h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_vertexes(cell h3index)
IS 'Returns all vertexes for a given cell, as H3 indexes.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_vertex_to_lat_lng(vertex h3index) RETURNS point
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_vertex_to_lat_lng(vertex h3index)
IS 'Get the geocoordinates of an H3 vertex.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_is_valid_vertex(vertex h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_valid_vertex(vertex h3index)
IS 'Whether the input is a valid H3 vertex.';
h3-pg-4.2.2/h3/sql/install/08-miscellaneous.sql 0000664 0000000 0000000 00000006066 14752347156 0021053 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Miscellaneous H3 functions
--|
--| These functions include descriptions of the H3 grid system.
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_great_circle_distance(a point, b point, unit text DEFAULT 'km') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_great_circle_distance(point, point, text)
IS 'The great circle distance in radians between two spherical coordinates.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_get_hexagon_area_avg(resolution integer, unit text DEFAULT 'km') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_hexagon_area_avg(integer, text)
IS 'Average hexagon area in square (kilo)meters at the given resolution.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_cell_area(cell h3index, unit text DEFAULT 'km^2') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_area(h3index, text)
IS 'Exact area for a specific cell (hexagon or pentagon).';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_get_hexagon_edge_length_avg(resolution integer, unit text DEFAULT 'km') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_hexagon_edge_length_avg(integer, text)
IS 'Average hexagon edge length in (kilo)meters at the given resolution.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_edge_length(edge h3index, unit text DEFAULT 'km') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_edge_length(h3index, text)
IS 'Exact length for a specific unidirectional edge.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_get_num_cells(resolution integer) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_num_cells(integer) IS
'Number of unique H3 indexes at the given resolution.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_get_res_0_cells() RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_res_0_cells()
IS 'Returns all 122 resolution 0 indexes.';
--@ availability: 4.0.0
CREATE OR REPLACE FUNCTION
h3_get_pentagons(resolution integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_pentagons(resolution integer)
IS 'All the pentagon H3 indexes at the specified resolution.'; h3-pg-4.2.2/h3/sql/install/10-operators.sql 0000664 0000000 0000000 00000011043 14752347156 0020206 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Operators
--@ internal
CREATE OR REPLACE FUNCTION h3index_distance(h3index, h3index) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ availability: 3.7.0
CREATE OPERATOR <-> (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_distance,
COMMUTATOR = <->
);
COMMENT ON OPERATOR <-> (h3index, h3index) IS
'Returns the distance in grid cells between the two indices (at the lowest resolution of the two).';
-- ---------- ---------- ---------- ---------- ---------- ---------- ----------
--| ## B-tree operators
--@ internal
CREATE OR REPLACE FUNCTION h3index_eq(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ availability: 0.1.0
CREATE OPERATOR = (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_eq,
COMMUTATOR = =,
NEGATOR = <>,
RESTRICT = eqsel,
JOIN = eqjoinsel,
HASHES, MERGES
);
COMMENT ON OPERATOR = (h3index, h3index) IS
'Returns true if two indexes are the same.';
--@ internal
CREATE OR REPLACE FUNCTION h3index_ne(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ availability: 0.1.0
CREATE OPERATOR <> (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_ne,
COMMUTATOR = <>,
NEGATOR = =,
RESTRICT = neqsel,
JOIN = neqjoinsel
);
--@ internal
CREATE OR REPLACE FUNCTION h3index_lt(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OPERATOR < (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_lt,
COMMUTATOR = > ,
NEGATOR = >= ,
RESTRICT = scalarltsel,
JOIN = scalarltjoinsel
);
--@ internal
CREATE OR REPLACE FUNCTION h3index_le(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OPERATOR <= (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_le,
COMMUTATOR = >= ,
NEGATOR = > ,
RESTRICT = scalarltsel,
JOIN = scalarltjoinsel
);
--@ internal
CREATE OR REPLACE FUNCTION h3index_gt(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OPERATOR > (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_gt,
COMMUTATOR = < ,
NEGATOR = <= ,
RESTRICT = scalargtsel,
JOIN = scalargtjoinsel
);
--@ internal
CREATE OR REPLACE FUNCTION h3index_ge(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OPERATOR >= (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_ge,
COMMUTATOR = <= ,
NEGATOR = < ,
RESTRICT = scalargtsel,
JOIN = scalargtjoinsel
);
-- ---------- ---------- ---------- ---------- ---------- ---------- ----------
--| ## R-tree Operators
--@ internal
CREATE OR REPLACE FUNCTION h3index_overlaps(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ availability: 3.6.1
CREATE OPERATOR && (
PROCEDURE = h3index_overlaps,
LEFTARG = h3index, RIGHTARG = h3index,
COMMUTATOR = &&,
RESTRICT = contsel, JOIN = contjoinsel
);
COMMENT ON OPERATOR && (h3index, h3index) IS
'Returns true if the two H3 indexes intersect.';
--@ internal
CREATE OR REPLACE FUNCTION h3index_contains(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ availability: 3.6.1
CREATE OPERATOR @> (
PROCEDURE = h3index_contains,
LEFTARG = h3index, RIGHTARG = h3index,
COMMUTATOR = <@,
RESTRICT = contsel, JOIN = contjoinsel
);
COMMENT ON OPERATOR @> (h3index, h3index) IS
'Returns true if A contains B.';
--@ internal
CREATE OR REPLACE FUNCTION h3index_contained_by(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ availability: 3.6.1
CREATE OPERATOR <@ (
PROCEDURE = h3index_contained_by,
LEFTARG = h3index, RIGHTARG = h3index,
COMMUTATOR = @>,
RESTRICT = contsel, JOIN = contjoinsel
);
COMMENT ON OPERATOR <@ (h3index, h3index) IS
'Returns true if A is contained by B.';
h3-pg-4.2.2/h3/sql/install/11-opclass_btree.sql 0000664 0000000 0000000 00000002310 14752347156 0021013 0 ustar 00root root 0000000 0000000 /*
* Copyright 2019 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- B-tree operator class
--@ internal
CREATE OR REPLACE FUNCTION h3index_cmp(h3index, h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OR REPLACE FUNCTION h3index_sortsupport(internal)
RETURNS void
AS 'h3', 'h3index_sortsupport'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OPERATOR CLASS h3index_ops DEFAULT FOR TYPE h3index USING btree AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 h3index_cmp(h3index, h3index),
FUNCTION 2 h3index_sortsupport(internal);
h3-pg-4.2.2/h3/sql/install/12-opclass_hash.sql 0000664 0000000 0000000 00000002124 14752347156 0020641 0 ustar 00root root 0000000 0000000 /*
* Copyright 2019 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- Hash operator class
--@ internal
CREATE OR REPLACE FUNCTION h3index_hash(h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OR REPLACE FUNCTION h3index_hash_extended(h3index, int8) RETURNS int8
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OPERATOR CLASS h3index_ops DEFAULT FOR TYPE h3index USING hash AS
OPERATOR 1 = ,
FUNCTION 1 h3index_hash(h3index),
FUNCTION 2 h3index_hash_extended(h3index, int8);
h3-pg-4.2.2/h3/sql/install/13-opclass_brin.sql 0000664 0000000 0000000 00000002106 14752347156 0020651 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- BRIN operator class
--@ internal
CREATE OPERATOR CLASS h3index_minmax_ops DEFAULT FOR TYPE h3index USING brin AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 brin_minmax_opcinfo(internal),
FUNCTION 2 brin_minmax_add_value(internal, internal, internal, internal),
FUNCTION 3 brin_minmax_consistent(internal, internal, internal),
FUNCTION 4 brin_minmax_union(internal, internal, internal);
h3-pg-4.2.2/h3/sql/install/14-opclass_spgist.sql 0000664 0000000 0000000 00000005531 14752347156 0021236 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| ## SP-GiST operator class (experimental)
--|
--| *This is still an experimental feature and may change in future versions.*
--| Add an SP-GiST index using the `h3index_ops_experimental` operator class:
--|
--| ```sql
--| -- CREATE INDEX [indexname] ON [tablename] USING spgist([column] h3index_ops_experimental);
--| CREATE INDEX spgist_idx ON h3_data USING spgist(hex h3index_ops_experimental);
--| ```
--@ internal
CREATE OR REPLACE FUNCTION h3index_spgist_config(internal, internal) RETURNS void
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OR REPLACE FUNCTION h3index_spgist_choose(internal, internal) RETURNS void
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OR REPLACE FUNCTION h3index_spgist_picksplit(internal, internal) RETURNS void
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OR REPLACE FUNCTION h3index_spgist_inner_consistent(internal, internal) RETURNS void
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
--@ internal
CREATE OR REPLACE FUNCTION h3index_spgist_leaf_consistent(internal, internal) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- intentionally *not* marked as DEFAULT,
-- until we are satisfied with the implementation
CREATE OPERATOR CLASS h3index_ops_experimental
FOR TYPE h3index USING spgist
AS
-- OPERATOR 1 << , -- RTLeftStrategyNumber
-- OPERATOR 2 &< , -- RTOverLeftStrategyNumber
-- OPERATOR 3 && , -- RTOverlapStrategyNumber
-- OPERATOR 4 &> , -- RTOverRightStrategyNumber
-- OPERATOR 5 >> , -- RTRightStrategyNumber
OPERATOR 6 = , -- RTSameStrategyNumber
OPERATOR 7 @> , -- RTContainsStrategyNumber
OPERATOR 8 <@ , -- RTContainedByStrategyNumber
-- OPERATOR 9 &<| , -- RTOverBelowStrategyNumber
-- OPERATOR 10 <<| , -- RTBelowStrategyNumber
-- OPERATOR 11 |>> , -- RTAboveStrategyNumber
-- OPERATOR 12 |&> , -- RTOverAboveStrategyNumber
FUNCTION 1 h3index_spgist_config(internal, internal),
FUNCTION 2 h3index_spgist_choose(internal, internal),
FUNCTION 3 h3index_spgist_picksplit(internal, internal),
FUNCTION 4 h3index_spgist_inner_consistent(internal, internal),
FUNCTION 5 h3index_spgist_leaf_consistent(internal, internal);
h3-pg-4.2.2/h3/sql/install/20-casts.sql 0000664 0000000 0000000 00000002560 14752347156 0017312 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Type casts
--@ internal
CREATE OR REPLACE FUNCTION
h3index_to_bigint(h3index) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (h3index AS bigint) WITH FUNCTION h3index_to_bigint(h3index);
COMMENT ON CAST (h3index AS bigint) IS
'Convert H3 index to bigint, which is useful when you need a decimal representation.';
--@ internal
CREATE OR REPLACE FUNCTION
bigint_to_h3index(bigint) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (bigint AS h3index) WITH FUNCTION bigint_to_h3index(bigint);
COMMENT ON CAST (h3index AS bigint) IS
'Convert bigint to H3 index.';
CREATE CAST (h3index AS point) WITH FUNCTION h3_cell_to_lat_lng(h3index);
COMMENT ON CAST (h3index AS point) IS
'Convert H3 index to point.'; h3-pg-4.2.2/h3/sql/install/30-extension.sql 0000664 0000000 0000000 00000002243 14752347156 0020210 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Extension specific functions
--@ availability: 1.0.0
CREATE OR REPLACE FUNCTION h3_get_extension_version() RETURNS text
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_extension_version() IS
'Get the currently installed version of the extension.';
--@ availability: 4.1.0
CREATE OR REPLACE FUNCTION h3_pg_migrate_pass_by_reference(h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_pg_migrate_pass_by_reference(h3index) IS
'Migrate h3index from pass-by-reference to pass-by-value.';
h3-pg-4.2.2/h3/sql/install/99-deprecated.sql 0000664 0000000 0000000 00000001631 14752347156 0020313 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Deprecated functions
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary(cell h3index, extend_antimeridian boolean) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary(h3index, boolean)
IS 'DEPRECATED: Use `SET h3.extend_antimeridian TO true` instead.';
h3-pg-4.2.2/h3/sql/updates/ 0000775 0000000 0000000 00000000000 14752347156 0015231 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3/sql/updates/h3--0.1.0--0.2.0.sql 0000664 0000000 0000000 00000025740 14752347156 0017513 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '0.2.0'" to load this file. \quit
-- Indexing functions (indexing.c)
CREATE OR REPLACE FUNCTION h3_geo_to_h3(point, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_geo_to_h3(point, resolution integer) IS
'Indexes the location at the specified resolution';
CREATE OR REPLACE FUNCTION h3_h3_to_geo(h3index) RETURNS point
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_to_geo(h3index) IS
'Finds the centroid of the index';
CREATE OR REPLACE FUNCTION h3_h3_to_geo_boundary(h3index) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_to_geo_boundary(h3index) IS
'Finds the boundary of the index';
-- Index inspection functions (inspection.c)
CREATE OR REPLACE FUNCTION h3_h3_get_resolution(h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_get_resolution(h3index) IS
'Returns the resolution of the index';
CREATE OR REPLACE FUNCTION h3_h3_get_base_cell(h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_get_base_cell(h3index) IS
'Returns the base cell number of the index';
CREATE OR REPLACE FUNCTION h3_h3_is_valid(h3index) RETURNS bool
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_is_valid(h3index) IS
'Returns true if the given H3Index is valid';
CREATE OR REPLACE FUNCTION h3_h3_is_res_class_iii(h3index) RETURNS bool
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_is_res_class_iii(h3index) IS
'Returns true if this index has a resolution with Class III orientation';
CREATE OR REPLACE FUNCTION h3_h3_is_pentagon(h3index) RETURNS bool
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_is_pentagon(h3index) IS
'Returns true if this index represents a pentagonal cell';
-- Grid traversal functions (traversal.c)
CREATE OR REPLACE FUNCTION h3_k_ring(h3index, k integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_k_ring(h3index, k integer) IS
'Produces indices within "k" distance of the origin index';
CREATE OR REPLACE FUNCTION h3_k_ring_distances(h3index, k integer DEFAULT 1, OUT index h3index, OUT distance int) RETURNS SETOF record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_k_ring_distances(h3index, k integer) IS
'Produces indices within "k" distance of the origin index paired with their distance to the origin';
CREATE OR REPLACE FUNCTION h3_hex_ring(h3index, k integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_hex_ring(h3index, k integer) IS
'Returns the hollow hexagonal ring centered at origin with distance "k"';
CREATE OR REPLACE FUNCTION h3_distance(h3index, h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_distance(h3index, h3index) IS
'Returns the distance in grid cells between the two indices';
CREATE OR REPLACE FUNCTION h3_experimental_h3_to_local_ij(origin h3index, index h3index) RETURNS POINT
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_experimental_h3_to_local_ij(origin h3index, index h3index) IS
'Produces local IJ coordinates for an H3 index anchored by an origin.
This function is experimental, and its output is not guaranteed to be compatible across different versions of H3.';
CREATE OR REPLACE FUNCTION h3_experimental_local_ij_to_h3(origin h3index, coord POINT) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_experimental_local_ij_to_h3(origin h3index, coord POINT) IS
'Produces an H3 index from local IJ coordinates anchored by an origin.
This function is experimental, and its output is not guaranteed to be compatible across different versions of H3.';
-- Hierarchical grid functions (hierarchy.c)
CREATE OR REPLACE FUNCTION h3_h3_to_parent(h3index, resolution integer DEFAULT -1) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_to_parent(h3index, resolution integer) IS
'Returns the parent of the given index';
CREATE OR REPLACE FUNCTION h3_h3_to_children(h3index, resolution integer DEFAULT -1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_to_children(index h3index, resolution integer) IS
'Returns the set of children of the given index';
CREATE OR REPLACE FUNCTION h3_compact(h3index[]) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_compact(h3index[]) IS
'Compacts the given array as best as possible';
CREATE OR REPLACE FUNCTION h3_uncompact(h3index[], resolution integer DEFAULT -1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_uncompact(h3index[], resolution integer) IS
'Uncompacts the given array at the given resolution. If no resolution is given, then it is chosen as one higher than the highest resolution in the set';
-- Region functions (regions.c)
CREATE OR REPLACE FUNCTION h3_polyfill(exterior polygon, holes polygon[], resolution integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE PARALLEL SAFE; -- NOT STRICT
COMMENT ON FUNCTION h3_polyfill(exterior polygon, holes polygon[], resolution integer) IS
'Takes an exterior polygon [and a set of hole polygon] and returns the set of hexagons that best fit the structure';
CREATE OR REPLACE FUNCTION h3_h3_set_to_linked_geo(h3index[], OUT exterior polygon, OUT holes polygon[]) RETURNS SETOF record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_set_to_linked_geo(h3index[]) IS
'Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons. Polygon outlines will follow GeoJSON MultiPolygon order: Each polygon will have one outer loop, which is first in the list, followed by any holes';
-- Unidirectional edge functions (uniedges.c)
CREATE OR REPLACE FUNCTION h3_h3_indexes_are_neighbors(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_indexes_are_neighbors(h3index, h3index) IS
'Returns true if the given indices are neighbors';
CREATE OR REPLACE FUNCTION h3_get_h3_unidirectional_edge(origin h3index, destination h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_h3_unidirectional_edge(origin h3index, destination h3index) IS
'Returns a unidirectional edge H3 index based on the provided origin and destination.';
CREATE OR REPLACE FUNCTION h3_h3_unidirectional_edge_is_valid(edge h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_unidirectional_edge_is_valid(edge h3index) IS
'Returns true if the given edge is valid.';
CREATE OR REPLACE FUNCTION h3_get_origin_h3_index_from_unidirectional_edge(edge h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_origin_h3_index_from_unidirectional_edge(edge h3index) IS
'Returns the origin index from the given edge.';
CREATE OR REPLACE FUNCTION h3_get_destination_h3_index_from_unidirectional_edge(edge h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_destination_h3_index_from_unidirectional_edge(edge h3index) IS
'Returns the destination index from the given edge.';
CREATE OR REPLACE FUNCTION h3_get_h3_indexes_from_unidirectional_edge(edge h3index, OUT origin h3index, OUT destination h3index) RETURNS record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_h3_indexes_from_unidirectional_edge(edge h3index) IS
'Returns the pair of indices from the given edge.';
CREATE OR REPLACE FUNCTION h3_get_h3_unidirectional_edges_from_hexagon(h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_h3_unidirectional_edges_from_hexagon(h3index) IS
'Returns all unidirectional edges with the given index as origin';
CREATE OR REPLACE FUNCTION h3_get_unidirectional_edge_boundary(edge h3index) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_unidirectional_edge_boundary(edge h3index) IS
'Provides the coordinates defining the unidirectional edge.';
-- Miscellaneous H3 functions (miscellaneous.c)
CREATE OR REPLACE FUNCTION h3_num_hexagons(resolution integer) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_num_hexagons(resolution integer) IS
'Number of unique H3 indexes at the given resolution.';
-- DEPRECATED in v3.4.0
CREATE OR REPLACE FUNCTION h3_degs_to_rads(float) RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_rads_to_degs(float) RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- DEPRECATED in v3.5.0
CREATE OR REPLACE FUNCTION h3_hex_area_km2(integer) RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_hex_area_m2(integer) RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_edge_length_km(integer) RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_edge_length_m(integer) RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_hex_range(h3index, k integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_hex_range_distances(h3index, k integer, OUT h3index, OUT int) RETURNS SETOF record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_hex_ranges(h3index[], k integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- DEPRECATED in v3.6.0
CREATE OR REPLACE FUNCTION h3_string_to_h3(cstring) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_h3_to_string(h3index) RETURNS cstring
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
h3-pg-4.2.2/h3/sql/updates/h3--0.1.0.sql 0000664 0000000 0000000 00000007010 14752347156 0016771 0 ustar 00root root 0000000 0000000 /*
* Copyright 2018 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION h3" to load this file. \quit
-- Declare shell type, allowing us to reference while defining functions
CREATE TYPE h3index;
CREATE OR REPLACE FUNCTION h3index_in(cstring) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_out(h3index) RETURNS cstring
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_eq(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_ne(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_lt(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_le(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_gt(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_ge(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_cmp(h3index, h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- Finally, we can provide the full definition of the data type
CREATE TYPE h3index (
INTERNALLENGTH = 8,
INPUT = h3index_in,
OUTPUT = h3index_out,
ALIGNMENT = double
);
-- Operators
CREATE OPERATOR = (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_eq,
COMMUTATOR = =,
NEGATOR = <>,
RESTRICT = eqsel,
JOIN = eqjoinsel,
HASHES, MERGES
);
CREATE OPERATOR <> (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_ne,
COMMUTATOR = <>,
NEGATOR = =,
RESTRICT = neqsel,
JOIN = neqjoinsel
);
CREATE OPERATOR < (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_lt,
COMMUTATOR = > ,
NEGATOR = >= ,
RESTRICT = scalarltsel,
JOIN = scalarltjoinsel
);
CREATE OPERATOR <= (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_le,
COMMUTATOR = >= ,
NEGATOR = > ,
RESTRICT = scalarltsel,
JOIN = scalarltjoinsel
);
CREATE OPERATOR > (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_gt,
COMMUTATOR = < ,
NEGATOR = <= ,
RESTRICT = scalargtsel,
JOIN = scalargtjoinsel
);
CREATE OPERATOR >= (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_ge,
COMMUTATOR = <= ,
NEGATOR = < ,
RESTRICT = scalargtsel,
JOIN = scalargtjoinsel
);
-- Operator class
CREATE OPERATOR CLASS btree_h3index_ops DEFAULT FOR TYPE h3index
USING btree AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 h3index_cmp(h3index, h3index);
h3-pg-4.2.2/h3/sql/updates/h3--0.2.0--0.3.0.sql 0000664 0000000 0000000 00000010651 14752347156 0017510 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '0.3.0'" to load this file. \quit
-- Custom helper functions
CREATE OR REPLACE FUNCTION h3_basecells() RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_basecells() IS
'Returns all 122 basecells.';
CREATE OR REPLACE FUNCTION __h3_h3_to_children_aux(index h3index, resolution integer, current INTEGER)
RETURNS SETOF h3index AS $$
DECLARE
retSet h3index[];
r h3index;
BEGIN
IF current = -1 THEN
SELECT h3_h3_get_resolution(index) into current;
END IF;
IF resolution = -1 THEN
SELECT h3_h3_get_resolution(index)+1 into resolution;
END IF;
IF current < resolution THEN
SELECT ARRAY(SELECT h3_h3_to_children_fast(index)) into retSet;
FOREACH r in ARRAY retSet LOOP
RETURN QUERY SELECT __h3_h3_to_children_aux(r, resolution, current + 1);
END LOOP;
ELSE
RETURN NEXT index;
END IF;
END;$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION h3_h3_to_children_slow(index h3index, resolution integer DEFAULT -1) RETURNS SETOF h3index
AS $$ SELECT __h3_h3_to_children_aux($1, $2, -1) $$ LANGUAGE SQL;
COMMENT ON FUNCTION h3_h3_to_children_slow(index h3index, resolution integer) IS
'Slower version of H3ToChildren but allocates less memory';
-- PostGIS
--CREATE OR REPLACE FUNCTION h3_geo_to_h3(geometry, resolution integer) RETURNS h3index
-- AS $$ SELECT h3_geo_to_h3($1::point, $2); $$ LANGUAGE SQL;
--CREATE OR REPLACE FUNCTION h3_geo_to_h3(geography, resolution integer) RETURNS h3index
-- AS $$ SELECT h3_geo_to_h3($1::geometry, $2); $$ LANGUAGE SQL;
--CREATE OR REPLACE FUNCTION h3_h3_to_geometry(h3index) RETURNS geometry
-- AS $$ SELECT ST_SetSRID(h3_h3_to_geo($1)::geometry, 4326) $$ LANGUAGE SQL;
--CREATE OR REPLACE FUNCTION h3_h3_to_geography(h3index) RETURNS geography
-- AS $$ SELECT h3_h3_to_geometry($1)::geography $$ LANGUAGE SQL;
--CREATE OR REPLACE FUNCTION h3_h3_to_geo_boundary_geometry(h3index) RETURNS geometry
-- AS $$ SELECT ST_SetSRID(h3_h3_to_geo_boundary($1)::geometry, 4326) $$ LANGUAGE SQL;
--CREATE OR REPLACE FUNCTION h3_h3_to_geo_boundary_geography(h3index) RETURNS geography
-- AS $$ SELECT h3_h3_to_geo_boundary_geometry($1)::geography $$ LANGUAGE SQL;
--CREATE OR REPLACE FUNCTION h3_polyfill(multi geometry, resolution integer) RETURNS SETOF h3index
-- AS $$ SELECT h3_polyfill(exterior, holes, resolution) FROM (
-- SELECT
-- -- extract exterior ring of each polygon
-- ST_MakePolygon(ST_ExteriorRing(poly))::polygon exterior,
-- -- extract holes of each polygon
-- (SELECT array_agg(hole)
-- FROM (
-- SELECT ST_MakePolygon(ST_InteriorRingN(
-- poly,
-- generate_series(1, ST_NumInteriorRings(poly))
-- ))::polygon AS hole
-- ) q_hole
-- ) holes
-- -- extract single polygons from multipolygon
-- FROM (
-- select (st_dump(multi)).geom as poly
-- ) q_poly GROUP BY poly
-- ) h3_polyfill; $$ LANGUAGE SQL IMMUTABLE STRICT;
--CREATE OR REPLACE FUNCTION h3_polyfill(multi geography, resolution integer) RETURNS SETOF h3index
--AS $$ SELECT h3_polyfill($1::geometry, $2) $$ LANGUAGE SQL;
-- Type casts
CREATE CAST (h3index AS point) WITH FUNCTION h3_h3_to_geo(h3index);
--CREATE CAST (h3index AS geometry) WITH FUNCTION h3_h3_to_geometry(h3index);
--CREATE CAST (h3index AS geography) WITH FUNCTION h3_h3_to_geography(h3index);
-- DEPRECATED in v1.0.0
CREATE OR REPLACE FUNCTION h3_haversine_distance(h3index, h3index) RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
h3-pg-4.2.2/h3/sql/updates/h3--0.3.0--0.3.1.sql 0000664 0000000 0000000 00000003262 14752347156 0017512 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '0.3.1'" to load this file. \quit
DROP FUNCTION IF EXISTS h3_h3_to_geo_boundary(h3index);
DROP FUNCTION IF EXISTS h3_h3_to_geo_boundary_geometry(h3index);
DROP FUNCTION IF EXISTS h3_h3_to_geo_boundary_geography(h3index);
CREATE OR REPLACE FUNCTION h3_h3_to_geo_boundary(h3index, extend_at_meridian BOOLEAN DEFAULT FALSE) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_h3_to_geo_boundary(h3index, boolean) IS
'Finds the boundary of the index, second argument extends coordinates when crossing 180th meridian to help visualization';
--CREATE OR REPLACE FUNCTION h3_h3_to_geo_boundary_geometry(h3index, extend BOOLEAN DEFAULT FALSE) RETURNS geometry
-- AS $$ SELECT ST_SetSRID(h3_h3_to_geo_boundary($1, $2)::geometry, 4326) $$ LANGUAGE SQL;
--CREATE OR REPLACE FUNCTION h3_h3_to_geo_boundary_geography(h3index, extend BOOLEAN DEFAULT FALSE) RETURNS geography
-- AS $$ SELECT h3_h3_to_geo_boundary_geometry($1, $2)::geography $$ LANGUAGE SQL; h3-pg-4.2.2/h3/sql/updates/h3--0.3.1--0.3.2.sql 0000664 0000000 0000000 00000001424 14752347156 0017512 0 ustar 00root root 0000000 0000000 /*
* Copyright 2018 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '0.3.2'" to load this file. \quit
-- NO CHANGES HERE, ONLY IN C FILES
h3-pg-4.2.2/h3/sql/updates/h3--0.3.2--0.4.0.sql 0000664 0000000 0000000 00000003673 14752347156 0017522 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '0.4.0'" to load this file. \quit
CREATE OR REPLACE FUNCTION h3_line(h3index, h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_line(h3index, h3index) IS
'Given two H3 indexes, return the line of indexes between them (inclusive).
This function may fail to find the line between two indexes, for
example if they are very far apart. It may also fail when finding
distances for indexes on opposite sides of a pentagon.';
CREATE OR REPLACE FUNCTION __h3_h3_to_children_aux(index h3index, resolution integer, current INTEGER)
RETURNS SETOF h3index AS $$
DECLARE
retSet h3index[];
r h3index;
BEGIN
IF current = -1 THEN
SELECT h3_h3_get_resolution(index) into current;
END IF;
IF resolution = -1 THEN
SELECT h3_h3_get_resolution(index)+1 into resolution;
END IF;
IF current < resolution THEN
SELECT ARRAY(SELECT h3_h3_to_children(index)) into retSet;
FOREACH r in ARRAY retSet LOOP
RETURN QUERY SELECT __h3_h3_to_children_aux(r, resolution, current + 1);
END LOOP;
ELSE
RETURN NEXT index;
END IF;
END;$$ LANGUAGE plpgsql; h3-pg-4.2.2/h3/sql/updates/h3--0.4.0--1.0.0.sql 0000664 0000000 0000000 00000010722 14752347156 0017507 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION h3" to load this file. \quit
DROP FUNCTION IF EXISTS h3_haversine_distance(h3index, h3index);
ALTER FUNCTION h3_basecells() RENAME TO h3_get_res_0_indexes;
COMMENT ON FUNCTION h3_get_res_0_indexes() IS
'Get all resolution 0 indexes.';
-- rename functions with double (h3_h3_) prefix
ALTER FUNCTION h3_h3_get_base_cell(h3index) RENAME TO h3_get_base_cell;
ALTER FUNCTION h3_h3_get_resolution(h3index) RENAME TO h3_get_resolution;
ALTER FUNCTION h3_h3_is_pentagon(h3index) RENAME TO h3_is_pentagon;
ALTER FUNCTION h3_h3_is_res_class_iii(h3index) RENAME TO h3_is_res_class_iii;
ALTER FUNCTION h3_h3_is_valid(h3index) RENAME TO h3_is_valid;
ALTER FUNCTION h3_h3_indexes_are_neighbors(h3index,h3index) RENAME TO h3_indexes_are_neighbors;
ALTER FUNCTION h3_h3_set_to_linked_geo(h3index[]) RENAME TO h3_set_to_linked_geo;
ALTER FUNCTION __h3_h3_to_children_aux(h3index,integer,integer) RENAME TO __h3_to_children_aux;
ALTER FUNCTION h3_h3_to_children(h3index,integer) RENAME TO h3_to_children;
ALTER FUNCTION h3_h3_to_children_slow(h3index,integer) RENAME TO h3_to_children_slow;
--ALTER FUNCTION h3_h3_to_geo_boundary_geography(h3index,boolean) RENAME TO h3_to_geo_boundary_geography;
--ALTER FUNCTION h3_h3_to_geo_boundary_geometry(h3index,boolean) RENAME TO h3_to_geo_boundary_geometry;
ALTER FUNCTION h3_h3_to_geo_boundary(h3index,boolean) RENAME TO h3_to_geo_boundary;
--ALTER FUNCTION h3_h3_to_geography(h3index) RENAME TO h3_to_geography;
ALTER FUNCTION h3_h3_to_geo(h3index) RENAME TO h3_to_geo;
--ALTER FUNCTION h3_h3_to_geometry(h3index) RENAME TO h3_to_geometry;
ALTER FUNCTION h3_h3_to_parent(h3index,integer) RENAME TO h3_to_parent;
ALTER FUNCTION h3_h3_to_string(h3index) RENAME TO h3_to_string;
ALTER FUNCTION h3_h3_unidirectional_edge_is_valid(h3index) RENAME TO h3_unidirectional_edge_is_valid;
--CREATE OR REPLACE FUNCTION h3_to_geo_boundary_geometry(h3index, extend BOOLEAN DEFAULT FALSE) RETURNS geometry
-- AS $$ SELECT ST_SetSRID(h3_to_geo_boundary($1, $2)::geometry, 4326) $$ LANGUAGE SQL;
--CREATE OR REPLACE FUNCTION h3_to_geo_boundary_geography(h3index, extend BOOLEAN DEFAULT FALSE) RETURNS geography
-- AS $$ SELECT h3_to_geo_boundary_geometry($1, $2)::geography $$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION h3_to_children_slow(index h3index, resolution integer DEFAULT -1) RETURNS SETOF h3index
AS $$ SELECT __h3_to_children_aux($1, $2, -1) $$ LANGUAGE SQL;
COMMENT ON FUNCTION h3_to_children_slow(index h3index, resolution integer) IS
'Slower version of H3ToChildren but allocates less memory';
CREATE OR REPLACE FUNCTION __h3_to_children_aux(index h3index, resolution integer, current INTEGER)
RETURNS SETOF h3index AS $$
DECLARE
retSet h3index[];
r h3index;
BEGIN
IF current = -1 THEN
SELECT h3_get_resolution(index) into current;
END IF;
IF resolution = -1 THEN
SELECT h3_get_resolution(index)+1 into resolution;
END IF;
IF current < resolution THEN
SELECT ARRAY(SELECT h3_to_children(index)) into retSet;
FOREACH r in ARRAY retSet LOOP
RETURN QUERY SELECT __h3_to_children_aux(r, resolution, current + 1);
END LOOP;
ELSE
RETURN NEXT index;
END IF;
END;$$ LANGUAGE plpgsql;
-- version
CREATE OR REPLACE FUNCTION h3_get_extension_version() RETURNS text
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_extension_version() IS
'Get the currently installed version of the extension.';
-- hash operators
CREATE OR REPLACE FUNCTION h3index_hash(h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OPERATOR CLASS hash_h3index_ops DEFAULT FOR TYPE h3index
USING hash AS
OPERATOR 1 = ,
FUNCTION 1 h3index_hash(h3index);
h3-pg-4.2.2/h3/sql/updates/h3--1.0.0--1.0.1.sql 0000664 0000000 0000000 00000001150 14752347156 0017500 0 ustar 00root root 0000000 0000000 /*
* Copyright 2018 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- no changes
h3-pg-4.2.2/h3/sql/updates/h3--1.0.1--1.0.2.sql 0000664 0000000 0000000 00000001150 14752347156 0017502 0 ustar 00root root 0000000 0000000 /*
* Copyright 2018 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- no changes
h3-pg-4.2.2/h3/sql/updates/h3--1.0.2--1.0.3.sql 0000664 0000000 0000000 00000001150 14752347156 0017504 0 ustar 00root root 0000000 0000000 /*
* Copyright 2018 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- no changes
h3-pg-4.2.2/h3/sql/updates/h3--1.0.3--1.0.4.sql 0000664 0000000 0000000 00000001424 14752347156 0017512 0 ustar 00root root 0000000 0000000 /*
* Copyright 2018 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '1.0.4'" to load this file. \quit
-- NO CHANGES HERE, ONLY IN C FILES
h3-pg-4.2.2/h3/sql/updates/h3--1.0.4--1.0.5.sql 0000664 0000000 0000000 00000001424 14752347156 0017514 0 ustar 00root root 0000000 0000000 /*
* Copyright 2018 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '1.0.5'" to load this file. \quit
-- NO CHANGES HERE, ONLY IN C FILES
h3-pg-4.2.2/h3/sql/updates/h3--1.0.5--1.0.6.sql 0000664 0000000 0000000 00000001424 14752347156 0017516 0 ustar 00root root 0000000 0000000 /*
* Copyright 2018 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '1.0.6'" to load this file. \quit
-- NO CHANGES HERE, ONLY IN C FILES
h3-pg-4.2.2/h3/sql/updates/h3--1.0.6--3.4.0.sql 0000664 0000000 0000000 00000001520 14752347156 0017514 0 ustar 00root root 0000000 0000000 /*
* Copyright 2018 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.4.0'" to load this file. \quit
DROP FUNCTION IF EXISTS h3_degs_to_rads(float);
DROP FUNCTION IF EXISTS h3_rads_to_degs(float);
h3-pg-4.2.2/h3/sql/updates/h3--3.4.0--3.4.1.sql 0000664 0000000 0000000 00000001357 14752347156 0017525 0 ustar 00root root 0000000 0000000 /*
* Copyright 2019 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.4.1'" to load this file. \quit
h3-pg-4.2.2/h3/sql/updates/h3--3.4.1--3.5.0.sql 0000664 0000000 0000000 00000004311 14752347156 0017517 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.5.0'" to load this file. \quit
CREATE OR REPLACE FUNCTION h3_get_faces(h3index) RETURNS integer[]
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_faces(h3index) IS
'Find all icosahedron faces intersected by a given H3 index';
ALTER FUNCTION h3_get_unidirectional_edge_boundary(h3index) RENAME TO h3_get_h3_unidirectional_edge_boundary;
-- replace separate length functions with single function
DROP FUNCTION IF EXISTS h3_edge_length_km(integer);
DROP FUNCTION IF EXISTS h3_edge_length_m(integer);
CREATE OR REPLACE FUNCTION h3_edge_length(resolution integer, km boolean DEFAULT FALSE) RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_edge_length(integer, boolean) IS
'Average hexagon edge length in (kilo)meters at the given resolution.';
-- replace separate area functions with single function
DROP FUNCTION IF EXISTS h3_hex_area_km2(integer);
DROP FUNCTION IF EXISTS h3_hex_area_m2(integer);
CREATE OR REPLACE FUNCTION h3_hex_area(resolution integer, km boolean DEFAULT FALSE) RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_hex_area(integer, boolean) IS
'Average hexagon area in square (kilo)meters at the given resolution.';
DROP FUNCTION IF EXISTS h3_hex_range(h3index, integer);
DROP FUNCTION IF EXISTS h3_hex_range_distances(h3index, integer);
DROP FUNCTION IF EXISTS h3_hex_ranges(h3index[], integer);
ALTER FUNCTION h3_set_to_linked_geo(h3index[]) RENAME TO h3_set_to_multi_polygon; h3-pg-4.2.2/h3/sql/updates/h3--3.5.0--3.6.0.sql 0000664 0000000 0000000 00000003672 14752347156 0017531 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.6.0'" to load this file. \quit
-- Hierarchical grid functions (hierarchy.c)
CREATE OR REPLACE FUNCTION h3_to_center_child(h3index, resolution integer DEFAULT -1) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_to_parent(h3index, resolution integer) IS
'Returns the center child (finer) index contained by input index at given resolution';
-- Miscellaneous H3 functions (miscellaneous.c)
CREATE OR REPLACE FUNCTION h3_get_pentagon_indexes(resolution integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_res_0_indexes() IS
'All the pentagon H3 indexes at the specified resolution.';
-- type casts
CREATE OR REPLACE FUNCTION h3index_to_bigint(h3index) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION bigint_to_h3index(bigint) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (h3index AS bigint) WITH FUNCTION h3index_to_bigint(h3index);
CREATE CAST (bigint AS h3index) WITH FUNCTION bigint_to_h3index(bigint);
-- string conversion already provided by type itself
DROP FUNCTION IF EXISTS h3_to_string(h3index);
DROP FUNCTION IF EXISTS h3_string_to_h3(cstring); h3-pg-4.2.2/h3/sql/updates/h3--3.6.0--3.6.1.sql 0000664 0000000 0000000 00000003233 14752347156 0017524 0 ustar 00root root 0000000 0000000 /*
* Copyright 2019 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.6.1'" to load this file. \quit
-- add R-tree operators
CREATE OR REPLACE FUNCTION h3index_overlaps(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_contains(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_contained_by(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OPERATOR && (
PROCEDURE = h3index_overlaps,
LEFTARG = h3index,
RIGHTARG = h3index,
COMMUTATOR = &&,
RESTRICT = contsel,
JOIN = contjoinsel
);
CREATE OPERATOR @> (
PROCEDURE = h3index_contains,
LEFTARG = h3index,
RIGHTARG = h3index,
COMMUTATOR = <@,
RESTRICT = contsel,
JOIN = contjoinsel
);
CREATE OPERATOR <@ (
PROCEDURE = h3index_contained_by,
LEFTARG = h3index,
RIGHTARG = h3index,
COMMUTATOR = @>,
RESTRICT = contsel,
JOIN = contjoinsel
);
h3-pg-4.2.2/h3/sql/updates/h3--3.6.1--3.6.2.sql 0000664 0000000 0000000 00000002324 14752347156 0017526 0 ustar 00root root 0000000 0000000 /*
* Copyright 2020 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.6.2'" to load this file. \quit
-- add sort support (see #24)
CREATE OR REPLACE FUNCTION h3index_sortsupport(internal)
RETURNS void
AS 'h3', 'h3index_sortsupport'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
ALTER OPERATOR family btree_h3index_ops USING btree ADD FUNCTION 2 (h3index) h3index_sortsupport(internal);
-- pass-by-value on supported systems (see #26)
UPDATE pg_type AS sink
SET typbyval = source.typbyval FROM (
SELECT typbyval FROM pg_type WHERE typname = 'int8'
) source
WHERE typname = 'h3index';
h3-pg-4.2.2/h3/sql/updates/h3--3.6.2--3.6.3.sql 0000664 0000000 0000000 00000001375 14752347156 0017535 0 ustar 00root root 0000000 0000000 /*
* Copyright 2020 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.6.3'" to load this file. \quit
-- no changes h3-pg-4.2.2/h3/sql/updates/h3--3.6.3--3.6.4.sql 0000664 0000000 0000000 00000001375 14752347156 0017537 0 ustar 00root root 0000000 0000000 /*
* Copyright 2020 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.6.4'" to load this file. \quit
-- no changes h3-pg-4.2.2/h3/sql/updates/h3--3.6.4--3.6.5.sql 0000664 0000000 0000000 00000003675 14752347156 0017546 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.6.5'" to load this file. \quit
-- Fix function flags which were previously only changed in install files
ALTER FUNCTION h3_get_h3_unidirectional_edge_boundary(h3index)
IMMUTABLE STRICT PARALLEL SAFE;
--ALTER FUNCTION h3_geo_to_h3(geometry, integer)
-- IMMUTABLE STRICT PARALLEL SAFE;
--ALTER FUNCTION h3_geo_to_h3(geography, integer)
-- IMMUTABLE STRICT PARALLEL SAFE;
--ALTER FUNCTION h3_to_geometry(h3index)
-- IMMUTABLE STRICT PARALLEL SAFE;
--ALTER FUNCTION h3_to_geography(h3index)
-- IMMUTABLE STRICT PARALLEL SAFE;
--ALTER FUNCTION h3_to_geo_boundary_geometry(h3index, boolean)
-- IMMUTABLE STRICT PARALLEL SAFE;
--ALTER FUNCTION h3_to_geo_boundary_geography(h3index, boolean)
-- IMMUTABLE STRICT PARALLEL SAFE;
--ALTER FUNCTION h3_polyfill(geometry, integer)
-- IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
--ALTER FUNCTION h3_polyfill(geography, integer)
-- IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
-- Add second support function for hash opclass
CREATE OR REPLACE FUNCTION h3index_hash_extended(h3index, int8) RETURNS int8
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
ALTER OPERATOR FAMILY hash_h3index_ops USING hash ADD
FUNCTION 2 h3index_hash_extended(h3index, int8); h3-pg-4.2.2/h3/sql/updates/h3--3.6.5--3.7.0.sql 0000664 0000000 0000000 00000006617 14752347156 0017542 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.7.0'" to load this file. \quit
CREATE OPERATOR <-> (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3_distance,
COMMUTATOR = <->
);
-- Broken since 1.0.0 on update path
--CREATE OR REPLACE FUNCTION h3_to_geometry(h3index) RETURNS geometry
-- AS $$ SELECT ST_SetSRID(h3_to_geo($1)::geometry, 4326) $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
--CREATE OR REPLACE FUNCTION h3_to_geography(h3index) RETURNS geography
-- AS $$ SELECT h3_to_geometry($1)::geography $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
-- New functions in core v3.7.0
CREATE OR REPLACE FUNCTION h3_point_dist(a point, b point, unit text DEFAULT 'km') RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_point_dist(point, point, text) IS
'The great circle distance in radians between two spherical coordinates.';
CREATE OR REPLACE FUNCTION h3_cell_area(cell h3index, unit text DEFAULT 'km^2') RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_cell_area(h3index, text) IS
'Exact area for a specific cell (hexagon or pentagon).';
CREATE OR REPLACE FUNCTION h3_exact_edge_length(edge h3index, unit text DEFAULT 'km') RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_exact_edge_length(h3index, text) IS
'Exact length for a specific unidirectional edge.';
-- New call signatures for hexarea and edgelength, using string instead of boolean
CREATE OR REPLACE FUNCTION h3_hex_area(resolution integer, unit text DEFAULT 'km') RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_hex_area(integer, text) IS
'Average hexagon area in square (kilo)meters at the given resolution.';
CREATE OR REPLACE FUNCTION h3_edge_length(resolution integer, unit text DEFAULT 'km') RETURNS float
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_edge_length(integer, text) IS
'Average hexagon edge length in (kilo)meters at the given resolution.';
DROP FUNCTION IF EXISTS h3_hex_area(integer, boolean);
DROP FUNCTION IF EXISTS h3_edge_length(integer, boolean);
CREATE OR REPLACE FUNCTION h3_hex_area(resolution integer, km boolean) RETURNS float
AS $$ SELECT h3_hex_area($1, CASE WHEN $2 THEN 'km' ELSE 'm' END) $$
IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
COMMENT ON FUNCTION h3_hex_area(integer, boolean) IS
'Deprecated: use string for unit';
CREATE OR REPLACE FUNCTION h3_edge_length(resolution integer, km boolean) RETURNS float
AS $$ SELECT h3_edge_length($1, CASE WHEN $2 THEN 'km' ELSE 'm' END) $$
IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
COMMENT ON FUNCTION h3_edge_length(integer, boolean) IS
'Deprecated: use string for unit';
h3-pg-4.2.2/h3/sql/updates/h3--3.7.0--3.7.1.sql 0000664 0000000 0000000 00000002320 14752347156 0017522 0 ustar 00root root 0000000 0000000 /*
* Copyright 2021 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.7.1'" to load this file. \quit
COMMENT ON OPERATOR && (h3index, h3index) IS
'Returns true if the two H3 indexes intersect';
COMMENT ON OPERATOR @> (h3index, h3index) IS
'Returns true if A containts B';
COMMENT ON OPERATOR <@ (h3index, h3index) IS
'Returns true if A is contained by B';
COMMENT ON OPERATOR = (h3index, h3index) IS
'Returns true if two indexes are the same';
COMMENT ON FUNCTION h3_hex_area(integer, boolean) IS
NULL;
COMMENT ON FUNCTION h3_edge_length(integer, boolean) IS
NULL; h3-pg-4.2.2/h3/sql/updates/h3--3.7.1--3.7.2.sql 0000664 0000000 0000000 00000001375 14752347156 0017535 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '3.7.2'" to load this file. \quit
-- no changes h3-pg-4.2.2/h3/sql/updates/h3--3.7.2--4.0.0.sql 0000664 0000000 0000000 00000030361 14752347156 0017523 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.0.0'" to load this file. \quit
-- Move postgis integration to its own extension
DROP FUNCTION IF EXISTS h3_geo_to_h3(geometry, resolution integer);
DROP FUNCTION IF EXISTS h3_geo_to_h3(geography, resolution integer);
DROP FUNCTION IF EXISTS h3_to_geo_boundary_geometry(h3index);
DROP FUNCTION IF EXISTS h3_to_geo_boundary_geography(h3index);
DROP FUNCTION IF EXISTS h3_to_geo_boundary_geometry(h3index, extend boolean);
DROP FUNCTION IF EXISTS h3_to_geo_boundary_geography(h3index, extend boolean);
DROP FUNCTION IF EXISTS h3_polyfill(multi geometry, resolution integer);
DROP FUNCTION IF EXISTS h3_polyfill(multi geography, resolution integer);
DROP CAST IF EXISTS (h3index AS geometry);
DROP CAST IF EXISTS (h3index AS geography);
DROP FUNCTION IF EXISTS h3_to_geometry(h3index);
DROP FUNCTION IF EXISTS h3_to_geography(h3index);
-- H3 Core v4 renames
-- indexing
ALTER FUNCTION h3_geo_to_h3(point, resolution integer) RENAME TO h3_lat_lng_to_cell;
CREATE OR REPLACE FUNCTION
h3_lat_lng_to_cell(latlng point, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
ALTER FUNCTION h3_to_geo(h3index) RENAME TO h3_cell_to_lat_lng;
CREATE OR REPLACE FUNCTION
h3_cell_to_lat_lng(cell h3index) RETURNS point
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
ALTER FUNCTION h3_to_geo_boundary(h3index, extend_at_meridian BOOLEAN) RENAME TO h3_cell_to_boundary;
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary(cell h3index, extend_at_meridian boolean DEFAULT FALSE) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- inspection
ALTER FUNCTION h3_get_base_cell(h3index) RENAME TO h3_get_base_cell_number;
ALTER FUNCTION h3_is_valid(h3index) RENAME TO h3_is_valid_cell;
ALTER FUNCTION h3_get_faces(h3index) RENAME TO h3_get_icosahedron_faces;
-- traversal
ALTER FUNCTION h3_k_ring(h3index, k integer) RENAME TO h3_grid_disk;
CREATE OR REPLACE FUNCTION
h3_grid_disk(origin h3index, k integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
ALTER FUNCTION h3_k_ring_distances(h3index, k integer, OUT index h3index, OUT distance int) RENAME TO h3_grid_disk_distances;
CREATE OR REPLACE FUNCTION
h3_grid_disk_distances(origin h3index, k integer DEFAULT 1, OUT index h3index, OUT distance int) RETURNS SETOF record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
DROP FUNCTION h3_hex_ring(h3index, k integer);
CREATE OR REPLACE FUNCTION
h3_grid_ring_unsafe(origin h3index, k integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_ring_unsafe(h3index, integer)
IS 'Returns the hollow hexagonal ring centered at origin with distance "k"';
DROP FUNCTION h3_line(h3index, h3index);
CREATE OR REPLACE FUNCTION
h3_grid_path_cells(origin h3index, destination h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_path_cells(h3index, h3index)
IS 'Given two H3 indexes, return the line of indexes between them (inclusive).
This function may fail to find the line between two indexes, for
example if they are very far apart. It may also fail when finding
distances for indexes on opposite sides of a pentagon.';
DROP OPERATOR <-> (h3index, h3index);
DROP FUNCTION h3_distance(h3index, h3index);
CREATE OR REPLACE FUNCTION
h3_grid_distance(origin h3index, destination h3index) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
ALTER FUNCTION h3_experimental_h3_to_local_ij(origin h3index, index h3index) RENAME TO h3_cell_to_local_ij;
ALTER FUNCTION h3_experimental_local_ij_to_h3(origin h3index, coord POINT) RENAME TO h3_local_ij_to_cell;
CREATE OPERATOR <-> (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3_grid_distance,
COMMUTATOR = <->
);
COMMENT ON OPERATOR <-> (h3index, h3index) IS
'Returns the distance in grid cells between the two indices';
-- hierarchy
DROP FUNCTION h3_to_parent(h3index, resolution integer);
CREATE OR REPLACE FUNCTION
h3_cell_to_parent(cell h3index, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_parent(cell h3index, resolution integer)
IS 'Returns the parent of the given index';
DROP FUNCTION h3_to_children(h3index, resolution integer);
CREATE OR REPLACE FUNCTION
h3_cell_to_children(cell h3index, resolution integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_children(cell h3index, resolution integer)
IS 'Returns the set of children of the given index';
DROP FUNCTION h3_to_center_child(h3index, resolution integer);
CREATE OR REPLACE FUNCTION
h3_cell_to_center_child(cell h3index, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_center_child(cell h3index, resolution integer)
IS 'Returns the center child (finer) index contained by input index at given resolution';
DROP FUNCTION h3_compact(h3index[]);
CREATE OR REPLACE FUNCTION
h3_compact_cells(cells h3index[]) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_compact_cells(cells h3index[])
IS 'Compacts the given array as best as possible';
DROP FUNCTION h3_uncompact(h3index[], resolution integer);
CREATE OR REPLACE FUNCTION
h3_uncompact_cells(cells h3index[], resolution integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_uncompact_cells(cells h3index[], resolution integer)
IS 'Uncompacts the given array at the given resolution.';
DROP FUNCTION IF EXISTS h3_to_children_slow(index h3index);
DROP FUNCTION IF EXISTS h3_to_children_slow(index h3index, resolution integer);
DROP FUNCTION IF EXISTS __h3_to_children_aux(index h3index, resolution integer, current integer);
--copied new
CREATE OR REPLACE FUNCTION
h3_cell_to_parent(cell h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_parent(cell h3index)
IS 'Returns the parent of the given index';
CREATE OR REPLACE FUNCTION
h3_cell_to_children(cell h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_children(cell h3index)
IS 'Returns the set of children of the given index';
CREATE OR REPLACE FUNCTION
h3_cell_to_center_child(cell h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_center_child(cell h3index)
IS 'Returns the center child (finer) index contained by input index at next resolution';
CREATE OR REPLACE FUNCTION
h3_uncompact_cells(cells h3index[]) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_uncompact_cells(cells h3index[])
IS 'Uncompacts the given array at the resolution one higher than the highest resolution in the set';
CREATE OR REPLACE FUNCTION __h3_cell_to_children_aux(index h3index, resolution integer, current integer)
RETURNS SETOF h3index AS $$
DECLARE
retSet h3index[];
r h3index;
BEGIN
IF current = -1 THEN
SELECT h3_get_resolution(index) into current;
END IF;
IF resolution = -1 THEN
SELECT h3_get_resolution(index)+1 into resolution;
END IF;
IF current < resolution THEN
SELECT ARRAY(SELECT h3_cell_to_children(index)) into retSet;
FOREACH r in ARRAY retSet LOOP
RETURN QUERY SELECT __h3_cell_to_children_aux(r, resolution, current + 1);
END LOOP;
ELSE
RETURN NEXT index;
END IF;
END;$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION h3_cell_to_children_slow(index h3index, resolution integer) RETURNS SETOF h3index
AS $$ SELECT __h3_cell_to_children_aux($1, $2, -1) $$ LANGUAGE SQL;
COMMENT ON FUNCTION h3_cell_to_children_slow(index h3index, resolution integer) IS
'Slower version of H3ToChildren but allocates less memory';
CREATE OR REPLACE FUNCTION h3_cell_to_children_slow(index h3index) RETURNS SETOF h3index
AS $$ SELECT __h3_cell_to_children_aux($1, -1, -1) $$ LANGUAGE SQL;
COMMENT ON FUNCTION h3_cell_to_children_slow(index h3index) IS
'Slower version of H3ToChildren but allocates less memory';
-- regions
ALTER FUNCTION h3_polyfill(exterior polygon, holes polygon[], resolution integer) RENAME TO h3_polygon_to_cells;
ALTER FUNCTION h3_set_to_multi_polygon(h3index[], OUT exterior polygon, OUT holes polygon[]) RENAME TO h3_cells_to_multi_polygon;
-- edge
ALTER FUNCTION h3_indexes_are_neighbors(h3index, h3index) RENAME TO h3_are_neighbor_cells;
CREATE OR REPLACE FUNCTION
h3_are_neighbor_cells(origin h3index, destination h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
ALTER FUNCTION h3_get_h3_unidirectional_edge(origin h3index, destination h3index) RENAME TO h3_cells_to_directed_edge;
ALTER FUNCTION h3_unidirectional_edge_is_valid(edge h3index) RENAME TO h3_is_valid_directed_edge;
ALTER FUNCTION h3_get_origin_h3_index_from_unidirectional_edge(edge h3index) RENAME TO h3_get_directed_edge_origin;
ALTER FUNCTION h3_get_destination_h3_index_from_unidirectional_edge(edge h3index) RENAME TO h3_get_directed_edge_destination;
ALTER FUNCTION h3_get_h3_indexes_from_unidirectional_edge(edge h3index, OUT origin h3index, OUT destination h3index) RENAME TO h3_directed_edge_to_cells;
ALTER FUNCTION h3_get_h3_unidirectional_edges_from_hexagon(h3index) RENAME TO h3_origin_to_directed_edges;
ALTER FUNCTION h3_get_h3_unidirectional_edge_boundary(edge h3index) RENAME TO h3_directed_edge_to_boundary;
-- miscellaneous
ALTER FUNCTION h3_point_dist(a point, b point, unit text) RENAME TO h3_great_circle_distance;
ALTER FUNCTION h3_hex_area(resolution integer, unit text) RENAME TO h3_get_hexagon_area_avg;
DROP FUNCTION IF EXISTS h3_edge_length(resolution integer, unit text);
CREATE OR REPLACE FUNCTION
h3_get_hexagon_edge_length_avg(resolution integer, unit text DEFAULT 'km') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_hexagon_edge_length_avg(integer, text)
IS 'Average hexagon edge length in (kilo)meters at the given resolution';
ALTER FUNCTION h3_exact_edge_length(edge h3index, unit text) RENAME TO h3_edge_length;
ALTER FUNCTION h3_num_hexagons(resolution integer) RENAME TO h3_get_num_cells;
ALTER FUNCTION h3_get_res_0_indexes() RENAME TO h3_get_res_0_cells;
ALTER FUNCTION h3_get_pentagon_indexes(resolution integer) RENAME TO h3_get_pentagons;
-- deprecated
DROP FUNCTION IF EXISTS h3_hex_area(integer, boolean);
DROP FUNCTION IF EXISTS h3_edge_length(integer, boolean);
-- copied from 07-vertex.sql
CREATE OR REPLACE FUNCTION
h3_cell_to_vertex(cell h3index, vertexNum integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_vertex(cell h3index, vertexNum integer)
IS 'Returns a single vertex for a given cell, as an H3 index';
CREATE OR REPLACE FUNCTION
h3_cell_to_vertexes(cell h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_vertexes(cell h3index)
IS 'Returns all vertexes for a given cell, as H3 indexes';
CREATE OR REPLACE FUNCTION
h3_vertex_to_lat_lng(vertex h3index) RETURNS point
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_vertex_to_lat_lng(vertex h3index)
IS 'Get the geocoordinates of an H3 vertex';
CREATE OR REPLACE FUNCTION
h3_is_valid_vertex(vertex h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_valid_vertex(vertex h3index)
IS 'Whether the input is a valid H3 vertex';
h3-pg-4.2.2/h3/sql/updates/h3--4.0.0--4.0.1.sql 0000664 0000000 0000000 00000017444 14752347156 0017523 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.0.1'" to load this file. \quit
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary_wkb(cell h3index) RETURNS bytea
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary_wkb(h3index)
IS 'Finds the boundary of the index, converts to EWKB.
Splits polygons when crossing 180th meridian.
This function has to return WKB since Postgres does not provide multipolygon type.';
-- deprecate extend flag
DROP FUNCTION IF EXISTS h3_cell_to_boundary(cell h3index, extend_at_meridian boolean);
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary(cell h3index, extend_antimeridian boolean) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary(h3index, boolean)
IS 'DEPRECATED: Use `SET h3.extend_antimeridian TO true` instead.';
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary(cell h3index) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary(h3index)
IS 'Finds the boundary of the index.
Use `SET h3.extend_antimeridian TO true` to extend coordinates when crossing 180th meridian.';
-- fix comments
-- indexing
COMMENT ON FUNCTION
h3_lat_lng_to_cell(point, integer)
IS 'Indexes the location at the specified resolution.';
COMMENT ON FUNCTION
h3_cell_to_lat_lng(h3index)
IS 'Finds the centroid of the index.';
-- inspection
COMMENT ON FUNCTION
h3_get_resolution(h3index)
IS 'Returns the resolution of the index.';
COMMENT ON FUNCTION
h3_get_base_cell_number(h3index)
IS 'Returns the base cell number of the index.';
COMMENT ON FUNCTION
h3_is_valid_cell(h3index)
IS 'Returns true if the given H3Index is valid.';
COMMENT ON FUNCTION
h3_is_res_class_iii(h3index)
IS 'Returns true if this index has a resolution with Class III orientation.';
COMMENT ON FUNCTION
h3_is_pentagon(h3index)
IS 'Returns true if this index represents a pentagonal cell.';
COMMENT ON FUNCTION
h3_get_icosahedron_faces(h3index)
IS 'Find all icosahedron faces intersected by a given H3 index.';
-- traversal
COMMENT ON FUNCTION
h3_grid_disk(h3index, integer)
IS 'Produces indices within "k" distance of the origin index.';
COMMENT ON FUNCTION
h3_grid_disk_distances(h3index, integer)
IS 'Produces indices within "k" distance of the origin index paired with their distance to the origin.';
COMMENT ON FUNCTION
h3_grid_ring_unsafe(h3index, integer)
IS 'Returns the hollow hexagonal ring centered at origin with distance "k".';
COMMENT ON FUNCTION
h3_grid_distance(h3index, h3index)
IS 'Returns the distance in grid cells between the two indices.';
COMMENT ON FUNCTION
h3_cell_to_local_ij(h3index, h3index)
IS 'Produces local IJ coordinates for an H3 index anchored by an origin.';
COMMENT ON FUNCTION
h3_local_ij_to_cell(h3index, point)
IS 'Produces an H3 index from local IJ coordinates anchored by an origin.';
-- hierarchy
COMMENT ON FUNCTION
h3_cell_to_parent(cell h3index, resolution integer)
IS 'Returns the parent of the given index.';
COMMENT ON FUNCTION
h3_cell_to_children(cell h3index, resolution integer)
IS 'Returns the set of children of the given index.';
COMMENT ON FUNCTION
h3_cell_to_center_child(cell h3index, resolution integer)
IS 'Returns the center child (finer) index contained by input index at given resolution.';
COMMENT ON FUNCTION
h3_compact_cells(cells h3index[])
IS 'Compacts the given array as best as possible.';
COMMENT ON FUNCTION
h3_cell_to_parent(cell h3index)
IS 'Returns the parent of the given index.';
COMMENT ON FUNCTION
h3_cell_to_children(cell h3index)
IS 'Returns the set of children of the given index.';
COMMENT ON FUNCTION
h3_cell_to_center_child(cell h3index)
IS 'Returns the center child (finer) index contained by input index at next resolution.';
COMMENT ON FUNCTION
h3_uncompact_cells(cells h3index[])
IS 'Uncompacts the given array at the resolution one higher than the highest resolution in the set.';
COMMENT ON FUNCTION
h3_cell_to_children_slow(index h3index, resolution integer)
IS 'Slower version of H3ToChildren but allocates less memory.';
COMMENT ON FUNCTION
h3_cell_to_children_slow(index h3index)
IS 'Slower version of H3ToChildren but allocates less memory.';
-- regions
COMMENT ON FUNCTION
h3_polygon_to_cells(polygon, polygon[], integer)
IS 'Takes an exterior polygon [and a set of hole polygon] and returns the set of hexagons that best fit the structure.';
COMMENT ON FUNCTION
h3_cells_to_multi_polygon(h3index[])
IS 'Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons. Polygon outlines will follow GeoJSON MultiPolygon order: Each polygon will have one outer loop, which is first in the list, followed by any holes.';
-- edge
COMMENT ON FUNCTION
h3_are_neighbor_cells(origin h3index, destination h3index)
IS 'Returns true if the given indices are neighbors.';
COMMENT ON FUNCTION
h3_origin_to_directed_edges(h3index)
IS 'Returns all unidirectional edges with the given index as origin.';
-- vertex
COMMENT ON FUNCTION
h3_cell_to_vertex(cell h3index, vertexNum integer)
IS 'Returns a single vertex for a given cell, as an H3 index.';
COMMENT ON FUNCTION
h3_cell_to_vertexes(cell h3index)
IS 'Returns all vertexes for a given cell, as H3 indexes.';
COMMENT ON FUNCTION
h3_vertex_to_lat_lng(vertex h3index)
IS 'Get the geocoordinates of an H3 vertex.';
COMMENT ON FUNCTION
h3_is_valid_vertex(vertex h3index)
IS 'Whether the input is a valid H3 vertex.';
-- miscellaneous
COMMENT ON FUNCTION
h3_great_circle_distance(point, point, text)
IS 'The great circle distance in radians between two spherical coordinates.';
COMMENT ON FUNCTION
h3_get_hexagon_area_avg(integer, text)
IS 'Average hexagon area in square (kilo)meters at the given resolution.';
COMMENT ON FUNCTION
h3_cell_area(h3index, text)
IS 'Exact area for a specific cell (hexagon or pentagon).';
COMMENT ON FUNCTION
h3_get_hexagon_edge_length_avg(integer, text)
IS 'Average hexagon edge length in (kilo)meters at the given resolution.';
COMMENT ON FUNCTION
h3_edge_length(h3index, text)
IS 'Exact length for a specific unidirectional edge.';
COMMENT ON FUNCTION
h3_get_num_cells(integer) IS
'Number of unique H3 indexes at the given resolution.';
COMMENT ON FUNCTION
h3_get_res_0_cells()
IS 'Returns all 122 resolution 0 indexes.';
COMMENT ON FUNCTION
h3_get_pentagons(resolution integer)
IS 'All the pentagon H3 indexes at the specified resolution.';
-- operators
COMMENT ON OPERATOR = (h3index, h3index) IS
'Returns true if two indexes are the same.';
COMMENT ON OPERATOR && (h3index, h3index) IS
'Returns true if the two H3 indexes intersect.';
COMMENT ON OPERATOR @> (h3index, h3index) IS
'Returns true if A containts B.';
COMMENT ON OPERATOR <@ (h3index, h3index) IS
'Returns true if A is contained by B.';
COMMENT ON OPERATOR <-> (h3index, h3index) IS
'Returns the distance in grid cells between the two indices.';
-- casts
COMMENT ON CAST (h3index AS bigint) IS
'Convert H3 index to bigint, which is useful when you need a decimal representation.';
COMMENT ON CAST (h3index AS bigint) IS
'Convert bigint to H3 index.';
COMMENT ON CAST (h3index AS point) IS
'Convert H3 index to point.';
h3-pg-4.2.2/h3/sql/updates/h3--4.0.1--4.0.2.sql 0000664 0000000 0000000 00000046274 14752347156 0017530 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.0.2'" to load this file. \quit
-- Due to mishandling of C-level renames in previous releases,
-- this upgrade attempts to align user-installations as much as possible
-- by copying the full-install script in its entirety
-- (except casts/operators/etc)
--
-- For more information see PR #87
CREATE OR REPLACE FUNCTION
h3index_in(cstring) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION
h3index_out(h3index) RETURNS cstring
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION
h3_lat_lng_to_cell(latlng point, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_lat_lng_to_cell(point, integer)
IS 'Indexes the location at the specified resolution.';
CREATE OR REPLACE FUNCTION
h3_cell_to_lat_lng(cell h3index) RETURNS point
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_lat_lng(h3index)
IS 'Finds the centroid of the index.';
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary(cell h3index) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary(h3index)
IS 'Finds the boundary of the index.
Use `SET h3.extend_antimeridian TO true` to extend coordinates when crossing 180th meridian.';
CREATE OR REPLACE FUNCTION
h3_get_resolution(h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_resolution(h3index)
IS 'Returns the resolution of the index.';
CREATE OR REPLACE FUNCTION
h3_get_base_cell_number(h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_base_cell_number(h3index)
IS 'Returns the base cell number of the index.';
CREATE OR REPLACE FUNCTION
h3_is_valid_cell(h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_valid_cell(h3index)
IS 'Returns true if the given H3Index is valid.';
CREATE OR REPLACE FUNCTION
h3_is_res_class_iii(h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_res_class_iii(h3index)
IS 'Returns true if this index has a resolution with Class III orientation.';
CREATE OR REPLACE FUNCTION
h3_is_pentagon(h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_pentagon(h3index)
IS 'Returns true if this index represents a pentagonal cell.';
CREATE OR REPLACE FUNCTION
h3_get_icosahedron_faces(h3index) RETURNS integer[]
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_icosahedron_faces(h3index)
IS 'Find all icosahedron faces intersected by a given H3 index.';
CREATE OR REPLACE FUNCTION
h3_grid_disk(origin h3index, k integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_disk(h3index, integer)
IS 'Produces indices within "k" distance of the origin index.';
CREATE OR REPLACE FUNCTION
h3_grid_disk_distances(origin h3index, k integer DEFAULT 1, OUT index h3index, OUT distance int) RETURNS SETOF record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_disk_distances(h3index, integer)
IS 'Produces indices within "k" distance of the origin index paired with their distance to the origin.';
CREATE OR REPLACE FUNCTION
h3_grid_ring_unsafe(origin h3index, k integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_ring_unsafe(h3index, integer)
IS 'Returns the hollow hexagonal ring centered at origin with distance "k".';
CREATE OR REPLACE FUNCTION
h3_grid_path_cells(origin h3index, destination h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_path_cells(h3index, h3index)
IS 'Given two H3 indexes, return the line of indexes between them (inclusive).
This function may fail to find the line between two indexes, for
example if they are very far apart. It may also fail when finding
distances for indexes on opposite sides of a pentagon.';
CREATE OR REPLACE FUNCTION
h3_grid_distance(origin h3index, destination h3index) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_grid_distance(h3index, h3index)
IS 'Returns the distance in grid cells between the two indices.';
CREATE OR REPLACE FUNCTION
h3_cell_to_local_ij(origin h3index, index h3index) RETURNS point
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_local_ij(h3index, h3index)
IS 'Produces local IJ coordinates for an H3 index anchored by an origin.';
CREATE OR REPLACE FUNCTION
h3_local_ij_to_cell(origin h3index, coord point) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_local_ij_to_cell(h3index, point)
IS 'Produces an H3 index from local IJ coordinates anchored by an origin.';
CREATE OR REPLACE FUNCTION
h3_cell_to_parent(cell h3index, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_parent(cell h3index, resolution integer)
IS 'Returns the parent of the given index.';
CREATE OR REPLACE FUNCTION
h3_cell_to_children(cell h3index, resolution integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_children(cell h3index, resolution integer)
IS 'Returns the set of children of the given index.';
CREATE OR REPLACE FUNCTION
h3_cell_to_center_child(cell h3index, resolution integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_center_child(cell h3index, resolution integer)
IS 'Returns the center child (finer) index contained by input index at given resolution.';
CREATE OR REPLACE FUNCTION
h3_compact_cells(cells h3index[]) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_compact_cells(cells h3index[])
IS 'Compacts the given array as best as possible.';
CREATE OR REPLACE FUNCTION
h3_uncompact_cells(cells h3index[], resolution integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_uncompact_cells(cells h3index[], resolution integer)
IS 'Uncompacts the given array at the given resolution.';
CREATE OR REPLACE FUNCTION
h3_cell_to_parent(cell h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_parent(cell h3index)
IS 'Returns the parent of the given index.';
CREATE OR REPLACE FUNCTION
h3_cell_to_children(cell h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_children(cell h3index)
IS 'Returns the set of children of the given index.';
CREATE OR REPLACE FUNCTION
h3_cell_to_center_child(cell h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_center_child(cell h3index)
IS 'Returns the center child (finer) index contained by input index at next resolution.';
CREATE OR REPLACE FUNCTION
h3_uncompact_cells(cells h3index[]) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_uncompact_cells(cells h3index[])
IS 'Uncompacts the given array at the resolution one higher than the highest resolution in the set.';
CREATE OR REPLACE FUNCTION __h3_cell_to_children_aux(index h3index, resolution integer, current integer)
RETURNS SETOF h3index AS $$
DECLARE
retSet h3index[];
r h3index;
BEGIN
IF current = -1 THEN
SELECT h3_get_resolution(index) into current;
END IF;
IF resolution = -1 THEN
SELECT h3_get_resolution(index)+1 into resolution;
END IF;
IF current < resolution THEN
SELECT ARRAY(SELECT h3_cell_to_children(index)) into retSet;
FOREACH r in ARRAY retSet LOOP
RETURN QUERY SELECT __h3_cell_to_children_aux(r, resolution, current + 1);
END LOOP;
ELSE
RETURN NEXT index;
END IF;
END;$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION h3_cell_to_children_slow(index h3index, resolution integer) RETURNS SETOF h3index
AS $$ SELECT __h3_cell_to_children_aux($1, $2, -1) $$ LANGUAGE SQL;
COMMENT ON FUNCTION h3_cell_to_children_slow(index h3index, resolution integer) IS
'Slower version of H3ToChildren but allocates less memory.';
CREATE OR REPLACE FUNCTION h3_cell_to_children_slow(index h3index) RETURNS SETOF h3index
AS $$ SELECT __h3_cell_to_children_aux($1, -1, -1) $$ LANGUAGE SQL;
COMMENT ON FUNCTION h3_cell_to_children_slow(index h3index) IS
'Slower version of H3ToChildren but allocates less memory.';
CREATE OR REPLACE FUNCTION
h3_polygon_to_cells(exterior polygon, holes polygon[], resolution integer DEFAULT 1) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE
-- intentionally NOT STRICT
CALLED ON NULL INPUT PARALLEL SAFE; COMMENT ON FUNCTION
h3_polygon_to_cells(polygon, polygon[], integer)
IS 'Takes an exterior polygon [and a set of hole polygon] and returns the set of hexagons that best fit the structure.';
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon(h3index[], OUT exterior polygon, OUT holes polygon[]) RETURNS SETOF record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cells_to_multi_polygon(h3index[])
IS 'Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons. Polygon outlines will follow GeoJSON MultiPolygon order: Each polygon will have one outer loop, which is first in the list, followed by any holes.';
CREATE OR REPLACE FUNCTION
h3_are_neighbor_cells(origin h3index, destination h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_are_neighbor_cells(origin h3index, destination h3index)
IS 'Returns true if the given indices are neighbors.';
CREATE OR REPLACE FUNCTION
h3_cells_to_directed_edge(origin h3index, destination h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cells_to_directed_edge(origin h3index, destination h3index)
IS 'Returns a unidirectional edge H3 index based on the provided origin and destination.';
CREATE OR REPLACE FUNCTION
h3_is_valid_directed_edge(edge h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_valid_directed_edge(edge h3index)
IS 'Returns true if the given edge is valid.';
CREATE OR REPLACE FUNCTION
h3_get_directed_edge_origin(edge h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_directed_edge_origin(edge h3index)
IS 'Returns the origin index from the given edge.';
CREATE OR REPLACE FUNCTION
h3_get_directed_edge_destination(edge h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_directed_edge_destination(edge h3index)
IS 'Returns the destination index from the given edge.';
CREATE OR REPLACE FUNCTION
h3_directed_edge_to_cells(edge h3index, OUT origin h3index, OUT destination h3index) RETURNS record
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_directed_edge_to_cells(edge h3index)
IS 'Returns the pair of indices from the given edge.';
CREATE OR REPLACE FUNCTION
h3_origin_to_directed_edges(h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_origin_to_directed_edges(h3index)
IS 'Returns all unidirectional edges with the given index as origin.';
CREATE OR REPLACE FUNCTION
h3_directed_edge_to_boundary(edge h3index) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_directed_edge_to_boundary(edge h3index)
IS 'Provides the coordinates defining the unidirectional edge.';
CREATE OR REPLACE FUNCTION
h3_cell_to_vertex(cell h3index, vertexNum integer) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_vertex(cell h3index, vertexNum integer)
IS 'Returns a single vertex for a given cell, as an H3 index.';
CREATE OR REPLACE FUNCTION
h3_cell_to_vertexes(cell h3index) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_vertexes(cell h3index)
IS 'Returns all vertexes for a given cell, as H3 indexes.';
CREATE OR REPLACE FUNCTION
h3_vertex_to_lat_lng(vertex h3index) RETURNS point
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_vertex_to_lat_lng(vertex h3index)
IS 'Get the geocoordinates of an H3 vertex.';
CREATE OR REPLACE FUNCTION
h3_is_valid_vertex(vertex h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_is_valid_vertex(vertex h3index)
IS 'Whether the input is a valid H3 vertex.';
CREATE OR REPLACE FUNCTION
h3_great_circle_distance(a point, b point, unit text DEFAULT 'km') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_great_circle_distance(point, point, text)
IS 'The great circle distance in radians between two spherical coordinates.';
CREATE OR REPLACE FUNCTION
h3_get_hexagon_area_avg(resolution integer, unit text DEFAULT 'km') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_hexagon_area_avg(integer, text)
IS 'Average hexagon area in square (kilo)meters at the given resolution.';
CREATE OR REPLACE FUNCTION
h3_cell_area(cell h3index, unit text DEFAULT 'km^2') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_area(h3index, text)
IS 'Exact area for a specific cell (hexagon or pentagon).';
CREATE OR REPLACE FUNCTION
h3_get_hexagon_edge_length_avg(resolution integer, unit text DEFAULT 'km') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_hexagon_edge_length_avg(integer, text)
IS 'Average hexagon edge length in (kilo)meters at the given resolution.';
CREATE OR REPLACE FUNCTION
h3_edge_length(edge h3index, unit text DEFAULT 'km') RETURNS double precision
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_edge_length(h3index, text)
IS 'Exact length for a specific unidirectional edge.';
CREATE OR REPLACE FUNCTION
h3_get_num_cells(resolution integer) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_num_cells(integer) IS
'Number of unique H3 indexes at the given resolution.';
CREATE OR REPLACE FUNCTION
h3_get_res_0_cells() RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_res_0_cells()
IS 'Returns all 122 resolution 0 indexes.';
CREATE OR REPLACE FUNCTION
h3_get_pentagons(resolution integer) RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_get_pentagons(resolution integer)
IS 'All the pentagon H3 indexes at the specified resolution.';
CREATE OR REPLACE FUNCTION h3index_eq(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_ne(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_lt(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_le(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_gt(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_ge(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_overlaps(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON OPERATOR && (h3index, h3index) IS
'Returns true if the two H3 indexes intersect.';
CREATE OR REPLACE FUNCTION h3index_contains(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON OPERATOR @> (h3index, h3index) IS
'Returns true if A containts B.';
CREATE OR REPLACE FUNCTION h3index_contained_by(h3index, h3index) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON OPERATOR <@ (h3index, h3index) IS
'Returns true if A is contained by B.';
COMMENT ON OPERATOR <-> (h3index, h3index) IS
'Returns the distance in grid cells between the two indices.';
CREATE OR REPLACE FUNCTION h3index_cmp(h3index, h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_sortsupport(internal)
RETURNS void
AS 'h3', 'h3index_sortsupport'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_hash(h3index) RETURNS integer
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_hash_extended(h3index, int8) RETURNS int8
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION
h3index_to_bigint(h3index) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON CAST (h3index AS bigint) IS
'Convert H3 index to bigint, which is useful when you need a decimal representation.';
CREATE OR REPLACE FUNCTION
bigint_to_h3index(bigint) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON CAST (h3index AS bigint) IS
'Convert bigint to H3 index.';
COMMENT ON CAST (h3index AS point) IS
'Convert H3 index to point.';
CREATE OR REPLACE FUNCTION h3_get_extension_version() RETURNS text
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_get_extension_version() IS
'Get the currently installed version of the extension.';
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary_wkb(cell h3index) RETURNS bytea
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary_wkb(h3index)
IS 'Finds the boundary of the index, converts to EWKB.
Splits polygons when crossing 180th meridian.
This function has to return WKB since Postgres does not provide multipolygon type.';
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary(cell h3index, extend_antimeridian boolean) RETURNS polygon
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary(h3index, boolean)
IS 'DEPRECATED: Use `SET h3.extend_antimeridian TO true` instead.';
h3-pg-4.2.2/h3/sql/updates/h3--4.0.2--4.0.3.sql 0000664 0000000 0000000 00000003053 14752347156 0017516 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.0.3'" to load this file. \quit
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon_wkb(h3index[]) RETURNS bytea
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cells_to_multi_polygon_wkb(h3index[])
IS 'Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons, converts to EWKB.
Splits polygons when crossing 180th meridian.';
-- BRIN operator class
CREATE OPERATOR CLASS h3index_minmax_ops DEFAULT FOR TYPE h3index USING brin AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 brin_minmax_opcinfo(internal),
FUNCTION 2 brin_minmax_add_value(internal, internal, internal, internal),
FUNCTION 3 brin_minmax_consistent(internal, internal, internal),
FUNCTION 4 brin_minmax_union(internal, internal, internal);
h3-pg-4.2.2/h3/sql/updates/h3--4.0.3--4.1.0.sql 0000664 0000000 0000000 00000005713 14752347156 0017522 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.1.0'" to load this file. \quit
DROP FUNCTION IF EXISTS h3_cell_to_boundary_wkb(h3index);
DROP FUNCTION IF EXISTS h3_cells_to_multi_polygon_wkb(h3index[]);
ALTER OPERATOR FAMILY btree_h3index_ops USING btree RENAME TO h3index_ops;
ALTER OPERATOR CLASS btree_h3index_ops USING btree RENAME TO h3index_ops;
ALTER OPERATOR FAMILY hash_h3index_ops USING hash RENAME TO h3index_ops;
ALTER OPERATOR CLASS hash_h3index_ops USING hash RENAME TO h3index_ops;
CREATE OR REPLACE FUNCTION h3_pg_migrate_pass_by_reference(h3index) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_pg_migrate_pass_by_reference(h3index) IS
'Migrate h3index from pass-by-reference to pass-by-value.';
-- make distance operator allow different resolutions
CREATE OR REPLACE FUNCTION h3index_distance(h3index, h3index) RETURNS bigint
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
DROP OPERATOR IF EXISTS <-> (h3index, h3index);
CREATE OPERATOR <-> (
LEFTARG = h3index,
RIGHTARG = h3index,
PROCEDURE = h3index_distance,
COMMUTATOR = <->
);
COMMENT ON OPERATOR <-> (h3index, h3index) IS
'Returns the distance in grid cells between the two indices (at the lowest resolution of the two).';
-- new child pos functions
-- see https://github.com/uber/h3/pull/719
CREATE OR REPLACE FUNCTION
h3_cell_to_child_pos(child h3index, parentRes integer) RETURNS int8
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_child_pos(child h3index, parentRes integer)
IS 'Returns the position of the child cell within an ordered list of all children of the cells parent at the specified resolution parentRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of childPosToCell.';
CREATE OR REPLACE FUNCTION
h3_child_pos_to_cell(childPos int8, parent h3index, childRes int) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_child_pos_to_cell(childPos int8, parent h3index, childRes int)
IS 'Returns the child cell at a given position within an ordered list of all children of parent at the specified resolution childRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of cellToChildPos.';
h3-pg-4.2.2/h3/sql/updates/h3--4.1.0--4.1.1.sql 0000664 0000000 0000000 00000001357 14752347156 0017521 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.1.1'" to load this file. \quit
h3-pg-4.2.2/h3/sql/updates/h3--4.1.1--4.1.2.sql 0000664 0000000 0000000 00000001357 14752347156 0017523 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.1.2'" to load this file. \quit
h3-pg-4.2.2/h3/sql/updates/h3--4.1.2--4.1.3.sql 0000664 0000000 0000000 00000001477 14752347156 0017530 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.1.3'" to load this file. \quit
COMMENT ON OPERATOR @> (h3index, h3index) IS
'Returns true if A contains B.'; h3-pg-4.2.2/h3/sql/updates/h3--4.1.3--4.1.4.sql 0000664 0000000 0000000 00000002064 14752347156 0017523 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.1.4'" to load this file. \quit
CREATE OR REPLACE FUNCTION
h3index_recv(internal) RETURNS h3index
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION
h3index_send(h3index) RETURNS bytea
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
ALTER TYPE h3index SET (
RECEIVE = h3index_recv,
SEND = h3index_send
);
h3-pg-4.2.2/h3/sql/updates/h3--4.1.4--4.2.0.sql 0000664 0000000 0000000 00000004737 14752347156 0017532 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.2.0'" to load this file. \quit
--@ availability: 4.2.0
CREATE OR REPLACE FUNCTION
h3_polygon_to_cells_experimental(exterior polygon, holes polygon[], resolution integer DEFAULT 1, containment_mode text DEFAULT 'center') RETURNS SETOF h3index
AS 'h3' LANGUAGE C IMMUTABLE
-- intentionally NOT STRICT
CALLED ON NULL INPUT PARALLEL SAFE; COMMENT ON FUNCTION
h3_polygon_to_cells_experimental(polygon, polygon[], integer, text)
IS 'Takes an exterior polygon [and a set of hole polygon] and returns the set of hexagons that best fit the structure.';
-- SP-GiST operator class
CREATE OR REPLACE FUNCTION h3index_spgist_config(internal, internal) RETURNS void
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_spgist_choose(internal, internal) RETURNS void
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_spgist_picksplit(internal, internal) RETURNS void
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_spgist_inner_consistent(internal, internal) RETURNS void
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3index_spgist_leaf_consistent(internal, internal) RETURNS boolean
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- intentionally *not* marked as DEFAULT
CREATE OPERATOR CLASS h3index_ops_experimental FOR TYPE h3index USING spgist AS
OPERATOR 6 = ,
OPERATOR 7 @> ,
OPERATOR 8 <@ ,
FUNCTION 1 h3index_spgist_config(internal, internal),
FUNCTION 2 h3index_spgist_choose(internal, internal),
FUNCTION 3 h3index_spgist_picksplit(internal, internal),
FUNCTION 4 h3index_spgist_inner_consistent(internal, internal),
FUNCTION 5 h3index_spgist_leaf_consistent(internal, internal);
h3-pg-4.2.2/h3/sql/updates/h3--4.2.0--4.2.1.sql 0000664 0000000 0000000 00000001357 14752347156 0017523 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.2.1'" to load this file. \quit
h3-pg-4.2.2/h3/sql/updates/h3--4.2.1--4.2.2.sql 0000664 0000000 0000000 00000001357 14752347156 0017525 0 ustar 00root root 0000000 0000000 /*
* Copyright 2025 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3 UPDATE TO '4.2.2'" to load this file. \quit
h3-pg-4.2.2/h3/src/ 0000775 0000000 0000000 00000000000 14752347156 0013554 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3/src/binding/ 0000775 0000000 0000000 00000000000 14752347156 0015166 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3/src/binding/edge.c 0000664 0000000 0000000 00000011444 14752347156 0016242 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_INFO_V1
#include // SRF_IS_FIRSTCALL
#include // HeapTuple
#include // PG_RETURN_POLYGON_P
#include "error.h"
#include "type.h"
#include "srf.h"
#if POSTGRESQL_VERSION_MAJOR >= 16
#include "varatt.h" //VAR_SIZE and friends moved to here from postgres.h
#endif
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_are_neighbor_cells);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cells_to_directed_edge);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_is_valid_directed_edge);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_directed_edge_origin);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_directed_edge_destination);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_directed_edge_to_cells);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_origin_to_directed_edges);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_directed_edge_to_boundary);
/* Returns whether or not the provided H3 cell indexes are neighbors. */
Datum
h3_are_neighbor_cells(PG_FUNCTION_ARGS)
{
int neighboring;
H3Index origin = PG_GETARG_H3INDEX(0);
H3Index destination = PG_GETARG_H3INDEX(1);
h3_assert(areNeighborCells(origin, destination, &neighboring));
PG_RETURN_BOOL(neighboring);
}
/* Returns a unidirectional edge H3 index based on the provided origin and destination. */
Datum
h3_cells_to_directed_edge(PG_FUNCTION_ARGS)
{
H3Index edge;
H3Index origin = PG_GETARG_H3INDEX(0);
H3Index destination = PG_GETARG_H3INDEX(1);
h3_assert(cellsToDirectedEdge(origin, destination, &edge));
PG_RETURN_H3INDEX(edge);
}
/* Determines if the provided H3Index is a valid unidirectional edge index. */
Datum
h3_is_valid_directed_edge(PG_FUNCTION_ARGS)
{
H3Index edge = PG_GETARG_H3INDEX(0);
int valid = isValidDirectedEdge(edge);
PG_RETURN_BOOL(valid);
}
/* Returns the origin hexagon from the unidirectional edge H3Index. */
Datum
h3_get_directed_edge_origin(PG_FUNCTION_ARGS)
{
H3Index origin;
H3Index edge = PG_GETARG_H3INDEX(0);
h3_assert(getDirectedEdgeOrigin(edge, &origin));
PG_RETURN_H3INDEX(origin);
}
/* Returns the destination hexagon from the unidirectional edge H3Index. */
Datum
h3_get_directed_edge_destination(PG_FUNCTION_ARGS)
{
H3Index destination;
H3Index edge = PG_GETARG_H3INDEX(0);
h3_assert(getDirectedEdgeDestination(edge, &destination));
PG_RETURN_H3INDEX(destination);
}
/* Returns the origin, destination pair of hexagon IDs for the given edge ID. */
Datum
h3_directed_edge_to_cells(PG_FUNCTION_ARGS)
{
TupleDesc tuple_desc;
Datum values[2];
bool nulls[2] = {false};
HeapTuple tuple;
Datum result;
H3Index edge = PG_GETARG_H3INDEX(0);
H3Index *cells = palloc(2 * sizeof(H3Index));
h3_assert(directedEdgeToCells(edge, cells));
ENSURE_TYPEFUNC_COMPOSITE(get_call_result_type(fcinfo, NULL, &tuple_desc));
values[0] = H3IndexGetDatum(cells[0]);
values[1] = H3IndexGetDatum(cells[1]);
tuple_desc = BlessTupleDesc(tuple_desc);
tuple = heap_form_tuple(tuple_desc, values, nulls);
result = HeapTupleGetDatum(tuple);
PG_RETURN_DATUM(result);
}
/* Provides all of the unidirectional edges from the current H3Index. */
Datum
h3_origin_to_directed_edges(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
int max = 6;
H3Index origin = PG_GETARG_H3INDEX(0);
H3Index *edges = palloc(max * sizeof(H3Index));
h3_assert(originToDirectedEdges(origin, edges));
funcctx->user_fctx = edges;
funcctx->max_calls = max;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/* Provides the coordinates defining the unidirectional edge. */
Datum
h3_directed_edge_to_boundary(PG_FUNCTION_ARGS)
{
CellBoundary boundary;
POLYGON *polygon;
int size;
H3Index edge = PG_GETARG_H3INDEX(0);
h3_assert(directedEdgeToBoundary(edge, &boundary));
size = offsetof(POLYGON, p[0]) +sizeof(polygon->p[0]) * boundary.numVerts;
polygon = (POLYGON *) palloc(size);
SET_VARSIZE(polygon, size);
polygon->npts = boundary.numVerts;
for (int v = 0; v < boundary.numVerts; v++)
{
polygon->p[v].x = radsToDegs(boundary.verts[v].lat);
polygon->p[v].y = radsToDegs(boundary.verts[v].lng);
}
PG_RETURN_POLYGON_P(polygon);
}
h3-pg-4.2.2/h3/src/binding/hierarchy.c 0000664 0000000 0000000 00000014202 14752347156 0017307 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_INFO_V1
#include // SRF_IS_FIRSTCALL
#include // ArrayType
#include "error.h"
#include "type.h"
#include "srf.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_parent);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_children);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_center_child);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_child_pos);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_child_pos_to_cell);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_compact_cells);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_uncompact_cells);
/* Returns the parent (coarser) index containing given index */
Datum
h3_cell_to_parent(PG_FUNCTION_ARGS)
{
H3Index parent;
H3Index origin = PG_GETARG_H3INDEX(0);
int resolution = PG_GETARG_OPTIONAL_RES(1, origin, -1);
h3_assert(cellToParent(origin, resolution, &parent));
PG_RETURN_H3INDEX(parent);
}
/* Returns children indexes at given resolution (or next resolution if none given) */
Datum
h3_cell_to_children(PG_FUNCTION_ARGS)
{
/* stuff done only on the first call of the function */
if (SRF_IS_FIRSTCALL())
{
int64_t max;
int64_t size;
H3Index *children;
/* create a function context for cross-call persistence */
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
/* switch to memory context appropriate for multiple function calls */
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* BEGIN One-time setup code */
/* ensure valid resolution target */
H3Index origin = PG_GETARG_H3INDEX(0);
int resolution = PG_GETARG_OPTIONAL_RES(1, origin, 1);
h3_assert(cellToChildrenSize(origin, resolution, &max));
size = max * sizeof(H3Index);
ASSERT(
AllocSizeIsValid(size),
ERRCODE_OUT_OF_MEMORY,
"Cannot allocate necessary amount memory, try using h3_cell_to_children_slow()"
);
children = palloc(size);
h3_assert(cellToChildren(origin, resolution, children));
funcctx->user_fctx = children;
funcctx->max_calls = max;
/* END One-time setup code */
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/* Returns the center child (finer) index contained by input index at given resolution */
Datum
h3_cell_to_center_child(PG_FUNCTION_ARGS)
{
H3Index child;
H3Index origin = PG_GETARG_H3INDEX(0);
int resolution = PG_GETARG_OPTIONAL_RES(1, origin, 1);
h3_assert(cellToCenterChild(origin, resolution, &child));
PG_RETURN_H3INDEX(child);
}
Datum
h3_cell_to_child_pos(PG_FUNCTION_ARGS)
{
H3Index child = PG_GETARG_H3INDEX(0);
int parentRes = PG_GETARG_INT32(1);
int64_t childPos;
h3_assert(cellToChildPos(child, parentRes, &childPos));
PG_RETURN_INT64(childPos);
}
Datum
h3_child_pos_to_cell(PG_FUNCTION_ARGS)
{
int64_t childPos = PG_GETARG_INT64(0);
H3Index parent = PG_GETARG_H3INDEX(1);
int childRes = PG_GETARG_INT32(2);
H3Index child;
h3_assert(childPosToCell(childPos, parent, childRes, &child));
PG_RETURN_H3INDEX(child);
}
Datum
h3_compact_cells(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
Datum value;
bool isnull;
int i = 0;
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
int max = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
ArrayIterator iterator = array_create_iterator(array, 0, NULL);
H3Index *h3set = palloc(max * sizeof(H3Index));
H3Index *compactedSet = palloc0(max * sizeof(H3Index));
/* Extract data from array into h3set, and wipe compactedSet memory */
while (array_iterate(iterator, &value, &isnull))
{
h3set[i++] = DatumGetH3Index(value);
}
h3_assert(compactCells(h3set, compactedSet, max));
funcctx->user_fctx = compactedSet;
funcctx->max_calls = max;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
Datum
h3_uncompact_cells(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
int resolution;
Datum value;
bool isnull;
int i = 0;
int64_t max;
H3Index *uncompactedSet;
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
int numCompacted = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
ArrayIterator iterator = array_create_iterator(array, 0, NULL);
H3Index *compactedSet = palloc(numCompacted * sizeof(H3Index));
/*
* Extract data from array into compactedSet, and wipe compactedSet
* memory
*/
while (array_iterate(iterator, &value, &isnull))
{
compactedSet[i++] = DatumGetH3Index(value);
}
if (PG_NARGS() == 2)
{
resolution = PG_GETARG_INT32(1);
}
else
{
/* resolution parameter not set */
int highRes = 0;
/* Find highest resolution in the given set */
for (int i = 0; i < numCompacted; i++)
{
int curRes = getResolution(compactedSet[i]);
if (curRes > highRes)
highRes = curRes;
}
/*
* If the highest resolution is the maximun allowed, uncompact to
* that
*/
/* Else uncompact one step further than the highest resolution */
resolution = (highRes == 15 ? highRes : highRes + 1);
}
h3_assert(uncompactCellsSize(compactedSet, numCompacted, resolution, &max));
uncompactedSet = palloc0(max * sizeof(H3Index));
h3_assert(uncompactCells(compactedSet, numCompacted, uncompactedSet, max, resolution));
funcctx->user_fctx = uncompactedSet;
funcctx->max_calls = max;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
h3-pg-4.2.2/h3/src/binding/indexing.c 0000664 0000000 0000000 00000006557 14752347156 0017154 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#if POSTGRESQL_VERSION_MAJOR >= 16
#include "varatt.h" //VAR_SIZE and friends moved to here from postgres.h
#endif
#include
#include // PG_FUNCTION_INFO_V1
#include // PG_GETARG_POINT_P
#include // fabs
#include "constants.h"
#include "error.h"
#include "type.h"
#include "guc.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_lat_lng_to_cell);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_lat_lng);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_boundary);
/* Indexes the location at the specified resolution */
Datum
h3_lat_lng_to_cell(PG_FUNCTION_ARGS)
{
H3Index cell;
LatLng location;
Point *point = PG_GETARG_POINT_P(0);
int resolution = PG_GETARG_INT32(1);
if (h3_guc_strict)
{
ASSERT(
point->x >= -180 && point->x <= 180,
ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE,
"Longitude must be between -180 and 180 degrees inclusive, but got %f.",
point->x
);
ASSERT(
point->y >= -90 && point->y <= 90,
ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE,
"Latitude must be between -90 and 90 degrees inclusive, but got %f.",
point->y
);
}
location.lng = degsToRads(point->x);
location.lat = degsToRads(point->y);
h3_assert(latLngToCell(&location, resolution, &cell));
PG_FREE_IF_COPY(point, 0);
PG_RETURN_H3INDEX(cell);
}
/* Finds the centroid of the index */
Datum
h3_cell_to_lat_lng(PG_FUNCTION_ARGS)
{
LatLng center;
Point *point = palloc(sizeof(Point));
H3Index cell = PG_GETARG_H3INDEX(0);
h3_assert(cellToLatLng(cell, ¢er));
point->x = radsToDegs(center.lng);
point->y = radsToDegs(center.lat);
PG_RETURN_POINT_P(point);
}
/* Finds the boundary of the index */
Datum
h3_cell_to_boundary(PG_FUNCTION_ARGS)
{
H3Index cell = PG_GETARG_H3INDEX(0);
double delta,
firstLon,
lon,
lat;
int size;
POLYGON *polygon;
CellBoundary boundary;
/* DEPRECATION BEGIN: Remove next major */
bool extend;
if (PG_NARGS() == 1)
{
extend = h3_guc_extend_antimeridian;
}
else
{
extend = PG_GETARG_BOOL(1);
H3_DEPRECATION("Please use `SET h3.extend_antimeridian TO true` instead of extend flag");
}
/* DEPRECATION END */
h3_assert(cellToBoundary(cell, &boundary));
size = offsetof(POLYGON, p) +sizeof(polygon->p[0]) * boundary.numVerts;
polygon = (POLYGON *) palloc(size);
SET_VARSIZE(polygon, size);
polygon->npts = boundary.numVerts;
firstLon = boundary.verts[0].lng;
if (firstLon < 0)
{
delta = -2 * M_PI;
}
else
{
delta = +2 * M_PI;
}
for (int v = 0; v < boundary.numVerts; v++)
{
lon = boundary.verts[v].lng;
lat = boundary.verts[v].lat;
/* check if different sign */
if (extend && fabs(lon - firstLon) > M_PI)
lon = lon + delta;
polygon->p[v].x = radsToDegs(lon);
polygon->p[v].y = radsToDegs(lat);
}
PG_RETURN_POLYGON_P(polygon);
}
h3-pg-4.2.2/h3/src/binding/inspection.c 0000664 0000000 0000000 00000006045 14752347156 0017512 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include // ArrayType
#include // get_typlenbyvalalign
#include // INT4OID
#include "error.h"
#include "type.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_resolution);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_base_cell_number);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_is_valid_cell);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_is_res_class_iii);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_is_pentagon);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_icosahedron_faces);
/* Returns the resolution of the index */
Datum
h3_get_resolution(PG_FUNCTION_ARGS)
{
H3Index hex = PG_GETARG_H3INDEX(0);
int32_t resolution = getResolution(hex);
PG_RETURN_INT32(resolution);
}
/* Returns the base cell number of the index */
Datum
h3_get_base_cell_number(PG_FUNCTION_ARGS)
{
H3Index hex = PG_GETARG_H3INDEX(0);
int32_t result = getBaseCellNumber(hex);
PG_RETURN_INT32(result);
}
/* Returns true if this is a valid H3 index */
Datum
h3_is_valid_cell(PG_FUNCTION_ARGS)
{
H3Index hex = PG_GETARG_H3INDEX(0);
bool result = isValidCell(hex);
PG_RETURN_BOOL(result);
}
/* Returns true if this index has a resolution with Class III orientation */
Datum
h3_is_res_class_iii(PG_FUNCTION_ARGS)
{
H3Index hex = PG_GETARG_H3INDEX(0);
bool result = isResClassIII(hex);
PG_RETURN_BOOL(result);
}
/* Returns true if this hex represents a pentagonal cell */
Datum
h3_is_pentagon(PG_FUNCTION_ARGS)
{
H3Index hex = PG_GETARG_H3INDEX(0);
bool result = isPentagon(hex);
PG_RETURN_BOOL(result);
}
/* Find all icosahedron faces intersected by a given H3 index */
Datum
h3_get_icosahedron_faces(PG_FUNCTION_ARGS)
{
Oid elmtype = INT4OID;
int16 elmlen;
bool elmbyval;
char elmalign;
int *faces;
Datum *elements;
int maxFaces;
ArrayType *result;
int nelems = 0;
H3Index hex = PG_GETARG_H3INDEX(0);
h3_assert(maxFaceCount(hex, &maxFaces));
/* get the faces */
faces = palloc(maxFaces * sizeof(int));
elements = palloc(maxFaces * sizeof(Datum));
h3_assert(getIcosahedronFaces(hex, faces));
for (int i = 0; i < maxFaces; i++)
{
int face = faces[i];
/* add any valid face to result array */
if (face > -1)
elements[nelems++] = Int32GetDatum(face);
}
/* build the array */
get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
result = construct_array(elements, nelems, elmtype, elmlen, elmbyval, elmalign);
PG_RETURN_ARRAYTYPE_P(result);
}
h3-pg-4.2.2/h3/src/binding/miscellaneous.c 0000664 0000000 0000000 00000012761 14752347156 0020204 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include // SRF_IS_FIRSTCALL
#include // PG_GETARG_POINT_P
#include // text_to_cstring
#include "error.h"
#include "type.h"
#include "srf.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_hexagon_area_avg);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_area);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_hexagon_edge_length_avg);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_edge_length);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_num_cells);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_res_0_cells);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_pentagons);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_great_circle_distance);
/* Average hexagon area in square (kilo)meters at the given resolution */
Datum
h3_get_hexagon_area_avg(PG_FUNCTION_ARGS)
{
int resolution = PG_GETARG_INT32(0);
char *unit = text_to_cstring(PG_GETARG_TEXT_PP(1));
double area;
if (strcmp(unit, "km") == 0)
h3_assert(getHexagonAreaAvgKm2(resolution, &area));
else if (strcmp(unit, "m") == 0)
h3_assert(getHexagonAreaAvgM2(resolution, &area));
else
ASSERT(0, ERRCODE_INVALID_PARAMETER_VALUE, "Unit must be m or km.");
PG_RETURN_FLOAT8(area);
}
/* Exact area for a specific cell (hexagon or pentagon) */
Datum
h3_cell_area(PG_FUNCTION_ARGS)
{
H3Index cell = PG_GETARG_H3INDEX(0);
char *unit = text_to_cstring(PG_GETARG_TEXT_PP(1));
double area;
if (strcmp(unit, "rads^2") == 0)
h3_assert(cellAreaRads2(cell, &area));
else if (strcmp(unit, "km^2") == 0)
h3_assert(cellAreaKm2(cell, &area));
else if (strcmp(unit, "m^2") == 0)
h3_assert(cellAreaM2(cell, &area));
else
ASSERT(0, ERRCODE_INVALID_PARAMETER_VALUE, "Unit must be m^2, km^2 or rads^2.");
PG_RETURN_FLOAT8(area);
}
/* Average hexagon edge length in (kilo)meters at the given resolution */
Datum
h3_get_hexagon_edge_length_avg(PG_FUNCTION_ARGS)
{
int resolution = PG_GETARG_INT32(0);
char *unit = text_to_cstring(PG_GETARG_TEXT_PP(1));
double length;
if (strcmp(unit, "km") == 0)
h3_assert(getHexagonEdgeLengthAvgKm(resolution, &length));
else if (strcmp(unit, "m") == 0)
h3_assert(getHexagonEdgeLengthAvgM(resolution, &length));
else
ASSERT(0, ERRCODE_INVALID_PARAMETER_VALUE, "Unit must be m or km.");
PG_RETURN_FLOAT8(length);
}
/* Exact length for a specific unidirectional edge */
Datum
h3_edge_length(PG_FUNCTION_ARGS)
{
H3Index edge = PG_GETARG_H3INDEX(0);
char *unit = text_to_cstring(PG_GETARG_TEXT_PP(1));
double length;
if (strcmp(unit, "rads") == 0)
h3_assert(edgeLengthRads(edge, &length));
else if (strcmp(unit, "km") == 0)
h3_assert(edgeLengthKm(edge, &length));
else if (strcmp(unit, "m") == 0)
h3_assert(edgeLengthM(edge, &length));
else
ASSERT(0, ERRCODE_INVALID_PARAMETER_VALUE, "Unit must be m, km or rads.");
PG_RETURN_FLOAT8(length);
}
/* Number of unique H3 indexes at the given resolution */
Datum
h3_get_num_cells(PG_FUNCTION_ARGS)
{
int64_t cells;
int resolution = PG_GETARG_INT32(0);
h3_assert(getNumCells(resolution, &cells));
PG_RETURN_INT64(cells);
}
/* Provides all resolution 0 indexes */
Datum
h3_get_res_0_cells(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
int count = res0CellCount();
H3Index *indexes = palloc(count * sizeof(H3Index));
h3_assert(getRes0Cells(indexes));
funcctx->user_fctx = indexes;
funcctx->max_calls = count;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/* All the pentagon H3 indexes at the specified resolution */
Datum
h3_get_pentagons(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
int resolution = PG_GETARG_INT32(0);
int count = pentagonCount();
H3Index *indexes = palloc(count * sizeof(H3Index));
h3_assert(getPentagons(resolution, indexes));
funcctx->user_fctx = indexes;
funcctx->max_calls = count;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/* The great circle distance in radians between two spherical coordinates */
Datum
h3_great_circle_distance(PG_FUNCTION_ARGS)
{
Point *aPoint = PG_GETARG_POINT_P(0);
Point *bPoint = PG_GETARG_POINT_P(1);
char *unit = text_to_cstring(PG_GETARG_TEXT_PP(2));
LatLng a;
LatLng b;
double distance;
a.lng = degsToRads(aPoint->x);
a.lat = degsToRads(aPoint->y);
b.lng = degsToRads(bPoint->x);
b.lat = degsToRads(bPoint->y);
if (strcmp(unit, "rads") == 0)
distance = greatCircleDistanceRads(&a, &b);
else if (strcmp(unit, "km") == 0)
distance = greatCircleDistanceKm(&a, &b);
else if (strcmp(unit, "m") == 0)
distance = greatCircleDistanceM(&a, &b);
else
ASSERT(0, ERRCODE_INVALID_PARAMETER_VALUE, "Unit must be m, km or rads.");
PG_RETURN_FLOAT8(distance);
}
h3-pg-4.2.2/h3/src/binding/regions.c 0000664 0000000 0000000 00000025571 14752347156 0017012 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include // SRF_IS_FIRSTCALL
#include // HeapTuple
#include // ArrayType
#include // PG_GETARG_POLYGON_P
#include // get_typlenbyvalalign
#include // POLYGONOID
#include // text_to_cstring
#include "error.h"
#include "type.h"
#include "srf.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_polygon_to_cells);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_polygon_to_cells_experimental);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cells_to_multi_polygon);
static void
polygonToGeoLoop(POLYGON *polygon, GeoLoop * geoloop)
{
geoloop->numVerts = polygon->npts;
geoloop->verts = (LatLng *) palloc(geoloop->numVerts * sizeof(LatLng));
for (int i = 0; i < geoloop->numVerts; i++)
{
geoloop->verts[i].lng = degsToRads(polygon->p[i].x);
geoloop->verts[i].lat = degsToRads(polygon->p[i].y);
}
}
static int
linkedGeoLoopToNativePolygonSize(LinkedGeoLoop * linkedLoop)
{
int count = 0;
LinkedLatLng *linkedCoord = linkedLoop->first;
while (linkedCoord != NULL)
{
count++;
linkedCoord = linkedCoord->next;
}
return count;
}
static void
linkedGeoLoopToNativePolygon(LinkedGeoLoop * linkedLoop, POLYGON *polygon)
{
int count;
LinkedLatLng *linkedCoord = linkedLoop->first;
count = 0;
while (linkedCoord != NULL)
{
(polygon->p[count]).x = radsToDegs(linkedCoord->vertex.lng);
(polygon->p[count]).y = radsToDegs(linkedCoord->vertex.lat);
linkedCoord = linkedCoord->next;
count++;
}
}
/*
* H3Error polygonToCells(const GeoPolygon *geoPolygon, int res, uint32_t flags, H3Index *out);
*/
Datum
h3_polygon_to_cells(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
int64_t maxSize;
H3Index *indices;
ArrayType *holes;
int nelems = 0;
int resolution;
GeoPolygon polygon;
Datum value;
bool isnull;
POLYGON *exterior;
if (PG_ARGISNULL(0))
ASSERT(0, ERRCODE_INVALID_PARAMETER_VALUE, "No polygon given to polyfill");
/* get function arguments */
exterior = PG_GETARG_POLYGON_P(0);
if (!PG_ARGISNULL(1))
{
holes = PG_GETARG_ARRAYTYPE_P(1);
nelems = ArrayGetNItems(ARR_NDIM(holes), ARR_DIMS(holes));
}
resolution = PG_GETARG_INT32(2);
/* build polygon */
polygonToGeoLoop(exterior, &(polygon.geoloop));
if (nelems)
{
int i = 0;
ArrayIterator iterator = array_create_iterator(holes, 0, NULL);
polygon.numHoles = nelems;
polygon.holes = (GeoLoop *) palloc(polygon.numHoles * sizeof(GeoLoop));
while (array_iterate(iterator, &value, &isnull))
{
if (isnull)
{
polygon.numHoles--;
}
else
{
POLYGON *hole = DatumGetPolygonP(value);
polygonToGeoLoop(hole, &(polygon.holes[i]));
i++;
}
}
}
else
{
polygon.numHoles = 0;
}
/* produce hexagons into allocated memory */
h3_assert(maxPolygonToCellsSize(&polygon, resolution, 0, &maxSize));
indices = palloc_extended(maxSize * sizeof(H3Index),
MCXT_ALLOC_HUGE | MCXT_ALLOC_ZERO);
h3_assert(polygonToCells(&polygon, resolution, 0, indices));
funcctx->user_fctx = indices;
funcctx->max_calls = maxSize;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/*
* H3Error polygonToCells(const GeoPolygon *geoPolygon, int res, uint32_t flags, H3Index *out);
*/
Datum
h3_polygon_to_cells_experimental(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
char *containment_mode;
int64_t maxSize;
H3Index *indices;
ArrayType *holes;
int nelems = 0;
uint32_t flags = 0;
int resolution;
GeoPolygon polygon;
Datum value;
bool isnull;
POLYGON *exterior;
if (PG_ARGISNULL(0))
ASSERT(0, ERRCODE_INVALID_PARAMETER_VALUE, "No polygon given to polyfill");
/* get function arguments */
exterior = PG_GETARG_POLYGON_P(0);
if (!PG_ARGISNULL(1))
{
holes = PG_GETARG_ARRAYTYPE_P(1);
nelems = ArrayGetNItems(ARR_NDIM(holes), ARR_DIMS(holes));
}
resolution = PG_GETARG_INT32(2);
if (!PG_ARGISNULL(3))
{
containment_mode = text_to_cstring(PG_GETARG_TEXT_PP(3));
if (strcmp(containment_mode, "center") == 0)
flags = 0;
else if (strcmp(containment_mode, "full") == 0)
flags = 1;
else if (strcmp(containment_mode, "overlapping") == 0)
flags = 2;
else if (strcmp(containment_mode, "overlapping_bbox") == 0)
flags = 3;
else
ASSERT(0, ERRCODE_INVALID_PARAMETER_VALUE, "Containment Mode must be center, full, overlapping, or overlapping_bbox.");
}
/* build polygon */
polygonToGeoLoop(exterior, &(polygon.geoloop));
if (nelems)
{
int i = 0;
ArrayIterator iterator = array_create_iterator(holes, 0, NULL);
polygon.numHoles = nelems;
polygon.holes = (GeoLoop *) palloc(polygon.numHoles * sizeof(GeoLoop));
while (array_iterate(iterator, &value, &isnull))
{
if (isnull)
{
polygon.numHoles--;
}
else
{
POLYGON *hole = DatumGetPolygonP(value);
polygonToGeoLoop(hole, &(polygon.holes[i]));
i++;
}
}
}
else
{
polygon.numHoles = 0;
}
/* produce hexagons into allocated memory */
h3_assert(maxPolygonToCellsSizeExperimental(&polygon, resolution, flags, &maxSize));
indices = palloc_extended(maxSize * sizeof(H3Index),
MCXT_ALLOC_HUGE | MCXT_ALLOC_ZERO);
h3_assert(polygonToCellsExperimental(&polygon, resolution, flags, maxSize, indices));
funcctx->user_fctx = indices;
funcctx->max_calls = maxSize;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/*
* https://stackoverflow.com/questions/51127189/how-to-return-array-into-array-with-custom-type-in-postgres-c-function
*/
Datum
h3_cells_to_multi_polygon(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
TupleDesc tuple_desc;
LinkedGeoPolygon *linkedPolygon;
LinkedGeoLoop *linkedLoop;
if (SRF_IS_FIRSTCALL())
{
Datum value;
bool isnull;
int i = 0;
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
int numHexes = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
ArrayIterator iterator = array_create_iterator(array, 0, NULL);
H3Index *h3set = palloc(numHexes * sizeof(H3Index));
/* Extract data from array into h3set, and wipe compactedSet memory */
while (array_iterate(iterator, &value, &isnull))
{
h3set[i++] = DatumGetH3Index(value);
}
/* produce hexagons into allocated memory */
linkedPolygon = palloc(sizeof(LinkedGeoPolygon));
h3_assert(cellsToLinkedMultiPolygon(h3set, numHexes, linkedPolygon));
ENSURE_TYPEFUNC_COMPOSITE(get_call_result_type(fcinfo, NULL, &tuple_desc));
funcctx->user_fctx = linkedPolygon;
funcctx->tuple_desc = BlessTupleDesc(tuple_desc);
MemoryContextSwitchTo(oldcontext);
}
funcctx = SRF_PERCALL_SETUP();
linkedPolygon = (LinkedGeoPolygon *) funcctx->user_fctx;
if (linkedPolygon)
{
HeapTuple tuple;
Datum result;
int count;
int size;
POLYGON *polygon;
Datum *elems;
Datum values[2];
bool nulls[2];
int16 typlen;
bool typbyval;
char typalign;
ArrayType *retarr;
tuple_desc = funcctx->tuple_desc;
linkedLoop = linkedPolygon->first;
count = linkedGeoLoopToNativePolygonSize(linkedLoop);
size = offsetof(POLYGON, p) +sizeof(polygon->p[0]) * count;
polygon = palloc0(size);
SET_VARSIZE(polygon, size);
polygon->npts = count;
linkedGeoLoopToNativePolygon(linkedLoop, polygon);
values[0] = PolygonPGetDatum(polygon);
nulls[0] = false;
/* construct array */
count = 0;
linkedLoop = linkedPolygon->first->next;
while (linkedLoop != NULL)
{
count++;
linkedLoop = linkedLoop->next;
}
elems = (Datum *) palloc(count * sizeof(Datum));
if (count)
{
linkedLoop = linkedPolygon->first->next;
for (int i = 0; i < count; i++)
{
int subcount = linkedGeoLoopToNativePolygonSize(linkedLoop);
POLYGON *polygon;
int size = offsetof(POLYGON, p) +sizeof(polygon->p[0]) * subcount;
polygon = palloc0(size);
SET_VARSIZE(polygon, size);
polygon->npts = subcount;
linkedGeoLoopToNativePolygon(linkedLoop, polygon);
elems[i] = PolygonPGetDatum(polygon);
linkedLoop = linkedLoop->next;
}
}
get_typlenbyvalalign(POLYGONOID, &typlen, &typbyval, &typalign);
retarr =
construct_array(elems, count, POLYGONOID, typlen, typbyval, typalign);
values[1] = PointerGetDatum(retarr);
nulls[1] = false;
tuple = heap_form_tuple(tuple_desc, values, nulls);
result = HeapTupleGetDatum(tuple);
funcctx->user_fctx = linkedPolygon->next;
SRF_RETURN_NEXT(funcctx, result);
}
else
{
destroyLinkedMultiPolygon(linkedPolygon);
SRF_RETURN_DONE(funcctx);
}
}
/* ---------------------------------------------------------------------------
* The GeoPolygon, LinkedLatLng, LinkedLatLng,
* LinkedGeoLoop, and LinkedGeoPolygon
*
* copied from H3 core for reference
*/
/** @struct GeoPolygon
* @brief Simplified core of GeoJSON Polygon coordinates definition
*/
/*
typedef struct {
double lat; ///< latitude in radians
double lon; ///< longitude in radians
} LatLng;
typedef struct {
int numVerts;
LatLng *verts;
} GeoLoop;
typedef struct {
GeoLoop geoloop; ///< exterior boundary of the polygon
int numHoles; ///< number of elements in the array pointed to by holes
GeoLoop *holes; ///< interior boundaries (holes) in the polygon
} GeoPolygon;
*/
/** @struct LinkedLatLng
* @brief A coordinate node in a linked geo structure, part of a linked list
*
typedef struct LinkedLatLng LinkedLatLng;
struct LinkedLatLng
{
LatLng vertex;
LinkedLatLng *next;
};
** @struct LinkedGeoLoop
* @brief A loop node in a linked geo structure, part of a linked list
*
typedef struct LinkedGeoLoop LinkedGeoLoop;
struct LinkedGeoLoop
{
LinkedLatLng *first;
LinkedLatLng *last;
LinkedGeoLoop *next;
};
** @struct LinkedGeoPolygon
* @brief A polygon node in a linked geo structure, part of a linked list.
*
typedef struct LinkedGeoPolygon LinkedGeoPolygon;
struct LinkedGeoPolygon
{
LinkedGeoLoop *first;
LinkedGeoLoop *last;
LinkedGeoPolygon *next;
};
*/
h3-pg-4.2.2/h3/src/binding/traversal.c 0000664 0000000 0000000 00000015742 14752347156 0017346 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include // SRF_IS_FIRSTCALL
#include // PG_GETARG_POINT_P
#include "error.h"
#include "type.h"
#include "srf.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_grid_disk);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_grid_disk_distances);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_grid_ring_unsafe);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_grid_distance);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_grid_path_cells);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_local_ij);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_local_ij_to_cell);
/*
* k-rings produces indices within k distance of the origin index.
*
* k-ring 0 is defined as the origin index, k-ring 1 is defined as k-ring 0 and
* all neighboring indices, and so on.
*
* Output is placed in the provided array in no particular order.
* There may be fewer elements in output, as can happen when crossing a
* pentagon.
*/
Datum
h3_grid_disk(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
int64_t max;
H3Index *indices;
/* get function arguments */
H3Index origin = PG_GETARG_H3INDEX(0);
int k = PG_GETARG_INT32(1);
h3_assert(maxGridDiskSize(k, &max));
indices = palloc(max * sizeof(H3Index));
h3_assert(gridDisk(origin, k, indices));
funcctx->user_fctx = indices;
funcctx->max_calls = max;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/*
* k-rings produces indices within k distance of the origin index.
*
* k-ring 0 is defined as the origin index, k-ring 1 is defined as k-ring 0 and
* all neighboring indices, and so on.
*
* Output is placed in the provided array in no particular order.
* There may be fewer elements in output, as can happen when crossing a
* pentagon.
*/
Datum
h3_grid_disk_distances(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
TupleDesc tuple_desc;
/* get function arguments */
H3Index origin = PG_GETARG_H3INDEX(0);
int k = PG_GETARG_INT32(1);
/*
* Allocate memory for the indices, the distances and the tuple used
* for
*/
/* returning */
int64_t maxSize;
hexDistanceTuple *user_fctx;
h3_assert(maxGridDiskSize(k, &maxSize));
user_fctx = palloc(sizeof(hexDistanceTuple));
user_fctx->indices = palloc(maxSize * sizeof(H3Index));
user_fctx->distances = palloc(maxSize * sizeof(int));
h3_assert(gridDiskDistances(origin, k, user_fctx->indices, user_fctx->distances));
ENSURE_TYPEFUNC_COMPOSITE(get_call_result_type(fcinfo, NULL, &tuple_desc));
funcctx->tuple_desc = BlessTupleDesc(tuple_desc);
funcctx->max_calls = maxSize;
funcctx->user_fctx = user_fctx;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEX_DISTANCES_FROM_USER_FCTX();
}
/*
* Produces the hollow hexagonal ring centered at origin with sides of length k.
*
* Throws if pentagonal distortion was encountered.
*/
Datum
h3_grid_ring_unsafe(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* get function arguments */
H3Index *indices;
H3Index origin = PG_GETARG_H3INDEX(0);
int k = PG_GETARG_INT32(1);
/*
* Find the size of the ring. If k is 0, then it is the same as
* k_ring.
*
* If k is larger than 0, the ring is the size of the circle with k,
* minus the circle with k-1
*/
int64_t maxSize;
int64_t innerSize;
h3_assert(maxGridDiskSize(k, &maxSize));
if (k > 0)
{
h3_assert(maxGridDiskSize(k - 1, &innerSize));
maxSize -= innerSize;
}
indices = palloc(maxSize * sizeof(H3Index));
h3_assert(gridRingUnsafe(origin, k, indices));
funcctx->user_fctx = indices;
funcctx->max_calls = maxSize;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/*
* Returns the distance in grid cells between the two indexes.
*
* Returns a negative number if finding the distance failed.
* Finding the distance can fail because the two indexes are not comparable
* (different resolutions), too far apart, or are separated by pentagonal
* distortion. This is the same set of limitations as the local IJ coordinate
* space functions.
*/
Datum
h3_grid_distance(PG_FUNCTION_ARGS)
{
H3Index originIndex = PG_GETARG_H3INDEX(0);
H3Index h3Index = PG_GETARG_H3INDEX(1);
int64_t distance;
h3_assert(gridDistance(originIndex, h3Index, &distance));
PG_RETURN_INT64(distance);
}
/*
* Given two H3 indexes, return the line of indexes between them (inclusive).
*
* This function may fail to find the line between two indexes, for
* example if they are very far apart. It may also fail when finding
* distances for indexes on opposite sides of a pentagon.
*/
Datum
h3_grid_path_cells(PG_FUNCTION_ARGS)
{
if (SRF_IS_FIRSTCALL())
{
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* get function arguments */
int64_t size;
H3Index *indices;
H3Index start = PG_GETARG_H3INDEX(0);
H3Index end = PG_GETARG_H3INDEX(1);
h3_assert(gridPathCellsSize(start, end, &size));
indices = palloc(size * sizeof(H3Index));
h3_assert(gridPathCells(start, end, indices));
funcctx->user_fctx = indices;
funcctx->max_calls = size;
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/*
* Produces local IJ coordinates for an H3 index anchored by an origin.
*/
Datum
h3_cell_to_local_ij(PG_FUNCTION_ARGS)
{
H3Index origin = PG_GETARG_H3INDEX(0);
H3Index index = PG_GETARG_H3INDEX(1);
Point *point = (Point *) palloc(sizeof(Point));
CoordIJ coord;
h3_assert(cellToLocalIj(origin, index, 0, &coord));
point->x = coord.i;
point->y = coord.j;
PG_RETURN_POINT_P(point);
}
/*
* Produces an H3 index from local IJ coordinates anchored by an origin.
*/
Datum
h3_local_ij_to_cell(PG_FUNCTION_ARGS)
{
H3Index origin = PG_GETARG_H3INDEX(0);
Point *point = PG_GETARG_POINT_P(1);
H3Index *index = (H3Index *) palloc(sizeof(H3Index));
CoordIJ coord;
coord.i = point->x;
coord.j = point->y;
h3_assert(localIjToCell(origin, &coord, 0, index));
PG_FREE_IF_COPY(point, 1);
PG_RETURN_H3INDEX(*index);
}
h3-pg-4.2.2/h3/src/binding/vertex.c 0000664 0000000 0000000 00000005342 14752347156 0016653 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include // SRF_IS_FIRSTCALL
#include // PG_RETURN_POINT_P
#include "error.h"
#include "type.h"
#include "srf.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_vertex);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_vertexes);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_vertex_to_lat_lng);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_is_valid_vertex);
/* Returns a single vertex for a given cell, as an H3 index */
Datum
h3_cell_to_vertex(PG_FUNCTION_ARGS)
{
H3Index vertex;
H3Index cell = PG_GETARG_H3INDEX(0);
int vertexNum = PG_GETARG_INT32(1);
h3_assert(cellToVertex(cell, vertexNum, &vertex));
PG_RETURN_H3INDEX(vertex);
}
/* Returns all vertexes for a given cell, as H3 indexes */
Datum
h3_cell_to_vertexes(PG_FUNCTION_ARGS)
{
/* stuff done only on the first call of the function */
if (SRF_IS_FIRSTCALL())
{
int maxSize;
int size;
H3Index *vertexes;
/* create a function context for cross-call persistence */
FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
/* switch to memory context appropriate for multiple function calls */
MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* BEGIN One-time setup code */
H3Index cell = PG_GETARG_H3INDEX(0);
maxSize = 6;
size = maxSize * sizeof(H3Index);
vertexes = palloc(size);
h3_assert(cellToVertexes(cell, vertexes));
funcctx->user_fctx = vertexes;
funcctx->max_calls = maxSize;
/* END One-time setup code */
MemoryContextSwitchTo(oldcontext);
}
SRF_RETURN_H3_INDEXES_FROM_USER_FCTX();
}
/* Get the geocoordinates of an H3 vertex */
Datum
h3_vertex_to_lat_lng(PG_FUNCTION_ARGS)
{
H3Index vertex = PG_GETARG_H3INDEX(0);
Point *point = palloc(sizeof(Point));
LatLng latlng;
h3_assert(vertexToLatLng(vertex, &latlng));
point->x = radsToDegs(latlng.lng);
point->y = radsToDegs(latlng.lat);
PG_RETURN_POINT_P(point);
}
/* Whether the input is a valid H3 vertex */
Datum
h3_is_valid_vertex(PG_FUNCTION_ARGS)
{
H3Index vertex = PG_GETARG_H3INDEX(0);
bool is_valid = isValidVertex(vertex);
PG_RETURN_BOOL(is_valid);
}
h3-pg-4.2.2/h3/src/config.h.in 0000664 0000000 0000000 00000001316 14752347156 0015600 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef H3_CONFIG_H
#define H3_CONFIG_H
#define POSTGRESQL_H3_VERSION "@INSTALL_VERSION@"
#endif /* H3_CONFIG_H */
h3-pg-4.2.2/h3/src/deprecated.c 0000664 0000000 0000000 00000006404 14752347156 0016024 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "deprecate.h"
H3_DEPRECATE("1.0.0", h3_basecells);
H3_DEPRECATE("1.0.0", h3_h3_get_base_cell);
H3_DEPRECATE("1.0.0", h3_h3_get_resolution);
H3_DEPRECATE("1.0.0", h3_h3_indexes_are_neighbors);
H3_DEPRECATE("1.0.0", h3_h3_is_pentagon);
H3_DEPRECATE("1.0.0", h3_h3_is_res_class_iii);
H3_DEPRECATE("1.0.0", h3_h3_is_valid);
H3_DEPRECATE("1.0.0", h3_h3_set_to_linked_geo);
H3_DEPRECATE("1.0.0", h3_h3_to_children);
H3_DEPRECATE("1.0.0", h3_h3_to_geo_boundary);
H3_DEPRECATE("1.0.0", h3_h3_to_geo);
H3_DEPRECATE("1.0.0", h3_h3_to_parent);
H3_DEPRECATE("1.0.0", h3_h3_unidirectional_edge_is_valid);
H3_DEPRECATE("1.0.0", h3_haversine_distance);
H3_DEPRECATE("3.4.0", h3_degs_to_rads);
H3_DEPRECATE("3.4.0", h3_rads_to_degs);
H3_DEPRECATE("3.5.0", h3_edge_length_km);
H3_DEPRECATE("3.5.0", h3_edge_length_m);
H3_DEPRECATE("3.5.0", h3_get_unidirectional_edge_boundary);
H3_DEPRECATE("3.5.0", h3_hex_area_km2);
H3_DEPRECATE("3.5.0", h3_hex_area_m2);
H3_DEPRECATE("3.5.0", h3_hex_range_distances);
H3_DEPRECATE("3.5.0", h3_hex_range);
H3_DEPRECATE("3.5.0", h3_hex_ranges);
H3_DEPRECATE("3.6.0", h3_h3_to_string);
H3_DEPRECATE("3.6.0", h3_string_to_h3);
H3_DEPRECATE("4.0.0", h3_compact);
H3_DEPRECATE("4.0.0", h3_distance);
H3_DEPRECATE("4.0.0", h3_exact_edge_length);
H3_DEPRECATE("4.0.0", h3_experimental_h3_to_local_ij);
H3_DEPRECATE("4.0.0", h3_experimental_local_ij_to_h3);
H3_DEPRECATE("4.0.0", h3_geo_to_h3);
H3_DEPRECATE("4.0.0", h3_get_base_cell);
H3_DEPRECATE("4.0.0", h3_get_destination_h3_index_from_unidirectional_edge);
H3_DEPRECATE("4.0.0", h3_get_faces);
H3_DEPRECATE("4.0.0", h3_get_h3_indexes_from_unidirectional_edge);
H3_DEPRECATE("4.0.0", h3_get_h3_unidirectional_edge_boundary);
H3_DEPRECATE("4.0.0", h3_get_h3_unidirectional_edge);
H3_DEPRECATE("4.0.0", h3_get_h3_unidirectional_edges_from_hexagon);
H3_DEPRECATE("4.0.0", h3_get_origin_h3_index_from_unidirectional_edge);
H3_DEPRECATE("4.0.0", h3_get_pentagon_indexes);
H3_DEPRECATE("4.0.0", h3_get_res_0_indexes);
H3_DEPRECATE("4.0.0", h3_hex_area);
H3_DEPRECATE("4.0.0", h3_hex_ring);
H3_DEPRECATE("4.0.0", h3_indexes_are_neighbors);
H3_DEPRECATE("4.0.0", h3_is_valid);
H3_DEPRECATE("4.0.0", h3_k_ring_distances);
H3_DEPRECATE("4.0.0", h3_k_ring);
H3_DEPRECATE("4.0.0", h3_line);
H3_DEPRECATE("4.0.0", h3_num_hexagons);
H3_DEPRECATE("4.0.0", h3_point_dist);
H3_DEPRECATE("4.0.0", h3_polyfill);
H3_DEPRECATE("4.0.0", h3_set_to_multi_polygon);
H3_DEPRECATE("4.0.0", h3_to_center_child);
H3_DEPRECATE("4.0.0", h3_to_geo_boundary);
H3_DEPRECATE("4.0.0", h3_to_geo);
H3_DEPRECATE("4.0.0", h3_uncompact);
H3_DEPRECATE("4.0.0", h3_unidirectional_edge_is_valid);
H3_DEPRECATE("4.1.0", h3_cell_to_boundary_wkb);
H3_DEPRECATE("4.1.0", h3_cells_to_multi_polygon_wkb);
h3-pg-4.2.2/h3/src/extension.c 0000664 0000000 0000000 00000002476 14752347156 0015745 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_INFO_V1
#include // cstring_to_text
#include "type.h"
#include "config.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_get_extension_version);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_pg_migrate_pass_by_reference);
/* Return version number for this extension (not main h3 lib) */
Datum
h3_get_extension_version(PG_FUNCTION_ARGS)
{
PG_RETURN_TEXT_P(cstring_to_text(POSTGRESQL_H3_VERSION));
}
/*
* Migration from pass-by-reference to pass-by-value
* https://github.com/zachasme/h3-pg/issues/31
*/
Datum
h3_pg_migrate_pass_by_reference(PG_FUNCTION_ARGS)
{
H3Index cell = (*((H3Index *) DatumGetPointer(PG_GETARG_DATUM(0))));
PG_RETURN_H3INDEX(cell);
}
h3-pg-4.2.2/h3/src/guc.c 0000664 0000000 0000000 00000002346 14752347156 0014503 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include // DefineCustom*Variable
bool h3_guc_strict = false;
bool h3_guc_extend_antimeridian = false;
void
_guc_init(void)
{
DefineCustomBoolVariable("h3.strict",
"Enable strict indexing (fail on invalid lng/lat).",
NULL,
&h3_guc_strict,
false,
PGC_USERSET,
0,
NULL,
NULL,
NULL);
DefineCustomBoolVariable("h3.extend_antimeridian",
"Extend boundaries by 180th meridian, when possible.",
NULL,
&h3_guc_extend_antimeridian,
false,
PGC_USERSET,
0,
NULL,
NULL,
NULL);
}
h3-pg-4.2.2/h3/src/guc.h 0000664 0000000 0000000 00000001355 14752347156 0014507 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef H3_GUC_H
#define H3_GUC_H
extern bool h3_guc_strict;
extern bool h3_guc_extend_antimeridian;
void _guc_init(void);
#endif /* H3_GUC_H */
h3-pg-4.2.2/h3/src/init.c 0000664 0000000 0000000 00000001707 14752347156 0014670 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* required for MSVC */
#define _USE_MATH_DEFINES
#include
#include // PG_MODULE_MAGIC
#include "guc.h"
/* see https://www.postgresql.org/docs/current/xfunc-c.html#XFUNC-C-DYNLOAD */
PG_MODULE_MAGIC;
void
_PG_init(void)
{
/* In case we allow using shared library h3, */
/* we could make version number assertion here */
_guc_init();
}
h3-pg-4.2.2/h3/src/opclass_btree.c 0000664 0000000 0000000 00000004133 14752347156 0016546 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include // SortSupport
#include "type.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_cmp);
Datum
h3index_cmp(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
uint32_t ret = 0;
if (a < b)
ret = 1;
else if (a > b)
ret = -1;
PG_RETURN_INT32(ret);
}
static int
h3index_cmp_abbrev(Datum x, Datum y, SortSupport ssup)
{
if (x == y)
return 0;
else if (x < y)
return 1;
else
return -1;
}
static int
h3index_cmp_full(Datum x, Datum y, SortSupport ssup)
{
H3Index a = DatumGetH3Index(x);
H3Index b = DatumGetH3Index(y);
if (a == b)
return 0;
else if (a < b)
return 1;
return -1;
}
static bool
h3index_abbrev_abort(int memtupcount, SortSupport ssup)
{
return 0;
}
static Datum
h3index_abbrev_convert(Datum original, SortSupport ssup)
{
H3Index a = DatumGetH3Index(original);
return a;
}
/*
* Sort support strategy routine
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_sortsupport);
Datum
h3index_sortsupport(PG_FUNCTION_ARGS)
{
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
ssup->comparator = h3index_cmp_full;
ssup->ssup_extra = NULL;
/* Enable sortsupport only on 64 bit Datum */
if (ssup->abbreviate && sizeof(Datum) == 8)
{
ssup->comparator = h3index_cmp_abbrev;
ssup->abbrev_converter = h3index_abbrev_convert;
ssup->abbrev_abort = h3index_abbrev_abort;
ssup->abbrev_full_comparator = h3index_cmp_full;
}
PG_RETURN_VOID();
}
h3-pg-4.2.2/h3/src/opclass_hash.c 0000664 0000000 0000000 00000002342 14752347156 0016370 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include // hash_any
#include "type.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_hash);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_hash_extended);
Datum
h3index_hash(PG_FUNCTION_ARGS)
{
H3Index index = PG_GETARG_H3INDEX(0);
Datum hash = hash_any((unsigned char *) &index, sizeof(index));
PG_RETURN_DATUM(hash);
}
Datum
h3index_hash_extended(PG_FUNCTION_ARGS)
{
H3Index index = PG_GETARG_H3INDEX(0);
int64_t seed = PG_GETARG_INT64(1);
Datum hash = hash_any_extended((unsigned char *) &index, sizeof(index), seed);
PG_RETURN_DATUM(hash);
}
h3-pg-4.2.2/h3/src/opclass_spgist.c 0000664 0000000 0000000 00000030666 14752347156 0016770 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include // Datum, etc.
#include // PG_FUNCTION_ARGS, etc.
#include // SP-GiST
#include "catalog/pg_type.h"
#include // Main H3 include
#include "type.h"
#include "error.h"
#include "inttypes.h"
#include "upstream_macros.h" // Technically not public API, but we need the bit macros
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_spgist_config);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_spgist_choose);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_spgist_picksplit);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_spgist_inner_consistent);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_spgist_leaf_consistent);
#define H3_ROOT_INDEX -1
#define NUM_BASE_CELLS 122
#define MAX_H3_RES 15
#define H3_NUM_CHILDREN 7
static int
spgist_cmp(H3Index * a, H3Index * b)
{
int aRes = getResolution(*a);
int bRes = getResolution(*b);
H3Index aParent;
H3Index bParent;
H3Error error;
cellToParent(*a, bRes, &aParent);
cellToParent(*b, aRes, &bParent);
/* a contains b */
if (*a == H3_ROOT_INDEX || *a == bParent)
{
return 1;
}
/* a contained by b */
if (*b == H3_ROOT_INDEX || *b == aParent)
{
return -1;
}
/* no overlap */
return 0;
}
/*
* Returns static information about the index implementation, including the data
* type OIDs of the prefix and node label data types.
*
* The first argument is a pointer to a spgConfigIn C struct, containing input
* data for the function.
*
* The second argument is a pointer to a spgConfigOut C struct, which the
* function must fill with result data.
*/
Datum
h3index_spgist_config(PG_FUNCTION_ARGS)
{
// struct {
// Oid attType data type to be indexed
// }
spgConfigIn *in = (spgConfigIn *) PG_GETARG_POINTER(0);
// struct {
// Oid prefixType data type of inner-tuple prefixes
// Oid labelType data type of inner-tuple node labels
// Oid leafType data type of leaf-tuple values
// bool canReturnData opclass can reconstruct original data
// bool longValuesOK opclass can cope with values > 1 page
// }
spgConfigOut *out = (spgConfigOut *) PG_GETARG_POINTER(1);
/* prefix is parent H3 index */
out->prefixType = in->attType;
/* no need for labels */
out->labelType = VOIDOID;
/*
* leafType should match the index storage type defined by the operator
* class's opckeytype catalog entry. (Note that opckeytype can be zero,
* implying the storage type is the same as the operator class's input type,
* which is the most common situation.) For reasons of backward compatibility,
* the config method can set leafType to some other value, and that value will
* be used; but this is deprecated since the index contents are then
* incorrectly identified in the catalogs. Also, it's permissible to leave
* leafType uninitialized (zero); that is interpreted as meaning the index
* storage type derived from opckeytype.
*/
//out->leafType = 0;
/*
* canReturnData should be set true if the operator class is capable of
* reconstructing the originally-supplied index value
*/
out->canReturnData = true;
out->longValuesOK = false;
PG_RETURN_VOID();
}
/*
* h3index_spgist_choose
* Chooses a method for inserting a new value into an inner tuple
*
* The choose function can determine either that the new value matches one of
* the existing child nodes, or that a new child node must be added, or that the
* new value is inconsistent with the tuple prefix and so the inner tuple must
* be split to create a less restrictive prefix.
*
* NOTE: When working with an inner tuple having unlabeled nodes, it is an error
* for choose to return spgAddNode, since the set of nodes is supposed to be
* fixed in such cases. Also, there is no provision for generating an unlabeled
* node in spgSplitTuple actions, since it is expected that an spgAddNode action
* will be needed as well.
*/
Datum
h3index_spgist_choose(PG_FUNCTION_ARGS)
{
/*--------------------------------------------------------------------------
* Datum datum original datum to be indexed
* Datum leafDatum current datum to be stored at leaf
* int level current level (counting from zero)
*
* (data from current inner tuple)
* bool allTheSame tuple is marked all-the-same?
* bool hasPrefix tuple has a prefix?
* Datum prefixDatum if so, the prefix value
* int nNodes number of nodes in the inner tuple
* Datum* nodeLabels node label values (NULL if none)
*--------------------------------------------------------------------------
*/
spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0);
/*--------------------------------------------------------------------------
* spgChooseResultType resultType action code, see below
*
* -- results for spgMatchNode --
* int nodeN descend to this node (index from 0)
* int levelAdd increment level by this much
* Datum restDatum new leaf datum
*
* -- results for spgAddNode --
* Datum nodeLabel new node's label
* int nodeN where to insert it (index from 0)
*
* -- results for spgSplitTuple --
* (info to form new upper-level inner tuple with one child tuple)
* bool prefixHasPrefix tuple should have a prefix?
* Datum prefixPrefixDatum if so, its value
* int prefixNNodes number of nodes
* Datum* prefixNodeLabels their labels (or NULL for no labels)
* int childNodeN which node gets child tuple
* (info to form new lower-level inner tuple with all old nodes)
* bool postfixHasPrefix tuple should have a prefix?
* Datum postfixPrefixDatum if so, its value
*--------------------------------------------------------------------------
*/
spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1);
int resolution = in->level;
H3Index insert = DatumGetH3Index(in->datum);
int node;
out->resultType = spgMatchNode;
out->result.matchNode.levelAdd = 1;
out->result.matchNode.restDatum = H3IndexGetDatum(insert);
if (!in->allTheSame)
{
if (resolution == 0)
{
node = getBaseCellNumber(insert);
}
else
{
node = H3_GET_INDEX_DIGIT(insert, resolution);
}
out->result.matchNode.nodeN = node;
}
PG_RETURN_VOID();
}
/**
* Decides how to create a new inner tuple over a set of leaf tuples.
*
* An inner tuple contains a set of one or more nodes,
* which represent groups of similar leaf values.
*
* struct spgPickSplitIn
* int nTuples number of leaf tuples
* Datum *datums their datums (array of length nTuples)
* int level current level (counting from zero)
*
* struct spgPickSplitOut
* bool hasPrefix new inner tuple should have a prefix?
* Datum prefixDatum if so, its value
* int nNodes number of nodes for new inner tuple
* Datum *nodeLabels their labels (or NULL for no labels)
* int *mapTuplesToNodes node index for each leaf tuple
* Datum *leafTupleDatums datum to store in each new leaf tuple
*/
Datum
h3index_spgist_picksplit(PG_FUNCTION_ARGS)
{
spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0);
spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1);
int resolution = in->level;
/* we don't need node labels */
out->nodeLabels = NULL;
out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples);
out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples);
if (resolution == 0)
{
/* at resolution 0 there is one node per base cell */
out->nNodes = NUM_BASE_CELLS;
out->hasPrefix = false;
}
else
{
/* at finer resolutions there is exactly 7 nodes, one per child */
H3Index first = DatumGetH3Index(in->datums[0]);
H3Index parent;
H3Error error;
h3_assert(cellToParent(first, resolution, &parent));
/*
* TODO: consider decreasing nNodes for pentagons which only have 6
* children?
*/
out->nNodes = H3_NUM_CHILDREN;
out->hasPrefix = true;
out->prefixDatum = H3IndexGetDatum(parent);
}
/* map each leaf tuple to node in the new inner tuple */
for (int i = 0; i < in->nTuples; i++)
{
H3Index insert = DatumGetH3Index(in->datums[i]);
int node;
if (resolution == 0)
{
/* first resolution is base cells */
node = getBaseCellNumber(insert);
}
else if (getResolution(insert) >= resolution)
{
/* finer resolutions use index digit 0-6 */
node = H3_GET_INDEX_DIGIT(insert, resolution);
}
else
{
/* coarse indexes are put into center node */
node = 0;
}
out->leafTupleDatums[i] = H3IndexGetDatum(insert);
out->mapTuplesToNodes[i] = node;
}
PG_RETURN_VOID();
}
/**
* Returns set of nodes (branches) to follow during tree search.
*
* Each query is a single H3 index to be checked against the parent prefix
*
* We either return all or none (except for res 0)
*/
Datum
h3index_spgist_inner_consistent(PG_FUNCTION_ARGS)
{
spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
H3Index parent = H3_NULL;
int bc,
i;
bool stop;
int innerNodes = in->nNodes;
if (in->hasPrefix)
{
parent = DatumGetH3Index(in->prefixDatum);
}
if (in->allTheSame)
{
/* Report that all nodes should be visited */
out->nNodes = in->nNodes;
out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
for (i = 0; i < in->nNodes; i++)
{
out->nodeNumbers[i] = i;
}
PG_RETURN_VOID();
}
out->levelAdds = palloc(sizeof(int) * innerNodes);
for (i = 0; i < innerNodes; ++i)
out->levelAdds[i] = 1;
/* We must descend into the quadrant(s) identified by which */
out->nodeNumbers = (int *) palloc(sizeof(int) * innerNodes);
out->nNodes = 0;
/* "which" is a bitmask of child nodes that satisfy all constraints */
bc = -1;
stop = false;
for (i = 0; i < in->nkeys; i++)
{
/* each scankey is a constraint to be checked against */
StrategyNumber strategy = in->scankeys[i].sk_strategy;
H3Index query = DatumGetH3Index(in->scankeys[i].sk_argument);
if (parent == H3_NULL)
{
if (bc > -1)
{
stop = true;
}
bc = getBaseCellNumber(query);
}
else
{
switch (strategy)
{
case RTSameStrategyNumber:
if (spgist_cmp(&parent, &query) == 0)
stop = true;
break;
case RTContainsStrategyNumber:
if (spgist_cmp(&parent, &query) == 0)
stop = true;
/* no overlap */
break;
case RTContainedByStrategyNumber:
if (spgist_cmp(&parent, &query) == 0)
stop = true;
/* no overlap */
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
break;
}
}
if (stop)
break; /* no need to consider remaining conditions */
}
if (!stop)
{
if (bc > -1)
{
out->nodeNumbers[out->nNodes] = bc;
out->nNodes++;
}
else
{
for (i = 0; i < innerNodes; i++)
{
out->nodeNumbers[out->nNodes] = i;
out->nNodes++;
}
}
}
PG_RETURN_VOID();
}
/**
* Returns true if a leaf satisfies a query.
*
* To satisfy the query, the leaf must satisfy all the conditions described by scankeys.
*/
Datum
h3index_spgist_leaf_consistent(PG_FUNCTION_ARGS)
{
spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
H3Index leaf = DatumGetH3Index(in->leafDatum);
bool retval = true;
out->leafValue = in->leafDatum;
/* leafDatum is what it is... */
out->recheck = false;
/* all tests are exact */
/* Perform the required comparison(s) */
for (int i = 0; i < in->nkeys; i++)
{
StrategyNumber strategy = in->scankeys[i].sk_strategy;
H3Index query = DatumGetH3Index(in->scankeys[i].sk_argument);
switch (strategy)
{
case RTSameStrategyNumber:
/* leaf is equal to query */
retval = (leaf == query);
break;
case RTContainsStrategyNumber:
/* leaf contains the query */
retval = (spgist_cmp(&leaf, &query) > 0);
break;
case RTContainedByStrategyNumber:
/* leaf is contained by the query */
retval = (spgist_cmp(&leaf, &query) < 0);
break;
default:
ereport(ERROR, (
errcode(ERRCODE_INTERNAL_ERROR),
errmsg("unrecognized StrategyNumber: %d", strategy))
);
}
if (!retval)
break;
}
PG_RETURN_BOOL(retval);
}
h3-pg-4.2.2/h3/src/operators.c 0000664 0000000 0000000 00000007222 14752347156 0015741 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include "error.h"
#include "type.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_distance);
/* b-tree */
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_eq);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_ne);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_lt);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_le);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_gt);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_ge);
/* r-tree */
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_overlaps);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_contains);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_contained_by);
/* static helpers */
static int
containment(H3Index a, H3Index b)
{
H3Index aParent = a;
H3Index bParent = b;
int aRes = getResolution(a);
int bRes = getResolution(b);
if (aRes > bRes)
h3_assert(cellToParent(a, bRes, &aParent));
else if (aRes < bRes)
h3_assert(cellToParent(b, aRes, &bParent));
/* a contains b */
if (a == bParent)
return 1;
/* a contained by b */
if (b == aParent)
return -1;
/* no overlap */
return 0;
}
/* distance operator allowing for different resolutions */
Datum
h3index_distance(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
int resA = getResolution(a);
int resB = getResolution(b);
H3Error error;
int64_t distance;
if (resA < resB)
h3_assert(cellToCenterChild(a, resB, &a));
else if (resB < resA)
h3_assert(cellToCenterChild(b, resA, &b));
error = gridDistance(a, b, &distance);
/* h3_assert(error); */
if (error)
distance = -1;
PG_RETURN_INT64(distance);
}
/* b-tree operators */
Datum
h3index_eq(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
bool ret = a == b;
PG_RETURN_BOOL(ret);
}
Datum
h3index_ne(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
bool ret = a != b;
PG_RETURN_BOOL(ret);
}
Datum
h3index_lt(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
bool ret = a < b;
PG_RETURN_BOOL(ret);
}
Datum
h3index_le(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
bool ret = a <= b;
PG_RETURN_BOOL(ret);
}
Datum
h3index_gt(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
bool ret = a > b;
PG_RETURN_BOOL(ret);
}
Datum
h3index_ge(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
bool ret = a >= b;
PG_RETURN_BOOL(ret);
}
/* r-tree operators */
Datum
h3index_overlaps(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
bool ret = containment(a, b) != 0;
PG_RETURN_BOOL(ret);
}
Datum
h3index_contains(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
bool ret = containment(a, b) > 0;
PG_RETURN_BOOL(ret);
}
Datum
h3index_contained_by(PG_FUNCTION_ARGS)
{
H3Index a = PG_GETARG_H3INDEX(0);
H3Index b = PG_GETARG_H3INDEX(1);
bool ret = containment(a, b) < 0;
PG_RETURN_BOOL(ret);
}
h3-pg-4.2.2/h3/src/srf.c 0000664 0000000 0000000 00000004557 14752347156 0014525 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // SRF_IS_FIRSTCALL
#include // HeapTuple
#include "type.h"
#include "srf.h"
/*
* Set-Returning-Function assume user fctx contains indices
* will skip missing (all zeros) indices
*/
Datum
srf_return_h3_indexes_from_user_fctx(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx = SRF_PERCALL_SETUP();
int call_cntr = funcctx->call_cntr;
int max_calls = funcctx->max_calls;
H3Index *indices = (H3Index *) funcctx->user_fctx;
/* skip missing indices (all zeros) */
while (call_cntr < max_calls && !indices[call_cntr])
{
funcctx->call_cntr = ++call_cntr;
};
if (call_cntr < max_calls)
{
Datum result = H3IndexGetDatum(indices[call_cntr]);
SRF_RETURN_NEXT(funcctx, result);
}
else
{
SRF_RETURN_DONE(funcctx);
}
}
/*
* Returns hex/distance tuples from user_fctx
* will skip missing (all zeros) indices
*/
Datum
srf_return_h3_index_distances_from_user_fctx(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx = SRF_PERCALL_SETUP();
int call_cntr = funcctx->call_cntr;
int max_calls = funcctx->max_calls;
hexDistanceTuple *user_fctx = funcctx->user_fctx;
H3Index *indices = user_fctx->indices;
int *distances = user_fctx->distances;
/* skip missing indices (all zeros) */
while (!indices[call_cntr])
{
funcctx->call_cntr = ++call_cntr;
};
if (call_cntr < max_calls)
{
TupleDesc tuple_desc = funcctx->tuple_desc;
Datum values[2];
bool nulls[2] = {false};
HeapTuple tuple;
Datum result;
values[0] = H3IndexGetDatum(indices[call_cntr]);
values[1] = Int32GetDatum(distances[call_cntr]);
tuple = heap_form_tuple(tuple_desc, values, nulls);
result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}
else
{
SRF_RETURN_DONE(funcctx);
}
}
h3-pg-4.2.2/h3/src/srf.h 0000664 0000000 0000000 00000002300 14752347156 0014512 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef H3_SRF_H
#define H3_SRF_H
#include
typedef struct
{
H3Index *indices;
int *distances;
} hexDistanceTuple;
/* helper functions to return sets from user fctx */
Datum srf_return_h3_indexes_from_user_fctx(PG_FUNCTION_ARGS);
Datum srf_return_h3_index_distances_from_user_fctx(PG_FUNCTION_ARGS);
/* macros to pass on fcinfo to above helpers */
#define SRF_RETURN_H3_INDEXES_FROM_USER_FCTX() \
return srf_return_h3_indexes_from_user_fctx(fcinfo)
#define SRF_RETURN_H3_INDEX_DISTANCES_FROM_USER_FCTX() \
return srf_return_h3_index_distances_from_user_fctx(fcinfo)
#endif /* H3_SRF_H */
h3-pg-4.2.2/h3/src/type.c 0000664 0000000 0000000 00000004030 14752347156 0014676 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include // needed for send/recv functions
#include "error.h"
#include "type.h"
/* conversion */
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_in);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_out);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_send);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_recv);
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3index_to_bigint);
PGDLLEXPORT PG_FUNCTION_INFO_V1(bigint_to_h3index);
/* textual input/output functions */
Datum
h3index_in(PG_FUNCTION_ARGS)
{
char *string = PG_GETARG_CSTRING(0);
H3Index h3;
h3_assert(stringToH3(string, &h3));
PG_RETURN_H3INDEX(h3);
}
Datum
h3index_out(PG_FUNCTION_ARGS)
{
H3Index h3 = PG_GETARG_H3INDEX(0);
char *string = palloc(17 * sizeof(char));
h3_assert(h3ToString(h3, string, 17));
PG_RETURN_CSTRING(string);
}
Datum
h3index_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
PG_RETURN_H3INDEX(pq_getmsgint64(buf));
}
Datum
h3index_send(PG_FUNCTION_ARGS)
{
H3Index h3 = PG_GETARG_H3INDEX(0);
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendint64(&buf, h3);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/* bigint conversion functions */
Datum
h3index_to_bigint(PG_FUNCTION_ARGS)
{
H3Index h3index = PG_GETARG_H3INDEX(0);
PG_RETURN_INT64(h3index);
}
Datum
bigint_to_h3index(PG_FUNCTION_ARGS)
{
int64_t bigint = PG_GETARG_INT64(0);
PG_RETURN_H3INDEX(bigint);
}
h3-pg-4.2.2/h3/src/upstream_macros.h 0000664 0000000 0000000 00000004306 14752347156 0017134 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Macros/etc copied from upsteam (needed for Debian packaging) */
#ifndef H3_UPSTREAM_MACROS_H
#define H3_UPSTREAM_MACROS_H
#include "h3api.h"
/* SOURCE coordijk.h */
/** @brief H3 digit representing ijk+ axes direction.
* Values will be within the lowest 3 bits of an integer.
*/
typedef enum {
/** H3 digit in center */
CENTER_DIGIT = 0,
/** H3 digit in k-axes direction */
K_AXES_DIGIT = 1,
/** H3 digit in j-axes direction */
J_AXES_DIGIT = 2,
/** H3 digit in j == k direction */
JK_AXES_DIGIT = J_AXES_DIGIT | K_AXES_DIGIT, /* 3 */
/** H3 digit in i-axes direction */
I_AXES_DIGIT = 4,
/** H3 digit in i == k direction */
IK_AXES_DIGIT = I_AXES_DIGIT | K_AXES_DIGIT, /* 5 */
/** H3 digit in i == j direction */
IJ_AXES_DIGIT = I_AXES_DIGIT | J_AXES_DIGIT, /* 6 */
/** H3 digit in the invalid direction */
INVALID_DIGIT = 7,
/** Valid digits will be less than this value. Same value as INVALID_DIGIT.
*/
NUM_DIGITS = INVALID_DIGIT,
/** Child digit which is skipped for pentagons */
PENTAGON_SKIPPED_DIGIT = K_AXES_DIGIT /* 1 */
} Direction;
/* SOURCE h3Index.h */
/** The number of bits in a single H3 resolution digit. */
#define H3_PER_DIGIT_OFFSET 3
/** 1's in the 3 bits of res 15 digit bits, 0's everywhere else. */
#define H3_DIGIT_MASK ((uint64_t)(7))
/**
* Gets the resolution res integer digit (0-7) of h3.
*/
#define H3_GET_INDEX_DIGIT(h3, res) \
((Direction)((((h3) >> ((MAX_H3_RES - (res)) * H3_PER_DIGIT_OFFSET)) & \
H3_DIGIT_MASK)))
#endif /* H3_UPSTREAM_MACROS_H */
h3-pg-4.2.2/h3/test/ 0000775 0000000 0000000 00000000000 14752347156 0013744 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3/test/CMakeLists.txt 0000664 0000000 0000000 00000001607 14752347156 0016510 0 ustar 00root root 0000000 0000000 set(TESTS
extension
hierarchy
indexing
inspection
miscellaneous
opclass_brin
opclass_btree
opclass_hash
opclass_spgist
regions
traversal
type
edge
)
if(PostgreSQL_REGRESS)
add_test(
NAME h3_regress
COMMAND ${PostgreSQL_REGRESS}
--temp-instance=${CMAKE_BINARY_DIR}/tmp
--bindir=${PostgreSQL_BIN_DIR}
--inputdir=${CMAKE_CURRENT_SOURCE_DIR}
--outputdir=${CMAKE_CURRENT_BINARY_DIR}
--load-extension h3
${TESTS}
)
endif()
if(PostgreSQL_VALIDATE_EXTUPGRADE)
add_test(
NAME h3_validate_extupgrade
COMMAND pg_validate_extupgrade
--extname h3
--from 0.1.0
--to ${INSTALL_VERSION}
)
endif()
# @TODO: Figure out how to inline on MacOS
if(NOT APPLE)
add_test(
NAME h3_inlined
COMMAND sh -c "! objdump -D ${PostgreSQL_PKG_LIBRARY_DIR}/h3.so | grep radsToDegs"
CONFIGURATIONS Release
)
endif()
h3-pg-4.2.2/h3/test/expected/ 0000775 0000000 0000000 00000000000 14752347156 0015545 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3/test/expected/clustering.out 0000664 0000000 0000000 00000000747 14752347156 0020465 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set string '\'801dfffffffffff\''
\set hexagon ':string::h3index'
CREATE TABLE h3_test_clustering (hex h3index PRIMARY KEY);
INSERT INTO h3_test_clustering (hex) SELECT * from h3_get_res_0_cells();
CREATE INDEX h3_test_clustering_index ON h3_test_clustering USING btree (hex);
CLUSTER h3_test_clustering_index ON h3_test_clustering;
--
-- TEST clustering on B-tree
--
SELECT hex = :hexagon FROM (
SELECT hex FROM h3_test_clustering WHERE hex = :hexagon
) q;
t
h3-pg-4.2.2/h3/test/expected/edge.out 0000664 0000000 0000000 00000002510 14752347156 0017200 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set hexagon '\'880326b885fffff\'::h3index'
\set neighbor '\'880326b887fffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set edge '\'1180326b885fffff\'::h3index'
--
-- TEST h3_are_neighbor_cells
--
SELECT h3_are_neighbor_cells(:hexagon, :neighbor);
t
SELECT NOT h3_are_neighbor_cells(:hexagon, :hexagon);
t
--
-- TEST h3_cells_to_directed_edge
--
SELECT h3_cells_to_directed_edge(:hexagon, :neighbor) = :edge;
t
--
-- TEST h3_is_valid_directed_edge
--
SELECT h3_is_valid_directed_edge(:edge);
t
SELECT NOT h3_is_valid_directed_edge(:hexagon);
t
--
-- TEST h3_get_directed_edge_origin and
-- h3_get_directed_edge_destination
--
SELECT h3_get_directed_edge_origin(:edge) = :hexagon
AND h3_get_directed_edge_destination(:edge) = :neighbor;
t
--
-- TEST h3_directed_edge_to_cells
--
SELECT h3_directed_edge_to_cells(:edge) = (:hexagon, :neighbor);
t
--
-- TEST h3_origin_to_directed_edges
--
SELECT array_length(array_agg(edge), 1) = 6 FROM (
SELECT h3_origin_to_directed_edges(:hexagon) edge
) q;
t
SELECT array_length(array_agg(edge), 1) = 5 expected FROM (
SELECT h3_origin_to_directed_edges(:pentagon) edge
) q;
t
--
-- TEST h3_directed_edge_to_boundary
--
SELECT h3_directed_edge_to_boundary(:edge)
~= polygon '((89.5830164946548,64.7146398954916),(89.5790678021742,64.2872231517217))'
t
h3-pg-4.2.2/h3/test/expected/extension.out 0000664 0000000 0000000 00000000514 14752347156 0020312 0 ustar 00root root 0000000 0000000 \pset tuples_only on
--
-- TEST h3_get_extension_version
--
SELECT h3_get_extension_version() ~ '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
OR h3_get_extension_version() = 'unreleased';
t
h3-pg-4.2.2/h3/test/expected/hierarchy.out 0000664 0000000 0000000 00000006115 14752347156 0020257 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- neighbouring indexes (one hexagon, one pentagon) at resolution 3
\set hexagon '\'831c02fffffffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set resolution 3
--
-- TEST h3_cell_to_children, h3_cell_to_parent and h3_to_center_chil
--
-- all parents of one the children of a hexagon, must be the original hexagon
SELECT bool_and(i = :hexagon) FROM (
SELECT h3_cell_to_parent(h3_cell_to_children(:hexagon)) i
) q;
t
-- one child of the parent of a hexagon, must be the original hexagon
SELECT bool_or(i = :hexagon) FROM (
SELECT h3_cell_to_children(h3_cell_to_parent(:hexagon)) i
) q;
t
-- all parents of one the children of a pentagon, must be the original pentagon
SELECT bool_and(i = :pentagon) FROM (
SELECT h3_cell_to_parent(h3_cell_to_children(:pentagon)) i
) q;
t
-- one child of the parent of a pentagon, must be the original pentagon
SELECT bool_or(i = :pentagon) FROM (
SELECT h3_cell_to_children(h3_cell_to_parent(:pentagon)) i
) q;
t
-- hexagon has 7 children
SELECT array_length(array_agg(hex), 1) = 7 FROM (
SELECT h3_cell_to_children(:hexagon) hex
) q;
t
-- pentagon has 6 children
SELECT array_length(array_agg(hex), 1) = 6 FROM (
SELECT h3_cell_to_children(:pentagon) hex
) q;
t
-- parent is one lower resolution
SELECT h3_get_resolution(h3_cell_to_parent(:hexagon)) = :resolution -1;
t
-- all children is one higher resolution
SELECT bool_and(r = :resolution +1) FROM (
SELECT h3_get_resolution(h3_cell_to_children(:hexagon)) r
) q;
t
-- parent of center child should be original index
SELECT :hexagon = h3_cell_to_parent(h3_cell_to_center_child(:hexagon, 15), :resolution);
t
--
-- TEST h3_cell_to_child_pos and h3_child_pos_to_cell
--
SELECT :hexagon = h3_child_pos_to_cell(
h3_cell_to_child_pos(:hexagon, :resolution - 1),
h3_cell_to_parent(:hexagon),
:resolution
);
t
SELECT COUNT(*) = 7 FROM (
SELECT h3_cell_to_child_pos(
h3_cell_to_children(:hexagon), :resolution - 1
)
) q;
t
--
-- TEST h3_compact_cells and h3_uncompact_cells
--
-- compacts the children of two hexes into the original two hexes
SELECT array_agg(result) is null FROM (
SELECT h3_compact_cells(
ARRAY(SELECT h3_cell_to_children(:hexagon) UNION SELECT h3_cell_to_children(:pentagon))
) result
EXCEPT SELECT unnest(ARRAY[:hexagon, :pentagon]) result
) q;
t
-- compact is inverse of uncompact
SELECT h3_compact_cells(ARRAY(SELECT h3_uncompact_cells(ARRAY[:hexagon], :resolution))) = :hexagon;
t
-- uncompacts all to same resolution, gives same result as getting children
SELECT array_agg(result) is null FROM (
SELECT h3_uncompact_cells(ARRAY(
SELECT h3_cell_to_children(:hexagon) UNION SELECT :pentagon
), :resolution +2) result
EXCEPT (
SELECT h3_cell_to_children(:hexagon, :resolution +2) result
UNION SELECT h3_cell_to_children(:pentagon, :resolution +2) result
)
) q;
t
--
-- TEST h3_cell_to_children_slow
--
-- h3_cell_to_children_slow and h3_cell_to_children have same result
SELECT array_agg(result) is null FROM (
SELECT h3_cell_to_children_slow(:hexagon, :resolution + 3) result
EXCEPT SELECT h3_cell_to_children(:hexagon, :resolution + 3) result
) q;
t
h3-pg-4.2.2/h3/test/expected/indexing.out 0000664 0000000 0000000 00000004231 14752347156 0020103 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- neighbouring indexes (one hexagon, one pentagon) at resolution 3
\set geo POINT(-144.52399108028, 49.7165031828995)
\set hexagon '\'831c02fffffffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set edgecross '\'8003fffffffffff\'::h3index'
\set resolution 3
--
-- TEST h3_cell_to_lat_lng and h3_lat_lng_to_cell
--
-- convertion to geo works
SELECT h3_cell_to_lat_lng(:hexagon) ~= :geo;
t
-- convertion to h3 index works
SELECT h3_lat_lng_to_cell(:geo, :resolution) = :hexagon;
t
-- h3_cell_to_lat_lng is inverse of h3_lat_lng_to_cell
SELECT h3_cell_to_lat_lng(i) ~= :geo AND h3_get_resolution(i) = :resolution FROM (
SELECT h3_lat_lng_to_cell(:geo, :resolution) AS i
) AS q;
t
-- h3_lat_lng_to_cell is inverse of h3_cell_to_lat_lng
SELECT h3_lat_lng_to_cell(g, r) = :hexagon FROM (
SELECT h3_cell_to_lat_lng(:hexagon) AS g, h3_get_resolution(:hexagon) AS r
) AS q;
t
-- same for pentagon
SELECT h3_lat_lng_to_cell(g, r) = :pentagon FROM (
SELECT h3_cell_to_lat_lng(:pentagon) AS g, h3_get_resolution(:pentagon) AS r
) AS q;
t
--
-- TEST h3_cell_to_boundary
--
-- polyfill of geo boundary returns original index
SELECT h3_polygon_to_cells(h3_cell_to_boundary(:hexagon), null, :resolution) = :hexagon;
t
-- same for pentagon
SELECT h3_polygon_to_cells(h3_cell_to_boundary(:pentagon), null, :resolution) = :pentagon;
t
-- the boundary of an edgecrossing index is different with flag set to true
SELECT h3_cell_to_boundary(:hexagon) ~= h3_cell_to_boundary(:hexagon, true)
AND NOT h3_cell_to_boundary(:edgecross) ~= h3_cell_to_boundary(:edgecross, true);
WARNING: Deprecated: Please use `SET h3.extend_antimeridian TO true` instead of extend flag
WARNING: Deprecated: Please use `SET h3.extend_antimeridian TO true` instead of extend flag
t
-- cell to parent RES_MISMATCH
CREATE FUNCTION h3_fail_indexing_cell_to_parent() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_cell_to_parent('831c02fffffffff', 10);
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
SELECT h3_fail_indexing_cell_to_parent();
t
DROP FUNCTION h3_fail_indexing_cell_to_parent;
h3-pg-4.2.2/h3/test/expected/inspection.out 0000664 0000000 0000000 00000002444 14752347156 0020455 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- neighbouring indexes (one hexagon, one pentagon) at resolution 3
\set invalid '\'0\''
\set hexagon '\'831c02fffffffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set resolution 3
--
-- TEST h3_get_resolution
--
SELECT h3_get_resolution(:hexagon) = :resolution AND h3_get_resolution(:pentagon) = :resolution;
t
--
-- TEST h3_get_base_cell_number
--
-- base cell is same for parents
SELECT h3_get_base_cell_number(:hexagon) = h3_get_base_cell_number(h3_cell_to_parent(:hexagon));
t
SELECT h3_get_base_cell_number(:pentagon) = h3_get_base_cell_number(h3_cell_to_parent(:pentagon));
t
--
-- TEST h3_is_valid_cell
--
SELECT h3_is_valid_cell(:hexagon) AND h3_is_valid_cell(:pentagon) AND NOT h3_is_valid_cell(:invalid);
t
--
-- TEST h3_is_res_class_iii
--
-- if index is Class III then parent is not
SELECT h3_is_res_class_iii(:hexagon) AND NOT h3_is_res_class_iii(h3_cell_to_parent(:hexagon));
t
SELECT h3_is_res_class_iii(:pentagon) AND NOT h3_is_res_class_iii(h3_cell_to_parent(:pentagon));
t
--
-- TEST h3_is_pentagon
--
SELECT h3_is_pentagon(:pentagon) AND NOT h3_is_pentagon(:hexagon);
t
--
-- TEST h3_get_icosahedron_faces
--
SELECT h3_get_icosahedron_faces('851c0047fffffff') = ARRAY[11,6];
t
SELECT h3_get_icosahedron_faces('851c004bfffffff') = ARRAY[6];
t
h3-pg-4.2.2/h3/test/expected/miscellaneous.out 0000664 0000000 0000000 00000004376 14752347156 0021153 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set degs 90.45
\set rads 1.57865030842887
\set epsilon 0.000000001
\set edge '\'1180326b885fffff\'::h3index'
--
-- TEST h3_great_circle_distance
--
\set lyon POINT(4.8422, 45.7597)
\set paris POINT(2.3508, 48.8567)
SELECT h3_great_circle_distance(:lyon, :paris, 'rads') - 0.0615628186794217 < :epsilon;
t
SELECT h3_great_circle_distance(:lyon, :paris, 'm') - 392217.1598841777 < :epsilon;
t
SELECT h3_great_circle_distance(:lyon, :paris, 'km') - 392.21715988417765 < :epsilon;
t
-- test that 'km' is the default unit
SELECT h3_great_circle_distance(:lyon, :paris, 'km') = h3_great_circle_distance(:lyon, :paris);
t
--
-- TEST h3_get_hexagon_area_avg
--
SELECT abs(h3_get_hexagon_area_avg(10, 'm') - 15047.50190766437) < :epsilon;
t
SELECT abs(h3_get_hexagon_area_avg(10, 'km') - 0.01504750190766435) < :epsilon;
t
SELECT h3_get_hexagon_area_avg(10, 'km') = h3_get_hexagon_area_avg(10);
t
--
-- TEST h3_cell_area
--
\set expected_km2 0.01119834221989390
SELECT abs((h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10), 'm^2') / 1000000) - :expected_km2) < :epsilon;
t
SELECT abs(h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10), 'km^2') - :expected_km2) < :epsilon;
t
SELECT h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10), 'rads^2') > 0;
t
-- default is km^2
SELECT h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10), 'km^2') = h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10));
t
--
-- TEST h3_get_hexagon_edge_length_avg
--
SELECT h3_get_hexagon_edge_length_avg(10, 'm') - 75.86378287 < :epsilon;
t
SELECT h3_get_hexagon_edge_length_avg(10, 'km') - 0.075863783 < :epsilon;
t
SELECT h3_get_hexagon_edge_length_avg(10, 'km') = h3_get_hexagon_edge_length_avg(10);
t
--
-- TEST h3_edge_length
--
SELECT h3_edge_length(:edge, 'rads') > 0;
t
SELECT h3_edge_length(:edge, 'km') > h3_edge_length(:edge, 'rads');
t
SELECT h3_edge_length(:edge, 'm') > h3_edge_length(:edge, 'km');
t
SELECT h3_edge_length(:edge) = h3_edge_length(:edge, 'km');
t
--
-- TEST h3_get_num_cells
--
SELECT h3_get_num_cells(0) = 122;
t
SELECT h3_get_num_cells(15) = 569707381193162;
t
--
-- TEST h3_get_res_0_cells
--
SELECT COUNT(*) = 122 FROM (SELECT h3_get_res_0_cells()) q;
t
--
-- TEST h3_get_pentagons
--
SELECT COUNT(*) = 12 FROM (SELECT h3_get_pentagons(6)) q;
t
h3-pg-4.2.2/h3/test/expected/opclass_brin.out 0000664 0000000 0000000 00000000602 14752347156 0020752 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set string '\'801dfffffffffff\''
\set hexagon ':string::h3index'
CREATE TABLE h3_test_brin (hex h3index PRIMARY KEY);
INSERT INTO h3_test_brin (hex) SELECT * FROM h3_get_res_0_cells();
CREATE INDEX h3_brin ON h3_test_brin USING brin (hex);
--
-- Test BRIN operator class
--
SELECT hex = :hexagon FROM (
SELECT hex FROM h3_test_brin WHERE hex = :hexagon
) q;
t
h3-pg-4.2.2/h3/test/expected/opclass_btree.out 0000664 0000000 0000000 00000000614 14752347156 0021124 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set string '\'801dfffffffffff\''
\set hexagon ':string::h3index'
CREATE TABLE h3_test_btree (hex h3index PRIMARY KEY);
INSERT INTO h3_test_btree (hex) SELECT * from h3_get_res_0_cells();
CREATE INDEX h3_btree ON h3_test_btree USING btree (hex);
--
-- TEST b-tree operator class
--
SELECT hex = :hexagon FROM (
SELECT hex FROM h3_test_btree WHERE hex = :hexagon
) q;
t
h3-pg-4.2.2/h3/test/expected/opclass_hash.out 0000664 0000000 0000000 00000000730 14752347156 0020745 0 ustar 00root root 0000000 0000000 \pset tuples_only on
CREATE TABLE h3_test_hash (hex h3index PRIMARY KEY);
INSERT INTO h3_test_hash (hex) SELECT * from h3_get_res_0_cells();
CREATE INDEX h3_btree ON h3_test_hash USING hash (hex);
ERROR: relation "h3_btree" already exists
--
-- TEST hash operator class
--
SELECT COUNT(hex) = 122 FROM (
SELECT hex FROM h3_test_hash WHERE hex IN (SELECT h3_get_res_0_cells())
) q;
t
CREATE TABLE h3_test_hash_part (hex h3index PRIMARY KEY) PARTITION BY HASH (hex);
h3-pg-4.2.2/h3/test/expected/opclass_spgist.out 0000664 0000000 0000000 00000001412 14752347156 0021331 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set hexagon '\'831c02fffffffff\'::h3index'
CREATE TABLE h3_test_spgist (hex h3index);
CREATE INDEX SPGIST_IDX ON h3_test_spgist USING spgist(hex h3index_ops_experimental);
INSERT INTO h3_test_spgist (hex) SELECT h3_cell_to_parent(:hexagon);
INSERT INTO h3_test_spgist (hex) SELECT h3_cell_to_children(:hexagon);
INSERT INTO h3_test_spgist (hex) SELECT h3_cell_to_center_child(:hexagon, 15);
--
-- TEST SP-GiST
--
SELECT COUNT(*) = 1 FROM h3_test_spgist WHERE hex @> :hexagon;
t
SELECT COUNT(*) = 8 FROM h3_test_spgist WHERE hex <@ :hexagon;
t
--
TRUNCATE TABLE h3_test_spgist;
INSERT INTO h3_test_spgist (hex) SELECT h3_cell_to_children(h3_cell_to_center_child(:hexagon, 10), 15);
SELECT COUNT(*) = 16807 FROM h3_test_spgist WHERE hex <@ :hexagon;
t
h3-pg-4.2.2/h3/test/expected/regions.out 0000664 0000000 0000000 00000005043 14752347156 0017746 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- res 0 index
\set res0index '\'8059fffffffffff\''
-- center hex
\set center '\'81583ffffffffff\''
-- 7 child hexes in res 0 index
\set solid 'ARRAY(SELECT h3_cell_to_children(:res0index, 1))'
-- 6 child hexes in rim of res 0 index
\set hollow 'array_remove(:solid, :center)'
-- pentagon
\set pentagon '\'831c00fffffffff\'::h3index'
--
-- TEST h3_polygon_to_cells and h3_cells_to_multi_polygon
--
-- h3_polygon_to_cells is inverse of h3_cells_to_multi_polygon for set without holes
SELECT array_agg(result) is null FROM (
SELECT h3_polygon_to_cells(exterior, holes, 1) result FROM (
SELECT exterior, holes FROM h3_cells_to_multi_polygon(:solid)
) qq
EXCEPT SELECT unnest(:solid) result
) q;
t
-- h3_polygon_to_cells is inverse of h3_cells_to_multi_polygon for set with a hole
SELECT array_agg(result) is null FROM (
SELECT h3_polygon_to_cells(exterior, holes, 1) result FROM (
SELECT exterior, holes FROM h3_cells_to_multi_polygon(:hollow)
) qq
EXCEPT SELECT unnest(:hollow) result
) q;
t
-- h3_polyfill doesn't segfault on NULL value in holes
SELECT TRUE FROM (
SELECT h3_polygon_to_cells(exterior, ARRAY[NULL::POLYGON], 1) result FROM (
SELECT exterior, holes FROM h3_cells_to_multi_polygon(
ARRAY[:pentagon]::H3Index[]
)
) qq
) q LIMIT 1;
t
-- h3_polyfill throws on non-polygons
CREATE FUNCTION h3_test_polyfill_bad1() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_polyfill(ST_GeomFromText('POINT(-71.160281 42.258729)',4326), 8);
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
CREATE FUNCTION h3_test_polyfill_bad2() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_polyfill(ST_GeomFromText('LINESTRING(-71.160281 42.258729,-71.160837 42.259113,-71.161144 42.25932)',4326), 8);
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
SELECT h3_test_polyfill_bad1();
t
SELECT h3_test_polyfill_bad2();
t
DROP FUNCTION h3_test_polyfill_bad1;
DROP FUNCTION h3_test_polyfill_bad2;
--
-- TEST h3_polygon_to_cells_experimental
--
-- h3_polygon_to_cells is inverse of h3_cells_to_multi_polygon for set without holes
SELECT array_agg(result) is null FROM (
SELECT h3_polygon_to_cells_experimental(exterior, holes, 1, 'overlapping') result FROM (
SELECT exterior, holes FROM h3_cells_to_multi_polygon(:solid)
) qq
EXCEPT SELECT h3_grid_disk(h3_cell_to_center_child(:res0index), 2) result
) q;
t
h3-pg-4.2.2/h3/test/expected/traversal.out 0000664 0000000 0000000 00000004040 14752347156 0020277 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set hexagon '\'880326b88dfffff\''
\set origin '\'880326b887fffff\''
\set pentagon '\'831c00fffffffff\''
--
-- TEST h3_grid_disk and h3_grid_ring_unsafe
--
-- gridDisk 0 is input index
SELECT h3_grid_disk(:hexagon, 0) = :hexagon;
t
-- gridDisk 2 is same as sum of gridRing 0, 1 and 2
SELECT array_agg(r) is null FROM (
SELECT h3_grid_disk(:hexagon, 2) r
EXCEPT (
SELECT h3_grid_ring_unsafe(:hexagon, 0) r
UNION SELECT h3_grid_ring_unsafe(:hexagon, 1) r
UNION SELECT h3_grid_ring_unsafe(:hexagon, 2) r
)
) q;
t
--
-- TEST h3_grid_disk_distances
--
-- correct number of indexes at distances 0, 1 and 2 for k=2
SELECT COUNT(index) filter (WHERE distance = 0) = 1
AND COUNT(index) filter (WHERE distance = 1) = 6
AND COUNT(index) filter (WHERE distance = 2) = 12
FROM (
SELECT index, distance FROM h3_grid_disk_distances(:hexagon, 2)
) q;
t
-- same for pentagon
SELECT COUNT(index) filter (WHERE distance = 0) = 1
AND COUNT(index) filter (WHERE distance = 1) = 5
AND COUNT(index) filter (WHERE distance = 2) = 10
FROM (
SELECT index, distance FROM h3_grid_disk_distances(:pentagon, 2)
) q;
t
--
-- TEST h3_grid_path_cells
--
SELECT ARRAY(SELECT h3_grid_path_cells('841c023ffffffff', '841c025ffffffff'))
= ARRAY['841c023ffffffff','841c027ffffffff','841c025ffffffff']::h3index[];
t
--
-- TEST h3_grid_distance
--
-- returns 1 for indexes with one index between them
SELECT h3_grid_distance('880326b881fffff', '880326b885fffff') = 1;
t
-- throws for invalid inputs
CREATE FUNCTION h3_test_grid_distance_invalid() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_grid_distance('880326b881fffff', h3_cell_to_parent('880326b885fffff')) = -1;
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
SELECT h3_test_grid_distance_invalid();
t
--
-- TEST h3_cell_to_local_ij and h3_local_ij_to_cell
--
-- they are inverse of each others
SELECT :hexagon = h3_local_ij_to_cell(:origin, h3_cell_to_local_ij(:origin, :hexagon));
t
h3-pg-4.2.2/h3/test/expected/type.out 0000664 0000000 0000000 00000002303 14752347156 0017255 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set string '\'801dfffffffffff\''
\set asbigint 576988517884755967
\set hexagon ':string::h3index'
\set pentagon '\'844c001ffffffff\'::h3index'
--
-- TEST operators
--
SELECT :hexagon = :hexagon;
t
SELECT NOT :hexagon = :pentagon;
t
SELECT NOT :hexagon <> :hexagon;
t
SELECT :hexagon <> :pentagon;
t
SELECT :pentagon <@ h3_cell_to_parent(:pentagon);
t
SELECT bool_and(:pentagon @> c) FROM (
SELECT h3_cell_to_children(:pentagon) c
) q;
t
--
-- TEST bigint casting
--
SELECT :asbigint = :hexagon::bigint;
t
SELECT :hexagon = :asbigint::h3index;
t
--
-- TEST binary io
--
CREATE TEMPORARY TABLE h3_test_binary_send (hex h3index PRIMARY KEY);
CREATE TEMPORARY TABLE h3_test_binary_recv (hex h3index PRIMARY KEY);
INSERT INTO h3_test_binary_send (hex) SELECT * from h3_get_res_0_cells();
-- we need to use \copy instead of SQL COPY in order to have relative path
\copy h3_test_binary_send TO 'h3_test_binary.bin' (FORMAT binary)
\copy h3_test_binary_recv FROM 'h3_test_binary.bin' (FORMAT binary)
-- make sure re-imported data matches original data
SELECT array_agg(hex) is null FROM (
SELECT hex FROM h3_test_binary_send
EXCEPT SELECT hex FROM h3_test_binary_recv
) q;
t
h3-pg-4.2.2/h3/test/expected/vertex.out 0000664 0000000 0000000 00000001265 14752347156 0017617 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set hexagon '\'880326b885fffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set vertex2 '\'2280326b885fffff\'::h3index'
\set geo POINT(65.60200108645547,89.57740563247555)
--
-- TEST h3_cell_to_vertex
--
SELECT h3_cell_to_vertex(:hexagon, 2) = :vertex2;
t
--
-- TEST h3_cell_to_vertexes
--
SELECT COUNT(*) = 6 FROM (
SELECT h3_cell_to_vertexes(:hexagon)
) q;
t
SELECT COUNT(*) = 5 FROM (
SELECT h3_cell_to_vertexes(:pentagon)
) q;
t
--
-- TEST h3_vertex_to_lat_lng
--
SELECT h3_vertex_to_lat_lng(:vertex2) ~= :geo;
t
--
-- TEST h3_is_valid_vertex and
--
SELECT h3_is_valid_vertex(:vertex2);
t
SELECT NOT h3_is_valid_vertex(:hexagon);
t
h3-pg-4.2.2/h3/test/sql/ 0000775 0000000 0000000 00000000000 14752347156 0014543 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3/test/sql/clustering.sql 0000664 0000000 0000000 00000000744 14752347156 0017450 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set string '\'801dfffffffffff\''
\set hexagon ':string::h3index'
CREATE TABLE h3_test_clustering (hex h3index PRIMARY KEY);
INSERT INTO h3_test_clustering (hex) SELECT * from h3_get_res_0_cells();
CREATE INDEX h3_test_clustering_index ON h3_test_clustering USING btree (hex);
CLUSTER h3_test_clustering_index ON h3_test_clustering;
--
-- TEST clustering on B-tree
--
SELECT hex = :hexagon FROM (
SELECT hex FROM h3_test_clustering WHERE hex = :hexagon
) q;
h3-pg-4.2.2/h3/test/sql/edge.sql 0000664 0000000 0000000 00000002456 14752347156 0016177 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set hexagon '\'880326b885fffff\'::h3index'
\set neighbor '\'880326b887fffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set edge '\'1180326b885fffff\'::h3index'
--
-- TEST h3_are_neighbor_cells
--
SELECT h3_are_neighbor_cells(:hexagon, :neighbor);
SELECT NOT h3_are_neighbor_cells(:hexagon, :hexagon);
--
-- TEST h3_cells_to_directed_edge
--
SELECT h3_cells_to_directed_edge(:hexagon, :neighbor) = :edge;
--
-- TEST h3_is_valid_directed_edge
--
SELECT h3_is_valid_directed_edge(:edge);
SELECT NOT h3_is_valid_directed_edge(:hexagon);
--
-- TEST h3_get_directed_edge_origin and
-- h3_get_directed_edge_destination
--
SELECT h3_get_directed_edge_origin(:edge) = :hexagon
AND h3_get_directed_edge_destination(:edge) = :neighbor;
--
-- TEST h3_directed_edge_to_cells
--
SELECT h3_directed_edge_to_cells(:edge) = (:hexagon, :neighbor);
--
-- TEST h3_origin_to_directed_edges
--
SELECT array_length(array_agg(edge), 1) = 6 FROM (
SELECT h3_origin_to_directed_edges(:hexagon) edge
) q;
SELECT array_length(array_agg(edge), 1) = 5 expected FROM (
SELECT h3_origin_to_directed_edges(:pentagon) edge
) q;
--
-- TEST h3_directed_edge_to_boundary
--
SELECT h3_directed_edge_to_boundary(:edge)
~= polygon '((89.5830164946548,64.7146398954916),(89.5790678021742,64.2872231517217))' h3-pg-4.2.2/h3/test/sql/extension.sql 0000664 0000000 0000000 00000000512 14752347156 0017276 0 ustar 00root root 0000000 0000000 \pset tuples_only on
--
-- TEST h3_get_extension_version
--
SELECT h3_get_extension_version() ~ '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
OR h3_get_extension_version() = 'unreleased';
h3-pg-4.2.2/h3/test/sql/hierarchy.sql 0000664 0000000 0000000 00000006044 14752347156 0017246 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- neighbouring indexes (one hexagon, one pentagon) at resolution 3
\set hexagon '\'831c02fffffffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set resolution 3
--
-- TEST h3_cell_to_children, h3_cell_to_parent and h3_to_center_chil
--
-- all parents of one the children of a hexagon, must be the original hexagon
SELECT bool_and(i = :hexagon) FROM (
SELECT h3_cell_to_parent(h3_cell_to_children(:hexagon)) i
) q;
-- one child of the parent of a hexagon, must be the original hexagon
SELECT bool_or(i = :hexagon) FROM (
SELECT h3_cell_to_children(h3_cell_to_parent(:hexagon)) i
) q;
-- all parents of one the children of a pentagon, must be the original pentagon
SELECT bool_and(i = :pentagon) FROM (
SELECT h3_cell_to_parent(h3_cell_to_children(:pentagon)) i
) q;
-- one child of the parent of a pentagon, must be the original pentagon
SELECT bool_or(i = :pentagon) FROM (
SELECT h3_cell_to_children(h3_cell_to_parent(:pentagon)) i
) q;
-- hexagon has 7 children
SELECT array_length(array_agg(hex), 1) = 7 FROM (
SELECT h3_cell_to_children(:hexagon) hex
) q;
-- pentagon has 6 children
SELECT array_length(array_agg(hex), 1) = 6 FROM (
SELECT h3_cell_to_children(:pentagon) hex
) q;
-- parent is one lower resolution
SELECT h3_get_resolution(h3_cell_to_parent(:hexagon)) = :resolution -1;
-- all children is one higher resolution
SELECT bool_and(r = :resolution +1) FROM (
SELECT h3_get_resolution(h3_cell_to_children(:hexagon)) r
) q;
-- parent of center child should be original index
SELECT :hexagon = h3_cell_to_parent(h3_cell_to_center_child(:hexagon, 15), :resolution);
--
-- TEST h3_cell_to_child_pos and h3_child_pos_to_cell
--
SELECT :hexagon = h3_child_pos_to_cell(
h3_cell_to_child_pos(:hexagon, :resolution - 1),
h3_cell_to_parent(:hexagon),
:resolution
);
SELECT COUNT(*) = 7 FROM (
SELECT h3_cell_to_child_pos(
h3_cell_to_children(:hexagon), :resolution - 1
)
) q;
--
-- TEST h3_compact_cells and h3_uncompact_cells
--
-- compacts the children of two hexes into the original two hexes
SELECT array_agg(result) is null FROM (
SELECT h3_compact_cells(
ARRAY(SELECT h3_cell_to_children(:hexagon) UNION SELECT h3_cell_to_children(:pentagon))
) result
EXCEPT SELECT unnest(ARRAY[:hexagon, :pentagon]) result
) q;
-- compact is inverse of uncompact
SELECT h3_compact_cells(ARRAY(SELECT h3_uncompact_cells(ARRAY[:hexagon], :resolution))) = :hexagon;
-- uncompacts all to same resolution, gives same result as getting children
SELECT array_agg(result) is null FROM (
SELECT h3_uncompact_cells(ARRAY(
SELECT h3_cell_to_children(:hexagon) UNION SELECT :pentagon
), :resolution +2) result
EXCEPT (
SELECT h3_cell_to_children(:hexagon, :resolution +2) result
UNION SELECT h3_cell_to_children(:pentagon, :resolution +2) result
)
) q;
--
-- TEST h3_cell_to_children_slow
--
-- h3_cell_to_children_slow and h3_cell_to_children have same result
SELECT array_agg(result) is null FROM (
SELECT h3_cell_to_children_slow(:hexagon, :resolution + 3) result
EXCEPT SELECT h3_cell_to_children(:hexagon, :resolution + 3) result
) q;
h3-pg-4.2.2/h3/test/sql/indexing.sql 0000664 0000000 0000000 00000003704 14752347156 0017075 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- neighbouring indexes (one hexagon, one pentagon) at resolution 3
\set geo POINT(-144.52399108028, 49.7165031828995)
\set hexagon '\'831c02fffffffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set edgecross '\'8003fffffffffff\'::h3index'
\set resolution 3
--
-- TEST h3_cell_to_lat_lng and h3_lat_lng_to_cell
--
-- convertion to geo works
SELECT h3_cell_to_lat_lng(:hexagon) ~= :geo;
-- convertion to h3 index works
SELECT h3_lat_lng_to_cell(:geo, :resolution) = :hexagon;
-- h3_cell_to_lat_lng is inverse of h3_lat_lng_to_cell
SELECT h3_cell_to_lat_lng(i) ~= :geo AND h3_get_resolution(i) = :resolution FROM (
SELECT h3_lat_lng_to_cell(:geo, :resolution) AS i
) AS q;
-- h3_lat_lng_to_cell is inverse of h3_cell_to_lat_lng
SELECT h3_lat_lng_to_cell(g, r) = :hexagon FROM (
SELECT h3_cell_to_lat_lng(:hexagon) AS g, h3_get_resolution(:hexagon) AS r
) AS q;
-- same for pentagon
SELECT h3_lat_lng_to_cell(g, r) = :pentagon FROM (
SELECT h3_cell_to_lat_lng(:pentagon) AS g, h3_get_resolution(:pentagon) AS r
) AS q;
--
-- TEST h3_cell_to_boundary
--
-- polyfill of geo boundary returns original index
SELECT h3_polygon_to_cells(h3_cell_to_boundary(:hexagon), null, :resolution) = :hexagon;
-- same for pentagon
SELECT h3_polygon_to_cells(h3_cell_to_boundary(:pentagon), null, :resolution) = :pentagon;
-- the boundary of an edgecrossing index is different with flag set to true
SELECT h3_cell_to_boundary(:hexagon) ~= h3_cell_to_boundary(:hexagon, true)
AND NOT h3_cell_to_boundary(:edgecross) ~= h3_cell_to_boundary(:edgecross, true);
-- cell to parent RES_MISMATCH
CREATE FUNCTION h3_fail_indexing_cell_to_parent() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_cell_to_parent('831c02fffffffff', 10);
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
SELECT h3_fail_indexing_cell_to_parent();
DROP FUNCTION h3_fail_indexing_cell_to_parent; h3-pg-4.2.2/h3/test/sql/inspection.sql 0000664 0000000 0000000 00000002414 14752347156 0017440 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- neighbouring indexes (one hexagon, one pentagon) at resolution 3
\set invalid '\'0\''
\set hexagon '\'831c02fffffffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set resolution 3
--
-- TEST h3_get_resolution
--
SELECT h3_get_resolution(:hexagon) = :resolution AND h3_get_resolution(:pentagon) = :resolution;
--
-- TEST h3_get_base_cell_number
--
-- base cell is same for parents
SELECT h3_get_base_cell_number(:hexagon) = h3_get_base_cell_number(h3_cell_to_parent(:hexagon));
SELECT h3_get_base_cell_number(:pentagon) = h3_get_base_cell_number(h3_cell_to_parent(:pentagon));
--
-- TEST h3_is_valid_cell
--
SELECT h3_is_valid_cell(:hexagon) AND h3_is_valid_cell(:pentagon) AND NOT h3_is_valid_cell(:invalid);
--
-- TEST h3_is_res_class_iii
--
-- if index is Class III then parent is not
SELECT h3_is_res_class_iii(:hexagon) AND NOT h3_is_res_class_iii(h3_cell_to_parent(:hexagon));
SELECT h3_is_res_class_iii(:pentagon) AND NOT h3_is_res_class_iii(h3_cell_to_parent(:pentagon));
--
-- TEST h3_is_pentagon
--
SELECT h3_is_pentagon(:pentagon) AND NOT h3_is_pentagon(:hexagon);
--
-- TEST h3_get_icosahedron_faces
--
SELECT h3_get_icosahedron_faces('851c0047fffffff') = ARRAY[11,6];
SELECT h3_get_icosahedron_faces('851c004bfffffff') = ARRAY[6];
h3-pg-4.2.2/h3/test/sql/miscellaneous.sql 0000664 0000000 0000000 00000004272 14752347156 0020134 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set degs 90.45
\set rads 1.57865030842887
\set epsilon 0.000000001
\set edge '\'1180326b885fffff\'::h3index'
--
-- TEST h3_great_circle_distance
--
\set lyon POINT(4.8422, 45.7597)
\set paris POINT(2.3508, 48.8567)
SELECT h3_great_circle_distance(:lyon, :paris, 'rads') - 0.0615628186794217 < :epsilon;
SELECT h3_great_circle_distance(:lyon, :paris, 'm') - 392217.1598841777 < :epsilon;
SELECT h3_great_circle_distance(:lyon, :paris, 'km') - 392.21715988417765 < :epsilon;
-- test that 'km' is the default unit
SELECT h3_great_circle_distance(:lyon, :paris, 'km') = h3_great_circle_distance(:lyon, :paris);
--
-- TEST h3_get_hexagon_area_avg
--
SELECT abs(h3_get_hexagon_area_avg(10, 'm') - 15047.50190766437) < :epsilon;
SELECT abs(h3_get_hexagon_area_avg(10, 'km') - 0.01504750190766435) < :epsilon;
SELECT h3_get_hexagon_area_avg(10, 'km') = h3_get_hexagon_area_avg(10);
--
-- TEST h3_cell_area
--
\set expected_km2 0.01119834221989390
SELECT abs((h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10), 'm^2') / 1000000) - :expected_km2) < :epsilon;
SELECT abs(h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10), 'km^2') - :expected_km2) < :epsilon;
SELECT h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10), 'rads^2') > 0;
-- default is km^2
SELECT h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10), 'km^2') = h3_cell_area(h3_lat_lng_to_cell(POINT(0, 0), 10));
--
-- TEST h3_get_hexagon_edge_length_avg
--
SELECT h3_get_hexagon_edge_length_avg(10, 'm') - 75.86378287 < :epsilon;
SELECT h3_get_hexagon_edge_length_avg(10, 'km') - 0.075863783 < :epsilon;
SELECT h3_get_hexagon_edge_length_avg(10, 'km') = h3_get_hexagon_edge_length_avg(10);
--
-- TEST h3_edge_length
--
SELECT h3_edge_length(:edge, 'rads') > 0;
SELECT h3_edge_length(:edge, 'km') > h3_edge_length(:edge, 'rads');
SELECT h3_edge_length(:edge, 'm') > h3_edge_length(:edge, 'km');
SELECT h3_edge_length(:edge) = h3_edge_length(:edge, 'km');
--
-- TEST h3_get_num_cells
--
SELECT h3_get_num_cells(0) = 122;
SELECT h3_get_num_cells(15) = 569707381193162;
--
-- TEST h3_get_res_0_cells
--
SELECT COUNT(*) = 122 FROM (SELECT h3_get_res_0_cells()) q;
--
-- TEST h3_get_pentagons
--
SELECT COUNT(*) = 12 FROM (SELECT h3_get_pentagons(6)) q;
h3-pg-4.2.2/h3/test/sql/opclass_brin.sql 0000664 0000000 0000000 00000000577 14752347156 0017753 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set string '\'801dfffffffffff\''
\set hexagon ':string::h3index'
CREATE TABLE h3_test_brin (hex h3index PRIMARY KEY);
INSERT INTO h3_test_brin (hex) SELECT * FROM h3_get_res_0_cells();
CREATE INDEX h3_brin ON h3_test_brin USING brin (hex);
--
-- Test BRIN operator class
--
SELECT hex = :hexagon FROM (
SELECT hex FROM h3_test_brin WHERE hex = :hexagon
) q;
h3-pg-4.2.2/h3/test/sql/opclass_btree.sql 0000664 0000000 0000000 00000000611 14752347156 0020107 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set string '\'801dfffffffffff\''
\set hexagon ':string::h3index'
CREATE TABLE h3_test_btree (hex h3index PRIMARY KEY);
INSERT INTO h3_test_btree (hex) SELECT * from h3_get_res_0_cells();
CREATE INDEX h3_btree ON h3_test_btree USING btree (hex);
--
-- TEST b-tree operator class
--
SELECT hex = :hexagon FROM (
SELECT hex FROM h3_test_btree WHERE hex = :hexagon
) q;
h3-pg-4.2.2/h3/test/sql/opclass_hash.sql 0000664 0000000 0000000 00000000653 14752347156 0017737 0 ustar 00root root 0000000 0000000 \pset tuples_only on
CREATE TABLE h3_test_hash (hex h3index PRIMARY KEY);
INSERT INTO h3_test_hash (hex) SELECT * from h3_get_res_0_cells();
CREATE INDEX h3_btree ON h3_test_hash USING hash (hex);
--
-- TEST hash operator class
--
SELECT COUNT(hex) = 122 FROM (
SELECT hex FROM h3_test_hash WHERE hex IN (SELECT h3_get_res_0_cells())
) q;
CREATE TABLE h3_test_hash_part (hex h3index PRIMARY KEY) PARTITION BY HASH (hex); h3-pg-4.2.2/h3/test/sql/opclass_spgist.sql 0000664 0000000 0000000 00000001402 14752347156 0020316 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set hexagon '\'831c02fffffffff\'::h3index'
CREATE TABLE h3_test_spgist (hex h3index);
CREATE INDEX SPGIST_IDX ON h3_test_spgist USING spgist(hex h3index_ops_experimental);
INSERT INTO h3_test_spgist (hex) SELECT h3_cell_to_parent(:hexagon);
INSERT INTO h3_test_spgist (hex) SELECT h3_cell_to_children(:hexagon);
INSERT INTO h3_test_spgist (hex) SELECT h3_cell_to_center_child(:hexagon, 15);
--
-- TEST SP-GiST
--
SELECT COUNT(*) = 1 FROM h3_test_spgist WHERE hex @> :hexagon;
SELECT COUNT(*) = 8 FROM h3_test_spgist WHERE hex <@ :hexagon;
--
TRUNCATE TABLE h3_test_spgist;
INSERT INTO h3_test_spgist (hex) SELECT h3_cell_to_children(h3_cell_to_center_child(:hexagon, 10), 15);
SELECT COUNT(*) = 16807 FROM h3_test_spgist WHERE hex <@ :hexagon;
h3-pg-4.2.2/h3/test/sql/regions.sql 0000664 0000000 0000000 00000005022 14752347156 0016731 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- res 0 index
\set res0index '\'8059fffffffffff\''
-- center hex
\set center '\'81583ffffffffff\''
-- 7 child hexes in res 0 index
\set solid 'ARRAY(SELECT h3_cell_to_children(:res0index, 1))'
-- 6 child hexes in rim of res 0 index
\set hollow 'array_remove(:solid, :center)'
-- pentagon
\set pentagon '\'831c00fffffffff\'::h3index'
--
-- TEST h3_polygon_to_cells and h3_cells_to_multi_polygon
--
-- h3_polygon_to_cells is inverse of h3_cells_to_multi_polygon for set without holes
SELECT array_agg(result) is null FROM (
SELECT h3_polygon_to_cells(exterior, holes, 1) result FROM (
SELECT exterior, holes FROM h3_cells_to_multi_polygon(:solid)
) qq
EXCEPT SELECT unnest(:solid) result
) q;
-- h3_polygon_to_cells is inverse of h3_cells_to_multi_polygon for set with a hole
SELECT array_agg(result) is null FROM (
SELECT h3_polygon_to_cells(exterior, holes, 1) result FROM (
SELECT exterior, holes FROM h3_cells_to_multi_polygon(:hollow)
) qq
EXCEPT SELECT unnest(:hollow) result
) q;
-- h3_polyfill doesn't segfault on NULL value in holes
SELECT TRUE FROM (
SELECT h3_polygon_to_cells(exterior, ARRAY[NULL::POLYGON], 1) result FROM (
SELECT exterior, holes FROM h3_cells_to_multi_polygon(
ARRAY[:pentagon]::H3Index[]
)
) qq
) q LIMIT 1;
-- h3_polyfill throws on non-polygons
CREATE FUNCTION h3_test_polyfill_bad1() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_polyfill(ST_GeomFromText('POINT(-71.160281 42.258729)',4326), 8);
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
CREATE FUNCTION h3_test_polyfill_bad2() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_polyfill(ST_GeomFromText('LINESTRING(-71.160281 42.258729,-71.160837 42.259113,-71.161144 42.25932)',4326), 8);
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
SELECT h3_test_polyfill_bad1();
SELECT h3_test_polyfill_bad2();
DROP FUNCTION h3_test_polyfill_bad1;
DROP FUNCTION h3_test_polyfill_bad2;
--
-- TEST h3_polygon_to_cells_experimental
--
-- h3_polygon_to_cells is inverse of h3_cells_to_multi_polygon for set without holes
SELECT array_agg(result) is null FROM (
SELECT h3_polygon_to_cells_experimental(exterior, holes, 1, 'overlapping') result FROM (
SELECT exterior, holes FROM h3_cells_to_multi_polygon(:solid)
) qq
EXCEPT SELECT h3_grid_disk(h3_cell_to_center_child(:res0index), 2) result
) q;
h3-pg-4.2.2/h3/test/sql/traversal.sql 0000664 0000000 0000000 00000004016 14752347156 0017270 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set hexagon '\'880326b88dfffff\''
\set origin '\'880326b887fffff\''
\set pentagon '\'831c00fffffffff\''
--
-- TEST h3_grid_disk and h3_grid_ring_unsafe
--
-- gridDisk 0 is input index
SELECT h3_grid_disk(:hexagon, 0) = :hexagon;
-- gridDisk 2 is same as sum of gridRing 0, 1 and 2
SELECT array_agg(r) is null FROM (
SELECT h3_grid_disk(:hexagon, 2) r
EXCEPT (
SELECT h3_grid_ring_unsafe(:hexagon, 0) r
UNION SELECT h3_grid_ring_unsafe(:hexagon, 1) r
UNION SELECT h3_grid_ring_unsafe(:hexagon, 2) r
)
) q;
--
-- TEST h3_grid_disk_distances
--
-- correct number of indexes at distances 0, 1 and 2 for k=2
SELECT COUNT(index) filter (WHERE distance = 0) = 1
AND COUNT(index) filter (WHERE distance = 1) = 6
AND COUNT(index) filter (WHERE distance = 2) = 12
FROM (
SELECT index, distance FROM h3_grid_disk_distances(:hexagon, 2)
) q;
-- same for pentagon
SELECT COUNT(index) filter (WHERE distance = 0) = 1
AND COUNT(index) filter (WHERE distance = 1) = 5
AND COUNT(index) filter (WHERE distance = 2) = 10
FROM (
SELECT index, distance FROM h3_grid_disk_distances(:pentagon, 2)
) q;
--
-- TEST h3_grid_path_cells
--
SELECT ARRAY(SELECT h3_grid_path_cells('841c023ffffffff', '841c025ffffffff'))
= ARRAY['841c023ffffffff','841c027ffffffff','841c025ffffffff']::h3index[];
--
-- TEST h3_grid_distance
--
-- returns 1 for indexes with one index between them
SELECT h3_grid_distance('880326b881fffff', '880326b885fffff') = 1;
-- throws for invalid inputs
CREATE FUNCTION h3_test_grid_distance_invalid() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_grid_distance('880326b881fffff', h3_cell_to_parent('880326b885fffff')) = -1;
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
SELECT h3_test_grid_distance_invalid();
--
-- TEST h3_cell_to_local_ij and h3_local_ij_to_cell
--
-- they are inverse of each others
SELECT :hexagon = h3_local_ij_to_cell(:origin, h3_cell_to_local_ij(:origin, :hexagon)); h3-pg-4.2.2/h3/test/sql/type.sql 0000664 0000000 0000000 00000002245 14752347156 0016250 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set string '\'801dfffffffffff\''
\set asbigint 576988517884755967
\set hexagon ':string::h3index'
\set pentagon '\'844c001ffffffff\'::h3index'
--
-- TEST operators
--
SELECT :hexagon = :hexagon;
SELECT NOT :hexagon = :pentagon;
SELECT NOT :hexagon <> :hexagon;
SELECT :hexagon <> :pentagon;
SELECT :pentagon <@ h3_cell_to_parent(:pentagon);
SELECT bool_and(:pentagon @> c) FROM (
SELECT h3_cell_to_children(:pentagon) c
) q;
--
-- TEST bigint casting
--
SELECT :asbigint = :hexagon::bigint;
SELECT :hexagon = :asbigint::h3index;
--
-- TEST binary io
--
CREATE TEMPORARY TABLE h3_test_binary_send (hex h3index PRIMARY KEY);
CREATE TEMPORARY TABLE h3_test_binary_recv (hex h3index PRIMARY KEY);
INSERT INTO h3_test_binary_send (hex) SELECT * from h3_get_res_0_cells();
-- we need to use \copy instead of SQL COPY in order to have relative path
\copy h3_test_binary_send TO 'h3_test_binary.bin' (FORMAT binary)
\copy h3_test_binary_recv FROM 'h3_test_binary.bin' (FORMAT binary)
-- make sure re-imported data matches original data
SELECT array_agg(hex) is null FROM (
SELECT hex FROM h3_test_binary_send
EXCEPT SELECT hex FROM h3_test_binary_recv
) q;
h3-pg-4.2.2/h3/test/sql/vertex.sql 0000664 0000000 0000000 00000001245 14752347156 0016603 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set hexagon '\'880326b885fffff\'::h3index'
\set pentagon '\'831c00fffffffff\'::h3index'
\set vertex2 '\'2280326b885fffff\'::h3index'
\set geo POINT(65.60200108645547,89.57740563247555)
--
-- TEST h3_cell_to_vertex
--
SELECT h3_cell_to_vertex(:hexagon, 2) = :vertex2;
--
-- TEST h3_cell_to_vertexes
--
SELECT COUNT(*) = 6 FROM (
SELECT h3_cell_to_vertexes(:hexagon)
) q;
SELECT COUNT(*) = 5 FROM (
SELECT h3_cell_to_vertexes(:pentagon)
) q;
--
-- TEST h3_vertex_to_lat_lng
--
SELECT h3_vertex_to_lat_lng(:vertex2) ~= :geo;
--
-- TEST h3_is_valid_vertex and
--
SELECT h3_is_valid_vertex(:vertex2);
SELECT NOT h3_is_valid_vertex(:hexagon); h3-pg-4.2.2/h3_postgis/ 0000775 0000000 0000000 00000000000 14752347156 0014535 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3_postgis/CMakeLists.txt 0000664 0000000 0000000 00000002530 14752347156 0017275 0 ustar 00root root 0000000 0000000 PostgreSQL_add_extension(postgresql_h3_postgis
RELOCATABLE
NAME h3_postgis
COMMENT "H3 PostGIS integration"
VERSION ${INSTALL_VERSION}
COMPONENT ${PROJECT_NAME}
REQUIRES
h3
postgis
postgis_raster
SOURCES
src/init.c
src/wkb_bbox3.c
src/wkb_indexing.c
src/wkb_linked_geo.c
src/wkb_regions.c
src/wkb_split.c
src/wkb_vect3.c
src/wkb.c
INSTALLS
sql/install/01-indexing.sql
sql/install/03-traversal.sql
sql/install/05-regions.sql
sql/install/10-operators.sql
sql/install/20-casts.sql
sql/install/30-wkb.sql
sql/install/40-rasters.sql
sql/install/99-deprecated.sql
UPDATES
sql/updates/h3_postgis--4.0.0.sql
sql/updates/h3_postgis--4.0.0--4.0.1.sql
sql/updates/h3_postgis--4.0.1--4.0.2.sql
sql/updates/h3_postgis--4.0.2--4.0.3.sql
sql/updates/h3_postgis--4.0.3--4.1.0.sql
sql/updates/h3_postgis--4.1.0--4.1.1.sql
sql/updates/h3_postgis--4.1.1--4.1.2.sql
sql/updates/h3_postgis--4.1.2--4.1.3.sql
sql/updates/h3_postgis--4.1.3--4.1.4.sql
sql/updates/h3_postgis--4.1.4--4.2.0.sql
sql/updates/h3_postgis--4.2.0--4.2.1.sql
sql/updates/h3_postgis--4.2.1--4.2.2.sql
)
# link
target_link_libraries(postgresql_h3_postgis PRIVATE postgresql_h3_shared h3)
# test
if(BUILD_TESTING AND PostgreSQL_PostGIS_FOUND)
add_subdirectory(test)
endif()
h3-pg-4.2.2/h3_postgis/sql/ 0000775 0000000 0000000 00000000000 14752347156 0015334 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3_postgis/sql/install/ 0000775 0000000 0000000 00000000000 14752347156 0017002 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3_postgis/sql/install/01-indexing.sql 0000664 0000000 0000000 00000006333 14752347156 0021553 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| The `GEOMETRY` data passed to `h3-pg` PostGIS functions should
--| be in SRID 4326. This is an expectation of the core H3 library.
--| Using other SRIDs, such as 3857, can result in either errors or
--| invalid data depending on the function.
--| For example, the `h3_polygon_to_cells()` function will fail with
--| an error in this scenario while the `h3_lat_lng_to_cell()` function
--| will return an invalid geometry.
--| # PostGIS Indexing Functions
--@ availability: 4.0.0
--@ refid: h3_lat_lng_to_cell_geometry
CREATE OR REPLACE FUNCTION h3_lat_lng_to_cell(geometry, resolution integer) RETURNS h3index
AS $$ SELECT h3_lat_lng_to_cell($1::point, $2); $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
COMMENT ON FUNCTION
h3_lat_lng_to_cell(geometry, resolution integer)
IS 'Indexes the location at the specified resolution.';
--@ availability: 4.0.0
--@ refid: h3_lat_lng_to_cell_geography
CREATE OR REPLACE FUNCTION h3_lat_lng_to_cell(geography, resolution integer) RETURNS h3index
AS $$ SELECT h3_lat_lng_to_cell($1::geometry, $2); $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
COMMENT ON FUNCTION
h3_lat_lng_to_cell(geometry, resolution integer)
IS 'Indexes the location at the specified resolution.';
--@ availability: 4.0.0
--@ refid: h3_cell_to_geometry
CREATE OR REPLACE FUNCTION h3_cell_to_geometry(h3index) RETURNS geometry
AS $$ SELECT ST_SetSRID(h3_cell_to_lat_lng($1)::geometry, 4326) $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
COMMENT ON FUNCTION
h3_cell_to_geometry(h3index)
IS 'Finds the centroid of the index.';
--@ availability: 4.0.0
--@ refid: h3_cell_to_geography
CREATE OR REPLACE FUNCTION h3_cell_to_geography(h3index) RETURNS geography
AS $$ SELECT h3_cell_to_geometry($1)::geography $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
COMMENT ON FUNCTION
h3_cell_to_geography(h3index)
IS 'Finds the centroid of the index.';
--@ availability: 4.0.0
--@ refid: h3_cell_to_boundary_geometry
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geometry(h3index) RETURNS geometry
AS $$ SELECT h3_cell_to_boundary_wkb($1)::geometry $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
COMMENT ON FUNCTION
h3_cell_to_boundary_geometry(h3index)
IS 'Finds the boundary of the index.
Splits polygons when crossing 180th meridian.';
--@ availability: 4.0.0
--@ refid: h3_cell_to_boundary_geography
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geography(h3index) RETURNS geography
AS $$ SELECT h3_cell_to_boundary_wkb($1)::geography $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
COMMENT ON FUNCTION
h3_cell_to_boundary_geography(h3index)
IS 'Finds the boundary of the index.
Splits polygons when crossing 180th meridian.';
h3-pg-4.2.2/h3_postgis/sql/install/03-traversal.sql 0000664 0000000 0000000 00000004174 14752347156 0021754 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # PostGIS Grid Traversal Functions
--@ availability: 4.1.0
--@ refid: h3_grid_path_cells_recursive
CREATE OR REPLACE FUNCTION
h3_grid_path_cells_recursive(origin h3index, destination h3index) RETURNS SETOF h3index
AS $$
BEGIN
IF (SELECT
origin != destination
AND NOT h3_are_neighbor_cells(origin, destination)
AND ((base1 != base2 AND NOT h3_are_neighbor_cells(base1, base2))
OR ((h3_is_pentagon(base1) OR h3_is_pentagon(base2))
AND NOT (
h3_get_icosahedron_faces(origin)
&& h3_get_icosahedron_faces(destination))))
FROM (
SELECT
h3_cell_to_parent(origin, 0) AS base1,
h3_cell_to_parent(destination, 0) AS base2) AS t)
THEN
RETURN QUERY WITH
points AS (
SELECT
h3_cell_to_geometry(origin) AS g1,
h3_cell_to_geometry(destination) AS g2),
cells AS (
SELECT
h3_lat_lng_to_cell(
ST_Centroid(ST_MakeLine(g1, g2)::geography),
h3_get_resolution(origin)) AS middle
FROM points)
SELECT h3_grid_path_cells_recursive(origin, middle) FROM cells
UNION
SELECT h3_grid_path_cells_recursive(middle, destination) FROM cells;
ELSE
RETURN QUERY SELECT h3_grid_path_cells(origin, destination);
END IF;
END;
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL SAFE;
h3-pg-4.2.2/h3_postgis/sql/install/05-regions.sql 0000664 0000000 0000000 00000010534 14752347156 0021416 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # PostGIS Region Functions
--@ availability: 4.0.0
--@ refid: h3_polygon_to_cells_geometry
CREATE OR REPLACE FUNCTION h3_polygon_to_cells(multi geometry, resolution integer) RETURNS SETOF h3index
AS $$ SELECT h3_polygon_to_cells(exterior, holes, resolution) FROM (
SELECT
-- extract exterior ring of each polygon
ST_MakePolygon(ST_ExteriorRing(poly))::polygon exterior,
-- extract holes of each polygon
(SELECT array_agg(hole)
FROM (
SELECT ST_MakePolygon(ST_InteriorRingN(
poly,
generate_series(1, ST_NumInteriorRings(poly))
))::polygon AS hole
) q_hole
) holes
-- extract single polygons from multipolygon
FROM (
select (st_dump(multi)).geom as poly
) q_poly GROUP BY poly
) h3_polygon_to_cells; $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
--@ availability: 4.0.0
--@ refid: h3_polygon_to_cells_geography
CREATE OR REPLACE FUNCTION h3_polygon_to_cells(multi geography, resolution integer) RETURNS SETOF h3index
AS $$ SELECT h3_polygon_to_cells($1::geometry, $2) $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
--@ availability: 4.1.0
--@ refid: h3_cells_to_multi_polygon_geometry
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon_geometry(h3index[]) RETURNS geometry
AS $$ SELECT h3_cells_to_multi_polygon_wkb($1)::geometry $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
--@ availability: 4.1.0
--@ refid: h3_cells_to_multi_polygon_geography
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon_geography(h3index[]) RETURNS geography
AS $$ SELECT h3_cells_to_multi_polygon_wkb($1)::geography $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
--@ availability: 4.1.0
--@ refid: h3_cells_to_multi_polygon_geometry_agg
CREATE AGGREGATE h3_cells_to_multi_polygon_geometry(h3index) (
sfunc = array_append,
stype = h3index[],
finalfunc = h3_cells_to_multi_polygon_geometry,
parallel = safe
);
--@ availability: 4.1.0
--@ refid: h3_cells_to_multi_polygon_geography_agg
CREATE AGGREGATE h3_cells_to_multi_polygon_geography(h3index) (
sfunc = array_append,
stype = h3index[],
finalfunc = h3_cells_to_multi_polygon_geography,
parallel = safe
);
--@ availability: 4.2.0
--@ refid: h3_polygon_to_cells_geometry_experimental
CREATE OR REPLACE FUNCTION h3_polygon_to_cells_experimental(multi geometry, resolution integer, containment_mode text DEFAULT 'center') RETURNS SETOF h3index
AS $$ SELECT h3_polygon_to_cells_experimental(exterior, holes, resolution, containment_mode) FROM (
SELECT
-- extract exterior ring of each polygon
ST_MakePolygon(ST_ExteriorRing(poly))::polygon exterior,
-- extract holes of each polygon
(SELECT array_agg(hole)
FROM (
SELECT ST_MakePolygon(ST_InteriorRingN(
poly,
generate_series(1, ST_NumInteriorRings(poly))
))::polygon AS hole
) q_hole
) holes
-- extract single polygons from multipolygon
FROM (
select (st_dump(multi)).geom as poly
) q_poly GROUP BY poly
) h3_polygon_to_cells; $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
--@ availability: 4.2.0
--@ refid: h3_polygon_to_cells_geography_experimental
CREATE OR REPLACE FUNCTION h3_polygon_to_cells_experimental(multi geography, resolution integer, containment_mode text DEFAULT 'center') RETURNS SETOF h3index
AS $$ SELECT h3_polygon_to_cells_experimental($1::geometry, $2, $3) $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
h3-pg-4.2.2/h3_postgis/sql/install/10-operators.sql 0000664 0000000 0000000 00000002050 14752347156 0021754 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # PostGIS Operators
--@ availability: 4.1.3
CREATE OPERATOR @ (
PROCEDURE = h3_lat_lng_to_cell,
LEFTARG = geometry, RIGHTARG = integer
);
COMMENT ON OPERATOR @ (geometry, integer) IS
'Index geometry at specified resolution.';
--@ availability: 4.1.3
CREATE OPERATOR @ (
PROCEDURE = h3_lat_lng_to_cell,
LEFTARG = geography, RIGHTARG = integer
);
COMMENT ON OPERATOR @ (geography, integer) IS
'Index geography at specified resolution.';
h3-pg-4.2.2/h3_postgis/sql/install/20-casts.sql 0000664 0000000 0000000 00000001616 14752347156 0021063 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- ---------- ---------- ---------- ---------- ---------- ---------- ----------
--| # PostGIS casts
--@ availability: 0.3.0
CREATE CAST (h3index AS geometry) WITH FUNCTION h3_cell_to_geometry(h3index);
--@ availability: 0.3.0
CREATE CAST (h3index AS geography) WITH FUNCTION h3_cell_to_geography(h3index);
h3-pg-4.2.2/h3_postgis/sql/install/30-wkb.sql 0000664 0000000 0000000 00000002654 14752347156 0020535 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # WKB indexing functions
--@ availability: 4.1.0
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary_wkb(cell h3index) RETURNS bytea
AS 'h3_postgis' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary_wkb(h3index)
IS 'Finds the boundary of the index, converts to EWKB.
Splits polygons when crossing 180th meridian.
This function has to return WKB since Postgres does not provide multipolygon type.';
--| # WKB regions functions
--@ availability: 4.1.0
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon_wkb(h3index[]) RETURNS bytea
AS 'h3_postgis' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cells_to_multi_polygon_wkb(h3index[])
IS 'Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons, converts to EWKB.
Splits polygons when crossing 180th meridian.';
h3-pg-4.2.2/h3_postgis/sql/install/40-rasters.sql 0000664 0000000 0000000 00000060526 14752347156 0021440 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--| # Raster processing functions
-- Get nodata value for ST_Clip function
-- ST_Clip sets nodata pixel values to minimum value by default, but it won't
-- set band nodata value in this case, which we need later for filtering dumped
-- values.
CREATE OR REPLACE FUNCTION __h3_raster_band_nodata(
rast raster,
nband integer)
RETURNS double precision
AS $$
SELECT coalesce(
ST_BandNoDataValue(rast, nband),
ST_MinPossibleValue(ST_BandPixelType(rast, nband)));
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_to_polygon(
rast raster,
nband integer)
RETURNS geometry
AS $$
SELECT ST_MinConvexHull(rast, nband);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
-- Area of a pixel close to the center of raster polygon, in meters
CREATE OR REPLACE FUNCTION __h3_raster_polygon_pixel_area(
rast raster,
poly geometry)
RETURNS double precision
AS $$
SELECT ST_Area(
ST_Transform(
ST_PixelAsPolygon(
rast,
ST_WorldToRasterCoordX(rast, c),
ST_WorldToRasterCoordY(rast, c)),
4326)::geography)
FROM ST_Centroid(poly) AS c
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_centroid_cell(
poly geometry,
resolution integer)
RETURNS h3index
AS $$
DECLARE
cell h3index := h3_lat_lng_to_cell(ST_Transform(ST_Centroid(poly), 4326), resolution);
BEGIN
IF h3_is_pentagon(cell) THEN
SELECT h3 INTO cell FROM h3_grid_disk(cell) AS h3 WHERE h3 != cell LIMIT 1;
END IF;
RETURN cell;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
-- Area of a cell close to the center of raster polygon, in meters
CREATE OR REPLACE FUNCTION __h3_raster_polygon_centroid_cell_area(
poly geometry,
resolution integer)
RETURNS double precision
AS $$
SELECT ST_Area(
h3_cell_to_boundary_geography(
__h3_raster_polygon_centroid_cell(poly, resolution)));
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
-- Get list of cells inside of the raster polygon,
-- buffered by `buffer` value (in meters).
-- If SRID != 4326 then additionally buffer by 1 pixel to account for transformation.
CREATE OR REPLACE FUNCTION __h3_raster_polygon_to_cells(
rast raster,
poly geometry,
resolution integer,
buffer double precision)
RETURNS SETOF h3index
AS $$
DECLARE
buffered geometry := poly;
BEGIN
IF ST_SRID(rast) != 4326 THEN
buffered := ST_Transform(
ST_Buffer(
poly,
greatest(ST_PixelWidth(rast), ST_PixelHeight(rast)),
'join=mitre'),
4326);
END IF;
IF buffer > 0.0 THEN
RETURN QUERY
SELECT h3_polygon_to_cells(
ST_Buffer(
buffered::geography,
buffer,
'join=mitre'),
resolution);
ELSE
RETURN QUERY
SELECT h3_polygon_to_cells(buffered, resolution);
END IF;
END
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
-- Get geometries of H3 cells interesecting raster polygon.
CREATE OR REPLACE FUNCTION __h3_raster_polygon_to_cell_boundaries_intersects(
rast raster,
poly geometry,
resolution integer)
RETURNS TABLE (h3 h3index, geom geometry)
AS $$
SELECT h3, geom
FROM
__h3_raster_polygon_to_cells(
rast,
poly,
resolution,
h3_get_hexagon_edge_length_avg(resolution, 'm') * 1.3
) AS h3,
ST_Transform(h3_cell_to_boundary_geometry(h3), ST_SRID(rast)) AS geom
WHERE ST_Intersects(geom, poly);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
-- Get raster coordinates of H3 cells with centroids inside the raster polygon
CREATE OR REPLACE FUNCTION __h3_raster_polygon_to_cell_coords_centroid(
rast raster,
poly geometry,
resolution integer)
RETURNS TABLE (h3 h3index, x integer, y integer)
AS $$
WITH
geoms AS (
SELECT
h3,
ST_Transform(
h3_cell_to_geometry(h3),
ST_SRID(poly)
) AS geom
FROM (
SELECT __h3_raster_polygon_to_cells(rast, poly, resolution, 0.0) AS h3
) t),
coords AS (
SELECT
h3,
ST_WorldToRasterCoordX(rast, geom) AS x,
ST_WorldToRasterCoordY(rast, geom) AS y
FROM geoms)
SELECT h3, x, y
FROM coords
WHERE
x BETWEEN 1 AND ST_Width(rast)
AND y BETWEEN 1 AND ST_Height(rast);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_to_cell_parts(
rast raster,
poly geometry,
resolution integer,
nband integer)
RETURNS TABLE (h3 h3index, part raster)
AS $$
DECLARE
nodata CONSTANT double precision := __h3_raster_band_nodata(rast, nband);
BEGIN
RETURN QUERY
SELECT c.h3, p AS part
FROM
__h3_raster_polygon_to_cell_boundaries_intersects(rast, poly, resolution) AS c,
ST_Clip(rast, nband, c.geom, nodata, TRUE) AS p
WHERE NOT ST_BandIsNoData(p, nband);
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
-- Get values corresponding to all H3 cells with centroids inside the
-- raster polygon. Assumes cell area is less than pixel area.
CREATE OR REPLACE FUNCTION __h3_raster_polygon_subpixel_cell_values(
rast raster,
poly geometry,
resolution integer,
nband integer)
RETURNS TABLE (h3 h3index, val double precision)
AS $$
SELECT
h3,
ST_Value(rast, nband, x, y) AS val
FROM __h3_raster_polygon_to_cell_coords_centroid(rast, poly, resolution);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--| ## Continuous raster data
--|
--| For rasters with pixel values representing continuous data (temperature, humidity,
--| elevation), the data inside H3 cells can be summarized by calculating number of
--| pixels, sum, mean, standard deviation, min and max for each cell inside a raster
--| and grouping these stats across multiple rasters by H3 index.
--|
--| ```
--| SELECT
--| (summary).h3 AS h3,
--| (h3_raster_summary_stats_agg((summary).stats)).*
--| FROM (
--| SELECT h3_raster_summary(rast, 8) AS summary
--| FROM rasters
--| ) t
--| GROUP BY 1;
--|
--| h3 | count | sum | mean | stddev | min | max
--| -----------------+-------+--------------------+---------------------+--------------------+-------+------------------
--| 882d638189fffff | 10 | 4.607657432556152 | 0.46076574325561526 | 1.3822972297668457 | 0 | 4.607657432556152
--| 882d64c4d1fffff | 10 | 3.6940908953547478 | 0.3694090895354748 | 1.099336879464068 | 0 | 3.667332887649536
--| 882d607431fffff | 11 | 6.219290263950825 | 0.5653900239955295 | 1.7624673707119065 | 0 | 6.13831996917724
--| <...>
--| ```
-- NOTE: `count` can be < 1 when cell area is less than pixel area
--@ availability: 4.1.1
CREATE TYPE h3_raster_summary_stats AS (
count double precision,
sum double precision,
mean double precision,
stddev double precision,
min double precision,
max double precision
);
-- ST_SummaryStats result type to h3_raster_summary_stats
CREATE OR REPLACE FUNCTION __h3_raster_to_summary_stats(stats summarystats)
RETURNS h3_raster_summary_stats
AS $$
SELECT ROW(
(stats).count,
(stats).sum,
(stats).mean,
(stats).stddev,
(stats).min,
(stats).max
)::h3_raster_summary_stats
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_summary_stats_agg_transfn(
s1 h3_raster_summary_stats,
s2 h3_raster_summary_stats)
RETURNS h3_raster_summary_stats
AS $$
WITH total AS (
SELECT
(s1).count + (s2).count AS count,
(s1).sum + (s2).sum AS sum)
SELECT ROW(
t.count,
t.sum,
t.sum / t.count,
sqrt(
(
-- sum of squared values: (variance + mean squared) * count
(((s1).stddev * (s1).stddev + (s1).mean * (s1).mean)) * (s1).count
+ (((s2).stddev * (s2).stddev + (s2).mean * (s2).mean)) * (s2).count
)
/ t.count
- ((t.sum * t.sum) / (t.count * t.count)) -- mean squared
),
least((s1).min, (s2).min),
greatest((s1).max, (s2).max)
)::h3_raster_summary_stats
FROM total AS t
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
--@ availability: 4.1.1
CREATE AGGREGATE h3_raster_summary_stats_agg(h3_raster_summary_stats) (
sfunc = __h3_raster_summary_stats_agg_transfn,
stype = h3_raster_summary_stats,
parallel = safe
);
CREATE OR REPLACE FUNCTION __h3_raster_polygon_summary_clip(
rast raster,
poly geometry,
resolution integer,
nband integer)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
SELECT
h3,
__h3_raster_to_summary_stats(ST_SummaryStats(part, nband, TRUE)) AS stats
FROM __h3_raster_polygon_to_cell_parts(rast, poly, resolution, nband);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--@ availability: 4.1.1
CREATE OR REPLACE FUNCTION h3_raster_summary_clip(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
SELECT __h3_raster_polygon_summary_clip(
rast,
__h3_raster_to_polygon(rast, nband),
resolution,
nband);
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_summary_clip(raster, integer, integer)
IS 'Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Clips the raster by H3 cell geometries and processes each part separately.';
--@ availability: 4.1.1
CREATE OR REPLACE FUNCTION h3_raster_summary_centroids(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
SELECT
h3_lat_lng_to_cell(ST_Transform(geom, 4326), resolution) AS h3,
ROW(
count(val),
sum(val),
avg(val),
stddev_pop(val),
min(val),
max(val)
)::h3_raster_summary_stats AS stats
FROM ST_PixelAsCentroids(rast, nband)
GROUP BY 1;
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_summary_centroids(raster, integer, integer)
IS 'Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Finds corresponding H3 cell for each pixel, then groups values by H3 index.';
CREATE OR REPLACE FUNCTION __h3_raster_polygon_summary_subpixel(
rast raster,
poly geometry,
resolution integer,
nband integer,
pixels_per_cell double precision)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
SELECT
h3,
ROW(
pixels_per_cell, -- count
val, -- sum
val, -- mean
0.0, -- stddev
val, -- min
val -- max
)::h3_raster_summary_stats AS stats
FROM __h3_raster_polygon_subpixel_cell_values(rast, poly, resolution, nband) t;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--@ availability: 4.1.1
CREATE OR REPLACE FUNCTION h3_raster_summary_subpixel(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
pixel_area CONSTANT double precision := __h3_raster_polygon_pixel_area(rast, poly);
cell_area CONSTANT double precision := __h3_raster_polygon_centroid_cell_area(poly, resolution);
BEGIN
RETURN QUERY SELECT (__h3_raster_polygon_summary_subpixel(
rast,
poly,
resolution,
nband,
cell_area / pixel_area)).*;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_summary_subpixel(raster, integer, integer)
IS 'Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Assumes H3 cell is smaller than a pixel. Finds corresponding pixel for each H3 cell in raster.';
--@ availability: 4.1.1
CREATE OR REPLACE FUNCTION h3_raster_summary(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
cell_area CONSTANT double precision := __h3_raster_polygon_centroid_cell_area(poly, resolution);
pixel_area CONSTANT double precision := __h3_raster_polygon_pixel_area(rast, poly);
pixels_per_cell CONSTANT double precision := cell_area / pixel_area;
BEGIN
IF pixels_per_cell > 70
AND (ST_Area(ST_Transform(poly, 4326)::geography) / cell_area) > 10000 / (pixels_per_cell - 70)
THEN
RETURN QUERY SELECT (__h3_raster_polygon_summary_clip(
rast,
poly,
resolution,
nband
)).*;
ELSIF pixels_per_cell > 1 THEN
RETURN QUERY SELECT (h3_raster_summary_centroids(
rast,
resolution,
nband
)).*;
ELSE
RETURN QUERY SELECT (__h3_raster_polygon_summary_subpixel(
rast,
poly,
resolution,
nband,
pixels_per_cell
)).*;
END IF;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_summary(raster, integer, integer)
IS 'Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Attempts to select an appropriate method based on number of pixels per H3 cell.';
--| ## Discrete raster data
--|
--| For rasters where pixels have discrete values corresponding to different classes
--| of land cover or land use, H3 cell data summary can be represented by a JSON object
--| with separate fields for each class. First, value, number of pixels and approximate
--| area are calculated for each H3 cell and value in a raster, then the stats are
--| grouped across multiple rasters by H3 index and value, and after that stats for
--| different values in a cell are combined into a single JSON object.
--|
--| The following example query additionally calculates a fraction of H3 cell pixels
--| for each value, using a window function to get a total number of pixels:
--| ```
--| WITH
--| summary AS (
--| -- get aggregated summary for each H3 index/value pair
--| SELECT h3, val, h3_raster_class_summary_item_agg(summary) AS item
--| FROM
--| rasters,
--| h3_raster_class_summary(rast, 8)
--| GROUP BY 1, 2),
--| summary_total AS (
--| -- add total number of pixels per H3 cell
--| SELECT h3, val, item, sum((item).count) OVER (PARTITION BY h3) AS total
--| FROM summary)
--| SELECT
--| h3,
--| jsonb_object_agg(
--| concat('class_', val::text),
--| h3_raster_class_summary_item_to_jsonb(item) -- val, count, area
--| || jsonb_build_object('fraction', (item).count / total) -- add fraction value
--| ORDER BY val
--| ) AS summary
--| FROM summary_total
--| GROUP BY 1;
--|
--| h3 | summary
--| ----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------
--| 88194e6f3bfffff | {"class_1": {"area": 75855.5748, "count": 46, "value": 1, "fraction": 0.4509}, "class_2": {"area": 92345.9171, "count": 56, "value": 2, "fraction": 0.5490}}
--| 88194e6f37fffff | {"class_1": {"area": 255600.3064, "count": 155, "value": 1, "fraction": 0.5}, "class_2": {"area": 255600.3064, "count": 155, "value": 2, "fraction": 0.5}}
--| 88194e6f33fffff | {"class_1": {"area": 336402.9840, "count": 204, "value": 1, "fraction": 0.5125}, "class_2": {"area": 319912.6416, "count": 194, "value": 2, "fraction": 0.4874}}
--| <...>
--| ```
--|
--| Area covered by pixels with the most frequent value in each cell:
--| ```
--| SELECT DISTINCT ON (h3)
--| h3, val, (item).area
--| FROM (
--| SELECT
--| h3, val, h3_raster_class_summary_item_agg(summary) AS item
--| FROM
--| rasters,
--| h3_raster_class_summary(rast, 8)
--| GROUP BY 1, 2
--| ) t
--| ORDER BY h3, (item).count DESC;
--|
--| h3 | val | area
--| -----------------+-----+--------------------
--| 88194e6f3bfffff | 5 | 23238.699360251427
--| 88194e6f37fffff | 9 | 60863.26022922993
--| 88194e6f33fffff | 8 | 76355.72646939754
--| <...>
--| ```
-- NOTE: `count` can be < 1 when cell area is less than pixel area
--@ availability: 4.1.1
CREATE TYPE h3_raster_class_summary_item AS (
val integer,
count double precision,
area double precision
);
--@ availability: 4.1.1
CREATE OR REPLACE FUNCTION h3_raster_class_summary_item_to_jsonb(
item h3_raster_class_summary_item)
RETURNS jsonb
AS $$
SELECT jsonb_build_object(
'value', (item).val,
'count', (item).count,
'area', (item).area
);
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_class_summary_item_to_jsonb(h3_raster_class_summary_item)
IS 'Convert raster summary to JSONB, example: `{"count": 10, "value": 2, "area": 16490.3423}`';
CREATE OR REPLACE FUNCTION __h3_raster_class_summary_item_agg_transfn(
s1 h3_raster_class_summary_item,
s2 h3_raster_class_summary_item)
RETURNS h3_raster_class_summary_item
AS $$
SELECT
s1.val,
s1.count + s2.count,
s1.area + s2.area;
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
--@ availability: 4.1.1
CREATE AGGREGATE h3_raster_class_summary_item_agg(h3_raster_class_summary_item) (
stype = h3_raster_class_summary_item,
sfunc = __h3_raster_class_summary_item_agg_transfn,
parallel = safe
);
CREATE OR REPLACE FUNCTION __h3_raster_class_summary_part(
rast raster,
nband integer,
pixel_area double precision)
RETURNS SETOF h3_raster_class_summary_item
AS $$
SELECT ROW(
value::integer,
count::double precision,
count * pixel_area
)::h3_raster_class_summary_item
FROM ST_ValueCount(rast, nband) t;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_class_polygon_summary_clip(
rast raster,
poly geometry,
resolution integer,
nband integer,
pixel_area double precision)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
WITH
summary AS (
SELECT
h3,
__h3_raster_class_summary_part(part, nband, pixel_area) AS summary
FROM __h3_raster_polygon_to_cell_parts(rast, poly, resolution, nband) t)
SELECT h3, (summary).val, summary
FROM summary;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--@ availability: 4.1.1
CREATE OR REPLACE FUNCTION h3_raster_class_summary_clip(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
BEGIN
RETURN QUERY SELECT (__h3_raster_class_polygon_summary_clip(
rast,
poly,
resolution,
nband,
__h3_raster_polygon_pixel_area(rast, poly)
)).*;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_class_summary_clip(raster, integer, integer)
IS 'Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Clips the raster by H3 cell geometries and processes each part separately.';
CREATE OR REPLACE FUNCTION __h3_raster_class_summary_centroids(
rast raster,
resolution integer,
nband integer,
pixel_area double precision)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
SELECT
h3_lat_lng_to_cell(ST_Transform(geom, 4326), resolution) AS h3,
val::integer AS val,
ROW(
val::integer,
count(*)::double precision,
count(*) * pixel_area
)::h3_raster_class_summary_item AS summary
FROM ST_PixelAsCentroids(rast, nband)
GROUP BY 1, 2;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--@ availability: 4.1.1
CREATE OR REPLACE FUNCTION h3_raster_class_summary_centroids(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
SELECT __h3_raster_class_summary_centroids(
rast,
resolution,
nband,
__h3_raster_polygon_pixel_area(rast, __h3_raster_to_polygon(rast, nband))
);
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_class_summary_centroids(raster, integer, integer)
IS 'Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Finds corresponding H3 cell for each pixel, then groups by H3 and value.';
CREATE OR REPLACE FUNCTION __h3_raster_class_polygon_summary_subpixel(
rast raster,
poly geometry,
resolution integer,
nband integer,
cell_area double precision,
pixel_area double precision)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
SELECT
h3,
val::integer AS val,
ROW(
val::integer,
cell_area / pixel_area,
cell_area
)::h3_raster_class_summary_item AS summary
FROM __h3_raster_polygon_subpixel_cell_values(rast, poly, resolution, nband);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--@ availability: 4.1.1
CREATE OR REPLACE FUNCTION h3_raster_class_summary_subpixel(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
BEGIN
RETURN QUERY SELECT (__h3_raster_class_polygon_summary_subpixel(
rast,
poly,
resolution,
nband,
__h3_raster_polygon_centroid_cell_area(poly, resolution),
__h3_raster_polygon_pixel_area(rast, poly)
)).*;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_class_summary_subpixel(raster, integer, integer)
IS 'Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Assumes H3 cell is smaller than a pixel. Finds corresponding pixel for each H3 cell in raster.';
--@ availability: 4.1.1
CREATE OR REPLACE FUNCTION h3_raster_class_summary(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
cell_area CONSTANT double precision := __h3_raster_polygon_centroid_cell_area(poly, resolution);
pixel_area CONSTANT double precision := __h3_raster_polygon_pixel_area(rast, poly);
pixels_per_cell CONSTANT double precision := cell_area / pixel_area;
BEGIN
IF pixels_per_cell > 70
AND (ST_Area(ST_Transform(poly, 4326)::geography) / cell_area) > 10000 / (pixels_per_cell - 70)
THEN
RETURN QUERY SELECT (__h3_raster_class_polygon_summary_clip(
rast,
poly,
resolution,
nband,
pixel_area
)).*;
ELSIF pixels_per_cell > 1 THEN
RETURN QUERY SELECT (__h3_raster_class_summary_centroids(
rast,
resolution,
nband,
pixel_area
)).*;
ELSE
RETURN QUERY SELECT (__h3_raster_class_polygon_summary_subpixel(
rast,
poly,
resolution,
nband,
cell_area,
pixel_area
)).*;
END IF;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_raster_class_summary(raster, integer, integer)
IS 'Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Attempts to select an appropriate method based on number of pixels per H3 cell.';
h3-pg-4.2.2/h3_postgis/sql/install/99-deprecated.sql 0000664 0000000 0000000 00000002231 14752347156 0022060 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--@ availability: 4.0.0
--@ deprecated
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geometry(h3index, extend_antimeridian boolean) RETURNS geometry
AS $$ SELECT ST_SetSRID(h3_cell_to_boundary($1, extend_antimeridian)::geometry, 4326) $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
--@ availability: 4.0.0
--@ deprecated
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geography(h3index, extend_antimeridian boolean) RETURNS geography
AS $$ SELECT ST_SetSRID(h3_cell_to_boundary($1, extend_antimeridian)::geometry, 4326) $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
h3-pg-4.2.2/h3_postgis/sql/updates/ 0000775 0000000 0000000 00000000000 14752347156 0017001 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.0.0--4.0.1.sql 0000664 0000000 0000000 00000005003 14752347156 0023027 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.0.1'" to load this file. \quit
-- deprecated
DROP FUNCTION IF EXISTS h3_cell_to_boundary_geometry(h3index, boolean);
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geometry(h3index, extend_antimeridian boolean) RETURNS geometry
AS $$ SELECT ST_SetSRID(h3_cell_to_boundary($1, extend_antimeridian)::geometry, 4326) $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
DROP FUNCTION IF EXISTS h3_cell_to_boundary_geography(h3index, boolean);
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geography(h3index, extend_antimeridian boolean) RETURNS geography
AS $$ SELECT ST_SetSRID(h3_cell_to_boundary($1, extend_antimeridian)::geometry, 4326) $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
-- new splitted version
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geometry(h3index) RETURNS geometry
AS $$ SELECT h3_cell_to_boundary_wkb($1)::geometry $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geography(h3index) RETURNS geography
AS $$ SELECT h3_cell_to_boundary_wkb($1)::geography $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
-- comments
COMMENT ON FUNCTION
h3_lat_lng_to_cell(geometry, resolution integer)
IS 'Indexes the location at the specified resolution.';
COMMENT ON FUNCTION
h3_lat_lng_to_cell(geometry, resolution integer)
IS 'Indexes the location at the specified resolution.';
COMMENT ON FUNCTION
h3_cell_to_geometry(h3index)
IS 'Finds the centroid of the index.';
COMMENT ON FUNCTION
h3_cell_to_geography(h3index)
IS 'Finds the centroid of the index.';
COMMENT ON FUNCTION
h3_cell_to_boundary_geometry(h3index)
IS 'Finds the boundary of the index.
Splits polygons when crossing 180th meridian.';
COMMENT ON FUNCTION
h3_cell_to_boundary_geography(h3index)
IS 'Finds the boundary of the index.
Splits polygons when crossing 180th meridian.';
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.0.0.sql 0000664 0000000 0000000 00000006277 14752347156 0022332 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION h3_postgis" to load this file. \quit
CREATE OR REPLACE FUNCTION h3_lat_lng_to_cell(geometry, resolution integer) RETURNS h3index
AS $$ SELECT h3_lat_lng_to_cell($1::point, $2); $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
CREATE OR REPLACE FUNCTION h3_lat_lng_to_cell(geography, resolution integer) RETURNS h3index
AS $$ SELECT h3_lat_lng_to_cell($1::geometry, $2); $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
CREATE OR REPLACE FUNCTION h3_cell_to_geometry(h3index) RETURNS geometry
AS $$ SELECT ST_SetSRID(h3_cell_to_lat_lng($1)::geometry, 4326) $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
CREATE OR REPLACE FUNCTION h3_cell_to_geography(h3index) RETURNS geography
AS $$ SELECT h3_cell_to_geometry($1)::geography $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geometry(h3index, extend boolean DEFAULT FALSE) RETURNS geometry
AS $$ SELECT ST_SetSRID(h3_cell_to_boundary($1, $2)::geometry, 4326) $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
CREATE OR REPLACE FUNCTION h3_cell_to_boundary_geography(h3index, extend boolean DEFAULT FALSE) RETURNS geography
AS $$ SELECT h3_cell_to_boundary_geometry($1, $2)::geography $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
CREATE OR REPLACE FUNCTION h3_polygon_to_cells(multi geometry, resolution integer) RETURNS SETOF h3index
AS $$ SELECT h3_polygon_to_cells(exterior, holes, resolution) FROM (
SELECT
-- extract exterior ring of each polygon
ST_MakePolygon(ST_ExteriorRing(poly))::polygon exterior,
-- extract holes of each polygon
(SELECT array_agg(hole)
FROM (
SELECT ST_MakePolygon(ST_InteriorRingN(
poly,
generate_series(1, ST_NumInteriorRings(poly))
))::polygon AS hole
) q_hole
) holes
-- extract single polygons from multipolygon
FROM (
select (st_dump(multi)).geom as poly
) q_poly GROUP BY poly
) h3_polygon_to_cells; $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
CREATE OR REPLACE FUNCTION h3_polygon_to_cells(multi geography, resolution integer) RETURNS SETOF h3index
AS $$ SELECT h3_polygon_to_cells($1::geometry, $2) $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
CREATE CAST (h3index AS geometry) WITH FUNCTION h3_cell_to_geometry(h3index);
CREATE CAST (h3index AS geography) WITH FUNCTION h3_cell_to_geography(h3index);
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.0.1--4.0.2.sql 0000664 0000000 0000000 00000001406 14752347156 0023034 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.0.2'" to load this file. \quit
-- no changes
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.0.2--4.0.3.sql 0000664 0000000 0000000 00000005677 14752347156 0023054 0 ustar 00root root 0000000 0000000 /*
* Copyright 2022 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.0.3'" to load this file. \quit
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon_geometry(h3index[]) RETURNS geometry
AS $$ SELECT h3_cells_to_multi_polygon_wkb($1)::geometry $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon_geography(h3index[]) RETURNS geography
AS $$ SELECT h3_cells_to_multi_polygon_wkb($1)::geography $$ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE SQL;
CREATE AGGREGATE h3_cells_to_multi_polygon_geometry(h3index) (
sfunc = array_append,
stype = h3index[],
finalfunc = h3_cells_to_multi_polygon_geometry,
parallel = safe
);
CREATE AGGREGATE h3_cells_to_multi_polygon_geography(h3index) (
sfunc = array_append,
stype = h3index[],
finalfunc = h3_cells_to_multi_polygon_geography,
parallel = safe
);
CREATE OR REPLACE FUNCTION
h3_grid_path_cells_recursive(origin h3index, destination h3index) RETURNS SETOF h3index
AS $$
BEGIN
IF (SELECT
origin != destination
AND NOT h3_are_neighbor_cells(origin, destination)
AND ((base1 != base2 AND NOT h3_are_neighbor_cells(base1, base2))
OR ((h3_is_pentagon(base1) OR h3_is_pentagon(base2))
AND NOT (
h3_get_icosahedron_faces(origin)
&& h3_get_icosahedron_faces(destination))))
FROM (
SELECT
h3_cell_to_parent(origin, 0) AS base1,
h3_cell_to_parent(destination, 0) AS base2) AS t)
THEN
RETURN QUERY WITH
points AS (
SELECT
h3_cell_to_geometry(origin) AS g1,
h3_cell_to_geometry(destination) AS g2),
cells AS (
SELECT
h3_lat_lng_to_cell(
ST_Centroid(ST_MakeLine(g1, g2)::geography),
h3_get_resolution(origin)) AS middle
FROM points)
SELECT h3_grid_path_cells_recursive(origin, middle) FROM cells
UNION
SELECT h3_grid_path_cells_recursive(middle, destination) FROM cells;
ELSE
RETURN QUERY SELECT h3_grid_path_cells(origin, destination);
END IF;
END;
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL SAFE;
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.0.3--4.1.0.sql 0000664 0000000 0000000 00000001367 14752347156 0023043 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.1.0'" to load this file. \quit
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.1.0--4.1.1.sql 0000664 0000000 0000000 00000047575 14752347156 0023055 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.1.1'" to load this file. \quit
-- raster
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary_wkb(cell h3index) RETURNS bytea
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary_wkb(h3index)
IS 'Finds the boundary of the index, converts to EWKB.
Splits polygons when crossing 180th meridian.
This function has to return WKB since Postgres does not provide multipolygon type.';
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon_wkb(h3index[]) RETURNS bytea
AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cells_to_multi_polygon_wkb(h3index[])
IS 'Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons, converts to EWKB.
Splits polygons when crossing 180th meridian.';
-- Raster processing
CREATE OR REPLACE FUNCTION __h3_raster_band_nodata(
rast raster,
nband integer)
RETURNS double precision
AS $$
SELECT coalesce(
ST_BandNoDataValue(rast, nband),
ST_MinPossibleValue(ST_BandPixelType(rast, nband)));
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_to_polygon(
rast raster,
nband integer)
RETURNS geometry
AS $$
SELECT ST_MinConvexHull(rast, nband);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_pixel_area(
rast raster,
poly geometry)
RETURNS double precision
AS $$
SELECT ST_Area(
ST_Transform(
ST_PixelAsPolygon(
rast,
ST_WorldToRasterCoordX(rast, c),
ST_WorldToRasterCoordY(rast, c)),
4326)::geography)
FROM ST_Centroid(poly) AS c
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_centroid_cell(
poly geometry,
resolution integer)
RETURNS h3index
AS $$
DECLARE
cell h3index := h3_lat_lng_to_cell(ST_Transform(ST_Centroid(poly), 4326), resolution);
BEGIN
IF h3_is_pentagon(cell) THEN
SELECT h3 INTO cell FROM h3_grid_disk(cell) AS h3 WHERE h3 != cell LIMIT 1;
END IF;
RETURN cell;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_centroid_cell_area(
poly geometry,
resolution integer)
RETURNS double precision
AS $$
SELECT ST_Area(
h3_cell_to_boundary_geography(
__h3_raster_polygon_centroid_cell(poly, resolution)));
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_to_cells(
rast raster,
poly geometry,
resolution integer,
buffer double precision)
RETURNS SETOF h3index
AS $$
DECLARE
buffered geometry := poly;
BEGIN
IF ST_SRID(rast) != 4326 THEN
buffered := ST_Transform(
ST_Buffer(
poly,
greatest(ST_PixelWidth(rast), ST_PixelHeight(rast)),
'join=mitre'),
4326);
END IF;
IF buffer > 0.0 THEN
RETURN QUERY
SELECT h3_polygon_to_cells(
ST_Buffer(
buffered::geography,
buffer,
'join=mitre'),
resolution);
ELSE
RETURN QUERY
SELECT h3_polygon_to_cells(buffered, resolution);
END IF;
END
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_to_cell_boundaries_intersects(
rast raster,
poly geometry,
resolution integer)
RETURNS TABLE (h3 h3index, geom geometry)
AS $$
SELECT h3, geom
FROM
__h3_raster_polygon_to_cells(
rast,
poly,
resolution,
h3_get_hexagon_edge_length_avg(resolution, 'm') * 1.3
) AS h3,
ST_Transform(h3_cell_to_boundary_geometry(h3), ST_SRID(rast)) AS geom
WHERE ST_Intersects(geom, poly);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_to_cell_coords_centroid(
rast raster,
poly geometry,
resolution integer)
RETURNS TABLE (h3 h3index, x integer, y integer)
AS $$
WITH
geoms AS (
SELECT
h3,
ST_Transform(
h3_cell_to_geometry(h3),
ST_SRID(poly)
) AS geom
FROM (
SELECT __h3_raster_polygon_to_cells(rast, poly, resolution, 0.0) AS h3
) t),
coords AS (
SELECT
h3,
ST_WorldToRasterCoordX(rast, geom) AS x,
ST_WorldToRasterCoordY(rast, geom) AS y
FROM geoms)
SELECT h3, x, y
FROM coords
WHERE
x BETWEEN 1 AND ST_Width(rast)
AND y BETWEEN 1 AND ST_Height(rast);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_to_cell_parts(
rast raster,
poly geometry,
resolution integer,
nband integer)
RETURNS TABLE (h3 h3index, part raster)
AS $$
DECLARE
nodata CONSTANT double precision := __h3_raster_band_nodata(rast, nband);
BEGIN
RETURN QUERY
SELECT c.h3, p AS part
FROM
__h3_raster_polygon_to_cell_boundaries_intersects(rast, poly, resolution) AS c,
ST_Clip(rast, nband, c.geom, nodata, TRUE) AS p
WHERE NOT ST_BandIsNoData(p, nband);
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_polygon_subpixel_cell_values(
rast raster,
poly geometry,
resolution integer,
nband integer)
RETURNS TABLE (h3 h3index, val double precision)
AS $$
SELECT
h3,
ST_Value(rast, nband, x, y) AS val
FROM __h3_raster_polygon_to_cell_coords_centroid(rast, poly, resolution);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
-- Raster processing: continuous data
CREATE TYPE h3_raster_summary_stats AS (
count double precision,
sum double precision,
mean double precision,
stddev double precision,
min double precision,
max double precision
);
CREATE OR REPLACE FUNCTION __h3_raster_to_summary_stats(stats summarystats)
RETURNS h3_raster_summary_stats
AS $$
SELECT ROW(
(stats).count,
(stats).sum,
(stats).mean,
(stats).stddev,
(stats).min,
(stats).max
)::h3_raster_summary_stats
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_summary_stats_agg_transfn(
s1 h3_raster_summary_stats,
s2 h3_raster_summary_stats)
RETURNS h3_raster_summary_stats
AS $$
WITH total AS (
SELECT
(s1).count + (s2).count AS count,
(s1).sum + (s2).sum AS sum)
SELECT ROW(
t.count,
t.sum,
t.sum / t.count,
sqrt(
(
-- sum of squared values: (variance + mean squared) * count
(((s1).stddev * (s1).stddev + (s1).mean * (s1).mean)) * (s1).count
+ (((s2).stddev * (s2).stddev + (s2).mean * (s2).mean)) * (s2).count
)
/ t.count
- ((t.sum * t.sum) / (t.count * t.count)) -- mean squared
),
least((s1).min, (s2).min),
greatest((s1).max, (s2).max)
)::h3_raster_summary_stats
FROM total AS t
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE AGGREGATE h3_raster_summary_stats_agg(h3_raster_summary_stats) (
sfunc = __h3_raster_summary_stats_agg_transfn,
stype = h3_raster_summary_stats,
parallel = safe
);
CREATE OR REPLACE FUNCTION __h3_raster_polygon_summary_clip(
rast raster,
poly geometry,
resolution integer,
nband integer)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
SELECT
h3,
__h3_raster_to_summary_stats(ST_SummaryStats(part, nband, TRUE)) AS stats
FROM __h3_raster_polygon_to_cell_parts(rast, poly, resolution, nband);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_raster_summary_clip(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
SELECT __h3_raster_polygon_summary_clip(
rast,
__h3_raster_to_polygon(rast, nband),
resolution,
nband);
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_summary_clip(raster, integer, integer)
IS 'Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Clips the raster by H3 cell geometries and processes each part separately.';
CREATE OR REPLACE FUNCTION h3_raster_summary_centroids(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
SELECT
h3_lat_lng_to_cell(ST_Transform(geom, 4326), resolution) AS h3,
ROW(
count(val),
sum(val),
avg(val),
stddev_pop(val),
min(val),
max(val)
)::h3_raster_summary_stats AS stats
FROM ST_PixelAsCentroids(rast, nband)
GROUP BY 1;
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_summary_centroids(raster, integer, integer)
IS 'Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Finds corresponding H3 cell for each pixel, then groups values by H3 index.';
CREATE OR REPLACE FUNCTION __h3_raster_polygon_summary_subpixel(
rast raster,
poly geometry,
resolution integer,
nband integer,
pixels_per_cell double precision)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
SELECT
h3,
ROW(
pixels_per_cell, -- count
val, -- sum
val, -- mean
0.0, -- stddev
val, -- min
val -- max
)::h3_raster_summary_stats AS stats
FROM __h3_raster_polygon_subpixel_cell_values(rast, poly, resolution, nband) t;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_raster_summary_subpixel(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
pixel_area CONSTANT double precision := __h3_raster_polygon_pixel_area(rast, poly);
cell_area CONSTANT double precision := __h3_raster_polygon_centroid_cell_area(poly, resolution);
BEGIN
RETURN QUERY SELECT (__h3_raster_polygon_summary_subpixel(
rast,
poly,
resolution,
nband,
cell_area / pixel_area)).*;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_summary_subpixel(raster, integer, integer)
IS 'Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Assumes H3 cell is smaller than a pixel. Finds corresponding pixel for each H3 cell in raster.';
CREATE OR REPLACE FUNCTION h3_raster_summary(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, stats h3_raster_summary_stats)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
cell_area CONSTANT double precision := __h3_raster_polygon_centroid_cell_area(poly, resolution);
pixel_area CONSTANT double precision := __h3_raster_polygon_pixel_area(rast, poly);
pixels_per_cell CONSTANT double precision := cell_area / pixel_area;
BEGIN
IF pixels_per_cell > 70
AND (ST_Area(ST_Transform(poly, 4326)::geography) / cell_area) > 10000 / (pixels_per_cell - 70)
THEN
RETURN QUERY SELECT (__h3_raster_polygon_summary_clip(
rast,
poly,
resolution,
nband
)).*;
ELSIF pixels_per_cell > 1 THEN
RETURN QUERY SELECT (h3_raster_summary_centroids(
rast,
resolution,
nband
)).*;
ELSE
RETURN QUERY SELECT (__h3_raster_polygon_summary_subpixel(
rast,
poly,
resolution,
nband,
pixels_per_cell
)).*;
END IF;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_summary(raster, integer, integer)
IS 'Returns `h3_raster_summary_stats` for each H3 cell in raster for a given band. Attempts to select an appropriate method based on number of pixels per H3 cell.';
-- Raster processing: discrete data
CREATE TYPE h3_raster_class_summary_item AS (
val integer,
count double precision,
area double precision
);
CREATE OR REPLACE FUNCTION h3_raster_class_summary_item_to_jsonb(
item h3_raster_class_summary_item)
RETURNS jsonb
AS $$
SELECT jsonb_build_object(
'value', (item).val,
'count', (item).count,
'area', (item).area
);
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_class_summary_item_to_jsonb(h3_raster_class_summary_item)
IS 'Convert raster summary to JSONB, example: `{"count": 10, "value": 2, "area": 16490.3423}`';
CREATE OR REPLACE FUNCTION __h3_raster_class_summary_item_agg_transfn(
s1 h3_raster_class_summary_item,
s2 h3_raster_class_summary_item)
RETURNS h3_raster_class_summary_item
AS $$
SELECT
s1.val,
s1.count + s2.count,
s1.area + s2.area;
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE AGGREGATE h3_raster_class_summary_item_agg(h3_raster_class_summary_item) (
stype = h3_raster_class_summary_item,
sfunc = __h3_raster_class_summary_item_agg_transfn,
parallel = safe
);
CREATE OR REPLACE FUNCTION __h3_raster_class_summary_part(
rast raster,
nband integer,
pixel_area double precision)
RETURNS SETOF h3_raster_class_summary_item
AS $$
SELECT ROW(
value::integer,
count::double precision,
count * pixel_area
)::h3_raster_class_summary_item
FROM ST_ValueCount(rast, nband) t;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION __h3_raster_class_polygon_summary_clip(
rast raster,
poly geometry,
resolution integer,
nband integer,
pixel_area double precision)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
WITH
summary AS (
SELECT
h3,
__h3_raster_class_summary_part(part, nband, pixel_area) AS summary
FROM __h3_raster_polygon_to_cell_parts(rast, poly, resolution, nband) t)
SELECT h3, (summary).val, summary
FROM summary;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_raster_class_summary_clip(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
BEGIN
RETURN QUERY SELECT (__h3_raster_class_polygon_summary_clip(
rast,
poly,
resolution,
nband,
__h3_raster_polygon_pixel_area(rast, poly)
)).*;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_class_summary_clip(raster, integer, integer)
IS 'Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Clips the raster by H3 cell geometries and processes each part separately.';
CREATE OR REPLACE FUNCTION __h3_raster_class_summary_centroids(
rast raster,
resolution integer,
nband integer,
pixel_area double precision)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
SELECT
h3_lat_lng_to_cell(ST_Transform(geom, 4326), resolution) AS h3,
val::integer AS val,
ROW(
val::integer,
count(*)::double precision,
count(*) * pixel_area
)::h3_raster_class_summary_item AS summary
FROM ST_PixelAsCentroids(rast, nband)
GROUP BY 1, 2;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_raster_class_summary_centroids(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
SELECT __h3_raster_class_summary_centroids(
rast,
resolution,
nband,
__h3_raster_polygon_pixel_area(rast, __h3_raster_to_polygon(rast, nband))
);
$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_class_summary_centroids(raster, integer, integer)
IS 'Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Finds corresponding H3 cell for each pixel, then groups by H3 and value.';
CREATE OR REPLACE FUNCTION __h3_raster_class_polygon_summary_subpixel(
rast raster,
poly geometry,
resolution integer,
nband integer,
cell_area double precision,
pixel_area double precision)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
SELECT
h3,
val::integer AS val,
ROW(
val::integer,
cell_area / pixel_area,
cell_area
)::h3_raster_class_summary_item AS summary
FROM __h3_raster_polygon_subpixel_cell_values(rast, poly, resolution, nband);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION h3_raster_class_summary_subpixel(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
BEGIN
RETURN QUERY SELECT (__h3_raster_class_polygon_summary_subpixel(
rast,
poly,
resolution,
nband,
__h3_raster_polygon_centroid_cell_area(poly, resolution),
__h3_raster_polygon_pixel_area(rast, poly)
)).*;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION
h3_raster_class_summary_subpixel(raster, integer, integer)
IS 'Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Assumes H3 cell is smaller than a pixel. Finds corresponding pixel for each H3 cell in raster.';
CREATE OR REPLACE FUNCTION h3_raster_class_summary(
rast raster,
resolution integer,
nband integer DEFAULT 1)
RETURNS TABLE (h3 h3index, val integer, summary h3_raster_class_summary_item)
AS $$
DECLARE
poly CONSTANT geometry := __h3_raster_to_polygon(rast, nband);
cell_area CONSTANT double precision := __h3_raster_polygon_centroid_cell_area(poly, resolution);
pixel_area CONSTANT double precision := __h3_raster_polygon_pixel_area(rast, poly);
pixels_per_cell CONSTANT double precision := cell_area / pixel_area;
BEGIN
IF pixels_per_cell > 70
AND (ST_Area(ST_Transform(poly, 4326)::geography) / cell_area) > 10000 / (pixels_per_cell - 70)
THEN
RETURN QUERY SELECT (__h3_raster_class_polygon_summary_clip(
rast,
poly,
resolution,
nband,
pixel_area
)).*;
ELSIF pixels_per_cell > 1 THEN
RETURN QUERY SELECT (__h3_raster_class_summary_centroids(
rast,
resolution,
nband,
pixel_area
)).*;
ELSE
RETURN QUERY SELECT (__h3_raster_class_polygon_summary_subpixel(
rast,
poly,
resolution,
nband,
cell_area,
pixel_area
)).*;
END IF;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION h3_raster_class_summary(raster, integer, integer)
IS 'Returns `h3_raster_class_summary_item` for each H3 cell and value for a given band. Attempts to select an appropriate method based on number of pixels per H3 cell.';
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.1.1--4.1.2.sql 0000664 0000000 0000000 00000002775 14752347156 0023050 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.1.2'" to load this file. \quit
-- fix wrong shared lib #117
CREATE OR REPLACE FUNCTION
h3_cell_to_boundary_wkb(cell h3index) RETURNS bytea
AS 'h3_postgis' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cell_to_boundary_wkb(h3index)
IS 'Finds the boundary of the index, converts to EWKB.
Splits polygons when crossing 180th meridian.
This function has to return WKB since Postgres does not provide multipolygon type.';
CREATE OR REPLACE FUNCTION
h3_cells_to_multi_polygon_wkb(h3index[]) RETURNS bytea
AS 'h3_postgis' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; COMMENT ON FUNCTION
h3_cells_to_multi_polygon_wkb(h3index[])
IS 'Create a LinkedGeoPolygon describing the outline(s) of a set of hexagons, converts to EWKB.
Splits polygons when crossing 180th meridian.';
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.1.2--4.1.3.sql 0000664 0000000 0000000 00000002255 14752347156 0023043 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.1.3'" to load this file. \quit
--@ availability: 4.1.3
CREATE OPERATOR @ (
PROCEDURE = h3_lat_lng_to_cell,
LEFTARG = geometry, RIGHTARG = integer
);
COMMENT ON OPERATOR @ (geometry, integer) IS
'Index geometry at specified resolution.';
--@ availability: 4.1.3
CREATE OPERATOR @ (
PROCEDURE = h3_lat_lng_to_cell,
LEFTARG = geography, RIGHTARG = integer
);
COMMENT ON OPERATOR @ (geography, integer) IS
'Index geography at specified resolution.';
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.1.3--4.1.4.sql 0000664 0000000 0000000 00000001367 14752347156 0023050 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.1.4'" to load this file. \quit
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.1.4--4.2.0.sql 0000664 0000000 0000000 00000004020 14752347156 0023033 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.2.0'" to load this file. \quit
CREATE OR REPLACE FUNCTION h3_polygon_to_cells_experimental(multi geometry, resolution integer, containment_mode text DEFAULT 'center') RETURNS SETOF h3index
AS $$ SELECT h3_polygon_to_cells_experimental(exterior, holes, resolution, containment_mode) FROM (
SELECT
-- extract exterior ring of each polygon
ST_MakePolygon(ST_ExteriorRing(poly))::polygon exterior,
-- extract holes of each polygon
(SELECT array_agg(hole)
FROM (
SELECT ST_MakePolygon(ST_InteriorRingN(
poly,
generate_series(1, ST_NumInteriorRings(poly))
))::polygon AS hole
) q_hole
) holes
-- extract single polygons from multipolygon
FROM (
select (st_dump(multi)).geom as poly
) q_poly GROUP BY poly
) h3_polygon_to_cells; $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
CREATE OR REPLACE FUNCTION h3_polygon_to_cells_experimental(multi geography, resolution integer, containment_mode text DEFAULT 'center') RETURNS SETOF h3index
AS $$ SELECT h3_polygon_to_cells_experimental($1::geometry, $2, $3) $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE CALLED ON NULL INPUT; -- NOT STRICT
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.2.0--4.2.1.sql 0000664 0000000 0000000 00000001367 14752347156 0023044 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.2.1'" to load this file. \quit
h3-pg-4.2.2/h3_postgis/sql/updates/h3_postgis--4.2.1--4.2.2.sql 0000664 0000000 0000000 00000001367 14752347156 0023046 0 ustar 00root root 0000000 0000000 /*
* Copyright 2025 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION h3_postgis UPDATE TO '4.2.2'" to load this file. \quit
h3-pg-4.2.2/h3_postgis/src/ 0000775 0000000 0000000 00000000000 14752347156 0015324 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3_postgis/src/init.c 0000664 0000000 0000000 00000001366 14752347156 0016441 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include // PG_MODULE_MAGIC
/* see https://www.postgresql.org/docs/current/xfunc-c.html#XFUNC-C-DYNLOAD */
PG_MODULE_MAGIC;
h3-pg-4.2.2/h3_postgis/src/wkb.c 0000664 0000000 0000000 00000021036 14752347156 0016255 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include "error.h"
#include "wkb.h"
#include "wkb_linked_geo.h"
#define WKB_BYTE_SIZE 1
#define WKB_INT_SIZE 4
#define WKB_DOUBLE_SIZE 8
#define WKB_NDR 1
#define WKB_XDR 0
#define WKB_POLYGON_TYPE 3
#define WKB_MULTIPOLYGON_TYPE 6
#define WKB_SRID_FLAG 0x20000000
#define WKB_SRID_DEFAULT 4326
#define ASSERT_WKB_DATA_WRITTEN(wkb, data) \
ASSERT( \
(uint8 *)wkb + VARSIZE(wkb) == data, \
ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, \
"# of written bytes (%d) must match allocation size (%d)", \
(int)(data - (uint8 *)wkb), VARSIZE(wkb))
static bool
boundary_is_empty(const CellBoundary * boundary);
static bool
boundary_is_closed(const CellBoundary * boundary);
static size_t
boundary_array_data_size(const CellBoundary * boundaries, int num);
static size_t
boundary_data_size(const CellBoundary * boundary);
static size_t
linked_geo_polygon_data_size(const LinkedGeoPolygon * multiPolygon);
static size_t
linked_geo_loop_data_size(const LinkedGeoLoop * loop);
static uint8 *
wkb_write_boundary_array_data(uint8 *data, const CellBoundary * boundaries, int num);
static uint8 *
wkb_write_boundary_data(uint8 *data, const CellBoundary * boundary);
static uint8 *
wkb_write_linked_geo_polygon_data(uint8 *data, const LinkedGeoPolygon * multiPolygon);
static uint8 *
wkb_write_lat_lng_array(uint8 *data, const LatLng * coord, int num);
static uint8 *
wkb_write_linked_geo_loop_data(uint8 *data, const LinkedGeoLoop * loop);
static uint8 *
wkb_write_lat_lng(uint8 *data, const LatLng * coord);
static uint8 *
wkb_write_endian(uint8 *data);
static uint8 *
wkb_write_int(uint8 *data, uint32 value);
static uint8 *
wkb_write(uint8 *data, const void *value, size_t size);
bytea *
boundary_array_to_wkb(const CellBoundary * boundaries, size_t num)
{
uint8 *data;
bytea *wkb;
size_t size = boundary_array_data_size(boundaries, num);
wkb = palloc(VARHDRSZ + size);
SET_VARSIZE(wkb, VARHDRSZ + size);
data = (uint8 *) VARDATA(wkb);
data = wkb_write_boundary_array_data(data, boundaries, num);
ASSERT_WKB_DATA_WRITTEN(wkb, data);
return wkb;
}
bytea *
boundary_to_wkb(const CellBoundary * boundary)
{
bytea *wkb;
uint8 *data;
size_t size = boundary_data_size(boundary);
wkb = palloc(VARHDRSZ + size);
SET_VARSIZE(wkb, VARHDRSZ + size);
data = (uint8 *) VARDATA(wkb);
data = wkb_write_boundary_data(data, boundary);
ASSERT_WKB_DATA_WRITTEN(wkb, data);
return wkb;
}
bytea *
linked_geo_polygon_to_wkb(const LinkedGeoPolygon * multiPolygon)
{
bytea *wkb;
uint8 *data;
size_t size = linked_geo_polygon_data_size(multiPolygon);
wkb = palloc(VARHDRSZ + size);
SET_VARSIZE(wkb, VARHDRSZ + size);
data = (uint8 *) VARDATA(wkb);
data = wkb_write_linked_geo_polygon_data(data, multiPolygon);
ASSERT_WKB_DATA_WRITTEN(wkb, data);
return wkb;
}
bool
boundary_is_empty(const CellBoundary * boundary)
{
return boundary->numVerts < 1;
}
bool
boundary_is_closed(const CellBoundary * boundary)
{
const LatLng *verts;
int numVerts;
if (boundary_is_empty(boundary))
return true;
verts = boundary->verts;
numVerts = boundary->numVerts;
return verts[0].lng == verts[numVerts - 1].lng
&& verts[1].lat == verts[numVerts - 1].lat;
}
size_t
boundary_array_data_size(const CellBoundary * boundaries, int num)
{
/* byte order + type + srid + # of polygons */
size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE * 3;
/* boundaries */
for (int i = 0; i < num; i++)
size += boundary_data_size(&boundaries[i]);
return size;
}
size_t
boundary_data_size(const CellBoundary * boundary)
{
/* byte order + type + srid + # of rings */
size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE * 3;
/* points */
if (!boundary_is_empty(boundary))
{
int numVerts = boundary->numVerts;
if (!boundary_is_closed(boundary))
numVerts++;
/* # of points, point data */
size += WKB_INT_SIZE + numVerts * WKB_DOUBLE_SIZE * 2;
}
return size;
}
size_t
linked_geo_polygon_data_size(const LinkedGeoPolygon * multiPolygon)
{
size_t size = 0;
int isMulti = (multiPolygon->next != NULL);
/* byte order + type + srid */
size = WKB_BYTE_SIZE + WKB_INT_SIZE * 2;
if (isMulti)
{
/* # of polygons */
size += WKB_INT_SIZE;
}
FOREACH_LINKED_POLYGON(multiPolygon, polygon)
{
if (isMulti)
{
/* byte order + type + srid */
size += WKB_BYTE_SIZE + WKB_INT_SIZE * 2;
}
/* # of rings */
size += WKB_INT_SIZE;
FOREACH_LINKED_LOOP(polygon, loop)
{
size += linked_geo_loop_data_size(loop);
}
}
return size;
}
size_t
linked_geo_loop_data_size(const LinkedGeoLoop * loop)
{
/* ring size */
size_t size = WKB_INT_SIZE;
/* point data (including closing point) */
size += WKB_DOUBLE_SIZE * (count_linked_lat_lng(loop) + 1) * 2;
return size;
}
uint8 *
wkb_write_boundary_array_data(uint8 *data, const CellBoundary * boundaries, int num)
{
/* byte order */
data = wkb_write_endian(data);
/* type */
data = wkb_write_int(data, WKB_MULTIPOLYGON_TYPE | WKB_SRID_FLAG);
/* SRID */
data = wkb_write_int(data, WKB_SRID_DEFAULT);
/* # of polygons */
data = wkb_write_int(data, num);
/* polygons */
for (int i = 0; i < num; i++)
data = wkb_write_boundary_data(data, &boundaries[i]);
return data;
}
uint8 *
wkb_write_boundary_data(uint8 *data, const CellBoundary * boundary)
{
/* byte order */
data = wkb_write_endian(data);
/* type */
data = wkb_write_int(data, WKB_POLYGON_TYPE | WKB_SRID_FLAG);
/* SRID */
data = wkb_write_int(data, WKB_SRID_DEFAULT);
/* # of rings */
data = wkb_write_int(data, boundary_is_empty(boundary) ? 0 : 1);
/* exterior ring */
if (!boundary_is_empty(boundary))
{
bool is_closed = boundary_is_closed(boundary);
data = wkb_write_int(data, boundary->numVerts + (is_closed ? 0 : 1));
data = wkb_write_lat_lng_array(data, boundary->verts, boundary->numVerts);
/* close the ring */
if (!is_closed)
data = wkb_write_lat_lng(data, &boundary->verts[0]);
}
return data;
}
uint8 *
wkb_write_linked_geo_polygon_data(uint8 *data, const LinkedGeoPolygon * multiPolygon)
{
int isMulti = (multiPolygon->next != NULL);
int type = isMulti ? WKB_MULTIPOLYGON_TYPE : WKB_POLYGON_TYPE;
/* byte order */
data = wkb_write_endian(data);
/* type */
data = wkb_write_int(data, type | WKB_SRID_FLAG);
/* SRID */
data = wkb_write_int(data, WKB_SRID_DEFAULT);
if (isMulti)
{
/* # of polygons */
data = wkb_write_int(data, count_linked_polygons(multiPolygon));
}
FOREACH_LINKED_POLYGON(multiPolygon, polygon)
{
if (isMulti)
{
/* byte order */
data = wkb_write_endian(data);
/* type */
data = wkb_write_int(data, WKB_POLYGON_TYPE | WKB_SRID_FLAG);
/* SRID */
data = wkb_write_int(data, WKB_SRID_DEFAULT);
}
/* # of rings */
data = wkb_write_int(data, count_linked_geo_loops(polygon));
/* rings */
FOREACH_LINKED_LOOP(polygon, loop)
{
data = wkb_write_linked_geo_loop_data(data, loop);
}
}
return data;
}
uint8 *
wkb_write_lat_lng_array(uint8 *data, const LatLng * coords, int num)
{
for (int i = 0; i < num; i++)
data = wkb_write_lat_lng(data, &coords[i]);
return data;
}
uint8 *
wkb_write_linked_geo_loop_data(uint8 *data, const LinkedGeoLoop * loop)
{
/* # of points (including closing point) */
data = wkb_write_int(data, count_linked_lat_lng(loop) + 1);
/* point data */
FOREACH_LINKED_LAT_LNG(loop, latlng)
{
data = wkb_write_lat_lng(data, &latlng->vertex);
}
/* closing point data */
data = wkb_write_lat_lng(data, &loop->first->vertex);
return data;
}
uint8 *
wkb_write_lat_lng(uint8 *data, const LatLng * coord)
{
data = wkb_write(data, &coord->lng, sizeof(coord->lng));
data = wkb_write(data, &coord->lat, sizeof(coord->lat));
return data;
}
uint8 *
wkb_write_endian(uint8 *data)
{
/* Always use native order */
uint32 order = 0x00000001;
data[0] = ((uint8 *) &order)[0] ? WKB_NDR : WKB_XDR;
return data + 1;
}
uint8 *
wkb_write_int(uint8 *data, uint32 value)
{
return wkb_write(data, &value, sizeof(value));
}
uint8 *
wkb_write(uint8 *data, const void *value, size_t size)
{
memcpy(data, value, size);
return data + size;
}
h3-pg-4.2.2/h3_postgis/src/wkb.h 0000664 0000000 0000000 00000002022 14752347156 0016254 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PGH3_WKB_H
#define PGH3_WKB_H
#include
#include
#include
#if POSTGRESQL_VERSION_MAJOR >= 16
#include "varatt.h" //VAR_SIZE and friends moved to here from postgres.h
#endif
bytea *
boundary_array_to_wkb(const CellBoundary * boundaries, size_t num);
bytea *
boundary_to_wkb(const CellBoundary * boundary);
bytea *
linked_geo_polygon_to_wkb(const LinkedGeoPolygon * multiPolygon);
#endif
h3-pg-4.2.2/h3_postgis/src/wkb_bbox3.c 0000664 0000000 0000000 00000011434 14752347156 0017353 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include "wkb_bbox3.h"
#include "wkb_linked_geo.h"
typedef struct
{
double x;
double y;
} Vect2;
static void
vect2_normalize(Vect2 * vect);
static short
vect2_segment_side(const Vect2 * start, const Vect2 * end, const Vect2 * point);
static void
bbox3_merge_vect3(const Vect3 * vect, Bbox3 * bbox);
void
bbox3_from_vect3(const Vect3 * vect, Bbox3 * bbox)
{
bbox->xmin = bbox->xmax = vect->x;
bbox->ymin = bbox->ymax = vect->y;
bbox->zmin = bbox->zmax = vect->z;
}
void
bbox3_merge(const Bbox3 * other, Bbox3 * bbox)
{
if (other->xmin < bbox->xmin)
bbox->xmin = other->xmin;
if (other->xmax > bbox->xmax)
bbox->xmax = other->xmax;
if (other->ymin < bbox->ymin)
bbox->ymin = other->ymin;
if (other->ymax > bbox->ymax)
bbox->ymax = other->ymax;
if (other->zmin < bbox->zmin)
bbox->zmin = other->zmin;
if (other->zmax > bbox->zmax)
bbox->zmax = other->zmax;
}
void
bbox3_from_linked_loop(const LinkedGeoLoop * loop, Bbox3 * bbox)
{
Vect3 vect,
nextVect;
vect3_from_lat_lng(&loop->first->vertex, &vect);
bbox3_from_vect3(&vect, bbox);
if (!loop->first->next)
return;
FOREACH_LINKED_LAT_LNG_PAIR(loop, cur, next)
{
Bbox3 segmentBbox;
vect3_from_lat_lng(&next->vertex, &nextVect);
if (!vect3_eq(&vect, &nextVect))
{
bbox3_from_segment_vect3(&vect, &nextVect, &segmentBbox);
bbox3_merge(&segmentBbox, bbox);
}
vect = nextVect;
}
}
int
bbox3_contains_vect3(const Bbox3 * bbox, const Vect3 * vect)
{
return bbox->xmin <= vect->x && vect->x <= bbox->xmax
&& bbox->ymin <= vect->y && vect->y <= bbox->ymax
&& bbox->zmin <= vect->z && vect->z <= bbox->zmax;
}
int
bbox3_contains_lat_lng(const Bbox3 * bbox, const LatLng * coord)
{
Vect3 vect;
vect3_from_lat_lng(coord, &vect);
return bbox3_contains_vect3(bbox, &vect);
}
void
bbox3_from_segment_vect3(const Vect3 * vect1, const Vect3 * vect2, Bbox3 * bbox)
{
Vect3 vn,
vect3;
Vect3 axes[6];
Vect2 r1,
r2,
orig;
short orig_side;
/* Init bbox */
bbox3_from_vect3(vect1, bbox);
bbox3_merge_vect3(vect2, bbox);
/* Check if end points are the same */
if (vect3_eq(vect1, vect2))
return;
/* Normal of plain containing endpoint vectors */
vect3_cross(vect1, vect2, &vn);
vect3_normalize(&vn);
/* Vector orthogonal to first endpoint vector */
vect3_cross(&vn, vect1, &vect3);
/* Project endpoint vectors onto the plane, using vect1/vect3 as basis */
r1.x = 1.0;
r1.y = 0.0;
r2.x = vect3_dot(vect2, vect1);
r2.y = vect3_dot(vect2, &vect3);
/* Origin side relative to the projected segment (r1, r2) */
orig.x = 0.0;
orig.y = 0.0;
orig_side = vect2_segment_side(&r1, &r2, &orig);
/* Axis points: (1, 0, 0), (-1, 0, 0), (0, 1, 0), ... */
memset(axes, 0, 6 * sizeof(Vect3));
axes[0].x = axes[2].y = axes[4].z = 1.0;
axes[1].x = axes[3].y = axes[5].z = -1.0;
for (int i = 0; i < 6; ++i)
{
/* Project axis onto the plane, normalize */
Vect2 rx;
rx.x = vect3_dot(&axes[i], vect1);
rx.y = vect3_dot(&axes[i], &vect3);
vect2_normalize(&rx);
/* Is projected axis vector between r1 and r2 */
/* (is origin on the opposite side of segment (r1, r2))? */
if (vect2_segment_side(&r1, &r2, &rx) != orig_side)
{
Vect3 vx;
vx.x = rx.x * vect1->x + rx.y * vect3.x;
vx.y = rx.x * vect1->y + rx.y * vect3.y;
vx.z = rx.x * vect1->z + rx.y * vect3.z;
bbox3_merge_vect3(&vx, bbox);
}
}
}
void
bbox3_from_segment_lat_lng(const LatLng * coord1, const LatLng * coord2, Bbox3 * bbox)
{
Vect3 vect1,
vect2;
vect3_from_lat_lng(coord1, &vect1);
vect3_from_lat_lng(coord2, &vect2);
bbox3_from_segment_vect3(&vect1, &vect2, bbox);
}
static void
vect2_normalize(Vect2 * vect)
{
double len = sqrt(vect->x * vect->x + vect->y * vect->y);
if (len > 0.0)
{
vect->x /= len;
vect->y /= len;
}
else
{
vect->x = 0.0;
vect->y = 0.0;
}
}
static short
vect2_segment_side(const Vect2 * start, const Vect2 * end, const Vect2 * point)
{
double side = (point->x - start->x) * (end->y - start->y)
- (end->x - start->x) * (point->y - start->y);
return (side == 0) ? 0 : (side < 0) ? -1 : 1;
}
static void
bbox3_merge_vect3(const Vect3 * vect, Bbox3 * bbox)
{
Bbox3 vect_bbox;
bbox3_from_vect3(vect, &vect_bbox);
bbox3_merge(&vect_bbox, bbox);
}
h3-pg-4.2.2/h3_postgis/src/wkb_bbox3.h 0000664 0000000 0000000 00000002501 14752347156 0017353 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PGH3_WKB_BBOX3_H
#define PGH3_WKB_BBOX3_H
#include
#include "wkb_vect3.h"
typedef struct
{
double xmin;
double xmax;
double ymin;
double ymax;
double zmin;
double zmax;
} Bbox3;
void
bbox3_from_vect3(const Vect3 * vect, Bbox3 * bbox);
void
bbox3_merge(const Bbox3 * other, Bbox3 * bbox);
void
bbox3_from_linked_loop(const LinkedGeoLoop * loop, Bbox3 * bbox);
int
bbox3_contains_vect3(const Bbox3 * bbox, const Vect3 * vect);
int
bbox3_contains_lat_lng(const Bbox3 * bbox, const LatLng * coord);
void
bbox3_from_segment_vect3(const Vect3 * vect1, const Vect3 * vect2, Bbox3 * bbox);
void
bbox3_from_segment_lat_lng(const LatLng * coord1, const LatLng * coord2, Bbox3 * bbox);
#endif
h3-pg-4.2.2/h3_postgis/src/wkb_indexing.c 0000664 0000000 0000000 00000013236 14752347156 0020145 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include
#include "constants.h"
#include "error.h"
#include "type.h"
#include "wkb_split.h"
#include "wkb_vect3.h"
#include "wkb.h"
#define SIGN(x) ((x < 0) ? -1 : (x > 0) ? 1 \
: 0)
#define ABS_LAT_MAX (degsToRads(89.9999))
#define SPLIT_ASSERT(condition, message) \
ASSERT( \
condition, \
ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, \
message)
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cell_to_boundary_wkb);
/* Converts CellBoundary coordinates to degrees in place */
static void
boundary_to_degs(CellBoundary * boundary);
/* Checks if CellBoundary is crossed by antimeridian */
static int
boundary_crosses_180_num(const CellBoundary * boundary);
/* Splits CellBoundary by antimeridian (and 0 meridian around poles) */
static void
boundary_split_180(const CellBoundary * boundary, CellBoundary * left, CellBoundary * right);
/*
Creates a boundary for polar cells with additional points on an antimeridian.
The functions adds 2 points (with lon. 180 and -180) for intersection with
antimeridian and 2 points on antimeridian close to the pole.
This allows to better display polar cells in e.g. Mercator projection.
*/
static void
boundary_split_180_polar(const CellBoundary * boundary, CellBoundary * res);
/* Finds the boundary of the index, converts to EWKB, splits the boundary by 180 meridian */
Datum
h3_cell_to_boundary_wkb(PG_FUNCTION_ARGS)
{
H3Index cell = PG_GETARG_H3INDEX(0);
bytea *wkb;
CellBoundary boundary;
int crossNum;
h3_assert(cellToBoundary(cell, &boundary));
crossNum = boundary_crosses_180_num(&boundary);
if (crossNum == 0)
{
/* Cell is not crossed by antimeridian */
boundary_to_degs(&boundary);
wkb = boundary_to_wkb(&boundary);
}
else if (crossNum == 1)
{
/* Cell boundary is crossed by antimeridian once */
CellBoundary split;
boundary_split_180_polar(&boundary, &split);
boundary_to_degs(&split);
wkb = boundary_to_wkb(&split);
}
else
{
/* Crossed by antimeridian */
CellBoundary parts[2];
boundary_split_180(&boundary, &parts[0], &parts[1]);
boundary_to_degs(&parts[0]);
boundary_to_degs(&parts[1]);
wkb = boundary_array_to_wkb(parts, 2);
}
PG_RETURN_BYTEA_P(wkb);
}
void
boundary_to_degs(CellBoundary * boundary)
{
LatLng *verts = boundary->verts;
const int numVerts = boundary->numVerts;
for (int v = 0; v < numVerts; v++)
{
verts[v].lng = radsToDegs(verts[v].lng);
verts[v].lat = radsToDegs(verts[v].lat);
}
}
int
boundary_crosses_180_num(const CellBoundary * boundary)
{
const int numVerts = boundary->numVerts;
const LatLng *verts = boundary->verts;
int num = 0;
for (int v = 0; v < numVerts; v++)
{
double lon = verts[v].lng;
double nextLon = verts[(v + 1) % numVerts].lng;
if (SIGN(lon) != SIGN(nextLon)
&& fabs(lon - nextLon) > M_PI)
{
++num;
}
}
return num;
}
void
boundary_split_180(const CellBoundary * boundary, CellBoundary * part1, CellBoundary * part2)
{
const int numVerts = boundary->numVerts;
const LatLng *verts = boundary->verts;
part1->numVerts = 0;
part2->numVerts = 0;
for (int v = 0; v < numVerts; v++)
{
int next = (v + 1) % numVerts;
double lon;
double nextLon;
CellBoundary *part;
lon = verts[v].lng;
nextLon = verts[next].lng;
part = (lon < 0) ? part1 : part2;
/* Add current vertex */
part->verts[part->numVerts++] = verts[v];
if (SIGN(lon) != SIGN(nextLon))
{
LatLng vert;
SPLIT_ASSERT(
fabs(lon - nextLon) > M_PI,
"Cell boundaries crossed by the Prime meridian "
"must be handled in `boundary_split_180_polar`");
vert.lat = split_180_lat(&verts[v], &verts[next]);
vert.lng = (lon < 0) ? -M_PI : M_PI;
/* Add split point */
/* current part */
part->verts[part->numVerts++] = vert;
/* next part */
vert.lng = -vert.lng;
part = (part == part1) ? part2 : part1;
part->verts[part->numVerts++] = vert;
}
}
}
void
boundary_split_180_polar(const CellBoundary * boundary, CellBoundary * res)
{
const int numVerts = boundary->numVerts;
const LatLng *verts = boundary->verts;
res->numVerts = 0;
for (int v = 0; v < numVerts; v++)
{
int next = (v + 1) % numVerts;
double lon;
double nextLon;
/* Add current vertex */
res->verts[res->numVerts++] = verts[v];
lon = verts[v].lng;
nextLon = verts[next].lng;
if (SIGN(lon) != SIGN(nextLon)
&& fabs(lon - nextLon) > M_PI)
{
LatLng vert;
double splitLat;
SPLIT_ASSERT(
v + 1 == res->numVerts,
"Cell boundaries crossed by antimeridian more than once "
"must be handled in `boundary_split_180`");
splitLat = split_180_lat(&verts[v], &verts[next]);
/* Add intersection point */
vert.lat = splitLat;
vert.lng = (lon < 0) ? -M_PI : M_PI;
res->verts[res->numVerts++] = vert;
/* Add points on antimeridian near the pole */
vert.lat = SIGN(vert.lat) * ABS_LAT_MAX;
res->verts[res->numVerts++] = vert;
vert.lng = -vert.lng;
res->verts[res->numVerts++] = vert;
/* Add intersection point */
vert.lat = splitLat;
res->verts[res->numVerts++] = vert;
}
}
}
h3-pg-4.2.2/h3_postgis/src/wkb_linked_geo.c 0000664 0000000 0000000 00000005510 14752347156 0020434 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include "wkb_linked_geo.h"
static void
free_linked_geo_loop(LinkedGeoLoop * loop);
int
count_linked_polygons(const LinkedGeoPolygon * multiPolygon)
{
int num = 0;
FOREACH_LINKED_POLYGON(multiPolygon, _)
++ num;
return num;
}
int
count_linked_geo_loops(const LinkedGeoPolygon * polygon)
{
int num = 0;
FOREACH_LINKED_LOOP(polygon, _)
++ num;
return num;
}
int
count_linked_lat_lng(const LinkedGeoLoop * loop)
{
int num = 0;
FOREACH_LINKED_LAT_LNG(loop, _)
++ num;
return num;
}
LinkedGeoPolygon *
copy_linked_geo_polygon(const LinkedGeoPolygon * polygon)
{
LinkedGeoPolygon *copy = palloc0(sizeof(LinkedGeoPolygon));
FOREACH_LINKED_LOOP(polygon, loop)
{
LinkedGeoLoop *loopCopy = copy_linked_geo_loop(loop);
add_linked_geo_loop(copy, loopCopy);
}
return copy;
}
LinkedGeoLoop *
copy_linked_geo_loop(const LinkedGeoLoop * loop)
{
LinkedGeoLoop *copy = palloc0(sizeof(LinkedGeoLoop));
FOREACH_LINKED_LAT_LNG(loop, latlng)
{
LinkedLatLng *latlng_copy = copy_linked_lat_lng(latlng);
add_linked_lat_lng(copy, latlng_copy);
}
return copy;
}
LinkedLatLng *
copy_linked_lat_lng(const LinkedLatLng * latlng)
{
LinkedLatLng *copy = palloc0(sizeof(LinkedLatLng));
copy->vertex = latlng->vertex;
return copy;
}
void
add_linked_geo_loop(LinkedGeoPolygon * polygon, LinkedGeoLoop * loop)
{
LinkedGeoLoop *last = polygon->last;
if (!last)
polygon->first = loop;
else
last->next = loop;
polygon->last = loop;
}
void
add_linked_lat_lng(LinkedGeoLoop * loop, LinkedLatLng * latlng)
{
LinkedLatLng *last = loop->last;
if (!last)
loop->first = latlng;
else
last->next = latlng;
loop->last = latlng;
}
void
free_linked_geo_polygon(LinkedGeoPolygon * multiPolygon)
{
LinkedGeoPolygon *polygon = multiPolygon;
while (polygon)
{
LinkedGeoPolygon *nextPolygon = polygon->next;
/* Free loops */
LinkedGeoLoop *loop = polygon->first;
while (loop)
{
LinkedGeoLoop *nextLoop = loop->next;
free_linked_geo_loop(loop);
loop = nextLoop;
}
pfree(polygon);
polygon = nextPolygon;
}
}
void
free_linked_geo_loop(LinkedGeoLoop * loop)
{
LinkedLatLng *latlng = loop->first;
while (latlng)
{
LinkedLatLng *next = latlng->next;
pfree(latlng);
latlng = next;
}
pfree(loop);
}
h3-pg-4.2.2/h3_postgis/src/wkb_linked_geo.h 0000664 0000000 0000000 00000004625 14752347156 0020447 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PGH3_WKB_LINKED_GEO_H
#define PGH3_WKB_LINKED_GEO_H
#include
#define _FOREACH_LINKED_ITEM(first, item, Type) \
for (Type *item = (first); \
item != NULL; \
item = item->next)
#define FOREACH_LINKED_POLYGON(multiPolygon, polygon) \
_FOREACH_LINKED_ITEM(multiPolygon, polygon, const LinkedGeoPolygon)
#define FOREACH_LINKED_POLYGON_NOCONST(multiPolygon, polygon) \
_FOREACH_LINKED_ITEM(multiPolygon, polygon, LinkedGeoPolygon)
#define FOREACH_LINKED_LOOP(polygon, loop) \
_FOREACH_LINKED_ITEM(polygon->first, loop, const LinkedGeoLoop)
#define FOREACH_LINKED_LOOP_NOCONST(polygon, loop) \
_FOREACH_LINKED_ITEM(polygon->first, loop, LinkedGeoLoop)
#define FOREACH_LINKED_LAT_LNG(loop, latlng) \
_FOREACH_LINKED_ITEM(loop->first, latlng, const LinkedLatLng)
#define FOREACH_LINKED_LAT_LNG_NOCONST(loop, latlng) \
_FOREACH_LINKED_ITEM(loop->first, latlng, LinkedLatLng)
/* NOTE: loop must contain at least 2 points */
#define FOREACH_LINKED_LAT_LNG_PAIR(loop, cur, next) \
for (const LinkedLatLng *cur = loop->first, *next = cur->next; \
cur != NULL; \
cur = cur->next, next = next->next ? next->next : loop->first)
int
count_linked_polygons(const LinkedGeoPolygon * multiPolygon);
int
count_linked_geo_loops(const LinkedGeoPolygon * polygon);
int
count_linked_lat_lng(const LinkedGeoLoop * loop);
LinkedGeoPolygon *
copy_linked_geo_polygon(const LinkedGeoPolygon * polygon);
LinkedGeoLoop *
copy_linked_geo_loop(const LinkedGeoLoop * loop);
LinkedLatLng *
copy_linked_lat_lng(const LinkedLatLng * latlng);
void
add_linked_geo_loop(LinkedGeoPolygon * polygon, LinkedGeoLoop * loop);
void
add_linked_lat_lng(LinkedGeoLoop * loop, LinkedLatLng * latlng);
void
free_linked_geo_polygon(LinkedGeoPolygon * multiPolygon);
#endif
h3-pg-4.2.2/h3_postgis/src/wkb_regions.c 0000664 0000000 0000000 00000005162 14752347156 0020005 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include // PG_FUNCTION_ARGS
#include // using arrays
#include "error.h"
#include "type.h"
#include "wkb_linked_geo.h"
#include "wkb_split.h"
#include "wkb.h"
PGDLLEXPORT PG_FUNCTION_INFO_V1(h3_cells_to_multi_polygon_wkb);
/* Converts LinkedGeoPolygon vertex coordinates to degrees in place */
static void
linked_geo_polygon_to_degs(LinkedGeoPolygon * multiPolygon);
Datum
h3_cells_to_multi_polygon_wkb(PG_FUNCTION_ARGS)
{
ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
LinkedGeoPolygon *linkedPolygon;
H3Error error;
int numHexes;
ArrayIterator iterator;
Datum value;
bool isnull;
H3Index *h3set;
bytea *wkb;
numHexes = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
h3set = palloc(numHexes * sizeof(H3Index));
/* Extract data from array into h3set */
iterator = array_create_iterator(array, 0, NULL);
numHexes = 0;
while (array_iterate(iterator, &value, &isnull))
{
h3set[numHexes++] = DatumGetH3Index(value);
}
/* produce hexagons into allocated memory */
linkedPolygon = palloc(sizeof(LinkedGeoPolygon));
h3_assert(cellsToLinkedMultiPolygon(h3set, numHexes, linkedPolygon));
if (is_linked_polygon_crossed_by_180(linkedPolygon))
{
/* Split by 180th meridian */
LinkedGeoPolygon *splitPolygon = split_linked_polygon_by_180(linkedPolygon);
linked_geo_polygon_to_degs(splitPolygon);
wkb = linked_geo_polygon_to_wkb(splitPolygon);
free_linked_geo_polygon(splitPolygon);
}
else
{
linked_geo_polygon_to_degs(linkedPolygon);
wkb = linked_geo_polygon_to_wkb(linkedPolygon);
}
destroyLinkedMultiPolygon(linkedPolygon);
pfree(linkedPolygon);
PG_RETURN_BYTEA_P(wkb);
}
void
linked_geo_polygon_to_degs(LinkedGeoPolygon * multiPolygon)
{
FOREACH_LINKED_POLYGON_NOCONST(multiPolygon, polygon)
{
FOREACH_LINKED_LOOP_NOCONST(polygon, loop)
{
FOREACH_LINKED_LAT_LNG_NOCONST(loop, latlng)
{
LatLng *vertex = &latlng->vertex;
vertex->lat = radsToDegs(vertex->lat);
vertex->lng = radsToDegs(vertex->lng);
}
}
}
}
h3-pg-4.2.2/h3_postgis/src/wkb_split.c 0000664 0000000 0000000 00000054646 14752347156 0017505 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include
#include
#include "constants.h"
#include "wkb_split.h"
#include "wkb_bbox3.h"
#include "wkb_linked_geo.h"
/*
Example: O-shaped polygon crossed by antimeridian.
After intersections are found and sorted by latitude,
intersection pairs 0-1 and 2-3 become segments in
exterior rings of the polygons in result.
|
+------(0)------+
| | |
| +---(1)---+ |
| | | | |
| | | | |
| +---(2)---+ |
| | |
+------(3)------+
|
Algorithm overview:
1. Initialization:
- empty array of vertices
- empty array of intersections
- empty array of interior rings not split by antimeridian
2. Processing polygon rings:
for each ring in the polygon:
if ring is crossed by prime or antimeridian:
for each segment in the ring:
- add first endpoint to array of vertices
if segment crosses antimeridian:
- add an intersection, link to first endpoint
else:
- add ring to array of non-split holes
3. Preparing data:
- sort intersections by latitude
- set sort order value for each intersection
4. Building multipolygon:
while there are unused vertices:
- create empty exterior ring
- start traversing vertex array forward starting from next unused vertex
while current vertex is unused:
- add current vertex to exterior ring
- get next vertex (depends on traversal direction)
if there is an intersection between vertices:
- add intersection point to exterior ring
- get adjacent intersection from sorted array
- add next intersection point to exterior ring
- update traversal direction based on intersection direction value
- get next vertex
- move to text vertex
- check which non-split holes are inside the exterior ring and add them to polygon
- add polygon to result
*/
#define SIGN(x) ((x < 0) ? -1 : (x > 0) ? 1 \
: 0)
#define FP_EQUAL(v1, v2) ((v1) == (v2) || fabs((v1) - (v2)) < DBL_EPSILON)
#define SPLIT_ASSERT(condition, message) \
ASSERT( \
condition, \
ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, \
message)
#define SPLIT_ASSERT_VALID_VERTEX_IDX(split, idx) \
SPLIT_ASSERT(0 <= idx && idx < split->vertexNum, "vertex index out of bounds")
#define INTERSECT_ARRAY_SIZE_INIT (4)
/* Direction of segment intersecting antimeridian: */
/* West-to-East or East-to-West */
typedef enum
{
SplitIntersectDir_None = 0,
SplitIntersectDir_WE,
SplitIntersectDir_EW
} SplitIntersectDir;
typedef struct
{
SplitIntersectDir dir;
bool isPrime;
double lat;
int vertexIdx;
int sortOrder;
} SplitIntersect;
typedef struct
{
const LatLng *latlngPtr;
int intersectIdx;
short sign; /* longitude sign is set explicitly in case
* longitude of the vertex itself is zero */
int link; /* links first and last vertices in a ring */
} SplitVertex;
typedef struct
{
/* Vertices */
int vertexNum;
SplitVertex *vertices;
/* Intersections */
int maxIntersectNum;
int intersectNum;
SplitIntersect *intersects;
SplitIntersect **sortedIntersects;
/* Non-split holes */
int holeNum;
const LinkedGeoLoop **holes;
} Split;
static bool
is_polygon_crossed(const LinkedGeoPolygon * polygon);
static LinkedGeoPolygon *
split_polygon(const LinkedGeoPolygon * polygon);
static bool
is_ring_crossed(const LinkedGeoLoop * ring);
static void
split_init(Split * split, int ringNum, int vertexNum);
static void
split_cleanup(Split * split);
static void
split_process_ring(Split * split, const LinkedGeoLoop * ring);
static void
split_prepare(Split * split);
static LinkedGeoPolygon *
split_create_multi_polygon(Split * split);
static int
split_add_vertex(Split * split, const LatLng * latlng);
static void
split_add_intersect_after(Split * split, int vertexIdx, SplitIntersectDir dir, bool isPrime, double lat);
static int
split_add_intersect(Split * split, SplitIntersectDir dir, bool isPrime, double lat);
static void
split_link_vertices(Split * split, int idx1, int idx2);
static void
split_add_hole(Split * split, const LinkedGeoLoop * hole);
static
void split_sort_intersects(Split * split);
static int
split_intersect_ptr_cmp(const void *a, const void *b);
static int
split_find_next_vertex(Split * split, int *start);
static LinkedGeoPolygon *
split_create_polygon_vertex(Split * split, int vertexIdx);
static void
split_polygon_assign_holes(Split * split, short sign, LinkedGeoPolygon * polygon);
static const SplitIntersect *
split_get_intersect_after(Split * split, int vertexIdx);
static void
split_intersect_get_lat_lng(const SplitIntersect * intersect, short sign, LatLng * latlng);
static int
count_polygon_vertices(const LinkedGeoPolygon * polygon, int *ringNum);
static short
lat_lng_ring_pos(const LinkedGeoLoop * ring, short sign, const Bbox3 * bbox, const LatLng * latlng);
static short
segment_intersect(const Vect3 * v1, const Vect3 * v2, const Vect3 * u1, const Vect3 * u2);
static short
point_segment_pos(const Vect3 * v1, const Vect3 * v2, const Vect3 * p);
static void
add_lat_lng(LinkedGeoLoop * loop, const LatLng * latlng);
bool
is_linked_polygon_crossed_by_180(const LinkedGeoPolygon * multiPolygon)
{
FOREACH_LINKED_POLYGON(multiPolygon, polygon)
{
if (is_polygon_crossed(polygon))
return true;
}
return false;
}
LinkedGeoPolygon *
split_linked_polygon_by_180(const LinkedGeoPolygon * multiPolygon)
{
LinkedGeoPolygon *result = NULL;
LinkedGeoPolygon *last = NULL;
FOREACH_LINKED_POLYGON(multiPolygon, polygon)
{
/* Split or copy next polygon */
LinkedGeoPolygon *nextResult = is_polygon_crossed(polygon)
? split_polygon(polygon)
: copy_linked_geo_polygon(polygon);
/* Add to result */
if (!result)
{
result = nextResult;
last = nextResult;
}
else
{
last->next = nextResult;
}
while (last->next)
last = last->next;
}
return result;
}
double
split_180_lat(const LatLng * coord1, const LatLng * coord2)
{
Vect3 p1,
p2,
normal,
s;
double y;
/* Normal of circle containing points: normal = p1 x p2 */
vect3_from_lat_lng(coord1, &p1);
vect3_from_lat_lng(coord2, &p2);
vect3_cross(&p1, &p2, &normal);
/* y coordinate of 0/180 meridian circle normal */
y = (coord1->lng < 0 || coord2->lng > 0) ? -1 : 1;
/* Circle plane intersection vector: s = (p1 x p2) x {0, y, 0} */
s.x = -(normal.z * y);
s.y = 0;
s.z = normal.x * y;
vect3_normalize(&s); /* intersection point coordinates on unit
* sphere */
return asin(s.z); /* latitude */
}
bool
is_polygon_crossed(const LinkedGeoPolygon * polygon)
{
return polygon->first
? is_ring_crossed(polygon->first)
: false;
}
LinkedGeoPolygon *
split_polygon(const LinkedGeoPolygon * polygon)
{
int ringNum,
vertexNum;
Split split;
LinkedGeoPolygon *result;
/* Init data */
vertexNum = count_polygon_vertices(polygon, &ringNum);
split_init(&split, ringNum, vertexNum);
/* Process rings */
FOREACH_LINKED_LOOP(polygon, ring)
{
if (ring == polygon->first || is_ring_crossed(ring))
split_process_ring(&split, ring);
else
split_add_hole(&split, ring);
}
/* Prepare data */
split_prepare(&split);
/* Build result */
result = split_create_multi_polygon(&split);
/* Cleanup */
split_cleanup(&split);
return result;
}
bool
is_ring_crossed(const LinkedGeoLoop * ring)
{
if (!ring->first || !ring->first->next)
return false;
FOREACH_LINKED_LAT_LNG_PAIR(ring, cur, next)
{
double lng = cur->vertex.lng;
double nextLng = next->vertex.lng;
if (SIGN(lng) != SIGN(nextLng)
&& fabs(lng - nextLng) > M_PI)
{
return true;
}
}
return false;
}
void
split_init(Split * split, int ringNum, int vertexNum)
{
*split = (Split)
{
0
};
split->vertices = palloc0(vertexNum * sizeof(SplitVertex));
split->maxIntersectNum = INTERSECT_ARRAY_SIZE_INIT;
split->intersects = palloc0(split->maxIntersectNum * sizeof(SplitIntersect));
if (ringNum > 1)
split->holes = palloc0((ringNum - 1) * sizeof(LinkedGeoLoop *));
}
void
split_cleanup(Split * split)
{
if (split->vertices)
pfree(split->vertices);
if (split->intersects)
pfree(split->intersects);
if (split->sortedIntersects)
pfree(split->sortedIntersects);
if (split->holes)
pfree(split->holes);
*split = (Split)
{
0
};
}
void
split_process_ring(Split * split, const LinkedGeoLoop * ring)
{
short sign = 0;
int vertexIdx = -1;
int firstVertexIdx = -1;
SPLIT_ASSERT(ring->first && ring->first->next, "polygon ring must have at least 2 vertices");
FOREACH_LINKED_LAT_LNG_PAIR(ring, cur, next)
{
double lng,
nextLng;
short nextSign;
/* Add vertex */
vertexIdx = split_add_vertex(split, &cur->vertex);
if (firstVertexIdx < 0)
firstVertexIdx = vertexIdx;
lng = cur->vertex.lng;
nextLng = next->vertex.lng;
nextSign = SIGN(nextLng);
if (sign == 0)
{
sign = SIGN(lng);
if (sign != 0)
{
/* Set sign for vertices traversed so far */
for (int i = firstVertexIdx; i <= vertexIdx; ++i)
split->vertices[i].sign = sign;
}
}
else
{
/* Set vertex sign */
split->vertices[vertexIdx].sign = sign;
}
if (sign != 0 && nextSign != 0 && nextSign != sign)
{
/* Prime or antimeridian crossed */
/* Add intersection after current vertex */
SplitIntersectDir dir = (sign < 0) ? SplitIntersectDir_WE : SplitIntersectDir_EW;
int isPrime = (fabs(lng - nextLng) < M_PI);
double lat = split_180_lat(&cur->vertex, &next->vertex);
split_add_intersect_after(split, vertexIdx, dir, isPrime, lat);
sign = nextSign;
}
}
/* Link first and last vertices */
split_link_vertices(split, firstVertexIdx, vertexIdx);
}
void
split_prepare(Split * split)
{
split_sort_intersects(split);
}
LinkedGeoPolygon *
split_create_multi_polygon(Split * split)
{
LinkedGeoPolygon *multiPolygon = NULL;
LinkedGeoPolygon *lastPolygon = NULL;
int vertexIdxStart = 0;
while (true)
{
LinkedGeoPolygon *polygon;
/* Get next unused vertex */
int vertexIdx = split_find_next_vertex(split, &vertexIdxStart);
if (vertexIdx < 0)
break; /* done */
/* Create next polygon */
polygon = split_create_polygon_vertex(split, vertexIdx);
/* Add to result */
if (!multiPolygon)
multiPolygon = polygon;
else
lastPolygon->next = polygon;
lastPolygon = polygon;
}
return multiPolygon;
}
int
split_add_vertex(Split * split, const LatLng * latlng)
{
int idx = split->vertexNum++;
SplitVertex *vertex = &split->vertices[idx];
vertex->latlngPtr = latlng;
vertex->intersectIdx = -1;
vertex->sign = 0;
vertex->link = -1;
return idx;
}
void
split_add_intersect_after(Split * split, int vertexIdx, SplitIntersectDir dir, bool isPrime, double lat)
{
int idx = split_add_intersect(split, dir, isPrime, lat);
SplitIntersect *intersect = &split->intersects[idx];
intersect->vertexIdx = vertexIdx;
split->vertices[vertexIdx].intersectIdx = idx;
}
int
split_add_intersect(Split * split, SplitIntersectDir dir, bool isPrime, double lat)
{
int idx;
SplitIntersect *intersect;
if (split->intersectNum == split->maxIntersectNum)
{
/* Reallocate memory for intersections */
int maxNum = split->maxIntersectNum * 2;
if (maxNum > split->vertexNum)
maxNum = split->vertexNum;
split->intersects = repalloc(split->intersects, maxNum * sizeof(SplitIntersect));
split->maxIntersectNum = maxNum;
}
idx = split->intersectNum++;
intersect = &split->intersects[idx];
intersect->dir = dir;
intersect->isPrime = isPrime;
intersect->lat = lat;
intersect->vertexIdx = -1;
intersect->sortOrder = -1;
return idx;
}
void
split_link_vertices(Split * split, int idx1, int idx2)
{
SPLIT_ASSERT_VALID_VERTEX_IDX(split, idx1);
SPLIT_ASSERT_VALID_VERTEX_IDX(split, idx2);
split->vertices[idx1].link = idx2;
split->vertices[idx2].link = idx1;
}
void
split_add_hole(Split * split, const LinkedGeoLoop * hole)
{
split->holes[split->holeNum++] = hole;
}
void
split_sort_intersects(Split * split)
{
SPLIT_ASSERT(split->intersectNum % 2 == 0, "intersection number must be even");
/* Collect intesection pointers */
split->sortedIntersects = palloc(split->intersectNum * sizeof(SplitIntersect *));
for (int i = 0; i < split->intersectNum; ++i)
{
SplitIntersect *intersect = &split->intersects[i];
split->sortedIntersects[i] = intersect;
}
/* Sort intersection pointers */
qsort(
split->sortedIntersects,
split->intersectNum,
sizeof(SplitIntersect *),
&split_intersect_ptr_cmp);
/* Assign sort order values to intersections */
for (int i = 0; i < split->intersectNum; ++i)
split->sortedIntersects[i]->sortOrder = i;
}
/**
Sort intersections by latitude
Sort value for points on prime meridian:
* 180 - lat, if lat >= 0
* -180 - lat, if lat < 0
-90 0 90
------*----------+----------*------>
meridian: prime antimeridian prime
value: -180-lat lat 180-lat
*/
int
split_intersect_ptr_cmp(const void *a, const void *b)
{
const SplitIntersect *i1 = *((const SplitIntersect **) a);
const SplitIntersect *i2 = *((const SplitIntersect **) b);
double v1,
v2;
v1 = i1->lat;
if (i1->isPrime)
v1 = ((v1 < 0) ? -M_PI : M_PI) - v1;
v2 = i2->lat;
if (i2->isPrime)
v2 = ((v2 < 0) ? -M_PI : M_PI) - v2;
return (v1 == v2)
? 0
: (v1 < v2) ? -1 : 1;
}
int
split_find_next_vertex(Split * split, int *start)
{
for (int i = *start; i < split->vertexNum; ++i)
{
if (split->vertices[i].latlngPtr)
{
*start = i + 1;
return i;
}
}
return -1;
}
LinkedGeoPolygon *
split_create_polygon_vertex(Split * split, int vertexIdx)
{
LinkedGeoPolygon *polygon;
LinkedGeoLoop *loop;
int idx,
nextIdx,
intersectIdx;
SplitVertex *vertex;
const SplitIntersect *intersect;
short sign,
step;
polygon = palloc0(sizeof(LinkedGeoPolygon));
loop = palloc0(sizeof(LinkedGeoLoop));
add_linked_geo_loop(polygon, loop);
idx = vertexIdx;
vertex = &split->vertices[idx];
sign = vertex->sign;
step = 1; /* vertex array traversal direction */
while (vertex->latlngPtr)
{
/* Add vertex */
add_lat_lng(loop, vertex->latlngPtr);
vertex->latlngPtr = NULL;
/*
* Get indices of the other segment endpoint and potential
* intersection
*/
if (vertex->link > -1 && ((step > 0) == (idx > vertex->link)))
{
nextIdx = vertex->link;
/* Potential intersection is after last ring vertex */
intersectIdx = (nextIdx > idx) ? nextIdx : idx;
}
else
{
nextIdx = idx + step;
/* Potential intersection is after first vertex */
intersectIdx = (nextIdx > idx) ? idx : nextIdx;
}
/* Is there an intersection after the vertex? */
SPLIT_ASSERT_VALID_VERTEX_IDX(split, intersectIdx);
intersect = split_get_intersect_after(split, intersectIdx);
if (intersect)
{
LatLng latlng;
int intersectSortOrder;
/* Add intersection vertex */
split_intersect_get_lat_lng(intersect, sign, &latlng);
add_lat_lng(loop, &latlng);
/* Find next intersection */
intersectSortOrder = (intersect->sortOrder % 2 == 0)
? intersect->sortOrder + 1
: intersect->sortOrder - 1;
intersect = split->sortedIntersects[intersectSortOrder];
intersectIdx = intersect->vertexIdx;
/* Add next intersection vertex */
split_intersect_get_lat_lng(intersect, sign, &latlng);
add_lat_lng(loop, &latlng);
/*
* Does intersecting segment end in the same hemisphere where the
* polygon is located?
*/
step = ((sign > 0) == (intersect->dir == SplitIntersectDir_WE)) ? 1 : -1;
if (step > 0)
{
/* Next vertex is the second endpoint of a segment */
const SplitVertex *segmentStart = &split->vertices[intersectIdx];
if (segmentStart->link > -1 && intersectIdx > segmentStart->link)
nextIdx = segmentStart->link;
else
nextIdx = intersectIdx + 1;
}
else
{
/* Next vertex is the first endpoint of a segment */
nextIdx = intersectIdx;
}
}
idx = nextIdx;
vertex = &split->vertices[idx];
}
/* Assign holes */
split_polygon_assign_holes(split, sign, polygon);
return polygon;
}
void
split_polygon_assign_holes(Split * split, short sign, LinkedGeoPolygon * polygon)
{
const LinkedGeoLoop *outerLoop;
Bbox3 bbox;
outerLoop = polygon->first;
bbox3_from_linked_loop(outerLoop, &bbox);
for (int i = 0; i < split->holeNum; ++i)
{
const LinkedGeoLoop *hole;
short pos = 0;
hole = split->holes[i];
if (!hole)
continue;
/* Check if hole vertices are inside the outher shell of the polygon */
FOREACH_LINKED_LAT_LNG(hole, cur)
{
pos = lat_lng_ring_pos(outerLoop, sign, &bbox, &cur->vertex);
if (pos != 0)
break; /* vertex is either inside or outside */
}
if (pos != -1)
{
/* Add hole loop copy to polygon */
LinkedGeoLoop *hole_copy = copy_linked_geo_loop(hole);
add_linked_geo_loop(polygon, hole_copy);
/* Remove hole from the list */
split->holes[i] = NULL;
}
}
}
const SplitIntersect *
split_get_intersect_after(Split * split, int vertexIdx)
{
int idx;
SPLIT_ASSERT_VALID_VERTEX_IDX(split, vertexIdx);
idx = split->vertices[vertexIdx].intersectIdx;
return (idx > -1)
? &split->intersects[idx]
: NULL;
}
void
split_intersect_get_lat_lng(const SplitIntersect * intersect, short sign, LatLng * latlng)
{
latlng->lat = intersect->lat;
if (intersect->isPrime)
latlng->lng = 0.0;
else
latlng->lng = (sign > 0) ? M_PI : -M_PI;
}
int
count_polygon_vertices(const LinkedGeoPolygon * polygon, int *ringNum)
{
int num = 0;
if (ringNum)
*ringNum = 0;
FOREACH_LINKED_LOOP(polygon, ring)
{
if (ringNum)
++(*ringNum);
num += count_linked_lat_lng(ring);
}
return num;
}
short
lat_lng_ring_pos(const LinkedGeoLoop * ring, short sign, const Bbox3 * bbox, const LatLng * latlng)
{
short signLatlng;
Vect3 vect,
outVect;
LatLng out;
int intersectNum = 0;
Vect3 curVect,
nextVect;
/* Check longitude sign */
signLatlng = SIGN(latlng->lng);
if (signLatlng != 0 && signLatlng != sign)
return -1;
vect3_from_lat_lng(latlng, &vect);
/* Check bbox */
if (!bbox3_contains_vect3(bbox, &vect))
return -1;
/* Create a point that's guaranteed to be outside the polygon */
out.lng = (latlng->lng == 0) ? -sign * 1e-10 : -latlng->lng;
out.lat = latlng->lat;
vect3_from_lat_lng(&out, &outVect);
/* Check if ring is a single vertex exactly matching the point */
if (!ring->first->next)
return true;
/*
* Count a number of intersections between the ring and (latlng, out)
* segment
*/
intersectNum = 0;
vect3_from_lat_lng(&ring->first->vertex, &curVect);
FOREACH_LINKED_LAT_LNG_PAIR(ring, cur, next)
{
short intersect;
/* Check if point matches ring vertex */
if (vect3_eq(&vect, &curVect))
return 0;
/* Next vertex */
vect3_from_lat_lng(&next->vertex, &nextVect);
if (!vect3_eq(&curVect, &nextVect))
{
intersect = segment_intersect(&curVect, &nextVect, &vect, &outVect);
if (intersect == 0)
return 0; /* point on ring segment */
if (intersect > 0)
++intersectNum;
}
curVect = nextVect;
}
return (intersectNum % 2 == 0) ? -1 : 1;
}
short
segment_intersect(const Vect3 * v1, const Vect3 * v2, const Vect3 * u1, const Vect3 * u2)
{
Vect3 vn,
un;
short v1Side,
v2Side,
u1Side,
u2Side;
double normalDot;
/* Normals of V and U planes */
vect3_cross(v1, v2, &vn);
vect3_normalize(&vn);
vect3_cross(u1, u2, &un);
vect3_normalize(&un);
/* Are the planes the same? */
normalDot = vect3_dot(&vn, &un);
if (FP_EQUAL(fabs(normalDot), 1.0))
{
short ret = point_segment_pos(v1, v2, u1);
if (ret == -1)
ret = point_segment_pos(v1, v2, u2);
if (ret == -1)
ret = point_segment_pos(u1, u2, v1);
if (ret == -1)
ret = point_segment_pos(u1, u2, v2);
return ret;
}
/* Which side of other segment are segment endpoints? */
v1Side = SIGN(vect3_dot(&un, v1));
v2Side = SIGN(vect3_dot(&un, v2));
u1Side = SIGN(vect3_dot(&vn, u1));
u2Side = SIGN(vect3_dot(&vn, u2));
/* v1 and v2 are on the same side of U plane */
if (v1Side == v2Side && v1Side != 0)
return -1;
/* u1 and u2 are on the same side of V plane */
if (u1Side == u2Side && u2Side != 0)
return -1;
if (v1Side != v2Side && (v1Side + v2Side) == 0
&& u1Side != u2Side && (u1Side + u2Side) == 0)
{
/* Intersection point */
Vect3 intersect;
vect3_cross(&vn, &un, &intersect);
vect3_normalize(&intersect);
/* Is intersection point inside both arcs? */
if (point_segment_pos(v1, v2, &intersect) != -1
&& point_segment_pos(u1, u2, &intersect) != -1)
{
return 1;
}
/* Try antipodal intersection point */
vect3_scale(&intersect, -1.0);
if (point_segment_pos(v1, v2, &intersect) != -1
&& point_segment_pos(u1, u2, &intersect) != -1)
{
return 1;
}
return -1;
}
return 0;
}
short
point_segment_pos(const Vect3 * v1, const Vect3 * v2, const Vect3 * p)
{
Vect3 middle;
double minSimilarity;
/* Check if point is same as one of segment endpoints */
if (vect3_eq(p, v1) || vect3_eq(p, v2))
return 0;
/* Find vector bisecting anble between segment endpoint vectors */
vect3_sum(v1, v2, &middle);
vect3_normalize(&middle);
/* How similar are endpoint vectors to bisecting vector? */
minSimilarity = vect3_dot(v1, &middle);
if (fabs(1.0 - minSimilarity) > 1e-10)
{
/* Segment is long enough to use dot product test. */
/* If point vector is more similar to bisecting vector, */
/*
* then it must be closer to center, so the point is inside the
* segment.
*/
return (vect3_dot(p, &middle) > minSimilarity) ? 1 : -1;
}
else
{
/* Segment is too short to use dot product test. */
/*
* Check if vectors from segment endpoints to the point are in
* opposite directions.
*/
Vect3 d1,
d2;
vect3_diff(p, v1, &d1);
vect3_normalize(&d1);
vect3_diff(p, v2, &d2);
vect3_normalize(&d2);
return (vect3_dot(&d1, &d2) < 0.0) ? 1 : -1;
}
}
void
add_lat_lng(LinkedGeoLoop * loop, const LatLng * latlng)
{
LinkedLatLng *linked;
if (loop->last)
{
/* Does new vertex exactly match the last one? */
const LatLng *last = &loop->last->vertex;
if (last->lat == latlng->lat && last->lng == latlng->lng)
return;
}
linked = palloc0(sizeof(LinkedLatLng));
linked->vertex.lat = latlng->lat;
linked->vertex.lng = latlng->lng;
add_linked_lat_lng(loop, linked);
}
h3-pg-4.2.2/h3_postgis/src/wkb_split.h 0000664 0000000 0000000 00000001706 14752347156 0017477 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PGH3_WKB_SPLIT_H
#define PGH3_WKB_SPLIT_H
#include
#include
#include "error.h"
bool
is_linked_polygon_crossed_by_180(const LinkedGeoPolygon * multiPolygon);
LinkedGeoPolygon *
split_linked_polygon_by_180(const LinkedGeoPolygon * multiPolygon);
double
split_180_lat(const LatLng * coord1, const LatLng * coord2);
#endif
h3-pg-4.2.2/h3_postgis/src/wkb_vect3.c 0000664 0000000 0000000 00000004436 14752347156 0017366 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include "wkb_vect3.h"
#define FP_EQUAL(v1, v2) ((v1) == (v2) || fabs((v1) - (v2)) < DBL_EPSILON)
void
vect3_from_lat_lng(const LatLng * coord, Vect3 * vect)
{
vect->x = cos(coord->lat) * cos(coord->lng);
vect->y = cos(coord->lat) * sin(coord->lng);
vect->z = sin(coord->lat);
}
void
vect3_to_lat_lng(const Vect3 * vect, LatLng * coord)
{
coord->lng = atan2(vect->y, vect->x);
coord->lat = asin(vect->z);
}
int
vect3_eq(const Vect3 * vect1, const Vect3 * vect2)
{
return FP_EQUAL(vect1->x, vect2->x)
&& FP_EQUAL(vect1->y, vect2->y)
&& FP_EQUAL(vect1->z, vect2->z);
}
void
vect3_normalize(Vect3 * vect)
{
double len = sqrt(vect->x * vect->x + vect->y * vect->y + vect->z * vect->z);
if (len > 0)
{
vect->x = vect->x / len;
vect->y = vect->y / len;
vect->z = vect->z / len;
}
else
{
vect->x = 0;
vect->y = 0;
vect->z = 0;
}
}
void
vect3_sum(const Vect3 * vect1, const Vect3 * vect2, Vect3 * sum)
{
sum->x = vect1->x + vect2->x;
sum->y = vect1->y + vect2->y;
sum->z = vect1->z + vect2->z;
}
void
vect3_diff(const Vect3 * vect1, const Vect3 * vect2, Vect3 * diff)
{
diff->x = vect1->x - vect2->x;
diff->y = vect1->y - vect2->y;
diff->z = vect1->z - vect2->z;
}
void
vect3_scale(Vect3 * vect, double factor)
{
vect->x *= factor;
vect->y *= factor;
vect->z *= factor;
}
void
vect3_cross(const Vect3 * vect1, const Vect3 * vect2, Vect3 * prod)
{
prod->x = vect1->y * vect2->z - vect1->z * vect2->y;
prod->y = vect1->z * vect2->x - vect1->x * vect2->z;
prod->z = vect1->x * vect2->y - vect1->y * vect2->x;
}
double
vect3_dot(const Vect3 * vect1, const Vect3 * vect2)
{
return (vect1->x * vect2->x) + (vect1->y * vect2->y) + (vect1->z * vect2->z);
}
h3-pg-4.2.2/h3_postgis/src/wkb_vect3.h 0000664 0000000 0000000 00000002504 14752347156 0017365 0 ustar 00root root 0000000 0000000 /*
* Copyright 2024 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PGH3_VECT3_H
#define PGH3_VECT3_H
#include
#include
#include
typedef struct
{
double x;
double y;
double z;
} Vect3;
void
vect3_from_lat_lng(const LatLng * coord, Vect3 * vect);
void
vect3_to_lat_lng(const Vect3 * vect, LatLng * coord);
int
vect3_eq(const Vect3 * vect1, const Vect3 * vect2);
void
vect3_normalize(Vect3 * vect);
void
vect3_sum(const Vect3 * vect1, const Vect3 * vect2, Vect3 * sum);
void
vect3_diff(const Vect3 * vect1, const Vect3 * vect2, Vect3 * diff);
void
vect3_scale(Vect3 * vect, double factor);
void
vect3_cross(const Vect3 * vect1, const Vect3 * vect2, Vect3 * prod);
double
vect3_dot(const Vect3 * vect1, const Vect3 * vect2);
#endif
h3-pg-4.2.2/h3_postgis/test/ 0000775 0000000 0000000 00000000000 14752347156 0015514 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3_postgis/test/CMakeLists.txt 0000664 0000000 0000000 00000001251 14752347156 0020253 0 ustar 00root root 0000000 0000000 set(TESTS
postgis
rasters
)
if(PostgreSQL_REGRESS)
add_test(
NAME "h3_postgis_regress"
COMMAND ${PostgreSQL_REGRESS}
--temp-instance=${CMAKE_BINARY_DIR}/tmp
--bindir=${PostgreSQL_BIN_DIR}
--inputdir=${CMAKE_CURRENT_SOURCE_DIR}
--outputdir=${CMAKE_CURRENT_BINARY_DIR}
--load-extension h3
--load-extension postgis
--load-extension postgis_raster
--load-extension h3_postgis
${TESTS}
)
endif()
if(PostgreSQL_VALIDATE_EXTUPGRADE)
add_test(
NAME "h3_postgis_validate_extupgrade"
COMMAND pg_validate_extupgrade
--extname h3_postgis
--from 4.0.0
--to ${INSTALL_VERSION}
)
endif()
h3-pg-4.2.2/h3_postgis/test/expected/ 0000775 0000000 0000000 00000000000 14752347156 0017315 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3_postgis/test/expected/postgis.out 0000664 0000000 0000000 00000021010 14752347156 0021530 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- Variables for testing
\set resolution 10
\set hexagon '\'8a63a9a99047fff\''
\set meter ST_SetSRID(ST_Point(6196902.235389061,1413172.0833316022), 3857)
\set degree ST_SetSRID(ST_Point(55.6677199224442,12.592131261648213), 4326)
\set edgecross '\'8003fffffffffff\'::h3index'
\set polar '\'81f2bffffffffff\'::h3index'
\set lat1 84.76455330449812
\set lat2 89.980298101841
\set epsilon 0.0000000000001
\set longPathEndpoint1 '\'842ca2bffffffff\'::h3index'
\set longPathEndpoint2 '\'842e611ffffffff\'::h3index'
-- polygon with 2 holes
\set with2holes '\'POLYGON((31.6520834 68.9912098,31.6521263 68.9910944,31.6530919 68.9912021,31.6540789 68.9912944,31.6550016 68.991279,31.6553449 68.9910175,31.6554737 68.9907713,31.6559458 68.990402,31.6563535 68.9900789,31.6568684 68.9897404,31.6569543 68.9895711,31.6567182 68.988671,31.6569114 68.9881786,31.6571045 68.9879401,31.6567611 68.9875708,31.6570401 68.9873015,31.6576195 68.986986,31.6581774 68.9868398,31.6584992 68.9870553,31.6586065 68.9872707,31.6590786 68.9873246,31.6594219 68.9872091,31.6596579 68.9870091,31.6596579 68.9867706,31.6601515 68.9866167,31.6607308 68.9864551,31.660409 68.986232,31.6601729 68.9860627,31.6605806 68.9859319,31.6614818 68.9859011,31.6620183 68.9857087,31.6622972 68.9854471,31.6628337 68.9852932,31.6633701 68.9852855,31.663928 68.9855702,31.6640782 68.9859088,31.6636705 68.9862166,31.6639924 68.9864859,31.664443 68.9868629,31.664679 68.9872091,31.6642928 68.9873784,31.6641426 68.9876939,31.6650009 68.9879016,31.6652155 68.9881555,31.6653657 68.9883709,31.6659665 68.9886941,31.6662884 68.9889941,31.666739 68.989248,31.6669321 68.9891095,31.6670394 68.9888787,31.6681123 68.9889326,31.6687345 68.989325,31.6692495 68.9895865,31.6701937 68.9897635,31.6710949 68.9897404,31.6725325 68.9897558,31.6733479 68.9898558,31.6743135 68.9904097,31.674571 68.990702,31.6747641 68.9909328,31.6745066 68.9911405,31.6738844 68.9912636,31.6731977 68.9914175,31.6734981 68.9916098,31.6739487 68.9915713,31.6744852 68.9915175,31.6750431 68.9914021,31.6751718 68.9911944,31.6752147 68.9910405,31.6756439 68.9910636,31.6765451 68.9912021,31.6777253 68.9912944,31.6784119 68.9912482,31.6790771 68.9911559,31.6793346 68.9913021,31.6787553 68.9916713,31.678133 68.9920867,31.6780472 68.9924483,31.6782617 68.9927098,31.6792702 68.9941098,31.6794419 68.9943636,31.6801715 68.9945328,31.6817808 68.9946328,31.6825533 68.9948174,31.6827249 68.995202,31.683712 68.9957404,31.6840124 68.9962634,31.684699 68.9965556,31.6848492 68.9968171,31.6841197 68.9969632,31.6831326 68.9969479,31.6827464 68.9969171,31.6824031 68.9968556,31.6821456 68.9967248,31.6813302 68.9968248,31.6810083 68.9971094,31.6806865 68.9971863,31.6802358 68.9971401,31.6792702 68.9967018,31.6787553 68.9963864,31.6780901 68.9958327,31.6777038 68.9956788,31.6766095 68.9955635,31.6762233 68.9954943,31.6759658 68.9952943,31.6753649 68.9951481,31.6746354 68.9952712,31.6735625 68.9951866,31.6728329 68.9951866,31.6726398 68.9953943,31.6719746 68.9955173,31.6709661 68.9954173,31.6704941 68.9951558,31.6700434 68.9948251,31.669743 68.9944867,31.6695285 68.994179,31.6693783 68.9939329,31.6690779 68.993756,31.6680479 68.9935867,31.663692 68.9929329,31.6628551 68.9927637,31.661675 68.9927637,31.6610527 68.9929637,31.6605377 68.9929714,31.6599583 68.9929406,31.6588855 68.9928175,31.658349 68.992656,31.657598 68.9923714,31.6567826 68.9922329,31.6552162 68.9920714,31.6541648 68.9919175,31.6531348 68.9916483,31.6523838 68.9914175,31.6520834 68.9912098),(31.657185 68.990202,31.657244 68.9903482,31.6574585 68.9903809,31.6578555 68.9903597,31.6581452 68.9902539,31.6583651 68.9900924,31.6582364 68.9899366,31.6578716 68.9898616,31.6575766 68.9898731,31.6573083 68.9899808,31.6572332 68.9900731,31.657185 68.990202),(31.6590196 68.9886094,31.6591644 68.9887229,31.6594058 68.9888537,31.6595936 68.9889557,31.6596794 68.9890441,31.6597116 68.9891345,31.659776 68.9892519,31.6599476 68.9891903,31.660012 68.9890614,31.6601139 68.9889383,31.6602641 68.9888326,31.6602749 68.988721,31.6601998 68.988596,31.6600603 68.988369,31.6601676 68.9882709,31.6604251 68.9882844,31.6607255 68.9882728,31.6609454 68.9881767,31.6605592 68.9880901,31.6604841 68.9879689,31.6603607 68.9878497,31.660071 68.9878843,31.6597867 68.9879959,31.6593575 68.9880305,31.6592127 68.9881132,31.6592234 68.9882421,31.6592127 68.9883786,31.6590196 68.9886094))\''::geometry(POLYGON)
-- S-shaped polygon with 4 holes crossing antimeridian
\set transmeridianWithHoles '\'POLYGON((-170 12.5, 170 12.5, 170 7.5, -175 7.5, -175 2.5, 170 2.5, 170 -12.5, -170 -12.5, -170 -7.5, 175 -7.5, 175 -2.5, -170 -2.5, -170 12.5), (-176 11.5, -179 11.5, -179 8.5, -176 8.5, -176 11.5), (174 11.5, 171 11.5, 171 8.5, 174 8.5, 174 11.5), (174 -3.5, 171 -3.5, 171 -6.5, 174 -6.5, 174 -3.5),(-176 -8.5, -179 -8.5, -179 -11.5, -176 -11.5, -176 -8.5))\''::geometry(POLYGON)
-- multipolygon with 2 polygons: one crossing and one not crossing antimeridian
\set transmeridianMulti '\'MULTIPOLYGON(((-175 50, -175 55, 175 55, 175 50, -175 50)), ((170 50, 170 55, 165 55, 165 50, 170 50)))\''::geometry(MULTIPOLYGON)
SELECT h3_lat_lng_to_cell(:degree, :resolution) = '8a63a9a99047fff';
t
-- meters are NOT reprojected
SELECT h3_lat_lng_to_cell(:meter, :resolution) <> '8a63a9a99047fff';
t
-- check back/forth conversion return same hex
SELECT h3_lat_lng_to_cell(h3_cell_to_geometry(:hexagon), :resolution) = '8a63a9a99047fff';
t
-- check num points in boundary
SELECT ST_NPoints(h3_cell_to_boundary_geometry(:hexagon)) = 7;
t
-- test strict h3_lat_lng_to_cell throws for bad latlon
CREATE FUNCTION h3_test_postgis_nounit() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_lat_lng_to_cell(POINT(360, 2.592131261648213), 1);
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
SET h3.strict TO true;
SELECT h3_test_postgis_nounit();
t
SET h3.strict TO false;
DROP FUNCTION h3_test_postgis_nounit;
-- Test wraparound
\set lon 55.6677199224442
\set lat 12.592131261648213
SELECT h3_lat_lng_to_cell(POINT(:lon, :lat), 7)
= h3_lat_lng_to_cell(POINT(:lon + 360, :lat), 7);
t
SELECT h3_lat_lng_to_cell(POINT(:lon, :lat ), 7)
= h3_lat_lng_to_cell(POINT(:lon, :lat + 360), 7);
t
-- test h3_grid_path_cells_recursive works for long path
SELECT COUNT(*) > 0 FROM (
SELECT h3_grid_path_cells_recursive(:longPathEndpoint1, :longPathEndpoint1)
) q;
t
-- h3_polygon_to_cells works for polygon with two holes
SELECT COUNT(*) = 48 FROM (
SELECT h3_polygon_to_cells(:with2holes, 10)
) q;
t
--
-- Test h3_cell_to_boundary_wkb
--
-- polyfill of geo boundary returns original index
SELECT h3_polygon_to_cells(h3_cell_to_boundary(:hexagon)::geometry::polygon, null, :resolution) = :hexagon;
t
-- the boundary of a non-edgecrossing index is a polygon
SELECT GeometryType(h3_cell_to_boundary_wkb(:hexagon)::geometry) LIKE 'POLYGON';
t
-- the boundary of an edgecrossing index is a multipolygon when split
SELECT GeometryType(h3_cell_to_boundary_wkb(:edgecross)::geometry) LIKE 'MULTIPOLYGON';
t
-- the boundary of a polar cell is a polygon
SELECT GeometryType(h3_cell_to_boundary_wkb(:polar)::geometry) LIKE 'POLYGON';
t
-- check num points in polar cell boundary
SELECT ST_NPoints(h3_cell_to_boundary_geometry(:polar)) = 11;
t
-- check latitude of antimeridian crossing points
SET h3.split_antimeridian TO true;
SELECT every(ABS(ST_Y(p) - :lat1) < :epsilon OR ABS(ST_Y(p) - :lat2) < :epsilon)
FROM (
SELECT (dp).geom AS p FROM (
(SELECT ST_DumpPoints(h3_cell_to_boundary_wkb(:edgecross)::geometry) AS dp)
) AS q1
) AS q2
WHERE ABS(ABS(ST_X(p)) - 180) < :epsilon;
t
--
-- Test h3_cells_to_multi_polygon_wkb
--
-- polygon is split in 4 polygons with 1 hole each
WITH split AS (
SELECT h3_cells_to_multi_polygon_wkb(
array(SELECT h3_polygon_to_cells(:transmeridianWithHoles, 4)))::geometry AS multi),
dp AS (SELECT ST_Dump(multi) AS dp FROM split),
polygons AS (SELECT (dp).geom AS geom FROM dp)
SELECT GeometryType(geom) LIKE 'POLYGON' AND ST_NumInteriorRings(geom) = 1
FROM polygons;
t
t
t
t
-- multipolygon with 2 polygons becomes multipolygon with 3
WITH split AS (
SELECT h3_cells_to_multi_polygon_wkb(
array(SELECT h3_polygon_to_cells(:transmeridianMulti, 3)))::geometry AS multi),
dp AS (SELECT ST_Dump(multi) AS dp FROM split)
SELECT COUNT(*) = 3 FROM dp;
t
-- h3_polygon_to_cells_experimental
SELECT COUNT(*) = 48 FROM (
SELECT h3_polygon_to_cells_experimental(:with2holes, 10, 'center')
) q;
t
SELECT COUNT(*) = 76 FROM (
SELECT h3_polygon_to_cells_experimental(:with2holes, 10, 'overlapping')
) q;
t
h3-pg-4.2.2/h3_postgis/test/expected/rasters.out 0000664 0000000 0000000 00000013016 14752347156 0021532 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set resolution 9
\set coverage_size 2
\set raster_size 25
\set pixel_size 0.0005
\set value_num 5
\set lat 51.5
\set lng -0.025
CREATE TABLE h3_test_rasters (id SERIAL, rast raster);
INSERT INTO h3_test_rasters (rast) (
WITH
vals AS (
SELECT array_agg(row) AS vals
FROM (
SELECT array_agg((x + y) % :value_num + 1) AS row
FROM
generate_series(1, :raster_size) AS x,
generate_series(1, :raster_size) AS y
GROUP BY y
) t),
rasts AS (
SELECT
ST_AddBand(
ST_MakeEmptyCoverage(
:raster_size, :raster_size,
:raster_size * :coverage_size, :raster_size * :coverage_size,
:lng, :lat,
:pixel_size, -(:pixel_size),
0, 0,
4326),
ARRAY[ROW(1, '8BUI', 1, 0)]::addbandarg[]
) AS rast)
SELECT ST_SetValues(r.rast, 1, 1, 1, v.vals)
FROM rasts r, vals v
);
CREATE FUNCTION h3_test_equal(
v1 double precision,
v2 double precision)
RETURNS boolean
AS $$
SELECT ABS(v1 - v2) < 1e-12;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE FUNCTION h3_test_raster_summary_stats_equal(
s1 h3_raster_summary_stats,
s2 h3_raster_summary_stats)
RETURNS boolean
AS $$
SELECT s1 IS NOT NULL AND s2 IS NOT NULL
AND h3_test_equal((s1).count, (s2).count)
AND h3_test_equal((s1).sum, (s2).sum)
AND h3_test_equal((s1).mean, (s2).mean)
AND h3_test_equal((s1).stddev, (s2).stddev)
AND h3_test_equal((s1).min, (s2).min)
AND h3_test_equal((s1).max, (s2).max);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE FUNCTION h3_test_raster_class_summary_item_equal(
i1 h3_raster_class_summary_item,
i2 h3_raster_class_summary_item)
RETURNS boolean
AS $$
SELECT i1 IS NOT NULL AND i2 IS NOT NULL
AND h3_test_equal((i1).val, (i2).val)
AND h3_test_equal((i1).count, (i2).count)
AND h3_test_equal((i1).area, (i2).area);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
-- Results of `h3_raster_summary_clip` and `h3_raster_summary_centroids`
-- should be identical
WITH
clip AS (
SELECT
h3,
h3_raster_summary_stats_agg(stats) AS stats
FROM (
-- h3, stats
SELECT (h3_raster_summary_clip(rast, :resolution)).*
FROM h3_test_rasters
) t
GROUP BY 1),
centroids AS (
SELECT
h3,
h3_raster_summary_stats_agg(stats) AS stats
FROM (
-- h3, stats
SELECT (h3_raster_summary_centroids(rast, :resolution)).*
FROM h3_test_rasters
) t
GROUP BY 1)
SELECT COUNT(*)
FROM clip a FULL OUTER JOIN centroids b ON a.h3 = b.h3
WHERE NOT h3_test_raster_summary_stats_equal(a.stats, b.stats);
0
-- Results of `h3_raster_class_summary_clip` and `h3_raster_class_summary_clip`
-- should be identical
WITH
clip AS (
SELECT
h3,
val,
h3_raster_class_summary_item_agg(summary) AS summary
FROM (
-- h3, val, summary
SELECT (h3_raster_class_summary_clip(rast, :resolution)).*
FROM h3_test_rasters
) t
GROUP BY 1, 2),
centroids AS (
SELECT
h3,
val,
h3_raster_class_summary_item_agg(summary) AS summary
FROM (
-- h3, val, summary
SELECT (h3_raster_class_summary_centroids(rast, :resolution)).*
FROM h3_test_rasters
) t
GROUP BY 1, 2)
SELECT COUNT(*)
FROM clip a FULL OUTER JOIN centroids b ON a.h3 = b.h3 AND a.val = b.val
WHERE NOT h3_test_raster_class_summary_item_equal(a.summary, b.summary);
0
-- Stats aggregation check:
-- stats for a cell intersecting multiple rasters (with aggregation) should be
-- the same when calculated on a union of rasters (without aggregation).
WITH
rast AS (
-- Union all test rasters
SELECT ST_Union(rast) AS rast FROM h3_test_rasters),
middle AS (
-- Find an H3 cell in a bottom-right corner of a first raster
-- (intersecting 4 rasters)
SELECT
h3_lat_lng_to_cell(
ST_MakePoint(
ST_RasterToWorldCoordX(rast, :raster_size),
ST_RasterToWorldCoordY(rast, :raster_size)),
:resolution
) AS h3
FROM rast),
summary1 AS (
-- Get summary from combined raster
SELECT t.stats
FROM (
-- h3, stats
SELECT (h3_raster_summary_clip(rast, :resolution)).*
FROM rast
) t, middle m
WHERE t.h3 = m.h3),
summary2 AS (
-- Get aggregates summary from separate rasters
SELECT h3_raster_summary_stats_agg(t.stats) AS stats
FROM (
-- h3, stats
SELECT (h3_raster_summary_clip(rast, :resolution)).*
FROM h3_test_rasters
) t, middle m
WHERE t.h3 = m.h3
GROUP BY t.h3)
SELECT h3_test_raster_summary_stats_equal(s1.stats, s2.stats)
FROM summary1 s1, summary2 s2;
t
DROP FUNCTION h3_test_raster_class_summary_item_equal(
h3_raster_class_summary_item,
h3_raster_class_summary_item);
DROP FUNCTION h3_test_raster_summary_stats_equal(
h3_raster_summary_stats,
h3_raster_summary_stats);
DROP FUNCTION h3_test_equal(double precision, double precision);
DROP TABLE h3_test_rasters;
h3-pg-4.2.2/h3_postgis/test/sql/ 0000775 0000000 0000000 00000000000 14752347156 0016313 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/h3_postgis/test/sql/postgis.sql 0000664 0000000 0000000 00000020710 14752347156 0020524 0 ustar 00root root 0000000 0000000 \pset tuples_only on
-- Variables for testing
\set resolution 10
\set hexagon '\'8a63a9a99047fff\''
\set meter ST_SetSRID(ST_Point(6196902.235389061,1413172.0833316022), 3857)
\set degree ST_SetSRID(ST_Point(55.6677199224442,12.592131261648213), 4326)
\set edgecross '\'8003fffffffffff\'::h3index'
\set polar '\'81f2bffffffffff\'::h3index'
\set lat1 84.76455330449812
\set lat2 89.980298101841
\set epsilon 0.0000000000001
\set longPathEndpoint1 '\'842ca2bffffffff\'::h3index'
\set longPathEndpoint2 '\'842e611ffffffff\'::h3index'
-- polygon with 2 holes
\set with2holes '\'POLYGON((31.6520834 68.9912098,31.6521263 68.9910944,31.6530919 68.9912021,31.6540789 68.9912944,31.6550016 68.991279,31.6553449 68.9910175,31.6554737 68.9907713,31.6559458 68.990402,31.6563535 68.9900789,31.6568684 68.9897404,31.6569543 68.9895711,31.6567182 68.988671,31.6569114 68.9881786,31.6571045 68.9879401,31.6567611 68.9875708,31.6570401 68.9873015,31.6576195 68.986986,31.6581774 68.9868398,31.6584992 68.9870553,31.6586065 68.9872707,31.6590786 68.9873246,31.6594219 68.9872091,31.6596579 68.9870091,31.6596579 68.9867706,31.6601515 68.9866167,31.6607308 68.9864551,31.660409 68.986232,31.6601729 68.9860627,31.6605806 68.9859319,31.6614818 68.9859011,31.6620183 68.9857087,31.6622972 68.9854471,31.6628337 68.9852932,31.6633701 68.9852855,31.663928 68.9855702,31.6640782 68.9859088,31.6636705 68.9862166,31.6639924 68.9864859,31.664443 68.9868629,31.664679 68.9872091,31.6642928 68.9873784,31.6641426 68.9876939,31.6650009 68.9879016,31.6652155 68.9881555,31.6653657 68.9883709,31.6659665 68.9886941,31.6662884 68.9889941,31.666739 68.989248,31.6669321 68.9891095,31.6670394 68.9888787,31.6681123 68.9889326,31.6687345 68.989325,31.6692495 68.9895865,31.6701937 68.9897635,31.6710949 68.9897404,31.6725325 68.9897558,31.6733479 68.9898558,31.6743135 68.9904097,31.674571 68.990702,31.6747641 68.9909328,31.6745066 68.9911405,31.6738844 68.9912636,31.6731977 68.9914175,31.6734981 68.9916098,31.6739487 68.9915713,31.6744852 68.9915175,31.6750431 68.9914021,31.6751718 68.9911944,31.6752147 68.9910405,31.6756439 68.9910636,31.6765451 68.9912021,31.6777253 68.9912944,31.6784119 68.9912482,31.6790771 68.9911559,31.6793346 68.9913021,31.6787553 68.9916713,31.678133 68.9920867,31.6780472 68.9924483,31.6782617 68.9927098,31.6792702 68.9941098,31.6794419 68.9943636,31.6801715 68.9945328,31.6817808 68.9946328,31.6825533 68.9948174,31.6827249 68.995202,31.683712 68.9957404,31.6840124 68.9962634,31.684699 68.9965556,31.6848492 68.9968171,31.6841197 68.9969632,31.6831326 68.9969479,31.6827464 68.9969171,31.6824031 68.9968556,31.6821456 68.9967248,31.6813302 68.9968248,31.6810083 68.9971094,31.6806865 68.9971863,31.6802358 68.9971401,31.6792702 68.9967018,31.6787553 68.9963864,31.6780901 68.9958327,31.6777038 68.9956788,31.6766095 68.9955635,31.6762233 68.9954943,31.6759658 68.9952943,31.6753649 68.9951481,31.6746354 68.9952712,31.6735625 68.9951866,31.6728329 68.9951866,31.6726398 68.9953943,31.6719746 68.9955173,31.6709661 68.9954173,31.6704941 68.9951558,31.6700434 68.9948251,31.669743 68.9944867,31.6695285 68.994179,31.6693783 68.9939329,31.6690779 68.993756,31.6680479 68.9935867,31.663692 68.9929329,31.6628551 68.9927637,31.661675 68.9927637,31.6610527 68.9929637,31.6605377 68.9929714,31.6599583 68.9929406,31.6588855 68.9928175,31.658349 68.992656,31.657598 68.9923714,31.6567826 68.9922329,31.6552162 68.9920714,31.6541648 68.9919175,31.6531348 68.9916483,31.6523838 68.9914175,31.6520834 68.9912098),(31.657185 68.990202,31.657244 68.9903482,31.6574585 68.9903809,31.6578555 68.9903597,31.6581452 68.9902539,31.6583651 68.9900924,31.6582364 68.9899366,31.6578716 68.9898616,31.6575766 68.9898731,31.6573083 68.9899808,31.6572332 68.9900731,31.657185 68.990202),(31.6590196 68.9886094,31.6591644 68.9887229,31.6594058 68.9888537,31.6595936 68.9889557,31.6596794 68.9890441,31.6597116 68.9891345,31.659776 68.9892519,31.6599476 68.9891903,31.660012 68.9890614,31.6601139 68.9889383,31.6602641 68.9888326,31.6602749 68.988721,31.6601998 68.988596,31.6600603 68.988369,31.6601676 68.9882709,31.6604251 68.9882844,31.6607255 68.9882728,31.6609454 68.9881767,31.6605592 68.9880901,31.6604841 68.9879689,31.6603607 68.9878497,31.660071 68.9878843,31.6597867 68.9879959,31.6593575 68.9880305,31.6592127 68.9881132,31.6592234 68.9882421,31.6592127 68.9883786,31.6590196 68.9886094))\''::geometry(POLYGON)
-- S-shaped polygon with 4 holes crossing antimeridian
\set transmeridianWithHoles '\'POLYGON((-170 12.5, 170 12.5, 170 7.5, -175 7.5, -175 2.5, 170 2.5, 170 -12.5, -170 -12.5, -170 -7.5, 175 -7.5, 175 -2.5, -170 -2.5, -170 12.5), (-176 11.5, -179 11.5, -179 8.5, -176 8.5, -176 11.5), (174 11.5, 171 11.5, 171 8.5, 174 8.5, 174 11.5), (174 -3.5, 171 -3.5, 171 -6.5, 174 -6.5, 174 -3.5),(-176 -8.5, -179 -8.5, -179 -11.5, -176 -11.5, -176 -8.5))\''::geometry(POLYGON)
-- multipolygon with 2 polygons: one crossing and one not crossing antimeridian
\set transmeridianMulti '\'MULTIPOLYGON(((-175 50, -175 55, 175 55, 175 50, -175 50)), ((170 50, 170 55, 165 55, 165 50, 170 50)))\''::geometry(MULTIPOLYGON)
SELECT h3_lat_lng_to_cell(:degree, :resolution) = '8a63a9a99047fff';
-- meters are NOT reprojected
SELECT h3_lat_lng_to_cell(:meter, :resolution) <> '8a63a9a99047fff';
-- check back/forth conversion return same hex
SELECT h3_lat_lng_to_cell(h3_cell_to_geometry(:hexagon), :resolution) = '8a63a9a99047fff';
-- check num points in boundary
SELECT ST_NPoints(h3_cell_to_boundary_geometry(:hexagon)) = 7;
-- test strict h3_lat_lng_to_cell throws for bad latlon
CREATE FUNCTION h3_test_postgis_nounit() RETURNS boolean LANGUAGE PLPGSQL
AS $$
BEGIN
PERFORM h3_lat_lng_to_cell(POINT(360, 2.592131261648213), 1);
RETURN false;
EXCEPTION WHEN OTHERS THEN
RETURN true;
END;
$$;
SET h3.strict TO true;
SELECT h3_test_postgis_nounit();
SET h3.strict TO false;
DROP FUNCTION h3_test_postgis_nounit;
-- Test wraparound
\set lon 55.6677199224442
\set lat 12.592131261648213
SELECT h3_lat_lng_to_cell(POINT(:lon, :lat), 7)
= h3_lat_lng_to_cell(POINT(:lon + 360, :lat), 7);
SELECT h3_lat_lng_to_cell(POINT(:lon, :lat ), 7)
= h3_lat_lng_to_cell(POINT(:lon, :lat + 360), 7);
-- test h3_grid_path_cells_recursive works for long path
SELECT COUNT(*) > 0 FROM (
SELECT h3_grid_path_cells_recursive(:longPathEndpoint1, :longPathEndpoint1)
) q;
-- h3_polygon_to_cells works for polygon with two holes
SELECT COUNT(*) = 48 FROM (
SELECT h3_polygon_to_cells(:with2holes, 10)
) q;
--
-- Test h3_cell_to_boundary_wkb
--
-- polyfill of geo boundary returns original index
SELECT h3_polygon_to_cells(h3_cell_to_boundary(:hexagon)::geometry::polygon, null, :resolution) = :hexagon;
-- the boundary of a non-edgecrossing index is a polygon
SELECT GeometryType(h3_cell_to_boundary_wkb(:hexagon)::geometry) LIKE 'POLYGON';
-- the boundary of an edgecrossing index is a multipolygon when split
SELECT GeometryType(h3_cell_to_boundary_wkb(:edgecross)::geometry) LIKE 'MULTIPOLYGON';
-- the boundary of a polar cell is a polygon
SELECT GeometryType(h3_cell_to_boundary_wkb(:polar)::geometry) LIKE 'POLYGON';
-- check num points in polar cell boundary
SELECT ST_NPoints(h3_cell_to_boundary_geometry(:polar)) = 11;
-- check latitude of antimeridian crossing points
SET h3.split_antimeridian TO true;
SELECT every(ABS(ST_Y(p) - :lat1) < :epsilon OR ABS(ST_Y(p) - :lat2) < :epsilon)
FROM (
SELECT (dp).geom AS p FROM (
(SELECT ST_DumpPoints(h3_cell_to_boundary_wkb(:edgecross)::geometry) AS dp)
) AS q1
) AS q2
WHERE ABS(ABS(ST_X(p)) - 180) < :epsilon;
--
-- Test h3_cells_to_multi_polygon_wkb
--
-- polygon is split in 4 polygons with 1 hole each
WITH split AS (
SELECT h3_cells_to_multi_polygon_wkb(
array(SELECT h3_polygon_to_cells(:transmeridianWithHoles, 4)))::geometry AS multi),
dp AS (SELECT ST_Dump(multi) AS dp FROM split),
polygons AS (SELECT (dp).geom AS geom FROM dp)
SELECT GeometryType(geom) LIKE 'POLYGON' AND ST_NumInteriorRings(geom) = 1
FROM polygons;
-- multipolygon with 2 polygons becomes multipolygon with 3
WITH split AS (
SELECT h3_cells_to_multi_polygon_wkb(
array(SELECT h3_polygon_to_cells(:transmeridianMulti, 3)))::geometry AS multi),
dp AS (SELECT ST_Dump(multi) AS dp FROM split)
SELECT COUNT(*) = 3 FROM dp;
-- h3_polygon_to_cells_experimental
SELECT COUNT(*) = 48 FROM (
SELECT h3_polygon_to_cells_experimental(:with2holes, 10, 'center')
) q;
SELECT COUNT(*) = 76 FROM (
SELECT h3_polygon_to_cells_experimental(:with2holes, 10, 'overlapping')
) q; h3-pg-4.2.2/h3_postgis/test/sql/rasters.sql 0000664 0000000 0000000 00000013004 14752347156 0020515 0 ustar 00root root 0000000 0000000 \pset tuples_only on
\set resolution 9
\set coverage_size 2
\set raster_size 25
\set pixel_size 0.0005
\set value_num 5
\set lat 51.5
\set lng -0.025
CREATE TABLE h3_test_rasters (id SERIAL, rast raster);
INSERT INTO h3_test_rasters (rast) (
WITH
vals AS (
SELECT array_agg(row) AS vals
FROM (
SELECT array_agg((x + y) % :value_num + 1) AS row
FROM
generate_series(1, :raster_size) AS x,
generate_series(1, :raster_size) AS y
GROUP BY y
) t),
rasts AS (
SELECT
ST_AddBand(
ST_MakeEmptyCoverage(
:raster_size, :raster_size,
:raster_size * :coverage_size, :raster_size * :coverage_size,
:lng, :lat,
:pixel_size, -(:pixel_size),
0, 0,
4326),
ARRAY[ROW(1, '8BUI', 1, 0)]::addbandarg[]
) AS rast)
SELECT ST_SetValues(r.rast, 1, 1, 1, v.vals)
FROM rasts r, vals v
);
CREATE FUNCTION h3_test_equal(
v1 double precision,
v2 double precision)
RETURNS boolean
AS $$
SELECT ABS(v1 - v2) < 1e-12;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE FUNCTION h3_test_raster_summary_stats_equal(
s1 h3_raster_summary_stats,
s2 h3_raster_summary_stats)
RETURNS boolean
AS $$
SELECT s1 IS NOT NULL AND s2 IS NOT NULL
AND h3_test_equal((s1).count, (s2).count)
AND h3_test_equal((s1).sum, (s2).sum)
AND h3_test_equal((s1).mean, (s2).mean)
AND h3_test_equal((s1).stddev, (s2).stddev)
AND h3_test_equal((s1).min, (s2).min)
AND h3_test_equal((s1).max, (s2).max);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE FUNCTION h3_test_raster_class_summary_item_equal(
i1 h3_raster_class_summary_item,
i2 h3_raster_class_summary_item)
RETURNS boolean
AS $$
SELECT i1 IS NOT NULL AND i2 IS NOT NULL
AND h3_test_equal((i1).val, (i2).val)
AND h3_test_equal((i1).count, (i2).count)
AND h3_test_equal((i1).area, (i2).area);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
-- Results of `h3_raster_summary_clip` and `h3_raster_summary_centroids`
-- should be identical
WITH
clip AS (
SELECT
h3,
h3_raster_summary_stats_agg(stats) AS stats
FROM (
-- h3, stats
SELECT (h3_raster_summary_clip(rast, :resolution)).*
FROM h3_test_rasters
) t
GROUP BY 1),
centroids AS (
SELECT
h3,
h3_raster_summary_stats_agg(stats) AS stats
FROM (
-- h3, stats
SELECT (h3_raster_summary_centroids(rast, :resolution)).*
FROM h3_test_rasters
) t
GROUP BY 1)
SELECT COUNT(*)
FROM clip a FULL OUTER JOIN centroids b ON a.h3 = b.h3
WHERE NOT h3_test_raster_summary_stats_equal(a.stats, b.stats);
-- Results of `h3_raster_class_summary_clip` and `h3_raster_class_summary_clip`
-- should be identical
WITH
clip AS (
SELECT
h3,
val,
h3_raster_class_summary_item_agg(summary) AS summary
FROM (
-- h3, val, summary
SELECT (h3_raster_class_summary_clip(rast, :resolution)).*
FROM h3_test_rasters
) t
GROUP BY 1, 2),
centroids AS (
SELECT
h3,
val,
h3_raster_class_summary_item_agg(summary) AS summary
FROM (
-- h3, val, summary
SELECT (h3_raster_class_summary_centroids(rast, :resolution)).*
FROM h3_test_rasters
) t
GROUP BY 1, 2)
SELECT COUNT(*)
FROM clip a FULL OUTER JOIN centroids b ON a.h3 = b.h3 AND a.val = b.val
WHERE NOT h3_test_raster_class_summary_item_equal(a.summary, b.summary);
-- Stats aggregation check:
-- stats for a cell intersecting multiple rasters (with aggregation) should be
-- the same when calculated on a union of rasters (without aggregation).
WITH
rast AS (
-- Union all test rasters
SELECT ST_Union(rast) AS rast FROM h3_test_rasters),
middle AS (
-- Find an H3 cell in a bottom-right corner of a first raster
-- (intersecting 4 rasters)
SELECT
h3_lat_lng_to_cell(
ST_MakePoint(
ST_RasterToWorldCoordX(rast, :raster_size),
ST_RasterToWorldCoordY(rast, :raster_size)),
:resolution
) AS h3
FROM rast),
summary1 AS (
-- Get summary from combined raster
SELECT t.stats
FROM (
-- h3, stats
SELECT (h3_raster_summary_clip(rast, :resolution)).*
FROM rast
) t, middle m
WHERE t.h3 = m.h3),
summary2 AS (
-- Get aggregates summary from separate rasters
SELECT h3_raster_summary_stats_agg(t.stats) AS stats
FROM (
-- h3, stats
SELECT (h3_raster_summary_clip(rast, :resolution)).*
FROM h3_test_rasters
) t, middle m
WHERE t.h3 = m.h3
GROUP BY t.h3)
SELECT h3_test_raster_summary_stats_equal(s1.stats, s2.stats)
FROM summary1 s1, summary2 s2;
DROP FUNCTION h3_test_raster_class_summary_item_equal(
h3_raster_class_summary_item,
h3_raster_class_summary_item);
DROP FUNCTION h3_test_raster_summary_stats_equal(
h3_raster_summary_stats,
h3_raster_summary_stats);
DROP FUNCTION h3_test_equal(double precision, double precision);
DROP TABLE h3_test_rasters;
h3-pg-4.2.2/include/ 0000775 0000000 0000000 00000000000 14752347156 0014076 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/include/CMakeLists.txt 0000664 0000000 0000000 00000000431 14752347156 0016634 0 ustar 00root root 0000000 0000000 add_library(postgresql_h3_shared
OBJECT error.c
)
target_link_libraries(postgresql_h3_shared
PRIVATE PostgreSQL::PostgreSQL
)
target_include_directories(postgresql_h3_shared
INTERFACE ./
)
set_target_properties(postgresql_h3_shared PROPERTIES POSITION_INDEPENDENT_CODE True)
h3-pg-4.2.2/include/constants.h 0000664 0000000 0000000 00000000204 14752347156 0016257 0 ustar 00root root 0000000 0000000 #ifndef H3_CONSTANTS_H
#define H3_CONSTANTS_H
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#endif /* H3_CONSTANTS_H */
h3-pg-4.2.2/include/deprecate.h 0000664 0000000 0000000 00000002375 14752347156 0016212 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include // PG_FUNCTION_INFO_V1
/* Inspired by PostGIS legacy function handling */
/* https://github.com/postgis/postgis/blob/master/postgis/postgis_legacy.c */
#define H3_DEPRECATE(version, funcname) \
PGDLLEXPORT PG_FUNCTION_INFO_V1(funcname); \
Datum funcname(PG_FUNCTION_ARGS) \
{ \
ereport(ERROR, (\
errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
errmsg("A stored procedure tried to use deprecated C function '%s'", \
__func__), \
errdetail("Library function '%s' was deprecated in h3 %s", \
__func__, version), \
errhint("Consider running: ALTER EXTENSION h3 UPDATE") \
)); \
PG_RETURN_POINTER(NULL); \
}
h3-pg-4.2.2/include/error.c 0000664 0000000 0000000 00000000375 14752347156 0015400 0 ustar 00root root 0000000 0000000 #include
void
h3_assert(int error)
{
if (error)
ereport(ERROR, (
errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
errmsg("error code: %i", error),
errhint("https://h3geo.org/docs/library/errors#table-of-error-codes")));
}
h3-pg-4.2.2/include/error.h 0000664 0000000 0000000 00000002477 14752347156 0015412 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef H3_ERROR_H
#define H3_ERROR_H
void h3_assert(int error);
#define ASSERT(condition, code, msg, ...) \
if (0 == (condition)) ereport(ERROR, ( \
errcode(code), \
errmsg(msg, ##__VA_ARGS__) \
))
#define ENSURE_TYPEFUNC_COMPOSITE(x) \
ASSERT( \
x == TYPEFUNC_COMPOSITE, \
ERRCODE_INVALID_PARAMETER_VALUE, \
"Function returning record called in context " \
"that cannot accept type record" \
)
#define H3_DEPRECATION(msg) \
ereport(WARNING, ( \
errmsg("Deprecated: %s", msg) \
))
#define DEBUG(msg, ...) \
ereport(NOTICE, ( \
errmsg(msg, ##__VA_ARGS__) \
))
#define DEBUG_H3INDEX(h3index) DEBUG("index: %lx", h3index)
#endif /* H3_ERROR_H */
h3-pg-4.2.2/include/type.h 0000664 0000000 0000000 00000003211 14752347156 0015225 0 ustar 00root root 0000000 0000000 /*
* Copyright 2023 Zacharias Knudsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef H3_TYPE_H
#define H3_TYPE_H
#include
/*
* DatumGetH3Index
* Returns H3 index value of a datum.
*
* Note: this macro hides whether h3 index is pass by value or by reference.
*/
#ifdef USE_FLOAT8_BYVAL
#define DatumGetH3Index(X) ((H3Index) (X))
#else
#define DatumGetH3Index(X) (* ((H3Index *) DatumGetPointer(X)))
#endif
/*
* H3IndexGetDatum
* Returns datum representation for an H3 index.
*
* Note: if H3 index is pass by reference, this function returns a reference
* to palloc'd space.
*/
#ifdef USE_FLOAT8_BYVAL
#define H3IndexGetDatum(X) ((Datum) (X))
#else
#define H3IndexGetDatum(X) Int64GetDatum((int64) (X))
#endif
/* Macros for fetching arguments and returning results of h3 index type */
#define PG_GETARG_H3INDEX(n) DatumGetH3Index(PG_GETARG_DATUM(n))
#define PG_RETURN_H3INDEX(x) return H3IndexGetDatum(x)
/* use origin resolution minus one when no resolution is given */
#define PG_GETARG_OPTIONAL_RES(n, cell, offset) \
PG_NARGS() == (n + 1) ? PG_GETARG_INT32(1) : getResolution(cell) + offset
#endif /* H3_TYPE_H */
h3-pg-4.2.2/scripts/ 0000775 0000000 0000000 00000000000 14752347156 0014142 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/scripts/bundle 0000775 0000000 0000000 00000000160 14752347156 0015336 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
git clean -xdf
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --target=pgxn
h3-pg-4.2.2/scripts/develop 0000775 0000000 0000000 00000000302 14752347156 0015521 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
sudo cmake --install build --component h3-pg
ctest --test-dir build --output-on-failure --build-config Release
h3-pg-4.2.2/scripts/documentation 0000775 0000000 0000000 00000000610 14752347156 0016736 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
# ensure we are in script dir
cd "$(dirname "$0")"
# go to python project dir
cd sql2doc
# ensure dependencies are present
poetry install
# generate markdown from sql files
poetry run -q -- python generate.py \
-g "API Reference" "../../h3/sql/install/*.sql" \
-g "PostGIS Integration" "../../h3_postgis/sql/install/*.sql" \
> "../../docs/api.md"
h3-pg-4.2.2/scripts/release 0000775 0000000 0000000 00000002032 14752347156 0015505 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
set -u
VERSION="$1"
: "$VERSION"
# Version number should be changed in root `CMakeLists.txt`.
sed -i -r "s/( VERSION )[0-9\.]+/\1$VERSION/g" CMakeLists.txt
# Set `INSTALL_VERSION` to "${PROJECT_VERSION}".
sed -i '/set(INSTALL_VERSION "${PROJECT_VERSION}")/s/^#//g' CMakeLists.txt
sed -i '/set(INSTALL_VERSION "unreleased")/s/^/#/g' CMakeLists.txt
# Update files (and cmake references) suffixed `--unreleased` should be renamed.
rename unreleased "$VERSION" h3/sql/updates/*--unreleased.sql
sed -i "s/unreleased/$VERSION/g" h3/sql/updates/*--$VERSION.sql
sed -i "s/unreleased/$VERSION/g" h3/CMakeLists.txt
rename unreleased "$VERSION" h3_postgis/sql/updates/*--unreleased.sql
sed -i "s/unreleased/$VERSION/g" h3_postgis/sql/updates/*--$VERSION.sql
sed -i "s/unreleased/$VERSION/g" h3_postgis/CMakeLists.txt
git checkout -b "release-$VERSION"
echo "Remember to":
echo " - Installer \`.sql\` files should have \`@ availability\` comments updated."
echo " - Update changelog by moving from \`Unreleased\` to a new section" h3-pg-4.2.2/scripts/sql2doc/ 0000775 0000000 0000000 00000000000 14752347156 0015511 5 ustar 00root root 0000000 0000000 h3-pg-4.2.2/scripts/sql2doc/Pipfile 0000664 0000000 0000000 00000000171 14752347156 0017023 0 ustar 00root root 0000000 0000000 [[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
lark-parser = "*"
h3-pg-4.2.2/scripts/sql2doc/generate.py 0000664 0000000 0000000 00000031753 14752347156 0017666 0 ustar 00root root 0000000 0000000 """
The concept:
We don't want to manually keep API docs in sync with SQL files.
So instead we should generate the docs from the sql/install/*.sql files.
We have the following things to document:
* Type
* Cast (put in same section as the type)
* Operator
* Function
* Aggregate function
Some functions do not need docs, we can signal this by not applying a comment.
This is internal operator functions for example.
We should parse the sql files, extract the above, and then document all things that
have comments applied.
We should probably hardcode a list of things that should not be documented, so we can
fail if new function are undocumented.
"""
import argparse
import glob
import re
from pathlib import Path
from lark import Lark, Transformer, v_args, visitors
import sys #TEST
# see PGXN::API::Indexer::_clean_html_body
def to_anchor(text):
text = re.sub(r"[^\w\s\(\)-]", "", text) # approximating processing done in Text::Markdown
text = re.sub(r"^([^a-zA-Z])", r"L\1", text)
text = re.sub(r"[^a-zA-Z0-9_:.-]+", ".", text)
return "#" + text
def md_heading(text, level=0):
return "#" * (level + 1) + " " + text
class Context:
level = 0
def __init__(self, statements_by_refid={}):
self.statements_by_refid = statements_by_refid
self.level = 0
class StmtBase:
level = 0
newline_before = True
newline_after = True
decorators = {}
def __init__(self, level=-1):
self.level = level
def set_decorators(self, decorators):
self.decorators = decorators
def get_decorator(self, key):
return self.decorators[key] if self.has_decorator(key) else None
def has_decorator(self, key):
return key in self.decorators
def is_internal(self):
return self.has_decorator("internal")
def is_deprecated(self):
return self.has_decorator("deprecated")
def is_visible(self):
return not self.is_internal() and not self.is_deprecated()
def get_refid(self):
return self.get_decorator("refid")
def get_ref_text(self):
return str(self)
def get_anchor(self):
if not self.is_visible() or self.level < 0:
return None
return to_anchor(str(self))
def get_refs(self, statements_by_refid):
ids = self.get_decorator("ref") or []
return [statements_by_refid[refid] for refid in ids if refid in statements_by_refid]
def to_ref(self):
return '{}'.format(self.get_anchor(), self.get_ref_text())
def to_md(self, context):
if not self.is_visible():
return ""
md = str(self)
if self.level > -1:
md = md_heading(md, self.level)
if self.newline_before:
md = "\n" + md
# availability
availability = self.get_decorator("availability")
if availability:
md += f"\n*Since v{availability}*"
# references
refs = self.get_refs(context.statements_by_refid)
if len(refs) > 0:
md += "\n\nSee also: "
md += ", ".join([ref.to_ref() for ref in refs])
if self.newline_after:
md += "\n"
return md
def __str__(self):
raise Execepion("Not implemented")
class Argument:
def __init__(self, argmode, name, argtype, default):
self.argmode = argmode
self.name = name
self.argtype = argtype
self.default = default
def get_typestr(self):
s = "`{}`".format(self.argtype)
if self.default:
s = "[{} = {}]".format(s, self.default)
return s
def __str__(self):
s = ""
if self.argmode:
s += self.argmode + " "
if self.name:
s += self.name + " "
s += "`{}`".format(self.argtype)
if self.default:
s = "[{} = {}]".format(s, self.default)
return s
class Column:
def __init__(self, name, coltype):
self.name = name;
self.coltype = coltype;
def __str__(self):
return "{} `{}`".format(self.name, self.coltype)
class FunctionReturnsBase:
pass
class FunctionReturns(FunctionReturnsBase):
def __init__(self, rettype):
self.rettype = rettype
def __str__(self):
return "`{}`".format(self.rettype)
class FunctionReturnsSet(FunctionReturns):
def __str__(self):
return "SETOF " + super().__str__()
class FunctionReturnsTable(FunctionReturnsBase):
def __init__(self, columns):
self.columns = columns
def __str__(self):
return "TABLE (" + ", ".join([str(col) for col in self.columns]) + ")";
class CustomMd(StmtBase):
def __init__(self, line):
super().__init__()
self.newline_after = False
match = re.match(r"(#+)\s*(.*)", line)
if match:
self.newline_before = True
self.level = max(0, len(match[1]) - 1)
self.line = match[2]
else:
self.newline_before = False
self.level = -1
self.line = line
def update_level(self, context):
if self.level > -1:
context.level = max(0, self.level)
def __str__(self):
return self.line
class CreateFunctionStmt(StmtBase):
def __init__(self, name: str, arguments, returns: FunctionReturnsBase):
super().__init__(2)
self.name = name
self.arguments = arguments or []
self.returns = returns
def get_ref_text(self):
return "{}({})".format(
self.name,
", ".join([arg.get_typestr() for arg in self.arguments]))
def __str__(self):
return "{}({}) ⇒ {}".format(
self.name,
", ".join([str(arg) for arg in self.arguments]),
self.returns)
class CreateAggregateStmt(StmtBase):
def __init__(self, name: str, arguments):
super().__init__(2)
self.name = name
self.arguments = arguments or []
def get_ref_text(self):
return "{}(setof {})".format(
self.name,
", ".join([arg.get_typestr() for arg in self.arguments]))
def __str__(self):
return "{}(setof {})".format(
self.name,
", ".join([str(arg) for arg in self.arguments]))
class CreateTypeStmt(StmtBase):
def __str__(self):
return ""
class CreateCastStmt(StmtBase):
def __init__(self, source, target):
super().__init__(2)
self.source = source
self.target = target
def __str__(self):
return "`{}` :: `{}`".format(self.source, self.target)
class CreateOperatorStmt(StmtBase):
def __init__(self, name, left, right):
super().__init__(2)
self.name = name
self.left = left
self.right = right
def __str__(self):
return "Operator: `{}` {} `{}`".format(self.left, self.name, self.right)
class CreateCommentStmt(StmtBase):
def __init__(self, text):
super().__init__()
self.text = text
def __str__(self):
return self.text
class SQLTransformer(Transformer):
def start(self, statements):
# flatten
flat = [item for sublist in statements for item in sublist]
return flat
@v_args(inline=True)
def custom_decorated_statement(self, decorators, statement = None):
if not statement:
raise visitors.Discard()
if decorators:
statement.set_decorators(decorators)
return statement
def custom_decorators(self, lines):
decorators = {}
for line in lines:
try:
key, value = line.split(":")
key = str(key).lower()
if key in ["ref"]:
decorators[key] = [v.strip() for v in value.split(",")]
else:
decorators[key] = value.strip()
except:
decorators[str(line).lower()] = True
return decorators
def custom_md_statement(self, children):
return children
# -- MARKDOWN --------------------------------------------------------------
@v_args(inline=True)
def custom_markdown(self, line=""):
line = re.sub(r"^--\|[ \t]?", '', line)
return CustomMd(line)
# -- CREATE TYPE -----------------------------------------------------------
def create_type_stmt(self, children):
return CreateTypeStmt()
# -- CREATE CAST -----------------------------------------------------------
@v_args(inline=True)
def create_cast_stmt(self, source, target, *children):
return CreateCastStmt(source, target)
# -- CREATE OPERATOR -------------------------------------------------------
@v_args(inline=True)
def create_oper_stmt(self, name, options):
return CreateOperatorStmt(name, options['LEFTARG'], options['RIGHTARG'])
def create_oper_opts(self, opts):
return {key: value for [key, value] in opts}
@v_args(inline=True)
def create_oper_opt(self, option, value=None):
return [str(option), value]
# -- CREATE FUNCTION -------------------------------------------------------
@v_args(inline=True)
def create_fun_ret_table_columns(self, columns):
return FunctionReturnsTable(columns)
def create_fun_rettype(self, children):
rettype = children[1]
if children[0] is not None:
return FunctionReturnsSet(rettype)
return FunctionReturns(rettype)
def column_list(self, children):
return children
@v_args(inline=True)
def column(self, name: str, coltype: str):
return Column(name, coltype)
@v_args(inline=True)
def create_func_stmt(self, name: str, arguments, returns, *opts):
# skip internal functions
if name.startswith("__"):
raise visitors.Discard()
return CreateFunctionStmt(name, arguments, returns)
# -- CREATE AGGREGATE ------------------------------------------------------
@v_args(inline=True)
def create_agg_stmt(self, name: str, arguments, *params):
return CreateAggregateStmt(name, arguments)
# -- CREATE COMMENT --------------------------------------------------------
@v_args(inline=True)
def comment_on_stmt(self, child, text):
return CreateCommentStmt(text)
# -- FUNCTION OR AGGREGATE ARGUMENTS ---------------------------------------
def argument_list(self, children):
return children
@v_args(inline=True)
def argument(self, argmode, name, argtype, default=None):
return Argument(argmode, name, argtype, default)
# -- CREATE OPERATOR CLASS -------------------------------------------------
def create_opcl_stmt(self, children):
raise visitors.Discard()
# -- SIMPLE RULES ----------------------------------------------------------
true = lambda self, _: "`true`"
false = lambda self, _: "`false`"
number = v_args(inline=True)(int)
def fun_name(self, children):
return children[1]
@v_args(inline=True)
def string(self, s):
return s[1:-1].replace('\\"', '"')
# -- TERMINALS -------------------------------------------------------------
def SIGNED_NUMBER(self, children):
return int(children)
def CNAME(self, cname):
return str(cname)
def OPERATOR(self, name):
return str(name)
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
'--group', '-g',
nargs=2, metavar=('header', 'glob'),
action='append',
help='Globs for each group of SQL install files')
return parser.parse_args()
def create_parser():
here = Path(__file__).parent
parser = Lark.open(
here / "sql.lark",
parser="lalr",
maybe_placeholders=True)
return parser
def process_group(parser, group, statements_by_refid):
[header, files_glob] = group
statements = []
paths = glob.glob(files_glob)
for path in sorted(paths):
group_statements = parse_file(parser, path)
# Add referenced statements to map
for statement in group_statements:
refid = statement.get_refid()
if refid:
statements_by_refid[refid] = statement
statements += group_statements
return (header, statements)
# return flat list of statements in file
def parse_file(parser, path):
with open(path) as fd:
sql = fd.read()
tree = parser.parse(sql)
statements = SQLTransformer(visit_tokens=True).transform(tree)
return statements
def statements_to_md(statements, context):
items = [stmt.to_md(context) for stmt in statements]
return [item for item in items if len(item) > 0]
def main():
args = parse_args()
parser = create_parser()
groups = []
statements_by_refid = {}
# Process groups
for item in args.group:
group = process_group(parser, item, statements_by_refid)
groups.append(group)
# Output
md = ""
for group in groups:
[header, statements] = group
md += md_heading(header) + "\n"
md += "\n".join(statements_to_md(statements, Context(statements_by_refid))) + "\n"
print(md)
if __name__ == "__main__":
main()
h3-pg-4.2.2/scripts/sql2doc/poetry.lock 0000664 0000000 0000000 00000001245 14752347156 0017707 0 ustar 00root root 0000000 0000000 [[package]]
name = "lark-parser"
version = "0.12.0"
description = "a modern parsing library"
category = "main"
optional = false
python-versions = "*"
[package.extras]
atomic-cache = ["atomicwrites"]
nearley = ["js2py"]
regex = ["regex"]
[metadata]
lock-version = "1.1"
python-versions = "^3.10"
content-hash = "13b79c3b8dcbda6768897ce2de93db42bde7daba97ed35e49cc12468f26e873d"
[metadata.files]
lark-parser = [
{file = "lark-parser-0.12.0.tar.gz", hash = "sha256:15967db1f1214013dca65b1180745047b9be457d73da224fcda3d9dd4e96a138"},
{file = "lark_parser-0.12.0-py2.py3-none-any.whl", hash = "sha256:0eaf30cb5ba787fe404d73a7d6e61df97b21d5a63ac26c5008c78a494373c675"},
]
h3-pg-4.2.2/scripts/sql2doc/pyproject.toml 0000664 0000000 0000000 00000000513 14752347156 0020424 0 ustar 00root root 0000000 0000000 [tool.poetry]
name = "documentation"
version = "1.0.0"
description = "Autogenerate postgres extension documentation from sql files"
authors = ["Zacharias Knudsen "]
[tool.poetry.dependencies]
python = "^3.10"
lark-parser = "^0.12.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
h3-pg-4.2.2/scripts/sql2doc/sql.lark 0000664 0000000 0000000 00000017742 14752347156 0017176 0 ustar 00root root 0000000 0000000 // statement list
start: custom_md_statement (";" custom_md_statement)* ";"?
// markdown or statement
custom_md_statement: (custom_markdown)* custom_decorated_statement
custom_decorated_statement: [custom_decorators] statement
// statement
?statement: create_type_stmt
| create_cast_stmt
| create_opcl_stmt
| create_oper_stmt
| create_func_stmt
| create_agg_stmt
| comment_on_stmt
custom_decorators: ("--@" /([^\n])+/)+
custom_markdown: /--\|[ \t]?([^\n])+/
| "--|\n"
// -----------------------------------------------------------------------------
// --------------- Basic SQL below ---------------------------------------------
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// CREATE TYPE name ...
create_type_stmt: "CREATE" "TYPE" CNAME "AS"? ("(" /([^\)])+/ ")")?
// -----------------------------------------------------------------------------
// CREATE CAST ...
create_cast_stmt: "CREATE" "CAST" "(" DATATYPE "AS" DATATYPE ")" "WITH" "FUNCTION" CNAME "(" /([^\)])+/ ")"
// -----------------------------------------------------------------------------
// CREATE OPERATOR CLASS name [ DEFAULT ] FOR TYPE data_type
// USING index_method [ FAMILY family_name ] AS
// { OPERATOR strategy_number operator_name [ ( op_type, op_type ) ] [ FOR SEARCH | FOR ORDER BY sort_family_name ]
// | FUNCTION support_number [ ( op_type [ , op_type ] ) ] function_name ( argument_type [, ...] )
// | STORAGE storage_type
// } [, ... ]
create_opcl_stmt: "CREATE" "OPERATOR" "CLASS" CNAME "DEFAULT"? "FOR" "TYPE" CNAME "USING" CNAME "AS" create_opcl_list
create_opcl_opts: "OPERATOR" SIGNED_NUMBER OPERATOR
| "FUNCTION" SIGNED_NUMBER fun_name "(" [argument_list] ")"
create_opcl_list: create_opcl_opts ("," create_opcl_opts)*
// -----------------------------------------------------------------------------
// CREATE OPERATOR name (
// PROCEDURE = function_name
// [, LEFTARG = left_type ] [, RIGHTARG = right_type ]
// [, COMMUTATOR = com_op ] [, NEGATOR = neg_op ]
// [, RESTRICT = res_proc ] [, JOIN = join_proc ]
// [, HASHES ] [, MERGES ]
// )
create_oper_stmt: "CREATE" "OPERATOR" OPERATOR "(" create_oper_opts ")"
create_oper_opt: /PROCEDURE/ "=" CNAME
| /LEFTARG/ "=" DATATYPE
| /RIGHTARG/ "=" DATATYPE
| /COMMUTATOR/ "=" OPERATOR
| /NEGATOR/ "=" OPERATOR
| /RESTRICT/ "=" CNAME
| /JOIN/ "=" CNAME
| /HASHES/
| /MERGES/
create_oper_opts: create_oper_opt ("," create_oper_opt)*
// -----------------------------------------------------------------------------
// CREATE [ OR REPLACE ] FUNCTION
// name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] )
// [ RETURNS rettype
// | RETURNS TABLE ( column_name column_type [, ...] ) ]
// { LANGUAGE lang_name
// | TRANSFORM { FOR TYPE type_name } [, ... ]
// | WINDOW
// | IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF
// | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
// | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
// | COST execution_cost
// | ROWS result_rows
// | SET configuration_parameter { TO value | = value | FROM CURRENT }
// | AS 'definition'
// | AS 'obj_file', 'link_symbol'
// } ...
// [ WITH ( attribute [, ...] ) ]
create_func_stmt: "CREATE" ("OR" "REPLACE")? "FUNCTION" fun_name "(" [argument_list] ")" [create_fun_rets] create_fun_opts*
?create_fun_rets: ("RETURNS" "TABLE" "(" create_fun_ret_table_columns ")")
| ("RETURNS" create_fun_rettype)
create_fun_opts: "LANGUAGE" (CNAME|"'" CNAME "'")
| ("IMMUTABLE" | "STABLE" | "VOLATILE" | ("NOT"? "LEAKPROOF"))
| (("CALLED" "ON" "NULL" "INPUT") | ("RETURNS" "NULL" "ON" "NULL" "INPUT") | "STRICT")
| ("PARALLEL" ("UNSAFE" | "RESTRICTED" | "SAFE"))
| "AS" string ("," string)?
create_fun_ret_table_columns: column_list
column_list: column ("," column)*
argument_list: argument ("," argument)*
!create_fun_rettype: ["SETOF"] DATATYPE
// -----------------------------------------------------------------------------
// CREATE [ OR REPLACE ] AGGREGATE name ( [ argmode ] [ argname ] arg_data_type [ , ... ] ) (
// SFUNC = sfunc,
// STYPE = state_data_type
// [ , SSPACE = state_data_size ]
// [ , FINALFUNC = ffunc ]
// [ , FINALFUNC_EXTRA ]
// [ , FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ]
// [ , COMBINEFUNC = combinefunc ]
// [ , SERIALFUNC = serialfunc ]
// [ , DESERIALFUNC = deserialfunc ]
// [ , INITCOND = initial_condition ]
// [ , MSFUNC = msfunc ]
// [ , MINVFUNC = minvfunc ]
// [ , MSTYPE = mstate_data_type ]
// [ , MSSPACE = mstate_data_size ]
// [ , MFINALFUNC = mffunc ]
// [ , MFINALFUNC_EXTRA ]
// [ , MFINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ]
// [ , MINITCOND = minitial_condition ]
// [ , SORTOP = sort_operator ]
// [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ]
// )
// create_agg_stmt: "CREATE" ("OR" "REPLACE")? "AGGREGATE" fun_name "(" [argument_list] ")" "(" agg_param_list ")"
create_agg_stmt: "CREATE" ("OR" "REPLACE")? "AGGREGATE" fun_name "(" [argument_list] ")" "(" agg_param_list ")"
agg_param_list: agg_param ("," agg_param)*
agg_param: "sfunc" "=" fun_name
| "stype" "=" DATATYPE
| "finalfunc" "=" fun_name
| "parallel" "=" ("safe"|"restricted"|"unsafe")
// -----------------------------------------------------------------------------
// COMMENT ON
// {
// ...
// CAST (source_type AS target_type) |
// ...
// FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) |
// ...
// OPERATOR operator_name (left_type, right_type) |
// ...
// } IS 'text'
comment_on_stmt: "COMMENT" "ON" comment_on_type "IS" string
comment_on_type: "CAST" "(" DATATYPE "AS" DATATYPE ")" -> comment_on_cast
| "FUNCTION" fun_name "(" [argument_list] ")" -> comment_on_function
| "OPERATOR" OPERATOR "(" argument "," argument ")" -> comment_on_operator
// -----------------------------------------------------------------------------
// SIMPLE RULES
column: CNAME DATATYPE
argument: [ARGMODE] [CNAME] DATATYPE ("DEFAULT" expr)?
ARGMODE.2: "IN" | "OUT" | "INOUT"
DATATYPE_SCALAR: "h3index"
| "raster"
| "summarystats"
| "h3_raster_summary_stats"
| "h3_raster_class_summary_item"
| "jsonb"
| "bigint"
| "boolean"
| "cstring"
| "double" WS "precision"
| "float"
| "geography"
| "geometry"
| "bytea"
| "int32"
| "int8"
| "integer"
| "internal"
| "int"
| "point"
| "polygon"
| "record"
| "text"
| "void"
DATATYPE: DATATYPE_SCALAR "[]"?
fun_name: [CNAME "."] CNAME
?expr: atom | string
atom: SIGNED_NUMBER -> number
| "TRUE" -> true
| "FALSE" -> false
string: STRING
// -----------------------------------------------------------------------------
// TERMINALS
// Terminals are used to match text into symbols.
// They can be defined as a combination of literals and other terminals.
LITERAL: SIGNED_NUMBER | ESCAPED_STRING
OPERATOR: ("+"|"-"|"*"|"/"|"<"|">"|"="|"~"|"!"|"@"|"#"|"%"|"^"|"&"|"|"|"`"|"?")+
STRING: "'" /([^'])+/ "'"
| "$$" /(.|\n)*?/ "$$"
MULTI_COMMENT : "/*" /(.|\n)+/ "*/"
SINGLE_COMMENT: "--" /[^\|@]/ /([^\n])*/
COMMAND: "\\" /([^\n])+/
// -----------------------------------------------------------------------------
%import common (CNAME, INT, ESCAPED_STRING, _STRING_ESC_INNER, SIGNED_NUMBER, WS, NEWLINE)
%ignore COMMAND
%ignore MULTI_COMMENT
%ignore SINGLE_COMMENT
%ignore WS