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