Coverage Report

Created: 2025-08-11 09:23

/src/gdal/frmts/wms/minidriver_arcgis_server.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  Arc GIS Server Client Driver
4
 * Purpose:  Implementation of Dataset and RasterBand classes for WMS
5
 *           and other similar services.
6
 * Author:   Alexander Lisovenko
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2014-2020, NextGIS <info@nextgis.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "wmsdriver.h"
15
#include "minidriver_arcgis_server.h"
16
17
#include <algorithm>
18
19
WMSMiniDriver_AGS::WMSMiniDriver_AGS()
20
0
{
21
0
}
22
23
WMSMiniDriver_AGS::~WMSMiniDriver_AGS()
24
0
{
25
0
}
26
27
static double GetBBoxCoord(const GDALWMSImageRequestInfo &iri, char what)
28
0
{
29
0
    switch (what)
30
0
    {
31
0
        case 'x':
32
0
            return std::min(iri.m_x0, iri.m_x1);
33
0
        case 'y':
34
0
            return std::min(iri.m_y0, iri.m_y1);
35
0
        case 'X':
36
0
            return std::max(iri.m_x0, iri.m_x1);
37
0
        case 'Y':
38
0
            return std::max(iri.m_y0, iri.m_y1);
39
0
    }
40
0
    return 0.0;
41
0
}
42
43
char **WMSMiniDriver_AGS::GetMetadataDomainList(void)
44
0
{
45
0
    return CSLAddString(nullptr, "LocationInfo");
46
0
}
47
48
CPLErr WMSMiniDriver_AGS::Initialize(CPLXMLNode *config,
49
                                     CPL_UNUSED char **papszOpenOptions)
50
0
{
51
    // Bounding box, if specified, has to be xyXY.
52
0
    m_bbox_order = CPLGetXMLValue(config, "BBoxOrder", "xyXY");
53
0
    if (m_bbox_order.size() < 4 || m_bbox_order.find("xyXY") != 0)
54
0
    {
55
0
        CPLError(CE_Failure, CPLE_AppDefined,
56
0
                 "GDALWMS: ArcGIS BBoxOrder value has to be xyXY");
57
0
        return CE_Failure;
58
0
    }
59
60
0
    m_base_url = CPLGetXMLValue(config, "ServerURL",
61
0
                                CPLGetXMLValue(config, "ServerUrl", ""));
62
0
    if (m_base_url.empty())
63
0
    {
64
0
        CPLError(CE_Failure, CPLE_AppDefined,
65
0
                 "GDALWMS: ArcGIS Server mini-driver: ServerURL missing.");
66
0
        return CE_Failure;
67
0
    }
68
69
0
    m_image_format = CPLGetXMLValue(config, "ImageFormat", "png");
70
0
    m_time_range = CPLGetXMLValue(config, "TimeRange", "");
71
0
    m_transparent =
72
0
        CPLString(CPLGetXMLValue(config, "Transparent", "")).tolower();
73
0
    m_layers = CPLGetXMLValue(config, "Layers", "");
74
75
0
    const char *irs = CPLGetXMLValue(config, "SRS", "102100");
76
77
0
    if (irs != nullptr)
78
0
    {
79
0
        if (STARTS_WITH_CI(
80
0
                irs, "EPSG:"))  // If we have EPSG code just convert it to WKT.
81
0
        {
82
0
            m_oSRS = ProjToSRS(irs);
83
0
            m_irs = irs + 5;
84
0
        }
85
0
        else  // If we have AGS code - try if it's EPSG.
86
0
        {
87
0
            m_irs = irs;
88
0
            m_oSRS = ProjToSRS("EPSG:" + m_irs);
89
0
        }
90
        // TODO: If we have AGS JSON.
91
0
    }
92
0
    m_identification_tolerance =
93
0
        CPLGetXMLValue(config, "IdentificationTolerance", "2");
94
95
0
    return CE_None;
96
0
}
97
98
void WMSMiniDriver_AGS::GetCapabilities(WMSMiniDriverCapabilities *caps)
99
0
{
100
0
    caps->m_has_getinfo = 1;
101
0
}
102
103
CPLErr WMSMiniDriver_AGS::TiledImageRequest(
104
    WMSHTTPRequest &request, const GDALWMSImageRequestInfo &iri,
105
    CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri)
106
0
{
107
0
    CPLString &url = request.URL;
108
0
    url = m_base_url;
109
110
    // Assume map service if exportImage is not explicitly requested.
111
0
    if ((url.ifind("/export?") == std::string::npos) &&
112
0
        (url.ifind("/exportImage?") == std::string::npos))
113
0
    {
114
0
        url += "/export?";
115
0
    }
116
117
0
    URLPrepare(url);
118
0
    url += "f=image";
119
0
    char *pszEscapedValue = CPLEscapeString(m_layers, -1, CPLES_URL);
120
0
    url += CPLOPrintf("&bbox=%.8f%%2C%.8f%%2C%.8f%%2C%.8f",
121
0
                      GetBBoxCoord(iri, m_bbox_order[0]),
122
0
                      GetBBoxCoord(iri, m_bbox_order[1]),
123
0
                      GetBBoxCoord(iri, m_bbox_order[2]),
124
0
                      GetBBoxCoord(iri, m_bbox_order[3])) +
125
0
           CPLOPrintf("&size=%d%%2C%d", iri.m_sx, iri.m_sy) +
126
0
           CPLOPrintf("&imageSR=%s", m_irs.c_str()) +
127
0
           CPLOPrintf("&bboxSR=%s", m_irs.c_str()) +
128
0
           CPLOPrintf("&format=%s", m_image_format.c_str()) +
129
0
           CPLOPrintf("&layers=%s", pszEscapedValue);
130
0
    CPLFree(pszEscapedValue);
131
132
0
    if (!m_transparent.empty())
133
0
    {
134
0
        url += "&transparent=" + m_transparent;
135
0
    }
136
0
    else
137
0
    {
138
0
        url += "&transparent=false";
139
0
    }
140
141
0
    if (!m_time_range.empty())
142
0
    {
143
0
        pszEscapedValue = CPLEscapeString(m_time_range, -1, CPLES_URL);
144
0
        url += CPLOPrintf("&time=%s", pszEscapedValue);
145
0
        CPLFree(pszEscapedValue);
146
0
    }
147
0
    else
148
0
    {
149
0
        url += "&time=";
150
0
    }
151
152
0
    return CE_None;
153
0
}
154
155
void WMSMiniDriver_AGS::GetTiledImageInfo(
156
    CPLString &url, const GDALWMSImageRequestInfo &iri,
157
    CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri, int nXInBlock,
158
    int nYInBlock)
159
0
{
160
0
    url = m_base_url;
161
162
0
    if (m_base_url.ifind("/identify?") == std::string::npos)
163
0
    {
164
0
        url += "/identify?";
165
0
    }
166
167
0
    URLPrepare(url);
168
    // Constant part.
169
0
    url += "f=json&geometryType=esriGeometryPoint&returnGeometry=false"
170
0
           "&layerdefs=&time=&layerTimeOptions=&maxAllowableOffset=";
171
172
0
    double fX = GetBBoxCoord(iri, 'x') +
173
0
                nXInBlock * (GetBBoxCoord(iri, 'X') - GetBBoxCoord(iri, 'x')) /
174
0
                    iri.m_sx;
175
0
    double fY = GetBBoxCoord(iri, 'y') +
176
0
                (iri.m_sy - nYInBlock) *
177
0
                    (GetBBoxCoord(iri, 'Y') - GetBBoxCoord(iri, 'y')) /
178
0
                    iri.m_sy;
179
180
0
    url += "&geometry=" + std::to_string(fX) + "%2C" + std::to_string(fY) +
181
0
           "&sr=" + m_irs;
182
183
0
    CPLString layers("visible");
184
0
    if (m_layers.find("show") != std::string::npos)
185
0
    {
186
0
        layers = m_layers;
187
0
        layers.replace(layers.find("show"), 4, "all");
188
0
    }
189
190
0
    if (m_layers.find("hide") != std::string::npos ||
191
0
        m_layers.find("include") != std::string::npos ||
192
0
        m_layers.find("exclude") != std::string::npos)
193
0
    {
194
0
        layers = "top";
195
0
    }
196
197
0
    url += "&layers=" + layers;
198
0
    url += "&tolerance=" + m_identification_tolerance;
199
0
    url += CPLOPrintf("&mapExtent=%.8f%%2C%.8f%%2C%.8f%%2C%.8f",
200
0
                      GetBBoxCoord(iri, m_bbox_order[0]),
201
0
                      GetBBoxCoord(iri, m_bbox_order[1]),
202
0
                      GetBBoxCoord(iri, m_bbox_order[2]),
203
0
                      GetBBoxCoord(iri, m_bbox_order[3])) +
204
0
           CPLOPrintf("&imageDisplay=%d%%2C%d%%2C96", iri.m_sx, iri.m_sy);
205
0
}