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

Revision 20418, 24.1 KB checked in by twain, 4 years ago (diff)

only do address intrapolation in append mode

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