diff --git a/NAMESPACE b/NAMESPACE index cfedd0c..1ac2b6a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,7 +2,9 @@ export(CSWCapabilities) export(CSWClient) +export(CSWDescribeRecord) export(CSWGetRecordById) +export(CSWGetRecords) export(OWSCapabilities) export(OWSClient) export(OWSOperation) diff --git a/R/CSWClient.R b/R/CSWClient.R index f89480a..a0e1595 100644 --- a/R/CSWClient.R +++ b/R/CSWClient.R @@ -24,8 +24,15 @@ #' \item{\code{getCapabilities()}}{ #' Get service capabilities. Inherited from OWS Client #' } +#' \item{\code{describeRecord(namespace, ...)}}{ +#' Describe records. Retrieves the XML schema for CSW records. By default, returns the XML schema +#' for the CSW records (http://www.opengis.net/cat/csw/2.0.2). For other schemas, specify the +#' \code{outputSchema} required, e.g. http://www.isotc211.org/2005/gmd for ISO 19115/19139 schema +#' } #' \item{\code{getRecordById(id, ...)}}{ -#' Get a record by Id. +#' Get a record by Id. By default, the record will be returned following the CSW schema +#' (http://www.opengis.net/cat/csw/2.0.2). For other schemas, specify the +#' \code{outputSchema} required, e.g. http://www.isotc211.org/2005/gmd for ISO 19115/19139 records. #' } #' } #' @@ -44,8 +51,19 @@ CSWClient <- R6Class("CSWClient", }, #describeRecord - describeRecord = function(){ - stop("Not yet implemented") + describeRecord = function(namespace, ...){ + self$INFO("Fetching schema...") + operations <- self$capabilities$getOperationsMetadata()$getOperations() + op <- operations[sapply(operations,function(x){x$getName()=="DescribeRecord"})] + if(length(op)>0){ + op <- op[[1]] + }else{ + errorMsg <- "Operation 'DescribeRecord' not supported by this service" + self$ERROR(errorMsg) + stop(errorMsg) + } + request <- CSWDescribeRecord$new(op, self$getUrl(), self$getVersion(), namespace = namespace, logger = self$loggerType, ...) + return(request$response) }, #getRecordById @@ -65,8 +83,20 @@ CSWClient <- R6Class("CSWClient", }, #getRecords - getRecords = function(outputSchema){ - stop("Not yet implemented") + getRecords = function(constraint = NULL, ...){ + self$INFO("Fetching records ...") + operations <- self$capabilities$getOperationsMetadata()$getOperations() + op <- operations[sapply(operations,function(x){x$getName()=="GetRecords"})] + if(length(op)>0){ + op <- op[[1]] + }else{ + errorMsg <- "Operation 'GetRecords' not supported by this service" + self$ERROR(errorMsg) + stop(errorMsg) + } + request <- CSWGetRecords$new(op, self$getUrl(), self$getVersion(), + constraint = constraint, logger = self$loggerType, ...) + return(request$response) } ) ) diff --git a/R/CSWDescribeRecord.R b/R/CSWDescribeRecord.R new file mode 100644 index 0000000..3ab97a2 --- /dev/null +++ b/R/CSWDescribeRecord.R @@ -0,0 +1,58 @@ +#' CSWDescribeRecord +#' +#' @docType class +#' @export +#' @keywords OGC CSW DescribeRecord +#' @return Object of \code{\link{R6Class}} for modelling a CSW DescribeRecord request +#' @format \code{\link{R6Class}} object. +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(url, version, namespace, ...)}}{ +#' This method is used to instantiate a CSWDescribeRecord object +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +CSWDescribeRecord <- R6Class("CSWDescribeRecord", + inherit = OWSRequest, + private = list( + name = "DescribeRecord", + defaultNamespace = "csw:http://www.opengis.net/cat/csw/2.0.2" + ), + public = list( + initialize = function(op, url, version, namespace = NULL, logger = NULL, ...) { + namedParams <- list(request = private$name, version = version) + + #default output schema + if(is.null(namespace)){ + namespace <- private$defaultNamespace + } + + #other default params + #note: normally typeName not mandatory in DescribeRecord + typeName <- switch(namespace, + "gmd:http://www.isotc211.org/2005/gmd" = "gmd:MD_Metadata", + "gfc:http://www.isotc211.org/2005/gfc" = "gfc:FC_FeatureCatalogue", + "csw:http://www.opengis.net/cat/csw/2.0.2" = "csw:Record", + "dcat:http://www.w3.org/ns/dcat#" = "dcat" + ) + namedParams <- c(namedParams, namespace = namespace, typeName = typeName) + + super$initialize(op, url, namedParams, mimeType = "text/xml", logger = logger, ...) + + #binding to XML schema + xsdObjs <- getNodeSet(self$response, "//ns:schema", c(ns = "http://www.w3.org/2001/XMLSchema")) + if(length(xsdObjs)>0){ + xsdObj <- xsdObjs[[1]] + tempf = tempfile() + destfile = paste(tempf,".xsd",sep='') + saveXML(xsdObj, destfile) + self$response <- xmlSchemaParse(destfile) + }else{ + self$response <- NULL + } + } + ) +) \ No newline at end of file diff --git a/R/CSWGetRecordById.R b/R/CSWGetRecordById.R index d782603..8a164b1 100644 --- a/R/CSWGetRecordById.R +++ b/R/CSWGetRecordById.R @@ -11,9 +11,6 @@ #' \item{\code{new(url, version, id)}}{ #' This method is used to instantiate a CSWGetRecordById object #' } -#' \item{\code{getRequest()}}{ -#' Get GetRecordById request -#' } #' } #' #' @author Emmanuel Blondel @@ -27,11 +24,14 @@ CSWGetRecordById <- R6Class("CSWGetRecordById", public = list( initialize = function(op, url, version, id, logger = NULL, ...) { namedParams <- list(request = private$name, version = version, id = id) + + #default output schema outputSchema <- list(...)$outputSchema if(is.null(outputSchema)){ outputSchema <- private$defaultOutputSchema namedParams <- c(namedParams, outputSchema = outputSchema) } + super$initialize(op, url, namedParams, mimeType = "text/xml", logger = logger, ...) #check response in case of ISO diff --git a/R/CSWGetRecords.R b/R/CSWGetRecords.R new file mode 100644 index 0000000..91e85cd --- /dev/null +++ b/R/CSWGetRecords.R @@ -0,0 +1,86 @@ +#' CSWGetRecords +#' +#' @docType class +#' @export +#' @keywords OGC CSW GetRecords +#' @return Object of \code{\link{R6Class}} for modelling a CSW GetRecords request +#' @format \code{\link{R6Class}} object. +#' +#' @section Methods: +#' \describe{ +#' \item{\code{new(url, version, constraints, ...)}}{ +#' This method is used to instantiate a CSWGetRecords object +#' } +#' } +#' +#' @author Emmanuel Blondel +#' +CSWGetRecords <- R6Class("CSWGetRecords", + inherit = OWSRequest, + private = list( + name = "GetRecords", + defaultOutputSchema = "http://www.opengis.net/cat/csw/2.0.2" + ), + public = list( + initialize = function(op, url, version, constraint = NULL, logger = NULL, ...) { + namedParams <- list(request = private$name, version = version) + if(!is.null(constraint)) namedParams <- c(namedParams, constraint = constraint) + + #default output schema + outputSchema <- list(...)$outputSchema + if(is.null(outputSchema)){ + outputSchema <- private$defaultOutputSchema + namedParams <- c(namedParams, outputSchema = outputSchema) + } + + #other default params + typeNames <- switch(outputSchema, + "http://www.isotc211.org/2005/gmd" = "gmd:MD_Metadata", + "http://www.isotc211.org/2005/gfc" = "gfc:FC_FeatureCatalogue", + "http://www.opengis.net/cat/csw/2.0.2" = "csw:Record", + "http://www.w3.org/ns/dcat#" = "dcat" + ) + namedParams <- c(namedParams, typeNames = typeNames) + namedParams[["resultType"]] <- "results" + namedParams[["CONSTRAINTLANGUAGE"]] <- "CQL_TEXT" + + super$initialize(op, url, namedParams, mimeType = "text/xml", logger = logger, ...) + + #bindings + self$response <- switch(outputSchema, + "http://www.isotc211.org/2005/gmd" = { + out <- NULL + xmlObjs <- getNodeSet(self$response, "//ns:MD_Metadata", c(ns = outputSchema)) + if(length(xmlObjs)>0){ + out <- lapply(xmlObjs,function(xmlObj){ + out.obj <- geometa::ISOMetadata$new() + out.obj$decode(xml = xmlObj) + return(out.obj) + }) + } + out + }, + "http://www.isotc211.org/2005/gfc" = { + out <- NULL + xmlObjs <- getNodeSet(self$response, "//ns:FC_FeatureCatalogue", c(ns = outputSchema)) + if(length(xmlObjs)>0){ + out <- lapply(xmlObjs,function(xmlObj){ + out.obj <- geometa::ISOFeatureCatalogue$new() + out.obj$decode(xml = xmlObj) + return(out.obj) + }) + } + out + }, + "http://www.opengis.net/cat/csw/2.0.2" = { + warnings(sprintf("R binding not yet supported for '%s'", outputSchema)) + self$response + }, + "http://www.w3.org/ns/dcat#" = { + warnings(sprintf("R binding not yet supported for '%s'", outputSchema)) + self$response + } + ) + } + ) +) \ No newline at end of file diff --git a/R/OWSOperation.R b/R/OWSOperation.R index cc825a7..07a42c0 100644 --- a/R/OWSOperation.R +++ b/R/OWSOperation.R @@ -36,7 +36,7 @@ OWSOperation <- R6Class("OWSOperation", private$name <- xmlGetAttr(xmlObj, "name") paramXML <- getNodeSet(xmlDoc(xmlObj), "//ns:Parameter", ns) private$parameters <- lapply(paramXML, function(x){ - param <- xpathSApply(xmlDoc(x), "//ns:Value", fun = xmlValue, namespaces = ns) + param <- unique(xpathSApply(xmlDoc(x), "//ns:Value", fun = xmlValue, namespaces = ns)) return(param) }) names(private$parameters) <- sapply(paramXML, xmlGetAttr, "name") diff --git a/R/OWSOperationsMetadata.R b/R/OWSOperationsMetadata.R index 32da8a3..c5f8a58 100644 --- a/R/OWSOperationsMetadata.R +++ b/R/OWSOperationsMetadata.R @@ -35,7 +35,9 @@ OWSOperationsMetadata <- R6Class("OWSOperationsMetadata", if(nrow(namespaces) > 0){ ns <- OWSUtils$findNamespace(namespaces, namespace) if(length(ns)>0){ - opXML <- getNodeSet(xmlObj, "//ns:OperationsMetadata/ns:Operation", ns) + if(namespace %in% names(ns)){ + opXML <- getNodeSet(xmlObj, "//ns:OperationsMetadata/ns:Operation", ns) + } } if(length(opXML)==0){ ns <- OWSUtils$findNamespace(namespaces, "ows") diff --git a/R/OWSRequest.R b/R/OWSRequest.R index 20d4865..cfd588c 100644 --- a/R/OWSRequest.R +++ b/R/OWSRequest.R @@ -39,7 +39,9 @@ OWSRequest <- R6Class("OWSRequest", responseContent <- content(r, encoding = "UTF-8") }else{ if(regexpr("xml",mimeType)>0){ - responseContent <- xmlParse(content(r, type = "text", encoding = "UTF-8")) + text <- content(r, type = "text", encoding = "UTF-8") + text <- gsub("", "", text) + responseContent <- xmlParse(text) }else{ responseContent <- content(r, type = mimeType, encoding = "UTF-8") } diff --git a/R/OWSServiceIdentification.R b/R/OWSServiceIdentification.R index f2e32e6..972bf0c 100644 --- a/R/OWSServiceIdentification.R +++ b/R/OWSServiceIdentification.R @@ -62,8 +62,10 @@ OWSServiceIdentification <- R6Class("OWSServiceIdentification", if(nrow(namespaces) > 0){ ns <- OWSUtils$findNamespace(namespaces, namespace) if(length(ns)>0){ - serviceXML <- getNodeSet(xmlObj, "//ns:Service", ns) - if(length(serviceXML)==0) serviceXML <- getNodeSet(xmlObj, "//ns:ServiceIdentification", ns) + if(namespace %in% names(ns)){ + serviceXML <- getNodeSet(xmlObj, "//ns:Service", ns) + if(length(serviceXML)==0) serviceXML <- getNodeSet(xmlObj, "//ns:ServiceIdentification", ns) + } } if(length(serviceXML)==0){ ns <- OWSUtils$findNamespace(namespaces, "ows") diff --git a/man/CSWClient.Rd b/man/CSWClient.Rd index c32ea81..5d6cff6 100644 --- a/man/CSWClient.Rd +++ b/man/CSWClient.Rd @@ -28,8 +28,15 @@ CSWClient \item{\code{getCapabilities()}}{ Get service capabilities. Inherited from OWS Client } + \item{\code{describeRecord(namespace, ...)}}{ + Describe records. Retrieves the XML schema for CSW records. By default, returns the XML schema + for the CSW records (http://www.opengis.net/cat/csw/2.0.2). For other schemas, specify the + \code{outputSchema} required, e.g. http://www.isotc211.org/2005/gmd for ISO 19115/19139 schema + } \item{\code{getRecordById(id, ...)}}{ - Get a record by Id. + Get a record by Id. By default, the record will be returned following the CSW schema + (http://www.opengis.net/cat/csw/2.0.2). For other schemas, specify the + \code{outputSchema} required, e.g. http://www.isotc211.org/2005/gmd for ISO 19115/19139 records. } } } diff --git a/man/CSWDescribeRecord.Rd b/man/CSWDescribeRecord.Rd new file mode 100644 index 0000000..eec4e46 --- /dev/null +++ b/man/CSWDescribeRecord.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/CSWDescribeRecord.R +\docType{class} +\name{CSWDescribeRecord} +\alias{CSWDescribeRecord} +\title{CSWDescribeRecord} +\format{\code{\link{R6Class}} object.} +\usage{ +CSWDescribeRecord +} +\value{ +Object of \code{\link{R6Class}} for modelling a CSW DescribeRecord request +} +\description{ +CSWDescribeRecord +} +\section{Methods}{ + +\describe{ + \item{\code{new(url, version, namespace, ...)}}{ + This method is used to instantiate a CSWDescribeRecord object + } +} +} + +\author{ +Emmanuel Blondel +} +\keyword{CSW} +\keyword{DescribeRecord} +\keyword{OGC} diff --git a/man/CSWGetRecordById.Rd b/man/CSWGetRecordById.Rd index acbb320..f6e50a9 100644 --- a/man/CSWGetRecordById.Rd +++ b/man/CSWGetRecordById.Rd @@ -20,9 +20,6 @@ CSWGetRecordById \item{\code{new(url, version, id)}}{ This method is used to instantiate a CSWGetRecordById object } - \item{\code{getRequest()}}{ - Get GetRecordById request - } } } diff --git a/man/CSWGetRecords.Rd b/man/CSWGetRecords.Rd new file mode 100644 index 0000000..24a59af --- /dev/null +++ b/man/CSWGetRecords.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/CSWGetRecords.R +\docType{class} +\name{CSWGetRecords} +\alias{CSWGetRecords} +\title{CSWGetRecords} +\format{\code{\link{R6Class}} object.} +\usage{ +CSWGetRecords +} +\value{ +Object of \code{\link{R6Class}} for modelling a CSW GetRecords request +} +\description{ +CSWGetRecords +} +\section{Methods}{ + +\describe{ + \item{\code{new(url, version, constraints, ...)}}{ + This method is used to instantiate a CSWGetRecords object + } +} +} + +\author{ +Emmanuel Blondel +} +\keyword{CSW} +\keyword{GetRecords} +\keyword{OGC} diff --git a/tests/testthat/test_CSWClient.R b/tests/testthat/test_CSWClient.R index 2ab7458..049a974 100644 --- a/tests/testthat/test_CSWClient.R +++ b/tests/testthat/test_CSWClient.R @@ -15,8 +15,21 @@ test_that("CSW 2.0.2 - GetCapabilities",{ expect_is(caps, "CSWCapabilities") }) +test_that("CSW 2.0.2 - GetRecords",{ + csw <- CSWClient$new("http://www.fao.org/geonetwork/srv/en/csw", "2.0.2", logger = "INFO") + xsd <- csw$describeRecord(outputSchema = "http://www.isotc211.org/2005/gmd") + +}) + test_that("CSW 2.0.2 - GetRecordById",{ csw <- CSWClient$new("http://www.fao.org/geonetwork/srv/en/csw", "2.0.2", logger = "INFO") md <- csw$getRecordById("fao-species-map-tth", outputSchema = "http://www.isotc211.org/2005/gmd") expect_is(md, "ISOMetadata") +}) + +test_that("CSW 2.0.2 - GetRecords",{ + csw <- CSWClient$new("http://www.fao.org/geonetwork/srv/en/csw", "2.0.2", logger = "INFO") + mdlist <- csw$getRecords(constraint = "AnyText+like+%cwp-grid%", outputSchema = "http://www.isotc211.org/2005/gmd") + expect_is(mdlist, "list") + expect_equal(unique(sapply(mdlist, function(x) {class(x)[1]})), "ISOMetadata") }) \ No newline at end of file