X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/6fc32d9645f98ec39c8773d6bf01cc2f6d4969de..88d623b6319c4be9068f018390ad9e18b6eac3ad:/lib/Geocode.php diff --git a/lib/Geocode.php b/lib/Geocode.php index 19ed354f..9249b314 100644 --- a/lib/Geocode.php +++ b/lib/Geocode.php @@ -1,5 +1,6 @@ aLangPrefOrder = $aLangPref; } - function setIncludeAddressDetails($bAddressDetails = true) - { - $this->bIncludeAddressDetails = (bool)$bAddressDetails; - } - function getIncludeAddressDetails() { return $this->bIncludeAddressDetails; @@ -87,21 +83,11 @@ $this->bIncludePolygonAsPoints = $b; } - function getIncludePolygonAsPoints() - { - return $this->bIncludePolygonAsPoints; - } - function setIncludePolygonAsText($b = true) { $this->bIncludePolygonAsText = $b; } - function getIncludePolygonAsText() - { - return $this->bIncludePolygonAsText; - } - function setIncludePolygonAsGeoJSON($b = true) { $this->bIncludePolygonAsGeoJSON = $b; @@ -122,11 +108,6 @@ $this->fPolygonSimplificationThreshold = $f; } - function setDeDupe($bDeDupe = true) - { - $this->bDeDupe = (bool)$bDeDupe; - } - function setLimit($iLimit = 10) { if ($iLimit > 50) $iLimit = 50; @@ -136,32 +117,11 @@ $this->iLimit = $this->iFinalLimit + min($this->iFinalLimit, 10); } - function setOffset($iOffset = 0) - { - $this->iOffset = $iOffset; - } - - function setFallback($bFallback = true) - { - $this->bFallback = (bool)$bFallback; - } - - function setExcludedPlaceIDs($a) - { - // TODO: force to int - $this->aExcludePlaceIDs = $a; - } - function getExcludedPlaceIDs() { return $this->aExcludePlaceIDs; } - function setBounded($bBoundedSearch = true) - { - $this->bBoundedSearch = (bool)$bBoundedSearch; - } - function setViewBox($fLeft, $fBottom, $fRight, $fTop) { $this->aViewBox = array($fLeft, $fBottom, $fRight, $fTop); @@ -173,11 +133,6 @@ return $this->aViewBox[0].','.$this->aViewBox[3].','.$this->aViewBox[2].','.$this->aViewBox[1]; } - function setRoute($aRoutePoints) - { - $this->aRoutePoints = $aRoutePoints; - } - function setFeatureType($sFeatureType) { switch($sFeatureType) @@ -199,8 +154,8 @@ function setRankRange($iMin, $iMax) { - $this->iMinAddressRank = (int)$iMin; - $this->iMaxAddressRank = (int)$iMax; + $this->iMinAddressRank = $iMin; + $this->iMaxAddressRank = $iMax; } function setNearPoint($aNearPoint, $fRadiusDeg = 0.1) @@ -208,11 +163,6 @@ $this->aNearPoint = array((float)$aNearPoint[0], (float)$aNearPoint[1], (float)$fRadiusDeg); } - function setCountryCodesList($aCountryCodes) - { - $this->aCountryCodes = $aCountryCodes; - } - function setQuery($sQueryString) { $this->sQuery = $sQueryString; @@ -228,11 +178,9 @@ function loadParamArray($aParams) { if (isset($aParams['addressdetails'])) $this->bIncludeAddressDetails = (bool)$aParams['addressdetails']; - if ((float) CONST_Postgresql_Version > 9.2) - { - if (isset($aParams['extratags'])) $this->bIncludeExtraTags = (bool)$aParams['extratags']; - if (isset($aParams['namedetails'])) $this->bIncludeNameDetails = (bool)$aParams['namedetails']; - } + if (isset($aParams['extratags'])) $this->bIncludeExtraTags = (bool)$aParams['extratags']; + if (isset($aParams['namedetails'])) $this->bIncludeNameDetails = (bool)$aParams['namedetails']; + if (isset($aParams['bounded'])) $this->bBoundedSearch = (bool)$aParams['bounded']; if (isset($aParams['dedupe'])) $this->bDeDupe = (bool)$aParams['dedupe']; @@ -358,7 +306,7 @@ $this->loadStructuredAddressElement($sPostalCode, 'postalcode' , 5, 11, array(5, 11)); $this->loadStructuredAddressElement($sCountry, 'country', 4, 4, false); - if (sizeof($this->aStructuredQuery) > 0) + if (sizeof($this->aStructuredQuery) > 0) { $this->sQuery = join(', ', $this->aStructuredQuery); if ($this->iMaxAddressRank < 30) @@ -393,19 +341,20 @@ function getDetails($aPlaceIDs) { + //$aPlaceIDs is an array with key: placeID and value: tiger-housenumber, if found, else -1 if (sizeof($aPlaceIDs) == 0) return array(); $sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted",$this->aLangPrefOrder))."]"; // Get the details for display (is this a redundant extra step?) - $sPlaceIDs = join(',',$aPlaceIDs); + $sPlaceIDs = join(',', array_keys($aPlaceIDs)); $sImportanceSQL = ''; if ($this->sViewboxSmallSQL) $sImportanceSQL .= " case when ST_Contains($this->sViewboxSmallSQL, ST_Collect(centroid)) THEN 1 ELSE 0.75 END * "; if ($this->sViewboxLargeSQL) $sImportanceSQL .= " case when ST_Contains($this->sViewboxLargeSQL, ST_Collect(centroid)) THEN 1 ELSE 0.75 END * "; $sSQL = "select osm_type,osm_id,class,type,admin_level,rank_search,rank_address,min(place_id) as place_id, min(parent_place_id) as parent_place_id, calculated_country_code as country_code,"; - $sSQL .= "get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,"; + $sSQL .= "get_address_by_language(place_id, -1, $sLanguagePrefArraySQL) as langaddress,"; $sSQL .= "get_name_by_language(name, $sLanguagePrefArraySQL) as placename,"; $sSQL .= "get_name_by_language(name, ARRAY['ref']) as ref,"; if ($this->bIncludeExtraTags) $sSQL .= "hstore_to_json(extratags)::text as extra,"; @@ -432,47 +381,91 @@ if (30 >= $this->iMinAddressRank && 30 <= $this->iMaxAddressRank) { + //only Tiger housenumbers and interpolation lines need to be interpolated, because they are saved as lines + // with start- and endnumber, the common osm housenumbers are usually saved as points + $sHousenumbers = ""; + $i = 0; + $length = count($aPlaceIDs); + foreach($aPlaceIDs as $placeID => $housenumber) + { + $i++; + $sHousenumbers .= "(".$placeID.", ".$housenumber.")"; + if($i<$length) + $sHousenumbers .= ", "; + } + if (CONST_Use_US_Tiger_Data) + { + //Tiger search only if a housenumber was searched and if it was found (i.e. aPlaceIDs[placeID] = housenumber != -1) (realized through a join) + $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, min(parent_place_id) as parent_place_id, 'us' as country_code"; + $sSQL .= ", get_address_by_language(place_id, housenumber_for_place, $sLanguagePrefArraySQL) as langaddress "; + $sSQL .= ", null as placename"; + $sSQL .= ", null as ref"; + if ($this->bIncludeExtraTags) $sSQL .= ", null as extra"; + if ($this->bIncludeNameDetails) $sSQL .= ", null as names"; + $sSQL .= ", avg(st_x(centroid)) as lon, avg(st_y(centroid)) as lat,"; + $sSQL .= $sImportanceSQL."-1.15 as importance "; + $sSQL .= ", (select max(p.importance*(p.rank_address+2)) from place_addressline s, placex p where s.place_id = min(blub.parent_place_id) and p.place_id = s.address_place_id and s.isaddress and p.importance is not null) as addressimportance "; + $sSQL .= ", null as extra_place "; + $sSQL .= " from (select place_id"; + //interpolate the Tiger housenumbers here + $sSQL .= ", ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) as centroid, parent_place_id, housenumber_for_place"; + $sSQL .= " from (location_property_tiger "; + $sSQL .= " join (values ".$sHousenumbers.") as housenumbers(place_id, housenumber_for_place) using(place_id)) "; + $sSQL .= " where housenumber_for_place>=0 and 30 between $this->iMinAddressRank and $this->iMaxAddressRank) as blub"; //postgres wants an alias here + $sSQL .= " group by place_id, housenumber_for_place"; //is this group by really needed?, place_id + housenumber (in combination) are unique + if (!$this->bDeDupe) $sSQL .= ", place_id "; + } + // osmline + // interpolation line search only if a housenumber was searched and if it was found (i.e. aPlaceIDs[placeID] = housenumber != -1) (realized through a join) $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, min(parent_place_id) as parent_place_id,'us' as country_code,"; - $sSQL .= "get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,"; - $sSQL .= "null as placename,"; - $sSQL .= "null as ref,"; - if ($this->bIncludeExtraTags) $sSQL .= "null as extra,"; - if ($this->bIncludeNameDetails) $sSQL .= "null as names,"; - $sSQL .= "avg(ST_X(centroid)) as lon,avg(ST_Y(centroid)) as lat, "; - $sSQL .= $sImportanceSQL."-1.15 as importance, "; - $sSQL .= "(select max(p.importance*(p.rank_address+2)) from place_addressline s, placex p where s.place_id = min(location_property_tiger.parent_place_id) and p.place_id = s.address_place_id and s.isaddress and p.importance is not null) as addressimportance, "; - $sSQL .= "null as extra_place "; - $sSQL .= "from location_property_tiger where place_id in ($sPlaceIDs) "; - $sSQL .= "and 30 between $this->iMinAddressRank and $this->iMaxAddressRank "; - $sSQL .= "group by place_id"; - if (!$this->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, min(parent_place_id) as parent_place_id,'us' as country_code,"; - $sSQL .= "get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,"; - $sSQL .= "null as placename,"; - $sSQL .= "null as ref,"; - if ($this->bIncludeExtraTags) $sSQL .= "null as extra,"; - if ($this->bIncludeNameDetails) $sSQL .= "null as names,"; - $sSQL .= "avg(ST_X(centroid)) as lon,avg(ST_Y(centroid)) as lat, "; - $sSQL .= $sImportanceSQL."-1.10 as importance, "; - $sSQL .= "(select max(p.importance*(p.rank_address+2)) from place_addressline s, placex p where s.place_id = min(location_property_aux.parent_place_id) and p.place_id = s.address_place_id and s.isaddress and p.importance is not null) as addressimportance, "; - $sSQL .= "null as extra_place "; - $sSQL .= "from location_property_aux where place_id in ($sPlaceIDs) "; - $sSQL .= "and 30 between $this->iMinAddressRank and $this->iMaxAddressRank "; - $sSQL .= "group by place_id"; - if (!$this->bDeDupe) $sSQL .= ",place_id"; - $sSQL .= ",get_address_by_language(place_id, $sLanguagePrefArraySQL) "; + $sSQL .= "select 'W' 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, min(parent_place_id) as parent_place_id, calculated_country_code as country_code, "; + $sSQL .= "get_address_by_language(place_id, housenumber_for_place, $sLanguagePrefArraySQL) as langaddress, "; + $sSQL .= "null as placename, "; + $sSQL .= "null as ref, "; + if ($this->bIncludeExtraTags) $sSQL .= "null as extra, "; + if ($this->bIncludeNameDetails) $sSQL .= "null as names, "; + $sSQL .= " avg(st_x(centroid)) as lon, avg(st_y(centroid)) as lat,"; + $sSQL .= $sImportanceSQL."-0.1 as importance, "; // slightly smaller than the importance for normal houses with rank 30, which is 0 + $sSQL .= " (select max(p.importance*(p.rank_address+2)) from place_addressline s, placex p"; + $sSQL .= " where s.place_id = min(blub.parent_place_id) and p.place_id = s.address_place_id and s.isaddress and p.importance is not null) as addressimportance,"; + $sSQL .= " null as extra_place "; + $sSQL .= " from (select place_id, calculated_country_code "; + //interpolate the housenumbers here + $sSQL .= ", CASE WHEN startnumber != endnumber THEN ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) "; + $sSQL .= " ELSE ST_LineInterpolatePoint(linegeo, 0.5) END as centroid"; + $sSQL .= ", parent_place_id, housenumber_for_place "; + $sSQL .= " from (location_property_osmline "; + $sSQL .= " join (values ".$sHousenumbers.") as housenumbers(place_id, housenumber_for_place) using(place_id)) "; + $sSQL .= " where housenumber_for_place>=0 and 30 between $this->iMinAddressRank and $this->iMaxAddressRank) as blub"; //postgres wants an alias here + $sSQL .= " group by place_id, housenumber_for_place, calculated_country_code "; //is this group by really needed?, place_id + housenumber (in combination) are unique + if (!$this->bDeDupe) $sSQL .= ", place_id "; + + if (CONST_Use_Aux_Location_data) + { + $sSQL .= " union "; + $sSQL .= "select 'L' as osm_type, place_id as osm_id, 'place' as class, 'house' as type, null as admin_level, 0 as rank_search, 0 as rank_address, min(place_id) as place_id, min(parent_place_id) as parent_place_id, 'us' as country_code, "; + $sSQL .= "get_address_by_language(place_id, -1, $sLanguagePrefArraySQL) as langaddress, "; + $sSQL .= "null as placename, "; + $sSQL .= "null as ref, "; + if ($this->bIncludeExtraTags) $sSQL .= "null as extra, "; + if ($this->bIncludeNameDetails) $sSQL .= "null as names, "; + $sSQL .= "avg(ST_X(centroid)) as lon, avg(ST_Y(centroid)) as lat, "; + $sSQL .= $sImportanceSQL."-1.10 as importance, "; + $sSQL .= "(select max(p.importance*(p.rank_address+2)) from place_addressline s, placex p where s.place_id = min(location_property_aux.parent_place_id) and p.place_id = s.address_place_id and s.isaddress and p.importance is not null) as addressimportance, "; + $sSQL .= "null as extra_place "; + $sSQL .= "from location_property_aux where place_id in ($sPlaceIDs) "; + $sSQL .= "and 30 between $this->iMinAddressRank and $this->iMaxAddressRank "; + $sSQL .= "group by place_id"; + if (!$this->bDeDupe) $sSQL .= ", place_id"; + $sSQL .= ", get_address_by_language(place_id, -1, $sLanguagePrefArraySQL) "; + } } $sSQL .= " order by importance desc"; if (CONST_Debug) { echo "
"; var_dump($sSQL); } - $aSearchResults = $this->oDB->getAll($sSQL); - - if (PEAR::IsError($aSearchResults)) - { - failInternalError("Could not get details for place.", $sSQL, $aSearchResults); - } + $aSearchResults = chksql($this->oDB->getAll($sSQL), + "Could not get details for place."); return $aSearchResults; } @@ -780,7 +773,7 @@ addressimportance: cumulated importance of address elements extra_place: type of place (for admin boundaries, if there is a place tag) aBoundingBox: bounding Box - label: short description of the object class/type (English only) + label: short description of the object class/type (English only) name: full name (currently the same as langaddress) foundorder: secondary ordering for places with same importance */ @@ -836,27 +829,22 @@ $sViewboxCentreSQL .= ")'::geometry,4326)"; $sSQL = "select st_buffer(".$sViewboxCentreSQL.",".(float)($_GET['routewidth']/69).")"; - $this->sViewboxSmallSQL = $this->oDB->getOne($sSQL); - if (PEAR::isError($this->sViewboxSmallSQL)) - { - failInternalError("Could not get small viewbox.", $sSQL, $this->sViewboxSmallSQL); - } + $this->sViewboxSmallSQL = chksql($this->oDB->getOne($sSQL), + "Could not get small viewbox."); $this->sViewboxSmallSQL = "'".$this->sViewboxSmallSQL."'::geometry"; $sSQL = "select st_buffer(".$sViewboxCentreSQL.",".(float)($_GET['routewidth']/30).")"; - $this->sViewboxLargeSQL = $this->oDB->getOne($sSQL); - if (PEAR::isError($this->sViewboxLargeSQL)) - { - failInternalError("Could not get large viewbox.", $sSQL, $this->sViewboxLargeSQL); - } + $this->sViewboxLargeSQL = chksql($this->oDB->getOne($sSQL), + "Could not get large viewbox."); $this->sViewboxLargeSQL = "'".$this->sViewboxLargeSQL."'::geometry"; $bBoundingBoxSearch = $this->bBoundedSearch; } // Do we have anything that looks like a lat/lon pair? - if ( $aLooksLike = looksLikeLatLonPair($sQuery) ){ + if ( $aLooksLike = looksLikeLatLonPair($sQuery) ) + { $this->setNearPoint(array($aLooksLike['lat'], $aLooksLike['lon'])); - $sQuery = $aLooksLike['query']; + $sQuery = $aLooksLike['query']; } $aSearchResults = array(); @@ -864,21 +852,21 @@ { // Start with a blank search $aSearches = array( - array('iSearchRank' => 0, - 'iNamePhrase' => -1, - 'sCountryCode' => false, - 'aName' => array(), - 'aAddress' => array(), + array('iSearchRank' => 0, + 'iNamePhrase' => -1, + 'sCountryCode' => false, + 'aName' => array(), + 'aAddress' => array(), 'aFullNameAddress' => array(), - 'aNameNonSearch' => array(), + 'aNameNonSearch' => array(), 'aAddressNonSearch' => array(), - 'sOperator' => '', - 'aFeatureName' => array(), - 'sClass' => '', - 'sType' => '', - 'sHouseNumber' => '', - 'fLat' => '', - 'fLon' => '', + 'sOperator' => '', + 'aFeatureName' => array(), + 'sClass' => '', + 'sType' => '', + 'sHouseNumber' => '', + 'fLat' => '', + 'fLon' => '', 'fRadius' => '' ) ); @@ -913,11 +901,11 @@ foreach($aSpecialTermsRaw as $aSpecialTerm) { $sQuery = str_replace($aSpecialTerm[0], ' ', $sQuery); - $sToken = $this->oDB->getOne("select make_standard_name('".$aSpecialTerm[1]."') as string"); + $sToken = chksql($this->oDB->getOne("select make_standard_name('".$aSpecialTerm[1]."') as string")); $sSQL = 'select * from (select word_id,word_token, word, class, type, country_code, operator'; $sSQL .= ' from word where word_token in (\' '.$sToken.'\')) as x where (class is not null and class not in (\'place\')) or country_code is not null'; if (CONST_Debug) var_Dump($sSQL); - $aSearchWords = $this->oDB->getAll($sSQL); + $aSearchWords = chksql($this->oDB->getAll($sSQL)); $aNewSearches = array(); foreach($aSearches as $aSearch) { @@ -962,13 +950,8 @@ $aTokens = array(); foreach($aPhrases as $iPhrase => $sPhrase) { - $aPhrase = $this->oDB->getRow("select make_standard_name('".pg_escape_string($sPhrase)."') as string"); - if (PEAR::isError($aPhrase)) - { - userError("Illegal query string (not an UTF-8 string): ".$sPhrase); - if (CONST_Debug) var_dump($aPhrase); - exit; - } + $aPhrase = chksql($this->oDB->getRow("select make_standard_name('".pg_escape_string($sPhrase)."') as string"), + "Cannot nomralize query string (is it an UTF-8 string?)"); if (trim($aPhrase['string'])) { $aPhrases[$iPhrase] = $aPhrase; @@ -995,11 +978,14 @@ if (CONST_Debug) var_Dump($sSQL); $aValidTokens = array(); - if (sizeof($aTokens)) $aDatabaseWords = $this->oDB->getAll($sSQL); - else $aDatabaseWords = array(); - if (PEAR::IsError($aDatabaseWords)) + if (sizeof($aTokens)) { - failInternalError("Could not get word tokens.", $sSQL, $aDatabaseWords); + $aDatabaseWords = chksql($this->oDB->getAll($sSQL), + "Could not get word tokens."); + } + else + { + $aDatabaseWords = array(); } $aPossibleMainWordIDs = array(); $aWordFrequencyScores = array(); @@ -1079,6 +1065,7 @@ // TODO: suggestions // Start the search process + // array with: placeid => -1 | tiger-housenumber $aResultPlaceIDs = array(); $aGroupedSearches = $this->getGroupedSearches($aSearches, $aPhraseTypes, $aPhrases, $aValidTokens, $aWordFrequencyScores, $bStructuredPhrases); @@ -1196,6 +1183,7 @@ foreach($aSearches as $aSearch) { $iQueryLoop++; + $searchedHousenumber = -1; if (CONST_Debug) { echo "
Search Loop, group $iGroupLoop, loop $iQueryLoop"; } if (CONST_Debug) _debugDumpGroupedSearches(array($iGroupedRank => array($aSearch)), $aValidTokens); @@ -1214,7 +1202,7 @@ $sSQL .= " and _st_intersects($this->sViewboxSmallSQL, geometry)"; $sSQL .= " order by st_area(geometry) desc limit 1"; if (CONST_Debug) var_dump($sSQL); - $aPlaceIDs = $this->oDB->getCol($sSQL); + $aPlaceIDs = chksql($this->oDB->getCol($sSQL)); } else { @@ -1226,7 +1214,7 @@ if (!$bBoundingBoxSearch && !$aSearch['fLon']) continue; if (!$aSearch['sClass']) continue; $sSQL = "select count(*) from pg_tables where tablename = 'place_classtype_".$aSearch['sClass']."_".$aSearch['sType']."'"; - if ($this->oDB->getOne($sSQL)) + if (chksql($this->oDB->getOne($sSQL))) { $sSQL = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." ct"; if ($sCountryCodesSQL) $sSQL .= " join placex using (place_id)"; @@ -1239,7 +1227,7 @@ if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, ct.centroid) asc"; $sSQL .= " limit $this->iLimit"; if (CONST_Debug) var_dump($sSQL); - $aPlaceIDs = $this->oDB->getCol($sSQL); + $aPlaceIDs = chksql($this->oDB->getCol($sSQL)); // If excluded place IDs are given, it is fair to assume that // there have been results in the small box, so no further @@ -1254,7 +1242,7 @@ if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, ct.centroid) asc"; $sSQL .= " limit $this->iLimit"; if (CONST_Debug) var_dump($sSQL); - $aPlaceIDs = $this->oDB->getCol($sSQL); + $aPlaceIDs = chksql($this->oDB->getCol($sSQL)); } } else @@ -1265,10 +1253,16 @@ if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc"; $sSQL .= " limit $this->iLimit"; if (CONST_Debug) var_dump($sSQL); - $aPlaceIDs = $this->oDB->getCol($sSQL); + $aPlaceIDs = chksql($this->oDB->getCol($sSQL)); } } } + // If a coordinate is given, the search must either + // be for a name or a special search. Ignore everythin else. + else if ($aSearch['fLon'] && !sizeof($aSearch['aName']) && !sizeof($aSearch['aAddress']) && !$aSearch['sClass']) + { + $aPlaceIDs = array(); + } else { $aPlaceIDs = array(); @@ -1280,7 +1274,13 @@ if ($aSearch['sHouseNumber'] && sizeof($aSearch['aAddress'])) { $sHouseNumberRegex = '\\\\m'.$aSearch['sHouseNumber'].'\\\\M'; - $aOrder[] = "exists(select place_id from placex where parent_place_id = search_name.place_id and transliteration(housenumber) ~* E'".$sHouseNumberRegex."' limit 1) desc"; + $aOrder[] = ""; + $aOrder[0] = " (exists(select place_id from placex where parent_place_id = search_name.place_id"; + $aOrder[0] .= " and transliteration(housenumber) ~* E'".$sHouseNumberRegex."' limit 1) "; + // also housenumbers from interpolation lines table are needed + $aOrder[0] .= " or exists(select place_id from location_property_osmline where parent_place_id = search_name.place_id"; + $aOrder[0] .= " and ".intval($aSearch['sHouseNumber']).">=startnumber and ".intval($aSearch['sHouseNumber'])."<=endnumber limit 1))"; + $aOrder[0] .= " desc"; } // TODO: filter out the pointless search terms (2 letter name tokens and less) @@ -1370,11 +1370,8 @@ $sSQL .= " limit ".$this->iLimit; if (CONST_Debug) { var_dump($sSQL); } - $aViewBoxPlaceIDs = $this->oDB->getAll($sSQL); - if (PEAR::IsError($aViewBoxPlaceIDs)) - { - failInternalError("Could not get places for search terms.", $sSQL, $aViewBoxPlaceIDs); - } + $aViewBoxPlaceIDs = chksql($this->oDB->getAll($sSQL), + "Could not get places for search terms."); //var_dump($aViewBoxPlaceIDs); // Did we have an viewbox matches? $aPlaceIDs = array(); @@ -1392,12 +1389,14 @@ //var_Dump($aPlaceIDs); //exit; + //now search for housenumber, if housenumber provided if ($aSearch['sHouseNumber'] && sizeof($aPlaceIDs)) { + $searchedHousenumber = intval($aSearch['sHouseNumber']); $aRoadPlaceIDs = $aPlaceIDs; $sPlaceIDs = join(',',$aPlaceIDs); - // Now they are indexed look for a house attached to a street we found + // Now they are indexed, look for a house attached to a street we found $sHouseNumberRegex = '\\\\m'.$aSearch['sHouseNumber'].'\\\\M'; $sSQL = "select place_id from placex where parent_place_id in (".$sPlaceIDs.") and transliteration(housenumber) ~* E'".$sHouseNumberRegex."'"; if (sizeof($this->aExcludePlaceIDs)) @@ -1406,44 +1405,80 @@ } $sSQL .= " limit $this->iLimit"; if (CONST_Debug) var_dump($sSQL); - $aPlaceIDs = $this->oDB->getCol($sSQL); + $aPlaceIDs = chksql($this->oDB->getCol($sSQL)); + + // if nothing found, search in the interpolation line table + if(!sizeof($aPlaceIDs)) + { + // do we need to use transliteration and the regex for housenumbers??? + //new query for lines, not housenumbers anymore + if($searchedHousenumber%2 == 0){ + //if housenumber is even, look for housenumber in streets with interpolationtype even or all + $sSQL = "select distinct place_id from location_property_osmline where parent_place_id in (".$sPlaceIDs.") and (interpolationtype='even' or interpolationtype='all') and ".$searchedHousenumber.">=startnumber and ".$searchedHousenumber."<=endnumber"; + }else{ + //look for housenumber in streets with interpolationtype odd or all + $sSQL = "select distinct place_id from location_property_osmline where parent_place_id in (".$sPlaceIDs.") and (interpolationtype='odd' or interpolationtype='all') and ".$searchedHousenumber.">=startnumber and ".$searchedHousenumber."<=endnumber"; + } - // If not try the aux fallback table - if (!sizeof($aPlaceIDs)) + if (sizeof($this->aExcludePlaceIDs)) + { + $sSQL .= " and place_id not in (".join(',', $this->aExcludePlaceIDs).")"; + } + //$sSQL .= " limit $this->iLimit"; + if (CONST_Debug) var_dump($sSQL); + //get place IDs + $aPlaceIDs = chksql($this->oDB->getCol($sSQL, 0)); + } + + // If nothing found try the aux fallback table + if (CONST_Use_Aux_Location_data && !sizeof($aPlaceIDs)) { $sSQL = "select place_id from location_property_aux where parent_place_id in (".$sPlaceIDs.") and housenumber = '".pg_escape_string($aSearch['sHouseNumber'])."'"; if (sizeof($this->aExcludePlaceIDs)) { - $sSQL .= " and place_id not in (".join(',',$this->aExcludePlaceIDs).")"; + $sSQL .= " and parent_place_id not in (".join(',',$this->aExcludePlaceIDs).")"; } //$sSQL .= " limit $this->iLimit"; if (CONST_Debug) var_dump($sSQL); - $aPlaceIDs = $this->oDB->getCol($sSQL); + $aPlaceIDs = chksql($this->oDB->getCol($sSQL)); } - if (!sizeof($aPlaceIDs)) + //if nothing was found in placex or location_property_aux, then search in Tiger data for this housenumber(location_property_tiger) + if (CONST_Use_US_Tiger_Data && !sizeof($aPlaceIDs)) { - $sSQL = "select place_id from location_property_tiger where parent_place_id in (".$sPlaceIDs.") and housenumber = '".pg_escape_string($aSearch['sHouseNumber'])."'"; + //new query for lines, not housenumbers anymore + if($searchedHousenumber%2 == 0){ + //if housenumber is even, look for housenumber in streets with interpolationtype even or all + $sSQL = "select distinct place_id from location_property_tiger where parent_place_id in (".$sPlaceIDs.") and (interpolationtype='even' or interpolationtype='all') and ".$searchedHousenumber.">=startnumber and ".$searchedHousenumber."<=endnumber"; + }else{ + //look for housenumber in streets with interpolationtype odd or all + $sSQL = "select distinct place_id from location_property_tiger where parent_place_id in (".$sPlaceIDs.") and (interpolationtype='odd' or interpolationtype='all') and ".$searchedHousenumber.">=startnumber and ".$searchedHousenumber."<=endnumber"; + } + if (sizeof($this->aExcludePlaceIDs)) { - $sSQL .= " and place_id not in (".join(',',$this->aExcludePlaceIDs).")"; + $sSQL .= " and place_id not in (".join(',', $this->aExcludePlaceIDs).")"; } //$sSQL .= " limit $this->iLimit"; if (CONST_Debug) var_dump($sSQL); - $aPlaceIDs = $this->oDB->getCol($sSQL); + //get place IDs + $aPlaceIDs = chksql($this->oDB->getCol($sSQL, 0)); } - // Fallback to the road + // Fallback to the road (if no housenumber was found) if (!sizeof($aPlaceIDs) && preg_match('/[0-9]+/', $aSearch['sHouseNumber'])) { $aPlaceIDs = $aRoadPlaceIDs; + //set to -1, if no housenumbers were found + $searchedHousenumber = -1; } - + //else: housenumber was found, remains saved in searchedHousenumber } + if ($aSearch['sClass'] && sizeof($aPlaceIDs)) { - $sPlaceIDs = join(',',$aPlaceIDs); + $sPlaceIDs = join(',', $aPlaceIDs); $aClassPlaceIDs = array(); if (!$aSearch['sOperator'] || $aSearch['sOperator'] == 'name') @@ -1454,18 +1489,18 @@ if ($sCountryCodesSQL) $sSQL .= " and calculated_country_code in ($sCountryCodesSQL)"; $sSQL .= " order by rank_search asc limit $this->iLimit"; if (CONST_Debug) var_dump($sSQL); - $aClassPlaceIDs = $this->oDB->getCol($sSQL); + $aClassPlaceIDs = chksql($this->oDB->getCol($sSQL)); } if (!$aSearch['sOperator'] || $aSearch['sOperator'] == 'near') // & in { $sSQL = "select count(*) from pg_tables where tablename = 'place_classtype_".$aSearch['sClass']."_".$aSearch['sType']."'"; - $bCacheTable = $this->oDB->getOne($sSQL); + $bCacheTable = chksql($this->oDB->getOne($sSQL)); $sSQL = "select min(rank_search) from placex where place_id in ($sPlaceIDs)"; if (CONST_Debug) var_dump($sSQL); - $this->iMaxRank = ((int)$this->oDB->getOne($sSQL)); + $this->iMaxRank = ((int)chksql($this->oDB->getOne($sSQL))); // For state / country level searches the normal radius search doesn't work very well $sPlaceGeom = false; @@ -1474,7 +1509,7 @@ // Try and get a polygon to search in instead $sSQL = "select geometry from placex where place_id in ($sPlaceIDs) and rank_search < $this->iMaxRank + 5 and st_geometrytype(geometry) in ('ST_Polygon','ST_MultiPolygon') order by rank_search asc limit 1"; if (CONST_Debug) var_dump($sSQL); - $sPlaceGeom = $this->oDB->getOne($sSQL); + $sPlaceGeom = chksql($this->oDB->getOne($sSQL)); } if ($sPlaceGeom) @@ -1486,7 +1521,7 @@ $this->iMaxRank += 5; $sSQL = "select place_id from placex where place_id in ($sPlaceIDs) and rank_search < $this->iMaxRank"; if (CONST_Debug) var_dump($sSQL); - $aPlaceIDs = $this->oDB->getCol($sSQL); + $aPlaceIDs = chksql($this->oDB->getCol($sSQL)); $sPlaceIDs = join(',',$aPlaceIDs); } @@ -1525,7 +1560,7 @@ if ($this->iOffset) $sSQL .= " offset $this->iOffset"; $sSQL .= " limit $this->iLimit"; if (CONST_Debug) var_dump($sSQL); - $aClassPlaceIDs = array_merge($aClassPlaceIDs, $this->oDB->getCol($sSQL)); + $aClassPlaceIDs = array_merge($aClassPlaceIDs, chksql($this->oDB->getCol($sSQL))); } else { @@ -1547,7 +1582,7 @@ if ($this->iOffset) $sSQL .= " offset $this->iOffset"; $sSQL .= " limit $this->iLimit"; if (CONST_Debug) var_dump($sSQL); - $aClassPlaceIDs = array_merge($aClassPlaceIDs, $this->oDB->getCol($sSQL)); + $aClassPlaceIDs = array_merge($aClassPlaceIDs, chksql($this->oDB->getCol($sSQL))); } } } @@ -1558,16 +1593,12 @@ } - if (PEAR::IsError($aPlaceIDs)) - { - failInternalError("Could not get place IDs from tokens." ,$sSQL, $aPlaceIDs); - } - if (CONST_Debug) { echo "
Place IDs: "; var_Dump($aPlaceIDs); } foreach($aPlaceIDs as $iPlaceID) { - $aResultPlaceIDs[$iPlaceID] = $iPlaceID; + // array for placeID => -1 | Tiger housenumber + $aResultPlaceIDs[$iPlaceID] = $searchedHousenumber; } if ($iQueryLoop > 20) break; } @@ -1575,16 +1606,28 @@ if (isset($aResultPlaceIDs) && sizeof($aResultPlaceIDs) && ($this->iMinAddressRank != 0 || $this->iMaxAddressRank != 30)) { // Need to verify passes rank limits before dropping out of the loop (yuk!) - $sSQL = "select place_id from placex where place_id in (".join(',',$aResultPlaceIDs).") "; + // reduces the number of place ids, like a filter + // rank_address is 30 for interpolated housenumbers + $sSQL = "select place_id from placex where place_id in (".join(',',array_keys($aResultPlaceIDs)).") "; $sSQL .= "and (placex.rank_address between $this->iMinAddressRank and $this->iMaxAddressRank "; if (14 >= $this->iMinAddressRank && 14 <= $this->iMaxAddressRank) $sSQL .= " OR (extratags->'place') = 'city'"; if ($this->aAddressRankList) $sSQL .= " OR placex.rank_address in (".join(',',$this->aAddressRankList).")"; - $sSQL .= ") UNION select place_id from location_property_tiger where place_id in (".join(',',$aResultPlaceIDs).") "; - $sSQL .= "and (30 between $this->iMinAddressRank and $this->iMaxAddressRank "; - if ($this->aAddressRankList) $sSQL .= " OR 30 in (".join(',',$this->aAddressRankList).")"; - $sSQL .= ")"; + if (CONST_Use_US_Tiger_Data) + { + $sSQL .= ") UNION select place_id from location_property_tiger where place_id in (".join(',',array_keys($aResultPlaceIDs)).") "; + $sSQL .= "and (30 between $this->iMinAddressRank and $this->iMaxAddressRank "; + if ($this->aAddressRankList) $sSQL .= " OR 30 in (".join(',',$this->aAddressRankList).")"; + } + $sSQL .= ") UNION select place_id from location_property_osmline where place_id in (".join(',',array_keys($aResultPlaceIDs)).")"; + $sSQL .= " and (30 between $this->iMinAddressRank and $this->iMaxAddressRank)"; if (CONST_Debug) var_dump($sSQL); - $aResultPlaceIDs = $this->oDB->getCol($sSQL); + $aFilteredPlaceIDs = chksql($this->oDB->getCol($sSQL)); + $tempIDs = array(); + foreach($aFilteredPlaceIDs as $placeID) + { + $tempIDs[$placeID] = $aResultPlaceIDs[$placeID]; //assign housenumber to placeID + } + $aResultPlaceIDs = $tempIDs; } //exit; @@ -1603,9 +1646,17 @@ else { // Just interpret as a reverse geocode - $iPlaceID = geocodeReverse((float)$this->aNearPoint[0], (float)$this->aNearPoint[1]); - if ($iPlaceID) - $aSearchResults = $this->getDetails(array($iPlaceID)); + $oReverse = new ReverseGeocode($this->oDB); + $oReverse->setZoom(18); + + $aLookup = $oReverse->lookup((float)$this->aNearPoint[0], + (float)$this->aNearPoint[1], + false); + + if (CONST_Debug) var_dump("Reverse search", $aLookup); + + if ($aLookup['place_id']) + $aSearchResults = $this->getDetails(array($aLookup['place_id'] => -1)); else $aSearchResults = array(); } @@ -1633,19 +1684,19 @@ if (CONST_Debug) { echo 'Recheck words:<\i>'; var_dump($aRecheckWords); } + $oPlaceLookup = new PlaceLookup($this->oDB); + $oPlaceLookup->setIncludePolygonAsPoints($this->bIncludePolygonAsPoints); + $oPlaceLookup->setIncludePolygonAsText($this->bIncludePolygonAsText); + $oPlaceLookup->setIncludePolygonAsGeoJSON($this->bIncludePolygonAsGeoJSON); + $oPlaceLookup->setIncludePolygonAsKML($this->bIncludePolygonAsKML); + $oPlaceLookup->setIncludePolygonAsSVG($this->bIncludePolygonAsSVG); + $oPlaceLookup->setPolygonSimplificationThreshold($this->fPolygonSimplificationThreshold); + foreach($aSearchResults as $iResNum => $aResult) { // Default $fDiameter = getResultDiameter($aResult); - $oPlaceLookup = new PlaceLookup($this->oDB); - $oPlaceLookup->setIncludePolygonAsPoints($this->bIncludePolygonAsPoints); - $oPlaceLookup->setIncludePolygonAsText($this->bIncludePolygonAsText); - $oPlaceLookup->setIncludePolygonAsGeoJSON($this->bIncludePolygonAsGeoJSON); - $oPlaceLookup->setIncludePolygonAsKML($this->bIncludePolygonAsKML); - $oPlaceLookup->setIncludePolygonAsSVG($this->bIncludePolygonAsSVG); - $oPlaceLookup->setPolygonSimplificationThreshold($this->fPolygonSimplificationThreshold); - $aOutlineResult = $oPlaceLookup->getOutlines($aResult['place_id'], $aResult['lon'], $aResult['lat'], $fDiameter/2); if ($aOutlineResult) { @@ -1676,16 +1727,16 @@ { $aResult['label'] = $aClassType[$aResult['class'].':'.$aResult['type']]['label']; } - + // if tag '&addressdetails=1' is set in query if ($this->bIncludeAddressDetails) { - $aResult['address'] = getAddressDetails($this->oDB, $sLanguagePrefArraySQL, $aResult['place_id'], $aResult['country_code']); + // getAddressDetails() is defined in lib.php and uses the SQL function get_addressdata in functions.sql + $aResult['address'] = getAddressDetails($this->oDB, $sLanguagePrefArraySQL, $aResult['place_id'], $aResult['country_code'], $aResultPlaceIDs[$aResult['place_id']]); if ($aResult['extra_place'] == 'city' && !isset($aResult['address']['city'])) { $aResult['address'] = array_merge(array('city' => array_shift(array_values($aResult['address']))), $aResult['address']); } } - if ($this->bIncludeExtraTags) { if ($aResult['extra'])