r-arcgis / arcgisutils Goto Github PK
View Code? Open in Web Editor NEWHome Page: http://r.esri.com/arcgisutils/
License: Apache License 2.0
Home Page: http://r.esri.com/arcgisutils/
License: Apache License 2.0
I'm not sure whether these would belong here or in arcgislayers or wouldn't be of interest at all but I've been using a couple of helper functions that make creating queries for FeatureLayers much easier. These are the two I'm currently using most often:
ansi_in <- function(needle, haystack, ..., .envir = rlang::current_env(), .con = DBI::ANSI()) {
glue::glue_sql(needle, " ", "IN", " ({haystack*})", ..., .envir = .envir, .con = .con)
}
sql_collapse <- function(x, sep = " AND ") {
glue::glue_sql_collapse(x, sep = sep)
}
I think the same syntax rules described here also apply to the REST API: https://doc.arcgis.com/en/drone2map/latest/help/sql-reference-for-elements-used-in-query-expressions.htm
Hi, so I have an issue where complex geometries fail to draw when they are uploaded to AGOL using the publish_layer()
feature. It appears that complex geometries have a significant difficulty loading when published to arcgis online.
Here is an example with some code that demonstrates this glaring issue in R while trying to pull down a public dataset involving CCRI:
`### Login to AGOL
token_AGOL <- auth_user(
username = "U",
password = "PW",
host = arc_host(),
expiration = 21600
)
set_arc_token(token_AGOL)
CCRI_Layer <- arc_open(CCRI_URL)
CCRI_Layer
NY_CCRI_DF <- arc_select(CCRI_Layer,where = "STATEFP = '36'")
NY_CCRI_DF
res <- publish_layer(NY_CCRI_DF, "R-arcgisBridge Upload Test")
res
'
Unfortunately, once the shapefile is uploaded it collapses and fails to draw any of the more complex geometries - i.e. the entirety of long island
Within the Rstudio environment ,there is no issue with plotting the data.... via the command plot(NY_CCRI_DF)
The questions I have are:
When Sys.getenv("ARCGIS_TOKEN")
returns an empty string, this will sometimes cause HTTP 498 "invalid token" error. Proposed solution is to empty the token with NULL.
Follow up with: R-ArcGIS/arcgislayers#58
Possible solution from gabor is to use vctrs standalone from r-lang
https://fosstodon.org/@gaborcsardi/111097312300288758
would like to have tibble-esque printing with data.frame class. The tables we get are fairly nasty.
Reprex:
library(arcgisutils)
library(arcgislayers)
token <- auth_code()
# TODO why does the refresh token, not give another refresh here?
refreshed <- refresh_token(token)
# BUG: refreshing a refreshed token errors!!!!!
refreshed |> refresh_token()
portals <- arc_base_req(
"https://arcgis.com/sharing/rest/portals/self",
arc_token()
) |>
req_body_form(f = "json") |>
req_perform() |>
resp_body_string() |>
RcppSimdJson::fparse()
This function will return a massive list that includes just about every piece of information you could possibly want to know about your user in your authorized portal.
library(httr2)
#> Warning: package 'httr2' was built under R version 4.3.1
library(arcgisutils)
#> Warning: package 'arcgisutils' was built under R version 4.3.1
portals <- arc_base_req(
"https://arcgis.com/sharing/rest/portals/self",
arc_token()
) |>
req_body_form(f = "json") |>
req_perform() |>
resp_body_string() |>
RcppSimdJson::fparse()
names(portals)
#> [1] "2DStylesGroupQuery" "3DBasemapGalleryGroupQuery"
#> [3] "analysisLayersGroupQuery" "basemapGalleryGroupQuery"
#> [5] "cdnUrl" "colorSetsGroupQuery"
#> [7] "contentCategorySetsGroupQuery" "customBaseUrl"
#> [9] "defaultBasemap" "defaultDevBasemap"
#> [11] "defaultExtent" "defaultVectorBasemap"
#> [13] "description" "devBasemapGalleryGroupQuery"
#> [15] "featuredGroups" "featuredItemsGroupQuery"
#> [17] "galleryTemplatesGroupQuery" "helpBase"
#> [19] "helperServices" "homePageFeaturedContent"
#> [21] "homePageFeaturedContentCount" "isPortal"
#> [23] "layerTemplatesGroupQuery" "livingAtlasGroupQuery"
#> [25] "name" "portalHostname"
#> [27] "portalMode" "portalName"
#> [29] "portalThumbnail" "rasterFunctionTemplatesGroupQuery"
#> [31] "rotatorPanels" "staticImagesUrl"
#> [33] "stylesGroupQuery" "supports3DTilesServices"
#> [35] "supportsHostedServices" "symbolSetsGroupQuery"
#> [37] "templatesGroupQuery" "thumbnail"
#> [39] "use3dBasemaps" "useVectorBasemaps"
#> [41] "vectorBasemapGalleryGroupQuery" "httpPort"
#> [43] "httpsPort" "ipCntryCode"
#> [45] "supportsOAuth" "isReadOnly"
#> [47] "currentVersion"
Created on 2024-03-02 with reprex v2.0.2
To fix this we can modify fetch_layer_metadata()
to this which adds f=json
to the url and adds the token in the appropriate manner using req_auth_bearer_token()
.
fetch_layer_metadata <- function(request, token) {
# add f=json to the url for querying
req <- httr2::req_url_query(request, f = "json")
# add the token
req <- httr2::req_auth_bearer_token(req, token)
# process the request and capture the response string
resp_string <- httr2::resp_body_string(
httr2::req_perform(req)
)
# process the response string
meta <- RcppSimdJson::fparse(resp_string)
# check if any errors occurred
detect_errors(meta)
# return the list
meta
}
TODO:
fetch_layer_metadata(httr2::request(furl),"")
https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer
CC @kbvernon
As title describes. Current error message is below. Users might not know how to create an authorization token. The message should indicate how to create a token if needed.
arcgisutils::obj_check_token(NULL)
#> Error:
#> ! `token` must be an <httr2_token> not <NULL>
#> Backtrace:
#> ▆
#> 1. └─arcgisutils::obj_check_token(NULL)
#> 2. └─cli::cli_abort(...) at arcgisutils/R/arc-token.R:128:5
#> 3. └─rlang::abort(...)
Created on 2024-03-20 with reprex v2.0.2
arcgisutils/R/utils-requests.R
Line 103 in b8be833
Maybe it could be useful that if there are no matches when using the filter_geom
argument in the arc_read
function, instead of an error, I get an empty dataset, just like when you do a filter in sf package with the query
argument.
library(arcgislayers)
geojson <- '{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[
[
-72.16260961974183,
4.170524735334581
],
[
-72.16260961974183,
3.5284018431919435
],
[
-71.25683279344358,
3.5284018431919435
],
[
-71.25683279344358,
4.170524735334581
],
[
-72.16260961974183,
4.170524735334581
]
]
],
"type": "Polygon"
}
}
]
}'
bbox <- geojsonsf::geojson_sf(geojson) |> sf::st_bbox()
url <- 'https://ags.esri.co/arcgis/rest/services/DatosAbiertos/PNN_2005/MapServer/0/'
data <- arc_read(url), filter_geom = bbox)
When I run that code, I get this error: Error in fts_raw[["attributes"]] : subíndice fuera de los límites
For validation and handling of input URLs within {arcgislayers} and this package, it would be helpful to have a reliable set of url validation and checking functions.
This reprex is a first attempt at adapting and updating the existing URL checking and validation code from {esri2sf}
for reuse in this package.
library(arcgisutils)
library(rlang)
#>
#> Attaching package: 'rlang'
#> The following object is masked from 'package:arcgisutils':
#>
#> %||%
# Helper for setting URL pattern
set_url_pattern <- function(pattern = NULL, nm = NULL, collapse = "|") {
if (is.null(pattern)) {
return(pattern)
}
if (!is.null(nm)) {
nm <- match.arg(nm, names(pattern))
pattern <- pattern[nm]
}
paste0(pattern, collapse = collapse)
}
# Does x match the pattern of a URL?
# @noRd
is_url <- function(
x,
pattern = NULL,
...) {
if (!is_vector(x) || is_empty(x)) {
return(FALSE)
}
url_pattern <-
"http[s]?://(?:[[:alnum:]]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+"
if (is.null(pattern)) {
return(grepl(url_pattern, x, ...))
}
pattern <- paste0(pattern, collapse = "|")
grepl(url_pattern, x, ...) & grepl(pattern, x, ...)
}
# Check if x is a valid URL
# @noRd
check_url <- function(
x,
pattern = NULL,
...,
allow_null = FALSE,
message = NULL,
arg = caller_arg(url),
call = caller_env()) {
if (allow_null && is.null(x)) {
return(invisible(NULL))
}
if (is_url(x, pattern = pattern, ...)) {
return(invisible(NULL))
}
check_string(
x,
allow_empty = FALSE,
allow_null = allow_null,
arg = arg,
call = call
)
if (is.null(message)) {
message <- "{.arg {arg}} must be a valid url,
not {.obj_type_friendly {x}}."
if (!is.null(pattern)) {
message <- "{.arg {arg}} must be a valid url
matching the pattern {.str {pattern}}"
}
}
cli::cli_abort(
message,
call = call
)
}
# Check if x is a string
# @noRd
check_string <- function(
x,
allow_empty = TRUE,
allow_null = FALSE,
arg = caller_arg(x),
call = caller_env()
) {
if (allow_null && is.null(x)) {
return(invisible(NULL))
}
message <- "{.arg {arg}} must be a scalar character vector."
if (is_scalar_character(x)) {
if (allow_empty || x != "") {
return(invisible(NULL))
}
message <- '{.arg {arg}} must be a non-empty string.'
}
cli::cli_abort(
message,
call = call
)
}
# Vector of valid ESRI service types
esri_service_types <- c(
"MapServer", "FeatureServer", "ImageServer", "GeoDataServer",
"GeocodeServer", "GeometryServer", "GPServer", "WFSServer", "WFCServer"
)
# Named vector of ESRI service types
esri_service_type_patterns <- setNames(
paste0("(?<=/rest/services).+/", esri_service_types, "/"),
esri_service_types
)
# Vector of valid ESRI service types
esri_layer_types <- c(
"FeatureLayer", "Table", "GroupLayer"
)
# Named vector of ESRI Item URL patterns
esri_item_url_patterns <- c(
"content" = "/home/content\\.html\\?view=",
"search" = "/home/search\\.html",
"item" = "/home/item\\.html\\?id=",
"group" = "/home/group\\.html\\?id=",
"user" = "/home/user\\.html\\?user=",
"scene" = "/home/webscene/viewer\\.html\\?webscene=",
"app" = "/index\\.html\\?appid=",
"notebook" = "/notebook/notebook\\.html\\?rid=",
"experience" = "/experience/"
)
# Named vector of ESRI URL patterns for data services and items
esri_service_url_patterns <- c(
"root" = "/rest/services/?$",
"service" = paste0("(?<=/rest/services)",
"(", paste0(esri_service_types, collapse = "|"), ")/?$"),
"layer" = paste0("(?<=/rest/services)",
"(", paste0(esri_service_types, collapse = "|"), ")",
"/[[:digit:]]+/?$"),
esri_service_type_patterns
)
# Named vector of messages for what defines a valid URL of the specified type
valid_esri_url_messages <- c(
"root" = "A {.val {type}} URL must end in {.str /rest/services}.",
"folder" = "A {.val {type}} URL must be a 'Folder' endpoint",
"service" = "A {.val {type}} URL must end in one of the supported service types ({esri_service_types})",
"feature" = "A {.val {type}} URL must end in a feature ID",
"content" = "A {.val {type}} URL must include the text {.str /content/}.",
"search" = "A {.val {type}} URL must include the text {.str /home/search.html?q=}.",
"item" = "An {.val {type}} URL must include the text {.str /home/item.html?id=}.",
"group" = "A {.val {type}} URL must include the text {.str /home/group.html?id=}.",
"user" = "A {.val {type}} URL must include the text {.str /home/user.html?user=}.",
"scene" = "A {.val {type}} URL must include the text {.str /home/webscene/viewer.html?webscene=}.",
"app" = "An {.val {type}} URL must include the text {.str /index.html?appid=}.",
"notebook" = "An {.val {type}} URL must include the text {.str /notebook/notebook.html?rid=}.",
"experience" = "An {.val {type}} URL must include the text {.str /experience/}.",
setNames(
paste0("A {.val {type}} URL must include the text {.str /", esri_service_types, "/} after {.str /rest/services/}"),
esri_service_types
)
)
# Is a character vector an ESRI service or item URL?
# @name is_esri_url
NULL
#> NULL
# - [is_esri_services_url()]: Does x match the pattern of a ESRI Item URL?
# @name is_esri_item_url
# @rdname is_esri_url
# @export
is_esri_item_url <- function(x, type = NULL) {
is_url(
x,
pattern = set_url_pattern(esri_item_url_patterns, nm = type),
perl = TRUE)
}
# - [is_esri_services_url()] Does x match the pattern of a ESRI Service URL?
# @name is_esri_services_url
# @rdname is_esri_url
# @export
is_esri_services_url <- function(x, type = NULL) {
is_url(
x,
pattern = set_url_pattern(esri_service_url_patterns, nm = type),
perl = TRUE)
}
# - [is_esri_folder_url()]: Is x a folder URL?
# @name is_esri_folder_url
# @rdname is_esri_url
# @export
is_esri_folder_url <- function(x, metadata = NULL, ..., call = caller_env()) {
if (is.null(metadata)) {
check_string(x, call = call)
metadata <- fetch_layer_metadata(httr2::request(x), ..., error_call = call)
}
# FIXME: This is carried over from esri2sf but needs to be double-checked
is_url(x, pattern = "/rest/services") & ("folders" %in% names(metadata))
}
check_esri_url <- function(x,
type = "service",
values = NULL,
allow_null = FALSE,
arg = caller_arg(x),
call = caller_env()) {
if (allow_null && is.null(x)) {
return(invisible(NULL))
}
check_url(x, arg = arg, call = call)
if (is.null(values)) {
values <- names(
c(esri_item_url_patterns,
esri_service_url_patterns,
esri_service_type_patterns)
)
}
type <- arg_match(type, values = values)
if (type %in% names(esri_service_url_patterns)) {
pattern <- esri_service_url_patterns
} else if (type %in% names(esri_item_url_patterns)) {
pattern <- esri_item_url_patterns
} else if (type %in% names(esri_service_type_patterns)) {
pattern <- esri_service_type_patterns
}
pattern <- set_url_pattern(pattern, type)
if (is_url(x, pattern = pattern, perl = TRUE)) {
return(invisible(NULL))
}
info_message <- valid_esri_url_messages[type]
info_message <- set_names(
info_message,
rep_len("i", length(info_message))
)
cli::cli_abort(
c("{.arg {arg}} must be a valid {.val {type}} url.",
validation_messages),
call = call
)
}
# Examples
item_url <- "https://www.arcgis.com/home/item.html?id=ebeb65deb5c14f4d8849fd68944b7ee6"
search_url <- "https://www.arcgis.com/home/search.html?restrict=false&sortField=relevance&sortOrder=desc&searchTerm=census#content"
root_url <- "https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services"
server_url <- "https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Census_2020_DHC_Total_Population/FeatureServer/"
layer_url <- "https://services.arcgis.com/P3ePLMYs2RVChkJx/ArcGIS/rest/services/USA_Census_2020_DHC_Total_Population/FeatureServer/3"
is_esri_item_url(item_url)
#> [1] TRUE
is_esri_item_url(item_url, "search")
#> [1] FALSE
is_esri_item_url(search_url)
#> [1] TRUE
is_esri_item_url(search_url, "item")
#> [1] FALSE
is_esri_services_url(root_url)
#> [1] TRUE
is_esri_services_url(server_url)
#> [1] TRUE
is_esri_services_url(server_url, "root")
#> [1] FALSE
is_esri_services_url(layer_url)
#> [1] TRUE
is_esri_services_url(layer_url, "ImageServer")
#> [1] FALSE
Created on 2024-02-23 with reprex v2.1.0
I know the ArcGIS authorization tokens expire but it is a PITA to have the set token disappear and require re-authentication every time I reset my session within R.
I have a revised, and re-revised version of the tidycensus::census_api_key()
function that I've used as a token setting helper in several different packages. I'd be happy to reuse that code to implement similar features for set_auth_token()
. You can see that here: https://github.com/elipousson/standaloner/blob/main/R/standalone-settoken.R
Potentially related to R-ArcGIS/arcgislayers#128
{sf} vendors GDAL, GEOS, and PROJ making it a massive R package. The {wk} package is very small and very lightweight and can operate on different geometry types.
It could be used instead of {sf} in many places. Where it is not possible to replace it, we can guard code execution with rlang::check_installed()
.
Working on {arcgisplaces}, I am thinking about how it may be used in production grade shiny applications. Since it is build mostly on Rust and httr2 it is fairly lightweight and it returns sf objects without actually having to have the package available (thank you S3 class system).
{sf} could be suggested, but shouldn't be required. Adding 300mb to a Docker container can be a limiting factor for creating a production ready application.
See R-ArcGIS/arcgisgeocode#17 and R-ArcGIS/arcgisgeocode#18 for repro
It would be nice to add a retry to requests that are ran in parallel. Sometimes a feature service is overwhelmed and an error may be returned (500) but the request was fine.
The challenge though, is that the only way to find out is to parse the response which is an expensive operation and we want to do that once at most. How might that be designed?
Changes to arcgisutils will have downstream effects on arcgislayers
. It's important that these downstream changes are recognized. This will likely have to be done with revdepcheck once we get arcgislayers published.
It's important that any breaking changes are caught early
set_auth_token()
is not accepting characters though it is designed to. There's a clear bug here:
function(token, quietly = FALSE) {
stopifnot(inherits(token, c("httr2_token", "character")))
if (inherits(token, "httr2_token")) {
Sys.setenv("ARCGIS_TOKEN" = token[["access_token"]])
} else if (inherits(token, "character")) {
Sys.setenv("ARCGIS_TOKEN" = token[["access_token"]]) # FIXME: characters cannot be subset like this
}
if (quietly) return(invisible())
cat("Token set to environment variable `ARCGIS_TOKEN`")
invisible(token)
}
There are a number of fixes that should occur here:
else if
branch:
else
arm should be added with a generic error that says token cannot be set. This should be unreachable, but may be reached in some off case I can't think of.A common need from processing many requests at once is to combine the results into a single data frame. This is done ad hoc in arcgislayers and arcpbf.
arcgislayers uses do.call(rbind.data.frame)
which is the slowest approach. arcpbf has adopted a hierarchy of the fastest implementations using collapse, data.table, dplyr, and base R. This should be provided in arcgisutils. It is needed in arcgeocode at the moment as well.
Also: https://github.com/R-ArcGIS/arcpbf/blob/main/R/post-process.R#L109-L121
Is your feature request related to a problem? Please describe.
validate_crs()
is used to create a spatialReference
json object. However this can be called many levels deep inside of a function and the context may get lost.
Describe the solution you'd like
It would be useful to introduce error_call
argument to allow us to pass in the caller environment in the case that validation fails.
Additional context
Additionally validate_crs()
creates a list with one element "spatialReference"
in most cases we subset directly into this element making the first element superfluous. It may make sense to adjust the function to to not contain this. Note that it would require adjusting all calls to this function.
Adding support for renderer
objects could help support features like R-ArcGIS/arcgislayers#119 and R-ArcGIS/arcgislayers#106 from {arcgislayers}
. Here are the three types of "renderers" from the developer documentation:
This will also include adding support for symbol objects:
Also color ramp objects:
And, finally, classification objects:
The documentation on drawingInfo and renderer in the Web Map Specification docs is another relevant reference.
I've put all these object types in as a checklist so I can check them off when I get them added in. If there are any existing functions for special handling of layer metadata that I should use as a model for structuring these additions, please let me know.
Describe the bug
The bug seems to be within arc_select()
. Code below works fine with just running arc_open(url)
. Thanks.
library(arcgislayers)
url <- 'https://gisportalp.itd.idaho.gov/xserver/rest/services/RH_GeneralService/MapServer/1'
road_network <- arc_select(arc_open(url))
#> Warning: `multi_req_perform()` was deprecated in httr2 1.0.0.
#> ℹ Please use `req_perform_parallel()` instead.
#> ℹ The deprecated feature was likely used in the arcgislayers package.
#> Please report the issue to the authors.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
#> Iterating ■■ 2% | ETA: 3mIterating ■■ 4% | ETA: 2mIterating ■■■ 8% | ETA:
#> 1mIterating ■■■■ 10% | ETA: 1mIterating ■■■■ 12% | ETA: 1mIterating ■■■■■■ 15%
#> | ETA: 39sIterating ■■■■■■ 17% | ETA: 35sIterating ■■■■■■■ 19% | ETA:
#> 31sIterating ■■■■■■■■ 23% | ETA: 26sIterating ■■■■■■■■■ 25% | ETA: 24sIterating
#> ■■■■■■■■■■ 31% | ETA: 18sIterating ■■■■■■■■■■■ 33% | ETA: 20sIterating
#> ■■■■■■■■■■■ 35% | ETA: 20sIterating ■■■■■■■■■■■■■ 38% | ETA: 19sIterating
#> ■■■■■■■■■■■■■■ 42% | ETA: 17sIterating ■■■■■■■■■■■■■■ 44% | ETA: 16sIterating
#> ■■■■■■■■■■■■■■■ 46% | ETA: 15sIterating ■■■■■■■■■■■■■■■■ 50% | ETA:
#> 13sIterating ■■■■■■■■■■■■■■■■■ 52% | ETA: 12sIterating ■■■■■■■■■■■■■■■■■ 54% |
#> ETA: 12sIterating ■■■■■■■■■■■■■■■■■■ 56% | ETA: 12sIterating
#> ■■■■■■■■■■■■■■■■■■■ 60% | ETA: 10sIterating ■■■■■■■■■■■■■■■■■■■ 62% | ETA:
#> 10sIterating ■■■■■■■■■■■■■■■■■■■■■ 65% | ETA: 8sIterating ■■■■■■■■■■■■■■■■■■■■■
#> 67% | ETA: 8sIterating ■■■■■■■■■■■■■■■■■■■■■■ 69% | ETA: 7sIterating
#> ■■■■■■■■■■■■■■■■■■■■■■ 71% | ETA: 7sIterating ■■■■■■■■■■■■■■■■■■■■■■■ 73% |
#> ETA: 6sIterating ■■■■■■■■■■■■■■■■■■■■■■■ 75% | ETA: 6sIterating
#> ■■■■■■■■■■■■■■■■■■■■■■■■■ 81% | ETA: 4sIterating ■■■■■■■■■■■■■■■■■■■■■■■■■■ 83%
#> | ETA: 4sIterating ■■■■■■■■■■■■■■■■■■■■■■■■■■ 85% | ETA: 3sIterating
#> ■■■■■■■■■■■■■■■■■■■■■■■■■■■ 87% | ETA: 3sIterating ■■■■■■■■■■■■■■■■■■■■■■■■■■■■
#> 88% | ETA: 3sIterating ■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 90% | ETA: 2sIterating
#> ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 92% | ETA: 2sIterating
#> ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 94% | ETA: 1sIterating
#> ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 96% | ETA: 1sIterating
#> ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 98% | ETA: 0s
#> Error in eval(expr, envir, enclos): Not a matrix.
Created on 2024-02-20 with reprex v2.0.2
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.2.2 (2022-10-31 ucrt)
#> os Windows 10 x64 (build 19045)
#> system x86_64, mingw32
#> ui RTerm
#> language (EN)
#> collate English_United States.utf8
#> ctype English_United States.utf8
#> tz America/Denver
#> date 2024-02-20
#> pandoc 3.1.1 @ C:/Program Files/RStudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown)
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> arcgislayers * 0.1.0 2023-11-13 [1] Github (R-ArcGIS/arcgislayers@67fabb2)
#> arcgisutils 0.1.0 2023-11-14 [1] Github (R-ArcGIS/arcgisutils@bb5d292)
#> class 7.3-22 2023-05-03 [1] CRAN (R 4.2.3)
#> classInt 0.4-9 2023-02-28 [1] CRAN (R 4.2.3)
#> cli 3.6.1 2023-03-23 [1] CRAN (R 4.2.3)
#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.3)
#> curl 5.2.0 2023-12-08 [1] CRAN (R 4.2.3)
#> DBI 1.2.1 2024-01-12 [1] CRAN (R 4.2.2)
#> digest 0.6.31 2022-12-11 [1] CRAN (R 4.2.3)
#> dplyr 1.1.2 2023-04-20 [1] CRAN (R 4.2.3)
#> e1071 1.7-13 2023-02-01 [1] CRAN (R 4.2.3)
#> evaluate 0.23 2023-11-01 [1] CRAN (R 4.2.3)
#> fansi 1.0.4 2023-01-22 [1] CRAN (R 4.2.3)
#> fastmap 1.1.1 2023-02-24 [1] CRAN (R 4.2.3)
#> fs 1.6.2 2023-04-25 [1] CRAN (R 4.2.3)
#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.3)
#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.3)
#> htmltools 0.5.7 2023-11-03 [1] CRAN (R 4.2.3)
#> httr2 1.0.0 2023-11-14 [1] CRAN (R 4.2.3)
#> jsonify 1.2.2 2022-11-09 [1] CRAN (R 4.2.3)
#> KernSmooth 2.23-21 2023-05-03 [1] CRAN (R 4.2.3)
#> knitr 1.45 2023-10-30 [1] CRAN (R 4.2.3)
#> lifecycle 1.0.4 2023-11-07 [1] CRAN (R 4.2.3)
#> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.2.3)
#> pillar 1.9.0 2023-03-22 [1] CRAN (R 4.2.3)
#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.3)
#> proxy 0.4-27 2022-06-09 [1] CRAN (R 4.2.3)
#> purrr 1.0.1 2023-01-10 [1] CRAN (R 4.2.3)
#> R.cache 0.16.0 2022-07-21 [1] CRAN (R 4.2.3)
#> R.methodsS3 1.8.2 2022-06-13 [1] CRAN (R 4.2.2)
#> R.oo 1.25.0 2022-06-12 [1] CRAN (R 4.2.2)
#> R.utils 2.12.2 2022-11-11 [1] CRAN (R 4.2.3)
#> R6 2.5.1 2021-08-19 [1] CRAN (R 4.2.3)
#> rappdirs 0.3.3 2021-01-31 [1] CRAN (R 4.2.3)
#> Rcpp 1.0.11 2023-07-06 [1] CRAN (R 4.2.3)
#> RcppSimdJson 0.1.10 2023-05-14 [1] CRAN (R 4.2.3)
#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.3)
#> rlang 1.1.3 2024-01-10 [1] CRAN (R 4.2.3)
#> rmarkdown 2.25 2023-09-18 [1] CRAN (R 4.2.3)
#> rstudioapi 0.15.0 2023-07-07 [1] CRAN (R 4.2.3)
#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.3)
#> sf 1.0-14 2023-07-11 [1] CRAN (R 4.2.3)
#> styler 1.10.1 2023-06-05 [1] CRAN (R 4.2.3)
#> tibble 3.2.1 2023-03-20 [1] CRAN (R 4.2.3)
#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.3)
#> units 0.8-2 2023-04-27 [1] CRAN (R 4.2.3)
#> utf8 1.2.3 2023-01-31 [1] CRAN (R 4.2.3)
#> vctrs 0.6.3 2023-06-14 [1] CRAN (R 4.2.3)
#> withr 3.0.0 2024-01-16 [1] CRAN (R 4.2.3)
#> xfun 0.39 2023-04-20 [1] CRAN (R 4.2.3)
#> yaml 2.3.7 2023-01-23 [1] CRAN (R 4.2.3)
#>
#> [1] C:/R/library
#> [2] C:/Program Files/R/R-4.2.2/library
#>
#> ──────────────────────────────────────────────────────────────────────────────
One of the more common causes of a failed request is an expired token. We should consider adding a check to the token to see if it has expired or not before continuing.
Needed:
In the future:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.