3  * SPDX-License-Identifier: GPL-2.0-only
 
   5  * This file is part of Nominatim. (https://nominatim.org)
 
   7  * Copyright (C) 2022 by the Nominatim developer community.
 
   8  * For a full list of authors see the git log.
 
  11 namespace Nominatim\ClassTypes;
 
  14  * Create a label tag for the given place that can be used as an XML name.
 
  16  * @param array[] $aPlace  Information about the place to label.
 
  18  * A label tag groups various object types together under a common
 
  19  * label. The returned value is lower case and has no spaces
 
  21 function getLabelTag($aPlace, $sCountry = null)
 
  23     $iRank = (int) ($aPlace['rank_address'] ?? 30);
 
  25     if (isset($aPlace['place_type'])) {
 
  26         $sLabel = $aPlace['place_type'];
 
  27     } elseif ($aPlace['class'] == 'boundary' && $aPlace['type'] == 'administrative') {
 
  28         $sLabel = getBoundaryLabel($iRank/2, $sCountry);
 
  29     } elseif ($aPlace['type'] == 'postal_code') {
 
  31     } elseif ($iRank < 26) {
 
  32         $sLabel = $aPlace['type'];
 
  33     } elseif ($iRank < 28) {
 
  35     } elseif ($aPlace['class'] == 'place'
 
  36             && ($aPlace['type'] == 'house_number' ||
 
  37                 $aPlace['type'] == 'house_name' ||
 
  38                 $aPlace['type'] == 'country_code')
 
  40         $sLabel = $aPlace['type'];
 
  42         $sLabel = $aPlace['class'];
 
  45     return strtolower(str_replace(' ', '_', $sLabel));
 
  49  * Create a label for the given place.
 
  51  * @param array[] $aPlace  Information about the place to label.
 
  53 function getLabel($aPlace, $sCountry = null)
 
  55     if (isset($aPlace['place_type'])) {
 
  56         return ucwords(str_replace('_', ' ', $aPlace['place_type']));
 
  59     if ($aPlace['class'] == 'boundary' && $aPlace['type'] == 'administrative') {
 
  60         return getBoundaryLabel(($aPlace['rank_address'] ?? 30)/2, $sCountry ?? null);
 
  63     // Return a label only for 'important' class/type combinations
 
  64     if (getImportance($aPlace) !== null) {
 
  65         return ucwords(str_replace('_', ' ', $aPlace['type']));
 
  73  * Return a simple label for an administrative boundary for the given country.
 
  75  * @param int $iAdminLevel   Content of admin_level tag.
 
  76  * @param string $sCountry   Country code of the country where the object is
 
  77  *                           in. May be null, in which case a world-wide
 
  79  * @param string $sFallback  String to return if no explicit string is listed.
 
  83 function getBoundaryLabel($iAdminLevel, $sCountry, $sFallback = 'Administrative')
 
  85     static $aBoundaryList = array (
 
  91                                            5 => 'State District',
 
  97                                            11 => 'Neighbourhood',
 
 110     if (isset($aBoundaryList[$sCountry])
 
 111         && isset($aBoundaryList[$sCountry][$iAdminLevel])
 
 113         return $aBoundaryList[$sCountry][$iAdminLevel];
 
 116     return $aBoundaryList['default'][$iAdminLevel] ?? $sFallback;
 
 120  * Return an estimated radius of how far the object node extends.
 
 122  * @param array[] $aPlace  Information about the place. This must be a node
 
 125  * @return float  The radius around the feature in degrees.
 
 127 function getDefRadius($aPlace)
 
 129     $aSpecialRadius = array(
 
 130                        'place:continent' => 25,
 
 131                        'place:country' => 7,
 
 132                        'place:state' => 2.6,
 
 133                        'place:province' => 2.6,
 
 134                        'place:region' => 1.0,
 
 135                        'place:county' => 0.7,
 
 136                        'place:city' => 0.16,
 
 137                        'place:municipality' => 0.16,
 
 138                        'place:island' => 0.32,
 
 139                        'place:postcode' => 0.16,
 
 140                        'place:town' => 0.04,
 
 141                        'place:village' => 0.02,
 
 142                        'place:hamlet' => 0.02,
 
 143                        'place:district' => 0.02,
 
 144                        'place:borough' => 0.02,
 
 145                        'place:suburb' => 0.02,
 
 146                        'place:locality' => 0.01,
 
 147                        'place:neighbourhood'=> 0.01,
 
 148                        'place:quarter' => 0.01,
 
 149                        'place:city_block' => 0.01,
 
 150                        'landuse:farm' => 0.01,
 
 151                        'place:farm' => 0.01,
 
 152                        'place:airport' => 0.015,
 
 153                        'aeroway:aerodrome' => 0.015,
 
 154                        'railway:station' => 0.005
 
 157     $sClassPlace = $aPlace['class'].':'.$aPlace['type'];
 
 159     return $aSpecialRadius[$sClassPlace] ?? 0.00005;
 
 163  * Get the icon to use with the given object.
 
 165 function getIcon($aPlace)
 
 168                'boundary:administrative' => 'poi_boundary_administrative',
 
 169                'place:city' => 'poi_place_city',
 
 170                'place:town' => 'poi_place_town',
 
 171                'place:village' => 'poi_place_village',
 
 172                'place:hamlet' => 'poi_place_village',
 
 173                'place:suburb' => 'poi_place_village',
 
 174                'place:locality' => 'poi_place_village',
 
 175                'place:airport' => 'transport_airport2',
 
 176                'aeroway:aerodrome' => 'transport_airport2',
 
 177                'railway:station' => 'transport_train_station2',
 
 178                'amenity:place_of_worship' => 'place_of_worship_unknown3',
 
 179                'amenity:pub' => 'food_pub',
 
 180                'amenity:bar' => 'food_bar',
 
 181                'amenity:university' => 'education_university',
 
 182                'tourism:museum' => 'tourist_museum',
 
 183                'amenity:arts_centre' => 'tourist_art_gallery2',
 
 184                'tourism:zoo' => 'tourist_zoo',
 
 185                'tourism:theme_park' => 'poi_point_of_interest',
 
 186                'tourism:attraction' => 'poi_point_of_interest',
 
 187                'leisure:golf_course' => 'sport_golf',
 
 188                'historic:castle' => 'tourist_castle',
 
 189                'amenity:hospital' => 'health_hospital',
 
 190                'amenity:school' => 'education_school',
 
 191                'amenity:theatre' => 'tourist_theatre',
 
 192                'amenity:library' => 'amenity_library',
 
 193                'amenity:fire_station' => 'amenity_firestation3',
 
 194                'amenity:police' => 'amenity_police2',
 
 195                'amenity:bank' => 'money_bank2',
 
 196                'amenity:post_office' => 'amenity_post_office',
 
 197                'tourism:hotel' => 'accommodation_hotel2',
 
 198                'amenity:cinema' => 'tourist_cinema',
 
 199                'tourism:artwork' => 'tourist_art_gallery2',
 
 200                'historic:archaeological_site' => 'tourist_archaeological2',
 
 201                'amenity:doctors' => 'health_doctors',
 
 202                'leisure:sports_centre' => 'sport_leisure_centre',
 
 203                'leisure:swimming_pool' => 'sport_swimming_outdoor',
 
 204                'shop:supermarket' => 'shopping_supermarket',
 
 205                'shop:convenience' => 'shopping_convenience',
 
 206                'amenity:restaurant' => 'food_restaurant',
 
 207                'amenity:fast_food' => 'food_fastfood',
 
 208                'amenity:cafe' => 'food_cafe',
 
 209                'tourism:guest_house' => 'accommodation_bed_and_breakfast',
 
 210                'amenity:pharmacy' => 'health_pharmacy_dispensing',
 
 211                'amenity:fuel' => 'transport_fuel',
 
 212                'natural:peak' => 'poi_peak',
 
 213                'natural:wood' => 'landuse_coniferous_and_deciduous',
 
 214                'shop:bicycle' => 'shopping_bicycle',
 
 215                'shop:clothes' => 'shopping_clothes',
 
 216                'shop:hairdresser' => 'shopping_hairdresser',
 
 217                'shop:doityourself' => 'shopping_diy',
 
 218                'shop:estate_agent' => 'shopping_estateagent2',
 
 219                'shop:car' => 'shopping_car',
 
 220                'shop:garden_centre' => 'shopping_garden_centre',
 
 221                'shop:car_repair' => 'shopping_car_repair',
 
 222                'shop:bakery' => 'shopping_bakery',
 
 223                'shop:butcher' => 'shopping_butcher',
 
 224                'shop:apparel' => 'shopping_clothes',
 
 225                'shop:laundry' => 'shopping_laundrette',
 
 226                'shop:beverages' => 'shopping_alcohol',
 
 227                'shop:alcohol' => 'shopping_alcohol',
 
 228                'shop:optician' => 'health_opticians',
 
 229                'shop:chemist' => 'health_pharmacy',
 
 230                'shop:gallery' => 'tourist_art_gallery2',
 
 231                'shop:jewelry' => 'shopping_jewelry',
 
 232                'tourism:information' => 'amenity_information',
 
 233                'historic:ruins' => 'tourist_ruin',
 
 234                'amenity:college' => 'education_school',
 
 235                'historic:monument' => 'tourist_monument',
 
 236                'historic:memorial' => 'tourist_monument',
 
 237                'historic:mine' => 'poi_mine',
 
 238                'tourism:caravan_site' => 'accommodation_caravan_park',
 
 239                'amenity:bus_station' => 'transport_bus_station',
 
 240                'amenity:atm' => 'money_atm2',
 
 241                'tourism:viewpoint' => 'tourist_view_point',
 
 242                'tourism:guesthouse' => 'accommodation_bed_and_breakfast',
 
 243                'railway:tram' => 'transport_tram_stop',
 
 244                'amenity:courthouse' => 'amenity_court',
 
 245                'amenity:recycling' => 'amenity_recycling',
 
 246                'amenity:dentist' => 'health_dentist',
 
 247                'natural:beach' => 'tourist_beach',
 
 248                'railway:tram_stop' => 'transport_tram_stop',
 
 249                'amenity:prison' => 'amenity_prison',
 
 250                'highway:bus_stop' => 'transport_bus_stop2'
 
 253     $sClassPlace = $aPlace['class'].':'.$aPlace['type'];
 
 255     return $aIcons[$sClassPlace] ?? null;
 
 259  * Get an icon for the given object with its full URL.
 
 261 function getIconFile($aPlace)
 
 263     if (CONST_MapIcon_URL === false) {
 
 267     $sIcon = getIcon($aPlace);
 
 269     if (!isset($sIcon)) {
 
 273     return CONST_MapIcon_URL.'/'.$sIcon.'.p.20.png';
 
 277  * Return a class importance value for the given place.
 
 279  * @param array[] $aPlace  Information about the place.
 
 281  * @return int  An importance value. The lower the value, the more
 
 282  *              important the class.
 
 284 function getImportance($aPlace)
 
 286     static $aWithImportance = null;
 
 288     if ($aWithImportance === null) {
 
 289         $aWithImportance = array_flip(array(
 
 290                                            'boundary:administrative',
 
 305                                            'highway:motorway_junction',
 
 311                                            'highway:residential',
 
 312                                            'highway:unclassified',
 
 313                                            'highway:living_street',
 
 320                                            'highway:pedestrian',
 
 323                                            'highway:motorway_link',
 
 324                                            'highway:trunk_link',
 
 325                                            'highway:primary_link',
 
 326                                            'landuse:industrial',
 
 327                                            'landuse:residential',
 
 329                                            'landuse:commercial',
 
 333                                            'amenity:place_of_worship',
 
 336                                            'amenity:university',
 
 338                                            'amenity:arts_centre',
 
 340                                            'tourism:theme_park',
 
 341                                            'tourism:attraction',
 
 342                                            'leisure:golf_course',
 
 347                                            'amenity:public_building',
 
 350                                            'amenity:community_centre',
 
 351                                            'amenity:fire_station',
 
 354                                            'amenity:post_office',
 
 358                                            'landuse:recreation_ground',
 
 363                                            'historic:archaeological_site',
 
 365                                            'leisure:sports_centre',
 
 366                                            'leisure:swimming_pool',
 
 369                                            'amenity:restaurant',
 
 372                                            'tourism:guest_house',
 
 376                                            'waterway:waterfall',
 
 381                                            'landuse:allotments',
 
 393                                            'shop:garden_centre',
 
 401                                            'shop:department_store',
 
 425                                            'shop:shopping_centre',
 
 432                                            'shop:travel_agency',
 
 435                                            'tourism:information',
 
 438                                            'place:house_number',
 
 439                                            'place:country_code',
 
 441                                            'highway:unsurfaced',
 
 447                                            'leisure:nature_reserve',
 
 449                                            'waterway:lock_gate',
 
 455                                            'leisure:playground',
 
 459                                            'tourism:caravan_site',
 
 460                                            'amenity:bus_station',
 
 461                                            'amenity:kindergarten',
 
 462                                            'highway:construction',
 
 464                                            'amenity:emergency_phone',
 
 466                                            'waterway:riverbank',
 
 470                                            'tourism:bed_and_breakfast',
 
 474                                            'amenity:courthouse',
 
 479                                            'amenity:grave_yard',
 
 482                                            'landuse:village_green',
 
 488                                            'railway:light_rail',
 
 489                                            'railway:narrow_gauge',
 
 491                                            'amenity:village_hall',
 
 493                                            'amenity:veterinary',
 
 494                                            'landuse:brownfield',
 
 496                                            'railway:historic_station',
 
 497                                            'landuse:construction',
 
 501                                            'highway:traffic_signals',
 
 504                                            'amenity:social_club',
 
 506                                            'amenity:health_centre',
 
 510                                            'amenity:ferry_terminal',
 
 516                                            'amenity:car_rental',
 
 519                                            'amenity:nursing_home',
 
 523                                            'highway:mini_roundabout',
 
 526                                            'amenity:bicycle_parking',
 
 530                                            'natural:cave_entrance',
 
 531                                            'amenity:crematorium',
 
 532                                            'tourism:picnic_site',
 
 541                                            'amenity:medical_centre',
 
 542                                            'historic:roman_road',
 
 544                                            'railway:subway_entrance',
 
 550                                            'railway:level_crossing',
 
 553                                            'tourism:apartments',
 
 565                                            'waterway:derelict_canal',
 
 567                                            'railway:disused_station',
 
 573     $sClassPlace = $aPlace['class'].':'.$aPlace['type'];
 
 575     return $aWithImportance[$sClassPlace] ?? null;