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