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

Revision 28184, 42.7 KB checked in by lonvia, 2 years ago (diff)

close database connections when no longer needed

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