mirror of
https://github.com/osm-search/Nominatim.git
synced 2026-02-26 11:08:13 +00:00
replace nominatim C program
This commit is contained in:
@@ -53,6 +53,8 @@ if (NOT ONLY_DOCS)
|
|||||||
include_directories(${PostgreSQL_INCLUDE_DIRS})
|
include_directories(${PostgreSQL_INCLUDE_DIRS})
|
||||||
link_directories(${PostgreSQL_LIBRARY_DIRS})
|
link_directories(${PostgreSQL_LIBRARY_DIRS})
|
||||||
|
|
||||||
|
find_package(PythonInterp 3)
|
||||||
|
|
||||||
find_program(PYOSMIUM pyosmium-get-changes)
|
find_program(PYOSMIUM pyosmium-get-changes)
|
||||||
if (NOT EXISTS "${PYOSMIUM}")
|
if (NOT EXISTS "${PYOSMIUM}")
|
||||||
set(PYOSMIUM_PATH "")
|
set(PYOSMIUM_PATH "")
|
||||||
@@ -168,7 +170,6 @@ endif()
|
|||||||
|
|
||||||
if (NOT ONLY_DOCS)
|
if (NOT ONLY_DOCS)
|
||||||
add_subdirectory(module)
|
add_subdirectory(module)
|
||||||
add_subdirectory(nominatim)
|
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(docs)
|
add_subdirectory(docs)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class SetupFunctions
|
|||||||
protected $iInstances;
|
protected $iInstances;
|
||||||
protected $sModulePath;
|
protected $sModulePath;
|
||||||
protected $aDSNInfo;
|
protected $aDSNInfo;
|
||||||
|
protected $bQuiet;
|
||||||
protected $bVerbose;
|
protected $bVerbose;
|
||||||
protected $sIgnoreErrors;
|
protected $sIgnoreErrors;
|
||||||
protected $bEnableDiffUpdates;
|
protected $bEnableDiffUpdates;
|
||||||
@@ -49,6 +50,7 @@ class SetupFunctions
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setting member variables based on command line options stored in $aCMDResult
|
// setting member variables based on command line options stored in $aCMDResult
|
||||||
|
$this->bQuiet = $aCMDResult['quiet'];
|
||||||
$this->bVerbose = $aCMDResult['verbose'];
|
$this->bVerbose = $aCMDResult['verbose'];
|
||||||
|
|
||||||
//setting default values which are not set by the update.php array
|
//setting default values which are not set by the update.php array
|
||||||
@@ -518,10 +520,16 @@ class SetupFunctions
|
|||||||
public function index($bIndexNoanalyse)
|
public function index($bIndexNoanalyse)
|
||||||
{
|
{
|
||||||
$sOutputFile = '';
|
$sOutputFile = '';
|
||||||
$sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i'
|
$sBaseCmd = CONST_BasePath.'/nominatim/nominatim.py'
|
||||||
.' -d '.escapeshellarg($this->aDSNInfo['database'])
|
.' -d '.escapeshellarg($this->aDSNInfo['database'])
|
||||||
.' -P '.escapeshellarg($this->aDSNInfo['port'])
|
.' -P '.escapeshellarg($this->aDSNInfo['port'])
|
||||||
.' -t '.escapeshellarg($this->iInstances.$sOutputFile);
|
.' -t '.escapeshellarg($this->iInstances.$sOutputFile);
|
||||||
|
if (!$this->bQuiet) {
|
||||||
|
$sBaseCmd .= ' -v';
|
||||||
|
}
|
||||||
|
if ($this->bVerbose) {
|
||||||
|
$sBaseCmd .= ' -v';
|
||||||
|
}
|
||||||
if (isset($this->aDSNInfo['hostspec'])) {
|
if (isset($this->aDSNInfo['hostspec'])) {
|
||||||
$sBaseCmd .= ' -H '.escapeshellarg($this->aDSNInfo['hostspec']);
|
$sBaseCmd .= ' -H '.escapeshellarg($this->aDSNInfo['hostspec']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
add_executable(nominatim export.c geometry.cpp import.c index.c input.c nominatim.c postgresql.c sprompt.c)
|
|
||||||
|
|
||||||
CHECK_SYMBOL_EXISTS(bswap_32 "byteswap.h" HAVE_BYTESWAP)
|
|
||||||
CHECK_SYMBOL_EXISTS(bswap32 "sys/endian.h" HAVE_SYS_ENDIAN)
|
|
||||||
|
|
||||||
target_compile_definitions(nominatim
|
|
||||||
PRIVATE HAVE_BYTESWAP=$<BOOL:${HAVE_BYTESWAP}>
|
|
||||||
PRIVATE HAVE_SYS_ENDIAN=$<BOOL:${HAVE_SYS_ENDIAN}>
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(nominatim ${LIBXML2_LIBRARIES} ${ZLIB_LIBRARIES} ${BZIP2_LIBRARIES} ${PostgreSQL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
|
|
||||||
@@ -1,558 +0,0 @@
|
|||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <libpq-fe.h>
|
|
||||||
|
|
||||||
#include "nominatim.h"
|
|
||||||
#include "export.h"
|
|
||||||
#include "postgresql.h"
|
|
||||||
|
|
||||||
extern int verbose;
|
|
||||||
|
|
||||||
int mode = 0;
|
|
||||||
|
|
||||||
void nominatim_export(int rank_min, int rank_max, const char *conninfo, const char *structuredoutputfile)
|
|
||||||
{
|
|
||||||
xmlTextWriterPtr writer;
|
|
||||||
|
|
||||||
int rankTotalDone;
|
|
||||||
|
|
||||||
PGconn *conn;
|
|
||||||
PGresult * res;
|
|
||||||
PGresult * resSectors;
|
|
||||||
PGresult * resPlaces;
|
|
||||||
|
|
||||||
int rank;
|
|
||||||
int i;
|
|
||||||
int iSector;
|
|
||||||
int tuples;
|
|
||||||
|
|
||||||
const char *paramValues[2];
|
|
||||||
int paramLengths[2];
|
|
||||||
int paramFormats[2];
|
|
||||||
uint32_t paramRank;
|
|
||||||
uint32_t paramSector;
|
|
||||||
uint32_t sector;
|
|
||||||
|
|
||||||
Oid pg_prepare_params[2];
|
|
||||||
|
|
||||||
conn = PQconnectdb(conninfo);
|
|
||||||
if (PQstatus(conn) != CONNECTION_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_sectors",
|
|
||||||
"select geometry_sector,count(*) from placex where rank_search = $1 and indexed_status = 0 group by geometry_sector order by geometry_sector",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) exit(EXIT_FAILURE);
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
pg_prepare_params[1] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_sector_places",
|
|
||||||
"select place_id from placex where rank_search = $1 and geometry_sector = $2",
|
|
||||||
2, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) exit(EXIT_FAILURE);
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
nominatim_exportCreatePreparedQueries(conn);
|
|
||||||
|
|
||||||
// Create the output file
|
|
||||||
writer = nominatim_exportXMLStart(structuredoutputfile);
|
|
||||||
|
|
||||||
for (rank = rank_min; rank <= rank_max; rank++)
|
|
||||||
{
|
|
||||||
printf("Starting rank %d\n", rank);
|
|
||||||
|
|
||||||
paramRank = PGint32(rank);
|
|
||||||
paramValues[0] = (char *)¶mRank;
|
|
||||||
paramLengths[0] = sizeof(paramRank);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
resSectors = PQexecPrepared(conn, "index_sectors", 1, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
if (PQresultStatus(resSectors) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_sectors: SELECT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(resSectors);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (PQftype(resSectors, 0) != PG_OID_INT4)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Sector value has unexpected type\n");
|
|
||||||
PQclear(resSectors);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (PQftype(resSectors, 1) != PG_OID_INT8)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Sector value has unexpected type\n");
|
|
||||||
PQclear(resSectors);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
rankTotalDone = 0;
|
|
||||||
for (iSector = 0; iSector < PQntuples(resSectors); iSector++)
|
|
||||||
{
|
|
||||||
sector = PGint32(*((uint32_t *)PQgetvalue(resSectors, iSector, 0)));
|
|
||||||
|
|
||||||
// Get all the place_id's for this sector
|
|
||||||
paramRank = PGint32(rank);
|
|
||||||
paramValues[0] = (char *)¶mRank;
|
|
||||||
paramLengths[0] = sizeof(paramRank);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
paramSector = PGint32(sector);
|
|
||||||
paramValues[1] = (char *)¶mSector;
|
|
||||||
paramLengths[1] = sizeof(paramSector);
|
|
||||||
paramFormats[1] = 1;
|
|
||||||
resPlaces = PQexecPrepared(conn, "index_sector_places", 2, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
if (PQresultStatus(resPlaces) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_sector_places: SELECT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(resPlaces);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (PQftype(resPlaces, 0) != PG_OID_INT8)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Place_id value has unexpected type\n");
|
|
||||||
PQclear(resPlaces);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
tuples = PQntuples(resPlaces);
|
|
||||||
for (i = 0; i < tuples; i++)
|
|
||||||
{
|
|
||||||
nominatim_exportPlace(PGint64(*((uint64_t *)PQgetvalue(resPlaces, i, 0))), conn, writer, NULL, NULL);
|
|
||||||
rankTotalDone++;
|
|
||||||
if (rankTotalDone%1000 == 0) printf("Done %i (k)\n", rankTotalDone/1000);
|
|
||||||
}
|
|
||||||
PQclear(resPlaces);
|
|
||||||
}
|
|
||||||
PQclear(resSectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
nominatim_exportXMLEnd(writer);
|
|
||||||
|
|
||||||
PQfinish(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nominatim_exportCreatePreparedQueries(PGconn * conn)
|
|
||||||
{
|
|
||||||
Oid pg_prepare_params[2];
|
|
||||||
PGresult * res;
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT8;
|
|
||||||
res = PQprepare(conn, "placex_details",
|
|
||||||
"select placex.osm_type, placex.osm_id, placex.class, placex.type, placex.name, placex.housenumber, placex.country_code, ST_AsText(placex.geometry), placex.admin_level, placex.rank_address, placex.rank_search, placex.parent_place_id, parent.osm_type, parent.osm_id, placex.indexed_status, placex.linked_place_id from placex left outer join placex as parent on (placex.parent_place_id = parent.place_id) where placex.place_id = $1",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error preparing placex_details: %s", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT8;
|
|
||||||
res = PQprepare(conn, "placex_address",
|
|
||||||
"select osm_type,osm_id,class,type,distance,cached_rank_address,isaddress from place_addressline join placex on (address_place_id = placex.place_id) where place_addressline.place_id = $1 and address_place_id != place_addressline.place_id order by cached_rank_address asc,osm_type,osm_id",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error preparing placex_address: %s", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT8;
|
|
||||||
res = PQprepare(conn, "placex_names",
|
|
||||||
"select (each(name)).key,(each(name)).value from (select name from placex where place_id = $1) as x order by (each(name)).key",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error preparing placex_names: %s", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT8;
|
|
||||||
res = PQprepare(conn, "placex_extratags",
|
|
||||||
"select (each(extratags)).key,(each(extratags)).value from (select extratags from placex where place_id = $1) as x order by (each(extratags)).key",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error preparing placex_extratags: %s", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlTextWriterPtr nominatim_exportXMLStart(const char *structuredoutputfile)
|
|
||||||
{
|
|
||||||
xmlTextWriterPtr writer;
|
|
||||||
|
|
||||||
writer = xmlNewTextWriterFilename(structuredoutputfile, 0);
|
|
||||||
if (writer==NULL)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Unable to open %s\n", structuredoutputfile);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
xmlTextWriterSetIndent(writer, 1);
|
|
||||||
if (xmlTextWriterStartDocument(writer, NULL, "UTF8", NULL) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterStartDocument failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (xmlTextWriterStartElement(writer, BAD_CAST "osmStructured") < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterStartElement failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST "0.1") < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterWriteAttribute failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (xmlTextWriterWriteAttribute(writer, BAD_CAST "generator", BAD_CAST "Nominatim") < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterWriteAttribute failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
mode = 0;
|
|
||||||
|
|
||||||
return writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nominatim_exportXMLEnd(xmlTextWriterPtr writer)
|
|
||||||
{
|
|
||||||
nominatim_exportEndMode(writer);
|
|
||||||
|
|
||||||
// End <osmStructured>
|
|
||||||
if (xmlTextWriterEndElement(writer) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterEndElement failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (xmlTextWriterEndDocument(writer) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterEndDocument failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
xmlFreeTextWriter(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nominatim_exportStartMode(xmlTextWriterPtr writer, int newMode)
|
|
||||||
{
|
|
||||||
if (mode == newMode) return;
|
|
||||||
|
|
||||||
nominatim_exportEndMode(writer);
|
|
||||||
|
|
||||||
switch(newMode)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if (xmlTextWriterStartElement(writer, BAD_CAST "add") < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterStartElement failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
if (xmlTextWriterStartElement(writer, BAD_CAST "update") < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterStartElement failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
if (xmlTextWriterStartElement(writer, BAD_CAST "delete") < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterStartElement failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mode = newMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nominatim_exportEndMode(xmlTextWriterPtr writer)
|
|
||||||
{
|
|
||||||
if (!mode) return;
|
|
||||||
|
|
||||||
if (xmlTextWriterEndElement(writer) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "xmlTextWriterEndElement failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nominatim_exportPlaceQueries(uint64_t place_id, PGconn * conn, struct export_data * querySet)
|
|
||||||
{
|
|
||||||
const char * paramValues[1];
|
|
||||||
int paramLengths[1];
|
|
||||||
int paramFormats[1];
|
|
||||||
uint64_t paramPlaceID;
|
|
||||||
|
|
||||||
paramPlaceID = PGint64(place_id);
|
|
||||||
paramValues[0] = (char *)¶mPlaceID;
|
|
||||||
paramLengths[0] = sizeof(paramPlaceID);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
|
|
||||||
querySet->res = PQexecPrepared(conn, "placex_details", 1, paramValues, paramLengths, paramFormats, 0);
|
|
||||||
if (PQresultStatus(querySet->res) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "placex_details: SELECT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(querySet->res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
querySet->resNames = PQexecPrepared(conn, "placex_names", 1, paramValues, paramLengths, paramFormats, 0);
|
|
||||||
if (PQresultStatus(querySet->resNames) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "placex_names: SELECT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(querySet->resNames);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
querySet->resAddress = PQexecPrepared(conn, "placex_address", 1, paramValues, paramLengths, paramFormats, 0);
|
|
||||||
if (PQresultStatus(querySet->resAddress) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "placex_address: SELECT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(querySet->resAddress);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
querySet->resExtraTags = PQexecPrepared(conn, "placex_extratags", 1, paramValues, paramLengths, paramFormats, 0);
|
|
||||||
if (PQresultStatus(querySet->resExtraTags) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "placex_extratags: SELECT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(querySet->resExtraTags);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nominatim_exportFreeQueries(struct export_data * querySet)
|
|
||||||
{
|
|
||||||
PQclear(querySet->res);
|
|
||||||
PQclear(querySet->resNames);
|
|
||||||
PQclear(querySet->resAddress);
|
|
||||||
PQclear(querySet->resExtraTags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Requirements: the prepared queries must exist
|
|
||||||
*/
|
|
||||||
void nominatim_exportPlace(uint64_t place_id, PGconn * conn,
|
|
||||||
xmlTextWriterPtr writer, pthread_mutex_t * writer_mutex, struct export_data * prevQuerySet)
|
|
||||||
{
|
|
||||||
struct export_data querySet;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
nominatim_exportPlaceQueries(place_id, conn, &querySet);
|
|
||||||
|
|
||||||
// Add, modify or delete?
|
|
||||||
if (prevQuerySet)
|
|
||||||
{
|
|
||||||
if ((PQgetvalue(prevQuerySet->res, 0, 14) && strcmp(PQgetvalue(prevQuerySet->res, 0, 14), "100") == 0) || PQntuples(querySet.res) == 0)
|
|
||||||
{
|
|
||||||
// Delete
|
|
||||||
if (writer_mutex) pthread_mutex_lock( writer_mutex );
|
|
||||||
nominatim_exportStartMode(writer, 3);
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "feature");
|
|
||||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "place_id", "%li", place_id);
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(prevQuerySet->res, 0, 0));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "id", BAD_CAST PQgetvalue(prevQuerySet->res, 0, 1));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "key", BAD_CAST PQgetvalue(prevQuerySet->res, 0, 2));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "value", BAD_CAST PQgetvalue(prevQuerySet->res, 0, 3));
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
if (writer_mutex) pthread_mutex_unlock( writer_mutex );
|
|
||||||
nominatim_exportFreeQueries(&querySet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (PQgetvalue(prevQuerySet->res, 0, 14) && strcmp(PQgetvalue(prevQuerySet->res, 0, 14), "1") == 0)
|
|
||||||
{
|
|
||||||
// Add
|
|
||||||
if (writer_mutex) pthread_mutex_lock( writer_mutex );
|
|
||||||
nominatim_exportStartMode(writer, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Update, but only if something has changed
|
|
||||||
|
|
||||||
// TODO: detect changes
|
|
||||||
|
|
||||||
if (writer_mutex) pthread_mutex_lock( writer_mutex );
|
|
||||||
nominatim_exportStartMode(writer, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Add
|
|
||||||
if (writer_mutex) pthread_mutex_lock( writer_mutex );
|
|
||||||
nominatim_exportStartMode(writer, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "feature");
|
|
||||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "place_id", "%li", place_id);
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(querySet.res, 0, 0));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "id", BAD_CAST PQgetvalue(querySet.res, 0, 1));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "key", BAD_CAST PQgetvalue(querySet.res, 0, 2));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "value", BAD_CAST PQgetvalue(querySet.res, 0, 3));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "rank", BAD_CAST PQgetvalue(querySet.res, 0, 9));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "importance", BAD_CAST PQgetvalue(querySet.res, 0, 10));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_place_id", BAD_CAST PQgetvalue(querySet.res, 0, 11));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_type", BAD_CAST PQgetvalue(querySet.res, 0, 12));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_id", BAD_CAST PQgetvalue(querySet.res, 0, 13));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "linked_place_id", BAD_CAST PQgetvalue(querySet.res, 0, 15));
|
|
||||||
|
|
||||||
if (PQntuples(querySet.resNames))
|
|
||||||
{
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "names");
|
|
||||||
|
|
||||||
for (i = 0; i < PQntuples(querySet.resNames); i++)
|
|
||||||
{
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "name");
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(querySet.resNames, i, 0));
|
|
||||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.resNames, i, 1));
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PQgetvalue(querySet.res, 0, 5) && strlen(PQgetvalue(querySet.res, 0, 5)))
|
|
||||||
{
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "houseNumber");
|
|
||||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.res, 0, 5));
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PQgetvalue(querySet.res, 0, 8) && strlen(PQgetvalue(querySet.res, 0, 8)))
|
|
||||||
{
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "adminLevel");
|
|
||||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.res, 0, 8));
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PQgetvalue(querySet.res, 0, 6) && strlen(PQgetvalue(querySet.res, 0, 6)))
|
|
||||||
{
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "countryCode");
|
|
||||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.res, 0, 6));
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PQntuples(querySet.resAddress) > 0)
|
|
||||||
{
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "address");
|
|
||||||
for (i = 0; i < PQntuples(querySet.resAddress); i++)
|
|
||||||
{
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST getRankLabel(atoi(PQgetvalue(querySet.resAddress, i, 5))));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "rank", BAD_CAST PQgetvalue(querySet.resAddress, i, 5));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(querySet.resAddress, i, 0));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "id", BAD_CAST PQgetvalue(querySet.resAddress, i, 1));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "key", BAD_CAST PQgetvalue(querySet.resAddress, i, 2));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "value", BAD_CAST PQgetvalue(querySet.resAddress, i, 3));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "distance", BAD_CAST PQgetvalue(querySet.resAddress, i, 4));
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "isaddress", BAD_CAST PQgetvalue(querySet.resAddress, i, 6));
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
}
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PQntuples(querySet.resExtraTags))
|
|
||||||
{
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "tags");
|
|
||||||
|
|
||||||
for (i = 0; i < PQntuples(querySet.resExtraTags); i++)
|
|
||||||
{
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "tag");
|
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(querySet.resExtraTags, i, 0));
|
|
||||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.resExtraTags, i, 1));
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
xmlTextWriterStartElement(writer, BAD_CAST "osmGeometry");
|
|
||||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.res, 0, 7));
|
|
||||||
xmlTextWriterEndElement(writer);
|
|
||||||
|
|
||||||
xmlTextWriterEndElement(writer); // </feature>
|
|
||||||
|
|
||||||
if (writer_mutex) pthread_mutex_unlock( writer_mutex );
|
|
||||||
|
|
||||||
nominatim_exportFreeQueries(&querySet);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char * getRankLabel(int rank)
|
|
||||||
{
|
|
||||||
switch (rank)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
return "continent";
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
return "sea";
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
case 6:
|
|
||||||
case 7:
|
|
||||||
return "country";
|
|
||||||
case 8:
|
|
||||||
case 9:
|
|
||||||
case 10:
|
|
||||||
case 11:
|
|
||||||
return "state";
|
|
||||||
case 12:
|
|
||||||
case 13:
|
|
||||||
case 14:
|
|
||||||
case 15:
|
|
||||||
return "county";
|
|
||||||
case 16:
|
|
||||||
return "city";
|
|
||||||
case 17:
|
|
||||||
return "town";
|
|
||||||
case 18:
|
|
||||||
return "village";
|
|
||||||
case 19:
|
|
||||||
return "unknown";
|
|
||||||
case 20:
|
|
||||||
return "suburb";
|
|
||||||
case 21:
|
|
||||||
return "postcode";
|
|
||||||
case 22:
|
|
||||||
return "neighborhood";
|
|
||||||
case 23:
|
|
||||||
return "postcode";
|
|
||||||
case 24:
|
|
||||||
return "unknown";
|
|
||||||
case 25:
|
|
||||||
return "postcode";
|
|
||||||
case 26:
|
|
||||||
return "street";
|
|
||||||
case 27:
|
|
||||||
return "access";
|
|
||||||
case 28:
|
|
||||||
return "building";
|
|
||||||
case 29:
|
|
||||||
default:
|
|
||||||
return "other";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#ifndef EXPORT_H
|
|
||||||
#define EXPORT_H
|
|
||||||
|
|
||||||
#include <libxml/encoding.h>
|
|
||||||
#include <libxml/xmlwriter.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
struct export_data
|
|
||||||
{
|
|
||||||
PGresult * res;
|
|
||||||
PGresult * resNames;
|
|
||||||
PGresult * resAddress;
|
|
||||||
PGresult * resExtraTags;
|
|
||||||
};
|
|
||||||
|
|
||||||
void nominatim_export(int rank_min, int rank_max, const char *conninfo, const char *structuredoutputfile);
|
|
||||||
void nominatim_exportCreatePreparedQueries(PGconn * conn);
|
|
||||||
|
|
||||||
xmlTextWriterPtr nominatim_exportXMLStart(const char *structuredoutputfile);
|
|
||||||
void nominatim_exportXMLEnd(xmlTextWriterPtr writer);
|
|
||||||
|
|
||||||
void nominatim_exportEndMode(xmlTextWriterPtr writer);
|
|
||||||
|
|
||||||
void nominatim_exportPlaceQueries(uint64_t place_id, PGconn * conn, struct export_data * querySet);
|
|
||||||
void nominatim_exportFreeQueries(struct export_data * querySet);
|
|
||||||
|
|
||||||
void nominatim_exportPlace(uint64_t place_id, PGconn * conn,
|
|
||||||
xmlTextWriterPtr writer, pthread_mutex_t * writer_mutex, struct export_data * prevQuerySet);
|
|
||||||
const char * getRankLabel(int rank);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,856 +0,0 @@
|
|||||||
/*
|
|
||||||
*/
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <libpq-fe.h>
|
|
||||||
|
|
||||||
#include <libxml/xmlstring.h>
|
|
||||||
#include <libxml/xmlreader.h>
|
|
||||||
#include <libxml/hash.h>
|
|
||||||
|
|
||||||
#include "nominatim.h"
|
|
||||||
#include "import.h"
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
typedef enum { FILETYPE_NONE, FILETYPE_STRUCTUREDV0P1 } filetypes_t;
|
|
||||||
typedef enum { FILEMODE_NONE, FILEMODE_ADD, FILEMODE_UPDATE, FILEMODE_DELETE } filemodes_t;
|
|
||||||
|
|
||||||
#define MAX_FEATUREADDRESS 5000
|
|
||||||
#define MAX_FEATURENAMES 10000
|
|
||||||
#define MAX_FEATUREEXTRATAGS 10000
|
|
||||||
#define MAX_FEATURENAMESTRING 1000000
|
|
||||||
#define MAX_FEATUREEXTRATAGSTRING 500000
|
|
||||||
|
|
||||||
struct feature_address
|
|
||||||
{
|
|
||||||
int place_id;
|
|
||||||
int rankAddress;
|
|
||||||
char isAddress[2];
|
|
||||||
xmlChar * type;
|
|
||||||
xmlChar * id;
|
|
||||||
xmlChar * key;
|
|
||||||
xmlChar * value;
|
|
||||||
xmlChar * distance;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct feature_tag
|
|
||||||
{
|
|
||||||
xmlChar * type;
|
|
||||||
xmlChar * value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct feature
|
|
||||||
{
|
|
||||||
xmlChar * placeID;
|
|
||||||
xmlChar * type;
|
|
||||||
xmlChar * id;
|
|
||||||
xmlChar * key;
|
|
||||||
xmlChar * value;
|
|
||||||
xmlChar * rankAddress;
|
|
||||||
xmlChar * rankSearch;
|
|
||||||
xmlChar * countryCode;
|
|
||||||
xmlChar * parentPlaceID;
|
|
||||||
xmlChar * parentType;
|
|
||||||
xmlChar * parentID;
|
|
||||||
xmlChar * adminLevel;
|
|
||||||
xmlChar * houseNumber;
|
|
||||||
xmlChar * geometry;
|
|
||||||
} feature;
|
|
||||||
|
|
||||||
int fileType = FILETYPE_NONE;
|
|
||||||
int fileMode = FILEMODE_ADD;
|
|
||||||
PGconn * conn;
|
|
||||||
struct feature_address featureAddress[MAX_FEATUREADDRESS];
|
|
||||||
struct feature_tag featureName[MAX_FEATURENAMES];
|
|
||||||
struct feature_tag featureExtraTag[MAX_FEATUREEXTRATAGS];
|
|
||||||
struct feature feature;
|
|
||||||
int featureAddressLines = 0;
|
|
||||||
int featureNameLines = 0;
|
|
||||||
int featureExtraTagLines = 0;
|
|
||||||
int featureCount = 0;
|
|
||||||
xmlHashTablePtr partionTableTagsHash;
|
|
||||||
xmlHashTablePtr partionTableTagsHashDelete;
|
|
||||||
char featureNameString[MAX_FEATURENAMESTRING];
|
|
||||||
char featureExtraTagString[MAX_FEATUREEXTRATAGSTRING];
|
|
||||||
|
|
||||||
extern int verbose;
|
|
||||||
|
|
||||||
void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
|
|
||||||
{
|
|
||||||
char * value;
|
|
||||||
float version;
|
|
||||||
int isAddressLine;
|
|
||||||
|
|
||||||
if (fileType == FILETYPE_NONE)
|
|
||||||
{
|
|
||||||
// Potential to handle other file types in the future / versions
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "osmStructured"))
|
|
||||||
{
|
|
||||||
value = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "version");
|
|
||||||
version = strtof(value, NULL);
|
|
||||||
xmlFree(value);
|
|
||||||
|
|
||||||
if (version == (float)0.1)
|
|
||||||
{
|
|
||||||
fileType = FILETYPE_STRUCTUREDV0P1;
|
|
||||||
fileMode = FILEMODE_ADD;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Unknown osmStructured version %f (%s)\n", version, value );
|
|
||||||
exit_nicely();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Unknown XML document type: %s\n", name );
|
|
||||||
exit_nicely();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "add"))
|
|
||||||
{
|
|
||||||
fileMode = FILEMODE_ADD;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "update"))
|
|
||||||
{
|
|
||||||
fileMode = FILEMODE_UPDATE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "delete"))
|
|
||||||
{
|
|
||||||
fileMode = FILEMODE_DELETE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fileMode == FILEMODE_NONE)
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Unknown import mode in: %s\n", name );
|
|
||||||
exit_nicely();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "feature"))
|
|
||||||
{
|
|
||||||
feature.placeID = xmlTextReaderGetAttribute(reader, BAD_CAST "place_id");
|
|
||||||
feature.type = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
|
|
||||||
feature.id = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
|
|
||||||
feature.key = xmlTextReaderGetAttribute(reader, BAD_CAST "key");
|
|
||||||
feature.value = xmlTextReaderGetAttribute(reader, BAD_CAST "value");
|
|
||||||
feature.rankAddress = xmlTextReaderGetAttribute(reader, BAD_CAST "rank");
|
|
||||||
feature.rankSearch = xmlTextReaderGetAttribute(reader, BAD_CAST "importance");
|
|
||||||
|
|
||||||
feature.parentPlaceID = xmlTextReaderGetAttribute(reader, BAD_CAST "parent_place_id");
|
|
||||||
/*
|
|
||||||
if (strlen(feature.parentPlaceID) == 0)
|
|
||||||
{
|
|
||||||
xmlFree(feature.parentPlaceID);
|
|
||||||
feature.parentPlaceID = NULL;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
feature.parentType = xmlTextReaderGetAttribute(reader, BAD_CAST "parent_type");
|
|
||||||
feature.parentID = xmlTextReaderGetAttribute(reader, BAD_CAST "parent_id");
|
|
||||||
|
|
||||||
feature.countryCode = NULL;
|
|
||||||
feature.adminLevel = NULL;
|
|
||||||
feature.houseNumber = NULL;
|
|
||||||
feature.geometry = NULL;
|
|
||||||
featureAddressLines = 0;
|
|
||||||
featureNameLines = 0;
|
|
||||||
featureExtraTagLines = 0;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "names")) return;
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "name"))
|
|
||||||
{
|
|
||||||
if (featureNameLines < MAX_FEATURENAMES)
|
|
||||||
{
|
|
||||||
featureName[featureNameLines].type = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
|
|
||||||
featureName[featureNameLines].value = xmlTextReaderReadString(reader);
|
|
||||||
featureNameLines++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Too many name elements (%s%s)\n", feature.type, feature.id);
|
|
||||||
// exit_nicely();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "tags")) return;
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "tag"))
|
|
||||||
{
|
|
||||||
if (featureExtraTagLines < MAX_FEATUREEXTRATAGS)
|
|
||||||
{
|
|
||||||
featureExtraTag[featureExtraTagLines].type = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
|
|
||||||
featureExtraTag[featureExtraTagLines].value = xmlTextReaderReadString(reader);
|
|
||||||
featureExtraTagLines++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Too many extra tag elements (%s%s)\n", feature.type, feature.id);
|
|
||||||
// exit_nicely();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "osmGeometry"))
|
|
||||||
{
|
|
||||||
feature.geometry = xmlTextReaderReadString(reader);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "adminLevel"))
|
|
||||||
{
|
|
||||||
feature.adminLevel = xmlTextReaderReadString(reader);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "countryCode"))
|
|
||||||
{
|
|
||||||
feature.countryCode = xmlTextReaderReadString(reader);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "houseNumber"))
|
|
||||||
{
|
|
||||||
feature.houseNumber = xmlTextReaderReadString(reader);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "address"))
|
|
||||||
{
|
|
||||||
featureAddressLines = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isAddressLine = 0;
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "continent"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "sea"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "country"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "state"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "county"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "city"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "town"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "village"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "unknown"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "suburb"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "postcode"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "neighborhood"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "street"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "access"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "building"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
else if (xmlStrEqual(name, BAD_CAST "other"))
|
|
||||||
{
|
|
||||||
isAddressLine = 1;
|
|
||||||
}
|
|
||||||
if (isAddressLine)
|
|
||||||
{
|
|
||||||
if (featureAddressLines < MAX_FEATUREADDRESS)
|
|
||||||
{
|
|
||||||
value = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "rank");
|
|
||||||
if (!value)
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Address element missing rank\n");
|
|
||||||
exit_nicely();
|
|
||||||
}
|
|
||||||
featureAddress[featureAddressLines].rankAddress = atoi(value);
|
|
||||||
xmlFree(value);
|
|
||||||
|
|
||||||
value = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "isaddress");
|
|
||||||
if (!value)
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Address element missing rank\n");
|
|
||||||
exit_nicely();
|
|
||||||
}
|
|
||||||
if (*value == 't') strcpy(featureAddress[featureAddressLines].isAddress, "t");
|
|
||||||
else strcpy(featureAddress[featureAddressLines].isAddress, "f");
|
|
||||||
xmlFree(value);
|
|
||||||
|
|
||||||
featureAddress[featureAddressLines].type = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
|
|
||||||
featureAddress[featureAddressLines].id = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
|
|
||||||
featureAddress[featureAddressLines].key = xmlTextReaderGetAttribute(reader, BAD_CAST "key");
|
|
||||||
featureAddress[featureAddressLines].value = xmlTextReaderGetAttribute(reader, BAD_CAST "value");
|
|
||||||
featureAddress[featureAddressLines].distance = xmlTextReaderGetAttribute(reader, BAD_CAST "distance");
|
|
||||||
|
|
||||||
featureAddressLines++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Too many address elements (%s%s)\n", feature.type, feature.id);
|
|
||||||
// exit_nicely();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
|
|
||||||
{
|
|
||||||
PGresult * res;
|
|
||||||
const char * paramValues[14];
|
|
||||||
char * place_id;
|
|
||||||
char * partionQueryName;
|
|
||||||
int i, namePos, lineTypeLen, lineValueLen;
|
|
||||||
|
|
||||||
if (xmlStrEqual(name, BAD_CAST "feature"))
|
|
||||||
{
|
|
||||||
featureCount++;
|
|
||||||
if (featureCount % 1000 == 0) printf("feature %i(k)\n", featureCount/1000);
|
|
||||||
/*
|
|
||||||
if (fileMode == FILEMODE_ADD)
|
|
||||||
{
|
|
||||||
resPlaceID = PQexecPrepared(conn, "get_new_place_id", 0, NULL, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(resPlaceID) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "get_place_id: INSERT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(resPlaceID);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
paramValues[0] = (const char *)feature.type;
|
|
||||||
paramValues[1] = (const char *)feature.id;
|
|
||||||
paramValues[2] = (const char *)feature.key;
|
|
||||||
paramValues[3] = (const char *)feature.value;
|
|
||||||
resPlaceID = PQexecPrepared(conn, "get_new_place_id", 4, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(resPlaceID) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_placex: INSERT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(resPlaceID);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
place_id = (char *)feature.placeID;
|
|
||||||
|
|
||||||
if (fileMode == FILEMODE_UPDATE || fileMode == FILEMODE_DELETE || fileMode == FILEMODE_ADD)
|
|
||||||
{
|
|
||||||
paramValues[0] = (const char *)place_id;
|
|
||||||
if (verbose) fprintf(stderr, "placex_delete: %s\n", paramValues[0]);
|
|
||||||
res = PQexecPrepared(conn, "placex_delete", 1, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "placex_delete: DELETE failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
if (verbose) fprintf(stderr, "search_name_delete: %s\n", paramValues[0]);
|
|
||||||
res = PQexecPrepared(conn, "search_name_delete", 1, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "search_name_delete: DELETE failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
if (verbose) fprintf(stderr, "place_addressline_delete: %s\n", paramValues[0]);
|
|
||||||
res = PQexecPrepared(conn, "place_addressline_delete", 1, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "place_addressline_delete: DELETE failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
partionQueryName = xmlHashLookup2(partionTableTagsHashDelete, feature.key, feature.value);
|
|
||||||
if (partionQueryName)
|
|
||||||
{
|
|
||||||
res = PQexecPrepared(conn, partionQueryName, 1, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: DELETE failed: %s", partionQueryName, PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileMode == FILEMODE_UPDATE || fileMode == FILEMODE_ADD)
|
|
||||||
{
|
|
||||||
// Insert into placex
|
|
||||||
paramValues[0] = (const char *)place_id;
|
|
||||||
paramValues[1] = (const char *)feature.type;
|
|
||||||
paramValues[2] = (const char *)feature.id;
|
|
||||||
paramValues[3] = (const char *)feature.key;
|
|
||||||
paramValues[4] = (const char *)feature.value;
|
|
||||||
|
|
||||||
featureNameString[0] = 0;
|
|
||||||
if (featureNameLines)
|
|
||||||
{
|
|
||||||
namePos = 0;
|
|
||||||
lineTypeLen = 0;
|
|
||||||
lineValueLen = 0;
|
|
||||||
for (i = 0; i < featureNameLines; i++)
|
|
||||||
{
|
|
||||||
lineTypeLen = (int)strlen((char *) featureName[i].type);
|
|
||||||
lineValueLen = (int)strlen((char *) featureName[i].value);
|
|
||||||
if (namePos+lineTypeLen+lineValueLen+7 > MAX_FEATURENAMESTRING)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "feature name too long: %s", (const char *)featureName[i].value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (namePos) strcpy(featureNameString+(namePos++), ",");
|
|
||||||
strcpy(featureNameString+(namePos++), "\"");
|
|
||||||
strcpy(featureNameString+namePos, (char*) featureName[i].type);
|
|
||||||
namePos += lineTypeLen;
|
|
||||||
strcpy(featureNameString+namePos, "\"=>\"");
|
|
||||||
namePos += 4;
|
|
||||||
strcpy(featureNameString+namePos, (char *) featureName[i].value);
|
|
||||||
namePos += lineValueLen;
|
|
||||||
strcpy(featureNameString+(namePos++), "\"");
|
|
||||||
|
|
||||||
xmlFree(featureName[i].type);
|
|
||||||
xmlFree(featureName[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
paramValues[5] = (const char *)featureNameString;
|
|
||||||
|
|
||||||
paramValues[6] = (const char *)feature.countryCode;
|
|
||||||
|
|
||||||
featureExtraTagString[0] = 0;
|
|
||||||
if (featureExtraTagLines)
|
|
||||||
{
|
|
||||||
namePos = 0;
|
|
||||||
lineTypeLen = 0;
|
|
||||||
lineValueLen = 0;
|
|
||||||
for (i = 0; i < featureExtraTagLines; i++)
|
|
||||||
{
|
|
||||||
lineTypeLen = strlen((char *) featureExtraTag[i].type);
|
|
||||||
lineValueLen = strlen((char *) featureExtraTag[i].value);
|
|
||||||
if (namePos+lineTypeLen+lineValueLen+7 > MAX_FEATUREEXTRATAGSTRING)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "feature extra tag too long: %s", (const char *)featureExtraTag[i].value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (namePos) strcpy(featureExtraTagString+(namePos++),",");
|
|
||||||
strcpy(featureExtraTagString+(namePos++), "\"");
|
|
||||||
strcpy(featureExtraTagString+namePos, (char *) featureExtraTag[i].type);
|
|
||||||
namePos += lineTypeLen;
|
|
||||||
strcpy(featureExtraTagString+namePos, "\"=>\"");
|
|
||||||
namePos += 4;
|
|
||||||
strcpy(featureExtraTagString+namePos, (char *) featureExtraTag[i].value);
|
|
||||||
namePos += lineValueLen;
|
|
||||||
strcpy(featureExtraTagString+(namePos++), "\"");
|
|
||||||
|
|
||||||
xmlFree(featureExtraTag[i].type);
|
|
||||||
xmlFree(featureExtraTag[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
paramValues[7] = (const char *)featureExtraTagString;
|
|
||||||
|
|
||||||
if (xmlStrlen(feature.parentPlaceID) == 0)
|
|
||||||
paramValues[8] = "0";
|
|
||||||
else
|
|
||||||
paramValues[8] = (const char *)feature.parentPlaceID;
|
|
||||||
|
|
||||||
paramValues[9] = (const char *)feature.adminLevel;
|
|
||||||
paramValues[10] = (const char *)feature.houseNumber;
|
|
||||||
paramValues[11] = (const char *)feature.rankAddress;
|
|
||||||
paramValues[12] = (const char *)feature.rankSearch;
|
|
||||||
paramValues[13] = (const char *)feature.geometry;
|
|
||||||
if (strlen(paramValues[3]) && strlen(paramValues[13]))
|
|
||||||
{
|
|
||||||
if (verbose) fprintf(stderr, "placex_insert: %s\n", paramValues[0]);
|
|
||||||
res = PQexecPrepared(conn, "placex_insert", 14, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_placex: INSERT failed: %s", PQerrorMessage(conn));
|
|
||||||
fprintf(stderr, "index_placex: INSERT failed: %s %s %s", paramValues[0], paramValues[1], paramValues[2]);
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < featureAddressLines; i++)
|
|
||||||
{
|
|
||||||
// insert into place_address
|
|
||||||
paramValues[0] = (const char *)place_id;
|
|
||||||
paramValues[1] = (const char *)featureAddress[i].distance;
|
|
||||||
if (paramValues[1] == NULL || strlen(paramValues[1]) == 0) paramValues[1] = "0";
|
|
||||||
paramValues[2] = (const char *)featureAddress[i].type;
|
|
||||||
paramValues[3] = (const char *)featureAddress[i].id;
|
|
||||||
paramValues[4] = (const char *)featureAddress[i].key;
|
|
||||||
paramValues[5] = (const char *)featureAddress[i].value;
|
|
||||||
paramValues[6] = (const char *)featureAddress[i].isAddress;
|
|
||||||
if (verbose) fprintf(stderr, "placex_insert: %s %s\n", paramValues[2], paramValues[3]);
|
|
||||||
res = PQexecPrepared(conn, "place_addressline_insert", 7, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "place_addressline_insert: INSERT failed: %s", PQerrorMessage(conn));
|
|
||||||
fprintf(stderr, "(%s,%s,%s,%s,%s,%s,%s)",paramValues[0],paramValues[1],paramValues[2],paramValues[3],paramValues[4],paramValues[5],paramValues[6]);
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
xmlFree(featureAddress[i].type);
|
|
||||||
xmlFree(featureAddress[i].id);
|
|
||||||
xmlFree(featureAddress[i].key);
|
|
||||||
xmlFree(featureAddress[i].value);
|
|
||||||
xmlFree(featureAddress[i].distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (featureNameLines)
|
|
||||||
{
|
|
||||||
if (xmlStrlen(feature.parentPlaceID) > 0 && featureAddressLines == 0)
|
|
||||||
{
|
|
||||||
paramValues[0] = (const char *)place_id;
|
|
||||||
paramValues[1] = (const char *)feature.parentPlaceID;
|
|
||||||
if (verbose) fprintf(stderr, "search_name_from_parent_insert: INSERT %s %s\n", paramValues[0], paramValues[1]);
|
|
||||||
res = PQexecPrepared(conn, "search_name_from_parent_insert", 2, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "search_name_from_parent_insert: INSERT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
paramValues[0] = (const char *)place_id;
|
|
||||||
if (verbose) fprintf(stderr, "search_name_insert: INSERT %s\n", paramValues[0]);
|
|
||||||
res = PQexecPrepared(conn, "search_name_insert", 1, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "search_name_insert: INSERT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
partionQueryName = xmlHashLookup2(partionTableTagsHash, feature.key, feature.value);
|
|
||||||
if (partionQueryName)
|
|
||||||
{
|
|
||||||
// insert into partition table
|
|
||||||
paramValues[0] = (const char *)place_id;
|
|
||||||
paramValues[1] = (const char *)feature.geometry;
|
|
||||||
res = PQexecPrepared(conn, partionQueryName, 2, paramValues, NULL, NULL, 0);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: INSERT failed: %s", partionQueryName, PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i = 0; i < featureAddressLines; i++)
|
|
||||||
{
|
|
||||||
xmlFree(featureAddress[i].type);
|
|
||||||
xmlFree(featureAddress[i].id);
|
|
||||||
xmlFree(featureAddress[i].key);
|
|
||||||
xmlFree(featureAddress[i].value);
|
|
||||||
xmlFree(featureAddress[i].distance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlFree(feature.placeID);
|
|
||||||
xmlFree(feature.type);
|
|
||||||
xmlFree(feature.id);
|
|
||||||
xmlFree(feature.key);
|
|
||||||
xmlFree(feature.value);
|
|
||||||
xmlFree(feature.rankAddress);
|
|
||||||
xmlFree(feature.rankSearch);
|
|
||||||
if (feature.countryCode) xmlFree(feature.countryCode);
|
|
||||||
if (feature.parentPlaceID) xmlFree(feature.parentPlaceID);
|
|
||||||
if (feature.parentType) xmlFree(feature.parentType);
|
|
||||||
if (feature.parentID) xmlFree(feature.parentID);
|
|
||||||
// if (feature.name) xmlFree(feature.name);
|
|
||||||
if (feature.adminLevel) xmlFree(feature.adminLevel);
|
|
||||||
if (feature.houseNumber) xmlFree(feature.houseNumber);
|
|
||||||
if (feature.geometry) xmlFree(feature.geometry);
|
|
||||||
|
|
||||||
// PQclear(resPlaceID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void processNode(xmlTextReaderPtr reader)
|
|
||||||
{
|
|
||||||
xmlChar *name;
|
|
||||||
name = xmlTextReaderName(reader);
|
|
||||||
if (name == NULL)
|
|
||||||
{
|
|
||||||
name = xmlStrdup(BAD_CAST "--");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (xmlTextReaderNodeType(reader))
|
|
||||||
{
|
|
||||||
case XML_READER_TYPE_ELEMENT:
|
|
||||||
StartElement(reader, name);
|
|
||||||
if (xmlTextReaderIsEmptyElement(reader))
|
|
||||||
EndElement(reader, name); /* No end_element for self closing tags! */
|
|
||||||
break;
|
|
||||||
case XML_READER_TYPE_END_ELEMENT:
|
|
||||||
EndElement(reader, name);
|
|
||||||
break;
|
|
||||||
case XML_READER_TYPE_TEXT:
|
|
||||||
case XML_READER_TYPE_CDATA:
|
|
||||||
case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
|
|
||||||
/* Ignore */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Unknown node type %d\n", xmlTextReaderNodeType(reader));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlFree(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int nominatim_import(const char *conninfo, const char *partionTagsFilename, const char *filename)
|
|
||||||
{
|
|
||||||
xmlTextReaderPtr reader;
|
|
||||||
int ret = 0;
|
|
||||||
PGresult * res;
|
|
||||||
FILE * partionTagsFile;
|
|
||||||
char * partionQueryName;
|
|
||||||
char partionQuerySQL[1024];
|
|
||||||
|
|
||||||
conn = PQconnectdb(conninfo);
|
|
||||||
if (PQstatus(conn) != CONNECTION_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
partionTableTagsHash = xmlHashCreate(200);
|
|
||||||
partionTableTagsHashDelete = xmlHashCreate(200);
|
|
||||||
|
|
||||||
partionTagsFile = fopen(partionTagsFilename, "rt");
|
|
||||||
if (!partionTagsFile)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Unable to read partition tags file: %s\n", partionTagsFilename);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
char buffer[1024], osmkey[256], osmvalue[256];
|
|
||||||
int fields;
|
|
||||||
while (fgets(buffer, sizeof(buffer), partionTagsFile) != NULL)
|
|
||||||
{
|
|
||||||
fields = sscanf( buffer, "%23s %63s", osmkey, osmvalue );
|
|
||||||
|
|
||||||
if ( fields <= 0 ) continue;
|
|
||||||
|
|
||||||
if ( fields != 2 )
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Error partition file\n");
|
|
||||||
exit_nicely();
|
|
||||||
}
|
|
||||||
partionQueryName = malloc(strlen("partition_insert_")+strlen(osmkey)+strlen(osmvalue)+2);
|
|
||||||
strcpy(partionQueryName, "partition_insert_");
|
|
||||||
strcat(partionQueryName, osmkey);
|
|
||||||
strcat(partionQueryName, "_");
|
|
||||||
strcat(partionQueryName, osmvalue);
|
|
||||||
|
|
||||||
strcpy(partionQuerySQL, "insert into place_classtype_");
|
|
||||||
strcat(partionQuerySQL, osmkey);
|
|
||||||
strcat(partionQuerySQL, "_");
|
|
||||||
strcat(partionQuerySQL, osmvalue);
|
|
||||||
strcat(partionQuerySQL, " (place_id, centroid) values ($1, ST_Centroid(st_setsrid($2, 4326)))");
|
|
||||||
|
|
||||||
res = PQprepare(conn, partionQueryName, partionQuerySQL, 2, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare %s: %s\n", partionQueryName, PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlHashAddEntry2(partionTableTagsHash, BAD_CAST osmkey, BAD_CAST osmvalue, BAD_CAST partionQueryName);
|
|
||||||
|
|
||||||
partionQueryName = malloc(strlen("partition_delete_")+strlen(osmkey)+strlen(osmvalue)+2);
|
|
||||||
strcpy(partionQueryName, "partition_delete_");
|
|
||||||
strcat(partionQueryName, osmkey);
|
|
||||||
strcat(partionQueryName, "_");
|
|
||||||
strcat(partionQueryName, osmvalue);
|
|
||||||
|
|
||||||
strcpy(partionQuerySQL, "delete from place_classtype_");
|
|
||||||
strcat(partionQuerySQL, osmkey);
|
|
||||||
strcat(partionQuerySQL, "_");
|
|
||||||
strcat(partionQuerySQL, osmvalue);
|
|
||||||
strcat(partionQuerySQL, " where place_id = $1::integer");
|
|
||||||
|
|
||||||
res = PQprepare(conn, partionQueryName, partionQuerySQL, 1, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare %s: %s\n", partionQueryName, PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlHashAddEntry2(partionTableTagsHashDelete, BAD_CAST osmkey, BAD_CAST osmvalue, BAD_CAST partionQueryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQprepare(conn, "get_new_place_id",
|
|
||||||
"select nextval('seq_place')",
|
|
||||||
0, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare get_new_place_id: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQprepare(conn, "get_place_id",
|
|
||||||
"select place_id from placex where osm_type = $1 and osm_id = $2 and class = $3 and type = $4",
|
|
||||||
4, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare get_place_id: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQprepare(conn, "placex_insert",
|
|
||||||
"insert into placex (place_id,osm_type,osm_id,class,type,name,country_code,extratags,parent_place_id,admin_level,housenumber,rank_address,rank_search,geometry) "
|
|
||||||
"values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, st_setsrid($14, 4326))",
|
|
||||||
12, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare placex_insert: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQprepare(conn, "search_name_insert",
|
|
||||||
"insert into search_name (place_id, search_rank, address_rank, country_code, name_vector, nameaddress_vector, centroid) "
|
|
||||||
"select place_id, rank_search, rank_address, country_code, make_keywords(name), "
|
|
||||||
"(select uniq(sort(array_agg(parent_search_name.name_vector))) from search_name as parent_search_name where place_id in "
|
|
||||||
"(select distinct address_place_id from place_addressline where place_addressline.place_id = $1 limit 1000)"
|
|
||||||
"), st_centroid(geometry) from placex "
|
|
||||||
"where place_id = $1",
|
|
||||||
1, NULL);
|
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare search_name_insert: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQprepare(conn, "search_name_from_parent_insert",
|
|
||||||
"insert into search_name (place_id, search_rank, address_rank, country_code, name_vector, nameaddress_vector, centroid) "
|
|
||||||
"select place_id, rank_search, rank_address, country_code, make_keywords(name), "
|
|
||||||
"(select uniq(sort(name_vector+nameaddress_vector)) from search_name as parent_search_name "
|
|
||||||
"where parent_search_name.place_id = $2 ), st_centroid(geometry) from placex "
|
|
||||||
"where place_id = $1",
|
|
||||||
2, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare search_name_insert: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQprepare(conn, "place_addressline_insert",
|
|
||||||
"insert into place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address) "
|
|
||||||
"select $1, place_id, false, $7, $2, rank_address from placex where osm_type = $3 and osm_id = $4 and class = $5 and type = $6",
|
|
||||||
7, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare place_addressline_insert: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQprepare(conn, "placex_delete",
|
|
||||||
"delete from placex where place_id = $1",
|
|
||||||
1, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare placex_delete: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQprepare(conn, "search_name_delete",
|
|
||||||
"delete from search_name where place_id = $1",
|
|
||||||
1, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare search_name_delete: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQprepare(conn, "place_addressline_delete",
|
|
||||||
"delete from place_addressline where place_id = $1",
|
|
||||||
1, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to prepare place_addressline_delete: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
featureCount = 0;
|
|
||||||
|
|
||||||
reader = inputUTF8(filename);
|
|
||||||
|
|
||||||
if (reader == NULL)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Unable to open %s\n", filename);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = xmlTextReaderRead(reader);
|
|
||||||
while (ret == 1)
|
|
||||||
{
|
|
||||||
processNode(reader);
|
|
||||||
ret = xmlTextReaderRead(reader);
|
|
||||||
}
|
|
||||||
if (ret != 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s : failed to parse\n", filename);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlFreeTextReader(reader);
|
|
||||||
xmlHashFree(partionTableTagsHash, NULL);
|
|
||||||
xmlHashFree(partionTableTagsHashDelete, NULL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef IMPORT_H
|
|
||||||
#define IMPORT_H
|
|
||||||
|
|
||||||
int nominatim_import(const char *conninfo, const char *partionTagsFilename, const char *filename);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,547 +0,0 @@
|
|||||||
/*
|
|
||||||
* triggers indexing (reparenting etc.) through setting resetting indexed_status: update placex/osmline set indexed_status = 0 where indexed_status > 0
|
|
||||||
* triggers placex_update and osmline_update
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <libpq-fe.h>
|
|
||||||
|
|
||||||
#include "nominatim.h"
|
|
||||||
#include "index.h"
|
|
||||||
#include "export.h"
|
|
||||||
#include "postgresql.h"
|
|
||||||
|
|
||||||
extern int verbose;
|
|
||||||
|
|
||||||
void run_indexing(int rank, int interpolation, PGconn *conn, int num_threads,
|
|
||||||
struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|
||||||
{
|
|
||||||
int tuples, count, sleepcount;
|
|
||||||
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
time_t rankStartTime;
|
|
||||||
int rankTotalTuples;
|
|
||||||
int rankCountTuples;
|
|
||||||
float rankPerSecond;
|
|
||||||
|
|
||||||
PGresult * resSectors;
|
|
||||||
PGresult * resPlaces;
|
|
||||||
PGresult * resNULL;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
int iSector;
|
|
||||||
int iResult;
|
|
||||||
|
|
||||||
const char *paramValues[2];
|
|
||||||
int paramLengths[2];
|
|
||||||
int paramFormats[2];
|
|
||||||
uint32_t paramRank;
|
|
||||||
uint32_t paramSector;
|
|
||||||
uint32_t sector;
|
|
||||||
|
|
||||||
xmlTextWriterPtr writer;
|
|
||||||
pthread_mutex_t writer_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
// Create the output file
|
|
||||||
writer = NULL;
|
|
||||||
if (structuredoutputfile)
|
|
||||||
{
|
|
||||||
writer = nominatim_exportXMLStart(structuredoutputfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interpolation)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Starting interpolation lines (location_property_osmline)\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Starting rank %d\n", rank);
|
|
||||||
}
|
|
||||||
|
|
||||||
rankCountTuples = 0;
|
|
||||||
rankPerSecond = 0;
|
|
||||||
|
|
||||||
paramRank = PGint32(rank);
|
|
||||||
paramValues[0] = (char *)¶mRank;
|
|
||||||
paramLengths[0] = sizeof(paramRank);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
|
|
||||||
if (interpolation)
|
|
||||||
{
|
|
||||||
resSectors = PQexecPrepared(conn, "index_sectors_osmline", 0, NULL, 0, NULL, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resSectors = PQexecPrepared(conn, "index_sectors", 1, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
}
|
|
||||||
if (PQresultStatus(resSectors) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_sectors: SELECT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(resSectors);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (PQftype(resSectors, 0) != PG_OID_INT4)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Sector value has unexpected type\n");
|
|
||||||
PQclear(resSectors);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (PQftype(resSectors, 1) != PG_OID_INT8)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Sector value has unexpected type\n");
|
|
||||||
PQclear(resSectors);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
rankTotalTuples = 0;
|
|
||||||
for (iSector = 0; iSector < PQntuples(resSectors); iSector++)
|
|
||||||
{
|
|
||||||
rankTotalTuples += PGint64(*((uint64_t *)PQgetvalue(resSectors, iSector, 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
rankStartTime = time(0);
|
|
||||||
for (iSector = 0; iSector <= PQntuples(resSectors); iSector++)
|
|
||||||
{
|
|
||||||
if (iSector > 0)
|
|
||||||
{
|
|
||||||
resPlaces = PQgetResult(conn);
|
|
||||||
if (PQresultStatus(resPlaces) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_sector_places: SELECT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(resPlaces);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (PQftype(resPlaces, 0) != PG_OID_INT8)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Place_id value has unexpected type\n");
|
|
||||||
PQclear(resPlaces);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
resNULL = PQgetResult(conn);
|
|
||||||
if (resNULL != NULL)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Unexpected non-null response\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iSector < PQntuples(resSectors))
|
|
||||||
{
|
|
||||||
sector = PGint32(*((uint32_t *)PQgetvalue(resSectors, iSector, 0)));
|
|
||||||
// fprintf(stderr, "\n Starting sector %d size %ld\n", sector, PGint64(*((uint64_t *)PQgetvalue(resSectors, iSector, 1))));
|
|
||||||
|
|
||||||
// Get all the place_id's for this sector
|
|
||||||
paramRank = PGint32(rank);
|
|
||||||
paramSector = PGint32(sector);
|
|
||||||
if (rankTotalTuples-rankCountTuples < num_threads*1000)
|
|
||||||
{
|
|
||||||
// no sectors
|
|
||||||
if (interpolation)
|
|
||||||
{
|
|
||||||
iResult = PQsendQueryPrepared(conn, "index_nosector_places_osmline", 0, NULL, 0, NULL, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
paramValues[0] = (char *)¶mRank;
|
|
||||||
paramLengths[0] = sizeof(paramRank);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
iResult = PQsendQueryPrepared(conn, "index_nosector_places", 1, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (interpolation)
|
|
||||||
{
|
|
||||||
iResult = PQsendQueryPrepared(conn, "index_sector_places_osmline", 1, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
paramValues[0] = (char *)¶mSector;
|
|
||||||
paramLengths[0] = sizeof(paramSector);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
paramValues[0] = (char *)¶mRank;
|
|
||||||
paramLengths[0] = sizeof(paramRank);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
paramValues[1] = (char *)¶mSector;
|
|
||||||
paramLengths[1] = sizeof(paramSector);
|
|
||||||
paramFormats[1] = 1;
|
|
||||||
iResult = PQsendQueryPrepared(conn, "index_sector_places", 2, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!iResult)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_sector_places: SELECT failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(resPlaces);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (iSector > 0)
|
|
||||||
{
|
|
||||||
count = 0;
|
|
||||||
rankPerSecond = 0;
|
|
||||||
tuples = PQntuples(resPlaces);
|
|
||||||
|
|
||||||
if (tuples > 0)
|
|
||||||
{
|
|
||||||
// Spawn threads
|
|
||||||
for (i = 0; i < num_threads; i++)
|
|
||||||
{
|
|
||||||
thread_data[i].res = resPlaces;
|
|
||||||
thread_data[i].tuples = tuples;
|
|
||||||
thread_data[i].count = &count;
|
|
||||||
thread_data[i].count_mutex = &count_mutex;
|
|
||||||
thread_data[i].writer = writer;
|
|
||||||
thread_data[i].writer_mutex = &writer_mutex;
|
|
||||||
if (interpolation)
|
|
||||||
{
|
|
||||||
thread_data[i].table = 0; // use interpolations table
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
thread_data[i].table = 1; // use placex table
|
|
||||||
}
|
|
||||||
pthread_create(&thread_data[i].thread, NULL, &nominatim_indexThread, (void *)&thread_data[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Monitor threads to give user feedback
|
|
||||||
sleepcount = 0;
|
|
||||||
while (count < tuples)
|
|
||||||
{
|
|
||||||
usleep(1000);
|
|
||||||
|
|
||||||
// Aim for one update per second
|
|
||||||
if (sleepcount++ > 1000)
|
|
||||||
{
|
|
||||||
rankPerSecond = ((float)rankCountTuples + (float)count) / MAX(difftime(time(0), rankStartTime),1);
|
|
||||||
if(interpolation)
|
|
||||||
{
|
|
||||||
fprintf(stderr, " Done %i in %i @ %f per second - Interpolation lines ETA (seconds): %f\n", (rankCountTuples + count), (int)(difftime(time(0), rankStartTime)), rankPerSecond, ((float)(rankTotalTuples - (rankCountTuples + count)))/rankPerSecond);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, " Done %i in %i @ %f per second - Rank %i ETA (seconds): %f\n", (rankCountTuples + count), (int)(difftime(time(0), rankStartTime)), rankPerSecond, rank, ((float)(rankTotalTuples - (rankCountTuples + count)))/rankPerSecond);
|
|
||||||
}
|
|
||||||
|
|
||||||
sleepcount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for everything to finish
|
|
||||||
for (i = 0; i < num_threads; i++)
|
|
||||||
{
|
|
||||||
pthread_join(thread_data[i].thread, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
rankCountTuples += tuples;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finished sector
|
|
||||||
rankPerSecond = (float)rankCountTuples / MAX(difftime(time(0), rankStartTime),1);
|
|
||||||
fprintf(stderr, " Done %i in %i @ %f per second - ETA (seconds): %f\n", rankCountTuples, (int)(difftime(time(0), rankStartTime)), rankPerSecond, ((float)(rankTotalTuples - rankCountTuples))/rankPerSecond);
|
|
||||||
|
|
||||||
PQclear(resPlaces);
|
|
||||||
}
|
|
||||||
if (rankTotalTuples-rankCountTuples < num_threads*20 && iSector < PQntuples(resSectors))
|
|
||||||
{
|
|
||||||
iSector = PQntuples(resSectors) - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Finished rank
|
|
||||||
fprintf(stderr, "\r Done %i in %i @ %f per second - FINISHED\n\n", rankCountTuples, (int)(difftime(time(0), rankStartTime)), rankPerSecond);
|
|
||||||
|
|
||||||
PQclear(resSectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile)
|
|
||||||
{
|
|
||||||
struct index_thread_data *thread_data;
|
|
||||||
|
|
||||||
PGconn *conn;
|
|
||||||
PGresult *res;
|
|
||||||
int num_rows = 0, status_code = 0;
|
|
||||||
int db_has_locale = 0;
|
|
||||||
char *result_string = NULL;
|
|
||||||
|
|
||||||
int rank;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
xmlTextWriterPtr writer;
|
|
||||||
pthread_mutex_t writer_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
Oid pg_prepare_params[2];
|
|
||||||
|
|
||||||
conn = PQconnectdb(conninfo);
|
|
||||||
if (PQstatus(conn) != CONNECTION_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQexec(conn, "SHOW lc_messages");
|
|
||||||
status_code = PQresultStatus(res);
|
|
||||||
if (status_code != PGRES_TUPLES_OK && status_code != PGRES_SINGLE_TUPLE) {
|
|
||||||
fprintf(stderr, "Failed determining database locale: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
num_rows = PQntuples(res);
|
|
||||||
if (num_rows > 0)
|
|
||||||
{
|
|
||||||
result_string = PQgetvalue(res, 0, 0);
|
|
||||||
if (result_string && (strlen(result_string) > 0) && (strcasecmp(result_string, "C") != 0))
|
|
||||||
{
|
|
||||||
// non-default locale if the result exists, is non-empty, and is not "C"
|
|
||||||
db_has_locale = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_sectors",
|
|
||||||
"select geometry_sector,count(*) from placex where rank_search = $1 and indexed_status > 0 group by geometry_sector order by geometry_sector",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sectors: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
res = PQprepare(conn, "index_sectors_osmline",
|
|
||||||
"select geometry_sector,count(*) from location_property_osmline where indexed_status > 0 group by geometry_sector order by geometry_sector",
|
|
||||||
0, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sectors: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_nosectors",
|
|
||||||
"select 0::integer,count(*) from placex where rank_search = $1 and indexed_status > 0",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sectors: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
pg_prepare_params[1] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_sector_places",
|
|
||||||
"select place_id from placex where rank_search = $1 and geometry_sector = $2 and indexed_status > 0",
|
|
||||||
2, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sector_places: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_nosector_places",
|
|
||||||
"select place_id from placex where rank_search = $1 and indexed_status > 0 order by geometry_sector",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_nosector_places: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_sector_places_osmline",
|
|
||||||
"select place_id from location_property_osmline where geometry_sector = $1 and indexed_status > 0",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sector_places: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
res = PQprepare(conn, "index_nosector_places_osmline",
|
|
||||||
"select place_id from location_property_osmline where indexed_status > 0 order by geometry_sector",
|
|
||||||
0, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_nosector_places: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
// Build the data for each thread
|
|
||||||
thread_data = (struct index_thread_data *)malloc(sizeof(struct index_thread_data)*num_threads);
|
|
||||||
for (i = 0; i < num_threads; i++)
|
|
||||||
{
|
|
||||||
thread_data[i].conn = PQconnectdb(conninfo);
|
|
||||||
if (PQstatus(thread_data[i].conn) != CONNECTION_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(thread_data[i].conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT8;
|
|
||||||
res = PQprepare(thread_data[i].conn, "index_placex",
|
|
||||||
"update placex set indexed_status = 0 where place_id = $1",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_placex: %s\n", PQerrorMessage(thread_data[i].conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT8;
|
|
||||||
res = PQprepare(thread_data[i].conn, "index_osmline",
|
|
||||||
"update location_property_osmline set indexed_status = 0 where place_id = $1",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_osmline: %s\n", PQerrorMessage(thread_data[i].conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
if (db_has_locale)
|
|
||||||
{
|
|
||||||
// Make sure the error message is not localized as we parse it later.
|
|
||||||
res = PQexec(thread_data[i].conn, "SET lc_messages TO 'C'");
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to set langauge: %s\n", PQerrorMessage(thread_data[i].conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
}
|
|
||||||
nominatim_exportCreatePreparedQueries(thread_data[i].conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Starting indexing rank (%i to %i) using %i threads\n", rank_min, rank_max, num_threads);
|
|
||||||
|
|
||||||
for (rank = rank_min; rank <= rank_max; rank++)
|
|
||||||
{
|
|
||||||
// OSMLINE: do reindexing (=> reparenting) for interpolation lines at rank 30, but before all other objects of rank 30
|
|
||||||
// reason: houses (rank 30) depend on the updated interpolation line, when reparenting (see placex_update in functions.sql)
|
|
||||||
if (rank == 30)
|
|
||||||
{
|
|
||||||
run_indexing(rank, 1, conn, num_threads, thread_data, structuredoutputfile);
|
|
||||||
}
|
|
||||||
run_indexing(rank, 0, conn, num_threads, thread_data, structuredoutputfile);
|
|
||||||
}
|
|
||||||
// Close all connections
|
|
||||||
for (i = 0; i < num_threads; i++)
|
|
||||||
{
|
|
||||||
PQfinish(thread_data[i].conn);
|
|
||||||
}
|
|
||||||
PQfinish(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *nominatim_indexThread(void * thread_data_in)
|
|
||||||
{
|
|
||||||
struct index_thread_data * thread_data = (struct index_thread_data * )thread_data_in;
|
|
||||||
struct export_data querySet;
|
|
||||||
|
|
||||||
PGresult *res;
|
|
||||||
|
|
||||||
const char *paramValues[1];
|
|
||||||
int paramLengths[1];
|
|
||||||
int paramFormats[1];
|
|
||||||
uint64_t paramPlaceID;
|
|
||||||
uint64_t place_id;
|
|
||||||
time_t updateStartTime;
|
|
||||||
unsigned table;
|
|
||||||
|
|
||||||
table = thread_data->table;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock( thread_data->count_mutex );
|
|
||||||
if (*(thread_data->count) >= thread_data->tuples)
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock( thread_data->count_mutex );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
place_id = PGint64(*((uint64_t *)PQgetvalue(thread_data->res, *thread_data->count, 0)));
|
|
||||||
(*thread_data->count)++;
|
|
||||||
|
|
||||||
pthread_mutex_unlock( thread_data->count_mutex );
|
|
||||||
|
|
||||||
if (verbose) fprintf(stderr, " Processing place_id %ld\n", place_id);
|
|
||||||
|
|
||||||
updateStartTime = time(0);
|
|
||||||
int done = 0;
|
|
||||||
|
|
||||||
if (thread_data->writer)
|
|
||||||
{
|
|
||||||
nominatim_exportPlaceQueries(place_id, thread_data->conn, &querySet);
|
|
||||||
}
|
|
||||||
|
|
||||||
while(!done)
|
|
||||||
{
|
|
||||||
paramPlaceID = PGint64(place_id);
|
|
||||||
paramValues[0] = (char *)¶mPlaceID;
|
|
||||||
paramLengths[0] = sizeof(paramPlaceID);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
if (table == 1) // table=1 for placex
|
|
||||||
{
|
|
||||||
res = PQexecPrepared(thread_data->conn, "index_placex", 1, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
}
|
|
||||||
else // table=0 for osmline
|
|
||||||
{
|
|
||||||
res = PQexecPrepared(thread_data->conn, "index_osmline", 1, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
}
|
|
||||||
if (PQresultStatus(res) == PGRES_COMMAND_OK)
|
|
||||||
done = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!strncmp(PQerrorMessage(thread_data->conn), "ERROR: deadlock detected", 25))
|
|
||||||
{
|
|
||||||
if (table == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_placex: UPDATE failed - deadlock, retrying (%ld)\n", place_id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_osmline: UPDATE failed - deadlock, retrying (%ld)\n", place_id);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
sleep(rand() % 10);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (table == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_placex: UPDATE failed: %s", PQerrorMessage(thread_data->conn));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_osmline: UPDATE failed: %s", PQerrorMessage(thread_data->conn));
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
if (difftime(time(0), updateStartTime) > 1) fprintf(stderr, " Slow place_id %ld\n", place_id);
|
|
||||||
|
|
||||||
if (thread_data->writer)
|
|
||||||
{
|
|
||||||
nominatim_exportPlace(place_id, thread_data->conn, thread_data->writer, thread_data->writer_mutex, &querySet);
|
|
||||||
nominatim_exportFreeQueries(&querySet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#ifndef INDEX_H
|
|
||||||
#define INDEX_H
|
|
||||||
|
|
||||||
#include <libxml/encoding.h>
|
|
||||||
#include <libxml/xmlwriter.h>
|
|
||||||
|
|
||||||
struct index_thread_data
|
|
||||||
{
|
|
||||||
pthread_t thread;
|
|
||||||
PGconn * conn;
|
|
||||||
PGresult * res;
|
|
||||||
int tuples;
|
|
||||||
int * count;
|
|
||||||
pthread_mutex_t * count_mutex;
|
|
||||||
xmlTextWriterPtr writer;
|
|
||||||
pthread_mutex_t * writer_mutex;
|
|
||||||
unsigned table;
|
|
||||||
};
|
|
||||||
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile);
|
|
||||||
void *nominatim_indexThread(void * thread_data_in);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
#define _FILE_OFFSET_BITS 64
|
|
||||||
#define _LARGEFILE64_SOURCE
|
|
||||||
|
|
||||||
#ifdef __MINGW_H
|
|
||||||
# include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <zlib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libxml/xmlreader.h>
|
|
||||||
#include <bzlib.h>
|
|
||||||
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
struct Input
|
|
||||||
{
|
|
||||||
char *name;
|
|
||||||
enum { plainFile, gzipFile, bzip2File } type;
|
|
||||||
void *fileHandle;
|
|
||||||
// needed by bzip2 when decompressing from multiple streams. other
|
|
||||||
// decompressors must ignore it.
|
|
||||||
FILE *systemHandle;
|
|
||||||
int eof;
|
|
||||||
char buf[4096];
|
|
||||||
int buf_ptr, buf_fill;
|
|
||||||
};
|
|
||||||
|
|
||||||
// tries to re-open the bz stream at the next stream start.
|
|
||||||
// returns 0 on success, -1 on failure.
|
|
||||||
int bzReOpen(struct Input *ctx, int *error)
|
|
||||||
{
|
|
||||||
// for copying out the last unused part of the block which
|
|
||||||
// has an EOS token in it. needed for re-initialising the
|
|
||||||
// next stream.
|
|
||||||
unsigned char unused[BZ_MAX_UNUSED];
|
|
||||||
void *unused_tmp_ptr = NULL;
|
|
||||||
int nUnused, i;
|
|
||||||
|
|
||||||
BZ2_bzReadGetUnused(error, (BZFILE *)(ctx->fileHandle), &unused_tmp_ptr, &nUnused);
|
|
||||||
if (*error != BZ_OK) return -1;
|
|
||||||
|
|
||||||
// when bzReadClose is called the unused buffer is deallocated,
|
|
||||||
// so it needs to be copied somewhere safe first.
|
|
||||||
for (i = 0; i < nUnused; ++i)
|
|
||||||
unused[i] = ((unsigned char *)unused_tmp_ptr)[i];
|
|
||||||
|
|
||||||
BZ2_bzReadClose(error, (BZFILE *)(ctx->fileHandle));
|
|
||||||
if (*error != BZ_OK) return -1;
|
|
||||||
|
|
||||||
// reassign the file handle
|
|
||||||
ctx->fileHandle = BZ2_bzReadOpen(error, ctx->systemHandle, 0, 0, unused, nUnused);
|
|
||||||
if (ctx->fileHandle == NULL || *error != BZ_OK) return -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int readFile(void *context, char * buffer, int len)
|
|
||||||
{
|
|
||||||
struct Input *ctx = context;
|
|
||||||
void *f = ctx->fileHandle;
|
|
||||||
int l = 0, error = 0;
|
|
||||||
|
|
||||||
if (ctx->eof || (len == 0))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
switch (ctx->type)
|
|
||||||
{
|
|
||||||
case plainFile:
|
|
||||||
l = read(*(int *)f, buffer, len);
|
|
||||||
if (l <= 0) ctx->eof = 1;
|
|
||||||
break;
|
|
||||||
case gzipFile:
|
|
||||||
l = gzread((gzFile)f, buffer, len);
|
|
||||||
if (l <= 0) ctx->eof = 1;
|
|
||||||
break;
|
|
||||||
case bzip2File:
|
|
||||||
l = BZ2_bzRead(&error, (BZFILE *)f, buffer, len);
|
|
||||||
|
|
||||||
// error codes BZ_OK and BZ_STREAM_END are both "OK", but the stream
|
|
||||||
// end means the reader needs to be reset from the original handle.
|
|
||||||
if (error != BZ_OK)
|
|
||||||
{
|
|
||||||
// for stream errors, try re-opening the stream before admitting defeat.
|
|
||||||
if (error != BZ_STREAM_END || bzReOpen(ctx, &error) != 0)
|
|
||||||
{
|
|
||||||
l = 0;
|
|
||||||
ctx->eof = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Bad file type\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (l < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "File reader received error %d (%d)\n", l, error);
|
|
||||||
l = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
char inputGetChar(void *context)
|
|
||||||
{
|
|
||||||
struct Input *ctx = context;
|
|
||||||
|
|
||||||
if (ctx->buf_ptr == ctx->buf_fill)
|
|
||||||
{
|
|
||||||
ctx->buf_fill = readFile(context, &ctx->buf[0], sizeof(ctx->buf));
|
|
||||||
ctx->buf_ptr = 0;
|
|
||||||
if (ctx->buf_fill == 0)
|
|
||||||
return 0;
|
|
||||||
if (ctx->buf_fill < 0)
|
|
||||||
{
|
|
||||||
perror("Error while reading file");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//readFile(context, &c, 1);
|
|
||||||
return ctx->buf[ctx->buf_ptr++];
|
|
||||||
}
|
|
||||||
|
|
||||||
int inputEof(void *context)
|
|
||||||
{
|
|
||||||
return ((struct Input *)context)->eof;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *inputOpen(const char *name)
|
|
||||||
{
|
|
||||||
const char *ext = strrchr(name, '.');
|
|
||||||
struct Input *ctx = malloc (sizeof(*ctx));
|
|
||||||
|
|
||||||
if (!ctx)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
memset(ctx, 0, sizeof(*ctx));
|
|
||||||
|
|
||||||
ctx->name = strdup(name);
|
|
||||||
|
|
||||||
if (ext && !strcmp(ext, ".gz"))
|
|
||||||
{
|
|
||||||
ctx->fileHandle = (void *)gzopen(name, "rb");
|
|
||||||
ctx->type = gzipFile;
|
|
||||||
}
|
|
||||||
else if (ext && !strcmp(ext, ".bz2"))
|
|
||||||
{
|
|
||||||
int error = 0;
|
|
||||||
ctx->systemHandle = fopen(name, "rb");
|
|
||||||
if (!ctx->systemHandle)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "error while opening file %s\n", name);
|
|
||||||
exit(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->fileHandle = (void *)BZ2_bzReadOpen(&error, ctx->systemHandle, 0, 0, NULL, 0);
|
|
||||||
ctx->type = bzip2File;
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int *pfd = malloc(sizeof(pfd));
|
|
||||||
if (pfd)
|
|
||||||
{
|
|
||||||
if (!strcmp(name, "-"))
|
|
||||||
{
|
|
||||||
*pfd = STDIN_FILENO;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int flags = O_RDONLY;
|
|
||||||
#ifdef O_LARGEFILE
|
|
||||||
flags |= O_LARGEFILE;
|
|
||||||
#endif
|
|
||||||
*pfd = open(name, flags);
|
|
||||||
if (*pfd < 0)
|
|
||||||
{
|
|
||||||
free(pfd);
|
|
||||||
pfd = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx->fileHandle = (void *)pfd;
|
|
||||||
ctx->type = plainFile;
|
|
||||||
}
|
|
||||||
if (!ctx->fileHandle)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "error while opening file %s\n", name);
|
|
||||||
exit(10);
|
|
||||||
}
|
|
||||||
ctx->buf_ptr = 0;
|
|
||||||
ctx->buf_fill = 0;
|
|
||||||
return (void *)ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
int inputClose(void *context)
|
|
||||||
{
|
|
||||||
struct Input *ctx = context;
|
|
||||||
void *f = ctx->fileHandle;
|
|
||||||
|
|
||||||
switch (ctx->type)
|
|
||||||
{
|
|
||||||
case plainFile:
|
|
||||||
close(*(int *)f);
|
|
||||||
free(f);
|
|
||||||
break;
|
|
||||||
case gzipFile:
|
|
||||||
gzclose((gzFile)f);
|
|
||||||
break;
|
|
||||||
case bzip2File:
|
|
||||||
BZ2_bzclose((BZFILE *)f);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Bad file type\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(ctx->name);
|
|
||||||
free(ctx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlTextReaderPtr inputUTF8(const char *name)
|
|
||||||
{
|
|
||||||
void *ctx = inputOpen(name);
|
|
||||||
|
|
||||||
if (!ctx)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Input reader create failed for: %s\n", name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return xmlReaderForIO(readFile, inputClose, (void *)ctx, NULL, NULL, 0);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#ifndef INPUT_H
|
|
||||||
#define INPUT_H
|
|
||||||
|
|
||||||
int readFile(void *context, char * buffer, int len);
|
|
||||||
int inputClose(void *context);
|
|
||||||
void *inputOpen(const char *name);
|
|
||||||
char inputGetChar(void *context);
|
|
||||||
int inputEof(void *context);
|
|
||||||
xmlTextReaderPtr inputUTF8(const char *name);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
/*
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# nominatim - [description]
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright 2010, Brian Quinion
|
|
||||||
# Based on osm2pgsql
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU General Public License
|
|
||||||
# as published by the Free Software Foundation; either version 2
|
|
||||||
# of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <libpq-fe.h>
|
|
||||||
|
|
||||||
#include "nominatim.h"
|
|
||||||
#include "postgresql.h"
|
|
||||||
#include "sprompt.h"
|
|
||||||
#include "index.h"
|
|
||||||
#include "export.h"
|
|
||||||
#include "import.h"
|
|
||||||
|
|
||||||
int verbose;
|
|
||||||
|
|
||||||
void exit_nicely(void)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error occurred, cleaning up\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void short_usage(char *arg0)
|
|
||||||
{
|
|
||||||
const char *name = basename(arg0);
|
|
||||||
|
|
||||||
fprintf(stderr, "Usage error. For further information see:\n");
|
|
||||||
fprintf(stderr, "\t%s -h|--help\n", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void long_usage(char *arg0)
|
|
||||||
{
|
|
||||||
const char *name = basename(arg0);
|
|
||||||
|
|
||||||
fprintf(stderr, "Usage:\n");
|
|
||||||
fprintf(stderr, "\t%s [options] planet.osms\n", name);
|
|
||||||
fprintf(stderr, "\nThis will import the structured osm data into a PostgreSQL database\n");
|
|
||||||
fprintf(stderr, "suitable for nominatim search engine\n");
|
|
||||||
fprintf(stderr, "\nOptions:\n");
|
|
||||||
fprintf(stderr, " -d|--database\tThe name of the PostgreSQL database to connect\n");
|
|
||||||
fprintf(stderr, " \tto (default: nominatim).\n");
|
|
||||||
fprintf(stderr, " -U|--username\tPostgresql user name.\n");
|
|
||||||
fprintf(stderr, " -W|--password\tForce password prompt.\n");
|
|
||||||
fprintf(stderr, " -H|--host\t\tDatabase server hostname or socket location.\n");
|
|
||||||
fprintf(stderr, " -P|--port\t\tDatabase server port.\n");
|
|
||||||
fprintf(stderr, " -i|--index\t\tIndex the database.\n");
|
|
||||||
fprintf(stderr, " -e|--export\t\tGenerate a structured file.\n");
|
|
||||||
fprintf(stderr, " -I|--import\t\tImport a structured file.\n");
|
|
||||||
fprintf(stderr, " -r|--minrank\t\tMinimum / starting rank. (default: 0))\n");
|
|
||||||
fprintf(stderr, " -R|--maxrank\t\tMaximum / finishing rank. (default: 30)\n");
|
|
||||||
fprintf(stderr, " -t|--threads\t\tNumber of threads to create for indexing.\n");
|
|
||||||
fprintf(stderr, " -F|--file\t\tfile to use (either to import or export).\n");
|
|
||||||
fprintf(stderr, " -T|--tagfile\t\tfile containing 'special' tag pairs\n");
|
|
||||||
fprintf(stderr, " \t(default: partitionedtags.def).\n");
|
|
||||||
fprintf(stderr, " -h|--help\t\tHelp information.\n");
|
|
||||||
fprintf(stderr, " -v|--verbose\t\tVerbose output.\n");
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
|
|
||||||
if (sizeof(int*) == 4)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n\nYou are running this on 32bit system - this will not work\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int long_usage_bool=0;
|
|
||||||
int pass_prompt=0;
|
|
||||||
const char *db = "nominatim";
|
|
||||||
const char *username=NULL;
|
|
||||||
const char *host=NULL;
|
|
||||||
const char *password=NULL;
|
|
||||||
const char *port = "5432";
|
|
||||||
const char *conninfo = NULL;
|
|
||||||
int index = 0;
|
|
||||||
int export = 0;
|
|
||||||
int import = 0;
|
|
||||||
int minrank = 0;
|
|
||||||
int maxrank = 30;
|
|
||||||
int threads = 1;
|
|
||||||
const char *file = NULL;
|
|
||||||
const char *tagsfile = "partitionedtags.def";
|
|
||||||
|
|
||||||
//import = 1;
|
|
||||||
//structuredinputfile = "out.osms";
|
|
||||||
|
|
||||||
PGconn *conn;
|
|
||||||
|
|
||||||
fprintf(stderr, "nominatim version %s\n\n", NOMINATIM_VERSION);
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
int c, option_index = 0;
|
|
||||||
static struct option long_options[] =
|
|
||||||
{
|
|
||||||
{"help", 0, 0, 'h'},
|
|
||||||
|
|
||||||
{"verbose", 0, 0, 'v'},
|
|
||||||
|
|
||||||
{"database", 1, 0, 'd'},
|
|
||||||
{"username", 1, 0, 'U'},
|
|
||||||
{"password", 0, 0, 'W'},
|
|
||||||
{"host", 1, 0, 'H'},
|
|
||||||
{"port", 1, 0, 'P'},
|
|
||||||
|
|
||||||
{"index", 0, 0, 'i'},
|
|
||||||
{"export", 0, 0, 'e'},
|
|
||||||
{"import", 1, 0, 'I'},
|
|
||||||
{"threads", 1, 0, 't'},
|
|
||||||
{"file", 1, 0, 'F'},
|
|
||||||
{"tagsfile", 1, 0, 'T'},
|
|
||||||
|
|
||||||
{"minrank", 1, 0, 'r'},
|
|
||||||
{"maxrank", 1, 0, 'R'},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "vhd:U:WH:P:ieIt:F:T:r:R:", long_options, &option_index);
|
|
||||||
if (c == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 'v':
|
|
||||||
verbose=1;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
db=optarg;
|
|
||||||
break;
|
|
||||||
case 'U':
|
|
||||||
username=optarg;
|
|
||||||
break;
|
|
||||||
case 'W':
|
|
||||||
pass_prompt=1;
|
|
||||||
break;
|
|
||||||
case 'H':
|
|
||||||
host=optarg;
|
|
||||||
break;
|
|
||||||
case 'P':
|
|
||||||
port=optarg;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
long_usage_bool=1;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
index=1;
|
|
||||||
break;
|
|
||||||
case 'e':
|
|
||||||
export=1;
|
|
||||||
break;
|
|
||||||
case 'I':
|
|
||||||
import=1;
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
threads=atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
minrank=atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'R':
|
|
||||||
maxrank=atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'F':
|
|
||||||
file=optarg;
|
|
||||||
break;
|
|
||||||
case 'T':
|
|
||||||
tagsfile=optarg;
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
default:
|
|
||||||
short_usage(argv[0]);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (long_usage_bool)
|
|
||||||
{
|
|
||||||
long_usage(argv[0]);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (threads < 1) threads = 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (argc == optind) { // No non-switch arguments
|
|
||||||
short_usage(argv[0]);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (index && import)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error: --index and --import options can not be used on the same database!\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pass_prompt)
|
|
||||||
password = simple_prompt("Password:", 100, 0);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
password = getenv("PGPASS");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test the database connection
|
|
||||||
conninfo = build_conninfo(db, username, password, host, port);
|
|
||||||
conn = PQconnectdb(conninfo);
|
|
||||||
if (PQstatus(conn) != CONNECTION_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQfinish(conn);
|
|
||||||
|
|
||||||
if (!index && !export && !import)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Please select index, export or import.\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (index) nominatim_index(minrank, maxrank, threads, conninfo, file);
|
|
||||||
if (export) nominatim_export(minrank, maxrank, conninfo, file);
|
|
||||||
if (import) nominatim_import(conninfo, tagsfile, file);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#ifndef NOMINATIM_H
|
|
||||||
#define NOMINATIM_H
|
|
||||||
|
|
||||||
#define MAX(x,y) (x > y?x:y)
|
|
||||||
#define MIN(x,y) (x < y?x:y)
|
|
||||||
|
|
||||||
struct output_options
|
|
||||||
{
|
|
||||||
const char *conninfo; /* Connection info string */
|
|
||||||
const char *prefix; /* prefix for table names */
|
|
||||||
int scale; /* scale for converting coordinates to fixed point */
|
|
||||||
int projection; /* SRS of projection */
|
|
||||||
int append; /* Append to existing data */
|
|
||||||
int slim; /* In slim mode */
|
|
||||||
int cache; /* Memory usable for cache in MB */
|
|
||||||
struct middle_t *mid; /* Mid storage to use */
|
|
||||||
const char *tblsindex; /* Pg Tablespace to store indexes */
|
|
||||||
const char *style; /* style file to use */
|
|
||||||
int expire_tiles_zoom; /* Zoom level for tile expiry list */
|
|
||||||
int expire_tiles_zoom_min; /* Minimum zoom level for tile expiry list */
|
|
||||||
const char *expire_tiles_filename; /* File name to output expired tiles list to */
|
|
||||||
int enable_hstore; /* add an additional hstore column with objects key/value pairs */
|
|
||||||
int enable_multi; /* Output multi-geometries instead of several simple geometries */
|
|
||||||
char** hstore_columns; /* list of columns that should be written into their own hstore column */
|
|
||||||
int n_hstore_columns; /* number of hstore columns */
|
|
||||||
};
|
|
||||||
|
|
||||||
void exit_nicely(void);
|
|
||||||
void short_usage(char *arg0);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
2
nominatim/nominatim.py
Normal file → Executable file
2
nominatim/nominatim.py
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
#! /usr/bin/env python
|
#! /usr/bin/env python3
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
# nominatim - [description]
|
# nominatim - [description]
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
|
|
||||||
%define svn @SVN@
|
|
||||||
|
|
||||||
Summary: Nominatim OpenStreetMap geocoding database
|
|
||||||
Name: @PACKAGE@
|
|
||||||
Group: Applications/Text
|
|
||||||
Version: @VERSION@
|
|
||||||
Release: 1.%{svn}%{?dist}
|
|
||||||
|
|
||||||
License: GPL
|
|
||||||
URL: http://svn.openstreetmap.org/applications/utils/nominatim
|
|
||||||
Source0: %{name}-%{version}-%{svn}.tar.bz2
|
|
||||||
Source1: nominatim-svn.sh
|
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
|
||||||
|
|
||||||
BuildRequires: geos-devel
|
|
||||||
BuildRequires: libxml2-devel
|
|
||||||
BuildRequires: postgresql-devel
|
|
||||||
BuildRequires: bzip2-devel
|
|
||||||
BuildRequires: proj-devel
|
|
||||||
|
|
||||||
%description
|
|
||||||
Processes data imported using osm2pgsql from the communtiy mapping project
|
|
||||||
at http://www.openstreetmap.org.
|
|
||||||
|
|
||||||
%prep
|
|
||||||
%setup -q -n %{name}
|
|
||||||
|
|
||||||
|
|
||||||
%build
|
|
||||||
|
|
||||||
export CFLAGS="$RPM_OPT_FLAGS"
|
|
||||||
export CXXFLAGS="$RPM_OPT_FLAGS"
|
|
||||||
|
|
||||||
make all
|
|
||||||
|
|
||||||
|
|
||||||
%install
|
|
||||||
rm -rf $RPM_BUILD_ROOT
|
|
||||||
install -D -p nominatim $RPM_BUILD_ROOT/usr/bin/nominatim
|
|
||||||
|
|
||||||
|
|
||||||
%clean
|
|
||||||
rm -rf $RPM_BUILD_ROOT
|
|
||||||
|
|
||||||
|
|
||||||
%files
|
|
||||||
%defattr(-,root,root)
|
|
||||||
%doc README.txt
|
|
||||||
%{_bindir}/nominatim
|
|
||||||
|
|
||||||
|
|
||||||
%changelog
|
|
||||||
* Fri Sep 09 2010 Brian Quinion <nominatim@brian.quinion.co.uk> 0.1-1.20070316svn
|
|
||||||
- Initial build
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
*/
|
|
||||||
#include <string.h>
|
|
||||||
#include "postgresql.h"
|
|
||||||
|
|
||||||
const char *build_conninfo(const char *db, const char *username, const char *password, const char *host, const char *port)
|
|
||||||
{
|
|
||||||
static char conninfo[1024];
|
|
||||||
|
|
||||||
conninfo[0]='\0';
|
|
||||||
strcat(conninfo, "dbname='");
|
|
||||||
strcat(conninfo, db);
|
|
||||||
strcat(conninfo, "'");
|
|
||||||
|
|
||||||
if (username)
|
|
||||||
{
|
|
||||||
strcat(conninfo, " user='");
|
|
||||||
strcat(conninfo, username);
|
|
||||||
strcat(conninfo, "'");
|
|
||||||
}
|
|
||||||
if (password)
|
|
||||||
{
|
|
||||||
strcat(conninfo, " password='");
|
|
||||||
strcat(conninfo, password);
|
|
||||||
strcat(conninfo, "'");
|
|
||||||
}
|
|
||||||
if (host)
|
|
||||||
{
|
|
||||||
strcat(conninfo, " host='");
|
|
||||||
strcat(conninfo, host);
|
|
||||||
strcat(conninfo, "'");
|
|
||||||
}
|
|
||||||
if (port)
|
|
||||||
{
|
|
||||||
strcat(conninfo, " port='");
|
|
||||||
strcat(conninfo, port);
|
|
||||||
strcat(conninfo, "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
return conninfo;
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef POSTGRESQL_H
|
|
||||||
#define POSTGRESQL_H
|
|
||||||
|
|
||||||
#define PG_OID_INT8 20
|
|
||||||
#define PG_OID_INT4 23
|
|
||||||
|
|
||||||
#if HAVE_BYTESWAP
|
|
||||||
#include <byteswap.h>
|
|
||||||
#define PG_BSWAP32(x) bswap_32(x)
|
|
||||||
#define PG_BSWAP64(x) bswap_64(x)
|
|
||||||
#elif HAVE_SYS_ENDIAN
|
|
||||||
#include <sys/endian.h>
|
|
||||||
#define PG_BSWAP32(x) bswap32(x)
|
|
||||||
#define PG_BSWAP64(x) bswap64(x)
|
|
||||||
#else
|
|
||||||
#error "No appropriate byteswap found for your system."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
|
||||||
#define PGint32(x) (x)
|
|
||||||
#define PGint64(x) (x)
|
|
||||||
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
|
||||||
#define PGint32(x) PG_BSWAP32(x)
|
|
||||||
#define PGint64(x) PG_BSWAP64(x)
|
|
||||||
#elif defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)
|
|
||||||
#define PGint32(x) (x)
|
|
||||||
#define PGint64(x) (x)
|
|
||||||
#elif defined(_BYTE_ORDER) && (_BYTE_ORDER == _LITTLE_ENDIAN)
|
|
||||||
#define PGint32(x) PG_BSWAP32(x)
|
|
||||||
#define PGint64(x) PG_BSWAP64(x)
|
|
||||||
#else
|
|
||||||
#error "Cannot determine byte order."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *build_conninfo(const char *db, const char *username, const char *password, const char *host, const char *port);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* sprompt.c
|
|
||||||
* simple_prompt() routine
|
|
||||||
*
|
|
||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* IDENTIFICATION
|
|
||||||
* $PostgreSQL: pgsql/src/port/sprompt.c,v 1.18 2006/10/04 00:30:14 momjian Exp $
|
|
||||||
*
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* PostgreSQL Database Management System
|
|
||||||
* (formerly known as Postgres, then as Postgres95)
|
|
||||||
*
|
|
||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
|
||||||
*
|
|
||||||
* Portions Copyright (c) 1994, The Regents of the University of California
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software and its
|
|
||||||
* documentation for any purpose, without fee, and without a written agreement
|
|
||||||
* is hereby granted, provided that the above copyright notice and this
|
|
||||||
* paragraph and the following two paragraphs appear in all copies.
|
|
||||||
*
|
|
||||||
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
|
||||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
|
|
||||||
* LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
|
|
||||||
* DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
|
||||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
|
||||||
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
|
|
||||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* simple_prompt
|
|
||||||
*
|
|
||||||
* Generalized function especially intended for reading in usernames and
|
|
||||||
* password interactively. Reads from /dev/tty or stdin/stderr.
|
|
||||||
*
|
|
||||||
* prompt: The prompt to print
|
|
||||||
* maxlen: How many characters to accept
|
|
||||||
* echo: Set to false if you want to hide what is entered (for passwords)
|
|
||||||
*
|
|
||||||
* Returns a malloc()'ed string with the input (w/o trailing newline).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define DEVTTY "/dev/tty"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <libpq-fe.h>
|
|
||||||
|
|
||||||
#ifdef __MINGW_H
|
|
||||||
# include <windows.h>
|
|
||||||
#else
|
|
||||||
# define HAVE_TERMIOS_H
|
|
||||||
# include <termios.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
extern char *simple_prompt(const char *prompt, int maxlen, int echo);
|
|
||||||
*/
|
|
||||||
|
|
||||||
char *
|
|
||||||
simple_prompt(const char *prompt, int maxlen, int echo)
|
|
||||||
{
|
|
||||||
int length;
|
|
||||||
char *destination;
|
|
||||||
FILE *termin,
|
|
||||||
*termout;
|
|
||||||
|
|
||||||
#ifdef HAVE_TERMIOS_H
|
|
||||||
struct termios t_orig,
|
|
||||||
t;
|
|
||||||
#else
|
|
||||||
#ifdef WIN32
|
|
||||||
HANDLE t = NULL;
|
|
||||||
LPDWORD t_orig = NULL;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
destination = (char *) malloc(maxlen + 1);
|
|
||||||
if (!destination)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Do not try to collapse these into one "w+" mode file. Doesn't work on
|
|
||||||
* some platforms (eg, HPUX 10.20).
|
|
||||||
*/
|
|
||||||
termin = fopen(DEVTTY, "r");
|
|
||||||
termout = fopen(DEVTTY, "w");
|
|
||||||
if (!termin || !termout
|
|
||||||
#ifdef WIN32
|
|
||||||
/* See DEVTTY comment for msys */
|
|
||||||
|| (getenv("OSTYPE") && strcmp(getenv("OSTYPE"), "msys") == 0)
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (termin)
|
|
||||||
fclose(termin);
|
|
||||||
if (termout)
|
|
||||||
fclose(termout);
|
|
||||||
termin = stdin;
|
|
||||||
termout = stderr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_TERMIOS_H
|
|
||||||
if (!echo)
|
|
||||||
{
|
|
||||||
tcgetattr(fileno(termin), &t);
|
|
||||||
t_orig = t;
|
|
||||||
t.c_lflag &= ~ECHO;
|
|
||||||
tcsetattr(fileno(termin), TCSAFLUSH, &t);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#ifdef WIN32
|
|
||||||
if (!echo)
|
|
||||||
{
|
|
||||||
/* get a new handle to turn echo off */
|
|
||||||
t_orig = (LPDWORD) malloc(sizeof(DWORD));
|
|
||||||
t = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
|
|
||||||
/* save the old configuration first */
|
|
||||||
GetConsoleMode(t, t_orig);
|
|
||||||
|
|
||||||
/* set to the new mode */
|
|
||||||
SetConsoleMode(t, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (prompt)
|
|
||||||
{
|
|
||||||
fputs(prompt, termout);
|
|
||||||
fflush(termout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fgets(destination, maxlen + 1, termin) == NULL)
|
|
||||||
destination[0] = '\0';
|
|
||||||
|
|
||||||
length = strlen(destination);
|
|
||||||
if (length > 0 && destination[length - 1] != '\n')
|
|
||||||
{
|
|
||||||
/* eat rest of the line */
|
|
||||||
char buf[128];
|
|
||||||
int buflen;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (fgets(buf, sizeof(buf), termin) == NULL)
|
|
||||||
break;
|
|
||||||
buflen = strlen(buf);
|
|
||||||
}
|
|
||||||
while (buflen > 0 && buf[buflen - 1] != '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length > 0 && destination[length - 1] == '\n')
|
|
||||||
/* remove trailing newline */
|
|
||||||
destination[length - 1] = '\0';
|
|
||||||
|
|
||||||
#ifdef HAVE_TERMIOS_H
|
|
||||||
if (!echo)
|
|
||||||
{
|
|
||||||
tcsetattr(fileno(termin), TCSAFLUSH, &t_orig);
|
|
||||||
fputs("\n", termout);
|
|
||||||
fflush(termout);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#ifdef WIN32
|
|
||||||
if (!echo)
|
|
||||||
{
|
|
||||||
/* reset to the original console mode */
|
|
||||||
SetConsoleMode(t, *t_orig);
|
|
||||||
fputs("\n", termout);
|
|
||||||
fflush(termout);
|
|
||||||
free(t_orig);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (termin != stdin)
|
|
||||||
{
|
|
||||||
fclose(termin);
|
|
||||||
fclose(termout);
|
|
||||||
}
|
|
||||||
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#ifndef SPROMPT_H
|
|
||||||
#define SPROMPT_H
|
|
||||||
char *simple_prompt(const char *prompt, int maxlen, int echo);
|
|
||||||
#endif
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,281 +0,0 @@
|
|||||||
/*
|
|
||||||
* The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T
|
|
||||||
* Bell Laboratories.
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose without fee is hereby granted, provided that this entire notice
|
|
||||||
* is included in all copies of any software which is or includes a copy
|
|
||||||
* or modification of this software and in all copies of the supporting
|
|
||||||
* documentation for such software.
|
|
||||||
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
|
||||||
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
|
|
||||||
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
|
||||||
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This code was originally written by Stephan Fortune in C code. I, Shane O'Sullivan,
|
|
||||||
* have since modified it, encapsulating it in a C++ class and, fixing memory leaks and
|
|
||||||
* adding accessors to the Voronoi Edges.
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose without fee is hereby granted, provided that this entire notice
|
|
||||||
* is included in all copies of any software which is or includes a copy
|
|
||||||
* or modification of this software and in all copies of the supporting
|
|
||||||
* documentation for such software.
|
|
||||||
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
|
||||||
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
|
|
||||||
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
|
||||||
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef VORONOI_DIAGRAM_GENERATOR
|
|
||||||
#define VORONOI_DIAGRAM_GENERATOR
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef NULL
|
|
||||||
#define NULL 0
|
|
||||||
#endif
|
|
||||||
#define DELETED -2
|
|
||||||
|
|
||||||
#define le 0
|
|
||||||
#define re 1
|
|
||||||
|
|
||||||
struct SourcePoint
|
|
||||||
{
|
|
||||||
int id;
|
|
||||||
double weight;
|
|
||||||
double x;
|
|
||||||
double y;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Freenode
|
|
||||||
{
|
|
||||||
struct Freenode *nextfree;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FreeNodeArrayList
|
|
||||||
{
|
|
||||||
struct Freenode* memory;
|
|
||||||
struct FreeNodeArrayList* next;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Freelist
|
|
||||||
{
|
|
||||||
struct Freenode *head;
|
|
||||||
int nodesize;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Point
|
|
||||||
{
|
|
||||||
float x,y;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PolygonPoint
|
|
||||||
{
|
|
||||||
struct Point coord;
|
|
||||||
double angle;
|
|
||||||
int boundary;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Polygon
|
|
||||||
{
|
|
||||||
int sitenbr;
|
|
||||||
struct Point coord;
|
|
||||||
int numpoints;
|
|
||||||
struct PolygonPoint * pointlist;
|
|
||||||
int boundary;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// structure used both for sites and for vertices
|
|
||||||
struct Site
|
|
||||||
{
|
|
||||||
struct Point coord;
|
|
||||||
struct Point coordout;
|
|
||||||
double weight;
|
|
||||||
int sitenbr;
|
|
||||||
int refcnt;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct Edge
|
|
||||||
{
|
|
||||||
float a,b,c;
|
|
||||||
struct Site *ep[2];
|
|
||||||
struct Site *reg[2];
|
|
||||||
int edgenbr;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GraphEdge
|
|
||||||
{
|
|
||||||
float x1,y1,x2,y2;
|
|
||||||
struct GraphEdge* next;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct Halfedge
|
|
||||||
{
|
|
||||||
struct Halfedge *ELleft, *ELright;
|
|
||||||
struct Edge *ELedge;
|
|
||||||
int ELrefcnt;
|
|
||||||
char ELpm;
|
|
||||||
struct Site *vertex;
|
|
||||||
float ystar;
|
|
||||||
struct Halfedge *PQnext;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class VoronoiDiagramGenerator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VoronoiDiagramGenerator();
|
|
||||||
~VoronoiDiagramGenerator();
|
|
||||||
|
|
||||||
bool generateVoronoi(struct SourcePoint* srcPoints, int numPoints, float minX, float maxX, float minY, float maxY, float minDist=0);
|
|
||||||
void getSitePoints(int sitenbr, int* numpoints, PolygonPoint** pS);
|
|
||||||
|
|
||||||
void resetIterator()
|
|
||||||
{
|
|
||||||
iteratorEdges = allEdges;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getNext(float& x1, float& y1, float& x2, float& y2)
|
|
||||||
{
|
|
||||||
if(iteratorEdges == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
x1 = iteratorEdges->x1;
|
|
||||||
x2 = iteratorEdges->x2;
|
|
||||||
y1 = iteratorEdges->y1;
|
|
||||||
y2 = iteratorEdges->y2;
|
|
||||||
|
|
||||||
iteratorEdges = iteratorEdges->next;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
void cleanup();
|
|
||||||
void cleanupEdges();
|
|
||||||
char *getfree(struct Freelist *fl);
|
|
||||||
struct Halfedge *PQfind();
|
|
||||||
int PQempty();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct Halfedge **ELhash;
|
|
||||||
struct Halfedge *HEcreate(), *ELleft(), *ELright(), *ELleftbnd();
|
|
||||||
struct Halfedge *HEcreate(struct Edge *e,int pm);
|
|
||||||
|
|
||||||
|
|
||||||
struct Point PQ_min();
|
|
||||||
struct Halfedge *PQextractmin();
|
|
||||||
void freeinit(struct Freelist *fl,int size);
|
|
||||||
void makefree(struct Freenode *curr,struct Freelist *fl);
|
|
||||||
void geominit();
|
|
||||||
void plotinit();
|
|
||||||
bool voronoi(int triangulate);
|
|
||||||
void ref(struct Site *v);
|
|
||||||
void deref(struct Site *v);
|
|
||||||
void endpoint(struct Edge *e,int lr,struct Site * s);
|
|
||||||
void endpoint(struct Edge *e1,int lr,struct Site * s, struct Edge *e2, struct Edge *e3);
|
|
||||||
|
|
||||||
void ELdelete(struct Halfedge *he);
|
|
||||||
struct Halfedge *ELleftbnd(struct Point *p);
|
|
||||||
struct Halfedge *ELright(struct Halfedge *he);
|
|
||||||
void makevertex(struct Site *v);
|
|
||||||
void out_triple(struct Site *s1, struct Site *s2,struct Site * s3);
|
|
||||||
|
|
||||||
void PQinsert(struct Halfedge *he,struct Site * v, float offset);
|
|
||||||
void PQdelete(struct Halfedge *he);
|
|
||||||
bool ELinitialize();
|
|
||||||
void ELinsert(struct Halfedge *lb, struct Halfedge *newHe);
|
|
||||||
struct Halfedge * ELgethash(int b);
|
|
||||||
struct Halfedge *ELleft(struct Halfedge *he);
|
|
||||||
struct Site *leftreg(struct Halfedge *he);
|
|
||||||
void out_site(struct Site *s);
|
|
||||||
bool PQinitialize();
|
|
||||||
int PQbucket(struct Halfedge *he);
|
|
||||||
void pushpoint(int sitenbr, double x, double y, int boundary);
|
|
||||||
int ccw( Point p0, Point p1, Point p2 );
|
|
||||||
void clip_line(struct Edge *e);
|
|
||||||
char *myalloc(unsigned n);
|
|
||||||
int right_of(struct Halfedge *el,struct Point *p);
|
|
||||||
|
|
||||||
struct Site *rightreg(struct Halfedge *he);
|
|
||||||
struct Edge *bisect(struct Site *s1,struct Site *s2);
|
|
||||||
float dist(struct Site *s,struct Site *t);
|
|
||||||
struct Site *intersect(struct Halfedge *el1, struct Halfedge *el2, struct Point *p=0);
|
|
||||||
|
|
||||||
void out_bisector(struct Edge *e);
|
|
||||||
void out_ep(struct Edge *e);
|
|
||||||
void out_vertex(struct Site *v);
|
|
||||||
struct Site *nextone();
|
|
||||||
|
|
||||||
void pushGraphEdge(float x1, float y1, float x2, float y2);
|
|
||||||
|
|
||||||
void openpl();
|
|
||||||
void line(float x1, float y1, float x2, float y2);
|
|
||||||
void circle(float x, float y, float radius);
|
|
||||||
void range(float minX, float minY, float maxX, float maxY);
|
|
||||||
|
|
||||||
|
|
||||||
struct Freelist hfl;
|
|
||||||
struct Halfedge *ELleftend, *ELrightend;
|
|
||||||
int ELhashsize;
|
|
||||||
|
|
||||||
int triangulate, sorted, plot, debug;
|
|
||||||
float xmin, xmax, ymin, ymax, deltax, deltay;
|
|
||||||
|
|
||||||
struct Site *sites;
|
|
||||||
struct Polygon *polygons;
|
|
||||||
struct Point corners[4];
|
|
||||||
int nsites;
|
|
||||||
int siteidx;
|
|
||||||
int sqrt_nsites;
|
|
||||||
int nvertices;
|
|
||||||
struct Freelist sfl;
|
|
||||||
struct Site *bottomsite;
|
|
||||||
|
|
||||||
int nedges;
|
|
||||||
struct Freelist efl;
|
|
||||||
int PQhashsize;
|
|
||||||
struct Halfedge *PQhash;
|
|
||||||
int PQcount;
|
|
||||||
int PQmin;
|
|
||||||
|
|
||||||
int ntry, totalsearch;
|
|
||||||
float pxmin, pxmax, pymin, pymax, cradius;
|
|
||||||
int total_alloc;
|
|
||||||
|
|
||||||
float borderMinX, borderMaxX, borderMinY, borderMaxY;
|
|
||||||
|
|
||||||
FreeNodeArrayList* allMemoryList;
|
|
||||||
FreeNodeArrayList* currentMemoryBlock;
|
|
||||||
|
|
||||||
GraphEdge* allEdges;
|
|
||||||
GraphEdge* iteratorEdges;
|
|
||||||
|
|
||||||
float minDistanceBetweenSites;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
int scomp(const void *p1,const void *p2);
|
|
||||||
int spcomp(const void *p1,const void *p2);
|
|
||||||
int anglecomp(const void * p1, const void * p2);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* The author of this software is Shane O'Sullivan.
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose without fee is hereby granted, provided that this entire notice
|
|
||||||
* is included in all copies of any software which is or includes a copy
|
|
||||||
* or modification of this software and in all copies of the supporting
|
|
||||||
* documentation for such software.
|
|
||||||
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
|
||||||
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
|
|
||||||
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
|
||||||
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <search.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include "VoronoiDiagramGenerator.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
double xmin, xmax, ymin, ymax;
|
|
||||||
scanf("%lf %lf %lf %lf", &xmin, &xmax, &ymin, &ymax) ;
|
|
||||||
|
|
||||||
SourcePoint * sites;
|
|
||||||
long nsites;
|
|
||||||
|
|
||||||
nsites = 0;
|
|
||||||
sites = (SourcePoint *) malloc(4000 * sizeof(SourcePoint));
|
|
||||||
while (scanf("%d %lf %lf %lf", &sites[nsites].id, &sites[nsites].weight, &sites[nsites].x, &sites[nsites].y) != EOF)
|
|
||||||
{
|
|
||||||
nsites++;
|
|
||||||
if (nsites % 4000 == 0) {
|
|
||||||
sites = (SourcePoint *)realloc(sites,(nsites+4000)*sizeof(SourcePoint));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VoronoiDiagramGenerator * pvdg;
|
|
||||||
pvdg = new VoronoiDiagramGenerator();
|
|
||||||
pvdg->generateVoronoi(sites, nsites, xmin, xmax, ymin, ymax, 0);
|
|
||||||
|
|
||||||
// printf("sites %ld\n-------------------------------\n", nsites);
|
|
||||||
PolygonPoint* pSitePoints;
|
|
||||||
int numpoints, i, j;
|
|
||||||
for(i = 0; i < nsites; i++)
|
|
||||||
{
|
|
||||||
pvdg->getSitePoints(i, &numpoints, &pSitePoints);
|
|
||||||
if (numpoints == 0)
|
|
||||||
{
|
|
||||||
printf("-- no points for %d\n", i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
printf("update temp_child_4076440_0 set resultgeom = st_setsrid('POLYGON((");
|
|
||||||
for(j = 0; j < numpoints; j++)
|
|
||||||
{
|
|
||||||
printf("%.15lf %.15lf,", pSitePoints[j].coord.x, pSitePoints[j].coord.y, (pSitePoints[j].angle/M_PI)*180);
|
|
||||||
}
|
|
||||||
printf("%.15lf %.15lf", pSitePoints[0].coord.x, pSitePoints[0].coord.y, (pSitePoints[j].angle/M_PI)*180);
|
|
||||||
printf("))'::geometry,4326) where id = %d;\n", sites[i].id);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float x1,y1,x2,y2;
|
|
||||||
// printf("sites %ld\n-------------------------------\n", nsites);
|
|
||||||
pvdg->resetIterator();
|
|
||||||
while(pvdg->getNext(x1,y1,x2,y2))
|
|
||||||
{
|
|
||||||
printf("(%f %f,%f %f)\n",x1,y1,x2, y2);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
delete pvdg;
|
|
||||||
free(sites);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user