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

Get osmconf.ini file in synch with GDAL #304

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@ name: R-CMD-check
jobs:
R-CMD-check:
if: "!(contains(github.event.head_commit.message, 'skip ci')||contains(github.event.head_commit.message, 'ci skip')||contains(github.event.head_commit.message, 'skip-ci')||contains(github.event.head_commit.message, 'ci-skip')||contains(github.event.head_commit.message, 'skip_ci')||contains(github.event.head_commit.message, 'ci_skip'))"
runs-on: ubuntu-latest

runs-on: ${{ matrix.config.os }}

timeout-minutes: 30

strategy:
fail-fast: false
matrix:
R: ['3.6.3', 'release']
config:
- {os: macos-latest, r: 'release'}
- {os: ubuntu-latest, r: 'release'}
- {os: ubuntu-latest, r: '3.6.3'}

env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
R_KEEP_PKG_SOURCE: yes
Expand All @@ -29,7 +37,7 @@ jobs:
uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
r-version: ${{ matrix.R }}
r-version: ${{ matrix.config.r }}

- name: Setup R Dependencies
uses: r-lib/actions/setup-r-dependencies@v2
Expand Down
2 changes: 1 addition & 1 deletion R/get-key-values.R
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ oe_get_keys.character = function(
# file. For example
# system.file("its-example.osm.pbf", package = "osmextract") |>
# sf::st_read(quiet = TRUE, query = "SELECT * FROM lines LIMIT 0")
get_fields_default(layer)
get_fields_default(layer, file = readLines(get_default_osmconf_ini()))
)

if (any(existing_fields %!in% default_fields)) {
Expand Down
161 changes: 94 additions & 67 deletions R/vectortranslate.R
Original file line number Diff line number Diff line change
Expand Up @@ -334,27 +334,21 @@ oe_vectortranslate = function(
extra_tags = NULL
}

# First we need to set the values for the parameter osmconf_ini (if it is set
# to NULL, i.e. the default).
if (is.null(osmconf_ini)) {
# The file osmconf.ini stored in the package is the default osmconf.ini used
# by GDAL at stored at the following link:
# https://github.com/OSGeo/gdal/blob/master/data/osmconf.ini
# It was saved on the 9th of July 2020.
osmconf_ini = system.file("osmconf.ini", package = "osmextract")
osmconf_ini = get_default_osmconf_ini()
}

# Add the extra tags to the default osmconf.ini. If the user set its own
# osmconf.ini file we need to skip this step.
if (
!is.null(extra_tags) &&
# The following condition checks whether the user set its own CONFIG file
osmconf_ini == system.file("osmconf.ini", package = "osmextract")
osmconf_ini == get_default_osmconf_ini()
) {
temp_ini = readLines(osmconf_ini)
id_old = get_id_layer(layer)
fields_old = get_fields_default(layer)
temp_ini[[id_old]] = paste0(
id_layer = get_id_layer(layer, temp_ini)
fields_old = get_fields_default(layer, temp_ini)
temp_ini[[id_layer]] = paste0(
"attributes=",
paste(unique(c(fields_old, extra_tags)), collapse = ",")
)
Expand Down Expand Up @@ -470,65 +464,68 @@ oe_vectortranslate = function(
gpkg_file_path
}

get_id_layer = function(layer) {
default_id = list(
points = 38L,
lines = 58L,
multipolygons = 90L,
multilinestrings = 108L,
other_relations = 126L
get_id_layer = function(layer, file) {
# Detect the ID of the row which specifies the attributes that must be
# included in the ogr2ogr conversion from .osm to .gpkg file for a given
# layer. The following pattern uses a simple heuristic which is based on the
# current (2024-11-12) structure of the osmconf.ini file, i.e. the attributes
# are specified in a single row which starts with "attributes=". The available
# layers are described below in that precise order. I need to make this ID
# detection automatic for #261 so I do not need to link the ogr2ogr operations
# to a fixed osmconf.ini file.
id_attributes <- grepl(
pattern = "^attributes=",
x = file,
perl = TRUE
)
id_attributes <- which(id_attributes)
stopifnot(length(id_attributes) == 5L)
stopifnot(layer %in% c("points", "lines", "multipolygons", "multilinestrings", "other_relations"))
switch(
layer,
"points" = id_attributes[1L],
"lines" = id_attributes[2L],
"multipolygons" = id_attributes[3L],
"multilinestrings" = id_attributes[4L],
"other_relations" = id_attributes[5L]
)
default_id[[layer]]
}
get_fields_default = function(layer) {
def_layers = list(
points = c(
"name",
"barrier",
"highway",
"ref",
"address",
"is_in",
"place",
"man_made"
),
lines = c(
"name",
"highway",
"waterway",
"aerialway",
"barrier",
"man_made",
"railway"
),
multipolygons = c(
"name",
"type",
"aeroway",
"amenity",
"admin_level",
"barrier",
"boundary",
"building",
"craft",
"geological",
"historic",
"land_area",
"landuse",
"leisure",
"man_made",
"military",
"natural",
"office",
"place",
"shop",
"sport",
"tourism"
),
multilinestrings = c("name", "type"),
other_relations = c("name", "type")
get_fields_default = function(layer, file) {
# The following code is used to extract the default keys which must be
# included in the ogr2ogr conversion from .osm to .gpkg format for a given
# layer. The following pattern uses a simple heuristic which is based on the
# current (2024-11-12) structure of the osmconf.ini file, i.e. the attributes
# are specified in a single row which starts with "attributes=". This
# automatic detection is required to implement #261.

# I cannot simply use grep(value = TRUE) since that matches the whole row, not
# only the part which I'm interested in. I need regexpr + regmatch
m = regexpr(
pattern = "(?<=^attributes=)\\S*",
text = file,
perl = TRUE
)
keys <- regmatches(x = file, m = m)
# The output of regmatches is a (character vector) which includes the matched
# substrings. It has a syntax like
# [1] a,b,c,d
# [2] a,d,e,f
# [3] b,f,g
# ...
# I need to split such sequence of keys using "," as a delimiter.
keys <- strsplit(keys, ",")
# I assume there are 5 layers specified according to the following order:
stopifnot(length(keys) == 5L)
stopifnot(layer %in% c("points", "lines", "multipolygons", "multilinestrings", "other_relations"))
# The output of strsplit is a list so I need [[i]] syntax.
switch(
layer,
"points" = keys[[1L]],
"lines" = keys[[2L]],
"multipolygons" = keys[[3L]],
"multilinestrings" = keys[[4L]],
"other_relations" = keys[[5L]]
)
def_layers[[layer]]
}

process_boundary = function(
Expand Down Expand Up @@ -598,3 +595,33 @@ process_spat = function(vectortranslate_options, boundary) {
process_clipsrc = function(vectortranslate_options, boundary) {
c(vectortranslate_options, "-clipsrc", sf::st_as_text(boundary))
}

# Get default osmconf.ini
get_default_osmconf_ini <- function() {
# I guess we have 3 options to retrieve the osmconf.ini file used by GDAL
# 1. Check the output of gdal-config --datadir (if possible)
# 2. Get the file bundled by sf (especially when using binary install of sf)
# 3. Fallback: osmconf.ini file shipped by this package

# Option 1
file <- try({
system2("gdal-config", args = "--datadir", stdout = TRUE)
},
silent = TRUE
)
if (!inherits(file, "try-error")) {
return(file.path(file, "osmconf.ini"))
}
# Option 2
file <- system.file("gdal/osmconf.ini", package = "sf")
if (!file.exists(file)) {
# Option 3
warning(
"The package couldn't retrive the osmconf.ini from GDAL installation. ",
"Defaulting to the one bundled in this package. ",
"Please raise a new issue at https://github.com/ropensci/osmextract/issues"
)
file <- system.file("osmconf.ini", package = "osmextract")
}
file
}