Line data Source code
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-2020, 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 <limits>
20 :
21 : class NGWWrapperRasterBand : public GDALProxyRasterBand
22 : {
23 : GDALRasterBand *poBaseBand;
24 :
25 : protected:
26 : virtual GDALRasterBand *
27 0 : RefUnderlyingRasterBand(bool /*bForceOpen*/) const override
28 : {
29 0 : return poBaseBand;
30 : }
31 :
32 : public:
33 0 : explicit NGWWrapperRasterBand(GDALRasterBand *poBaseBandIn)
34 0 : : poBaseBand(poBaseBandIn)
35 : {
36 0 : eDataType = poBaseBand->GetRasterDataType();
37 0 : poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
38 0 : }
39 :
40 0 : virtual ~NGWWrapperRasterBand()
41 0 : {
42 0 : }
43 : };
44 :
45 : /*
46 : * OGRNGWDataset()
47 : */
48 0 : OGRNGWDataset::OGRNGWDataset()
49 : : nBatchSize(-1), nPageSize(-1), bFetchedPermissions(false),
50 : bHasFeaturePaging(false), bExtInNativeData(false), bMetadataDerty(false),
51 : papoLayers(nullptr), nLayers(0), poRasterDS(nullptr), nRasters(0),
52 : nCacheExpires(604800), // 7 days
53 : nCacheMaxSize(67108864), // 64 MB
54 0 : osJsonDepth("32")
55 : {
56 0 : }
57 :
58 : /*
59 : * ~OGRNGWDataset()
60 : */
61 0 : OGRNGWDataset::~OGRNGWDataset()
62 : {
63 : // Last sync with server.
64 0 : OGRNGWDataset::FlushCache(true);
65 :
66 0 : if (poRasterDS != nullptr)
67 : {
68 0 : GDALClose(poRasterDS);
69 0 : poRasterDS = nullptr;
70 : }
71 :
72 0 : for (int i = 0; i < nLayers; ++i)
73 : {
74 0 : delete papoLayers[i];
75 : }
76 0 : CPLFree(papoLayers);
77 0 : }
78 :
79 : /*
80 : * FetchPermissions()
81 : */
82 0 : void OGRNGWDataset::FetchPermissions()
83 : {
84 0 : if (bFetchedPermissions)
85 : {
86 0 : return;
87 : }
88 :
89 0 : if (IsUpdateMode())
90 : {
91 : // Check connection and is it read only.
92 0 : char **papszHTTPOptions = GetHeaders();
93 : stPermissions = NGWAPI::CheckPermissions(
94 0 : osUrl, osResourceId, papszHTTPOptions, IsUpdateMode());
95 0 : CSLDestroy(papszHTTPOptions);
96 : }
97 : else
98 : {
99 0 : stPermissions.bDataCanRead = true;
100 0 : stPermissions.bResourceCanRead = true;
101 0 : stPermissions.bDatastructCanRead = true;
102 0 : stPermissions.bMetadataCanRead = true;
103 : }
104 0 : bFetchedPermissions = true;
105 : }
106 :
107 : /*
108 : * TestCapability()
109 : */
110 0 : int OGRNGWDataset::TestCapability(const char *pszCap)
111 : {
112 0 : FetchPermissions();
113 0 : if (EQUAL(pszCap, ODsCCreateLayer))
114 : {
115 0 : return stPermissions.bResourceCanCreate;
116 : }
117 0 : else if (EQUAL(pszCap, ODsCDeleteLayer))
118 : {
119 0 : return stPermissions.bResourceCanDelete;
120 : }
121 0 : else if (EQUAL(pszCap, "RenameLayer"))
122 : {
123 0 : return stPermissions.bResourceCanUpdate;
124 : }
125 0 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
126 : {
127 0 : return stPermissions.bDataCanWrite; // FIXME: Check on resource level
128 : // is this permission set?
129 : }
130 0 : else if (EQUAL(pszCap, ODsCRandomLayerRead))
131 : {
132 0 : return stPermissions.bDataCanRead;
133 : }
134 0 : else if (EQUAL(pszCap, ODsCZGeometries))
135 : {
136 0 : return TRUE;
137 : }
138 : else
139 : {
140 0 : return FALSE;
141 : }
142 : }
143 :
144 : /*
145 : * GetLayer()
146 : */
147 0 : OGRLayer *OGRNGWDataset::GetLayer(int iLayer)
148 : {
149 0 : if (iLayer < 0 || iLayer >= nLayers)
150 : {
151 0 : return nullptr;
152 : }
153 : else
154 : {
155 0 : return papoLayers[iLayer];
156 : }
157 : }
158 :
159 : /*
160 : * Open()
161 : */
162 0 : bool OGRNGWDataset::Open(const std::string &osUrlIn,
163 : const std::string &osResourceIdIn,
164 : char **papszOpenOptionsIn, bool bUpdateIn,
165 : int nOpenFlagsIn)
166 : {
167 0 : osUrl = osUrlIn;
168 0 : osResourceId = osResourceIdIn;
169 :
170 0 : eAccess = bUpdateIn ? GA_Update : GA_ReadOnly;
171 :
172 : osUserPwd = CSLFetchNameValueDef(papszOpenOptionsIn, "USERPWD",
173 0 : CPLGetConfigOption("NGW_USERPWD", ""));
174 :
175 0 : nBatchSize =
176 0 : atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "BATCH_SIZE",
177 : CPLGetConfigOption("NGW_BATCH_SIZE", "-1")));
178 :
179 0 : nPageSize =
180 0 : atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "PAGE_SIZE",
181 : CPLGetConfigOption("NGW_PAGE_SIZE", "-1")));
182 0 : if (nPageSize == 0)
183 : {
184 0 : nPageSize = -1;
185 : }
186 :
187 0 : nCacheExpires = atoi(CSLFetchNameValueDef(
188 : papszOpenOptionsIn, "CACHE_EXPIRES",
189 : CPLGetConfigOption("NGW_CACHE_EXPIRES", "604800")));
190 :
191 0 : nCacheMaxSize = atoi(CSLFetchNameValueDef(
192 : papszOpenOptionsIn, "CACHE_MAX_SIZE",
193 : CPLGetConfigOption("NGW_CACHE_MAX_SIZE", "67108864")));
194 :
195 0 : bExtInNativeData =
196 0 : CPLFetchBool(papszOpenOptionsIn, "NATIVE_DATA",
197 0 : CPLTestBool(CPLGetConfigOption("NGW_NATIVE_DATA", "NO")));
198 :
199 : osJsonDepth =
200 : CSLFetchNameValueDef(papszOpenOptionsIn, "JSON_DEPTH",
201 0 : CPLGetConfigOption("NGW_JSON_DEPTH", "32"));
202 :
203 : osExtensions =
204 : CSLFetchNameValueDef(papszOpenOptionsIn, "EXTENSIONS",
205 0 : CPLGetConfigOption("NGW_EXTENSIONS", ""));
206 :
207 0 : if (osExtensions.empty())
208 : {
209 0 : bExtInNativeData = false;
210 : }
211 :
212 0 : return Init(nOpenFlagsIn);
213 : }
214 :
215 : /*
216 : * Open()
217 : *
218 : * The pszFilename templates:
219 : * - NGW:http://some.nextgis.com/resource/0
220 : * - NGW:http://some.nextgis.com:8000/test/resource/0
221 : */
222 0 : bool OGRNGWDataset::Open(const char *pszFilename, char **papszOpenOptionsIn,
223 : bool bUpdateIn, int nOpenFlagsIn)
224 : {
225 0 : NGWAPI::Uri stUri = NGWAPI::ParseUri(pszFilename);
226 :
227 0 : if (stUri.osPrefix != "NGW")
228 : {
229 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s",
230 : pszFilename);
231 0 : return false;
232 : }
233 :
234 0 : osUrl = stUri.osAddress;
235 0 : osResourceId = stUri.osResourceId;
236 :
237 0 : return Open(stUri.osAddress, stUri.osResourceId, papszOpenOptionsIn,
238 0 : bUpdateIn, nOpenFlagsIn);
239 : }
240 :
241 : /*
242 : * Init()
243 : */
244 0 : bool OGRNGWDataset::Init(int nOpenFlagsIn)
245 : {
246 : // NOTE: Skip check API version at that moment. We expected API v3.
247 :
248 : // Get resource details.
249 0 : CPLJSONDocument oResourceDetailsReq;
250 0 : char **papszHTTPOptions = GetHeaders();
251 0 : bool bResult = oResourceDetailsReq.LoadUrl(
252 0 : NGWAPI::GetResource(osUrl, osResourceId), papszHTTPOptions);
253 :
254 0 : CPLDebug("NGW", "Get resource %s details %s", osResourceId.c_str(),
255 : bResult ? "success" : "failed");
256 :
257 0 : if (bResult)
258 : {
259 0 : CPLJSONObject oRoot = oResourceDetailsReq.GetRoot();
260 :
261 0 : if (oRoot.IsValid())
262 : {
263 0 : std::string osResourceType = oRoot.GetString("resource/cls");
264 0 : FillMetadata(oRoot);
265 :
266 0 : if (osResourceType == "resource_group")
267 : {
268 : // Check feature paging.
269 0 : FillCapabilities(papszHTTPOptions);
270 0 : if (oRoot.GetBool("resource/children", false))
271 : {
272 : // Get child resources.
273 0 : bResult = FillResources(papszHTTPOptions, nOpenFlagsIn);
274 : }
275 : }
276 0 : else if ((osResourceType == "vector_layer" ||
277 0 : osResourceType == "postgis_layer"))
278 : {
279 : // Check feature paging.
280 0 : FillCapabilities(papszHTTPOptions);
281 : // Add vector layer.
282 0 : AddLayer(oRoot, papszHTTPOptions, nOpenFlagsIn);
283 : }
284 0 : else if (osResourceType == "mapserver_style" ||
285 0 : osResourceType == "qgis_vector_style" ||
286 0 : osResourceType == "raster_style" ||
287 0 : osResourceType == "qgis_raster_style" ||
288 0 : osResourceType == "wmsclient_layer")
289 : {
290 : // GetExtent from parent.
291 0 : OGREnvelope stExtent;
292 0 : std::string osParentId = oRoot.GetString("resource/parent/id");
293 0 : bool bExtentResult = NGWAPI::GetExtent(
294 0 : osUrl, osParentId, papszHTTPOptions, 3857, stExtent);
295 :
296 0 : if (!bExtentResult)
297 : {
298 : // Set full extent for EPSG:3857.
299 0 : stExtent.MinX = -20037508.34;
300 0 : stExtent.MaxX = 20037508.34;
301 0 : stExtent.MinY = -20037508.34;
302 0 : stExtent.MaxY = 20037508.34;
303 : }
304 :
305 0 : CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f",
306 : stExtent.MinX, stExtent.MinY, stExtent.MaxX,
307 : stExtent.MaxY);
308 :
309 0 : int nEPSG = 3857;
310 : // Get parent details. We can skip this as default SRS in NGW is
311 : // 3857.
312 0 : if (osResourceType == "wmsclient_layer")
313 : {
314 0 : nEPSG = oRoot.GetInteger("wmsclient_layer/srs/id", nEPSG);
315 : }
316 : else
317 : {
318 0 : CPLJSONDocument oResourceReq;
319 0 : bResult = oResourceReq.LoadUrl(
320 0 : NGWAPI::GetResource(osUrl, osResourceId),
321 : papszHTTPOptions);
322 :
323 0 : if (bResult)
324 : {
325 0 : CPLJSONObject oParentRoot = oResourceReq.GetRoot();
326 0 : if (osResourceType == "mapserver_style" ||
327 0 : osResourceType == "qgis_vector_style")
328 : {
329 0 : nEPSG = oParentRoot.GetInteger(
330 : "vector_layer/srs/id", nEPSG);
331 : }
332 0 : else if (osResourceType == "raster_style" ||
333 0 : osResourceType == "qgis_raster_style")
334 : {
335 0 : nEPSG = oParentRoot.GetInteger(
336 : "raster_layer/srs/id", nEPSG);
337 : }
338 : }
339 : }
340 :
341 : // Create raster dataset.
342 0 : std::string osRasterUrl = NGWAPI::GetTMS(osUrl, osResourceId);
343 : char *pszRasterUrl =
344 0 : CPLEscapeString(osRasterUrl.c_str(), -1, CPLES_XML);
345 0 : const char *pszConnStr = CPLSPrintf(
346 : "<GDAL_WMS><Service name=\"TMS\">"
347 : "<ServerUrl>%s</ServerUrl></Service><DataWindow>"
348 : "<UpperLeftX>-20037508.34</"
349 : "UpperLeftX><UpperLeftY>20037508.34</UpperLeftY>"
350 : "<LowerRightX>20037508.34</"
351 : "LowerRightX><LowerRightY>-20037508.34</LowerRightY>"
352 : "<TileLevel>%d</TileLevel><TileCountX>1</TileCountX>"
353 : "<TileCountY>1</TileCountY><YOrigin>top</YOrigin></"
354 : "DataWindow>"
355 : "<Projection>EPSG:%d</Projection><BlockSizeX>256</"
356 : "BlockSizeX>"
357 : "<BlockSizeY>256</BlockSizeY><BandsCount>%d</BandsCount>"
358 : "<Cache><Type>file</Type><Expires>%d</Expires><MaxSize>%d</"
359 : "MaxSize>"
360 : "</Cache><ZeroBlockHttpCodes>204,404</ZeroBlockHttpCodes></"
361 : "GDAL_WMS>",
362 : pszRasterUrl,
363 : 22, // NOTE: We have no limit in zoom levels.
364 : nEPSG, // NOTE: Default SRS is EPSG:3857.
365 : 4, nCacheExpires, nCacheMaxSize);
366 :
367 0 : CPLFree(pszRasterUrl);
368 :
369 0 : poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
370 : pszConnStr,
371 : GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
372 : nullptr, nullptr, nullptr));
373 :
374 0 : if (poRasterDS)
375 : {
376 0 : bResult = true;
377 0 : nRasterXSize = poRasterDS->GetRasterXSize();
378 0 : nRasterYSize = poRasterDS->GetRasterYSize();
379 :
380 0 : for (int iBand = 1; iBand <= poRasterDS->GetRasterCount();
381 : iBand++)
382 : {
383 0 : SetBand(iBand, new NGWWrapperRasterBand(
384 0 : poRasterDS->GetRasterBand(iBand)));
385 : }
386 :
387 : // Set pixel limits.
388 0 : bool bHasTransform = false;
389 0 : double geoTransform[6] = {0.0};
390 0 : double invGeoTransform[6] = {0.0};
391 0 : if (poRasterDS->GetGeoTransform(geoTransform) == CE_None)
392 : {
393 0 : bHasTransform =
394 0 : GDALInvGeoTransform(geoTransform,
395 : invGeoTransform) == TRUE;
396 : }
397 :
398 0 : if (bHasTransform)
399 : {
400 0 : GDALApplyGeoTransform(
401 : invGeoTransform, stExtent.MinX, stExtent.MinY,
402 : &stPixelExtent.MinX, &stPixelExtent.MaxY);
403 :
404 0 : GDALApplyGeoTransform(
405 : invGeoTransform, stExtent.MaxX, stExtent.MaxY,
406 : &stPixelExtent.MaxX, &stPixelExtent.MinY);
407 :
408 0 : CPLDebug("NGW",
409 : "Raster extent in px is: %f, %f, %f, %f",
410 : stPixelExtent.MinX, stPixelExtent.MinY,
411 : stPixelExtent.MaxX, stPixelExtent.MaxY);
412 : }
413 : else
414 : {
415 0 : stPixelExtent.MinX = 0.0;
416 0 : stPixelExtent.MinY = 0.0;
417 0 : stPixelExtent.MaxX = std::numeric_limits<double>::max();
418 0 : stPixelExtent.MaxY = std::numeric_limits<double>::max();
419 : }
420 : }
421 : else
422 : {
423 0 : bResult = false;
424 : }
425 : }
426 0 : else if (osResourceType ==
427 : "raster_layer") // FIXME: Do we need this check? &&
428 : // nOpenFlagsIn & GDAL_OF_RASTER )
429 : {
430 0 : AddRaster(oRoot, papszHTTPOptions);
431 : }
432 : else
433 : {
434 0 : bResult = false;
435 : }
436 : // TODO: Add support for baselayers, webmap, wfsserver_service,
437 : // wmsserver_service.
438 : }
439 : }
440 :
441 0 : CSLDestroy(papszHTTPOptions);
442 0 : return bResult;
443 : }
444 :
445 : /*
446 : * FillResources()
447 : */
448 0 : bool OGRNGWDataset::FillResources(char **papszOptions, int nOpenFlagsIn)
449 : {
450 0 : CPLJSONDocument oResourceDetailsReq;
451 0 : bool bResult = oResourceDetailsReq.LoadUrl(
452 0 : NGWAPI::GetChildren(osUrl, osResourceId), papszOptions);
453 :
454 0 : if (bResult)
455 : {
456 0 : CPLJSONArray oChildren(oResourceDetailsReq.GetRoot());
457 0 : for (int i = 0; i < oChildren.Size(); ++i)
458 : {
459 0 : CPLJSONObject oChild = oChildren[i];
460 0 : std::string osResourceType = oChild.GetString("resource/cls");
461 0 : if ((osResourceType == "vector_layer" ||
462 0 : osResourceType == "postgis_layer"))
463 : {
464 : // Add vector layer. If failed, try next layer.
465 0 : AddLayer(oChild, papszOptions, nOpenFlagsIn);
466 : }
467 0 : else if ((osResourceType == "raster_layer" ||
468 0 : osResourceType == "wmsclient_layer") &&
469 0 : nOpenFlagsIn & GDAL_OF_RASTER)
470 : {
471 0 : AddRaster(oChild, papszOptions);
472 : }
473 : // TODO: Add support for baselayers, webmap, wfsserver_service,
474 : // wmsserver_service.
475 : }
476 : }
477 0 : return bResult;
478 : }
479 :
480 : /*
481 : * AddLayer()
482 : */
483 0 : void OGRNGWDataset::AddLayer(const CPLJSONObject &oResourceJsonObject,
484 : char **papszOptions, int nOpenFlagsIn)
485 : {
486 0 : std::string osLayerResourceId;
487 0 : if (nOpenFlagsIn & GDAL_OF_VECTOR)
488 : {
489 0 : OGRNGWLayer *poLayer = new OGRNGWLayer(this, oResourceJsonObject);
490 0 : papoLayers = (OGRNGWLayer **)CPLRealloc(
491 0 : papoLayers, (nLayers + 1) * sizeof(OGRNGWLayer *));
492 0 : papoLayers[nLayers++] = poLayer;
493 0 : osLayerResourceId = poLayer->GetResourceId();
494 : }
495 : else
496 : {
497 0 : osLayerResourceId = oResourceJsonObject.GetString("resource/id");
498 : }
499 :
500 : // Check styles exist and add them as rasters.
501 0 : if (nOpenFlagsIn & GDAL_OF_RASTER &&
502 0 : oResourceJsonObject.GetBool("resource/children", false))
503 : {
504 0 : CPLJSONDocument oResourceChildReq;
505 0 : bool bResult = oResourceChildReq.LoadUrl(
506 0 : NGWAPI::GetChildren(osUrl, osLayerResourceId), papszOptions);
507 :
508 0 : if (bResult)
509 : {
510 0 : CPLJSONArray oChildren(oResourceChildReq.GetRoot());
511 0 : for (int i = 0; i < oChildren.Size(); ++i)
512 : {
513 0 : AddRaster(oChildren[i], papszOptions);
514 : }
515 : }
516 : }
517 0 : }
518 :
519 : /*
520 : * AddRaster()
521 : */
522 0 : void OGRNGWDataset::AddRaster(const CPLJSONObject &oRasterJsonObj,
523 : char **papszOptions)
524 : {
525 0 : std::string osOutResourceId;
526 0 : std::string osOutResourceName;
527 0 : std::string osResourceType = oRasterJsonObj.GetString("resource/cls");
528 0 : if (osResourceType == "mapserver_style" ||
529 0 : osResourceType == "qgis_vector_style" ||
530 0 : osResourceType == "raster_style" ||
531 0 : osResourceType == "qgis_raster_style" ||
532 0 : osResourceType == "wmsclient_layer")
533 : {
534 0 : osOutResourceId = oRasterJsonObj.GetString("resource/id");
535 0 : osOutResourceName = oRasterJsonObj.GetString("resource/display_name");
536 : }
537 0 : else if (osResourceType == "raster_layer")
538 : {
539 : std::string osRasterResourceId =
540 0 : oRasterJsonObj.GetString("resource/id");
541 0 : CPLJSONDocument oResourceRequest;
542 0 : bool bResult = oResourceRequest.LoadUrl(
543 0 : NGWAPI::GetChildren(osUrl, osRasterResourceId), papszOptions);
544 :
545 0 : if (bResult)
546 : {
547 0 : CPLJSONArray oChildren(oResourceRequest.GetRoot());
548 0 : for (int i = 0; i < oChildren.Size(); ++i)
549 : {
550 0 : CPLJSONObject oChild = oChildren[i];
551 0 : osResourceType = oChild.GetString("resource/cls");
552 0 : if (osResourceType == "raster_style" ||
553 0 : osResourceType == "qgis_raster_style")
554 : {
555 0 : AddRaster(oChild, papszOptions);
556 : }
557 : }
558 : }
559 : }
560 :
561 0 : if (!osOutResourceId.empty())
562 : {
563 0 : if (osOutResourceName.empty())
564 : {
565 0 : osOutResourceName = "raster_" + osOutResourceId;
566 : }
567 :
568 0 : CPLDebug("NGW", "Add raster %s: %s", osOutResourceId.c_str(),
569 : osOutResourceName.c_str());
570 :
571 0 : GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_NAME", nRasters),
572 : CPLSPrintf("NGW:%s/resource/%s",
573 : osUrl.c_str(),
574 : osOutResourceId.c_str()),
575 : "SUBDATASETS");
576 0 : GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_DESC", nRasters),
577 : osOutResourceName.c_str(), "SUBDATASETS");
578 0 : nRasters++;
579 : }
580 0 : }
581 :
582 : /*
583 : * ICreateLayer
584 : */
585 0 : OGRLayer *OGRNGWDataset::ICreateLayer(const char *pszNameIn,
586 : const OGRGeomFieldDefn *poGeomFieldDefn,
587 : CSLConstList papszOptions)
588 : {
589 0 : if (!IsUpdateMode())
590 : {
591 0 : CPLError(CE_Failure, CPLE_AppDefined,
592 : "Operation not available in read-only mode");
593 0 : return nullptr;
594 : }
595 :
596 0 : const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
597 : const auto poSpatialRef =
598 0 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
599 :
600 : // Check permissions as we create new layer in memory and will create in
601 : // during SyncToDisk.
602 0 : FetchPermissions();
603 :
604 0 : if (!stPermissions.bResourceCanCreate)
605 : {
606 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
607 0 : return nullptr;
608 : }
609 :
610 : // Check input parameters.
611 0 : if ((eGType < wkbPoint || eGType > wkbMultiPolygon) &&
612 0 : (eGType < wkbPoint25D || eGType > wkbMultiPolygon25D))
613 : {
614 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported geometry type: %s",
615 : OGRGeometryTypeToName(eGType));
616 0 : return nullptr;
617 : }
618 :
619 0 : if (!poSpatialRef)
620 : {
621 0 : CPLError(CE_Failure, CPLE_AppDefined, "Undefined spatial reference");
622 0 : return nullptr;
623 : }
624 :
625 0 : OGRSpatialReference *poSRSClone = poSpatialRef->Clone();
626 0 : poSRSClone->AutoIdentifyEPSG();
627 0 : const char *pszEPSG = poSRSClone->GetAuthorityCode(nullptr);
628 0 : int nEPSG = -1;
629 0 : if (pszEPSG != nullptr)
630 : {
631 0 : nEPSG = atoi(pszEPSG);
632 : }
633 :
634 0 : if (nEPSG != 3857) // TODO: Check NextGIS Web supported SRS.
635 : {
636 0 : CPLError(CE_Failure, CPLE_AppDefined,
637 : "Unsupported spatial reference EPSG code: %d", nEPSG);
638 0 : poSRSClone->Release();
639 0 : return nullptr;
640 : }
641 :
642 : // Do we already have this layer? If so, should we blow it away?
643 0 : bool bOverwrite = CPLFetchBool(papszOptions, "OVERWRITE", false);
644 0 : for (int iLayer = 0; iLayer < nLayers; ++iLayer)
645 : {
646 0 : if (EQUAL(pszNameIn, papoLayers[iLayer]->GetName()))
647 : {
648 0 : if (bOverwrite)
649 : {
650 0 : DeleteLayer(iLayer);
651 0 : break;
652 : }
653 : else
654 : {
655 0 : CPLError(CE_Failure, CPLE_AppDefined,
656 : "Layer %s already exists, CreateLayer failed.\n"
657 : "Use the layer creation option OVERWRITE=YES to "
658 : "replace it.",
659 : pszNameIn);
660 0 : poSRSClone->Release();
661 0 : return nullptr;
662 : }
663 : }
664 : }
665 :
666 : // Create layer.
667 0 : std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", "");
668 0 : std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", "");
669 0 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
670 : OGRNGWLayer *poLayer =
671 0 : new OGRNGWLayer(this, pszNameIn, poSRSClone, eGType, osKey, osDesc);
672 0 : poSRSClone->Release();
673 0 : papoLayers = (OGRNGWLayer **)CPLRealloc(
674 0 : papoLayers, (nLayers + 1) * sizeof(OGRNGWLayer *));
675 0 : papoLayers[nLayers++] = poLayer;
676 0 : return poLayer;
677 : }
678 :
679 : /*
680 : * DeleteLayer()
681 : */
682 0 : OGRErr OGRNGWDataset::DeleteLayer(int iLayer)
683 : {
684 0 : if (!IsUpdateMode())
685 : {
686 0 : CPLError(CE_Failure, CPLE_AppDefined,
687 : "Operation not available in read-only mode.");
688 0 : return OGRERR_FAILURE;
689 : }
690 :
691 0 : if (iLayer < 0 || iLayer >= nLayers)
692 : {
693 0 : CPLError(CE_Failure, CPLE_AppDefined,
694 : "Layer %d not in legal range of 0 to %d.", iLayer,
695 0 : nLayers - 1);
696 0 : return OGRERR_FAILURE;
697 : }
698 :
699 0 : OGRNGWLayer *poLayer = static_cast<OGRNGWLayer *>(papoLayers[iLayer]);
700 :
701 0 : if (poLayer->GetResourceId() != "-1")
702 : {
703 : // For layers from server we can check permissions.
704 :
705 : // We can skip check permissions here as papoLayers[iLayer]->Delete()
706 : // will return false if no delete permission available.
707 0 : FetchPermissions();
708 :
709 0 : if (!stPermissions.bResourceCanDelete)
710 : {
711 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
712 0 : return OGRERR_FAILURE;
713 : }
714 : }
715 :
716 0 : if (poLayer->Delete())
717 : {
718 0 : delete poLayer;
719 0 : memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
720 0 : sizeof(void *) * (nLayers - iLayer - 1));
721 0 : nLayers--;
722 : }
723 :
724 0 : return OGRERR_NONE;
725 : }
726 :
727 : /*
728 : * FillMetadata()
729 : */
730 0 : void OGRNGWDataset::FillMetadata(const CPLJSONObject &oRootObject)
731 : {
732 0 : std::string osCreateDate = oRootObject.GetString("resource/creation_date");
733 0 : if (!osCreateDate.empty())
734 : {
735 0 : GDALDataset::SetMetadataItem("creation_date", osCreateDate.c_str());
736 : }
737 0 : osName = oRootObject.GetString("resource/display_name");
738 0 : SetDescription(osName.c_str());
739 0 : GDALDataset::SetMetadataItem("display_name", osName.c_str());
740 0 : std::string osDescription = oRootObject.GetString("resource/description");
741 0 : if (!osDescription.empty())
742 : {
743 0 : GDALDataset::SetMetadataItem("description", osDescription.c_str());
744 : }
745 0 : std::string osResourceType = oRootObject.GetString("resource/cls");
746 0 : if (!osResourceType.empty())
747 : {
748 0 : GDALDataset::SetMetadataItem("resource_type", osResourceType.c_str());
749 : }
750 : std::string osResourceParentId =
751 0 : oRootObject.GetString("resource/parent/id");
752 0 : if (!osResourceParentId.empty())
753 : {
754 0 : GDALDataset::SetMetadataItem("parent_id", osResourceParentId.c_str());
755 : }
756 0 : GDALDataset::SetMetadataItem("id", osResourceId.c_str());
757 :
758 : std::vector<CPLJSONObject> items =
759 0 : oRootObject.GetObj("resmeta/items").GetChildren();
760 :
761 0 : for (const CPLJSONObject &item : items)
762 : {
763 0 : std::string osSuffix = NGWAPI::GetResmetaSuffix(item.GetType());
764 0 : GDALDataset::SetMetadataItem((item.GetName() + osSuffix).c_str(),
765 0 : item.ToString().c_str(), "NGW");
766 : }
767 0 : }
768 :
769 : /*
770 : * FlushMetadata()
771 : */
772 0 : bool OGRNGWDataset::FlushMetadata(char **papszMetadata)
773 : {
774 0 : if (!bMetadataDerty)
775 : {
776 0 : return true;
777 : }
778 :
779 : bool bResult =
780 0 : NGWAPI::FlushMetadata(osUrl, osResourceId, papszMetadata, GetHeaders());
781 0 : if (bResult)
782 : {
783 0 : bMetadataDerty = false;
784 : }
785 :
786 0 : return bResult;
787 : }
788 :
789 : /*
790 : * SetMetadata()
791 : */
792 0 : CPLErr OGRNGWDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
793 : {
794 0 : FetchPermissions();
795 0 : if (!stPermissions.bMetadataCanWrite)
796 : {
797 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
798 0 : return CE_Failure;
799 : }
800 :
801 0 : CPLErr eResult = GDALDataset::SetMetadata(papszMetadata, pszDomain);
802 0 : if (eResult == CE_None && pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
803 : {
804 0 : eResult = FlushMetadata(papszMetadata) ? CE_None : CE_Failure;
805 : }
806 0 : return eResult;
807 : }
808 :
809 : /*
810 : * SetMetadataItem()
811 : */
812 0 : CPLErr OGRNGWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
813 : const char *pszDomain)
814 : {
815 0 : FetchPermissions();
816 0 : if (!stPermissions.bMetadataCanWrite)
817 : {
818 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
819 0 : return CE_Failure;
820 : }
821 0 : if (pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
822 : {
823 0 : bMetadataDerty = true;
824 : }
825 0 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
826 : }
827 :
828 : /*
829 : * FlushCache()
830 : */
831 0 : CPLErr OGRNGWDataset::FlushCache(bool bAtClosing)
832 : {
833 0 : CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
834 0 : if (!FlushMetadata(GetMetadata("NGW")))
835 0 : eErr = CE_Failure;
836 0 : return eErr;
837 : }
838 :
839 : /*
840 : * GetHeaders()
841 : */
842 0 : char **OGRNGWDataset::GetHeaders() const
843 : {
844 0 : char **papszOptions = nullptr;
845 0 : papszOptions = CSLAddString(papszOptions, "HEADERS=Accept: */*");
846 : papszOptions =
847 0 : CSLAddNameValue(papszOptions, "JSON_DEPTH", osJsonDepth.c_str());
848 0 : if (!osUserPwd.empty())
849 : {
850 0 : papszOptions = CSLAddString(papszOptions, "HTTPAUTH=BASIC");
851 0 : std::string osUserPwdOption("USERPWD=");
852 0 : osUserPwdOption += osUserPwd;
853 0 : papszOptions = CSLAddString(papszOptions, osUserPwdOption.c_str());
854 : }
855 0 : return papszOptions;
856 : }
857 :
858 : /*
859 : * SQLUnescape()
860 : * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
861 : * dependency on sqlite
862 : */
863 0 : static CPLString SQLUnescape(const char *pszVal)
864 : {
865 0 : char chQuoteChar = pszVal[0];
866 0 : if (chQuoteChar != '\'' && chQuoteChar != '"')
867 0 : return pszVal;
868 :
869 0 : CPLString osRet;
870 0 : pszVal++;
871 0 : while (*pszVal != '\0')
872 : {
873 0 : if (*pszVal == chQuoteChar)
874 : {
875 0 : if (pszVal[1] == chQuoteChar)
876 0 : pszVal++;
877 : else
878 0 : break;
879 : }
880 0 : osRet += *pszVal;
881 0 : pszVal++;
882 : }
883 0 : return osRet;
884 : }
885 :
886 : /*
887 : * SQLTokenize()
888 : * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
889 : * dependency on sqlite
890 : */
891 0 : static char **SQLTokenize(const char *pszStr)
892 : {
893 0 : char **papszTokens = nullptr;
894 0 : bool bInQuote = false;
895 0 : char chQuoteChar = '\0';
896 0 : bool bInSpace = true;
897 0 : CPLString osCurrentToken;
898 0 : while (*pszStr != '\0')
899 : {
900 0 : if (*pszStr == ' ' && !bInQuote)
901 : {
902 0 : if (!bInSpace)
903 : {
904 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
905 0 : osCurrentToken.clear();
906 : }
907 0 : bInSpace = true;
908 : }
909 0 : else if ((*pszStr == '(' || *pszStr == ')' || *pszStr == ',') &&
910 0 : !bInQuote)
911 : {
912 0 : if (!bInSpace)
913 : {
914 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
915 0 : osCurrentToken.clear();
916 : }
917 0 : osCurrentToken.clear();
918 0 : osCurrentToken += *pszStr;
919 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
920 0 : osCurrentToken.clear();
921 0 : bInSpace = true;
922 : }
923 0 : else if (*pszStr == '"' || *pszStr == '\'')
924 : {
925 0 : if (bInQuote && *pszStr == chQuoteChar && pszStr[1] == chQuoteChar)
926 : {
927 0 : osCurrentToken += *pszStr;
928 0 : osCurrentToken += *pszStr;
929 0 : pszStr += 2;
930 0 : continue;
931 : }
932 0 : else if (bInQuote && *pszStr == chQuoteChar)
933 : {
934 0 : osCurrentToken += *pszStr;
935 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
936 0 : osCurrentToken.clear();
937 0 : bInSpace = true;
938 0 : bInQuote = false;
939 0 : chQuoteChar = '\0';
940 : }
941 0 : else if (bInQuote)
942 : {
943 0 : osCurrentToken += *pszStr;
944 : }
945 : else
946 : {
947 0 : chQuoteChar = *pszStr;
948 0 : osCurrentToken.clear();
949 0 : osCurrentToken += chQuoteChar;
950 0 : bInQuote = true;
951 0 : bInSpace = false;
952 : }
953 : }
954 : else
955 : {
956 0 : osCurrentToken += *pszStr;
957 0 : bInSpace = false;
958 : }
959 0 : pszStr++;
960 : }
961 :
962 0 : if (!osCurrentToken.empty())
963 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
964 :
965 0 : return papszTokens;
966 : }
967 :
968 : /*
969 : * ExecuteSQL()
970 : */
971 0 : OGRLayer *OGRNGWDataset::ExecuteSQL(const char *pszStatement,
972 : OGRGeometry *poSpatialFilter,
973 : const char *pszDialect)
974 : {
975 : // Clean statement string.
976 0 : CPLString osStatement(pszStatement);
977 0 : osStatement = osStatement.Trim().replaceAll(" ", " ");
978 :
979 0 : if (STARTS_WITH_CI(osStatement, "DELLAYER:"))
980 : {
981 0 : CPLString osLayerName = osStatement.substr(strlen("DELLAYER:"));
982 0 : if (osLayerName.endsWith(";"))
983 : {
984 0 : osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
985 0 : osLayerName.Trim();
986 : }
987 :
988 0 : CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
989 :
990 0 : for (int iLayer = 0; iLayer < nLayers; ++iLayer)
991 : {
992 0 : if (EQUAL(papoLayers[iLayer]->GetName(), osLayerName))
993 : {
994 0 : DeleteLayer(iLayer);
995 0 : return nullptr;
996 : }
997 : }
998 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
999 : osLayerName.c_str());
1000 :
1001 0 : return nullptr;
1002 : }
1003 :
1004 0 : if (STARTS_WITH_CI(osStatement, "DELETE FROM"))
1005 : {
1006 : // Get layer name from pszStatement DELETE FROM layer;.
1007 0 : CPLString osLayerName = osStatement.substr(strlen("DELETE FROM "));
1008 0 : if (osLayerName.endsWith(";"))
1009 : {
1010 0 : osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
1011 0 : osLayerName.Trim();
1012 : }
1013 :
1014 0 : CPLDebug("NGW", "Delete features from layer with name %s.",
1015 : osLayerName.c_str());
1016 :
1017 : OGRNGWLayer *poLayer =
1018 0 : static_cast<OGRNGWLayer *>(GetLayerByName(osLayerName));
1019 0 : if (poLayer)
1020 : {
1021 0 : poLayer->DeleteAllFeatures();
1022 : }
1023 : else
1024 : {
1025 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
1026 : osLayerName.c_str());
1027 : }
1028 0 : return nullptr;
1029 : }
1030 :
1031 0 : if (STARTS_WITH_CI(osStatement, "DROP TABLE"))
1032 : {
1033 : // Get layer name from pszStatement DELETE FROM layer;.
1034 0 : CPLString osLayerName = osStatement.substr(strlen("DROP TABLE "));
1035 0 : if (osLayerName.endsWith(";"))
1036 : {
1037 0 : osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
1038 0 : osLayerName.Trim();
1039 : }
1040 :
1041 0 : CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
1042 :
1043 0 : for (int iLayer = 0; iLayer < nLayers; ++iLayer)
1044 : {
1045 0 : if (EQUAL(papoLayers[iLayer]->GetName(), osLayerName))
1046 : {
1047 0 : DeleteLayer(iLayer);
1048 0 : return nullptr;
1049 : }
1050 : }
1051 :
1052 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
1053 : osLayerName.c_str());
1054 :
1055 0 : return nullptr;
1056 : }
1057 :
1058 0 : if (STARTS_WITH_CI(osStatement, "ALTER TABLE "))
1059 : {
1060 0 : if (osStatement.endsWith(";"))
1061 : {
1062 0 : osStatement = osStatement.substr(0, osStatement.size() - 1);
1063 0 : osStatement.Trim();
1064 : }
1065 :
1066 0 : CPLStringList aosTokens(SQLTokenize(osStatement));
1067 : /* ALTER TABLE src_table RENAME TO dst_table */
1068 0 : if (aosTokens.size() == 6 && EQUAL(aosTokens[3], "RENAME") &&
1069 0 : EQUAL(aosTokens[4], "TO"))
1070 : {
1071 0 : const char *pszSrcTableName = aosTokens[2];
1072 0 : const char *pszDstTableName = aosTokens[5];
1073 :
1074 : OGRNGWLayer *poLayer = static_cast<OGRNGWLayer *>(
1075 0 : GetLayerByName(SQLUnescape(pszSrcTableName)));
1076 0 : if (poLayer)
1077 : {
1078 0 : poLayer->Rename(SQLUnescape(pszDstTableName));
1079 0 : return nullptr;
1080 : }
1081 :
1082 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
1083 : pszSrcTableName);
1084 : }
1085 : else
1086 : {
1087 0 : CPLError(CE_Failure, CPLE_AppDefined,
1088 : "Unsupported alter table operation. Only rename table to "
1089 : "... support.");
1090 : }
1091 0 : return nullptr;
1092 : }
1093 :
1094 : // SELECT xxxxx FROM yyyy WHERE zzzzzz;
1095 0 : if (STARTS_WITH_CI(osStatement, "SELECT "))
1096 : {
1097 0 : swq_select oSelect;
1098 0 : CPLDebug("NGW", "Select statement: %s", osStatement.c_str());
1099 0 : if (oSelect.preparse(osStatement) != CE_None)
1100 : {
1101 0 : return nullptr;
1102 : }
1103 :
1104 0 : if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
1105 0 : oSelect.table_count == 1 && oSelect.order_specs == 0)
1106 : {
1107 : OGRNGWLayer *poLayer = reinterpret_cast<OGRNGWLayer *>(
1108 0 : GetLayerByName(oSelect.table_defs[0].table_name));
1109 0 : if (nullptr == poLayer)
1110 : {
1111 0 : CPLError(CE_Failure, CPLE_AppDefined,
1112 : "Layer %s not found in dataset.",
1113 0 : oSelect.table_defs[0].table_name);
1114 0 : return nullptr;
1115 : }
1116 :
1117 0 : std::set<std::string> aosFields;
1118 0 : bool bSkip = false;
1119 0 : for (int i = 0; i < oSelect.result_columns(); ++i)
1120 : {
1121 0 : swq_col_func col_func = oSelect.column_defs[i].col_func;
1122 0 : if (col_func != SWQCF_NONE)
1123 : {
1124 0 : bSkip = true;
1125 0 : break;
1126 : }
1127 :
1128 0 : if (oSelect.column_defs[i].distinct_flag)
1129 : {
1130 0 : CPLError(CE_Warning, CPLE_AppDefined,
1131 : "Distinct not supported.");
1132 0 : bSkip = true;
1133 0 : break;
1134 : }
1135 :
1136 0 : if (oSelect.column_defs[i].field_name != nullptr)
1137 : {
1138 0 : if (EQUAL(oSelect.column_defs[i].field_name, "*"))
1139 : {
1140 0 : aosFields.clear();
1141 0 : aosFields.emplace(oSelect.column_defs[i].field_name);
1142 0 : break;
1143 : }
1144 : else
1145 : {
1146 0 : aosFields.emplace(oSelect.column_defs[i].field_name);
1147 : }
1148 : }
1149 : }
1150 :
1151 0 : std::string osNgwSelect;
1152 0 : for (int iKey = 0; iKey < oSelect.order_specs; iKey++)
1153 : {
1154 0 : swq_order_def *psKeyDef = oSelect.order_defs + iKey;
1155 0 : if (iKey > 0)
1156 : {
1157 0 : osNgwSelect += ",";
1158 : }
1159 :
1160 0 : if (psKeyDef->ascending_flag == TRUE)
1161 : {
1162 0 : osNgwSelect += psKeyDef->field_name;
1163 : }
1164 : else
1165 : {
1166 0 : osNgwSelect += "-" + std::string(psKeyDef->field_name);
1167 : }
1168 : }
1169 :
1170 0 : if (oSelect.where_expr != nullptr)
1171 : {
1172 0 : if (!osNgwSelect.empty())
1173 : {
1174 0 : osNgwSelect += "&";
1175 : }
1176 : osNgwSelect +=
1177 0 : OGRNGWLayer::TranslateSQLToFilter(oSelect.where_expr);
1178 : }
1179 :
1180 0 : if (osNgwSelect.empty())
1181 : {
1182 0 : bSkip = true;
1183 : }
1184 :
1185 0 : if (!bSkip)
1186 : {
1187 0 : if (aosFields.empty())
1188 : {
1189 0 : CPLError(
1190 : CE_Failure, CPLE_AppDefined,
1191 : "SELECT statement is invalid: field list is empty.");
1192 0 : return nullptr;
1193 : }
1194 :
1195 0 : if (poLayer->SyncToDisk() != OGRERR_NONE)
1196 : {
1197 0 : return nullptr;
1198 : }
1199 :
1200 0 : OGRNGWLayer *poOutLayer = poLayer->Clone();
1201 0 : if (aosFields.size() == 1 && *(aosFields.begin()) == "*")
1202 : {
1203 0 : poOutLayer->SetIgnoredFields(nullptr);
1204 : }
1205 : else
1206 : {
1207 0 : poOutLayer->SetSelectedFields(aosFields);
1208 : }
1209 0 : poOutLayer->SetSpatialFilter(poSpatialFilter);
1210 :
1211 0 : if (osNgwSelect
1212 0 : .empty()) // If we here oSelect.where_expr is empty
1213 : {
1214 0 : poOutLayer->SetAttributeFilter(nullptr);
1215 : }
1216 : else
1217 : {
1218 0 : std::string osAttributeFilte = "NGW:" + osNgwSelect;
1219 0 : poOutLayer->SetAttributeFilter(osAttributeFilte.c_str());
1220 : }
1221 0 : return poOutLayer;
1222 : }
1223 : }
1224 : }
1225 :
1226 0 : return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter, pszDialect);
1227 : }
1228 :
1229 : /*
1230 : * GetProjectionRef()
1231 : */
1232 0 : const OGRSpatialReference *OGRNGWDataset::GetSpatialRef() const
1233 : {
1234 0 : if (poRasterDS != nullptr)
1235 : {
1236 0 : return poRasterDS->GetSpatialRef();
1237 : }
1238 0 : return GDALDataset::GetSpatialRef();
1239 : }
1240 :
1241 : /*
1242 : * GetGeoTransform()
1243 : */
1244 0 : CPLErr OGRNGWDataset::GetGeoTransform(double *padfTransform)
1245 : {
1246 0 : if (poRasterDS != nullptr)
1247 : {
1248 0 : return poRasterDS->GetGeoTransform(padfTransform);
1249 : }
1250 0 : return GDALDataset::GetGeoTransform(padfTransform);
1251 : }
1252 :
1253 : /*
1254 : * IRasterIO()
1255 : */
1256 0 : CPLErr OGRNGWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1257 : int nXSize, int nYSize, void *pData,
1258 : int nBufXSize, int nBufYSize,
1259 : GDALDataType eBufType, int nBandCount,
1260 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1261 : GSpacing nLineSpace, GSpacing nBandSpace,
1262 : GDALRasterIOExtraArg *psExtraArg)
1263 : {
1264 0 : if (poRasterDS != nullptr)
1265 : {
1266 0 : if (stPixelExtent.IsInit())
1267 : {
1268 0 : OGREnvelope stTestExtent;
1269 0 : stTestExtent.MinX = static_cast<double>(nXOff);
1270 0 : stTestExtent.MinY = static_cast<double>(nYOff);
1271 0 : stTestExtent.MaxX = static_cast<double>(nXOff + nXSize);
1272 0 : stTestExtent.MaxY = static_cast<double>(nYOff + nYSize);
1273 :
1274 0 : if (!stPixelExtent.Intersects(stTestExtent))
1275 : {
1276 0 : CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
1277 : stPixelExtent.MinX, stPixelExtent.MinY,
1278 : stPixelExtent.MaxX, stPixelExtent.MaxY);
1279 0 : CPLDebug("NGW", "RasterIO extent is: %f, %f, %f, %f",
1280 : stTestExtent.MinX, stTestExtent.MinY,
1281 : stTestExtent.MaxX, stTestExtent.MaxY);
1282 :
1283 : // Fill buffer transparent color.
1284 0 : memset(pData, 0,
1285 0 : static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount *
1286 0 : GDALGetDataTypeSizeBytes(eBufType));
1287 0 : return CE_None;
1288 : }
1289 : }
1290 : }
1291 0 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1292 : nBufXSize, nBufYSize, eBufType, nBandCount,
1293 : panBandMap, nPixelSpace, nLineSpace,
1294 0 : nBandSpace, psExtraArg);
1295 : }
1296 :
1297 : /*
1298 : * FillCapabilities()
1299 : */
1300 0 : void OGRNGWDataset::FillCapabilities(char **papszOptions)
1301 : {
1302 : // Check NGW version. Paging available from 3.1
1303 0 : CPLJSONDocument oRouteReq;
1304 0 : if (oRouteReq.LoadUrl(NGWAPI::GetVersion(osUrl), papszOptions))
1305 : {
1306 0 : CPLJSONObject oRoot = oRouteReq.GetRoot();
1307 :
1308 0 : if (oRoot.IsValid())
1309 : {
1310 0 : std::string osVersion = oRoot.GetString("nextgisweb", "0.0");
1311 0 : bHasFeaturePaging = NGWAPI::CheckVersion(osVersion, 3, 1);
1312 :
1313 0 : CPLDebug("NGW", "Is feature paging supported: %s",
1314 0 : bHasFeaturePaging ? "yes" : "no");
1315 : }
1316 : }
1317 0 : }
1318 :
1319 : /*
1320 : * Extensions()
1321 : */
1322 0 : std::string OGRNGWDataset::Extensions() const
1323 : {
1324 0 : return osExtensions;
1325 : }
|