[postgis-tickets] r16366 - lwgeom_median: Avoid division by zero

Paul Ramsey pramsey at cleverelephant.ca
Fri Jan 26 07:18:40 PST 2018


Author: pramsey
Date: 2018-01-26 07:18:40 -0800 (Fri, 26 Jan 2018)
New Revision: 16366

Modified:
   trunk/liblwgeom/cunit/cu_algorithm.c
   trunk/liblwgeom/lwgeom_median.c
Log:
lwgeom_median: Avoid division by zero
(Raúl Marín Rodríguez)
Closes #3997
Closes https://github.com/postgis/postgis/pull/198


Modified: trunk/liblwgeom/cunit/cu_algorithm.c
===================================================================
--- trunk/liblwgeom/cunit/cu_algorithm.c	2018-01-26 15:10:58 UTC (rev 16365)
+++ trunk/liblwgeom/cunit/cu_algorithm.c	2018-01-26 15:18:40 UTC (rev 16366)
@@ -1122,8 +1122,9 @@
 	LWPOINT* expected_result = NULL;
 	POINT4D actual_pt;
 	POINT4D expected_pt;
+	const double tolerance = FP_TOLERANCE / 10.0;
 
-	LWPOINT* result = lwgeom_median(g, FP_TOLERANCE / 10.0, iter_count, fail_if_not_converged);
+	LWPOINT* result = lwgeom_median(g, tolerance, iter_count, fail_if_not_converged);
 	int passed = LW_FALSE;
 
 	if (expected != NULL)
@@ -1143,10 +1144,10 @@
 		passed = passed && (lwgeom_has_z((LWGEOM*) expected_result) == lwgeom_has_z((LWGEOM*) result));
 		if (!lwgeom_is_empty((LWGEOM*) result))
 		{
-			passed = passed && FP_EQUALS(actual_pt.x, expected_pt.x);
-			passed = passed && FP_EQUALS(actual_pt.y, expected_pt.y);
-			passed = passed && (!lwgeom_has_z((LWGEOM*) expected_result) || FP_EQUALS(actual_pt.z, expected_pt.z));
-			passed = passed && (!lwgeom_has_m((LWGEOM*) expected_result) || FP_EQUALS(actual_pt.m, expected_pt.m));
+			passed = passed && abs(actual_pt.x - expected_pt.x) < tolerance;
+			passed = passed && abs(actual_pt.y - expected_pt.y) < tolerance;
+			passed = passed && (!lwgeom_has_z((LWGEOM*) expected_result) || abs(actual_pt.z - expected_pt.z) < tolerance);
+			passed = passed && (!lwgeom_has_m((LWGEOM*) expected_result) || abs(actual_pt.m - expected_pt.m) < tolerance);
 		}
 		if (!passed)
 			printf("median_test input %s (parsed %s) expected %s got %s\n", input, lwgeom_to_ewkt(g), lwgeom_to_ewkt((LWGEOM*) expected_result), lwgeom_to_ewkt((LWGEOM*) result));
@@ -1229,6 +1230,19 @@
 	/* Unsupported geometry type */
 	do_median_test("POLYGON((1 0,0 1,1 2,2 1,1 0))", NULL, LW_TRUE, 1000);
 	do_median_test("POLYGON((1 0,0 1,1 2,2 1,1 0))", NULL, LW_FALSE, 1000);
+
+	/* Intermediate point included in the set */
+	do_median_test("MULTIPOINT ZM ("
+			"(0 0 20000 0.5),"
+			"(0 0 59000 0.5),"
+			"(0 48000 -20000 1.3),"
+			"(0 -48000 -20000 1.3),"
+			"(0 -3000 -3472.22222222222262644208967685699462890625 1),"
+			"(0 3000 3472.22222222222262644208967685699462890625 1),"
+			"(0 0 -1644.736842105263121993630193173885345458984375 1),"
+			"(0 0 1644.736842105263121993630193173885345458984375 1)"
+			")",
+		"POINT (0 0 0)", LW_TRUE, 296);
 }
 
 static void test_point_density(void)

Modified: trunk/liblwgeom/lwgeom_median.c
===================================================================
--- trunk/liblwgeom/lwgeom_median.c	2018-01-26 15:10:58 UTC (rev 16365)
+++ trunk/liblwgeom/lwgeom_median.c	2018-01-26 15:18:40 UTC (rev 16366)
@@ -94,7 +94,7 @@
 			double dx = 0;
 			double dy = 0;
 			double dz = 0;
-			double r_inv;
+			double d_sqr;
 			for (i = 0; i < npoints; i++)
 			{
 				if (distances[i] > DBL_EPSILON)
@@ -105,11 +105,15 @@
 				}
 			}
 
-			r_inv = 1.0 / sqrt ( dx*dx + dy*dy + dz*dz );
-
-			next.x = FP_MAX(0, 1.0 - r_inv)*next.x + FP_MIN(1.0, r_inv)*curr->x;
-			next.y = FP_MAX(0, 1.0 - r_inv)*next.y + FP_MIN(1.0, r_inv)*curr->y;
-			next.z = FP_MAX(0, 1.0 - r_inv)*next.z + FP_MIN(1.0, r_inv)*curr->z;
+			d_sqr = sqrt(dx*dx + dy*dy + dz*dz);
+			/* Avoid division by zero if the intermediate point is the median */
+			if (d_sqr > DBL_EPSILON)
+			{
+				double r_inv = FP_MAX(0, 1.0 / d_sqr);
+				next.x = (1.0 - r_inv)*next.x + r_inv*curr->x;
+				next.y = (1.0 - r_inv)*next.y + r_inv*curr->y;
+				next.z = (1.0 - r_inv)*next.z + r_inv*curr->z;
+			}
 		}
 
 		delta = distance3d_pt_pt(curr, &next);



More information about the postgis-tickets mailing list