PostGIS  3.4.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 1150 of file lwlinearreferencing.c.

1151 {
1152  LWLINE *l1, *l2;
1153  int i;
1154  GBOX gbox1, gbox2;
1155  double tmin, tmax;
1156  double *mvals;
1157  int nmvals = 0;
1158  double mintime;
1159  double mindist2 = FLT_MAX; /* minimum distance, squared */
1160 
1161  if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2))
1162  {
1163  lwerror("Both input geometries must have a measure dimension");
1164  return -1;
1165  }
1166 
1167  l1 = lwgeom_as_lwline(g1);
1168  l2 = lwgeom_as_lwline(g2);
1169 
1170  if (!l1 || !l2)
1171  {
1172  lwerror("Both input geometries must be linestrings");
1173  return -1;
1174  }
1175 
1176  if (l1->points->npoints < 2 || l2->points->npoints < 2)
1177  {
1178  lwerror("Both input lines must have at least 2 points");
1179  return -1;
1180  }
1181 
1182  /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */
1183  /* because we cannot afford the float rounding inaccuracy when */
1184  /* we compare the ranges for overlap below */
1185  lwgeom_calculate_gbox(g1, &gbox1);
1186  lwgeom_calculate_gbox(g2, &gbox2);
1187 
1188  /*
1189  * Find overlapping M range
1190  * WARNING: may be larger than the real one
1191  */
1192 
1193  tmin = FP_MAX(gbox1.mmin, gbox2.mmin);
1194  tmax = FP_MIN(gbox1.mmax, gbox2.mmax);
1195 
1196  if (tmax < tmin)
1197  {
1198  LWDEBUG(1, "Inputs never exist at the same time");
1199  return -2;
1200  }
1201 
1202  // lwnotice("Min:%g, Max:%g", tmin, tmax);
1203 
1204  /*
1205  * Collect M values in common time range from inputs
1206  */
1207 
1208  mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints));
1209 
1210  /* TODO: also clip the lines ? */
1211  nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals);
1212  nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals);
1213 
1214  /* Sort values in ascending order */
1215  qsort(mvals, nmvals, sizeof(double), compare_double);
1216 
1217  /* Remove duplicated values */
1218  nmvals = uniq(mvals, nmvals);
1219 
1220  if (nmvals < 2)
1221  {
1222  {
1223  /* there's a single time, must be that one... */
1224  double t0 = mvals[0];
1225  POINT4D p0, p1;
1226  LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0);
1227  if (mindist)
1228  {
1229  if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0))
1230  {
1231  lwfree(mvals);
1232  lwerror("Could not find point with M=%g on first geom", t0);
1233  return -1;
1234  }
1235  if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0))
1236  {
1237  lwfree(mvals);
1238  lwerror("Could not find point with M=%g on second geom", t0);
1239  return -1;
1240  }
1241  *mindist = distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1);
1242  }
1243  lwfree(mvals);
1244  return t0;
1245  }
1246  }
1247 
1248  /*
1249  * For each consecutive pair of measures, compute time of closest point
1250  * approach and actual distance between points at that time
1251  */
1252  mintime = tmin;
1253  for (i = 1; i < nmvals; ++i)
1254  {
1255  double t0 = mvals[i - 1];
1256  double t1 = mvals[i];
1257  double t;
1258  POINT4D p0, p1, q0, q1;
1259  int seg;
1260  double dist2;
1261 
1262  // lwnotice("T %g-%g", t0, t1);
1263 
1264  seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0);
1265  if (-1 == seg)
1266  continue; /* possible, if GBOX is approximated */
1267  // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z);
1268 
1269  seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg);
1270  if (-1 == seg)
1271  continue; /* possible, if GBOX is approximated */
1272  // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z);
1273 
1274  seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0);
1275  if (-1 == seg)
1276  continue; /* possible, if GBOX is approximated */
1277  // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z);
1278 
1279  seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg);
1280  if (-1 == seg)
1281  continue; /* possible, if GBOX is approximated */
1282  // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z);
1283 
1284  t = segments_tcpa(&p0, &p1, &q0, &q1, t0, t1);
1285 
1286  /*
1287  lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g",
1288  p0.x, p0.y, p0.z,
1289  q0.x, q0.y, q0.z, t);
1290  */
1291 
1292  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);
1293  if (dist2 < mindist2)
1294  {
1295  mindist2 = dist2;
1296  mintime = t;
1297  // lwnotice("MINTIME: %g", mintime);
1298  }
1299  }
1300 
1301  /*
1302  * Release memory
1303  */
1304 
1305  lwfree(mvals);
1306 
1307  if (mindist)
1308  {
1309  *mindist = sqrt(mindist2);
1310  }
1311  /*lwnotice("MINDIST: %g", sqrt(mindist2));*/
1312 
1313  return mintime;
1314 }
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
Definition: measures3d.c:1029
void lwfree(void *mem)
Definition: lwutil.c:242
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: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
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: