PostGIS  3.7.0dev-r@@SVN_REVISION@@

◆ lwgeom_tcpa()

double lwgeom_tcpa ( const LWGEOM g1,
const LWGEOM g2,
double *  mindist 
)

Find the time of closest point of approach.

Parameters
mindistif not null will be set to the minimum distance between the trajectories at the closest point of approach.
Returns
the time value in which the minimum distance was reached, -1 if inputs are invalid (lwerror is called in that case), -2 if the trajectories do not share any point in time.

Definition at line 1138 of file lwlinearreferencing.c.

1139 {
1140  LWLINE *l1, *l2;
1141  int i;
1142  GBOX gbox1, gbox2;
1143  double tmin, tmax;
1144  double *mvals;
1145  int nmvals = 0;
1146  double mintime;
1147  double mindist2 = FLT_MAX; /* minimum distance, squared */
1148 
1149  if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2))
1150  {
1151  lwerror("Both input geometries must have a measure dimension");
1152  return -1;
1153  }
1154 
1155  l1 = lwgeom_as_lwline(g1);
1156  l2 = lwgeom_as_lwline(g2);
1157 
1158  if (!l1 || !l2)
1159  {
1160  lwerror("Both input geometries must be linestrings");
1161  return -1;
1162  }
1163 
1164  if (l1->points->npoints < 2 || l2->points->npoints < 2)
1165  {
1166  lwerror("Both input lines must have at least 2 points");
1167  return -1;
1168  }
1169 
1170  /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */
1171  /* because we cannot afford the float rounding inaccuracy when */
1172  /* we compare the ranges for overlap below */
1173  lwgeom_calculate_gbox(g1, &gbox1);
1174  lwgeom_calculate_gbox(g2, &gbox2);
1175 
1176  /*
1177  * Find overlapping M range
1178  * WARNING: may be larger than the real one
1179  */
1180 
1181  tmin = FP_MAX(gbox1.mmin, gbox2.mmin);
1182  tmax = FP_MIN(gbox1.mmax, gbox2.mmax);
1183 
1184  if (tmax < tmin)
1185  {
1186  LWDEBUG(1, "Inputs never exist at the same time");
1187  return -2;
1188  }
1189 
1190  // lwnotice("Min:%g, Max:%g", tmin, tmax);
1191 
1192  /*
1193  * Collect M values in common time range from inputs
1194  */
1195 
1196  mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints));
1197 
1198  /* TODO: also clip the lines ? */
1199  nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals);
1200  nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals);
1201 
1202  /* Sort values in ascending order */
1203  qsort(mvals, nmvals, sizeof(double), compare_double);
1204 
1205  /* Remove duplicated values */
1206  nmvals = uniq(mvals, nmvals);
1207 
1208  if (nmvals < 2)
1209  {
1210  {
1211  /* there's a single time, must be that one... */
1212  double t0 = mvals[0];
1213  POINT4D p0, p1;
1214  LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0);
1215  if (mindist)
1216  {
1217  if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0))
1218  {
1219  lwfree(mvals);
1220  lwerror("Could not find point with M=%g on first geom", t0);
1221  return -1;
1222  }
1223  if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0))
1224  {
1225  lwfree(mvals);
1226  lwerror("Could not find point with M=%g on second geom", t0);
1227  return -1;
1228  }
1229  *mindist = distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1);
1230  }
1231  lwfree(mvals);
1232  return t0;
1233  }
1234  }
1235 
1236  /*
1237  * For each consecutive pair of measures, compute time of closest point
1238  * approach and actual distance between points at that time
1239  */
1240  mintime = tmin;
1241  for (i = 1; i < nmvals; ++i)
1242  {
1243  double t0 = mvals[i - 1];
1244  double t1 = mvals[i];
1245  double t;
1246  POINT4D p0, p1, q0, q1;
1247  int seg;
1248  double dist2;
1249 
1250  // lwnotice("T %g-%g", t0, t1);
1251 
1252  seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0);
1253  if (-1 == seg)
1254  continue; /* possible, if GBOX is approximated */
1255  // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z);
1256 
1257  seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg);
1258  if (-1 == seg)
1259  continue; /* possible, if GBOX is approximated */
1260  // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z);
1261 
1262  seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0);
1263  if (-1 == seg)
1264  continue; /* possible, if GBOX is approximated */
1265  // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z);
1266 
1267  seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg);
1268  if (-1 == seg)
1269  continue; /* possible, if GBOX is approximated */
1270  // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z);
1271 
1272  t = segments_tcpa(&p0, &p1, &q0, &q1, t0, t1);
1273 
1274  /*
1275  lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g",
1276  p0.x, p0.y, p0.z,
1277  q0.x, q0.y, q0.z, t);
1278  */
1279 
1280  dist2 = (q0.x - p0.x) * (q0.x - p0.x) + (q0.y - p0.y) * (q0.y - p0.y) + (q0.z - p0.z) * (q0.z - p0.z);
1281  if (dist2 < mindist2)
1282  {
1283  mindist2 = dist2;
1284  mintime = t;
1285  // lwnotice("MINTIME: %g", mintime);
1286  }
1287  }
1288 
1289  /*
1290  * Release memory
1291  */
1292 
1293  lwfree(mvals);
1294 
1295  if (mindist)
1296  {
1297  *mindist = sqrt(mindist2);
1298  }
1299  /*lwnotice("MINDIST: %g", sqrt(mindist2));*/
1300 
1301  return mintime;
1302 }
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
Definition: measures3d.c:1066
void lwfree(void *mem)
Definition: lwutil.c:248
int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
Calculate bounding box of a geometry, automatically taking into account whether it is cartesian or ge...
Definition: lwgeom.c:755
void * lwalloc(size_t size)
Definition: lwutil.c:227
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:941
#define FP_MAX(A, B)
#define FP_MIN(A, B)
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:106
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static int ptarray_collect_mvals(const POINTARRAY *pa, double tmin, double tmax, double *mvals)
static int compare_double(const void *pa, const void *pb)
static double segments_tcpa(POINT4D *p0, const POINT4D *p1, POINT4D *q0, const POINT4D *q1, double t0, double t1)
static int ptarray_locate_along_linear(const POINTARRAY *pa, double m, POINT4D *p, uint32_t from)
static int uniq(double *vals, int nvals)
double mmax
Definition: liblwgeom.h:361
double mmin
Definition: liblwgeom.h:360
POINTARRAY * points
Definition: liblwgeom.h:483
double x
Definition: liblwgeom.h:414
double z
Definition: liblwgeom.h:414
double y
Definition: liblwgeom.h:414
uint32_t npoints
Definition: liblwgeom.h:427

References compare_double(), distance3d_pt_pt(), FP_MAX, FP_MIN, lwalloc(), LWDEBUG, LWDEBUGF, lwerror(), lwfree(), lwgeom_as_lwline(), lwgeom_calculate_gbox(), lwgeom_has_m(), GBOX::mmax, GBOX::mmin, POINTARRAY::npoints, LWLINE::points, ptarray_collect_mvals(), ptarray_locate_along_linear(), segments_tcpa(), uniq(), POINT4D::x, POINT4D::y, and POINT4D::z.

Referenced by ST_ClosestPointOfApproach(), ST_DistanceCPA(), and test_lwgeom_tcpa().

Here is the call graph for this function:
Here is the caller graph for this function: