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  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "ogr_amigocloud.h"
30 #include "ogr_p.h"
31 #include "ogrgeojsonreader.h"
32 
33 CPL_CVSID("$Id: ogramigocloudlayer.cpp 22f7e8df5b2966e7025652cfbe5ee9b143d0e008 2021-02-22 12:01:13 -0500 Victor $")
34 
35 /************************************************************************/
36 /*                         OGRAmigoCloudLayer()                            */
37 /************************************************************************/
38 
OGRAmigoCloudLayer(OGRAmigoCloudDataSource * poDSIn)39 OGRAmigoCloudLayer::OGRAmigoCloudLayer(OGRAmigoCloudDataSource* poDSIn) :
40     poDS(poDSIn),
41     poFeatureDefn(nullptr),
42     osFIDColName("amigo_id"),
43     bEOF(FALSE),
44     nFetchedObjects(-1),
45     iNextInFetchedObjects(0),
46     iNext(0),
47     poCachedObj(nullptr)
48 {
49 }
50 
51 /************************************************************************/
52 /*                         ~OGRAmigoCloudLayer()                           */
53 /************************************************************************/
54 
~OGRAmigoCloudLayer()55 OGRAmigoCloudLayer::~OGRAmigoCloudLayer()
56 
57 {
58     if( poCachedObj != nullptr )
59         json_object_put(poCachedObj);
60 
61     if( poFeatureDefn != nullptr )
62         poFeatureDefn->Release();
63 }
64 
65 /************************************************************************/
66 /*                            ResetReading()                            */
67 /************************************************************************/
68 
ResetReading()69 void OGRAmigoCloudLayer::ResetReading()
70 
71 {
72     if( poCachedObj != nullptr )
73         json_object_put(poCachedObj);
74     poCachedObj = nullptr;
75     bEOF = FALSE;
76     nFetchedObjects = -1;
77     iNextInFetchedObjects = 0;
78     iNext = 0;
79 }
80 
81 /************************************************************************/
82 /*                           GetLayerDefn()                             */
83 /************************************************************************/
84 
GetLayerDefn()85 OGRFeatureDefn * OGRAmigoCloudLayer::GetLayerDefn()
86 {
87     return GetLayerDefnInternal(nullptr);
88 }
89 
90 /************************************************************************/
91 /*                            BuildFeature()                            */
92 /************************************************************************/
93 
BuildFeature(json_object * poRowObj)94 OGRFeature *OGRAmigoCloudLayer::BuildFeature(json_object* poRowObj)
95 {
96     OGRFeature* poFeature = nullptr;
97     if( poRowObj != nullptr &&
98         json_object_get_type(poRowObj) == json_type_object )
99     {
100         poFeature = new OGRFeature(poFeatureDefn);
101 
102         if( !osFIDColName.empty() )
103         {
104             json_object* poVal = CPL_json_object_object_get(poRowObj, osFIDColName);
105             if( poVal != nullptr &&
106                 json_object_get_type(poVal) == json_type_string )
107             {
108                 std::string amigo_id = json_object_get_string(poVal);
109                 OGRAmigoCloudFID aFID(amigo_id, iNext);
110                 mFIDs[aFID.iFID] = aFID;
111                 poFeature->SetFID(aFID.iFID);
112             }
113         }
114 
115         for(int i=0;i<poFeatureDefn->GetFieldCount();i++)
116         {
117             json_object* poVal = CPL_json_object_object_get(poRowObj,
118                             poFeatureDefn->GetFieldDefn(i)->GetNameRef());
119 
120             if( poVal == nullptr )
121             {
122                 poFeature->SetFieldNull(i);
123             }
124             else if( json_object_get_type(poVal) == json_type_string )
125             {
126                 poFeature->SetField(i, json_object_get_string(poVal));
127             }
128             else if( json_object_get_type(poVal) == json_type_int ||
129                      json_object_get_type(poVal) == json_type_boolean )
130             {
131                 poFeature->SetField(i, (GIntBig)json_object_get_int64(poVal));
132             }
133             else if( json_object_get_type(poVal) == json_type_double )
134             {
135                 poFeature->SetField(i, json_object_get_double(poVal));
136             }
137         }
138 
139         for(int i=0;i<poFeatureDefn->GetGeomFieldCount();i++)
140         {
141             OGRGeomFieldDefn* poGeomFldDefn = poFeatureDefn->GetGeomFieldDefn(i);
142             json_object* poVal = CPL_json_object_object_get(poRowObj,
143                             poGeomFldDefn->GetNameRef());
144             if( poVal != nullptr &&
145                 json_object_get_type(poVal) == json_type_string )
146             {
147                 OGRGeometry* poGeom = OGRGeometryFromHexEWKB(
148                                         json_object_get_string(poVal), nullptr, FALSE);
149                 if( poGeom != nullptr )
150                     poGeom->assignSpatialReference(poGeomFldDefn->GetSpatialRef());
151                 poFeature->SetGeomFieldDirectly(i, poGeom);
152             }
153         }
154     }
155     return poFeature;
156 }
157 
158 /************************************************************************/
159 /*                        FetchNewFeatures()                            */
160 /************************************************************************/
161 
FetchNewFeatures(GIntBig iNextIn)162 json_object* OGRAmigoCloudLayer::FetchNewFeatures(GIntBig iNextIn)
163 {
164     CPLString osSQL = osBaseSQL;
165     if (osSQL.ifind("SELECT") != std::string::npos &&
166         osSQL.ifind(" LIMIT ") == std::string::npos)
167     {
168         osSQL += " LIMIT ";
169         osSQL += CPLSPrintf("%d", GetFeaturesToFetch());
170         osSQL += " OFFSET ";
171         osSQL += CPLSPrintf(CPL_FRMT_GIB, iNextIn);
172     }
173     return poDS->RunSQL(osSQL);
174 }
175 
176 /************************************************************************/
177 /*                       GetNextRawFeature()                            */
178 /************************************************************************/
179 
GetNextRawFeature()180 OGRFeature *OGRAmigoCloudLayer::GetNextRawFeature()
181 {
182     if( bEOF )
183         return nullptr;
184 
185     if( iNextInFetchedObjects >= nFetchedObjects )
186     {
187         if( nFetchedObjects > 0 && nFetchedObjects < GetFeaturesToFetch() )
188         {
189             bEOF = TRUE;
190             return nullptr;
191         }
192 
193         if( poFeatureDefn == nullptr && osBaseSQL.empty() )
194         {
195             GetLayerDefn();
196         }
197 
198         json_object* poObj = FetchNewFeatures(iNext);
199         if( poObj == nullptr )
200         {
201             bEOF = TRUE;
202             return nullptr;
203         }
204 
205         if( poFeatureDefn == nullptr )
206         {
207             GetLayerDefnInternal(poObj);
208         }
209 
210         json_object* poRows = CPL_json_object_object_get(poObj, "data");
211 
212         if( poRows == nullptr ||
213             json_object_get_type(poRows) != json_type_array ||
214             json_object_array_length(poRows) == 0 )
215         {
216             json_object_put(poObj);
217             bEOF = TRUE;
218             return nullptr;
219         }
220 
221         if( poCachedObj != nullptr )
222             json_object_put(poCachedObj);
223         poCachedObj = poObj;
224 
225         nFetchedObjects = static_cast<decltype(nFetchedObjects)>(json_object_array_length(poRows));
226         iNextInFetchedObjects = 0;
227     }
228 
229     json_object* poRows = CPL_json_object_object_get(poCachedObj, "data");
230     json_object* poRowObj = json_object_array_get_idx(poRows, iNextInFetchedObjects);
231 
232     iNextInFetchedObjects ++;
233 
234     OGRFeature* poFeature = BuildFeature(poRowObj);
235 
236     std::map<GIntBig, OGRAmigoCloudFID>::iterator it = mFIDs.find(poFeature->GetFID());
237     if (it != mFIDs.end())
238     {
239         iNext = it->second.iIndex + 1;
240     }
241 
242     return poFeature;
243 }
244 
245 /************************************************************************/
246 /*                           GetNextFeature()                           */
247 /************************************************************************/
248 
GetNextFeature()249 OGRFeature *OGRAmigoCloudLayer::GetNextFeature()
250 {
251     while( true )
252     {
253         OGRFeature *poFeature = GetNextRawFeature();
254         if (poFeature == nullptr)
255             return nullptr;
256 
257         if((m_poFilterGeom == nullptr
258             || FilterGeometry( poFeature->GetGeometryRef() ) )
259         && (m_poAttrQuery == nullptr
260             || m_poAttrQuery->Evaluate( poFeature )) )
261         {
262             return poFeature;
263         }
264         else
265             delete poFeature;
266     }
267 }
268 
269 /************************************************************************/
270 /*                           TestCapability()                           */
271 /************************************************************************/
272 
TestCapability(const char * pszCap)273 int OGRAmigoCloudLayer::TestCapability( const char * pszCap )
274 
275 {
276     if ( EQUAL(pszCap, OLCStringsAsUTF8) )
277         return TRUE;
278     return FALSE;
279 }
280 
281 /************************************************************************/
282 /*                          EstablishLayerDefn()                        */
283 /************************************************************************/
284 
EstablishLayerDefn(const char * pszLayerName,json_object * poObjIn)285 void OGRAmigoCloudLayer::EstablishLayerDefn(const char* pszLayerName,
286                                             json_object* poObjIn)
287 {
288     poFeatureDefn = new OGRFeatureDefn(pszLayerName);
289     poFeatureDefn->Reference();
290     poFeatureDefn->SetGeomType(wkbNone);
291 
292     CPLString osSQL;
293     size_t nPos = osBaseSQL.ifind(" LIMIT ");
294     if( nPos != std::string::npos )
295     {
296         osSQL = osBaseSQL;
297         size_t nSize = osSQL.size();
298         for(size_t i = nPos + strlen(" LIMIT "); i < nSize; i++)
299         {
300             if( osSQL[i] == ' ' )
301                 break;
302             osSQL[i] = '0';
303         }
304     }
305     else
306         osSQL.Printf("%s LIMIT 0", osBaseSQL.c_str());
307     json_object* poObj = poObjIn;
308     if( poObj == nullptr )
309     {
310         poObj = poDS->RunSQL(osSQL);
311         if( poObj == nullptr )
312         {
313             return;
314         }
315     }
316 
317     json_object* poFields = CPL_json_object_object_get(poObj, "columns");
318     if( poFields == nullptr || json_object_get_type(poFields) != json_type_array)
319     {
320         if( poObjIn == nullptr )
321             json_object_put(poObj);
322         return;
323     }
324 
325     auto size = json_object_array_length(poFields);
326 
327     for(auto i=decltype(size){0}; i< size; i++)
328     {
329         json_object *obj = json_object_array_get_idx(poFields, i);
330 
331         if(obj != nullptr && json_object_get_type(obj) == json_type_object)
332         {
333             std::string fieldName;
334             std::string fieldType;
335 
336             json_object_iter it;
337             it.key = nullptr;
338             it.val = nullptr;
339             it.entry = nullptr;
340             json_object_object_foreachC(obj, it)
341             {
342                 const char *pszColName = it.key;
343                 if(it.val != nullptr)
344                 {
345                     if(EQUAL(pszColName, "name"))
346                     {
347                         fieldName = json_object_get_string(it.val);
348                     } else if(EQUAL(pszColName, "type"))
349                     {
350                         fieldType = json_object_get_string(it.val);
351                     }
352                 }
353             }
354             if(!fieldName.empty() && !fieldType.empty())
355             {
356                 if(EQUAL(fieldType.c_str(), "string") ||
357                    EQUAL(fieldType.c_str(), "unknown(19)") /* name */ )
358                 {
359                     OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTString);
360                     poFeatureDefn->AddFieldDefn(&oFieldDefn);
361                 }
362                 else if(EQUAL(fieldType.c_str(), "number") ||
363                         EQUAL(fieldType.c_str(), "float") ||
364                         EQUAL(fieldType.c_str(), "real"))
365                 {
366                     OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTReal);
367                     poFeatureDefn->AddFieldDefn(&oFieldDefn);
368                 }
369                 else if(EQUAL(fieldType.c_str(), "integer"))
370                 {
371                     OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTInteger);
372                     poFeatureDefn->AddFieldDefn(&oFieldDefn);
373                 }
374                 else if(EQUAL(fieldType.c_str(), "bigint"))
375                 {
376                     OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTInteger64);
377                     poFeatureDefn->AddFieldDefn(&oFieldDefn);
378                 }
379                 else if(EQUAL(fieldType.c_str(), "date"))
380                 {
381                     OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTDate);
382                     poFeatureDefn->AddFieldDefn(&oFieldDefn);
383                 }
384                 else if(EQUAL(fieldType.c_str(), "datetime"))
385                 {
386                     OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTDateTime);
387                     poFeatureDefn->AddFieldDefn(&oFieldDefn);
388                 }
389                 else if(EQUAL(fieldType.c_str(), "geometry"))
390                 {
391                     OGRAmigoCloudGeomFieldDefn *poFieldDefn =
392                             new OGRAmigoCloudGeomFieldDefn(fieldName.c_str(), wkbUnknown);
393                     poFeatureDefn->AddGeomFieldDefn(poFieldDefn, FALSE);
394                     OGRSpatialReference* poSRS = GetSRS(fieldName.c_str(), &poFieldDefn->nSRID);
395                     if( poSRS != nullptr )
396                     {
397                         poFeatureDefn->GetGeomFieldDefn(
398                                 poFeatureDefn->GetGeomFieldCount() - 1)->SetSpatialRef(poSRS);
399                         poSRS->Release();
400                     } else {
401                         poFeatureDefn->GetGeomFieldDefn(
402                                 poFeatureDefn->GetGeomFieldCount() - 1)->SetSpatialRef(poSRS);
403                     }
404                 }
405                 else if(EQUAL(fieldType.c_str(), "boolean"))
406                 {
407                     OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTInteger);
408                     oFieldDefn.SetSubType(OFSTBoolean);
409                     poFeatureDefn->AddFieldDefn(&oFieldDefn);
410                 }
411                 else
412                 {
413                     CPLDebug("AMIGOCLOUD", "Unhandled type: %s. Defaulting to string", fieldType.c_str());
414                     OGRFieldDefn oFieldDefn(fieldName.c_str(), OFTString);
415                     poFeatureDefn->AddFieldDefn(&oFieldDefn);
416                 }
417             }
418         }
419     }
420     if( poObjIn == nullptr )
421         json_object_put(poObj);
422 }
423 
424 /************************************************************************/
425 /*                               GetSRS()                               */
426 /************************************************************************/
427 
GetSRS(const char * pszGeomCol,int * pnSRID)428 OGRSpatialReference* OGRAmigoCloudLayer::GetSRS(const char* pszGeomCol,
429                                              int *pnSRID)
430 {
431     json_object* poObj = poDS->RunSQL(GetSRS_SQL(pszGeomCol));
432     json_object* poRowObj = OGRAMIGOCLOUDGetSingleRow(poObj);
433     if( poRowObj == nullptr )
434     {
435         if( poObj != nullptr )
436             json_object_put(poObj);
437         return nullptr;
438     }
439 
440     json_object* poSRID = CPL_json_object_object_get(poRowObj, "srid");
441     if( poSRID != nullptr && json_object_get_type(poSRID) == json_type_int )
442     {
443         *pnSRID = json_object_get_int(poSRID);
444     }
445 
446     json_object* poSRTEXT = CPL_json_object_object_get(poRowObj, "srtext");
447     OGRSpatialReference* poSRS = nullptr;
448     if( poSRTEXT != nullptr && json_object_get_type(poSRTEXT) == json_type_string )
449     {
450         const char* pszSRTEXT = json_object_get_string(poSRTEXT);
451         poSRS = new OGRSpatialReference();
452         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
453         if( poSRS->importFromWkt(pszSRTEXT) != OGRERR_NONE )
454         {
455             delete poSRS;
456             poSRS = nullptr;
457         }
458     }
459     json_object_put(poObj);
460 
461     return poSRS;
462 }
463