Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@
^data-raw$
^README_files/.*$
^CRAN-SUBMISSION$
^[.]?air[.]toml$
^\.vscode$
^\.github$
5 changes: 5 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"Posit.air-vscode"
]
}
10 changes: 10 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"[r]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "Posit.air-vscode"
},
"[quarto]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "quarto.quarto"
}
}
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Encoding: UTF-8
URL: https://github.com/juanfonsecaLS1/azuremapsr
BugReports: https://github.com/juanfonsecaLS1/azuremapsr/issues
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
RoxygenNote: 7.3.3
Imports:
geojsonsf (>= 2.0.3),
httr2 (>= 1.2.1),
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# azuremapsr (development version)

- Fixed bug in `get_point` function ([#4](https://github.com/juanfonsecaLS1/azuremapsr/issues/4)), an `sf` object with multiple features is now admitted.

# azuremapsr 0.0.2

- Fixed bug in `POSTbody_builder_directions_geojson` when waypoints were not provided.
Expand Down
108 changes: 66 additions & 42 deletions R/bodyBuilder.R
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
#' Build GeoJSON Body for Route Directions
#'
#' Constructs the GeoJSON part of the request body for the Azure Maps Route
#' Constructs the GeoJSON part of the request body for the Azure Maps Route
#' Directions API. This includes the origin, destination, and any waypoints.
#'
#' @param origin A numeric vector of coordinates (longitude, latitude) or an `sf`
#' @param origin A numeric vector of coordinates (longitude, latitude) or an `sf`
#' object representing the starting point.
#' @param destination A numeric vector of coordinates (longitude, latitude) or an
#' @param destination A numeric vector of coordinates (longitude, latitude) or an
#' `sf` object representing the end point.
#' @param waypoints Optional. A numeric vector, a matrix of coordinates, or an
#' @param waypoints Optional. A numeric vector, a matrix of coordinates, or an
#' `sf` object with POINT geometries for intermediate stops.
#'
#' @return A list formatted as a GeoJSON FeatureCollection, ready to be
#' @return A list formatted as a GeoJSON FeatureCollection, ready to be
#' included in the API request body.
#' @export
#'
Expand All @@ -21,33 +21,41 @@
#' waypoints <- c(-122.20687, 47.612002)
#' geojson_part <- POSTbody_builder_directions_geojson(origin, destination, waypoints)
#' }
POSTbody_builder_directions_geojson <- function(origin,
destination,
waypoints = NULL){

POSTbody_builder_directions_geojson <- function(
origin,
destination,
waypoints = NULL
) {
sfc_origin <- get_point(origin)

sfc_destination <- get_point(destination)

if(!is.null(waypoints)){
sfc_waypoints <- get_point(waypoints)
if (!is.null(waypoints)) {
sfc_waypoints <- get_point(waypoints, multiple = TRUE)
} else {
sfc_waypoints <- NULL
}

sf_body <- sf::st_as_sf(c(sfc_origin,sfc_waypoints,sfc_destination))
sf_body <- sf::st_as_sf(c(sfc_origin, sfc_waypoints, sfc_destination))
sf_body$pointIndex <- rownames(sf_body) |> as.integer()
sf_body$pointIndex <- sf_body$pointIndex - 1

sf_body$pointType <- "waypoint"

if(nrow(sf_body)>2){
sf_body$pointType[2:(nrow(sf_body)-1)] <- "viaWaypoint"
if (nrow(sf_body) > 2) {
sf_body$pointType[2:(nrow(sf_body) - 1)] <- "viaWaypoint"
}

geojson_body <- geojsonsf::sf_geojson(sf_body[,c("pointIndex","pointType","x")])
geojson_body <- geojsonsf::sf_geojson(sf_body[, c(
"pointIndex",
"pointType",
"x"
)])

geojson_list <- stringr::str_remove_all(geojson_body,"(?<=pointIndex\":\\d{1,2})\\.0") |>
geojson_list <- stringr::str_remove_all(
geojson_body,
"(?<=pointIndex\":\\d{1,2})\\.0"
) |>
jsonlite::fromJSON() |>
as.list()

Expand All @@ -59,14 +67,14 @@ POSTbody_builder_directions_geojson <- function(origin,

#' Build JSON Parameter Body for Route Directions
#'
#' Constructs the JSON part of the request body containing routing parameters
#' Constructs the JSON part of the request body containing routing parameters
#' for the Azure Maps Route Directions API.
#'
#' @param params A list of routing parameters, such as `travelMode`,
#' @param params A list of routing parameters, such as `travelMode`,
#' `routeType`, `departAt`, etc.
#' @param tz A string specifying the timezone for any date-time parameters.
#'
#' @return A list of routing parameters, with values formatted and unboxed as
#' @return A list of routing parameters, with values formatted and unboxed as
#' required for the JSON request.
#' @export
#'
Expand All @@ -78,47 +86,63 @@ POSTbody_builder_directions_geojson <- function(origin,
#' )
#' json_part <- POSTbody_builder_directions_json(params, "UTC")
#' }
POSTbody_builder_directions_json <- function(params,tz){

POSTbody_builder_directions_json <- function(params, tz) {
template_params <- pkg.env$template_params_directions

initial_check <- check_params(params,template_params,tz)
initial_check <- check_params(params, template_params, tz)

# Date formatting
datecols <- c("departAt","arriveAt")
datecols <- c("departAt", "arriveAt")

datecols_check <- datecols %in% names(params)

if(any(datecols_check)){
tmp_date <- lubridate::as_datetime(params[[datecols[datecols_check]]],tz = tz) |> lubridate::with_tz("UTC")
params[[datecols[datecols_check]]] <- strftime(tmp_date,format = "%Y-%Om-%dT%H:%M:%OS3Z")
if (any(datecols_check)) {
tmp_date <- lubridate::as_datetime(
params[[datecols[datecols_check]]],
tz = tz
) |>
lubridate::with_tz("UTC")
params[[datecols[datecols_check]]] <- strftime(
tmp_date,
format = "%Y-%Om-%dT%H:%M:%OS3Z"
)
}

default_list_names <- names(template_params)[vapply(template_params,function(x){x$required},logical(1))]

default_list_names <- default_list_names[!(default_list_names %in% names(params))]

if (length(default_list_names)>0){

default_list <- lapply(default_list_names,function(j){template_params[[j]]$value[1]})
names(default_list) <- default_list_names

params <- c(params,default_list)
default_list_names <- names(template_params)[vapply(
template_params,
function(x) {
x$required
},
logical(1)
)]

default_list_names <- default_list_names[
!(default_list_names %in% names(params))
]

if (length(default_list_names) > 0) {
default_list <- lapply(default_list_names, function(j) {
template_params[[j]]$value[1]
})
names(default_list) <- default_list_names

params <- c(params, default_list)
}

## Unboxing to match the JSON structure of the API
final_list_names <- names(params)

to_unbox_names <- final_list_names[vapply(final_list_names,function(x){!template_params[[x]]$multiple},logical(1))]
to_unbox_names <- final_list_names[vapply(
final_list_names,
function(x) {
!template_params[[x]]$multiple
},
logical(1)
)]

for (i in to_unbox_names) {
params[[i]] <- jsonlite::unbox(params[[i]])
}

params
}





2 changes: 1 addition & 1 deletion R/data.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ NULL
#' @docType data
#' @format a `httr2` response object
#' @keywords directions response
NULL
NULL
73 changes: 50 additions & 23 deletions R/get_points.R
Original file line number Diff line number Diff line change
@@ -1,67 +1,94 @@
#' Create sfc Object from Coordinates or sf Object
#' Create sfc Object from Coordinates or sf POINT Object
#'
#' Converts a pair of coordinates, a matrix of coordinates, or an sf POINT object into an sfc object for use in GeoJSON bodies.
#' Converts a pair of coordinates (numeric vector), a matrix of coordinates, or an sf/sfc POINT object into an sfc object for use in GeoJSON bodies.
#' Only POINT geometries are supported. The output is always in EPSG:4326.
#'
#' @param x A numeric vector of length 2, a matrix with coordinates, or an sf object of POINT type.
#' @param x A numeric vector of length 2, a matrix with two columns (coordinates), or an sf/sfc object of POINT type.
#' @param multiple Logical; if TRUE, allows handling of multiple features (e.g., when input is an sfc or sf object with more than one POINT). Default is FALSE.
#'
#' @return An sfc object with coordinates in EPSG:4326.
#' @export
#'
#' @examples
#' get_point(c(-122.201399,47.608678))
get_point <- function(x){
#' get_point(c(-122.201399, 47.608678))
#' get_point(
#' matrix(
#' c(-122.201399, 47.608678, -122.202, 47.609),
#' ncol = 2,
#' byrow = TRUE
#' ),
#' multiple = TRUE
#' )
#' library(sf)
#' pt <- st_sf(
#' geometry = st_sfc(st_point(c(-122.201399, 47.608678)), crs = 4326)
#' )
#' get_point(pt)
get_point <- function(x, multiple = FALSE) {
UseMethod("get_point")
}


#' @rdname get_point
#' @export
get_point.default <- function(x){
stop("Points should be either numeric vectors with coordinates or sf objects")
get_point.default <- function(x, multiple = FALSE) {
stop("Points should be provided as a numeric vector/matrix or sf object!")
}


#' @rdname get_point
#' @export
get_point.numeric <- function(x){
if (length(x)!=2) {
stop("Point coordinates should be a vector with two values!")
get_point.numeric <- function(x, multiple = FALSE) {
if (length(x) != 2) {
stop("Point coordinates must be a vector with two values!")
}

sf::st_sfc(sf::st_point(x), crs = 4326)

}


#' @rdname get_point
#' @export
get_point.matrix <- function(x){
do.call(c,lapply(1:nrow(x),function(i) get_point(x[i,])))
get_point.matrix <- function(x, multiple = FALSE) {
if (ncol(x) != 2) {
stop("Matrix with coordinates must have two columns!")
}

if (!multiple && nrow(x) > 1) {
warning(
"Provided matrix contains more than one row, taking the first row",
call. = FALSE
)
x <- x[1, , drop = FALSE]
}

do.call(c, lapply(1:nrow(x), function(i) get_point(x[i, ])))
}


#' @rdname get_point
#' @export
get_point.sf <- function(x){
get_point(sf::st_geometry(x))
get_point.sf <- function(x, multiple = FALSE) {
get_point(sf::st_geometry(x), multiple = multiple)
}


#' @rdname get_point
#' @export
get_point.sfc <- function(x){

if (length(x)>1) {
warning("Provided point contains more than one feature, taking the first one",call. = FALSE)
get_point.sfc <- function(x, multiple = FALSE) {
if (!multiple && length(x) > 1) {
warning(
"Provided point contains more than one feature, taking the first one",
call. = FALSE
)
x <- x[1]
}

if (is.na(sf::st_crs(x))){
if (is.na(sf::st_crs(x))) {
sf::st_crs(x) <- 4326
}

if (sf::st_crs(x)!=4326){
x <- sf::st_transform(x,4326)
if (sf::st_crs(x) != 4326) {
x <- sf::st_transform(x, 4326)
}

x
Expand Down
Loading