Nothing
#' Create Esri layer objects
#'
#' These functions are used to generate list objects that can be converted into
#' json objects that are used in REST API requests. Notably they are used for adding
#' R objects as items to a portal.
#'
#' A `featureCollection` defines a layer of features that will be stored on a web map.
#' It consists of an array of `layer`s. The `layer` contains the features
#' (attributes and geometries) as a `featureSet` (see [as_esri_featureset()]) and
#' additional metadata which is stored in the `layerDefinition`object. The
#' `layerDefinition` most importantly documents the fields in the object, the object ID,
#' and additional metadata such as name, title, and display scale.
#'
#' Additional documentation for these json object:
#'
#' - [`layer`](https://developers.arcgis.com/rest/services-reference/enterprise/layer/)
#' - [`layerDefinition`](https://developers.arcgis.com/web-map-specification/objects/layerDefinition/)
#' - [`featureCollection`](https://developers.arcgis.com/web-map-specification/objects/featureCollection/)
#'
#' @export
#' @rdname layer_json
#' @param x an object of class `data.frame`. This can be an `sf` object or `tibble` or
#' any other subclass of `data.frame`.
#' @param name a scalar character of the name of the layer. Must be unique.
#' @param title A user-friendly string title for the layer that can be used in
#' a table of contents.
#' @param layer_definition a layer definition list as created by `as_layer_definition()`.
#' A default is derived from `x` and the `name` object.
#' @param id A number indicating the index position of the layer in the WMS or map service.
#' @param layer_url default `NULL`. A string URL to a service that should be used for all queries
#' against the layer. Used with hosted tiled map services on ArcGIS Online when
#' there is an associated feature service that allows for queries.
#' @param legend_url default `NULL`. A string URL to a legend graphic for the layer.
#' Used with WMS layers. The URL usually contains a GetLegendGraphic request.
#' @param popup_info default `NULL`. A list that can be converted into a
#' [popupInfo](https://developers.arcgis.com/web-map-specification/objects/popupInfo/)
#' object defining the pop-up window content for the layer. There is no helper for
#' popupInfo objects.
#' @inheritParams cli::cli_abort
#'
#' @returns
#'
#' A list object containing the required fields for each respective json type.
#' The results can be converted to json using `jsonify::to_json(x, unbox = TRUE)`
#' @examples
#' ld <- as_layer_definition(iris, "iris", "objectID")
#' l <- as_layer(iris, "iris name", "Iris Title")
#' fc <- as_feature_collection(layers = list(l))
as_layer <- function(
x,
name,
title,
layer_definition = as_layer_definition(
x,
name,
"object_id",
infer_esri_type(x)
),
id = NULL,
layer_url = NULL,
legend_url = NULL,
popup_info = NULL,
call = rlang::caller_env()
) {
if (!rlang::inherits_any(x, "data.frame")) {
cli::cli_abort(
"Expected {.cls data.frame} found {.obj_type_friendly {x}}",
call = call
)
} else if (!rlang::is_scalar_character(name)) {
cli::cli_abort(
"{.arg name} must be a scalar character vector.",
call = call
)
} else if (!rlang::is_scalar_character(title)) {
cli::cli_abort(
"{.arg title} must be a scalar character vector.",
call = call
)
} else if (!nzchar(name)) {
cli::cli_abort(
"{.arg name} must not be empty.",
call = call
)
} else if (!nzchar(title)) {
cli::cli_abort(
"{.arg name} must not be empty.",
call = call
)
}
oid_field <- layer_definition[["objectIdField"]]
# check that the OID field is present in x if not, create it.
# if not numeric error
# if numeric & not integer cast to int
if (!oid_field %in% colnames(x)) {
x[[oid_field]] <- 1:nrow(x)
} else if (!is.numeric(x[[oid_field]])) {
cli::cli_abort("{.arg x} must have a numeric Object ID field", call = call)
} else if (!is.integer(x[[oid_field]])) {
x[[oid_field]] <- as.integer(x[[oid_field]])
}
compact(
list(
featureSet = as_featureset(x),
id = id,
layerDefinition = layer_definition,
layerUrl = layer_url,
legendUrl = legend_url,
name = name,
title = title,
popupInfo = popup_info
)
)
}
#' @param object_id_field a scalar character vector indicating the name of the
#' object ID field in the dataset.
#' @param fields a data.frame describing the fields in `x`. These values are inferred
#' by default via [`infer_esri_type()`].
#' @param display_field default `NULL`. A scalar character containing the name of the field that
#' best summarizes the feature. Values from this field are used by default as
#' the titles for pop-up windows.
#' @param has_attachments default `FALSE`.
#' @param drawing_info default `NULL`. See REST documentation in details for more.
#' There are no helpers or validators for `drawingInfo` objects.
#' @param max_scale default `NULL`. A number representing the maximum scale at
#' which the layer definition will be applied. The number is the scale's
#' denominator; thus, a value of 2400 represents a scale of 1/2,400. A value
#' of 0 indicates that the layer definition will be applied regardless of
#' how far you zoom in.
#' @param min_scale default `NULL`. A number representing the minimum scale at which the layer definition will be applied.
#' @param templates default `NULL`. See REST documentation in details for more.
#' @param type_id_field default `NULL`. See REST documentation in details for more.
#' @param types An array of type objects available for the dataset.
#' This is used when the `type_id_field` is populated. NOTE there are no
#' helper functions to create type objects. Any type list objects must match the
#' json structure when passed to `jsonify::to_json(x, unbox = TRUE)`.
#' @export
#' @rdname layer_json
as_layer_definition <- function(
x,
name,
object_id_field,
fields = infer_esri_type(x),
display_field = NULL,
drawing_info = NULL,
has_attachments = FALSE,
max_scale = 0,
min_scale = 0,
templates = NULL,
type_id_field = NULL,
types = NULL,
call = rlang::caller_env()
) {
if (!rlang::inherits_any(x, "data.frame")) {
cli::cli_abort(
"Expected {.cls data.frame} found {.obj_type_friendly {x}}",
call = call
)
} else if (!rlang::is_scalar_character(name)) {
cli::cli_abort(
"{.arg name} must be a scalar character vector.",
call = call
)
} else if (!rlang::is_scalar_character(object_id_field)) {
cli::cli_abort(
"{.arg object_id_field} must be a scalar character vector.",
call = call
)
} else if (!nzchar(name)) {
cli::cli_abort(
"{.arg name} must not be empty.",
call = call
)
} else if (!nzchar(object_id_field)) {
cli::cli_abort(
"{.arg object_id_field} must not be empty.",
call = call
)
}
geo_type <- determine_esri_geo_type(x, call = call)
# get geo-type. If NULL `Table` else `Feature Layer`
type <- if (is.null(geo_type)) {
"Table"
} else {
"Feature Layer"
}
# check display field
if (!is.null(display_field) && !(display_field %in% colnames(x))) {
cli::cli_abort(
"{.arg display_field} must be a column in {.arg x}",
call = call
)
}
# check OID / create if needed
if (!object_id_field %in% fields[["name"]]) {
oid_fields <- data.frame(
name = object_id_field,
type = "esriFieldTypeOID",
alias = object_id_field,
length = NA,
editable = FALSE,
nullable = FALSE
)
fields <- rbind(oid_fields, fields)
} else {
oid_position <- which(object_id_field %in% fields[["name"]])
fields[["type"]][oid_position] <- "esriFieldTypeOID"
# make sure its not editable or nullable
fields[["editable"]][oid_position] <- FALSE
fields[["nullable"]][oid_position] <- FALSE
# remove length if present
fields[["length"]][oid_position] <- NA
}
layer_def_body <- list(
name = name,
displayField = display_field,
# https://developers.arcgis.com/documentation/common-data-types/drawinginfo.htm
drawingInfo = drawing_info,
# https://developers.arcgis.com/documentation/common-data-types/field.htm
objectIdField = object_id_field,
geometryType = geo_type,
fields = fields,
hasAttachments = FALSE,
maxScale = max_scale,
minScale = min_scale,
# https://developers.arcgis.com/documentation/common-data-types/template.htm
templates = templates,
# Must be "Feature Layer" or "Table"
type = type,
# field that determine the geometry type
typeIdField = type_id_field,
# https://developers.arcgis.com/documentation/common-data-types/type.htm
types = types,
# this is not documented anywhere but is required from what i can tell
extent = as_extent(x)
)
compact(layer_def_body)
}
#' @export
#' @rdname layer_json
#'
#' @param layers a list of layers as created by `as_layer()`.
#' @param show_legend default `FALSE`. Logical scalar indicating if this layer
#' should be shown in the legend in client applications.
as_feature_collection <- function(
layers = list(),
show_legend = TRUE,
call = rlang::caller_env()
) {
if (!rlang::is_list(layers)) {
cli::cli_abort(
c(
"Invalid {.arg layers} object.",
"i" = "expected {.cls list}, found {.obj_type_friendly {layers}}"
),
call = call
)
}
c(list(layers = layers), showLegend = show_legend)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.