Merge pull request #3686 from astridx/output_names

Output names as setting
This commit is contained in:
Sarah Hoffmann
2025-04-01 20:16:15 +02:00
committed by GitHub
4 changed files with 250 additions and 5 deletions

View File

@@ -602,6 +602,43 @@ results gathered so far.
Note that under high load you may observe that users receive different results Note that under high load you may observe that users receive different results
than usual without seeing an error. This may cause some confusion. than usual without seeing an error. This may cause some confusion.
#### NOMINATIM_OUTPUT_NAMES
| Summary | |
| -------------- | --------------------------------------------------- |
| **Description:** | Specifies order of name tags |
| **Format:** | string: comma-separated list of tag names |
| **Default:** | name:XX,name,brand,official_name:XX,short_name:XX,official_name,short_name,ref |
Specifies the order in which different name tags are used.
The values in this list determine the preferred order of name variants,
including language-specific names (in OSM: the name tag with and without any language suffix).
Comma-separated list, where :XX stands for language suffix
(e.g. name:en) and no :XX stands for general tags (e.g. name).
See also [NOMINATIM_DEFAULT_LANGUAGE](#nominatim_default_language).
!!! note
If NOMINATIM_OUTPUT_NAMES = `name:XX,name,short_name:XX,short_name` the search follows
```
'name', 'short_name'
```
if we have no preferred language order for showing search results.
For languages ['en', 'es'] the search follows
```
'name:en', 'name:es',
'name',
'short_name:en', 'short_name:es',
'short_name'
```
For those familiar with the internal implementation, the `_place_*` expansion is added, but to simplify, it is not included in this example.
### Logging Settings ### Logging Settings
#### NOMINATIM_LOG_DB #### NOMINATIM_LOG_DB

View File

@@ -192,6 +192,13 @@ NOMINATIM_REQUEST_TIMEOUT=60
# to geocode" instead. # to geocode" instead.
NOMINATIM_SEARCH_WITHIN_COUNTRIES=False NOMINATIM_SEARCH_WITHIN_COUNTRIES=False
# Specifies the order in which different name tags are used.
# The values in this list determine the preferred order of name variants,
# including language-specific names.
# Comma-separated list, where :XX stands for language-specific tags
# (e.g. name:en) and no :XX stands for general tags (e.g. name).
NOMINATIM_OUTPUT_NAMES=name:XX,name,brand,official_name:XX,short_name:XX,official_name,short_name,ref
### Log settings ### Log settings
# #
# The following options allow to enable logging of API requests. # The following options allow to enable logging of API requests.

View File

@@ -8,6 +8,7 @@
Helper functions for localizing names of results. Helper functions for localizing names of results.
""" """
from typing import Mapping, List, Optional from typing import Mapping, List, Optional
from .config import Configuration
import re import re
@@ -20,14 +21,18 @@ class Locales:
""" """
def __init__(self, langs: Optional[List[str]] = None): def __init__(self, langs: Optional[List[str]] = None):
self.config = Configuration(None)
self.languages = langs or [] self.languages = langs or []
self.name_tags: List[str] = [] self.name_tags: List[str] = []
# Build the list of supported tags. It is currently hard-coded. parts = self.config.OUTPUT_NAMES.split(',')
self._add_lang_tags('name')
self._add_tags('name', 'brand') for part in parts:
self._add_lang_tags('official_name', 'short_name') part = part.strip()
self._add_tags('official_name', 'short_name', 'ref') if part.endswith(":XX"):
self._add_lang_tags(part[:-3])
else:
self._add_tags(part)
def __bool__(self) -> bool: def __bool__(self) -> bool:
return len(self.languages) > 0 return len(self.languages) > 0

View File

@@ -27,6 +27,62 @@ def test_display_name_none_localized():
assert loc.display_name({'ref': '34', 'name:de': 'DE'}) == '34' assert loc.display_name({'ref': '34', 'name:de': 'DE'}) == '34'
def test_output_names_none_localized():
loc = Locales()
expected_tags = [
'name', '_place_name', 'brand', '_place_brand', 'official_name', '_place_official_name',
'short_name', '_place_short_name', 'ref', '_place_ref'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_none_localized_and_custom_output_names(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name:XX,entrance:XX,name,brand,test_tag,'
'official_name:XX,short_name:XX,alt_name:XX'
)
loc = Locales()
expected_tags = [
'name', '_place_name', 'brand', '_place_brand', 'test_tag', '_place_test_tag'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_none_localized_and_custom_output_names_more_than_two_changes(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name:XX,brand,test_tag:XX,official_name,short_name:XX,'
'alt_name,another_tag_with:XX,another_tag_withoutXX'
)
loc = Locales()
expected_tags = [
'brand', '_place_brand', 'official_name', '_place_official_name', 'alt_name',
'_place_alt_name', 'another_tag_withoutXX', '_place_another_tag_withoutXX'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_none_localized_and_custom_output_names_including_space(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name:XX,name ,short_name:XX, short_name'
)
loc = Locales()
expected_tags = [
'name', '_place_name', 'short_name', '_place_short_name'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_display_name_localized(): def test_display_name_localized():
loc = Locales(['en', 'de']) loc = Locales(['en', 'de'])
@@ -35,6 +91,146 @@ def test_display_name_localized():
assert loc.display_name({'ref': '34', 'name:de': 'DE'}) == 'DE' assert loc.display_name({'ref': '34', 'name:de': 'DE'}) == 'DE'
def test_output_names_localized():
loc = Locales(['en', 'es'])
expected_tags = [
'name:en', '_place_name:en', 'name:es', '_place_name:es', 'name', '_place_name', 'brand',
'_place_brand', 'official_name:en', '_place_official_name:en', 'official_name:es',
'_place_official_name:es', 'short_name:en', '_place_short_name:en', 'short_name:es',
'_place_short_name:es', 'official_name', '_place_official_name', 'short_name',
'_place_short_name', 'ref', '_place_ref'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_localized_and_custom_output_names_including_space(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name:XX,name ,short_name:XX, short_name'
)
loc = Locales(['en', 'es'])
expected_tags = [
'name:en', '_place_name:en', 'name:es', '_place_name:es',
'name', '_place_name',
'short_name:en', '_place_short_name:en', 'short_name:es', '_place_short_name:es',
'short_name', '_place_short_name'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_localized_and_custom_output_names(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name:XX,entrance:XX,name,brand,test_tag,official_name:XX,short_name:XX,alt_name:XX'
)
loc = Locales(['en', 'es'])
expected_tags = [
'name:en', '_place_name:en', 'name:es', '_place_name:es', 'entrance:en',
'_place_entrance:en', 'entrance:es', '_place_entrance:es', 'name', '_place_name',
'brand', '_place_brand', 'test_tag', '_place_test_tag', 'official_name:en',
'_place_official_name:en', 'official_name:es', '_place_official_name:es',
'short_name:en', '_place_short_name:en', 'short_name:es', '_place_short_name:es',
'alt_name:en', '_place_alt_name:en', 'alt_name:es', '_place_alt_name:es'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_localized_and_custom_output_names_start_with_tag_that_has_no_XX(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name,brand,test_tag,official_name:XX,short_name:XX,alt_name:XX'
)
loc = Locales(['en', 'es'])
expected_tags = [
'name', '_place_name', 'brand', '_place_brand', 'test_tag', '_place_test_tag',
'official_name:en', '_place_official_name:en', 'official_name:es',
'_place_official_name:es', 'short_name:en', '_place_short_name:en', 'short_name:es',
'_place_short_name:es', 'alt_name:en', '_place_alt_name:en', 'alt_name:es',
'_place_alt_name:es'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_localized_and_custom_output_names_no_named_tags(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name,brand,test_tag'
)
loc = Locales(['en', 'es'])
expected_tags = [
'name', '_place_name', 'brand', '_place_brand', 'test_tag', '_place_test_tag'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_localized_and_custom_output_names_only_named_tags(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name:XX,entrance:XX,official_name:XX,short_name:XX,alt_name:XX'
)
loc = Locales(['en', 'es'])
expected_tags = [
'name:en', '_place_name:en', 'name:es', '_place_name:es', 'entrance:en',
'_place_entrance:en', 'entrance:es', '_place_entrance:es', 'official_name:en',
'_place_official_name:en', 'official_name:es', '_place_official_name:es',
'short_name:en', '_place_short_name:en', 'short_name:es', '_place_short_name:es',
'alt_name:en', '_place_alt_name:en', 'alt_name:es', '_place_alt_name:es'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_localized_and_custom_output_names_more_than_two_changes(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name:XX,brand,test_tag:XX,official_name,short_name:XX,'
'alt_name,another_tag_with:XX,another_tag_withoutXX'
)
loc = Locales(['en', 'es'])
expected_tags = [
'name:en', '_place_name:en', 'name:es', '_place_name:es', 'brand', '_place_brand',
'test_tag:en', '_place_test_tag:en', 'test_tag:es', '_place_test_tag:es', 'official_name',
'_place_official_name', 'short_name:en', '_place_short_name:en', 'short_name:es',
'_place_short_name:es', 'alt_name', '_place_alt_name', 'another_tag_with:en',
'_place_another_tag_with:en', 'another_tag_with:es', '_place_another_tag_with:es',
'another_tag_withoutXX', '_place_another_tag_withoutXX'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_output_names_localized_and_custom_output_names_XX_in_the_middle(monkeypatch):
monkeypatch.setenv(
'NOMINATIM_OUTPUT_NAMES',
'name:XX,br:XXand,test_tag:XX,official_name,sh:XXort_name:XX,'
'alt_name,another_tag_with:XX,another_tag_withoutXX'
)
loc = Locales(['en', 'es'])
expected_tags = [
'name:en', '_place_name:en', 'name:es', '_place_name:es', 'br:XXand', '_place_br:XXand',
'test_tag:en', '_place_test_tag:en', 'test_tag:es', '_place_test_tag:es', 'official_name',
'_place_official_name', 'sh:XXort_name:en', '_place_sh:XXort_name:en', 'sh:XXort_name:es',
'_place_sh:XXort_name:es', 'alt_name', '_place_alt_name', 'another_tag_with:en',
'_place_another_tag_with:en', 'another_tag_with:es', '_place_another_tag_with:es',
'another_tag_withoutXX', '_place_another_tag_withoutXX'
]
assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
def test_display_name_preference(): def test_display_name_preference():
loc = Locales(['en', 'de']) loc = Locales(['en', 'de'])