Coverage Report

Created: 2026-10-07 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/lwin_wkt.c
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * PostGIS - Spatial Types for PostgreSQL
4
 * http://postgis.net
5
 *
6
 * PostGIS is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * PostGIS is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
 **********************************************************************
20
 *
21
 * Copyright (C) 2010 Paul Ramsey <pramsey@cleverelephant.ca>
22
 *
23
 **********************************************************************/
24
25
26
#include <stdlib.h>
27
#include <ctype.h> /* for isspace */
28
29
#include "lwin_wkt.h"
30
#include "lwin_wkt_parse.h"
31
#include "lwgeom_log.h"
32
33
34
/*
35
* Error messages for failures in the parser.
36
*/
37
const char *parser_error_messages[] =
38
{
39
  "",
40
  "geometry requires more points",
41
  "geometry must have an odd number of points",
42
  "geometry contains non-closed rings",
43
  "can not mix dimensionality in a geometry",
44
  "parse error - invalid geometry",
45
  "invalid WKB type",
46
  "incontinuous compound curve",
47
  "triangle must have exactly 4 points",
48
  "geometry has too many points",
49
  "parse error - invalid geometry"
50
};
51
52
430
#define SET_PARSER_ERROR(errno) { \
53
430
    global_parser_result.message = parser_error_messages[(errno)]; \
54
430
    global_parser_result.errcode = (errno); \
55
430
    global_parser_result.errlocation = wkt_yylloc.last_column; \
56
430
  }
57
58
/**
59
* Read the SRID number from an SRID=<> string
60
*/
61
int wkt_lexer_read_srid(char *str)
62
177
{
63
177
  char *c = str;
64
177
  long i = 0;
65
177
  int32_t srid;
66
67
177
  if( ! str ) return SRID_UNKNOWN;
68
177
  c += 5; /* Advance past "SRID=" */
69
177
  i = strtol(c, NULL, 10);
70
177
  srid = clamp_srid((int32_t)i);
71
  /* TODO: warn on explicit UNKNOWN srid ? */
72
177
  return srid;
73
177
}
74
75
static lwflags_t wkt_dimensionality(char *dimensionality)
76
455k
{
77
455k
  size_t i = 0;
78
455k
  lwflags_t flags = 0;
79
80
455k
  if( ! dimensionality )
81
442k
    return flags;
82
83
  /* If there's an explicit dimensionality, we use that */
84
27.3k
  for( i = 0; i < strlen(dimensionality); i++ )
85
14.6k
  {
86
14.6k
    if( (dimensionality[i] == 'Z') || (dimensionality[i] == 'z') )
87
7.35k
      FLAGS_SET_Z(flags,1);
88
7.25k
    else if( (dimensionality[i] == 'M') || (dimensionality[i] == 'm') )
89
7.25k
      FLAGS_SET_M(flags,1);
90
    /* only a space is accepted in between */
91
0
    else if( ! isspace(dimensionality[i]) ) break;
92
14.6k
  }
93
12.7k
  return flags;
94
455k
}
95
96
97
/**
98
* Force the dimensionality of a geometry to match the dimensionality
99
* of a set of flags (usually derived from a ZM WKT tag).
100
*/
101
static int wkt_parser_set_dims(LWGEOM *geom, lwflags_t flags)
102
464k
{
103
464k
  int hasz = FLAGS_GET_Z(flags);
104
464k
  int hasm = FLAGS_GET_M(flags);
105
464k
  uint32_t i = 0;
106
107
  /* Error on junk */
108
464k
  if( ! geom )
109
0
    return LW_FAILURE;
110
111
464k
  FLAGS_SET_Z(geom->flags, hasz);
112
464k
  FLAGS_SET_M(geom->flags, hasm);
113
114
464k
  switch( geom->type )
115
464k
  {
116
33.9k
    case POINTTYPE:
117
33.9k
    {
118
33.9k
      LWPOINT *pt = (LWPOINT*)geom;
119
33.9k
      if ( pt->point )
120
33.9k
      {
121
33.9k
        FLAGS_SET_Z(pt->point->flags, hasz);
122
33.9k
        FLAGS_SET_M(pt->point->flags, hasm);
123
33.9k
      }
124
33.9k
      break;
125
0
    }
126
483
    case TRIANGLETYPE:
127
776
    case CIRCSTRINGTYPE:
128
43.7k
    case LINETYPE:
129
43.7k
    {
130
43.7k
      LWLINE *ln = (LWLINE*)geom;
131
43.7k
      if ( ln->points )
132
43.7k
      {
133
43.7k
        FLAGS_SET_Z(ln->points->flags, hasz);
134
43.7k
        FLAGS_SET_M(ln->points->flags, hasm);
135
43.7k
      }
136
43.7k
      break;
137
776
    }
138
0
    case NURBSCURVETYPE:
139
0
    {
140
0
      LWNURBSCURVE *curve = (LWNURBSCURVE*)geom;
141
0
      if ( curve->points )
142
0
      {
143
0
        FLAGS_SET_Z(curve->points->flags, hasz);
144
0
        FLAGS_SET_M(curve->points->flags, hasm);
145
0
      }
146
0
      break;
147
776
    }
148
18.9k
    case POLYGONTYPE:
149
18.9k
    {
150
18.9k
      LWPOLY *poly = (LWPOLY*)geom;
151
102k
      for ( i = 0; i < poly->nrings; i++ )
152
83.1k
      {
153
83.1k
        if( poly->rings[i] )
154
83.1k
        {
155
83.1k
          FLAGS_SET_Z(poly->rings[i]->flags, hasz);
156
83.1k
          FLAGS_SET_M(poly->rings[i]->flags, hasm);
157
83.1k
        }
158
83.1k
      }
159
18.9k
      break;
160
776
    }
161
1.24k
    case CURVEPOLYTYPE:
162
1.24k
    {
163
1.24k
      LWCURVEPOLY *poly = (LWCURVEPOLY*)geom;
164
42.8k
      for ( i = 0; i < poly->nrings; i++ )
165
41.5k
        wkt_parser_set_dims(poly->rings[i], flags);
166
1.24k
      break;
167
776
    }
168
366k
    default:
169
366k
    {
170
366k
      if ( lwtype_is_collection(geom->type) )
171
366k
      {
172
366k
        LWCOLLECTION *col = (LWCOLLECTION*)geom;
173
786k
        for ( i = 0; i < col->ngeoms; i++ )
174
419k
          wkt_parser_set_dims(col->geoms[i], flags);
175
366k
        return LW_SUCCESS;
176
366k
      }
177
0
      else
178
0
      {
179
0
        LWDEBUGF(2,"Unknown geometry type: %d", geom->type);
180
0
        return LW_FAILURE;
181
0
      }
182
366k
    }
183
464k
  }
184
185
97.8k
  return LW_SUCCESS;
186
464k
}
187
188
/**
189
* Read the dimensionality from a flag, if provided. Then check that the
190
* dimensionality matches that of the pointarray. If the dimension counts
191
* match, ensure the pointarray is using the right "Z" or "M".
192
*/
193
static int wkt_pointarray_dimensionality(POINTARRAY *pa, lwflags_t flags)
194
248k
{
195
248k
  int hasz = FLAGS_GET_Z(flags);
196
248k
  int hasm = FLAGS_GET_M(flags);
197
248k
  int ndims = 2 + hasz + hasm;
198
199
  /* No dimensionality or array means we go with what we have */
200
248k
  if( ! (flags && pa) )
201
246k
    return LW_TRUE;
202
203
1.86k
  LWDEBUGF(5,"dimensionality ndims == %d", ndims);
204
1.86k
  LWDEBUGF(5,"FLAGS_NDIMS(pa->flags) == %d", FLAGS_NDIMS(pa->flags));
205
206
  /*
207
  * ndims > 2 implies that the flags have something useful to add,
208
  * that there is a 'Z' or an 'M' or both.
209
  */
210
1.86k
  if( ndims > 2 )
211
1.86k
  {
212
    /* Mismatch implies a problem */
213
1.86k
    if ( FLAGS_NDIMS(pa->flags) != ndims )
214
12
      return LW_FALSE;
215
    /* Match means use the explicit dimensionality */
216
1.85k
    else
217
1.85k
    {
218
1.85k
      FLAGS_SET_Z(pa->flags, hasz);
219
1.85k
      FLAGS_SET_M(pa->flags, hasm);
220
1.85k
    }
221
1.86k
  }
222
223
1.85k
  return LW_TRUE;
224
1.86k
}
225
226
227
228
/**
229
* Build a 2d coordinate.
230
*/
231
POINT wkt_parser_coord_2(double c1, double c2)
232
624k
{
233
624k
  POINT p;
234
624k
  p.flags = 0;
235
624k
  p.x = c1;
236
624k
  p.y = c2;
237
624k
  p.z = p.m = 0.0;
238
624k
  FLAGS_SET_Z(p.flags, 0);
239
624k
  FLAGS_SET_M(p.flags, 0);
240
624k
  return p;
241
624k
}
242
243
/**
244
* Note, if this is an XYM coordinate we'll have to fix it later when we build
245
* the object itself and have access to the dimensionality token.
246
*/
247
POINT wkt_parser_coord_3(double c1, double c2, double c3)
248
9.21k
{
249
9.21k
    POINT p;
250
9.21k
    p.flags = 0;
251
9.21k
    p.x = c1;
252
9.21k
    p.y = c2;
253
9.21k
    p.z = c3;
254
9.21k
    p.m = 0;
255
9.21k
    FLAGS_SET_Z(p.flags, 1);
256
9.21k
    FLAGS_SET_M(p.flags, 0);
257
9.21k
    return p;
258
9.21k
}
259
260
/**
261
*/
262
POINT wkt_parser_coord_4(double c1, double c2, double c3, double c4)
263
1.06k
{
264
1.06k
  POINT p;
265
1.06k
  p.flags = 0;
266
1.06k
  p.x = c1;
267
1.06k
  p.y = c2;
268
1.06k
  p.z = c3;
269
1.06k
  p.m = c4;
270
1.06k
  FLAGS_SET_Z(p.flags, 1);
271
1.06k
  FLAGS_SET_M(p.flags, 1);
272
1.06k
  return p;
273
1.06k
}
274
275
POINTARRAY* wkt_parser_ptarray_add_coord(POINTARRAY *pa, POINT p)
276
634k
{
277
634k
  POINT4D pt;
278
634k
  LWDEBUG(4,"entered");
279
280
  /* Error on trouble */
281
634k
  if( ! pa )
282
0
  {
283
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
284
0
    return NULL;
285
0
  }
286
287
  /* Check that the coordinate has the same dimensionality as the array */
288
634k
  if( FLAGS_NDIMS(p.flags) != FLAGS_NDIMS(pa->flags) )
289
2
  {
290
2
    ptarray_free(pa);
291
2
    SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
292
2
    return NULL;
293
2
  }
294
295
  /* While parsing the point arrays, XYM and XMZ points are both treated as XYZ */
296
634k
  pt.x = p.x;
297
634k
  pt.y = p.y;
298
634k
  if( FLAGS_GET_Z(pa->flags) )
299
10.2k
    pt.z = p.z;
300
634k
  if( FLAGS_GET_M(pa->flags) )
301
1.06k
    pt.m = p.m;
302
  /* If the destination is XYM, we'll write the third coordinate to m */
303
634k
  if( FLAGS_GET_M(pa->flags) && ! FLAGS_GET_Z(pa->flags) )
304
0
    pt.m = p.z;
305
306
634k
  ptarray_append_point(pa, &pt, LW_TRUE); /* Allow duplicate points in array */
307
634k
  return pa;
308
634k
}
309
310
/**
311
* Start a point array from the first coordinate.
312
*/
313
POINTARRAY* wkt_parser_ptarray_new(POINT p)
314
398k
{
315
398k
  int ndims = FLAGS_NDIMS(p.flags);
316
398k
  POINTARRAY *pa = ptarray_construct_empty((ndims>2), (ndims>3), 4);
317
398k
  LWDEBUG(4,"entered");
318
398k
  if ( ! pa )
319
0
  {
320
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
321
0
    return NULL;
322
0
  }
323
398k
  return wkt_parser_ptarray_add_coord(pa, p);
324
398k
}
325
326
/**
327
* Create a new point. Null point array implies empty. Null dimensionality
328
* implies no specified dimensionality in the WKT.
329
*/
330
LWGEOM* wkt_parser_point_new(POINTARRAY *pa, char *dimensionality)
331
240k
{
332
240k
  lwflags_t flags = wkt_dimensionality(dimensionality);
333
240k
  LWDEBUG(4,"entered");
334
335
  /* No pointarray means it is empty */
336
240k
  if( ! pa )
337
1.72k
    return lwpoint_as_lwgeom(lwpoint_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags)));
338
339
  /* If the number of dimensions is not consistent, we have a problem. */
340
238k
  if( wkt_pointarray_dimensionality(pa, flags) == LW_FALSE )
341
3
  {
342
3
    ptarray_free(pa);
343
3
    SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
344
3
    return NULL;
345
3
  }
346
347
  /* Only one point allowed in our point array! */
348
238k
  if( pa->npoints != 1 )
349
25
  {
350
25
    ptarray_free(pa);
351
25
    SET_PARSER_ERROR(PARSER_ERROR_LESSPOINTS);
352
25
    return NULL;
353
25
  }
354
355
238k
  return lwpoint_as_lwgeom(lwpoint_construct(SRID_UNKNOWN, NULL, pa));
356
238k
}
357
358
359
/**
360
* Create a new linestring. Null point array implies empty. Null dimensionality
361
* implies no specified dimensionality in the WKT. Check for numpoints >= 2 if
362
* requested.
363
*/
364
LWGEOM* wkt_parser_linestring_new(POINTARRAY *pa, char *dimensionality)
365
105k
{
366
105k
  lwflags_t flags = wkt_dimensionality(dimensionality);
367
105k
  LWDEBUG(4,"entered");
368
369
  /* No pointarray means it is empty */
370
105k
  if( ! pa )
371
102k
    return lwline_as_lwgeom(lwline_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags)));
372
373
  /* If the number of dimensions is not consistent, we have a problem. */
374
3.95k
  if( wkt_pointarray_dimensionality(pa, flags) == LW_FALSE )
375
3
  {
376
3
    ptarray_free(pa);
377
3
    SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
378
3
    return NULL;
379
3
  }
380
381
  /* Apply check for not enough points, if requested. */
382
3.95k
  if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_MINPOINTS) && (pa->npoints < 2) )
383
0
  {
384
0
    ptarray_free(pa);
385
0
    SET_PARSER_ERROR(PARSER_ERROR_MOREPOINTS);
386
0
    return NULL;
387
0
  }
388
389
3.95k
  return lwline_as_lwgeom(lwline_construct(SRID_UNKNOWN, NULL, pa));
390
3.95k
}
391
392
/**
393
* Create a new circularstring. Null point array implies empty. Null dimensionality
394
* implies no specified dimensionality in the WKT.
395
* Circular strings are just like linestrings, except with slightly different
396
* validity rules (minpoint == 3, numpoints % 2 == 1).
397
*/
398
LWGEOM* wkt_parser_circularstring_new(POINTARRAY *pa, char *dimensionality)
399
3.18k
{
400
3.18k
  lwflags_t flags = wkt_dimensionality(dimensionality);
401
3.18k
  LWDEBUG(4,"entered");
402
403
  /* No pointarray means it is empty */
404
3.18k
  if( ! pa )
405
1.06k
    return lwcircstring_as_lwgeom(lwcircstring_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags)));
406
407
  /* If the number of dimensions is not consistent, we have a problem. */
408
2.11k
  if( wkt_pointarray_dimensionality(pa, flags) == LW_FALSE )
409
3
  {
410
3
    ptarray_free(pa);
411
3
    SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
412
3
    return NULL;
413
3
  }
414
415
  /* Apply check for not enough points, if requested. */
416
2.10k
  if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_MINPOINTS) && (pa->npoints < 3) )
417
0
  {
418
0
    ptarray_free(pa);
419
0
    SET_PARSER_ERROR(PARSER_ERROR_MOREPOINTS);
420
0
    return NULL;
421
0
  }
422
423
  /* Apply check for odd number of points, if requested. */
424
2.10k
  if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_ODD) && ((pa->npoints % 2) == 0) )
425
0
  {
426
0
    ptarray_free(pa);
427
0
    SET_PARSER_ERROR(PARSER_ERROR_ODDPOINTS);
428
0
    return NULL;
429
0
  }
430
431
2.10k
  return lwcircstring_as_lwgeom(lwcircstring_construct(SRID_UNKNOWN, NULL, pa));
432
2.10k
}
433
434
LWGEOM* wkt_parser_triangle_new(POINTARRAY *pa, char *dimensionality)
435
5.29k
{
436
5.29k
  lwflags_t flags = wkt_dimensionality(dimensionality);
437
5.29k
  LWDEBUG(4,"entered");
438
439
  /* No pointarray means it is empty */
440
5.29k
  if( ! pa )
441
1.15k
    return lwtriangle_as_lwgeom(lwtriangle_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags)));
442
443
  /* If the number of dimensions is not consistent, we have a problem. */
444
4.14k
  if( wkt_pointarray_dimensionality(pa, flags) == LW_FALSE )
445
3
  {
446
3
    ptarray_free(pa);
447
3
    SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
448
3
    return NULL;
449
3
  }
450
451
  /* Triangles need four points. */
452
4.14k
  if( (pa->npoints != 4) )
453
39
  {
454
39
    ptarray_free(pa);
455
39
    SET_PARSER_ERROR(PARSER_ERROR_TRIANGLEPOINTS);
456
39
    return NULL;
457
39
  }
458
459
  /* Triangles need closure. */
460
4.10k
  if( ! ptarray_is_closed_z(pa) )
461
134
  {
462
134
    ptarray_free(pa);
463
134
    SET_PARSER_ERROR(PARSER_ERROR_UNCLOSED);
464
134
    return NULL;
465
134
  }
466
467
3.96k
  return lwtriangle_as_lwgeom(lwtriangle_construct(SRID_UNKNOWN, NULL, pa));
468
4.10k
}
469
470
LWGEOM* wkt_parser_polygon_new(POINTARRAY *pa, char dimcheck)
471
18.8k
{
472
18.8k
  LWPOLY *poly = NULL;
473
18.8k
  LWDEBUG(4,"entered");
474
475
  /* No pointarray is a problem */
476
18.8k
  if( ! pa )
477
0
  {
478
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
479
0
    return NULL;
480
0
  }
481
482
18.8k
  poly = lwpoly_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(pa->flags), FLAGS_GET_M(pa->flags));
483
484
  /* Error out if we can't build this polygon. */
485
18.8k
  if( ! poly )
486
0
  {
487
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
488
0
    return NULL;
489
0
  }
490
491
18.8k
  wkt_parser_polygon_add_ring(lwpoly_as_lwgeom(poly), pa, dimcheck);
492
18.8k
  return lwpoly_as_lwgeom(poly);
493
18.8k
}
494
495
LWGEOM* wkt_parser_polygon_add_ring(LWGEOM *poly, POINTARRAY *pa, char dimcheck)
496
149k
{
497
149k
  LWDEBUG(4,"entered");
498
499
  /* Bad inputs are a problem */
500
149k
  if( ! (pa && poly) )
501
0
  {
502
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
503
0
    return NULL;
504
0
  }
505
506
  /* Rings must agree on dimensionality */
507
149k
  if( FLAGS_NDIMS(poly->flags) != FLAGS_NDIMS(pa->flags) )
508
5
  {
509
5
    ptarray_free(pa);
510
5
    lwgeom_free(poly);
511
5
    SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
512
5
    return NULL;
513
5
  }
514
515
  /* Apply check for minimum number of points, if requested. */
516
149k
  if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_MINPOINTS) && (pa->npoints < 4) )
517
0
  {
518
0
    ptarray_free(pa);
519
0
    lwgeom_free(poly);
520
0
    SET_PARSER_ERROR(PARSER_ERROR_MOREPOINTS);
521
0
    return NULL;
522
0
  }
523
524
  /* Apply check for not closed rings, if requested. */
525
149k
  if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_CLOSURE) &&
526
0
      ! (dimcheck == 'Z' ? ptarray_is_closed_z(pa) : ptarray_is_closed_2d(pa)) )
527
0
  {
528
0
    ptarray_free(pa);
529
0
    lwgeom_free(poly);
530
0
    SET_PARSER_ERROR(PARSER_ERROR_UNCLOSED);
531
0
    return NULL;
532
0
  }
533
534
  /* If something goes wrong adding a ring, error out. */
535
149k
  if ( LW_FAILURE == lwpoly_add_ring(lwgeom_as_lwpoly(poly), pa) )
536
0
  {
537
0
    ptarray_free(pa);
538
0
    lwgeom_free(poly);
539
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
540
0
    return NULL;
541
0
  }
542
149k
  return poly;
543
149k
}
544
545
LWGEOM* wkt_parser_polygon_finalize(LWGEOM *poly, char *dimensionality)
546
20.4k
{
547
20.4k
  lwflags_t flags = wkt_dimensionality(dimensionality);
548
20.4k
  int flagdims = FLAGS_NDIMS(flags);
549
20.4k
  LWDEBUG(4,"entered");
550
551
  /* Null input implies empty return */
552
20.4k
  if( ! poly )
553
20.0k
    return lwpoly_as_lwgeom(lwpoly_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags)));
554
555
  /* If the number of dimensions are not consistent, we have a problem. */
556
431
  if( flagdims > 2 )
557
223
  {
558
223
    if ( flagdims != FLAGS_NDIMS(poly->flags) )
559
4
    {
560
4
      lwgeom_free(poly);
561
4
      SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
562
4
      return NULL;
563
4
    }
564
565
    /* Harmonize the flags in the sub-components with the wkt flags */
566
219
    if( LW_FAILURE == wkt_parser_set_dims(poly, flags) )
567
0
    {
568
0
      lwgeom_free(poly);
569
0
      SET_PARSER_ERROR(PARSER_ERROR_OTHER);
570
0
      return NULL;
571
0
    }
572
219
  }
573
574
427
  return poly;
575
431
}
576
577
LWGEOM* wkt_parser_curvepolygon_new(LWGEOM *ring)
578
678
{
579
678
  LWGEOM *poly;
580
678
  LWDEBUG(4,"entered");
581
582
  /* Toss error on null geometry input */
583
678
  if( ! ring )
584
0
  {
585
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
586
0
    return NULL;
587
0
  }
588
589
  /* Construct poly and add the ring. */
590
678
  poly = lwcurvepoly_as_lwgeom(lwcurvepoly_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(ring->flags), FLAGS_GET_M(ring->flags)));
591
  /* Return the result. */
592
678
  return wkt_parser_curvepolygon_add_ring(poly,ring);
593
678
}
594
595
LWGEOM* wkt_parser_curvepolygon_add_ring(LWGEOM *poly, LWGEOM *ring)
596
67.9k
{
597
67.9k
  LWDEBUG(4,"entered");
598
599
  /* Toss error on null input */
600
67.9k
  if( ! (ring && poly) )
601
0
  {
602
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
603
0
    LWDEBUG(4,"inputs are null");
604
0
    return NULL;
605
0
  }
606
607
  /* All the elements must agree on dimensionality */
608
67.9k
  if( FLAGS_NDIMS(poly->flags) != FLAGS_NDIMS(ring->flags) )
609
3
  {
610
3
    LWDEBUG(4,"dimensionality does not match");
611
3
    lwgeom_free(ring);
612
3
    lwgeom_free(poly);
613
3
    SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
614
3
    return NULL;
615
3
  }
616
617
  /* Apply check for minimum number of points, if requested. */
618
67.9k
  if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_MINPOINTS) )
619
0
  {
620
0
    uint32_t vertices_needed = 3;
621
622
0
    if ( ring->type == LINETYPE )
623
0
      vertices_needed = 4;
624
625
0
    if (lwgeom_count_vertices(ring) < vertices_needed)
626
0
    {
627
0
      LWDEBUG(4,"number of points is incorrect");
628
0
      lwgeom_free(ring);
629
0
      lwgeom_free(poly);
630
0
      SET_PARSER_ERROR(PARSER_ERROR_MOREPOINTS);
631
0
      return NULL;
632
0
    }
633
0
  }
634
635
  /* Apply check for not closed rings, if requested. */
636
67.9k
  if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_CLOSURE) )
637
0
  {
638
0
    int is_closed = 1;
639
0
    LWDEBUG(4,"checking ring closure");
640
0
    switch ( ring->type )
641
0
    {
642
0
      case LINETYPE:
643
0
      is_closed = lwline_is_closed(lwgeom_as_lwline(ring));
644
0
      break;
645
646
0
      case CIRCSTRINGTYPE:
647
0
      is_closed = lwcircstring_is_closed(lwgeom_as_lwcircstring(ring));
648
0
      break;
649
650
0
      case COMPOUNDTYPE:
651
0
      is_closed = lwcompound_is_closed(lwgeom_as_lwcompound(ring));
652
0
      break;
653
654
0
      case NURBSCURVETYPE:
655
0
      is_closed = lwgeom_is_closed(ring);
656
0
      break;
657
0
    }
658
0
    if ( ! is_closed )
659
0
    {
660
0
      LWDEBUG(4,"ring is not closed");
661
0
      lwgeom_free(ring);
662
0
      lwgeom_free(poly);
663
0
      SET_PARSER_ERROR(PARSER_ERROR_UNCLOSED);
664
0
      return NULL;
665
0
    }
666
0
  }
667
668
67.9k
  if( LW_FAILURE == lwcurvepoly_add_ring(lwgeom_as_lwcurvepoly(poly), ring) )
669
0
  {
670
0
    LWDEBUG(4,"failed to add ring");
671
0
    lwgeom_free(ring);
672
0
    lwgeom_free(poly);
673
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
674
0
    return NULL;
675
0
  }
676
677
67.9k
  return poly;
678
67.9k
}
679
680
LWGEOM* wkt_parser_curvepolygon_finalize(LWGEOM *poly, char *dimensionality)
681
2.09k
{
682
2.09k
  lwflags_t flags = wkt_dimensionality(dimensionality);
683
2.09k
  int flagdims = FLAGS_NDIMS(flags);
684
2.09k
  LWDEBUG(4,"entered");
685
686
  /* Null input implies empty return */
687
2.09k
  if( ! poly )
688
1.49k
    return lwcurvepoly_as_lwgeom(lwcurvepoly_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags)));
689
690
601
  if ( flagdims > 2 )
691
224
  {
692
    /* If the number of dimensions are not consistent, we have a problem. */
693
224
    if( flagdims != FLAGS_NDIMS(poly->flags) )
694
4
    {
695
4
      lwgeom_free(poly);
696
4
      SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
697
4
      return NULL;
698
4
    }
699
700
    /* Harmonize the flags in the sub-components with the wkt flags */
701
220
    if( LW_FAILURE == wkt_parser_set_dims(poly, flags) )
702
0
    {
703
0
      lwgeom_free(poly);
704
0
      SET_PARSER_ERROR(PARSER_ERROR_OTHER);
705
0
      return NULL;
706
0
    }
707
220
  }
708
709
597
  return poly;
710
601
}
711
712
LWGEOM* wkt_parser_collection_new(LWGEOM *geom)
713
69.9k
{
714
69.9k
  LWCOLLECTION *col;
715
69.9k
  LWGEOM **geoms;
716
69.9k
  static int ngeoms = 1;
717
69.9k
  LWDEBUG(4,"entered");
718
719
  /* Toss error on null geometry input */
720
69.9k
  if( ! geom )
721
0
  {
722
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
723
0
    return NULL;
724
0
  }
725
726
  /* Create our geometry array */
727
69.9k
  geoms = lwalloc(sizeof(LWGEOM*) * ngeoms);
728
69.9k
  geoms[0] = geom;
729
730
  /* Make a new collection */
731
69.9k
  col = lwcollection_construct(COLLECTIONTYPE, SRID_UNKNOWN, NULL, ngeoms, geoms);
732
733
  /* Return the result. */
734
69.9k
  return lwcollection_as_lwgeom(col);
735
69.9k
}
736
737
738
LWGEOM* wkt_parser_compound_new(LWGEOM *geom)
739
1.04k
{
740
1.04k
  LWCOLLECTION *col;
741
1.04k
  LWGEOM **geoms;
742
1.04k
  static int ngeoms = 1;
743
1.04k
  LWDEBUG(4,"entered");
744
745
  /* Toss error on null geometry input */
746
1.04k
  if( ! geom )
747
0
  {
748
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
749
0
    return NULL;
750
0
  }
751
752
  /* Elements of a compoundcurve cannot be empty, because */
753
  /* empty things can't join up and form a ring */
754
1.04k
  if ( lwgeom_is_empty(geom) )
755
3
  {
756
3
    lwgeom_free(geom);
757
3
    SET_PARSER_ERROR(PARSER_ERROR_INCONTINUOUS);
758
3
    return NULL;
759
3
  }
760
761
  /* Create our geometry array */
762
1.04k
  geoms = lwalloc(sizeof(LWGEOM*) * ngeoms);
763
1.04k
  geoms[0] = geom;
764
765
  /* Make a new collection */
766
1.04k
  col = lwcollection_construct(COLLECTIONTYPE, SRID_UNKNOWN, NULL, ngeoms, geoms);
767
768
  /* Return the result. */
769
1.04k
  return lwcollection_as_lwgeom(col);
770
1.04k
}
771
772
773
LWGEOM* wkt_parser_compound_add_geom(LWGEOM *col, LWGEOM *geom)
774
1.97k
{
775
1.97k
  LWDEBUG(4,"entered");
776
777
  /* Toss error on null geometry input */
778
1.97k
  if( ! (geom && col) )
779
0
  {
780
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
781
0
    return NULL;
782
0
  }
783
784
  /* All the elements must agree on dimensionality */
785
1.97k
  if( FLAGS_NDIMS(col->flags) != FLAGS_NDIMS(geom->flags) )
786
3
  {
787
3
    lwgeom_free(col);
788
3
    lwgeom_free(geom);
789
3
    SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
790
3
    return NULL;
791
3
  }
792
793
1.97k
  if( LW_FAILURE == lwcompound_add_lwgeom((LWCOMPOUND*)col, geom) )
794
46
  {
795
46
    lwgeom_free(col);
796
46
    lwgeom_free(geom);
797
46
    SET_PARSER_ERROR(PARSER_ERROR_INCONTINUOUS);
798
46
    return NULL;
799
46
  }
800
801
1.92k
  return col;
802
1.97k
}
803
804
805
LWGEOM* wkt_parser_compound_finalize(LWGEOM *compound, char *dimensionality)
806
2.64k
{
807
2.64k
  lwflags_t flags = wkt_dimensionality(dimensionality);
808
2.64k
  int flagdims = FLAGS_NDIMS(flags);
809
810
  /* No geometry means it is empty */
811
2.64k
  if ( ! compound )
812
1.74k
  {
813
1.74k
    return lwcompound_as_lwgeom(lwcompound_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags)));
814
1.74k
  }
815
816
898
  if ( flagdims > 2 )
817
202
  {
818
    /* If the number of dimensions are not consistent, we have a problem. */
819
202
    if( flagdims != FLAGS_NDIMS(compound->flags) )
820
3
    {
821
3
      lwgeom_free(compound);
822
3
      SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
823
3
      return NULL;
824
3
    }
825
826
    /* Harmonize the flags in the sub-components with the wkt flags */
827
199
    if( LW_FAILURE == wkt_parser_set_dims(compound, flags) )
828
0
    {
829
0
      lwgeom_free(compound);
830
0
      SET_PARSER_ERROR(PARSER_ERROR_OTHER);
831
0
      return NULL;
832
0
    }
833
199
  }
834
895
  compound->type = COMPOUNDTYPE;
835
895
  return compound;
836
898
}
837
838
839
840
LWGEOM* wkt_parser_collection_add_geom(LWGEOM *col, LWGEOM *geom)
841
331k
{
842
331k
  LWDEBUG(4,"entered");
843
844
  /* Toss error on null geometry input */
845
331k
  if( ! (geom && col) )
846
0
  {
847
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
848
0
    return NULL;
849
0
  }
850
851
331k
  return lwcollection_as_lwgeom(lwcollection_add_lwgeom(lwgeom_as_lwcollection(col), geom));
852
331k
}
853
854
/**
855
 * Finalize a geometry collection by validating and harmonizing dimensionality, and set its collection type.
856
 *
857
 * Validates that the provided collection (geom) matches the explicit dimensionality tokens (Z/M) if present,
858
 * harmonizes sub-geometry dimensionality when needed, and sets geom->type to the requested collection type.
859
 * If geom is NULL an empty collection of the requested type is constructed (SRID_UNKNOWN) and returned.
860
 *
861
 * @param lwtype Target collection type (e.g., COLLECTIONTYPE).
862
 * @param geom Geometry to finalize; may be NULL to produce an empty collection. On error this function frees geom.
863
 * @param dimensionality Optional dimensionality token string (e.g. "Z", "M", "ZM"); used to enforce or harmonize Z/M presence.
864
 *
865
 * @return The finalized LWGEOM (possibly the input geom, modified), or NULL on error.
866
 *
867
 * Side effects:
868
 * - May free the input geom on error.
869
 * - May call wkt_parser_set_dims() to propagate dimensionality to sub-geometries.
870
 * - On error sets parser error codes: PARSER_ERROR_MIXDIMS (dimensionality mismatch) or PARSER_ERROR_OTHER.
871
 */
872
LWGEOM* wkt_parser_collection_finalize(int lwtype, LWGEOM *geom, char *dimensionality)
873
75.5k
{
874
75.5k
  lwflags_t flags = wkt_dimensionality(dimensionality);
875
75.5k
  int flagdims = FLAGS_NDIMS(flags);
876
877
  /* No geometry means it is empty */
878
75.5k
  if( ! geom )
879
11.5k
  {
880
11.5k
    return lwcollection_as_lwgeom(lwcollection_construct_empty(lwtype, SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags)));
881
11.5k
  }
882
883
  /* There are 'Z' or 'M' tokens in the signature */
884
64.0k
  if ( flagdims > 2 )
885
2.83k
  {
886
2.83k
    LWCOLLECTION *col = lwgeom_as_lwcollection(geom);
887
2.83k
    uint32_t i;
888
889
22.6k
    for ( i = 0 ; i < col->ngeoms; i++ )
890
19.9k
    {
891
19.9k
      LWGEOM *subgeom = col->geoms[i];
892
19.9k
      if ( FLAGS_NDIMS(flags) != FLAGS_NDIMS(subgeom->flags) &&
893
17.6k
         ! lwgeom_is_empty(subgeom) )
894
146
      {
895
146
        lwgeom_free(geom);
896
146
        SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
897
146
        return NULL;
898
146
      }
899
900
19.7k
      if ( lwtype == COLLECTIONTYPE &&
901
2.63k
         ( (FLAGS_GET_Z(flags) != FLAGS_GET_Z(subgeom->flags)) ||
902
1.70k
           (FLAGS_GET_M(flags) != FLAGS_GET_M(subgeom->flags)) ) &&
903
1.66k
        ! lwgeom_is_empty(subgeom) )
904
1
      {
905
1
        lwgeom_free(geom);
906
1
        SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
907
1
        return NULL;
908
1
      }
909
19.7k
    }
910
911
    /* Harmonize the collection dimensionality */
912
2.68k
    if( LW_FAILURE == wkt_parser_set_dims(geom, flags) )
913
0
    {
914
0
      lwgeom_free(geom);
915
0
      SET_PARSER_ERROR(PARSER_ERROR_OTHER);
916
0
      return NULL;
917
0
    }
918
2.68k
  }
919
920
  /* Set the collection type */
921
63.8k
  geom->type = lwtype;
922
923
63.8k
  return geom;
924
64.0k
}
925
926
/**
927
 * Create a 2D control point for NURBS with the weight stored in the M component (XYM).
928
 *
929
 * @param c1 X coordinate.
930
 * @param c2 Y coordinate.
931
 * @param weight Weight for the NURBS control point (stored in M).
932
 * @return POINT with X=c1, Y=c2, Z=0.0 and M=weight; flags indicate no Z and presence of M.
933
 */
934
POINT wkt_parser_nurbs_coord_3(double c1, double c2, double weight)
935
0
{
936
0
  POINT p;
937
0
  p.flags = 0;
938
0
  p.x = c1;
939
0
  p.y = c2;
940
0
  p.z = 0.0;
941
0
  p.m = weight;  /* Weight goes in M coordinate */
942
0
  FLAGS_SET_Z(p.flags, 0);
943
0
  FLAGS_SET_M(p.flags, 1);
944
0
  return p;
945
0
}
946
947
/**
948
 * Create a 3D control point for NURBS with the weight stored in the M component (XYZM).
949
 *
950
 * The returned POINT has X=c1, Y=c2, Z=c3, and M=weight, and has both Z and M flags enabled.
951
 *
952
 * @param c1 X coordinate.
953
 * @param c2 Y coordinate.
954
 * @param c3 Z coordinate.
955
 * @param weight Curve weight stored in the point's M component.
956
 * @return POINT initialized as an XYZM control point with appropriate flags set.
957
 */
958
POINT wkt_parser_nurbs_coord_4(double c1, double c2, double c3, double weight)
959
0
{
960
0
  POINT p;
961
0
  p.flags = 0;
962
0
  p.x = c1;
963
0
  p.y = c2;
964
0
  p.z = c3;
965
0
  p.m = weight;  /* Weight goes in M coordinate */
966
0
  FLAGS_SET_Z(p.flags, 1);
967
0
  FLAGS_SET_M(p.flags, 1);
968
0
  return p;
969
0
}
970
971
/**
972
 * Create a new NURBS curve from WKT parsing
973
 */
974
975
/**
976
 * Construct a NURBS curve geometry from parsed components.
977
 *
978
 * Builds and returns a LWNURBSCURVE (as LWGEOM) using the supplied polynomial
979
 * degree, control points, optional per-control-point weights, and optional
980
 * knot vector. If degree == 0, degree 2 is used for backward compatibility.
981
 *
982
 * The function validates:
983
 * - degree is in a supported range (1..10; 0 is treated as 2),
984
 * - points count is at least degree + 1,
985
 * - if weights are provided, their count equals the number of points and all
986
 *   weights are positive (weights are read from the X coordinate of the
987
 *   supplied weights POINTARRAY),
988
 * - if knots are provided, their count equals points_count + degree + 1 and
989
 *   the knot vector is non-decreasing (knot values are read from the X
990
 *   coordinate of the supplied knots POINTARRAY),
991
 * - dimensionality of the points is compatible with the optional
992
 *   dimensionality token.
993
 *
994
 * Ownership and side effects:
995
 * - The function takes ownership of the supplied POINTARRAY pointers: it will
996
 *   free them on error and will consume them on success (they are passed into
997
 *   the constructed NURBS curve). Caller must not use or free them after
998
 *   calling this function.
999
 * - On any validation or construction failure the function returns NULL and
1000
 *   sets the parser error state.
1001
 *
1002
 * @param degree Polynomial degree for the NURBS curve (0 treated as 2).
1003
 * @param points Control point array (must be non-NULL for a non-empty curve).
1004
 * @param weights Optional weight array (one weight per control point; weight
1005
 *                value is read from each point's X coordinate).
1006
 * @param knots Optional knot array (knot values read from each point's X
1007
 *              coordinate).
1008
 * @param dimensionality Optional dimensionality token (e.g. "Z", "M", "ZM")
1009
 *                        used to derive Z/M flags for the constructed curve.
1010
 *
1011
 * @return Pointer to the constructed LWGEOM representing the NURBS curve, or
1012
 *         NULL on error.
1013
 */
1014
LWGEOM* wkt_parser_nurbscurve_new(double degree, POINTARRAY *points, POINTARRAY *weights, POINTARRAY *knots, char *dimensionality)
1015
0
{
1016
0
  LWNURBSCURVE *curve;
1017
0
  double *weight_array = NULL;
1018
0
  double *knot_array = NULL;
1019
0
  uint32_t nweights = 0, nknots = 0;
1020
0
  lwflags_t flags = wkt_dimensionality(dimensionality);
1021
0
  int int_degree;
1022
1023
0
  LWDEBUG(4,"entered wkt_parser_nurbscurve_new");
1024
1025
  /* Validate degree is finite and not fractional */
1026
0
  if (!isfinite(degree)) {
1027
0
    if (points) ptarray_free(points);
1028
0
    if (weights) ptarray_free(weights);
1029
0
    if (knots) ptarray_free(knots);
1030
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1031
0
    return NULL;
1032
0
  }
1033
1034
  /* Check if degree has a fractional part */
1035
0
  if (fabs(degree - round(degree)) >= 1e-10) {
1036
0
    if (points) ptarray_free(points);
1037
0
    if (weights) ptarray_free(weights);
1038
0
    if (knots) ptarray_free(knots);
1039
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1040
0
    return NULL;
1041
0
  }
1042
1043
  /* Cast to int after validation */
1044
0
  int_degree = (int)round(degree);
1045
1046
  /* Degree 0 defaults to quadratic (degree 2) */
1047
0
  if (int_degree == 0)
1048
0
    int_degree = 2;
1049
1050
  /* Validate degree range */
1051
0
  if (int_degree < 1 || int_degree > 10) {
1052
0
    if (points) ptarray_free(points);
1053
0
    if (weights) ptarray_free(weights);
1054
0
    if (knots) ptarray_free(knots);
1055
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1056
0
    return NULL;
1057
0
  }
1058
1059
  /* Handle empty geometry */
1060
0
  if (!points) {
1061
0
    if (weights) ptarray_free(weights);
1062
0
    if (knots) ptarray_free(knots);
1063
0
    return lwnurbscurve_as_lwgeom(lwnurbscurve_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags)));
1064
0
  }
1065
1066
  /* Validate minimum points for degree */
1067
0
  if (points->npoints < (uint32_t)(int_degree + 1)) {
1068
0
    ptarray_free(points);
1069
0
    if (weights) ptarray_free(weights);
1070
0
    if (knots) ptarray_free(knots);
1071
0
    SET_PARSER_ERROR(PARSER_ERROR_MOREPOINTS);
1072
0
    return NULL;
1073
0
  }
1074
1075
  /* Check dimensionality consistency */
1076
0
  if (wkt_pointarray_dimensionality(points, flags) == LW_FALSE) {
1077
0
    ptarray_free(points);
1078
0
    if (weights) ptarray_free(weights);
1079
0
    if (knots) ptarray_free(knots);
1080
0
    SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
1081
0
    return NULL;
1082
0
  }
1083
1084
  /* Extract weights if provided */
1085
0
  if (weights && weights->npoints > 0) {
1086
0
    int all_weights_one = LW_TRUE;
1087
1088
0
    nweights = weights->npoints;
1089
0
    if (nweights != points->npoints) {
1090
0
      ptarray_free(points);
1091
0
      ptarray_free(weights);
1092
0
      if (knots) ptarray_free(knots);
1093
0
      SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1094
0
      return NULL;
1095
0
    }
1096
1097
0
    weight_array = lwalloc(sizeof(double) * nweights);
1098
0
    for (uint32_t i = 0; i < nweights; i++) {
1099
0
      POINT2D p;
1100
0
      if (!getPoint2d_p(weights, i, &p)) {
1101
0
        ptarray_free(points);
1102
0
        ptarray_free(weights);
1103
0
        if (knots) ptarray_free(knots);
1104
0
        lwfree(weight_array);
1105
0
        SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1106
0
        return NULL;
1107
0
      }
1108
0
      weight_array[i] = p.x; /* Weight is stored in X coordinate */
1109
1110
0
      if (weight_array[i] <= 0.0) {
1111
0
        ptarray_free(points);
1112
0
        ptarray_free(weights);
1113
0
        if (knots) ptarray_free(knots);
1114
0
        lwfree(weight_array);
1115
0
        SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1116
0
        return NULL;
1117
0
      }
1118
0
      if (!FP_EQUALS(weight_array[i], 1.0))
1119
0
        all_weights_one = LW_FALSE;
1120
0
    }
1121
0
    ptarray_free(weights);
1122
1123
0
    if (all_weights_one) {
1124
0
      lwfree(weight_array);
1125
0
      weight_array = NULL;
1126
0
      nweights = 0;
1127
0
    }
1128
0
  }
1129
1130
  /* Extract knots if provided */
1131
0
  if (knots && knots->npoints > 0) {
1132
0
    nknots = knots->npoints;
1133
0
    uint32_t expected_knots = points->npoints + int_degree + 1;
1134
1135
0
    if (nknots != expected_knots) {
1136
0
      ptarray_free(points);
1137
0
      if (weight_array) lwfree(weight_array);
1138
0
      ptarray_free(knots);
1139
0
      SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1140
0
      return NULL;
1141
0
    }
1142
1143
0
    knot_array = lwalloc(sizeof(double) * nknots);
1144
0
    for (uint32_t i = 0; i < nknots; i++) {
1145
0
      POINT2D p;
1146
0
      if (!getPoint2d_p(knots, i, &p)) {
1147
0
        ptarray_free(points);
1148
0
        if (weight_array) lwfree(weight_array);
1149
0
        ptarray_free(knots);
1150
0
        lwfree(knot_array);
1151
0
        SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1152
0
        return NULL;
1153
0
      }
1154
0
      knot_array[i] = p.x; /* Knot value is stored in X coordinate */
1155
0
    }
1156
1157
    /* Validate knot vector is non-decreasing */
1158
0
    for (uint32_t i = 1; i < nknots; i++) {
1159
0
      if (knot_array[i] < knot_array[i-1]) {
1160
0
        ptarray_free(points);
1161
0
        if (weight_array) lwfree(weight_array);
1162
0
        ptarray_free(knots);
1163
0
        lwfree(knot_array);
1164
0
        SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1165
0
        return NULL;
1166
0
      }
1167
0
    }
1168
1169
0
    ptarray_free(knots);
1170
0
  }
1171
1172
  /* Create the NURBS curve */
1173
0
  curve = lwnurbscurve_construct(SRID_UNKNOWN, NULL, int_degree, points, weight_array, knot_array, nweights, nknots);
1174
1175
0
  if (!curve) {
1176
0
    ptarray_free(points);
1177
0
    if (weight_array) lwfree(weight_array);
1178
0
    if (knot_array) lwfree(knot_array);
1179
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1180
0
    return NULL;
1181
0
  }
1182
  /* Deep-copied by constructor */
1183
0
  if (weight_array) lwfree(weight_array);
1184
0
  if (knot_array) lwfree(knot_array);
1185
0
  return lwnurbscurve_as_lwgeom(curve);
1186
0
}
1187
1188
1189
/**
1190
 * Create an empty NURBS curve with the given dimensionality token.
1191
 *
1192
 * The `dimensionality` token (e.g., "Z", "M", "ZM", or NULL) controls whether
1193
 * the created empty NURBS geometry has Z and/or M components. The returned
1194
 * geometry uses SRID_UNKNOWN.
1195
 *
1196
 * @param dimensionality Dimensionality token (case-insensitive) or NULL for 2D.
1197
 * @return A newly allocated empty LWNURBSCURVE as an LWGEOM on success;
1198
 *         NULL on failure (parser error set).
1199
 */
1200
LWGEOM* wkt_parser_nurbscurve_empty(char *dimensionality)
1201
0
{
1202
0
  lwflags_t flags = wkt_dimensionality(dimensionality);
1203
0
  LWNURBSCURVE *curve;
1204
1205
0
  LWDEBUG(4,"entered wkt_parser_nurbscurve_empty");
1206
1207
0
  curve = lwnurbscurve_construct_empty(SRID_UNKNOWN,
1208
0
    FLAGS_GET_Z(flags), FLAGS_GET_M(flags));
1209
1210
0
  if (!curve) {
1211
0
    SET_PARSER_ERROR(PARSER_ERROR_OTHER);
1212
0
    return NULL;
1213
0
  }
1214
1215
0
  return lwnurbscurve_as_lwgeom(curve);
1216
0
}
1217
1218
/**
1219
 * Register a parsed geometry and apply an SRID.
1220
 *
1221
 * If `geom` is non-NULL, sets its SRID to `srid` when `srid` is not SRID_UNKNOWN and
1222
 * is within the valid range; otherwise sets the geometry SRID to SRID_UNKNOWN.
1223
 * Stores the geometry pointer in the global parser result object.
1224
 *
1225
 * @param geom Parsed geometry to register (may be NULL — function returns without action).
1226
 * @param srid Spatial reference identifier to assign to the geometry.
1227
 */
1228
void
1229
wkt_parser_geometry_new(LWGEOM *geom, int32_t srid)
1230
683
{
1231
683
  LWDEBUG(4,"entered");
1232
683
  LWDEBUGF(4,"geom %p",geom);
1233
683
  LWDEBUGF(4,"srid %d",srid);
1234
1235
683
  if ( geom == NULL )
1236
0
  {
1237
0
    lwerror("Parsed geometry is null!");
1238
0
    return;
1239
0
  }
1240
1241
683
  if ( srid != SRID_UNKNOWN && srid <= SRID_MAXIMUM )
1242
41
    lwgeom_set_srid(geom, srid);
1243
642
  else
1244
642
    lwgeom_set_srid(geom, SRID_UNKNOWN);
1245
1246
683
  global_parser_result.geom = geom;
1247
683
}
1248
1249
void lwgeom_parser_result_init(LWGEOM_PARSER_RESULT *parser_result)
1250
2.67k
{
1251
2.67k
  memset(parser_result, 0, sizeof(LWGEOM_PARSER_RESULT));
1252
2.67k
}
1253
1254
1255
void lwgeom_parser_result_free(LWGEOM_PARSER_RESULT *parser_result)
1256
0
{
1257
0
  if ( parser_result->geom )
1258
0
  {
1259
0
    lwgeom_free(parser_result->geom);
1260
0
    parser_result->geom = 0;
1261
0
  }
1262
0
  if ( parser_result->serialized_lwgeom )
1263
0
  {
1264
0
    lwfree(parser_result->serialized_lwgeom );
1265
0
    parser_result->serialized_lwgeom = 0;
1266
0
  }
1267
  /* We don't free parser_result->message because
1268
     it is a const *char */
1269
0
}
1270
1271
/*
1272
* Public function used for easy access to the parser.
1273
*/
1274
LWGEOM *lwgeom_from_wkt(const char *wkt, const char check)
1275
2.67k
{
1276
2.67k
  LWGEOM_PARSER_RESULT r;
1277
1278
2.67k
  if( LW_FAILURE == lwgeom_parse_wkt(&r, wkt, check) )
1279
2.02k
  {
1280
2.02k
    lwerror("%s", r.message);
1281
2.02k
    return NULL;
1282
2.02k
  }
1283
1284
648
  return r.geom;
1285
2.67k
}