Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: WMS Client Driver
4 : * Purpose: Implementation of Dataset and RasterBand classes for WMS
5 : * and other similar services.
6 : * Author: Adam Nowacki, nowak@xpam.de
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2007, Adam Nowacki
10 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
11 : * Copyright (c) 2017, Dmitry Baryshnikov, <polimax@mail.ru>
12 : * Copyright (c) 2017, NextGIS, <info@nextgis.com>
13 : *
14 : * SPDX-License-Identifier: MIT
15 : ****************************************************************************/
16 :
17 : #ifndef WMSDRIVER_H_INCLUDED
18 : #define WMSDRIVER_H_INCLUDED
19 :
20 : #include <algorithm>
21 : #include <cmath>
22 : #include <map>
23 : #include <set>
24 : #include <vector>
25 : #include <utility>
26 :
27 : #include "cpl_conv.h"
28 : #include "cpl_curl_priv.h"
29 : #include "cpl_http.h"
30 : #include "gdal_alg.h"
31 : #include "gdal_pam.h"
32 : #include "gdalwarper.h"
33 : #include "ogr_spatialref.h"
34 :
35 : #include "gdalhttp.h"
36 :
37 : class GDALWMSDataset;
38 : class GDALWMSRasterBand;
39 :
40 : /* -------------------------------------------------------------------- */
41 : /* Helper functions. */
42 : /* -------------------------------------------------------------------- */
43 : OGRSpatialReference ProjToSRS(const CPLString &proj);
44 :
45 : // Decode s from encoding "base64" or "XMLencoded".
46 : // If encoding is "file", s is the file name on input and file content on output
47 : // If encoding is not recognized, does nothing
48 : const char *WMSUtilDecode(CPLString &s, const char *encoding);
49 :
50 : // Ensure that the url ends in ? or &
51 : void URLPrepare(CPLString &url);
52 : // void URLAppend(CPLString *url, const char *s);
53 : // void URLAppendF(CPLString *url, const char *s, ...) CPL_PRINT_FUNC_FORMAT (2,
54 : // 3); void URLAppend(CPLString *url, const CPLString &s);
55 : CPLString BufferToVSIFile(GByte *buffer, size_t size);
56 :
57 : int StrToBool(const char *p);
58 : int URLSearchAndReplace(CPLString *base, const char *search, const char *fmt,
59 : ...) CPL_PRINT_FUNC_FORMAT(3, 4);
60 : /* Convert a.b.c.d to a * 0x1000000 + b * 0x10000 + c * 0x100 + d */
61 : int VersionStringToInt(const char *version);
62 :
63 : class GDALWMSImageRequestInfo
64 : {
65 : public:
66 : double m_x0, m_y0;
67 : double m_x1, m_y1;
68 : int m_sx, m_sy;
69 : };
70 :
71 : class GDALWMSDataWindow
72 : {
73 : public:
74 : double m_x0, m_y0;
75 : double m_x1, m_y1;
76 : int m_sx, m_sy;
77 : int m_tx, m_ty, m_tlevel;
78 :
79 : enum
80 : {
81 : BOTTOM = -1,
82 : DEFAULT = 0,
83 : TOP = 1
84 : } m_y_origin;
85 :
86 702 : GDALWMSDataWindow()
87 702 : : m_x0(-180), m_y0(90), m_x1(180), m_y1(-90), m_sx(-1), m_sy(-1),
88 702 : m_tx(0), m_ty(0), m_tlevel(-1), m_y_origin(DEFAULT)
89 : {
90 702 : }
91 : };
92 :
93 : class GDALWMSTiledImageRequestInfo
94 : {
95 : public:
96 : int m_x, m_y;
97 : int m_level;
98 : };
99 :
100 : /************************************************************************/
101 : /* Mini Driver Related */
102 : /************************************************************************/
103 :
104 : class GDALWMSRasterIOHint
105 : {
106 : public:
107 349 : GDALWMSRasterIOHint()
108 349 : : m_x0(0), m_y0(0), m_sx(0), m_sy(0), m_overview(0), m_valid(false)
109 : {
110 349 : }
111 :
112 : int m_x0;
113 : int m_y0;
114 : int m_sx;
115 : int m_sy;
116 : int m_overview;
117 : bool m_valid;
118 : };
119 :
120 : typedef enum
121 : {
122 : OVERVIEW_ROUNDED,
123 : OVERVIEW_FLOOR
124 : } GDALWMSOverviewDimComputationMethod;
125 :
126 : class WMSMiniDriverCapabilities
127 : {
128 : public:
129 : // Default capabilities, suitable in most cases
130 349 : WMSMiniDriverCapabilities()
131 349 : : m_has_getinfo(0), m_has_geotransform(1),
132 349 : m_overview_dim_computation_method(OVERVIEW_ROUNDED)
133 : {
134 349 : }
135 :
136 : int m_has_getinfo; // Does it have meaningful implementation
137 : int m_has_geotransform;
138 : GDALWMSOverviewDimComputationMethod m_overview_dim_computation_method;
139 : };
140 :
141 : /* All data returned by mini-driver as pointer should remain valid for
142 : mini-driver lifetime and should be freed by mini-driver destructor unless
143 : otherwise specified.
144 : */
145 :
146 : // Base class for minidrivers
147 : // A minidriver has to implement at least the Initialize and the
148 : // TiledImageRequest
149 : //
150 : class WMSMiniDriver
151 : {
152 : friend class GDALWMSDataset;
153 :
154 : public:
155 348 : WMSMiniDriver() : m_parent_dataset(nullptr)
156 : {
157 348 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
158 348 : }
159 :
160 348 : virtual ~WMSMiniDriver()
161 348 : {
162 348 : }
163 :
164 : public:
165 : // MiniDriver specific initialization from XML, required
166 : // Called once at the beginning of the dataset initialization
167 : virtual CPLErr Initialize(CPLXMLNode *config, char **papszOpenOptions) = 0;
168 :
169 : // Called once at the end of the dataset initialization
170 347 : virtual CPLErr EndInit()
171 : {
172 347 : return CE_None;
173 : }
174 :
175 : // Error message returned in url, required
176 : // Set error message in request.Error
177 : // If tile doesn't exist serverside, set request.range to "none"
178 : virtual CPLErr
179 : TiledImageRequest(CPL_UNUSED WMSHTTPRequest &,
180 : CPL_UNUSED const GDALWMSImageRequestInfo &iri,
181 : CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri) = 0;
182 :
183 : // change capabilities to be used by the parent
184 340 : virtual void GetCapabilities(CPL_UNUSED WMSMiniDriverCapabilities *caps)
185 : {
186 340 : }
187 :
188 : // signal by setting the m_has_getinfo in the GetCapabilities call
189 : virtual void
190 0 : GetTiledImageInfo(CPL_UNUSED CPLString &url,
191 : CPL_UNUSED const GDALWMSImageRequestInfo &iri,
192 : CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri,
193 : CPL_UNUSED int nXInBlock, CPL_UNUSED int nYInBlock)
194 : {
195 0 : }
196 :
197 187 : virtual OGRSpatialReference GetSpatialRef()
198 : {
199 187 : return m_oSRS;
200 : }
201 :
202 0 : virtual char **GetMetadataDomainList()
203 : {
204 0 : return nullptr;
205 : }
206 :
207 : protected:
208 : CPLString m_base_url;
209 : OGRSpatialReference m_oSRS;
210 : GDALWMSDataset *m_parent_dataset;
211 : };
212 :
213 : class WMSMiniDriverFactory
214 : {
215 : public:
216 99 : WMSMiniDriverFactory()
217 99 : {
218 99 : }
219 :
220 99 : virtual ~WMSMiniDriverFactory()
221 99 : {
222 99 : }
223 :
224 : public:
225 : virtual WMSMiniDriver *New() const = 0;
226 : CPLString m_name;
227 : };
228 :
229 : // Interface with the global mini driver manager
230 : WMSMiniDriver *NewWMSMiniDriver(const CPLString &name);
231 : void WMSRegisterMiniDriverFactory(WMSMiniDriverFactory *mdf);
232 : void WMSDeregisterMiniDrivers(GDALDriver *);
233 :
234 : // WARNING: Called by GDALDestructor, unsafe to use any static objects
235 : void WMSDeregister(GDALDriver *);
236 :
237 : /************************************************************************/
238 : /* GDALWMSCache */
239 : /************************************************************************/
240 : enum GDALWMSCacheItemStatus
241 : {
242 : CACHE_ITEM_NOT_FOUND,
243 : CACHE_ITEM_OK,
244 : CACHE_ITEM_EXPIRED
245 : };
246 :
247 : class GDALWMSCacheImpl
248 : {
249 : public:
250 213 : GDALWMSCacheImpl(const CPLString &soPath, CPLXMLNode * /*pConfig*/)
251 213 : : m_soPath(soPath)
252 : {
253 213 : }
254 :
255 213 : virtual ~GDALWMSCacheImpl()
256 213 : {
257 213 : }
258 :
259 : virtual CPLErr Insert(const char *pszKey, const CPLString &osFileName) = 0;
260 : virtual enum GDALWMSCacheItemStatus
261 : GetItemStatus(const char *pszKey) const = 0;
262 : virtual GDALDataset *GetDataset(const char *pszKey,
263 : char **papszOpenOptions) const = 0;
264 : virtual void Clean() = 0;
265 : virtual int GetCleanThreadRunTimeout() = 0;
266 :
267 : protected:
268 : CPLString m_soPath;
269 : };
270 :
271 213 : class GDALWMSCache
272 : {
273 : friend class GDALWMSDataset;
274 :
275 : public:
276 : GDALWMSCache();
277 : ~GDALWMSCache();
278 :
279 : public:
280 : CPLErr Initialize(const char *pszUrl, CPLXMLNode *pConfig);
281 : CPLErr Insert(const char *pszKey, const CPLString &osFileName);
282 : enum GDALWMSCacheItemStatus GetItemStatus(const char *pszKey) const;
283 : GDALDataset *GetDataset(const char *pszKey, char **papszOpenOptions) const;
284 : void Clean();
285 :
286 : protected:
287 213 : CPLString CachePath() const
288 : {
289 213 : return m_osCachePath;
290 : }
291 :
292 : protected:
293 : CPLString m_osCachePath{};
294 : bool m_bIsCleanThreadRunning = false;
295 : time_t m_nCleanThreadLastRunTime = 0;
296 :
297 : private:
298 : GDALWMSCacheImpl *m_poCache = nullptr;
299 : CPLJoinableThread *m_hThread = nullptr;
300 : };
301 :
302 : /************************************************************************/
303 : /* GDALWMSDataset */
304 : /************************************************************************/
305 :
306 : class GDALWMSDataset final : public GDALPamDataset
307 : {
308 : friend class GDALWMSRasterBand;
309 :
310 : public:
311 : GDALWMSDataset();
312 : virtual ~GDALWMSDataset();
313 :
314 : const OGRSpatialReference *GetSpatialRef() const override;
315 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
316 :
317 : virtual CPLErr GetGeoTransform(double *gt) override;
318 : virtual CPLErr SetGeoTransform(double *gt) override;
319 : virtual CPLErr AdviseRead(int x0, int y0, int sx, int sy, int bsx, int bsy,
320 : GDALDataType bdt, int band_count, int *band_map,
321 : char **options) override;
322 :
323 : virtual char **GetMetadataDomainList() override;
324 : virtual const char *GetMetadataItem(const char *pszName,
325 : const char *pszDomain = "") override;
326 :
327 0 : void SetColorTable(GDALColorTable *pct)
328 : {
329 0 : m_poColorTable = pct;
330 0 : }
331 :
332 9 : void mSetBand(int i, GDALRasterBand *band)
333 : {
334 9 : SetBand(i, band);
335 9 : }
336 :
337 72 : GDALWMSRasterBand *mGetBand(int i)
338 : {
339 72 : return reinterpret_cast<GDALWMSRasterBand *>(GetRasterBand(i));
340 : }
341 :
342 48 : const GDALWMSDataWindow *WMSGetDataWindow() const
343 : {
344 48 : return &m_data_window;
345 : }
346 :
347 3 : void WMSSetBlockSize(int x, int y)
348 : {
349 3 : m_block_size_x = x;
350 3 : m_block_size_y = y;
351 3 : }
352 :
353 3 : void WMSSetRasterSize(int x, int y)
354 : {
355 3 : nRasterXSize = x;
356 3 : nRasterYSize = y;
357 3 : }
358 :
359 3 : void WMSSetBandsCount(int count)
360 : {
361 3 : nBands = count;
362 3 : }
363 :
364 3 : void WMSSetClamp(bool flag = true)
365 : {
366 3 : m_clamp_requests = flag;
367 3 : }
368 :
369 3 : void WMSSetDataType(GDALDataType type)
370 : {
371 3 : m_data_type = type;
372 3 : }
373 :
374 3 : void WMSSetDataWindow(const GDALWMSDataWindow &window)
375 : {
376 3 : m_data_window = window;
377 3 : }
378 :
379 1 : void WMSSetDefaultBlockSize(int x, int y)
380 : {
381 1 : m_default_block_size_x = x;
382 1 : m_default_block_size_y = y;
383 1 : }
384 :
385 1 : void WMSSetDefaultDataWindowCoordinates(double x0, double y0, double x1,
386 : double y1)
387 : {
388 1 : m_default_data_window.m_x0 = x0;
389 1 : m_default_data_window.m_y0 = y0;
390 1 : m_default_data_window.m_x1 = x1;
391 1 : m_default_data_window.m_y1 = y1;
392 1 : }
393 :
394 0 : void WMSSetDefaultTileCount(int tilecountx, int tilecounty)
395 : {
396 0 : m_default_tile_count_x = tilecountx;
397 0 : m_default_tile_count_y = tilecounty;
398 0 : }
399 :
400 1 : void WMSSetDefaultTileLevel(int tlevel)
401 : {
402 1 : m_default_data_window.m_tlevel = tlevel;
403 1 : }
404 :
405 1 : void WMSSetDefaultOverviewCount(int overview_count)
406 : {
407 1 : m_default_overview_count = overview_count;
408 1 : }
409 :
410 1 : void WMSSetNeedsDataWindow(bool flag)
411 : {
412 1 : m_bNeedsDataWindow = flag;
413 1 : }
414 :
415 3 : static void list2vec(std::vector<double> &v, const char *pszList)
416 : {
417 3 : if ((pszList == nullptr) || (pszList[0] == 0))
418 0 : return;
419 3 : char **papszTokens = CSLTokenizeString2(
420 : pszList, " \t\n\r", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
421 3 : v.clear();
422 6 : for (int i = 0; i < CSLCount(papszTokens); i++)
423 3 : v.push_back(CPLStrtod(papszTokens[i], nullptr));
424 3 : CSLDestroy(papszTokens);
425 : }
426 :
427 3 : void WMSSetNoDataValue(const char *pszNoData)
428 : {
429 3 : list2vec(vNoData, pszNoData);
430 3 : }
431 :
432 0 : void WMSSetMinValue(const char *pszMin)
433 : {
434 0 : list2vec(vMin, pszMin);
435 0 : }
436 :
437 0 : void WMSSetMaxValue(const char *pszMax)
438 : {
439 0 : list2vec(vMax, pszMax);
440 0 : }
441 :
442 : // Set open options for tiles
443 : // Works like a <set>, only one entry with a give name can exist, last one
444 : // set wins If the value is null, the entry is deleted
445 : void SetTileOO(const char *pszName, const char *pszValue);
446 :
447 2 : void SetXML(const char *psz)
448 : {
449 2 : m_osXML.clear();
450 2 : if (psz)
451 2 : m_osXML = psz;
452 2 : }
453 :
454 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
455 : static GDALDataset *CreateCopy(const char *pszFilename,
456 : GDALDataset *poSrcDS, int bStrict,
457 : char **papszOptions,
458 : GDALProgressFunc pfnProgress,
459 : void *pProgressData);
460 :
461 : const char *const *GetHTTPRequestOpts();
462 :
463 : static const char *GetServerConfig(const char *URI,
464 : char **papszHTTPOptions);
465 : static void DestroyCfgMutex();
466 : static void ClearConfigCache();
467 :
468 : protected:
469 : virtual CPLErr IRasterIO(GDALRWFlag rw, int x0, int y0, int sx, int sy,
470 : void *buffer, int bsx, int bsy, GDALDataType bdt,
471 : int band_count, BANDMAP_TYPE band_map,
472 : GSpacing nPixelSpace, GSpacing nLineSpace,
473 : GSpacing nBandSpace,
474 : GDALRasterIOExtraArg *psExtraArg) override;
475 : CPLErr Initialize(CPLXMLNode *config, char **papszOpenOptions);
476 :
477 : GDALWMSDataWindow m_data_window;
478 : WMSMiniDriver *m_mini_driver;
479 : WMSMiniDriverCapabilities m_mini_driver_caps;
480 : GDALWMSCache *m_cache;
481 : OGRSpatialReference m_oSRS{};
482 : GDALColorTable *m_poColorTable;
483 : std::vector<double> vNoData;
484 : std::vector<double> vMin;
485 : std::vector<double> vMax;
486 : GDALDataType m_data_type;
487 : int m_block_size_x;
488 : int m_block_size_y;
489 : GDALWMSRasterIOHint m_hint;
490 : int m_use_advise_read;
491 : int m_verify_advise_read;
492 : int m_offline_mode;
493 : int m_http_max_conn;
494 : int m_http_timeout;
495 : char **m_http_options;
496 : // Open Option list for tiles
497 : char **m_tileOO;
498 : int m_clamp_requests;
499 : int m_unsafeSsl;
500 : std::set<int> m_http_zeroblock_codes;
501 : int m_zeroblock_on_serverexceptions;
502 : CPLString m_osUserAgent;
503 : CPLString m_osReferer;
504 : CPLString m_osUserPwd;
505 : std::string m_osAccept{}; // HTTP Accept header
506 :
507 : GDALWMSDataWindow m_default_data_window;
508 : int m_default_block_size_x;
509 : int m_default_block_size_y;
510 : int m_default_tile_count_x;
511 : int m_default_tile_count_y;
512 : int m_default_overview_count;
513 :
514 : bool m_bNeedsDataWindow;
515 :
516 : CPLString m_osXML;
517 :
518 : // Per session cache of server configurations
519 : typedef std::map<CPLString, CPLString> StringMap_t;
520 : static CPLMutex *cfgmtx;
521 : static StringMap_t cfg;
522 : };
523 :
524 : /************************************************************************/
525 : /* GDALWMSRasterBand */
526 : /************************************************************************/
527 :
528 : class GDALWMSRasterBand final : public GDALPamRasterBand
529 : {
530 : friend class GDALWMSDataset;
531 : void ComputeRequestInfo(GDALWMSImageRequestInfo &iri,
532 : GDALWMSTiledImageRequestInfo &tiri, int x, int y);
533 :
534 : CPLString osMetadataItem;
535 : CPLString osMetadataItemURL;
536 :
537 : public:
538 : GDALWMSRasterBand(GDALWMSDataset *parent_dataset, int band, double scale);
539 : virtual ~GDALWMSRasterBand();
540 : bool AddOverview(double scale);
541 : virtual double GetNoDataValue(int *) override;
542 : virtual double GetMinimum(int *) override;
543 : virtual double GetMaximum(int *) override;
544 : virtual GDALColorTable *GetColorTable() override;
545 : virtual CPLErr AdviseRead(int x0, int y0, int sx, int sy, int bsx, int bsy,
546 : GDALDataType bdt, char **options) override;
547 :
548 : virtual GDALColorInterp GetColorInterpretation() override;
549 : virtual CPLErr SetColorInterpretation(GDALColorInterp) override;
550 : virtual CPLErr IReadBlock(int x, int y, void *buffer) override;
551 : virtual CPLErr IRasterIO(GDALRWFlag rw, int x0, int y0, int sx, int sy,
552 : void *buffer, int bsx, int bsy, GDALDataType bdt,
553 : GSpacing nPixelSpace, GSpacing nLineSpace,
554 : GDALRasterIOExtraArg *psExtraArg) override;
555 : virtual int HasArbitraryOverviews() override;
556 : virtual int GetOverviewCount() override;
557 : virtual GDALRasterBand *GetOverview(int n) override;
558 :
559 : virtual char **GetMetadataDomainList() override;
560 : virtual const char *GetMetadataItem(const char *pszName,
561 : const char *pszDomain = "") override;
562 :
563 : protected:
564 : CPLErr ReadBlocks(int x, int y, void *buffer, int bx0, int by0, int bx1,
565 : int by1, int advise_read);
566 : bool IsBlockInCache(int x, int y);
567 : CPLErr AskMiniDriverForBlock(WMSHTTPRequest &request, int x, int y);
568 : CPLErr ReadBlockFromCache(const char *pszKey, int x, int y,
569 : int to_buffer_band, void *buffer,
570 : int advise_read);
571 : CPLErr ReadBlockFromFile(const CPLString &soFileName, int x, int y,
572 : int to_buffer_band, void *buffer, int advise_read);
573 : CPLErr ReadBlockFromDataset(GDALDataset *ds, int x, int y,
574 : int to_buffer_band, void *buffer,
575 : int advise_read);
576 : CPLErr EmptyBlock(int x, int y, int to_buffer_band, void *buffer);
577 : static CPLErr ReportWMSException(const char *file_name);
578 :
579 : protected:
580 : GDALWMSDataset *m_parent_dataset;
581 : double m_scale;
582 : std::vector<GDALWMSRasterBand *> m_overviews;
583 : int m_overview;
584 : GDALColorInterp m_color_interp;
585 : int m_nAdviseReadBX0;
586 : int m_nAdviseReadBY0;
587 : int m_nAdviseReadBX1;
588 : int m_nAdviseReadBY1;
589 : };
590 :
591 : #endif /* notdef WMSDRIVER_H_INCLUDED */
|