22 #include "utils/builtins.h"
24 #include <libxml/tree.h>
25 #include <libxml/parser.h>
28 #include "../postgis_config.h"
29 #include "lwgeom_pg.h"
58 xmlNodePtr xmlroot = NULL;
60 if (PG_ARGISNULL(0)) PG_RETURN_NULL();
62 xml_input = PG_GETARG_TEXT_P(0);
63 xml = text_to_cstring(xml_input);
64 xml_size = VARSIZE_ANY_EXHDR(xml_input);
67 xmldoc = xmlReadMemory(xml, xml_size, NULL, NULL, 0);
69 if (!xmldoc || (xmlroot = xmlDocGetRootElement(xmldoc)) == NULL) {
72 lwpgerror(
"invalid MARC21/XML document.");
87 geom = geometry_serialize(lwgeom);
91 PG_RETURN_POINTER(geom);
97 char *colon_pos, *node_name;
100 if (!xn || xn->type != XML_ELEMENT_NODE)
106 node_name = (
char*)xn->name;
107 colon_pos = strchr(node_name,
':');
109 node_name = colon_pos + 1;
111 return strcmp(node_name, xml_name) == 0;
120 if(literal == NULL)
return LW_FALSE;
122 literal_length = strlen(literal);
124 POSTGIS_DEBUGF(2,
"is_literal_valid called (%s)", literal);
126 if (literal_length < 3)
return LW_FALSE;
140 if (literal[0] ==
'N' || literal[0] ==
'E' || literal[0] ==
'S' || literal[0] ==
'W' || literal[0] ==
'+' || literal[0] ==
'-') {
142 if (literal_length < 4) {
143 POSTGIS_DEBUGF(3,
" invalid literal length (%d): \"%s\"", literal_length, literal);
150 for (
int j = coord_start; j < literal_length; j++) {
152 if (!isdigit(literal[j])) {
161 POSTGIS_DEBUGF(3,
" invalid character '%c' at the degrees section: \"%s\"", literal[j], literal);
170 if (literal[j] ==
'.' || literal[j] ==
',') {
174 if (num_dec_sep > 1)
return LW_FALSE;
177 POSTGIS_DEBUGF(3,
" invalid character '%c' in %d: \"%s\"", literal[j], j, literal);
186 POSTGIS_DEBUGF(2,
"=> is_literal_valid returns LW_TRUE for \"%s\"", literal);
208 size_t literal_length;
210 char start_character = literal[0];
211 int start_literal = 0;
214 const size_t numdigits_degrees = 3;
215 const size_t numdigits_minutes = 2;
216 const size_t numdigits_seconds = 2;
218 POSTGIS_DEBUGF(2,
"parse_geo_literal called (%s)", literal);
219 POSTGIS_DEBUGF(2,
" start character: %c", start_character);
221 literal_length = strlen(literal);
223 if (!isdigit(start_character)) start_literal = 1;
225 POSTGIS_DEBUGF(2,
" start_literal=%d", start_literal);
227 dgr = palloc(
sizeof(
char)*numdigits_degrees+1);
228 snprintf(dgr, numdigits_degrees+1,
"%s", &literal[start_literal]);
230 if (strchr(literal,
'.') == NULL && strchr(literal,
',') == NULL) {
243 POSTGIS_DEBUG(2,
" lat/lon integer coordinates detected");
244 POSTGIS_DEBUGF(2,
" parsed degrees (lon/lat): %s", dgr);
251 if (literal_length > (start_literal + numdigits_degrees)) {
253 min = palloc(
sizeof(
char)*numdigits_minutes+1);
254 snprintf(min, numdigits_minutes+1,
"%s", &literal[start_literal+numdigits_degrees]);
255 POSTGIS_DEBUGF(2,
" parsed minutes (lon/lat): %s", min);
260 if (literal_length >= (start_literal + numdigits_degrees + numdigits_minutes)) {
262 sec = palloc(
sizeof(
char)*numdigits_seconds+1);
263 snprintf(sec, numdigits_seconds+1,
"%s", &literal[start_literal+numdigits_degrees+numdigits_minutes]);
264 POSTGIS_DEBUGF(2,
" parsed seconds (lon/lat): %s", sec);
277 POSTGIS_DEBUG(2,
" decimal coordinates detected");
279 if (strchr(literal,
',')) {
284 literal[literal_length-strlen(strchr(literal,
','))]=
'.';
285 POSTGIS_DEBUGF(2,
" decimal separator changed to '.': %s",literal);
290 if (literal[start_literal + numdigits_degrees] ==
'.') {
301 char *dec = palloc(
sizeof(
char)*literal_length+1);
302 snprintf(dec, literal_length+1,
"%s", &literal[start_literal]);
305 POSTGIS_DEBUGF(2,
" parsed decimal degrees: %s", dec);
309 }
else if (literal[start_literal + numdigits_degrees + numdigits_minutes] ==
'.') {
320 size_t len_decimal_minutes = literal_length - (start_literal + numdigits_degrees);
322 min = palloc(
sizeof(
char)*len_decimal_minutes+1);
323 snprintf(min, len_decimal_minutes+1,
"%s", &literal[start_literal + numdigits_degrees]);
325 POSTGIS_DEBUGF(2,
" parsed degrees: %s", dgr);
326 POSTGIS_DEBUGF(2,
" parsed decimal minutes: %s", min);
328 result = atof(dgr) + (atof(min) / 60);
333 }
else if (literal[start_literal + numdigits_degrees + numdigits_minutes + numdigits_seconds] ==
'.') {
345 size_t len_decimal_seconds = literal_length - (start_literal + numdigits_degrees + numdigits_minutes);
347 min = palloc(
sizeof(
char)*numdigits_minutes+1);
348 snprintf(min, numdigits_minutes+1,
"%s", &literal[start_literal + numdigits_degrees]);
350 sec = palloc(
sizeof(
char)*len_decimal_seconds+1);
351 snprintf(sec, len_decimal_seconds+1,
"%s", &literal[start_literal + numdigits_degrees + numdigits_minutes]);
353 result = atof(dgr) + (atof(min) / 60) + (atof(sec) / 3600);
355 POSTGIS_DEBUGF(2,
" parsed degrees: %s", dgr);
356 POSTGIS_DEBUGF(2,
" parsed minutes: %s", min);
357 POSTGIS_DEBUGF(2,
" parsed decimal seconds: %s", sec);
372 if (start_character ==
'S' || start_character ==
'W' || start_character ==
'-') {
374 POSTGIS_DEBUGF(2,
" switching sign due to start character: '%c'", start_character);
379 POSTGIS_DEBUGF(2,
"=> parse_geo_literal returns: %.*f (in decimal degrees)", literal_length-(3+start_literal),
result);
388 xmlNodePtr datafield;
392 uint8_t geometry_type;
397 POSTGIS_DEBUGF(2,
"parse_marc21 called: root '<%s>'", xnode->name);
405 lwpgerror(
"invalid MARC21/XML document. Root element <record> expected but <%s> found.",xnode->name);
410 for (datafield = xnode->children; datafield != NULL; datafield = datafield->next) {
417 if (datafield->type != XML_ELEMENT_NODE)
continue;
419 if (!
is_xml_element(datafield,
"datafield") || xmlStrcmp(xmlGetProp(datafield, (xmlChar*)
"tag"),(xmlChar*)
"034") != 0)
continue;
421 POSTGIS_DEBUG(3,
" datafield found");
423 for (subfield = datafield->children; subfield != NULL; subfield = subfield->next) {
425 if (subfield->type != XML_ELEMENT_NODE)
continue;
429 code = (
char*) xmlGetProp(subfield, (xmlChar*)
"code");
431 if ((strcmp(code,
"d") != 0 &&
432 strcmp(code,
"e") != 0 &&
433 strcmp(code,
"f") != 0 &&
434 strcmp(code,
"g")) != 0)
437 literal = (
char*) xmlNodeGetContent(subfield);
439 POSTGIS_DEBUGF(3,
" subfield code '%s': %s", code, literal);
443 if (strcmp(code,
"d") == 0) lw = literal;
444 else if (strcmp(code,
"e") == 0) le = literal;
445 else if (strcmp(code,
"f") == 0) ln = literal;
446 else if (strcmp(code,
"g") == 0) ls = literal;
450 lwpgerror(
"parse error - invalid literal at 034$%s: \"%s\"", code, literal);
456 xmlFreeNode(subfield);
458 if (lw && le && ln && ls) {
466 if (ngeoms > 0) lwgeoms = (
LWGEOM**)
469 if (fabs(
w - e) < 0.0000001f && fabs(n -
s) < 0.0000001f) {
487 if (ngeoms && result_type != geometry_type) {
490 result_type = geometry_type;
497 if (lw || le || ln || ls) {
499 lwpgerror(
"parse error - the Coded Cartographic Mathematical Data (datafield:034) in the given MARC21/XML is incomplete. Coordinates for subfields \"$d\",\"$e\",\"$f\" and \"$g\" are expected.");
506 POSTGIS_DEBUG(5,
" xmlFreeNode(datafield)");
507 xmlFreeNode(datafield);
515 }
else if (ngeoms > 1) {
519 for (i = 0; i < ngeoms; i++) {
535 POSTGIS_DEBUG(2,
"=> parse_marc21 returns NULL");
char result[OUT_DOUBLE_BUFFER_SIZE]
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
void lwgeom_free(LWGEOM *geom)
LWPOLY * lwpoly_construct_envelope(int32_t srid, double x1, double y1, double x2, double y2)
void * lwrealloc(void *mem, size_t size)
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
void lwgeom_force_clockwise(LWGEOM *lwgeom)
Force Right-hand-rule on LWGEOM polygons.
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
void * lwalloc(size_t size)
#define LW_TRUE
Return types for functions with status returns.
#define SRID_UNKNOWN
Unknown SRID value.
This library is the generic geometry handling section of PostGIS.
static bool is_xml_element(xmlNodePtr xn, const char *xml_name)
PG_FUNCTION_INFO_V1(ST_GeomFromMARC21)
static LWGEOM * parse_marc21(xmlNodePtr xnode)
Datum ST_GeomFromMARC21(PG_FUNCTION_ARGS)
static double parse_geo_literal(char *literal)
static int is_literal_valid(const char *literal)
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.