source: subversion/applications/utils/export/osm2pgsql-64/output-gazetteer.c @ 26020

Revision 26020, 38.1 KB checked in by frederik, 3 years ago (diff)

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).

Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include <libpq-fe.h>
6
7#include "osmtypes.h"
8#include "middle.h"
9#include "output.h"
10#include "output-gazetteer.h"
11#include "pgsql.h"
12#include "reprojection.h"
13#include "build_geometry.h"
14
15#define BUFFER_SIZE 4096
16
17#define SRID (project_getprojinfo()->srs)
18
19#define CREATE_KEYVALUETYPE_TYPE                \
20   "CREATE TYPE keyvalue AS ("                  \
21   "  key TEXT,"                                \
22   "  value TEXT"                               \
23   ")"
24
25#define CREATE_WORDSCORE_TYPE                   \
26   "CREATE TYPE wordscore AS ("                 \
27   "  word TEXT,"                                \
28   "  score FLOAT"                               \
29   ")"
30
31#define CREATE_PLACE_TABLE                      \
32   "CREATE TABLE place ("                       \
33   "  place_id BIGINT,"                         \
34   "  osm_type CHAR(1) NOT NULL,"               \
35   "  osm_id BIGINT NOT NULL,"                  \
36   "  class TEXT NOT NULL,"                     \
37   "  type TEXT NOT NULL,"                      \
38   "  name keyvalue[],"                         \
39   "  admin_level INTEGER,"                     \
40   "  housenumber TEXT,"                        \
41   "  street TEXT,"                             \
42   "  isin TEXT,"                               \
43   "  postcode TEXT,"                           \
44   "  country_code VARCHAR(2),"                 \
45   "  street_place_id BIGINT,"                  \
46   "  rank_address INTEGER,"                    \
47   "  rank_search INTEGER,"                     \
48   "  indexed BOOLEAN"                          \
49   ") %s %s"
50
51#define V2_CREATE_PLACE_TABLE                   \
52   "CREATE TABLE place ("                       \
53   "  osm_type CHAR(1) NOT NULL,"               \
54   "  osm_id BIGINT NOT NULL,"                  \
55   "  class TEXT NOT NULL,"                     \
56   "  type TEXT NOT NULL,"                      \
57   "  name HSTORE,"                             \
58   "  admin_level INTEGER,"                     \
59   "  housenumber TEXT,"                        \
60   "  street TEXT,"                             \
61   "  isin TEXT,"                               \
62   "  postcode TEXT,"                           \
63   "  country_code VARCHAR(2),"                 \
64   "  extratags HSTORE"                         \
65   ") %s %s"
66
67#define ADMINLEVEL_NONE 100
68
69#define CREATE_PLACE_ID_INDEX \
70   "CREATE INDEX place_id_idx ON place USING BTREE (osm_type, osm_id) %s %s"
71
72#define TAGINFO_NODE 0x1u
73#define TAGINFO_WAY  0x2u
74#define TAGINFO_AREA 0x4u
75
76static const struct taginfo {
77   const char   *name;
78   const char   *value;
79   unsigned int flags;
80} taginfo[] = {
81   { "aeroway",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
82   { "amenity",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
83   { "boundary",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
84   { "bridge",    NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
85   { "building",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
86   { "craft",     NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
87   { "emergency", NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
88   { "highway",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
89   { "historic",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
90   { "landuse",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
91   { "leisure",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
92   { "military",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
93   { "natural",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
94   { "office",    NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
95   { "place",     NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
96   { "railway",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
97   { "shop",      NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
98   { "tourism",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
99   { "tunnel",    NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
100   { "waterway",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
101   { NULL,        NULL,             0                                     }
102};
103
104//static int gazetteer_delete_relation(osmid_t osm_id);
105
106static const struct output_options *Options = NULL;
107static PGconn *Connection = NULL;
108static int CopyActive = 0;
109static char Buffer[BUFFER_SIZE];
110static unsigned int BufferLen = 0;
111
112static PGconn *ConnectionError = NULL;
113static int CopyErrorActive = 0;
114static char BufferError[BUFFER_SIZE];
115static unsigned int BufferErrorLen = 0;
116
117static FILE * hLog = NULL;
118
119static void require_slim_mode(void)
120{
121   if (!Options->slim)
122   {
123      fprintf(stderr, "Cannot apply diffs unless in slim mode\n");
124      exit_nicely();
125   }
126
127   return;
128}
129
130static void copy_data(const char *sql)
131{
132   unsigned int sqlLen = strlen(sql);
133
134   /* Make sure we have an active copy */
135   if (!CopyActive)
136   {
137      pgsql_exec(Connection, PGRES_COPY_IN, "COPY place FROM STDIN");
138      CopyActive = 1;
139   }
140
141   /* If the combination of old and new data is too big, flush old data */
142   if (BufferLen + sqlLen > BUFFER_SIZE - 10)
143   {
144      pgsql_CopyData("place", Connection, Buffer);
145      BufferLen = 0;
146   }
147
148   /*
149    * If new data by itself is too big, output it immediately,
150    * otherwise just add it to the buffer.
151    */
152   if (sqlLen > BUFFER_SIZE - 10)
153   {
154      pgsql_CopyData("Place", Connection, sql);
155      sqlLen = 0;
156   }
157   else if (sqlLen > 0)
158   {
159      strcpy(Buffer + BufferLen, sql);
160      BufferLen += sqlLen;
161      sqlLen = 0;
162   }
163
164   /* If we have completed a line, output it */
165   if (BufferLen > 0 && Buffer[BufferLen-1] == '\n')
166   {
167      pgsql_CopyData("place", Connection, Buffer);
168      BufferLen = 0;
169   }
170
171   return;
172}
173
174static void stop_copy(void)
175{
176   PGresult *res;
177
178   /* Do we have a copy active? */
179   if (!CopyActive) return;
180
181   /* Terminate the copy */
182   if (PQputCopyEnd(Connection, NULL) != 1)
183   {
184      fprintf(stderr, "COPY_END for place failed: %s\n", PQerrorMessage(Connection));
185      exit_nicely();
186   }
187
188   /* Check the result */
189   res = PQgetResult(Connection);
190   if (PQresultStatus(res) != PGRES_COMMAND_OK)
191   {
192      fprintf(stderr, "COPY_END for place failed: %s\n", PQerrorMessage(Connection));
193      PQclear(res);
194      exit_nicely();
195   }
196
197   /* Discard the result */
198   PQclear(res);
199
200   /* We no longer have an active copy */
201   CopyActive = 0;
202
203   return;
204}
205
206static void copy_error_data(const char *sql)
207{
208   unsigned int sqlLen = strlen(sql);
209
210fprintf(hLog, "%s", sql);
211
212   /* Make sure we have an active copy */
213   if (!CopyErrorActive)
214   {
215      pgsql_exec(ConnectionError, PGRES_COPY_IN, "COPY import_polygon_error (osm_type, osm_id, class, type, name, country_code, updated, errormessage, prevgeometry, newgeometry) FROM stdin;");
216      CopyErrorActive = 1;
217   }
218
219   /* If the combination of old and new data is too big, flush old data */
220   if (BufferErrorLen + sqlLen > BUFFER_SIZE - 10)
221   {
222      pgsql_CopyData("import_polygon_error", ConnectionError, BufferError);
223      BufferErrorLen = 0;
224   }
225
226   /*
227    * If new data by itself is too big, output it immediately,
228    * otherwise just add it to the buffer.
229    */
230   if (sqlLen > BUFFER_SIZE - 10)
231   {
232      pgsql_CopyData("import_polygon_error", ConnectionError, sql);
233      sqlLen = 0;
234   }
235   else if (sqlLen > 0)
236   {
237      strcpy(BufferError + BufferErrorLen, sql);
238      BufferErrorLen += sqlLen;
239      sqlLen = 0;
240   }
241
242   /* If we have completed a line, output it */
243   if (BufferErrorLen > 0 && BufferError[BufferErrorLen-1] == '\n')
244   {
245      pgsql_CopyData("place", ConnectionError, BufferError);
246      BufferErrorLen = 0;
247   }
248
249   return;
250}
251
252static void stop_error_copy(void)
253{
254   PGresult *res;
255
256   /* Do we have a copy active? */
257   if (!CopyErrorActive) return;
258
259   /* Terminate the copy */
260   if (PQputCopyEnd(ConnectionError, NULL) != 1)
261   {
262      fprintf(stderr, "COPY_END for import_polygon_error failed: %s\n", PQerrorMessage(ConnectionError));
263      exit_nicely();
264   }
265
266   /* Check the result */
267   res = PQgetResult(ConnectionError);
268   if (PQresultStatus(res) != PGRES_COMMAND_OK)
269   {
270      fprintf(stderr, "COPY_END for import_polygon_error failed: %s\n", PQerrorMessage(ConnectionError));
271      PQclear(res);
272      exit_nicely();
273   }
274
275   /* Discard the result */
276   PQclear(res);
277
278   /* We no longer have an active copy */
279   CopyErrorActive = 0;
280
281   return;
282}
283
284static int split_tags(struct keyval *tags, unsigned int flags, struct keyval *names, struct keyval *places, struct keyval *extratags, 
285   int* admin_level, char ** housenumber, char ** street, char ** isin, char ** postcode, char ** countrycode)
286{
287   int placehouse = 0;
288   int placebuilding = 0;
289   char landuse[256];
290   struct keyval *item;
291
292   *admin_level = ADMINLEVEL_NONE;
293   *housenumber = 0;
294   *street = 0;
295   *isin = 0;
296   int isinsize = 0;
297   *postcode = 0;
298   *countrycode = 0;
299   *landuse = 0;
300
301   /* Initialise the result lists */
302   initList(names);
303   initList(places);
304   initList(extratags);
305
306   /* Loop over the tags */
307   while ((item = popItem(tags)) != NULL)
308   {
309//      fprintf(stderr, "%s\n", item->key);
310
311      /* If this is a name tag, add it to the name list */
312      if (strcmp(item->key, "ref") == 0 ||
313          strcmp(item->key, "int_ref") == 0 ||
314          strcmp(item->key, "nat_ref") == 0 ||
315          strcmp(item->key, "reg_ref") == 0 ||
316          strcmp(item->key, "loc_ref") == 0 ||
317          strcmp(item->key, "old_ref") == 0 ||
318          strcmp(item->key, "ncn_ref") == 0 ||
319          strcmp(item->key, "rcn_ref") == 0 ||
320          strcmp(item->key, "lcn_ref") == 0 ||
321          strcmp(item->key, "iata") == 0 ||
322          strcmp(item->key, "icao") == 0 ||
323          strcmp(item->key, "pcode:1") == 0 ||
324          strcmp(item->key, "pcode:2") == 0 ||
325          strcmp(item->key, "pcode:3") == 0 ||
326          strcmp(item->key, "un:pcode:1") == 0 ||
327          strcmp(item->key, "un:pcode:2") == 0 ||
328          strcmp(item->key, "un:pcode:3") == 0 ||
329          strcmp(item->key, "name") == 0 ||
330          (strncmp(item->key, "name:", 5) == 0) ||
331          strcmp(item->key, "int_name") == 0 ||
332          (strncmp(item->key, "int_name:", 9) == 0) || 
333          strcmp(item->key, "nat_name") == 0 ||
334          (strncmp(item->key, "nat_name:", 9) == 0) || 
335          strcmp(item->key, "reg_name") == 0 ||
336          (strncmp(item->key, "reg_name:", 9) == 0) || 
337          strcmp(item->key, "loc_name") == 0 ||
338          (strncmp(item->key, "loc_name:", 9) == 0) || 
339          strcmp(item->key, "old_name") == 0 ||
340          (strncmp(item->key, "old_name:", 9) == 0) || 
341          strcmp(item->key, "alt_name") == 0 ||
342          (strncmp(item->key, "alt_name:", 9) == 0) || 
343          strcmp(item->key, "official_name") == 0 ||
344          (strncmp(item->key, "official_name:", 14) == 0) || 
345          strcmp(item->key, "commonname") == 0 ||
346          (strncmp(item->key, "commonname:", 11) == 0) ||
347          strcmp(item->key, "common_name") == 0 ||
348          (strncmp(item->key, "common_name:", 12) == 0) ||
349          strcmp(item->key, "place_name") == 0 ||
350          (strncmp(item->key, "place_name:", 11) == 0) ||
351          strcmp(item->key, "short_name") == 0 ||
352          (strncmp(item->key, "short_name:", 11) == 0) ||
353          strcmp(item->key, "operator") == 0) //operator is a bit of an oddity
354      {
355         pushItem(names, item);
356      }
357      else if (strcmp(item->key, "aeroway") == 0 ||
358               strcmp(item->key, "amenity") == 0 ||
359               strcmp(item->key, "boundary") == 0 ||
360               strcmp(item->key, "bridge") == 0 ||
361               strcmp(item->key, "craft") == 0 ||
362               strcmp(item->key, "emergency") == 0 ||
363               strcmp(item->key, "highway") == 0 ||
364               strcmp(item->key, "historic") == 0 ||
365               strcmp(item->key, "leisure") == 0 ||
366               strcmp(item->key, "military") == 0 ||
367               strcmp(item->key, "natural") == 0 ||
368               strcmp(item->key, "office") == 0 ||
369               strcmp(item->key, "place") == 0 ||
370               strcmp(item->key, "railway") == 0 ||
371               strcmp(item->key, "shop") == 0 ||
372               strcmp(item->key, "tourism") == 0 ||
373               strcmp(item->key, "tunnel") == 0 ||
374               strcmp(item->key, "waterway") == 0 )
375      {
376         pushItem(places, item);
377      }
378      else if (strcmp(item->key, "addr:housename") == 0)
379      {
380         pushItem(names, item);
381         placehouse = 1;
382      }
383      else if (strcmp(item->key, "landuse") == 0)
384      {
385         strcpy(landuse, item->value);
386         freeItem(item);
387      }
388      else if (strcmp(item->key, "postal_code") == 0 ||
389          strcmp(item->key, "post_code") == 0 ||
390          strcmp(item->key, "postcode") == 0 ||
391          strcmp(item->key, "addr:postcode") == 0 ||
392          strcmp(item->key, "tiger:zip_left") == 0 ||
393          strcmp(item->key, "tiger:zip_right") == 0)
394      {
395         if (*postcode)
396            freeItem(item);
397         else
398            *postcode = item->value;
399      }
400      else if (strcmp(item->key, "addr:street") == 0)
401      {
402         *street = item->value;
403      }
404      else if ((strcmp(item->key, "country_code_iso3166_1_alpha_2") == 0 || 
405                strcmp(item->key, "country_code_iso3166_1") == 0 || 
406                strcmp(item->key, "country_code_iso3166") == 0 || 
407                strcmp(item->key, "country_code") == 0 || 
408                strcmp(item->key, "iso3166-1") == 0 || 
409                strcmp(item->key, "ISO3166-1") == 0 || 
410                strcmp(item->key, "iso3166") == 0 || 
411                strcmp(item->key, "is_in:country_code") == 0 || 
412                strcmp(item->key, "addr:country") == 0 ||
413                strcmp(item->key, "addr:country_code") == 0) 
414                && strlen(item->value) == 2)
415      {
416         *countrycode = item->value;
417      }
418      else if (strcmp(item->key, "addr:housenumber") == 0)
419      {
420         // house number can be far more complex than just a single house number - leave for postgresql to deal with
421         *housenumber = item->value; 
422         placehouse = 1;
423      }
424      else if (strcmp(item->key, "addr:interpolation") == 0)
425      {
426         // house number can be far more complex than just a single house number - leave for postgresql to deal with
427         *housenumber = item->value; 
428         addItem(places, "place", "houses", 1);
429      }
430      else if (strcmp(item->key, "is_in") == 0 ||
431          (strncmp(item->key, "is_in:", 5) == 0) ||
432          strcmp(item->key, "addr:country")== 0 ||
433          strcmp(item->key, "addr:county")== 0 ||
434          strcmp(item->key, "tiger:county")== 0 ||
435          strcmp(item->key, "addr:city") == 0 ||
436          strcmp(item->key, "addr:state_code") == 0 ||
437          strcmp(item->key, "addr:state") == 0)
438      {
439         *isin = realloc(*isin, isinsize + 2 + strlen(item->value));
440         *(*isin+isinsize) = ',';
441         strcpy(*isin+1+isinsize, item->value);
442         isinsize += 1 + strlen(item->value);
443      }
444      else if (strcmp(item->key, "admin_level") == 0)
445      {
446         *admin_level = atoi(item->value);
447      }
448      else if (strcmp(item->key, "tracktype") == 0 ||
449               strcmp(item->key, "traffic_calming") == 0 ||
450               strcmp(item->key, "service") == 0 ||
451               strcmp(item->key, "cuisine") == 0 ||
452               strcmp(item->key, "capital") == 0 ||
453               strcmp(item->key, "dispending") == 0 ||
454               strcmp(item->key, "religion") == 0 ||
455               strcmp(item->key, "denomination") == 0 ||
456               strcmp(item->key, "sport") == 0 ||
457               strcmp(item->key, "internet_access") == 0 ||
458               strcmp(item->key, "lanes") == 0 ||
459               strcmp(item->key, "surface") == 0 ||
460               strcmp(item->key, "smoothness") == 0 ||
461               strcmp(item->key, "width") == 0 ||
462               strcmp(item->key, "est_width") == 0 ||
463               strcmp(item->key, "incline") == 0 ||
464               strcmp(item->key, "opening_hours") == 0 ||
465               strcmp(item->key, "food_hours") == 0 ||
466               strcmp(item->key, "collection_times") == 0 ||
467               strcmp(item->key, "service_times") == 0 ||
468               strcmp(item->key, "smoking_hours") == 0 ||
469               strcmp(item->key, "disused") == 0 ||
470               strcmp(item->key, "wheelchair") == 0 ||
471               strcmp(item->key, "sac_scale") == 0 ||
472               strcmp(item->key, "trail_visibility") == 0 ||
473               strcmp(item->key, "mtb:scale") == 0 ||
474               strcmp(item->key, "mtb:description") == 0 ||
475               strcmp(item->key, "wood") == 0 ||
476               strcmp(item->key, "drive_thru") == 0 ||
477               strcmp(item->key, "drive_in") == 0 ||
478               strcmp(item->key, "access") == 0 ||
479               strcmp(item->key, "vehicle") == 0 ||
480               strcmp(item->key, "bicyle") == 0 ||
481               strcmp(item->key, "foot") == 0 ||
482               strcmp(item->key, "goods") == 0 ||
483               strcmp(item->key, "hgv") == 0 ||
484               strcmp(item->key, "motor_vehicle") == 0 ||
485               strcmp(item->key, "motor_car") == 0 ||
486               (strncmp(item->key, "access:", 7) == 0) ||
487               (strncmp(item->key, "contact:", 8) == 0) ||
488               (strncmp(item->key, "drink:", 6) == 0) ||
489               strcmp(item->key, "oneway") == 0 ||
490               strcmp(item->key, "date_on") == 0 ||
491               strcmp(item->key, "date_off") == 0 ||
492               strcmp(item->key, "day_on") == 0 ||
493               strcmp(item->key, "day_off") == 0 ||
494               strcmp(item->key, "hour_on") == 0 ||
495               strcmp(item->key, "hour_off") == 0 ||
496               strcmp(item->key, "maxweight") == 0 ||
497               strcmp(item->key, "maxheight") == 0 ||
498               strcmp(item->key, "speed") == 0 ||
499               strcmp(item->key, "disused") == 0 ||
500               strcmp(item->key, "toll") == 0 ||
501               strcmp(item->key, "charge") == 0 ||
502               strcmp(item->key, "population") == 0 ||
503               strcmp(item->key, "description") == 0 ||
504               strcmp(item->key, "image") == 0 ||
505               strcmp(item->key, "attribution") == 0 ||
506               strcmp(item->key, "fax") == 0 ||
507               strcmp(item->key, "email") == 0 ||
508               strcmp(item->key, "url") == 0 ||
509               strcmp(item->key, "website") == 0 ||
510               strcmp(item->key, "phone") == 0 ||
511               strcmp(item->key, "phone") == 0 ||
512               strcmp(item->key, "tel") == 0 ||
513               strcmp(item->key, "real_ale") == 0 ||
514               strcmp(item->key, "smoking") == 0 ||
515               strcmp(item->key, "food") == 0 ||
516               strcmp(item->key, "camra") == 0 ||
517               strcmp(item->key, "brewery") == 0 ||
518               strcmp(item->key, "wikipedia") == 0)
519      {
520          pushItem(extratags, item);
521      }
522      else if (strcmp(item->key, "building") == 0)
523      {
524          placebuilding = 1;
525      }
526      else
527      {
528         freeItem(item);
529      }
530   }
531
532   if (placehouse && !listHasData(places))
533   {
534      addItem(places, "place", "house", 1);
535   }
536
537   // Fallback place types - only used if we didn't create something more specific already
538   if (placebuilding && !listHasData(places))
539   {
540      addItem(places, "building", "yes", 1);
541   }
542
543   if (*landuse && !listHasData(places))
544   {
545      addItem(places, "landuse", landuse, 1);
546   }
547
548   if (*postcode && !listHasData(places))
549   {
550      addItem(places, "place", "postcode", 1);
551   }
552
553   // Try to convert everything to an area
554   return 1;
555}
556
557void escape_array_record(char *out, int len, const char *in)
558{
559    int count = 0;
560    const char *old_in = in, *old_out = out;
561
562    if (!len)
563        return;
564
565    while(*in && count < len-3) {
566        switch(*in) {
567            case '\\': *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; count+= 8; break;
568            case '\n':
569            case '\r':
570            case '\t':
571            case '"':
572                // This is a bit naughty - we know that nominatim ignored these characters so just drop them now for simplicity
573                *out++ = ' '; count++; break;
574            default:   *out++ = *in; count++; break;
575        }
576        in++;
577    }
578    *out = '\0';
579
580    if (*in)
581        fprintf(stderr, "%s truncated at %d chars: %s\n%s\n", __FUNCTION__, count, old_in, old_out);
582}
583
584
585static void add_place_v1(char osm_type, osmid_t osm_id, const char *class, const char *type, struct keyval *names, struct keyval *extratags,
586   int adminlevel, const char *housenumber, const char *street, const char *isin, const char *postcode, const char *countrycode, const char *wkt)
587{
588   int first;
589   struct keyval *name;
590   char sql[2048];
591
592   /* Output a copy line for this place */
593   sprintf(sql, "\\N\t%c\t%" PRIdOSMID "\t", osm_type, osm_id);
594   copy_data(sql);
595
596   escape(sql, sizeof(sql), class);
597   copy_data(sql);
598   copy_data("\t");
599
600   escape(sql, sizeof(sql), type);
601   copy_data(sql);
602   copy_data("\t");
603
604   /* start name array */
605   if (listHasData(names))
606   {
607      copy_data("{");
608      first = 1;
609      for (name = firstItem(names); name; name = nextItem(names, name))
610      {
611         if (first) first = 0;
612         else copy_data(",");
613
614         copy_data("\"(\\\\\"");
615
616         escape_array_record(sql, sizeof(sql), name->key);
617         copy_data(sql);
618
619         copy_data("\\\\\",\\\\\"");
620
621         escape_array_record(sql, sizeof(sql), name->value);
622         copy_data(sql);
623
624         copy_data("\\\\\")\"");
625      }
626      copy_data("}\t");
627   }
628   else
629   {
630      copy_data("\\N\t");
631   }
632
633   sprintf(sql, "%d\t", adminlevel);
634   copy_data(sql);
635
636   if (housenumber)
637   {
638      escape(sql, sizeof(sql), housenumber);
639      copy_data(sql);
640      copy_data("\t");
641   }
642   else
643   {
644      copy_data("\\N\t");
645   }
646
647   if (street)
648   {
649      escape(sql, sizeof(sql), street);
650      copy_data(sql);
651      copy_data("\t");
652   }
653   else
654   {
655      copy_data("\\N\t");
656   }
657
658   if (isin)
659   {
660      // Skip the leading ',' from the contactination
661      escape(sql, sizeof(sql), isin+1);
662      copy_data(sql);
663      copy_data("\t");
664   }
665   else
666   {
667      copy_data("\\N\t");
668   }
669
670   if (postcode)
671   {
672      escape(sql, sizeof(sql), postcode);
673      copy_data(sql);
674      copy_data("\t");
675   }
676   else
677   {
678      copy_data("\\N\t");
679   }
680
681   if (countrycode)
682   {
683      escape(sql, sizeof(sql), countrycode);
684      copy_data(sql);
685      copy_data("\t");
686   }
687   else
688   {
689     copy_data("\\N\t");
690   }
691
692   copy_data("\\N\t");
693
694   copy_data("\\N\t");
695
696   copy_data("\\N\t");
697
698   copy_data("\\N\t");
699
700   sprintf(sql, "SRID=%d;", SRID);
701   copy_data(sql);
702   copy_data(wkt);
703
704   copy_data("\n");
705
706//fprintf(stderr, "%c %" PRIdOSMID " %s\n", osm_type, osm_id, wkt);
707
708   return;
709}
710
711static void add_place_v2(char osm_type, osmid_t osm_id, const char *class, const char *type, struct keyval *names, struct keyval *extratags,
712   int adminlevel, const char *housenumber, const char *street, const char *isin, const char *postcode, const char *countrycode, const char *wkt)
713{
714   int first;
715   struct keyval *name;
716   char sql[2048];
717
718   /* Output a copy line for this place */
719   sprintf(sql, "%c\t%" PRIdOSMID "\t", osm_type, osm_id);
720   copy_data(sql);
721
722   escape(sql, sizeof(sql), class);
723   copy_data(sql);
724   copy_data("\t");
725
726   escape(sql, sizeof(sql), type);
727   copy_data(sql);
728   copy_data("\t");
729
730   /* start name array */
731   if (listHasData(names))
732   {
733      first = 1;
734      for (name = firstItem(names); name; name = nextItem(names, name))
735      {
736         if (first) first = 0;
737         else copy_data(", ");
738
739         copy_data("\"");
740
741         escape_array_record(sql, sizeof(sql), name->key);
742         copy_data(sql);
743
744         copy_data("\"=>\"");
745
746         escape_array_record(sql, sizeof(sql), name->value);
747         copy_data(sql);
748
749         copy_data("\"");
750      }
751      copy_data("\t");
752   }
753   else
754   {
755      copy_data("\\N\t");
756   }
757
758   sprintf(sql, "%d\t", adminlevel);
759   copy_data(sql);
760
761   if (housenumber)
762   {
763      escape(sql, sizeof(sql), housenumber);
764      copy_data(sql);
765      copy_data("\t");
766   }
767   else
768   {
769      copy_data("\\N\t");
770   }
771
772   if (street)
773   {
774      escape(sql, sizeof(sql), street);
775      copy_data(sql);
776      copy_data("\t");
777   }
778   else
779   {
780      copy_data("\\N\t");
781   }
782
783   if (isin)
784   {
785      // Skip the leading ',' from the contactination
786      escape(sql, sizeof(sql), isin+1);
787      copy_data(sql);
788      copy_data("\t");
789   }
790   else
791   {
792      copy_data("\\N\t");
793   }
794
795   if (postcode)
796   {
797      escape(sql, sizeof(sql), postcode);
798      copy_data(sql);
799      copy_data("\t");
800   }
801   else
802   {
803      copy_data("\\N\t");
804   }
805
806   if (countrycode)
807   {
808      escape(sql, sizeof(sql), countrycode);
809      copy_data(sql);
810      copy_data("\t");
811   }
812   else
813   {
814     copy_data("\\N\t");
815   }
816
817   /* extra tags array */
818   if (listHasData(names))
819   {
820      first = 1;
821      for (name = firstItem(extratags); name; name = nextItem(extratags, name))
822      {
823         if (first) first = 0;
824         else copy_data(", ");
825
826         copy_data("\"");
827
828         escape_array_record(sql, sizeof(sql), name->key);
829         copy_data(sql);
830
831         copy_data("\"=>\"");
832
833         escape_array_record(sql, sizeof(sql), name->value);
834         copy_data(sql);
835
836         copy_data("\"");
837      }
838      copy_data("\t");
839   }
840   else
841   {
842      copy_data("\\N\t");
843   }
844
845   sprintf(sql, "SRID=%d;", SRID);
846   copy_data(sql);
847   copy_data(wkt);
848
849   copy_data("\n");
850
851//fprintf(stderr, "%c %" PRIdOSMID " %s\n", osm_type, osm_id, wkt);
852
853   return;
854}
855
856static void add_polygon_error(char osm_type, osmid_t osm_id, const char *class, const char *type, 
857  struct keyval *names, const char *countrycode, const char *wkt)
858{
859   int first;
860   struct keyval *name;
861   char sql[2048];
862
863   /* Output a copy line for this place */
864   sprintf(sql, "%c\t%" PRIdOSMID "\t", osm_type, osm_id);
865   copy_error_data(sql);
866
867   escape(sql, sizeof(sql), class);
868   copy_error_data(sql);
869   copy_error_data("\t");
870
871   escape(sql, sizeof(sql), type);
872   copy_error_data(sql);
873   copy_error_data("\t");
874
875   /* start name array */
876   if (listHasData(names))
877   {
878      first = 1;
879      for (name = firstItem(names); name; name = nextItem(names, name))
880      {
881         if (first) first = 0;
882         else copy_error_data(", ");
883
884         copy_error_data("\"");
885
886         escape_array_record(sql, sizeof(sql), name->key);
887         copy_error_data(sql);
888
889         copy_error_data("\"=>\"");
890
891         escape_array_record(sql, sizeof(sql), name->value);
892         copy_error_data(sql);
893
894         copy_error_data("\"");
895      }
896      copy_error_data("\t");
897   }
898   else
899   {
900      copy_error_data("\\N\t");
901   }
902
903   if (countrycode)
904   {
905      escape(sql, sizeof(sql), countrycode);
906      copy_error_data(sql);
907      copy_error_data("\t");
908   }
909   else
910   {
911     copy_error_data("\\N\t");
912   }
913
914   copy_error_data("now\tNot a polygon\t\\N\t");
915
916   sprintf(sql, "SRID=%d;", SRID);
917   copy_error_data(sql);
918   copy_error_data(wkt);
919
920   copy_error_data("\n");
921
922//fprintf(stderr, "%c %" PRIdOSMID " %s\n", osm_type, osm_id, wkt);
923
924   return;
925}
926
927static void add_place(char osm_type, osmid_t osm_id, const char *class, const char *type, struct keyval *names, struct keyval *extratags,
928   int adminlevel, const char *housenumber, const char *street, const char *isin, const char *postcode, const char *countrycode, const char *wkt)
929{
930   if (Options->enable_hstore)
931      add_place_v2(osm_type, osm_id, class, type, names, extratags, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
932   else
933      add_place_v1(osm_type, osm_id, class, type, names, extratags, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
934   return;
935}
936
937static void delete_place(char osm_type, osmid_t osm_id)
938{
939   /* Stop any active copy */
940   stop_copy();
941
942   /* Delete all places for this object */
943   pgsql_exec(Connection, PGRES_COMMAND_OK, "DELETE FROM place WHERE osm_type = '%c' AND osm_id = %" PRIdOSMID, osm_type, osm_id);
944
945   return;
946}
947
948static int gazetteer_out_start(const struct output_options *options)
949{
950   /* Save option handle */
951   Options = options;
952
953   /* Connection to the database */
954   Connection = PQconnectdb(options->conninfo);
955   ConnectionError = PQconnectdb(options->conninfo);
956
957   /* Check to see that the backend connection was successfully made */
958   if (PQstatus(Connection) != CONNECTION_OK)
959   {
960      fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(Connection));
961      exit_nicely();
962   }
963
964   /* Start a transaction */
965   pgsql_exec(Connection, PGRES_COMMAND_OK, "BEGIN");
966
967   /* (Re)create the table unless we are appending */
968   if (!Options->append)
969   {
970      /* Drop any existing table */
971      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS place");
972      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TYPE if exists keyvalue cascade");
973      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TYPE if exists wordscore cascade");
974      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TYPE if exists stringlanguagetype cascade");
975      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TYPE if exists keyvaluetype cascade");
976      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP FUNCTION IF EXISTS get_connected_ways(integer[])");
977
978      /* Create types and functions */
979      if (!Options->enable_hstore)
980      {
981            pgsql_exec(Connection, PGRES_COMMAND_OK, CREATE_KEYVALUETYPE_TYPE, "", "");
982      }
983      pgsql_exec(Connection, PGRES_COMMAND_OK, CREATE_WORDSCORE_TYPE, Options->tblsmain_data);
984
985      /* Create the new table */
986      if (Options->tblsmain_data)
987      {
988          pgsql_exec(Connection, PGRES_COMMAND_OK, Options->enable_hstore ? 
989            V2_CREATE_PLACE_TABLE : CREATE_PLACE_TABLE, "TABLESPACE", Options->tblsmain_data);
990      }
991      else
992      {
993          pgsql_exec(Connection, PGRES_COMMAND_OK, Options->enable_hstore ? V2_CREATE_PLACE_TABLE : CREATE_PLACE_TABLE, "", "");
994      }
995      if (Options->tblsmain_index)
996      {
997          pgsql_exec(Connection, PGRES_COMMAND_OK, CREATE_PLACE_ID_INDEX, "TABLESPACE", Options->tblsmain_index);
998      }
999      else
1000      {
1001          pgsql_exec(Connection, PGRES_COMMAND_OK, CREATE_PLACE_ID_INDEX, "", "");
1002      }
1003
1004      pgsql_exec(Connection, PGRES_TUPLES_OK, "SELECT AddGeometryColumn('place', 'geometry', %d, 'GEOMETRY', 2)", SRID);
1005      pgsql_exec(Connection, PGRES_COMMAND_OK, "ALTER TABLE place ALTER COLUMN geometry SET NOT NULL");
1006   }
1007
1008   /* Setup middle layer */
1009   options->mid->start(options);
1010
1011   hLog = fopen("log", "w");
1012
1013   return 0;
1014}
1015
1016static void gazetteer_out_stop(void)
1017{
1018   /* Process any remaining ways and relations */
1019//   Options->mid->iterate_ways( gazetteer_out_way );
1020//   Options->mid->iterate_relations( gazetteer_process_relation );
1021
1022   /* No longer need to access middle layer */
1023   Options->mid->stop();
1024
1025   /* Stop any active copy */
1026   stop_copy();
1027   //stop_error_copy();
1028   fclose(hLog);
1029
1030   /* Commit transaction */
1031   pgsql_exec(Connection, PGRES_COMMAND_OK, "COMMIT");
1032
1033   /* Analyse the table */
1034   //pgsql_exec(Connection, PGRES_COMMAND_OK, "ANALYZE place");
1035
1036   return;
1037}
1038
1039static void gazetteer_out_cleanup(void)
1040{
1041   return;
1042}
1043
1044static int gazetteer_add_node(osmid_t id, double lat, double lon, struct keyval *tags)
1045{
1046   struct keyval names;
1047   struct keyval places;
1048   struct keyval extratags;
1049   struct keyval *place;
1050   int adminlevel;
1051   char * housenumber;
1052   char * street;
1053   char * isin;
1054   char * postcode;
1055   char * countrycode;
1056   char wkt[128];
1057
1058//fprintf(stderr, "node\n");
1059
1060   /* Split the tags */
1061   split_tags(tags, TAGINFO_NODE, &names, &places, &extratags, &adminlevel, &housenumber, &street, &isin, &postcode, &countrycode);
1062
1063   /* Feed this node to the middle layer */
1064   Options->mid->nodes_set(id, lat, lon, tags);
1065
1066   /* Are we interested in this item? */
1067   if (listHasData(&names) || listHasData(&places))
1068   {
1069      sprintf(wkt, "POINT(%.15g %.15g)", lon, lat);
1070      for (place = firstItem(&places); place; place = nextItem(&places, place))
1071      {
1072         add_place('N', id, place->key, place->value, &names, &extratags, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
1073      }
1074   }
1075
1076   if (isin) free(isin);
1077
1078   /* Free tag lists */
1079   resetList(&names);
1080   resetList(&places);
1081
1082   return 0;
1083}
1084
1085static int gazetteer_add_way(osmid_t id, osmid_t *ndv, int ndc, struct keyval *tags)
1086{
1087   struct keyval names;
1088   struct keyval places;
1089   struct keyval extratags;
1090   struct keyval *place;
1091   int adminlevel;
1092   char * housenumber;
1093   char * street;
1094   char * isin;
1095   char * postcode;
1096   char * countrycode;
1097   int area;
1098
1099//fprintf(stderr, "way\n");
1100
1101   /* Split the tags */
1102   area = split_tags(tags, TAGINFO_WAY, &names, &places, &extratags, &adminlevel, &housenumber, &street, &isin, &postcode, &countrycode);
1103
1104   /* Feed this way to the middle layer */
1105   Options->mid->ways_set(id, ndv, ndc, tags, 0);
1106
1107   /* Are we interested in this item? */
1108   if (listHasData(&names) || listHasData(&places))
1109   {
1110      struct osmNode *nodev;
1111      int nodec;
1112      char *wkt;
1113   
1114      /* Fetch the node details */
1115      nodev = malloc(ndc * sizeof(struct osmNode));
1116      nodec = Options->mid->nodes_get_list(nodev, ndv, ndc);
1117
1118      /* Get the geometry of the object */
1119      if ((wkt = get_wkt_simple(nodev, nodec, area)) != NULL && strlen(wkt) > 0)
1120      {
1121         for (place = firstItem(&places); place; place = nextItem(&places, place))
1122         {
1123            add_place('W', id, place->key, place->value, &names, &extratags, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
1124         }
1125      }
1126
1127      /* Free the geometry */
1128      free(wkt);
1129
1130      /* Free the nodes */
1131      free(nodev);
1132   }
1133
1134   if (isin) free(isin);
1135
1136   /* Free tag lists */
1137   resetList(&names);
1138   resetList(&places);
1139
1140   return 0;
1141}
1142
1143static int gazetteer_add_relation(osmid_t id, struct member *members, int member_count, struct keyval *tags)
1144{
1145   struct keyval names;
1146   struct keyval places;
1147   struct keyval extratags;
1148   int adminlevel;
1149   char * housenumber;
1150   char * street;
1151   char * isin;
1152   char * postcode;
1153   char * countrycode;
1154   int area, wkt_size;
1155   const char *type;
1156   const char *boundary;
1157
1158   type = getItem(tags, "type");
1159   if (!type)
1160      return 0;
1161
1162   if (!strcmp(type, "associatedStreet") || !strcmp(type, "relatedStreet"))
1163   {
1164      Options->mid->relations_set(id, members, member_count, tags);
1165      return 0;
1166   }
1167
1168   if (strcmp(type, "boundary") && strcmp(type, "multipolygon"))
1169      return 0;
1170
1171   boundary = getItem(tags, "boundary");
1172   if (!boundary) boundary = "administrative";
1173
1174   /* Split the tags */
1175   area = split_tags(tags, TAGINFO_AREA, &names, &places, &extratags, &adminlevel, &housenumber, &street, &isin, &postcode, &countrycode);
1176
1177   if (listHasData(&names))
1178   {
1179      /* get the boundary path (ways) */
1180      int i, count;
1181      int *xcount = malloc( (member_count+1) * sizeof(int) );
1182      struct keyval *xtags  = malloc( (member_count+1) * sizeof(struct keyval) );
1183      struct osmNode **xnodes = malloc( (member_count+1) * sizeof(struct osmNode*) );
1184
1185      count = 0;
1186      for (i=0; i<member_count; i++)
1187      {
1188         /* only interested in ways */
1189         if (members[i].type != OSMTYPE_WAY)
1190            continue;
1191
1192         initList(&(xtags[count]));
1193         if (Options->mid->ways_get( members[i].id, &(xtags[count]), &(xnodes[count]), &(xcount[count])))
1194            continue;
1195         count++;
1196      }
1197      xnodes[count] = NULL;
1198      xcount[count] = 0;
1199
1200      wkt_size = build_geometry(id, xnodes, xcount, 1, 1, 1000000);
1201      for (i=0;i<wkt_size;i++)
1202      {
1203         char *wkt = get_wkt(i);
1204         if (strlen(wkt) && (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))))
1205         {
1206            if (Options->enable_hstore)
1207                add_place('R', id, "boundary", boundary, &names, &extratags, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
1208            else
1209                add_place('R', id, "boundary", "adminitrative", &names, &extratags, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
1210         }
1211         else
1212         {
1213            //add_polygon_error('R', id, "boundary", "adminitrative", &names, countrycode, wkt);
1214         }
1215         free(wkt);
1216      }
1217      clear_wkts();
1218
1219      for( i=0; i<count; i++ )
1220      {
1221         resetList( &(xtags[i]) );
1222         free( xnodes[i] );
1223      }
1224
1225      free(xcount);
1226      free(xtags);
1227      free(xnodes);
1228   }
1229
1230   if (isin) free(isin);
1231
1232   /* Free tag lists */
1233   resetList(&names);
1234   resetList(&places);
1235
1236   return 0;
1237}
1238
1239static int gazetteer_delete_node(osmid_t id)
1240{
1241   /* Make sure we are in slim mode */
1242   require_slim_mode();
1243
1244   /* Delete all references to this node */
1245   delete_place('N', id);
1246
1247   /* Feed this delete to the middle layer */
1248   Options->mid->nodes_delete(id);
1249
1250   return 0;
1251}
1252
1253static int gazetteer_delete_way(osmid_t id)
1254{
1255   /* Make sure we are in slim mode */
1256   require_slim_mode();
1257
1258   /* Delete all references to this way */
1259   delete_place('W', id);
1260
1261   /* Feed this delete to the middle layer */
1262   Options->mid->ways_delete(id);
1263
1264   return 0;
1265}
1266
1267static int gazetteer_delete_relation(osmid_t id)
1268{
1269   /* Make sure we are in slim mode */
1270   require_slim_mode();
1271
1272   /* Delete all references to this relation */
1273   delete_place('R', id);
1274
1275   /* Feed this delete to the middle layer */
1276   Options->mid->relations_delete(id);
1277
1278   return 0;
1279}
1280
1281static int gazetteer_modify_node(osmid_t id, double lat, double lon, struct keyval *tags)
1282{
1283   require_slim_mode();
1284   Options->mid->nodes_delete(id);
1285   return gazetteer_add_node(id, lat, lon, tags);
1286}
1287
1288static int gazetteer_modify_way(osmid_t id, osmid_t *ndv, int ndc, struct keyval *tags)
1289{
1290   require_slim_mode();
1291   Options->mid->ways_delete(id);
1292   return gazetteer_add_way(id, ndv, ndc, tags);
1293}
1294
1295static int gazetteer_modify_relation(osmid_t id, struct member *members, int member_count, struct keyval *tags)
1296{
1297   require_slim_mode();
1298   Options->mid->relations_delete(id);
1299   return gazetteer_add_relation(id, members, member_count, tags);
1300}
1301
1302struct output_t out_gazetteer = {
1303   .start = gazetteer_out_start,
1304   .stop = gazetteer_out_stop,
1305   .cleanup = gazetteer_out_cleanup,
1306
1307   .node_add = gazetteer_add_node,
1308   .way_add = gazetteer_add_way,
1309   .relation_add = gazetteer_add_relation,
1310
1311   .node_modify = gazetteer_modify_node,
1312   .way_modify = gazetteer_modify_way,
1313   .relation_modify = gazetteer_modify_relation,
1314
1315   .node_delete = gazetteer_delete_node,
1316   .way_delete = gazetteer_delete_way,
1317   .relation_delete = gazetteer_delete_relation
1318};
Note: See TracBrowser for help on using the repository browser.