PostGIS 3.0.6dev-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 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}
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: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
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:161
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: