2013-07-10 18:14:17 +04:00
< ? php
2022-01-03 18:23:58 +03:00
/**
* SPDX - License - Identifier : GPL - 2.0 - only
*
* This file is part of Nominatim . ( https :// nominatim . org )
*
* Copyright ( C ) 2022 by the Nominatim developer community .
* For a full list of authors see the git log .
*/
2016-09-04 04:19:48 +03:00
2016-09-16 03:27:36 +03:00
namespace Nominatim ;
2020-12-15 12:09:55 +03:00
require_once ( CONST_LibDir . '/AddressDetails.php' );
require_once ( CONST_LibDir . '/Result.php' );
2017-10-20 16:41:26 +03:00
2016-09-04 04:19:48 +03:00
class PlaceLookup
{
protected $oDB ;
2017-10-22 20:01:14 +03:00
protected $aLangPrefOrderSql = " '' " ;
2016-09-04 04:19:48 +03:00
protected $bAddressDetails = false ;
protected $bExtraTags = false ;
protected $bNameDetails = false ;
protected $bIncludePolygonAsText = false ;
protected $bIncludePolygonAsGeoJSON = false ;
protected $bIncludePolygonAsKML = false ;
protected $bIncludePolygonAsSVG = false ;
protected $fPolygonSimplificationThreshold = 0.0 ;
2017-10-22 20:01:14 +03:00
protected $sAnchorSql = null ;
protected $sAddressRankListSql = null ;
protected $sAllowedTypesSQLList = null ;
protected $bDeDupe = true ;
2016-09-04 04:19:48 +03:00
2016-09-16 03:27:36 +03:00
public function __construct ( & $oDB )
2016-09-04 04:19:48 +03:00
{
$this -> oDB =& $oDB ;
}
2017-10-24 00:28:00 +03:00
public function doDeDupe ()
{
return $this -> bDeDupe ;
}
2018-07-11 00:38:27 +03:00
public function setIncludeAddressDetails ( $b )
2018-03-18 03:49:26 +03:00
{
2018-07-11 00:38:27 +03:00
$this -> bAddressDetails = $b ;
2018-03-18 03:49:26 +03:00
}
2017-10-24 00:28:00 +03:00
public function loadParamArray ( $oParams , $sGeomType = null )
2017-10-23 23:33:14 +03:00
{
$aLangs = $oParams -> getPreferredLanguages ();
$this -> aLangPrefOrderSql =
2019-02-24 18:14:36 +03:00
'ARRAY[' . join ( ',' , $this -> oDB -> getDBQuotedList ( $aLangs )) . ']' ;
2017-10-23 23:33:14 +03:00
$this -> bExtraTags = $oParams -> getBool ( 'extratags' , false );
$this -> bNameDetails = $oParams -> getBool ( 'namedetails' , false );
2017-10-24 00:28:00 +03:00
$this -> bDeDupe = $oParams -> getBool ( 'dedupe' , $this -> bDeDupe );
if ( $sGeomType === null || $sGeomType == 'geojson' ) {
$this -> bIncludePolygonAsGeoJSON = $oParams -> getBool ( 'polygon_geojson' );
}
2018-03-18 03:49:26 +03:00
if ( $oParams -> getString ( 'format' , '' ) !== 'geojson' ) {
if ( $sGeomType === null || $sGeomType == 'text' ) {
$this -> bIncludePolygonAsText = $oParams -> getBool ( 'polygon_text' );
}
if ( $sGeomType === null || $sGeomType == 'kml' ) {
$this -> bIncludePolygonAsKML = $oParams -> getBool ( 'polygon_kml' );
}
if ( $sGeomType === null || $sGeomType == 'svg' ) {
$this -> bIncludePolygonAsSVG = $oParams -> getBool ( 'polygon_svg' );
}
2017-10-24 00:28:00 +03:00
}
2017-10-23 23:33:14 +03:00
$this -> fPolygonSimplificationThreshold
= $oParams -> getFloat ( 'polygon_threshold' , 0.0 );
$iWantedTypes =
( $this -> bIncludePolygonAsText ? 1 : 0 ) +
( $this -> bIncludePolygonAsGeoJSON ? 1 : 0 ) +
( $this -> bIncludePolygonAsKML ? 1 : 0 ) +
( $this -> bIncludePolygonAsSVG ? 1 : 0 );
if ( $iWantedTypes > CONST_PolygonOutput_MaximumTypes ) {
if ( CONST_PolygonOutput_MaximumTypes ) {
2017-10-26 22:21:21 +03:00
userError ( 'Select only ' . CONST_PolygonOutput_MaximumTypes . ' polgyon output option' );
2017-10-23 23:33:14 +03:00
} else {
2017-10-26 22:21:21 +03:00
userError ( 'Polygon output is disabled' );
2017-10-23 23:33:14 +03:00
}
}
}
2017-10-24 00:28:00 +03:00
public function getMoreUrlParams ()
2017-10-22 20:01:14 +03:00
{
2017-10-24 00:28:00 +03:00
$aParams = array ();
2021-07-10 15:59:38 +03:00
if ( $this -> bAddressDetails ) {
$aParams [ 'addressdetails' ] = '1' ;
}
if ( $this -> bExtraTags ) {
$aParams [ 'extratags' ] = '1' ;
}
if ( $this -> bNameDetails ) {
$aParams [ 'namedetails' ] = '1' ;
}
2017-10-24 00:28:00 +03:00
2021-07-10 15:59:38 +03:00
if ( $this -> bIncludePolygonAsText ) {
$aParams [ 'polygon_text' ] = '1' ;
}
if ( $this -> bIncludePolygonAsGeoJSON ) {
$aParams [ 'polygon_geojson' ] = '1' ;
}
if ( $this -> bIncludePolygonAsKML ) {
$aParams [ 'polygon_kml' ] = '1' ;
}
if ( $this -> bIncludePolygonAsSVG ) {
$aParams [ 'polygon_svg' ] = '1' ;
}
2017-10-24 00:28:00 +03:00
if ( $this -> fPolygonSimplificationThreshold > 0.0 ) {
$aParams [ 'polygon_threshold' ] = $this -> fPolygonSimplificationThreshold ;
}
2021-07-10 15:59:38 +03:00
if ( ! $this -> bDeDupe ) {
$aParams [ 'dedupe' ] = '0' ;
}
2017-10-24 00:28:00 +03:00
return $aParams ;
2017-10-22 20:01:14 +03:00
}
2017-10-24 00:28:00 +03:00
public function setAnchorSql ( $sPoint )
2017-10-22 20:01:14 +03:00
{
2017-10-24 00:28:00 +03:00
$this -> sAnchorSql = $sPoint ;
2017-10-22 20:01:14 +03:00
}
public function setAddressRankList ( $aList )
{
$this -> sAddressRankListSql = '(' . join ( ',' , $aList ) . ')' ;
}
public function setAllowedTypesSQLList ( $sSql )
{
$this -> sAllowedTypesSQLList = $sSql ;
}
2016-09-16 03:27:36 +03:00
public function setLanguagePreference ( $aLangPrefOrder )
2016-09-04 04:19:48 +03:00
{
2019-02-24 18:14:36 +03:00
$this -> aLangPrefOrderSql = $this -> oDB -> getArraySQL (
$this -> oDB -> getDBQuotedList ( $aLangPrefOrder )
);
2016-09-04 04:19:48 +03:00
}
2017-10-22 20:01:14 +03:00
private function addressImportanceSql ( $sGeometry , $sPlaceId )
2017-10-20 16:41:26 +03:00
{
2017-10-22 20:01:14 +03:00
if ( $this -> sAnchorSql ) {
$sSQL = 'ST_Distance(' . $this -> sAnchorSql . ',' . $sGeometry . ')' ;
} else {
$sSQL = '(SELECT max(ai_p.importance * (ai_p.rank_address + 2))' ;
$sSQL .= ' FROM place_addressline ai_s, placex ai_p' ;
$sSQL .= ' WHERE ai_s.place_id = ' . $sPlaceId ;
$sSQL .= ' AND ai_p.place_id = ai_s.address_place_id ' ;
$sSQL .= ' AND ai_s.isaddress ' ;
$sSQL .= ' AND ai_p.importance is not null)' ;
}
return $sSQL . ' AS addressimportance,' ;
}
private function langAddressSql ( $sHousenumber )
{
2021-07-10 15:59:38 +03:00
if ( $this -> bAddressDetails ) {
2018-07-11 00:38:27 +03:00
return '' ; // langaddress will be computed from address details
2021-07-10 15:59:38 +03:00
}
2018-07-11 00:38:27 +03:00
2017-10-22 20:01:14 +03:00
return 'get_address_by_language(place_id,' . $sHousenumber . ',' . $this -> aLangPrefOrderSql . ') AS langaddress,' ;
2017-10-20 16:41:26 +03:00
}
2016-09-16 03:27:36 +03:00
public function lookupOSMID ( $sType , $iID )
2016-09-04 04:19:48 +03:00
{
2019-03-10 17:42:58 +03:00
$sSQL = 'select place_id from placex where osm_type = :type and osm_id = :id' ;
$iPlaceID = $this -> oDB -> getOne ( $sSQL , array ( ':type' => $sType , ':id' => $iID ));
2016-09-04 04:19:48 +03:00
2017-10-20 16:41:26 +03:00
if ( ! $iPlaceID ) {
return null ;
}
2017-10-22 21:20:56 +03:00
$aResults = $this -> lookup ( array ( $iPlaceID => new Result ( $iPlaceID )));
2018-05-22 13:22:15 +03:00
return empty ( $aResults ) ? null : reset ( $aResults );
2016-09-04 04:19:48 +03:00
}
2017-10-22 21:20:56 +03:00
public function lookup ( $aResults , $iMinRank = 0 , $iMaxRank = 30 )
2016-09-04 04:19:48 +03:00
{
2018-05-22 13:22:15 +03:00
Debug :: newFunction ( 'Place lookup' );
if ( empty ( $aResults )) {
2017-10-22 21:20:56 +03:00
return array ();
2017-10-20 16:41:26 +03:00
}
2017-10-22 20:01:14 +03:00
$aSubSelects = array ();
$sPlaceIDs = Result :: joinIdsByTable ( $aResults , Result :: TABLE_PLACEX );
if ( $sPlaceIDs ) {
2018-05-22 13:22:15 +03:00
Debug :: printVar ( 'Ids from placex' , $sPlaceIDs );
2017-10-22 20:01:14 +03:00
$sSQL = 'SELECT ' ;
$sSQL .= ' osm_type,' ;
$sSQL .= ' osm_id,' ;
$sSQL .= ' class,' ;
$sSQL .= ' type,' ;
$sSQL .= ' admin_level,' ;
$sSQL .= ' rank_search,' ;
$sSQL .= ' rank_address,' ;
$sSQL .= ' min(place_id) AS place_id,' ;
$sSQL .= ' min(parent_place_id) AS parent_place_id,' ;
2017-10-25 21:11:51 +03:00
$sSQL .= ' -1 as housenumber,' ;
2017-10-22 20:01:14 +03:00
$sSQL .= ' country_code,' ;
$sSQL .= $this -> langAddressSql ( '-1' );
$sSQL .= ' get_name_by_language(name,' . $this -> aLangPrefOrderSql . ') AS placename,' ;
$sSQL .= " get_name_by_language(name, ARRAY['ref']) AS ref, " ;
if ( $this -> bExtraTags ) {
$sSQL .= 'hstore_to_json(extratags)::text AS extra,' ;
}
if ( $this -> bNameDetails ) {
$sSQL .= 'hstore_to_json(name)::text AS names,' ;
}
$sSQL .= ' avg(ST_X(centroid)) AS lon, ' ;
$sSQL .= ' avg(ST_Y(centroid)) AS lat, ' ;
$sSQL .= ' COALESCE(importance,0.75-(rank_search::float/40)) AS importance, ' ;
$sSQL .= $this -> addressImportanceSql (
2017-10-22 22:59:08 +03:00
'ST_Collect(centroid)' ,
2017-10-22 20:01:14 +03:00
'min(CASE WHEN placex.rank_search < 28 THEN placex.place_id ELSE placex.parent_place_id END)'
);
2020-02-29 00:07:06 +03:00
$sSQL .= " COALESCE(extratags->'place', extratags->'linked_place') AS extra_place " ;
2017-10-22 20:01:14 +03:00
$sSQL .= ' FROM placex' ;
$sSQL .= " WHERE place_id in ( $sPlaceIDs ) " ;
$sSQL .= ' AND (' ;
$sSQL .= " placex.rank_address between $iMinRank and $iMaxRank " ;
if ( 14 >= $iMinRank && 14 <= $iMaxRank ) {
$sSQL .= " OR (extratags->'place') = 'city' " ;
}
if ( $this -> sAddressRankListSql ) {
$sSQL .= ' OR placex.rank_address in ' . $this -> sAddressRankListSql ;
}
2017-10-26 22:21:21 +03:00
$sSQL .= ' ) ' ;
2017-10-22 20:01:14 +03:00
if ( $this -> sAllowedTypesSQLList ) {
$sSQL .= 'AND placex.class in ' . $this -> sAllowedTypesSQLList ;
}
$sSQL .= ' AND linked_place_id is null ' ;
$sSQL .= ' GROUP BY ' ;
$sSQL .= ' osm_type, ' ;
$sSQL .= ' osm_id, ' ;
$sSQL .= ' class, ' ;
$sSQL .= ' type, ' ;
$sSQL .= ' admin_level, ' ;
$sSQL .= ' rank_search, ' ;
$sSQL .= ' rank_address, ' ;
$sSQL .= ' housenumber,' ;
$sSQL .= ' country_code, ' ;
$sSQL .= ' importance, ' ;
2021-07-10 15:59:38 +03:00
if ( ! $this -> bDeDupe ) {
$sSQL .= 'place_id,' ;
}
if ( ! $this -> bAddressDetails ) {
$sSQL .= 'langaddress, ' ;
}
2017-10-22 20:01:14 +03:00
$sSQL .= ' placename, ' ;
$sSQL .= ' ref, ' ;
2021-07-10 15:59:38 +03:00
if ( $this -> bExtraTags ) {
$sSQL .= 'extratags, ' ;
}
if ( $this -> bNameDetails ) {
$sSQL .= 'name, ' ;
}
2020-03-02 00:24:32 +03:00
$sSQL .= ' extra_place ' ;
2017-10-22 20:01:14 +03:00
$aSubSelects [] = $sSQL ;
2016-09-04 04:19:48 +03:00
}
2017-10-22 20:01:14 +03:00
// postcode table
$sPlaceIDs = Result :: joinIdsByTable ( $aResults , Result :: TABLE_POSTCODE );
if ( $sPlaceIDs ) {
2018-05-22 13:22:15 +03:00
Debug :: printVar ( 'Ids from location_postcode' , $sPlaceIDs );
2017-10-22 20:01:14 +03:00
$sSQL = 'SELECT' ;
$sSQL .= " 'P' as osm_type, " ;
2017-10-26 22:21:21 +03:00
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id = lp.parent_place_id) as osm_id,' ;
2017-10-22 20:01:14 +03:00
$sSQL .= " 'place' as class, 'postcode' as type, " ;
2018-08-05 16:47:55 +03:00
$sSQL .= ' null::smallint as admin_level, rank_search, rank_address,' ;
2017-10-22 20:01:14 +03:00
$sSQL .= ' place_id, parent_place_id,' ;
2018-08-05 16:47:55 +03:00
$sSQL .= ' -1 as housenumber,' ;
2017-10-22 20:01:14 +03:00
$sSQL .= ' country_code,' ;
$sSQL .= $this -> langAddressSql ( '-1' );
2017-10-26 22:21:21 +03:00
$sSQL .= ' postcode as placename,' ;
$sSQL .= ' postcode as ref,' ;
2021-07-10 15:59:38 +03:00
if ( $this -> bExtraTags ) {
$sSQL .= 'null::text AS extra,' ;
}
if ( $this -> bNameDetails ) {
$sSQL .= 'null::text AS names,' ;
}
2017-10-26 22:21:21 +03:00
$sSQL .= ' ST_x(geometry) AS lon, ST_y(geometry) AS lat,' ;
$sSQL .= ' (0.75-(rank_search::float/40)) AS importance, ' ;
2017-10-22 20:01:14 +03:00
$sSQL .= $this -> addressImportanceSql ( 'geometry' , 'lp.parent_place_id' );
2018-08-05 16:47:55 +03:00
$sSQL .= ' null::text AS extra_place ' ;
2017-10-26 22:21:21 +03:00
$sSQL .= 'FROM location_postcode lp' ;
2017-10-22 20:01:14 +03:00
$sSQL .= " WHERE place_id in ( $sPlaceIDs ) " ;
$sSQL .= " AND lp.rank_address between $iMinRank and $iMaxRank " ;
$aSubSelects [] = $sSQL ;
}
2016-09-04 04:19:48 +03:00
2017-10-22 20:01:14 +03:00
// All other tables are rank 30 only.
if ( $iMaxRank == 30 ) {
// TIGER table
if ( CONST_Use_US_Tiger_Data ) {
$sPlaceIDs = Result :: joinIdsByTable ( $aResults , Result :: TABLE_TIGER );
if ( $sPlaceIDs ) {
2018-05-22 13:22:15 +03:00
Debug :: printVar ( 'Ids from Tiger table' , $sPlaceIDs );
2017-10-22 20:01:14 +03:00
$sHousenumbers = Result :: sqlHouseNumberTable ( $aResults , Result :: TABLE_TIGER );
// Tiger search only if a housenumber was searched and if it was found
// (realized through a join)
2017-10-26 22:21:21 +03:00
$sSQL = ' SELECT ' ;
2017-10-22 20:01:14 +03:00
$sSQL .= " 'T' AS osm_type, " ;
2017-10-26 22:21:21 +03:00
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id=blub.parent_place_id) as osm_id, ' ;
2017-10-22 20:01:14 +03:00
$sSQL .= " 'place' AS class, " ;
$sSQL .= " 'house' AS type, " ;
2018-08-05 16:47:55 +03:00
$sSQL .= ' null::smallint AS admin_level, ' ;
2017-10-22 20:01:14 +03:00
$sSQL .= ' 30 AS rank_search, ' ;
$sSQL .= ' 30 AS rank_address, ' ;
$sSQL .= ' place_id, ' ;
$sSQL .= ' parent_place_id, ' ;
$sSQL .= ' housenumber_for_place as housenumber,' ;
$sSQL .= " 'us' AS country_code, " ;
$sSQL .= $this -> langAddressSql ( 'housenumber_for_place' );
2018-08-05 16:47:55 +03:00
$sSQL .= ' null::text AS placename, ' ;
$sSQL .= ' null::text AS ref, ' ;
2021-07-10 15:59:38 +03:00
if ( $this -> bExtraTags ) {
$sSQL .= 'null::text AS extra,' ;
}
if ( $this -> bNameDetails ) {
$sSQL .= 'null::text AS names,' ;
}
2017-10-26 22:21:21 +03:00
$sSQL .= ' st_x(centroid) AS lon, ' ;
$sSQL .= ' st_y(centroid) AS lat,' ;
$sSQL .= ' -1.15 AS importance, ' ;
2017-10-22 20:01:14 +03:00
$sSQL .= $this -> addressImportanceSql ( 'centroid' , 'blub.parent_place_id' );
2018-08-05 16:47:55 +03:00
$sSQL .= ' null::text AS extra_place ' ;
2017-10-26 22:21:21 +03:00
$sSQL .= ' FROM (' ;
$sSQL .= ' SELECT place_id, ' ; // interpolate the Tiger housenumbers here
2022-01-27 16:08:08 +03:00
$sSQL .= ' CASE WHEN startnumber != endnumber' ;
$sSQL .= ' THEN ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float)' ;
$sSQL .= ' ELSE ST_LineInterpolatePoint(linegeo, 0.5) END AS centroid, ' ;
2017-10-26 22:21:21 +03:00
$sSQL .= ' parent_place_id, ' ;
$sSQL .= ' housenumber_for_place' ;
$sSQL .= ' FROM (' ;
$sSQL .= ' location_property_tiger ' ;
$sSQL .= ' JOIN (values ' . $sHousenumbers . ') AS housenumbers(place_id, housenumber_for_place) USING(place_id)) ' ;
$sSQL .= ' WHERE ' ;
$sSQL .= ' housenumber_for_place >= startnumber' ;
$sSQL .= ' AND housenumber_for_place <= endnumber' ;
$sSQL .= ' ) AS blub' ; //postgres wants an alias here
2017-10-22 20:01:14 +03:00
$aSubSelects [] = $sSQL ;
}
}
2016-09-04 04:19:48 +03:00
2017-10-22 20:01:14 +03:00
// osmline - interpolated housenumbers
$sPlaceIDs = Result :: joinIdsByTable ( $aResults , Result :: TABLE_OSMLINE );
if ( $sPlaceIDs ) {
2018-05-22 13:22:15 +03:00
Debug :: printVar ( 'Ids from interpolation' , $sPlaceIDs );
2017-10-22 20:01:14 +03:00
$sHousenumbers = Result :: sqlHouseNumberTable ( $aResults , Result :: TABLE_OSMLINE );
// interpolation line search only if a housenumber was searched
// (realized through a join)
2017-10-26 22:21:21 +03:00
$sSQL = 'SELECT ' ;
2017-10-22 20:01:14 +03:00
$sSQL .= " 'W' AS osm_type, " ;
2017-10-26 22:21:21 +03:00
$sSQL .= ' osm_id, ' ;
2017-10-22 20:01:14 +03:00
$sSQL .= " 'place' AS class, " ;
$sSQL .= " 'house' AS type, " ;
2018-08-05 16:47:55 +03:00
$sSQL .= ' null::smallint AS admin_level, ' ;
2017-10-22 20:01:14 +03:00
$sSQL .= ' 30 AS rank_search, ' ;
$sSQL .= ' 30 AS rank_address, ' ;
$sSQL .= ' place_id, ' ;
$sSQL .= ' parent_place_id, ' ;
$sSQL .= ' housenumber_for_place as housenumber,' ;
$sSQL .= ' country_code, ' ;
$sSQL .= $this -> langAddressSql ( 'housenumber_for_place' );
2018-08-05 16:47:55 +03:00
$sSQL .= ' null::text AS placename, ' ;
$sSQL .= ' null::text AS ref, ' ;
2021-07-10 15:59:38 +03:00
if ( $this -> bExtraTags ) {
$sSQL .= 'null::text AS extra, ' ;
}
if ( $this -> bNameDetails ) {
$sSQL .= 'null::text AS names, ' ;
}
2017-10-22 20:01:14 +03:00
$sSQL .= ' st_x(centroid) AS lon, ' ;
$sSQL .= ' st_y(centroid) AS lat, ' ;
// slightly smaller than the importance for normal houses
2017-10-26 22:21:21 +03:00
$sSQL .= ' -0.1 AS importance, ' ;
2017-10-22 20:01:14 +03:00
$sSQL .= $this -> addressImportanceSql ( 'centroid' , 'blub.parent_place_id' );
2018-08-05 16:47:55 +03:00
$sSQL .= ' null::text AS extra_place ' ;
2017-10-26 22:21:21 +03:00
$sSQL .= ' FROM (' ;
$sSQL .= ' SELECT ' ;
$sSQL .= ' osm_id, ' ;
$sSQL .= ' place_id, ' ;
$sSQL .= ' country_code, ' ;
$sSQL .= ' CASE ' ; // interpolate the housenumbers here
$sSQL .= ' WHEN startnumber != endnumber ' ;
$sSQL .= ' THEN ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) ' ;
2022-01-26 23:24:24 +03:00
$sSQL .= ' ELSE linegeo ' ;
2017-10-26 22:21:21 +03:00
$sSQL .= ' END as centroid, ' ;
$sSQL .= ' parent_place_id, ' ;
$sSQL .= ' housenumber_for_place ' ;
$sSQL .= ' FROM (' ;
$sSQL .= ' location_property_osmline ' ;
$sSQL .= ' JOIN (values ' . $sHousenumbers . ') AS housenumbers(place_id, housenumber_for_place) USING(place_id)' ;
$sSQL .= ' ) ' ;
$sSQL .= ' WHERE housenumber_for_place >= 0 ' ;
$sSQL .= ' ) as blub' ; //postgres wants an alias here
2017-10-22 20:01:14 +03:00
$aSubSelects [] = $sSQL ;
}
2016-09-04 04:19:48 +03:00
}
2018-05-22 13:22:15 +03:00
if ( empty ( $aSubSelects )) {
2017-10-22 21:20:56 +03:00
return array ();
2017-10-22 20:01:14 +03:00
}
2018-05-22 13:22:15 +03:00
$sSQL = join ( ' UNION ' , $aSubSelects );
Debug :: printSQL ( $sSQL );
2019-03-10 17:42:58 +03:00
$aPlaces = $this -> oDB -> getAll ( $sSQL , null , 'Could not lookup place' );
2017-10-22 20:01:14 +03:00
foreach ( $aPlaces as & $aPlace ) {
2019-05-01 00:21:53 +03:00
$aPlace [ 'importance' ] = ( float ) $aPlace [ 'importance' ];
2017-10-22 20:01:14 +03:00
if ( $this -> bAddressDetails ) {
// to get addressdetails for tiger data, the housenumber is needed
2018-07-11 00:38:27 +03:00
$aPlace [ 'address' ] = new AddressDetails (
$this -> oDB ,
2018-03-18 03:49:26 +03:00
$aPlace [ 'place_id' ],
2018-07-11 00:38:27 +03:00
$aPlace [ 'housenumber' ],
$this -> aLangPrefOrderSql
2018-03-18 03:49:26 +03:00
);
2018-07-11 00:38:27 +03:00
$aPlace [ 'langaddress' ] = $aPlace [ 'address' ] -> getLocaleAddress ();
2018-03-18 03:49:26 +03:00
}
2017-10-22 20:01:14 +03:00
if ( $this -> bExtraTags ) {
if ( $aPlace [ 'extra' ]) {
$aPlace [ 'sExtraTags' ] = json_decode ( $aPlace [ 'extra' ]);
} else {
$aPlace [ 'sExtraTags' ] = ( object ) array ();
}
}
if ( $this -> bNameDetails ) {
2022-03-17 13:02:02 +03:00
$aPlace [ 'sNameDetails' ] = $this -> extractNames ( $aPlace [ 'names' ]);
2017-10-22 20:01:14 +03:00
}
2020-05-16 00:00:04 +03:00
$aPlace [ 'addresstype' ] = ClassTypes\getLabelTag (
2020-05-18 23:20:36 +03:00
$aPlace ,
$aPlace [ 'country_code' ]
2018-07-10 00:20:46 +03:00
);
2020-08-26 18:15:11 +03:00
$aResults [ $aPlace [ 'place_id' ]] = $aPlace ;
2016-09-04 04:19:48 +03:00
}
2020-08-27 10:33:21 +03:00
$aResults = array_filter (
$aResults ,
function ( $v ) {
return ! ( $v instanceof Result );
}
);
2020-08-26 18:15:11 +03:00
Debug :: printVar ( 'Places' , $aResults );
2016-09-04 04:19:48 +03:00
2020-08-26 18:15:11 +03:00
return $aResults ;
2016-09-04 04:19:48 +03:00
}
2022-03-17 13:02:02 +03:00
private function extractNames ( $sNames )
{
if ( ! $sNames ) {
return ( object ) array ();
}
$aFullNames = json_decode ( $sNames );
$aNames = array ();
foreach ( $aFullNames as $sKey => $sValue ) {
if ( strpos ( $sKey , '_place_' ) === 0 ) {
$sSubKey = substr ( $sKey , 7 );
if ( array_key_exists ( $sSubKey , $aFullNames )) {
$aNames [ $sKey ] = $sValue ;
} else {
$aNames [ $sSubKey ] = $sValue ;
}
} else {
$aNames [ $sKey ] = $sValue ;
}
}
return $aNames ;
}
2016-09-14 04:16:46 +03:00
/* returns an array which will contain the keys
* aBoundingBox
* and may also contain one or more of the keys
* asgeojson
* askml
* assvg
* astext
* lat
* lon
*/
2018-05-18 16:34:50 +03:00
public function getOutlines ( $iPlaceID , $fLon = null , $fLat = null , $fRadius = null , $fLonReverse = null , $fLatReverse = null )
2016-09-04 04:19:48 +03:00
{
$aOutlineResult = array ();
2021-07-10 15:59:38 +03:00
if ( ! $iPlaceID ) {
return $aOutlineResult ;
}
2016-09-04 04:19:48 +03:00
2020-12-15 17:37:31 +03:00
// Get the bounding box and outline polygon
$sSQL = 'select place_id,0 as numfeatures,st_area(geometry) as area,' ;
if ( $fLonReverse != null && $fLatReverse != null ) {
$sSQL .= ' ST_Y(closest_point) as centrelat,' ;
$sSQL .= ' ST_X(closest_point) as centrelon,' ;
} else {
$sSQL .= ' ST_Y(centroid) as centrelat, ST_X(centroid) as centrelon,' ;
}
$sSQL .= ' ST_YMin(geometry) as minlat,ST_YMax(geometry) as maxlat,' ;
$sSQL .= ' ST_XMin(geometry) as minlon,ST_XMax(geometry) as maxlon' ;
2021-07-10 15:59:38 +03:00
if ( $this -> bIncludePolygonAsGeoJSON ) {
$sSQL .= ',ST_AsGeoJSON(geometry) as asgeojson' ;
}
if ( $this -> bIncludePolygonAsKML ) {
$sSQL .= ',ST_AsKML(geometry) as askml' ;
}
if ( $this -> bIncludePolygonAsSVG ) {
$sSQL .= ',ST_AsSVG(geometry) as assvg' ;
}
if ( $this -> bIncludePolygonAsText ) {
$sSQL .= ',ST_AsText(geometry) as astext' ;
}
2020-12-15 17:37:31 +03:00
if ( $fLonReverse != null && $fLatReverse != null ) {
$sFrom = ' from (SELECT * , CASE WHEN (class = \'highway\') AND (ST_GeometryType(geometry) = \'ST_LineString\') THEN ' ;
$sFrom .= ' ST_ClosestPoint(geometry, ST_SetSRID(ST_Point(' . $fLatReverse . ',' . $fLonReverse . '),4326))' ;
$sFrom .= ' ELSE centroid END AS closest_point' ;
$sFrom .= ' from placex where place_id = ' . $iPlaceID . ') as plx' ;
} else {
$sFrom = ' from placex where place_id = ' . $iPlaceID ;
}
if ( $this -> fPolygonSimplificationThreshold > 0 ) {
$sSQL .= ' from (select place_id,centroid,ST_SimplifyPreserveTopology(geometry,' . $this -> fPolygonSimplificationThreshold . ') as geometry' . $sFrom . ') as plx' ;
} else {
$sSQL .= $sFrom ;
}
2016-09-04 04:19:48 +03:00
2020-12-15 17:37:31 +03:00
$aPointPolygon = $this -> oDB -> getRow ( $sSQL , null , 'Could not get outline' );
2016-09-04 04:19:48 +03:00
2020-12-15 17:37:31 +03:00
if ( $aPointPolygon && $aPointPolygon [ 'place_id' ]) {
if ( $aPointPolygon [ 'centrelon' ] !== null && $aPointPolygon [ 'centrelat' ] !== null ) {
$aOutlineResult [ 'lat' ] = $aPointPolygon [ 'centrelat' ];
$aOutlineResult [ 'lon' ] = $aPointPolygon [ 'centrelon' ];
}
2016-09-04 04:19:48 +03:00
2021-07-10 15:59:38 +03:00
if ( $this -> bIncludePolygonAsGeoJSON ) {
$aOutlineResult [ 'asgeojson' ] = $aPointPolygon [ 'asgeojson' ];
}
if ( $this -> bIncludePolygonAsKML ) {
$aOutlineResult [ 'askml' ] = $aPointPolygon [ 'askml' ];
}
if ( $this -> bIncludePolygonAsSVG ) {
$aOutlineResult [ 'assvg' ] = $aPointPolygon [ 'assvg' ];
}
if ( $this -> bIncludePolygonAsText ) {
$aOutlineResult [ 'astext' ] = $aPointPolygon [ 'astext' ];
}
2016-09-08 04:16:22 +03:00
2020-12-15 17:37:31 +03:00
if ( abs ( $aPointPolygon [ 'minlat' ] - $aPointPolygon [ 'maxlat' ]) < 0.0000001 ) {
$aPointPolygon [ 'minlat' ] = $aPointPolygon [ 'minlat' ] - $fRadius ;
$aPointPolygon [ 'maxlat' ] = $aPointPolygon [ 'maxlat' ] + $fRadius ;
}
2016-09-04 04:19:48 +03:00
2020-12-15 17:37:31 +03:00
if ( abs ( $aPointPolygon [ 'minlon' ] - $aPointPolygon [ 'maxlon' ]) < 0.0000001 ) {
$aPointPolygon [ 'minlon' ] = $aPointPolygon [ 'minlon' ] - $fRadius ;
$aPointPolygon [ 'maxlon' ] = $aPointPolygon [ 'maxlon' ] + $fRadius ;
2016-09-04 04:19:48 +03:00
}
2020-12-15 17:37:31 +03:00
$aOutlineResult [ 'aBoundingBox' ] = array (
( string ) $aPointPolygon [ 'minlat' ],
( string ) $aPointPolygon [ 'maxlat' ],
( string ) $aPointPolygon [ 'minlon' ],
( string ) $aPointPolygon [ 'maxlon' ]
);
2016-09-14 04:16:46 +03:00
}
2016-09-04 04:19:48 +03:00
// as a fallback we generate a bounding box without knowing the size of the geometry
2016-09-08 04:16:22 +03:00
if (( ! isset ( $aOutlineResult [ 'aBoundingBox' ])) && isset ( $fLon )) {
2019-03-05 20:14:25 +03:00
$aBounds = array (
'minlat' => $fLat - $fRadius ,
'maxlat' => $fLat + $fRadius ,
'minlon' => $fLon - $fRadius ,
'maxlon' => $fLon + $fRadius
);
2016-09-04 04:19:48 +03:00
$aOutlineResult [ 'aBoundingBox' ] = array (
2016-09-10 22:10:52 +03:00
( string ) $aBounds [ 'minlat' ],
( string ) $aBounds [ 'maxlat' ],
( string ) $aBounds [ 'minlon' ],
( string ) $aBounds [ 'maxlon' ]
);
2016-09-04 04:19:48 +03:00
}
return $aOutlineResult ;
}
}