PostGIS  3.4.0dev-r@@SVN_REVISION@@
lwgeodetic_measures.c
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  * This MobilityDB code is provided under The PostgreSQL License.
4  * Copyright (c) 2016-2023, Université libre de Bruxelles and MobilityDB
5  * contributors
6  *
7  * MobilityDB includes portions of PostGIS version 3 source code released
8  * under the GNU General Public License (GPLv2 or later).
9  * Copyright (c) 2001-2023, PostGIS contributors
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose, without fee, and without a written
13  * agreement is hereby granted, provided that the above copyright notice and
14  * this paragraph and the following two paragraphs appear in all copies.
15  *
16  * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
17  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
18  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
19  * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
20  * OF SUCH DAMAGE.
21  *
22  * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
23  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
24  * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
25  * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
26  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
27  *
28  *****************************************************************************/
29 
39 // #include "point/geography_funcs.h"
40 
41 /* C */
42 #include <float.h>
43 /* PostGIS */
44 #include <liblwgeom_internal.h>
45 #include <lwgeodetic_tree.h>
46 
47 /*****************************************************************************/
48 
49 
50 /***********************************************************************
51  * ST_LineSubstring for geographies
52  ***********************************************************************/
53 
59 static void
61  const POINT4D *p1, const POINT4D *p2, POINT4D *p,
62  const SPHEROID *s, double f)
63 
64 {
65  GEOGRAPHIC_POINT g, g1, g2;
66  geographic_point_init(p1->x, p1->y, &g1);
67  geographic_point_init(p2->x, p2->y, &g2);
68  int success;
69  double dist, dir;
70 
71  /* Special sphere case */
72  if ( s == NULL || s->a == s->b )
73  {
74  /* Calculate distance and direction between g1 and g2 */
75  dist = sphere_distance(&g1, &g2);
76  dir = sphere_direction(&g1, &g2, dist);
77  /* Compute interpolation point */
78  success = sphere_project(&g1, dist*f, dir, &g);
79  }
80  /* Spheroid case */
81  else
82  {
83  /* Calculate distance and direction between g1 and g2 */
84  dist = spheroid_distance(&g1, &g2, s);
85  dir = spheroid_direction(&g1, &g2, s);
86  /* Compute interpolation point */
87  success = spheroid_project(&g1, s, dist*f, dir, &g);
88  }
89 
90  /* If success, use newly computed lat and lon,
91  * otherwise return precomputed cartesian result */
92  if (success == LW_SUCCESS)
93  {
96  }
97 }
98 
99 
103 LWGEOM *
105  const LWLINE *lwline,
106  const SPHEROID *s,
107  double from, double to,
108  double tolerance)
109 {
110  POINTARRAY *dpa;
111  POINTARRAY *ipa = lwline->points;
112  LWGEOM *lwresult;
113  POINT4D pt;
114  POINT4D p1, p2;
115  GEOGRAPHIC_POINT g1, g2;
116  int nsegs, i;
117  double length, slength, tlength;
118  int state = 0; /* 0 = before, 1 = inside */
119  uint32_t srid = lwline->srid;
120 
121  /*
122  * Create a dynamic pointarray with an initial capacity
123  * equal to full copy of input points
124  */
126  (char) FLAGS_GET_Z(ipa->flags),
127  (char) FLAGS_GET_M(ipa->flags),
128  ipa->npoints);
129 
130  /* Compute total line length */
131  length = ptarray_length_spheroid(ipa, s);
132 
133  /* Get 'from' and 'to' lengths */
134  from = length * from;
135  to = length * to;
136  tlength = 0;
137  getPoint4d_p(ipa, 0, &p1);
138  geographic_point_init(p1.x, p1.y, &g1);
139  nsegs = ipa->npoints - 1;
140  for (i = 0; i < nsegs; i++)
141  {
142  double dseg;
143  getPoint4d_p(ipa, (uint32_t) i+1, &p2);
144  geographic_point_init(p2.x, p2.y, &g2);
145 
146  /* Find the length of this segment */
147  /* Special sphere case */
148  if ( s->a == s->b )
149  slength = s->radius * sphere_distance(&g1, &g2);
150  /* Spheroid case */
151  else
152  slength = spheroid_distance(&g1, &g2, s);
153 
154  /* We are before requested start. */
155  if (state == 0) /* before */
156  {
157  if (fabs ( from - ( tlength + slength ) ) <= tolerance)
158  {
159  /* Second point is our start */
160  ptarray_append_point(dpa, &p2, LW_FALSE);
161  state = 1; /* we're inside now */
162  goto END;
163  }
164  else if (fabs(from - tlength) <= tolerance)
165  {
166  /* First point is our start */
167  ptarray_append_point(dpa, &p1, LW_FALSE);
168  /*
169  * We're inside now, but will check
170  * 'to' point as well
171  */
172  state = 1;
173  }
174  /*
175  * Didn't reach the 'from' point,
176  * nothing to do
177  */
178  else if (from > tlength + slength)
179  {
180  goto END;
181  }
182  else /* tlength < from < tlength+slength */
183  {
184  /* Our start is between first and second point */
185  dseg = (from - tlength) / slength;
186  interpolate_point4d_spheroid(&p1, &p2, &pt, s, dseg);
187  ptarray_append_point(dpa, &pt, LW_FALSE);
188  /* We're inside now, but will check 'to' point as well */
189  state = 1;
190  }
191  }
192 
193  if (state == 1) /* inside */
194  {
195  /* 'to' point is our second point. */
196  if (fabs(to - ( tlength + slength ) ) <= tolerance )
197  {
198  ptarray_append_point(dpa, &p2, LW_FALSE);
199  break; /* substring complete */
200  }
201  /*
202  * 'to' point is our first point.
203  * (should only happen if 'to' is 0)
204  */
205  else if (fabs(to - tlength) <= tolerance)
206  {
207  ptarray_append_point(dpa, &p1, LW_FALSE);
208  break; /* substring complete */
209  }
210  /*
211  * Didn't reach the 'end' point,
212  * just copy second point
213  */
214  else if (to > tlength + slength)
215  {
216  ptarray_append_point(dpa, &p2, LW_FALSE);
217  goto END;
218  }
219  /*
220  * 'to' point falls on this segment
221  * Interpolate and break.
222  */
223  else if (to < tlength + slength )
224  {
225  dseg = (to - tlength) / slength;
226  interpolate_point4d_spheroid(&p1, &p2, &pt, s, dseg);
227  ptarray_append_point(dpa, &pt, LW_FALSE);
228  break;
229  }
230  }
231 
232  END:
233  tlength += slength;
234  memcpy(&p1, &p2, sizeof(POINT4D));
235  }
236 
237  if (dpa->npoints <= 1) {
238  lwresult = lwpoint_as_lwgeom(lwpoint_construct(srid, NULL, dpa));
239  }
240  else {
241  lwresult = lwline_as_lwgeom(lwline_construct(srid, NULL, dpa));
242  }
243 
244  return lwresult;
245 }
246 
247 
248 /***********************************************************************
249  * Interpolate a point along a geographic line.
250  ***********************************************************************/
251 
255 LWGEOM *
257  const LWLINE *line, double length_fraction,
258  const SPHEROID *s, char repeat)
259 {
260  POINT4D pt;
261  uint32_t i;
262  uint32_t points_to_interpolate;
263  uint32_t points_found = 0;
264  double length;
265  double length_fraction_increment = length_fraction;
266  double length_fraction_consumed = 0;
267  char has_z = (char) lwgeom_has_z(lwline_as_lwgeom(line));
268  char has_m = (char) lwgeom_has_m(lwline_as_lwgeom(line));
269  const POINTARRAY* ipa = line->points;
270  POINTARRAY *opa;
271  POINT4D p1, p2;
272  POINT3D q1, q2;
273  LWGEOM *lwresult;
274  GEOGRAPHIC_POINT g1, g2;
275  uint32_t srid = line->srid;
276 
277  /* Empty.InterpolatePoint == Point Empty */
278  if ( lwline_is_empty(line) )
279  {
280  return lwgeom_clone_deep(lwline_as_lwgeom(line));
281  }
282 
283  /*
284  * If distance is one of the two extremes, return the point on that
285  * end rather than doing any computations
286  */
287  if ( length_fraction == 0.0 || length_fraction == 1.0 )
288  {
289  if ( length_fraction == 0.0 )
290  getPoint4d_p(ipa, 0, &pt);
291  else
292  getPoint4d_p(ipa, ipa->npoints-1, &pt);
293 
294  opa = ptarray_construct(has_z, has_m, 1);
295  ptarray_set_point4d(opa, 0, &pt);
296  return lwpoint_as_lwgeom(lwpoint_construct(srid, NULL, opa));
297  }
298 
299  /* Interpolate points along the line */
300  length = ptarray_length_spheroid(ipa, s);
301  points_to_interpolate = repeat ? (uint32_t) floor(1 / length_fraction) : 1;
302  opa = ptarray_construct(has_z, has_m, points_to_interpolate);
303 
304  getPoint4d_p(ipa, 0, &p1);
305  geographic_point_init(p1.x, p1.y, &g1);
306  for ( i = 0; i < ipa->npoints - 1 && points_found < points_to_interpolate; i++ )
307  {
308  double segment_length_frac;
309  getPoint4d_p(ipa, i+1, &p2);
310  geographic_point_init(p2.x, p2.y, &g2);
311 
312  /* Special sphere case */
313  if ( s->a == s->b )
314  segment_length_frac = s->radius * sphere_distance(&g1, &g2) / length;
315  /* Spheroid case */
316  else
317  segment_length_frac = spheroid_distance(&g1, &g2, s) / length;
318 
319  /* If our target distance is before the total length we've seen
320  * so far. create a new point some distance down the current
321  * segment.
322  */
323  while ( length_fraction < length_fraction_consumed + segment_length_frac && points_found < points_to_interpolate )
324  {
325  geog2cart(&g1, &q1);
326  geog2cart(&g2, &q2);
327  double segment_fraction = (length_fraction - length_fraction_consumed) / segment_length_frac;
328  interpolate_point4d_spheroid(&p1, &p2, &pt, s, segment_fraction);
329  ptarray_set_point4d(opa, points_found++, &pt);
330  length_fraction += length_fraction_increment;
331  }
332 
333  length_fraction_consumed += segment_length_frac;
334 
335  p1 = p2;
336  g1 = g2;
337  }
338 
339  /* Return the last point on the line. This shouldn't happen, but
340  * could if there's some floating point rounding errors. */
341  if (points_found < points_to_interpolate)
342  {
343  getPoint4d_p(ipa, ipa->npoints - 1, &pt);
344  ptarray_set_point4d(opa, points_found, &pt);
345  }
346 
347  if (opa->npoints <= 1)
348  {
349  lwresult = lwpoint_as_lwgeom(lwpoint_construct(srid, NULL, opa));
350  } else {
351  lwresult = lwmpoint_as_lwgeom(lwmpoint_construct(srid, opa));
352  }
353 
354  return lwresult;
355 }
356 
360 double
362  const POINTARRAY *pa,
363  const POINT4D *p4d,
364  const SPHEROID *s,
365  double tolerance,
366  double *mindistout,
367  POINT4D *proj4d)
368 {
369  GEOGRAPHIC_EDGE e;
370  GEOGRAPHIC_POINT a, b, nearest = {0}; /* make compiler quiet */
371  POINT4D p1, p2;
372  const POINT2D *p;
373  POINT2D proj;
374  uint32_t i, seg = 0;
375  int use_sphere = (s->a == s->b ? 1 : 0);
376  int hasz;
377  double za = 0.0, zb = 0.0;
378  double distance,
379  length, /* Used for computing lengths */
380  seglength = 0.0, /* length of the segment where the closest point is located */
381  partlength = 0.0, /* length from the beginning of the point array to the closest point */
382  totlength = 0.0; /* length of the point array */
383 
384  /* Initialize our point */
385  geographic_point_init(p4d->x, p4d->y, &a);
386 
387  /* Handle point/point case here */
388  if ( pa->npoints <= 1)
389  {
390  double mindist = 0.0;
391  if ( pa->npoints == 1 )
392  {
393  p = getPoint2d_cp(pa, 0);
394  geographic_point_init(p->x, p->y, &b);
395  /* Sphere special case, axes equal */
396  mindist = s->radius * sphere_distance(&a, &b);
397  /* If close or greater than tolerance, get the real answer to be sure */
398  if ( ! use_sphere || mindist > 0.95 * tolerance )
399  {
400  mindist = spheroid_distance(&a, &b, s);
401  }
402  }
403  if ( mindistout ) *mindistout = mindist;
404  return 0.0;
405  }
406 
407  /* Make result really big, so that everything will be smaller than it */
408  distance = FLT_MAX;
409 
410  /* Initialize start of line */
411  p = getPoint2d_cp(pa, 0);
412  geographic_point_init(p->x, p->y, &(e.start));
413 
414  /* Iterate through the edges in our line */
415  for ( i = 1; i < pa->npoints; i++ )
416  {
417  double d;
418  p = getPoint2d_cp(pa, i);
419  geographic_point_init(p->x, p->y, &(e.end));
420  /* Get the spherical distance between point and edge */
421  d = s->radius * edge_distance_to_point(&e, &a, &b);
422  /* New shortest distance! Record this distance / location / segment */
423  if ( d < distance )
424  {
425  distance = d;
426  nearest = b;
427  seg = i - 1;
428  }
429  /* We've gotten closer than the tolerance... */
430  if ( d < tolerance )
431  {
432  /* Working on a sphere? The answer is correct, return */
433  if ( use_sphere )
434  {
435  break;
436  }
437  /* Far enough past the tolerance that the spheroid calculation won't change things */
438  else if ( d < tolerance * 0.95 )
439  {
440  break;
441  }
442  /* On a spheroid and near the tolerance? Confirm that we are *actually* closer than tolerance */
443  else
444  {
445  d = spheroid_distance(&a, &nearest, s);
446  /* Yes, closer than tolerance, return! */
447  if ( d < tolerance )
448  break;
449  }
450  }
451  e.start = e.end;
452  }
453 
454  if ( mindistout ) *mindistout = distance;
455 
456  /* See if we have a third dimension */
457  hasz = (bool) FLAGS_GET_Z(pa->flags);
458 
459  /* Initialize first point of array */
460  getPoint4d_p(pa, 0, &p1);
461  geographic_point_init(p1.x, p1.y, &a);
462  if ( hasz )
463  za = p1.z;
464 
465  /* Loop and sum the length for each segment */
466  for ( i = 1; i < pa->npoints; i++ )
467  {
468  getPoint4d_p(pa, i, &p1);
469  geographic_point_init(p1.x, p1.y, &b);
470  if ( hasz )
471  zb = p1.z;
472 
473  /* Special sphere case */
474  if ( s->a == s->b )
475  length = s->radius * sphere_distance(&a, &b);
476  /* Spheroid case */
477  else
478  length = spheroid_distance(&a, &b, s);
479 
480  /* Add in the vertical displacement if we're in 3D */
481  if ( hasz )
482  length = sqrt( (zb-za)*(zb-za) + length*length );
483 
484  /* Add this segment length to the total length */
485  totlength += length;
486 
487  /* Add this segment length to the partial length */
488  if (i - 1 < seg)
489  partlength += length;
490  else if (i - 1 == seg)
491  /* Save segment length for computing the final value of partlength */
492  seglength = length;
493 
494  /* B gets incremented in the next loop, so we save the value here */
495  a = b;
496  za = zb;
497  }
498 
499  /* Copy nearest into 2D/4D holder */
500  proj4d->x = proj.x = rad2deg(nearest.lon);
501  proj4d->y = proj.y = rad2deg(nearest.lat);
502 
503  /* Compute distance from beginning of the segment to closest point */
504 
505  /* Start of the segment */
506  getPoint4d_p(pa, seg, &p1);
507  geographic_point_init(p1.x, p1.y, &a);
508 
509  /* Closest point */
510  geographic_point_init(proj4d->x, proj4d->y, &b);
511 
512  /* Special sphere case */
513  if ( s->a == s->b )
514  length = s->radius * sphere_distance(&a, &b);
515  /* Spheroid case */
516  else
517  length = spheroid_distance(&a, &b, s);
518 
519  if ( hasz )
520  {
521  /* Compute Z and M values for closest point */
522  double f = length / seglength;
523  getPoint4d_p(pa, seg + 1, &p2);
524  proj4d->z = p1.z + ((p2.z - p1.z) * f);
525  proj4d->m = p1.m + ((p2.m - p1.m) * f);
526  /* Add in the vertical displacement if we're in 3D */
527  za = p1.z;
528  zb = proj4d->z;
529  length = sqrt( (zb-za)*(zb-za) + length*length );
530  }
531 
532  /* Add this segment length to the total */
533  partlength += length;
534 
535  /* Location of any point on a zero-length line is 0 */
536  /* See http://trac.osgeo.org/postgis/ticket/1772#comment:2 */
537  if ( partlength == 0 || totlength == 0 )
538  return 0.0;
539 
540  /* For robustness, force 0 when closest point == startpoint */
541  p = getPoint2d_cp(pa, 0);
542  if ( seg == 0 && p2d_same(&proj, p) )
543  return 0.0;
544 
545  /* For robustness, force 1 when closest point == endpoint */
546  p = getPoint2d_cp(pa, pa->npoints - 1);
547  if ( (seg >= (pa->npoints-2)) && p2d_same(&proj, p) )
548  return 1.0;
549 
550  return partlength / totlength;
551 }
552 
553 /*****************************************************************************/
char * s
Definition: cu_in_wkt.c:23
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
#define LW_FALSE
Definition: liblwgeom.h:94
#define LW_SUCCESS
Definition: liblwgeom.h:97
LWGEOM * lwmpoint_as_lwgeom(const LWMPOINT *obj)
Definition: lwgeom.c:304
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:529
POINTARRAY * ptarray_construct(char hasz, char hasm, uint32_t npoints)
Construct an empty pointarray, allocating storage and setting the npoints, but not filling in any inf...
Definition: ptarray.c:51
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:934
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:165
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition: lwpoint.c:129
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition: ptarray.c:59
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:166
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:125
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_FALSE,...
Definition: ptarray.c:147
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:941
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition: lwgeom_api.c:369
LWMPOINT * lwmpoint_construct(int32_t srid, const POINTARRAY *pa)
Definition: lwmpoint.c:52
int lwline_is_empty(const LWLINE *line)
int p2d_same(const POINT2D *p1, const POINT2D *p2)
Definition: lwalgorithm.c:49
double longitude_radians_normalize(double lon)
Convert a longitude to the range of -PI,PI.
Definition: lwgeodetic.c:50
double latitude_radians_normalize(double lat)
Convert a latitude to the range of -PI/2,PI/2.
Definition: lwgeodetic.c:78
int sphere_project(const GEOGRAPHIC_POINT *r, double distance, double azimuth, GEOGRAPHIC_POINT *n)
Given a starting location r, a distance and an azimuth to the new point, compute the location of the ...
Definition: lwgeodetic.c:1320
void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g)
Initialize a geographic point.
Definition: lwgeodetic.c:180
double ptarray_length_spheroid(const POINTARRAY *pa, const SPHEROID *s)
Definition: lwgeodetic.c:3243
double sphere_distance(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e)
Given two points on a unit sphere, calculate their distance apart in radians.
Definition: lwgeodetic.c:948
double sphere_direction(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e, double d)
Given two points on a unit sphere, calculate the direction from s to e.
Definition: lwgeodetic.c:979
double edge_distance_to_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest)
Definition: lwgeodetic.c:1222
void geog2cart(const GEOGRAPHIC_POINT *g, POINT3D *p)
Convert spherical coordinates to cartesian coordinates on unit sphere.
Definition: lwgeodetic.c:404
#define rad2deg(r)
Definition: lwgeodetic.h:81
double spheroid_distance(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid)
Computes the shortest distance along the surface of the spheroid between two points,...
Definition: lwspheroid.c:79
int spheroid_project(const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g)
Given a location, an azimuth and a distance, computes the location of the projected point.
Definition: lwspheroid.c:128
double spheroid_direction(const GEOGRAPHIC_POINT *r, const GEOGRAPHIC_POINT *s, const SPHEROID *spheroid)
Computes the forward azimuth of the geodesic joining two points on the spheroid, using the inverse ge...
Definition: lwspheroid.c:105
double ptarray_locate_point_spheroid(const POINTARRAY *pa, const POINT4D *p4d, const SPHEROID *s, double tolerance, double *mindistout, POINT4D *proj4d)
Locate a point along the point array defining a geographic line.
LWGEOM * geography_interpolate_points(const LWLINE *line, double length_fraction, const SPHEROID *s, char repeat)
Interpolate a point along a geographic line.
LWGEOM * geography_substring(const LWLINE *lwline, const SPHEROID *s, double from, double to, double tolerance)
Return the part of a line between two fractional locations.
static void interpolate_point4d_spheroid(const POINT4D *p1, const POINT4D *p2, POINT4D *p, const SPHEROID *s, double f)
Find interpolation point p between geography points p1 and p2 so that the len(p1,p) == len(p1,...
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 distance(double x1, double y1, double x2, double y2)
Definition: lwtree.c:1032
GEOGRAPHIC_POINT start
Definition: lwgeodetic.h:64
GEOGRAPHIC_POINT end
Definition: lwgeodetic.h:65
Two-point great circle segment from a to b.
Definition: lwgeodetic.h:63
Point in spherical coordinates on the world.
Definition: lwgeodetic.h:54
POINTARRAY * points
Definition: liblwgeom.h:483
int32_t srid
Definition: liblwgeom.h:484
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 npoints
Definition: liblwgeom.h:427