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

Revision 25215, 27.5 KB checked in by frederik, 3 years ago (diff)

who ever said you can CREATE TYPE on a TABLESPACE?

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   ") TABLESPACE %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   ") TABLESPACE %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) TABLESPACE %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   { "amenity",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
82   { "highway",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
83   { "historic", NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
84   { "natural",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
85   { "leisure",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
86   { "place",    NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
87   { "railway",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
88   { "shop",     NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
89   { "tourism",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
90   { "waterway", NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
91   { "landuse",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
92   { "building", NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
93   { "bridge",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
94   { "tunnel",   NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
95   { "aeroway",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
96   { "boundary",  NULL,             TAGINFO_NODE|TAGINFO_WAY|TAGINFO_AREA },
97   { NULL,       NULL,             0                                     }
98};
99
100//static int gazetteer_delete_relation(int osm_id);
101
102static const struct output_options *Options = NULL;
103static PGconn *Connection = NULL;
104static int CopyActive = 0;
105static char Buffer[BUFFER_SIZE];
106static unsigned int BufferLen = 0;
107
108static void require_slim_mode(void)
109{
110   if (!Options->slim)
111   {
112      fprintf(stderr, "Cannot apply diffs unless in slim mode\n");
113      exit_nicely();
114   }
115
116   return;
117}
118
119static void copy_data(const char *sql)
120{
121   unsigned int sqlLen = strlen(sql);
122
123   /* Make sure we have an active copy */
124   if (!CopyActive)
125   {
126      pgsql_exec(Connection, PGRES_COPY_IN, "COPY place FROM STDIN");
127      CopyActive = 1;
128   }
129
130   /* If the combination of old and new data is too big, flush old data */
131   if (BufferLen + sqlLen > BUFFER_SIZE - 10)
132   {
133      pgsql_CopyData("place", Connection, Buffer);
134      BufferLen = 0;
135   }
136
137   /*
138    * If new data by itself is too big, output it immediately,
139    * otherwise just add it to the buffer.
140    */
141   if (sqlLen > BUFFER_SIZE - 10)
142   {
143      pgsql_CopyData("Place", Connection, sql);
144      sqlLen = 0;
145   }
146   else if (sqlLen > 0)
147   {
148      strcpy(Buffer + BufferLen, sql);
149      BufferLen += sqlLen;
150      sqlLen = 0;
151   }
152
153   /* If we have completed a line, output it */
154   if (BufferLen > 0 && Buffer[BufferLen-1] == '\n')
155   {
156      pgsql_CopyData("place", Connection, Buffer);
157      BufferLen = 0;
158   }
159
160   return;
161}
162
163static void stop_copy(void)
164{
165   PGresult *res;
166
167   /* Do we have a copy active? */
168   if (!CopyActive) return;
169
170   /* Terminate the copy */
171   if (PQputCopyEnd(Connection, NULL) != 1)
172   {
173      fprintf(stderr, "COPY_END for place failed: %s\n", PQerrorMessage(Connection));
174      exit_nicely();
175   }
176
177   /* Check the result */
178   res = PQgetResult(Connection);
179   if (PQresultStatus(res) != PGRES_COMMAND_OK)
180   {
181      fprintf(stderr, "COPY_END for place failed: %s\n", PQerrorMessage(Connection));
182      PQclear(res);
183      exit_nicely();
184   }
185
186   /* Discard the result */
187   PQclear(res);
188
189   /* We no longer have an active copy */
190   CopyActive = 0;
191
192   return;
193}
194
195static int split_tags(struct keyval *tags, unsigned int flags, struct keyval *names, struct keyval *places, 
196   int* admin_level, char ** housenumber, char ** street, char ** isin, char ** postcode, char ** countrycode)
197{
198   int area = 0;
199   int placehouse = 0;
200   struct keyval *item;
201
202   *admin_level = ADMINLEVEL_NONE;
203   *housenumber = 0;
204   *street = 0;
205   *isin = 0;
206   int isinsize = 0;
207   *postcode = 0;
208   *countrycode = 0;
209
210   /* Initialise the result lists */
211   initList(names);
212   initList(places);
213
214   /* Loop over the tags */
215   while ((item = popItem(tags)) != NULL)
216   {
217//      fprintf(stderr, "%s\n", item->key);
218
219      /* If this is a name tag, add it to the name list */
220      if (strcmp(item->key, "ref") == 0 ||
221          strcmp(item->key, "iata") == 0 ||
222          strcmp(item->key, "icao") == 0 ||
223          strcmp(item->key, "pcode:1") == 0 ||
224          strcmp(item->key, "pcode:2") == 0 ||
225          strcmp(item->key, "pcode:3") == 0 ||
226          strcmp(item->key, "un:pcode:1") == 0 ||
227          strcmp(item->key, "un:pcode:2") == 0 ||
228          strcmp(item->key, "un:pcode:3") == 0 ||
229          strcmp(item->key, "name") == 0 ||
230          (strncmp(item->key, "name:", 5) == 0) ||
231          strcmp(item->key, "int_name") == 0 ||
232          (strncmp(item->key, "int_name:", 9) == 0) || 
233          strcmp(item->key, "nat_name") == 0 ||
234          (strncmp(item->key, "nat_name:", 9) == 0) || 
235          strcmp(item->key, "reg_name") == 0 ||
236          (strncmp(item->key, "reg_name:", 9) == 0) || 
237          strcmp(item->key, "loc_name") == 0 ||
238          (strncmp(item->key, "loc_name:", 9) == 0) || 
239          strcmp(item->key, "old_name") == 0 ||
240          (strncmp(item->key, "old_name:", 9) == 0) || 
241          strcmp(item->key, "alt_name") == 0 ||
242          (strncmp(item->key, "alt_name:", 9) == 0) || 
243          strcmp(item->key, "official_name") == 0 ||
244          (strncmp(item->key, "official_name:", 14) == 0) || 
245          strcmp(item->key, "commonname") == 0 ||
246          (strncmp(item->key, "commonname:", 11) == 0) ||
247          strcmp(item->key, "common_name") == 0 ||
248          (strncmp(item->key, "common_name:", 12) == 0) ||
249          strcmp(item->key, "place_name") == 0 ||
250          (strncmp(item->key, "place_name:", 11) == 0) ||
251          strcmp(item->key, "short_name") == 0 ||
252          (strncmp(item->key, "short_name:", 11) == 0))
253      {
254         pushItem(names, item);
255      }
256      else if (strcmp(item->key, "addr:housename") == 0)
257      {
258         pushItem(names, item);
259         placehouse = 1;
260      }
261      else if (strcmp(item->key, "postal_code") == 0 ||
262          strcmp(item->key, "post_code") == 0 ||
263          strcmp(item->key, "postcode") == 0 ||
264          strcmp(item->key, "addr:postcode") == 0)
265      {
266         *postcode = item->value;
267         addItem(places, "place", "postcode", 1);
268      }
269      else if (strcmp(item->key, "addr:street") == 0)
270      {
271         *street = item->value;
272      }
273      else if ((strcmp(item->key, "country_code_iso3166_1_alpha_2") == 0 || 
274                strcmp(item->key, "country_code_iso3166_1") == 0 || 
275                strcmp(item->key, "country_code_iso3166") == 0 || 
276                strcmp(item->key, "country_code") == 0 || 
277                strcmp(item->key, "iso3166-1") == 0 || 
278                strcmp(item->key, "ISO3166-1") == 0 || 
279                strcmp(item->key, "iso3166") == 0 || 
280                strcmp(item->key, "is_in:country_code") == 0 || 
281                strcmp(item->key, "addr:country") == 0 ||
282                strcmp(item->key, "addr:country_code") == 0) 
283                && strlen(item->value) == 2)
284      {
285         *countrycode = item->value;
286      }
287      else if (strcmp(item->key, "addr:housenumber") == 0)
288      {
289         // house number can be far more complex than just a single house number - leave for postgresql to deal with
290         *housenumber = item->value; 
291         placehouse = 1;
292      }
293      else if (strcmp(item->key, "addr:interpolation") == 0)
294      {
295         // house number can be far more complex than just a single house number - leave for postgresql to deal with
296         *housenumber = item->value; 
297         addItem(places, "place", "houses", 1);
298      }
299      else if (strcmp(item->key, "is_in") == 0 ||
300          (strncmp(item->key, "is_in:", 5) == 0) ||
301          strcmp(item->key, "addr:country")== 0 ||
302          strcmp(item->key, "addr:county")== 0 ||
303          strcmp(item->key, "addr:city") == 0||
304          strcmp(item->key, "addr:state") == 0)
305      {
306         *isin = realloc(*isin, isinsize + 2 + strlen(item->value));
307         *(*isin+isinsize) = ',';
308         strcpy(*isin+1+isinsize, item->value);
309         isinsize += 1 + strlen(item->value);
310      }
311      else if (strcmp(item->key, "admin_level") == 0)
312      {
313         *admin_level = atoi(item->value);
314      }
315      else
316      {
317         const struct taginfo *t;
318
319         /* If this is a tag we want then add it to the place list */
320         for (t = taginfo; t->name != NULL; t++)
321         {
322            if ((t->flags & flags) != 0)
323            {
324               if (strcmp(t->name, item->key) == 0 &&
325                   (t->value == NULL || strcmp(t->value, item->value) == 0))
326               {
327                  if ((t->flags & TAGINFO_AREA) != 0) area = 1;
328
329                  pushItem(places, item);
330
331                  break;
332               }
333            }
334         }
335
336         /* Free the tag if we didn't want it */
337         if (t->name == NULL) freeItem(item);
338      }
339   }
340
341   if (placehouse)
342   {
343      addItem(places, "place", "house", 1);
344   }
345
346   return area;
347}
348
349void escape_array_record(char *out, int len, const char *in)
350{
351    int count = 0;
352    const char *old_in = in, *old_out = out;
353
354    if (!len)
355        return;
356
357    while(*in && count < len-3) {
358        switch(*in) {
359            case '\\': *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; *out++ = '\\'; count+= 8; break;
360            case '\n':
361            case '\r':
362            case '\t':
363            case '"':
364                // This is a bit naughty - we know that nominatim ignored these characters so just drop them now for simplicity
365                *out++ = ' '; count++; break;
366            default:   *out++ = *in; count++; break;
367        }
368        in++;
369    }
370    *out = '\0';
371
372    if (*in)
373        fprintf(stderr, "%s truncated at %d chars: %s\n%s\n", __FUNCTION__, count, old_in, old_out);
374}
375
376
377static void add_place_v1(char osm_type, int osm_id, const char *class, const char *type, struct keyval *names, 
378   int adminlevel, const char *housenumber, const char *street, const char *isin, const char *postcode, const char *countrycode, const char *wkt)
379{
380   int first;
381   struct keyval *name;
382   char sql[2048];
383
384   /* Output a copy line for this place */
385   sprintf(sql, "\\N\t%c\t%d\t", osm_type, osm_id);
386   copy_data(sql);
387
388   escape(sql, sizeof(sql), class);
389   copy_data(sql);
390   copy_data("\t");
391
392   escape(sql, sizeof(sql), type);
393   copy_data(sql);
394   copy_data("\t");
395
396   /* start name array */
397   if (listHasData(names))
398   {
399      copy_data("{");
400      first = 1;
401      for (name = firstItem(names); name; name = nextItem(names, name))
402      {
403         if (first) first = 0;
404         else copy_data(",");
405
406         copy_data("\"(\\\\\"");
407
408         escape_array_record(sql, sizeof(sql), name->key);
409         copy_data(sql);
410
411         copy_data("\\\\\",\\\\\"");
412
413         escape_array_record(sql, sizeof(sql), name->value);
414         copy_data(sql);
415
416         copy_data("\\\\\")\"");
417      }
418      copy_data("}\t");
419   }
420   else
421   {
422      copy_data("\\N\t");
423   }
424
425   sprintf(sql, "%d\t", adminlevel);
426   copy_data(sql);
427
428   if (housenumber)
429   {
430      escape(sql, sizeof(sql), housenumber);
431      copy_data(sql);
432      copy_data("\t");
433   }
434   else
435   {
436      copy_data("\\N\t");
437   }
438
439   if (street)
440   {
441      escape(sql, sizeof(sql), street);
442      copy_data(sql);
443      copy_data("\t");
444   }
445   else
446   {
447      copy_data("\\N\t");
448   }
449
450   if (isin)
451   {
452      // Skip the leading ',' from the contactination
453      escape(sql, sizeof(sql), isin+1);
454      copy_data(sql);
455      copy_data("\t");
456   }
457   else
458   {
459      copy_data("\\N\t");
460   }
461
462   if (postcode)
463   {
464      escape(sql, sizeof(sql), postcode);
465      copy_data(sql);
466      copy_data("\t");
467   }
468   else
469   {
470      copy_data("\\N\t");
471   }
472
473   if (countrycode)
474   {
475      escape(sql, sizeof(sql), countrycode);
476      copy_data(sql);
477      copy_data("\t");
478   }
479   else
480   {
481     copy_data("\\N\t");
482   }
483
484   copy_data("\\N\t");
485
486   copy_data("\\N\t");
487
488   copy_data("\\N\t");
489
490   copy_data("\\N\t");
491
492   sprintf(sql, "SRID=%d;", SRID);
493   copy_data(sql);
494   copy_data(wkt);
495
496   copy_data("\n");
497
498//fprintf(stderr, "%c %d %s\n", osm_type, osm_id, wkt);
499
500   return;
501}
502
503static void add_place_v2(char osm_type, int osm_id, const char *class, const char *type, struct keyval *names, 
504   int adminlevel, const char *housenumber, const char *street, const char *isin, const char *postcode, const char *countrycode, const char *wkt)
505{
506   int first;
507   struct keyval *name;
508   char sql[2048];
509
510   /* Output a copy line for this place */
511   sprintf(sql, "%c\t%d\t", osm_type, osm_id);
512   copy_data(sql);
513
514   escape(sql, sizeof(sql), class);
515   copy_data(sql);
516   copy_data("\t");
517
518   escape(sql, sizeof(sql), type);
519   copy_data(sql);
520   copy_data("\t");
521
522   /* start name array */
523   if (listHasData(names))
524   {
525      first = 1;
526      for (name = firstItem(names); name; name = nextItem(names, name))
527      {
528         if (first) first = 0;
529         else copy_data(", ");
530
531         copy_data("\"");
532
533         escape_array_record(sql, sizeof(sql), name->key);
534         copy_data(sql);
535
536         copy_data("\"=>\"");
537
538         escape_array_record(sql, sizeof(sql), name->value);
539         copy_data(sql);
540
541         copy_data("\"");
542      }
543      copy_data("\t");
544   }
545   else
546   {
547      copy_data("\\N\t");
548   }
549
550   sprintf(sql, "%d\t", adminlevel);
551   copy_data(sql);
552
553   if (housenumber)
554   {
555      escape(sql, sizeof(sql), housenumber);
556      copy_data(sql);
557      copy_data("\t");
558   }
559   else
560   {
561      copy_data("\\N\t");
562   }
563
564   if (street)
565   {
566      escape(sql, sizeof(sql), street);
567      copy_data(sql);
568      copy_data("\t");
569   }
570   else
571   {
572      copy_data("\\N\t");
573   }
574
575   if (isin)
576   {
577      // Skip the leading ',' from the contactination
578      escape(sql, sizeof(sql), isin+1);
579      copy_data(sql);
580      copy_data("\t");
581   }
582   else
583   {
584      copy_data("\\N\t");
585   }
586
587   if (postcode)
588   {
589      escape(sql, sizeof(sql), postcode);
590      copy_data(sql);
591      copy_data("\t");
592   }
593   else
594   {
595      copy_data("\\N\t");
596   }
597
598   if (countrycode)
599   {
600      escape(sql, sizeof(sql), countrycode);
601      copy_data(sql);
602      copy_data("\t");
603   }
604   else
605   {
606     copy_data("\\N\t");
607   }
608
609   // Extra tags - TODO
610   copy_data("\\N\t");
611
612   sprintf(sql, "SRID=%d;", SRID);
613   copy_data(sql);
614   copy_data(wkt);
615
616   copy_data("\n");
617
618//fprintf(stderr, "%c %d %s\n", osm_type, osm_id, wkt);
619
620   return;
621}
622
623static void add_place(char osm_type, int osm_id, const char *class, const char *type, struct keyval *names, 
624   int adminlevel, const char *housenumber, const char *street, const char *isin, const char *postcode, const char *countrycode, const char *wkt)
625{
626   if (Options->enable_hstore)
627      add_place_v2(osm_type, osm_id, class, type, names, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
628   else
629      add_place_v1(osm_type, osm_id, class, type, names, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
630   return;
631}
632
633static void delete_place(char osm_type, int osm_id)
634{
635   /* Stop any active copy */
636   stop_copy();
637
638   /* Delete all places for this object */
639   pgsql_exec(Connection, PGRES_COMMAND_OK, "DELETE FROM place WHERE osm_type = '%c' AND osm_id = %d", osm_type, osm_id);
640
641   return;
642}
643
644static int gazetteer_out_start(const struct output_options *options)
645{
646   /* Save option handle */
647   Options = options;
648
649   /* Connection to the database */
650   Connection = PQconnectdb(options->conninfo);
651
652   /* Check to see that the backend connection was successfully made */
653   if (PQstatus(Connection) != CONNECTION_OK)
654   {
655      fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(Connection));
656      exit_nicely();
657   }
658
659   /* Start a transaction */
660   pgsql_exec(Connection, PGRES_COMMAND_OK, "BEGIN");
661
662   /* (Re)create the table unless we are appending */
663   if (!Options->append)
664   {
665      /* Drop any existing table */
666      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS place");
667      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TYPE if exists keyvalue cascade");
668      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TYPE if exists wordscore cascade");
669      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TYPE if exists stringlanguagetype cascade");
670      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP TYPE if exists keyvaluetype cascade");
671      pgsql_exec(Connection, PGRES_COMMAND_OK, "DROP FUNCTION IF EXISTS get_connected_ways(integer[])");
672
673      /* Create types and functions */
674      if (!Options->enable_hstore)
675      {
676         pgsql_exec(Connection, PGRES_COMMAND_OK, CREATE_KEYVALUETYPE_TYPE, Options->tblsmain_data);
677      }
678      pgsql_exec(Connection, PGRES_COMMAND_OK, CREATE_WORDSCORE_TYPE, Options->tblsmain_data);
679
680      /* Create the new table */
681      if (Options->enable_hstore)
682         pgsql_exec(Connection, PGRES_COMMAND_OK, V2_CREATE_PLACE_TABLE, Options->tblsmain_data);
683      else
684         pgsql_exec(Connection, PGRES_COMMAND_OK, CREATE_PLACE_TABLE, Options->tblsmain_data);
685      pgsql_exec(Connection, PGRES_COMMAND_OK, CREATE_PLACE_ID_INDEX, Options->tblsmain_index);
686      pgsql_exec(Connection, PGRES_TUPLES_OK, "SELECT AddGeometryColumn('place', 'geometry', %d, 'GEOMETRY', 2)", SRID);
687      pgsql_exec(Connection, PGRES_COMMAND_OK, "ALTER TABLE place ALTER COLUMN geometry SET NOT NULL");
688   }
689
690   /* Setup middle layer */
691   options->mid->start(options);
692
693   return 0;
694}
695
696static void gazetteer_out_stop(void)
697{
698   /* Process any remaining ways and relations */
699//   Options->mid->iterate_ways( gazetteer_out_way );
700//   Options->mid->iterate_relations( gazetteer_process_relation );
701
702   /* No longer need to access middle layer */
703   Options->mid->stop();
704
705   /* Stop any active copy */
706   stop_copy();
707
708   /* Commit transaction */
709   pgsql_exec(Connection, PGRES_COMMAND_OK, "COMMIT");
710
711   /* Analyse the table */
712   //pgsql_exec(Connection, PGRES_COMMAND_OK, "ANALYZE place");
713
714   /* Interpolate any addresses - has to be done after all nodes commited  */
715   if (Options->append)
716   {
717      pgsql_exec(Connection, PGRES_TUPLES_OK, "select count(*) from (select update_place(place_id::integer) from placex where indexed=false and class='place' and type='houses') as x");
718   }
719
720   return;
721}
722
723static void gazetteer_out_cleanup(void)
724{
725   return;
726}
727
728static int gazetteer_add_node(int id, double lat, double lon, struct keyval *tags)
729{
730   struct keyval names;
731   struct keyval places;
732   struct keyval *place;
733   int adminlevel;
734   char * housenumber;
735   char * street;
736   char * isin;
737   char * postcode;
738   char * countrycode;
739   char wkt[128];
740
741//fprintf(stderr, "node\n");
742
743   /* Split the tags */
744   split_tags(tags, TAGINFO_NODE, &names, &places, &adminlevel, &housenumber, &street, &isin, &postcode, &countrycode);
745
746   /* Feed this node to the middle layer */
747   Options->mid->nodes_set(id, lat, lon, tags);
748
749   /* Are we interested in this item? */
750   if (listHasData(&names) || listHasData(&places))
751   {
752      sprintf(wkt, "POINT(%.15g %.15g)", lon, lat);
753      for (place = firstItem(&places); place; place = nextItem(&places, place))
754      {
755         add_place('N', id, place->key, place->value, &names, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
756      }
757   }
758
759   if (isin) free(isin);
760
761   /* Free tag lists */
762   resetList(&names);
763   resetList(&places);
764
765   return 0;
766}
767
768static int gazetteer_add_way(int id, int *ndv, int ndc, struct keyval *tags)
769{
770   struct keyval names;
771   struct keyval places;
772   struct keyval *place;
773   int adminlevel;
774   char * housenumber;
775   char * street;
776   char * isin;
777   char * postcode;
778   char * countrycode;
779   int area;
780
781//fprintf(stderr, "way\n");
782
783   /* Split the tags */
784   area = split_tags(tags, TAGINFO_WAY, &names, &places, &adminlevel, &housenumber, &street, &isin, &postcode, &countrycode);
785
786   /* Feed this way to the middle layer */
787   Options->mid->ways_set(id, ndv, ndc, tags, 0);
788
789   /* Are we interested in this item? */
790   if (listHasData(&names) || listHasData(&places))
791   {
792      struct osmNode *nodev;
793      int nodec;
794      char *wkt;
795   
796      /* Fetch the node details */
797      nodev = malloc(ndc * sizeof(struct osmNode));
798      nodec = Options->mid->nodes_get_list(nodev, ndv, ndc);
799
800      /* Get the geometry of the object */
801      if ((wkt = get_wkt_simple(nodev, nodec, area)) != NULL && strlen(wkt) > 0)
802      {
803         for (place = firstItem(&places); place; place = nextItem(&places, place))
804         {
805            add_place('W', id, place->key, place->value, &names, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
806         }
807      }
808
809      /* Free the geometry */
810      free(wkt);
811
812      /* Free the nodes */
813      free(nodev);
814   }
815
816   if (isin) free(isin);
817
818   /* Free tag lists */
819   resetList(&names);
820   resetList(&places);
821
822   return 0;
823}
824
825static int gazetteer_add_relation(int id, struct member *members, int member_count, struct keyval *tags)
826{
827   struct keyval names;
828   struct keyval places;
829   int adminlevel;
830   char * housenumber;
831   char * street;
832   char * isin;
833   char * postcode;
834   char * countrycode;
835   int area, wkt_size;
836   const char *type;
837   const char *boundary;
838
839   type = getItem(tags, "type");
840   if (!type)
841      return 0;
842
843   if (!strcmp(type, "associatedStreet") || !strcmp(type, "relatedStreet"))
844   {
845      Options->mid->relations_set(id, members, member_count, tags);
846      return 0;
847   }
848
849   if (strcmp(type, "boundary") && strcmp(type, "multipolygon"))
850      return 0;
851
852   boundary = getItem(tags, "boundary");
853   if (!boundary) boundary = "administrative";
854
855   /* Split the tags */
856   area = split_tags(tags, TAGINFO_AREA, &names, &places, &adminlevel, &housenumber, &street, &isin, &postcode, &countrycode);
857
858   if (listHasData(&names))
859   {
860      /* get the boundary path (ways) */
861      int i, count;
862      int *xcount = malloc( (member_count+1) * sizeof(int) );
863      struct keyval *xtags  = malloc( (member_count+1) * sizeof(struct keyval) );
864      struct osmNode **xnodes = malloc( (member_count+1) * sizeof(struct osmNode*) );
865
866      count = 0;
867      for (i=0; i<member_count; i++)
868      {
869         /* only interested in ways */
870         if (members[i].type != OSMTYPE_WAY)
871            continue;
872
873         initList(&(xtags[count]));
874         if (Options->mid->ways_get( members[i].id, &(xtags[count]), &(xnodes[count]), &(xcount[count])))
875            continue;
876         count++;
877      }
878      xnodes[count] = NULL;
879      xcount[count] = 0;
880
881      wkt_size = build_geometry(id, xnodes, xcount, 1, 1, 1000000);
882      for (i=0;i<wkt_size;i++)
883      {
884         char *wkt = get_wkt(i);
885         if (strlen(wkt) && (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))))
886         {
887            if (Options->enable_hstore)
888                add_place('R', id, "boundary", boundary, &names, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
889            else
890                add_place('R', id, "boundary", "adminitrative", &names, adminlevel, housenumber, street, isin, postcode, countrycode, wkt);
891         }
892         free(wkt);
893      }
894      clear_wkts();
895
896      for( i=0; i<count; i++ )
897      {
898         resetList( &(xtags[i]) );
899         free( xnodes[i] );
900      }
901
902      free(xcount);
903      free(xtags);
904      free(xnodes);
905   }
906
907   if (isin) free(isin);
908
909   /* Free tag lists */
910   resetList(&names);
911   resetList(&places);
912
913   return 0;
914}
915
916static int gazetteer_delete_node(int id)
917{
918   /* Make sure we are in slim mode */
919   require_slim_mode();
920
921   /* Delete all references to this node */
922   delete_place('N', id);
923
924   /* Feed this delete to the middle layer */
925   Options->mid->nodes_delete(id);
926
927   return 0;
928}
929
930static int gazetteer_delete_way(int id)
931{
932   /* Make sure we are in slim mode */
933   require_slim_mode();
934
935   /* Delete all references to this way */
936   delete_place('W', id);
937
938   /* Feed this delete to the middle layer */
939   Options->mid->ways_delete(id);
940
941   return 0;
942}
943
944static int gazetteer_delete_relation(int id)
945{
946   /* Make sure we are in slim mode */
947   require_slim_mode();
948
949   /* Delete all references to this relation */
950   delete_place('R', id);
951
952   /* Feed this delete to the middle layer */
953   Options->mid->relations_delete(id);
954
955   return 0;
956}
957
958static int gazetteer_modify_node(int id, double lat, double lon, struct keyval *tags)
959{
960   require_slim_mode();
961   Options->mid->nodes_delete(id);
962   return gazetteer_add_node(id, lat, lon, tags);
963}
964
965static int gazetteer_modify_way(int id, int *ndv, int ndc, struct keyval *tags)
966{
967   require_slim_mode();
968   Options->mid->ways_delete(id);
969   return gazetteer_add_way(id, ndv, ndc, tags);
970}
971
972static int gazetteer_modify_relation(int id, struct member *members, int member_count, struct keyval *tags)
973{
974   require_slim_mode();
975   Options->mid->relations_delete(id);
976   return gazetteer_add_relation(id, members, member_count, tags);
977}
978
979struct output_t out_gazetteer = {
980   .start = gazetteer_out_start,
981   .stop = gazetteer_out_stop,
982   .cleanup = gazetteer_out_cleanup,
983
984   .node_add = gazetteer_add_node,
985   .way_add = gazetteer_add_way,
986   .relation_add = gazetteer_add_relation,
987
988   .node_modify = gazetteer_modify_node,
989   .way_modify = gazetteer_modify_way,
990   .relation_modify = gazetteer_modify_relation,
991
992   .node_delete = gazetteer_delete_node,
993   .way_delete = gazetteer_delete_way,
994   .relation_delete = gazetteer_delete_relation
995};
Note: See TracBrowser for help on using the repository browser.