PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwin_geojson.c
Go to the documentation of this file.
1/**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * PostGIS is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * PostGIS is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **********************************************************************
20 *
21 * Copyright 2019 Darafei Praliaskouski <me@komzpa.net>
22 * Copyright 2013 Sandro Santilli <strk@kbt.io>
23 * Copyright 2011 Kashif Rasul <kashif.rasul@gmail.com>
24 *
25 **********************************************************************/
26
27#include "liblwgeom.h"
28#include "lwgeom_log.h"
29#include "../postgis_config.h"
30
31#if defined(HAVE_LIBJSON)
32
33#define JSON_C_VERSION_013 (13 << 8)
34
35#include <json.h>
36
37#if !defined(JSON_C_VERSION_NUM) || JSON_C_VERSION_NUM < JSON_C_VERSION_013
38#include <json_object_private.h>
39#endif
40
41#ifndef JSON_C_VERSION
42/* Adds support for libjson < 0.10 */
43#define json_tokener_error_desc(x) json_tokener_errors[(x)]
44#endif
45
46#include <string.h>
47
48/* Prototype */
49static LWGEOM *parse_geojson(json_object *geojson, int *hasz);
50
51static inline json_object *
52findMemberByName(json_object *poObj, const char *pszName)
53{
54 json_object *poTmp;
55 json_object_iter it;
56
57 poTmp = poObj;
58
59 if (!pszName || !poObj)
60 return NULL;
61
62 it.key = NULL;
63 it.val = NULL;
64 it.entry = NULL;
65
66 if (json_object_get_object(poTmp))
67 {
68 if (!json_object_get_object(poTmp)->head)
69 {
70 lwerror("invalid GeoJSON representation");
71 return NULL;
72 }
73
74 for (it.entry = json_object_get_object(poTmp)->head;
75 (it.entry ? (it.key = (char *)it.entry->k, it.val = (json_object *)it.entry->v, it.entry) : 0);
76 it.entry = it.entry->next)
77 {
78 if (strcasecmp((char *)it.key, pszName) == 0)
79 return it.val;
80 }
81 }
82
83 return NULL;
84}
85
86static inline json_object *
87parse_coordinates(json_object *geojson)
88{
89 json_object *coordinates = findMemberByName(geojson, "coordinates");
90 if (!coordinates)
91 {
92 lwerror("Unable to find 'coordinates' in GeoJSON string");
93 return NULL;
94 }
95
96 if (json_type_array != json_object_get_type(coordinates))
97 {
98 lwerror("The 'coordinates' in GeoJSON are not an array");
99 return NULL;
100 }
101 return coordinates;
102}
103
104
105static inline int
106parse_geojson_coord(json_object *poObj, int *hasz, POINTARRAY *pa)
107{
108 POINT4D pt = {0, 0, 0, 0};
109
110 if (json_object_get_type(poObj) == json_type_array)
111 {
112 json_object *poObjCoord = NULL;
113 const int nSize = json_object_array_length(poObj);
114 if (nSize == 0)
115 return LW_TRUE;
116 if (nSize < 2)
117 {
118 lwerror("Too few ordinates in GeoJSON");
119 return LW_FAILURE;
120 }
121
122 /* Read X coordinate */
123 poObjCoord = json_object_array_get_idx(poObj, 0);
124 pt.x = json_object_get_double(poObjCoord);
125
126 /* Read Y coordinate */
127 poObjCoord = json_object_array_get_idx(poObj, 1);
128 pt.y = json_object_get_double(poObjCoord);
129
130 if (nSize > 2) /* should this be >= 3 ? */
131 {
132 /* Read Z coordinate */
133 poObjCoord = json_object_array_get_idx(poObj, 2);
134 pt.z = json_object_get_double(poObjCoord);
135 *hasz = LW_TRUE;
136 }
137 }
138 else
139 {
140 /* If it's not an array, just don't handle it */
141 lwerror("The 'coordinates' in GeoJSON are not sufficiently nested");
142 return LW_FAILURE;
143 }
144
145 return ptarray_append_point(pa, &pt, LW_TRUE);
146}
147
148static inline LWGEOM *
149parse_geojson_point(json_object *geojson, int *hasz)
150{
151 json_object *coords = parse_coordinates(geojson);
152 if (!coords)
153 return NULL;
154 POINTARRAY *pa = ptarray_construct_empty(1, 0, 1);
155 parse_geojson_coord(coords, hasz, pa);
156 return (LWGEOM *)lwpoint_construct(0, NULL, pa);
157}
158
159static inline LWGEOM *
160parse_geojson_linestring(json_object *geojson, int *hasz)
161{
162 json_object *points = parse_coordinates(geojson);
163 if (!points)
164 return NULL;
165 POINTARRAY *pa = ptarray_construct_empty(1, 0, 1);
166 const int nPoints = json_object_array_length(points);
167 for (int i = 0; i < nPoints; i++)
168 {
169 json_object *coords = json_object_array_get_idx(points, i);
170 parse_geojson_coord(coords, hasz, pa);
171 }
172 return (LWGEOM *)lwline_construct(0, NULL, pa);
173}
174
175static inline LWPOLY *
176parse_geojson_poly_rings(json_object *rings, int *hasz)
177{
178 if (!rings || json_object_get_type(rings) != json_type_array)
179 return NULL;
180
181 int nRings = json_object_array_length(rings);
182
183 /* No rings => POLYGON EMPTY */
184 if (!nRings)
185 return lwpoly_construct_empty(0, 1, 0);
186
187 /* Expecting up to nRings otherwise */
188 POINTARRAY **ppa = (POINTARRAY **)lwalloc(sizeof(POINTARRAY *) * nRings);
189 int o = 0;
190
191 for (int i = 0; i < nRings; i++)
192 {
193 json_object *points = json_object_array_get_idx(rings, i);
194 if (!points || json_object_get_type(points) != json_type_array)
195 {
196 for (int k = 0; k < o; k++)
197 ptarray_free(ppa[k]);
198 lwfree(ppa);
199 lwerror("The 'coordinates' in GeoJSON ring are not an array");
200 return NULL;
201 }
202 int nPoints = json_object_array_length(points);
203
204 /* Skip empty rings */
205 if (!nPoints)
206 {
207 /* Empty outer? Don't promote first hole to outer, holes don't matter. */
208 if (!i)
209 break;
210 else
211 continue;
212 }
213
214 ppa[o] = ptarray_construct_empty(1, 0, 1);
215 for (int j = 0; j < nPoints; j++)
216 {
217 json_object *coords = NULL;
218 coords = json_object_array_get_idx(points, j);
219 if (LW_FAILURE == parse_geojson_coord(coords, hasz, ppa[o]))
220 {
221 for (int k = 0; k <= o; k++)
222 ptarray_free(ppa[k]);
223 lwfree(ppa);
224 lwerror("The 'coordinates' in GeoJSON are not sufficiently nested");
225 return NULL;
226 }
227 }
228 o++;
229 }
230
231 /* All the rings were empty! */
232 if (!o)
233 {
234 lwfree(ppa);
235 return lwpoly_construct_empty(0, 1, 0);
236 }
237
238 return lwpoly_construct(0, NULL, o, ppa);
239}
240
241static inline LWGEOM *
242parse_geojson_polygon(json_object *geojson, int *hasz)
243{
244 return (LWGEOM *)parse_geojson_poly_rings(parse_coordinates(geojson), hasz);
245}
246
247static inline LWGEOM *
248parse_geojson_multipoint(json_object *geojson, int *hasz)
249{
250 json_object *points = parse_coordinates(geojson);
251 if (!points)
252 return NULL;
254
255 const int nPoints = json_object_array_length(points);
256 for (int i = 0; i < nPoints; ++i)
257 {
258 POINTARRAY *pa = ptarray_construct_empty(1, 0, 1);
259 json_object *coord = json_object_array_get_idx(points, i);
260 if (parse_geojson_coord(coord, hasz, pa))
261 geom = lwmpoint_add_lwpoint(geom, lwpoint_construct(0, NULL, pa));
262 else
263 {
264 lwmpoint_free(geom);
265 ptarray_free(pa);
266 return NULL;
267 }
268 }
269
270 return (LWGEOM *)geom;
271}
272
273static inline LWGEOM *
274parse_geojson_multilinestring(json_object *geojson, int *hasz)
275{
276 json_object *mls = parse_coordinates(geojson);
277 if (!mls)
278 return NULL;
280 const int nLines = json_object_array_length(mls);
281 for (int i = 0; i < nLines; ++i)
282 {
283 POINTARRAY *pa = ptarray_construct_empty(1, 0, 1);
284 json_object *coords = json_object_array_get_idx(mls, i);
285
286 if (json_type_array == json_object_get_type(coords))
287 {
288 const int nPoints = json_object_array_length(coords);
289 for (int j = 0; j < nPoints; ++j)
290 {
291 json_object *coord = json_object_array_get_idx(coords, j);
292 if (!parse_geojson_coord(coord, hasz, pa))
293 {
294 lwmline_free(geom);
295 ptarray_free(pa);
296 return NULL;
297 }
298 }
299 geom = lwmline_add_lwline(geom, lwline_construct(0, NULL, pa));
300 }
301 else
302 {
303 lwmline_free(geom);
304 ptarray_free(pa);
305 return NULL;
306 }
307 }
308 return (LWGEOM *)geom;
309}
310
311static inline LWGEOM *
312parse_geojson_multipolygon(json_object *geojson, int *hasz)
313{
314 json_object *polys = parse_coordinates(geojson);
315 if (!polys)
316 return NULL;
318 int nPolys = json_object_array_length(polys);
319
320 for (int i = 0; i < nPolys; ++i)
321 {
322 json_object *rings = json_object_array_get_idx(polys, i);
323 LWPOLY *poly = parse_geojson_poly_rings(rings, hasz);
324 if (poly)
325 geom = (LWGEOM *)lwmpoly_add_lwpoly((LWMPOLY *)geom, poly);
326 }
327
328 return geom;
329}
330
331static inline LWGEOM *
332parse_geojson_geometrycollection(json_object *geojson, int *hasz)
333{
334 json_object *poObjGeoms = findMemberByName(geojson, "geometries");
335 if (!poObjGeoms)
336 {
337 lwerror("Unable to find 'geometries' in GeoJSON string");
338 return NULL;
339 }
341
342 if (json_type_array == json_object_get_type(poObjGeoms))
343 {
344 const int nGeoms = json_object_array_length(poObjGeoms);
345 for (int i = 0; i < nGeoms; ++i)
346 {
347 json_object *poObjGeom = json_object_array_get_idx(poObjGeoms, i);
348 LWGEOM *t = parse_geojson(poObjGeom, hasz);
349 if (t)
350 geom = (LWGEOM *)lwcollection_add_lwgeom((LWCOLLECTION *)geom, t);
351 else
352 {
353 lwgeom_free(geom);
354 return NULL;
355 }
356 }
357 }
358
359 return geom;
360}
361
362static inline LWGEOM *
363parse_geojson(json_object *geojson, int *hasz)
364{
365 json_object *type = NULL;
366 const char *name;
367
368 if (!geojson)
369 {
370 lwerror("invalid GeoJSON representation");
371 return NULL;
372 }
373
374 type = findMemberByName(geojson, "type");
375 if (!type)
376 {
377 lwerror("unknown GeoJSON type");
378 return NULL;
379 }
380
381 name = json_object_get_string(type);
382
383 if (strcasecmp(name, "Point") == 0)
384 return parse_geojson_point(geojson, hasz);
385
386 if (strcasecmp(name, "LineString") == 0)
387 return parse_geojson_linestring(geojson, hasz);
388
389 if (strcasecmp(name, "Polygon") == 0)
390 return parse_geojson_polygon(geojson, hasz);
391
392 if (strcasecmp(name, "MultiPoint") == 0)
393 return parse_geojson_multipoint(geojson, hasz);
394
395 if (strcasecmp(name, "MultiLineString") == 0)
396 return parse_geojson_multilinestring(geojson, hasz);
397
398 if (strcasecmp(name, "MultiPolygon") == 0)
399 return parse_geojson_multipolygon(geojson, hasz);
400
401 if (strcasecmp(name, "GeometryCollection") == 0)
402 return parse_geojson_geometrycollection(geojson, hasz);
403
404 lwerror("invalid GeoJson representation");
405 return NULL; /* Never reach */
406}
407
408#endif /* HAVE_LIBJSON */
409
410LWGEOM *
411lwgeom_from_geojson(const char *geojson, char **srs)
412{
413#ifndef HAVE_LIBJSON
414 *srs = NULL;
415 lwerror("You need JSON-C for lwgeom_from_geojson");
416 return NULL;
417#else /* HAVE_LIBJSON */
418
419 /* Begin to Parse json */
420 json_tokener *jstok = json_tokener_new();
421 json_object *poObj = json_tokener_parse_ex(jstok, geojson, -1);
422 if (jstok->err != json_tokener_success)
423 {
424 char err[256];
425 snprintf(err, 256, "%s (at offset %d)", json_tokener_error_desc(jstok->err), jstok->char_offset);
426 json_tokener_free(jstok);
427 json_object_put(poObj);
428 lwerror("%s", err);
429 return NULL;
430 }
431 json_tokener_free(jstok);
432
433 *srs = NULL;
434 json_object *poObjSrs = findMemberByName(poObj, "crs");
435 if (poObjSrs != NULL)
436 {
437 json_object *poObjSrsType = findMemberByName(poObjSrs, "type");
438 if (poObjSrsType != NULL)
439 {
440 json_object *poObjSrsProps = findMemberByName(poObjSrs, "properties");
441 if (poObjSrsProps)
442 {
443 json_object *poNameURL = findMemberByName(poObjSrsProps, "name");
444 if (poNameURL)
445 {
446 const char *pszName = json_object_get_string(poNameURL);
447 if (pszName)
448 {
449 *srs = lwalloc(strlen(pszName) + 1);
450 strcpy(*srs, pszName);
451 }
452 }
453 }
454 }
455 }
456
457 int hasz = LW_FALSE;
458 LWGEOM *lwgeom = parse_geojson(poObj, &hasz);
459 json_object_put(poObj);
460 if (!lwgeom)
461 return NULL;
462
463 if (!hasz)
464 {
465 LWGEOM *tmp = lwgeom_force_2d(lwgeom);
466 lwgeom_free(lwgeom);
467 lwgeom = tmp;
468 }
469 lwgeom_add_bbox(lwgeom);
470 return lwgeom;
471#endif /* HAVE_LIBJSON */
472}
#define LW_FALSE
Definition liblwgeom.h:94
#define COLLECTIONTYPE
Definition liblwgeom.h:108
void lwmpoint_free(LWMPOINT *mpt)
Definition lwmpoint.c:72
#define LW_FAILURE
Definition liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1246
#define MULTILINETYPE
Definition liblwgeom.h:106
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition lwpoint.c:129
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition lwmpoint.c:45
#define MULTIPOINTTYPE
Definition liblwgeom.h:105
LWGEOM * lwgeom_force_2d(const LWGEOM *geom)
Strip out the Z/M components of an LWGEOM.
Definition lwgeom.c:821
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition ptarray.c:59
void * lwalloc(size_t size)
Definition lwutil.c:227
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwline.c:42
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
void lwfree(void *mem)
Definition lwutil.c:248
LWMLINE * lwmline_add_lwline(LWMLINE *mobj, const LWLINE *obj)
Definition lwmline.c:46
LWMPOLY * lwmpoly_add_lwpoly(LWMPOLY *mobj, const LWPOLY *obj)
Definition lwmpoly.c:47
void ptarray_free(POINTARRAY *pa)
Definition ptarray.c:327
LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition lwpoly.c:43
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
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_FALSE,...
Definition ptarray.c:147
void lwmline_free(LWMLINE *mline)
Definition lwmline.c:112
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:93
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition lwpoly.c:161
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition lwgeom.c:723
This library is the generic geometry handling section of PostGIS.
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static LWGEOM * parse_geojson_multipoint(json_object *geojson, int *hasz)
#define json_tokener_error_desc(x)
static LWGEOM * parse_geojson(json_object *geojson, int *hasz)
static LWGEOM * parse_geojson_multipolygon(json_object *geojson, int *hasz)
static json_object * parse_coordinates(json_object *geojson)
static int parse_geojson_coord(json_object *poObj, int *hasz, POINTARRAY *pa)
static LWGEOM * parse_geojson_linestring(json_object *geojson, int *hasz)
static LWGEOM * parse_geojson_geometrycollection(json_object *geojson, int *hasz)
static json_object * findMemberByName(json_object *poObj, const char *pszName)
static LWGEOM * parse_geojson_point(json_object *geojson, int *hasz)
static LWPOLY * parse_geojson_poly_rings(json_object *rings, int *hasz)
static LWGEOM * parse_geojson_polygon(json_object *geojson, int *hasz)
static LWGEOM * parse_geojson_multilinestring(json_object *geojson, int *hasz)
LWGEOM * lwgeom_from_geojson(const char *geojson, char **srs)
Create an LWGEOM object from a GeoJSON representation.
double x
Definition liblwgeom.h:414
double z
Definition liblwgeom.h:414
double y
Definition liblwgeom.h:414