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