PostGIS  3.0.6dev-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 1144 of file lwlinearreferencing.c.

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

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: