postcode/zipcode improvements, finish work on handling extratags

This commit is contained in:
Brian Quinion
2010-12-07 13:41:02 +00:00
parent 491f5961e1
commit 631e8d09ab
11 changed files with 378 additions and 121 deletions

View File

@@ -7,7 +7,9 @@
$oDB =& DB::connect(CONST_Database_DSN.($bNew?'?new_link=true':''), false);
if (PEAR::IsError($oDB))
{
fail($oDB->getMessage(), 'Unable to connect to the database');
var_dump(CONST_Database_DSN);
var_Dump($oDB);
fail($oDB->getMessage());
}
$oDB->setFetchMode(DB_FETCHMODE_ASSOC);
$oDB->query("SET DateStyle TO 'sql,european'");

View File

@@ -697,12 +697,8 @@
$sSQL .= " length(name::text) as namelength ";
$sSQL .= " from place_addressline join placex on (address_place_id = placex.place_id)";
$sSQL .= " where place_addressline.place_id = $iPlaceID and (rank_address > 0 OR address_place_id = $iPlaceID)";
// and isaddress";
if ($sCountryCode)
{
$sSQL .= " and (placex.country_code IS NULL OR placex.country_code = '".$sCountryCode."' OR rank_address < 4)";
}
$sSQL .= " order by cached_rank_address desc,fromarea desc,distance asc,rank_search desc,namelength desc";
$sSQL .= " order by cached_rank_address desc,isaddress desc,fromarea desc,distance asc,rank_search desc,namelength
desc";
//var_dump($sSQL);
$aAddressLines = $oDB->getAll($sSQL);
if (PEAR::IsError($aAddressLines))

View File

@@ -31,8 +31,8 @@ body {
float: right;
}
</style>
<script src="OpenLayers.js"></script>
<script src="http://www.openstreetmap.org/openlayers/OpenStreetMap.js"></script>
<script src="js/OpenLayers.js"></script>
<script src="js/OpenStreetMap.js"></script>
<script src="prototype-1.6.0.3.js"></script>
<script type="text/javascript">
@@ -111,6 +111,12 @@ foreach($aPolyPoints as $aPolyPoint)
echo ' <div>Coverage: <span class="area">'.($aPointDetails['isarea']=='t'?'Polygon':'Point').'</span></div>';
$sOSMType = ($aPointDetails['osm_type'] == 'N'?'node':($aPointDetails['osm_type'] == 'W'?'way':($aPointDetails['osm_type'] == 'R'?'relation':'')));
if ($sOSMType) echo ' <div>OSM: <span class="osm"><span class="label"></span>'.$sOSMType.' <a href="http://www.openstreetmap.org/browse/'.$sOSMType.'/'.$aPointDetails['osm_id'].'">'.$aPointDetails['osm_id'].'</a></span></div>';
echo ' <div>Extra Tags: ';
foreach($aPointDetails['aExtraTags'] as $sKey => $sValue)
{
echo ' <div class="line"><span class="name">'.$sValue.'</span> ('.$sKey.')</div>';
}
echo ' </div>';
echo '</div>';
echo '<h2>Address</h2>';

View File

@@ -51,7 +51,7 @@ void nominatim_export(int rank_min, int rank_max, const char *conninfo, const ch
pg_prepare_params[0] = PG_OID_INT4;
res = PQprepare(conn, "index_sectors",
"select geometry_sector,count(*) from placex where rank_search = $1 and indexed = true group by geometry_sector order by geometry_sector",
"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);
@@ -118,7 +118,7 @@ void nominatim_export(int rank_min, int rank_max, const char *conninfo, const ch
PQclear(resPlaces);
exit(EXIT_FAILURE);
}
if (PQftype(resPlaces, 0) != PG_OID_INT8)
if (PQftype(resPlaces, 0) != PG_OID_INT4)
{
fprintf(stderr, "Place_id value has unexpected type\n");
PQclear(resPlaces);
@@ -128,7 +128,7 @@ void nominatim_export(int rank_min, int rank_max, const char *conninfo, const ch
tuples = PQntuples(resPlaces);
for(i = 0; i < tuples; i++)
{
nominatim_exportPlace(PGint64(*((uint64_t *)PQgetvalue(resPlaces, i, 0))), conn, writer, NULL);
nominatim_exportPlace(PGint32(*((uint32_t *)PQgetvalue(resPlaces, i, 0))), conn, writer, NULL);
rankTotalDone++;
if (rankTotalDone%1000 == 0) printf("Done %i (k)\n", rankTotalDone/1000);
}
@@ -160,7 +160,7 @@ void nominatim_exportCreatePreparedQueries(PGconn * conn)
pg_prepare_params[0] = PG_OID_INT8;
res = PQprepare(conn, "placex_address",
"select osm_type,osm_id,class,type,distance,cached_rank_address from place_addressline join placex on (address_place_id = placex.place_id) where isaddress and place_addressline.place_id = $1 and address_place_id != place_addressline.place_id order by cached_rank_address asc",
"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",
1, pg_prepare_params);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
@@ -171,7 +171,7 @@ void nominatim_exportCreatePreparedQueries(PGconn * conn)
pg_prepare_params[0] = PG_OID_INT8;
res = PQprepare(conn, "placex_names",
"select (each(name)).key,(each(name)).value from (select name as name from placex where place_id = $1) as x",
"select (each(name)).key,(each(name)).value from (select name from placex where place_id = $1) as x",
1, pg_prepare_params);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
@@ -179,6 +179,17 @@ void nominatim_exportCreatePreparedQueries(PGconn * 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",
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)
@@ -251,6 +262,7 @@ void nominatim_exportPlace(uint64_t place_id, PGconn * conn, xmlTextWriterPtr wr
PGresult * res;
PGresult * resNames;
PGresult * resAddress;
PGresult * resExtraTags;
int i;
@@ -289,6 +301,14 @@ void nominatim_exportPlace(uint64_t place_id, PGconn * conn, xmlTextWriterPtr wr
exit(EXIT_FAILURE);
}
resExtraTags = PQexecPrepared(conn, "placex_extratags", 1, paramValues, paramLengths, paramFormats, 0);
if (PQresultStatus(resExtraTags) != PGRES_TUPLES_OK)
{
fprintf(stderr, "placex_extratags: SELECT failed: %s", PQerrorMessage(conn));
PQclear(resExtraTags);
exit(EXIT_FAILURE);
}
if (writer_mutex) pthread_mutex_lock( writer_mutex );
xmlTextWriterStartElement(writer, BAD_CAST "feature");
@@ -300,7 +320,7 @@ void nominatim_exportPlace(uint64_t place_id, PGconn * conn, xmlTextWriterPtr wr
xmlTextWriterWriteAttribute(writer, BAD_CAST "rank", BAD_CAST PQgetvalue(res, 0, 9));
xmlTextWriterWriteAttribute(writer, BAD_CAST "importance", BAD_CAST PQgetvalue(res, 0, 10));
if (PQgetvalue(res, 0, 4) && strlen(PQgetvalue(res, 0, 4)))
if (PQntuples(resNames))
{
xmlTextWriterStartElement(writer, BAD_CAST "names");
@@ -348,11 +368,28 @@ void nominatim_exportPlace(uint64_t place_id, PGconn * conn, xmlTextWriterPtr wr
xmlTextWriterWriteAttribute(writer, BAD_CAST "key", BAD_CAST PQgetvalue(resAddress, i, 2));
xmlTextWriterWriteAttribute(writer, BAD_CAST "value", BAD_CAST PQgetvalue(resAddress, i, 3));
xmlTextWriterWriteAttribute(writer, BAD_CAST "distance", BAD_CAST PQgetvalue(resAddress, i, 4));
xmlTextWriterWriteAttribute(writer, BAD_CAST "isaddress", BAD_CAST PQgetvalue(resAddress, i, 6));
xmlTextWriterEndElement(writer);
}
xmlTextWriterEndElement(writer);
}
if (PQntuples(resExtraTags))
{
xmlTextWriterStartElement(writer, BAD_CAST "tags");
for(i = 0; i < PQntuples(resExtraTags); i++)
{
xmlTextWriterStartElement(writer, BAD_CAST "tag");
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(resExtraTags, i, 0));
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(resExtraTags, i, 1));
xmlTextWriterEndElement(writer);
}
xmlTextWriterEndElement(writer);
}
xmlTextWriterStartElement(writer, BAD_CAST "osmGeometry");
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(res, 0, 7));
xmlTextWriterEndElement(writer);

View File

@@ -18,10 +18,14 @@ typedef enum { FILEMODE_NONE, FILEMODE_ADD, FILEMODE_UPDATE, FILEMODE_DELETE } f
#define MAX_FEATUREADDRESS 500
#define MAX_FEATURENAMES 1000
#define MAX_FEATUREEXTRATAGS 100
#define MAX_FEATURENAMESTRING 100000
#define MAX_FEATUREEXTRATAGSTRING 50000
struct feature_address {
int place_id;
int rankAddress;
char isAddress[2];
xmlChar * type;
xmlChar * id;
xmlChar * key;
@@ -29,7 +33,7 @@ struct feature_address {
xmlChar * distance;
};
struct feature_name {
struct feature_tag {
xmlChar * type;
xmlChar * value;
};
@@ -52,14 +56,16 @@ int fileType = FILETYPE_NONE;
int fileMode = FILEMODE_ADD;
PGconn * conn;
struct feature_address featureAddress[MAX_FEATUREADDRESS];
struct feature_name featureName[MAX_FEATURENAMES];
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;
char featureNameString[MAX_FEATURENAMESTRING];
char featureExtraTagString[MAX_FEATUREEXTRATAGSTRING];
void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
{
@@ -147,6 +153,19 @@ void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
}
return;
}
if (xmlStrEqual(name, BAD_CAST "tags")) return;
if (xmlStrEqual(name, BAD_CAST "tag"))
{
featureExtraTag[featureExtraTagLines].type = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
featureExtraTag[featureExtraTagLines].value = xmlTextReaderReadString(reader);
featureExtraTagLines++;
if (featureExtraTagLines >= MAX_FEATUREEXTRATAGS)
{
fprintf( stderr, "Too many extra tag elements\n");
exit_nicely();
}
return;
}
if (xmlStrEqual(name, BAD_CAST "osmGeometry"))
{
feature.geometry = xmlTextReaderReadString(reader);
@@ -248,6 +267,16 @@ void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
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");
@@ -273,7 +302,7 @@ void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
const char * paramValues[11];
char * place_id;
char * partionQueryName;
int i;
int i, namePos, lineTypeLen, lineValueLen;
if (xmlStrEqual(name, BAD_CAST "feature"))
{
@@ -345,13 +374,69 @@ void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
paramValues[2] = (const char *)feature.id;
paramValues[3] = (const char *)feature.key;
paramValues[4] = (const char *)feature.value;
// paramValues[5] = (const char *)feature.name;
paramValues[6] = (const char *)feature.adminLevel;
paramValues[7] = (const char *)feature.houseNumber;
paramValues[8] = (const char *)feature.rankAddress;
paramValues[9] = (const char *)feature.rankSearch;
paramValues[10] = (const char *)feature.geometry;
res = PQexecPrepared(conn, "placex_insert", 11, paramValues, NULL, NULL, 0);
featureNameString[0] = 0;
if (featureNameLines)
{
namePos = 0;
lineTypeLen = 0;
lineValueLen = 0;
for(i = 0; i < featureNameLines; i++)
{
lineTypeLen = strlen(BAD_CAST featureName[i].type);
lineValueLen = strlen(BAD_CAST 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, BAD_CAST featureName[i].type);
namePos += lineTypeLen;
strcpy(featureNameString+namePos, "\"=>\"");
namePos += 4;
strcpy(featureNameString+namePos, BAD_CAST featureName[i].value);
namePos += lineValueLen;
strcpy(featureNameString+(namePos++), "\"");
}
}
paramValues[5] = (const char *)featureNameString;
featureExtraTagString[0] = 0;
if (featureExtraTagLines)
{
namePos = 0;
lineTypeLen = 0;
lineValueLen = 0;
for(i = 0; i < featureExtraTagLines; i++)
{
lineTypeLen = strlen(BAD_CAST featureExtraTag[i].type);
lineValueLen = strlen(BAD_CAST 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, BAD_CAST featureExtraTag[i].type);
namePos += lineTypeLen;
strcpy(featureExtraTagString+namePos, "\"=>\"");
namePos += 4;
strcpy(featureExtraTagString+namePos, BAD_CAST featureExtraTag[i].value);
namePos += lineValueLen;
strcpy(featureExtraTagString+(namePos++), "\"");
}
}
paramValues[6] = (const char *)featureExtraTagString;
paramValues[7] = (const char *)feature.adminLevel;
paramValues[8] = (const char *)feature.houseNumber;
paramValues[9] = (const char *)feature.rankAddress;
paramValues[10] = (const char *)feature.rankSearch;
paramValues[11] = (const char *)feature.geometry;
res = PQexecPrepared(conn, "placex_insert", 12, paramValues, NULL, NULL, 0);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "index_placex: INSERT failed: %s", PQerrorMessage(conn));
@@ -369,7 +454,8 @@ void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
paramValues[3] = (const char *)featureAddress[i].id;
paramValues[4] = (const char *)featureAddress[i].key;
paramValues[5] = (const char *)featureAddress[i].value;
res = PQexecPrepared(conn, "place_addressline_insert", 6, paramValues, NULL, NULL, 0);
paramValues[6] = (const char *)featureAddress[i].isAddress;
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));
@@ -555,9 +641,9 @@ int nominatim_import(const char *conninfo, const char *partionTagsFilename, cons
}
res = PQprepare(conn, "placex_insert",
"insert into placex (place_id,osm_type,osm_id,class,type,name,admin_level,housenumber,rank_address,rank_search,geometry) "
"values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, st_setsrid($11, 4326))",
11, NULL);
"insert into placex (place_id,osm_type,osm_id,class,type,name,extratags,admin_level,housenumber,rank_address,rank_search,geometry) "
"values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, st_setsrid($12, 4326))",
12, NULL);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "Failed to prepare placex_insert: %s\n", PQerrorMessage(conn));
@@ -573,14 +659,14 @@ int nominatim_import(const char *conninfo, const char *partionTagsFilename, cons
1, NULL);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "Failed to prepare placex_insert: %s\n", PQerrorMessage(conn));
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, true, $2, rank_address from placex where osm_type = $3 and osm_id = $4 and class = $5 and type = $6",
6, NULL);
"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));

View File

@@ -121,6 +121,14 @@ void nominatim_index(int rank_min, int rank_max, int num_threads, const char *co
fprintf(stderr, "Failed preparing index_placex: %s\n", PQerrorMessage(conn));
exit(EXIT_FAILURE);
}
PQclear(res);
res = PQexec(thread_data[i].conn, "set enable_seqscan = false");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "Failed disabling sequential scan: %s\n", PQerrorMessage(conn));
exit(EXIT_FAILURE);
}
PQclear(res);
nominatim_exportCreatePreparedQueries(thread_data[i].conn);

View File

@@ -579,6 +579,8 @@ BEGIN
RAISE EXCEPTION 'Adding location with rank > 25 (% rank %)', place_id, rank_search;
END IF;
RAISE WARNING 'Adding location with rank > 25 (% rank %)', place_id, rank_search;
x := deleteLocationArea(partition, place_id);
isarea := false;
@@ -611,20 +613,24 @@ BEGIN
ELSEIF rank_search < 26 THEN
diameter := 0.02;
IF rank_search = 14 THEN
IF rank_address = 0 THEN
diameter := 0.02;
ELSEIF rank_search <= 14 THEN
diameter := 1.2;
ELSEIF rank_search <= 15 THEN
diameter := 1;
ELSEIF rank_search = 15 THEN
ELSEIF rank_search <= 16 THEN
diameter := 0.5;
ELSEIF rank_search = 16 THEN
diameter := 0.15;
ELSEIF rank_search = 17 THEN
ELSEIF rank_search <= 17 THEN
diameter := 0.2;
ELSEIF rank_search <= 21 THEN
diameter := 0.05;
ELSEIF rank_search = 21 THEN
diameter := 0.01;
ELSEIF rank_search = 25 THEN
diameter := 0.005;
END IF;
RAISE WARNING 'adding % diameter %', place_id, diameter;
secgeo := ST_Buffer(geometry, diameter);
x := insertLocationAreaLarge(partition, place_id, country_code, keywords, rank_search, rank_address, true, ST_Centroid(geometry), secgeo);
@@ -746,6 +752,7 @@ DECLARE
housenum INTEGER;
linegeo GEOMETRY;
search_place_id INTEGER;
defpostalcode TEXT;
havefirstpoint BOOLEAN;
linestr TEXT;
@@ -753,6 +760,7 @@ BEGIN
newpoints := 0;
IF interpolationtype = 'odd' OR interpolationtype = 'even' OR interpolationtype = 'all' THEN
select postcode from placex where osm_type = 'W' and osm_id = wayid INTO defpostalcode;
select nodes from planet_osm_ways where id = wayid INTO waynodes;
--RAISE WARNING 'interpolation % % %',wayid,interpolationtype,waynodes;
IF array_upper(waynodes, 1) IS NOT NULL THEN
@@ -819,9 +827,9 @@ BEGIN
FOR housenum IN startnumber..endnumber BY stepsize LOOP
-- this should really copy postcodes but it puts a huge burdon on the system for no big benefit
-- ideally postcodes should move up to the way
insert into placex (osm_type, osm_id, class, type, admin_level, housenumber, street, isin,
insert into placex (osm_type, osm_id, class, type, admin_level, housenumber, street, isin, postcode,
country_code, parent_place_id, rank_address, rank_search, indexed_status, geometry)
values ('N',prevnode.osm_id, prevnode.class, prevnode.type, prevnode.admin_level, housenum, prevnode.street, prevnode.isin,
values ('N',prevnode.osm_id, prevnode.class, prevnode.type, prevnode.admin_level, housenum, prevnode.street, prevnode.isin, coalesce(prevnode.postcode, defpostalcode),
prevnode.country_code, prevnode.parent_place_id, prevnode.rank_address, prevnode.rank_search, 1, ST_Line_Interpolate_Point(linegeo, (housenum::float-orginalstartnumber::float)/originalnumberrange::float));
newpoints := newpoints + 1;
--RAISE WARNING 'interpolation number % % ',prevnode.place_id,housenum;
@@ -890,12 +898,12 @@ BEGIN
NEW.place_id := nextval('seq_place');
NEW.indexed_status := 1; --STATUS_NEW
NEW.country_code := get_country_code(NEW.geometry, NEW.country_code);
NEW.country_code := lower(get_country_code(NEW.geometry, NEW.country_code));
NEW.partition := get_partition(NEW.geometry, NEW.country_code);
NEW.geometry_sector := geometry_sector(NEW.partition, NEW.geometry);
-- copy 'name' to or from the default language (if there is a default language)
IF NEW.name is not null THEN
IF NEW.name is not null AND array_upper(%#NEW.name,1) > 1 THEN
default_language := get_country_language_code(NEW.country_code);
IF default_language IS NOT NULL THEN
IF NEW.name ? 'name' AND NOT NEW.name ? ('name:'||default_language) THEN
@@ -947,11 +955,11 @@ BEGIN
NEW.rank_search := 17;
NEW.rank_address := 0;
ELSEIF NEW.type in ('town') THEN
NEW.rank_search := 17;
NEW.rank_address := NEW.rank_search;
ELSEIF NEW.type in ('village','hamlet','municipality','district','unincorporated_area','borough') THEN
NEW.rank_search := 18;
NEW.rank_address := 17;
NEW.rank_address := 16;
ELSEIF NEW.type in ('village','hamlet','municipality','district','unincorporated_area','borough') THEN
NEW.rank_search := 19;
NEW.rank_address := 16;
ELSEIF NEW.type in ('airport') AND ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') THEN
NEW.rank_search := 18;
NEW.rank_address := 17;
@@ -1031,6 +1039,9 @@ BEGIN
END IF;
ELSEIF NEW.class = 'boundary' THEN
IF ST_GeometryType(NEW.geometry) NOT IN ('ST_Polygon','ST_MultiPolygon') THEN
return NULL;
END IF;
NEW.rank_search := NEW.admin_level * 2;
NEW.rank_address := NEW.rank_search;
ELSEIF NEW.class = 'landuse' AND ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') THEN
@@ -1075,6 +1086,10 @@ BEGIN
NEW.rank_address := 30;
END IF;
IF (NEW.extratags -> 'capital') = 'yes' THEN
NEW.rank_search := NEW.rank_search -1;
END IF;
-- Block import below rank 22
-- IF NEW.rank_search > 22 THEN
-- RETURN NULL;
@@ -1154,7 +1169,8 @@ DECLARE
search_maxrank INTEGER;
address_maxrank INTEGER;
address_street_word_id INTEGER;
parent_place_id_count INTEGER;
parent_place_id_rank INTEGER;
isin TEXT[];
isin_tokens INT[];
@@ -1246,7 +1262,7 @@ BEGIN
--RAISE WARNING 'x1';
-- Is this node part of a way?
FOR location IN select * from placex where osm_type = 'W'
and osm_id in (select id from planet_osm_ways where nodes && ARRAY[NEW.osm_id::integer])
and osm_id in (select id from planet_osm_ways where nodes && ARRAY[NEW.osm_id::integer] limit 10)
LOOP
--RAISE WARNING '%', location;
-- Way IS a road then we are on it - that must be our road
@@ -1339,6 +1355,7 @@ BEGIN
search_diameter := search_diameter * 2;
END LOOP;
--return NEW;
--RAISE WARNING 'x6 %',NEW.parent_place_id;
-- If we didn't find any road fallback to standard method
@@ -1365,20 +1382,25 @@ BEGIN
-- Merge address from parent
nameaddress_vector := array_merge(nameaddress_vector, location.nameaddress_vector);
--return NEW;
-- Performance, it would be more acurate to do all the rest of the import process but it takes too long
-- Just be happy with inheriting from parent road only
IF NEW.rank_search <= 25 THEN
result := add_location(NEW.place_id, NEW.country_code, NEW.partition, name_vector, NEW.rank_search, NEW.rank_address, NEW.geometry);
END IF;
result := insertSearchName(NEW.partition, NEW.place_id, NEW.country_code, name_vector, nameaddress_vector, NEW.rank_search, NEW.rank_address, place_centroid);
-- INSERT INTO search_name values (NEW.place_id, NEW.rank_search, NEW.rank_address, 0, NEW.country_code,
-- name_vector, nameaddress_vector, place_centroid);
return NEW;
END IF;
END IF;
--RAISE WARNING ' INDEXING: %',NEW;
RAISE WARNING ' INDEXING: %',NEW;
NEW.parent_place_id = 0;
parent_place_id_rank = 0;
-- convert isin to array of tokenids
isin_tokens := '{}'::int[];
@@ -1394,11 +1416,42 @@ BEGIN
END IF;
isin_tokens := uniq(sort(isin_tokens));
END IF;
IF NEW.postcode IS NOT NULL THEN
isin := regexp_split_to_array(NEW.postcode, E'[;,]');
IF array_upper(isin, 1) IS NOT NULL THEN
FOR i IN 1..array_upper(isin, 1) LOOP
address_street_word_id := get_name_id(make_standard_name(isin[i]));
IF address_street_word_id IS NOT NULL THEN
isin_tokens := isin_tokens + address_street_word_id;
END IF;
END LOOP;
END IF;
isin_tokens := uniq(sort(isin_tokens));
END IF;
-- try using the isin value to find parent places
IF array_upper(isin_tokens, 1) IS NOT NULL THEN
FOR i IN 1..array_upper(isin_tokens, 1) LOOP
--RAISE WARNING ' ISIN: % % % %',NEW.partition, place_centroid, search_maxrank, isin_tokens[i];
FOR location IN SELECT * from getNearestNamedFeature(NEW.partition, place_centroid, search_maxrank, isin_tokens[i]) LOOP
nameaddress_vector := array_merge(nameaddress_vector, location.keywords::integer[]);
INSERT INTO place_addressline VALUES (NEW.place_id, location.place_id, false, NOT address_havelevel[location.rank_address], location.distance, location.rank_address);
address_havelevel[location.rank_address] := true;
IF location.rank_address > parent_place_id_rank THEN
NEW.parent_place_id = location.place_id;
parent_place_id_rank = location.rank_address;
END IF;
END LOOP;
END LOOP;
END IF;
-- Process area matches
location_rank_search := 100;
location_distance := 0;
--RAISE WARNING '%', NEW.partition;
FOR location IN SELECT * from getNearFeatures(NEW.partition, place_centroid, search_maxrank, isin_tokens) LOOP
--RAISE WARNING ' AREA: %',location;
@@ -1415,21 +1468,38 @@ BEGIN
INSERT INTO place_addressline VALUES (NEW.place_id, location.place_id, true, NOT address_havelevel[location.rank_address], location.distance, location.rank_address);
address_havelevel[location.rank_address] := true;
IF location.rank_address > parent_place_id_rank THEN
NEW.parent_place_id = location.place_id;
parent_place_id_rank = location.rank_address;
END IF;
END IF;
END LOOP;
-- try using the isin value to find parent places
IF array_upper(isin_tokens, 1) IS NOT NULL THEN
FOR i IN 1..array_upper(isin_tokens, 1) LOOP
-- for long ways we should add search terms for the entire length
IF st_length(NEW.geometry) > 0.05 THEN
FOR location IN SELECT * from getNearestNamedFeature(NEW.partition, place_centroid, search_maxrank, isin_tokens[i]) LOOP
location_rank_search := 100;
location_distance := 0;
FOR location IN SELECT * from getNearFeatures(NEW.partition, NEW.geometry, search_maxrank, isin_tokens) LOOP
IF location.rank_search < location_rank_search THEN
location_rank_search := location.rank_search;
location_distance := location.distance * 1.5;
END IF;
IF location.distance < location_distance THEN
-- Add it to the list of search terms
nameaddress_vector := array_merge(nameaddress_vector, location.keywords::integer[]);
INSERT INTO place_addressline VALUES (NEW.place_id, location.place_id, false, NOT address_havelevel[location.rank_address], location.distance, location.rank_address);
address_havelevel[location.rank_address] := true;
END LOOP;
INSERT INTO place_addressline VALUES (NEW.place_id, location.place_id, true, false, location.distance, location.rank_address);
END IF;
END LOOP;
END IF;
-- if we have a name add this to the name search table
@@ -1842,13 +1912,14 @@ DECLARE
searchcountrycode varchar(2);
searchhousenumber TEXT;
searchrankaddress INTEGER;
searchpostcode TEXT;
BEGIN
found := 1000;
search := languagepref;
result := '{}';
select country_code,housenumber,rank_address from placex where place_id = for_place_id into searchcountrycode,searchhousenumber,searchrankaddress;
select country_code,housenumber,rank_address,postcode from placex where place_id = for_place_id into searchcountrycode,searchhousenumber,searchrankaddress,searchpostcode;
FOR location IN
select CASE WHEN address_place_id = for_place_id AND rank_address = 0 THEN 100 ELSE rank_address END as rank_address,
@@ -1877,6 +1948,10 @@ BEGIN
result[(100 - 28)] := searchhousenumber;
END IF;
IF searchpostcode IS NOT NULL THEN
result[(100 - 5)] := searchpostcode;
END IF;
-- No country polygon - add it from the country_code
IF found > 4 THEN
select get_name_by_language(country_name.name,languagepref) as name from placex join country_name using (country_code)
@@ -2043,6 +2118,7 @@ BEGIN
where placex.place_id = search_place_id
and place.osm_type = placex.osm_type and place.osm_id = placex.osm_id
and place.class = placex.class and place.type = placex.type;
update placex set indexed_status = 1 where place_id = search_place_id;
update placex set indexed_status = 0 where place_id = search_place_id;
return true;
END;
@@ -2062,12 +2138,12 @@ BEGIN
isin = place.isin,
postcode = place.postcode,
country_code = place.country_code,
parent_place_id = null,
indexed_status = 2
parent_place_id = null
from place
where placex.place_id = search_place_id
and place.osm_type = placex.osm_type and place.osm_id = placex.osm_id
and place.class = placex.class and place.type = placex.type;
update placex set indexed_status = 2 where place_id = search_place_id;
update placex set indexed_status = 0 where place_id = search_place_id;
return true;
END;
@@ -2220,6 +2296,11 @@ DECLARE
newpoints INTEGER;
numberrange INTEGER;
rangestartnumber INTEGER;
place_centroid GEOMETRY;
partition INTEGER;
parent_place_id INTEGER;
location RECORD;
address_street_word_id INTEGER;
BEGIN
@@ -2251,12 +2332,21 @@ BEGIN
RETURN 0;
END IF;
place_centroid := ST_Centroid(linegeo);
partition := get_partition(place_centroid, 'us');
parent_place_id := null;
address_street_word_id := get_name_id(make_standard_name(in_street));
IF address_street_word_id IS NOT NULL THEN
FOR location IN SELECT * from getNearestNamedRoadFeature(partition, place_centroid, address_street_word_id) LOOP
parent_place_id := location.place_id;
END LOOP;
END IF;
newpoints := 0;
FOR housenum IN startnumber..endnumber BY stepsize LOOP
insert into placex (osm_type, osm_id, class, type, admin_level, housenumber, street, isin, postcode,
country_code, parent_place_id, rank_address, rank_search, indexed_status, geometry)
values ('T', nextval('seq_tigger_house'), 'place', 'house', null, housenum, in_street, in_isin, in_postcode,
'us', null, 30, 30, 1, ST_Line_Interpolate_Point(linegeo, (housenum::float-rangestartnumber::float)/numberrange::float));
insert into location_property_tiger (place_id, partition, parent_place_id, housenumber, postcode, centroid)
values (nextval('seq_place'), 2, parent_place_id, housenum, in_postcode,
ST_Line_Interpolate_Point(linegeo, (housenum::float-rangestartnumber::float)/numberrange::float));
newpoints := newpoints + 1;
END LOOP;

View File

@@ -31,6 +31,12 @@ CREATE INDEX idx_search_name_-partition-_centroid ON search_name_-partition- USI
CREATE INDEX idx_search_name_-partition-_name_vector ON search_name_-partition- USING GIN (name_vector gin__int_ops);
CREATE INDEX idx_search_name_-partition-_nameaddress_vector ON search_name_-partition- USING GIN (nameaddress_vector gin__int_ops);
CREATE TABLE location_property_-partition- () INHERITS (location_property);
CREATE INDEX idx_location_property_-partition-_place_id ON location_property_-partition- USING BTREE (place_id);
CREATE INDEX idx_location_property_-partition-_parent_place_id ON location_property_-partition- USING BTREE (parent_place_id);
CREATE INDEX idx_location_property_-partition-_housenumber_parent_place_id ON location_property_-partition- USING BTREE (parent_place_id, housenumber);
--CREATE INDEX idx_location_property_-partition-_centroid ON location_property_-partition- USING GIST (centroid);
-- end
create or replace function getNearFeatures(in_partition INTEGER, point GEOMETRY, maxrank INTEGER, isin_tokens INT[]) RETURNS setof nearfeature AS $$
@@ -46,7 +52,7 @@ BEGIN
UNION ALL
SELECT * FROM location_area_country WHERE ST_Contains(geometry, point) and rank_search < maxrank
) as location_area
ORDER BY rank_search desc, isin_tokens && keywords desc, isguess asc, rank_address asc, ST_Distance(point, centroid) ASC
ORDER BY rank_address desc, isin_tokens && keywords desc, isguess asc, ST_Distance(point, centroid) * CASE WHEN rank_address = 16 AND rank_search = 16 THEN 0.25 WHEN rank_address = 16 AND rank_search = 17 THEN 0.5 ELSE 1 END ASC
LOOP
RETURN NEXT r;
END LOOP;
@@ -85,12 +91,14 @@ DECLARE
BEGIN
IF in_rank_search <= 4 THEN
DELETE FROM location_area_country where place_id = in_place_id;
INSERT INTO location_area_country values (in_partition, in_place_id, in_country_code, in_keywords, in_rank_search, in_rank_address, in_estimate, in_centroid, in_geometry);
RETURN TRUE;
END IF;
-- start
IF in_partition = -partition- THEN
DELETE FROM location_area_large_-partition- where place_id = in_place_id;
INSERT INTO location_area_large_-partition- values (in_partition, in_place_id, in_country_code, in_keywords, in_rank_search, in_rank_address, in_estimate, in_centroid, in_geometry);
RETURN TRUE;
END IF;
@@ -169,10 +177,12 @@ create or replace function insertSearchName(
DECLARE
BEGIN
DELETE FROM search_name WHERE place_id = in_place_id;
INSERT INTO search_name values (in_place_id, in_rank_search, in_rank_address, 0, in_country_code,
in_name_vector, in_nameaddress_vector, in_centroid);
IF in_rank_search <= 4 THEN
DELETE FROM search_name_country WHERE place_id = in_place_id;
INSERT INTO search_name_country values (in_place_id, in_rank_search, in_rank_address, 0, in_country_code,
in_name_vector, in_nameaddress_vector, in_centroid);
RETURN TRUE;
@@ -180,6 +190,7 @@ BEGIN
-- start
IF in_partition = -partition- THEN
DELETE FROM search_name_-partition- values WHERE place_id = in_place_id;
INSERT INTO search_name_-partition- values (in_place_id, in_rank_search, in_rank_address, 0, in_country_code,
in_name_vector, in_nameaddress_vector, in_centroid);
RETURN TRUE;

View File

@@ -91,6 +91,20 @@ CREATE TABLE location_area_large () INHERITS (location_area);
CREATE TABLE location_area_roadnear () INHERITS (location_area);
CREATE TABLE location_area_roadfar () INHERITS (location_area);
drop table IF EXISTS location_property CASCADE;
CREATE TABLE location_property (
place_id INTEGER,
partition integer,
parent_place_id INTEGER,
housenumber TEXT,
postcode TEXT
);
SELECT AddGeometryColumn('location_property', 'centroid', 4326, 'POINT', 2);
CREATE TABLE location_property_tiger () INHERITS (location_property);
CREATE INDEX idx_location_property_tiger_place_id ON location_property_tiger USING BTREE (place_id);
CREATE INDEX idx_location_property_tiger_parent_place_id ON location_property_tiger USING BTREE (parent_place_id);
CREATE INDEX idx_location_property_tiger_housenumber_parent_place_id ON location_property_tiger USING BTREE (parent_place_id, housenumber);
drop table IF EXISTS search_name_blank CASCADE;
CREATE TABLE search_name_blank (
place_id INTEGER,
@@ -250,3 +264,6 @@ CREATE INDEX idx_placex_pendingbylatlon ON placex USING BTREE (geometry_index(ge
where geometry_index(geometry_sector,indexed,name) IS NOT NULL;
CREATE INDEX idx_placex_interpolation ON placex USING BTREE (geometry_sector) where indexed = false and class='place' and type='houses';
CREATE INDEX idx_placex_sector ON placex USING BTREE (geometry_sector,rank_address,osm_type,osm_id);
DROP SEQUENCE seq_postcodes;
CREATE SEQUENCE seq_postcodes start 1;

View File

@@ -22,7 +22,9 @@
array('create-tables', '', 0, 1, 0, 0, 'bool', 'Create main tables'),
array('create-partitions', '', 0, 1, 0, 0, 'bool', 'Create required partition tables and triggers'),
array('load-data', '', 0, 1, 0, 0, 'bool', 'Copy data to live tables from import table'),
array('import-tiger-data', '', 0, 1, 0, 0, 'bool', 'Import tiger data'),
array('import-tiger-data', '', 0, 1, 0, 0, 'bool', 'Import tiger data (not included in \'all\')'),
array('calculate-postcodes', '', 0, 1, 0, 0, 'bool', 'Calculate postcode centroids'),
array('index', '', 0, 1, 0, 0, 'bool', 'Index the data'),
);
getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
@@ -64,11 +66,12 @@
pgsqlRunScriptFile(CONST_Path_Postgresql_Postgis.'/postgis.sql');
pgsqlRunScriptFile(CONST_Path_Postgresql_Postgis.'/spatial_ref_sys.sql');
pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturaleathdata.sql');
pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql');
pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql');
pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode.sql');
pgsqlRunScriptFile(CONST_BasePath.'/data/us_statecounty.sql');
pgsqlRunScriptFile(CONST_BasePath.'/data/us_state.sql');
pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql');
pgsqlRunScriptFile(CONST_BasePath.'/data/worldboundaries.sql');
}
@@ -76,6 +79,10 @@
{
$bDidSomething = true;
passthru(CONST_BasePath.'/osm2pgsql/osm2pgsql -lsc -O gazetteer -C 10000 --hstore -d nominatim '.$aCMDResult['osm-file']);
$oDB =& getDB();
$x = $oDB->getRow('select * from place limit 1');
if (!$x || PEAR::isError($x)) fail('No Data');
}
if ($aCMDResult['create-functions'] || $aCMDResult['all'])
@@ -121,6 +128,7 @@
}
$sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
}
pgsqlRunScript($sTemplate);
}
@@ -172,7 +180,7 @@
echo "\n";
}
if ($aCMDResult['import-tiger-data'] || $aCMDResult['all'])
if ($aCMDResult['import-tiger-data'])
{
$bDidSomething = true;
@@ -185,6 +193,7 @@
foreach(glob(CONST_BasePath.'/data/tiger2009/*.sql') as $sFile)
{
echo $sFile.': ';
if ((int)basename($sFile) <= 53033) continue;
$hFile = fopen($sFile, "r");
$sSQL = fgets($hFile, 100000);
$iLines = 0;
@@ -226,6 +235,29 @@
}
}
if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all'])
{
$oDB =& getDB();
if (!pg_query($oDB->connection, 'DELETE from placex where osm_type=\'P\'')) fail(pg_last_error($oDB->connection));
$sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,country_code,geometry) ";
$sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,country_code,";
$sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from (select country_code,postcode,";
$sSQL .= "avg(st_x(st_centroid(geometry))) as x,avg(st_y(st_centroid(geometry))) as y ";
$sSQL .= "from place where postcode is not null group by country_code,postcode) as x";
if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
$sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,country_code,geometry) ";
$sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,'us',";
$sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from us_postcode";
if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
}
if ($aCMDResult['index'] || $aCMDResult['all'])
{
$bDidSomething = true;
passthru(CONST_BasePath.'/nominatim/nominatim -i -d nominatim -t '.$iInstances);
}
if (!$bDidSomething)
{
showUsage($aCMDOptions, true);

View File

@@ -2,13 +2,15 @@
require_once(dirname(dirname(__FILE__)).'/lib/init-website.php');
require_once(CONST_BasePath.'/lib/log.php');
$sOutputFormat = 'html';
/*
$fLoadAvg = getLoadAverage();
if ($fLoadAvg > 3)
{
echo "Page temporarily blocked due to high server load\n";
exit;
}
*/
ini_set('memory_limit', '200M');
$oDB =& getDB();
@@ -55,16 +57,13 @@
$aPointDetails['icon'] = $aClassType[$aPointDetails['class'].':'.$aPointDetails['type']]['icon'];
// Get all alternative names (languages, etc)
$aPointDetails['aNames'] = array();
/*
for($i = 1; $i <= $aPointDetails['numnames']; $i++)
{
$sSQL = "select name[$i].key, name[$i].value from placex where place_id = $iPlaceID limit 1";
$aNameItem = $oDB->getRow($sSQL);
if (substr($aNameItem['key'],0,5) == 'name:') $aNameItem['key'] = substr($aNameItem['key'],5);
$aPointDetails['aNames'][$aNameItem['key']] = $aNameItem['value'];
}
*/
$sSQL = "select (each(name)).key,(each(name)).value from placex where place_id = $iPlaceID order by (each(name)).key";
$aPointDetails['aNames'] = $oDB->getAssoc($sSQL);
// Extra tags
$sSQL = "select (each(extratags)).key,(each(extratags)).value from placex where place_id = $iPlaceID order by (each(extratags)).key";
$aPointDetails['aExtraTags'] = $oDB->getAssoc($sSQL);
// Get the bounding box and outline polygon
$sSQL = "select *,ST_AsText(outline) as outlinestring from get_place_boundingbox($iPlaceID)";
$aPointPolygon = $oDB->getRow($sSQL);
@@ -94,45 +93,18 @@
$aPointPolygon['maxlon'] = $aPointPolygon['maxlon'] + $fRadius;
}
// If it is a road then force all nearby buildings to be indexed (so we can show then in the list)
/*
if ($aPointDetails['rank_address'] == 26)
{
$sSQL = "UPDATE placex set indexed = true from placex as srcplace where placex.indexed = false";
$sSQL .= " and ST_DWithin(placex.geometry, srcplace.geometry, 0.0005) and srcplace.place_id = $iPlaceID";
$oDB->query($sSQL);
}
*/
// Address
$aAddressLines = getAddressDetails($oDB, $sLanguagePrefArraySQL, $iPlaceID, $aPointDetails['country_code'], true);
/*
$sSQL = "select placex.place_id, osm_type, osm_id, class, type, housenumber, admin_level, rank_address, rank_search, ";
$sSQL .= "get_searchrank_label(rank_search) as rank_search_label, fromarea, distance, ";
// All places this is an imediate parent of
$sSQL = "select placex.place_id, osm_type, osm_id, class, type, housenumber, admin_level, rank_address, ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') as isarea, st_distance(geometry, placegeometry) as distance, ";
$sSQL .= " get_name_by_language(name,$sLanguagePrefArraySQL) as localname, length(name::text) as namelength ";
$sSQL .= " from place_addressline join placex on (address_place_id = placex.place_id)";
$sSQL .= " where place_addressline.place_id = $iPlaceID and ((rank_address > 0 AND rank_address < ".$aPointDetails['rank_address'].") OR address_place_id = $iPlaceID) and placex.place_id != $iPlaceID";
if ($aPointDetails['country_code'])
{
$sSQL .= " and (placex.country_code IS NULL OR placex.country_code = '".$aPointDetails['country_code']."' OR rank_address < 4)";
}
$sSQL .= " order by cached_rank_address desc,rank_search desc,fromarea desc,distance asc,namelength desc";
$aAddressLines = $oDB->getAll($sSQL);
IF (PEAR::IsError($aAddressLines))
{
var_dump($aAddressLines);
exit;
}
*/
// All places this is a parent of
$iMaxRankAddress = $aPointDetails['rank_address']+13;
$sSQL = "select placex.place_id, osm_type, osm_id, class, type, housenumber, admin_level, cached_rank_address, ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') as isarea, distance, ";
$sSQL .= " get_name_by_language(name,$sLanguagePrefArraySQL) as localname, length(name::text) as namelength ";
$sSQL .= " from (select * from place_addressline where address_place_id = $iPlaceID and cached_rank_address < $iMaxRankAddress) as place_addressline join placex on (place_addressline.place_id = placex.place_id)";
$sSQL .= " where place_addressline.address_place_id = $iPlaceID and placex.rank_address < $iMaxRankAddress and cached_rank_address > 0 and placex.place_id != $iPlaceID";
$sSQL .= " and type != 'postcode'";
$sSQL .= " order by cached_rank_address asc,rank_search asc,get_name_by_language(name,$sLanguagePrefArraySQL),housenumber limit 1000";
$sSQL .= " from placex, (select geometry as placegeometry from placex where place_id = $iPlaceID) as x";
$sSQL .= " where parent_place_id = $iPlaceID";
// $sSQL .= " and type != 'postcode'";
$sSQL .= " order by rank_address asc,rank_search asc,get_name_by_language(name,$sLanguagePrefArraySQL),housenumber";
$aParentOfLines = $oDB->getAll($sSQL);
logEnd($oDB, $hLog, 1);
include('.htlib/output/details-html.php');
include(CONST_BasePath.'/lib/template/details-'.$sOutputFormat.'.php');