[postgis-tickets] [SCM] PostGIS; Spatial objects for PostgreSQL. branch master-hexgrid created. acdcf8b23fc2cdd348b3ccb7858f54b8cff3301a
git at osgeo.org
git at osgeo.org
Mon Oct 28 09:31:38 PDT 2019
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS; Spatial objects for PostgreSQL.".
The branch, master-hexgrid has been created
at acdcf8b23fc2cdd348b3ccb7858f54b8cff3301a (commit)
- Log -----------------------------------------------------------------
commit acdcf8b23fc2cdd348b3ccb7858f54b8cff3301a
Merge: cba68ed 9978963
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Mon Oct 28 09:30:00 2019 -0700
Merge branch 'master' of https://git.osgeo.org/gogs/postgis/postgis into svn-trunk-hexgrid
commit cba68ed602c4268ea50a36251e5417fa5b5ed6f1
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu Aug 22 20:29:10 2019 -0700
push
diff --git a/postgis/lwgeom_hexgrid.c b/postgis/lwgeom_hexgrid.c
index 0ab720d..61278b4 100644
--- a/postgis/lwgeom_hexgrid.c
+++ b/postgis/lwgeom_hexgrid.c
@@ -163,8 +163,8 @@ Datum ST_ShapeGrid(PG_FUNCTION_ARGS)
GSERIALIZED *gbounds;
GeometryGridState *state;
- LWGEOM *lwgeom;
+ LWGEOM *lwgeom;
bool isnull[3] = {0,0,0}; /* needed to say no value is null */
Datum tuple_arr[3]; /* used to construct the composite return value */
HeapTuple tuple;
commit c3de9f82a5e80fb8684b7d70f8d7ee977b8ab27f
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu Aug 22 20:21:42 2019 -0700
mixed decls and code
diff --git a/postgis/lwgeom_hexgrid.c b/postgis/lwgeom_hexgrid.c
index 8024032..0ab720d 100644
--- a/postgis/lwgeom_hexgrid.c
+++ b/postgis/lwgeom_hexgrid.c
@@ -163,6 +163,7 @@ Datum ST_ShapeGrid(PG_FUNCTION_ARGS)
GSERIALIZED *gbounds;
GeometryGridState *state;
+ LWGEOM *lwgeom;
bool isnull[3] = {0,0,0}; /* needed to say no value is null */
Datum tuple_arr[3]; /* used to construct the composite return value */
@@ -253,7 +254,6 @@ Datum ST_ShapeGrid(PG_FUNCTION_ARGS)
j = state->cell_current / state->cell_width;
/* Generate geometry */
- LWGEOM *lwgeom;
switch (state->cell_shape)
{
case SHAPE_HEXAGON:
commit 3dfb4c7defa41f0c7227172f6ae7a112ccfb3e0e
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu Aug 22 20:12:35 2019 -0700
CI build issues
diff --git a/postgis/lwgeom_hexgrid.c b/postgis/lwgeom_hexgrid.c
index 45b6769..8024032 100644
--- a/postgis/lwgeom_hexgrid.c
+++ b/postgis/lwgeom_hexgrid.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "fmgr.h"
#include "funcapi.h"
+#include "access/htup_details.h"
#include "../postgis_config.h"
#include "lwgeom_pg.h"
@@ -156,11 +157,11 @@ GeometryGridState;
PG_FUNCTION_INFO_V1(ST_ShapeGrid);
Datum ST_ShapeGrid(PG_FUNCTION_ARGS)
{
+ int i, j;
FuncCallContext *funcctx;
MemoryContext oldcontext, newcontext;
GSERIALIZED *gbounds;
- GBOX bounds;
GeometryGridState *state;
bool isnull[3] = {0,0,0}; /* needed to say no value is null */
@@ -170,15 +171,20 @@ Datum ST_ShapeGrid(PG_FUNCTION_ARGS)
if (SRF_IS_FIRSTCALL())
{
+ const char *func_name;
+ double bounds_width, bounds_height;
+ int gbounds_is_empty;
+ GBOX bounds;
+ double size;
funcctx = SRF_FIRSTCALL_INIT();
/* get a local copy of what we're doing a dump points on */
gbounds = PG_GETARG_GSERIALIZED_P(1);
- double size = PG_GETARG_FLOAT8(0);
+ size = PG_GETARG_FLOAT8(0);
- int gbounds_is_empty = (gserialized_get_gbox_p(gbounds, &bounds) == LW_FAILURE);
- double bounds_width = bounds.xmax - bounds.xmin;
- double bounds_height = bounds.ymax - bounds.ymin;
+ gbounds_is_empty = (gserialized_get_gbox_p(gbounds, &bounds) == LW_FAILURE);
+ bounds_width = bounds.xmax - bounds.xmin;
+ bounds_height = bounds.ymax - bounds.ymin;
/* quick opt-out if we get nonsensical inputs */
if (size <= 0.0 || gbounds_is_empty ||
@@ -198,7 +204,7 @@ Datum ST_ShapeGrid(PG_FUNCTION_ARGS)
state->srid = gserialized_get_srid(gbounds);
state->size = size;
- const char *func_name = get_func_name(fcinfo->flinfo->fn_oid);
+ func_name = get_func_name(fcinfo->flinfo->fn_oid);
if (strcmp(func_name, "st_hexagongrid") == 0)
{
state->cell_shape = SHAPE_HEXAGON;
@@ -243,8 +249,8 @@ Datum ST_ShapeGrid(PG_FUNCTION_ARGS)
SRF_RETURN_DONE(funcctx);
}
- int i = state->cell_current % state->cell_width;
- int j = state->cell_current / state->cell_width;
+ i = state->cell_current % state->cell_width;
+ j = state->cell_current / state->cell_width;
/* Generate geometry */
LWGEOM *lwgeom;
commit 6a1e56958834957bcde1fcf3c502af30e2f02389
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu Aug 22 15:47:57 2019 -0700
First draft, still needs documentation
diff --git a/postgis/Makefile.in b/postgis/Makefile.in
index 76a2c01..d821041 100644
--- a/postgis/Makefile.in
+++ b/postgis/Makefile.in
@@ -91,6 +91,7 @@ PG_OBJS= \
lwgeom_geos_prepared.o \
lwgeom_geos_clean.o \
lwgeom_geos_relatematch.o \
+ lwgeom_hexgrid.o \
lwgeom_export.o \
lwgeom_in_gml.o \
lwgeom_in_kml.o \
diff --git a/postgis/lwgeom_hexgrid.c b/postgis/lwgeom_hexgrid.c
new file mode 100644
index 0000000..45b6769
--- /dev/null
+++ b/postgis/lwgeom_hexgrid.c
@@ -0,0 +1,354 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ *
+ * PostGIS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * PostGIS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
+ *
+ **********************************************************************
+ *
+ * Copyright 2009 Mark Cave-Ayland <mark.cave-ayland at siriusit.co.uk>
+ * Copyright 2009-2017 Paul Ramsey <pramsey at cleverelephant.ca>
+ * Copyright 2018 Darafei Praliaskouski <me at komzpa.net>
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+#include "fmgr.h"
+#include "funcapi.h"
+
+#include "../postgis_config.h"
+#include "lwgeom_pg.h"
+#include "liblwgeom.h"
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+
+
+Datum ST_Hexagon(PG_FUNCTION_ARGS);
+Datum ST_Square(PG_FUNCTION_ARGS);
+Datum ST_ShapeGrid(PG_FUNCTION_ARGS);
+
+/* ********* ********* ********* ********* ********* ********* ********* ********* */
+
+/* XXX TO DO: what about negative sizes? */
+
+/* Ratio of hexagon edge length to height = 2*cos(pi/6) */
+#define HXR 1.7320508075688774
+
+static const double hex_x[] = {0.0, 1.0, 1.5, 1.0, 0.0, -0.5, 0.0};
+static const double hex_y[] = {0.0, 0.0, 0.5*HXR, HXR, HXR, 0.5*HXR, 0.0};
+
+static LWGEOM *
+hexagon(double origin_x, double origin_y, double edge, int cell_i, int cell_j, uint32_t srid)
+{
+ double height = edge * HXR;
+ POINT4D pt;
+ POINTARRAY **ppa = lwalloc(sizeof(POINTARRAY*));
+ POINTARRAY *pa = ptarray_construct(0, 0, 7);
+ uint32_t i;
+
+ double offset_x = origin_x + (1.5 * edge * cell_i);
+ double offset_y = origin_y + (height * cell_j) + (0.5 * height * (cell_i % 2));
+ pt.z = pt.m = 0.0;
+
+ for (i = 0; i < 7; ++i)
+ {
+ pt.x = edge * hex_x[i] + offset_x;
+ pt.y = edge * hex_y[i] + offset_y;
+ ptarray_set_point4d(pa, i, &pt);
+ }
+
+ ppa[0] = pa;
+ return lwpoly_as_lwgeom(lwpoly_construct(srid, NULL, 1 /* nrings */, ppa));
+}
+
+static int
+hexagon_grid_size(double edge, const GBOX *gbox, int *cell_width, int *cell_height)
+{
+ double hex_height = edge * HXR;
+ double bounds_width = gbox->xmax - gbox->xmin;
+ double bounds_height = gbox->ymax - gbox->ymin;
+ int width = (int)floor(bounds_width/(1.5*edge)) + 1;
+ int height = (int)floor(bounds_height/hex_height) + 1;
+ if (cell_width) *cell_width = width;
+ if (cell_height) *cell_height = height;
+ return width * height;
+}
+
+/* ********* ********* ********* ********* ********* ********* ********* ********* */
+
+static const double sqr_x[] = {0.0, 1.0, 1.0, 0.0, 0.0};
+static const double sqr_y[] = {0.0, 0.0, 1.0, 1.0, 0.0};
+
+static LWGEOM *
+square(double origin_x, double origin_y, double E, int cell_i, int cell_j, uint32_t srid)
+{
+ POINT4D pt;
+ POINTARRAY **ppa = lwalloc(sizeof(POINTARRAY*));
+ POINTARRAY *pa = ptarray_construct(0, 0, 5);
+ uint32_t i;
+
+ double offset_x = origin_x + (E * cell_i);
+ double offset_y = origin_y + (E * cell_j);
+ pt.z = pt.m = 0.0;
+
+ for (i = 0; i < 5; ++i)
+ {
+ pt.x = E * sqr_x[i] + offset_x;
+ pt.y = E * sqr_y[i] + offset_y;
+ ptarray_set_point4d(pa, i, &pt);
+ }
+
+ ppa[0] = pa;
+ return lwpoly_as_lwgeom(lwpoly_construct(srid, NULL, 1 /* nrings */, ppa));
+}
+
+static int
+square_grid_size(double edge, const GBOX *gbox, int *cell_width, int *cell_height)
+{
+ int width = (int)floor((gbox->xmax - gbox->xmin)/edge) + 1;
+ int height = (int)floor((gbox->ymax - gbox->ymin)/edge) + 1;
+ if (cell_width) *cell_width = width;
+ if (cell_height) *cell_height = height;
+ return width * height;
+}
+
+/* ********* ********* ********* ********* ********* ********* ********* ********* */
+
+typedef enum
+{
+ SHAPE_SQUARE,
+ SHAPE_HEXAGON,
+ SHAPE_TRIANGLE
+} GeometryShape;
+
+typedef struct GeometryGridState
+{
+ int cell_current;
+ int cell_count;
+ int cell_width;
+ int cell_height;
+ GBOX bounds;
+ double size;
+ uint32_t srid;
+ GeometryShape cell_shape;
+}
+GeometryGridState;
+
+/**
+* ST_HexagonGrid(size double default 1.0,
+* bounds geometry default 'LINESTRING(0 0, 100 100)')
+*/
+PG_FUNCTION_INFO_V1(ST_ShapeGrid);
+Datum ST_ShapeGrid(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ MemoryContext oldcontext, newcontext;
+
+ GSERIALIZED *gbounds;
+ GBOX bounds;
+ GeometryGridState *state;
+
+ bool isnull[3] = {0,0,0}; /* needed to say no value is null */
+ Datum tuple_arr[3]; /* used to construct the composite return value */
+ HeapTuple tuple;
+ Datum result; /* the actual composite return value */
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ funcctx = SRF_FIRSTCALL_INIT();
+
+ /* get a local copy of what we're doing a dump points on */
+ gbounds = PG_GETARG_GSERIALIZED_P(1);
+ double size = PG_GETARG_FLOAT8(0);
+
+ int gbounds_is_empty = (gserialized_get_gbox_p(gbounds, &bounds) == LW_FAILURE);
+ double bounds_width = bounds.xmax - bounds.xmin;
+ double bounds_height = bounds.ymax - bounds.ymin;
+
+ /* quick opt-out if we get nonsensical inputs */
+ if (size <= 0.0 || gbounds_is_empty ||
+ bounds_width <= 0.0 || bounds_height <= 0.0)
+ {
+ funcctx = SRF_PERCALL_SETUP();
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ newcontext = funcctx->multi_call_memory_ctx;
+ oldcontext = MemoryContextSwitchTo(newcontext);
+
+ /* Create function state */
+ state = lwalloc(sizeof(GeometryGridState));
+ state->cell_current = 0;
+ state->bounds = bounds;
+ state->srid = gserialized_get_srid(gbounds);
+ state->size = size;
+
+ const char *func_name = get_func_name(fcinfo->flinfo->fn_oid);
+ if (strcmp(func_name, "st_hexagongrid") == 0)
+ {
+ state->cell_shape = SHAPE_HEXAGON;
+ state->cell_count = hexagon_grid_size(size, &state->bounds,
+ &state->cell_width, &state->cell_height);
+ }
+ else if (strcmp(func_name, "st_squaregrid") == 0)
+ {
+ state->cell_shape = SHAPE_SQUARE;
+ state->cell_count = square_grid_size(size, &state->bounds,
+ &state->cell_width, &state->cell_height);
+ }
+ else
+ {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("%s called from unsupported functional context '%s'", __func__, func_name)));
+ }
+
+ funcctx->user_fctx = state;
+
+ /* get tuple description for return type */
+ if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE)
+ {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ }
+
+ BlessTupleDesc(funcctx->tuple_desc);
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /* stuff done on every call of the function */
+ funcctx = SRF_PERCALL_SETUP();
+ newcontext = funcctx->multi_call_memory_ctx;
+
+ /* get state */
+ state = funcctx->user_fctx;
+
+ /* Stop when we've used up all the grid squares */
+ if (state->cell_current >= state->cell_count)
+ {
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ int i = state->cell_current % state->cell_width;
+ int j = state->cell_current / state->cell_width;
+
+ /* Generate geometry */
+ LWGEOM *lwgeom;
+ switch (state->cell_shape)
+ {
+ case SHAPE_HEXAGON:
+ lwgeom = hexagon(state->bounds.xmin, state->bounds.ymin, state->size, i, j, state->srid);
+ break;
+ case SHAPE_SQUARE:
+ lwgeom = square(state->bounds.xmin, state->bounds.ymin, state->size, i, j, state->srid);
+ break;
+ default:
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("%s called from with unsupported shape '%d'", __func__, state->cell_shape)));
+ }
+
+ tuple_arr[0] = PointerGetDatum(geometry_serialize(lwgeom));
+ lwfree(lwgeom);
+
+ /* Store grid cell coordinates */
+ tuple_arr[1] = Int32GetDatum(i);
+ tuple_arr[2] = Int32GetDatum(j);
+
+ tuple = heap_form_tuple(funcctx->tuple_desc, tuple_arr, isnull);
+ result = HeapTupleGetDatum(tuple);
+ state->cell_current++;
+ SRF_RETURN_NEXT(funcctx, result);
+}
+
+/**
+* ST_Hexagon(size double,
+* origin geometry default 'POINT(0 0)',
+* int cellx default 0, int celly default 0)
+*/
+PG_FUNCTION_INFO_V1(ST_Hexagon);
+Datum ST_Hexagon(PG_FUNCTION_ARGS)
+{
+ LWPOINT *lwpt;
+ double size = PG_GETARG_FLOAT8(0);
+ GSERIALIZED *gorigin = PG_GETARG_GSERIALIZED_P(1);
+ GSERIALIZED *ghex;
+ int cell_i = PG_GETARG_INT32(2);
+ int cell_j = PG_GETARG_INT32(3);
+ LWGEOM *lwhex;
+ LWGEOM *lworigin = lwgeom_from_gserialized(gorigin);
+
+ if (lwgeom_is_empty(lworigin))
+ {
+ elog(ERROR, "%s: origin point is empty", __func__);
+ PG_RETURN_NULL();
+ }
+
+ lwpt = lwgeom_as_lwpoint(lworigin);
+ if (!lwpt)
+ {
+ elog(ERROR, "%s: origin argument is not a point", __func__);
+ PG_RETURN_NULL();
+ }
+
+ lwhex = hexagon(lwpoint_get_x(lwpt), lwpoint_get_y(lwpt),
+ size, cell_i, cell_j,
+ lwgeom_get_srid(lworigin));
+
+ ghex = geometry_serialize(lwhex);
+ PG_FREE_IF_COPY(gorigin, 1);
+ PG_RETURN_POINTER(ghex);
+}
+
+
+/**
+* ST_Square(size double,
+* origin geometry default 'POINT(0 0)',
+* int cellx default 0, int celly default 0)
+*/
+PG_FUNCTION_INFO_V1(ST_Square);
+Datum ST_Square(PG_FUNCTION_ARGS)
+{
+ LWPOINT *lwpt;
+ double size = PG_GETARG_FLOAT8(0);
+ GSERIALIZED *gorigin = PG_GETARG_GSERIALIZED_P(1);
+ GSERIALIZED *gsqr;
+ int cell_i = PG_GETARG_INT32(2);
+ int cell_j = PG_GETARG_INT32(3);
+ LWGEOM *lwsqr;
+ LWGEOM *lworigin = lwgeom_from_gserialized(gorigin);
+
+ if (lwgeom_is_empty(lworigin))
+ {
+ elog(ERROR, "%s: origin point is empty", __func__);
+ PG_RETURN_NULL();
+ }
+
+ lwpt = lwgeom_as_lwpoint(lworigin);
+ if (!lwpt)
+ {
+ elog(ERROR, "%s: origin argument is not a point", __func__);
+ PG_RETURN_NULL();
+ }
+
+ lwsqr = square(lwpoint_get_x(lwpt), lwpoint_get_y(lwpt),
+ size, cell_i, cell_j,
+ lwgeom_get_srid(lworigin));
+
+ gsqr = geometry_serialize(lwsqr);
+ PG_FREE_IF_COPY(gorigin, 1);
+ PG_RETURN_POINTER(gsqr);
+}
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 1dc1b1e..773ecb6 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -5991,6 +5991,46 @@ CREATE OR REPLACE FUNCTION ST_InterpolatePoint(Line geometry, Point geometry)
LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
_COST_MEDIUM;
+---------------------------------------------------------------
+-- Grid / Hexagon coverage functions
+---
+
+-- Availability: 3.0.0
+CREATE OR REPLACE FUNCTION ST_Hexagon(size float8, origin geometry DEFAULT 'POINT(0 0)', cell_i int4 DEFAULT 0, cell_j int4 DEFAULT 0)
+ RETURNS geometry
+ AS 'MODULE_PATHNAME', 'ST_Hexagon'
+ LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
+ _COST_MEDIUM;
+
+-- Availability: 3.0.0
+CREATE OR REPLACE FUNCTION ST_Square(size float8, origin geometry DEFAULT 'POINT(0 0)', cell_i int4 DEFAULT 0, cell_j int4 DEFAULT 0)
+ RETURNS geometry
+ AS 'MODULE_PATHNAME', 'ST_Square'
+ LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
+ _COST_MEDIUM;
+
+-- Availability: 1.0.0
+CREATE TYPE geometry_grid AS (
+ geom geometry,
+ i integer,
+ j integer
+);
+
+-- Availability: 3.0.0
+CREATE OR REPLACE FUNCTION ST_HexagonGrid(size float8 DEFAULT 1.0, bounds geometry DEFAULT 'LINESTRING(0 0, 100 100)')
+ RETURNS SETOF geometry_grid
+ AS 'MODULE_PATHNAME', 'ST_ShapeGrid'
+ LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
+ _COST_MEDIUM;
+
+-- Availability: 3.0.0
+CREATE OR REPLACE FUNCTION ST_SquareGrid(size float8 DEFAULT 1.0, bounds geometry DEFAULT 'LINESTRING(0 0, 100 100)')
+ RETURNS SETOF geometry_grid
+ AS 'MODULE_PATHNAME', 'ST_ShapeGrid'
+ LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
+ _COST_MEDIUM;
+
+
-- moved to separate file cause its invovled
#include "postgis_brin.sql.in"
-----------------------------------------------------------------------
hooks/post-receive
--
PostGIS; Spatial objects for PostgreSQL.
More information about the postgis-tickets
mailing list