PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwgeom_in_kml.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 2009 Oslandia
22 *
23 **********************************************************************/
24
42#include <libxml/tree.h>
43#include <libxml/parser.h>
44#include <string.h>
45
46#include "postgres.h"
47#include "utils/builtins.h"
48
49#include "../postgis_config.h"
50#include "lwgeom_pg.h"
51#include "liblwgeom.h"
52
53
54
55/*
56TODO:
57 - OGC:LonLat84_5773 explicit support (rather than EPSG:4326)
58 - altitudeModeGroup relativeToGround Z Altitude
59 computation upon Geoid
60*/
61
62
63Datum geom_from_kml(PG_FUNCTION_ARGS);
64static LWGEOM* parse_kml(xmlNodePtr xnode, bool *hasz);
65
66#define KML_NS ((char *) "http://www.opengis.net/kml/2.2")
67
68
69static inline bool
70is_kml_element(xmlNodePtr xn, const char *kml_name)
71{
72 char *colon_pos;
73 char *node_name;
74
75 /* Not an element node, can't do anything */
76 if (!xn || xn->type != XML_ELEMENT_NODE)
77 return false;
78
79 /* If there's a colon in the element name, */
80 /* move past it before checking for equality with */
81 /* the element name we are looking for */
82 node_name = (char*)xn->name;
83 colon_pos = strchr(node_name, ':');
84 if (colon_pos)
85 node_name = colon_pos + 1;
86
87 return strcmp(node_name, kml_name) == 0;
88}
89
90
96Datum geom_from_kml(PG_FUNCTION_ARGS)
97{
98 GSERIALIZED *geom;
99 LWGEOM *lwgeom, *hlwgeom;
100 xmlDocPtr xmldoc;
101 text *xml_input;
102 int xml_size;
103 char *xml;
104 bool hasz=true;
105 xmlNodePtr xmlroot=NULL;
106
107 /* Get the KML stream */
108 if (PG_ARGISNULL(0)) PG_RETURN_NULL();
109 xml_input = PG_GETARG_TEXT_P(0);
110 xml = text_to_cstring(xml_input);
111 xml_size = VARSIZE_ANY_EXHDR(xml_input);
112
113 /* Begin to Parse XML doc */
114 xmlInitParser();
115 xmldoc = xmlReadMemory(xml, xml_size, NULL, NULL, 0);
116 // xmldoc = xmlReadMemory(xml, xml_size, NULL, NULL, XML_PARSE_SAX1);
117 if (!xmldoc || (xmlroot = xmlDocGetRootElement(xmldoc)) == NULL)
118 {
119 xmlFreeDoc(xmldoc);
120 xmlCleanupParser();
121 lwpgerror("invalid KML representation");
122 }
123
124 lwgeom = parse_kml(xmlroot, &hasz);
125
126 /* Homogenize geometry result if needed */
127 if (lwgeom->type == COLLECTIONTYPE)
128 {
129 hlwgeom = lwgeom_homogenize(lwgeom);
130 lwgeom_release(lwgeom);
131 lwgeom = hlwgeom;
132 }
133
134 lwgeom_add_bbox(lwgeom);
135
136 /* KML geometries could be either 2 or 3D
137 *
138 * So we deal with 3D in all structures allocation, and flag hasz
139 * to false if we met once a missing Z dimension
140 * In this case, we force recursive 2D.
141 */
142 if (!hasz)
143 {
144 LWGEOM *tmp = lwgeom_force_2d(lwgeom);
145 lwgeom_free(lwgeom);
146 lwgeom = tmp;
147 }
148
149 geom = geometry_serialize(lwgeom);
150 lwgeom_free(lwgeom);
151
152 xmlFreeDoc(xmldoc);
153 xmlCleanupParser();
154
155 PG_RETURN_POINTER(geom);
156}
157
158
163static bool is_kml_namespace(xmlNodePtr xnode, bool is_strict)
164{
165 xmlNsPtr *ns, *p;
166
167 ns = xmlGetNsList(xnode->doc, xnode);
168 /*
169 * If no namespace is available we could return true anyway
170 * (because we work only on KML fragment, we don't want to
171 * 'oblige' to add namespace on the geometry root node)
172 */
173 if (ns == NULL) return !is_strict;
174
175 for (p=ns ; *p ; p++)
176 {
177 if ((*p)->href == NULL || (*p)->prefix == NULL ||
178 xnode->ns == NULL || xnode->ns->prefix == NULL) continue;
179
180 if (!xmlStrcmp(xnode->ns->prefix, (*p)->prefix))
181 {
182 if (!strcmp((char *) (*p)->href, KML_NS))
183 {
184 xmlFree(ns);
185 return true;
186 } else {
187 xmlFree(ns);
188 return false;
189 }
190 }
191 }
192
193 xmlFree(ns);
194 return !is_strict; /* Same reason here to not return false */;
195}
196
197
198/* Temporarily disabling unused function. */
199#if 0
204static xmlChar *kmlGetProp(xmlNodePtr xnode, xmlChar *prop)
205{
206 xmlChar *value;
207
208 if (!is_kml_namespace(xnode, true))
209 return xmlGetProp(xnode, prop);
210
211 value = xmlGetNsProp(xnode, prop, (xmlChar *) KML_NS);
212
213 /* In last case try without explicit namespace */
214 if (value == NULL) value = xmlGetNoNsProp(xnode, prop);
215
216 return value;
217}
218#endif
219
220
221#if 0 /* unused */
225static double parse_kml_double(char *d, bool space_before, bool space_after)
226{
227 char *p;
228 int st;
229 enum states
230 {
231 INIT = 0,
232 NEED_DIG = 1,
233 DIG = 2,
234 NEED_DIG_DEC = 3,
235 DIG_DEC = 4,
236 EXP = 5,
237 NEED_DIG_EXP = 6,
238 DIG_EXP = 7,
239 END = 8
240 };
241
242 /*
243 * Double pattern
244 * [-|\+]?[0-9]+(\.)?([0-9]+)?([Ee](\+|-)?[0-9]+)?
245 * We could also meet spaces before and/or after
246 * this pattern upon parameters
247 */
248
249 if (space_before) while (isspace(*d)) d++;
250 for (st = INIT, p = d ; *p ; p++)
251 {
252
253lwpgnotice("State: %d, *p=%c", st, *p);
254
255 if (isdigit(*p))
256 {
257 if (st == INIT || st == NEED_DIG) st = DIG;
258 else if (st == NEED_DIG_DEC) st = DIG_DEC;
259 else if (st == NEED_DIG_EXP || st == EXP) st = DIG_EXP;
260 else if (st == DIG || st == DIG_DEC || st == DIG_EXP);
261 else lwpgerror("invalid KML representation");
262 }
263 else if (*p == '.')
264 {
265 if (st == DIG) st = NEED_DIG_DEC;
266 else lwpgerror("invalid KML representation");
267 }
268 else if (*p == '-' || *p == '+')
269 {
270 if (st == INIT) st = NEED_DIG;
271 else if (st == EXP) st = NEED_DIG_EXP;
272 else lwpgerror("invalid KML representation");
273 }
274 else if (*p == 'e' || *p == 'E')
275 {
276 if (st == DIG || st == DIG_DEC) st = EXP;
277 else lwpgerror("invalid KML representation");
278 }
279 else if (isspace(*p))
280 {
281 if (!space_after) lwpgerror("invalid KML representation");
282 if (st == DIG || st == DIG_DEC || st == DIG_EXP)st = END;
283 else if (st == NEED_DIG_DEC) st = END;
284 else if (st == END);
285 else lwpgerror("invalid KML representation");
286 }
287 else lwpgerror("invalid KML representation");
288 }
289
290 if (st != DIG && st != NEED_DIG_DEC && st != DIG_DEC && st != DIG_EXP && st != END)
291 lwpgerror("invalid KML representation");
292
293 return atof(d);
294}
295#endif /* unused */
296
297
301static POINTARRAY* parse_kml_coordinates(xmlNodePtr xnode, bool *hasz)
302{
303 xmlChar *kml_coord;
304 bool found;
305 POINTARRAY *dpa;
306 int seen_kml_dims = 0;
307 int kml_dims;
308 char *p, *q;
309 POINT4D pt;
310 double d;
311
312 if (xnode == NULL) lwpgerror("invalid KML representation");
313
314 for (found = false ; xnode != NULL ; xnode = xnode->next)
315 {
316 if (xnode->type != XML_ELEMENT_NODE) continue;
317 if (!is_kml_namespace(xnode, false)) continue;
318 if (!is_kml_element(xnode, "coordinates")) continue;
319
320 found = true;
321 break;
322 }
323 if (!found) lwpgerror("invalid KML representation");
324
325 /* We begin to retrieve coordinates string */
326 kml_coord = xmlNodeGetContent(xnode);
327 p = (char *) kml_coord;
328
329 /* KML coordinates pattern: x1,y1 x2,y2
330 * x1,y1,z1 x2,y2,z2
331 */
332
333 /* Now we create PointArray from coordinates values */
334 /* HasZ, !HasM, 1pt */
335 dpa = ptarray_construct_empty(1, 0, 1);
336
337 while (*p && isspace(*p)) ++p;
338 for (kml_dims=0; *p ; p++)
339 {
340//lwpgnotice("*p:%c, kml_dims:%d", *p, kml_dims);
341 if ( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) {
342 kml_dims++;
343 errno = 0; d = strtod(p, &q);
344 if ( errno != 0 ) {
345 // TODO: destroy dpa, return NULL
346 lwpgerror("invalid KML representation"); /*: %s", strerror(errno));*/
347 }
348 if (kml_dims == 1) pt.x = d;
349 else if (kml_dims == 2) pt.y = d;
350 else if (kml_dims == 3) pt.z = d;
351 else {
352 lwpgerror("invalid KML representation"); /* (more than 3 dimensions)"); */
353 // TODO: destroy dpa, return NULL
354 }
355
356//lwpgnotice("after strtod d:%f, *q:%c, kml_dims:%d", d, *q, kml_dims);
357
358 if ( *q && ! isspace(*q) && *q != ',' ) {
359 lwpgerror("invalid KML representation"); /* (invalid character %c follows ordinate value)", *q); */
360 }
361
362 /* Look-ahead to see if we're done reading */
363 while (*q && isspace(*q)) ++q;
364 if ( isdigit(*q) || *q == '+' || *q == '-' || *q == '.' || ! *q ) {
365 if ( kml_dims < 2 ) lwpgerror("invalid KML representation"); /* (not enough ordinates)"); */
366 else if ( kml_dims < 3 ) *hasz = false;
367 if ( ! seen_kml_dims ) seen_kml_dims = kml_dims;
368 else if ( seen_kml_dims != kml_dims ) {
369 lwpgerror("invalid KML representation: mixed coordinates dimension");
370 }
371 ptarray_append_point(dpa, &pt, LW_TRUE);
372 kml_dims = 0;
373 }
374 p = q-1; /* will be incremented on next iteration */
375//lwpgnotice("after look-ahead *p:%c, kml_dims:%d", *p, kml_dims);
376 } else if ( *p != ',' && ! isspace(*p) ) {
377 lwpgerror("invalid KML representation"); /* (unexpected character %c)", *p); */
378 }
379 }
380
381 xmlFree(kml_coord);
382
383 /* TODO: we shouldn't need to clone here */
384 return ptarray_clone_deep(dpa);
385}
386
387
391static LWGEOM* parse_kml_point(xmlNodePtr xnode, bool *hasz)
392{
393 POINTARRAY *pa;
394
395 if (xnode->children == NULL) lwpgerror("invalid KML representation");
396 pa = parse_kml_coordinates(xnode->children, hasz);
397 if (pa->npoints != 1) lwpgerror("invalid KML representation");
398
399 return (LWGEOM *) lwpoint_construct(4326, NULL, pa);
400}
401
402
406static LWGEOM* parse_kml_line(xmlNodePtr xnode, bool *hasz)
407{
408 POINTARRAY *pa;
409
410 if (xnode->children == NULL) lwpgerror("invalid KML representation");
411 pa = parse_kml_coordinates(xnode->children, hasz);
412 if (pa->npoints < 2) lwpgerror("invalid KML representation");
413
414 return (LWGEOM *) lwline_construct(4326, NULL, pa);
415}
416
417
421static LWGEOM* parse_kml_polygon(xmlNodePtr xnode, bool *hasz)
422{
423 int ring;
424 xmlNodePtr xa, xb;
425 POINTARRAY **ppa = NULL;
426 int outer_rings = 0;
427
428 for (xa = xnode->children ; xa != NULL ; xa = xa->next)
429 {
430
431 /* Polygon/outerBoundaryIs */
432 if (xa->type != XML_ELEMENT_NODE) continue;
433 if (!is_kml_namespace(xa, false)) continue;
434 if (!is_kml_element(xa, "outerBoundaryIs")) continue;
435
436 for (xb = xa->children ; xb != NULL ; xb = xb->next)
437 {
438
439 if (xb->type != XML_ELEMENT_NODE) continue;
440 if (!is_kml_namespace(xb, false)) continue;
441 if (!is_kml_element(xb, "LinearRing")) continue;
442
443 ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
444 ppa[0] = parse_kml_coordinates(xb->children, hasz);
445
446 if (ppa[0]->npoints < 4)
447 lwpgerror("invalid KML representation");
448
449 if ((!*hasz && !ptarray_is_closed_2d(ppa[0])) ||
450 ( *hasz && !ptarray_is_closed_3d(ppa[0])))
451 {
452 POINT4D pt;
453 getPoint4d_p(ppa[0], 0, &pt);
454 ptarray_append_point(ppa[0], &pt, LW_TRUE);
455 lwpgnotice("forced closure on an un-closed KML polygon");
456 }
457 outer_rings++;
458 }
459 }
460
461 if (outer_rings != 1)
462 lwpgerror("invalid KML representation");
463
464 for (ring=1, xa = xnode->children ; xa != NULL ; xa = xa->next)
465 {
466
467 /* Polygon/innerBoundaryIs */
468 if (xa->type != XML_ELEMENT_NODE) continue;
469 if (!is_kml_namespace(xa, false)) continue;
470 if (!is_kml_element(xa, "innerBoundaryIs")) continue;
471
472 for (xb = xa->children ; xb != NULL ; xb = xb->next)
473 {
474
475 if (xb->type != XML_ELEMENT_NODE) continue;
476 if (!is_kml_namespace(xb, false)) continue;
477 if (!is_kml_element(xb, "LinearRing")) continue;
478
479 ppa = (POINTARRAY**) lwrealloc(ppa, sizeof(POINTARRAY*) * (ring + 1));
480 ppa[ring] = parse_kml_coordinates(xb->children, hasz);
481
482 if (ppa[ring]->npoints < 4)
483 lwpgerror("invalid KML representation");
484
485 if ((!*hasz && !ptarray_is_closed_2d(ppa[ring])) ||
486 ( *hasz && !ptarray_is_closed_3d(ppa[ring])))
487 {
488 POINT4D pt;
489 getPoint4d_p(ppa[ring], 0, &pt);
490 ptarray_append_point(ppa[ring], &pt, LW_TRUE);
491 lwpgnotice("forced closure on an un-closed KML polygon");
492 }
493
494 ring++;
495 }
496 }
497
498 /* Exterior Ring is mandatory */
499 if (ppa == NULL || ppa[0] == NULL) lwpgerror("invalid KML representation");
500
501 return (LWGEOM *) lwpoly_construct(4326, NULL, ring, ppa);
502}
503
504
508static LWGEOM* parse_kml_multi(xmlNodePtr xnode, bool *hasz)
509{
510 LWGEOM *geom;
511 xmlNodePtr xa;
512
514
515 for (xa = xnode->children ; xa != NULL ; xa = xa->next)
516 {
517
518 if (xa->type != XML_ELEMENT_NODE) continue;
519 if (!is_kml_namespace(xa, false)) continue;
520
521 if ( is_kml_element(xa, "Point")
522 || is_kml_element(xa, "LineString")
523 || is_kml_element(xa, "Polygon")
524 || is_kml_element(xa, "MultiGeometry"))
525 {
526
527 if (xa->children == NULL) break;
528 geom = (LWGEOM*)lwcollection_add_lwgeom((LWCOLLECTION*)geom, parse_kml(xa, hasz));
529 }
530 }
531
532 return geom;
533}
534
535
539static LWGEOM* parse_kml(xmlNodePtr xnode, bool *hasz)
540{
541 xmlNodePtr xa = xnode;
542
543 while (xa != NULL && (xa->type != XML_ELEMENT_NODE
544 || !is_kml_namespace(xa, false))) xa = xa->next;
545
546 if (xa == NULL) lwpgerror("invalid KML representation");
547
548 if (is_kml_element(xa, "Point"))
549 return parse_kml_point(xa, hasz);
550
551 if (is_kml_element(xa, "LineString"))
552 return parse_kml_line(xa, hasz);
553
554 if (is_kml_element(xa, "Polygon"))
555 return parse_kml_polygon(xa, hasz);
556
557 if (is_kml_element(xa, "MultiGeometry"))
558 return parse_kml_multi(xa, hasz);
559
560 lwpgerror("invalid KML representation");
561 return NULL; /* Never reach */
562}
#define COLLECTIONTYPE
Definition liblwgeom.h:122
void * lwrealloc(void *mem, size_t size)
Definition lwutil.c:235
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1138
int ptarray_is_closed_3d(const POINTARRAY *pa)
Definition ptarray.c:714
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition lwpoint.c:129
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
LWGEOM * lwgeom_homogenize(const LWGEOM *geom)
void * lwalloc(size_t size)
Definition lwutil.c:227
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwline.c:42
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition lwgeom_api.c:125
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
int ptarray_is_closed_2d(const POINTARRAY *pa)
Definition ptarray.c:701
#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.
void lwgeom_release(LWGEOM *lwgeom)
Free the containing LWGEOM and the associated BOX.
Definition lwgeom.c:450
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition lwgeom.c:677
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition ptarray.c:634
This library is the generic geometry handling section of PostGIS.
static LWGEOM * parse_kml(xmlNodePtr xnode, bool *hasz)
Parse KML.
static POINTARRAY * parse_kml_coordinates(xmlNodePtr xnode, bool *hasz)
Parse kml:coordinates.
PG_FUNCTION_INFO_V1(geom_from_kml)
Ability to parse KML geometry fragment and to return an LWGEOM or an error message.
#define KML_NS
Datum geom_from_kml(PG_FUNCTION_ARGS)
static LWGEOM * parse_kml_polygon(xmlNodePtr xnode, bool *hasz)
Parse KML Polygon.
static LWGEOM * parse_kml_multi(xmlNodePtr xnode, bool *hasz)
Parse KML MultiGeometry.
static LWGEOM * parse_kml_point(xmlNodePtr xnode, bool *hasz)
Parse KML point.
static LWGEOM * parse_kml_line(xmlNodePtr xnode, bool *hasz)
Parse KML lineString.
static bool is_kml_namespace(xmlNodePtr xnode, bool is_strict)
Return false if current element namespace is not a KML one Return true otherwise.
static bool is_kml_element(xmlNodePtr xn, const char *kml_name)
int value
Definition genraster.py:62
char * text_to_cstring(const text *textptr)
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
uint8_t type
Definition liblwgeom.h:448
double x
Definition liblwgeom.h:400
double z
Definition liblwgeom.h:400
double y
Definition liblwgeom.h:400
uint32_t npoints
Definition liblwgeom.h:413