1 /******************************************************************************
2 * $Id: ogrtopojsonreader.cpp 28886 2015-04-12 23:09:13Z rouault $
3 *
4 * Project: OpenGIS Simple Features Reference Implementation
5 * Purpose: Implementation of OGRTopoJSONReader class
6 * Author: Even Rouault, even dot rouault at mines dash paris dot org
7 *
8 ******************************************************************************
9 * Copyright (c) 2013, Even Rouault <even dot rouault at mines-paris dot org>
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 "ogrgeojsonreader.h"
31 #include "ogrgeojsonutils.h"
32 #include "ogr_geojson.h"
33 #include <json.h> // JSON-C
34 #include <ogr_api.h>
35
36 /************************************************************************/
37 /* OGRTopoJSONReader() */
38 /************************************************************************/
39
OGRTopoJSONReader()40 OGRTopoJSONReader::OGRTopoJSONReader()
41 : poGJObject_( NULL )
42 {
43 // Take a deep breath and get to work.
44 }
45
46 /************************************************************************/
47 /* ~OGRTopoJSONReader() */
48 /************************************************************************/
49
~OGRTopoJSONReader()50 OGRTopoJSONReader::~OGRTopoJSONReader()
51 {
52 if( NULL != poGJObject_ )
53 {
54 json_object_put(poGJObject_);
55 }
56
57 poGJObject_ = NULL;
58 }
59
60 /************************************************************************/
61 /* Parse() */
62 /************************************************************************/
63
Parse(const char * pszText)64 OGRErr OGRTopoJSONReader::Parse( const char* pszText )
65 {
66 if( NULL != pszText )
67 {
68 json_tokener* jstok = NULL;
69 json_object* jsobj = NULL;
70
71 jstok = json_tokener_new();
72 jsobj = json_tokener_parse_ex(jstok, pszText, -1);
73 if( jstok->err != json_tokener_success)
74 {
75 CPLError( CE_Failure, CPLE_AppDefined,
76 "TopoJSON parsing error: %s (at offset %d)",
77 json_tokener_error_desc(jstok->err), jstok->char_offset);
78
79 json_tokener_free(jstok);
80 return OGRERR_CORRUPT_DATA;
81 }
82 json_tokener_free(jstok);
83
84 /* JSON tree is shared for while lifetime of the reader object
85 * and will be released in the destructor.
86 */
87 poGJObject_ = jsobj;
88 }
89
90 return OGRERR_NONE;
91 }
92
93 typedef struct
94 {
95 double dfScale0, dfScale1;
96 double dfTranslate0, dfTranslate1;
97 } ScalingParams;
98
99 /************************************************************************/
100 /* ParsePoint() */
101 /************************************************************************/
102
ParsePoint(json_object * poPoint,double * pdfX,double * pdfY)103 static int ParsePoint(json_object* poPoint, double* pdfX, double* pdfY)
104 {
105 if( poPoint != NULL && json_type_array == json_object_get_type(poPoint) &&
106 json_object_array_length(poPoint) == 2 )
107 {
108 json_object* poX = json_object_array_get_idx(poPoint, 0);
109 json_object* poY = json_object_array_get_idx(poPoint, 1);
110 if( poX != NULL &&
111 (json_type_int == json_object_get_type(poX) ||
112 json_type_double == json_object_get_type(poX)) &&
113 poY != NULL &&
114 (json_type_int == json_object_get_type(poY) ||
115 json_type_double == json_object_get_type(poY)) )
116 {
117 *pdfX = json_object_get_double(poX);
118 *pdfY = json_object_get_double(poY);
119 return TRUE;
120 }
121 }
122 return FALSE;
123 }
124
125 /************************************************************************/
126 /* ParseArc() */
127 /************************************************************************/
128
ParseArc(OGRLineString * poLS,json_object * poArcsDB,int nArcID,int bReverse,ScalingParams * psParams)129 static void ParseArc(OGRLineString* poLS, json_object* poArcsDB, int nArcID,
130 int bReverse, ScalingParams* psParams)
131 {
132 json_object* poArcDB = json_object_array_get_idx(poArcsDB, nArcID);
133 if( poArcDB == NULL || json_type_array != json_object_get_type(poArcDB) )
134 return;
135 int nPoints = json_object_array_length(poArcDB);
136 double dfAccX = 0, dfAccY = 0;
137 int nBaseIndice = poLS->getNumPoints();
138 for(int i=0; i<nPoints; i++)
139 {
140 json_object* poPoint = json_object_array_get_idx(poArcDB, i);
141 double dfX = 0.0, dfY = 0.0;
142 if( ParsePoint( poPoint, &dfX, &dfY ) )
143 {
144 dfAccX += dfX;
145 dfAccY += dfY;
146 double dfX = dfAccX * psParams->dfScale0 + psParams->dfTranslate0;
147 double dfY = dfAccY * psParams->dfScale1 + psParams->dfTranslate1;
148 if( i == 0 )
149 {
150 if( !bReverse && poLS->getNumPoints() > 0 )
151 {
152 poLS->setNumPoints( nBaseIndice + nPoints - 1 );
153 nBaseIndice --;
154 continue;
155 }
156 else if( bReverse && poLS->getNumPoints() > 0 )
157 {
158 poLS->setNumPoints( nBaseIndice + nPoints - 1 );
159 nPoints --;
160 if( nPoints == 0 )
161 break;
162 }
163 else
164 poLS->setNumPoints( nBaseIndice + nPoints );
165 }
166
167 if( !bReverse )
168 poLS->setPoint(nBaseIndice + i, dfX, dfY);
169 else
170 poLS->setPoint(nBaseIndice + nPoints - 1 - i, dfX, dfY);
171 }
172 }
173 }
174
175 /************************************************************************/
176 /* ParseLineString() */
177 /************************************************************************/
178
ParseLineString(OGRLineString * poLS,json_object * poRing,json_object * poArcsDB,ScalingParams * psParams)179 static void ParseLineString(OGRLineString* poLS, json_object* poRing,
180 json_object* poArcsDB, ScalingParams* psParams)
181 {
182 int nArcsDB = json_object_array_length(poArcsDB);
183
184 int nArcsRing = json_object_array_length(poRing);
185 for(int j=0; j<nArcsRing; j++)
186 {
187 json_object* poArcId = json_object_array_get_idx(poRing, j);
188 if( poArcId != NULL && json_type_int == json_object_get_type(poArcId) )
189 {
190 int nArcId = json_object_get_int(poArcId);
191 int bReverse = FALSE;
192 if( nArcId < 0 )
193 {
194 nArcId = - nArcId - 1;
195 bReverse = TRUE;
196 }
197 if( nArcId < nArcsDB )
198 {
199 ParseArc(poLS, poArcsDB, nArcId, bReverse, psParams);
200 }
201 }
202 }
203 }
204
205 /************************************************************************/
206 /* ParsePolygon() */
207 /************************************************************************/
208
ParsePolygon(OGRPolygon * poPoly,json_object * poArcsObj,json_object * poArcsDB,ScalingParams * psParams)209 static void ParsePolygon(OGRPolygon* poPoly, json_object* poArcsObj,
210 json_object* poArcsDB, ScalingParams* psParams)
211 {
212 int nRings = json_object_array_length(poArcsObj);
213 for(int i=0; i<nRings; i++)
214 {
215 OGRLinearRing* poLR = new OGRLinearRing();
216 poPoly->addRingDirectly(poLR);
217
218 json_object* poRing = json_object_array_get_idx(poArcsObj, i);
219 if( poRing != NULL && json_type_array == json_object_get_type(poRing) )
220 {
221 ParseLineString(poLR, poRing, poArcsDB, psParams);
222 }
223 }
224 }
225
226 /************************************************************************/
227 /* ParseMultiLineString() */
228 /************************************************************************/
229
ParseMultiLineString(OGRMultiLineString * poMLS,json_object * poArcsObj,json_object * poArcsDB,ScalingParams * psParams)230 static void ParseMultiLineString(OGRMultiLineString* poMLS, json_object* poArcsObj,
231 json_object* poArcsDB, ScalingParams* psParams)
232 {
233 int nRings = json_object_array_length(poArcsObj);
234 for(int i=0; i<nRings; i++)
235 {
236 OGRLineString* poLS = new OGRLineString();
237 poMLS->addGeometryDirectly(poLS);
238
239 json_object* poRing = json_object_array_get_idx(poArcsObj, i);
240 if( poRing != NULL && json_type_array == json_object_get_type(poRing) )
241 {
242 ParseLineString(poLS, poRing, poArcsDB, psParams);
243 }
244 }
245 }
246
247 /************************************************************************/
248 /* ParseMultiPolygon() */
249 /************************************************************************/
250
ParseMultiPolygon(OGRMultiPolygon * poMultiPoly,json_object * poArcsObj,json_object * poArcsDB,ScalingParams * psParams)251 static void ParseMultiPolygon(OGRMultiPolygon* poMultiPoly, json_object* poArcsObj,
252 json_object* poArcsDB, ScalingParams* psParams)
253 {
254 int nPolys = json_object_array_length(poArcsObj);
255 for(int i=0; i<nPolys; i++)
256 {
257 OGRPolygon* poPoly = new OGRPolygon();
258 poMultiPoly->addGeometryDirectly(poPoly);
259
260 json_object* poPolyArcs = json_object_array_get_idx(poArcsObj, i);
261 if( poPolyArcs != NULL && json_type_array == json_object_get_type(poPolyArcs) )
262 {
263 ParsePolygon(poPoly, poPolyArcs, poArcsDB, psParams);
264 }
265 }
266 }
267
268 /************************************************************************/
269 /* ParseObject() */
270 /************************************************************************/
271
ParseObject(const char * pszId,json_object * poObj,OGRGeoJSONLayer * poLayer,json_object * poArcsDB,ScalingParams * psParams)272 static void ParseObject(const char* pszId,
273 json_object* poObj, OGRGeoJSONLayer* poLayer,
274 json_object* poArcsDB, ScalingParams* psParams)
275 {
276 json_object* poType = OGRGeoJSONFindMemberByName(poObj, "type");
277 if( poType == NULL || json_object_get_type(poType) != json_type_string )
278 return;
279 const char* pszType = json_object_get_string(poType);
280
281 json_object* poArcsObj = OGRGeoJSONFindMemberByName(poObj, "arcs");
282 json_object* poCoordinatesObj = OGRGeoJSONFindMemberByName(poObj, "coordinates");
283 if( strcmp(pszType, "Point") == 0 || strcmp(pszType, "MultiPoint") == 0 )
284 {
285 if( poCoordinatesObj == NULL || json_type_array != json_object_get_type(poCoordinatesObj) )
286 return;
287 }
288 else
289 {
290 if( poArcsObj == NULL || json_type_array != json_object_get_type(poArcsObj) )
291 return;
292 }
293
294 if( pszId == NULL )
295 {
296 json_object* poId = OGRGeoJSONFindMemberByName(poObj, "id");
297 if( poId != NULL &&
298 (json_type_string == json_object_get_type(poId) ||
299 json_type_int == json_object_get_type(poId)) )
300 {
301 pszId = json_object_get_string(poId);
302 }
303 }
304
305 OGRFeature* poFeature = new OGRFeature(poLayer->GetLayerDefn());
306 if( pszId != NULL )
307 poFeature->SetField("id", pszId);
308
309 json_object* poProperties = OGRGeoJSONFindMemberByName(poObj, "properties");
310 if( poProperties != NULL && json_type_object == json_object_get_type(poProperties) )
311 {
312 int nField = -1;
313 json_object_iter it;
314 it.key = NULL;
315 it.val = NULL;
316 it.entry = NULL;
317 json_object_object_foreachC( poProperties, it )
318 {
319 nField = poFeature->GetFieldIndex(it.key);
320 OGRGeoJSONReaderSetField(poLayer, poFeature, nField, it.key, it.val, FALSE, 0);
321 }
322 }
323
324 OGRGeometry* poGeom = NULL;
325 if( strcmp(pszType, "Point") == 0 )
326 {
327 double dfX = 0.0, dfY = 0.0;
328 if( ParsePoint( poCoordinatesObj, &dfX, &dfY ) )
329 {
330 dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0;
331 dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1;
332 poGeom = new OGRPoint(dfX, dfY);
333 }
334 else
335 poGeom = new OGRPoint();
336 }
337 else if( strcmp(pszType, "MultiPoint") == 0 )
338 {
339 OGRMultiPoint* poMP = new OGRMultiPoint();
340 poGeom = poMP;
341 int nTuples = json_object_array_length(poCoordinatesObj);
342 for(int i=0; i<nTuples; i++)
343 {
344 json_object* poPair = json_object_array_get_idx(poCoordinatesObj, i);
345 double dfX = 0.0, dfY = 0.0;
346 if( ParsePoint( poPair, &dfX, &dfY ) )
347 {
348 dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0;
349 dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1;
350 poMP->addGeometryDirectly(new OGRPoint(dfX, dfY));
351 }
352 }
353 }
354 else if( strcmp(pszType, "LineString") == 0 )
355 {
356 OGRLineString* poLS = new OGRLineString();
357 poGeom = poLS;
358 ParseLineString(poLS, poArcsObj, poArcsDB, psParams);
359 }
360 else if( strcmp(pszType, "MultiLineString") == 0 )
361 {
362 OGRMultiLineString* poMLS = new OGRMultiLineString();
363 poGeom = poMLS;
364 ParseMultiLineString(poMLS, poArcsObj, poArcsDB, psParams);
365 }
366 else if( strcmp(pszType, "Polygon") == 0 )
367 {
368 OGRPolygon* poPoly = new OGRPolygon();
369 poGeom = poPoly;
370 ParsePolygon(poPoly, poArcsObj, poArcsDB, psParams);
371 }
372 else if( strcmp(pszType, "MultiPolygon") == 0 )
373 {
374 OGRMultiPolygon* poMultiPoly = new OGRMultiPolygon();
375 poGeom = poMultiPoly;
376 ParseMultiPolygon(poMultiPoly, poArcsObj, poArcsDB, psParams);
377 }
378
379 if( poGeom != NULL )
380 poFeature->SetGeometryDirectly(poGeom);
381 poLayer->AddFeature(poFeature);
382 delete poFeature;
383 }
384
385
386 /************************************************************************/
387 /* EstablishLayerDefn() */
388 /************************************************************************/
389
EstablishLayerDefn(OGRFeatureDefn * poDefn,json_object * poObj)390 static void EstablishLayerDefn(OGRFeatureDefn* poDefn,
391 json_object* poObj)
392 {
393 json_object* poObjProps = OGRGeoJSONFindMemberByName( poObj, "properties" );
394 if( NULL != poObjProps &&
395 json_object_get_type(poObjProps) == json_type_object )
396 {
397 json_object_iter it;
398 it.key = NULL;
399 it.val = NULL;
400 it.entry = NULL;
401 json_object_object_foreachC( poObjProps, it )
402 {
403 OGRGeoJSONReaderAddOrUpdateField(poDefn, it.key, it.val, FALSE, 0);
404 }
405 }
406 }
407
408 /************************************************************************/
409 /* ParseObjectMain() */
410 /************************************************************************/
411
ParseObjectMain(const char * pszId,json_object * poObj,OGRGeoJSONDataSource * poDS,OGRGeoJSONLayer ** ppoMainLayer,json_object * poArcs,ScalingParams * psParams,int nPassNumber)412 static int ParseObjectMain(const char* pszId, json_object* poObj,
413 OGRGeoJSONDataSource* poDS,
414 OGRGeoJSONLayer **ppoMainLayer,
415 json_object* poArcs,
416 ScalingParams* psParams,
417 int nPassNumber)
418 {
419 int bNeedSecondPass = FALSE;
420
421 if( poObj != NULL && json_type_object == json_object_get_type( poObj ) )
422 {
423 json_object* poType = OGRGeoJSONFindMemberByName(poObj, "type");
424 if( poType != NULL && json_type_string == json_object_get_type( poType ) )
425 {
426 const char* pszType = json_object_get_string(poType);
427 if( nPassNumber == 1 && strcmp(pszType, "GeometryCollection") == 0 )
428 {
429 json_object* poGeometries = OGRGeoJSONFindMemberByName(poObj, "geometries");
430 if( poGeometries != NULL &&
431 json_type_array == json_object_get_type( poGeometries ) )
432 {
433 if( pszId == NULL )
434 {
435 json_object* poId = OGRGeoJSONFindMemberByName(poObj, "id");
436 if( poId != NULL &&
437 (json_type_string == json_object_get_type(poId) ||
438 json_type_int == json_object_get_type(poId)) )
439 {
440 pszId = json_object_get_string(poId);
441 }
442 }
443
444 OGRGeoJSONLayer* poLayer = new OGRGeoJSONLayer(
445 pszId ? pszId : "TopoJSON", NULL,
446 wkbUnknown, poDS );
447 OGRFeatureDefn* poDefn = poLayer->GetLayerDefn();
448 {
449 OGRFieldDefn fldDefn( "id", OFTString );
450 poDefn->AddFieldDefn( &fldDefn );
451 }
452
453 int nGeometries = json_object_array_length(poGeometries);
454 /* First pass to establish schema */
455 for(int i=0; i<nGeometries; i++)
456 {
457 json_object* poGeom =
458 json_object_array_get_idx(poGeometries, i);
459 if( poGeom != NULL &&
460 json_type_object == json_object_get_type( poGeom ) )
461 {
462 EstablishLayerDefn(poDefn, poGeom);
463 }
464 }
465
466 /* Second pass to build objects */
467 for(int i=0; i<nGeometries; i++)
468 {
469 json_object* poGeom =
470 json_object_array_get_idx(poGeometries, i);
471 if( poGeom != NULL &&
472 json_type_object == json_object_get_type( poGeom ) )
473 {
474 ParseObject(NULL, poGeom, poLayer, poArcs, psParams);
475 }
476 }
477
478 poDS->AddLayer(poLayer);
479 }
480 }
481 else if( strcmp(pszType, "Point") == 0 ||
482 strcmp(pszType, "MultiPoint") == 0 ||
483 strcmp(pszType, "LineString") == 0 ||
484 strcmp(pszType, "MultiLineString") == 0 ||
485 strcmp(pszType, "Polygon") == 0 ||
486 strcmp(pszType, "MultiPolygon") == 0 )
487 {
488 if( nPassNumber == 1 )
489 {
490 if( *ppoMainLayer == NULL )
491 {
492 *ppoMainLayer = new OGRGeoJSONLayer(
493 "TopoJSON", NULL, wkbUnknown, poDS );
494 {
495 OGRFieldDefn fldDefn( "id", OFTString );
496 (*ppoMainLayer)->GetLayerDefn()->AddFieldDefn( &fldDefn );
497 }
498 }
499 OGRFeatureDefn* poDefn = (*ppoMainLayer)->GetLayerDefn();
500 EstablishLayerDefn(poDefn, poObj);
501 bNeedSecondPass = TRUE;
502 }
503 else
504 ParseObject(pszId, poObj, *ppoMainLayer, poArcs, psParams);
505 }
506 }
507 }
508 return bNeedSecondPass;
509 }
510
511 /************************************************************************/
512 /* ReadLayers() */
513 /************************************************************************/
514
ReadLayers(OGRGeoJSONDataSource * poDS)515 void OGRTopoJSONReader::ReadLayers( OGRGeoJSONDataSource* poDS )
516 {
517 if( NULL == poGJObject_ )
518 {
519 CPLDebug( "TopoJSON",
520 "Missing parset TopoJSON data. Forgot to call Parse()?" );
521 return;
522 }
523
524 ScalingParams sParams;
525 sParams.dfScale0 = 1.0;
526 sParams.dfScale1 = 1.0;
527 sParams.dfTranslate0 = 0.0;
528 sParams.dfTranslate1 = 0.0;
529 json_object* poObjTransform = OGRGeoJSONFindMemberByName( poGJObject_, "transform" );
530 if( NULL != poObjTransform && json_type_object == json_object_get_type( poObjTransform ) )
531 {
532 json_object* poObjScale = OGRGeoJSONFindMemberByName( poObjTransform, "scale" );
533 if( NULL != poObjScale && json_type_array == json_object_get_type( poObjScale ) &&
534 json_object_array_length( poObjScale ) == 2 )
535 {
536 json_object* poScale0 = json_object_array_get_idx(poObjScale, 0);
537 json_object* poScale1 = json_object_array_get_idx(poObjScale, 1);
538 if( poScale0 != NULL &&
539 (json_object_get_type(poScale0) == json_type_double ||
540 json_object_get_type(poScale0) == json_type_int) &&
541 poScale1 != NULL &&
542 (json_object_get_type(poScale1) == json_type_double ||
543 json_object_get_type(poScale1) == json_type_int) )
544 {
545 sParams.dfScale0 = json_object_get_double(poScale0);
546 sParams.dfScale1 = json_object_get_double(poScale1);
547 }
548 }
549
550 json_object* poObjTranslate = OGRGeoJSONFindMemberByName( poObjTransform, "translate" );
551 if( NULL != poObjTranslate && json_type_array == json_object_get_type( poObjTranslate ) &&
552 json_object_array_length( poObjTranslate ) == 2 )
553 {
554 json_object* poTranslate0 = json_object_array_get_idx(poObjTranslate, 0);
555 json_object* poTranslate1 = json_object_array_get_idx(poObjTranslate, 1);
556 if( poTranslate0 != NULL &&
557 (json_object_get_type(poTranslate0) == json_type_double ||
558 json_object_get_type(poTranslate0) == json_type_int) &&
559 poTranslate1 != NULL &&
560 (json_object_get_type(poTranslate1) == json_type_double ||
561 json_object_get_type(poTranslate1) == json_type_int) )
562 {
563 sParams.dfTranslate0 = json_object_get_double(poTranslate0);
564 sParams.dfTranslate1 = json_object_get_double(poTranslate1);
565 }
566 }
567 }
568
569 json_object* poArcs = OGRGeoJSONFindMemberByName( poGJObject_, "arcs" );
570 if( poArcs == NULL || json_type_array != json_object_get_type( poArcs ) )
571 return;
572
573 OGRGeoJSONLayer* poMainLayer = NULL;
574
575 json_object* poObjects = OGRGeoJSONFindMemberByName( poGJObject_, "objects" );
576 if( poObjects == NULL )
577 return;
578
579 if( json_type_object == json_object_get_type( poObjects ) )
580 {
581 json_object_iter it;
582 it.key = NULL;
583 it.val = NULL;
584 it.entry = NULL;
585 int bNeedSecondPass = FALSE;
586 json_object_object_foreachC( poObjects, it )
587 {
588 json_object* poObj = it.val;
589 bNeedSecondPass |= ParseObjectMain(it.key, poObj, poDS, &poMainLayer, poArcs, &sParams, 1);
590 }
591 if( bNeedSecondPass )
592 {
593 it.key = NULL;
594 it.val = NULL;
595 it.entry = NULL;
596 json_object_object_foreachC( poObjects, it )
597 {
598 json_object* poObj = it.val;
599 ParseObjectMain(it.key, poObj, poDS, &poMainLayer, poArcs, &sParams, 2);
600 }
601 }
602 }
603 else if( json_type_array == json_object_get_type( poObjects ) )
604 {
605 int nObjects = json_object_array_length(poObjects);
606 int bNeedSecondPass = FALSE;
607 for(int i=0; i<nObjects; i++)
608 {
609 json_object* poObj = json_object_array_get_idx(poObjects, i);
610 bNeedSecondPass |= ParseObjectMain(NULL, poObj, poDS, &poMainLayer, poArcs, &sParams, 1);
611 }
612 if( bNeedSecondPass )
613 {
614 for(int i=0; i<nObjects; i++)
615 {
616 json_object* poObj = json_object_array_get_idx(poObjects, i);
617 ParseObjectMain(NULL, poObj, poDS, &poMainLayer, poArcs, &sParams, 2);
618 }
619 }
620 }
621
622 if( poMainLayer != NULL )
623 poDS->AddLayer(poMainLayer);
624
625 }
626