1 /******************************************************************************
2  * $Id: ogrelasticlayer.cpp 28375 2015-01-30 12:06:11Z rouault $
3  *
4  * Project:  ElasticSearch Translator
5  * Purpose:
6  * Author:
7  *
8  ******************************************************************************
9  * Copyright (c) 2011, Adam Estrada
10  * Copyright (c) 2012-2013, Even Rouault <even dot rouault at mines-paris dot org>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "ogr_elastic.h"
32 #include "cpl_conv.h"
33 #include "cpl_minixml.h"
34 #include "ogr_api.h"
35 #include "ogr_p.h"
36 #include <json.h> // JSON-C
37 
38 CPL_CVSID("$Id: ogrelasticlayer.cpp 28375 2015-01-30 12:06:11Z rouault $");
39 
40 /************************************************************************/
41 /*                           OGRElasticLayer()                          */
42 /************************************************************************/
43 
OGRElasticLayer(CPL_UNUSED const char * pszFilename,const char * pszLayerName,OGRElasticDataSource * poDS,OGRSpatialReference * poSRSIn,CPL_UNUSED int bWriteMode)44 OGRElasticLayer::OGRElasticLayer(CPL_UNUSED const char* pszFilename,
45                                  const char* pszLayerName,
46                                  OGRElasticDataSource* poDS,
47                                  OGRSpatialReference *poSRSIn,
48                                  CPL_UNUSED int bWriteMode) {
49     this->pszLayerName = CPLStrdup(pszLayerName);
50     this->poDS = poDS;
51     this->pAttributes = NULL;
52 
53     // If we are overwriting, then delete the current index if it exists
54     if (poDS->bOverwrite) {
55         poDS->DeleteIndex(CPLSPrintf("%s/%s", poDS->GetName(), pszLayerName));
56     }
57 
58     // Create the index
59     poDS->UploadFile(CPLSPrintf("%s/%s", poDS->GetName(), pszLayerName), "");
60 
61     // If we have a user specified mapping, then go ahead and update it now
62     if (poDS->pszMapping != NULL) {
63         poDS->UploadFile(CPLSPrintf("%s/%s/FeatureCollection/_mapping", poDS->GetName(), pszLayerName),
64                 poDS->pszMapping);
65     }
66 
67     poFeatureDefn = new OGRFeatureDefn(pszLayerName);
68     SetDescription( poFeatureDefn->GetName() );
69     poFeatureDefn->Reference();
70 
71     poSRS = poSRSIn;
72     if (poSRS)
73         poSRS->Reference();
74 
75     poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
76 
77     ResetReading();
78     return;
79 }
80 
81 /************************************************************************/
82 /*                         ~OGRElasticLayer()                           */
83 /************************************************************************/
84 
~OGRElasticLayer()85 OGRElasticLayer::~OGRElasticLayer() {
86     PushIndex();
87 
88     CPLFree(pszLayerName);
89 
90     poFeatureDefn->Release();
91 
92     if (poSRS != NULL)
93         poSRS->Release();
94 }
95 
96 
97 /************************************************************************/
98 /*                            GetLayerDefn()                            */
99 /************************************************************************/
100 
GetLayerDefn()101 OGRFeatureDefn * OGRElasticLayer::GetLayerDefn() {
102     return poFeatureDefn;
103 }
104 
105 /************************************************************************/
106 /*                            ResetReading()                            */
107 /************************************************************************/
108 
ResetReading()109 void OGRElasticLayer::ResetReading() {
110     return;
111 }
112 
113 /************************************************************************/
114 /*                           GetNextFeature()                           */
115 /************************************************************************/
116 
GetNextFeature()117 OGRFeature *OGRElasticLayer::GetNextFeature() {
118     CPLError(CE_Failure, CPLE_NotSupported,
119             "Cannot read features when writing a Elastic file");
120     return NULL;
121 }
122 
123 /************************************************************************/
124 /*                            AppendGroup()                             */
125 /************************************************************************/
126 
AppendGroup(json_object * parent,const CPLString & name)127 json_object *AppendGroup(json_object *parent, const CPLString &name) {
128     json_object *obj = json_object_new_object();
129     json_object *properties = json_object_new_object();
130     json_object_object_add(parent, name, obj);
131     json_object_object_add(obj, "properties", properties);
132     return properties;
133 }
134 
135 /************************************************************************/
136 /*                           AddPropertyMap()                           */
137 /************************************************************************/
138 
AddPropertyMap(const CPLString & type,const CPLString & format="")139 json_object *AddPropertyMap(const CPLString &type, const CPLString &format = "") {
140     json_object *obj = json_object_new_object();
141     json_object_object_add(obj, "store", json_object_new_string("yes"));
142     json_object_object_add(obj, "type", json_object_new_string(type.c_str()));
143     if (!format.empty()) {
144         json_object_object_add(obj, "format", json_object_new_string(format.c_str()));
145     }
146     return obj;
147 }
148 
149 /************************************************************************/
150 /*                             BuildMap()                               */
151 /************************************************************************/
152 
BuildMap()153 CPLString OGRElasticLayer::BuildMap() {
154     json_object *map = json_object_new_object();
155     json_object *properties = json_object_new_object();
156 
157     json_object *Feature = AppendGroup(map, "FeatureCollection");
158     json_object_object_add(Feature, "type", AddPropertyMap("string"));
159     json_object_object_add(Feature, "properties", properties);
160     if (pAttributes) json_object_object_add(properties, "properties", (json_object *) pAttributes);
161     json_object *geometry = AppendGroup(Feature, "geometry");
162     json_object_object_add(geometry, "type", AddPropertyMap("string"));
163     json_object_object_add(geometry, "coordinates", AddPropertyMap("geo_point"));
164 
165     CPLString jsonMap(json_object_to_json_string(map));
166     json_object_put(map);
167 
168     // The attribute's were freed from the deletion of the map object
169 	// because we added it as a child of one of the map object attributes
170     if (pAttributes) {
171         pAttributes = NULL;
172     }
173 
174     return jsonMap;
175 }
176 
177 /************************************************************************/
178 /*                           ICreateFeature()                            */
179 /************************************************************************/
180 
ICreateFeature(OGRFeature * poFeature)181 OGRErr OGRElasticLayer::ICreateFeature(OGRFeature *poFeature) {
182 
183     // Check to see if the user has elected to only write out the mapping file
184     // This method will only write out one layer from the vector file in cases where there are multiple layers
185     if (poDS->pszWriteMap != NULL) {
186         if (pAttributes) {
187             CPLString map = BuildMap();
188 
189             // Write the map to a file
190             FILE *f = fopen(poDS->pszWriteMap, "wb");
191             if (f) {
192                 fwrite(map.c_str(), 1, map.length(), f);
193                 fclose(f);
194             }
195         }
196         return OGRERR_NONE;
197     }
198 
199     // Check to see if we have any fields to upload to this index
200     if (poDS->pszMapping == NULL && pAttributes) {
201         poDS->UploadFile(CPLSPrintf("%s/%s/FeatureCollection/_mapping", poDS->GetName(), pszLayerName), BuildMap());
202     }
203 
204     // Get the center point of the geometry
205     OGREnvelope env;
206 	if (!poFeature->GetGeometryRef()) {
207 		return OGRERR_FAILURE;
208 	}
209     poFeature->GetGeometryRef()->getEnvelope(&env);
210 
211     json_object *fieldObject = json_object_new_object();
212     json_object *geometry = json_object_new_object();
213     json_object *coordinates = json_object_new_array();
214     json_object *properties = json_object_new_object();
215 
216     json_object_object_add(fieldObject, "geometry", geometry);
217     json_object_object_add(geometry, "type", json_object_new_string("POINT"));
218     json_object_object_add(geometry, "coordinates", coordinates);
219     json_object_array_add(coordinates, json_object_new_double((env.MaxX + env.MinX)*0.5));
220     json_object_array_add(coordinates, json_object_new_double((env.MaxY + env.MinY)*0.5));
221     json_object_object_add(fieldObject, "type", json_object_new_string("Feature"));
222     json_object_object_add(fieldObject, "properties", properties);
223 
224     // For every field that
225     int fieldCount = poFeatureDefn->GetFieldCount();
226     for (int i = 0; i < fieldCount; i++) {
227 		if(!poFeature->IsFieldSet( i ) ) {
228 			continue;
229 		}
230         switch (poFeatureDefn->GetFieldDefn(i)->GetType()) {
231             case OFTInteger:
232                 json_object_object_add(properties,
233                         poFeatureDefn->GetFieldDefn(i)->GetNameRef(),
234                         json_object_new_int(poFeature->GetFieldAsInteger(i)));
235                 break;
236             case OFTReal:
237                 json_object_object_add(properties,
238                         poFeatureDefn->GetFieldDefn(i)->GetNameRef(),
239                         json_object_new_double(poFeature->GetFieldAsDouble(i)));
240                 break;
241             default:
242             {
243                 CPLString tmp = poFeature->GetFieldAsString(i);
244                 json_object_object_add(properties,
245                         poFeatureDefn->GetFieldDefn(i)->GetNameRef(),
246                         json_object_new_string(tmp));
247             }
248         }
249     }
250 
251     // Build the field string
252     CPLString fields(json_object_to_json_string(fieldObject));
253     json_object_put(fieldObject);
254 
255     // Check to see if we're using bulk uploading
256     if (poDS->nBulkUpload > 0) {
257         sIndex += CPLSPrintf("{\"index\" :{\"_index\":\"%s\", \"_type\":\"FeatureCollection\"}}\n", pszLayerName) +
258                 fields + "\n\n";
259 
260         // Only push the data if we are over our bulk upload limit
261         if ((int) sIndex.length() > poDS->nBulkUpload) {
262             PushIndex();
263         }
264 
265     } else { // Fall back to using single item upload for every feature
266         poDS->UploadFile(CPLSPrintf("%s/%s/FeatureCollection/", poDS->GetName(), pszLayerName), fields);
267     }
268 
269     return OGRERR_NONE;
270 }
271 
272 /************************************************************************/
273 /*                             PushIndex()                              */
274 /************************************************************************/
275 
PushIndex()276 void OGRElasticLayer::PushIndex() {
277     if (sIndex.empty()) {
278         return;
279     }
280 
281     poDS->UploadFile(CPLSPrintf("%s/_bulk", poDS->GetName()), sIndex);
282     sIndex.clear();
283 }
284 
285 /************************************************************************/
286 /*                            CreateField()                             */
287 /************************************************************************/
288 
CreateField(OGRFieldDefn * poFieldDefn,CPL_UNUSED int bApproxOK)289 OGRErr OGRElasticLayer::CreateField(OGRFieldDefn *poFieldDefn,
290                                     CPL_UNUSED int bApproxOK) {
291     if (!pAttributes) {
292         pAttributes = json_object_new_object();
293     }
294 
295     switch (poFieldDefn->GetType()) {
296         case OFTInteger:
297             json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("integer"));
298             break;
299         case OFTReal:
300             json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("float"));
301             break;
302         case OFTString:
303             json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("string"));
304             break;
305         case OFTDateTime:
306         case OFTDate:
307             json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("date", "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd"));
308             break;
309         default:
310 
311             // These types are mapped as strings and may not be correct
312             /*
313                             OFTTime:
314                             OFTIntegerList = 1,
315                             OFTRealList = 3,
316                             OFTStringList = 5,
317                             OFTWideString = 6,
318                             OFTWideStringList = 7,
319                             OFTBinary = 8,
320                             OFTMaxType = 11
321              */
322             json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("string"));
323     }
324 
325     poFeatureDefn->AddFieldDefn(poFieldDefn);
326     return OGRERR_NONE;
327 }
328 
329 /************************************************************************/
330 /*                           TestCapability()                           */
331 /************************************************************************/
332 
TestCapability(const char * pszCap)333 int OGRElasticLayer::TestCapability(const char * pszCap) {
334     if (EQUAL(pszCap, OLCFastFeatureCount))
335         return FALSE;
336 
337     else if (EQUAL(pszCap, OLCStringsAsUTF8))
338         return TRUE;
339 
340     else if (EQUAL(pszCap, OLCSequentialWrite))
341         return TRUE;
342     else if (EQUAL(pszCap, OLCCreateField))
343         return TRUE;
344     else
345         return FALSE;
346 }
347 
348 /************************************************************************/
349 /*                          GetFeatureCount()                           */
350 /************************************************************************/
351 
GetFeatureCount(CPL_UNUSED int bForce)352 GIntBig OGRElasticLayer::GetFeatureCount(CPL_UNUSED int bForce) {
353     CPLError(CE_Failure, CPLE_NotSupported,
354             "Cannot read features when writing a Elastic file");
355     return 0;
356 }
357