|
8 | 8 | #' test conflict scenarios, and makes both data and analysis open for free use |
9 | 9 | #' by the public.* |
10 | 10 | #' |
11 | | -#' In order to access data from the ACLED API, you first must register an |
12 | | -#' an account. Note, that the ACLED API used here provides a *living database* |
| 11 | +#' In order to access data from the ACLED API, you first must register |
| 12 | +#' an account. Make sure you register with your institutional domain |
| 13 | +#' (e.g., organization, university, or company) email address rather than |
| 14 | +#' your personal email address, in order to be able to use the API, |
| 15 | +#' as explained [here](https://acleddata.com/myacled-faqs). |
| 16 | +#' Note that the ACLED API provides a *living database* |
13 | 17 | #' with single events being altered or removed altogether over time. |
14 | 18 | #' |
15 | 19 | #' @name acled |
16 | 20 | #' @param years A numeric vector specifying the years for which to make |
17 | | -#' ACLED data available (between 1997 and today). Defaults to 2000. |
18 | | -#' @param key ACLED API key obtained by registering with ACLED (see Details). |
| 21 | +#' ACLED data available (between 1997 and today). |
19 | 22 | #' @param email Email addressed used to register with ACLED (see Details). |
| 23 | +#' @param password Password used to register with ACLED (see Details). |
20 | 24 | #' @param accept_terms A logical indicating if you agree to abide by ACLED's terms |
21 | 25 | #' of use. Defaults to FALSE, thus must be manually set to TRUE. |
22 | 26 | #' @keywords resource |
|
27 | 31 | #' \doi{doi:10.1057/s41599-023-01559-4} |
28 | 32 | #' @source Armed Conflict Location & Event Data Project (ACLED). |
29 | 33 | #' @include register.R |
30 | | -#' @importFrom httr2 request req_perform resp_check_status resp_body_json |
| 34 | +#' @importFrom httr2 request req_perform resp_body_json |
31 | 35 | #' @export |
32 | 36 | get_acled <- function( |
33 | | - years = 2000, |
34 | | - key = Sys.getenv("ACLED_ACCESS_KEY"), |
35 | | - email = Sys.getenv("ACLED_ACCESS_EMAIL"), |
| 37 | + years, |
| 38 | + email = Sys.getenv("ACLED_EMAIL"), |
| 39 | + password = Sys.getenv("ACLED_PASSWORD"), |
36 | 40 | accept_terms = FALSE) { |
| 41 | + # check input arguments |
37 | 42 | if (!accept_terms) { |
38 | | - msg <- "Please read and agree to ACLED's Terms of Use here:\nhttps://acleddata.com/terms-of-use/" |
| 43 | + msg <- "Please read and agree to ACLED's Terms of Use here:\nhttps://acleddata.com/terms-and-conditions" |
39 | 44 | stop(msg) |
40 | 45 | } else { |
41 | | - msg <- "You agreed to abide to ACLED's Terms of Use (https://acleddata.com/terms-of-use/)." |
42 | | - message(msg) |
| 46 | + if (isTRUE(mapme_options()[["verbose"]])) { |
| 47 | + msg <- "You agreed to abide to ACLED's Terms of Use (https://acleddata.com/terms-and-conditions)." |
| 48 | + message(msg) |
| 49 | + } |
43 | 50 | } |
44 | | - |
45 | 51 | if (is.null(email) | email == "") { |
46 | 52 | msg <- "Please specify your email registered with ACLED." |
47 | 53 | stop(msg) |
48 | 54 | } |
49 | | - |
50 | | - if (is.null(key) | key == "") { |
51 | | - msg <- "Please specify your API key registered with ACLED." |
| 55 | + if (is.null(password) | password == "") { |
| 56 | + msg <- "Please specify your password registered with ACLED." |
52 | 57 | stop(msg) |
53 | 58 | } |
54 | | - |
55 | | - years <- check_available_years(years, 1997:2024) |
| 59 | + years <- check_available_years(years, 1997:as.integer(format(Sys.Date(), "%Y"))) |
56 | 60 |
|
57 | 61 | function(x, |
58 | 62 | name = "acled", |
59 | 63 | type = "vector", |
60 | 64 | outdir = mapme_options()[["outdir"]], |
61 | 65 | verbose = mapme_options()[["verbose"]]) { |
| 66 | + # token authorization url |
| 67 | + .token_url <- "https://acleddata.com/oauth/token" |
62 | 68 | acled_yearly <- purrr::map_chr(years, function(year) { |
63 | 69 | filename <- sprintf("acled_events_%s.gpkg", year) |
64 | | - |
| 70 | + # make full path to output file |
65 | 71 | if (is.null(outdir)) { |
66 | 72 | filename <- file.path(tempdir(), filename) |
67 | 73 | } else { |
68 | 74 | filename <- file.path(outdir, filename) |
69 | 75 | } |
70 | | - |
| 76 | + # if already available just return the file name |
71 | 77 | if (spds_exists(filename)) { |
72 | 78 | return(filename) |
73 | 79 | } |
74 | | - |
75 | | - base_url <- .prep_acled_url(key, email, year) |
| 80 | + # start building the request |
| 81 | + base_url <- "https://acleddata.com/api/acled/read" |
| 82 | + req <- httr2::request(base_url) |
| 83 | + # add year as query parameter |
| 84 | + req <- httr2::req_url_query(req, year = year) |
| 85 | + # authenticate the request |
| 86 | + req <- httr2::req_oauth_password( |
| 87 | + req, |
| 88 | + client = httr2::oauth_client("acled", .token_url), |
| 89 | + username = email, |
| 90 | + password = password |
| 91 | + ) |
| 92 | + # prepare for page iteration |
76 | 93 | next_page <- TRUE |
77 | 94 | page <- 1 |
78 | | - data <- NULL |
79 | | - |
| 95 | + data_lst <- NULL |
| 96 | + running_count <- 0L |
| 97 | + # iterate pages |
80 | 98 | while (next_page) { |
81 | | - page_url <- paste0(base_url, sprintf("&page=%s", page)) |
82 | | - |
83 | | - req <- request(page_url) |
84 | | - rsp <- req_perform(req) |
85 | | - resp_check_status(rsp) |
86 | | - cnt <- resp_body_json(rsp) |
87 | | - |
88 | | - if (cnt$status != 200) { |
89 | | - stop("ACLED API query failed with message:\n", cnt$error$message) |
90 | | - } |
91 | | - |
92 | | - if (cnt$count == 0) { |
| 99 | + req <- httr2::req_url_query(req, page = page) |
| 100 | + resp <- httr2::req_perform(req) |
| 101 | + cnt <- httr2::resp_body_json(resp, simplifyVector = TRUE, flatten = TRUE) |
| 102 | + # check if we still have some data |
| 103 | + count <- cnt$count |
| 104 | + if (count == 0L) { |
93 | 105 | next_page <- FALSE |
94 | 106 | next |
95 | 107 | } |
96 | | - |
97 | | - events <- purrr::map(cnt$data, function(y) as.data.frame(y)) |
98 | | - events <- purrr::list_rbind(events) |
99 | | - events <- st_as_sf(events, |
100 | | - coords = c("longitude", "latitude"), |
101 | | - crs = st_crs("EPSG:4326") |
102 | | - ) |
103 | | - |
| 108 | + # increment running count |
| 109 | + running_count <- running_count + count |
| 110 | + # get data as data.frame |
| 111 | + events <- cnt$data |
| 112 | + # store events data.frame in list |
104 | 113 | if (page == 1) { |
105 | | - data <- list(events) |
| 114 | + data_lst <- list(events) |
| 115 | + total_count <- cnt$total_count |
106 | 116 | } else { |
107 | | - data[[page]] <- events |
| 117 | + data_lst[[page]] <- events |
108 | 118 | } |
109 | | - |
| 119 | + # increment page number |
110 | 120 | page <- page + 1 |
111 | 121 | } |
112 | | - |
113 | | - if (is.null(data)) { |
| 122 | + # check everything OK |
| 123 | + if (running_count != total_count) { |
| 124 | + msg <- sprintf("ACLED API returned only %d events out of %d.", running_count, total_count) |
| 125 | + stop(msg) |
| 126 | + } |
| 127 | + if (is.null(data_lst)) { |
114 | 128 | stop("ACLED API returned 0 events.") |
115 | 129 | } |
116 | | - |
117 | | - data <- st_sf(tibble::as_tibble(purrr::list_rbind(data))) |
118 | | - write_sf(data, filename) |
| 130 | + # rbind list into global data.frame |
| 131 | + data <- purrr::list_rbind(data_lst) |
| 132 | + # convert to sf object |
| 133 | + data <- sf::st_as_sf(data, |
| 134 | + coords = c("longitude", "latitude"), |
| 135 | + crs = sf::st_crs("EPSG:4326")) |
| 136 | + # convert all non-geometry columns to character to be compatible with previous version |
| 137 | + # temporarily extract geometry |
| 138 | + geom <- sf::st_geometry(data) |
| 139 | + # convert non-geometry columns |
| 140 | + data_df <- sf::st_drop_geometry(data) |
| 141 | + data_df[] <- lapply(data_df, as.character) |
| 142 | + # reattach geometry |
| 143 | + data <- sf::st_sf(data_df, geom) |
| 144 | + # write the file and return file name |
| 145 | + sf::write_sf(data, filename) |
119 | 146 | return(filename) |
120 | 147 | }) |
121 | | - |
| 148 | + # make footprint |
122 | 149 | bbox <- c(xmin = -180.0, ymin = -90.0, xmax = 180.0, ymax = 90.0) |
123 | | - fps <- st_as_sfc(st_bbox(bbox, crs = "EPSG:4326")) |
124 | | - fps <- st_as_sf(rep(fps, length(acled_yearly))) |
| 150 | + fps <- sf::st_as_sfc(sf::st_bbox(bbox, crs = "EPSG:4326")) |
| 151 | + fps <- sf::st_as_sf(rep(fps, length(acled_yearly))) |
125 | 152 | fps[["source"]] <- acled_yearly |
126 | 153 | fps <- make_footprints(fps, what = "vector") |
127 | 154 | } |
128 | 155 | } |
129 | 156 |
|
130 | | -.prep_acled_url <- function(key, email, year) { |
131 | | - url <- "https://api.acleddata.com/acled/read?" |
132 | | - url <- paste0(url, "terms=accept") |
133 | | - url <- paste0(url, "&key=", key) |
134 | | - url <- paste0(url, "&email=", email) |
135 | | - url <- paste0(url, "&year=", year) |
136 | | - url |
137 | | -} |
138 | | - |
139 | 157 | register_resource( |
140 | 158 | name = "acled", |
141 | 159 | description = "Armed Conflict Location & Event Data (ACLED)", |
|
0 commit comments