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 |
/* these defines select one of three possible xml parsers */ |
21 |
|
/* this is in fact selected depending on the plattform in the Makefile */ |
22 |
|
// #define OSM_DOM_PARSER |
23 |
|
// #define OSM_STREAM_PARSER |
24 |
|
|
25 |
#include <stdio.h> |
#include <stdio.h> |
26 |
#include <stdlib.h> |
#include <stdlib.h> |
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" |
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; |
370 |
xmlFree(prop); |
xmlFree(prop); |
371 |
} |
} |
372 |
|
|
373 |
|
/* append node to end of hash table if present */ |
374 |
|
if(osm->node_hash) { |
375 |
|
hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)]; |
376 |
|
while(*item) item = &(*item)->next; |
377 |
|
|
378 |
|
*item = g_new0(hash_item_t, 1); |
379 |
|
(*item)->data.node = node; |
380 |
|
} |
381 |
|
|
382 |
/* scan for tags and attach a list of tags */ |
/* scan for tags and attach a list of tags */ |
383 |
tag_t **tag = &node->tag; |
tag_t **tag = &node->tag; |
384 |
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { |
for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { |
490 |
node_chain_t *node_chain = g_new0(node_chain_t, 1); |
node_chain_t *node_chain = g_new0(node_chain_t, 1); |
491 |
|
|
492 |
/* search matching node */ |
/* search matching node */ |
493 |
node_chain->node = osm->node; |
node_chain->node = osm_get_node_by_id(osm, id); |
|
while(node_chain->node && node_chain->node->id != id) |
|
|
node_chain->node = node_chain->node->next; |
|
|
|
|
494 |
if(!node_chain->node) printf("Node id %lu not found\n", id); |
if(!node_chain->node) printf("Node id %lu not found\n", id); |
495 |
|
else node_chain->node->ways++; |
|
if(node_chain->node) |
|
|
node_chain->node->ways++; |
|
496 |
|
|
497 |
xmlFree(prop); |
xmlFree(prop); |
498 |
|
|
502 |
return NULL; |
return NULL; |
503 |
} |
} |
504 |
|
|
505 |
#ifndef OSM_STREAM_PARSER |
#ifdef OSM_DOM_PARSER |
506 |
static way_t *osm_parse_osm_way(osm_t *osm, |
static way_t *osm_parse_osm_way(osm_t *osm, |
507 |
xmlDocPtr doc, xmlNode *a_node) { |
xmlDocPtr doc, xmlNode *a_node) { |
508 |
xmlNode *cur_node = NULL; |
xmlNode *cur_node = NULL; |
531 |
xmlFree(prop); |
xmlFree(prop); |
532 |
} |
} |
533 |
|
|
534 |
|
/* append way to end of hash table if present */ |
535 |
|
if(osm->way_hash) { |
536 |
|
hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)]; |
537 |
|
while(*item) item = &(*item)->next; |
538 |
|
|
539 |
|
*item = g_new0(hash_item_t, 1); |
540 |
|
(*item)->data.way = way; |
541 |
|
} |
542 |
|
|
543 |
/* scan for tags/nodes and attach their lists */ |
/* scan for tags/nodes and attach their lists */ |
544 |
tag_t **tag = &way->tag; |
tag_t **tag = &way->tag; |
545 |
node_chain_t **node_chain = &way->node_chain; |
node_chain_t **node_chain = &way->node_chain; |
665 |
|
|
666 |
case WAY: |
case WAY: |
667 |
/* search matching way */ |
/* search matching way */ |
668 |
member->way = osm->way; |
member->way = osm_get_way_by_id(osm, id); |
|
while(member->way && member->way->id != id) |
|
|
member->way = member->way->next; |
|
|
|
|
669 |
if(!member->way) { |
if(!member->way) { |
670 |
member->type = WAY_ID; |
member->type = WAY_ID; |
671 |
member->id = id; |
member->id = id; |
674 |
|
|
675 |
case NODE: |
case NODE: |
676 |
/* search matching node */ |
/* search matching node */ |
677 |
member->node = osm->node; |
member->node = osm_get_node_by_id(osm, id); |
|
while(member->node && member->node->id != id) |
|
|
member->node = member->node->next; |
|
|
|
|
678 |
if(!member->node) { |
if(!member->node) { |
679 |
member->type = NODE_ID; |
member->type = NODE_ID; |
680 |
member->id = id; |
member->id = id; |
683 |
|
|
684 |
case RELATION: |
case RELATION: |
685 |
/* search matching relation */ |
/* search matching relation */ |
686 |
member->relation = osm->relation; |
member->relation = osm_get_relation_by_id(osm, id); |
|
while(member->relation && member->relation->id != id) |
|
|
member->relation = member->relation->next; |
|
|
|
|
687 |
if(!member->relation) { |
if(!member->relation) { |
688 |
member->type = NODE_ID; |
member->type = NODE_ID; |
689 |
member->id = id; |
member->id = id; |
707 |
return member; |
return member; |
708 |
} |
} |
709 |
|
|
710 |
#ifndef OSM_STREAM_PARSER |
#ifdef OSM_DOM_PARSER |
711 |
static relation_t *osm_parse_osm_relation(osm_t *osm, |
static relation_t *osm_parse_osm_relation(osm_t *osm, |
712 |
xmlDocPtr doc, xmlNode *a_node) { |
xmlDocPtr doc, xmlNode *a_node) { |
713 |
xmlNode *cur_node = NULL; |
xmlNode *cur_node = NULL; |
759 |
|
|
760 |
/* ----------------------- generic xml handling -------------------------- */ |
/* ----------------------- generic xml handling -------------------------- */ |
761 |
|
|
762 |
/* parse loc entry */ |
/* parse osm entry */ |
763 |
static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) { |
static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) { |
764 |
xmlNode *cur_node = NULL; |
xmlNode *cur_node = NULL; |
765 |
|
|
841 |
|
|
842 |
/* allocate memory to hold osm file description */ |
/* allocate memory to hold osm file description */ |
843 |
osm = g_new0(osm_t, 1); |
osm = g_new0(osm_t, 1); |
844 |
|
osm->node_hash = g_new0(hash_table_t, 1); |
845 |
|
osm->way_hash = g_new0(hash_table_t, 1); |
846 |
|
|
847 |
for (cur_node = a_node; cur_node; cur_node = cur_node->next) { |
for (cur_node = a_node; cur_node; cur_node = cur_node->next) { |
848 |
if (cur_node->type == XML_ELEMENT_NODE) { |
if (cur_node->type == XML_ELEMENT_NODE) { |
880 |
|
|
881 |
/* ------------------ osm handling ----------------- */ |
/* ------------------ osm handling ----------------- */ |
882 |
|
|
883 |
|
/* the two hash tables eat over 512kBytes memory and may thus be */ |
884 |
|
/* freed at any time. osm2go can work without them (albeit slower) */ |
885 |
|
static void hash_table_free(hash_table_t *table) { |
886 |
|
if(!table) return; |
887 |
|
|
888 |
|
int i; |
889 |
|
for(i=0;i<65536;i++) { |
890 |
|
hash_item_t *item = table->hash[i]; |
891 |
|
while(item) { |
892 |
|
hash_item_t *next = item->next; |
893 |
|
g_free(item); |
894 |
|
item = next; |
895 |
|
} |
896 |
|
} |
897 |
|
} |
898 |
|
|
899 |
|
void osm_hash_tables_free(osm_t *osm) { |
900 |
|
hash_table_free(osm->node_hash); |
901 |
|
osm->node_hash = NULL; |
902 |
|
hash_table_free(osm->way_hash); |
903 |
|
osm->way_hash = NULL; |
904 |
|
} |
905 |
|
|
906 |
void osm_free(icon_t **icon, osm_t *osm) { |
void osm_free(icon_t **icon, osm_t *osm) { |
907 |
if(!osm) return; |
if(!osm) return; |
908 |
|
|
909 |
|
osm_hash_tables_free(osm); |
910 |
|
|
911 |
if(osm->bounds) osm_bounds_free(osm->bounds); |
if(osm->bounds) osm_bounds_free(osm->bounds); |
912 |
if(osm->user) osm_users_free(osm->user); |
if(osm->user) osm_users_free(osm->user); |
913 |
if(osm->way) osm_ways_free(osm->way); |
if(osm->way) osm_ways_free(osm->way); |
1088 |
|
|
1089 |
pos2lpos(osm->bounds, &node->pos, &node->lpos); |
pos2lpos(osm->bounds, &node->pos, &node->lpos); |
1090 |
|
|
1091 |
|
/* append node to end of hash table if present */ |
1092 |
|
if(osm->node_hash) { |
1093 |
|
hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)]; |
1094 |
|
while(*item) item = &(*item)->next; |
1095 |
|
|
1096 |
|
*item = g_new0(hash_item_t, 1); |
1097 |
|
(*item)->data.node = node; |
1098 |
|
} |
1099 |
|
|
1100 |
/* just an empty element? then return the node as it is */ |
/* just an empty element? then return the node as it is */ |
1101 |
if(xmlTextReaderIsEmptyElement(reader)) |
if(xmlTextReaderIsEmptyElement(reader)) |
1102 |
return node; |
return node; |
1134 |
node_chain_t *node_chain = g_new0(node_chain_t, 1); |
node_chain_t *node_chain = g_new0(node_chain_t, 1); |
1135 |
|
|
1136 |
/* search matching node */ |
/* search matching node */ |
1137 |
node_chain->node = osm->node; |
node_chain->node = osm_get_node_by_id(osm, id); |
|
while(node_chain->node && node_chain->node->id != id) |
|
|
node_chain->node = node_chain->node->next; |
|
|
|
|
1138 |
if(!node_chain->node) printf("Node id %lu not found\n", id); |
if(!node_chain->node) printf("Node id %lu not found\n", id); |
1139 |
|
else node_chain->node->ways++; |
|
if(node_chain->node) |
|
|
node_chain->node->ways++; |
|
1140 |
|
|
1141 |
xmlFree(prop); |
xmlFree(prop); |
1142 |
|
|
1173 |
xmlFree(prop); |
xmlFree(prop); |
1174 |
} |
} |
1175 |
|
|
1176 |
|
/* append way to end of hash table if present */ |
1177 |
|
if(osm->way_hash) { |
1178 |
|
hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)]; |
1179 |
|
while(*item) item = &(*item)->next; |
1180 |
|
|
1181 |
|
*item = g_new0(hash_item_t, 1); |
1182 |
|
(*item)->data.way = way; |
1183 |
|
} |
1184 |
|
|
1185 |
/* just an empty element? then return the way as it is */ |
/* just an empty element? then return the way as it is */ |
1186 |
/* (this should in fact never happen as this would be a way without nodes) */ |
/* (this should in fact never happen as this would be a way without nodes) */ |
1187 |
if(xmlTextReaderIsEmptyElement(reader)) |
if(xmlTextReaderIsEmptyElement(reader)) |
1237 |
|
|
1238 |
case WAY: |
case WAY: |
1239 |
/* search matching way */ |
/* search matching way */ |
1240 |
member->way = osm->way; |
member->way = osm_get_way_by_id(osm, id); |
|
while(member->way && member->way->id != id) |
|
|
member->way = member->way->next; |
|
|
|
|
1241 |
if(!member->way) { |
if(!member->way) { |
1242 |
member->type = WAY_ID; |
member->type = WAY_ID; |
1243 |
member->id = id; |
member->id = id; |
1246 |
|
|
1247 |
case NODE: |
case NODE: |
1248 |
/* search matching node */ |
/* search matching node */ |
1249 |
member->node = osm->node; |
member->node = osm_get_node_by_id(osm, id); |
|
while(member->node && member->node->id != id) |
|
|
member->node = member->node->next; |
|
|
|
|
1250 |
if(!member->node) { |
if(!member->node) { |
1251 |
member->type = NODE_ID; |
member->type = NODE_ID; |
1252 |
member->id = id; |
member->id = id; |
1255 |
|
|
1256 |
case RELATION: |
case RELATION: |
1257 |
/* search matching relation */ |
/* search matching relation */ |
1258 |
member->relation = osm->relation; |
member->relation = osm_get_relation_by_id(osm, id); |
|
while(member->relation && member->relation->id != id) |
|
|
member->relation = member->relation->next; |
|
|
|
|
1259 |
if(!member->relation) { |
if(!member->relation) { |
1260 |
member->type = NODE_ID; |
member->type = NODE_ID; |
1261 |
member->id = id; |
member->id = id; |
1323 |
|
|
1324 |
if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { |
if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { |
1325 |
char *subname = (char*)xmlTextReaderConstName(reader); |
char *subname = (char*)xmlTextReaderConstName(reader); |
1326 |
if(strcasecmp(subname, "nd") == 0) { |
if(strcasecmp(subname, "member") == 0) { |
1327 |
*member = process_member(reader, osm); |
*member = process_member(reader, osm); |
1328 |
if(*member) member = &(*member)->next; |
if(*member) member = &(*member)->next; |
1329 |
} else if(strcasecmp(subname, "tag") == 0) { |
} else if(strcasecmp(subname, "tag") == 0) { |
1341 |
static osm_t *process_osm(xmlTextReaderPtr reader) { |
static osm_t *process_osm(xmlTextReaderPtr reader) { |
1342 |
/* alloc osm structure */ |
/* alloc osm structure */ |
1343 |
osm_t *osm = g_new0(osm_t, 1); |
osm_t *osm = g_new0(osm_t, 1); |
1344 |
|
osm->node_hash = g_new0(hash_table_t, 1); |
1345 |
|
osm->way_hash = g_new0(hash_table_t, 1); |
1346 |
|
|
1347 |
node_t **node = &osm->node; |
node_t **node = &osm->node; |
1348 |
way_t **way = &osm->way; |
way_t **way = &osm->way; |
1354 |
g_assert(name); |
g_assert(name); |
1355 |
|
|
1356 |
/* read next node */ |
/* read next node */ |
1357 |
|
int num_elems = 0; |
1358 |
|
const int tick_every = 50; // Balance responsive appearance with performance. |
1359 |
int ret = xmlTextReaderRead(reader); |
int ret = xmlTextReaderRead(reader); |
1360 |
while(ret == 1) { |
while(ret == 1) { |
1361 |
|
|
1392 |
break; |
break; |
1393 |
} |
} |
1394 |
ret = xmlTextReaderRead(reader); |
ret = xmlTextReaderRead(reader); |
1395 |
|
|
1396 |
|
if (num_elems++ > tick_every) { |
1397 |
|
num_elems = 0; |
1398 |
|
banner_busy_tick(); |
1399 |
|
} |
1400 |
} |
} |
1401 |
|
|
1402 |
g_assert(0); |
g_assert(0); |
1428 |
/* ----------------------- end of stream parser tests ------------------- */ |
/* ----------------------- end of stream parser tests ------------------- */ |
1429 |
#endif |
#endif |
1430 |
|
|
1431 |
|
#ifdef OSM_QND_XML_PARSER |
1432 |
|
/* -------------------------- qnd-xml parser tests ------------------- */ |
1433 |
|
|
1434 |
|
#ifdef USE_FLOAT |
1435 |
|
#define GET_PROP_POS(a,b,c) qnd_xml_get_prop_float(a, b, c) |
1436 |
|
#else |
1437 |
|
#define GET_PROP_POS(a,b,c) qnd_xml_get_prop_double(a, b, c) |
1438 |
|
#endif |
1439 |
|
|
1440 |
|
gboolean osm_bounds_cb(qnd_xml_stack_t *stack, |
1441 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1442 |
|
|
1443 |
|
/* get parent pointer */ |
1444 |
|
osm_t *osm = (osm_t*)stack->prev->userdata[0]; |
1445 |
|
|
1446 |
|
if(osm->bounds) { |
1447 |
|
errorf(NULL, "Doubly defined bounds"); |
1448 |
|
return FALSE; |
1449 |
|
} |
1450 |
|
|
1451 |
|
bounds_t *bounds = osm->bounds = g_new0(bounds_t, 1); |
1452 |
|
|
1453 |
|
bounds->ll_min.lat = bounds->ll_min.lon = NAN; |
1454 |
|
bounds->ll_max.lat = bounds->ll_max.lon = NAN; |
1455 |
|
|
1456 |
|
GET_PROP_POS(attributes, "minlat", &bounds->ll_min.lat); |
1457 |
|
GET_PROP_POS(attributes, "minlon", &bounds->ll_min.lon); |
1458 |
|
GET_PROP_POS(attributes, "maxlat", &bounds->ll_max.lat); |
1459 |
|
GET_PROP_POS(attributes, "maxlon", &bounds->ll_max.lon); |
1460 |
|
|
1461 |
|
if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) || |
1462 |
|
isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) { |
1463 |
|
errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)", |
1464 |
|
bounds->ll_min.lat, bounds->ll_min.lon, |
1465 |
|
bounds->ll_max.lat, bounds->ll_max.lon); |
1466 |
|
|
1467 |
|
osm_bounds_free(bounds); |
1468 |
|
osm->bounds = NULL; |
1469 |
|
return FALSE; |
1470 |
|
} |
1471 |
|
|
1472 |
|
|
1473 |
|
/* calculate map zone which will be used as a reference for all */ |
1474 |
|
/* drawing/projection later on */ |
1475 |
|
pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2, |
1476 |
|
(bounds->ll_max.lon + bounds->ll_min.lon)/2 }; |
1477 |
|
|
1478 |
|
pos2lpos_center(¢er, &bounds->center); |
1479 |
|
|
1480 |
|
/* the scale is needed to accomodate for "streching" */ |
1481 |
|
/* by the mercartor projection */ |
1482 |
|
bounds->scale = cos(DEG2RAD(center.lat)); |
1483 |
|
|
1484 |
|
pos2lpos_center(&bounds->ll_min, &bounds->min); |
1485 |
|
bounds->min.x -= bounds->center.x; |
1486 |
|
bounds->min.y -= bounds->center.y; |
1487 |
|
bounds->min.x *= bounds->scale; |
1488 |
|
bounds->min.y *= bounds->scale; |
1489 |
|
|
1490 |
|
pos2lpos_center(&bounds->ll_max, &bounds->max); |
1491 |
|
bounds->max.x -= bounds->center.x; |
1492 |
|
bounds->max.y -= bounds->center.y; |
1493 |
|
bounds->max.x *= bounds->scale; |
1494 |
|
bounds->max.y *= bounds->scale; |
1495 |
|
|
1496 |
|
return TRUE; |
1497 |
|
} |
1498 |
|
|
1499 |
|
static gboolean osm_tag_cb(qnd_xml_stack_t *stack, |
1500 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1501 |
|
|
1502 |
|
tag_t *tag = *(tag_t**)stack->prev->userdata[1] = g_new0(tag_t, 1); |
1503 |
|
|
1504 |
|
tag->key = qnd_xml_get_prop_str(attributes, "k"); |
1505 |
|
tag->value = qnd_xml_get_prop_str(attributes, "v"); |
1506 |
|
|
1507 |
|
if(!tag->key || !tag->value) { |
1508 |
|
printf("incomplete tag key/value %s/%s\n", tag->key, tag->value); |
1509 |
|
osm_tags_free(tag); |
1510 |
|
tag = NULL; |
1511 |
|
} else |
1512 |
|
stack->prev->userdata[1] = &tag->next; |
1513 |
|
|
1514 |
|
return TRUE; |
1515 |
|
} |
1516 |
|
|
1517 |
|
static gboolean osm_node_cb(qnd_xml_stack_t *stack, |
1518 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1519 |
|
|
1520 |
|
osm_t *osm = (osm_t*)stack->prev->userdata[0]; |
1521 |
|
|
1522 |
|
/* allocate a new node structure. userdata[1] points to the current */ |
1523 |
|
/* position a new node is to be stored */ |
1524 |
|
node_t *node = *(node_t**)stack->prev->userdata[1] = |
1525 |
|
stack->userdata[0] = g_new0(node_t, 1); |
1526 |
|
stack->prev->userdata[1] = &node->next; |
1527 |
|
|
1528 |
|
qnd_xml_get_prop_gulong(attributes, "id", &node->id); |
1529 |
|
GET_PROP_POS(attributes, "lat", &node->pos.lat); |
1530 |
|
GET_PROP_POS(attributes, "lon", &node->pos.lon); |
1531 |
|
node->user = osm_user(osm, qnd_xml_get_prop(attributes, "user")); |
1532 |
|
node->visible = qnd_xml_get_prop_is(attributes, "visible", "true"); |
1533 |
|
node->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp")); |
1534 |
|
|
1535 |
|
pos2lpos(osm->bounds, &node->pos, &node->lpos); |
1536 |
|
|
1537 |
|
/* store current tag pointer in userdata for fast access to current tag */ |
1538 |
|
stack->userdata[1] = &node->tag; |
1539 |
|
|
1540 |
|
/* append node to end of hash table if present */ |
1541 |
|
if(osm->node_hash) { |
1542 |
|
hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)]; |
1543 |
|
while(*item) item = &(*item)->next; |
1544 |
|
|
1545 |
|
*item = g_new0(hash_item_t, 1); |
1546 |
|
(*item)->data.node = node; |
1547 |
|
} |
1548 |
|
|
1549 |
|
return TRUE; |
1550 |
|
} |
1551 |
|
|
1552 |
|
static gboolean osm_way_nd_cb(qnd_xml_stack_t *stack, |
1553 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1554 |
|
|
1555 |
|
osm_t *osm = (osm_t*)stack->prev->prev->userdata[0]; |
1556 |
|
|
1557 |
|
item_id_t id; |
1558 |
|
if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) { |
1559 |
|
/* allocate a new node_chain structure */ |
1560 |
|
node_chain_t *node_chain = *(node_chain_t**)stack->prev->userdata[2] = |
1561 |
|
g_new0(node_chain_t, 1); |
1562 |
|
|
1563 |
|
/* search matching node */ |
1564 |
|
node_chain->node = osm_get_node_by_id(osm, id); |
1565 |
|
if(!node_chain->node) printf("Node id %lu not found\n", id); |
1566 |
|
else node_chain->node->ways++; |
1567 |
|
|
1568 |
|
stack->prev->userdata[2] = &node_chain->next; |
1569 |
|
} |
1570 |
|
|
1571 |
|
return TRUE; |
1572 |
|
} |
1573 |
|
|
1574 |
|
gboolean osm_way_cb(qnd_xml_stack_t *stack, |
1575 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1576 |
|
|
1577 |
|
osm_t *osm = (osm_t*)stack->prev->userdata[0]; |
1578 |
|
|
1579 |
|
/* allocate a new way structure. userdata[2] points to the current */ |
1580 |
|
/* position a new way is to be stored in the way list */ |
1581 |
|
way_t *way = *(way_t**)stack->prev->userdata[2] = |
1582 |
|
stack->userdata[0] = g_new0(way_t, 1); |
1583 |
|
stack->prev->userdata[2] = &way->next; |
1584 |
|
|
1585 |
|
qnd_xml_get_prop_gulong(attributes, "id", &way->id); |
1586 |
|
way->user = osm_user(osm, qnd_xml_get_prop(attributes, "user")); |
1587 |
|
way->visible = qnd_xml_get_prop_is(attributes, "visible", "true"); |
1588 |
|
way->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp")); |
1589 |
|
|
1590 |
|
/* store current tag and node_chain pointers in userdata for fast */ |
1591 |
|
/* access to current tag/node_chain entry */ |
1592 |
|
stack->userdata[1] = &way->tag; |
1593 |
|
stack->userdata[2] = &way->node_chain; |
1594 |
|
|
1595 |
|
/* append way to end of hash table if present */ |
1596 |
|
if(osm->way_hash) { |
1597 |
|
hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)]; |
1598 |
|
while(*item) item = &(*item)->next; |
1599 |
|
|
1600 |
|
*item = g_new0(hash_item_t, 1); |
1601 |
|
(*item)->data.way = way; |
1602 |
|
} |
1603 |
|
|
1604 |
|
return TRUE; |
1605 |
|
} |
1606 |
|
|
1607 |
|
static gboolean osm_rel_member_cb(qnd_xml_stack_t *stack, |
1608 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1609 |
|
|
1610 |
|
osm_t *osm = (osm_t*)stack->prev->prev->userdata[0]; |
1611 |
|
|
1612 |
|
member_t *member = *(member_t**)stack->prev->userdata[2] = |
1613 |
|
g_new0(member_t, 1); |
1614 |
|
stack->prev->userdata[2] = &member->next; |
1615 |
|
member->type = ILLEGAL; |
1616 |
|
|
1617 |
|
char *type = qnd_xml_get_prop(attributes, "type"); |
1618 |
|
if(type) { |
1619 |
|
if(strcasecmp(type, "way") == 0) member->type = WAY; |
1620 |
|
else if(strcasecmp(type, "node") == 0) member->type = NODE; |
1621 |
|
else if(strcasecmp(type, "relation") == 0) member->type = RELATION; |
1622 |
|
} |
1623 |
|
|
1624 |
|
item_id_t id; |
1625 |
|
if(qnd_xml_get_prop_gulong(attributes, "ref", &id)) { |
1626 |
|
switch(member->type) { |
1627 |
|
case ILLEGAL: |
1628 |
|
printf("Unable to store illegal type\n"); |
1629 |
|
break; |
1630 |
|
|
1631 |
|
case WAY: |
1632 |
|
/* search matching way */ |
1633 |
|
member->way = osm_get_way_by_id(osm, id); |
1634 |
|
if(!member->way) { |
1635 |
|
member->type = WAY_ID; |
1636 |
|
member->id = id; |
1637 |
|
} |
1638 |
|
break; |
1639 |
|
|
1640 |
|
case NODE: |
1641 |
|
/* search matching node */ |
1642 |
|
member->node = osm_get_node_by_id(osm, id); |
1643 |
|
if(!member->node) { |
1644 |
|
member->type = NODE_ID; |
1645 |
|
member->id = id; |
1646 |
|
} |
1647 |
|
break; |
1648 |
|
|
1649 |
|
case RELATION: |
1650 |
|
/* search matching relation */ |
1651 |
|
member->relation = osm_get_relation_by_id(osm, id); |
1652 |
|
if(!member->relation) { |
1653 |
|
member->type = NODE_ID; |
1654 |
|
member->id = id; |
1655 |
|
} |
1656 |
|
break; |
1657 |
|
|
1658 |
|
case WAY_ID: |
1659 |
|
case NODE_ID: |
1660 |
|
case RELATION_ID: |
1661 |
|
break; |
1662 |
|
} |
1663 |
|
} |
1664 |
|
|
1665 |
|
return TRUE; |
1666 |
|
} |
1667 |
|
|
1668 |
|
gboolean osm_rel_cb(qnd_xml_stack_t *stack, |
1669 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1670 |
|
|
1671 |
|
osm_t *osm = (osm_t*)stack->prev->userdata[0]; |
1672 |
|
|
1673 |
|
/* allocate a new relation structure. userdata[3] points to the current */ |
1674 |
|
/* position a new relation is to be stored at in the relation list */ |
1675 |
|
relation_t *relation = *(relation_t**)stack->prev->userdata[3] = |
1676 |
|
stack->userdata[0] = g_new0(relation_t, 1); |
1677 |
|
stack->prev->userdata[3] = &relation->next; |
1678 |
|
|
1679 |
|
qnd_xml_get_prop_gulong(attributes, "id", &relation->id); |
1680 |
|
relation->user = osm_user(osm, qnd_xml_get_prop(attributes, "user")); |
1681 |
|
relation->visible = qnd_xml_get_prop_is(attributes, "visible", "true"); |
1682 |
|
relation->time = convert_iso8601(qnd_xml_get_prop(attributes, "timestamp")); |
1683 |
|
|
1684 |
|
/* store current tag and member pointers in userdata for fast access */ |
1685 |
|
/* to current tag and members in their chains */ |
1686 |
|
stack->userdata[1] = &relation->tag; |
1687 |
|
stack->userdata[2] = &relation->member; |
1688 |
|
|
1689 |
|
return TRUE; |
1690 |
|
} |
1691 |
|
|
1692 |
|
gboolean osm_cb(qnd_xml_stack_t *stack, |
1693 |
|
qnd_xml_attribute_t *attributes, gpointer data) { |
1694 |
|
|
1695 |
|
g_assert(!stack->userdata[0]); |
1696 |
|
|
1697 |
|
/* also set parents (roots) userdata as it's the parsers return value */ |
1698 |
|
osm_t *osm = stack->prev->userdata[0] = |
1699 |
|
stack->userdata[0] = g_new0(osm_t, 1); |
1700 |
|
|
1701 |
|
osm->node_hash = g_new0(hash_table_t, 1); |
1702 |
|
osm->way_hash = g_new0(hash_table_t, 1); |
1703 |
|
|
1704 |
|
/* store direct pointers for faster list access */ |
1705 |
|
/* (otherwise we'd have to search the end of the lists for every item */ |
1706 |
|
/* to be attached) */ |
1707 |
|
stack->userdata[1] = &osm->node; |
1708 |
|
stack->userdata[2] = &osm->way; |
1709 |
|
stack->userdata[3] = &osm->relation; |
1710 |
|
|
1711 |
|
return TRUE; |
1712 |
|
} |
1713 |
|
|
1714 |
|
|
1715 |
|
/* these structures describe the content qnd_xml expects while parsing */ |
1716 |
|
qnd_xml_entry_t osm_node_tag = { "tag", osm_tag_cb, QND_XML_LEAF }; |
1717 |
|
|
1718 |
|
qnd_xml_entry_t osm_way_tag = { "tag", osm_tag_cb, QND_XML_LEAF }; |
1719 |
|
qnd_xml_entry_t osm_way_nd = { "nd", osm_way_nd_cb, QND_XML_LEAF }; |
1720 |
|
|
1721 |
|
qnd_xml_entry_t osm_rel_tag = { "tag", osm_tag_cb, QND_XML_LEAF }; |
1722 |
|
qnd_xml_entry_t osm_rel_member = { "member", osm_rel_member_cb, QND_XML_LEAF }; |
1723 |
|
|
1724 |
|
qnd_xml_entry_t osm_bounds = { "bounds", osm_bounds_cb, QND_XML_LEAF }; |
1725 |
|
|
1726 |
|
qnd_xml_entry_t *node_children[] = { &osm_node_tag }, |
1727 |
|
osm_node = { "node", osm_node_cb, QND_XML_CHILDREN(node_children) }; |
1728 |
|
|
1729 |
|
qnd_xml_entry_t *way_children[] = { &osm_way_tag, &osm_way_nd }, |
1730 |
|
osm_way = { "way", osm_way_cb, QND_XML_CHILDREN(way_children) }; |
1731 |
|
|
1732 |
|
qnd_xml_entry_t *rel_children[] = { &osm_rel_tag, &osm_rel_member }, |
1733 |
|
osm_rel = { "rel", osm_rel_cb, QND_XML_CHILDREN(rel_children) }; |
1734 |
|
|
1735 |
|
/* the osm element */ |
1736 |
|
qnd_xml_entry_t *osm_children[] = { |
1737 |
|
&osm_bounds, &osm_node, &osm_way, &osm_rel }; |
1738 |
|
qnd_xml_entry_t osm = { "osm", osm_cb, QND_XML_CHILDREN(osm_children) }; |
1739 |
|
|
1740 |
|
/* the root element */ |
1741 |
|
qnd_xml_entry_t *root_children[] = { &osm }; |
1742 |
|
qnd_xml_entry_t root = { "<root>", NULL, QND_XML_CHILDREN(root_children) }; |
1743 |
|
|
1744 |
|
// gcc `pkg-config --cflags --libs glib-2.0` -o qnd_xml qnd_xml.c |
1745 |
|
|
1746 |
|
|
1747 |
|
|
1748 |
|
/* ----------------------- end of qnd-xml parser tests ------------------- */ |
1749 |
|
#endif |
1750 |
|
|
1751 |
|
|
1752 |
#include <sys/time.h> |
#include <sys/time.h> |
1753 |
|
|
1754 |
osm_t *osm_parse(char *filename) { |
osm_t *osm_parse(char *filename) { |
1756 |
struct timeval start; |
struct timeval start; |
1757 |
gettimeofday(&start, NULL); |
gettimeofday(&start, NULL); |
1758 |
|
|
1759 |
|
#ifdef OSM_STREAM_PARSER |
1760 |
LIBXML_TEST_VERSION; |
LIBXML_TEST_VERSION; |
1761 |
|
|
|
#ifdef OSM_STREAM_PARSER |
|
1762 |
// use stream parser |
// use stream parser |
1763 |
osm_t *osm = process_file(filename); |
osm_t *osm = process_file(filename); |
1764 |
xmlCleanupParser(); |
xmlCleanupParser(); |
1765 |
|
#endif |
1766 |
|
|
1767 |
|
#ifdef OSM_DOM_PARSER |
1768 |
|
LIBXML_TEST_VERSION; |
1769 |
|
|
|
#else |
|
1770 |
// parse into a tree |
// parse into a tree |
1771 |
/* parse the file and get the DOM */ |
/* parse the file and get the DOM */ |
1772 |
xmlDoc *doc = NULL; |
xmlDoc *doc = NULL; |
1779 |
osm_t *osm = osm_parse_doc(doc); |
osm_t *osm = osm_parse_doc(doc); |
1780 |
#endif |
#endif |
1781 |
|
|
1782 |
|
#ifdef OSM_QND_XML_PARSER |
1783 |
|
osm_t *osm = NULL; |
1784 |
|
if(!(osm = qnd_xml_parse(filename, &root, NULL))) { |
1785 |
|
errorf(NULL, "While parsing \"%s\"", filename); |
1786 |
|
return NULL; |
1787 |
|
} |
1788 |
|
#endif |
1789 |
|
|
1790 |
struct timeval end; |
struct timeval end; |
1791 |
gettimeofday(&end, NULL); |
gettimeofday(&end, NULL); |
1792 |
|
|
2045 |
return osm_generate_xml(osm, RELATION, relation); |
return osm_generate_xml(osm, RELATION, relation); |
2046 |
} |
} |
2047 |
|
|
2048 |
|
/* the following three functions are eating much CPU power */ |
2049 |
|
/* as they search the objects lists. Hashing is supposed to help */ |
2050 |
node_t *osm_get_node_by_id(osm_t *osm, item_id_t id) { |
node_t *osm_get_node_by_id(osm_t *osm, item_id_t id) { |
2051 |
|
if(id > 0 && osm->node_hash) { |
2052 |
|
// use hash table if present |
2053 |
|
hash_item_t *item = osm->node_hash->hash[ID2HASH(id)]; |
2054 |
|
while(item) { |
2055 |
|
if(item->data.node->id == id) |
2056 |
|
return item->data.node; |
2057 |
|
|
2058 |
|
item = item->next; |
2059 |
|
} |
2060 |
|
} |
2061 |
|
|
2062 |
|
/* use linear search if no hash tables are present or search in hash table failed */ |
2063 |
node_t *node = osm->node; |
node_t *node = osm->node; |
2064 |
while(node) { |
while(node) { |
2065 |
if(node->id == id) |
if(node->id == id) |
2066 |
return node; |
return node; |
2067 |
|
|
2068 |
node = node->next; |
node = node->next; |
2069 |
} |
} |
2070 |
|
|
2071 |
return NULL; |
return NULL; |
2072 |
} |
} |
2073 |
|
|
2074 |
way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) { |
way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) { |
2075 |
|
if(id > 0 && osm->way_hash) { |
2076 |
|
// use hash table if present |
2077 |
|
hash_item_t *item = osm->way_hash->hash[ID2HASH(id)]; |
2078 |
|
while(item) { |
2079 |
|
if(item->data.way->id == id) |
2080 |
|
return item->data.way; |
2081 |
|
|
2082 |
|
item = item->next; |
2083 |
|
} |
2084 |
|
} |
2085 |
|
|
2086 |
|
/* use linear search if no hash tables are present or search on hash table failed */ |
2087 |
way_t *way = osm->way; |
way_t *way = osm->way; |
2088 |
while(way) { |
while(way) { |
2089 |
if(way->id == id) |
if(way->id == id) |
2090 |
return way; |
return way; |
2091 |
|
|
2092 |
way = way->next; |
way = way->next; |
2093 |
} |
} |
2094 |
|
|
2095 |
return NULL; |
return NULL; |
2096 |
} |
} |
2097 |
|
|
2098 |
relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) { |
relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) { |
2099 |
|
// use linear search |
2100 |
relation_t *relation = osm->relation; |
relation_t *relation = osm->relation; |
2101 |
while(relation) { |
while(relation) { |
2102 |
if(relation->id == id) |
if(relation->id == id) |
2190 |
*lnode = node; |
*lnode = node; |
2191 |
} |
} |
2192 |
|
|
2193 |
|
void osm_node_restore(osm_t *osm, node_t *node) { |
2194 |
|
printf("Restoring node\n"); |
2195 |
|
|
2196 |
|
/* attach to end of node list */ |
2197 |
|
node_t **lnode = &osm->node; |
2198 |
|
while(*lnode) lnode = &(*lnode)->next; |
2199 |
|
*lnode = node; |
2200 |
|
} |
2201 |
|
|
2202 |
way_t *osm_way_new(void) { |
way_t *osm_way_new(void) { |
2203 |
printf("Creating new way\n"); |
printf("Creating new way\n"); |
2204 |
|
|
2613 |
|
|
2614 |
return new_tags; |
return new_tags; |
2615 |
} |
} |
2616 |
|
|
2617 |
|
/* return plain text of type */ |
2618 |
|
char *osm_type_string(type_t type) { |
2619 |
|
const struct { type_t type; char *name; } types[] = { |
2620 |
|
{ ILLEGAL, "illegal" }, |
2621 |
|
{ NODE, "node" }, |
2622 |
|
{ WAY, "way" }, |
2623 |
|
{ RELATION, "relation" }, |
2624 |
|
{ NODE_ID, "node id" }, |
2625 |
|
{ WAY_ID, "way id" }, |
2626 |
|
{ RELATION_ID, "relation id" }, |
2627 |
|
{ 0, NULL } |
2628 |
|
}; |
2629 |
|
|
2630 |
|
int i; |
2631 |
|
for(i=0;types[i].name;i++) |
2632 |
|
if(type == types[i].type) |
2633 |
|
return types[i].name; |
2634 |
|
|
2635 |
|
return NULL; |
2636 |
|
} |
2637 |
|
|
2638 |
|
char *osm_object_string(type_t type, void *object) { |
2639 |
|
char *type_str = osm_type_string(type); |
2640 |
|
|
2641 |
|
if(!object) |
2642 |
|
return g_strdup_printf("%s #<invalid>", type_str); |
2643 |
|
|
2644 |
|
switch(type) { |
2645 |
|
case ILLEGAL: |
2646 |
|
return g_strdup_printf("%s #<unspec>", type_str); |
2647 |
|
break; |
2648 |
|
case NODE: |
2649 |
|
return g_strdup_printf("%s #%ld", type_str, ((node_t*)object)->id); |
2650 |
|
break; |
2651 |
|
case WAY: |
2652 |
|
return g_strdup_printf("%s #%ld", type_str, ((way_t*)object)->id); |
2653 |
|
break; |
2654 |
|
case RELATION: |
2655 |
|
return g_strdup_printf("%s #%ld", type_str, ((relation_t*)object)->id); |
2656 |
|
break; |
2657 |
|
case NODE_ID: |
2658 |
|
case WAY_ID: |
2659 |
|
case RELATION_ID: |
2660 |
|
return g_strdup_printf("%s #%ld", type_str, *((item_id_t*)object)); |
2661 |
|
break; |
2662 |
|
} |
2663 |
|
return NULL; |
2664 |
|
} |
2665 |
|
|
2666 |
|
// vim:et:ts=8:sw=2:sts=2:ai |