Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/amigocloud/ogramigocloudlayer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  AmigoCloud Translator
4
 * Purpose:  Implements OGRAmigoCloudLayer class.
5
 * Author:   Victor Chernetsky, <victor at amigocloud dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2015, Victor Chernetsky, <victor at amigocloud dot com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogr_amigocloud.h"
14
#include "ogr_p.h"
15
#include "ogrlibjsonutils.h"
16
17
0
OGRAmigoCloudGeomFieldDefn::~OGRAmigoCloudGeomFieldDefn() = default;
18
19
/************************************************************************/
20
/*                         OGRAmigoCloudLayer()                            */
21
/************************************************************************/
22
23
OGRAmigoCloudLayer::OGRAmigoCloudLayer(OGRAmigoCloudDataSource *poDSIn)
24
80
    : poDS(poDSIn), poFeatureDefn(nullptr), osFIDColName("amigo_id"),
25
80
      bEOF(FALSE), nFetchedObjects(-1), iNextInFetchedObjects(0), iNext(0),
26
80
      poCachedObj(nullptr)
27
80
{
28
80
}
29
30
/************************************************************************/
31
/*                         ~OGRAmigoCloudLayer()                           */
32
/************************************************************************/
33
34
OGRAmigoCloudLayer::~OGRAmigoCloudLayer()
35
36
80
{
37
80
    if (poCachedObj != nullptr)
38
0
        json_object_put(poCachedObj);
39
40
80
    if (poFeatureDefn != nullptr)
41
0
        poFeatureDefn->Release();
42
80
}
43
44
/************************************************************************/
45
/*                            ResetReading()                            */
46
/************************************************************************/
47
48
void OGRAmigoCloudLayer::ResetReading()
49
50
0
{
51
0
    if (poCachedObj != nullptr)
52
0
        json_object_put(poCachedObj);
53
0
    poCachedObj = nullptr;
54
0
    bEOF = FALSE;
55
0
    nFetchedObjects = -1;
56
0
    iNextInFetchedObjects = 0;
57
0
    iNext = 0;
58
0
}
59
60
/************************************************************************/
61
/*                           GetLayerDefn()                             */
62
/************************************************************************/
63
64
const OGRFeatureDefn *OGRAmigoCloudLayer::GetLayerDefn() const
65
0
{
66
0
    return const_cast<OGRAmigoCloudLayer *>(this)->GetLayerDefnInternal(
67
0
        nullptr);
68
0
}
69
70
/************************************************************************/
71
/*                            BuildFeature()                            */
72
/************************************************************************/
73
74
OGRFeature *OGRAmigoCloudLayer::BuildFeature(json_object *poRowObj)
75
0
{
76
0
    OGRFeature *poFeature = nullptr;
77
0
    if (poRowObj != nullptr &&
78
0
        json_object_get_type(poRowObj) == json_type_object)
79
0
    {
80
0
        poFeature = new OGRFeature(poFeatureDefn);
81
82
0
        if (!osFIDColName.empty())
83
0
        {
84
0
            json_object *poVal =
85
0
                CPL_json_object_object_get(poRowObj, osFIDColName);
86
0
            if (poVal != nullptr &&
87
0
                json_object_get_type(poVal) == json_type_string)
88
0
            {
89
0
                std::string amigo_id = json_object_get_string(poVal);
90
0
                OGRAmigoCloudFID aFID(amigo_id, iNext);
91
0
                mFIDs[aFID.iFID] = aFID;
92
0
                poFeature->SetFID(aFID.iFID);
93
0
            }
94
0
        }
95
96
0
        for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
97
0
        {
98
0
            json_object *poVal = CPL_json_object_object_get(
99
0
                poRowObj, poFeatureDefn->GetFieldDefn(i)->GetNameRef());
100
101
0
            if (poVal == nullptr)
102
0
            {
103
0
                poFeature->SetFieldNull(i);
104
0
            }
105
0
            else if (json_object_get_type(poVal) == json_type_string)
106
0
            {
107
0
                poFeature->SetField(i, json_object_get_string(poVal));
108
0
            }
109
0
            else if (json_object_get_type(poVal) == json_type_int ||
110
0
                     json_object_get_type(poVal) == json_type_boolean)
111
0
            {
112
0
                poFeature->SetField(i, (GIntBig)json_object_get_int64(poVal));
113
0
            }
114
0
            else if (json_object_get_type(poVal) == json_type_double)
115
0
            {
116
0
                poFeature->SetField(i, json_object_get_double(poVal));
117
0
            }
118
0
        }
119
120
0
        for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
121
0
        {
122
0
            OGRGeomFieldDefn *poGeomFldDefn =
123
0
                poFeatureDefn->GetGeomFieldDefn(i);
124
0
            json_object *poVal = CPL_json_object_object_get(
125
0
                poRowObj, poGeomFldDefn->GetNameRef());
126
0
            if (poVal != nullptr &&
127
0
                json_object_get_type(poVal) == json_type_string)
128
0
            {
129
0
                OGRGeometry *poGeom = OGRGeometryFromHexEWKB(
130
0
                    json_object_get_string(poVal), nullptr, FALSE);
131
0
                if (poGeom != nullptr)
132
0
                    poGeom->assignSpatialReference(
133
0
                        poGeomFldDefn->GetSpatialRef());
134
0
                poFeature->SetGeomFieldDirectly(i, poGeom);
135
0
            }
136
0
        }
137
0
    }
138
0
    return poFeature;
139
0
}
140
141
/************************************************************************/
142
/*                        FetchNewFeatures()                            */
143
/************************************************************************/
144
145
json_object *OGRAmigoCloudLayer::FetchNewFeatures(GIntBig iNextIn)
146
80
{
147
80
    CPLString osSQL = osBaseSQL;
148
80
    if (osSQL.ifind("SELECT") != std::string::npos &&
149
80
        osSQL.ifind(" LIMIT ") == std::string::npos)
150
80
    {
151
80
        osSQL += " LIMIT ";
152
80
        osSQL += CPLSPrintf("%d", GetFeaturesToFetch());
153
80
        osSQL += " OFFSET ";
154
80
        osSQL += CPLSPrintf(CPL_FRMT_GIB, iNextIn);
155
80
    }
156
80
    return poDS->RunSQL(osSQL);
157
80
}
158
159
/************************************************************************/
160
/*                       GetNextRawFeature()                            */
161
/************************************************************************/
162
163
OGRFeature *OGRAmigoCloudLayer::GetNextRawFeature()
164
80
{
165
80
    if (bEOF)
166
0
        return nullptr;
167
168
80
    if (iNextInFetchedObjects >= nFetchedObjects)
169
80
    {
170
80
        if (nFetchedObjects > 0 && nFetchedObjects < GetFeaturesToFetch())
171
0
        {
172
0
            bEOF = TRUE;
173
0
            return nullptr;
174
0
        }
175
176
80
        if (poFeatureDefn == nullptr && osBaseSQL.empty())
177
0
        {
178
0
            GetLayerDefn();
179
0
        }
180
181
80
        json_object *poObj = FetchNewFeatures(iNext);
182
80
        if (poObj == nullptr)
183
80
        {
184
80
            bEOF = TRUE;
185
80
            return nullptr;
186
80
        }
187
188
0
        if (poFeatureDefn == nullptr)
189
0
        {
190
0
            GetLayerDefnInternal(poObj);
191
0
        }
192
193
0
        json_object *poRows = CPL_json_object_object_get(poObj, "data");
194
195
0
        if (poRows == nullptr ||
196
0
            json_object_get_type(poRows) != json_type_array ||
197
0
            json_object_array_length(poRows) == 0)
198
0
        {
199
0
            json_object_put(poObj);
200
0
            bEOF = TRUE;
201
0
            return nullptr;
202
0
        }
203
204
0
        if (poCachedObj != nullptr)
205
0
            json_object_put(poCachedObj);
206
0
        poCachedObj = poObj;
207
208
0
        nFetchedObjects = static_cast<decltype(nFetchedObjects)>(
209
0
            json_object_array_length(poRows));
210
0
        iNextInFetchedObjects = 0;
211
0
    }
212
213
0
    json_object *poRows = CPL_json_object_object_get(poCachedObj, "data");
214
0
    json_object *poRowObj =
215
0
        json_object_array_get_idx(poRows, iNextInFetchedObjects);
216
217
0
    iNextInFetchedObjects++;
218
219
0
    OGRFeature *poFeature = BuildFeature(poRowObj);
220
221
0
    std::map<GIntBig, OGRAmigoCloudFID>::iterator it =
222
0
        mFIDs.find(poFeature->GetFID());
223
0
    if (it != mFIDs.end())
224
0
    {
225
0
        iNext = it->second.iIndex + 1;
226
0
    }
227
228
0
    return poFeature;
229
80
}
230
231
/************************************************************************/
232
/*                           GetNextFeature()                           */
233
/************************************************************************/
234
235
OGRFeature *OGRAmigoCloudLayer::GetNextFeature()
236
80
{
237
80
    while (true)
238
80
    {
239
80
        OGRFeature *poFeature = GetNextRawFeature();
240
80
        if (poFeature == nullptr)
241
80
            return nullptr;
242
243
0
        if ((m_poFilterGeom == nullptr ||
244
0
             FilterGeometry(poFeature->GetGeometryRef())) &&
245
0
            (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
246
0
        {
247
0
            return poFeature;
248
0
        }
249
0
        else
250
0
            delete poFeature;
251
0
    }
252
80
}
253
254
/************************************************************************/
255
/*                           TestCapability()                           */
256
/************************************************************************/
257
258
int OGRAmigoCloudLayer::TestCapability(const char *pszCap) const
259
260
0
{
261
0
    if (EQUAL(pszCap, OLCStringsAsUTF8))
262
0
        return TRUE;
263
0
    else if (EQUAL(pszCap, OLCZGeometries))
264
0
        return TRUE;
265
0
    return FALSE;
266
0
}
267
268
/************************************************************************/
269
/*                          EstablishLayerDefn()                        */
270
/************************************************************************/
271
272
void OGRAmigoCloudLayer::EstablishLayerDefn(const char *pszLayerName,
273
                                            json_object *poObjIn)
274
0
{
275
0
    poFeatureDefn = new OGRFeatureDefn(pszLayerName);
276
0
    poFeatureDefn->Reference();
277
0
    poFeatureDefn->SetGeomType(wkbNone);
278
279
0
    CPLString osSQL;
280
0
    size_t nPos = osBaseSQL.ifind(" LIMIT ");
281
0
    if (nPos != std::string::npos)
282
0
    {
283
0
        osSQL = osBaseSQL;
284
0
        size_t nSize = osSQL.size();
285
0
        for (size_t i = nPos + strlen(" LIMIT "); i < nSize; i++)
286
0
        {
287
0
            if (osSQL[i] == ' ')
288
0
                break;
289
0
            osSQL[i] = '0';
290
0
        }
291
0
    }
292
0
    else
293
0
        osSQL.Printf("%s LIMIT 0", osBaseSQL.c_str());
294
0
    json_object *poObj = poObjIn;
295
0
    if (poObj == nullptr)
296
0
    {
297
0
        poObj = poDS->RunSQL(osSQL);
298
0
        if (poObj == nullptr)
299
0
        {
300
0
            return;
301
0
        }
302
0
    }
303
304
0
    json_object *poFields = CPL_json_object_object_get(poObj, "columns");
305
0
    if (poFields == nullptr ||
306
0
        json_object_get_type(poFields) != json_type_array)
307
0
    {
308
0
        if (poObjIn == nullptr)
309
0
            json_object_put(poObj);
310
0
        return;
311
0
    }
312
313
0
    auto size = json_object_array_length(poFields);
314
315
0
    for (auto i = decltype(size){0}; i < size; i++)
316
0
    {
317
0
        json_object *obj = json_object_array_get_idx(poFields, i);
318
319
0
        if (obj != nullptr && json_object_get_type(obj) == json_type_object)
320
0
        {
321
0
            std::string fieldName;
322
0
            std::string fieldType;
323
324
0
            json_object_iter it;
325
0
            it.key = nullptr;
326
0
            it.val = nullptr;
327
0
            it.entry = nullptr;
328
0
            json_object_object_foreachC(obj, it)
329
0
            {
330
0
                const char *pszColName = it.key;
331
0
                if (it.val != nullptr)
332
0
                {
333
0
                    if (EQUAL(pszColName, "name"))
334
0
                    {
335
0
                        fieldName = json_object_get_string(it.val);
336
0
                    }
337
0
                    else if (EQUAL(pszColName, "type"))
338
0
                    {
339
0
                        fieldType = json_object_get_string(it.val);
340
0
                    }
341
0
                }
342
0
            }
343
0
            if (!fieldName.empty() && !fieldType.empty())
344
0
            {
345
0
                if (EQUAL(fieldType.c_str(), "string") ||
346
0
                    EQUAL(fieldType.c_str(), "unknown(19)") /* name */)
347
0
                {
348
0
                    OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTString);
349
0
                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
350
0
                }
351
0
                else if (EQUAL(fieldType.c_str(), "number") ||
352
0
                         EQUAL(fieldType.c_str(), "float") ||
353
0
                         EQUAL(fieldType.c_str(), "real"))
354
0
                {
355
0
                    OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTReal);
356
0
                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
357
0
                }
358
0
                else if (EQUAL(fieldType.c_str(), "integer"))
359
0
                {
360
0
                    OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTInteger);
361
0
                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
362
0
                }
363
0
                else if (EQUAL(fieldType.c_str(), "bigint"))
364
0
                {
365
0
                    OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTInteger64);
366
0
                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
367
0
                }
368
0
                else if (EQUAL(fieldType.c_str(), "date"))
369
0
                {
370
0
                    OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTDate);
371
0
                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
372
0
                }
373
0
                else if (EQUAL(fieldType.c_str(), "datetime"))
374
0
                {
375
0
                    OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTDateTime);
376
0
                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
377
0
                }
378
0
                else if (EQUAL(fieldType.c_str(), "geometry"))
379
0
                {
380
0
                    auto poFieldDefn =
381
0
                        std::make_unique<OGRAmigoCloudGeomFieldDefn>(
382
0
                            fieldName.c_str(), wkbUnknown);
383
0
                    OGRSpatialReference *poSRS =
384
0
                        GetSRS(fieldName.c_str(), &poFieldDefn->nSRID);
385
0
                    if (poSRS != nullptr)
386
0
                    {
387
0
                        poFieldDefn->SetSpatialRef(poSRS);
388
0
                        poSRS->Release();
389
0
                    }
390
0
                    poFeatureDefn->AddGeomFieldDefn(std::move(poFieldDefn));
391
0
                }
392
0
                else if (EQUAL(fieldType.c_str(), "boolean"))
393
0
                {
394
0
                    OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTInteger);
395
0
                    oFieldDefn.SetSubType(OFSTBoolean);
396
0
                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
397
0
                }
398
0
                else
399
0
                {
400
0
                    CPLDebug("AMIGOCLOUD",
401
0
                             "Unhandled type: %s. Defaulting to string",
402
0
                             fieldType.c_str());
403
0
                    OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTString);
404
0
                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
405
0
                }
406
0
            }
407
0
        }
408
0
    }
409
0
    if (poObjIn == nullptr)
410
0
        json_object_put(poObj);
411
0
}
412
413
/************************************************************************/
414
/*                               GetSRS()                               */
415
/************************************************************************/
416
417
OGRSpatialReference *OGRAmigoCloudLayer::GetSRS(const char *pszGeomCol,
418
                                                int *pnSRID)
419
0
{
420
0
    json_object *poObj = poDS->RunSQL(GetSRS_SQL(pszGeomCol));
421
0
    json_object *poRowObj = OGRAMIGOCLOUDGetSingleRow(poObj);
422
0
    if (poRowObj == nullptr)
423
0
    {
424
0
        if (poObj != nullptr)
425
0
            json_object_put(poObj);
426
0
        return nullptr;
427
0
    }
428
429
0
    json_object *poSRID = CPL_json_object_object_get(poRowObj, "srid");
430
0
    if (poSRID != nullptr && json_object_get_type(poSRID) == json_type_int)
431
0
    {
432
0
        *pnSRID = json_object_get_int(poSRID);
433
0
    }
434
435
0
    json_object *poSRTEXT = CPL_json_object_object_get(poRowObj, "srtext");
436
0
    OGRSpatialReference *poSRS = nullptr;
437
0
    if (poSRTEXT != nullptr &&
438
0
        json_object_get_type(poSRTEXT) == json_type_string)
439
0
    {
440
0
        const char *pszSRTEXT = json_object_get_string(poSRTEXT);
441
0
        poSRS = new OGRSpatialReference();
442
0
        poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
443
0
        if (poSRS->importFromWkt(pszSRTEXT) != OGRERR_NONE)
444
0
        {
445
0
            delete poSRS;
446
0
            poSRS = nullptr;
447
0
        }
448
0
    }
449
0
    json_object_put(poObj);
450
451
0
    return poSRS;
452
0
}
453
454
/************************************************************************/
455
/*                             GetDataset()                             */
456
/************************************************************************/
457
458
GDALDataset *OGRAmigoCloudLayer::GetDataset()
459
0
{
460
0
    return poDS;
461
0
}