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
10 : *
11 : * SPDX-License-Identifier: MIT
12 : *******************************************************************************/
13 :
14 : #include "ogr_ngw.h"
15 :
16 : #include "cpl_http.h"
17 :
18 : #include <limits>
19 :
20 : namespace NGWAPI
21 : {
22 :
23 0 : std::string GetPermissions(const std::string &osUrl,
24 : const std::string &osResourceId)
25 : {
26 0 : return osUrl + "/api/resource/" + osResourceId + "/permission";
27 : }
28 :
29 0 : std::string GetResource(const std::string &osUrl,
30 : const std::string &osResourceId)
31 : {
32 0 : return osUrl + "/api/resource/" + osResourceId;
33 : }
34 :
35 0 : std::string GetChildren(const std::string &osUrl,
36 : const std::string &osResourceId)
37 : {
38 0 : return osUrl + "/api/resource/?parent=" + osResourceId;
39 : }
40 :
41 0 : std::string GetFeature(const std::string &osUrl,
42 : const std::string &osResourceId)
43 : {
44 0 : return osUrl + "/api/resource/" + osResourceId + "/feature/";
45 : }
46 :
47 0 : std::string GetTMS(const std::string &osUrl, const std::string &osResourceId)
48 : {
49 0 : return osUrl +
50 : "/api/component/render/"
51 : "tile?z=${z}&x=${x}&y=${y}&resource=" +
52 0 : osResourceId;
53 : }
54 :
55 : std::string
56 0 : GetFeaturePage(const std::string &osUrl, const std::string &osResourceId,
57 : GIntBig nStart, int nCount, const std::string &osFields,
58 : const std::string &osWhere, const std::string &osSpatialWhere,
59 : const std::string &osExtensions, bool IsGeometryIgnored)
60 : {
61 0 : std::string osFeatureUrl = GetFeature(osUrl, osResourceId);
62 0 : bool bParamAdd = false;
63 0 : if (nCount > 0)
64 : {
65 0 : osFeatureUrl += "?offset=" + std::to_string(nStart) +
66 0 : "&limit=" + std::to_string(nCount);
67 0 : bParamAdd = true;
68 : }
69 :
70 0 : if (!osFields.empty())
71 : {
72 0 : if (bParamAdd)
73 : {
74 0 : osFeatureUrl += "&fields=" + osFields;
75 : }
76 : else
77 : {
78 0 : osFeatureUrl += "?fields=" + osFields;
79 0 : bParamAdd = true;
80 : }
81 : }
82 :
83 0 : if (!osWhere.empty())
84 : {
85 0 : if (bParamAdd)
86 : {
87 0 : osFeatureUrl += "&" + osWhere;
88 : }
89 : else
90 : {
91 0 : osFeatureUrl += "?" + osWhere;
92 0 : bParamAdd = true;
93 : }
94 : }
95 :
96 0 : if (!osSpatialWhere.empty())
97 : {
98 0 : if (bParamAdd)
99 : {
100 0 : osFeatureUrl += "&intersects=" + osSpatialWhere;
101 : }
102 : else
103 : {
104 0 : osFeatureUrl += "?intersects=" + osSpatialWhere;
105 0 : bParamAdd = true;
106 : }
107 : }
108 :
109 0 : if (bParamAdd)
110 : {
111 0 : osFeatureUrl += "&extensions=" + osExtensions;
112 : }
113 : else
114 : {
115 0 : osFeatureUrl += "?extensions=" + osExtensions;
116 0 : bParamAdd = true;
117 : }
118 0 : CPL_IGNORE_RET_VAL(bParamAdd);
119 :
120 0 : if (IsGeometryIgnored)
121 : {
122 0 : osFeatureUrl += "&geom=no";
123 : }
124 :
125 0 : return osFeatureUrl;
126 : }
127 :
128 0 : std::string GetRoute(const std::string &osUrl)
129 : {
130 0 : return osUrl + "/api/component/pyramid/route";
131 : }
132 :
133 0 : std::string GetUpload(const std::string &osUrl)
134 : {
135 0 : return osUrl + "/api/component/file_upload/upload";
136 : }
137 :
138 0 : std::string GetVersion(const std::string &osUrl)
139 : {
140 0 : return osUrl + "/api/component/pyramid/pkg_version";
141 : }
142 :
143 0 : bool CheckVersion(const std::string &osVersion, int nMajor, int nMinor,
144 : int nPatch)
145 : {
146 0 : int nCurrentMajor(0);
147 0 : int nCurrentMinor(0);
148 0 : int nCurrentPatch(0);
149 :
150 0 : CPLStringList aosList(CSLTokenizeString2(osVersion.c_str(), ".", 0));
151 0 : if (aosList.size() > 2)
152 : {
153 0 : nCurrentMajor = atoi(aosList[0]);
154 0 : nCurrentMinor = atoi(aosList[1]);
155 0 : nCurrentPatch = atoi(aosList[2]);
156 : }
157 0 : else if (aosList.size() > 1)
158 : {
159 0 : nCurrentMajor = atoi(aosList[0]);
160 0 : nCurrentMinor = atoi(aosList[1]);
161 : }
162 0 : else if (aosList.size() > 0)
163 : {
164 0 : nCurrentMajor = atoi(aosList[0]);
165 : }
166 :
167 0 : return nCurrentMajor >= nMajor && nCurrentMinor >= nMinor &&
168 0 : nCurrentPatch >= nPatch;
169 : }
170 :
171 50 : Uri ParseUri(const std::string &osUrl)
172 : {
173 50 : Uri stOut;
174 50 : std::size_t nFound = osUrl.find(":");
175 50 : if (nFound == std::string::npos)
176 : {
177 50 : return stOut;
178 : }
179 :
180 0 : stOut.osPrefix = osUrl.substr(0, nFound);
181 0 : std::string osUrlInt = CPLString(osUrl.substr(nFound + 1)).tolower();
182 :
183 0 : nFound = osUrlInt.find("/resource/");
184 0 : if (nFound == std::string::npos)
185 : {
186 0 : return stOut;
187 : }
188 :
189 0 : stOut.osAddress = osUrlInt.substr(0, nFound);
190 :
191 : std::string osResourceId =
192 0 : CPLString(osUrlInt.substr(nFound + strlen("/resource/"))).Trim();
193 :
194 0 : nFound = osResourceId.find('/');
195 0 : if (nFound != std::string::npos)
196 : {
197 0 : stOut.osResourceId = osResourceId.substr(0, nFound);
198 0 : stOut.osNewResourceName = osResourceId.substr(nFound + 1);
199 : }
200 : else
201 : {
202 0 : stOut.osResourceId = std::move(osResourceId);
203 : }
204 :
205 0 : return stOut;
206 : }
207 :
208 0 : static void ReportError(const GByte *pabyData, int nDataLen)
209 : {
210 0 : CPLJSONDocument oResult;
211 0 : if (oResult.LoadMemory(pabyData, nDataLen))
212 : {
213 0 : CPLJSONObject oRoot = oResult.GetRoot();
214 0 : if (oRoot.IsValid())
215 : {
216 0 : std::string osErrorMessage = oRoot.GetString("message");
217 0 : if (!osErrorMessage.empty())
218 : {
219 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
220 : osErrorMessage.c_str());
221 0 : return;
222 : }
223 : }
224 : }
225 :
226 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected error occurred.");
227 : }
228 :
229 0 : std::string CreateResource(const std::string &osUrl,
230 : const std::string &osPayload,
231 : char **papszHTTPOptions)
232 : {
233 0 : CPLErrorReset();
234 0 : std::string osPayloadInt = "POSTFIELDS=" + osPayload;
235 :
236 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "CUSTOMREQUEST=POST");
237 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, osPayloadInt.c_str());
238 : papszHTTPOptions =
239 0 : CSLAddString(papszHTTPOptions,
240 : "HEADERS=Content-Type: application/json\r\nAccept: */*");
241 :
242 0 : CPLDebug("NGW", "CreateResource request payload: %s", osPayload.c_str());
243 :
244 0 : CPLJSONDocument oCreateReq;
245 0 : bool bResult = oCreateReq.LoadUrl(GetResource(osUrl, ""), papszHTTPOptions);
246 0 : CSLDestroy(papszHTTPOptions);
247 0 : std::string osResourceId("-1");
248 0 : CPLJSONObject oRoot = oCreateReq.GetRoot();
249 0 : if (oRoot.IsValid())
250 : {
251 0 : if (bResult)
252 : {
253 0 : osResourceId = oRoot.GetString("id", "-1");
254 : }
255 : else
256 : {
257 0 : std::string osErrorMessage = oRoot.GetString("message");
258 0 : if (!osErrorMessage.empty())
259 : {
260 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
261 : osErrorMessage.c_str());
262 : }
263 : }
264 : }
265 0 : return osResourceId;
266 : }
267 :
268 0 : bool UpdateResource(const std::string &osUrl, const std::string &osResourceId,
269 : const std::string &osPayload, char **papszHTTPOptions)
270 : {
271 0 : CPLErrorReset();
272 0 : std::string osPayloadInt = "POSTFIELDS=" + osPayload;
273 :
274 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "CUSTOMREQUEST=PUT");
275 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, osPayloadInt.c_str());
276 : papszHTTPOptions =
277 0 : CSLAddString(papszHTTPOptions,
278 : "HEADERS=Content-Type: application/json\r\nAccept: */*");
279 :
280 0 : CPLDebug("NGW", "UpdateResource request payload: %s", osPayload.c_str());
281 :
282 0 : CPLHTTPResult *psResult = CPLHTTPFetch(
283 0 : GetResource(osUrl, osResourceId).c_str(), papszHTTPOptions);
284 0 : CSLDestroy(papszHTTPOptions);
285 0 : bool bResult = false;
286 0 : if (psResult)
287 : {
288 0 : bResult = psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
289 :
290 : // Get error message.
291 0 : if (!bResult)
292 : {
293 0 : ReportError(psResult->pabyData, psResult->nDataLen);
294 : }
295 0 : CPLHTTPDestroyResult(psResult);
296 : }
297 : else
298 : {
299 0 : CPLError(CE_Failure, CPLE_AppDefined, "Update resource %s failed",
300 : osResourceId.c_str());
301 : }
302 0 : return bResult;
303 : }
304 :
305 0 : bool DeleteResource(const std::string &osUrl, const std::string &osResourceId,
306 : char **papszHTTPOptions)
307 : {
308 0 : CPLErrorReset();
309 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "CUSTOMREQUEST=DELETE");
310 0 : CPLHTTPResult *psResult = CPLHTTPFetch(
311 0 : GetResource(osUrl, osResourceId).c_str(), papszHTTPOptions);
312 0 : bool bResult = false;
313 0 : if (psResult)
314 : {
315 0 : bResult = psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
316 : // Get error message.
317 0 : if (!bResult)
318 : {
319 0 : ReportError(psResult->pabyData, psResult->nDataLen);
320 : }
321 0 : CPLHTTPDestroyResult(psResult);
322 : }
323 0 : CSLDestroy(papszHTTPOptions);
324 0 : return bResult;
325 : }
326 :
327 0 : bool RenameResource(const std::string &osUrl, const std::string &osResourceId,
328 : const std::string &osNewName, char **papszHTTPOptions)
329 : {
330 0 : CPLJSONObject oPayload;
331 0 : CPLJSONObject oResource("resource", oPayload);
332 0 : oResource.Add("display_name", osNewName);
333 0 : std::string osPayload = oPayload.Format(CPLJSONObject::PrettyFormat::Plain);
334 :
335 0 : return UpdateResource(osUrl, osResourceId, osPayload, papszHTTPOptions);
336 : }
337 :
338 0 : OGRwkbGeometryType NGWGeomTypeToOGRGeomType(const std::string &osGeomType)
339 : {
340 : // http://docs.nextgis.com/docs_ngweb_dev/doc/developer/vector_data_types.html#nextgisweb.feature_layer.interface.GEOM_TYPE
341 0 : if (osGeomType == "POINT")
342 0 : return wkbPoint;
343 0 : else if (osGeomType == "LINESTRING")
344 0 : return wkbLineString;
345 0 : else if (osGeomType == "POLYGON")
346 0 : return wkbPolygon;
347 0 : else if (osGeomType == "MULTIPOINT")
348 0 : return wkbMultiPoint;
349 0 : else if (osGeomType == "MULTILINESTRING")
350 0 : return wkbMultiLineString;
351 0 : else if (osGeomType == "MULTIPOLYGON")
352 0 : return wkbMultiPolygon;
353 0 : else if (osGeomType == "POINTZ")
354 0 : return wkbPoint25D;
355 0 : else if (osGeomType == "LINESTRINGZ")
356 0 : return wkbLineString25D;
357 0 : else if (osGeomType == "POLYGONZ")
358 0 : return wkbPolygon25D;
359 0 : else if (osGeomType == "MULTIPOINTZ")
360 0 : return wkbMultiPoint25D;
361 0 : else if (osGeomType == "MULTILINESTRINGZ")
362 0 : return wkbMultiLineString25D;
363 0 : else if (osGeomType == "MULTIPOLYGONZ")
364 0 : return wkbMultiPolygon25D;
365 : else
366 0 : return wkbUnknown;
367 : }
368 :
369 0 : std::string OGRGeomTypeToNGWGeomType(OGRwkbGeometryType eType)
370 : {
371 0 : switch (eType)
372 : { // Don't flatten
373 0 : case wkbPoint:
374 0 : return "POINT";
375 0 : case wkbLineString:
376 0 : return "LINESTRING";
377 0 : case wkbPolygon:
378 0 : return "POLYGON";
379 0 : case wkbMultiPoint:
380 0 : return "MULTIPOINT";
381 0 : case wkbMultiLineString:
382 0 : return "MULTILINESTRING";
383 0 : case wkbMultiPolygon:
384 0 : return "MULTIPOLYGON";
385 0 : case wkbPoint25D:
386 0 : return "POINTZ";
387 0 : case wkbLineString25D:
388 0 : return "LINESTRINGZ";
389 0 : case wkbPolygon25D:
390 0 : return "POLYGONZ";
391 0 : case wkbMultiPoint25D:
392 0 : return "MULTIPOINTZ";
393 0 : case wkbMultiLineString25D:
394 0 : return "MULTILINESTRINGZ";
395 0 : case wkbMultiPolygon25D:
396 0 : return "MULTIPOLYGONZ";
397 0 : default:
398 0 : return "";
399 : }
400 : }
401 :
402 0 : OGRFieldType NGWFieldTypeToOGRFieldType(const std::string &osFieldType)
403 : {
404 : // http://docs.nextgis.com/docs_ngweb_dev/doc/developer/vector_data_types.html#nextgisweb.feature_layer.interface.FIELD_TYPE
405 0 : if (osFieldType == "INTEGER")
406 0 : return OFTInteger;
407 0 : else if (osFieldType == "BIGINT")
408 0 : return OFTInteger64;
409 0 : else if (osFieldType == "REAL")
410 0 : return OFTReal;
411 0 : else if (osFieldType == "STRING")
412 0 : return OFTString;
413 0 : else if (osFieldType == "DATE")
414 0 : return OFTDate;
415 0 : else if (osFieldType == "TIME")
416 0 : return OFTTime;
417 0 : else if (osFieldType == "DATETIME")
418 0 : return OFTDateTime;
419 : else
420 0 : return OFTString;
421 : }
422 :
423 0 : std::string OGRFieldTypeToNGWFieldType(OGRFieldType eType)
424 : {
425 0 : switch (eType)
426 : {
427 0 : case OFTInteger:
428 0 : return "INTEGER";
429 0 : case OFTInteger64:
430 0 : return "BIGINT";
431 0 : case OFTReal:
432 0 : return "REAL";
433 0 : case OFTString:
434 0 : return "STRING";
435 0 : case OFTDate:
436 0 : return "DATE";
437 0 : case OFTTime:
438 0 : return "TIME";
439 0 : case OFTDateTime:
440 0 : return "DATETIME";
441 0 : default:
442 0 : return "STRING";
443 : }
444 : }
445 :
446 0 : Permissions CheckPermissions(const std::string &osUrl,
447 : const std::string &osResourceId,
448 : char **papszHTTPOptions, bool bReadWrite)
449 : {
450 0 : Permissions stOut;
451 0 : CPLErrorReset();
452 0 : CPLJSONDocument oPermissionReq;
453 0 : bool bResult = oPermissionReq.LoadUrl(GetPermissions(osUrl, osResourceId),
454 : papszHTTPOptions);
455 :
456 0 : CPLJSONObject oRoot = oPermissionReq.GetRoot();
457 0 : if (oRoot.IsValid())
458 : {
459 0 : if (bResult)
460 : {
461 0 : stOut.bResourceCanRead = oRoot.GetBool("resource/read", true);
462 0 : stOut.bResourceCanCreate =
463 0 : oRoot.GetBool("resource/create", bReadWrite);
464 0 : stOut.bResourceCanUpdate =
465 0 : oRoot.GetBool("resource/update", bReadWrite);
466 0 : stOut.bResourceCanDelete =
467 0 : oRoot.GetBool("resource/delete", bReadWrite);
468 :
469 0 : stOut.bDatastructCanRead = oRoot.GetBool("datastruct/read", true);
470 0 : stOut.bDatastructCanWrite =
471 0 : oRoot.GetBool("datastruct/write", bReadWrite);
472 :
473 0 : stOut.bDataCanRead = oRoot.GetBool("data/read", true);
474 0 : stOut.bDataCanWrite = oRoot.GetBool("data/write", bReadWrite);
475 :
476 0 : stOut.bMetadataCanRead = oRoot.GetBool("metadata/read", true);
477 0 : stOut.bMetadataCanWrite =
478 0 : oRoot.GetBool("metadata/write", bReadWrite);
479 : }
480 : else
481 : {
482 0 : std::string osErrorMessage = oRoot.GetString("message");
483 0 : if (osErrorMessage.empty())
484 : {
485 0 : osErrorMessage = "Get permissions failed";
486 : }
487 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMessage.c_str());
488 : }
489 : }
490 : else
491 : {
492 0 : CPLError(CE_Failure, CPLE_AppDefined, "Get permissions failed");
493 : }
494 :
495 0 : return stOut;
496 : }
497 :
498 0 : std::string GetFeatureCount(const std::string &osUrl,
499 : const std::string &osResourceId)
500 : {
501 0 : return osUrl + "/api/resource/" + osResourceId + "/feature_count";
502 : }
503 :
504 0 : std::string GetLayerExtent(const std::string &osUrl,
505 : const std::string &osResourceId)
506 : {
507 0 : return osUrl + "/api/resource/" + osResourceId + "/extent";
508 : }
509 :
510 0 : std::string GetResmetaSuffix(CPLJSONObject::Type eType)
511 : {
512 0 : switch (eType)
513 : {
514 0 : case CPLJSONObject::Type::Integer:
515 : case CPLJSONObject::Type::Long:
516 0 : return ".d";
517 0 : case CPLJSONObject::Type::Double:
518 0 : return ".f";
519 0 : default:
520 0 : return "";
521 : }
522 : }
523 :
524 0 : void FillResmeta(const CPLJSONObject &oRoot, char **papszMetadata)
525 : {
526 0 : CPLJSONObject oResMeta("resmeta", oRoot);
527 0 : CPLJSONObject oResMetaItems("items", oResMeta);
528 0 : CPLStringList oaMetadata(papszMetadata, FALSE);
529 0 : for (int i = 0; i < oaMetadata.size(); ++i)
530 : {
531 0 : std::string osItem = oaMetadata[i];
532 0 : size_t nPos = osItem.find("=");
533 0 : if (nPos != std::string::npos)
534 : {
535 0 : std::string osItemName = osItem.substr(0, nPos);
536 0 : CPLString osItemValue = osItem.substr(nPos + 1);
537 :
538 0 : if (osItemName.size() > 2)
539 : {
540 0 : size_t nSuffixPos = osItemName.size() - 2;
541 0 : std::string osSuffix = osItemName.substr(nSuffixPos);
542 0 : if (osSuffix == ".d")
543 : {
544 0 : GInt64 nVal = CPLAtoGIntBig(osItemValue.c_str());
545 0 : oResMetaItems.Add(osItemName.substr(0, nSuffixPos), nVal);
546 0 : continue;
547 : }
548 :
549 0 : if (osSuffix == ".f")
550 : {
551 0 : oResMetaItems.Add(osItemName.substr(0, nSuffixPos),
552 : CPLAtofM(osItemValue.c_str()));
553 0 : continue;
554 : }
555 : }
556 :
557 0 : oResMetaItems.Add(osItemName, osItemValue);
558 : }
559 : }
560 0 : }
561 :
562 0 : bool FlushMetadata(const std::string &osUrl, const std::string &osResourceId,
563 : char **papszMetadata, char **papszHTTPOptions)
564 : {
565 0 : if (nullptr == papszMetadata)
566 : {
567 0 : return true;
568 : }
569 0 : CPLJSONObject oMetadataJson;
570 0 : FillResmeta(oMetadataJson, papszMetadata);
571 :
572 0 : return UpdateResource(
573 : osUrl, osResourceId,
574 0 : oMetadataJson.Format(CPLJSONObject::PrettyFormat::Plain),
575 0 : papszHTTPOptions);
576 : }
577 :
578 0 : bool DeleteFeature(const std::string &osUrl, const std::string &osResourceId,
579 : const std::string &osFeatureId, char **papszHTTPOptions)
580 : {
581 0 : CPLErrorReset();
582 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "CUSTOMREQUEST=DELETE");
583 0 : std::string osUrlInt = GetFeature(osUrl, osResourceId) + osFeatureId;
584 0 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrlInt.c_str(), papszHTTPOptions);
585 0 : CSLDestroy(papszHTTPOptions);
586 0 : bool bResult = false;
587 0 : if (psResult)
588 : {
589 0 : bResult = psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
590 : // Get error message.
591 0 : if (!bResult)
592 : {
593 0 : ReportError(psResult->pabyData, psResult->nDataLen);
594 : }
595 0 : CPLHTTPDestroyResult(psResult);
596 : }
597 0 : return bResult;
598 : }
599 :
600 0 : GIntBig CreateFeature(const std::string &osUrl, const std::string &osResourceId,
601 : const std::string &osFeatureJson, char **papszHTTPOptions)
602 : {
603 0 : CPLErrorReset();
604 0 : std::string osPayloadInt = "POSTFIELDS=" + osFeatureJson;
605 :
606 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "CUSTOMREQUEST=POST");
607 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, osPayloadInt.c_str());
608 : papszHTTPOptions =
609 0 : CSLAddString(papszHTTPOptions,
610 : "HEADERS=Content-Type: application/json\r\nAccept: */*");
611 :
612 0 : CPLDebug("NGW", "CreateFeature request payload: %s", osFeatureJson.c_str());
613 :
614 0 : std::string osUrlInt = GetFeature(osUrl, osResourceId);
615 :
616 0 : CPLJSONDocument oCreateFeatureReq;
617 0 : bool bResult = oCreateFeatureReq.LoadUrl(osUrlInt, papszHTTPOptions);
618 0 : CSLDestroy(papszHTTPOptions);
619 :
620 0 : CPLJSONObject oRoot = oCreateFeatureReq.GetRoot();
621 0 : GIntBig nOutFID = OGRNullFID;
622 0 : if (oRoot.IsValid())
623 : {
624 0 : if (bResult)
625 : {
626 0 : nOutFID = oRoot.GetLong("id", OGRNullFID);
627 : }
628 : else
629 : {
630 0 : std::string osErrorMessage = oRoot.GetString("message");
631 0 : if (osErrorMessage.empty())
632 : {
633 0 : osErrorMessage = "Create new feature failed";
634 : }
635 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMessage.c_str());
636 : }
637 : }
638 : else
639 : {
640 0 : CPLError(CE_Failure, CPLE_AppDefined, "Create new feature failed");
641 : }
642 :
643 0 : CPLDebug("NGW", "CreateFeature new FID: " CPL_FRMT_GIB, nOutFID);
644 0 : return nOutFID;
645 : }
646 :
647 0 : bool UpdateFeature(const std::string &osUrl, const std::string &osResourceId,
648 : const std::string &osFeatureId,
649 : const std::string &osFeatureJson, char **papszHTTPOptions)
650 : {
651 0 : CPLErrorReset();
652 0 : std::string osPayloadInt = "POSTFIELDS=" + osFeatureJson;
653 :
654 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "CUSTOMREQUEST=PUT");
655 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, osPayloadInt.c_str());
656 : papszHTTPOptions =
657 0 : CSLAddString(papszHTTPOptions,
658 : "HEADERS=Content-Type: application/json\r\nAccept: */*");
659 :
660 0 : CPLDebug("NGW", "UpdateFeature request payload: %s", osFeatureJson.c_str());
661 :
662 0 : std::string osUrlInt = GetFeature(osUrl, osResourceId) + osFeatureId;
663 0 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrlInt.c_str(), papszHTTPOptions);
664 0 : CSLDestroy(papszHTTPOptions);
665 0 : bool bResult = false;
666 0 : if (psResult)
667 : {
668 0 : bResult = psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
669 :
670 : // Get error message.
671 0 : if (!bResult)
672 : {
673 0 : ReportError(psResult->pabyData, psResult->nDataLen);
674 : }
675 0 : CPLHTTPDestroyResult(psResult);
676 : }
677 0 : return bResult;
678 : }
679 :
680 0 : std::vector<GIntBig> PatchFeatures(const std::string &osUrl,
681 : const std::string &osResourceId,
682 : const std::string &osFeaturesJson,
683 : char **papszHTTPOptions)
684 : {
685 0 : std::vector<GIntBig> aoFIDs;
686 0 : CPLErrorReset();
687 0 : std::string osPayloadInt = "POSTFIELDS=" + osFeaturesJson;
688 :
689 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "CUSTOMREQUEST=PATCH");
690 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, osPayloadInt.c_str());
691 : papszHTTPOptions =
692 0 : CSLAddString(papszHTTPOptions,
693 : "HEADERS=Content-Type: application/json\r\nAccept: */*");
694 :
695 0 : CPLDebug("NGW", "PatchFeatures request payload: %s",
696 : osFeaturesJson.c_str());
697 :
698 0 : std::string osUrlInt = GetFeature(osUrl, osResourceId);
699 0 : CPLJSONDocument oPatchFeatureReq;
700 0 : bool bResult = oPatchFeatureReq.LoadUrl(osUrlInt, papszHTTPOptions);
701 0 : CSLDestroy(papszHTTPOptions);
702 :
703 0 : CPLJSONObject oRoot = oPatchFeatureReq.GetRoot();
704 0 : if (oRoot.IsValid())
705 : {
706 0 : if (bResult)
707 : {
708 0 : CPLJSONArray aoJSONIDs = oRoot.ToArray();
709 0 : for (int i = 0; i < aoJSONIDs.Size(); ++i)
710 : {
711 0 : GIntBig nOutFID = aoJSONIDs[i].GetLong("id", OGRNullFID);
712 0 : aoFIDs.push_back(nOutFID);
713 : }
714 : }
715 : else
716 : {
717 0 : std::string osErrorMessage = oRoot.GetString("message");
718 0 : if (osErrorMessage.empty())
719 : {
720 0 : osErrorMessage = "Patch features failed";
721 : }
722 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMessage.c_str());
723 : }
724 : }
725 : else
726 : {
727 0 : CPLError(CE_Failure, CPLE_AppDefined, "Patch features failed");
728 : }
729 0 : return aoFIDs;
730 : }
731 :
732 0 : bool GetExtent(const std::string &osUrl, const std::string &osResourceId,
733 : char **papszHTTPOptions, int nEPSG, OGREnvelope &stExtent)
734 : {
735 0 : CPLErrorReset();
736 0 : CPLJSONDocument oExtentReq;
737 0 : bool bResult = oExtentReq.LoadUrl(GetLayerExtent(osUrl, osResourceId),
738 : papszHTTPOptions);
739 :
740 0 : CPLJSONObject oRoot = oExtentReq.GetRoot();
741 0 : if (!bResult)
742 : {
743 0 : std::string osErrorMessage = oRoot.GetString("message");
744 0 : if (osErrorMessage.empty())
745 : {
746 0 : osErrorMessage = "Get extent failed";
747 : }
748 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMessage.c_str());
749 0 : return false;
750 : }
751 : // Response extent spatial reference is EPSG:4326.
752 :
753 0 : double dfMinX = oRoot.GetDouble("extent/minLon");
754 0 : double dfMinY = oRoot.GetDouble("extent/minLat");
755 0 : double dfMaxX = oRoot.GetDouble("extent/maxLon");
756 0 : double dfMaxY = oRoot.GetDouble("extent/maxLat");
757 :
758 : double adfCoordinatesX[4];
759 : double adfCoordinatesY[4];
760 0 : adfCoordinatesX[0] = dfMinX;
761 0 : adfCoordinatesY[0] = dfMinY;
762 0 : adfCoordinatesX[1] = dfMinX;
763 0 : adfCoordinatesY[1] = dfMaxY;
764 0 : adfCoordinatesX[2] = dfMaxX;
765 0 : adfCoordinatesY[2] = dfMaxY;
766 0 : adfCoordinatesX[3] = dfMaxX;
767 0 : adfCoordinatesY[3] = dfMinY;
768 :
769 0 : OGRSpatialReference o4326SRS;
770 0 : o4326SRS.SetWellKnownGeogCS("WGS84");
771 0 : o4326SRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
772 0 : OGRSpatialReference o3857SRS;
773 0 : o3857SRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
774 0 : if (o3857SRS.importFromEPSG(nEPSG) != OGRERR_NONE)
775 : {
776 0 : CPLError(CE_Failure, CPLE_AppDefined,
777 : "Project extent SRS to EPSG:3857 failed");
778 0 : return false;
779 : }
780 :
781 : OGRCoordinateTransformation *poTransform =
782 0 : OGRCreateCoordinateTransformation(&o4326SRS, &o3857SRS);
783 0 : if (poTransform)
784 : {
785 0 : poTransform->Transform(4, adfCoordinatesX, adfCoordinatesY);
786 0 : delete poTransform;
787 :
788 0 : stExtent.MinX = std::numeric_limits<double>::max();
789 0 : stExtent.MaxX = std::numeric_limits<double>::min();
790 0 : stExtent.MinY = std::numeric_limits<double>::max();
791 0 : stExtent.MaxY = std::numeric_limits<double>::min();
792 :
793 0 : for (int i = 1; i < 4; ++i)
794 : {
795 0 : if (stExtent.MinX > adfCoordinatesX[i])
796 : {
797 0 : stExtent.MinX = adfCoordinatesX[i];
798 : }
799 0 : if (stExtent.MaxX < adfCoordinatesX[i])
800 : {
801 0 : stExtent.MaxX = adfCoordinatesX[i];
802 : }
803 0 : if (stExtent.MinY > adfCoordinatesY[i])
804 : {
805 0 : stExtent.MinY = adfCoordinatesY[i];
806 : }
807 0 : if (stExtent.MaxY < adfCoordinatesY[i])
808 : {
809 0 : stExtent.MaxY = adfCoordinatesY[i];
810 : }
811 : }
812 : }
813 0 : return true;
814 : }
815 :
816 0 : CPLJSONObject UploadFile(const std::string &osUrl,
817 : const std::string &osFilePath, char **papszHTTPOptions,
818 : GDALProgressFunc pfnProgress, void *pProgressData)
819 : {
820 0 : CPLErrorReset();
821 0 : papszHTTPOptions = CSLAddString(
822 : papszHTTPOptions, CPLSPrintf("FORM_FILE_PATH=%s", osFilePath.c_str()));
823 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "FORM_FILE_NAME=file");
824 :
825 0 : const char *pszFormFileName = CPLGetFilename(osFilePath.c_str());
826 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "FORM_KEY_0=name");
827 0 : papszHTTPOptions = CSLAddString(
828 : papszHTTPOptions, CPLSPrintf("FORM_VALUE_0=%s", pszFormFileName));
829 0 : papszHTTPOptions = CSLAddString(papszHTTPOptions, "FORM_ITEM_COUNT=1");
830 :
831 : CPLHTTPResult *psResult =
832 0 : CPLHTTPFetchEx(GetUpload(osUrl).c_str(), papszHTTPOptions, pfnProgress,
833 : pProgressData, nullptr, nullptr);
834 0 : CSLDestroy(papszHTTPOptions);
835 0 : CPLJSONObject oResult;
836 0 : if (psResult)
837 : {
838 0 : const bool bResult =
839 0 : psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
840 :
841 : // Get error message.
842 0 : if (!bResult)
843 : {
844 0 : ReportError(psResult->pabyData, psResult->nDataLen);
845 0 : CPLHTTPDestroyResult(psResult);
846 0 : return oResult;
847 : }
848 0 : CPLJSONDocument oFileJson;
849 0 : if (oFileJson.LoadMemory(psResult->pabyData, psResult->nDataLen))
850 : {
851 0 : oResult = oFileJson.GetRoot();
852 : }
853 0 : CPLHTTPDestroyResult(psResult);
854 : }
855 : else
856 : {
857 0 : CPLError(CE_Failure, CPLE_AppDefined, "Upload file %s failed",
858 : osFilePath.c_str());
859 : }
860 0 : return oResult;
861 : }
862 :
863 : } // namespace NGWAPI
|