1

Looking at OpenStreeMap underlying data I see that the data that I want is at the relation level and when I query the OSM using osmdata in R I cannot get this data. https://www.openstreetmap.org/relation/15019644

The code that I have is as follows:

 bbx <- getbb("Ireland")
 y <- bbx %>%
 opq(timeout = 25*100,osm_types = 'relation')%>%
 add_osm_feature("plant:source","wind") %>%
 osmdata_sf() 

I can get the osm_point data, but not the osm_relation data that aggregates, and joins the points.

asked Jul 17, 2024 at 10:02

1 Answer 1

1

osmdata_sf seems to transform only polygon and line relations, but you can get point relation data from xml response, transform it to data.frames / tibbles and, if needed, join to sf point object:

library(osmdata)
library(sf)
library(xml2)
library(dplyr)
library(tidyr)
library(purrr)
# Overpass response as XML
wind_xml <- 
 getbb("Ireland") |> 
 opq(timeout = 25*100,osm_types = "relation") |> 
 add_osm_feature("plant:source","wind") |> 
 osmdata_xml() 
# XML response to "regular" osmdata sf object 
wind_sf <- osmdata_sf(doc = wind_xml)
# find all relations with `<member type="node" ... >` nodes,
# use realtion id as list names,
# from every relation find all node members, extract attributes (type, ref, role),
# turn named lists into frames with bind_rows,
# bind resulting frames, use list names for `relation` column
relation_nodes <- 
 xml_find_all(wind_xml, "//relation[member[@type='node']]") %>% 
 set_names(map(.,xml_attr, "id")) |> 
 map(\(x) xml_find_all(x, "./member[@type='node']") |> xml_attrs()) |> 
 map(bind_rows) |> 
 list_rbind(names_to = "relation")
relation_nodes
#> # A tibble: 2,578 ×ばつ 4
#> relation type ref role 
#> <chr> <chr> <chr> <chr> 
#> 1 6949501 node 2616376676 generator
#> 2 6949501 node 2616376686 generator
#> 3 6949501 node 2616376653 generator
#> 4 6949501 node 2616376627 generator
#> 5 6949501 node 2616376638 generator
#> 6 6949501 node 2616376608 generator
#> 7 6949501 node 2616376612 generator
#> 8 6949501 node 2616376595 generator
#> 9 6949501 node 2616376588 generator
#> 10 6949501 node 2616376560 generator
#> # i 2,568 more rows
# similar aporach as before, but now extract all tag elements,
# pivot wider, tag keys to columns
relation_tags <- 
 xml_find_all(wind_xml, "//relation[member[@type='node']]") %>% 
 set_names(map(.,xml_attr, "id")) |> 
 map(\(x) xml_find_all(x, "./tag") |> xml_attrs()) |> 
 map(bind_rows) |> 
 list_rbind(names_to = "relation") |> 
 pivot_wider(names_from = k, values_from = v) 
relation_tags
#> # A tibble: 239 ×ばつ 37
#> relation name operator plant:output:electri...1 `plant:source` power `repd:id`
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> 
#> 1 6949501 Tangy SSE 18.7 MW wind plant 3467 
#> 2 8384695 Slie... Platina 54 MW wind plant <NA> 
#> 3 8555637 Coom... <NA> 8.5 MW wind plant <NA> 
#> 4 9195703 Bool... Brookfi... 30 MW wind plant <NA> 
#> 5 9196204 Ball... Greenco... 48.3 MW wind plant <NA> 
#> 6 9285446 Gruig <NA> 25 MW wind plant 3241 
#> 7 9285684 Creg... Gaelect... 13.8 MW wind plant 4653 
#> 8 9296897 Loug... B9 / Re... 10.5 MW wind plant 4680 
#> 9 9296910 Bin ... <NA> 9 MW wind plant 3113 
#> 10 9298972 Alta... <NA> 26 MW wind plant 3575 
#> # i 229 more rows
#> # i abbreviated name: 1​`plant:output:electricity`
#> # i 30 more variables: site <chr>, type <chr>, `plant:method` <chr>,
#> # wikidata <chr>, wikipedia <chr>, alt_name <chr>, fixme <chr>, owner <chr>,
#> # start_date <chr>, `operator:wikidata` <chr>, `operator:wikipedia` <chr>,
#> # website <chr>, turbines <chr>, note <chr>, `alt_name:en` <chr>,
#> # `name:en` <chr>, `name:ga` <chr>, old_operator <chr>, old_website <chr>, ...

Form here you can for example join relation_nodes to osm_points object and create MULTIPOINTs of relations:

wind_multipoint_sf <- 
 wind_sf$osm_points |> 
 inner_join(relation_nodes, by = join_by(osm_id == ref)) |> 
 group_by(relation) |> 
 summarise()
wind_multipoint_sf
#> Simple feature collection with 239 features and 1 field
#> Geometry type: GEOMETRY
#> Dimension: XY
#> Bounding box: xmin: -10.10166 ymin: 51.62229 xmax: -5.656859 ymax: 55.5659
#> Geodetic CRS: WGS 84
#> # A tibble: 239 ×ばつ 2
#> relation geometry
#> <chr> <GEOMETRY [°]>
#> 1 10102897 POINT (-6.186668 54.93249)
#> 2 10664357 MULTIPOINT ((-8.635368 53.12872), (-8.63826 53.12859), (-8.634468 5...
#> 3 11113896 MULTIPOINT ((-6.365975 52.17676), (-6.363355 52.17386), (-6.360543 ...
#> 4 11113897 MULTIPOINT ((-8.226164 54.13107), (-8.228405 54.13296), (-8.231114 ...
#> 5 11113898 MULTIPOINT ((-7.79886 52.52894), (-7.795786 52.5316), (-7.771544 52...
#> 6 11198793 MULTIPOINT ((-7.303677 54.92539), (-7.310983 54.92871), (-7.3077 54...
#> 7 11198794 MULTIPOINT ((-7.26855 54.91813), (-7.25883 54.91843), (-7.258841 54...
#> 8 11198795 MULTIPOINT ((-6.744441 54.94007), (-6.748539 54.93631), (-6.745422 ...
#> 9 12864417 MULTIPOINT ((-6.257705 55.03091), (-6.256246 55.02987), (-6.254009 ...
#> 10 12864418 MULTIPOINT ((-6.44266 55.0026), (-6.450407 55.0003), (-6.450604 54....
#> # i 229 more rows

Or perhaps just join both relation_nodes & relation_tags frames to points sf object:


wind_joined_sf <- 
 wind_sf$osm_points |> 
 as_tibble() |> 
 st_as_sf() |> 
 inner_join(relation_nodes, by = join_by(osm_id == ref)) |> 
 relocate(relation:role, .after = 1) |> 
 left_join(relation_tags, by = "relation") |> 
 janitor::remove_empty()
#> value for "which" not specified, defaulting to c("rows", "cols")
wind_joined_sf
#> Simple feature collection with 2578 features and 88 fields
#> Geometry type: POINT
#> Dimension: XY
#> Bounding box: xmin: -10.10166 ymin: 51.62229 xmax: -5.656859 ymax: 55.5659
#> Geodetic CRS: WGS 84
#> # A tibble: 2,578 ×ばつ 89
#> osm_id relation type.x role name.x base_height check_date colour
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> 
#> 1 370577265 15261309 node generator <NA> <NA> <NA> <NA> 
#> 2 370577267 15261309 node generator <NA> <NA> <NA> <NA> 
#> 3 410873377 14995512 node generator <NA> <NA> <NA> <NA> 
#> 4 410873379 14995512 node generator <NA> <NA> <NA> <NA> 
#> 5 410873382 14995512 node generator <NA> <NA> <NA> <NA> 
#> 6 410873383 14995512 node generator <NA> <NA> <NA> <NA> 
#> 7 432678296 14175147 node generator <NA> <NA> <NA> <NA> 
#> 8 885438777 14170264 node generator <NA> <NA> <NA> <NA> 
#> 9 885438788 14170264 node generator <NA> <NA> <NA> <NA> 
#> 10 885438789 14170264 node generator <NA> <NA> <NA> <NA> 
#> # i 2,568 more rows
#> # i 81 more variables: construction <chr>,
#> # `construction:generator:method` <chr>,
#> # `construction:generator:output:electricity` <chr>,
#> # `construction:generator:source` <chr>, `construction:generator:type` <chr>,
#> # `construction:power.x` <chr>, designation <chr>, `disused:power.x` <chr>,
#> # fixme.x <chr>, `generator:height` <chr>, `generator:manufacturer` <chr>, ...
glimpse(wind_joined_sf)
#> Rows: 2,578
#> Columns: 89
#> $ osm_id <chr> "370577265", "370577267", ...
#> $ relation <chr> "15261309", "15261309", "1...
#> $ type.x <chr> "node", "node", "node", "n...
#> $ role <chr> "generator", "generator", ...
#> $ name.x <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ base_height <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ check_date <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ colour <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ construction <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `construction:generator:method` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `construction:generator:output:electricity` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `construction:generator:source` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `construction:generator:type` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `construction:power.x` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ designation <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `disused:power.x` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ fixme.x <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `generator:height` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `generator:manufacturer` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `generator:method` <chr> "wind_turbine", "wind_turb...
#> $ `generator:output:electricity` <chr> "0.85 MW", "0.85 MW", "1.3...
#> $ `generator:source` <chr> "wind", "wind", "wind", "w...
#> $ `generator:type` <chr> "horizontal_axis", "horizo...
#> $ has_aeronautical_lights <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ height <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `height:hub` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `height:range` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ man_made <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ manufacturer.x <chr> "Vestas", "Vestas", "Norde...
#> $ `manufacturer:type` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `manufacturer:wikidata` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ model <chr> "V52", "V52", "N60", "N60"...
#> $ `monitoring:weather` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ note.x <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ old_operator.x <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ old_website.x <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ operator.x <chr> "Tornado Electrical Ltd", ...
#> $ `operator:website.x` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `operator:wikidata.x` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `operator:wikipedia.x` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ owner.x <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ power.x <chr> "generator", "generator", ...
#> $ ref <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `repd:id.x` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `rotor:diameter` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `rotor:swept_area` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ source.x <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `source:position` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ start_date.x <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `tower:construction` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `tower:type` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ year_of_construction.x <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ geometry <POINT [°]> POINT (-7.344967 52....
#> $ name.y <chr> "Beallough Wind Farm", "Be...
#> $ operator.y <chr> "Tornado Electrical Ltd", ...
#> $ `plant:output:electricity` <chr> "1.7 MW", "1.7 MW", "7.7 M...
#> $ `plant:source` <chr> "wind", "wind", "wind", "w...
#> $ power.y <chr> "plant", "plant", "plant",...
#> $ `repd:id.y` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ site <chr> "wind_farm", "wind_farm", ...
#> $ type.y <chr> "site", "site", "site", "s...
#> $ `plant:method` <chr> "wind_turbine", "wind_turb...
#> $ wikidata <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ wikipedia <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ alt_name <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ fixme.y <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ owner.y <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ start_date.y <chr> "2008", "2008", NA, NA, NA...
#> $ `operator:wikidata.y` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `operator:wikipedia.y` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ website <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ turbines <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ note.y <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `alt_name:en` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `name:en` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `name:ga` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ old_operator.y <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ old_website.y <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ year_of_construction.y <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ source.y <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `disused:power.y` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ end_date <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `construction:power.y` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `operator:website.y` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ phone <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ `contact:facebook` <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ old_name <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ description <chr> NA, NA, NA, NA, NA, NA, NA...
#> $ manufacturer.y <chr> NA, NA, NA, NA, NA, NA, NA...

Created on 2024年07月17日 with reprex v2.1.0

answered Jul 17, 2024 at 18:14
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for this brilliant response- it is outstanding! Thanks so much!!!!

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.