Hello. I think the example in the following picture clarifies my confusing question. The two points represent a car that happened to be at the shown points at the shown time with the shown speed (orange number; just to know I don't need to calculate the speed, already have it).
It would be great if there is a tool or if you know a(n easy) way to calculate/estimate the (date and) timestamp when the car crossed the yellow'ish line. Of course it is possible "by hand" with just measuring the distance and calculating the mean speed, but I have around 39,000 unique trips with around 3,700,000 points travelling a distance up to around 25km and around 30 cross sections over a span of 2 weeks.
I am on Windows 10 and I use ArcGIS Pro 3.0.1.
Thank you in advance for taking your time to read and in the best case answer my question.
@Adam1 Interesting problem - the picture helps. You can try using the Generate Near Table geoprocessing tool to get the distance of the points from the line. Looks like you have the speed and time stamp values available to incorporate some calculations using the Near Table generated. The table can be exported as Excel using Table To Excel for ease of using formulas if you prefer spreadsheets as output.
Hello, thank you for your suggestion. I had a look at it and tried it, but I am not really sure how to go on after using that tool. I will try the other idea first, but I will keep this one in mind! Thanks!
This seems like a good job for Python.
This script assumes velocity in m/s and constant acceleration between the points. It outputs a table that lists each trip with all crossed lines with crossing time and velocity.
To run it:
import arcpy, datetime
# define inputs
in_points = "Path/to/points"
trip_field = "TripID"
time_field = "Time"
velocity_field = "Velocity"
in_cross_sections = "Path/to/cross_sections"
cross_section_field = "CrossSectionID"
# define outputs
# for your huge amount of data, I recommend saving into RAM, so it runs faster
# don't forget to export the table afterwards!
out_folder = "memory"
out_name = "result"
# function for calculating the time and velocity at which the vehicle hits the point of interest
# this assumes constant acceleration!
# v0: start velocity in [m/s]
# a: acceleration in [m/s/s]
# s: distance from start to point of interest in [m]
# returns a tuple of (seconds, velocity)
def calc_time_and_velocity(v0, a, s):
# for constant acceleration:
# v(t) = a * t + v0
# s(t) = 0.5 * a * t^2 + v0 * t
# ==> t(s) = -v0/a +- sqrt( (v0 / a)^2 + 2 * s / a )
t = -v0 / a - math.sqrt( math.pow(v0 / a, 2) + 2 * s / a)
if t <= 0:
t = -v0 / a + math.sqrt( math.pow(v0 / a, 2) + 2 * s / a)
v = a * t + v0
return (t, v)
# create output table
out_table = arcpy.management.CreateTable(out_folder, out_name)
arcpy.management.AddField(out_table, "Trip", "LONG")
arcpy.management.AddField(out_table, "CrossSection", "LONG")
arcpy.management.AddField(out_table, "Time", "DATE")
arcpy.management.AddField(out_table, "Velocity", "FLOAT")
# read input data
points = [row for row in arcpy.da.SearchCursor(in_points, ["SHAPE@", trip_field, time_field, velocity_field])]
cross_sections = [row for row in arcpy.da.SearchCursor(in_cross_sections, ["SHAPE@", cross_section_field])]
sr = points[0][0].spatialReference
# get all trips
trips = list({p[1] for p in points})
# start writing into the output table
with arcpy.da.InsertCursor(out_table, ["Trip", "CrossSection", "Time", "Velocity"]) as cursor:
# loop through the trips
for trip in trips:
# extract the points of that trip
trip_points = [p for p in points if p[1] == trip]
# sort by time
trip_points.sort(key=lambda p: p[2])
# loop through point pairs
for i in range(1, len(trip_points)):
p1 = trip_points[i-1]
p2 = trip_points[i]
line = arcpy.Polyline(arcpy.Array([p1[0].firstPoint, p2[0].firstPoint]), sr)
# find intersecting cross sections
intersecting_cs = [cs for cs in cross_sections if not line.disjoint(cs[0])]
# loop through those cross_sections
for cs in intersecting_cs:
# get the intersection point
p = line.intersect(cs[0], 1)
# calculate time and velocity
dv = p2[3] - p1[3]
dt = (p2[2] - p1[2]).seconds
a = dv / dt
s = p1[0].distanceTo(p)
t, v = calc_time_and_velocity(p1[3], a, s)
time = p1[2] + datetime.timedelta(seconds=t)
# write into the output table
cursor.insertRow([trip, cs[1], time, v])
Results for some made-up test data:
Again, this script assumes velocity values given in meters per second, so you have to calculate that from your values (I'm assuming these are km/h).
Hello, thank you so much for your huge effort!
I tried it with two different datasets but unfortunately I got some errors.
With the first dataset I got this error:
Traceback (most recent call last):
File "<string>", line 48, in <module>
File "<string>", line 48, in <listcomp>
RuntimeError: A column was specified that does not exist.
This probably has to do something with how I altered my data and somehow did something wrong/weird to the "SHAPE"/geometry attributes of it?
Then I tried it with a "clean" dataset which I have not worked with before and got this error:
Traceback (most recent call last):
File "<string>", line 62, in <module>
TypeError: '<' not supported between instances of 'NoneType' and 'NoneType'
Do you have an idea why this is?
Thank you.
First one is wrong column names. Second one tries to sort empty date values.
Can you send me a small subset of your points, either here or in a private message? In most cases, it's easier to troubleshoot that way.
So I don't really know why, but I don't get those errors anymore, I just tried another different dataset (Initial data was in 650 files, which I first merged together into 5 files and then those 5 again into one file).
Instead I first had a division by zero error when "a" was zero when both velocities were the same. I fixed that by including this to your code:
# for constant acceleration:
# v(t) = a * t + v0
# s(t) = 0.5 * a * t^2 + v0 * t
# ==> t(s) = -v0/a +- sqrt( (v0 / a)^2 + 2 * s / a )
if a != 0:
t = -v0 / a - math.sqrt(math.pow(v0 / a, 2) + 2 * s / a)
if t <= 0:
t = -v0 / a + math.sqrt(math.pow(v0 / a, 2) + 2 * s / a)
v = a * t + v0
else:
t = s / v0
v = v0
return (t, v)
Now I get a ValueError: math domain error. I included some prints in your code to see what values cause that error:
Trip: 1292665561
p2[2] - p1[2]
2019-09-19 08:12:59 - 2019-09-19 08:12:44.000001
dt: 14
-
p2[3] - p1[3]
0.2777777777777778 - 5.277777777777778
dv: -5.0
-
a: -0.35714285714285715
-
distToCross: 56.91868192594062
v0: 5.277777777777778
a: -0.35714285714285715
s: 56.91868192594062
Traceback (most recent call last):
File "<string>", line 121, in <module>
File "<string>", line 38, in calc_time_and_velocity
ValueError: math domain error
(and don't ask me why this one datetime has an added .000001)
The situation looks like in the following picture, if it helps. The actual point is just right at the tip of the arrow, so it is just a tad behind the cross-section.
So I changed the code a bit and it worked on this one dataset without errors now. I tink the data sometimes is just a bit odd. I added that if the ValueError occurs, instead of using the given speed values, the calculation should use speed and acceleration values that are calculated from the distance and time between the two individual tracks. I'll see if it will work on all the data.
Edit: Oh and sorry about not getting back to you about sending you a small sample of my data, but I think sadly I am not allowed to, even just a small snippet, sorry!
So now it worked how it should. Thank you so much for your help! I will post the final code that worked for me here soon.