PostGIS  2.4.9dev-r@@SVN_REVISION@@
cu_lwstroke.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * Copyright (C) 2017 Sandro Santilli <strk@kbt.io>
7  *
8  * This is free software; you can redistribute and/or modify it under
9  * the terms of the GNU General Public Licence. See the COPYING file.
10  *
11  **********************************************************************/
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <math.h> /* for M_PI */
17 #include "CUnit/Basic.h"
18 #include "CUnit/CUnit.h"
19 
20 #include "liblwgeom_internal.h"
21 #include "cu_tester.h"
22 
23 /* #define SKIP_TEST_RETAIN_ANGLE 1 */
24 
25 
26 static LWGEOM* lwgeom_from_text(const char *str)
27 {
29  if( LW_FAILURE == lwgeom_parse_wkt(&r, (char*)str, LW_PARSER_CHECK_NONE) )
30  return NULL;
31  return r.geom;
32 }
33 
34 static char* lwgeom_to_text(const LWGEOM *geom, int prec)
35 {
36  gridspec grid;
37  LWGEOM *gridded;
38  char *wkt;
39 
40  memset(&grid, 0, sizeof(gridspec));
41  grid.xsize = prec;
42  grid.ysize = prec;
43  gridded = lwgeom_grid(geom, &grid);
44 
45  wkt = lwgeom_to_wkt(gridded, WKT_ISO, 15, NULL);
46  lwgeom_free(gridded);
47  return wkt;
48 }
49 
50 static void test_lwcurve_linearize(void)
51 {
52  LWGEOM *in;
53  LWGEOM *out, *out2;
54  char *str;
55  int toltype;
56 
57  /***********************************************************
58  *
59  * Segments per quadrant tolerance type
60  *
61  ***********************************************************/
62 
64 
65  /* 2 quadrants arc (180 degrees, PI radians) */
66  in = lwgeom_from_text("CIRCULARSTRING(0 0,100 100,200 0)");
67  /* 2 segment per quadrant */
68  out = lwcurve_linearize(in, 2, toltype, 0);
69  str = lwgeom_to_text(out, 2);
70  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,30 70,100 100,170 70,200 0)");
71  lwfree(str);
72  lwgeom_free(out);
73  /* 3 segment per quadrant */
74  out = lwcurve_linearize(in, 3, toltype, 0);
75  str = lwgeom_to_text(out, 2);
76  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,14 50,50 86,100 100,150 86,186 50,200 0)");
77  lwfree(str);
78  lwgeom_free(out);
79  /* 3.5 segment per quadrant (invalid) */
81  out = lwcurve_linearize(in, 3.5, toltype, 0);
82  CU_ASSERT( out == NULL );
83  ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: segments per quadrant must be an integer value, got 3.5");
84  lwgeom_free(out);
85  /* -2 segment per quadrant (invalid) */
87  out = lwcurve_linearize(in, -2, toltype, 0);
88  CU_ASSERT( out == NULL );
89  ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: segments per quadrant must be at least 1, got -2");
90  lwgeom_free(out);
91  /* 0 segment per quadrant (invalid) */
93  out = lwcurve_linearize(in, 0, toltype, 0);
94  CU_ASSERT( out == NULL );
95  ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: segments per quadrant must be at least 1, got 0");
96  lwgeom_free(out);
97  lwgeom_free(in);
98 
99  /* 1.5 quadrants arc (145 degrees - PI*3/4 radians ) */
100  in = lwgeom_from_text("CIRCULARSTRING(29.2893218813453 70.7106781186548,100 100,200 0)");
101  /* 2 segment per quadrant */
102  out = lwcurve_linearize(in, 2, toltype, 0);
103  str = lwgeom_to_text(out, 2);
104  ASSERT_STRING_EQUAL(str, "LINESTRING(30 70,100 100,170 70,200 0)");
105  lwfree(str);
106  lwgeom_free(out);
107  /* 3 segment per quadrant - non-symmetric */
108  out = lwcurve_linearize(in, 3, toltype, 0);
109  str = lwgeom_to_text(out, 2);
110  ASSERT_STRING_EQUAL(str, "LINESTRING(30 70,74 96,126 96,170 70,196 26,200 0)");
111  lwfree(str);
112  lwgeom_free(out);
113 
114  /* 3 segment per quadrant - symmetric */
115  out = lwcurve_linearize(in, 3, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
116  str = lwgeom_to_text(out, 2);
117  ASSERT_STRING_EQUAL(str, "LINESTRING(30 70,70 96,116 98,158 80,190 46,200 0)");
118  lwfree(str);
119  lwgeom_free(out);
120 
121 #ifndef SKIP_TEST_RETAIN_ANGLE
122  /* 3 segment per quadrant - symmetric/retain_angle */
123  out = lwcurve_linearize(in, 3, toltype,
126  );
127  str = lwgeom_to_text(out, 2);
128  ASSERT_STRING_EQUAL(str, "LINESTRING(30 70,40 80,86 100,138 92,180 60,200 14,200 0)");
129  lwfree(str);
130  lwgeom_free(out);
131 #endif /* SKIP_TEST_RETAIN_ANGLE */
132 
133  lwgeom_free(in);
134 
135  /***********************************************************
136  *
137  * Maximum deviation tolerance type
138  *
139  ***********************************************************/
140 
142 
143  in = lwgeom_from_text("CIRCULARSTRING(0 0,100 100,200 0)");
144 
145  /* Maximum of 10 units of difference, asymmetric */
146  out = lwcurve_linearize(in, 10, toltype, 0);
147  str = lwgeom_to_text(out, 2);
148  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,38 78,124 98,190 42,200 0)");
149  lwfree(str);
150  lwgeom_free(out);
151  /* Maximum of 0 units of difference (invalid) */
153  out = lwcurve_linearize(in, 0, toltype, 0);
154  CU_ASSERT( out == NULL );
155  ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max deviation must be bigger than 0, got 0");
156  /* Maximum of -2 units of difference (invalid) */
158  out = lwcurve_linearize(in, -2, toltype, 0);
159  CU_ASSERT( out == NULL );
160  ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max deviation must be bigger than 0, got -2");
161  /* Maximum of 10 units of difference, symmetric */
162  out = lwcurve_linearize(in, 10, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
163  str = lwgeom_to_text(out, 2);
164  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,30 70,100 100,170 70,200 0)");
165  lwfree(str);
166  lwgeom_free(out);
167  /* Maximum of 20 units of difference, asymmetric */
168  out = lwcurve_linearize(in, 20, toltype, 0);
169  str = lwgeom_to_text(out, 2);
170  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,72 96,184 54,200 0)");
171  lwfree(str);
172  lwgeom_free(out);
173  /* Maximum of 20 units of difference, symmetric */
174  out = lwcurve_linearize(in, 20, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
175  str = lwgeom_to_text(out, 2);
176  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,50 86,150 86,200 0)");
177  lwfree(str);
178  lwgeom_free(out);
179 
180 #ifndef SKIP_TEST_RETAIN_ANGLE
181  /* Maximum of 20 units of difference, symmetric/retain angle */
182  out = lwcurve_linearize(in, 20, toltype,
185  );
186  str = lwgeom_to_text(out, 2);
187  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,4 28,100 100,196 28,200 0)");
188  lwfree(str);
189  lwgeom_free(out);
190 #endif /* SKIP_TEST_RETAIN_ANGLE */
191 
192  lwgeom_free(in);
193 
194  /*
195  * Clockwise ~90 degrees south-west to north-west quadrants
196  * starting at ~22 degrees west of vertical line
197  *
198  * See https://trac.osgeo.org/postgis/ticket/3772
199  */
201  in = lwgeom_from_text("CIRCULARSTRING(71.96 -65.64,22.2 -18.52,20 50)");
202 
203  /* 4 units of max deviation - symmetric */
204  out = lwcurve_linearize(in, 4, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
205  str = lwgeom_to_text(out, 2);
206  ASSERT_STRING_EQUAL(str, "LINESTRING(72 -66,34 -38,16 4,20 50)");
207  lwfree(str);
208  lwgeom_free(out);
209  lwgeom_free(in);
210 
211  /*
212  * Clockwise ~90 degrees north-west to south-west quadrants
213  * starting at ~22 degrees west of vertical line
214  *
215  * See https://trac.osgeo.org/postgis/ticket/3772
216  */
218  in = lwgeom_from_text("CIRCULARSTRING(20 50,22.2 -18.52,71.96 -65.64)");
219 
220  /* 4 units of max deviation - symmetric */
221  out = lwcurve_linearize(in, 4, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
222  str = lwgeom_to_text(out, 2);
223  ASSERT_STRING_EQUAL(str, "LINESTRING(20 50,16 4,34 -38,72 -66)");
224  lwfree(str);
225  lwgeom_free(out);
226 
227  /* max deviation bigger than twice the radius
228  * we really only want to make sure NOT to enter
229  * an infinite loop here.
230  * See https://trac.osgeo.org/postgis/ticket/4031
231  */
232  out = lwcurve_linearize(in, 500, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
233  str = lwgeom_to_text(out, 2);
234  ASSERT_STRING_EQUAL(str, "LINESTRING(20 50,72 -66)");
235  lwfree(str);
236  lwgeom_free(out);
237 
238  lwgeom_free(in);
239 
240  /*
241  * ROBUSTNESS: big radius, small tolerance
242  * See https://trac.osgeo.org/postgis/ticket/4058
243  * NOTE: we are really only interested in not enterying
244  * an infinite loop here
245  */
247  in = lwgeom_from_text("CIRCULARSTRING("
248  "2696000.553 1125699.831999999936670, "
249  "2695950.552000000141561 1125749.833000000100583, "
250  "2695865.195999999996275 1125835.189000)");
251  out = lwcurve_linearize(in, 0.0001, toltype, 0);
252  str = lwgeom_to_text(out, 2);
253  ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695866 1125836)");
254  lwfree(str);
255  lwgeom_free(out);
256  out = lwcurve_linearize(in, 0.0001, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
257  str = lwgeom_to_text(out, 2);
258  ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695866 1125836)");
259  lwfree(str);
260  lwgeom_free(out);
261 #ifndef SKIP_TEST_RETAIN_ANGLE
262  out = lwcurve_linearize(in, 0.0001, toltype,
265  );
266  str = lwgeom_to_text(out, 2);
267  ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695932 1125768,2695866 1125836)");
268  lwfree(str);
269  lwgeom_free(out);
270 #endif /* SKIP_TEST_RETAIN_ANGLE */
271  lwgeom_free(in);
272 
273  /***********************************************************
274  *
275  * Maximum angle tolerance type
276  *
277  ***********************************************************/
278 
280 
281  in = lwgeom_from_text("CIRCULARSTRING(0 0,100 100,200 0)");
282 
283  /* Maximum of 45 degrees, asymmetric */
284  out = lwcurve_linearize(in, M_PI / 4.0, toltype, 0);
285  str = lwgeom_to_text(out, 2);
286  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,30 70,100 100,170 70,200 0)");
287  lwfree(str);
288  lwgeom_free(out);
289  /* Maximum of 0 degrees (invalid) */
291  out = lwcurve_linearize(in, 0, toltype, 0);
292  CU_ASSERT( out == NULL );
293  ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max angle must be bigger than 0, got 0");
294  /* Maximum of -2 degrees (invalid) */
296  out = lwcurve_linearize(in, -2, toltype, 0);
297  CU_ASSERT( out == NULL );
298  ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max angle must be bigger than 0, got -2");
299  /* Maximum of 360 degrees, just return endpoints... */
301  out = lwcurve_linearize(in, M_PI*4, toltype, 0);
302  str = lwgeom_to_text(out, 2);
303  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,200 0)");
304  lwfree(str);
305  lwgeom_free(out);
306  /* Maximum of 70 degrees, asymmetric */
307  out = lwcurve_linearize(in, 70 * M_PI / 180, toltype, 0);
308  str = lwgeom_to_text(out, 2);
309  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,66 94,176 64,200 0)");
310  lwfree(str);
311  lwgeom_free(out);
312  /* Maximum of 70 degrees, symmetric */
313  out = lwcurve_linearize(in, 70 * M_PI / 180, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
314  str = lwgeom_to_text(out, 2);
315  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,50 86,150 86,200 0)");
316  lwfree(str);
317  lwgeom_free(out);
318 
319 #ifndef SKIP_TEST_RETAIN_ANGLE
320  /* Maximum of 70 degrees, symmetric/retain angle */
321  out = lwcurve_linearize(in, 70 * M_PI / 180, toltype,
324  );
325  str = lwgeom_to_text(out, 2);
326  ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,6 34,100 100,194 34,200 0)");
327  lwfree(str);
328  lwgeom_free(out);
329 #endif /* SKIP_TEST_RETAIN_ANGLE */
330 
331  lwgeom_free(in);
332 
333  /***********************************************************
334  *
335  * Direction neutrality
336  *
337  ***********************************************************/
338 
339  in = lwgeom_from_text("CIRCULARSTRING(71.96 -65.64,22.2 -18.52,20 50)");
340  out = lwcurve_linearize(in, M_PI/4.0,
343  lwgeom_reverse(in);
344  out2 = lwcurve_linearize(in, M_PI/4.0,
347  lwgeom_reverse(out2);
348  if ( ! lwgeom_same(out, out2) )
349  {
350  fprintf(stderr, "linearization is not direction neutral:\n");
351  str = lwgeom_to_wkt(out, WKT_ISO, 18, NULL);
352  fprintf(stderr, "OUT1: %s\n", str);
353  lwfree(str);
354  str = lwgeom_to_wkt(out2, WKT_ISO, 18, NULL);
355  fprintf(stderr, "OUT2: %s\n", str);
356  lwfree(str);
357  }
358  CU_ASSERT( lwgeom_same(out, out2) );
359  lwgeom_free(out2);
360  lwgeom_free(out);
361  lwgeom_free(in);
362 }
363 
364 /*
365 ** Used by the test harness to register the tests in this file.
366 */
367 void lwstroke_suite_setup(void);
369 {
370  CU_pSuite suite = CU_add_suite("lwstroke", NULL, NULL);
372 }
LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid)
Definition: lwgeom.c:1912
Tolerance expresses the maximum angle between the radii generating approximation line vertices...
Definition: liblwgeom.h:2217
char * r
Definition: cu_in_wkt.c:24
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition: lwout_wkt.c:669
void lwfree(void *mem)
Definition: lwutil.c:244
#define ASSERT_STRING_EQUAL(o, e)
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1099
Tolerance expresses the maximum distance between an arbitrary point on the curve and the closest poin...
Definition: liblwgeom.h:2209
void lwstroke_suite_setup(void)
Definition: cu_lwstroke.c:368
static char * lwgeom_to_text(const LWGEOM *geom, int prec)
Definition: cu_lwstroke.c:34
#define LW_FAILURE
Definition: liblwgeom.h:79
#define LW_PARSER_CHECK_NONE
Definition: liblwgeom.h:2013
#define WKT_ISO
Definition: liblwgeom.h:2083
int lwgeom_parse_wkt(LWGEOM_PARSER_RESULT *parser_result, char *wktstr, int parse_flags)
Parse a WKT geometry string into an LWGEOM structure.
void cu_error_msg_reset()
static void test_lwcurve_linearize(void)
Definition: cu_lwstroke.c:50
Parser result structure: returns the result of attempting to convert (E)WKT/(E)WKB to LWGEOM...
Definition: liblwgeom.h:2020
#define PG_ADD_TEST(suite, testfunc)
static LWGEOM * lwgeom_from_text(const char *str)
Definition: cu_lwstroke.c:26
char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
geom1 same as geom2 iff
Definition: lwgeom.c:544
Symmetric linearization means that the output vertices would be the same no matter the order of the p...
Definition: liblwgeom.h:2226
void lwgeom_reverse(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition: lwgeom.c:93
Tolerance expresses the number of segments to use for each quarter of circle (quadrant).
Definition: liblwgeom.h:2202
Retain angle instructs the engine to try its best to retain the requested angle between generating ra...
Definition: liblwgeom.h:2246
char cu_error_msg[MAX_CUNIT_ERROR_LENGTH+1]
LWGEOM * lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:734
Snap to grid.