source: subversion/applications/utils/export/osm2pgsql/parse-primitive.c @ 26588

Last change on this file since 26588 was 26020, checked in by frederik, 9 years ago

Branch of osm2pgsql that does not use intarray. Unsure if it works
with Postgres 8.3; does work with 8.4. Performance seems to be
identical to original; this version can be compiled with -DOSMID64
(or #define OSMID64 in osmtypes.h) to support OSM IDs larger than
a 4 byte integer. Using this option seems to increase database size
by something like 10%, and not significantly affect speed.

Not tested with Postgres 9.0; also, tire expiry and gazetteer functions
are untested (even though there's little reason to believe they should
be broken).

File size: 15.4 KB
Line 
1/*
2#-----------------------------------------------------------------------------
3# osm2pgsql - converts planet.osm file into PostgreSQL
4# compatible output suitable to be rendered by mapnik
5#-----------------------------------------------------------------------------
6# Original Python implementation by Artem Pavlenko
7# Re-implementation by Jon Burgess, Copyright 2006
8#
9# This program is free software; you can redistribute it and/or
10# modify it under the terms of the GNU General Public License
11# as published by the Free Software Foundation; either version 2
12# of the License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22#-----------------------------------------------------------------------------
23*/
24
25/*
26
27This is a version of osm2pgsql without proper XML parsing
28it should arrive at the same results as the normal osm2pgsql
29and take an hour less to process the full planet file but
30YMMV. This is just a proof of concept and should not be used
31in a production environment.
32
33 */
34
35#define _GNU_SOURCE
36#define UNUSED  __attribute__ ((unused))
37
38#include <unistd.h>
39#include <stdlib.h>
40#include <string.h>
41#include <assert.h>
42#include <ctype.h>
43
44#include "osmtypes.h"
45#include "sanitizer.h"
46#include "reprojection.h"
47#include "input.h"
48#include "output.h"
49
50
51char *extractAttribute(char **token, int tokens, char *attname)
52{
53    char buffer[256];
54    int cl;
55    int i;
56    sprintf(buffer, "%s=\"", attname);
57    cl = strlen(buffer);
58    for (i=0; i<tokens; i++)
59    {
60        if (!strncmp(token[i], buffer, cl))
61        {
62            char *quote = index(token[i] + cl, '"');
63            if (quote == NULL) quote = token[i] + strlen(token[i]) + 1;
64            *quote = 0;
65            if (strchr(token[i]+cl, '&') == 0) return (token[i] + cl);
66
67            char *in;
68            char *out;
69            for (in=token[i]+cl, out=token[i]+cl; *in; in++)
70            {
71                if (*in == '&')
72                {
73                    if (!strncmp(in+1, "quot;", 5))
74                    {
75                        *out++ = '"';
76                        in+=5;
77                    }
78                    else if (!strncmp(in+1, "lt;", 3))
79                    {
80                        *out++ = '<';
81                        in+=3;
82                    }
83                    else if (!strncmp(in+1, "gt;", 3))
84                    {
85                        *out++ = '>';
86                        in+=3;
87                    }
88                    else if (!strncmp(in+1, "apos;", 5))
89                    {
90                        *out++ = '\'';
91                        in+=5;
92                    }
93                }
94                else
95                {
96                    *out++ = *in;
97                }
98            }
99            *out = 0;
100            return (token[i]+cl);
101        }
102    }
103    return NULL;
104}
105
106/* Parses the action="foo" tags in JOSM change files. Obvisouly not useful from osmChange files */
107static actions_t ParseAction(char **token, int tokens, struct osmdata_t *osmdata)
108{
109    if( osmdata->filetype == FILETYPE_OSMCHANGE || osmdata->filetype == FILETYPE_PLANETDIFF )
110        return osmdata->action;
111    actions_t new_action = ACTION_NONE;
112    char *action = extractAttribute(token, tokens, "action");
113    if( action == NULL )
114        new_action = ACTION_CREATE;
115    else if( strcmp((char *)action, "modify") == 0 )
116        new_action = ACTION_MODIFY;
117    else if( strcmp((char *)action, "delete") == 0 )
118        new_action = ACTION_DELETE;
119    else
120    {
121        fprintf( stderr, "Unknown value for action: %s\n", (char*)action );
122        exit_nicely();
123    }
124    return new_action;
125}
126
127static void StartElement(char *name, char *line, struct osmdata_t *osmdata)
128{
129    char *xid, *xlat, *xlon, *xk, *xv, *xrole, *xtype;
130    char *token[255];
131    int tokens = 0;
132
133    if (osmdata->filetype == FILETYPE_NONE)
134    {
135        if (!strcmp(name, "?xml")) return;
136        if (!strcmp(name, "osm"))
137        {
138            osmdata->filetype = FILETYPE_OSM;
139            osmdata->action = ACTION_CREATE;
140        }
141        else if (!strcmp(name, "osmChange"))
142        {
143            osmdata->filetype = FILETYPE_OSMCHANGE;
144            osmdata->action = ACTION_NONE;
145        }
146        else if (!strcmp(name, "planetdiff"))
147        {
148            osmdata->filetype = FILETYPE_PLANETDIFF;
149            osmdata->action = ACTION_NONE;
150        }
151        else
152        {
153            fprintf( stderr, "Unknown XML document type: %s\n", name );
154            exit_nicely();
155        }
156        return;
157    }
158
159    tokens=1;
160    token[0] = line;
161    int quote = 0;
162    char *i;
163    for (i=line; *i; i++)
164    {
165        if (quote)
166        {
167            if (*i == '"') 
168            {
169                quote = 0;
170            }
171        }
172        else
173        {
174            if (*i == '"')
175            {
176                quote = 1;
177            }
178            else if (isspace(*i))
179            {
180                *i = 0;
181                token[tokens++] = i + 1;
182            }
183        }
184    }
185
186    if (!strcmp(name, "node")) {
187        xid  = extractAttribute(token, tokens, "id");
188        xlon = extractAttribute(token, tokens, "lon");
189        xlat = extractAttribute(token, tokens, "lat");
190        assert(xid); assert(xlon); assert(xlat);
191
192        osmdata->osm_id  = strtoosmid((char *)xid, NULL, 10);
193        osmdata->node_lon = strtod((char *)xlon, NULL);
194        osmdata->node_lat = strtod((char *)xlat, NULL);
195        osmdata->action = ParseAction(token, tokens, osmdata);
196
197        if (osmdata->osm_id > osmdata->max_node)
198            osmdata->max_node = osmdata->osm_id;
199
200        osmdata->count_node++;
201        if (osmdata->count_node%10000 == 0)
202            printStatus(osmdata);
203    } else if (!strcmp(name, "tag")) {
204        xk = extractAttribute(token, tokens, "k");
205        assert(xk);
206
207        /* 'created_by' and 'source' are common and not interesting to mapnik renderer */
208        if (strcmp((char *)xk, "created_by") && strcmp((char *)xk, "source")) {
209            char *p;
210            xv = extractAttribute(token, tokens, "v");
211            assert(xv);
212            while ((p = strchr(xk, ' ')))
213                *p = '_';
214
215            addItem(&(osmdata->tags), xk, (char *)xv, 0);
216        }
217    } else if (!strcmp(name, "way")) {
218
219        xid  = extractAttribute(token, tokens, "id");
220        assert(xid);
221        osmdata->osm_id   = strtoosmid((char *)xid, NULL, 10);
222        osmdata->action = ParseAction(token, tokens, osmdata);
223
224        if (osmdata->osm_id > osmdata->max_way)
225            osmdata->max_way = osmdata->osm_id;
226
227        osmdata->count_way++;
228        if (osmdata->count_way%1000 == 0)
229            printStatus(osmdata);
230
231        osmdata->nd_count = 0;
232    } else if (!strcmp(name, "nd")) {
233        xid  = extractAttribute(token, tokens, "ref");
234        assert(xid);
235
236        osmdata->nds[osmdata->nd_count++] = strtoosmid( (char *)xid, NULL, 10 );
237
238        if( osmdata->nd_count >= osmdata->nd_max )
239          realloc_nodes(osmdata);
240    } else if (!strcmp(name, "relation")) {
241        xid  = extractAttribute(token, tokens, "id");
242        assert(xid);
243        osmdata->osm_id   = strtoosmid((char *)xid, NULL, 10);
244        osmdata->action = ParseAction(token, tokens, osmdata);
245
246        if (osmdata->osm_id > osmdata->max_rel)
247            osmdata->max_rel = osmdata->osm_id;
248
249        osmdata->count_rel++;
250        if (osmdata->count_rel%10 == 0)
251            printStatus(osmdata);
252
253        osmdata->member_count = 0;
254    } else if (!strcmp(name, "member")) {
255        xrole = extractAttribute(token, tokens, "role");
256        assert(xrole);
257
258        xtype = extractAttribute(token, tokens, "type");
259        assert(xtype);
260
261        xid  = extractAttribute(token, tokens, "ref");
262        assert(xid);
263
264        osmdata->members[osmdata->member_count].id   = strtoosmid( (char *)xid, NULL, 0 );
265        osmdata->members[osmdata->member_count].role = strdup( (char *)xrole );
266
267        /* Currently we are only interested in 'way' members since these form polygons with holes */
268        if (!strcmp(xtype, "way"))
269            osmdata->members[osmdata->member_count].type = OSMTYPE_WAY;
270        else if (!strcmp(xtype, "node"))
271            osmdata->members[osmdata->member_count].type = OSMTYPE_NODE;
272        else if (!strcmp(xtype, "relation"))
273            osmdata->members[osmdata->member_count].type = OSMTYPE_RELATION;
274        osmdata->member_count++;
275
276        if( osmdata->member_count >= osmdata->member_max )
277            realloc_members(osmdata);
278    } else if (!strcmp(name, "add") ||
279               !strcmp(name, "create")) {
280        osmdata->action = ACTION_MODIFY; // Turns all creates into modifies, makes it resiliant against inconsistant snapshots.
281    } else if (!strcmp(name, "modify")) {
282        osmdata->action = ACTION_MODIFY;
283    } else if (!strcmp(name, "delete")) {
284        osmdata->action = ACTION_DELETE;
285    } else if (!strcmp(name, "bound")) {
286        /* ignore */
287    } else if (!strcmp(name, "bounds")) {
288        /* ignore */
289    } else if (!strcmp(name, "changeset")) {
290        /* ignore */
291    } else {
292        fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
293    }
294
295    // Collect extra attribute information and add as tags
296    if (osmdata->extra_attributes && (!strcmp(name, "node") ||
297                                      !strcmp(name, "way") ||
298                                      !strcmp(name, "relation")))
299    {
300        char *xtmp;
301
302        xtmp = extractAttribute(token, tokens, "user");
303        if (xtmp) {
304          addItem(&(osmdata->tags), "osm_user", (char *)xtmp, 0);
305        }
306
307        xtmp = extractAttribute(token, tokens, "uid");
308        if (xtmp) {
309          addItem(&(osmdata->tags), "osm_uid", (char *)xtmp, 0);
310        }
311
312        xtmp = extractAttribute(token, tokens, "version");
313        if (xtmp) {
314          addItem(&(osmdata->tags), "osm_version", (char *)xtmp, 0);
315        }
316
317        xtmp = extractAttribute(token, tokens, "timestamp");
318        if (xtmp) {
319          addItem(&(osmdata->tags), "osm_timestamp", (char *)xtmp, 0);
320        }
321    }
322}
323
324static void EndElement(const char *name, struct osmdata_t *osmdata)
325{
326    if (!strcmp(name, "node")) {
327      if (node_wanted(osmdata, osmdata->node_lat, osmdata->node_lon)) {
328        reproject(&(osmdata->node_lat), &(osmdata->node_lon));
329            if( osmdata->action == ACTION_CREATE )
330              osmdata->out->node_add(osmdata->osm_id, osmdata->node_lat, osmdata->node_lon, &(osmdata->tags));
331            else if( osmdata->action == ACTION_MODIFY )
332              osmdata->out->node_modify(osmdata->osm_id, osmdata->node_lat, osmdata->node_lon, &(osmdata->tags));
333            else if( osmdata->action == ACTION_DELETE )
334              osmdata->out->node_delete(osmdata->osm_id);
335            else
336            {
337              fprintf( stderr, "Don't know action for node %" PRIdOSMID "\n", osmdata->osm_id );
338              exit_nicely();
339            }
340        }
341      resetList(&(osmdata->tags));
342    } else if (!strcmp(name, "way")) {
343      if( osmdata->action == ACTION_CREATE )
344        osmdata->out->way_add(osmdata->osm_id, osmdata->nds, osmdata->nd_count, &(osmdata->tags) );
345        else if( osmdata->action == ACTION_MODIFY )
346          osmdata->out->way_modify(osmdata->osm_id, osmdata->nds, osmdata->nd_count, &(osmdata->tags) );
347        else if( osmdata->action == ACTION_DELETE )
348            osmdata->out->way_delete(osmdata->osm_id);
349        else
350        {
351            fprintf( stderr, "Don't know action for way %" PRIdOSMID "\n", osmdata->osm_id );
352            exit_nicely();
353        }
354      resetList(&(osmdata->tags));
355    } else if (!strcmp(name, "relation")) {
356        if( osmdata->action == ACTION_CREATE )
357          osmdata->out->relation_add(osmdata->osm_id, osmdata->members, osmdata->member_count, &(osmdata->tags));
358        else if( osmdata->action == ACTION_MODIFY )
359          osmdata->out->relation_modify(osmdata->osm_id, osmdata->members, osmdata->member_count, &(osmdata->tags));
360        else if( osmdata->action == ACTION_DELETE )
361          osmdata->out->relation_delete(osmdata->osm_id);
362        else
363        {
364          fprintf( stderr, "Don't know action for relation %" PRIdOSMID "\n", osmdata->osm_id );
365          exit_nicely();
366        }
367        resetList(&(osmdata->tags));
368        resetMembers(osmdata);
369    } else if (!strcmp(name, "tag")) {
370        /* ignore */
371    } else if (!strcmp(name, "nd")) {
372        /* ignore */
373    } else if (!strcmp(name, "member")) {
374        /* ignore */
375    } else if (!strcmp(name, "osm")) {
376        printStatus(osmdata);
377        osmdata->filetype = FILETYPE_NONE;
378    } else if (!strcmp(name, "osmChange")) {
379        printStatus(osmdata);
380        osmdata->filetype = FILETYPE_NONE;
381    } else if (!strcmp(name, "planetdiff")) {
382        printStatus(osmdata);
383        osmdata->filetype = FILETYPE_NONE;
384    } else if (!strcmp(name, "bound")) {
385        /* ignore */
386    } else if (!strcmp(name, "bounds")) {
387        /* ignore */
388    } else if (!strcmp(name, "changeset")) {
389        /* ignore */
390      resetList(&(osmdata->tags)); /* We may have accumulated some tags even if we ignored the changeset */
391    } else if (!strcmp(name, "add")) {
392        osmdata->action = ACTION_NONE;
393    } else if (!strcmp(name, "create")) {
394        osmdata->action = ACTION_NONE;
395    } else if (!strcmp(name, "modify")) {
396        osmdata->action = ACTION_NONE;
397    } else if (!strcmp(name, "delete")) {
398        osmdata->action = ACTION_NONE;
399    } else {
400        fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
401    }
402}
403
404static void process(char *line, struct osmdata_t *osmdata) {
405    char *lt = index(line, '<');
406    if (lt)
407    {
408        char *spc = index(lt+1, ' ');
409        char *gt = index(lt+1, '>');
410        char *nx = spc;
411        if (*(lt+1) == '/')
412        {
413            *gt = 0;
414            EndElement(lt+2, osmdata);
415        }
416        else
417        {
418            int slash = 0;
419            if (gt != NULL) { 
420                *gt-- = 0; 
421                if (nx == NULL || gt < nx) nx = gt; 
422                while(gt>lt)
423                {
424                    if (*gt=='/') { slash=1; *gt=0; break; }
425                    if (!isspace(*gt)) break;
426                    gt--;
427                }
428            }
429            *nx++ = 0;
430            //printf ("nx=%d, lt+1=#%s#\n", nx-lt,lt+1);
431            StartElement(lt+1, nx, osmdata);
432            if (slash) EndElement(lt+1, osmdata);
433        }
434    }
435}
436
437int streamFilePrimitive(char *filename, int sanitize UNUSED, struct osmdata_t *osmdata) {
438    struct Input *i;
439    char buffer[65536];
440    int bufsz = 0;
441    int offset = 0;
442
443    i = inputOpen(filename);
444
445    if (i != NULL) {
446        while(1)
447        {
448            bufsz = bufsz + readFile(i, buffer + bufsz, sizeof(buffer) - bufsz);
449            char *nl = index(buffer, '\n');
450            if (nl == 0) break;
451            *nl=0;
452            while (nl && nl < buffer + bufsz)
453            {
454                *nl = 0;
455                process(buffer + offset, osmdata);
456                offset = nl - buffer + 1;
457                //printf("\nsearch line at %d, buffer sz is %d = ",offset, bufsz);
458                nl = index(buffer + offset, '\n');
459                //printf("%d\n", nl ? nl-buffer : -1);
460            }
461            memcpy(buffer, buffer + offset, bufsz - offset);
462            bufsz = bufsz - offset;
463            offset = 0;
464        }
465    } else {
466        fprintf(stderr, "Unable to open %s\n", filename);
467        return 1;
468    }
469    return 0;
470}
Note: See TracBrowser for help on using the repository browser.