source: trunk/osm05.c @ 141

Last change on this file since 141 was 141, checked in by simon, 5 years ago

Merge the 'client-library-split' branch into trunk

New Features:

  • Split the project into a library and a client
  • Relicense all code as LGPL
  • Remove all global variables
  • Make most functions thread-safe (Exceptions: parsing maps and rules)
  • Add a GObject based API
  • Add zoom level 18 support
  • Add capability to query and edit rules

Note: This code was created during the Google Summer of Code 2009.

  • Property svn:eol-style set to native
  • Property svn:keywords set to "Date Author Id Revision HeadURL"
  • Property svn:mime-type set to text/x-csrc
File size: 14.6 KB
Line 
1/*
2 * Memphis - Cairo Rederer for OSM in C
3 * Copyright (C) 2008  Marius Rieder <marius.rieder@durchmesser.ch>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <glib.h>
21#include <glib/gstdio.h>
22#include <time.h>
23#include <expat.h>
24#include <string.h>
25
26#include "list.h"
27#include "mlib.h"
28#include "osm05.h"
29#include "memphis-data-pool.h"
30
31#define BUFFSIZE 1024
32
33typedef struct mapUserData_ mapUserData;
34struct mapUserData_ {
35  // Pointers to work with
36  osmTag *cTag;
37  osmNode *cNode;
38  osmWay *cWay;
39  MemphisDataPool *pool;
40  // Collected Data
41  osmFile *osm;
42  // Counts (used for debugging only!)
43  int cntTag;
44  int cntNd;
45  gint8 debug_level;
46};
47
48/**
49 * osmStartElement:
50 * @userdata:  Void
51 * @name:  The element name
52 * @atts: The element attribs
53 *
54 * called when the start of an element has been detected.
55 */
56static void XMLCALL
57osmStartElement(void *userData, const char *name, const char **atts) {
58    mapUserData *data = (mapUserData *) userData;
59    osmFile *osm = data->osm;
60    GStringChunk *stringChunk = data->pool->stringChunk;
61    GTree *stringTree = data->pool->stringTree;
62    gint8 debug_level = data->debug_level;
63   
64    if (debug_level > 1)
65        g_fprintf (stdout, "osm05startElement\n");
66    // Parsing Bounds
67    if (strncmp((char *) name, "bounds", 6) == 0) {
68        if (debug_level > 1)
69            g_fprintf (stdout, "Parsing Bounds\n");
70        while (*atts != NULL) {
71            if(strcmp((char *) *(atts), "minlat" ) == 0) {
72                sscanf((char *) *(atts+1),"%f",&osm->minlat);
73            } else if(strcmp((char *) *(atts), "minlon") == 0) {
74                sscanf((char *) *(atts+1),"%f",&osm->minlon);
75            } else if(strcmp((char *) *(atts), "maxlat") == 0) {
76                sscanf((char *) *(atts+1),"%f",&osm->maxlat);
77            } else if(strcmp((char *) *(atts), "maxlon") == 0) {
78                sscanf((char *) *(atts+1),"%f",&osm->maxlon);
79            }
80            atts+=2;
81        }
82    }
83    // Parsing Node
84    else if (strncmp((char *) name, "node", 4) == 0) {
85        if (debug_level > 1)
86            g_fprintf (stdout, "Parsing Node\n");
87        data->cNode = g_new(osmNode, 1);
88        while (*atts != NULL) {
89            if(strcmp((char *) *(atts), "id") == 0) {
90                sscanf((char *) *(atts+1),"%i",&data->cNode->id);
91            } else if(strcmp((char *) *(atts), "lat") == 0) {
92                sscanf((char *) *(atts+1),"%f",&data->cNode->lat);
93            } else if(strcmp((char *) *(atts), "lon") == 0) {
94                sscanf((char *) *(atts+1),"%f",&data->cNode->lon);
95            }
96            atts+=2;
97        }
98
99        data->cNode->tag = NULL;
100        data->cNode->layer = 0;
101
102        // Insert Node
103        osm->nodecnt++;
104        g_hash_table_insert(osm->nodeidx, &data->cNode->id, data->cNode);
105        LL_PREPEND(data->cNode, osm->nodes);
106
107        if (debug_level > 1)
108            g_fprintf (stdout, "NODE: %i %f %f\n", data->cNode->id,
109                    data->cNode->lat, data->cNode->lon);
110    }
111    // Parsing Tags
112    else if (strncmp((char *) name, "tag", 4) == 0) {
113        if (debug_level > 1)
114            g_fprintf (stdout, "Parsing Tag\n");
115
116        if (!data->cNode && !data->cWay) // End if there is nothing to add the tag to
117            return;
118           
119        char *k=NULL, *v=NULL;
120
121        while (*atts != NULL) {
122            if(strncmp((char *) *(atts), "k", 1) == 0) {
123                if(strcmp((char *) *(atts+1), "created_by") == 0) {
124                    return;
125                } else if(strncmp((char *) *(atts+1), "source", 6) == 0) {
126                    return;
127                }
128                k = (char *) *(atts+1);
129            } else if(strncmp((char *) *(atts), "v", 1) == 0) {
130                if(strcmp(k, "layer") == 0) {
131                    if (data->cNode)
132                        sscanf((char *) *(atts+1),"%hi",& data->cNode->layer);
133                    else if (data->cWay)
134                        sscanf((char *) *(atts+1),"%hi",& data->cWay->layer);
135                    return;
136                } else if(strcmp(k, "name") == 0) {
137                    if (data->cWay) {
138                        data->cWay->name = m_string_chunk_get(stringChunk, stringTree,
139                                                        (char *) *(atts+1));
140                    }
141                    return;
142                }
143                v = (char *) *(atts+1);
144            }
145            atts += 2;
146        }
147       
148        data->cTag = g_new(osmTag, 1);
149        data->cTag->key = m_string_chunk_get(stringChunk, stringTree, k);
150        data->cTag->value = m_string_chunk_get(stringChunk, stringTree, v);
151       
152        if (debug_level > 1)
153            g_fprintf (stdout, "Tag: %s => %s\n", data->cTag->key, data->cTag->value);
154
155        data->cntTag++;
156        if (data->cNode)
157            LL_INSERT_KEY(data->cTag, data->cNode->tag);
158        else if (data->cWay)
159            LL_INSERT_KEY(data->cTag, data->cWay->tag);
160
161        data->cTag = NULL;
162    }
163    // Parsing Way
164    else if (strncmp((char *) name, "way", 3) == 0) {
165        if (debug_level > 1)
166            g_fprintf (stdout, "Parsing Way\n");
167        data->cWay = g_new(osmWay, 1);
168        while (*atts != NULL) {
169            if(strncmp((char *) *(atts), "id", 2) == 0) {
170                sscanf((char *) *(atts+1), "%i", &data->cWay->id);
171                break;
172            }
173            atts+=2;
174        }
175
176        data->cWay->tag = NULL;
177        data->cWay->nd = NULL;
178        data->cWay->name = NULL;
179        data->cWay->layer = 0;
180
181        // Insert Way
182        osm->waycnt++;
183        LL_PREPEND(data->cWay, osm->ways);
184
185        if (debug_level > 1)
186            g_fprintf (stdout, "WAY(%i)\n", data->cWay->id);
187    }
188    // Parsing WayNode
189    else if (strncmp((char *) name, "nd", 2) == 0) {
190        if (debug_level > 1)
191            g_fprintf (stdout, "Parsing Nd\n");
192        int ref = 0;
193        while (*atts != NULL) {
194            if(strncmp((char *) *(atts), "ref", 2) == 0) {
195                sscanf((char *) *(atts+1),"%i",&ref);
196                break;
197            }
198            atts+=2;
199        }
200
201        if (ref) {
202            data->cntNd++;
203            osmNode *n;
204           
205            n = g_hash_table_lookup(osm->nodeidx, &ref);
206            if (!n) {
207                g_warning("No node with reference %d found!\n", ref);
208                return;
209            }
210
211            // Insert WayNode
212            data->cWay->nd = g_slist_prepend(data->cWay->nd, n);
213
214            if (debug_level > 1)
215                g_fprintf (stdout, " ND( %f %f )\n", n->lat, n->lon);
216
217            data->cNode = NULL;
218        }
219    }
220}
221
222
223/**
224 * osmEndElement:
225 * @userData:  Void
226 * @name:  The element name
227 *
228 * called when the end of an element has been detected.
229 */
230static void XMLCALL
231osmEndElement(void *userData, const char *name) {
232    mapUserData *data = (mapUserData *) userData;
233    gint8 debug_level = data->debug_level;
234   
235    if (debug_level > 1)
236        g_fprintf(stdout, "osm05endElement\n");
237    if (strncmp((char *) name, "node", 4) == 0) {
238        data->cNode = NULL;
239    } else if (strncmp((char *) name, "way", 3) == 0) {
240        if (data->cWay->nd != NULL)
241            data->cWay->nd = g_slist_reverse(data->cWay->nd);
242        data->cWay = NULL;
243    }
244}
245
246/**
247 * rulesetRead
248 */
249osmFile* osmRead(const char *filename, gint8 debug_level) {
250    if (debug_level > 1)
251        g_fprintf (stdout, "osmRead\n");
252
253    // Local Vars
254    GTimer *tOsmRead = g_timer_new();
255    unsigned int size;
256    unsigned int read = 0;
257    struct stat filestat;
258    int len;
259    int done;
260    char *buf;
261    osmFile *osm;
262    mapUserData *data;
263   
264    // Test file
265    if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
266        g_critical ("Error: \"%s\" is not a file.\n", filename);
267        return NULL;
268    }
269   
270    g_stat(filename, &filestat);
271    size = (int) filestat.st_size;
272
273    // Open file
274    FILE *fd = fopen(filename,"r");
275    if(fd == NULL) {
276        g_critical ("Error: Can't open file \"%s\"\n", filename);
277        return NULL;
278    }
279
280    // Init vars
281    data = g_new(mapUserData, 1);
282    data->cTag = NULL;
283    data->cNode = NULL;
284    data->cWay = NULL;
285    data->pool = memphis_data_pool_new ();
286    data->cntTag = 0;
287    data->cntNd = 0;
288    data->debug_level = debug_level;
289
290    osm = g_new(osmFile, 1);
291    osm->nodes = NULL;
292    osm->nodeidx = g_hash_table_new(g_int_hash, g_int_equal);
293    osm->nodecnt = 0;
294    osm->ways = NULL;
295    osm->waycnt = 0;
296    osm->minlon = -190;
297    osm->minlat = -190;
298    osm->maxlon = -190;
299    osm->maxlat = -190;
300    data->osm = osm;
301
302    if (debug_level > 0) {
303        g_fprintf (stdout, " OSM parsing   0%%");
304        fflush(stdout);
305    }
306
307    // Create XML Parser
308    XML_Parser parser = XML_ParserCreate(NULL);
309    XML_SetElementHandler(parser, osmStartElement, osmEndElement);
310    XML_SetUserData(parser, data);
311
312    // Create Buffer
313    buf = g_malloc(BUFFSIZE*sizeof(char));
314
315    // Looping over XML
316    while(!feof(fd)) {
317        len = (int)fread(buf, 1, BUFFSIZE, fd);
318        if (ferror(fd)) {
319            g_fprintf (stderr, "Read error\n");
320            return NULL;
321        }
322        read += len;
323        if (debug_level > 0) {
324            g_fprintf (stdout, "\r OSM parsing % 3i%%", (int)((read*100)/size));
325            fflush(stdout);
326        }
327        done = len < sizeof(buf);
328        if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
329            g_fprintf (stderr, "Parse error at line %iu:\n%s\n",
330                (int) XML_GetCurrentLineNumber(parser),
331                XML_ErrorString(XML_GetErrorCode(parser)));
332            exit(-1);
333        }
334    }
335
336    // Cleaning Memory
337    XML_ParserFree(parser);
338    g_free(buf);
339    fclose(fd);
340
341    // No bounds set
342    if(osm->minlon == -190 || osm->minlat == -190 ||
343       osm->maxlon == -190 || osm->maxlat == -190) {
344       
345        osm->minlon = 360.0;
346        osm->minlat = 180.0;
347        osm->maxlon = -360.0;
348        osm->maxlat = -180.0;
349
350        osmNode *node;
351        LIST_FOREACH(node, osm->nodes) {
352            if(node->lon < osm->minlon)
353                osm->minlon = node->lon;
354            if(node->lat < osm->minlat)
355                osm->minlat = node->lat;
356            if(node->lon > osm->maxlon)
357                osm->maxlon = node->lon;
358            if(node->lat > osm->maxlat)
359                osm->maxlat = node->lat;
360        }
361    }
362
363    g_hash_table_destroy(osm->nodeidx);
364    osm->nodeidx = NULL;
365   
366    if (debug_level > 0)
367        fprintf(stdout,"\r OSM parsing done. (%i/%i/%i/%i) [%fs]\n",
368                osm->nodecnt, osm->waycnt, data->cntTag, data->cntNd,
369                g_timer_elapsed(tOsmRead,NULL));
370   
371    g_timer_destroy(tOsmRead);
372    g_free(data);
373
374    return(osm);
375}
376
377osmFile* osmRead_from_buffer (const char *buffer, guint size, gint8 debug_level) {
378    if (debug_level > 1)
379        g_fprintf (stdout, "osmRead\n");
380
381    g_assert (buffer != NULL && size > 0);
382
383    // Local Vars
384    GTimer *tOsmRead = g_timer_new();
385    int isDone = 0;
386    osmFile *osm;
387    mapUserData *data;
388
389    // Init vars
390    data = g_new(mapUserData, 1);
391    data->cTag = NULL;
392    data->cNode = NULL;
393    data->cWay = NULL;
394    data->pool = memphis_data_pool_new ();
395    data->cntTag = 0;
396    data->cntNd = 0;
397    data->debug_level = debug_level;
398
399    osm = g_new(osmFile, 1);
400    osm->nodes = NULL;
401    osm->nodeidx = g_hash_table_new(g_int_hash, g_int_equal);
402    osm->nodecnt = 0;
403    osm->ways = NULL;
404    osm->waycnt = 0;
405    osm->minlon = -190;
406    osm->minlat = -190;
407    osm->maxlon = -190;
408    osm->maxlat = -190;
409
410    data->osm = osm;
411
412    if (debug_level > 0) {
413        g_fprintf (stdout, " OSM parsing   0%%");
414        fflush(stdout);
415    }
416
417    // Create XML Parser
418    XML_Parser parser = XML_ParserCreate(NULL);
419    XML_SetElementHandler(parser, osmStartElement, osmEndElement);
420    XML_SetUserData(parser, data);
421
422    // Parse the buffer
423    if (XML_Parse (parser, buffer, size, isDone) == XML_STATUS_ERROR) {
424        g_fprintf (stderr, "Parse error at line %iu:\n%s\n",
425            (int) XML_GetCurrentLineNumber(parser),
426            XML_ErrorString(XML_GetErrorCode(parser)));
427        exit (-1);
428    }
429
430    // Cleaning Memory
431    XML_ParserFree(parser);
432
433    // No bounds set
434    if(osm->minlon == -190 || osm->minlat == -190 ||
435        osm->maxlon == -190 || osm->maxlat == -190) {
436
437        osm->minlon = 360.0;
438        osm->minlat = 180.0;
439        osm->maxlon = -360.0;
440        osm->maxlat = -180.0;
441
442        osmNode *node;
443        LIST_FOREACH(node, osm->nodes) {
444            if(node->lon < osm->minlon)
445                osm->minlon = node->lon;
446            if(node->lat < osm->minlat)
447                osm->minlat = node->lat;
448            if(node->lon > osm->maxlon)
449                osm->maxlon = node->lon;
450            if(node->lat > osm->maxlat)
451                osm->maxlat = node->lat;
452        }
453    }
454
455    g_hash_table_destroy(osm->nodeidx);
456    osm->nodeidx = NULL;
457   
458    if (debug_level > 0)
459        fprintf(stdout,"\r OSM parsing done. (%i/%i/%i/%i) [%fs]\n",
460                osm->nodecnt, osm->waycnt, data->cntTag, data->cntNd,
461                g_timer_elapsed(tOsmRead,NULL));
462   
463    g_timer_destroy(tOsmRead);
464
465    return osm;
466}
467
468void osmFree(osmFile *osm) {
469    osmWay *way, *lway;
470    osmNode *node, *lnode;
471    osmTag *tag, *ltag;
472    for(way = osm->ways, lway = NULL;
473        way != NULL;
474        lway = way, way = way->next)
475    {
476        g_slist_free(way->nd);
477        for(tag = way->tag, ltag = NULL;
478            tag != NULL;
479            ltag = tag, tag = tag->next)
480        {
481            if(ltag)
482                g_free(ltag);
483        }
484        if(ltag)
485            g_free(ltag);
486        if(lway)
487            g_free(lway);
488    }
489    g_free(lway);
490   
491    for(node = osm->nodes, lnode = NULL;
492        node != NULL;
493        lnode = node, node = node->next)
494    {
495        for(tag = node->tag, ltag = NULL;
496            tag != NULL;
497            ltag = tag, tag = tag->next)
498        {
499            if(ltag)
500                g_free(ltag);
501        }
502        if(ltag)
503            g_free(ltag);
504        if(lnode)
505            g_free(lnode);
506    }
507    g_free(lnode);
508    g_free(osm);
509}
510
511/*
512 * vim: expandtab shiftwidth=4 tabstop=4:
513 */
514
Note: See TracBrowser for help on using the repository browser.