8 #include <libxml/xmlstring.h>
9 #include <libxml/xmlreader.h>
10 #include <libxml/hash.h>
12 #include "nominatim.h"
16 typedef enum { FILETYPE_NONE, FILETYPE_STRUCTUREDV0P1 } filetypes_t;
17 typedef enum { FILEMODE_NONE, FILEMODE_ADD, FILEMODE_UPDATE, FILEMODE_DELETE } filemodes_t;
19 #define MAX_FEATUREADDRESS 500
20 #define MAX_FEATURENAMES 1000
22 struct feature_address {
43 xmlChar * rankAddress;
45 xmlChar * countryCode;
47 xmlChar * houseNumber;
51 int fileType = FILETYPE_NONE;
52 int fileMode = FILEMODE_ADD;
54 struct feature_address featureAddress[MAX_FEATUREADDRESS];
55 struct feature_name featureName[MAX_FEATURENAMES];
56 struct feature feature;
57 int featureAddressLines = 0;
58 int featureNameLines = 0;
60 xmlHashTablePtr partionTableTagsHash;
64 void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
70 if (fileType == FILETYPE_NONE)
72 // Potential to handle other file types in the future / versions
73 if (xmlStrEqual(name, BAD_CAST "osmStructured"))
75 value = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "version");
76 version = strtof(value, NULL);
79 if (version == (float)0.1)
81 fileType = FILETYPE_STRUCTUREDV0P1;
82 fileMode = FILEMODE_ADD;
86 fprintf( stderr, "Unknown osmStructured version %f\n", version );
92 fprintf( stderr, "Unknown XML document type: %s\n", name );
98 if (xmlStrEqual(name, BAD_CAST "add"))
100 fileMode = FILEMODE_ADD;
103 if (xmlStrEqual(name, BAD_CAST "update"))
105 fileMode = FILEMODE_UPDATE;
108 if (xmlStrEqual(name, BAD_CAST "delete"))
110 fileMode = FILEMODE_DELETE;
113 if (fileMode == FILEMODE_NONE)
115 fprintf( stderr, "Unknown import mode in: %s\n", name );
119 if (xmlStrEqual(name, BAD_CAST "feature"))
121 feature.type = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
122 feature.id = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
123 feature.key = xmlTextReaderGetAttribute(reader, BAD_CAST "key");
124 feature.value = xmlTextReaderGetAttribute(reader, BAD_CAST "value");
125 feature.rankAddress = xmlTextReaderGetAttribute(reader, BAD_CAST "rank");
126 feature.rankSearch = xmlTextReaderGetAttribute(reader, BAD_CAST "importance");
128 feature.countryCode = NULL;
129 feature.adminLevel = NULL;
130 feature.houseNumber = NULL;
131 feature.geometry = NULL;
132 featureAddressLines = 0;
133 featureNameLines = 0;
137 if (xmlStrEqual(name, BAD_CAST "names")) return;
138 if (xmlStrEqual(name, BAD_CAST "name"))
140 featureName[featureNameLines].type = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
141 featureName[featureNameLines].value = xmlTextReaderReadString(reader);
143 if (featureNameLines >= MAX_FEATURENAMES)
145 fprintf( stderr, "Too many name elements\n");
150 if (xmlStrEqual(name, BAD_CAST "osmGeometry"))
152 feature.geometry = xmlTextReaderReadString(reader);
155 if (xmlStrEqual(name, BAD_CAST "adminLevel"))
157 feature.adminLevel = xmlTextReaderReadString(reader);
160 if (xmlStrEqual(name, BAD_CAST "countryCode"))
162 feature.countryCode = xmlTextReaderReadString(reader);
165 if (xmlStrEqual(name, BAD_CAST "houseNumber"))
167 feature.houseNumber = xmlTextReaderReadString(reader);
170 if (xmlStrEqual(name, BAD_CAST "address"))
172 featureAddressLines = 0;
176 if (xmlStrEqual(name, BAD_CAST "continent"))
180 else if (xmlStrEqual(name, BAD_CAST "sea"))
184 else if (xmlStrEqual(name, BAD_CAST "country"))
188 else if (xmlStrEqual(name, BAD_CAST "state"))
192 else if (xmlStrEqual(name, BAD_CAST "county"))
196 else if (xmlStrEqual(name, BAD_CAST "city"))
200 else if (xmlStrEqual(name, BAD_CAST "town"))
204 else if (xmlStrEqual(name, BAD_CAST "village"))
208 else if (xmlStrEqual(name, BAD_CAST "unknown"))
212 else if (xmlStrEqual(name, BAD_CAST "suburb"))
216 else if (xmlStrEqual(name, BAD_CAST "postcode"))
220 else if (xmlStrEqual(name, BAD_CAST "neighborhood"))
224 else if (xmlStrEqual(name, BAD_CAST "street"))
228 else if (xmlStrEqual(name, BAD_CAST "access"))
232 else if (xmlStrEqual(name, BAD_CAST "building"))
236 else if (xmlStrEqual(name, BAD_CAST "other"))
242 value = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "rank");
245 fprintf( stderr, "Address element missing rank\n");
248 featureAddress[featureAddressLines].rankAddress = atoi(value);
251 featureAddress[featureAddressLines].type = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
252 featureAddress[featureAddressLines].id = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
253 featureAddress[featureAddressLines].key = xmlTextReaderGetAttribute(reader, BAD_CAST "key");
254 featureAddress[featureAddressLines].value = xmlTextReaderGetAttribute(reader, BAD_CAST "value");
255 featureAddress[featureAddressLines].distance = xmlTextReaderGetAttribute(reader, BAD_CAST "distance");
257 featureAddressLines++;
258 if (featureAddressLines >= MAX_FEATUREADDRESS)
260 fprintf( stderr, "Too many address elements\n");
266 fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
269 void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
272 PGresult * resPlaceID;
273 const char * paramValues[11];
275 char * partionQueryName;
278 if (xmlStrEqual(name, BAD_CAST "feature"))
281 if (featureCount % 1000 == 0) printf("feature %i(k)\n", featureCount/1000);
283 if (fileMode == FILEMODE_ADD)
285 resPlaceID = PQexecPrepared(conn, "get_new_place_id", 0, NULL, NULL, NULL, 0);
286 if (PQresultStatus(resPlaceID) != PGRES_TUPLES_OK)
288 fprintf(stderr, "get_place_id: INSERT failed: %s", PQerrorMessage(conn));
295 paramValues[0] = (const char *)feature.type;
296 paramValues[1] = (const char *)feature.id;
297 paramValues[2] = (const char *)feature.key;
298 paramValues[3] = (const char *)feature.value;
299 resPlaceID = PQexecPrepared(conn, "get_new_place_id", 4, paramValues, NULL, NULL, 0);
300 if (PQresultStatus(resPlaceID) != PGRES_TUPLES_OK)
302 fprintf(stderr, "index_placex: INSERT failed: %s", PQerrorMessage(conn));
307 place_id = PQgetvalue(resPlaceID, 0, 0);
309 if (fileMode == FILEMODE_UPDATE || fileMode == FILEMODE_DELETE)
311 paramValues[0] = (const char *)place_id;
312 res = PQexecPrepared(conn, "placex_delete", 1, paramValues, NULL, NULL, 0);
313 if (PQresultStatus(res) != PGRES_COMMAND_OK)
315 fprintf(stderr, "placex_delete: DELETE failed: %s", PQerrorMessage(conn));
321 res = PQexecPrepared(conn, "search_name_delete", 1, paramValues, NULL, NULL, 0);
322 if (PQresultStatus(res) != PGRES_COMMAND_OK)
324 fprintf(stderr, "search_name_delete: DELETE failed: %s", PQerrorMessage(conn));
330 res = PQexecPrepared(conn, "place_addressline_delete", 1, paramValues, NULL, NULL, 0);
331 if (PQresultStatus(res) != PGRES_COMMAND_OK)
333 fprintf(stderr, "place_addressline_delete: DELETE failed: %s", PQerrorMessage(conn));
340 if (fileMode == FILEMODE_UPDATE || fileMode == FILEMODE_ADD)
342 // Insert into placex
343 paramValues[0] = (const char *)place_id;
344 paramValues[1] = (const char *)feature.type;
345 paramValues[2] = (const char *)feature.id;
346 paramValues[3] = (const char *)feature.key;
347 paramValues[4] = (const char *)feature.value;
348 // paramValues[5] = (const char *)feature.name;
349 paramValues[6] = (const char *)feature.adminLevel;
350 paramValues[7] = (const char *)feature.houseNumber;
351 paramValues[8] = (const char *)feature.rankAddress;
352 paramValues[9] = (const char *)feature.rankSearch;
353 paramValues[10] = (const char *)feature.geometry;
354 res = PQexecPrepared(conn, "placex_insert", 11, paramValues, NULL, NULL, 0);
355 if (PQresultStatus(res) != PGRES_COMMAND_OK)
357 fprintf(stderr, "index_placex: INSERT failed: %s", PQerrorMessage(conn));
363 for(i = 0; i < featureAddressLines; i++)
365 // insert into place_address
366 paramValues[0] = (const char *)place_id;
367 paramValues[1] = (const char *)featureAddress[i].distance;
368 paramValues[2] = (const char *)featureAddress[i].type;
369 paramValues[3] = (const char *)featureAddress[i].id;
370 paramValues[4] = (const char *)featureAddress[i].key;
371 paramValues[5] = (const char *)featureAddress[i].value;
372 res = PQexecPrepared(conn, "place_addressline_insert", 6, paramValues, NULL, NULL, 0);
373 if (PQresultStatus(res) != PGRES_COMMAND_OK)
375 fprintf(stderr, "place_addressline_insert: INSERT failed: %s", PQerrorMessage(conn));
381 xmlFree(featureAddress[i].type);
382 xmlFree(featureAddress[i].id);
383 xmlFree(featureAddress[i].key);
384 xmlFree(featureAddress[i].value);
385 xmlFree(featureAddress[i].distance);
388 if (featureNameLines)
390 paramValues[0] = (const char *)place_id;
391 res = PQexecPrepared(conn, "search_name_insert", 1, paramValues, NULL, NULL, 0);
392 if (PQresultStatus(res) != PGRES_COMMAND_OK)
394 fprintf(stderr, "search_name_insert: INSERT failed: %s", PQerrorMessage(conn));
401 partionQueryName = xmlHashLookup2(partionTableTagsHash, feature.key, feature.value);
402 if (partionQueryName)
404 // insert into partition table
405 paramValues[0] = (const char *)place_id;
406 paramValues[1] = (const char *)feature.geometry;
407 res = PQexecPrepared(conn, partionQueryName, 2, paramValues, NULL, NULL, 0);
408 if (PQresultStatus(res) != PGRES_COMMAND_OK)
410 fprintf(stderr, "%s: INSERT failed: %s", partionQueryName, PQerrorMessage(conn));
421 for(i = 0; i < featureAddressLines; i++)
423 xmlFree(featureAddress[i].type);
424 xmlFree(featureAddress[i].id);
425 xmlFree(featureAddress[i].key);
426 xmlFree(featureAddress[i].value);
427 xmlFree(featureAddress[i].distance);
431 xmlFree(feature.type);
433 xmlFree(feature.key);
434 xmlFree(feature.value);
435 xmlFree(feature.rankAddress);
436 xmlFree(feature.rankSearch);
437 // if (feature.name) xmlFree(feature.name);
438 if (feature.countryCode) xmlFree(feature.countryCode);
439 if (feature.adminLevel) xmlFree(feature.adminLevel);
440 if (feature.houseNumber) xmlFree(feature.houseNumber);
441 if (feature.geometry) xmlFree(feature.geometry);
447 static void processNode(xmlTextReaderPtr reader)
450 name = xmlTextReaderName(reader);
453 name = xmlStrdup(BAD_CAST "--");
456 switch(xmlTextReaderNodeType(reader))
458 case XML_READER_TYPE_ELEMENT:
459 StartElement(reader, name);
460 if (xmlTextReaderIsEmptyElement(reader))
461 EndElement(reader, name); /* No end_element for self closing tags! */
463 case XML_READER_TYPE_END_ELEMENT:
464 EndElement(reader, name);
466 case XML_READER_TYPE_TEXT:
467 case XML_READER_TYPE_CDATA:
468 case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
472 fprintf(stderr, "Unknown node type %d\n", xmlTextReaderNodeType(reader));
479 int nominatim_import(const char *conninfo, const char *partionTagsFilename, const char *filename)
481 xmlTextReaderPtr reader;
484 FILE * partionTagsFile;
485 char * partionQueryName;
486 char partionQuerySQL[1024];
488 conn = PQconnectdb(conninfo);
489 if (PQstatus(conn) != CONNECTION_OK)
491 fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
495 partionTableTagsHash = xmlHashCreate(200);
497 partionTagsFile = fopen(partionTagsFilename, "rt");
498 if (!partionTagsFile)
500 fprintf(stderr, "Unable to read partition tags file: %s\n", partionTagsFilename);
504 char buffer[1024], osmkey[256], osmvalue[256];
506 while(fgets(buffer, sizeof(buffer), partionTagsFile) != NULL)
508 fields = sscanf( buffer, "%23s %63s", osmkey, osmvalue );
510 if( fields <= 0 ) continue;
514 fprintf( stderr, "Error partition file\n");
517 partionQueryName = malloc(strlen("partition_insert_")+strlen(osmkey)+strlen(osmvalue)+2);
518 strcpy(partionQueryName, "partition_insert_");
519 strcat(partionQueryName, osmkey);
520 strcat(partionQueryName, "_");
521 strcat(partionQueryName, osmvalue);
523 strcpy(partionQuerySQL, "insert into place_classtype_");
524 strcat(partionQuerySQL, osmkey);
525 strcat(partionQuerySQL, "_");
526 strcat(partionQuerySQL, osmvalue);
527 strcat(partionQuerySQL, " (place_id, centroid) values ($1, ST_Centroid(st_setsrid($2, 4326)))");
529 res = PQprepare(conn, partionQueryName, partionQuerySQL, 2, NULL);
530 if (PQresultStatus(res) != PGRES_COMMAND_OK)
532 fprintf(stderr, "Failed to prepare %s: %s\n", partionQueryName, PQerrorMessage(conn));
536 xmlHashAddEntry2(partionTableTagsHash, BAD_CAST osmkey, BAD_CAST osmvalue, BAD_CAST partionQueryName);
539 res = PQprepare(conn, "get_new_place_id",
540 "select nextval('seq_place')",
542 if (PQresultStatus(res) != PGRES_COMMAND_OK)
544 fprintf(stderr, "Failed to prepare get_new_place_id: %s\n", PQerrorMessage(conn));
548 res = PQprepare(conn, "get_place_id",
549 "select place_id from placex where osm_type = $1 and osm_id = $2 and class = $3 and type = $4",
551 if (PQresultStatus(res) != PGRES_COMMAND_OK)
553 fprintf(stderr, "Failed to prepare get_place_id: %s\n", PQerrorMessage(conn));
557 res = PQprepare(conn, "placex_insert",
558 "insert into placex (place_id,osm_type,osm_id,class,type,name,admin_level,housenumber,rank_address,rank_search,geometry) "
559 "values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, st_setsrid($11, 4326))",
561 if (PQresultStatus(res) != PGRES_COMMAND_OK)
563 fprintf(stderr, "Failed to prepare placex_insert: %s\n", PQerrorMessage(conn));
567 res = PQprepare(conn, "search_name_insert",
568 "insert into search_name (place_id, search_rank, address_rank, country_code, name_vector, nameaddress_vector, centroid) "
569 "select place_id, rank_address, rank_search, country_code, make_keywords(name), "
570 "(select uniq(sort(array_agg(name_vector))) from place_addressline join search_name on "
571 "(address_place_id = search_name.place_id) where place_addressline.place_id = $1 ), st_centroid(geometry) from placex "
572 "where place_id = $1",
574 if (PQresultStatus(res) != PGRES_COMMAND_OK)
576 fprintf(stderr, "Failed to prepare placex_insert: %s\n", PQerrorMessage(conn));
580 res = PQprepare(conn, "place_addressline_insert",
581 "insert into place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address) "
582 "select $1, place_id, false, true, $2, rank_address from placex where osm_type = $3 and osm_id = $4 and class = $5 and type = $6",
584 if (PQresultStatus(res) != PGRES_COMMAND_OK)
586 fprintf(stderr, "Failed to prepare place_addressline_insert: %s\n", PQerrorMessage(conn));
590 res = PQprepare(conn, "placex_delete",
591 "delete from placex where place_id = $1",
593 if (PQresultStatus(res) != PGRES_COMMAND_OK)
595 fprintf(stderr, "Failed to prepare placex_delete: %s\n", PQerrorMessage(conn));
599 res = PQprepare(conn, "search_name_delete",
600 "delete from search_name where place_id = $1",
602 if (PQresultStatus(res) != PGRES_COMMAND_OK)
604 fprintf(stderr, "Failed to prepare search_name_delete: %s\n", PQerrorMessage(conn));
608 res = PQprepare(conn, "place_addressline_delete",
609 "delete from place_addressline where place_id = $1",
611 if (PQresultStatus(res) != PGRES_COMMAND_OK)
613 fprintf(stderr, "Failed to prepare place_addressline_delete: %s\n", PQerrorMessage(conn));
619 reader = inputUTF8(filename);
623 fprintf(stderr, "Unable to open %s\n", filename);
627 ret = xmlTextReaderRead(reader);
631 ret = xmlTextReaderRead(reader);
634 fprintf(stderr, "%s : failed to parse\n", filename);
638 xmlFreeTextReader(reader);
639 xmlHashFree(partionTableTagsHash, NULL);