Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/wcs/gmlcoverage.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Generic support for GML Coverage descriptions.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2006, Frank Warmerdam <warmerdam@pobox.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "gdal_priv.h"
15
16
#include <cstdlib>
17
#include <cstring>
18
19
#include "cpl_conv.h"
20
#include "cpl_error.h"
21
#include "cpl_minixml.h"
22
#include "cpl_string.h"
23
#include "ogr_api.h"
24
#include "ogr_core.h"
25
#include "ogr_geometry.h"
26
#include "ogr_spatialref.h"
27
#include "gmlcoverage.h"
28
29
/************************************************************************/
30
/*                        ParseGMLCoverageDesc()                        */
31
/************************************************************************/
32
33
CPLErr WCSParseGMLCoverage(CPLXMLNode *psXML, int *pnXSize, int *pnYSize,
34
                           GDALGeoTransform &gt, char **ppszProjection)
35
36
0
{
37
0
    CPLStripXMLNamespace(psXML, nullptr, TRUE);
38
39
    /* -------------------------------------------------------------------- */
40
    /*      Isolate RectifiedGrid.  Eventually we will need to support      */
41
    /*      other georeferencing objects.                                   */
42
    /* -------------------------------------------------------------------- */
43
0
    CPLXMLNode *psRG = CPLSearchXMLNode(psXML, "=RectifiedGrid");
44
0
    CPLXMLNode *psOriginPoint = nullptr;
45
0
    const char *pszOffset1 = nullptr;
46
0
    const char *pszOffset2 = nullptr;
47
48
0
    if (psRG != nullptr)
49
0
    {
50
0
        psOriginPoint = CPLGetXMLNode(psRG, "origin.Point");
51
0
        if (psOriginPoint == nullptr)
52
0
            psOriginPoint = CPLGetXMLNode(psRG, "origin");
53
54
0
        CPLXMLNode *psOffset1 = CPLGetXMLNode(psRG, "offsetVector");
55
0
        if (psOffset1 != nullptr)
56
0
        {
57
0
            pszOffset1 = CPLGetXMLValue(psOffset1, "", nullptr);
58
0
            pszOffset2 =
59
0
                CPLGetXMLValue(psOffset1->psNext, "=offsetVector", nullptr);
60
0
        }
61
0
    }
62
63
    /* -------------------------------------------------------------------- */
64
    /*      If we are missing any of the origin or 2 offsets then give up.  */
65
    /* -------------------------------------------------------------------- */
66
0
    if (psRG == nullptr || psOriginPoint == nullptr || pszOffset1 == nullptr ||
67
0
        pszOffset2 == nullptr)
68
0
    {
69
0
        CPLError(CE_Failure, CPLE_AppDefined,
70
0
                 "Unable to find GML RectifiedGrid, origin or offset vectors");
71
0
        return CE_Failure;
72
0
    }
73
74
    /* -------------------------------------------------------------------- */
75
    /*      Search for the GridEnvelope and derive the raster size.         */
76
    /* -------------------------------------------------------------------- */
77
0
    char **papszLow =
78
0
        CSLTokenizeString(CPLGetXMLValue(psRG, "limits.GridEnvelope.low", ""));
79
0
    char **papszHigh =
80
0
        CSLTokenizeString(CPLGetXMLValue(psRG, "limits.GridEnvelope.high", ""));
81
82
0
    if (CSLCount(papszLow) < 2 || CSLCount(papszHigh) < 2)
83
0
    {
84
0
        CPLError(CE_Failure, CPLE_AppDefined,
85
0
                 "Unable to find or parse GridEnvelope.low/high.");
86
0
        CSLDestroy(papszLow);
87
0
        CSLDestroy(papszHigh);
88
0
        return CE_Failure;
89
0
    }
90
91
0
    if (pnXSize != nullptr)
92
0
        *pnXSize = atoi(papszHigh[0]) - atoi(papszLow[0]) + 1;
93
0
    if (pnYSize != nullptr)
94
0
        *pnYSize = atoi(papszHigh[1]) - atoi(papszLow[1]) + 1;
95
96
0
    CSLDestroy(papszLow);
97
0
    CSLDestroy(papszHigh);
98
99
    /* -------------------------------------------------------------------- */
100
    /*      Extract origin location.                                        */
101
    /* -------------------------------------------------------------------- */
102
0
    OGRPoint *poOriginGeometry = nullptr;
103
0
    std::unique_ptr<OGRGeometry> poGeom;
104
0
    const char *pszSRSName = nullptr;
105
106
0
    {
107
0
        bool bOldWrap = false;
108
109
        // Old coverages (i.e. WCS) just have <pos> under <origin>, so we
110
        // may need to temporarily force <origin> to <Point>.
111
0
        if (psOriginPoint->eType == CXT_Element &&
112
0
            EQUAL(psOriginPoint->pszValue, "origin"))
113
0
        {
114
0
            strcpy(psOriginPoint->pszValue, "Point");
115
0
            bOldWrap = true;
116
0
        }
117
0
        poGeom.reset(
118
0
            OGRGeometry::FromHandle(OGR_G_CreateFromGMLTree(psOriginPoint)));
119
120
0
        if (poGeom != nullptr &&
121
0
            wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
122
0
        {
123
0
            poOriginGeometry = poGeom->toPoint();
124
0
        }
125
126
0
        if (bOldWrap)
127
0
            strcpy(psOriginPoint->pszValue, "origin");
128
129
        // SRS?
130
0
        pszSRSName = CPLGetXMLValue(psOriginPoint, "srsName", nullptr);
131
0
    }
132
133
    /* -------------------------------------------------------------------- */
134
    /*      Extract offset(s)                                               */
135
    /* -------------------------------------------------------------------- */
136
0
    bool bSuccess = false;
137
138
0
    char **papszOffset1Tokens =
139
0
        CSLTokenizeStringComplex(pszOffset1, " ,", FALSE, FALSE);
140
0
    char **papszOffset2Tokens =
141
0
        CSLTokenizeStringComplex(pszOffset2, " ,", FALSE, FALSE);
142
143
0
    if (CSLCount(papszOffset1Tokens) >= 2 &&
144
0
        CSLCount(papszOffset2Tokens) >= 2 && poOriginGeometry != nullptr)
145
0
    {
146
0
        gt.xorig = poOriginGeometry->getX();
147
0
        gt.xscale = CPLAtof(papszOffset1Tokens[0]);
148
0
        gt.xrot = CPLAtof(papszOffset1Tokens[1]);
149
0
        gt.yorig = poOriginGeometry->getY();
150
0
        gt.yrot = CPLAtof(papszOffset2Tokens[0]);
151
0
        gt.yscale = CPLAtof(papszOffset2Tokens[1]);
152
153
        // offset from center of pixel.
154
0
        gt.xorig -= gt.xscale * 0.5;
155
0
        gt.xorig -= gt.xrot * 0.5;
156
0
        gt.yorig -= gt.yrot * 0.5;
157
0
        gt.yorig -= gt.yscale * 0.5;
158
159
0
        bSuccess = true;
160
0
    }
161
162
0
    CSLDestroy(papszOffset1Tokens);
163
0
    CSLDestroy(papszOffset2Tokens);
164
165
    /* -------------------------------------------------------------------- */
166
    /*      If we have gotten a geotransform, then try to interpret the     */
167
    /*      srsName.                                                        */
168
    /* -------------------------------------------------------------------- */
169
0
    if (bSuccess && pszSRSName != nullptr &&
170
0
        (*ppszProjection == nullptr || strlen(*ppszProjection) == 0))
171
0
    {
172
0
        if (STARTS_WITH_CI(pszSRSName, "epsg:"))
173
0
        {
174
0
            OGRSpatialReference oSRS;
175
0
            if (oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE)
176
0
                oSRS.exportToWkt(ppszProjection);
177
0
        }
178
0
        else if (STARTS_WITH_CI(pszSRSName, "urn:ogc:def:crs:"))
179
0
        {
180
0
            OGRSpatialReference oSRS;
181
0
            if (oSRS.importFromURN(pszSRSName) == OGRERR_NONE)
182
0
                oSRS.exportToWkt(ppszProjection);
183
0
        }
184
0
        else
185
0
            *ppszProjection = CPLStrdup(pszSRSName);
186
0
    }
187
188
0
    if (*ppszProjection)
189
0
        CPLDebug("GDALJP2Metadata", "Got projection from GML box: %s",
190
0
                 *ppszProjection);
191
192
0
    return CE_None;
193
0
}