[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