PostGIS 3.0.6dev-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 < 2)
115 {
116 lwerror("Too few ordinates in GeoJSON");
117 return LW_FAILURE;
118 }
119
120 /* Read X coordinate */
121 poObjCoord = json_object_array_get_idx(poObj, 0);
122 pt.x = json_object_get_double(poObjCoord);
123
124 /* Read Y coordinate */
125 poObjCoord = json_object_array_get_idx(poObj, 1);
126 pt.y = json_object_get_double(poObjCoord);
127
128 if (nSize > 2) /* should this be >= 3 ? */
129 {
130 /* Read Z coordinate */
131 poObjCoord = json_object_array_get_idx(poObj, 2);
132 pt.z = json_object_get_double(poObjCoord);
133 *hasz = LW_TRUE;
134 }
135 }
136 else
137 {
138 /* If it's not an array, just don't handle it */
139 lwerror("The 'coordinates' in GeoJSON are not sufficiently nested");
140 return LW_FAILURE;
141 }
142
143 return ptarray_append_point(pa, &pt, LW_TRUE);
144}
145
146static inline LWGEOM *
147parse_geojson_point(json_object *geojson, int *hasz)
148{
149 json_object *coords = parse_coordinates(geojson);
150 if (!coords)
151 return NULL;
152 POINTARRAY *pa = ptarray_construct_empty(1, 0, 1);
153 parse_geojson_coord(coords, hasz, pa);
154 return (LWGEOM *)lwpoint_construct(0, NULL, pa);
155}
156
157static inline LWGEOM *
158parse_geojson_linestring(json_object *geojson, int *hasz)
159{
160 json_object *points = parse_coordinates(geojson);
161 if (!points)
162 return NULL;
163 POINTARRAY *pa = ptarray_construct_empty(1, 0, 1);
164 const int nPoints = json_object_array_length(points);
165 for (int i = 0; i < nPoints; i++)
166 {
167 json_object *coords = json_object_array_get_idx(points, i);
168 parse_geojson_coord(coords, hasz, pa);
169 }
170 return (LWGEOM *)lwline_construct(0, NULL, pa);
171}
172
173static inline LWPOLY *
174parse_geojson_poly_rings(json_object *rings, int *hasz)
175{
176 if (!rings || json_object_get_type(rings) != json_type_array)
177 return NULL;
178
179 int nRings = json_object_array_length(rings);
180
181 /* No rings => POLYGON EMPTY */
182 if (!nRings)
183 return lwpoly_construct_empty(0, 1, 0);
184
185 /* Expecting up to nRings otherwise */
186 POINTARRAY **ppa = (POINTARRAY **)lwalloc(sizeof(POINTARRAY *) * nRings);
187 int o = 0;
188
189 for (int i = 0; i < nRings; i++)
190 {
191 json_object *points = json_object_array_get_idx(rings, i);
192 if (!points || json_object_get_type(points) != json_type_array)
193 {
194 for (int k = 0; k < o; k++)
195 ptarray_free(ppa[k]);
196 lwfree(ppa);
197 lwerror("The 'coordinates' in GeoJSON ring are not an array");
198 return NULL;
199 }
200 int nPoints = json_object_array_length(points);
201
202 /* Skip empty rings */
203 if (!nPoints)
204 {
205 /* Empty outer? Don't promote first hole to outer, holes don't matter. */
206 if (!i)
207 break;
208 else
209 continue;
210 }
211
212 ppa[o] = ptarray_construct_empty(1, 0, 1);
213 for (int j = 0; j < nPoints; j++)
214 {
215 json_object *coords = NULL;
216 coords = json_object_array_get_idx(points, j);
217 if (LW_FAILURE == parse_geojson_coord(coords, hasz, ppa[o]))
218 {
219 for (int k = 0; k <= o; k++)
220 ptarray_free(ppa[k]);
221 lwfree(ppa);
222 lwerror("The 'coordinates' in GeoJSON are not sufficiently nested");
223 return NULL;
224 }
225 }
226 o++;
227 }
228
229 /* All the rings were empty! */
230 if (!o)
231 {
232 lwfree(ppa);
233 return lwpoly_construct_empty(0, 1, 0);
234 }
235
236 return lwpoly_construct(0, NULL, o, ppa);
237}
238
239static inline LWGEOM *
240parse_geojson_polygon(json_object *geojson, int *hasz)
241{
242 return (LWGEOM *)parse_geojson_poly_rings(parse_coordinates(geojson), hasz);
243}
244
245static inline LWGEOM *
246parse_geojson_multipoint(json_object *geojson, int *hasz)
247{
248 json_object *points = parse_coordinates(geojson);
249 if (!points)
250 return NULL;
252
253 const int nPoints = json_object_array_length(points);
254 for (int i = 0; i < nPoints; ++i)
255 {
256 POINTARRAY *pa = ptarray_construct_empty(1, 0, 1);
257 json_object *coord = json_object_array_get_idx(points, i);
258 if (parse_geojson_coord(coord, hasz, pa))
259 geom = lwmpoint_add_lwpoint(geom, lwpoint_construct(0, NULL, pa));
260 else
261 {
262 lwmpoint_free(geom);
263 ptarray_free(pa);
264 return NULL;
265 }
266 }
267
268 return (LWGEOM *)geom;
269}
270
271static inline LWGEOM *
272parse_geojson_multilinestring(json_object *geojson, int *hasz)
273{
274 json_object *mls = parse_coordinates(geojson);
275 if (!mls)
276 return NULL;
278 const int nLines = json_object_array_length(mls);
279 for (int i = 0; i < nLines; ++i)
280 {
281 POINTARRAY *pa = ptarray_construct_empty(1, 0, 1);
282 json_object *coords = json_object_array_get_idx(mls, i);
283
284 if (json_type_array == json_object_get_type(coords))
285 {
286 const int nPoints = json_object_array_length(coords);
287 for (int j = 0; j < nPoints; ++j)
288 {
289 json_object *coord = json_object_array_get_idx(coords, j);
290 if (!parse_geojson_coord(coord, hasz, pa))
291 {
292 lwmline_free(geom);
293 ptarray_free(pa);
294 return NULL;
295 }
296 }
297 geom = lwmline_add_lwline(geom, lwline_construct(0, NULL, pa));
298 }
299 else
300 {
301 lwmline_free(geom);
302 ptarray_free(pa);
303 return NULL;
304 }
305 }
306 return (LWGEOM *)geom;
307}
308
309static inline LWGEOM *
310parse_geojson_multipolygon(json_object *geojson, int *hasz)
311{
312 json_object *polys = parse_coordinates(geojson);
313 if (!polys)
314 return NULL;
316 int nPolys = json_object_array_length(polys);
317
318 for (int i = 0; i < nPolys; ++i)
319 {
320 json_object *rings = json_object_array_get_idx(polys, i);
321 LWPOLY *poly = parse_geojson_poly_rings(rings, hasz);
322 if (poly)
323 geom = (LWGEOM *)lwmpoly_add_lwpoly((LWMPOLY *)geom, poly);
324 }
325
326 return geom;
327}
328
329static inline LWGEOM *
330parse_geojson_geometrycollection(json_object *geojson, int *hasz)
331{
332 json_object *poObjGeoms = findMemberByName(geojson, "geometries");
333 if (!poObjGeoms)
334 {
335 lwerror("Unable to find 'geometries' in GeoJSON string");
336 return NULL;
337 }
339
340 if (json_type_array == json_object_get_type(poObjGeoms))
341 {
342 const int nGeoms = json_object_array_length(poObjGeoms);
343 for (int i = 0; i < nGeoms; ++i)
344 {
345 json_object *poObjGeom = json_object_array_get_idx(poObjGeoms, i);
346 LWGEOM *t = parse_geojson(poObjGeom, hasz);
347 if (t)
348 geom = (LWGEOM *)lwcollection_add_lwgeom((LWCOLLECTION *)geom, t);
349 else
350 {
351 lwgeom_free(geom);
352 return NULL;
353 }
354 }
355 }
356
357 return geom;
358}
359
360static inline LWGEOM *
361parse_geojson(json_object *geojson, int *hasz)
362{
363 json_object *type = NULL;
364 const char *name;
365
366 if (!geojson)
367 {
368 lwerror("invalid GeoJSON representation");
369 return NULL;
370 }
371
372 type = findMemberByName(geojson, "type");
373 if (!type)
374 {
375 lwerror("unknown GeoJSON type");
376 return NULL;
377 }
378
379 name = json_object_get_string(type);
380
381 if (strcasecmp(name, "Point") == 0)
382 return parse_geojson_point(geojson, hasz);
383
384 if (strcasecmp(name, "LineString") == 0)
385 return parse_geojson_linestring(geojson, hasz);
386
387 if (strcasecmp(name, "Polygon") == 0)
388 return parse_geojson_polygon(geojson, hasz);
389
390 if (strcasecmp(name, "MultiPoint") == 0)
391 return parse_geojson_multipoint(geojson, hasz);
392
393 if (strcasecmp(name, "MultiLineString") == 0)
394 return parse_geojson_multilinestring(geojson, hasz);
395
396 if (strcasecmp(name, "MultiPolygon") == 0)
397 return parse_geojson_multipolygon(geojson, hasz);
398
399 if (strcasecmp(name, "GeometryCollection") == 0)
400 return parse_geojson_geometrycollection(geojson, hasz);
401
402 lwerror("invalid GeoJson representation");
403 return NULL; /* Never reach */
404}
405
406#endif /* HAVE_LIBJSON */
407
408LWGEOM *
409lwgeom_from_geojson(const char *geojson, char **srs)
410{
411#ifndef HAVE_LIBJSON
412 *srs = NULL;
413 lwerror("You need JSON-C for lwgeom_from_geojson");
414 return NULL;
415#else /* HAVE_LIBJSON */
416
417 /* Begin to Parse json */
418 json_tokener *jstok = json_tokener_new();
419 json_object *poObj = json_tokener_parse_ex(jstok, geojson, -1);
420 if (jstok->err != json_tokener_success)
421 {
422 char err[256];
423 snprintf(err, 256, "%s (at offset %d)", json_tokener_error_desc(jstok->err), jstok->char_offset);
424 json_tokener_free(jstok);
425 json_object_put(poObj);
426 lwerror(err);
427 return NULL;
428 }
429 json_tokener_free(jstok);
430
431 *srs = NULL;
432 json_object *poObjSrs = findMemberByName(poObj, "crs");
433 if (poObjSrs != NULL)
434 {
435 json_object *poObjSrsType = findMemberByName(poObjSrs, "type");
436 if (poObjSrsType != NULL)
437 {
438 json_object *poObjSrsProps = findMemberByName(poObjSrs, "properties");
439 if (poObjSrsProps)
440 {
441 json_object *poNameURL = findMemberByName(poObjSrsProps, "name");
442 if (poNameURL)
443 {
444 const char *pszName = json_object_get_string(poNameURL);
445 if (pszName)
446 {
447 *srs = lwalloc(strlen(pszName) + 1);
448 strcpy(*srs, pszName);
449 }
450 }
451 }
452 }
453 }
454
455 int hasz = LW_FALSE;
456 LWGEOM *lwgeom = parse_geojson(poObj, &hasz);
457 json_object_put(poObj);
458 if (!lwgeom)
459 return NULL;
460
461 if (!hasz)
462 {
463 LWGEOM *tmp = lwgeom_force_2d(lwgeom);
464 lwgeom_free(lwgeom);
465 lwgeom = tmp;
466 }
467 lwgeom_add_bbox(lwgeom);
468 return lwgeom;
469#endif /* HAVE_LIBJSON */
470}
#define LW_FALSE
Definition liblwgeom.h:108
#define COLLECTIONTYPE
Definition liblwgeom.h:122
void lwmpoint_free(LWMPOINT *mpt)
Definition lwmpoint.c:72
#define LW_FAILURE
Definition liblwgeom.h:110
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1138
#define MULTILINETYPE
Definition liblwgeom.h:120
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:119
LWGEOM * lwgeom_force_2d(const LWGEOM *geom)
Strip out the Z/M components of an LWGEOM.
Definition lwgeom.c:775
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:121
void lwfree(void *mem)
Definition lwutil.c:242
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:107
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:677
This library is the generic geometry handling section of PostGIS.
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition lwutil.c:190
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:400
double z
Definition liblwgeom.h:400
double y
Definition liblwgeom.h:400