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-2025, NextGIS <info@nextgis.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : *******************************************************************************/
13 :
14 : #include "ogr_ngw.h"
15 :
16 : // #include <gdalsubdatasetinfo.h>
17 :
18 : /*
19 : * GetHeaders()
20 : */
21 0 : static CPLStringList GetHeaders(const std::string &osUserPwdIn = "",
22 : const std::string &osConnectTimeout = "",
23 : const std::string &osTimeout = "",
24 : const std::string &osRetryCount = "",
25 : const std::string &osRetryDelay = "")
26 : {
27 0 : CPLStringList aosHTTPOptions;
28 0 : aosHTTPOptions.AddString("HEADERS=Accept: */*");
29 0 : if (!osUserPwdIn.empty())
30 : {
31 0 : aosHTTPOptions.AddString("HTTPAUTH=BASIC");
32 0 : std::string osUserPwdOption("USERPWD=");
33 0 : osUserPwdOption += osUserPwdIn;
34 0 : aosHTTPOptions.AddString(osUserPwdOption.c_str());
35 : }
36 :
37 0 : if (!osConnectTimeout.empty())
38 : {
39 0 : aosHTTPOptions.AddNameValue("CONNECTTIMEOUT", osConnectTimeout.c_str());
40 : }
41 :
42 0 : if (!osTimeout.empty())
43 : {
44 0 : aosHTTPOptions.AddNameValue("TIMEOUT", osTimeout.c_str());
45 : }
46 :
47 0 : if (!osRetryCount.empty())
48 : {
49 0 : aosHTTPOptions.AddNameValue("MAX_RETRY", osRetryCount.c_str());
50 : }
51 0 : if (!osRetryDelay.empty())
52 : {
53 0 : aosHTTPOptions.AddNameValue("RETRY_DELAY", osRetryDelay.c_str());
54 : }
55 0 : return aosHTTPOptions;
56 : }
57 :
58 : /*
59 : * OGRNGWDriverIdentify()
60 : */
61 :
62 52357 : static int OGRNGWDriverIdentify(GDALOpenInfo *poOpenInfo)
63 : {
64 52357 : return STARTS_WITH_CI(poOpenInfo->pszFilename, "NGW:");
65 : }
66 :
67 : /*
68 : * OGRNGWDriverOpen()
69 : */
70 :
71 0 : static GDALDataset *OGRNGWDriverOpen(GDALOpenInfo *poOpenInfo)
72 : {
73 0 : if (OGRNGWDriverIdentify(poOpenInfo) == 0)
74 : {
75 0 : return nullptr;
76 : }
77 :
78 0 : OGRNGWDataset *poDS = new OGRNGWDataset();
79 0 : if (!poDS->Open(poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions,
80 0 : poOpenInfo->eAccess == GA_Update, poOpenInfo->nOpenFlags))
81 : {
82 0 : delete poDS;
83 0 : poDS = nullptr;
84 : }
85 :
86 0 : return poDS;
87 : }
88 :
89 : /*
90 : * OGRNGWDriverCreate()
91 : *
92 : * Add new datasource name at the end of URL:
93 : * NGW:http://some.nextgis.com/resource/0/new_name
94 : * NGW:http://some.nextgis.com:8000/test/resource/0/new_name
95 : */
96 :
97 : static GDALDataset *
98 32 : OGRNGWDriverCreate(const char *pszName, CPL_UNUSED int nBands,
99 : CPL_UNUSED int nXSize, CPL_UNUSED int nYSize,
100 : CPL_UNUSED GDALDataType eDT, char **papszOptions)
101 :
102 : {
103 96 : NGWAPI::Uri stUri = NGWAPI::ParseUri(pszName);
104 32 : CPLErrorReset();
105 32 : if (stUri.osPrefix != "NGW")
106 : {
107 32 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s", pszName);
108 32 : return nullptr;
109 : }
110 :
111 0 : CPLDebug("NGW", "Parse uri result. URL: %s, ID: %s, New name: %s",
112 : stUri.osAddress.c_str(), stUri.osResourceId.c_str(),
113 : stUri.osNewResourceName.c_str());
114 :
115 0 : std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", "");
116 0 : std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", "");
117 : std::string osUserPwd = CSLFetchNameValueDef(
118 0 : papszOptions, "USERPWD", CPLGetConfigOption("NGW_USERPWD", ""));
119 :
120 0 : CPLJSONObject oPayload;
121 0 : CPLJSONObject oResource("resource", oPayload);
122 0 : oResource.Add("cls", "resource_group");
123 0 : oResource.Add("display_name", stUri.osNewResourceName);
124 0 : if (!osKey.empty())
125 : {
126 0 : oResource.Add("keyname", osKey);
127 : }
128 :
129 0 : if (!osDesc.empty())
130 : {
131 0 : oResource.Add("description", osDesc);
132 : }
133 :
134 0 : CPLJSONObject oParent("parent", oResource);
135 0 : oParent.Add("id", atoi(stUri.osResourceId.c_str()));
136 :
137 : std::string osConnectTimeout =
138 : CSLFetchNameValueDef(papszOptions, "CONNECTTIMEOUT",
139 0 : CPLGetConfigOption("NGW_CONNECTTIMEOUT", ""));
140 : std::string osTimeout = CSLFetchNameValueDef(
141 0 : papszOptions, "TIMEOUT", CPLGetConfigOption("NGW_TIMEOUT", ""));
142 :
143 : std::string osNewResourceId = NGWAPI::CreateResource(
144 0 : stUri.osAddress, oPayload.Format(CPLJSONObject::PrettyFormat::Plain),
145 0 : GetHeaders(osUserPwd, osConnectTimeout, osTimeout));
146 0 : if (osNewResourceId == "-1")
147 : {
148 0 : return nullptr;
149 : }
150 :
151 0 : OGRNGWDataset *poDS = new OGRNGWDataset();
152 :
153 0 : if (!poDS->Open(stUri.osAddress, osNewResourceId, papszOptions, true,
154 : GDAL_OF_RASTER | GDAL_OF_VECTOR)) // TODO: GDAL_OF_GNM
155 : {
156 0 : delete poDS;
157 0 : poDS = nullptr;
158 : }
159 :
160 0 : return poDS;
161 : }
162 :
163 : /*
164 : * OGRNGWDriverDelete()
165 : */
166 0 : static CPLErr OGRNGWDriverDelete(const char *pszName)
167 : {
168 0 : NGWAPI::Uri stUri = NGWAPI::ParseUri(pszName);
169 0 : CPLErrorReset();
170 :
171 0 : if (stUri.osPrefix != "NGW")
172 : {
173 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s", pszName);
174 0 : return CE_Failure;
175 : }
176 :
177 0 : if (!stUri.osNewResourceName.empty())
178 : {
179 0 : CPLError(CE_Warning, CPLE_NotSupported,
180 : "Cannot delete new resource with name %s", pszName);
181 0 : return CE_Failure;
182 : }
183 :
184 0 : if (stUri.osResourceId == "0")
185 : {
186 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot delete resource 0");
187 0 : return CE_Failure;
188 : }
189 :
190 0 : std::string osUserPwd = CPLGetConfigOption("NGW_USERPWD", "");
191 0 : std::string osConnectTimeout = CPLGetConfigOption("NGW_CONNECTTIMEOUT", "");
192 0 : std::string osTimeout = CPLGetConfigOption("NGW_TIMEOUT", "");
193 0 : std::string osRetryCount = CPLGetConfigOption("NGW_MAX_RETRY", "");
194 0 : std::string osRetryDelay = CPLGetConfigOption("NGW_RETRY_DELAY", "");
195 :
196 : auto aosHTTPOptions = GetHeaders(osUserPwd, osConnectTimeout, osTimeout,
197 0 : osRetryCount, osRetryDelay);
198 :
199 0 : return NGWAPI::DeleteResource(stUri.osAddress, stUri.osResourceId,
200 : aosHTTPOptions)
201 0 : ? CE_None
202 0 : : CE_Failure;
203 : }
204 :
205 : /*
206 : * OGRNGWDriverRename()
207 : */
208 0 : static CPLErr OGRNGWDriverRename(const char *pszNewName, const char *pszOldName)
209 : {
210 0 : NGWAPI::Uri stUri = NGWAPI::ParseUri(pszOldName);
211 0 : CPLErrorReset();
212 0 : if (stUri.osPrefix != "NGW")
213 : {
214 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s",
215 : pszOldName);
216 0 : return CE_Failure;
217 : }
218 0 : CPLDebug("NGW", "Parse uri result. URL: %s, ID: %s, New name: %s",
219 : stUri.osAddress.c_str(), stUri.osResourceId.c_str(), pszNewName);
220 :
221 0 : std::string osUserPwd = CPLGetConfigOption("NGW_USERPWD", "");
222 0 : std::string osConnectTimeout = CPLGetConfigOption("NGW_CONNECTTIMEOUT", "");
223 0 : std::string osTimeout = CPLGetConfigOption("NGW_TIMEOUT", "");
224 0 : std::string osRetryCount = CPLGetConfigOption("NGW_MAX_RETRY", "");
225 0 : std::string osRetryDelay = CPLGetConfigOption("NGW_RETRY_DELAY", "");
226 :
227 : auto aosHTTPOptions = GetHeaders(osUserPwd, osConnectTimeout, osTimeout,
228 0 : osRetryCount, osRetryDelay);
229 :
230 0 : return NGWAPI::RenameResource(stUri.osAddress, stUri.osResourceId,
231 : pszNewName, aosHTTPOptions)
232 0 : ? CE_None
233 0 : : CE_Failure;
234 : }
235 :
236 : /*
237 : * OGRNGWDriverCreateCopy()
238 : */
239 18 : static GDALDataset *OGRNGWDriverCreateCopy(const char *pszFilename,
240 : GDALDataset *poSrcDS, int bStrict,
241 : char **papszOptions,
242 : GDALProgressFunc pfnProgress,
243 : void *pProgressData)
244 : {
245 : // Check destination dataset,
246 54 : NGWAPI::Uri stUri = NGWAPI::ParseUri(pszFilename);
247 18 : CPLErrorReset();
248 18 : if (stUri.osPrefix != "NGW")
249 : {
250 18 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s",
251 : pszFilename);
252 18 : return nullptr;
253 : }
254 :
255 0 : bool bCloseDS = false;
256 0 : std::string osFilename;
257 :
258 : // Check if source GDALDataset is tiff.
259 0 : if (EQUAL(poSrcDS->GetDriverName(), "GTiff") == FALSE)
260 : {
261 0 : GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
262 : // Compress to minimize network transfer.
263 0 : const char *apszOptions[] = {"COMPRESS=LZW", "NUM_THREADS=ALL_CPUS",
264 : nullptr};
265 0 : std::string osTempFilename = CPLGenerateTempFilenameSafe("ngw_tmp");
266 0 : osTempFilename += ".tif";
267 0 : GDALDataset *poTmpDS = poDriver->CreateCopy(
268 : osTempFilename.c_str(), poSrcDS, bStrict,
269 : const_cast<char **>(apszOptions), pfnProgress, pProgressData);
270 :
271 0 : if (poTmpDS != nullptr)
272 : {
273 0 : bCloseDS = true;
274 0 : osFilename = std::move(osTempFilename);
275 0 : poSrcDS = poTmpDS;
276 : }
277 : else
278 : {
279 0 : CPLError(CE_Failure, CPLE_NotSupported,
280 : "NGW driver doesn't support %s source raster.",
281 0 : poSrcDS->GetDriverName());
282 0 : return nullptr;
283 : }
284 : }
285 :
286 0 : if (osFilename.empty())
287 : {
288 : // Check if source tiff is local file.
289 0 : CPLStringList oaFiles(poSrcDS->GetFileList());
290 0 : for (int i = 0; i < oaFiles.size(); ++i)
291 : {
292 : // Check extension tif
293 0 : const std::string osExt = CPLGetExtensionSafe(oaFiles[i]);
294 0 : if (EQUALN(osExt.c_str(), "tif", 3))
295 : {
296 0 : osFilename = oaFiles[i];
297 0 : break;
298 : }
299 : }
300 : }
301 :
302 : // Check bands count.
303 0 : auto nBands = poSrcDS->GetRasterCount();
304 0 : auto nDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
305 :
306 0 : if (bCloseDS)
307 : {
308 0 : GDALClose((GDALDatasetH)poSrcDS);
309 : }
310 :
311 0 : std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", "");
312 0 : std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", "");
313 : std::string osUserPwd = CSLFetchNameValueDef(
314 0 : papszOptions, "USERPWD", CPLGetConfigOption("NGW_USERPWD", ""));
315 : std::string osStyleName =
316 0 : CSLFetchNameValueDef(papszOptions, "RASTER_STYLE_NAME", "");
317 :
318 : std::string osConnectTimeout =
319 : CSLFetchNameValueDef(papszOptions, "CONNECTTIMEOUT",
320 0 : CPLGetConfigOption("NGW_CONNECTTIMEOUT", ""));
321 : std::string osTimeout = CSLFetchNameValueDef(
322 0 : papszOptions, "TIMEOUT", CPLGetConfigOption("NGW_TIMEOUT", ""));
323 : std::string osRetryCount = CSLFetchNameValueDef(
324 0 : papszOptions, "MAX_RETRY", CPLGetConfigOption("NGW_MAX_RETRY", ""));
325 : std::string osRetryDelay = CSLFetchNameValueDef(
326 0 : papszOptions, "RETRY_DELAY", CPLGetConfigOption("NGW_RETRY_DELAY", ""));
327 :
328 : // Send file
329 : auto aosHTTPOptions = GetHeaders(osUserPwd, osConnectTimeout, osTimeout,
330 0 : osRetryCount, osRetryDelay);
331 : CPLJSONObject oFileJson =
332 : NGWAPI::UploadFile(stUri.osAddress, osFilename, aosHTTPOptions,
333 0 : pfnProgress, pProgressData);
334 :
335 0 : if (bCloseDS) // Delete temp tiff file.
336 : {
337 0 : VSIUnlink(osFilename.c_str());
338 : }
339 :
340 0 : if (!oFileJson.IsValid())
341 : {
342 0 : return nullptr;
343 : }
344 :
345 0 : CPLJSONArray oUploadMeta = oFileJson.GetArray("upload_meta");
346 0 : if (!oUploadMeta.IsValid() || oUploadMeta.Size() == 0)
347 : {
348 0 : CPLError(CE_Failure, CPLE_AppDefined, "Get unexpected response: %s.",
349 0 : oFileJson.Format(CPLJSONObject::PrettyFormat::Plain).c_str());
350 0 : return nullptr;
351 : }
352 :
353 : // Create raster layer
354 : // Create payload
355 0 : CPLJSONObject oPayloadRaster;
356 0 : CPLJSONObject oResource("resource", oPayloadRaster);
357 0 : oResource.Add("cls", "raster_layer");
358 0 : oResource.Add("display_name", stUri.osNewResourceName);
359 0 : if (!osKey.empty())
360 : {
361 0 : oResource.Add("keyname", osKey);
362 : }
363 :
364 0 : if (!osDesc.empty())
365 : {
366 0 : oResource.Add("description", osDesc);
367 : }
368 :
369 0 : CPLJSONObject oParent("parent", oResource);
370 0 : oParent.Add("id", atoi(stUri.osResourceId.c_str()));
371 :
372 0 : CPLJSONObject oRasterLayer("raster_layer", oPayloadRaster);
373 0 : oRasterLayer.Add("source", oUploadMeta[0]);
374 :
375 0 : CPLJSONObject oSrs("srs", oRasterLayer);
376 0 : oSrs.Add("id", 3857); // Now only Web Mercator supported.
377 :
378 : auto osRasterResourceId = NGWAPI::CreateResource(
379 : stUri.osAddress,
380 0 : oPayloadRaster.Format(CPLJSONObject::PrettyFormat::Plain),
381 0 : aosHTTPOptions);
382 0 : if (osRasterResourceId == "-1")
383 : {
384 0 : return nullptr;
385 : }
386 :
387 : // Create raster style
388 0 : CPLJSONObject oPayloadRasterStyle;
389 0 : CPLJSONObject oResourceStyle("resource", oPayloadRasterStyle);
390 :
391 : // NGW v3.1 supported different raster types: 1 band and 16/32 bit, RGB/RGBA
392 : // rasters and etc.
393 : // For RGB/RGBA rasters we can create default raster_style.
394 : // For other types - qml style file path is mandatory.
395 : std::string osQMLPath =
396 0 : CSLFetchNameValueDef(papszOptions, "RASTER_QML_PATH", "");
397 :
398 0 : bool bCreateStyle = true;
399 0 : if (osQMLPath.empty())
400 : {
401 0 : if ((nBands == 3 || nBands == 4) && nDataType == GDT_Byte)
402 : {
403 0 : oResourceStyle.Add("cls", "raster_style");
404 : }
405 : else
406 : {
407 0 : CPLError(CE_Warning, CPLE_NotSupported,
408 : "Default NGW raster style supports only 3 (RGB) or 4 "
409 : "(RGBA) and 8 "
410 : "bit byte bands. Raster has %d bands and data type %s",
411 : nBands, GDALGetDataTypeName(nDataType));
412 0 : bCreateStyle = false;
413 : }
414 : }
415 : else
416 : {
417 0 : oResourceStyle.Add("cls", "qgis_raster_style");
418 :
419 : // Upload QML file
420 : oFileJson =
421 0 : NGWAPI::UploadFile(stUri.osAddress, osQMLPath, aosHTTPOptions,
422 0 : pfnProgress, pProgressData);
423 0 : oUploadMeta = oFileJson.GetArray("upload_meta");
424 0 : if (!oUploadMeta.IsValid() || oUploadMeta.Size() == 0)
425 : {
426 0 : CPLError(
427 : CE_Failure, CPLE_AppDefined, "Get unexpected response: %s.",
428 0 : oFileJson.Format(CPLJSONObject::PrettyFormat::Plain).c_str());
429 0 : return nullptr;
430 : }
431 : CPLJSONObject oQGISRasterStyle("qgis_raster_style",
432 0 : oPayloadRasterStyle);
433 0 : oQGISRasterStyle.Add("file_upload", oUploadMeta[0]);
434 : }
435 :
436 0 : if (bCreateStyle)
437 : {
438 0 : if (osStyleName.empty())
439 : {
440 0 : osStyleName = stUri.osNewResourceName;
441 : }
442 0 : oResourceStyle.Add("display_name", osStyleName);
443 0 : CPLJSONObject oParentRaster("parent", oResourceStyle);
444 0 : oParentRaster.Add("id", atoi(osRasterResourceId.c_str()));
445 :
446 : auto osStyleResourceId = NGWAPI::CreateResource(
447 : stUri.osAddress,
448 0 : oPayloadRasterStyle.Format(CPLJSONObject::PrettyFormat::Plain),
449 0 : aosHTTPOptions);
450 0 : if (osStyleResourceId == "-1")
451 : {
452 0 : return nullptr;
453 : }
454 : }
455 :
456 0 : OGRNGWDataset *poDS = new OGRNGWDataset();
457 :
458 0 : if (!poDS->Open(stUri.osAddress, osRasterResourceId, papszOptions, true,
459 : GDAL_OF_RASTER))
460 : {
461 0 : delete poDS;
462 0 : poDS = nullptr;
463 : }
464 :
465 0 : return poDS;
466 : }
467 :
468 : /*
469 : * RegisterOGRNGW()
470 : */
471 :
472 1686 : void RegisterOGRNGW()
473 : {
474 1686 : if (GDALGetDriverByName("NGW") != nullptr)
475 : {
476 302 : return;
477 : }
478 :
479 1384 : GDALDriver *poDriver = new GDALDriver();
480 :
481 1384 : poDriver->SetDescription("NGW");
482 1384 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "NextGIS Web");
483 1384 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
484 1384 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
485 1384 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
486 1384 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_LAYER, "YES");
487 1384 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
488 1384 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
489 1384 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
490 1384 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/ngw.html");
491 1384 : poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "NGW:");
492 1384 : poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
493 1384 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS,
494 1384 : "NATIVE OGRSQL SQLITE");
495 :
496 1384 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
497 1384 : poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
498 1384 : "Name AlternativeName Domain");
499 1384 : poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
500 1384 : "AlternativeName Domain");
501 1384 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
502 1384 : poDriver->SetMetadataItem(GDAL_DCAP_FIELD_DOMAINS, "YES");
503 :
504 1384 : poDriver->SetMetadataItem(
505 : GDAL_DMD_OPENOPTIONLIST,
506 : "<OpenOptionList>"
507 : " <Option name='USERPWD' scope='raster,vector' type='string' "
508 : "description='Username and password, separated by colon'/>"
509 : " <Option name='PAGE_SIZE' scope='vector' type='integer' "
510 : "description='Limit feature count while fetching from server. Default "
511 : "value is -1 - no limit' default='-1'/>"
512 : " <Option name='BATCH_SIZE' scope='vector' type='integer' "
513 : "description='Size of feature insert and update operations cache "
514 : "before send to server. If batch size is -1 batch mode is disabled' "
515 : "default='-1'/>"
516 : " <Option name='NATIVE_DATA' scope='vector' type='boolean' "
517 : "description='Whether to store the native Json representation of "
518 : "extensions key. If EXTENSIONS not set or empty, NATIVE_DATA defaults "
519 : "to NO' default='NO'/>"
520 : " <Option name='CACHE_EXPIRES' scope='raster' type='integer' "
521 : "description='Time in seconds cached files will stay valid. If cached "
522 : "file expires it is deleted when maximum size of cache is reached. "
523 : "Also expired file can be overwritten by the new one from web' "
524 : "default='604800'/>"
525 : " <Option name='CACHE_MAX_SIZE' scope='raster' type='integer' "
526 : "description='The cache maximum size in bytes. If cache reached "
527 : "maximum size, expired cached files will be deleted' "
528 : "default='67108864'/>"
529 : " <Option name='JSON_DEPTH' scope='raster,vector' type='integer' "
530 : "description='The depth of json response that can be parsed. If depth "
531 : "is greater than this value, parse error occurs' default='32'/>"
532 : " <Option name='EXTENSIONS' scope='vector' type='string' "
533 : "description='Comma separated extensions list. Available are "
534 : "description and attachment' default=''/>"
535 : " <Option name='CONNECTTIMEOUT' scope='raster,vector' type='integer' "
536 : "description='Maximum delay for the connection to be established "
537 : "before "
538 : "being aborted in seconds'/>"
539 : " <Option name='TIMEOUT' scope='raster,vector' type='integer' "
540 : "description='Maximum delay for the whole request to complete before "
541 : "being aborted in seconds'/>"
542 : " <Option name='MAX_RETRY' scope='raster,vector' type='integer' "
543 : "description='Maximum number of retry attempts if a 429, 502, 503 or "
544 : "504 "
545 : "HTTP error occurs'/>"
546 : " <Option name='RETRY_DELAY' scope='raster,vector' type='integer' "
547 : "description='Number of seconds between retry attempts'/>"
548 1384 : "</OpenOptionList>");
549 :
550 1384 : poDriver->SetMetadataItem(
551 : GDAL_DMD_CREATIONOPTIONLIST,
552 : "<CreationOptionList>"
553 : " <Option name='KEY' scope='raster,vector' type='string' "
554 : "description='Key value. Must be unique in whole NextGIS Web "
555 : "instance'/>"
556 : " <Option name='DESCRIPTION' scope='raster,vector' type='string' "
557 : "description='Resource description'/>"
558 : " <Option name='RASTER_STYLE_NAME' scope='raster' type='string' "
559 : "description='Raster layer style name'/>"
560 : " <Option name='USERPWD' scope='raster,vector' type='string' "
561 : "description='Username and password, separated by colon'/>"
562 : " <Option name='PAGE_SIZE' scope='vector' type='integer' "
563 : "description='Limit feature count while fetching from server. Default "
564 : "value is -1 - no limit' default='-1'/>"
565 : " <Option name='BATCH_SIZE' scope='vector' type='integer' "
566 : "description='Size of feature insert and update operations cache "
567 : "before send to server. If batch size is -1 batch mode is disabled' "
568 : "default='-1'/>"
569 : " <Option name='NATIVE_DATA' scope='vector' type='boolean' "
570 : "description='Whether to store the native Json representation of "
571 : "extensions key. If EXTENSIONS not set or empty, NATIVE_DATA defaults "
572 : "to NO' default='NO'/>"
573 : " <Option name='CACHE_EXPIRES' scope='raster' type='integer' "
574 : "description='Time in seconds cached files will stay valid. If cached "
575 : "file expires it is deleted when maximum size of cache is reached. "
576 : "Also expired file can be overwritten by the new one from web' "
577 : "default='604800'/>"
578 : " <Option name='CACHE_MAX_SIZE' scope='raster' type='integer' "
579 : "description='The cache maximum size in bytes. If cache reached "
580 : "maximum size, expired cached files will be deleted' "
581 : "default='67108864'/>"
582 : " <Option name='JSON_DEPTH' scope='raster,vector' type='integer' "
583 : "description='The depth of json response that can be parsed. If depth "
584 : "is greater than this value, parse error occurs' default='32'/>"
585 : " <Option name='RASTER_QML_PATH' scope='raster' type='string' "
586 : "description='Raster QMS style path'/>"
587 : " <Option name='EXTENSIONS' scope='vector' type='string' "
588 : "description='Comma separated extensions list. Available are "
589 : "description and attachment' default=''/>"
590 : " <Option name='CONNECTTIMEOUT' scope='raster,vector' type='integer' "
591 : "description='Maximum delay for the connection to be established "
592 : "before "
593 : "being aborted in seconds'/>"
594 : " <Option name='TIMEOUT' scope='raster,vector' type='integer' "
595 : "description='Maximum delay for the whole request to complete before "
596 : "being aborted in seconds'/>"
597 : " <Option name='MAX_RETRY' scope='raster,vector' type='integer' "
598 : "description='Maximum number of retry attempts if a 429, 502, 503 or "
599 : "504 "
600 : "HTTP error occurs'/>"
601 : " <Option name='RETRY_DELAY' scope='raster,vector' type='integer' "
602 : "description='Number of seconds between retry attempts'/>"
603 1384 : "</CreationOptionList>");
604 :
605 1384 : poDriver->SetMetadataItem(
606 : GDAL_DS_LAYER_CREATIONOPTIONLIST,
607 : "<LayerCreationOptionList>"
608 : " <Option name='OVERWRITE' type='boolean' description='Whether to "
609 : "overwrite an existing table with the layer name to be created' "
610 : "default='NO'/>"
611 : " <Option name='KEY' type='string' description='Key value. Must be "
612 : "unique in whole NextGIS Web instance'/>"
613 : " <Option name='DESCRIPTION' type='string' description='Resource "
614 : "description'/>"
615 1384 : "</LayerCreationOptionList>");
616 :
617 1384 : poDriver->SetMetadataItem(
618 : GDAL_DMD_CREATIONFIELDDATATYPES,
619 1384 : "Integer Integer64 Real String Date DateTime Time");
620 1384 : poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
621 1384 : "AlternativeName");
622 1384 : poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES");
623 1384 : poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
624 1384 : poDriver->SetMetadataItem(GDAL_DCAP_RENAME_LAYERS, "YES");
625 1384 : poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES, "Coded");
626 :
627 1384 : poDriver->pfnOpen = OGRNGWDriverOpen;
628 1384 : poDriver->pfnIdentify = OGRNGWDriverIdentify;
629 1384 : poDriver->pfnCreate = OGRNGWDriverCreate;
630 1384 : poDriver->pfnCreateCopy = OGRNGWDriverCreateCopy;
631 1384 : poDriver->pfnDelete = OGRNGWDriverDelete;
632 1384 : poDriver->pfnRename = OGRNGWDriverRename;
633 :
634 1384 : GetGDALDriverManager()->RegisterDriver(poDriver);
635 : }
|