Diff of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 8 by harbaum, Thu Dec 11 20:42:10 2008 UTC revision 40 by harbaum, Sun Jan 18 19:43:20 2009 UTC
# Line 17  Line 17 
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>
# Line 31  Line 34 
34  #include <libxml/tree.h>  #include <libxml/tree.h>
35    
36  #include "appdata.h"  #include "appdata.h"
37    #include "banner.h"
38    
39  #ifndef LIBXML_TREE_ENABLED  #ifndef LIBXML_TREE_ENABLED
40  #error "Tree not enabled in libxml"  #error "Tree not enabled in libxml"
# Line 42  Line 46 
46  #define OSM_SORT_LAST  #define OSM_SORT_LAST
47  // #define OSM_SORT_FIRST  // #define OSM_SORT_FIRST
48    
49  /* ------------------------- user handling --------------------- */  /* ------------------------- bounds handling --------------------- */
50    
51  static void osm_bounds_free(bounds_t *bounds) {  static void osm_bounds_free(bounds_t *bounds) {
52    free(bounds);    free(bounds);
# Line 54  static void osm_bounds_dump(bounds_t *bo Line 58  static void osm_bounds_dump(bounds_t *bo
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;
# Line 149  void osm_users_dump(user_t *user) { Line 153  void osm_users_dump(user_t *user) {
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;
# Line 170  static user_t *osm_user(osm_t *osm, char Line 175  static user_t *osm_user(osm_t *osm, char
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;
# Line 323  void osm_nodes_dump(node_t *node) { Line 330  void osm_nodes_dump(node_t *node) {
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;
# Line 491  node_chain_t *osm_parse_osm_way_nd(osm_t Line 498  node_chain_t *osm_parse_osm_way_nd(osm_t
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;
# Line 696  member_t *osm_parse_osm_relation_member( Line 703  member_t *osm_parse_osm_relation_member(
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;
# Line 1281  static relation_t *process_relation(xmlT Line 1288  static relation_t *process_relation(xmlT
1288    
1289      if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {      if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1290        char *subname = (char*)xmlTextReaderConstName(reader);        char *subname = (char*)xmlTextReaderConstName(reader);
1291        if(strcasecmp(subname, "nd") == 0) {        if(strcasecmp(subname, "member") == 0) {
1292          *member = process_member(reader, osm);          *member = process_member(reader, osm);
1293          if(*member) member = &(*member)->next;          if(*member) member = &(*member)->next;
1294        } else if(strcasecmp(subname, "tag") == 0) {        } else if(strcasecmp(subname, "tag") == 0) {
# Line 1310  static osm_t *process_osm(xmlTextReaderP Line 1317  static osm_t *process_osm(xmlTextReaderP
1317    g_assert(name);    g_assert(name);
1318    
1319    /* read next node */    /* read next node */
1320      int num_elems = 0;
1321      const int tick_every = 50; // Balance responsive appearance with performance.
1322    int ret = xmlTextReaderRead(reader);    int ret = xmlTextReaderRead(reader);
1323    while(ret == 1) {    while(ret == 1) {
1324    
# Line 1346  static osm_t *process_osm(xmlTextReaderP Line 1355  static osm_t *process_osm(xmlTextReaderP
1355        break;        break;
1356      }      }
1357      ret = xmlTextReaderRead(reader);      ret = xmlTextReaderRead(reader);
1358    
1359        if (num_elems++ > tick_every) {
1360          num_elems = 0;
1361          banner_busy_tick();
1362        }
1363    }    }
1364    
1365    g_assert(0);    g_assert(0);
# Line 1377  static osm_t *process_file(const char *f Line 1391  static osm_t *process_file(const char *f
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    #ifdef USE_FLOAT
1398    #define GET_PROP_POS(a,b,c) qnd_xml_get_prop_float(a, b, c)
1399    #else
1400    #define GET_PROP_POS(a,b,c) qnd_xml_get_prop_double(a, b, c)
1401    #endif
1402    
1403    gboolean osm_bounds_cb(qnd_xml_stack_t *stack,
1404                           qnd_xml_attribute_t *attributes, gpointer data) {
1405    
1406      /* get parent pointer */
1407      osm_t *osm = (osm_t*)stack->prev->userdata[0];
1408    
1409      if(osm->bounds) {
1410        errorf(NULL, "Doubly defined bounds");
1411        return FALSE;
1412      }
1413    
1414      bounds_t *bounds = osm->bounds = g_new0(bounds_t, 1);
1415    
1416      bounds->ll_min.lat = bounds->ll_min.lon = NAN;
1417      bounds->ll_max.lat = bounds->ll_max.lon = NAN;
1418    
1419      GET_PROP_POS(attributes, "minlat", &bounds->ll_min.lat);
1420      GET_PROP_POS(attributes, "minlon", &bounds->ll_min.lon);
1421      GET_PROP_POS(attributes, "maxlat", &bounds->ll_max.lat);
1422      GET_PROP_POS(attributes, "maxlon", &bounds->ll_max.lon);
1423    
1424      if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
1425         isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
1426        errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
1427               bounds->ll_min.lat, bounds->ll_min.lon,
1428               bounds->ll_max.lat, bounds->ll_max.lon);
1429    
1430        osm_bounds_free(bounds);
1431        osm->bounds = NULL;
1432        return FALSE;
1433      }
1434    
1435    
1436      /* calculate map zone which will be used as a reference for all */
1437      /* drawing/projection later on */
1438      pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
1439                       (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
1440    
1441      pos2lpos_center(&center, &bounds->center);
1442    
1443      /* the scale is needed to accomodate for "streching" */
1444      /* by the mercartor projection */
1445      bounds->scale = cos(DEG2RAD(center.lat));
1446    
1447      pos2lpos_center(&bounds->ll_min, &bounds->min);
1448      bounds->min.x -= bounds->center.x;
1449      bounds->min.y -= bounds->center.y;
1450      bounds->min.x *= bounds->scale;
1451      bounds->min.y *= bounds->scale;
1452    
1453      pos2lpos_center(&bounds->ll_max, &bounds->max);
1454      bounds->max.x -= bounds->center.x;
1455      bounds->max.y -= bounds->center.y;
1456      bounds->max.x *= bounds->scale;
1457      bounds->max.y *= bounds->scale;
1458    
1459      return TRUE;
1460    }
1461    
1462    static gboolean osm_tag_cb(qnd_xml_stack_t *stack,
1463                             qnd_xml_attribute_t *attributes, gpointer data) {
1464    
1465      tag_t *tag = *(tag_t**)stack->prev->userdata[1] = g_new0(tag_t, 1);
1466    
1467      tag->key = qnd_xml_get_prop_str(attributes, "k");
1468      tag->value = qnd_xml_get_prop_str(attributes, "v");
1469    
1470      if(!tag->key || !tag->value) {
1471        printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
1472        osm_tags_free(tag);
1473        tag = NULL;
1474      } else
1475        stack->prev->userdata[1] = &tag->next;
1476    
1477      return TRUE;
1478    }
1479    
1480    static gboolean osm_node_cb(qnd_xml_stack_t *stack,
1481                         qnd_xml_attribute_t *attributes, gpointer data) {
1482    
1483      osm_t *osm = (osm_t*)stack->prev->userdata[0];
1484    
1485      /* allocate a new node structure. userdata[1] points to the current */
1486      /* position a new node is to be stored */
1487      node_t *node = *(node_t**)stack->prev->userdata[1] =
1488        stack->userdata[0] = g_new0(node_t, 1);
1489      stack->prev->userdata[1] = &node->next;
1490    
1491      qnd_xml_get_prop_gulong(attributes, "id", &node->id);
1492      GET_PROP_POS(attributes, "lat", &node->pos.lat);
1493      GET_PROP_POS(attributes, "lon", &node->pos.lon);
1494      node->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));
1495      node->visible = qnd_xml_get_prop_is(attributes, "visible", "true");
1496      node->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));
1497    
1498      pos2lpos(osm->bounds, &node->pos, &node->lpos);
1499    
1500      /* store current tag pointer in userdata for fast access to current tag */
1501      stack->userdata[1] = &node->tag;
1502    
1503      return TRUE;
1504    }
1505    
1506    static gboolean osm_way_nd_cb(qnd_xml_stack_t *stack,
1507                             qnd_xml_attribute_t *attributes, gpointer data) {
1508    
1509      osm_t *osm = (osm_t*)stack->prev->prev->userdata[0];
1510    
1511      item_id_t id;
1512      if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) {
1513        /* allocate a new node_chain structure */
1514        node_chain_t *node_chain = *(node_chain_t**)stack->prev->userdata[2] =
1515          g_new0(node_chain_t, 1);
1516    
1517        /* search matching node */
1518        node_chain->node = osm->node;
1519        while(node_chain->node && node_chain->node->id != id)
1520          node_chain->node = node_chain->node->next;
1521    
1522        if(!node_chain->node) printf("Node id %lu not found\n", id);
1523    
1524        if(node_chain->node)
1525          node_chain->node->ways++;
1526    
1527        stack->prev->userdata[2] = &node_chain->next;
1528      }
1529    
1530      return TRUE;
1531    }
1532    
1533    gboolean osm_way_cb(qnd_xml_stack_t *stack,
1534                        qnd_xml_attribute_t *attributes, gpointer data) {
1535    
1536      osm_t *osm = (osm_t*)stack->prev->userdata[0];
1537    
1538      /* allocate a new way structure. userdata[2] points to the current */
1539      /* position a new way is to be stored in the way list */
1540      way_t *way = *(way_t**)stack->prev->userdata[2] =
1541        stack->userdata[0] = g_new0(way_t, 1);
1542      stack->prev->userdata[2] = &way->next;
1543    
1544      qnd_xml_get_prop_gulong(attributes, "id", &way->id);
1545      way->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));
1546      way->visible = qnd_xml_get_prop_is(attributes, "visible", "true");
1547      way->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));
1548    
1549      /* store current tag and node_chain pointers in userdata for fast */
1550      /* access to current tag/node_chain entry */
1551      stack->userdata[1] = &way->tag;
1552      stack->userdata[2] = &way->node_chain;
1553    
1554      return TRUE;
1555    }
1556    
1557    static gboolean osm_rel_member_cb(qnd_xml_stack_t *stack,
1558                             qnd_xml_attribute_t *attributes, gpointer data) {
1559    
1560      osm_t *osm = (osm_t*)stack->prev->prev->userdata[0];
1561    
1562      member_t *member = *(member_t**)stack->prev->userdata[2] =
1563        g_new0(member_t, 1);
1564      stack->prev->userdata[2] = &member->next;
1565      member->type = ILLEGAL;
1566    
1567      char *type = qnd_xml_get_prop(attributes, "type");
1568      if(type) {
1569        if(strcasecmp(type, "way") == 0)           member->type = WAY;
1570        else if(strcasecmp(type, "node") == 0)     member->type = NODE;
1571        else if(strcasecmp(type, "relation") == 0) member->type = RELATION;
1572      }
1573    
1574      item_id_t id;
1575      if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) {
1576        switch(member->type) {
1577        case ILLEGAL:
1578          printf("Unable to store illegal type\n");
1579          break;
1580    
1581        case WAY:
1582          /* search matching way */
1583          member->way = osm->way;
1584          while(member->way && member->way->id != id)
1585            member->way = member->way->next;
1586    
1587          if(!member->way) {
1588            member->type = WAY_ID;
1589            member->id = id;
1590          }
1591          break;
1592    
1593        case NODE:
1594          /* search matching node */
1595          member->node = osm->node;
1596          while(member->node && member->node->id != id)
1597            member->node = member->node->next;
1598    
1599          if(!member->node) {
1600            member->type = NODE_ID;
1601            member->id = id;
1602          }
1603          break;
1604    
1605        case RELATION:
1606          /* search matching relation */
1607          member->relation = osm->relation;
1608          while(member->relation && member->relation->id != id)
1609            member->relation = member->relation->next;
1610    
1611          if(!member->relation) {
1612            member->type = NODE_ID;
1613            member->id = id;
1614          }
1615          break;
1616    
1617        case WAY_ID:
1618        case NODE_ID:
1619        case RELATION_ID:
1620          break;
1621        }
1622      }
1623    
1624      return TRUE;
1625    }
1626    
1627    gboolean osm_rel_cb(qnd_xml_stack_t *stack,
1628                        qnd_xml_attribute_t *attributes, gpointer data) {
1629    
1630      osm_t *osm = (osm_t*)stack->prev->userdata[0];
1631    
1632      /* allocate a new relation structure. userdata[3] points to the current */
1633      /* position a new relation is to be stored at in the relation list */
1634      relation_t *relation = *(relation_t**)stack->prev->userdata[3] =
1635        stack->userdata[0] = g_new0(relation_t, 1);
1636      stack->prev->userdata[3] = &relation->next;
1637    
1638      qnd_xml_get_prop_gulong(attributes, "id", &relation->id);
1639      relation->user = osm_user(osm, qnd_xml_get_prop(attributes, "user"));
1640      relation->visible = qnd_xml_get_prop_is(attributes, "visible", "true");
1641      relation->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp"));
1642    
1643      /* store current tag and member pointers in userdata for fast access */
1644      /* to current tag and members in their chains */
1645      stack->userdata[1] = &relation->tag;
1646      stack->userdata[2] = &relation->member;
1647    
1648      return TRUE;
1649    }
1650    
1651    gboolean osm_cb(qnd_xml_stack_t *stack,
1652                    qnd_xml_attribute_t *attributes, gpointer data) {
1653    
1654      g_assert(!stack->userdata[0]);
1655    
1656      /* also set parents (roots) userdata as it's the parsers return value */
1657      osm_t *osm = stack->prev->userdata[0] =
1658        stack->userdata[0] = g_new0(osm_t, 1);
1659    
1660      /* store direct pointers for faster list access */
1661      /* (otherwise we'd have to search the end of the lists for every item */
1662      /* to be attached) */
1663      stack->userdata[1] = &osm->node;
1664      stack->userdata[2] = &osm->way;
1665      stack->userdata[3] = &osm->relation;
1666    
1667      return TRUE;
1668    }
1669    
1670    
1671    /* these structures describe the content qnd_xml expects while parsing */
1672    qnd_xml_entry_t osm_node_tag = { "tag", osm_tag_cb, QND_XML_LEAF };
1673    
1674    qnd_xml_entry_t osm_way_tag = { "tag", osm_tag_cb, QND_XML_LEAF };
1675    qnd_xml_entry_t osm_way_nd = { "nd", osm_way_nd_cb, QND_XML_LEAF };
1676    
1677    qnd_xml_entry_t osm_rel_tag = { "tag", osm_tag_cb, QND_XML_LEAF };
1678    qnd_xml_entry_t osm_rel_member = { "member", osm_rel_member_cb, QND_XML_LEAF };
1679    
1680    qnd_xml_entry_t osm_bounds = { "bounds", osm_bounds_cb, QND_XML_LEAF };
1681    
1682    qnd_xml_entry_t *node_children[] = { &osm_node_tag },
1683      osm_node = { "node", osm_node_cb, QND_XML_CHILDREN(node_children) };
1684    
1685    qnd_xml_entry_t *way_children[] = { &osm_way_tag, &osm_way_nd },
1686      osm_way = { "way", osm_way_cb, QND_XML_CHILDREN(way_children) };
1687    
1688    qnd_xml_entry_t *rel_children[] = { &osm_rel_tag, &osm_rel_member },
1689      osm_rel = { "rel", osm_rel_cb, QND_XML_CHILDREN(rel_children) };
1690    
1691    /* the osm element */
1692    qnd_xml_entry_t *osm_children[] = {
1693      &osm_bounds, &osm_node, &osm_way, &osm_rel };
1694    qnd_xml_entry_t osm = { "osm", osm_cb, QND_XML_CHILDREN(osm_children) };
1695    
1696    /* the root element */
1697    qnd_xml_entry_t *root_children[] = { &osm };
1698    qnd_xml_entry_t root = { "<root>", NULL, QND_XML_CHILDREN(root_children) };
1699    
1700    // gcc `pkg-config --cflags --libs glib-2.0` -o qnd_xml qnd_xml.c
1701    
1702    
1703    
1704    /* ----------------------- end of qnd-xml parser tests ------------------- */
1705    #endif
1706    
1707    
1708  #include <sys/time.h>  #include <sys/time.h>
1709    
1710  osm_t *osm_parse(char *filename) {  osm_t *osm_parse(char *filename) {
# Line 1384  osm_t *osm_parse(char *filename) { Line 1712  osm_t *osm_parse(char *filename) {
1712    struct timeval start;    struct timeval start;
1713    gettimeofday(&start, NULL);    gettimeofday(&start, NULL);
1714    
   LIBXML_TEST_VERSION;  
1715    
1716  #ifdef OSM_STREAM_PARSER  #ifdef OSM_STREAM_PARSER
1717      LIBXML_TEST_VERSION;
1718    
1719    // use stream parser    // use stream parser
1720    osm_t *osm = process_file(filename);    osm_t *osm = process_file(filename);
1721    xmlCleanupParser();    xmlCleanupParser();
1722    #endif
1723    
1724    #ifdef OSM_DOM_PARSER
1725      LIBXML_TEST_VERSION;
1726    
 #else  
1727    // parse into a tree    // parse into a tree
1728    /* parse the file and get the DOM */    /* parse the file and get the DOM */
1729    xmlDoc *doc = NULL;    xmlDoc *doc = NULL;
# Line 1404  osm_t *osm_parse(char *filename) { Line 1736  osm_t *osm_parse(char *filename) {
1736    osm_t *osm = osm_parse_doc(doc);    osm_t *osm = osm_parse_doc(doc);
1737  #endif  #endif
1738    
1739    #ifdef OSM_QND_XML_PARSER
1740      osm_t *osm = NULL;
1741      if(!(osm = qnd_xml_parse(filename, &root, NULL))) {
1742        errorf(NULL, "While parsing \"%s\"", filename);
1743        return NULL;
1744      }
1745    #endif
1746    
1747    struct timeval end;    struct timeval end;
1748    gettimeofday(&end, NULL);    gettimeofday(&end, NULL);
1749    
# Line 2192  tag_t *osm_tags_copy(tag_t *src_tag, gbo Line 2532  tag_t *osm_tags_copy(tag_t *src_tag, gbo
2532    
2533    return new_tags;    return new_tags;
2534  }  }
2535    // vim:et:ts=8:sw=2:sts=2:ai

Legend:
Removed from v.8  
changed lines
  Added in v.40