Skip to content
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

st_nearest_points do not intersect nearest line feature #790

Closed
bcaradima opened this issue Jul 9, 2018 · 15 comments
Closed

st_nearest_points do not intersect nearest line feature #790

bcaradima opened this issue Jul 9, 2018 · 15 comments

Comments

@bcaradima
Copy link

I have a sites (pt, POINT) and river network (lines, LINESTRING) dataset. Often, a site is located a few meters away from the nearest line feature. For a given site I would like to locate the nearest point along the nearest line that intersects that line using only the sf package (which is a fantastic package BTW!). I have posted this problem on Stack Exchange but have not found a satisfactory and reliable solution since posting. @barryrowlingson and others replied on SE, and @barryrowlingson kindly posted an issue (#788) requesting such a feature on sf that has already been implemented in 0.6.4.

I installed sf_0.6-4 and made use of st_nearest_feature() and st_nearest_points(), but found that the LINESTRING output from st_nearest_points sometimes does not intersect the nearest line. I'm not sure why this is happening and have tried using the st_snap function as well without success. I would appreciate any help in understanding why the nearest point here is still a small distance away from intersecting the nearest line. I've posted a reproducible example below since I'm uncertain if this is a package issue or not; if it isn't then closing the issue would be OK with me.

library(sf)
library(tidyverse)

pt <- structure(list(SiteId = "NAWA_9", EZG_NR = 102382, h1 = 187641, 
                     h2 = 188276, geometry = structure(list(structure(c(605975, 
                                                                        220845), class = c("XY", "POINT", "sfg"))), class = c("sfc_POINT", 
                                                                                                                              "sfc"), precision = 0, bbox = structure(c(605975, 220845, 
                                                                                                                                                                        605975, 220845), .Names = c("xmin", "ymin", "xmax", "ymax"
                                                                                                                                                                        ), class = "bbox"), crs = structure(list(epsg = NA_integer_, 
                                                                                                                                                                                                                 proj4string = "+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 +x_0=600000 +y_0=200000 +ellps=bessel +units=m +no_defs"), .Names = c("epsg", 
                                                                                                                                                                                                                                                                                                                                                                            "proj4string"), class = "crs"), n_empty = 0L)), .Names = c("SiteId", 
                                                                                                                                                                                                                                                                                                                                                                                                                                       "EZG_NR", "h1", "h2", "geometry"), row.names = c(NA, -1L), class = c("sf", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "data.frame"), sf_column = "geometry", agr = structure(c(NA_integer_, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     NA_integer_, NA_integer_, NA_integer_), class = "factor", .Label = c("constant", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          "aggregate", "identity"), .Names = c("SiteId", "EZG_NR", "h1", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               "h2")))

lines <- structure(list(OBJECTID_1 = c(50285, 50459, 50460, 50893, 51233, 
                                       51826), FNODE_ = c(67310, 67495, 67197, 67935, 68287, 68934), 
                        TNODE_ = c(67197, 65833, 67495, 67495, 67935, 67935), LPOLY_ = c(0, 
                                                                                         0, 0, 0, 0, 0), RPOLY_ = c(0, 0, 0, 0, 0, 0), LENGTH = c(450.060414716, 
                                                                                                                                                  1714.92888098, 349.503218869, 381.106076374, 605.268729687, 
                                                                                                                                                  835.277620554), OBJECTID = c(1555924, 1556346, 1556073, 17889262, 
                                                                                                                                                                               17889263, 17889261), OBJECTVAL = c("Bach", "Bach", "Bach_U", 
                                                                                                                                                                                                                  "Bach", "Bach", "Bach"), NAME = c(NA, "Limpach", NA, "Limpach", 
                                                                                                                                                                                                                                                    NA, "Limpach"), UNTERIRDIS = c(NA, NA, "unbestimmt", NA, 
                                                                                                                                                                                                                                                                                   NA, NA), OBJECTORIG = c("LK25", "LK25", "LK25", "GN25", "GN25", 
                                                                                                                                                                                                                                                                                                           "GN25"), YEAROFCHAN = c(1998, 2000, 2000, 2005, 2005, 2005
                                                                                                                                                                                                                                                                                                           ), GEWISSNR = c(12343, 796, 12343, 796, 380438, 796), LAUFNR = c(0, 
                                                                                                                                                                                                                                                                                                                                                                            0, 0, 0, 0, 0), LINST = c("CH", "CH", "CH", "CH", "CH", "CH"
                                                                                                                                                                                                                                                                                                                                                                            ), BACHNR = c(NA_character_, NA_character_, NA_character_, 
                                                                                                                                                                                                                                                                                                                                                                                          NA_character_, NA_character_, NA_character_), GWLNR = c("CH0123430000", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                  "CH0007960000", "CH0123430000", "CH0007960000", "CH3804380000", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                  "CH0007960000"), Binary = c(1L, 1L, 1L, 1L, 1L, 1L), Shape_Leng = c(450.060414716, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      1714.92888098, 349.503218869, 381.106025792, 605.268728778, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      835.27768502), ReachId = c(50285L, 50459L, 50460L, 50893L, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 51233L, 51826L), SiteId = c("NAWA_9", "NAWA_9", "NAWA_9", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             "NAWA_9", "NAWA_9", "NAWA_9"), EZG_NR = c(102382, 102382, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       102382, 102382, 102382, 102382), h1 = c(187641, 187641, 187641, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               187641, 187641, 187641), h2 = c(188276, 188276, 188276, 188276, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               188276, 188276), geometry = structure(list(structure(c(605489.669045818, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      605491.875, 605509.375, 605519.375, 605529.375, 605546.875, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      605566.875, 605584.375, 605596.875, 605609.375, 605635.625, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      220964.545921764, 220965.59375, 220966.90625, 220969.40625, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      220969.40625, 220973.09375, 220975.59375, 220976.203125, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      220973.703125, 220968.703125, 220950.59375), .Dim = c(11L, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            2L), class = c("XY", "LINESTRING", "sfg")), structure(c(605916.125, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    605931.625, 605985.375, 606039.1875, 606092.625, 606113.125, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    606126.625, 606173.125, 606266.375, 606311.483253449, 220742.09375, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    220766.5, 220852.796875, 220939.203125, 221025.09375, 221058.796875, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    221073.90625, 221109.09375, 221179.5, 221214.752345892), .Dim = c(10L, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      2L), class = c("XY", "LINESTRING", "sfg")), structure(c(605635.625, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              605916.125, 220950.59375, 220742.09375), .Dim = c(2L, 2L), class = c("XY", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   "LINESTRING", "sfg")), structure(c(605756.541259766, 605767.375, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      605800.6875, 605836.3125, 605854.875, 605877.375, 605916.125, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      220397.253540039, 220423.09375, 220508.296875, 220594.59375, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      220641.5, 220680.59375, 220742.09375), .Dim = c(7L, 2L), class = c("XY", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         "LINESTRING", "sfg")), structure(c(605762.325220285, 605756.541259766, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            220392.666224025, 220397.253540039), .Dim = c(2L, 2L), class = c("XY", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             "LINESTRING", "sfg")), structure(c(605755.910450333, 605756.541259766, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                220395.72591234, 220397.253540039), .Dim = c(2L, 2L), class = c("XY", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "LINESTRING", "sfg"))), n_empty = 0L, crs = structure(list(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  epsg = NA_integer_, proj4string = "+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 +x_0=600000 +y_0=200000 +ellps=bessel +units=m +no_defs"), .Names = c("epsg", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 "proj4string"), class = "crs"), class = c("sfc_LINESTRING", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           "sfc"), precision = 0, bbox = structure(c(605489.669045818, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     220392.666224025, 606311.483253449, 221214.752345892), .Names = c("xmin", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       "ymin", "xmax", "ymax"), class = "bbox"))), .Names = c("OBJECTID_1", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              "FNODE_", "TNODE_", "LPOLY_", "RPOLY_", "LENGTH", "OBJECTID", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              "OBJECTVAL", "NAME", "UNTERIRDIS", "OBJECTORIG", "YEAROFCHAN", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              "GEWISSNR", "LAUFNR", "LINST", "BACHNR", "GWLNR", "Binary", "Shape_Leng", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              "ReachId", "SiteId", "EZG_NR", "h1", "h2", "geometry"), row.names = c("50285", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    "50459", "50460", "50893", "51233", "51826"), class = c("sf", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "data.frame"), sf_column = "geometry", agr = structure(c(NA_integer_, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     NA_integer_, NA_integer_, NA_integer_, NA_integer_, NA_integer_, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     NA_integer_, NA_integer_, NA_integer_, NA_integer_, NA_integer_, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     NA_integer_, NA_integer_, NA_integer_, NA_integer_, NA_integer_, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     NA_integer_, NA_integer_, NA_integer_, NA_integer_, NA_integer_, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     NA_integer_, NA_integer_, NA_integer_), class = "factor", .Label = c("constant", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          "aggregate", "identity"), .Names = c("OBJECTID_1", "FNODE_", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               "TNODE_", "LPOLY_", "RPOLY_", "LENGTH", "OBJECTID", "OBJECTVAL", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               "NAME", "UNTERIRDIS", "OBJECTORIG", "YEAROFCHAN", "GEWISSNR", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               "LAUFNR", "LINST", "BACHNR", "GWLNR", "Binary", "Shape_Leng", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               "ReachId", "SiteId", "EZG_NR", "h1", "h2")))

# Select nearest reach
reach.nearest <- st_nearest_feature(pt, lines)
reach.nearest <- lines[reach.nearest,]
st_distance(pt, reach.nearest)

# Locate nearest point along nearest reach (Returns line)
pt.nearest <- st_nearest_points(pt, reach.nearest)
cat("Line intersects:", st_intersects(pt.nearest, reach.nearest, sparse=FALSE)[1,1], "|")
st_distance(pt.nearest, reach.nearest)

pt.nearest <- st_cast(pt.nearest, "POINT")[2]
cat("Point (st_cast) intersects:", st_intersects(pt.nearest, reach.nearest, sparse=FALSE)[1,1], "|")

pt.snap <- st_snap(pt.nearest, reach.nearest, tolerance = 1e-9)
cat("Point (st_snap) intersects:", st_intersects(pt.snap, reach.nearest, sparse=FALSE)[1,1], "|\n")
st_distance(pt.snap, reach.nearest)
@bcaradima bcaradima changed the title st_nearest_points do not intersect nearest feature st_nearest_points do not intersect nearest line feature Jul 9, 2018
@edzer
Copy link
Member

edzer commented Jul 9, 2018

This is what happens when doing this kind of calculations with floating point doubles. Setting precision before doing the computations might help; read the help of st_set_precision and st_as_binary.

@bcaradima
Copy link
Author

bcaradima commented Jul 9, 2018

Thank you, Edzer. If I understand the help documents correctly, lowering the precision to an acceptable value (considering the spatial scale of the analysis) would allow st_intersect (and other geometric operations) to round the input coordinates of a point and line 3.501388e-11m apart and to confirm that they do intersect. Using the same data, setting the precision to 1e-05m yields the following:

# see for explanation: https://r-spatial.github.io/sf/reference/st_as_binary.html
x <- st_distance(pt.nearest, reach.nearest)
precision <- 1e-05
round(x*precision)/precision

pt.nearest <- st_set_precision(pt.nearest, precision = 1e-05)
reach.nearest <- st_set_precision(reach.nearest, precision = 1e-05)
st_distance(pt.nearest, reach.nearest)
st_intersects(pt.nearest, reach.nearest, sparse=FALSE)

Would it be a worthwhile feature set the precision when using st_read?

@bcaradima bcaradima reopened this Jul 9, 2018
@edzer
Copy link
Member

edzer commented Jul 9, 2018

I don't think you want that much rounding:

> st_set_precision(pt.nearest, precision = 1e-05) %>% st_as_binary %>% st_as_sfc
Geometry set for 1 feature 
geometry type:  POINT
dimension:      XY
bbox:           xmin: 6e+05 ymin: 2e+05 xmax: 6e+05 ymax: 2e+05
epsg (SRID):    NA
proj4string:    NA
POINT (6e+05 2e+05)

@bcaradima
Copy link
Author

My mistake! My coordinates are in meters, so if the nearest site and nearest line coordinates differ at three or four decimal places then that should be an acceptable intersection. So I should set a scale argument of 1000 (to specify 3 decimal places of precision). If the scale argument equals zero, the distance between the two points is 3.501388e-11 meters. But if I use a scale of 1000, the distance increases:

>     p <- 1000
>     pt.nearest <- st_set_precision(pt.nearest, precision = p)
>     reach.nearest <- st_set_precision(reach.nearest, precision = p)
>     st_distance(pt.nearest, reach.nearest)
Units: m
             [,1]
[1,] 0.0001278386
>     st_intersects(pt.nearest, reach.nearest, sparse=FALSE)
      [,1]
[1,] FALSE
>     
>     st_set_precision(pt.nearest, precision = p) %>% st_as_binary %>% st_as_sfc
Geometry set for 1 feature 
geometry type:  POINT
dimension:      XY
bbox:           xmin: 605979 ymin: 220842.5 xmax: 605979 ymax: 220842.5
epsg (SRID):    NA
proj4string:    NA
POINT (605979 220842.5)

That doesn't really make sense, unless I'm missing something.

@edzer
Copy link
Member

edzer commented Jul 9, 2018

Maybe you should set precisions before computing pt.nearest etc.

@bcaradima
Copy link
Author

bcaradima commented Jul 9, 2018

Setting the scale arguments of the example data gives an intersection between the st_nearest_points output and nearest reach, but not the point extracted from the st_nearest_points output and the nearest reach.

> pt <- st_set_precision(pt, precision=1000)
> lines <- st_set_precision(lines, precision=1000)
> 
> # Select nearest reach
> reach.nearest <- st_nearest_feature(pt, lines)
> reach.nearest <- lines[reach.nearest,]
> st_distance(pt, reach.nearest)
Units: m
         [,1]
[1,] 4.684326
> 
> # Locate nearest point along nearest reach (Returns line)
> pt.nearest <- st_nearest_points(pt, reach.nearest)
> cat("Line intersects:", st_intersects(pt.nearest, reach.nearest, sparse=FALSE)[1,1], "|")
Line intersects: TRUE |> st_distance(pt.nearest, reach.nearest)
Units: m
     [,1]
[1,]    0
> 
> pt.nearest <- st_cast(pt.nearest, "POINT")[2]
> cat("Point (st_cast) intersects:", st_intersects(pt.nearest, reach.nearest, sparse=FALSE)[1,1], "|")
Point (st_cast) intersects: FALSE |
> st_distance(pt.nearest, reach.nearest)
Units: m
             [,1]
[1,] 0.0001278386

@edzer
Copy link
Member

edzer commented Jul 10, 2018

I think this problem cannot be solved. Setting precision essentially rounds coordinates to a (scaled) integer grid. If you want to represent points on lines connecting points on an integer grid exactly, you need to store them as rational numbers, i.e. as a ratio of two (possibly very large) integers. What R (or GEOS) does is approximating this ratio when storing it as an eight byte double. By that, we're back at R FAQ 7.31.

@bcaradima
Copy link
Author

bcaradima commented Jul 10, 2018

So if a given point has a nearby location along a line, R cannot exactly match the coordinates of that nearest location even to a limited number of decimal points?

My motive for this particular task is to measure upstream/downstream distances from sites located along a river network, but I suppose I can always work around this by buffering the point slightly and splitting the reach based on that buffer. Interestingly, this can be done in ArcGIS just with intersecting points.

I have since posted an additional question on SE, but I would consider closing it it has no possible solution.

@rsbivand
Copy link
Member

Do not use this issues channel, and preferably not SO (sic) either, because your general approach does not appear to have been thought through.

If you are as you now belatedly reveal dealing with points (sampling points) on a stream network, look first at the specific tools designed to that purpose, in particular the SSN package and STARS ArcGIS toolbox (JSS paper at https://doi.org/10.18637/jss.v056.i02), and the openSTARS package using GRASS rather than ArcGIS. None of your interventions so far show that you have actually done enough background work. Do also see Mira Kattwinkel's talk at eRum 2018 on openSTARS.

If having done this and contacted those researchers, you find that precision is not just your misunderstanding, you may be able to contribute to SSN, STARS and/or openSTARS.

@bcaradima
Copy link
Author

bcaradima commented Jul 11, 2018

Thank you for suggesting these interesting alternatives, however there are several reasons why I believe they are unsuitable. I am more interested in quantifying habitat connectivity and quality for fish communities based on their movement ranges, and would like to integrate fish observations, barriers, and other stream attributes as nodes or edges in a directed graph based on a vector-based river network at a large spatial scale. I would prefer to avoid "reinventing the wheel", but I find that these alternatives are either based on closed-source software (STARS) or are not well-suited to the nature of the data (openSTARS) and the types of analyses (SSN) that I'd like to do.

I opened this issue because the st_nearest_points tool returns points that are 10^-11 units from the nearest line and do not intersect the line. @edzer has pointed out that this cannot be resolved if the precision is lowered and the points and lines still do not intersect. My last question (an ignorant one perhaps) before closing would be whether this task can be done at all in R or if it is a limitation of R itself when doing geometric calculations.

Thank you for your helpful replies.

@edzer
Copy link
Member

edzer commented Jul 11, 2018

I think it is symptomatic of any system that represents its coordinates with finite-precision floating point numbers rather than ratios of integers not limited by size; see WR Franklin, 1984. Cartographic errors symptomatic of underlying algebra problems; International Symposium on Spatial Data Handling, Zurich, Switzerland 286.

It wouldn't be rocket science to write a package that does this, the question is whether this problem is large enough for you to do so.


> fortunes::fortune("there is no if")

Evelyn Hall: I would like to know how (if) I can extract some of the
information from the summary of my nlme.
Simon Blomberg: This is R. There is no if. Only how.
   -- Evelyn Hall and Simon 'Yoda' Blomberg
      R-help (April 2005)

@rsbivand
Copy link
Member

As @edzer commented, this is a feature of the IEEE 754 floating point representation used on most computing hardware. This is an international standard that R and effectively everyone else chooses to use. Really, your problem is conceptual, as these differences cannot be measured in surveying, and the natural phenomena you are representing with points and lines are not points/lines. If you care to contribute to JTS/GEOS/PostGIS to resolve your problem, fine. Do look at GRASS, which uses different topological choices than JTS/GEOS. There vector points are assigned to streams using a raster stream representation in r.stream.snap, so the rasterization resolution of the stream would decide.

@barryrowlingson
Copy link
Contributor

I suspect the problem is that whether a point lies exactly on a line is defined mathematically, but because of finite precision arithmetic two different algorithms, or even two different implementations of the same algorithm may differ in their opinions. In over-simplified terms, the midpoint of a line is (A+B)/2, but floating point precision might mean that's not the same as (B+A)/2. Then depending on how the code computes the distance from that "midpoint" to the line it might well not be absolute zero.

One algorithm's point on a line is another algorithm's point-not-quite-on-a-line.

@bcaradima
Copy link
Author

I've mostly used ArcGIS in the past and locating points along a line was a "trivial" task, with the underlying topology of geodatabases cleanly resolving whether point features intersect. As @edzer and @rsbivand pointed out, differences of 10^-11 units wouldn't matter in my use case, but I just didn't understand why any non-zero distances were returned at all (coming from ArcGIS I haven't seen this happen before).

My current workaround would be to just buffer the sites by a hundredth of a millimeter and then split the lines (which are often hundreds of meters in length) using the buffer. I will also take a look at the possibility of using GRASS.

Again, thank you all for your help.

@rsbivand
Copy link
Member

By the way, because ArcGIS is proprietary, you don't see how they address the same issues of computational geometry. They have to make assumptions about snap distances that may or may not be exposed to the user in the programming API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants