PostGIS  2.1.10dev-r@@SVN_REVISION@@
lwin_geojson.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  *
5  * Copyright 2013 Sandro Santilli <strk@keybit.net>
6  * Copyright 2011 Kashif Rasul <kashif.rasul@gmail.com>
7  *
8  * This is free software; you can redistribute and/or modify it under
9  * the terms of the GNU General Public Licence. See the COPYING file.
10  *
11  **********************************************************************/
12 
13 #include <assert.h>
14 #include "liblwgeom.h"
15 #include "lwgeom_log.h"
16 #include "../postgis_config.h"
17 
18 #ifdef HAVE_LIBJSON
19 
20 #include <string.h>
21 
22 #ifdef HAVE_LIBJSON_C
23 #include <json-c/json.h>
24 #include <json-c/json_object_private.h>
25 #else
26 #include <json/json.h>
27 #include <json/json_object_private.h>
28 #endif
29 
30 #ifndef JSON_C_VERSION
31 // Adds support for libjson < 0.10
32 # define json_tokener_error_desc(x) json_tokener_errors[(x)]
33 #endif
34 
35 
36 static void geojson_lwerror(char *msg, int error_code)
37 {
38  LWDEBUGF(3, "lwgeom_from_geojson ERROR %i", error_code);
39  lwerror("%s", msg);
40 }
41 
42 /* Prototype */
43 static LWGEOM* parse_geojson(json_object *geojson, int *hasz, int root_srid);
44 
45 static json_object*
46 findMemberByName(json_object* poObj, const char* pszName )
47 {
48  json_object* poTmp;
49  json_object_iter it;
50 
51  poTmp = poObj;
52 
53  if( NULL == pszName || NULL == poObj)
54  return NULL;
55 
56  it.key = NULL;
57  it.val = NULL;
58  it.entry = NULL;
59 
60  if( NULL != json_object_get_object(poTmp) )
61  {
62  if( NULL == json_object_get_object(poTmp)->head )
63  {
64  geojson_lwerror("invalid GeoJSON representation", 2);
65  return NULL;
66  }
67 
68  for( it.entry = json_object_get_object(poTmp)->head;
69  ( it.entry ?
70  ( it.key = (char*)it.entry->k,
71  it.val = (json_object*)it.entry->v, it.entry) : 0);
72  it.entry = it.entry->next)
73  {
74  if( strcasecmp((char *)it.key, pszName )==0 )
75  return it.val;
76  }
77  }
78 
79  return NULL;
80 }
81 
82 
83 static int
84 parse_geojson_coord(json_object *poObj, int *hasz, POINTARRAY *pa)
85 {
86  POINT4D pt;
87 
88  LWDEBUGF(3, "parse_geojson_coord called for object %s.", json_object_to_json_string( poObj ) );
89 
90  if( json_type_array == json_object_get_type( poObj ) )
91  {
92 
93  json_object* poObjCoord = NULL;
94  const int nSize = json_object_array_length( poObj );
95  LWDEBUGF(3, "parse_geojson_coord called for array size %d.", nSize );
96 
97  if ( nSize < 2 )
98  {
99  geojson_lwerror("Too few ordinates in GeoJSON", 4);
100  return LW_FAILURE;
101  }
102 
103  // Read X coordinate
104  poObjCoord = json_object_array_get_idx( poObj, 0 );
105  pt.x = json_object_get_double( poObjCoord );
106  LWDEBUGF(3, "parse_geojson_coord pt.x = %f.", pt.x );
107 
108  // Read Y coordinate
109  poObjCoord = json_object_array_get_idx( poObj, 1 );
110  pt.y = json_object_get_double( poObjCoord );
111  LWDEBUGF(3, "parse_geojson_coord pt.y = %f.", pt.y );
112 
113  if( nSize > 2 ) /* should this be >= 3 ? */
114  {
115  // Read Z coordinate
116  poObjCoord = json_object_array_get_idx( poObj, 2 );
117  pt.z = json_object_get_double( poObjCoord );
118  LWDEBUGF(3, "parse_geojson_coord pt.z = %f.", pt.z );
119  *hasz = LW_TRUE;
120  }
121  else if ( nSize == 2 )
122  {
123  *hasz = LW_FALSE;
124  /* Initialize Z coordinate, if required */
125  if ( FLAGS_GET_Z(pa->flags) ) pt.z = 0.0;
126  }
127  else
128  {
129  /* TODO: should we account for nSize > 3 ? */
130  /* more than 3 coordinates, we're just dropping dimensions here... */
131  }
132 
133  /* Initialize M coordinate, if required */
134  if ( FLAGS_GET_M(pa->flags) ) pt.m = 0.0;
135 
136  }
137  else
138  {
139  /* If it's not an array, just don't handle it */
140  return LW_FAILURE;
141  }
142 
143  return ptarray_append_point(pa, &pt, LW_TRUE);
144 }
145 
146 static LWGEOM*
147 parse_geojson_point(json_object *geojson, int *hasz, int root_srid)
148 {
149  LWGEOM *geom;
150  POINTARRAY *pa;
151  json_object* coords = NULL;
152 
153  LWDEBUGF(3, "parse_geojson_point called with root_srid = %d.", root_srid );
154 
155  coords = findMemberByName( geojson, "coordinates" );
156  if ( ! coords )
157  {
158  geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
159  return NULL;
160  }
161 
162  pa = ptarray_construct_empty(1, 0, 1);
163  parse_geojson_coord(coords, hasz, pa);
164 
165  geom = (LWGEOM *) lwpoint_construct(root_srid, NULL, pa);
166  LWDEBUG(2, "parse_geojson_point finished.");
167  return geom;
168 }
169 
170 static LWGEOM*
171 parse_geojson_linestring(json_object *geojson, int *hasz, int root_srid)
172 {
173  LWGEOM *geom;
174  POINTARRAY *pa;
175  json_object* points = NULL;
176  int i = 0;
177 
178  LWDEBUG(2, "parse_geojson_linestring called.");
179 
180  points = findMemberByName( geojson, "coordinates" );
181  if ( ! points ) {
182  geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
183  return NULL;
184  }
185 
186  pa = ptarray_construct_empty(1, 0, 1);
187 
188  if( json_type_array == json_object_get_type( points ) )
189  {
190  const int nPoints = json_object_array_length( points );
191  for(i = 0; i < nPoints; ++i)
192  {
193  json_object* coords = NULL;
194  coords = json_object_array_get_idx( points, i );
195  parse_geojson_coord(coords, hasz, pa);
196  }
197  }
198 
199  geom = (LWGEOM *) lwline_construct(root_srid, NULL, pa);
200 
201  LWDEBUG(2, "parse_geojson_linestring finished.");
202  return geom;
203 }
204 
205 static LWGEOM*
206 parse_geojson_polygon(json_object *geojson, int *hasz, int root_srid)
207 {
208  POINTARRAY **ppa = NULL;
209  json_object* rings = NULL;
210  json_object* points = NULL;
211  int i = 0, j = 0;
212  int nRings = 0, nPoints = 0;
213 
214  rings = findMemberByName( geojson, "coordinates" );
215  if ( ! rings )
216  {
217  geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
218  return NULL;
219  }
220 
221  if ( json_type_array != json_object_get_type(rings) )
222  {
223  geojson_lwerror("The 'coordinates' in GeoJSON are not an array", 4);
224  return NULL;
225  }
226 
227  nRings = json_object_array_length( rings );
228 
229  /* No rings => POLYGON EMPTY */
230  if ( ! nRings )
231  {
232  return (LWGEOM *)lwpoly_construct_empty(root_srid, 0, 0);
233  }
234 
235  for ( i = 0; i < nRings; i++ )
236  {
237  points = json_object_array_get_idx(rings, i);
238  if ( ! points || json_object_get_type(points) != json_type_array )
239  {
240  geojson_lwerror("The 'coordinates' in GeoJSON ring are not an array", 4);
241  return NULL;
242  }
243  nPoints = json_object_array_length(points);
244 
245  /* Skip empty rings */
246  if ( nPoints == 0 ) continue;
247 
248  if ( ! ppa )
249  ppa = (POINTARRAY**)lwalloc(sizeof(POINTARRAY*) * nRings);
250 
251  ppa[i] = ptarray_construct_empty(1, 0, 1);
252  for ( j = 0; j < nPoints; j++ )
253  {
254  json_object* coords = NULL;
255  coords = json_object_array_get_idx( points, j );
256  parse_geojson_coord(coords, hasz, ppa[i]);
257  }
258  }
259 
260  /* All the rings were empty! */
261  if ( ! ppa )
262  return (LWGEOM *)lwpoly_construct_empty(root_srid, 0, 0);
263 
264  return (LWGEOM *) lwpoly_construct(root_srid, NULL, nRings, ppa);
265 }
266 
267 static LWGEOM*
268 parse_geojson_multipoint(json_object *geojson, int *hasz, int root_srid)
269 {
270  LWGEOM *geom;
271  int i = 0;
272  json_object* poObjPoints = NULL;
273 
274  if (!root_srid)
275  {
276  geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOINTTYPE, root_srid, 1, 0);
277  }
278  else
279  {
281  }
282 
283  poObjPoints = findMemberByName( geojson, "coordinates" );
284  if ( ! poObjPoints ) {
285  geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
286  return NULL;
287  }
288 
289  if( json_type_array == json_object_get_type( poObjPoints ) )
290  {
291  const int nPoints = json_object_array_length( poObjPoints );
292  for( i = 0; i < nPoints; ++i)
293  {
294  POINTARRAY *pa;
295  json_object* poObjCoords = NULL;
296  poObjCoords = json_object_array_get_idx( poObjPoints, i );
297 
298  pa = ptarray_construct_empty(1, 0, 1);
299  parse_geojson_coord(poObjCoords, hasz, pa);
300 
301  geom = (LWGEOM*)lwmpoint_add_lwpoint((LWMPOINT*)geom,
302  (LWPOINT*)lwpoint_construct(root_srid, NULL, pa));
303  }
304  }
305 
306  return geom;
307 }
308 
309 static LWGEOM*
310 parse_geojson_multilinestring(json_object *geojson, int *hasz, int root_srid)
311 {
312  LWGEOM *geom = NULL;
313  int i, j;
314  json_object* poObjLines = NULL;
315 
316  if (!root_srid)
317  {
318  geom = (LWGEOM *)lwcollection_construct_empty(MULTILINETYPE, root_srid, 1, 0);
319  }
320  else
321  {
323  }
324 
325  poObjLines = findMemberByName( geojson, "coordinates" );
326  if ( ! poObjLines ) {
327  geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
328  return NULL;
329  }
330 
331  if( json_type_array == json_object_get_type( poObjLines ) )
332  {
333  const int nLines = json_object_array_length( poObjLines );
334  for( i = 0; i < nLines; ++i)
335  {
336  POINTARRAY *pa = NULL;
337  json_object* poObjLine = NULL;
338  poObjLine = json_object_array_get_idx( poObjLines, i );
339  pa = ptarray_construct_empty(1, 0, 1);
340 
341  if( json_type_array == json_object_get_type( poObjLine ) )
342  {
343  const int nPoints = json_object_array_length( poObjLine );
344  for(j = 0; j < nPoints; ++j)
345  {
346  json_object* coords = NULL;
347  coords = json_object_array_get_idx( poObjLine, j );
348  parse_geojson_coord(coords, hasz, pa);
349  }
350 
351  geom = (LWGEOM*)lwmline_add_lwline((LWMLINE*)geom,
352  (LWLINE*)lwline_construct(root_srid, NULL, pa));
353  }
354  }
355  }
356 
357  return geom;
358 }
359 
360 static LWGEOM*
361 parse_geojson_multipolygon(json_object *geojson, int *hasz, int root_srid)
362 {
363  LWGEOM *geom = NULL;
364  int i, j, k;
365  json_object* poObjPolys = NULL;
366 
367  if (!root_srid)
368  {
369  geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOLYGONTYPE, root_srid, 1, 0);
370  }
371  else
372  {
374  }
375 
376  poObjPolys = findMemberByName( geojson, "coordinates" );
377  if ( ! poObjPolys )
378  {
379  geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
380  return NULL;
381  }
382 
383  if( json_type_array == json_object_get_type( poObjPolys ) )
384  {
385  const int nPolys = json_object_array_length( poObjPolys );
386 
387  for(i = 0; i < nPolys; ++i)
388  {
389  json_object* poObjPoly = json_object_array_get_idx( poObjPolys, i );
390 
391  if( json_type_array == json_object_get_type( poObjPoly ) )
392  {
393  LWPOLY *lwpoly = lwpoly_construct_empty(geom->srid, lwgeom_has_z(geom), lwgeom_has_m(geom));
394  int nRings = json_object_array_length( poObjPoly );
395 
396  for(j = 0; j < nRings; ++j)
397  {
398  json_object* points = json_object_array_get_idx( poObjPoly, j );
399 
400  if( json_type_array == json_object_get_type( points ) )
401  {
402 
403  POINTARRAY *pa = ptarray_construct_empty(1, 0, 1);
404 
405  int nPoints = json_object_array_length( points );
406  for ( k=0; k < nPoints; k++ )
407  {
408  json_object* coords = json_object_array_get_idx( points, k );
409  parse_geojson_coord(coords, hasz, pa);
410  }
411 
412  lwpoly_add_ring(lwpoly, pa);
413  }
414  }
415  geom = (LWGEOM*)lwmpoly_add_lwpoly((LWMPOLY*)geom, lwpoly);
416  }
417  }
418  }
419 
420  return geom;
421 }
422 
423 static LWGEOM*
424 parse_geojson_geometrycollection(json_object *geojson, int *hasz, int root_srid)
425 {
426  LWGEOM *geom = NULL;
427  int i;
428  json_object* poObjGeoms = NULL;
429 
430  if (!root_srid)
431  {
432  geom = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, root_srid, 1, 0);
433  }
434  else
435  {
437  }
438 
439  poObjGeoms = findMemberByName( geojson, "geometries" );
440  if ( ! poObjGeoms )
441  {
442  geojson_lwerror("Unable to find 'geometries' in GeoJSON string", 4);
443  return NULL;
444  }
445 
446  if( json_type_array == json_object_get_type( poObjGeoms ) )
447  {
448  const int nGeoms = json_object_array_length( poObjGeoms );
449  json_object* poObjGeom = NULL;
450  for(i = 0; i < nGeoms; ++i )
451  {
452  poObjGeom = json_object_array_get_idx( poObjGeoms, i );
454  parse_geojson(poObjGeom, hasz, root_srid));
455  }
456  }
457 
458  return geom;
459 }
460 
461 static LWGEOM*
462 parse_geojson(json_object *geojson, int *hasz, int root_srid)
463 {
464  json_object* type = NULL;
465  const char* name;
466 
467  if( NULL == geojson )
468  {
469  geojson_lwerror("invalid GeoJSON representation", 2);
470  return NULL;
471  }
472 
473  type = findMemberByName( geojson, "type" );
474  if( NULL == type )
475  {
476  geojson_lwerror("unknown GeoJSON type", 3);
477  return NULL;
478  }
479 
480  name = json_object_get_string( type );
481 
482  if( strcasecmp( name, "Point" )==0 )
483  return parse_geojson_point(geojson, hasz, root_srid);
484 
485  if( strcasecmp( name, "LineString" )==0 )
486  return parse_geojson_linestring(geojson, hasz, root_srid);
487 
488  if( strcasecmp( name, "Polygon" )==0 )
489  return parse_geojson_polygon(geojson, hasz, root_srid);
490 
491  if( strcasecmp( name, "MultiPoint" )==0 )
492  return parse_geojson_multipoint(geojson, hasz, root_srid);
493 
494  if( strcasecmp( name, "MultiLineString" )==0 )
495  return parse_geojson_multilinestring(geojson, hasz, root_srid);
496 
497  if( strcasecmp( name, "MultiPolygon" )==0 )
498  return parse_geojson_multipolygon(geojson, hasz, root_srid);
499 
500  if( strcasecmp( name, "GeometryCollection" )==0 )
501  return parse_geojson_geometrycollection(geojson, hasz, root_srid);
502 
503  lwerror("invalid GeoJson representation");
504  return NULL; /* Never reach */
505 }
506 
507 #endif /* HAVE_LIBJSON */
508 
509 LWGEOM*
510 lwgeom_from_geojson(const char *geojson, char **srs)
511 {
512 #ifndef HAVE_LIBJSON
513  *srs = NULL;
514  lwerror("You need JSON-C for lwgeom_from_geojson");
515  return NULL;
516 #else /* HAVE_LIBJSON */
517 
518  /* size_t geojson_size = strlen(geojson); */
519 
520  LWGEOM *lwgeom;
521  int hasz=LW_TRUE;
522  json_tokener* jstok = NULL;
523  json_object* poObj = NULL;
524  json_object* poObjSrs = NULL;
525  *srs = NULL;
526 
527  /* Begin to Parse json */
528  jstok = json_tokener_new();
529  poObj = json_tokener_parse_ex(jstok, geojson, -1);
530  if( jstok->err != json_tokener_success)
531  {
532  char err[256];
533  snprintf(err, 256, "%s (at offset %d)", json_tokener_error_desc(jstok->err), jstok->char_offset);
534  json_tokener_free(jstok);
535  json_object_put(poObj);
536  geojson_lwerror(err, 1);
537  return NULL;
538  }
539  json_tokener_free(jstok);
540 
541  poObjSrs = findMemberByName( poObj, "crs" );
542  if (poObjSrs != NULL)
543  {
544  json_object* poObjSrsType = findMemberByName( poObjSrs, "type" );
545  if (poObjSrsType != NULL)
546  {
547  json_object* poObjSrsProps = findMemberByName( poObjSrs, "properties" );
548  if ( poObjSrsProps )
549  {
550  json_object* poNameURL = findMemberByName( poObjSrsProps, "name" );
551  if ( poNameURL )
552  {
553  const char* pszName = json_object_get_string( poNameURL );
554  if ( pszName )
555  {
556  *srs = lwalloc(strlen(pszName) + 1);
557  strcpy(*srs, pszName);
558  }
559  }
560  }
561  }
562  }
563 
564  lwgeom = parse_geojson(poObj, &hasz, 0);
565  json_object_put(poObj);
566 
567  lwgeom_add_bbox(lwgeom);
568 
569  if (!hasz)
570  {
571  LWGEOM *tmp = lwgeom_force_2d(lwgeom);
572  lwgeom_free(lwgeom);
573  lwgeom = tmp;
574 
575  LWDEBUG(2, "geom_from_geojson called.");
576  }
577 
578  return lwgeom;
579 #endif /* HAVE_LIBJSON */
580 }
581 
582 
double x
Definition: liblwgeom.h:308
double m
Definition: liblwgeom.h:308
static LWGEOM * parse_geojson(json_object *geojson, int *hasz, int root_srid)
Definition: lwin_geojson.c:462
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition: ptarray.c:57
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1006
#define MULTIPOINTTYPE
Definition: liblwgeom.h:63
static LWGEOM * parse_geojson_multipolygon(json_object *geojson, int *hasz, int root_srid)
Definition: lwin_geojson.c:361
static void geojson_lwerror(char *msg, int error_code)
Definition: lwin_geojson.c:36
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:50
static int parse_geojson_coord(json_object *poObj, int *hasz, POINTARRAY *pa)
Definition: lwin_geojson.c:84
LWMLINE * lwmline_add_lwline(LWMLINE *mobj, const LWLINE *obj)
Definition: lwmline.c:33
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:792
int32_t srid
Definition: liblwgeom.h:355
#define LW_FAILURE
Definition: liblwgeom.h:54
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:67
static LWGEOM * parse_geojson_polygon(json_object *geojson, int *hasz, int root_srid)
Definition: lwin_geojson.c:206
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_TRUE, then a duplicate point will not be added.
Definition: ptarray.c:141
#define LW_FALSE
Definition: liblwgeom.h:52
uint8_t flags
Definition: liblwgeom.h:325
LWPOLY * lwpoly_construct(int srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition: lwpoly.c:29
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:51
LWLINE * lwline_construct(int srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:29
#define json_tokener_error_desc(x)
Definition: lwin_geojson.c:32
LWGEOM * lwgeom_force_2d(const LWGEOM *geom)
Strip out the Z/M components of an LWGEOM.
Definition: lwgeom.c:646
static LWGEOM * parse_geojson_multilinestring(json_object *geojson, int *hasz, int root_srid)
Definition: lwin_geojson.c:310
#define FLAGS_GET_Z(flags)
Macros for manipulating the 'flags' byte.
Definition: liblwgeom.h:106
double z
Definition: liblwgeom.h:308
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:65
static LWGEOM * parse_geojson_linestring(json_object *geojson, int *hasz, int root_srid)
Definition: lwin_geojson.c:171
static LWGEOM * parse_geojson_point(json_object *geojson, int *hasz, int root_srid)
Definition: lwin_geojson.c:147
static LWGEOM * parse_geojson_geometrycollection(json_object *geojson, int *hasz, int root_srid)
Definition: lwin_geojson.c:424
static json_object * findMemberByName(json_object *poObj, const char *pszName)
Definition: lwin_geojson.c:46
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition: lwmpoint.c:32
LWPOLY * lwpoly_construct_empty(int srid, char hasz, char hasm)
Definition: lwpoly.c:66
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition: lwgeom.c:555
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:107
int lwpoly_add_ring(LWPOLY *poly, POINTARRAY *pa)
Add a ring, allocating extra space if necessary.
Definition: lwpoly.c:154
LWPOINT * lwpoint_construct(int srid, GBOX *bbox, POINTARRAY *point)
Definition: lwpoint.c:96
void * lwalloc(size_t size)
Definition: lwutil.c:175
static LWGEOM * parse_geojson_multipoint(json_object *geojson, int *hasz, int root_srid)
Definition: lwin_geojson.c:268
double y
Definition: liblwgeom.h:308
#define MULTILINETYPE
Definition: liblwgeom.h:64
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int srid, char hasz, char hasm)
Definition: lwcollection.c:81
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:55
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:799
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:174
LWMPOLY * lwmpoly_add_lwpoly(LWMPOLY *mobj, const LWPOLY *obj)
Definition: lwmpoly.c:34
#define COLLECTIONTYPE
Definition: liblwgeom.h:66
This library is the generic geometry handling section of PostGIS.
LWGEOM * lwgeom_from_geojson(const char *geojson, char **srs)
Create an LWGEOM object from a GeoJSON representation.
Definition: lwin_geojson.c:510