17 |
* along with OSM2Go. If not, see <http://www.gnu.org/licenses/>. |
* along with OSM2Go. If not, see <http://www.gnu.org/licenses/>. |
18 |
*/ |
*/ |
19 |
|
|
20 |
#define OSM_STREAM_PARSER |
/* xml parsing has a performance issue */ |
21 |
|
// #define OSM_DOM_PARSER |
22 |
|
// #define OSM_STREAM_PARSER |
23 |
|
#define OSM_QND_XML_PARSER |
24 |
|
|
25 |
#include <stdio.h> |
#include <stdio.h> |
26 |
#include <stdlib.h> |
#include <stdlib.h> |
58 |
bounds->ll_min.lon, bounds->ll_max.lon); |
bounds->ll_min.lon, bounds->ll_max.lon); |
59 |
} |
} |
60 |
|
|
61 |
#ifndef OSM_STREAM_PARSER |
#ifdef OSM_DOM_PARSER |
62 |
static bounds_t *osm_parse_osm_bounds(osm_t *osm, |
static bounds_t *osm_parse_osm_bounds(osm_t *osm, |
63 |
xmlDocPtr doc, xmlNode *a_node) { |
xmlDocPtr doc, xmlNode *a_node) { |
64 |
char *prop; |
char *prop; |
153 |
} |
} |
154 |
|
|
155 |
static user_t *osm_user(osm_t *osm, char *name) { |
static user_t *osm_user(osm_t *osm, char *name) { |
156 |
|
if(!name) return NULL; |
157 |
|
|
158 |
/* search through user list */ |
/* search through user list */ |
159 |
user_t **user = &osm->user; |
user_t **user = &osm->user; |
175 |
|
|
176 |
static |
static |
177 |
time_t convert_iso8601(const char *str) { |
time_t convert_iso8601(const char *str) { |
178 |
|
if(!str) return 0; |
179 |
|
|
180 |
tzset(); |
tzset(); |
181 |
|
|
182 |
struct tm ctime; |
struct tm ctime; |
330 |
} |
} |
331 |
} |
} |
332 |
|
|
333 |
#ifndef OSM_STREAM_PARSER |
#ifdef OSM_DOM_PARSER |
334 |
static node_t *osm_parse_osm_node(osm_t *osm, |
static node_t *osm_parse_osm_node(osm_t *osm, |
335 |
xmlDocPtr doc, xmlNode *a_node) { |
xmlDocPtr doc, xmlNode *a_node) { |
336 |
xmlNode *cur_node = NULL; |
xmlNode *cur_node = NULL; |
498 |
return NULL; |
return NULL; |
499 |
} |
} |
500 |
|
|
501 |
#ifndef OSM_STREAM_PARSER |
#ifdef OSM_DOM_PARSER |
502 |
static way_t *osm_parse_osm_way(osm_t *osm, |
static way_t *osm_parse_osm_way(osm_t *osm, |
503 |
xmlDocPtr doc, xmlNode *a_node) { |
xmlDocPtr doc, xmlNode *a_node) { |
504 |
xmlNode *cur_node = NULL; |
xmlNode *cur_node = NULL; |
703 |
return member; |
return member; |
704 |
} |
} |
705 |
|
|
706 |
#ifndef OSM_STREAM_PARSER |
#ifdef OSM_DOM_PARSER |
707 |
static relation_t *osm_parse_osm_relation(osm_t *osm, |
static relation_t *osm_parse_osm_relation(osm_t *osm, |
708 |
xmlDocPtr doc, xmlNode *a_node) { |
xmlDocPtr doc, xmlNode *a_node) { |
709 |
xmlNode *cur_node = NULL; |
xmlNode *cur_node = NULL; |
1391 |
/* ----------------------- end of stream parser tests ------------------- */ |
/* ----------------------- end of stream parser tests ------------------- */ |
1392 |
#endif |
#endif |
1393 |
|
|
1394 |
|
#ifdef OSM_QND_XML_PARSER |
1395 |
|
/* -------------------------- qnd-xml parser tests ------------------- */ |
1396 |
|
|
1397 |
|
gboolean osm_bounds_cb(qnd_xml_stack_t *stack, |
1398 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1399 |
|
|
1400 |
|
/* get parent pointer */ |
1401 |
|
osm_t *osm = (osm_t*)stack->prev->userdata[0]; |
1402 |
|
|
1403 |
|
if(osm->bounds) { |
1404 |
|
errorf(NULL, "Doubly defined bounds"); |
1405 |
|
return FALSE; |
1406 |
|
} |
1407 |
|
|
1408 |
|
bounds_t *bounds = osm->bounds = g_new0(bounds_t, 1); |
1409 |
|
|
1410 |
|
bounds->ll_min.lat = bounds->ll_min.lon = NAN; |
1411 |
|
bounds->ll_max.lat = bounds->ll_max.lon = NAN; |
1412 |
|
|
1413 |
|
qnd_xml_get_prop_double(attributes, "minlat", &bounds->ll_min.lat); |
1414 |
|
qnd_xml_get_prop_double(attributes, "minlon", &bounds->ll_min.lon); |
1415 |
|
qnd_xml_get_prop_double(attributes, "maxlat", &bounds->ll_max.lat); |
1416 |
|
qnd_xml_get_prop_double(attributes, "maxlon", &bounds->ll_max.lon); |
1417 |
|
|
1418 |
|
if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) || |
1419 |
|
isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) { |
1420 |
|
errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)", |
1421 |
|
bounds->ll_min.lat, bounds->ll_min.lon, |
1422 |
|
bounds->ll_max.lat, bounds->ll_max.lon); |
1423 |
|
|
1424 |
|
osm_bounds_free(bounds); |
1425 |
|
osm->bounds = NULL; |
1426 |
|
return FALSE; |
1427 |
|
} |
1428 |
|
|
1429 |
|
|
1430 |
|
/* calculate map zone which will be used as a reference for all */ |
1431 |
|
/* drawing/projection later on */ |
1432 |
|
pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2, |
1433 |
|
(bounds->ll_max.lon + bounds->ll_min.lon)/2 }; |
1434 |
|
|
1435 |
|
pos2lpos_center(¢er, &bounds->center); |
1436 |
|
|
1437 |
|
/* the scale is needed to accomodate for "streching" */ |
1438 |
|
/* by the mercartor projection */ |
1439 |
|
bounds->scale = cos(DEG2RAD(center.lat)); |
1440 |
|
|
1441 |
|
pos2lpos_center(&bounds->ll_min, &bounds->min); |
1442 |
|
bounds->min.x -= bounds->center.x; |
1443 |
|
bounds->min.y -= bounds->center.y; |
1444 |
|
bounds->min.x *= bounds->scale; |
1445 |
|
bounds->min.y *= bounds->scale; |
1446 |
|
|
1447 |
|
pos2lpos_center(&bounds->ll_max, &bounds->max); |
1448 |
|
bounds->max.x -= bounds->center.x; |
1449 |
|
bounds->max.y -= bounds->center.y; |
1450 |
|
bounds->max.x *= bounds->scale; |
1451 |
|
bounds->max.y *= bounds->scale; |
1452 |
|
|
1453 |
|
return TRUE; |
1454 |
|
} |
1455 |
|
|
1456 |
|
static gboolean osm_tag_cb(qnd_xml_stack_t *stack, |
1457 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1458 |
|
|
1459 |
|
tag_t *tag = *(tag_t**)stack->prev->userdata[1] = g_new0(tag_t, 1); |
1460 |
|
|
1461 |
|
tag->key = qnd_xml_get_prop_str(attributes, "k"); |
1462 |
|
tag->value = qnd_xml_get_prop_str(attributes, "v"); |
1463 |
|
|
1464 |
|
if(!tag->key || !tag->value) { |
1465 |
|
printf("incomplete tag key/value %s/%s\n", tag->key, tag->value); |
1466 |
|
osm_tags_free(tag); |
1467 |
|
tag = NULL; |
1468 |
|
} else |
1469 |
|
stack->prev->userdata[1] = &tag->next; |
1470 |
|
|
1471 |
|
return TRUE; |
1472 |
|
} |
1473 |
|
|
1474 |
|
static gboolean osm_node_cb(qnd_xml_stack_t *stack, |
1475 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1476 |
|
|
1477 |
|
osm_t *osm = (osm_t*)stack->prev->userdata[0]; |
1478 |
|
|
1479 |
|
/* allocate a new node structure. userdata[1] points to the current */ |
1480 |
|
/* position a new node is to be stored */ |
1481 |
|
node_t *node = *(node_t**)stack->prev->userdata[1] = |
1482 |
|
stack->userdata[0] = g_new0(node_t, 1); |
1483 |
|
stack->prev->userdata[1] = &node->next; |
1484 |
|
|
1485 |
|
qnd_xml_get_prop_gulong(attributes, "id", &node->id); |
1486 |
|
qnd_xml_get_prop_double(attributes, "lat", &node->pos.lat); |
1487 |
|
qnd_xml_get_prop_double(attributes, "lon", &node->pos.lon); |
1488 |
|
node->user = osm_user(osm, qnd_xml_get_prop(attributes, "user")); |
1489 |
|
node->visible = qnd_xml_get_prop_is(attributes, "visible", "true"); |
1490 |
|
node->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp")); |
1491 |
|
|
1492 |
|
pos2lpos(osm->bounds, &node->pos, &node->lpos); |
1493 |
|
|
1494 |
|
/* store current tag pointer in userdata for fast access to current tag */ |
1495 |
|
stack->userdata[1] = &node->tag; |
1496 |
|
|
1497 |
|
return TRUE; |
1498 |
|
} |
1499 |
|
|
1500 |
|
static gboolean osm_way_nd_cb(qnd_xml_stack_t *stack, |
1501 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1502 |
|
|
1503 |
|
osm_t *osm = (osm_t*)stack->prev->prev->userdata[0]; |
1504 |
|
|
1505 |
|
item_id_t id; |
1506 |
|
if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) { |
1507 |
|
/* allocate a new node_chain structure */ |
1508 |
|
node_chain_t *node_chain = *(node_chain_t**)stack->prev->userdata[2] = |
1509 |
|
g_new0(node_chain_t, 1); |
1510 |
|
|
1511 |
|
/* search matching node */ |
1512 |
|
node_chain->node = osm->node; |
1513 |
|
while(node_chain->node && node_chain->node->id != id) |
1514 |
|
node_chain->node = node_chain->node->next; |
1515 |
|
|
1516 |
|
if(!node_chain->node) printf("Node id %lu not found\n", id); |
1517 |
|
|
1518 |
|
if(node_chain->node) |
1519 |
|
node_chain->node->ways++; |
1520 |
|
|
1521 |
|
stack->prev->userdata[2] = &node_chain->next; |
1522 |
|
} |
1523 |
|
|
1524 |
|
return TRUE; |
1525 |
|
} |
1526 |
|
|
1527 |
|
gboolean osm_way_cb(qnd_xml_stack_t *stack, |
1528 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1529 |
|
|
1530 |
|
osm_t *osm = (osm_t*)stack->prev->userdata[0]; |
1531 |
|
|
1532 |
|
/* allocate a new way structure. userdata[2] points to the current */ |
1533 |
|
/* position a new way is to be stored in the way list */ |
1534 |
|
way_t *way = *(way_t**)stack->prev->userdata[2] = |
1535 |
|
stack->userdata[0] = g_new0(way_t, 1); |
1536 |
|
stack->prev->userdata[2] = &way->next; |
1537 |
|
|
1538 |
|
qnd_xml_get_prop_gulong(attributes, "id", &way->id); |
1539 |
|
way->user = osm_user(osm, qnd_xml_get_prop(attributes, "user")); |
1540 |
|
way->visible = qnd_xml_get_prop_is(attributes, "visible", "true"); |
1541 |
|
way->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp")); |
1542 |
|
|
1543 |
|
/* store current tag and node_chain pointers in userdata for fast */ |
1544 |
|
/* access to current tag/node_chain entry */ |
1545 |
|
stack->userdata[1] = &way->tag; |
1546 |
|
stack->userdata[2] = &way->node_chain; |
1547 |
|
|
1548 |
|
return TRUE; |
1549 |
|
} |
1550 |
|
|
1551 |
|
static gboolean osm_rel_member_cb(qnd_xml_stack_t *stack, |
1552 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1553 |
|
|
1554 |
|
osm_t *osm = (osm_t*)stack->prev->prev->userdata[0]; |
1555 |
|
|
1556 |
|
member_t *member = *(member_t**)stack->prev->userdata[2] = |
1557 |
|
g_new0(member_t, 1); |
1558 |
|
stack->prev->userdata[2] = &member->next; |
1559 |
|
member->type = ILLEGAL; |
1560 |
|
|
1561 |
|
char *type = qnd_xml_get_prop(attributes, "type"); |
1562 |
|
if(type) { |
1563 |
|
if(strcasecmp(type, "way") == 0) member->type = WAY; |
1564 |
|
else if(strcasecmp(type, "node") == 0) member->type = NODE; |
1565 |
|
else if(strcasecmp(type, "relation") == 0) member->type = RELATION; |
1566 |
|
} |
1567 |
|
|
1568 |
|
item_id_t id; |
1569 |
|
if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) { |
1570 |
|
switch(member->type) { |
1571 |
|
case ILLEGAL: |
1572 |
|
printf("Unable to store illegal type\n"); |
1573 |
|
break; |
1574 |
|
|
1575 |
|
case WAY: |
1576 |
|
/* search matching way */ |
1577 |
|
member->way = osm->way; |
1578 |
|
while(member->way && member->way->id != id) |
1579 |
|
member->way = member->way->next; |
1580 |
|
|
1581 |
|
if(!member->way) { |
1582 |
|
member->type = WAY_ID; |
1583 |
|
member->id = id; |
1584 |
|
} |
1585 |
|
break; |
1586 |
|
|
1587 |
|
case NODE: |
1588 |
|
/* search matching node */ |
1589 |
|
member->node = osm->node; |
1590 |
|
while(member->node && member->node->id != id) |
1591 |
|
member->node = member->node->next; |
1592 |
|
|
1593 |
|
if(!member->node) { |
1594 |
|
member->type = NODE_ID; |
1595 |
|
member->id = id; |
1596 |
|
} |
1597 |
|
break; |
1598 |
|
|
1599 |
|
case RELATION: |
1600 |
|
/* search matching relation */ |
1601 |
|
member->relation = osm->relation; |
1602 |
|
while(member->relation && member->relation->id != id) |
1603 |
|
member->relation = member->relation->next; |
1604 |
|
|
1605 |
|
if(!member->relation) { |
1606 |
|
member->type = NODE_ID; |
1607 |
|
member->id = id; |
1608 |
|
} |
1609 |
|
break; |
1610 |
|
|
1611 |
|
case WAY_ID: |
1612 |
|
case NODE_ID: |
1613 |
|
case RELATION_ID: |
1614 |
|
break; |
1615 |
|
} |
1616 |
|
} |
1617 |
|
|
1618 |
|
return TRUE; |
1619 |
|
} |
1620 |
|
|
1621 |
|
gboolean osm_rel_cb(qnd_xml_stack_t *stack, |
1622 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1623 |
|
|
1624 |
|
osm_t *osm = (osm_t*)stack->prev->userdata[0]; |
1625 |
|
|
1626 |
|
/* allocate a new relation structure. userdata[3] points to the current */ |
1627 |
|
/* position a new relation is to be stored at in the relation list */ |
1628 |
|
relation_t *relation = *(relation_t**)stack->prev->userdata[3] = |
1629 |
|
stack->userdata[0] = g_new0(relation_t, 1); |
1630 |
|
stack->prev->userdata[3] = &relation->next; |
1631 |
|
|
1632 |
|
qnd_xml_get_prop_gulong(attributes, "id", &relation->id); |
1633 |
|
relation->user = osm_user(osm, qnd_xml_get_prop(attributes, "user")); |
1634 |
|
relation->visible = qnd_xml_get_prop_is(attributes, "visible", "true"); |
1635 |
|
relation->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp")); |
1636 |
|
|
1637 |
|
/* store current tag and member pointers in userdata for fast access */ |
1638 |
|
/* to current tag and members in their chains */ |
1639 |
|
stack->userdata[1] = &relation->tag; |
1640 |
|
stack->userdata[2] = &relation->member; |
1641 |
|
|
1642 |
|
return TRUE; |
1643 |
|
} |
1644 |
|
|
1645 |
|
gboolean osm_cb(qnd_xml_stack_t *stack, |
1646 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1647 |
|
|
1648 |
|
g_assert(!stack->userdata[0]); |
1649 |
|
|
1650 |
|
/* also set parents (roots) userdata as it's the parsers return value */ |
1651 |
|
osm_t *osm = stack->prev->userdata[0] = |
1652 |
|
stack->userdata[0] = g_new0(osm_t, 1); |
1653 |
|
|
1654 |
|
/* store direct pointers for faster list access */ |
1655 |
|
/* (otherwise we'd have to search the end of the lists for every item */ |
1656 |
|
/* to be attached) */ |
1657 |
|
stack->userdata[1] = &osm->node; |
1658 |
|
stack->userdata[2] = &osm->way; |
1659 |
|
stack->userdata[3] = &osm->relation; |
1660 |
|
|
1661 |
|
return TRUE; |
1662 |
|
} |
1663 |
|
|
1664 |
|
|
1665 |
|
/* these structures describe the content qnd_xml expects while parsing */ |
1666 |
|
qnd_xml_entry_t osm_node_tag = { "tag", osm_tag_cb, QND_XML_LEAF }; |
1667 |
|
|
1668 |
|
qnd_xml_entry_t osm_way_tag = { "tag", osm_tag_cb, QND_XML_LEAF }; |
1669 |
|
qnd_xml_entry_t osm_way_nd = { "nd", osm_way_nd_cb, QND_XML_LEAF }; |
1670 |
|
|
1671 |
|
qnd_xml_entry_t osm_rel_tag = { "tag", osm_tag_cb, QND_XML_LEAF }; |
1672 |
|
qnd_xml_entry_t osm_rel_member = { "member", osm_rel_member_cb, QND_XML_LEAF }; |
1673 |
|
|
1674 |
|
qnd_xml_entry_t osm_bounds = { "bounds", osm_bounds_cb, QND_XML_LEAF }; |
1675 |
|
|
1676 |
|
qnd_xml_entry_t *node_children[] = { &osm_node_tag }, |
1677 |
|
osm_node = { "node", osm_node_cb, QND_XML_CHILDREN(node_children) }; |
1678 |
|
|
1679 |
|
qnd_xml_entry_t *way_children[] = { &osm_way_tag, &osm_way_nd }, |
1680 |
|
osm_way = { "way", osm_way_cb, QND_XML_CHILDREN(way_children) }; |
1681 |
|
|
1682 |
|
qnd_xml_entry_t *rel_children[] = { &osm_rel_tag, &osm_rel_member }, |
1683 |
|
osm_rel = { "rel", osm_rel_cb, QND_XML_CHILDREN(rel_children) }; |
1684 |
|
|
1685 |
|
/* the osm element */ |
1686 |
|
qnd_xml_entry_t *osm_children[] = { |
1687 |
|
&osm_bounds, &osm_node, &osm_way, &osm_rel }; |
1688 |
|
qnd_xml_entry_t osm = { "osm", osm_cb, QND_XML_CHILDREN(osm_children) }; |
1689 |
|
|
1690 |
|
/* the root element */ |
1691 |
|
qnd_xml_entry_t *root_children[] = { &osm }; |
1692 |
|
qnd_xml_entry_t root = { "<root>", NULL, QND_XML_CHILDREN(root_children) }; |
1693 |
|
|
1694 |
|
// gcc `pkg-config --cflags --libs glib-2.0` -o qnd_xml qnd_xml.c |
1695 |
|
|
1696 |
|
|
1697 |
|
|
1698 |
|
/* ----------------------- end of qnd-xml parser tests ------------------- */ |
1699 |
|
#endif |
1700 |
|
|
1701 |
|
|
1702 |
#include <sys/time.h> |
#include <sys/time.h> |
1703 |
|
|
1704 |
osm_t *osm_parse(char *filename) { |
osm_t *osm_parse(char *filename) { |
1706 |
struct timeval start; |
struct timeval start; |
1707 |
gettimeofday(&start, NULL); |
gettimeofday(&start, NULL); |
1708 |
|
|
|
LIBXML_TEST_VERSION; |
|
1709 |
|
|
1710 |
#ifdef OSM_STREAM_PARSER |
#ifdef OSM_STREAM_PARSER |
1711 |
|
LIBXML_TEST_VERSION; |
1712 |
|
|
1713 |
// use stream parser |
// use stream parser |
1714 |
osm_t *osm = process_file(filename); |
osm_t *osm = process_file(filename); |
1715 |
xmlCleanupParser(); |
xmlCleanupParser(); |
1716 |
|
#endif |
1717 |
|
|
1718 |
|
#ifdef OSM_DOM_PARSER |
1719 |
|
LIBXML_TEST_VERSION; |
1720 |
|
|
|
#else |
|
1721 |
// parse into a tree |
// parse into a tree |
1722 |
/* parse the file and get the DOM */ |
/* parse the file and get the DOM */ |
1723 |
xmlDoc *doc = NULL; |
xmlDoc *doc = NULL; |
1730 |
osm_t *osm = osm_parse_doc(doc); |
osm_t *osm = osm_parse_doc(doc); |
1731 |
#endif |
#endif |
1732 |
|
|
1733 |
|
#ifdef OSM_QND_XML_PARSER |
1734 |
|
osm_t *osm = NULL; |
1735 |
|
if(!(osm = qnd_xml_parse(filename, &root, NULL))) { |
1736 |
|
errorf(NULL, "While parsing \"%s\"", filename); |
1737 |
|
return NULL; |
1738 |
|
} |
1739 |
|
#endif |
1740 |
|
|
1741 |
struct timeval end; |
struct timeval end; |
1742 |
gettimeofday(&end, NULL); |
gettimeofday(&end, NULL); |
1743 |
|
|