-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Trimming links with length less than scene pixel resolution #11
Comments
Yes, there is actually already a function to do this called If your examples above are not removing the single-pixel links, please post some data that would allow me to recreate the problem. Finally, if you use the |
So I turned off the additional notes call in prune_network and recompiled the package. Having done that should have realised that the function call right below does the same thing... I used the data from issue #10 to produce this. Posted again here: Just a quick observation/stab at the problem: Thanks! I wanted labelled segments so rasterizing the links is better for me in R for now. |
This issue stems from the fact that there are many ways to "correctly" represent a topology or skeletonize a mask. The difficulty with this is that we can't simply delete links, as that would break the network connectivity. We have to delete links and reorganize the network to maintain connectivity, which is non-trivial. One solution could be to simply delete the n-pixels links, and extend the links at its endpoint to cover the deleted portion. However, the issue with this is that we would have two (or more) links occupying n of the same pixel. Perhaps a little difficult to understand with words here, but the short of it is that we have to extend links to maintain connectivity lost by deleting links. |
I implemented a fix for this: First, a links terminal coordinates are its nodes. Second, a link with a distance of 1 or sqrt(2) means that 2 nodes are 8-neighbor connected. Let's say there are 2 links, A and B, with A terminating in node 1 and B terminating in node 2. Nodes 1 and 2 are adjacent in the sense above, so link C connects the two. When you rasterize the scene, because C is so short it won't show up in the raster (at least, in my R-rasterization it did not). That is actually how I found this issue in the first place. We can generate a new node, node 3, as the mean of the coordinates of nodes 1 and 2, and remove nodes 1 and 2. This will arbitrarily put the node on either pixel when rasterizing, but maintain connectivity between the nodes and links. To your point above, the corresponding links don't need to be adjusted in length, but their connectivity does. The length doesn't need to be adjusted because if you maintain a raster with the same resolution as the resolution you generated the links, they'll intersect on the edge of the pixel anyways. I have an "OK" fix for this in R, by no means good, the code is below. links and nodes are "sf" objects (simple features), and the code iterates through length 2 < pixresolution nodes.
|
That's a little hard to digest without a diagram. Could you whip one up real quick? Also, RG keeps track of each pixel of each link (the 'idx' attribute), so no need to find these instances by length. |
Oh, I should also mention that there is a slight bug in the |
Working on it! Rereading see that it's confusing, but basically the short links are artifacts of the skeleton. I'm not using that as a starting point, I'm rasterizing the links LINESTRING in R, which is calling gdal. Any links that have total length <2*pixres where pixres is the spatial resolution of the target raster won't show in that raster. Waiting for something to finish and will hit you with some pictures. |
OK, I am still on the version prior to the one you put out last week (so links and nodes are unprojected in EPSG 4326). In QGIS if we take a look at link 1292, we see that it is pretty short. The two nodes are on adjacent pixels in fact. If we rasterize the links LINEFEATURE in R, which I believe calls gdal to do (see st_rasterize, https://cran.r-project.org/web/packages/stars/vignettes/stars5.html), because that link is so small it doesn't show up. In the image below different colors arbitrarily represent different individual links. The display here is not projected so may be distorted a little. The corresponding rasterized nodes look like this: The basic idea of the script I put above is to define a new node in the original POINT object whose location is the midpoint between the original 2 nodes (1, 2) and whose connectivity is the union of links adjacent to nodes 1, 2 minus the short link. The short link is then deleted. Because that node is on the border of the pixel corresponding to two links, I don't change the link length (anyways the link length is discrete based on the mask resolution and this value wouldn't 'fit'). Visually, the orange is the new node: The resulting rasterized node section loses one of the adjacent nodes, but the rasterized links will look the same. Looking now, the actual links won't lie on the node but if you need to check what links spatially intersect an intersection with a tolerance of pixres x sqrt(2) will work. Otherwise the connectivity remains the same. The distance from the link end to the node is something like pixres x sqrt(2)/2. |
I forgot to add here, the way I implemented it was to get out of Python as fast as possible because I had to get some results. There's probably a faster way to do it and a proper place to do it, like the prune_network call, rather than what I did. |
Thanks for the detailed write up with figs. My initial thought here is that this would be a post-processing tool, maybe a function called The reason it wasn't implemented (beyond links of one pixel length) is as described above--it was just too complicated and low priority during the initial development of RG. RG has additional constraints that you don't adhere to in your processing tool--the primary of these being that each link can be explicitly mapped back to the raster space (i.e. links are collection of pixel centroids), and links must connect all nodes (i.e. there can't be a gap as shown in your final figure). The rest of RG functionality is built under the assumption that those constraints are met, so it would be a very big job to try to implement this as a pre-processing tool (or default processing step). If you (or anyone) ever wanted to take a stab at developing something for Python, that'd be great! Given the current task list, it seems unlikely that I'd get to it in the near future. |
I ran into the following issue. There is a link present which is so small that when I rasterize my links json in R that it literally has no points (some kind of interpolation issue probably). It's visible in the image below. So the link is caused because there are an excess number of links here in part due to these small islands. It has a length of 30sqrt(2), so basically 2 nodes which are diagonally adjacent on a 30-m resolution grid. I found 4 total links like this on the scene.
I see two solutions:
Compute all link lengths
For all link lengths < cutoff (say 2 pix) {
remove the link. Combine the two nodes to be one node
Connections of new node will be downstream/upstream neighbors (should max be 4 neighbors?)
More examples below:
The text was updated successfully, but these errors were encountered: