R/TDX.R

Defines functions Bus_Schedule retrieve_first Bus_Shape Bus_Route Bus_StopOfRoute histo_data date_rev get_token

Documented in Bus_Route Bus_Schedule Bus_Shape Bus_StopOfRoute get_token

library(dplyr)
library(jsonlite)
library(XML)
library(xml2)
library(httr)
library(sf)
library(urltools)
library(cli)
library(data.table)
library(progress)
library(archive)
library(fs)

# usethis::use_package("dplyr")
# usethis::use_package("jsonlite")
# usethis::use_package("XML")
# usethis::use_package("xml2")
# usethis::use_package("httr")
# usethis::use_package("sf")
# usethis::use_package("urltools")
# usethis::use_package("cli")
# usethis::use_package("data.table")
# usethis::use_package("progress")
# usethis::use_package("archive")
# usethis::use_package("fs")

# TDX_County=read.table("C:/Users/USER/OneDrive - The University of Sydney (Students)/Desktop/R Transportation/R Github Project/NYCU_TDX/data/tdx_county.txt", encoding="UTF-8", sep=",", header=T)
# usethis::use_data(TDX_County, overwrite=T)
#
# TDX_Railway=read.table("C:/Users/USER/OneDrive - The University of Sydney (Students)/Desktop/R Transportation/R Github Project/NYCU_TDX/data/tdx_railway.txt", encoding="UTF-8", sep=",", header=T)
# usethis::use_data(TDX_Railway, overwrite=T)
#
# TDX_RoadClass=read.table("C:/Users/USER/OneDrive - The University of Sydney (Students)/Desktop/R Transportation/R Github Project/NYCU_TDX/data/tdx_roadclass.txt", encoding="UTF-8", sep=",", header=T)
# usethis::use_data(TDX_RoadClass, overwrite=T)



# #---API Form---#
# url_all=data.frame(Mode=c("Bus","Rail","Bike","Air","Ship"),
#                    url=c("https://tdx.transportdata.tw/webapi/File/Swagger/V3/2998e851-81d0-40f5-b26d-77e2f5ac4118",
#                          "https://tdx.transportdata.tw/webapi/File/Swagger/V3/268fc230-2e04-471b-a728-a726167c1cfc",
#                          "https://tdx.transportdata.tw/webapi/File/Swagger/V3/2cc9b888-a592-496f-99de-9ab35b7fb70d",
#                          "https://tdx.transportdata.tw/webapi/File/Swagger/V3/eb87998f-2f9c-4592-8d75-c62e5b724962",
#                          "https://tdx.transportdata.tw/webapi/File/Swagger/V3/38e6a4c2-5111-4449-8f37-6347caf4a7fc"))
# name_remain=function(x){
#   all_name=c("type","title","format","description")
#   name_remain=all_name[all_name %in% x]
#   return(name_remain)
# }
#
# all_api=data.frame()
# for(k in c(1:nrow(url_all))){
#   api_content=fromJSON(url_all$url[k])
#   for(i in c(1:length(api_content$components$schemas))){
#     temp=bind_rows(mapply(function(x) list(data.frame(rbind(api_content$components$schemas[[i]]$properties[[x]])) %>% select(name_remain(names(.)))), c(1:length(api_content$components$schemas[[i]]$properties))))
#     if(nrow(temp)!=0){
#       temp=cbind(Attribute=names(api_content$components$schemas[[i]]$properties), temp)
#       temp=cbind(API=api_content$components$schemas[[i]]$title, temp)
#       all_api=bind_rows(all_api, cbind(Mode=url_all$Mode[k], temp))
#     }
#   }
# }
# for(i in c(4:7)){
#   all_api[[i]]=mapply(function(x) ifelse(is.null(all_api[[i]][[x]]), NA, all_api[[i]][[x]]), c(1:nrow(all_api)))
# }
# write.csv(all_api, "./others/all_api_attribute.csv", row.names=F)



# #---need to update periodically---#
# download.file("https://segis.moi.gov.tw/FileDownload/Download.aspx?u=j0e6LYeQ1bVn3G9Pxpg5fa7y7eVLLN9lAKKOtEZ0Khj4hKtfaYF5I99aRvHukitTJ2QQ%2bfduz9CenperpgRf1w%3d%3d", paste0(tempdir(), "./STATCatalog.xlsx"), mode="wb", quiet=T)
# catalog=readxl::read_xlsx(paste0(tempdir(), "./STATCatalog.xlsx"), sheet=1, skip=1)
# colnames(catalog)=c("TYPE1","TYPE2","DATANAME","TIME","SPACE","UNIT","COLUMN")
# catalog_temp=filter(catalog, DATANAME %in% c("\u884c\u653f\u5340\u91ab\u7642\u9662\u6240\u7d71\u8a08",
#                                         "\u7d71\u8a08\u5340\u91ab\u7642\u9662\u6240\u7d71\u8a08"), !TIME %in% c("97Y","98Y"))%>%
#   group_by(DATANAME, UNIT, TIME)%>%
#   summarise(SPACE=paste(SPACE, collapse="|"))
# write.csv(catalog_temp, "./others/hospital_area_time.csv", row.names=F)
# catalog_temp=filter(catalog, DATANAME %in% c("\u884c\u653f\u5340\u4eba\u53e3\u7d71\u8a08", "\u7d71\u8a08\u5340\u4eba\u53e3\u7d71\u8a08",
#                                              "\u884c\u653f\u5340\u4e94\u6b72\u5e74\u9f61\u7d44\u6027\u5225\u4eba\u53e3\u7d71\u8a08",
#                                              "\u7d71\u8a08\u5340\u4e94\u6b72\u5e74\u9f61\u7d44\u6027\u5225\u4eba\u53e3\u7d71\u8a08"))%>%
#   mutate(DATANAME=case_when(
#     grepl("\u4e94\u6b72\u5e74\u9f61\u7d44\u6027\u5225\u4eba\u53e3\u7d71\u8a08", DATANAME) ~ "\u4e94\u6b72\u5e74\u9f61\u7d44\u6027\u5225\u4eba\u53e3\u7d71\u8a08",
#     grepl("\u4eba\u53e3\u7d71\u8a08", DATANAME) ~ "\u4eba\u53e3\u7d71\u8a08"
#   ))%>%
#   group_by(DATANAME, UNIT, TIME)%>%
#   summarise(SPACE=paste(SPACE, collapse="|"))%>%
#   mutate(SA=case_when(
#     grepl("\u6700\u5c0f", UNIT) ~ "SA0",
#     grepl("\u4e00\u7d1a", UNIT) ~ "SA1",
#     grepl("\u4e8c\u7d1a", UNIT) ~ "SA2",
#     grepl("\u6751\u91cc", UNIT) ~ "Village",
#     grepl("\u9109\u93ae\u5e02\u5340", UNIT) ~ "Town",
#     grepl("\u7e23\u5e02", UNIT) ~ "County"
#   ))
# catalog_temp$TIME_NUM=mapply(function(x) (as.numeric(strsplit(catalog_temp$TIME, "Y|M")[[x]][1])+1911)*12+as.numeric(strsplit(catalog_temp$TIME, "Y|M")[[x]][2]), c(1:nrow(catalog_temp)))
# catalog_temp$TIME_lab=paste0(as.numeric(mapply(function(x) strsplit(catalog_temp$TIME, "Y|M")[[x]][1], c(1:nrow(catalog_temp))))+1911, "-", mapply(function(x) strsplit(catalog_temp$TIME, "Y|M")[[x]][2], c(1:nrow(catalog_temp))))
# catalog_temp=arrange(catalog_temp, DATANAME, UNIT, TIME_NUM)
# write.csv(catalog_temp, "./others/pop_area_time.csv", row.names=F)
# catalog_temp=filter(catalog, grepl("\u7d71\u8a08\u5340\u570b\u571f\u5229\u7528\u8abf\u67e5\u7d71\u8a08", DATANAME))%>%
#   mutate(DATANAME="\u7d71\u8a08\u5340\u570b\u571f\u5229\u7528\u8abf\u67e5\u7d71\u8a08")%>%
#   group_by(DATANAME, UNIT, TIME)%>%
#   summarise(SPACE=paste(SPACE, collapse="|"))%>%
#   mutate(SA=case_when(
#     grepl("\u6700\u5c0f", UNIT) ~ "SA0",
#     grepl("\u4e00\u7d1a", UNIT) ~ "SA1",
#     grepl("\u4e8c\u7d1a", UNIT) ~ "SA2"
#   ))
# catalog_temp$Year=as.numeric(gsub("Y", "", catalog_temp$TIME))+1911
# catalog_temp=arrange(catalog_temp, DATANAME, UNIT, Year)
# write.csv(catalog_temp, "./others/landuse_area_time.csv", row.names=F)
# catalog_temp=filter(catalog, grepl("\u5de5\u5546\u5bb6\u6578", DATANAME), !TIME %in% c("97Y","98Y"))%>%
#   group_by(DATANAME, UNIT, TIME)%>%
#   summarise(SPACE=paste(SPACE, collapse="|"))
# write.csv(catalog_temp, "./others/business_area_time.csv", row.names=F)
# catalog_temp=filter(catalog, grepl("GML", DATANAME))%>%
#   select(DATANAME, TIME)%>%
#   mutate(SA=case_when(
#     grepl("\u6700\u5c0f", DATANAME) ~ "SA0",
#     grepl("\u4e00\u7d1a", DATANAME) ~ "SA1",
#     grepl("\u4e8c\u7d1a", DATANAME) ~ "SA2"
#   ),
#   TIME_NUM=mapply(function(x) (as.numeric(strsplit(TIME, "Y|M")[[x]][1])+1911)*12+as.numeric(strsplit(TIME, "Y|M")[[x]][2]), c(1:nrow(.))),
#   TIME_lab=paste0(as.numeric(mapply(function(x) strsplit(TIME, "Y|M")[[x]][1], c(1:nrow(.))))+1911, "-", mapply(function(x) strsplit(TIME, "Y|M")[[x]][2], c(1:nrow(.)))))
# write.csv(catalog_temp, "./others/statistical_area.csv", row.names=F)
# catalog_temp=filter(catalog, grepl("\u6821\u5225\u6982\u89bd", DATANAME), !grepl("\u7a7a\u5927", DATANAME))%>%
#   filter(grepl("\u9ad8\u7d1a\u4e2d\u7b49\u5b78\u6821|\u5927\u5c08\u6821\u9662|\u570b\u6c11\u4e2d\u5b78|\u570b\u6c11\u5c0f\u5b78", DATANAME))%>%
#   select(DATANAME, TIME, SPACE)%>%
#   mutate(Year=as.numeric(gsub("Y", "", TIME))+1911)%>%
#   mutate(Level=case_when(
#     grepl("\u9ad8\u7d1a\u4e2d\u7b49\u5b78\u6821", DATANAME) ~ "senior",
#     grepl("\u5927\u5c08\u6821\u9662", DATANAME) ~ "university",
#     grepl("\u570b\u6c11\u5c0f\u5b78", DATANAME) ~ "elementary",
#     grepl("\u570b\u6c11\u4e2d\u5b78", DATANAME) ~ "junior"
#   ))
# write.csv(catalog_temp, "./others/school_year.csv", row.names=F)


#---get the token---#
#' @export
get_token=function(client_id, client_secret){
  if (!require(httr)) install.packages("httr")
  if (!require(jsonlite)) install.packages("jsonlite")

  if("TDX_token.txt" %in% dir(Sys.getenv("HOME"))){
    act=read.table(paste0(Sys.getenv("HOME"), "/TDX_token.txt"))$V1
    x=GET("https://tdx.transportdata.tw/api/basic/v2/Basic/County?%24format=JSON", add_headers(Accept="application/+json", Authorization=paste("Bearer", act)))

    if(grepl("invalid", x)){
      cat(paste0("The access token is expired or invalid. Get newer one!\n"))
    }else{
      cat(paste0("The access token is valid. Use it!\n"))
      return(act)
      break
    }
  }

  x=POST("https://tdx.transportdata.tw/auth/realms/TDXConnect/protocol/openid-connect/token",
         encode="form",
         body=list(
           grant_type="client_credentials",
           client_id=client_id,
           client_secret=client_secret
         ))
  act=content(x)$access_token
  if(is.null(act)){
    stop("Your 'client_id' or 'client_secret' is WRONG!!")
  }else{
    cat("Connect Successfully! This token will expire in 1 day. New token is stored.\n")
    write.table(act, paste0(Sys.getenv("HOME"), "/TDX_token.txt"), col.names=F, row.names=F)
    return(act)
  }
}



date_rev=function(dates){
  if(grepl(",", dates)){
    dates_all=unique(unlist(strsplit(dates, ",")))
  }else if(grepl("~", dates)){
    dates_all=unique(unlist(strsplit(dates, "~")))
  }else{
    dates_all=dates
  }

  tryCatch({
    dates_all=as.Date(dates_all)
    if(grepl("~", dates)){
      dates_all=as.Date(dates_all[1]:dates_all[2])
    }
  }, error=function(err){
    cat(paste0("Type", paste0(rep(" ", 12), collapse=""), "Format", paste0(rep(" ", 18), collapse=""), "Example\n", paste0(rep("=", 61), collapse=""), "\nSingle Date\tYYYY-MM-DD\t\t2023-01-01\nMultiple Dates\tYYYY-MM-DD,YYYY-MM-DD\t2023-01-01,2023-02-01\nDate Range\tYYYY-MM-DD~YYYY-MM-DD\t2023-01-01~2023-01-31\n"))
    stop("Date value is invalid! Format of the date is listed above.")
  })
  if(sum(is.na(dates_all))!=0){
    cat(paste0("Type", paste0(rep(" ", 12), collapse=""), "Format", paste0(rep(" ", 18), collapse=""), "Example\n", paste0(rep("=", 61), collapse=""), "\nSingle Date\tYYYY-MM-DD\t\t2023-01-01\nMultiple Dates\tYYYY-MM-DD,YYYY-MM-DD\t2023-01-01,2023-02-01\nDate Range\tYYYY-MM-DD~YYYY-MM-DD\t2023-01-01~2023-01-31\n"))
    stop("Date value is invalid! Format of the date is listed above.")
  }
  return(dates_all)
}



histo_data=function(access_token, mode, type, cou_ope, dates){
  if (!require(cli)) install.packages("cli")

  dates_all=date_rev(dates)
  all_data=data.frame()
  num_of_nodata=0
  cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}]  {.emph Date: {dates_all[pb_current]}}", total=length(dates_all))
  for(i in as.character(dates_all)){
    cli_progress_update()

    if(mode %in% c("Bus","Bike")){
      if(cou_ope=="Intercity"){
        url=paste0("https://tdx.transportdata.tw/api/historical/v2/Historical/", mode, "/", type, "/Date/", i, "/InterCity?%24format=CSV")
      }else if(cou_ope %in% TDX_County$Code){
        url=paste0("https://tdx.transportdata.tw/api/historical/v2/Historical/", mode, "/", type, "/Date/", i, "/City/", cou_ope, "?%24format=CSV")
      }else{
        print(TDX_County)
        stop(paste0("City: '", cou_ope, "' is not valid. Please check out the parameter table above."))
      }
    }else if(mode=="Rail"){
      if(cou_ope %in% c("TRTC","KRTC","TYMC","NTDLRT","TMRT","KLRT")){
        url=paste0("https://tdx.transportdata.tw/api/historical/v2/Historical/", mode, "/Metro/", type, "/Date/", i, "/", cou_ope, "?%24format=CSV")
      }else if(cou_ope %in% c("TRA","THSR")){
        url=paste0("https://tdx.transportdata.tw/api/historical/v2/Historical/", mode, "/", cou_ope, "/", type, "/Date/", i, "?%24format=CSV")
      }else if(cou_ope=="AFR"){
        stop("'AFR' does not provide historical data up to now!")
      }else{
        print(TDX_Railway)
        stop(paste0("Railway: '", cou_ope, "' is not valid. Please check out the parameter table above."))
      }
    }

    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    if(is.na(content(x, "text", encoding="UTF-8"))){
      num_of_nodata=num_of_nodata+1
      cli_alert_info(paste0("Data of ", i, " is not avaliable!\n"))
    }else if(content(x, "text", encoding="UTF-8")==""){
      num_of_nodata=num_of_nodata+1
      cli_alert_info(paste0("Data of ", i, " is not avaliable!\n"))
    }else{
      all_data_temp=read.csv(textConnection(content(x, "text", encoding="UTF-8")), header=T)
      if("invalid.token" %in% names(all_data_temp)){
        stop(paste0("Your access token is invalid!"))
      }else if("Message" %in% names(all_data_temp)){
        stop(all_data_temp$Message)
      }else{
        all_data=rbind(all_data, cbind(all_data_temp, "InfoDate"=i))
      }
    }
  }
  cli_alert_info(ifelse(num_of_nodata==0, "All Done!", paste0("All Done!\n", num_of_nodata, " dates have no data!")))
  cli_progress_done()
  return(all_data)
}



#' @export
Bus_StopOfRoute=function(access_token, county, dates=F, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")
  if (!require(cli)) install.packages("cli")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(dates==F){
    if(county=="Intercity"){
      url="https://tdx.transportdata.tw/api/basic/v2/Bus/StopOfRoute/InterCity?%24format=JSON"
    }else{
      url=paste0("https://tdx.transportdata.tw/api/basic/v2/Bus/StopOfRoute/City/", county, "?%24format=JSON")
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      bus_info=fromJSON(content(x, as="text"))
    }, error=function(err){
      if (grepl("invalid", conditionMessage(err))){
        stop(paste0("Your access token is invalid!"))
      }
    })
    if("Message" %in% names(bus_info)){
      print(TDX_County)
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }

    bus_info$RouteName=bus_info$RouteName$Zh_tw
    bus_info$SubRouteName=bus_info$SubRouteName$Zh_tw
    num_of_stop=mapply(function(x) nrow(bus_info$Stops[[x]]), c(1:nrow(bus_info)))
    cat(paste0(length(num_of_stop), " Routes\n"))
    bus_stop_temp=data.frame(StopUID=unlist(mapply(function(x) bus_info$Stops[[x]]$StopUID, c(1:nrow(bus_info)))),
                             StopID=unlist(mapply(function(x) bus_info$Stops[[x]]$StopID, c(1:nrow(bus_info)))),
                             StopName=unlist(mapply(function(x) bus_info$Stops[[x]]$StopName$Zh_tw, c(1:nrow(bus_info)))),
                             StopBoarding=unlist(mapply(function(x) bus_info$Stops[[x]]$StopBoarding, c(1:nrow(bus_info)))),
                             StopSequence=unlist(mapply(function(x) bus_info$Stops[[x]]$StopSequence, c(1:nrow(bus_info)))),
                             PositionLon=unlist(mapply(function(x) bus_info$Stops[[x]]$StopPosition$PositionLon, c(1:nrow(bus_info)))),
                             PositionLat=unlist(mapply(function(x) bus_info$Stops[[x]]$StopPosition$PositionLat, c(1:nrow(bus_info)))),
                             StationID=unlist(mapply(function(x) if(!is.null(bus_info$Stops[[x]]$StationID)) bus_info$Stops[[x]]$StationID else rep(NA, num_of_stop[x]), c(1:nrow(bus_info)))),
                             LocationCityCode=unlist(mapply(function(x) bus_info$Stops[[x]]$LocationCityCode, c(1:nrow(bus_info)))))

    bus_info=as.data.frame(lapply(bus_info[, c("RouteUID","RouteID","RouteName","SubRouteUID","SubRouteID","SubRouteName","Direction")], rep, num_of_stop))
    bus_stop=cbind(bus_info, bus_stop_temp)
    rm(bus_info, bus_stop_temp, num_of_stop)

    cat(paste0("#---", county, " Stop of Route Downloaded---#\n"))
  }else{
    bus_stop=histo_data(access_token, "Bus", "StopOfRoute", county, dates)
  }

  if(dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(bus_stop, out, row.names=F)
    }
  }else if(dtype=="sf"){
    bus_stop$geometry=st_as_sfc(ifelse(!is.na(bus_stop$PositionLon), paste0("POINT(", bus_stop$PositionLon, " ", bus_stop$PositionLat, ")"), "POINT EMPTY"))
    bus_stop=st_sf(bus_stop, crs=4326)

    if(grepl(".shp", out) & out!=F){
      write_sf(bus_stop, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(bus_stop)
}



#' @export
Bus_Route=function(access_token, county, dates=F, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(dates==F){
    if(county=="Intercity"){
      url="https://tdx.transportdata.tw/api/basic/v2/Bus/Route/InterCity?%24format=JSON"
    }else{
      url=paste0("https://tdx.transportdata.tw/api/basic/v2/Bus/Route/City/", county, "?$format=JSON")
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      bus_info=fromJSON(content(x, as="text"))
    }, error=function(err){
      if (grepl("invalid", conditionMessage(err))){
        stop(paste0("Your access token is invalid!"))
      }
    })
    if("Message" %in% names(bus_info)){
      print(TDX_County)
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }

    bus_info$RouteName=bus_info$RouteName$Zh_tw
    names(bus_info)[grepl("Zh", names(bus_info))]=gsub("Zh", "", names(bus_info)[grepl("Zh", names(bus_info))])
    num_of_subroute=mapply(function(x) nrow(bus_info$SubRoutes[[x]]), c(1:nrow(bus_info)))
    cat(paste0(length(num_of_subroute), " Routes\n"))

    bus_route_temp=data.frame(SubRouteUID=unlist(mapply(function(x) bus_info$SubRoutes[[x]]$SubRouteUID, c(1:nrow(bus_info)))),
                              SubRouteID=unlist(mapply(function(x) bus_info$SubRoutes[[x]]$SubRouteID, c(1:nrow(bus_info)))),
                              SubRouteName=unlist(mapply(function(x) bus_info$SubRoutes[[x]]$SubRouteName$Zh_tw, c(1:nrow(bus_info)))),
                              Direction=unlist(mapply(function(x) bus_info$SubRoutes[[x]]$Direction, c(1:nrow(bus_info)))),
                              FirstBusTime=unlist(mapply(function(x) if(!is.null(bus_info$SubRoutes[[x]]$FirstBusTime)) bus_info$SubRoutes[[x]]$FirstBusTime else rep(NA, num_of_subroute[x]), c(1:nrow(bus_info)))),
                              LastBusTime=unlist(mapply(function(x) if(!is.null(bus_info$SubRoutes[[x]]$LastBusTime)) bus_info$SubRoutes[[x]]$LastBusTime else rep(NA, num_of_subroute[x]), c(1:nrow(bus_info)))),
                              HolidayFirstBusTime=unlist(mapply(function(x) if(!is.null(bus_info$SubRoutes[[x]]$HolidayFirstBusTime)) bus_info$SubRoutes[[x]]$HolidayFirstBusTime else rep(NA, num_of_subroute[x]), c(1:nrow(bus_info)))),
                              HolidayLastBusTime=unlist(mapply(function(x) if(!is.null(bus_info$SubRoutes[[x]]$HolidayLastBusTime)) bus_info$SubRoutes[[x]]$HolidayLastBusTime else rep(NA, num_of_subroute[x]), c(1:nrow(bus_info)))))

    col_retain=c("RouteUID","RouteID","RouteName","BusRouteType","DepartureStopName","DestinationStopName","TicketPriceDescription","FareBufferZoneDescription")
    col_retain=col_retain[col_retain %in% names(bus_info)]
    bus_info=as.data.frame(lapply(bus_info[, col_retain], rep, num_of_subroute))
    bus_route=cbind(bus_info, bus_route_temp)

    col_seq=c("RouteUID","RouteID","RouteName", "SubRouteUID", "SubRouteID", "SubRouteName", "Direction", "BusRouteType","DepartureStopName","DestinationStopName","TicketPriceDescription","FareBufferZoneDescription", "FirstBusTime", "LastBusTime", "HolidayFirstBusTime", "HolidayLastBusTime")
    col_seq=col_seq[col_seq %in% names(bus_route)]
    bus_route=bus_route[,col_seq]
  }else{
    bus_route=histo_data(access_token, "Bus", "Route", county, dates)
  }

  if(nchar(out)!=0 & out!=F){
    write.csv(bus_route, out, row.names=F)
  }
  return(bus_route)
}



#' @export
Bus_Shape=function(access_token, county, dates=F, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(dates==F){
    if(county=="Intercity"){
      url="https://tdx.transportdata.tw/api/basic/v2/Bus/Shape/InterCity?&$format=JSON"
    }else{
      url=paste0("https://tdx.transportdata.tw/api/basic/v2/Bus/Shape/City/", county, "?&$format=JSON")
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      bus_shape=fromJSON(content(x, as="text"))
    }, error=function(err){
      if (grepl("invalid", conditionMessage(err))){
        stop(paste0("Your access token is invalid!"))
      }
    })
    if("Message" %in% names(bus_shape)){
      print(TDX_County)
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }

    bus_shape$RouteName=bus_shape$RouteName$Zh_tw
    bus_shape$SubRouteName=bus_shape$SubRouteName$Zh_tw

    col_seq=c("RouteUID", "RouteID", "RouteName", "SubRouteUID", "SubRouteID", "SubRouteName", "Direction", "Geometry")
    col_seq=col_seq[col_seq %in% names(bus_shape)]
    bus_shape=bus_shape[, col_seq]%>%
      rename(geometry=Geometry)

    cat(paste0("#---", county, " Bus Shape Downloaded---#\n"))
  }else{
    bus_shape=histo_data(access_token, "Bus", "Shape", county, dates)
    bus_shape=rename(bus_shape, geometry=Geometry)
  }

  if(dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(bus_shape, out, row.names=F)
    }
  }else if(dtype=="sf"){
    bus_shape$geometry=st_as_sfc(bus_shape$geometry)
    bus_shape=st_sf(bus_shape, crs=4326)

    if(grepl(".shp", out) & out!=F){
      write_sf(bus_shape, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(bus_shape)
}



# used to retrieve the first row in function Bus_Schedule
retrieve_first=function(dat){
  temp=data.frame(t(mapply(function(x){
    first_row=t(unlist(dat[[x]][1,]))
    return(first_row)
  }, c(1:length(dat)))))
  return(temp)
}



#' @export
Bus_Schedule=function(access_token, county, dates=F, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(data.table)) install.packages("data.table")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(cli)) install.packages("cli")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(dates==F){
    iti_time=1
  }else{
    date_all=as.character(date_rev(dates))
    iti_time=length(date_all)
    cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}]  {.emph Date: {date_all[pb_current]}}", total=iti_time)
  }

  bus_schedule=data.frame()
  for(i in c(1:iti_time)){
    if(dates!=F){
      cli_progress_update()
    }

    if(dates==F){
      if(county=="Intercity"){
        url="https://tdx.transportdata.tw/api/basic/v2/Bus/Schedule/InterCity?&$format=JSON"
      }else{
        url=paste0("https://tdx.transportdata.tw/api/basic/v2/Bus/Schedule/City/", county, "?&$format=JSON")
      }
    }else{
      if(county=="Intercity"){
        url=paste0("https://tdx.transportdata.tw/api/historical/v2/Historical/Bus/Schedule/Date/", date_all[i], "/InterCity/?%24format=JSON")
      }else{
        url=paste0("https://tdx.transportdata.tw/api/historical/v2/Historical/Bus/Schedule/Date/", date_all[i], "/City/", county, "?%24format=JSON")
      }
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      if(dates==F){
        bus_info=fromJSON(content(x, as="text"))
      }else{
        bus_info=fromJSON(content(x, as="text"))$BusSchedules
      }
    }, error=function(err){
      if (grepl("invalid", conditionMessage(err))){
        stop(paste0("Your access token is invalid!"))
      }
    })
    if("Message" %in% names(bus_info)){
      if(county %in% TDX_County$Code){
        stop(paste0("Bus schedule data of '",county, "' is not avaliable.\n", bus_info$Message))
      }
      else{
        print(TDX_County)
        stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
      }
    }

    bus_info$RouteName=bus_info$RouteName$Zh_tw
    bus_info$SubRouteName=bus_info$SubRouteName$Zh_tw
    bus_route=bus_info[, c("RouteUID","RouteID","RouteName","SubRouteUID","SubRouteID","SubRouteName","Direction")]

    num_of_freq=unlist(mapply(function(x) ifelse(is.null(nrow(bus_info$Frequencys[[x]])), 0, nrow(bus_info$Frequencys[[x]])), c(1:nrow(bus_info))))
    num_of_time=unlist(mapply(function(x) ifelse(is.null(nrow(bus_info$Timetables[[x]])), 0, nrow(bus_info$Timetables[[x]])), c(1:nrow(bus_info))))

    bus_freq=data.frame(StartTime=unlist(mapply(function(x) bus_info$Frequencys[[x]]$StartTime, c(1:nrow(bus_info)))),
                        EndTime=unlist(mapply(function(x) bus_info$Frequencys[[x]]$EndTime, c(1:nrow(bus_info)))),
                        MinHeadwayMins=unlist(mapply(function(x) bus_info$Frequencys[[x]]$MinHeadwayMins, c(1:nrow(bus_info)))),
                        MaxHeadwayMins=unlist(mapply(function(x) bus_info$Frequencys[[x]]$MaxHeadwayMins, c(1:nrow(bus_info)))),
                        rbindlist(mapply(function(x) bus_info$Frequencys[[x]]$ServiceDay, c(1:nrow(bus_info)))))
    bus_freq=cbind(bus_route[rep(c(1:nrow(bus_route)), times=num_of_freq),], bus_freq)

    bus_time=mapply(function(x) list(retrieve_first(bus_info$Timetables[[x]]$StopTimes)), which(num_of_time!=0))
    bus_time=do.call(bind_rows, bus_time)%>%
      data.frame()
    bus_time=select(bus_time, -X5)
    colnames(bus_time)=c("StopSequence","StopUID","StopID","StopName","ArrivalTime","DepartureTime")
    day_oper=mapply(function(x) list(bus_info$Timetables[[x]]$ServiceDay), c(1:nrow(bus_info))) %>% do.call(rbind, .)
    if(!is.null(day_oper)){
      bus_time=data.frame(TripID=unlist(mapply(function(x) ifelse(is.null(bus_info$Timetables[[x]]$TripID), list(rep(NA, num_of_time[x])), list(bus_info$Timetables[[x]]$TripID)), c(1:nrow(bus_info)))),
                          bus_time,
                          day_oper)
    }else{
      bus_time=data.frame(TripID=unlist(mapply(function(x) ifelse(is.null(bus_info$Timetables[[x]]$TripID), list(rep(NA, num_of_time[x])), list(bus_info$Timetables[[x]]$TripID)), c(1:nrow(bus_info)))),
                          bus_time)
    }
    bus_time=cbind(bus_route[rep(c(1:nrow(bus_route)), times=num_of_time),], bus_time)

    bus_schedule_temp=bind_rows(bus_freq, bus_time)
    row.names(bus_schedule_temp)=NULL

    if(dates==F){
      bus_schedule=bus_schedule_temp
    }else{
      bus_schedule_temp=cbind(bus_schedule_temp, "InfoDate"=date_all[i])
      bus_schedule=rbind(bus_schedule, bus_schedule_temp)
      if(i==iti_time){
        cli_progress_done()
      }
    }
  }

  cat(paste0("#---", county, " Bus Schedule Downloaded---#\n"))

  if(nchar(out)!=0 & out!=F){
    write.csv(bus_schedule, out, row.names=F)
  }
  return(bus_schedule)
}



#' @export
Bus_Vehicle=function(access_token, county, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(county=="Intercity"){
    url="https://tdx.transportdata.tw/api/basic/v2/Bus/Vehicle/InterCity?&$format=JSON"
  }else{
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Bus/Vehicle/City/", county, "?&$format=JSON")
  }
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    bus_veh=fromJSON(content(x, as="text"))
  }, error=function(err){
    if (grepl("invalid", conditionMessage(err))){
      stop(paste0("Your access token is invalid!"))
    }
  })
  if("Message" %in% names(bus_veh)){
    if(county %in% TDX_County$Code){
      stop(paste0("'",county, "' bus vehicle data is not avaliable.\n", bus_veh$Message))
    }else{
      print(TDX_County)
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }
  }

  bus_veh=select(bus_veh, -"UpdateTime")

  if(nchar(out)!=0 & out!=F){
    write.csv(bus_veh, out, row.names=F)
  }
  return(bus_veh)
}



#' @export
Rail_StationOfLine=function(access_token, operator, dates=F, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(dates==F){
    if(operator=="TRA"){
      url="https://tdx.transportdata.tw/api/basic/v2/Rail/TRA/StationOfLine?&%24format=JSON"
    }else if(operator=="THSR"){
      stop("Please use function 'Rail_Station()' to retrieve the station of high speed rail (THSR).")
    }else if(operator=="AFR"){
      url=paste0("https://tdx.transportdata.tw/api/basic/v3/Rail/AFR/StationOfLine?&%24format=JSON")
    }else if(operator %in% TDX_Railway$Code){
      url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/Metro/StationOfLine/", operator, "?&%24format=JSON")
    }else{
      print(TDX_Railway)
      stop(paste0("'", operator, "' is not valid operator. Please check out the table of railway code above"))
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      if(operator=="AFR"){
        rail_info=fromJSON(content(x, as="text"))$StationOfLines
      }else{
        rail_info=fromJSON(content(x, as="text"))
      }
    }, error=function(err){
      if (grepl("invalid", conditionMessage(err))){
        stop(paste0("Your access token is invalid!"))
      }
    })

    num_of_station=unlist(mapply(function(x) nrow(rail_info$Stations[[x]]), c(1:nrow(rail_info))))

    if(operator=="TRA"){
      rail_station_temp=data.frame(Sequence=unlist(mapply(function(x) rail_info$Stations[[x]]$Sequence, c(1:nrow(rail_info)))),
                                   StationID=unlist(mapply(function(x) rail_info$Stations[[x]]$StationID, c(1:nrow(rail_info)))),
                                   StationName=unlist(mapply(function(x) rail_info$Stations[[x]]$StationName, c(1:nrow(rail_info)))),
                                   TraveledDistance=unlist(mapply(function(x) rail_info$Stations[[x]]$TraveledDistance, c(1:nrow(rail_info)))))
    }else if(operator=="AFR"){
      rail_station_temp=data.frame(Sequence=unlist(mapply(function(x) rail_info$Stations[[x]]$Sequence, c(1:nrow(rail_info)))),
                                   StationID=unlist(mapply(function(x) rail_info$Stations[[x]]$StationID, c(1:nrow(rail_info)))),
                                   StationName=unlist(mapply(function(x) rail_info$Stations[[x]]$StationName$Zh_tw, c(1:nrow(rail_info)))),
                                   CumulativeDistance=unlist(mapply(function(x) rail_info$Stations[[x]]$CumulativeDistance, c(1:nrow(rail_info)))))
    }else{
      rail_station_temp=data.frame(Sequence=unlist(mapply(function(x) rail_info$Stations[[x]]$Sequence, c(1:nrow(rail_info)))),
                                   StationID=unlist(mapply(function(x) rail_info$Stations[[x]]$StationID, c(1:nrow(rail_info)))),
                                   StationName=unlist(mapply(function(x) rail_info$Stations[[x]]$StationName$Zh_tw, c(1:nrow(rail_info)))))
    }

    rail_info=data.frame(LineID=rail_info$LineID[rep(c(1:nrow(rail_info)), num_of_station)])
    rail_station_line=cbind(rail_info, rail_station_temp)

    if (operator=="TRA"){
      url="https://tdx.transportdata.tw/api/basic/v2/Rail/TRA/Line?&%24format=JSON"
      x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))
      rail_line=fromJSON(content(x, as="text"))
      rail_line$LineName=rail_line$LineNameZh
      rail_line$LineSectionName=rail_line$LineSectionNameZh
      rail_station_line=left_join(rail_station_line, rail_line[, c("LineID","LineName","LineSectionName","IsBranch")], by=c("LineID"))%>%
        dplyr::select(LineID, LineName, LineSectionName, IsBranch, Sequence, StationID, StationName, TraveledDistance)
    }else if (operator=="AFR"){
      url="https://tdx.transportdata.tw/api/basic/v3/Rail/AFR/Line?&%24format=JSON"
      x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))
      rail_line=fromJSON(content(x, as="text"))$Lines
      rail_line$LineName=rail_line$LineName$Zh_tw
      rail_line$LineSectionName=rail_line$LineSectionName$Zh_tw
      rail_station_line=left_join(rail_station_line, rail_line)%>%
        dplyr::select(LineID, LineName, LineSectionName, IsBranch, Sequence, StationID, StationName, CumulativeDistance)
    }else{
      url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/Metro/Line/", operator,"?&%24format=JSON")
      x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))
      dat=content(x)
      rail_line=data.frame(LineID=unlist(mapply(function(x) dat[[x]]$LineID, c(1:length(dat)))),
                           LineName=unlist(mapply(function(x) dat[[x]]$LineName$Zh_tw, c(1:length(dat)))),
                           LineSectionName=unlist(mapply(function(x) ifelse(is.null(dat[[1]]$LineSectionName$Zh_tw), NA, dat[[1]]$LineSectionName$Zh_tw), c(1:length(dat)))),
                           IsBranch=unlist(mapply(function(x) dat[[x]]$IsBranch, c(1:length(dat)))))
      rail_station_line=left_join(rail_station_line, rail_line, by=c("LineID"))%>%
        dplyr::select(LineID, LineName, LineSectionName, IsBranch, Sequence, StationID, StationName)
    }
  }else{
    rail_station_line=histo_data(access_token, "Rail", "StationOfLine", operator, dates)
  }

  cat(paste0("#---", operator, " Station of Line Downloaded---#\n"))

  if(nchar(out)!=0 & out!=F){
    write.csv(rail_station_line, out, row.names=F)
  }
  return(rail_station_line)
}



#' @export
Rail_Station=function(access_token, operator, dates=F, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(dates==F){
    if(operator=="TRA"){
      url="https://tdx.transportdata.tw/api/basic/v2/Rail/TRA/Station?&%24format=JSON"
    }else if(operator=="THSR"){
      url="https://tdx.transportdata.tw/api/basic/v2/Rail/THSR/Station?&%24format=JSON"
    }else if(operator=="AFR"){
      url=paste0("https://tdx.transportdata.tw/api/basic/v3/Rail/AFR/Station?&%24format=JSON")
    }else if(operator %in% TDX_Railway$Code){
      url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/Metro/Station/", operator, "?&%24format=JSON")
    }else{
      print(TDX_Railway)
      stop(paste0("'", operator, "' is not valid operator. Please check out the table of railway code above."))
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      if(operator=="AFR"){
        rail_station=fromJSON(content(x, as="text"))$Stations
      }else{
        rail_station=fromJSON(content(x, as="text"))
      }
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })

    rail_station$StationName=rail_station$StationName$Zh_tw
    rail_station$PositionLon=rail_station$StationPosition$PositionLon
    rail_station$PositionLat=rail_station$StationPosition$PositionLat

    if (operator=="TRA"){
      rail_station=rail_station[, c("StationUID","StationID","StationName","StationClass","StationAddress","StationPhone","LocationCity","LocationCityCode","LocationTown","LocationTownCode","PositionLon","PositionLat")]
    }else if(operator=="THSR"){
      rail_station=rail_station[, c("StationUID","StationID","StationCode","StationName","StationAddress","LocationCity","LocationCityCode","LocationTown","LocationTownCode","PositionLon","PositionLat")]
    }else if(operator=="AFR"){
      rail_station=rail_station[, c("StationUID","StationID","StationClass","ReservationCode","StationName","StationAddress","StationPhone","PositionLon","PositionLat")]
    }else{
      rail_station=rail_station[, c("StationUID","StationID","StationName","StationAddress","BikeAllowOnHoliday","LocationCity","LocationCityCode","LocationTown","LocationTownCode","PositionLon","PositionLat")]
    }
  }else{
    rail_station=histo_data(access_token, "Rail", "Station", operator, dates)
  }

  cat(paste0("#---", operator, " Station Downloaded---#\n"))

  if (dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(rail_station, out, row.names=F)
    }
  }else if (dtype=="sf"){
    rail_station$geometry=st_as_sfc(ifelse(!is.na(rail_station$PositionLon), paste0("POINT(", rail_station$PositionLon, " ", rail_station$PositionLat, ")"), "POINT EMPTY"))
    rail_station=st_sf(rail_station, crs=4326)

    if(grepl(".shp", out) & out!=F){
      write_sf(rail_station, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(rail_station)
}



#' @export
Rail_Shape=function(access_token, operator, dates=F, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(dates==F){
    if(operator=="TRA"){
      url="https://tdx.transportdata.tw/api/basic/v3/Rail/TRA/Shape?%24format=JSON"
    }else if(operator=="THSR"){
      url="https://tdx.transportdata.tw/api/basic/v2/Rail/THSR/Shape?%24format=JSON"
    }else if(operator=="AFR"){
      stop("AFR does not provide route geometry data up to now! Please check out other rail system.")
    }else if(operator %in% TDX_Railway$Code){
      url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/Metro/Shape/", operator, "?&%24format=JSON")
    }else{
      print(TDX_Railway)
      stop(paste0("'", operator, "' is not valid operator. Please check out the table of railway code above"))
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      if(operator %in% c("TRA","AFR")){
        rail_shape=fromJSON(content(x, as="text"))$Shapes
      }else{
        rail_shape=fromJSON(content(x, as="text"))
      }
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })

    rail_shape$LineName=rail_shape$LineName$Zh_tw
    rail_shape=rename(rail_shape, geometry=Geometry)%>%
      dplyr::select(LineID, LineName, geometry)
  }else{
    rail_shape=histo_data(access_token, "Rail", "Shape", operator, dates)
    rail_shape=rename(rail_shape, geometry=Geometry)
  }

  cat(paste0("#---", operator, " Shape Downloaded---#\n"))

  if(dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(rail_shape, out, row.names=F)
    }
  }else if(dtype=="sf"){
    rail_shape$geometry=st_as_sfc(rail_shape$geometry)
    rail_shape=st_sf(rail_shape, crs=4326)

    if(grepl(".shp", out) & out!=F){
      write_sf(rail_shape, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(rail_shape)
}



#' @export
Bike_Station=function(access_token, county, dates=F, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(dates==F){
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Bike/Station/City/", county, "?&$format=JSON")
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      bike_station=fromJSON(content(x, as="text"))
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })
    if("Message" %in% names(bike_station)){
      if(county %in% TDX_County$Code){
        stop(paste0("'",county, "' has no bike sharing system or data is not avaliable.\n", bike_station$Message))
      }
      else{
        print(TDX_County)
        stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
      }
    }

    bike_station$StationName=bike_station$StationName$Zh_tw
    bike_station$StationAddress=bike_station$StationAddress$Zh_tw
    bike_station$PositionLon=bike_station$StationPosition$PositionLon
    bike_station$PositionLat=bike_station$StationPosition$PositionLat
    col_req=c("StationUID", "StationID", "StationName", "PositionLon", "PositionLat", "StationAddress", "BikesCapacity", "ServiceType")
    col_req=col_req[col_req %in% names(bike_station)]
    bike_station=bike_station[, col_req]
  }else{
    bike_station=histo_data(access_token, "Bike", "Station", county, dates)
  }

  if (dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(bike_station, out, row.names=F)
    }
  }else if (dtype=="sf"){
    bike_station$geometry=paste0("POINT(", bike_station$PositionLon, " ", bike_station$PositionLat, ")")
    bike_station$geometry=st_as_sfc(bike_station$geometry)
    bike_station=st_sf(bike_station, crs=4326)

    if (grepl(".shp", out) & out!=F){
      write_sf(bike_station, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(bike_station)
}



#' @export
Geocoding=function(access_token, address, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(sf)) install.packages("sf")
  if (!require(urltools)) install.packages("urltools")
  if (!require(httr)) install.packages("httr")
  if (!require(cli)) install.packages("cli")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  # pb=progress_bar$new(format="(:spin) [:bar] :percent  ", total=length(address), clear=F, width=80)
  cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}]", total=length(address))
  address_record=data.frame()
  record_fail=c()

  for (i in c(1:length(address))){
    cli_progress_update()
    nexti=F
    while (!nexti){
      tryCatch({
        address_temp=gsub("?@", 1, address[i])%>%
          gsub("\\/", "", .) %>%
          url_encode()

        url=paste0("https://tdx.transportdata.tw/api/advanced/V3/Map/GeoCode/Coordinate/Address/", address_temp, "?&$format=JSON")
        add_temp=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))%>%
          content()

        if (length(add_temp)==0){
          cli_alert_info(paste0("CANNOT Geocode ", AddressOriginal=address[i], "\n"))
          record_fail=c(record_fail, address[i])
        }else if (length(add_temp)==14){
          stop("Your access token is invalid!")
        }else{
          add_temp=data.frame(AddressOriginal=address[i],
                              AddressNew=add_temp[[1]]$Address,
                              geometry=add_temp[[1]]$Geometry)
          address_record=rbind(address_record, add_temp)
        }
        nexti=T

      }, error=function(err){
        if (grepl("externalptr", conditionMessage(err))){
          cat(paste0("Reconnect!!\n"))
          nexti=F
        }else if(grepl("subscript out of bounds", conditionMessage(err))){
          cli_alert_info(paste0("CANNOT Geocode ", AddressOriginal=address[i], "\n"))
          record_fail=c(record_fail, address[i])
          nexti=T
        }else{
          stop("Your access token is invalid!")
        }
      })
    }
  }
  cli_progress_done()

  datanum_ori=nrow(address_record)
  address_record=distinct(address_record)
  datanum_rev=nrow(address_record)

  cat("Geocoding Summary",
      paste0("Total:      ", length(address)),
      paste0("Success:    ", datanum_rev),
      paste0("Duplicated: ", datanum_ori-datanum_rev),
      paste0("Fail:       ", length(record_fail)),
      sep="\n")

  if (dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(address_record, out, row.names=F)
    }
  }else if (dtype=="sf"){
    address_record$geometry=st_as_sfc(address_record$geometry)
    address_record=st_sf(address_record, crs=4326)

    if (grepl(".shp", out) & out!=F){
      write_sf(address_record, out, layer_options="ENCODING=UTF-8")
    }else if (!(grepl(".shp", out)) & out!=F){
      stop("The file name must contain '.shp'\n")
    }
  }
  return(list(SUCCESS=address_record, FAIL=record_fail))
}



#' @export
Road_Network=function(access_token, county, roadclass, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(sf)) install.packages("sf")
  if (!require(httr)) install.packages("httr")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if (county %in% c(TDX_County$Code[1:22], "ALL") & roadclass %in% c(0,1,3,"ALL")){
    if (county!="ALL" & roadclass=="ALL"){
      # road of specific county and all road class
      url=paste0("https://tdx.transportdata.tw/api/basic/V3/Map/Road/Network/City/", county, "?&%24format=JSON")
      skip_flag=F
    }else if (county!="ALL" & roadclass %in% c(0,1,3)){
      # road of specific county and specific road class
      url=paste0("https://tdx.transportdata.tw/api/basic/V3/Map/Road/Network/City/", county, "?%24filter=RoadClass%20eq%20%27", roadclass, "%27&%24format=JSON")
      skip_flag=F
    }else if (county=="ALL" & roadclass %in% c(0,1,3)){
      # road of all county and specific road class
      url=paste0("https://tdx.transportdata.tw/api/basic/V3/Map/Road/Network/RoadClass/", roadclass, "?&$format=JSON")
      skip_flag=F
    }else if (county=="ALL" & roadclass=="ALL"){
      # road of all county and all road class
      road=data.frame()
      for (i in c(0,1,3)){
        url=paste0("https://tdx.transportdata.tw/api/basic/V3/Map/Road/Network/RoadClass/", i, "?&$format=JSON")
        x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

        tryCatch({
          temp=fromJSON(content(x, as="text"))
        }, error=function(err){
          stop(paste0("Your access token is invalid!"))
        })
        temp=temp[, c("RoadClass","RoadClassName","RoadID","RoadName","RoadNameID","Geometry")]%>%
          rename(geometry=Geometry)

        road=rbind(road, temp)
        cat(paste0("Road Class ", i, " Downloaded\n"))
        skip_flag=T
      }
    }

    if(skip_flag==F){
      x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

      tryCatch({
        road=fromJSON(content(x, as="text"))
      }, error=function(err){
        stop(paste0("Your access token is invalid!"))
      })

      if(class(road)=="data.frame"){
        road=road[, c("RoadClass","RoadClassName","RoadID","RoadName","RoadNameID","Geometry")]%>%
          rename(geometry=Geometry)
      }else{
        stop(paste0("'", county, " has no RoadClass ", roadclass))
      }
    }

  }else if(!(county %in% c(TDX_County$Code[1:22], "ALL"))){
    print(c(TDX_County$Code[1:22], "ALL"))
    stop(paste0("'", county, "' is invalid county code. Please check out the parameter table above."))
  }else if(!(RoadClass %in% c(0,1,3,"ALL"))){
    print(TDX_RoadClass)
    stop(paste0("'", RoadClass, "' is invalid RoadClass code. Please check out the parameter table above."))
  }

  if (dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(road, out, row.names=F)
    }
  }else if (dtype=="sf"){
    road$geometry=st_as_sfc(road$geometry)
    road=st_sf(road, crs=4326)

    if (grepl(".shp", out) & out!=F){
      write_sf(road, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(road)
}



#' @export
Rail_TimeTable=function(access_token, operator, record, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if (record=="station"){
    if (operator=="TRA"){
      url="https://tdx.transportdata.tw/api/basic/v3/Rail/TRA/GeneralStationTimetable?&$format=JSON"
    }else if (operator=="THSR"){
      stop("THSR does not provide 'station' time table up to now! Please use 'general' time table.")
    }else if (operator %in% c("TRTC","KRTC","TYMC","NTDLRT","KLRT")){
      url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/Metro/StationTimeTable/", operator, "?&%24format=JSON")
    }else if (operator %in% c("TMRT","AFR")){
      stop(paste0(operator, " does not provide 'station' time table up to now! Please check out other rail system."))
    }else{
      print(TDX_Railway)
      stop(paste0("'", operator, "' is not valid operator. Please check out the table of railway code above."))
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      if(operator=="TRA"){
        data_all=fromJSON(content(x, as="text", encoding="UTF-8"))$StationTimetables
      }else{
        data_all=fromJSON(content(x, as="text"))
      }
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })

    if (operator=="TRA"){
      data_all$StationName=data_all$StationName$Zh_tw
      station=data_all[, c("StationID","StationName","Direction")]%>%
        cbind(data_all$ServiceDay)

      rail_timetable_temp=data.frame(Sequence=unlist(mapply(function(x) data_all$Timetables[[x]]$Sequence, c(1:nrow(data_all)))),
                                     TrainNo=unlist(mapply(function(x) data_all$Timetables[[x]]$TrainNo, c(1:nrow(data_all)))),
                                     DestinationStationID=unlist(mapply(function(x) data_all$Timetables[[x]]$DestinationStationID, c(1:nrow(data_all)))),
                                     DestinationStationName=unlist(mapply(function(x) data_all$Timetables[[x]]$DestinationStationName$Zh_tw, c(1:nrow(data_all)))),
                                     TrainTypeID=unlist(mapply(function(x) data_all$Timetables[[x]]$TrainTypeID, c(1:nrow(data_all)))),
                                     TrainTypeCode=unlist(mapply(function(x) data_all$Timetables[[x]]$TrainTypeCode, c(1:nrow(data_all)))),
                                     TrainTypeName=unlist(mapply(function(x) data_all$Timetables[[x]]$TrainTypeName$Zh_tw, c(1:nrow(data_all)))),
                                     ArrivalTime=unlist(mapply(function(x) data_all$Timetables[[x]]$ArrivalTime, c(1:nrow(data_all)))),
                                     DepartureTime=unlist(mapply(function(x) data_all$Timetables[[x]]$DepartureTime, c(1:nrow(data_all)))))
    }else if (operator %in% c("TRTC","KRTC","TYMC","NTDLRT","KLRT")){
      data_all$StationName=data_all$StationName$Zh_tw
      data_all$DestinationStationName=data_all$DestinationStationName$Zh_tw
      station=data_all[, c("RouteID","LineID","StationID","StationName","Direction","DestinationStaionID","DestinationStationName")]%>%
        cbind(data_all$ServiceDay)

      rail_timetable_temp=data.frame(Sequence=unlist(mapply(function(x) data_all$Timetables[[x]]$Sequence, c(1:nrow(data_all)))),
                                     ArrivalTime=unlist(mapply(function(x) data_all$Timetables[[x]]$ArrivalTime, c(1:nrow(data_all)))),
                                     DepartureTime=unlist(mapply(function(x) data_all$Timetables[[x]]$DepartureTime, c(1:nrow(data_all)))))
    }
    num_of_table=mapply(function(x) nrow(data_all$Timetables[[x]]), c(1:nrow(data_all)))
    station=station[rep(c(1:nrow(station)), num_of_table), ]
    rail_timetable=cbind(station, rail_timetable_temp)
  }else if (record=="general"){
    if (operator=="TRA"){
      url="https://tdx.transportdata.tw/api/basic/v3/Rail/TRA/GeneralTrainTimetable?&$format=JSON"
    }else if (operator=="THSR"){
      url="https://tdx.transportdata.tw/api/basic/v2/Rail/THSR/GeneralTimetable?&$format=JSON"
    }else if (operator %in% c("TRTC","KRTC","TYMC","NTDLRT","KLRT","TMRT")){
      stop("MRT system does not provide 'general' time table up to now! Please use 'station' time table.")
    }else if (operator=="AFR"){
      url="https://tdx.transportdata.tw/api/basic/v3/Rail/AFR/GeneralTrainTimetable?&%24format=JSON"
    }else{
      print(TDX_Railway)
      stop(paste0("'", operator, "' is not valid operator. Please check out the table of railway code above"))
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      if(operator %in% c("TRA","AFR")){
        data_all=fromJSON(content(x, as="text"))$TrainTimetables
      }else{
        data_all=fromJSON(content(x, as="text"))$GeneralTimetable
      }
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })

    if (operator %in% c("TRA","AFR")){
      tra_info=cbind(data_all$TrainInfo, data_all$ServiceDay)
      tra_info$TrainTypeName=tra_info$TrainTypeName$Zh_tw
      tra_info$StartingStationName=tra_info$StartingStationName$Zh_tw
      tra_info$EndingStationName=tra_info$EndingStationName$Zh_tw
      rail_timetable_temp=data.frame(StopSequence=unlist(mapply(function(x) data_all$StopTimes[[x]]$StopSequence, c(1:nrow(data_all)))),
                                     StationID=unlist(mapply(function(x) data_all$StopTimes[[x]]$StationID, c(1:nrow(data_all)))),
                                     StationName=unlist(mapply(function(x) data_all$StopTimes[[x]]$StationName$Zh_tw, c(1:nrow(data_all)))),
                                     ArrivalTime=unlist(mapply(function(x) data_all$StopTimes[[x]]$ArrivalTime, c(1:nrow(data_all)))),
                                     DepartureTime=unlist(mapply(function(x) data_all$StopTimes[[x]]$DepartureTime, c(1:nrow(data_all)))))

      num_of_station=mapply(function(x) nrow(data_all$StopTimes[[x]]), c(1:nrow(data_all)))
      tra_info=tra_info[rep(c(1:nrow(tra_info)), num_of_station), ]
      rail_timetable=cbind(tra_info, rail_timetable_temp)
    }else if (operator=="THSR"){
      temp=data.frame(TrainNo=data_all$GeneralTrainInfo$TrainNo,
                      Direction=data_all$GeneralTrainInfo$Direction,
                      StartingStationID=data_all$GeneralTrainInfo$StartingStationID,
                      EndingStationID=data_all$GeneralTrainInfo$EndingStationID)
      tra_info=cbind(temp, data_all$ServiceDay)

      rail_timetable_temp=data.frame(StopSequence=unlist(mapply(function(x) data_all$StopTimes[[x]]$StopSequence, c(1:nrow(data_all)))),
                                     StationID=unlist(mapply(function(x) data_all$StopTimes[[x]]$StationID, c(1:nrow(data_all)))),
                                     StationName=unlist(mapply(function(x) data_all$StopTimes[[x]]$StationName$Zh_tw, c(1:nrow(data_all)))),
                                     ArrivalTime=unlist(mapply(function(x) data_all$StopTimes[[x]]$ArrivalTime, c(1:nrow(data_all)))),
                                     DepartureTime=unlist(mapply(function(x) data_all$StopTimes[[x]]$DepartureTime, c(1:nrow(data_all)))))

      num_of_station=mapply(function(x) nrow(data_all$StopTimes[[x]]), c(1:nrow(data_all)))
      tra_info=tra_info[rep(c(1:nrow(tra_info)), num_of_station), ]
      rail_timetable=cbind(tra_info, rail_timetable_temp)
    }
  }else{
    stop("'", record, "' is not valid format of timetable. Please use 'station' or 'general'.")
  }

  if (nchar(out)!=0 & out!=F){
    write.csv(rail_timetable, out, row.names=F)
  }
  return(rail_timetable)
}



#' @export
Rail_TravelTime=function(access_token, operator, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(operator %in% c("TRTC","KRTC","TYMC","NTDLRT","TMRT","KLRT")){
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/Metro/S2STravelTime/", operator, "?%24format=JSON")
  }else{
    stop(paste0("'",operator, "' travel time data is not avaliable.\n"))
  }

  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    rail_info=fromJSON(content(x, as="text"))
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })
  if("Message" %in% names(rail_info)){
    if(operator %in% TDX_Railway$Code){
      stop(paste0("'",operator, "' travel time data is not avaliable.\n", rail_info$Message))
    }
    else{
      print(TDX_Railway)
      stop(paste0("City: '", operator, "' is not valid. Please check out the parameter table above."))
    }
  }

  travel_time=data.frame(Sequence=unlist(mapply(function(x) list(rail_info$TravelTimes[[x]]$Sequence), c(1:nrow(rail_info)))),
                         FromStationID=unlist(mapply(function(x) list(rail_info$TravelTimes[[x]]$FromStationID), c(1:nrow(rail_info)))),
                         FromStationName=unlist(mapply(function(x) list(rail_info$TravelTimes[[x]]$FromStationName$Zh_tw), c(1:nrow(rail_info)))),
                         ToStationID=unlist(mapply(function(x) list(rail_info$TravelTimes[[x]]$ToStationID), c(1:nrow(rail_info)))),
                         ToStationName=unlist(mapply(function(x) list(rail_info$TravelTimes[[x]]$ToStationName$Zh_tw), c(1:nrow(rail_info)))),
                         RunTime=unlist(mapply(function(x) list(rail_info$TravelTimes[[x]]$RunTime), c(1:nrow(rail_info)))),
                         StopTime=unlist(mapply(function(x) ifelse(is.null(rail_info$TravelTimes[[x]]$StopTime), list(rep(NA, times=nrow(rail_info$TravelTimes[[x]]))), list(rail_info$TravelTimes[[x]]$StopTime)), c(1:nrow(rail_info)))))

  num_of_s2s=mapply(function(x) nrow(rail_info$TravelTimes[[x]]), c(1:nrow(rail_info)))
  req_col=c("LineNo","LineID","RouteID","TrainType")
  req_col=req_col[req_col %in% names(rail_info)]
  rail_info=rail_info[rep(c(1:nrow(rail_info)), times=num_of_s2s), req_col]

  travel_time=cbind(rail_info, travel_time)
  row.names(travel_time)=NULL

  if (nchar(out)!=0 & out!=F){
    write.csv(travel_time, out, row.names=F)
  }
  return(travel_time)
}



#' @export
Rail_StationExit=function(access_token, operator, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(sf)) install.packages("sf")
  if (!require(httr)) install.packages("httr")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(operator %in% c("TRTC","KRTC","TYMC","NTDLRT","TMRT","KLRT")){
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/Metro/StationExit/", operator, "?%24format=JSON")
  }else if(operator=="TRA"){
    url=paste0("https://tdx.transportdata.tw/api/basic/v3/Rail/", operator, "/StationExit/?%24format=JSON")
  }else{
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/", operator, "/StationExit/?%24format=JSON")
  }
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    if(operator=="TRA"){
      rail_info=fromJSON(content(x, as="text"))$StationExits
    }else{
      rail_info=fromJSON(content(x, as="text"))
    }
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })

  if("MESSAGE" %in% toupper(names(rail_info))){
    if(operator %in% TDX_Railway$Code){
      stop(paste0("'",operator, "' station exit data is not avaliable.\n", rail_info$Message))
    }
    else{
      print(TDX_Railway)
      stop(paste0("City: '", operator, "' is not valid. Please check out the parameter table above."))
    }
  }

  if(operator=="TRA"){
    station_exit=data.frame(ExitID=unlist(mapply(function(x) list(rail_info$Exits[[x]]$ExitID), c(1:nrow(rail_info)))),
                            ExitName=unlist(mapply(function(x) list(rail_info$Exits[[x]]$ExitName$Zh_tw), c(1:nrow(rail_info)))),
                            PositionLon=unlist(mapply(function(x) ifelse(is.null(rail_info$Exits[[x]]$ExitPosition$PositionLon), list(rep(NA, times=nrow(rail_info$Exits[[x]]))), list(rail_info$Exits[[x]]$ExitPosition$PositionLon)), c(1:nrow(rail_info)))),
                            PositionLat=unlist(mapply(function(x) ifelse(is.null(rail_info$Exits[[x]]$ExitPosition$PositionLat), list(rep(NA, times=nrow(rail_info$Exits[[x]]))), list(rail_info$Exits[[x]]$ExitPosition$PositionLat)), c(1:nrow(rail_info)))),
                            LocationDescription=unlist(mapply(function(x) list(rail_info$Exits[[x]]$LocationDescription), c(1:nrow(rail_info)))),
                            Stair=unlist(mapply(function(x) list(rail_info$Exits[[x]]$Stair), c(1:nrow(rail_info)))),
                            Escalator=unlist(mapply(function(x) list(rail_info$Exits[[x]]$Escalator), c(1:nrow(rail_info)))),
                            Elevator=unlist(mapply(function(x) list(rail_info$Exits[[x]]$Escalator), c(1:nrow(rail_info)))))
    num_of_exit=mapply(function(x) nrow(rail_info$Exits[[x]]), c(1:nrow(rail_info)))
    rail_info$StationName=rail_info$StationName$Zh_tw
    station_exit=cbind(rail_info[rep(c(1:nrow(rail_info)), num_of_exit), c("StationID","StationName")], station_exit)
    row.names(station_exit)=NULL
  }else{
    station_exit=data.frame(StationID=rail_info$StationID,
                            StationName=rail_info$StationName$Zh_tw,
                            ExitID=rail_info$ExitID,
                            ExitName=rail_info$ExitName$Zh_tw,
                            PositionLon=rail_info$ExitPosition$PositionLon,
                            PositionLat=rail_info$ExitPosition$PositionLat,
                            LocationDescription=unlist(ifelse(is.null(rail_info$LocationDescription), list(rep(NA, nrow(rail_info))), list(rail_info$LocationDescription))),
                            Stair=unlist(ifelse(is.null(rail_info$Stair), list(rep(NA, nrow(rail_info))), list(rail_info$Stair))),
                            Escalator=unlist(ifelse(is.null(rail_info$Escalator), list(rep(NA, nrow(rail_info))), list(rail_info$Escalator))),
                            Elevator=unlist(ifelse(is.null(rail_info$Elevator), list(rep(NA, nrow(rail_info))), list(rail_info$Elevator))))
  }

  if (dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(station_exit, out, row.names=F)
    }
  }else if (dtype=="sf"){
    station_exit$geometry=st_as_sfc(ifelse(!is.na(station_exit$PositionLon), paste0("POINT(", station_exit$PositionLon, " ", station_exit$PositionLat, ")"), "POINT EMPTY"))
    station_exit=st_sf(station_exit, crs=4326)

    if (grepl(".shp", out) & out!=F){
      write_sf(station_exit, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(station_exit)
}



#' @export
Bike_Shape=function(access_token, county, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  url=paste0("https://tdx.transportdata.tw/api/basic/v2/Cycling/Shape/City/", county, "?&$format=JSON")
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    bike_shape=fromJSON(content(x, as="text"))
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })
  if("Message" %in% names(bike_shape) | length(bike_shape)==0){
    if(county %in% TDX_County$Code){
      stop(paste0("'",county, "' has no cycling path or data is not avaliable.\n", bike_shape$Message))
    }
    else{
      print(TDX_County)
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }
  }

  if(dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(bike_shape, out, row.names=F)
    }
  }else if (dtype=="sf"){
    bike_shape=rename(bike_shape, geometry=Geometry)
    bike_shape$geometry=st_as_sfc(bike_shape$geometry)
    bike_shape=st_sf(bike_shape, crs=4326)

    if (grepl(".shp", out) & out!=F){
      write_sf(bike_shape, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(bike_shape)
}



#' @export
Air_Schedule=function(access_token, domestic=T, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if (domestic){
    url="https://tdx.transportdata.tw/api/basic/v2/Air/GeneralSchedule/Domestic?$format=JSON"
  }else{
    url="https://tdx.transportdata.tw/api/basic/v2/Air/GeneralSchedule/International?$format=JSON"
  }

  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    air_schedule=fromJSON(content(x, as="text"))
  }, error=function(err){
    cat(paste0("ERROR: ", conditionMessage(err), "\n"))
    stop(paste0("Your access token is invalid!"))
  })

  air_schedule$CodeShare_AirlineID=mapply(function(x) ifelse(is.null(air_schedule$CodeShare[[x]]$AirlineID), NA, paste(air_schedule$CodeShare[[x]]$AirlineID, collapse="|")), c(1:nrow(air_schedule)))
  air_schedule$CodeShare_FlightNumber=mapply(function(x) ifelse(is.null(air_schedule$CodeShare[[x]]$FlightNumber), NA, paste(air_schedule$CodeShare[[x]]$FlightNumber, collapse="|")), c(1:nrow(air_schedule)))
  air_schedule=dplyr::select(air_schedule, -CodeShare, -VersionID, -UpdateTime)

  if (nchar(out)!=0 & out!=F){
    write.csv(air_schedule, out, row.names=F)
  }
  return(air_schedule)
}



#' @export
Tourism=function(access_token, county, poi, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if (!(county %in% c(TDX_County$Code[1:22], "ALL"))){
    print(TDX_County[1:22,])
    stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
  }else if (!(poi %in% c("ScenicSpot","Restaurant","Hotel"))){
    stop(paste0("City: '", poi, "' is not valid. Please use the 'ScenicSpot', 'Restaurant', or 'Hotel'."))
  }

  if (county=="ALL"){
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Tourism/", poi, "?&%24format=JSON")
  }else{
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Tourism/", poi, "/", county, "?&$format=JSON")
  }
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    poidf=fromJSON(content(x, as="text"))
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })

  if(length(poidf)==0){
    stop(paste0("'", poi, "' data of '", county, "' is not avaliable.\n"))
  }else{
    poidf=cbind(poidf, poidf$Position)
    if(poi=="ScenicSpot"){
      req_col=c("ScenicSpotID","ScenicSpotName","DescriptionDetail","Description","Address","ZipCode","Phone","WebsiteUrl","OpenTime","Level","Class1","Class2","Class3","City","PositionLon","PositionLat","TravelInfo")
      req_col=req_col[req_col %in% names(poidf)]
      poidf=poidf[, req_col]
    }else if(poi=="Restaurant"){
      req_col=c("RestaurantID","RestaurantName","Description","Address","Phone","OpenTime","Class","City","PositionLon","PositionLat")
      req_col=req_col[req_col %in% names(poidf)]
      poidf=poidf[, req_col]
    }else if(poi=="Hotel"){
      req_col=c("HotelID","HotelName","Description","Address","ZipCode","Phone","Fax","WebsiteUrl","OpenTime","Class","Spec","ServiceInfo","ParkingInfo","Grade","City","PositionLon","PositionLat")
      req_col=req_col[req_col %in% names(poidf)]
      poidf=poidf[, req_col]
    }
  }

  if (dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(poidf, out, row.names=F)
    }
    return(poidf)
  }else if (dtype=="sf"){
    poidf$geometry=st_as_sfc(paste0("POINT(", poidf$PositionLon, " ", poidf$PositionLat, ")"))
    poidf=st_sf(poidf, crs=4326)

    if (grepl(".shp", out) & out!=F){
      write_sf(poidf, out, layer_options="ENCODING=UTF-8")
    }
    return(poidf)
  }
}



#' @export
Bus_TravelTime=function(access_token, county, routeid, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(data.table)) install.packages("data.table")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(cli)) install.packages("cli")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(county=="Tainan"){
    warning("Travel time data of Tainan retrieves all the routes. Parameter 'routeid' is muted.\n")
    url="https://tdx.transportdata.tw/api/basic/v3/Bus/S2STravelTime/City/Tainan?%24format=JSON"
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))
    tryCatch({
      subroute_info=fromJSON(content(x, as="text"))$S2STravelTimes
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })
    route_info=subroute_info[, c("RouteUID","RouteID","SubRouteUID","SubRouteID")]
    s2stimes=rbindlist(mapply(function(x) list(subroute_info$TravelTimes[[x]]), c(1:nrow(subroute_info))))
    num_of_s2s=mapply(function(x) nrow(subroute_info$TravelTimes[[x]]), c(1:nrow(subroute_info)))
    route_info=route_info[rep(c(1:nrow(route_info)), num_of_s2s),]
    traveltime_ALL=cbind(route_info, s2stimes)
  }else{
    routeid=unique(routeid)
    cat(paste0("Total: ", length(routeid), " Routes\n"))
    cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}]  {.emph RouteID: {routeid[pb_current]}}", total=length(routeid))
    num_of_nodata=0

    traveltime_ALL=data.frame()
    for (route in routeid){
      cli_progress_update()
      if (county=="Intercity"){
        url=paste0("https://tdx.transportdata.tw/api/basic/v2/Bus/S2STravelTime/InterCity/", route, "?&%24format=JSON")
      }else if(county %in% TDX_County$Code[1:22]){
        url=paste0("https://tdx.transportdata.tw/api/basic/v2/Bus/S2STravelTime/City/", county, "/", route, "?&%24format=JSON")
      }else{
        print(TDX_County[1:22,])
        stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
      }
      x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

      tryCatch({
        subroute_info=fromJSON(content(x, as="text"))
      }, error=function(err){
        stop(paste0("Your access token is invalid!"))
      })
      if(length(subroute_info)==0){
        cli_alert_info(paste0("Data of RouteID: ", route, " is not available."))
        num_of_nodata=num_of_nodata+1
        next
      }else{
        if(sum(lengths(subroute_info$TravelTimes))==0){
          cli_alert_info(paste0("Data of RouteID: ", route, " is not available."))
          num_of_nodata=num_of_nodata+1
          next
        }
        subroute_info=dplyr::select(subroute_info, -UpdateTime)
      }

      route_info=subroute_info[, c("RouteUID","RouteID","SubRouteUID","SubRouteID","Direction")]
      s2stimes=mapply(function(x) list(subroute_info$TravelTimes[[x]]$S2STimes), c(1:nrow(subroute_info)))

      week_info=data.frame(Weekday=unlist(mapply(function(x) list(subroute_info$TravelTimes[[x]]$Weekday), c(1:nrow(subroute_info)))),
                           StartHour=unlist(mapply(function(x) list(subroute_info$TravelTimes[[x]]$StartHour), c(1:nrow(subroute_info)))),
                           EndHour=unlist(mapply(function(x) list(subroute_info$TravelTimes[[x]]$EndHour), c(1:nrow(subroute_info)))))
      num_of_week_info=lengths(s2stimes)
      route_info=route_info[rep(c(1:nrow(route_info)), num_of_week_info),]
      route_info=cbind(route_info, week_info)

      num_of_s2s=unlist(mapply(function(y) list(mapply(function(x) nrow(s2stimes[[y]][[x]]), c(1:length(s2stimes[[y]])))), c(1:length(s2stimes))))
      route_info=route_info[rep(c(1:nrow(route_info)), num_of_s2s),]

      s2s_df=rbindlist(mapply(function(x) list(rbindlist(subroute_info$TravelTimes[[x]]$S2STimes)), c(1:nrow(subroute_info))))
      traveltime=cbind(route_info, s2s_df)
      row.names(traveltime)=NULL
      traveltime_ALL=rbind(traveltime_ALL, traveltime)
    }
    cli_alert_info(ifelse(num_of_nodata==0, "All Done!", paste0("All Done!\n", num_of_nodata, " RouteIDs have no data!")))
    cli_progress_done()
  }

  if (nchar(out)!=0 & out!=F){
    write.csv(traveltime_ALL, out, row.names=F)
  }
  return(traveltime_ALL)
}



#' @export
Rail_ODFare=function(access_token, operator, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(data.table)) install.packages("data.table")

  if (operator %in% c("TRA","THSR")){
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/", operator, "/ODFare?&%24format=JSON")
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))
  }else if(operator %in% c("TRTC","KRTC","TYMC","NTDLRT","TMRT","KLRT")){
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Rail/Metro/ODFare/", operator, "?&%24format=JSON")
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))
  }else if (operator=="AFR"){
    url=paste0("https://tdx.transportdata.tw/api/basic/v3/Rail/AFR/ODFare?&%24format=JSON")
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))
  }else{
    print(TDX_Railway)
    stop(paste0("'", operator, "' is not valid operator. Please check out the table of railway code above"))
  }

  tryCatch({
    if(operator=="AFR"){
      rail_info=fromJSON(content(x, as="text"))$ODFares
    }else{
      rail_info=fromJSON(content(x, as="text"))
    }
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })

  rail_info$OriginStationName=rail_info$OriginStationName$Zh_tw
  rail_info$DestinationStationName=rail_info$DestinationStationName$Zh_tw
  num_of_fare=mapply(function(x) nrow(rail_info$Fares[[x]]), c(1:nrow(rail_info)))
  rail_fare=rbindlist(rail_info$Fares)

  col_req=c("OriginStationID","OriginStationName","DestinationStationID","DestinationStationName","Direction","TrainType","TravelDistance")
  col_req=col_req[col_req %in% names(rail_info)]
  rail_info=rail_info[rep(c(1:nrow(rail_info)), num_of_fare), col_req]
  rail_fare=cbind(rail_info, rail_fare)

  if (nchar(out)!=0 & out!=F){
    write.csv(odfare, out, row.names=F)
  }
  return(rail_fare)
}



#' @export
Car_Park=function(access_token, county, street, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(!county %in% TDX_County$Code[1:22]){
    print(TDX_County[1:22,])
    stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
  }

  if(street=="off"){
    url=paste0("https://tdx.transportdata.tw/api/basic/v1/Parking/OffStreet/CarPark/City/", county, "?&%24format=JSON")
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      carpark=fromJSON(content(x, as="text"))$CarParks
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })
    if(is.null(carpark) | length(carpark)==0){
      stop(paste0("Car parking data of '", county, "' is not avaliable."))
    }

    carpark$CarParkName=carpark$CarParkName$Zh_tw
    carpark$CarParkShortName=carpark$CarParkShortName$Zh_tw
    carpark$ParkingTypes=mapply(function(x) paste(carpark$ParkingTypes[[x]], collapse="|"), c(1:nrow(carpark)))
    carpark$ParkingSiteTypes=mapply(function(x) paste(carpark$ParkingSiteTypes[[x]], collapse="|"), c(1:nrow(carpark)))
    carpark$ChargeTypes=mapply(function(x) paste(carpark$ChargeTypes[[x]], collapse="|"), c(1:nrow(carpark)))
    carpark=cbind(carpark, carpark$CarParkPosition)%>%
      select(-CarParkPosition)
    carpark$ParkingAreaID=mapply(function(x) paste(carpark$ParkingAreas[[x]]$ParkingAreaID, collapse="|"), c(1:nrow(carpark)))
    carpark$ParkingAreaName=mapply(function(x) paste(carpark$ParkingAreas[[x]]$ParkingAreaName$Zh_tw, collapse="|"), c(1:nrow(carpark)))
    if("ParkingAreas" %in% names(carpark)){
      carpark=dplyr::select(carpark, -ParkingAreas)
    }
  }else if(street=="on"){
    url=paste0("https://tdx.transportdata.tw/api/basic/v1/Parking/OnStreet/ParkingSpot/City/", county, "?%24format=JSON")
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      carpark=fromJSON(content(x, as="text"))$ParkingSegmentSpots
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })
    if(is.null(carpark) | length(carpark)==0){
      stop(paste0("Car parking data of '", county, "' is not avaliable."))
    }

    carpark=cbind(carpark, carpark$Position)%>%
      select(-Position)
  }else{
    stop("Parameter 'street' must be 'on' or 'off'.")
  }


  if (dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(carpark, out, row.names=F)
    }
    return(carpark)
  }else if (dtype=="sf"){
    if("Geometry" %in% names(carpark) & street=="on"){
      cat("Data provides 'POLYGON' geometry. Note that the 'POINT' geometry of the parking area are also retrieved.\n")
      carpark=rename(carpark, geometry=Geometry)
      carpark$geometry=st_as_sfc(carpark$geometry)
      carpark=st_sf(carpark, crs=3826)%>%
        st_transform(crs=4326)
    }else{
      cat("Data provides 'POINT' geometry.\n")
      carpark$geometry=st_as_sfc(paste0("POINT(", carpark$PositionLon, " ", carpark$PositionLat, ")"))
      carpark=st_sf(carpark, crs=4326)
    }

    if (grepl(".shp", out) & out!=F){
      write_sf(carpark, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(carpark)
}



#' @export
Ship_Port=function(access_token, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  url="https://tdx.transportdata.tw/api/basic/v3/Ship/Port?&%24format=JSON"
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    shipport=fromJSON(content(x, as="text"))$Ports
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })

  shipport=cbind(shipport, shipport$PortPosition)%>%
    dplyr::select(-PortPosition)
  shipport$PortName=shipport$PortName$Zh_tw

  if (dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(shipport, out, row.names=F)
    }
    return(shipport)
  }else if (dtype=="sf"){
    shipport$geometry=st_as_sfc(paste0("POINT(", shipport$PositionLon, " ", shipport$PositionLat, ")"))
    shipport=st_sf(shipport, crs=4326)

    if (grepl(".shp", out) & out!=F){
      write_sf(shipport, out, layer_options="ENCODING=UTF-8")
    }

    return(shipport)
  }
}


#' @export
Ship_Route=function(access_token, county, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  url=paste0("https://tdx.transportdata.tw/api/basic/v3/Ship/Route/Domestic/City/", county, "?&%24format=JSON")
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    shiproute=fromJSON(content(x, as="text"))
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })
  if("Message" %in% names(shiproute) | length(shiproute)==0){
    if(county %in% TDX_County$Code){
      stop(paste0("'",county, "' has no ship route or data is not avaliable.\n", shiproute$Message))
    }
    else{
      print(TDX_County)
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }
  }

  shiproute=shiproute$Routes
  shiproute$Operators=mapply(function(x) paste(shiproute$Operators[[x]], collapse="|"), c(1:nrow(shiproute)))
  shiproute$RouteName=shiproute$RouteName$Zh_tw
  shiproute$TicketPriceDescription=shiproute$TicketPriceDescription$Zh_tw

  if (nchar(out)!=0 & out!=F){
    write.csv(shiproute, out, row.names=F)
  }
  return(shiproute)
}



#' @export
Ship_StopOfRoute=function(access_token, county, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  url=paste0("https://tdx.transportdata.tw/api/basic/v3/Ship/StopOfRoute/Domestic/City/", county, "?&%24format=JSON")
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    stopofroute=fromJSON(content(x, as="text"))
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })
  if("Message" %in% names(stopofroute)){
    if(county %in% TDX_County$Code){
      stop(paste0("'",county, "' has no ship route or data is not avaliable.\n", stopofroute$Message))
    }
    else{
      print(TDX_County)
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }
  }

  stopofroute=stopofroute$StopOfRoutes
  if(length(stopofroute)==0){
    stop(paste0("'",county, "' has no ship route or data is not avaliable.\n", stopofroute$Message))
  }
  stopofroute$RouteName=stopofroute$RouteName$Zh_tw
  stopofroute$OperatorID=mapply(function(x) paste(stopofroute$Operators[[x]]$OperatorID, collapse="|"), c(1:nrow(stopofroute)))

  all_stop=bind_rows(mapply(function(x) list(stopofroute$Stops[[x]]), c(1:nrow(stopofroute))))
  all_stop$PortName=all_stop$PortName$Zh_tw
  num_of_stop=mapply(function(x) nrow(stopofroute$Stops[[x]]), c(1:nrow(stopofroute)))

  stopofroute=cbind(stopofroute[rep(c(1:nrow(stopofroute)), num_of_stop),], all_stop)%>%
    dplyr::select(-Operators, -Stops)%>%
    arrange( RouteID, Direction, StopSequence)

  if (nchar(out)!=0 & out!=F){
    write.csv(stopofroute, out, row.names=F)
  }
  return(stopofroute)
}



#' @export
Bus_RouteFare=function(access_token, county, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(data.table)) install.packages("data.table")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  cat("Please wait for a while...\n")
  if(county=="Intercity"){
    url="https://tdx.transportdata.tw/api/basic/v2/Bus/RouteFare/InterCity?&%24format=JSON"
  }else{
    url=paste0("https://tdx.transportdata.tw/api/basic/v2/Bus/RouteFare/City/", county, "?&%24format=JSON")
  }
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  tryCatch({
    bus_info=fromJSON(content(x, as="text"))
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })
  if("Message" %in% names(bus_info)){
    if(county %in% TDX_County$Code){
      stop(paste0("'",county, "' has no data avaliable.\n", bus_info$Message))
    }
    else{
      print(TDX_County)
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }
  }

  if(unique(bus_info$FarePricingType)==0){
    temp=mapply(function(x) bus_info$SectionFares[[x]]$BufferZones[[1]], c(1:nrow(bus_info)))
    route_buffer=data.frame(SectionSequence=unlist(mapply(function(x) temp[[x]]$SectionSequence, c(1:length(temp)))),
                            Direction=unlist(mapply(function(x) temp[[x]]$Direction, c(1:length(temp)))),
                            BZOStopID=unlist(mapply(function(x) temp[[x]]$FareBufferZoneOrigin$StopID, c(1:length(temp)))),
                            BZOStopName=unlist(mapply(function(x) temp[[x]]$FareBufferZoneOrigin$StopName, c(1:length(temp)))),
                            BZDStopID=unlist(mapply(function(x) temp[[x]]$FareBufferZoneDestination$StopID, c(1:length(temp)))),
                            BZDStopName=unlist(mapply(function(x) temp[[x]]$FareBufferZoneDestination$StopName, c(1:length(temp)))))
    num_of_buffer=lengths(mapply(function(x) temp[[x]]$SectionSequence, c(1:length(temp))))
    bus_info_bz=cbind(bus_info[rep(c(1:nrow(bus_info)), num_of_buffer), c("RouteID","RouteName","SubRouteID","SubRouteName")], route_buffer)

    temp=rbindlist(mapply(function(x) list(bus_info$SectionFares[[x]]$Fares[[1]]), c(1:nrow(bus_info))))
    num_of_fare=mapply(function(x) nrow(bus_info$SectionFares[[x]]$Fares[[1]]), c(1:nrow(bus_info)))
    bus_info_fare=cbind(bus_info[rep(c(1:nrow(bus_info)), num_of_fare), ], temp)%>%
      dplyr::select(-SectionFares, -UpdateTime)
  }else if(unique(bus_info$FarePricingType)==1){
    stop_info=data.frame(Direction=unlist(mapply(function(x) bus_info$ODFares[[x]]$Direction, c(1:nrow(bus_info)))),
                         OriginStopID=unlist(mapply(function(x) bus_info$ODFares[[x]]$OriginStop$StopID, c(1:nrow(bus_info)))),
                         OriginStopName=unlist(mapply(function(x) bus_info$ODFares[[x]]$OriginStop$StopName, c(1:nrow(bus_info)))),
                         DestinationStopID=unlist(mapply(function(x) bus_info$ODFares[[x]]$DestinationStop$StopID, c(1:nrow(bus_info)))),
                         DestinationStopName=unlist(mapply(function(x) bus_info$ODFares[[x]]$DestinationStop$StopName, c(1:nrow(bus_info)))))
    num_of_stop=unlist(mapply(function(x) ifelse(is.null(nrow(bus_info$ODFares[[x]])), 0, nrow(bus_info$ODFares[[x]])), c(1:nrow(bus_info))))

    stop_info=cbind(bus_info[rep(c(1:nrow(bus_info)), num_of_stop), c("RouteID","RouteName","OperatorID","OperatorNo","SubRouteID","SubRouteName","FarePricingType","IsFreeBus","IsForAllSubRoutes")], stop_info)
    bus_info=bus_info[num_of_stop!=0, ]

    fare_data=data.frame(TicketType=unlist(mapply(function(y) mapply(function(x) bus_info$ODFares[[y]]$Fares[[x]]$TicketType, c(1:nrow(bus_info$ODFares[[y]]))), c(1:nrow(bus_info)))),
                         FareClass=unlist(mapply(function(y) mapply(function(x) bus_info$ODFares[[y]]$Fares[[x]]$FareClass, c(1:nrow(bus_info$ODFares[[y]]))), c(1:nrow(bus_info)))),
                         Price=unlist(mapply(function(y) mapply(function(x) bus_info$ODFares[[y]]$Fares[[x]]$Price, c(1:nrow(bus_info$ODFares[[y]]))), c(1:nrow(bus_info)))))
    num_of_fare=unlist(mapply(function(y) mapply(function(x) nrow(bus_info$ODFares[[y]]$Fares[[x]]), c(1:nrow(bus_info$ODFares[[y]]))), c(1:nrow(bus_info))))

    bus_info_fare=cbind(stop_info[rep(c(1:nrow(stop_info)), num_of_fare),], fare_data)
  }else if (unique(bus_info$FarePricingType)==2){
    stage_info=data.frame(Direction=unlist(mapply(function(x) bus_info$StageFares[[x]]$Direction, c(1:nrow(bus_info)))),
                          OriginStage_StopID=unlist(mapply(function(x) bus_info$StageFares[[x]]$OriginStage$StopID, c(1:nrow(bus_info)))),
                          OriginStage_StopName=unlist(mapply(function(x) bus_info$StageFares[[x]]$OriginStage$StopName, c(1:nrow(bus_info)))),
                          OriginStage_Sequence=unlist(mapply(function(x) bus_info$StageFares[[x]]$OriginStage$Sequence, c(1:nrow(bus_info)))),
                          DestinationStage_StopID=unlist(mapply(function(x) bus_info$StageFares[[x]]$DestinationStage$StopID, c(1:nrow(bus_info)))),
                          DestinationStage_StopName=unlist(mapply(function(x) bus_info$StageFares[[x]]$DestinationStage$StopName, c(1:nrow(bus_info)))),
                          DestinationStage_Sequence=unlist(mapply(function(x) bus_info$StageFares[[x]]$DestinationStage$Sequence, c(1:nrow(bus_info)))))
    num_of_stage=mapply(function(x) nrow(bus_info$StageFares[[x]]), c(1:nrow(bus_info)))
    stage_info=cbind(bus_info[rep(c(1:nrow(bus_info)), num_of_stage), c("RouteID","RouteName","OperatorID","OperatorNo","SubRouteID","SubRouteName","FarePricingType","IsFreeBus","IsForAllSubRoutes")], stage_info)

    fare_data=data.frame(FareName=unlist(mapply(function(y) mapply(function(x) bus_info$StageFares[[y]]$Fares[[x]]$FareName, c(1:nrow(bus_info$StageFares[[y]]))), c(1:nrow(bus_info)))),
                         TicketType=unlist(mapply(function(y) mapply(function(x) bus_info$StageFares[[y]]$Fares[[x]]$TicketType, c(1:nrow(bus_info$StageFares[[y]]))), c(1:nrow(bus_info)))),
                         FareClass=unlist(mapply(function(y) mapply(function(x) bus_info$StageFares[[y]]$Fares[[x]]$FareClass, c(1:nrow(bus_info$StageFares[[y]]))), c(1:nrow(bus_info)))),
                         Price=unlist(mapply(function(y) mapply(function(x) bus_info$StageFares[[y]]$Fares[[x]]$Price, c(1:nrow(bus_info$StageFares[[y]]))), c(1:nrow(bus_info)))))
    num_of_fare=unlist(mapply(function(y) mapply(function(x) nrow(bus_info$StageFares[[y]]$Fares[[x]]), c(1:nrow(bus_info$StageFares[[y]]))), c(1:nrow(bus_info))))

    bus_info_fare=cbind(stage_info[rep(c(1:nrow(stage_info)), num_of_fare),], fare_data)
  }


  if (nchar(out)!=0 & out!=F){
    if(unique(bus_info$FarePricingType)==0){
      write.csv(bus_info_bz, paste0(gsub(".txt|.csv", "", out), "_BufferZone.", ifelse(grepl("csv", out), "csv", "txt")), row.names=F)
      write.csv(bus_info_fare, paste0(gsub(".txt|.csv", "", out), "_ZoneFare.", ifelse(grepl("csv", out), "csv", "txt")), row.names=F)
    }else{
      write.csv(bus_info_fare, out, row.names=F)
    }
  }

  row.names(bus_info_fare)=NULL
  if (unique(bus_info$FarePricingType)==0){
    row.names(bus_info_bz)=NULL
    warning("Please use '$BufferZone' to retrieve the buffer zone of bus route, and use '$ZoneFare' to  retrieve the fare of each buffer zone.")
    return(list(BufferZone=bus_info_bz, ZoneFare=bus_info_fare))
  }else{
    return(bus_info_fare)
  }
}



#' @export
Bike_Remain_His=function(access_token, county, dates, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(httr)) install.packages("httr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  url=paste0("https://tdx.transportdata.tw/api/historical/v2/Historical/Bike/Availability/", county, "?Dates=", dates, "&%24format=CSV")
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

  if(content(x, "text", encoding="UTF-8")==""){
    stop(paste0("'",county, "' has no bike sharing system or data is not avaliable.\n", bike_station$Message))
  }
  bike_remain=read.csv(textConnection(content(x, "text", encoding="UTF-8")), header=T)

  if("invalid.token" %in% names(bike_remain)){
    stop(paste0("Your access token is invalid!"))
  }else if("Message" %in% names(bike_remain)){
    if(grepl("2021-06-01", bike_remain$Message)){
      stop(paste0("Historical data can only be retrived after date '2021-06-01'!"))
    }else if(grepl("City", bike_remain$Message)){
      print(print(TDX_County[1:22,]))
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }else{
      stop(paste0("Date value is invalid! Format of the date:\n", "\nType\t\tFormat\t\t\tExample\n", paste0(rep("=", 61), collapse=""), "\nSingle Date\tYYYY-MM-DD\t\t2023-01-01\nMultiple Dates\tYYYY-MM-DD;YYYY-MM-DD\t2023-01-01;2023-02-01\nDate Range\tYYYY-MM-DD~YYYY-MM-DD\t2023-01-01~2023-01-31"))
    }
  }

  if (nchar(out)!=0 & out!=F){
    write.csv(bike_remain, out, row.names=F)
  }
  return(bike_remain)
}



#' @export
Freeway_Shape=function(geotype, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(xml2)) install.packages("xml2")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")

  if(!dtype %in% c("text","sf")){
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(geotype=="section"){
    # SectionID
    tryCatch({
      x=read_xml("https://tisvcloud.freeway.gov.tw/history/motc20/Section.xml")
    }, error=function(err){
      stop(paste0("Original data in database contains errors!"))
    })
    freeway_section=data.frame(SectionID=xml_text(xml_find_all(x, xpath = ".//d1:SectionID")),
                               SubAuthorityCode=xml_text(xml_find_all(x, xpath = ".//d1:SubAuthorityCode")),
                               SectionName=xml_text(xml_find_all(x, xpath = ".//d1:SectionName")),
                               RoadID=xml_text(xml_find_all(x, xpath = ".//d1:RoadID")),
                               RoadName=xml_text(xml_find_all(x, xpath = ".//d1:RoadName")),
                               RoadClass=xml_text(xml_find_all(x, xpath = ".//d1:RoadClass")),
                               RoadDirection=xml_text(xml_find_all(x, xpath = ".//d1:RoadDirection")),
                               RoadSection_start=xml_text(xml_find_all(x, xpath = ".//d1:Start")),
                               RoadSection_end=xml_text(xml_find_all(x, xpath = ".//d1:End")),
                               SectionLength=xml_text(xml_find_all(x, xpath = ".//d1:SectionLength")),
                               SectionMile_start=xml_text(xml_find_all(x, xpath = ".//d1:StartKM")),
                               SectionMile_end=xml_text(xml_find_all(x, xpath = ".//d1:EndKM")),
                               SpeedLimit=xml_text(xml_find_all(x, xpath = ".//d1:SpeedLimit")))

    # SectionID shape
    tryCatch({
      x=read_xml("https://tisvcloud.freeway.gov.tw/history/motc20/SectionShape.xml")
    }, error=function(err){
      stop(paste0("Original data in database contains errors!"))
    })
    freeway_section_shape=data.frame(SectionID=xml_text(xml_find_all(x, xpath = ".//d1:SectionID")),
                                     Geometry=xml_text(xml_find_all(x, xpath = ".//d1:Geometry")))
    freeway_shape=left_join(freeway_section, freeway_section_shape)%>%
      filter(!is.na(Geometry))

    if(dtype=="sf"){
      freeway_shape=mutate(freeway_shape, Geometry=st_as_sfc(Geometry))%>%
        st_sf(crs=4326)
    }

  }else if(geotype=="link"){
    # LinkID
    tryCatch({
      x=read_xml("https://tisvcloud.freeway.gov.tw/history/motc20/SectionLink.xml")
    }, error=function(err){
      stop(paste0("Original data in database contains errors!"))
    })
    freeway_shape=data.frame(SectionID=rep(xml_text(xml_find_all(x, xpath = ".//d1:SectionID")), times=xml_length(xml_find_all(x, xpath = ".//d1:LinkIDs"))),
                             LinkID=xml_text(xml_find_all(x, xpath = ".//d1:LinkID")))
    temp_id=paste0('"', freeway_shape$LinkID, '"')
    temp_id=paste0("[", paste(temp_id, collapse=","), "]")

    # LinkID
    x=POST("https://link.motc.gov.tw/v2/Road/Link/Shape/Geometry/WKT?$format=JSON",
           content_type("application/json"),
           body=temp_id)
    temp=content(x)
    temp=data.frame(LinkID=do.call(rbind, lapply(c(1:length(temp)), function (i) temp[[i]]$LinkID)),
                    Geometry=do.call(rbind, lapply(c(1:length(temp)), function (i) temp[[i]]$Geometry)))%>%
      distinct()
    freeway_shape=left_join(freeway_shape, temp)%>%
      filter(!(is.na(Geometry)))

    if(dtype=="sf"){
      freeway_shape=mutate(freeway_shape, Geometry=st_as_sfc(Geometry))%>%
        st_sf(crs=4326)
    }

  }else if(geotype=="gantry"){
    # GantryID
    x=read_xml("https://tisvcloud.freeway.gov.tw/history/motc20/ETag.xml")
    freeway_shape=data.frame(ETagGantryID=xml_text(xml_find_all(x, xpath = ".//d1:ETagGantryID")),
                             LinkID=xml_text(xml_find_all(x, xpath = ".//d1:LinkID")),
                             LocationType=xml_text(xml_find_all(x, xpath = ".//d1:LocationType")),
                             PositionLon=xml_text(xml_find_all(x, xpath = ".//d1:PositionLon")),
                             PositionLat=xml_text(xml_find_all(x, xpath = ".//d1:PositionLat")),
                             RoadID=xml_text(xml_find_all(x, xpath = ".//d1:RoadID")),
                             RoadName=xml_text(xml_find_all(x, xpath = ".//d1:RoadName")),
                             RoadClass=xml_text(xml_find_all(x, xpath = ".//d1:RoadClass")),
                             RoadDirection=xml_text(xml_find_all(x, xpath = ".//d1:RoadDirection")),
                             Start=xml_text(xml_find_all(x, xpath = ".//d1:RoadSection//d1:Start")),
                             End=xml_text(xml_find_all(x, xpath = ".//d1:RoadSection//d1:End")),
                             LocationMile=xml_text(xml_find_all(x, xpath = ".//d1:LocationMile")))

    if(dtype=="sf"){
      freeway_shape=mutate(freeway_shape, Geometry=st_as_sfc(paste0("POINT(", PositionLon, " ", PositionLat, ")")))%>%
        st_sf(crs=4326)
    }

  }else if(geotype=="gantryod"){
    x=read_xml("https://tisvcloud.freeway.gov.tw/history/motc20/ETagPair.xml")
    freeway_shape=data.frame(ETagPairID=xml_text(xml_find_all(x, xpath = ".//d1:ETagPairID")),
                             StartETagGantryID=xml_text(xml_find_all(x, xpath = ".//d1:StartETagGantryID")),
                             EndETagGantryID=xml_text(xml_find_all(x, xpath = ".//d1:EndETagGantryID")),
                             Description=xml_text(xml_find_all(x, xpath = ".//d1:Description")),
                             Distance=xml_text(xml_find_all(x, xpath = ".//d1:Distance")),
                             StartLinkID=xml_text(xml_find_all(x, xpath = ".//d1:StartLinkID")),
                             EndLinkID=xml_text(xml_find_all(x, xpath = ".//d1:EndLinkID")),
                             Geometry=xml_text(xml_find_all(x, xpath = ".//d1:Geometry")))

    if(dtype=="sf"){
      freeway_shape=mutate(freeway_shape, Geometry=st_as_sfc(Geometry))%>%
        st_sf(crs=4326)
    }
  }else if(geotype=="vd"){
    x=read_xml("https://tisvcloud.freeway.gov.tw/history/motc20/VD.xml")

    xml_node=c("VDID","SubAuthorityCode","BiDirectional","LinkID","Bearing","RoadDirection","LaneNum","ActualLaneNum","VDType",
               "LocationType","DetectionType","PositionLon","PositionLat","RoadID","RoadName","RoadClass","Start","End","LocationMile")

    freeway_shape=data.frame(temp_id=c(1:length(xml_find_all(x, xpath = ".//d1:VD"))))
    for (i in c(1:length(xml_node))){
      node_name=xml_node[i]
      temp=xml_text(xml_find_all(x, xpath=paste0(".//d1:", xml_node[i])))
      temp_id=grepl(node_name, xml_find_all(x, xpath=".//d1:VD"))
      temp_id=which(temp_id)
      temp=data.frame(temp_id, temp)
      colnames(temp)[2]=node_name
      freeway_shape=left_join(freeway_shape, temp, by="temp_id")
    }
    freeway_shape=dplyr::select(freeway_shape, -temp_id)

    if(dtype=="sf"){
      freeway_shape=mutate(freeway_shape, Geometry=st_as_sfc(paste0("POINT(", PositionLon, " ", PositionLat, ")")))%>%
        st_sf(crs=4326)
    }
  }else{
    stop("'geotype' must be 'section', 'link', 'gantry', or, 'gantryod'.\n")
  }

  if (dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(freeway_shape, out, row.names=F)
    }
  }else if (dtype=="sf"){
    if (grepl(".shp", out) & out!=F){
      write_sf(freeway_shape, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(freeway_shape)
}



#' @export
District_Shape=function(district, time=NULL, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(sf)) install.packages("sf")
  if (!require(archive)) install.packages("archive")
  if (!require(XML)) install.packages("XML")
  options(timeout=1000)

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(district %in% c("SA0","SA1","SA2")){
    all_data=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/statistical_area.csv")%>%
      filter(SA==district)
    if(!is.null(time)){
      if(nchar(time)!=7 | !grepl("-", time)){
        stop(paste0("Date format is valid! It should be 'YYYY-MM'!"))
      }
      time_num=as.numeric(unlist(strsplit(time, "-")))
      time_num=time_num[1]*12+time_num[2]
      all_data_temp=all_data[which.min(abs(all_data$TIME_NUM-time_num)),]
      if(all_data_temp$TIME_lab!=time){
        cat(paste0("Data ", time, " is not avaliable. Download data ", all_data_temp$TIME_lab, " instead.\nAll data available for ", district, " is listed below:\n"), paste(all_data$TIME_lab, collapse=", "), "\n")
      }else{
        cat(paste0("Download the data in ", all_data_temp$TIME_lab, ".\n"))
      }
    }else{
      all_data_temp=all_data[which.max(all_data$TIME_NUM),]
      cat(paste0("Download the latest data ", all_data_temp$TIME_lab, ".\nIf a specific time of data is required, please set the argument 'time'.\n"))
    }
    time_rev=all_data_temp$TIME
  }else if(district %in% c("County","Town","Village")){
    if(!is.null(time)){cat("Argument 'time' is deprecated.\n")}
  }else{
    stop(paste0("Argument 'district' should be 'County', 'Town', 'Village', 'SA0', 'SA1', or 'SA2'."))
  }

  if(district=="County"){
    url="https://maps.nlsc.gov.tw/download/%E7%B8%A3%E5%B8%82%E7%95%8C%E7%B7%9A(TWD97%E7%B6%93%E7%B7%AF%E5%BA%A6).zip"
  }else if(district=="Town"){
    url="https://maps.nlsc.gov.tw/download/%E9%84%89%E9%8E%AE%E5%B8%82%E5%8D%80%E7%95%8C%E7%B7%9A(TWD97%E7%B6%93%E7%B7%AF%E5%BA%A6).zip"
  }else if(district=="Village"){
    url="https://maps.nlsc.gov.tw/download/%E6%9D%91(%E9%87%8C)%E7%95%8C(TWD97_121%E5%88%86%E5%B8%B6).zip"
  }else if(district=="SA0"){
    url=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=B%2fAMiCXtTLpw0dsuDX3ECw%3d%3d&STTIME=", time_rev, "&STUNIT=null&BOUNDARY=%E5%85%A8%E5%9C%8B")
  }else if(district=="SA1"){
    url=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=KauzeQbZ0OwB7oPXscC47g%3d%3d&STTIME=", time_rev, "&STUNIT=null&BOUNDARY=%E5%85%A8%E5%9C%8B")
  }else if(district=="SA2"){
    url=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=w6DncCAc5Scpyqawns3cBg%3d%3d&STTIME=", time_rev, "&STUNIT=null&BOUNDARY=%E5%85%A8%E5%9C%8B")
  }

  unlink(list.files(tempdir(), full.names=T), recursive=T)
  download.file(url, paste0(tempdir(), "/shape.zip"), mode="wb", quiet=T)
  untar(paste0(tempdir(), "/shape.zip"), exdir=paste0(tempdir(), "/shape"))

  if(district %in% c("County","Town","Village")){
    dir_files=dir(paste0(tempdir(), "/shape"), full.names=T, recursive=T, pattern="shp")
    dir_files=dir_files[which.max(file.info(dir_files)$size)]
    district_shape=read_sf(dir_files)
    district_shape=st_transform(district_shape, crs=3826)
  }else{
    dir_files=dir(paste0(tempdir(), "/shape"), full.names=T, recursive=T, pattern="rar")
    # archive(dir_files)
    con=archive_read(dir_files)
    suppressWarnings({xml_file=readLines(con, ok=T)})
    close(con)
    xml_file=xmlParse(xml_file)
    saveXML(xml_file, file=paste0(tempdir(), "/xml_file.gml"), encoding="UTF-8")
    district_shape=read_sf(paste0(tempdir(), "/xml_file.gml"))
    towncode=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/TOWNCODE.csv", colClasses=c("COUNTYCODE"="character","TOWNCODE"="character"))

    if(district=="SA0"){
      colnames(district_shape)=c("temp0","SA0CODE","temp1","TOWNCODE","temp_TOWNNAME","COUNTYCODE","temp_COUNTYNAME","AREA","temp2","temp3","temp4","temp5","temp6","temp7","SA1CODE","SA2CODE","temp8","Population","Household","temp9","geometry")
    }else if(district=="SA1"){
      colnames(district_shape)=c("temp0","SA1CODE","temp1","TOWNCODE","temp_TOWNNAME","COUNTYCODE","temp_COUNTYNAME","AREA","temp2","temp3","temp4","temp5","temp6","temp7","temp8","temp9","SA2CODE","Population","Household","temp10","geometry")
    }else if(district=="SA2"){
      colnames(district_shape)=c("temp0","SA2CODE","temp1","TOWNCODE","temp_TOWNNAME","COUNTYCODE","temp_COUNTYNAME","AREA","temp2","temp3","temp4","temp5","temp6","temp7","temp8","temp9","temp10","Population","Household","temp10","geometry")
    }

    st_geometry(district_shape)="geometry"
    district_shape=district_shape[, !grepl("temp", names(district_shape))]
    district_shape$TOWNCODE=ifelse(nchar(district_shape$TOWNCODE)==7, paste0("0", district_shape$TOWNCODE), district_shape$TOWNCODE)
    district_shape$COUNTYCODE=ifelse(nchar(district_shape$COUNTYCODE)==4, paste0("0", district_shape$COUNTYCODE), district_shape$COUNTYCODE)
    district_shape=left_join(district_shape, towncode, by=c("TOWNCODE","COUNTYCODE"))
    district_shape=st_zm(district_shape, drop=T)
    district_shape=st_sf(district_shape, crs=3826)
  }


  if (grepl(".shp", out) & out!=F){
    write_sf(district_shape, out, layer_options="ENCODING=UTF-8")
  }
  return(district_shape)
}



#' @export
Population=function(district, time=NULL, age=F, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(urltools)) install.packages("urltools")
  if (!require(cli)) install.packages("cli")
  options(timeout=1000)

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(district %in% c("County","Town","Village","SA0","SA1","SA2")){
    all_data=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/pop_area_time.csv")%>%
      filter(SA==district, DATANAME==ifelse(age==T, "\u4e94\u6b72\u5e74\u9f61\u7d44\u6027\u5225\u4eba\u53e3\u7d71\u8a08", "\u4eba\u53e3\u7d71\u8a08"))
  }else{
    stop(paste0("Argument 'district' should be 'County', 'Town', 'Village', 'SA0', 'SA1', or 'SA2'."))
  }

  if(!is.null(time)){
    if(nchar(time)!=7 | !grepl("-", time)){
      stop(paste0("Date format is valid! It should be 'YYYY-MM'!"))
    }
    time_num=as.numeric(unlist(strsplit(time, "-")))
    time_num=time_num[1]*12+time_num[2]
    all_data_temp=all_data[which.min(abs(all_data$TIME_NUM-time_num)),]
    if(all_data_temp$TIME_lab!=time){
      cat(paste0("Data ", time, " is not avaliable. Download data ", all_data_temp$TIME_lab, " instead.\nAll data available for ", district, " is listed below:\n"), paste(all_data$TIME_lab, collapse=", "), "\n")
    }else{
      cat(paste0("Download the data in ", all_data_temp$TIME_lab, ".\n"))
    }
  }else{
    all_data_temp=all_data[which.max(all_data$TIME_NUM),]
    cat(paste0("Download the latest data ", all_data_temp$TIME_lab, ".\nIf a specific time of data is required, please set the argument 'time'.\n"))
  }
  time_rev=all_data_temp$TIME


  if(district=="County"){
    if(age){
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=7A2dE0lGbbiQg8ggGoTmLg%3d%3d&STTIME=", time_rev, "&STUNIT=U01CO&BOUNDARY=%E5%85%A8%E5%9C%8B")
    }else{
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=Aeiv7Oil4QUv76rbeaj3Hw%3d%3d&STTIME=", time_rev, "&STUNIT=U01CO&BOUNDARY=%E5%85%A8%E5%9C%8B")
    }
  }else if(district=="Town"){
    if(age){
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=ZA5vfYBRjOAOLs4Yfz3tCQ%3d%3d&STTIME=", time_rev, "&STUNIT=U01TO&BOUNDARY=%E5%85%A8%E5%9C%8B")
    }else{
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=QXVT3AX8GQy1NqiSsJGV4A%3d%3d&STTIME=", time_rev, "&STUNIT=U01TO&BOUNDARY=%E5%85%A8%E5%9C%8B")
    }
  }else if(district=="Village"){
    if(age){
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=ltVp3Seiz4UacOUlJ5WlGw%3d%3d&STTIME=", time_rev, "&STUNIT=U01VI&BOUNDARY=%E5%85%A8%E5%9C%8B")
    }else{
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=4pFoRRKEKSnTDaIyzlU4rQ%3d%3d&STTIME=", time_rev, "&STUNIT=U01VI&BOUNDARY=%E5%85%A8%E5%9C%8B")
    }
  }else if(district=="SA0"){
    if(age){
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=N6mbogBbiIhLpTay1Nfzeg%3d%3d&STTIME=", time_rev, "&STUNIT=U0200&BOUNDARY=", toupper(url_encode(unlist(strsplit(all_data_temp$SPACE, "\\|")))))
    }else{
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=N5l9H0sZ9JX%2fMQp8qSxyKg%3d%3d&STTIME=", time_rev, "&STUNIT=U0200&BOUNDARY=", toupper(url_encode(unlist(strsplit(all_data_temp$SPACE, "\\|")))))
    }
  }else if(district=="SA1"){
    if(age){
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=3FG6sa9njC%2fvY4%2f2tMYnpg%3d%3d&STTIME=", time_rev, "&STUNIT=U0201&BOUNDARY=", toupper(url_encode(unlist(strsplit(all_data_temp$SPACE, "\\|")))))
    }else{
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=yYLunxoEPygac%2fHXuXQi2w%3d%3d&STTIME=", time_rev, "&STUNIT=U0201&BOUNDARY=", toupper(url_encode(unlist(strsplit(all_data_temp$SPACE, "\\|")))))
    }
  }else if(district=="SA2"){
    if(age){
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=xbNe6XuALyiq0Tr%2fTSmLlA%3d%3d&STTIME=", time_rev, "&STUNIT=U0202&BOUNDARY=", toupper(url_encode(unlist(strsplit(all_data_temp$SPACE, "\\|")))))
    }else{
      url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=XjqpPDCzautWVyIadXhbpA%3d%3d&STTIME=", time_rev, "&STUNIT=U0202&BOUNDARY=", toupper(url_encode(unlist(strsplit(all_data_temp$SPACE, "\\|")))))
    }
  }

  population=data.frame()
  cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}]", total=length(url_all))
  for(url in c(1:length(url_all))){
    cli_progress_update()
    unlink(list.files(tempdir(), full.names=T), recursive=T)
    download.file(url_all[url], paste0(tempdir(), "/temp_pop_TDX.zip"), mode="wb", quiet=T)
    untar(paste0(tempdir(), "/temp_pop_TDX.zip"), exdir=paste0(tempdir(), "/temp_pop_TDX"))
    dir_file=dir(dir(paste0(tempdir(), "/temp_pop_TDX"), full.names=T), full.names=T, pattern="csv")

    # latest data use 'UTF-8' encoding, therefore, try read again if error occurs when using 'Big-5'
    suppressWarnings({
      population_temp=read.csv(dir_file, fileEncoding="Big5")
      population_temp=population_temp[-1, ]
    })
    if(nrow(population_temp)==0){
      population_temp=read.csv(dir_file)
      population_temp=population_temp[-1, ]
    }

    population_temp[, grepl("CNT|RAT|DEN", colnames(population_temp))]=matrix(as.numeric(as.matrix(population_temp[, grepl("CNT|RAT|DEN", colnames(population_temp))])), nrow=nrow(population_temp))
    population=rbind(population, population_temp)

    unlink(paste0(tempdir(), "/temp_pop_TDX"), recursive=T)
    file.remove(paste0(tempdir(), "/temp_pop_TDX.zip"))
    rm(population_temp)
  }
  cli_progress_done()

  temp_id=as.numeric(mapply(function(x) which(c("COUNTY_ID","COUNTY","TOWN_ID","TOWN","VILLAGE","V_ID")[x]==colnames(population)), c(1:6)))
  colnames(population)[temp_id[!is.na(temp_id)]]=c("COUNTYCODE","COUNTYNAME","TOWNCODE","TOWNNAME","VILLNAME","VILLCODE")[!is.na(temp_id)]
  temp_id=as.numeric(mapply(function(x) which(c("CODE2","CODE1","CODEBASE")[x]==colnames(population)), c(1:3)))
  colnames(population)[temp_id[!is.na(temp_id)]]=c("SA2CODE","SA1CODE","SA0CODE")[!is.na(temp_id)]

  if (nchar(out)!=0 & out!=F){
    write.csv(population, out, row.names=F)
  }
  return(population)
}



#' @export
Freeway_History=function(file, date, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(data.table)) install.packages("data.table")
  if (!require(progress)) install.packages("progress")

  if(sum(dir() %in% c("temp_freeway_TDX.zip","temp_freeway_TDX"))){
    stop(paste0("Please remove or rename the directory 'temp_freeway_TDX.zip' and 'temp_freeway_TDX.zip' in advance!!\n"))
  }

  freeway_data=data.frame()
  if(file %in% c("M03A","M04A","M05A","M08A")){
    url=paste0("https://tisvcloud.freeway.gov.tw/history/TDCS/", file, "/", gsub("-", "", date), "/00/TDCS_", file, "_", gsub("-", "", date), "_000000.csv")
    if(suppressWarnings(ncol(fread(url)))!=0){
      pb=progress_bar$new(format="(:spin) [:bar] :percent  ", total=24*12, clear=F, width=80)
      for(hr in c(0:23)){
        for(minu in seq(0, 55, 5)){
          pb$tick()
          url=paste0("https://tisvcloud.freeway.gov.tw/history/TDCS/", file, "/", gsub("-", "", date), "/",
                     ifelse(nchar(hr)==1, paste0("0", hr), hr), "/TDCS_", file, "_", gsub("-", "", date), "_",
                     ifelse(nchar(hr)==1, paste0("0", hr), hr), ifelse(nchar(minu)==1, paste0("0", minu), minu), "00.csv")
          temp=fread(url, showProgress=F)
          if(file=="M03A"){
            colnames(temp)=c("TimeInterval","GantryID","Direction","VehicleType","Flow")
          }else if(file=="M04A"){
            colnames(temp)=c("TimeInterval","GantryFrom","GantryTo","VehicleType","TravelTime","Flow")
          }else if(file=="M05A"){
            colnames(temp)=c("TimeInterval","GantryFrom","GantryTo","VehicleType","SpaceMeanSpeed","Flow")
          }else if(file=="M08A"){
            colnames(temp)=c("TimeInterval","GantryFrom","GantryTo","VehicleType","Flow")
          }
          freeway_data=rbind(freeway_data, temp)
          cat(paste0(date, " ", ifelse(nchar(hr)==1, paste0("0", hr), hr), ":", ifelse(nchar(minu)==1, paste0("0", minu), minu)))
        }
      }
    }else{
      url=paste0("https://tisvcloud.freeway.gov.tw/history/TDCS/", file, "/", file, "_", gsub("-", "", date), ".tar.gz")
      download.file(url, "./temp_freeway_TDX.zip", quiet=T)
      untar("temp_freeway_TDX.zip", exdir="temp_freeway_TDX")
      dir_file=paste0(dir("temp_freeway_TDX", full.names=T))
      dir_file=dir(dir_file, full.names=T)
      dir_file=dir(dir_file, full.names=T)
      dir_file=dir(dir_file, full.names=T)
      if(length(dir_file)==0){
        unlink("temp_freeway_TDX", recursive=T)
        file.remove("temp_freeway_TDX.zip")
        stop(paste0("Data of ", date, " is not updated to Traffic Database, Freeway Bureau, MOTC\n Or please check out if your date format is valid!"))
      }
      freeway_data=rbindlist(lapply(dir_file, fread))

      if(file=="M03A"){
        colnames(freeway_data)=c("TimeInterval","GantryID","Direction","VehicleType","Flow")
      }else if(file=="M04A"){
        colnames(freeway_data)=c("TimeInterval","GantryFrom","GantryTo","VehicleType","TravelTime","Flow")
      }else if(file=="M05A"){
        colnames(freeway_data)=c("TimeInterval","GantryFrom","GantryTo","VehicleType","SpaceMeanSpeed","Flow")
      }else if(file=="M08A"){
        colnames(freeway_data)=c("TimeInterval","GantryFrom","GantryTo","VehicleType","Flow")
      }

      unlink("temp_freeway_TDX", recursive=T)
      file.remove("temp_freeway_TDX.zip")
    }
  }else if(file %in% c("M06A","M07A")){
    url=paste0("https://tisvcloud.freeway.gov.tw/history/TDCS/", file, "/", gsub("-", "", date), "/00/TDCS_", file, "_", gsub("-", "", date), "_000000.csv")

    if(suppressWarnings(ncol(fread(url)))!=0){

      pb=progress_bar$new(format="(:spin) [:bar] :percent  ", total=24, clear=F, width=80)
      for(hr in c(0:23)){
        pb$tick()
        url=paste0("https://tisvcloud.freeway.gov.tw/history/TDCS/", file, "/", gsub("-", "", date), "/",
                   ifelse(nchar(hr)==1, paste0("0", hr), hr), "/TDCS_", file, "_", gsub("-", "", date), "_",
                   ifelse(nchar(hr)==1, paste0("0", hr), hr), "0000.csv")
        temp=fread(url, showProgress=F)

        if(file=="M06A"){
          colnames(temp)=c("VehicleType","DetectionTime_O","GantryID_O","DetectionTime_D","GantryID_D","TripLength","TripEnd","TripInformation")
        }else if(file=="M07A"){
          colnames(temp)=c("TimeInterval","GantryFrom","VehicleType","TripDistance","Flow")
        }
        freeway_data=rbind(freeway_data, temp)
        cat(paste0(date, " Hour:", ifelse(nchar(hr)==1, paste0("0", hr), hr)))
      }
    }else{
      if(file=="M06A"){
        cat("Please wait for a while...\n")
      }
      url=paste0("https://tisvcloud.freeway.gov.tw/history/TDCS/", file, "/", file, "_", gsub("-", "", date), ".tar.gz")
      download.file(url, "./temp_freeway_TDX.zip", quiet=T)
      untar("temp_freeway_TDX.zip", exdir="temp_freeway_TDX")
      dir_file=paste0(dir("temp_freeway_TDX", full.names=T))
      dir_file=dir(dir_file, full.names=T)
      dir_file=dir(dir_file, full.names=T)
      dir_file=dir(dir_file, full.names=T)
      if(length(dir_file)==0){
        unlink("temp_freeway_TDX", recursive=T)
        file.remove("temp_freeway_TDX.zip")
        stop(paste0("Data of ", date, " is not updated to Traffic Database, Freeway Bureau, MOTC\n Or please check out if your date format is valid!"))
      }

      freeway_data=rbindlist(lapply(dir_file, fread))

      if(file=="M06A"){
        colnames(freeway_data)=c("VehicleType","DetectionTime_O","GantryID_O","DetectionTime_D","GantryID_D","TripLength","TripEnd","TripInformation")
      }else if(file=="M07A"){
        colnames(freeway_data)=c("TimeInterval","GantryFrom","VehicleType","TripDistance","Flow")
      }

      unlink("temp_freeway_TDX", recursive=T)
      file.remove("temp_freeway_TDX.zip")
    }
  }else{
    stop(paste0("Please use valid `file` parameter, including 'M03A`, 'M04A`, 'M05A`, 'M06A`, 'M07A` and 'M08A`\n"))
  }

  if (nchar(out)!=0 & out!=F){
    write.csv(freeway_data, out, row.names=F)
  }
  return(freeway_data)
}



#' @export
Bus_RealTime=function(access_token, county, format, dates, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(httr)) install.packages("httr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(format=="frequency"){
    url=paste0("https://tdx.transportdata.tw/api/historical/v2/Historical/Bus/RealTimeByFrequency/City/", county, "?Dates=", dates, "&%24format=CSV")
  }else if(format=="stop"){
    url=paste0("https://tdx.transportdata.tw/api/historical/v2/Historical/Bus/RealTimeNearStop/City/", county, "?Dates=", dates, "&%24format=CSV")
  }else{
    stop("Parameter 'format' should be 'frequency' or 'stop'.")
  }
  x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))
  bus_real_time=read.csv(textConnection(content(x, "text", encoding="UTF-8")), header=T)

  if("invalid.token" %in% names(bus_real_time)){
    stop(paste0("Your access token is invalid!"))
  }else if("Message" %in% names(bus_real_time)){
    if(grepl("Dates:", bus_real_time$Message)){
      stop("Parameter 'Dates' are invalid. And it should not be more than 7 days!")
    }else if(grepl("City", bus_real_time$Message)){
      print(TDX_County)
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }else{
      stop(paste0("Date value is invalid! Format of the date:\n", "\nType\t\tFormat\t\t\tExample\n", paste0(rep("=", 61), collapse=""), "\nSingle Date\tYYYY-MM-DD\t\t2023-01-01\nMultiple Dates\tYYYY-MM-DD;YYYY-MM-DD\t2023-01-01;2023-02-01\nDate Range\tYYYY-MM-DD~YYYY-MM-DD\t2023-01-01~2023-01-31"))
    }
  }

  if (nchar(out)!=0 & out!=F){
    write.csv(bus_real_time, out, row.names=F)
  }
  return(bus_real_time)
}



#' @export
Income=function(year, out=F){
  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  tryCatch({
    url=paste0("https://eip.fia.gov.tw/data/ias/ias", year-1911, "/", year-1911, "_165-9.csv")
    income=read.csv(url)
  }, error=function(err){
    stop(paste0("Data of year '", year, "' is not provided!"))
  })
  colnames(income)=c("CountyTown","Village","Houses","Amount","MEAN","MED","Q1","Q3","SD","CV")

  if (nchar(out)!=0 & out!=F){
    write.csv(income, out, row.names=F)
  }
  return(income)
}



#' @export
gtfs=function(access_token, mode, county=F, out=F){
  gtfs_label=c("agency","calendar","calendar_dates","frequencies","routes","shapes","stop_times","stops","trips")
  cat(paste0("Note that GTFS data includes ", paste(gtfs_label, collapse=", "), ". All data would be stored as 'txt' file in a directory if they are exported.\n"))

  gtfs_list=list()
  for(i in c(1:length(gtfs_label))){
    if(mode=="Bus"){
      url=paste0("https://tdx.transportdata.tw/api/premium/v2/GTFS/Static/", mode, "/City/", county, "/", gtfs_label[i], "?%24format=txt")
    }else if(mode %in% c("TRA","THSR")){
      url=paste0("https://tdx.transportdata.tw/api/premium/v2/GTFS/Static/", mode, "/", gtfs_label[i], "?%24format=txt")
    }else{
      stop(paste0("Data of '", mode, "' is not available."))
    }

    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    if(content(x, "text", encoding="UTF-8")==""){
      cat(paste0("'", gtfs_label[i], "' data is not available.\n"))
      gtfs_list[gtfs_label[i]]=list(c())
    }else{
      all_data_temp=read.csv(textConnection(content(x, "text", encoding="UTF-8")), header=T)
      if("invalid.token" %in% names(all_data_temp)){
        stop(paste0("Your access token is invalid!"))
      }else if(sum(grepl("Message", names(all_data_temp)))!=0){
        stop(fromJSON(content(x, "text", encoding="UTF-8"))$Message)
      }else{
        gtfs_list[gtfs_label[i]]=list(all_data_temp)
        if(out!=F){
          write.table(all_data_temp, paste0(out, "/", gtfs_label[i], ".txt"), sep=",")
        }
        cat(paste0("'", gtfs_label[i], "' data downloaded.\n"))
      }
    }
  }
  return(gtfs_list)
}



#' @export
Bike_OD_His=function(bikesys, time, out=F){
  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  if(bikesys==1){
    url="https://tcgbusfs.blob.core.windows.net/dotapp/youbike_ticket_opendata/YouBikeHis.csv"
  }else if(bikesys==2){
    url="https://tcgbusfs.blob.core.windows.net/dotapp/youbike_second_ticket_opendata/YouBikeHis.csv"
  }else{
    stop("Argument 'bikesys' should be either 1 or 2!")
  }
  dir_file=read.csv(url)

  if(nchar(time)==7 & grepl("-", time)){
    time_year=substr(time, 1, regexpr("-", time)-1)
    time_month=substr(time, regexpr("-", time)+1, regexpr("-", time)+2)
  }else{
    stop("Please check out the format of 'time'! It should be 'YYYY-MM'.")
  }

  if(sum(grepl(time, dir_file$fileURL))!=0){
    url=dir_file$fileURL[which(grepl(time, dir_file$fileURL))]
    unlink(list.files(tempdir(), full.names=T), recursive=T)
    download.file(url, paste0(tempdir(), "/temp_youbike_TDX.zip"), mode="wb", quiet=T)
    untar(paste0(tempdir(), "/temp_youbike_TDX.zip"), exdir=paste0(tempdir(), "/temp_youbike_TDX"))

    dir_file=dir(paste0(tempdir(), "/temp_youbike_TDX"), full.names=T)
    if(grepl("csv", dir_file)==F){
      dir_file=dir(dir(paste0(tempdir(), "/temp_youbike_TDX"), full.names=T), full.names=T)
    }

    if(grepl("rent", read.csv(dir_file, header=F, nrows=1)[1])){
      bike_od_his=read.csv(dir_file, header=T)
    }else{
      bike_od_his=read.csv(dir_file, header=F)
    }
    colnames(bike_od_his)=c("RentTime","RentStation","ReturnTime","ReturnStation","RideTime","Date")
    unlink(paste0(tempdir(), "/temp_youbike_TDX"), recursive=T)
    file.remove(paste0(tempdir(), "/temp_youbike_TDX.zip"))
  }else{
    stop(paste0("Data of Youbike ", bikesys, ".0 in ", time, " is not available!"))
  }

  if(nchar(out)!=0 & out!=F){
    write.csv(bike_od_his, out, row.names=F)
  }
  return(bike_od_his)
}



#' @export
Landuse=function(district, year, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(urltools)) install.packages("urltools")
  if (!require(cli)) install.packages("cli")
  if (!require(fs)) install.packages("fs")
  options(timeout=1000)

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt'.\n")
  }

  all_data=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/landuse_area_time.csv")
  if(district %in% c("SA0","SA1","SA2")){
    all_data_temp=filter(all_data, Year==year, SA==district)
    dis_code=c("U0200","U0201","U0202")[which(district==c("SA0","SA1","SA2"))]
  }else{
    stop(paste0("Argument 'district' should be 'SA0', 'SA1', or 'SA2'."))
  }

  if(nrow(all_data_temp)==0){
    stop(paste0("The data is only provided in the following years:\n ", paste(sort(unique(all_data$Year)), collapse=", ")))
  }else{
    year_rev=paste0(year-1911, "Y")
    all_county=unlist(strsplit(all_data_temp$SPACE, "\\|"))
    cat(paste0("Year of ", year, " in the following ",  length(all_county), " counties will be downloaded:\n", paste(all_county, collapse=", "), "\n"))
  }

  landuse_name=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/landuse_name.csv")
  if(year %in% c(2014,2015)){
    landuse_name=landuse_name[landuse_name$CATEGORY=="95-104",]
    url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=iI0sdRY7vC5HS5s9QthynQ%3d%3d&STTIME=", year_rev, "&STUNIT=", dis_code, "&BOUNDARY=", toupper(url_encode(unlist(strsplit(all_data_temp$SPACE, "\\|")))))
  }else if(year %in% c(2016:2019)){
    landuse_name=landuse_name[landuse_name$CATEGORY=="105-108",]
    url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=XkioVMS8Cor5vqlKLsBFQg%3d%3d&STTIME=", year_rev, "&STUNIT=", dis_code, "&BOUNDARY=", toupper(url_encode(unlist(strsplit(all_data_temp$SPACE, "\\|")))))
  }else{
    landuse_name=landuse_name[landuse_name$CATEGORY=="109-",]
    url_all=paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=UobMnDnnf0xMbxoTeOgmPA%3d%3d&STTIME=", year_rev, "&STUNIT=", dis_code, "&BOUNDARY=", toupper(url_encode(unlist(strsplit(all_data_temp$SPACE, "\\|")))))
  }

  prog_text=unlist(strsplit(all_data_temp$SPACE, "\\|"))
  landuse=data.frame()
  cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}] {prog_text[url]}", total=length(url_all))
  for(url in c(1:length(url_all))){
    cli_progress_update()
    unlink(list.files(tempdir(), full.names=T), recursive=T)
    download.file(url_all[url], paste0(tempdir(), "/temp_landuse_TDX.zip"), mode="wb", quiet=T)
    untar(paste0(tempdir(), "/temp_landuse_TDX.zip"), exdir=paste0(tempdir(), "/temp_landuse_TDX"))
    dir_file=dir_ls(paste0(tempdir(), "/temp_landuse_TDX"), recurse=T)
    dir_file=dir_file[grepl("csv", dir_file)]

    # latest data use 'UTF-8' encoding, therefore, try read again if error occurs when using 'Big-5'
    suppressWarnings({
      landuse_temp=read.csv(dir_file, fileEncoding="Big5")
      landuse_temp=landuse_temp[-1, ]
    })
    if(nrow(landuse_temp)==0){
      landuse_temp=read.csv(dir_file)
      landuse_temp=landuse_temp[-1, ]
    }

    landuse_temp[, grepl("L0|SUM", colnames(landuse_temp))]=matrix(suppressWarnings(as.numeric(as.matrix(landuse_temp[, grepl("L0|SUM", colnames(landuse_temp))]))), nrow=nrow(landuse_temp))
    landuse_temp=replace(landuse_temp, is.na(landuse_temp), 0)
    colnames(landuse_temp)[mapply(function(x) which(landuse_name$ORI_NAME[x]==colnames(landuse_temp)), c(1:nrow(landuse_name)))]=landuse_name$NEW_NAME

    unlink(paste0(tempdir(), "/temp_landuse_TDX"), recursive=T)
    file.remove(paste0(tempdir(), "/temp_landuse_TDX.zip"))

    landuse=rbind(landuse, landuse_temp)
    rm(landuse_temp)
  }
  cli_progress_done()

  temp_id=as.numeric(mapply(function(x) which(c("CODE2","CODE1","CODEBASE")[x]==colnames(landuse)), c(1:9)))
  colnames(landuse)[temp_id[!is.na(temp_id)]]=c("SA2CODE","SA1CODE","SA0CODE")[!is.na(temp_id)]

  if (nchar(out)!=0 & out!=F){
    write.csv(landuse, out, row.names=F)
  }
  return(landuse)
}



#' @export
House_Price=function(year, season, out=F){
  if (!require(dplyr)) install.packages("dplyr")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt'.\n")
  }
  if(year<2012){
    stop("Year is invalid. The data is available from year 2012!")
  }else{
    if(season %in% c(1:4)){
      year_sea=paste0(year-1911, "S", season)
    }else{
      stop("Season is invalid. It should be 1, 2, 3, or 4!")
    }
  }

  url=paste0("https://plvr.land.moi.gov.tw//DownloadSeason?season=", year_sea, "&type=zip&fileName=lvr_landcsv.zip")
  unlink(list.files(tempdir(), full.names=T), recursive=T)
  download.file(url, paste0(tempdir(), "/house_price_tdx.zip"), mode="wb", quiet=T)
  untar(paste0(tempdir(), "/house_price_tdx.zip"), exdir=paste0(tempdir(), "/house_price_tdx"))
  dir_file=dir(paste0(tempdir(), "/house_price_tdx"), full.names=T)
  dir_file=dir_file[grepl(paste(paste0("lvr_land_", c("a","b","c"), ".csv"), collapse="|"), dir_file)]

  houseprice_name=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/houseprice_name.csv")
  house_price=data.frame()
  for(i in dir_file){
    house_price_temp=read.csv(i)
    colnames(house_price_temp)=gsub("\\.", "", colnames(house_price_temp))
    colnames(house_price_temp)=mapply(function(x) houseprice_name$COL_NAME[which(colnames(house_price_temp)[x]==houseprice_name$ORI_NAME)], c(1:ncol(house_price_temp)))
    house_price_temp=house_price_temp[-1,]
    house_price=bind_rows(house_price, house_price_temp)
  }

  temp=c("LAND_AREA","ROOM","HALL","BATH","BUILD_TOTAL_AREA","TOTAL_PRICE","UNIT_PRICE","PARKING_AREA","PARKING_PRICE","BUILD_AREA_MAIN","BUILD_AREA_AUX","BALCONY_AREA","PARKING_UNIT_PRICE")
  temp=temp[temp %in% colnames(house_price)]
  house_price[, temp]=matrix(as.numeric(unlist(house_price[, temp])), nrow(house_price))

  # house_price=mutate(house_price, TRANSACTION_DATE=case_when(
  #   nchar(TRANSACTION_DATE)==6 ~ as.Date(paste0(as.numeric(substr(TRANSACTION_DATE, 1, 2))+1911, "-", substr(TRANSACTION_DATE, 3, 4), "-", substr(TRANSACTION_DATE, 5, 6))),
  #   nchar(TRANSACTION_DATE)==7 ~ as.Date(paste0(as.numeric(substr(TRANSACTION_DATE, 1, 3))+1911, "-", substr(TRANSACTION_DATE, 4, 5), "-", substr(TRANSACTION_DATE, 6, 7))),
  #   TRUE ~ NA
  # ), CONSTRUCTION_DATE=case_when(
  #   nchar(CONSTRUCTION_DATE)==6 ~ as.Date(paste0(as.numeric(substr(CONSTRUCTION_DATE, 1, 2))+1911, "-", substr(CONSTRUCTION_DATE, 3, 4), "-", substr(CONSTRUCTION_DATE, 5, 6))),
  #   nchar(CONSTRUCTION_DATE)==7 ~ as.Date(paste0(as.numeric(substr(CONSTRUCTION_DATE, 1, 3))+1911, "-", substr(CONSTRUCTION_DATE, 4, 5), "-", substr(CONSTRUCTION_DATE, 6, 7))),
  #   TRUE ~ NA
  # ))

  if(nchar(out)!=0 & out!=F){
    write.csv(house_price, out, row.names=F)
  }
  return(house_price)
}



#' @export
School=function(level, year, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(urltools)) install.packages("urltools")
  if (!require(sf)) install.packages("sf")
  if (!require(fs)) install.packages("fs")

  if(dtype=="text"){
    if(!(grepl(".csv|.txt", out)) & out!=F){
      stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
    }
  }else if(dtype=="sf"){
    if(!(grepl(".shp", out)) & out!=F){
      stop("The file name must contain '.shp' when exporting shapefile.\n")
    }
  }else{
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }

  all_data=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/school_year.csv")
  all_data_temp=filter(all_data, Year==year, Level==tolower(level))
  if(nrow(all_data_temp)==0){
    if(!tolower(level) %in% c("elementary","junior","senior","university")){
      stop(paste0("'", level, "' is invalid! Argument 'level' must be 'elementary', 'junior', 'senior', or 'university'!"))
    }else{
      stop(paste0("Date of '", tolower(level), "' is only available in the following years:\n", paste(sort(unique(filter(all_data, Level==tolower(level))$Year)), collapse=", ")))
    }
  }else{
    year_rev=paste0(year-1911, "Y")
  }

  if(tolower(level)=="elementary"){
    if(year<=2015){
      url_all=ifelse(all_data_temp$SPACE=="\u5168\u570b(\u4e0d\u542b\u91d1\u9580\u3001\u9023\u6c5f\u3001\u6f8e\u6e56)",
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=zPzp%2fhQW6gJCn0esWovJpg%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))),
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=SJCPGkQm3KtYoC3pTYtCdw%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))))
    }else{
      url_all=ifelse(all_data_temp$SPACE=="\u5168\u570b(\u4e0d\u542b\u91d1\u9580\u3001\u9023\u6c5f\u3001\u6f8e\u6e56)",
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=Ywl8HJm92fAisiMe4xmGAw%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))),
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=uqj1F9fpF00qkT7GLYw22Q%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))))
    }
  }else if(tolower(level)=="junior"){
    if(year<=2015){
      url_all=ifelse(all_data_temp$SPACE=="\u5168\u570b(\u4e0d\u542b\u91d1\u9580\u3001\u9023\u6c5f\u3001\u6f8e\u6e56)",
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=BoS3OzTL6YBOxfojkr3v6Q%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))),
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=cIOfve6OcAkzVfS8433JSQ%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))))
    }else{
      url_all=ifelse(all_data_temp$SPACE=="\u5168\u570b(\u4e0d\u542b\u91d1\u9580\u3001\u9023\u6c5f\u3001\u6f8e\u6e56)",
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=wSnHwaMwjUruS14yh91lIA%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))),
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=qO17yG6gEf44WCJ3kNnqaw%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))))
    }
  }else if(tolower(level)=="senior"){
    if(year<=2013){
      url_all=ifelse(all_data_temp$SPACE=="\u5168\u570b(\u4e0d\u542b\u91d1\u9580\u3001\u9023\u6c5f\u3001\u6f8e\u6e56)",
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=3VXKvoXzKHUw7l9w4dn7YA%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))),
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=Q49B6SRee7ql5pYobe2aBQ%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))))
    }else{
      url_all=ifelse(all_data_temp$SPACE=="\u5168\u570b(\u4e0d\u542b\u91d1\u9580\u3001\u9023\u6c5f\u3001\u6f8e\u6e56)",
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=uWD8BdyU4K6oRx27MFGYbw%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))),
                     paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=0%2fiLe8nwWzCK5k7tEJ0k4Q%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))))
    }
  }else if(tolower(level)=="university"){
    url_all=ifelse(all_data_temp$SPACE=="\u5168\u570b(\u4e0d\u542b\u91d1\u9580\u3001\u9023\u6c5f\u3001\u6f8e\u6e56)",
                   paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=V0hFaorLGCnjkSeiT9FIFA%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))),
                   paste0("https://segis.moi.gov.tw/STATCloud/reqcontroller.file?method=filedown.downloadproductfile&code=vV2xmoaMzm7yaic2cRgS9g%3d%3d&STTIME=", year_rev, "&STUNIT=null&BOUNDARY=", toupper(url_encode(all_data_temp$SPACE))))
  }

  school=data.frame()
  for(url in c(1:length(url_all))){
    unlink(list.files(tempdir(), full.names=T), recursive=T)
    download.file(url_all[url], paste0(tempdir(), "/school_tdx.zip"), mode="wb", quiet=T)
    untar(paste0(tempdir(), "/school_tdx.zip"), exdir=paste0(tempdir(), "/school_tdx"))
    dir_file=dir_ls(paste0(tempdir(), "/school_tdx"), recurse=T)
    dir_file=dir_file[grepl("csv", dir_file)]

    suppressWarnings({
      school_temp=read.csv(dir_file, fileEncoding="Big5")
      school_temp=school_temp=school_temp[-1,]
    })
    if(nrow(school_temp)==0){
      school_temp=read.csv(dir_file)
      school_temp=school_temp=school_temp[-1,]
    }

    req_col=c(colnames(school_temp)[grepl("CNT|AREA", colnames(school_temp))], "X", "Y")
    school_temp[, req_col]=matrix(as.numeric(unlist(school_temp[, req_col])), nrow=nrow(school_temp))
    school=rbind(school, school_temp)
  }

  if(dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(school, out, row.names=F)
    }
  }else if(dtype=="sf"){
    school=mutate(school, geometry=st_as_sfc(ifelse(is.na(X) | is.na(Y), "POINT EMPTY", paste0("POINT(", X, " ", Y, ")"))))%>%
      st_sf(crs=3826)
    if (grepl(".shp", out) & out!=F){
      write_sf(school, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(school)
}



#' @export
Hospital=function(district, time, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(urltools)) install.packages("urltools")
  if (!require(cli)) install.packages("cli")
  if (!require(sf)) install.packages("sf")

  if(dtype=="text"){
    dtype_rev="CSV"
  }else if(dtype=="sf"){
    dtype_rev="SHP"
  }else{
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }


  # check if the month is valid
  if(nchar(time)!=7 | !grepl("-", time)){
    stop(paste0("Date format is valid! It should be 'YYYY-MM'!"))
  }
  if(as.numeric(substr(time, 1, regexpr("-", time)-1))<2010){
    stop("Only year after 2010 is provided!")
  }
  time_rev=paste0(as.numeric(substr(time, 1, regexpr("-", time)-1))-1911, "Y", substr(time, regexpr("-", time)+1, 10), "M")
  area_time=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/hospital_area_time.csv")
  area_time$TIME=factor(area_time$TIME, levels=(data.frame(TIME=unique(area_time$TIME), TIME_temp=as.numeric(gsub("Y|M", "", unique(area_time$TIME)))) %>% arrange(TIME_temp))$TIME)

  space=c("\u7e23\u5e02","\u9109\u93ae\u5e02\u5340","\u6700\u5c0f\u7d71\u8a08\u5340","\u4e00\u7d1a\u767c\u5e03\u5340","\u4e8c\u7d1a\u767c\u5e03\u5340")[which(district==c("County","Town","SA0","SA1","SA2"))]
  if(length(space)==1){
    space_all=filter(area_time, UNIT==space, TIME==time_rev)
    if(nrow(space_all)==1){
      all_county=toupper(url_encode(unlist(strsplit(space_all$SPACE, "\\|"))))
    }else{
      temp=sort(unique(filter(area_time, UNIT==space)$TIME))
      stop(paste0("Data of '", time, "' is not available. Please use the following time :\n", paste(paste0(as.numeric(substr(temp, 1, regexpr("Y", temp)-1))+1911, "-", substr(temp, regexpr("Y", temp)+1, regexpr("M", temp)-1)), collapse=", ")))
    }
  }else{
    stop("The argument of 'district' must be 'County', 'Town', 'SA0', 'SA1', or 'SA2'!\nNote that data of village is not provided!")
  }

  if(district=="County"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=DD43C811BCD7FDDE4D4367A22F6B8FF9&STTIME=", time_rev, "&STUNIT=U01CO&BOUNDARY=%E5%85%A8%E5%9C%8B&TYPE=", dtype_rev)
  }else if(district=="Town"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=DD43C811BCD7FDDE6980D5C9940A11CD&STTIME=", time_rev, "&STUNIT=U01TO&BOUNDARY=%E5%85%A8%E5%9C%8B&TYPE=", dtype_rev)
  }else if(district=="SA0"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=DD43C811BCD7FDDE460E9DA2BF58557E&STTIME=", time_rev, "&STUNIT=U0200&BOUNDARY=", all_county, "&TYPE=", dtype_rev)
  }else if(district=="SA1"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=DD43C811BCD7FDDE838B75E02ACC9E6E&STTIME=", time_rev, "&STUNIT=U0201&BOUNDARY=", all_county, "&TYPE=", dtype_rev)
  }else if(district=="SA2"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=DD43C811BCD7FDDE184F1603ADC8AF64&STTIME=", time_rev, "&STUNIT=U0202&BOUNDARY=", all_county, "&TYPE=", dtype_rev)
  }

  hospital=data.frame()
  cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}]", total=length(url_all))
  for(url in url_all){
    cli_progress_update()
    unlink(list.files(tempdir(), full.names=T), recursive=T)
    download.file(url, paste0(tempdir(), "/hospital_TDX.zip"), mode="wb", quiet=T)
    untar(paste0(tempdir(), "/hospital_TDX.zip"), exdir=paste0(tempdir(), "/hospital_TDX"))
    dir_file=dir(dir(paste0(tempdir(), "/hospital_TDX"), full.names=T), full.names=T)

    if(dtype=="text"){
      dir_file=dir_file[grepl(".csv", dir_file)]
      hospital_temp=read.csv(dir_file, fileEncoding="Big5")
      hospital_temp=hospital_temp[-1, ]
      hospital_temp[, grepl("CNT|BED|SRVP|SRVB", colnames(hospital_temp))]=matrix(as.numeric(as.matrix(hospital_temp[, grepl("CNT|BED|SRVP|SRVB", colnames(hospital_temp))])), nrow=nrow(hospital_temp))
    }else{
      untar(dir_file, exdir=paste0(dir(paste0(tempdir(), "/hospital_TDX"), full.names=T), "/hospital_TDX"))
      dir_file=dir(paste0(dir(paste0(tempdir(), "/hospital_TDX"), full.names=T), "/hospital_TDX"), full.names=T)
      hospital_temp=st_read(dir_file[grepl("SHP", dir_file)], options="ENCODING=Big5", quiet=T)
      hospital_temp$SPECODE=NULL
      hospital_temp=st_sf(hospital_temp, crs=3826)%>%
        st_zm()%>%
        st_transform(crs=4326)
    }
    unlink(paste0(tempdir(), "/hospital_TDX"), recursive=T)
    file.remove(paste0(tempdir(), "/hospital_TDX.zip"))
    if(nrow(hospital)==0){
      hospital=hospital_temp
    }else{
      hospital=bind_rows(hospital, hospital_temp)
    }
    rm(hospital_temp)
  }
  cli_progress_done()

  temp_id=as.numeric(mapply(function(x) which(c("COUNTY_ID","COUNTY","TOWN_ID","TOWN","VILLAGE","V_ID")[x]==colnames(hospital)), c(1:6)))
  colnames(hospital)[temp_id[!is.na(temp_id)]]=c("COUNTYCODE","COUNTYNAME","TOWNCODE","TOWNNAME","VILLNAME","VILLCODE")[!is.na(temp_id)]

  if(dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(hospital, out, row.names=F)
    }
  }else if(dtype=="sf"){
    if (grepl(".shp", out) & out!=F){
      write_sf(hospital, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(hospital)
}



#' @export
Business=function(district, time, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(urltools)) install.packages("urltools")
  if (!require(cli)) install.packages("cli")
  if (!require(sf)) install.packages("sf")

  if(dtype=="text"){
    dtype_rev="CSV"
  }else if(dtype=="sf"){
    dtype_rev="SHP"
  }else{
    stop(paste0(dtype, " is not valid format. Please use 'text' or 'sf'.\n"))
  }
  if(!(grepl(".shp", out)) & out!=F & dtype=="sf"){
    stop("The file name must contain '.shp' when exporting shapefile.\n")
  }
  if(!(grepl(".csv|.txt", out)) & out!=F & dtype=="text"){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }


  # check if the month is valid
  if(nchar(time)!=7 | !grepl("-", time)){
    stop(paste0("Date format is valid! It should be 'YYYY-MM'!"))
  }
  if(as.numeric(substr(time, 1, regexpr("-", time)-1))<2010){
    stop("Only year after 2010 is provided!")
  }
  time_rev=paste0(as.numeric(substr(time, 1, regexpr("-", time)-1))-1911, "Y", substr(time, regexpr("-", time)+1, 10), "M")
  area_time=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/business_area_time.csv")
  area_time$TIME=factor(area_time$TIME, levels=(data.frame(TIME=unique(area_time$TIME), TIME_temp=as.numeric(gsub("Y|M", "", unique(area_time$TIME)))) %>% arrange(TIME_temp))$TIME)

  space=c("\u7e23\u5e02","\u9109\u93ae\u5e02\u5340","\u6700\u5c0f\u7d71\u8a08\u5340","\u4e00\u7d1a\u767c\u5e03\u5340","\u4e8c\u7d1a\u767c\u5e03\u5340")[which(district==c("County","Town","SA0","SA1","SA2"))]
  if(length(space)==1){
    space_all=filter(area_time, UNIT==space, TIME==time_rev)
    if(nrow(space_all)==1){
      all_county=toupper(url_encode(unlist(strsplit(space_all$SPACE, "\\|"))))
    }else{
      temp=sort(unique(filter(area_time, UNIT==space)$TIME))
      stop(paste0("Data of '", time, "' is not available. Please use the following time :\n", paste(paste0(as.numeric(substr(temp, 1, regexpr("Y", temp)-1))+1911, "-", substr(temp, regexpr("Y", temp)+1, regexpr("M", temp)-1)), collapse=", ")))
    }
  }else{
    stop("The argument of 'district' must be 'County', 'Town', 'SA0', 'SA1', or 'SA2'!\nNote that data of village is not provided!")
  }

  if(district=="County"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=EE602A1FC2DD0B0042C5C56A3F604041&STTIME=", time_rev, "&STUNIT=U01CO&BOUNDARY=", all_county, "&TYPE=", dtype_rev)
  }else if(district=="Town"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=EE602A1FC2DD0B0064DC1A457DEF3EC1&STTIME=", time_rev, "&STUNIT=U01TO&BOUNDARY=", all_county, "&TYPE=", dtype_rev)
  }else if(district=="SA0"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=EE602A1FC2DD0B0044D64C160D2534B7&STTIME=", time_rev, "&STUNIT=U0200&BOUNDARY=", all_county, "&TYPE=", dtype_rev)
  }else if(district=="SA1"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=EE602A1FC2DD0B006E41E92D5357C450&STTIME=", time_rev, "&STUNIT=U0201&BOUNDARY=", all_county, "&TYPE=", dtype_rev)
  }else if(district=="SA2"){
    url_all=paste0("https://segis.moi.gov.tw/STAT/Generic/Project/GEN_STAT.ashx?method=downloadproductfile&code=353AEEEB2A70D08EB964C8DC6A58C2F0&STTIME=", time_rev, "&STUNIT=U0202&BOUNDARY=", all_county, "&TYPE=", dtype_rev)
  }
  business_name=read.csv("https://raw.githubusercontent.com/ChiaJung-Yeh/NYCU_TDX/main/others/business_name.csv")

  business=data.frame()
  cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}]", total=length(url_all))
  for(url in url_all){
    cli_progress_update()
    unlink(list.files(tempdir(), full.names=T), recursive=T)
    download.file(url, paste0(tempdir(), "/business_TDX.zip"), mode="wb", quiet=T)
    untar(paste0(tempdir(), "/business_TDX.zip"), exdir=paste0(tempdir(), "/business_TDX"))
    dir_file=dir(dir(paste0(tempdir(), "/business_TDX"), full.names=T), full.names=T)

    if(dtype=="text"){
      dir_file=dir_file[grepl(".csv", dir_file)]
      business_temp=read.csv(dir_file, fileEncoding="Big5")
      business_temp=business_temp[-1, ]
      business_temp[, grepl("CNT", colnames(business_temp))]=matrix(as.numeric(as.matrix(business_temp[, grepl("CNT", colnames(business_temp))])), nrow=nrow(business_temp))
      colnames(business_temp)[mapply(function(x) which(business_name$ORI_NAME[x]==colnames(business_temp)), c(1:nrow(business_name)))]=business_name$NEW_NAME
    }else{
      untar(dir_file, exdir=paste0(dir(paste0(tempdir(), "/business_TDX"), full.names=T), "/business_TDX"))
      dir_file=dir(paste0(dir(paste0(tempdir(), "/business_TDX"), full.names=T), "/business_TDX"), full.names=T)
      business_temp=st_read(dir_file[grepl("SHP", dir_file)], options="ENCODING=Big5", quiet=T)
      business_temp=st_sf(business_temp, crs=3826)%>%
        st_zm()%>%
        st_transform(crs=4326)
      colnames(business_temp)[mapply(function(x) which(business_name$ORI_NAME[x]==colnames(business_temp)), c(1:nrow(business_name)))]=business_name$NEW_NAME
    }
    unlink(paste0(tempdir(), "/business_TDX"), recursive=T)
    file.remove(paste0(tempdir(), "/business_TDX.zip"))
    if(nrow(business)==0){
      business=business_temp
    }else{
      business=bind_rows(business, business_temp)
    }
    rm(business_temp)
  }
  cli_progress_done()

  temp_id=as.numeric(mapply(function(x) which(c("COUNTY_ID","COUNTY","TOWN_ID","TOWN","VILLAGE","V_ID")[x]==colnames(business)), c(1:6)))
  colnames(business)[temp_id[!is.na(temp_id)]]=c("COUNTYCODE","COUNTYNAME","TOWNCODE","TOWNNAME","VILLNAME","VILLCODE")[!is.na(temp_id)]

  if(dtype=="text"){
    if (nchar(out)!=0 & out!=F){
      write.csv(business, out, row.names=F)
    }
  }else if(dtype=="sf"){
    if (grepl(".shp", out) & out!=F){
      write_sf(business, out, layer_options="ENCODING=UTF-8")
    }
  }
  return(business)
}



#' @export
Crash=function(access_token, crash, county, time, dtype="text", out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(httr)) install.packages("httr")

  if((grepl(".csv|.txt", out)) & out!=F){
    warning("No need to set '.csv' or '.txt' when exporting the file in this function. Just set the directory and file name!\n")
  }
  if(!crash %in% c("A1", "A2")){
    stop("Argument 'crash' must be either 'A1' or 'A2'!")
  }

  county_code=c("TPE","NWT","TAO","TXG","TNN","KHH","KEE","HSZ","HSQ","MIA","CHA","NAN","YUN","CYQ","CYI","PIF","ILA","HUA","TTT","KIN","PEN","LIE")[which(county==TDX_County$Code)]
  if(length(county_code)==0 | is.na(county_code)){
    print(TDX_County[1:22,])
    stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
  }

  tryCatch({
    ym_rev=paste0(time, "-01~", as.Date(paste0(ifelse(substr(time, 6, 7)==12, as.numeric(substr(time, 1, 4))+1, substr(time, 1, 4)), "-", ifelse(substr(time, 6, 7)==12, "01", as.numeric(substr(time, 6, 7))+1), "-01"))-1)
  }, error=function(err){
    stop(paste0("Date format is valid! It should be 'YYYY-MM'!"))
  })

  tryCatch({
    url=paste0("https://tdx.transportdata.tw/api/historical/v1/TrafficAccident/", crash, "T1/", county_code, "?Dates=", ym_rev, "&$format=json")
    x=GET(url, add_headers(Accept="application/json", Authorization=paste("Bearer", access_token)))
    crash_t1=fromJSON(content(x, as="text", encoding="UTF-8"))
    url=paste0("https://tdx.transportdata.tw/api/historical/v1/TrafficAccident/", crash, "T2/", county_code, "?Dates=", ym_rev, "&$format=json")
    x=GET(url, add_headers(Accept="application/json", Authorization=paste("Bearer", access_token)))
    crash_t2=fromJSON(content(x, as="text", encoding="UTF-8"))
  }, error=function(err){
    stop(paste0("Your access token is invalid!"))
  })

  tryCatch({
    url="https://tdx.transportdata.tw/api/historical/v1/AccidentBasic/TrafficEventCode?$format=json"
    x=GET(url, add_headers(Accept="application/json", Authorization=paste("Bearer", access_token)))
    crash_code=fromJSON(content(x, as="text", encoding="UTF-8"))
  })

  if(length(crash_t1)==0 | sum(names(crash_t1) %in% "ErrorMessage")){
    warning(paste0(county, " has no crash in ", time,  " ,or data (crash data) is not avaliable."))
  }
  if(length(crash_t2)==0 | sum(names(crash_t2) %in% "ErrorMessage")){
    warning(paste0(county, " has no crash in ", time,  " ,or data (person data) is not avaliable."))
  }

  if(dtype=="sf" & length(crash_t1)!=0){
    crash_t1$geometry=st_as_sfc(ifelse(!(crash_t1$Lon==0 | is.na(crash_t1$Lon) | crash_t1$Lat==0 | is.na(crash_t1$Lat)), paste0("POINT(", crash_t1$Lon, " ", crash_t1$Lat, ")"), "POINT EMPTY"))
    crash_t1=st_sf(crash_t1, crs=4326)
    if(sum(st_is_empty(crash_t1$geometry))!=0){
      warning(paste0("Note that some of latitude and longitude are missing, and cannot retrieve the geometry!"))
    }
    if(nchar(out)!=0 & out!=F){
      write.table(colnames(crash_t1), paste0(out, "_T1colname.txt"), sep=",", row.names=F, col.names=F)
      write_sf(crash_t1, paste0(out, "_T1.shp"), layer_options="ENCODING=UTF-8")
      warning(paste0("Some colnames have been shorten. The original column name is retrieved in, '", paste0(out, "_T1colname.txt")), "'")
    }
  }else{
    if(nchar(out)!=0 & out!=F){
      write.csv(crash_t1, paste0(out, "_T1.csv"), row.names=F)
    }
  }

  if(nchar(out)!=0 & out!=F){
    write.csv(crash_t2, paste0(out, "_T2.csv"), row.names=F)
    write.csv(crash_code, paste0(out, "_DEF.csv"), row.names=F)
  }

  crash_all=list(T1=crash_t1, T2=crash_t2, DEF=crash_code)
  warning(paste0("Please use '$T1' to retrieve the crash data, and use '$T2' to retrieve the person data."))
  return(crash_all)
}



#' @export
Bus_ScheduleEst=function(access_token, county, routename, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(data.table)) install.packages("data.table")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(cli)) install.packages("cli")
  if (!require(urltools)) install.packages("urltools")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  routename=unique(routename)
  cat(paste0("Total: ", length(routename), " Routes\n"))
  cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}]  {.emph RouteName: {routename[pb_current]}}", total=length(routename))
  num_of_nodata=0

  sch_est_ALL=data.frame()
  for (route in routename){
    cli_progress_update()
    if (county=="Intercity"){
      url=paste0("https://tdx.transportdata.tw/api/premium/V3/Map/Estimation/Bus/Schedule/InterCity/RouteName/", toupper(url_encode(route)), "?&%24format=JSON")
    }else if(county %in% TDX_County$Code[1:22]){
      url=paste0("https://tdx.transportdata.tw/api/premium/V3/Map/Estimation/Bus/Schedule/City/", county, "/RouteName/", toupper(url_encode(route)), "?&%24format=JSON")
    }else{
      print(TDX_County[1:22,])
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      subroute_info=fromJSON(content(x, as="text"))$Schedules
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })
    if(length(subroute_info)==0){
      cli_alert_info(paste0("Data of RouteID: ", route, " is not available."))
      num_of_nodata=num_of_nodata+1
      next
    }

    subroute_info$TripID=c(1:nrow(subroute_info))

    timetable_all=mapply(function(x) subroute_info$Timetables[[x]]$StopTimes, c(1:length(subroute_info$Timetables)))
    for(i in c(1:length(timetable_all))){
      timetable_all[[i]]$StopName=timetable_all[[i]]$StopName$Zh_tw
    }
    timetable_all=rbindlist(timetable_all)

    rep_id=mapply(function(x) nrow(subroute_info$Timetables[[x]]$StopTimes[[1]]), c(1:length(subroute_info$Timetables)))
    timetable_all=data.frame(timetable_all, ServiceTag=rep(mapply(function(x) subroute_info$Timetables[[x]]$ServiceDay$ServiceTag, c(1:length(subroute_info$Timetables))), times=rep_id))

    timetable_all=cbind(subroute_info[rep(c(1:nrow(subroute_info)), times=rep_id), c("TripID","SubRouteUID","SubRouteID","SubRouteName","Direction")], timetable_all)
    row.names(timetable_all)=NULL

    sch_est_ALL=rbind(sch_est_ALL, timetable_all)
  }
  cli_alert_info(ifelse(num_of_nodata==0, "All Done!", paste0("All Done!\n", num_of_nodata, " RouteIDs have no data!")))
  cli_progress_done()

  if (nchar(out)!=0 & out!=F){
    write.csv(sch_est_ALL, out, row.names=F)
  }
  return(sch_est_ALL)
}



#' @export
Bus_Distance=function(access_token, county, routeid, out=F){
  if (!require(dplyr)) install.packages("dplyr")
  if (!require(data.table)) install.packages("data.table")
  if (!require(jsonlite)) install.packages("jsonlite")
  if (!require(httr)) install.packages("httr")
  if (!require(cli)) install.packages("cli")

  if(!(grepl(".csv|.txt", out)) & out!=F){
    stop("The file name must contain '.csv' or '.txt' when exporting text.\n")
  }

  routeid=unique(routeid)
  cat(paste0("Total: ", length(routeid), " Routes\n"))
  cli_progress_bar(format="Downloading {pb_bar} {pb_percent} [{pb_eta}]  {.emph RouteID: {routeid[pb_current]}}", total=length(routeid))
  num_of_nodata=0

  busdist_ALL=data.frame()
  for (route in routeid){
    cli_progress_update()
    if (county=="Intercity"){
      url=paste0("https://tdx.transportdata.tw/api/premium/S2SDistance/InterCity/RouteID/", route, "?&%24format=JSON")
    }else if(county %in% TDX_County$Code[1:22]){
      url=paste0("https://tdx.transportdata.tw/api/premium/S2SDistance/City/", county, "/RouteID/", route, "?&%24format=JSON")
    }else{
      print(TDX_County[1:22,])
      stop(paste0("City: '", county, "' is not valid. Please check out the parameter table above."))
    }
    x=GET(url, add_headers(Accept="application/+json", Authorization=paste("Bearer", access_token)))

    tryCatch({
      subroute_info=fromJSON(content(x, as="text"))
    }, error=function(err){
      stop(paste0("Your access token is invalid!"))
    })
    if(length(subroute_info)==0){
      cli_alert_info(paste0("Data of RouteID: ", route, " is not available."))
      num_of_nodata=num_of_nodata+1
      next
    }else{
      if(sum(lengths(subroute_info$Stops))==0){
        cli_alert_info(paste0("Data of RouteID: ", route, " is not available."))
        num_of_nodata=num_of_nodata+1
        next
      }
    }

    subroute_info$RouteName=subroute_info$RouteName$Zh_tw
    subroute_info$SubRouteName=subroute_info$SubRouteName$Zh_tw

    subroute_info=subroute_info[lengths(subroute_info$Stops)!=0,]

    if(nrow(subroute_info)!=0 & sum(subroute_info$Stops %in% NA)==0){
      rep_id=mapply(function(x) nrow(subroute_info$Stops[[x]]), c(1:nrow(subroute_info)))
      busdist=cbind(subroute_info[rep(c(1:nrow(subroute_info)), rep_id), c("RouteUID","RouteID","RouteName","SubRouteUID","SubRouteID","SubRouteName","Direction")], rbindlist(subroute_info$Stops))
      row.names(busdist)=NULL
      busdist_ALL=rbind(busdist_ALL, busdist)
    }else{
      cli_alert_info(paste0("Data of RouteID: ", route, " is not available."))
    }
  }

  cli_alert_info(ifelse(num_of_nodata==0, "All Done!", paste0("All Done!\n", num_of_nodata, " RouteIDs have no data!")))
  cli_progress_done()

  if (nchar(out)!=0 & out!=F){
    write.csv(busdist_ALL, out, row.names=F)
  }
  return(busdist_ALL)
}
ChiaJung-Yeh/NYCU_TDX documentation built on Aug. 28, 2024, 10:43 p.m.