Skip to content
Kevin Ballantyne edited this page Mar 5, 2021 · 3 revisions

Welcome to the eodms-ogc-client-py wiki!

Table of Contents

End-to-End Order Example (using Python)

The following steps use Python to search the EODMS_Catalog CSW and WCS services to order a Radarsat-1 image from the EODMS.

For this example, the following restrictions will be place on the query:

  • A bounding box surrounding the City of Ottawa city limits with coordinates:
lower corner: -76.3556 44.9617
upper corner: -75.2466 45.5371
  • All dates before to March 29, 2013 as this was the last day Radarsat-1 transmitted data

Python Packages

The only Python package that needs to be installed prior running these scripts is the Requests package (https://realpython.com/python-requests/). It can be installed by running the command pip install requests.

GetRecords POST Request

SET VARIABLES

First step is to set the input parameters (restrictions listed above) for the GetRecords request.

Python code:

# Set specific parameters
lower_corner = '-76.3556 44.9617'
upper_corner = '-75.2466 45.5371'
end_date = '2013-03-29Z'

CREATE REQUEST XML

Next, create the XML POST GetRecords request with the above variables. The request tells the CSW to return the first 15 records based on these variables (or search criteria). NOTE: To change the number of records returned, change the maxRecords attribute in the XML below to the desired value.

GetRecords POST request example:

<?xml version="1.0" encoding="UTF-8"?>
<csw:GetRecords service='CSW' version='2.0.2'
    maxRecords='15'
    startPosition='1'
    resultType='results'
    outputFormat='application/xml'
    outputSchema='http://www.opengis.net/cat/csw/2.0.2'
    xmlns='http://www.opengis.net/cat/csw/2.0.2'
    xmlns:csw='http://www.opengis.net/cat/csw/2.0.2'
    xmlns:ogc='http://www.opengis.net/ogc'
    xmlns:ows='http://www.opengis.net/ows'
    xmlns:dc='http://purl.org/dc/elements/1.1/'
    xmlns:dct='http://purl.org/dc/terms/'
    xmlns:gml='http://www.opengis.net/gml'
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xsi:schemaLocation='http://www.opengis.net/cat/csw/2.0.2
    http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd'>
    <csw:Query typeNames='csw:Record'>
        <csw:ElementSetName typeNames='csw:Record'>full</csw:elementsetname>
        <csw:Constraint version="1.1.0">
            <ogc:Filter>
                <ogc:And>
                    <ogc:PropertyIsLessThan>
                        <ogc:PropertyName>dc:date</ogc:propertyname>
                        <ogc:Literal>2013-03-29Z</ogc:literal>
                    </ogc:propertyislessthan>
                    <ogc:PropertyIsLike escapeChar='\\' singleChar='?' 
                        wildCard='*'>
                        <ogc:PropertyName>dc:title</ogc:propertyname>
                        <ogc:Literal>*</ogc:literal>
                    </ogc:propertyislike>
                    <ogc:BBOX>
                        <ogc:PropertyName>ows:BoundingBox</ogc:propertyname>
                        <gml:Envelope>
                            <gml:lowerCorner>-76.3556 44.9617</gml:lowercorner>
                            <gml:upperCorner>-75.2466 45.5371</gml:uppercorner>
                        </gml:envelope>
                    </ogc:bbox>
                </ogc:and>
         </ogc:filter>
        </csw:constraint>
    </csw:query>
</csw:getrecords>

Python code of request:

# Submit a GetRecords to the CSW
post_xml = '''<?xml version="1.0" encoding="UTF-8"?>
<csw:GetRecords service='CSW' version='2.0.2'
    maxRecords='15'
    startPosition='1'
    resultType='results'
    outputFormat='application/xml'
    outputSchema='http://www.opengis.net/cat/csw/2.0.2'
    xmlns='http://www.opengis.net/cat/csw/2.0.2'
    xmlns:csw='http://www.opengis.net/cat/csw/2.0.2'
    xmlns:ogc='http://www.opengis.net/ogc'
    xmlns:ows='http://www.opengis.net/ows'
    xmlns:dc='http://purl.org/dc/elements/1.1/'
    xmlns:dct='http://purl.org/dc/terms/'
    xmlns:gml='http://www.opengis.net/gml'
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xsi:schemaLocation='http://www.opengis.net/cat/csw/2.0.2
    http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd'>
    <csw:Query typeNames='csw:Record'>
        <csw:ElementSetName typeNames='csw:Record'>full</csw:elementsetname>
        <csw:Constraint version="1.1.0">
            <ogc:Filter>
                <ogc:And>
                    <ogc:PropertyIsLessThan>
                        <ogc:PropertyName>dc:date</ogc:propertyname>
                        <ogc:Literal>%s</ogc:literal>
                    </ogc:propertyislessthan>
                    <ogc:PropertyIsLike escapeChar='\\' singleChar='?' 
                        wildCard='*'>
                        <ogc:PropertyName>dc:title</ogc:propertyname>
                        <ogc:Literal>*</ogc:literal>
                    </ogc:propertyislike>
                    <ogc:BBOX>
                        <ogc:PropertyName>ows:BoundingBox</ogc:propertyname>
                        <gml:Envelope>
                            <gml:lowerCorner>%s</gml:lowercorner>
                            <gml:upperCorner>%s</gml:uppercorner>
                        </gml:envelope>
                    </ogc:bbox>
                </ogc:and>
         </ogc:filter>
        </csw:constraint>
    </csw:query>
</csw:getrecords>''' % (end_date, lower_corner, upper_corner)

SEND THE REQUEST

The next step is to send the request to the WCS URL. In Python, use the requests object to send a POST request:

csw_url = 'https://www.eodms-sgdot.nrcan-rncan.gc.ca/MetaManagerCSW' \
            '/csw/eodms_catalog'
headers = {'Content-Type':'application/xml'}
csw_r = requests.post(csw_url, data=post_xml)

The GetRecords response will contain all the records up to the maxRecords value (in this case the first 15). The following HTTP response will be returned from the CSW:

<?xml version="1.0" encoding="UTF-8"?>
<GetRecordsResponse xmlns="http://www.opengis.net/cat/csw/2.0.2">
    <SearchStatus timestamp="2019-11-22T04:11:02-05:00"/>
    <SearchResults numberOfRecordsMatched="705" 
        numberOfRecordsReturned="15" nextRecord="16">
        <csw:Record xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" 
            xmlns:ows="http://www.opengis.net/ows" 
            xmlns:dct="http://purl.org/dc/terms/" 
            xmlns:dc="http://purl.org/dc/elements/1.1/">
            <dc:identifier>1508208</dc:identifier>
            <dc:title>Radarsat-1 Raw Scenes</dc:title>
            <dc:type>dataset</dc:type>
            <dc:subject>Satellites, Imaging,Radar, Digital Collection, 
                Mapping, Spatial Data, Remote Sensing
            </dc:subject>
            <dc:subject>F2</dc:subject>
            <dc:format>GeoTIFF</dc:format>
            <dc:creator>Government of Canada; Natural Resources Canada; 
                Earth Sciences Sector; Canada Centre for Mapping and 
                Earth Observation
            </dc:creator>
            <dc:description>The Radarsat-1 satellite has a synthetic 
                aperture radar(SAR) imaging instrument. The images are 
                used internationally to manage and monitor the Earth's 
                resources and to monitor global climate change, as well 
                as in many other commercial and scientific applications. 
                RADARSAT-1 is ideally suited to supporting these tasks 
                because of its wide range of beams, SAR technology, 
                frequent revisit period, high-quality products and fast, 
                efficient delivery. Each of Radarsat-1's seven beam 
                modes offer a different image resolution. The modes 
                include Fine, which covers an area of 50 km × 50 km 
                (31 mi × 31 mi) (2,500 km2 (970 sq mi)) with a resolution 
                of 10 metres (33 ft); Standard, which covers an area of 
                100 km × 100 km (62 mi × 62 mi) (10,000 km2 (3,900 sq mi)) 
                and has a resolution of 30 metres (98 ft); and ScanSAR 
                wide, which covers a 500 km × 500 km (310 mi × 310 mi) 
                (250,000 km2 (97,000 sq mi)) area with a resolution of 
                100 metres (330 ft). Radarsat-1 also has the unique 
                ability to direct its beam at different angles.
            </dc:description>
            <dct:abstract>The Radarsat-1 satellite has a synthetic 
                aperture radar(SAR) imaging instrument. The images are 
                used internationally to manage and monitor the Earth's 
                resources and to monitor global climate change, as well 
                as in many other commercial and scientific applications. 
                RADARSAT-1 is ideally suited to supporting these tasks 
                because of its wide range of beams, SAR technology, 
                frequent revisit period, high-quality products and fast, 
                efficient delivery. Each of Radarsat-1's seven beam 
                modes offer a different image resolution. The modes 
                include Fine, which covers an area of 50 km × 50 km 
                (31 mi × 31 mi) (2,500 km2 (970 sq mi)) with a resolution 
                of 10 metres (33 ft); Standard, which covers an area of 
                100 km × 100 km (62 mi × 62 mi) (10,000 km2 (3,900 sq mi)) 
                and has a resolution of 30 metres (98 ft); and ScanSAR 
                wide, which covers a 500 km × 500 km (310 mi × 310 mi) 
                (250,000 km2 (97,000 sq mi)) area with a resolution of 
                100 metres (330 ft). Radarsat-1 also has the unique 
                ability to direct its beam at different angles.
            </dct:abstract>
            <dc:publisher>NRCan/CCMEO/EODMS section head</dc:publisher>
            <dc:date>2013-02-21</dc:date>
            <dc:language>eng; CAN</dc:language>
            <dct:references scheme="OVERVIEW">
                https://was-eodms.compusult.net/wes/getObject?
                FeatureID=SERVICE-RSAT1_001-000000000000000000-1508208
                &amp;ObjectType=Thumbview&amp;collectionId=Radarsat1
            </dct:references>
            <dct:references scheme="THUMBVIEW">
                https://was-eodms.compusult.net/wes/getObject?
                FeatureID=SERVICE-RSAT1_001-000000000000000000-1508208
                &amp;ObjectType=Thumbview&amp;collectionId=Radarsat1
            </dct:references>
            <dct:references scheme="transferOptions">
                http://gs.mdacorporation.com/SatelliteData/
                Radarsat1/Radarsat1.aspx
            </dct:references>
            <dct:references scheme="transferOptions">
                http://ceocat.ccrs.nrcan.gc.ca
            </dct:references>
            <dc:source>
                https://www.eodms-sgdot.nrcan-rncan.gc.ca:80/MetaManagerCSW
                /csw/eodms_catalog?service=CSW&amp;request=GetRecordById
                &amp;version=2.0.2&amp;ElementSetName=full&amp;outputSchema=http://
                www.isotc211.org/2005/gmd&amp;Id=1508208&amp;outputFormat=text/html
            </dc:source>
            <ows:BoundingBox>
                <ows:LowerCorner>-76.275153 45.191208</ows:lowercorner>
                <ows:UpperCorner>-75.444661 45.739692</ows:uppercorner>
            </ows:boundingbox>
        </csw:record>
        <csw:Record xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" 
            xmlns:ows="http://www.opengis.net/ows" 
            xmlns:dct="http://purl.org/dc/terms/" 
            xmlns:dc="http://purl.org/dc/elements/1.1/">
            .
            .
            .
        </csw:record>
        .
        .
        .
    </searchresults>
</getrecordsresponse>

EXTRACT RECORD ID AND COLLECTION ID FROM RESPONSE

Using the GetRecords HTTP response, the record ID and the collection ID can be extracted. The record ID is taken from <dc:identifier> in the response. The collection ID can be extracted by parsing the URL in the <dct:references>. The next step in the script is to convert the XML response into an ElementTree and get the XML element of the first record (or remove the break and add each rec_element to a list to go through each record in the response):

record_tag = '{http://www.opengis.net/cat/csw/2.0.2}Record'
for child in root.iter('*'):
    if child.find(record_tag):
        rec_element = child.find(record_tag)
        break

Using the record element, locate the <dc:identifier> element and get its text:

# Get the ID of the first record
id_tag = '{http://purl.org/dc/elements/1.1/}identifier'
id_el = rec_element.find(id_tag)
rec_id = id_el.text

Next, locate the <dct:references> in the record element and parse the URL from it:

url_parse = urlparse(ref_url)
url_query = url_parse.query
query_items = {u.split('=')[0]:u.split('=')[1] for u in url_query.split('&')}
collection_id = query_items['collectionId']

DescribeCoverage Request

The DescribeCoverage feature can be sent as a POST or GET request.

The URL for the DescribeCoverage GET request is: https://www.eodms-sgdot.nrcan-rncan.gc.ca/wes/services/WESOrder/wcs?SERVICE=WCS&version=2.0.1&REQUEST=DescribeCoverage&coverageId=1508208&CollectionId=Radarsat1

The POST request would be:

<?xml version="1.0" encoding="UTF-8"?>
<wcs:DescribeCoverage service="WCS" version="2.0.1"
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xsi:schemaLocation="http://www.opengis.net/wcs/2.0
    http://schemas.opengis.net/wcs/2.0/wcsAll.xsd"
    xmlns="http://www.opengis.net/wcs/2.0"
    xmlns:wcs="http://www.opengis.net/wcs/2.0">
    <wcs:CoverageId>Radarsat1--5117806</wcs:coverageid>
</wcs:describecoverage>

For the Python scripts, the GET URL will be used. The code for the DescribeCoverage GET URL is:

# Submit a DescribeCoverage GET request to the WCS using the record and collection IDs
wcs_url = 'https://www.eodms-sgdot.nrcan-rncan.gc.ca/wes/services/WESOrder/' \
    'wcs?SERVICE=WCS&version=2.0.1&REQUEST=DescribeCoverage' \
    '&coverageId=%s&CollectionId=%s' % (rec_id, collection_id)

Using the WCS service requires a user account. To send an authenticated request in Python, a session containing the username and password has to be created first. The following code creates a session with username and password and then sends the GET request through the session:

session = requests.Session()
session.auth = (username, password)
wcs_desccov = session.get(url=wcs_url)

Next, the DescribeCoverage will send back the following response if the request was successful:

<wcs:CoverageDescriptions xmlns:wcs="http://www.opengis.net/wcs/2.0">
    <wcs:CoverageDescription xmlns:gml="http://www.opengis.net/gml/3.2" 
        gml:id="Cd5a023dc-0003-4ddf-b5cc-5d2e533eac05">
        <wcs:CoverageId>Radarsat1--1508208</wcs:coverageid>
        <metadata xmlns="http://www.opengis.net/gmlcov/1.0" 
            xmlns:xlink="http://www.w3.org/1999/xlink" 
            xlink:href="https://www.eodms-sgdot.nrcan-rncan.gc.ca/wes
            /services/WESSearch/csw/Radarsat1?request=GetRecordById
            &Id=1508208&version=2.0.2&outputSchema=http://schema.compusult.net/
            services/2.2.0/WESSearch/csw&service=CSW" 
            xlink:title="CSW GetRecordById" 
            xlink:type="simple"/>
        <gml:domainSet>
            <gml:Polygon xmlns:gml="http://www.opengis.net/gml/3.2" 
            gml:id="C49b46f31-cbe9-433b-9201-d4f0cc04ee0d" 
            srsDimension="2" srsName="EPSG:4326">
                <gml:exterior>
                    <gml:LinearRing>
                        <gml:posList>
                            -76.275153 45.652944 
                            -76.157606 45.191208 
                            -75.444661 45.278003 
                            -75.556206 45.739692 
                            -76.275153 45.652944
                        </gml:poslist>
                    </gml:linearring>
                </gml:exterior>
            </gml:polygon>
        </gml:domainset>
        <rangeType xmlns="http://www.opengis.net/gmlcov/1.0"/>
        <wcs:ServiceParameters>
            <wcs:CoverageSubtype>GridCoverage</wcs:coveragesubtype>
            <wcs:nativeFormat/>
            <wcs:Extension>
                <DestinationTypes xmlns="http://schema.compusult.net/
                    services/2.7.0/WESOrder/wcs">
                    <Type>Download</type>
                </destinationtypes>
                <Parameter xmlns="http://schema.compusult.net/services/
                    2.7.0/WESOrder">
                    <Name>packagingFormat</name>
                    <Title>Packaging Format</title>
                    <Description>Packaging Format</description>
                    <Default>ZIP</default>
                    <Choices>
                        <Value>ZIP</value>
                        <Display>Zip</display>
                        <Description>Zip</description>
                        <DependentParameters/>
                    </choices>
                    <Choices>
                        <Value>TAR</value>
                        <Display>Tar</display>
                        <Description>Tar</description>
                        <DependentParameters/>
                    </choices>
                    <Choices>
                        <Value>TARGZ</value>
                        <Display>Tar/GZ</display>
                        <Description>Tar/GZ</description>
                        <DependentParameters/>
                    </choices>
                    <Choices>
                        <Value>TAR</value>
                        <Display>Tar</display>
                        <Description>Tar</description>
                        <DependentParameters/>
                    </choices>
                    <Choices>
                        <Value>TARGZ</value>
                        <Display>Tar/GZ</display>
                        <Description>Tar/GZ</description>
                        <DependentParameters/>
                    </choices>
                </parameter>
            </wcs:extension>
        </wcs:serviceparameters>
    </wcs:coveragedescription>
</wcs:coveragedescriptions>

GetCoverage Request

If the request was successful, the status code of 200 will be returned with the request and the GetCoverage operation can be sent to the WCS. A GET request similar to the DescribeCoverage can be sent to the WCS (adding format=applicationgml+xml to the URL query).

In this case, a POST request is used. For the POST request, the CoverageId must be in the format of <collection></collection>--<record_id></record_id> so in this case the CoverageId is Radarsat1&amp;&#35;45&#59;&amp;&#35;45&#59;1508208:

wcs_getcov &amp;&#35;61&#59; session.post(url&amp;&#35;61&#59;wcs_url, data&amp;&#35;61&#59;getcov_post)

Once the request has been sent, the user will receive an “EODMS Image Request Submitted Notification” email letting them know that their request has been submitted. Shortly after receiving this email, the user should then receive another email called “EODMS Image Request Delivery Notification” with download links for the specific image.