Я пытаюсь создать случайные точки на строке, используя SF. Однако данные являются многострочными, и для st_line_sample
требуется строковая строка. Поэтому я хотел бы превратить многострочную строку только в одну строку. Если вы посмотрите на код, я должен сгенерировать только одну точку для строки, но он генерирует одну точку для каждой строки. Я не могу соединить линии, чтобы они превратились в одну строку, которую можно было бы использовать для создания на ней одной точки.
Как можно получить только одну строку строки из мультилинии, чтобы сгенерировать количество случайных точек на линии?
library(sf)
library(mapview)
chemins.simple %>%
st_zm(geom) %>%
st_cast('LINESTRING') %>%
st_line_sample(n = 1,
type = "regular") %>%
mapview() + mapview(chemins.simple)
Данные ниже
chemins.simple = structure(list(Name = "Sentier Parc Maisonneuve", description = NA_character_,
timestamp = structure(NA_real_, class = c("POSIXct", "POSIXt"
), tzone = ""), begin = structure(NA_real_, class = c("POSIXct",
"POSIXt"), tzone = ""), end = structure(NA_real_, class = c("POSIXct",
"POSIXt"), tzone = ""), altitudeMode = NA_character_, tessellate = NA_integer_,
extrude = NA_integer_, visibility = NA_integer_, drawOrder = NA_integer_,
icon = NA_character_, sentier = NA_character_, layer = NA_character_,
path = NA_character_, geom = structure(list(structure(list(
structure(c(299582.847886435, 299567.18907952, 299533.785023203,
299515.754560643, 299489.877968861, 299474.41195845,
299482.409654204, 299490.968980874, 5047541.45303887,
5047515.52023998, 5047476.83372278, 5047450.17574194,
5047418.25329369, 5047405.40685391, 5047374.54606886,
5047360.74761632, 0, 0, 0, 0, 0, 0, 0, 0), dim = c(8L,
3L)), structure(c(299490.968980874, 299497.29763185,
299517.115667715, 299552.432591057, 5047360.74761632,
5047350.76804681, 5047334.25633307, 5047315.1869463,
0, 0, 0, 0), dim = 4:3), structure(c(299552.432591057,
299572.471975622, 299580.660505604, 299582.021973701,
5047315.1869463, 5047290.4503043, 5047268.99526155, 5047206.73936411,
0, 0, 0, 0), dim = 4:3), structure(c(299582.021973701,
299588.0164882, 299600.947249195, 299630.602987485, 299665.55385166,
299701.470512757, 299727.863346363, 299758.996957146,
299802.35340954, 299839.140374969, 299876.649347262,
299907.772848518, 299930.504446245, 5047206.73936411,
5047178.37871541, 5047157.46467836, 5047127.49377699,
5047104.97108792, 5047092.39963862, 5047085.8799339,
5047078.22045237, 5047075.4591553, 5047067.43201061,
5047049.77064173, 5047028.70621312, 5047003.83116685,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), dim = c(13L,
3L)), structure(c(299651.605306308, 299634.64341842,
299624.972126109, 299582.847886435, 5047607.50989447,
5047604.07051358, 5047596.26302026, 5047541.45303887,
0, 0, 0, 0), dim = 4:3), structure(c(299903.07988968,
299838.094689635, 299772.477232293, 299750.23396301,
299735.638185202, 299715.208156948, 299702.078535598,
299685.670961566, 299669.997900071, 299657.600720696,
299651.605306308, 5047528.38059123, 5047560.33007056,
5047598.91464364, 5047604.02168543, 5047594.94570617,
5047587.32852712, 5047587.33920063, 5047592.8052447,
5047604.45040378, 5047608.09567279, 5047607.50989447,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), dim = c(11L, 3L)),
structure(c(300348.696267149, 300327.322202229, 300298.695288719,
300263.694927326, 300246.383397842, 300232.079546476,
300194.889733763, 300177.248808883, 300154.270060961,
300141.323210388, 300128.611331444, 300115.132109253,
300099.688617737, 300072.709345899, 299903.07988968,
5047307.57255003, 5047318.94799577, 5047324.78492774,
5047343.34999599, 5047361.17527199, 5047377.0898541,
5047393.47561699, 5047396.94207427, 5047395.32322611,
5047396.42340946, 5047406.92957364, 5047427.66034101,
5047443.0760719, 5047456.36482701, 5047528.38059123,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), dim = c(15L,
3L)), structure(c(299903.127127851, 299903.320414223,
299915.378648026, 299923.777440411, 299930.504446245,
5046960.75487254, 5046961.28337721, 5046988.90350206,
5047001.25750258, 5047003.83116685, 0, 0, 0, 0, 0), dim = c(5L,
3L)), structure(c(299930.504446245, 299949.583123368,
299959.545929842, 299980.866628314, 300003.611260665,
300023.310726376, 300048.444105637, 300077.234279445,
300100.919930205, 300104.481131745, 5047003.83116685,
5046971.68800182, 5046943.00547969, 5046920.72166176,
5046911.97900684, 5046915.78123032, 5046930.48587239,
5046957.6847821, 5046987.79590053, 5046994.35977285,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0), dim = c(10L, 3L)), structure(c(300104.481131745,
300119.138934739, 300134.109164134, 300150.529356202,
300177.155842816, 300212.510325105, 300224.537076035,
300224.346205566, 300237.013058666, 300263.691579113,
300284.285763285, 300390.750589685, 300420.305984624,
5046994.35977285, 5047024.27306488, 5047045.34739571,
5047054.4237419, 5047054.76757684, 5047019.84146242,
5047006.38152575, 5046994.74822331, 5046983.65083931,
5046961.0006817, 5046941.44510252, 5046881.74703166,
5046896.63190805, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0), dim = c(13L, 3L)), structure(c(300457.455775157,
300447.44086401, 300426.293881923, 300391.700426951,
300375.480661379, 300360.17836955, 300348.696267149,
5047223.3217804, 5047233.97932101, 5047244.71822396,
5047259.60178559, 5047274.83602678, 5047297.9309186,
5047307.57255003, 0, 0, 0, 0, 0, 0, 0), dim = c(7L, 3L
)), structure(c(300420.305984624, 300449.031711306, 300489.528913656,
300498.295918156, 300511.803110218, 300514.373762732,
300511.842867013, 300499.106689568, 300493.293484818,
300480.192280261, 300468.535068035, 300460.197952614,
5046896.63190805, 5046898.79319606, 5046912.94366797,
5046931.84221716, 5046948.5561874, 5046974.00273835,
5047006.63268926, 5047116.61272606, 5047149.5168537,
5047192.05949512, 5047212.74350708, 5047221.64446475,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), dim = c(12L, 3L)),
structure(c(300460.197952613, 300457.455775157, 5047221.64446475,
5047223.3217804, 0, 0), dim = 2:3)), class = c("XYZ",
"MULTILINESTRING", "sfg"))), n_empty = 0L, crs = structure(list(
input = "EPSG:32188", wkt = "PROJCRS[\"NAD83 / MTM zone 8\",\n BASEGEOGCRS[\"NAD83\",\n DATUM[\"North American Datum 1983\",\n ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n LENGTHUNIT[\"metre\",1]]],\n PRIMEM[\"Greenwich\",0,\n ANGLEUNIT[\"degree\",0.0174532925199433]],\n ID[\"EPSG\",4269]],\n CONVERSION[\"MTM zone 8\",\n METHOD[\"Transverse Mercator\",\n ID[\"EPSG\",9807]],\n PARAMETER[\"Latitude of natural origin\",0,\n ANGLEUNIT[\"degree\",0.0174532925199433],\n ID[\"EPSG\",8801]],\n PARAMETER[\"Longitude of natural origin\",-73.5,\n ANGLEUNIT[\"degree\",0.0174532925199433],\n ID[\"EPSG\",8802]],\n PARAMETER[\"Scale factor at natural origin\",0.9999,\n SCALEUNIT[\"unity\",1],\n ID[\"EPSG\",8805]],\n PARAMETER[\"False easting\",304800,\n LENGTHUNIT[\"metre\",1],\n ID[\"EPSG\",8806]],\n PARAMETER[\"False northing\",0,\n LENGTHUNIT[\"metre\",1],\n ID[\"EPSG\",8807]]],\n CS[Cartesian,2],\n AXIS[\"easting (E(X))\",east,\n ORDER[1],\n LENGTHUNIT[\"metre\",1]],\n AXIS[\"northing (N(Y))\",north,\n ORDER[2],\n LENGTHUNIT[\"metre\",1]],\n USAGE[\n SCOPE[\"Engineering survey, topographic mapping.\"],\n AREA[\"Canada - Quebec between 75°W and 72°W.; Canada - Ontario - east of 75°W.\"],\n BBOX[44.98,-75,62.53,-72]],\n ID[\"EPSG\",32188]]"), class = "crs"), class = c("sfc_MULTILINESTRING",
"sfc"), precision = 0, bbox = structure(c(xmin = 299474.41195845,
ymin = 5046881.74703166, xmax = 300514.373762732, ymax = 5047608.09567279
), class = "bbox"), z_range = structure(c(zmin = 0, zmax = 0
), class = "z_range"))), row.names = 1L, sf_column = "geom", agr = structure(c(Name = NA_integer_,
description = NA_integer_, timestamp = NA_integer_, begin = NA_integer_,
end = NA_integer_, altitudeMode = NA_integer_, tessellate = NA_integer_,
extrude = NA_integer_, visibility = NA_integer_, drawOrder = NA_integer_,
icon = NA_integer_, sentier = NA_integer_, layer = NA_integer_,
path = NA_integer_), levels = c("constant", "aggregate", "identity"
), class = "factor"), class = c("sf", "data.frame"))
Обновлено:
С этими данными (перерисованными в QGIS) все работает нормально. Однако я не знаю, почему это нельзя сделать легко (перейти от данных выше к данным ниже...)
chemins.simple %>%
filter(Name %in% "sentier_velo") %>%
st_transform(crs = 32188) %>%
st_zm(geom) %>%
st_sample(size = 25, type = "regular") %>%
mapview() +
mapview(chemins.simple)
chemins.simple = structure(list(Name = "sentier_velo", description = NA_character_,
timestamp = structure(NA_real_, tzone = "", class = c("POSIXct",
"POSIXt")), begin = structure(NA_real_, tzone = "", class = c("POSIXct",
"POSIXt")), end = structure(NA_real_, tzone = "", class = c("POSIXct",
"POSIXt")), altitudeMode = NA_character_, tessellate = NA_integer_,
extrude = NA_integer_, visibility = NA_integer_, drawOrder = NA_integer_,
icon = NA_character_, sentier = NA_character_, layer = NA_character_,
path = NA_character_, geom = structure(list(structure(list(
structure(c(299903.127127852, 299915.378648026, 299923.777440411,
299930.504446245, 299949.583123368, 299959.545929842,
299980.866628314, 300003.611260665, 300023.310726376,
300048.444105637, 300077.234279445, 300100.919930205,
300119.138934739, 300134.109164134, 300150.529356202,
300177.155842816, 300224.537076035, 300224.346205566,
300284.285763285, 300390.750589685, 300420.305984624,
300449.031711306, 300489.528913656, 300498.295918157,
300511.803110218, 300514.373762732, 300511.842867013,
300499.106689568, 300493.293484818, 300480.192280261,
300468.535068035, 300460.197952613, 300457.455775157,
300447.44086401, 300426.293881923, 300391.700426951,
300375.480661379, 300360.17836955, 300348.696267149,
300327.322202229, 300298.695288719, 300263.694927326,
300246.383397842, 300232.079546476, 300194.889733763,
300177.248808883, 300154.270060961, 300141.323210388,
300128.611331444, 300115.132109253, 300099.688617737,
300072.709345899, 299903.07988968, 299838.094689635,
299772.477232293, 299750.23396301, 299735.638185202,
299715.208156948, 299702.078535598, 299685.670961566,
299669.997900071, 299657.600720696, 299651.605306308,
299634.64341842, 299624.972126109, 299582.847886435,
299567.18907952, 299533.785023203, 299515.754560643,
299489.877968861, 299474.41195845, 299482.409654204,
299490.968980874, 299497.29763185, 299517.115667715,
299552.432591057, 299572.471975622, 299580.660505604,
299582.021973701, 299588.0164882, 299600.947249195, 299630.602987485,
299665.55385166, 299701.470512757, 299758.996957146,
299802.35340954, 299839.140374969, 299876.649347262,
299907.772848518, 299930.504446245, 5046960.75487254,
5046988.90350206, 5047001.25750258, 5047003.83116685,
5046971.68800182, 5046943.00547969, 5046920.72166176,
5046911.97900684, 5046915.78123032, 5046930.48587239,
5046957.6847821, 5046987.79590053, 5047024.27306488,
5047045.34739571, 5047054.4237419, 5047054.76757684,
5047006.38152575, 5046994.74822331, 5046941.44510252,
5046881.74703166, 5046896.63190805, 5046898.79319606,
5046912.94366797, 5046931.84221716, 5046948.5561874,
5046974.00273835, 5047006.63268926, 5047116.61272606,
5047149.5168537, 5047192.05949512, 5047212.74350708,
5047221.64446475, 5047223.3217804, 5047233.97932101,
5047244.71822396, 5047259.60178558, 5047274.83602677,
5047297.9309186, 5047307.57255003, 5047318.94799577,
5047324.78492774, 5047343.34999599, 5047361.17527199,
5047377.0898541, 5047393.47561699, 5047396.94207427,
5047395.3232261, 5047396.42340945, 5047406.92957363,
5047427.66034101, 5047443.0760719, 5047456.36482701,
5047528.38059123, 5047560.33007056, 5047598.91464364,
5047604.02168543, 5047594.94570617, 5047587.32852712,
5047587.33920063, 5047592.8052447, 5047604.45040378,
5047608.09567278, 5047607.50989447, 5047604.07051358,
5047596.26302026, 5047541.45303887, 5047515.52023998,
5047476.83372278, 5047450.17574194, 5047418.25329369,
5047405.40685391, 5047374.54606886, 5047360.74761632,
5047350.76804681, 5047334.25633307, 5047315.1869463,
5047290.4503043, 5047268.99526155, 5047206.73936411,
5047178.37871541, 5047157.46467836, 5047127.49377699,
5047104.97108792, 5047092.39963862, 5047078.22045236,
5047075.4591553, 5047067.43201061, 5047049.77064173,
5047028.70621312, 5047003.83116685), dim = c(90L, 2L))), class = c("XY",
"MULTILINESTRING", "sfg"))), class = c("sfc_MULTILINESTRING",
"sfc"), precision = 0, bbox = structure(c(xmin = 299474.41195845,
ymin = 5046881.74703166, xmax = 300514.373762732, ymax = 5047608.09567278
), class = "bbox"), crs = structure(list(input = "EPSG:32188",
wkt = "PROJCRS[\"NAD83 / MTM zone 8\",\n BASEGEOGCRS[\"NAD83\",\n DATUM[\"North American Datum 1983\",\n ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n LENGTHUNIT[\"metre\",1]]],\n PRIMEM[\"Greenwich\",0,\n ANGLEUNIT[\"degree\",0.0174532925199433]],\n ID[\"EPSG\",4269]],\n CONVERSION[\"MTM zone 8\",\n METHOD[\"Transverse Mercator\",\n ID[\"EPSG\",9807]],\n PARAMETER[\"Latitude of natural origin\",0,\n ANGLEUNIT[\"degree\",0.0174532925199433],\n ID[\"EPSG\",8801]],\n PARAMETER[\"Longitude of natural origin\",-73.5,\n ANGLEUNIT[\"degree\",0.0174532925199433],\n ID[\"EPSG\",8802]],\n PARAMETER[\"Scale factor at natural origin\",0.9999,\n SCALEUNIT[\"unity\",1],\n ID[\"EPSG\",8805]],\n PARAMETER[\"False easting\",304800,\n LENGTHUNIT[\"metre\",1],\n ID[\"EPSG\",8806]],\n PARAMETER[\"False northing\",0,\n LENGTHUNIT[\"metre\",1],\n ID[\"EPSG\",8807]]],\n CS[Cartesian,2],\n AXIS[\"easting (E(X))\",east,\n ORDER[1],\n LENGTHUNIT[\"metre\",1]],\n AXIS[\"northing (N(Y))\",north,\n ORDER[2],\n LENGTHUNIT[\"metre\",1]],\n USAGE[\n SCOPE[\"Engineering survey, topographic mapping.\"],\n AREA[\"Canada - Quebec between 75°W and 72°W.; Canada - Ontario - east of 75°W.\"],\n BBOX[44.98,-75,62.53,-72]],\n ID[\"EPSG\",32188]]"), class = "crs"), n_empty = 0L)), row.names = 1L, sf_column = "geom", agr = structure(c(Name = NA_integer_,
description = NA_integer_, timestamp = NA_integer_, begin = NA_integer_,
end = NA_integer_, altitudeMode = NA_integer_, tessellate = NA_integer_,
extrude = NA_integer_, visibility = NA_integer_, drawOrder = NA_integer_,
icon = NA_integer_, sentier = NA_integer_, layer = NA_integer_,
path = NA_integer_), levels = c("constant", "aggregate", "identity"
), class = "factor"), class = c("sf", "data.frame"))
Выдает ошибку: Error in st_line_sample(., density = 2, type = "regular") : inherits(x, "sfc_LINESTRING") is not TRUE
Я имел в виду скорее принципиально. Подойдет ли вам использование плотности точек вместо абсолютного количества? Хотя с моей стороны выборка работает нормально, если я только заменю n
на density
в вашем репрексе, chemins.simple %>% st_zm(geom) %>% st_cast('LINESTRING') %>% st_line_sample(density = .02, type = "regular")
Я бы предпочел абсолютную сумму. Почему я не могу сделать это одной строкой? Это облегчит задачу!
Превратить ее в одну линию, подходящую для выборки (т. е. без перекрытий и пересечений), немного сложно, поскольку вам нужно будет выяснить и исправить порядок сегментов, а также направление сегментов. В противном случае вы получите что-то вроде этого: - i.sstatic.net/dvJtT.png (строка строк — это просто последовательность точек, и порядок точек имеет значение). Было бы мало коротких путей, если бы это был просто круговой путь. Или линейный путь без развилок. Но сейчас это похоже на проблему с графом, что-то для igraph/tidygraph/sfnetworks.
ХОРОШО! Это изображение — именно то, что я получал, когда менял геометрию и пытался получить одну линию. Я думал, что будет проще, но оказалось, что это более сложная проблема.
Следующий подход, вероятно, нелегко обобщить для других форм, но он работает с этим конкретным репрексом.
Координаты проекции сначала округляются (до метра), чтобы конечные точки линий оказались в одинаковых узлах в графовой сети, затем строки преобразуются в объект sfnetworks
(igraph
/ tidygraph
+ пространственный). Отсюда мы можем получить правильную последовательность сегментов по эйлерову пути (пройти через каждое ребро ровно один раз), преобразовать ребра обратно в объект sf
и подмножество сегментов линий с индексами ребер из пути.
Еще есть st_line_merge()
, и как только координаты округляются, он неплохо справляется со своей задачей, но не может справиться с этим трехсторонним соединением и возвращает 2 строки, цикл + этот короткий участок.
library(sf)
#> Linking to GEOS 3.11.2, GDAL 3.6.2, PROJ 9.2.0; sf_use_s2() is TRUE
library(sfnetworks)
library(tidygraph, warn.conflicts = FALSE)
library(dplyr, warn.conflicts = FALSE)
library(mapview)
line_sfn <-
chemins.simple %>%
st_zm(geom) %>%
st_geometry() %>%
st_cast("LINESTRING") %>%
# round coordinates so line endpoints would end up in same graph nodes
lapply(function(x) round(x, 0)) %>%
# back to sfc
st_sfc(crs = st_crs(chemins.simple)) %>%
# directed graph
as_sfnetwork(directed = TRUE)
line_sfn
#> # A sfnetwork with 13 nodes and 13 edges
#> #
#> # CRS: EPSG:32188
#> #
#> # A directed simple graph with 1 component with spatially explicit edges
#> #
#> # A tibble: 13 × 1
#> x
#> <POINT [m]>
#> 1 (299583 5047541)
#> 2 (299491 5047361)
#> 3 (299552 5047315)
#> 4 (299582 5047207)
#> 5 (299931 5047004)
#> 6 (299652 5047608)
#> # ℹ 7 more rows
#> #
#> # A tibble: 13 × 3
#> from to x
#> <int> <int> <LINESTRING [m]>
#> 1 1 2 (299583 5047541, 299567 5047516, 299534 5047477, 299516 5047450, …
#> 2 2 3 (299491 5047361, 299497 5047351, 299517 5047334, 299552 5047315)
#> 3 3 4 (299552 5047315, 299572 5047290, 299581 5047269, 299582 5047207)
#> # ℹ 10 more rows
plot(line_sfn)
as.igraph(line_sfn) %>% plot()
eulerian_path <-
line_sfn %>%
# edges back to regular sf
st_as_sf(active = "edges") %>%
# find Eulerian path (pass through every edge exactly once),
# use edge indices to subset edges in sf object in correct order
slice(igraph::eulerian_path(line_sfn)$epath %>% as.vector()) %>%
# lines to multipoints
st_cast("MULTIPOINT") %>%
# to a single multipoint feature, now correctly ordered
summarise(do_union = FALSE) %>%
# points to linestring
st_cast("LINESTRING")
eulerian_path
#> Simple feature collection with 1 feature and 0 fields
#> Geometry type: LINESTRING
#> Dimension: XY
#> Bounding box: xmin: 299474 ymin: 5046882 xmax: 300514 ymax: 5047608
#> Projected CRS: NAD83 / MTM zone 8
#> # A tibble: 1 × 1
#> x
#> <LINESTRING [m]>
#> 1 (299903 5046961, 299903 5046961, 299915 5046989, 299924 5047001, 299931 50470…
pt_samples <- st_line_sample(eulerian_path, n = 20)
mapview(eulerian_path) + mapview(pt_samples)
sf
sf::st_line_merge()
делает немного больше, чем я ожидал, по крайней мере, с помощью этого репрекса мы можем получить действительный результат при приведении st_line_merge()
MULTILINESTRING
результата к POINT
s, а затем к LINESTRING
:
merged_ <-
chemins.simple %>%
st_zm(geom) %>%
# 1 multiline
st_geometry() %>%
# to 13 line features
st_cast("LINESTRING") %>%
lapply(function(x) round(x, 0)) %>%
st_sfc(crs = st_crs(chemins.simple)) %>%
# to 1 multiline with 13 lines
st_union() %>%
# to 1 multiline with 2 lines
st_line_merge() %>%
# to 96 point features
st_cast("POINT") %>%
# to 1 multipoint
st_combine() %>%
st_cast("LINESTRING")
merged_
#> Geometry set for 1 feature
#> Geometry type: LINESTRING
#> Dimension: XY
#> Bounding box: xmin: 299474 ymin: 5046882 xmax: 300514 ymax: 5047608
#> Projected CRS: NAD83 / MTM zone 8
#> LINESTRING (299903 5046961, 299915 5046989, 299...
plot(merged_)
Created on 2024-04-25 with reprex v2.1.0
Было бы приемлемым обходным решением, если бы вы сохранили многострочный код, но использовали
st_line_sample()
сdensity
вместоn
?