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