├── .gitignore ├── Dockerfile ├── LICENSE.txt ├── Makefile ├── README.md ├── doc ├── generate.py ├── hexagons-example.png ├── pgh3.md └── ukis-logo.png ├── docker-compose.yaml ├── expected ├── compact_test.out ├── hierarchy_test.out ├── index_test.out ├── misc_test.out └── region_test.out ├── pgh3.control ├── sql ├── compact_test.sql ├── hierarchy_test.sql ├── index_test.sql ├── misc_test.sql ├── pgh3.sql └── region_test.sql └── src ├── compact.c ├── hierarchy.c ├── index.c ├── misc.c ├── neighbor.c ├── pgh3.c ├── region.c ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.swp 4 | *.swo 5 | *.ignored 6 | sql/*--*.sql 7 | /results 8 | .idea 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM pgrouting/pgrouting:13-3.1-3.1.3 2 | 3 | ARG CMAKE_VERSION=3.22.2 4 | ARG H3_VERSION=3.7.2 5 | ARG CPUS=4 6 | 7 | # dependencies 8 | RUN apt-get update 9 | RUN apt-get install -y git wget gcc build-essential libssl-dev clang-format cmake-curses-gui lcov doxygen libtool postgresql-server-dev-13 10 | 11 | # install cmake 12 | RUN wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}.tar.gz 13 | RUN tar xzf cmake-${CMAKE_VERSION}.tar.gz 14 | WORKDIR cmake-${CMAKE_VERSION} 15 | RUN ./bootstrap 16 | RUN make 17 | RUN make install 18 | WORKDIR .. 19 | 20 | # install h3 binding 21 | RUN mkdir h3 22 | WORKDIR h3 23 | RUN wget https://github.com/uber/h3/archive/refs/tags/v${H3_VERSION}.tar.gz 24 | RUN tar xf v${H3_VERSION}.tar.gz 25 | RUN mkdir build 26 | WORKDIR build 27 | RUN cmake -DCMAKE_C_FLAGS=-fPIC ../h3-${H3_VERSION} 28 | RUN make -j $CPUS 29 | RUN make install 30 | WORKDIR ../../ 31 | 32 | # install pgh3 33 | RUN git clone https://github.com/Anagraph/pgh3 34 | WORKDIR pgh3 35 | RUN git checkout ca5307f 36 | RUN make -j ${CPUS} 37 | RUN make install 38 | WORKDIR .. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EXTENSION = pgh3 2 | EXTVERSION = $(shell grep default_version $(EXTENSION).control | \ 3 | sed -e "s/default_version[[:space:]]*=[[:space:]]*'\([^']*\)'/\1/") 4 | 5 | 6 | #OBJS = $(patsubst %.c,%,$(wildcard src/*.c)) 7 | OBJS = $(patsubst %.c,%.o,$(wildcard src/*.c)) 8 | MODULE_big = $(EXTENSION) 9 | DATA = $(sort $(filter-out $(wildcard sql/*--*.sql),$(wildcard sql/*.sql))) 10 | # link to libmagic 11 | SHLIB_LINK += -lh3 12 | DOCS = $(wildcard doc/*.md) 13 | PG_CONFIG = pg_config 14 | PG91 = $(shell $(PG_CONFIG) --version | grep -qE " 8\.| 9\.0" && echo no || echo yes) 15 | REGRESS = index_test region_test hierarchy_test misc_test compact_test 16 | 17 | # the -D switch to add the version of the extension is compiler specific for gcc 18 | override CFLAGS += -I/usr/local/include/ -L/usr/local/lib/ -DEXTVERSION='"$(EXTVERSION)"' -std=c11 19 | 20 | 21 | ifeq ($(PG91),yes) 22 | all: sql/$(EXTENSION)--$(EXTVERSION).sql 23 | 24 | sql/$(EXTENSION)--$(EXTVERSION).sql: sql/$(EXTENSION).sql 25 | cp $< $@ 26 | 27 | DATA = $(sort $(wildcard sql/*--*.sql) sql/$(EXTENSION)--$(EXTVERSION).sql) 28 | EXTRA_CLEAN = sql/$(EXTENSION)--$(EXTVERSION).sql 29 | endif 30 | 31 | PGXS := $(shell $(PG_CONFIG) --pgxs) 32 | include $(PGXS) 33 | 34 | 35 | .PHONY: doc 36 | 37 | doc: 38 | python doc/generate.py h3 >doc/pgh3.md 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [PostgreSQL](https://www.postgresql.org) extension for the [H3](https://uber.github.io/h3) hierarchical geospatial indexing system 2 | 3 |  4 | 5 | This extension integrates with the [PostGIS](https://postgis.net/) geometry type. Namely `Point`, `Polygon` and `MultiPolygon` geometries are supported. 6 | 7 | To see the functions implemented in this extension please refer to the [SQL API](doc/pgh3.md). 8 | 9 | 10 | ## Installation 11 | 12 | __Compile H3 as a shared library__. The source is available on [github](https://github.com/uber/h3), there are als 13 | o detailed instructions for building H3 available in that repository, but to make H3 work with this extension there 14 | are additional build flags required. There are essentialy two ways to compile H3 which allow building the pgh3 extension 15 | later. 16 | 17 | Compile H3 as a static library. This allows statically linking H3 to the pgh3 extension. 18 | 19 | cmake -DCMAKE_C_FLAGS=-fPIC . 20 | make 21 | sudo make install 22 | 23 | Or compile H3 as a shared library. This creates a dynamically loaded library which may also be used by other programs. When 24 | you update the H3 library, all programms using it will automatically use the new version (PostgreSQL needs to 25 | be restarted for that). 26 | 27 | cmake -DBUILD_SHARED_LIBS=1 -DCMAKE_INSTALL_PREFIX:PATH=/usr . 28 | make 29 | sudo make install 30 | 31 | In case you have set up your `LD_LIBRARY_PATH` for PostgreSQL to also look in `/usr/local` for libraries, you can ommit the `-DCMAKE_INSTALL_PREFIX:PATH=/usr` switch. 32 | 33 | __Compile the pgh3 extension__: 34 | 35 | make 36 | sudo make install 37 | 38 | __Run the tests__ (this is an optional step): 39 | 40 | make installcheck 41 | 42 | ## Usage 43 | 44 | Before using this extension, it needs to be added to the databases using postgresqls [CREATE EXTENSION](https://www.postgresql.org/docs/current/static/sql-createextension.html) command: 45 | 46 | create extension postgis; -- dependency of pgh3, must be created first 47 | create extension pgh3; 48 | 49 | For usage examples see the unittests in the `sql/*_test.sql` files. 50 | 51 | ### Configuration 52 | 53 | This extensions allows configuring some parts of its behaviour. This configuration is done using additional keys to `postgresql.conf` 54 | 55 | #### pgh3.polyfill_mem 56 | 57 | The H3 `polyfill` function requires a preallocation of the memory for the generates indexes. Depending of the size of the 58 | given polygon, its shape and the resolution this may exhaust the memory given to this extension. In this case 59 | this function will be terminated by the database server and a corresponding notice will be given. 60 | 61 | This memory limit can be increased using the `pgh3.polyfill_mem` configuration parameter in the `postgresql.conf` file. The 62 | default value for this setting 1024MB (PostgreSQL internal `MaxAllocSize`). Syntax for the setting is 63 | 64 | pgh3.polyfill_mem = 1024MB 65 | 66 | For values larger than `MaxAllocSize`, the PostgreSQL `MemoryContextAllocHuge` allocator will be used. 67 | 68 | _This setting is only available when using a PostgreSQL version >= 10_. On earlier versions the memory limit is set to 1024MB. 69 | 70 | ### Error handling 71 | 72 | Most errors emmitted by this extension are making use of the [PostgreSQL error codes](https://www.postgresql.org/docs/current/errcodes-appendix.html). 73 | This allows a more explicit handling of certain errors in application code or PL/pgSQL procedures. To give a short example using the `psql` command line: 74 | 75 | h3=# \set VERBOSITY verbose 76 | h3=# select count(*) from h3_polyfill(st_geomfromtext('POLYGON((30 10,40 40,20 40,10 20,30 10))'), 10); 77 | ERROR: 53400: pgh3.polyfill_mem: requested memory allocation (7.58GB) exceeded the configured value (1023MB). 78 | CONTEXT: PL/pgSQL function h3_polyfill(geometry,integer) line 4 at RETURN QUERY 79 | LOCATION: __h3_polyfill_palloc0, util.c:158 80 | 81 | Looking at he PostgreSQL documentaion, error code `53400` stands for `configuration_limit_exceeded`. 82 | 83 | 84 | ## TODO 85 | 86 | * Implement more parts of the H3 API 87 | * Use the `numeric`-type for H3 indexes? 88 | 89 | # Legal an Licensing 90 | 91 | This software is licensed under the [Apache 2.0 License](LICENSE.txt). 92 | 93 | (c) 2018 German Aerospace Center (DLR); German Remote Sensing Data Center; Department: Geo-Risks and Civil Security 94 | 95 | [](https://www.dlr.de/eoc/en/desktopdefault.aspx/tabid-5413/10560_read-21914/) 96 | -------------------------------------------------------------------------------- /doc/generate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Pulls documentation from the database schema 3 | 4 | import sys 5 | import psycopg2 6 | import psycopg2.extras 7 | 8 | HEADER=''' 9 | # PostgreSQL H3 extension 10 | 11 | Postgresql extension to wrap the hexagonal hierarchical geospatial indexing system of [h3 library](https://github.com/uber/h3). 12 | 13 | ''' 14 | 15 | def function_defs(cur, ffilter='h3\\_%'): 16 | cur.execute(''' 17 | select proc.oid, 18 | proc.proname as name, 19 | pg_get_function_identity_arguments(proc.oid) as args, 20 | pg_get_function_result(proc.oid) as result, 21 | dsc.description as desc 22 | from pg_proc proc 23 | left join pg_description dsc on dsc.objoid = proc.oid 24 | where proname like %s escape '\\' 25 | order by proc.proname''', [ffilter]) 26 | return cur.fetchall() 27 | 28 | 29 | def print_function_def(fnc): 30 | sys.stdout.write("### {0}".format(fnc['name'])) 31 | sys.stdout.write('\n\n{0}'.format(fnc['desc'] or '')) 32 | sys.stdout.write("\n\n__Synopsis:__ `{0}({1})`".format(fnc['name'], fnc['args'])) 33 | sys.stdout.write("\n\n__Returntype:__ `{0}`".format(fnc['result'])) 34 | 35 | sys.stdout.write('\n\n\n') 36 | 37 | 38 | if __name__ == '__main__': 39 | dbname = sys.argv[1] 40 | conn = psycopg2.connect(dbname=dbname) 41 | cur = conn.cursor(cursor_factory = psycopg2.extras.DictCursor) 42 | 43 | sys.stdout.write(HEADER) 44 | sys.stdout.write(''' 45 | ## Functions\n\n''') 46 | for fnc in function_defs(cur): 47 | print_function_def(fnc) 48 | 49 | sys.stdout.write(''' 50 | ## Internal functions 51 | 52 | These are mostly function for which a more comfortable wrapper function exists. 53 | 54 | ''') 55 | for fnc in function_defs(cur, ffilter='_h3_%'): 56 | print_function_def(fnc) 57 | 58 | -------------------------------------------------------------------------------- /doc/hexagons-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlr-eoc/pgh3/a225ac6da928cb0668780221da64061d3c4b1d54/doc/hexagons-example.png -------------------------------------------------------------------------------- /doc/pgh3.md: -------------------------------------------------------------------------------- 1 | 2 | # PostgreSQL H3 extension 3 | 4 | Postgresql extension to wrap the hexagonal hierarchical geospatial indexing system of [h3 library](https://github.com/uber/h3). 5 | 6 | 7 | ## Functions 8 | 9 | ### h3_compact 10 | 11 | Compacts the array of given H3 indexes as best as possible 12 | 13 | __Synopsis:__ `h3_compact(h3indexes text[])` 14 | 15 | __Returntype:__ `SETOF text` 16 | 17 | 18 | ### h3_edge_length_km 19 | 20 | Average hexagon edge length in kilometers at the given resolution. 21 | 22 | __Synopsis:__ `h3_edge_length_km(resolution integer)` 23 | 24 | __Returntype:__ `double precision` 25 | 26 | 27 | ### h3_edge_length_m 28 | 29 | Average hexagon edge length in meters at the given resolution. 30 | 31 | __Synopsis:__ `h3_edge_length_m(resolution integer)` 32 | 33 | __Returntype:__ `double precision` 34 | 35 | 36 | ### h3_ext_version 37 | 38 | Returns the version number of the H3 extension. This is not the version number of the h3 library itself. 39 | 40 | __Synopsis:__ `h3_ext_version()` 41 | 42 | __Returntype:__ `text` 43 | 44 | 45 | ### h3_geo_to_h3index 46 | 47 | Get the H3 index for the point at the given resolution. 48 | 49 | __Synopsis:__ `h3_geo_to_h3index(p point, resolution integer)` 50 | 51 | __Returntype:__ `text` 52 | 53 | 54 | ### h3_geo_to_h3index 55 | 56 | Get the H3 index for the PostGIS point geometry at the given resolution. 57 | 58 | __Synopsis:__ `h3_geo_to_h3index(g geometry, resolution integer)` 59 | 60 | __Returntype:__ `text` 61 | 62 | 63 | ### h3_get_basecell 64 | 65 | Get the base cell for a H3 index. 66 | 67 | __Synopsis:__ `h3_get_basecell(h3index text)` 68 | 69 | __Returntype:__ `integer` 70 | 71 | 72 | ### h3_get_basecells 73 | 74 | Returns all base cells. 75 | 76 | __Synopsis:__ `h3_get_basecells()` 77 | 78 | __Returntype:__ `SETOF text` 79 | 80 | 81 | ### h3_get_resolution 82 | 83 | Get the resolution for a H3 index. 84 | 85 | __Synopsis:__ `h3_get_resolution(h3index text)` 86 | 87 | __Returntype:__ `integer` 88 | 89 | 90 | ### h3_h3index_is_valid 91 | 92 | Check if a H3 index is valid. 93 | 94 | __Synopsis:__ `h3_h3index_is_valid(h3index text)` 95 | 96 | __Returntype:__ `boolean` 97 | 98 | 99 | ### h3_h3index_to_geo 100 | 101 | Convert a H3 index to coordinates. Returned as a PostGIS point geometry. 102 | 103 | __Synopsis:__ `h3_h3index_to_geo(h3index text)` 104 | 105 | __Returntype:__ `geometry` 106 | 107 | 108 | ### h3_h3index_to_geoboundary 109 | 110 | Convert the boundary of H3 index to polygon coordinates. Returned as a PostGIS polygon geometry. 111 | 112 | __Synopsis:__ `h3_h3index_to_geoboundary(h3index text)` 113 | 114 | __Returntype:__ `geometry` 115 | 116 | 117 | ### h3_hexagon_area_km2 118 | 119 | Average hexagon area in square kilometers at the given resolution. 120 | 121 | __Synopsis:__ `h3_hexagon_area_km2(resolution integer)` 122 | 123 | __Returntype:__ `double precision` 124 | 125 | 126 | ### h3_hexagon_area_m2 127 | 128 | Average hexagon area in square meters at the given resolution. 129 | 130 | __Synopsis:__ `h3_hexagon_area_m2(resolution integer)` 131 | 132 | __Returntype:__ `double precision` 133 | 134 | 135 | ### h3_kring 136 | 137 | Returns the neighbor indices within the given distance. 138 | 139 | __Synopsis:__ `h3_kring(h3index text, distance integer)` 140 | 141 | __Returntype:__ `SETOF text` 142 | 143 | 144 | ### h3_polyfill 145 | 146 | Fills the given PostGIS polygon or multipolygon with hexagons at the given resolution. Holes in the polygon will be omitted. 147 | 148 | The H3 `polyfill` function requires a preallocation of the memory for the generates indexes. Depending on the size of the 149 | given polygon, its shape and resolution this may exhaust the memory given to this extension. In this case 150 | this function will be terminated by the database server and a corresponding notice will be given. 151 | 152 | The memory limit can be increased using the `pgh3.polyfill_mem` configuration parameter in the `postgresql.conf` file. The 153 | default value for this setting 1024MB (PostgreSQL internal `MaxAllocSize`). Syntax for the setting is 154 | 155 | pgh3.polyfill_mem = 1024MB 156 | 157 | For values larger than `MaxAllocSize`, the PostgreSQL `MemoryContextAllocHuge` allocator will be used. 158 | 159 | _This setting is only available when using a PostgreSQL version >= 10_. On earlier versions the memory limit is set to 1024MB. 160 | 161 | If this does not resolve the issue, there is essentially one way to work around this issue: Cut the polygon into segments and run this function to each of them seperately. The PostGIS functions `ST_Subdivide`, `ST_Split` and `ST_Segmentize` may be helpful. 162 | 163 | 164 | __Synopsis:__ `h3_polyfill(geom geometry, resolution integer)` 165 | 166 | __Returntype:__ `SETOF text` 167 | 168 | 169 | ### h3_polyfill_estimate 170 | 171 | Estimate the number of indexes required to fill the given PostGIS polygon or multipolygon with hexagons at the given resolution. Holes in the polygon will be omitted. 172 | 173 | __Synopsis:__ `h3_polyfill_estimate(geom geometry, resolution integer)` 174 | 175 | __Returntype:__ `integer` 176 | 177 | 178 | ### h3_to_children 179 | 180 | Returns the children (finer) indexes contained the given index. 181 | 182 | __Synopsis:__ `h3_to_children(h3index text, resolution integer)` 183 | 184 | __Returntype:__ `SETOF text` 185 | 186 | 187 | ### h3_to_parent 188 | 189 | Returns the parent (coarser) index containing the given index. 190 | 191 | __Synopsis:__ `h3_to_parent(h3index text, resolution integer)` 192 | 193 | __Returntype:__ `text` 194 | 195 | 196 | ### h3_uncompact 197 | 198 | Uncompacts the array of given H3 indexes 199 | 200 | __Synopsis:__ `h3_uncompact(h3indexes text[], resolution integer)` 201 | 202 | __Returntype:__ `SETOF text` 203 | 204 | 205 | 206 | ## Internal functions 207 | 208 | These are mostly function for which a more comfortable wrapper function exists. 209 | 210 | ### _h3_h3index_to_geo 211 | 212 | Convert a H3 index to coordinates. Returned as a postgresql point type. 213 | 214 | __Synopsis:__ `_h3_h3index_to_geo(h3index text)` 215 | 216 | __Returntype:__ `point` 217 | 218 | 219 | ### _h3_h3index_to_geoboundary 220 | 221 | Convert the boundary of H3 index to polygon coordinates. Returned as a postgresql native polygon type. 222 | 223 | __Synopsis:__ `_h3_h3index_to_geoboundary(h3index text)` 224 | 225 | __Returntype:__ `polygon` 226 | 227 | 228 | ### _h3_polyfill_polygon_c 229 | 230 | Fills the given exterior ring with hexagons at the given resolution. The interior_ring polygons are understood as holes and will be omitted. 231 | 232 | __Synopsis:__ `_h3_polyfill_polygon_c(exterior_ring polygon, interior_rings polygon[], resolution integer)` 233 | 234 | __Returntype:__ `SETOF text` 235 | 236 | 237 | ### _h3_polyfill_polygon_estimate_c 238 | 239 | Estimate the number of indexes required to fill the given exterior ring with hexagons at the given resolution. The interior_ring polygons are understood as holes and will be omitted. 240 | 241 | __Synopsis:__ `_h3_polyfill_polygon_estimate_c(exterior_ring polygon, interior_rings polygon[], resolution integer)` 242 | 243 | __Returntype:__ `integer` 244 | 245 | 246 | -------------------------------------------------------------------------------- /doc/ukis-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlr-eoc/pgh3/a225ac6da928cb0668780221da64061d3c4b1d54/doc/ukis-logo.png -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.2" 2 | services: 3 | db: 4 | image: postgresql-pgh3 5 | build: 6 | context: . 7 | restart: always 8 | environment: 9 | POSTGRES_DB: postgres 10 | POSTGRES_PASSWORD: mypassword 11 | command: postgres -c shared_buffers=2GB -c max_connections=20 -c effective_cache_size=6GB -c maintenance_work_mem=1GB -c checkpoint_completion_target=0.9 -c wal_buffers=16MB -c default_statistics_target=500 -c random_page_cost=1.1 -c effective_io_concurrency=200 -c work_mem=52428kB -c min_wal_size=4GB -c max_wal_size=16GB -c max_worker_processes=2 -c max_parallel_workers_per_gather=1 12 | ports: 13 | - target: 5432 14 | published: 8433 15 | protocol: tcp 16 | mode: host 17 | volumes: 18 | - pg_data:/var/lib/postgresql/data 19 | volumes: 20 | pgdata: 21 | -------------------------------------------------------------------------------- /expected/compact_test.out: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | NOTICE: extension "postgis" already exists, skipping 3 | create extension if not exists pgh3; 4 | NOTICE: extension "pgh3" already exists, skipping 5 | /* 6 | Indexes are taken from the testCompact.c "sunnyvale" unittests 7 | 8 | generated with: 9 | 10 | echo '89283470c27ffff' | kRing 9 | sed -E "s/^(.*)$/'\1',/g" | sed 'N;s/\n/ /;' | sed 'N;s/\n/ /;' 11 | */ 12 | create table sunnyvale (h3index text); 13 | insert into sunnyvale (h3index) values 14 | ('89283470c27ffff'), ('89283470c23ffff'), ('89283470c2fffff'), ('89283470d5bffff'), ('89283470d53ffff'), ('89283470dcbffff'), ('89283470c37ffff'), ('89283470c33ffff'), 15 | ('89283470c3bffff'), ('89283470c2bffff'), ('89283470893ffff'), ('89283470897ffff'), ('89283470d4bffff'), ('89283470d43ffff'), ('89283470d57ffff'), ('89283470dcfffff'), 16 | ('89283470dc3ffff'), ('89283470ddbffff'), ('89283470cafffff'), ('89283470cabffff'), ('89283470c07ffff'), ('89283470c0fffff'), ('89283470c77ffff'), ('89283470c67ffff'), 17 | ('8928347089bffff'), ('89283470883ffff'), ('89283470887ffff'), ('892834708b3ffff'), ('89283470d4fffff'), ('89283470d47ffff'), ('89283470d0bffff'), ('89283470d1bffff'), 18 | ('89283470dc7ffff'), ('89283470dd7ffff'), ('89283470dd3ffff'), ('89283470ca7ffff'), ('89283470ca3ffff'), ('89283470cbbffff'), ('89283470c17ffff'), ('89283470c03ffff'), 19 | ('89283470c0bffff'), ('89283470c73ffff'), ('89283470c63ffff'), ('89283470c6fffff'), ('892834708d7ffff'), ('8928347088bffff'), ('8928347088fffff'), ('892834708bbffff'), 20 | ('892834708a3ffff'), ('892834708b7ffff'), ('89283470d7bffff'), ('89283470d73ffff'), ('89283470d0fffff'), ('89283470d03ffff'), ('89283470d13ffff'), ('89283470d8bffff'), 21 | ('89283470d9bffff'), ('89283472b6fffff'), ('89283472b6bffff'), ('89283470cb7ffff'), ('89283470cb3ffff'), ('89283470c87ffff'), ('89283470c8fffff'), ('89283470c13ffff'), 22 | ('89283470c1bffff'), ('89283470c57ffff'), ('89283470c47ffff'), ('89283470c7bffff'), ('89283470c6bffff'), ('892834708d3ffff'), ('892834708c3ffff'), ('892834708c7ffff'), 23 | ('89283470813ffff'), ('89283470817ffff'), ('892834708abffff'), ('892834708afffff'), ('892834708a7ffff'), ('89283470d6bffff'), ('89283470d63ffff'), ('89283470d77ffff'), 24 | ('89283470d3bffff'), ('89283470d07ffff'), ('89283470d17ffff'), ('89283470d8fffff'), ('89283470d83ffff'), ('89283470d93ffff'), ('89283472b67ffff'), ('89283472b63ffff'), 25 | ('89283472b7bffff'), ('89283472b4fffff'), ('89283472b4bffff'), ('89283470c97ffff'), ('89283470c83ffff'), ('89283470c8bffff'), ('89283470cc7ffff'), ('89283470ccfffff'), 26 | ('89283470c53ffff'), ('89283470c43ffff'), ('89283470c4fffff'), ('89283470eb7ffff'), ('89283470ea7ffff'), ('892834708dbffff'), ('892834708cbffff'), ('892834708cfffff'), 27 | ('8928347081bffff'), ('89283470803ffff'), ('89283470807ffff'), ('89283470833ffff'), ('89283470837ffff'), ('892834709dbffff'), ('892834709d3ffff'), ('89283470d6fffff'), 28 | ('89283470d67ffff'), ('89283470d2bffff'), ('89283470d23ffff'), ('89283470d33ffff'), ('89283470dabffff'), ('89283470dbbffff'), ('89283470d87ffff'), ('89283470d97ffff'), 29 | ('89283472b2fffff'), ('89283472b2bffff'), ('89283472b77ffff'), ('89283472b73ffff'), ('89283472b47ffff'), ('89283472b43ffff'), ('89283472b5bffff'), ('89283472a2fffff'), 30 | ('89283470c93ffff'), ('89283470c9bffff'), ('89283470cd7ffff'), ('89283470cc3ffff'), ('89283470ccbffff'), ('89283470127ffff'), ('89283470c5bffff'), ('89283470c4bffff'), 31 | ('89283470eb3ffff'), ('89283470ea3ffff'), ('89283470eafffff'), ('89283470e37ffff'), ('89283470e27ffff'), ('89283470853ffff'), ('89283470857ffff'), ('8928347080bffff'), 32 | ('8928347080fffff'), ('8928347083bffff'), ('89283470823ffff'), ('89283470827ffff'), ('892834709cbffff'), ('892834709c3ffff'), ('892834709d7ffff'), ('8928347099bffff'), 33 | ('89283470993ffff'), ('89283470d2fffff'), ('89283470d27ffff'), ('89283470d37ffff'), ('89283470dafffff'), ('89283470da3ffff'), ('89283470db3ffff'), ('8928347764bffff'), 34 | ('8928347765bffff'), ('89283472b27ffff'), ('89283472b23ffff'), ('89283472b3bffff'), ('89283472b0fffff'), ('89283472b0bffff'), ('89283472b57ffff'), ('89283472b53ffff'), 35 | ('89283472a27ffff'), ('89283472a23ffff'), ('89283472a2bffff'), ('89283472a67ffff'), ('89283472a6fffff'), ('89283470cd3ffff'), ('89283470cdbffff'), ('89283470137ffff'), 36 | ('89283470123ffff'), ('8928347012fffff'), ('89283470e97ffff'), ('89283470e87ffff'), ('89283470ebbffff'), ('89283470eabffff'), ('89283470e33ffff'), ('89283470e23ffff'), 37 | ('89283470e2fffff'), ('8928347085bffff'), ('89283470843ffff'), ('89283470847ffff'), ('89283470873ffff'), ('89283470877ffff'), ('8928347082bffff'), ('8928347082fffff'), 38 | ('8928347095bffff'), ('89283470953ffff'), ('892834709cfffff'), ('892834709c7ffff'), ('8928347098bffff'), ('89283470983ffff'), ('89283470997ffff'), ('8928347725bffff'), 39 | ('89283477253ffff'), ('892834772cbffff'), ('892834772dbffff'), ('89283470da7ffff'), ('89283470db7ffff'), ('8928347764fffff'), ('89283477643ffff'), ('89283477653ffff'), 40 | ('892834776cbffff'), ('89283472b37ffff'), ('89283472b33ffff'), ('89283472b07ffff'), ('89283472b03ffff'), ('89283472b1bffff'), ('89283472bcfffff'), ('89283472bcbffff'), 41 | ('89283472a37ffff'), ('89283472a33ffff'), ('89283472a3bffff'), ('89283472a77ffff'), ('89283472a63ffff'), ('89283472a6bffff'), ('892834701a7ffff'), ('892834701afffff'), 42 | ('89283470133ffff'), ('8928347013bffff'), ('8928347012bffff'), ('89283470e93ffff'), ('89283470e83ffff'), ('89283470e8fffff'), ('89283470e17ffff'), ('89283470e07ffff'), 43 | ('89283470e3bffff'), ('89283470e2bffff'), ('89283470a93ffff'), ('89283470a97ffff'), ('8928347084bffff'), ('8928347084fffff'), ('8928347087bffff'), ('89283470863ffff'), 44 | ('89283470867ffff'), ('89283470b93ffff'), ('89283470b97ffff'), ('8928347094bffff'), ('89283470943ffff'), ('89283470957ffff'), ('8928347091bffff'), ('89283470913ffff'), 45 | ('8928347098fffff'), ('89283470987ffff'), ('8928347724bffff'), ('89283477243ffff'), ('89283477257ffff'), ('892834772cfffff'), ('892834772c3ffff'), ('892834772d3ffff'), 46 | ('8928347766bffff'), ('8928347767bffff'), ('89283477647ffff'), ('89283477657ffff'), ('892834776cfffff'), ('892834776c3ffff'), ('892834776dbffff'), ('89283472bafffff'), 47 | ('89283472babffff'), ('89283472b17ffff'), ('89283472b13ffff'), ('89283472bc7ffff'), ('89283472bc3ffff'), ('89283472bdbffff'), ('89283472aafffff'); 48 | select count(*) from h3_compact(array(select h3index from sunnyvale)); 49 | count 50 | ------- 51 | 73 52 | (1 row) 53 | 54 | select h3_compact(array((select h3_to_children('89283470c27ffff', 10)))); 55 | h3_compact 56 | ----------------- 57 | 89283470c27ffff 58 | (1 row) 59 | 60 | select h3_uncompact(array['89283470c27ffff'], 10) except select h3_to_children('89283470c27ffff', 10); 61 | h3_uncompact 62 | -------------- 63 | (0 rows) 64 | 65 | -- test a full round trip (1/2). should return 0 rows. 66 | select h3index from sunnyvale 67 | except 68 | select h3_uncompact(array[h3_compact(array(select h3index from sunnyvale))], 9); 69 | h3index 70 | --------- 71 | (0 rows) 72 | 73 | -- test a full round trip (2/2). should return 0 rows. 74 | select h3_uncompact(array[h3_compact(array(select h3index from sunnyvale))], 9) 75 | except 76 | select h3index from sunnyvale; 77 | h3_uncompact 78 | -------------- 79 | (0 rows) 80 | 81 | -------------------------------------------------------------------------------- /expected/hierarchy_test.out: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | NOTICE: extension "postgis" already exists, skipping 3 | create extension if not exists pgh3; 4 | NOTICE: extension "pgh3" already exists, skipping 5 | /* hierarchy */ 6 | select h3_to_parent('85639c63fffffff', 4); 7 | h3_to_parent 8 | ----------------- 9 | 84639c7ffffffff 10 | (1 row) 11 | 12 | select h3_to_parent('85639c63fffffff', 1); 13 | h3_to_parent 14 | ----------------- 15 | 8163bffffffffff 16 | (1 row) 17 | 18 | select h3_to_children('82639ffffffffff', 3); 19 | h3_to_children 20 | ----------------- 21 | 836398fffffffff 22 | 836399fffffffff 23 | 83639afffffffff 24 | 83639bfffffffff 25 | 83639cfffffffff 26 | 83639dfffffffff 27 | 83639efffffffff 28 | (7 rows) 29 | 30 | -------------------------------------------------------------------------------- /expected/index_test.out: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | create extension if not exists pgh3; 3 | /* indexing */ 4 | select trunc(p[0]::numeric, 5) x, trunc(p[1]::numeric, 5) y 5 | from ( 6 | select h3_h3index_to_geo('85639c63fffffff')::point p 7 | ) f; 8 | x | y 9 | ----------+--------- 10 | 52.12336 | 9.40691 11 | (1 row) 12 | 13 | select trunc(st_x(p)::numeric, 5) x, trunc(st_y(p)::numeric, 5) y 14 | from ( 15 | select h3_h3index_to_geo('85639c63fffffff')::geometry p 16 | ) f; 17 | x | y 18 | ----------+--------- 19 | 52.12336 | 9.40691 20 | (1 row) 21 | 22 | select h3_geo_to_h3index('(9.40691761982618,52.1233617183044)'::point, 5); 23 | h3_geo_to_h3index 24 | ------------------- 25 | 851f1383fffffff 26 | (1 row) 27 | 28 | select h3_h3index_is_valid('85639c63fffffff'); -- is valid 29 | h3_h3index_is_valid 30 | --------------------- 31 | t 32 | (1 row) 33 | 34 | select h3_h3index_is_valid('85639cdddddddd'); -- is invalid 35 | h3_h3index_is_valid 36 | --------------------- 37 | f 38 | (1 row) 39 | 40 | select h3_get_resolution('85639c63fffffff'); -- = 5 41 | h3_get_resolution 42 | ------------------- 43 | 5 44 | (1 row) 45 | 46 | select h3_get_basecell('85639c63fffffff'); -- = 5 47 | h3_get_basecell 48 | ----------------- 49 | 49 50 | (1 row) 51 | 52 | -------------------------------------------------------------------------------- /expected/misc_test.out: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | NOTICE: extension "postgis" already exists, skipping 3 | create extension if not exists pgh3; 4 | NOTICE: extension "pgh3" already exists, skipping 5 | /******* misc functions *********************************/ 6 | select h3_hexagon_area_km2(4); 7 | h3_hexagon_area_km2 8 | --------------------- 9 | 1770.323552 10 | (1 row) 11 | 12 | select h3_hexagon_area_m2(4); 13 | h3_hexagon_area_m2 14 | -------------------- 15 | 1770323552 16 | (1 row) 17 | 18 | select h3_edge_length_km(4); 19 | h3_edge_length_km 20 | ------------------- 21 | 22.6063794 22 | (1 row) 23 | 24 | select h3_edge_length_m(4); 25 | h3_edge_length_m 26 | ------------------ 27 | 22606.3794 28 | (1 row) 29 | 30 | -------------------------------------------------------------------------------- /expected/region_test.out: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | NOTICE: extension "postgis" already exists, skipping 3 | create extension if not exists pgh3; 4 | NOTICE: extension "pgh3" already exists, skipping 5 | /* test data */ 6 | create table test_geometries(name text primary key, geom geometry); 7 | insert into test_geometries (name, geom) values 8 | ('polygon with hole', 'POLYGON((51.0708433295253 -23.5514097581317,61.0708433295253 11.4485902418683,31.0708433295253 6.44859024186827,26.0708433295253 -13.5514097581317,51.0708433295253 -23.5514097581317),(36.0708433295253 -3.55140975813173,51.0708433295253 1.44859024186827,46.0708433295253 -13.5514097581317,36.0708433295253 -3.55140975813173))'::geometry), 9 | ('multipolygon with hole', 'MULTIPOLYGON(((1.42997600913934 32.3311063409985,-18.5700239908607 37.3311063409985,6.42997600913934 22.3311063409985,1.42997600913934 32.3311063409985)),((-18.5700239908607 27.3311063409985,-28.5700239908607 22.3311063409985,-28.5700239908607 2.33110634099847,-8.57002399086066 -2.66889365900153,6.42997600913934 12.3311063409985,-18.5700239908607 27.3311063409985),(-8.57002399086066 12.3311063409985,-18.5700239908607 7.33110634099847,-18.5700239908607 17.3311063409985,-8.57002399086066 12.3311063409985)))'); 10 | /* region */ 11 | select h3_polyfill_estimate(geom, 1) 12 | from test_geometries where name = 'polygon with hole'; 13 | h3_polyfill_estimate 14 | ---------------------- 15 | 61 16 | (1 row) 17 | 18 | select array_length(array_agg(i), 1) 19 | from ( select h3_polyfill(geom, 1) i 20 | from test_geometries 21 | where name = 'polygon with hole' ) f; 22 | array_length 23 | -------------- 24 | 15 25 | (1 row) 26 | 27 | select i 28 | from ( 29 | select h3_polyfill(geom, 1) i 30 | from test_geometries where name = 'polygon with hole' 31 | ) f 32 | order by i; 33 | i 34 | ----------------- 35 | 81623ffffffffff 36 | 8162bffffffffff 37 | 816afffffffffff 38 | 817a3ffffffffff 39 | 817a7ffffffffff 40 | 817abffffffffff 41 | 817afffffffffff 42 | 817b3ffffffffff 43 | 817bbffffffffff 44 | 81963ffffffffff 45 | 8196bffffffffff 46 | 8197bffffffffff 47 | 81a23ffffffffff 48 | 81a33ffffffffff 49 | 81a37ffffffffff 50 | (15 rows) 51 | 52 | select i 53 | from ( 54 | select h3_polyfill(geom, 1) i 55 | from test_geometries where name = 'multipolygon with hole' 56 | ) f 57 | order by i; 58 | i 59 | ----------------- 60 | 8139bffffffffff 61 | 81543ffffffffff 62 | 81547ffffffffff 63 | 8154bffffffffff 64 | 81557ffffffffff 65 | 8155bffffffffff 66 | 81567ffffffffff 67 | 81577ffffffffff 68 | 8159bffffffffff 69 | 81743ffffffffff 70 | 8174bffffffffff 71 | 81753ffffffffff 72 | 8175bffffffffff 73 | 817c7ffffffffff 74 | 817cbffffffffff 75 | 817cfffffffffff 76 | (16 rows) 77 | 78 | -------------------------------------------------------------------------------- /pgh3.control: -------------------------------------------------------------------------------- 1 | comment = 'Postgresql H3 bindings' 2 | default_version = '0.3.0' 3 | relocatable = true 4 | -------------------------------------------------------------------------------- /sql/compact_test.sql: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | create extension if not exists pgh3; 3 | 4 | /* 5 | Indexes are taken from the testCompact.c "sunnyvale" unittests 6 | 7 | generated with: 8 | 9 | echo '89283470c27ffff' | kRing 9 | sed -E "s/^(.*)$/'\1',/g" | sed 'N;s/\n/ /;' | sed 'N;s/\n/ /;' 10 | */ 11 | create table sunnyvale (h3index text); 12 | insert into sunnyvale (h3index) values 13 | ('89283470c27ffff'), ('89283470c23ffff'), ('89283470c2fffff'), ('89283470d5bffff'), ('89283470d53ffff'), ('89283470dcbffff'), ('89283470c37ffff'), ('89283470c33ffff'), 14 | ('89283470c3bffff'), ('89283470c2bffff'), ('89283470893ffff'), ('89283470897ffff'), ('89283470d4bffff'), ('89283470d43ffff'), ('89283470d57ffff'), ('89283470dcfffff'), 15 | ('89283470dc3ffff'), ('89283470ddbffff'), ('89283470cafffff'), ('89283470cabffff'), ('89283470c07ffff'), ('89283470c0fffff'), ('89283470c77ffff'), ('89283470c67ffff'), 16 | ('8928347089bffff'), ('89283470883ffff'), ('89283470887ffff'), ('892834708b3ffff'), ('89283470d4fffff'), ('89283470d47ffff'), ('89283470d0bffff'), ('89283470d1bffff'), 17 | ('89283470dc7ffff'), ('89283470dd7ffff'), ('89283470dd3ffff'), ('89283470ca7ffff'), ('89283470ca3ffff'), ('89283470cbbffff'), ('89283470c17ffff'), ('89283470c03ffff'), 18 | ('89283470c0bffff'), ('89283470c73ffff'), ('89283470c63ffff'), ('89283470c6fffff'), ('892834708d7ffff'), ('8928347088bffff'), ('8928347088fffff'), ('892834708bbffff'), 19 | ('892834708a3ffff'), ('892834708b7ffff'), ('89283470d7bffff'), ('89283470d73ffff'), ('89283470d0fffff'), ('89283470d03ffff'), ('89283470d13ffff'), ('89283470d8bffff'), 20 | ('89283470d9bffff'), ('89283472b6fffff'), ('89283472b6bffff'), ('89283470cb7ffff'), ('89283470cb3ffff'), ('89283470c87ffff'), ('89283470c8fffff'), ('89283470c13ffff'), 21 | ('89283470c1bffff'), ('89283470c57ffff'), ('89283470c47ffff'), ('89283470c7bffff'), ('89283470c6bffff'), ('892834708d3ffff'), ('892834708c3ffff'), ('892834708c7ffff'), 22 | ('89283470813ffff'), ('89283470817ffff'), ('892834708abffff'), ('892834708afffff'), ('892834708a7ffff'), ('89283470d6bffff'), ('89283470d63ffff'), ('89283470d77ffff'), 23 | ('89283470d3bffff'), ('89283470d07ffff'), ('89283470d17ffff'), ('89283470d8fffff'), ('89283470d83ffff'), ('89283470d93ffff'), ('89283472b67ffff'), ('89283472b63ffff'), 24 | ('89283472b7bffff'), ('89283472b4fffff'), ('89283472b4bffff'), ('89283470c97ffff'), ('89283470c83ffff'), ('89283470c8bffff'), ('89283470cc7ffff'), ('89283470ccfffff'), 25 | ('89283470c53ffff'), ('89283470c43ffff'), ('89283470c4fffff'), ('89283470eb7ffff'), ('89283470ea7ffff'), ('892834708dbffff'), ('892834708cbffff'), ('892834708cfffff'), 26 | ('8928347081bffff'), ('89283470803ffff'), ('89283470807ffff'), ('89283470833ffff'), ('89283470837ffff'), ('892834709dbffff'), ('892834709d3ffff'), ('89283470d6fffff'), 27 | ('89283470d67ffff'), ('89283470d2bffff'), ('89283470d23ffff'), ('89283470d33ffff'), ('89283470dabffff'), ('89283470dbbffff'), ('89283470d87ffff'), ('89283470d97ffff'), 28 | ('89283472b2fffff'), ('89283472b2bffff'), ('89283472b77ffff'), ('89283472b73ffff'), ('89283472b47ffff'), ('89283472b43ffff'), ('89283472b5bffff'), ('89283472a2fffff'), 29 | ('89283470c93ffff'), ('89283470c9bffff'), ('89283470cd7ffff'), ('89283470cc3ffff'), ('89283470ccbffff'), ('89283470127ffff'), ('89283470c5bffff'), ('89283470c4bffff'), 30 | ('89283470eb3ffff'), ('89283470ea3ffff'), ('89283470eafffff'), ('89283470e37ffff'), ('89283470e27ffff'), ('89283470853ffff'), ('89283470857ffff'), ('8928347080bffff'), 31 | ('8928347080fffff'), ('8928347083bffff'), ('89283470823ffff'), ('89283470827ffff'), ('892834709cbffff'), ('892834709c3ffff'), ('892834709d7ffff'), ('8928347099bffff'), 32 | ('89283470993ffff'), ('89283470d2fffff'), ('89283470d27ffff'), ('89283470d37ffff'), ('89283470dafffff'), ('89283470da3ffff'), ('89283470db3ffff'), ('8928347764bffff'), 33 | ('8928347765bffff'), ('89283472b27ffff'), ('89283472b23ffff'), ('89283472b3bffff'), ('89283472b0fffff'), ('89283472b0bffff'), ('89283472b57ffff'), ('89283472b53ffff'), 34 | ('89283472a27ffff'), ('89283472a23ffff'), ('89283472a2bffff'), ('89283472a67ffff'), ('89283472a6fffff'), ('89283470cd3ffff'), ('89283470cdbffff'), ('89283470137ffff'), 35 | ('89283470123ffff'), ('8928347012fffff'), ('89283470e97ffff'), ('89283470e87ffff'), ('89283470ebbffff'), ('89283470eabffff'), ('89283470e33ffff'), ('89283470e23ffff'), 36 | ('89283470e2fffff'), ('8928347085bffff'), ('89283470843ffff'), ('89283470847ffff'), ('89283470873ffff'), ('89283470877ffff'), ('8928347082bffff'), ('8928347082fffff'), 37 | ('8928347095bffff'), ('89283470953ffff'), ('892834709cfffff'), ('892834709c7ffff'), ('8928347098bffff'), ('89283470983ffff'), ('89283470997ffff'), ('8928347725bffff'), 38 | ('89283477253ffff'), ('892834772cbffff'), ('892834772dbffff'), ('89283470da7ffff'), ('89283470db7ffff'), ('8928347764fffff'), ('89283477643ffff'), ('89283477653ffff'), 39 | ('892834776cbffff'), ('89283472b37ffff'), ('89283472b33ffff'), ('89283472b07ffff'), ('89283472b03ffff'), ('89283472b1bffff'), ('89283472bcfffff'), ('89283472bcbffff'), 40 | ('89283472a37ffff'), ('89283472a33ffff'), ('89283472a3bffff'), ('89283472a77ffff'), ('89283472a63ffff'), ('89283472a6bffff'), ('892834701a7ffff'), ('892834701afffff'), 41 | ('89283470133ffff'), ('8928347013bffff'), ('8928347012bffff'), ('89283470e93ffff'), ('89283470e83ffff'), ('89283470e8fffff'), ('89283470e17ffff'), ('89283470e07ffff'), 42 | ('89283470e3bffff'), ('89283470e2bffff'), ('89283470a93ffff'), ('89283470a97ffff'), ('8928347084bffff'), ('8928347084fffff'), ('8928347087bffff'), ('89283470863ffff'), 43 | ('89283470867ffff'), ('89283470b93ffff'), ('89283470b97ffff'), ('8928347094bffff'), ('89283470943ffff'), ('89283470957ffff'), ('8928347091bffff'), ('89283470913ffff'), 44 | ('8928347098fffff'), ('89283470987ffff'), ('8928347724bffff'), ('89283477243ffff'), ('89283477257ffff'), ('892834772cfffff'), ('892834772c3ffff'), ('892834772d3ffff'), 45 | ('8928347766bffff'), ('8928347767bffff'), ('89283477647ffff'), ('89283477657ffff'), ('892834776cfffff'), ('892834776c3ffff'), ('892834776dbffff'), ('89283472bafffff'), 46 | ('89283472babffff'), ('89283472b17ffff'), ('89283472b13ffff'), ('89283472bc7ffff'), ('89283472bc3ffff'), ('89283472bdbffff'), ('89283472aafffff'); 47 | 48 | 49 | select count(*) from h3_compact(array(select h3index from sunnyvale)); 50 | 51 | select h3_compact(array((select h3_to_children('89283470c27ffff', 10)))); 52 | 53 | select h3_uncompact(array['89283470c27ffff'], 10) except select h3_to_children('89283470c27ffff', 10); 54 | 55 | -- test a full round trip (1/2). should return 0 rows. 56 | select h3index from sunnyvale 57 | except 58 | select h3_uncompact(array[h3_compact(array(select h3index from sunnyvale))], 9); 59 | 60 | -- test a full round trip (2/2). should return 0 rows. 61 | select h3_uncompact(array[h3_compact(array(select h3index from sunnyvale))], 9) 62 | except 63 | select h3index from sunnyvale; 64 | -------------------------------------------------------------------------------- /sql/hierarchy_test.sql: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | create extension if not exists pgh3; 3 | 4 | 5 | /* hierarchy */ 6 | 7 | select h3_to_parent('85639c63fffffff', 4); 8 | 9 | select h3_to_parent('85639c63fffffff', 1); 10 | 11 | select h3_to_children('82639ffffffffff', 3); 12 | -------------------------------------------------------------------------------- /sql/index_test.sql: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | create extension if not exists pgh3; 3 | 4 | 5 | /* indexing */ 6 | 7 | select trunc(p[0]::numeric, 5) x, trunc(p[1]::numeric, 5) y 8 | from ( 9 | select h3_h3index_to_geo('85639c63fffffff')::point p 10 | ) f; 11 | 12 | select trunc(st_x(p)::numeric, 5) x, trunc(st_y(p)::numeric, 5) y 13 | from ( 14 | select h3_h3index_to_geo('85639c63fffffff')::geometry p 15 | ) f; 16 | 17 | select h3_geo_to_h3index('(9.40691761982618,52.1233617183044)'::point, 5); 18 | 19 | select h3_h3index_is_valid('85639c63fffffff'); -- is valid 20 | 21 | select h3_h3index_is_valid('85639cdddddddd'); -- is invalid 22 | 23 | select h3_get_resolution('85639c63fffffff'); -- = 5 24 | 25 | select h3_get_basecell('85639c63fffffff'); -- = 5 26 | -------------------------------------------------------------------------------- /sql/misc_test.sql: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | create extension if not exists pgh3; 3 | 4 | /******* misc functions *********************************/ 5 | 6 | select h3_hexagon_area_km2(4); 7 | 8 | select h3_hexagon_area_m2(4); 9 | 10 | select h3_edge_length_km(4); 11 | 12 | select h3_edge_length_m(4); 13 | -------------------------------------------------------------------------------- /sql/pgh3.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | CREATE FUNCTION h3_ext_version() RETURNS text 4 | AS 'pgh3', 'h3_ext_version' 5 | IMMUTABLE LANGUAGE C STRICT; 6 | comment on function h3_ext_version() is 'Returns the version number of the H3 extension. This is not the version number of the h3 library itself.'; 7 | 8 | 9 | /******* Indexing functions *********************************/ 10 | 11 | CREATE FUNCTION h3_geo_to_h3index(p point, resolution integer) RETURNS text 12 | AS 'pgh3', 'h3_geo_to_h3index' 13 | IMMUTABLE LANGUAGE C STRICT; 14 | comment on function h3_geo_to_h3index(p point, integer) is 'Get the H3 index for the point at the given resolution.'; 15 | 16 | /* 17 | comment on function byteamagic_mime(bytea) is 18 | 'return the mimetype of the bytea data as text'; 19 | 20 | */ 21 | 22 | create function h3_geo_to_h3index(g geometry, resolution integer) returns text as $$ 23 | begin 24 | if st_geometrytype(g) != 'ST_Point' then 25 | raise exception 'h3 only supports point geometries'; 26 | end if; 27 | return h3_geo_to_h3index(g::point, resolution); 28 | end; 29 | $$ language plpgsql immutable strict; 30 | comment on function h3_geo_to_h3index(g geometry, integer) is 'Get the H3 index for the PostGIS point geometry at the given resolution.'; 31 | 32 | 33 | create function _h3_h3index_to_geo(h3index text) returns point 34 | as 'pgh3', '_h3_h3index_to_geo' 35 | IMMUTABLE LANGUAGE C STRICT ; 36 | comment on function _h3_h3index_to_geo(h3index text) is 'Convert a H3 index to coordinates. Returned as a postgresql point type.'; 37 | 38 | create function h3_h3index_to_geo(h3index text) returns geometry 39 | as $$ 40 | declare 41 | g geometry; 42 | begin 43 | select st_makepoint(p[0], p[1]) into g 44 | from ( 45 | select _h3_h3index_to_geo(h3index) p 46 | ) foo; 47 | 48 | return g; 49 | end; 50 | $$ language plpgsql immutable strict; 51 | comment on function h3_h3index_to_geo(h3index text) is 'Convert a H3 index to coordinates. Returned as a PostGIS point geometry.'; 52 | 53 | 54 | 55 | create function _h3_h3index_to_geoboundary(h3index text) returns polygon 56 | as 'pgh3', '_h3_h3index_to_geoboundary' 57 | IMMUTABLE LANGUAGE C STRICT ; 58 | comment on function _h3_h3index_to_geoboundary(h3index text) is 'Convert the boundary of H3 index to polygon coordinates. Returned as a postgresql native polygon type.'; 59 | 60 | create function h3_h3index_to_geoboundary(h3index text) returns geometry 61 | as $$ 62 | declare 63 | g geometry; 64 | point_geoms geometry[]; 65 | begin 66 | select _h3_h3index_to_geoboundary(h3index)::geometry into g; 67 | 68 | return g; 69 | end; 70 | $$ language plpgsql immutable strict; 71 | comment on function h3_h3index_to_geoboundary(h3index text) is 'Convert the boundary of H3 index to polygon coordinates. Returned as a PostGIS polygon geometry.'; 72 | 73 | 74 | create function h3_h3index_is_valid(h3index text) returns boolean 75 | as 'pgh3', 'h3_h3index_is_valid' 76 | IMMUTABLE LANGUAGE C STRICT ; 77 | comment on function h3_h3index_is_valid(h3index text) is 'Check if a H3 index is valid.'; 78 | 79 | create function h3_get_resolution(h3index text) returns integer 80 | as 'pgh3', 'h3_get_resolution' 81 | IMMUTABLE LANGUAGE C STRICT ; 82 | comment on function h3_get_resolution(h3index text) is 'Get the resolution for a H3 index.'; 83 | 84 | create function h3_get_basecell(h3index text) returns integer 85 | as 'pgh3', 'h3_get_basecell' 86 | IMMUTABLE LANGUAGE C STRICT ; 87 | comment on function h3_get_basecell(h3index text) is 'Get the base cell for a H3 index.'; 88 | 89 | -- this syntax requires postgresql >= 9. To support earlier versions a 90 | -- temporary function instead of the block would be needed 91 | do $$ 92 | begin 93 | create function h3_get_basecells() returns setof text 94 | as 'pgh3', 'h3_get_basecells' 95 | immutable language c strict ; 96 | comment on function h3_get_basecells() is 'Returns all base cells.'; 97 | exception when undefined_function then 98 | -- ignore. pgh3 is compiled without this function. 99 | raise notice 'h3_get_basecells is not supported'; 100 | end $$; 101 | 102 | 103 | /******* hierarchy functions *********************************/ 104 | 105 | 106 | create function h3_to_parent(h3index text, resolution integer) returns text 107 | as 'pgh3', 'h3_to_parent' 108 | IMMUTABLE LANGUAGE C STRICT ; 109 | comment on function h3_to_parent(h3index text, resolution integer) is 'Returns the parent (coarser) index containing the given index.'; 110 | 111 | create function h3_to_children(h3index text, resolution integer) returns setof text 112 | as 'pgh3', 'h3_to_children' 113 | immutable language c strict ; 114 | comment on function h3_to_children(h3index text, resolution integer) is 'Returns the children (finer) indexes contained the given index.'; 115 | 116 | /******* neighbor functions *********************************/ 117 | 118 | create function h3_kring(h3index text, distance integer) returns setof text 119 | as 'pgh3', 'h3_kring' 120 | IMMUTABLE LANGUAGE C STRICT ; 121 | comment on function h3_kring(h3index text, distance integer) is 'Returns the neighbor indices within the given distance.'; 122 | 123 | /******* misc functions *********************************/ 124 | 125 | create function h3_hexagon_area_km2(resolution integer) returns double precision 126 | as 'pgh3', 'h3_hexagon_area_km2' 127 | IMMUTABLE LANGUAGE C STRICT ; 128 | comment on function h3_hexagon_area_km2(resolution integer) is 'Average hexagon area in square kilometers at the given resolution.'; 129 | 130 | create function h3_hexagon_area_m2(resolution integer) returns double precision 131 | as 'pgh3', 'h3_hexagon_area_m2' 132 | IMMUTABLE LANGUAGE C STRICT ; 133 | comment on function h3_hexagon_area_m2(resolution integer) is 'Average hexagon area in square meters at the given resolution.'; 134 | 135 | create function h3_edge_length_km(resolution integer) returns double precision 136 | as 'pgh3', 'h3_edge_length_km' 137 | IMMUTABLE LANGUAGE C STRICT ; 138 | comment on function h3_edge_length_km(resolution integer) is 'Average hexagon edge length in kilometers at the given resolution.'; 139 | 140 | create function h3_edge_length_m(resolution integer) returns double precision 141 | as 'pgh3', 'h3_edge_length_m' 142 | IMMUTABLE LANGUAGE C STRICT ; 143 | comment on function h3_edge_length_m(resolution integer) is 'Average hexagon edge length in meters at the given resolution.'; 144 | 145 | /******* region functions *********************************/ 146 | 147 | CREATE FUNCTION _h3_polyfill_polygon_c(exterior_ring polygon, interior_rings polygon[], 148 | resolution integer) RETURNS SETOF text 149 | AS 'pgh3', '_h3_polyfill_polygon' 150 | IMMUTABLE LANGUAGE C; 151 | comment on function _h3_polyfill_polygon_c(exterior_ring polygon, interior_rings polygon[], resolution integer) is 152 | 'Fills the given exterior ring with hexagons at the given resolution. The interior_ring polygons are understood as holes and will be omitted.'; 153 | 154 | 155 | create function h3_polyfill(geom geometry, resolution integer) returns setof text as $$ 156 | begin 157 | 158 | return query select _h3_polyfill_polygon_c(exterior_ring, interior_rings, resolution) 159 | from ( 160 | select 161 | st_makepolygon(st_exteriorring(g))::polygon exterior_ring, 162 | (select array_agg(st_makepolygon(st_interiorringn(g, i))::polygon) rings 163 | from ( 164 | select generate_series(0, st_numinteriorrings(g)) i 165 | ) gs 166 | where gs.i > 0 -- index starts with 1 167 | ) interior_rings 168 | from ( 169 | select geom g 170 | where st_geometrytype(geom) = 'ST_Polygon' 171 | union select g 172 | from ( 173 | select (st_dump(geom)).geom as g 174 | where st_geometrytype(geom) = 'ST_MultiPolygon' 175 | ) d 176 | ) polys 177 | group by g 178 | ) pg_polys; 179 | 180 | return; 181 | end; 182 | $$ language plpgsql immutable strict; 183 | comment on function h3_polyfill(polygong geometry, resolution integer) is 184 | 'Fills the given PostGIS polygon or multipolygon with hexagons at the given resolution. Holes in the polygon will be omitted. 185 | 186 | The H3 `polyfill` function requires a preallocation of the memory for the generates indexes. Depending on the size of the 187 | given polygon, its shape and resolution this may exhaust the memory given to this extension. In this case 188 | this function will be terminated by the database server and a corresponding notice will be given. 189 | 190 | The memory limit can be increased using the `pgh3.polyfill_mem` configuration parameter in the `postgresql.conf` file. The 191 | default value for this setting 1024MB (PostgreSQL internal `MaxAllocSize`). Syntax for the setting is 192 | 193 | pgh3.polyfill_mem = 1024MB 194 | 195 | For values larger than `MaxAllocSize`, the PostgreSQL `MemoryContextAllocHuge` allocator will be used. 196 | 197 | _This setting is only available when using a PostgreSQL version >= 10_. On earlier versions the memory limit is set to 1024MB. 198 | 199 | If this does not resolve the issue, there is essentially one way to work around this issue: Cut the polygon into segments and run this function to each of them seperately. The PostGIS functions `ST_Subdivide`, `ST_Split` and `ST_Segmentize` may be helpful. 200 | '; 201 | 202 | 203 | CREATE FUNCTION _h3_polyfill_polygon_estimate_c(exterior_ring polygon, interior_rings polygon[], 204 | resolution integer) RETURNS integer 205 | AS 'pgh3', '_h3_polyfill_polygon_estimate' 206 | IMMUTABLE LANGUAGE C; 207 | comment on function _h3_polyfill_polygon_estimate_c(exterior_ring polygon, interior_rings polygon[], resolution integer) is 208 | 'Estimate the number of indexes required to fill the given exterior ring with hexagons at the given resolution. The interior_ring polygons are understood as holes and will be omitted.'; 209 | 210 | 211 | create function h3_polyfill_estimate(geom geometry, resolution integer) returns integer as $$ 212 | declare 213 | result integer; 214 | begin 215 | 216 | select sum(_h3_polyfill_polygon_estimate_c(exterior_ring, interior_rings, resolution)) into result 217 | from ( 218 | select 219 | st_makepolygon(st_exteriorring(g))::polygon exterior_ring, 220 | (select array_agg(st_makepolygon(st_interiorringn(g, i))::polygon) rings 221 | from ( 222 | select generate_series(0, st_numinteriorrings(g)) i 223 | ) gs 224 | where gs.i > 0 -- index starts with 1 225 | ) interior_rings 226 | from ( 227 | select geom g 228 | where st_geometrytype(geom) = 'ST_Polygon' 229 | union select g 230 | from ( 231 | select (st_dump(geom)).geom as g 232 | where st_geometrytype(geom) = 'ST_MultiPolygon' 233 | ) d 234 | ) polys 235 | group by g 236 | ) pg_polys; 237 | 238 | return result; 239 | end; 240 | $$ language plpgsql immutable strict; 241 | comment on function h3_polyfill_estimate(polygong geometry, resolution integer) is 242 | 'Estimate the number of indexes required to fill the given PostGIS polygon or multipolygon with hexagons at the given resolution. Holes in the polygon will be omitted.'; 243 | 244 | 245 | /******* compacting functions *********************************/ 246 | 247 | CREATE FUNCTION h3_compact(h3indexes text[]) RETURNS SETOF text 248 | AS 'pgh3', 'h3_compact' 249 | IMMUTABLE LANGUAGE C; 250 | comment on function h3_compact(h3indexes text[]) is 251 | 'Compacts the array of given H3 indexes as best as possible'; 252 | 253 | 254 | CREATE FUNCTION h3_uncompact(h3indexes text[], resolution integer) RETURNS SETOF text 255 | AS 'pgh3', 'h3_uncompact' 256 | IMMUTABLE LANGUAGE C; 257 | comment on function h3_uncompact(h3indexes text[], resolution integer) is 258 | 'Uncompacts the array of given H3 indexes'; 259 | 260 | -------------------------------------------------------------------------------- /sql/region_test.sql: -------------------------------------------------------------------------------- 1 | create extension if not exists postgis; 2 | create extension if not exists pgh3; 3 | 4 | 5 | /* test data */ 6 | 7 | create table test_geometries(name text primary key, geom geometry); 8 | insert into test_geometries (name, geom) values 9 | ('polygon with hole', 'POLYGON((51.0708433295253 -23.5514097581317,61.0708433295253 11.4485902418683,31.0708433295253 6.44859024186827,26.0708433295253 -13.5514097581317,51.0708433295253 -23.5514097581317),(36.0708433295253 -3.55140975813173,51.0708433295253 1.44859024186827,46.0708433295253 -13.5514097581317,36.0708433295253 -3.55140975813173))'::geometry), 10 | ('multipolygon with hole', 'MULTIPOLYGON(((1.42997600913934 32.3311063409985,-18.5700239908607 37.3311063409985,6.42997600913934 22.3311063409985,1.42997600913934 32.3311063409985)),((-18.5700239908607 27.3311063409985,-28.5700239908607 22.3311063409985,-28.5700239908607 2.33110634099847,-8.57002399086066 -2.66889365900153,6.42997600913934 12.3311063409985,-18.5700239908607 27.3311063409985),(-8.57002399086066 12.3311063409985,-18.5700239908607 7.33110634099847,-18.5700239908607 17.3311063409985,-8.57002399086066 12.3311063409985)))'); 11 | 12 | 13 | /* region */ 14 | 15 | select h3_polyfill_estimate(geom, 1) 16 | from test_geometries where name = 'polygon with hole'; 17 | 18 | select array_length(array_agg(i), 1) 19 | from ( select h3_polyfill(geom, 1) i 20 | from test_geometries 21 | where name = 'polygon with hole' ) f; 22 | 23 | select i 24 | from ( 25 | select h3_polyfill(geom, 1) i 26 | from test_geometries where name = 'polygon with hole' 27 | ) f 28 | order by i; 29 | 30 | select i 31 | from ( 32 | select h3_polyfill(geom, 1) i 33 | from test_geometries where name = 'multipolygon with hole' 34 | ) f 35 | order by i; 36 | -------------------------------------------------------------------------------- /src/compact.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Deutsches Zentrum für Luft- und Raumfahrt e.V. 3 | * (German Aerospace Center), German Remote Sensing Data Center 4 | * Department: Geo-Risks and Civil Security 5 | * 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | 21 | #include "util.h" 22 | 23 | #include "postgres.h" 24 | #include "catalog/pg_type.h" 25 | #include "utils/builtins.h" 26 | #include "fmgr.h" 27 | #include "utils/array.h" 28 | #include "utils/lsyscache.h" 29 | #include "funcapi.h" 30 | 31 | #include