├── .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 | ![](doc/hexagons-example.png) 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 | [![UKIS](doc/ukis-logo.png)](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

32 | 33 | /** 34 | * returns an allocated array of H3Indexes 35 | */ 36 | static H3Index * 37 | pg_array_to_h3index_array(ArrayType *indexarray, int * num_indexes) 38 | { 39 | *num_indexes = 0; 40 | if (indexarray != NULL) { 41 | int ndim = ARR_NDIM(indexarray); 42 | 43 | if (ndim != 1) { 44 | fail_and_report("The array if h3indexes must be an 1-dimensional array"); 45 | } 46 | 47 | Oid elemtype = ARR_ELEMTYPE(indexarray); 48 | if (elemtype != TEXTOID) { 49 | fail_and_report("The type of the h3index array must be text"); 50 | } 51 | 52 | int16 typlen; 53 | bool typbyval; 54 | char typalign; 55 | get_typlenbyvalalign(elemtype, &typlen, &typbyval, &typalign); 56 | 57 | int *dim = ARR_DIMS(indexarray); 58 | H3Index * h3indexes = palloc0(dim[0] * sizeof(H3Index)); 59 | 60 | bool isnull; 61 | Datum indexarray_datum = PointerGetDatum(indexarray); 62 | for (int i = 1; i <= dim[0]; i++) { 63 | 64 | Datum index_datum = array_get_element(indexarray_datum, ndim, &i, 65 | -1, typlen, typbyval, typalign, &isnull); 66 | if (isnull) { 67 | fail_and_report("h3 index at array position %d is null", i); 68 | } 69 | 70 | text *index_text = DatumGetTextP(index_datum); 71 | char * index_cstr = text_to_cstring(index_text); 72 | 73 | if (__h3_index_from_cstring(index_cstr, &(h3indexes[i-1])) == false) { 74 | pfree(h3indexes); 75 | fail_and_report("could not parse the h3 index at array position %d", i); 76 | return NULL; 77 | } 78 | } 79 | 80 | (*num_indexes) = dim[0]; 81 | 82 | return h3indexes; 83 | } 84 | 85 | return NULL; 86 | } 87 | 88 | 89 | PG_FUNCTION_INFO_V1(h3_compact); 90 | 91 | Datum 92 | h3_compact(PG_FUNCTION_ARGS) 93 | { 94 | FuncCallContext *funcctx; 95 | int call_cntr = 0; 96 | int max_calls = 0; 97 | MemoryContext oldcontext; 98 | H3Index *compacted_indexes = NULL; 99 | 100 | if (SRF_IS_FIRSTCALL()) { 101 | // early exit when no idexes are given 102 | if (PG_ARGISNULL(0)) { 103 | PG_RETURN_NULL(); 104 | } 105 | 106 | funcctx = SRF_FIRSTCALL_INIT(); 107 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); 108 | 109 | ArrayType * uncompacted_indexes_array = PG_GETARG_ARRAYTYPE_P(0); 110 | 111 | // convert the indexes to their native format 112 | int num_uncompacted_indexes = 0; 113 | H3Index * uncompacted_indexes = pg_array_to_h3index_array(uncompacted_indexes_array, &num_uncompacted_indexes); 114 | 115 | if (num_uncompacted_indexes == 0) { 116 | PG_RETURN_NULL(); // early exit - nothing to do 117 | } 118 | 119 | // allocate memory for the results 120 | compacted_indexes = __h3_polyfill_palloc0(num_uncompacted_indexes * sizeof(H3Index)); 121 | 122 | if (H3_EXPORT(compact)(uncompacted_indexes, compacted_indexes, num_uncompacted_indexes) != 0) { 123 | pfree(uncompacted_indexes); 124 | pfree(compacted_indexes); 125 | fail_and_report("Error while compacting the h3 indexes"); 126 | } 127 | pfree(uncompacted_indexes); 128 | 129 | for (int i = 0; i < num_uncompacted_indexes; i++) { 130 | if (compacted_indexes[i] != 0) { 131 | if (i > max_calls) { 132 | // fill NULL "holes" in list of hexagons with the hexagons located 133 | // after the NULL value. This allows us to use call_cntr and max_calls 134 | // for iteration during SRF calls. 135 | compacted_indexes[max_calls] = compacted_indexes[i]; 136 | compacted_indexes[i] = 0; 137 | } 138 | max_calls++; 139 | } 140 | } 141 | 142 | report_debug1("Compacted %d H3 hexagons to %d", 143 | num_uncompacted_indexes, max_calls); 144 | 145 | if (max_calls > 0) { 146 | // keep track of the results 147 | funcctx->max_calls = max_calls; 148 | funcctx->user_fctx = compacted_indexes; 149 | } 150 | else { 151 | // fast track when no results 152 | pfree(compacted_indexes); 153 | compacted_indexes = NULL; 154 | 155 | MemoryContextSwitchTo(oldcontext); 156 | SRF_RETURN_DONE(funcctx); 157 | } 158 | MemoryContextSwitchTo(oldcontext); 159 | } 160 | 161 | // stuff done on every call of the function 162 | funcctx = SRF_PERCALL_SETUP(); 163 | 164 | // Initialize per-call variables 165 | call_cntr = funcctx->call_cntr; 166 | max_calls = funcctx->max_calls; 167 | compacted_indexes = funcctx->user_fctx; 168 | 169 | if (call_cntr < max_calls) { 170 | text * index_text = __h3_index_to_text(compacted_indexes[call_cntr]); 171 | SRF_RETURN_NEXT(funcctx, PointerGetDatum(index_text)); 172 | } 173 | else { 174 | pfree(compacted_indexes); 175 | compacted_indexes = NULL; 176 | 177 | SRF_RETURN_DONE(funcctx); 178 | } 179 | 180 | } 181 | 182 | 183 | PG_FUNCTION_INFO_V1(h3_uncompact); 184 | 185 | Datum 186 | h3_uncompact(PG_FUNCTION_ARGS) 187 | { 188 | FuncCallContext *funcctx; 189 | int call_cntr = 0; 190 | int max_calls = 0; 191 | MemoryContext oldcontext; 192 | H3Index *uncompacted_indexes = NULL; 193 | 194 | if (SRF_IS_FIRSTCALL()) { 195 | // early exit when no idexes are given 196 | if (PG_ARGISNULL(0)) { 197 | PG_RETURN_NULL(); 198 | } 199 | int resolution = PG_GETARG_INT32(1); 200 | 201 | funcctx = SRF_FIRSTCALL_INIT(); 202 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); 203 | 204 | ArrayType * compacted_indexes_array = PG_GETARG_ARRAYTYPE_P(0); 205 | 206 | // convert the indexes to their native format 207 | int num_compacted_indexes = 0; 208 | H3Index * compacted_indexes = pg_array_to_h3index_array(compacted_indexes_array, &num_compacted_indexes); 209 | 210 | if (num_compacted_indexes == 0) { 211 | PG_RETURN_NULL(); // early exit - nothing to do 212 | } 213 | 214 | // estimate the number of uncompacted indexes 215 | int num_estimated_uncompacted_indexes = H3_EXPORT(maxUncompactSize)(compacted_indexes, 216 | num_compacted_indexes, resolution); 217 | if (num_estimated_uncompacted_indexes < 0) { 218 | pfree(compacted_indexes); 219 | fail_and_report("Error while estimating the number of uncompacted indexes" 220 | " for %d compacted indexes and the target resolution %d", num_compacted_indexes, resolution); 221 | } 222 | 223 | // allocate memory for the results 224 | uncompacted_indexes = __h3_polyfill_palloc0(num_estimated_uncompacted_indexes * sizeof(H3Index)); 225 | 226 | if (H3_EXPORT(uncompact)(compacted_indexes, num_compacted_indexes, 227 | uncompacted_indexes, num_estimated_uncompacted_indexes, resolution) != 0) { 228 | pfree(uncompacted_indexes); 229 | pfree(compacted_indexes); 230 | fail_and_report("Error while uncompacting the h3 indexes"); 231 | } 232 | pfree(compacted_indexes); 233 | 234 | for (int i = 0; i < num_estimated_uncompacted_indexes; i++) { 235 | if (uncompacted_indexes[i] != 0) { 236 | if (i > max_calls) { 237 | // fill NULL "holes" in list of hexagons with the hexagons located 238 | // after the NULL value. This allows us to use call_cntr and max_calls 239 | // for iteration during SRF calls. 240 | uncompacted_indexes[max_calls] = uncompacted_indexes[i]; 241 | uncompacted_indexes[i] = 0; 242 | } 243 | max_calls++; 244 | } 245 | } 246 | 247 | report_debug1("Uncompacted %d H3 hexagons to %d", 248 | num_compacted_indexes, max_calls); 249 | 250 | if (max_calls > 0) { 251 | // keep track of the results 252 | funcctx->max_calls = max_calls; 253 | funcctx->user_fctx = uncompacted_indexes; 254 | } 255 | else { 256 | // fast track when no results 257 | pfree(uncompacted_indexes); 258 | uncompacted_indexes = NULL; 259 | 260 | MemoryContextSwitchTo(oldcontext); 261 | SRF_RETURN_DONE(funcctx); 262 | } 263 | MemoryContextSwitchTo(oldcontext); 264 | } 265 | 266 | // stuff done on every call of the function 267 | funcctx = SRF_PERCALL_SETUP(); 268 | 269 | // Initialize per-call variables 270 | call_cntr = funcctx->call_cntr; 271 | max_calls = funcctx->max_calls; 272 | uncompacted_indexes = funcctx->user_fctx; 273 | 274 | if (call_cntr < max_calls) { 275 | text * index_text = __h3_index_to_text(uncompacted_indexes[call_cntr]); 276 | SRF_RETURN_NEXT(funcctx, PointerGetDatum(index_text)); 277 | } 278 | else { 279 | pfree(uncompacted_indexes); 280 | uncompacted_indexes = NULL; 281 | 282 | SRF_RETURN_DONE(funcctx); 283 | } 284 | 285 | } 286 | -------------------------------------------------------------------------------- /src/hierarchy.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/geo_decls.h" 29 | #include "funcapi.h" 30 | 31 | #include

32 | 33 | 34 | PG_FUNCTION_INFO_V1(h3_to_parent); 35 | 36 | /* 37 | * Return the parent (coarser) index containing the index in the parameter. 38 | * 39 | * The Index is returned in its string representation 40 | */ 41 | Datum 42 | h3_to_parent(PG_FUNCTION_ARGS) 43 | { 44 | text *index_text = PG_GETARG_TEXT_P(0); 45 | char * index_cstr = text_to_cstring(index_text); 46 | H3Index index; 47 | __h3_index_from_cstring(index_cstr, &index); 48 | 49 | int resolution = PG_GETARG_INT32(1); 50 | 51 | H3Index parent = H3_EXPORT(h3ToParent)(index, resolution); 52 | 53 | text * parent_text = __h3_index_to_text(parent); 54 | PG_RETURN_TEXT_P(parent_text); 55 | } 56 | 57 | 58 | PG_FUNCTION_INFO_V1(h3_to_children); 59 | 60 | /* 61 | * Return the child (finer) index contained the index in the given 62 | * resolution. 63 | * 64 | * The indexes are returned in thier string representations 65 | */ 66 | Datum 67 | h3_to_children(PG_FUNCTION_ARGS) 68 | { 69 | FuncCallContext *funcctx; 70 | int call_cntr = 0; 71 | int max_calls = 0; 72 | MemoryContext oldcontext; 73 | H3Index *hexagons = NULL; 74 | 75 | if (SRF_IS_FIRSTCALL()) { 76 | 77 | text *parent_index_text = PG_GETARG_TEXT_P(0); 78 | char * parent_index_cstr = text_to_cstring(parent_index_text); 79 | H3Index parent_index; 80 | __h3_index_from_cstring(parent_index_cstr, &parent_index); 81 | 82 | int child_resolution = PG_GETARG_INT32(1); 83 | 84 | funcctx = SRF_FIRSTCALL_INIT(); 85 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); 86 | 87 | int num_children = H3_EXPORT(maxH3ToChildrenSize)(parent_index, child_resolution); 88 | max_calls = num_children; 89 | 90 | report_debug1("Generating %d H3 child hexagons at resolution %d", 91 | num_children, child_resolution); 92 | 93 | hexagons = palloc0(num_children * sizeof(H3Index)); 94 | H3_EXPORT(h3ToChildren)(parent_index, child_resolution, hexagons); 95 | 96 | if (max_calls > 0) { 97 | // keep track of the results 98 | funcctx->max_calls = max_calls; 99 | funcctx->user_fctx = hexagons; 100 | } 101 | else { 102 | // fast track when no results 103 | pfree(hexagons); 104 | hexagons = NULL; 105 | 106 | MemoryContextSwitchTo(oldcontext); 107 | SRF_RETURN_DONE(funcctx); 108 | } 109 | MemoryContextSwitchTo(oldcontext); 110 | } 111 | 112 | // stuff done on every call of the function 113 | funcctx = SRF_PERCALL_SETUP(); 114 | 115 | // Initialize per-call variables 116 | call_cntr = funcctx->call_cntr; 117 | max_calls = funcctx->max_calls; 118 | hexagons = funcctx->user_fctx; 119 | 120 | if (call_cntr < max_calls) { 121 | text * index_text = __h3_index_to_text(hexagons[call_cntr]); 122 | SRF_RETURN_NEXT(funcctx, PointerGetDatum(index_text)); 123 | } 124 | else { 125 | pfree(hexagons); 126 | hexagons = NULL; 127 | 128 | SRF_RETURN_DONE(funcctx); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/index.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/geo_decls.h" 29 | #include "funcapi.h" 30 | 31 | #include

32 | 33 | 34 | PG_FUNCTION_INFO_V1(h3_geo_to_h3index); 35 | 36 | /* 37 | * Find the H3 index for a coordinate pair. 38 | * 39 | * The Index is returned in its string representation 40 | */ 41 | Datum 42 | h3_geo_to_h3index(PG_FUNCTION_ARGS) 43 | { 44 | Point *p = PG_GETARG_POINT_P(0); 45 | 46 | GeoCoord location; 47 | location.lat = degsToRads(p->y); 48 | location.lon = degsToRads(p->x); 49 | 50 | int resolution = PG_GETARG_INT32(1); 51 | 52 | H3Index index = H3_EXPORT(geoToH3)(&location, resolution); 53 | if (index == 0) { 54 | fail_and_report("Could not convert the coordinates (%f %f) to a H3 index", p->x, p->y); 55 | } 56 | 57 | text * index_text = __h3_index_to_text(index); 58 | PG_RETURN_TEXT_P(index_text); 59 | } 60 | 61 | 62 | 63 | PG_FUNCTION_INFO_V1(_h3_h3index_to_geo); 64 | 65 | /* 66 | * Return the centroid coordinates for the given h3 index. 67 | * 68 | * The index is expected in its string serialized representation. 69 | */ 70 | Datum 71 | _h3_h3index_to_geo(PG_FUNCTION_ARGS) 72 | { 73 | text *index_text = PG_GETARG_TEXT_P(0); 74 | 75 | char * index_cstr = text_to_cstring(index_text); 76 | H3Index index; 77 | __h3_index_from_cstring(index_cstr, &index); 78 | 79 | GeoCoord coord; 80 | H3_EXPORT(h3ToGeo)(index, &coord); 81 | 82 | // return as a postgresql native point 83 | Point *p = palloc0(sizeof(Point)); 84 | p->y = radsToDegs(coord.lat); 85 | p->x = radsToDegs(coord.lon); 86 | 87 | PG_RETURN_POINT_P(p); 88 | } 89 | 90 | 91 | 92 | PG_FUNCTION_INFO_V1(_h3_h3index_to_geoboundary); 93 | 94 | /* 95 | * Return the boundary coordinates for the given h3 index as a 96 | * native postgresql polygon 97 | * 98 | * The index is expected in its string serialized representation. 99 | */ 100 | Datum 101 | _h3_h3index_to_geoboundary(PG_FUNCTION_ARGS) 102 | { 103 | text *index_text = PG_GETARG_TEXT_P(0); 104 | 105 | char * index_cstr = text_to_cstring(index_text); 106 | H3Index index; 107 | __h3_index_from_cstring(index_cstr, &index); 108 | 109 | GeoBoundary gp; 110 | H3_EXPORT(h3ToGeoBoundary)(index, &gp); 111 | 112 | // return as an polygon 113 | // 114 | // initialize the POLYGON structure 115 | POLYGON *poly = NULL; 116 | int size = offsetof(POLYGON, p) + (sizeof(poly->p[0]) * gp.numVerts); 117 | poly = (POLYGON*) palloc0(size); 118 | SET_VARSIZE(poly, size); 119 | 120 | poly->npts = gp.numVerts; 121 | 122 | for(int i = 0; i < gp.numVerts; i++) { 123 | poly->p[i].y = radsToDegs(gp.verts[i].lat); 124 | poly->p[i].x = radsToDegs(gp.verts[i].lon); 125 | } 126 | 127 | __h3_make_bound_box(poly); 128 | 129 | PG_RETURN_POLYGON_P(poly); 130 | } 131 | 132 | 133 | PG_FUNCTION_INFO_V1(h3_h3index_is_valid); 134 | 135 | /* 136 | * Return True when an H3 index is valid. 137 | * 138 | * The index is expected in its string serialized representation. 139 | */ 140 | Datum 141 | h3_h3index_is_valid(PG_FUNCTION_ARGS) 142 | { 143 | text *index_text = PG_GETARG_TEXT_P(0); 144 | 145 | char * index_cstr = text_to_cstring(index_text); 146 | H3Index index; 147 | __h3_index_from_cstring(index_cstr, &index); 148 | 149 | int isvalid = H3_EXPORT(h3IsValid)(index); 150 | 151 | PG_RETURN_BOOL(isvalid != 0); 152 | } 153 | 154 | 155 | PG_FUNCTION_INFO_V1(h3_get_resolution); 156 | 157 | /* 158 | * Get the H3 resolution. 159 | * 160 | * The index is expected in its string serialized representation. 161 | */ 162 | Datum 163 | h3_get_resolution(PG_FUNCTION_ARGS) 164 | { 165 | text *index_text = PG_GETARG_TEXT_P(0); 166 | 167 | char * index_cstr = text_to_cstring(index_text); 168 | H3Index index; 169 | __h3_index_from_cstring(index_cstr, &index); 170 | 171 | int res = H3_EXPORT(h3GetResolution)(index); 172 | 173 | PG_RETURN_INT32(res); 174 | } 175 | 176 | 177 | PG_FUNCTION_INFO_V1(h3_get_basecell); 178 | 179 | /* 180 | * Get the H3 basecell. 181 | * 182 | * The index is expected in its string serialized representation. 183 | */ 184 | Datum 185 | h3_get_basecell(PG_FUNCTION_ARGS) 186 | { 187 | text *index_text = PG_GETARG_TEXT_P(0); 188 | 189 | char * index_cstr = text_to_cstring(index_text); 190 | H3Index index; 191 | __h3_index_from_cstring(index_cstr, &index); 192 | 193 | int basecell = H3_EXPORT(h3GetBaseCell)(index); 194 | 195 | PG_RETURN_INT32(basecell); 196 | } 197 | 198 | 199 | #if PGH3_H3_VERSION_NUM >= 30400 200 | 201 | PG_FUNCTION_INFO_V1(h3_get_basecells); 202 | 203 | /* 204 | * Return all indexes at resolution 0, the base cells. 205 | * 206 | * The indexes are returned in thier string representations 207 | */ 208 | Datum 209 | h3_get_basecells(PG_FUNCTION_ARGS) 210 | { 211 | FuncCallContext *funcctx; 212 | int call_cntr = 0; 213 | int max_calls = 0; 214 | MemoryContext oldcontext; 215 | H3Index *hexagons = NULL; 216 | 217 | if (SRF_IS_FIRSTCALL()) { 218 | funcctx = SRF_FIRSTCALL_INIT(); 219 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); 220 | 221 | int num_children = H3_EXPORT(res0IndexCount)(); 222 | max_calls = num_children; 223 | 224 | hexagons = palloc0(num_children * sizeof(H3Index)); 225 | H3_EXPORT(getRes0Indexes)(hexagons); 226 | 227 | if (max_calls > 0) { 228 | // keep track of the results 229 | funcctx->max_calls = max_calls; 230 | funcctx->user_fctx = hexagons; 231 | } 232 | else { 233 | // fast track when no results 234 | pfree(hexagons); 235 | hexagons = NULL; 236 | 237 | MemoryContextSwitchTo(oldcontext); 238 | SRF_RETURN_DONE(funcctx); 239 | } 240 | MemoryContextSwitchTo(oldcontext); 241 | } 242 | 243 | // stuff done on every call of the function 244 | funcctx = SRF_PERCALL_SETUP(); 245 | 246 | // Initialize per-call variables 247 | call_cntr = funcctx->call_cntr; 248 | max_calls = funcctx->max_calls; 249 | hexagons = funcctx->user_fctx; 250 | 251 | if (call_cntr < max_calls) { 252 | text * index_text = __h3_index_to_text(hexagons[call_cntr]); 253 | SRF_RETURN_NEXT(funcctx, PointerGetDatum(index_text)); 254 | } 255 | else { 256 | pfree(hexagons); 257 | hexagons = NULL; 258 | 259 | SRF_RETURN_DONE(funcctx); 260 | } 261 | } 262 | 263 | #endif 264 | -------------------------------------------------------------------------------- /src/misc.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 | 28 | #include

29 | 30 | 31 | #define H3_RES_TO_FLOAT8_FUNC(FUNC_NAME, H3_FUNC_NAME) \ 32 | Datum \ 33 | FUNC_NAME(PG_FUNCTION_ARGS) \ 34 | { \ 35 | int resolution = PG_GETARG_INT32(0); \ 36 | double value = H3_EXPORT(H3_FUNC_NAME)(resolution); \ 37 | PG_RETURN_FLOAT8(value); \ 38 | } 39 | 40 | 41 | PG_FUNCTION_INFO_V1(h3_hexagon_area_km2); 42 | H3_RES_TO_FLOAT8_FUNC(h3_hexagon_area_km2, hexAreaKm2); 43 | 44 | PG_FUNCTION_INFO_V1(h3_hexagon_area_m2); 45 | H3_RES_TO_FLOAT8_FUNC(h3_hexagon_area_m2, hexAreaM2); 46 | 47 | PG_FUNCTION_INFO_V1(h3_edge_length_km); 48 | H3_RES_TO_FLOAT8_FUNC(h3_edge_length_km, edgeLengthKm); 49 | 50 | PG_FUNCTION_INFO_V1(h3_edge_length_m); 51 | H3_RES_TO_FLOAT8_FUNC(h3_edge_length_m, edgeLengthM); 52 | 53 | -------------------------------------------------------------------------------- /src/neighbor.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/geo_decls.h" 29 | #include "funcapi.h" 30 | 31 | #include

32 | 33 | 34 | PG_FUNCTION_INFO_V1(h3_kring); 35 | 36 | /* 37 | * Returns the neigbor indices within the given distance. 38 | * 39 | * The indexex are returned in thier string representations 40 | */ 41 | Datum 42 | h3_kring(PG_FUNCTION_ARGS) 43 | { 44 | FuncCallContext *funcctx; 45 | int call_cntr = 0; 46 | int max_calls = 0; 47 | MemoryContext oldcontext; 48 | H3Index *hexagons = NULL; 49 | 50 | if (SRF_IS_FIRSTCALL()) { 51 | 52 | text *center_index_text = PG_GETARG_TEXT_P(0); 53 | char * center_index_cstr = text_to_cstring(center_index_text); 54 | H3Index center_index; 55 | __h3_index_from_cstring(center_index_cstr, ¢er_index); 56 | 57 | int distance = PG_GETARG_INT32(1); 58 | 59 | funcctx = SRF_FIRSTCALL_INIT(); 60 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); 61 | 62 | int max_num_neighbors = H3_EXPORT(maxKringSize)(distance); 63 | 64 | report_debug1("Generating %d H3 child hexagons within distance %d", 65 | max_num_neighbors, distance); 66 | 67 | hexagons = palloc0(max_num_neighbors * sizeof(H3Index)); 68 | kRing(center_index, distance, hexagons); 69 | 70 | for (int i = 0; i < max_num_neighbors; i++) { 71 | if (hexagons[i] != 0) { 72 | if (i > max_calls) { 73 | // fill NULL "holes" in list of hexagons with the hexagons located 74 | // after the NULL value. This allows us to use call_cntr and max_calls 75 | // for iteration during SRF calls. 76 | hexagons[max_calls] = hexagons[i]; 77 | hexagons[i] = 0; 78 | } 79 | max_calls++; 80 | } 81 | } 82 | 83 | if (max_calls > 0) { 84 | // keep track of the results 85 | funcctx->max_calls = max_calls; 86 | funcctx->user_fctx = hexagons; 87 | } 88 | else { 89 | // fast track when no results 90 | pfree(hexagons); 91 | hexagons = NULL; 92 | 93 | MemoryContextSwitchTo(oldcontext); 94 | SRF_RETURN_DONE(funcctx); 95 | } 96 | MemoryContextSwitchTo(oldcontext); 97 | } 98 | 99 | // stuff done on every call of the function 100 | funcctx = SRF_PERCALL_SETUP(); 101 | 102 | // Initialize per-call variables 103 | call_cntr = funcctx->call_cntr; 104 | max_calls = funcctx->max_calls; 105 | hexagons = funcctx->user_fctx; 106 | 107 | if (call_cntr < max_calls) { 108 | text * index_text = __h3_index_to_text(hexagons[call_cntr]); 109 | SRF_RETURN_NEXT(funcctx, PointerGetDatum(index_text)); 110 | } 111 | else { 112 | pfree(hexagons); 113 | hexagons = NULL; 114 | 115 | SRF_RETURN_DONE(funcctx); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/pgh3.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 | #include "postgres.h" 21 | #include "fmgr.h" 22 | #include "utils/builtins.h" 23 | 24 | #ifdef PG_MODULE_MAGIC 25 | PG_MODULE_MAGIC; 26 | #endif 27 | 28 | 29 | PG_FUNCTION_INFO_V1(h3_ext_version); 30 | 31 | /* 32 | * return the version of this extension (not h3 itself) 33 | * as text. 34 | */ 35 | Datum 36 | h3_ext_version(PG_FUNCTION_ARGS) 37 | { 38 | #ifdef EXTVERSION 39 | /* EXTVERSION is only available on some compilers. see Makefile */ 40 | PG_RETURN_TEXT_P(cstring_to_text(EXTVERSION)); 41 | #else 42 | PG_RETURN_TEXT_P(cstring_to_text("unknown")); 43 | #endif 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/region.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/array.h" 26 | #include "utils/builtins.h" 27 | #include 28 | #include "fmgr.h" 29 | #include "utils/geo_decls.h" 30 | #include "utils/lsyscache.h" 31 | #include "access/tupmacs.h" 32 | #include "funcapi.h" 33 | 34 | #include

35 | 36 | 37 | static void 38 | __h3_free_geopolygon_internal_structs(GeoPolygon *h3polygon) { 39 | if (h3polygon->numHoles > 0) { 40 | 41 | for (int hi = 0; hi < h3polygon->numHoles; hi++) { 42 | pfree(h3polygon->holes[hi].verts); 43 | h3polygon->holes[hi].verts = NULL; 44 | } 45 | pfree(h3polygon->holes); 46 | h3polygon->holes = NULL; 47 | } 48 | pfree(h3polygon->geofence.verts); 49 | h3polygon->geofence.verts = NULL; 50 | } 51 | 52 | static void 53 | __h3_pgpolygon_to_geofence(POLYGON *poly, Geofence *geofence) 54 | { 55 | 56 | // initialize the Geofence structure 57 | geofence->verts = (GeoCoord*) palloc0( 58 | (sizeof(geofence->verts[0]) * poly->npts)); 59 | 60 | geofence->numVerts = poly->npts; 61 | 62 | for (int i = 0; i < poly->npts; i++) { 63 | geofence->verts[i].lat = degsToRads(poly->p[i].y); 64 | geofence->verts[i].lon = degsToRads(poly->p[i].x); 65 | } 66 | } 67 | 68 | static void 69 | __h3_polyfill_build_geopolygon(GeoPolygon *h3polygon, POLYGON *exterior_ring, ArrayType *interior_rings) 70 | { 71 | // initialize 72 | h3polygon->numHoles = 0; 73 | h3polygon->holes = NULL; 74 | h3polygon->geofence.verts = NULL; 75 | 76 | // fill the geofence of the h3 struct with the values of the exterior ring. 77 | __h3_pgpolygon_to_geofence(exterior_ring, &(h3polygon->geofence)); 78 | 79 | if (interior_rings != NULL) { 80 | int ndim = ARR_NDIM(interior_rings); 81 | int *dim = ARR_DIMS(interior_rings); 82 | 83 | if (ndim > 0) { // not an empty array 84 | if (ndim != 1) { 85 | fail_and_report("The interior rings argument must be Null, " 86 | "an empty array or a 1-dimensional array"); 87 | } 88 | 89 | Oid elemtype = ARR_ELEMTYPE(interior_rings); 90 | if (elemtype != POLYGONOID) { 91 | fail_and_report("the type of the interior_rings array must be " 92 | "postgresqls polygon type"); 93 | } 94 | 95 | int16 typlen; 96 | bool typbyval; 97 | char typalign; 98 | get_typlenbyvalalign(elemtype, &typlen, &typbyval, &typalign); 99 | 100 | h3polygon->numHoles = dim[0]; 101 | h3polygon->holes = (Geofence *) palloc0(h3polygon->numHoles * (sizeof(Geofence))); 102 | 103 | bool isnull; 104 | Datum interior_rings_datum = PointerGetDatum(interior_rings); 105 | for (int i = 1; i <= dim[0]; i++) { 106 | 107 | Datum ring_datum = array_get_element(interior_rings_datum, ndim, &i, 108 | -1, typlen, typbyval, typalign, &isnull); 109 | if (isnull) { 110 | fail_and_report("interior ring at position %d is null", i); 111 | } 112 | 113 | POLYGON *ring = DatumGetPolygonP(ring_datum); 114 | __h3_pgpolygon_to_geofence(ring, &(h3polygon->holes[i - 1])); 115 | } 116 | } 117 | } 118 | } 119 | 120 | 121 | /** 122 | * attempt to check the result of maxPolyfillSize for integer overflows. 123 | * 124 | * maxPolyfillSize uses an integer to return the number of estimated hexagons, 125 | * for large polygons and/or high resolutions this may exceed the capacity 126 | * of the integer type. 127 | * 128 | * This function certainly does not catch all cases of an overflow, but it 129 | * should at least help a bit. 130 | * 131 | * this is an issue to be handled in H3 itself (using 132 | * unsigned long, ... etc as the return type). 133 | */ 134 | static int 135 | h3_maxPolyfillSize_checked(GeoPolygon *h3polygon, int resolution) 136 | { 137 | int numHexagons = H3_EXPORT(maxPolyfillSize)(h3polygon, resolution); 138 | 139 | if ((numHexagons < 0) || (numHexagons == INT_MAX)) { 140 | fail_and_report_with_code( 141 | ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE, 142 | "Integer overflow detected when estimating the number of hexagons " 143 | "for a polyfill at resolution %d. Please use a smaller resolution.", resolution); 144 | } 145 | 146 | return numHexagons; 147 | } 148 | 149 | 150 | PG_FUNCTION_INFO_V1(_h3_polyfill_polygon); 151 | 152 | Datum 153 | _h3_polyfill_polygon(PG_FUNCTION_ARGS) 154 | { 155 | FuncCallContext *funcctx; 156 | int call_cntr = 0; 157 | int max_calls = 0; 158 | MemoryContext oldcontext; 159 | H3Index *hexagons = NULL; 160 | 161 | if (SRF_IS_FIRSTCALL()) { 162 | // early exit when exterior_ring is null 163 | if (PG_ARGISNULL(0)) { 164 | PG_RETURN_NULL(); 165 | } 166 | 167 | funcctx = SRF_FIRSTCALL_INIT(); 168 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); 169 | 170 | POLYGON *exterior_ring = PG_GETARG_POLYGON_P(0); 171 | ArrayType *interior_rings = NULL; 172 | if (!(PG_ARGISNULL(1))) { 173 | interior_rings = PG_GETARG_ARRAYTYPE_P(1); 174 | } 175 | int resolution = PG_GETARG_INT32(2); 176 | 177 | GeoPolygon h3polygon; 178 | __h3_polyfill_build_geopolygon(&h3polygon, exterior_ring, interior_rings); 179 | 180 | int numHexagons = h3_maxPolyfillSize_checked(&h3polygon, resolution); 181 | 182 | report_debug1("Generating an estimated number of %d H3 " 183 | "hexagons at resolution %d", numHexagons, resolution); 184 | 185 | hexagons = __h3_polyfill_palloc0(numHexagons * sizeof(H3Index)); 186 | H3_EXPORT(polyfill)(&h3polygon, resolution, hexagons); 187 | __h3_free_geopolygon_internal_structs(&h3polygon); 188 | 189 | for (int i = 0; i < numHexagons; i++) { 190 | if (hexagons[i] != 0) { 191 | if (i > max_calls) { 192 | // fill NULL "holes" in list of hexagons with the hexagons located 193 | // after the NULL value. This allows us to use call_cntr and max_calls 194 | // for iteration during SRF calls. 195 | hexagons[max_calls] = hexagons[i]; 196 | hexagons[i] = 0; 197 | } 198 | max_calls++; 199 | } 200 | } 201 | report_debug1("Generated exactly %d H3 hexagons at resolution %d", 202 | max_calls, resolution); 203 | 204 | if (max_calls > 0) { 205 | // keep track of the results 206 | funcctx->max_calls = max_calls; 207 | funcctx->user_fctx = hexagons; 208 | } 209 | else { 210 | // fast track when no results 211 | pfree(hexagons); 212 | hexagons = NULL; 213 | 214 | MemoryContextSwitchTo(oldcontext); 215 | SRF_RETURN_DONE(funcctx); 216 | } 217 | MemoryContextSwitchTo(oldcontext); 218 | } 219 | 220 | // stuff done on every call of the function 221 | funcctx = SRF_PERCALL_SETUP(); 222 | 223 | // Initialize per-call variables 224 | call_cntr = funcctx->call_cntr; 225 | max_calls = funcctx->max_calls; 226 | hexagons = funcctx->user_fctx; 227 | 228 | if (call_cntr < max_calls) { 229 | text * index_text = __h3_index_to_text(hexagons[call_cntr]); 230 | SRF_RETURN_NEXT(funcctx, PointerGetDatum(index_text)); 231 | } 232 | else { 233 | pfree(hexagons); 234 | hexagons = NULL; 235 | 236 | SRF_RETURN_DONE(funcctx); 237 | } 238 | } 239 | 240 | 241 | PG_FUNCTION_INFO_V1(_h3_polyfill_polygon_estimate); 242 | 243 | Datum 244 | _h3_polyfill_polygon_estimate(PG_FUNCTION_ARGS) 245 | { 246 | // early exit when exterior_ring is null 247 | if (PG_ARGISNULL(0)) { 248 | PG_RETURN_NULL(); 249 | } 250 | 251 | POLYGON *exterior_ring = PG_GETARG_POLYGON_P(0); 252 | ArrayType *interior_rings = NULL; 253 | if (!(PG_ARGISNULL(1))) { 254 | interior_rings = PG_GETARG_ARRAYTYPE_P(1); 255 | } 256 | int resolution = PG_GETARG_INT32(2); 257 | 258 | GeoPolygon h3polygon; 259 | __h3_polyfill_build_geopolygon(&h3polygon, exterior_ring, interior_rings); 260 | 261 | int numHexagons = h3_maxPolyfillSize_checked(&h3polygon, resolution); 262 | 263 | __h3_free_geopolygon_internal_structs(&h3polygon); 264 | 265 | PG_RETURN_INT32(numHexagons); 266 | } 267 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Deutsches Zentrum für Luft- und Raumfahrt e.V. 3 | * (German Aerospace Center), Earth Observation Center 4 | * 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | #include "util.h" 21 | 22 | #include "utils/memutils.h" 23 | #include "utils/guc.h" // for GetConfigOption* 24 | 25 | #define H3_INDEX_STR_LEN 17 26 | 27 | 28 | /* 29 | * Convert an H3Index to a text* 30 | */ 31 | text * 32 | __h3_index_to_text(H3Index index) 33 | { 34 | char *outstr = (char *) palloc0(H3_INDEX_STR_LEN * sizeof(char)); 35 | SET_VARSIZE(outstr, H3_INDEX_STR_LEN * sizeof(char)); 36 | 37 | H3_EXPORT(h3ToString)(index, outstr, H3_INDEX_STR_LEN); 38 | 39 | text *iout = cstring_to_text(outstr); 40 | pfree(outstr); 41 | return iout; 42 | } 43 | 44 | /** 45 | * convert an cstring to an h3index 46 | */ 47 | bool 48 | __h3_index_from_cstring(const char *cstr, H3Index *index) 49 | { 50 | (*index) = stringToH3(cstr); 51 | if ((*index) == 0) { 52 | fail_and_report("Could not convert the value '%s' to a H3 index", cstr); 53 | return false; 54 | } 55 | return true; 56 | } 57 | 58 | /* 59 | * Make the smallest bounding box for the given polygon. 60 | * 61 | * This funciton is taken from postgresql 62 | */ 63 | void 64 | __h3_make_bound_box(POLYGON *poly) 65 | { 66 | int i; 67 | double x1, 68 | y1, 69 | x2, 70 | y2; 71 | 72 | Assert(poly->npts > 0); 73 | 74 | x1 = x2 = poly->p[0].x; 75 | y2 = y1 = poly->p[0].y; 76 | for (i = 1; i < poly->npts; i++) 77 | { 78 | if (poly->p[i].x < x1) 79 | x1 = poly->p[i].x; 80 | if (poly->p[i].x > x2) 81 | x2 = poly->p[i].x; 82 | if (poly->p[i].y < y1) 83 | y1 = poly->p[i].y; 84 | if (poly->p[i].y > y2) 85 | y2 = poly->p[i].y; 86 | } 87 | 88 | poly->boundbox.low.x = x1; 89 | poly->boundbox.high.x = x2; 90 | poly->boundbox.low.y = y1; 91 | poly->boundbox.high.y = y2; 92 | } 93 | 94 | static const char * 95 | human_byte_size(size_t size) 96 | { 97 | static char *suffix[] = {"B", "KB", "MB", "GB", "TB"}; 98 | static char length = sizeof(suffix) / sizeof(suffix[0]); 99 | 100 | int i = 0; 101 | 102 | double size_dbl = size; 103 | 104 | if (size > 1024) { 105 | for (i = 0; (size / 1024) > 0 && i 100000 // GetConfigOptionByName(const char *name, const char **varname, bool missing_ok) 126 | // exists with PG 9.6+, GUC_UNIT_MB with PG 10 127 | if (max_polyfill_mem <= 0) { 128 | const char * max_polyfill_mem_str = GetConfigOptionByName(PGH3_POLYFILL_MEM_SETTING_NAME, NULL, true); 129 | if (max_polyfill_mem_str != NULL) { 130 | 131 | int max_polyfill_mem_mb = 0; 132 | 133 | if (!parse_int(max_polyfill_mem_str, &max_polyfill_mem_mb, GUC_UNIT_MB, NULL)) { 134 | 135 | fail_and_report_with_code( 136 | ERRCODE_CONFIG_FILE_ERROR, 137 | PGH3_POLYFILL_MEM_SETTING_NAME ": could not parse value \"%s\"", 138 | max_polyfill_mem_str); 139 | 140 | } 141 | max_polyfill_mem = (size_t)max_polyfill_mem_mb * 1024 * 1024; 142 | } 143 | } 144 | #endif 145 | 146 | // not set, so set max. possible allocation using the standard allocator 147 | if (max_polyfill_mem == 0) { 148 | max_polyfill_mem = MaxAllocSize; 149 | } 150 | 151 | report_debug1(PGH3_POLYFILL_MEM_SETTING_NAME ": using %s of the possible %ldMB.", 152 | human_byte_size(size), 153 | max_polyfill_mem / 1024 /1024); 154 | 155 | if (size > max_polyfill_mem) { 156 | 157 | fail_and_report_with_code( 158 | ERRCODE_CONFIGURATION_LIMIT_EXCEEDED, 159 | PGH3_POLYFILL_MEM_SETTING_NAME ": requested memory allocation (%s) exceeds the upper limit (%ldMB).", 160 | human_byte_size(size), 161 | max_polyfill_mem / 1024 /1024); 162 | 163 | } 164 | 165 | int flags = 0; 166 | if (!AllocSizeIsValid(size)) { 167 | // the huge allocator is required 168 | flags |= MCXT_ALLOC_HUGE; 169 | } 170 | flags |= MCXT_ALLOC_ZERO; 171 | 172 | return palloc_extended(size, flags); 173 | } 174 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 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 | #ifndef __PGH3_UTIL_H__ 21 | #define __PGH3_UTIL_H__ 22 | 23 | #include "postgres.h" 24 | #include "fmgr.h" 25 | #include "utils/builtins.h" 26 | #include "utils/geo_decls.h" 27 | 28 | #include

29 | 30 | #define ARRNELEMS(x) ArrayGetNItems(ARR_NDIM(x), ARR_DIMS(x)) 31 | 32 | #define fail_and_report_with_code(code, msg, ...) \ 33 | ereport(ERROR, \ 34 | (errcode(code), errmsg(msg, ##__VA_ARGS__))); 35 | 36 | #define fail_and_report(msg, ...) \ 37 | fail_and_report_with_code(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, msg, ##__VA_ARGS__); 38 | 39 | #define report_notice(msg, ...) \ 40 | ereport(NOTICE, \ 41 | (errmsg(msg, ##__VA_ARGS__))); 42 | 43 | #define report_debug1(msg, ...) \ 44 | ereport(DEBUG1, \ 45 | (errmsg(msg, ##__VA_ARGS__))); 46 | 47 | // See https://www.postgresql.org/docs/9.2/runtime-config-custom.html for adding 48 | // custom options 49 | #define PGH3_POLYFILL_MEM_SETTING_NAME "pgh3.polyfill_mem" 50 | 51 | // combined version number for H3, using the same method postgresql uses 52 | #ifdef H3_VERSION_MAJOR 53 | #define PGH3_H3_VERSION_NUM (H3_VERSION_MAJOR * 10000) + (H3_VERSION_MINOR * 100) + H3_VERSION_PATCH 54 | #else 55 | #define PGH3_H3_VERSION_NUM 0 56 | #endif 57 | 58 | 59 | text * __h3_index_to_text(H3Index); 60 | void __h3_make_bound_box(POLYGON *poly); 61 | bool __h3_index_from_cstring(const char *str, H3Index *index); 62 | void * __h3_polyfill_palloc0(size_t size); 63 | 64 | #endif // __PGH3_UTIL_H__ 65 | --------------------------------------------------------------------------------