]> git.openstreetmap.org Git - nominatim.git/commitdiff
hstore names / refactoring
authorBrian Quinion <openstreetmap@brian.quinion.co.uk>
Wed, 27 Oct 2010 14:05:42 +0000 (14:05 +0000)
committerBrian Quinion <openstreetmap@brian.quinion.co.uk>
Wed, 27 Oct 2010 14:05:42 +0000 (14:05 +0000)
lib/db.php
settings/settings.php
sql/functions.sql
sql/tables.sql
utils/setup.php
website/search.php [new file with mode: 0755]

index 4f7336c132b3e4852dc711f7a3ffca13e1ab0c7f..a2bba81f587be2d40260efa45d8c2c154d04d23f 100644 (file)
@@ -1,15 +1,19 @@
 <?php
        require_once('DB.php');
 
 <?php
        require_once('DB.php');
 
-       // Get the database object
-       $oDB =& DB::connect(CONST_Database_DSN, false);
-       if (PEAR::IsError($oDB))
+       function &getDB()
        {
        {
-               fail($oDB->getMessage(), 'Unable to connect to the database');
+               // Get the database object
+               $oDB =& DB::connect(CONST_Database_DSN, false);
+               if (PEAR::IsError($oDB))
+               {
+                       fail($oDB->getMessage(), 'Unable to connect to the database');
+               }
+               $oDB->setFetchMode(DB_FETCHMODE_ASSOC);
+               $oDB->query("SET DateStyle TO 'sql,european'");
+               $oDB->query("SET client_encoding TO 'utf-8'");
+               return $oDB;
        }
        }
-       $oDB->setFetchMode(DB_FETCHMODE_ASSOC);
-       $oDB->query("SET DateStyle TO 'sql,european'");
-       $oDB->query("SET client_encoding TO 'utf-8'");
 
        function getDBQuoted($s)
        {
 
        function getDBQuoted($s)
        {
index f8075cb1e98abb37c29087b4809ed37fdc550f66..ca90b3c553095266f1bfa6aef13788cfc56275a4 100644 (file)
@@ -1,5 +1,8 @@
 <?php
 
 <?php
 
+       if (file_exists(CONST_BasePath.'/settings/local.php')) require_once(CONST_BasePath.'/settings/local.php');
+       if (isset($_GET['debug']) && $_GET['debug']) @define('CONST_Debug', true);
+
        // General settings
        @define('CONST_Debug', false);
        @define('CONST_Database_DSN', 'pgsql://@/nominatim');
        // General settings
        @define('CONST_Debug', false);
        @define('CONST_Database_DSN', 'pgsql://@/nominatim');
@@ -23,5 +26,3 @@
        @define('CONST_Search_AreaPolygons_Enabled', true);
 
        @define('CONST_Suggestions_Enabled', false);
        @define('CONST_Search_AreaPolygons_Enabled', true);
 
        @define('CONST_Suggestions_Enabled', false);
-
-
index 8776e8a7f95184961a8ea9e969ecf618ec61f389..f28e1d0ff9540ded4c92f4caf7efef1c4eb2f64b 100644 (file)
@@ -106,11 +106,11 @@ $$
 LANGUAGE plpgsql IMMUTABLE;
 
 CREATE OR REPLACE FUNCTION transliteration(text) RETURNS text
 LANGUAGE plpgsql IMMUTABLE;
 
 CREATE OR REPLACE FUNCTION transliteration(text) RETURNS text
-  AS '/home/brian/nominatim/live/osm2pgsql/gazetteer/gazetteer.so', 'transliteration'
+  AS '{modulepath}/nominatim.so', 'transliteration'
 LANGUAGE c IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION gettokenstring(text) RETURNS text
 LANGUAGE c IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION gettokenstring(text) RETURNS text
-  AS '/home/brian/nominatim/live/osm2pgsql/gazetteer/gazetteer.so', 'gettokenstring'
+  AS '{modulepath}/nominatim.so', 'gettokenstring'
 LANGUAGE c IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION make_standard_name(name TEXT) RETURNS TEXT
 LANGUAGE c IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION make_standard_name(name TEXT) RETURNS TEXT
@@ -1004,12 +1004,14 @@ BEGIN
         NEW.rank_search := 26;
         NEW.rank_address := NEW.rank_search;
       ELSEIF NEW.type in ('house','building') THEN
         NEW.rank_search := 26;
         NEW.rank_address := NEW.rank_search;
       ELSEIF NEW.type in ('house','building') THEN
-        NEW.rank_search := 28;
+        NEW.rank_search := 30;
         NEW.rank_address := NEW.rank_search;
       ELSEIF NEW.type in ('houses') THEN
         -- can't guarantee all required nodes loaded yet due to caching in osm2pgsql
         -- insert new point into place for each derived building
         --i := create_interpolation(NEW.osm_id, NEW.housenumber);
         NEW.rank_address := NEW.rank_search;
       ELSEIF NEW.type in ('houses') THEN
         -- can't guarantee all required nodes loaded yet due to caching in osm2pgsql
         -- insert new point into place for each derived building
         --i := create_interpolation(NEW.osm_id, NEW.housenumber);
+        NEW.rank_search := 28;
+        NEW.rank_address := 0;
       END IF;
 
     ELSEIF NEW.class = 'boundary' THEN
       END IF;
 
     ELSEIF NEW.class = 'boundary' THEN
@@ -1075,10 +1077,10 @@ BEGIN
       -- mark items within the geometry for re-indexing
 --    RAISE WARNING 'placex poly insert: % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type;
 -- work around bug in postgis
       -- mark items within the geometry for re-indexing
 --    RAISE WARNING 'placex poly insert: % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type;
 -- work around bug in postgis
-      update placex set indexed = false where indexed and (ST_Contains(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry)) 
-       AND rank_search > NEW.rank_search and ST_geometrytype(placex.geometry) = 'ST_Point';
-      update placex set indexed = false where indexed and (ST_Contains(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry)) 
-       AND rank_search > NEW.rank_search and ST_geometrytype(placex.geometry) != 'ST_Point';
+      update placex set indexed_status = 2 where (ST_Contains(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry)) 
+       AND rank_search > NEW.rank_search and indexed = 0 and ST_geometrytype(placex.geometry) = 'ST_Point';
+      update placex set indexed_status = 2 where (ST_Contains(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry)) 
+       AND rank_search > NEW.rank_search and indexed = 0 and ST_geometrytype(placex.geometry) != 'ST_Point';
     END IF;
   ELSE
     -- mark nearby items for re-indexing, where 'nearby' depends on the features rank_search and is a complete guess :(
     END IF;
   ELSE
     -- mark nearby items for re-indexing, where 'nearby' depends on the features rank_search and is a complete guess :(
@@ -1103,7 +1105,7 @@ BEGIN
     END IF;
     IF diameter > 0 THEN
 --      RAISE WARNING 'placex point insert: % % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type,diameter;
     END IF;
     IF diameter > 0 THEN
 --      RAISE WARNING 'placex point insert: % % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type,diameter;
-      update placex set indexed = false where indexed and rank_search > NEW.rank_search and ST_DWithin(placex.geometry, NEW.geometry, diameter);
+      update placex set indexed = 2 where indexed and rank_search > NEW.rank_search and ST_DWithin(placex.geometry, NEW.geometry, diameter);
     END IF;
 
   END IF;
     END IF;
 
   END IF;
@@ -1272,6 +1274,7 @@ BEGIN
             -- Try and find a way that is close roughly parellel to this line
             FOR relation IN SELECT place_id FROM placex
               WHERE ST_DWithin(location.geometry, placex.geometry, 0.001) and placex.rank_search = 26
             -- Try and find a way that is close roughly parellel to this line
             FOR relation IN SELECT place_id FROM placex
               WHERE ST_DWithin(location.geometry, placex.geometry, 0.001) and placex.rank_search = 26
+                and st_geometrytype(location.geometry) in ('ST_LineString')
               ORDER BY (ST_distance(placex.geometry, ST_Line_Interpolate_Point(location.geometry,0))+
                         ST_distance(placex.geometry, ST_Line_Interpolate_Point(location.geometry,0.5))+
                         ST_distance(placex.geometry, ST_Line_Interpolate_Point(location.geometry,1))) ASC limit 1
               ORDER BY (ST_distance(placex.geometry, ST_Line_Interpolate_Point(location.geometry,0))+
                         ST_distance(placex.geometry, ST_Line_Interpolate_Point(location.geometry,0.5))+
                         ST_distance(placex.geometry, ST_Line_Interpolate_Point(location.geometry,1))) ASC limit 1
@@ -1460,38 +1463,11 @@ BEGIN
 --END IF;
 
   -- mark everything linked to this place for re-indexing
 --END IF;
 
   -- mark everything linked to this place for re-indexing
-  UPDATE placex set indexed = false from place_addressline where address_place_id = OLD.place_id and placex.place_id = place_addressline.place_id and indexed;
+  UPDATE placex set indexed_status = 2 from place_addressline where address_place_id = OLD.place_id 
+    and placex.place_id = place_addressline.place_id and indexed_status = 0;
 
   -- do the actual delete
   DELETE FROM location_area where place_id = OLD.place_id;
 
   -- do the actual delete
   DELETE FROM location_area where place_id = OLD.place_id;
-  DELETE FROM location_point where place_id = OLD.place_id;
-  DELETE FROM location_point_0 where place_id = OLD.place_id;
-  DELETE FROM location_point_1 where place_id = OLD.place_id;
-  DELETE FROM location_point_2 where place_id = OLD.place_id;
-  DELETE FROM location_point_3 where place_id = OLD.place_id;
-  DELETE FROM location_point_4 where place_id = OLD.place_id;
-  DELETE FROM location_point_5 where place_id = OLD.place_id;
-  DELETE FROM location_point_6 where place_id = OLD.place_id;
-  DELETE FROM location_point_7 where place_id = OLD.place_id;
-  DELETE FROM location_point_8 where place_id = OLD.place_id;
-  DELETE FROM location_point_9 where place_id = OLD.place_id;
-  DELETE FROM location_point_10 where place_id = OLD.place_id;
-  DELETE FROM location_point_11 where place_id = OLD.place_id;
-  DELETE FROM location_point_12 where place_id = OLD.place_id;
-  DELETE FROM location_point_13 where place_id = OLD.place_id;
-  DELETE FROM location_point_14 where place_id = OLD.place_id;
-  DELETE FROM location_point_15 where place_id = OLD.place_id;
-  DELETE FROM location_point_16 where place_id = OLD.place_id;
-  DELETE FROM location_point_17 where place_id = OLD.place_id;
-  DELETE FROM location_point_18 where place_id = OLD.place_id;
-  DELETE FROM location_point_19 where place_id = OLD.place_id;
-  DELETE FROM location_point_20 where place_id = OLD.place_id;
-  DELETE FROM location_point_21 where place_id = OLD.place_id;
-  DELETE FROM location_point_22 where place_id = OLD.place_id;
-  DELETE FROM location_point_23 where place_id = OLD.place_id;
-  DELETE FROM location_point_24 where place_id = OLD.place_id;
-  DELETE FROM location_point_25 where place_id = OLD.place_id;
-  DELETE FROM location_point_26 where place_id = OLD.place_id;
   DELETE FROM search_name where place_id = OLD.place_id;
   DELETE FROM place_addressline where place_id = OLD.place_id;
   DELETE FROM place_addressline where address_place_id = OLD.place_id;
   DELETE FROM search_name where place_id = OLD.place_id;
   DELETE FROM place_addressline where place_id = OLD.place_id;
   DELETE FROM place_addressline where address_place_id = OLD.place_id;
@@ -1663,12 +1639,12 @@ BEGIN
     IF st_area(NEW.geometry) < 1 AND st_area(existinggeometry) < 1 THEN
 
       -- re-index points that have moved in / out of the polygon, could be done as a single query but postgres gets the index usage wrong
     IF st_area(NEW.geometry) < 1 AND st_area(existinggeometry) < 1 THEN
 
       -- re-index points that have moved in / out of the polygon, could be done as a single query but postgres gets the index usage wrong
-      update placex set indexed = false where indexed and 
+      update placex set indexed_status = 2 where indexed_status = 0 and 
           (ST_Contains(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry))
           AND NOT (ST_Contains(existinggeometry, placex.geometry) OR ST_Intersects(existinggeometry, placex.geometry))
           AND rank_search > NEW.rank_search;
 
           (ST_Contains(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry))
           AND NOT (ST_Contains(existinggeometry, placex.geometry) OR ST_Intersects(existinggeometry, placex.geometry))
           AND rank_search > NEW.rank_search;
 
-      update placex set indexed = false where indexed and 
+      update placex set indexed_status = 2 where indexed_status = 0 and 
           (ST_Contains(existinggeometry, placex.geometry) OR ST_Intersects(existinggeometry, placex.geometry))
           AND NOT (ST_Contains(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry))
           AND rank_search > NEW.rank_search;
           (ST_Contains(existinggeometry, placex.geometry) OR ST_Intersects(existinggeometry, placex.geometry))
           AND NOT (ST_Contains(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry))
           AND rank_search > NEW.rank_search;
@@ -1694,8 +1670,8 @@ BEGIN
     IF NOT update_location_nameonly(existingplacex.place_id, NEW.name) THEN
 
       IF st_area(NEW.geometry) < 0.5 THEN
     IF NOT update_location_nameonly(existingplacex.place_id, NEW.name) THEN
 
       IF st_area(NEW.geometry) < 0.5 THEN
-        UPDATE placex set indexed = false from place_addressline where address_place_id = existingplacex.place_id 
-          and placex.place_id = place_addressline.place_id and indexed;
+        UPDATE placex set indexed_status = 2 from place_addressline where address_place_id = existingplacex.place_id 
+          and placex.place_id = place_addressline.place_id and indexed_status = 0;
       END IF;
 
     END IF;
       END IF;
 
     END IF;
@@ -1716,8 +1692,8 @@ BEGIN
 
       -- performance, can't take the load of re-indexing a whole country / huge area
       IF st_area(NEW.geometry) < 0.5 THEN
 
       -- performance, can't take the load of re-indexing a whole country / huge area
       IF st_area(NEW.geometry) < 0.5 THEN
-        UPDATE placex set indexed = false from place_addressline where address_place_id = existingplacex.place_id 
-          and placex.place_id = place_addressline.place_id and indexed;
+        UPDATE placex set indexed_status = 2 from place_addressline where address_place_id = existingplacex.place_id 
+          and placex.place_id = place_addressline.place_id and indexed_status = 0;
       END IF;
 
     END IF;
       END IF;
 
     END IF;
@@ -1773,14 +1749,14 @@ DECLARE
   found BOOLEAN;
 BEGIN
 
   found BOOLEAN;
 BEGIN
 
-  IF (array_upper(name, 1) is null) THEN
-    return null;
+  IF name is null THEN
+    RETURN null;
   END IF;
 
   search := languagepref;
 
   FOR j IN 1..array_upper(search, 1) LOOP
   END IF;
 
   search := languagepref;
 
   FOR j IN 1..array_upper(search, 1) LOOP
-    IF name ? search[j] AND trim(name->search[j] != '') THEN
+    IF name ? search[j] AND trim(name->search[j]) != '' THEN
       return trim(name->search[j]);
     END IF;
   END LOOP;
       return trim(name->search[j]);
     END IF;
   END LOOP;
@@ -1869,28 +1845,24 @@ BEGIN
   search := languagepref;
   result := '{}';
 
   search := languagepref;
   result := '{}';
 
---  UPDATE placex set indexed = false where indexed = true and place_id = for_place_id;
-  UPDATE placex set indexed = true where indexed = false and place_id = for_place_id;
-
   select country_code,housenumber,rank_address from placex where place_id = for_place_id into searchcountrycode,searchhousenumber,searchrankaddress;
 
   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,
   select country_code,housenumber,rank_address from placex where place_id = for_place_id into searchcountrycode,searchhousenumber,searchrankaddress;
 
   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,
-      CASE WHEN type = 'postcode' THEN 'name'->postcode ELSE name END as name,
+      CASE WHEN type = 'postcode' THEN 'name' => postcode ELSE name END as name,
       distance,length(name::text) as namelength 
       from place_addressline join placex on (address_place_id = placex.place_id) 
       where place_addressline.place_id = for_place_id and ((rank_address > 0 AND rank_address < searchrankaddress) OR address_place_id = for_place_id)
       and (placex.country_code IS NULL OR searchcountrycode IS NULL OR placex.country_code = searchcountrycode OR rank_address < 4)
       order by rank_address desc,fromarea desc,distance asc,rank_search desc,namelength desc
   LOOP
       distance,length(name::text) as namelength 
       from place_addressline join placex on (address_place_id = placex.place_id) 
       where place_addressline.place_id = for_place_id and ((rank_address > 0 AND rank_address < searchrankaddress) OR address_place_id = for_place_id)
       and (placex.country_code IS NULL OR searchcountrycode IS NULL OR placex.country_code = searchcountrycode OR rank_address < 4)
       order by rank_address desc,fromarea desc,distance asc,rank_search desc,namelength desc
   LOOP
-    IF array_upper(search, 1) IS NOT NULL AND array_upper(location.name, 1) IS NOT NULL THEN
+    IF array_upper(search, 1) IS NOT NULL AND location.name IS NOT NULL THEN
       FOR j IN 1..array_upper(search, 1) LOOP
       FOR j IN 1..array_upper(search, 1) LOOP
-        FOR k IN 1..array_upper(location.name, 1) LOOP
-          IF (found > location.rank_address AND location.name[k].key = search[j] AND location.name[k].value != '') AND NOT result && ARRAY[trim(location.name[k].value)] THEN
-            result[(100 - location.rank_address)] := trim(location.name[k].value);
-            found := location.rank_address;
-          END IF;
-        END LOOP;
+        IF (found > location.rank_address AND location.name ? search[j] AND location.name -> search[j] != ''
+            AND NOT result && ARRAY[location.name -> search[j]]) THEN
+          result[(100 - location.rank_address)] := trim(location.name -> search[j]);
+          found := location.rank_address;
+        END IF;
       END LOOP;
     END IF;
   END LOOP;
       END LOOP;
     END IF;
   END LOOP;
@@ -1931,8 +1903,7 @@ BEGIN
   search := languagepref;
   result := '{}';
 
   search := languagepref;
   result := '{}';
 
---  UPDATE placex set indexed = false where indexed = true and place_id = for_place_id;
-  UPDATE placex set indexed = true where indexed = false and place_id = for_place_id;
+  UPDATE placex set indexed_status = 0 where indexed_status > 0 and place_id = for_place_id;
 
   select country_code,housenumber from placex where place_id = for_place_id into searchcountrycode,searchhousenumber;
 
 
   select country_code,housenumber from placex where place_id = for_place_id into searchcountrycode,searchhousenumber;
 
index a5b83c30fea778fe3cedab5ebd4f6f3e315ee94e..62a4eb36a1398a249809ba1234c3207e8a2b2955 100644 (file)
@@ -63,7 +63,8 @@ CREATE TABLE word (
   class text,
   type text,
   country_code varchar(2),
   class text,
   type text,
   country_code varchar(2),
-  search_name_count INTEGER
+  search_name_count INTEGER,
+  operator TEXT
   );
 SELECT AddGeometryColumn('word', 'location', 4326, 'GEOMETRY', 2);
 CREATE INDEX idx_word_word_id on word USING BTREE (word_id);
   );
 SELECT AddGeometryColumn('word', 'location', 4326, 'GEOMETRY', 2);
 CREATE INDEX idx_word_word_id on word USING BTREE (word_id);
index bc8f14f72811a22e3efe53700700615719144164..72dd9c9a12e40e09ba2afb08c2d02a50ff941729 100755 (executable)
                array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
                array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
 
                array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
                array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
 
-               array('create-db', '', 0, 1, 0, 0, 'bool', 'Build a blank nominatim db'),
-               array('load-data', '', 0, 1, 1, 1, 'realpath', 'Import a osm file'),
+               array('all', '', 0, 1, 1, 1, 'realpath', 'Do the complete process'),
+
+               array('create-db', '', 0, 1, 0, 0, 'bool', 'Create nominatim db'),
+               array('setup-db', '', 0, 1, 0, 0, 'bool', 'Build a blank nominatim db'),
+               array('import-data', '', 0, 1, 1, 1, 'realpath', 'Import a osm file'),
+               array('create-functions', '', 0, 1, 0, 0, 'bool', 'Create functions'),
+               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('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'),
        );
        getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
 
        $bDidSomething = false;
 
        );
        getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
 
        $bDidSomething = false;
 
-       if ($aCMDResult['create-db'])
+       if ($aCMDResult['create-db'] || isset($aCMDResult['all']))
+       {
+               $bDidSomething = true;
+               $oDB =& DB::connect(CONST_Database_DSN, false);
+               if (!PEAR::isError($oDB))
+               {
+                       fail('database already exists');
+               }
+               passthru('createdb nominatim');
+       }
+
+       if ($aCMDResult['create-db'] || isset($aCMDResult['all']))
        {
                $bDidSomething = true;
                // TODO: path detection, detection memory, etc.
        {
                $bDidSomething = true;
                // TODO: path detection, detection memory, etc.
-//             passthru('createdb nominatim');
+
+               $oDB =& getDB();
                passthru('createlang plpgsql nominatim');
                passthru('createlang plpgsql nominatim');
-               passthru('psql -f '.CONST_Path_Postgresql_Contrib.'/_int.sql nominatim');
-               passthru('psql -f '.CONST_Path_Postgresql_Contrib.'/hstore.sql nominatim');
-               passthru('psql -f '.CONST_Path_Postgresql_Postgis.'/postgis.sql nominatim');
-               passthru('psql -f '.CONST_Path_Postgresql_Postgis.'/spatial_ref_sys.sql nominatim');
-               passthru('psql -f '.CONST_BasePath.'/data/country_name.sql nominatim');
-               passthru('psql -f '.CONST_BasePath.'/data/country_osm_grid.sql nominatim');
-               passthru('psql -f '.CONST_BasePath.'/data/gb_postcode.sql nominatim');
-               passthru('psql -f '.CONST_BasePath.'/data/us_statecounty.sql nominatim');
-               passthru('psql -f '.CONST_BasePath.'/data/us_state.sql nominatim');
-               passthru('psql -f '.CONST_BasePath.'/data/worldboundaries.sql nominatim');
+               pgsqlRunScriptFile(CONST_Path_Postgresql_Contrib.'/_int.sql');
+               pgsqlRunScriptFile(CONST_Path_Postgresql_Contrib.'/hstore.sql');
+               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_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/worldboundaries.sql');
+       }
+
+       if (isset($aCMDResult['all']) && !isset($aCMDResult['import-data'])) $aCMDResult['import-data'] = $aCMDResult['all'];
+       if (isset($aCMDResult['import-data']) && $aCMDResult['import-data'])
+       {
+               $bDidSomething = true;
+               passthru(CONST_BasePath.'/osm2pgsql/osm2pgsql -lsc -O gazetteer -C 10000 --hstore -d nominatim '.$aCMDResult['import-data']);
+       }
+
+       if ($aCMDResult['create-functions'] || isset($aCMDResult['all']))
+       {
+               $bDidSomething = true;
+               $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
+               $sTemplate = str_replace('{modulepath}',CONST_BasePath.'/module', $sTemplate);
+               pgsqlRunScript($sTemplate);
        }
 
        }
 
-       if (isset($aCMDResult['load-data']) && $aCMDResult['load-data'])
+       if ($aCMDResult['create-tables'] || isset($aCMDResult['all']))
        {
                $bDidSomething = true;
        {
                $bDidSomething = true;
-               passthru(CONST_BasePath.'/osm2pgsql/osm2pgsql -lsc -O gazetteer -C 10000 --hstore -d nominatim '.$aCMDResult['load-data']);
-               passthru('psql -f '.CONST_BasePath.'/sql/functions.sql nominatim');
-               passthru('psql -f '.CONST_BasePath.'/sql/tables.sql nominatim');
+               pgsqlRunScriptFile(CONST_BasePath.'/sql/tables.sql');
        }
 
        }
 
-       if ($aCMDResult['create-partitions'])
+       if ($aCMDResult['create-partitions'] || isset($aCMDResult['all']))
        {
                $bDidSomething = true;
        {
                $bDidSomething = true;
+               $oDB =& getDB();
                $sSQL = 'select distinct country_code from country_name order by country_code';
                $aPartitions = $oDB->getCol($sSQL);
                if (PEAR::isError($aPartitions))
                $sSQL = 'select distinct country_code from country_name order by country_code';
                $aPartitions = $oDB->getCol($sSQL);
                if (PEAR::isError($aPartitions))
                        }
                        $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
                }
                        }
                        $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
                }
-               echo $sTemplate;
-               exit;
+               pgsqlRunScript($sTemplate);
+       }
+
+       if ($aCMDResult['load-data'] || isset($aCMDResult['all']))
+       {
+               $bDidSomething = true;
+               pgsqlRunScriptFile(CONST_BasePath.'/sql/loaddata.sql');
        }
 
        if (!$bDidSomething)
        {
                showUsage($aCMDOptions, true);
        }
        }
 
        if (!$bDidSomething)
        {
                showUsage($aCMDOptions, true);
        }
+
+       function pgsqlRunScriptFile($sFilename)
+       {
+               if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
+
+               // Convert database DSN to psql paramaters
+               $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
+               $sCMD = 'psql -f '.$sFilename.' '.$aDSNInfo['database'];
+
+               $aDescriptors = array(
+                       0 => array('pipe', 'r'),
+                       1 => array('pipe', 'w'),
+                       2 => array('file', '/dev/null', 'a')
+               );
+               $ahPipes = null;
+               $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
+               if (!is_resource($hProcess)) fail('unable to start pgsql');
+
+               fclose($ahPipes[0]);
+
+               // TODO: error checking
+               while(!feof($ahPipes[1]))
+               {
+                       echo fread($ahPipes[1], 4096);
+               }
+               fclose($ahPipes[1]);
+
+               proc_close($hProcess);
+       }
+
+       function pgsqlRunScript($sScript)
+       {
+               // Convert database DSN to psql paramaters
+               $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
+               $sCMD = 'psql '.$aDSNInfo['database'];
+
+               $aDescriptors = array(
+                       0 => array('pipe', 'r'),
+                       1 => array('pipe', 'w'),
+                       2 => array('file', '/dev/null', 'a')
+               );
+               $ahPipes = null;
+               $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
+               if (!is_resource($hProcess)) fail('unable to start pgsql');
+
+               fwrite($ahPipes[0], $sScript);
+               fclose($ahPipes[0]);
+
+               // TODO: error checking
+               while(!feof($ahPipes[1]))
+               {
+                       echo fread($ahPipes[1], 4096);
+               }
+               fclose($ahPipes[1]);
+
+               proc_close($hProcess);
+       }
diff --git a/website/search.php b/website/search.php
new file mode 100755 (executable)
index 0000000..a123e83
--- /dev/null
@@ -0,0 +1,1024 @@
+<?php
+       require_once(dirname(dirname(__FILE__)).'/lib/init-website.php');
+       require_once(CONST_BasePath.'/lib/log.php');
+
+       ini_set('memory_limit', '200M');
+       $oDB =& getDB();
+
+       // Display defaults
+       $fLat = CONST_Default_Lat;
+       $fLon = CONST_Default_Lon;
+       $iZoom = CONST_Default_Zoom;
+       $bBoundingBoxSearch = isset($_GET['bounded'])?(bool)$_GET['bounded']:false;
+       $sOutputFormat = 'html';
+       $aSearchResults = array();
+       $aExcludePlaceIDs = array();
+       $sSuggestion = $sSuggestionURL = false;
+       $bDeDupe = isset($_GET['dedupe'])?(bool)$_GET['dedupe']:true;
+       $bReverseInPlan = false;
+       $iLimit = isset($_GET['limit'])?(int)$_GET['limit']:10;
+       $iMaxRank = 20;
+       if ($iLimit > 100) $iLimit = 100;
+
+       // Format for output
+       if (isset($_GET['format']) && ($_GET['format'] == 'html' || $_GET['format'] == 'xml' || $_GET['format'] == 'json' ||  $_GET['format'] == 'jsonv2'))
+       {
+               $sOutputFormat = $_GET['format'];
+       }
+
+       // Show / use polygons
+       $bShowPolygons = isset($_GET['polygon']) && $_GET['polygon'];
+
+       // Show address breakdown
+       $bShowAddressDetails = isset($_GET['addressdetails']) && $_GET['addressdetails'];
+
+       // Prefered language    
+       $aLangPrefOrder = getPrefferedLangauges();
+       if (isset($aLangPrefOrder['name:de'])) $bReverseInPlan = true;
+       $sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted",$aLangPrefOrder))."]";
+
+       if (isset($_GET['exclude_place_ids']) && $_GET['exclude_place_ids'])
+       {
+               foreach(explode(',',$_GET['exclude_place_ids']) as $iExcludedPlaceID)
+               {
+                       $iExcludedPlaceID = (int)$iExcludedPlaceID;
+                       if ($iExcludedPlaceID) $aExcludePlaceIDs[$iExcludedPlaceID] = $iExcludedPlaceID;
+               }
+       }
+               
+       // Search query
+       $sQuery = (isset($_GET['q'])?trim($_GET['q']):'');
+       if (!$sQuery && $_SERVER['PATH_INFO'] && $_SERVER['PATH_INFO'][0] == '/')
+       {
+               $sQuery = substr($_SERVER['PATH_INFO'], 1);
+
+               // reverse order of '/' seperated string
+               $aPhrases = explode('/', $sQuery);              
+               $aPhrases = array_reverse($aPhrases); 
+               $sQuery = join(', ',$aPhrases);
+       }
+
+       if ($sQuery)
+       {
+               $hLog = logStart($oDB, 'search', $sQuery, $aLangPrefOrder);
+
+               // If we have a view box create the SQL
+               // Small is the actual view box, Large is double (on each axis) that 
+               $sViewboxCentreSQL = $sViewboxSmallSQL = $sViewboxLargeSQL = false;
+               if (isset($_GET['viewboxlbrt']) && $_GET['viewboxlbrt'])
+               {
+                       $aCoOrdinatesLBRT = explode(',',$_GET['viewboxlbrt']);
+                       $_GET['viewbox'] = $aCoOrdinatesLBRT[0].','.$aCoOrdinatesLBRT[3].','.$aCoOrdinatesLBRT[2].','.$aCoOrdinatesLBRT[1];
+               }
+               if (isset($_GET['viewbox']) && $_GET['viewbox'])
+               {
+                       $aCoOrdinates = explode(',',$_GET['viewbox']);
+                       $sViewboxSmallSQL = "ST_SetSRID(ST_MakeBox2D(ST_Point(".(float)$aCoOrdinates[0].",".(float)$aCoOrdinates[1]."),ST_Point(".(float)$aCoOrdinates[2].",".(float)$aCoOrdinates[3].")),4326)";
+                       $fHeight = $aCoOrdinates[0]-$aCoOrdinates[2];
+                       $fWidth = $aCoOrdinates[1]-$aCoOrdinates[3];
+                       $aCoOrdinates[0] += $fHeight;
+                       $aCoOrdinates[2] -= $fHeight;
+                       $aCoOrdinates[1] += $fWidth;
+                       $aCoOrdinates[3] -= $fWidth;
+                       $sViewboxLargeSQL = "ST_SetSRID(ST_MakeBox2D(ST_Point(".(float)$aCoOrdinates[0].",".(float)$aCoOrdinates[1]."),ST_Point(".(float)$aCoOrdinates[2].",".(float)$aCoOrdinates[3].")),4326)";
+               }
+               if (isset($_GET['route']) && $_GET['route'] && isset($_GET['routewidth']) && $_GET['routewidth'])
+               {
+                       $aPoints = explode(',',$_GET['route']);
+                       if (sizeof($aPoints) % 2 != 0)
+                       {
+                               echo "Uneven number of points";
+                               exit;
+                       }
+                       $sViewboxCentreSQL = "ST_SetSRID('LINESTRING(";
+                       $fPrevCoord = false;
+                       foreach($aPoints as $i => $fPoint)
+                       {
+                               if ($i%2)
+                               {
+                                       if ($i != 1) $sViewboxCentreSQL .= ",";
+                                       $sViewboxCentreSQL .= ((float)$fPoint).' '.$fPrevCoord;
+                               }
+                               else
+                               {
+                                       $fPrevCoord = (float)$fPoint;
+                               }
+                       }
+                       $sViewboxCentreSQL .= ")'::geometry,4326)";
+
+                       $sSQL = "select st_buffer(".$sViewboxCentreSQL.",".(float)($_GET['routewidth']/69).")";
+                       $sViewboxSmallSQL = $oDB->getOne($sSQL);
+                       if (PEAR::isError($sViewboxSmallSQL))
+                       {
+                               var_dump($sViewboxSmallSQL);
+                               exit;
+                       }
+                       $sViewboxSmallSQL = "'".$sViewboxSmallSQL."'::geometry";
+
+                       $sSQL = "select st_buffer(".$sViewboxCentreSQL.",".(float)($_GET['routewidth']/30).")";
+                       $sViewboxLargeSQL = $oDB->getOne($sSQL);
+                       if (PEAR::isError($sViewboxLargeSQL))
+                       {
+                               var_dump($sViewboxLargeSQL);
+                               exit;
+                       }
+                       $sViewboxLargeSQL = "'".$sViewboxLargeSQL."'::geometry";
+               }
+
+               // Do we have anything that looks like a lat/lon pair?
+               if (preg_match('/\\b([NS])[ ]+([0-9]+[0-9.]*)[ ]+([0-9.]+)?[, ]+([EW])[ ]+([0-9]+)[ ]+([0-9]+[0-9.]*)?\\b/', $sQuery, $aData))
+               {
+                       $_GET['nearlat'] = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60);
+                       $_GET['nearlon'] = ($aData[4]=='E'?1:-1) * ($aData[5] + $aData[6]/60);
+                       $sQuery = trim(str_replace($aData[0], ' ', $sQuery));
+               }
+               elseif (preg_match('/\\b([0-9]+)[ ]+([0-9]+[0-9.]*)?[ ]+([NS])[, ]+([0-9]+)[ ]+([0-9]+[0-9.]*)?[ ]+([EW])\\b/', $sQuery, $aData))
+               {
+                       $_GET['nearlat'] = ($aData[3]=='N'?1:-1) * ($aData[1] + $aData[2]/60);
+                       $_GET['nearlon'] = ($aData[6]=='E'?1:-1) * ($aData[4] + $aData[5]/60);
+                       $sQuery = trim(str_replace($aData[0], ' ', $sQuery));
+               }
+               elseif (preg_match('/(\\[|\\b)(-?[0-9]+[0-9.]*)[, ]+(-?[0-9]+[0-9.]*)(\\]|\\b])/', $sQuery, $aData))
+               {
+                       $_GET['nearlat'] = $aData[2];
+                       $_GET['nearlon'] = $aData[3];
+                       $sQuery = trim(str_replace($aData[0], ' ', $sQuery));
+               }
+
+               if ($sQuery)
+               {
+
+                       // Start with a blank search
+                       $aSearches = array(
+                               array('iSearchRank' => 0, 'iNamePhrase' => 0, 'sCountryCode' => false, 'aName'=>array(), 'aAddress'=>array(), 
+                                       'sOperator'=>'', 'aFeatureName' => array(), 'sClass'=>'', 'sType'=>'', 'sHouseNumber'=>'', 'fLat'=>'', 'fLon'=>'', 'fRadius'=>'')
+                       );
+
+                       $sNearPointSQL = false;
+                       if (isset($_GET['nearlat']) && isset($_GET['nearlon']))
+                       {
+                               $sNearPointSQL = "ST_SetSRID(ST_Point(".(float)$_GET['nearlon'].",".$_GET['nearlat']."),4326)";
+                               $aSearches[0]['fLat'] = (float)$_GET['nearlat'];
+                               $aSearches[0]['fLon'] = (float)$_GET['nearlon'];
+                               $aSearches[0]['fRadius'] = 0.1;
+                       }
+
+                       $bSpecialTerms = false;
+                       preg_match_all('/\\[(.*)=(.*)\\]/', $sQuery, $aSpecialTermsRaw, PREG_SET_ORDER);
+                       $aSpecialTerms = array();
+                       foreach($aSpecialTermsRaw as $aSpecialTerm)
+                       {
+                               $sQuery = str_replace($aSpecialTerm[0], ' ', $sQuery);
+                               $aSpecialTerms[strtolower($aSpecialTerm[1])] = $aSpecialTerm[2];
+                       }
+
+                       preg_match_all('/\\[([a-zA-Z]*)\\]/', $sQuery, $aSpecialTermsRaw, PREG_SET_ORDER);
+                       $aSpecialTerms = array();
+                       foreach($aSpecialTermsRaw as $aSpecialTerm)
+                       {
+                               $sQuery = str_replace($aSpecialTerm[0], ' ', $sQuery);
+                               $sToken = $oDB->getOne("select make_standard_name('".$aSpecialTerm[1]."') as string");
+                               $sSQL = 'select * from (select word_id,word_token, word, class, type, location, country_code, operator';
+                               $sSQL .= ' from word where word_token in (\' '.$sToken.'\')) as x where (class is not null and class != \'place\') or country_code is not null';
+                               $aSearchWords = $oDB->getAll($sSQL);
+                               $aNewSearches = array();
+                               foreach($aSearches as $aSearch)
+                               {
+                                       foreach($aSearchWords as $aSearchTerm)
+                                       {
+                                               $aNewSearch = $aSearch;                 
+                                               if ($aSearchTerm['country_code'])
+                                               {
+                                                       $aNewSearch['sCountryCode'] = strtolower($aSearchTerm['country_code']);
+                                                       $aNewSearches[] = $aNewSearch;
+                                                       $bSpecialTerms = true;
+                                               }
+                                               if ($aSearchTerm['class'])
+                                               {
+                                                       $aNewSearch['sClass'] = $aSearchTerm['class'];
+                                                       $aNewSearch['sType'] = $aSearchTerm['type'];
+                                                       $aNewSearches[] = $aNewSearch;
+                                                       $bSpecialTerms = true;
+                                               }
+                                       }
+                               }
+                               $aSearches = $aNewSearches;
+                       }
+
+                       // Split query into phrases
+                       // Commas are used to reduce the search space by indicating where phrases split
+                       $aPhrases = explode(',',$sQuery);
+
+                       // Convert each phrase to standard form
+                       // Create a list of standard words
+                       // Get all 'sets' of words
+                       // Generate a complete list of all 
+                       $aTokens = array();
+                       foreach($aPhrases as $iPhrase => $sPhrase)
+                       {
+                               $aPhrase = $oDB->getRow("select make_standard_name('".pg_escape_string($sPhrase)."') as string");
+                               if (PEAR::isError($aPhrase))
+                               {
+                                       var_dump($aPhrase);
+                                       exit;
+                               }
+                               if (trim($aPhrase['string']))
+                               {
+                                       $aPhrases[$iPhrase] = $aPhrase;
+                                       $aPhrases[$iPhrase]['words'] = explode(' ',$aPhrases[$iPhrase]['string']);
+                                       $aPhrases[$iPhrase]['wordsets'] = getWordSets($aPhrases[$iPhrase]['words']);
+                                       $aTokens = array_merge($aTokens, getTokensFromSets($aPhrases[$iPhrase]['wordsets']));
+                               }
+                               else
+                               {
+                                       unset($aPhrases[$iPhrase]);
+                               }
+                       }                       
+
+                       // reindex phrases - we make assumptions later on
+                       $aPhrases = array_values($aPhrases);
+
+                       if (sizeof($aTokens))
+                       {
+
+                       // Check which tokens we have, get the ID numbers                       
+                       $sSQL = 'select word_id,word_token, word, class, type, location, country_code, operator';
+                       $sSQL .= ' from word where word_token in ('.join(',',array_map("getDBQuoted",$aTokens)).')';
+//                     $sSQL .= ' group by word_token, word, class, type, location,country_code';
+
+                       if (CONST_Debug) var_Dump($sSQL);
+
+                       $aValidTokens = array();
+                       if (sizeof($aTokens))
+                               $aDatabaseWords = $oDB->getAll($sSQL);
+                       else
+                               $aDatabaseWords = array();
+                       if (PEAR::IsError($aDatabaseWords))
+                       {
+                               var_dump($sSQL, $aDatabaseWords);
+                               exit;
+                       }
+                       foreach($aDatabaseWords as $aToken)
+                       {
+                               if (isset($aValidTokens[$aToken['word_token']]))
+                               {
+                                       $aValidTokens[$aToken['word_token']][] = $aToken;
+                               }
+                               else
+                               {
+                                       $aValidTokens[$aToken['word_token']] = array($aToken);
+                               }
+                       }
+                       if (CONST_Debug) var_Dump($aPhrases, $aValidTokens);
+
+                        $aSuggestion = array();
+                        $bSuggestion = false;
+                       if (CONST_Suggestions_Enabled)
+                       {
+                               foreach($aPhrases as $iPhrase => $aPhrase)
+                               {
+                                       if (!isset($aValidTokens[' '.$aPhrase['wordsets'][0][0]]))
+                                       {
+                                               $sQuotedPhrase = getDBQuoted(' '.$aPhrase['wordsets'][0][0]);
+                                               $aSuggestionWords = getWordSuggestions($oDB, $aPhrase['wordsets'][0][0]);
+                                               $aRow = $aSuggestionWords[0];
+                                               if ($aRow && $aRow['word'])
+                                               {
+                                                       $aSuggestion[] = $aRow['word'];
+                                                       $bSuggestion = true;
+                                               }
+                                               else
+                                               {
+                                                       $aSuggestion[] = $aPhrase['string'];
+                                               }
+                                       }
+                                       else
+                                       {
+                                               $aSuggestion[] = $aPhrase['string'];
+                                       }
+                               }
+                       }
+                       if ($bSuggestion) $sSuggestion = join(', ',$aSuggestion);
+
+                       // Try and calculate GB postcodes we might be missing
+                       foreach($aTokens as $sToken)
+                       {
+                               if (!isset($aValidTokens[$sToken]) && !isset($aValidTokens[' '.$sToken]) && preg_match('/^([A-Z][A-Z]?[0-9][0-9A-Z]? ?[0-9])([A-Z][A-Z])$/', strtoupper(trim($sToken)), $aData))
+                               {
+                                       if (substr($aData[1],-2,1) != ' ')
+                                       {
+                                               $aData[0] = substr($aData[0],0,strlen($aData[1]-1)).' '.substr($aData[0],strlen($aData[1]-1));
+                                               $aData[1] = substr($aData[1],0,-1).' '.substr($aData[1],-1,1);
+                                       }
+                                       $aGBPostcodeLocation = gbPostcodeCalculate($aData[0], $aData[1], $aData[2], $oDB);
+                                       if ($aGBPostcodeLocation)
+                                       {
+                                               $aValidTokens[$sToken] = $aGBPostcodeLocation;
+                                       }
+                               }
+                       }
+
+                       // Any words that have failed completely?
+                       // TODO: suggestions
+
+                       // Start the search process
+                       $aResultPlaceIDs = array();
+
+                       /*
+                               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();
+
+                                       foreach($aPhrases[$iPhrase]['wordsets'] as $iWordset => $aWordset)
+                                       {
+                                               $aWordsetSearches = $aSearches;
+
+                                               // Add all words from this wordset
+                                               foreach($aWordset as $sToken)
+                                               {
+                                                       $aNewWordsetSearches = array();
+                                                       
+                                                       foreach($aWordsetSearches as $aCurrentSearch)
+                                                       {
+                                                               // If the token is valid
+                                                               if (isset($aValidTokens[' '.$sToken]))
+                                                               {
+                                                                       foreach($aValidTokens[' '.$sToken] as $aSearchTerm)
+                                                                       {
+                                                                               $aSearch = $aCurrentSearch;
+                                                                               $aSearch['iSearchRank']++;
+                                                                               if ($aSearchTerm['country_code'] !== null && $aSearchTerm['country_code'] != '0')
+                                                                               {
+                                                                                       if ($aSearch['sCountryCode'] === false)
+                                                                                       {
+                                                                                               $aSearch['sCountryCode'] = strtolower($aSearchTerm['country_code']);
+                                                                                               // Country is almost always at the end of the string - increase score for finding it anywhere else (opimisation)
+                                                                                               if ($iWordset+1 != sizeof($aPhrases[$iPhrase]['wordsets']) || $iPhrase+1 != sizeof($aPhrases)) $aSearch['iSearchRank'] += 5;
+                                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                                       }
+                                                                               }
+                                                                               elseif ($aSearchTerm['lat'] !== '' && $aSearchTerm['lat'] !== null)
+                                                                               {
+                                                                                       if ($aSearch['fLat'] === '')
+                                                                                       {
+                                                                                               $aSearch['fLat'] = $aSearchTerm['lat'];
+                                                                                               $aSearch['fLon'] = $aSearchTerm['lon'];
+                                                                                               $aSearch['fRadius'] = $aSearchTerm['radius'];
+                                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                                       }
+                                                                               }
+                                                                               elseif ($aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'house')
+                                                                               {
+                                                                                       if ($aSearch['sHouseNumber'] === '')
+                                                                                       {
+                                                                                               $aSearch['sHouseNumber'] = $sToken;
+                                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+
+                                                                                               // Fall back to not searching for this item (better than nothing)
+                                                                                               $aSearch = $aCurrentSearch;
+                                                                                               $aSearch['iSearchRank'] += 2;
+                                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                                       }
+                                                                               }
+                                                                               elseif ($aSearchTerm['class'] !== '' && $aSearchTerm['class'] !== null)
+                                                                               {
+                                                                                       if ($aSearch['sClass'] === '')
+                                                                                       {
+                                                                                               $aSearch['sOperator'] = $aSearchTerm['operator'];
+                                                                                               $aSearch['sClass'] = $aSearchTerm['class'];
+                                                                                               $aSearch['sType'] = $aSearchTerm['type'];
+                                                                                               if (sizeof($aSearch['aName'])) $aSearch['sOperator'] = 'name';
+                                                                                               else $aSearch['sOperator'] = 'near'; // near = in for the moment
+
+                                                                                               // Do we have a shortcut id?
+                                                                                               if ($aSearch['sOperator'] == 'name')
+                                                                                               {
+                                                                                                       $sSQL = "select get_tagpair('".$aSearch['sClass']."', '".$aSearch['sType']."')";
+                                                                                                       if ($iAmenityID = $oDB->getOne($sSQL))
+                                                                                                       {
+                                                                                                               $aValidTokens[$aSearch['sClass'].':'.$aSearch['sType']] = array('word_id' => $iAmenityID);
+                                                                                                               $aSearch['aName'][$iAmenityID] = $iAmenityID;
+                                                                                                               $aSearch['sClass'] = '';
+                                                                                                               $aSearch['sType'] = '';
+                                                                                                       }
+                                                                                               }
+                                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                                       }
+                                                                               }
+                                                                               else
+                                                                               {
+                                                                                       $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+                                                                                       if (!sizeof($aSearch['aName']))
+                                                                                       {
+                                                                                               $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+                                                                                               $aSearch['iNamePhrase'] = $iPhrase;
+                                                                                       }
+                                                                                       if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                               }
+                                                                       }
+                                                               }
+                                                               if (isset($aValidTokens[$sToken]))
+                                                               {
+                                                                       // Allow searching for a word - but at extra cost
+                                                                       foreach($aValidTokens[$sToken] as $aSearchTerm)
+                                                                       {
+                                                                               $aSearch = $aCurrentSearch;
+                                                                               $aSearch['iSearchRank']+=5;
+                                                                               $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+                                                                               if (!sizeof($aSearch['aName']) || $aSearch['iNamePhrase'] == $iPhrase)
+                                                                               {
+                                                                                       $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+                                                                                       $aSearch['iNamePhrase'] = $iPhrase;
+                                                                               }
+                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                       }
+                                                               }
+                                                               else
+                                                               {
+                                                                       // Allow skipping a word - but at EXTREAM cost
+                                                                       //$aSearch = $aCurrentSearch;
+                                                                       //$aSearch['iSearchRank']+=100;
+                                                                       //$aNewWordsetSearches[] = $aSearch;
+                                                               }
+                                                       }
+                                                       // Sort and cut
+                                                       usort($aNewWordsetSearches, 'bySearchRank');
+                                                       $aWordsetSearches = array_slice($aNewWordsetSearches, 0, 50);
+                                               }                                               
+//                                             var_Dump('<hr>',sizeof($aWordsetSearches)); exit;
+
+                                               $aNewPhraseSearches = array_merge($aNewPhraseSearches, $aNewWordsetSearches);
+                                               usort($aNewPhraseSearches, 'bySearchRank');
+                                               $aNewPhraseSearches = array_slice($aNewPhraseSearches, 0, 50);
+                                       }
+
+                                       // Re-group the searches by their score, junk anything over 20 as just not worth trying
+                                       $aGroupedSearches = array();
+                                       foreach($aNewPhraseSearches as $aSearch)
+                                       {
+                                               if ($aSearch['iSearchRank'] < $iMaxRank)
+                                               {
+                                                       if (!isset($aGroupedSearches[$aSearch['iSearchRank']])) $aGroupedSearches[$aSearch['iSearchRank']] = array();
+                                                       $aGroupedSearches[$aSearch['iSearchRank']][] = $aSearch;
+                                               }
+                                       }
+                                       ksort($aGroupedSearches);
+
+                                       $iSearchCount = 0;
+                                       $aSearches = array();
+                                       foreach($aGroupedSearches as $iScore => $aNewSearches)
+                                       {
+                                               $iSearchCount += sizeof($aNewSearches);
+                                               $aSearches = array_merge($aSearches, $aNewSearches);
+                                               if ($iSearchCount > 50) break;
+                                       }
+                               }
+                       }
+                       else
+                       {
+                                       // Re-group the searches by their score, junk anything over 20 as just not worth trying
+                                       $aGroupedSearches = array();
+                                       foreach($aSearches as $aSearch)
+                                       {
+                                               if ($aSearch['iSearchRank'] < $iMaxRank)
+                                               {
+                                                       if (!isset($aGroupedSearches[$aSearch['iSearchRank']])) $aGroupedSearches[$aSearch['iSearchRank']] = array();
+                                                       $aGroupedSearches[$aSearch['iSearchRank']][] = $aSearch;
+                                               }
+                                       }
+                                       ksort($aGroupedSearches);
+                       }
+                               
+                               if (CONST_Debug) var_Dump($aGroupedSearches);
+
+                               if ($bReverseInPlan)
+                               {
+                                       foreach($aGroupedSearches as $iGroup => $aSearches)
+                                       {
+                                               foreach($aSearches as $iSearch => $aSearch)
+                                               {
+                                                       if (sizeof($aSearch['aAddress']))
+                                                       {
+                                                               $aReverseSearch = $aSearch;
+                                                               $iReverseItem = array_pop($aSearch['aAddress']);
+                                                               $aReverseSearch['aName'][$iReverseItem] = $iReverseItem;
+                                                               $aGroupedSearches[$iGroup][] = $aReverseSearch;
+                                                       }
+                                               }
+                                       }
+                               }
+
+//var_Dump($aGroupedSearches); exit;
+
+                               // Filter out duplicate searches
+                               $aSearchHash = array();
+                               foreach($aGroupedSearches as $iGroup => $aSearches)
+                               {
+                                       foreach($aSearches as $iSearch => $aSearch)
+                                       {
+                                               $sHash = serialize($aSearch);
+                                               if (isset($aSearchHash[$sHash]))
+                                               {
+                                                       unset($aGroupedSearches[$iGroup][$iSearch]);
+                                                       if (sizeof($aGroupedSearches[$iGroup]) == 0) unset($aGroupedSearches[$iGroup]);
+                                               }
+                                               else
+                                               {
+                                                       $aSearchHash[$sHash] = 1;
+                                               }
+                                       }
+                               }
+                               
+                               if (CONST_Debug) _debugDumpGroupedSearches($aGroupedSearches, $aValidTokens);
+
+                               if ($bReverseInPlan)
+                               {
+                                       foreach($aGroupedSearches as $iGroup => $aSearches)
+                                       {
+                                               foreach($aSearches as $iSearch => $aSearch)
+                                               {
+                                                       if (sizeof($aSearch['aAddress']))
+                                                       {
+                                                               $aReverseSearch = $aSearch;
+                                                               $iReverseItem = array_pop($aSearch['aAddress']);
+                                                               $aReverseSearch['aName'][$iReverseItem] = $iReverseItem;
+                                                               $aGroupedSearches[$iGroup][] = $aReverseSearch;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               // Filter out duplicate searches
+                               $aSearchHash = array();
+                               foreach($aGroupedSearches as $iGroup => $aSearches)
+                               {
+                                       foreach($aSearches as $iSearch => $aSearch)
+                                       {
+                                               $sHash = serialize($aSearch);
+                                               if (isset($aSearchHash[$sHash]))
+                                               {
+                                                       unset($aGroupedSearches[$iGroup][$iSearch]);
+                                                       if (sizeof($aGroupedSearches[$iGroup]) == 0) unset($aGroupedSearches[$iGroup]);
+                                               }
+                                               else
+                                               {
+                                                       $aSearchHash[$sHash] = 1;
+                                               }
+                                       }
+                               }
+
+                               if (CONST_Debug) _debugDumpGroupedSearches($aGroupedSearches, $aValidTokens);
+
+                               $iGroupLoop = 0;
+                               $iQueryLoop = 0;
+                               foreach($aGroupedSearches as $iGroupedRank => $aSearches)
+                               {
+                                       $iGroupLoop++;
+                                       foreach($aSearches as $aSearch)
+                                       {
+                                               $iQueryLoop++;
+                                               // Must have a location term
+                                               if (!sizeof($aSearch['aName']) && !sizeof($aSearch['aAddress']))
+                                               {
+                                                       if (!$bBoundingBoxSearch && !$aSearch['fLon']) continue;
+                                                       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))
+                                                       {
+                                                               $sSQL = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType'];                                                               
+                                                               $sSQL .= " where st_contains($sViewboxSmallSQL, centroid)";
+                                                               if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
+                                                               $sSQL .= " limit $iLimit";
+                                                               if (CONST_Debug) var_dump($sSQL);
+                                                               $aPlaceIDs = $oDB->getCol($sSQL);
+
+                                                               if (!sizeof($aPlaceIDs))
+                                                               {
+                                                                       $sSQL = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType'];                                                               
+                                                                       $sSQL .= " where st_contains($sViewboxLargeSQL, centroid)";
+                                                                       if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
+                                                                       $sSQL .= " limit $iLimit";
+                                                                       if (CONST_Debug) var_dump($sSQL);
+                                                                       $aPlaceIDs = $oDB->getCol($sSQL);
+                                                               }
+                                                       }
+                                                       else
+                                                       {
+                                                               $sSQL = "select place_id from placex where class='".$aSearch['sClass']."' and type='".$aSearch['sType']."'";
+                                                               $sSQL .= " and st_contains($sViewboxSmallSQL, centroid)";
+                                                               if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
+                                                               $sSQL .= " limit $iLimit";
+                                                               if (CONST_Debug) var_dump($sSQL);
+                                                               $aPlaceIDs = $oDB->getCol($sSQL);
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       if ($aSearch['aName'] == array(282=>'282')) continue;
+
+                                                       if (CONST_Debug) var_dump('<hr>',$aSearch);
+                                                       if (CONST_Debug) _debugDumpGroupedSearches(array($iGroupedRank => array($aSearch)), $aValidTokens);     
+                                                       $aPlaceIDs = array();
+                                               
+                                                       // First we need a position, either aName or fLat or both
+                                                       $aTerms = array();
+                                                       $aOrder = array();
+                                                       if (sizeof($aSearch['aName'])) $aTerms[] = "name_vector @> ARRAY[".join($aSearch['aName'],",")."]";
+                                                       if (sizeof($aSearch['aAddress']) && $aSearch['aName'] != $aSearch['aAddress']) $aTerms[] = "nameaddress_vector @> ARRAY[".join($aSearch['aAddress'],",")."]";
+                                                       if ($aSearch['sCountryCode']) $aTerms[] = "country_code = '".pg_escape_string($aSearch['sCountryCode'])."'";
+                                                       if ($aSearch['sHouseNumber']) $aTerms[] = "address_rank in (26,27)";
+                                                       if ($aSearch['fLon'] && $aSearch['fLat'])
+                                                       {
+                                                               $aTerms[] = "ST_DWithin(centroid, ST_SetSRID(ST_Point(".$aSearch['fLon'].",".$aSearch['fLat']."),4326), ".$aSearch['fRadius'].")";
+                                                               $aOrder[] = "ST_Distance(centroid, ST_SetSRID(ST_Point(".$aSearch['fLon'].",".$aSearch['fLat']."),4326)) ASC";
+                                                       }
+                                                       if (sizeof($aExcludePlaceIDs))
+                                                       {
+                                                               $aTerms[] = "place_id not in (".join(',',$aExcludePlaceIDs).")";
+                                                       }
+                                                       if ($bBoundingBoxSearch) $aTerms[] = "centroid && $sViewboxSmallSQL";
+                                                       if ($sNearPointSQL) $aOrder[] = "ST_Distance($sNearPointSQL, centroid) asc";
+                                                       if ($sViewboxSmallSQL) $aOrder[] = "ST_Contains($sViewboxSmallSQL, centroid) desc";
+                                                       if ($sViewboxLargeSQL) $aOrder[] = "ST_Contains($sViewboxLargeSQL, centroid) desc";
+                                                       $aOrder[] = "search_rank ASC";
+                                               
+                                                       if (sizeof($aTerms))
+                                                       {
+                                                               $sSQL = "select place_id";
+                                                               if ($sViewboxSmallSQL) $sSQL .= ",ST_Contains($sViewboxSmallSQL, centroid) as in_small";
+                                                               else $sSQL .= ",false as in_small";
+                                                               if ($sViewboxLargeSQL) $sSQL .= ",ST_Contains($sViewboxLargeSQL, centroid) as in_large";
+                                                               else $sSQL .= ",false as in_large";
+                                                               $sSQL .= " from search_name";
+                                                               $sSQL .= " where ".join(' and ',$aTerms);
+                                                               $sSQL .= " order by ".join(', ',$aOrder);
+                                                               if ($aSearch['sHouseNumber'])
+                                                                       $sSQL .= " limit 50";
+                                                               elseif (!sizeof($aSearch['aName']) && !sizeof($aSearch['aAddress']) && $aSearch['sClass'])
+                                                                       $sSQL .= " limit 1";
+                                                               else
+                                                                       $sSQL .= " limit ".$iLimit;
+
+                                                               if (CONST_Debug) var_dump($sSQL);
+                                                               $aViewBoxPlaceIDs = $oDB->getAll($sSQL);
+                                                               if (PEAR::IsError($aViewBoxPlaceIDs))
+                                                               {
+                                                                       var_dump($sSQL, $aViewBoxPlaceIDs);                                     
+                                                                       exit;
+                                                               }
+
+                                                               // Did we have an viewbox matches?
+                                                               $aPlaceIDs = array();
+                                                               $bViewBoxMatch = false;
+                                                               foreach($aViewBoxPlaceIDs as $aViewBoxRow)
+                                                               {
+                                                                       if ($bViewBoxMatch == 1 && $aViewBoxRow['in_small'] == 'f') break;
+                                                                       if ($bViewBoxMatch == 2 && $aViewBoxRow['in_large'] == 'f') break;
+                                                                       if ($aViewBoxRow['in_small'] == 't') $bViewBoxMatch = 1;
+                                                                       else if ($aViewBoxRow['in_large'] == 't') $bViewBoxMatch = 2;
+                                                                       $aPlaceIDs[] = $aViewBoxRow['place_id'];
+                                                               }
+                                                       }
+
+                                                       if ($aSearch['sHouseNumber'] && sizeof($aPlaceIDs))
+                                                       {
+                                                               $sPlaceIDs = join(',',$aPlaceIDs);
+       
+                                                               $sHouseNumberRegex = '\\\\m'.str_replace(' ','[-, ]',$aSearch['sHouseNumber']).'\\\\M';
+
+                                                               // Make sure everything nearby is indexed (if we pre-indexed houses this wouldn't be needed!)
+                                                               $sSQL = "update placex set indexed = true from placex as f where placex.indexed = false";
+                                                               $sSQL .= " and f.place_id in (".$sPlaceIDs.") and ST_DWithin(placex.geometry, f.geometry, 0.004)";
+                                                               $sSQL .= " and placex.housenumber ~* E'".$sHouseNumberRegex."'";
+                                                               $sSQL .= " and placex.class='place' and placex.type='house'";
+                                                               if (CONST_Debug) var_dump($sSQL);
+                                                               $oDB->query($sSQL);
+                                                       
+                                                               // Now they are indexed look for a house attached to a street we found
+                                                               $sSQL = "select place_id from placex where street_place_id in (".$sPlaceIDs.") and housenumber ~* E'".$sHouseNumberRegex."'";
+                                                               if (sizeof($aExcludePlaceIDs))
+                                                               {
+                                                                       $sSQL .= " and place_id not in (".join(',',$aExcludePlaceIDs).")";
+                                                               }
+                                                               $sSQL .= " limit $iLimit";
+                                                               if (CONST_Debug) var_dump($sSQL);
+                                                               $aPlaceIDs = $oDB->getCol($sSQL);
+                                                       }
+                                               
+                                                       if ($aSearch['sClass'] && sizeof($aPlaceIDs))
+                                                       {
+                                                               $sPlaceIDs = join(',',$aPlaceIDs);
+
+                                                               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 .= " order by rank_search asc limit $iLimit";
+                                                                       if (CONST_Debug) var_dump($sSQL);
+                                                                       $aPlaceIDs = $oDB->getCol($sSQL);
+                                                               }
+                                                               
+                                                               if (!$aSearch['sOperator'] || $aSearch['sOperator'] == 'near') // & in
+                                                               {
+                                                                       $sSQL = "select rank_search from placex where place_id in ($sPlaceIDs) order by rank_search asc limit 1";
+                                                                       if (CONST_Debug) var_dump($sSQL);
+                                                                       $iMaxRank = ((int)$oDB->getOne($sSQL)) + 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);
+
+                                                                       $fRange = 0.01;
+                                                                       $sSQL = "select count(*) from pg_tables where tablename = 'place_classtype_".$aSearch['sClass']."_".$aSearch['sType']."'";
+                                                                       if ($oDB->getOne($sSQL))
+                                                                       {
+                                                                               // More efficient - can make the range bigger
+                                                                       $fRange = 0.05;
+                                                                               $sSQL = "select l.place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." as l";
+                                                                               $sSQL .= ",placex as f where ";
+                                                                               $sSQL .= "f.place_id in ($sPlaceIDs) and ST_DWithin(l.centroid, st_centroid(f.geometry), $fRange) ";
+                                                                               if (sizeof($aExcludePlaceIDs))
+                                                                               {
+                                                                                       $sSQL .= " and l.place_id not in (".join(',',$aExcludePlaceIDs).")";
+                                                                               }
+                                                                               if ($sNearPointSQL) $sSQL .= " order by ST_Distance($sNearPointSQL, l.geometry) ASC";
+                                                                               else $sSQL .= " order by ST_Distance(l.centroid, f.geometry) asc";
+                                                                               $sSQL .= " limit $iLimit";
+                                                                               if (CONST_Debug) var_dump($sSQL);
+                                                                               $aPlaceIDs = $oDB->getCol($sSQL);
+                                                                       }
+                                                                       else
+                                                                       {
+                                                                               if (isset($aSearch['fRadius']) && $aSearch['fRadius']) $fRange = $aSearch['fRadius'];
+                                                                               $sSQL = "select l.place_id from placex as l,placex as f where ";
+                                                                               $sSQL .= "f.place_id in ($sPlaceIDs) and ST_DWithin(l.geometry, st_centroid(f.geometry), $fRange) ";
+                                                                               $sSQL .= "and l.class='".$aSearch['sClass']."' and l.type='".$aSearch['sType']."' ";
+                                                                               if (sizeof($aExcludePlaceIDs))
+                                                                               {
+                                                                                       $sSQL .= " and l.place_id not in (".join(',',$aExcludePlaceIDs).")";
+                                                                               }
+                                                                               if ($sNearPointSQL) $sSQL .= " order by ST_Distance($sNearPointSQL, l.geometry) ASC";
+                                                                               else $sSQL .= " order by ST_Distance(l.geometry, f.geometry) asc, l.rank_search ASC";
+                                                                               $sSQL .= " limit $iLimit";
+                                                                               if (CONST_Debug) var_dump($sSQL);
+                                                                               $aPlaceIDs = $oDB->getCol($sSQL);
+                                                                       }
+                                                               }
+                                                       }
+                                               
+                                               }
+
+                                               if (PEAR::IsError($aPlaceIDs))
+                                               {
+                                                       var_dump($sSQL, $aPlaceIDs);                                    
+                                                       exit;
+                                               }
+
+                                               if (CONST_Debug) var_Dump($aPlaceIDs);
+
+                                               foreach($aPlaceIDs as $iPlaceID)
+                                               {
+                                                       $aResultPlaceIDs[$iPlaceID] = $iPlaceID;
+                                               }
+                                               if ($iQueryLoop > 20) break;
+                                       }
+                                       //exit;
+                                       if (sizeof($aResultPlaceIDs)) break;
+                                       if ($iGroupLoop > 4) break;
+                                       if ($iQueryLoop > 30) break;
+                               }
+//exit;
+                               // Did we find anything?        
+                               if (sizeof($aResultPlaceIDs))
+                               {
+//var_Dump($aResultPlaceIDs);exit;
+                                       // Get the details for display (is this a redundant extra step?)
+                                       $sPlaceIDs = join(',',$aResultPlaceIDs);
+                                       $sOrderSQL = 'CASE ';
+                                       foreach(array_keys($aResultPlaceIDs) as $iOrder => $iPlaceID)
+                                       {
+                                               $sOrderSQL .= 'when min(place_id) = '.$iPlaceID.' then '.$iOrder.' ';
+                                       }
+                                       $sOrderSQL .= ' ELSE 10000000 END ASC';
+                                       $sSQL = "select osm_type,osm_id,class,type,rank_search,rank_address,min(place_id) as place_id,country_code,";
+                                       $sSQL .= "get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,";
+                                       $sSQL .= "get_name_by_language(name, $sLanguagePrefArraySQL) as placename,";
+                                       $sSQL .= "get_name_by_language(name, ARRAY['ref']) as ref,";
+                                       $sSQL .= "avg(ST_X(ST_Centroid(geometry))) as lon,avg(ST_Y(ST_Centroid(geometry))) as lat ";
+                                       $sSQL .= "from placex where place_id in ($sPlaceIDs) ";
+                                       $sSQL .= "group by osm_type,osm_id,class,type,rank_search,rank_address,country_code";
+                                       if (!$bDeDupe) $sSQL .= ",place_id";
+                                       $sSQL .= ",get_address_by_language(place_id, $sLanguagePrefArraySQL) ";
+                                       $sSQL .= ",get_name_by_language(name, $sLanguagePrefArraySQL) ";
+                                       $sSQL .= ",get_name_by_language(name, ARRAY['ref']) ";
+                                       $sSQL .= "order by rank_search,rank_address,".$sOrderSQL;
+                                       if (CONST_Debug) var_dump('<hr>',$sSQL);
+                                       $aSearchResults = $oDB->getAll($sSQL);
+//var_dump($sSQL,$aSearchResults);exit;
+
+                                       if (PEAR::IsError($aSearchResults))
+                                       {
+                                               var_dump($sSQL, $aSearchResults);                                       
+                                               exit;
+                                       }
+                               }
+                       }
+               }
+       
+       $sSearchResult = '';
+       if (!sizeof($aSearchResults) && isset($_GET['q']) && $_GET['q'])
+       {
+               $sSearchResult = 'No Results Found';
+       }
+       
+       $aClassType = getClassTypesWithImportance();
+
+       foreach($aSearchResults as $iResNum => $aResult)
+       {
+               if (CONST_Search_AreaPolygons || true)
+               {
+                       // Get the bounding box and outline polygon
+                       $sSQL = "select place_id,numfeatures,area,outline,";
+                       $sSQL .= "ST_Y(ST_PointN(ExteriorRing(ST_Box2D(outline)),4)) as minlat,ST_Y(ST_PointN(ExteriorRing(ST_Box2D(outline)),2)) as maxlat,";
+                       $sSQL .= "ST_X(ST_PointN(ExteriorRing(ST_Box2D(outline)),1)) as minlon,ST_X(ST_PointN(ExteriorRing(ST_Box2D(outline)),3)) as maxlon,";
+                       $sSQL .= "ST_AsText(outline) as outlinestring from get_place_boundingbox_quick(".$aResult['place_id'].")";
+                       $aPointPolygon = $oDB->getRow($sSQL);
+                       if (PEAR::IsError($aPointPolygon))
+                       {
+                               var_dump($sSQL, $aPointPolygon);
+                               exit;
+                       }
+                       if ($aPointPolygon['place_id'])
+                       {
+                               // 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))
+                               {
+                                       $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;
+                               }
+
+                               // Output data suitable for display (points and a bounding box)
+                               if ($bShowPolygons)
+                               {
+                                       $aResult['aPolyPoints'] = array();
+                                       foreach($aPolyPoints as $aPoint)
+                                       {
+                                               $aResult['aPolyPoints'][] = array($aPoint[1], $aPoint[2]);
+                                       }
+                               }
+                               $aResult['aBoundingBox'] = array($aPointPolygon['minlat'],$aPointPolygon['maxlat'],$aPointPolygon['minlon'],$aPointPolygon['maxlon']);
+                       }
+               }
+
+               if (!isset($aResult['aBoundingBox']))
+               {
+                       // Default
+                       $fDiameter = 0.0001;
+
+                       if (isset($aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter']) 
+                                       && $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter'])
+                       {
+                               $fDiameter = $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defzoom'];
+                       }
+                       elseif (isset($aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter']) 
+                                       && $aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter'])
+                       {
+                               $fDiameter = $aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter'];
+                       }
+                       $fRadius = $fDiameter / 2;
+
+                       $iSteps = max(8,min(100,$fRadius * 3.14 * 100000));
+                       $fStepSize = (2*pi())/$iSteps;
+                       $aPolyPoints = array();
+                       for($f = 0; $f < 2*pi(); $f += $fStepSize)
+                       {
+                               $aPolyPoints[] = array('',$aResult['lon']+($fRadius*sin($f)),$aResult['lat']+($fRadius*cos($f)));
+                       }
+                       $aPointPolygon['minlat'] = $aResult['lat'] - $fRadius;
+                       $aPointPolygon['maxlat'] = $aResult['lat'] + $fRadius;
+                       $aPointPolygon['minlon'] = $aResult['lon'] - $fRadius;
+                       $aPointPolygon['maxlon'] = $aResult['lon'] + $fRadius;
+
+                       // Output data suitable for display (points and a bounding box)
+                       if ($bShowPolygons)
+                       {
+                               $aResult['aPolyPoints'] = array();
+                               foreach($aPolyPoints as $aPoint)
+                               {
+                                       $aResult['aPolyPoints'][] = array($aPoint[1], $aPoint[2]);
+                               }
+                       }
+                       $aResult['aBoundingBox'] = array($aPointPolygon['minlat'],$aPointPolygon['maxlat'],$aPointPolygon['minlon'],$aPointPolygon['maxlon']);
+               }
+
+               // Is there an icon set for this type of result?
+               if (isset($aClassType[$aResult['class'].':'.$aResult['type']]['icon']) 
+                       && $aClassType[$aResult['class'].':'.$aResult['type']]['icon'])
+               {
+                       $aResult['icon'] = CONST_Website_BaseURL.'images/mapicons/'.$aClassType[$aResult['class'].':'.$aResult['type']]['icon'].'.p.20.png';
+               }
+
+               if ($bShowAddressDetails)
+               {
+                       $aResult['address'] = getAddressDetails($oDB, $sLanguagePrefArraySQL, $aResult['place_id'], $aResult['country_code']);
+               }
+
+               if (isset($aClassType[$aResult['class'].':'.$aResult['type']]['importance']) 
+                       && $aClassType[$aResult['class'].':'.$aResult['type']]['importance'])
+               {
+                       $aResult['importance'] = $aClassType[$aResult['class'].':'.$aResult['type']]['importance'];
+               }
+               else
+               {
+                       $aResult['importance'] = 1000000000000000;
+               }
+
+               $aResult['name'] = $aResult['langaddress'];
+               $aResult['foundorder'] = $iResNum;
+               $aSearchResults[$iResNum] = $aResult;
+       }
+       
+       uasort($aSearchResults, 'byImportance');
+       
+       $aOSMIDDone = array();
+       $aClassTypeNameDone = array();
+       $aToFilter = $aSearchResults;
+       $aSearchResults = array();
+
+       $bFirst = true;
+       foreach($aToFilter as $iResNum => $aResult)
+       {
+               if ($aResult['type'] == 'adminitrative') $aResult['type'] = 'administrative';
+               $aExcludePlaceIDs[$aResult['place_id']] = $aResult['place_id'];
+               if ($bFirst)
+               {
+                       $fLat = $aResult['lat'];
+                       $fLon = $aResult['lon'];
+                       if (isset($aResult['zoom'])) $iZoom = $aResult['zoom'];
+                       $bFirst = false;
+               }
+               if (!$bDeDupe || (!isset($aOSMIDDone[$aResult['osm_type'].$aResult['osm_id']])
+                       && !isset($aClassTypeNameDone[$aResult['osm_type'].$aResult['osm_class'].$aResult['name']])))
+               {
+                       $aOSMIDDone[$aResult['osm_type'].$aResult['osm_id']] = true;
+                       $aClassTypeNameDone[$aResult['osm_type'].$aResult['osm_class'].$aResult['name']] = true;
+                       $aSearchResults[] = $aResult;
+               }
+       }
+
+       $sDataDate = $oDB->getOne("select TO_CHAR(lastimportdate - '1 day'::interval,'YYYY/MM/DD') from import_status limit 1");
+
+       if (isset($_GET['nearlat']) && isset($_GET['nearlon']))
+       {
+               $sQuery .= ' ['.$_GET['nearlat'].','.$_GET['nearlon'].']';
+       }
+
+       if ($sQuery)
+       {
+               logEnd($oDB, $hLog, sizeof($aToFilter));
+       }
+       $sMoreURL = CONST_Website_BaseURL.'search?format='.urlencode($sOutputFormat).'&exclude_place_ids='.join(',',$aExcludePlaceIDs);
+       $sMoreURL .= '&accept-language='.$_SERVER["HTTP_ACCEPT_LANGUAGE"];
+       if ($bShowPolygons) $sMoreURL .= '&polygon=1';
+       if ($bShowAddressDetails) $sMoreURL .= '&addressdetails=1';
+       if (isset($_GET['viewbox']) && $_GET['viewbox']) $sMoreURL .= '&viewbox='.urlencode($_GET['viewbox']);
+       if (isset($_GET['nearlat']) && isset($_GET['nearlon'])) $sMoreURL .= '&nearlat='.(float)$_GET['nearlat'].'&nearlon='.(float)$_GET['nearlon'];
+       if ($sSuggestion)
+       {
+               $sSuggestionURL = $sMoreURL.'&q='.urlencode($sSuggestion);
+       }
+       $sMoreURL .= '&q='.urlencode($sQuery);
+
+       if (CONST_Debug) exit;
+
+       include('.htlib/output/search-'.$sOutputFormat.'.php');