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
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
#### NOMINATIM_LOG_DB

View File

@@ -192,6 +192,13 @@ NOMINATIM_REQUEST_TIMEOUT=60
# to geocode" instead.
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
#
# The following options allow to enable logging of API requests.

View File

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