[SCM] PostGIS branch master updated. 3.7.0beta2-95-g2d730fa21
git at osgeo.org
git at osgeo.org
Fri Aug 21 05:19:54 PDT 2026
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".
The branch, master has been updated
via 2d730fa218e1c2af3883359a82b6581b5bc917ce (commit)
via 02b1be58d5dcde9654f5f828459f72a47cd45c3c (commit)
via b4c83952e0a6f4830209cd09a118eb58fd307899 (commit)
via 878b8d1adf1861f7abe69fbb11d163a330e33609 (commit)
from 7b8342a25554a44b83feaff2904fd7addd530d8f (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 2d730fa218e1c2af3883359a82b6581b5bc917ce
Merge: 7b8342a25 02b1be58d
Author: Darafei Praliaskouski <komzpa at gmail.com>
Date: Fri Aug 21 05:19:53 2026 -0700
Merge pull request 'liblwgeom: stream NURBS bbox Bezier extraction' (!755) from Komzpa/postgis:fix/nurbs-gbox-work-budget-547330051 into master
## Summary
- Replace whole-curve NURBS knot insertion in bbox calculation with streaming local Bezier extraction.
- Preserve per-span bbox calculation while avoiding repeated full-array copies for large valid curves.
- Add bbox regressions for rational Z/M, unclamped knots, and high-degree implicit-knot NURBS.
Closes https://issues.oss-fuzz.com/issues/547330051
## Tests
- `make -C liblwgeom`
- `make -C liblwgeom/cunit check` (`376` tests, `5940` asserts)
- targeted `gserialized_from_bytea_fuzzer` dummy runner: exact 56-byte testcase, 100 iterations, `elapsed=1.49 exit=0`
- baseline dummy runner on the same testcase/loop timed out at 20 s (`exit=124`)
- `git clang-format --extensions c,h upstream/master --diff`
Reviewed-on: https://gitea.osgeo.org/postgis/postgis/pulls/755
commit 02b1be58d5dcde9654f5f828459f72a47cd45c3c
Author: Darafei Praliaskouski <me at komzpa.net>
Date: Fri Aug 21 16:18:54 2026 +0400
liblwgeom: document streaming NURBS bbox extraction
diff --git a/liblwgeom/gbox.c b/liblwgeom/gbox.c
index 6a210d9a3..d29541a4a 100644
--- a/liblwgeom/gbox.c
+++ b/liblwgeom/gbox.c
@@ -1030,8 +1030,10 @@ lwnurbscurve_calculate_gbox_cartesian(const LWNURBSCURVE *curve, GBOX *gbox)
lwnurbscurve_gbox_init(gbox, lwflags(FLAGS_GET_Z(curve->flags), FLAGS_GET_M(curve->flags), 0));
/* Stream the standard B-spline to Bezier decomposition with one current
- * and one next control net. This preserves the exact span geometry while
- * avoiding whole-curve knot refinement and its quadratic memory churn. */
+ * and one next control net. This is Boehm knot insertion arranged so the
+ * insertions for one knot block are applied only to the local control net,
+ * preserving the exact span geometry while avoiding whole-curve knot
+ * refinement and its quadratic memory churn. */
{
NURBS_BBOX_HPOINT bezier[degree + 1];
NURBS_BBOX_HPOINT next_bezier[degree + 1];
@@ -1048,6 +1050,8 @@ lwnurbscurve_calculate_gbox_cartesian(const LWNURBSCURVE *curve, GBOX *gbox)
{
uint32_t knot_block_start = b;
+ /* Exact comparison is intentional: these values come directly from
+ * the validated knot vector or its generated uniform replacement. */
while (b < m && knots[b + 1] == knots[b])
b++;
@@ -1056,6 +1060,9 @@ lwnurbscurve_calculate_gbox_cartesian(const LWNURBSCURVE *curve, GBOX *gbox)
{
double numer = knots[b] - knots[a];
+ /* Precompute the knot-insertion blend factors for this block.
+ * A zero denominator would mean the validated active span has
+ * collapsed, so fail instead of feeding NaNs to the bbox code. */
for (j = degree; j > multiplicity; j--)
{
double denom = knots[a + j] - knots[a];
@@ -1074,6 +1081,9 @@ lwnurbscurve_calculate_gbox_cartesian(const LWNURBSCURVE *curve, GBOX *gbox)
{
uint32_t save = r - j;
+ /* Each pass raises the current knot multiplicity by one.
+ * The rightmost point saved on every pass becomes the left
+ * side of the next span's control net. */
s = multiplicity + j;
for (i = degree; i >= s; i--)
{
@@ -1086,6 +1096,9 @@ lwnurbscurve_calculate_gbox_cartesian(const LWNURBSCURVE *curve, GBOX *gbox)
}
}
+ /* Only bbox spans inside the NURBS active parameter domain
+ * [U[p], U[n]]. Unclamped explicit knot vectors can contain valid
+ * knots before or after that domain, and those must not contribute. */
if (knots[b] > knots[a] && knots[a] >= domain_min && knots[b] <= domain_max)
{
if (lwnurbscurve_add_bezier_span_gbox(bezier, degree, gbox->flags, 0, gbox) ==
@@ -1100,6 +1113,8 @@ lwnurbscurve_calculate_gbox_cartesian(const LWNURBSCURVE *curve, GBOX *gbox)
if (knots[b] >= domain_max || b >= m)
break;
+ /* Complete the next span from the saved right edge and the untouched
+ * original control points that are entering the moving local window. */
for (i = degree - FP_MIN(multiplicity, degree); i <= degree; i++)
lwnurbscurve_get_hpoint(curve, b - degree + i, &next_bezier[i]);
commit b4c83952e0a6f4830209cd09a118eb58fd307899
Author: Darafei Praliaskouski <me at komzpa.net>
Date: Fri Aug 21 16:10:46 2026 +0400
NEWS: document NURBS bbox extraction fix
diff --git a/NEWS b/NEWS
index 03f7d58f6..63e6b127a 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,9 @@ These are only changes since 3.7.0beta2.
- Codex security scan, guard the recursive NURBSCURVE bounding box
against non-finite control points to stop a backend CPU denial
of service reachable from WKT input (Darafei Praliaskouski)
+ - GT-755, OSSFuzz 547330051, stream NURBSCURVE bounding box Bezier extraction
+ to avoid repeated full-array knot insertion on large valid curves
+ (Darafei Praliaskouski)
- Fix geometry input parsing to stop malformed SRID prefixes before the
end of the input string
(Dennis Tighe, Google)
commit 878b8d1adf1861f7abe69fbb11d163a330e33609
Author: Darafei Praliaskouski <me at komzpa.net>
Date: Fri Aug 21 15:18:01 2026 +0400
liblwgeom: stream NURBS bbox Bezier extraction
Replace whole-curve NURBS knot insertion for bbox calculation with a streaming Bezier decomposition that only keeps the current and next local control nets. This preserves the exact span bbox contract while avoiding repeated full-array copies for large valid curves.
Credit to OSS-Fuzz.
OSS-Fuzz: https://issues.oss-fuzz.com/issues/547330051
diff --git a/liblwgeom/cunit/cu_gserialized1.c b/liblwgeom/cunit/cu_gserialized1.c
index 374fec6bf..c31228c75 100644
--- a/liblwgeom/cunit/cu_gserialized1.c
+++ b/liblwgeom/cunit/cu_gserialized1.c
@@ -196,6 +196,17 @@ static void test_lwgeom_calculate_gbox(void)
CU_ASSERT_EQUAL(next_float_up(b.ymax), next_float_up(0.75));
lwgeom_free(g);
+ /* The same rational span logic must honor Z and M ordinates. */
+ g = lwgeom_from_wkt("NURBSCURVE ZM(2, (0 0 0 0, 1 1 1 2, 2 0 0 0), (1, 3, 1), (0, 0, 0, 1, 1, 1))",
+ LW_PARSER_CHECK_NONE);
+ lwgeom_calculate_gbox_cartesian(g, &b);
+ CU_ASSERT_DOUBLE_EQUAL(b.xmin, 0.0, 0.0000001);
+ CU_ASSERT_DOUBLE_EQUAL(b.xmax, 2.0, 0.0000001);
+ CU_ASSERT_EQUAL(next_float_up(b.ymax), next_float_up(0.75));
+ CU_ASSERT_EQUAL(next_float_up(b.zmax), next_float_up(0.75));
+ CU_ASSERT_EQUAL(next_float_up(b.mmax), next_float_up(1.5));
+ lwgeom_free(g);
+
/* Repeated knots split the curve into independent Bezier spans. */
g = lwgeom_from_wkt("NURBSCURVE(2, (0 0, 1 1, 2 0, 3 1, 4 0), (1, 1, 1, 1, 1), (0, 0, 0, 0.5, 0.5, 1, 1, 1))",
LW_PARSER_CHECK_NONE);
@@ -205,6 +216,26 @@ static void test_lwgeom_calculate_gbox(void)
CU_ASSERT_EQUAL(next_float_up(b.ymax), next_float_up(0.5));
lwgeom_free(g);
+ /* An explicit unclamped knot vector only exposes the active parameter
+ * domain, so local extraction must not emit off-domain spans. */
+ g = lwgeom_from_wkt("NURBSCURVE(2, (0 0, 1 1, 2 0), (1, 1, 1), (0, 1, 2, 3, 4, 5))", LW_PARSER_CHECK_NONE);
+ lwgeom_calculate_gbox_cartesian(g, &b);
+ CU_ASSERT_DOUBLE_EQUAL(b.xmin, 0.0, 0.0000001);
+ CU_ASSERT_DOUBLE_EQUAL(b.ymin, 0.0, 0.0000001);
+ CU_ASSERT_DOUBLE_EQUAL(b.xmax, 1.5, 0.0000001);
+ CU_ASSERT_EQUAL(next_float_up(b.ymax), next_float_up(2.0 / 3.0));
+ lwgeom_free(g);
+
+ /* A high-degree implicit-knot NURBS must keep the exact span bbox rather
+ * than falling back to the control-point hull ymax of 100. */
+ g = lwgeom_from_wkt("NURBSCURVE(6, (0 0, 1 100, 2 100, 3 100, 4 100, 5 100, 6 0))", LW_PARSER_CHECK_NONE);
+ lwgeom_calculate_gbox_cartesian(g, &b);
+ CU_ASSERT_DOUBLE_EQUAL(b.xmin, 0.0, 0.0000001);
+ CU_ASSERT_DOUBLE_EQUAL(b.xmax, 6.0, 0.0000001);
+ CU_ASSERT_EQUAL(next_float_up(b.ymax), next_float_up(96.875));
+ CU_ASSERT(b.ymax < 100.0);
+ lwgeom_free(g);
+
/* A non-finite control point coordinate must not make the recursive
* Bezier-subdivision bbox spin: NaN never compares equal to itself, so
* the stop tests in lwnurbscurve_add_bezier_span_gbox() could not
diff --git a/liblwgeom/gbox.c b/liblwgeom/gbox.c
index 13ebedfa4..6a210d9a3 100644
--- a/liblwgeom/gbox.c
+++ b/liblwgeom/gbox.c
@@ -37,14 +37,6 @@ typedef struct {
double w;
} NURBS_BBOX_HPOINT;
-typedef struct {
- uint32_t degree;
- uint32_t npoints;
- uint32_t nknots;
- NURBS_BBOX_HPOINT *points;
- double *knots;
-} NURBS_BBOX_WORK;
-
GBOX* gbox_new(lwflags_t flags)
{
GBOX *g = (GBOX*)lwalloc(sizeof(GBOX));
@@ -740,6 +732,25 @@ lwnurbscurve_hpoint_lerp(const NURBS_BBOX_HPOINT *a, const NURBS_BBOX_HPOINT *b,
return out;
}
+/**
+ * Loads one control point in homogeneous coordinates, applying the implicit
+ * weight 1.0 for polynomial curves so Bezier extraction can use the same
+ * arithmetic for rational and non-rational spans.
+ */
+static void
+lwnurbscurve_get_hpoint(const LWNURBSCURVE *curve, uint32_t index, NURBS_BBOX_HPOINT *hpoint)
+{
+ POINT4D point;
+ double weight = (curve->weights && index < curve->nweights) ? curve->weights[index] : 1.0;
+
+ getPoint4d_p(curve->points, index, &point);
+ hpoint->x = point.x * weight;
+ hpoint->y = point.y * weight;
+ hpoint->z = FLAGS_GET_Z(curve->flags) ? point.z * weight : 0.0;
+ hpoint->m = FLAGS_GET_M(curve->flags) ? point.m * weight : 0.0;
+ hpoint->w = weight;
+}
+
/**
* Expands a Cartesian box in each enabled dimension, keeping dimensionality
* decisions out of the projection and subdivision helpers.
@@ -971,217 +982,6 @@ lwnurbscurve_add_bezier_span_gbox(const NURBS_BBOX_HPOINT *points,
return LW_SUCCESS;
}
-/**
- * Counts exact copies of a knot value. Exact comparison is intentional: this
- * code queries values already present in, or copied directly into, the vector.
- */
-static uint32_t
-lwnurbscurve_knot_multiplicity(const double *knots, uint32_t nknots, double knot)
-{
- uint32_t i, multiplicity = 0;
-
- for (i = 0; i < nknots; i++)
- {
- if (knots[i] == knot)
- multiplicity++;
- }
-
- return multiplicity;
-}
-
-/**
- * Finds the half-open knot span [U[i], U[i + 1]) used by knot insertion,
- * clamping either domain endpoint to its valid boundary span. The work knot
- * vector has already been validated as nondecreasing.
- */
-static uint32_t
-lwnurbscurve_gbox_find_span(const NURBS_BBOX_WORK *work, double knot)
-{
- uint32_t npoints = work->npoints;
- uint32_t degree = work->degree;
- uint32_t low, high, mid;
-
- if (knot >= work->knots[npoints])
- return npoints - 1;
- if (knot <= work->knots[degree])
- return degree;
-
- low = degree;
- high = npoints;
- mid = (low + high) / 2;
- while (knot < work->knots[mid] || knot >= work->knots[mid + 1])
- {
- if (knot < work->knots[mid])
- high = mid;
- else
- low = mid;
- mid = (low + high) / 2;
- }
-
- return mid;
-}
-
-/**
- * Inserts one knot into the homogeneous B-spline work arrays while preserving
- * the curve, allowing its spans to be converted into Bezier form.
- */
-static int
-lwnurbscurve_insert_knot_once(NURBS_BBOX_WORK *work, double knot)
-{
- uint32_t i;
- uint32_t degree = work->degree;
- uint32_t n = work->npoints - 1;
- uint32_t k = lwnurbscurve_gbox_find_span(work, knot);
- uint32_t s = lwnurbscurve_knot_multiplicity(work->knots, work->nknots, knot);
- NURBS_BBOX_HPOINT *points;
- double *knots;
-
- /* Boehm knot insertion in homogeneous coordinates preserves the rational
- * curve while replacing the affected control points by convex blends. */
- if (s > degree)
- return LW_FAILURE;
-
- points = lwalloc(sizeof(NURBS_BBOX_HPOINT) * (work->npoints + 1));
- knots = lwalloc(sizeof(double) * (work->nknots + 1));
-
- for (i = 0; i <= k; i++)
- knots[i] = work->knots[i];
- knots[k + 1] = knot;
- for (i = k + 1; i < work->nknots; i++)
- knots[i + 1] = work->knots[i];
-
- for (i = 0; i <= k - degree; i++)
- points[i] = work->points[i];
- for (i = k - s + 1; i <= n + 1; i++)
- points[i] = work->points[i - 1];
-
- for (i = k - degree + 1; i <= k - s; i++)
- {
- double denom = work->knots[i + degree] - work->knots[i];
- double alpha;
-
- if (denom == 0.0)
- {
- lwfree(points);
- lwfree(knots);
- return LW_FAILURE;
- }
-
- alpha = (knot - work->knots[i]) / denom;
- points[i] = lwnurbscurve_hpoint_lerp(&work->points[i - 1], &work->points[i], alpha);
- }
-
- lwfree(work->points);
- lwfree(work->knots);
- work->points = points;
- work->knots = knots;
- work->npoints++;
- work->nknots++;
-
- return LW_SUCCESS;
-}
-
-/**
- * Raises a knot to the requested multiplicity through curve-preserving
- * insertions, preparing domain boundaries and interior Bezier breaks.
- */
-static int
-lwnurbscurve_ensure_knot_multiplicity(NURBS_BBOX_WORK *work, double knot, uint32_t target)
-{
- while (lwnurbscurve_knot_multiplicity(work->knots, work->nknots, knot) < target)
- {
- if (lwnurbscurve_insert_knot_once(work, knot) == LW_FAILURE)
- return LW_FAILURE;
- }
-
- return LW_SUCCESS;
-}
-
-/**
- * Refines the working knot vector into independent Bezier spans, making each
- * non-empty part suitable for conservative convex-hull bounding.
- */
-static int
-lwnurbscurve_make_bezier_work(NURBS_BBOX_WORK *work, double domain_min, double domain_max)
-{
- uint32_t i;
-
- /* A B-spline decomposes into Bezier spans once the domain endpoints have
- * multiplicity p + 1 and every interior knot has multiplicity p. Zero-width
- * spans remain in the knot vector and are skipped by the caller. */
- if (lwnurbscurve_ensure_knot_multiplicity(work, domain_min, work->degree + 1) == LW_FAILURE ||
- lwnurbscurve_ensure_knot_multiplicity(work, domain_max, work->degree + 1) == LW_FAILURE)
- return LW_FAILURE;
-
- for (i = 0; i < work->nknots;)
- {
- double knot = work->knots[i];
- uint32_t multiplicity = 1;
-
- while (i + multiplicity < work->nknots && work->knots[i + multiplicity] == knot)
- multiplicity++;
-
- if (knot > domain_min && knot < domain_max && multiplicity < work->degree)
- {
- if (lwnurbscurve_insert_knot_once(work, knot) == LW_FAILURE)
- return LW_FAILURE;
- i = 0;
- }
- else
- {
- i += multiplicity;
- }
- }
-
- return LW_SUCCESS;
-}
-
-/**
- * Releases the owned arrays used for refinement. Both pointers may be NULL
- * because initialization can fail after allocating only part of the workset.
- */
-static void
-lwnurbscurve_free_work(NURBS_BBOX_WORK *work)
-{
- if (work->points)
- lwfree(work->points);
- if (work->knots)
- lwfree(work->knots);
-}
-
-/**
- * Builds owned homogeneous control-point and knot arrays for bounding, so knot
- * insertion can refine the curve without modifying the input geometry.
- */
-static int
-lwnurbscurve_init_work(const LWNURBSCURVE *curve, NURBS_BBOX_WORK *work)
-{
- uint32_t i;
-
- memset(work, 0, sizeof(NURBS_BBOX_WORK));
- work->degree = curve->degree;
- work->npoints = curve->points->npoints;
- work->knots = lwnurbscurve_get_or_generate_knots(curve, &work->nknots);
- if (!work->knots || work->nknots == 0)
- return LW_FAILURE;
-
- work->points = lwalloc(sizeof(NURBS_BBOX_HPOINT) * work->npoints);
- for (i = 0; i < work->npoints; i++)
- {
- POINT4D point;
- double weight = (curve->weights && i < curve->nweights) ? curve->weights[i] : 1.0;
-
- getPoint4d_p(curve->points, i, &point);
- work->points[i].x = point.x * weight;
- work->points[i].y = point.y * weight;
- work->points[i].z = FLAGS_GET_Z(curve->flags) ? point.z * weight : 0.0;
- work->points[i].m = FLAGS_GET_M(curve->flags) ? point.m * weight : 0.0;
- work->points[i].w = weight;
- }
-
- return LW_SUCCESS;
-}
-
static int lwpoint_calculate_gbox_cartesian(LWPOINT *point, GBOX *gbox)
{
if ( ! point ) return LW_FAILURE;
@@ -1204,42 +1004,114 @@ static int lwline_calculate_gbox_cartesian(LWLINE *line, GBOX *gbox)
static int
lwnurbscurve_calculate_gbox_cartesian(const LWNURBSCURVE *curve, GBOX *gbox)
{
- uint32_t i;
+ uint32_t a, b, degree, i, j, m, multiplicity, npoints, r, s;
int found_span = LW_FALSE;
- double domain_min, domain_max;
- NURBS_BBOX_WORK work;
+ double domain_max, domain_min;
+ double *knots;
+ uint32_t nknots;
if (!curve || !curve->points || curve->points->npoints == 0)
return LW_FAILURE;
- if (lwnurbscurve_init_work(curve, &work) == LW_FAILURE)
+ degree = curve->degree;
+ npoints = curve->points->npoints;
+ knots = lwnurbscurve_get_or_generate_knots(curve, &nknots);
+ if (!knots || nknots == 0)
return LW_FAILURE;
- domain_min = work.knots[work.degree];
- domain_max = work.knots[work.npoints];
- if (domain_max <= domain_min || lwnurbscurve_make_bezier_work(&work, domain_min, domain_max) == LW_FAILURE)
+ domain_min = knots[degree];
+ domain_max = knots[npoints];
+ if (domain_max <= domain_min || nknots != npoints + degree + 1)
{
- lwnurbscurve_free_work(&work);
+ lwfree(knots);
return LW_FAILURE;
}
lwnurbscurve_gbox_init(gbox, lwflags(FLAGS_GET_Z(curve->flags), FLAGS_GET_M(curve->flags), 0));
- for (i = work.degree; i < work.npoints; i++)
+ /* Stream the standard B-spline to Bezier decomposition with one current
+ * and one next control net. This preserves the exact span geometry while
+ * avoiding whole-curve knot refinement and its quadratic memory churn. */
{
- if (work.knots[i] < domain_min || work.knots[i + 1] > domain_max || work.knots[i + 1] <= work.knots[i])
- continue;
+ NURBS_BBOX_HPOINT bezier[degree + 1];
+ NURBS_BBOX_HPOINT next_bezier[degree + 1];
+ double alphas[degree ? degree : 1];
- if (lwnurbscurve_add_bezier_span_gbox(
- &work.points[i - work.degree], work.degree, gbox->flags, 0, gbox) == LW_FAILURE)
+ for (i = 0; i <= degree; i++)
+ lwnurbscurve_get_hpoint(curve, i, &bezier[i]);
+
+ m = npoints + degree;
+ a = degree;
+ b = degree + 1;
+
+ while (b < m)
{
- lwnurbscurve_free_work(&work);
- return LW_FAILURE;
+ uint32_t knot_block_start = b;
+
+ while (b < m && knots[b + 1] == knots[b])
+ b++;
+
+ multiplicity = b - knot_block_start + 1;
+ if (multiplicity < degree)
+ {
+ double numer = knots[b] - knots[a];
+
+ for (j = degree; j > multiplicity; j--)
+ {
+ double denom = knots[a + j] - knots[a];
+
+ if (denom == 0.0)
+ {
+ lwfree(knots);
+ return LW_FAILURE;
+ }
+
+ alphas[j - multiplicity - 1] = numer / denom;
+ }
+
+ r = degree - multiplicity;
+ for (j = 1; j <= r; j++)
+ {
+ uint32_t save = r - j;
+
+ s = multiplicity + j;
+ for (i = degree; i >= s; i--)
+ {
+ double alpha = alphas[i - s];
+ bezier[i] = lwnurbscurve_hpoint_lerp(&bezier[i - 1], &bezier[i], alpha);
+ }
+
+ if (b < m)
+ next_bezier[save] = bezier[degree];
+ }
+ }
+
+ if (knots[b] > knots[a] && knots[a] >= domain_min && knots[b] <= domain_max)
+ {
+ if (lwnurbscurve_add_bezier_span_gbox(bezier, degree, gbox->flags, 0, gbox) ==
+ LW_FAILURE)
+ {
+ lwfree(knots);
+ return LW_FAILURE;
+ }
+ found_span = LW_TRUE;
+ }
+
+ if (knots[b] >= domain_max || b >= m)
+ break;
+
+ for (i = degree - FP_MIN(multiplicity, degree); i <= degree; i++)
+ lwnurbscurve_get_hpoint(curve, b - degree + i, &next_bezier[i]);
+
+ for (i = 0; i <= degree; i++)
+ bezier[i] = next_bezier[i];
+
+ a = b;
+ b++;
}
- found_span = LW_TRUE;
}
- lwnurbscurve_free_work(&work);
+ lwfree(knots);
return found_span ? LW_SUCCESS : LW_FAILURE;
}
-----------------------------------------------------------------------
Summary of changes:
NEWS | 3 +
liblwgeom/cunit/cu_gserialized1.c | 31 ++++
liblwgeom/gbox.c | 359 +++++++++++++-------------------------
3 files changed, 157 insertions(+), 236 deletions(-)
hooks/post-receive
--
PostGIS
More information about the postgis-tickets
mailing list