From 979aebbfcd0bbdac322dfe7fadeadd88f06fb58a Mon Sep 17 00:00:00 2001 From: Sarah Hoffmann Date: Sun, 15 Sep 2024 11:22:13 +0200 Subject: [PATCH] remove PHP frontend --- CMakeLists.txt | 49 +- cmake/paths-py.tmpl | 15 - lib-php/AddressDetails.php | 191 ----- lib-php/ClassTypes.php | 576 ------------- lib-php/DB.php | 362 --------- lib-php/DatabaseError.php | 42 - lib-php/DebugHtml.php | 189 ----- lib-php/DebugNone.php | 19 - lib-php/Geocode.php | 938 --------------------- lib-php/ParameterParser.php | 157 ---- lib-php/Phrase.php | 89 -- lib-php/PlaceLookup.php | 615 -------------- lib-php/Result.php | 129 --- lib-php/ReverseGeocode.php | 401 --------- lib-php/SearchContext.php | 319 -------- lib-php/SearchDescription.php | 985 ----------------------- lib-php/SearchPosition.php | 95 --- lib-php/Shell.php | 92 --- lib-php/SimpleWordList.php | 144 ---- lib-php/SpecialSearchOperator.php | 52 -- lib-php/Status.php | 59 -- lib-php/TokenCountry.php | 82 -- lib-php/TokenHousenumber.php | 116 --- lib-php/TokenList.php | 134 --- lib-php/TokenPartial.php | 127 --- lib-php/TokenPostcode.php | 111 --- lib-php/TokenSpecialTerm.php | 125 --- lib-php/TokenWord.php | 110 --- lib-php/cmd.php | 199 ----- lib-php/dotenv_loader.php | 21 - lib-php/init-cmd.php | 13 - lib-php/init-website.php | 98 --- lib-php/init.php | 12 - lib-php/lib.php | 246 ------ lib-php/log.php | 104 --- lib-php/output.php | 38 - lib-php/setup_functions.php | 27 - lib-php/template/address-geocodejson.php | 83 -- lib-php/template/address-geojson.php | 85 -- lib-php/template/address-json.php | 82 -- lib-php/template/address-xml.php | 110 --- lib-php/template/details-json.php | 120 --- lib-php/template/error-json.php | 19 - lib-php/template/error-xml.php | 7 - lib-php/template/search-batch-json.php | 83 -- lib-php/template/search-geocodejson.php | 72 -- lib-php/template/search-geojson.php | 83 -- lib-php/template/search-json.php | 81 -- lib-php/template/search-xml.php | 138 ---- lib-php/tokenizer/icu_tokenizer.php | 235 ------ lib-php/tokenizer/legacy_tokenizer.php | 265 ------ lib-php/website/deletable.php | 36 - lib-php/website/details.php | 263 ------ lib-php/website/lookup.php | 101 --- lib-php/website/polygons.php | 63 -- lib-php/website/reverse-only-search.php | 20 - lib-php/website/reverse.php | 95 --- lib-php/website/search.php | 93 --- lib-php/website/status.php | 56 -- 59 files changed, 1 insertion(+), 9270 deletions(-) delete mode 100644 cmake/paths-py.tmpl delete mode 100644 lib-php/AddressDetails.php delete mode 100644 lib-php/ClassTypes.php delete mode 100644 lib-php/DB.php delete mode 100644 lib-php/DatabaseError.php delete mode 100644 lib-php/DebugHtml.php delete mode 100644 lib-php/DebugNone.php delete mode 100644 lib-php/Geocode.php delete mode 100644 lib-php/ParameterParser.php delete mode 100644 lib-php/Phrase.php delete mode 100644 lib-php/PlaceLookup.php delete mode 100644 lib-php/Result.php delete mode 100644 lib-php/ReverseGeocode.php delete mode 100644 lib-php/SearchContext.php delete mode 100644 lib-php/SearchDescription.php delete mode 100644 lib-php/SearchPosition.php delete mode 100644 lib-php/Shell.php delete mode 100644 lib-php/SimpleWordList.php delete mode 100644 lib-php/SpecialSearchOperator.php delete mode 100644 lib-php/Status.php delete mode 100644 lib-php/TokenCountry.php delete mode 100644 lib-php/TokenHousenumber.php delete mode 100644 lib-php/TokenList.php delete mode 100644 lib-php/TokenPartial.php delete mode 100644 lib-php/TokenPostcode.php delete mode 100644 lib-php/TokenSpecialTerm.php delete mode 100644 lib-php/TokenWord.php delete mode 100644 lib-php/cmd.php delete mode 100644 lib-php/dotenv_loader.php delete mode 100644 lib-php/init-cmd.php delete mode 100644 lib-php/init-website.php delete mode 100644 lib-php/init.php delete mode 100644 lib-php/lib.php delete mode 100644 lib-php/log.php delete mode 100644 lib-php/output.php delete mode 100755 lib-php/setup_functions.php delete mode 100644 lib-php/template/address-geocodejson.php delete mode 100644 lib-php/template/address-geojson.php delete mode 100644 lib-php/template/address-json.php delete mode 100644 lib-php/template/address-xml.php delete mode 100644 lib-php/template/details-json.php delete mode 100644 lib-php/template/error-json.php delete mode 100644 lib-php/template/error-xml.php delete mode 100644 lib-php/template/search-batch-json.php delete mode 100644 lib-php/template/search-geocodejson.php delete mode 100644 lib-php/template/search-geojson.php delete mode 100644 lib-php/template/search-json.php delete mode 100644 lib-php/template/search-xml.php delete mode 100644 lib-php/tokenizer/icu_tokenizer.php delete mode 100644 lib-php/tokenizer/legacy_tokenizer.php delete mode 100644 lib-php/website/deletable.php delete mode 100644 lib-php/website/details.php delete mode 100644 lib-php/website/lookup.php delete mode 100644 lib-php/website/polygons.php delete mode 100644 lib-php/website/reverse-only-search.php delete mode 100644 lib-php/website/reverse.php delete mode 100644 lib-php/website/search.php delete mode 100644 lib-php/website/status.php diff --git a/CMakeLists.txt b/CMakeLists.txt index 19bf6655..11f7f929 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,25 +74,6 @@ if (BUILD_IMPORTER OR BUILD_API) find_package(PythonInterp 3.7 REQUIRED) endif() -#----------------------------------------------------------------------------- -# PHP -#----------------------------------------------------------------------------- - -# Setting PHP binary variable as to command line (prevailing) or auto detect - -if (BUILD_API) - if (NOT PHP_BIN) - find_program (PHP_BIN php) - endif() - # sanity check if PHP binary exists - if (NOT EXISTS ${PHP_BIN}) - message(WARNING "PHP binary not found. Only Python frontend can be used.") - set(PHP_BIN "") - else() - message (STATUS "Using PHP binary " ${PHP_BIN}) - endif() -endif() - #----------------------------------------------------------------------------- # import scripts and utilities (importer only) #----------------------------------------------------------------------------- @@ -125,8 +106,6 @@ if (BUILD_TESTS) find_program(PYTHON_BEHAVE behave) find_program(PYLINT NAMES pylint3 pylint) find_program(PYTEST NAMES pytest py.test-3 py.test) - find_program(PHPCS phpcs) - find_program(PHPUNIT phpunit) if (PYTHON_BEHAVE) message(STATUS "Using Python behave binary ${PYTHON_BEHAVE}") @@ -141,24 +120,6 @@ if (BUILD_TESTS) message(WARNING "behave not found. BDD tests disabled." ) endif() - if (PHPUNIT) - message(STATUS "Using phpunit binary ${PHPUNIT}") - add_test(NAME php - COMMAND ${PHPUNIT} ./ - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/php) - else() - message(WARNING "phpunit not found. PHP unit tests disabled." ) - endif() - - if (PHPCS) - message(STATUS "Using phpcs binary ${PHPCS}") - add_test(NAME phpcs - COMMAND ${PHPCS} --report-width=120 --colors lib-php - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) - else() - message(WARNING "phpcs not found. PHP linting tests disabled." ) - endif() - if (PYLINT) message(STATUS "Using pylint binary ${PYLINT}") add_test(NAME pylint @@ -203,11 +164,7 @@ if (BUILD_IMPORTER) DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME nominatim) - if (EXISTS ${PHP_BIN}) - configure_file(${PROJECT_SOURCE_DIR}/cmake/paths-py.tmpl paths-py.installed) - else() - configure_file(${PROJECT_SOURCE_DIR}/cmake/paths-py-no-php.tmpl paths-py.installed) - endif() + configure_file(${PROJECT_SOURCE_DIR}/cmake/paths-py-no-php.tmpl paths-py.installed) foreach (submodule nominatim_db nominatim_api) install(DIRECTORY src/${submodule} @@ -243,10 +200,6 @@ if (BUILD_MODULE) DESTINATION ${NOMINATIM_LIBDIR}/module) endif() -if (BUILD_API AND EXISTS ${PHP_BIN}) - install(DIRECTORY lib-php DESTINATION ${NOMINATIM_LIBDIR}) -endif() - install(FILES settings/env.defaults settings/address-levels.json settings/phrase-settings.json diff --git a/cmake/paths-py.tmpl b/cmake/paths-py.tmpl deleted file mode 100644 index 372a4546..00000000 --- a/cmake/paths-py.tmpl +++ /dev/null @@ -1,15 +0,0 @@ -# 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. -""" -Path settings for extra data used by Nominatim (installed version). -""" -from pathlib import Path - -PHPLIB_DIR = (Path('@NOMINATIM_LIBDIR@') / 'lib-php').resolve() -SQLLIB_DIR = (Path('@NOMINATIM_LIBDIR@') / 'lib-sql').resolve() -DATA_DIR = Path('@NOMINATIM_DATADIR@').resolve() -CONFIG_DIR = Path('@NOMINATIM_CONFIGDIR@').resolve() diff --git a/lib-php/AddressDetails.php b/lib-php/AddressDetails.php deleted file mode 100644 index cfdd0416..00000000 --- a/lib-php/AddressDetails.php +++ /dev/null @@ -1,191 +0,0 @@ -iPlaceID = $iPlaceID; - - if (is_array($mLangPref)) { - $mLangPref = $oDB->getArraySQL($oDB->getDBQuotedList($mLangPref)); - } - - if (!isset($sHousenumber)) { - $sHousenumber = -1; - } - - $sSQL = 'SELECT *,'; - $sSQL .= ' get_name_by_language(name,'.$mLangPref.') as localname'; - $sSQL .= ' FROM get_addressdata('.$iPlaceID.','.$sHousenumber.')'; - $sSQL .= ' ORDER BY rank_address DESC, isaddress DESC'; - - $this->aAddressLines = $oDB->getAll($sSQL); - } - - private static function isAddress($aLine) - { - return $aLine['isaddress'] || $aLine['type'] == 'country_code'; - } - - public function getAddressDetails($bAll = false) - { - if ($bAll) { - return $this->aAddressLines; - } - - return array_filter($this->aAddressLines, array(__CLASS__, 'isAddress')); - } - - public function getLocaleAddress() - { - $aParts = array(); - $sPrevResult = ''; - - foreach ($this->aAddressLines as $aLine) { - if ($aLine['isaddress'] && $sPrevResult != $aLine['localname']) { - $sPrevResult = $aLine['localname']; - $aParts[] = $sPrevResult; - } - } - - return join(', ', $aParts); - } - - public function getAddressNames() - { - $aAddress = array(); - - foreach ($this->aAddressLines as $aLine) { - if (!self::isAddress($aLine)) { - continue; - } - - $sTypeLabel = ClassTypes\getLabelTag($aLine); - - $sName = null; - if (isset($aLine['localname']) && $aLine['localname']!=='') { - $sName = $aLine['localname']; - } elseif (isset($aLine['housenumber']) && $aLine['housenumber']!=='') { - $sName = $aLine['housenumber']; - } - - if (isset($sName) - && (!isset($aAddress[$sTypeLabel]) - || $aLine['class'] == 'place') - ) { - $aAddress[$sTypeLabel] = $sName; - - if (!empty($aLine['name'])) { - $this->addSubdivisionCode($aAddress, $aLine['admin_level'], $aLine['name']); - } - } - } - - return $aAddress; - } - - /** - * Annotates the given json with geocodejson address information fields. - * - * @param array $aJson Json hash to add the fields to. - * - * Geocodejson has the following fields: - * street, locality, postcode, city, district, - * county, state, country - * - * Postcode and housenumber are added by type, district is not used. - * All other fields are set according to address rank. - */ - public function addGeocodeJsonAddressParts(&$aJson) - { - foreach (array_reverse($this->aAddressLines) as $aLine) { - if (!$aLine['isaddress']) { - continue; - } - - if (!isset($aLine['localname']) || $aLine['localname'] == '') { - continue; - } - - if ($aLine['type'] == 'postcode' || $aLine['type'] == 'postal_code') { - $aJson['postcode'] = $aLine['localname']; - continue; - } - - if ($aLine['type'] == 'house_number') { - $aJson['housenumber'] = $aLine['localname']; - continue; - } - - if ($this->iPlaceID == $aLine['place_id']) { - continue; - } - - $iRank = (int)$aLine['rank_address']; - - if ($iRank > 25 && $iRank < 28) { - $aJson['street'] = $aLine['localname']; - } elseif ($iRank >= 22 && $iRank <= 25) { - $aJson['locality'] = $aLine['localname']; - } elseif ($iRank >= 17 && $iRank <= 21) { - $aJson['district'] = $aLine['localname']; - } elseif ($iRank >= 13 && $iRank <= 16) { - $aJson['city'] = $aLine['localname']; - } elseif ($iRank >= 10 && $iRank <= 12) { - $aJson['county'] = $aLine['localname']; - } elseif ($iRank >= 5 && $iRank <= 9) { - $aJson['state'] = $aLine['localname']; - } elseif ($iRank == 4) { - $aJson['country'] = $aLine['localname']; - } - } - } - - public function getAdminLevels() - { - $aAddress = array(); - foreach (array_reverse($this->aAddressLines) as $aLine) { - if (self::isAddress($aLine) - && isset($aLine['admin_level']) - && $aLine['admin_level'] < 15 - && !isset($aAddress['level'.$aLine['admin_level']]) - ) { - $aAddress['level'.$aLine['admin_level']] = $aLine['localname']; - } - } - return $aAddress; - } - - public function debugInfo() - { - return $this->aAddressLines; - } - - private function addSubdivisionCode(&$aAddress, $iAdminLevel, $nameDetails) - { - if (is_string($nameDetails)) { - $nameDetails = json_decode('{' . str_replace('"=>"', '":"', $nameDetails) . '}', true); - } - if (!empty($nameDetails['ISO3166-2'])) { - $aAddress["ISO3166-2-lvl$iAdminLevel"] = $nameDetails['ISO3166-2']; - } - } -} diff --git a/lib-php/ClassTypes.php b/lib-php/ClassTypes.php deleted file mode 100644 index 0561f482..00000000 --- a/lib-php/ClassTypes.php +++ /dev/null @@ -1,576 +0,0 @@ - array ( - 1 => 'Continent', - 2 => 'Country', - 3 => 'Region', - 4 => 'State', - 5 => 'State District', - 6 => 'County', - 7 => 'Municipality', - 8 => 'City', - 9 => 'City District', - 10 => 'Suburb', - 11 => 'Neighbourhood', - 12 => 'City Block' - ), - 'no' => array ( - 3 => 'State', - 4 => 'County' - ), - 'se' => array ( - 3 => 'State', - 4 => 'County' - ) - ); - - if (isset($aBoundaryList[$sCountry]) - && isset($aBoundaryList[$sCountry][$iAdminLevel]) - ) { - return $aBoundaryList[$sCountry][$iAdminLevel]; - } - - return $aBoundaryList['default'][$iAdminLevel] ?? $sFallback; -} - -/** - * Return an estimated radius of how far the object node extends. - * - * @param array[] $aPlace Information about the place. This must be a node - * feature. - * - * @return float The radius around the feature in degrees. - */ -function getDefRadius($aPlace) -{ - $aSpecialRadius = array( - 'place:continent' => 25, - 'place:country' => 7, - 'place:state' => 2.6, - 'place:province' => 2.6, - 'place:region' => 1.0, - 'place:county' => 0.7, - 'place:city' => 0.16, - 'place:municipality' => 0.16, - 'place:island' => 0.32, - 'place:postcode' => 0.16, - 'place:town' => 0.04, - 'place:village' => 0.02, - 'place:hamlet' => 0.02, - 'place:district' => 0.02, - 'place:borough' => 0.02, - 'place:suburb' => 0.02, - 'place:locality' => 0.01, - 'place:neighbourhood'=> 0.01, - 'place:quarter' => 0.01, - 'place:city_block' => 0.01, - 'landuse:farm' => 0.01, - 'place:farm' => 0.01, - 'place:airport' => 0.015, - 'aeroway:aerodrome' => 0.015, - 'railway:station' => 0.005 - ); - - $sClassPlace = $aPlace['class'].':'.$aPlace['type']; - - return $aSpecialRadius[$sClassPlace] ?? 0.00005; -} - -/** - * Get the icon to use with the given object. - */ -function getIcon($aPlace) -{ - $aIcons = array( - 'boundary:administrative' => 'poi_boundary_administrative', - 'place:city' => 'poi_place_city', - 'place:town' => 'poi_place_town', - 'place:village' => 'poi_place_village', - 'place:hamlet' => 'poi_place_village', - 'place:suburb' => 'poi_place_village', - 'place:locality' => 'poi_place_village', - 'place:airport' => 'transport_airport2', - 'aeroway:aerodrome' => 'transport_airport2', - 'railway:station' => 'transport_train_station2', - 'amenity:place_of_worship' => 'place_of_worship_unknown3', - 'amenity:pub' => 'food_pub', - 'amenity:bar' => 'food_bar', - 'amenity:university' => 'education_university', - 'tourism:museum' => 'tourist_museum', - 'amenity:arts_centre' => 'tourist_art_gallery2', - 'tourism:zoo' => 'tourist_zoo', - 'tourism:theme_park' => 'poi_point_of_interest', - 'tourism:attraction' => 'poi_point_of_interest', - 'leisure:golf_course' => 'sport_golf', - 'historic:castle' => 'tourist_castle', - 'amenity:hospital' => 'health_hospital', - 'amenity:school' => 'education_school', - 'amenity:theatre' => 'tourist_theatre', - 'amenity:library' => 'amenity_library', - 'amenity:fire_station' => 'amenity_firestation3', - 'amenity:police' => 'amenity_police2', - 'amenity:bank' => 'money_bank2', - 'amenity:post_office' => 'amenity_post_office', - 'tourism:hotel' => 'accommodation_hotel2', - 'amenity:cinema' => 'tourist_cinema', - 'tourism:artwork' => 'tourist_art_gallery2', - 'historic:archaeological_site' => 'tourist_archaeological2', - 'amenity:doctors' => 'health_doctors', - 'leisure:sports_centre' => 'sport_leisure_centre', - 'leisure:swimming_pool' => 'sport_swimming_outdoor', - 'shop:supermarket' => 'shopping_supermarket', - 'shop:convenience' => 'shopping_convenience', - 'amenity:restaurant' => 'food_restaurant', - 'amenity:fast_food' => 'food_fastfood', - 'amenity:cafe' => 'food_cafe', - 'tourism:guest_house' => 'accommodation_bed_and_breakfast', - 'amenity:pharmacy' => 'health_pharmacy_dispensing', - 'amenity:fuel' => 'transport_fuel', - 'natural:peak' => 'poi_peak', - 'natural:wood' => 'landuse_coniferous_and_deciduous', - 'shop:bicycle' => 'shopping_bicycle', - 'shop:clothes' => 'shopping_clothes', - 'shop:hairdresser' => 'shopping_hairdresser', - 'shop:doityourself' => 'shopping_diy', - 'shop:estate_agent' => 'shopping_estateagent2', - 'shop:car' => 'shopping_car', - 'shop:garden_centre' => 'shopping_garden_centre', - 'shop:car_repair' => 'shopping_car_repair', - 'shop:bakery' => 'shopping_bakery', - 'shop:butcher' => 'shopping_butcher', - 'shop:apparel' => 'shopping_clothes', - 'shop:laundry' => 'shopping_laundrette', - 'shop:beverages' => 'shopping_alcohol', - 'shop:alcohol' => 'shopping_alcohol', - 'shop:optician' => 'health_opticians', - 'shop:chemist' => 'health_pharmacy', - 'shop:gallery' => 'tourist_art_gallery2', - 'shop:jewelry' => 'shopping_jewelry', - 'tourism:information' => 'amenity_information', - 'historic:ruins' => 'tourist_ruin', - 'amenity:college' => 'education_school', - 'historic:monument' => 'tourist_monument', - 'historic:memorial' => 'tourist_monument', - 'historic:mine' => 'poi_mine', - 'tourism:caravan_site' => 'accommodation_caravan_park', - 'amenity:bus_station' => 'transport_bus_station', - 'amenity:atm' => 'money_atm2', - 'tourism:viewpoint' => 'tourist_view_point', - 'tourism:guesthouse' => 'accommodation_bed_and_breakfast', - 'railway:tram' => 'transport_tram_stop', - 'amenity:courthouse' => 'amenity_court', - 'amenity:recycling' => 'amenity_recycling', - 'amenity:dentist' => 'health_dentist', - 'natural:beach' => 'tourist_beach', - 'railway:tram_stop' => 'transport_tram_stop', - 'amenity:prison' => 'amenity_prison', - 'highway:bus_stop' => 'transport_bus_stop2' - ); - - $sClassPlace = $aPlace['class'].':'.$aPlace['type']; - - return $aIcons[$sClassPlace] ?? null; -} - -/** - * Get an icon for the given object with its full URL. - */ -function getIconFile($aPlace) -{ - if (CONST_MapIcon_URL === false) { - return null; - } - - $sIcon = getIcon($aPlace); - - if (!isset($sIcon)) { - return null; - } - - return CONST_MapIcon_URL.'/'.$sIcon.'.p.20.png'; -} - -/** - * Return a class importance value for the given place. - * - * @param array[] $aPlace Information about the place. - * - * @return int An importance value. The lower the value, the more - * important the class. - */ -function getImportance($aPlace) -{ - static $aWithImportance = null; - - if ($aWithImportance === null) { - $aWithImportance = array_flip(array( - 'boundary:administrative', - 'place:country', - 'place:state', - 'place:province', - 'place:county', - 'place:city', - 'place:region', - 'place:island', - 'place:town', - 'place:village', - 'place:hamlet', - 'place:suburb', - 'place:locality', - 'landuse:farm', - 'place:farm', - 'highway:motorway_junction', - 'highway:motorway', - 'highway:trunk', - 'highway:primary', - 'highway:secondary', - 'highway:tertiary', - 'highway:residential', - 'highway:unclassified', - 'highway:living_street', - 'highway:service', - 'highway:track', - 'highway:road', - 'highway:byway', - 'highway:bridleway', - 'highway:cycleway', - 'highway:pedestrian', - 'highway:footway', - 'highway:steps', - 'highway:motorway_link', - 'highway:trunk_link', - 'highway:primary_link', - 'landuse:industrial', - 'landuse:residential', - 'landuse:retail', - 'landuse:commercial', - 'place:airport', - 'aeroway:aerodrome', - 'railway:station', - 'amenity:place_of_worship', - 'amenity:pub', - 'amenity:bar', - 'amenity:university', - 'tourism:museum', - 'amenity:arts_centre', - 'tourism:zoo', - 'tourism:theme_park', - 'tourism:attraction', - 'leisure:golf_course', - 'historic:castle', - 'amenity:hospital', - 'amenity:school', - 'amenity:theatre', - 'amenity:public_building', - 'amenity:library', - 'amenity:townhall', - 'amenity:community_centre', - 'amenity:fire_station', - 'amenity:police', - 'amenity:bank', - 'amenity:post_office', - 'leisure:park', - 'amenity:park', - 'landuse:park', - 'landuse:recreation_ground', - 'tourism:hotel', - 'tourism:motel', - 'amenity:cinema', - 'tourism:artwork', - 'historic:archaeological_site', - 'amenity:doctors', - 'leisure:sports_centre', - 'leisure:swimming_pool', - 'shop:supermarket', - 'shop:convenience', - 'amenity:restaurant', - 'amenity:fast_food', - 'amenity:cafe', - 'tourism:guest_house', - 'amenity:pharmacy', - 'amenity:fuel', - 'natural:peak', - 'waterway:waterfall', - 'natural:wood', - 'natural:water', - 'landuse:forest', - 'landuse:cemetery', - 'landuse:allotments', - 'landuse:farmyard', - 'railway:rail', - 'waterway:canal', - 'waterway:river', - 'waterway:stream', - 'shop:bicycle', - 'shop:clothes', - 'shop:hairdresser', - 'shop:doityourself', - 'shop:estate_agent', - 'shop:car', - 'shop:garden_centre', - 'shop:car_repair', - 'shop:newsagent', - 'shop:bakery', - 'shop:furniture', - 'shop:butcher', - 'shop:apparel', - 'shop:electronics', - 'shop:department_store', - 'shop:books', - 'shop:yes', - 'shop:outdoor', - 'shop:mall', - 'shop:florist', - 'shop:charity', - 'shop:hardware', - 'shop:laundry', - 'shop:shoes', - 'shop:beverages', - 'shop:dry_cleaning', - 'shop:carpet', - 'shop:computer', - 'shop:alcohol', - 'shop:optician', - 'shop:chemist', - 'shop:gallery', - 'shop:mobile_phone', - 'shop:sports', - 'shop:jewelry', - 'shop:pet', - 'shop:beauty', - 'shop:stationery', - 'shop:shopping_centre', - 'shop:general', - 'shop:electrical', - 'shop:toys', - 'shop:jeweller', - 'shop:betting', - 'shop:household', - 'shop:travel_agency', - 'shop:hifi', - 'amenity:shop', - 'tourism:information', - 'place:house', - 'place:house_name', - 'place:house_number', - 'place:country_code', - 'leisure:pitch', - 'highway:unsurfaced', - 'historic:ruins', - 'amenity:college', - 'historic:monument', - 'railway:subway', - 'historic:memorial', - 'leisure:nature_reserve', - 'leisure:common', - 'waterway:lock_gate', - 'natural:fell', - 'amenity:nightclub', - 'highway:path', - 'leisure:garden', - 'landuse:reservoir', - 'leisure:playground', - 'leisure:stadium', - 'historic:mine', - 'natural:cliff', - 'tourism:caravan_site', - 'amenity:bus_station', - 'amenity:kindergarten', - 'highway:construction', - 'amenity:atm', - 'amenity:emergency_phone', - 'waterway:lock', - 'waterway:riverbank', - 'natural:coastline', - 'tourism:viewpoint', - 'tourism:hostel', - 'tourism:bed_and_breakfast', - 'railway:halt', - 'railway:platform', - 'railway:tram', - 'amenity:courthouse', - 'amenity:recycling', - 'amenity:dentist', - 'natural:beach', - 'place:moor', - 'amenity:grave_yard', - 'waterway:drain', - 'landuse:grass', - 'landuse:village_green', - 'natural:bay', - 'railway:tram_stop', - 'leisure:marina', - 'highway:stile', - 'natural:moor', - 'railway:light_rail', - 'railway:narrow_gauge', - 'natural:land', - 'amenity:village_hall', - 'waterway:dock', - 'amenity:veterinary', - 'landuse:brownfield', - 'leisure:track', - 'railway:historic_station', - 'landuse:construction', - 'amenity:prison', - 'landuse:quarry', - 'amenity:telephone', - 'highway:traffic_signals', - 'natural:heath', - 'historic:house', - 'amenity:social_club', - 'landuse:military', - 'amenity:health_centre', - 'historic:building', - 'amenity:clinic', - 'highway:services', - 'amenity:ferry_terminal', - 'natural:marsh', - 'natural:hill', - 'highway:raceway', - 'amenity:taxi', - 'amenity:take_away', - 'amenity:car_rental', - 'place:islet', - 'amenity:nursery', - 'amenity:nursing_home', - 'amenity:toilets', - 'amenity:hall', - 'waterway:boatyard', - 'highway:mini_roundabout', - 'historic:manor', - 'tourism:chalet', - 'amenity:bicycle_parking', - 'amenity:hotel', - 'waterway:weir', - 'natural:wetland', - 'natural:cave_entrance', - 'amenity:crematorium', - 'tourism:picnic_site', - 'landuse:wood', - 'landuse:basin', - 'natural:tree', - 'leisure:slipway', - 'landuse:meadow', - 'landuse:piste', - 'amenity:care_home', - 'amenity:club', - 'amenity:medical_centre', - 'historic:roman_road', - 'historic:fort', - 'railway:subway_entrance', - 'historic:yes', - 'highway:gate', - 'leisure:fishing', - 'historic:museum', - 'amenity:car_wash', - 'railway:level_crossing', - 'leisure:bird_hide', - 'natural:headland', - 'tourism:apartments', - 'amenity:shopping', - 'natural:scrub', - 'natural:fen', - 'building:yes', - 'mountain_pass:yes', - 'amenity:parking', - 'highway:bus_stop', - 'place:postcode', - 'amenity:post_box', - 'place:houses', - 'railway:preserved', - 'waterway:derelict_canal', - 'amenity:dead_pub', - 'railway:disused_station', - 'railway:abandoned', - 'railway:disused' - )); - } - - $sClassPlace = $aPlace['class'].':'.$aPlace['type']; - - return $aWithImportance[$sClassPlace] ?? null; -} diff --git a/lib-php/DB.php b/lib-php/DB.php deleted file mode 100644 index 553d9452..00000000 --- a/lib-php/DB.php +++ /dev/null @@ -1,362 +0,0 @@ -sDSN = $sDSN ?? getSetting('DATABASE_DSN'); - } - - public function connect($bNew = false, $bPersistent = true) - { - if (isset($this->connection) && !$bNew) { - return true; - } - $aConnOptions = array( - \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, - \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, - \PDO::ATTR_PERSISTENT => $bPersistent - ); - - // https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php - try { - $this->connection = new \PDO($this->sDSN, null, null, $aConnOptions); - } catch (\PDOException $e) { - $sMsg = 'Failed to establish database connection:' . $e->getMessage(); - throw new \Nominatim\DatabaseError($sMsg, 500, null, $e->getMessage()); - } - - $this->connection->exec("SET DateStyle TO 'sql,european'"); - $this->connection->exec("SET client_encoding TO 'utf-8'"); - // Disable JIT and parallel workers. They interfere badly with search SQL. - $this->connection->exec('SET max_parallel_workers_per_gather TO 0'); - if ($this->getPostgresVersion() >= 11) { - $this->connection->exec('SET jit_above_cost TO -1'); - } - - $iMaxExecution = ini_get('max_execution_time'); - if ($iMaxExecution > 0) { - $this->connection->setAttribute(\PDO::ATTR_TIMEOUT, $iMaxExecution); // seconds - } - - return true; - } - - // returns the number of rows that were modified or deleted by the SQL - // statement. If no rows were affected returns 0. - public function exec($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed') - { - $val = null; - try { - if (isset($aInputVars)) { - $stmt = $this->connection->prepare($sSQL); - $stmt->execute($aInputVars); - } else { - $val = $this->connection->exec($sSQL); - } - } catch (\PDOException $e) { - throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL); - } - return $val; - } - - /** - * Executes query. Returns first row as array. - * Returns false if no result found. - * - * @param string $sSQL - * - * @return array[] - */ - public function getRow($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed') - { - try { - $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage); - $row = $stmt->fetch(); - } catch (\PDOException $e) { - throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL); - } - return $row; - } - - /** - * Executes query. Returns first value of first result. - * Returns false if no results found. - * - * @param string $sSQL - * - * @return array[] - */ - public function getOne($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed') - { - try { - $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage); - $row = $stmt->fetch(\PDO::FETCH_NUM); - if ($row === false) { - return false; - } - } catch (\PDOException $e) { - throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL); - } - return $row[0]; - } - - /** - * Executes query. Returns array of results (arrays). - * Returns empty array if no results found. - * - * @param string $sSQL - * - * @return array[] - */ - public function getAll($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed') - { - try { - $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage); - $rows = $stmt->fetchAll(); - } catch (\PDOException $e) { - throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL); - } - return $rows; - } - - /** - * Executes query. Returns array of the first value of each result. - * Returns empty array if no results found. - * - * @param string $sSQL - * - * @return array[] - */ - public function getCol($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed') - { - $aVals = array(); - try { - $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage); - - while (($val = $stmt->fetchColumn(0)) !== false) { // returns first column or false - $aVals[] = $val; - } - } catch (\PDOException $e) { - throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL); - } - return $aVals; - } - - /** - * Executes query. Returns associate array mapping first value to second value of each result. - * Returns empty array if no results found. - * - * @param string $sSQL - * - * @return array[] - */ - public function getAssoc($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed') - { - try { - $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage); - - $aList = array(); - while ($aRow = $stmt->fetch(\PDO::FETCH_NUM)) { - $aList[$aRow[0]] = $aRow[1]; - } - } catch (\PDOException $e) { - throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL); - } - return $aList; - } - - /** - * Executes query. Returns a PDO statement to iterate over. - * - * @param string $sSQL - * - * @return PDOStatement - */ - public function getQueryStatement($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed') - { - try { - if (isset($aInputVars)) { - $stmt = $this->connection->prepare($sSQL); - $stmt->execute($aInputVars); - } else { - $stmt = $this->connection->query($sSQL); - } - } catch (\PDOException $e) { - throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL); - } - return $stmt; - } - - /** - * St. John's Way => 'St. John\'s Way' - * - * @param string $sVal Text to be quoted. - * - * @return string - */ - public function getDBQuoted($sVal) - { - return $this->connection->quote($sVal); - } - - /** - * Like getDBQuoted, but takes an array. - * - * @param array $aVals List of text to be quoted. - * - * @return array[] - */ - public function getDBQuotedList($aVals) - { - return array_map(function ($sVal) { - return $this->getDBQuoted($sVal); - }, $aVals); - } - - /** - * [1,2,'b'] => 'ARRAY[1,2,'b']'' - * - * @param array $aVals List of text to be quoted. - * - * @return string - */ - public function getArraySQL($a) - { - return 'ARRAY['.join(',', $a).']'; - } - - /** - * Check if a table exists in the database. Returns true if it does. - * - * @param string $sTableName - * - * @return boolean - */ - public function tableExists($sTableName) - { - $sSQL = 'SELECT count(*) FROM pg_tables WHERE tablename = :tablename'; - return ($this->getOne($sSQL, array(':tablename' => $sTableName)) == 1); - } - - /** - * Deletes a table. Returns true if deleted or didn't exist. - * - * @param string $sTableName - * - * @return boolean - */ - public function deleteTable($sTableName) - { - return $this->exec('DROP TABLE IF EXISTS '.$sTableName.' CASCADE') == 0; - } - - /** - * Tries to connect to the database but on failure doesn't throw an exception. - * - * @return boolean - */ - public function checkConnection() - { - $bExists = true; - try { - $this->connect(true); - } catch (\Nominatim\DatabaseError $e) { - $bExists = false; - } - return $bExists; - } - - /** - * e.g. 9.6, 10, 11.2 - * - * @return float - */ - public function getPostgresVersion() - { - $sVersionString = $this->getOne('SHOW server_version_num'); - preg_match('#([0-9]?[0-9])([0-9][0-9])[0-9][0-9]#', $sVersionString, $aMatches); - return (float) ($aMatches[1].'.'.$aMatches[2]); - } - - /** - * e.g. 2, 2.2 - * - * @return float - */ - public function getPostgisVersion() - { - $sVersionString = $this->getOne('select postgis_lib_version()'); - preg_match('#^([0-9]+)[.]([0-9]+)[.]#', $sVersionString, $aMatches); - return (float) ($aMatches[1].'.'.$aMatches[2]); - } - - /** - * Returns an associate array of postgresql database connection settings. Keys can - * be 'database', 'hostspec', 'port', 'username', 'password'. - * Returns empty array on failure, thus check if at least 'database' is set. - * - * @return array[] - */ - public static function parseDSN($sDSN) - { - // https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php - $aInfo = array(); - if (preg_match('/^pgsql:(.+)$/', $sDSN, $aMatches)) { - foreach (explode(';', $aMatches[1]) as $sKeyVal) { - list($sKey, $sVal) = explode('=', $sKeyVal, 2); - if ($sKey == 'host') { - $sKey = 'hostspec'; - } elseif ($sKey == 'dbname') { - $sKey = 'database'; - } elseif ($sKey == 'user') { - $sKey = 'username'; - } - $aInfo[$sKey] = $sVal; - } - } - return $aInfo; - } - - /** - * Takes an array of settings and return the DNS string. Key names can be - * 'database', 'hostspec', 'port', 'username', 'password' but aliases - * 'dbname', 'host' and 'user' are also supported. - * - * @return string - * - */ - public static function generateDSN($aInfo) - { - $sDSN = sprintf( - 'pgsql:host=%s;port=%s;dbname=%s;user=%s;password=%s;', - $aInfo['host'] ?? $aInfo['hostspec'] ?? '', - $aInfo['port'] ?? '', - $aInfo['dbname'] ?? $aInfo['database'] ?? '', - $aInfo['user'] ?? '', - $aInfo['password'] ?? '' - ); - $sDSN = preg_replace('/\b\w+=;/', '', $sDSN); - $sDSN = preg_replace('/;\Z/', '', $sDSN); - - return $sDSN; - } -} diff --git a/lib-php/DatabaseError.php b/lib-php/DatabaseError.php deleted file mode 100644 index 68f1efe6..00000000 --- a/lib-php/DatabaseError.php +++ /dev/null @@ -1,42 +0,0 @@ -oPDOErr = $oPDOErr; - $this->sSql = $sSql; - } - - public function __toString() - { - return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; - } - - public function getSqlError() - { - return $this->oPDOErr->getMessage(); - } - - public function getSqlDebugDump() - { - if (CONST_Debug) { - return var_export($this->oPDOErr, true); - } else { - return $this->sSql; - } - } -} diff --git a/lib-php/DebugHtml.php b/lib-php/DebugHtml.php deleted file mode 100644 index 7b0cba2d..00000000 --- a/lib-php/DebugHtml.php +++ /dev/null @@ -1,189 +0,0 @@ -

Debug output for $sHeading

\n"; - } - - public static function newSection($sHeading) - { - echo "

$sHeading

\n"; - } - - public static function printVar($sHeading, $mVar) - { - echo '
'.$sHeading. ':  ';
-        Debug::outputVar($mVar, str_repeat(' ', strlen($sHeading) + 3));
-        echo "
\n"; - } - - public static function fmtArrayVals($aArr) - { - return array('__debug_format' => 'array_vals', 'data' => $aArr); - } - - public static function printDebugArray($sHeading, $oVar) - { - - if ($oVar === null) { - Debug::printVar($sHeading, 'null'); - } else { - Debug::printVar($sHeading, $oVar->debugInfo()); - } - } - - public static function printDebugTable($sHeading, $aVar) - { - echo ''.$sHeading.":\n"; - echo "\n"; - if (!empty($aVar)) { - echo " \n"; - $aKeys = array(); - $aInfo = reset($aVar); - if (!is_array($aInfo)) { - $aInfo = $aInfo->debugInfo(); - } - foreach ($aInfo as $sKey => $mVal) { - echo ' '."\n"; - $aKeys[] = $sKey; - } - echo " \n"; - foreach ($aVar as $oRow) { - $aInfo = $oRow; - if (!is_array($oRow)) { - $aInfo = $oRow->debugInfo(); - } - echo " \n"; - foreach ($aKeys as $sKey) { - echo ' '."\n"; - } - echo " \n"; - } - } - echo "
'.$sKey.'
';
-                    if (isset($aInfo[$sKey])) {
-                        Debug::outputVar($aInfo[$sKey], '');
-                    }
-                    echo '
\n"; - } - - public static function printGroupedSearch($aSearches, $aWordsIDs) - { - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - foreach ($aSearches as $aRankedSet) { - foreach ($aRankedSet as $aRow) { - $aRow->dumpAsHtmlTableRow($aWordsIDs); - } - } - echo '
rankName TokensName NotAddress TokensAddress Notcountryoperatorclasstypepostcodehousenumber
'; - } - - public static function printGroupTable($sHeading, $aVar) - { - echo ''.$sHeading.":\n"; - echo "\n"; - if (!empty($aVar)) { - echo " \n"; - echo ' '."\n"; - $aKeys = array(); - $aInfo = reset($aVar)[0]; - if (!is_array($aInfo)) { - $aInfo = $aInfo->debugInfo(); - } - foreach ($aInfo as $sKey => $mVal) { - echo ' '."\n"; - $aKeys[] = $sKey; - } - echo " \n"; - foreach ($aVar as $sGrpKey => $aGroup) { - foreach ($aGroup as $oRow) { - $aInfo = $oRow; - if (!is_array($oRow)) { - $aInfo = $oRow->debugInfo(); - } - echo " \n"; - echo ' '."\n"; - foreach ($aKeys as $sKey) { - echo ' '."\n"; - } - echo " \n"; - } - } - } - echo "
Group'.$sKey.'
'.$sGrpKey.'
';
-                        if (!empty($aInfo[$sKey])) {
-                            Debug::outputVar($aInfo[$sKey], '');
-                        }
-                        echo '
\n"; - } - - public static function printSQL($sSQL) - { - echo '

'.date('c').' '.htmlspecialchars($sSQL, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401).'

'."\n"; - } - - private static function outputVar($mVar, $sPreNL) - { - if (is_array($mVar) && !isset($mVar['__debug_format'])) { - $sPre = ''; - foreach ($mVar as $mKey => $aValue) { - echo $sPre; - $iKeyLen = Debug::outputSimpleVar($mKey); - echo ' => '; - Debug::outputVar( - $aValue, - $sPreNL.str_repeat(' ', $iKeyLen + 4) - ); - $sPre = "\n".$sPreNL; - } - } elseif (is_array($mVar) && isset($mVar['__debug_format'])) { - if (!empty($mVar['data'])) { - $sPre = ''; - foreach ($mVar['data'] as $mValue) { - echo $sPre; - Debug::outputSimpleVar($mValue); - $sPre = ', '; - } - } - } elseif (is_object($mVar) && method_exists($mVar, 'debugInfo')) { - Debug::outputVar($mVar->debugInfo(), $sPreNL); - } elseif (is_a($mVar, 'stdClass')) { - Debug::outputVar(json_decode(json_encode($mVar), true), $sPreNL); - } else { - Debug::outputSimpleVar($mVar); - } - } - - private static function outputSimpleVar($mVar) - { - if (is_bool($mVar)) { - echo ''.($mVar ? 'True' : 'False').''; - return $mVar ? 4 : 5; - } - - if (is_string($mVar)) { - $sOut = "'$mVar'"; - } else { - $sOut = (string)$mVar; - } - - echo htmlspecialchars($sOut, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401); - return strlen($sOut); - } -} diff --git a/lib-php/DebugNone.php b/lib-php/DebugNone.php deleted file mode 100644 index 818cc086..00000000 --- a/lib-php/DebugNone.php +++ /dev/null @@ -1,19 +0,0 @@ -oDB =& $oDB; - $this->oPlaceLookup = new PlaceLookup($this->oDB); - $this->oTokenizer = new \Nominatim\Tokenizer($this->oDB); - } - - public function setLanguagePreference($aLangPref) - { - $this->aLangPrefOrder = $aLangPref; - } - - public function getMoreUrlParams() - { - if ($this->aStructuredQuery) { - $aParams = $this->aStructuredQuery; - } else { - $aParams = array('q' => $this->sQuery); - } - - $aParams = array_merge($aParams, $this->oPlaceLookup->getMoreUrlParams()); - - if ($this->aExcludePlaceIDs) { - $aParams['exclude_place_ids'] = implode(',', $this->aExcludePlaceIDs); - } - - if ($this->bBoundedSearch) { - $aParams['bounded'] = '1'; - } - - if ($this->aCountryCodes) { - $aParams['countrycodes'] = implode(',', $this->aCountryCodes); - } - - if ($this->aViewBox) { - $aParams['viewbox'] = join(',', $this->aViewBox); - } - - return $aParams; - } - - public function setLimit($iLimit = 10) - { - if ($iLimit > 50) { - $iLimit = 50; - } elseif ($iLimit < 1) { - $iLimit = 1; - } - - $this->iFinalLimit = $iLimit; - $this->iLimit = $iLimit + max($iLimit, 10); - } - - public function setFeatureType($sFeatureType) - { - switch ($sFeatureType) { - case 'country': - $this->setRankRange(4, 4); - break; - case 'state': - $this->setRankRange(8, 8); - break; - case 'city': - $this->setRankRange(14, 16); - break; - case 'settlement': - $this->setRankRange(8, 20); - break; - } - } - - public function setRankRange($iMin, $iMax) - { - $this->iMinAddressRank = $iMin; - $this->iMaxAddressRank = $iMax; - } - - public function setViewbox($aViewbox) - { - $aBox = array_map('floatval', $aViewbox); - - $this->aViewBox[0] = max(-180.0, min($aBox[0], $aBox[2])); - $this->aViewBox[1] = max(-90.0, min($aBox[1], $aBox[3])); - $this->aViewBox[2] = min(180.0, max($aBox[0], $aBox[2])); - $this->aViewBox[3] = min(90.0, max($aBox[1], $aBox[3])); - - if ($this->aViewBox[2] - $this->aViewBox[0] < 0.000000001 - || $this->aViewBox[3] - $this->aViewBox[1] < 0.000000001 - ) { - userError("Bad parameter 'viewbox'. Not a box."); - } - } - - private function viewboxImportanceFactor($fX, $fY) - { - if (!$this->aViewBox) { - return 1; - } - - $fWidth = ($this->aViewBox[2] - $this->aViewBox[0])/2; - $fHeight = ($this->aViewBox[3] - $this->aViewBox[1])/2; - - $fXDist = abs($fX - ($this->aViewBox[0] + $this->aViewBox[2])/2); - $fYDist = abs($fY - ($this->aViewBox[1] + $this->aViewBox[3])/2); - - if ($fXDist <= $fWidth && $fYDist <= $fHeight) { - return 1; - } - - if ($fXDist <= $fWidth * 3 && $fYDist <= 3 * $fHeight) { - return 0.5; - } - - return 0.25; - } - - public function setQuery($sQueryString) - { - $this->sQuery = $sQueryString; - $this->aStructuredQuery = false; - } - - public function getQueryString() - { - return $this->sQuery; - } - - - public function loadParamArray($oParams, $sForceGeometryType = null) - { - $this->bBoundedSearch = $oParams->getBool('bounded', $this->bBoundedSearch); - - $this->setLimit($oParams->getInt('limit', $this->iFinalLimit)); - $this->iOffset = $oParams->getInt('offset', $this->iOffset); - - $this->bFallback = $oParams->getBool('fallback', $this->bFallback); - - // List of excluded Place IDs - used for more accurate pageing - $sExcluded = $oParams->getStringList('exclude_place_ids'); - if ($sExcluded) { - foreach ($sExcluded as $iExcludedPlaceID) { - $iExcludedPlaceID = (int)$iExcludedPlaceID; - if ($iExcludedPlaceID) { - $aExcludePlaceIDs[$iExcludedPlaceID] = $iExcludedPlaceID; - } - } - - if (isset($aExcludePlaceIDs)) { - $this->aExcludePlaceIDs = $aExcludePlaceIDs; - } - } - - // Only certain ranks of feature - $sFeatureType = $oParams->getString('featureType'); - if (!$sFeatureType) { - $sFeatureType = $oParams->getString('featuretype'); - } - if ($sFeatureType) { - $this->setFeatureType($sFeatureType); - } - - // Country code list - $sCountries = $oParams->getStringList('countrycodes'); - if ($sCountries) { - foreach ($sCountries as $sCountryCode) { - if (preg_match('/^[a-zA-Z][a-zA-Z]$/', $sCountryCode)) { - $aCountries[] = strtolower($sCountryCode); - } - } - if (isset($aCountries)) { - $this->aCountryCodes = $aCountries; - } - } - - $aViewbox = $oParams->getStringList('viewboxlbrt'); - if ($aViewbox) { - if (count($aViewbox) != 4) { - userError("Bad parameter 'viewboxlbrt'. Expected 4 coordinates."); - } - $this->setViewbox($aViewbox); - } else { - $aViewbox = $oParams->getStringList('viewbox'); - if ($aViewbox) { - if (count($aViewbox) != 4) { - userError("Bad parameter 'viewbox'. Expected 4 coordinates."); - } - $this->setViewBox($aViewbox); - } else { - $aRoute = $oParams->getStringList('route'); - $fRouteWidth = $oParams->getFloat('routewidth'); - if ($aRoute && $fRouteWidth) { - $this->aRoutePoints = $aRoute; - $this->aRouteWidth = $fRouteWidth; - } - } - } - - $this->oPlaceLookup->loadParamArray($oParams, $sForceGeometryType); - $this->oPlaceLookup->setIncludeAddressDetails($oParams->getBool('addressdetails', false)); - } - - public function setQueryFromParams($oParams) - { - // Search query - $sQuery = $oParams->getString('q'); - if (!$sQuery) { - $this->setStructuredQuery( - $oParams->getString('amenity'), - $oParams->getString('street'), - $oParams->getString('city'), - $oParams->getString('county'), - $oParams->getString('state'), - $oParams->getString('country'), - $oParams->getString('postalcode') - ); - } else { - $this->setQuery($sQuery); - } - } - - public function loadStructuredAddressElement($sValue, $sKey, $iNewMinAddressRank, $iNewMaxAddressRank, $aItemListValues) - { - $sValue = trim($sValue); - if (!$sValue) { - return false; - } - $this->aStructuredQuery[$sKey] = $sValue; - if ($this->iMinAddressRank == 0 && $this->iMaxAddressRank == 30) { - $this->iMinAddressRank = $iNewMinAddressRank; - $this->iMaxAddressRank = $iNewMaxAddressRank; - } - if ($aItemListValues) { - $this->aAddressRankList = array_merge($this->aAddressRankList, $aItemListValues); - } - return true; - } - - public function setStructuredQuery($sAmenity = false, $sStreet = false, $sCity = false, $sCounty = false, $sState = false, $sCountry = false, $sPostalCode = false) - { - $this->sQuery = false; - - // Reset - $this->iMinAddressRank = 0; - $this->iMaxAddressRank = 30; - $this->aAddressRankList = array(); - - $this->aStructuredQuery = array(); - $this->sAllowedTypesSQLList = false; - - $this->loadStructuredAddressElement($sAmenity, 'amenity', 26, 30, false); - $this->loadStructuredAddressElement($sStreet, 'street', 26, 30, false); - $this->loadStructuredAddressElement($sCity, 'city', 14, 24, false); - $this->loadStructuredAddressElement($sCounty, 'county', 9, 13, false); - $this->loadStructuredAddressElement($sState, 'state', 8, 8, false); - $this->loadStructuredAddressElement($sPostalCode, 'postalcode', 5, 11, array(5, 11)); - $this->loadStructuredAddressElement($sCountry, 'country', 4, 4, false); - - if (!empty($this->aStructuredQuery)) { - $this->sQuery = join(', ', $this->aStructuredQuery); - if ($this->iMaxAddressRank < 30) { - $this->sAllowedTypesSQLList = '(\'place\',\'boundary\')'; - } - } - } - - public function fallbackStructuredQuery() - { - $aParams = $this->aStructuredQuery; - - if (!$aParams || count($aParams) == 1) { - return false; - } - - $aOrderToFallback = array('postalcode', 'street', 'city', 'county', 'state'); - - foreach ($aOrderToFallback as $sType) { - if (isset($aParams[$sType])) { - unset($aParams[$sType]); - $this->setStructuredQuery(@$aParams['amenity'], @$aParams['street'], @$aParams['city'], @$aParams['county'], @$aParams['state'], @$aParams['country'], @$aParams['postalcode']); - return true; - } - } - - return false; - } - - public function getGroupedSearches($aSearches, $aPhrases, $oValidTokens) - { - /* - Calculate all searches using oValidTokens 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 => $oPhrase) { - $aNewPhraseSearches = array(); - $oPosition = new SearchPosition( - $oPhrase->getPhraseType(), - $iPhrase, - count($aPhrases) - ); - - foreach ($oPhrase->getWordSets() as $aWordset) { - $aWordsetSearches = $aSearches; - - // Add all words from this wordset - foreach ($aWordset as $iToken => $sToken) { - $aNewWordsetSearches = array(); - $oPosition->setTokenPosition($iToken, count($aWordset)); - - foreach ($aWordsetSearches as $oCurrentSearch) { - foreach ($oValidTokens->get($sToken) as $oSearchTerm) { - if ($oSearchTerm->isExtendable($oCurrentSearch, $oPosition)) { - $aNewSearches = $oSearchTerm->extendSearch( - $oCurrentSearch, - $oPosition - ); - - foreach ($aNewSearches as $oSearch) { - if ($oSearch->getRank() < $this->iMaxRank) { - $aNewWordsetSearches[] = $oSearch; - } - } - } - } - } - // Sort and cut - usort($aNewWordsetSearches, array('Nominatim\SearchDescription', 'bySearchRank')); - $aWordsetSearches = array_slice($aNewWordsetSearches, 0, 50); - } - - $aNewPhraseSearches = array_merge($aNewPhraseSearches, $aNewWordsetSearches); - usort($aNewPhraseSearches, array('Nominatim\SearchDescription', 'bySearchRank')); - - $aSearchHash = array(); - foreach ($aNewPhraseSearches as $iSearch => $aSearch) { - $sHash = serialize($aSearch); - if (isset($aSearchHash[$sHash])) { - unset($aNewPhraseSearches[$iSearch]); - } else { - $aSearchHash[$sHash] = 1; - } - } - - $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) { - $iRank = $aSearch->getRank(); - if ($iRank < $this->iMaxRank) { - if (!isset($aGroupedSearches[$iRank])) { - $aGroupedSearches[$iRank] = array(); - } - $aGroupedSearches[$iRank][] = $aSearch; - } - } - ksort($aGroupedSearches); - - $iSearchCount = 0; - $aSearches = array(); - foreach ($aGroupedSearches as $aNewSearches) { - $iSearchCount += count($aNewSearches); - $aSearches = array_merge($aSearches, $aNewSearches); - if ($iSearchCount > 50) { - break; - } - } - } - - // Revisit searches, drop bad searches and give penalty to unlikely combinations. - $aGroupedSearches = array(); - foreach ($aSearches as $oSearch) { - if (!$oSearch->isValidSearch()) { - continue; - } - - $iRank = $oSearch->getRank(); - if (!isset($aGroupedSearches[$iRank])) { - $aGroupedSearches[$iRank] = array(); - } - $aGroupedSearches[$iRank][] = $oSearch; - } - ksort($aGroupedSearches); - - return $aGroupedSearches; - } - - /* Perform the actual query lookup. - - Returns an ordered list of results, each with the following fields: - osm_type: type of corresponding OSM object - N - node - W - way - R - relation - P - postcode (internally computed) - osm_id: id of corresponding OSM object - class: general object class (corresponds to tag key of primary OSM tag) - type: subclass of object (corresponds to tag value of primary OSM tag) - admin_level: see https://wiki.openstreetmap.org/wiki/Admin_level - rank_search: rank in search hierarchy - (see also https://wiki.openstreetmap.org/wiki/Nominatim/Development_overview#Country_to_street_level) - rank_address: rank in address hierarchy (determines orer in address) - place_id: internal key (may differ between different instances) - country_code: ISO country code - langaddress: localized full address - placename: localized name of object - ref: content of ref tag (if available) - lon: longitude - lat: latitude - importance: importance of place based on Wikipedia link count - 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) - name: full name (currently the same as langaddress) - foundorder: secondary ordering for places with same importance - */ - - - public function lookup() - { - Debug::newFunction('Geocode::lookup'); - if (!$this->sQuery && !$this->aStructuredQuery) { - return array(); - } - - Debug::printDebugArray('Geocode', $this); - - $oCtx = new SearchContext(); - - if ($this->aRoutePoints) { - $oCtx->setViewboxFromRoute( - $this->oDB, - $this->aRoutePoints, - $this->aRouteWidth, - $this->bBoundedSearch - ); - } elseif ($this->aViewBox) { - $oCtx->setViewboxFromBox($this->aViewBox, $this->bBoundedSearch); - } - if ($this->aExcludePlaceIDs) { - $oCtx->setExcludeList($this->aExcludePlaceIDs); - } - if ($this->aCountryCodes) { - $oCtx->setCountryList($this->aCountryCodes); - } - - Debug::newSection('Query Preprocessing'); - - $sQuery = $this->sQuery; - if (!preg_match('//u', $sQuery)) { - userError('Query string is not UTF-8 encoded.'); - } - - // Do we have anything that looks like a lat/lon pair? - $sQuery = $oCtx->setNearPointFromQuery($sQuery); - - if ($sQuery || $this->aStructuredQuery) { - // Start with a single blank search - $aSearches = array(new SearchDescription($oCtx)); - - if ($sQuery) { - $sQuery = $aSearches[0]->extractKeyValuePairs($sQuery); - } - - $sSpecialTerm = ''; - if ($sQuery) { - preg_match_all( - '/\\[([\\w ]*)\\]/u', - $sQuery, - $aSpecialTermsRaw, - PREG_SET_ORDER - ); - if (!empty($aSpecialTermsRaw)) { - Debug::printVar('Special terms', $aSpecialTermsRaw); - } - - foreach ($aSpecialTermsRaw as $aSpecialTerm) { - $sQuery = str_replace($aSpecialTerm[0], ' ', $sQuery); - if (!$sSpecialTerm) { - $sSpecialTerm = $aSpecialTerm[1]; - } - } - } - if (!$sSpecialTerm && $this->aStructuredQuery - && isset($this->aStructuredQuery['amenity'])) { - $sSpecialTerm = $this->aStructuredQuery['amenity']; - unset($this->aStructuredQuery['amenity']); - } - - if ($sSpecialTerm && !$aSearches[0]->hasOperator()) { - $aTokens = $this->oTokenizer->tokensForSpecialTerm($sSpecialTerm); - - if (!empty($aTokens)) { - $aNewSearches = array(); - $oPosition = new SearchPosition('', 0, 1); - $oPosition->setTokenPosition(0, 1); - - foreach ($aSearches as $oSearch) { - foreach ($aTokens as $oToken) { - $aNewSearches = array_merge( - $aNewSearches, - $oToken->extendSearch($oSearch, $oPosition) - ); - } - } - $aSearches = $aNewSearches; - } - } - - // Split query into phrases - // Commas are used to reduce the search space by indicating where phrases split - $aPhrases = array(); - if ($this->aStructuredQuery) { - foreach ($this->aStructuredQuery as $iPhrase => $sPhrase) { - $aPhrases[] = new Phrase($sPhrase, $iPhrase); - } - } else { - foreach (explode(',', $sQuery) as $sPhrase) { - $aPhrases[] = new Phrase($sPhrase, ''); - } - } - - Debug::printDebugArray('Search context', $oCtx); - Debug::printDebugArray('Base search', empty($aSearches) ? null : $aSearches[0]); - - Debug::newSection('Tokenization'); - $oValidTokens = $this->oTokenizer->extractTokensFromPhrases($aPhrases); - - if ($oValidTokens->count() > 0) { - $oCtx->setFullNameWords($oValidTokens->getFullWordIDs()); - - $aPhrases = array_filter($aPhrases, function ($oPhrase) { - return $oPhrase->getWordSets() !== null; - }); - - // Any words that have failed completely? - // TODO: suggestions - - Debug::printGroupTable('Valid Tokens', $oValidTokens->debugInfo()); - Debug::printDebugTable('Phrases', $aPhrases); - - Debug::newSection('Search candidates'); - - $aGroupedSearches = $this->getGroupedSearches($aSearches, $aPhrases, $oValidTokens); - - if (!$this->aStructuredQuery) { - // Reverse phrase array and also reverse the order of the wordsets in - // the first and final phrase. Don't bother about phrases in the middle - // because order in the address doesn't matter. - $aPhrases = array_reverse($aPhrases); - $aPhrases[0]->invertWordSets(); - if (count($aPhrases) > 1) { - $aPhrases[count($aPhrases)-1]->invertWordSets(); - } - $aReverseGroupedSearches = $this->getGroupedSearches($aSearches, $aPhrases, $oValidTokens); - - foreach ($aReverseGroupedSearches as $aSearches) { - foreach ($aSearches as $aSearch) { - if (!isset($aGroupedSearches[$aSearch->getRank()])) { - $aGroupedSearches[$aSearch->getRank()] = array(); - } - $aGroupedSearches[$aSearch->getRank()][] = $aSearch; - } - } - - ksort($aGroupedSearches); - } - } 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->getRank() < $this->iMaxRank) { - if (!isset($aGroupedSearches[$aSearch->getRank()])) { - $aGroupedSearches[$aSearch->getRank()] = array(); - } - $aGroupedSearches[$aSearch->getRank()][] = $aSearch; - } - } - ksort($aGroupedSearches); - } - - // 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 (empty($aGroupedSearches[$iGroup])) { - unset($aGroupedSearches[$iGroup]); - } - } else { - $aSearchHash[$sHash] = 1; - } - } - } - - Debug::printGroupedSearch( - $aGroupedSearches, - $oValidTokens->debugTokenByWordIdList() - ); - - // Start the search process - $iGroupLoop = 0; - $iQueryLoop = 0; - $aNextResults = array(); - foreach ($aGroupedSearches as $iGroupedRank => $aSearches) { - $iGroupLoop++; - $aResults = $aNextResults; - foreach ($aSearches as $oSearch) { - $iQueryLoop++; - - Debug::newSection("Search Loop, group $iGroupLoop, loop $iQueryLoop"); - Debug::printGroupedSearch( - array($iGroupedRank => array($oSearch)), - $oValidTokens->debugTokenByWordIdList() - ); - - $aNewResults = $oSearch->query( - $this->oDB, - $this->iMinAddressRank, - $this->iMaxAddressRank, - $this->iLimit - ); - - // The same result may appear in different rounds, only - // use the one with minimal rank. - foreach ($aNewResults as $iPlace => $oRes) { - if (!isset($aResults[$iPlace]) - || $aResults[$iPlace]->iResultRank > $oRes->iResultRank) { - $aResults[$iPlace] = $oRes; - } - } - - if ($iQueryLoop > 20) { - break; - } - } - - if (!empty($aResults)) { - $aSplitResults = Result::splitResults($aResults); - Debug::printVar('Split results', $aSplitResults); - if ($iGroupLoop <= 4 - && reset($aSplitResults['head'])->iResultRank > 0 - && $iGroupedRank !== array_key_last($aGroupedSearches)) { - // Haven't found an exact match for the query yet. - // Therefore add result from the next group level. - $aNextResults = $aSplitResults['head']; - foreach ($aNextResults as $oRes) { - $oRes->iResultRank--; - } - foreach ($aSplitResults['tail'] as $oRes) { - $oRes->iResultRank--; - $aNextResults[$oRes->iId] = $oRes; - } - $aResults = array(); - } else { - $aResults = $aSplitResults['head']; - } - } - - if (!empty($aResults) && ($this->iMinAddressRank != 0 || $this->iMaxAddressRank != 30)) { - // Need to verify passes rank limits before dropping out of the loop (yuk!) - // reduces the number of place ids, like a filter - // rank_address is 30 for interpolated housenumbers - $aFilterSql = array(); - $sPlaceIds = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX); - if ($sPlaceIds) { - $sSQL = 'SELECT place_id FROM placex '; - $sSQL .= 'WHERE place_id in ('.$sPlaceIds.') '; - $sSQL .= ' AND ('; - $sSQL .= " placex.rank_address between $this->iMinAddressRank and $this->iMaxAddressRank "; - $sSQL .= " OR placex.rank_search between $this->iMinAddressRank and $this->iMaxAddressRank "; - if ($this->aAddressRankList) { - $sSQL .= ' OR placex.rank_address in ('.join(',', $this->aAddressRankList).')'; - } - $sSQL .= ')'; - $aFilterSql[] = $sSQL; - } - $sPlaceIds = Result::joinIdsByTable($aResults, Result::TABLE_POSTCODE); - if ($sPlaceIds) { - $sSQL = ' SELECT place_id FROM location_postcode lp '; - $sSQL .= 'WHERE place_id in ('.$sPlaceIds.') '; - $sSQL .= " AND (lp.rank_address between $this->iMinAddressRank and $this->iMaxAddressRank "; - if ($this->aAddressRankList) { - $sSQL .= ' OR lp.rank_address in ('.join(',', $this->aAddressRankList).')'; - } - $sSQL .= ') '; - $aFilterSql[] = $sSQL; - } - - $aFilteredIDs = array(); - if ($aFilterSql) { - $sSQL = join(' UNION ', $aFilterSql); - Debug::printSQL($sSQL); - $aFilteredIDs = $this->oDB->getCol($sSQL); - } - - $tempIDs = array(); - foreach ($aResults as $oResult) { - if (($this->iMaxAddressRank == 30 && - ($oResult->iTable == Result::TABLE_OSMLINE - || $oResult->iTable == Result::TABLE_TIGER)) - || in_array($oResult->iId, $aFilteredIDs) - ) { - $tempIDs[$oResult->iId] = $oResult; - } - } - $aResults = $tempIDs; - } - - if (!empty($aResults) || $iGroupLoop > 4 || $iQueryLoop > 30) { - break; - } - } - } else { - // Just interpret as a reverse geocode - $oReverse = new ReverseGeocode($this->oDB); - $oReverse->setZoom(18); - - $oLookup = $oReverse->lookupPoint($oCtx->sqlNear, false); - - Debug::printVar('Reverse search', $oLookup); - - if ($oLookup) { - $aResults = array($oLookup->iId => $oLookup); - } - } - - // No results? Done - if (empty($aResults)) { - if ($this->bFallback && $this->fallbackStructuredQuery()) { - return $this->lookup(); - } - - return array(); - } - - if ($this->aAddressRankList) { - $this->oPlaceLookup->setAddressRankList($this->aAddressRankList); - } - $this->oPlaceLookup->setAllowedTypesSQLList($this->sAllowedTypesSQLList); - $this->oPlaceLookup->setLanguagePreference($this->aLangPrefOrder); - if ($oCtx->hasNearPoint()) { - $this->oPlaceLookup->setAnchorSql($oCtx->sqlNear); - } - - $aSearchResults = $this->oPlaceLookup->lookup($aResults); - - $aRecheckWords = preg_split('/\b[\s,\\-]*/u', $sQuery); - foreach ($aRecheckWords as $i => $sWord) { - if (!preg_match('/[\pL\pN]/', $sWord)) { - unset($aRecheckWords[$i]); - } - } - - Debug::printVar('Recheck words', $aRecheckWords); - - foreach ($aSearchResults as $iIdx => $aResult) { - $fRadius = ClassTypes\getDefRadius($aResult); - - $aOutlineResult = $this->oPlaceLookup->getOutlines($aResult['place_id'], $aResult['lon'], $aResult['lat'], $fRadius); - if ($aOutlineResult) { - $aResult = array_merge($aResult, $aOutlineResult); - } - - // Is there an icon set for this type of result? - $sIcon = ClassTypes\getIconFile($aResult); - if (isset($sIcon)) { - $aResult['icon'] = $sIcon; - } - - $sLabel = ClassTypes\getLabel($aResult); - if (isset($sLabel)) { - $aResult['label'] = $sLabel; - } - $aResult['name'] = $aResult['langaddress']; - - if ($oCtx->hasNearPoint()) { - $aResult['importance'] = 0.001; - $aResult['foundorder'] = $aResult['addressimportance']; - } else { - if ($aResult['importance'] == 0) { - $aResult['importance'] = 0.0001; - } - $aResult['importance'] *= $this->viewboxImportanceFactor( - $aResult['lon'], - $aResult['lat'] - ); - - // secondary ordering (for results with same importance (the smaller the better): - // - approximate importance of address parts - if (isset($aResult['addressimportance']) && $aResult['addressimportance']) { - $aResult['foundorder'] = -$aResult['addressimportance']/10; - } else { - $aResult['foundorder'] = -$aResult['importance']; - } - // - number of exact matches from the query - $aResult['foundorder'] -= $aResults[$aResult['place_id']]->iExactMatches; - // - importance of the class/type - $iClassImportance = ClassTypes\getImportance($aResult); - if (isset($iClassImportance)) { - $aResult['foundorder'] += 0.0001 * $iClassImportance; - } else { - $aResult['foundorder'] += 0.01; - } - // - rank - $aResult['foundorder'] -= 0.00001 * (30 - $aResult['rank_search']); - - // Adjust importance for the number of exact string matches in the result - $iCountWords = 0; - $sAddress = $aResult['langaddress']; - foreach ($aRecheckWords as $i => $sWord) { - if (grapheme_stripos($sAddress, $sWord)!==false) { - $iCountWords++; - if (preg_match('/(^|,)\s*'.preg_quote($sWord, '/').'\s*(,|$)/', $sAddress)) { - $iCountWords += 0.1; - } - } - } - - // 0.1 is a completely arbitrary number but something in the range 0.1 to 0.5 would seem right - $aResult['importance'] = $aResult['importance'] + ($iCountWords*0.1); - } - $aSearchResults[$iIdx] = $aResult; - } - uasort($aSearchResults, 'byImportance'); - Debug::printVar('Pre-filter results', $aSearchResults); - - $aOSMIDDone = array(); - $aClassTypeNameDone = array(); - $aToFilter = $aSearchResults; - $aSearchResults = array(); - - foreach ($aToFilter as $aResult) { - $this->aExcludePlaceIDs[$aResult['place_id']] = $aResult['place_id']; - if (!$this->oPlaceLookup->doDeDupe() || (!isset($aOSMIDDone[$aResult['osm_type'].$aResult['osm_id']]) - && !isset($aClassTypeNameDone[$aResult['osm_type'].$aResult['class'].$aResult['type'].$aResult['name'].$aResult['admin_level']])) - ) { - $aOSMIDDone[$aResult['osm_type'].$aResult['osm_id']] = true; - $aClassTypeNameDone[$aResult['osm_type'].$aResult['class'].$aResult['type'].$aResult['name'].$aResult['admin_level']] = true; - $aSearchResults[] = $aResult; - } - - // Absolute limit on number of results - if (count($aSearchResults) >= $this->iFinalLimit) { - break; - } - } - - Debug::printVar('Post-filter results', $aSearchResults); - return $aSearchResults; - } // end lookup() - - public function debugInfo() - { - return array( - 'Query' => $this->sQuery, - 'Structured query' => $this->aStructuredQuery, - 'Name keys' => Debug::fmtArrayVals($this->aLangPrefOrder), - 'Excluded place IDs' => Debug::fmtArrayVals($this->aExcludePlaceIDs), - 'Limit (for searches)' => $this->iLimit, - 'Limit (for results)'=> $this->iFinalLimit, - 'Country codes' => Debug::fmtArrayVals($this->aCountryCodes), - 'Bounded search' => $this->bBoundedSearch, - 'Viewbox' => Debug::fmtArrayVals($this->aViewBox), - 'Route points' => Debug::fmtArrayVals($this->aRoutePoints), - 'Route width' => $this->aRouteWidth, - 'Max rank' => $this->iMaxRank, - 'Min address rank' => $this->iMinAddressRank, - 'Max address rank' => $this->iMaxAddressRank, - 'Address rank list' => Debug::fmtArrayVals($this->aAddressRankList) - ); - } -} // end class diff --git a/lib-php/ParameterParser.php b/lib-php/ParameterParser.php deleted file mode 100644 index a4936d37..00000000 --- a/lib-php/ParameterParser.php +++ /dev/null @@ -1,157 +0,0 @@ -aParams = ($aParams === null) ? $_GET : $aParams; - } - - public function getBool($sName, $bDefault = false) - { - if (!isset($this->aParams[$sName]) - || !is_string($this->aParams[$sName]) - || strlen($this->aParams[$sName]) == 0 - ) { - return $bDefault; - } - - return (bool) $this->aParams[$sName]; - } - - public function getInt($sName, $bDefault = false) - { - if (!isset($this->aParams[$sName]) || is_array($this->aParams[$sName])) { - return $bDefault; - } - - if (!preg_match('/^[+-]?[0-9]+$/', $this->aParams[$sName])) { - userError("Integer number expected for parameter '$sName'"); - } - - return (int) $this->aParams[$sName]; - } - - public function getFloat($sName, $bDefault = false) - { - if (!isset($this->aParams[$sName]) || is_array($this->aParams[$sName])) { - return $bDefault; - } - - if (!preg_match('/^[+-]?[0-9]*\.?[0-9]+$/', $this->aParams[$sName])) { - userError("Floating-point number expected for parameter '$sName'"); - } - - return (float) $this->aParams[$sName]; - } - - public function getString($sName, $bDefault = false) - { - if (!isset($this->aParams[$sName]) - || !is_string($this->aParams[$sName]) - || strlen($this->aParams[$sName]) == 0 - ) { - return $bDefault; - } - - return $this->aParams[$sName]; - } - - public function getSet($sName, $aValues, $sDefault = false) - { - if (!isset($this->aParams[$sName]) - || !is_string($this->aParams[$sName]) - || strlen($this->aParams[$sName]) == 0 - ) { - return $sDefault; - } - - if (!in_array($this->aParams[$sName], $aValues, true)) { - userError("Parameter '$sName' must be one of: ".join(', ', $aValues)); - } - - return $this->aParams[$sName]; - } - - public function getStringList($sName, $aDefault = false) - { - $sValue = $this->getString($sName); - - if ($sValue) { - // removes all NULL, FALSE and Empty Strings but leaves 0 (zero) values - return array_values(array_filter(explode(',', $sValue), 'strlen')); - } - - return $aDefault; - } - - public function getPreferredLanguages($sFallback = null) - { - if ($sFallback === null && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { - $sFallback = $_SERVER['HTTP_ACCEPT_LANGUAGE']; - } - - $aLanguages = array(); - $sLangString = $this->getString('accept-language', $sFallback); - - if ($sLangString - && preg_match_all('/(([a-z]{1,8})([-_][a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $sLangString, $aLanguagesParse, PREG_SET_ORDER) - ) { - foreach ($aLanguagesParse as $iLang => $aLanguage) { - $aLanguages[$aLanguage[1]] = isset($aLanguage[5])?(float)$aLanguage[5]:1 - ($iLang/100); - if (!isset($aLanguages[$aLanguage[2]])) { - $aLanguages[$aLanguage[2]] = $aLanguages[$aLanguage[1]]/10; - } - } - arsort($aLanguages); - } - if (empty($aLanguages) && CONST_Default_Language) { - $aLanguages[CONST_Default_Language] = 1; - } - - foreach ($aLanguages as $sLanguage => $fLanguagePref) { - $this->addNameTag($aLangPrefOrder, 'name:'.$sLanguage); - } - $this->addNameTag($aLangPrefOrder, 'name'); - $this->addNameTag($aLangPrefOrder, 'brand'); - foreach ($aLanguages as $sLanguage => $fLanguagePref) { - $this->addNameTag($aLangPrefOrder, 'official_name:'.$sLanguage); - $this->addNameTag($aLangPrefOrder, 'short_name:'.$sLanguage); - } - $this->addNameTag($aLangPrefOrder, 'official_name'); - $this->addNameTag($aLangPrefOrder, 'short_name'); - $this->addNameTag($aLangPrefOrder, 'ref'); - $this->addNameTag($aLangPrefOrder, 'type'); - return $aLangPrefOrder; - } - - private function addNameTag(&$aLangPrefOrder, $sTag) - { - $aLangPrefOrder[$sTag] = $sTag; - $aLangPrefOrder['_place_'.$sTag] = '_place_'.$sTag; - } - - public function hasSetAny($aParamNames) - { - foreach ($aParamNames as $sName) { - if ($this->getBool($sName)) { - return true; - } - } - - return false; - } -} diff --git a/lib-php/Phrase.php b/lib-php/Phrase.php deleted file mode 100644 index 4ed4d402..00000000 --- a/lib-php/Phrase.php +++ /dev/null @@ -1,89 +0,0 @@ -sPhrase = trim($sPhrase); - $this->sPhraseType = $sPhraseType; - } - - /** - * Get the original phrase of the string. - */ - public function getPhrase() - { - return $this->sPhrase; - } - - /** - * Return the element type of the phrase. - * - * @return string Pharse type if the phrase comes from a structured query - * or empty string otherwise. - */ - public function getPhraseType() - { - return $this->sPhraseType; - } - - public function setWordSets($aWordSets) - { - $this->aWordSets = $aWordSets; - } - - /** - * Return the array of possible segmentations of the phrase. - * - * @return string[][] Array of segmentations, each consisting of an - * array of terms. - */ - public function getWordSets() - { - return $this->aWordSets; - } - - /** - * Invert the set of possible segmentations. - * - * @return void - */ - public function invertWordSets() - { - foreach ($this->aWordSets as $i => $aSet) { - $this->aWordSets[$i] = array_reverse($aSet); - } - } - - public function debugInfo() - { - return array( - 'Type' => $this->sPhraseType, - 'Phrase' => $this->sPhrase, - 'WordSets' => $this->aWordSets - ); - } -} diff --git a/lib-php/PlaceLookup.php b/lib-php/PlaceLookup.php deleted file mode 100644 index 895a30df..00000000 --- a/lib-php/PlaceLookup.php +++ /dev/null @@ -1,615 +0,0 @@ -oDB =& $oDB; - } - - public function doDeDupe() - { - return $this->bDeDupe; - } - - public function setIncludeAddressDetails($b) - { - $this->bAddressDetails = $b; - } - - public function loadParamArray($oParams, $sGeomType = null) - { - $aLangs = $oParams->getPreferredLanguages(); - $this->aLangPrefOrderSql = - 'ARRAY['.join(',', $this->oDB->getDBQuotedList($aLangs)).']'; - - $this->bExtraTags = $oParams->getBool('extratags', false); - $this->bNameDetails = $oParams->getBool('namedetails', false); - - $this->bDeDupe = $oParams->getBool('dedupe', $this->bDeDupe); - - if ($sGeomType === null || $sGeomType == 'geojson') { - $this->bIncludePolygonAsGeoJSON = $oParams->getBool('polygon_geojson'); - } - - 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'); - } - } - $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) { - userError('Select only '.CONST_PolygonOutput_MaximumTypes.' polygon output option'); - } else { - userError('Polygon output is disabled'); - } - } - } - - public function getMoreUrlParams() - { - $aParams = array(); - - if ($this->bAddressDetails) { - $aParams['addressdetails'] = '1'; - } - if ($this->bExtraTags) { - $aParams['extratags'] = '1'; - } - if ($this->bNameDetails) { - $aParams['namedetails'] = '1'; - } - - 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'; - } - - if ($this->fPolygonSimplificationThreshold > 0.0) { - $aParams['polygon_threshold'] = $this->fPolygonSimplificationThreshold; - } - - if (!$this->bDeDupe) { - $aParams['dedupe'] = '0'; - } - - return $aParams; - } - - public function setAnchorSql($sPoint) - { - $this->sAnchorSql = $sPoint; - } - - public function setAddressRankList($aList) - { - $this->sAddressRankListSql = '('.join(',', $aList).')'; - } - - public function setAllowedTypesSQLList($sSql) - { - $this->sAllowedTypesSQLList = $sSql; - } - - public function setLanguagePreference($aLangPrefOrder) - { - $this->aLangPrefOrderSql = $this->oDB->getArraySQL( - $this->oDB->getDBQuotedList($aLangPrefOrder) - ); - } - - private function addressImportanceSql($sGeometry, $sPlaceId) - { - 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) - { - if ($this->bAddressDetails) { - return ''; // langaddress will be computed from address details - } - - return 'get_address_by_language(place_id,'.$sHousenumber.','.$this->aLangPrefOrderSql.') AS langaddress,'; - } - - public function lookupOSMID($sType, $iID) - { - $sSQL = 'select place_id from placex where osm_type = :type and osm_id = :id'; - $iPlaceID = $this->oDB->getOne($sSQL, array(':type' => $sType, ':id' => $iID)); - - if (!$iPlaceID) { - return null; - } - - $aResults = $this->lookup(array($iPlaceID => new Result($iPlaceID)), 0, 30, true); - - return empty($aResults) ? null : reset($aResults); - } - - public function lookup($aResults, $iMinRank = 0, $iMaxRank = 30, $bAllowLinked = false) - { - Debug::newFunction('Place lookup'); - - if (empty($aResults)) { - return array(); - } - $aSubSelects = array(); - - $sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX); - if ($sPlaceIDs) { - Debug::printVar('Ids from placex', $sPlaceIDs); - $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,'; - $sSQL .= ' -1 as housenumber,'; - $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( - 'ST_Collect(centroid)', - 'min(CASE WHEN placex.rank_search < 28 THEN placex.place_id ELSE placex.parent_place_id END)' - ); - $sSQL .= " COALESCE(extratags->'place', extratags->'linked_place') AS extra_place "; - $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; - } - $sSQL .= ' ) '; - if ($this->sAllowedTypesSQLList) { - $sSQL .= 'AND placex.class in '.$this->sAllowedTypesSQLList; - } - if (!$bAllowLinked) { - $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, '; - if (!$this->bDeDupe) { - $sSQL .= 'place_id,'; - } - if (!$this->bAddressDetails) { - $sSQL .= 'langaddress, '; - } - $sSQL .= ' placename, '; - $sSQL .= ' ref, '; - if ($this->bExtraTags) { - $sSQL .= 'extratags, '; - } - if ($this->bNameDetails) { - $sSQL .= 'name, '; - } - $sSQL .= ' extra_place '; - - $aSubSelects[] = $sSQL; - } - - // postcode table - $sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_POSTCODE); - if ($sPlaceIDs) { - Debug::printVar('Ids from location_postcode', $sPlaceIDs); - $sSQL = 'SELECT'; - $sSQL .= " 'P' as osm_type,"; - $sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id = lp.parent_place_id) as osm_id,'; - $sSQL .= " 'place' as class, 'postcode' as type,"; - $sSQL .= ' null::smallint as admin_level, rank_search, rank_address,'; - $sSQL .= ' place_id, parent_place_id,'; - $sSQL .= ' -1 as housenumber,'; - $sSQL .= ' country_code,'; - $sSQL .= $this->langAddressSql('-1'); - $sSQL .= ' postcode as placename,'; - $sSQL .= ' postcode as ref,'; - if ($this->bExtraTags) { - $sSQL .= 'null::text AS extra,'; - } - if ($this->bNameDetails) { - $sSQL .= 'null::text AS names,'; - } - $sSQL .= ' ST_x(geometry) AS lon, ST_y(geometry) AS lat,'; - $sSQL .= ' (0.75-(rank_search::float/40)) AS importance, '; - $sSQL .= $this->addressImportanceSql('geometry', 'lp.parent_place_id'); - $sSQL .= ' null::text AS extra_place '; - $sSQL .= 'FROM location_postcode lp'; - $sSQL .= " WHERE place_id in ($sPlaceIDs) "; - $sSQL .= " AND lp.rank_address between $iMinRank and $iMaxRank"; - - $aSubSelects[] = $sSQL; - } - - // 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) { - Debug::printVar('Ids from Tiger table', $sPlaceIDs); - $sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_TIGER); - // Tiger search only if a housenumber was searched and if it was found - // (realized through a join) - $sSQL = ' SELECT '; - $sSQL .= " 'T' AS osm_type, "; - $sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id=blub.parent_place_id) as osm_id, '; - $sSQL .= " 'place' AS class, "; - $sSQL .= " 'house' AS type, "; - $sSQL .= ' null::smallint AS admin_level, '; - $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'); - $sSQL .= ' null::text AS placename, '; - $sSQL .= ' null::text AS ref, '; - if ($this->bExtraTags) { - $sSQL .= 'null::text AS extra,'; - } - if ($this->bNameDetails) { - $sSQL .= 'null::text AS names,'; - } - $sSQL .= ' st_x(centroid) AS lon, '; - $sSQL .= ' st_y(centroid) AS lat,'; - $sSQL .= ' -1.15 AS importance, '; - $sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id'); - $sSQL .= ' null::text AS extra_place '; - $sSQL .= ' FROM ('; - $sSQL .= ' SELECT place_id, '; // interpolate the Tiger housenumbers here - $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, '; - $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 - - $aSubSelects[] = $sSQL; - } - } - - // osmline - interpolated housenumbers - $sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_OSMLINE); - if ($sPlaceIDs) { - Debug::printVar('Ids from interpolation', $sPlaceIDs); - $sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_OSMLINE); - // interpolation line search only if a housenumber was searched - // (realized through a join) - $sSQL = 'SELECT '; - $sSQL .= " 'W' AS osm_type, "; - $sSQL .= ' osm_id, '; - $sSQL .= " 'place' AS class, "; - $sSQL .= " 'house' AS type, "; - $sSQL .= ' null::smallint AS admin_level, '; - $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'); - $sSQL .= ' null::text AS placename, '; - $sSQL .= ' null::text AS ref, '; - if ($this->bExtraTags) { - $sSQL .= 'null::text AS extra, '; - } - if ($this->bNameDetails) { - $sSQL .= 'null::text AS names, '; - } - $sSQL .= ' st_x(centroid) AS lon, '; - $sSQL .= ' st_y(centroid) AS lat, '; - // slightly smaller than the importance for normal houses - $sSQL .= ' -0.1 AS importance, '; - $sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id'); - $sSQL .= ' null::text AS extra_place '; - $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) '; - $sSQL .= ' ELSE linegeo '; - $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 - - $aSubSelects[] = $sSQL; - } - } - - if (empty($aSubSelects)) { - return array(); - } - - $sSQL = join(' UNION ', $aSubSelects); - Debug::printSQL($sSQL); - $aPlaces = $this->oDB->getAll($sSQL, null, 'Could not lookup place'); - - foreach ($aPlaces as &$aPlace) { - $aPlace['importance'] = (float) $aPlace['importance']; - if ($this->bAddressDetails) { - // to get addressdetails for tiger data, the housenumber is needed - $aPlace['address'] = new AddressDetails( - $this->oDB, - $aPlace['place_id'], - $aPlace['housenumber'], - $this->aLangPrefOrderSql - ); - $aPlace['langaddress'] = $aPlace['address']->getLocaleAddress(); - } - - if ($this->bExtraTags) { - if ($aPlace['extra']) { - $aPlace['sExtraTags'] = json_decode($aPlace['extra'], true); - } else { - $aPlace['sExtraTags'] = (object) array(); - } - } - - if ($this->bNameDetails) { - $aPlace['sNameDetails'] = $this->extractNames($aPlace['names']); - } - - $aPlace['addresstype'] = ClassTypes\getLabelTag( - $aPlace, - $aPlace['country_code'] - ); - - $aResults[$aPlace['place_id']] = $aPlace; - } - - $aResults = array_filter( - $aResults, - function ($v) { - return !($v instanceof Result); - } - ); - - Debug::printVar('Places', $aResults); - - return $aResults; - } - - - private function extractNames($sNames) - { - if (!$sNames) { - return (object) array(); - } - - $aFullNames = json_decode($sNames, true); - $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; - } - - - /* 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 - */ - public function getOutlines($iPlaceID, $fLon = null, $fLat = null, $fRadius = null, $fLonReverse = null, $fLatReverse = null) - { - - $aOutlineResult = array(); - if (!$iPlaceID) { - return $aOutlineResult; - } - - // Get the bounding box and outline polygon - $sSQL = 'select place_id,0 as numfeatures,st_area(geometry) as area,'; - $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'; - 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'; - } - - $sSQL .= ' FROM (SELECT place_id'; - if ($fLonReverse != null && $fLatReverse != null) { - $sSQL .= ',CASE WHEN (class = \'highway\') AND (ST_GeometryType(geometry) = \'ST_LineString\') THEN '; - $sSQL .=' ST_ClosestPoint(geometry, ST_SetSRID(ST_Point('.$fLatReverse.','.$fLonReverse.'),4326))'; - $sSQL .=' ELSE centroid END AS centroid'; - } else { - $sSQL .= ',centroid'; - } - if ($this->fPolygonSimplificationThreshold > 0) { - $sSQL .= ',ST_SimplifyPreserveTopology(geometry,'.$this->fPolygonSimplificationThreshold.') as geometry'; - } else { - $sSQL .= ',geometry'; - } - $sSQL .= ' FROM placex where place_id = '.$iPlaceID.') as plx'; - - $aPointPolygon = $this->oDB->getRow($sSQL, null, 'Could not get outline'); - - if ($aPointPolygon && $aPointPolygon['place_id']) { - if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null) { - $aOutlineResult['lat'] = $aPointPolygon['centrelat']; - $aOutlineResult['lon'] = $aPointPolygon['centrelon']; - } - - 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']; - } - - if (abs($aPointPolygon['minlat'] - $aPointPolygon['maxlat']) < 0.0000001) { - $aPointPolygon['minlat'] = $aPointPolygon['minlat'] - $fRadius; - $aPointPolygon['maxlat'] = $aPointPolygon['maxlat'] + $fRadius; - } - - if (abs($aPointPolygon['minlon'] - $aPointPolygon['maxlon']) < 0.0000001) { - $aPointPolygon['minlon'] = $aPointPolygon['minlon'] - $fRadius; - $aPointPolygon['maxlon'] = $aPointPolygon['maxlon'] + $fRadius; - } - - $aOutlineResult['aBoundingBox'] = array( - (string)$aPointPolygon['minlat'], - (string)$aPointPolygon['maxlat'], - (string)$aPointPolygon['minlon'], - (string)$aPointPolygon['maxlon'] - ); - } - - // as a fallback we generate a bounding box without knowing the size of the geometry - if ((!isset($aOutlineResult['aBoundingBox'])) && isset($fLon)) { - $aBounds = array( - 'minlat' => $fLat - $fRadius, - 'maxlat' => $fLat + $fRadius, - 'minlon' => $fLon - $fRadius, - 'maxlon' => $fLon + $fRadius - ); - - $aOutlineResult['aBoundingBox'] = array( - (string)$aBounds['minlat'], - (string)$aBounds['maxlat'], - (string)$aBounds['minlon'], - (string)$aBounds['maxlon'] - ); - } - return $aOutlineResult; - } -} diff --git a/lib-php/Result.php b/lib-php/Result.php deleted file mode 100644 index 4b244d1d..00000000 --- a/lib-php/Result.php +++ /dev/null @@ -1,129 +0,0 @@ - $this->iTable, - 'ID' => $this->iId, - 'House number' => $this->iHouseNumber, - 'Exact Matches' => $this->iExactMatches, - 'Result rank' => $this->iResultRank - ); - } - - - public function __construct($sId, $iTable = Result::TABLE_PLACEX) - { - $this->iTable = $iTable; - $this->iId = (int) $sId; - } - - public static function joinIdsByTable($aResults, $iTable) - { - return join(',', array_keys(array_filter( - $aResults, - function ($aValue) use ($iTable) { - return $aValue->iTable == $iTable; - } - ))); - } - - public static function joinIdsByTableMinRank($aResults, $iTable, $iMinAddressRank) - { - return join(',', array_keys(array_filter( - $aResults, - function ($aValue) use ($iTable, $iMinAddressRank) { - return $aValue->iTable == $iTable && $aValue->iAddressRank >= $iMinAddressRank; - } - ))); - } - - public static function joinIdsByTableMaxRank($aResults, $iTable, $iMaxAddressRank) - { - return join(',', array_keys(array_filter( - $aResults, - function ($aValue) use ($iTable, $iMaxAddressRank) { - return $aValue->iTable == $iTable && $aValue->iAddressRank <= $iMaxAddressRank; - } - ))); - } - - public static function sqlHouseNumberTable($aResults, $iTable) - { - $sHousenumbers = ''; - $sSep = ''; - foreach ($aResults as $oResult) { - if ($oResult->iTable == $iTable) { - $sHousenumbers .= $sSep.'('.$oResult->iId.','; - $sHousenumbers .= $oResult->iHouseNumber.')'; - $sSep = ','; - } - } - - return $sHousenumbers; - } - - /** - * Split a result array into highest ranked result and the rest - * - * @param object[] $aResults List of results to split. - * - * @return array[] - */ - public static function splitResults($aResults) - { - $aHead = array(); - $aTail = array(); - $iMinRank = 10000; - - foreach ($aResults as $oRes) { - if ($oRes->iResultRank < $iMinRank) { - $aTail += $aHead; - $aHead = array($oRes->iId => $oRes); - $iMinRank = $oRes->iResultRank; - } elseif ($oRes->iResultRank == $iMinRank) { - $aHead[$oRes->iId] = $oRes; - } else { - $aTail[$oRes->iId] = $oRes; - } - } - - return array('head' => $aHead, 'tail' => $aTail); - } -} diff --git a/lib-php/ReverseGeocode.php b/lib-php/ReverseGeocode.php deleted file mode 100644 index f6ea590f..00000000 --- a/lib-php/ReverseGeocode.php +++ /dev/null @@ -1,401 +0,0 @@ -oDB =& $oDB; - } - - - public function setZoom($iZoom) - { - // 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 - 13 => 19, // Village - 14 => 22, // Neighbourhood - 15 => 25, // Locality - 16 => 26, // major street - 17 => 27, // minor street - 18 => 30, // or >, Building - 19 => 30, // or >, Building - ); - $this->iMaxRank = (isset($iZoom) && isset($aZoomRank[$iZoom]))?$aZoomRank[$iZoom]:28; - } - - /** - * Find the closest interpolation with the given search diameter. - * - * @param string $sPointSQL Reverse geocoding point as SQL - * @param float $fSearchDiam Search diameter - * - * @return Record of the interpolation or null. - */ - protected function lookupInterpolation($sPointSQL, $fSearchDiam) - { - Debug::newFunction('lookupInterpolation'); - $sSQL = 'SELECT place_id, parent_place_id, 30 as rank_search,'; - $sSQL .= ' (CASE WHEN endnumber != startnumber'; - $sSQL .= ' THEN (endnumber - startnumber) * ST_LineLocatePoint(linegeo,'.$sPointSQL.')'; - $sSQL .= ' ELSE startnumber END) as fhnr,'; - $sSQL .= ' startnumber, endnumber, step,'; - $sSQL .= ' ST_Distance(linegeo,'.$sPointSQL.') as distance'; - $sSQL .= ' FROM location_property_osmline'; - $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', linegeo, '.$fSearchDiam.')'; - $sSQL .= ' and indexed_status = 0 and startnumber is not NULL '; - $sSQL .= ' and parent_place_id != 0'; - $sSQL .= ' ORDER BY distance ASC limit 1'; - Debug::printSQL($sSQL); - - return $this->oDB->getRow( - $sSQL, - null, - 'Could not determine closest housenumber on an osm interpolation line.' - ); - } - - protected function lookupLargeArea($sPointSQL, $iMaxRank) - { - $sCountryCode = $this->getCountryCode($sPointSQL); - if (CONST_Search_WithinCountries and $sCountryCode == null) { - return null; - } - - if ($iMaxRank > 4) { - $aPlace = $this->lookupPolygon($sPointSQL, $iMaxRank); - if ($aPlace) { - return new Result($aPlace['place_id']); - } - } - - // If no polygon which contains the searchpoint is found, - // searches in the country_osm_grid table for a polygon. - return $this->lookupInCountry($sPointSQL, $iMaxRank, $sCountryCode); - } - - protected function getCountryCode($sPointSQL) - { - Debug::newFunction('getCountryCode'); - // searches for polygon in table country_osm_grid which contains the searchpoint - // and searches for the nearest place node to the searchpoint in this polygon - $sSQL = 'SELECT country_code FROM country_osm_grid'; - $sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.') LIMIT 1'; - Debug::printSQL($sSQL); - - $sCountryCode = $this->oDB->getOne( - $sSQL, - null, - 'Could not determine country polygon containing the point.' - ); - return $sCountryCode; - } - - protected function lookupInCountry($sPointSQL, $iMaxRank, $sCountryCode) - { - Debug::newFunction('lookupInCountry'); - if ($sCountryCode) { - if ($iMaxRank > 4) { - // look for place nodes with the given country code - $sSQL = 'SELECT place_id FROM'; - $sSQL .= ' (SELECT place_id, rank_search,'; - $sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance'; - $sSQL .= ' FROM placex'; - $sSQL .= ' WHERE osm_type = \'N\''; - $sSQL .= ' AND country_code = \''.$sCountryCode.'\''; - $sSQL .= ' AND rank_address between 4 and 25'; // needed to select right index - $sSQL .= ' AND rank_search between 5 and ' .min(25, $iMaxRank); - $sSQL .= ' AND type != \'postcode\''; - $sSQL .= ' AND name IS NOT NULL '; - $sSQL .= ' and indexed_status = 0 and linked_place_id is null'; - $sSQL .= ' AND ST_Buffer(geometry, reverse_place_diameter(rank_search)) && '.$sPointSQL; - $sSQL .= ') as a '; - $sSQL .= 'WHERE distance <= reverse_place_diameter(rank_search)'; - $sSQL .= ' ORDER BY rank_search DESC, distance ASC'; - $sSQL .= ' LIMIT 1'; - Debug::printSQL($sSQL); - - $aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.'); - Debug::printVar('Country node', $aPlace); - - if ($aPlace) { - return new Result($aPlace['place_id']); - } - } - - // still nothing, then return the country object - $sSQL = 'SELECT place_id, ST_distance('.$sPointSQL.', centroid) as distance'; - $sSQL .= ' FROM placex'; - $sSQL .= ' WHERE country_code = \''.$sCountryCode.'\''; - $sSQL .= ' AND rank_search = 4 AND rank_address = 4'; - $sSQL .= ' AND class in (\'boundary\', \'place\')'; - $sSQL .= ' AND linked_place_id is null'; - $sSQL .= ' ORDER BY distance ASC'; - Debug::printSQL($sSQL); - - $aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.'); - Debug::printVar('Country place', $aPlace); - if ($aPlace) { - return new Result($aPlace['place_id']); - } - } - - return null; - } - - /** - * Search for areas or nodes for areas or nodes between state and suburb level. - * - * @param string $sPointSQL Search point as SQL string. - * @param int $iMaxRank Maximum address rank of the feature. - * - * @return Record of the found feature or null. - * - * Searches first for polygon that contains the search point. - * If such a polygon is found, place nodes with a higher rank are - * searched inside the polygon. - */ - protected function lookupPolygon($sPointSQL, $iMaxRank) - { - Debug::newFunction('lookupPolygon'); - // polygon search begins at suburb-level - if ($iMaxRank > 25) { - $iMaxRank = 25; - } - // no polygon search over country-level - if ($iMaxRank < 5) { - $iMaxRank = 5; - } - // search for polygon - $sSQL = 'SELECT place_id, parent_place_id, rank_address, rank_search FROM'; - $sSQL .= '(select place_id, parent_place_id, rank_address, rank_search, country_code, geometry'; - $sSQL .= ' FROM placex'; - $sSQL .= ' WHERE ST_GeometryType(geometry) in (\'ST_Polygon\', \'ST_MultiPolygon\')'; - // Ensure that query planner doesn't use the index on rank_search. - $sSQL .= ' AND coalesce(rank_search, 0) between 5 and ' .$iMaxRank; - $sSQL .= ' AND rank_address between 4 and 25'; // needed for index selection - $sSQL .= ' AND geometry && '.$sPointSQL; - $sSQL .= ' AND type != \'postcode\' '; - $sSQL .= ' AND name is not null'; - $sSQL .= ' AND indexed_status = 0 and linked_place_id is null'; - $sSQL .= ' ORDER BY rank_search DESC LIMIT 50 ) as a'; - $sSQL .= ' WHERE ST_Contains(geometry, '.$sPointSQL.' )'; - $sSQL .= ' ORDER BY rank_search DESC LIMIT 1'; - Debug::printSQL($sSQL); - - $aPoly = $this->oDB->getRow($sSQL, null, 'Could not determine polygon containing the point.'); - Debug::printVar('Polygon result', $aPoly); - - if ($aPoly) { - // if a polygon is found, search for placenodes begins ... - $iRankAddress = $aPoly['rank_address']; - $iRankSearch = $aPoly['rank_search']; - $iPlaceID = $aPoly['place_id']; - - if ($iRankSearch != $iMaxRank) { - $sSQL = 'SELECT place_id FROM '; - $sSQL .= '(SELECT place_id, rank_search, country_code, geometry,'; - $sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance'; - $sSQL .= ' FROM placex'; - $sSQL .= ' WHERE osm_type = \'N\''; - $sSQL .= ' AND rank_search > '.$iRankSearch; - $sSQL .= ' AND rank_search <= '.$iMaxRank; - $sSQL .= ' AND rank_address between 4 and 25'; // needed to select right index - $sSQL .= ' AND type != \'postcode\''; - $sSQL .= ' AND name IS NOT NULL '; - $sSQL .= ' AND indexed_status = 0 AND linked_place_id is null'; - $sSQL .= ' AND ST_Buffer(geometry, reverse_place_diameter(rank_search)) && '.$sPointSQL; - $sSQL .= ' ORDER BY rank_search DESC, distance ASC'; - $sSQL .= ' limit 100) as a'; - $sSQL .= ' WHERE ST_Contains((SELECT geometry FROM placex WHERE place_id = '.$iPlaceID.'), geometry )'; - $sSQL .= ' AND distance <= reverse_place_diameter(rank_search)'; - $sSQL .= ' ORDER BY rank_search DESC, distance ASC'; - $sSQL .= ' LIMIT 1'; - Debug::printSQL($sSQL); - - $aPlaceNode = $this->oDB->getRow($sSQL, null, 'Could not determine place node.'); - Debug::printVar('Nearest place node', $aPlaceNode); - if ($aPlaceNode) { - return $aPlaceNode; - } - } - } - return $aPoly; - } - - - public function lookup($fLat, $fLon, $bDoInterpolation = true) - { - return $this->lookupPoint( - 'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)', - $bDoInterpolation - ); - } - - public function lookupPoint($sPointSQL, $bDoInterpolation = true) - { - Debug::newFunction('lookupPoint'); - // Find the nearest point - $fSearchDiam = 0.006; - $oResult = null; - $aPlace = null; - - // for POI or street level - if ($this->iMaxRank >= 26) { - // starts if the search is on POI or street level, - // searches for the nearest POI or street, - // if a street is found and a POI is searched for, - // the nearest POI which the found street is a parent of is chosen. - $sSQL = 'select place_id,parent_place_id,rank_address,country_code,'; - $sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance'; - $sSQL .= ' FROM '; - $sSQL .= ' placex'; - $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')'; - $sSQL .= ' AND'; - $sSQL .= ' rank_address between 26 and '.$this->iMaxRank; - $sSQL .= ' and (name is not null or housenumber is not null'; - $sSQL .= ' or rank_address between 26 and 27)'; - $sSQL .= ' and (rank_address between 26 and 27'; - $sSQL .= ' or ST_GeometryType(geometry) != \'ST_LineString\')'; - $sSQL .= ' and class not in (\'boundary\')'; - $sSQL .= ' and indexed_status = 0 and linked_place_id is null'; - $sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') '; - $sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))'; - $sSQL .= ' ORDER BY distance ASC limit 1'; - Debug::printSQL($sSQL); - - $aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.'); - - Debug::printVar('POI/street level result', $aPlace); - if ($aPlace) { - $iPlaceID = $aPlace['place_id']; - $oResult = new Result($iPlaceID); - $iRankAddress = $aPlace['rank_address']; - } - - if ($aPlace) { - // if street and maxrank > streetlevel - if ($iRankAddress <= 27 && $this->iMaxRank > 27) { - // find the closest object (up to a certain radius) of which the street is a parent of - $sSQL = ' select place_id,'; - $sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance'; - $sSQL .= ' FROM '; - $sSQL .= ' placex'; - // radius ? - $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, 0.001)'; - $sSQL .= ' AND parent_place_id = '.$iPlaceID; - $sSQL .= ' and rank_address > 28'; - $sSQL .= ' and ST_GeometryType(geometry) != \'ST_LineString\''; - $sSQL .= ' and (name is not null or housenumber is not null)'; - $sSQL .= ' and class not in (\'boundary\')'; - $sSQL .= ' and indexed_status = 0 and linked_place_id is null'; - $sSQL .= ' ORDER BY distance ASC limit 1'; - Debug::printSQL($sSQL); - - $aStreet = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.'); - Debug::printVar('Closest POI result', $aStreet); - - if ($aStreet) { - $aPlace = $aStreet; - $oResult = new Result($aStreet['place_id']); - $iRankAddress = 30; - } - } - - // In the US we can check TIGER data for nearest housenumber - if (CONST_Use_US_Tiger_Data - && $iRankAddress <= 27 - && $aPlace['country_code'] == 'us' - && $this->iMaxRank >= 28 - ) { - $sSQL = 'SELECT place_id,parent_place_id,30 as rank_search,'; - $sSQL .= ' (endnumber - startnumber) * ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fhnr,'; - $sSQL .= ' startnumber, endnumber, step,'; - $sSQL .= ' ST_Distance('.$sPointSQL.', linegeo) as distance'; - $sSQL .= ' FROM location_property_tiger WHERE parent_place_id = '.$oResult->iId; - $sSQL .= ' AND ST_DWithin('.$sPointSQL.', linegeo, 0.001)'; - $sSQL .= ' ORDER BY distance ASC limit 1'; - Debug::printSQL($sSQL); - - $aPlaceTiger = $this->oDB->getRow($sSQL, null, 'Could not determine closest Tiger place.'); - Debug::printVar('Tiger house number result', $aPlaceTiger); - - if ($aPlaceTiger) { - $aPlace = $aPlaceTiger; - $oResult = new Result($aPlaceTiger['place_id'], Result::TABLE_TIGER); - $iRndNum = max(0, round($aPlaceTiger['fhnr'] / $aPlaceTiger['step']) * $aPlaceTiger['step']); - $oResult->iHouseNumber = $aPlaceTiger['startnumber'] + $iRndNum; - if ($oResult->iHouseNumber > $aPlaceTiger['endnumber']) { - $oResult->iHouseNumber = $aPlaceTiger['endnumber']; - } - $iRankAddress = 30; - } - } - } - - if ($bDoInterpolation && $this->iMaxRank >= 30) { - $fDistance = $fSearchDiam; - if ($aPlace) { - // We can't reliably go from the closest street to an - // interpolation line because the closest interpolation - // may have a different street segments as a parent. - // Therefore allow an interpolation line to take precedence - // even when the street is closer. - $fDistance = $iRankAddress < 28 ? 0.001 : $aPlace['distance']; - } - - $aHouse = $this->lookupInterpolation($sPointSQL, $fDistance); - Debug::printVar('Interpolation result', $aPlace); - - if ($aHouse) { - $oResult = new Result($aHouse['place_id'], Result::TABLE_OSMLINE); - $iRndNum = max(0, round($aHouse['fhnr'] / $aHouse['step']) * $aHouse['step']); - $oResult->iHouseNumber = $aHouse['startnumber'] + $iRndNum; - if ($oResult->iHouseNumber > $aHouse['endnumber']) { - $oResult->iHouseNumber = $aHouse['endnumber']; - } - $aPlace = $aHouse; - } - } - - if (!$aPlace) { - // if no POI or street is found ... - $oResult = $this->lookupLargeArea($sPointSQL, 25); - } - } else { - // lower than street level ($iMaxRank < 26 ) - $oResult = $this->lookupLargeArea($sPointSQL, $this->iMaxRank); - } - - Debug::printVar('Final result', $oResult); - return $oResult; - } -} diff --git a/lib-php/SearchContext.php b/lib-php/SearchContext.php deleted file mode 100644 index 3223b5c9..00000000 --- a/lib-php/SearchContext.php +++ /dev/null @@ -1,319 +0,0 @@ -aFullNameWords = $aWordList; - } - - public function getFullNameTerms() - { - return $this->aFullNameWords; - } - - /** - * Check if a reference point is defined. - * - * @return bool True if a reference point is defined. - */ - public function hasNearPoint() - { - return $this->fNearRadius !== false; - } - - /** - * Get radius around reference point. - * - * @return float Search radius around reference point. - */ - public function nearRadius() - { - return $this->fNearRadius; - } - - /** - * Set search reference point in WGS84. - * - * If set, then only places around this point will be taken into account. - * - * @param float $fLat Latitude of point. - * @param float $fLon Longitude of point. - * @param float $fRadius Search radius around point. - * - * @return void - */ - public function setNearPoint($fLat, $fLon, $fRadius = 0.1) - { - $this->fNearRadius = $fRadius; - $this->sqlNear = 'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)'; - } - - /** - * Check if the search is geographically restricted. - * - * Searches are restricted if a reference point is given or if - * a bounded viewbox is set. - * - * @return bool True, if the search is geographically bounded. - */ - public function isBoundedSearch() - { - return $this->hasNearPoint() || ($this->sqlViewboxSmall && $this->bViewboxBounded); - } - - /** - * Set rectangular viewbox. - * - * The viewbox may be bounded which means that no search results - * must be outside the viewbox. - * - * @param float[4] $aViewBox Coordinates of the viewbox. - * @param bool $bBounded True if the viewbox is bounded. - * - * @return void - */ - public function setViewboxFromBox(&$aViewBox, $bBounded) - { - $this->bViewboxBounded = $bBounded; - $this->sqlViewboxCentre = ''; - - $this->sqlViewboxSmall = sprintf( - 'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)', - $aViewBox[0], - $aViewBox[1], - $aViewBox[2], - $aViewBox[3] - ); - - $fHeight = abs($aViewBox[0] - $aViewBox[2]); - $fWidth = abs($aViewBox[1] - $aViewBox[3]); - - $this->sqlViewboxLarge = sprintf( - 'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)', - max($aViewBox[0], $aViewBox[2]) + $fHeight, - max($aViewBox[1], $aViewBox[3]) + $fWidth, - min($aViewBox[0], $aViewBox[2]) - $fHeight, - min($aViewBox[1], $aViewBox[3]) - $fWidth - ); - } - - /** - * Set viewbox along a route. - * - * The viewbox may be bounded which means that no search results - * must be outside the viewbox. - * - * @param object $oDB Nominatim::DB instance to use for computing the box. - * @param string[] $aRoutePoints List of x,y coordinates along a route. - * @param float $fRouteWidth Buffer around the route to use. - * @param bool $bBounded True if the viewbox bounded. - * - * @return void - */ - public function setViewboxFromRoute(&$oDB, $aRoutePoints, $fRouteWidth, $bBounded) - { - $this->bViewboxBounded = $bBounded; - $this->sqlViewboxCentre = "ST_SetSRID('LINESTRING("; - $sSep = ''; - foreach ($aRoutePoints as $aPoint) { - $fPoint = (float)$aPoint; - $this->sqlViewboxCentre .= $sSep.$fPoint; - $sSep = ($sSep == ' ') ? ',' : ' '; - } - $this->sqlViewboxCentre .= ")'::geometry,4326)"; - - $sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/69).')'; - $sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get small viewbox'); - $this->sqlViewboxSmall = "'".$sGeom."'::geometry"; - - $sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/30).')'; - $sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get large viewbox'); - $this->sqlViewboxLarge = "'".$sGeom."'::geometry"; - } - - /** - * Set list of excluded place IDs. - * - * @param integer[] $aExcluded List of IDs. - * - * @return void - */ - public function setExcludeList($aExcluded) - { - $this->sqlExcludeList = ' not in ('.join(',', $aExcluded).')'; - } - - /** - * Set list of countries to restrict search to. - * - * @param string[] $aCountries List of two-letter lower-case country codes. - * - * @return void - */ - public function setCountryList($aCountries) - { - $this->sqlCountryList = '('.join(',', array_map('addQuotes', $aCountries)).')'; - $this->aCountryList = $aCountries; - } - - /** - * Extract a reference point from a query string. - * - * @param string $sQuery Query to scan. - * - * @return string The remaining query string. - */ - public function setNearPointFromQuery($sQuery) - { - $aResult = parseLatLon($sQuery); - - if ($aResult !== false - && $aResult[1] <= 90.1 - && $aResult[1] >= -90.1 - && $aResult[2] <= 180.1 - && $aResult[2] >= -180.1 - ) { - $this->setNearPoint($aResult[1], $aResult[2]); - $sQuery = trim(str_replace($aResult[0], ' ', $sQuery)); - } - - return $sQuery; - } - - /** - * Get an SQL snippet for computing the distance from the reference point. - * - * @param string $sObj SQL variable name to compute the distance from. - * - * @return string An SQL string. - */ - public function distanceSQL($sObj) - { - return 'ST_Distance('.$this->sqlNear.", $sObj)"; - } - - /** - * Get an SQL snippet for checking if something is within range of the - * reference point. - * - * @param string $sObj SQL variable name to compute if it is within range. - * - * @return string An SQL string. - */ - public function withinSQL($sObj) - { - return sprintf('ST_DWithin(%s, %s, %F)', $sObj, $this->sqlNear, $this->fNearRadius); - } - - /** - * Get an SQL snippet of the importance factor of the viewbox. - * - * The importance factor is computed by checking if an object is within - * the viewbox and/or the extended version of the viewbox. - * - * @param string $sObj SQL variable name of object to weight the importance - * - * @return string SQL snippet of the factor with a leading multiply sign. - */ - public function viewboxImportanceSQL($sObj) - { - $sSQL = ''; - - if ($this->sqlViewboxSmall) { - $sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxSmall, $sObj) THEN 1 ELSE 0.5 END"; - } - if ($this->sqlViewboxLarge) { - $sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxLarge, $sObj) THEN 1 ELSE 0.5 END"; - } - - return $sSQL; - } - - /** - * SQL snippet checking if a place ID should be excluded. - * - * @param string $sVariable SQL variable name of place ID to check, - * potentially prefixed with more SQL. - * - * @return string SQL snippet. - */ - public function excludeSQL($sVariable) - { - if ($this->sqlExcludeList) { - return $sVariable.$this->sqlExcludeList; - } - - return ''; - } - - /** - * Check if the given country is covered by the search context. - * - * @param string $sCountryCode Country code of the country to check. - * - * @return True, if no country code restrictions are set or the - * country is included in the country list. - */ - public function isCountryApplicable($sCountryCode) - { - return $this->aCountryList === null || in_array($sCountryCode, $this->aCountryList); - } - - public function debugInfo() - { - return array( - 'Near radius' => $this->fNearRadius, - 'Near point (SQL)' => $this->sqlNear, - 'Bounded viewbox' => $this->bViewboxBounded, - 'Viewbox (SQL, small)' => $this->sqlViewboxSmall, - 'Viewbox (SQL, large)' => $this->sqlViewboxLarge, - 'Viewbox (SQL, centre)' => $this->sqlViewboxCentre, - 'Countries (SQL)' => $this->sqlCountryList, - 'Excluded IDs (SQL)' => $this->sqlExcludeList - ); - } -} diff --git a/lib-php/SearchDescription.php b/lib-php/SearchDescription.php deleted file mode 100644 index 5d2caf00..00000000 --- a/lib-php/SearchDescription.php +++ /dev/null @@ -1,985 +0,0 @@ -oContext = $oContext; - } - - /** - * Get current search rank. - * - * The higher the search rank the lower the likelihood that the - * search is a correct interpretation of the search query. - * - * @return integer Search rank. - */ - public function getRank() - { - return $this->iSearchRank; - } - - /** - * Extract key/value pairs from a query. - * - * Key/value pairs are recognised if they are of the form [=]. - * If multiple terms of this kind are found then all terms are removed - * but only the first is used for search. - * - * @param string $sQuery Original query string. - * - * @return string The query string with the special search patterns removed. - */ - public function extractKeyValuePairs($sQuery) - { - // Search for terms of kind [=]. - preg_match_all( - '/\\[([\\w_]*)=([\\w_]*)\\]/', - $sQuery, - $aSpecialTermsRaw, - PREG_SET_ORDER - ); - - foreach ($aSpecialTermsRaw as $aTerm) { - $sQuery = str_replace($aTerm[0], ' ', $sQuery); - if (!$this->hasOperator()) { - $this->setPoiSearch(Operator::TYPE, $aTerm[1], $aTerm[2]); - } - } - - return $sQuery; - } - - /** - * Check if the combination of parameters is sensible. - * - * @return bool True, if the search looks valid. - */ - public function isValidSearch() - { - if (empty($this->aName)) { - if ($this->sHouseNumber) { - return false; - } - if (!$this->sClass && !$this->sCountryCode) { - return false; - } - } - if ($this->bNameNeedsAddress && empty($this->aAddress)) { - return false; - } - - return true; - } - - /////////// Search building functions - - /** - * Create a copy of this search description adding to search rank. - * - * @param integer $iTermCost Cost to add to the current search rank. - * - * @return object Cloned search description. - */ - public function clone($iTermCost) - { - $oSearch = clone $this; - $oSearch->iSearchRank += $iTermCost; - - return $oSearch; - } - - /** - * Check if the search currently includes a name. - * - * @param bool bIncludeNonNames If true stop-word tokens are taken into - * account, too. - * - * @return bool True, if search has a name. - */ - public function hasName($bIncludeNonNames = false) - { - return !empty($this->aName) - || (!empty($this->aNameNonSearch) && $bIncludeNonNames); - } - - /** - * Check if the search currently includes an address term. - * - * @return bool True, if any address term is included, including stop-word - * terms. - */ - public function hasAddress() - { - return !empty($this->aAddress) || !empty($this->aAddressNonSearch); - } - - /** - * Check if a country restriction is currently included in the search. - * - * @return bool True, if a country restriction is set. - */ - public function hasCountry() - { - return $this->sCountryCode !== ''; - } - - /** - * Check if a postcode is currently included in the search. - * - * @return bool True, if a postcode is set. - */ - public function hasPostcode() - { - return $this->sPostcode !== ''; - } - - /** - * Check if a house number is set for the search. - * - * @return bool True, if a house number is set. - */ - public function hasHousenumber() - { - return $this->sHouseNumber !== ''; - } - - /** - * Check if a special type of place is requested. - * - * param integer iOperator When set, check for the particular - * operator used for the special type. - * - * @return bool True, if speial type is requested or, if requested, - * a special type with the given operator. - */ - public function hasOperator($iOperator = null) - { - return $iOperator === null ? $this->iOperator != Operator::NONE : $this->iOperator == $iOperator; - } - - /** - * Add the given token to the list of terms to search for in the address. - * - * @param integer iID ID of term to add. - * @param bool bSearchable Term should be used to search for result - * (i.e. term is not a stop word). - */ - public function addAddressToken($iId, $bSearchable = true) - { - if ($bSearchable) { - $this->aAddress[$iId] = $iId; - } else { - $this->aAddressNonSearch[$iId] = $iId; - } - } - - /** - * Add the given full-word token to the list of terms to search for in the - * name. - * - * @param integer iId ID of term to add. - * @param bool bRareName True if the term is infrequent enough to not - * require other constraints for efficient search. - */ - public function addNameToken($iId, $bRareName) - { - $this->aName[$iId] = $iId; - $this->bRareName = $bRareName; - $this->bNameNeedsAddress = false; - } - - /** - * Add the given partial token to the list of terms to search for in - * the name. - * - * @param integer iID ID of term to add. - * @param bool bSearchable Term should be used to search for result - * (i.e. term is not a stop word). - * @param bool bNeedsAddress True if the term is too unspecific to be used - * in a stand-alone search without an address - * to narrow down the search. - * @param integer iPhraseNumber Index of phrase, where the partial term - * appears. - */ - public function addPartialNameToken($iId, $bSearchable, $bNeedsAddress, $iPhraseNumber) - { - if (empty($this->aName)) { - $this->bNameNeedsAddress = $bNeedsAddress; - } elseif ($bSearchable && count($this->aName) >= 2) { - $this->bNameNeedsAddress = false; - } else { - $this->bNameNeedsAddress &= $bNeedsAddress; - } - if ($bSearchable) { - $this->aName[$iId] = $iId; - } else { - $this->aNameNonSearch[$iId] = $iId; - } - $this->iNamePhrase = $iPhraseNumber; - } - - /** - * Set country restriction for the search. - * - * @param string sCountryCode Country code of country to restrict search to. - */ - public function setCountry($sCountryCode) - { - $this->sCountryCode = $sCountryCode; - $this->iNamePhrase = -1; - } - - /** - * Set postcode search constraint. - * - * @param string sPostcode Postcode the result should have. - */ - public function setPostcode($sPostcode) - { - $this->sPostcode = $sPostcode; - $this->iNamePhrase = -1; - } - - /** - * Make this search a search for a postcode object. - * - * @param integer iId Token Id for the postcode. - * @param string sPostcode Postcode to look for. - */ - public function setPostcodeAsName($iId, $sPostcode) - { - $this->iOperator = Operator::POSTCODE; - $this->aAddress = array_merge($this->aAddress, $this->aName); - $this->aName = array($iId => $sPostcode); - $this->bRareName = true; - $this->iNamePhrase = -1; - } - - /** - * Set house number search cnstraint. - * - * @param string sNumber House number the result should have. - */ - public function setHousenumber($sNumber) - { - $this->sHouseNumber = $sNumber; - $this->iNamePhrase = -1; - } - - /** - * Make this search a search for a house number. - * - * @param integer iId Token Id for the house number. - */ - public function setHousenumberAsName($iId) - { - $this->aAddress = array_merge($this->aAddress, $this->aName); - $this->bRareName = false; - $this->bNameNeedsAddress = true; - $this->aName = array($iId => $iId); - $this->iNamePhrase = -1; - } - - /** - * Make this search a POI search. - * - * In a POI search, objects are not (only) searched by their name - * but also by the primary OSM key/value pair (class and type in Nominatim). - * - * @param integer $iOperator Type of POI search - * @param string $sClass Class (or OSM tag key) of POI. - * @param string $sType Type (or OSM tag value) of POI. - * - * @return void - */ - public function setPoiSearch($iOperator, $sClass, $sType) - { - $this->iOperator = $iOperator; - $this->sClass = $sClass; - $this->sType = $sType; - $this->iNamePhrase = -1; - } - - public function getNamePhrase() - { - return $this->iNamePhrase; - } - - /** - * Get the global search context. - * - * @return object Objects of global search constraints. - */ - public function getContext() - { - return $this->oContext; - } - - /////////// Query functions - - - /** - * Query database for places that match this search. - * - * @param object $oDB Nominatim::DB instance to use. - * @param integer $iMinRank Minimum address rank to restrict search to. - * @param integer $iMaxRank Maximum address rank to restrict search to. - * @param integer $iLimit Maximum number of results. - * - * @return mixed[] An array with two fields: IDs contains the list of - * matching place IDs and houseNumber the houseNumber - * if applicable or -1 if not. - */ - public function query(&$oDB, $iMinRank, $iMaxRank, $iLimit) - { - $aResults = array(); - - if ($this->sCountryCode - && empty($this->aName) - && !$this->iOperator - && !$this->sClass - && !$this->oContext->hasNearPoint() - ) { - // Just looking for a country - look it up - if (4 >= $iMinRank && 4 <= $iMaxRank) { - $aResults = $this->queryCountry($oDB); - } - } elseif (empty($this->aName) && empty($this->aAddress)) { - // Neither name nor address? Then we must be - // looking for a POI in a geographic area. - if ($this->oContext->isBoundedSearch()) { - $aResults = $this->queryNearbyPoi($oDB, $iLimit); - } - } elseif ($this->iOperator == Operator::POSTCODE) { - // looking for postcode - $aResults = $this->queryPostcode($oDB, $iLimit); - } else { - // Ordinary search: - // First search for places according to name and address. - $aResults = $this->queryNamedPlace( - $oDB, - $iMinRank, - $iMaxRank, - $iLimit - ); - - // finally get POIs if requested - if ($this->sClass && !empty($aResults)) { - $aResults = $this->queryPoiByOperator($oDB, $aResults, $iLimit); - } - } - - Debug::printDebugTable('Place IDs', $aResults); - - if (!empty($aResults) && $this->sPostcode) { - $sPlaceIds = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX); - if ($sPlaceIds) { - $sSQL = 'SELECT place_id FROM placex'; - $sSQL .= ' WHERE place_id in ('.$sPlaceIds.')'; - $sSQL .= " AND postcode != '".$this->sPostcode."'"; - Debug::printSQL($sSQL); - $aFilteredPlaceIDs = $oDB->getCol($sSQL); - if ($aFilteredPlaceIDs) { - foreach ($aFilteredPlaceIDs as $iPlaceId) { - $aResults[$iPlaceId]->iResultRank++; - } - } - } - } - - return $aResults; - } - - - private function queryCountry(&$oDB) - { - $sSQL = 'SELECT place_id FROM placex '; - $sSQL .= "WHERE country_code='".$this->sCountryCode."'"; - $sSQL .= ' AND rank_search = 4'; - if ($this->oContext->bViewboxBounded) { - $sSQL .= ' AND ST_Intersects('.$this->oContext->sqlViewboxSmall.', geometry)'; - } - $sSQL .= ' ORDER BY st_area(geometry) DESC LIMIT 1'; - - Debug::printSQL($sSQL); - - $iPlaceId = $oDB->getOne($sSQL); - - $aResults = array(); - if ($iPlaceId) { - $aResults[$iPlaceId] = new Result($iPlaceId); - } - - return $aResults; - } - - private function queryNearbyPoi(&$oDB, $iLimit) - { - if (!$this->sClass) { - return array(); - } - - $aDBResults = array(); - $sPoiTable = $this->poiTable(); - - if ($oDB->tableExists($sPoiTable)) { - $sSQL = 'SELECT place_id FROM '.$sPoiTable.' ct'; - if ($this->oContext->sqlCountryList) { - $sSQL .= ' JOIN placex USING (place_id)'; - } - if ($this->oContext->hasNearPoint()) { - $sSQL .= ' WHERE '.$this->oContext->withinSQL('ct.centroid'); - } elseif ($this->oContext->bViewboxBounded) { - $sSQL .= ' WHERE ST_Contains('.$this->oContext->sqlViewboxSmall.', ct.centroid)'; - } - if ($this->oContext->sqlCountryList) { - $sSQL .= ' AND country_code in '.$this->oContext->sqlCountryList; - } - $sSQL .= $this->oContext->excludeSQL(' AND place_id'); - if ($this->oContext->sqlViewboxCentre) { - $sSQL .= ' ORDER BY ST_Distance('; - $sSQL .= $this->oContext->sqlViewboxCentre.', ct.centroid) ASC'; - } elseif ($this->oContext->hasNearPoint()) { - $sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('ct.centroid').' ASC'; - } - $sSQL .= " LIMIT $iLimit"; - Debug::printSQL($sSQL); - $aDBResults = $oDB->getCol($sSQL); - } - - if ($this->oContext->hasNearPoint()) { - $sSQL = 'SELECT place_id FROM placex WHERE '; - $sSQL .= 'class = :class and type = :type'; - $sSQL .= ' AND '.$this->oContext->withinSQL('geometry'); - $sSQL .= ' AND linked_place_id is null'; - if ($this->oContext->sqlCountryList) { - $sSQL .= ' AND country_code in '.$this->oContext->sqlCountryList; - } - $sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('centroid').' ASC'; - $sSQL .= " LIMIT $iLimit"; - Debug::printSQL($sSQL); - $aDBResults = $oDB->getCol( - $sSQL, - array(':class' => $this->sClass, ':type' => $this->sType) - ); - } - - $aResults = array(); - foreach ($aDBResults as $iPlaceId) { - $aResults[$iPlaceId] = new Result($iPlaceId); - } - - return $aResults; - } - - private function queryPostcode(&$oDB, $iLimit) - { - $sSQL = 'SELECT p.place_id FROM location_postcode p '; - - if (!empty($this->aAddress)) { - $sSQL .= ', search_name s '; - $sSQL .= 'WHERE s.place_id = p.parent_place_id '; - $sSQL .= 'AND array_cat(s.nameaddress_vector, s.name_vector)'; - $sSQL .= ' @> '.$oDB->getArraySQL($this->aAddress).' AND '; - } else { - $sSQL .= 'WHERE '; - } - - $sSQL .= "p.postcode = '".reset($this->aName)."'"; - $sSQL .= $this->countryCodeSQL(' AND p.country_code'); - if ($this->oContext->bViewboxBounded) { - $sSQL .= ' AND ST_Intersects('.$this->oContext->sqlViewboxSmall.', geometry)'; - } - $sSQL .= $this->oContext->excludeSQL(' AND p.place_id'); - $sSQL .= " LIMIT $iLimit"; - - Debug::printSQL($sSQL); - - $aResults = array(); - foreach ($oDB->getCol($sSQL) as $iPlaceId) { - $aResults[$iPlaceId] = new Result($iPlaceId, Result::TABLE_POSTCODE); - } - - return $aResults; - } - - private function queryNamedPlace(&$oDB, $iMinAddressRank, $iMaxAddressRank, $iLimit) - { - $aTerms = array(); - $aOrder = array(); - - if (!empty($this->aName)) { - $aTerms[] = 'name_vector @> '.$oDB->getArraySQL($this->aName); - } - if (!empty($this->aAddress)) { - // For infrequent name terms disable index usage for address - if ($this->bRareName) { - $aTerms[] = 'array_cat(nameaddress_vector,ARRAY[]::integer[]) @> '.$oDB->getArraySQL($this->aAddress); - } else { - $aTerms[] = 'nameaddress_vector @> '.$oDB->getArraySQL($this->aAddress); - } - } - - $sCountryTerm = $this->countryCodeSQL('country_code'); - if ($sCountryTerm) { - $aTerms[] = $sCountryTerm; - } - - if ($this->sHouseNumber) { - $aTerms[] = 'address_rank between 16 and 30'; - } elseif (!$this->sClass || $this->iOperator == Operator::NAME) { - if ($iMinAddressRank > 0) { - $aTerms[] = "((address_rank between $iMinAddressRank and $iMaxAddressRank) or (search_rank between $iMinAddressRank and $iMaxAddressRank))"; - } - } - - if ($this->oContext->hasNearPoint()) { - $aTerms[] = $this->oContext->withinSQL('centroid'); - $aOrder[] = $this->oContext->distanceSQL('centroid'); - } elseif ($this->sPostcode) { - if (empty($this->aAddress)) { - $aTerms[] = "EXISTS(SELECT place_id FROM location_postcode p WHERE p.postcode = '".$this->sPostcode."' AND ST_DWithin(search_name.centroid, p.geometry, 0.12))"; - } else { - $aOrder[] = "(SELECT min(ST_Distance(search_name.centroid, p.geometry)) FROM location_postcode p WHERE p.postcode = '".$this->sPostcode."')"; - } - } - - $sExcludeSQL = $this->oContext->excludeSQL('place_id'); - if ($sExcludeSQL) { - $aTerms[] = $sExcludeSQL; - } - - if ($this->oContext->bViewboxBounded) { - $aTerms[] = 'centroid && '.$this->oContext->sqlViewboxSmall; - } - - if ($this->sHouseNumber) { - $sImportanceSQL = '- abs(26 - address_rank) + 3'; - } else { - $sImportanceSQL = '(CASE WHEN importance = 0 OR importance IS NULL THEN 0.75001-(search_rank::float/40) ELSE importance END)'; - } - $sImportanceSQL .= $this->oContext->viewboxImportanceSQL('centroid'); - $aOrder[] = "$sImportanceSQL DESC"; - - $aFullNameAddress = $this->oContext->getFullNameTerms(); - if (!empty($aFullNameAddress)) { - $sExactMatchSQL = ' ( '; - $sExactMatchSQL .= ' SELECT count(*) FROM ( '; - $sExactMatchSQL .= ' SELECT unnest('.$oDB->getArraySQL($aFullNameAddress).')'; - $sExactMatchSQL .= ' INTERSECT '; - $sExactMatchSQL .= ' SELECT unnest(nameaddress_vector)'; - $sExactMatchSQL .= ' ) s'; - $sExactMatchSQL .= ') as exactmatch'; - $aOrder[] = 'exactmatch DESC'; - } else { - $sExactMatchSQL = '0::int as exactmatch'; - } - - if (empty($aTerms)) { - return array(); - } - - if ($this->hasHousenumber()) { - $sHouseNumberRegex = $oDB->getDBQuoted('\\\\m'.$this->sHouseNumber.'\\\\M'); - - // Housenumbers on streets and places. - $sPlacexSql = 'SELECT array_agg(place_id) FROM placex'; - $sPlacexSql .= ' WHERE parent_place_id = sin.place_id AND sin.address_rank < 30'; - $sPlacexSql .= $this->oContext->excludeSQL(' AND place_id'); - $sPlacexSql .= ' and housenumber ~* E'.$sHouseNumberRegex; - - // Interpolations on streets and places. - $sInterpolSql = 'null'; - $sTigerSql = 'null'; - if (preg_match('/^[0-9]+$/', $this->sHouseNumber)) { - $sIpolHnr = 'WHERE parent_place_id = sin.place_id '; - $sIpolHnr .= ' AND startnumber is not NULL AND sin.address_rank < 30'; - $sIpolHnr .= ' AND '.$this->sHouseNumber.' between startnumber and endnumber'; - $sIpolHnr .= ' AND ('.$this->sHouseNumber.' - startnumber) % step = 0'; - - $sInterpolSql = 'SELECT array_agg(place_id) FROM location_property_osmline '.$sIpolHnr; - if (CONST_Use_US_Tiger_Data) { - $sTigerSql = 'SELECT array_agg(place_id) FROM location_property_tiger '.$sIpolHnr; - $sTigerSql .= " and sin.country_code = 'us'"; - } - } - - if ($this->sClass) { - $iLimit = 40; - } - - $sSelfHnr = 'SELECT * FROM placex WHERE place_id = search_name.place_id'; - $sSelfHnr .= ' AND housenumber ~* E'.$sHouseNumberRegex; - - $aTerms[] = '(address_rank < 30 or exists('.$sSelfHnr.'))'; - - - $sSQL = 'SELECT sin.*, '; - $sSQL .= '('.$sPlacexSql.') as placex_hnr, '; - $sSQL .= '('.$sInterpolSql.') as interpol_hnr, '; - $sSQL .= '('.$sTigerSql.') as tiger_hnr '; - $sSQL .= ' FROM ('; - $sSQL .= ' SELECT place_id, address_rank, country_code,'.$sExactMatchSQL.','; - $sSQL .= ' CASE WHEN importance = 0 OR importance IS NULL'; - $sSQL .= ' THEN 0.75001-(search_rank::float/40) ELSE importance END as importance'; - $sSQL .= ' FROM search_name'; - $sSQL .= ' WHERE '.join(' and ', $aTerms); - $sSQL .= ' ORDER BY '.join(', ', $aOrder); - $sSQL .= ' LIMIT 40000'; - $sSQL .= ') as sin'; - $sSQL .= ' ORDER BY address_rank = 30 desc, placex_hnr, interpol_hnr, tiger_hnr,'; - $sSQL .= ' importance'; - $sSQL .= ' LIMIT '.$iLimit; - } else { - if ($this->sClass) { - $iLimit = 40; - } - - $sSQL = 'SELECT place_id, address_rank, '.$sExactMatchSQL; - $sSQL .= ' FROM search_name'; - $sSQL .= ' WHERE '.join(' and ', $aTerms); - $sSQL .= ' ORDER BY '.join(', ', $aOrder); - $sSQL .= ' LIMIT '.$iLimit; - } - - Debug::printSQL($sSQL); - - $aDBResults = $oDB->getAll($sSQL, null, 'Could not get places for search terms.'); - - $aResults = array(); - - foreach ($aDBResults as $aResult) { - $oResult = new Result($aResult['place_id']); - $oResult->iExactMatches = $aResult['exactmatch']; - $oResult->iAddressRank = $aResult['address_rank']; - - $bNeedResult = true; - if ($this->hasHousenumber() && $aResult['address_rank'] < 30) { - if ($aResult['placex_hnr']) { - foreach (explode(',', substr($aResult['placex_hnr'], 1, -1)) as $sPlaceID) { - $iPlaceID = intval($sPlaceID); - $oHnrResult = new Result($iPlaceID); - $oHnrResult->iExactMatches = $aResult['exactmatch']; - $oHnrResult->iAddressRank = 30; - $aResults[$iPlaceID] = $oHnrResult; - $bNeedResult = false; - } - } - if ($aResult['interpol_hnr']) { - foreach (explode(',', substr($aResult['interpol_hnr'], 1, -1)) as $sPlaceID) { - $iPlaceID = intval($sPlaceID); - $oHnrResult = new Result($iPlaceID, Result::TABLE_OSMLINE); - $oHnrResult->iExactMatches = $aResult['exactmatch']; - $oHnrResult->iAddressRank = 30; - $oHnrResult->iHouseNumber = intval($this->sHouseNumber); - $aResults[$iPlaceID] = $oHnrResult; - $bNeedResult = false; - } - } - if ($aResult['tiger_hnr']) { - foreach (explode(',', substr($aResult['tiger_hnr'], 1, -1)) as $sPlaceID) { - $iPlaceID = intval($sPlaceID); - $oHnrResult = new Result($iPlaceID, Result::TABLE_TIGER); - $oHnrResult->iExactMatches = $aResult['exactmatch']; - $oHnrResult->iAddressRank = 30; - $oHnrResult->iHouseNumber = intval($this->sHouseNumber); - $aResults[$iPlaceID] = $oHnrResult; - $bNeedResult = false; - } - } - - if ($aResult['address_rank'] < 26) { - $oResult->iResultRank += 2; - } else { - $oResult->iResultRank++; - } - } - - if ($bNeedResult) { - $aResults[$aResult['place_id']] = $oResult; - } - } - - return $aResults; - } - - - private function queryPoiByOperator(&$oDB, $aParentIDs, $iLimit) - { - $aResults = array(); - $sPlaceIDs = Result::joinIdsByTable($aParentIDs, Result::TABLE_PLACEX); - - if (!$sPlaceIDs) { - return $aResults; - } - - if ($this->iOperator == Operator::TYPE || $this->iOperator == Operator::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 '; - $sSQL .= " WHERE place_id in ($sPlaceIDs)"; - $sSQL .= " AND class='".$this->sClass."' "; - $sSQL .= " AND type='".$this->sType."'"; - $sSQL .= ' AND linked_place_id is null'; - $sSQL .= $this->oContext->excludeSQL(' AND place_id'); - $sSQL .= ' ORDER BY rank_search ASC '; - $sSQL .= " LIMIT $iLimit"; - - Debug::printSQL($sSQL); - - foreach ($oDB->getCol($sSQL) as $iPlaceId) { - $aResults[$iPlaceId] = new Result($iPlaceId); - } - } - - // NEAR and IN are handled the same - if ($this->iOperator == Operator::TYPE || $this->iOperator == Operator::NEAR) { - $sClassTable = $this->poiTable(); - $bCacheTable = $oDB->tableExists($sClassTable); - - $sSQL = "SELECT min(rank_search) FROM placex WHERE place_id in ($sPlaceIDs)"; - Debug::printSQL($sSQL); - $iMaxRank = (int) $oDB->getOne($sSQL); - - // For state / country level searches the normal radius search doesn't work very well - $sPlaceGeom = false; - if ($iMaxRank < 9 && $bCacheTable) { - // Try and get a polygon to search in instead - $sSQL = 'SELECT geometry FROM placex'; - $sSQL .= " WHERE place_id in ($sPlaceIDs)"; - $sSQL .= " AND rank_search < $iMaxRank + 5"; - $sSQL .= ' AND ST_Area(Box2d(geometry)) < 20'; - $sSQL .= " AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon')"; - $sSQL .= ' ORDER BY rank_search ASC '; - $sSQL .= ' LIMIT 1'; - Debug::printSQL($sSQL); - $sPlaceGeom = $oDB->getOne($sSQL); - } - - if ($sPlaceGeom) { - $sPlaceIDs = false; - } else { - $iMaxRank += 5; - $sSQL = 'SELECT place_id FROM placex'; - $sSQL .= " WHERE place_id in ($sPlaceIDs) and rank_search < $iMaxRank"; - Debug::printSQL($sSQL); - $aPlaceIDs = $oDB->getCol($sSQL); - $sPlaceIDs = join(',', $aPlaceIDs); - } - - if ($sPlaceIDs || $sPlaceGeom) { - $fRange = 0.01; - if ($bCacheTable) { - // More efficient - can make the range bigger - $fRange = 0.05; - - $sOrderBySQL = ''; - if ($this->oContext->hasNearPoint()) { - $sOrderBySQL = $this->oContext->distanceSQL('l.centroid'); - } elseif ($sPlaceIDs) { - $sOrderBySQL = 'ST_Distance(l.centroid, f.geometry)'; - } elseif ($sPlaceGeom) { - $sOrderBySQL = "ST_Distance(st_centroid('".$sPlaceGeom."'), l.centroid)"; - } - - $sSQL = 'SELECT distinct i.place_id'; - if ($sOrderBySQL) { - $sSQL .= ', i.order_term'; - } - $sSQL .= ' from (SELECT l.place_id'; - if ($sOrderBySQL) { - $sSQL .= ','.$sOrderBySQL.' as order_term'; - } - $sSQL .= ' from '.$sClassTable.' as l'; - - if ($sPlaceIDs) { - $sSQL .= ',placex as f WHERE '; - $sSQL .= "f.place_id in ($sPlaceIDs) "; - $sSQL .= " AND ST_DWithin(l.centroid, f.centroid, $fRange)"; - } elseif ($sPlaceGeom) { - $sSQL .= " WHERE ST_Contains('$sPlaceGeom', l.centroid)"; - } - - $sSQL .= $this->oContext->excludeSQL(' AND l.place_id'); - $sSQL .= 'limit 300) i '; - if ($sOrderBySQL) { - $sSQL .= 'order by order_term asc'; - } - $sSQL .= " limit $iLimit"; - - Debug::printSQL($sSQL); - - foreach ($oDB->getCol($sSQL) as $iPlaceId) { - $aResults[$iPlaceId] = new Result($iPlaceId); - } - } else { - if ($this->oContext->hasNearPoint()) { - $fRange = $this->oContext->nearRadius(); - } - - $sOrderBySQL = ''; - if ($this->oContext->hasNearPoint()) { - $sOrderBySQL = $this->oContext->distanceSQL('l.geometry'); - } else { - $sOrderBySQL = 'ST_Distance(l.geometry, f.geometry)'; - } - - $sSQL = 'SELECT distinct l.place_id'; - if ($sOrderBySQL) { - $sSQL .= ','.$sOrderBySQL.' as orderterm'; - } - $sSQL .= ' FROM placex as l, placex as f'; - $sSQL .= " WHERE f.place_id in ($sPlaceIDs)"; - $sSQL .= " AND ST_DWithin(l.geometry, f.centroid, $fRange)"; - $sSQL .= " AND l.class='".$this->sClass."'"; - $sSQL .= " AND l.type='".$this->sType."'"; - $sSQL .= $this->oContext->excludeSQL(' AND l.place_id'); - if ($sOrderBySQL) { - $sSQL .= 'ORDER BY orderterm ASC'; - } - $sSQL .= " limit $iLimit"; - - Debug::printSQL($sSQL); - - foreach ($oDB->getCol($sSQL) as $iPlaceId) { - $aResults[$iPlaceId] = new Result($iPlaceId); - } - } - } - } - - return $aResults; - } - - private function poiTable() - { - return 'place_classtype_'.$this->sClass.'_'.$this->sType; - } - - private function countryCodeSQL($sVar) - { - if ($this->sCountryCode) { - return $sVar.' = \''.$this->sCountryCode."'"; - } - if ($this->oContext->sqlCountryList) { - return $sVar.' in '.$this->oContext->sqlCountryList; - } - - return ''; - } - - /////////// Sort functions - - - public static function bySearchRank($a, $b) - { - if ($a->iSearchRank == $b->iSearchRank) { - return $a->iOperator + strlen($a->sHouseNumber) - - $b->iOperator - strlen($b->sHouseNumber); - } - - return $a->iSearchRank < $b->iSearchRank ? -1 : 1; - } - - //////////// Debugging functions - - - public function debugInfo() - { - return array( - 'Search rank' => $this->iSearchRank, - 'Country code' => $this->sCountryCode, - 'Name terms' => $this->aName, - 'Name terms (stop words)' => $this->aNameNonSearch, - 'Address terms' => $this->aAddress, - 'Address terms (stop words)' => $this->aAddressNonSearch, - 'Address terms (full words)' => $this->aFullNameAddress ?? '', - 'Special search' => $this->iOperator, - 'Class' => $this->sClass, - 'Type' => $this->sType, - 'House number' => $this->sHouseNumber, - 'Postcode' => $this->sPostcode - ); - } - - public function dumpAsHtmlTableRow(&$aWordIDs) - { - $kf = function ($k) use (&$aWordIDs) { - return $aWordIDs[$k] ?? '['.$k.']'; - }; - - echo ''; - echo "$this->iSearchRank"; - echo ''.join(', ', array_map($kf, $this->aName)).''; - echo ''.join(', ', array_map($kf, $this->aNameNonSearch)).''; - echo ''.join(', ', array_map($kf, $this->aAddress)).''; - echo ''.join(', ', array_map($kf, $this->aAddressNonSearch)).''; - echo ''.$this->sCountryCode.''; - echo ''.Operator::toString($this->iOperator).''; - echo ''.$this->sClass.''; - echo ''.$this->sType.''; - echo ''.$this->sPostcode.''; - echo ''.$this->sHouseNumber.''; - - echo ''; - } -} diff --git a/lib-php/SearchPosition.php b/lib-php/SearchPosition.php deleted file mode 100644 index aeeeb2c3..00000000 --- a/lib-php/SearchPosition.php +++ /dev/null @@ -1,95 +0,0 @@ -sPhraseType = $sPhraseType; - $this->iPhrase = $iPhrase; - $this->iNumPhrases = $iNumPhrases; - } - - public function setTokenPosition($iToken, $iNumTokens) - { - $this->iToken = $iToken; - $this->iNumTokens = $iNumTokens; - } - - /** - * Check if the phrase can be of the given type. - * - * @param string $sType Type of phrse requested. - * - * @return True if the phrase is untyped or of the given type. - */ - public function maybePhrase($sType) - { - return $this->sPhraseType == '' || $this->sPhraseType == $sType; - } - - /** - * Check if the phrase is exactly of the given type. - * - * @param string $sType Type of phrse requested. - * - * @return True if the phrase of the given type. - */ - public function isPhrase($sType) - { - return $this->sPhraseType == $sType; - } - - /** - * Return true if the token is the very first in the query. - */ - public function isFirstToken() - { - return $this->iPhrase == 0 && $this->iToken == 0; - } - - /** - * Check if the token is the final one in the query. - */ - public function isLastToken() - { - return $this->iToken + 1 == $this->iNumTokens && $this->iPhrase + 1 == $this->iNumPhrases; - } - - /** - * Check if the current token is part of the first phrase in the query. - */ - public function isFirstPhrase() - { - return $this->iPhrase == 0; - } - - /** - * Get the phrase position in the query. - */ - public function getPhrase() - { - return $this->iPhrase; - } -} diff --git a/lib-php/Shell.php b/lib-php/Shell.php deleted file mode 100644 index 4be13235..00000000 --- a/lib-php/Shell.php +++ /dev/null @@ -1,92 +0,0 @@ -baseCmd = $sBaseCmd; - $this->aParams = array(); - $this->aEnv = null; // null = use the same environment as the current PHP process - - $this->stdoutString = null; - - foreach ($aParams as $sParam) { - $this->addParams($sParam); - } - } - - public function addParams(...$aParams) - { - foreach ($aParams as $sParam) { - if (isset($sParam) && $sParam !== null && $sParam !== '') { - array_push($this->aParams, $sParam); - } - } - return $this; - } - - public function addEnvPair($sKey, $sVal) - { - if (isset($sKey) && $sKey && isset($sVal)) { - if (!isset($this->aEnv)) { - $this->aEnv = $_ENV; - } - $this->aEnv = array_merge($this->aEnv, array($sKey => $sVal), $_ENV); - } - return $this; - } - - public function escapedCmd() - { - $aEscaped = array_map(function ($sParam) { - return $this->escapeParam($sParam); - }, array_merge(array($this->baseCmd), $this->aParams)); - - return join(' ', $aEscaped); - } - - public function run($bExitOnFail = false) - { - $sCmd = $this->escapedCmd(); - // $aEnv does not need escaping, proc_open seems to handle it fine - - $aFDs = array( - 0 => array('pipe', 'r'), - 1 => STDOUT, - 2 => STDERR - ); - $aPipes = null; - $hProc = @proc_open($sCmd, $aFDs, $aPipes, null, $this->aEnv); - if (!is_resource($hProc)) { - throw new \Exception('Unable to run command: ' . $sCmd); - } - - fclose($aPipes[0]); // no stdin - - $iStat = proc_close($hProc); - - if ($iStat != 0 && $bExitOnFail) { - exit($iStat); - } - - return $iStat; - } - - private function escapeParam($sParam) - { - return (preg_match('/^-*\w+$/', $sParam)) ? $sParam : escapeshellarg($sParam); - } -} diff --git a/lib-php/SimpleWordList.php b/lib-php/SimpleWordList.php deleted file mode 100644 index 7009d370..00000000 --- a/lib-php/SimpleWordList.php +++ /dev/null @@ -1,144 +0,0 @@ - 0) { - $this->aWords = explode(' ', $sPhrase); - } else { - $this->aWords = array(); - } - } - - /** - * Get all possible tokens that are present in this word list. - * - * @return array The list of string tokens in the word list. - */ - public function getTokens() - { - $aTokens = array(); - $iNumWords = count($this->aWords); - - for ($i = 0; $i < $iNumWords; $i++) { - $sPhrase = $this->aWords[$i]; - $aTokens[$sPhrase] = $sPhrase; - - for ($j = $i + 1; $j < $iNumWords; $j++) { - $sPhrase .= ' '.$this->aWords[$j]; - $aTokens[$sPhrase] = $sPhrase; - } - } - - return $aTokens; - } - - /** - * Compute all possible permutations of phrase splits that result in - * words which are in the token list. - */ - public function getWordSets($oTokens) - { - $iNumWords = count($this->aWords); - - if ($iNumWords == 0) { - return null; - } - - // Caches the word set for the partial phrase up to word i. - $aSetCache = array_fill(0, $iNumWords, array()); - - // Initialise first element of cache. There can only be the word. - if ($oTokens->containsAny($this->aWords[0])) { - $aSetCache[0][] = array($this->aWords[0]); - } - - // Now do the next elements using what we already have. - for ($i = 1; $i < $iNumWords; $i++) { - for ($j = $i; $j > 0; $j--) { - $sPartial = $j == $i ? $this->aWords[$j] : $this->aWords[$j].' '.$sPartial; - if (!empty($aSetCache[$j - 1]) && $oTokens->containsAny($sPartial)) { - $aPartial = array($sPartial); - foreach ($aSetCache[$j - 1] as $aSet) { - if (count($aSet) < SimpleWordList::MAX_WORDSET_LEN) { - $aSetCache[$i][] = array_merge($aSet, $aPartial); - } - } - if (count($aSetCache[$i]) > 2 * SimpleWordList::MAX_WORDSETS) { - usort( - $aSetCache[$i], - array('\Nominatim\SimpleWordList', 'cmpByArraylen') - ); - $aSetCache[$i] = array_slice( - $aSetCache[$i], - 0, - SimpleWordList::MAX_WORDSETS - ); - } - } - } - - // finally the current full phrase - $sPartial = $this->aWords[0].' '.$sPartial; - if ($oTokens->containsAny($sPartial)) { - $aSetCache[$i][] = array($sPartial); - } - } - - $aWordSets = $aSetCache[$iNumWords - 1]; - usort($aWordSets, array('\Nominatim\SimpleWordList', 'cmpByArraylen')); - return array_slice($aWordSets, 0, SimpleWordList::MAX_WORDSETS); - } - - /** - * Custom search routine which takes two arrays. The array with the fewest - * items wins. If same number of items then the one with the longest first - * element wins. - */ - public static function cmpByArraylen($aA, $aB) - { - $iALen = count($aA); - $iBLen = count($aB); - - if ($iALen == $iBLen) { - return strlen($aB[0]) <=> strlen($aA[0]); - } - - return ($iALen < $iBLen) ? -1 : 1; - } - - public function debugInfo() - { - return $this->aWords; - } -} diff --git a/lib-php/SpecialSearchOperator.php b/lib-php/SpecialSearchOperator.php deleted file mode 100644 index 94df59ea..00000000 --- a/lib-php/SpecialSearchOperator.php +++ /dev/null @@ -1,52 +0,0 @@ -getConstants(); - - Operator::$aConstantNames = array(); - foreach ($aConstants as $sName => $iValue) { - Operator::$aConstantNames[$iValue] = $sName; - } - } - - return Operator::$aConstantNames[$iOperator]; - } -} diff --git a/lib-php/Status.php b/lib-php/Status.php deleted file mode 100644 index 4f1555cd..00000000 --- a/lib-php/Status.php +++ /dev/null @@ -1,59 +0,0 @@ -oDB =& $oDB; - } - - public function status() - { - if (!$this->oDB) { - throw new Exception('No database', 700); - } - - try { - $this->oDB->connect(); - } catch (\Nominatim\DatabaseError $e) { - throw new Exception('Database connection failed', 700); - } - - $oTokenizer = new \Nominatim\Tokenizer($this->oDB); - $oTokenizer->checkStatus(); - } - - public function dataDate() - { - $sSQL = 'SELECT EXTRACT(EPOCH FROM lastimportdate) FROM import_status LIMIT 1'; - $iDataDateEpoch = $this->oDB->getOne($sSQL); - - if ($iDataDateEpoch === false) { - throw new Exception('Import date is not available', 705); - } - - return $iDataDateEpoch; - } - - public function databaseVersion() - { - $sSQL = 'SELECT value FROM nominatim_properties WHERE property = \'database_version\''; - return $this->oDB->getOne($sSQL); - } -} diff --git a/lib-php/TokenCountry.php b/lib-php/TokenCountry.php deleted file mode 100644 index 3f93f45e..00000000 --- a/lib-php/TokenCountry.php +++ /dev/null @@ -1,82 +0,0 @@ -iId = $iId; - $this->sCountryCode = $sCountryCode; - } - - public function getId() - { - return $this->iId; - } - - /** - * Check if the token can be added to the given search. - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return True if the token is compatible with the search configuration - * given the position. - */ - public function isExtendable($oSearch, $oPosition) - { - return !$oSearch->hasCountry() - && $oPosition->maybePhrase('country') - && $oSearch->getContext()->isCountryApplicable($this->sCountryCode); - } - - /** - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return SearchDescription[] List of derived search descriptions. - */ - public function extendSearch($oSearch, $oPosition) - { - $oNewSearch = $oSearch->clone($oPosition->isLastToken() ? 1 : 6); - $oNewSearch->setCountry($this->sCountryCode); - - return array($oNewSearch); - } - - public function debugInfo() - { - return array( - 'ID' => $this->iId, - 'Type' => 'country', - 'Info' => $this->sCountryCode - ); - } - - public function debugCode() - { - return 'C'; - } -} diff --git a/lib-php/TokenHousenumber.php b/lib-php/TokenHousenumber.php deleted file mode 100644 index 62c2a624..00000000 --- a/lib-php/TokenHousenumber.php +++ /dev/null @@ -1,116 +0,0 @@ -iId = $iId; - $this->sToken = $sToken; - } - - public function getId() - { - return $this->iId; - } - - /** - * Check if the token can be added to the given search. - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return True if the token is compatible with the search configuration - * given the position. - */ - public function isExtendable($oSearch, $oPosition) - { - return !$oSearch->hasHousenumber() - && !$oSearch->hasOperator(\Nominatim\Operator::POSTCODE) - && $oPosition->maybePhrase('street'); - } - - /** - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return SearchDescription[] List of derived search descriptions. - */ - public function extendSearch($oSearch, $oPosition) - { - $aNewSearches = array(); - - // sanity check: if the housenumber is not mainly made - // up of numbers, add a penalty - $iSearchCost = 1; - if (preg_match('/\\d/', $this->sToken) === 0 - || preg_match_all('/[^0-9 ]/', $this->sToken, $aMatches) > 3) { - $iSearchCost += strlen($this->sToken) - 1; - } - if (!$oSearch->hasOperator(\Nominatim\Operator::NONE)) { - $iSearchCost++; - } - if (empty($this->iId)) { - $iSearchCost++; - } - // also must not appear in the middle of the address - if ($oSearch->hasAddress() || $oSearch->hasPostcode()) { - $iSearchCost++; - } - - $oNewSearch = $oSearch->clone($iSearchCost); - $oNewSearch->setHousenumber($this->sToken); - $aNewSearches[] = $oNewSearch; - - // Housenumbers may appear in the name when the place has its own - // address terms. - if ($this->iId !== null - && ($oSearch->getNamePhrase() >= 0 || !$oSearch->hasName()) - && !$oSearch->hasAddress() - ) { - $oNewSearch = $oSearch->clone($iSearchCost); - $oNewSearch->setHousenumberAsName($this->iId); - - $aNewSearches[] = $oNewSearch; - } - - return $aNewSearches; - } - - - public function debugInfo() - { - return array( - 'ID' => $this->iId, - 'Type' => 'house number', - 'Info' => array('nr' => $this->sToken) - ); - } - - public function debugCode() - { - return 'H'; - } -} diff --git a/lib-php/TokenList.php b/lib-php/TokenList.php deleted file mode 100644 index 9a3950a1..00000000 --- a/lib-php/TokenList.php +++ /dev/null @@ -1,134 +0,0 @@ -aTokens); - } - - /** - * Check if there are tokens for the given token word. - * - * @param string $sWord Token word to look for. - * - * @return bool True if there is one or more token for the token word. - */ - public function contains($sWord) - { - return isset($this->aTokens[$sWord]); - } - - /** - * Check if there are partial or full tokens for the given word. - * - * @param string $sWord Token word to look for. - * - * @return bool True if there is one or more token for the token word. - */ - public function containsAny($sWord) - { - return isset($this->aTokens[$sWord]); - } - - /** - * Get the list of tokens for the given token word. - * - * @param string $sWord Token word to look for. - * - * @return object[] Array of tokens for the given token word or an - * empty array if no tokens could be found. - */ - public function get($sWord) - { - return isset($this->aTokens[$sWord]) ? $this->aTokens[$sWord] : array(); - } - - public function getFullWordIDs() - { - $ids = array(); - - foreach ($this->aTokens as $aTokenList) { - foreach ($aTokenList as $oToken) { - if (is_a($oToken, '\Nominatim\Token\Word')) { - $ids[$oToken->getId()] = $oToken->getId(); - } - } - } - - return $ids; - } - - /** - * Add a new token for the given word. - * - * @param string $sWord Word the token describes. - * @param object $oToken Token object to add. - * - * @return void - */ - public function addToken($sWord, $oToken) - { - if (isset($this->aTokens[$sWord])) { - $this->aTokens[$sWord][] = $oToken; - } else { - $this->aTokens[$sWord] = array($oToken); - } - } - - public function debugTokenByWordIdList() - { - $aWordsIDs = array(); - foreach ($this->aTokens as $sToken => $aWords) { - foreach ($aWords as $aToken) { - $iId = $aToken->getId(); - if ($iId !== null) { - $aWordsIDs[$iId] = '#'.$sToken.'('.$aToken->debugCode().' '.$iId.')#'; - } - } - } - - return $aWordsIDs; - } - - public function debugInfo() - { - return $this->aTokens; - } -} diff --git a/lib-php/TokenPartial.php b/lib-php/TokenPartial.php deleted file mode 100644 index 3dc6f308..00000000 --- a/lib-php/TokenPartial.php +++ /dev/null @@ -1,127 +0,0 @@ -iId = $iId; - $this->bNumberToken = (bool) preg_match('#^[0-9 ]+$#', $sToken); - $this->iSearchNameCount = $iSearchNameCount; - } - - public function getId() - { - return $this->iId; - } - - /** - * Check if the token can be added to the given search. - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return True if the token is compatible with the search configuration - * given the position. - */ - public function isExtendable($oSearch, $oPosition) - { - return !$oPosition->isPhrase('country'); - } - - /** - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return SearchDescription[] List of derived search descriptions. - */ - public function extendSearch($oSearch, $oPosition) - { - $aNewSearches = array(); - - // Partial token in Address. - if (($oPosition->isPhrase('') || !$oPosition->isFirstPhrase()) - && $oSearch->hasName() - ) { - $iSearchCost = $this->bNumberToken ? 2 : 1; - if ($this->iSearchNameCount >= CONST_Max_Word_Frequency) { - $iSearchCost += 1; - } - - $oNewSearch = $oSearch->clone($iSearchCost); - $oNewSearch->addAddressToken( - $this->iId, - $this->iSearchNameCount < CONST_Max_Word_Frequency - ); - - $aNewSearches[] = $oNewSearch; - } - - // Partial token in Name. - if ((!$oSearch->hasPostcode() && !$oSearch->hasAddress()) - && (!$oSearch->hasName(true) - || $oSearch->getNamePhrase() == $oPosition->getPhrase()) - ) { - $iSearchCost = 1; - if (!$oSearch->hasName(true)) { - $iSearchCost += 1; - } - if ($this->bNumberToken) { - $iSearchCost += 1; - } - - $oNewSearch = $oSearch->clone($iSearchCost); - $oNewSearch->addPartialNameToken( - $this->iId, - $this->iSearchNameCount < CONST_Max_Word_Frequency, - $this->iSearchNameCount > CONST_Search_NameOnlySearchFrequencyThreshold, - $oPosition->getPhrase() - ); - - $aNewSearches[] = $oNewSearch; - } - - return $aNewSearches; - } - - - public function debugInfo() - { - return array( - 'ID' => $this->iId, - 'Type' => 'partial', - 'Info' => array( - 'count' => $this->iSearchNameCount - ) - ); - } - - public function debugCode() - { - return 'w'; - } -} diff --git a/lib-php/TokenPostcode.php b/lib-php/TokenPostcode.php deleted file mode 100644 index 0ff92929..00000000 --- a/lib-php/TokenPostcode.php +++ /dev/null @@ -1,111 +0,0 @@ -iId = $iId; - $iSplitPos = strpos($sPostcode, '@'); - if ($iSplitPos === false) { - $this->sPostcode = $sPostcode; - } else { - $this->sPostcode = substr($sPostcode, 0, $iSplitPos); - } - $this->sCountryCode = empty($sCountryCode) ? '' : $sCountryCode; - } - - public function getId() - { - return $this->iId; - } - - /** - * Check if the token can be added to the given search. - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return True if the token is compatible with the search configuration - * given the position. - */ - public function isExtendable($oSearch, $oPosition) - { - return !$oSearch->hasPostcode() && $oPosition->maybePhrase('postalcode'); - } - - /** - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return SearchDescription[] List of derived search descriptions. - */ - public function extendSearch($oSearch, $oPosition) - { - $aNewSearches = array(); - - // If we have structured search or this is the first term, - // make the postcode the primary search element. - if ($oSearch->hasOperator(\Nominatim\Operator::NONE) && $oPosition->isFirstToken()) { - $oNewSearch = $oSearch->clone(1); - $oNewSearch->setPostcodeAsName($this->iId, $this->sPostcode); - - $aNewSearches[] = $oNewSearch; - } - - // If we have a structured search or this is not the first term, - // add the postcode as an addendum. - if (!$oSearch->hasOperator(\Nominatim\Operator::POSTCODE) - && ($oPosition->isPhrase('postalcode') || $oSearch->hasName()) - ) { - $iPenalty = 1; - if (strlen($this->sPostcode) < 4) { - $iPenalty += 4 - strlen($this->sPostcode); - } - $oNewSearch = $oSearch->clone($iPenalty); - $oNewSearch->setPostcode($this->sPostcode); - - $aNewSearches[] = $oNewSearch; - } - - return $aNewSearches; - } - - public function debugInfo() - { - return array( - 'ID' => $this->iId, - 'Type' => 'postcode', - 'Info' => $this->sPostcode.'('.$this->sCountryCode.')' - ); - } - - public function debugCode() - { - return 'P'; - } -} diff --git a/lib-php/TokenSpecialTerm.php b/lib-php/TokenSpecialTerm.php deleted file mode 100644 index 475ae71b..00000000 --- a/lib-php/TokenSpecialTerm.php +++ /dev/null @@ -1,125 +0,0 @@ -iId = $iID; - $this->sClass = $sClass; - $this->sType = $sType; - $this->iOperator = $iOperator; - } - - public function getId() - { - return $this->iId; - } - - /** - * Check if the token can be added to the given search. - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return True if the token is compatible with the search configuration - * given the position. - */ - public function isExtendable($oSearch, $oPosition) - { - return !$oSearch->hasOperator() - && $oPosition->isPhrase('') - && ($this->iOperator != \Nominatim\Operator::NONE - || (!$oSearch->hasAddress() && !$oSearch->hasHousenumber() && !$oSearch->hasCountry())); - } - - /** - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return SearchDescription[] List of derived search descriptions. - */ - public function extendSearch($oSearch, $oPosition) - { - $iSearchCost = 0; - - $iOp = $this->iOperator; - if ($iOp == \Nominatim\Operator::NONE) { - if ($oPosition->isFirstToken() - || $oSearch->hasName() - || $oSearch->getContext()->isBoundedSearch() - ) { - $iOp = \Nominatim\Operator::NAME; - $iSearchCost += 3; - } else { - $iOp = \Nominatim\Operator::NEAR; - $iSearchCost += 4; - if (!$oPosition->isFirstToken()) { - $iSearchCost += 3; - } - } - } elseif ($oPosition->isFirstToken()) { - $iSearchCost += 2; - } elseif ($oPosition->isLastToken()) { - $iSearchCost += 4; - } else { - $iSearchCost += 6; - } - - if ($oSearch->hasHousenumber()) { - $iSearchCost ++; - } - - $oNewSearch = $oSearch->clone($iSearchCost); - $oNewSearch->setPoiSearch($iOp, $this->sClass, $this->sType); - - return array($oNewSearch); - } - - - public function debugInfo() - { - return array( - 'ID' => $this->iId, - 'Type' => 'special term', - 'Info' => array( - 'class' => $this->sClass, - 'type' => $this->sType, - 'operator' => \Nominatim\Operator::toString($this->iOperator) - ) - ); - } - - public function debugCode() - { - return 'S'; - } -} diff --git a/lib-php/TokenWord.php b/lib-php/TokenWord.php deleted file mode 100644 index a7557d38..00000000 --- a/lib-php/TokenWord.php +++ /dev/null @@ -1,110 +0,0 @@ -iId = $iId; - $this->iSearchNameCount = $iSearchNameCount; - $this->iTermCount = $iTermCount; - } - - public function getId() - { - return $this->iId; - } - - /** - * Check if the token can be added to the given search. - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return True if the token is compatible with the search configuration - * given the position. - */ - public function isExtendable($oSearch, $oPosition) - { - return !$oPosition->isPhrase('country'); - } - - /** - * Derive new searches by adding this token to an existing search. - * - * @param object $oSearch Partial search description derived so far. - * @param object $oPosition Description of the token position within - the query. - * - * @return SearchDescription[] List of derived search descriptions. - */ - public function extendSearch($oSearch, $oPosition) - { - // Full words can only be a name if they appear at the beginning - // of the phrase. In structured search the name must forcibly in - // the first phrase. In unstructured search it may be in a later - // phrase when the first phrase is a house number. - if ($oSearch->hasName() - || !($oPosition->isFirstPhrase() || $oPosition->isPhrase('')) - ) { - if ($this->iTermCount > 1 - && ($oPosition->isPhrase('') || !$oPosition->isFirstPhrase()) - ) { - $oNewSearch = $oSearch->clone(1); - $oNewSearch->addAddressToken($this->iId); - - return array($oNewSearch); - } - } elseif (!$oSearch->hasName(true)) { - $oNewSearch = $oSearch->clone(1); - $oNewSearch->addNameToken( - $this->iId, - CONST_Search_NameOnlySearchFrequencyThreshold - && $this->iSearchNameCount - < CONST_Search_NameOnlySearchFrequencyThreshold - ); - - return array($oNewSearch); - } - - return array(); - } - - public function debugInfo() - { - return array( - 'ID' => $this->iId, - 'Type' => 'word', - 'Info' => array( - 'count' => $this->iSearchNameCount, - 'terms' => $this->iTermCount - ) - ); - } - - public function debugCode() - { - return 'W'; - } -} diff --git a/lib-php/cmd.php b/lib-php/cmd.php deleted file mode 100644 index 6f1299dd..00000000 --- a/lib-php/cmd.php +++ /dev/null @@ -1,199 +0,0 @@ -= $iSize || $aArg[$i][0] == '-') { - showUsage($aSpec, $bExitOnError, 'Parameter of \''.$aLine[0].'\' is missing'); - } - - switch ($aLine[6]) { - case 'realpath': - $xVal[] = realpath($aArg[$i]); - break; - case 'realdir': - $sPath = realpath(dirname($aArg[$i])); - if ($sPath) { - $xVal[] = $sPath . '/' . basename($aArg[$i]); - } else { - $xVal[] = $sPath; - } - break; - case 'bool': - $xVal[] = (bool)$aArg[$i]; - break; - case 'int': - $xVal[] = (int)$aArg[$i]; - break; - case 'float': - $xVal[] = (float)$aArg[$i]; - break; - default: - $xVal[] = $aArg[$i]; - break; - } - } - if ($aLine[4] == 1) { - $xVal = $xVal[0]; - } - } else { - $xVal = true; - } - } else { - fail('Variable numbers of params not yet supported'); - } - - if ($aLine[3] > 1) { - if (!array_key_exists($aLine[0], $aResult)) { - $aResult[$aLine[0]] = array(); - } - $aResult[$aLine[0]][] = $xVal; - } else { - $aResult[$aLine[0]] = $xVal; - } - } else { - $bUnknown = $aArg[$i]; - } - } - - if (array_key_exists('help', $aResult)) { - showUsage($aSpec); - } - if ($bUnknown && $bExitOnUnknown) { - showUsage($aSpec, $bExitOnError, 'Unknown option \''.$bUnknown.'\''); - } - - foreach ($aSpec as $aLine) { - if (is_array($aLine)) { - if ($aCounts[$aLine[0]] < $aLine[2]) { - showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is missing'); - } - if ($aCounts[$aLine[0]] > $aLine[3]) { - showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is present too many times'); - } - if ($aLine[6] == 'bool' && !array_key_exists($aLine[0], $aResult)) { - $aResult[$aLine[0]] = false; - } - } - } - return $bUnknown; -} - -function showUsage($aSpec, $bExit = false, $sError = false) -{ - if ($sError) { - echo basename($_SERVER['argv'][0]).': '.$sError."\n"; - echo 'Try `'.basename($_SERVER['argv'][0]).' --help` for more information.'."\n"; - exit; - } - echo 'Usage: '.basename($_SERVER['argv'][0])."\n"; - $bFirst = true; - foreach ($aSpec as $aLine) { - if (is_array($aLine)) { - if ($bFirst) { - $bFirst = false; - echo "\n"; - } - $aNames = array(); - if ($aLine[1]) { - $aNames[] = '-'.$aLine[1]; - } - if ($aLine[0]) { - $aNames[] = '--'.$aLine[0]; - } - $sName = join(', ', $aNames); - echo ' '.$sName.str_repeat(' ', 30-strlen($sName)).$aLine[7]."\n"; - } else { - echo $aLine."\n"; - } - } - echo "\n"; - exit; -} - -function info($sMsg) -{ - echo date('Y-m-d H:i:s == ').$sMsg."\n"; -} - -$aWarnings = array(); - - -function warn($sMsg) -{ - $GLOBALS['aWarnings'][] = $sMsg; - echo date('Y-m-d H:i:s == ').'WARNING: '.$sMsg."\n"; -} - - -function repeatWarnings() -{ - foreach ($GLOBALS['aWarnings'] as $sMsg) { - echo ' * ',$sMsg."\n"; - } -} - - -function setupHTTPProxy() -{ - if (!getSettingBool('HTTP_PROXY')) { - return; - } - - $sProxy = 'tcp://'.getSetting('HTTP_PROXY_HOST').':'.getSetting('HTTP_PROXY_PROT'); - $aHeaders = array(); - - $sLogin = getSetting('HTTP_PROXY_LOGIN'); - $sPassword = getSetting('HTTP_PROXY_PASSWORD'); - - if ($sLogin && $sPassword) { - $sAuth = base64_encode($sLogin.':'.$sPassword); - $aHeaders = array('Proxy-Authorization: Basic '.$sAuth); - } - - $aProxyHeader = array( - 'proxy' => $sProxy, - 'request_fulluri' => true, - 'header' => $aHeaders - ); - - $aContext = array('http' => $aProxyHeader, 'https' => $aProxyHeader); - stream_context_set_default($aContext); -} diff --git a/lib-php/dotenv_loader.php b/lib-php/dotenv_loader.php deleted file mode 100644 index bcddf008..00000000 --- a/lib-php/dotenv_loader.php +++ /dev/null @@ -1,21 +0,0 @@ -load(CONST_ConfigDir.'/env.defaults'); - - if (file_exists('.env')) { - $dotenv->load('.env'); - } -} diff --git a/lib-php/init-cmd.php b/lib-php/init-cmd.php deleted file mode 100644 index 44e7adb2..00000000 --- a/lib-php/init-cmd.php +++ /dev/null @@ -1,13 +0,0 @@ -getCode() == 0 ? 500 : $exception->getCode()); - header('Content-type: application/json; charset=utf-8'); - include(CONST_LibDir.'/template/error-json.php'); - exit(); -} - -function exception_handler_xml($exception) -{ - http_response_code($exception->getCode() == 0 ? 500 : $exception->getCode()); - header('Content-type: text/xml; charset=utf-8'); - echo ''."\n"; - include(CONST_LibDir.'/template/error-xml.php'); - exit(); -} - -function shutdown_exception_handler_xml() -{ - $error = error_get_last(); - if ($error !== null && $error['type'] === E_ERROR) { - exception_handler_xml(new \Exception($error['message'], 500)); - } -} - -function shutdown_exception_handler_json() -{ - $error = error_get_last(); - if ($error !== null && $error['type'] === E_ERROR) { - exception_handler_json(new \Exception($error['message'], 500)); - } -} - - -function set_exception_handler_by_format($sFormat = null) -{ - // Multiple calls to register_shutdown_function will cause multiple callbacks - // to be executed, we only want the last executed. Thus we don't want to register - // one by default without an explicit $sFormat set. - - if (!isset($sFormat)) { - set_exception_handler('exception_handler_json'); - } elseif ($sFormat == 'xml') { - set_exception_handler('exception_handler_xml'); - register_shutdown_function('shutdown_exception_handler_xml'); - } else { - set_exception_handler('exception_handler_json'); - register_shutdown_function('shutdown_exception_handler_json'); - } -} -// set a default -set_exception_handler_by_format(); - - -/*************************************************************************** - * HTTP Reply header setup - */ - -if (CONST_NoAccessControl) { - header('Access-Control-Allow-Origin: *'); - header('Access-Control-Allow-Methods: OPTIONS,GET'); - if (!empty($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) { - header('Access-Control-Allow-Headers: '.$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']); - } -} -if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') { - exit; -} - -if (CONST_Debug) { - header('Content-type: text/html; charset=utf-8'); -} diff --git a/lib-php/init.php b/lib-php/init.php deleted file mode 100644 index 9e71a761..00000000 --- a/lib-php/init.php +++ /dev/null @@ -1,12 +0,0 @@ -getOne("select max(osm_id) from place where osm_type = 'N'"); - // Lookup the timestamp that node was created - $sLastNodeURL = 'https://www.openstreetmap.org/api/0.6/node/'.$iLastOSMID.'/1'; - $sLastNodeXML = file_get_contents($sLastNodeURL); - - if ($sLastNodeXML === false) { - return false; - } - - preg_match('#timestamp="(([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})Z)"#', $sLastNodeXML, $aLastNodeDate); - - return $aLastNodeDate[1]; -} - - -function byImportance($a, $b) -{ - if ($a['importance'] != $b['importance']) { - return ($a['importance'] > $b['importance']?-1:1); - } - - return $a['foundorder'] <=> $b['foundorder']; -} - - -function javascript_renderData($xVal, $iOptions = 0) -{ - $sCallback = isset($_GET['json_callback']) ? $_GET['json_callback'] : ''; - if ($sCallback && !preg_match('/^[$_\p{L}][$_\p{L}\p{Nd}.[\]]*$/u', $sCallback)) { - // Unset, we call javascript_renderData again during exception handling - unset($_GET['json_callback']); - throw new Exception('Invalid json_callback value', 400); - } - - $iOptions |= JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES; - if (isset($_GET['pretty']) && in_array(strtolower($_GET['pretty']), array('1', 'true'))) { - $iOptions |= JSON_PRETTY_PRINT; - } - - $jsonout = json_encode($xVal, $iOptions); - - if ($sCallback) { - header('Content-Type: application/javascript; charset=UTF-8'); - echo $_GET['json_callback'].'('.$jsonout.')'; - } else { - header('Content-Type: application/json; charset=UTF-8'); - echo $jsonout; - } -} - -function addQuotes($s) -{ - return "'".$s."'"; -} - -function parseLatLon($sQuery) -{ - $sFound = null; - $fQueryLat = null; - $fQueryLon = null; - - if (preg_match('/\\s*([NS])[\s]+([0-9]+[0-9.]*)[°\s]+([0-9.]+)?[′\']*[,\s]+([EW])[\s]+([0-9]+)[°\s]+([0-9]+[0-9.]*)[′\']*\\s*/', $sQuery, $aData)) { - /* 1 2 3 4 5 6 - * degrees decimal minutes - * N 40 26.767, W 79 58.933 - * N 40°26.767′, W 79°58.933′ - */ - $sFound = $aData[0]; - $fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60); - $fQueryLon = ($aData[4]=='E'?1:-1) * ($aData[5] + $aData[6]/60); - } elseif (preg_match('/\\s*([0-9]+)[°\s]+([0-9]+[0-9.]*)?[′\']*[\s]+([NS])[,\s]+([0-9]+)[°\s]+([0-9]+[0-9.]*)?[′\'\s]+([EW])\\s*/', $sQuery, $aData)) { - /* 1 2 3 4 5 6 - * degrees decimal minutes - * 40 26.767 N, 79 58.933 W - * 40° 26.767′ N 79° 58.933′ W - */ - $sFound = $aData[0]; - $fQueryLat = ($aData[3]=='N'?1:-1) * ($aData[1] + $aData[2]/60); - $fQueryLon = ($aData[6]=='E'?1:-1) * ($aData[4] + $aData[5]/60); - } elseif (preg_match('/\\s*([NS])[\s]+([0-9]+)[°\s]+([0-9]+)[′\'\s]+([0-9]+)[″"]*[,\s]+([EW])[\s]+([0-9]+)[°\s]+([0-9]+)[′\'\s]+([0-9]+)[″"]*\\s*/', $sQuery, $aData)) { - /* 1 2 3 4 5 6 7 8 - * degrees decimal seconds - * N 40 26 46 W 79 58 56 - * N 40° 26′ 46″, W 79° 58′ 56″ - */ - $sFound = $aData[0]; - $fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60 + $aData[4]/3600); - $fQueryLon = ($aData[5]=='E'?1:-1) * ($aData[6] + $aData[7]/60 + $aData[8]/3600); - } elseif (preg_match('/\\s*([0-9]+)[°\s]+([0-9]+)[′\'\s]+([0-9]+[0-9.]*)[″"\s]+([NS])[,\s]+([0-9]+)[°\s]+([0-9]+)[′\'\s]+([0-9]+[0-9.]*)[″"\s]+([EW])\\s*/', $sQuery, $aData)) { - /* 1 2 3 4 5 6 7 8 - * degrees decimal seconds - * 40 26 46 N 79 58 56 W - * 40° 26′ 46″ N, 79° 58′ 56″ W - * 40° 26′ 46.78″ N, 79° 58′ 56.89″ W - */ - $sFound = $aData[0]; - $fQueryLat = ($aData[4]=='N'?1:-1) * ($aData[1] + $aData[2]/60 + $aData[3]/3600); - $fQueryLon = ($aData[8]=='E'?1:-1) * ($aData[5] + $aData[6]/60 + $aData[7]/3600); - } elseif (preg_match('/\\s*([NS])[\s]+([0-9]+[0-9]*\\.[0-9]+)[°]*[,\s]+([EW])[\s]+([0-9]+[0-9]*\\.[0-9]+)[°]*\\s*/', $sQuery, $aData)) { - /* 1 2 3 4 - * degrees decimal - * N 40.446° W 79.982° - */ - $sFound = $aData[0]; - $fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2]); - $fQueryLon = ($aData[3]=='E'?1:-1) * ($aData[4]); - } elseif (preg_match('/\\s*([0-9]+[0-9]*\\.[0-9]+)[°\s]+([NS])[,\s]+([0-9]+[0-9]*\\.[0-9]+)[°\s]+([EW])\\s*/', $sQuery, $aData)) { - /* 1 2 3 4 - * degrees decimal - * 40.446° N 79.982° W - */ - $sFound = $aData[0]; - $fQueryLat = ($aData[2]=='N'?1:-1) * ($aData[1]); - $fQueryLon = ($aData[4]=='E'?1:-1) * ($aData[3]); - } elseif (preg_match('/(\\s*\\[|^\\s*|\\s*)(-?[0-9]+[0-9]*\\.[0-9]+)[,\s]+(-?[0-9]+[0-9]*\\.[0-9]+)(\\]\\s*|\\s*$|\\s*)/', $sQuery, $aData)) { - /* 1 2 3 4 - * degrees decimal - * 12.34, 56.78 - * 12.34 56.78 - * [12.456,-78.90] - */ - $sFound = $aData[0]; - $fQueryLat = $aData[2]; - $fQueryLon = $aData[3]; - } else { - return false; - } - - return array($sFound, $fQueryLat, $fQueryLon); -} - -function addressRankToGeocodeJsonType($iAddressRank) -{ - if ($iAddressRank >= 29 && $iAddressRank <= 30) { - return 'house'; - } - if ($iAddressRank >= 26 && $iAddressRank < 28) { - return 'street'; - } - if ($iAddressRank >= 22 && $iAddressRank < 26) { - return 'locality'; - } - if ($iAddressRank >= 17 && $iAddressRank < 22) { - return 'district'; - } - if ($iAddressRank >= 13 && $iAddressRank < 17) { - return 'city'; - } - if ($iAddressRank >= 10 && $iAddressRank < 13) { - return 'county'; - } - if ($iAddressRank >= 5 && $iAddressRank < 10) { - return 'state'; - } - if ($iAddressRank >= 4 && $iAddressRank < 5) { - return 'country'; - } - - return 'locality'; -} - -if (!function_exists('array_key_last')) { - function array_key_last(array $array) - { - if (!empty($array)) { - return key(array_slice($array, -1, 1, true)); - } - } -} diff --git a/lib-php/log.php b/lib-php/log.php deleted file mode 100644 index 1d567733..00000000 --- a/lib-php/log.php +++ /dev/null @@ -1,104 +0,0 @@ -getDBQuotedList(array( - $sType, - $hLog[0], - $hLog[2], - $hLog[1], - $sUserAgent, - join(',', $aLanguageList), - $sOutputFormat, - $hLog[3] - ))); - $sSQL .= ')'; - $oDB->exec($sSQL); - } - - return $hLog; -} - -function logEnd(&$oDB, $hLog, $iNumResults) -{ - $fEndTime = microtime(true); - - if (CONST_Log_DB) { - $aEndTime = explode('.', $fEndTime); - if (!isset($aEndTime[1])) { - $aEndTime[1] = '0'; - } - $sEndTime = date('Y-m-d H:i:s', $aEndTime[0]).'.'.$aEndTime[1]; - - $sSQL = 'update new_query_log set endtime = '.$oDB->getDBQuoted($sEndTime).', results = '.$iNumResults; - $sSQL .= ' where starttime = '.$oDB->getDBQuoted($hLog[0]); - $sSQL .= ' and ipaddress = '.$oDB->getDBQuoted($hLog[1]); - $sSQL .= ' and query = '.$oDB->getDBQuoted($hLog[2]); - $oDB->exec($sSQL); - } - - if (CONST_Log_File) { - $aOutdata = sprintf( - "[%s] %.4f %d %s \"%s\"\n", - $hLog[0], - $fEndTime-$hLog[5], - $iNumResults, - $hLog[4], - $hLog[2] - ); - file_put_contents(CONST_Log_File, $aOutdata, FILE_APPEND | LOCK_EX); - } -} diff --git a/lib-php/output.php b/lib-php/output.php deleted file mode 100644 index 44c4dde8..00000000 --- a/lib-php/output.php +++ /dev/null @@ -1,38 +0,0 @@ - 'Feature', - 'properties' => array( - 'geocoding' => array() - ) - ); - - if (isset($aPlace['place_id'])) { - $aFilteredPlaces['properties']['geocoding']['place_id'] = $aPlace['place_id']; - } - $sOSMType = formatOSMType($aPlace['osm_type']); - if ($sOSMType) { - $aFilteredPlaces['properties']['geocoding']['osm_type'] = $sOSMType; - $aFilteredPlaces['properties']['geocoding']['osm_id'] = $aPlace['osm_id']; - } - - $aFilteredPlaces['properties']['geocoding']['osm_key'] = $aPlace['class']; - $aFilteredPlaces['properties']['geocoding']['osm_value'] = $aPlace['type']; - - $aFilteredPlaces['properties']['geocoding']['type'] = addressRankToGeocodeJsonType($aPlace['rank_address']); - - $aFilteredPlaces['properties']['geocoding']['accuracy'] = (int) $fDistance; - - $aFilteredPlaces['properties']['geocoding']['label'] = $aPlace['langaddress']; - - if ($aPlace['placename'] !== null) { - $aFilteredPlaces['properties']['geocoding']['name'] = $aPlace['placename']; - } - - if (isset($aPlace['address'])) { - $aPlace['address']->addGeocodeJsonAddressParts( - $aFilteredPlaces['properties']['geocoding'] - ); - - $aFilteredPlaces['properties']['geocoding']['admin'] - = $aPlace['address']->getAdminLevels(); - } - - if (isset($aPlace['asgeojson'])) { - $aFilteredPlaces['geometry'] = json_decode($aPlace['asgeojson'], true); - } else { - $aFilteredPlaces['geometry'] = array( - 'type' => 'Point', - 'coordinates' => array( - (float) $aPlace['lon'], - (float) $aPlace['lat'] - ) - ); - } - - javascript_renderData(array( - 'type' => 'FeatureCollection', - 'geocoding' => array( - 'version' => '0.1.0', - 'attribution' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', - 'licence' => 'ODbL', - 'query' => $sQuery - ), - 'features' => array($aFilteredPlaces) - )); -} diff --git a/lib-php/template/address-geojson.php b/lib-php/template/address-geojson.php deleted file mode 100644 index dc3c3832..00000000 --- a/lib-php/template/address-geojson.php +++ /dev/null @@ -1,85 +0,0 @@ - 'Feature', - 'properties' => array() - ); - - if (isset($aPlace['place_id'])) { - $aFilteredPlaces['properties']['place_id'] = $aPlace['place_id']; - } - $sOSMType = formatOSMType($aPlace['osm_type']); - if ($sOSMType) { - $aFilteredPlaces['properties']['osm_type'] = $sOSMType; - $aFilteredPlaces['properties']['osm_id'] = $aPlace['osm_id']; - } - - $aFilteredPlaces['properties']['place_rank'] = $aPlace['rank_search']; - - $aFilteredPlaces['properties']['category'] = $aPlace['class']; - $aFilteredPlaces['properties']['type'] = $aPlace['type']; - - $aFilteredPlaces['properties']['importance'] = $aPlace['importance']; - - $aFilteredPlaces['properties']['addresstype'] = strtolower($aPlace['addresstype']); - - $aFilteredPlaces['properties']['name'] = $aPlace['placename']; - - $aFilteredPlaces['properties']['display_name'] = $aPlace['langaddress']; - - if (isset($aPlace['address'])) { - $aFilteredPlaces['properties']['address'] = $aPlace['address']->getAddressNames(); - } - if (isset($aPlace['sExtraTags'])) { - $aFilteredPlaces['properties']['extratags'] = $aPlace['sExtraTags']; - } - if (isset($aPlace['sNameDetails'])) { - $aFilteredPlaces['properties']['namedetails'] = $aPlace['sNameDetails']; - } - - if (isset($aPlace['aBoundingBox'])) { - $aFilteredPlaces['bbox'] = array( - (float) $aPlace['aBoundingBox'][2], // minlon - (float) $aPlace['aBoundingBox'][0], // minlat - (float) $aPlace['aBoundingBox'][3], // maxlon - (float) $aPlace['aBoundingBox'][1] // maxlat - ); - } - - if (isset($aPlace['asgeojson'])) { - $aFilteredPlaces['geometry'] = json_decode($aPlace['asgeojson'], true); - } else { - $aFilteredPlaces['geometry'] = array( - 'type' => 'Point', - 'coordinates' => array( - (float) $aPlace['lon'], - (float) $aPlace['lat'] - ) - ); - } - - - javascript_renderData(array( - 'type' => 'FeatureCollection', - 'licence' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', - 'features' => array($aFilteredPlaces) - )); -} diff --git a/lib-php/template/address-json.php b/lib-php/template/address-json.php deleted file mode 100644 index 0766eaf4..00000000 --- a/lib-php/template/address-json.php +++ /dev/null @@ -1,82 +0,0 @@ -getAddressNames(); - } - if (isset($aPlace['sExtraTags'])) { - $aFilteredPlaces['extratags'] = $aPlace['sExtraTags']; - } - if (isset($aPlace['sNameDetails'])) { - $aFilteredPlaces['namedetails'] = $aPlace['sNameDetails']; - } - - if (isset($aPlace['aBoundingBox'])) { - $aFilteredPlaces['boundingbox'] = $aPlace['aBoundingBox']; - } - - if (isset($aPlace['asgeojson'])) { - $aFilteredPlaces['geojson'] = json_decode($aPlace['asgeojson'], true); - } - - if (isset($aPlace['assvg'])) { - $aFilteredPlaces['svg'] = $aPlace['assvg']; - } - - if (isset($aPlace['astext'])) { - $aFilteredPlaces['geotext'] = $aPlace['astext']; - } - - if (isset($aPlace['askml'])) { - $aFilteredPlaces['geokml'] = $aPlace['askml']; - } -} - -javascript_renderData($aFilteredPlaces); diff --git a/lib-php/template/address-xml.php b/lib-php/template/address-xml.php deleted file mode 100644 index c418a4c4..00000000 --- a/lib-php/template/address-xml.php +++ /dev/null @@ -1,110 +0,0 @@ -\n"; - -echo '\n"; - -if (empty($aPlace)) { - if (isset($sError)) { - echo "$sError"; - } else { - echo 'Unable to geocode'; - } -} else { - echo ''.htmlspecialchars($aPlace['langaddress']).''; - - if (isset($aPlace['address'])) { - echo ''; - foreach ($aPlace['address']->getAddressNames() as $sKey => $sValue) { - $sKey = str_replace(' ', '_', $sKey); - echo "<$sKey>"; - echo htmlspecialchars($sValue); - echo ""; - } - echo ''; - } - - if (isset($aPlace['sExtraTags'])) { - echo ''; - foreach ($aPlace['sExtraTags'] as $sKey => $sValue) { - echo ''; - } - echo ''; - } - - if (isset($aPlace['sNameDetails'])) { - echo ''; - foreach ($aPlace['sNameDetails'] as $sKey => $sValue) { - echo ''; - echo htmlspecialchars($sValue); - echo ''; - } - echo ''; - } - - if (isset($aPlace['askml'])) { - echo "\n"; - echo $aPlace['askml']; - echo ''; - } -} - -echo ''; diff --git a/lib-php/template/details-json.php b/lib-php/template/details-json.php deleted file mode 100644 index ae80a85b..00000000 --- a/lib-php/template/details-json.php +++ /dev/null @@ -1,120 +0,0 @@ -format(DateTime::RFC3339); -$aPlaceDetails['importance'] = (float) $aPointDetails['importance']; -$aPlaceDetails['calculated_importance'] = (float) $aPointDetails['calculated_importance']; - -$aPlaceDetails['extratags'] = $aPointDetails['aExtraTags']; -$aPlaceDetails['calculated_wikipedia'] = $aPointDetails['wikipedia']; -$sIcon = Nominatim\ClassTypes\getIconFile($aPointDetails); -if (isset($sIcon)) { - $aPlaceDetails['icon'] = $sIcon; -} - -$aPlaceDetails['rank_address'] = (int) $aPointDetails['rank_address']; -$aPlaceDetails['rank_search'] = (int) $aPointDetails['rank_search']; - -$aPlaceDetails['isarea'] = $aPointDetails['isarea']; -$aPlaceDetails['centroid'] = array( - 'type' => 'Point', - 'coordinates' => array( (float) $aPointDetails['lon'], (float) $aPointDetails['lat'] ) - ); - -$aPlaceDetails['geometry'] = json_decode($aPointDetails['asgeojson'], true); - -$funcMapAddressLine = function ($aFull) { - return array( - 'localname' => $aFull['localname'], - 'place_id' => isset($aFull['place_id']) ? (int) $aFull['place_id'] : null, - 'osm_id' => isset($aFull['osm_id']) ? (int) $aFull['osm_id'] : null, - 'osm_type' => isset($aFull['osm_type']) ? $aFull['osm_type'] : null, - 'place_type' => isset($aFull['place_type']) ? $aFull['place_type'] : null, - 'class' => $aFull['class'], - 'type' => $aFull['type'], - 'admin_level' => isset($aFull['admin_level']) ? (int) $aFull['admin_level'] : null, - 'rank_address' => $aFull['rank_address'] ? (int) $aFull['rank_address'] : null, - 'distance' => (float) $aFull['distance'], - 'isaddress' => isset($aFull['isaddress']) ? (bool) $aFull['isaddress'] : null - ); -}; - -$funcMapKeyword = function ($aFull) { - return array( - 'id' => (int) $aFull['word_id'], - 'token' => $aFull['word_token'] - ); -}; - -if ($aAddressLines) { - $aPlaceDetails['address'] = array_map($funcMapAddressLine, $aAddressLines); -} - -if ($aLinkedLines) { - $aPlaceDetails['linked_places'] = array_map($funcMapAddressLine, $aLinkedLines); -} - -if ($bIncludeKeywords) { - $aPlaceDetails['keywords'] = array(); - - if ($aPlaceSearchNameKeywords) { - $aPlaceDetails['keywords']['name'] = array_map($funcMapKeyword, $aPlaceSearchNameKeywords); - } else { - $aPlaceDetails['keywords']['name'] = array(); - } - - if ($aPlaceSearchAddressKeywords) { - $aPlaceDetails['keywords']['address'] = array_map($funcMapKeyword, $aPlaceSearchAddressKeywords); - } else { - $aPlaceDetails['keywords']['address'] = array(); - } -} - -if ($bIncludeHierarchy) { - if ($bGroupHierarchy) { - $aPlaceDetails['hierarchy'] = array(); - foreach ($aHierarchyLines as $aAddressLine) { - if ($aAddressLine['type'] == 'yes') { - $sType = $aAddressLine['class']; - } else { - $sType = $aAddressLine['type']; - } - - if (!isset($aPlaceDetails['hierarchy'][$sType])) { - $aPlaceDetails['hierarchy'][$sType] = array(); - } - $aPlaceDetails['hierarchy'][$sType][] = $funcMapAddressLine($aAddressLine); - } - } else { - $aPlaceDetails['hierarchy'] = array_map($funcMapAddressLine, $aHierarchyLines); - } -} - -javascript_renderData($aPlaceDetails); diff --git a/lib-php/template/error-json.php b/lib-php/template/error-json.php deleted file mode 100644 index fea7d5c5..00000000 --- a/lib-php/template/error-json.php +++ /dev/null @@ -1,19 +0,0 @@ - $exception->getCode(), - 'message' => $exception->getMessage() - ); - - if (CONST_Debug) { - $error['details'] = $exception->getFile() . '('. $exception->getLine() . ')'; - } - - javascript_renderData(array('error' => $error)); diff --git a/lib-php/template/error-xml.php b/lib-php/template/error-xml.php deleted file mode 100644 index a21ac198..00000000 --- a/lib-php/template/error-xml.php +++ /dev/null @@ -1,7 +0,0 @@ - - getCode() ?> - getMessage() ?> - -
getFile() . '('. $exception->getLine() . ')' ?>
- -
\ No newline at end of file diff --git a/lib-php/template/search-batch-json.php b/lib-php/template/search-batch-json.php deleted file mode 100644 index 430237a2..00000000 --- a/lib-php/template/search-batch-json.php +++ /dev/null @@ -1,83 +0,0 @@ - $aPointDetails) { - $aPlace = array( - 'place_id'=>$aPointDetails['place_id'], - ); - - $sOSMType = formatOSMType($aPointDetails['osm_type']); - if ($sOSMType) { - $aPlace['osm_type'] = $sOSMType; - $aPlace['osm_id'] = $aPointDetails['osm_id']; - } - - if (isset($aPointDetails['aBoundingBox'])) { - $aPlace['boundingbox'] = array( - $aPointDetails['aBoundingBox'][0], - $aPointDetails['aBoundingBox'][1], - $aPointDetails['aBoundingBox'][2], - $aPointDetails['aBoundingBox'][3] - ); - } - - if (isset($aPointDetails['zoom'])) { - $aPlace['zoom'] = $aPointDetails['zoom']; - } - - $aPlace['lat'] = $aPointDetails['lat']; - $aPlace['lon'] = $aPointDetails['lon']; - $aPlace['display_name'] = $aPointDetails['name']; - $aPlace['place_rank'] = $aPointDetails['rank_search']; - - $aPlace['category'] = $aPointDetails['class']; - $aPlace['type'] = $aPointDetails['type']; - - $aPlace['importance'] = $aPointDetails['importance']; - - if (isset($aPointDetails['icon'])) { - $aPlace['icon'] = $aPointDetails['icon']; - } - - if (isset($aPointDetails['address'])) { - $aPlace['address'] = $aPointDetails['address']->getAddressNames(); - } - - if (isset($aPointDetails['asgeojson'])) { - $aPlace['geojson'] = json_decode($aPointDetails['asgeojson'], true); - } - - if (isset($aPointDetails['assvg'])) { - $aPlace['svg'] = $aPointDetails['assvg']; - } - - if (isset($aPointDetails['astext'])) { - $aPlace['geotext'] = $aPointDetails['astext']; - } - - if (isset($aPointDetails['askml'])) { - $aPlace['geokml'] = $aPointDetails['askml']; - } - - $aFilteredPlaces[] = $aPlace; - } - $aOutput['batch'][] = $aFilteredPlaces; -} - -javascript_renderData($aOutput, array('geojson')); diff --git a/lib-php/template/search-geocodejson.php b/lib-php/template/search-geocodejson.php deleted file mode 100644 index bba41a0d..00000000 --- a/lib-php/template/search-geocodejson.php +++ /dev/null @@ -1,72 +0,0 @@ - $aPointDetails) { - $aPlace = array( - 'type' => 'Feature', - 'properties' => array( - 'geocoding' => array() - ) - ); - - if (isset($aPointDetails['place_id'])) { - $aPlace['properties']['geocoding']['place_id'] = $aPointDetails['place_id']; - } - $sOSMType = formatOSMType($aPointDetails['osm_type']); - if ($sOSMType) { - $aPlace['properties']['geocoding']['osm_type'] = $sOSMType; - $aPlace['properties']['geocoding']['osm_id'] = $aPointDetails['osm_id']; - } - $aPlace['properties']['geocoding']['osm_key'] = $aPointDetails['class']; - $aPlace['properties']['geocoding']['osm_value'] = $aPointDetails['type']; - - $aPlace['properties']['geocoding']['type'] = addressRankToGeocodeJsonType($aPointDetails['rank_address']); - - $aPlace['properties']['geocoding']['label'] = $aPointDetails['langaddress']; - - if ($aPointDetails['placename'] !== null) { - $aPlace['properties']['geocoding']['name'] = $aPointDetails['placename']; - } - - if (isset($aPointDetails['address'])) { - $aPointDetails['address']->addGeocodeJsonAddressParts( - $aPlace['properties']['geocoding'] - ); - - $aPlace['properties']['geocoding']['admin'] - = $aPointDetails['address']->getAdminLevels(); - } - - if (isset($aPointDetails['asgeojson'])) { - $aPlace['geometry'] = json_decode($aPointDetails['asgeojson'], true); - } else { - $aPlace['geometry'] = array( - 'type' => 'Point', - 'coordinates' => array( - (float) $aPointDetails['lon'], - (float) $aPointDetails['lat'] - ) - ); - } - $aFilteredPlaces[] = $aPlace; -} - - -javascript_renderData(array( - 'type' => 'FeatureCollection', - 'geocoding' => array( - 'version' => '0.1.0', - 'attribution' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', - 'licence' => 'ODbL', - 'query' => $sQuery - ), - 'features' => $aFilteredPlaces - )); diff --git a/lib-php/template/search-geojson.php b/lib-php/template/search-geojson.php deleted file mode 100644 index 7665700d..00000000 --- a/lib-php/template/search-geojson.php +++ /dev/null @@ -1,83 +0,0 @@ - $aPointDetails) { - $aPlace = array( - 'type' => 'Feature', - 'properties' => array( - 'place_id'=>$aPointDetails['place_id'], - ) - ); - - $sOSMType = formatOSMType($aPointDetails['osm_type']); - if ($sOSMType) { - $aPlace['properties']['osm_type'] = $sOSMType; - $aPlace['properties']['osm_id'] = $aPointDetails['osm_id']; - } - - if (isset($aPointDetails['aBoundingBox'])) { - $aPlace['bbox'] = array( - (float) $aPointDetails['aBoundingBox'][2], // minlon - (float) $aPointDetails['aBoundingBox'][0], // minlat - (float) $aPointDetails['aBoundingBox'][3], // maxlon - (float) $aPointDetails['aBoundingBox'][1] // maxlat - ); - } - - if (isset($aPointDetails['zoom'])) { - $aPlace['properties']['zoom'] = $aPointDetails['zoom']; - } - - $aPlace['properties']['display_name'] = $aPointDetails['name']; - - $aPlace['properties']['place_rank'] = $aPointDetails['rank_search']; - $aPlace['properties']['category'] = $aPointDetails['class']; - - $aPlace['properties']['type'] = $aPointDetails['type']; - - $aPlace['properties']['importance'] = $aPointDetails['importance']; - - if (isset($aPointDetails['icon']) && $aPointDetails['icon']) { - $aPlace['properties']['icon'] = $aPointDetails['icon']; - } - - if (isset($aPointDetails['address'])) { - $aPlace['properties']['address'] = $aPointDetails['address']->getAddressNames(); - } - - if (isset($aPointDetails['asgeojson'])) { - $aPlace['geometry'] = json_decode($aPointDetails['asgeojson'], true); - } else { - $aPlace['geometry'] = array( - 'type' => 'Point', - 'coordinates' => array( - (float) $aPointDetails['lon'], - (float) $aPointDetails['lat'] - ) - ); - } - - - if (isset($aPointDetails['sExtraTags'])) { - $aPlace['properties']['extratags'] = $aPointDetails['sExtraTags']; - } - if (isset($aPointDetails['sNameDetails'])) { - $aPlace['properties']['namedetails'] = $aPointDetails['sNameDetails']; - } - - $aFilteredPlaces[] = $aPlace; -} - -javascript_renderData(array( - 'type' => 'FeatureCollection', - 'licence' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', - 'features' => $aFilteredPlaces - )); diff --git a/lib-php/template/search-json.php b/lib-php/template/search-json.php deleted file mode 100644 index 5fb13020..00000000 --- a/lib-php/template/search-json.php +++ /dev/null @@ -1,81 +0,0 @@ - $aPointDetails) { - $aPlace = array( - 'place_id'=>$aPointDetails['place_id'], - 'licence'=>'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', - ); - - $sOSMType = formatOSMType($aPointDetails['osm_type']); - if ($sOSMType) { - $aPlace['osm_type'] = $sOSMType; - $aPlace['osm_id'] = $aPointDetails['osm_id']; - } - - if (isset($aPointDetails['aBoundingBox'])) { - $aPlace['boundingbox'] = $aPointDetails['aBoundingBox']; - } - - if (isset($aPointDetails['zoom'])) { - $aPlace['zoom'] = $aPointDetails['zoom']; - } - - $aPlace['lat'] = $aPointDetails['lat']; - $aPlace['lon'] = $aPointDetails['lon']; - - $aPlace['display_name'] = $aPointDetails['name']; - - if ($sOutputFormat == 'jsonv2' || $sOutputFormat == 'geojson') { - $aPlace['place_rank'] = $aPointDetails['rank_search']; - $aPlace['category'] = $aPointDetails['class']; - } else { - $aPlace['class'] = $aPointDetails['class']; - } - $aPlace['type'] = $aPointDetails['type']; - - $aPlace['importance'] = $aPointDetails['importance']; - - if (isset($aPointDetails['icon']) && $aPointDetails['icon']) { - $aPlace['icon'] = $aPointDetails['icon']; - } - - if (isset($aPointDetails['address'])) { - $aPlace['address'] = $aPointDetails['address']->getAddressNames(); - } - - if (isset($aPointDetails['asgeojson'])) { - $aPlace['geojson'] = json_decode($aPointDetails['asgeojson'], true); - } - - if (isset($aPointDetails['assvg'])) { - $aPlace['svg'] = $aPointDetails['assvg']; - } - - if (isset($aPointDetails['astext'])) { - $aPlace['geotext'] = $aPointDetails['astext']; - } - - if (isset($aPointDetails['askml'])) { - $aPlace['geokml'] = $aPointDetails['askml']; - } - - if (isset($aPointDetails['sExtraTags'])) { - $aPlace['extratags'] = $aPointDetails['sExtraTags']; - } - if (isset($aPointDetails['sNameDetails'])) { - $aPlace['namedetails'] = $aPointDetails['sNameDetails']; - } - - $aFilteredPlaces[] = $aPlace; -} - -javascript_renderData($aFilteredPlaces); diff --git a/lib-php/template/search-xml.php b/lib-php/template/search-xml.php deleted file mode 100644 index 8dda65e2..00000000 --- a/lib-php/template/search-xml.php +++ /dev/null @@ -1,138 +0,0 @@ -\n"; - -echo '<'; -echo (isset($sXmlRootTag)?$sXmlRootTag:'searchresults'); -echo " timestamp='".date(DATE_RFC822)."'"; -echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'"; -echo " querystring='".htmlspecialchars($sQuery, ENT_QUOTES)."'"; -if (isset($aMoreParams['viewbox'])) { - echo " viewbox='".htmlspecialchars($aMoreParams['viewbox'], ENT_QUOTES)."'"; -} -if (isset($aMoreParams['exclude_place_ids'])) { - echo " exclude_place_ids='".htmlspecialchars($aMoreParams['exclude_place_ids'])."'"; -} -echo " more_url='".htmlspecialchars($sMoreURL)."'"; -echo ">\n"; - -foreach ($aSearchResults as $iResNum => $aResult) { - echo "'; - } - echo "\n"; - echo $aResult['askml']; - echo ''; - } - - if (isset($aResult['sExtraTags'])) { - if (!$bHasDelim) { - $bHasDelim = true; - echo '>'; - } - echo "\n"; - foreach ($aResult['sExtraTags'] as $sKey => $sValue) { - echo ''; - } - echo ''; - } - - if (isset($aResult['sNameDetails'])) { - if (!$bHasDelim) { - $bHasDelim = true; - echo '>'; - } - echo "\n"; - foreach ($aResult['sNameDetails'] as $sKey => $sValue) { - echo ''; - echo htmlspecialchars($sValue); - echo ''; - } - echo ''; - } - - if (isset($aResult['address'])) { - if (!$bHasDelim) { - $bHasDelim = true; - echo '>'; - } - echo "\n"; - foreach ($aResult['address']->getAddressNames() as $sKey => $sValue) { - $sKey = str_replace(' ', '_', $sKey); - echo "<$sKey>"; - echo htmlspecialchars($sValue); - echo ""; - } - } - - if ($bHasDelim) { - echo ''; - } else { - echo '/>'; - } -} - -echo ''; diff --git a/lib-php/tokenizer/icu_tokenizer.php b/lib-php/tokenizer/icu_tokenizer.php deleted file mode 100644 index e45d0765..00000000 --- a/lib-php/tokenizer/icu_tokenizer.php +++ /dev/null @@ -1,235 +0,0 @@ -oDB =& $oDB; - $this->oNormalizer = \Transliterator::createFromRules(CONST_Term_Normalization_Rules); - $this->oTransliterator = \Transliterator::createFromRules(CONST_Transliteration); - } - - public function checkStatus() - { - $sSQL = 'SELECT word_id FROM word WHERE word_id is not null limit 1'; - $iWordID = $this->oDB->getOne($sSQL); - if ($iWordID === false) { - throw new \Exception('Query failed', 703); - } - if (!$iWordID) { - throw new \Exception('No value', 704); - } - } - - - public function normalizeString($sTerm) - { - if ($this->oNormalizer === null) { - return $sTerm; - } - - return $this->oNormalizer->transliterate($sTerm); - } - - - public function mostFrequentWords($iNum) - { - $sSQL = "SELECT word FROM word WHERE type = 'W'"; - $sSQL .= "ORDER BY info->'count' DESC LIMIT ".$iNum; - return $this->oDB->getCol($sSQL); - } - - - private function makeStandardWord($sTerm) - { - return trim($this->oTransliterator->transliterate(' '.$sTerm.' ')); - } - - - public function tokensForSpecialTerm($sTerm) - { - $aResults = array(); - - $sSQL = "SELECT word_id, info->>'class' as class, info->>'type' as type "; - $sSQL .= ' FROM word WHERE word_token = :term and type = \'S\''; - - Debug::printVar('Term', $sTerm); - Debug::printSQL($sSQL); - $aSearchWords = $this->oDB->getAll($sSQL, array(':term' => $this->makeStandardWord($sTerm))); - - Debug::printVar('Results', $aSearchWords); - - foreach ($aSearchWords as $aSearchTerm) { - $aResults[] = new \Nominatim\Token\SpecialTerm( - $aSearchTerm['word_id'], - $aSearchTerm['class'], - $aSearchTerm['type'], - \Nominatim\Operator::TYPE - ); - } - - Debug::printVar('Special term tokens', $aResults); - - return $aResults; - } - - - public function extractTokensFromPhrases(&$aPhrases) - { - $sNormQuery = ''; - $aWordLists = array(); - $aTokens = array(); - foreach ($aPhrases as $iPhrase => $oPhrase) { - $sNormQuery .= ','.$this->normalizeString($oPhrase->getPhrase()); - $sPhrase = $this->makeStandardWord($oPhrase->getPhrase()); - Debug::printVar('Phrase', $sPhrase); - - $oWordList = new SimpleWordList($sPhrase); - $aTokens = array_merge($aTokens, $oWordList->getTokens()); - $aWordLists[] = $oWordList; - } - - Debug::printVar('Tokens', $aTokens); - Debug::printVar('WordLists', $aWordLists); - - $oValidTokens = $this->computeValidTokens($aTokens, $sNormQuery); - - foreach ($aPhrases as $iPhrase => $oPhrase) { - $oPhrase->setWordSets($aWordLists[$iPhrase]->getWordSets($oValidTokens)); - } - - return $oValidTokens; - } - - - private function computeValidTokens($aTokens, $sNormQuery) - { - $oValidTokens = new TokenList(); - - if (!empty($aTokens)) { - $this->addTokensFromDB($oValidTokens, $aTokens, $sNormQuery); - - // Try more interpretations for Tokens that could not be matched. - foreach ($aTokens as $sToken) { - if ($sToken[0] != ' ' && !$oValidTokens->contains($sToken)) { - if (preg_match('/^([0-9]{5}) [0-9]{4}$/', $sToken, $aData)) { - // US ZIP+4 codes - merge in the 5-digit ZIP code - $oValidTokens->addToken( - $sToken, - new Token\Postcode(null, $aData[1], 'us') - ); - } elseif (preg_match('/^[0-9]+$/', $sToken)) { - // Unknown single word token with a number. - // Assume it is a house number. - $oValidTokens->addToken( - $sToken, - new Token\HouseNumber(null, trim($sToken)) - ); - } - } - } - } - - return $oValidTokens; - } - - - private function addTokensFromDB(&$oValidTokens, $aTokens, $sNormQuery) - { - // Check which tokens we have, get the ID numbers - $sSQL = 'SELECT word_id, word_token, type, word,'; - $sSQL .= " info->>'op' as operator,"; - $sSQL .= " info->>'class' as class, info->>'type' as ctype,"; - $sSQL .= " info->>'count' as count,"; - $sSQL .= " info->>'lookup' as lookup"; - $sSQL .= ' FROM word WHERE word_token in ('; - $sSQL .= join(',', $this->oDB->getDBQuotedList($aTokens)).')'; - - Debug::printSQL($sSQL); - - $aDBWords = $this->oDB->getAll($sSQL, null, 'Could not get word tokens.'); - - foreach ($aDBWords as $aWord) { - $iId = (int) $aWord['word_id']; - $sTok = $aWord['word_token']; - - switch ($aWord['type']) { - case 'C': // country name tokens - if ($aWord['word'] !== null) { - $oValidTokens->addToken( - $sTok, - new Token\Country($iId, $aWord['word']) - ); - } - break; - case 'H': // house number tokens - $sLookup = $aWord['lookup'] ?? $aWord['word_token']; - $oValidTokens->addToken($sTok, new Token\HouseNumber($iId, $sLookup)); - break; - case 'P': // postcode tokens - // Postcodes are not normalized, so they may have content - // that makes SQL injection possible. Reject postcodes - // that would need special escaping. - if ($aWord['word'] !== null - && pg_escape_string($aWord['word']) == $aWord['word'] - ) { - $iSplitPos = strpos($aWord['word'], '@'); - if ($iSplitPos === false) { - $sPostcode = $aWord['word']; - } else { - $sPostcode = substr($aWord['word'], 0, $iSplitPos); - } - - $oValidTokens->addToken( - $sTok, - new Token\Postcode($iId, $sPostcode, null) - ); - } - break; - case 'S': // tokens for classification terms (special phrases) - if ($aWord['class'] !== null && $aWord['ctype'] !== null) { - $oValidTokens->addToken($sTok, new Token\SpecialTerm( - $iId, - $aWord['class'], - $aWord['ctype'], - (isset($aWord['operator'])) ? Operator::NEAR : Operator::NONE - )); - } - break; - case 'W': // full-word tokens - $oValidTokens->addToken($sTok, new Token\Word( - $iId, - (int) $aWord['count'], - substr_count($aWord['word_token'], ' ') - )); - break; - case 'w': // partial word terms - $oValidTokens->addToken($sTok, new Token\Partial( - $iId, - $aWord['word_token'], - (int) $aWord['count'] - )); - break; - default: - break; - } - } - } -} diff --git a/lib-php/tokenizer/legacy_tokenizer.php b/lib-php/tokenizer/legacy_tokenizer.php deleted file mode 100644 index 6f3d2304..00000000 --- a/lib-php/tokenizer/legacy_tokenizer.php +++ /dev/null @@ -1,265 +0,0 @@ -oDB =& $oDB; - $this->oNormalizer = \Transliterator::createFromRules(CONST_Term_Normalization_Rules); - } - - public function checkStatus() - { - $sStandardWord = $this->oDB->getOne("SELECT make_standard_name('a')"); - if ($sStandardWord === false) { - throw new \Exception('Module failed', 701); - } - - if ($sStandardWord != 'a') { - throw new \Exception('Module call failed', 702); - } - - $sSQL = "SELECT word_id FROM word WHERE word_token IN (' a')"; - $iWordID = $this->oDB->getOne($sSQL); - if ($iWordID === false) { - throw new \Exception('Query failed', 703); - } - if (!$iWordID) { - throw new \Exception('No value', 704); - } - } - - - public function normalizeString($sTerm) - { - if ($this->oNormalizer === null) { - return $sTerm; - } - - return $this->oNormalizer->transliterate($sTerm); - } - - - public function mostFrequentWords($iNum) - { - $sSQL = 'SELECT word FROM word WHERE word is not null '; - $sSQL .= 'ORDER BY search_name_count DESC LIMIT '.$iNum; - return $this->oDB->getCol($sSQL); - } - - - public function tokensForSpecialTerm($sTerm) - { - $aResults = array(); - - $sSQL = 'SELECT word_id, class, type FROM word '; - $sSQL .= ' WHERE word_token = \' \' || make_standard_name(:term)'; - $sSQL .= ' AND class is not null AND class not in (\'place\')'; - - Debug::printVar('Term', $sTerm); - Debug::printSQL($sSQL); - $aSearchWords = $this->oDB->getAll($sSQL, array(':term' => $sTerm)); - - Debug::printVar('Results', $aSearchWords); - - foreach ($aSearchWords as $aSearchTerm) { - $aResults[] = new \Nominatim\Token\SpecialTerm( - $aSearchTerm['word_id'], - $aSearchTerm['class'], - $aSearchTerm['type'], - \Nominatim\Operator::TYPE - ); - } - - Debug::printVar('Special term tokens', $aResults); - - return $aResults; - } - - - public function extractTokensFromPhrases(&$aPhrases) - { - // First get the normalized version of all phrases - $sNormQuery = ''; - $sSQL = 'SELECT '; - $aParams = array(); - foreach ($aPhrases as $iPhrase => $oPhrase) { - $sNormQuery .= ','.$this->normalizeString($oPhrase->getPhrase()); - $sSQL .= 'make_standard_name(:' .$iPhrase.') as p'.$iPhrase.','; - $aParams[':'.$iPhrase] = $oPhrase->getPhrase(); - - // Conflicts between US state abbreviations and various words - // for 'the' in different languages - switch (strtolower($oPhrase->getPhrase())) { - case 'il': - $aParams[':'.$iPhrase] = 'illinois'; - break; - case 'al': - $aParams[':'.$iPhrase] = 'alabama'; - break; - case 'la': - $aParams[':'.$iPhrase] = 'louisiana'; - break; - default: - $aParams[':'.$iPhrase] = $oPhrase->getPhrase(); - break; - } - } - $sSQL = substr($sSQL, 0, -1); - - Debug::printSQL($sSQL); - Debug::printVar('SQL parameters', $aParams); - - $aNormPhrases = $this->oDB->getRow($sSQL, $aParams); - - Debug::printVar('SQL result', $aNormPhrases); - - // now compute all possible tokens - $aWordLists = array(); - $aTokens = array(); - foreach ($aNormPhrases as $sPhrase) { - $oWordList = new SimpleWordList($sPhrase); - - foreach ($oWordList->getTokens() as $sToken) { - $aTokens[' '.$sToken] = ' '.$sToken; - $aTokens[$sToken] = $sToken; - } - - $aWordLists[] = $oWordList; - } - - Debug::printVar('Tokens', $aTokens); - Debug::printVar('WordLists', $aWordLists); - - $oValidTokens = $this->computeValidTokens($aTokens, $sNormQuery); - - foreach ($aPhrases as $iPhrase => $oPhrase) { - $oPhrase->setWordSets($aWordLists[$iPhrase]->getWordSets($oValidTokens)); - } - - return $oValidTokens; - } - - - private function computeValidTokens($aTokens, $sNormQuery) - { - $oValidTokens = new TokenList(); - - if (!empty($aTokens)) { - $this->addTokensFromDB($oValidTokens, $aTokens, $sNormQuery); - - // Try more interpretations for Tokens that could not be matched. - foreach ($aTokens as $sToken) { - if ($sToken[0] != ' ' && !$oValidTokens->contains($sToken)) { - if (preg_match('/^([0-9]{5}) [0-9]{4}$/', $sToken, $aData)) { - // US ZIP+4 codes - merge in the 5-digit ZIP code - $oValidTokens->addToken( - $sToken, - new Token\Postcode(null, $aData[1], 'us') - ); - } elseif (preg_match('/^[0-9]+$/', $sToken)) { - // Unknown single word token with a number. - // Assume it is a house number. - $oValidTokens->addToken( - $sToken, - new Token\HouseNumber(null, trim($sToken)) - ); - } - } - } - } - - return $oValidTokens; - } - - - private function addTokensFromDB(&$oValidTokens, $aTokens, $sNormQuery) - { - // Check which tokens we have, get the ID numbers - $sSQL = 'SELECT word_id, word_token, word, class, type, country_code,'; - $sSQL .= ' operator, coalesce(search_name_count, 0) as count'; - $sSQL .= ' FROM word WHERE word_token in ('; - $sSQL .= join(',', $this->oDB->getDBQuotedList($aTokens)).')'; - - Debug::printSQL($sSQL); - - $aDBWords = $this->oDB->getAll($sSQL, null, 'Could not get word tokens.'); - - foreach ($aDBWords as $aWord) { - $oToken = null; - $iId = (int) $aWord['word_id']; - - if ($aWord['class']) { - // Special terms need to appear in their normalized form. - // (postcodes are not normalized in the word table) - $sNormWord = $this->normalizeString($aWord['word']); - if ($aWord['word'] && strpos($sNormQuery, $sNormWord) === false) { - continue; - } - - if ($aWord['class'] == 'place' && $aWord['type'] == 'house') { - $oToken = new Token\HouseNumber($iId, trim($aWord['word_token'])); - } elseif ($aWord['class'] == 'place' && $aWord['type'] == 'postcode') { - if ($aWord['word'] - && pg_escape_string($aWord['word']) == $aWord['word'] - ) { - $oToken = new Token\Postcode( - $iId, - $aWord['word'], - $aWord['country_code'] - ); - } - } else { - // near and in operator the same at the moment - $oToken = new Token\SpecialTerm( - $iId, - $aWord['class'], - $aWord['type'], - $aWord['operator'] ? Operator::NEAR : Operator::NONE - ); - } - } elseif ($aWord['country_code']) { - $oToken = new Token\Country($iId, $aWord['country_code']); - } elseif ($aWord['word_token'][0] == ' ') { - $oToken = new Token\Word( - $iId, - (int) $aWord['count'], - substr_count($aWord['word_token'], ' ') - ); - // For backward compatibility: ignore all partial tokens with more - // than one word. - } elseif (strpos($aWord['word_token'], ' ') === false) { - $oToken = new Token\Partial( - $iId, - $aWord['word_token'], - (int) $aWord['count'] - ); - } - - if ($oToken) { - // remove any leading spaces - if ($aWord['word_token'][0] == ' ') { - $oValidTokens->addToken(substr($aWord['word_token'], 1), $oToken); - } else { - $oValidTokens->addToken($aWord['word_token'], $oToken); - } - } - } - } -} diff --git a/lib-php/website/deletable.php b/lib-php/website/deletable.php deleted file mode 100644 index ffb202fd..00000000 --- a/lib-php/website/deletable.php +++ /dev/null @@ -1,36 +0,0 @@ -getSet('format', array('json'), 'json'); -set_exception_handler_by_format($sOutputFormat); - -$oDB = new Nominatim\DB(CONST_Database_DSN); -$oDB->connect(); - -$sSQL = 'select placex.place_id, country_code,'; -$sSQL .= " name->'name' as name, i.* from placex, import_polygon_delete i"; -$sSQL .= ' where placex.osm_id = i.osm_id and placex.osm_type = i.osm_type'; -$sSQL .= ' and placex.class = i.class and placex.type = i.type'; -$aPolygons = $oDB->getAll($sSQL, null, 'Could not get list of deleted OSM elements.'); - -if (CONST_Debug) { - var_dump($aPolygons); - exit; -} - -if ($sOutputFormat == 'json') { - javascript_renderData($aPolygons); -} diff --git a/lib-php/website/details.php b/lib-php/website/details.php deleted file mode 100644 index 98fb6ef7..00000000 --- a/lib-php/website/details.php +++ /dev/null @@ -1,263 +0,0 @@ -getSet('format', array('json'), 'json'); -set_exception_handler_by_format($sOutputFormat); - -$aLangPrefOrder = $oParams->getPreferredLanguages(); - -$sPlaceId = $oParams->getString('place_id'); -$sOsmType = $oParams->getSet('osmtype', array('N', 'W', 'R')); -$iOsmId = $oParams->getInt('osmid', 0); -$sClass = $oParams->getString('class'); - -$bIncludeKeywords = $oParams->getBool('keywords', false); -$bIncludeAddressDetails = $oParams->getBool('addressdetails', false); -$bIncludeLinkedPlaces = $oParams->getBool('linkedplaces', true); -$bIncludeHierarchy = $oParams->getBool('hierarchy', false); -$bGroupHierarchy = $oParams->getBool('group_hierarchy', false); -$bIncludePolygonAsGeoJSON = $oParams->getBool('polygon_geojson', false); - -$oDB = new Nominatim\DB(CONST_Database_DSN); -$oDB->connect(); - -$sLanguagePrefArraySQL = $oDB->getArraySQL($oDB->getDBQuotedList($aLangPrefOrder)); - -if ($sOsmType && $iOsmId !== 0) { - $sSQL = 'SELECT place_id FROM placex WHERE osm_type = :type AND osm_id = :id'; - $aSQLParams = array(':type' => $sOsmType, ':id' => $iOsmId); - // osm_type and osm_id are not unique enough - if ($sClass) { - $sSQL .= ' AND class= :class'; - $aSQLParams[':class'] = $sClass; - } - $sSQL .= ' ORDER BY class ASC'; - $sPlaceId = $oDB->getOne($sSQL, $aSQLParams); - - - // Nothing? Maybe it's an interpolation. - // XXX Simply returns the first parent street it finds. It should - // get a house number and get the right interpolation. - if (!$sPlaceId && $sOsmType == 'W' && (!$sClass || $sClass == 'place')) { - $sSQL = 'SELECT place_id FROM location_property_osmline' - .' WHERE osm_id = :id LIMIT 1'; - $sPlaceId = $oDB->getOne($sSQL, array(':id' => $iOsmId)); - } - - // Be nice about our error messages for broken geometry - - if (!$sPlaceId && $oDB->tableExists('import_polygon_error')) { - $sSQL = 'SELECT '; - $sSQL .= ' osm_type, '; - $sSQL .= ' osm_id, '; - $sSQL .= ' errormessage, '; - $sSQL .= ' class, '; - $sSQL .= ' type, '; - $sSQL .= " get_name_by_language(name,$sLanguagePrefArraySQL) AS localname,"; - $sSQL .= ' ST_AsText(prevgeometry) AS prevgeom, '; - $sSQL .= ' ST_AsText(newgeometry) AS newgeom'; - $sSQL .= ' FROM import_polygon_error '; - $sSQL .= ' WHERE osm_type = :type'; - $sSQL .= ' AND osm_id = :id'; - $sSQL .= ' ORDER BY updated DESC'; - $sSQL .= ' LIMIT 1'; - $aPointDetails = $oDB->getRow($sSQL, array(':type' => $sOsmType, ':id' => $iOsmId)); - if ($aPointDetails) { - if (preg_match('/\[(-?\d+\.\d+) (-?\d+\.\d+)\]/', $aPointDetails['errormessage'], $aMatches)) { - $aPointDetails['error_x'] = $aMatches[1]; - $aPointDetails['error_y'] = $aMatches[2]; - } else { - $aPointDetails['error_x'] = 0; - $aPointDetails['error_y'] = 0; - } - include(CONST_LibDir.'/template/details-error-'.$sOutputFormat.'.php'); - exit; - } - } - - if ($sPlaceId === false) { - throw new \Exception('No place with that OSM ID found.', 404); - } -} else { - if ($sPlaceId === false) { - userError('Required parameters missing. Need either osmtype/osmid or place_id.'); - } -} - -$iPlaceID = (int)$sPlaceId; - -if (CONST_Use_US_Tiger_Data) { - $iParentPlaceID = $oDB->getOne('SELECT parent_place_id FROM location_property_tiger WHERE place_id = '.$iPlaceID); - if ($iParentPlaceID) { - $iPlaceID = $iParentPlaceID; - } -} - -// interpolated house numbers -$iParentPlaceID = $oDB->getOne('SELECT parent_place_id FROM location_property_osmline WHERE place_id = '.$iPlaceID); -if ($iParentPlaceID) { - $iPlaceID = $iParentPlaceID; -} - -// artificial postcodes -$iParentPlaceID = $oDB->getOne('SELECT parent_place_id FROM location_postcode WHERE place_id = '.$iPlaceID); -if ($iParentPlaceID) { - $iPlaceID = $iParentPlaceID; -} - -$hLog = logStart($oDB, 'details', $_SERVER['QUERY_STRING'], $aLangPrefOrder); - -// Get the details for this point -$sSQL = 'SELECT place_id, osm_type, osm_id, class, type, name, admin_level,'; -$sSQL .= ' housenumber, postcode, country_code,'; -$sSQL .= ' importance, wikipedia,'; -$sSQL .= ' ROUND(EXTRACT(epoch FROM indexed_date)) AS indexed_epoch,'; -$sSQL .= ' parent_place_id, '; -$sSQL .= ' rank_address, '; -$sSQL .= ' rank_search, '; -$sSQL .= " get_name_by_language(name,$sLanguagePrefArraySQL) AS localname, "; -$sSQL .= " ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') AS isarea, "; -$sSQL .= ' ST_y(centroid) AS lat, '; -$sSQL .= ' ST_x(centroid) AS lon, '; -$sSQL .= ' CASE '; -$sSQL .= ' WHEN importance = 0 OR importance IS NULL '; -$sSQL .= ' THEN 0.75-(rank_search::float/40) '; -$sSQL .= ' ELSE importance '; -$sSQL .= ' END as calculated_importance, '; -if ($bIncludePolygonAsGeoJSON) { - $sSQL .= ' ST_AsGeoJSON(CASE '; - $sSQL .= ' WHEN ST_NPoints(geometry) > 5000 '; - $sSQL .= ' THEN ST_SimplifyPreserveTopology(geometry, 0.0001) '; - $sSQL .= ' ELSE geometry '; - $sSQL .= ' END) as asgeojson'; -} else { - $sSQL .= ' ST_AsGeoJSON(centroid) as asgeojson'; -} -$sSQL .= ' FROM placex '; -$sSQL .= " WHERE place_id = $iPlaceID"; - -$aPointDetails = $oDB->getRow($sSQL, null, 'Could not get details of place object.'); - -if (!$aPointDetails) { - throw new \Exception('No place with that place ID found.', 404); -} - -$aPointDetails['localname'] = $aPointDetails['localname']?$aPointDetails['localname']:$aPointDetails['housenumber']; - -// Get all alternative names (languages, etc) -$sSQL = 'SELECT (each(name)).key,(each(name)).value FROM placex '; -$sSQL .= "WHERE place_id = $iPlaceID ORDER BY (each(name)).key"; -$aPointDetails['aNames'] = $oDB->getAssoc($sSQL); - -// Address tags -$sSQL = 'SELECT (each(address)).key as key,(each(address)).value FROM placex '; -$sSQL .= "WHERE place_id = $iPlaceID ORDER BY key"; -$aPointDetails['aAddressTags'] = $oDB->getAssoc($sSQL); - -// Extra tags -$sSQL = 'SELECT (each(extratags)).key,(each(extratags)).value FROM placex '; -$sSQL .= "WHERE place_id = $iPlaceID ORDER BY (each(extratags)).key"; -$aPointDetails['aExtraTags'] = $oDB->getAssoc($sSQL); - -// Address -$aAddressLines = false; -if ($bIncludeAddressDetails) { - $oDetails = new Nominatim\AddressDetails($oDB, $iPlaceID, -1, $sLanguagePrefArraySQL); - $aAddressLines = $oDetails->getAddressDetails(true); -} - -// Linked places -$aLinkedLines = false; -if ($bIncludeLinkedPlaces) { - $sSQL = 'SELECT placex.place_id, osm_type, osm_id, class, type, housenumber,'; - $sSQL .= ' admin_level, rank_address, '; - $sSQL .= " ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') AS isarea,"; - $sSQL .= " ST_DistanceSpheroid(geometry, placegeometry, 'SPHEROID[\"WGS 84\",6378137,298.257223563, AUTHORITY[\"EPSG\",\"7030\"]]') AS distance, "; - $sSQL .= " get_name_by_language(name,$sLanguagePrefArraySQL) AS localname, "; - $sSQL .= ' length(name::text) AS namelength '; - $sSQL .= ' FROM '; - $sSQL .= ' placex, '; - $sSQL .= ' ( '; - $sSQL .= ' SELECT centroid AS placegeometry '; - $sSQL .= ' FROM placex '; - $sSQL .= " WHERE place_id = $iPlaceID "; - $sSQL .= ' ) AS x'; - $sSQL .= " WHERE linked_place_id = $iPlaceID"; - $sSQL .= ' ORDER BY '; - $sSQL .= ' rank_address ASC, '; - $sSQL .= ' rank_search ASC, '; - $sSQL .= " get_name_by_language(name, $sLanguagePrefArraySQL), "; - $sSQL .= ' housenumber'; - $aLinkedLines = $oDB->getAll($sSQL); -} - -// All places this is an immediate parent of -$aHierarchyLines = false; -if ($bIncludeHierarchy) { - $sSQL = 'SELECT obj.place_id, osm_type, osm_id, class, type, housenumber,'; - $sSQL .= " admin_level, rank_address, ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') AS isarea,"; - $sSQL .= " ST_DistanceSpheroid(geometry, placegeometry, 'SPHEROID[\"WGS 84\",6378137,298.257223563, AUTHORITY[\"EPSG\",\"7030\"]]') AS distance, "; - $sSQL .= " get_name_by_language(name,$sLanguagePrefArraySQL) AS localname, "; - $sSQL .= ' length(name::text) AS namelength '; - $sSQL .= ' FROM '; - $sSQL .= ' ( '; - $sSQL .= ' SELECT placex.place_id, osm_type, osm_id, class, type, housenumber, admin_level, rank_address, rank_search, geometry, name '; - $sSQL .= ' FROM placex '; - $sSQL .= " WHERE parent_place_id = $iPlaceID "; - $sSQL .= ' ORDER BY '; - $sSQL .= ' rank_address ASC, '; - $sSQL .= ' rank_search ASC '; - $sSQL .= ' LIMIT 500 '; - $sSQL .= ' ) AS obj,'; - $sSQL .= ' ( '; - $sSQL .= ' SELECT centroid AS placegeometry '; - $sSQL .= ' FROM placex '; - $sSQL .= " WHERE place_id = $iPlaceID "; - $sSQL .= ' ) AS x'; - $sSQL .= ' ORDER BY '; - $sSQL .= ' rank_address ASC, '; - $sSQL .= ' rank_search ASC, '; - $sSQL .= ' localname, '; - $sSQL .= ' housenumber'; - $aHierarchyLines = $oDB->getAll($sSQL); -} - -$aPlaceSearchNameKeywords = false; -$aPlaceSearchAddressKeywords = false; -if ($bIncludeKeywords) { - $sSQL = "SELECT * FROM search_name WHERE place_id = $iPlaceID"; - $aPlaceSearchName = $oDB->getRow($sSQL); - - if (!empty($aPlaceSearchName)) { - $sWordIds = substr($aPlaceSearchName['name_vector'], 1, -1); - if (!empty($sWordIds)) { - $sSQL = 'SELECT * FROM word WHERE word_id in ('.$sWordIds.')'; - $aPlaceSearchNameKeywords = $oDB->getAll($sSQL); - } - - $sWordIds = substr($aPlaceSearchName['nameaddress_vector'], 1, -1); - if (!empty($sWordIds)) { - $sSQL = 'SELECT * FROM word WHERE word_id in ('.$sWordIds.')'; - $aPlaceSearchAddressKeywords = $oDB->getAll($sSQL); - } - } -} - -logEnd($oDB, $hLog, 1); - -include(CONST_LibDir.'/template/details-'.$sOutputFormat.'.php'); diff --git a/lib-php/website/lookup.php b/lib-php/website/lookup.php deleted file mode 100644 index 3a7ddb85..00000000 --- a/lib-php/website/lookup.php +++ /dev/null @@ -1,101 +0,0 @@ -getSet('format', array('xml', 'json', 'jsonv2', 'geojson', 'geocodejson'), 'xml'); -set_exception_handler_by_format($sOutputFormat); - -// Preferred language -$aLangPrefOrder = $oParams->getPreferredLanguages(); - -$oDB = new Nominatim\DB(CONST_Database_DSN); -$oDB->connect(); - -$hLog = logStart($oDB, 'place', $_SERVER['QUERY_STRING'], $aLangPrefOrder); - -$aSearchResults = array(); -$aCleanedQueryParts = array(); - -$oPlaceLookup = new Nominatim\PlaceLookup($oDB); -$oPlaceLookup->loadParamArray($oParams); -$oPlaceLookup->setIncludeAddressDetails($oParams->getBool('addressdetails', true)); - -$aOsmIds = explode(',', $oParams->getString('osm_ids', '')); - -if (count($aOsmIds) > CONST_Places_Max_ID_count) { - userError('Bulk User: Only ' . CONST_Places_Max_ID_count . ' ids are allowed in one request.'); -} - -foreach ($aOsmIds as $sItem) { - // Skip empty sItem - if (empty($sItem)) { - continue; - } - - $sType = $sItem[0]; - $iId = (int) substr($sItem, 1); - if ($iId > 0 && ($sType == 'N' || $sType == 'W' || $sType == 'R')) { - $aCleanedQueryParts[] = $sType . $iId; - $oPlace = $oPlaceLookup->lookupOSMID($sType, $iId); - if ($oPlace) { - // we want to use the search-* output templates, so we need to fill - // $aSearchResults and slightly change the (reverse search) oPlace - // key names - $oResult = $oPlace; - unset($oResult['aAddress']); - if (isset($oPlace['aAddress'])) { - $oResult['address'] = $oPlace['aAddress']; - } - if ($sOutputFormat != 'geocodejson') { - unset($oResult['langaddress']); - $oResult['name'] = $oPlace['langaddress']; - } - - $aOutlineResult = $oPlaceLookup->getOutlines( - $oPlace['place_id'], - $oPlace['lon'], - $oPlace['lat'], - Nominatim\ClassTypes\getDefRadius($oPlace) - ); - - if ($aOutlineResult) { - $oResult = array_merge($oResult, $aOutlineResult); - } - - $aSearchResults[] = $oResult; - } - } -} - - -if (CONST_Debug) { - exit; -} - -$sXmlRootTag = 'lookupresults'; -$sQuery = join(',', $aCleanedQueryParts); -// we initialize these to avoid warnings in our logfile -$sViewBox = ''; -$bShowPolygons = ''; -$aExcludePlaceIDs = array(); -$sMoreURL = ''; - -logEnd($oDB, $hLog, 1); - -$sOutputTemplate = ($sOutputFormat == 'jsonv2') ? 'json' : $sOutputFormat; -include(CONST_LibDir.'/template/search-'.$sOutputTemplate.'.php'); diff --git a/lib-php/website/polygons.php b/lib-php/website/polygons.php deleted file mode 100644 index 5a90abe5..00000000 --- a/lib-php/website/polygons.php +++ /dev/null @@ -1,63 +0,0 @@ -getSet('format', array('json'), 'json'); -set_exception_handler_by_format($sOutputFormat); - -$iDays = $oParams->getInt('days', false); -$bReduced = $oParams->getBool('reduced', false); -$sClass = $oParams->getString('class', false); - -$oDB = new Nominatim\DB(CONST_Database_DSN); -$oDB->connect(); - -$iTotalBroken = (int) $oDB->getOne('SELECT count(*) FROM import_polygon_error'); - -$aPolygons = array(); -while ($iTotalBroken && empty($aPolygons)) { - $sSQL = 'SELECT osm_type, osm_id, class, type, name->\'name\' as "name",'; - $sSQL .= 'country_code, errormessage, updated'; - $sSQL .= ' FROM import_polygon_error'; - - $aWhere = array(); - if ($iDays) { - $aWhere[] = "updated > 'now'::timestamp - '".$iDays." day'::interval"; - $iDays++; - } - - if ($bReduced) { - $aWhere[] = "errormessage like 'Area reduced%'"; - } - if ($sClass) { - $sWhere[] = "class = '".pg_escape_string($sClass)."'"; - } - - if (!empty($aWhere)) { - $sSQL .= ' WHERE '.join(' and ', $aWhere); - } - - $sSQL .= ' ORDER BY updated desc LIMIT 1000'; - $aPolygons = $oDB->getAll($sSQL); -} - -if (CONST_Debug) { - var_dump($aPolygons); - exit; -} - -if ($sOutputFormat == 'json') { - javascript_renderData($aPolygons); -} diff --git a/lib-php/website/reverse-only-search.php b/lib-php/website/reverse-only-search.php deleted file mode 100644 index 43cbd265..00000000 --- a/lib-php/website/reverse-only-search.php +++ /dev/null @@ -1,20 +0,0 @@ -getSet('format', array('xml', 'json', 'jsonv2', 'geojson', 'geocodejson'), 'jsonv2'); -set_exception_handler_by_format($sOutputFormat); - -throw new Exception('Reverse-only import does not support forward searching.', 404); diff --git a/lib-php/website/reverse.php b/lib-php/website/reverse.php deleted file mode 100644 index f24c655a..00000000 --- a/lib-php/website/reverse.php +++ /dev/null @@ -1,95 +0,0 @@ -getSet('format', array('xml', 'json', 'jsonv2', 'geojson', 'geocodejson'), 'xml'); -set_exception_handler_by_format($sOutputFormat); - -// Preferred language -$aLangPrefOrder = $oParams->getPreferredLanguages(); - -$oDB = new Nominatim\DB(CONST_Database_DSN); -$oDB->connect(); - -$hLog = logStart($oDB, 'reverse', $_SERVER['QUERY_STRING'], $aLangPrefOrder); - -$oPlaceLookup = new Nominatim\PlaceLookup($oDB); -$oPlaceLookup->loadParamArray($oParams); -$oPlaceLookup->setIncludeAddressDetails($oParams->getBool('addressdetails', true)); - -$sOsmType = $oParams->getSet('osm_type', array('N', 'W', 'R')); -$iOsmId = $oParams->getInt('osm_id', -1); -$fLat = $oParams->getFloat('lat'); -$fLon = $oParams->getFloat('lon'); -$iZoom = $oParams->getInt('zoom', 18); - -if ($sOsmType && $iOsmId > 0) { - $aPlace = $oPlaceLookup->lookupOSMID($sOsmType, $iOsmId); -} elseif ($fLat !== false && $fLon !== false) { - $oReverseGeocode = new Nominatim\ReverseGeocode($oDB); - $oReverseGeocode->setZoom($iZoom); - - $oLookup = $oReverseGeocode->lookup($fLat, $fLon); - - if ($oLookup) { - $aPlaces = $oPlaceLookup->lookup(array($oLookup->iId => $oLookup)); - if (!empty($aPlaces)) { - $aPlace = reset($aPlaces); - } - } -} else { - userError('Need coordinates or OSM object to lookup.'); -} - -if (isset($aPlace)) { - $aOutlineResult = $oPlaceLookup->getOutlines( - $aPlace['place_id'], - $aPlace['lon'], - $aPlace['lat'], - Nominatim\ClassTypes\getDefRadius($aPlace), - $fLat, - $fLon - ); - - if ($aOutlineResult) { - $aPlace = array_merge($aPlace, $aOutlineResult); - } -} else { - $aPlace = array(); -} - -logEnd($oDB, $hLog, count($aPlace) ? 1 : 0); - -if (CONST_Debug) { - var_dump($aPlace); - exit; -} - -if ($sOutputFormat == 'geocodejson') { - $sQuery = $fLat.','.$fLon; - if (isset($aPlace['place_id'])) { - $fDistance = $oDB->getOne( - 'SELECT ST_Distance(ST_SetSRID(ST_Point(:lon,:lat),4326), centroid) FROM placex where place_id = :placeid', - array(':lon' => $fLon, ':lat' => $fLat, ':placeid' => $aPlace['place_id']) - ); - } -} - -$sOutputTemplate = ($sOutputFormat == 'jsonv2') ? 'json' : $sOutputFormat; -include(CONST_LibDir.'/template/address-'.$sOutputTemplate.'.php'); diff --git a/lib-php/website/search.php b/lib-php/website/search.php deleted file mode 100644 index e8f23983..00000000 --- a/lib-php/website/search.php +++ /dev/null @@ -1,93 +0,0 @@ -connect(); -$oParams = new Nominatim\ParameterParser(); - -$oGeocode = new Nominatim\Geocode($oDB); - -$aLangPrefOrder = $oParams->getPreferredLanguages(); -$oGeocode->setLanguagePreference($aLangPrefOrder); - -// Format for output -$sOutputFormat = $oParams->getSet('format', array('xml', 'json', 'jsonv2', 'geojson', 'geocodejson'), 'jsonv2'); -set_exception_handler_by_format($sOutputFormat); - -$oGeocode->loadParamArray($oParams, null); - -if (CONST_Search_BatchMode && isset($_GET['batch'])) { - $aBatch = json_decode($_GET['batch'], true); - $aBatchResults = array(); - foreach ($aBatch as $aBatchParams) { - $oBatchGeocode = clone $oGeocode; - $oBatchParams = new Nominatim\ParameterParser($aBatchParams); - $oBatchGeocode->loadParamArray($oBatchParams); - $oBatchGeocode->setQueryFromParams($oBatchParams); - $aSearchResults = $oBatchGeocode->lookup(); - $aBatchResults[] = $aSearchResults; - } - include(CONST_LibDir.'/template/search-batch-json.php'); - exit; -} - -$oGeocode->setQueryFromParams($oParams); - -if (!$oGeocode->getQueryString() - && isset($_SERVER['PATH_INFO']) - && strlen($_SERVER['PATH_INFO']) > 0 - && $_SERVER['PATH_INFO'][0] == '/' -) { - $sQuery = substr(rawurldecode($_SERVER['PATH_INFO']), 1); - - // reverse order of '/' separated string - $aPhrases = explode('/', $sQuery); - $aPhrases = array_reverse($aPhrases); - $sQuery = join(', ', $aPhrases); - $oGeocode->setQuery($sQuery); -} - -$hLog = logStart($oDB, 'search', $oGeocode->getQueryString(), $aLangPrefOrder); - -$aSearchResults = $oGeocode->lookup(); - -logEnd($oDB, $hLog, count($aSearchResults)); - -$sQuery = $oGeocode->getQueryString(); - -$aMoreParams = $oGeocode->getMoreUrlParams(); -$aMoreParams['format'] = $sOutputFormat; -if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { - $aMoreParams['accept-language'] = $_SERVER['HTTP_ACCEPT_LANGUAGE']; -} - -if (isset($_SERVER['REQUEST_SCHEME']) - && isset($_SERVER['HTTP_HOST']) - && isset($_SERVER['DOCUMENT_URI']) -) { - $sMoreURL = $_SERVER['REQUEST_SCHEME'].'://' - .$_SERVER['HTTP_HOST'].$_SERVER['DOCUMENT_URI'].'/?' - .http_build_query($aMoreParams); -} else { - $sMoreURL = '/search.php?'.http_build_query($aMoreParams); -} - -if (CONST_Debug) { - exit; -} - -$sOutputTemplate = ($sOutputFormat == 'jsonv2') ? 'json' : $sOutputFormat; -include(CONST_LibDir.'/template/search-'.$sOutputTemplate.'.php'); diff --git a/lib-php/website/status.php b/lib-php/website/status.php deleted file mode 100644 index 2839f72a..00000000 --- a/lib-php/website/status.php +++ /dev/null @@ -1,56 +0,0 @@ -getSet('format', array('text', 'json'), 'text'); - -$oDB = new Nominatim\DB(CONST_Database_DSN); - -if ($sOutputFormat == 'json') { - header('content-type: application/json; charset=UTF-8'); -} - - -try { - $oStatus = new Nominatim\Status($oDB); - $oStatus->status(); - - if ($sOutputFormat == 'json') { - $epoch = $oStatus->dataDate(); - $aResponse = array( - 'status' => 0, - 'message' => 'OK', - 'data_updated' => (new DateTime('@'.$epoch))->format(DateTime::RFC3339), - 'software_version' => CONST_NominatimVersion - ); - $sDatabaseVersion = $oStatus->databaseVersion(); - if ($sDatabaseVersion) { - $aResponse['database_version'] = $sDatabaseVersion; - } - javascript_renderData($aResponse); - } else { - echo 'OK'; - } -} catch (Exception $oErr) { - if ($sOutputFormat == 'json') { - $aResponse = array( - 'status' => $oErr->getCode(), - 'message' => $oErr->getMessage() - ); - javascript_renderData($aResponse); - } else { - header('HTTP/1.0 500 Internal Server Error'); - echo 'ERROR: '.$oErr->getMessage(); - } -} -- 2.39.5