Skip to content

Commit a538316

Browse files
authored
Merge pull request #275 from R-ArcGIS/arc-open-refactor
rewrite arc_open to support item id's or portal urls
2 parents 684c35a + 24bf44a commit a538316

16 files changed

+230
-91
lines changed

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ License: Apache License (>= 2)
2222
Encoding: UTF-8
2323
LazyData: true
2424
Imports:
25-
arcgisutils (>= 0.2.0),
25+
arcgisutils (>= 0.3.3.9000),
2626
arcpbf (>= 0.1.5),
2727
cli,
2828
httr2 (>= 1.0.5),
@@ -34,7 +34,7 @@ Imports:
3434
terra,
3535
utils
3636
Depends:
37-
R (>= 4.1.0)
37+
R (>= 4.2.0)
3838
Roxygen: list(markdown = TRUE)
3939
RoxygenNote: 7.3.2
4040
Suggests:

NEWS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
- `update_features()` is now parallelized and sends updates in chunks see `chunk_size` argument and `progress` arguments. The return type is now a `data.frame` and not a list with `updateResults`
66
- `delete_features()` is now parallelized and deletes in chunks. See above.
7-
- `{arcgislayers}` now depends on R 4.1 or higher.
7+
- `{arcgislayers}` now depends on R 4.2 or higher.
88

99
## New features
1010

11+
- `arc_open()` now works with item IDs or a variety of URLs such as item, user, group, and more <https://github.com/R-ArcGIS/arcgislayers/pull/275>
1112
- `add_definition()` (#178), `update_definition()` (#127), and `delete_definition()` functions for FeatureServer and FeatureLayer objects.
1213

1314
## Bug Fixes

R/arc-open.R

Lines changed: 110 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
#' `r lifecycle::badge("experimental")`
1010
#'
1111
#' @param url The url of the remote resource. Must be of length one.
12-
#' @param token your authorization token.
12+
#' @inheritParams arcgisutils::arc_item
1313
#'
1414
#' @seealso arc_select arc_raster
1515
#' @export
1616
#' @returns
17-
#' Depending on the provided URL returns a `FeatureLayer`, `Table`, `FeatureServer`, `ImageServer`, or `MapServer`. Each of these objects is a named list containing the properties of the service.
17+
#' Depending on item ID or URL returns a `PortalItem`, `FeatureLayer`, `Table`, `FeatureServer`, `ImageServer`, or `MapServer`, `GeocodeServer`, among other. Each of these objects is a named list containing the properties of the service.
1818
#' @examples
1919
#' \dontrun{
20+
#'
21+
#' # FeatureServer ID
22+
#' arc_open("3b7221d4e47740cab9235b839fa55cd7")
23+
#'
2024
#' # FeatureLayer
2125
#' furl <- paste0(
2226
#' "https://services3.arcgis.com/ZvidGQkLaDJxRSJ2/arcgis/rest/services/",
@@ -54,64 +58,124 @@
5458
#'
5559
#' arc_open(map_url)
5660
#' }
57-
arc_open <- function(url, token = arc_token()) {
58-
check_url(url)
61+
arc_open <- function(url, host = arc_host(), token = arc_token()) {
62+
check_string(url, allow_empty = FALSE)
5963

60-
# parse url query and strip from url if query matches default
61-
query <- parse_url_query(url) %||% list()
62-
url <- clear_url_query(url)
63-
64-
# extract layer metadata
65-
meta <- fetch_layer_metadata(url, token)
64+
if (!is_url(url)) {
65+
e_msg <- "Expected an item ID or url to a portal item."
6666

67-
# set url for later use
68-
meta[["url"]] <- url
67+
item <- rlang::try_fetch(
68+
arc_item(url, host, token),
69+
error = function(cnd) cli::cli_abort(e_msg, call = rlang::caller_call(2))
70+
)
6971

70-
# layer class
71-
layer_class <- gsub("\\s", "", meta[["type"]])
72+
if (is.null(item$url)) {
73+
# return a portal item if the url is null
74+
return(item)
75+
}
7276

73-
# if it's missing it means it's a server type. Need to deduce.
74-
if (length(layer_class) == 0) {
75-
if (any(grepl("pixel|band|raster", names(meta)))) {
76-
layer_class <- "ImageServer"
77-
} else if (grepl("MapServer", meta[["url"]])) {
78-
layer_class <- "MapServer"
79-
} else if (
80-
"layers" %in% names(meta) || grepl("FeatureServer", meta[["url"]])
81-
) {
82-
layer_class <- "FeatureServer"
83-
} else {
84-
return(meta)
77+
# return the portal item if the url type is null
78+
if (is.null(arc_url_type(item$url))) {
79+
return(item)
8580
}
81+
82+
# otherwise we fetch the url from the new item
83+
url <- item$url
8684
}
8785

88-
# construct the appropriate class based on the resultant `layer_class`
89-
res <- switch(
90-
layer_class,
91-
"FeatureLayer" = structure(
92-
meta,
93-
class = layer_class,
94-
query = query
95-
),
96-
"Table" = structure(
97-
meta,
98-
class = layer_class,
99-
query = query
86+
# parse the provided url
87+
info <- arc_url_parse(url)
88+
89+
if (is.null(info$type)) {
90+
cli::cli_abort(
91+
c(
92+
"!" = "Unable to open the provided url or item ID.",
93+
"i" = "If you think this an error, please create an issue:",
94+
"{.url https://github.com/r-arcgis/arcgislayers/issues/new}"
95+
)
96+
)
97+
}
98+
99+
switch(
100+
info$type,
101+
"FeatureServer" = {
102+
as_layer_class(clear_url_query(url), token, info$type)
103+
},
104+
"MapServer" = as_layer_class(
105+
clear_url_query(url),
106+
token,
107+
info$type
100108
),
101-
"FeatureServer" = structure(
102-
meta,
103-
class = layer_class
109+
"ImageServer" = as_layer_class(
110+
clear_url_query(url),
111+
token,
112+
info$type
104113
),
105-
"ImageServer" = structure(meta, class = layer_class),
106-
"MapServer" = structure(meta, class = layer_class),
107-
"GroupLayer" = structure(meta, class = layer_class),
114+
"SceneServer" = as_layer_class(url, token, info$type),
115+
"GeocodeServer" = as_layer_class(url, token, info$type),
116+
# FIXME, unclear how to use this...
117+
"GeometryServer" = as_layer_class(url, token, info$type),
118+
# FIXME, unclear how to use this...
119+
"GPServer" = as_layer_class(url, token, info$type),
120+
"item" = {
121+
# if we have an item url, we fetch the item
122+
item <- arc_item(info$query$id, host = host, token = token)
123+
124+
# if there is no associated url we return the item
125+
if (is.null(item[["url"]])) {
126+
return(item)
127+
}
128+
129+
url_type <- arc_url_type(item[["url"]])
130+
131+
if (is.null(url_type)) {
132+
return(item)
133+
}
134+
135+
# if there is a URL we're going to recurse
136+
arc_open(item[["url"]], host = host, token = token)
137+
},
138+
"user" = arc_user(info$query$user, host = host, token = token),
139+
"group" = arc_group(info$query$id, host = host, token = token),
140+
"webscene" = arc_item(info$query$webscene, host = host, token = token),
141+
"app" = arc_item(info$query$appid, host = host, token = token),
142+
"notebook" = arc_item(info$query$id, host = host, token = token),
143+
"experience" = {
144+
path_components <- strsplit(info$path, "/")[[1]]
145+
exp_id <- path_components[which(path_components == "experience") + 1]
146+
arc_item(exp_id, host = host, token = token)
147+
},
148+
"storymap" = {
149+
path_components <- strsplit(info$path, "/")[[1]]
150+
sm_id <- path_components[which(path_components == "stories") + 1]
151+
arc_item(sm_id, host = host, token = token)
152+
},
153+
"dashboard" = {
154+
path_components <- strsplit(info$path, "/")[[1]]
155+
db_id <- path_components[which(path_components == "dashboards") + 1]
156+
arc_item(db_id, host = host, token = token)
157+
},
158+
"datapipeline" = arc_item(info$query$item, host = host, token = token),
159+
"webapp" = arc_item(info$query$id, host = host, token = token),
108160
cli::cli_abort(
109161
c(
110-
"Service type {.val {layer_class}} is not supported.",
162+
"Service type {.val {info$type}} is not supported at this time.",
111163
"i" = "Please report this at {.url https://github.com/R-ArcGIS/arcgislayers/issues}"
112164
)
113165
)
114166
)
167+
}
168+
169+
#' Fetch metadata and an appropriate class
170+
#' @noRd
171+
as_layer_class <- function(url, token, class = NULL) {
172+
meta <- fetch_layer_metadata(url, token)
173+
meta[["url"]] <- url
174+
cls <- if (is.null(meta[["type"]])) {
175+
NULL
176+
} else {
177+
gsub("\\s+", "", meta[["type"]])
178+
}
115179

116-
res
180+
structure(meta, class = c(cls %||% class, "list"))
117181
}

R/print-methods.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ print.FeatureServer <- function(x, n, ...) {
121121

122122
header <- sprintf(
123123
"<%s <%s, %s>>",
124-
class(x),
124+
class(x)[1],
125125
fts_lbl,
126126
tbls_lbl
127127
)
@@ -197,7 +197,7 @@ print.MapServer <- function(x, ...) print.FeatureServer(x, ...)
197197
print.ImageServer <- function(x, ...) {
198198
header <- sprintf(
199199
"<%s <%i bands, %i fields>>",
200-
class(x),
200+
class(x)[1],
201201
x$bandCount,
202202
length(x$fields$name) %||% 0
203203
)

R/utils.R

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -134,24 +134,6 @@ coalesce_crs <- function(x, y) {
134134
}
135135
}
136136

137-
#' Does x match the pattern of a URL?
138-
#' @noRd
139-
is_url <- function(x, pattern = NULL, ...) {
140-
if (
141-
!rlang::is_vector(x) || rlang::is_empty(x) || !rlang::is_scalar_character(x)
142-
) {
143-
return(FALSE)
144-
}
145-
146-
url_pattern <-
147-
"http[s]?://(?:[[:alnum:]]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+"
148-
149-
if (is.null(pattern)) {
150-
return(grepl(url_pattern, x, ...))
151-
}
152-
153-
grepl(url_pattern, x, ...) & grepl(pattern, x, ...)
154-
}
155137

156138
#' Check if x is a valid URL
157139
#' @noRd

dev/downloads.R

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ library(dplyr)
22
library(ggplot2)
33

44
downloads <- cranlogs::cran_downloads(
5-
c("arcgislayers", "arcgisutils", "arcpbf", "arcgisgeocode", "arcgisplaces"),
5+
c("arcgislayers", "arcgisgeocode", "arcgisplaces", "calcite"),
66
from = "2024-01-11",
77
to = Sys.Date()
88
) |>
@@ -19,8 +19,21 @@ downloads <- cranlogs::cran_downloads(
1919

2020
ggplot(downloads) +
2121
geom_line(aes(week_of, total_downloads)) +
22-
facet_wrap("package", scales = "free", ncol = 2)
22+
facet_wrap("package", scales = "free_y", ncol = 2) +
23+
labs(y = "Cumulative downloads", x = "")
2324

2425
downloads |>
2526
summarise(downloads = sum(downloads), .by = package) |>
2627
arrange(desc(downloads))
28+
29+
30+
weekly <- cranlogs::cran_downloads(
31+
c("arcgislayers", "arcgisgeocode", "arcgisplaces", "calcite"),
32+
from = "2024-01-11",
33+
to = Sys.Date()
34+
) |>
35+
as_tibble() |>
36+
filter(count > 0) |>
37+
mutate(week_of = lubridate::floor_date(date, "week")) |>
38+
group_by(package, week_of) |>
39+
summarise(total = sum(count))

man/arc_open.Rd

Lines changed: 9 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/arc_read.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/arc_select.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/attachments.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)