]> git.openstreetmap.org Git - nominatim.git/commitdiff
Merge remote-tracking branch 'upstream/master'
authorSarah Hoffmann <lonvia@denofr.de>
Wed, 5 Dec 2012 23:26:48 +0000 (00:26 +0100)
committerSarah Hoffmann <lonvia@denofr.de>
Wed, 5 Dec 2012 23:26:48 +0000 (00:26 +0100)
Conflicts:
sql/tables.sql
utils/setup.php
website/search.php

1  2 
settings/settings.php
sql/tables.sql
utils/setup.php
website/search.php

diff --combined settings/settings.php
index ce4bec5a0aa52549c2449bc6217e5cc59c54db8b,0a214ce9fa92746432072a5ee848d0b17b69a6ce..8ebd81ee6c326b84f8b85999a975e1d2b8653840
        @define('CONST_Osmosis_Binary', '/usr/bin/osmosis');
  
        // Website settings
 -      @define('CONST_ClosedForIndexing', false);
 -      @define('CONST_ClosedForIndexingExceptionIPs', '');
        @define('CONST_BlockedIPs', '');
 +      @define('CONST_IPBanFile', CONST_BasePath.'/settings/ip_blocks');
 +      @define('CONST_WhitelistedIPs', '');
 +      @define('CONST_BlockedUserAgents', '');
 +      @define('CONST_BlockReverseMaxLoad', 15);
        @define('CONST_BulkUserIPs', '');
  
 -      @define('CONST_Website_BaseURL', 'http://'.php_uname('n').'/');
 +      @define('CONST_Website_BaseURL', 'http://nominatim.openstreetmap.org/');
        @define('CONST_Tile_Default', 'Mapnik');
  
 -      @define('CONST_Default_Language', 'xx');
 +      @define('CONST_Default_Language', 'en');
        @define('CONST_Default_Lat', 20.0);
        @define('CONST_Default_Lon', 0.0);
        @define('CONST_Default_Zoom', 2);
  
        @define('CONST_Search_AreaPolygons_Enabled', true);
        @define('CONST_Search_AreaPolygons', true);
+       @define('CONST_Search_TryDroppedAddressTerms', false);
  
        @define('CONST_Suggestions_Enabled', false);
  
+       // Set to zero to disable polygon output
+       @define('CONST_PolygonOutput_MaximumTypes', 1);
        // Log settings
        @define('CONST_Log_DB', true);
        @define('CONST_Log_File', false);
diff --combined sql/tables.sql
index a0ad66a8353d814754d5cd6e576c976865f8b7c1,212d78c60752718ea1caf84926ec28ff0a9a4c7c..b5b59c16c1a54f1abfdf4f63ede6377b7e6999a0
@@@ -23,6 -23,19 +23,6 @@@ CREATE TABLE import_npi_log 
    event text
    );
  
 ---drop table IF EXISTS query_log;
 -CREATE TABLE query_log (
 -  starttime timestamp,
 -  query text,
 -  ipaddress text,
 -  endtime timestamp,
 -  results integer
 -  );
 -CREATE INDEX idx_query_log ON query_log USING BTREE (starttime);
 -GRANT SELECT ON query_log TO "www-data" ;
 -GRANT INSERT ON query_log TO "www-data" ;
 -GRANT UPDATE ON query_log TO "www-data" ;
 -
  CREATE TABLE new_query_log (
    type text,
    starttime timestamp,
@@@ -30,7 -43,6 +30,7 @@@
    useragent text,
    language text,
    query text,
 +  searchterm text,
    endtime timestamp,
    results integer,
    format text,
@@@ -41,6 -53,9 +41,6 @@@ GRANT INSERT ON new_query_log TO "www-d
  GRANT UPDATE ON new_query_log TO "www-data" ;
  GRANT SELECT ON new_query_log TO "www-data" ;
  
 -create view vw_search_query_log as SELECT substr(query, 1, 50) AS query, starttime, endtime - starttime AS duration, substr(useragent, 1, 20) as 
 -useragent, language, results, ipaddress FROM new_query_log WHERE type = 'search' ORDER BY starttime DESC;
 -
  --drop table IF EXISTS report_log;
  CREATE TABLE report_log (
    starttime timestamp,
@@@ -62,9 -77,9 +62,9 @@@ CREATE TABLE word 
    country_code varchar(2),
    search_name_count INTEGER,
    operator TEXT
 -  );
 +  ) TABLESPACE ssd;
  SELECT AddGeometryColumn('word', 'location', 4326, 'GEOMETRY', 2);
 -CREATE INDEX idx_word_word_token on word USING BTREE (word_token);
 +CREATE INDEX idx_word_word_token on word USING BTREE (word_token) TABLESPACE ssd;
  --CREATE INDEX idx_word_trigram ON word USING gin(word_trigram gin_trgm_ops) WITH (fastupdate = off);
  GRANT SELECT ON word TO "www-data" ;
  DROP SEQUENCE seq_word;
@@@ -122,8 -137,8 +122,8 @@@ CREATE TABLE search_name_blank 
  SELECT AddGeometryColumn('search_name_blank', 'centroid', 4326, 'GEOMETRY', 2);
  
  drop table IF EXISTS search_name;
 -CREATE TABLE search_name () INHERITS (search_name_blank);
 -CREATE INDEX idx_search_name_place_id ON search_name USING BTREE (place_id);
 +CREATE TABLE search_name () INHERITS (search_name_blank) TABLESPACE ssd;
 +CREATE INDEX idx_search_name_place_id ON search_name USING BTREE (place_id) TABLESPACE ssd;
  
  drop table IF EXISTS place_addressline;
  CREATE TABLE place_addressline (
    isaddress boolean,
    distance float,
    cached_rank_address integer
 -  );
 -CREATE INDEX idx_place_addressline_place_id on place_addressline USING BTREE (place_id);
 +  ) TABLESPACE data;
 +CREATE INDEX idx_place_addressline_place_id on place_addressline USING BTREE (place_id) TABLESPACE ssd;
  
  drop table IF EXISTS place_boundingbox CASCADE;
  CREATE TABLE place_boundingbox (
@@@ -187,14 -202,14 +187,14 @@@ CREATE TABLE placex 
    wikipedia TEXT, -- calculated wikipedia article name (language:title)
    geometry_sector INTEGER,
    calculated_country_code varchar(2)
 -  );
 +  ) TABLESPACE ssd;
  SELECT AddGeometryColumn('placex', 'centroid', 4326, 'GEOMETRY', 2);
 -CREATE UNIQUE INDEX idx_place_id ON placex USING BTREE (place_id);
 -CREATE INDEX idx_placex_osmid ON placex USING BTREE (osm_type, osm_id);
 -CREATE INDEX idx_placex_linked_place_id ON placex USING BTREE (linked_place_id);
 -CREATE INDEX idx_placex_rank_search ON placex USING BTREE (rank_search, geometry_sector);
 -CREATE INDEX idx_placex_geometry ON placex USING GIST (geometry);
 -CREATE INDEX idx_placex_adminname on placex USING BTREE (make_standard_name(name->'name'),rank_search) WHERE osm_type='N' and rank_search < 26;
 +CREATE UNIQUE INDEX idx_place_id ON placex USING BTREE (place_id) TABLESPACE ssd;
 +CREATE INDEX idx_placex_osmid ON placex USING BTREE (osm_type, osm_id) TABLESPACE ssd;
 +CREATE INDEX idx_placex_linked_place_id ON placex USING BTREE (linked_place_id) TABLESPACE ssd;
 +CREATE INDEX idx_placex_rank_search ON placex USING BTREE (rank_search, geometry_sector) TABLESPACE ssd;
 +CREATE INDEX idx_placex_geometry ON placex USING GIST (geometry) TABLESPACE ssd;
 +CREATE INDEX idx_placex_adminname on placex USING BTREE (make_standard_name(name->'name'),rank_search) TABLESPACE ssd WHERE osm_type='N' and rank_search < 26;
  
  --CREATE INDEX idx_placex_indexed ON placex USING BTREE (indexed);
  
@@@ -234,18 -249,8 +234,8 @@@ CREATE TRIGGER place_before_delete BEFO
  CREATE TRIGGER place_before_insert BEFORE INSERT ON place
      FOR EACH ROW EXECUTE PROCEDURE place_insert();
  
- alter table placex add column geometry_sector INTEGER;
- alter table placex add column indexed_status INTEGER;
- alter table placex add column indexed_date TIMESTAMP;
- update placex set geometry_sector = geometry_sector(geometry);
- drop index idx_placex_pendingbylatlon;
- drop index idx_placex_interpolation;
  drop index idx_placex_sector;
- CREATE INDEX idx_placex_pendingbylatlon ON placex USING BTREE (geometry_index(geometry_sector,indexed,name),rank_search) TABLESPACE ssd
-   where geometry_index(geometry_sector,indexed,name) IS NOT NULL;
- CREATE INDEX idx_placex_interpolation ON placex USING BTREE (geometry_sector) TABLESPACE ssd 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);
 +CREATE INDEX idx_placex_sector ON placex USING BTREE (geometry_sector,rank_address,osm_type,osm_id) TABLESPACE ssd;
  
  DROP SEQUENCE seq_postcodes;
  CREATE SEQUENCE seq_postcodes start 1;
@@@ -263,7 -268,8 +253,8 @@@ CREATE TABLE import_polygon_error 
    );
  SELECT AddGeometryColumn('import_polygon_error', 'prevgeometry', 4326, 'GEOMETRY', 2);
  SELECT AddGeometryColumn('import_polygon_error', 'newgeometry', 4326, 'GEOMETRY', 2);
 -CREATE INDEX idx_import_polygon_error_osmid ON import_polygon_error USING BTREE (osm_type, osm_id);
 +CREATE INDEX idx_import_polygon_error_osmid ON import_polygon_error USING BTREE (osm_type, osm_id) TABLESPACE ssd;
+ GRANT SELECT ON import_polygon_error TO "www-data";
  
  drop table import_polygon_delete;
  CREATE TABLE import_polygon_delete (
    class TEXT NOT NULL,
    type TEXT NOT NULL
    );
 -CREATE INDEX idx_import_polygon_delete_osmid ON import_polygon_delete USING BTREE (osm_type, osm_id);
 +CREATE INDEX idx_import_polygon_delete_osmid ON import_polygon_delete USING BTREE (osm_type, osm_id) TABLESPACE ssd;
+ GRANT SELECT ON import_polygon_delete TO "www-data";
  
  drop sequence file;
  CREATE SEQUENCE file start 1;
diff --combined utils/setup.php
index 70652fbc9b07a2d66a5c558a118c41d3d1033b7c,3fe5a7abec3594f4e455443a23f3e3e1d87c4358..d402b5543ec86d0095a805b67a0bb32dff6acda5
                $pgver = (float) CONST_Postgresql_Version;
                if ($pgver < 9.1) {
                        pgsqlRunScriptFile(CONST_Path_Postgresql_Contrib.'/hstore.sql');
+                       pgsqlRunScriptFile(CONST_BasePath.'/sql/hstore_compatability_9_0.sql');
                } else {
                        pgsqlRunScript('CREATE EXTENSION hstore');
                }
                        echo "Please download and build osm2pgsql.\nIf it is already installed, check the path in your local settings (settings/local.php) file.\n";
                        fail("osm2pgsql not found in '$osm2pgsql'");
                }
 +              $osm2pgsql .= ' --tablespace-slim-index ssd --tablespace-main-index ssd --tablespace-main-data ssd --tablespace-slim-data data';
                $osm2pgsql .= ' -lsc -O gazetteer --hstore';
 -              $osm2pgsql .= ' -C '.$iCacheMemory;
 +              $osm2pgsql .= ' -C 16000';
                $osm2pgsql .= ' -d '.$aDSNInfo['database'].' '.$aCMDResult['osm-file'];
                passthruCheckReturn($osm2pgsql);
  
                $sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,calculated_country_code,";
                $sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from (select calculated_country_code,postcode,";
                $sSQL .= "avg(st_x(st_centroid(geometry))) as x,avg(st_y(st_centroid(geometry))) as y ";
 -              $sSQL .= "from placex where postcode is not null group by calculated_country_code,postcode) as x";
 +              $sSQL .= "from placex where postcode is not null and calculated_country_code not in ('ie') group by calculated_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,calculated_country_code,geometry) ";
                pg_query($oDB->connection, 'TRUNCATE import_status');
                $sSQL = "INSERT INTO import_status VALUES('".$aRepMatch[2]."')";
                pg_query($oDB->connection, $sSQL);
-               exit;
        }
  
        if ($aCMDResult['index'] || $aCMDResult['all'])
                @symlink(CONST_BasePath.'/website/reverse.php', $sTargetDir.'/reverse.php');
                @symlink(CONST_BasePath.'/website/search.php', $sTargetDir.'/search.php');
                @symlink(CONST_BasePath.'/website/search.php', $sTargetDir.'/index.php');
+               @symlink(CONST_BasePath.'/website/deletable.php', $sTargetDir.'/deletable.php');
+               @symlink(CONST_BasePath.'/website/polygons.php', $sTargetDir.'/polygons.php');
                @symlink(CONST_BasePath.'/website/images', $sTargetDir.'/images');
                @symlink(CONST_BasePath.'/website/js', $sTargetDir.'/js');
+               @symlink(CONST_BasePath.'/website/css', $sTargetDir.'/css');
                echo "Symlinks created\n";
        }
  
diff --combined website/search.php
index c165b92b38c461567035c3c18135abd888ee301c,08c2eee095049c3ff6090c3de03bb42a265e6d8c..b7a59613e3073f75de6b4fea6ee8776c341ef402
        $sSuggestion = $sSuggestionURL = false;
        $bDeDupe = isset($_GET['dedupe'])?(bool)$_GET['dedupe']:true;
        $bReverseInPlan = false;
-       $iLimit = isset($_GET['limit'])?(int)$_GET['limit']:10;
+       $iFinalLimit = isset($_GET['limit'])?(int)$_GET['limit']:10;
        $iOffset = isset($_GET['offset'])?(int)$_GET['offset']:0;
        $iMaxRank = 20;
-       if ($iLimit > 100) $iLimit = 100;
+       if ($iFinalLimit > 50) $iFinalLimit = 50;
+     $iLimit = $iFinalLimit + min($iFinalLimit, 10);
        $iMinAddressRank = 0;
        $iMaxAddressRank = 30;
  
        }
  
        // Show / use polygons
-       $bShowPolygons = isset($_GET['polygon']) && $_GET['polygon'];
+       $bShowPolygons = (boolean)isset($_GET['polygon']) && $_GET['polygon'];
+     if ($sOutputFormat == 'html') {
+               $bAsText = $bShowPolygons;
+               $bShowPolygons = false;
+               $bAsGeoJSON = false;
+               $bAsKML = false;
+               $bAsSVG = false;
+       } else {
+               $bAsGeoJSON = (boolean)isset($_GET['polygon_geojson']) && $_GET['polygon_geojson'];
+               $bAsKML = (boolean)isset($_GET['polygon_kml']) && $_GET['polygon_kml'];
+               $bAsSVG = (boolean)isset($_GET['polygon_svg']) && $_GET['polygon_svg'];
+               $bAsText = (boolean)isset($_GET['polygon_text']) && $_GET['polygon_text'];
+               if ((($bShowPolygons?1:0)
+                  + ($bAsGeoJSON?1:0)
+                  + ($bAsKML?1:0)
+                  + ($bAsSVG?1:0)
+                  + ($bAsText?1:0)
+                       ) > CONST_PolygonOutput_MaximumTypes) {
+                       if (CONST_PolygonOutput_MaximumTypes) {
+                               userError("Select only ".CONST_PolygonOutput_MaximumTypes." polgyon output option");
+                       } else {
+                               userError("Polygon output is disabled");
+                       }
+                       exit;
+               }
+       }
  
        // Show address breakdown
        $bShowAddressDetails = isset($_GET['addressdetails']) && $_GET['addressdetails'];
@@@ -41,7 -67,6 +67,7 @@@
        if (isset($aLangPrefOrder['name:de'])) $bReverseInPlan = true;
        if (isset($aLangPrefOrder['name:ru'])) $bReverseInPlan = true;
        if (isset($aLangPrefOrder['name:ja'])) $bReverseInPlan = true;
 +      if (isset($aLangPrefOrder['name:pl'])) $bReverseInPlan = true;
  
        $sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted",$aLangPrefOrder))."]";
  
                $aPhrases = array_reverse($aPhrases); 
                $sQuery = join(', ',$aPhrases);
        }
+       function structuredAddressElement(&$aStructuredQuery, &$iMinAddressRank, &$iMaxAddressRank, $aParams, $sKey, $iNewMinAddressRank, $iNewMaxAddressRank)
+       {
+               if (!isset($_GET[$sKey])) return false;
+               $sValue = trim($_GET[$sKey]);
+               if (!$sValue) return false;
+               $aStructuredQuery[$sKey] = $sValue;
+               if ($iMinAddressRank == 0 && $iMaxAddressRank == 30) {
+                       $iMinAddressRank = $iNewMinAddressRank;
+                       $iMaxAddressRank = $iNewMaxAddressRank;
+               }
+               return true;
+       }
+       // Structured query?
+       $aStructuredOptions = array(
+                               array('amenity', 26, 30),
+                               array('street', 26, 30),
+                               array('city', 14, 24),
+                               array('county', 9, 13),
+                               array('state', 8, 8),
+                               array('country', 4, 4),
+                               array('postalcode', 16, 25),
+                               );
+       $aStructuredQuery = array();
+       foreach($aStructuredOptions as $aStructuredOption)
+       {
+               loadStructuredAddressElement($aStructuredQuery, $iMinAddressRank, $iMaxAddressRank, $_GET, $aStructuredOption[0], $aStructuredOption[1], $aStructuredOption[2]);
+       }
+       if (sizeof($aStructuredQuery) > 0) {
+               $sQuery = join(', ', $aStructuredQuery);
+       }
        if ($sQuery)
        {
                $hLog = logStart($oDB, 'search', $sQuery, $aLangPrefOrder);
                        $aPoints = explode(',',$_GET['route']);
                        if (sizeof($aPoints) % 2 != 0)
                        {
-                               echo "Uneven number of points";
+                               userError("Uneven number of points");
                                exit;
                        }
                        $sViewboxCentreSQL = "ST_SetSRID('LINESTRING(";
                                failInternalError("Could not get large viewbox.", $sSQL, $sViewboxLargeSQL);
                        }
                        $sViewboxLargeSQL = "'".$sViewboxLargeSQL."'::geometry";
+                       $bBoundingBoxSearch = true;
                }
  
                // Do we have anything that looks like a lat/lon pair?
                        }
                }
  
-               if ($sQuery)
+               if ($sQuery || $aStructuredQuery)
                {
                        // Start with a blank search
                        $aSearches = array(
  
                        preg_match_all('/\\[([a-zA-Z]*)\\]/', $sQuery, $aSpecialTermsRaw, PREG_SET_ORDER);
                        $aSpecialTerms = array();
+                       if (isset($aStructuredQuery['amenity']) && $aStructuredQuery['amenity'])
+                       {
+                               $aSpecialTermsRaw[] = array('['.$aStructuredQuery['amenity'].']', $aStructuredQuery['amenity']);
+                               unset($aStructuredQuery['amenity']);
+                       }
                        foreach($aSpecialTermsRaw as $aSpecialTerm)
                        {
                                $sQuery = str_replace($aSpecialTerm[0], ' ', $sQuery);
  
                        // Split query into phrases
                        // Commas are used to reduce the search space by indicating where phrases split
-                       $aPhrases = explode(',',$sQuery);
+                       if (sizeof($aStructuredQuery) > 0)
+                       {
+                               $aPhrases = $aStructuredQuery;
+                               $bStructuredPhrases = true;
+                       }
+                       else
+                       {
+                               $aPhrases = explode(',',$sQuery);
+                               $bStructuredPhrases = false;
+                       }
  
                        // Convert each phrase to standard form
                        // Create a list of standard words
                                $aPhrase = $oDB->getRow("select make_standard_name('".pg_escape_string($sPhrase)."') as string");
                                if (PEAR::isError($aPhrase))
                                {
-                                       echo "Illegal query string (not an UTF-8 string): ".$sPhrase;
+                                       userError("Illegal query string (not an UTF-8 string): ".$sPhrase);
                                        if (CONST_Debug) var_dump($aPhrase);
                                        exit;
                                }
                        }
  
                        // reindex phrases - we make assumptions later on
+                       $aPhraseTypes = array_keys($aPhrases);
                        $aPhrases = array_values($aPhrases);
  
                        if (sizeof($aTokens))
                                Calculate all searches using aValidTokens i.e.
  
                                'Wodsworth Road, Sheffield' =>
-                                       
                                Phrase Wordset
                                0      0       (wodsworth road)
                                0      1       (wodsworth)(road)
                                1      0       (sheffield)
-                               
                                Score how good the search is so they can be ordered
                        */
                                foreach($aPhrases as $iPhrase => $sPhrase)
                                {
                                        $aNewPhraseSearches = array();
+                                       if ($bStructuredPhrases) $sPhraseType = $aPhraseTypes[$iPhrase];
+                                       else $sPhraseType = '';
  
                                        foreach($aPhrases[$iPhrase]['wordsets'] as $aWordset)
                                        {
                                                {
  //echo "<br><b>$sToken</b>";
                                                        $aNewWordsetSearches = array();
-                                                       
                                                        foreach($aWordsetSearches as $aCurrentSearch)
                                                        {
  //echo "<i>";
                                                                        {
                                                                                $aSearch = $aCurrentSearch;
                                                                                $aSearch['iSearchRank']++;
-                                                                               if ($aSearchTerm['country_code'] !== null && $aSearchTerm['country_code'] != '0')
+                                                                               if (($sPhraseType == '' || $sPhraseType == 'country') && $aSearchTerm['country_code'] !== null && $aSearchTerm['country_code'] != '0')
                                                                                {
                                                                                        if ($aSearch['sCountryCode'] === false)
                                                                                        {
                                                                                                if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
                                                                                        }
                                                                                }
-                                                                               elseif ($aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'house')
+                                                                               elseif (($sPhraseType == '' || $sPhraseType == 'street') && $aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'house')
                                                                                {
                                                                                        if ($aSearch['sHouseNumber'] === '')
                                                                                        {
  */
                                                                                        }
                                                                                }
-                                                                               elseif ($aSearchTerm['class'] !== '' && $aSearchTerm['class'] !== null)
+                                                                               elseif ($sPhraseType == '' && $aSearchTerm['class'] !== '' && $aSearchTerm['class'] !== null)
                                                                                {
                                                                                        if ($aSearch['sClass'] === '')
                                                                                        {
                                                                                {
                                                                                        if (sizeof($aSearch['aName']))
                                                                                        {
-                                                                                               if (!isset($aValidTokens[$sToken]) || strlen($sToken) < 4 || strpos($sToken, ' ') !== false)
+                                                                                               if (($sPhraseType != 'street' && $sPhraseType != 'country') && (!isset($aValidTokens[$sToken]) || strlen($sToken) < 4 || strpos($sToken, ' ') !== false))
                                                                                                {
                                                                                                        $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
                                                                                                }
                                                                        {
                                                                                if (isset($aSearchTerm['word_id']) && $aSearchTerm['word_id'])
                                                                                {
- //var_Dump('<hr>',$aSearch['aName']);
-                                                                               if (sizeof($aCurrentSearch['aName'])  && strlen($sToken) >= 4)
-                                                                               {
-                                                                               $aSearch = $aCurrentSearch;
-                                                                                       $aSearch['iSearchRank'] += 1;
-                                                                                       $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
-                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
-                                                                               }
+                                                                                       if (($sPhraseType != 'street') && sizeof($aCurrentSearch['aName']) && strlen($sToken) >= 4)
+                                                                                       {
+                                                                                               $aSearch = $aCurrentSearch;
+                                                                                               $aSearch['iSearchRank'] += 1;
+                                                                                               $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+                                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                                       }
  
-                                                                               if (!sizeof($aCurrentSearch['aName']) || $aCurrentSearch['iNamePhrase'] == $iPhrase)
-                                                                               {
-                                                                               $aSearch = $aCurrentSearch;
-                                                                                       $aSearch['iSearchRank'] += 2;
-                                                                                       if (preg_match('#^[0-9]+$#', $sToken)) $aSearch['iSearchRank'] += 2;
-                                                                                       $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
-                                                                                       $aSearch['iNamePhrase'] = $iPhrase;
-                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
-                                                                               }
+                                                                                       if (!sizeof($aCurrentSearch['aName']) || $aCurrentSearch['iNamePhrase'] == $iPhrase)
+                                                                                       {
+                                                                                               $aSearch = $aCurrentSearch;
+                                                                                               $aSearch['iSearchRank'] += 2;
+                                                                                               if (preg_match('#^[0-9]+$#', $sToken)) $aSearch['iSearchRank'] += 2;
+                                                                                               $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+                                                                                               $aSearch['iNamePhrase'] = $iPhrase;
+                                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                                       }
                                                                                }
                                                                        }
                                                                }
                                        }
                                }
  
+                               if (CONST_Search_TryDroppedAddressTerms && sizeof($aStructuredQuery) > 0)
+                               {
+                                       $aCopyGroupedSearches = $aGroupedSearches;
+                                       foreach($aCopyGroupedSearches as $iGroup => $aSearches)
+                                       {
+                                               foreach($aSearches as $iSearch => $aSearch)
+                                               {
+                                                       $aReductionsList = array($aSearch['aAddress']);
+                                                       $iSearchRank = $aSearch['iSearchRank'];
+                                                       while(sizeof($aReductionsList) > 0)
+                                                       {
+                                                               $iSearchRank += 5;
+                                                               if ($iSearchRank > iMaxRank) break 3;
+                                                               $aNewReductionsList = array();
+                                                               foreach($aReductionsList as $aReductionsWordList)
+                                                               {
+                                                                       for ($iReductionWord = 0; $iReductionWord < sizeof($aReductionsWordList); $iReductionWord++)
+                                                                       {
+                                                                               $aReductionsWordListResult = array_merge(array_slice($aReductionsWordList, 0, $iReductionWord), array_slice($aReductionsWordList, $iReductionWord+1));
+                                                                               $aReverseSearch = $aSearch;
+                                                                               $aSearch['aAddress'] = $aReductionsWordListResult;
+                                                                               $aSearch['iSearchRank'] = $iSearchRank;
+                                                                               $aGroupedSearches[$iSearchRank][] = $aReverseSearch;
+                                                                               if (sizeof($aReductionsWordListResult) > 0)
+                                                                               {
+                                                                                       $aNewReductionsList[] = $aReductionsWordListResult;
+                                                                               }
+                                                                       }
+                                                               }
+                                                               $aReductionsList = $aNewReductionsList;
+                                                       }
+                                               }
+                                       }
+                                       ksort($aGroupedSearches);
+                               }
                                // Filter out duplicate searches
                                $aSearchHash = array();
                                foreach($aGroupedSearches as $iGroup => $aSearches)
                                                // Must have a location term
                                                if (!sizeof($aSearch['aName']) && !sizeof($aSearch['aAddress']) && !$aSearch['fLon'])
                                                {
-                                                       if ($aSearch['sCountryCode'] && !$aSearch['sClass'])
+                                                       if ($aSearch['sCountryCode'] && !$aSearch['sClass'] && !$aSearch['sHouseNumber'])
                                                        {
                                                                if (4 >= $iMinAddressRank && 4 <= $iMaxAddressRank)
                                                                {
                                                                if (!$aSearch['sClass']) continue;
                                                                if (CONST_Debug) var_dump('<hr>',$aSearch);
                                                                if (CONST_Debug) _debugDumpGroupedSearches(array($iGroupedRank => array($aSearch)), $aValidTokens);     
                                                                $sSQL = "select count(*) from pg_tables where tablename = 'place_classtype_".$aSearch['sClass']."_".$aSearch['sType']."'";
                                                                if ($oDB->getOne($sSQL))
                                                                {
                                                        else
                                                        {
                                                                $sSQL = "select place_id from placex where class='".$aSearch['sClass']."' and type='".$aSearch['sType']."'";
-                                                               $sSQL .= " and st_contains($sViewboxSmallSQL, geometry)";
+                                                               $sSQL .= " and st_contains($sViewboxSmallSQL, geometry) and linked_place_id is null";
                                                                if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";                                                             
                                                                if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
                                                                $sSQL .= " limit $iLimit";
                                                                else
                                                                        $sSQL .= " limit ".$iLimit;
  
 -                                                              if (CONST_Debug) { var_dump($sSQL); }
 +                                                              if (CONST_Debug) var_dump($sSQL);
 +                                                              $iStartTime = time();
                                                                $aViewBoxPlaceIDs = $oDB->getAll($sSQL);
                                                                if (PEAR::IsError($aViewBoxPlaceIDs))
                                                                {
                                                                        failInternalError("Could not get places for search terms.", $sSQL, $aViewBoxPlaceIDs);
                                                                }
 +                                                              if (time() - $iStartTime > 60) {
 +                                                                      file_put_contents(CONST_BasePath.'/log/long_queries.log', date('Y-m-d H:i:s', $iStartTime).' '.$sSQL."\n", FILE_APPEND);
 +                                                              }
 +
  //var_dump($aViewBoxPlaceIDs);
                                                                // Did we have an viewbox matches?
                                                                $aPlaceIDs = array();
                                                        {
                                                                $sPlaceIDs = join(',',$aPlaceIDs);
  
+                                                               $aClassPlaceIDs = array();
                                                                if (!$aSearch['sOperator'] || $aSearch['sOperator'] == 'name')
                                                                {
                                                                        // If they were searching for a named class (i.e. 'Kings Head pub') then we might have an extra match
                                                                        $sSQL = "select place_id from placex where place_id in ($sPlaceIDs) and class='".$aSearch['sClass']."' and type='".$aSearch['sType']."'";
+                                                                       $sSQL .= " and linked_place_id is null";
                                                                        if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";                                                             
                                                                        $sSQL .= " order by rank_search asc limit $iLimit";
                                                                        if (CONST_Debug) var_dump($sSQL);
-                                                                       $aPlaceIDs = $oDB->getCol($sSQL);
+                                                                       $aClassPlaceIDs = $oDB->getCol($sSQL);
                                                                }
                                                                
                                                                if (!$aSearch['sOperator'] || $aSearch['sOperator'] == 'near') // & in
                                                                        if ($iMaxRank < 9 && $bCacheTable)
                                                                        {
                                                                                // Try and get a polygon to search in instead
-       $sSQL = "select geometry from placex where place_id in ($sPlaceIDs) and rank_search < $iMaxRank + 5 and st_geometrytype(geometry) in ('ST_Polygon','ST_MultiPolygon') order by rank_search asc limit 1";
-       if (CONST_Debug) var_dump($sSQL);
-       $sPlaceGeom = $oDB->getOne($sSQL);
+                                                                               $sSQL = "select geometry from placex where place_id in ($sPlaceIDs) and rank_search < $iMaxRank + 5 and st_geometrytype(geometry) in ('ST_Polygon','ST_MultiPolygon') order by rank_search asc limit 1";
+                                                                               if (CONST_Debug) var_dump($sSQL);
+                                                                               $sPlaceGeom = $oDB->getOne($sSQL);
                                                                        }
                                                                        
                                                                        if ($sPlaceGeom)
                                                                        else
                                                                        {
                                                                                $iMaxRank += 5;
-                                                                       $sSQL = "select place_id from placex where place_id in ($sPlaceIDs) and rank_search < $iMaxRank";
-                                                                       if (CONST_Debug) var_dump($sSQL);
-                                                                       $aPlaceIDs = $oDB->getCol($sSQL);
-                                                                       $sPlaceIDs = join(',',$aPlaceIDs);
+                                                                               $sSQL = "select place_id from placex where place_id in ($sPlaceIDs) and rank_search < $iMaxRank";
+                                                                               if (CONST_Debug) var_dump($sSQL);
+                                                                               $aPlaceIDs = $oDB->getCol($sSQL);
+                                                                               $sPlaceIDs = join(',',$aPlaceIDs);
                                                                        }
  
                                                                        if ($sPlaceIDs || $sPlaceGeom)
                                                                                else if ($sPlaceIDs) $sOrderBySQL = "ST_Distance(l.centroid, f.geometry)";
                                                                                else if ($sPlaceGeom) $sOrderBysSQL = "ST_Distance(st_centroid('".$sPlaceGeom."'), l.centroid)";
                                                                                
-                                                                               $sSQL = "select distinct l.place_id".($sOrderBysSQL?','.$sOrderBysSQL:'')." from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." as l";
+                                                                               $sSQL = "select distinct l.place_id".($sOrderBySQL?','.$sOrderBySQL:'')." from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." as l";
                                                                                if ($sCountryCodesSQL) $sSQL .= " join placex as lp using (place_id)";
                                                                                if ($sPlaceIDs)
                                                                                {
                                                                                        $sSQL .= " and l.place_id not in (".join(',',$aExcludePlaceIDs).")";
                                                                                }
                                                                                if ($sCountryCodesSQL) $sSQL .= " and lp.country_code in ($sCountryCodesSQL)";
-                                                                               if ($sOrderBy) $sSQL .= "order by ".$OrderBysSQL." asc";
+                                                                               if ($sOrderBySQL) $sSQL .= "order by ".$sOrderBySQL." asc";
                                                                                if ($iOffset) $sSQL .= " offset $iOffset";
                                                                                $sSQL .= " limit $iLimit";
                                                                                if (CONST_Debug) var_dump($sSQL);
-                                                                               $aPlaceIDs = $oDB->getCol($sSQL);
+                                                                               $aClassPlaceIDs = array_merge($aClassPlaceIDs, $oDB->getCol($sSQL));
                                                                        }
                                                                        else
                                                                        {
                                                                                if ($iOffset) $sSQL .= " offset $iOffset";
                                                                                $sSQL .= " limit $iLimit";
                                                                                if (CONST_Debug) var_dump($sSQL);
-                                                                               $aPlaceIDs = $oDB->getCol($sSQL);
+                                                                               $aClassPlaceIDs = array_merge($aClassPlaceIDs, $oDB->getCol($sSQL));
                                                                        }
                                                                        }
                                                                }
+                                                               $aPlaceIDs = $aClassPlaceIDs;
                                                        }
                                                
                                                }
                                                }
                                                if ($iQueryLoop > 20) break;
                                        }
                                        //exit;
-                                       if (sizeof($aResultPlaceIDs)) break;
+                                       if (isset($aResultPlaceIDs) && sizeof($aResultPlaceIDs)) break;
                                        if ($iGroupLoop > 4) break;
                                        if ($iQueryLoop > 30) break;
                                }
  //exit;
                                // Did we find anything?        
-                               if (sizeof($aResultPlaceIDs))
+                               if (isset($aResultPlaceIDs) && sizeof($aResultPlaceIDs))
                                {
  //var_Dump($aResultPlaceIDs);exit;
                                        // Get the details for display (is this a redundant extra step?)
                        $sSQL = "select place_id,0 as numfeatures,st_area(geometry) as area,";
                        $sSQL .= "ST_Y(centroid) as centrelat,ST_X(centroid) as centrelon,";
                        $sSQL .= "ST_Y(ST_PointN(ST_ExteriorRing(Box2D(geometry)),4)) as minlat,ST_Y(ST_PointN(ST_ExteriorRing(Box2D(geometry)),2)) as maxlat,";
-                       $sSQL .= "ST_X(ST_PointN(ST_ExteriorRing(Box2D(geometry)),1)) as minlon,ST_X(ST_PointN(ST_ExteriorRing(Box2D(geometry)),3)) as maxlon,";
-                       $sSQL .= "ST_AsText(geometry) as outlinestring from placex where place_id = ".$aResult['place_id'].' and st_geometrytype(Box2D(geometry)) = \'ST_Polygon\'';
+                       $sSQL .= "ST_X(ST_PointN(ST_ExteriorRing(Box2D(geometry)),1)) as minlon,ST_X(ST_PointN(ST_ExteriorRing(Box2D(geometry)),3)) as maxlon";
+                       if ($bAsGeoJSON) $sSQL .= ",ST_AsGeoJSON(geometry) as asgeojson";
+                       if ($bAsKML) $sSQL .= ",ST_AsKML(geometry) as askml";
+                       if ($bAsSVG) $sSQL .= ",ST_AsSVG(geometry) as assvg";
+                       if ($bAsText || $bShowPolygons) $sSQL .= ",ST_AsText(geometry) as astext";
+                       $sSQL .= " from placex where place_id = ".$aResult['place_id'].' and st_geometrytype(Box2D(geometry)) = \'ST_Polygon\'';
                        $aPointPolygon = $oDB->getRow($sSQL);
                        if (PEAR::IsError($aPointPolygon))
                        {
                        }
                        if ($aPointPolygon['place_id'])
                        {
+                               if ($bAsGeoJSON) $aResult['asgeojson'] = $aPointPolygon['asgeojson'];
+                               if ($bAsKML) $aResult['askml'] = $aPointPolygon['askml'];
+                               if ($bAsSVG) $aResult['assvg'] = $aPointPolygon['assvg'];
+                               if ($bAsText) $aResult['astext'] = $aPointPolygon['astext'];
                                if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null ) {
                                        $aResult['lat'] = $aPointPolygon['centrelat'];
                                        $aResult['lon'] = $aPointPolygon['centrelon'];
                                }
-                               // Translate geometary string to point array
-                               if (preg_match('#POLYGON\\(\\(([- 0-9.,]+)#',$aPointPolygon['outlinestring'],$aMatch))
-                               {
-                                       preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/',$aMatch[1],$aPolyPoints,PREG_SET_ORDER);
-                               }
-                               elseif (preg_match('#POINT\\((-?[0-9.]+) (-?[0-9.]+)\\)#',$aPointPolygon['outlinestring'],$aMatch))
+                               if ($bShowPolygons) 
                                {
-                                       $fRadius = 0.01;
-                                       $iSteps = ($fRadius * 40000)^2;
-                                       $fStepSize = (2*pi())/$iSteps;
-                                       $aPolyPoints = array();
-                                       for($f = 0; $f < 2*pi(); $f += $fStepSize)
+                                       // Translate geometary string to point array
+                                       if (preg_match('#POLYGON\\(\\(([- 0-9.,]+)#',$aPointPolygon['astext'],$aMatch))
+                                       {
+                                               preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/',$aMatch[1],$aPolyPoints,PREG_SET_ORDER);
+                                       }
+                                       elseif (preg_match('#MULTIPOLYGON\\(\\(\\(([- 0-9.,]+)#',$aPointPolygon['astext'],$aMatch))
                                        {
-                                               $aPolyPoints[] = array('',$aMatch[1]+($fRadius*sin($f)),$aMatch[2]+($fRadius*cos($f)));
+                                               preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/',$aMatch[1],$aPolyPoints,PREG_SET_ORDER);
+                                       }
+                                       elseif (preg_match('#POINT\\((-?[0-9.]+) (-?[0-9.]+)\\)#',$aPointPolygon['astext'],$aMatch))
+                                       {
+                                               $fRadius = 0.01;
+                                               $iSteps = ($fRadius * 40000)^2;
+                                               $fStepSize = (2*pi())/$iSteps;
+                                               $aPolyPoints = array();
+                                               for($f = 0; $f < 2*pi(); $f += $fStepSize)
+                                               {
+                                                       $aPolyPoints[] = array('',$aMatch[1]+($fRadius*sin($f)),$aMatch[2]+($fRadius*cos($f)));
+                                               }
+                                               $aPointPolygon['minlat'] = $aPointPolygon['minlat'] - $fRadius;
+                                               $aPointPolygon['maxlat'] = $aPointPolygon['maxlat'] + $fRadius;
+                                               $aPointPolygon['minlon'] = $aPointPolygon['minlon'] - $fRadius;
+                                               $aPointPolygon['maxlon'] = $aPointPolygon['maxlon'] + $fRadius;
                                        }
-                                       $aPointPolygon['minlat'] = $aPointPolygon['minlat'] - $fRadius;
-                                       $aPointPolygon['maxlat'] = $aPointPolygon['maxlat'] + $fRadius;
-                                       $aPointPolygon['minlon'] = $aPointPolygon['minlon'] - $fRadius;
-                                       $aPointPolygon['maxlon'] = $aPointPolygon['maxlon'] + $fRadius;
                                }
  
                                // Output data suitable for display (points and a bounding box)
                }
  
                // Absolute limit on number of results
-               if (sizeof($aSearchResults) >= $iLimit) break;
+               if (sizeof($aSearchResults) >= $iFinalLimit) break;
        }
  
        $sDataDate = $oDB->getOne("select TO_CHAR(lastimportdate - '1 day'::interval,'YYYY/MM/DD') from import_status limit 1");