From c988be1ddf10c7838c4e057adb351b1c076c6cca Mon Sep 17 00:00:00 2001 From: Brian Quinion Date: Tue, 3 May 2011 17:34:51 +0000 Subject: [PATCH] handle pure lat,lon search by passing to reverse geocoding and returning first match as a search result --- lib/lib.php | 99 ++++++++++++++++++++++++++++++++++++++++++++++ website/search.php | 74 +++++++++++++++++++++++++++++++++- 2 files changed, 171 insertions(+), 2 deletions(-) diff --git a/lib/lib.php b/lib/lib.php index 7f56a1de..20b1f8ce 100644 --- a/lib/lib.php +++ b/lib/lib.php @@ -816,3 +816,102 @@ $aSimilar = $oDB->getAll($sSQL); return $aSimilar; } + + function geocodeReverse($fLat, $fLon, $iZoom=18) + { + $oDB =& getDB(); + + $sPointSQL = "ST_SetSRID(ST_Point($fLon,$fLat),4326)"; + + // Zoom to rank, this could probably be calculated but a lookup gives fine control + $aZoomRank = array( + 0 => 2, // Continent / Sea + 1 => 2, + 2 => 2, + 3 => 4, // Country + 4 => 4, + 5 => 8, // State + 6 => 10, // Region + 7 => 10, + 8 => 12, // County + 9 => 12, + 10 => 17, // City + 11 => 17, + 12 => 18, // Town / Village + 13 => 18, + 14 => 22, // Suburb + 15 => 22, + 16 => 26, // Street, TODO: major street? + 17 => 26, + 18 => 30, // or >, Building + 19 => 30, // or >, Building + ); + $iMaxRank = isset($aZoomRank[$iZoom])?$aZoomRank[$iZoom]:28; + + // Find the nearest point + $fSearchDiam = 0.0001; + $iPlaceID = null; + $aArea = false; + $fMaxAreaDistance = 1; + while(!$iPlaceID && $fSearchDiam < $fMaxAreaDistance) + { + $fSearchDiam = $fSearchDiam * 2; + + // If we have to expand the search area by a large amount then we need a larger feature + // then there is a limit to how small the feature should be + if ($fSearchDiam > 2 && $iMaxRank > 4) $iMaxRank = 4; + if ($fSearchDiam > 1 && $iMaxRank > 9) $iMaxRank = 8; + if ($fSearchDiam > 0.8 && $iMaxRank > 10) $iMaxRank = 10; + if ($fSearchDiam > 0.6 && $iMaxRank > 12) $iMaxRank = 12; + if ($fSearchDiam > 0.2 && $iMaxRank > 17) $iMaxRank = 17; + if ($fSearchDiam > 0.1 && $iMaxRank > 18) $iMaxRank = 18; + if ($fSearchDiam > 0.008 && $iMaxRank > 22) $iMaxRank = 22; + if ($fSearchDiam > 0.001 && $iMaxRank > 26) $iMaxRank = 26; + + $sSQL = 'select place_id,parent_place_id from placex'; + $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')'; + $sSQL .= ' and rank_search != 28 and rank_search >= '.$iMaxRank; + $sSQL .= ' and (name is not null or housenumber is not null)'; + $sSQL .= ' and class not in (\'waterway\')'; + $sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') '; + $sSQL .= ' OR ST_DWithin('.$sPointSQL.', ST_Centroid(geometry), '.$fSearchDiam.'))'; + $sSQL .= ' ORDER BY ST_distance('.$sPointSQL.', geometry) ASC limit 1'; +//var_dump($sSQL); + $aPlace = $oDB->getRow($sSQL); + $iPlaceID = $aPlace['place_id']; + if (PEAR::IsError($iPlaceID)) + { + var_Dump($sSQL, $iPlaceID); + exit; + } + } + + // The point we found might be too small - use the address to find what it is a child of + if ($iPlaceID) + { + $sSQL = "select address_place_id from place_addressline where cached_rank_address <= $iMaxRank and place_id = $iPlaceID order by cached_rank_address desc,isaddress desc,distance desc limit 1"; + $iPlaceID = $oDB->getOne($sSQL); + if (PEAR::IsError($iPlaceID)) + { + var_Dump($sSQL, $iPlaceID); + exit; + } + + if ($iPlaceID && $aPlace['place_id'] && $iMaxRank < 28) + { + $sSQL = "select address_place_id from place_addressline where cached_rank_address <= $iMaxRank and place_id = ".$aPlace['place_id']." order by cached_rank_address desc,isaddress desc,distance desc"; + $iPlaceID = $oDB->getOne($sSQL); + if (PEAR::IsError($iPlaceID)) + { + var_Dump($sSQL, $iPlaceID); + exit; + } + } + if (!$iPlaceID) + { + $iPlaceID = $aPlace['place_id']; + } + } + + return $iPlaceID; + } diff --git a/website/search.php b/website/search.php index cd52d227..ba7df0f0 100755 --- a/website/search.php +++ b/website/search.php @@ -183,7 +183,7 @@ $_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)) + elseif (preg_match('/(\\[|\\b)(-?[0-9]+[0-9.]*)[, ]+(-?[0-9]+[0-9.]*)(\\]|\\b)/', $sQuery, $aData)) { $_GET['nearlat'] = $aData[2]; $_GET['nearlon'] = $aData[3]; @@ -192,7 +192,6 @@ if ($sQuery) { - // Start with a blank search $aSearches = array( array('iSearchRank' => 0, 'iNamePhrase' => -1, 'sCountryCode' => false, 'aName'=>array(), 'aAddress'=>array(), @@ -983,6 +982,75 @@ $aSearchResults = $oDB->getAll($sSQL); //var_dump($sSQL,$aSearchResults);exit; + if (PEAR::IsError($aSearchResults)) + { + var_dump($sSQL, $aSearchResults); + exit; + } + } + } // end if ($sQuery) + else + { + if (isset($_GET['nearlat']) && trim($_GET['nearlat'])!=='' && isset($_GET['nearlon']) && trim($_GET['nearlon']) !== '') + { + $iPlaceID = geocodeReverse($_GET['nearlat'], $_GET['nearlon']); + $aResultPlaceIDs = array($iPlaceID); + + // TODO: this needs refactoring! + + // 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'; + $sSQL = "select osm_type,osm_id,class,type,admin_level,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 .= $sOrderSQL." as porder, "; + $sSQL .= "coalesce(importance,0.9-(rank_search::float/30)) as importance "; + $sSQL .= "from placex where place_id in ($sPlaceIDs) "; + $sSQL .= "and placex.rank_address between $iMinAddressRank and $iMaxAddressRank "; + $sSQL .= "group by osm_type,osm_id,class,type,admin_level,rank_search,rank_address,country_code,importance"; + 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 .= " union "; + $sSQL .= "select 'T' as osm_type,place_id as osm_id,'place' as class,'house' as type,null as admin_level,30 as rank_search,30 as rank_address,min(place_id) as place_id,'us' as country_code,"; + $sSQL .= "get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,"; + $sSQL .= "null as placename,"; + $sSQL .= "null as ref,"; + $sSQL .= "avg(ST_X(centroid)) as lon,avg(ST_Y(centroid)) as lat, "; +// $sSQL .= $sOrderSQL." as porder, "; + $sSQL .= "-0.15 as importance "; + $sSQL .= "from location_property_tiger where place_id in ($sPlaceIDs) "; + $sSQL .= "and 30 between $iMinAddressRank and $iMaxAddressRank "; + $sSQL .= "group by place_id"; + if (!$bDeDupe) $sSQL .= ",place_id"; + $sSQL .= " union "; + $sSQL .= "select 'L' as osm_type,place_id as osm_id,'place' as class,'house' as type,null as admin_level,30 as rank_search,30 as rank_address,min(place_id) as place_id,'us' as country_code,"; + $sSQL .= "get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,"; + $sSQL .= "null as placename,"; + $sSQL .= "null as ref,"; + $sSQL .= "avg(ST_X(centroid)) as lon,avg(ST_Y(centroid)) as lat, "; +// $sSQL .= $sOrderSQL." as porder, "; + $sSQL .= "-0.10 as importance "; + $sSQL .= "from location_property_aux where place_id in ($sPlaceIDs) "; + $sSQL .= "and 30 between $iMinAddressRank and $iMaxAddressRank "; + $sSQL .= "group by place_id"; + if (!$bDeDupe) $sSQL .= ",place_id"; + $sSQL .= ",get_address_by_language(place_id, $sLanguagePrefArraySQL) "; + $sSQL .= "order by importance desc"; +// $sSQL .= "order by rank_search,rank_address,porder asc"; + if (CONST_Debug) var_dump('
',$sSQL); + $aSearchResults = $oDB->getAll($sSQL); +//var_dump($sSQL,$aSearchResults);exit; + if (PEAR::IsError($aSearchResults)) { var_dump($sSQL, $aSearchResults); @@ -995,6 +1063,8 @@ $sSearchResult = ''; if (!sizeof($aSearchResults) && isset($_GET['q']) && $_GET['q']) { +echo "here"; +exit; $sSearchResult = 'No Results Found'; } //var_Dump($aSearchResults); -- 2.39.5