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;