PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches

◆ 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}
void * lwalloc(size_t size)
Definition lwutil.c:227
double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
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:783
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:207
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition lwgeom.c:969
#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: