From 3a966fbad713357ca51db81ff4afeba0ad25abdb Mon Sep 17 00:00:00 2001 From: eblondel Date: Thu, 15 Feb 2018 01:44:34 +0100 Subject: [PATCH] #2 push existing WFS client (draft) --- .travis.yml | 41 +++++ DESCRIPTION | 2 +- NAMESPACE | 16 ++ R/OWSClient.R | 141 +++++++++++++++ R/OWSRequest.R | 39 +++++ R/OWSServiceIdentification.R | 192 +++++++++++++++++++++ R/OWSUtils.R | 152 ++++++++++++++++ R/WFSCapabilities.R | 113 ++++++++++++ R/WFSClient.R | 68 ++++++++ R/WFSDescribeFeatureType.R | 63 +++++++ R/WFSFeatureType.R | 295 ++++++++++++++++++++++++++++++++ R/WFSFeatureTypeElement.R | 112 ++++++++++++ R/WFSGetFeature.R | 43 +++++ man/OWSClient.Rd | 80 +++++++++ man/OWSServiceIdentification.Rd | 53 ++++++ man/WFSCapabilities.Rd | 47 +++++ man/WFSClient.Rd | 48 ++++++ man/WFSDescribeFeatureType.Rd | 37 ++++ man/WFSFeatureType.Rd | 58 +++++++ man/WFSFeatureTypeElement.Rd | 49 ++++++ man/WFSGetFeature.Rd | 34 ++++ tests/testthat/placeholder | 0 tests/testthat/test_WFSClient.R | 41 +++++ 23 files changed, 1723 insertions(+), 1 deletion(-) create mode 100644 .travis.yml create mode 100644 R/OWSClient.R create mode 100644 R/OWSRequest.R create mode 100644 R/OWSServiceIdentification.R create mode 100644 R/OWSUtils.R create mode 100644 R/WFSCapabilities.R create mode 100644 R/WFSClient.R create mode 100644 R/WFSDescribeFeatureType.R create mode 100644 R/WFSFeatureType.R create mode 100644 R/WFSFeatureTypeElement.R create mode 100644 R/WFSGetFeature.R create mode 100644 man/OWSClient.Rd create mode 100644 man/OWSServiceIdentification.Rd create mode 100644 man/WFSCapabilities.Rd create mode 100644 man/WFSClient.Rd create mode 100644 man/WFSDescribeFeatureType.Rd create mode 100644 man/WFSFeatureType.Rd create mode 100644 man/WFSFeatureTypeElement.Rd create mode 100644 man/WFSGetFeature.Rd delete mode 100644 tests/testthat/placeholder create mode 100644 tests/testthat/test_WFSClient.R diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6a63790 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,41 @@ +# R for travis: see documentation at https://docs.travis-ci.com/user/languages/r +language: R +sudo: required +cache: packages + +services: + - docker + +before_install: + - docker pull kartoza/postgis + - docker run -d --name="postgis" kartoza/postgis + - docker pull oscarfonts/geoserver + - docker run --link postgis:postgis -d -p 8080:8080 oscarfonts/geoserver + +r: + - oldrel + - release + - devel + +r_binary_packages: + - rgdal + +r_packages: + - R6 + - httr + - XML + - testthat + - covr + +r_check_args: --as-cran + +after_script: + - ./travis-tool.sh dump_logs + +after_success: + - Rscript -e 'library(covr); codecov()' + +notifications: + email: + on_success: change + on_failure: change \ No newline at end of file diff --git a/DESCRIPTION b/DESCRIPTION index 763b88d..052b2a7 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -3,7 +3,7 @@ Version: 0.1 Date: 2018-02-14 Title: interface to OGC Web-Services (OWS) Authors@R: c(person("Emmanuel", "Blondel", role = c("aut", "cre"), email = "emmanuel.blondel1@gmail.com"), - person("Norbert", "Billet", role = c("ctb"), email = "norbert.billet@ird.fr")) + person("Norbert", "Billet", role = c("ctb"))) Maintainer: Emmanuel Blondel Depends: R (>= 2.15) Imports: R6, httr, XML (>= 3.96-1.1), sp, rgdal diff --git a/NAMESPACE b/NAMESPACE index 6ae9268..ea65f5b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,2 +1,18 @@ # Generated by roxygen2: do not edit by hand +export(OWSClient) +export(OWSRequest) +export(OWSServiceIdentification) +export(OWSUtils) +export(OWSUtilsInternal) +export(WFSCapabilities) +export(WFSClient) +export(WFSDescribeFeatureType) +export(WFSFeatureType) +export(WFSFeatureTypeElement) +export(WFSGetFeature) +import(XML) +import(httr) +import(rgdal) +import(sp) +importFrom(R6,R6Class) diff --git a/R/OWSClient.R b/R/OWSClient.R new file mode 100644 index 0000000..1813e3f --- /dev/null +++ b/R/OWSClient.R @@ -0,0 +1,141 @@ +#' OWSClient +#' +#' @docType class +#' @importFrom R6 R6Class +#' @import httr +#' @import XML +#' @import sp +#' @import rgdal +#' @export +#' @keywords OGC Common OWS +#' @return Object of \code{\link{R6Class}} with methods for interfacing +#' a Common OGC web-service. +#' @format \code{\link{R6Class}} object. +#' +#' @examples +#' \dontrun{ +#' OWSClient$new("http://localhost:8080/geoserver/ows", version = "1.1.0") +#' } +#' +#' @field loggerType the type of logger +#' @field verbose.info if basic logs have to be printed +#' @field verbose.debug if verbose logs have to be printed +#' @field url the Base url of OWS service +#' @field version the version of OWS service +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(url, version, user, pwd, logger)}}{ +#' This method is used to instantiate a OWSClient with the \code{url} of the +#' OGC service. Authentication (\code{user}/\code{pwd}) is not yet supported and will +#' be added with the support of service transactional modes. By default, the \code{logger} +#' argument will be set to \code{NULL} (no logger). This argument accepts two possible +#' values: \code{INFO}: to print only \pkg{ows4R} logs, \code{DEBUG}: to print more verbose logs +#' } +#' \item{\code{logger(type, text)}}{ +#' Basic logger to report logs. Used internally +#' } +#' \item{\code{INFO(text)}}{ +#' Logger to report information. Used internally +#' } +#' \item{\code{WARN(text)}}{ +#' Logger to report warnings. Used internally +#' } +#' \item{\code{ERROR(text)}}{ +#' Logger to report errors. Used internally +#' } +#' \item{\code{getUrl()}}{ +#' Get the service URL +#' } +#' \item{\code{getVersion()}}{ +#' Get the service version +#' } +#' \item{\code{getCapabilities()}}{ +#' Get the service capabilities +#' } +#' \item{\code{getClassName()}}{ +#' Retrieves the name of the class instance +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +OWSClient <- R6Class("OWSClient", + + lock_objects = FALSE, + + #TODO for support of transactional operations + #TODO provider specific formatter to prevent these fields to be printable + private = list( + user = NA, + pwd = NA + ), + + public = list( + #logger + verbose.info = FALSE, + verbose.debug = FALSE, + loggerType = NULL, + logger = function(type, text){ + if(self$verbose.info){ + cat(sprintf("[ows4R][%s] %s \n", type, text)) + } + }, + INFO = function(text){self$logger("INFO", text)}, + WARN = function(text){self$logger("WARN", text)}, + ERROR = function(text){self$logger("ERROR", text)}, + + #fields + url = NA, + version = NA, + capabilities = NA, + + #initialize + initialize = function(url, version, user = NULL, pwd = NULL, logger = NULL) { + + #logger + if(!missing(logger)){ + if(!is.null(logger)){ + self$loggerType <- toupper(logger) + if(!(self$loggerType %in% c("INFO","DEBUG"))){ + stop(sprintf("Unknown logger type '%s", logger)) + } + if(self$loggerType == "INFO"){ + self$verbose.info = TRUE + }else if(self$loggerType == "DEBUG"){ + self$verbose.info = TRUE + self$verbose.debug = TRUE + } + } + } + + #fields + if (!missing(url)) self$url <- url + if (substring(self$url, nchar(self$url)) != "?"){ + self$url <- paste(self$url, "?", sep = "") + } + if (!missing(version)) self$version <- version + }, + + #getUrl + getUrl = function(){ + return(self$url) + }, + + #getVersion + getVersion = function(){ + return(self$version) + }, + + #getCapabilities + getCapabilities = function() { + return(self$capabilities) + }, + + #getClassName + getClassName = function(){ + return(class(self)[1]) + } + ) +) + diff --git a/R/OWSRequest.R b/R/OWSRequest.R new file mode 100644 index 0000000..d8ffd85 --- /dev/null +++ b/R/OWSRequest.R @@ -0,0 +1,39 @@ +#' @export +OWSRequest <- R6Class("OWSRequest", + #private methods + private = list( + #buildRequest + buildRequest = function(baseUrl, namedParams, mimeType){ + params <- paste(names(namedParams), namedParams, sep = "=", collapse = "&") + request <- paste(baseUrl, "&", params, sep = "") + message("Fetching ", request) + r <- GET(request) + responseContent <- NULL + if(is.null(mimeType)){ + responseContent <- content(r) + }else{ + if(regexpr("xml",mimeType)>0){ + responseContent <- xmlParse(content(r, type = "text")) + }else{ + responseContent <- content(r, type = mimeType) + } + } + response <- list(request = request, status = status_code(r), response = responseContent) + return(response) + } + ), + #public methods + public = list( + request = NA, + status = NA, + response = NA, + #initialize + initialize = function(baseUrl, namedParams, mimeType = "text/xml") { + req <- private$buildRequest(baseUrl, namedParams, mimeType) + self$request <- req$request + self$status <- req$status + self$response <- req$response + } + ), + +) \ No newline at end of file diff --git a/R/OWSServiceIdentification.R b/R/OWSServiceIdentification.R new file mode 100644 index 0000000..68678d1 --- /dev/null +++ b/R/OWSServiceIdentification.R @@ -0,0 +1,192 @@ +#' OWSServiceIdentification +#' +#' @docType class +#' @export +#' @keywords OGC OWS Service Identification +#' @return Object of \code{\link{R6Class}} for modelling an OGC Service Identification +#' @format \code{\link{R6Class}} object. +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(xmlObj, url, service)}}{ +#' This method is used to instantiate a OWSServiceIdentification object +#' } +#' \item{\code{getName()}}{ +#' Get service name +#' } +#' \item{\code{getTitle()}}{ +#' Get service title +#' } +#' \item{\code{getAbstract()}}{ +#' Get service abstract +#' } +#' \item{\code{getKeywords()}}{ +#' Get service keywords +#' } +#' \item{\code{getOnlineResource()}}{ +#' Get service online resource +#' } +#' \item{\code{getServiceType}}{ +#' Get service type +#' } +#' \item{\code{getServiceTypeVersion}}{ +#' Get service type version +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +OWSServiceIdentification <- R6Class("OWSServiceIdentification", + private = list( + name = NA, + title = NA, + abstract = NA, + keywords = NA, + onlineResource = NA, + serviceType = NA, + serviceTypeVersion = NA, + + #fetchServiceIdentification + fetchServiceIdentification = function(xmlObj, version, service){ + + namespaces <- NULL + if(all(class(xmlObj) == c("XMLInternalDocument","XMLAbstractDocument"))){ + namespaces <- OWSUtils$getNamespaces(xmlObj) + } + namespaces <- as.data.frame(namespaces) + + namespace <- tolower(service) + + serviceXML <- NULL + if(nrow(namespaces) > 0){ + + ns <- NULL + if(service == "WFS"){ + if(version == "1.0.0"){ + ns <- OWSUtils$findNamespace(namespaces, namespace) + if(!is.null(ns)) serviceXML <- getNodeSet(xmlObj, "//ns:Service", ns) + }else{ + ns <- OWSUtils$findNamespace(namespaces, "ows") + if(!is.null(ns)) serviceXML <- getNodeSet(xmlObj, "//ns:ServiceIdentification", ns) + } + }else if(service == "WMS"){ + ns <- OWSUtils$findNamespace(namespaces, namespace) + if(!is.null(ns)) serviceXML <- getNodeSet(xmlObj, "//ns:Service", ns) + } + }else{ + if(service == "WFS"){ + if(version == "1.0.0"){ + serviceXML <- getNodeSet(xmlObj, "//Service") + }else{ + serviceXML <- getNodeSet(xmlObj, "//ServiceIdentification") + } + }else if(service == "WMS"){ + serviceXML <- getNodeSet(xmlObj, "//Service") + } + } + + serviceName <- NULL + serviceTitle <- NULL + serviceAbstract <- NULL + serviceKeywords <- NULL + serviceOnlineResource <- NULL + serviceType <- NULL + serviceTypeVersion <- NULL + if(length(serviceXML) > 0){ + + serviceXML <- serviceXML[[1]] + + children <- xmlChildren(serviceXML) + + if(!is.null(children$Name)){ + serviceName <- xmlValue(children$Name) + } + if(!is.null(children$Title)){ + serviceTitle <- xmlValue(children$Title) + } + if(!is.null(children$Abstract)){ + serviceAbstract <- xmlValue(children$Abstract) + } + if(!is.null(children$Keywords)){ + + if(version == "1.0.0"){ + serviceKeywords <- strsplit(gsub(" ", "", xmlValue(children$Keywords)), ",")[[1]] + }else{ + serviceKeywordListXML <- xmlChildren(children$Keywords) + serviceKeywords <- as.vector(sapply(serviceKeywordListXML, xmlValue)) + } + + } + if(!is.null(children$OnlineResource)){ + serviceOnlineResource <- xmlValue(children$OnlineResource) + } + if(!is.null(children$ServiceType)){ + serviceType <- xmlValue(children$ServiceType) + } + if(!is.null(children$ServiceTypeVersion)){ + serviceTypeVersion <- xmlValue(children$ServiceTypeVersion) + } + + } + + serviceIdentification <- list( + name = serviceName, + title = serviceTitle, + abstract = serviceAbstract, + keywords = serviceKeywords, + onlineResource = serviceOnlineResource, + serviceType = serviceType, + serviceTypeVersion = serviceTypeVersion + ) + + return(serviceIdentification) + } + ), + public = list( + initialize = function(xmlObj, version, namespace){ + serviceIdentification <- private$fetchServiceIdentification(xmlObj, version, namespace) + private$name <- serviceIdentification$name + private$title <- serviceIdentification$title + private$abstract <- serviceIdentification$abstract + private$keywords <- serviceIdentification$keywords + private$onlineResource <- serviceIdentification$onlineResource + private$serviceType <- serviceIdentification$serviceType + private$serviceTypeVersion <- serviceIdentification$serviceTypeVersion + }, + + #getName + getName = function(){ + return(private$name) + }, + + #getTitle + getTitle = function(){ + return(private$title) + }, + + #getAbstract + getAbstract = function(){ + return(private$abstract) + }, + + #getKeywords + getKeywords = function(){ + return(private$keywords) + }, + + #getOnlineResource + getOnlineResource = function(){ + return(private$onlineResource) + }, + + #getServiceType + getServiceType = function(){ + return(private$serviceType) + }, + + #getServiceTypeVersion + getServiceTypeVersion = function(){ + return(private$serviceTypeVersion) + } + ) +) \ No newline at end of file diff --git a/R/OWSUtils.R b/R/OWSUtils.R new file mode 100644 index 0000000..162ae55 --- /dev/null +++ b/R/OWSUtils.R @@ -0,0 +1,152 @@ +#' @export +OWSUtilsInternal <- R6Class("OWSUtilsInternal", + public = list( + + initialize = function(){ + }, + + #getNamespaces + #--------------------------------------------------------------- + getNamespaces = function(xmlObj) { + nsFromXML <- xmlNamespaceDefinitions(xmlObj) + nsDefs.df <- do.call("rbind", + lapply(nsFromXML, + function(x){ + c(x$id, x$uri) + })) + + if(!is.null(nsDefs.df)){ + row.names(nsDefs.df) <- 1:nrow(nsDefs.df) + nsDefs.df <-as.data.frame(nsDefs.df, stringAsFactors = FALSE) + if(nrow(nsDefs.df) > 0){ + colnames(nsDefs.df) <- c("id","uri") + nsDefs.df$id <- as.character(nsDefs.df$id) + nsDefs.df$uri <- as.character(nsDefs.df$uri) + } + nsDefs.df <- unique(nsDefs.df) + nsDefs.df <- nsDefs.df[!duplicated(nsDefs.df$uri),] + } + + return(nsDefs.df) + }, + + #findNamespace + #--------------------------------------------------------------- + findNamespace = function(namespaces, identifier){ + ns <- c(ns = namespaces$uri[grep(identifier, namespaces$uri)]) + return(ns) + }, + + #toBBOX + toBBOX = function(xmin, xmax, ymin, ymax) { + df <- data.frame(min = c(xmin, ymin), max= c(xmax, ymax)) + row.names(df)<- c("x","y") + m <- as.matrix(df) + return(m) + }, + + #findP4s + findP4s = function(srsName, morphToESRI=FALSE) { + + if (missing(srsName)) { + stop("please provide a spatial reference system name") + } + proj.lst <- as.character(projInfo("proj")$name) + + #we remove the latlong proj for compatibility with sp + proj.lst <- proj.lst[proj.lst != "latlong" & proj.lst != "latlon"] + + #build combinations of know proj and datum + proj.datum.grd <- expand.grid(proj=proj.lst, datum=as.character(projInfo("datum")$name), stringsAsFactors=FALSE) + + #remove the carthage datum which make my system crash + proj.datum.grd <- proj.datum.grd[proj.datum.grd$datum != "carthage", ] + + #function to ask WKT representation + getShowWkt <- function(x) { + res <- try(showWKT(paste("+proj=", x[1], " +datum=", x[2], sep=""), morphToESRI=morphToESRI), silent=TRUE) + if (class(res) == "try-error") { + return(NA) + } else { + return(res) + } + } + + #ask WKT for all projection + GCS <- apply(proj.datum.grd, 1, FUN=getShowWkt) + GCS.df <- data.frame(proj=proj.datum.grd$proj, datum=proj.datum.grd$datum, WKT=GCS, stringsAsFactors=FALSE) + + #keep only valids + GCS.df <- GCS.df[! is.na(GCS.df$WKT),] + + #the pattern to find + pattern <- paste("GEOGCS[\"", srsName, "\"", sep="") + + #search for pattern + GCS.df <- GCS.df[substr(tolower(GCS.df$WKT), 1, nchar(pattern)) == tolower(pattern),] + + #keep only first SRS in case of identical WKT representation + GCS.df <- GCS.df[!duplicated(GCS.df$WKT),] + if (nrow(GCS.df) > 0) { + #return the proj4 definition + return(paste("+proj=", GCS.df$proj, " +datum=", GCS.df$datum, sep="")) + } else { + #not found, return NA + return(NA) + } + }, + + #toCRS + #--------------------------------------------------------------- + toCRS = function(srsName){ + + srsPattern = "EPSG:" #match case 1 + if(attr(regexpr(srsPattern, srsName, ignore.case = T), + "match.length") > 0){ + srsStr <- unlist(strsplit(srsName, ":")) + epsg <- srsStr[length(srsStr)] + srsDef <- paste("+init=epsg:", epsg, sep="") + }else{ + #search if srsName is a WKT PROJ name (PROJCS or GEOGCS) + #if yes set srs with the corresponding proj4string + #first search without any consideration of the ESRI representation + srsDef <- self$findP4s(srsName, morphToESRI=FALSE) + if (is.na(srsDef)) { + #if not found search with consideration of the ESRI representation + srsDef <- self$findP4s(srsName, morphToESRI=TRUE) + } + if (! is.na(srsDef) && ! length(srsDef) == 1) { + srsDef <- NA + } + } + return(CRS(srsDef)) + }, + + #toEPSG + #--------------------------------------------------------------- + toEPSG = function(crs){ + args <- CRSargs(crs) + init <- unlist(strsplit(args," "))[1] + epsg <- toupper(unlist(strsplit(init,"+init="))[2]) + return(epsg) + }, + + #getAspectRatio + #--------------------------------------------------------------- + getAspectRatio = function(bbox){ + ratio <- (bbox[1,2]-bbox[1,1]) / (bbox[2,2]-bbox[2,1]) + return(ratio) + } + + ) +) + +#'@export +OWSUtils <- list( + getNamespaces = OWSUtilsInternal$new()$getNamespaces, + findNamespace = OWSUtilsInternal$new()$findNamespace, + toBBOX = OWSUtilsInternal$new()$toBBOX, + toCRS = OWSUtilsInternal$new()$toCRS, + toEPSG = OWSUtilsInternal$new()$toEPSG, + getAspectRatio = OWSUtilsInternal$new()$getAspectRatio +) \ No newline at end of file diff --git a/R/WFSCapabilities.R b/R/WFSCapabilities.R new file mode 100644 index 0000000..91335d1 --- /dev/null +++ b/R/WFSCapabilities.R @@ -0,0 +1,113 @@ +#' WFSGetCapabilities +#' +#' @docType class +#' @export +#' @keywords OGC WFS GetCapabilities +#' @return Object of \code{\link{R6Class}} with methods for interfacing an OGC +#' Web Feature Service Get Capabilities document. +#' @format \code{\link{R6Class}} object. +#' +#' @examples +#' \dontrun{ +#' WFSGetCapabilities$new("http://localhost:8080/geoserver/wfs", version = "1.1.1") +#' } +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(url, version)}}{ +#' This method is used to instantiate a WFSGetCapabilities object +#' } +#' \item{\code{getServiceIdentification()}}{ +#' Get the service identification +#' } +#' \item{\code{getFeatureTypes()}}{ +#' Retrieves the list of feature types +#' } +#' \item{\code{findFeatureTypeByName(name, exact)}}{ +#' Find feature type(s) by name. +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +WFSCapabilities <- R6Class("WFSCapabilities", + + private = list( + + url = NA, + version = NA, + request = NA, + serviceIdentification = NA, + featureTypes = NA, + + #buildRequest + buildRequest = function(url, version){ + namedParams <- list(request = "GetCapabilities", version = version) + request <- OWSRequest$new(url, namedParams, "text/xml") + return(request) + }, + + #fetchFeatureTypes + fetchFeatureTypes = function(xmlObj, url, version){ + + wfsNs <- NULL + if(all(class(xmlObj) == c("XMLInternalDocument","XMLAbstractDocument"))){ + namespaces <- OWSUtils$getNamespaces(xmlObj) + wfsNs <- OWSUtils$findNamespace(namespaces, "wfs") + } + + featureTypesXML <- getNodeSet(xmlObj, "//ns:FeatureType", wfsNs) + + featureTypesList <- lapply(featureTypesXML, + function(x){ + WFSFeatureType$new(x, url, version) + }) + + return(featureTypesList) + + } + + ), + + public = list( + + #initialize + initialize = function(url, version) { + private$request <- private$buildRequest(url, version) + xmlObj <- private$request$response + private$serviceIdentification <- OWSServiceIdentification$new(xmlObj, version, "WFS") + private$featureTypes = private$fetchFeatureTypes(xmlObj, url, version) + }, + + #getServiceIdentification + getServiceIdentification = function(){ + return(private$serviceIdentification) + }, + + #getFeatureTypes + getFeatureTypes = function(){ + return(private$featureTypes) + }, + + #findFeatureTypeByName + findFeatureTypeByName = function(expr, exact = FALSE){ + result <- lapply(private$featureTypes, + function(x){ + ft <- NULL + if(exact){ + if(x$getName() == expr) ft <- x + }else{ + if(attr(regexpr(expr, x$getName()), + "match.length") != -1){ + ft <- x + } + } + return(ft) + }) + result <- result[!sapply(result, is.null)] + if(length(result) == 1) result <- result[[1]] + return(result) + } + + ) +) \ No newline at end of file diff --git a/R/WFSClient.R b/R/WFSClient.R new file mode 100644 index 0000000..16ee729 --- /dev/null +++ b/R/WFSClient.R @@ -0,0 +1,68 @@ +#' WFSClient +#' +#' @docType class +#' @export +#' @keywords OGC WFS Feature +#' @return Object of \code{\link{R6Class}} with methods for interfacing an OGC +#' Web Feature Service. +#' @format \code{\link{R6Class}} object. +#' +#' @examples +#' \dontrun{ +#' OWSClient$new("http://localhost:8080/geoserver/wfs", version = "1.1.1") +#' } +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(url, version, user, pwd, logger)}}{ +#' This method is used to instantiate a WFSClient with the \code{url} of the +#' OGC service. Authentication (\code{user}/\code{pwd}) is not yet supported and will +#' be added with the support of service transactional modes. By default, the \code{logger} +#' argument will be set to \code{NULL} (no logger). This argument accepts two possible +#' values: \code{INFO}: to print only \pkg{ows4R} logs, \code{DEBUG}: to print more verbose logs +#' } +#' \item{\code{describeFeatureType(typeName)}}{ +#' Get the description of a given featureType +#' } +#' \item{\code{getFeatures(typeName)}}{ +#' Retrieves the features for a given feature type +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +WFSClient <- R6Class("WFSClient", + inherit = OWSClient, + public = list( + #initialize + initialize = function(url, version = NULL, user = NULL, pwd = NULL, logger = NULL) { + super$initialize(url, version, user, pwd, logger) + if(any(attr(regexpr("wfs", self$url),"match.length") == -1, + attr(regexpr("WFS", self$url), "match.length") == -1)){ + self$url <- paste(self$url, "service=WFS", sep = "") + } + self$capabilities = WFSCapabilities$new(self$url, self$version) + }, + + #describeFeatureType + describeFeatureType = function(typeName){ + describeFeatureType <- NULL + ft <- self$capabilities$findFeatureTypeByName(typeName, exact = TRUE) + if(is(ft, "WFSFeatureType")){ + describeFeatureType <- ft$getDescription() + } + return(describeFeatureType) + }, + + #getFeatures + getFeatures = function(typeName){ + features <- NULL + ft <- self$capabilities$findFeatureTypeByName(typeName, exact = TRUE) + if(is(ft,"WFSFeatureType")){ + features <- ft$getFeatures() + } + return(features) + } + ) +) + diff --git a/R/WFSDescribeFeatureType.R b/R/WFSDescribeFeatureType.R new file mode 100644 index 0000000..b29f264 --- /dev/null +++ b/R/WFSDescribeFeatureType.R @@ -0,0 +1,63 @@ +#' WFSDescribeFeatureType +#' +#' @docType class +#' @export +#' @keywords OGC WFS DescribeFeatureType +#' @return Object of \code{\link{R6Class}} for modelling a WFS DescribeFeatureType request +#' @format \code{\link{R6Class}} object. +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(url, version, typeName)}}{ +#' This method is used to instantiate a WFSDescribeFeatureType object +#' } +#' \item{\code{getRequest()}}{ +#' Get DescribeFeatureType request +#' } +#' \item{\code{getContent()}}{ +#' Get content +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +WFSDescribeFeatureType <- R6Class("WFSDescribeFeatureType", + private = list( + request = NA, + content = NA, + + #buildRequest + buildRequest = function(url, version, typeName){ + namedParams <- list(request = "DescribeFeatureType", version = version, typeName = typeName) + request <- OWSRequest$new(url, namedParams, "text/xml") + return(request) + }, + + #fetchFeatureTypeDescription + fetchFeatureTypeDescription = function(xmlObj){ + namespaces <- OWSUtils$getNamespaces(xmlObj) + xsdNs <- OWSUtils$findNamespace(namespaces, "XMLSchema") + elementXML <- getNodeSet(xmlObj, "//ns:sequence/ns:element", xsdNs) + elements <- lapply(elementXML, WFSFeatureTypeElement$new) + return(elements) + } + ), + public = list( + initialize = function(url, version, typeName) { + private$request <- private$buildRequest(url, version, typeName) + xmlObj <- private$request$response + private$content = private$fetchFeatureTypeDescription(xmlObj) + }, + + #getRequest + getRequest = function(){ + return(private$request) + }, + + #getContent + getContent = function(){ + return(private$content) + } + ) + +) \ No newline at end of file diff --git a/R/WFSFeatureType.R b/R/WFSFeatureType.R new file mode 100644 index 0000000..06a1abc --- /dev/null +++ b/R/WFSFeatureType.R @@ -0,0 +1,295 @@ +#' WFSFeatureType +#' +#' @docType class +#' @export +#' @keywords OGC WFS FeatureType +#' @return Object of \code{\link{R6Class}} modelling a WFS feature type +#' @format \code{\link{R6Class}} object. +#' +#' @note Class used internally by ows4R. +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(xmlObj, url, version)}}{ +#' This method is used to instantiate a \code{WFSFeatureType} object +#' } +#' \item{\code{getName()}}{ +#' Get feature type name +#' } +#' \item{\code{getTitle()}}{ +#' Get feature type title +#' } +#' \item{\code{getAbstract()}}{ +#' Get feature type abstract +#' } +#' \item{\code{getKeywords()}}{ +#' Get feature type keywords +#' } +#' \item{\code{getDefaultCRS()}}{ +#' Get feature type default CRS +#' } +#' \item{\code{getBoundingBox()}}{ +#' Get feature type bounding box +#' } +#' \item{\code{getDescription()}}{ +#' Get feature type description +#' } +#' \item{\code{getFeatures()}}{ +#' Get features +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +WFSFeatureType <- R6Class("WFSFeatureType", + + private = list( + gmlIdAttributeName = "gml_id", + + url = NA, + version = NA, + + name = NA, + title = NA, + abstract = NA, + keywords = NA, + defaultCRS = NA, + WGS84BoundingBox = NA, + + description = NA, + features = NULL, + + #fetchFeatureType + fetchFeatureType = function(xmlObj, version){ + + children <- xmlChildren(xmlObj) + + ftName <- NULL + if(!is.null(children$Name)){ + ftName <- xmlValue(children$Name) + } + + ftTitle <- NULL + if(!is.null(children$Title)){ + ftTitle <- xmlValue(children$Title) + } + + ftAbstract <- NULL + if(!is.null(children$Abstract)){ + ftAbstract <- xmlValue(children$Abstract) + } + + ftKeywords <- NULL + if(!is.null(children$Keywords)){ + if(version == "1.0.0"){ + ftKeywords <- strsplit(gsub(" ", "", xmlValue(children$Keywords)), ",")[[1]] + }else{ + ftKeywordListXML <- xmlChildren(children$Keywords) + ftKeywords <- as.vector(sapply(ftKeywordListXML, xmlValue)) + } + } + + ftDefaultCRS <- NULL + if(version == "1.0.0"){ + if(!is.null(children$SRS)){ + ftDefaultCRS <- xmlValue(children$SRS) + } + }else if(version == "1.1.0"){ + if(!is.null(children$DefaultSRS)){ + ftDefaultCRS <- xmlValue(children$DefaultSRS) + } + }else { + if(!is.null(children$DefaultCRS)){ + ftDefaultCRS <- xmlValue(children$DefaultCRS) + } + } + ftDefaultCRS <- OWSUtils$toCRS(ftDefaultCRS) + + ftBoundingBox <- NULL + if(version == "1.0.0"){ + bboxXML <- children$LatLongBoundingBox + if(!is.null(bboxXML)){ + ftBoundingBox <- OWSUtils$toBBOX( + as.numeric(xmlGetAttr(bboxXML,"minx")), + as.numeric(xmlGetAttr(bboxXML,"maxx")), + as.numeric(xmlGetAttr(bboxXML,"miny")), + as.numeric(xmlGetAttr(bboxXML,"maxy")) + ) + + } + }else{ + bboxXML <- children$WGS84BoundingBox + if(!is.null(bboxXML)){ + corners <- xmlChildren(bboxXML) + lc <- as.numeric(unlist(strsplit(xmlValue(corners$LowerCorner)," "))) + uc <- as.numeric(unlist(strsplit(xmlValue(corners$UpperCorner)," "))) + ftBoundingBox <- OWSUtils$toBBOX(lc[1], uc[1], lc[2], uc[2]) + } + } + + featureType <- list( + name = ftName, + title = ftTitle, + abstract = ftAbstract, + keywords = ftKeywords, + defaultCRS = ftDefaultCRS, + WGS84BoundingBox = ftBoundingBox + ) + + return(featureType) + + }, + + #fetchDescription + fetchDescription = function(){ + ftDescription <- WFSDescribeFeatureType$new(private$url, private$version, private$name) + return(ftDescription); + }, + + #fetchFeatures- + fetchFeatures = function(){ + description <- self$getDescription() + ftFeatures <- WFSGetFeature$new(private$url, private$version, private$name) + xmlObj <- ftFeatures$getRequest()$response + + #write the file to disk + tempf = tempfile() + destfile = paste(tempf,".gml",sep='') + saveXML(xmlObj, destfile) + + #download.file(wfsRequest, destfile, mode="wb") + layername <- ogrListLayers(destfile) + if (length(layername) != 1) { + stop("Error with layers in the input dataset") + } + + #hasGeometry? + hasGeometry = FALSE + for(element in description$getContent()){ + if(element$getType() == "Spatial"){ + hasGeometry = TRUE + break + } + } + + #ftFeatures + if(hasGeometry){ + srs <- CRSargs(self$getDefaultCRS()) + ftFeatures = readOGR(destfile, layername, p4s = srs, disambiguateFIDs = TRUE) + ftFeatures <- spChFIDs(ftFeatures, as.character(ftFeatures@data[,private$gmlIdAttributeName])) + + }else{ + if(private$version == "1.0.0"){ + membersContent <- sapply(getNodeSet(xmlObj, "//gml:featureMember"), function(x) xmlChildren(x)) + fid <- sapply(membersContent, function(x) xmlAttrs(x)) + membersAttributes <- xmlToDataFrame( + nodes = getNodeSet(xmlObj, "//gml:featureMember/*[@*]"), + stringsAsFactors = FALSE + ) + + }else if(private$version == "1.1.0"){ + membersContent <- xmlChildren(getNodeSet(xmlObj, "//gml:featureMembers")[[1]]) + fid <- sapply(membersContent, function(x) xmlAttrs(x)) + membersAttributes <- xmlToDataFrame( + nodes = getNodeSet(xmlObj, "//gml:featureMembers/*[@*]"), + stringsAsFactors = FALSE + ) + + }else if(private$version == "2.0.0"){ + membersContent <- sapply(getNodeSet(xmlObj, "//wfs:member"), function(x) xmlChildren(x)) + fid <- sapply(membersContent, function(x) xmlAttrs(x)) + membersAttributes <- xmlToDataFrame( + nodes = getNodeSet(xmlObj, "//wfs:member/*[@*]"), + stringsAsFactors = FALSE + ) + + } + + ftFeatures <- cbind(fid, membersAttributes, stringsAsFactors = FALSE) + } + + #validating attributes + for(element in description$getContent()){ + attrType <- element$getType() + if(!is.null(attrType) && attrType != "Spatial"){ + attrName = element$getName() + if(hasGeometry){ + ftFeatures@data[,attrName] <- as(ftFeatures@data[,attrName], + element$getType()) + }else{ + ftFeatures[,attrName] <- as(ftFeatures[,attrName], + element$getType()) + } + } + } + return(ftFeatures); + } + + ), + public = list( + + initialize = function(xmlObj, url, version){ + + private$url = url + private$version = version + + featureType = private$fetchFeatureType(xmlObj, version) + private$name = featureType$name + private$title = featureType$title + private$abstract = featureType$abstract + private$keywords = featureType$keywords + private$defaultCRS = featureType$defaultCRS + private$WGS84BoundingBox = featureType$WGS84BoundingBox + + }, + + #getName + getName = function(){ + return(private$name) + }, + + #getTitle + getTitle = function(){ + return(private$title) + }, + + #getAbstract + getAbstract = function(){ + return(private$abstract) + }, + + #getKeywords + getKeywords = function(){ + return(private$keywords) + }, + + #getDefaultCRS + getDefaultCRS = function(){ + return(private$defaultCRS) + }, + + #getBoundingBox + getBoundingBox = function(){ + return(private$WGS84BoundingBox) + }, + + #getDescription + getDescription = function(){ + if(typeof(private$description) != "environment"){ + message("Fetching FeatureType description...") + private$description <- private$fetchDescription() + } + return(private$description) + }, + + #getFeatures + getFeatures = function(){ + if(is.null(private$features)){ + message("Fetching FeatureType data...") + private$features <- private$fetchFeatures() + } + return(private$features) + } + + ) +) \ No newline at end of file diff --git a/R/WFSFeatureTypeElement.R b/R/WFSFeatureTypeElement.R new file mode 100644 index 0000000..c9f13a2 --- /dev/null +++ b/R/WFSFeatureTypeElement.R @@ -0,0 +1,112 @@ +#' WFSFeatureTypeElement +#' +#' @docType class +#' @export +#' @keywords OGC WFS FeatureType +#' @return Object of \code{\link{R6Class}} modelling a WFS feature type element +#' @format \code{\link{R6Class}} object. +#' +#' @note Class used internally by ows4R. +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(xmlObj)}}{ +#' This method is used to instantiate a \code{WFSFeatureTypeElement} object +#' } +#' \item{\code{getMinOccurs()}}{ +#' Get min occurs +#' } +#' \item{\code{getMaxOccurs()}}{ +#' Get max occurs +#' } +#' \item{\code{isNillable()}}{ +#' Get TRUE if nillable, FALSE otherwise +#' } +#' \item{\code{getName()}}{ +#' Get element name +#' } +#' \item{\code{getType()}}{ +#' Get element type +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +WFSFeatureTypeElement <- R6Class("WFSFeatureTypeElement", + private = list( + minOccurs = NA, + maxOccurs = NA, + nillable = NA, + name = NA, + type = NA, + + #fetchElement + fetchElement = function(xmlObj){ + + #minOccurs + elementMinOccurs <- xmlGetAttr(xmlObj, "minOccurs") + #maxOccurs + elementMaxOccurs <- xmlGetAttr(xmlObj, "maxOccurs") + #nillable + elementNillable <- ifelse(xmlGetAttr(xmlObj, "nillable") == "true", TRUE, FALSE) + #name + elementName <- xmlGetAttr(xmlObj, "name") + #type + elementType <- NULL + type <- xmlGetAttr(xmlObj, "type") + if(attr(regexpr("gml", type), + "match.length") > 0) elementType <- "Spatial" + if(type == "xsd:string") elementType <- "character" + if(type == "xsd:int") elementType <- "integer" + if(type == "xsd:decimal") elementType <- "double" + if(type == "xsd:boolean") elementType <- "logical" + if(type == "xsd:date") elementType <- "character" #TODO + if(type == "xsd:dateTime") elementType <- "character" #TODO + + element <- list( + minOccurs = elementMinOccurs, + maxOccurs = elementMaxOccurs, + nillable = elementNillable, + name = elementName, + type = elementType + ) + return(element) + } + ), + + public = list( + initialize = function(xmlObj){ + element = private$fetchElement(xmlObj) + private$minOccurs = element$minOccurs + private$maxOccurs = element$maxOccurs + private$nillable = element$nillable + private$name = element$name + private$type = element$type + }, + + #getMinOccurs + getMinOccurs = function(){ + return(private$minOccurs) + }, + + #getMaxOccurs + getMaxOccurs = function(){ + return(private$maxOccurs) + }, + + #isNillable + isNillable = function(){ + return(private$nillable) + }, + + #getName + getName = function(){ + return(private$name) + }, + + #getType + getType = function(){ + return(private$type) + } + ) +) \ No newline at end of file diff --git a/R/WFSGetFeature.R b/R/WFSGetFeature.R new file mode 100644 index 0000000..84ce43d --- /dev/null +++ b/R/WFSGetFeature.R @@ -0,0 +1,43 @@ +#' WFSGetFeature +#' +#' @docType class +#' @export +#' @keywords OGC WFS GetFeature +#' @return Object of \code{\link{R6Class}} for modelling a WFS GetFeature request +#' @format \code{\link{R6Class}} object. +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(url, version, typeName)}}{ +#' This method is used to instantiate a WFSGetFeature object +#' } +#' \item{\code{getRequest()}}{ +#' Get GetFeature request +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +WFSGetFeature <- R6Class("WFSGetFeature", + private = list( + request = NA, + + #buildRequest + buildRequest = function(url, version, typeName){ + namedParams <- list(request = "GetFeature", version = version, typeName = typeName) + request <- OWSRequest$new(url, namedParams, "text/xml") + return(request) + } + + ), + public = list( + initialize = function(url, version, typeName) { + private$request <- private$buildRequest(url, version, typeName) + }, + + #getRequest + getRequest = function(){ + return(private$request) + } + ) +) \ No newline at end of file diff --git a/man/OWSClient.Rd b/man/OWSClient.Rd new file mode 100644 index 0000000..719bed3 --- /dev/null +++ b/man/OWSClient.Rd @@ -0,0 +1,80 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/OWSClient.R +\docType{class} +\name{OWSClient} +\alias{OWSClient} +\title{OWSClient} +\format{\code{\link{R6Class}} object.} +\usage{ +OWSClient +} +\value{ +Object of \code{\link{R6Class}} with methods for interfacing +a Common OGC web-service. +} +\description{ +OWSClient +} +\section{Fields}{ + +\describe{ +\item{\code{loggerType}}{the type of logger} + +\item{\code{verbose.info}}{if basic logs have to be printed} + +\item{\code{verbose.debug}}{if verbose logs have to be printed} + +\item{\code{url}}{the Base url of OWS service} + +\item{\code{version}}{the version of OWS service} +}} + +\section{Methods}{ + +\describe{ + \item{\code{new(url, version, user, pwd, logger)}}{ + This method is used to instantiate a OWSClient with the \code{url} of the + OGC service. Authentication (\code{user}/\code{pwd}) is not yet supported and will + be added with the support of service transactional modes. By default, the \code{logger} + argument will be set to \code{NULL} (no logger). This argument accepts two possible + values: \code{INFO}: to print only \pkg{ows4R} logs, \code{DEBUG}: to print more verbose logs + } + \item{\code{logger(type, text)}}{ + Basic logger to report logs. Used internally + } + \item{\code{INFO(text)}}{ + Logger to report information. Used internally + } + \item{\code{WARN(text)}}{ + Logger to report warnings. Used internally + } + \item{\code{ERROR(text)}}{ + Logger to report errors. Used internally + } + \item{\code{getUrl()}}{ + Get the service URL + } + \item{\code{getVersion()}}{ + Get the service version + } + \item{\code{getCapabilities()}}{ + Get the service capabilities + } + \item{\code{getClassName()}}{ + Retrieves the name of the class instance + } +} +} + +\examples{ +\dontrun{ + OWSClient$new("http://localhost:8080/geoserver/ows", version = "1.1.0") +} + +} +\author{ +Emmanuel Blondel +} +\keyword{Common} +\keyword{OGC} +\keyword{OWS} diff --git a/man/OWSServiceIdentification.Rd b/man/OWSServiceIdentification.Rd new file mode 100644 index 0000000..5f905f1 --- /dev/null +++ b/man/OWSServiceIdentification.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/OWSServiceIdentification.R +\docType{class} +\name{OWSServiceIdentification} +\alias{OWSServiceIdentification} +\title{OWSServiceIdentification} +\format{\code{\link{R6Class}} object.} +\usage{ +OWSServiceIdentification +} +\value{ +Object of \code{\link{R6Class}} for modelling an OGC Service Identification +} +\description{ +OWSServiceIdentification +} +\section{Methods}{ + +\describe{ + \item{\code{new(xmlObj, url, service)}}{ + This method is used to instantiate a OWSServiceIdentification object + } + \item{\code{getName()}}{ + Get service name + } + \item{\code{getTitle()}}{ + Get service title + } + \item{\code{getAbstract()}}{ + Get service abstract + } + \item{\code{getKeywords()}}{ + Get service keywords + } + \item{\code{getOnlineResource()}}{ + Get service online resource + } + \item{\code{getServiceType}}{ + Get service type + } + \item{\code{getServiceTypeVersion}}{ + Get service type version + } +} +} + +\author{ +Emmanuel Blondel +} +\keyword{Identification} +\keyword{OGC} +\keyword{OWS} +\keyword{Service} diff --git a/man/WFSCapabilities.Rd b/man/WFSCapabilities.Rd new file mode 100644 index 0000000..9bd880e --- /dev/null +++ b/man/WFSCapabilities.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/WFSCapabilities.R +\docType{class} +\name{WFSCapabilities} +\alias{WFSCapabilities} +\title{WFSGetCapabilities} +\format{\code{\link{R6Class}} object.} +\usage{ +WFSCapabilities +} +\value{ +Object of \code{\link{R6Class}} with methods for interfacing an OGC +Web Feature Service Get Capabilities document. +} +\description{ +WFSGetCapabilities +} +\section{Methods}{ + +\describe{ + \item{\code{new(url, version)}}{ + This method is used to instantiate a WFSGetCapabilities object + } + \item{\code{getServiceIdentification()}}{ + Get the service identification + } + \item{\code{getFeatureTypes()}}{ + Retrieves the list of feature types + } + \item{\code{findFeatureTypeByName(name, exact)}}{ + Find feature type(s) by name. + } +} +} + +\examples{ +\dontrun{ + WFSGetCapabilities$new("http://localhost:8080/geoserver/wfs", version = "1.1.1") +} + +} +\author{ +Emmanuel Blondel +} +\keyword{GetCapabilities} +\keyword{OGC} +\keyword{WFS} diff --git a/man/WFSClient.Rd b/man/WFSClient.Rd new file mode 100644 index 0000000..c6f5848 --- /dev/null +++ b/man/WFSClient.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/WFSClient.R +\docType{class} +\name{WFSClient} +\alias{WFSClient} +\title{WFSClient} +\format{\code{\link{R6Class}} object.} +\usage{ +WFSClient +} +\value{ +Object of \code{\link{R6Class}} with methods for interfacing an OGC +Web Feature Service. +} +\description{ +WFSClient +} +\section{Methods}{ + +\describe{ + \item{\code{new(url, version, user, pwd, logger)}}{ + This method is used to instantiate a WFSClient with the \code{url} of the + OGC service. Authentication (\code{user}/\code{pwd}) is not yet supported and will + be added with the support of service transactional modes. By default, the \code{logger} + argument will be set to \code{NULL} (no logger). This argument accepts two possible + values: \code{INFO}: to print only \pkg{ows4R} logs, \code{DEBUG}: to print more verbose logs + } + \item{\code{describeFeatureType(typeName)}}{ + Get the description of a given featureType + } + \item{\code{getFeatures(typeName)}}{ + Retrieves the features for a given feature type + } +} +} + +\examples{ +\dontrun{ + OWSClient$new("http://localhost:8080/geoserver/wfs", version = "1.1.1") +} + +} +\author{ +Emmanuel Blondel +} +\keyword{Feature} +\keyword{OGC} +\keyword{WFS} diff --git a/man/WFSDescribeFeatureType.Rd b/man/WFSDescribeFeatureType.Rd new file mode 100644 index 0000000..b05e8dc --- /dev/null +++ b/man/WFSDescribeFeatureType.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/WFSDescribeFeatureType.R +\docType{class} +\name{WFSDescribeFeatureType} +\alias{WFSDescribeFeatureType} +\title{WFSDescribeFeatureType} +\format{\code{\link{R6Class}} object.} +\usage{ +WFSDescribeFeatureType +} +\value{ +Object of \code{\link{R6Class}} for modelling a WFS DescribeFeatureType request +} +\description{ +WFSDescribeFeatureType +} +\section{Methods}{ + +\describe{ + \item{\code{new(url, version, typeName)}}{ + This method is used to instantiate a WFSDescribeFeatureType object + } + \item{\code{getRequest()}}{ + Get DescribeFeatureType request + } + \item{\code{getContent()}}{ + Get content + } +} +} + +\author{ +Emmanuel Blondel +} +\keyword{DescribeFeatureType} +\keyword{OGC} +\keyword{WFS} diff --git a/man/WFSFeatureType.Rd b/man/WFSFeatureType.Rd new file mode 100644 index 0000000..e500787 --- /dev/null +++ b/man/WFSFeatureType.Rd @@ -0,0 +1,58 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/WFSFeatureType.R +\docType{class} +\name{WFSFeatureType} +\alias{WFSFeatureType} +\title{WFSFeatureType} +\format{\code{\link{R6Class}} object.} +\usage{ +WFSFeatureType +} +\value{ +Object of \code{\link{R6Class}} modelling a WFS feature type +} +\description{ +WFSFeatureType +} +\note{ +Class used internally by ows4R. +} +\section{Methods}{ + +\describe{ + \item{\code{new(xmlObj, url, version)}}{ + This method is used to instantiate a \code{WFSFeatureType} object + } + \item{\code{getName()}}{ + Get feature type name + } + \item{\code{getTitle()}}{ + Get feature type title + } + \item{\code{getAbstract()}}{ + Get feature type abstract + } + \item{\code{getKeywords()}}{ + Get feature type keywords + } + \item{\code{getDefaultCRS()}}{ + Get feature type default CRS + } + \item{\code{getBoundingBox()}}{ + Get feature type bounding box + } + \item{\code{getDescription()}}{ + Get feature type description + } + \item{\code{getFeatures()}}{ + Get features + } +} +} + +\author{ +Emmanuel Blondel +} +\keyword{FeatureType} +\keyword{OGC} +\keyword{WFS} diff --git a/man/WFSFeatureTypeElement.Rd b/man/WFSFeatureTypeElement.Rd new file mode 100644 index 0000000..3bd6fdd --- /dev/null +++ b/man/WFSFeatureTypeElement.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/WFSFeatureTypeElement.R +\docType{class} +\name{WFSFeatureTypeElement} +\alias{WFSFeatureTypeElement} +\title{WFSFeatureTypeElement} +\format{\code{\link{R6Class}} object.} +\usage{ +WFSFeatureTypeElement +} +\value{ +Object of \code{\link{R6Class}} modelling a WFS feature type element +} +\description{ +WFSFeatureTypeElement +} +\note{ +Class used internally by ows4R. +} +\section{Methods}{ + +\describe{ + \item{\code{new(xmlObj)}}{ + This method is used to instantiate a \code{WFSFeatureTypeElement} object + } + \item{\code{getMinOccurs()}}{ + Get min occurs + } + \item{\code{getMaxOccurs()}}{ + Get max occurs + } + \item{\code{isNillable()}}{ + Get TRUE if nillable, FALSE otherwise + } + \item{\code{getName()}}{ + Get element name + } + \item{\code{getType()}}{ + Get element type + } +} +} + +\author{ +Emmanuel Blondel +} +\keyword{FeatureType} +\keyword{OGC} +\keyword{WFS} diff --git a/man/WFSGetFeature.Rd b/man/WFSGetFeature.Rd new file mode 100644 index 0000000..12def01 --- /dev/null +++ b/man/WFSGetFeature.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/WFSGetFeature.R +\docType{class} +\name{WFSGetFeature} +\alias{WFSGetFeature} +\title{WFSGetFeature} +\format{\code{\link{R6Class}} object.} +\usage{ +WFSGetFeature +} +\value{ +Object of \code{\link{R6Class}} for modelling a WFS GetFeature request +} +\description{ +WFSGetFeature +} +\section{Methods}{ + +\describe{ + \item{\code{new(url, version, typeName)}}{ + This method is used to instantiate a WFSGetFeature object + } + \item{\code{getRequest()}}{ + Get GetFeature request + } +} +} + +\author{ +Emmanuel Blondel +} +\keyword{GetFeature} +\keyword{OGC} +\keyword{WFS} diff --git a/tests/testthat/placeholder b/tests/testthat/placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/tests/testthat/test_WFSClient.R b/tests/testthat/test_WFSClient.R new file mode 100644 index 0000000..4b9d19b --- /dev/null +++ b/tests/testthat/test_WFSClient.R @@ -0,0 +1,41 @@ +# test_WFS.R +# Author: Emmanuel Blondel +# +# Description: Integration tests for WFS Client +#======================= +require(ows4R, quietly = TRUE) +require(testthat) +context("WFS") + +test_that("WFS 1.0.0",{ + wfs <- WFSClient$new("http://localhost:8080/geoserver/ows?", "1.0.0") + expect_is(wfs, "WFSClient") + caps <- wfs$getCapabilities() + expect_is(caps, "WFSCapabilities") + ft <- caps$findFeatureTypeByName("topp:tasmania_water_bodies", exact = TRUE) + expect_is(ft, "WFSFeatureType") + ft.sp <- ft$getFeatures() + expect_is(ft.sp, "SpatialPolygonsDataFrame") +}) + +test_that("WFS 1.1.0",{ + wfs <- WFSClient$new("http://localhost:8080/geoserver/ows?", "1.1.0") + expect_is(wfs, "WFSClient") + caps <- wfs$getCapabilities() + expect_is(caps, "WFSCapabilities") + ft <- caps$findFeatureTypeByName("topp:tasmania_water_bodies", exact = TRUE) + expect_is(ft, "WFSFeatureType") + ft.sp <- ft$getFeatures() + expect_is(ft.sp, "SpatialPolygonsDataFrame") +}) + +test_that("WFS 2.0.0",{ + wfs <- WFSClient$new("http://localhost:8080/geoserver/ows?", "2.0.0") + expect_is(wfs, "WFSClient") + caps <- wfs$getCapabilities() + expect_is(caps, "WFSCapabilities") + ft <- caps$findFeatureTypeByName("topp:tasmania_water_bodies", exact = TRUE) + expect_is(ft, "WFSFeatureType") + ft.sp <- ft$getFeatures() + expect_is(ft.sp, "SpatialPolygonsDataFrame") +}) \ No newline at end of file