X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/4c2c499860d631b746e0db10a5b2ff9cd279c241..c8c8471e737ff9344d98296cf6c238f9f659b3d4:/lib/lib.php diff --git a/lib/lib.php b/lib/lib.php index 8a1fb902..1564a83c 100644 --- a/lib/lib.php +++ b/lib/lib.php @@ -1,9 +1,34 @@

Internal Server Error

"; + echo '

Nominatim has encountered an internal error while processing your request. This is most likely because of a bug in the software.

'; + echo "

Details: ".$sError,"

"; + echo '

Feel free to report the bug in the OSM bug database. Please include the error message above an the URL you used.

'; + if (CONST_Debug) + { + echo "

Debugging Information


"; + if ($sSQL) { + echo "

SQL query

".$sSQL.""; + } + if ($vDumpVar) { + echo "

Result

"; + var_dump($vDumpVar); + echo ""; + } + } + echo "\n\n"; + exit; + + } + function fail($sError, $sUserError = false) { if (!$sUserError) $sUserError = $sError; - log('ERROR:'.$sError); + error_log('ERROR: '.$sError); echo $sUserError."\n"; exit; } @@ -24,7 +49,14 @@ $aLoadAverage = explode(' ',$sLoadAverage); return (int)$aLoadAverage[0]; } - + + function getProcessorCount() + { + $sCPU = file_get_contents('/proc/cpuinfo'); + preg_match_all('#processor : [0-9]+#', $sCPU, $aMatches); + return sizeof($aMatches[0]); + } + function bySearchRank($a, $b) { if ($a['iSearchRank'] == $b['iSearchRank']) return 0; @@ -33,12 +65,18 @@ function byImportance($a, $b) { + if ($a['importance'] != $b['importance']) + return ($a['importance'] > $b['importance']?-1:1); +/* if ($a['aPointPolygon']['numfeatures'] != $b['aPointPolygon']['numfeatures']) return ($a['aPointPolygon']['numfeatures'] > $b['aPointPolygon']['numfeatures']?-1:1); if ($a['aPointPolygon']['area'] != $b['aPointPolygon']['area']) return ($a['aPointPolygon']['area'] > $b['aPointPolygon']['area']?-1:1); - if ($a['importance'] != $b['importance']) - return ($a['importance'] < $b['importance']?-1:1); +// if ($a['levenshtein'] != $b['levenshtein']) +// return ($a['levenshtein'] < $b['levenshtein']?-1:1); + if ($a['rank_search'] != $b['rank_search']) + return ($a['rank_search'] < $b['rank_search']?-1:1); +*/ return ($a['foundorder'] < $b['foundorder']?-1:1); } @@ -49,7 +87,7 @@ { $_SERVER["HTTP_ACCEPT_LANGUAGE"] = $_GET['accept-language']; } - + $aLanguages = array(); if (preg_match_all('/(([a-z]{1,8})(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $aLanguagesParse, PREG_SET_ORDER)) { @@ -152,61 +190,20 @@ function gbPostcodeCalculate($sPostcode, $sPostcodeSector, $sPostcodeEnd, &$oDB) { // Try an exact match on the gb_postcode table - $sSQL = 'select \'AA\', ST_X(ST_Centroid(geometry)) as lon,ST_Y(ST_Centroid(geometry)) as lat from gb_postcode where upper(postcode) = \''.$sPostcode.'\''; + $sSQL = 'select \'AA\', ST_X(ST_Centroid(geometry)) as lon,ST_Y(ST_Centroid(geometry)) as lat from gb_postcode where postcode = \''.$sPostcode.'\''; $aNearPostcodes = $oDB->getAll($sSQL); if (PEAR::IsError($aNearPostcodes)) { var_dump($sSQL, $aNearPostcodes); exit; } - - if (!sizeof($aNearPostcodes)) - { - $sSQL = 'select substring(upper(postcode) from \'^[A-Z][A-Z]?[0-9][0-9A-Z]? [0-9]([A-Z][A-Z])$\'),ST_X(ST_Centroid(geometry)) as lon,ST_Y(ST_Centroid(geometry)) as lat from placex where country_code::text = \'gb\'::text AND substring(upper(postcode) from \'^([A-Z][A-Z]?[0-9][0-9A-Z]? [0-9])[A-Z][A-Z]$\') = \''.$sPostcodeSector.'\' and class=\'place\' and type=\'postcode\' '; - $sSQL .= ' union '; - $sSQL .= 'select substring(upper(postcode) from \'^[A-Z][A-Z]?[0-9][0-9A-Z]? [0-9]([A-Z][A-Z])$\'),ST_X(ST_Centroid(geometry)) as lon,ST_Y(ST_Centroid(geometry)) as lat from gb_postcode where substring(upper(postcode) from \'^([A-Z][A-Z]?[0-9][0-9A-Z]? [0-9])[A-Z][A-Z]$\') = \''.$sPostcodeSector.'\''; - $aNearPostcodes = $oDB->getAll($sSQL); - if (PEAR::IsError($aNearPostcodes)) - { - var_dump($sSQL, $aNearPostcodes); - exit; - } - } - - if (!sizeof($aNearPostcodes)) + + if (sizeof($aNearPostcodes)) { - return false; + return array(array('lat' => $aNearPostcodes[0]['lat'], 'lon' => $aNearPostcodes[0]['lon'], 'radius' => 0.005)); } - $fTotalLat = 0; - $fTotalLon = 0; - $fTotalFac = 0; - foreach($aNearPostcodes as $aPostcode) - { - $iDiff = gbPostcodeAlphaDifference($sPostcodeEnd, $aPostcode['substring'])*2 + 1; - if ($iDiff == 0) - $fFac = 1; - else - $fFac = 1/($iDiff*$iDiff); - - $fTotalFac += $fFac; - $fTotalLat += $aPostcode['lat'] * $fFac; - $fTotalLon += $aPostcode['lon'] * $fFac; - } - if ($fTotalFac) - { - $fLat = $fTotalLat / $fTotalFac; - $fLon = $fTotalLon / $fTotalFac; - $fRadius = min(0.1 / $fTotalFac, 0.02); - return array(array('lat' => $fLat, 'lon' => $fLon, 'radius' => $fRadius)); - } return false; - - /* - $fTotalFac is a suprisingly good indicator of accuracy - $iZoom = 18 + round(log($fTotalFac,32)); - $iZoom = max(13,min(18,$iZoom)); - */ } function usPostcodeCalculate($sPostcode, &$oDB) @@ -271,21 +268,21 @@ function getClassTypes() { return array( - 'boundary:adminitrative:2' => array('label'=>'Country','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), - 'boundary:adminitrative:4' => array('label'=>'State','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), - 'boundary:adminitrative:5' => array('label'=>'State District','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), - 'boundary:adminitrative:6' => array('label'=>'County','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), - 'boundary:adminitrative:8' => array('label'=>'City','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), - 'boundary:adminitrative:9' => array('label'=>'City District','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), - 'boundary:adminitrative:10' => array('label'=>'Suburb','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), - 'boundary:adminitrative:11' => array('label'=>'Neighbourhood','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), - 'place:city' => array('label'=>'City','frequency'=>66,'icon'=>'poi_place_city','defzoom'=>12, 'defdiameter' => 0.32,), + 'boundary:administrative:2' => array('label'=>'Country','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), 'place:country' => array('label'=>'Country','frequency'=>0,'icon'=>'poi_boundary_administrative','defzoom'=>6, 'defdiameter' => 15,), + 'boundary:administrative:4' => array('label'=>'State','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), 'place:state' => array('label'=>'State','frequency'=>0,'icon'=>'poi_boundary_administrative','defzoom'=>8, 'defdiameter' => 5.12,), - 'place:region' => array('label'=>'State','frequency'=>0,'icon'=>'poi_boundary_administrative','defzoom'=>8, 'defdiameter' => 5.12,), - 'place:island' => array('label'=>'Island','frequency'=>288,'icon'=>'','defzoom'=>11, 'defdiameter' => 0.64,), + 'boundary:administrative:5' => array('label'=>'State District','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), + 'boundary:administrative:6' => array('label'=>'County','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), 'place:county' => array('label'=>'County','frequency'=>108,'icon'=>'poi_boundary_administrative','defzoom'=>10, 'defdiameter' => 1.28,), - 'boundary:adminitrative' => array('label'=>'Administrative','frequency'=>413,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), + 'boundary:administrative:8' => array('label'=>'City','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), + 'place:city' => array('label'=>'City','frequency'=>66,'icon'=>'poi_place_city','defzoom'=>12, 'defdiameter' => 0.32,), + 'boundary:administrative:9' => array('label'=>'City District','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), + 'boundary:administrative:10' => array('label'=>'Suburb','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), + 'boundary:administrative:11' => array('label'=>'Neighbourhood','frequency'=>0,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), + 'place:region' => array('label'=>'Region','frequency'=>0,'icon'=>'poi_boundary_administrative','defzoom'=>8, 'defdiameter' => 5.12,), + 'place:island' => array('label'=>'Island','frequency'=>288,'icon'=>'','defzoom'=>11, 'defdiameter' => 0.64,), + 'boundary:administrative' => array('label'=>'Administrative','frequency'=>413,'icon'=>'poi_boundary_administrative', 'defdiameter' => 0.32,), 'place:town' => array('label'=>'Town','frequency'=>1497,'icon'=>'poi_place_town','defzoom'=>14, 'defdiameter' => 0.08,), 'place:village' => array('label'=>'Village','frequency'=>11230,'icon'=>'poi_place_village','defzoom'=>15, 'defdiameter' => 0.04,), 'place:hamlet' => array('label'=>'Hamlet','frequency'=>7075,'icon'=>'poi_place_village','defzoom'=>15, 'defdiameter' => 0.04,), @@ -428,6 +425,9 @@ 'amenity:shop' => array('label'=>'Shop','frequency'=>61,'icon'=>'',), 'place:house' => array('label'=>'House','frequency'=>2086,'icon'=>'','defzoom'=>18,), + 'place:house_name' => array('label'=>'House','frequency'=>2086,'icon'=>'','defzoom'=>18,), + 'place:house_number' => array('label'=>'House Number','frequency'=>2086,'icon'=>'','defzoom'=>18,), + 'place:country_code' => array('label'=>'Country Code','frequency'=>2086,'icon'=>'','defzoom'=>18,), // @@ -549,6 +549,7 @@ 'amenity:shopping' => array('label'=>'Shopping','frequency'=>21,'icon'=>'',), 'natural:scrub' => array('label'=>'Scrub','frequency'=>20,'icon'=>'',), 'natural:fen' => array('label'=>'Fen','frequency'=>20,'icon'=>'',), + 'building:yes' => array('label'=>'Building','frequency'=>200,'icon'=>'',), 'amenity:parking' => array('label'=>'Parking','frequency'=>3157,'icon'=>'',), 'highway:bus_stop' => array('label'=>'Bus Stop','frequency'=>35777,'icon'=>'transport_bus_stop2',), @@ -612,7 +613,7 @@ else { if (is_bool($xVal)) return $xVal?'true':'false'; - if (is_numeric($xVal)) return $xVal; +// if (is_numeric($xVal)) return $xVal; return '"'.str_replace('>','\\>',str_replace(array("\n","\r"),'\\n',str_replace(array("\n\r","\r\n"),'\\n',str_replace('"','\\"',$xVal)))).'"'; } } @@ -620,11 +621,17 @@ function _debugDumpGroupedSearches($aData, $aTokens) { $aWordsIDs = array(); - foreach($aTokens as $sToken => $aWords) + if ($aTokens) { - foreach($aWords as $aToken) + foreach($aTokens as $sToken => $aWords) { - $aWordsIDs[$aToken['word_id']] = $sToken.'('.$aToken['word_id'].')'; + if ($aWords) + { + foreach($aWords as $aToken) + { + $aWordsIDs[$aToken['word_id']] = $sToken.'('.$aToken['word_id'].')'; + } + } } } echo ""; @@ -649,11 +656,8 @@ $sSep = ''; foreach($aRow['aAddress'] as $iWordID) { -// if (!isset($aRow['aName'][$iWordID])) - { - echo $sSep.'#'.$aWordsIDs[$iWordID].'#'; - $sSep = ', '; - } + echo $sSep.'#'.$aWordsIDs[$iWordID].'#'; + $sSep = ', '; } echo ""; @@ -678,9 +682,41 @@ function getAddressDetails(&$oDB, $sLanguagePrefArraySQL, $iPlaceID, $sCountryCode = false, $bRaw = false) { - $aHouseNumber = $oDB->getRow('select housenumber, get_name_by_language(name,ARRAY[\'addr:housename\']) as housename,rank_search from placex where place_id = '.$iPlaceID); + $sSQL = "select *,get_name_by_language(name,$sLanguagePrefArraySQL) as localname from get_addressdata($iPlaceID)"; + IF (!$bRaw) $sSQL .= " WHERE isaddress OR type = 'country_code'"; + $sSQL .= " order by rank_address desc,isaddress desc"; + $aAddressLines = $oDB->getAll($sSQL); + if (PEAR::IsError($aAddressLines)) + { + var_dump($aAddressLines); + exit; + } + if ($bRaw) return $aAddressLines; +//echo "
";
+//var_dump($aAddressLines);
+		$aAddress = array();
+		$aClassType = getClassTypes();
+		foreach($aAddressLines as $aLine)
+		{
+			$aTypeLabel = false;
+			if (isset($aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']])) $aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']];
+			elseif (isset($aClassType[$aLine['class'].':'.$aLine['type']])) $aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type']];
+			else $aTypeLabel = array('simplelabel'=>$aLine['class']);
+			if ($aTypeLabel && ($aLine['localname'] || $aLine['housenumber']))
+			{
+				$sTypeLabel = strtolower(isset($aTypeLabel['simplelabel'])?$aTypeLabel['simplelabel']:$aTypeLabel['label']);
+				$sTypeLabel = str_replace(' ','_',$sTypeLabel);
+				if (!isset($aAddress[$sTypeLabel]) && $aLine['localname']) $aAddress[$sTypeLabel] = $aLine['localname']?$aLine['localname']:$aLine['housenumber'];
+			}
+		}
+//var_dump($aAddress);
+//exit;
+		return $aAddress;
+
+		$aHouseNumber = $oDB->getRow('select housenumber, get_name_by_language(name,ARRAY[\'addr:housename\']) as housename,rank_search,postcode from placex where place_id = '.$iPlaceID);
 		$sHouseNumber = $aHouseNumber['housenumber'];
 		$sHouseName = $aHouseNumber['housename'];
+		$sPostcode = $aHouseNumber['postcode'];
 		$iRank = $aHouseNumber['rank_search'];
 
 	        // Address
@@ -690,12 +726,8 @@
 		$sSQL .= " length(name::text) as namelength ";
 	        $sSQL .= " from place_addressline join placex on (address_place_id = placex.place_id)";
         	$sSQL .= " where place_addressline.place_id = $iPlaceID and (rank_address > 0 OR address_place_id = $iPlaceID)";
-// and isaddress";
-	        if ($sCountryCode)
-        	{
-                	$sSQL .= " and (placex.country_code IS NULL OR placex.country_code = '".$sCountryCode."' OR rank_address < 4)";
-	        }
-        	$sSQL .= " order by cached_rank_address desc,fromarea desc,distance asc,rank_search desc,namelength desc";
+		if (!$bRaw) $sSQL .= " and isaddress";
+        	$sSQL .= " order by cached_rank_address desc,isaddress desc,fromarea desc,distance asc,rank_search desc,namelength desc";
 //var_dump($sSQL);
 	        $aAddressLines = $oDB->getAll($sSQL);
         	if (PEAR::IsError($aAddressLines))
@@ -723,11 +755,18 @@
 				if ($aTypeLabel && ($aLine['localname'] || $aLine['housenumber']))
 				{
 					$sTypeLabel = strtolower(isset($aTypeLabel['simplelabel'])?$aTypeLabel['simplelabel']:$aTypeLabel['label']);
+					$sTypeLabel = str_replace(' ','_',$sTypeLabel);
 					if (!isset($aAddress[$sTypeLabel]) && $aLine['localname']) $aAddress[$sTypeLabel] = $aLine['localname']?$aLine['localname']:$aLine['housenumber'];
 				}
 				$iMinRank = $aLine['rank_address'];
 			}
 		}
+
+		if ($sPostcode)
+		{
+			$aAddress['postcode'] = $sPostcode;
+		}
+
 		if ($iMinRank > 4 && $sCountryCode)
 		{
 			$sSQL = "select get_name_by_language(country_name.name,$sLanguagePrefArraySQL) as name";
@@ -758,3 +797,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;
+	}