/src/gdal/ogr/ogrsf_frmts/ngw/gdalngwdataset.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /******************************************************************************* |
2 | | * Project: NextGIS Web Driver |
3 | | * Purpose: Implements NextGIS Web Driver |
4 | | * Author: Dmitry Baryshnikov, dmitry.baryshnikov@nextgis.com |
5 | | * Language: C++ |
6 | | ******************************************************************************* |
7 | | * The MIT License (MIT) |
8 | | * |
9 | | * Copyright (c) 2018-2025, NextGIS <info@nextgis.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | *******************************************************************************/ |
13 | | |
14 | | #include "ogr_ngw.h" |
15 | | |
16 | | #include "cpl_http.h" |
17 | | #include "gdal_proxy.h" |
18 | | |
19 | | #include <array> |
20 | | #include <limits> |
21 | | |
22 | | class NGWWrapperRasterBand : public GDALProxyRasterBand |
23 | | { |
24 | | GDALRasterBand *poBaseBand; |
25 | | |
26 | | protected: |
27 | | virtual GDALRasterBand * |
28 | | RefUnderlyingRasterBand(bool /*bForceOpen*/) const override; |
29 | | |
30 | | public: |
31 | | explicit NGWWrapperRasterBand(GDALRasterBand *poBaseBandIn) |
32 | 0 | : poBaseBand(poBaseBandIn) |
33 | 0 | { |
34 | 0 | eDataType = poBaseBand->GetRasterDataType(); |
35 | 0 | poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize); |
36 | 0 | } |
37 | | }; |
38 | | |
39 | | GDALRasterBand * |
40 | | NGWWrapperRasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const |
41 | 0 | { |
42 | 0 | return poBaseBand; |
43 | 0 | } |
44 | | |
45 | | static const char *FormGDALTMSConnectionString(const std::string &osUrl, |
46 | | const std::string &osResourceId, |
47 | | int nEPSG, int nCacheExpires, |
48 | | int nCacheMaxSize) |
49 | 0 | { |
50 | 0 | std::string osRasterUrl = NGWAPI::GetTMSURL(osUrl, osResourceId); |
51 | 0 | char *pszRasterUrl = CPLEscapeString(osRasterUrl.c_str(), -1, CPLES_XML); |
52 | 0 | const char *pszConnStr = |
53 | 0 | CPLSPrintf("<GDAL_WMS><Service name=\"TMS\">" |
54 | 0 | "<ServerUrl>%s</ServerUrl></Service><DataWindow>" |
55 | 0 | "<UpperLeftX>-20037508.34</" |
56 | 0 | "UpperLeftX><UpperLeftY>20037508.34</UpperLeftY>" |
57 | 0 | "<LowerRightX>20037508.34</" |
58 | 0 | "LowerRightX><LowerRightY>-20037508.34</LowerRightY>" |
59 | 0 | "<TileLevel>%d</TileLevel><TileCountX>1</TileCountX>" |
60 | 0 | "<TileCountY>1</TileCountY><YOrigin>top</YOrigin></" |
61 | 0 | "DataWindow>" |
62 | 0 | "<Projection>EPSG:%d</Projection><BlockSizeX>256</" |
63 | 0 | "BlockSizeX>" |
64 | 0 | "<BlockSizeY>256</BlockSizeY><BandsCount>%d</BandsCount>" |
65 | 0 | "<Cache><Type>file</Type><Expires>%d</Expires><MaxSize>%d</" |
66 | 0 | "MaxSize>" |
67 | 0 | "</Cache><ZeroBlockHttpCodes>204,404</ZeroBlockHttpCodes></" |
68 | 0 | "GDAL_WMS>", |
69 | 0 | pszRasterUrl, |
70 | 0 | 22, // NOTE: We have no limit in zoom levels. |
71 | 0 | nEPSG, // NOTE: Default SRS is EPSG:3857. |
72 | 0 | 4, nCacheExpires, nCacheMaxSize); |
73 | |
|
74 | 0 | CPLFree(pszRasterUrl); |
75 | 0 | return pszConnStr; |
76 | 0 | } |
77 | | |
78 | | static std::string GetStylesIdentifiers(const CPLJSONArray &aoStyles, int nDeep) |
79 | 0 | { |
80 | 0 | std::string sOut; |
81 | 0 | if (nDeep > 255) |
82 | 0 | { |
83 | 0 | return sOut; |
84 | 0 | } |
85 | | |
86 | 0 | for (const auto &subobj : aoStyles) |
87 | 0 | { |
88 | 0 | auto sType = subobj.GetString("item_type"); |
89 | 0 | if (sType == "layer") |
90 | 0 | { |
91 | 0 | auto sId = subobj.GetString("layer_style_id"); |
92 | 0 | if (!sId.empty()) |
93 | 0 | { |
94 | 0 | if (sOut.empty()) |
95 | 0 | { |
96 | 0 | sOut = std::move(sId); |
97 | 0 | } |
98 | 0 | else |
99 | 0 | { |
100 | 0 | sOut += "," + sId; |
101 | 0 | } |
102 | 0 | } |
103 | 0 | } |
104 | 0 | else |
105 | 0 | { |
106 | 0 | auto aoChildren = subobj.GetArray("children"); |
107 | 0 | auto sId = GetStylesIdentifiers(aoChildren, nDeep + 1); |
108 | 0 | if (!sId.empty()) |
109 | 0 | { |
110 | 0 | if (sOut.empty()) |
111 | 0 | { |
112 | 0 | sOut = std::move(sId); |
113 | 0 | } |
114 | 0 | else |
115 | 0 | { |
116 | 0 | sOut += "," + sId; |
117 | 0 | } |
118 | 0 | } |
119 | 0 | } |
120 | 0 | } |
121 | 0 | return sOut; |
122 | 0 | } |
123 | | |
124 | | /* |
125 | | * OGRNGWDataset() |
126 | | */ |
127 | | OGRNGWDataset::OGRNGWDataset() |
128 | 353 | : nBatchSize(-1), nPageSize(-1), bFetchedPermissions(false), |
129 | 353 | bHasFeaturePaging(false), bExtInNativeData(false), bMetadataDerty(false), |
130 | 353 | poRasterDS(nullptr), nRasters(0), nCacheExpires(604800), // 7 days |
131 | 353 | nCacheMaxSize(67108864), // 64 MB |
132 | 353 | osJsonDepth("32") |
133 | 353 | { |
134 | 353 | } |
135 | | |
136 | | /* |
137 | | * ~OGRNGWDataset() |
138 | | */ |
139 | | OGRNGWDataset::~OGRNGWDataset() |
140 | 353 | { |
141 | | // Last sync with server. |
142 | 353 | OGRNGWDataset::FlushCache(true); |
143 | | |
144 | 353 | if (poRasterDS != nullptr) |
145 | 0 | { |
146 | 0 | GDALClose(poRasterDS); |
147 | 0 | poRasterDS = nullptr; |
148 | 0 | } |
149 | 353 | } |
150 | | |
151 | | /* |
152 | | * FetchPermissions() |
153 | | */ |
154 | | void OGRNGWDataset::FetchPermissions() |
155 | 0 | { |
156 | 0 | if (bFetchedPermissions) |
157 | 0 | { |
158 | 0 | return; |
159 | 0 | } |
160 | | |
161 | 0 | if (IsUpdateMode()) |
162 | 0 | { |
163 | | // Check connection and is it read only. |
164 | 0 | stPermissions = NGWAPI::CheckPermissions( |
165 | 0 | osUrl, osResourceId, GetHeaders(false), IsUpdateMode()); |
166 | 0 | } |
167 | 0 | else |
168 | 0 | { |
169 | 0 | stPermissions.bDataCanRead = true; |
170 | 0 | stPermissions.bResourceCanRead = true; |
171 | 0 | stPermissions.bDatastructCanRead = true; |
172 | 0 | stPermissions.bMetadataCanRead = true; |
173 | 0 | } |
174 | 0 | bFetchedPermissions = true; |
175 | 0 | } |
176 | | |
177 | | /* |
178 | | * TestCapability() |
179 | | */ |
180 | | int OGRNGWDataset::TestCapability(const char *pszCap) |
181 | 0 | { |
182 | 0 | FetchPermissions(); |
183 | 0 | if (EQUAL(pszCap, ODsCCreateLayer)) |
184 | 0 | { |
185 | 0 | return stPermissions.bResourceCanCreate; |
186 | 0 | } |
187 | 0 | else if (EQUAL(pszCap, ODsCDeleteLayer)) |
188 | 0 | { |
189 | 0 | return stPermissions.bResourceCanDelete; |
190 | 0 | } |
191 | 0 | else if (EQUAL(pszCap, "RenameLayer")) |
192 | 0 | { |
193 | 0 | return stPermissions.bResourceCanUpdate; |
194 | 0 | } |
195 | 0 | else if (EQUAL(pszCap, ODsCRandomLayerWrite)) |
196 | 0 | { |
197 | 0 | return stPermissions.bDataCanWrite; // FIXME: Check on resource level |
198 | | // is this permission set? |
199 | 0 | } |
200 | 0 | else if (EQUAL(pszCap, ODsCRandomLayerRead)) |
201 | 0 | { |
202 | 0 | return stPermissions.bDataCanRead; |
203 | 0 | } |
204 | 0 | else if (EQUAL(pszCap, ODsCZGeometries)) |
205 | 0 | { |
206 | 0 | return TRUE; |
207 | 0 | } |
208 | 0 | else if (EQUAL(pszCap, ODsCAddFieldDomain)) |
209 | 0 | { |
210 | 0 | return stPermissions.bResourceCanCreate; |
211 | 0 | } |
212 | 0 | else if (EQUAL(pszCap, ODsCDeleteFieldDomain)) |
213 | 0 | { |
214 | 0 | return stPermissions.bResourceCanDelete; |
215 | 0 | } |
216 | 0 | else if (EQUAL(pszCap, ODsCUpdateFieldDomain)) |
217 | 0 | { |
218 | 0 | return stPermissions.bResourceCanUpdate; |
219 | 0 | } |
220 | 0 | else |
221 | 0 | { |
222 | 0 | return FALSE; |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | | /* |
227 | | * GetLayer() |
228 | | */ |
229 | | OGRLayer *OGRNGWDataset::GetLayer(int iLayer) |
230 | 0 | { |
231 | 0 | if (iLayer < 0 || iLayer >= GetLayerCount()) |
232 | 0 | { |
233 | 0 | return nullptr; |
234 | 0 | } |
235 | 0 | else |
236 | 0 | { |
237 | 0 | return aoLayers[iLayer].get(); |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | | /* |
242 | | * Open() |
243 | | */ |
244 | | bool OGRNGWDataset::Open(const std::string &osUrlIn, |
245 | | const std::string &osResourceIdIn, |
246 | | char **papszOpenOptionsIn, bool bUpdateIn, |
247 | | int nOpenFlagsIn) |
248 | 313 | { |
249 | 313 | osUrl = osUrlIn; |
250 | 313 | osResourceId = osResourceIdIn; |
251 | | |
252 | 313 | eAccess = bUpdateIn ? GA_Update : GA_ReadOnly; |
253 | | |
254 | 313 | osUserPwd = CSLFetchNameValueDef(papszOpenOptionsIn, "USERPWD", |
255 | 313 | CPLGetConfigOption("NGW_USERPWD", "")); |
256 | | |
257 | 313 | nBatchSize = |
258 | 313 | atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "BATCH_SIZE", |
259 | 313 | CPLGetConfigOption("NGW_BATCH_SIZE", "-1"))); |
260 | | |
261 | 313 | nPageSize = |
262 | 313 | atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "PAGE_SIZE", |
263 | 313 | CPLGetConfigOption("NGW_PAGE_SIZE", "-1"))); |
264 | 313 | if (nPageSize == 0) |
265 | 0 | { |
266 | 0 | nPageSize = -1; |
267 | 0 | } |
268 | | |
269 | 313 | nCacheExpires = atoi(CSLFetchNameValueDef( |
270 | 313 | papszOpenOptionsIn, "CACHE_EXPIRES", |
271 | 313 | CPLGetConfigOption("NGW_CACHE_EXPIRES", "604800"))); |
272 | | |
273 | 313 | nCacheMaxSize = atoi(CSLFetchNameValueDef( |
274 | 313 | papszOpenOptionsIn, "CACHE_MAX_SIZE", |
275 | 313 | CPLGetConfigOption("NGW_CACHE_MAX_SIZE", "67108864"))); |
276 | | |
277 | 313 | bExtInNativeData = |
278 | 313 | CPLFetchBool(papszOpenOptionsIn, "NATIVE_DATA", |
279 | 313 | CPLTestBool(CPLGetConfigOption("NGW_NATIVE_DATA", "NO"))); |
280 | | |
281 | 313 | osJsonDepth = |
282 | 313 | CSLFetchNameValueDef(papszOpenOptionsIn, "JSON_DEPTH", |
283 | 313 | CPLGetConfigOption("NGW_JSON_DEPTH", "32")); |
284 | | |
285 | 313 | osExtensions = |
286 | 313 | CSLFetchNameValueDef(papszOpenOptionsIn, "EXTENSIONS", |
287 | 313 | CPLGetConfigOption("NGW_EXTENSIONS", "")); |
288 | | |
289 | 313 | osConnectTimeout = |
290 | 313 | CSLFetchNameValueDef(papszOpenOptionsIn, "CONNECTTIMEOUT", |
291 | 313 | CPLGetConfigOption("NGW_CONNECTTIMEOUT", "")); |
292 | 313 | osTimeout = CSLFetchNameValueDef(papszOpenOptionsIn, "TIMEOUT", |
293 | 313 | CPLGetConfigOption("NGW_TIMEOUT", "")); |
294 | 313 | osRetryCount = |
295 | 313 | CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_RETRY", |
296 | 313 | CPLGetConfigOption("NGW_MAX_RETRY", "")); |
297 | 313 | osRetryDelay = |
298 | 313 | CSLFetchNameValueDef(papszOpenOptionsIn, "RETRY_DELAY", |
299 | 313 | CPLGetConfigOption("NGW_RETRY_DELAY", "")); |
300 | | |
301 | 313 | if (osExtensions.empty()) |
302 | 313 | { |
303 | 313 | bExtInNativeData = false; |
304 | 313 | } |
305 | | |
306 | 313 | CPLDebug("NGW", |
307 | 313 | "Open options:\n" |
308 | 313 | " BATCH_SIZE %d\n" |
309 | 313 | " PAGE_SIZE %d\n" |
310 | 313 | " CACHE_EXPIRES %d\n" |
311 | 313 | " CACHE_MAX_SIZE %d\n" |
312 | 313 | " JSON_DEPTH %s\n" |
313 | 313 | " EXTENSIONS %s\n" |
314 | 313 | " CONNECTTIMEOUT %s\n" |
315 | 313 | " TIMEOUT %s\n" |
316 | 313 | " MAX_RETRY %s\n" |
317 | 313 | " RETRY_DELAY %s", |
318 | 313 | nBatchSize, nPageSize, nCacheExpires, nCacheMaxSize, |
319 | 313 | osJsonDepth.c_str(), osExtensions.c_str(), |
320 | 313 | osConnectTimeout.c_str(), osTimeout.c_str(), osRetryCount.c_str(), |
321 | 313 | osRetryDelay.c_str()); |
322 | | |
323 | 313 | return Init(nOpenFlagsIn); |
324 | 313 | } |
325 | | |
326 | | /* |
327 | | * Open() |
328 | | * |
329 | | * The pszFilename templates: |
330 | | * - NGW:http://some.nextgis.com/resource/0 |
331 | | * - NGW:http://some.nextgis.com:8000/test/resource/0 |
332 | | */ |
333 | | bool OGRNGWDataset::Open(const char *pszFilename, char **papszOpenOptionsIn, |
334 | | bool bUpdateIn, int nOpenFlagsIn) |
335 | 353 | { |
336 | 353 | NGWAPI::Uri stUri = NGWAPI::ParseUri(pszFilename); |
337 | | |
338 | 353 | if (stUri.osPrefix != "NGW") |
339 | 40 | { |
340 | 40 | CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s", |
341 | 40 | pszFilename); |
342 | 40 | return false; |
343 | 40 | } |
344 | | |
345 | 313 | osUrl = stUri.osAddress; |
346 | 313 | osResourceId = stUri.osResourceId; |
347 | | |
348 | 313 | return Open(stUri.osAddress, stUri.osResourceId, papszOpenOptionsIn, |
349 | 313 | bUpdateIn, nOpenFlagsIn); |
350 | 353 | } |
351 | | |
352 | | /* |
353 | | * SetupRasterDSWrapper() |
354 | | */ |
355 | | void OGRNGWDataset::SetupRasterDSWrapper(const OGREnvelope &stExtent) |
356 | 0 | { |
357 | 0 | if (poRasterDS) |
358 | 0 | { |
359 | 0 | nRasterXSize = poRasterDS->GetRasterXSize(); |
360 | 0 | nRasterYSize = poRasterDS->GetRasterYSize(); |
361 | |
|
362 | 0 | for (int iBand = 1; iBand <= poRasterDS->GetRasterCount(); iBand++) |
363 | 0 | { |
364 | 0 | SetBand(iBand, |
365 | 0 | new NGWWrapperRasterBand(poRasterDS->GetRasterBand(iBand))); |
366 | 0 | } |
367 | |
|
368 | 0 | if (stExtent.IsInit()) |
369 | 0 | { |
370 | | // Set pixel limits. |
371 | 0 | bool bHasTransform = false; |
372 | 0 | GDALGeoTransform gt, invGT; |
373 | 0 | if (poRasterDS->GetGeoTransform(gt) == CE_None) |
374 | 0 | { |
375 | 0 | bHasTransform = gt.GetInverse(invGT); |
376 | 0 | } |
377 | |
|
378 | 0 | if (bHasTransform) |
379 | 0 | { |
380 | 0 | invGT.Apply(stExtent.MinX, stExtent.MinY, &stPixelExtent.MinX, |
381 | 0 | &stPixelExtent.MaxY); |
382 | |
|
383 | 0 | invGT.Apply(stExtent.MaxX, stExtent.MaxY, &stPixelExtent.MaxX, |
384 | 0 | &stPixelExtent.MinY); |
385 | |
|
386 | 0 | CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f", |
387 | 0 | stPixelExtent.MinX, stPixelExtent.MinY, |
388 | 0 | stPixelExtent.MaxX, stPixelExtent.MaxY); |
389 | 0 | } |
390 | 0 | else |
391 | 0 | { |
392 | 0 | stPixelExtent.MinX = 0.0; |
393 | 0 | stPixelExtent.MinY = 0.0; |
394 | 0 | stPixelExtent.MaxX = std::numeric_limits<double>::max(); |
395 | 0 | stPixelExtent.MaxY = std::numeric_limits<double>::max(); |
396 | 0 | } |
397 | 0 | } |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | /* |
402 | | * Init() |
403 | | */ |
404 | | bool OGRNGWDataset::Init(int nOpenFlagsIn) |
405 | 313 | { |
406 | | // NOTE: Skip check API version at that moment. We expected API v3 or higher. |
407 | | |
408 | | // Get resource details. |
409 | 313 | CPLJSONDocument oResourceDetailsReq; |
410 | 313 | auto aosHTTPOptions = GetHeaders(false); |
411 | 313 | bool bResult = oResourceDetailsReq.LoadUrl( |
412 | 313 | NGWAPI::GetResourceURL(osUrl, osResourceId), aosHTTPOptions); |
413 | | |
414 | 313 | CPLDebug("NGW", "Get resource %s details %s", osResourceId.c_str(), |
415 | 313 | bResult ? "success" : "failed"); |
416 | | |
417 | 313 | if (bResult) |
418 | 0 | { |
419 | 0 | CPLJSONObject oRoot = oResourceDetailsReq.GetRoot(); |
420 | |
|
421 | 0 | if (oRoot.IsValid()) |
422 | 0 | { |
423 | 0 | auto osResourceType = oRoot.GetString("resource/cls"); |
424 | 0 | FillMetadata(oRoot); |
425 | |
|
426 | 0 | if (osResourceType == "resource_group") |
427 | 0 | { |
428 | | // Check feature paging. |
429 | 0 | FillCapabilities(aosHTTPOptions); |
430 | 0 | if (oRoot.GetBool("resource/children", false)) |
431 | 0 | { |
432 | | // Get child resources. |
433 | 0 | bResult = FillResources(aosHTTPOptions, nOpenFlagsIn); |
434 | 0 | } |
435 | 0 | } |
436 | 0 | else if (NGWAPI::CheckSupportedType(false, osResourceType)) |
437 | 0 | { |
438 | | // Check feature paging. |
439 | 0 | FillCapabilities(aosHTTPOptions); |
440 | | // Add vector layer. |
441 | 0 | AddLayer(oRoot, aosHTTPOptions, nOpenFlagsIn); |
442 | 0 | } |
443 | 0 | else if (osResourceType == "mapserver_style" || |
444 | 0 | osResourceType == "qgis_vector_style" || |
445 | 0 | osResourceType == "raster_style" || |
446 | 0 | osResourceType == "qgis_raster_style") |
447 | 0 | { |
448 | | // GetExtent from parent. |
449 | 0 | OGREnvelope stExtent; |
450 | 0 | std::string osParentId = oRoot.GetString("resource/parent/id"); |
451 | 0 | bool bExtentResult = NGWAPI::GetExtent( |
452 | 0 | osUrl, osParentId, aosHTTPOptions, 3857, stExtent); |
453 | |
|
454 | 0 | if (!bExtentResult) |
455 | 0 | { |
456 | | // Set full extent for EPSG:3857. |
457 | 0 | stExtent.MinX = -20037508.34; |
458 | 0 | stExtent.MaxX = 20037508.34; |
459 | 0 | stExtent.MinY = -20037508.34; |
460 | 0 | stExtent.MaxY = 20037508.34; |
461 | 0 | } |
462 | |
|
463 | 0 | CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f", |
464 | 0 | stExtent.MinX, stExtent.MinY, stExtent.MaxX, |
465 | 0 | stExtent.MaxY); |
466 | |
|
467 | 0 | int nEPSG = 3857; |
468 | | // NOTE: Get parent details. We can skip this as default SRS in |
469 | | // NGW is 3857. |
470 | 0 | CPLJSONDocument oResourceReq; |
471 | 0 | bResult = oResourceReq.LoadUrl( |
472 | 0 | NGWAPI::GetResourceURL(osUrl, osResourceId), |
473 | 0 | aosHTTPOptions); |
474 | |
|
475 | 0 | if (bResult) |
476 | 0 | { |
477 | 0 | CPLJSONObject oParentRoot = oResourceReq.GetRoot(); |
478 | 0 | if (osResourceType == "mapserver_style" || |
479 | 0 | osResourceType == "qgis_vector_style") |
480 | 0 | { |
481 | 0 | nEPSG = oParentRoot.GetInteger("vector_layer/srs/id", |
482 | 0 | nEPSG); |
483 | 0 | } |
484 | 0 | else if (osResourceType == "raster_style" || |
485 | 0 | osResourceType == "qgis_raster_style") |
486 | 0 | { |
487 | 0 | nEPSG = oParentRoot.GetInteger("raster_layer/srs/id", |
488 | 0 | nEPSG); |
489 | 0 | } |
490 | 0 | } |
491 | |
|
492 | 0 | const char *pszConnStr = FormGDALTMSConnectionString( |
493 | 0 | osUrl, osResourceId, nEPSG, nCacheExpires, nCacheMaxSize); |
494 | 0 | CPLDebug("NGW", "Open %s as '%s'", osResourceType.c_str(), |
495 | 0 | pszConnStr); |
496 | 0 | poRasterDS = GDALDataset::FromHandle(GDALOpenEx( |
497 | 0 | pszConnStr, |
498 | 0 | GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL, |
499 | 0 | nullptr, nullptr, nullptr)); |
500 | 0 | SetupRasterDSWrapper(stExtent); |
501 | 0 | } |
502 | 0 | else if (osResourceType == "wmsclient_layer") |
503 | 0 | { |
504 | 0 | OGREnvelope stExtent; |
505 | | // Set full extent for EPSG:3857. |
506 | 0 | stExtent.MinX = -20037508.34; |
507 | 0 | stExtent.MaxX = 20037508.34; |
508 | 0 | stExtent.MinY = -20037508.34; |
509 | 0 | stExtent.MaxY = 20037508.34; |
510 | |
|
511 | 0 | CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f", |
512 | 0 | stExtent.MinX, stExtent.MinY, stExtent.MaxX, |
513 | 0 | stExtent.MaxY); |
514 | |
|
515 | 0 | int nEPSG = oRoot.GetInteger("wmsclient_layer/srs/id", 3857); |
516 | |
|
517 | 0 | const char *pszConnStr = FormGDALTMSConnectionString( |
518 | 0 | osUrl, osResourceId, nEPSG, nCacheExpires, nCacheMaxSize); |
519 | 0 | poRasterDS = GDALDataset::FromHandle(GDALOpenEx( |
520 | 0 | pszConnStr, |
521 | 0 | GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL, |
522 | 0 | nullptr, nullptr, nullptr)); |
523 | 0 | SetupRasterDSWrapper(stExtent); |
524 | 0 | } |
525 | 0 | else if (osResourceType == "basemap_layer") |
526 | 0 | { |
527 | 0 | auto osTMSURL = oRoot.GetString("basemap_layer/url"); |
528 | 0 | int nEPSG = 3857; |
529 | 0 | auto osQMS = oRoot.GetString("basemap_layer/qms"); |
530 | 0 | if (!osQMS.empty()) |
531 | 0 | { |
532 | 0 | CPLJSONDocument oDoc; |
533 | 0 | if (oDoc.LoadMemory(osQMS)) |
534 | 0 | { |
535 | 0 | auto oQMLRoot = oDoc.GetRoot(); |
536 | 0 | nEPSG = oQMLRoot.GetInteger("epsg"); |
537 | 0 | } |
538 | 0 | } |
539 | | |
540 | | // TODO: for EPSG != 3857 need to calc full extent |
541 | 0 | if (nEPSG != 3857) |
542 | 0 | { |
543 | 0 | bResult = false; |
544 | 0 | } |
545 | 0 | else |
546 | 0 | { |
547 | 0 | OGREnvelope stExtent; |
548 | | // Set full extent for EPSG:3857. |
549 | 0 | stExtent.MinX = -20037508.34; |
550 | 0 | stExtent.MaxX = 20037508.34; |
551 | 0 | stExtent.MinY = -20037508.34; |
552 | 0 | stExtent.MaxY = 20037508.34; |
553 | |
|
554 | 0 | const char *pszConnStr = FormGDALTMSConnectionString( |
555 | 0 | osTMSURL, osResourceId, nEPSG, nCacheExpires, |
556 | 0 | nCacheMaxSize); |
557 | 0 | poRasterDS = GDALDataset::FromHandle(GDALOpenEx( |
558 | 0 | pszConnStr, |
559 | 0 | GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL, |
560 | 0 | nullptr, nullptr, nullptr)); |
561 | 0 | SetupRasterDSWrapper(stExtent); |
562 | 0 | } |
563 | 0 | } |
564 | 0 | else if (osResourceType == "webmap") |
565 | 0 | { |
566 | 0 | OGREnvelope stExtent; |
567 | | // Set full extent for EPSG:3857. |
568 | 0 | stExtent.MinX = -20037508.34; |
569 | 0 | stExtent.MaxX = 20037508.34; |
570 | 0 | stExtent.MinY = -20037508.34; |
571 | 0 | stExtent.MaxY = 20037508.34; |
572 | | |
573 | | // Get all styles |
574 | 0 | auto aoChildren = oRoot.GetArray("webmap/children"); |
575 | 0 | auto sIdentifiers = GetStylesIdentifiers(aoChildren, 0); |
576 | |
|
577 | 0 | const char *pszConnStr = FormGDALTMSConnectionString( |
578 | 0 | osUrl, sIdentifiers, 3857, nCacheExpires, nCacheMaxSize); |
579 | 0 | poRasterDS = GDALDataset::FromHandle(GDALOpenEx( |
580 | 0 | pszConnStr, |
581 | 0 | GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL, |
582 | 0 | nullptr, nullptr, nullptr)); |
583 | 0 | SetupRasterDSWrapper(stExtent); |
584 | 0 | } |
585 | 0 | else if (osResourceType == "raster_layer") |
586 | 0 | { |
587 | 0 | auto osCogURL = NGWAPI::GetCOGURL(osUrl, osResourceId); |
588 | 0 | auto osConnStr = std::string("/vsicurl/") + osCogURL; |
589 | |
|
590 | 0 | CPLDebug("NGW", "Raster url is: %s", osConnStr.c_str()); |
591 | |
|
592 | 0 | poRasterDS = GDALDataset::FromHandle(GDALOpenEx( |
593 | 0 | osConnStr.c_str(), |
594 | 0 | GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL, |
595 | 0 | nullptr, nullptr, nullptr)); |
596 | | |
597 | | // Add styles if exists |
598 | 0 | auto osRasterResourceId = oRoot.GetString("resource/id"); |
599 | 0 | CPLJSONDocument oResourceRequest; |
600 | 0 | bool bLoadResult = oResourceRequest.LoadUrl( |
601 | 0 | NGWAPI::GetChildrenURL(osUrl, osRasterResourceId), |
602 | 0 | aosHTTPOptions); |
603 | 0 | if (bLoadResult) |
604 | 0 | { |
605 | 0 | CPLJSONArray oChildren(oResourceRequest.GetRoot()); |
606 | 0 | for (const auto &oChild : oChildren) |
607 | 0 | { |
608 | 0 | AddRaster(oChild); |
609 | 0 | } |
610 | 0 | } |
611 | |
|
612 | 0 | SetupRasterDSWrapper(OGREnvelope()); |
613 | 0 | } |
614 | 0 | else |
615 | 0 | { |
616 | 0 | bResult = false; |
617 | 0 | } |
618 | | |
619 | | // TODO: Add support for wfsserver_service, wmsserver_service, |
620 | | // raster_mosaic, tileset. |
621 | 0 | } |
622 | 0 | } |
623 | | |
624 | 313 | return bResult; |
625 | 313 | } |
626 | | |
627 | | /* |
628 | | * FillResources() |
629 | | */ |
630 | | bool OGRNGWDataset::FillResources(const CPLStringList &aosHTTPOptions, |
631 | | int nOpenFlagsIn) |
632 | 0 | { |
633 | 0 | CPLJSONDocument oResourceDetailsReq; |
634 | | // Fill domains |
635 | 0 | bool bResult = oResourceDetailsReq.LoadUrl( |
636 | 0 | NGWAPI::GetSearchURL(osUrl, "cls", "lookup_table"), aosHTTPOptions); |
637 | 0 | if (bResult) |
638 | 0 | { |
639 | 0 | CPLJSONArray oChildren(oResourceDetailsReq.GetRoot()); |
640 | 0 | for (const auto &oChild : oChildren) |
641 | 0 | { |
642 | 0 | OGRNGWCodedFieldDomain oDomain(oChild); |
643 | 0 | if (oDomain.GetID() > 0) |
644 | 0 | { |
645 | 0 | moDomains[oDomain.GetID()] = std::move(oDomain); |
646 | 0 | } |
647 | 0 | } |
648 | 0 | } |
649 | | |
650 | | // Fill child resources |
651 | 0 | bResult = oResourceDetailsReq.LoadUrl( |
652 | 0 | NGWAPI::GetChildrenURL(osUrl, osResourceId), aosHTTPOptions); |
653 | |
|
654 | 0 | if (bResult) |
655 | 0 | { |
656 | 0 | CPLJSONArray oChildren(oResourceDetailsReq.GetRoot()); |
657 | 0 | for (const auto &oChild : oChildren) |
658 | 0 | { |
659 | 0 | if (nOpenFlagsIn & GDAL_OF_VECTOR) |
660 | 0 | { |
661 | | // Add vector layer. If failed, try next layer. |
662 | 0 | AddLayer(oChild, aosHTTPOptions, nOpenFlagsIn); |
663 | 0 | } |
664 | |
|
665 | 0 | if (nOpenFlagsIn & GDAL_OF_RASTER) |
666 | 0 | { |
667 | 0 | AddRaster(oChild); |
668 | 0 | } |
669 | 0 | } |
670 | 0 | } |
671 | 0 | return bResult; |
672 | 0 | } |
673 | | |
674 | | /* |
675 | | * AddLayer() |
676 | | */ |
677 | | void OGRNGWDataset::AddLayer(const CPLJSONObject &oResourceJsonObject, |
678 | | const CPLStringList &aosHTTPOptions, |
679 | | int nOpenFlagsIn) |
680 | 0 | { |
681 | 0 | auto osResourceType = oResourceJsonObject.GetString("resource/cls"); |
682 | 0 | if (!NGWAPI::CheckSupportedType(false, osResourceType)) |
683 | 0 | { |
684 | | // NOTE: Only vector_layer and postgis_layer types now supported |
685 | 0 | return; |
686 | 0 | } |
687 | | |
688 | 0 | auto osLayerResourceId = oResourceJsonObject.GetString("resource/id"); |
689 | 0 | if (nOpenFlagsIn & GDAL_OF_VECTOR) |
690 | 0 | { |
691 | 0 | OGRNGWLayerPtr poLayer(new OGRNGWLayer(this, oResourceJsonObject)); |
692 | 0 | aoLayers.emplace_back(poLayer); |
693 | 0 | osLayerResourceId = poLayer->GetResourceId(); |
694 | 0 | } |
695 | | |
696 | | // Check styles exist and add them as rasters. |
697 | 0 | if (nOpenFlagsIn & GDAL_OF_RASTER && |
698 | 0 | oResourceJsonObject.GetBool("resource/children", false)) |
699 | 0 | { |
700 | 0 | CPLJSONDocument oResourceChildReq; |
701 | 0 | bool bResult = oResourceChildReq.LoadUrl( |
702 | 0 | NGWAPI::GetChildrenURL(osUrl, osLayerResourceId), aosHTTPOptions); |
703 | |
|
704 | 0 | if (bResult) |
705 | 0 | { |
706 | 0 | CPLJSONArray oChildren(oResourceChildReq.GetRoot()); |
707 | 0 | for (const auto &oChild : oChildren) |
708 | 0 | { |
709 | 0 | AddRaster(oChild); |
710 | 0 | } |
711 | 0 | } |
712 | 0 | } |
713 | 0 | } |
714 | | |
715 | | /* |
716 | | * AddRaster() |
717 | | */ |
718 | | void OGRNGWDataset::AddRaster(const CPLJSONObject &oRasterJsonObj) |
719 | 0 | { |
720 | 0 | auto osResourceType = oRasterJsonObj.GetString("resource/cls"); |
721 | 0 | if (!NGWAPI::CheckSupportedType(true, osResourceType)) |
722 | 0 | { |
723 | 0 | return; |
724 | 0 | } |
725 | | |
726 | 0 | auto osOutResourceId = oRasterJsonObj.GetString("resource/id"); |
727 | 0 | auto osOutResourceName = oRasterJsonObj.GetString("resource/display_name"); |
728 | |
|
729 | 0 | if (osOutResourceName.empty()) |
730 | 0 | { |
731 | 0 | osOutResourceName = "raster_" + osOutResourceId; |
732 | 0 | } |
733 | |
|
734 | 0 | CPLDebug("NGW", "Add raster %s: %s", osOutResourceId.c_str(), |
735 | 0 | osOutResourceName.c_str()); |
736 | |
|
737 | 0 | GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_NAME", nRasters + 1), |
738 | 0 | CPLSPrintf("NGW:%s/resource/%s", osUrl.c_str(), |
739 | 0 | osOutResourceId.c_str()), |
740 | 0 | "SUBDATASETS"); |
741 | 0 | GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_DESC", nRasters + 1), |
742 | 0 | CPLSPrintf("%s (%s)", |
743 | 0 | osOutResourceName.c_str(), |
744 | 0 | osResourceType.c_str()), |
745 | 0 | "SUBDATASETS"); |
746 | 0 | nRasters++; |
747 | 0 | } |
748 | | |
749 | | /* |
750 | | * ICreateLayer |
751 | | */ |
752 | | OGRLayer *OGRNGWDataset::ICreateLayer(const char *pszNameIn, |
753 | | const OGRGeomFieldDefn *poGeomFieldDefn, |
754 | | CSLConstList papszOptions) |
755 | 0 | { |
756 | 0 | if (!IsUpdateMode()) |
757 | 0 | { |
758 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
759 | 0 | "Operation not available in read-only mode"); |
760 | 0 | return nullptr; |
761 | 0 | } |
762 | | |
763 | 0 | const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone; |
764 | 0 | const auto poSpatialRef = |
765 | 0 | poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr; |
766 | | |
767 | | // Check permissions as we create new layer in memory and will create in |
768 | | // during SyncToDisk. |
769 | 0 | FetchPermissions(); |
770 | |
|
771 | 0 | if (!stPermissions.bResourceCanCreate) |
772 | 0 | { |
773 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted."); |
774 | 0 | return nullptr; |
775 | 0 | } |
776 | | |
777 | | // Check input parameters. |
778 | 0 | if ((eGType < wkbPoint || eGType > wkbMultiPolygon) && |
779 | 0 | (eGType < wkbPoint25D || eGType > wkbMultiPolygon25D)) |
780 | 0 | { |
781 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unsupported geometry type: %s", |
782 | 0 | OGRGeometryTypeToName(eGType)); |
783 | 0 | return nullptr; |
784 | 0 | } |
785 | | |
786 | 0 | if (!poSpatialRef) |
787 | 0 | { |
788 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Undefined spatial reference"); |
789 | 0 | return nullptr; |
790 | 0 | } |
791 | | |
792 | 0 | OGRSpatialReference *poSRSClone = poSpatialRef->Clone(); |
793 | 0 | poSRSClone->AutoIdentifyEPSG(); |
794 | 0 | const char *pszEPSG = poSRSClone->GetAuthorityCode(nullptr); |
795 | 0 | int nEPSG = -1; |
796 | 0 | if (pszEPSG != nullptr) |
797 | 0 | { |
798 | 0 | nEPSG = atoi(pszEPSG); |
799 | 0 | } |
800 | |
|
801 | 0 | if (nEPSG != 3857) // TODO: Check NextGIS Web supported SRS. |
802 | 0 | { |
803 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
804 | 0 | "Unsupported spatial reference EPSG code: %d", nEPSG); |
805 | 0 | poSRSClone->Release(); |
806 | 0 | return nullptr; |
807 | 0 | } |
808 | | |
809 | | // Do we already have this layer? If so, should we blow it away? |
810 | 0 | bool bOverwrite = CPLFetchBool(papszOptions, "OVERWRITE", false); |
811 | 0 | for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer) |
812 | 0 | { |
813 | 0 | if (EQUAL(pszNameIn, aoLayers[iLayer]->GetName())) |
814 | 0 | { |
815 | 0 | if (bOverwrite) |
816 | 0 | { |
817 | 0 | DeleteLayer(iLayer); |
818 | 0 | break; |
819 | 0 | } |
820 | 0 | else |
821 | 0 | { |
822 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
823 | 0 | "Layer %s already exists, CreateLayer failed.\n" |
824 | 0 | "Use the layer creation option OVERWRITE=YES to " |
825 | 0 | "replace it.", |
826 | 0 | pszNameIn); |
827 | 0 | poSRSClone->Release(); |
828 | 0 | return nullptr; |
829 | 0 | } |
830 | 0 | } |
831 | 0 | } |
832 | | |
833 | | // Create layer. |
834 | 0 | std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", ""); |
835 | 0 | std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", ""); |
836 | 0 | poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
837 | 0 | OGRNGWLayerPtr poLayer( |
838 | 0 | new OGRNGWLayer(this, pszNameIn, poSRSClone, eGType, osKey, osDesc)); |
839 | 0 | poSRSClone->Release(); |
840 | 0 | aoLayers.emplace_back(poLayer); |
841 | 0 | return poLayer.get(); |
842 | 0 | } |
843 | | |
844 | | /* |
845 | | * DeleteLayer() |
846 | | */ |
847 | | OGRErr OGRNGWDataset::DeleteLayer(int iLayer) |
848 | 0 | { |
849 | 0 | if (!IsUpdateMode()) |
850 | 0 | { |
851 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
852 | 0 | "Operation not available in read-only mode."); |
853 | 0 | return OGRERR_FAILURE; |
854 | 0 | } |
855 | | |
856 | 0 | if (iLayer < 0 || iLayer >= GetLayerCount()) |
857 | 0 | { |
858 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
859 | 0 | "Layer %d not in legal range of 0 to %d.", iLayer, |
860 | 0 | GetLayerCount() - 1); |
861 | 0 | return OGRERR_FAILURE; |
862 | 0 | } |
863 | | |
864 | 0 | auto poLayer = aoLayers[iLayer]; |
865 | 0 | if (poLayer->GetResourceId() != "-1") |
866 | 0 | { |
867 | | // For layers from server we can check permissions. |
868 | | |
869 | | // We can skip check permissions here as papoLayers[iLayer]->Delete() |
870 | | // will return false if no delete permission available. |
871 | 0 | FetchPermissions(); |
872 | |
|
873 | 0 | if (!stPermissions.bResourceCanDelete) |
874 | 0 | { |
875 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted."); |
876 | 0 | return OGRERR_FAILURE; |
877 | 0 | } |
878 | 0 | } |
879 | | |
880 | 0 | if (poLayer->Delete()) |
881 | 0 | { |
882 | 0 | aoLayers.erase(aoLayers.begin() + iLayer); |
883 | 0 | } |
884 | |
|
885 | 0 | return OGRERR_NONE; |
886 | 0 | } |
887 | | |
888 | | /* |
889 | | * FillMetadata() |
890 | | */ |
891 | | void OGRNGWDataset::FillMetadata(const CPLJSONObject &oRootObject) |
892 | 0 | { |
893 | 0 | std::string osCreateDate = oRootObject.GetString("resource/creation_date"); |
894 | 0 | if (!osCreateDate.empty()) |
895 | 0 | { |
896 | 0 | GDALDataset::SetMetadataItem("creation_date", osCreateDate.c_str()); |
897 | 0 | } |
898 | 0 | osName = oRootObject.GetString("resource/display_name"); |
899 | 0 | SetDescription(osName.c_str()); |
900 | 0 | GDALDataset::SetMetadataItem("display_name", osName.c_str()); |
901 | 0 | std::string osDescription = oRootObject.GetString("resource/description"); |
902 | 0 | if (!osDescription.empty()) |
903 | 0 | { |
904 | 0 | GDALDataset::SetMetadataItem("description", osDescription.c_str()); |
905 | 0 | } |
906 | 0 | std::string osResourceType = oRootObject.GetString("resource/cls"); |
907 | 0 | if (!osResourceType.empty()) |
908 | 0 | { |
909 | 0 | GDALDataset::SetMetadataItem("resource_type", osResourceType.c_str()); |
910 | 0 | } |
911 | 0 | std::string osResourceParentId = |
912 | 0 | oRootObject.GetString("resource/parent/id"); |
913 | 0 | if (!osResourceParentId.empty()) |
914 | 0 | { |
915 | 0 | GDALDataset::SetMetadataItem("parent_id", osResourceParentId.c_str()); |
916 | 0 | } |
917 | 0 | GDALDataset::SetMetadataItem("id", osResourceId.c_str()); |
918 | |
|
919 | 0 | std::vector<CPLJSONObject> items = |
920 | 0 | oRootObject.GetObj("resmeta/items").GetChildren(); |
921 | |
|
922 | 0 | for (const CPLJSONObject &item : items) |
923 | 0 | { |
924 | 0 | std::string osSuffix = NGWAPI::GetResmetaSuffix(item.GetType()); |
925 | 0 | GDALDataset::SetMetadataItem((item.GetName() + osSuffix).c_str(), |
926 | 0 | item.ToString().c_str(), "NGW"); |
927 | 0 | } |
928 | 0 | } |
929 | | |
930 | | /* |
931 | | * FlushMetadata() |
932 | | */ |
933 | | bool OGRNGWDataset::FlushMetadata(char **papszMetadata) |
934 | 353 | { |
935 | 353 | if (!bMetadataDerty) |
936 | 353 | { |
937 | 353 | return true; |
938 | 353 | } |
939 | | |
940 | 0 | bool bResult = NGWAPI::FlushMetadata(osUrl, osResourceId, papszMetadata, |
941 | 0 | GetHeaders(false)); |
942 | 0 | if (bResult) |
943 | 0 | { |
944 | 0 | bMetadataDerty = false; |
945 | 0 | } |
946 | |
|
947 | 0 | return bResult; |
948 | 353 | } |
949 | | |
950 | | /* |
951 | | * SetMetadata() |
952 | | */ |
953 | | CPLErr OGRNGWDataset::SetMetadata(char **papszMetadata, const char *pszDomain) |
954 | 0 | { |
955 | 0 | FetchPermissions(); |
956 | 0 | if (!stPermissions.bMetadataCanWrite) |
957 | 0 | { |
958 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted."); |
959 | 0 | return CE_Failure; |
960 | 0 | } |
961 | | |
962 | 0 | CPLErr eResult = GDALDataset::SetMetadata(papszMetadata, pszDomain); |
963 | 0 | if (eResult == CE_None && pszDomain != nullptr && EQUAL(pszDomain, "NGW")) |
964 | 0 | { |
965 | 0 | eResult = FlushMetadata(papszMetadata) ? CE_None : CE_Failure; |
966 | 0 | } |
967 | 0 | return eResult; |
968 | 0 | } |
969 | | |
970 | | /* |
971 | | * SetMetadataItem() |
972 | | */ |
973 | | CPLErr OGRNGWDataset::SetMetadataItem(const char *pszName, const char *pszValue, |
974 | | const char *pszDomain) |
975 | 0 | { |
976 | 0 | FetchPermissions(); |
977 | 0 | if (!stPermissions.bMetadataCanWrite) |
978 | 0 | { |
979 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted."); |
980 | 0 | return CE_Failure; |
981 | 0 | } |
982 | 0 | if (pszDomain != nullptr && EQUAL(pszDomain, "NGW")) |
983 | 0 | { |
984 | 0 | bMetadataDerty = true; |
985 | 0 | } |
986 | 0 | return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain); |
987 | 0 | } |
988 | | |
989 | | /* |
990 | | * FlushCache() |
991 | | */ |
992 | | CPLErr OGRNGWDataset::FlushCache(bool bAtClosing) |
993 | 353 | { |
994 | 353 | CPLErr eErr = GDALDataset::FlushCache(bAtClosing); |
995 | 353 | if (!FlushMetadata(GetMetadata("NGW"))) |
996 | 0 | eErr = CE_Failure; |
997 | 353 | return eErr; |
998 | 353 | } |
999 | | |
1000 | | /* |
1001 | | * GetHeaders() |
1002 | | */ |
1003 | | CPLStringList OGRNGWDataset::GetHeaders(bool bSkipRetry) const |
1004 | 313 | { |
1005 | 313 | CPLStringList aosOptions; |
1006 | 313 | aosOptions.AddNameValue("HEADERS", "Accept: */*"); |
1007 | 313 | aosOptions.AddNameValue("JSON_DEPTH", osJsonDepth.c_str()); |
1008 | 313 | if (!osUserPwd.empty()) |
1009 | 0 | { |
1010 | 0 | aosOptions.AddNameValue("HTTPAUTH", "BASIC"); |
1011 | 0 | aosOptions.AddNameValue("USERPWD", osUserPwd.c_str()); |
1012 | 0 | } |
1013 | | |
1014 | 313 | if (!osConnectTimeout.empty()) |
1015 | 0 | { |
1016 | 0 | aosOptions.AddNameValue("CONNECTTIMEOUT", osConnectTimeout.c_str()); |
1017 | 0 | } |
1018 | | |
1019 | 313 | if (!osTimeout.empty()) |
1020 | 0 | { |
1021 | 0 | aosOptions.AddNameValue("TIMEOUT", osTimeout.c_str()); |
1022 | 0 | } |
1023 | | |
1024 | 313 | if (!bSkipRetry) |
1025 | 313 | { |
1026 | 313 | if (!osRetryCount.empty()) |
1027 | 0 | { |
1028 | 0 | aosOptions.AddNameValue("MAX_RETRY", osRetryCount.c_str()); |
1029 | 0 | } |
1030 | 313 | if (!osRetryDelay.empty()) |
1031 | 0 | { |
1032 | 0 | aosOptions.AddNameValue("RETRY_DELAY", osRetryDelay.c_str()); |
1033 | 0 | } |
1034 | 313 | } |
1035 | 313 | return aosOptions; |
1036 | 313 | } |
1037 | | |
1038 | | /* |
1039 | | * SQLUnescape() |
1040 | | * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want |
1041 | | * dependency on sqlite |
1042 | | */ |
1043 | | static CPLString SQLUnescape(const char *pszVal) |
1044 | 0 | { |
1045 | 0 | char chQuoteChar = pszVal[0]; |
1046 | 0 | if (chQuoteChar != '\'' && chQuoteChar != '"') |
1047 | 0 | return pszVal; |
1048 | | |
1049 | 0 | CPLString osRet; |
1050 | 0 | pszVal++; |
1051 | 0 | while (*pszVal != '\0') |
1052 | 0 | { |
1053 | 0 | if (*pszVal == chQuoteChar) |
1054 | 0 | { |
1055 | 0 | if (pszVal[1] == chQuoteChar) |
1056 | 0 | pszVal++; |
1057 | 0 | else |
1058 | 0 | break; |
1059 | 0 | } |
1060 | 0 | osRet += *pszVal; |
1061 | 0 | pszVal++; |
1062 | 0 | } |
1063 | 0 | return osRet; |
1064 | 0 | } |
1065 | | |
1066 | | /* |
1067 | | * SQLTokenize() |
1068 | | * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want |
1069 | | * dependency on sqlite |
1070 | | */ |
1071 | | static char **SQLTokenize(const char *pszStr) |
1072 | 0 | { |
1073 | 0 | char **papszTokens = nullptr; |
1074 | 0 | bool bInQuote = false; |
1075 | 0 | char chQuoteChar = '\0'; |
1076 | 0 | bool bInSpace = true; |
1077 | 0 | CPLString osCurrentToken; |
1078 | 0 | while (*pszStr != '\0') |
1079 | 0 | { |
1080 | 0 | if (*pszStr == ' ' && !bInQuote) |
1081 | 0 | { |
1082 | 0 | if (!bInSpace) |
1083 | 0 | { |
1084 | 0 | papszTokens = CSLAddString(papszTokens, osCurrentToken); |
1085 | 0 | osCurrentToken.clear(); |
1086 | 0 | } |
1087 | 0 | bInSpace = true; |
1088 | 0 | } |
1089 | 0 | else if ((*pszStr == '(' || *pszStr == ')' || *pszStr == ',') && |
1090 | 0 | !bInQuote) |
1091 | 0 | { |
1092 | 0 | if (!bInSpace) |
1093 | 0 | { |
1094 | 0 | papszTokens = CSLAddString(papszTokens, osCurrentToken); |
1095 | 0 | osCurrentToken.clear(); |
1096 | 0 | } |
1097 | 0 | osCurrentToken.clear(); |
1098 | 0 | osCurrentToken += *pszStr; |
1099 | 0 | papszTokens = CSLAddString(papszTokens, osCurrentToken); |
1100 | 0 | osCurrentToken.clear(); |
1101 | 0 | bInSpace = true; |
1102 | 0 | } |
1103 | 0 | else if (*pszStr == '"' || *pszStr == '\'') |
1104 | 0 | { |
1105 | 0 | if (bInQuote && *pszStr == chQuoteChar && pszStr[1] == chQuoteChar) |
1106 | 0 | { |
1107 | 0 | osCurrentToken += *pszStr; |
1108 | 0 | osCurrentToken += *pszStr; |
1109 | 0 | pszStr += 2; |
1110 | 0 | continue; |
1111 | 0 | } |
1112 | 0 | else if (bInQuote && *pszStr == chQuoteChar) |
1113 | 0 | { |
1114 | 0 | osCurrentToken += *pszStr; |
1115 | 0 | papszTokens = CSLAddString(papszTokens, osCurrentToken); |
1116 | 0 | osCurrentToken.clear(); |
1117 | 0 | bInSpace = true; |
1118 | 0 | bInQuote = false; |
1119 | 0 | chQuoteChar = '\0'; |
1120 | 0 | } |
1121 | 0 | else if (bInQuote) |
1122 | 0 | { |
1123 | 0 | osCurrentToken += *pszStr; |
1124 | 0 | } |
1125 | 0 | else |
1126 | 0 | { |
1127 | 0 | chQuoteChar = *pszStr; |
1128 | 0 | osCurrentToken.clear(); |
1129 | 0 | osCurrentToken += chQuoteChar; |
1130 | 0 | bInQuote = true; |
1131 | 0 | bInSpace = false; |
1132 | 0 | } |
1133 | 0 | } |
1134 | 0 | else |
1135 | 0 | { |
1136 | 0 | osCurrentToken += *pszStr; |
1137 | 0 | bInSpace = false; |
1138 | 0 | } |
1139 | 0 | pszStr++; |
1140 | 0 | } |
1141 | |
|
1142 | 0 | if (!osCurrentToken.empty()) |
1143 | 0 | papszTokens = CSLAddString(papszTokens, osCurrentToken); |
1144 | |
|
1145 | 0 | return papszTokens; |
1146 | 0 | } |
1147 | | |
1148 | | /* |
1149 | | * ExecuteSQL() |
1150 | | */ |
1151 | | OGRLayer *OGRNGWDataset::ExecuteSQL(const char *pszStatement, |
1152 | | OGRGeometry *poSpatialFilter, |
1153 | | const char *pszDialect) |
1154 | 0 | { |
1155 | | // Clean statement string. |
1156 | 0 | CPLString osStatement(pszStatement); |
1157 | 0 | osStatement = osStatement.Trim().replaceAll(" ", " "); |
1158 | |
|
1159 | 0 | if (STARTS_WITH_CI(osStatement, "DELLAYER:")) |
1160 | 0 | { |
1161 | 0 | CPLString osLayerName = osStatement.substr(strlen("DELLAYER:")); |
1162 | 0 | if (osLayerName.endsWith(";")) |
1163 | 0 | { |
1164 | 0 | osLayerName = osLayerName.substr(0, osLayerName.size() - 1); |
1165 | 0 | osLayerName.Trim(); |
1166 | 0 | } |
1167 | |
|
1168 | 0 | CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str()); |
1169 | |
|
1170 | 0 | for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer) |
1171 | 0 | { |
1172 | 0 | if (EQUAL(aoLayers[iLayer]->GetName(), osLayerName)) |
1173 | 0 | { |
1174 | 0 | DeleteLayer(iLayer); |
1175 | 0 | return nullptr; |
1176 | 0 | } |
1177 | 0 | } |
1178 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s", |
1179 | 0 | osLayerName.c_str()); |
1180 | |
|
1181 | 0 | return nullptr; |
1182 | 0 | } |
1183 | | |
1184 | 0 | if (STARTS_WITH_CI(osStatement, "DELETE FROM")) |
1185 | 0 | { |
1186 | 0 | osStatement = osStatement.substr(strlen("DELETE FROM ")); |
1187 | 0 | if (osStatement.endsWith(";")) |
1188 | 0 | { |
1189 | 0 | osStatement = osStatement.substr(0, osStatement.size() - 1); |
1190 | 0 | osStatement.Trim(); |
1191 | 0 | } |
1192 | |
|
1193 | 0 | std::size_t found = osStatement.find("WHERE"); |
1194 | 0 | CPLString osLayerName; |
1195 | 0 | if (found == std::string::npos) |
1196 | 0 | { // No where clause |
1197 | 0 | osLayerName = osStatement; |
1198 | 0 | osStatement.clear(); |
1199 | 0 | } |
1200 | 0 | else |
1201 | 0 | { |
1202 | 0 | osLayerName = osStatement.substr(0, found); |
1203 | 0 | osLayerName.Trim(); |
1204 | 0 | osStatement = osStatement.substr(found + strlen("WHERE ")); |
1205 | 0 | } |
1206 | |
|
1207 | 0 | OGRNGWLayer *poLayer = |
1208 | 0 | reinterpret_cast<OGRNGWLayer *>(GetLayerByName(osLayerName)); |
1209 | 0 | if (nullptr == poLayer) |
1210 | 0 | { |
1211 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1212 | 0 | "Layer %s not found in dataset.", osName.c_str()); |
1213 | 0 | return nullptr; |
1214 | 0 | } |
1215 | | |
1216 | 0 | if (osStatement.empty()) |
1217 | 0 | { |
1218 | 0 | poLayer->DeleteAllFeatures(); |
1219 | 0 | } |
1220 | 0 | else |
1221 | 0 | { |
1222 | 0 | CPLDebug("NGW", "Delete features with statement %s", |
1223 | 0 | osStatement.c_str()); |
1224 | 0 | OGRFeatureQuery oQuery; |
1225 | 0 | OGRErr eErr = oQuery.Compile(poLayer->GetLayerDefn(), osStatement); |
1226 | 0 | if (eErr != OGRERR_NONE) |
1227 | 0 | { |
1228 | 0 | return nullptr; |
1229 | 0 | } |
1230 | | |
1231 | | // Ignore all fields except first and ignore geometry |
1232 | 0 | auto poLayerDefn = poLayer->GetLayerDefn(); |
1233 | 0 | poLayerDefn->SetGeometryIgnored(TRUE); |
1234 | 0 | if (poLayerDefn->GetFieldCount() > 0) |
1235 | 0 | { |
1236 | 0 | std::set<std::string> osFields; |
1237 | 0 | OGRFieldDefn *poFieldDefn = poLayerDefn->GetFieldDefn(0); |
1238 | 0 | osFields.insert(poFieldDefn->GetNameRef()); |
1239 | 0 | poLayer->SetSelectedFields(osFields); |
1240 | 0 | } |
1241 | 0 | CPLString osNgwDelete = |
1242 | 0 | "NGW:" + |
1243 | 0 | OGRNGWLayer::TranslateSQLToFilter( |
1244 | 0 | reinterpret_cast<swq_expr_node *>(oQuery.GetSWQExpr())); |
1245 | |
|
1246 | 0 | poLayer->SetAttributeFilter(osNgwDelete); |
1247 | |
|
1248 | 0 | std::vector<GIntBig> aiFeaturesIDs; |
1249 | 0 | OGRFeature *poFeat; |
1250 | 0 | while ((poFeat = poLayer->GetNextFeature()) != nullptr) |
1251 | 0 | { |
1252 | 0 | aiFeaturesIDs.push_back(poFeat->GetFID()); |
1253 | 0 | OGRFeature::DestroyFeature(poFeat); |
1254 | 0 | } |
1255 | |
|
1256 | 0 | poLayer->DeleteFeatures(aiFeaturesIDs); |
1257 | | |
1258 | | // Reset all filters and ignores |
1259 | 0 | poLayerDefn->SetGeometryIgnored(FALSE); |
1260 | 0 | poLayer->SetAttributeFilter(nullptr); |
1261 | 0 | poLayer->SetIgnoredFields(nullptr); |
1262 | 0 | } |
1263 | 0 | return nullptr; |
1264 | 0 | } |
1265 | | |
1266 | 0 | if (STARTS_WITH_CI(osStatement, "DROP TABLE")) |
1267 | 0 | { |
1268 | | // Get layer name from pszStatement DELETE FROM layer;. |
1269 | 0 | CPLString osLayerName = osStatement.substr(strlen("DROP TABLE ")); |
1270 | 0 | if (osLayerName.endsWith(";")) |
1271 | 0 | { |
1272 | 0 | osLayerName = osLayerName.substr(0, osLayerName.size() - 1); |
1273 | 0 | osLayerName.Trim(); |
1274 | 0 | } |
1275 | |
|
1276 | 0 | CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str()); |
1277 | |
|
1278 | 0 | for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer) |
1279 | 0 | { |
1280 | 0 | if (EQUAL(aoLayers[iLayer]->GetName(), osLayerName)) |
1281 | 0 | { |
1282 | 0 | DeleteLayer(iLayer); |
1283 | 0 | return nullptr; |
1284 | 0 | } |
1285 | 0 | } |
1286 | | |
1287 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s", |
1288 | 0 | osLayerName.c_str()); |
1289 | |
|
1290 | 0 | return nullptr; |
1291 | 0 | } |
1292 | | |
1293 | 0 | if (STARTS_WITH_CI(osStatement, "ALTER TABLE ")) |
1294 | 0 | { |
1295 | 0 | if (osStatement.endsWith(";")) |
1296 | 0 | { |
1297 | 0 | osStatement = osStatement.substr(0, osStatement.size() - 1); |
1298 | 0 | osStatement.Trim(); |
1299 | 0 | } |
1300 | |
|
1301 | 0 | CPLStringList aosTokens(SQLTokenize(osStatement)); |
1302 | | /* ALTER TABLE src_table RENAME TO dst_table */ |
1303 | 0 | if (aosTokens.size() == 6 && EQUAL(aosTokens[3], "RENAME") && |
1304 | 0 | EQUAL(aosTokens[4], "TO")) |
1305 | 0 | { |
1306 | 0 | const char *pszSrcTableName = aosTokens[2]; |
1307 | 0 | const char *pszDstTableName = aosTokens[5]; |
1308 | |
|
1309 | 0 | OGRNGWLayer *poLayer = static_cast<OGRNGWLayer *>( |
1310 | 0 | GetLayerByName(SQLUnescape(pszSrcTableName))); |
1311 | 0 | if (poLayer) |
1312 | 0 | { |
1313 | 0 | poLayer->Rename(SQLUnescape(pszDstTableName)); |
1314 | 0 | return nullptr; |
1315 | 0 | } |
1316 | | |
1317 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s", |
1318 | 0 | pszSrcTableName); |
1319 | 0 | } |
1320 | 0 | else |
1321 | 0 | { |
1322 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1323 | 0 | "Unsupported alter table operation. Only rename table to " |
1324 | 0 | "... support."); |
1325 | 0 | } |
1326 | 0 | return nullptr; |
1327 | 0 | } |
1328 | | |
1329 | | // SELECT xxxxx FROM yyyy WHERE zzzzzz; |
1330 | 0 | if (STARTS_WITH_CI(osStatement, "SELECT ")) |
1331 | 0 | { |
1332 | 0 | swq_select oSelect; |
1333 | 0 | CPLDebug("NGW", "Select statement: %s", osStatement.c_str()); |
1334 | 0 | if (oSelect.preparse(osStatement) != CE_None) |
1335 | 0 | { |
1336 | 0 | return nullptr; |
1337 | 0 | } |
1338 | | |
1339 | 0 | if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr && |
1340 | 0 | oSelect.table_count == 1 && oSelect.order_specs == 0) |
1341 | 0 | { |
1342 | 0 | OGRNGWLayer *poLayer = reinterpret_cast<OGRNGWLayer *>( |
1343 | 0 | GetLayerByName(oSelect.table_defs[0].table_name)); |
1344 | 0 | if (nullptr == poLayer) |
1345 | 0 | { |
1346 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1347 | 0 | "Layer %s not found in dataset.", |
1348 | 0 | oSelect.table_defs[0].table_name); |
1349 | 0 | return nullptr; |
1350 | 0 | } |
1351 | | |
1352 | 0 | std::set<std::string> aosFields; |
1353 | 0 | bool bSkip = false; |
1354 | 0 | for (int i = 0; i < oSelect.result_columns(); ++i) |
1355 | 0 | { |
1356 | 0 | swq_col_func col_func = oSelect.column_defs[i].col_func; |
1357 | 0 | if (col_func != SWQCF_NONE) |
1358 | 0 | { |
1359 | 0 | bSkip = true; |
1360 | 0 | break; |
1361 | 0 | } |
1362 | | |
1363 | 0 | if (oSelect.column_defs[i].distinct_flag) |
1364 | 0 | { |
1365 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1366 | 0 | "Distinct not supported."); |
1367 | 0 | bSkip = true; |
1368 | 0 | break; |
1369 | 0 | } |
1370 | | |
1371 | 0 | if (oSelect.column_defs[i].field_name != nullptr) |
1372 | 0 | { |
1373 | 0 | if (EQUAL(oSelect.column_defs[i].field_name, "*")) |
1374 | 0 | { |
1375 | 0 | aosFields.clear(); |
1376 | 0 | aosFields.emplace(oSelect.column_defs[i].field_name); |
1377 | 0 | break; |
1378 | 0 | } |
1379 | 0 | else |
1380 | 0 | { |
1381 | 0 | aosFields.emplace(oSelect.column_defs[i].field_name); |
1382 | 0 | } |
1383 | 0 | } |
1384 | 0 | } |
1385 | |
|
1386 | 0 | std::string osNgwSelect; |
1387 | 0 | for (int iKey = 0; iKey < oSelect.order_specs; iKey++) |
1388 | 0 | { |
1389 | 0 | swq_order_def *psKeyDef = oSelect.order_defs + iKey; |
1390 | 0 | if (iKey > 0) |
1391 | 0 | { |
1392 | 0 | osNgwSelect += ","; |
1393 | 0 | } |
1394 | |
|
1395 | 0 | if (psKeyDef->ascending_flag == TRUE) |
1396 | 0 | { |
1397 | 0 | osNgwSelect += psKeyDef->field_name; |
1398 | 0 | } |
1399 | 0 | else |
1400 | 0 | { |
1401 | 0 | osNgwSelect += "-" + std::string(psKeyDef->field_name); |
1402 | 0 | } |
1403 | 0 | } |
1404 | |
|
1405 | 0 | if (oSelect.where_expr != nullptr) |
1406 | 0 | { |
1407 | 0 | if (!osNgwSelect.empty()) |
1408 | 0 | { |
1409 | 0 | osNgwSelect += "&"; |
1410 | 0 | } |
1411 | 0 | osNgwSelect += |
1412 | 0 | OGRNGWLayer::TranslateSQLToFilter(oSelect.where_expr); |
1413 | 0 | } |
1414 | |
|
1415 | 0 | if (osNgwSelect.empty()) |
1416 | 0 | { |
1417 | 0 | bSkip = true; |
1418 | 0 | } |
1419 | |
|
1420 | 0 | if (!bSkip) |
1421 | 0 | { |
1422 | 0 | if (aosFields.empty()) |
1423 | 0 | { |
1424 | 0 | CPLError( |
1425 | 0 | CE_Failure, CPLE_AppDefined, |
1426 | 0 | "SELECT statement is invalid: field list is empty."); |
1427 | 0 | return nullptr; |
1428 | 0 | } |
1429 | | |
1430 | 0 | if (poLayer->SyncToDisk() != OGRERR_NONE) |
1431 | 0 | { |
1432 | 0 | return nullptr; |
1433 | 0 | } |
1434 | | |
1435 | 0 | OGRNGWLayer *poOutLayer = poLayer->Clone(); |
1436 | 0 | if (aosFields.size() == 1 && *(aosFields.begin()) == "*") |
1437 | 0 | { |
1438 | 0 | poOutLayer->SetIgnoredFields(nullptr); |
1439 | 0 | } |
1440 | 0 | else |
1441 | 0 | { |
1442 | 0 | poOutLayer->SetSelectedFields(aosFields); |
1443 | 0 | } |
1444 | 0 | poOutLayer->SetSpatialFilter(poSpatialFilter); |
1445 | |
|
1446 | 0 | if (osNgwSelect |
1447 | 0 | .empty()) // If we here oSelect.where_expr is empty |
1448 | 0 | { |
1449 | 0 | poOutLayer->SetAttributeFilter(nullptr); |
1450 | 0 | } |
1451 | 0 | else |
1452 | 0 | { |
1453 | 0 | std::string osAttributeFilte = "NGW:" + osNgwSelect; |
1454 | 0 | poOutLayer->SetAttributeFilter(osAttributeFilte.c_str()); |
1455 | 0 | } |
1456 | 0 | return poOutLayer; |
1457 | 0 | } |
1458 | 0 | } |
1459 | 0 | } |
1460 | | |
1461 | 0 | return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter, pszDialect); |
1462 | 0 | } |
1463 | | |
1464 | | /* |
1465 | | * GetProjectionRef() |
1466 | | */ |
1467 | | const OGRSpatialReference *OGRNGWDataset::GetSpatialRef() const |
1468 | 0 | { |
1469 | 0 | if (poRasterDS != nullptr) |
1470 | 0 | { |
1471 | 0 | return poRasterDS->GetSpatialRef(); |
1472 | 0 | } |
1473 | 0 | return GDALDataset::GetSpatialRef(); |
1474 | 0 | } |
1475 | | |
1476 | | /* |
1477 | | * GetGeoTransform() |
1478 | | */ |
1479 | | CPLErr OGRNGWDataset::GetGeoTransform(GDALGeoTransform >) const |
1480 | 0 | { |
1481 | 0 | if (poRasterDS != nullptr) |
1482 | 0 | { |
1483 | 0 | return poRasterDS->GetGeoTransform(gt); |
1484 | 0 | } |
1485 | 0 | return GDALDataset::GetGeoTransform(gt); |
1486 | 0 | } |
1487 | | |
1488 | | /* |
1489 | | * IRasterIO() |
1490 | | */ |
1491 | | CPLErr OGRNGWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
1492 | | int nXSize, int nYSize, void *pData, |
1493 | | int nBufXSize, int nBufYSize, |
1494 | | GDALDataType eBufType, int nBandCount, |
1495 | | BANDMAP_TYPE panBandMap, GSpacing nPixelSpace, |
1496 | | GSpacing nLineSpace, GSpacing nBandSpace, |
1497 | | GDALRasterIOExtraArg *psExtraArg) |
1498 | 0 | { |
1499 | 0 | if (poRasterDS != nullptr) |
1500 | 0 | { |
1501 | 0 | if (stPixelExtent.IsInit()) |
1502 | 0 | { |
1503 | 0 | OGREnvelope stTestExtent; |
1504 | 0 | stTestExtent.MinX = static_cast<double>(nXOff); |
1505 | 0 | stTestExtent.MinY = static_cast<double>(nYOff); |
1506 | 0 | stTestExtent.MaxX = static_cast<double>(nXOff + nXSize); |
1507 | 0 | stTestExtent.MaxY = static_cast<double>(nYOff + nYSize); |
1508 | |
|
1509 | 0 | if (!stPixelExtent.Intersects(stTestExtent)) |
1510 | 0 | { |
1511 | 0 | CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f", |
1512 | 0 | stPixelExtent.MinX, stPixelExtent.MinY, |
1513 | 0 | stPixelExtent.MaxX, stPixelExtent.MaxY); |
1514 | 0 | CPLDebug("NGW", "RasterIO extent is: %f, %f, %f, %f", |
1515 | 0 | stTestExtent.MinX, stTestExtent.MinY, |
1516 | 0 | stTestExtent.MaxX, stTestExtent.MaxY); |
1517 | | |
1518 | | // Fill buffer transparent color. |
1519 | 0 | memset(pData, 0, |
1520 | 0 | static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount * |
1521 | 0 | GDALGetDataTypeSizeBytes(eBufType)); |
1522 | 0 | return CE_None; |
1523 | 0 | } |
1524 | 0 | } |
1525 | 0 | } |
1526 | 0 | return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, |
1527 | 0 | nBufXSize, nBufYSize, eBufType, nBandCount, |
1528 | 0 | panBandMap, nPixelSpace, nLineSpace, |
1529 | 0 | nBandSpace, psExtraArg); |
1530 | 0 | } |
1531 | | |
1532 | | /* |
1533 | | * FillCapabilities() |
1534 | | */ |
1535 | | void OGRNGWDataset::FillCapabilities(const CPLStringList &aosHTTPOptions) |
1536 | 0 | { |
1537 | | // Check NGW version. Paging available from 3.1 |
1538 | 0 | CPLJSONDocument oRouteReq; |
1539 | 0 | if (oRouteReq.LoadUrl(NGWAPI::GetVersionURL(osUrl), aosHTTPOptions)) |
1540 | 0 | { |
1541 | 0 | CPLJSONObject oRoot = oRouteReq.GetRoot(); |
1542 | |
|
1543 | 0 | if (oRoot.IsValid()) |
1544 | 0 | { |
1545 | 0 | std::string osVersion = oRoot.GetString("nextgisweb", "0.0"); |
1546 | 0 | bHasFeaturePaging = NGWAPI::CheckVersion(osVersion, 3, 1); |
1547 | |
|
1548 | 0 | CPLDebug("NGW", "Is feature paging supported: %s", |
1549 | 0 | bHasFeaturePaging ? "yes" : "no"); |
1550 | 0 | } |
1551 | 0 | } |
1552 | 0 | } |
1553 | | |
1554 | | /* |
1555 | | * Extensions() |
1556 | | */ |
1557 | | std::string OGRNGWDataset::Extensions() const |
1558 | 0 | { |
1559 | 0 | return osExtensions; |
1560 | 0 | } |
1561 | | |
1562 | | /* |
1563 | | * GetFieldDomainNames() |
1564 | | */ |
1565 | | std::vector<std::string> OGRNGWDataset::GetFieldDomainNames(CSLConstList) const |
1566 | 0 | { |
1567 | 0 | std::vector<std::string> oDomainNamesList; |
1568 | 0 | std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger, |
1569 | 0 | OFTInteger64}; |
1570 | 0 | for (auto const &oDom : moDomains) |
1571 | 0 | { |
1572 | 0 | for (auto eFieldType : aeFieldTypes) |
1573 | 0 | { |
1574 | 0 | auto pOgrDom = oDom.second.ToFieldDomain(eFieldType); |
1575 | 0 | if (pOgrDom != nullptr) |
1576 | 0 | { |
1577 | 0 | oDomainNamesList.emplace_back(pOgrDom->GetName()); |
1578 | 0 | } |
1579 | 0 | } |
1580 | 0 | } |
1581 | 0 | return oDomainNamesList; |
1582 | 0 | } |
1583 | | |
1584 | | /* |
1585 | | * GetFieldDomain() |
1586 | | */ |
1587 | | const OGRFieldDomain * |
1588 | | OGRNGWDataset::GetFieldDomain(const std::string &name) const |
1589 | 0 | { |
1590 | 0 | std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger, |
1591 | 0 | OFTInteger64}; |
1592 | 0 | for (auto const &oDom : moDomains) |
1593 | 0 | { |
1594 | 0 | for (auto eFieldType : aeFieldTypes) |
1595 | 0 | { |
1596 | 0 | auto pOgrDom = oDom.second.ToFieldDomain(eFieldType); |
1597 | 0 | if (pOgrDom != nullptr) |
1598 | 0 | { |
1599 | 0 | if (pOgrDom->GetName() == name) |
1600 | 0 | { |
1601 | 0 | return pOgrDom; |
1602 | 0 | } |
1603 | 0 | } |
1604 | 0 | } |
1605 | 0 | } |
1606 | 0 | return nullptr; |
1607 | 0 | } |
1608 | | |
1609 | | /* |
1610 | | * DeleteFieldDomain() |
1611 | | */ |
1612 | | bool OGRNGWDataset::DeleteFieldDomain(const std::string &name, |
1613 | | std::string &failureReason) |
1614 | 0 | { |
1615 | 0 | if (eAccess != GA_Update) |
1616 | 0 | { |
1617 | 0 | failureReason = |
1618 | 0 | "DeleteFieldDomain() not supported on read-only dataset"; |
1619 | 0 | return false; |
1620 | 0 | } |
1621 | | |
1622 | 0 | std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger, |
1623 | 0 | OFTInteger64}; |
1624 | 0 | for (auto const &oDom : moDomains) |
1625 | 0 | { |
1626 | 0 | for (auto eFieldType : aeFieldTypes) |
1627 | 0 | { |
1628 | 0 | auto pOgrDom = oDom.second.ToFieldDomain(eFieldType); |
1629 | 0 | if (pOgrDom != nullptr) |
1630 | 0 | { |
1631 | 0 | if (pOgrDom->GetName() == name) |
1632 | 0 | { |
1633 | 0 | auto nResourceID = oDom.second.GetID(); |
1634 | |
|
1635 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1636 | 0 | "Delete following domains with common " |
1637 | 0 | "identifier " CPL_FRMT_GIB ": %s.", |
1638 | 0 | nResourceID, |
1639 | 0 | oDom.second.GetDomainsNames().c_str()); |
1640 | |
|
1641 | 0 | auto result = NGWAPI::DeleteResource( |
1642 | 0 | GetUrl(), std::to_string(nResourceID), |
1643 | 0 | GetHeaders(false)); |
1644 | 0 | if (!result) |
1645 | 0 | { |
1646 | 0 | failureReason = CPLGetLastErrorMsg(); |
1647 | 0 | return result; |
1648 | 0 | } |
1649 | | |
1650 | 0 | moDomains.erase(nResourceID); |
1651 | | |
1652 | | // Remove domain from fields definitions |
1653 | 0 | for (const auto &oLayer : aoLayers) |
1654 | 0 | { |
1655 | 0 | for (int i = 0; |
1656 | 0 | i < oLayer->GetLayerDefn()->GetFieldCount(); ++i) |
1657 | 0 | { |
1658 | 0 | OGRFieldDefn *poFieldDefn = |
1659 | 0 | oLayer->GetLayerDefn()->GetFieldDefn(i); |
1660 | 0 | if (oDom.second.HasDomainName( |
1661 | 0 | poFieldDefn->GetDomainName())) |
1662 | 0 | { |
1663 | 0 | auto oTemporaryUnsealer( |
1664 | 0 | poFieldDefn->GetTemporaryUnsealer()); |
1665 | 0 | poFieldDefn->SetDomainName(std::string()); |
1666 | 0 | } |
1667 | 0 | } |
1668 | 0 | } |
1669 | 0 | return true; |
1670 | 0 | } |
1671 | 0 | } |
1672 | 0 | } |
1673 | 0 | } |
1674 | 0 | failureReason = "Domain does not exist"; |
1675 | 0 | return false; |
1676 | 0 | } |
1677 | | |
1678 | | /* |
1679 | | * CreateNGWLookupTableJson() |
1680 | | */ |
1681 | | static std::string CreateNGWLookupTableJson(const OGRCodedFieldDomain *pDomain, |
1682 | | GIntBig nResourceId) |
1683 | 0 | { |
1684 | 0 | CPLJSONObject oResourceJson; |
1685 | | // Add resource json item. |
1686 | 0 | CPLJSONObject oResource("resource", oResourceJson); |
1687 | 0 | oResource.Add("cls", "lookup_table"); |
1688 | 0 | CPLJSONObject oResourceParent("parent", oResource); |
1689 | 0 | oResourceParent.Add("id", nResourceId); |
1690 | 0 | oResource.Add("display_name", pDomain->GetName()); |
1691 | 0 | oResource.Add("description", pDomain->GetDescription()); |
1692 | | |
1693 | | // Add vector_layer json item. |
1694 | 0 | CPLJSONObject oLookupTable("lookup_table", oResourceJson); |
1695 | 0 | CPLJSONObject oLookupTableItems("items", oLookupTable); |
1696 | 0 | const auto enumeration = pDomain->GetEnumeration(); |
1697 | 0 | for (int i = 0; enumeration[i].pszCode != nullptr; ++i) |
1698 | 0 | { |
1699 | 0 | const char *pszValCurrent = ""; |
1700 | | // NGW not supported null as coded value, so set it as "" |
1701 | 0 | if (enumeration[i].pszValue != nullptr) |
1702 | 0 | { |
1703 | 0 | pszValCurrent = enumeration[i].pszValue; |
1704 | 0 | } |
1705 | 0 | oLookupTableItems.Add(enumeration[i].pszCode, pszValCurrent); |
1706 | 0 | } |
1707 | |
|
1708 | 0 | return oResourceJson.Format(CPLJSONObject::PrettyFormat::Plain); |
1709 | 0 | } |
1710 | | |
1711 | | /* |
1712 | | * AddFieldDomain() |
1713 | | */ |
1714 | | bool OGRNGWDataset::AddFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain, |
1715 | | std::string &failureReason) |
1716 | 0 | { |
1717 | 0 | const std::string domainName(domain->GetName()); |
1718 | 0 | if (eAccess != GA_Update) |
1719 | 0 | { |
1720 | 0 | failureReason = "Add field domain not supported on read-only dataset"; |
1721 | 0 | return false; |
1722 | 0 | } |
1723 | | |
1724 | 0 | if (GetFieldDomain(domainName) != nullptr) |
1725 | 0 | { |
1726 | 0 | failureReason = "A domain of identical name already exists"; |
1727 | 0 | return false; |
1728 | 0 | } |
1729 | | |
1730 | 0 | if (domain->GetDomainType() != OFDT_CODED) |
1731 | 0 | { |
1732 | 0 | failureReason = "Unsupported domain type"; |
1733 | 0 | return false; |
1734 | 0 | } |
1735 | | |
1736 | 0 | auto osPalyload = CreateNGWLookupTableJson( |
1737 | 0 | static_cast<OGRCodedFieldDomain *>(domain.get()), |
1738 | 0 | static_cast<GIntBig>(std::stol(osResourceId))); |
1739 | |
|
1740 | 0 | std::string osResourceIdInt = |
1741 | 0 | NGWAPI::CreateResource(osUrl, osPalyload, GetHeaders()); |
1742 | 0 | if (osResourceIdInt == "-1") |
1743 | 0 | { |
1744 | 0 | failureReason = CPLGetLastErrorMsg(); |
1745 | 0 | return false; |
1746 | 0 | } |
1747 | 0 | auto osNewResourceUrl = NGWAPI::GetResourceURL(osUrl, osResourceIdInt); |
1748 | 0 | CPLJSONDocument oResourceDetailsReq; |
1749 | 0 | bool bResult = |
1750 | 0 | oResourceDetailsReq.LoadUrl(osNewResourceUrl, GetHeaders(false)); |
1751 | 0 | if (!bResult) |
1752 | 0 | { |
1753 | 0 | failureReason = CPLGetLastErrorMsg(); |
1754 | 0 | return false; |
1755 | 0 | } |
1756 | | |
1757 | 0 | OGRNGWCodedFieldDomain oDomain(oResourceDetailsReq.GetRoot()); |
1758 | 0 | if (oDomain.GetID() == 0) |
1759 | 0 | { |
1760 | 0 | failureReason = "Failed to parse domain detailes from NGW"; |
1761 | 0 | return false; |
1762 | 0 | } |
1763 | 0 | moDomains[oDomain.GetID()] = std::move(oDomain); |
1764 | 0 | return true; |
1765 | 0 | } |
1766 | | |
1767 | | /* |
1768 | | * UpdateFieldDomain() |
1769 | | */ |
1770 | | bool OGRNGWDataset::UpdateFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain, |
1771 | | std::string &failureReason) |
1772 | 0 | { |
1773 | 0 | const std::string domainName(domain->GetName()); |
1774 | 0 | if (eAccess != GA_Update) |
1775 | 0 | { |
1776 | 0 | failureReason = "Add field domain not supported on read-only dataset"; |
1777 | 0 | return false; |
1778 | 0 | } |
1779 | | |
1780 | 0 | if (GetFieldDomain(domainName) == nullptr) |
1781 | 0 | { |
1782 | 0 | failureReason = "The domain should already exist to be updated"; |
1783 | 0 | return false; |
1784 | 0 | } |
1785 | | |
1786 | 0 | if (domain->GetDomainType() != OFDT_CODED) |
1787 | 0 | { |
1788 | 0 | failureReason = "Unsupported domain type"; |
1789 | 0 | return false; |
1790 | 0 | } |
1791 | | |
1792 | 0 | auto nResourceId = GetDomainIdByName(domainName); |
1793 | 0 | if (nResourceId == 0) |
1794 | 0 | { |
1795 | 0 | failureReason = "Failed get NGW domain identifier"; |
1796 | 0 | return false; |
1797 | 0 | } |
1798 | | |
1799 | 0 | auto osPayload = CreateNGWLookupTableJson( |
1800 | 0 | static_cast<const OGRCodedFieldDomain *>(domain.get()), |
1801 | 0 | static_cast<GIntBig>(std::stol(osResourceId))); |
1802 | |
|
1803 | 0 | if (!NGWAPI::UpdateResource(osUrl, osResourceId, osPayload, GetHeaders())) |
1804 | 0 | { |
1805 | 0 | failureReason = CPLGetLastErrorMsg(); |
1806 | 0 | return false; |
1807 | 0 | } |
1808 | | |
1809 | 0 | auto osNewResourceUrl = NGWAPI::GetResourceURL(osUrl, osResourceId); |
1810 | 0 | CPLJSONDocument oResourceDetailsReq; |
1811 | 0 | bool bResult = |
1812 | 0 | oResourceDetailsReq.LoadUrl(osNewResourceUrl, GetHeaders(false)); |
1813 | 0 | if (!bResult) |
1814 | 0 | { |
1815 | 0 | failureReason = CPLGetLastErrorMsg(); |
1816 | 0 | return false; |
1817 | 0 | } |
1818 | | |
1819 | 0 | OGRNGWCodedFieldDomain oDomain(oResourceDetailsReq.GetRoot()); |
1820 | 0 | if (oDomain.GetID() == 0) |
1821 | 0 | { |
1822 | 0 | failureReason = "Failed to parse domain detailes from NGW"; |
1823 | 0 | return false; |
1824 | 0 | } |
1825 | 0 | moDomains[oDomain.GetID()] = std::move(oDomain); |
1826 | 0 | return true; |
1827 | 0 | } |
1828 | | |
1829 | | /* |
1830 | | * GetDomainByID() |
1831 | | */ |
1832 | | OGRNGWCodedFieldDomain OGRNGWDataset::GetDomainByID(GIntBig id) const |
1833 | 0 | { |
1834 | 0 | auto pos = moDomains.find(id); |
1835 | 0 | if (pos == moDomains.end()) |
1836 | 0 | { |
1837 | 0 | return OGRNGWCodedFieldDomain(); |
1838 | 0 | } |
1839 | 0 | else |
1840 | 0 | { |
1841 | 0 | return pos->second; |
1842 | 0 | } |
1843 | 0 | } |
1844 | | |
1845 | | /* |
1846 | | * GetDomainIdByName() |
1847 | | */ |
1848 | | GIntBig OGRNGWDataset::GetDomainIdByName(const std::string &osDomainName) const |
1849 | 0 | { |
1850 | 0 | for (auto const &oDom : moDomains) |
1851 | 0 | { |
1852 | 0 | if (oDom.second.HasDomainName(osDomainName)) |
1853 | 0 | { |
1854 | 0 | return oDom.first; |
1855 | 0 | } |
1856 | 0 | } |
1857 | 0 | return 0L; |
1858 | 0 | } |