cyschneck / centerline-width

A Python package to find the centerline and width of rivers based on the latitude and longitude of the right and left bank
MIT License
42 stars 9 forks source link

Widths don't seem to be perpendicular to centerline #4

Closed mthaddon closed 8 months ago

mthaddon commented 8 months ago

I've attached an image showing part of a centerline-widths plot. I'd like to understand why the widths don't seem to be perpendicular to the centerline. (Please let me know if you'd prefer feedback in another way, I'm not sure this is really an "issue" or a problem with the options I'm passing to centerline-width.

Figure_1

cyschneck commented 8 months ago

At a glance it looks like the centerline is jagged and the width lines are made in reference to the centerline so that might be impacting the direction they generate

You can try smoothing the centerline but if you include your data I can give it a look in more depth

mthaddon commented 8 months ago

Sure, I've retried it with apply_smoothing=True but get a similar result:

Figure_2

Here's the data:

nordschleife_0.csv

cyschneck commented 8 months ago

So, the width lines are not generated by making a line that is perpendicular to the centerline, but perpendicular to the average slopes of the nearby "Evenly Spaced" centerline points. This is what transect_span_distance is for and transect_span_distance=3 means that slope between three points is averaged, and then -1/(average of three slopes) produces the perpendicular line for that span.

227870492-69d105b2-0d3e-4d50-90d9-e938400a58fb

But looking over your question, I think I could make this more clear and expand the functionality to match your question better. Right now, the centerline that you see is not the value that the width lines are generated from. Instead, on the backend it is using either "Evenly Spaced" or "Smoothed" coordinates to find the width lines. This was done to make the width points generate at an even space from each other along the centerline

The Voronoi centerline that you see when using display_true_centerline is overlaid on top and is not where the points come from directly, that is why it is called the 'true' centerline. You can see it a little in the image from below when the Voronoi centerline and the scattered Evenly Spaced (where the width line slopes are generated) are overlaid with each other and they are offset a bit. This is the result of converting Voronoi into Evenly Spaced (see here for how that will impact the lines)

image

But, if you want to be able to get the width lines generated from the Voronoi instead of the default "Evenly Spaced" (or optional Smoothed) it will likely run into the issue where the Voronoi points are not evenly spaced, so the slopes between them are going to be more dramatic and unpredictable. But for the sake of completeness I will add this functionality.

Can you post the code that you are using to generate the river_object and river_object.plotCenterlineWidth so I can give it look as well?

cyschneck commented 8 months ago

I've been looking into this more and I believe I found the discrepancy you are referring to.

The issue appear to be the result of how the plot is being displayed. By default, Matplotlib will not have equal x and y axis values, but instead plots them individually, so it is possible for the final image to appear squashed/stretched. When Matplotlib is forced to be equal (when ax.set_aspect('equal', adjustable='datalim')) you can see the lines appear perpendicular

Current Default (Unequal) Axis Equal Axis

For debugging purposes, here is the same short river, but the lines for both the slope and the normal line (which will be the width line) are plotted and the axis are adjusted to be equal

image

slope = (30.03784314898877 - 30.0378681399185) /  (-92.86789302061173 - -92.86788520796165)
slope = 3.1987775552842788
normal_of_slope = -0.31261942498878414
angle = arctan((normal_of_slope - slope) / (1 + slope * normal_of_slope)))
angle = -90.0

I have been working a change to how the backend slope is calculated (which may be implemented if it shows noticable improvements) but you can see that even changing how the slope is calculated, the resulting slope and normal are still perpendicular when the axis are adjusted to be equal

Current Version Upcoming Version

I'll add the option to plot the axes equally in the next release this weekend

mthaddon commented 8 months ago

Thanks for the very detailed explanation. I think ax.set_aspect('equal', adjustable='datalim') is a code change, as I don't see an option for changing that currently?

I'm generating the river_object as follows:

centerline_width.riverCenterline(
    csv_data="%s_%s.csv" % (file_prefix, i),
    interpolate_data=True,
    interpolate_n=50,
    interpolate_n_centerpoints=17000,
    equal_distance=5,
)

And then for the widths:

plot_args = dict(
    apply_smoothing=True,
    remove_intersections=True,
)
river_object.plotCenterlineWidth(**plot_args)
river_width_dict = river_object.riverWidthFromCenterline(**plot_args)
cyschneck commented 8 months ago

Yes, it is currently in development with a few other pieces, but if all the tests pass it should be able to go out this weekend

cyschneck commented 8 months ago

New release is out with the new flag, but some of the current testing structure for width lines seems to having some trouble, so I'll fix that this week

cyschneck commented 8 months ago

Ok, hot fix is up for the bug in v1.4.0. The newest release v1.4.1 is up and seems to working as expected. I'll be running it through more tests this week, but should be able to add the new equal_axis option for this issue