PostGIS  3.4.0dev-r@@SVN_REVISION@@
lwgeom.c
Go to the documentation of this file.
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) 2001-2006 Refractions Research Inc.
22  * Copyright (C) 2017-2018 Daniel Baston <dbaston@gmail.com>
23  *
24  **********************************************************************/
25 
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 
31 #include "liblwgeom_internal.h"
32 #include "lwgeom_log.h"
33 
34 #define out_stack_size 32
35 
37 void
39 {
40  LWCOLLECTION *coll;
41  uint32_t i;
42 
43  switch (lwgeom->type)
44  {
45  case POLYGONTYPE:
46  lwpoly_force_clockwise((LWPOLY *)lwgeom);
47  return;
48 
49  case TRIANGLETYPE:
51  return;
52 
53  /* Not handle POLYHEDRALSURFACE and TIN
54  as they are supposed to be well oriented */
55  case MULTIPOLYGONTYPE:
56  case COLLECTIONTYPE:
57  coll = (LWCOLLECTION *)lwgeom;
58  for (i=0; i<coll->ngeoms; i++)
59  lwgeom_force_clockwise(coll->geoms[i]);
60  return;
61  }
62 }
63 
65 int
67 {
68  switch (lwgeom->type)
69  {
70  case POLYGONTYPE:
71  return lwpoly_is_clockwise((LWPOLY *)lwgeom);
72 
73  case TRIANGLETYPE:
74  return lwtriangle_is_clockwise((LWTRIANGLE *)lwgeom);
75 
76  case MULTIPOLYGONTYPE:
77  case COLLECTIONTYPE:
78  {
79  uint32_t i;
80  LWCOLLECTION* coll = (LWCOLLECTION *)lwgeom;
81 
82  for (i=0; i < coll->ngeoms; i++)
83  if (!lwgeom_is_clockwise(coll->geoms[i]))
84  return LW_FALSE;
85  return LW_TRUE;
86  }
87  default:
88  return LW_TRUE;
89  return LW_FALSE;
90  }
91 }
92 
93 LWGEOM *
94 lwgeom_reverse(const LWGEOM *geom)
95 {
96  LWGEOM *geomout = lwgeom_clone_deep(geom);
97  lwgeom_reverse_in_place(geomout);
98  return geomout;
99 }
100 
102 void
104 {
105  uint32_t i;
106  LWCOLLECTION *col;
107  if (!geom)
108  return;
109 
110  switch (geom->type)
111  {
112  case MULTIPOINTTYPE:
113  case POINTTYPE:
114  {
115  return;
116  }
117  case TRIANGLETYPE:
118  case CIRCSTRINGTYPE:
119  case LINETYPE:
120  {
121  LWLINE *line = (LWLINE *)(geom);
123  return;
124  }
125  case POLYGONTYPE:
126  {
127  LWPOLY *poly = (LWPOLY *)(geom);
128  if (!poly->rings)
129  return;
130  uint32_t r;
131  for (r = 0; r < poly->nrings; r++)
133  return;
134  }
135  /* CompoundCurve needs to also reverse the sub-geometries */
136  /* so that the end-points remain coincident */
137  case COMPOUNDTYPE:
138  {
139  uint32_t ngeoms;
140  col = (LWCOLLECTION *)(geom);
141  if (!col->geoms)
142  return;
143  ngeoms = col->ngeoms;
144  for (i=0; i<ngeoms; i++)
146  for (i=0; i<col->ngeoms/2; i++) {
147  LWGEOM* tmp = col->geoms[i];
148  col->geoms[i] = col->geoms[ngeoms-i-1];
149  col->geoms[ngeoms-i-1] = tmp;
150  }
151  return;
152  }
153  case MULTICURVETYPE:
154  case MULTILINETYPE:
155  case MULTIPOLYGONTYPE:
156  case MULTISURFACETYPE:
158  case TINTYPE:
159  case COLLECTIONTYPE:
160  case CURVEPOLYTYPE:
161  {
162  col = (LWCOLLECTION *)(geom);
163  if (!col->geoms)
164  return;
165  for (i=0; i<col->ngeoms; i++)
167  return;
168  }
169  default:
170  {
171  lwerror("%s: Unknown geometry type: %s", __func__, lwtype_name(geom->type));
172  return;
173  }
174 
175  }
176 }
177 
178 LWLINE *
179 lwgeom_as_lwline(const LWGEOM *lwgeom)
180 {
181  if ( lwgeom == NULL ) return NULL;
182  if ( lwgeom->type == LINETYPE )
183  return (LWLINE *)lwgeom;
184  else return NULL;
185 }
186 
187 LWCIRCSTRING *
189 {
190  if ( lwgeom == NULL ) return NULL;
191  if ( lwgeom->type == CIRCSTRINGTYPE )
192  return (LWCIRCSTRING *)lwgeom;
193  else return NULL;
194 }
195 
196 LWCOMPOUND *
198 {
199  if ( lwgeom == NULL ) return NULL;
200  if ( lwgeom->type == COMPOUNDTYPE )
201  return (LWCOMPOUND *)lwgeom;
202  else return NULL;
203 }
204 
205 LWCURVEPOLY *
207 {
208  if ( lwgeom == NULL ) return NULL;
209  if ( lwgeom->type == CURVEPOLYTYPE )
210  return (LWCURVEPOLY *)lwgeom;
211  else return NULL;
212 }
213 
214 LWPOLY *
215 lwgeom_as_lwpoly(const LWGEOM *lwgeom)
216 {
217  if ( lwgeom == NULL ) return NULL;
218  if ( lwgeom->type == POLYGONTYPE )
219  return (LWPOLY *)lwgeom;
220  else return NULL;
221 }
222 
223 LWTRIANGLE *
225 {
226  if ( lwgeom == NULL ) return NULL;
227  if ( lwgeom->type == TRIANGLETYPE )
228  return (LWTRIANGLE *)lwgeom;
229  else return NULL;
230 }
231 
232 LWCOLLECTION *
234 {
235  if ( lwgeom == NULL ) return NULL;
236  if ( lwgeom_is_collection(lwgeom) )
237  return (LWCOLLECTION*)lwgeom;
238  else return NULL;
239 }
240 
241 LWMPOINT *
243 {
244  if ( lwgeom == NULL ) return NULL;
245  if ( lwgeom->type == MULTIPOINTTYPE )
246  return (LWMPOINT *)lwgeom;
247  else return NULL;
248 }
249 
250 LWMLINE *
251 lwgeom_as_lwmline(const LWGEOM *lwgeom)
252 {
253  if ( lwgeom == NULL ) return NULL;
254  if ( lwgeom->type == MULTILINETYPE )
255  return (LWMLINE *)lwgeom;
256  else return NULL;
257 }
258 
259 LWMPOLY *
260 lwgeom_as_lwmpoly(const LWGEOM *lwgeom)
261 {
262  if ( lwgeom == NULL ) return NULL;
263  if ( lwgeom->type == MULTIPOLYGONTYPE )
264  return (LWMPOLY *)lwgeom;
265  else return NULL;
266 }
267 
268 LWPSURFACE *
270 {
271  if ( lwgeom->type == POLYHEDRALSURFACETYPE )
272  return (LWPSURFACE *)lwgeom;
273  else return NULL;
274 }
275 
276 LWTIN *
277 lwgeom_as_lwtin(const LWGEOM *lwgeom)
278 {
279  if ( lwgeom->type == TINTYPE )
280  return (LWTIN *)lwgeom;
281  else return NULL;
282 }
283 
285 {
286  return (LWGEOM *)obj;
287 }
288 
290 {
291  return (LWGEOM *)obj;
292 }
293 
295 {
296  if ( obj == NULL ) return NULL;
297  return (LWGEOM *)obj;
298 }
300 {
301  if ( obj == NULL ) return NULL;
302  return (LWGEOM *)obj;
303 }
305 {
306  if ( obj == NULL ) return NULL;
307  return (LWGEOM *)obj;
308 }
310 {
311  if ( obj == NULL ) return NULL;
312  return (LWGEOM *)obj;
313 }
315 {
316  if ( obj == NULL ) return NULL;
317  return (LWGEOM *)obj;
318 }
320 {
321  if ( obj == NULL ) return NULL;
322  return (LWGEOM *)obj;
323 }
325 {
326  if ( obj == NULL ) return NULL;
327  return (LWGEOM *)obj;
328 }
330 {
331  if ( obj == NULL ) return NULL;
332  return (LWGEOM *)obj;
333 }
335 {
336  if ( obj == NULL ) return NULL;
337  return (LWGEOM *)obj;
338 }
340 {
341  if ( obj == NULL ) return NULL;
342  return (LWGEOM *)obj;
343 }
345 {
346  if ( obj == NULL ) return NULL;
347  return (LWGEOM *)obj;
348 }
349 
350 
354 uint8_t MULTITYPE[NUMTYPES] =
355 {
356  0,
357  MULTIPOINTTYPE, /* 1 */
358  MULTILINETYPE, /* 2 */
359  MULTIPOLYGONTYPE, /* 3 */
360  0,0,0,0,
361  MULTICURVETYPE, /* 8 */
362  MULTICURVETYPE, /* 9 */
363  MULTISURFACETYPE, /* 10 */
364  POLYHEDRALSURFACETYPE, /* 11 */
365  0, 0,
366  TINTYPE, /* 14 */
367  0
368 };
369 
370 uint8_t lwtype_multitype(uint8_t type)
371 {
372  if (type > 15) return 0;
373  return MULTITYPE[type];
374 }
375 
379 LWGEOM *
380 lwgeom_as_multi(const LWGEOM *lwgeom)
381 {
382  LWGEOM **ogeoms;
383  LWGEOM *ogeom = NULL;
384  GBOX *box = NULL;
385  int type;
386 
387  type = lwgeom->type;
388 
389  if ( ! MULTITYPE[type] ) return lwgeom_clone(lwgeom);
390 
391  if( lwgeom_is_empty(lwgeom) )
392  {
394  MULTITYPE[type],
395  lwgeom->srid,
396  FLAGS_GET_Z(lwgeom->flags),
397  FLAGS_GET_M(lwgeom->flags)
398  );
399  }
400  else
401  {
402  ogeoms = lwalloc(sizeof(LWGEOM*));
403  ogeoms[0] = lwgeom_clone(lwgeom);
404 
405  /* Sub-geometries are not allowed to have bboxes or SRIDs, move the bbox to the collection */
406  box = ogeoms[0]->bbox;
407  ogeoms[0]->bbox = NULL;
408  ogeoms[0]->srid = SRID_UNKNOWN;
409 
410  ogeom = (LWGEOM *)lwcollection_construct(MULTITYPE[type], lwgeom->srid, box, 1, ogeoms);
411  }
412 
413  return ogeom;
414 }
415 
419 LWGEOM *
420 lwgeom_as_curve(const LWGEOM *lwgeom)
421 {
422  LWGEOM *ogeom;
423  int type = lwgeom->type;
424  /*
425  int hasz = FLAGS_GET_Z(lwgeom->flags);
426  int hasm = FLAGS_GET_M(lwgeom->flags);
427  int32_t srid = lwgeom->srid;
428  */
429 
430  switch(type)
431  {
432  case LINETYPE:
433  /* turn to COMPOUNDCURVE */
434  ogeom = (LWGEOM*)lwcompound_construct_from_lwline((LWLINE*)lwgeom);
435  break;
436  case POLYGONTYPE:
438  break;
439  case MULTILINETYPE:
440  /* turn to MULTICURVE */
441  ogeom = lwgeom_clone(lwgeom);
442  ogeom->type = MULTICURVETYPE;
443  break;
444  case MULTIPOLYGONTYPE:
445  /* turn to MULTISURFACE */
446  ogeom = lwgeom_clone(lwgeom);
447  ogeom->type = MULTISURFACETYPE;
448  break;
449  case COLLECTIONTYPE:
450  default:
451  ogeom = lwgeom_clone(lwgeom);
452  break;
453  }
454 
455  /* TODO: copy bbox from input geom ? */
456 
457  return ogeom;
458 }
459 
460 
467 void
469 {
470  if ( ! lwgeom )
471  lwerror("lwgeom_release: someone called on 0x0");
472 
473  LWDEBUGF(3, "releasing type %s", lwtype_name(lwgeom->type));
474 
475  /* Drop bounding box (always a copy) */
476  if ( lwgeom->bbox )
477  {
478  LWDEBUGF(3, "lwgeom_release: releasing bbox. %p", lwgeom->bbox);
479  lwfree(lwgeom->bbox);
480  }
481  lwfree(lwgeom);
482 
483 }
484 
485 
486 /* @brief Clone LWGEOM object. Serialized point lists are not copied.
487  *
488  * @see ptarray_clone
489  */
490 LWGEOM *
491 lwgeom_clone(const LWGEOM *lwgeom)
492 {
493  LWDEBUGF(2, "lwgeom_clone called with %p, %s",
494  lwgeom, lwtype_name(lwgeom->type));
495 
496  switch (lwgeom->type)
497  {
498  case POINTTYPE:
499  return (LWGEOM *)lwpoint_clone((LWPOINT *)lwgeom);
500  case LINETYPE:
501  return (LWGEOM *)lwline_clone((LWLINE *)lwgeom);
502  case CIRCSTRINGTYPE:
503  return (LWGEOM *)lwcircstring_clone((LWCIRCSTRING *)lwgeom);
504  case POLYGONTYPE:
505  return (LWGEOM *)lwpoly_clone((LWPOLY *)lwgeom);
506  case TRIANGLETYPE:
507  return (LWGEOM *)lwtriangle_clone((LWTRIANGLE *)lwgeom);
508  case COMPOUNDTYPE:
509  case CURVEPOLYTYPE:
510  case MULTICURVETYPE:
511  case MULTISURFACETYPE:
512  case MULTIPOINTTYPE:
513  case MULTILINETYPE:
514  case MULTIPOLYGONTYPE:
516  case TINTYPE:
517  case COLLECTIONTYPE:
518  return (LWGEOM *)lwcollection_clone((LWCOLLECTION *)lwgeom);
519  default:
520  lwerror("lwgeom_clone: Unknown geometry type: %s", lwtype_name(lwgeom->type));
521  return NULL;
522  }
523 }
524 
528 LWGEOM *
529 lwgeom_clone_deep(const LWGEOM *lwgeom)
530 {
531  LWDEBUGF(2, "lwgeom_clone called with %p, %s",
532  lwgeom, lwtype_name(lwgeom->type));
533 
534  switch (lwgeom->type)
535  {
536  case POINTTYPE:
537  case LINETYPE:
538  case CIRCSTRINGTYPE:
539  case TRIANGLETYPE:
540  return (LWGEOM *)lwline_clone_deep((LWLINE *)lwgeom);
541  case POLYGONTYPE:
542  return (LWGEOM *)lwpoly_clone_deep((LWPOLY *)lwgeom);
543  case COMPOUNDTYPE:
544  case CURVEPOLYTYPE:
545  case MULTICURVETYPE:
546  case MULTISURFACETYPE:
547  case MULTIPOINTTYPE:
548  case MULTILINETYPE:
549  case MULTIPOLYGONTYPE:
551  case TINTYPE:
552  case COLLECTIONTYPE:
553  return (LWGEOM *)lwcollection_clone_deep((LWCOLLECTION *)lwgeom);
554  default:
555  lwerror("lwgeom_clone_deep: Unknown geometry type: %s", lwtype_name(lwgeom->type));
556  return NULL;
557  }
558 }
559 
560 
564 char*
565 lwgeom_to_ewkt(const LWGEOM *lwgeom)
566 {
567  char* wkt = NULL;
568  size_t wkt_size = 0;
569 
570  wkt = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, 12, &wkt_size);
571 
572  if ( ! wkt )
573  {
574  lwerror("Error writing geom %p to WKT", lwgeom);
575  }
576 
577  return wkt;
578 }
579 
590 char
591 lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
592 {
593  LWDEBUGF(2, "lwgeom_same(%s, %s) called",
594  lwtype_name(lwgeom1->type),
595  lwtype_name(lwgeom2->type));
596 
597  if ( lwgeom1->type != lwgeom2->type )
598  {
599  LWDEBUG(3, " type differ");
600 
601  return LW_FALSE;
602  }
603 
604  if ( FLAGS_GET_ZM(lwgeom1->flags) != FLAGS_GET_ZM(lwgeom2->flags) )
605  {
606  LWDEBUG(3, " ZM flags differ");
607 
608  return LW_FALSE;
609  }
610 
611  /* Check boxes if both already computed */
612  if ( lwgeom1->bbox && lwgeom2->bbox )
613  {
614  /*lwnotice("bbox1:%p, bbox2:%p", lwgeom1->bbox, lwgeom2->bbox);*/
615  if ( ! gbox_same(lwgeom1->bbox, lwgeom2->bbox) )
616  {
617  LWDEBUG(3, " bounding boxes differ");
618 
619  return LW_FALSE;
620  }
621  }
622 
623  /* geoms have same type, invoke type-specific function */
624  switch (lwgeom1->type)
625  {
626  case POINTTYPE:
627  return lwpoint_same((LWPOINT *)lwgeom1,
628  (LWPOINT *)lwgeom2);
629  case LINETYPE:
630  return lwline_same((LWLINE *)lwgeom1,
631  (LWLINE *)lwgeom2);
632  case POLYGONTYPE:
633  return lwpoly_same((LWPOLY *)lwgeom1,
634  (LWPOLY *)lwgeom2);
635  case TRIANGLETYPE:
636  return lwtriangle_same((LWTRIANGLE *)lwgeom1,
637  (LWTRIANGLE *)lwgeom2);
638  case CIRCSTRINGTYPE:
639  return lwcircstring_same((LWCIRCSTRING *)lwgeom1,
640  (LWCIRCSTRING *)lwgeom2);
641  case MULTIPOINTTYPE:
642  case MULTILINETYPE:
643  case MULTIPOLYGONTYPE:
644  case MULTICURVETYPE:
645  case MULTISURFACETYPE:
646  case COMPOUNDTYPE:
647  case CURVEPOLYTYPE:
649  case TINTYPE:
650  case COLLECTIONTYPE:
651  return lwcollection_same((LWCOLLECTION *)lwgeom1,
652  (LWCOLLECTION *)lwgeom2);
653  default:
654  lwerror("lwgeom_same: unsupported geometry type: %s",
655  lwtype_name(lwgeom1->type));
656  return LW_FALSE;
657  }
658 
659 }
660 
661 int
662 lwpoint_inside_circle(const LWPOINT *p, double cx, double cy, double rad)
663 {
664  const POINT2D *pt;
665  POINT2D center;
666 
667  if ( ! p || ! p->point )
668  return LW_FALSE;
669 
670  pt = getPoint2d_cp(p->point, 0);
671 
672  center.x = cx;
673  center.y = cy;
674 
675  if ( distance2d_pt_pt(pt, &center) < rad )
676  return LW_TRUE;
677 
678  return LW_FALSE;
679 }
680 
681 void
683 {
684  if ( lwgeom->bbox ) lwfree(lwgeom->bbox);
685  lwgeom->bbox = NULL;
686  FLAGS_SET_BBOX(lwgeom->flags, 0);
687 }
688 
694 void
696 {
697  /* an empty LWGEOM has no bbox */
698  if ( lwgeom_is_empty(lwgeom) ) return;
699 
700  if ( lwgeom->bbox ) return;
701  FLAGS_SET_BBOX(lwgeom->flags, 1);
702  lwgeom->bbox = gbox_new(lwgeom->flags);
703  lwgeom_calculate_gbox(lwgeom, lwgeom->bbox);
704 }
705 
706 void
708 {
709  lwgeom_drop_bbox(lwgeom);
710  lwgeom_add_bbox(lwgeom);
711 }
712 
713 void
715 {
716  if ( lwgeom_is_empty(lwgeom) ) return;
717 
718  FLAGS_SET_BBOX(lwgeom->flags, 1);
719 
720  if ( ! ( gbox || lwgeom->bbox ) )
721  {
722  lwgeom->bbox = gbox_new(lwgeom->flags);
723  lwgeom_calculate_gbox(lwgeom, lwgeom->bbox);
724  }
725  else if ( gbox && ! lwgeom->bbox )
726  {
727  lwgeom->bbox = gbox_clone(gbox);
728  }
729 
730  if ( lwgeom_is_collection(lwgeom) )
731  {
732  uint32_t i;
733  LWCOLLECTION *lwcol = (LWCOLLECTION*)lwgeom;
734 
735  for ( i = 0; i < lwcol->ngeoms; i++ )
736  {
737  lwgeom_add_bbox_deep(lwcol->geoms[i], lwgeom->bbox);
738  }
739  }
740 }
741 
742 const GBOX *
744 {
745  /* add it if not already there */
746  lwgeom_add_bbox((LWGEOM *)lwg);
747  return lwg->bbox;
748 }
749 
750 
755 int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
756 {
757  gbox->flags = lwgeom->flags;
758  if( FLAGS_GET_GEODETIC(lwgeom->flags) )
759  return lwgeom_calculate_gbox_geodetic(lwgeom, gbox);
760  else
761  return lwgeom_calculate_gbox_cartesian(lwgeom, gbox);
762 }
763 
764 void
766 {
767  lwgeom->srid = SRID_UNKNOWN; /* TODO: To be changed to SRID_UNKNOWN */
768 }
769 
770 LWGEOM *
771 lwgeom_segmentize2d(const LWGEOM *lwgeom, double dist)
772 {
773  switch (lwgeom->type)
774  {
775  case LINETYPE:
776  return (LWGEOM *)lwline_segmentize2d((LWLINE *)lwgeom,
777  dist);
778  case POLYGONTYPE:
779  return (LWGEOM *)lwpoly_segmentize2d((LWPOLY *)lwgeom,
780  dist);
781  case MULTILINETYPE:
782  case MULTIPOLYGONTYPE:
783  case COLLECTIONTYPE:
785  (LWCOLLECTION *)lwgeom, dist);
786 
787  default:
788  return lwgeom_clone(lwgeom);
789  }
790 }
791 
792 LWGEOM*
794 {
795  return lwgeom_force_dims(geom, 0, 0, 0, 0);
796 }
797 
798 LWGEOM*
799 lwgeom_force_3dz(const LWGEOM *geom, double zval)
800 {
801  return lwgeom_force_dims(geom, 1, 0, zval, 0);
802 }
803 
804 LWGEOM*
805 lwgeom_force_3dm(const LWGEOM *geom, double mval)
806 {
807  return lwgeom_force_dims(geom, 0, 1, 0, mval);
808 }
809 
810 LWGEOM*
811 lwgeom_force_4d(const LWGEOM *geom, double zval, double mval)
812 {
813  return lwgeom_force_dims(geom, 1, 1, zval, mval);
814 }
815 
816 LWGEOM*
817 lwgeom_force_dims(const LWGEOM *geom, int hasz, int hasm, double zval, double mval)
818 {
819  if (!geom)
820  return NULL;
821  switch(geom->type)
822  {
823  case POINTTYPE:
824  return lwpoint_as_lwgeom(lwpoint_force_dims((LWPOINT*)geom, hasz, hasm, zval, mval));
825  case CIRCSTRINGTYPE:
826  case LINETYPE:
827  case TRIANGLETYPE:
828  return lwline_as_lwgeom(lwline_force_dims((LWLINE*)geom, hasz, hasm, zval, mval));
829  case POLYGONTYPE:
830  return lwpoly_as_lwgeom(lwpoly_force_dims((LWPOLY*)geom, hasz, hasm, zval, mval));
831  case COMPOUNDTYPE:
832  case CURVEPOLYTYPE:
833  case MULTICURVETYPE:
834  case MULTISURFACETYPE:
835  case MULTIPOINTTYPE:
836  case MULTILINETYPE:
837  case MULTIPOLYGONTYPE:
839  case TINTYPE:
840  case COLLECTIONTYPE:
841  return lwcollection_as_lwgeom(lwcollection_force_dims((LWCOLLECTION*)geom, hasz, hasm, zval, mval));
842  default:
843  lwerror("lwgeom_force_2d: unsupported geom type: %s", lwtype_name(geom->type));
844  return NULL;
845  }
846 }
847 
848 LWGEOM*
849 lwgeom_force_sfs(LWGEOM *geom, int version)
850 {
851  LWCOLLECTION *col;
852  uint32_t i;
853  LWGEOM *g;
854 
855  /* SFS 1.2 version */
856  if (version == 120)
857  {
858  switch(geom->type)
859  {
860  /* SQL/MM types */
861  case CIRCSTRINGTYPE:
862  case COMPOUNDTYPE:
863  case CURVEPOLYTYPE:
864  case MULTICURVETYPE:
865  case MULTISURFACETYPE:
866  return lwgeom_stroke(geom, 32);
867 
868  case COLLECTIONTYPE:
869  col = (LWCOLLECTION*)geom;
870  for ( i = 0; i < col->ngeoms; i++ )
871  col->geoms[i] = lwgeom_force_sfs((LWGEOM*)col->geoms[i], version);
872 
873  return lwcollection_as_lwgeom((LWCOLLECTION*)geom);
874 
875  default:
876  return (LWGEOM *)geom;
877  }
878  }
879 
880 
881  /* SFS 1.1 version */
882  switch(geom->type)
883  {
884  /* SQL/MM types */
885  case CIRCSTRINGTYPE:
886  case COMPOUNDTYPE:
887  case CURVEPOLYTYPE:
888  case MULTICURVETYPE:
889  case MULTISURFACETYPE:
890  return lwgeom_stroke(geom, 32);
891 
892  /* SFS 1.2 types */
893  case TRIANGLETYPE:
894  g = lwpoly_as_lwgeom(lwpoly_from_lwlines((LWLINE*)geom, 0, NULL));
895  lwgeom_free(geom);
896  return g;
897 
898  case TINTYPE:
899  col = (LWCOLLECTION*) geom;
900  for ( i = 0; i < col->ngeoms; i++ )
901  {
902  g = lwpoly_as_lwgeom(lwpoly_from_lwlines((LWLINE*)col->geoms[i], 0, NULL));
903  lwgeom_free(col->geoms[i]);
904  col->geoms[i] = g;
905  }
906  col->type = COLLECTIONTYPE;
907  return lwmpoly_as_lwgeom((LWMPOLY*)geom);
908 
910  geom->type = COLLECTIONTYPE;
911  return (LWGEOM *)geom;
912 
913  /* Collection */
914  case COLLECTIONTYPE:
915  col = (LWCOLLECTION*)geom;
916  for ( i = 0; i < col->ngeoms; i++ )
917  col->geoms[i] = lwgeom_force_sfs((LWGEOM*)col->geoms[i], version);
918 
919  return lwcollection_as_lwgeom((LWCOLLECTION*)geom);
920 
921  default:
922  return (LWGEOM *)geom;
923  }
924 }
925 
926 int32_t
928 {
929  if ( ! geom ) return SRID_UNKNOWN;
930  return geom->srid;
931 }
932 
933 int
934 lwgeom_has_z(const LWGEOM *geom)
935 {
936  if ( ! geom ) return LW_FALSE;
937  return FLAGS_GET_Z(geom->flags);
938 }
939 
940 int
941 lwgeom_has_m(const LWGEOM *geom)
942 {
943  if ( ! geom ) return LW_FALSE;
944  return FLAGS_GET_M(geom->flags);
945 }
946 
947 int
949 {
950  if ( ! geom ) return LW_FALSE;
951  return FLAGS_GET_GEODETIC(geom->flags);
952 }
953 
954 int
955 lwgeom_ndims(const LWGEOM *geom)
956 {
957  if ( ! geom ) return 0;
958  return FLAGS_NDIMS(geom->flags);
959 }
960 
961 
962 
963 void
965 {
966  LWPOINT *pt;
967  LWLINE *ln;
968  LWPOLY *ply;
969  LWCOLLECTION *col;
970  uint32_t i;
971 
973  if ( geom->bbox )
975 
976  switch(geom->type)
977  {
978  case POINTTYPE:
979  pt = (LWPOINT*)geom;
980  if ( pt->point )
982  break;
983  case LINETYPE:
984  ln = (LWLINE*)geom;
985  if ( ln->points )
987  break;
988  case POLYGONTYPE:
989  ply = (LWPOLY*)geom;
990  for ( i = 0; i < ply->nrings; i++ )
991  FLAGS_SET_GEODETIC(ply->rings[i]->flags, value);
992  break;
993  case MULTIPOINTTYPE:
994  case MULTILINETYPE:
995  case MULTIPOLYGONTYPE:
996  case COLLECTIONTYPE:
997  col = (LWCOLLECTION*)geom;
998  for ( i = 0; i < col->ngeoms; i++ )
999  lwgeom_set_geodetic(col->geoms[i], value);
1000  break;
1001  default:
1002  lwerror("lwgeom_set_geodetic: unsupported geom type: %s", lwtype_name(geom->type));
1003  return;
1004  }
1005 }
1006 
1007 void
1009 {
1010  uint32_t i;
1011  switch (lwgeom->type)
1012  {
1013  LWPOINT *point;
1014  LWLINE *line;
1015  LWPOLY *poly;
1016  LWTRIANGLE *triangle;
1017  LWCOLLECTION *coll;
1018 
1019  case POINTTYPE:
1020  point = (LWPOINT *)lwgeom;
1022  return;
1023  case LINETYPE:
1024  line = (LWLINE *)lwgeom;
1026  return;
1027  case POLYGONTYPE:
1028  poly = (LWPOLY *)lwgeom;
1029  for (i=0; i<poly->nrings; i++)
1030  ptarray_longitude_shift(poly->rings[i]);
1031  return;
1032  case TRIANGLETYPE:
1033  triangle = (LWTRIANGLE *)lwgeom;
1034  ptarray_longitude_shift(triangle->points);
1035  return;
1036  case MULTIPOINTTYPE:
1037  case MULTILINETYPE:
1038  case MULTIPOLYGONTYPE:
1039  case POLYHEDRALSURFACETYPE:
1040  case TINTYPE:
1041  case COLLECTIONTYPE:
1042  coll = (LWCOLLECTION *)lwgeom;
1043  for (i=0; i<coll->ngeoms; i++)
1044  lwgeom_longitude_shift(coll->geoms[i]);
1045  return;
1046  default:
1047  lwerror("lwgeom_longitude_shift: unsupported geom type: %s",
1048  lwtype_name(lwgeom->type));
1049  }
1050 }
1051 
1052 int
1054 {
1055  int type = geom->type;
1056 
1057  if( lwgeom_is_empty(geom) )
1058  return LW_FALSE;
1059 
1060  /* Test linear types for closure */
1061  switch (type)
1062  {
1063  case LINETYPE:
1064  return lwline_is_closed((LWLINE*)geom);
1065  case POLYGONTYPE:
1066  return lwpoly_is_closed((LWPOLY*)geom);
1067  case CIRCSTRINGTYPE:
1068  return lwcircstring_is_closed((LWCIRCSTRING*)geom);
1069  case COMPOUNDTYPE:
1070  return lwcompound_is_closed((LWCOMPOUND*)geom);
1071  case TINTYPE:
1072  return lwtin_is_closed((LWTIN*)geom);
1073  case POLYHEDRALSURFACETYPE:
1074  return lwpsurface_is_closed((LWPSURFACE*)geom);
1075  }
1076 
1077  /* Recurse into collections and see if anything is not closed */
1078  if ( lwgeom_is_collection(geom) )
1079  {
1080  LWCOLLECTION *col = lwgeom_as_lwcollection(geom);
1081  uint32_t i;
1082  int closed;
1083  for ( i = 0; i < col->ngeoms; i++ )
1084  {
1085  closed = lwgeom_is_closed(col->geoms[i]);
1086  if ( ! closed )
1087  return LW_FALSE;
1088  }
1089  return LW_TRUE;
1090  }
1091 
1092  /* All non-linear non-collection types we will call closed */
1093  return LW_TRUE;
1094 }
1095 
1096 int
1098 {
1099  if( ! geom ) return LW_FALSE;
1100  return lwtype_is_collection(geom->type);
1101 }
1102 
1104 int
1106 {
1107  switch (type)
1108  {
1109  case MULTIPOINTTYPE:
1110  case MULTILINETYPE:
1111  case MULTIPOLYGONTYPE:
1112  case COLLECTIONTYPE:
1113  case CURVEPOLYTYPE:
1114  case COMPOUNDTYPE:
1115  case MULTICURVETYPE:
1116  case MULTISURFACETYPE:
1117  case POLYHEDRALSURFACETYPE:
1118  case TINTYPE:
1119  return LW_TRUE;
1120  break;
1121 
1122  default:
1123  return LW_FALSE;
1124  }
1125 }
1126 
1130 uint32_t
1132 {
1133  switch (type)
1134  {
1135  case POINTTYPE:
1136  return MULTIPOINTTYPE;
1137  case LINETYPE:
1138  return MULTILINETYPE;
1139  case POLYGONTYPE:
1140  return MULTIPOLYGONTYPE;
1141  case CIRCSTRINGTYPE:
1142  return MULTICURVETYPE;
1143  case COMPOUNDTYPE:
1144  return MULTICURVETYPE;
1145  case CURVEPOLYTYPE:
1146  return MULTISURFACETYPE;
1147  case TRIANGLETYPE:
1148  return TINTYPE;
1149  default:
1150  return COLLECTIONTYPE;
1151  }
1152 }
1153 
1154 
1155 void lwgeom_free(LWGEOM *lwgeom)
1156 {
1157 
1158  /* There's nothing here to free... */
1159  if( ! lwgeom ) return;
1160 
1161  LWDEBUGF(5,"freeing a %s",lwtype_name(lwgeom->type));
1162 
1163  switch (lwgeom->type)
1164  {
1165  case POINTTYPE:
1166  lwpoint_free((LWPOINT *)lwgeom);
1167  break;
1168  case LINETYPE:
1169  lwline_free((LWLINE *)lwgeom);
1170  break;
1171  case POLYGONTYPE:
1172  lwpoly_free((LWPOLY *)lwgeom);
1173  break;
1174  case CIRCSTRINGTYPE:
1175  lwcircstring_free((LWCIRCSTRING *)lwgeom);
1176  break;
1177  case TRIANGLETYPE:
1178  lwtriangle_free((LWTRIANGLE *)lwgeom);
1179  break;
1180  case MULTIPOINTTYPE:
1181  lwmpoint_free((LWMPOINT *)lwgeom);
1182  break;
1183  case MULTILINETYPE:
1184  lwmline_free((LWMLINE *)lwgeom);
1185  break;
1186  case MULTIPOLYGONTYPE:
1187  lwmpoly_free((LWMPOLY *)lwgeom);
1188  break;
1189  case POLYHEDRALSURFACETYPE:
1190  lwpsurface_free((LWPSURFACE *)lwgeom);
1191  break;
1192  case TINTYPE:
1193  lwtin_free((LWTIN *)lwgeom);
1194  break;
1195  case CURVEPOLYTYPE:
1196  case COMPOUNDTYPE:
1197  case MULTICURVETYPE:
1198  case MULTISURFACETYPE:
1199  case COLLECTIONTYPE:
1200  lwcollection_free((LWCOLLECTION *)lwgeom);
1201  break;
1202  default:
1203  lwerror("lwgeom_free called with unknown type (%d) %s", lwgeom->type, lwtype_name(lwgeom->type));
1204  }
1205  return;
1206 }
1207 
1208 int lwgeom_needs_bbox(const LWGEOM *geom)
1209 {
1210  assert(geom);
1211  if ( geom->type == POINTTYPE )
1212  {
1213  return LW_FALSE;
1214  }
1215  else if ( geom->type == LINETYPE )
1216  {
1217  if ( lwgeom_count_vertices(geom) <= 2 )
1218  return LW_FALSE;
1219  else
1220  return LW_TRUE;
1221  }
1222  else if ( geom->type == MULTIPOINTTYPE )
1223  {
1224  if ( ((LWCOLLECTION*)geom)->ngeoms == 1 )
1225  return LW_FALSE;
1226  else
1227  return LW_TRUE;
1228  }
1229  else if ( geom->type == MULTILINETYPE )
1230  {
1231  if ( ((LWCOLLECTION*)geom)->ngeoms == 1 && lwgeom_count_vertices(geom) <= 2 )
1232  return LW_FALSE;
1233  else
1234  return LW_TRUE;
1235  }
1236  else
1237  {
1238  return LW_TRUE;
1239  }
1240 }
1241 
1246 uint32_t lwgeom_count_vertices(const LWGEOM *geom)
1247 {
1248  int result = 0;
1249 
1250  /* Null? Zero. */
1251  if( ! geom ) return 0;
1252 
1253  LWDEBUGF(4, "lwgeom_count_vertices got type %s",
1254  lwtype_name(geom->type));
1255 
1256  /* Empty? Zero. */
1257  if( lwgeom_is_empty(geom) ) return 0;
1258 
1259  switch (geom->type)
1260  {
1261  case POINTTYPE:
1262  result = 1;
1263  break;
1264  case TRIANGLETYPE:
1265  case CIRCSTRINGTYPE:
1266  case LINETYPE:
1267  result = lwline_count_vertices((const LWLINE *)geom);
1268  break;
1269  case POLYGONTYPE:
1270  result = lwpoly_count_vertices((const LWPOLY *)geom);
1271  break;
1272  case COMPOUNDTYPE:
1273  case CURVEPOLYTYPE:
1274  case MULTICURVETYPE:
1275  case MULTISURFACETYPE:
1276  case MULTIPOINTTYPE:
1277  case MULTILINETYPE:
1278  case MULTIPOLYGONTYPE:
1279  case POLYHEDRALSURFACETYPE:
1280  case TINTYPE:
1281  case COLLECTIONTYPE:
1283  break;
1284  default:
1285  lwerror("%s: unsupported input geometry type: %s",
1286  __func__, lwtype_name(geom->type));
1287  break;
1288  }
1289  LWDEBUGF(3, "counted %d vertices", result);
1290  return result;
1291 }
1292 
1298 int lwgeom_dimension(const LWGEOM *geom)
1299 {
1300 
1301  /* Null? Zero. */
1302  if( ! geom ) return -1;
1303 
1304  LWDEBUGF(4, "lwgeom_dimension got type %s",
1305  lwtype_name(geom->type));
1306 
1307  /* Empty? Zero. */
1308  /* if( lwgeom_is_empty(geom) ) return 0; */
1309 
1310  switch (geom->type)
1311  {
1312  case POINTTYPE:
1313  case MULTIPOINTTYPE:
1314  return 0;
1315  case CIRCSTRINGTYPE:
1316  case LINETYPE:
1317  case COMPOUNDTYPE:
1318  case MULTICURVETYPE:
1319  case MULTILINETYPE:
1320  return 1;
1321  case TRIANGLETYPE:
1322  case POLYGONTYPE:
1323  case CURVEPOLYTYPE:
1324  case MULTISURFACETYPE:
1325  case MULTIPOLYGONTYPE:
1326  case TINTYPE:
1327  return 2;
1328  case POLYHEDRALSURFACETYPE:
1329  {
1330  /* A closed polyhedral surface contains a volume. */
1331  int closed = lwpsurface_is_closed((LWPSURFACE*)geom);
1332  return ( closed ? 3 : 2 );
1333  }
1334  case COLLECTIONTYPE:
1335  {
1336  int maxdim = 0;
1337  uint32_t i;
1338  LWCOLLECTION *col = (LWCOLLECTION*)geom;
1339  for( i = 0; i < col->ngeoms; i++ )
1340  {
1341  int dim = lwgeom_dimension(col->geoms[i]);
1342  maxdim = ( dim > maxdim ? dim : maxdim );
1343  }
1344  return maxdim;
1345  }
1346  default:
1347  lwerror("%s: unsupported input geometry type: %s",
1348  __func__, lwtype_name(geom->type));
1349  }
1350  return -1;
1351 }
1352 
1356 uint32_t lwgeom_count_rings(const LWGEOM *geom)
1357 {
1358  int result = 0;
1359 
1360  /* Null? Empty? Zero. */
1361  if( ! geom || lwgeom_is_empty(geom) )
1362  return 0;
1363 
1364  switch (geom->type)
1365  {
1366  case POINTTYPE:
1367  case CIRCSTRINGTYPE:
1368  case COMPOUNDTYPE:
1369  case MULTICURVETYPE:
1370  case MULTIPOINTTYPE:
1371  case MULTILINETYPE:
1372  case LINETYPE:
1373  result = 0;
1374  break;
1375  case TRIANGLETYPE:
1376  result = 1;
1377  break;
1378  case POLYGONTYPE:
1379  result = ((LWPOLY *)geom)->nrings;
1380  break;
1381  case CURVEPOLYTYPE:
1382  result = ((LWCURVEPOLY *)geom)->nrings;
1383  break;
1384  case MULTISURFACETYPE:
1385  case MULTIPOLYGONTYPE:
1386  case POLYHEDRALSURFACETYPE:
1387  case TINTYPE:
1388  case COLLECTIONTYPE:
1389  {
1390  LWCOLLECTION *col = (LWCOLLECTION*)geom;
1391  uint32_t i = 0;
1392  for( i = 0; i < col->ngeoms; i++ )
1393  result += lwgeom_count_rings(col->geoms[i]);
1394  break;
1395  }
1396  default:
1397  lwerror("lwgeom_count_rings: unsupported input geometry type: %s", lwtype_name(geom->type));
1398  break;
1399  }
1400  LWDEBUGF(3, "counted %d rings", result);
1401  return result;
1402 }
1403 
1404 int lwgeom_has_srid(const LWGEOM *geom)
1405 {
1406  if ( geom->srid != SRID_UNKNOWN )
1407  return LW_TRUE;
1408 
1409  return LW_FALSE;
1410 }
1411 
1412 
1414 {
1415  uint32_t i;
1416  int dimensionality = 0;
1417  for ( i = 0; i < col->ngeoms; i++ )
1418  {
1419  int d = lwgeom_dimensionality(col->geoms[i]);
1420  if ( d > dimensionality )
1421  dimensionality = d;
1422  }
1423  return dimensionality;
1424 }
1425 
1426 extern int lwgeom_dimensionality(const LWGEOM *geom)
1427 {
1428  int dim;
1429 
1430  LWDEBUGF(3, "lwgeom_dimensionality got type %s",
1431  lwtype_name(geom->type));
1432 
1433  switch (geom->type)
1434  {
1435  case POINTTYPE:
1436  case MULTIPOINTTYPE:
1437  return 0;
1438  break;
1439  case LINETYPE:
1440  case CIRCSTRINGTYPE:
1441  case MULTILINETYPE:
1442  case COMPOUNDTYPE:
1443  case MULTICURVETYPE:
1444  return 1;
1445  break;
1446  case POLYGONTYPE:
1447  case TRIANGLETYPE:
1448  case CURVEPOLYTYPE:
1449  case MULTIPOLYGONTYPE:
1450  case MULTISURFACETYPE:
1451  return 2;
1452  break;
1453 
1454  case POLYHEDRALSURFACETYPE:
1455  case TINTYPE:
1456  dim = lwgeom_is_closed(geom)?3:2;
1457  return dim;
1458  break;
1459 
1460  case COLLECTIONTYPE:
1461  return lwcollection_dimensionality((const LWCOLLECTION *)geom);
1462  break;
1463  default:
1464  lwerror("lwgeom_dimensionality: unsupported input geometry type: %s",
1465  lwtype_name(geom->type));
1466  break;
1467  }
1468  return 0;
1469 }
1470 
1471 extern LWGEOM* lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance)
1472 {
1473  LWGEOM *out = lwgeom_clone_deep(in);
1475  return out;
1476 }
1477 
1479 {
1480  LWCOLLECTION *col;
1481  LWPOLY *poly;
1482  uint32_t i;
1483 
1484  if ( (!in) || lwgeom_is_empty(in) ) return;
1485 
1486  /* TODO: check for lwgeom NOT having the specified dimension ? */
1487 
1488  LWDEBUGF(4, "lwgeom_flip_coordinates, got type: %s",
1489  lwtype_name(in->type));
1490 
1491  switch (in->type)
1492  {
1493  case POINTTYPE:
1494  ptarray_swap_ordinates(lwgeom_as_lwpoint(in)->point, o1, o2);
1495  break;
1496 
1497  case LINETYPE:
1498  ptarray_swap_ordinates(lwgeom_as_lwline(in)->points, o1, o2);
1499  break;
1500 
1501  case CIRCSTRINGTYPE:
1502  ptarray_swap_ordinates(lwgeom_as_lwcircstring(in)->points, o1, o2);
1503  break;
1504 
1505  case POLYGONTYPE:
1506  poly = (LWPOLY *) in;
1507  for (i=0; i<poly->nrings; i++)
1508  {
1509  ptarray_swap_ordinates(poly->rings[i], o1, o2);
1510  }
1511  break;
1512 
1513  case TRIANGLETYPE:
1514  ptarray_swap_ordinates(lwgeom_as_lwtriangle(in)->points, o1, o2);
1515  break;
1516 
1517  case MULTIPOINTTYPE:
1518  case MULTILINETYPE:
1519  case MULTIPOLYGONTYPE:
1520  case COLLECTIONTYPE:
1521  case COMPOUNDTYPE:
1522  case CURVEPOLYTYPE:
1523  case MULTISURFACETYPE:
1524  case MULTICURVETYPE:
1525  case POLYHEDRALSURFACETYPE:
1526  case TINTYPE:
1527  col = (LWCOLLECTION *) in;
1528  for (i=0; i<col->ngeoms; i++)
1529  {
1530  lwgeom_swap_ordinates(col->geoms[i], o1, o2);
1531  }
1532  break;
1533 
1534  default:
1535  lwerror("lwgeom_swap_ordinates: unsupported geometry type: %s",
1536  lwtype_name(in->type));
1537  return;
1538  }
1539 
1540  /* only refresh bbox if X or Y changed */
1541  if ( in->bbox && (o1 < 2 || o2 < 2) )
1542  {
1543  lwgeom_refresh_bbox(in);
1544  }
1545 }
1546 
1547 void lwgeom_set_srid(LWGEOM *geom, int32_t srid)
1548 {
1549  uint32_t i;
1550 
1551  LWDEBUGF(4,"entered with srid=%d",srid);
1552 
1553  geom->srid = srid;
1554 
1555  if ( lwgeom_is_collection(geom) )
1556  {
1557  /* All the children are set to the same SRID value */
1558  LWCOLLECTION *col = lwgeom_as_lwcollection(geom);
1559  for ( i = 0; i < col->ngeoms; i++ )
1560  {
1561  lwgeom_set_srid(col->geoms[i], srid);
1562  }
1563  }
1564 }
1565 
1566 
1567 /**************************************************************/
1568 
1569 static int
1570 cmp_point_x(const void *pa, const void *pb)
1571 {
1572  LWPOINT *p1 = *((LWPOINT **)pa);
1573  LWPOINT *p2 = *((LWPOINT **)pb);
1574 
1575  const POINT2D *pt1 = getPoint2d_cp(p1->point, 0);
1576  const POINT2D *pt2 = getPoint2d_cp(p2->point, 0);
1577 
1578  return (pt1->x > pt2->x) ? 1 : ((pt1->x < pt2->x) ? -1 : 0);
1579 }
1580 
1581 static int
1582 cmp_point_y(const void *pa, const void *pb)
1583 {
1584  LWPOINT *p1 = *((LWPOINT **)pa);
1585  LWPOINT *p2 = *((LWPOINT **)pb);
1586 
1587  const POINT2D *pt1 = getPoint2d_cp(p1->point, 0);
1588  const POINT2D *pt2 = getPoint2d_cp(p2->point, 0);
1589 
1590  return (pt1->y > pt2->y) ? 1 : ((pt1->y < pt2->y) ? -1 : 0);
1591 }
1592 
1593 int
1595 {
1596  int geometry_modified = LW_FALSE;
1597  switch (geom->type)
1598  {
1599  /* No-op! Cannot remove points */
1600  case POINTTYPE:
1601  case TRIANGLETYPE:
1602  return geometry_modified;
1603  case LINETYPE: {
1604  LWLINE *g = (LWLINE *)(geom);
1605  POINTARRAY *pa = g->points;
1606  uint32_t npoints = pa->npoints;
1607  ptarray_remove_repeated_points_in_place(pa, tolerance, 2);
1608  geometry_modified = npoints != pa->npoints;
1609  /* Invalid input, discard the collapsed line */
1610  if (pa->npoints < 2)
1611  {
1612  pa->npoints = 0;
1613  geometry_modified = LW_TRUE;
1614  }
1615  break;
1616  }
1617  case POLYGONTYPE: {
1618  uint32_t j = 0;
1619  LWPOLY *g = (LWPOLY *)(geom);
1620  for (uint32_t i = 0; i < g->nrings; i++)
1621  {
1622  POINTARRAY *pa = g->rings[i];
1623  uint32_t npoints = pa->npoints;
1624  ptarray_remove_repeated_points_in_place(pa, tolerance, 4);
1625  geometry_modified |= npoints != pa->npoints;
1626  /* Drop collapsed rings */
1627  if (pa->npoints < 4)
1628  {
1629  geometry_modified = LW_TRUE;
1630  ptarray_free(pa);
1631  continue;
1632  }
1633  g->rings[j++] = pa;
1634  }
1635  /* Update ring count */
1636  g->nrings = j;
1637  break;
1638  }
1639  case MULTIPOINTTYPE: {
1640  double tolsq = tolerance * tolerance;
1641  LWMPOINT *mpt = (LWMPOINT *)geom;
1642 
1643  for (uint8_t dim = 0; dim < 2; dim++)
1644  {
1645  /* sort by y, then by x - this way the result is sorted by x */
1646  qsort(mpt->geoms, mpt->ngeoms, sizeof(LWPOINT *), dim ? cmp_point_x : cmp_point_y);
1647  for (uint32_t i = 0; i < mpt->ngeoms; i++)
1648  {
1649  if (!mpt->geoms[i])
1650  continue;
1651 
1652  const POINT2D *pti = getPoint2d_cp(mpt->geoms[i]->point, 0);
1653 
1654  /* check upcoming points if they're within tolerance of current one */
1655  for (uint32_t j = i + 1; j < mpt->ngeoms; j++)
1656  {
1657  if (!mpt->geoms[j])
1658  continue;
1659 
1660  const POINT2D *ptj = getPoint2d_cp(mpt->geoms[j]->point, 0);
1661 
1662  /* check that the point is in the strip of tolerance around the point */
1663  if ((dim ? ptj->x - pti->x : ptj->y - pti->y) > tolerance)
1664  break;
1665 
1666  /* remove any upcoming point that is within tolerance circle */
1667  if (distance2d_sqr_pt_pt(pti, ptj) <= tolsq)
1668  {
1669  lwpoint_free(mpt->geoms[j]);
1670  mpt->geoms[j] = NULL;
1671  geometry_modified = LW_TRUE;
1672  }
1673  }
1674  }
1675 
1676  /* compactify array of points */
1677  uint32_t i = 0;
1678  for (uint32_t j = 0; j < mpt->ngeoms; j++)
1679  if (mpt->geoms[j])
1680  mpt->geoms[i++] = mpt->geoms[j];
1681  mpt->ngeoms = i;
1682  }
1683 
1684  break;
1685  }
1686 
1687  case CIRCSTRINGTYPE:
1688  /* Dunno how to handle these, will return untouched */
1689  return geometry_modified;
1690 
1691  /* Can process most multi* types as generic collection */
1692  case MULTILINETYPE:
1693  case MULTIPOLYGONTYPE:
1694  case TINTYPE:
1695  case COLLECTIONTYPE:
1696  /* Curve types we mostly ignore, but allow the linear */
1697  /* portions to be processed by recursing into them */
1698  case MULTICURVETYPE:
1699  case CURVEPOLYTYPE:
1700  case MULTISURFACETYPE:
1701  case COMPOUNDTYPE: {
1702  uint32_t i, j = 0;
1703  LWCOLLECTION *col = (LWCOLLECTION *)(geom);
1704  for (i = 0; i < col->ngeoms; i++)
1705  {
1706  LWGEOM *g = col->geoms[i];
1707  if (!g)
1708  continue;
1709  geometry_modified |= lwgeom_remove_repeated_points_in_place(g, tolerance);
1710  /* Drop zero'ed out geometries */
1711  if (lwgeom_is_empty(g))
1712  {
1713  lwgeom_free(g);
1714  continue;
1715  }
1716  col->geoms[j++] = g;
1717  }
1718  /* Update geometry count */
1719  col->ngeoms = j;
1720  break;
1721  }
1722  default: {
1723  lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type));
1724  break;
1725  }
1726  }
1727 
1728  if (geometry_modified)
1729  lwgeom_drop_bbox(geom);
1730  return geometry_modified;
1731 }
1732 
1733 
1734 /**************************************************************/
1735 
1736 int
1737 lwgeom_simplify_in_place(LWGEOM *geom, double epsilon, int preserve_collapsed)
1738 {
1739  int modified = LW_FALSE;
1740  switch (geom->type)
1741  {
1742  /* No-op! Cannot simplify points or triangles */
1743  case POINTTYPE:
1744  return modified;
1745  case TRIANGLETYPE:
1746  {
1747  if (preserve_collapsed)
1748  return modified;
1749  LWTRIANGLE *t = lwgeom_as_lwtriangle(geom);
1750  POINTARRAY *pa = t->points;
1751  ptarray_simplify_in_place(pa, epsilon, 0);
1752  if (pa->npoints < 3)
1753  {
1754  pa->npoints = 0;
1755  modified = LW_TRUE;
1756  }
1757  break;
1758  }
1759  case LINETYPE:
1760  {
1761  LWLINE *g = (LWLINE*)(geom);
1762  POINTARRAY *pa = g->points;
1763  uint32_t in_npoints = pa->npoints;
1764  ptarray_simplify_in_place(pa, epsilon, 2);
1765  modified = in_npoints != pa->npoints;
1766  /* Invalid output */
1767  if (pa->npoints == 1 && pa->maxpoints > 1)
1768  {
1769  /* Use first point as last point */
1770  if (preserve_collapsed)
1771  {
1772  pa->npoints = 2;
1773  ptarray_copy_point(pa, 0, 1);
1774  }
1775  /* Finish the collapse process */
1776  else
1777  {
1778  pa->npoints = 0;
1779  }
1780  }
1781  /* Duped output, force collapse */
1782  if (pa->npoints == 2 && !preserve_collapsed)
1783  {
1784  if (p2d_same(getPoint2d_cp(pa, 0), getPoint2d_cp(pa, 1)))
1785  pa->npoints = 0;
1786  }
1787  break;
1788  }
1789  case POLYGONTYPE:
1790  {
1791  uint32_t i, j = 0;
1792  LWPOLY *g = (LWPOLY*)(geom);
1793  for (i = 0; i < g->nrings; i++)
1794  {
1795  POINTARRAY *pa = g->rings[i];
1796  /* Only stop collapse on first ring */
1797  int minpoints = (preserve_collapsed && i == 0) ? 4 : 0;
1798  /* Skip zero'ed out rings */
1799  if(!pa)
1800  continue;
1801  uint32_t in_npoints = pa->npoints;
1802  ptarray_simplify_in_place(pa, epsilon, minpoints);
1803  modified |= in_npoints != pa->npoints;
1804  /* Drop collapsed rings */
1805  if(pa->npoints < 4)
1806  {
1807  if (i == 0)
1808  {
1809  /* If the outter ring is dropped, all can be dropped */
1810  for (i = 0; i < g->nrings; i++)
1811  {
1812  pa = g->rings[i];
1813  ptarray_free(pa);
1814  }
1815  break;
1816  }
1817  else
1818  {
1819  /* Drop this inner ring only */
1820  ptarray_free(pa);
1821  continue;
1822  }
1823  }
1824  g->rings[j++] = pa;
1825  }
1826  /* Update ring count */
1827  g->nrings = j;
1828  break;
1829  }
1830  /* Can process all multi* types as generic collection */
1831  case MULTIPOINTTYPE:
1832  case MULTILINETYPE:
1833  case MULTIPOLYGONTYPE:
1834  case TINTYPE:
1835  case COLLECTIONTYPE:
1836  {
1837  uint32_t i, j = 0;
1838  LWCOLLECTION *col = (LWCOLLECTION*)geom;
1839  for (i = 0; i < col->ngeoms; i++)
1840  {
1841  LWGEOM *g = col->geoms[i];
1842  if (!g) continue;
1843  modified |= lwgeom_simplify_in_place(g, epsilon, preserve_collapsed);
1844  /* Drop zero'ed out geometries */
1845  if(lwgeom_is_empty(g))
1846  {
1847  lwgeom_free(g);
1848  continue;
1849  }
1850  col->geoms[j++] = g;
1851  }
1852  /* Update geometry count */
1853  col->ngeoms = j;
1854  break;
1855  }
1856  default:
1857  {
1858  lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type));
1859  break;
1860  }
1861  }
1862 
1863  if (modified)
1864  {
1865  lwgeom_drop_bbox(geom);
1866  }
1867  return modified;
1868 }
1869 
1870 LWGEOM* lwgeom_simplify(const LWGEOM *igeom, double dist, int preserve_collapsed)
1871 {
1872  LWGEOM *lwgeom_out = lwgeom_clone_deep(igeom);
1873  lwgeom_simplify_in_place(lwgeom_out, dist, preserve_collapsed);
1874  if (lwgeom_is_empty(lwgeom_out))
1875  {
1876  lwgeom_free(lwgeom_out);
1877  return NULL;
1878  }
1879  return lwgeom_out;
1880 }
1881 
1882 /**************************************************************/
1883 
1884 
1885 double lwgeom_area(const LWGEOM *geom)
1886 {
1887  int type = geom->type;
1888 
1889  if ( type == POLYGONTYPE )
1890  return lwpoly_area((LWPOLY*)geom);
1891  else if ( type == CURVEPOLYTYPE )
1892  return lwcurvepoly_area((LWCURVEPOLY*)geom);
1893  else if (type == TRIANGLETYPE )
1894  return lwtriangle_area((LWTRIANGLE*)geom);
1895  else if ( lwgeom_is_collection(geom) )
1896  {
1897  double area = 0.0;
1898  uint32_t i;
1899  LWCOLLECTION *col = (LWCOLLECTION*)geom;
1900  for ( i = 0; i < col->ngeoms; i++ )
1901  area += lwgeom_area(col->geoms[i]);
1902  return area;
1903  }
1904  else
1905  return 0.0;
1906 }
1907 
1908 double lwgeom_perimeter(const LWGEOM *geom)
1909 {
1910  int type = geom->type;
1911  if ( type == POLYGONTYPE )
1912  return lwpoly_perimeter((LWPOLY*)geom);
1913  else if ( type == CURVEPOLYTYPE )
1914  return lwcurvepoly_perimeter((LWCURVEPOLY*)geom);
1915  else if ( type == TRIANGLETYPE )
1916  return lwtriangle_perimeter((LWTRIANGLE*)geom);
1917  else if ( lwgeom_is_collection(geom) )
1918  {
1919  double perimeter = 0.0;
1920  uint32_t i;
1921  LWCOLLECTION *col = (LWCOLLECTION*)geom;
1922  for ( i = 0; i < col->ngeoms; i++ )
1923  perimeter += lwgeom_perimeter(col->geoms[i]);
1924  return perimeter;
1925  }
1926  else
1927  return 0.0;
1928 }
1929 
1930 double lwgeom_perimeter_2d(const LWGEOM *geom)
1931 {
1932  int type = geom->type;
1933  if ( type == POLYGONTYPE )
1934  return lwpoly_perimeter_2d((LWPOLY*)geom);
1935  else if ( type == CURVEPOLYTYPE )
1936  return lwcurvepoly_perimeter_2d((LWCURVEPOLY*)geom);
1937  else if ( type == TRIANGLETYPE )
1938  return lwtriangle_perimeter_2d((LWTRIANGLE*)geom);
1939  else if ( lwgeom_is_collection(geom) )
1940  {
1941  double perimeter = 0.0;
1942  uint32_t i;
1943  LWCOLLECTION *col = (LWCOLLECTION*)geom;
1944  for ( i = 0; i < col->ngeoms; i++ )
1945  perimeter += lwgeom_perimeter_2d(col->geoms[i]);
1946  return perimeter;
1947  }
1948  else
1949  return 0.0;
1950 }
1951 
1952 double lwgeom_length(const LWGEOM *geom)
1953 {
1954  int type = geom->type;
1955  if ( type == LINETYPE )
1956  return lwline_length((LWLINE*)geom);
1957  else if ( type == CIRCSTRINGTYPE )
1958  return lwcircstring_length((LWCIRCSTRING*)geom);
1959  else if ( type == COMPOUNDTYPE )
1960  return lwcompound_length((LWCOMPOUND*)geom);
1961  else if ( lwgeom_is_collection(geom) )
1962  {
1963  double length = 0.0;
1964  uint32_t i;
1965  LWCOLLECTION *col = (LWCOLLECTION*)geom;
1966  for ( i = 0; i < col->ngeoms; i++ )
1967  length += lwgeom_length(col->geoms[i]);
1968  return length;
1969  }
1970  else
1971  return 0.0;
1972 }
1973 
1974 double lwgeom_length_2d(const LWGEOM *geom)
1975 {
1976  int type = geom->type;
1977  if ( type == LINETYPE )
1978  return lwline_length_2d((LWLINE*)geom);
1979  else if ( type == CIRCSTRINGTYPE )
1980  return lwcircstring_length_2d((LWCIRCSTRING*)geom);
1981  else if ( type == COMPOUNDTYPE )
1982  return lwcompound_length_2d((LWCOMPOUND*)geom);
1983  else if ( lwgeom_is_collection(geom) )
1984  {
1985  double length = 0.0;
1986  uint32_t i;
1987  LWCOLLECTION *col = (LWCOLLECTION*)geom;
1988  for ( i = 0; i < col->ngeoms; i++ )
1989  length += lwgeom_length_2d(col->geoms[i]);
1990  return length;
1991  }
1992  else
1993  return 0.0;
1994 }
1995 
1996 void
1997 lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
1998 {
1999  int type = geom->type;
2000  uint32_t i;
2001 
2002  switch(type)
2003  {
2004  /* Take advantage of fact tht pt/ln/circ/tri have same memory structure */
2005  case POINTTYPE:
2006  case LINETYPE:
2007  case CIRCSTRINGTYPE:
2008  case TRIANGLETYPE:
2009  {
2010  LWLINE *l = (LWLINE*)geom;
2011  ptarray_affine(l->points, affine);
2012  break;
2013  }
2014  case POLYGONTYPE:
2015  {
2016  LWPOLY *p = (LWPOLY*)geom;
2017  for( i = 0; i < p->nrings; i++ )
2018  ptarray_affine(p->rings[i], affine);
2019  break;
2020  }
2021  case CURVEPOLYTYPE:
2022  {
2023  LWCURVEPOLY *c = (LWCURVEPOLY*)geom;
2024  for( i = 0; i < c->nrings; i++ )
2025  lwgeom_affine(c->rings[i], affine);
2026  break;
2027  }
2028  default:
2029  {
2030  if( lwgeom_is_collection(geom) )
2031  {
2032  LWCOLLECTION *c = (LWCOLLECTION*)geom;
2033  for( i = 0; i < c->ngeoms; i++ )
2034  {
2035  lwgeom_affine(c->geoms[i], affine);
2036  }
2037  }
2038  else
2039  {
2040  lwerror("lwgeom_affine: unable to handle type '%s'", lwtype_name(type));
2041  }
2042  }
2043  }
2044 
2045  /* Recompute bbox if needed */
2046  if (geom->bbox)
2047  lwgeom_refresh_bbox(geom);
2048 }
2049 
2050 void
2051 lwgeom_scale(LWGEOM *geom, const POINT4D *factor)
2052 {
2053  int type = geom->type;
2054  uint32_t i;
2055 
2056  switch(type)
2057  {
2058  /* Take advantage of fact tht pt/ln/circ/tri have same memory structure */
2059  case POINTTYPE:
2060  case LINETYPE:
2061  case CIRCSTRINGTYPE:
2062  case TRIANGLETYPE:
2063  {
2064  LWLINE *l = (LWLINE*)geom;
2065  ptarray_scale(l->points, factor);
2066  break;
2067  }
2068  case POLYGONTYPE:
2069  {
2070  LWPOLY *p = (LWPOLY*)geom;
2071  for( i = 0; i < p->nrings; i++ )
2072  ptarray_scale(p->rings[i], factor);
2073  break;
2074  }
2075  case CURVEPOLYTYPE:
2076  {
2077  LWCURVEPOLY *c = (LWCURVEPOLY*)geom;
2078  for( i = 0; i < c->nrings; i++ )
2079  lwgeom_scale(c->rings[i], factor);
2080  break;
2081  }
2082  default:
2083  {
2084  if( lwgeom_is_collection(geom) )
2085  {
2086  LWCOLLECTION *c = (LWCOLLECTION*)geom;
2087  for( i = 0; i < c->ngeoms; i++ )
2088  {
2089  lwgeom_scale(c->geoms[i], factor);
2090  }
2091  }
2092  else
2093  {
2094  lwerror("lwgeom_scale: unable to handle type '%s'", lwtype_name(type));
2095  }
2096  }
2097  }
2098 
2099  /* Recompute bbox if needed */
2100  if (geom->bbox)
2101  lwgeom_refresh_bbox(geom);
2102 }
2103 
2104 LWGEOM *
2105 lwgeom_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
2106 {
2107  switch(type)
2108  {
2109  case POINTTYPE:
2110  return lwpoint_as_lwgeom(lwpoint_construct_empty(srid, hasz, hasm));
2111  case LINETYPE:
2112  return lwline_as_lwgeom(lwline_construct_empty(srid, hasz, hasm));
2113  case POLYGONTYPE:
2114  return lwpoly_as_lwgeom(lwpoly_construct_empty(srid, hasz, hasm));
2115  case CURVEPOLYTYPE:
2116  return lwcurvepoly_as_lwgeom(lwcurvepoly_construct_empty(srid, hasz, hasm));
2117  case CIRCSTRINGTYPE:
2118  return lwcircstring_as_lwgeom(lwcircstring_construct_empty(srid, hasz, hasm));
2119  case TRIANGLETYPE:
2120  return lwtriangle_as_lwgeom(lwtriangle_construct_empty(srid, hasz, hasm));
2121  case COMPOUNDTYPE:
2122  case MULTIPOINTTYPE:
2123  case MULTILINETYPE:
2124  case MULTIPOLYGONTYPE:
2125  case COLLECTIONTYPE:
2126  return lwcollection_as_lwgeom(lwcollection_construct_empty(type, srid, hasz, hasm));
2127  default:
2128  lwerror("lwgeom_construct_empty: unsupported geometry type: %s",
2129  lwtype_name(type));
2130  return NULL;
2131  }
2132 }
2133 
2134 int
2135 lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt)
2136 {
2137  if ( ! lwgeom || lwgeom_is_empty(lwgeom) )
2138  return LW_FAILURE;
2139 
2140  switch( lwgeom->type )
2141  {
2142  case POINTTYPE:
2143  return ptarray_startpoint(((LWPOINT*)lwgeom)->point, pt);
2144  case TRIANGLETYPE:
2145  case CIRCSTRINGTYPE:
2146  case LINETYPE:
2147  return ptarray_startpoint(((LWLINE*)lwgeom)->points, pt);
2148  case POLYGONTYPE:
2149  return lwpoly_startpoint((LWPOLY*)lwgeom, pt);
2150  case TINTYPE:
2151  case CURVEPOLYTYPE:
2152  case COMPOUNDTYPE:
2153  case MULTIPOINTTYPE:
2154  case MULTILINETYPE:
2155  case MULTIPOLYGONTYPE:
2156  case COLLECTIONTYPE:
2157  case POLYHEDRALSURFACETYPE:
2158  return lwcollection_startpoint((LWCOLLECTION*)lwgeom, pt);
2159  default:
2160  lwerror("lwgeom_startpoint: unsupported geometry type: %s", lwtype_name(lwgeom->type));
2161  return LW_FAILURE;
2162  }
2163 }
2164 
2165 void
2167 {
2168  if (!geom) return;
2169  if (lwgeom_is_empty(geom)) return;
2170  switch ( geom->type )
2171  {
2172  case POINTTYPE:
2173  {
2174  LWPOINT *pt = (LWPOINT*)(geom);
2175  ptarray_grid_in_place(pt->point, grid);
2176  return;
2177  }
2178  case CIRCSTRINGTYPE:
2179  case TRIANGLETYPE:
2180  case LINETYPE:
2181  {
2182  LWLINE *ln = (LWLINE*)(geom);
2183  ptarray_grid_in_place(ln->points, grid);
2184  /* For invalid line, return an EMPTY */
2185  if (ln->points->npoints < 2)
2186  ln->points->npoints = 0;
2187  return;
2188  }
2189  case POLYGONTYPE:
2190  {
2191  LWPOLY *ply = (LWPOLY*)(geom);
2192  if (!ply->rings) return;
2193 
2194  /* Check first the external ring */
2195  uint32_t i = 0;
2196  POINTARRAY *pa = ply->rings[0];
2197  ptarray_grid_in_place(pa, grid);
2198  if (pa->npoints < 4)
2199  {
2200  /* External ring collapsed: free everything */
2201  for (i = 0; i < ply->nrings; i++)
2202  {
2203  ptarray_free(ply->rings[i]);
2204  }
2205  ply->nrings = 0;
2206  return;
2207  }
2208 
2209  /* Check the other rings */
2210  uint32_t j = 1;
2211  for (i = 1; i < ply->nrings; i++)
2212  {
2213  POINTARRAY *pa = ply->rings[i];
2214  ptarray_grid_in_place(pa, grid);
2215 
2216  /* Skip bad rings */
2217  if (pa->npoints >= 4)
2218  {
2219  ply->rings[j++] = pa;
2220  }
2221  else
2222  {
2223  ptarray_free(pa);
2224  }
2225  }
2226  /* Adjust ring count appropriately */
2227  ply->nrings = j;
2228  return;
2229  }
2230  case MULTIPOINTTYPE:
2231  case MULTILINETYPE:
2232  case MULTIPOLYGONTYPE:
2233  case TINTYPE:
2234  case COLLECTIONTYPE:
2235  case COMPOUNDTYPE:
2236  {
2237  LWCOLLECTION *col = (LWCOLLECTION*)(geom);
2238  uint32_t i, j = 0;
2239  if (!col->geoms) return;
2240  for (i = 0; i < col->ngeoms; i++)
2241  {
2242  LWGEOM *g = col->geoms[i];
2243  lwgeom_grid_in_place(g, grid);
2244  /* Empty geoms need to be freed */
2245  /* before we move on */
2246  if (lwgeom_is_empty(g))
2247  {
2248  lwgeom_free(g);
2249  continue;
2250  }
2251  col->geoms[j++] = g;
2252  }
2253  col->ngeoms = j;
2254  return;
2255  }
2256  default:
2257  {
2258  lwerror("%s: Unsupported geometry type: %s", __func__,
2259  lwtype_name(geom->type));
2260  return;
2261  }
2262  }
2263 }
2264 
2265 
2266 LWGEOM *
2267 lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid)
2268 {
2269  LWGEOM *lwgeom_out = lwgeom_clone_deep(lwgeom);
2270  lwgeom_grid_in_place(lwgeom_out, grid);
2271  return lwgeom_out;
2272 }
2273 
2274 
2275 /* Prototype for recursion */
2276 static void lwgeom_subdivide_recursive(const LWGEOM *geom,
2277  uint8_t dimension,
2278  uint32_t maxvertices,
2279  uint32_t depth,
2280  LWCOLLECTION *col,
2281  double gridSize);
2282 
2283 static void
2285  uint8_t dimension,
2286  uint32_t maxvertices,
2287  uint32_t depth,
2288  LWCOLLECTION *col,
2289  double gridSize)
2290 {
2291  const uint32_t maxdepth = 50;
2292 
2293  if (!geom)
2294  return;
2295 
2296  const GBOX *box_in = lwgeom_get_bbox(geom);
2297  if (!box_in)
2298  return;
2299 
2300  LW_ON_INTERRUPT(return);
2301 
2302  GBOX clip;
2303  gbox_duplicate(box_in, &clip);
2304  double width = clip.xmax - clip.xmin;
2305  double height = clip.ymax - clip.ymin;
2306 
2307  if ( geom->type == POLYHEDRALSURFACETYPE || geom->type == TINTYPE )
2308  lwerror("%s: unsupported geometry type '%s'", __func__, lwtype_name(geom->type));
2309 
2310  if ( width == 0.0 && height == 0.0 )
2311  {
2312  if ( geom->type == POINTTYPE && dimension == 0)
2314  return;
2315  }
2316 
2317  if (width == 0.0)
2318  {
2319  clip.xmax += FP_TOLERANCE;
2320  clip.xmin -= FP_TOLERANCE;
2321  width = 2 * FP_TOLERANCE;
2322  }
2323  if (height == 0.0)
2324  {
2325  clip.ymax += FP_TOLERANCE;
2326  clip.ymin -= FP_TOLERANCE;
2327  height = 2 * FP_TOLERANCE;
2328  }
2329 
2330  /* Always just recurse into collections */
2331  if ( lwgeom_is_collection(geom) && geom->type != MULTIPOINTTYPE )
2332  {
2333  LWCOLLECTION *incol = (LWCOLLECTION*)geom;
2334  /* Don't increment depth yet, since we aren't actually
2335  * subdividing geometries yet */
2336  for (uint32_t i = 0; i < incol->ngeoms; i++ )
2337  lwgeom_subdivide_recursive(incol->geoms[i], dimension, maxvertices, depth, col, gridSize);
2338  return;
2339  }
2340 
2341  if (lwgeom_dimension(geom) < dimension)
2342  {
2343  /* We've hit a lower dimension object produced by clipping at
2344  * a shallower recursion level. Ignore it. */
2345  return;
2346  }
2347 
2348  /* But don't go too far. 2^50 ~= 10^15, that's enough subdivision */
2349  /* Just add what's left */
2350  if ( depth > maxdepth )
2351  {
2353  return;
2354  }
2355 
2356  uint32_t nvertices = lwgeom_count_vertices(geom);
2357 
2358  /* Skip empties entirely */
2359  if (nvertices == 0)
2360  return;
2361 
2362  /* If it is under the vertex tolerance, just add it, we're done */
2363  if (nvertices <= maxvertices)
2364  {
2366  return;
2367  }
2368 
2369  uint8_t split_ordinate = (width > height) ? 0 : 1;
2370  double center = (split_ordinate == 0) ? (clip.xmin + clip.xmax) / 2 : (clip.ymin + clip.ymax) / 2;
2371  double pivot = DBL_MAX;
2372  if (geom->type == POLYGONTYPE)
2373  {
2374  uint32_t ring_to_trim = 0;
2375  double ring_area = 0;
2376  double pivot_eps = DBL_MAX;
2377  double pt_eps = DBL_MAX;
2378  POINTARRAY *pa;
2379  LWPOLY *lwpoly = (LWPOLY *)geom;
2380 
2381  /* if there are more points in holes than in outer ring */
2382  if (nvertices >= 2 * lwpoly->rings[0]->npoints)
2383  {
2384  /* trim holes starting from biggest */
2385  for (uint32_t i = 1; i < lwpoly->nrings; i++)
2386  {
2387  double current_ring_area = fabs(ptarray_signed_area(lwpoly->rings[i]));
2388  if (current_ring_area >= ring_area)
2389  {
2390  ring_area = current_ring_area;
2391  ring_to_trim = i;
2392  }
2393  }
2394  }
2395 
2396  pa = lwpoly->rings[ring_to_trim];
2397 
2398  /* find most central point in chosen ring */
2399  for (uint32_t i = 0; i < pa->npoints; i++)
2400  {
2401  double pt;
2402  if (split_ordinate == 0)
2403  pt = getPoint2d_cp(pa, i)->x;
2404  else
2405  pt = getPoint2d_cp(pa, i)->y;
2406  pt_eps = fabs(pt - center);
2407  if (pivot_eps > pt_eps)
2408  {
2409  pivot = pt;
2410  pivot_eps = pt_eps;
2411  }
2412  }
2413  }
2414  GBOX subbox1, subbox2;
2415  gbox_duplicate(&clip, &subbox1);
2416  gbox_duplicate(&clip, &subbox2);
2417 
2418  if (pivot == DBL_MAX)
2419  pivot = center;
2420 
2421  if (split_ordinate == 0)
2422  {
2423  if (FP_NEQUALS(subbox1.xmax, pivot) && FP_NEQUALS(subbox1.xmin, pivot))
2424  subbox1.xmax = subbox2.xmin = pivot;
2425  else
2426  subbox1.xmax = subbox2.xmin = center;
2427  }
2428  else
2429  {
2430  if (FP_NEQUALS(subbox1.ymax, pivot) && FP_NEQUALS(subbox1.ymin, pivot))
2431  subbox1.ymax = subbox2.ymin = pivot;
2432  else
2433  subbox1.ymax = subbox2.ymin = center;
2434  }
2435 
2436  ++depth;
2437 
2438  {
2440  geom->srid, subbox1.xmin, subbox1.ymin, subbox1.xmax, subbox1.ymax);
2441  LWGEOM *clipped = lwgeom_intersection_prec(geom, subbox, gridSize);
2442  lwgeom_simplify_in_place(clipped, 0.0, LW_TRUE);
2443  lwgeom_free(subbox);
2444  if (clipped && !lwgeom_is_empty(clipped))
2445  {
2446  lwgeom_subdivide_recursive(clipped, dimension, maxvertices, depth, col, gridSize);
2447  lwgeom_free(clipped);
2448  }
2449  }
2450  {
2452  geom->srid, subbox2.xmin, subbox2.ymin, subbox2.xmax, subbox2.ymax);
2453  LWGEOM *clipped = lwgeom_intersection_prec(geom, subbox, gridSize);
2454  lwgeom_simplify_in_place(clipped, 0.0, LW_TRUE);
2455  lwgeom_free(subbox);
2456  if (clipped && !lwgeom_is_empty(clipped))
2457  {
2458  lwgeom_subdivide_recursive(clipped, dimension, maxvertices, depth, col, gridSize);
2459  lwgeom_free(clipped);
2460  }
2461  }
2462 }
2463 
2464 LWCOLLECTION *
2465 lwgeom_subdivide_prec(const LWGEOM *geom, uint32_t maxvertices, double gridSize)
2466 {
2467  static uint32_t startdepth = 0;
2468  static uint32_t minmaxvertices = 5;
2469  LWCOLLECTION *col;
2470 
2472 
2473  if ( lwgeom_is_empty(geom) )
2474  return col;
2475 
2476  if ( maxvertices < minmaxvertices )
2477  {
2478  lwcollection_free(col);
2479  lwerror("%s: cannot subdivide to fewer than %d vertices per output", __func__, minmaxvertices);
2480  }
2481 
2482  lwgeom_subdivide_recursive(geom, lwgeom_dimension(geom), maxvertices, startdepth, col, gridSize);
2483  lwgeom_set_srid((LWGEOM*)col, geom->srid);
2484  return col;
2485 }
2486 
2487 LWCOLLECTION *
2488 lwgeom_subdivide(const LWGEOM *geom, uint32_t maxvertices)
2489 {
2490  return lwgeom_subdivide_prec(geom, maxvertices, -1);
2491 }
2492 
2493 
2494 
2495 int
2497 {
2498  int type = geom->type;
2499 
2500  if( type != LINETYPE )
2501  {
2502  lwnotice("Geometry is not a LINESTRING");
2503  return LW_FALSE;
2504  }
2505  return lwline_is_trajectory((LWLINE*)geom);
2506 }
2507 
2508 #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
2509 STATIC_ASSERT(sizeof(double) == sizeof(uint64_t),this_should_be_true);
2510 
2511 static double trim_preserve_decimal_digits(double d, int32_t decimal_digits)
2512 {
2513  uint64_t dint = 0;
2514  memcpy(&dint, &d, sizeof(double));
2515  /* Extract the exponent from the IEEE 754 integer representation, which */
2516  /* corresponds to floor(log2(fabs(d))) */
2517  const int exponent = (int)((dint >> 52) & 2047) - 1023;
2518  /* (x * 851 + 255) / 256 == 1 + (int)(x * log2(10)) for x in [0,30] */
2519  int bits_needed = 1 + exponent + (decimal_digits * 851 + 255) / 256;
2520  /* for negative values, (x * 851 + 255) / 256 == (int)(x * log2(10)), so */
2521  /* substract one */
2522  if (decimal_digits < 0)
2523  bits_needed --;
2524 
2525  /* This will also handle NaN and Inf since exponent = 1023, and thus for */
2526  /* reasonable decimal_digits values bits_needed will be > 52 */
2527  if (bits_needed >= 52)
2528  {
2529  return d;
2530  }
2531  if (bits_needed < 1 )
2532  bits_needed = 1;
2533  const uint64_t mask = 0xffffffffffffffffULL << (52 - bits_needed);
2534  dint &= mask;
2535  memcpy(&d, &dint, sizeof(double));
2536  return d;
2537 }
2538 
2539 void lwgeom_trim_bits_in_place(LWGEOM* geom, int32_t prec_x, int32_t prec_y, int32_t prec_z, int32_t prec_m)
2540 {
2542  POINT4D p;
2543 
2544  while (lwpointiterator_has_next(it))
2545  {
2546  lwpointiterator_peek(it, &p);
2547  p.x = trim_preserve_decimal_digits(p.x, prec_x);
2548  p.y = trim_preserve_decimal_digits(p.y, prec_y);
2549  if (lwgeom_has_z(geom))
2550  p.z = trim_preserve_decimal_digits(p.z, prec_z);
2551  if (lwgeom_has_m(geom))
2552  p.m = trim_preserve_decimal_digits(p.m, prec_m);
2554  }
2555 
2557 }
2558 
2559 LWGEOM *
2561 {
2562  int32_t srid = lwgeom_get_srid(lwgeom);
2563  uint8_t hasz = lwgeom_has_z(lwgeom);
2564  uint8_t hasm = lwgeom_has_m(lwgeom);
2565 
2566  switch (lwgeom->type)
2567  {
2568  case POINTTYPE:
2569  case MULTIPOINTTYPE: {
2570  return lwgeom_construct_empty(lwgeom->type, srid, hasz, hasm);
2571  }
2572  case LINETYPE:
2573  case CIRCSTRINGTYPE: {
2574  if (lwgeom_is_closed(lwgeom) || lwgeom_is_empty(lwgeom))
2575  return (LWGEOM *)lwmpoint_construct_empty(srid, hasz, hasm);
2576  else
2577  {
2578  LWLINE *lwline = (LWLINE *)lwgeom;
2579  LWMPOINT *lwmpoint = lwmpoint_construct_empty(srid, hasz, hasm);
2580  POINT4D pt;
2581  getPoint4d_p(lwline->points, 0, &pt);
2582  lwmpoint_add_lwpoint(lwmpoint, lwpoint_make(srid, hasz, hasm, &pt));
2583  getPoint4d_p(lwline->points, lwline->points->npoints - 1, &pt);
2584  lwmpoint_add_lwpoint(lwmpoint, lwpoint_make(srid, hasz, hasm, &pt));
2585 
2586  return (LWGEOM *)lwmpoint;
2587  }
2588  }
2589  case MULTILINETYPE:
2590  case MULTICURVETYPE: {
2591  LWMLINE *lwmline = (LWMLINE *)lwgeom;
2592  POINT4D *out = lwalloc(sizeof(POINT4D) * lwmline->ngeoms * 2);
2593  uint32_t n = 0;
2594 
2595  for (uint32_t i = 0; i < lwmline->ngeoms; i++)
2596  {
2597  LWMPOINT *points = lwgeom_as_lwmpoint(lwgeom_boundary((LWGEOM *)lwmline->geoms[i]));
2598  if (!points)
2599  continue;
2600 
2601  for (uint32_t k = 0; k < points->ngeoms; k++)
2602  {
2603  POINT4D pt = getPoint4d(points->geoms[k]->point, 0);
2604 
2605  uint8_t seen = LW_FALSE;
2606  for (uint32_t j = 0; j < n; j++)
2607  {
2608  if (memcmp(&(out[j]), &pt, sizeof(POINT4D)) == 0)
2609  {
2610  seen = LW_TRUE;
2611  out[j] = out[--n];
2612  break;
2613  }
2614  }
2615  if (!seen)
2616  out[n++] = pt;
2617  }
2618 
2619  lwgeom_free((LWGEOM *)points);
2620  }
2621 
2622  LWMPOINT *lwmpoint = lwmpoint_construct_empty(srid, hasz, hasm);
2623 
2624  for (uint32_t i = 0; i < n; i++)
2625  lwmpoint_add_lwpoint(lwmpoint, lwpoint_make(srid, hasz, hasm, &(out[i])));
2626 
2627  lwfree(out);
2628 
2629  return (LWGEOM *)lwmpoint;
2630  }
2631  case TRIANGLETYPE: {
2632  LWTRIANGLE *lwtriangle = (LWTRIANGLE *)lwgeom;
2633  POINTARRAY *points = ptarray_clone_deep(lwtriangle->points);
2634  return (LWGEOM *)lwline_construct(srid, 0, points);
2635  }
2636  case POLYGONTYPE: {
2637  LWPOLY *lwpoly = (LWPOLY *)lwgeom;
2638 
2639  LWMLINE *lwmline = lwmline_construct_empty(srid, hasz, hasm);
2640  for (uint32_t i = 0; i < lwpoly->nrings; i++)
2641  {
2642  POINTARRAY *ring = ptarray_clone_deep(lwpoly->rings[i]);
2643  lwmline_add_lwline(lwmline, lwline_construct(srid, 0, ring));
2644  }
2645 
2646  /* Homogenize the multilinestring to hopefully get a single LINESTRING */
2647  LWGEOM *lwout = lwgeom_homogenize((LWGEOM *)lwmline);
2648  lwgeom_free((LWGEOM *)lwmline);
2649  return lwout;
2650  }
2651  case CURVEPOLYTYPE: {
2652  LWCURVEPOLY *lwcurvepoly = (LWCURVEPOLY *)lwgeom;
2653  LWCOLLECTION *lwcol = lwcollection_construct_empty(MULTICURVETYPE, srid, hasz, hasm);
2654 
2655  for (uint32_t i = 0; i < lwcurvepoly->nrings; i++)
2656  lwcol = lwcollection_add_lwgeom(lwcol, lwgeom_clone_deep(lwcurvepoly->rings[i]));
2657 
2658  return (LWGEOM *)lwcol;
2659  }
2660  case MULTIPOLYGONTYPE:
2661  case COLLECTIONTYPE:
2662  case TINTYPE: {
2663  LWCOLLECTION *lwcol = (LWCOLLECTION *)lwgeom;
2664  LWCOLLECTION *lwcol_boundary = lwcollection_construct_empty(COLLECTIONTYPE, srid, hasz, hasm);
2665 
2666  for (uint32_t i = 0; i < lwcol->ngeoms; i++)
2667  lwcollection_add_lwgeom(lwcol_boundary, lwgeom_boundary(lwcol->geoms[i]));
2668 
2669  LWGEOM *lwout = lwgeom_homogenize((LWGEOM *)lwcol_boundary);
2670  lwgeom_free((LWGEOM *)lwcol_boundary);
2671 
2672  return lwout;
2673  }
2674  default:
2675  lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(lwgeom->type));
2676  return NULL;
2677  }
2678 }
2679 
2680 int
2681 lwgeom_isfinite(const LWGEOM *lwgeom)
2682 {
2684  int hasz = lwgeom_has_z(lwgeom);
2685  int hasm = lwgeom_has_m(lwgeom);
2686 
2687  while (lwpointiterator_has_next(it))
2688  {
2689  POINT4D p;
2690  lwpointiterator_next(it, &p);
2691  int finite = isfinite(p.x) &&
2692  isfinite(p.y) &&
2693  (hasz ? isfinite(p.z) : 1) &&
2694  (hasm ? isfinite(p.m) : 1);
2695 
2696  if (!finite)
2697  {
2699  return LW_FALSE;
2700  }
2701  }
2703  return LW_TRUE;
2704 }
char * r
Definition: cu_in_wkt.c:24
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:262
int gbox_same(const GBOX *g1, const GBOX *g2)
Check if 2 given Gbox are the same.
Definition: gbox.c:164
void gbox_duplicate(const GBOX *original, GBOX *duplicate)
Copy the values of original GBOX into duplicate.
Definition: gbox.c:433
int lwgeom_calculate_gbox_cartesian(const LWGEOM *lwgeom, GBOX *gbox)
Calculate the 2-4D bounding box of a geometry.
Definition: gbox.c:740
GBOX * gbox_new(lwflags_t flags)
Create a new gbox with the dimensionality indicated by the flags.
Definition: gbox.c:32
GBOX * gbox_clone(const GBOX *gbox)
Definition: gbox.c:45
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoint.c:151
POINT4D getPoint4d(const POINTARRAY *pa, uint32_t n)
Definition: lwgeom_api.c:108
#define LW_FALSE
Definition: liblwgeom.h:94
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
#define COMPOUNDTYPE
Definition: liblwgeom.h:110
double distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2)
Definition: measures.c:2398
void lwmpoint_free(LWMPOINT *mpt)
Definition: lwmpoint.c:72
LWPOINTITERATOR * lwpointiterator_create(const LWGEOM *g)
Create a new LWPOINTITERATOR over supplied LWGEOM*.
Definition: lwiterator.c:242
LWLINE * lwline_segmentize2d(const LWLINE *line, double dist)
Definition: lwline.c:132
void lwpoint_free(LWPOINT *pt)
Definition: lwpoint.c:213
#define LW_FAILURE
Definition: liblwgeom.h:96
LWCOLLECTION * lwcollection_segmentize2d(const LWCOLLECTION *coll, double dist)
Definition: lwcollection.c:251
#define CURVEPOLYTYPE
Definition: liblwgeom.h:111
void lwmpoly_free(LWMPOLY *mpoly)
Definition: lwmpoly.c:53
#define MULTILINETYPE
Definition: liblwgeom.h:106
LWCURVEPOLY * lwcurvepoly_construct_from_lwpoly(LWPOLY *lwpoly)
Construct an equivalent curve polygon from a polygon.
Definition: lwcurvepoly.c:52
int lwpointiterator_next(LWPOINTITERATOR *s, POINT4D *p)
Attempts to assign the next point in the iterator to p, and advances the iterator to the next point.
Definition: lwiterator.c:210
#define MULTISURFACETYPE
Definition: liblwgeom.h:113
#define LINETYPE
Definition: liblwgeom.h:103
void lwtin_free(LWTIN *tin)
Definition: lwtin.c:39
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
Definition: lwgeodetic.c:3027
#define WKT_EXTENDED
Definition: liblwgeom.h:2186
LWGEOM * lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
Convert type with arcs into equivalent linearized type.
Definition: lwstroke.c:871
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
#define FLAGS_SET_BBOX(flags, value)
Definition: liblwgeom.h:174
int lwpointiterator_peek(LWPOINTITERATOR *s, POINT4D *p)
Attempts to assigns the next point in the iterator to p.
Definition: lwiterator.c:193
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition: ptarray.c:647
LWMLINE * lwmline_add_lwline(LWMLINE *mobj, const LWLINE *obj)
Definition: lwmline.c:46
void lwpointiterator_destroy(LWPOINTITERATOR *s)
Free all memory associated with the iterator.
Definition: lwiterator.c:267
LWMLINE * lwmline_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwmline.c:38
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:165
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
LWTRIANGLE * lwtriangle_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwtriangle.c:58
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition: lwmpoint.c:45
#define TINTYPE
Definition: liblwgeom.h:116
LWPOLY * lwpoly_construct_envelope(int32_t srid, double x1, double y1, double x2, double y2)
Definition: lwpoly.c:98
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
int lwpointiterator_modify_next(LWPOINTITERATOR *s, const POINT4D *p)
Attempts to replace the next point int the iterator with p, and advances the iterator to the next poi...
Definition: lwiterator.c:224
LWGEOM * lwgeom_homogenize(const LWGEOM *geom)
Definition: lwhomogenize.c:208
void lwfree(void *mem)
Definition: lwutil.c:242
#define FLAGS_NDIMS(flags)
Definition: liblwgeom.h:179
#define POLYGONTYPE
Definition: liblwgeom.h:104
void lwcircstring_free(LWCIRCSTRING *curve)
Definition: lwcircstring.c:97
LWMPOINT * lwmpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwmpoint.c:39
void lwtriangle_free(LWTRIANGLE *triangle)
Definition: lwtriangle.c:69
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:114
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:109
LWPOINTITERATOR * lwpointiterator_create_rw(LWGEOM *g)
Create a new LWPOINTITERATOR over supplied LWGEOM* Supports modification of coordinates during iterat...
Definition: lwiterator.c:251
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
Definition: lwcollection.c:92
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:166
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:357
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:125
LWGEOM * lwgeom_intersection_prec(const LWGEOM *geom1, const LWGEOM *geom2, double gridSize)
void ptarray_free(POINTARRAY *pa)
Definition: ptarray.c:319
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition: lwout_wkt.c:708
#define FLAGS_GET_ZM(flags)
Definition: liblwgeom.h:180
LWPOLY * lwpoly_segmentize2d(const LWPOLY *line, double dist)
Definition: lwpoly.c:312
int lwpointiterator_has_next(LWPOINTITERATOR *s)
Returns LW_TRUE if there is another point available in the iterator.
Definition: lwiterator.c:202
#define MULTICURVETYPE
Definition: liblwgeom.h:112
#define TRIANGLETYPE
Definition: liblwgeom.h:115
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:188
#define FLAGS_SET_GEODETIC(flags, value)
Definition: liblwgeom.h:175
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:42
void lwpsurface_free(LWPSURFACE *psurf)
Definition: lwpsurface.c:39
void lwpoly_free(LWPOLY *poly)
Definition: lwpoly.c:175
void lwmline_free(LWMLINE *mline)
Definition: lwmline.c:112
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
int lwline_is_trajectory(const LWLINE *geom)
Definition: lwline.c:454
LWCURVEPOLY * lwcurvepoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwcurvepoly.c:35
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:215
LWPOLY * lwpoly_from_lwlines(const LWLINE *shell, uint32_t nholes, const LWLINE **holes)
Definition: lwpoly.c:360
#define NUMTYPES
Definition: liblwgeom.h:118
LWCOMPOUND * lwcompound_construct_from_lwline(const LWLINE *lwpoly)
Construct an equivalent compound curve from a linestring.
Definition: lwcompound.c:204
LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoly.c:161
LWCIRCSTRING * lwcircstring_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwcircstring.c:79
void lwline_free(LWLINE *line)
Definition: lwline.c:67
LWLINE * lwline_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwline.c:55
#define FLAGS_GET_GEODETIC(flags)
Definition: liblwgeom.h:168
LWPOINT * lwpoint_make(int32_t srid, int hasz, int hasm, const POINT4D *p)
Definition: lwpoint.c:206
enum LWORD_T LWORD
Ordinate names.
double lwline_length_2d(const LWLINE *line)
Definition: lwline.c:520
void ptarray_longitude_shift(POINTARRAY *pa)
Longitude shift for a pointarray.
Definition: ptarray.c:1498
LWLINE * lwline_clone_deep(const LWLINE *lwgeom)
Definition: lwline.c:109
double lwcircstring_length_2d(const LWCIRCSTRING *circ)
Definition: lwcircstring.c:274
LWPOINT * lwpoint_clone(const LWPOINT *lwgeom)
Definition: lwpoint.c:239
int lwcircstring_is_closed(const LWCIRCSTRING *curve)
Definition: lwcircstring.c:261
void ptarray_reverse_in_place(POINTARRAY *pa)
Definition: ptarray.c:331
LWLINE * lwline_clone(const LWLINE *lwgeom)
Definition: lwline.c:93
char lwcollection_same(const LWCOLLECTION *p1, const LWCOLLECTION *p2)
check for same geometry composition
Definition: lwcollection.c:279
double lwtriangle_area(const LWTRIANGLE *triangle)
Find the area of the outer ring.
Definition: lwtriangle.c:178
double lwcompound_length_2d(const LWCOMPOUND *comp)
Definition: lwcompound.c:74
double ptarray_signed_area(const POINTARRAY *pa)
Returns the area in cartesian units.
Definition: ptarray.c:1016
#define LW_ON_INTERRUPT(x)
int lwline_is_closed(const LWLINE *line)
Definition: lwline.c:445
uint32_t lwline_count_vertices(const LWLINE *line)
Definition: lwline.c:505
char lwtriangle_same(const LWTRIANGLE *p1, const LWTRIANGLE *p2)
Definition: lwtriangle.c:126
int lwpoly_startpoint(const LWPOLY *lwpoly, POINT4D *pt)
Definition: lwpoly.c:524
LWPOINT * lwpoint_force_dims(const LWPOINT *lwpoint, int hasz, int hasm, double zval, double mval)
Definition: lwpoint.c:304
int lwcollection_startpoint(const LWCOLLECTION *col, POINT4D *pt)
Definition: lwcollection.c:550
int ptarray_startpoint(const POINTARRAY *pa, POINT4D *pt)
Definition: ptarray.c:2076
int lwcompound_is_closed(const LWCOMPOUND *curve)
Definition: lwcompound.c:35
double lwtriangle_perimeter_2d(const LWTRIANGLE *triangle)
Definition: lwtriangle.c:210
int lwpoly_is_clockwise(LWPOLY *poly)
Definition: lwpoly.c:288
void ptarray_grid_in_place(POINTARRAY *pa, const gridspec *grid)
Snap to grid.
Definition: ptarray.c:2091
double lwpoly_area(const LWPOLY *poly)
Find the area of the outer ring - sum (area of inner rings).
Definition: lwpoly.c:434
int lwtriangle_is_clockwise(LWTRIANGLE *triangle)
Definition: lwtriangle.c:113
char lwline_same(const LWLINE *p1, const LWLINE *p2)
Definition: lwline.c:141
void ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, uint32_t min_points)
Definition: ptarray.c:1535
double lwtriangle_perimeter(const LWTRIANGLE *triangle)
Definition: lwtriangle.c:201
void ptarray_simplify_in_place(POINTARRAY *pa, double tolerance, uint32_t minpts)
Definition: ptarray.c:1713
LWLINE * lwline_force_dims(const LWLINE *lwline, int hasz, int hasm, double zval, double mval)
Definition: lwline.c:486
double lwcompound_length(const LWCOMPOUND *comp)
Definition: lwcompound.c:69
LWCOLLECTION * lwcollection_clone_deep(const LWCOLLECTION *lwgeom)
Deep clone LWCOLLECTION object.
Definition: lwcollection.c:150
LWCOLLECTION * lwcollection_force_dims(const LWCOLLECTION *lwcol, int hasz, int hasm, double zval, double mval)
Definition: lwcollection.c:476
void lwtriangle_force_clockwise(LWTRIANGLE *triangle)
Definition: lwtriangle.c:106
#define FP_TOLERANCE
Floating point comparators.
void ptarray_scale(POINTARRAY *pa, const POINT4D *factor)
WARNING, make sure you send in only 16-member double arrays or obviously things will go pear-shaped f...
Definition: ptarray.c:2058
void ptarray_affine(POINTARRAY *pa, const AFFINE *affine)
Affine transform a pointarray.
Definition: ptarray.c:1890
int lwpsurface_is_closed(const LWPSURFACE *psurface)
Definition: lwpsurface.c:99
double lwcurvepoly_perimeter(const LWCURVEPOLY *poly)
Definition: lwcurvepoly.c:147
uint32_t lwpoly_count_vertices(const LWPOLY *poly)
Definition: lwpoly.c:418
char lwpoly_same(const LWPOLY *p1, const LWPOLY *p2)
Definition: lwpoly.c:339
double lwline_length(const LWLINE *line)
Definition: lwline.c:513
void lwpoly_force_clockwise(LWPOLY *poly)
Definition: lwpoly.c:268
uint32_t lwcollection_count_vertices(const LWCOLLECTION *col)
Definition: lwcollection.c:500
void ptarray_swap_ordinates(POINTARRAY *pa, LWORD o1, LWORD o2)
Swap ordinate values o1 and o2 on a given POINTARRAY.
Definition: ptarray.c:379
double lwpoly_perimeter_2d(const LWPOLY *poly)
Compute the sum of polygon rings length (forcing 2d computation).
Definition: lwpoly.c:485
int lwtin_is_closed(const LWTIN *tin)
Definition: lwtin.c:93
char lwcircstring_same(const LWCIRCSTRING *p1, const LWCIRCSTRING *p2)
Definition: lwcircstring.c:131
LWPOLY * lwpoly_clone_deep(const LWPOLY *lwgeom)
Definition: lwpoly.c:228
double lwcircstring_length(const LWCIRCSTRING *circ)
Definition: lwcircstring.c:269
double lwcurvepoly_area(const LWCURVEPOLY *curvepoly)
This should be rewritten to make use of the curve itself.
Definition: lwcurvepoly.c:133
LWCOLLECTION * lwcollection_clone(const LWCOLLECTION *lwgeom)
Clone LWCOLLECTION object.
Definition: lwcollection.c:124
int lwpoly_is_closed(const LWPOLY *poly)
Definition: lwpoly.c:499
LWPOLY * lwpoly_clone(const LWPOLY *lwgeom)
Definition: lwpoly.c:213
void ptarray_copy_point(POINTARRAY *pa, uint32_t from, uint32_t to)
Definition: lwgeom_api.c:394
double lwcurvepoly_perimeter_2d(const LWCURVEPOLY *poly)
Definition: lwcurvepoly.c:159
double lwpoly_perimeter(const LWPOLY *poly)
Compute the sum of polygon rings length.
Definition: lwpoly.c:467
LWTRIANGLE * lwtriangle_clone(const LWTRIANGLE *lwgeom)
Definition: lwtriangle.c:99
#define FP_NEQUALS(A, B)
char lwpoint_same(const LWPOINT *p1, const LWPOINT *p2)
Definition: lwpoint.c:264
LWCIRCSTRING * lwcircstring_clone(const LWCIRCSTRING *curve)
Definition: lwcircstring.c:124
LWPOLY * lwpoly_force_dims(const LWPOLY *lwpoly, int hasz, int hasm, double zval, double mval)
Definition: lwpoly.c:394
int p2d_same(const POINT2D *p1, const POINT2D *p2)
Definition: lwalgorithm.c:49
int lwgeom_is_closed(const LWGEOM *geom)
Return true or false depending on whether a geometry is a linear feature that closes on itself.
Definition: lwgeom.c:1053
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition: lwgeom.c:707
LWGEOM * lwgeom_force_dims(const LWGEOM *geom, int hasz, int hasm, double zval, double mval)
Definition: lwgeom.c:817
static double trim_preserve_decimal_digits(double d, int32_t decimal_digits)
Definition: lwgeom.c:2511
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
LWGEOM * lwcompound_as_lwgeom(const LWCOMPOUND *obj)
Definition: lwgeom.c:324
void lwgeom_set_geodetic(LWGEOM *geom, int value)
Set the FLAGS geodetic bit on geometry an all sub-geometries and pointlists.
Definition: lwgeom.c:964
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
LWGEOM * lwgeom_simplify(const LWGEOM *igeom, double dist, int preserve_collapsed)
Simplification.
Definition: lwgeom.c:1870
char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
geom1 same as geom2 iff
Definition: lwgeom.c:591
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:309
int lwgeom_ndims(const LWGEOM *geom)
Return the number of dimensions (2, 3, 4) in a geometry.
Definition: lwgeom.c:955
uint32_t lwtype_get_collectiontype(uint8_t type)
Given an lwtype number, what homogeneous collection can hold it?
Definition: lwgeom.c:1131
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:927
int lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt)
Definition: lwgeom.c:2135
int lwgeom_is_collection(const LWGEOM *geom)
Determine whether a LWGEOM can contain sub-geometries or not.
Definition: lwgeom.c:1097
LWMPOINT * lwgeom_as_lwmpoint(const LWGEOM *lwgeom)
Definition: lwgeom.c:242
void lwgeom_set_srid(LWGEOM *geom, int32_t srid)
Set the SRID on an LWGEOM For collections, only the parent gets an SRID, all the children get SRID_UN...
Definition: lwgeom.c:1547
void lwgeom_trim_bits_in_place(LWGEOM *geom, int32_t prec_x, int32_t prec_y, int32_t prec_z, int32_t prec_m)
Trim the bits of an LWGEOM in place, to optimize it for compression.
Definition: lwgeom.c:2539
void lwgeom_longitude_shift(LWGEOM *lwgeom)
Definition: lwgeom.c:1008
LWGEOM * lwgeom_segmentize2d(const LWGEOM *lwgeom, double dist)
Definition: lwgeom.c:771
LWGEOM * lwgeom_as_multi(const LWGEOM *lwgeom)
Create a new LWGEOM of the appropriate MULTI* type.
Definition: lwgeom.c:380
double lwgeom_perimeter_2d(const LWGEOM *geom)
Definition: lwgeom.c:1930
int lwpoint_inside_circle(const LWPOINT *p, double cx, double cy, double rad)
Definition: lwgeom.c:662
static int cmp_point_x(const void *pa, const void *pb)
Definition: lwgeom.c:1570
LWGEOM * lwmline_as_lwgeom(const LWMLINE *obj)
Definition: lwgeom.c:299
LWGEOM * lwmpoint_as_lwgeom(const LWMPOINT *obj)
Definition: lwgeom.c:304
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:329
int lwgeom_is_trajectory(const LWGEOM *geom)
Return LW_TRUE or LW_FALSE depending on whether or not a geometry is a linestring with measure value ...
Definition: lwgeom.c:2496
LWGEOM * lwgeom_force_sfs(LWGEOM *geom, int version)
Definition: lwgeom.c:849
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep-clone an LWGEOM object.
Definition: lwgeom.c:529
LWMPOLY * lwgeom_as_lwmpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:260
LWGEOM * lwgeom_force_3dm(const LWGEOM *geom, double mval)
Definition: lwgeom.c:805
int lwgeom_has_srid(const LWGEOM *geom)
Return true or false depending on whether a geometry has a valid SRID set.
Definition: lwgeom.c:1404
LWGEOM * lwgeom_force_4d(const LWGEOM *geom, double zval, double mval)
Definition: lwgeom.c:811
double lwgeom_length(const LWGEOM *geom)
Definition: lwgeom.c:1952
LWGEOM * lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance)
Definition: lwgeom.c:1471
int lwgeom_needs_bbox(const LWGEOM *geom)
Check whether or not a lwgeom is big enough to warrant a bounding box.
Definition: lwgeom.c:1208
int lwgeom_remove_repeated_points_in_place(LWGEOM *geom, double tolerance)
Definition: lwgeom.c:1594
LWGEOM * lwpsurface_as_lwgeom(const LWPSURFACE *obj)
Definition: lwgeom.c:289
LWGEOM * lwgeom_force_3dz(const LWGEOM *geom, double zval)
Definition: lwgeom.c:799
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:934
LWGEOM * lwmpoly_as_lwgeom(const LWMPOLY *obj)
Definition: lwgeom.c:294
int lwtype_is_collection(uint8_t type)
Return TRUE if the geometry may contain sub-geometries, i.e.
Definition: lwgeom.c:1105
char * lwgeom_to_ewkt(const LWGEOM *lwgeom)
Return an alloced string.
Definition: lwgeom.c:565
void lwgeom_drop_bbox(LWGEOM *lwgeom)
Call this function to drop BBOX and SRID from LWGEOM.
Definition: lwgeom.c:682
int lwgeom_isfinite(const LWGEOM *lwgeom)
Check if a LWGEOM has any non-finite (NaN or Inf) coordinates.
Definition: lwgeom.c:2681
static int lwcollection_dimensionality(const LWCOLLECTION *col)
Definition: lwgeom.c:1413
LWTRIANGLE * lwgeom_as_lwtriangle(const LWGEOM *lwgeom)
Definition: lwgeom.c:224
double lwgeom_area(const LWGEOM *geom)
Definition: lwgeom.c:1885
LWMLINE * lwgeom_as_lwmline(const LWGEOM *lwgeom)
Definition: lwgeom.c:251
uint32_t lwgeom_count_vertices(const LWGEOM *geom)
Count points in an LWGEOM.
Definition: lwgeom.c:1246
LWGEOM * lwgeom_reverse(const LWGEOM *geom)
Definition: lwgeom.c:94
int lwgeom_dimension(const LWGEOM *geom)
For an LWGEOM, returns 0 for points, 1 for lines, 2 for polygons, 3 for volume, and the max dimension...
Definition: lwgeom.c:1298
LWCOLLECTION * lwgeom_subdivide(const LWGEOM *geom, uint32_t maxvertices)
Definition: lwgeom.c:2488
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
LWGEOM * lwgeom_as_curve(const LWGEOM *lwgeom)
Create a new LWGEOM of the appropriate CURVE* type.
Definition: lwgeom.c:420
void lwgeom_swap_ordinates(LWGEOM *in, LWORD o1, LWORD o2)
Swap ordinate values in every vertex of the geometry.
Definition: lwgeom.c:1478
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition: lwgeom.c:1997
uint8_t lwtype_multitype(uint8_t type)
Definition: lwgeom.c:370
LWGEOM * lwgeom_boundary(LWGEOM *lwgeom)
Definition: lwgeom.c:2560
LWCURVEPOLY * lwgeom_as_lwcurvepoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:206
const GBOX * lwgeom_get_bbox(const LWGEOM *lwg)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:743
static int cmp_point_y(const void *pa, const void *pb)
Definition: lwgeom.c:1582
double lwgeom_length_2d(const LWGEOM *geom)
Definition: lwgeom.c:1974
uint32_t lwgeom_count_rings(const LWGEOM *geom)
Count rings in an LWGEOM.
Definition: lwgeom.c:1356
void lwgeom_reverse_in_place(LWGEOM *geom)
Reverse vertex order of LWGEOM.
Definition: lwgeom.c:103
void lwgeom_force_clockwise(LWGEOM *lwgeom)
Force Right-hand-rule on LWGEOM polygons.
Definition: lwgeom.c:38
int lwgeom_is_solid(const LWGEOM *geom)
Return LW_TRUE if geometry has SOLID flag.
Definition: lwgeom.c:948
#define STATIC_ASSERT(COND, MSG)
Definition: lwgeom.c:2508
LWGEOM * lwtin_as_lwgeom(const LWTIN *obj)
Definition: lwgeom.c:284
LWGEOM * lwcurvepoly_as_lwgeom(const LWCURVEPOLY *obj)
Definition: lwgeom.c:319
uint8_t MULTITYPE[NUMTYPES]
Look-up for the correct MULTI* type promotion for singleton types.
Definition: lwgeom.c:354
LWTIN * lwgeom_as_lwtin(const LWGEOM *lwgeom)
Definition: lwgeom.c:277
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
Definition: lwgeom.c:491
int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
Calculate the gbox for this geometry, a cartesian box or geodetic box, depending on how it is flagged...
Definition: lwgeom.c:755
LWGEOM * lwcircstring_as_lwgeom(const LWCIRCSTRING *obj)
Definition: lwgeom.c:314
LWPSURFACE * lwgeom_as_lwpsurface(const LWGEOM *lwgeom)
Definition: lwgeom.c:269
void lwgeom_add_bbox_deep(LWGEOM *lwgeom, GBOX *gbox)
Compute a box for geom and all sub-geometries, if not already computed.
Definition: lwgeom.c:714
int lwgeom_dimensionality(const LWGEOM *geom)
Return the dimensionality (relating to point/line/poly) of an lwgeom.
Definition: lwgeom.c:1426
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition: lwgeom.c:233
void lwgeom_free(LWGEOM *lwgeom)
Definition: lwgeom.c:1155
LWGEOM * lwtriangle_as_lwgeom(const LWTRIANGLE *obj)
Definition: lwgeom.c:334
LWCIRCSTRING * lwgeom_as_lwcircstring(const LWGEOM *lwgeom)
Definition: lwgeom.c:188
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:215
static void lwgeom_subdivide_recursive(const LWGEOM *geom, uint8_t dimension, uint32_t maxvertices, uint32_t depth, LWCOLLECTION *col, double gridSize)
Definition: lwgeom.c:2284
void lwgeom_release(LWGEOM *lwgeom)
Free the containing LWGEOM and the associated BOX.
Definition: lwgeom.c:468
double lwgeom_perimeter(const LWGEOM *geom)
Definition: lwgeom.c:1908
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:941
void lwgeom_scale(LWGEOM *geom, const POINT4D *factor)
Definition: lwgeom.c:2051
LWCOLLECTION * lwgeom_subdivide_prec(const LWGEOM *geom, uint32_t maxvertices, double gridSize)
Definition: lwgeom.c:2465
LWGEOM * lwgeom_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
Definition: lwgeom.c:2105
LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid)
Definition: lwgeom.c:2267
int lwgeom_is_clockwise(LWGEOM *lwgeom)
Check clockwise orientation on LWGEOM polygons.
Definition: lwgeom.c:66
int lwgeom_simplify_in_place(LWGEOM *geom, double epsilon, int preserve_collapsed)
Definition: lwgeom.c:1737
void lwgeom_add_bbox(LWGEOM *lwgeom)
Ensure there's a box in the LWGEOM.
Definition: lwgeom.c:695
LWGEOM * lwgeom_force_2d(const LWGEOM *geom)
Strip out the Z/M components of an LWGEOM.
Definition: lwgeom.c:793
void lwgeom_drop_srid(LWGEOM *lwgeom)
Definition: lwgeom.c:765
void lwgeom_grid_in_place(LWGEOM *geom, const gridspec *grid)
Definition: lwgeom.c:2166
LWCOMPOUND * lwgeom_as_lwcompound(const LWGEOM *lwgeom)
Definition: lwgeom.c:197
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:83
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition: lwutil.c:177
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:101
static double distance2d_sqr_pt_pt(const POINT2D *p1, const POINT2D *p2)
Definition: lwinline.h:35
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwinline.h:203
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:131
int value
Definition: genraster.py:62
type
Definition: ovdump.py:42
static double pivot(double *left, double *right)
Definition: rt_statistics.c:40
double ymax
Definition: liblwgeom.h:357
double xmax
Definition: liblwgeom.h:355
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
lwflags_t flags
Definition: liblwgeom.h:353
uint32_t ngeoms
Definition: liblwgeom.h:580
uint8_t type
Definition: liblwgeom.h:578
LWGEOM ** geoms
Definition: liblwgeom.h:575
LWGEOM ** rings
Definition: liblwgeom.h:603
uint32_t nrings
Definition: liblwgeom.h:608
uint8_t type
Definition: liblwgeom.h:462
GBOX * bbox
Definition: liblwgeom.h:458
int32_t srid
Definition: liblwgeom.h:460
lwflags_t flags
Definition: liblwgeom.h:461
POINTARRAY * points
Definition: liblwgeom.h:483
uint8_t type
Definition: liblwgeom.h:486
LWLINE ** geoms
Definition: liblwgeom.h:547
uint32_t ngeoms
Definition: liblwgeom.h:552
uint32_t ngeoms
Definition: liblwgeom.h:538
LWPOINT ** geoms
Definition: liblwgeom.h:533
POINTARRAY * point
Definition: liblwgeom.h:471
uint8_t type
Definition: liblwgeom.h:474
POINTARRAY ** rings
Definition: liblwgeom.h:519
uint32_t nrings
Definition: liblwgeom.h:524
POINTARRAY * points
Definition: liblwgeom.h:495
double y
Definition: liblwgeom.h:390
double x
Definition: liblwgeom.h:390
double m
Definition: liblwgeom.h:414
double x
Definition: liblwgeom.h:414
double z
Definition: liblwgeom.h:414
double y
Definition: liblwgeom.h:414
lwflags_t flags
Definition: liblwgeom.h:431
uint32_t maxpoints
Definition: liblwgeom.h:428
uint32_t npoints
Definition: liblwgeom.h:427
Snap-to-grid.
Definition: liblwgeom.h:1375