R/api.R

Defines functions .parse_api_data.google_geohost .parse_api_data.google_geocode .parse_api_data.google_revgeocode .parse_api_data.baidu_convcoord .parse_api_data.ipify_geohost .parse_api_data.ipify_getip .read_api.baidu_api_geocode .read_api.gaode_api_geocode .read_api.ipinfo_api_geohost synthesize_googlemap_api embed_googlemap_api_generic synthesize_google_api_geocode synthesize_google_api_revgeocode synthesize_baidumap_api embed_baidumap_api_generic synthesize_baidu_api_geocode synthesize_baidu_api_revgeocode synthesize_baidu_api_convcoord synthesize_gaodemap_api embed_gaodemap_api_generic synthesize_gaode_api_geocode synthesize_gaode_api_revgeocode synthesize_gaode_api_convcoord synthesize_ipify_api embed_ipify_api_generic synthesize_ipify_api_geohost synthesize_ipify_api_getip synthesize_ipstack_api embed_ipstack_api_generic synthesize_ipstack_api_geohost synthesize_ipinfo_api embed_ipinfo_api_generic synthesize_ipinfo_api_geohost

Documented in synthesize_baidu_api_convcoord synthesize_baidu_api_geocode synthesize_baidu_api_revgeocode synthesize_baidumap_api synthesize_gaode_api_convcoord synthesize_gaode_api_geocode synthesize_gaode_api_revgeocode synthesize_gaodemap_api synthesize_google_api_geocode synthesize_google_api_revgeocode synthesize_googlemap_api synthesize_ipify_api synthesize_ipify_api_geohost synthesize_ipify_api_getip synthesize_ipinfo_api synthesize_ipinfo_api_geohost synthesize_ipstack_api synthesize_ipstack_api_geohost

# ======================Read API=======================
# use aseskit::parse_api_data

#' @export
.parse_api_data.google_geohost <- function(x, ...)
    parse_geohost_result(x, ...)

#' @export
.parse_api_data.baidu_geohost <- .parse_api_data.google_geohost

#' @export
.parse_api_data.gaode_geohost <- .parse_api_data.google_geohost

#' @export
.parse_api_data.google_geocode <- function(x, ...)
    parse_geocode_result(x, ...)

#' @export
.parse_api_data.baidu_geocode <- .parse_api_data.google_geocode

#' @export
.parse_api_data.gaode_geocode <- .parse_api_data.google_geocode

#' @export
.parse_api_data.google_revgeocode <- function(x, ...)
    parse_revgeocode_result(x, ...)

#' @export
.parse_api_data.baidu_revgeocode <- .parse_api_data.google_revgeocode

#' @export
.parse_api_data.gaode_revgeocode <- .parse_api_data.google_revgeocode

# .parse_api_data.google_convcoord <- function(x, ...)
#     parse_convcoord_result(x, ...)

#' @export
.parse_api_data.baidu_convcoord <- function(x, ...)
     parse_convcoord_result(x, ...)

#' @export
.parse_api_data.gaode_convcoord <- .parse_api_data.baidu_convcoord

#' @export
.parse_api_data.ipify_geohost <- function(x, ...)
    parse_geohost_result(x, ...)

#' @export
.parse_api_data.ipinfo_geohost <- .parse_api_data.ipify_geohost

#' @export
.parse_api_data.ipstack_geohost <- .parse_api_data.ipify_geohost

#' @export
.parse_api_data.ipify_getip <- function(x, ...)
    parse_getip_result(x, ...)

#' @export
.parse_api_data.default <- aseskit:::.parse_api_data.default

# ==============Read API for data============
# use aseskit::read_api

#' @export
.read_api.default <- aseskit:::.read_api.default

#' @export
.read_api.google_api_geocode <- .read_api.default

#' @export
.read_api.google_api_revgeocode <- .read_api.google_api_geocode

#' @export
.read_api.baidu_api_geocode <- function(
    api_url, use_curl=FALSE, parse_json=TRUE, time=0, encoding='UTF-8', 
    url_encode=TRUE, name_out=NULL, ...){
    # use_curl = FALSE, and encoding='UTF-8'
    aseskit:::extract_api_data(
        api_url=api_url, use_curl=use_curl, parse_json=TRUE, time=time, 
        encoding=encoding, url_encode=url_encode, name_out=name_out, ...)
}

#' @export
.read_api.baidu_api_revgeocode <- .read_api.baidu_api_geocode

#' @export
.read_api.baidu_api_convcoord <- .read_api.baidu_api_geocode

#' @export
.read_api.gaode_api_geocode <- function(
    api_url, use_curl=FALSE, parse_json=TRUE, time=0, encoding='UTF-8', 
    url_encode=TRUE, name_out=NULL, ...){
    # use_curl = FALSE, and encoding='UTF-8'
    aseskit:::extract_api_data(
        api_url=api_url, use_curl=use_curl, parse_json=TRUE, time=time, 
        encoding=encoding, url_encode=url_encode, name_out=name_out, ...)
}

#' @export
.read_api.gaode_api_revgeocode <- .read_api.gaode_api_geocode

#' @export
.read_api.gaode_api_convcoord <- .read_api.gaode_api_geocode

#' @export
.read_api.ipinfo_api_geohost <- function(
    api_url, use_curl=FALSE, parse_json=TRUE, time=0, encoding='unknown', 
    url_encode=TRUE, name_out=NULL, ...){
    # use_curl = FALSE, and encoding='UTF-8'
    aseskit:::extract_api_data(
        api_url=api_url, use_curl=use_curl, parse_json=TRUE, time=time, 
        encoding=encoding, url_encode=url_encode, name_out=name_out, ...)
}

#' @export
.read_api.ipstack_api_geohost <- .read_api.ipinfo_api_geohost

#' @export
.read_api.ipify_api_geohost <- .read_api.ipinfo_api_geohost

#' @export
.read_api.ipify_api_getip <- .read_api.ipinfo_api_geohost

# ================Synthesize API URLs======================
# use aseskit::synthesize_apis()

## -------------Google api-----------------

#' Synthesize googlemap API
#' 
#' Working function for \code{\link[aseskit]{synthesize_api}()}. It will call
#' lower functions \code{\link{synthesize_google_api_geocode}()} or 
#' \code{\link{synthesize_google_api_revgeocode}()}.
#' @param url_body character, the leading argument in the API URL.
#' @param api character, 'geocode' or 'revgeocode'.
#' @param use_curl logical, whether use \code{curl}(). Default TRUE.
#' @param ... other arguments to pass to the function, e.g., \code{language}, 
#' \code{client}, \code{signature}, \code{key}
#'
#' @note When \code{key} is omitted or set NULL, the function will search for it
#' in the cache. If nothing is found, a GUI wizard will be launched for you to 
#' input the API key. If you want to bypass API key, set \code{key} = NA.
#' 
#' @return A \code{url} object for \code{\link{get_api_data}()} use.
#' @export
#'
#' @references Googlemaps API documentation: 
#' \url{https://developers.google.com/maps/documentation}
#' @examples
#' \dontrun{
#' synthesize_googlemap_api("<Address>", key="<YOUR GOOGLEMAP KEY>")
#' }
synthesize_googlemap_api <- function(
    url_body, api=c('geocode', 'revgeocode'), use_curl=TRUE, ...){
    
    api <- match.arg(api)
    stopifnot(is.character(url_body))
    stopifnot(is.logical(use_curl))
    
    fun_dict <- list(geocode    = synthesize_google_api_geocode,
                     revgeocode = synthesize_google_api_revgeocode
                )
    fun <- fun_dict[[api]]
    fun(url_body=url_body, use_curl=use_curl, ...)
}

#' @importFrom glue glue
#' @importFrom aseskit iif ifnull aline
embed_googlemap_api_generic <- function(
    url_char, language=NULL, client='', signature='', key=NULL, ...){
    # generic function to add auth in API
    stopifnot(is.character(url_char))
    len <- length(url_char)
    
    if (! is.null(language)){
        valid_lang <- c(
            "ar", "be", "lv", "bg", "mk", "bn", "ml", "ca", "mr", "cs", "my",
            "da", "nl", "de", "no", "el", "pa", "en", "pl", "en-Au", "pt", 
            "en-GB", "pt-BR", "es", "pt-PT", "eu", "ro", "fa", "ru", "fi", "sk",
            "fil", "sl", "fr", "sq", "gl", "sr", "gu", "sv", "hi", "ta", "hr", 
            "te", "hu", "th", "id", "tl", "it", "tr", "iw", "uk", "ja", "uz", 
            "kk", "vi", "kn", "zh-CN","ko", "zh-TW", "ky")
        lang <- match.arg(language, valid_lang, several.ok=TRUE)
        url_char <- glue::glue('{url_char}&language={aline(lang, len)}')
    }
    # add authorization part
    if (nchar(ifnull(client, '')) > 0 && nchar(ifnull(signature, '')) > 0){
        url_char <- glue::glue('{url_char}&client={client}&signature={signature}')
    }else if (is.null(key)) {
        url_char <- glue::glue('{url_char}&key={getApiKey("google")}')
    }else if (is.na(key)){
        url_char <- url_char
    }else if (is.character(key) && nchar(key) > 0) {
        url_char <- glue::glue('{url_char}&key={key}')
    }else{
        stop('either client and signature, or a valid API key must be provided ',
             'for Google API!')
    }
    return(url_char)
}

#' @param region character, for geocoding. The region code, specified as a ccTLD 
#' ("top-level domain") two-character value. This parameter will only influence,
#'  not fully restrict, results from the geocoder.
#' @param components character, for geocoding. A components filter with elements 
#' separated by a pipe (|). The components filter is required if the request doesn't 
#' include an address. Each element in the components filter consists of a 
#' \code{component:value} pair, and fully restricts the results from the geocoder. 
#'
#' @importFrom aseskit iif ifnull check_curl aline
#' @importFrom glue glue
#' @export
#' @rdname synthesize_googlemap_api
synthesize_google_api_geocode <- function(
    url_body, region=NULL, components=NULL, use_curl=TRUE, ...){
    
    # check args
    dots <- list(...)
    stopifnot(is.null(region) || is.character(region))
    stopifnot(is.null(components) || is.character(components))
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    len <- length(url_body)
    
    # Choose a google api_url
    # https://maps.googleapis.com/maps/api/geocode/json?address=ADDRESS&key=API_KEY 
    # for outside China
    # https://ditu.google.cn/maps/api/geocode/json?address=ADDRESS&key=API_KEY 
    # for inside China
    cname <- try(ip.country(), silent=TRUE)
    api_url <- if (cname == "CN") 'https://ditu.google.cn/maps/api/' else
        api_url <- 'https://maps.googleapis.com/maps/api/'
    
    # start
    api_url <- glue::glue('{api_url}geocode/json')
    use_curl <- aseskit::check_curl(api_url, "Google Maps Geocoding", use_curl)
    
    url_char <- glue::glue('{api_url}?address={url_body}')
    
    if (! is.null(region)){
        region <- aseskit::aline(region, length(url_body))
        valid_reg <- which(nchar(region) > 0)
        if (length(valid_reg) > 0) url_char[valid_reg] <- glue(
            '{url_char[valid_reg]}&region={region[valid_reg]}')
    }
    if (! is.null(components)){
        components <- aseskit::aline(components, length(url_body))
        valid_cmp <- which(nchar(components) > 0)
        if (length(valid_cmp) > 0) url_char[valid_cmp] <- glue(
            '{url_char[valid_cmp]}&components={components[valid_cmp]}')
    }
    url_char <- embed_googlemap_api_generic(url_char, ...)
    return(mapply(structure, url_char, name_out=name_out, MoreArgs=list(
        class=c('google_api_geocode', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}


#' @param result_type character, for regeocoding. A filter of one or more address 
#' types, separated by a pipe (|). If the parameter contains multiple address types,
#'  the API returns all addresses that match any of the types. A note about 
#'  processing: The result_type parameter does not restrict the search to the 
#'  specified address type(s). Rather, the result_type acts as a post-search 
#'  filter: the API fetches all results for the specified latlng, then discards 
#'  those results that do not match the specified address type(s). Note: 
#'  This parameter is available only for requests that include an API key or 
#'  a client ID. The following values are supported: \cr
#'  'street_address', 'route', 'intersection', 'political', 'country', 
#'  'administrative_area_level_1', 'administrative_area_level_2',
#'  'administrative_area_level_3', 'administrative_area_level_4', 
#'  'administrative_area_level_5', 'colloquial_area', 'locality', 'sublocality', 
#'  'neighborhood', 'premise', 'subpremise', 'postal_code', 'natural_feature', 
#'  'airport', 'park', 'point_of_interest.'
#' @param location_type character, for revgeocoding. A filter of one or more 
#' location types, separated by a pipe (|). If the parameter contains multiple 
#' location types, the API returns all addresses that match any of the types. 
#' A note about processing: The location_type parameter does not restrict the 
#' search to the specified location type(s). Rather, the location_type acts as 
#' a post-search filter: the API fetches all results for the specified latlng, 
#' then discards those results that do not match the specified location type(s). 
#' Note: This parameter is available only for requests that include an API key 
#' or a client ID. The following values are supported: \cr
#' 'ROOFTOP', 'RANGE_INTERPOLATED', 'GEOMETRIC_CENTER', 'APPROXIMATE.'
#'
#' @importFrom glue glue
#' @importFrom aseskit iif ifnull check_curl aline
#' @export
#' @rdname synthesize_googlemap_api
synthesize_google_api_revgeocode <- function(
    url_body, result_type=NULL, location_type=NULL, use_curl=TRUE, 
    ...){
    
    # check args
    dots <- list(...)
    stopifnot(is.null(result_type) || is.character(result_type))
    stopifnot(is.null(location_type) || is.character(location_type))
    ## url_body valid? if invalid, url = NA_character_
    stopifnot(is.character(url_body))
    valid_url_body <- grepl("^\\-?\\d+?\\.?\\d*?,\\-?\\d+?\\.?\\d*$", url_body)
    if (! all(valid_url_body))
        warning('latlng must be in the format of "lat,lon". Coordinates #',
                paste(which(! valid_url_body), collapse=', '), 
                ' are not correctly formatted.')
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    len <- length(url_body)
    
    # Choose a google api_url
    # https://maps.googleapis.com/maps/api/geocode/json?address=ADDRESS&key=API_KEY 
    # for outside China
    # https://ditu.google.cn/maps/api/geocode/json?address=ADDRESS&key=API_KEY 
    # for inside China
    cname <- try(ip.country(), silent=TRUE)
    api_url <- if (cname == "CN") 'https://ditu.google.cn/maps/api/' else
        api_url <- 'https://maps.googleapis.com/maps/api/'
    
    # start
    api_url <- glue::glue('{api_url}geocode/json')
    use_curl <- aseskit::check_curl(api_url, "Google Maps Geocoding", use_curl)
    url_char <- glue::glue('{api_url}?latlng={url_body}')
    
    if (length(result_type) > 0){
        result_type_dict <- c(
            'street_address', 'route', 'intersection', 'political', 'country',
            paste('administrative_area_level_', 1:5, sep=''), 'colloquial_area',
            'locality', 'sublocality', paste('sublocality_level_', 1:5, sep=''),
            'neighborhood', 'premise', 'subpremise', 'postal_code', 'natural_feature',
            'airport', 'park', 'point_of_interest')
        result_type <- aseskit::aline(result_type, len)
        valid_rslt_type <- vapply(
            strsplit(result_type, '\\|'), function(rslt_type){
                all(rslt_type %in% result_type_dict)}, FUN.VALUE=logical(1L))
        if (! all(valid_rslt_type)) 
            warning('result_type must be in ', paste(result_type_dict, collapse=', '),
                    ' or these valid values concatenated with pipe (|).')
        if (any(valid_rslt_type))
            url_char[valid_rslt_type] <- glue::glue(
                '{url_char[valid_rslt_type]}&result_type={result_type[valid_rslt_type]}')
    }
    if (length(location_type) > 0){
        location_type_dict <- c(
            'ROOFTOP', 'RANGE_INTERPOLATED', 'GEOMETRIC_CENTER', 'APPROXIMATE')
        location_type <- aseskit::aline(location_type, len)
        valid_loc_type <- vapply(
            strsplit(location_type, '\\|'), function(loc_type){
                all(loc_type %in% location_type_dict)}, FUN.VALUE=logical(1L))
        if (! all(valid_loc_type))
            warning('location_type must be in ', paste(location_type_dict, collapse=', '),
                    ' or these valid values concatenated with pipe (|).')
        if (any(valid_loc_type))
            url_char[valid_loc_type] <- glue::glue(
                '{url_char[valid_loc_type]}&location_type={location_type[valid_loc_type]}')
    }
    url_char <- embed_googlemap_api_generic(url_char, ...)
    url_char[! valid_url_body] <- NA_character_
    return(mapply(structure, url_char, name_out=url_body, MoreArgs=list(
        class=c('google_api_revgeocode', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}

# --------------baidu api-----------------------

#' Synthesize baidumap API
#' 
#' Working function for \code{\link[aseskit]{synthesize_api}()}. It will call
#' lower functions \code{\link{synthesize_baidu_api_geocode}()} or 
#' \code{\link{synthesize_baidu_api_revgeocode}()}
#' or \code{synthesize_baidu_api_convcoord}.
#' @param url_body character, the leading argument in the API URL.
#' @param api character, 'geocode', 'revgeocode', 'convcoord'.
#' @param use_curl logical, whether use \code{curl}(). Default TRUE.
#' @param ... other arguments to pass to the function, e.g., \code{key}
#'
#' @note When \code{key} is omitted or set NULL, the function will search for it
#' in the cache. If nothing is found, a GUI wizard will be launched for you to 
#' input the API key. If you want to bypass API key, set \code{key} = NA.
#' 
#' @return A \code{url} object for \code{\link{get_api_data}()} use.
#' @export
#'
#' @references Baidu map API documentation: 
#' \url{http://lbsyun.baidu.com/index.php?title=webapi/guide}
#' @examples
#' \dontrun{
#' synthesize_baidumap_api("<Address>", key="<YOUR BAIDUMAP KEY>")
#' }
synthesize_baidumap_api <- function(
    url_body, api=c('geocode', 'revgeocode', 'convcoord'), use_curl=FALSE, ...){
    
    api <- match.arg(api)
    stopifnot(is.character(url_body))
    
    fun_dict <- list(geocode    = synthesize_baidu_api_geocode,
                     revgeocode = synthesize_baidu_api_revgeocode,
                     convcoord  = synthesize_baidu_api_convcoord
    )
    fun <- fun_dict[[api]]
    fun(url_body=url_body, use_curl=use_curl, ...)
}

#' @importFrom glue glue
#' @importFrom aseskit getApiKey iif
embed_baidumap_api_generic <- function(url_char, key=NULL, ...){
    stopifnot(is.character(url_char))
    if (is.null(key)){
        key <- aseskit::getApiKey("baidu")
        url_char <- glue::glue('{url_char}&ak={key}')
    }else if (is.na(key)){
        
    }else if (is.character(key) && nchar(key) > 0){
        url_char <- glue::glue('{url_char}&ak={key}')
    }else{
        stop("Please use a valid baidu API key.")
    }
    return(url_char)
}

#' @param ics character, for geocoding. 'BD-09', 'GCJ-02' or 'WGS-84'.
#' @param ocs character, for geocoding. 'BD-09', 'GCJ-02' or 'WGS-84'
#' @param city character, for geocoding. A valid Chinese city name to limit the search range.
#'
#' @importFrom aseskit check_curl aline iif ifnull
#' @importFrom glue glue
#' @export
#' @rdname synthesize_baidumap_api
synthesize_baidu_api_geocode <- function(
    url_body, ics=c('BD-09', 'GCJ-02', 'WGS-84'), 
    ocs=c('BD-09', 'GCJ-02', 'WGS-84'), city=NULL, use_curl=FALSE, ...){
    # check args
    dots <- list(...)
    stopifnot(is.null(city) || is.character(city))
    ics <- if (! is.null(ics)) match.arg(ics, several.ok=TRUE) else 'BD-09'
    ocs <- if (! is.null(ocs)) match.arg(ocs, several.ok=TRUE) else 'BD-09'
    
    dots <- list(...)
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    len <- length(url_body)
    ocs <- aseskit::aline(ocs, len)
    ics <- aseskit::aline(ics, len)
    
    # start
    api_url <- 'http://api.map.baidu.com/geocoder/v2/'
    use_curl <- aseskit::check_curl(api_url, "Baidu Map Geocoding", use_curl)
    
    url_char <- glue::glue('{api_url}?address={url_body}&output=json')
    ocs_gcj <- which(ocs == "GCJ-02")
    if (length(ocs_gcj) > 0)
        url_char[ocs_gcj] <- glue::glue('{url_char[ocs_gcj]}&ret_coordtype=gcj02ll')
    if (! is.null(city)) {
        city <- aseskit::aline(city, len)
        valid_city <- city[nchar(city) > 0]
        url_char[valid_city] <- glue::glue(
            '{url_char[valid_city]}&city={city[valid_city]}')
    }
    url_char <- embed_baidumap_api_generic(url_char, ...)
    return(mapply(structure, url_char, name_out=url_body, MoreArgs=list(
        class=c('baidu_api_geocode', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}


#' @param pois numeric 1 or 0, for revgeocode. whether callback surrounding pois
#'  (1=Yes, 0=No). You will need to apply for special service access when calling 
#'  pois outside China. Default NULL (0).
#' @param radius numeric 0-1000, for revgeocode. The radius (m) for calling back pois.
#'  Default NULL (indicating 1000).
#' @param extensions_road logical, for revgeocode. Callback 3 streets near the spot 
#'  when set to \code{TRUE}. Default FALSE.
#' @param extensions_town logical, for revgeocode. Callback info of town when set 
#'  to \code{TRUE}. Default FALSE.
#' @param language character, for revgeocode. Valid language: "local" or "el", "gu", 
#' "en", "vi", "ca", "it", "iw", "sv", "eu", "ar", "cs", "gl", "id", "es", "ru", 
#' "sr", "nl", "pt", "tr", "tl", "lv", "lt", "th", "ro", "fil", "ta", "fr", "bg", 
#' "hr", "bn", "de", "hu", "fa", "hi", "fi", "da","ja", "te", "ml", "ko", "kn", 
#' "sk", "pl", "uk", "sl", "mr", "local", "en-GB", "en-AU", "zh-TW", "pt-BR", 
#' "pt-PT", "zh-CN". Default NULL.
#' @param language_auto numeric 1 or 0, for revgeocode. whether auto fill the 
#'  admin area (1=Yes, 0=No). You will need to apply for special service access
#'  when using multi-language functionality. Default NULL (ignored).
#' @param latest_admin numeric 1 or 0, for revgeocode. whether get the latest 
#'  administrative area info (1=Yes, 0=No). Default 0.
#'
#' @importFrom glue glue
#' @importFrom aseskit iif ifnull check_curl aline
#' @export
#' @rdname synthesize_baidumap_api
synthesize_baidu_api_revgeocode <- function(
    url_body, ics=c('BD-09', 'GCJ-02', 'WGS-84'), 
    ocs=c('BD-09', 'GCJ-02', 'WGS-84'), pois=NULL, radius=NULL, 
    extensions_road=FALSE, extensions_town=FALSE, language=NULL, 
    language_auto=NULL, latest_admin=0, use_curl=FALSE, ...
){
    # check args
    stopifnot(is.character(url_body))
    # valid url? if invalid, url = NA_character_
    valid_url_body <- grepl("^\\-?\\d+?\\.?\\d*?,\\-?\\d+?\\.?\\d*$", url_body)
    if (! all(valid_url_body))
        warning('latlng must be in the format of "lat,lon". Coordinates #',
                paste(which(! valid_url_body), collapse=', '), 
                'are not correctly formatted.')
    
    stopifnot(is.null(pois) || is.numeric(pois))
    stopifnot(is.null(radius) || is.numeric(radius))
    stopifnot(is.logical(extensions_road))
    stopifnot(is.logical(extensions_town))
    stopifnot(is.null(language) || is.character(language))
    stopifnot(is.null(language_auto) || is.numeric(language_auto))
    stopifnot(is.numeric(latest_admin))
    ics <- if (! is.null(ics)) match.arg(ics, several.ok=TRUE) else 'BD-09'
    ocs <- if (! is.null(ocs)) match.arg(ocs, several.ok=TRUE) else 'BD-09'
    
    dots <- list(...)
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    len <- length(url_body)
    ocs <- aseskit::aline(ocs, len)
    ics <- aseskit::aline(ics, len)
    
    # start
    api_url <- 'http://api.map.baidu.com/geocoder/v2/'
    use_curl <- aseskit::check_curl(api_url, "Baidu Map RevGeocoding", use_curl)
    url_char <- glue::glue('{api_url}?location={url_body}&output=json')
    
    # if ics == 'BD-09', coordtype can be blank
    ics_gcj <- which(ics == "GCJ-02")
    ics_wgs <- which(ics == "WGS-84")
    url_char[ics_gcj] <- glue::glue('{url_char[ics_gcj]}&coordtype=gcj02ll')
    url_char[ics_wgs] <- glue::glue('{url_char[ics_wgs]}&coordtype=wgs84ll')
    ocs_gcj <- which(ocs == 'GCJ-02')
    url_char[ocs_gcj] <- glue::glue('{url_char[ocs_gcj]}&ret_coordtype=gcj02ll')
    
    if (! is.null(pois)) {
        pois <- aseskit::aline(pois, len)
        valid_pois <- which(pois %in% 0:1)
        if (length(valid_pois) > 0) url_char[valid_pois] <- glue(
            '{url_char[valid_pois]}&pois={pois[valid_pois]}')
    }
    if (! is.null(radius)){
        radius <- aseskit::aline(radius, len)
        valid_radius <- which(radius > 0)
        if (length(valid_radius) > 0) url_char[valid_radius] <- glue(
            '{url_char[valid_radius]}&radius={radius[valid_radius]}')
    }
    ext_road <- aseskit::aline(extensions_road, len)
    if (any(ext_road))
        url_char[ext_road] <- glue::glue(
            '{url_char[ext_road]}&extensions_road=true')
    ext_town <- aseskit::aline(extensions_town, len)
    if (any(ext_town))
        url_char[ext_town] <- glue::glue(
            '{url_char[ext_town]}&extensions_town=true')
    if (! is.null(language)){
        valid_lang <- c(
            "el", "gu", "en", "vi", "ca", "it", "iw", "sv", "eu", "ar", "cs",
            "gl", "id", "es", "ru", "sr", "nl", "pt", "tr", "tl", "lv", "lt", 
            "th", "ro", "fil", "ta", "fr", "bg", "hr", "bn", "de", "hu", "fa", 
            "hi", "fi", "da","ja", "te", "ml", "ko", "kn", "sk", "pl", "uk", 
            "sl", "mr", "local", "en-GB", "en-AU", "zh-TW", "pt-BR", "pt-PT", 
            "zh-CN")
        language <- match.arg(language, valid_lang, several.ok=TRUE)
        url_char <- glue::glue('{url_char}&language={aline(language, len)}')
    }
    if (! is.null(language_auto)) if (any(language_auto %in% 0:1)){
        language_auto <- aseskit::aline(language_auto, len)
        valid_lang_auto <- which(language_auto %in% 0:1)
        url_char[valid_lang_auto] <- glue::glue(
            '{url_char[valid_lang_auto]}&language_auto=',
            '{language_auto[valid_lang_auto]}')
    }
    if (any(latest_admin == 1)){
        latest_admin <- aseskit::aline(latest_admin, len)
        valid_ladmin <- which(latest_admin == 1)
        url_char[valid_ladmin] <- glue::glue(
            '{url_char[valid_ladmin]}&latest_admin=',
            '{latest_admin[valid_ladmin]}')
    }
    url_char <- embed_baidumap_api_generic(url_char, ...)
    url_char[! valid_url_body] <- NA_character_
    return(mapply(structure, url_char, name_out=url_body, MoreArgs=list(
        class=c('baidu_api_revgeocode', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}

#' @param coord_from character, for conv coord. 'BD-09', 'GCJ-02', or 'WGS-84'
#' @param coord_to character, for conv coord. 'BD-09', 'GCJ-02', or 'WGS-84'
#'
#' @importFrom glue glue
#' @importFrom aseskit iif ifnull check_curl
#' @export
#' @rdname synthesize_baidumap_api
synthesize_baidu_api_convcoord <- function(
    url_body, coord_from=c('BD-09', 'GCJ-02', 'WGS-84'),
    coord_to=c('BD-09', 'GCJ-02', 'WGS-84'), use_curl=FALSE,
...){
    # check args
    coordsys_code <- c(`WGS-84`=1, `GCJ-02`=3, `BD-09`=5)
    coord_from <- match.arg(coord_from)
    coord_from <- coordsys_code[coord_from]
    coord_to <- match.arg(coord_to)
    coord_to <- coordsys_code[coord_to]
    dots <- list(...)
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    # valid url? if invalid, replace with 999,999
    url_body <- unlist(strsplit(url_body, ';'))
    valid_url_body <- grepl("^\\-?\\d+?\\.?\\d*?,\\-?\\d+?\\.?\\d*$", url_body)
    if (! all(valid_url_body)){
        warning('latlng must be in the format of "lon,lat;lon,lat". Coordinates #',
                paste(which(! valid_url_body), collapse=', '), 
                'are not correctly formatted.')
        url_body[! valid_url_body] <- '999,999'
    }
    url_body <- paste(url_body, collapse=';')

    # start
    api_url <- 'http://api.map.baidu.com/geoconv/v1/'
    use_curl <- aseskit::check_curl(api_url, "Baidu Coords Convert", use_curl)
    url_char <- glue::glue('{api_url}?coords={url_body}&from={coord_from}',
                           '&to={coord_to}')
    url_char <- embed_baidumap_api_generic(url_char, ...)
    return(mapply(structure, url_char, name_out=url_body, MoreArgs=list(
        class=c('baidu_api_convcoord', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}

# -------------- Gaode api--------------------------
#' Synthesize gaode map API
#' 
#' Working function for \code{\link[aseskit]{synthesize_api}()}. It will call
#' lower functions \code{\link{synthesize_gaode_api_geocode}()} or 
#' \code{\link{synthesize_gaode_api_revgeocode}()}
#' or \code{\link{synthesize_gaode_api_convcoord}()}.
#' @param url_body character, the leading argument in the API URL. If you want to
#' use batch mode, you can synthesize them with pipe "|" (no more than 10 per batch).
#' E.g., "beijing|tianjin|shanghai".
#' @param api character, 'geocode', 'revgeocode', 'convcoord'.
#' @param use_curl logical, whether use \code{curl}(). Default TRUE.
#' @param ... other arguments to pass to the function, e.g., \code{key}
#'
#' @note When \code{key} is omitted or set NULL, the function will search for it
#' in the cache. If nothing is found, a GUI wizard will be launched for you to 
#' input the API key. If you want to bypass API key, set \code{key} = NA.
#' 
#' @return A \code{url} object for \code{\link{get_api_data}()} use.
#' @export
#'
#' @references Gaode map API documentation: 
#' \url{https://lbs.amap.com/api/webservice/guide/api}
#' @examples
#' \dontrun{
#' synthesize_gaodemap_api("<Address>", key="<YOUR GAODEMAP KEY>")
#' }
synthesize_gaodemap_api <- function(
    url_body, api=c('geocode', 'revgeocode', 'convcoord'), use_curl=FALSE, ...){
    
    api <- match.arg(api)
    stopifnot(is.character(url_body))
    
    fun_dict <- list(geocode    = synthesize_gaode_api_geocode,
                     revgeocode = synthesize_gaode_api_revgeocode,
                     convcoord  = synthesize_gaode_api_convcoord
    )
    fun <- fun_dict[[api]]
    fun(url_body=url_body, use_curl=use_curl, ...)
}

#' @importFrom glue glue
#' @importFrom aseskit getApiKey iif ifnull
embed_gaodemap_api_generic <- function(
    url_char, key=NULL, sig=NULL, ...){
    
    stopifnot(is.character(url_char))
    
    if (is.null(key)){
        key <- aseskit::getApiKey("gaode")
        url_char <- glue::glue('{url_char}&key={key}')
    }else if (is.na(key)){
        
    }else if (is.character(key) && nchar(key) > 0){
        url_char <- glue::glue('{url_char}&key={key}')
    }else{
        stop("Please use a valid gaode map API key.")
    }
    
    if (nchar(aseskit::ifnull(sig, '')) > 0) 
        url_char <- glue::glue('{url_char}&sig={sig}')
    
    return(url_char)
}

#' @param batch logical, for geocoding, If use batch inquery mode. Default FALSE.
#' @param city character, for geocoding. A valid Chinese city name to limit results.
#'
#' @importFrom aseskit check_curl aline iif ifnull
#' @importFrom glue glue
#' @export
#' @rdname synthesize_gaodemap_api
synthesize_gaode_api_geocode <- function(
    url_body, batch=FALSE, city=NULL, use_curl=FALSE, ...){

    # check args
    dots <- list(...)
    stopifnot(is.logical(batch))
    stopifnot(is.null(city) || is.character(city))
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    len <- length(url_body)
    batch <- aseskit::aline(batch, length(url_body))
    
    # start
    api_url <- 'https://restapi.amap.com/v3/geocode/geo'
    use_curl <- aseskit::check_curl(api_url, "Gaode Map (Amap) Geocoding", use_curl)
    
    url_char <- glue::glue('{api_url}?address={url_body}&output=JSON')
    if (! is.null(city)) 
        url_char <- glue::glue('{url_char}&city={aline(city, len)}')
    if (any(batch)) 
        url_char[batch] <- glue::glue('{url_char[batch]}&batch=true')
    
    url_char <- embed_gaodemap_api_generic(url_char, ...)
    return(mapply(structure, enc2utf8(url_char), name_out=name_out, MoreArgs=list(
        class=c('gaode_api_geocode', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}

#' @param extensions character, 'all' or 'base', for revgeocode. Return basic 
#'  info ('base') or base+additional info ('all'). Default "all".
#' @param poitype character, for revgeocode. One or more POI type codes (separated 
#'  by a pipe '|'). The valid POI type codes refers to 
#'  \url{https://lbs.amap.com/api/webservice/download}. 
#'  It will not take effect when applying batch mode (multiple \code{latlng}s 
#'  are provided). Only effective when \code{extensions=="all"}. Default NULL.
#' @param radius numeric (0-3000), for revgeocode. The radius (m) for calling 
#'  back pois. Default NULL (1000).
#' @param roadlevel numeric 0 or 1, for revgeocode. Return all the roads (0) or 
#'  main roads only (1)
#' @param homeorcorp numeric 0-2, for revgeocode. Order the callback pois, \itemize{
#'  \item 0: do not intervene the order of the pois. \cr
#'  \item 1: home-related pois are prioritized. \cr
#'  \item 2: coorporate-related pois are prioritized.
#'  } Default NULL (0).
#'
#' @importFrom glue glue
#' @importFrom aseskit iif ifnull check_curl aline
#' @export
#' @rdname synthesize_gaodemap_api
#' 
synthesize_gaode_api_revgeocode <- function(
    url_body, batch=FALSE, extensions=c('all', 'base'), poitype=NULL, radius=NULL, 
    roadlevel=NULL, homeorcorp=NULL, use_curl=FALSE, ...){
    # check args
    dots <- list(...)
    if (!is.null(extensions)) 
        extensions <- match.arg(extensions, several.ok=TRUE)
    stopifnot(is.character(url_body))
    # url_body valid? If invalid, replace it with 999,999
    url_body <- unlist(strsplit(url_body, '\\|'))
    valid_url_body <- grepl("^\\-?\\d+?\\.?\\d*?,\\-?\\d+?\\.?\\d*$", url_body)
    if (! all(valid_url_body)){
        warning('latlng must be in the format of "lon,lat;lon,lat". Coordinates #',
                paste(which(! valid_url_body), collapse=', '), 
                'are not correctly formatted.')
        url_body[! valid_url_body] <- '999,999'
    }
    url_body <- paste(url_body, collapse='|')
    stopifnot(is.null(poitype) || is.character(poitype))
    stopifnot(is.null(radius) || is.numeric(radius))
    stopifnot(is.null(roadlevel) || is.numeric(roadlevel))
    stopifnot(is.null(homeorcorp) || is.numeric(homeorcorp))
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    # start 
    api_url <- 'https://restapi.amap.com/v3/geocode/regeo'
    use_curl <- aseskit::check_curl(api_url, "Gaode Map (Amap) RevGeocoding", use_curl)
    url_char <- glue::glue('{api_url}?location={url_body}&output=JSON')
    if (any(batch)) 
        url_char[batch] <- glue::glue('{url_char[batch]}&batch=true')
    if (!is.null(extensions)) if (any(extensions == 'all')){
        ext_all <- which(extensions == 'all')
        url_char[ext_all] <- glue::glue('{url_char[ext_all]}&extensions=all')
        if (! is.null(poitype)){
            valid_poitype <- grepl('^(\\d{6}\\|)*\\d{6}$', poitype)
            if (! (all(valid_poitype))) {
                warning("poitype must be valid POI TYPECODEs concatenated with pipe (|)',
                        '(e.g, '001010' or '001010|001020').")
                poitype <- poitype[valid_poitype]
                # Note: when batch is TRUE, poitype not effective
                url_char[valid_poitype] <- glue(
                    '{url_char[valid_poitype]}&poitype=',
                    '{poitype[valid_poitype]}')
            }
        }
        if (! is.null(radius)) {
            radius <- aseskit::aline(radius, len)
            valid_radius <- which(radius > 0)
            if (length(valid_radius) > 0) url_char[valid_radius] <- glue::glue(
                '{url_char[valid_radius]}&radius={radius[valid_radius]}')
        }
        if (! is.null(roadlevel)) {
            roadlevel <- aseskit::aline(roadlevel, len)
            valid_roadlvl <- which(roadlevel %in% 0:1)
            if (length(vald_roadlvl)) url_char[valid_roadlvl] <- glue::glue(
                '{url_char[valid_roadlvl]}&roadlevel={roadlevel[valid_roadlvl]}')
        }
        if (! is.null(homeorcorp)){
            homeorcorp <- aseskit::aline(homeorcorp, len)
            valid_hoc <- which(homeorcorp %in% 0:2)
            if (length(valid_hoc) > 0) url_char[valid_hoc] <- glue::glue(
                '{url_char[valid_hoc]}&homeorcorp={homeorcorp[valid_hoc]}')
        }
    }
    url_char <- embed_gaodemap_api_generic(url_char, ...)
    
    return(mapply(structure, enc2utf8(url_char), name_out=name_out, MoreArgs=list(
        class=c('gaode_api_revgeocode', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}


#' @param coord_from character, for conv coord. 'BD-09', 'GCJ-02', or 'WGS-84'. 
#' Default 'GCJ-02'.
#' @param coord_to character, for conv coord. 'BD-09', 'GCJ-02', or 'WGS-84'.
#' Only 'GCJ-02' is now supported.
#'
#' @importFrom glue glue
#' @importFrom aseskit iif ifnull check_curl
#' @export
#' @rdname synthesize_gaodemap_api
synthesize_gaode_api_convcoord <- function(
    url_body, batch=FALSE, coord_from=c('WGS-84', 'BD-09', 'GCJ-02'),
    coord_to=c('GCJ-02', 'BD-09', 'WGS-84'), use_curl=FALSE, ...){
    
    # check args
    dots <- list(...)
    coord_code <- c(`WGS-84`='gps', `GCJ-02`='autonavi', `BD-09`='baidu')
    coord_from <- match.arg(coord_from)
    if (coord_from == 'GCJ-02')
        stop('gaode map only supports conversion to GCJ-02. No transformation ',
             'will be actually conducted (but API usage quota will be consumed anyway). ',
             'Coordinate conversion has been stopped.')
    coord_from <- coord_code[coord_from]
    coord_to <- match.arg(coord_to)
    if (coord_to != "GCJ-02") 
        stop('gaode map API now only supports conversion to GCJ-02 (autonavi).')
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    # url_body valid? If invalid, replace with 999,999
    stopifnot(is.character(url_body))
    url_body <- unlist(strsplit(url_body, ';'))
    valid_url_body <- grepl("^\\-?\\d+?\\.?\\d*?,\\-?\\d+?\\.?\\d*$", url_body)
    if (! all(valid_url_body)){
        warning('latlng must be in the format of "lon,lat;lon,lat". Coordinates #',
                paste(which(! valid_url_body), collapse=', '), 
                'are not correctly formatted.')
        url_body[! valid_url_body] <- '999,999'
    }
    url_body <- paste(url_body, collapse=';')
    
    # start
    api_url <- 'https://restapi.amap.com/v3/assistant/coordinate/'
    use_curl <- aseskit::check_curl(api_url, "Baidu Coords Convert", use_curl)
    url_char <- glue::glue('{api_url}convert?locations={url_body}',
                           '&coordsys={coord_from}&output=json')
    url_char <- embed_gaodemap_api_generic(url_char, ...)
    
    return(mapply(structure, enc2utf8(url_char), name_out=name_out, MoreArgs=list(
        class=c('gaode_api_convcoord', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}

# -----------------ipify api------------------

#' Synthesize ipify API
#' 
#' Working function for \code{\link[aseskit]{synthesize_api}()}. It will call
#' lower functions \code{\link{synthesize_ipify_api_geohost}()} or 
#' \code{\link{synthesize_ipify_api_getip}()}.
#' @param url_body character, the leading argument of the API URL.
#' @param api character, 'geohost', 'getip'.
#' @param use_curl logical, whether use \code{curl}(). Default TRUE.
#' @param ... other arguments to pass to the function, e.g., \code{key}
#'
#' @note When \code{key} is omitted or set NULL, the function will search for it
#' in the cache. If nothing is found, a GUI wizard will be launched for you to 
#' input the API key. If you want to bypass API key, set \code{key} = NA.
#' 
#' @return A \code{url} object for \code{\link{get_api_data}()} use.
#' @export
#'
#' @references ipify API documentation: 
#' \url{https://www.ipify.org}
#' @examples
#' \dontrun{
#' synthesize_ipify_api("<IP Address>", key="<YOUR IPIFY KEY>")
#' }
synthesize_ipify_api <- function(
    url_body, api=c('geohost', 'getip'), use_curl=FALSE, ...){
    
    api <- match.arg(api)
    stopifnot(is.character(url_body))
    
    fun_dict <- list(geohost = synthesize_ipify_api_geohost,
                     getip   = synthesize_ipify_api_getip)
    fun <- fun_dict[[api]]
    fun(url_body=url_body, use_curl=use_curl, ...)
}

#' @importFrom glue glue
#' @importFrom aseskit getApiKey iif check_curl
embed_ipify_api_generic <- function(url_char, key=NULL, ...){
    stopifnot(is.character(url_char))
    
    if (is.null(key)){
        key <- aseskit::getApiKey('ipify')
    }else if (is.na(key)){
        return(url_char)
    }
    url_char <- glue::glue('{url_char}?apiKey={key}')
    return(url_char)
}


#' @importFrom glue glue
#' @importFrom aseskit iif ifnull
#' @export
#' @rdname synthesize_ipify_api
synthesize_ipify_api_geohost <- function(url_body, use_curl=FALSE,...){
    # check args
    dots <- list(...)
    stopifnot(is.character(url_body))
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    # start
    api_url <- "https://geo.ipify.org/api/v1"
    use_curl <- aseskit::check_curl(api_url, "ipify geohost", use_curl)
    url_char <- api_url
    url_char <- embed_ipify_api_generic(url_char, ...)  # key first
    url_char <- glue::glue('{url_char}&ipAddress={url_body}')
    
    return(mapply(structure, url_char, name_out=name_out, MoreArgs=list(
        class=c('ipify_api_geohost', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}

#' @importFrom aseskit iif ifnull
#' @importFrom glue glue
#' @export
#' @rdname synthesize_ipify_api
synthesize_ipify_api_getip <- function(url_body, use_curl=FALSE,...){
    # check args
    dots <- list(...)
    stopifnot(is.character(url_body))
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    # start
    api_url <- "https://api.ipify.org?format=json"
    use_curl <- aseskit::check_curl(api_url, "ipify getip", use_curl)
    url_char <- api_url
    
    return(mapply(structure, url_char, name_out=name_out, MoreArgs=list(
        class=c('ipify_api_getip', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}

# -----------ipstack api-----------

#' Synthesize ipstack API
#' 
#' Working function for \code{\link[aseskit]{synthesize_api}()}. It will call
#' lower functions \code{\link{synthesize_ipstack_api_geohost}()}.
#' @param url_body character, the leading argument in the API URL.
#' @param api character, 'geohost'.
#' @param use_curl logical, whether use \code{curl}(). Default TRUE.
#' @param ... other arguments to pass to the function, e.g., \code{key}
#'
#' @note When \code{key} is omitted or set NULL, the function will search for it
#' in the cache. If nothing is found, a GUI wizard will be launched for you to 
#' input the API key. If you want to bypass API key, set \code{key} = NA.
#' 
#' @return A \code{url} object for \code{\link{get_api_data}()} use.
#' @export
#'
#' @references ipstack API documentation: 
#' \url{https://ipstack.com/documentation}
#' @examples
#' \dontrun{
#' synthesize_ipstack_api("<IP Address>", key="<YOUR IPSTACK KEY>")
#' }
synthesize_ipstack_api <- function(
    url_body, api=c('geohost'), use_curl=FALSE, ...){
    
    api <- match.arg(api)
    stopifnot(is.character(url_body))
    
    fun_dict <- list(geohost    = synthesize_ipstack_api_geohost)
    fun <- fun_dict[[api]]
    fun(url_body=url_body, use_curl=use_curl, ...)
}

#' @importFrom glue glue
#' @importFrom aseskit getApiKey iif check_curl
embed_ipstack_api_generic <- function(url_char, key=NULL, ...){
    stopifnot(is.character(url_char))
    if (is.null(key)) {
        key <- aseskit::getApiKey('ipstack')
    }else if (is.na(key)) {  # if NA, do not use the key
        return(url_char)
    }
    url_char <- glue::glue('{url_char}?access_key={key}')
    return(url_char)
}


#' @importFrom aseskit iif ifnull
#' @importFrom glue glue
#' @export
#' @rdname synthesize_ipstack_api
synthesize_ipstack_api_geohost <- function(url_body, use_curl=FALSE, ...){
    # check args
    dots <- list(...)
    stopifnot(is.character(url_body))
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    # start
    api_url= "http://api.ipstack.com/"
    use_curl <- aseskit::check_curl(api_url, "ipstack geohost", use_curl)
    url_char <- glue::glue('{api_url}{url_body}')
    url_char <- embed_ipstack_api_generic(url_char, ...)

    return(mapply(structure, url_char, name_out=name_out, MoreArgs=list(
        class=c('ipstack_api_geohost', 'url'), use_curl=use_curl),
        SIMPLIFY=FALSE))
}

# -----------------ipinfo api------------------

#' Synthesize ipinfo API
#' 
#' Working function for \code{\link[aseskit]{synthesize_api}()}. It will call
#' lower functions \code{\link{synthesize_ipinfo_api_geohost}()}.
#' @param url_body character, the leading argument in the API URL.
#' @param api character, 'geohost'.
#' @param use_curl logical, whether use \code{curl}(). Default TRUE.
#' @param ... other arguments to pass to the function, e.g., \code{key}
#'
#' @note When \code{key} is omitted or set NULL, the function will search for it
#' in the cache. If nothing is found, a GUI wizard will be launched for you to 
#' input the API key. If you want to bypass API key, set \code{key} = NA.
#' 
#' @return A \code{url} object for \code{\link{get_api_data}()} use.
#' @export
#'
#' @references ipinfo API documentation: 
#' \url{https://ipinfo.io/developers}
#' @examples
#' \dontrun{
#' synthesize_ipinfo_api("<IP Address>", key="<YOUR IPINFO KEY>")
#' }
synthesize_ipinfo_api <- function(
    url_body, api=c('geohost'), use_curl=FALSE, ...){
    
    api <- match.arg(api)
    stopifnot(is.character(url_body))
    
    fun_dict <- list(geohost    = synthesize_ipinfo_api_geohost)
    fun <- fun_dict[[api]]
    fun(url_body=url_body, use_curl=use_curl, ...)
}

#' @importFrom glue glue
#' @importFrom aseskit getApiKey iif
embed_ipinfo_api_generic <- function(url_char, key=NULL, ...){
    stopifnot(is.character(url_char))

    # key is not mandatory
    if (is.null(key)) {
        key <- aseskit::getApiKey('ipinfo')
    }else if (is.na(key)) {
        return(url_char)
    }
    url_char <- glue::glue('{url_char}?token={key}')
    return(url_char)
}


#' @importFrom aseskit check_curl iif ifnull
#' @importFrom glue glue
#' @export
#' @rdname synthesize_ipinfo_api
synthesize_ipinfo_api_geohost <- function(url_body, use_curl=FALSE,...){
    # check args
    dots <- list(...)
    stopifnot(is.character(url_body))
    name_out <- aseskit::ifnull(dots$name_out, url_body)
    
    # start
    api_url <- "https://ipinfo.io/"
    use_curl <- aseskit::check_curl(api_url, "ipinfo geohost", use_curl)
    url_char <- glue::glue('{api_url}{url_body}/json')
    url_char <- embed_ipinfo_api_generic(url_char, ...)
    
    return(mapply(structure, url_char, name_out=name_out, MoreArgs=list(
        class=c('ipinfo_api_geohost', 'url'), use_curl=use_curl), 
        SIMPLIFY=FALSE))
}
madlogos/asesgeo documentation built on Aug. 9, 2019, 9:53 a.m.