;
; NOSA HEADER START
;
; The contents of this file are subject to the terms of the NASA Open
; Source Agreement (NOSA), Version 1.3 only (the "Agreement"). You may
; not use this file except in compliance with the Agreement.
;
; You can obtain a copy of the agreement at
; docs/NASA_Open_Source_Agreement_1.3.txt
; or
; https://cdaweb.gsfc.nasa.gov/WebServices/NASA_Open_Source_Agreement_1.3.txt.
;
; See the Agreement for the specific language governing permissions
; and limitations under the Agreement.
;
; When distributing Covered Code, include this NOSA HEADER in each
; file and include the Agreement file at
; docs/NASA_Open_Source_Agreement_1.3.txt. If applicable, add the
; following below this NOSA HEADER, with the fields enclosed by
; brackets "[]" replaced with your own identifying information:
; Portions Copyright [yyyy] [name of copyright owner}
;
; NOSA HEADER END
;
; Copyright (c) 2010-2023 United States Government as represented by the
; National Aeronautics and Space Administration. No copyright is claimed
; in the United States under Title 17, U.S.Code. All Other Rights
; Reserved.
;
;
;+
; This class represents the remotely callable interface to
; <a href="https://www.nasa.gov/">NASA</a>'s
; <a href="https://spdf.gsfc.nasa.gov/">Space Physics Data Facility</a>
; (SPDF)
; <a href="https://cdaweb.gsfc.nasa.gov/">Coordinated Data Analysis
; System</a> (CDAS).
;
; @copyright Copyright (c) 2010-2023 United States Government as
; represented by the National Aeronautics and Space Administration.
; No copyright is claimed in the United States under Title 17,
; U.S.Code. All Other Rights Reserved.
;
; @author B. Harris
;-
;+
; Creates an object representing CDAS.
;
; If access to the Internet is through an HTTP proxy, the caller
; should ensure that the HTTP_PROXY environment is correctly set
; before this method is called. The HTTP_PROXY value should be of
; the form
; http://username:password@hostname:port/.
;
; @keyword endpoint {in} {optional} {type=string}
; {default='https://cdaweb.gsfc.nasa.gov/WS/cdasr/1'}
; URL of CDAS web service.
; @keyword userAgent {in} {optional} {type=string} {default=WsExample}
; HTTP user-agent value used in communications with CDAS.
; @keyword defaultDataview {in} {optional} {type=string}
; {default=sp_phys}
; default CDAS dataview value to use in subsequent calls
; when no value is specified.
; @keyword sslVerifyPeer {in} {optional} {type=int}
; {default=SpdfGetDefaultSslVerifyPeer()}
; Specifies whether the authenticity of the peer's SSL
; certificate should be verified. When 0, the connection
; succeeds regardless of what the peer SSL certificate
; contains.
; @returns a reference to a CDAS object.
;-
function SpdfCdas::init, $
endpoint = endpoint, $
userAgent = userAgent, $
defaultDataview = defaultDataview, $
sslVerifyPeer = sslVerifyPeer
compile_opt idl2
self.endpoint = 'https://cdaweb.gsfc.nasa.gov/WS/cdasr/1'
self.version = '%VERSION*'
self.currentVersionUrl = $
'https://cdaweb.gsfc.nasa.gov/WebServices/REST/spdfCdasVersion.txt'
if keyword_set(endpoint) then self.endpoint = endpoint
if ~keyword_set(userAgent) then userAgent = 'WsExample'
self.userAgent = 'User-Agent: ' + userAgent + '/' + $
self.version + ' (' + !version.os + ' ' + !version.arch + $
') IDL/' + !version.release
self.defaultDataview = 'sp_phys'
if keyword_set(defaultDataview) then begin
self.defaultDataview = defaultDataview
endif
self.ssl_verify_peer = SpdfGetDefaultSslVerifyPeer()
if n_elements(sslVerifyPeer) gt 0 then begin
self.ssl_verify_peer = sslVerifyPeer
endif
self.proxySettings = obj_new('SpdfHttpProxy')
self.retryLimit = 100
return, self
end
;+
; Performs cleanup operations when this object is destroyed.
;-
pro SpdfCdas::cleanup
compile_opt idl2
if obj_valid(self.proxySettings) then obj_destroy, self.proxySettings
end
;+
; Gets the current endpoint value.
;
; @returns current endpoint string value.
;-
function SpdfCdas::getEndpoint
compile_opt idl2
return, self.endpoint
end
;+
; Gets the current userAgent value.
;
; @returns current userAgent string value.
;-
function SpdfCdas::getUserAgent
compile_opt idl2
return, self.userAgent
end
;+
; Gets the current defaultDataview value.
;
; @returns current defaultDataview string value.
;-
function SpdfCdas::getDefaultDataview
compile_opt idl2
return, self.defaultDataview
end
;+
; Gets the version of this class.
;
; @returns version of this class.
;-
function SpdfCdas::getVersion
compile_opt idl2
return, self.version
end
;+
; Gets the most up to date version of this class.
;
; @returns most up to date version of this class.
;-
function SpdfCdas::getCurrentVersion
compile_opt idl2
catch, errorStatus
if (errorStatus ne 0) then begin
catch, /cancel
url->getProperty, response_code=responseCode
print, 'SpdfCdas::getCurrentVersion failed with responseCode ', $
responseCode, ' for ', self.currentVersionUrl
print, ' with HTTP_PROXY = ', getenv('HTTP_PROXY')
return, ''
endif
url = obj_new('IDLnetURL', $
proxy_authentication = $
self.proxySettings.getAuthentication(), $
proxy_hostname = self.proxySettings.getHostname(), $
proxy_port = self.proxySettings.getPort(), $
proxy_username = self.proxySettings.getUsername(), $
proxy_password = self.proxySettings.getPassword())
version = url->get(/string_array, url=self.currentVersionUrl)
if (n_elements(version) ne 1) then begin
; May have been redirected to pleasecontactus.html
return, ''
endif
; May want to add a regex validation here.
return, version
end
;+
; Compares getVersion() and getCurrentVersion() to determine if this
; class is up to date.
;
; @returns true if getVersion() >= getCurrentVersion(). Otherwise
; false.
;-
function SpdfCdas::isUpToDate
compile_opt idl2
version = strsplit(self->getVersion(), '.', /extract)
versionElements = n_elements(version)
currentVersion = strsplit(self->getCurrentVersion(), '.', /extract)
currentVersionElements = n_elements(currentVersion)
if versionElements eq 1 or currentVersionElements eq 1 then begin
; Do not know what the versions are so return up-to-date
return, 1
endif
if versionElements lt currentVersionElements then begin
elements = versionElements
endif else begin
elements = currentVersionElements
endelse
for i = 0, elements - 1 do begin
if 0 + version[i] lt 0 + currentVersion[i] then return, 0
endfor
if versionElements lt currentVersionElements then begin
return, 0
endif else begin
return, 1
endelse
end
;+
; "Percent encodes" the given string to escape reserved charaters. This
; method is merely a wrapper around the IDLnetURL::URLEncode method which
; does nothing on versions of IDL where the IDLnetURL::URLEncode method
; does not exist.
;
; @param value {in} {required} {type=string}
; the string to be encoded.
; @returns percent encoded representation of the given value or the
; orignal value if called on a version of IDL without the
; IDLnetURL::URLEncode method.
;-
function SpdfCdas::encode, $
value
compile_opt idl2
; compile_opt static (idl >= 8.3)
catch, errorStatus
if (errorStatus ne 0) then begin
; idl version < 8.5.1
catch, /cancel
return, value
endif
return, IDLnetURL.URLEncode(value)
end
;+
; Gets the node's value of the first child of the first item of the
; specified element of the given DOM document.
;
; @private
;
; @param domElement {in} {required} {type=IDLffXMLDOMDocument}
; DOM element to search
; @param tagName {in} {required} {type=string}
; A scalar string containing the tag name of the desired
; element.
; @returns node's string value(s) of the first child of the item(s)
; of the specified element of the given DOM document. An
; empty string is returned if the value cannot be found.
;-
function SpdfCdas::getNamedElementsFirstChildValue, $
domElement, tagName
compile_opt idl2
nodeList = domElement->getElementsByTagName(tagName)
if nodeList->getLength() eq 0 then return, ''
values = strarr(nodeList->getLength())
for i = 0, nodeList->getLength() - 1 do begin
domNode = nodeList->item(i)
child = domNode->getFirstChild()
if obj_valid(child) then begin
values[i] = child->getNodeValue()
endif else begin
values[i] = ''
endelse
endfor
if n_elements(values) eq 1 then return, values[0] $
else return, values
end
;+
; Gets a description of all the dataviews that are available.
;
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns array of SpdfDataviewDescription objects.
; If there are no dataviews, an array of length
; one is returned with the first element being a null
; object reference.
;-
function SpdfCdas::getDataviews, $
httpErrorReporter=errorReporter
compile_opt idl2
url = self.endpoint + '/dataviews'
dvDom = self->makeGetRequest('N/A', url, $
errorReporter=errorReporter)
if ~obj_valid(dvDom) then return, objarr(1)
dataviewElements = $
dvDom->getElementsByTagName('DataviewDescription')
dataviews = objarr(dataviewElements->getLength(), /nozero)
for i = 0, dataviewElements->getLength() - 1 do begin
dvElement = dataviewElements->item(i)
id = $
self->getNamedElementsFirstChildValue(dvElement, 'Id')
endpointAddress = $
self->getNamedElementsFirstChildValue(dvElement, $
'EndpointAddress')
title = $
self->getNamedElementsFirstChildValue(dvElement, 'Title')
subtitle = $
self->getNamedElementsFirstChildValue(dvElement, 'SubTitle')
overview = $
self->getNamedElementsFirstChildValue(dvElement, 'Overview')
underConstructionStr = $
self->getNamedElementsFirstChildValue(dvElement, $
'UnderConstruction')
if strcmp(underConstructionStr, 'true', /fold_case) eq 1 $
then begin
underConstruction = 1b
endif else begin
underConstruction = 0b
endelse
noticeUrl = $
self->getNamedElementsFirstChildValue(dvElement, $
'NoticeUrl')
publicAccessStr = $
self->getNamedElementsFirstChildValue(dvElement, $
'PublicAccess')
if (strcmp(publicAccessStr, 'true', /fold_case) eq 1) then begin
publicAccess = 1b
endif else begin
publicAccess = 0b
endelse
dataviews[i] = $
obj_new('SpdfDataviewDescription', id, endpointAddress, $
title, subtitle, overview, underConstruction, $
noticeUrl, publicAccess)
endfor
obj_destroy, dvDom
return, dataviews
end
;+
; Gets a description of all the observatory groups.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @keyword instrumentTypes {in} {optional} {type=strarr}
; names of instrument-types which restrict the returned
; observatory groups to only those supporting the specified
; instrument-types. Valid values are those returned by
; getInstrumentTypes.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns array of SpdfObservatoryGroupDescription objects.
; If there are no observatory groups, an array of length
; one is returned with the first element being a null
; object reference.
;-
function SpdfCdas::getObservatoryGroups, $
dataview = dataview, $
instrumentTypes = iTypes, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
if ~keyword_set(dataview) then dataview = self.defaultDataview
url = self.endpoint + '/dataviews/' + dataview + $
'/observatoryGroups?'
for i = 0, n_elements(iTypes) - 1 do begin
url = url + 'instrumentType=' + iTypes[i] + '&'
endfor
url = strmid(url, 0, strlen(url) - 1)
ogDom = self->makeGetRequest(dataview, url, $
authenticator = authenticator, $
errorReporter = errorReporter)
if ~obj_valid(ogDom) then return, objarr(1)
ogElements = $
ogDom->getElementsByTagName('ObservatoryGroupDescription')
observatoryGroups = objarr(ogElements->getLength(), /nozero)
for i = 0, ogElements->getLength() - 1 do begin
ogElement = ogElements->item(i)
name = $
self->getNamedElementsFirstChildValue(ogElement, 'Name')
observatoryIds = $
self->getNamedElementsFirstChildValue(ogElement, $
'ObservatoryId')
observatoryGroups[i] = $
obj_new('SpdfObservatoryGroupDescription', name, $
observatoryIds)
endfor
obj_destroy, ogDom
return, observatoryGroups
end
;+
; Gets a description of all the instrument types.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @keyword observatoryGroups {in} {optional} {type=strarr}
; names of observatory-groups which restrict the returned
; instrument types to only those supporting the specified
; observatory-groups. Valid values are those returned by
; getObservatoryGroups.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns array of SpdfInstrumentTypeDescription objects.
; If there are no intrument types, an array of length one
; is returned with the first element being a null object
; reference.
;-
function SpdfCdas::getInstrumentTypes, $
dataview = dataview, $
observatoryGroups = oGroups, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
if ~keyword_set(dataview) then dataview = self.defaultDataview
url = self.endpoint + '/dataviews/' + dataview + '/instrumentTypes?'
for i = 0, n_elements(oGroups) - 1 do begin
url = url + 'observatoryGroup=' + oGroups[i] + '&'
endfor
url = strmid(url, 0, strlen(url) - 1)
itDom = self->makeGetRequest(dataview, url, $
authenticator = authenticator, $
errorReporter = errorReporter)
if ~obj_valid(itDom) then return, objarr(1)
itElements = $
itDom->getElementsByTagName('InstrumentTypeDescription')
instrumentTypes = objarr(itElements->getLength(), /nozero)
for i = 0, itElements->getLength() - 1 do begin
itElement = itElements->item(i)
name = $
self->getNamedElementsFirstChildValue(itElement, 'Name')
instrumentTypes[i] = $
obj_new('SpdfInstrumentTypeDescription', name)
endfor
obj_destroy, itDom
return, instrumentTypes
end
;+
; Gets a description of all the datasets.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @keyword observatoryGroups {in} {optional} {type=strarr}
; names of observatory-groups which restrict the returned
; datasets to only those supporting the specified
; observatory-groups. Valid values are those returned by
; getObservatoryGroups.
; @keyword instrumentTypes {in} {optional} {type=strarr}
; names of instrument-types which restrict the returned
; datasets to only those supporting the specified
; instrument-types. Valid values are those returned by
; getInstrumentTypes.
; @keyword observatories {in} {optional} {type=strarr}
; names of observatories which restrict the returned
; datasets to only those supporting the specified
; observatories. Valid values are those returned by
; getObservatories.
; @keyword instruments {in} {optional} {type=strarr}
; names of instruments which restrict the returned
; datasets to only those supporting the specified
; instruments. Valid values are those returned by
; getInstruments.
; @keyword startDate {in} {optional} {type=julday}
; value that restricts the returned dataset to only
; those that contain data after this date.
; @keyword stopDate {in} {optional} {type=julday}
; value that restricts the returned dataset to only
; those that contain data before this date.
; @keyword identifier {in} {optional} {type=string}
; a dataset identifier. The value may be a CDAS
; (e.g., AC_H2_MFI), DOI (e.g., 10.48322/fh85-fj47),
; or SPASE [ResourceID]
; (e.g., spase://NASA/NumericalData/ACE/MAG/L2/PT1H)
; identifier. If specified, all other keywords are
; ignored.
; @keyword idPattern {in} {optional} {type=string}
; a java.util.regex compatible
; <a href="https://en.wikipedia.org/wiki/Regex">regular
; expression</a> that must match the dataset's identifier
; value. Omitting this parameter is equivalent to ".*".
; @keyword labelPattern {in} {optional} {type=string}
; a java.util.regex compatible
; <a href="https://en.wikipedia.org/wiki/Regex">regular
; expression</a> that must match the dataset's label
; text. Omitting this parameter is equivalent to ".*".
; Embedded matching flag expressions (e.g., (?i) for
; case insensitive match mode) are supported and likely
; to be useful in this case.
; @keyword notesPattern {in} {optional} {type=string}
; a java.util.regex compatible
; <a href="https://en.wikipedia.org/wiki/Regex">regular
; expression</a> that must match the dataset's notes
; text. Omitting this parameter is equivalent to ".*".
; Embedded matching flag expressions (e.g., (?i) for
; case insensitive match mode) are supported and likely
; to be useful in this case.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns array of SpdfDatasetDescription objects. If there are no
; datasets, an array of length one is returned with the
; first element being a null object reference.
;-
function SpdfCdas::getDatasets, $
dataview = dataview, $
observatoryGroups = observatoryGroups, $
instrumentTypes = instrumentTypes, $
observatories = observatories, $
instruments = instruments, $
startDate = startDate, $
stopDate = stopDate, $
identifier = identifier, $
idPattern = idPattern, $
labelPattern = labelPattern, $
notesPattern = notesPattern, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
if ~keyword_set(dataview) then dataview = self.defaultDataview
url = self.endpoint + '/dataviews/' + dataview + '/datasets?'
for i = 0, n_elements(observatoryGroups) - 1 do begin
url = url + 'observatoryGroup=' + observatoryGroups[i] + '&'
endfor
for i = 0, n_elements(instrumentTypes) - 1 do begin
url = url + 'instrumentType=' + instrumentTypes[i] + '&'
endfor
for i = 0, n_elements(observatories) - 1 do begin
url = url + 'observatory=' + observatories[i] + '&'
endfor
for i = 0, n_elements(instruments) - 1 do begin
url = url + 'instrument=' + instruments[i] + '&'
endfor
if keyword_set(startDate) then begin
url = url + 'startDate=' + self->julDay2Iso8601(startDate) + '&'
endif
if keyword_set(stopDate) then begin
url = url + 'stopDate=' + self->julDay2Iso8601(stopDate) + '&'
endif
if keyword_set(identifier) then begin
url = url + 'id=' + identifier+ '&'
endif
if keyword_set(idPattern) then begin
url = url + 'idPattern=' + idPattern + '&'
endif
if keyword_set(labelPattern) then begin
url = url + 'labelPattern=' + labelPattern + '&'
endif
if keyword_set(notesPattern) then begin
url = url + 'notesPattern=' + notesPattern + '&'
endif
url = strmid(url, 0, strlen(url) - 1)
dsDom = self->makeGetRequest(dataview, url, $
authenticator = authenticator, $
errorReporter = errorReporter)
if ~obj_valid(dsDom) then begin
return, objarr(1)
endif
dsElements = $
dsDom->getElementsByTagName('DatasetDescription')
datasets = objarr(dsElements->getLength(), /nozero)
for i = 0, dsElements->getLength() - 1 do begin
dsElement = dsElements->item(i)
id = $
self->getNamedElementsFirstChildValue(dsElement, 'Id')
doi = $
self->getNamedElementsFirstChildValue(dsElement, 'Doi')
resourceId = $
self->getNamedElementsFirstChildValue(dsElement, 'SpaseResourceId')
observatories = $
self->getNamedElementsFirstChildValue(dsElement, $
'Observatory')
instruments = $
self->getNamedElementsFirstChildValue(dsElement, $
'Instrument')
observatoryGroups = $
self->getNamedElementsFirstChildValue(dsElement, $
'ObservatoryGroup')
instrumentTypes = $
self->getNamedElementsFirstChildValue(dsElement, $
'InstrumentTypes')
label = $
self->getNamedElementsFirstChildValue(dsElement, 'Label')
timeInterval = self->getTimeIntervalChild(dsElement)
piName = $
self->getNamedElementsFirstChildValue(dsElement, 'PiName')
piAffiliation = self->getNamedElementsFirstChildValue($
dsElement, 'PiAffiliation')
notes = $
self->getNamedElementsFirstChildValue(dsElement, 'Notes')
linkElements = dsElement->getElementsByTagName('DatasetLink')
if linkElements->getLength() gt 0 then begin
datasetLinks = objarr(linkElements->getLength(), /nozero)
for j = 0, linkElements->getLength() - 1 do begin
linkElement = linkElements->item(j)
title = $
self->getNamedElementsFirstChildValue(linkElement, $
'Title')
text = $
self->getNamedElementsFirstChildValue(linkElement, $
'Text')
url = $
self->getNamedElementsFirstChildValue(linkElement, $
'Url')
datasetLinks[j] = $
obj_new('SpdfDatasetLink', title, text, url)
endfor
endif else begin
datasetLinks = obj_new()
endelse
mdElements = dsElement->getElementsByTagName('AdditionalMetadata')
if mdElements->getLength() gt 0 then begin
additionalMd = objarr(mdElements->getLength(), /nozero)
for j = 0, mdElements->getLength() - 1 do begin
mdElement = mdElements->item(j)
attrs = mdElement->GetAttributes()
typeNode = attrs->getNamedItem('Type')
type = typeNode->GetNodeValue()
mdElementChild = mdElement->getFirstChild()
url = mdElementChild->GetNodeValue()
additionalMd[j] = $
obj_new('SpdfAdditionalMetadata', type, url)
endfor
endif else begin
additionalMd = obj_new()
endelse
datasets[i] = $
obj_new('SpdfDatasetDescription', id, observatories, $
instruments, observatoryGroups, instrumentTypes, $
label, timeInterval, piName, piAffiliation, notes, $
datasetLinks, doi, resourceId, additionalMd)
endfor
obj_destroy, dsDom
return, datasets
end
;+
; Gets a description of a dataset's data inventory.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param dataset {in} {type=string}
; identifies the dataset. A <a href="https://www.doi.org/">DOI</a>
; value requires IDL 8.5.1 higher.
; @keyword timeInterval {in} {optional} {type=SpdfTimeInterval}
; TimeInteval to restrict return inventory.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns an SpdfInventoryDescription or a null reference if no
; inventory is available.
;-
function SpdfCdas::getInventory, $
dataview = dataview, dataset, $
timeInterval = timeInterval, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
if ~keyword_set(dataview) then dataview = self.defaultDataview
url = self.endpoint + '/dataviews/' + dataview + '/datasets/' + $
self->encode(dataset) + '/inventory'
if keyword_set(timeInterval) then begin
url = url + '/' + timeInterval.getIso8601Start(basicFormat=1) + $
',' + timeInterval.getIso8601Stop(basicFormat=1)
endif
inventoryDom = self->makeGetRequest(dataview, url, $
authenticator = authenticator, $
errorReporter = errorReporter)
inventoryElements = $
inventoryDom->getElementsByTagName('InventoryDescription')
if inventoryElements->getLength() eq 0 then begin
obj_destroy, inventoryDom
return, obj_new()
endif
inventoryElement = inventoryElements->item(0)
id = self->getNamedElementsFirstChildValue(inventoryElement, 'Id')
timeIntervalElements = $
inventoryElement->getElementsByTagName('TimeInterval')
if timeIntervalElements->getLength() gt 0 then begin
timeIntervals = $
objarr(timeIntervalElements->getLength(), /nozero)
for i = 0, timeIntervalElements->getLength() - 1 do begin
timeIntervals[i] = $
self->getTimeInterval(timeIntervalElements->item(i))
endfor
endif else begin
timeIntervals = obj_new()
endelse
obj_destroy, inventoryDom
return, obj_new('SpdfInventoryDescription', id, timeIntervals)
end
;+
; Gets a small example time interval for the specified dataset. The
; interval is near the end of the dataset's data inventory. The
; returned interval is not guaranteed to have non-fill data for any
; specific variable.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param dataset {in} {type=string}
; identifies the dataset. A <a href="https://www.doi.org/">DOI</a>
; value requires IDL 8.5.1 higher.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns an SpdfTimeInterval that is likely, but not guaranteed, to
; have data or a null reference if no interval is found.
;-
function SpdfCdas::getExampleTimeInterval, $
dataview = dataview, $
dataset, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
inventory = self->getInventory(dataview = dataview, dataset, $
authenticator = authenticator, $
HttpErrorReporter = errorReporter)
if obj_valid(inventory) then begin
intervals = inventory.getTimeIntervals()
example = intervals[-1]
if stregex(dataset, 'MMS[1-4]_.+_BRST_.+') ne -1 then begin
delta = 1.d / 86400.d
endif else begin
delta = 2.d / 24.d
endelse
example.setStart, example.getStop() - delta
return, example
endif
return, obj_new()
end
;+
; Gets a description of a dataset's variables.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param dataset {in} {type=string}
; identifies the dataset. A <a href="https://www.doi.org/">DOI</a>
; value requires IDL 8.5.1 higher.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns array of SpdfVariableDescription objects. If the dataset
; has no variables, an array of one null object is
; returned.
;-
function SpdfCdas::getVariables, $
dataview = dataview, dataset, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
if ~keyword_set(dataview) then dataview = self.defaultDataview
url = self.endpoint + '/dataviews/' + dataview + '/datasets/' + $
self->encode(dataset) + '/variables'
varDom = self->makeGetRequest(dataview, url, $
authenticator = authenticator, $
errorReporter = errorReporter)
if ~obj_valid(varDom) then return, objarr(1)
varElements = $
varDom->getElementsByTagName('VariableDescription')
if varElements->getLength() eq 0 then begin
obj_destroy, varDom
return, objarr(1)
endif
varDescriptions = objarr(varElements->getLength(), /nozero)
for i = 0, varElements->getLength() - 1 do begin
varElement = varElements->item(i)
name = $
self->getNamedElementsFirstChildValue(varElement, 'Name')
shortDescription = $
self->getNamedElementsFirstChildValue(varElement, $
'ShortDescription')
longDescription = $
self->getNamedElementsFirstChildValue(varElement, $
'LongDescription')
parent = $
self->getNamedElementsFirstChildValue(varElement, 'Parent')
children = $
self->getNamedElementsFirstChildValue(varElement, $
'Children')
varDescriptions[i] = $
obj_new('SpdfVariableDescription', name, shortDescription, $
longDescription, parent = parent, children = children)
endfor
obj_destroy, varDom
return, varDescriptions
end
;+
; Gets the names of a dataset's variables. This method is like the
; getVariables method except that it only return the variable names and
; not the other metadata in a SpdfVariableDescription object.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param dataset {in} {type=string}
; identifies the dataset.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns array of strings containing the dataset's variable names. If
; the dataset has no variables, !null is returned.
;-
function SpdfCdas::getVariableNames, $
dataview = dataview, dataset, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
descriptions = self->getVariables(dataview=dataview, dataset, $
authenticator=authenticator, $
httpErrorReporter=httpErrorReporter)
if ~obj_valid(descriptions[0]) then begin
return, !null
endif
names = strarr(n_elements(descriptions))
for i = 0, n_elements(descriptions) - 1 do begin
names[i] = descriptions[i]->getName()
endfor
obj_destroy, descriptions
return, names
end
;+
; Gets <a href="https://cdf.gsfc.nasa.gov/">Common Data Format</a>
; data from the specified dataset.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param timeIntervals {in} {type=objarr of SpdfTimeIntervals}
; time intervals of data to get.
; @param dataset {in} {type=string}
; identifies the dataset from which data is being
; requested.
; @param variables {in} {type=strarr}
; names of variable's whose data is being requested.
; If the first (only) name is "ALL-VARIABLES", then the
; resulting CDF will contain all variables.
; @keyword cdfVersion {in} {optional} {type=int}
; is the CDF file version that any created CDF files
; should be (2 or 3).
; @keyword cdfFormat {in} {optional} {type=string}
; CDF format of returned data. Valid values are:
; Binary, CDFML, GzipCDFML, ZipCDFML.
; @keyword binData {in} {optional} {type=SpdfBinData}
; data binning parameters to apply to the result file.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns SpdfCdasDataResult object.
;-
function SpdfCdas::getCdfData, $
dataview = dataview, $
timeIntervals, $
dataset, $
variables, $
cdfVersion = cdfVersion, $
cdfFormat = cdfFormat, $
binData = binData, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
if ~keyword_set(dataview) then dataview = self.defaultDataview
sizeTimeIntervals = size(timeIntervals)
if sizeTimeIntervals[0] eq 0 then begin
timeIntervals = [timeIntervals]
endif
datasetRequest = $
obj_new('SpdfDatasetRequest', dataset, variables)
cdfRequest = $
obj_new('SpdfCdfRequest', timeIntervals, datasetRequest, $
cdfVersion = cdfVersion, cdfFormat = cdfFormat, $
binData = binData)
dataRequest = $
obj_new('SpdfCdasDataRequest', cdfRequest)
result = self->getData(dataview = dataview, dataRequest, $
authenticator = authenticator, $
httpErrorReporter = errorReporter)
obj_destroy, datasetRequest
obj_destroy, cdfRequest
obj_destroy, dataRequest
return, result
end
;+
; Gets a textual representation of data from the specified dataset.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param timeInterval {in} {type=SpdfTimeInterval}
; time range of data to get.
; @param dataset {in} {type=string}
; identifies the dataset from which data is being
; requested.
; @param variables {in} {type=strarr}
; names of variable's whose data is being requested.
; If no names are specified, the data of all variables
; is returned.
; @keyword compression {in} {optional} {type=int}
; the type of compression to use on the result file.
; Valid values are: Uncompressed, Gzip, Bzip2, Zip.
; @keyword format {in} {optional} {type=string} {default='Plain'}
; format of result file. Valid values are: Plain, CSV.
; @keyword binData {in} {optional} {type=SpdfBinData}
; data binning parameters to apply to the result file.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns SpdfCdasDataResult object.
;-
function SpdfCdas::getTextData, $
dataview = dataview, timeInterval, dataset, variables, $
compression = compression, $
format = format, $
binData = binData, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
if ~keyword_set(dataview) then dataview = self.defaultDataview
datasetRequest = $
obj_new('SpdfDatasetRequest', dataset, variables)
textRequest = $
obj_new('SpdfTextRequest', timeInterval, datasetRequest, $
compression = compression, format = format, binData = binData)
dataRequest = $
obj_new('SpdfCdasDataRequest', textRequest)
result = self->getData(dataview = dataview, dataRequest, $
authenticator = authenticator, $
httpErrorReporter = errorReporter)
obj_destroy, datasetRequest
obj_destroy, textRequest
obj_destroy, dataRequest
return, result
end
;+
; Gets a graphical representation of data from the specified dataset.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param timeInterval {in} {type=SpdfTimeInterval}
; time range of data to get.
; @param datasetRequests {in} {type=objarr of SpdfDatasetRequest}
; identifies the datasets and variables from which data
; is being requested.
; @keyword graphOptions {in} {optional} {type=int}
; graphing options. Valid values are:
; CoarseNoiseFilter, DoubleHeightYAxis, CombineGraphs.
; @keyword imageFormat {in} {optional} {type=strarr}
; Format options for graph. Valid values are:
; GIF, PNG, PS, PDF.
; @keyword binData {in} {optional} {type=SpdfBinData}
; data binning parameters to apply to the result file.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns SpdfCdasDataResult object.
;-
function SpdfCdas::getGraphData, $
dataview = dataview, timeInterval, datasetRequests, $
graphOptions = graphOptions, imageFormat = imageFormat, $
binData = binData, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
if ~keyword_set(dataview) then dataview = self.defaultDataview
graphRequest = $
obj_new('SpdfGraphRequest', $
timeInterval, datasetRequests, $
graphOptions = graphOptions, $
imageFormats = imageFormats, $
binData = binData)
dataRequest = $
obj_new('SpdfCdasDataRequest', graphRequest)
result = self->getData(dataview = dataview, dataRequest, $
authenticator = authenticator, $
httpErrorReporter = errorReporter)
obj_destroy, graphRequest
obj_destroy, dataRequest
return, result
end
;+
; Gets data from the specified dataset and returns it in a
; <a href="https://spdf.gsfc.nasa.gov/CDAWlib.html">CDAWlib</a>
; structure.
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param timeIntervals {in} {type=objarr of SpdfTimeIntervals}
; time intervals of data to get.
; @param dataset {in} {type=string}
; identifies the dataset from which data is being
; requested.
; @param variables {in} {type=strarr}
; names of variable's whose data is being requested.
; If the first (only) name is "ALL-VARIABLES", then the
; resulting CDF will contain all variables.
; @keyword binData {in} {optional} {type=SpdfBinData}
; data binning parameters to apply to the result file.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @keyword keepfiles {in} {optional} {type=boolean} {default=false}
; The KEEPFILES keyword causes downloaded data files to be
; retained. Normally these files are deleted
; after the data is read into the IDL environment.
; @keyword quiet {in} {optional} {type=boolean} {default=false}
; Suppresses error messages.
; @keyword verbose {in} {optional} {type=boolean} {default=false}
; The VERBOSE keyword causes additional status and debugging
; information to be printed.
; @keyword callback_function {in} {optional} {type=string}
; this keyword value is the name of the IDL function that
; is to be called during this retrieval operation. The
; callbacks provide feedback to the user about the ongoing
; operation, as well as provide a method to cancel an
; ongoing operation. If this keyword is not set, no
; callback to the caller is made. For information on
; creating a callback function, see "Using Callbacks with
; the IDLnetURL Object" in the IDL documentation.
; @keyword callback_data {in} {optional} {type=reference}
; this keyword value contains data that is passed to the
; caller when a callback is made. The data contained in
; this variable is defined and set by the caller. The
; variable is passed, unmodified, directly to the caller
; as a parameter in the callback function. If this keyword
; is not set, the corresponding callback parameter's value
; is undefined.
; @returns CDAWlib structure.
;-
function SpdfCdas::getCdawlibData, $
dataview = dataview, $
timeIntervals, $
dataset, $
variables, $
binData = binData, $
authenticator = authenticator, $
httpErrorReporter = errorReporter, $
keepfiles = keepfiles, $
quiet = quiet, $
verbose = verbose, $
callback_function = callback_function, $
callback_data = callback_data
compile_opt idl2
dataResults = $
self->getCdfData(dataview = dataview, timeIntervals, $
dataset, variables, binData = binData, $
authenticator = authenticator, $
httpErrorReporter = errorReporter)
if ~obj_valid(dataResults) then begin
return, obj_new()
endif
fileDescriptions = dataResults->getFileDescriptions()
if n_elements(fileDescriptions) gt 0 then begin
localCdfNames = $
self->download(fileDescriptions, keepfiles = keepfiles, $
quiet = quiet, verbose = verbose, $
callback_function = callback_function, $
callback_data = callback_data)
localCdfNames2 = localCdfNames
allVars = ''
; reads data into handles (memory) should be fastest
data = read_mycdf(allVars, localCdfNames2, all = 1, $
/nodata)
; reads data into .dat structure tags
; data = read_mycdf(variables, localCdfNames)
if n_tags(data) eq 3 && $
array_equal (tag_names(data), $
['DATASET', 'ERROR', 'STATUS']) then begin
if keyword_set(verbose) then begin
print, 'Error in read_mycdf()'
print, ' ERROR: ', data.error
print, ' STATUS: ', data.status
endif
obj_destroy, fileDescriptions
obj_destroy, dataResults
return, 1
endif
newbuf = hsave_struct(data, /nosave)
; don't use the /nosave if you want it saved
; to a save file, need to specify a file name though.
if ~keyword_set(keepfiles) then begin
file_delete, localCdfNames
endif
if keyword_set(verbose) then begin
if keyword_set(keepfiles) then begin
print, 'Data sub/superset returned in local CDF file: ', $
localCdfNames
endif
endif
endif else begin
if ~keyword_set(quiet) then begin
print, 'No data found'
endif
endelse
obj_destroy, fileDescriptions
obj_destroy, dataResults
return, newbuf ; data
end
;+
; Gets data from the specified dataset and returns it in an IDL Hash as
; defined by the
; <a href="https://spdf.gsfc.nasa.gov/pub/software/cdf/dist/latest/idl/CDF_READCDF.txt">CDF_READCDF</a>
; function. Use of this function requires CDF 3.8.1 or higher. The
; following is a brief example of what the returned hash looks like:<pre>
; {
; "CDFInfo": {
; "FileName": "test",
; "NumGlobalAttrs": 5,
; "NumVariableAttrs": 5,
; "NumVariables": 2,
; "Format": "SINGLE_FILE",
; "Majority": "ROW_MAJOR",
; "Version": "3.8.1",
; ...
; },
; "GlobalAttrs": {
; "Project": {
; 0: "LWS>Living With a Star"
; },
; "Source_name": {
; 0: "RBSP-B>Radiation Belt Storm Probe B"
; },
; ...
; },
; "Variables": {
; "Latitude": {
; "VarInfo": {
; "DataType": "CDF_UINT1",
; "NumElems": 1,
; "RecVary": "VARY",
; "NumDims": 1,
; "DimSizes": [3],
; ...
; },
; "VarAttrs": {
; "VALIDMIN": -10,
; "VALIDMAX": 90,
; ...
; }
; "VarData": [[25, 54, 5], [15, 25, 35], [10, 28, 55]]
; },
; ...
; }
; }
; </pre>
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param timeIntervals {in} {type=objarr of SpdfTimeIntervals}
; time intervals of data to get.
; @param dataset {in} {type=string}
; identifies the dataset from which data is being
; requested.
; @param variables {in} {type=strarr}
; names of variable's whose data is being requested.
; If the first (only) name is "ALL-VARIABLES", then the
; resulting CDF will contain all variables.
; @keyword binData {in} {optional} {type=SpdfBinData}
; data binning parameters to apply to the result file.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @keyword keepfiles {in} {optional} {type=boolean} {default=false}
; The KEEPFILES keyword causes downloaded data files to be
; retained. Normally these files are deleted
; after the data is read into the IDL environment.
; @keyword quiet {in} {optional} {type=boolean} {default=false}
; Suppresses error messages.
; @keyword verbose {in} {optional} {type=boolean} {default=false}
; The VERBOSE keyword causes additional status and debugging
; information to be printed.
; @keyword callback_function {in} {optional} {type=string}
; this keyword value is the name of the IDL function that
; is to be called during this retrieval operation. The
; callbacks provide feedback to the user about the ongoing
; operation, as well as provide a method to cancel an
; ongoing operation. If this keyword is not set, no
; callback to the caller is made. For information on
; creating a callback function, see "Using Callbacks with
; the IDLnetURL Object" in the IDL documentation.
; @keyword callback_data {in} {optional} {type=reference}
; this keyword value contains data that is passed to the
; caller when a callback is made. The data contained in
; this variable is defined and set by the caller. The
; variable is passed, unmodified, directly to the caller
; as a parameter in the callback function. If this keyword
; is not set, the corresponding callback parameter's value
; is undefined.
; @returns HASH containing the data.
;-
function SpdfCdas::getCdfDataHash, $
dataview = dataview, $
timeIntervals, $
dataset, $
variables, $
binData = binData, $
authenticator = authenticator, $
httpErrorReporter = errorReporter, $
keepfiles = keepfiles, $
quiet = quiet, $
verbose = verbose, $
callback_function = callback_function, $
callback_data = callback_data
compile_opt idl2
catch, errorStatus
if (errorStatus ne 0) then begin
; most likely, old CDF without CDF_READCDF
catch, /cancel
print, !error_state.msg
return, hash()
endif
dataResults = $
self->getCdfData(dataview = dataview, timeIntervals, $
dataset, variables, binData = binData, $
authenticator = authenticator, $
httpErrorReporter = errorReporter)
if ~obj_valid(dataResults) then begin
return, hash()
endif
fileDescriptions = dataResults->getFileDescriptions()
if n_elements(fileDescriptions) gt 0 then begin
if n_elements(fileDescriptions) gt 1 and $
~keyword_set(quiet) then begin
print, 'WARNING:', n_elements(fileDescriptions), $
'CDF files returned.'
print, 'All but the first will be ignored.'
endif
localCdfNames = $
self->download(fileDescriptions, keepfiles = keepfiles, $
quiet = quiet, verbose = verbose, $
callback_function = callback_function, $
callback_data = callback_data)
data = cdf_readcdf(localCdfNames[0])
if ~keyword_set(keepfiles) then begin
file_delete, localCdfNames
endif
if keyword_set(verbose) then begin
if keyword_set(keepfiles) then begin
print, 'Data sub/superset returned in local CDF file: ', $
localCdfNames
endif
endif
endif else begin
if ~keyword_set(quiet) then begin
print, 'No data found'
endif
endelse
obj_destroy, fileDescriptions
obj_destroy, dataResults
return, data
end
;+
; Downloads the specified remote files and returns their local filenames.
;
; @private
;
; @param fileDescriptions {in} {type=objarr of SpdfFileDescription}
; The remote files to download.
; @keyword keepfiles {in} {optional} {type=boolean} {default=false}
; The KEEPFILES keyword causes downloaded data files to be
; retained. Normally these files are deleted
; after the data is read into the IDL environment.
; @keyword quiet {in} {optional} {type=boolean} {default=false}
; Suppresses error messages.
; @keyword verbose {in} {optional} {type=boolean} {default=false}
; The VERBOSE keyword causes additional status and debugging
; information to be printed.
; @keyword callback_function {in} {optional} {type=string}
; this keyword value is the name of the IDL function that
; is to be called during this retrieval operation. The
; callbacks provide feedback to the user about the ongoing
; operation, as well as provide a method to cancel an
; ongoing operation. If this keyword is not set, no
; callback to the caller is made. For information on
; creating a callback function, see "Using Callbacks with
; the IDLnetURL Object" in the IDL documentation.
; @keyword callback_data {in} {optional} {type=reference}
; this keyword value contains data that is passed to the
; caller when a callback is made. The data contained in
; this variable is defined and set by the caller. The
; variable is passed, unmodified, directly to the caller
; as a parameter in the callback function. If this keyword
; is not set, the corresponding callback parameter's value
; is undefined.
; @returns HASH containing the data.
;-
function SpdfCdas::download, $
fileDescriptions, $
keepfiles = keepfiles, $
quiet = quiet, $
verbose = verbose, $
callback_function = callback_function, $
callback_data = callback_data
compile_opt idl2
localCdfNames = strarr(n_elements(fileDescriptions))
if n_elements(fileDescriptions) gt 0 then begin
cd, current=cwd
if ~file_test(cwd, /write) then begin
tmpDir = getenv('IDL_TMPDIR')
pushd, tmpDir
if ~keyword_set(quiet) then begin
print, 'Warning: The current working directory (', $
cwd, ') is not writable.'
print, 'Temporarily changing working directory to ', $
tmpDir
if keyword_set(keepfiles) then begin
print, 'Downloaded files will be saved to ', tmpDir
endif
endif
endif
for i = 0, n_elements(fileDescriptions) - 1 do begin
if obj_valid(fileDescriptions[i]) then begin
if keyword_set(callback_function) then begin
statusInfo = strarr(1)
statusInfo[0] = $
string(i + 1, n_elements(fileDescriptions), $
fileDescriptions[i]->getName(), $
format='(%"Beginning file retrieval (%d) of (%d) (%s)")')
progressInfo = lon64arr(5)
progressInfo[0] = 0
continue = call_function(callback_function, $
statusInfo, $
progressInfo, $
callback_data)
if ~continue then begin
if n_elements(tmpDir) ne 0 then popd
return, localCdfNames
endif
endif
localCdfNames[i] = $
fileDescriptions[i]->getFile($
callback_function=callback_function, $
callback_data=callback_data, $
sslVerifyPeer=self.ssl_verify_peer)
endif
endfor
endif
return, localCdfNames
end
;+
; Converts the given Julian Day value to an ISO 8601 string
; representation.
;
; @private
;
; @param value {in} {type=julDay}
; Julian day value to convert.
; @returns ISO 8601 string representation of the given value
;-
function SpdfCdas::julDay2Iso8601, $
value
compile_opt idl2
caldat, value, month, day, year, hour, minute, second
return, string(year, month, day, hour, minute, second, $
format=self.iso8601Format)
end
;+
; Make a request to CDAS for the specified data.
;
; @private
;
; @keyword dataview {in} {optional} {type=string}
; name of dataview to access.
; @param dataRequest {in} {type=SpdfCdasDataRequest}
; specifies the data to get.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword httpErrorReporter {in} {optional}
; {type=SpdfHttpErrorReporter}
; used to report an HTTP error.
; @returns SpdfCdasDataResult object.
;-
function SpdfCdas::getData, $
dataview = dataview, $
dataRequest, $
authenticator = authenticator, $
httpErrorReporter = errorReporter
compile_opt idl2
if ~keyword_set(dataview) then dataview = self.defaultDataview
url = self.endpoint + '/dataviews/' + dataview + '/datasets'
requestDoc = obj_new('IDLffXMLDOMDocument')
requestDom = $
requestDoc->appendChild( $
dataRequest->createDomElement(requestDoc))
requestDoc->save, string=xmlRequest
obj_destroy, requestDoc
dataDoc = self->makePostRequest(dataview, url, xmlRequest, $
authenticator = authenticator, $
errorReporter = errorReporter)
if ~obj_valid(dataDoc) then return, obj_new()
dataResult = self->getDataResult(dataDoc)
obj_destroy, dataDoc
return, dataResult
end
;+
; Creates an SpdfCdasDataResult object from the given cdas:DataResult
; XML document.
;
; @private
;
; @param doc {in} {type=IDLffXMLDOMDocument}
; cdas:DataResult XML document.
; @returns SpdfCdasDataResult object.
;-
function SpdfCdas::getDataResult, $
doc
compile_opt idl2
fileDescriptions = self->getFileDescriptions(doc)
messages = self->getDataResultText(doc, 'Message')
warnings = self->getDataResultText(doc, 'Warning')
statuses = self->getDataResultText(doc, 'Status')
errors = self->getDataResultText(doc, 'Error')
dataResult = obj_new('SpdfCdasDataResult', $
fileDescriptions, $
messages = messages, warnings = warnings, $
statuses = statuses, errors = errors)
return, dataResult
end
;+
; Creates SpdfFileDescription object(s) from the FileDescription
; elements in the given cdas:DataResult XML document.
;
; @private
;
; @param doc {in} {type=IDLffXMLDOMDocument}
; cdas:DataResult XML document.
; @returns objarr of SpdfFileDescription objects.
;-
function SpdfCdas::getFileDescriptions, $
doc
compile_opt idl2
fileDescriptions = obj_new()
fileElements = doc->getElementsByTagName('FileDescription')
if fileElements->getLength() gt 0 then begin
fileDescriptions = objarr(fileElements->getLength(), /nozero)
for i = 0, fileElements->getLength() - 1 do begin
fileElement = fileElements->item(i)
name = $
self->getNamedElementsFirstChildValue(fileElement, $
'Name')
mimeType = $
self->getNamedElementsFirstChildValue(fileElement, $
'MimeType')
startTime = $
self->getJulDate((fileElement->$
getElementsByTagName('StartTime'))->item(0))
endTime = $
self->getJulDate((fileElement->$
getElementsByTagName('EndTime'))->item(0))
timeInterval = $
obj_new('SpdfTimeInterval', startTime, endTime)
length = $
self->getNamedElementsFirstChildValue(fileElement, $
'Length')
lastModified = $
self->getNamedElementsFirstChildValue(fileElement, $
'LastModified')
thumbnailDescription = $
self->getThumbnailDescription(fileElement)
thumbnailId = self->getThumbnailId(fileElement)
fileDescriptions[i] = obj_new('SpdfFileDescription', $
name, mimeType, timeInterval, length, lastModified, $
thumbnailDescription = thumbnailDescription, $
thumbnailId = thumbnailId)
obj_destroy, timeInterval
obj_destroy, thumbnailDescription
endfor
end
return, fileDescriptions
end
;+
; Creates an SpdfThumbnailDescription object from the FileDescription
; element in the given cdas:DataResult XML document.
;
; @private
;
; @param fileElement {in} {type=IDLffXMLDOMDocument}
; cdas:FileDescription element from a cdas:DataResult XML
; document.
; @returns SpdfThumbnailDescription object.
;-
function SpdfCdas::getThumbnailDescription, $
fileElement
compile_opt idl2
thumbnailDescription = obj_new()
thumbnailDescriptionElements = $
fileElement->getElementsByTagName('ThumbnailDescription')
if thumbnailDescriptionElements->getLength() gt 0 then begin
thumbnailDescriptionElement = $
thumbnailDescriptionElements->item(0)
type = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'Type')
name = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'Name')
dataset = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'Dataset')
timeInterval = self->getTimeIntervalChild(thumbnailElement)
varName = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'VarName')
options = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'Options')
numFrames = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'NumFrames')
numRows = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'NumRows')
numCols = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'NumCols')
titleHeight = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'TitleHeight')
thumbnailHeight = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'ThumbnailHeight')
thumbnailWidth = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'ThumbnailWidth')
startRecord = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'StartRecord')
myScale = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'MyScale')
xyStep = $
self->getNamedElementsFirstChildValue(thumbnailElement, $
'XyStep')
thumbnailDescription = obj_new('SpdfThumbnailDescription', $
type, name, dataset, timeInterval, varName, options, $
numFrames, numRows, numCols, titleHeight, thumbnailHeight, $
thumbnailWidth, startRecord, myScale, xyStep)
obj_destroy, timeInterval
end
return, thumbnailDescription
end
;+
; Creates a SpdfTimeInterval object from a child TimeInterval element
; of the given node from a cdas:DataResult XML document.
;
; @private
;
; @param domNode {in} {type=IDLffXMLDOMNode}
; node from a cdas:DataResult XML document.
; @returns a reference to a SpdfTimeInterval object.
;-
function SpdfCdas::getTimeIntervalChild, $
domNode
compile_opt idl2
timeInterval = obj_new()
timeIntervalElements = domNode->getElementsByTagName('TimeInterval')
if timeIntervalElements->getLength() gt 0 then begin
timeInterval = $
self->getTimeInterval(timeIntervalElements->item(0))
end
return, timeInterval
end
;+
; Creates a SpdfTimeInterval object from the given TimeInterval element
; from a cdas:DataResult XML document.
;
; @private
;
; @param timeIntervalElement {in} {type=IDLffXMLDOMNode}
; element from a cdas:DataResult XML document.
; @returns a reference to a SpdfTimeInterval object.
;-
function SpdfCdas::getTimeInterval, $
timeIntervalElement
compile_opt idl2
startDate = $
self->getJulDate((timeIntervalElement->$
getElementsByTagName('Start'))->item(0))
endDate = $
self->getJulDate((timeIntervalElement->$
getElementsByTagName('End'))->item(0))
return, obj_new('SpdfTimeInterval', startDate, endDate)
end
;+
; Creates a julday object from the given time element from a
; cdas:DataResult XML document.
;
; @private
;
; @param dateTimeElement {in} {type=IDLffXMLDOMNodeList}
; list whose first child is to be converted into a julday
; @returns julday representation of first child of given
; dateTimeElement.
;-
function SpdfCdas::getJulDate, $
dateTimeElement
compile_opt idl2
dateFormat='(I4, 1X, I2, 1X, I2, 1X, I2, 1X, I2, 1X, I2)'
dateTimeStr = (dateTimeElement->getFirstChild())->getNodeValue()
reads, dateTimeStr, format=dateFormat, $
year, month, day, hour, minute, second
return, julday(month, day, year, hour, minute, second)
end
;+
; Gets the ThumbnailId value from the given cdas:FileDescription
; element.
;
; @private
;
; @param fileElement {in} {type=IDLffXMLDOMNode}
; cdas:FileDescription node.
; @returns ThumbnailId string value.
;-
function SpdfCdas::getThumbnailId, $
fileElement
compile_opt idl2
return, self->getNamedElementsFirstChildValue(fileElement, $
'ThumbnailId')
end
;+
; Gets the specified node values from the given cdas:DataResult XML
; document.
;
; @private
;
; @param resultDoc {in} {type=IDLffXMLDOMDocument}
; cdas:DataResult XML document.
; @param type {in} {type=string}
; name of elements whose value is to be gotten. This is
; expected to be one of Message, Warning, Status, or Error.
; @returns strarr of the specified node values from the given document.
;-
function SpdfCdas::getDataResultText, $
resultDoc, type
return, self->getNamedElementsFirstChildValue(resultDoc, type)
end
;+
; Perform an HTTP GET request to the given URL. This method provides
; functionality similar to doing
; obj_new('IDLffXMLDOMDocument', filename=url)
; except that this method will catch an authorization error (401),
; call the supplied authenticator function, and then retry the
; request with the authentication credentials obtained from the
; call to the callers authentication function.
;
; @private
;
; @param dataview {in} {type=string}
; name of dataview to access.
; @param url {in} {type=string}
; URL of GET request to make.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword errorReporter {in} {optional} {type=string}
; name of IDL procedure to call if an HTTP error occurs.
; @returns reference to IDLffXMLDOMDocument representation of HTTP
; response entity.
;-
function SpdfCdas::makeGetRequest, $
dataview, url, $
authenticator = authenticator, $
errorReporter = errorReporter
compile_opt idl2
username = ''
password = ''
retries = 0
catch, errorStatus
if (errorStatus ne 0) then begin
catch, /cancel
reply = $
self->handleHttpError( $
requestUrl, dataview, username, password, $
authenticator = authenticator, $
errorReporter = errorReporter)
obj_destroy, requestUrl
if reply eq 0 || retries gt self.retryLimit then return, obj_new()
retries = retries + 1
endif
requestUrl = self->getRequestUrl(url, username, password)
; The following stopped working so don't bother (for now).
; contentType = self->getResponseHeader(requestUrl, 'Content-Type')
;
; if (strlowcase(contentType) ne 'application/xml') then begin
;
; ; Probably an html error page
;
; obj_destroy, requestUrl
;
; return, obj_new()
; endif
result = string(requestUrl->get(/buffer))
obj_destroy, requestUrl
return, obj_new('IDLffXMLDOMDocument', string=result)
end
;+
; Perform an HTTP POST request to the given URL. If an authorization
; error (401) occurs, the supplied authenticator function is called,
; and then the request is retried with the authentication credentials
; obtained from the call to the callers authentication function.
;
; @private
;
; @param dataview {in} {type=string}
; name of dataview to access.
; @param url {in} {type=string}
; URL of GET request to make.
; @param xmlRequest {in} {type=string}
; XML entity body to be include in the request.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword errorReporter {in} {optional} {type=string}
; name of IDL procedure to call if an HTTP error occurs.
; @returns reference to IDLffXMLDOMDocument representation of HTTP
; response entity.
;-
function SpdfCdas::makePostRequest, $
dataview, url, xmlRequest, $
authenticator = authenticator, $
errorReporter = errorReporter
compile_opt idl2
username = ''
password = ''
retries = 0
catch, errorStatus
if (errorStatus ne 0) then begin
catch, /cancel
reply = $
self->handleHttpError( $
requestUrl, dataview, username, password, $
authenticator = authenticator, $
errorReporter = errorReporter)
obj_destroy, requestUrl
if reply eq 0 || retries gt self.retryLimit then return, obj_new()
retries = retries + 1
endif
requestUrl = self->getRequestUrl(url, username, password)
requestUrl->setProperty, header='Content-Type: application/xml'
; print, 'POSTing ', xmlRequest
; print, 'POSTing to ', url
; requestUrl.GetProperty, ssl_verify_peer=sslVerifyPeer
; print, 'ssl_verify_peer =', sslVerifyPeer
; requestUrl.SetProperty, ssl_verify_host=0
; requestUrl.GetProperty, ssl_verify_host=sslVerifyHost
; print, 'ssl_verify_host =', sslVerifyHost
result = requestUrl->put(xmlRequest, /buffer, /post, url=url)
obj_destroy, requestUrl
return, obj_new('IDLffXMLDOMDocument', filename=result)
end
;+
; Function to get an HTTP response header.
;
; @private
;
; @param request {in} {type=IDLnetURL}
; HTTP request.
; @param header {in} {type=string}
; name of response header to get.
; @returns the requested response header value or '' if it does not
; exist.
;-
function SpdfCdas::getResponseHeader, $
request, header
compile_opt idl2
request->getProperty, response_header=responseHeader
headers = strsplit(responseHeader, string(10B), /extract)
for i = 0, n_elements(headers) - 1 do begin
value = strsplit(headers[i], ':', /extract)
if (strlowcase(value[0]) eq strlowcase(header)) then return, value[1]
endfor
return, ''
end
;+
; Function to handle HTTP request errors. If an authorization error
; (401) has occurred and an authenticator is provided, the given
; authenticator is called to obtain authentication credentials.
; If 429 or 503 with a Retry-After header has occurred, execution is
; suspended for the Retry-After value.
; For any other error, if an errorReporter has been provided, it is
; called.
;
; @private
;
; @param request {in} {type=IDLnetURL}
; HTTP request that caused the error.
; @param dataview {in} {type=string}
; name of dataview to access.
; @param username {out} {type=string}
; username value obtained by calling the given authenticator.
; @param password {out} {type=string}
; password value obtained by calling the given authenticator.
; @keyword authenticator {in} {optional} {type=SpdfAuthenticator}
; authenticator that is used when a dataview requiring
; authentication is specified.
; @keyword errorReporter {in} {optional} {type=string}
; name of IDL procedure to call if an HTTP error occurs.
; @returns a value of 1 corrective action has occurred and a value of
; 0 if not. The corrective action for a 401 is setting the username
; and password. Corrective action for a 429/503 is waiting the
; specified time.
;-
function SpdfCdas::handleHttpError, $
request, dataview, username, password, $
authenticator = authenticator, $
errorReporter = errorReporter
compile_opt idl2
request->getProperty, $
response_code=responseCode, $
response_header=responseHeader, $
response_filename=responseFilename
if responseCode eq 401 && $
keyword_set(authenticator) then begin
reply = $
call_method('getCredentials', authenticator, $
dataview, username, password)
if reply eq 0 then begin
return, 0
endif
endif else if responseCode eq 429 || responseCode eq 503 then begin
retryAfter = stregex(responseHeader, $
'Retry-After: ([0-9]+)' + string(13b), $
/extract, /subexpr)
if n_elements(retryAfter) eq 2 && $
strlen(retryAfter[1]) gt 0 then begin
wait, fix(retryAfter[1])
endif
endif else begin
if keyword_set(errorReporter) then begin
call_method, 'reportError', errorReporter, $
responseCode, responseHeader, responseFilename
endif
return, 0
endelse
return, 1
end
;+
; Create an IDLnetURL object from the given URL with any supplied
; authentication values set.
;
; @private
;
; @param url {in} {type=string}
; URL.
; @param username {in} {type=string}
; username.
; @param password {in} {type=string}
; password.
; @returns reference to a IDLnetURL with any supplied authentication
; values set.
;-
function SpdfCdas::getRequestUrl, $
url, username, password
compile_opt idl2
requestUrl = $
obj_new('IDLnetURL', $
proxy_authentication = $
self.proxySettings.getAuthentication(), $
proxy_hostname = self.proxySettings.getHostname(), $
proxy_port = self.proxySettings.getPort(), $
proxy_username = self.proxySettings.getUsername(), $
proxy_password = self.proxySettings.getPassword(), $
ssl_verify_peer = self.ssl_verify_peer)
urlComponents = parse_url(url)
requestUrl->setProperty, $
header=self.userAgent, $
url_scheme=urlComponents.scheme, $
url_host=urlComponents.host, $
url_port=urlComponents.port, $
url_path=urlComponents.path, $
url_query=urlComponents.query
if username ne '' then begin
requestUrl->setProperty, $
authentication=3, $
url_username=username, $
url_password=password
endif
return, requestUrl
end
;+
; Defines the SpdfCdas class.
;
; @field endpoint URL of CDAS web service.
; @field defaultDataview CDAS dataview to access when the dataview is
; not specified.
; @field userAgent HTTP
; <a href="https://tools.ietf.org/html/rfc2616#section-14.43">
; user-agent value</a> to use in communications with CDAS.
; @field version identifies the version of this class.
; @field currentVersionUrl URL to the file identifying the most up to
; date version of this class.
; @field proxySettings HTTP proxy settings to use.
; @field ssl_verify_peer IDLnetURL SSL_VERIFY_PEER property value.
; @field retryLimit retry limit for requests that fail with an http
; status of 429 or 503 with a Retry-After header.
;-
pro SpdfCdas__define
compile_opt idl2
struct = { SpdfCdas, $
endpoint:'', $
userAgent:'', $
defaultDataview:'', $
version:'', $
currentVersionUrl:'', $
proxySettings:obj_new(), $
ssl_verify_peer:1, $
retryLimit:100 $
}
end