Repository: twain47/Nominatim Branch: master Commit: fd68dd4ee63f Files: 725 Total size: 5.4 MB Directory structure: gitextract_d41547bl/ ├── .codespellrc ├── .flake8 ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── config.yml │ │ ├── feature_request.md │ │ ├── report-issues-with-search-results.md │ │ └── report-problems-with-the-software.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── actions/ │ │ ├── build-nominatim/ │ │ │ └── action.yml │ │ ├── setup-postgresql/ │ │ │ └── action.yml │ │ └── setup-postgresql-windows/ │ │ └── action.yml │ └── workflows/ │ └── ci-tests.yml ├── .gitignore ├── .gitmodules ├── .mypy.ini ├── AUTHORS ├── CONTRIBUTING.md ├── COPYING ├── ChangeLog ├── LICENSES/ │ ├── Apache-2.0.txt │ └── GPL-2.0-only.txt ├── Makefile ├── README.md ├── SECURITY.md ├── VAGRANT.md ├── Vagrantfile ├── data/ │ └── words.sql ├── docs/ │ ├── admin/ │ │ ├── Advanced-Installations.md │ │ ├── Deployment-Python.md │ │ ├── Faq.md │ │ ├── Import.md │ │ ├── Installation.md │ │ ├── Maintenance.md │ │ ├── Migration.md │ │ ├── Setup-Nominatim-UI.md │ │ └── Update.md │ ├── api/ │ │ ├── Details.md │ │ ├── Faq.md │ │ ├── Lookup.md │ │ ├── Output.md │ │ ├── Overview.md │ │ ├── Reverse.md │ │ ├── Search.md │ │ └── Status.md │ ├── customize/ │ │ ├── Country-Settings.md │ │ ├── Import-Styles.md │ │ ├── Importance.md │ │ ├── Overview.md │ │ ├── Postcodes.md │ │ ├── Ranking.md │ │ ├── Result-Formatting.md │ │ ├── SQLite.md │ │ ├── Settings.md │ │ ├── Special-Phrases.md │ │ ├── Tiger.md │ │ └── Tokenizers.md │ ├── develop/ │ │ ├── Database-Layout.md │ │ ├── Development-Environment.md │ │ ├── ICU-Tokenizer-Modules.md │ │ ├── Indexing.md │ │ ├── Testing.md │ │ ├── Tokenizers.md │ │ ├── address-tables.plantuml │ │ ├── data-sources.md │ │ ├── osm2pgsql-tables.plantuml │ │ ├── overview.md │ │ ├── parenting-flow.plantuml │ │ └── search-tables.plantuml │ ├── extra.css │ ├── index.md │ ├── library/ │ │ ├── Configuration.md │ │ ├── Getting-Started.md │ │ ├── Input-Parameter-Types.md │ │ ├── Low-Level-DB-Access.md │ │ ├── NominatimAPI.md │ │ └── Result-Handling.md │ ├── mk_install_instructions.py │ └── styles.css ├── lib-lua/ │ ├── flex-base.lua │ ├── import-address.lua │ ├── import-admin.lua │ ├── import-extratags.lua │ ├── import-full.lua │ ├── import-street.lua │ ├── taginfo.lua │ └── themes/ │ └── nominatim/ │ ├── init.lua │ ├── presets.lua │ └── topics/ │ ├── address.lua │ ├── admin.lua │ ├── full.lua │ └── street.lua ├── lib-sql/ │ ├── functions/ │ │ ├── associated_street_triggers.sql │ │ ├── importance.sql │ │ ├── interpolation.sql │ │ ├── partition-functions.sql │ │ ├── place_triggers.sql │ │ ├── placex_triggers.sql │ │ ├── postcode_triggers.sql │ │ ├── ranking.sql │ │ ├── updates.sql │ │ └── utils.sql │ ├── functions.sql │ ├── grants.sql │ ├── indices.sql │ ├── partition-tables.src.sql │ ├── postcode_tables.sql │ ├── table-triggers.sql │ ├── tables/ │ │ ├── addressline.sql │ │ ├── entrance.sql │ │ ├── import_reports.sql │ │ ├── importance_tables.sql │ │ ├── interpolation.sql │ │ ├── location_area.sql │ │ ├── nominatim_properties.sql │ │ ├── placex.sql │ │ ├── postcodes.sql │ │ ├── search_name.sql │ │ ├── status.sql │ │ └── tiger.sql │ ├── tables.sql │ ├── tiger_import_finish.sql │ ├── tiger_import_start.sql │ └── tokenizer/ │ └── icu_tokenizer.sql ├── man/ │ ├── create-manpage.py │ └── nominatim.1 ├── mkdocs.yml ├── munin/ │ ├── nominatim_importlag │ ├── nominatim_query_speed │ └── nominatim_requests ├── nominatim-cli.py ├── packaging/ │ ├── nominatim-api/ │ │ ├── COPYING │ │ ├── README.md │ │ ├── extra_src/ │ │ │ └── paths.py │ │ └── pyproject.toml │ └── nominatim-db/ │ ├── COPYING │ ├── README.md │ ├── extra_src/ │ │ └── nominatim_db/ │ │ └── paths.py │ ├── pyproject.toml │ └── scripts/ │ └── nominatim ├── settings/ │ ├── address-levels.json │ ├── country-names/ │ │ ├── ad.yaml │ │ ├── ae.yaml │ │ ├── af.yaml │ │ ├── ag.yaml │ │ ├── ai.yaml │ │ ├── al.yaml │ │ ├── am.yaml │ │ ├── ao.yaml │ │ ├── ar.yaml │ │ ├── at.yaml │ │ ├── au.yaml │ │ ├── az.yaml │ │ ├── ba.yaml │ │ ├── bb.yaml │ │ ├── bd.yaml │ │ ├── be.yaml │ │ ├── bf.yaml │ │ ├── bg.yaml │ │ ├── bh.yaml │ │ ├── bi.yaml │ │ ├── bj.yaml │ │ ├── bm.yaml │ │ ├── bn.yaml │ │ ├── bo.yaml │ │ ├── br.yaml │ │ ├── bs.yaml │ │ ├── bt.yaml │ │ ├── bw.yaml │ │ ├── by.yaml │ │ ├── bz.yaml │ │ ├── ca.yaml │ │ ├── cd.yaml │ │ ├── cf.yaml │ │ ├── cg.yaml │ │ ├── ch.yaml │ │ ├── ci.yaml │ │ ├── ck.yaml │ │ ├── cl.yaml │ │ ├── cm.yaml │ │ ├── cn.yaml │ │ ├── co.yaml │ │ ├── cr.yaml │ │ ├── cu.yaml │ │ ├── cv.yaml │ │ ├── cy.yaml │ │ ├── cz.yaml │ │ ├── de.yaml │ │ ├── dj.yaml │ │ ├── dk.yaml │ │ ├── dm.yaml │ │ ├── do.yaml │ │ ├── dz.yaml │ │ ├── ec.yaml │ │ ├── ee.yaml │ │ ├── eg.yaml │ │ ├── eh.yaml │ │ ├── er.yaml │ │ ├── es.yaml │ │ ├── et.yaml │ │ ├── fi.yaml │ │ ├── fj.yaml │ │ ├── fk.yaml │ │ ├── fm.yaml │ │ ├── fo.yaml │ │ ├── fr.yaml │ │ ├── ga.yaml │ │ ├── gb.yaml │ │ ├── gd.yaml │ │ ├── ge.yaml │ │ ├── gg.yaml │ │ ├── gh.yaml │ │ ├── gi.yaml │ │ ├── gl.yaml │ │ ├── gm.yaml │ │ ├── gn.yaml │ │ ├── gq.yaml │ │ ├── gr.yaml │ │ ├── gs.yaml │ │ ├── gt.yaml │ │ ├── gw.yaml │ │ ├── gy.yaml │ │ ├── hn.yaml │ │ ├── hr.yaml │ │ ├── ht.yaml │ │ ├── hu.yaml │ │ ├── id.yaml │ │ ├── ie.yaml │ │ ├── il.yaml │ │ ├── im.yaml │ │ ├── in.yaml │ │ ├── io.yaml │ │ ├── iq.yaml │ │ ├── ir.yaml │ │ ├── is.yaml │ │ ├── it.yaml │ │ ├── je.yaml │ │ ├── jm.yaml │ │ ├── jo.yaml │ │ ├── jp.yaml │ │ ├── ke.yaml │ │ ├── kg.yaml │ │ ├── kh.yaml │ │ ├── ki.yaml │ │ ├── km.yaml │ │ ├── kn.yaml │ │ ├── kp.yaml │ │ ├── kr.yaml │ │ ├── kw.yaml │ │ ├── ky.yaml │ │ ├── kz.yaml │ │ ├── la.yaml │ │ ├── lb.yaml │ │ ├── lc.yaml │ │ ├── li.yaml │ │ ├── lk.yaml │ │ ├── lr.yaml │ │ ├── ls.yaml │ │ ├── lt.yaml │ │ ├── lu.yaml │ │ ├── lv.yaml │ │ ├── ly.yaml │ │ ├── ma.yaml │ │ ├── mc.yaml │ │ ├── md.yaml │ │ ├── me.yaml │ │ ├── mg.yaml │ │ ├── mh.yaml │ │ ├── mk.yaml │ │ ├── ml.yaml │ │ ├── mm.yaml │ │ ├── mn.yaml │ │ ├── mr.yaml │ │ ├── ms.yaml │ │ ├── mt.yaml │ │ ├── mu.yaml │ │ ├── mv.yaml │ │ ├── mw.yaml │ │ ├── mx.yaml │ │ ├── my.yaml │ │ ├── mz.yaml │ │ ├── na.yaml │ │ ├── ne.yaml │ │ ├── ng.yaml │ │ ├── ni.yaml │ │ ├── nl.yaml │ │ ├── no.yaml │ │ ├── np.yaml │ │ ├── nr.yaml │ │ ├── nu.yaml │ │ ├── nz.yaml │ │ ├── om.yaml │ │ ├── pa.yaml │ │ ├── pe.yaml │ │ ├── pg.yaml │ │ ├── ph.yaml │ │ ├── pk.yaml │ │ ├── pl.yaml │ │ ├── pn.yaml │ │ ├── ps.yaml │ │ ├── pt.yaml │ │ ├── pw.yaml │ │ ├── py.yaml │ │ ├── qa.yaml │ │ ├── ro.yaml │ │ ├── rs.yaml │ │ ├── ru.yaml │ │ ├── rw.yaml │ │ ├── sa.yaml │ │ ├── sb.yaml │ │ ├── sc.yaml │ │ ├── sd.yaml │ │ ├── se.yaml │ │ ├── sg.yaml │ │ ├── sh.yaml │ │ ├── si.yaml │ │ ├── sk.yaml │ │ ├── sl.yaml │ │ ├── sm.yaml │ │ ├── sn.yaml │ │ ├── so.yaml │ │ ├── sr.yaml │ │ ├── ss.yaml │ │ ├── st.yaml │ │ ├── sv.yaml │ │ ├── sy.yaml │ │ ├── sz.yaml │ │ ├── tc.yaml │ │ ├── td.yaml │ │ ├── tg.yaml │ │ ├── th.yaml │ │ ├── tj.yaml │ │ ├── tk.yaml │ │ ├── tl.yaml │ │ ├── tm.yaml │ │ ├── tn.yaml │ │ ├── to.yaml │ │ ├── tr.yaml │ │ ├── tt.yaml │ │ ├── tv.yaml │ │ ├── tw.yaml │ │ ├── tz.yaml │ │ ├── ua.yaml │ │ ├── ug.yaml │ │ ├── us.yaml │ │ ├── uy.yaml │ │ ├── uz.yaml │ │ ├── va.yaml │ │ ├── vc.yaml │ │ ├── ve.yaml │ │ ├── vg.yaml │ │ ├── vn.yaml │ │ ├── vu.yaml │ │ ├── ws.yaml │ │ ├── xk.yaml │ │ ├── ye.yaml │ │ ├── za.yaml │ │ ├── zm.yaml │ │ └── zw.yaml │ ├── country_settings.yaml │ ├── env.defaults │ ├── icu-rules/ │ │ ├── extended-unicode-to-asccii.yaml │ │ ├── unicode-digits-to-decimal.yaml │ │ ├── variants-bg.yaml │ │ ├── variants-ca.yaml │ │ ├── variants-cs.yaml │ │ ├── variants-da.yaml │ │ ├── variants-de.yaml │ │ ├── variants-el.yaml │ │ ├── variants-en.yaml │ │ ├── variants-es.yaml │ │ ├── variants-et.yaml │ │ ├── variants-eu.yaml │ │ ├── variants-fi.yaml │ │ ├── variants-fr.yaml │ │ ├── variants-gl.yaml │ │ ├── variants-hu.yaml │ │ ├── variants-it.yaml │ │ ├── variants-mg.yaml │ │ ├── variants-ms.yaml │ │ ├── variants-nl.yaml │ │ ├── variants-no.yaml │ │ ├── variants-pl.yaml │ │ ├── variants-pt.yaml │ │ ├── variants-ro.yaml │ │ ├── variants-ru.yaml │ │ ├── variants-sk.yaml │ │ ├── variants-sl.yaml │ │ ├── variants-sv.yaml │ │ ├── variants-tr.yaml │ │ ├── variants-uk.yaml │ │ └── variants-vi.yaml │ ├── icu_tokenizer.yaml │ └── phrase-settings.json ├── src/ │ ├── nominatim_api/ │ │ ├── __init__.py │ │ ├── config.py │ │ ├── connection.py │ │ ├── core.py │ │ ├── errors.py │ │ ├── localization.py │ │ ├── logging.py │ │ ├── lookup.py │ │ ├── py.typed │ │ ├── query_preprocessing/ │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── config.py │ │ │ ├── normalize.py │ │ │ ├── regex_replace.py │ │ │ └── split_japanese_phrases.py │ │ ├── result_formatting.py │ │ ├── results.py │ │ ├── reverse.py │ │ ├── search/ │ │ │ ├── __init__.py │ │ │ ├── db_search_builder.py │ │ │ ├── db_search_fields.py │ │ │ ├── db_search_lookups.py │ │ │ ├── db_searches/ │ │ │ │ ├── __init__.py │ │ │ │ ├── address_search.py │ │ │ │ ├── base.py │ │ │ │ ├── country_search.py │ │ │ │ ├── near_search.py │ │ │ │ ├── place_search.py │ │ │ │ ├── poi_search.py │ │ │ │ └── postcode_search.py │ │ │ ├── geocoder.py │ │ │ ├── icu_tokenizer.py │ │ │ ├── postcode_parser.py │ │ │ ├── query.py │ │ │ ├── query_analyzer_factory.py │ │ │ └── token_assignment.py │ │ ├── server/ │ │ │ ├── __init__.py │ │ │ ├── asgi_adaptor.py │ │ │ ├── content_types.py │ │ │ ├── falcon/ │ │ │ │ ├── __init__.py │ │ │ │ └── server.py │ │ │ └── starlette/ │ │ │ ├── __init__.py │ │ │ └── server.py │ │ ├── sql/ │ │ │ ├── __init__.py │ │ │ ├── async_core_library.py │ │ │ ├── sqlalchemy_functions.py │ │ │ ├── sqlalchemy_schema.py │ │ │ ├── sqlalchemy_types/ │ │ │ │ ├── __init__.py │ │ │ │ ├── geometry.py │ │ │ │ ├── int_array.py │ │ │ │ ├── json.py │ │ │ │ └── key_value.py │ │ │ └── sqlite_functions.py │ │ ├── status.py │ │ ├── timeout.py │ │ ├── types.py │ │ ├── typing.py │ │ ├── utils/ │ │ │ ├── __init__.py │ │ │ └── json_writer.py │ │ ├── v1/ │ │ │ ├── __init__.py │ │ │ ├── classtypes.py │ │ │ ├── format.py │ │ │ ├── format_json.py │ │ │ ├── format_xml.py │ │ │ ├── helpers.py │ │ │ └── server_glue.py │ │ └── version.py │ └── nominatim_db/ │ ├── __init__.py │ ├── cli.py │ ├── clicmd/ │ │ ├── __init__.py │ │ ├── add_data.py │ │ ├── admin.py │ │ ├── api.py │ │ ├── args.py │ │ ├── convert.py │ │ ├── export.py │ │ ├── freeze.py │ │ ├── index.py │ │ ├── refresh.py │ │ ├── replication.py │ │ ├── setup.py │ │ └── special_phrases.py │ ├── config.py │ ├── data/ │ │ ├── __init__.py │ │ ├── country_info.py │ │ ├── place_info.py │ │ ├── place_name.py │ │ └── postcode_format.py │ ├── db/ │ │ ├── __init__.py │ │ ├── connection.py │ │ ├── properties.py │ │ ├── query_pool.py │ │ ├── sql_preprocessor.py │ │ ├── status.py │ │ └── utils.py │ ├── errors.py │ ├── indexer/ │ │ ├── __init__.py │ │ ├── indexer.py │ │ ├── progress.py │ │ └── runners.py │ ├── paths.py │ ├── tokenizer/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── factory.py │ │ ├── icu_rule_loader.py │ │ ├── icu_token_analysis.py │ │ ├── icu_tokenizer.py │ │ ├── place_sanitizer.py │ │ ├── sanitizers/ │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── clean_housenumbers.py │ │ │ ├── clean_postcodes.py │ │ │ ├── clean_tiger_tags.py │ │ │ ├── config.py │ │ │ ├── delete_tags.py │ │ │ ├── split_name_list.py │ │ │ ├── strip_brace_terms.py │ │ │ ├── tag_analyzer_by_language.py │ │ │ └── tag_japanese.py │ │ └── token_analysis/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── config_variants.py │ │ ├── generic.py │ │ ├── generic_mutation.py │ │ ├── housenumbers.py │ │ ├── postcodes.py │ │ └── simple_trie.py │ ├── tools/ │ │ ├── __init__.py │ │ ├── add_osm_data.py │ │ ├── admin.py │ │ ├── check_database.py │ │ ├── collect_os_info.py │ │ ├── convert_sqlite.py │ │ ├── database_import.py │ │ ├── exec_utils.py │ │ ├── freeze.py │ │ ├── migration.py │ │ ├── postcodes.py │ │ ├── refresh.py │ │ ├── replication.py │ │ ├── special_phrases/ │ │ │ ├── __init__.py │ │ │ ├── importer_statistics.py │ │ │ ├── sp_csv_loader.py │ │ │ ├── sp_importer.py │ │ │ ├── sp_wiki_loader.py │ │ │ └── special_phrase.py │ │ └── tiger_data.py │ ├── typing.py │ ├── utils/ │ │ ├── __init__.py │ │ ├── centroid.py │ │ └── url_utils.py │ └── version.py ├── test/ │ ├── bdd/ │ │ ├── conftest.py │ │ ├── features/ │ │ │ ├── api/ │ │ │ │ ├── details/ │ │ │ │ │ ├── language.feature │ │ │ │ │ ├── params.feature │ │ │ │ │ └── simple.feature │ │ │ │ ├── lookup/ │ │ │ │ │ └── simple.feature │ │ │ │ ├── reverse/ │ │ │ │ │ ├── geometry.feature │ │ │ │ │ ├── language.feature │ │ │ │ │ ├── layers.feature │ │ │ │ │ ├── queries.feature │ │ │ │ │ ├── v1_geocodejson.feature │ │ │ │ │ ├── v1_geojson.feature │ │ │ │ │ ├── v1_json.feature │ │ │ │ │ ├── v1_params.feature │ │ │ │ │ └── v1_xml.feature │ │ │ │ ├── search/ │ │ │ │ │ ├── language.feature │ │ │ │ │ ├── params.feature │ │ │ │ │ ├── postcode.feature │ │ │ │ │ ├── queries.feature │ │ │ │ │ ├── simple.feature │ │ │ │ │ ├── structured.feature │ │ │ │ │ └── v1_geocodejson.feature │ │ │ │ └── status/ │ │ │ │ ├── failures.feature │ │ │ │ └── simple.feature │ │ │ ├── db/ │ │ │ │ ├── import/ │ │ │ │ │ ├── addressing.feature │ │ │ │ │ ├── country.feature │ │ │ │ │ ├── entrances.feature │ │ │ │ │ ├── interpolation.feature │ │ │ │ │ ├── linking.feature │ │ │ │ │ ├── naming.feature │ │ │ │ │ ├── parenting.feature │ │ │ │ │ ├── placex.feature │ │ │ │ │ ├── postcodes.feature │ │ │ │ │ ├── rank_computation.feature │ │ │ │ │ └── search_name.feature │ │ │ │ ├── query/ │ │ │ │ │ ├── housenumbers.feature │ │ │ │ │ ├── interpolation.feature │ │ │ │ │ ├── japanese.feature │ │ │ │ │ ├── linking.feature │ │ │ │ │ ├── normalization.feature │ │ │ │ │ ├── postcodes.feature │ │ │ │ │ ├── reverse.feature │ │ │ │ │ └── search_simple.feature │ │ │ │ └── update/ │ │ │ │ ├── addressing.feature │ │ │ │ ├── country.feature │ │ │ │ ├── entrances.feature │ │ │ │ ├── interpolation.feature │ │ │ │ ├── linked_places.feature │ │ │ │ ├── parenting.feature │ │ │ │ ├── postcode.feature │ │ │ │ └── simple.feature │ │ │ └── osm2pgsql/ │ │ │ ├── import/ │ │ │ │ ├── broken.feature │ │ │ │ ├── custom_style.feature │ │ │ │ ├── entrances.feature │ │ │ │ ├── interpolation.feature │ │ │ │ ├── relation.feature │ │ │ │ ├── simple.feature │ │ │ │ └── tags.feature │ │ │ └── update/ │ │ │ ├── entrances.feature │ │ │ ├── interpolations.feature │ │ │ ├── postcodes.feature │ │ │ ├── relation.feature │ │ │ ├── simple.feature │ │ │ └── tags.feature │ │ ├── test_api.py │ │ ├── test_db.py │ │ ├── test_osm2pgsql.py │ │ └── utils/ │ │ ├── __init__.py │ │ ├── api_result.py │ │ ├── api_runner.py │ │ ├── checks.py │ │ ├── db.py │ │ ├── geometry_alias.py │ │ ├── grid.py │ │ └── place_inserter.py │ ├── python/ │ │ ├── api/ │ │ │ ├── conftest.py │ │ │ ├── fake_adaptor.py │ │ │ ├── query_processing/ │ │ │ │ ├── test_normalize.py │ │ │ │ ├── test_regex_replace.py │ │ │ │ └── test_split_japanese_phrases.py │ │ │ ├── search/ │ │ │ │ ├── test_api_search_query.py │ │ │ │ ├── test_db_search_builder.py │ │ │ │ ├── test_icu_query_analyzer.py │ │ │ │ ├── test_postcode_parser.py │ │ │ │ ├── test_query.py │ │ │ │ ├── test_query_analyzer_factory.py │ │ │ │ ├── test_search_address.py │ │ │ │ ├── test_search_country.py │ │ │ │ ├── test_search_near.py │ │ │ │ ├── test_search_places.py │ │ │ │ ├── test_search_poi.py │ │ │ │ ├── test_search_postcode.py │ │ │ │ └── test_token_assignment.py │ │ │ ├── test_api_connection.py │ │ │ ├── test_api_deletable_v1.py │ │ │ ├── test_api_details.py │ │ │ ├── test_api_lookup.py │ │ │ ├── test_api_polygons_v1.py │ │ │ ├── test_api_reverse.py │ │ │ ├── test_api_search.py │ │ │ ├── test_api_status.py │ │ │ ├── test_api_types.py │ │ │ ├── test_export.py │ │ │ ├── test_helpers_v1.py │ │ │ ├── test_localization.py │ │ │ ├── test_result_formatting_v1.py │ │ │ ├── test_result_formatting_v1_reverse.py │ │ │ ├── test_results.py │ │ │ ├── test_server_glue_v1.py │ │ │ ├── test_timeout.py │ │ │ └── test_warm.py │ │ ├── cli/ │ │ │ ├── conftest.py │ │ │ ├── test_cli.py │ │ │ ├── test_cmd_admin.py │ │ │ ├── test_cmd_api.py │ │ │ ├── test_cmd_import.py │ │ │ ├── test_cmd_index.py │ │ │ ├── test_cmd_refresh.py │ │ │ └── test_cmd_replication.py │ │ ├── config/ │ │ │ ├── test_config.py │ │ │ └── test_config_load_module.py │ │ ├── conftest.py │ │ ├── cursor.py │ │ ├── data/ │ │ │ └── test_country_info.py │ │ ├── db/ │ │ │ ├── test_connection.py │ │ │ ├── test_properties.py │ │ │ ├── test_sql_preprocessor.py │ │ │ ├── test_status.py │ │ │ └── test_utils.py │ │ ├── dummy_tokenizer.py │ │ ├── indexer/ │ │ │ └── test_indexing.py │ │ ├── mock_icu_word_table.py │ │ ├── pytest.ini │ │ ├── tokenizer/ │ │ │ ├── sanitizers/ │ │ │ │ ├── test_clean_housenumbers.py │ │ │ │ ├── test_clean_postcodes.py │ │ │ │ ├── test_clean_tiger_tags.py │ │ │ │ ├── test_delete_tags.py │ │ │ │ ├── test_sanitizer_config.py │ │ │ │ ├── test_split_name_list.py │ │ │ │ ├── test_strip_brace_terms.py │ │ │ │ ├── test_tag_analyzer_by_language.py │ │ │ │ └── test_tag_japanese.py │ │ │ ├── test_factory.py │ │ │ ├── test_icu.py │ │ │ ├── test_icu_rule_loader.py │ │ │ ├── test_place_sanitizer.py │ │ │ └── token_analysis/ │ │ │ ├── test_analysis_postcodes.py │ │ │ ├── test_generic.py │ │ │ ├── test_generic_mutation.py │ │ │ └── test_simple_trie.py │ │ ├── tools/ │ │ │ ├── conftest.py │ │ │ ├── test_add_osm_data.py │ │ │ ├── test_admin.py │ │ │ ├── test_check_database.py │ │ │ ├── test_database_import.py │ │ │ ├── test_exec_utils.py │ │ │ ├── test_freeze.py │ │ │ ├── test_import_special_phrases.py │ │ │ ├── test_migration.py │ │ │ ├── test_postcodes.py │ │ │ ├── test_refresh.py │ │ │ ├── test_refresh_address_levels.py │ │ │ ├── test_refresh_create_functions.py │ │ │ ├── test_refresh_wiki_data.py │ │ │ ├── test_replication.py │ │ │ ├── test_sp_csv_loader.py │ │ │ ├── test_sp_importer.py │ │ │ ├── test_sp_wiki_loader.py │ │ │ └── test_tiger_data.py │ │ └── utils/ │ │ ├── test_centroid.py │ │ └── test_json_writer.py │ ├── testdata/ │ │ ├── sp_csv_test.csv │ │ └── special_phrases_test_content.txt │ └── testdb/ │ ├── additional_api_test.data.osm │ ├── apidb-test-data.pbf │ ├── full_en_phrases_test.csv │ └── tiger/ │ └── 01001.csv ├── utils/ │ ├── import_multiple_regions.sh │ └── update_database.sh └── vagrant/ ├── Install-on-Ubuntu-22.sh └── Install-on-Ubuntu-24.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .codespellrc ================================================ # https://github.com/codespell-project/codespell [codespell] skip = ./man/nominatim.1,data,./docs/styles.css,lib-php,module,munin,osm2pgsql,./test,./settings/*.lua,./settings/*.yaml,./settings/**/*.yaml,./settings/icu-rules,./nominatim/tokenizer/token_analysis/config_variants.py # Need to be lowercase in the list # Unter = Unter den Linden (an example address) ignore-words-list = inout,unter ================================================ FILE: .flake8 ================================================ [flake8] max-line-length = 100 max-doc-length = 100 extend-ignore = # something == None constructs are needed for SQLAlchemy E711 per-file-ignores = __init__.py: F401 test/python/utils/test_json_writer.py: E131 **/conftest.py: E402 test/bdd/*: F821 ================================================ FILE: .github/FUNDING.yml ================================================ github: lonvia custom: "https://nominatim.org/funding/" ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ contact_links: - name: Nominatim Discussions url: https://github.com/osm-search/Nominatim/discussions about: Ask questions, get support, share ideas and discuss with community members. - name: Discussions about OpenStreetMap data url: https://community.openstreetmap.org/ about: Ask questions about the data used by Nominatim and discuss with the OSM community. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** **Describe the solution you'd like** **Describe alternatives you've considered** **Additional context** ================================================ FILE: .github/ISSUE_TEMPLATE/report-issues-with-search-results.md ================================================ --- name: Report issues with search results about: You have searched something with Nominatim and did not get the expected result. title: '' labels: '' assignees: '' --- ## What did you search for? ## What result did you get? ## What result did you expect? **When the result is in the right place and just named wrongly:** **When the result is missing completely:** ## Further details ================================================ FILE: .github/ISSUE_TEMPLATE/report-problems-with-the-software.md ================================================ --- name: Report problems with the software about: You have your own installation of Nominatim and found a bug. title: '' labels: '' assignees: '' --- **Describe the bug** **To Reproduce** **Software Environment (please complete the following information):** - Nominatim version: - Postgresql version: - Postgis version: - OS: **Hardware Configuration (please complete the following information):** - RAM: - number of CPUs: - type and size of disks: **Postgresql Configuration:** **Nominatim Configuration:** **Additional context** ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ## Summary ## AI usage ## Contributor guidelines (mandatory) - [ ] I have adhered to the [coding style](https://github.com/osm-search/Nominatim/blob/master/CONTRIBUTING.md#coding-style) - [ ] I have [tested](https://github.com/osm-search/Nominatim/blob/master/CONTRIBUTING.md#testing) the proposed changes - [ ] I have [disclosed](https://github.com/osm-search/Nominatim/blob/master/CONTRIBUTING.md#using-ai-assisted-code-generators) above any use of AI to generate code, documentation, or the pull request description ================================================ FILE: .github/actions/build-nominatim/action.yml ================================================ name: 'Build Nominatim' inputs: dependencies: description: 'Where to install dependencies from (pip/apt)' required: false default: 'pip' runs: using: "composite" steps: - name: Clean out the disk run: | sudo rm -rf /opt/hostedtoolcache/go /opt/hostedtoolcache/CodeQL /usr/lib/jvm /usr/local/share/chromium /usr/local/lib/android df -h shell: bash - name: Install general prerequisites run: | sudo apt-get install -y -qq libspatialite-dev libsqlite3-mod-spatialite libicu-dev virtualenv python3-dev osm2pgsql shell: bash - name: Install prerequisites from apt run: | sudo apt-get install -y -qq python3-icu python3-datrie python3-jinja2 python3-psutil python3-dotenv python3-yaml python3-sqlalchemy python3-psycopg python3-asyncpg python3-mwparserfromhell shell: bash if: inputs.dependencies == 'apt' - name: Setup virtual environment (for pip) run: | virtualenv venv ./venv/bin/pip install -U pip shell: bash if: inputs.dependencies == 'pip' - name: Setup virtual environment (for apt) run: | virtualenv venv --system-site-packages shell: bash if: inputs.dependencies == 'apt' - name: Build nominatim run: ./venv/bin/pip install Nominatim/packaging/nominatim-{api,db} shell: bash ================================================ FILE: .github/actions/setup-postgresql/action.yml ================================================ name: 'Setup Postgresql and Postgis' description: 'Installs PostgreSQL and PostGIS and configures it for CI tests' inputs: postgresql-version: description: 'Version of PostgreSQL to install' required: true runs: using: "composite" steps: - name: Remove existing PostgreSQL run: | sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y sudo apt-get purge -yq postgresql* sudo apt-get update -qq shell: bash - name: Install PostgreSQL run: | sudo apt-get install -y -qq --no-install-suggests --no-install-recommends postgresql-client-${PGVER} postgresql-${PGVER}-postgis-3 postgresql-${PGVER}-postgis-3-scripts postgresql-contrib-${PGVER} postgresql-${PGVER} shell: bash env: PGVER: ${{ inputs.postgresql-version }} - name: Adapt postgresql configuration run: | echo 'fsync = off' | sudo tee /etc/postgresql/${PGVER}/main/conf.d/local.conf echo 'synchronous_commit = off' | sudo tee -a /etc/postgresql/${PGVER}/main/conf.d/local.conf echo 'full_page_writes = off' | sudo tee -a /etc/postgresql/${PGVER}/main/conf.d/local.conf echo 'shared_buffers = 1GB' | sudo tee -a /etc/postgresql/${PGVER}/main/conf.d/local.conf echo 'port = 5432' | sudo tee -a /etc/postgresql/${PGVER}/main/conf.d/local.conf shell: bash env: PGVER: ${{ inputs.postgresql-version }} - name: Setup database run: | sudo systemctl restart postgresql sudo -u postgres createuser -S www-data sudo -u postgres createuser -s runner shell: bash ================================================ FILE: .github/actions/setup-postgresql-windows/action.yml ================================================ name: 'Setup Postgresql and Postgis on Windows' description: 'Installs PostgreSQL and PostGIS for Windows and configures it for CI tests' inputs: postgresql-version: description: 'Version of PostgreSQL to install' required: true runs: using: "composite" steps: - name: Set up PostgreSQL variables shell: pwsh run: | $version = "${{ inputs.postgresql-version }}" $root = "C:\Program Files\PostgreSQL\$version" $bin = "$root\bin" echo "PGROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append echo "PGBIN=$bin" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append echo "$bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Decide Postgis version (Windows) id: postgis-ver shell: pwsh run: | echo "PowerShell version: ${PSVersionTable.PSVersion}" $PG_VERSION = Split-Path $env:PGROOT -Leaf $postgis_page = "https://download.osgeo.org/postgis/windows/pg$PG_VERSION" echo "Detecting PostGIS version from $postgis_page for PostgreSQL $PG_VERSION" $pgis_bundle = (Invoke-WebRequest -Uri $postgis_page -ErrorAction Stop).Links.Where({$_.href -match "^postgis.*zip$"}).href if (!$pgis_bundle) { Write-Error "Could not find latest PostGIS version in $postgis_page that would match ^postgis.*zip$ pattern" exit 1 } $pgis_bundle = [IO.Path]::ChangeExtension($pgis_bundle, [NullString]::Value) $pgis_bundle_url = "$postgis_page/$pgis_bundle.zip" Add-Content $env:GITHUB_OUTPUT "postgis_file=$pgis_bundle" Add-Content $env:GITHUB_OUTPUT "postgis_bundle_url=$pgis_bundle_url" - uses: actions/cache@v5 with: path: | C:/postgis.zip key: postgis-cache-${{ steps.postgis-ver.outputs.postgis_file }} - name: Download postgis shell: pwsh run: | if (!(Test-Path "C:\postgis.zip")){(new-object net.webclient).DownloadFile($env:PGIS_BUNDLE_URL, "c:\postgis.zip")} if (Test-path "c:\postgis_archive"){Remove-Item "c:\postgis_archive" -Recurse -Force} 7z x c:\postgis.zip -oc:\postgis_archive env: PGIS_BUNDLE_URL: ${{ steps.postgis-ver.outputs.postgis_bundle_url }} - name: Install postgis shell: bash run: | echo "Root: $PGROOT, Bin: $PGBIN" cp -r c:/postgis_archive/postgis-bundle-*/* "$PGROOT" - name: Start PostgreSQL on Windows run: | $pgService = Get-Service -Name postgresql* Set-Service -InputObject $pgService -Status running -StartupType automatic Start-Process -FilePath "$env:PGBIN\pg_isready" -Wait -PassThru shell: pwsh - name: Adapt postgresql configuration shell: pwsh env: PGPASSWORD: root run: | & "$env:PGBIN\psql" -U postgres -d postgres -c "ALTER SYSTEM SET fsync = 'off';" & "$env:PGBIN\psql" -U postgres -d postgres -c "ALTER SYSTEM SET synchronous_commit = 'off';" & "$env:PGBIN\psql" -U postgres -d postgres -c "ALTER SYSTEM SET full_page_writes = 'off';" & "$env:PGBIN\psql" -U postgres -d postgres -c "ALTER SYSTEM SET shared_buffers = '1GB';" & "$env:PGBIN\psql" -U postgres -d postgres -c "ALTER SYSTEM SET port = 5432;" Restart-Service -Name postgresql* Start-Process -FilePath "$env:PGBIN\pg_isready" -Wait -PassThru - name: Setup database users shell: pwsh env: PGPASSWORD: root run: | & "$env:PGBIN\createuser" -U postgres -S www-data & "$env:PGBIN\createuser" -U postgres -s runner ================================================ FILE: .github/workflows/ci-tests.yml ================================================ name: CI Tests on: [ push, pull_request ] jobs: create-archive: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: submodules: true - uses: actions/cache@v5 with: path: | data/country_osm_grid.sql.gz key: nominatim-country-data-1 - name: Package tarball run: | if [ ! -f data/country_osm_grid.sql.gz ]; then wget --no-verbose -O data/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz fi cd .. tar czf nominatim-src.tar.bz2 Nominatim mv nominatim-src.tar.bz2 Nominatim - name: 'Upload Artifact' uses: actions/upload-artifact@v7 with: name: full-source path: nominatim-src.tar.bz2 retention-days: 1 tests: needs: create-archive strategy: matrix: flavour: ["ubuntu-22", "ubuntu-24"] include: - flavour: ubuntu-22 ubuntu: 22 postgresql: 12 lua: '5.1' dependencies: pip python: '3.9' - flavour: ubuntu-24 ubuntu: 24 postgresql: 18 lua: '5.3' dependencies: apt python: 'builtin' runs-on: ubuntu-${{ matrix.ubuntu }}.04 steps: - uses: actions/download-artifact@v8 with: name: full-source - name: Unpack Nominatim run: tar xf nominatim-src.tar.bz2 - uses: ./Nominatim/.github/actions/setup-postgresql with: postgresql-version: ${{ matrix.postgresql }} - uses: ./Nominatim/.github/actions/build-nominatim with: dependencies: ${{ matrix.dependencies }} - uses: actions/cache@v5 with: path: | /usr/local/bin/osm2pgsql key: osm2pgsql-bin-22-1 if: matrix.ubuntu == '22' - name: Set up Python uses: actions/setup-python@v6 with: python-version: ${{ matrix.python }} if: matrix.python != 'builtin' - name: Compile osm2pgsql run: | if [ ! -f /usr/local/bin/osm2pgsql ]; then sudo apt-get install -y -qq libboost-system-dev libboost-filesystem-dev libexpat1-dev zlib1g-dev libbz2-dev libpq-dev libproj-dev libicu-dev liblua${LUA_VERSION}-dev lua-dkjson nlohmann-json3-dev mkdir osm2pgsql-build cd osm2pgsql-build git clone https://github.com/osm2pgsql-dev/osm2pgsql mkdir build cd build cmake ../osm2pgsql make sudo make install cd ../.. rm -rf osm2pgsql-build else sudo apt-get install -y -qq libexpat1 liblua${LUA_VERSION} fi if: matrix.ubuntu == '22' env: LUA_VERSION: ${{ matrix.lua }} - name: Install test prerequisites (apt) run: sudo apt-get install -y -qq python3-pytest python3-pytest-asyncio uvicorn python3-falcon python3-aiosqlite python3-pyosmium if: matrix.dependencies == 'apt' - name: Install test prerequisites (pip) run: ./venv/bin/pip install pytest-asyncio falcon starlette asgi_lifespan aiosqlite osmium uvicorn if: matrix.dependencies == 'pip' - name: Install test prerequisites run: ./venv/bin/pip install pytest-bdd - name: Install latest flake8 run: ./venv/bin/pip install -U flake8 - name: Python linting run: ../venv/bin/python -m flake8 src test/python test/bdd working-directory: Nominatim - name: Install mypy and typechecking info run: ./venv/bin/pip install -U mypy types-PyYAML types-jinja2 types-psutil types-requests types-ujson types-Pygments typing-extensions if: matrix.dependencies == 'pip' - name: Python static typechecking run: ../venv/bin/python -m mypy --strict --python-version 3.9 src working-directory: Nominatim if: matrix.dependencies == 'pip' - name: Python unit tests run: ../venv/bin/python -m pytest test/python working-directory: Nominatim - name: BDD tests run: | ../venv/bin/python -m pytest test/bdd --nominatim-purge working-directory: Nominatim tests-windows: needs: create-archive runs-on: windows-latest steps: - uses: actions/download-artifact@v8 with: name: full-source - name: Unpack Nominatim run: tar xf nominatim-src.tar.bz2 - uses: ./Nominatim/.github/actions/setup-postgresql-windows with: postgresql-version: 17 - name: Set up Python uses: actions/setup-python@v6 with: python-version: '3.14' - name: Install Spatialite run: | Invoke-WebRequest -Uri "https://www.gaia-gis.it/gaia-sins/windows-bin-amd64/mod_spatialite-5.1.0-win-amd64.7z" -OutFile "spatialite.7z" 7z x spatialite.7z -o"C:\spatialite" echo "C:\spatialite\mod_spatialite-5.1.0-win-amd64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install osm2pgsql run: | Invoke-WebRequest -Uri "https://osm2pgsql.org/download/windows/osm2pgsql-latest-x64.zip" -OutFile "osm2pgsql.zip" Expand-Archive -Path "osm2pgsql.zip" -DestinationPath "C:\osm2pgsql" $BinDir = Get-ChildItem -Path "C:\osm2pgsql" -Recurse -Filter "osm2pgsql.exe" | Select-Object -ExpandProperty DirectoryName | Select-Object -First 1 if (-not $BinDir) { Write-Error "Could not find osm2pgsql.exe" exit 1 } echo "$BinDir" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append $FullExePath = Join-Path $BinDir "osm2pgsql.exe" echo "NOMINATIM_OSM2PGSQL_BINARY=$FullExePath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Set UTF-8 encoding run: | [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8 - name: Install PyICU from wheel run: | python -m pip install https://github.com/cgohlke/pyicu-build/releases/download/v2.16.0/pyicu-2.16-cp314-cp314-win_amd64.whl - name: Install test prerequisites run: | python -m pip install -U pip python -m pip install pytest pytest-asyncio "psycopg[binary]!=3.3.0" python-dotenv pyyaml jinja2 psutil sqlalchemy pytest-bdd falcon starlette uvicorn asgi_lifespan aiosqlite osmium mwparserfromhell - name: Python unit tests run: | python -m pytest test/python -k "not (import_osm or run_osm2pgsql)" working-directory: Nominatim install: runs-on: ubuntu-latest needs: create-archive strategy: matrix: name: [Ubuntu-22, Ubuntu-24] include: - name: Ubuntu-22 image: "ubuntu:22.04" ubuntu: 22 install_mode: install-apache - name: Ubuntu-24 image: "ubuntu:24.04" ubuntu: 24 install_mode: install-apache container: image: ${{ matrix.image }} env: LANG: en_US.UTF-8 defaults: run: shell: sudo -Hu nominatim bash --noprofile --norc -eo pipefail {0} steps: - name: Prepare container (Ubuntu) run: | export APT_LISTCHANGES_FRONTEND=none export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y git sudo wget ln -snf /usr/share/zoneinfo/$CONTAINER_TIMEZONE /etc/localtime && echo $CONTAINER_TIMEZONE > /etc/timezone shell: bash - name: Setup import user run: | useradd -m nominatim echo 'nominatim ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/nominiatim echo "/home/nominatim/Nominatim/vagrant/Install-on-${OS}.sh no $INSTALL_MODE" > /home/nominatim/vagrant.sh shell: bash env: OS: ${{ matrix.name }} INSTALL_MODE: ${{ matrix.install_mode }} - uses: actions/download-artifact@v8 with: name: full-source path: /home/nominatim - name: Install Nominatim run: | export USERNAME=nominatim export USERHOME=/home/nominatim export NOSYSTEMD=yes export HAVE_SELINUX=no tar xf nominatim-src.tar.bz2 . vagrant.sh working-directory: /home/nominatim - name: Prepare import environment run: | mv Nominatim/test/testdb/apidb-test-data.pbf test.pbf rm -rf Nominatim mkdir data-env-reverse working-directory: /home/nominatim - name: Add nominatim to path run: | sudo ln -s /home/nominatim/nominatim-venv/bin/nominatim /usr/local/bin/nominatim - name: Need lua binary run: | sudo apt-get install -y lua5.4 lua-dkjson - name: Print version run: nominatim --version working-directory: /home/nominatim/nominatim-project - name: Print taginfo run: lua ./nominatim-venv/lib/*/site-packages/nominatim_db/resources/lib-lua/taginfo.lua working-directory: /home/nominatim - name: Collect host OS information run: nominatim admin --collect-os-info working-directory: /home/nominatim/nominatim-project - name: Import run: nominatim import --osm-file ../test.pbf working-directory: /home/nominatim/nominatim-project - name: Import special phrases run: nominatim special-phrases --import-from-wiki working-directory: /home/nominatim/nominatim-project - name: Check full import run: nominatim admin --check-database working-directory: /home/nominatim/nominatim-project - name: Warm up database run: nominatim admin --warm working-directory: /home/nominatim/nominatim-project - name: Install osmium run: | /home/nominatim/nominatim-venv/bin/pip install osmium - name: Run update run: | nominatim replication --init NOMINATIM_REPLICATION_MAX_DIFF=1 nominatim replication --once working-directory: /home/nominatim/nominatim-project - name: Clean up database run: nominatim refresh --postcodes --word-tokens working-directory: /home/nominatim/nominatim-project - name: Run reverse-only import run : | echo 'NOMINATIM_DATABASE_DSN="pgsql:dbname=reverse"' >> .env nominatim import --osm-file ../test.pbf --reverse-only --no-updates working-directory: /home/nominatim/data-env-reverse - name: Check reverse-only import run: nominatim admin --check-database working-directory: /home/nominatim/data-env-reverse - name: Clean up database (reverse-only import) run: nominatim refresh --postcodes --word-tokens working-directory: /home/nominatim/nominatim-project install-no-superuser: runs-on: ubuntu-24.04 needs: create-archive steps: - uses: actions/download-artifact@v8 with: name: full-source - name: Unpack Nominatim run: tar xf nominatim-src.tar.bz2 - uses: ./Nominatim/.github/actions/setup-postgresql with: postgresql-version: 16 - uses: ./Nominatim/.github/actions/build-nominatim - name: Prepare import environment run: | mv Nominatim/test/testdb/apidb-test-data.pbf test.pbf rm -rf Nominatim - name: Prepare Database run: | ./venv/bin/nominatim import --prepare-database - name: Create import user run: | sudo -u postgres createuser osm-import psql -d nominatim -c "ALTER USER \"osm-import\" WITH PASSWORD 'osm-import'" psql -d nominatim -c 'GRANT CREATE ON SCHEMA public TO "osm-import"' - name: Run import run: | NOMINATIM_DATABASE_DSN="pgsql:host=127.0.0.1;dbname=nominatim;user=osm-import;password=osm-import" ./venv/bin/nominatim import --continue import-from-file --osm-file test.pbf - name: Check full import run: ./venv/bin/nominatim admin --check-database migrate: runs-on: ubuntu-24.04 needs: create-archive steps: - uses: actions/download-artifact@v8 with: name: full-source - name: Unpack Nominatim run: tar xf nominatim-src.tar.bz2 - uses: ./Nominatim/.github/actions/setup-postgresql with: postgresql-version: 18 - name: Install Python dependencies run: | sudo apt-get install --no-install-recommends virtualenv osm2pgsql - name: Install Nominatim master version run: | virtualenv master cd Nominatim ../master/bin/pip install packaging/nominatim-db - name: Install Nominatim from pypi run: | virtualenv release ./release/bin/pip install nominatim-db - name: Import Nominatim database using release run: | ./release/bin/nominatim import --osm-file Nominatim/test/testdb/apidb-test-data.pbf ./release/bin/nominatim add-data --file Nominatim/test/testdb/additional_api_test.data.osm - name: Migrate to master version run: | ./master/bin/nominatim admin --migrate ./release/bin/nominatim add-data --file Nominatim/test/testdb/additional_api_test.data.osm ================================================ FILE: .gitignore ================================================ *.log *.pyc *.swp docs/develop/*.png site-html build dist .coverage .vagrant data/country_osm_grid.sql.gz ================================================ FILE: .gitmodules ================================================ ================================================ FILE: .mypy.ini ================================================ [mypy] plugins = sqlalchemy.ext.mypy.plugin [mypy-icu.*] ignore_missing_imports = True [mypy-asyncpg.*] ignore_missing_imports = True [mypy-dotenv.*] ignore_missing_imports = True ================================================ FILE: AUTHORS ================================================ Nominatim was written by: * Brian Quinion * Sarah Hoffmann * Marc Tobias Metten * markigail * AntoJvlt * gemo1011 * darkshredder and many more. For a full list of contributors see the Git logs or visit https://github.com/openstreetmap/Nominatim/graphs/contributors ================================================ FILE: CONTRIBUTING.md ================================================ # Nominatim contribution guidelines ## Reporting Bugs Bugs can be reported at https://github.com/openstreetmap/Nominatim/issues. Please always open a separate issue for each problem. In particular, do not add your bugs to closed issues. They may look similar to you but often are completely different from the maintainer's point of view. If you just have a question or when you are not sure if what you have found is an actual bug, use the [Discussion section](https://github.com/openstreetmap/Nominatim/discussions). If you have questions about the underlying OpenStreetMap data rather than the geocoding software, then the [OpenStreetMap forum](https://community.openstreetmap.org/) is the best place to start. ## Workflow for Pull Requests We love to get pull requests from you. We operate the "Fork & Pull" model explained at https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests You should fork the project into your own repo, create a topic branch there and then make a single pull requests back to the main repository. Your pull requests will then be reviewed and discussed. Enable CI (we use Github Actions workflows) on your fork. It will run the full test suite whenever you push to one of your branches. https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository Please make sure to follow these guidelines: * Make sure CI passes _before_ opening the pull request. Check the "Actions" tab in your repo to make sure everything works. * Make sure that you have time to react to these comments and amend the code or engage in a conversation. Do not expect that others will pick up your code, it will almost never happen. * Open a separate pull request for each issue you want to address. Don't mix multiple changes. In particular, don't mix style cleanups with feature pull requests (exceptions, see 'Style modernisation' below). * For small fixes and amendments open a PR directly. If you plan to make larger changes, please open an issue first or comment on the appropriate issue to outline your planned implementation. ### Using AI-assisted code generators PRs that include AI-generated content, may that be in code, in the PR description or in documentation need to 1. clearly mark the AI-generated sections as such, for example, by mentioning all use of AI in the PR description, and 2. include proof that you have run the generated code on an actual installation of Nominatim. Adding and executing tests will not be sufficient. You need to show that the code actually solves the problem the PR claims to solve. ## Getting Started with Development Please see the development section of the Nominatim documentation for * [an architecture overview](https://nominatim.org/release-docs/develop/develop/overview/) and backgrounds on some of the algorithms * [how to set up a development environment](https://nominatim.org/release-docs/develop/develop/Development-Environment/) * and background on [how tests are organised](https://nominatim.org/release-docs/develop/develop/Testing/) ## Coding style The coding style for Python is enforced with flake8. It can be tested with: ``` make lint ``` SQL code is currently not linted but should follow the following rules: * 2 spaces indentation * UPPER CASE for all SQL keywords ### Style modernisation There are a few places where we modernize code style as we go. The following changes can be made when you touch the code anyway: * update copyright date in the file header to the current year * switch Python code to use [generics in standard collections](https://docs.python.org/3/whatsnew/3.9.html#type-hinting-generics-in-standard-collections) ## Testing Before submitting a pull request make sure that the tests pass: ``` make tests ``` ## Releases Nominatim follows semantic versioning. Major releases are done for large changes that require (or at least strongly recommend) a reimport of the databases. Minor releases can usually be applied to existing databases. Patch releases contain bug fixes only and are released from a separate branch where the relevant changes are cherry-picked from the master branch. Checklist for releases: * [ ] increase versions in * `src/nominatim_api/version.py` * `src/nominatim_db/version.py` * [ ] update `ChangeLog` (copy information from patch releases from release branch) * [ ] complete `docs/admin/Migration.md` * [ ] update EOL dates in `SECURITY.md` * [ ] commit and make sure CI tests pass * [ ] update OSMF production repo and release new version -post1 there * [ ] test migration * download, build and import previous version * migrate using master version * run updates using master version * [ ] prepare tarball: * `git clone https://github.com/osm-search/Nominatim` (switch to right branch!) * `rm -r .git*` * copy country data into `data/` * add version to base directory and package * [ ] upload tarball to https://nominatim.org * [ ] prepare documentation * check out new docs branch * change git checkout instructions to tarball download instructions or adapt version on existing ones * build documentation and copy to https://github.com/osm-search/nominatim-org-site * add new version to history * [ ] check release tarball * download tarball as per new documentation instructions * compile and import Nominatim * run `nominatim --version` to confirm correct version * [ ] tag new release and add a release on github.com * [ ] build pip packages and upload to pypi * `make build` * `twine upload dist/*` ================================================ FILE: COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: ChangeLog ================================================ 5.3.0 * reorganise postcode and interpolation handling into separate tables * add table for handling associatedStreet relations (thanks @Itz-Agasta) * allow TIGER data imports on frozen tables (thanks @AyushDharDubey) * add layer filtering option to search cli command (thanks @Itz-Agasta) * add support for addr:interpolation together with addr:housenumber * add support for exclude_place_ids to take an OSM ID (thanks @Itz-Agasta) * add more abbreviations for Malay (thanks @harithilmi) and Pakistan (thanks @bgo-eiu) * adaptions to boundary processing in Czech Republic, Australia and Brazil * return admin_level tag in extratags (thanks @kad-link) * clean up table creation SQL and add more constraints * add new cli administration tool to grant read rights to a new user (thanks @Itz-Agasta) * remove uvicorn from the deployment instructions, guvicorn is now sufficient * fix entrance output for reverse and lookup * fix performance issue with instantiation of config (thanks @AyushDharDubey) * fix badly quoted SQL * ensure use of UTF-8 everywhere (thanks @kad-link) * fix endless loop when indexing only partial ranks (thanks @jayaddison) * fix erroring on /search endpoint when Postgresql wasn't running on startup * improve searching addresses by addr:* tags (thanks @Itz-Agasta) * add langauge-aware penalty for matching country names (thanks @Itz-Agasta) * compute default langauge also for linked place names (thanks @jayaddison) * use mwparserfromhell for parsing wiki pages (thanks @AyushDharDubey) * refactor table creation SQL to be reusable in tests * reduce SQL queries for reverse * enforce non-null importance and remove all code that expects otherwise * add rematching against country names * import now reports on total time (thanks @daishu0000) * give wikidata matches preference over name matching when linking places * removed nat_name from list of parsed names * exclude zero-only postcodes * type annotation updates due to dependency changes (thanks @vytas5) * improve display of test names for BDD tests * enable CI unit tests on Windows (thanks @kad-link) * remove unused settings from the documentation * various fixes and improvements to documentation (thanks @28Deepakpandey, @Aditya30ag, @AmmarYasser455, @jonas-endter-optimax-energy, @vai67) 5.2.0 * increase minimum required Python to 3.9 * index and output entrances of buildings and areas (thanks @emlove) * name tags used for creating display names are now configurable (thanks @astridx) * new pattern-replacement query preprocessor (thanks @TuringVerified) * special phrases can now be filtered by presence of tags (thanks @anqixxx) * lua import style now always includes tags required by Nominatim * improved query time reporting and logging * improve word matching for languages with no word boundaries * POIs with addresses inherited from surrounding building are no longer returned in the address layer * avoid creating a directory for the tokenizer when not needed * replace behave with pytest-bdd for BDD testing * refactoring and performance improvements to query parsing * various smaller updates to styles * remove English as default language for South Korea * remove Japanese word variants * updated country names for Norwegians (thanks @Johannes-Andersen) * remove support for deprecated osm2pgsql gazetteer style * fix updating of importances (also needs to update search_name table) * fix query for deletable endpoint to use index again * fix reindexing of contained places when a boundary is deleted and reinstated * fix difference computation error when updating postcodes * bracket handling sanitizer no longer strips bracket terms in the middle of name * reduce precision of stored coordinates to 7-digits everywhere * avoid ST_Relate as it seems buggy on some systems * remove setting for logging queries in DB, no longer functional * postcode updates no longer require a project directory (needed for tests) * refactor locale handling code (thanks @anqixxx) * code updates for newer Python (thanks @emmanuel-ferdman) * better test coverage (thanks @asharmalik19) * various fixes and improvements to documentation (thanks @anqixxx, @dave-meyer, @hasandiwan) 5.1.0 * replace datrie with simple internal trie implementation * add pattern-based postcode parser for queries, postcodes no longer need to be present in OSM to be found * take variants into account when computing token similarity * add extratags output to geocodejson format * fix default layer setting used for structured queries * update abbreviation lists for Russian and English (thanks @shoorick, @IvanShift, @mhsrn21) * fix variant generation for Norwegian * fix normalization around space-like characters * improve postcode search and handling of postcodes in queries * reorganise internal query structure and get rid of slow enums * enable code linting for tests * various code moderinsations in test code (thanks @eumiro) * remove setting osm2pgsql location via config.lib_dir * make SQL functions parallel save as far as possible (thanks @otbutz) * various fixes and improvements to documentation (thanks @TuringVerified) 5.0.0 * increase required versions for PostgreSQL (12+), PostGIS (3.0+) * remove installation via cmake and debundle osm2pgsql * remove deprecated PHP frontend * remove deprecated legacy tokenizer * add configurable pre-processing of queries * add query pre-processor to split up Japanese addresses * rewrite of osm2pgsql style implementation (also adds support for osm2pgsql-themepark) * reduce the number of SQL queries needed to complete a 'lookup' call * improve computation of centroid for lines with only two points * improve bbox output for postcode areas * improve result order by returning the largest object when other things are equal * add fallback for reverse geocoding to default country tables * exclude postcode areas from reverse geocoding * disable search endpoint when database is reverse-only (regression) * minor performance improvements to area split algorithm * switch table and index creation to use autocommit mode to avoid deadlocks * drop overly long ways during import * restrict automatic migrations to versions 4.3+ * switch linting from pylint to flake8 * switch tests to use a wikimedia test file in the new CSV style * various fixes and improvements to documentation 4.5.0 * allow building Nominatim as a pip package * make osm2pgsql building optional * switch importer to psycopg3 * allow output format of web search to be customized in self-installations * look up potential postcode areas for postcode results * add word usage statistics for address terms * implement more light-weight CSV format for wiki importance tables * rewrite SQL for place search to use window functions * increase search radius when filtering by postcode * prefer POI points over POI areas * reintroduce full terms for address terms in search_name table * reindex postcodes when their parent is deleted * indexing: precompute counts of affected rows * ensure consistent country assignments for overlapping countries * make Nominatim[Async]API context manager to ensure proper calling of close() * make usage of project dir optional for library * drop interpolations when no parent can be found * style tweaks to reflect OSM usage (man_made, highway and others) * deprecation of: bundled osm2pgsql, legacy tokenizer, PHP frontend * make documentation buildable without CMake * various fixes and improvements to documentation 4.4.1 * fix geocodejson output: admin level output should only print boundaries * updating: restrict invalidation of child objects on large street features * restrict valid interpolation house numbers to 0-999999 * fix import error when SQLAlchemy 1.4 and psycopg3 are installed * various typo fixes in the documentation 4.4.0 * add export to SQLite database and SQLite support for the frontend * switch to Python frontend as the default frontend * update to osm2pgsql 1.11.0 * add support for new osm2pgsql middle table format * simplify geometry for large polygon objects not used in addresses * various performance tweaks for search in Python frontend * fix regression in search with categories where it was confused with near search * partially roll back use of SQLAlchemy lambda statements due to bugs in SQLAlchemy * fix handling of timezones for timestamps from the database * fix handling of full address searches in connection with a viewbox * fix postcode computation of highway areas * fix handling of timeout errors for Python <= 3.10 * fix address computation for postcode areas * fix variable shadowing in osm2pgsql flex script, causing bugs with LuaJIT * make sure extratags are always null when empty * reduce importance of places without wikipedia reference * improve performance of word count computations * drop support for wikipedia tags with full URLs * replace get_addressdata() SQL implementation with a Python function * improve display name for non-address features * fix postcode validation for postcodes with country code (thanks @pawel-wroniszewski) * add possibility to run imports without superuser database rights (thanks @robbe-haesendonck) * new CLI command for cleaning deleted relations (thanks @lujoh) * add check for database version in the CLI check command * updates to import styles ignoring more unused objects * various typo fixes (thanks @kumarUjjawal) 4.3.2 * fix potential SQL injection issue for 'nominatim admin --collect-os-info' * PHP frontend: fix on-the-fly lookup of postcode areas near boundaries * Python frontend: improve handling of viewbox * Python frontend: correct deployment instructions 4.3.1 * reintroduce result rematching * improve search of multi-part names * fix accidentally switched meaning of --reverse-only and --search-only in warm command 4.3.0 * fix failing importance recalculation command * fix merging of linked names into unnamed boundaries * fix a number of corner cases with interpolation splitting resulting in invalid geometries * fix failure in website generation when password contains curly brackets * fix broken use of ST_Project in PostGIS 3.4 * new NOMINATIM_SEARCH_WITHIN_COUNTRIES setting to restrict reverse lookups to known countries (thanks @alfmarcua) * allow negative OSM IDs (thanks @alfmarcua) * disallow import of Tiger data in a frozen DB * avoid UPDATE to change settings to be compatible with r/o DBs (thanks @t-tomek) * update bundled osm2pgsql to 1.9.2 * reorganise osm2pgsql flex style and make it the default * exclude names ending in :wikipedia from indexing * no longer accept comma as a list separator in name tags * process forward dependencies on update to catch updates in geometries of ways and relations * fix handling of isolated silent letters during transliteration * no longer assign postcodes to large linear features like rivers * introduce nominatim.paths module for finding data and libraries * documentation layout changed to material theme * new documentation section for library * various smaller fixes to existing documentation (thanks @woodpeck, @bloom256, @biswajit-k) * updates to vagrant install scripts, drop support for Ubuntu 18 (thanks @n-timofeev) * removed obsolete configuration variables from env.defaults * add script for generating a taginfo description (thanks @biswajit-k) * modernize Python code around BDD test and add testing of Python frontend * lots of new BDD tests for API output 4.2.3 * fix deletion handling for 'nominatim add-data' * adapt place_force_delete() to new deletion handling * flex style: avoid dropping of postcode areas * fix update errors on address interpolation handling 4.2.2 * extend flex-style library to fully support all default styles * fix handling of Hebrew aleph * do not assign postcodes to rivers * fix string matching in PHP code * update osm2pgsql (various updates to flex) * fix slow query when deleting places on update * fix CLI details query * fix recalculation of importance values * fix polygon simplification in reverse results * add class/type information to reverse geocodejson result * minor improvements to default tokenizer configuration * various smaller fixes to documentation 4.2.1 * fix XSS vulnerability in debug view 4.2.0 * add experimental support for osm2pgsql flex style * introduce secondary importance value to be retrieved from a raster data file (currently still unused, to replace address importance, thanks to @tareqpi) * add new report tool `nominatim admin --collect-os-info` (thanks @micahcochran, @tareqpi) * reorganise index to improve lookup performance and size * run index creation after import in parallel * run ANALYZE more selectively to speed up continuation of indexing * fix crash on update when addr:interpolation receives an illegal value * fix minimum number of retrieved results to be at least 10 * fix search for combinations of special term + name (e.g Hotel Bellevue) * do not return interpolations without a parent street on reverse search * improve invalidation of linked places on updates * fix address parsing for interpolation lines * make sure socket timeouts are respected during replication (working around a bug in some versions of pyosmium) * update bundled osm2pgsql to 1.7.1 * add support for PostgreSQL 15 * typing fixes to work with latest type annotations from typeshed * smaller improvements to documentation (thanks to @mausch) 4.1.1 * fix XSS vulnerability in debug view 4.1.0 * switch to ICU tokenizer as default * add housenumber normalization and support optional spaces during search * add postcode format checking and support optional spaces during search * add function for cleaning housenumbers in word table * add updates/deletion of country names imported from OSM * linked places no longer overwrite names from a place permanently * move default country name configuration into yaml file (thanks @tareqpi) * more compact layout for interpolation and TIGER tables * introduce mutations to ICU tokenizer (used for German umlauts) * support reinitializing a full project directory with refresh --website * fix various issues with linked places on updates * add support for external sanitizers and token analyzers * add CLI commands for forced indexing * add CLI command for version report * add offline import mode * change geocodejson to return a feature class in the 'type' field * add ISO3166-2 to address output (thanks @I70l0teN4ik) * improve parsing and matching of addr: tags * support relations as street members of associatedStreet * better ranking for address results from TIGER data * adapt rank classification to changed tag usage in OSM * update bundled osm2pgsql to 1.6.0 * add typing information to Python code * improve unit test coverage * reorganise and speed up code for BDD tests, drop support for scenes * move PHP unit tests to PHP 9.5 * extensive typo fixes in documentation (thanks @woodpeck,@StephanGeorg, @amandasaurus, @nslxndr, @stefkiourk, @Luflosi, @kianmeng) * drop official support for installation on CentOS * add installation instructions for Ubuntu 22.04 * add support for PHP8 * add setup instructions for updates and systemd * drop support for PostgreSQL 9.5 4.0.2 * fix XSS vulnerability in debug view 4.0.1 * fix initialisation error in replication script * ICU tokenizer: avoid any special characters in word tokens * better error message when API php script does not exist * fix quoting of house numbers in SQL queries * small fixes and improvements in search query parsing * add documentation for moving the database to a different machine 4.0.0 * refactor name token computation and introduce ICU tokenizer * name processing now happens in the indexer outside the DB * reorganizes abbreviation handling and moves it to the indexing phases * adds preprocessing of names * add country-specific ranking for Spain, Slovakia * partially switch to using SP-GIST indexes * better updating of dependent addresses for name changes in streets * remove unused/broken tables for external housenumbers * move external postcodes to CSV format and no longer save them in tables (adds support for postcodes for arbitrary countries) * remove postcode helper entries from placex (thanks @AntoJvlt) * change required format for TIGER data to CSV * move configuration of default languages from wiki into config file * expect customized configuration files in project directory by default * disable search API for reverse-only import (thanks @darkshredder) * port most of maintenance/import code to Python and remove PHP utils * add catch-up mode for replication * add updating of special phrases (thanks @AntoJvlt) * add support for special phrases in CSV files (thanks @AntoJvlt) * switch to case-independent matching between place and boundary names * remove disabling of reverse query parsing * minor tweaks to search algorithm to avoid more false positives * major overhaul of the administrator and developer documentation * add security disclosure policy * add testing of installation scripts via CI * drop support for Python < 3.6 and Postgresql < 9.5 3.7.3 * fix XSS vulnerability in debug view 3.7.2 * fix database check for reverse-only imports * do not error out in status API result when import date is missing * add array_key_last function for PHP < 7.3 (thanks to @woodpeck) * fix more url when server name is unknown (thanks to @mogita) * commit changes to replication log table 3.7.1 * fix smaller issues with special phrases import (thanks @AntoJvlt) * add index to speed up continued indexing during import * fix index on location_property_tiger(parent_place_id) (thanks @changpingc) * make sure Python code is backward-compatible with Python 3.5 * various documentation fixes 3.7.0 * switch to dotenv for configuration file * introduce 'make install' (reorganising most of the code) * introduce nominatim tool as replacement for various php scripts * introduce project directories and allow multiple installations from same build * clean up BDD tests: drop nose, reorganise step code * simplify test database for API BDD tests and autoinstall database * port most of the code for command-line tools to Python (thanks to @darkshredder and @AntoJvlt) * add tests for all tooling * replace pyosmium-get-changes with custom internal implementation using pyosmium * improve search for queries with housenumber and partial terms * add database versioning * use jinja2 for preprocessing SQL files * introduce automatic migrations * reverse fix preference of interpolations over housenumbers * parallelize indexing of postcodes * add non-key indexes to speed up housenumber + street searches * switch housenumber field in placex to save transliterated names 3.6.0 * add full support for searching by and displaying of addr:* tags * improve address output for large-area objects * better use of country names from OSM data for search and display * better debug output for reverse call * add support for addr:place links without an place equivalent in OSM * improve finding postcodes with normalisation artefacts * batch object to index for rank 30, avoiding a wrap-around of transaction IDs in PostgreSQL * introduce dynamic address rank computation for administrative boundaries depending on linked objects and their place in the admin level hierarchy * add country-specific address ranking for Indonesia, Russia, Belgium and the Netherlands (thanks @hendrikmoree) * make sure wikidata/wikipedia tags are imported for all styles * make POIs searchable by name and housenumber (thanks @joy-yyd) * reverse geocoding now ignores places without an address rank (rivers etc.) * installation of a webserver is no longer mandatory, for development use the php internal webserver via 'make serve * reduce the influence of place nodes in addresses * drop support for the unspecific is_in tag * various minor tweaks to supplied styles * move HTML web frontend into its own project * move scripts for processing external data sources into separate directories * introduce separate configuration for website (thanks @krahulreddy) * update documentation, in particular, clean up development docs * update osm2pgsql to 1.4.0 3.5.2 * ensure that wikipedia tags are imported for all styles * reinstate verbosity for indexing during updates * make house number reappear in display name on named POIs * introduce batch processing in indexer to avoid transaction ID overrun * increase splitting for large geometries to improve indexing speed * remove deprecated get_magic_quotes_gpc() function * make sure that all postcodes have an entry in word and are thus searchable * remove use of ST_Covers in conjunction with ST_Intersects, causes bad query planning and slow updates in Postgis3 * update osm2pgsql 3.5.1 * disable jit and parallel processing in PostgreSQL for osm2pgsql * update libosmium to 2.15.6 (fixes an issue with processing hanging on large multipolygons) 3.5.0 * structured select on HTML search page * new PHP Nominatim\Shell class to wrap shell escaping * remove polygon parameter from all API calls * improve handling of postcode areas * reorganise place linking algorithm, now using wikidata tag as well * remove linkees from search_name and larger_area tables * introduce country-specific address ranks * reorganise rank address computation * cleanup of partition function * improve parenting for large POIs * add support for Postgresql 12 and Postgis 3 * add earlier cleanup when --drop is given, to reduce memory usage * remove use of place_id in URLs * replace C nominatim indexer with a simpler Python implementation * split up the huge sql/functions.sql file * move osm2pgsql tests to osm2pgsql * add new extratags style which imports all tags from OSM * add new script for checking the import after completion * update osm2pgsql, reducing memory usage * use new wikipedia importance and add processing of wikidata tags * add search form for details page * use ExtraDataPath for country_grid table * remove short_name from list of names to be displayed * split up CMakeFile, so that all parts can be built separately * update installation instructions for CentOS and Ubuntu * add script for importing/updating multiple country extracts * various documentation improvements 3.4.2 * fix security bug in /details endpoint where user input was not properly sanitized 3.4.1 * update osm2pgsql to fix hans during updates and lost address numbers during updates 3.4.0 * increase required version for PostgreSQL(9.3), PostGIS(2.2) and PHP(7.0) * better error reporting for out-of-memory errors * exclude postcode ranges separated by colon from centre point calculation * update osm2pgsql, better handling of imports without flatnode file * switch to more efficient algorithm for word set computation * use only boundaries for country and state parts of addresses * improve updates of addresses with housenumbers and interpolations * remove country from place_addressline table and use country_code instead * optimise indexes on search_name partition tables * improve searching of attached streets for large objects like airports * drop support for python 2 * new scripts for importing Wikidata for importance * create and drop indexes concurrently to not clash with auto vacuum * various documentation improvements 3.3.0 * zoom 17 in reverse now zooms in on minor streets * fix use of postcode relations in address * support for housenumber 0 on interpolations * replace database abstraction DB with PDO and switch to using exceptions * exclude line features at rank 30 from reverse geocoding * remove self-reference and country from place_addressline * make json output more readable (less escaping) * update conversion scripts for postcodes * scripts in utils/ are no longer executable (always use scripts in build dir) * remove Natural Earth country fallback (OSM is complete enough) * make rank assignments configurable * allow accept languages with underscore * new reverse-only import mode (without search index table) * rely on boundaries only for states and countries * update osm2pgsql, now using a configurable style * provide multiple import styles * improve search when house number and postcodes are dropped * overhaul of setup code * add support for PHPUnit 6 * update test database * various documentation improvements 3.2.0 * complete rewrite of reverse search algorithm * add new geojson and geocodejson output formats * add simple export script to export addresses to CSV * remove is_in terms from address computation * remove unused search_name_country tables * various smaller fixes to query parsing * convert Tokens and token types to class types * correctly handle update when boundary object type is changed * improve debug output for /search endpoint * update to latest osm2pgsql and leaflet.js * overhaul of /details endpoint: * new class parameter when using osmtype/osmid parameters * permalink to instance-independent osmtype/osmid parameter format * new json output format * update CentOS vagrant machine to use SELinux * add vagrant scripts for Ubuntu 18.04 * fix build process for BSD * enable running the database on a different host than the setup scripts * allow to configure use of custom PHP binaries (PHP_BIN) * extensive coding style improvements to PHP code * more PHP unit tests for new classes * increase coverage for API tests * add documentation for API 3.1.0 * rework postcode handling and introduce location_postcode table * make setup less verbose and print a summary in the end * setup: error out when web site user does not exist * add more API tests to complete code coverage * reinstate key-value amenity search (special term [key=value]) * fix detection of coordinates in query * various smaller tweaks to ranking of search interpretations * complete overhaul of PHP frontend code using OOP * add address rank to details page * update Tiger scripts for 2017 data and clean up unused code * various bug fixes and improvements to UI * improve reverse geocoding performance close to coasts * more PHP style cleanup (quoting) * allow unnamed road in reverse geocoding to avoid too far off results * add function to recalculate counts for full-word search term * add function to check if new updates are available * update documentation and switch to mkdocs for generating HTML 3.0.1 * fix bug in geometry building algorithm in osm2pgsql * fix typos in build instructions 3.0.0 * move to cmake build system * various fixes to HTML output * reverse endpoint now can return geometries * add support for PHP7 * move to on-the-fly computation of interpolations * improve handling of linked places (updating) * improve test framework: * replace lettuce with behave * use smaller database for API tests * drop support for postgres < 9.1, postgis < 2.0 and PHP < 5.4 * make external data use optional (useful for imports without US) * detect postgres and postgis versions automatically * clean up query logging and remove unused tables * move installation documentation into this repo * add self-documenting vagrant scripts * remove --create-website, recommend to use website directory in build * add accessor functions for URL parameters and improve error checking * remove IP blocking and rate-limiting code * enable CI via travis * reformatting for more consistent coding style * make country search term creation part of setup * update country names and country grid * handle roads that cross boundaries better * keep full information on address tags * update to refactored osm2pgsql which use libosmium based types * switch from osmosis to pyosmium for updates * be more strict when matching against special search terms * handle postcode entries with multiple values correctly 2.5 * reverse geocoding includes looking up housenumbers from Tiger data * added parameter to return simplified geometries * new lookup call for getting address information for OSM objects * new namedetails and extratags parameters that expose the name and extratags fields of the placex table * mobile website * reverse web view 2.4 * drop support for postgres 8.4 * rewrite address interpolation * switch to C++ version of osm2pgsql and rewrite tag filtering * support for bridge:name and tunnel:name, man_made, junction * drop way-node index (reduces database size by about 15%) * add support for configuring tablespaces and webserver user * better evaluation of search queries in right-to-left notation * improve house number search for streets with many duplicate entries * code cleanup (remove unused functions and tables) 2.3 * further improve ordering of results * support for more lat/lon formats in search-as-reverse * fix handling of GB postcodes * new functional test suite * support for waterway relations * inherit postcodes from street to poi * fix housenumber normalisation to find non-latin house numbers * take viewbox into account for ordering of results * pois may now inherit address tags from surrounding buildings * improve what objects may participate in an address * clean up handled class/type combinations to current OSM usage * lots of bug fixes 2.2 * correct database rights for www-data * add timestamps for update output * load postgis via extension for postgis >= 2.0 * remove non-admin boundaries from addresses * further improve ordering of results with same importance * merge addr:postcode tags into object addresses * include rank and importance in reverse geocode output * replace ST_Line_Interpolate_Point with ST_LineInterpolatePoint (for postgis >= 2.1) * update osm2pgsql to latest version * properly detect changes of admin_level * remove landuses when name is removed * smaller fixes 2.1 * update to openlayers 2.12 (now custom built) * update fallback OSM boundaries * add support for postgresql 9.2/9.3 and postgis 2.x * add structured queries * add options for polygon output in various formats (geojson, svg, kml, postgis text) * maintenance functions for deleting objects and updating regions (plcae_force_update/place_force_delete) * web view for polygons that need deleting * rate limiting using memcache * improve layout of details page * add support for boundary:postal_code * full CORS support * improve parenting of POIs * support for extract daily diffs from Geofabrik * support for addresses without a street (addr:place and conscription number house numbers) * improve layout of word and search_name_* tables * support for US ZIP+4 codes * refactoring of front-end PHP code * lots of smaller bug fixes 2.0.1 * delete outdated entries from location_area_country * remove remaining uses of INTEGER, to allow node ids larger than 2^31 ================================================ FILE: LICENSES/Apache-2.0.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: LICENSES/GPL-2.0-only.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: Makefile ================================================ all: # Building of wheels build: clean-build build-db build-api clean-build: rm -f dist/* build-db: python3 -m build packaging/nominatim-db --outdir dist/ build-api: python3 -m build packaging/nominatim-api --outdir dist/ # Tests tests: mypy lint pytest bdd mypy: mypy --strict --python-version 3.9 src pytest: pytest test/python lint: flake8 src test/python test/bdd bdd: pytest test/bdd --nominatim-purge # Documentation doc: mkdocs build serve-doc: mkdocs serve manpage: argparse-manpage --pyfile man/create-manpage.py --function get_parser --project-name Nominatim --url https://nominatim.org > man/nominatim.1 --author 'the Nominatim developer community' --author-email info@nominatim.org .PHONY: tests mypy pytest lint bdd build clean-build build-db build-api doc serve-doc manpage ================================================ FILE: README.md ================================================ [![Build Status](https://github.com/osm-search/Nominatim/workflows/CI%20Tests/badge.svg)](https://github.com/osm-search/Nominatim/actions?query=workflow%3A%22CI+Tests%22) Nominatim ========= Nominatim (from the Latin, 'by name') is a tool to search OpenStreetMap data by name and address (geocoding) and to generate synthetic addresses of OSM points (reverse geocoding). An instance with up-to-date data can be found at https://nominatim.openstreetmap.org. Nominatim is also used as one of the sources for the Search box on the OpenStreetMap home page. Documentation ============= The documentation of the latest development version is in the `docs/` subdirectory. A HTML version can be found at https://nominatim.org/release-docs/develop/ . Installation ============ The latest stable release can be downloaded from https://nominatim.org. There you can also find [installation instructions for the release](https://nominatim.org/release-docs/latest/admin/Installation), as well as an extensive [Troubleshooting/FAQ section](https://nominatim.org/release-docs/latest/admin/Faq/). [Detailed installation instructions for current master](https://nominatim.org/release-docs/develop/admin/Installation) can be found at nominatim.org as well. A quick summary of the necessary steps: 1. Clone this git repository and download the country grid git clone https://github.com/osm-search/Nominatim.git wget -O Nominatim/data/country_osm_grid.sql.gz https://nominatim.org/data/country_grid.sql.gz 2. Create a Python virtualenv and install the packages: python3 -m venv nominatim-venv ./nominatim-venv/bin/pip install packaging/nominatim-{api,db} 3. Create a project directory, get OSM data and import: mkdir nominatim-project cd nominatim-project ../nominatim-venv/bin/nominatim import --osm-file 2>&1 | tee setup.log 4. Start the webserver: ./nominatim-venv/bin/pip install uvicorn falcon ../nominatim-venv/bin/nominatim serve License ======= The Python source code is available under a GPL license version 3 or later. The Lua configuration files for osm2pgsql are released under the Apache License, Version 2.0. All other files are under a GPLv2 license. Contributing ============ Contributions, bug reports and pull requests are welcome. When reporting a bug, please use one of the [issue templates](https://github.com/osm-search/Nominatim/issues/new/choose) and make sure to provide all the information requested. If you are not sure if you have really found a bug, please ask for help in the forums first (see 'Questions' below). For details on contributing, have a look at the [contribution guide](CONTRIBUTING.md). Questions and help ================== If you have questions about search results and the OpenStreetMap data used in the search, use the [OSM Forum](https://community.openstreetmap.org/). For questions, community help and discussions around the software and your own installation of Nominatim, use the [Github discussions forum](https://github.com/osm-search/Nominatim/discussions). ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions All Nominatim releases receive security updates for two years. The following table lists the end of support for all currently supported versions. | Version | End of support for security updates | | ------- | ----------------------------------- | | 5.3.x | 2028-04-04 | | 5.2.x | 2027-10-29 | | 5.1.x | 2027-04-01 | | 5.0.x | 2027-02-06 | | 4.5.x | 2026-09-12 | ## Reporting a Vulnerability If you believe, you have found an issue in Nominatim that has implications on security, please send a description of the issue to **security@nominatim.org**. You will receive an acknowledgement of your mail within 3 work days where we also notify you of the next steps. ## How we Disclose Security Issues ** The following section only applies to security issues found in released versions. Issues that concern the master development branch only will be fixed immediately on the branch with the corresponding PR containing the description of the nature and severity of the issue. ** Patches for identified security issues are applied to all affected versions and new minor versions are released. At the same time we release a statement at the [Nominatim blog](https://nominatim.org/blog/) describing the nature of the incident. ## List of Previous Incidents * 2023-11-20 - [SQL injection vulnerability](https://nominatim.org/2023/11/20/release-432.html) * 2023-02-21 - [cross-site scripting vulnerability](https://nominatim.org/2023/02/21/release-421.html) * 2020-05-04 - [SQL injection issue on /details endpoint](https://lists.openstreetmap.org/pipermail/geocoding/2020-May/002012.html) ================================================ FILE: VAGRANT.md ================================================ # Install Nominatim in a virtual machine for development and testing This document describes how you can install Nominatim inside a Ubuntu 24 virtual machine on your desktop/laptop (host machine). The goal is to give you a development environment to easily edit code and run the test suite without affecting the rest of your system. The installation can run largely unsupervised. You should expect 1h from start to finish depending on how fast your computer and download speed is. ## Prerequisites 1. [Virtualbox](https://www.virtualbox.org/wiki/Downloads) 2. [Vagrant](https://www.vagrantup.com/downloads.html) 3. Nominatim git clone https://github.com/openstreetmap/Nominatim.git ## Installation 1. Start the virtual machine vagrant up ubuntu24-nginx 2. Log into the virtual machine vagrant ssh ubuntu24-nginx 3. Import a small country (Monaco) See the FAQ how to skip this step and point Nominatim to an existing database. ``` # inside the virtual machine: cd nominatim-project wget --no-verbose --output-document=monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf nominatim import --osm-file monaco.osm.pbf 2>&1 | tee monaco.$$.log ``` To repeat an import you'd need to delete the database first dropdb --if-exists nominatim ## Development Vagrant maps the virtual machine's port 8089 to your host machine. Thus you can see Nominatim in action on [localhost:8089](http://localhost:8089/nominatim/). You edit code on your host machine in any editor you like. There is no need to restart any software: just refresh your browser window. Use the functions of the `log()` object to create temporary debug output. Add `&debug=1` to the URL to see the output. In the Python BDD test you can use `logger.info()` for temporary debug statements. For more information on running tests, see https://nominatim.org/release-docs/develop/develop/Testing/ ## FAQ ##### Will it run on Windows? Yes, Vagrant and Virtualbox can be installed on MS Windows just fine. You need a 64bit version of Windows. ##### Will it run on Apple Silicon? You might need to replace Virtualbox with [Parallels](https://www.parallels.com/products/desktop/). There is no free/open source version of Parallels. ##### Why Monaco, can I use another country? Of course! The Monaco import takes less than 10 minutes and works with 2GB RAM. ##### Will the results be the same as those from nominatim.openstreetmap.org? No. Long-running Nominatim installations will differ once new import features (or bug fixes) get added since those usually only get applied to new/changed data. Also this document skips the optional Wikipedia data import which affects ranking of search results. See [Nominatim installation](https://nominatim.org/release-docs/latest/admin/Installation) for details. ##### Why Ubuntu? Can I test CentOS/Fedora/CoreOS/FreeBSD? There used to be a Vagrant script for CentOS available, but the Nominatim directory isn't symlinked/mounted to the host which makes development trickier. We used it mainly for debugging installation with SELinux. In general Nominatim will run in the other environments. The installation steps are slightly different, e.g. the name of the package manager, Apache2 package name, location of files. We chose Ubuntu because that is closest to the nominatim.openstreetmap.org production environment. You can configure/download other Vagrant boxes from [https://app.vagrantup.com/boxes/search](https://app.vagrantup.com/boxes/search). ##### How can I connect to an existing database? Let's say you have a Postgres database named `nominatim_it` on server `your-server.com` and port `5432`. The Postgres username is `postgres`. You can edit the `.env` in your project directory and point Nominatim to it. NOMINATIM_DATABASE_DSN="pgsql:host=your-server.com;port=5432;user=postgres;dbname=nominatim_it No data import or restarting necessary. If the Postgres installation is behind a firewall, you can try ssh -L 9999:localhost:5432 your-username@your-server.com inside the virtual machine. It will map the port to `localhost:9999` and then you edit `.env` file with NOMINATIM_DATABASE_DSN="pgsql:host=localhost;port=9999;user=postgres;dbname=nominatim_it" To access postgres directly remember to specify the hostname, e.g. `psql --host localhost --port 9999 nominatim_it` ##### My computer is slow and the import takes too long. Can I start the virtual machine "in the cloud"? Yes. It's possible to start the virtual machine on [Amazon AWS (plugin)](https://github.com/mitchellh/vagrant-aws) or [DigitalOcean (plugin)](https://github.com/smdahlen/vagrant-digitalocean). ================================================ FILE: Vagrantfile ================================================ # -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| # Apache webserver config.vm.network "forwarded_port", guest: 80, host: 8089 config.vm.network "forwarded_port", guest: 8088, host: 8088 # If true, then any SSH connections made will enable agent forwarding. config.ssh.forward_agent = true # Never sync the current directory to /vagrant. config.vm.synced_folder ".", "/vagrant", disabled: true checkout = "yes" if ENV['CHECKOUT'] != 'y' then checkout = "no" end config.vm.provider "hyperv" do |hv, override| hv.memory = 2048 hv.linked_clone = true if ENV['CHECKOUT'] != 'y' then override.vm.synced_folder ".", "/home/vagrant/Nominatim", type: "smb", smb_host: ENV['SMB_HOST'] || ENV['COMPUTERNAME'] end end config.vm.provider "virtualbox" do |vb, override| vb.gui = false vb.memory = 2048 vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate//vagrant","0"] if ENV['CHECKOUT'] != 'y' then override.vm.synced_folder ".", "/home/vagrant/Nominatim" end end config.vm.provider "parallels" do |prl, override| prl.update_guest_tools = false prl.memory = 2048 if ENV['CHECKOUT'] != 'y' then override.vm.synced_folder ".", "/home/vagrant/Nominatim" end end config.vm.provider "libvirt" do |lv, override| lv.memory = 2048 lv.nested = true if ENV['CHECKOUT'] != 'y' then override.vm.synced_folder ".", "/home/vagrant/Nominatim", type: 'nfs', nfs_udp: false end end config.vm.define "ubuntu22", primary: true do |sub| sub.vm.box = "generic/ubuntu2204" sub.vm.provision :shell do |s| s.path = "vagrant/Install-on-Ubuntu-22.sh" s.privileged = false s.args = [checkout] end end config.vm.define "ubuntu22-apache" do |sub| sub.vm.box = "generic/ubuntu2204" sub.vm.provision :shell do |s| s.path = "vagrant/Install-on-Ubuntu-22.sh" s.privileged = false s.args = [checkout, "install-apache"] end end config.vm.define "ubuntu22-nginx" do |sub| sub.vm.box = "generic/ubuntu2204" sub.vm.provision :shell do |s| s.path = "vagrant/Install-on-Ubuntu-22.sh" s.privileged = false s.args = [checkout, "install-nginx"] end end config.vm.define "ubuntu24" do |sub| sub.vm.box = "bento/ubuntu-24.04" if RUBY_PLATFORM.include?('darwin') && RUBY_PLATFORM.include?('arm64') # Apple M processor sub.vm.box = 'gutehall/ubuntu24-04' end sub.vm.provision :shell do |s| s.path = "vagrant/Install-on-Ubuntu-24.sh" s.privileged = false s.args = [checkout] end end config.vm.define "ubuntu24-apache" do |sub| sub.vm.box = "bento/ubuntu-24.04" sub.vm.provision :shell do |s| s.path = "vagrant/Install-on-Ubuntu-24.sh" s.privileged = false s.args = [checkout, "install-apache"] end end config.vm.define "ubuntu24-nginx" do |sub| sub.vm.box = "bento/ubuntu-24.04" sub.vm.provision :shell do |s| s.path = "vagrant/Install-on-Ubuntu-24.sh" s.privileged = false s.args = [checkout, "install-nginx"] end end end ================================================ FILE: data/words.sql ================================================ -- -- PostgreSQL database dump -- SET statement_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; SET check_function_bodies = false; SET client_min_messages = warning; SET search_path = public, pg_catalog; SET default_tablespace = ''; SET default_with_oids = false; -- -- Name: word_frequencies; Type: TABLE; Schema: public; Owner: -; Tablespace: -- DROP TABLE IF EXISTS word_frequencies; CREATE TABLE word_frequencies ( word_token text, count bigint ); -- -- Data for Name: word_frequencies; Type: TABLE DATA; Schema: public; Owner: - -- COPY word_frequencies (word_token, count) FROM stdin; gooseberry 501 qew 501 coaster 501 lubecker st 501 georg st 501 napravlinii 501 r georges bizet 501 jr chang pan xian 501 1550 501 wellington dr 501 starii 501 lundy 501 polizeistation 501 railway rd 501 r st antoine 501 gril 501 wirtschaft 501 ripa 501 minsenv 501 espanya 501 woodend 501 suz 501 sternen 501 mattos 501 schaf 501 mcdonald cr 501 gove 501 cox rd 501 murta 501 fermata 501 agios nikolaos 501 rake 501 dra 501 desportivo 501 talbach 501 n water st 501 prisma 501 banff 501 prigorodnaia ul 501 haller st 501 kabinit 501 zuider 501 krymskaia 501 irlanda 501 patha 501 misery 501 pleasant dr 501 banannarkanin 501 westfild rd 501 giono 501 cree 501 krems 501 hiram 501 st g 501 elkins 501 cantone 501 macchia 501 mex 1 501 adana sanliurfa otoyolu 502 poziomkowa 502 isidore 502 n 117 502 bhyrte 502 camat 502 qili 502 pinecrest dr 502 uva 502 tbr 502 armstrong rd 502 c so giuseppe garibaldi 502 evergreen st 502 grushivskogo vulitsia 502 d 949 502 2190 502 macro 502 pampas 502 rollo 502 viaraya 502 upon 502 baker cr 502 muhlhauser 502 kalinowa 502 coille 502 malteser 502 57a 502 sh25 502 shchirbakova 502 solon 502 e 40 e 41 502 a37 502 veronese 502 lloyd st 502 fructuoso 502 oun 502 purdy 502 mcnair 502 aleflshmaleflyte 502 banja 502 huntsville 502 asti 502 hensley 502 e 451 502 ozira 502 avtomagazin 502 myru st 502 mundial 502 plum cr 502 l 530 502 wainwright 502 schreiber 502 whitefild 502 ul zoi kosmodimianskoi 503 prvomajska 503 carney 503 sh5 503 rispublika 503 grasweg 503 mosconi 503 vistula 503 mclaren 503 seminar 503 arral 503 doshkilnii 503 piani 503 alder st 503 cisco 503 oliver rd 503 mulas 503 taurus 503 tusday 503 honved 503 1 88 503 rosenheimer st 503 labattoir 503 bahama 503 wegkreuz 503 bicicletas 503 refinery 503 dechant 503 mckinley st 503 2111 503 320th st 503 castlereagh 503 doan 503 aleflshhdalefhamza 503 kliuchivaia ul 503 v lazio 503 qtalefr 503 baustoffe 503 krolowej jadwigi 503 ooster 503 blokker 503 963 503 246a 503 hage 503 baixada 503 albion st 503 orbita 503 shearwater 503 zhong hua lu 503 daisy ln 503 keian electric railway keian main ln 503 lewiston 503 amedee 503 caupolican 504 incheon 504 pitt st 504 lokomotiv 504 brandes 504 bejar 504 mcdonald rd 504 nippo 504 shoh 504 weseler st 504 armazem 504 sharaf 504 khy 504 c s sebastian 504 casetta 504 bridger 504 fisher st 504 53k 504 gotthard 504 kreisverband 504 romualdo 504 landbackerei 504 krm 504 j st 504 tryon 504 hubertusweg 504 haustechnik 504 panasa 504 backbone 504 smit 504 sommet 504 carroll st 504 hayes rd 504 miscelanea 504 bramante 504 carmes 504 rocky branch 504 hunyadi janos u 504 bei chuan 504 co rd 28 504 windwood 504 sindicato 504 hulshoff 504 iper 504 economy 504 lepinette 504 mynydd 504 velarde 504 wooded 504 thessalonikes 504 vazhinka 504 hangweg 504 huipyeonzzyx 504 stardust 505 judd 505 c granada 505 gr bd 505 211th 505 turkish 505 simion 505 d 177 505 stockholms 505 5a c 505 valia 505 n 14 505 curt 505 w end 505 erlengrund 505 firmin 505 dig 505 sp233 505 izumi 505 wheatfild 505 markets 505 doll 505 putri 505 biriozovi 505 banki 505 vini 505 ia 27 505 radica 505 church vw 505 hermano 505 a76 505 gospital 505 xxx 505 sp71 505 akazin 505 shared 505 fifth st 505 xiaono 505 durval 505 podlisnaia 505 d 927 505 cis 505 randstad 505 harsfa 505 lawrence av 506 gentle 506 mcquen 506 meadow c 506 gabrila narutowicza 506 paralia 506 nauchno 506 1047 506 orikhovaia 506 adalberto 506 d 929 506 balle 506 jjimjilbang 506 yayayyat 506 odenwald st 506 stoll 506 dortmunder 506 shay 506 ic 2 506 ca 66 506 s fernando 506 trek 506 penedes 506 regato 506 crouch 506 varshavskoi shossi 506 tulane 506 jewett 506 djebel 506 chemistry 506 her 506 poison 506 1013 506 cumberland rd 506 e 903 506 los robles 506 di5 506 fireside 506 r paul cezanne 506 longchamp 506 pastora 506 yakima 506 av general paz 506 abteilung 506 events 506 gyeongjeon ln 506 eira 506 beans 506 lexus 506 standart 506 chas 506 pab 506 a167 506 rauch 506 laureano 506 ap 9 506 konoba 506 mo a 506 wakefild rd 506 co rd e 506 koupali 506 nh30 506 uranus 506 pulku 506 tashkintskaia 506 gioberti 506 pree 507 adnan 507 shalefrain 3 507 arpents 507 mercearia 507 fazant 507 decima 507 tupper 507 seven 1 holdings 507 oak cr 507 1077 507 a470 507 g219 507 peterson rd 507 murchison 507 nibelungen st 507 ukrainska vulitsia 507 bernstein 507 dan in luan huang yuki ze gyeongbu exp s 507 cutter 507 landsborough 507 dust 507 unitarian 507 augenoptik 507 chacon 507 ozirna vulitsia 507 saglik 507 s ding mu 507 yhdys 507 mather 507 ewald 507 townline rd 507 prospikt stroitilii 507 tikhniki 507 s jorge 507 linhares 507 ponente 507 delft 507 comm ctr 507 croas 507 mackenna 507 athlone 507 us 30 alternate old us 330 507 kwiatkowskigo 507 esteves 507 zwickaur 507 1650 507 electrica 507 tasca 507 wurttembergische 507 dao107 507 slovenija 507 mt zion cemetery 507 hiriy 507 limbach 507 starego 507 mzhd 507 preta 507 milliken 507 edgard 507 tiga 507 us 42 507 conn 507 quy 508 geant 508 cavaillon 508 jiffy lube 508 vorosmarty u 508 pitrovskogo vulitsia 508 leopard 508 geneva subdivision 508 lebre 508 anderson av 508 33rd st 508 fourneau 508 v novara 508 woluwe 508 berea 508 stoneouse 508 heritage ln 508 cian 508 pelto 508 alzira 508 stifan 508 s342 508 kamianka 508 rodnichok 508 tx 289 508 okhrany 508 paracelsus 508 spas 508 soccorso 508 brookwood dr 508 las lomas 508 2055 508 donk 508 birches 508 tineretului 508 ecoponto 508 studentenwerk 508 chp 508 rua 16 508 gyeongjeon 508 tolstoi 508 sp70 508 pineridge 508 rouse 508 cowper 508 berens 508 neubaur 508 2040 508 salem church 508 general building 508 bite 508 hansens 508 2191 508 rosemead 508 coffe 508 khntrl 508 kazan 508 cameron rd 509 ani 509 howland 509 pojazdow 509 okmulgee 509 ligne de savenay a landerneau 509 frunzi ul 509 balefnkh mly 509 monteagudo 509 cent 509 wurm 509 dixi rd 509 iuzhnoi 509 barkers 509 r du ch vert 509 luxemburger 509 rope 509 swirczewskigo 509 kongo 509 cruceiro 509 azure 509 pommerai 509 maximilian st 509 hammerweg 509 mission rd 509 redondel 509 ufa 509 r des noyers 509 hanwa 509 furza 509 miller ln 509 arriaga 509 3500 509 stefana batorego 509 karbyshiva 509 mesa dr 509 porche 509 odisskaia 509 2210 509 certosa 509 masarykovo 509 d 618 509 rats 509 absa 509 carpenter rd 509 bellviw 509 tangenziale est 509 barren 509 shms 509 bai chuan 509 vang 509 akacfa 509 ocotillo 509 shvp 509 bradley st 509 tervuren 509 n 25 509 dactivite 509 penna 509 comm rd 509 mechelse stwg 509 bidv 509 r 10 510 verdon 510 av aristide briand 510 bluberry ln 510 bella vsta 510 kiosko 510 chon 510 b 191 510 caruso 510 taqwa 510 voronizhskaia 510 perle 510 huhangyong gaosu 510 m14 510 ballarat 510 sh 14 510 beija 510 talat 510 n mkt st 510 5 kvetna 510 trones 510 dao176 510 orono 510 ebers 510 av presidente vargas 510 manzanillo 510 aydin 510 infancia 510 zikenhuis 510 mangga 510 l 173 510 oficina de correos 510 farr 510 dalniaia ul 510 phahon 510 barber shop 510 pascoal 510 murs 510 krakow 510 tiatralnaia ul 510 mskhn 510 bessi 510 sunlight 510 odori 510 rn6 510 oakcrest 510 laderas 510 frindship church 510 lafayette av 510 goldene 510 ep20 510 huhangyong exp 510 teczowa 510 cerezos 511 kulinariia 511 vysokaia 511 garaze 511 meall 511 stairs 511 tsilinnaia 511 antik 511 karma 511 ukrainky 511 kajaanin 511 wacker 511 latour 511 linder 511 contre 511 buschweg 511 ptitsifabrika 511 privokzalna 511 ruines 511 k 67 511 b 184 511 shalefh 511 columbus st 511 ash gr 511 hms 511 2025 511 n cr 511 asrama 511 converse 511 steinbruchweg 511 ilinka 511 total accs 511 kastell 511 wilden 511 marly 511 eintracht 511 n76 511 keller st 511 hatar 511 e 314 511 brot 511 badgers 511 ptarmigan 511 channing 511 1105 511 negara 511 khristova 512 bliska 512 warrego 512 abadia 512 gibson rd 512 lambeth 512 comuna 512 r louis bleriot 512 3c 512 n 7th av 512 dial 512 popple 512 bighorn 512 winkl 512 republiky 512 beatrix st 512 piramida 512 rabelo 512 e maple st 512 grasse 512 kolk 512 b60 512 fu xing lu 512 leeuw 512 kasprowicza 512 rocket 512 pesantren 512 fabra 512 sotni 512 bezrucova 512 seegraben 512 piacenza 512 potoka 512 montoya 512 longmeadow 512 aparecido 512 p za roma 512 clove 512 cth n 512 newstead 512 zhudalg 512 chante 512 mav 512 daodasdakdabs 513 wangen 513 sp73 513 mshhd 513 tidewater 513 mizhraionnaia 513 tasty 513 haslach 513 rmlte 513 stell 513 vigny 513 melezes 513 chickamauga 513 standort 513 clayton rd 513 waldpark 513 oberlin 513 arroyos 513 r101 513 ze15 513 sivastopolskaia ul 513 pl de verdun 513 landgasthaus 513 rona 513 molise 513 jacint 513 rua onze 513 golden state fwy 513 westerly 513 di4 513 kuopion 513 1 476 513 trinity church 513 hales 513 bartley 513 guo daodasdakdabs hao 513 kayu 513 cr 39 513 food mart 513 schaferei 513 gorkogo st 513 llorenc 513 rameau 513 tresor 513 walter st 513 manson 513 koya 513 doolittle 513 mechanical 513 n gr av 513 ferial 513 thionville 513 sotsialnogo 513 larchen st 513 eckert 513 cevennes 513 secao 513 herrenhaus 513 polivalente 513 iss 514 n 322 514 obsluzhivaniia 514 r augu ste renoir 514 cordel 514 d 643 514 andras 514 kab 514 serge 514 ademar 514 holywell 514 lutter 514 boulay 514 ende 514 vozrozhdinii 514 blu cr 514 platanen 514 bootshaus 514 ulsan 514 av of saints 514 emirates 514 obecni urad 514 rapida 514 pentland 514 asc 514 pleasant hill church 514 morg 514 v dei pini 514 alexander of macedonia 514 p t 514 omsk 514 collettore 514 partizanski 514 l 136 514 lloyds tsb 514 chardonnay 514 st 9 514 vat 514 a df pl 514 olimpiiskii 514 hanover st 514 iturbide 514 golfe 514 1185 514 zhong yang zong wu huan xing xian 514 asp 514 waddell 514 colas 514 guadalquivir 514 chine 514 cottage ln 514 dzamija 514 pioneer dr 514 school number 1 514 amstel 515 aquileia 515 feld bg st 515 cypriana kamila norwida 515 boyd st 515 chiusa 515 hochwald 515 antistaseos 515 bluff rd 515 zhd 515 lastra 515 supercarretera 515 schleswig 515 broome 515 stapleton 515 hunger 515 malinovskogo 515 rn 2 515 alefmalefmzalefdh 515 sony 515 timoteo 515 anas 515 trio 515 wayne st 515 mekong 515 jatoba 515 hillwood 515 ljubljanska 515 groveland 515 mdkhte 515 natalia 515 paradise rd 515 radio shack 515 lamadrid 515 leiten 515 sidings 515 nowej 515 dorm 515 sainsburys local 515 miskii 515 tangerine 515 horny 515 arran 515 private dr 515 b 81 515 aspen ct 515 eifelautobahn 515 kharkovskaia 515 dominicana 515 e 18 ah8 515 savona 515 2070 515 brecon 516 benfica 516 tanka 516 kir 516 menotti 516 st johns cemetery 516 montseny 516 richnoi piriulok 516 harp 516 lupin 516 rua boa vsta 516 71a 516 piute 516 byn 516 cra 28 516 signature 516 xiao tian chuan 516 agraria 516 vvur 516 riqulme 516 volcano 516 shan yang dao 516 confederate 516 bold 516 colibris 516 miskas 516 sportu 516 kool 516 n 24th st 516 yum 516 tojo 516 1 66 516 td bank 516 hough 516 oak rdge rd 516 1s 516 collision 516 borngasse 516 d 820 516 birgit 516 cranbourne 516 devine 516 upt 516 pk bd 516 ostrovskogo vulitsia 516 cards 516 elio 516 sio 516 v cassia 516 simpson rd 516 swing 516 crowne 516 w chester pk 517 sdis 517 guo daodabsdabdak hao 517 sultana 517 rn2 517 hannibal 517 hackett 517 kholiittai bhiiddrbhng 517 av g 517 munch 517 detmolder st 517 yam 517 studinchiskaia ul 517 esportes 517 l 21 517 bhiiddrbhng 517 new r 517 mgdl 517 carson st 517 solnichni piriulok 517 mccloud 517 proliv 517 qua 517 n63 517 highschool 517 amadeo 517 d 220 517 jmy 517 daodabsdabdak 517 stadt graz 517 simona 517 sinagoga 517 siwa 517 texas av 517 okrzei 517 zong guan t lu 517 reten 517 stn depuration 517 spring branch 517 paja 517 dewberry 517 bloomingdale 517 dubki 517 20b 517 lyzhnaia 517 s pablo av 517 sheridan st 517 sevigne 517 tortuga 517 3115 517 lloyds bank 517 micala 517 teaching 517 poplars 518 rosemount 518 raba 518 neva 518 navasiolki 518 abilene 518 giambattista 518 aviator 518 wladyslawa jagilly 518 shhalefb 518 av 5 518 g 11 518 powell rd 518 kopalnia 518 palas 518 mdm 518 us 275 518 quasimodo 518 yothin 518 traurhalle 518 shorewood 518 vysoka 518 rak 518 qusada 518 inside 518 keepers 518 agropecuaria 518 atwater 518 strana 518 1113 518 aubrac 518 sophin 518 zinnia 518 cheonannonsan exp 518 jarv 518 cheonannonsan 518 villefranche 518 hlm 518 shang hai rao cheng gao su 518 sahil 518 culvert 518 859 518 rasen 519 pond st 519 g 7 519 resurrection 519 martir 519 nijverheids 519 iugra 519 wassermuhle 519 4500 519 08a 519 town hall 519 woodbine av 519 reseda 519 abid 519 ibiza 519 kniovna 519 dimitrov 519 r leon blum 519 bouillon 519 clearwater cr 519 worth st 519 vyhlidka 519 argun 519 moseley 519 shanghai ring exp 519 apolo 519 ferte 519 zoll st 519 that 519 olney 519 heywood 519 oakton 519 hashim 519 1170 519 alefltjnb 519 lormeau 519 arda 519 1 280 519 skoki 519 placeta 519 prinsen 519 rhodfa 519 hoyuk 519 kollataja 519 knooppunt 519 maisonneuve 519 arrigo 519 flowage 519 guarderia 519 rutgers 519 proyecto 519 n 3rd av 520 trans niderrhein magistrale 520 mazipi 520 zahir 520 t10 520 pleasant gr church 520 instytut 520 spolecznej 520 weimarer 520 veranda 520 yon 520 b 91 520 d 919 520 ss48 520 pionir 520 us 380 520 carpinteria 520 greenhills 520 homestead dr 520 visual 520 apricot 520 kasai 520 cuckoo 520 kemuning 520 cellar 520 arrayanes 520 1007 520 climat 520 wern 520 r des chardonnerets 520 matilda 520 983 520 berliner al 520 dan in xin gyeongbuseon 520 niki 520 vergel 520 autoroute jean lesage 520 edwarda 520 livi 520 chefferi 520 cliff rd 520 powstancow wilkopolskich 520 abraq 520 c s migul 520 2310 520 badgasse 520 bgn 520 ilse 520 tpu 520 gladstone st 521 janes 521 bernardi 521 vittime 521 taille 521 wisconsin central railroad 521 arodromio 521 boylston 521 jungbugosokdoro 521 r des artisans 521 coxs 521 abrams 521 zadni 521 1214 521 gau 521 blackwater r 521 seabrook 521 filding 521 childers 521 glowackigo 521 tsz 521 norden 521 wurttem 521 v 2 giugno 521 zalisi 521 burns rd 521 basler st 521 drinks 521 long rd 521 wells st 521 rinaldi 521 spotted 521 dinner 521 morristown 521 krolewska 521 giraud 521 pacifica 521 carrollton 521 autumn ln 521 griggs 521 lli 521 callahan 521 margaridas 521 r du pressoir 521 bumi 521 nuclear 521 faidherbe 521 mccarty 521 henderson st 522 unidocente 522 e 13 522 tudela 522 chinh 522 xi huang gao su 522 stanley av 522 ptu 522 k 66 522 bethleem church 522 lucknow 522 selim 522 guo dao8 hao 522 b 17 522 collegiate 522 r du ft 522 quen elizabeth wy 522 a d muhle 522 alkotmany u 522 32nd st 522 minster 522 docks 522 a wisengrund 522 sparse 522 b 255 522 us post office 522 d 218 522 turkestan siberia railway 522 rmk 522 teck 522 ring 2 522 kirchfeld 522 cth h 522 duncan st 522 druck 522 finances 522 sr 72 522 1450 522 oya 522 schnellfahrstrecke koln rh main 522 ug 522 amapa 522 dezesseis 522 o3 522 kifern 522 preparatoria 522 planetarium 522 alianza 522 35th st 522 hsl zuid 522 distretto 522 pokoju 522 burgenland 522 illa 522 pedemontana 522 anaya 522 zion church 522 clubhaus 522 gz 522 gerolamo 522 bydgoska 522 get 522 pazar 522 jester 522 hazen 522 robespirre 522 autoroute felix leclerc 522 bnsf railway 523 brande 523 tourist information 523 jing jiu xian 523 dwrbyn 523 fevereiro 523 ash av 523 tikkurilan 523 khudayr 523 guild 523 khana 523 urozhainaia ul 523 vesta 523 willow wy 523 remise 523 fco 523 like 523 azteca 523 harrell 523 parkhill 523 v monte rosa 523 scalinata 523 boite 523 amberley 523 carretas 523 dushana 523 pridorozhnaia 523 singing 523 tavistock 523 molodiozhni piriulok 523 sr 32 523 frati 523 volgograd 523 stn app 523 mujer 523 pl du champ de foire 523 collada 523 memorial pk 523 wolga 523 gaylord 523 crocetta 523 poul 523 chishma 523 d 603 523 larsen 523 endeavour 523 sportkompliks 523 bakeshop 524 av georges pompidou 524 piles 524 baylor 524 eurospar 524 tirheim 524 a 64 524 osterleden 524 piramide 524 frisen st 524 health ctr 524 golondrinas 524 colne 524 davis ln 524 silvestro 524 teja 524 kazanskaia 524 earle 524 stevens rd 524 keluarga 524 r jules gusde 524 breast 524 horizons 524 neu westbahn 524 uniwersytet 524 foxtail 524 phrrdhphlllng 524 petrol stn 524 maples 524 dawson rd 524 periwinkle 524 jlalefl 524 svoboda 524 mwqf 524 bidwell 524 snowsho 524 roure 524 dixon rd 524 basingstoke 524 sherman av 524 annas 524 varshavskoi 524 n 25th st 524 e14 524 nhs 524 malmo 524 cuiaba 524 halda 524 guo dao186 hao 524 ban ji dian t shen hu xian 524 drinking 524 849 524 rawhide 524 bernice 524 leroy merlin 524 chenevires 524 csarda 524 classroom 524 riddle 525 durant 525 petty 525 gong qian chuan 525 glades 525 windsor st 525 korner st 525 bland 525 chinquapin 525 xiantag 525 fresco 525 litva 525 rick 525 6000 525 druzhbi vulitsia 525 perito 525 o 52 525 sinopec 525 eurospin 525 c 49 525 ivanova 525 guter 525 st andrews rd 525 jarzebinowa 525 gourmand 525 mutter 525 bousqut 525 amarante 525 v matteotti 525 addington 525 pulai 525 ripon 525 kholm 525 expressa 525 observatorio 525 naturel 525 primax 525 caspian 525 immo 525 anggerik 525 badan 525 semi 525 teo 525 artinos 525 grist 525 stn service e leclerc 525 d 216 525 christiana 526 ostkustbanan 526 acacia ln 526 rzemislnicza 526 ager 526 kunterbunt 526 kastanin st 526 1 bruhl 526 palefyyn 526 rongwu 526 sinistro 526 dessau 526 morvan 526 acme 526 zimmerman 526 kozi 526 sharpe 526 z sport pl 526 brink st 526 hackney 526 dao54 526 marks spencer 526 ahon 526 wun 526 cr 42 526 eastwood dr 526 phetkasem rd 526 etap 526 albert einstein st 526 architecture 526 aleflalefswd 526 anvil 526 kashtanovaia 526 pappelallee 526 rais 526 anonima 526 maple cr 526 weid 526 btn 526 libero 526 southwind 526 contour 526 v francesco crispi 526 sodu g 526 opiki 526 s40 526 d 206 526 opshtina 526 zuoxhli 526 helms 526 vanessa 526 nordlicher 526 d 205 526 meteor 526 theodor storm st 526 jingha 526 mecklenburger 526 hangzhou ruili exp 526 national rt 42 526 deutschen 526 raiffaizin bank aval 526 copley 526 colombes 526 ainmalefr 526 lois ln 526 depanneur 527 skipton 527 candle 527 pine ct 527 scott av 527 s215 527 6th st n 527 miradouro 527 golf rd 527 nassau st 527 psj 527 bussteig 1 527 sr 73 527 pineurst dr 527 smolinskoi 527 r40 527 b 109 527 belmont st 527 mladost 527 3a c 527 r de stn 527 chateau deau 527 vostochni piriulok 527 mami 527 nimitz 527 nansen 527 smith cemetery 527 profissionalni 527 azalefdgalefn 527 pichler 527 stringer 527 kings r 527 feurbach 527 tanzschule 527 amal 527 sr 35 527 egan 527 beam 527 kiwanis 527 viwing 527 jizni 527 kuan 527 r 4 527 wohnbau 527 carrizo 527 longviw dr 527 berna 527 1 57 527 e 24 527 3011 527 prioziornaia 527 alpenverein 528 dagubusangan exp 528 se 15th st 528 gasoline 528 robinhood 528 tupac amaru 528 knowle 528 pracy 528 fiacre 528 b 93 528 petar 528 qullen st 528 fame 528 rt t 528 juniper ln 528 bdy st 528 hegi 528 l 78 528 bebe 528 dagubusangan 528 dantoni 528 chana 528 honor 528 beacon st 528 d 178 528 crown st 528 ss4 528 cra 26 528 gruben 528 touristiqu 528 heike 528 odilon 528 dvaro 528 kone 528 gall gall 528 amsel st 528 palefrq 528 jurija 528 bleus 528 geyer 528 thalia 528 dwr 528 mayberry 528 munitsipalni 528 pl dr 528 drink 528 tullys 528 c po 528 prairi av 528 keikyu main ln 528 tool 528 skelly 528 graduate 528 democracia 528 34th st 528 corning 529 itea antirrio 529 w madison st 529 pom 529 meter 529 brig 529 bazin 529 montpelir 529 permanente 529 paderborner 529 dmitra 529 credit mutul de bretagne 529 ruta nacional 3 529 lyndon b johnson fwy 529 almacenes 529 mubarak 529 glengarry 529 kelly st 529 r av 529 bca 529 geren 529 dakar 529 interamericana 529 st 2240 529 jing ha gao su 529 navchalnii 529 wp 529 albrecht durer st 529 caserta 529 nikolskii 529 quiteria 529 migul hidalgo costilla 529 grabowa 529 krasny 529 santambrogio 529 hagebau 529 morrison rd 529 lodewijk 529 dwyer 529 antwerpse stwg 529 old state rd 529 c madrid 529 pretoria 529 bash 529 mahmoud 529 krista 529 2 3 529 gorham 529 elim 529 forces 529 dempster 529 no name 529 bodo 529 dickey 529 amizade 529 outfitters 529 gurtel 529 gaviotas 529 cailloux 529 met 529 kal 529 savickaja vulica 529 ep15 529 clearviw dr 529 parish rd 529 epsom 529 altair 529 horsepen 529 he nei chuan 529 cr 41 529 atha 529 stonewood 529 besucherpark 529 scherer 529 khomeini 529 r mozart 529 l 73 530 temple st 530 st stefan cel mare 530 orwell 530 b 189 530 taha 530 purtas 530 gdn ln 530 tongu 530 portobello 530 muri 530 jovanovitsha 530 bipa 530 olimpiiskaia ul 530 chengkun ln 530 gisborne 530 ottica 530 gallen 530 rua paraiba 530 eastland 530 duclos 530 ul mindiliiva 530 tariq 530 nerudova 530 chenai 530 pez 530 athens thessaloniki evzoni 530 loring 530 klinge 530 crew 530 kharab 530 lakeridge 530 heung 530 volkova 530 editado por ivan arenas 530 mogilki 530 atv 530 karvej 530 vtoraia 530 ethnikes 530 arteaga 530 williams av 530 charanvincharanvinanjilinsan 530 r43 530 browns cr 530 oazis 530 boones 530 medellin 530 thibault 530 old us 330 530 chengkun 530 us 5 531 kandi 531 licenciado 531 geziret 531 howards 531 qumada 531 mirova 531 ashgrove 531 amapolas 531 wyli 531 bosse 531 facundo 531 errekea 531 c sevilla 531 reale 531 fukagawa 531 rua 13 de maio 531 nogura 531 langton 531 mibili 531 s228 531 edoardo 531 julitte 531 arthurs 531 stuart st 531 blumenau 531 bundesanstalt technisches hilfswerk thw 531 stellwerk 531 b 244 531 roti 531 bos ln 531 fomento 531 c s francisco 531 1134 531 sheridan rd 531 barba 531 fontane st 531 b 95 531 80n 531 haverhill 531 hohenzollern st 531 knigi 531 tilsiter st 531 jinshan 531 bellas 531 saddlebrook 531 purdu 531 magdalene 532 s ctr st 532 volontari 532 socita 532 pamiati 532 cinaga 532 dao311 532 r danjou 532 griffin rd 532 poirirs 532 ny g 532 popov 532 pomoshch 532 shuanghli 532 ul tsiolkovskogo 532 consulo 532 northwood dr 532 taitung 532 54a 532 mqr 532 allmend st 532 prumyslova 532 kaffee 532 r jacqus brel 532 holland rd 532 patrice 532 mt pleasant church 532 oaxaca 532 muskrat 532 v umbria 532 sh 82 532 d 163 532 tsin 532 frdwsy 532 nemocnice 532 ujezd 532 makarinko 532 nsr 532 co rd 31 532 lagar 532 cle 532 macarthur bd 532 kas 533 v della vittoria 533 horca 533 giron 533 hawthorne rd 533 ping ru gao su 533 jakes 533 ho hong luan huang yuki ze honam exp 533 marinkapelle 533 harvard av 533 rossiiskoi 533 1 35w 533 red oak dr 533 westelijke 533 1870 533 darmstadt 533 bronze 533 tyrone 533 liberazione 533 s224 533 yardley 533 wingfild 533 bozeny 533 2150 533 beausoleil 533 areas agricolas 533 noce 533 swm2 533 bakke 533 stavok 533 doorman 533 trakiia 533 i shi zi dong ch dao 533 shalefhd 533 sentir cotir 533 royd 533 badminton 533 corran 533 gondola 533 tradgards 533 october 533 h3 533 seccion 533 sherwood rd 533 severin 533 sp82 533 aunweg 533 meurthe 533 evesham 533 byrne 533 dogwood rd 533 n fork feather r 533 towarowa 533 editado 533 sman 533 zeus 533 achadh 533 gd 533 t15 533 feed 533 s61 533 buckner 533 gaspare 534 kuc 534 jeil 534 bouqut 534 eccles 534 mokyklos 534 6th av n 534 s agustin 534 ligne shinkansen sanyo 534 bayswater 534 jackson dr 534 disna 534 wenhua 534 chemi 534 sklady 534 jaune 534 psaje 4 534 duqusne 534 bakr 534 antena 534 church c 534 sanhardio sinkansen 534 dogwood st 534 scott dr 534 barqa 534 luar 534 prolitarska vulitsia 534 giro 534 kibitzweg 534 v delle rose 534 ho hong luan huang yuki ze 534 dan pi xin 534 lilinthal st 534 irvin 534 creation 534 hemlock ln 534 whittington 534 mem 534 success 534 pl du 19 mars 1962 534 beverly dr 534 long chuan 534 ind bd 534 tianjin 534 paradise ln 534 rrrnguuddh 534 marijke 534 mistleto 534 liberty av 534 french broad r 534 eglise notre dame de lassomption 534 rrrnguuddh uulllikorrngiireei 534 uulllikorrngiireei 534 pap 534 corts 535 marbach 535 sargento cabral 535 winter trl 535 shaffer 535 shane 535 securite 535 inspiration 535 korla 535 brugse 535 guillen 535 moveis 535 hitchcock 535 ridgeland 535 szanjo sinkanszen 535 1225 535 chia 535 schaffhauser 535 highpoint 535 footbridge 535 hells 535 odeon 535 w mkt st 535 westway 535 policlinica 535 s western main ln 535 masi 535 aspen ln 535 rua 15 535 mlynsky 535 szanjo 535 admiralty 535 bulldog 535 rua marechal deodoro 535 pizzas 535 bexley 535 yeo 535 bocaiuva 535 l 29 535 parkwood dr 535 yale univ 535 turin 535 schonaur 535 tawil 535 a 89 536 regio 536 lome 536 yayasan 536 giao 536 amour 536 around 536 davy 536 ainlmyn 536 parrot 536 lambs 536 nhw 536 sivirni piriulok 536 kustbanan 536 lida 536 ferruccio 536 hwn 536 falcon dr 536 commonwealth av 536 beyer 536 awg 536 ful stn 536 grossa 536 r13 536 neunkirchen 536 elise 536 dao309 536 gertrudis 536 bk st 536 br 316 536 oranje st 536 cochabamba 536 vtb 24 536 ivy st 536 muvelodesi 536 maraichers 536 gaucho 536 maybach 536 bilorusskaia 536 marcellin 536 indus 536 l 190 536 zhimchuzhina 536 mibilni 536 aleflnby 536 ellis rd 537 torrecilla 537 lindenwood 537 catharina 537 bryant st 537 cth e 537 rude 537 burton rd 537 kampweg 537 branchement 537 iowa st 537 loli 537 tualatin 537 102 qun 537 dao12 537 inst 537 gup 537 ibm 537 saverio 537 gharbiyah 537 al des peuplirs 537 tham 537 1082 537 onder 537 hani 537 badger cr 537 rio parana 537 piters 537 korinthos 537 parr 537 av 4 537 azalea dr 537 volia 537 rund 537 romans 537 2120 537 pk vw 537 consulado 537 turksib 537 n 4th av 537 margarethen 537 rayburn 537 schwartz 537 085 537 jackson cr 537 e 52 e 60 537 aras 537 rejo 537 landon 537 whrf rd 537 traube 537 av rd 537 us 44 537 parcheggio disabili 537 ss51 537 margit 537 carbonera 537 hakodate 538 xin dong ming gao su dao lu 538 b145 538 savane 538 gallagher 538 rodovia comandante joao ribeiro de barros 538 1091 538 desvio 538 thelma 538 a 104 538 ferguson rd 538 karczma 538 eunice 538 mudurlugu 538 kastoria 538 petru 538 mcdougall 538 kz 538 koto 538 rundwanderweg 538 aleksandar makedonski 538 pochinok 538 piu 538 masraf 538 lusine 538 nuri 538 generali 538 betania 538 tulsa 538 post ch ag 538 anderson cr 538 w 17th st 538 sry 538 fysl 538 d885 538 abdon 538 raritan 538 lenoir 538 wingert 538 kickapoo 538 salvia 538 vejlevej 538 1 67 538 ctra santander vinaroz 538 villares 538 huset 538 aleflmdrste 538 bugambilias 538 mure 538 capricorn 538 united states hwy 34 538 dg 538 jenner 538 ss17 538 fix price 538 state ln rd 538 ishun 538 bennet 538 puget 538 a 59 539 polici 539 conrado 539 voru 539 bateau 539 fels 539 polish 539 suryatlalefk 539 cr 46 539 026 539 nashua 539 overhill 539 flaubert 539 cardigan 539 seen 539 abukuma 539 grazhdanskaia ul 539 bricomarche 539 botro 539 l 140 539 meadowlands 539 usina 539 gerrit 539 s eastern main ln 539 l 361 539 fuzhou 539 vinzenz 539 kathryn 539 v s francesco dassisi 539 stalina 539 joubert 539 stocker 539 baleflalef 539 leipzig hof 539 misko 539 dempsey 539 64n 539 stockport 539 ky 80 539 prise 539 chabrowa 539 scizka 539 bono 539 twhyd 539 sp81 540 fees 540 400th 540 hedges 540 mechanic st 540 raabe 540 n31 540 c libertad 540 escanaba 540 ezers 540 grindstone 540 cab 540 rua orquideas 540 clothes 540 ss73 540 d 937 540 tirras 540 peking 540 798 540 flevolijn 540 pacific st 540 narzissenweg 540 butler rd 540 dufour 540 l4 540 l 151 540 tranquil 540 mayenne 540 ipanema 540 darsena 540 homburger 540 horns 540 jaagpad 540 vanguard 540 barbados 540 stocks 540 kayseri 540 pommern 540 villon 540 kiler st 540 importadora 540 militsii 540 windmuhlen 540 praspekt 540 alefljzyrte 540 rua j 540 pok 540 vegyesbolt 540 d 675 541 wuke 541 ditali 541 dimiana 541 almaty 541 dinamarca 541 dkhtralefnh 541 waldler 541 n 432 541 osinovka 541 rt yellowhead 541 ashbrook 541 speed camera 541 menengah 541 manara 541 michelbach 541 hessenweg 541 antigo 541 kalinina ul 541 r des sapins 541 boven 541 kuznichnaia 541 rudnik 541 aleixo 541 rafala 541 r ampere 541 bachelet 541 saraiva 541 michels 541 schellfischlini 541 cranford 541 r du chene 541 boing 541 ikhlas 541 menara 541 complanare 541 gellert 541 amer 541 r50 541 fo shan i huan 541 chenal 541 patuxent 541 arcangel 541 jose maria morelos 541 1036 541 jhalefd 541 phetkasem 541 lennon 542 biologiqu 542 proximite 542 frankston 542 direita 542 denizu 542 ygalefl 542 tirpark 542 adkins 542 ostsee 542 dezoito 542 snowden 542 firth 542 r 24 542 byaleflyq 542 arodromnaia 542 caffetteria 542 valls 542 stetson 542 goth 542 zwirki 1 wigury 542 scranton 542 celio 542 neisse 542 trotter 542 grenzgraben 542 whiteill 542 salguiro 542 loir 542 vvdas 542 l 39 542 donjon 542 e 234 542 v giuseppe parini 542 coupe 542 krutoi 542 hiper 542 corta 542 bazan 542 gave 542 msr 543 hancock st 543 l 163 543 sharps 543 perdido 543 n 601 543 kharbin 543 turkestan 543 hnos 543 magnoliia 543 produce 543 kerk ln 543 koster 543 cisterna 543 stephanus 543 av georges clemenceau 543 july 543 lobby 543 kert 543 av hipolito yrigoyen 543 monastere 543 integracao 543 caseros 543 saka 543 jdwl 543 espina 543 henriqus 543 solidaridad 543 palladio 543 subdivison 543 090 543 nzoz 543 muscat 543 meireles 543 wars 543 old r rd 543 nijverheidsweg 543 kaminica 543 lohe 543 national trust 543 tinistaia ul 543 maan 543 telepizza 544 c s pedro 544 roo 544 sinkanszen 544 tove 544 murmanskoi 544 carlota 544 utopia 544 autoteile 544 bjork 544 fayetteville 544 zilinii 544 brise 544 rayo 544 calade 544 mlyna 544 html 544 therme 544 grote st 544 svirdlova vulitsia 544 khvtsh 544 herrick 544 vins 544 khai 544 shalefrain 2 544 aaa 544 wittener st 544 s 15th st 544 manty 544 belmont rd 544 rodin 544 garros 544 w 18th st 544 buddy 544 pacinotti 544 shainbh 544 gaziantep 544 naif 544 reichenbacher 544 dorfbrunnen 544 zwijger 544 leu 544 eisenacher 544 harding st 544 bryson 544 zay 544 sandusky 545 laurel rd 545 r de provence 545 etos 545 stud 545 caridad 545 rudy 545 tonga 545 fosters 545 hyland 545 cr av 545 szabadsag ter 545 charcuteri 545 g110 545 fischerweg 545 xinjiang 545 d 191 545 archery 545 stanislawa wyspianskigo 545 n 8 545 tamagawa 545 us 56 545 alpenblick 545 crossings 545 torretta 545 weinstube 545 anello 545 mapleton 545 rua da igreja 545 035 545 thomas muntzer st 545 conquista 545 sntr 545 fusilles 545 2031 545 d 224 545 pl des tilleuls 545 nasi 545 tenerife 545 greensboro 545 cela 545 streda 545 barnes rd 545 schoner 545 dekra 545 l 200 545 great clips 545 tejo 545 escola mun de ensino fundamental 545 graig 545 valenciana 545 ulivi 545 4a c 545 cecilio 545 shaheed 545 b 254 545 cra 25 545 sta elena 545 cementerio mun 545 automat 546 farmaci 546 ponent 546 persian 546 d 210 546 s central av 546 hov 546 slack 546 alpine dr 546 gesundheitszentrum 546 hofmark 546 rittergut 546 shan yang dian qi t dao ben xian 546 municipales 546 seoulogwaksunhwangosokdoro 546 mcra 546 74a 546 taylor dr 546 osterfeld 546 oziorni 546 c h 546 botaniqu 546 livres 546 fancy 546 caseys general st 546 movi 546 altona 546 ars 546 balance 546 travail 546 kapsalon 546 strani 546 staufen 546 bigelow 546 sip 546 flagstone 546 medersa 546 podlesna 546 4010 546 cuneo 546 lowery 546 rn 11 547 dawes 547 horou 547 brown av 547 stjepana 547 c 52 547 clifton rd 547 testa 547 lugano 547 wladyslawa broniwskigo 547 bight 547 venizelou 547 qnp 547 andreu 547 k 65 547 ransom 547 kostelni 547 hofgarten 547 jumping 547 las vegas 547 dennen 547 primarschule 547 miniature 547 neris 547 guo dao53 hao 547 s36 547 lidia 547 padiglione 547 farhan 547 reed rd 547 bryd 547 hart st 547 salz st 547 couronne 547 dan in luan huang yuki ze gyeongbu exp n 547 capao 547 sp62 547 bellerive 547 rn34 547 pommir 547 dama 547 christians 547 guo daodabdamdang hao 547 fakultesi 547 sportivna 547 middel 547 dani 547 las vinas 547 rua rosas 547 elgin st 547 255th 547 juri 547 50 st 547 circulation 547 parka 547 cliffe 547 av principal 547 stoneleigh 547 m21 547 reza 547 bordj 547 alter weg 547 e mkt st 547 pluit 547 l 15 548 brewers 548 montagna 548 angostura 548 ze14 548 blaze 548 s338 548 maasdijk 548 venn 548 centrul 548 roccolo 548 tamarac 548 alefbyb 548 coldstream 548 woodmere 548 alessandria 548 edgewood rd 548 r des forges 548 traiteur 548 ul vorovskogo 548 stotis 548 ch des pres 548 biblioteca mun 548 pecanha 548 populiren 548 l 74 548 caselle 548 moa 548 piatra 548 sondergade 548 rosebank 548 pravda 548 toa 548 esteve 548 tanyard 548 a45 548 d 323 548 meadow ct 548 ul bogdana khmilnitskogo 548 martin luther king 548 bunting 548 cumberland st 548 suur 548 kempen 548 a96 548 dellappennino 548 waldsidlung 548 ch du chateau 548 antona 548 a 94 549 hilal 549 bundesanstalt technisches hilfswerk 549 patron 549 s 40 549 vezer 549 c po de futbol 549 rockville 549 lanza 549 seoul ring exp 549 waldheim 549 calzados 549 pinedale 549 pirates 549 anyar 549 raduzhnaia ul 549 optima 549 macia 549 marl 549 etela 549 ferraris 549 rousse 549 dillard 549 clubhouse dr 549 herr 549 savino 549 khaled 549 k 68 549 heine st 549 first church of christ scintist 549 malina 549 dincendi 549 sik 549 b 30 549 rn38 549 65a 549 ingleside 549 1a c 549 pogranichnaia ul 549 tallinna 549 koninginne 549 sobral 549 n33 549 landscape 549 xiang gang you zheng hong kong post 549 ck 549 vercelli 549 puglia 549 medival 549 talca 549 waldfriden 549 stivana 549 zhongshan rd 550 coburn 550 strait 550 federal hwy 550 bunny 550 antrim 550 khralefyb 550 dao18 550 cintura 550 melba 550 reinigung 550 bogdan 550 semmelweis 550 odd 550 sackville 550 benin 550 auto del sur 550 zwembad 550 khmys 550 kalefml 550 1112 550 nahkauf 550 diqu 550 prtco 550 hbnym 550 daodabdamdang 550 rua sta luzia 550 paschi 550 jin chuan 550 collina 550 grobla 550 petanqu 550 duren 550 rsaleflt 550 bergische 550 xiandal 550 barsuki 550 l 116 550 jenni 550 ind rd 550 scar 550 badweg 550 robinwood 550 r marcel pagnol 550 oak gr rd 551 jerrys 551 ze16 551 nair 551 guo dao19 hao 551 lir 551 v giovanni boccaccio 551 charcoal 551 mornington 551 banamex 551 sadova st 551 korczaka 551 ulianova 551 airlines 551 acqu 551 bernat 551 hickey 551 race st 551 sayh 551 tanager 551 lidicka 551 kholiittai 551 nh9 551 clinico 551 fairviw cemetery 551 febbraio 551 corr 551 filles 551 neukirchen 551 ch du lavoir 551 sap 551 a 48 551 kasteel st 551 horcajo 551 b52 551 sykes 551 warsaw 551 giochi 551 zhkkh 551 ul gaidara 551 coles express 551 druckerei 551 bermudez 551 av sao paulo 551 career 551 s222 551 antero 552 superstrada 552 faisal 552 ledesma 552 pitrova 552 jeune 552 diving 552 acevedo 552 wormser 552 g15w 552 cisowa 552 kuytun 552 papen 552 bartholomew 552 prolitarska 552 shasha 552 valta t 552 39a 552 duncan rd 552 artima vulitsia 552 pitrovka 552 r du tilleul 552 clive 552 n 2nd av 552 dezenove 552 r du viux moulin 552 a d bahn 552 r centrale 552 pisacane 553 huffman 553 tunguska 553 renfrew 553 quiktrip 553 gongke 553 984 553 escondida 553 jewellery 553 doon 553 3300 553 latorre 553 corrado 553 falefdl 553 nhm 553 lougheed hwy 553 eren 553 guadarrama 553 gimnazium 553 chanhlu 553 hoher weg 553 mapa 553 windemere 553 r st nicolas 553 breil 553 dinkey cr 553 donnelly 553 zwaluw 553 dotoru 553 syh 553 v stambanan 553 krupp 553 paliw 553 n broad st 553 olive gdn 553 amphitheater 553 350th 553 mannerheimin 553 diga 553 w maple st 553 europea 553 rance 553 danske 554 pinfold 554 shang yu xian 554 gigante 554 don bosco 554 sycamore cr 554 dekalb 554 hsynyh 554 monastir 554 raqul 554 maiakovskogo vulitsia 554 shhdalefy 554 qasim 554 rwe 554 wug 554 laveri 554 albeniz 554 capanna 554 bethel cemetery 554 r 256 554 hausen 554 ctt 554 other 554 sibirskaia ul 554 ruta nacional 14 554 fasanen st 554 orchidees 554 brooks rd 554 fridays 554 riz 554 v mazzini 554 al des chenes 554 brodi 554 ribbon 554 guo daodasdakdass hao 554 thuringer st 554 schonblick 554 defence 554 westerplatte 554 daodasdakdass 554 w state st 554 stearns 554 aleflsryainte 554 zachary 554 n jefferson st 554 valletta 554 vantage 554 geary 554 890 554 arch st 554 cuil 554 hurontario 554 baross 554 pow 555 lerma 555 loise 555 washington rd 555 picton 555 neptun 555 maxime 555 1212 555 amanecer 555 avtozimnik 555 sudhang 555 prevost 555 pawn 555 geranium 555 auto de mediterrania 555 bourse 555 wheatsheaf 555 alimentacion 555 kupang 555 kincaid 555 leatherwood 555 v tevere 555 traktovaia ul 555 sp92 555 economia 555 cathy 555 martin dr 555 woodroffe 555 paddington 555 tinker 555 deerfild dr 555 onibus 555 lombardo 555 glenfild 555 mtn av 555 jordao 555 poz 555 airy 555 lothar 555 natale 555 1035 555 zagorodnaia ul 555 marjori 555 modas 555 knightsbridge 555 las rosas 555 jordan rd 555 liningradskii 555 volks 555 millstream 555 virrey 555 shinjuku 555 salar 555 barnabas 555 c paz 555 vegetable 555 marten 555 salvator 556 kinizsi 556 pavlovka 556 dabrowka 556 cervera 556 n scottsdale rd 556 jula 556 passa 556 davis cr 556 busto 556 886 556 pors 556 b23 556 cromer 556 kidul 556 hali 556 flstyn 556 mahon 556 evangelist 556 magnit kosmitik 556 a300 556 58a 556 jas 556 mollard 556 meisen 556 crepe 556 leonard st 556 vrede 556 microrregiao 556 prospero 556 khngngpsnsdddhlllg 556 hosok 556 rungkut 556 almaden 556 jeovas 556 biltmore 556 caswell 556 dpd 556 pawnshop 556 berzu 556 heck 556 garrido 556 consejo 556 portella 556 st 10 556 101 qun 556 ita vla 556 herenweg 556 v flaminia 556 trafford 556 jacki 556 tranquility 556 04 22 556 baywa 556 town open space 557 berga 557 b 35 557 old farm rd 557 khor 557 grenville 557 ul novosiolov 557 kalan 557 hopfengarten 557 cottage st 557 b 455 557 druid 557 ss115 557 a350 557 sabin 557 kochi 557 castellar 557 r verte 557 cerrillos 557 terrir 557 mlodzizowa 557 l 10 557 abruzzese 557 thumb 557 aleflmtalefr 557 damour 557 obaga 557 rigel 557 oc 557 b 462 557 buna vsta 557 spider 557 ss35 557 huasenv 557 kaminnaia 557 chirvonoarmiiska vulitsia 557 us 410 557 us 91 sr 18 557 gateway dr 557 r de normandi 557 silskogo 557 r jean monnet 557 malek 557 ivroopt 557 bann 557 arnika 557 fu in gao su 558 damaschke 558 bedford st 558 hermon 558 v antonio vivaldi 558 vous 558 chartwell 558 fear 558 maltings 558 ferri 558 heathcote 558 wybudowani 558 wheeler rd 558 nadrzeczna 558 mhaleffzte 558 cerezo 558 gilbert st 558 roseland 558 c dr fleming 558 tela 558 c g 558 kami 558 noronha 558 herndon 558 cadiz subdivision 558 esquinas 558 aussichtspunkt 558 vignoble 558 al nipodleglosci 558 elm ct 558 daphne 558 cantos 558 grind 558 r 5 558 mysliwska 558 d 942 558 mooney 558 belvidere 558 venetian 558 1131 558 sannitico 558 hutt 558 steinmetz 558 roku 558 powell st 558 nova poshta 2 558 vire 558 karla marksa st 558 av de liberte 558 scintifico 559 duin 559 hayes st 559 davis av 559 pokrovskii 559 pl du chateau 559 jakobs 559 andromeda 559 oak hill rd 559 wuppertal 559 florianopolis 559 cripple 559 moors 559 argonne 559 poitirs 559 imports 559 holbein 559 linne 559 geoje 559 ji yan gao su 559 c s isidro 559 iris st 559 travellers 559 auto del cantabrico 559 chambery 559 nato 559 sirena 559 pl de toros 559 bavaria 559 bourke 559 d 219 559 tsara 559 pr bernhard ln 559 ekonom 559 church of nazarene 559 799 559 etats 559 vermeer 559 edmonds 559 chantemerle 559 whiteorse 559 newington 559 voortrekker 559 vereinshaus 559 b 32 559 wildwood ln 559 ayr 559 cory 559 o 4 559 chrzcicila 559 stern st 559 salute 560 las flores 560 midland hwy 560 nc 24 560 55a 560 phillips st 560 s marcos 560 heil 560 magistrala weglowa 560 donskoi 560 quensland 560 ravintola 560 giusto 560 marini 560 crenshaw bd 560 tub 560 tubingen 560 ludal 560 l 288 560 m 19 560 spoor st 560 monitor 560 baroni 560 kalvaria 560 horses 560 svitog 560 vegetarian 560 nevers 560 windsor ct 560 tilos 560 e 421 560 burnley 560 gregoriou 560 henning 560 r de leurope 560 psmar 560 af 560 gregor 560 peripheral 560 antal 560 ladopotamos 560 wody 560 mai dang lao 560 signora 560 laituri 560 nelly 560 svalka 560 kandang 560 coty 560 livio 560 puro 560 blk 2 560 vinschgaur 560 c 65 560 kantonalbank 560 dimas 561 fink 561 applid 561 yanzhiwu 561 officers 561 aoki 561 gradska 561 lesne 561 inga 561 heilbronner st 561 sid 561 d 917 561 columbus av 561 barbiri 561 melia 561 beech rd 561 whitworth 561 besson 561 shmvrt 561 kuznitsova 561 hands 561 fino 561 huan ba tongri 561 yanzhiwu exp 561 baise 561 irene st 561 gosudarstvinnogo 561 himmelreich 561 jackass 561 rubis 561 massachusetts tpk 561 lohr 561 deer run 561 sheetz 561 c 47 561 bussardweg 561 carriage ln 561 plenty 561 harrison rd 561 walt 561 msainwd 561 leben 561 yan zhi wu gao su gong lu 561 a 63 561 impala 561 coyote cr 561 pando 561 moksha 561 konya 561 alexander rd 561 tourismus 561 virag 561 gauss 561 cassidy 561 hama 561 sharm 562 wolters 562 luga 562 mersin 562 chestnut ln 562 ring 1 562 pilas 562 3s 562 bahntrassenradweg 562 malmin 562 q jia 562 hilliard 562 held 562 consolidated 562 johann sebastian bach st 562 tg 562 algeri po ste 562 periphereiake odos 562 pizza express 562 hawthorne dr 562 panhandle 562 mitsui 562 kendo 562 gatewood 562 slim 562 garnett 562 bure 562 adriatico 562 jingzhu exp 562 d 619 562 199th 562 d 840 562 fontenelle 562 waldsee 562 rheintalbahn 562 assiniboine 562 planas 562 betsy 562 48a 562 piraus 562 piton 562 zmaj 562 hamam 562 despagne 562 sfr 562 alder cr 562 paynes 563 u haupt st 563 chongqing 563 valk 563 bagutte 563 oulad 563 olympus 563 uslug 563 gurre 563 nghia 563 voinov 563 gruppa 563 lauberge 563 15n 563 lancaster rd 563 deventerweg 563 associazione 563 r du marche 563 khngngnynnrnnlddgvai 563 n 330 563 damen 563 koblenz 563 89a 563 wels 563 tbain 563 schulbus 563 hollins 563 1141 563 fest pl 563 clairire 563 japon 563 springwater 563 gr13 563 upc 563 pipeline rd 563 n ctr st 563 autostrada dei trafori 563 mikhaila grushivskogo vulitsia 563 filmann 563 petsmart 563 bourges 563 kirsch 563 brune 563 jong 563 ivanovskoi 563 v xxiv maggio 564 kron 564 ruta provincial 11 564 huidu 564 hutchison 564 e 762 564 rheinland 564 superiure 564 dok 564 metzger 564 tumulo 564 sparte 564 castelnuovo 564 nabirizhni 564 commanderi 564 rd 1 564 whistler 564 rib 564 terzo 564 krasna 564 hayfild 564 imp des jardins 564 strawberry ln 564 dinamo 564 r st michel 564 merrimac 564 paroquial 564 travessera 564 jagodowa 564 phra 564 bw 8 564 checkpoint 564 markgrafen 564 uptown 564 narita 564 ze20 564 ready 564 toc 564 eglise st etinne 564 toulon 564 diamantina 565 vrtic 565 pidagogichiskii 565 cervino 565 krainiaia 565 emporio 565 vincents 565 aliksandar makidonski 565 d 468 565 randolph st 565 trafori 565 maschinenbau 565 fantasy 565 belanger 565 r alphonse daudet 565 plantation dr 565 sw st 565 salter 565 harewood 565 damiao 565 mili 565 zacarias 565 c 58 565 bei lu dao 565 drumheller 565 r des fosses 565 nid 565 203rd 565 deken 565 r10 565 buurtweg 565 spanin 565 duck cr 565 alefyylvn 565 residentialarea 565 schilling 565 wasen 565 g7 565 donskaia 565 sr 42 old sr 10 565 quntuop 565 shokoladnitsa 565 popovka 565 vladimirescu 566 bildungszentrum 566 stover 566 arta 566 deesilla 566 khshalefwrzy 566 akademia 566 zeeman 566 burrito 566 szkola podstawowa 566 sesto 566 ryan rd 566 suk 566 bhr 566 1950 566 aurrera 566 banksia 566 c s roqu 566 kuria 566 aromos 566 luang 566 dao186 566 escape 566 phyllis 566 tab 566 forages 566 zrodlana 566 marmol 566 vive 566 3102 566 dancing 566 batavia 566 ep9 567 elzen 567 bagley 567 parkfild 567 kors 567 ul zhukovskogo 567 picacho 567 kitchens 567 mbtsain 567 gobel 567 n90 567 oberen 567 rundkurs 567 royce 567 s305 567 wi 32 567 g321 567 r des vosges 567 lugova vulitsia 567 costa rica 567 n 134 567 v magenta 567 corey 567 v giovanni amendola 567 cuirt 567 hamadi 567 pitka 567 central pk 567 marata 567 garvey 567 bogoroditsi 567 cm 412 567 bathurst st 567 mahendra hwy 567 granary 567 lyautey 567 imia 567 yayla 567 3111 567 walsall 567 tilden 567 earl st 567 2115 567 boni 567 autonoma 567 wilson dr 567 falkenstein 567 qadim 568 essex st 568 pfarr st 568 kinderspil 568 jnc rd 568 meix 568 ambar 568 poso 568 987 568 hussein 568 baraji 568 holly rd 568 1115 568 kazachia 568 anhalt 568 carnarvon 568 brookstone 568 h r blk 568 s311 568 si guo zong guan zi dong ch dao 568 pinturas 568 fach 568 mowbray 568 ladder 568 bamiyan 568 028 568 mylly 568 rindo 568 hallam 568 baitul 568 ch de chapelle 568 19b 568 st georg 568 2d 568 laurel cr 568 v giovanni verga 568 hjr 568 souris 568 loveland 568 simpson st 568 mudrogo 568 beale 568 hornbach 568 v brescia 568 elektrotechnik 568 romualda traugutta 568 wayland 568 d 918 568 johnson dr 568 ivey 568 sviatitilia 568 frg 568 r de bourgogne 568 lh 568 botany 568 agadir 569 agion 569 integrated 569 milne 569 guo dao11 hao 569 nstrn 569 onda 569 beobia 569 dublin rd 569 chastni 569 patel 569 ishikarigawa 569 khoziaistvinni 569 longfild 569 aumailngkrdhrrngkko 569 pobidy ul 569 bunder 569 skrzyzowani 569 kuros 569 chiuahua 569 vidin t 569 daycare 569 vignerons 569 bartolome mitre 569 dade 569 phil 569 e 272 569 16b 569 aubrey 569 d 181 569 223rd 569 3002 569 dres 569 zapote 570 zamecka 570 amadeu 570 chop 570 camper 570 meric 570 1720 570 b 465 570 taleflb 570 l 1140 570 laut 570 shakhta 570 thatcher 570 us 66 old us 99 570 nautico 570 jokai u 570 anges 570 qlyb 570 shoprite 570 b 471 570 than 570 alderwood 570 precision 570 m 40 570 varidades 570 rua da paz 570 adel 570 livanivskogo 570 jamil 570 d 974 570 tolbukhina 570 fistivalnaia ul 570 oakes 570 v degli alpini 570 juniper st 570 sp78 570 slauson 570 52a 570 ma 28 570 johnson ln 570 vaio 570 laporte 570 t16 570 js 571 chartreuse 571 dinfanteri 571 abbot 571 gure 571 e 18th st 571 v cantonale 571 eo7 571 mississauga 571 e 21st st 571 randy 571 c sol 571 poinciana 571 komeri 571 midwest 571 r du 8 mai 571 tampico 571 g323 571 walpole 571 ameghino 571 tompkins 571 macao 571 beek st 571 n 301 571 romao 571 lamarmora 571 v della chisa 571 bina 571 fd 571 blus 571 hickory rd 571 wigwam 571 sandgrube 571 folwark 571 ctra de madrid 571 notaria 572 quiznos 572 irineu 572 dolly 572 cabras 572 maitre 572 arnold st 572 fisk 572 vysokoi 572 b 431 572 chapin 572 gilead 572 kharkivska 572 bluwater 572 engelbert 572 winkel st 572 jeanette 572 hanger 572 centra 572 guss 572 s207 572 ep10 572 veerweg 572 nevado 572 platane 572 summit rd 572 progriss 572 gavilan 572 abbud 572 tura 572 e church st 572 bayerische 572 varas 572 rovnsutoa100 572 gabelsberger st 572 yau 572 planning 572 pret 572 stubbs 572 tetto 572 milton rd 572 union church 572 lamp 572 stantsii 572 buchanan st 572 rosenheim 572 pickens 572 r des ormes 573 mt auk branch 573 dolce vita 573 synalef 573 systeme u 573 mcguire 573 dom byta 573 6362 573 n 9 573 tsintar 573 vinaroz 573 tumbleweed 573 r louise michel 573 whitestone 573 rito 573 grafen 573 amins 573 univ bd 573 av parana 573 depositos 573 erdo 573 kortrijk 573 cra 27 573 kulz 573 rhine 573 kbc 573 associations 573 wallace rd 573 mark st 573 spil pl 573 cevre 573 v pasubio 573 eugeniusza 573 higuras 573 r des carrires 573 pancake 573 v sta lucia 573 alefbrhm 573 lituvos 573 riverton 573 charme 573 bakar 573 keolis 573 hory 573 is rd 573 blu trl 573 chester st 573 centennial dr 573 liningradskoi shossi 574 massachusetts av 574 cornet 574 hato 574 karntner 574 idlewild 574 aksu 574 zhuno 574 combs 574 rmt 574 trevor 574 danji 574 e ctr st 574 m52 574 sano 574 gabrille 574 overflow 574 private sect name no 574 ivrea 574 local private sect name no 574 bifurcacion 574 bearn 574 us 3 574 mnkhm bgyn 574 carro 574 a414 574 orellana 574 distrital 574 khmelnytskoho 574 doran 574 national rt 13 574 wlkp 574 2600 574 jr kagoshima honsen 574 s312 574 sagamore 574 stresemann st 574 verreri 574 niftibaza 574 se st 574 s wales main ln 574 mhlh 574 section 3 574 e 801 574 bild 575 rozen st 575 leuvense stwg 575 kopi 575 goa 575 chemnitzer st 575 birizka 575 tanners 575 schumann st 575 baudelaire 575 josef st 575 sault 575 nivo 575 kuwait 575 hmalefdy 575 riachulo 575 klinovaia 575 suka 575 galitskogo 575 lynn st 575 ainalefmr 575 pikin 575 sh7 575 brad 575 tanneri 575 halstead 575 bloor 575 buissons 575 cocina 575 yang an t lu 575 glavnaia 575 b 203 575 fuchsweg 575 chantry 575 covadonga 575 vody 575 ft st 575 bradford rd 575 chancery 575 veteran 575 n52 575 208th 575 nang 575 inspection 575 boro 575 virkhniaia ul 575 mita 575 g111 575 kea 1 575 tran 575 d 159 575 infirmary 575 1130 575 reit pl 576 r pirre brossolette 576 senioren 576 trucks 576 yukun exp 576 pyeong qigyakhli luan huang yuki ze 576 troitskaia 576 vorder 576 av f 576 zhong yang xian 576 caddo 576 d 606 576 eclipse 576 aurbach 576 kostiol 576 av du marechal leclerc 576 yukun 576 b 209 576 ebe 576 ikot 576 dich 576 shipley 576 s sebastian 576 colors 576 samarkand 576 toronto district school board 576 muan 576 ting1 576 tournai 576 856 576 qigyakhli 576 bayda 576 ashcroft 576 jose hernandez 576 balbin 576 pitrol 576 klettersteig 576 cordelirs 576 nau 576 daily yamazaki 576 steingasse 576 d 215 576 tongyeong 576 lerdo 576 3106 576 cataluna 576 co rd 30 576 n21 576 holzbau 576 us 131 576 linii 576 ep16 577 aleksandrowka 577 lalef 577 cercado 577 f2 577 transformator wizowy 577 vecchi 577 ostergade 577 chapaiva ul 577 flow 577 bard 577 smak 577 aleflbnk 577 balcarce 577 daquitaine 577 reformhaus 577 aix 577 stredisko 577 csapos kut 577 lp 8 577 jabir 577 ddhi 577 williams cr 577 kecil 577 admin 577 shale 577 frind 577 kalk 577 seidel 577 burger st 577 locust av 577 wilhelm busch st 577 synagoge 577 pong 577 frituur 577 passion 577 yu in luan huang yuki ze 577 carlsbad 577 dorozhni 577 odvojak 577 marii sklodovskoi kiuri vulitsia 577 e 125 577 janvir 577 r des tulipes 577 pleasure 578 a 41 578 lituva 578 2027 578 967 578 dzialkowe 578 porter rd 578 nahon 578 estuary 578 poinsettia 578 w jefferson st 578 huong 578 haharina 578 condamine 578 maybank 578 prunus 578 sr439 578 landshuter 578 akushirskii 578 sklodovskoi 578 v st 578 b 43 578 hopfen 578 manso 578 nh47 578 dong jiu zhou zi dong ch dao 578 najswitszego 578 autov del norde ste 578 2610707 578 milutina 578 eschen 578 pine rdge rd 578 shaurma 578 abdellah 578 kop 578 kepala 578 calzado 578 voronizh 578 roxas 578 firm 578 epulet 579 rua palmeiras 579 stockbridge 579 d 996 579 mallard dr 579 manaus 579 l 523 579 wendel 579 st 2244 579 coudray 579 lanark 579 buk 579 n 430 579 n 1st av 579 badi 579 union cemetery 579 brook rd 579 us 66 historic 579 hunyadi u 579 bruckenweg 579 northbrook 579 carrira 579 luki 579 a65 579 cavallo 579 dalefwd 579 sovkhoza 579 issa 579 meike 579 sp61 579 40th st 579 safra 579 elmwood dr 579 blackberry ln 579 melchor ocampo 579 pale 579 alpha bank 579 malinovaia 579 kosmitik 579 arantes 579 wizowy 579 bilac 579 papineau 579 marlene 579 viljandi 579 pilone 579 ep11 579 dourado 579 eyth 579 dari 579 mercadinho 580 minister 580 huan qi tongri 580 rollin 580 stadtwald 580 host 580 bernabe 580 denia 580 vorm 580 umgeungs st 580 hshtm 580 jagdhutte 580 ultra 580 studi 580 cerny 580 pitrovskoi 580 alefy 580 bogen st 580 lapin 580 battaglia 580 r grande 580 verdugo 580 gezelle 580 nanarupusu 580 reconquista 580 frontenac 580 damon 580 pomeroy 580 ctra de gijon a sevilla 580 himalaya 580 muhl bg 580 r des frenes 580 trot 580 students 581 mascarenhas 581 tsintralni rynok 581 pinho 581 polyvalent 581 tite 581 santiago del estero 581 e 871 581 garii 581 tuba 581 uss 581 st 8 581 meri 581 samgeori 581 35w 581 viajes 581 locust ln 581 csapos 581 surprise 581 gottinger 581 05 23 581 socidade 581 oliver st 581 b 170 581 coleridge 581 rampart 581 pionirskii piriulok 581 binder 581 krzysztofa 582 larrea 582 wilhelmina ln 582 young rd 582 douane 582 ul dimitrova 582 hebel 582 heatherwood 582 agenzia 582 torrens 582 ermine 582 e 803 582 2a av 582 diba 582 d 960 582 crawley 582 barge 582 obligado 582 zetkin 582 c del sol 582 mdyryte 582 sivastopolskaia 582 parku 582 navoi 582 d 948 582 r de prairi 582 karlsruher st 582 liniinaia ul 582 ss62 582 chuck 582 calvary baptist church 582 superiur 582 wynn 582 mein 582 frhng 582 chartered 582 zhi jian chuan 582 chennai 582 st nikolaus 582 vendeglo 582 potable 582 roos 582 fox st 582 kurfursten 582 central exp 582 tisserands 582 worship 582 appulo 583 olaf 583 planalto 583 ser 583 a t u 583 recife 583 republicii 583 amancio 583 coria 583 yongin 583 galveston 583 tuwima 583 dobo 583 iuzhno 583 bua 583 basile 583 lade st 583 ep17 583 rosali 583 baz 583 landsberger st 583 gasthuis 583 gerber st 583 calders 583 sidewalk 583 13b 583 tickets 583 jyvaskylan t 583 orzechowa 583 kardynala stefana wyszynskigo 583 perugia 583 venti 583 stredni 583 kremser 583 lan nan gao su 583 paulou 583 shar 583 khirr 583 peyre 583 aftokinitodromos 584 kentrikis 584 elche 584 mwy 7 central peloponnese 584 palmares 584 xi pan gao su 584 zoltan 584 5 de febrero 584 myanmar 584 autokinetodromos 7 kentrikes peloponnesou 584 kennedy dr 584 kentrikes peloponnesou 584 r st louis 584 autobahn 7 584 deere 584 autoroute 7 peloponnese centrale 584 autoroute 7 584 strzelecka 584 giovan 584 jinghu ln 584 archers 584 batyra 584 state trust 584 aftokinitodromos 7 kentrikis peloponnisou 584 galdos 584 n 629 584 xipan exp 584 lal 584 hampton ct 584 merlo 584 marina dr 584 bowles 584 silent 584 idlewood 584 stokhod 584 autobahn 7 zentral peloponnes 584 autokinetodromos 7 584 formula 584 yunikuro 584 xi gu chuan 584 19th av 584 aftokinitodromos 7 584 platanos 584 kentrikis peloponnisou 584 peloponnese centrale 584 wildwood rd 584 schapman 584 zentral peloponnes 584 mormon 584 central peloponnese 584 obecna 584 ventas 584 n r 584 khtybt 584 nasa 584 mwy 7 584 laiteri 585 876 585 acacia st 585 kabupaten 585 h2 585 mausoleum 585 lautaro 585 1 go travnia vulitsia 585 spina 585 ouse 585 fox ln 585 a 60 585 qro 585 d 947 585 llana 585 kline 585 abby 585 galgen bg 585 nye 585 jims 585 cyril 585 pus 585 bleich 585 juan escutia 585 vastkustbanan 585 casimir 585 stoneenge 585 sudliche 585 3a av 585 fawr 585 rua primeiro de maio 585 s 16th st 585 anp 585 dorada 585 redon 585 canara 585 dat 585 v buren st 585 ballon 585 juca 585 athol 585 ist 585 ainsworth 585 cerreto 585 rp7 585 tuileris 585 av du marechal foch 585 voyages 585 nick 585 rua dez 585 melon 586 conejo 586 salonu 586 herrero 586 campground rd 586 c antonio machado 586 sexton 586 1111 586 townhall 586 arkhangila 586 pillar 586 asin 586 nagano 586 miasnoi 586 n 627 586 staza 586 fairwood 586 benz st 586 campa 586 riverside av 586 lortzing st 586 kanyakumari 586 r du couvent 586 689 586 valette 586 capolinea 586 okolovrhardstin 586 kanetuzidousiyadou 586 posiliniia 586 rua maranhao 586 fisica 586 bedok 586 ant 586 mex 40d 586 walgau 586 kan etsu jidosha do 586 limousin 586 gumnasio 586 stelvio 586 hongkong 587 plantation rd 587 artisan 587 telecom italia 587 savenay 587 r jacqus prevert 587 piva 587 cra 24 587 baghdad 587 stout 587 boskij 587 stick 587 carpenters 587 uralskii 587 hidayah 587 lebensmittel 587 obelisk 587 layout 587 presidential 587 s13 587 centenaire 587 cj 587 e oak st 587 gazity 587 nemcove 587 duzen 587 weissenbach 587 lauterbach 587 mulberry ln 587 ribes 587 lale 587 us 21 587 embarcadero 587 bernhard st 587 beltline 587 truro 587 trial 587 litoranea 587 49a 587 blanken 587 yosef 587 av du marechal de lattre de tassigny 587 margot 587 rembrandt ln 588 laire 588 eleutheriou benizelou 588 nera 588 veloso 588 chuncheon 588 4100 588 padel 588 guo daodabdam hao 588 retro 588 300th st 588 fuxing 588 garganta 588 schnee 588 yang funo qing shan 588 childhood 588 nutmeg 588 breakwater 588 dodson 588 ti yu guan 588 mcfarland 588 peloponnes 588 pnb 588 daodabdam 588 babylon 588 fiddlers 588 maddox 588 saco 588 garages 588 weston rd 588 guo dao58 hao 588 achterweg 588 tigli 588 yura 588 konichnaia 588 potsdamer st 588 njm 588 sanmarukukahu 588 ah75 589 california st 589 ungaretti 589 mouton 589 r gabril peri 589 sundeteria 589 spluga 589 cascine 589 resturant 589 ban ji dian t jing du xian 589 kub 589 komsomolskii piriulok 589 li qun luan huang yuki ze yeongdong exp 589 l 113 589 gurnsey 589 burkina 589 lagan 589 tatra 589 leoncio 589 valentina 589 marsden 589 old sr 10 589 eurico 589 mustashfa 589 07 25 589 barbary 589 b18 589 drugs 589 serravalle 589 liz 589 mdou 589 canoas 589 rodovia presidente dutra 589 deich st 589 rua bela vsta 589 buche 589 abaia 589 guo dao13 hao 589 oczyszczalnia 589 michelin 589 2nee 589 claiborne 589 v verdi 589 spalding 589 match 589 cota 589 material 589 p6 590 frobel st 590 hegel 590 3007 590 s104 590 06 24 590 martinet 590 hillviw dr 590 muhlen bg 590 honamgosokdoro 590 boissire 590 dak 590 angmad 590 a kanal 590 e 82 590 khwy 590 3n 590 hirtenweg 590 janice 590 presidents 590 norwalk 590 meisin 590 evergreen av 590 donde 590 guo daodak hao 590 breed 590 dean st 590 norwida 590 ashbury 590 kings ct 590 carman 590 kamal 590 r denis papin 590 navarre 590 bilingu 590 chipmunk 590 gettys 590 hip 590 2222 590 cremona 591 kennet 591 tovarni 591 vermelha 591 tyn 591 tupelo 591 rega 591 ladang 591 kart 591 bethel rd 591 xipan 591 204th 591 kaplan 591 hearthstone 591 milagros 591 donaldo 591 xvii 591 neusser 591 guardia civil 591 130 01 591 bronco 591 zaniatosti 591 hotutomotuto 591 canario 591 kanava 591 bolsa 591 hesiru 591 marburger 591 trend 591 mercat 591 n 620 591 canal de nantes a brest 591 mirnaia ul 591 reynolds rd 591 sperberweg 591 chu yun ji dao 591 fiscal 591 l 75 591 dbstalefn 591 korenblom 591 fahre 591 campion 591 landerneau 591 e 55 e 60 591 cebu 591 girasol 591 about 591 houston st 591 dn66 592 this 592 v zara 592 vanir 592 us 611 592 jasmins 592 310th st 592 brennan 592 plato 592 glinnglng 592 borro 592 have 592 kanala 592 bidnogo 592 popiluszki 592 parkering 592 filomena 592 lure 592 peloponnisou 592 historische 592 mabry 592 susa 592 mosevej 592 r de lhopital 592 ulybka 592 steeplechase 592 raval 592 jernbane 592 glava 592 imigrantes 592 porter st 592 bushaltestelle 592 sadah 592 almas 592 erzincan 592 kansai 592 gallardo 592 herzl 592 broadwater 592 palestro 592 pivnichna 593 maurinne 593 ovrazhnaia ul 593 solitude 593 mitchells 593 saltillo 593 sosta 593 bergstation 593 lisia 593 truite 593 umbro 593 n frnt st 593 50k 593 moraine 593 smetanova 593 oak ct 593 b 97 593 royal oak 593 berliner pl 593 pocahontas 593 stone st 593 moudania 593 antioch church 593 bilefelder 593 warande 593 shoreline dr 593 rash 593 c 51 593 st joseph 593 sens 593 elizabeth dr 593 weather 593 hanse 593 b 73 594 bayviw dr 594 vytsmn 594 beagle 594 cameo 594 v luigi pirandello 594 redland 594 litniaia 594 sezione 594 neptuno 594 perrires 594 woodley 594 brett 594 sobornaia 594 oconnell 594 2609600 594 thar 594 gio 594 herminio 594 bank spoldzilczy 594 pr bernhard st 594 feijo 594 1 43 594 herne 594 pasealekua 594 av pasteur 594 w oak st 594 montgomery rd 594 vilaine 594 pari 594 cefn 594 susana 594 latino 594 hacia 594 belvoir 594 gypsum 594 essarts 594 indian trl 594 dentist 594 r25 594 rua alagoas 594 zabolo 594 rennsteig 594 rizzo 594 carrara 594 lussac 594 n coast ln 594 koppelweg 595 campania 595 larchen 595 zimmerei 595 orin 595 elektronik 595 dere 595 hlalefl 595 valdemar 595 lizy 595 domain 595 pasika 595 bratislava 595 athanasiou 595 garrigus 595 raab 595 smolinskaia 595 hui jin ruo song shi huato luno ke 595 zashchity 595 makarova 595 v s giorgio 595 cisne 595 us 90 alternate 595 stadtverwaltung 595 az 93 595 2006 595 walnut ln 595 hamud 595 manen 595 dvorik 595 snoman trl 595 ohio tpk 595 ep6 595 v sicilia 595 qryt 595 jamal 595 nottoway 595 wurth 595 rnge rd 595 svenska kyrkan 595 byers 595 n 44 595 sunnyvale 595 beeke 595 studanka 595 veldweg 595 tillman 595 ladour 595 arequipa 595 skye 595 civica 596 magee 596 vermillion 596 platani 596 conservancy 596 savickaja 596 posadas 596 pedrosa 596 pleasant hill rd 596 b38 596 corbeil 596 b 279 596 a 36 596 kentucky av 596 harpers 596 vala 596 dion 596 l 3080 596 raduzhnaia 596 volgogradskaia 596 carte 596 abba 596 jr heng xu he xian 596 c central 596 depuradora 596 serene 596 web 596 khmrbndy 596 rois 596 co rd 26 596 st 7 596 rog 596 victorin 596 megan 596 skate pk 596 tennishalle 596 869 596 jr yokosuka ln 596 c so europa 596 espagne 596 toom 596 sousse 596 castilho 596 mashru 596 bijou 596 hakim 596 l 22 596 katz 596 danny 596 pits 596 sinan 596 louka 597 bellegarde 597 saveurs 597 ayres 597 br 369 597 loti 597 caroline st 597 d 158 597 settle carlisle railway 597 av de espana 597 domus 597 lilli 597 koil 597 amapola 597 iancu 597 mistnogo 597 coracao 597 sarandi 597 provinzial 597 palmirs 597 allo 597 tinistaia 597 everglades 597 v francesco baracca 597 soldat 597 thirry 597 birdi 597 uiut 597 gods 597 n 112 597 quinones 597 volturno 597 mitterweg 597 mohr 597 pl de liberation 597 co 7 597 aguda 597 villarejo 597 st jugend 597 samba 597 teto 597 urheilu 598 trilho 598 ho hong luan huang xin 598 relay 598 misuvdonatu 598 schuhhaus 598 parvis 598 xunhag 598 kawm 598 pl jean jaures 598 steinkreuz 598 qalefym 598 record 598 trim 598 frhalefn 598 okna 598 midori 598 apc 598 portofino 598 mcneil 598 yo 598 patrocinio 598 r du paradis 598 vilas 598 1023 598 dao170 598 somers 598 catalans 598 bch bd 598 ministirstvo 598 volg 598 honam hsl 598 sta teresa 598 c7 598 gaviota 598 royston 598 festhalle 598 talavera 598 dovatora 598 cabra 598 zorrilla 598 ildefonso 598 materiaux 598 brookline 598 madden 598 tains 599 kennel 599 recreativa 599 phat 599 barbarossa 599 shkola 5 599 mandi 599 rua treze de maio 599 alps 599 v tagliamento 599 chiu 599 e411 599 horster 599 5n 599 aida 599 ithaca 599 parus 599 sour 599 bennett st 599 ramalho 599 v liguria 599 harlow 599 prudy 599 anemones 599 leesville 599 hildegard 599 llbnyn 599 bole 599 martiou 599 versicherung 599 harju 599 agenor 599 mestsky 599 kivi 599 kurzer 599 benetton 599 mlainb 599 hasel 599 chirvonoarmiiska 599 langdale 599 v luigi cadorna 599 grigoriia 599 elizy orzeszkowej 599 cavalcanti 599 franje 600 wildpark 600 schuler 600 rua jose bonifacio 600 makelan 600 w side fwy 600 b 202 600 tag 600 escultor 600 mathilde 600 v padova 600 guan yu 600 sharqiyah 600 jingmetoro 600 berufsschule 600 burgundy 600 noodles 600 redhill 600 richland cr 600 20th av 600 fed 600 baurn 600 krefelder 600 fridrichs 600 gas stn 600 berliner pumpe 600 urozhainaia 600 a36 601 brookhurst 601 einkaufszentrum 601 sweetbriar 601 guo daodasdassdam hao 601 228th 601 ahumada 601 survey 601 azucenas 601 sheep cr 601 thon 601 daodasdassdam 601 chequrs 601 familinzentrum 601 residency 601 w church st 601 foothill fwy 601 br 470 601 long lake 601 ipes 601 pains 601 bonifatius 601 kan etsu exp 601 butik 601 beaver brook 601 sharif 601 832 601 ss77 601 041 601 helados 601 passau 601 indre 601 moschee 601 marcia 601 estatua 601 idea 601 boufs 601 eyre 601 deer run rd 601 r ste anne 601 sr 8 601 goud 601 brau 602 cinqunta 602 puit 602 kapi 602 maje 602 ken de ji 602 obs 602 plaqu 602 s205 602 hermoso 602 jezusa 602 litnii 602 brighton main ln 602 tomaz 602 chancellor 602 maleflk 602 mitra 602 maritim 602 negros 602 bibliothequ municipale 602 885 602 woodmont 602 geelong 602 ott 602 fabriks 602 wild rice r 602 phdhm 602 cr 36 602 rua rio grande do sul 602 ingrid 602 steiger 602 ligne de lyon perrache a marseille st charles 602 gorlitzer 602 roosevelt hwy 602 trift st 602 e17 602 watson st 602 luzon 602 faculte 602 ligne de lyon perrache a marseille st charles v grenoble 602 s stambanan 602 2700 602 kuca 602 kazmunaygas 602 bij 602 faso 603 calaveras 603 rodina 603 v torquato tasso 603 boschetto 603 peschira 603 croisette 603 daodabdatdab 603 baileys 603 pinsionni 603 cormorant 603 mex 57 603 komercni banka 603 boschi 603 ss3 603 a wasserturm 603 oborony 603 gvardiiskaia ul 603 volkshochschule 603 pisochnaia 603 quart 603 guo daodabdatdab hao 603 heredia 603 qdalefhamza 603 dzialkowa 603 rouget 603 dress 603 wonder 603 visioli 603 vendee 603 bao mao gao su 603 oulun 603 jameson 603 snell 603 chilton 603 2132 603 us 167 603 kwa 603 las heras 603 minskaia ul 603 d 6015 603 l 82 603 ficus 603 cth b 603 mbvalef 604 ravenwood 604 carthage 604 antofagasta 604 aquduc 604 caccia 604 poteau 604 marley 604 conni 604 maude 604 chemnitz 604 r des aubepines 604 kucuk 604 compagni 604 meadowbrook dr 604 fischteich 604 casali 604 v della staz 604 totem 604 1 39 604 switzerland 604 varna 604 969 604 oakwood av 604 underhill 604 letras 604 blk 1 604 jr kisei ln 604 outpost 604 oakleigh 604 frome 604 schweizerische 604 roman rd 604 club ho 604 arl 604 47a 604 molin 604 n 22nd st 604 westerwald 604 dinkey 605 ingolstadt 605 sumber 605 feart 605 highfild rd 605 silas 605 allgemeinmedizin 605 ivi 605 l 141 605 bezirs 605 short rd 605 teboil 605 milo 605 matas 605 bateman 605 brt 605 melis 605 gouverneur 605 narcis 605 s bway st 605 folk 605 b14 605 soiuz 605 arctic 605 palisade 605 flughafen st 605 tsentralnaya st 605 loaf 605 northern bd 605 saray 605 driving rnge 605 tamandare 605 d 203 605 autonomo 605 delia 605 bhmn 605 oak rdge dr 605 v del mare 606 r lavoisir 606 novi piriulok 606 poghota 606 yalefsyn 606 31st st 606 paragon 606 vodni 606 cabezas 606 karlsbader st 606 r de bel air 606 poplar rd 606 n57 606 e jefferson st 606 liber 606 regensburger st 606 cinder 606 bene 606 br 285 606 grillo 606 schanz 606 hering 606 dozsa gyorgy ut 606 passeggiata 606 n central av 606 oillets 606 verissimo 606 minskoi 606 adalbert stifter st 606 abingdon 606 toshkent 606 lynnwood 606 durer st 606 yasin 606 keren 606 halli 606 1055 606 purisima 606 engineers 606 r rene cassin 606 canos 606 r 254 606 opale 606 incorporated 606 montalvo 606 nervo 606 balkan 607 hortencias 607 worldwide 607 s cristobal 607 bayreuther 607 tronco 607 cherokee dr 607 s 36 607 fallbrook 607 dahlia st 607 carsharing 607 dukuh 607 morava 607 radnor 607 sp63 607 gr canal 607 botanichna 607 lenclos 607 ketteler st 607 detmolder 607 durham rd 607 silverado 607 advance auto parts 607 cypriana 607 saul 607 deluxe 607 national hwy 607 nordsee 607 b83 607 amsel 607 banco santander 607 moderne 607 roanoke r 607 paige 607 dlhe 607 rockdale 607 meitner 607 fortes 607 prats 607 100n 607 anak 607 campanario 607 perry rd 607 sp90 607 theresia 607 strandvejen 607 stinson 608 29th st 608 restaurants 608 thomas dr 608 luzern 608 floris 608 collin 608 une 608 rivero 608 intercity 608 sachsische 608 stratford rd 608 roseville 608 a d hohe 608 beechnut 608 chemins de fer abidjan niger 608 n 21st st 608 sligo 608 sp 330 608 kau 608 ozirna 608 hazait 608 palmer st 608 halfords 608 kopernikus st 608 olimpiiskaia 608 haystack 608 b 68 608 kukhnia 608 deacon 608 mesta 608 1032 608 d550 608 kent rd 608 1107 608 botelho 608 chiltern main ln 608 iuzhni piriulok 608 aviacion 608 elsi 608 dio 609 wrzshy 609 siri 609 sanches 609 krist 609 dechets 609 rodovia governador antonio mariz 609 245th 609 emmett 609 e 01 e 80 609 pond rd 609 mabini 609 sp 270 609 wi 35 609 mwl 609 viilles 609 ze13 609 b 246 609 kalina 609 hatcher 609 huang yan gao su 609 schweriner 609 kleist st 609 muse 609 aleflainalefmte 609 arbour 609 delfino 609 echangeur 609 surtes 609 capac 609 woodside rd 609 henriquz 609 a weir 609 huangyan exp 609 cornish main ln 609 pritchard 609 novotel 609 sohn 609 v s lorenzo 610 pyeongtag 610 chshmh 610 parakampse 610 aguilas 610 uslugi 610 flisen 610 politsiia 610 dao4 610 annen 610 a muhlen bg 610 51a 610 zephyr 610 canfild 610 sada 610 materiales 610 florencia 610 topfer 610 c colon 610 c 53 610 plymouth rd 610 b 311 610 prager 610 gloucester rd 610 sovkhoz 610 n 234 610 pon 610 harrat 610 slavianskaia 610 putt 610 ksar 610 cra 22 610 v nuova 610 spitz 610 dunkirk 610 dao53 610 rodelbahn 610 juno 610 jana matejki 610 1989 610 bldyte 610 r des pommirs 610 kisei 610 zhong jin chuan 610 palats 610 cobre 610 malfa 610 1 580 610 badener 610 g92 611 werft 611 dermatologic 611 sean 611 ukrtilikom 611 makam 611 ela 611 d 166 611 pirvomaiskii piriulok 611 kilkenny 611 aubert 611 cth f 611 wire 611 b 253 611 a92 611 grup 611 52n 611 mulla 611 s jefferson st 611 eliot 611 br 267 611 s maple st 611 epad 611 lany 611 3o 611 specialist 611 qwr 611 smith av 611 antelope cr 611 aguascalintes 611 iakutskaia 611 new zealand contiguous zone 612 holder 612 nvvh 612 vincennes 612 3008 612 bonheur 612 papir 612 v guglilmo oberdan 612 970 612 urbain 612 gallup 612 17th av 612 g10 612 mania 612 mose 612 hosteria 612 r henri barbusse 612 ebony 612 blucher st 612 centers 612 katun 612 962 612 sycamore ln 612 national rt 56 612 cameron st 612 sidlerweg 612 ouches 612 mallet 612 waldrand 612 kona 612 theophile 612 cedre 612 koshesi 612 zambrano 612 lidir 612 aryk 612 beeive 612 sanno 612 shangno 612 amberwood 612 demetrio 613 fox cr 613 jugendhaus 613 arches 613 gentse 613 w pine st 613 woburn 613 arabe 613 soba 613 louvain 613 weisses 613 pj 613 odakyu electric railway odawara ln 613 partigiani 613 lariat 613 r du 11 novembre 1918 613 rua piaui 613 us 4 613 niftianikov 613 us 218 613 r des iris 613 kripost 613 motorcycle parking 613 culvers 613 yeongdonggosokdoro 613 mauritius 613 erasmus 613 tonys 613 sutton rd 613 bridlewood 613 coventry rd 613 bulnes 613 desirto 613 traktovaia 613 a1 m 613 wolverhampton 613 autohof 614 hawthorne av 614 administracion 614 liget 614 corne 614 erfurter st 614 brusselse stwg 614 jeep trl 614 jagerweg 614 barkley 614 joncs 614 police municipale 614 991 614 019 614 brew 614 tonkin 614 merc mun 614 espana portugal 614 ventana 614 lecture 614 avocado 614 bristol rd 614 838 614 marysville 614 almirante brown 614 alter fridhof 614 katholisches 614 nicolai 614 brianza 614 jon 614 ctr dr 614 v veneto 614 sholokhova 614 helen st 614 hauff 614 bancomer 615 millfild 615 milosa 615 bily 615 d 204 615 chubut 615 bronx 615 visiolaia ul 615 ptolemaida 615 dunmore 615 rainha 615 cth g 615 oh 615 dechetteri 615 qrte 615 lipu g 615 narodnaia ul 615 shaw rd 615 rdge dr 615 marty 615 roldan 615 felton 615 brook ln 615 postfiliale 615 wyandotte 615 lap 615 ignacio allende 615 l 49 615 dlrg 615 kampong 615 brussels 615 aldridge 615 reichs 615 polycliniqu 615 796 615 arina 615 donu 615 benigno 616 2a c 616 qust 616 pristizh 616 safi 616 massenet 616 quens dr 616 gers 616 albemarle 616 shdh 616 mercury drug 616 durazno 616 dover rd 616 pitirburga 616 kio 616 film 616 moneta 616 pato 616 eibenweg 616 antares 616 sr 26 old sr 6 616 fistivalnaia 616 ituzaingo 616 cth d 616 e 45 e 60 616 frunze st 616 vocabolo 616 wembley 616 suiza 616 1350 616 r du lac 616 b16 616 calcutta 616 pidade 616 lefebvre 616 pryor 616 aitken 616 nazareno 616 halla 616 lesny 616 saisons 616 waffle ho 617 milagrosa 617 less 617 som 617 hutten st 617 cadet 617 nabi 617 supa 617 v benedetto croce 617 adriana 617 freundschaft 617 vickers 617 jacka 617 daodabdakdam 617 864 617 jr fan tian xian 617 ul tirishkovoi 617 1051 617 fairground 617 johnstown 617 mhmdy 617 bahar 617 cth c 617 nandos 617 fahd 617 kladovishchi 617 sotsialni 617 visconti 617 adolph 617 cholla 617 vogele 617 shaws 617 guo daodabdakdam hao 617 governor john davis lodge tpk 617 pancho 617 rodo 617 tateyama 617 office depot 617 purcell 617 stein bg 618 jaroslawa 618 colo 618 sigmund 618 bade 618 ting2 618 gymnazium 618 turystyczna 618 maze 618 polly 618 steinach 618 brownsville 618 blubird ln 618 b 466 618 d 817 618 television 618 sck 618 hochfeld 618 decor 618 1 86 618 lillo 618 touhokuhonsen 618 ropa 618 c nou 618 khabrat 618 droits 618 oficinas 618 194th 618 quu 618 vuong 618 1080 618 egerton 618 ignazio 618 5th av n 619 g315 619 vytauto 619 faja 619 augusta st 619 chaussee st 619 lisandro 619 iunosti 619 kul 619 presszo 619 97a 619 1045 619 fragua 619 gajowa 619 ainmq 619 ainwalefd 619 gosokdoro 619 legal 619 fenix 619 changi 619 kwan 619 lazar 619 didabs 620 kisgrube 620 connecting 620 phillips 66 620 zeisigweg 620 martindale 620 partizanskii 620 rein 620 r0 620 tarlac 620 timor 620 frank st 620 huangyan 620 l 24 620 borromeo 620 aleflnwr 620 fire stn 620 skipper 620 aleflhjr 620 winners 620 ss131 620 jr tokaido hauptlini 620 tyne 620 avenija 620 montreuil 620 wh 620 snoman 620 ca 91 620 1066 620 bati 620 cuts 620 hulu 620 dlain 620 valadares 620 1 95 njtp 620 kro 620 st wis 620 veicular 620 slovenska posta 621 st st 621 v achille grandi 621 cabinas 621 d 976 621 hillman 621 moricz 621 oktiabrskii piriulok 621 cra 1 621 rua para 621 weeping 621 s 14th st 621 renaud 621 cafetaria 621 rudi 621 macleod 621 iloilo 621 trib 621 todos 621 bles 621 heizung 621 r de plaine 621 ninemile 621 redentor 621 ditz 621 shose 621 mullen 621 monash 621 powiatowa 621 charente 621 imp du moulin 621 rodovia raposo tavares 621 meadow st 621 employee 621 eri st 621 nascente 621 maj 621 k 63 621 romani 621 poniatowskigo 621 zacatecas 622 kreisel 622 bargain 622 5ta 622 slaughter 622 gabelsberger 622 kruse 622 autov del norte 622 deir 622 verano 622 masaryka 622 goff 622 pohjoinen 622 meadowlark ln 622 cheviot 622 jr gao shan xian 622 gales 622 shlvm 622 b54 622 rif 622 zhinskaia 622 nous 622 post rd 622 yu xiang gao su 622 regatta 622 spitalul 622 shrkht 622 horgerate 622 il 622 stowe 622 doubletree 622 lyndale 622 josefa ortiz de dominguz 622 a71 622 belcher 623 bastia 623 1085 623 d 1006 623 watling st 623 r14 623 fraunhofer 623 b72 623 mid yuba r 623 hrad 623 horka 623 specht 623 b20 623 rodovia marechal rondon 623 fursten 623 bogu 623 nama 623 tuul 623 jing shi gao su gong lu 623 rodovia anhangura 623 trevino 623 swede 623 hyacinth 623 rua belo horizonte 623 r du verger 623 st peters church 623 anfiteatro 623 zebra 623 mcdanil 623 smyrna 623 panther cr 624 crowley 624 bahru 624 opticians 624 p za guglilmo marconi 624 hkr 624 stantsionnaia ul 624 mental 624 orchards 624 bennetts 624 shhrstalefn 624 gorkogo ul 624 otel 624 ohio av 624 rsh 624 drb 624 miller cr 624 d 165 624 silverwood 624 kirchhof 624 policlinico 624 jr sanin main ln 624 tally 624 andra 624 5th st n 624 daodak 624 wittener 624 nft 624 k 62 624 jd 625 18th av 625 nativite 625 yu in neng cu luan huang yuki ze jungbunaryuk exp 625 castlewood 625 landi 625 casilina 625 v s michele 625 thermal 625 vysilki 625 c 50 625 40a 625 lurah 625 marins 625 ln st 625 faber 625 salem rd 625 holiday inn express 625 equstre 625 glenwood av 625 archimede 625 r st joseph 625 ah72 625 woodward av 625 kheurbet 625 b 102 625 prade 625 n62 625 liberty rd 625 stacey 625 donegal 625 fabrichnaia ul 625 s81 625 hivatal 625 stuben 626 pickerel 626 shortcut 626 sister 626 niko 626 w gr av 626 incline 626 blumenhaus 626 jr shan yang xian 626 alexandra rd 626 c 63 626 pinede 626 cypress dr 626 bokasuiso 626 riks 626 shoshone 626 d 765 626 klosterhof 626 negre 626 claires 626 frailes 626 891 626 hampton rd 626 joris 626 2deg 626 025 626 sportanlage 626 aar 626 xiao fang shui xiang 626 primer 626 cistermas 626 rosental 626 nika 626 yamuna exp 626 stewart av 626 shkola 4 626 lt casars 626 locker 626 siracusa 626 costantino 626 sr10 626 hartland 627 ch des ecolirs 627 290th st 627 espanol 627 lours 627 dowling 627 kifer 627 timberlane 627 lc 627 c 46 627 sp77 627 50 av 627 hansa st 627 engenho 627 abyad 627 sutera 627 rua 14 627 staten 627 aleflmdynte 627 1027 627 textile 627 jacksons 627 burleigh 627 lander 627 av 3 627 fok 627 michurina vulitsia 627 tsintralnii 627 wnibowzicia 627 autobus 627 fritz reuter st 627 pingo 627 frwshgalefh 627 hod 627 musical 627 ferran 627 bascule 627 ze9 628 karla marksa vulitsia 628 mill branch 628 houay 628 paderborn 628 sahib 628 besancon 628 briquteri 628 esterna 628 cypress av 628 suzhou 628 ogilvi 628 ul isinina 628 us 6 sr 11 628 tai tian chuan 628 bamberger st 628 middlefild 628 bonilla 628 a pk 628 fauna 628 vermelho 628 w bell rd 628 pickle 628 pekseg 628 hounds 628 lightning 628 herons 628 barrir islands 628 mers 628 pine r 628 2220 628 212th 628 crestline 629 hoteru 629 huato 629 cajero 629 hotan 629 quincailleri 629 aiken 629 2045 629 jill 629 konsultatsiia 629 hanley 629 rees 629 dutchman 629 a66 629 d 934 629 tsentralnaya 629 petersen 629 creighton 629 arca 629 glacire 629 edwards rd 629 oberhausen 629 richfild 629 n 75 629 lindner 629 l 3004 629 philosophenweg 629 tabacchi 629 redwing 629 calcio 629 ferrire 629 gr union canal 629 daccuil 629 959 629 us 212 629 budai 629 button 629 bocskai 629 prairi cr 629 2281 629 brighton rd 629 seriola 629 10b 629 antuna 629 rijlat 629 rimembranze 629 897 629 provencale 629 magnolia ln 629 balagur 629 viliki 629 d 144 629 klosterweg 629 aleftstralefd 630 belediyesi 630 dgf 630 natures 630 adao 630 oriula 630 sary 630 ze10 630 boukasuisou 630 qr 630 cassiano 630 nicolaas 630 libra 630 jawa 630 french cr 630 magellan 630 richard st 630 sami 630 3ra 630 burch 630 moos st 630 eleonora 630 parry 630 parral 630 brookshire 630 fajardo 630 pyramide 630 materials 630 kemal 630 gbuz 630 copernico 630 kulturni 630 nito 630 bundesanstalt 630 tools 630 automobiles 630 steinfeld 630 egge 630 routes 631 records 631 drago 631 ghat 631 ona 631 pushkin 631 alma st 631 alvaro obregon 631 ludalg 631 klary 631 soap 631 clover ln 631 s309 631 mcclellan 631 b19 631 juventud 631 ike 631 sturgeon r 631 elmo 631 r du temple 631 herve 631 w ctr st 631 oak gr church 631 findlay 631 fourth st 631 e 763 631 bunaventura 631 denham 631 galle 631 los sauces 631 fasanen 631 willow ct 632 castleton 632 huhang 632 cra 23 632 wedgewood dr 632 cypress cr 632 vassar 632 maroc 632 siridnia 632 thomas mann st 632 deuxime 632 e s st 632 b 41 632 kuro 632 100k 632 newbery 632 monitka 632 olivares 632 ch du cimetire 632 e 441 632 sangre 632 sabkhat 632 g12 632 pate 632 nottingham rd 632 rin 632 nantucket 632 1089 632 ambleside 632 1st av s 632 vallarta 632 b 57 632 fl 60 632 dao24 632 n 16 632 wheaton 632 coire 632 cais 632 tiny 632 bushland 632 estudios 632 edessa 632 silcher st 633 luniversite 633 fontanella 633 kozatska 633 bmo 633 s roqu 633 assuncao 633 rp14 633 prilaz 633 balmaceda 633 kapines 633 2 av 633 platforma 633 rispublikanskaia 633 yarrow 633 surface 633 fridrich engels st 633 r des chataignirs 633 ashworth 633 rabi 633 schuylkill 633 s35 633 kliuchivaia 633 gaudi 633 redding 633 hiribidea 633 ilot 633 victory bd 633 tranchee 633 wilshire bd 633 r hector berlioz 633 padi 633 cruce longitudinal 633 courcelles 633 budy 633 wharton 633 enge 633 bdr 633 metrobank 633 chataignerai 633 laurin 633 l 1 633 bk cr 633 bispo 633 alefnqlalefb 633 pavese 634 av de las americas 634 emporia 634 mechelse 634 dimitrija 634 phuoc 634 westheimer 634 planches 634 picon 634 sagar 634 fern st 634 n36 634 sailing 634 kirchner 634 squash 634 jeffery 634 15 33 634 ca 72 634 us 280 634 ive 634 r de resistance 634 r pirre curi 634 d 212 634 morte 634 prestes 634 spirito 634 homero 634 hmdalefn 634 perkebunan 634 arcades 634 vernon st 634 addis 634 cra 21 634 brandweer 635 stele 635 elmira 635 khar 635 ferrovia firenze roma 635 896 635 ze12 635 argyle st 635 n 19th st 635 vauxhall 635 neuhauser 635 contreras 635 1132 635 montesquiu 635 lozano 635 189th 635 cukraszda 635 ua 635 casares 635 bellamy 635 baltazar 635 viterbo 635 cristobal colon 635 aldrich 635 vestergade 635 bradbury 635 a 12 635 golfviw 635 glendale av 635 viso 635 maricopa 635 rua dom pedro 2 635 welser 635 sade 635 forskolan 635 curil 635 kardinal 636 s andres 636 pamatnik 636 vladimirskaia 636 bestattungen 636 apostolo 636 c calvario 636 freiwillige feurwer 636 henson 636 ellis st 636 emili 636 pendidikan 636 jin ji ri ben t dao da ban xian 636 nordbahn 636 moray 636 hortensia 636 nadi 636 elm cr 636 saturno 636 zapravka 636 motocross 636 beryl 636 gibbons 636 340th 636 co rd 22 636 industrialni 636 crofton 636 toys r us 636 turning 636 jingzhang exp 636 parken 636 chillicothe 636 term 636 pozharni 636 ofiar 636 l 16 636 v vittorio emanule 636 hindu 636 butlers 636 investments 636 hedevej 637 xiuang exp 637 rp4 637 rua nove 637 50n 637 dongha exp 637 tilsiter 637 hammer st 637 202nd 637 877 637 beauchamp 637 hrte 637 pirival 637 hema 637 verici 637 ep13 637 aleflbw 637 lcr 637 farah 637 soft 637 m 12 637 danemark 637 autov de las rias bajas 637 sp76 637 alkali 637 yu kun gao su 637 d 908 637 d 161 637 judge 637 wezel 637 dott 637 4c 637 amethyst 637 kw 638 sotelo 638 mansilla 638 shaftesbury 638 barbecu 638 gerry 638 secretariat 638 mlynowka 638 habil 638 dingle 638 s9 638 murto 638 visna 638 schonau 638 debra 638 carousel 638 kepler st 638 carpe 638 werkstatten 638 chapultepec 638 tecnologia 638 gabrila mistral 638 pochtovi 638 alo 638 soniachna vulitsia 638 s 3 638 hoyas 638 chevy 638 koblenzer st 638 touraine 638 thermes 638 orthopadi 638 eagle cr 638 kilato 638 fong 638 yaakov 638 11 29 638 mada 638 megalou 638 r du priure 638 comunitaria 638 buttermilk 638 ridgefild 638 chiornoi 639 fraile 639 839 639 brq 639 uhren 639 stiles 639 gracas 639 villages 639 bernadette 639 komintirna 639 manna 639 brosses 639 ono 639 270th st 639 manin 639 milli 639 e 65 e 80 639 casco 639 commerce dr 639 dn 639 isar 639 kioski 639 46a 639 sail 639 konstantego 639 jyvaskylan 639 herriot 640 e pine st 640 costa coffee 640 lloyds pharmacy 640 webber 640 wolf tabakwaren 640 lahden vla 640 narodov 640 rozcesti 640 makmur 640 v fabio filzi 640 ecologico 640 kawasaki 640 acuna 640 baga 640 teles 640 crawford st 640 greenacres 640 haselbach 640 profesional 640 shaikh 640 sp67 640 c s n 640 switch 640 montale 640 hai nan huan xian gao su hainan ring exp 640 rua mato grosso 640 centras 640 chaussures 640 valenzula 640 lesniczowka 640 zibarat 640 galt 640 calixto 640 delfin 640 brunn 640 zia 640 quoc 640 duchess 640 olof 640 jbr 640 sellers 640 mishash 640 v liberta 640 culebra 640 afrika 640 zilini 641 moskovska 641 viveres 641 laurens 641 cort 641 territoriale 641 14b 641 ep14 641 rustavili 641 ozero 641 longa 641 uj 641 limekiln 641 zhrte 641 purpose 641 zolnirzy 641 milton st 641 komercni 641 v regina margherita 641 pluto 641 coulter 641 e 233 641 condado 641 da ze chuan 641 pertanian 641 gleason 641 1 80 1 90 641 luonnonsuojelu 641 g78 641 palm dr 641 kentro 641 clarence st 641 r des cedres 641 falcao 641 tbryz 641 grazhdanskaia 641 birch cr 641 pinang 641 farrington 641 or 99e 641 madrona 641 corregidora 642 alkotmany 642 petes 642 charrire 642 daves 642 k 47 642 alarcon 642 kavkazskii 642 a 22 642 eucalipto 642 sta clara 642 batlle 642 trapeza 642 pui 642 rzeczna 642 thompsons 642 k 60 642 jeziorna 642 polskich 642 dimitri 642 ny 25a 642 shliakh 642 immigration 642 mater 642 ayam 642 evanston 642 escobedo 642 oasi 642 fgc 643 dala 643 georgia av 643 dao3 643 reed st 643 turnbull 643 oneill 643 cavallotti 643 fall cr 643 oreilly auto parts 643 waterbury 643 lijster 643 congreso 643 liberia 643 delmas 643 bellwood 643 spartak 643 pantry 643 d 211 643 maxim 643 hallera 643 sunbury 643 mt olive church 643 frankrijk 643 d 301 643 us 501 643 rocky rd 643 d 905 643 lynn rd 643 wlyainsr 643 chicago ln 643 gur 643 ardennes 643 tirarztpraxis 643 tchibo 643 leeward 643 warren av 643 ngan 644 187th 644 aptichni 644 espiritu 644 batan 644 rendelo 644 1 680 644 herbert st 644 mannheimer st 644 negras 644 chorro 644 alvarenga 644 thal 644 n 18th st 644 201st 644 ponderosa dr 644 863 644 sanriku 644 ss26 644 atletico 644 r thames 644 ilina 644 us 122 644 crosstown 645 cooley 645 sycamore rd 645 027 645 zigel st 645 canela 645 patterson rd 645 kristen 645 pittman 645 carme 645 junipero 645 stanitsa 645 1014 645 260th st 645 slatina 645 v sardegna 645 skolevej 645 thornwood 645 gorno 645 emscherschnellweg 645 sunkus sankusu 645 nyuop 645 gila 645 purple heart memorial hwy 645 1 4 645 westwind 645 atu 645 haintsmalefvt 646 t9 646 darb 646 tstp 646 lvy 646 robledo 646 nalogovaia 646 sun yat sen fwy 646 colonna 646 irik 646 kenvtukiuraidotikin 646 premir inn 646 ascension 646 calf 646 io 646 nelli 646 germantown 646 goulburn 646 inchuan 646 norberto 646 shoal cr 646 obergasse 646 gitmana 646 b 313 646 settentrionale 646 basel 646 ava 646 rena 646 foster st 646 salines 646 bogdana khmilnitskogo ul 646 sivas 646 rasm 646 buffalo r 646 lawrence rd 646 assumption 646 w s st 646 lomba 646 saud 646 r bellevu 647 kade 647 pis 647 duffy 647 heidi 647 792 647 buca 647 alwan 647 autoservicio 647 burwood 647 menard 647 n80 647 v giovanni falcone 647 suttner 647 geraumt 647 flagler 647 pulaskigo 647 r claude debussy 647 5c 647 calypso 647 r ln 647 lizard 647 charlotte st 647 d 146 647 zhong chuan 647 ink 647 southern tir exp 647 disabled 647 k 48 647 ashbourne 648 bride 648 mayfild rd 648 lucero 648 lat 648 zhong shan dao 648 aleflainyn 648 lvivska 648 rosengasse 648 nas 648 70a 648 vitorino 648 bodenseeautobahn 648 kraal 648 yak 648 stallion 648 p za giuseppe garibaldi 648 a487 648 delaney 648 scottish 648 orzeszkowej 648 matije 648 suncrest 648 kenosha 648 dovbusha 648 buford 648 zajazd 648 salut 648 bertram 648 alemannen st 648 hour 648 fourmile cr 648 r de lorraine 648 residenza 648 cesario 648 ss223 648 kolcsey 648 prosta 648 gypsy 648 altes rathaus 648 church hill 649 yar 649 porin t 649 sandhurst 649 sahl 649 d 151 649 aliia 649 aleflqdymte 649 academy st 649 16 34 649 gps 649 greenfild rd 649 alaska hwy 649 191st 649 essener 649 reger 649 perum 649 tianno 649 salefhb 649 fyalefd 649 bailly 649 rodnikovaia ul 649 1103 649 fordham 649 victoria dr 649 pohon 649 volker 649 under construction 649 vg 649 papandreou 649 e 851 649 kalamas 649 gading 649 marcondes 649 canchas 649 acadia 649 baltiiskii 649 seagull 650 hameenlinnan vla 650 lirr main ln 650 stadtmobil 650 ss33 650 rooster 650 d 973 650 masarykova 650 tikhnichiskii 650 tamara 650 st einheit 650 panjang 650 zviazku 650 lavage 650 sigen 650 a33 650 neumuhle 650 cinqu 650 ep8 650 k 49 650 lanchonete 650 kiuri 650 fairfild rd 650 rainy 650 spoorweg 650 kuta 650 n 11 650 littleton 650 dourimpig 651 v adige 651 v don minzoni 651 valente 651 aleflqalefhrte 651 facilitis 651 heilbronner 651 ester 651 l 60 651 kampe 651 mulligan 651 1101 651 n 86 651 genaro 651 wanne 651 socar 651 hyang 651 byvshaia 651 cambridge dr 651 agha 651 bruna 651 ss67 651 bouchet 651 qdrt 651 knott 651 dn2 651 binario 651 meister 651 uusi 651 stycznia 651 villaverde 651 fifth av 651 papeteri 651 skalka 651 anza 651 tioga 651 abarrotes 651 alston 651 escorial 652 2c 652 portail 652 laki 652 polisi 652 aguilera 652 dily 652 garita 652 mantova 652 taikos 652 bolognesi 652 dao19 652 confederation 652 spoon 652 viry 652 sichovikh striltsiv vulitsia 652 fuoco 652 erickson 652 toi 652 zavodska vulitsia 652 rennweg 652 kmart 652 begonias 652 r15 652 davison 652 wheatland 652 zizkova 652 morelia 652 bmt 652 43a 652 ban shen dian qi t dao ben xian 652 milosha 652 bragg 652 w 16th st 652 tropicana 652 orkhon 652 wildcat cr 652 skilift 652 alpen st 653 hopper 653 n60 653 miszka 653 mouse 653 makedonski 653 pullman 653 b 275 653 v s marco 653 simo 653 cowley 653 spring cr rd 653 sereno 653 br 277 653 pa 611 653 vicks 653 rua castro alves 653 khbrte 653 donovan 653 wold 653 griya 653 isabelle 653 opolska 653 gamestop 653 kreis st 653 boon 653 sviatoga 654 vigili 654 axe 654 naturfreunde 654 hosp st 654 montigny 654 elizy 654 hbyb 654 seguro 654 cherry cr 654 abandoned cnr rowy 654 aranda 654 co rd 23 654 staatliche 654 virchow 654 severnaya st 654 gain 654 hume hwy 654 raglan 654 romea 654 1008 654 dealer 654 halal 654 d 154 654 novaya 654 pools 654 rua barao do rio branco 654 nicklaus 654 aleflnsr 654 ul vatutina 654 rsm 654 whitby 655 42a 655 gobetti 655 d 751 655 aussenstelle 655 peyton 655 sucia 655 phi 655 eluard 655 wetan 655 ss 77 della val di chinti 655 rossbach 655 soriana 655 rempart 655 woodlake 655 cooke 655 dn15 655 njain 655 fildcrest 655 plumpe 655 barberry 655 mumbai 655 zaslonova 655 orozco 655 naturfreundeaus 655 komsomolskii prospikt 655 d 164 655 bull cr 655 modelo 655 rusty 655 casimiro 655 colliry 655 capdenac 655 monk 655 hftm 655 michal st 655 gray st 655 elizabeth av 655 goodwood 655 al des acacias 656 dong jing huan zhuang 656 rua 13 656 coliseum 656 hzm 656 bryce 656 molendijk 656 trader 656 bellflower 656 mic 656 e15 656 luther st 656 galo 656 cth m 656 co rd 14 656 rimbaud 656 destra 656 sanat 656 riverviw rd 656 appel 656 peloponnesou 656 st 6 656 spor 656 ruta nacional 40 656 berner 656 shark 656 osnabrucker st 656 sudlicher 656 ruan 656 ban he zi dong ch dao 657 gr pl 657 konstytucji 657 bao fu gao su 657 fiore 657 leopold st 657 johannes st 657 triton 657 iunost 657 aril 657 ecluse 657 tijuana 657 naciminto 657 howard rd 657 chisetta 657 sp69 657 tbilisi 657 wirt 657 tangenziale sud 657 g319 657 iguazu 657 r de lavenir 657 xu chuan 657 damascus 657 kraus 657 londonderry 657 simms 657 autostrada messina palermo 657 c 39 657 zo 658 ss63 658 ennis 658 haddon 658 hoz 658 n201 658 laboratoriia 658 fischer st 658 khr 658 b 185 658 parents 658 palmyra 658 myjnia 658 co rd 25 658 schutzenweg 658 lortzing 658 wu he ruo xia zi dong ch dao 658 aliso 658 vil 658 qabr 658 beinn 658 cr 32 658 rob 658 jr dong bei ben xian 658 widokowa 658 louche 658 spectrum 658 reus 658 coy 658 climbing 658 freirr vom stein st 658 leven 658 edgewater dr 658 tyler st 658 alde 659 bonaventure 659 rua sergipe 659 casper 659 cushing 659 v enrico toti 659 hs2 phase 2b w midlands to leeds 659 khotil 659 morris rd 659 av francois mitterrand 659 kort 659 wein bg 659 b 72 659 jr kagoshima main ln 659 pumpwerk 659 costes 659 lecce 659 kathe kollwitz st 659 prosperity 659 boswell 659 mirante 659 servico 659 bouvreuils 659 at sf railway 659 campinas 659 ss10 659 genom 659 tern 659 volna 659 curva 660 woodlawn av 660 eastex 660 meadowood 660 kinderkrippe 660 avias 660 murray rd 660 a56 660 rdwy 660 e 584 660 melati 660 v s giovanni bosco 660 eagle st 660 whole 660 enriquz 660 roberts st 660 peche 660 spartan 660 tali 660 av de paris 660 mccall 660 berken ln 660 beasley 660 rodovia ulisses guimaras 660 miditsinskoi 660 parlour 660 l 23 660 cumhuriyet 660 jones cr 660 paf 660 weissdornweg 660 universitats st 660 urbanizacao 660 jingshen exp 660 palmer rd 661 keiyo 661 johnson av 661 beaux 661 dickerson 661 wyspianskigo 661 iwaki 661 brin 661 mcallister 661 jingshen 661 ober st 661 bezirk 661 argo 661 ah32 661 884 661 gaa 661 disabili 661 b12 661 3020 661 https 661 malden 661 mitallurgov 661 susanne 661 st andrews dr 661 beng 661 ways 661 a 370 662 viborgvej 662 taipei 662 gedenkstatte 662 ul krylova 662 state rd 662 batas 662 trirer st 662 delegacion 662 nugget 662 riabinovaia ul 662 alfamart 662 betribshof 662 pasarela 662 aintyte 662 tennis ct 662 bermejo 662 l 11 662 gurtozhitok 662 silpo 662 fm 1960 662 novii 662 through 662 office de tourisme 662 1 24 662 d 155 662 redlands 662 e 402 662 razvaliny 662 scheune 662 westviw dr 662 noorder 663 jungbunaryukgosokdoro 663 pobedy 663 sch 663 alon 663 vyytsmn 663 aliksandar 663 cozy 663 vtoroi 663 sunnybrook 663 mosel st 663 heidelberger 663 ferrol 663 kirchen pl 663 cua 663 rjlte 663 wedge 663 kilkis 663 v udine 663 mahendra 663 1 35e 663 s206 663 gower 663 lasem 663 xe 663 fridberger 663 a259 663 l 1100 663 goss 663 sufla 663 pa 3 663 jakobus 663 nether 663 pl de iglesia 664 termas 664 rozsa u 664 espacio 664 heimat 664 wide 664 slovenska sporitelna 664 poseidon 664 wells rd 664 orto 664 daodabsdatdam 664 fuko 664 painter 664 hermenegildo 664 manco 664 arabia 664 lee hwy 664 blood 664 m18 664 sadovoi 664 orden 664 r maurice ravel 664 zbalefrte 664 handy 664 aleflbydalefhamza 664 o df st 664 birizovka 664 guo daodabsdatdam hao 664 us 53 664 1 270 664 bauhaus 665 haro 665 n maple st 665 kedai 665 nh65 665 kosmonavtiv 665 armour 665 technologi 665 luitpold 665 maurer 665 chang gu chuan 665 vau 665 gippsland 665 landshaftnii 665 koloniia 665 peiraios 665 gens 665 templirs 665 hilfswerk 665 holl 665 juniper dr 665 raadhuis st 665 meridinne 665 dah 665 n eastern standard gauge ln 665 berne 665 lawndale 665 maple ct 665 khalefnh 666 terul 666 rt de gare 666 2350 666 peremohy 666 core 666 vitorio 666 astana 666 demos 666 gardes 666 c 66 666 greenside 666 gertrud 666 cosmetics 666 hoff 666 ayers 666 allens 666 breitenbach 666 khlibozavod 666 cowboy trl 666 mejia 666 ota 666 ivropa 666 lesli st 666 r des mimosas 666 clarion 666 innsbrucker 666 dt 666 sr 134 666 morandi 666 wali 666 joker 666 rdge st 666 nikko 666 charlis 666 jr musashino ln 666 brompton 666 libertadores 666 travessia 666 sjo 666 carne 666 vijas 666 g1512 667 cuza 667 rosenheimer 667 barcelos 667 manley 667 manitoba 667 d 306 667 crook 667 bernal 667 ace hardware 667 mediterranean 667 ambasada 667 castellanos 667 raso 667 lulu 667 cordon 667 rebel 667 c 48 667 reinhard 667 escalante 667 casilla 667 shqalefyq 667 bruckner st 667 fairlane 667 durance 667 brandstrom 667 tsitkin 667 sassari 667 daiv 667 supermercados 667 cerna 667 boathouse 667 hummel 667 n 623 667 ward rd 667 197th 667 prolongee 667 kga 667 mataram 667 erzurum 668 minni 668 bazarnaia ul 668 teakwood 668 829 668 us 20 bus 668 vaches 668 cracker 668 goda 668 jr wu zang ye xian 668 gorman 668 aleflthalefnwyte 668 sooruzhiniia 668 jokai mor u 668 reinhold 668 johanniter 668 12e 668 1n 668 koper 668 tele 668 v chisa 668 atp 668 dolgoi 668 r albert camus 668 aleflrhmn 668 mansfild rd 668 frame 668 stoneaven 668 marinas 668 linke rheinstrecke 669 matin 669 ruy 669 ovrazhnaia 669 aloha 669 pur 669 photography 669 blustone 669 mawar 669 engvej 669 08 26 669 u df st 669 cfa 669 copperfild 669 amerika 669 shore dr 669 butt 669 sivirnoi 669 wright rd 669 joseph st 669 oven 669 harveys 669 palazzetto 669 marca 669 barres 669 toluca 669 dunav 669 sardis 669 pellegrino 669 qunha luan huang yuki ze 669 complexo 670 2004 670 lanches 670 herb 670 sever 670 verizon wireless 670 chaikinoi 670 ruko 670 swim 670 kassel 670 devil 670 damjanich 670 hyeonda 670 jubb 670 maintalautobahn 670 bergs 670 pfeiffer 670 r7 670 nibelungen 670 sore 670 scrub 670 txiki 670 r des pinsons 670 botte 670 022 670 v vittorio emanule 2 670 husky 671 drugstore 671 antonino 671 rstwralefn 671 edicola 671 reinaldo 671 l 12 671 grant rd 671 village rd 671 further 671 n 15 671 khamis 671 koban 671 mot 671 r jules verne 671 naos 671 ch vert 671 roqus 671 swimming pool 671 juko 671 sulayman 671 palefrkhyng 671 soriano 671 rt de lyon 672 ny 27 672 almendro 672 lesi 672 ptit 672 maiskii 672 iao 672 stathmos 672 k 43 672 stadtbucherei 672 kew 672 deg 672 americano 672 hrvatska posta 672 zeche 672 tamworth 672 gui chuan 672 rigg 672 raiffeisen bank 672 g106 672 barraca 672 montcalm 672 vhardizd 672 inman 672 korean war veterans memorial hwy 672 l 20 672 divisione 672 polk st 672 roder 672 sbkhte 673 rheintal 673 oum 673 b 304 673 gotz 673 falk 673 riserva 673 livski 673 m 25 673 gola 673 rear 673 aok 673 s212 673 nasser 673 s 13th st 673 n v 673 rostov 673 fuk 673 r st jacqus 673 rodoanel 673 b67 673 mindener 673 rosenthal 673 polizeiinspektion 673 kirpichnaia ul 673 nachtigallenweg 673 provost 673 tani 673 salisbury rd 673 kaufhaus 674 aconcagua 674 butler st 674 saddleback 674 blythe 674 kowloon 674 lora 674 silverio 674 sweden 674 peter st 674 guta 674 vatutina vulitsia 674 280th st 674 ritter st 674 batorego 674 2018 674 flash 674 s80 674 horna 674 florida av 674 hoop 674 speicher 674 kerk pln 674 telekom austria 674 ul 40 lit oktiabria 674 matheson 674 bouf 674 empress 674 v edmondo de amicis 675 taillis 675 espaco 675 burgstall 675 idrotts 675 ralf 675 southridge 675 lazara 675 grandviw av 675 fruteria 675 set 675 pustertaler 675 menuiseri 675 lucille 675 twain 675 barrack 675 schild 675 doubs 675 stationsvej 675 2054 675 canadian pacific railway belleville subdivision 675 sewell 675 jifang 675 vodokanal 675 kastsiol 675 rassvit 675 pfarrhof 675 co rd 24 676 boguna 676 varenne 676 gagarina st 676 informacion 676 heath rd 676 lantinen 676 roa 676 lilac ln 676 polytechnic 676 rodovia ulysses guimaras 676 tibet 676 aswad 676 pl du 8 mai 1945 676 makidonski 676 brussel 676 ramsay 676 rua quinze de novembro 676 flider st 676 soul 676 wegweiser 676 lougheed 676 industripark 676 veld st 676 old sr 6 677 richtung 677 blacksmith 677 buddhist 677 oril 677 elisabeth st 677 epa 677 superette 677 1106 677 stube 677 w 14th st 677 alefldalefyry 677 eve 677 quijote 677 sagebrush 677 pz 677 193rd 677 cra 19 677 colton 677 halt 677 1 20 1 59 677 united methodist church 677 baskin robbins 677 n church st 677 ruhrschnellweg 677 ah150 677 throat clinic 677 shogakko 677 condes 677 lincoln dr 677 nicolas bravo 677 proxi 677 fleuve 677 granda 678 fox rd 678 fabian 678 falah 678 warszawa 678 hypovereinsbank 678 banska 678 safa 678 pinta 678 morgans 678 vazov 678 vitale 678 ul kommunarov 678 ne ste 678 neugasse 678 buhler 678 caidat 678 n state st 678 red lion 678 araucarias 678 hanuman 678 kari 678 flugel 678 chutes 678 rua nova 678 montezuma 678 alefljdyd 678 villars 678 lwowska 678 ukrainskaia ul 678 airport dr 678 gephura 678 i ban guo dao112 hao 678 wendell 678 kirava 678 cider 678 zdravlja 678 maire 678 seed 678 anh 678 granit 678 davi 678 jiffy 679 st 2260 679 waldchen 679 v toscana 679 rank 679 atili 679 carpets 679 27th st 679 34a 679 lak 679 chiba 679 698 679 kilpatrick 679 rfv 679 autoroute de lest 679 jolla 679 ze11 679 ul stipana razina 679 kohlen 679 v 20 settembre 679 buzon de correos 679 richardson rd 679 panzio 679 matte 679 stroud 679 trav 679 posyandu 679 mechtat 679 giroiam 679 januario 679 spruce av 679 cavalcavia 679 lukas 679 chateaux 679 198th 679 moika 679 e 16th st 679 cukirnia 679 r molire 679 2125 679 e 533 680 imp des lilas 680 a wald 680 us 97 680 markische 680 reg 680 gebruder 680 arica 680 totalerg 680 raspberry 680 sakit 680 supreme 680 v vittorio alfiri 680 ven 680 maryland av 680 hall ln 680 weseler 680 crematorium 680 thor 680 mjyd 680 3005 680 punjab 680 austin st 680 alemannen 680 monge 680 v enrico mattei 680 iberia 680 scikow 680 964 680 bedford rd 680 antonin 680 frisco 680 nasosnaia 680 recyclinghof 680 kurhaus 680 c6 680 sfc 681 lhorloge 681 salefdraleft 681 tennis courts 681 campbells 681 iugo 681 bahnsteig 681 d 99 681 wasser st 681 obshchiobrazovatilnoi 681 psaje 3 681 koblenzer 681 korolinko 681 birchwood dr 681 palmers 681 bohm 681 niman 681 samarinda 681 wly 681 educacional 681 heating 681 depot rd 681 tirol 681 hunter rd 681 aquarius 681 psi 681 mtn cr 681 jk 681 yalefs 681 stal 681 co rd 27 681 eberhard 681 696 681 laja 681 er ding mu 682 ojos 682 henrys 682 ontonagon 682 weeks 682 koge 682 bandiri 682 white oak dr 682 r louis pasteur 682 trestle 682 uspiniia 682 rother 682 tunas 682 sp66 682 are 682 apostola 682 karting 682 attilio 682 tash 682 daji 682 dolomiti 682 zagreb 682 xiao chuan 682 brandenburger st 682 bens 682 prut 682 its 682 pischanaia ul 682 turistica 682 plikhanova 682 merles 682 bay rd 682 schulte 682 rainbow dr 682 dalefwwd 683 witolda 683 lassen 683 forestal 683 finger 683 sandoval 683 a43 683 charon 683 autoroute dolympia 683 vallo 683 mwy a5 ionian mwy 683 covey 683 dan in xin 683 energia 683 carls jr 683 controle 683 dolympia 683 laurelwood 683 mestska 683 verdagur 683 rancheria 683 csrio 683 kendrick 683 crafts 683 779 683 ange 683 liuks 683 yunus 683 qtainte 683 trigo 683 tadeu 683 goldsmith 684 grives 684 bons 684 postes 684 pokrovskaia 684 784 684 kanda 684 southwestern 684 bangalore 684 brentwood dr 684 petunia 684 3200 684 ezequil 684 aurel 684 kildare 684 av 7 684 yogurt 684 viar 684 mott 684 bursa 684 an abhainn mhor 684 autokinetodromos a5 ionia odos 684 v raffallo sanzio 684 dao58 684 lindley 684 s carlos 684 taunus st 684 065 684 chkalova vulitsia 684 aker 684 euronics 684 99a 684 c s antonio 684 sera 684 miodowa 684 gottardo 684 ul pugachiova 685 zigeleiweg 685 2003 685 r du marechal leclerc 685 vanderbilt 685 a graben 685 semmering schnell st 685 d 162 685 andersen 685 palm st 685 ayer 685 kolkhozni 685 ferrovia roma napoli 685 darul 685 ortaokulu 685 molodyozhnaya st 685 davis dr 685 rn 4 685 lochan 685 contiguous 685 howard av 685 sorlandsbanen 685 ferrovia roma napoli alta velocita 685 ary 685 first christian church 685 stockholm 685 veer 685 pogranichnaia 685 us 7 685 s100 685 gallatin 685 studinchiskaia 685 aleflsalefdte 685 zentrale 685 mong 685 dato 685 38k 685 xing lin gao su gong lu 686 donat 686 commercial rd 686 forca 686 jeffris 686 valsugana 686 smith ln 686 cardinale 686 inicial 686 girasoles 686 jagilly 686 zapad 686 pischani 686 ok 51 686 zagorodnaia 686 burgruine 686 commodore 686 appletree 686 albacete 686 meira 686 blom 686 wendover 686 ridder 686 n 20th st 686 piri 686 pages 687 2500 687 hopi 687 r 217 687 suriu 687 lugova 687 islington 687 zurcher st 687 k 59 687 r 255 687 penrose 687 kelter 687 mcconnell 687 co rd 29 687 rossignol 687 gramme 687 greeley 687 boulanger 687 antenor 687 ruta de plata 687 karlsbader 687 wood rd 687 c interna zona agricola 687 borba 687 3600 687 goleti 688 olympian mwy 688 dispensaire 688 paquis 688 railways 688 bratshi 688 marbella 688 thorbecke 688 joshin 688 bibliotheek 688 olya 688 36a 688 builders 688 jr chang qi xian 688 cycling 688 866 688 epp 688 moussa 688 mayr 688 grota 688 krai 688 s pine st 688 khn 688 hirten 688 midway rd 688 activity 688 zand st 688 muril 688 morskaia 689 gomeria 689 d 147 689 pd 689 holcomb 689 cv rd 689 sidler st 689 geranios 689 autov del sur 689 maladziozhnaia vulitsa 689 n r rd 689 e 36 689 romeral 689 latimer 689 braddock 689 sum 689 talstation 689 honeysuckle ln 689 nore 689 terraza 689 cagliari 689 trevo 689 rua getulio vargas 690 arakawa 690 a sonnenhang 690 hsk 690 huu 690 justino 690 zollhaus 690 meishin 690 harrys 690 castle rd 690 krasnoarmiiskii 690 sandford 690 solicitors 690 senter 690 meda 690 oaklawn 690 miner 690 seniorenheim 690 spray 690 rua pernambuco 690 shomaker 690 komenda 690 c f 690 athenes thessaloniqu evzoni 690 jainfr 690 riou 690 zikit 690 guterbahnhof 690 colosio 690 zaliznichna vulitsia 691 whitefish 691 mshainvl 691 kite 691 rua 15 de novembro 691 4th av n 691 unibank 691 miron 691 eid 691 leuven 691 nico 691 helle 691 a 17 691 maranatha 691 vulcan 691 jurong 691 pushkina ul 691 2035 691 marii sklodowskij curi 691 palencia 691 fanny 691 vange 691 us 250 691 1re 691 commonwealth bank 692 furst 692 chiorni 692 galvan 692 josefs 692 sibley 692 agatha 692 aleflqds 692 tees 692 nurseris 692 active 692 barnet 692 zhongzheng 692 dalefnsh 692 fild rd 692 stadtischer 692 libby 692 james rd 692 weser st 692 eo5 692 hertog 692 r charles de gaulle 692 hari 692 cr 35 692 jugendzentrum 692 d 909 692 olimpica 692 cotir 692 lean 692 stadtring 692 masse 692 flandres 692 c 40 692 moldova 692 r st exupery 692 mosley 693 gina 693 gascogne 693 mkhbz 693 vishniva vulitsia 693 sr 118 693 capella 693 sanga 693 distillery 693 1102 693 rjalefyy 693 anjos 693 burley 693 jeolla ln 693 kitchener 693 sp248 693 1044 693 mccullough 693 e8 693 sparda bank 693 wzgorze 693 n 17th st 693 reiterhof 693 d 988 693 renan 693 lackawanna 693 artima 693 recinto 694 couvert 694 guo daodabdamdab hao 694 2800 694 dellacqua 694 pridpriiatii 694 archives 694 souk 694 h1 694 artisanale 694 metcalf 694 carso 694 donja 694 zilioni piriulok 694 vernet 694 kelten st 694 guo daodac hao 694 kuk 694 leopoldina 694 v genova 694 phillips rd 694 sofi 694 mill cr rd 694 sandown 694 campbell cr 694 schutz 694 departamental 694 kampus 694 mostovaia ul 694 druzstevni 694 galefz 694 ul mikhanizatorov 694 acero 694 transmission 694 sichovikh 694 abbotts 694 r henri dunant 694 frankfort 694 peggy 694 cardinal ln 694 gaillarde 695 donner 695 clark av 695 william hill 695 roan 695 eglantirs 695 018 695 oakland av 695 rostock 695 boileau 695 nel 695 ibarska magistrala 695 bgdalefd 695 historico 695 dauvergne 695 shangri 695 novosiolov 695 kirova ul 695 fisher rd 695 v ludovico ariosto 695 carter st 695 leoni 695 det 695 stina 695 nepomuk 695 schlossgarten 695 graha 695 loomis 696 ravenswood 696 co rd 7 696 dale st 696 heinemann 696 recreo 696 marshalls 696 leap 696 bangladesh 696 sporthal 696 s rafal 696 pervenches 696 molodyozhnaya 696 pi pi xin 696 abdulla 696 brkte 696 manufacturing 696 e dr 696 230th st 696 repairs 696 al armii krajowej 696 porvoon 696 warbler 696 masons 696 franprix 696 weller 696 e 331 697 colombe 697 peguy 697 alefljnwby 697 berthold 697 us 321 697 derby rd 697 rp2 697 bagel 697 egger 697 zumbro 697 pskovskoi 697 linear 697 co operative group 697 yarra 697 celtic 697 mud lake 697 mamma 697 ji ye chuan 697 nautilus 697 wzalefrte 697 generalitat 697 bodelschwingh 697 goldenen 697 import 697 cra 20 697 2240 697 libig st 697 spigel 698 friday 698 fairviw st 698 daodac 698 rni 698 tempio 698 churrascaria 698 otto st 698 a361 698 vogels 698 sakuru kei 698 fabriqu 698 garran 698 quintero 698 smtt 698 vasut u 698 oran 698 lokalbahn 698 savon rata 698 da chuan 698 mortimer 698 sender 698 astra 698 rby 698 loa 698 kaspii 698 n 09 698 matta 698 venosta 698 bruhl st 698 bilagroprombank 698 qrh 699 nobrega 699 contournement 699 broniwskigo 699 huntley 699 hirschen 699 twelvemile 699 nepean 699 borek 699 kappa 699 xviii 699 pneus 699 fidel 699 cruise 699 douglas dr 699 empresarial 699 ruggero 699 creeks 699 d 138 699 haras 699 beckett 699 freiburger 699 freiberger 699 b 92 699 rawdat 699 capistrano 699 morskoi 700 greendale 700 occidente 700 maurits 700 cisneros 700 dmitriia 700 ings 700 mone 700 gazprom nift 700 gamble 700 egidio 700 connell 700 zdorovi 700 uralsib 700 haig 700 1 70 bus 700 peregrine 700 varsity 700 birkenallee 700 hazel st 700 kilometer 700 sentinel 700 townhouse 700 b 252 700 elevator 700 christuskirche 700 sequia 701 langudocinne 701 electronica 701 roden 701 garennes 701 evi 701 mkr 701 nyc 701 dao11 701 situ 701 orquidea 701 jenderal 701 shadows 701 autov do noro ste 701 w n st 701 geza 701 11r 701 sennar 701 ndeg1 701 rua sao francisco 701 hernando 701 cro 701 toki 701 k 57 701 coliseo 701 tokaido national rt 1 701 h st 701 neckar st 701 dera 701 milagro 701 st marys cemetery 701 bruchweg 701 esglesia 701 240th st 701 979 701 anaheim 701 autostrada do atlantico 701 ozark 702 hemlock dr 702 d 613 702 escalir 702 dubrova 702 zalesi 702 hendricks 702 meza 702 05k 702 r de lhotel de ville 702 groupe casino 702 mabel 702 s107 702 fontenay 702 kad 702 blackfoot 702 damai 702 internationale 702 v 24 maggio 702 klara 702 travers 702 kaufmann 702 braunschweiger 702 swiat 702 khalturina 702 maryse 702 sams club 703 031 703 hudud 703 greenwood av 703 gnc 703 augustus 703 ring ln 703 woodhill 703 pl linina 703 lionel 703 shenzhen 703 tirminal 703 laurence 703 anexo 703 leather 703 bakkerij 703 ivanivka 703 schlachthof 703 st 5 703 ny 25 703 cobble 703 dazeglio 703 florence av 703 jimmy johns 703 gissener 703 destro 704 arenes 704 i ding mu 704 almagro 704 traditional 704 rt de paris 704 paulina 704 cth a 704 musset 704 gruppo 704 terrazas 704 w 15th st 704 laquitaine 704 n50 704 autov del nord est 704 727 704 v enrico berlingur 704 schuhe 704 espinoza 704 st 4 704 vercors 704 ecologica 704 seicomart 704 avanti 704 schaffer 704 hu hang yong gao su 704 malinowa 704 235th 704 logan st 704 mangrove 704 meilenstein 704 castiglione 705 profsoiuznaia ul 705 sunningdale 705 etapa 705 junquira 705 lee av 705 pimenta 705 sr53 705 flurweg 705 140th st 705 200th st 705 serca 705 chilexpress 705 rosegger 705 chickadee 705 anger st 705 podgorna 705 b 36 705 kolodits 705 teleski 705 badr 705 grazer st 705 apple st 705 scheffel 705 rbs 705 meunirs 705 d 213 705 c 45 705 v don giovanni minzoni 705 fourche 705 radhus 705 sudheide gifhorn gmbh 706 daodabdamdab 706 739 706 bos st 706 bennett rd 706 sidova 706 n43 706 aleflalefbyd 706 divers 706 medard 706 li gen chuan 706 ze8 706 shou du q zhong yang lian luo zi dong ch dao 706 cigales 706 pfalz 706 r des bouleaux 706 yoli 706 weglowa 706 g98 706 treille 706 darmstadter 707 stad 707 hans bockler st 707 kiu 707 meats 707 sag 707 sorrel 707 thessaloniqu 707 ambrosio 707 hippolyte 707 amman 707 bibliotek 707 ch de fontaine 707 b 58 707 s vicente 707 help 707 minute 707 turistico 707 majid 707 b 11 707 studentenwohnheim 707 gus 707 759 707 loblaw companis limited 707 haci 707 bronte 707 sp 300 707 yayasiyaf 707 40d 707 partners 707 juliana ln 707 179th 708 kolb 708 sanatorium 708 d 922 708 ianvaria 708 southland 708 sunburst 708 sycamore av 708 stran 708 lirr 708 elmwood av 708 cilmaninnardonan 708 prov 708 developpement 708 kleinbahn 708 chemnitzer 708 edmunds 708 pkn orlen 708 lingkar 708 yvonne 708 s105 708 ferrata 708 acuducto 708 elf 708 narutowicza 708 jingno 708 paddocks 708 turquoise 708 lami 708 chalmers 708 ohm 708 meissner 708 parent 708 hir 708 carter rd 709 nvp 709 puigcerda 709 rothen 709 pitrovskii 709 pnc bank 709 1 70 us 40 709 mex 15d 709 cooper rd 709 g 5 709 ss64 709 breche 709 pikku 709 laz parking 709 p za della repubblica 709 monumental 709 rotterdam 709 944 709 c 43 709 bookshop 709 us 68 709 meijer 709 rosi 709 powstancow slaskich 709 cortland 709 v indipendenza 709 ancha 709 auvergne 709 levesqu 709 kliuchi 709 bonneville 709 pl de fontaine 709 pros 709 ctra central 709 conejos 709 nanaimo 710 flagstaff 710 aliksiivka 710 nationale bewegwijzeringsdinst 710 tula 710 bewegwijzeringsdinst 710 bm 710 strate 710 industrialnaia ul 710 kirkko t 710 driver 710 chalon 710 sibirskaia 710 pune 710 stanley rd 710 muzeul 710 urology 710 jati 710 michta 710 us 58 710 dalby 710 armee 710 botilleria 710 arbres 710 mcknight 710 rostocker st 710 e42 710 hipolito yrigoyen 710 poseidonos 710 alefldktwr 710 mullins 710 lein 710 bheag 710 botiv 710 a w 710 nemzeti dohanybolt 711 maladziozhnaia 711 hester 711 prospect rd 711 woods rd 711 dro 711 olivera 711 994 711 family mart 711 viadukt 711 jarnvags g 711 olsen 711 roswell 711 rosary 711 naberezhnaya st 711 us bank 711 ridweg 711 pattaya 711 fir st 711 hoh 711 testigos 711 lamont 711 vacas 711 tok 711 r du ruisseau 711 cerrado 711 lovell 711 bengkel 711 m10 711 saida 711 lunch 711 constance 712 ach 712 beef 712 tribune 712 timberland 712 matsuyama exp 712 shuo 712 pomorska 712 burlington northern railroad 712 kromme 712 waitrose 712 carmichal 712 marsh rd 712 atahualpa 712 crows 712 fairchild 712 media mkt 712 europa st 712 bicentennial 712 vacant 712 nat 712 e 17th st 712 kirovskii 712 mella 712 us 221 712 foster rd 712 holiday inn 712 pravdy 712 serviceweg 712 sheung 712 aoyama 712 ausiyotupu 713 zosh 713 st dei parchi 713 thuy 713 come 713 co rd 9 713 osnabruck 713 854 713 1017 713 ukrainy 713 hostinec 713 r de tuileri 713 e11 713 iochtarach 713 k 61 713 madalena 713 cerritos 713 dores 713 aino 713 s209 713 germaine 713 ulmer st 713 obala 713 e 26 713 creston 713 kristiyan 713 telep 713 whipple 713 chinook 713 rn14 713 matratzen 713 sportsman 713 koivu 713 organic 713 hmalefm 713 simply mkt 713 1 78 713 wydzial 713 avangard 713 rade 714 c 41 714 mito 714 rodnikovaia 714 vero 714 g 1 714 whitmore 714 eo30 714 cope 714 ss71 714 thun 714 gestion 714 rochette 714 b70 714 monumento ai caduti 714 1030 714 stig 714 reiter 714 eu 714 perrire 714 altamirano 714 nachalnaia 714 ouachita 714 kampen 714 miczyslawa 714 abdulaziz 714 haven st 714 pocztowy 714 mano 714 vokzalna 714 s church st 714 atenas 714 a82 714 jugos 714 araguaia 715 barns 715 angerweg 715 autostrada dei fiori 715 brunet 715 pushkinskaia ul 715 costcutter 715 yanjiang exp 715 dina 715 biz 715 nh7 715 kolos 715 baywood 715 steeg 715 balefshalef 715 seton 715 schwaben 715 ader 715 yan jiang gao su 715 todor 715 rospichat 715 charlotten 715 cr 29 715 surya 715 c de iglesia 715 devi 715 kubanskaia ul 715 virgil 715 pujol 715 villette 715 huang he 715 pirce st 715 cow cr 715 k 54 716 redeemer 716 condorcet 716 ladbrokes 716 wm 716 shaker 716 fagundes 716 southend 716 mosquito cr 716 jr yu zan xian 716 raionna 716 franki 716 958 716 plainfild 716 rede 716 medzi 716 waal 716 makan 716 selwyn 716 legends 716 gimnazija 716 yamanote ln 716 allid 716 bagels 716 masson 716 bakamni 716 orthopedics 716 vira 716 kristianskaia ul 716 1210 717 basketball ct 717 coco i fan wu 717 parcela 717 m 9 717 denner 717 silverdale 717 dobri 717 rumah warga 717 coloma 717 greenwood rd 717 arrowhead dr 717 ionian mwy 717 gulph 717 186th 717 gautir 717 schwan 717 mktbte 717 performance 717 steuben 717 collins rd 717 sotsialnoi 717 831 718 sp60 718 desire 718 shake 718 fours 718 passaur 718 huron st 718 craighead 718 mtwstte 718 colorada 718 panda express 718 connecticut tpk 718 haan 718 hameen t 718 konstantin 718 cr 31 718 fourth av 718 dreams 718 bolzano 718 sofla 718 b 256 718 parkova 718 bloomington 718 skwer 718 n oak st 719 rudolph 719 r de bretagne 719 kassa 719 kvarn 719 guide 719 danton 719 rua oito 719 ruti 719 casse 719 barton rd 719 sp58 719 graham rd 719 hql 719 breiter 719 d 559 719 florentino 719 rn4 719 ollegil 719 suncheonwanju 719 rema 1000 719 massiv 719 boyce 720 spruce ln 720 shchorsa vulitsia 720 geiger 720 yamato 720 experimental 720 condos 720 stantsionnaia 720 surau 720 saving 720 laos 720 bruch st 720 estudio 720 silcher 720 c funte 720 rosita 720 oakhill 720 keizer 720 hongha luan huang yuki ze namha exp 720 mikolaja reja 720 morra 720 rua sao sebastiao 720 mokykla 720 karaoke 721 ul gastillo 721 henderson rd 721 sucker cr 721 water ln 721 taylor cr 721 ul karla libknikhta 721 lear 721 ups st 721 satellite 721 khwh 721 r dalsace 721 ong 721 panteon 721 feinkost 721 rdge av 721 final 721 gerhardt 721 bayer 721 rua ceara 721 locks 721 sanin 721 r georges brassens 721 lancashire 721 severnaya 721 gleneagles 721 golu 721 offentliche 721 13 31 721 dutton 721 ultimos 721 lb 721 wiggins 722 lisnoi piriulok 722 cra 17 722 leibniz 722 lisnichistvo 722 spyglass 722 luxury 722 sanliurfa 722 szpitalna 722 magpi 722 debbi 722 e 78 722 nuno 722 833 722 standish 722 sit 722 hollister 722 s24 722 av e 722 sendai 722 stanislawa moniuszki 722 defense 722 kitty 723 carling 723 trinite 723 180th st 723 tsvitochni 723 autostrada azzurra 723 briarwood dr 723 878 723 rembrandt st 723 vicario 723 furtado 723 a see 723 6015 723 us 400 723 oman 723 fisheris 723 s oak st 723 beton 723 1 dorfe 723 hunter st 723 luck 723 kidd 723 ins 723 mattress 723 chacra 723 wende 723 872 723 gateouse 723 alefswalefq 723 kapel st 724 curda 724 campoamor 724 weng 724 muhsin 724 medicale 724 r 1 724 pompeu 724 chestnut dr 724 paqurettes 724 s83 724 saga 724 kopf 724 brushy cr 724 brooklands 724 eglantines 724 gunn 724 naselje 724 lovers ln 724 wessex 724 http globus tut by 724 saghir 724 al saints 724 ning 724 n 23rd st 724 hanaur 724 chama 724 amity 724 ul lva tolstogo 724 torgovaia 724 manzanares 724 ambinte 724 onofre 724 e 86 725 wedding 725 romeu 725 carmona 725 copacabana 725 cuarta 725 silber 725 medan 725 cerkiw 725 trikuharria 725 e 74 725 bear cr rd 725 lacul 725 halefrte 725 piz 725 alford 725 woodpecker 725 jalisco 725 e 411 726 daang maharlika 726 3003 726 spoldzilcza 726 loblaw 726 muhlenteich 726 winnipeg 726 fabrichnaia 726 galefbte 726 des moines r 726 n71 726 makro 726 connor 726 grasmere 726 templeton 726 28th st 726 doumer 726 las palmas 726 barbershop 727 sviatykh 727 hrbitov 727 keur 727 a kirch bg 727 divina 727 villafranca 727 kasseler st 727 santagostino 727 toto 727 bonner st 727 acker st 727 oldenburger st 727 oxfam 727 warwick rd 727 sauvage 727 6100 727 abbots 727 961 727 lexington av 727 muzykalnaia shkola 727 1 405 727 maplewood dr 727 watson rd 727 splash 727 durian 727 byvej 727 aleflshmalefly 727 us 219 727 valley bd 727 olegario 727 38a 727 taw 727 fruhling st 727 rp6 727 moacir 728 sovitskii piriulok 728 janssen 728 n shore dr 728 spencer rd 728 ss14 728 wilfrid 728 old us 66 728 gron st 728 halden st 728 umum 728 aleflgrbyte 728 r de labbaye 728 castellon 728 1040 728 sp430 728 210th st 728 10 28 728 v sta maria 728 raceway 728 lajeado 728 emmaus 728 canadian tire 728 cra 18 728 leli 728 1939 728 los alamos 728 judson 728 vermilion 728 concrete 728 vr bank 728 cllja 729 naturdenkmal 729 siatista 729 bund 729 galan 729 celia 729 b11 729 harro 729 longford 729 thicket 729 flandre 729 schlesir st 729 sportovni 729 perche 729 melrose av 729 k 51 729 bullard 729 pastelaria 729 haveforeningen 729 uachtarach 729 mcgill 729 xiao tian ji dian t xiao tian yuan xian 729 char 729 voikova 729 130th st 729 fonds 729 chippewa national forest 729 us 136 730 ul entuziastov 730 kinley 730 saintes 730 teollisuus 730 787 730 biriznia 730 poznan 730 plante 730 lubecker 730 courbet 730 konsum 730 murphy rd 730 sambre 730 industril 730 suro 730 pelletir 730 b 34 730 jiuhag 730 susi 730 qiong 730 salaria 730 biriozovka 730 wetland 730 marches 730 daodal 730 jr bei lu xian 730 internat 730 sp50 730 bears 730 leuschner 730 delguur 730 chad 730 condo 730 dalniaia 730 bittersweet 730 fara 730 manantial 730 cerf 730 pavia 731 carrizal 731 sette 731 triana 731 canadas 731 v don luigi sturzo 731 abn amro 731 avtostoianka 731 shiv 731 sylvester 731 represa 731 brug st 731 bivacco 731 halef 731 1nee 731 clam 731 sulz 731 commercio 731 imperial hwy 731 parcul 731 pescador 731 botanic 731 steep 731 l 551 731 bergmann 731 sumter 731 turners 731 pipers 731 lahden t 731 flider 731 oswego 731 ruin 731 escutia 731 rosa luxem bg st 731 church av 732 br 376 732 havel 732 vitus 732 m58 732 picos 732 st pauls church 732 s45 732 piccola 732 jame 732 bec 732 grizzly cr 732 ulya 732 wooden 732 maurizio 732 ul dikabristov 732 unisex 732 pyeong qi rou jing luan huang yuki ze 732 woodcock 732 qlbalefn 732 2011 732 kopernikus 732 2007 732 692 732 bruce st 732 kosong 732 rattlesnake cr 732 mirabeau 733 188th 733 1 40 bus 733 tennis pl 733 hurtos 733 poiana 733 jct 733 crowsnest hwy 733 mendel 733 pase 733 fgup 733 freshwater 733 lav 733 epping 733 roserai 733 chantilly 733 paisley 733 wohnheim 733 magno 733 36k 733 gyogyszertar 733 hwy 3 733 rowley 733 ivana franko ul 733 cadore 733 r de montagne 733 cook rd 733 qalefsm 734 triumph 734 c 38 734 r de tour 734 walnut dr 734 guardian 734 schulen 734 leg 734 meta 734 av victor hugo 734 wordsworth 734 donut 734 kyrkan 734 l3 734 costituzione 734 hagener 734 spinnaker 734 zvizda 734 huqingping 734 rua espirito santo 734 ahmar 734 shallow 734 oal 734 tucson 734 laco 734 pirvi 734 mfts 734 12 30 734 sainwd 735 interiors 735 kilian 735 atencion 735 krzywa 735 scotia 735 santo domingo 735 westpark 735 deco 735 grassi 735 uz 735 sobre 735 groote 735 faculdade 735 978 735 gouveia 735 887 735 b 105 735 evang 735 arquitecto 735 lazzaro 735 valley st 735 boudewijn 735 d 152 735 pesa 735 paralela 735 heladeria 735 haya 735 ep7 735 cadena 735 fulda 735 co rd 21 735 stupiniv 735 aq 735 komitit 736 nh19 736 wilhelms 736 kalkofen 736 tenmile cr 736 rua principal 736 ditiacha 736 mercurio 736 tweede 736 pickett 736 sp55 736 wirtshaus 736 mirnaia 736 shhdalef 736 mariano moreno 736 bern st 736 donau st 736 bourassa 736 khngng 736 offenbach 736 moffat 736 plummer 736 d 153 736 dorp st 736 holler 736 mulini 736 assisi 736 huff 736 oconnor 736 floridas 736 r12 737 lighting 737 monro av 737 harts 737 cohen 737 renmin 737 jino 737 tirritoriia 737 raton 737 ruch 737 hosp rd 737 bohdana 737 whiteead 737 eleutheriou 737 gandara 737 matar 737 yamuna 737 franziskus 737 coates 737 r george sand 737 otichistvinnoi 737 mission bd 737 aleflsgyr 737 ruta nacional 12 737 rhon 737 places 737 hazm 737 villegas 737 jar 738 fichte 738 pavilhao 738 swift cr 738 ze7 738 matadero 738 schwanen 738 wisental 738 t8 738 746 738 yu zhan luan huang yuki ze jungang exp 738 edwards st 738 s dr 738 h01 738 praje 738 unite 738 joyeria 738 else 738 amis 738 beyond 738 1012 738 co rd 13 738 hannoversche st 738 thir 738 baoli exp 739 saha 739 hid 739 fira 739 lilin st 739 hassel 739 paul st 739 pep 739 sedan 739 cantons 739 junganggosokdoro 739 krivoi 739 toy 739 irigoyen 739 bao lin gao su 739 cipreses 739 stadtbibliothek 739 esk 739 fireouse 739 jesucristo 739 1 90 bus 739 pinhal 739 vitirinarnaia 739 sanderson 739 kapitana 739 informatiqu 739 e elm st 739 jazz 739 hoche 740 emu 740 morska 740 br 040 740 salvation army 740 anna st 740 ayrton 740 196th 740 minho 740 annunziata 740 cherbourg 740 whale 740 musik 740 simco 740 figura 740 shine 740 emporium 740 scintist 740 goodrich 740 wallace st 740 loves 740 w 13th st 740 vineland 740 rotatoria 740 casita 740 ochistni 740 ocean av 740 boyle 740 regenbogen 740 vaquria 740 akron 740 huntingdon 740 pinewood dr 740 tolosa 741 mani 741 rua 11 741 l 191 741 podstantsiia 741 harlan 741 camelback 741 shiva 741 woodside dr 741 autostrada do norte 741 blyth 741 tuck 741 skin 741 pirikriostok 741 apgyocharo 741 minden 741 oto 741 roslyn 741 ilicha 741 christchurch 741 basil 741 stationery 741 baro 741 tikhnologii 741 once 741 moctezuma 741 gvardiiskaia 741 empalme 741 khalifa 741 k 58 741 fo ut 741 v camillo benso conte di cavour 741 kind 741 kopec 741 landratsamt 741 farina 741 cyprus 741 fregusias 741 antartida 741 tyson 742 uab 742 ngoc 742 regia 742 120th st 742 dimithardr 742 manitou 742 org 742 potsdam 742 n iia 742 hu zhu gao su 742 1205 742 kure 742 upravlinnia 742 clarkson 742 voi romaine 742 r8 742 kemper 742 nasr 742 1070 742 caracol 742 waterloo rd 742 mastir 742 svenska 742 pago 742 suck 742 before 742 e 574 742 alte schule 742 gee 743 co rd 19 743 sarre 743 996 743 25b 743 bacchus 743 taka 743 unter df st 743 murdock 743 14 32 743 gaj 743 184th 743 hager 743 karasu 743 413 01 743 b 37 743 yee 743 sp72 743 bkr 743 perpignan 743 av 2 743 1905 743 ambulatorio 743 co rd 3 743 deak ferenc u 743 zio 743 robson 743 humbug 743 bataille 743 delegacia 743 peixe 743 trir 744 mananrakanentarin 744 r105 744 lermitage 744 paganini 744 castors 744 mtn vw rd 744 n fwy 744 mex 40 744 cwm 744 ch de leglise 744 autostrada wolnosci 744 maternity 744 slaski 744 svitli 744 hawaii 744 newbridge 744 waldo 744 jisr 744 tongsenv 744 3rd st n 744 trt 744 us 72 744 eo8 744 beat 744 n pine st 744 3080 744 lippe 745 khdr 745 needham 745 tsrkva 745 wicklow 745 alum 745 1750 745 250th st 745 sixmile 745 snc 745 r 297 745 teresita 745 24h 745 allard 745 rimont obuvi 745 kridit 745 v pimonte 745 dong hai dao guo dao1 hao 745 mastirskaia 745 e 21 745 nalefdy 745 riabinovaia 745 aic 745 celler 746 pile 746 dimitar 746 sorolla 746 lair 746 balefr 746 jake 746 lacroix 746 huning exp 746 hofweg 746 lens 746 spencer st 746 mex 180 746 3805 746 sluis 746 khlyfte 746 pasco 746 frisch 746 xin chuan 746 capitello 746 maverick 746 halton 746 veterinario 746 coney 746 douglas av 746 observation 746 hohenzollern 746 alimentari 747 poteri 747 kozpont 747 aachener st 747 brama 747 greentree 747 rl 747 granados 747 catolicos 747 aldi nord 747 xang 747 uxbridge 747 pulo 747 beechwood dr 747 liga 747 lausanne 747 militare 747 razhardizd 747 customer 747 muno 747 178th 748 r pirre mari curi 748 r du bourg 748 amand 748 balmes 748 devesa 748 tabla 748 rtd 748 v nino bixio 748 neustadter st 748 faulkner 748 bambu 748 rn 1 tw 748 maidstone 748 nanlu 748 malefhamza 748 shevchenko 748 istok 748 liniinaia 748 azucena 748 moor ln 748 fluvial 748 sr 60 749 poitou 749 squires 749 dm drogeri mkt 749 b 229 749 pumpe 749 adele 749 etc 749 stewart rd 749 marsa 749 wolverine 749 marlow 749 frukty 749 fairlawn 749 yongb 749 sonnenschein 749 yama 749 kohls 749 hemingway 749 miriam 749 propertis 749 dunas 750 wiltshire 750 brok st 750 magnet 750 fhd 750 saivriya 750 sp52 750 wilgen 750 rua flores 750 suir 750 brp 750 broomfild 750 lujan 750 kniazia 750 rid st 750 belmont av 750 romashka 750 conley 750 b 175 750 radford 750 skoroi 750 carrir 750 famiglia 750 marlboro 751 tikhii 751 winn 751 matsuyama expwy 751 tanger 751 mlalef 751 crabapple 751 stacy 751 duluth 751 willa 751 barid al maghrib 751 lombo 751 gogolia vulitsia 752 ridgeviw dr 752 wiosenna 752 tevere 752 d6 752 er yan gao su 752 13th av 752 anta 752 hodge 752 mexicana 752 knowles 752 sp57 752 vasilivka 752 o st 752 leonor 752 s214 752 seligman subdivision 752 moreton 752 ca 2 752 langeweg 752 standing 752 cempaka 752 leuvense 752 d 157 752 malefyr 752 738 752 trirer 752 nob 752 thousand 752 starej 752 david st 752 christy 752 giang 752 hulva 752 gruta 752 huntington dr 752 thurston 752 diussh 752 cr 33 752 sawit 753 frunzi vulitsia 753 rivoliutsionnaia ul 753 corsica 753 josefina 753 frias 753 merah 753 fornaci 753 rotdornweg 753 holstein 753 deborah 753 gbaint 753 historischer 753 mezarlik 753 criqu 753 waldhof 753 gaines 753 armory 753 lepanto 753 v grazia deledda 753 andersa 753 tarasa shivchinko ul 753 arlington av 753 popeyes 753 co rd 12 753 v luigi einaudi 754 n 525 754 newport rd 754 shwe 754 sh 7 754 sovitskoi 754 4th st n 754 r 2 754 blackbird 754 schreibwaren 754 mitteldeutsche schleife 754 vicent 754 bailey rd 754 dw 754 goldfinch 754 ellesmere 754 cra 2 754 clipper 754 myra 754 jeolla 754 r des fauvettes 754 brenta 754 al wojska polskigo 755 oberschule 755 cattaneo 755 tets 755 beresford 755 doral 755 akita 755 bustamante 755 jozefa pilsudskigo 755 comida 755 korona 755 kleber 755 creekviw 755 gardenias 755 candida 755 byst 755 e 13th st 755 tas 755 3802 755 iksanpohang exp 755 valdes 755 policial 755 seguridad 755 wil 755 airpark 755 casona 755 franklin rd 755 jing gang ao gao su gong lu 755 bron 755 shingle 755 valeria 756 lacey 756 s302 756 gana 756 osnabrucker 756 s18 756 sci 756 escalada 756 albert rd 756 netto marken discount 756 dogs 756 858 756 rimini 756 richmond st 756 971 756 cannes 756 v della resistenza 756 soho 756 d 1083 756 woodburn 756 meunir 756 agricolas 757 norge 757 xiuang 757 indiana av 757 ditskaia poliklinika 757 pombal 757 gable 757 aleflainly 757 classe 757 tbc 757 cedar ct 757 eger 757 lars 757 progres 757 milltown 757 morris st 757 wilson cr 757 ul 50 lit oktiabria 757 heerweg 757 desoto 758 laird 758 plainviw 758 riverside rd 758 598 758 father 758 great n rd 758 r liffey 758 stezka 758 bayan 758 m37 758 halefshm 758 bochum 758 thuong 758 parkvej 758 moosbach 758 milligan 758 cra 15 758 lunion 758 matejki 758 rn8 758 diva 758 postamt 758 shchil 758 ultramar 758 ul titova 758 rech 758 pergola 758 floriana 758 itea 759 audrey 759 szecheni u 759 tiburtina 759 dorfteich 759 stanislawa staszica 759 cr 26 759 brahms st 759 schmuck 759 mercantil 759 merisirs 759 s204 759 v s giovanni 759 shlmh 759 848 759 st marys 759 highbury 759 ante 759 adolfo lopez mateos 759 174th 759 peterborough 759 veiculos 759 schwabischer albverein 759 estevao 759 ringwood 759 weiden st 760 akademika 760 busstation 760 boucle 760 jesionowa 760 parquadero 760 e 901 760 dangjin 760 welland 760 eer 760 g309 760 praha 760 ninos heros 760 brive 760 co rd 20 760 rua rio de janeiro 760 waterworks 760 tout 760 quiros 760 mammoth 760 ruska 760 nacos 760 e 15th st 760 alefmyr 761 evergreen ln 761 blume 761 accessoris 761 haur 761 encinas 761 jalefbr 761 new hope church 761 v fratelli bandira 761 tirso 761 hardees 761 co rd 4 761 mshalefsh 761 sulpice 761 magon 761 9710 761 laundromat 761 chelmsford 761 chatel 761 glenwood dr 761 comstock 761 s82 761 bode 761 sp56 761 hsin 761 couturir 761 arbutus 761 alta velocita 761 colmar 761 r du canal 762 florestal 762 895 762 katharinen 762 maua 762 brugge 762 miail 762 n fork kings r 762 pons 762 3001 762 a49 762 obst 762 ford st 762 uralskaia ul 762 8b 762 cavaliri 762 burt 762 b 42 762 nusa 762 cerveceria 762 tiatralnaia 762 prigorodnaia 762 longo 762 qurweg 762 b 388 762 herder st 762 crocker 762 1002 762 qulban 762 kastanin 762 b 226 762 schans 762 dusseldorfer st 763 schmidgasse 763 romualda 763 sabah 763 universitas 763 1 30 763 burial 763 institucion 763 bartolo 763 vilikogo 763 edgeill 763 mhr 763 sucha 763 g36 763 msp 763 divoire 763 buttonwood 763 zaliznichna 763 v solferino 763 plumbing 763 c 42 763 antenne 763 juvenal 763 parnell 763 byta 764 shhrdalefry 764 ditskogo 764 trebol 764 venture 764 mak 764 ricci 764 northern cretan hwy 764 cock 764 lombardi 764 arbre 764 vijver 764 plataforma 764 ilkogretim 764 spring rd 764 nr32 764 pero 764 hwy 6 764 hintergasse 764 avignon 764 gentil 764 iguacu 764 grp 764 ammerseeautobahn 764 s 12th st 764 klin 764 b 224 764 komeda 764 miaso 765 sti 765 lubelska 765 haleffz 765 15th av 765 galiriia 765 magazine 765 circulo 765 main weser bahn 765 cra 16 765 zsigmond 765 college dr 765 pera 765 myr 765 v gabrile dannunzio 765 jans 765 c s juan 765 buzon 765 barbara st 765 eng 765 lahan 765 spiaggia 765 dav 765 belles 765 rua 9 765 felso 765 baumann 765 2244 766 08k 766 lk rd 766 havlickova 766 sp53 766 tinggi 766 blessed 766 iman 766 eldon 766 presidio 766 tanks 766 emploi 766 darre 766 kat 766 ul tilmana 766 concejo 766 golfclub 766 firmino 766 fermin 766 co rd 5 766 argentinas 766 whiting 766 groves 766 gaia 767 renee 767 baoli 767 union rd 767 romulo 767 blugrass 767 njtp 767 rip 767 triftweg 767 benavides 767 guadalupe victoria 767 glen rd 767 ibarska 767 formosa fwy 767 g2211 767 avoca 767 chateauneuf 767 traugutta 767 ay 767 timbers 767 klee 767 rays 767 dlouha 768 industrias 768 childs 768 settle 768 cranbrook 768 pandan 768 champions 768 douar 768 guanabara 768 new england hwy 768 mlt 768 merel 768 tejada 768 r16 768 chaplin 768 brusselse 768 roper 768 luong 768 mdynte 768 antwerpse 768 mane 768 trang 768 adidas 768 eller 768 cimitir 768 pitstsa 768 dew 769 tbte 769 datong 769 co rd 11 769 tocantins 769 inpost 769 eroski 769 khdyr 769 greenwood dr 769 kalinina vulitsia 769 oldfild 769 nisa 769 d 134 769 a 26 769 nii 769 soo 769 landhotel 769 l 50 769 politia 769 haikou 769 alice st 769 itshak 769 xiang2 769 788 769 iksanpohang 769 villalba 770 trip 770 martens 770 mathis 770 aubrais 770 sino 770 geni 770 arb 770 siben 770 177th 770 37a 770 bahnhofs pl 770 lotte 770 spring ln 770 m 32 770 d 95 770 odell 770 mondo 770 myru 770 turun t 770 bowden 770 farrell 770 robin rd 770 clips 770 claret 770 berges 770 porin 770 marsh ln 770 baume 770 trinita 770 teren 770 abdelkader 770 leopolda 770 avtoshkola 770 duff 771 baurnhof 771 castanos 771 rp1 771 d 941 771 b 500 771 pintail 771 garin 771 3rd av n 771 icici 771 harald 771 skolska 771 chichester 771 dela 771 mainlm 771 v 1 maggio 771 kill 771 sirova 771 meadow cr 771 third st 771 benzina 771 dohanybolt 771 beech av 771 dara 772 gesu 772 osborn 772 technologiqu 772 954 772 669 772 eo9 772 gr lyon 772 zarza 772 gal 772 aleflhmralefhamza 772 k 52 772 rivage 772 hotell 772 borovaia 772 atilio 772 vojvode 772 m53 772 monastero 772 zdrowia 772 rosbank 772 adelino 772 vishniva 772 domingus 773 moulton 773 kommuny 773 tapia 773 huning 773 grace st 773 alger 773 gazebo 773 ainthmalefn 773 potts 773 bch st 773 alva 773 us 377 773 gebr 773 dagu 773 detention 773 switych 773 viachislava 773 kwun 773 e 461 773 r du marechal foch 773 lantana 773 a48 773 1 26 773 lhermitage 773 slabada 773 morgan rd 773 ukrainska 774 beino 774 pikes 774 b 239 774 maaaraviramaga 774 rochers 774 926 774 sacco 774 poultry 774 mason st 774 infanta 774 entertainment 774 gresham 774 vorota 774 hobson 774 kyrk 774 sempione 774 nazaire 774 rompetrol 774 wildhorse 774 gari 774 graham st 774 pompe 774 hornos 775 2089 775 danille 775 haga 775 karl libknecht st 775 lakeviw rd 775 pacific mwy 775 699 775 times 24th 775 brosse 775 new jersey tpk 775 bomba 775 laprida 775 tsyvn 775 hdyqte 775 saldanha 775 kirpichnaia 775 dul 775 ripoll 775 sat 775 natsionalnii 775 basler 775 at t 775 st marys rd 775 pwr 775 ags 776 pik 776 purok 776 lanir 776 herrmann 776 frenchman 776 bankomat bz wbk 776 819 776 prokhodnaia 776 swansea 776 orintale 776 mstwsf 776 1 8 776 automotriz 776 secondaria 776 yongah 776 baumschule 776 rua santos dumont 776 uchibni 776 mnswr 776 cr st 776 adler st 776 e 552 776 gosudarstvinnaia 776 rosewood dr 776 ganesh 776 1022 776 pqu infantil 776 bagh 776 zenith 777 landau 777 sananbananvinshinvinon 777 mouhoun 777 ontario st 777 lannec 777 millard 777 benitez 777 corno 777 calico 777 parfumeri 777 96a 777 bolnichnaia ul 777 rua 12 777 v s giuseppe 777 b 312 777 moosweg 777 hnshyalef 777 torri 777 myrtle av 777 packstation 778 ul rozy liuksim bg 778 tractor 778 warte 778 poplar dr 778 a fridhof 778 boquron 778 ioanna 778 ek 778 v s pitro 778 torrejon 778 prirody 778 bcp 778 sonia 778 lazio 778 tuttle 778 n25 778 n70 778 thuaidh 778 guayaquil 778 langgasse 778 wilsons 778 mitteldeutsche 779 toilette 779 qinglan 779 lybyalef 779 restaurang 779 okruzni 779 minnesota eastern railroad 779 caravaggio 779 14k 779 miller av 779 krumme 779 jr guan xi xian 779 845 779 tovary 779 barragem 779 ippolito 779 oko 779 sotsialistichiskaia ul 779 sr 9 779 shuttle 779 kennedy st 779 fontanelle 779 bd de leurope 779 matt 779 frutas 779 hermanas 779 k 56 779 carp 779 panfilova 779 ellington 780 haywood 780 iskra 780 krzyz 780 pices 780 fu shi chuan 780 dakota minnesota eastern railroad 780 espinosa 780 fitzroy 780 latina 780 svobodi 780 sokolska 780 kraividchiskii 780 ripple 780 but 780 wilkes 780 emas 780 lenz 780 bering 780 26th st 780 helipad 780 toscanini 780 teran 780 perales 780 nizalizhnosti vulitsia 780 wart 781 mwy a5 781 ludwigs 781 servizio 781 bocchetta 781 demesne 781 mariner 781 hawley 781 k 55 781 scalo 781 storey 781 fisch 781 dottor 781 strass 781 molkerei 781 erh 781 descanso 781 alsina 781 varzea 781 claudel 781 fishers 781 2050 781 milner 781 simia 781 event 781 autokinetodromos a5 782 d 939 782 balcon 782 grotto 782 sunderland 782 rua goias 782 mthry 782 maribor 782 dargent 782 fallen 782 radishchiva 782 tipton 782 lacy 782 shshm 782 vsta dr 782 sei 782 c 44 782 danisches bettenlager 782 pioneiro 782 kenya 782 cr 34 782 wasserfall 782 evans st 782 tagliamento 782 bagni 782 kee 782 alians 782 sirius 782 phou 782 lebenshilfe 782 polis 783 cb 783 woodman 783 njd 783 saar st 783 tanosveny 783 asi 783 brun 783 lyle 783 midtown 783 mennonite 783 a51 783 co rd 6 783 sportplatzweg 783 s pablo 783 bitter 783 schnellfahrstrecke hannover wurz bg 783 goodyear 783 lakeviw av 783 saturnino 783 zamok 783 s304 783 metirs 783 fairways 783 amiral 783 rockford 784 langdon 784 cra 14 784 marshal 784 flora st 784 zoom 784 domino 784 seligman 784 xvi 784 khu 784 cavee 784 jiraskova 784 shan shou xian 784 sudirman 784 pirson 784 throat 784 hoven 784 developmental 784 birch dr 784 50a 784 drh 784 falken st 784 shosse 785 r jean jacqus rousseau 785 mau 785 r roger salengro 785 nuraghe 785 werder 785 jewellers 785 industrialnaia 785 ep5 785 burrows 785 crestwood dr 785 gregoire 785 26a 785 citadelle 785 psp 785 slalefh 785 plans 785 v napoli 785 s307 785 lech 785 av de andalucia 785 rt napoleon 785 blauwe 785 al des tilleuls 785 gifford 786 erskine 786 finken st 786 tanzania 786 orchard av 786 walde 786 boulangeri patisseri 786 sp54 786 181st 786 cantu 786 ormond 786 r du commerce 786 velocita 786 deutsches rotes kreuz 786 vasa 786 norwich rd 786 skaly 786 highwood 786 kniznica 786 acqudotto 786 1 37 786 priorbank 786 poort 786 fahr 786 zavoda 786 marathonos 786 turi 786 funeraria 786 boll 786 peach st 787 r des marronnirs 787 wand 787 bit 787 ovoshchi 787 syracuse 787 berghof 787 shri 787 braille 787 dixi hwy 787 pigeonnir 787 v dante 787 grundwassermesstelle 787 marte 787 ivy ln 787 paine 787 saffron 787 b 248 787 us 151 787 curlew 787 nikoli 787 speedy 787 profsoiuznaia 787 1125 788 hundred 788 long st 788 macelleria 788 nickel 788 carlyle 788 e n st 788 c s jose 788 streetcar 788 polder 788 nordea 788 liguria 788 foss 788 v garibaldi 788 gagnon 788 branly 788 av de madrid 788 diable 788 danforth 788 anel 788 millet 788 meucci 788 dite 788 dobroliubova 788 weavers 788 marko 788 stone rd 788 1 93 788 parkhominko 788 3d 788 attiki odos 789 aleflshrqyte 789 rivoliutsionnaia 789 co rd 18 789 staro 789 fruhling 789 fuji 789 sab 789 setia 789 sutter 789 sander 789 cayuga 789 belleviw 789 lawns 789 erf 789 hetres 789 gao su t lu 789 1120 789 ignacio zaragoza 789 brico 789 sakuru 789 wlswalefly 790 lamia 790 1st st n 790 campus dr 790 gianni 790 g213 790 famille 790 clydesdale 790 kastely 790 a 21 790 islet 790 ridley 790 pecan st 790 rink 790 2016 790 rivoli 790 gs25 790 1020 790 hemlock st 790 n bway 790 groneweg 790 larc 791 gatos 791 t5 791 gillespi 791 loccitane 791 redstone 791 tohoku exp 791 namhagosokdoro 791 taleflqalefny 791 deutscher 791 mazari 791 kalinina st 791 769 791 illia 791 claudius 791 vlaanderen 791 2o 791 cambria 791 aubach 791 tvl 791 mcclure 791 w elm st 791 2013 791 castano 791 zeiss 791 osman 791 alefhl 791 schlossgasse 791 sta isabel 791 canales 791 niuweweg 791 bayviw av 791 aufm 792 gazi 792 mastri 792 maghrib 792 rustaveli 792 titus 792 maison neuve 792 shevchenka st 792 campamento 792 khalid 792 r carnot 792 redwood dr 792 collins st 792 payless 792 novosiolki 792 pocztowa 792 klarwerk 792 schonen 792 fresnos 792 d 139 792 c 36 792 personal 792 ancona 792 c 55 793 sr 42 793 sart 793 jana kochanowskigo 793 linh 793 ikan 793 2010 793 curri 793 singleton 793 frere 793 gera 793 tere 793 atelirs 793 rubin 793 jana 3 sobiskigo 793 016 793 slate cr 793 compostela 793 racing 793 diakoni 794 olimpia 794 fruits 794 st 3 794 gres 794 marshall rd 794 schanze 794 eiken ln 794 thayer 794 peabody 794 ohio st 794 palmira 794 qullenweg 794 saiid 794 lanxin ln 794 cretes 794 td canada trust 794 azalefdy 794 frain 794 gottlib 794 g2 jinghu exp 794 wintergreen 794 mad r 794 lancelot 794 dunkerqu 794 roseill 794 mzalefrain 794 slip 795 upas 795 bresse 795 oase 795 frente 795 guilford 795 1093 795 qian chuan 795 foy 795 los llanos 795 lkw 795 krasnaya st 795 overbrook 795 ghadir 795 autov del e ste 795 algarrobo 795 gent 795 plantage 795 serafim 795 grayson 795 molo 795 pauli 795 luise 795 krause 795 668 795 kruis st 795 shang xin yu zi dong ch dao 796 l 52 796 bota 796 guia 796 syndicat 796 zigelhutte 796 frozen 796 theas 796 b 64 796 ca 39 796 schelde 796 kaplica cmentarna 796 ortsverband 796 dorrego 796 california av 797 layton 797 bema 797 tops 797 goldener 797 sta rita 797 soborna 797 brazos 797 fraternite 797 katowicka 797 2nd st n 797 savoia 797 190th st 797 folsom 797 corpus 797 brazil 797 strathmore 797 leeuwerik 797 eingang 797 vara 797 edf 797 lick cr 798 sama 798 dunlop 798 wilmot 798 kelly rd 798 dorsey 798 snowy 798 rn 6 798 amadeus 798 d 135 798 pinetree 798 tempo 798 attiki 798 komplek 798 oneil 798 saal 798 chill 798 tejera 798 program 798 dill 798 arrondissement 798 colibri 798 kimberley 798 used 798 lebuh 798 qumado 798 schisstand 799 iriguchi 799 geer 799 caixa economica federal 799 ciclo 799 mazraat 799 belmonte 799 shota 799 sawgrass 799 timberlake 799 crispi 799 varese 799 c5 799 macs 799 cor 799 crucero 799 maranon 799 vb 799 grenir 799 ribara 799 lisi ukrainki ul 799 sanai 799 9b 799 ubs 799 sdt 799 raiffaizin 799 vez 800 unifid 800 chblohvo 800 n 260 800 1 79 800 1 st 800 volt 800 chaumes 800 verda 800 a55 800 forth 800 spedition 800 duis 800 boccaccio 800 ster 800 waldhaus 800 vorwerk 800 ovrag 800 prosper 800 co rd 16 800 av jean moulin 800 zlota 800 pansionat 800 puc 801 olaya 801 schlesir 801 francoise 801 parallel 801 brickyard 801 818 801 parkdale 801 gonglu 801 abasolo 801 fris 801 gallina 801 harapan 801 linda ln 801 manchester rd 801 purgos 801 s22 801 hampstead 801 subdistrict 802 biber 802 russ 802 rover 802 sr 4 802 linina prospikt 802 cretan 802 jysk 802 nab 802 vasiliia 802 montgomery st 802 yainr 802 zee 802 osa 802 ainzyz 802 596 802 felsen 802 serdan 802 blacks 802 founders 802 k 45 802 parchi 802 mt1 802 pescara 802 nis 803 ccc 803 prayer 803 lisova vulitsia 803 aleflbhr 803 burning 803 afflunte 803 elia 803 espejo 803 shkola 3 803 highland st 803 170th st 803 m 18 803 auriol 803 kasa 803 ul furmanova 803 vogt 803 b 47 803 kathy 803 29a 803 co rd 17 803 curvo 803 688 803 holly dr 803 b5 803 kordon 804 panificadora 804 zigelei st 804 infotafel 804 2005 804 waco 804 hokkaido exp 804 delmar 804 platinum 804 lavandires 804 stato 804 ze5 804 electro 804 ukrainskaia 804 immeuble 804 alemania 804 rostilikom 804 yadkin 804 investment 804 cisa 804 aparicio 804 kwong 804 nadaii 804 ora 804 b 236 804 tristan 804 oberfeld 804 jalan desa 804 hermann lons st 804 cooper st 804 vendeghaz 804 dublior 804 larche 804 borg 804 strandweg 805 adda 805 jam 805 beard 805 taimuzu 805 zviozdnaia ul 805 ochsen 805 slaska 805 stoneridge 805 soniachna 805 gruppe 805 reliance 805 russel 805 weil 805 waffle 805 romagna 805 stanhope 806 eglise st jean bapti ste 806 1st av n 806 yonge st 806 verbena 806 kirchsteig 806 ainrq 806 abo 806 parkside dr 806 heather ln 806 a 96 806 iglesia ni cristo 806 grenz st 806 sacro 806 spruce dr 807 ptda 807 ingeniria 807 xxiv 807 ss2 807 havelock 807 bendigo 807 carwash 807 m56 807 regi des transports marseillais 807 thrush 807 co rd 15 807 kearney 807 olympian 807 otlk 807 hofe 807 surabaya 807 aztec 807 nh66 808 794 808 palmeira 808 okruzhnaia 808 satama 808 vivinda 808 jane st 808 boak 808 bazarnaia 808 pedrera 808 vertrib 808 naziyat 808 heartland 808 cuore 808 r6 808 edison st 808 palmera 808 eddi 808 diamant 808 rotonde 808 satu 808 gordon rd 808 orange av 808 blucher 808 mex 2 808 sirinivaia ul 808 winter st 809 vitiranov 809 slym 809 mokra 809 hor 809 cul 809 merchant 809 visiolaia 809 predio 809 vival 809 4wd 809 mt pleasant 809 alagoas 809 boreole 809 heuvel 809 150th st 809 shiwi 809 s85 809 ryder 809 v del lavoro 809 d 911 809 osidlowa 810 jagillonska 810 stalingrad 810 leophoros 810 heitor 810 lindo 810 pleasant valley rd 810 reiger 810 marcina 810 vizd 810 mahattat 810 nalefzyte 810 aspen dr 810 wright st 810 brede 810 husca 810 tires 810 lakotelep 810 columbia av 810 sculpture 810 n 240 810 wolka 810 penedo 811 hiroshima 811 russo 811 pokrovka 811 eminescu 811 mutiara 811 paroisse 811 ticino 811 customs 811 ernstings family 811 alvorada 811 unirii 811 friar 811 melody ln 811 uda 811 brice 811 s state st 811 182nd 811 r des bruyeres 811 shelly 811 apart 811 gornja 812 birkenhof 812 placido 812 a 57 812 p5 812 polni 812 bettenlager 812 polonia 812 enchanted 812 852 812 belisario 812 v goffredo mameli 812 d 143 812 mt pleasant rd 812 portola 812 hope st 812 poros 812 capel 812 2260 812 porters 813 sixt 813 tulipanes 813 b 300 813 denison 813 2607901 813 bqaleflte 813 damas 813 telefonica o2 813 1ra 813 n 31 813 hurley 813 bosweg 813 rossilkhozbank 813 reedy cr 813 748 813 kalamata 813 torunska 813 zakladna 813 heid 814 sosa 814 bars 814 canterbury rd 814 metano 814 battlefild 814 peu 814 eichholz 814 neuss 814 stadtmaur 814 micro 814 barney 814 947 814 bramar 815 sp33 815 dy 815 hanging 815 maiskaia ul 815 2012 815 nome 815 mainz 815 perdana 815 charleroi 815 jazmines 815 saad 815 bmx 815 ranch rd 815 kiivskii 815 liningradskoi 815 tract 815 giovi 815 mtalefr 815 venecia 815 taj 815 bell rd 815 valeri 815 alcazar 815 r des sources 815 wilkopolskich 816 galindo 816 sike 816 lifestyle 816 blackthorn 816 palm av 816 pomocy 816 zhovtnia 816 pozharnaia chast 816 v bruno buozzi 816 morada 816 kereszt 816 r du village 816 trabajo 816 warrington 816 dippe 816 brenda 816 placid 816 sta barbara 816 lupo 816 irq 816 cuto 816 bildstock 816 quang 816 patos 816 gyula 816 poor 817 spence 817 sh 29 817 herculano 817 reggio 817 beauvoir 817 princess st 817 k2 817 abaixo 817 nanjiang ln 817 ksicia 817 mountainviw 817 wiland 817 99w 817 lamaasatvisarga 817 cambio 817 stag 817 ovidio 817 e 07 817 himmelfahrt 817 halsey 817 katholischer 818 us 309 818 ring v 818 fp 818 reunion 818 vidin 818 floor 818 ryans 818 brunel 818 cardinal dr 818 rotten 818 956 818 raunsantananvinenlasinsan 818 pervomayskaya st 818 cerda 818 construcao 818 jr han guan xian 818 729 819 paya 819 l190 819 t6 819 saikyo 819 b 80 819 1 87 819 emslandautobahn 819 stresemann 819 montaigne 819 boleslawa prusa 819 gauguin 819 v amerigo vespucci 819 w dr 819 main av 819 aura 819 dip 819 l 30 819 hdfc 819 rogers rd 819 elderberry 819 cr 28 819 iug 819 arcangelo 819 zwischen 819 bharat 819 stausee 819 ostfrisenspiss 819 beatriz 820 xiang1 820 aleflsflalef 820 leme 820 victorian 820 zyd 820 buyuk 820 elmore 820 tat 820 jmainyte 820 rochus 820 lavalleja 820 parrish 820 alvin 820 679 820 panera bread 820 etr 820 earls 820 hristo 820 comunita 820 r des primeveres 821 treviso 821 v pitro mascagni 821 camel 821 voskhod 821 fusion 821 horace 821 gb 821 usj 821 glan 821 roberta 821 esdoorn 821 hei chuan 821 lichten 821 alcaldia 821 whisper 821 seventh day adventist church 821 umgeungs 821 sidler 821 zhyr 821 encanto 821 ted 821 tokyu 822 leonidas 822 serna 822 atlantis 822 bulgaria 822 sure 822 rk 822 pescadores 822 d 141 822 jingzhu 822 dik 822 225th 822 apron 822 yongizbat 822 shi tian gao su 822 schone aussicht 822 dewitt 822 sh4 822 chua 822 seitenkanal 822 koh 822 rollins 822 constantino 822 ruskin 822 htl 822 nazare 823 watsons 823 labor 823 sivir 823 bassi 823 bulls 823 vasquz 823 qullen 823 bolnichni 823 beauvais 823 garazhni 823 laghetto 823 zweirad 823 deadman 823 d 920 823 remi 823 alfred st 823 215th 823 b 241 823 kotliarivskogo 823 bree 824 ul kutuzova 824 stipova vulitsia 824 alefyralefn 824 hermann st 824 b10 824 b 76 824 piski 824 quintas 824 audio 824 sp64 824 av d 824 epinettes 824 atkins 824 co rd 8 824 estes 824 starenweg 824 rochefort 824 mitskivicha 824 britton 824 madonnina 824 cynthia 824 mbou 824 lode 824 rudolf breitscheid st 824 mirror 824 bhardlgariia 824 dalnii 824 seniunija 825 obuv 825 pretty 825 c so italia 825 nationwide 825 spolem 825 zeedijk 825 wiza 825 ze6 825 appleby 825 ambrose 825 maywood 825 014 825 knoxville 825 longno 825 deiriyamazaki 825 vergine 825 ionian 825 n 15th st 825 senhor 826 shkolnaya st 826 cheryl 826 r des coqulicots 826 nederland 826 pk cr 826 r dr 826 sours 826 cordero 826 gorzdrav 826 wanderwege 826 creekside dr 826 rush cr 826 empr 826 lan hai gao su 826 ozdorovitilni 826 cartwright 826 birch av 826 oakhurst 826 unicredit bank 826 mansur 826 ridgewood dr 826 itzhak 826 radcliffe 826 denseignement 826 als 826 169th 826 ainbdalefllh 826 bonifica 826 matisse 826 construccion 826 pressing 826 b 38 826 yu chuan 826 nigeria 827 n elm st 827 saur 827 richmond av 827 ul 40 lit pobidy 827 retirement 827 moniuszki 827 ronsard 827 ferroviaria 827 comando 827 spusk 827 lanse 827 fohrenweg 827 herradura 827 n35 827 constituyentes 827 whataburger 827 charca 827 richeliu 828 swisscom 828 baumana 828 aller 828 16th av 828 darah 828 us 150 828 shkolnaia vulitsa 828 furlong 828 m 11 828 whisky 828 haye 828 rossignols 828 sia 828 giulia 828 evans rd 828 curitiba 828 locean 828 software 828 hummingbird ln 828 12b 828 chambre 829 m9 829 areia 829 camas 829 jaguar 829 asahi 829 tikhaia ul 829 ss113 829 rheinstrecke 829 pl de lhotel de ville 829 hooker 829 pl wolnosci 829 los angeles 829 polaris 829 novas 830 crocus 830 prirodni 830 lesage 830 1136 830 rua h 830 sige 830 abruzzi 830 preescolar 830 manastirea 830 calles 830 aquarium 830 bessemer 830 technisches 830 resorts 830 zwarte 830 110th st 830 artl 830 luneburger 830 thralefn 830 llbnaleft 830 latelir 830 szosa 830 1010 830 peron 2 831 a teich 831 goli 831 rn 5 831 lorena 831 frisen 831 barnsley 831 zell 831 macquari 831 r du college 831 pollard 831 cra 12 831 dostoivskogo 831 eat 831 thru 831 amparo 831 s 11th st 831 haldenweg 831 vigna 831 loureiro 831 montero 831 vasutallomas 831 heimatmuseum 831 gallego 831 sergipe 832 mariz 832 nko 832 soldirs 832 appaloosa 832 birch rd 832 wilkigo 832 mckee 832 bombay 832 somerville 832 pedestrian 832 a 39 832 rp11 832 220th st 832 elwood 832 iasnaia 832 monjas 832 abri 832 marianne 833 vondel 833 tei 833 rentals 833 aachen 833 voyage 833 d 132 833 974 833 kinney 833 thin 833 pichincha 833 wear 833 kea 3 833 faraday 833 balefzalefr 833 shoppers drug mart 833 ajoncs 833 capucins 833 fairviw dr 833 khao 833 a s 833 2nd av n 834 f3 834 985 834 zuni 834 r des moulins 834 niz 834 kellys 834 pioneer rd 834 niderrhein 834 eastfild 834 grants 834 aon 834 ernstings 834 carrick 834 izvor 834 jodlowa 834 v marco polo 834 sabana 834 emma st 834 rack 834 gorse 834 m11 834 brd 834 ainyalefdte 834 rozas 835 sr 15 835 lejas 835 leblanc 835 1941 835 cra 13 835 serwis 835 gesamtschule 835 guarani 835 helsinki 835 cline 835 parodos 835 newton rd 835 abbotsford 835 huda 835 32a 835 falmouth 835 jungle 835 853 835 tweed 835 mostowa 835 saut 835 nar 835 lavadero 835 paradero 835 3100 835 cosmos 836 henritta 836 joban exp 836 jeunesse 836 tatum 836 s106 836 muralla 836 n18 836 dryden 836 johnsons 836 robidog 836 polivoi 836 danisches 836 patagonia 836 kalinovka 836 phare 836 cinega 836 give wy 836 falla 836 gounod 836 tumulus 836 ss309 837 yani 837 pegasus 837 b9 837 1 275 837 kyzyl 837 damian 837 drumul 837 173rd 837 b 294 837 budowi 837 st 2 837 roosevelt st 837 chilis 837 razvitiia 837 r de bellevu 837 noto 837 kosmos 837 e walnut st 837 corpo 837 kubanskaia 837 schwarzenbach 837 acapulco 837 br 282 837 werk st 838 relax 838 revolution 838 ote 838 bamberger 838 lem 838 kastanje ln 838 n19 838 qnalefte 838 ges 838 grammar 838 kontora 838 hickory dr 838 pervomayskaya 838 171st 838 11b 838 cantine 838 camp rd 838 st peter 838 faye 838 chinti 838 bhavan 838 dreick 838 risparmio 839 roussillon 839 d 98 839 exclusive 839 white rd 839 lofts 839 alondra 839 punt 839 ushakova 839 965 839 kirpichni 839 e 532 839 kapell 839 k 46 839 cayo 839 bananghanin 839 boulodrome 839 levis 839 ionia odos 839 terpel 839 holman 839 cane cr 839 cr 27 839 lamm 839 naturler 839 quatorze 840 lang st 840 myosotis 840 vaasan 840 ca 99 840 giusti 840 lakeshore rd 840 saginaw 840 us 30 alternate 840 cascata 840 192nd 840 evangeliqu 840 masia 840 umspannwerk 840 borden 840 mela 840 stusa 840 liv 840 einstein st 840 pirimogi vulitsia 840 sukhumvit 840 dalefyrte 840 karya 840 disney 841 firs 841 r des fontaines 841 tuin 841 aleflalefhmr 841 uudd 841 godfrey 841 gdn state pwy 841 draper 841 yukari 841 placette 841 rabelais 841 tecumse 841 khlyj 841 kneza 841 sp47 841 comptoir 841 dris 841 013 841 timber ln 841 ul kotovskogo 841 parini 841 chapaiva vulitsia 841 mcarthur 841 sleep 842 mclaughlin 842 sp51 842 coon cr 842 chiliuskintsiv 842 villard 842 axel 842 tata 842 bodegas 842 joya 842 klubnaia ul 842 quarto 842 ring 3 842 bloom 842 fugo 842 d200 842 cottonwood dr 842 schloss pl 842 winchester rd 842 camos 842 heinrich st 843 jezero 843 schooner 843 telford 843 merino 843 cr 23 843 calmette 843 tile 843 windham 843 rookery 843 perdu 843 koyu 843 porfirio 843 byk 843 robinsons 843 woman 843 naturale 843 tapas 843 kirchengasse 843 omnibus 843 profi 843 holding 843 qarah 843 330th 843 barid 843 made 844 moorland 844 language 844 bonaparte 844 weymouth 844 costilla 844 uniqu 844 locanda 844 zao tandir 844 sotsialistichiskaia 844 d 160 844 joly 844 dales 844 morgen 844 mtn vw dr 844 hainan 844 sp43 844 czarna 844 mainlh 845 ktm 845 buzzard 845 johor 845 30th st 845 c ramon cajal 845 stadtsparkasse 845 1021 845 r des jonquilles 845 canberra 845 harmoni 845 bethune 845 waterman 845 obrero 845 mostovaia 845 tacoma 845 roller 845 moscow 846 wacholderweg 846 orlik 846 universita 846 linea de alta velocidad madrid zaragoza barcelona frontera francesa 846 transportes 846 rangel 846 mushola 846 valley vw rd 846 danau 846 bufe 846 jin ji ri ben t dao ming gu wu xian 846 krzyza 846 mastra 846 ceramica 846 sidlungs st 846 kosumo shi you 846 australian 846 745 846 hlavna 846 laurel dr 847 moron 847 015 847 vcto 847 piscina mun 847 vinh 847 pwy dr 847 d 136 847 astor 847 ext 847 holle 847 ralefh 847 hallen 847 nankai 847 r des remparts 847 jalefl 847 klingen 847 hamzah 847 schmide st 847 ats 847 peron 1 848 familiar 848 zagalnoosvitnia 848 breda 848 levequ 848 av ccvcn 848 33a 848 orchard c 848 sidlungsweg 848 angeli 848 marseillais 848 chemical 848 e 761 848 gutshof 848 wanderpark pl 848 us 431 849 lost cr 849 capucines 849 let 849 augsburger st 849 fossil 849 read 849 coniston 849 gardiner 849 dan in luan huang xin gyeongbu hsl 849 onyx 849 koko 849 holderlin st 849 gudes 849 jiu dong hai dao 849 bete 849 s16 849 universitats 849 lekarna 849 g2501 849 ss 106 jonica 849 botanichnii 849 98k 849 birkdale 849 conestoga 850 vs 850 us 385 850 rvo 850 daffodil 850 d 916 850 hogeweg 850 tsutaya 850 ul bilinskogo 850 1029 851 pontiac 851 moltke st 851 allendale 851 evergreen rd 851 laser 851 ul chaikovskogo 851 firro 851 cats 851 d 936 851 chininxanin 851 wijk 851 bulak 851 35e 851 kuchen 851 bla 851 bait 851 827 851 paud 851 optic 851 guo daodasdassdang hao 851 daodasdassdang 851 hessen 851 dau 851 verge 851 riki 851 gallegos 851 lucena 851 milnitsa 851 amro 851 step 852 silskaia 852 rondeau 852 atlantica 852 northway 852 neuapostolische kirche 852 nh53 852 morgan st 852 bst 852 venustiano carranza 852 oceano 852 shkolnaya 852 genoa 852 dodd 852 ostbahn 852 salmon r 852 b 45 852 808 852 filzi 852 dassisi 852 catherine st 852 fora 852 zarichna vulitsia 853 juminsenteo 853 xl 853 vicenza 853 autobuses 853 860 853 latham 853 performing 853 mess 853 londis 853 henritte 853 infantes 853 jalefygalefh 853 sfantul 853 990 853 obshchistvo 853 laranjeiras 853 rocio 854 44a 854 101 05 854 tailor 854 third av 854 newland 854 fairy 854 newton st 854 elm rd 854 cecile 854 operations 854 namur 854 lagunas 854 jolit 854 parador 854 date 854 lirio 854 americana 854 fortis 854 jug 854 waltham 854 tyrsova 854 ross rd 854 1 grund 854 manege 855 jugendherberge 855 fortress 855 universitesi 855 stipova 855 leith 855 westbourne 855 graca 855 targowa 855 antalya 855 hort 855 w walnut st 855 17 35 855 rua sao pedro 855 aramburu 855 sp37 855 bottega 855 riggs 855 semard 855 25th st 855 marchand 855 wawa 855 albano 856 tana 856 20k 856 cramer 856 ismal 856 armenia 856 columbia st 856 autolib 856 tiroler st 856 frantsiia 856 liceul 856 ulu 856 lucerne 856 locale 856 pmr 856 tampines 856 macau 856 msainp 856 schulz 856 torreon 856 morike st 856 toma 856 vitnam veterans memorial hwy 857 regensburger 857 choi 857 hamilton av 857 abidjan 857 suburban 857 abelardo 857 sr 3 857 holdings 857 k1 857 sukhaia 857 gorni 857 whetstone 857 callao 857 livre 857 franc 857 waldfridhof 857 len 857 grover 857 jungbu exp 857 grandviw dr 858 r de garenne 858 steeple 858 hoyos 858 troitskii 858 r des hirondelles 858 paraguai 858 d 78 858 seeblick 858 alpina 858 pobida 858 1004 858 paterson 858 gat 858 westtangente 858 weirweg 858 unis 858 people 858 abbey rd 859 decembri 859 touch 859 container 859 amarillo 859 enkanlasensaninan 859 casera 859 2300 859 ahl 859 briarcliff 859 meio 859 abbaye 859 1016 859 sunset rd 859 cibc 859 car wash 859 k 53 859 v john fitzgerald kennedy 859 birch ln 859 therapy 859 afb 859 rua sao joao 859 dri 859 lebuhraya utara selatan 860 terrell 860 larbre 860 lanka 860 stadt witten 860 jinja 860 heaton 860 arnaud 860 budynek 860 coop jednota 860 pkn 860 casa de cultura 860 elm dr 860 rust 860 semmering 860 archi 860 ravens 860 1 45 860 amphitheatre 860 borja 860 s elm st 860 bouchard 860 kingston rd 860 barstow 860 l 85 861 mezra 861 vecinal 861 creekwood 861 dao13 861 lugovoi 861 arabian 861 guo dao38 hao 861 tide 861 pima 861 wimbledon 861 buckland 861 turpan 861 poprzeczna 861 delhaize 861 1110 861 bridal 861 peaceful 861 mersey 861 tandoori 861 v bologna 861 tully 861 inspiktsiia 861 l2 861 leader price 861 jmalefl 861 breckenridge 861 vub 862 cassino 862 comunidade 862 yerushalaim 862 celle 862 staats 862 r traversire 862 downey 862 deutsch 862 1d 862 b50 862 rivires 862 c principal 862 adult 862 nut 863 aleflalefmyr 863 oca 863 pali 863 andriivka 863 bare 863 delight 863 kyrko 863 benavente 863 support 863 quzon 863 economic 863 b17 863 dunlap 863 haskell 863 robinson st 864 k 44 864 beeline 864 crowsnest 864 long cr 864 robert st 864 sfs gyeongbu 864 taylor av 864 showa 864 b 70 864 chip 864 pekao 864 wheatley 864 pilgrims 864 vana 864 billings 864 reims 864 atwood 864 790 864 magdeburger st 865 plads 865 dahr 865 ferrand 865 etna 865 hero 865 barth 865 lanxin 865 knife 865 r du 11 novembre 865 haid 865 fy rd 865 consulting 865 denise 865 brir 865 d 150 865 vsi 865 quarenta 865 hallmark 865 muntzer 865 wa 99 865 stawowa 865 chasseurs 865 neuhof 866 s201 866 hedwig 866 khngngkolyoiingltthllliisoko 866 helm 866 itu 866 amistad 866 salzburger st 866 697 866 ertu 866 tatnift 866 maior 866 dahl 866 bilefeld 866 serpentine 866 zili 866 parrilla 866 smithy 866 concept 866 fai 866 fellows 867 sanitatshaus 867 rua 7 de setembro 867 hartman 867 relif 867 medici 867 old rd 867 brethren 867 r jeanne darc 867 n 232 867 ulmen st 867 mchs 867 bolnichnaia 867 northland 867 safe 867 svincolo 867 kessler 867 klooster st 867 gregg 867 spirits 867 canning 867 ss38 867 sham 868 sherman st 868 bastide 868 quince 868 cresta 868 ah62 868 cr 30 868 zsilinszky 868 ul budionnogo 868 sablire 868 r des fleurs 868 v michelangelo buonarroti 868 hansestadt lubeck 868 winona 868 overseas 868 windsong 868 nan bei da dao 868 prince st 868 piastowska 868 carri 868 zviozdnaia 868 russkaia 869 forestir 869 kel 869 willamette 869 keats 869 platja 869 lights 869 sel 869 psaje 2 869 v vincenzo bellini 869 prada 869 nt 869 arche 869 alfons 869 charlemagne 869 andorra 869 gr trunk rd 869 griboidova 870 sp36 870 v umberto 1 870 nevez 870 rossiiskaia ul 870 teichweg 870 palu 870 nautiqu 870 tyres 870 ferinwohnungen 870 famous 870 rodano 870 sp45 870 douglas rd 870 khola 870 e 14 870 moya 870 d 925 870 855 870 eemaliger 870 virginia st 870 senegal 870 abra 871 seward 871 aviles 871 moskovskoi shossi 871 desjardins 871 jana kilinskigo 871 s301 871 kenmore 871 pang 871 packard 871 patrol 871 wilhelmina st 871 policji 871 savitskaia vulitsa 871 naviglio 871 caserma 871 bond st 871 thoir 871 bosqus 872 rutledge 872 a d heide 872 bath rd 872 auditorio 872 alimentation 872 card 872 sport pl st 872 autov rias baixas 872 donaldson 872 ji tian chuan 872 774 872 pig 872 1 91 872 pape 872 greenland 872 todo 872 showroom 873 jewelers 873 parkers 873 also 873 mino 873 quartire 873 church of god 873 oldham 873 prestwick 873 plover 873 kwm 873 notch 873 tuna 873 ss53 873 prirodi 873 murat 873 055 873 abn 873 n 113 873 chornovola 873 r de croix 873 laurel av 873 rn11 873 gei 873 dunham 874 sherwin 874 goose cr 874 goulart 874 negri 874 dubrovka 874 garnir 874 m60 874 olgi 874 izba 874 constantine 874 archibald 874 roxbury 874 windsor dr 874 penha 874 northwestern 874 kosmetik 874 lili 874 s walnut st 874 edgemont 874 viario 874 fidiralnoi 875 pte 875 pit r 875 angelina 875 warren rd 875 pea 875 midas 875 sabadell 875 v gatano donizetti 875 munchner st 875 drexel 875 vado 875 wilkins 875 boyaca 875 modulo 875 bainbridge 875 greyhound 875 schulze 875 medford 875 krdy 876 verga 876 hwy 17 trans canada 876 uwe 876 ikebukuro 876 doria 876 vos 876 dao113 876 teton 876 parkovochnogo 876 mallory 876 hwy 401 876 coimbra 876 rua sta catarina 876 studiia 876 gku administrator moskovskogo parkovochnogo prostranstva 876 westpac 876 pandora 877 watling 877 admirala 877 coal cr 877 agri 877 matyas 877 ter dr 877 afton 877 nya 877 turner rd 877 prostranstva 877 cadillac 877 heller 877 twilight 877 summerhill 877 heathfild 877 nagel 877 morningside dr 877 zino 878 underground 878 greve 878 fressnapf 878 craven 878 320th 878 oreilly 878 977 878 ze1 878 d 97 878 beograd bar 878 sanitar 878 tacos 878 d 999 878 tsui 878 niles 878 progresso 878 mrt 878 d 140 878 ceiba 878 tremblay 878 bajcsy 878 giuliano 878 furth 878 c e 878 lupine 878 v fratelli rosselli 879 aosta 879 solidarnosci 879 ningjingyan exp 879 ghana 879 posolstvo 879 donji 879 jing chuan 879 botanichiskii 879 stang 879 honorio 879 baseline rd 879 iaroslava 879 ze3 879 funo 879 160th st 879 baki 879 siglo 879 eventos 879 gananmanzeninrain 879 ningjingyan 879 grazer 879 narva 879 family dollar 879 4x4 879 rosco 879 zhu jing yan gao su 879 pst 879 k 41 879 campestre 879 s33 879 kolyma 879 seville 879 2da 879 avtodublir 880 bucherei 880 sirinivaia 880 kos 880 perdrix 880 vodokhranilishchi 880 brecht 880 avtodublir bama 880 hofmann 880 cr 24 880 robin ln 880 v della pace 880 kavkaz 880 hongha luan huang yuki ze 880 v montello 880 wallis 880 2400 880 cda 880 baznycia 880 grimes 880 batteri 880 tizi 880 minimarkit 880 madina 880 millan 881 big branch 881 949 881 systeme 881 923 881 lucca 881 enmedio 881 romains 881 pumpkin 881 quirino 881 athenon 881 cg 881 vail 881 49k 882 human 882 s4 882 lyman 882 rua sete 882 naples 882 kham 882 ritz 882 carles 882 malom 882 w ln 882 schutzenverein 882 gorda 882 brucker 882 gradina 882 valhalla 882 hci 882 girl 882 fore 882 846 882 khlong 882 lawton 882 guba 882 24th st 882 moreland 882 sp44 882 landry 882 tho 882 middenweg 882 tannen st 882 direttissima 883 jwalefd 883 abilio 883 merrick 883 stryd 883 production 883 obroncow 883 lee rd 883 spragu 883 mort 883 springbrook 883 alefljdydte 883 aquiles 883 iop 883 pfarrheim 883 burro 883 valery 883 ul timiriaziva 883 westport 883 taquria 883 belgrade bar 883 tanglewood dr 883 colchester 883 lilinweg 883 bok 883 lisbon 883 troon 883 name no 884 rigole 884 bramley 884 foxwood 884 toby 884 kristall 884 booker 884 roseaux 884 elton 884 n s exp 884 erik 884 cavalcante 884 a 25 884 loteamento 884 mikhanizatorov 884 dactivites 884 katharina 884 ahorro 884 comm technician name no 884 fc s martin 884 ul truda 884 jewish 885 497 885 sanzio 885 017 885 cher 885 mais 885 lockhart 885 prior 885 yanjiang 885 albert heijn 885 e 38 ah61 885 tejar 885 painted 885 kelten 885 paketshop 885 d 129 885 pionirska 886 frida 886 dubh 886 nos 886 boyne 886 kofi 886 oktiabrskoi 886 charcot 886 garfild st 886 zwycistwa 886 laroport 886 nimen 886 bong 886 manastir 886 hais 886 breiten 886 dbyrstalefn 886 zurcher 886 liman 886 polar 886 dandenong 886 sassafras 886 dorfkirche 886 till 886 outback 886 marton 887 bangor 887 sh 73 887 millwood 887 entre rios 887 monika 887 mitchell rd 887 andy 887 departemental 887 bonfim 887 r102 887 c cervantes 887 mt2 887 lek 887 n59 888 828 888 kriz 888 homestead rd 888 rufino 888 silos 888 keio 888 tec 888 florence st 888 kanali 888 slovnaft 888 v gioacchino rossini 888 kommunalnaia ul 888 berggasse 888 nmp 888 fakultit 888 suan 888 nasiliniia 888 hudson st 888 granger 888 poza 888 wurzburger st 888 778 888 terrasses 889 amo 889 g207 889 colette 889 ukrnafta 889 pembina 889 koltsivaia ul 889 kaminni 889 bona 889 cook st 889 bernd 889 lewis rd 889 sydenham 889 rajon 889 krasina 889 wake 889 s203 889 borisa 889 bygade 889 merida 889 encino 889 brookhaven 889 zg 889 cinq 890 companis 890 bingo 890 traian 890 bourgeois 890 vicuna 890 crespo 890 2630 890 villanova 890 cattail 890 grunewald 890 stauffen 890 pusteria 890 176th 890 kristianskaia 890 quretaro 890 thrift 890 mojave 890 hankyu 891 kivit 891 pozulo 891 d 955 891 kapliczka 891 sp65 891 airline 891 belgium 891 buntu 891 kola 891 legend 891 fk 891 matilde 891 hospitalir 891 heu 891 thiar 891 euclid av 891 lansi 891 zalioji g 891 philippines 892 whitfild 892 carla 892 kastner 892 av du 8 mai 1945 892 tourterelles 892 eski 892 goldenrod 892 olumpia odos 892 vodopad 892 szobor 892 gainsborough 892 eastman 892 parcel 892 oliksandra 892 kar 892 e 12th st 892 kum 893 pischanaia 893 horta 893 g st 893 jacarandas 893 centro de salud 893 hokuriku shinkansen 893 av getulio vargas 893 egypt 893 bus stn 893 noah 893 ordonez 893 departement 893 kosa 893 843 893 zahnarzt 893 r 22 894 adair 894 tif 894 takasaki 894 gioia 894 trindade 894 14th av 894 heads 894 starling 894 reformatus 894 drogaria 894 artisans 894 olinda 894 delivery 894 tedi 894 bowls 894 ambulatoriia 895 mercator 895 sava 895 jasna 895 gar 895 hwy 2 895 baldy 895 palau 895 hammam 895 us 35 895 neuqun 895 lian huo exp 895 599 895 alefmyn 895 754 896 parnu 896 cipriano 896 weldon 896 hardwick 896 alefslalefmy 896 s 1 896 groblje 896 espanola 896 1 71 896 ditsad 896 658 896 lobster 896 mup 896 d 131 896 orta 896 183rd 896 teknik 896 a wein bg 897 ogolnoksztalcace 897 r de labreuvoir 897 d 933 897 rua amazonas 897 baixa 897 merton 897 sudharzautobahn 897 bymalefrstalefn 897 chanoine 897 lonsdale 897 wodna 897 ilan 897 789 897 turm st 897 rose av 898 muddy cr 898 moral 898 enclave 898 sr 17 898 cambridge st 898 publiczne 898 cra 9 898 libano 898 yes 898 uferweg 898 paola 898 slpe 898 jacopo 898 fairhaven 898 cale 898 kendal 898 singapore 899 pensiunea 899 vordere 899 sonoma 899 hoyt 899 888 899 benedict 899 alpino 899 malinovka 899 colima 899 buis 899 nana 899 rohr 899 chic 899 skylark 899 gusde 899 mannheimer 899 una 899 ssr 899 vivero 899 mason rd 899 pearce 899 dis 899 radisson 899 great western main ln 900 uruguai 900 cambrian 900 bei lu xin gan xian 900 modrzewiowa 900 xin cun 900 dwalefr 900 desarrollo 900 mizuho in xing 900 arana 900 mirim 900 panorama st 900 sande 900 supirmarkit 900 wichita 900 bruce hwy 900 hospedaje 900 ferrovia del brennero 900 auto del mediterraneo 901 milani 901 vee 901 nene 901 hatton 901 ecm1 901 cheong 901 lgv mediterranee ln5 901 r11 901 klubnaia 901 selby 901 1920 901 ysgol 901 hajar 901 pinheiros 901 shea 901 kuchai 901 hunts 902 primrose ln 902 m29 902 23rd st 902 honghli 902 gdyr 902 larkin 902 najd 902 poplar av 902 tancsics mialy u 903 sh 3 903 al st 903 toul 903 paramo 903 areas 903 abdel 903 lunga 903 veit 903 coombe 903 tinh 903 atherton 903 naciones 903 bowi 903 centerville 903 rideau 903 927 903 wave 903 rud 903 pil 903 melani 903 judicial 904 berta 904 hickman 904 pickering 904 znachiniia 904 moulay 904 lipu 904 castle st 904 wagner st 904 v della liberta 904 greece 904 fridhofweg 904 leones 904 schultz 904 tuv 904 pramen 904 mrkhz 904 caldera 904 foxglove 904 barclays bank plc 904 pasubio 905 angle 905 kosta 905 mcbride 905 otish 905 naberezhnaya 905 bixio 905 hilario 905 kleist 905 gesellschaft 905 antiqu 905 1060 905 n 16th st 905 cra 6 906 edmondo 906 anson 906 sista 906 frisor 906 e 61 906 uplands 906 1026 906 v fiume 906 haley 906 giratoire 906 aktiv 906 s21 906 fishing cr 906 pinon 906 schuster 906 35a 906 pozos 906 e 11 906 khoztovary 906 ninth 906 aulnes 906 mil rd 907 mathews 907 veilchenweg 907 lucni 907 683 907 e35 907 roig 907 foro 907 785 907 kungs 907 emory 907 rau 907 western union 907 corbett 907 amigo 907 dauphin 907 miasokombinat 907 pushkina vulitsia 907 lincoln hwy 907 d300 907 prospikt pobidy 907 cocos 907 bain 908 diogo 908 austral 908 maxx 908 cali 908 stadtgraben 908 v grenoble 908 kent st 908 limoges 908 catamarca 908 technische 908 fabre 908 hubertus st 908 stroitil 908 fife 908 di3 909 barranquillo 909 schweizer 909 shirokaia ul 909 795 909 bison 909 pane 909 ul tolstogo 909 r des violettes 909 21a 909 easement 909 n walnut st 909 m25 909 marion st 909 galvao 909 jing zhu gao su 909 chang pan zi dong ch dao 909 aleflnyl 910 glenbrook 910 beaconsfild 910 hofacker 910 whiteouse 910 c 34 910 v giotto 910 syr 910 vuka 910 823 910 arret 910 highline 910 anselmo 910 eatery 911 aussenring 911 rua 10 911 gottingen 911 dalefrwkhalefnh 911 hurto 911 melgar 911 ss42 911 mahal 911 sandhill 911 labelle 911 balef 911 shetland 911 ferrovia adriatica 911 elmer 911 pokrova 911 industrigebit 911 krd 912 rudolf disel st 912 asen 912 autov mudejar 912 gsp 912 druzhbi 912 golf course rd 912 samsung 912 obern 912 kiril 912 hutan 912 kirova st 912 r des rosirs 913 keene 913 17b 913 italy 913 ntra 913 noor 913 t mobile 913 carnation 913 hanshin 913 rudolfsbahn 913 westland 913 ss45bis 913 dns 913 shan tian chuan 913 greenbank 913 jorg 913 lilin 913 l1 913 r des cerisirs 913 1733 914 barreiro 914 khalil 914 heckenweg 914 570 000 914 dag 914 ungwan 914 traders 914 otto hahn st 914 warrior 914 d 902 914 667 914 ingeniros 914 cornelio 914 aroma 915 aleflvn 915 moll 915 administrator 915 savitskaia 915 iona 915 rt nationale 915 wincentego witosa 915 mnr dr 915 lez 915 sp49 915 voz 915 zero 915 merchants 916 dubai 916 ward st 916 ruinas 916 styles 916 nah 916 orla 916 bock 916 jing bang ji xing dian t ben xian 916 evangelische kirche 916 ford rd 916 koppel 916 sluzhby 916 lintas 916 olumpia 916 r georges clemenceau 916 ambassador 917 st michal 917 disco 917 medway 917 ouadi 917 sawmill rd 917 feurwache 917 po tribovaniiu 917 altenheim 917 pl de liberte 917 jalefmainte 917 bps sbirbank 917 jansen 917 mainzer st 917 karin 917 sunset bd 917 d 92 918 willow av 918 martel 918 buckskin 918 vik 918 rott 918 campagna 918 sagaidachnogo 918 publix 918 vf 918 darya 918 sorgente 918 lafayette st 918 pista deportiva 918 wendy 918 tuohyeong 918 s49 918 bender 918 caron 918 strazacka 918 handels 918 hmwd 919 pottery 919 morel 919 stollen 919 tiziano 919 agora 919 pirshotravniva vulitsia 919 rasa 919 shy 919 tempel 919 golo 919 tawi 919 felde 919 passe 919 pilon 919 hwang 919 pomme 919 mouttes 919 lowry 919 badia 919 harris st 919 v pitro nenni 920 sukhoi 920 sovereign 920 cristiana 920 areal 920 banbury 920 pnjm 920 q yang dao 920 eo6 920 e 372 920 regents 920 airport bd 920 first united methodist church 920 ingo 920 konzum 920 ze4 920 b 470 920 2014 920 695 920 cabildo 920 may st 921 ainlwalefn 921 claveles 921 rockaway 921 bhalefr 921 lynx 921 lobato 921 axis 921 custodio 921 kaminna 921 educational 921 emef 921 redwood hwy 921 valdez 921 sta ana 921 sturt 921 tomb 922 luhu 922 manresa 922 dalias 922 sloan 922 vaca 922 give 922 grub 922 cambridge rd 922 s88 922 cutler 922 d 142 922 seguin 922 mildred 922 nasir 922 bway av 922 867 922 rua parana 922 centura 922 goddard 922 a46 922 1 av 923 welfare 923 e 802 923 m 3 923 s308 923 innere 923 judischer 923 gamma 923 vento 923 advanced 923 molenbeek 923 olimpio 923 odenwald 923 renner 923 marine dr 923 1090 923 salaf 923 csob 923 rainir 923 w 12th st 923 nebo 923 asb 923 meseta 924 sofiia 924 b 107 924 harcourt 924 cas 924 kohi 924 pedroso 924 psaje 1 924 jerez 924 kings hwy 924 lill 924 sfax 924 laavu 924 stewart st 924 q jia bian li shang dian 924 wrights 924 decheteri 924 warszawy 924 longhorn 924 vidyalaya 924 edmonton 924 utica 924 schwarzbach 924 talon 924 1003 925 upravliniia 925 diter 925 delano 925 naturelle 925 caseys 925 ss47 925 palisades 925 schmitz 925 d 901 925 alsace 925 cra 11 925 hockey 925 silvia 925 prudente 925 perfect 925 kantine 925 paderewskigo 925 1011 925 eagle dr 925 nikolskoi 926 molly 926 justus 926 fence 926 jasminowa 926 stadtweg 926 munster st 926 bordes 926 danbury 926 dokomosiyotupu 926 martinus 926 deledda 926 d 149 926 sibir 926 1140 926 brunnengasse 926 turk 926 870 926 karls 926 amorim 927 schwab 927 kauno 927 d 128 927 galeana 927 schleenweg 927 goutte 927 voinkomat 927 assembleia 927 jiangno 927 649 927 campina 927 r des pins 927 pugachiova 927 sukiya 927 sherry 927 r de legalite 927 sfly 927 misiones 927 docomo 927 valleyviw 927 picardi 927 holly st 928 temps 928 tita 928 568 928 zagrebacka 928 drainage 928 tannery 928 r du marais 928 livorno 928 yai 928 a 92 928 vivo 928 territorial 928 zahnarztpraxis 928 liffey 928 walnut cr 928 hathaway 928 sp48 928 basswood 929 f4 929 lichfild 929 teachers 929 broadviw 929 thorne 929 b 88 929 sedlo 929 khabb 929 mid fork feather r 929 seaplane 929 dalia 929 nikolai 929 baltic 929 br 158 929 intesa 929 hewitt 929 optiqu 929 ivory 929 entry 929 s a 929 ming gu wu t dao ming gu wu ben xian 930 d 943 930 r du bois 930 linzer st 930 memory ln 930 huang chuan 930 lewiatan 930 sputnik 930 middletown 930 ponti 930 tifenbach 930 rn5 930 frederik 930 zapovidnik 930 beuken ln 930 droim 930 indian oil 930 br 381 930 ligne de paris austerlitz a bordeaux st jean 930 greer 930 goldbach 930 antico 931 torquato 931 cra 3 931 cra 8 931 gia 931 raco 931 ainbwd 931 polana 931 pius 931 togo 931 fenwick 931 isral national trl 931 lia 931 bocca 931 marengo 931 corneille 931 677 932 b 214 932 aleflsryain 932 noria 932 alefbtdalefyyte 932 helsingin t 932 rua sem denominacao 932 yves rocher 932 elster 932 g1511 932 stour 932 sudheide 932 1015 932 steinacker 932 lider 932 c 33 932 dps 932 fichten st 932 nh52 932 lauter 933 skov 933 v filippo turati 933 falaise 933 uncle 933 hmlk 933 denmark 933 jacksonville 933 wisma 933 lemann 933 doshkolnoi 933 pirvaia 933 us 79 933 gervais 934 2690 934 pequno 934 pfarr 934 danjou 934 mos burger 934 aussichtsturm 934 kiosqu 934 vicenc 934 loo 934 forsyth 934 chapel rd 934 amaru 934 lininskaia ul 934 loges 934 erno 934 bakker 934 estetica 934 nan hai dian qi t dao nan hai ben xian 934 extreme 934 45a 934 juncal 934 826 935 ganga 935 odizhda 935 warden 935 sadovaya st 935 secundario 935 rua seis 935 e 371 935 proposed 935 wohnen 935 vitor 935 skholeio 935 autostrada serenissima 935 palazzina 935 tus 935 shoppe 935 fig 935 a 75 935 a d kirche 935 lots 935 evitement 936 pontes 936 nelken st 936 nusayn 936 bennington 936 tulipan 936 allmend 936 jude 936 citi 936 placer 936 b6 936 n15 936 vojvodi 936 hals 936 r lamartine 936 3900 936 minskaia 936 sima 937 catherines 937 kingsland 937 baker rd 937 cr 22 937 maz 937 ahmet 937 reja 937 springvale 937 wanda 937 voiny 937 potenza 937 foreign 937 casar 937 kate 937 lapa 937 chengde 938 captains 938 town cr 938 reithalle 938 lilinthal 938 maple rd 938 gallini 938 karoly 938 pata 938 funes 938 ss 3 bis tiberina 938 s103 938 evening 938 d 1075 938 prusa 938 elba 938 rana 938 udoli 939 greggs 939 commerce st 939 e25 939 pusat 939 samsun 939 rector 939 odessa 939 60n 939 kc 939 d 87 939 wastewater 939 deal 939 plasencia 939 perry st 939 goncalo 939 mallorca 939 ul girtsina 939 metropolitano 940 runway 940 ukraine 940 trainte 940 plano 940 coq 940 12 de octubre 940 cortina 940 iceland 940 v provinciale 940 bobcat 940 starlight 940 arsenal 940 jaww 940 24a 940 ligne de st germain des fosses a nimes courbessac 940 toti 941 malone 941 dusseldorfer 941 1800 941 genesis 941 turner st 941 volleyball 941 crete 941 windmuhle 941 justicia 941 sidli 941 galway 941 sila 941 2017 941 dobson 942 ioan 942 papa johns 942 menahem 942 uralskaia 942 erika 942 babcock 942 jing gang ao gao su 942 kiler 942 kumano kaido 942 mudejar 942 dorogi 942 parcours 942 scouts 942 a59 943 mawr 943 saitama 943 pirandello 943 prolitarskii 943 conoco 943 parmentir 943 thalefnwyte 943 colonial dr 943 junho 943 rua minas gerais 943 parsonage 943 topeka 943 transportnaia ul 943 apotek 944 autobusz 944 g310 944 brifkasten 944 s31 944 bellaire 944 fulton st 944 pogodna 944 jornalista 944 egret 944 residenz 944 second st 944 d 148 944 isidoro 944 holderlin 944 riverwood 944 tribovaniiu 944 band 944 citadel 944 mh 944 pirshotravniva 944 g3012 945 837 945 793 945 lamas 945 merkur 945 boleslawa chrobrego 945 alois 945 nh16 945 karlsruher 945 pnc 945 nizavisimosti 945 transilvania 945 louisa 945 dagua 945 fitch 945 travelodge 945 banc 945 kolbe 945 farmatsiia 945 darcy 945 d 89 945 best buy 945 kamila 945 20 de novimbre 946 moore rd 946 yuma 946 marilyn 946 r jean mermoz 946 galilee 946 686 946 moshe 946 co rd 1 946 v dellartigianato 946 pira 946 brem 946 preparatory 946 whitley 946 canova 946 angola 946 28k 946 mnr farm 946 n 14th st 946 canal rd 947 chatelet 947 inwood 947 cru 947 rodoviario 947 976 947 mcmillan 947 s52 947 arago 947 tasman 947 franks 947 mk 947 r de source 947 native 947 cheikh 947 nemzeti 947 olivet 947 mugut 948 166th 948 gazprombank 948 c4 948 rockingham 948 nbu 948 damiano 948 g209 948 chiquito 948 snp 948 muniz 948 zalioji 948 singer 948 dionisio 948 skatepark 948 1 74 948 silishchi 948 ellison 948 hyo 949 locke 949 905000 949 botica 949 okrezna 949 parker rd 949 kenyon 949 bft 949 bayonne 949 zayed 949 praga 949 lr rd 949 t4 949 957 950 roosevelt av 950 ludlow 950 littoral 950 st v 950 vivian 950 biscayne 950 ver 950 biskupa 950 midden 950 591 950 middlesex 950 armada 950 cary 950 rbc 951 e 119 ah8 951 lesperance 951 alternative 951 a bahndamm 951 spaulding 951 heilige 951 fils 951 us 180 951 nikolaivka 951 pushkinskaia 951 canarias 951 ormeaux 951 teluk 951 rawson 951 1604 951 co rd 10 951 yarmouth 951 a 73 951 mass 951 ipe 952 rams 952 tule 952 ponton 952 737 952 athenes 952 lhomme 952 us 422 952 kanto 952 824 952 eighth 952 summit dr 952 leach 952 rurale 952 ul 1 maia 953 sayli 953 841 953 cra 10 953 844 953 r5 953 bills 953 b 104 953 diderot 953 emam 953 nova vulitsia 953 st johns rd 953 jm 953 sonnenhof 953 thompson st 954 a 11 954 rosebud 954 sergent 954 ny 5 954 kochanowskigo 954 982 954 mana 954 pennsylvania tpk 954 albverein 954 elagazas 954 reeds 954 b 176 955 savoy 955 baixas 955 subaru 955 ravenna 955 sbi 955 northumberland 955 erhard 955 raionu 955 cra 4 955 sign 955 d 90 955 subs 955 merzweckhalle 955 saleflm 956 babushkina 956 busan 956 otp bank 956 savio 956 resende 956 gentry 956 granby 956 767 956 693 956 pech 956 1005 956 parker st 956 servis 957 machi 957 rostand 957 koroliova 957 lm 957 hein 957 sltalefn 957 sandringham 957 moller 957 cullen 957 drury 957 ark 957 hardwood 957 ul enirgitikov 957 steven 958 bhshty 958 select 958 weber st 958 mossen 958 alpi 958 onion 958 padana 958 jockey 958 kanaalweg 958 harris rd 958 buck cr 958 kathleen 958 filling 958 valerio 958 spree 959 malec 959 amur yakutsk mainline 959 pave 959 kentrikes 959 d4 959 setor 960 aga 960 denny 960 ok 66 960 tourism 960 nordic 960 caciqu 960 kamp st 960 yusuf 960 maxima 960 slot 960 laurel ln 960 975 960 hwy 7 961 g307 961 xian exp 961 vorderer 961 turf 961 haiti 961 registro 961 r des alouttes 961 forest preserve district of cook co 961 southport 961 sargent 961 allis 961 lansing 961 georgios 962 d 928 962 eifel 962 kneipp 962 beta 962 salina 962 diz 962 caballo 962 lise 962 holly ln 962 word 962 stomatologichiskaia 962 canteras 962 alefystgalefh 962 4000 962 heritage dr 962 11e 962 waterviw 963 gu chuan 963 ilkokulu 963 ah31 963 zimmer 963 rose ln 963 st johns church 963 your 963 birds 963 brigadeiro 963 iva 963 b 303 963 puyrredon 963 lian huo gao su gong lu 963 location 963 whitaker 963 lister 963 guo dao56 hao 963 gornaia ul 963 elevated 963 erlen st 963 163rd 963 sheriff 964 striltsiv 964 armas 964 chagas 964 sing 964 madrasat 964 iia 964 kreta 964 foz 964 potrero 964 cortile 965 e 58 e 571 965 edmundo 965 hochbealter 965 aleflqdym 965 us 129 965 ql 1a 965 g206 965 e 68 965 av independencia 966 margaret st 966 trout cr 966 paw 966 casals 966 rshyd 966 b 56 966 juwelir 966 uddhko 966 ligne de marseille a vintimille 966 thu 966 morozova 966 boulogne 966 pam 966 marceau 966 annette 966 dhi 966 s14 966 gorkogo vulitsia 966 hdwd 966 rup 966 galvani 966 g214 966 jr ri li xian 966 waska 967 chickasaw 967 birig 967 ligne de moret veneux les sablons a lyon perrache 967 produktovi magazin 967 bronson 967 courbessac 967 eucaliptos 967 v belvedere 967 pavilon 967 comunal 967 lyndhurst 968 zigel 968 a 15 968 majora 968 salle polyvalente 968 margurites 968 manse 968 alefrd 968 transformator 968 lindbergh 968 lennox 968 aleflsyalefraleft 968 ola 968 farnham 968 adrin 968 summer st 968 divine 969 kelso 969 musholla 969 platt 969 zarichi 969 09 27 969 chaika 969 w 11th st 969 stadthalle 969 1 d au 969 bankasi 970 e 17 970 kly 970 sampson 970 citibank 970 yaylasi 970 s bway 970 large 970 golfo 970 tis 970 pernambuco 970 valence 970 10e 970 cinnamon 970 daly 970 mjmain 970 didir 970 sidlungs 970 oktiabr 970 946 970 f st 970 dalt 971 chaucer 971 gemeindeverwaltung 971 legionow 971 cathedrale 971 fc belgrano 971 jewelry 971 30a 971 bucks 971 a bach 971 sp39 971 anders 972 begraafplaats 972 sumac 972 937 972 rezende 972 baracca 972 polizei 972 alp 972 gertrude 972 metropolitana 972 notarius 972 brandenburger 972 cosme 972 oo 972 whippoorwill 972 thuringer bahn 973 836 973 nalefnwalefyy 973 erlenbach 973 aun 973 bassiin 973 kykr 973 guru 973 osipinko 973 butchers 973 helios 973 b 247 973 siberia 973 ji ye jia 973 3004 973 penny ln 973 zelena 973 mrkhzy 973 cajon 973 curve 973 zeno 974 state bank of india 974 breezy 974 aman 974 499 974 v salvo dacquisto 974 hebert 974 regent st 974 litchfild 974 57k 974 conca 974 b 44 974 savi 975 telstra 975 807 975 caseta 975 misisipo 975 lp rd 975 delices 975 eulalia 975 shl 975 b 188 975 687 975 n 122 975 colmenar 975 azzurra 975 adams av 975 t7 975 kiivska vulitsia 975 jing hu xian 975 sklodowskij 975 mandalay 975 st des fridens 976 wohnhaus 976 r de vallee 976 buttercup 976 schwarzwald st 976 broadmoor 976 dans 976 ear 976 easter 976 ian 977 franck 977 delaware av 977 prix 977 bucher 977 sadovi piriulok 977 sept 977 pomoshchi 977 benizelou 977 walmart supercenter 977 fryderyka szopena 977 vlksm 977 artesia 977 cagayan 977 dhaka 977 sportzentrum 977 feldgasse 977 dzialkowy 977 sdla 978 prim 978 sr 14 978 pepinire 978 votweg 978 descartes 978 eben 978 adams rd 978 us 127 978 italo 978 n washington st 978 694 978 rockland 978 woodhouse 978 vente 978 polje 978 mstfalef 978 commissariat 978 fuha yu luan huang yuki ze seohaan exp 978 kreuzung 978 dao38 978 861 978 humphrey 979 gauthir 979 metallbau 979 freeport 979 swr 979 r anatole france 979 gilberto 979 jr ao yu xian 979 ul shchorsa 979 t 01 979 titan 979 bcr 979 wildwood dr 979 allomas 979 fati 979 trolley 979 echeverria 979 puri 979 plzla 979 d 85 979 silla 980 marston 980 yokohama 980 fahrrad 980 n 110 980 913 980 sadowa 980 larson 980 tupac 980 olympia odos 980 ah70 980 lann 980 backpackers 980 eastviw 980 rua g 980 frederick st 980 dao112 980 d 145 980 webster st 980 mining 980 1201 980 camps 981 septembre 981 veneux 981 s102 981 k 38 981 cr 21 981 paraiba 981 rubi 981 falefrs 981 ama 981 zst 981 sunday 981 piscinas 981 elaine 981 bayard 981 rosecrans 981 jollibee 981 s306 982 towpath 982 demetriou 982 tsby 982 georgian 982 newmarket 982 jens 982 polova vulitsia 982 lawrence st 982 d 910 982 joint 982 ming shen gao su dao lu 983 mate 983 bal 983 r aristide briand 983 d 126 983 varity 983 moriah 983 kratka 983 bru 983 cutt 983 dollar tree 983 981 983 vergara 983 bajas 983 pine rd 983 shhr 983 cochran 983 847 984 polig 984 landsberger 984 flanders 984 tsrb 984 mossy 984 753 984 eton 984 restauracja 984 709 984 gul 984 ron 984 strandbad 984 kanal st 984 beag 984 koinoteta 984 livingstone 985 pine ln 985 bts 985 e 53 985 vester 985 11 listopada 985 rom 985 gerardo 985 main ln 985 cheese 985 ketteler 985 akker 985 haw 985 aia 985 aleflwtny 985 d 133 985 montano 986 gov 986 plough 986 597 986 barron 986 agentur 986 laar 986 a44 986 nua 986 kommunalnaia 986 bernardes 986 landweg 986 angelica 986 mhic 986 stars 986 ariosto 986 humber 986 vilniaus 986 spessart 987 kale 987 shepard 987 paroquia 987 lords 987 gleann 987 steamboat 987 918 987 volgy 987 share 987 yakutsk 987 n dr 987 us 190 987 expedicionario 987 rigas 987 nift 987 chardonnerets 988 trailway 988 us 92 988 plum st 988 droite 988 wellington rd 988 880 988 moinho 988 octavio 988 pete 988 rodgers 988 zentral 988 n17 988 kaserne 988 stahl 988 aron 988 disused 988 police stn 989 ulmen 989 ceres 989 holanda 989 myrtle st 989 caminito 989 danville 989 meadow rd 989 us 119 989 zalefdh 989 escobar 989 bhd 989 a 72 989 maiskaia 989 leonel 989 mccarthy 990 gemini 990 prinz 990 kiowa 990 rua bahia 990 mcpherson 990 gartner 990 kessel 990 r 23 990 mariia 990 wein st 990 noyer 990 malaysia 991 biru 991 beltran 991 cormir 991 linden av 991 viktor 991 sycamore dr 991 univirmag 991 790000 992 okulu 992 giorgiia 992 e 03 992 hamn 992 sp38 992 656 992 aleflainrby 992 mockingbird ln 992 giv 992 aleflshhyd 992 swanson 992 woodville 993 zigler 993 us 18 993 us 191 993 hokuriku jidosha do 993 pelayo 993 kurt schumacher st 993 chist 993 mshrwain 993 radi 993 meitetsu 993 brava 993 nk 993 bayern 994 edson 994 cerros 994 upa 994 cancer 994 v po 994 arbeit 994 ul volodarskogo 994 hansestadt 994 loree 994 crimson 994 phardt 994 limerick 994 mel 994 743 994 alefbalef 994 fraun 994 conifer 994 anthonys 994 eusebio 995 dellartigianato 995 botanico 995 dong hai dao xin gan xian jr tokaido shinkansen 995 crag 995 balzac 995 kere 995 feira 995 a 24 995 lerchen st 995 romania 995 vhutobankusiyotupu 995 jordi 995 749 995 587 996 herder 996 28a 996 comet 996 rua 8 996 kilomitr 996 mediterrania 996 cba 996 grado 996 14n 996 gras 996 mgc 996 684 996 morne 996 erica 996 mays 996 v santantonio 996 ik 996 mateus 997 parliament 997 rathenau 997 ramiro 997 okq8 997 ghost 997 791 997 ripina 997 101 02 997 wines 997 farley 997 jp 997 foundry 997 christi 997 e 251 997 d 96 997 lininu 997 cds 998 alefby 998 forest ln 998 doo 998 cochrane 998 577 998 gilman 998 isles 998 flynn 998 husova 998 b8 998 soldir 999 marcello 999 assunta 999 953 999 kraft 999 nelken 999 moat 999 fryderyka chopina 999 m20 999 unita 999 junin 999 sciri 999 eo1 999 washburn 999 lgv mediterranee 1000 banen 1000 bukowa 1000 ajuntament 1000 preston rd 1000 wonderland 1000 contact 1000 olimp 1000 blancs 1000 first st 1000 rodzinny 1000 prestige 1000 waterway 1000 santangelo 1000 tnt 1000 781 1000 maquis 1000 ul chirnyshivskogo 1000 kronen 1001 hamlin 1001 bleriot 1001 long ln 1001 ruta nacional 9 1001 colmado 1001 gku 1001 francesca 1001 mohammad 1001 crenshaw 1001 tamiami trl 1001 k 42 1001 feliz 1001 frau 1001 205th 1001 cvs pharmacy 1001 krasoty 1001 skogs 1001 windsor rd 1001 malraux 1001 pereulok 1002 jakuba 1002 hasen 1002 kurchatova 1002 945 1002 best western 1002 s digo fwy 1002 g324 1002 autozone 1002 chaume 1002 csatorna 1002 monmouth 1002 phan 1002 khutor 1002 tango 1002 riverfront 1002 jarnvags 1002 pedreira 1002 handel st 1002 a 16 1003 coill 1003 gemeenteuis 1003 placita 1003 launay 1003 chemist 1003 tent 1003 shalefrain 1 1003 mahatma 1003 lesglesia 1003 davignon 1003 brookside dr 1003 saale 1003 mosubaga 1003 beeches 1004 despensa 1004 tirishkovoi 1004 llys 1004 pana 1004 s washington st 1004 wesleyan 1004 v dellindustria 1004 conner 1004 szabadsag u 1004 r st jean 1004 oakville 1004 corbin 1004 cementiri 1004 stadtische 1004 kerkhof 1005 magna 1005 thailand 1005 kamen 1005 anchorage 1005 village hall 1005 d 900 1005 geist 1005 a 70 1005 omaha 1005 zarichni 1005 310th 1005 riccardo 1005 arpad u 1005 a61 1005 783 1006 spain 1006 barri 1006 zolotaia 1006 s 10th st 1006 gibdd 1006 lada 1006 lecluse 1006 washington bd 1006 qbr 1006 nkd 1006 22nd st 1006 2015 1006 batea 1006 stipnoi 1006 ararat 1006 piaui 1006 tomba 1006 punto enel 1006 ralston 1006 jing ban dian qi t dao jing ban ben xian 1006 rayon 1007 rostocker 1007 arbol 1007 nikolaou 1007 ugarte 1007 dacia 1007 nave 1007 maidan 1007 manula 1007 boule 1007 814 1007 list 1007 beatty 1008 klyte 1008 bjada 1008 finley 1008 berlingur 1008 v dei mille 1008 cr 16 1008 xi i gao su 1008 moorweg 1008 peaje 1008 margarida 1008 krasnykh 1008 dessous 1008 venancio 1008 seguros 1008 bui 1008 lonesome 1008 menhir 1008 k 33 1009 gaillard 1009 r du presbytere 1009 plane 1009 pogibshim 1009 lydia 1009 cheney 1009 istochnik 1009 21st st 1009 d 88 1009 westend 1009 podlesi 1009 millstone 1009 bora 1009 aime 1009 sagrada 1009 mono 1010 ladeira 1010 ginasio 1010 r des saules 1010 squire 1010 linden pl 1010 pemberton 1010 fv 1010 informatica 1010 1025 1010 hike 1010 magnolia dr 1010 martial 1010 sbida 1010 restaurant brands intl inc 1010 azalefdralefh 1011 hrvatska 1011 pelourinho 1011 altas 1011 juliana st 1011 pets 1011 rontgen st 1011 pica 1011 harrit 1011 kot 1011 pharma 1011 b100 1011 skunk 1012 gerber 1012 fullerton 1012 mesquita 1012 834 1012 290th 1012 lic 1012 commercial st 1012 brampton 1012 silta 1012 prairis 1012 montt 1012 union av 1012 rojo 1012 kala 1013 986 1013 2002 1013 guadiana 1013 jeff 1013 janusza 1013 luch 1013 bergamo 1013 ross st 1013 oxley 1013 aleflalefmalefm 1013 okko 1013 chick fil a 1013 carrasco 1013 m 04 1013 sudan 1013 6b 1013 anzac 1013 slavy 1013 gervasio 1013 161st 1013 circolo 1013 forn 1013 basic 1013 giselabahn 1013 croydon 1013 r st martin 1013 backstube 1014 pack 1014 arce 1014 kolonka 1014 retraite 1014 hang rui gao su 1014 kirova vulitsia 1014 a 42 1014 bischof 1014 hastanesi 1014 obhardizdnaia 1014 chestnut av 1014 twelve 1014 giugno 1014 eder 1014 piaski 1014 jacquline 1014 petrom 1014 n 13th st 1015 occidental 1015 ortodoxa 1015 priv 1015 opet petrolculuk a s 1015 clothing 1015 fuha yu luan huang yuki ze 1015 liquors 1015 menendez 1015 hayward 1015 linden ln 1015 annapolis 1015 muara 1015 cedar rd 1015 a17 1016 tysiaclecia 1016 lake shore dr 1016 ladis 1016 br 364 1016 asternweg 1016 flight 1016 ami 1016 grapevine 1016 d 951 1016 bale 1016 seaton 1016 crewe 1016 hell 1016 seohaangosokdoro 1017 773 1017 dachni 1017 aristides 1017 rhode 1017 sp42 1017 736 1017 needle 1017 steeles 1017 d 121 1017 dane 1017 calinte 1017 petrolculuk 1017 catalpa 1017 strong 1017 chifa 1017 parkviw dr 1017 1250 1017 d 76 1017 d 122 1018 mcintyre 1018 g65w 1018 v xx settembre 1018 78k 1018 qui 1018 zavodskoi 1018 bolt 1018 singh 1018 popolo 1018 us 76 1018 theo 1018 springfild rd 1018 lauren 1018 trilha 1018 ise 1018 lila 1018 manzanita 1018 beata 1018 917 1018 rigo 1018 svit 1018 baskin 1018 embajada 1018 d 952 1019 15d 1019 unter df 1019 d 201 1019 gemeindezentrum 1019 sporta 1019 mariners 1019 esculas 1019 olson 1019 jingzhang 1019 enzo 1019 creperi 1019 e 117 1020 boulder cr 1020 gelato 1020 frainy 1020 apex 1020 poland 1020 enfild 1020 huhangyong 1020 orchard dr 1020 molini 1020 dammweg 1020 s r o 1020 us 183 1020 konigs 1020 francisco villa 1020 r de lindustri 1021 barda 1021 zacisze 1021 potters 1021 indomaret 1021 scenic dr 1021 banyan 1021 merry 1021 d 100 1021 ah30 1021 pasteleria 1021 lodi 1021 salz bg tiroler bahn 1021 172nd 1022 nasan 1022 rapide 1022 casars 1022 neru 1022 plinio 1022 habana 1022 halefdy 1022 privado 1022 mimorial 1022 ln5 1022 erzsebet 1023 parko 1023 high rd 1023 jump 1023 sr 26 1023 kelvin 1023 cana 1023 aleflalefbtdalefyyte 1023 mica 1023 12th av 1023 aqabat 1023 15k 1023 rn 3 1024 904 1024 biriozka 1024 dipl 1024 simons 1024 panagia 1024 krasnaya 1024 us 160 1024 slade 1024 756 1024 rivas 1025 mandiri 1025 101 03 1025 nachtegaal 1025 poincare 1025 tuscany 1025 e 92 1025 overlook dr 1025 planita 1025 barrios 1025 sorrento 1025 petrus 1025 815 1025 b 65 1025 hamburger st 1025 ghar 1026 nolan 1026 sunset av 1026 750000 1026 agostino 1026 sweets 1026 wyatt 1026 needles subdivision 1026 dn7 1026 tulipes 1026 rua cinco 1026 academic 1026 e 47 1026 kios 1026 lade 1026 fabryczna 1026 colt 1027 acton 1027 weisse 1027 malibu 1027 butts 1027 guidan 1027 g93 1027 scottsdale 1027 jessi 1027 bey 1027 burma 1027 national rt 9 1027 cluster 1027 bartolomeu 1028 m39 1028 mcgee 1028 xix 1028 exterior 1028 732 1028 chair 1028 lanzhou 1028 teofilo 1028 fussweg 1028 594 1028 aleflainbd 1028 bleiche 1028 757 1028 gyeongbu exp s 1028 mindiliiva 1028 russia 1029 miteo 1029 tot 1029 daudet 1029 logrono 1029 rochdale 1029 solnyshko 1029 pure 1029 goffredo 1029 dahlinweg 1029 rua 7 1029 dikabristov 1029 bici 1029 morike 1029 dave 1029 sully 1029 gornji 1030 lomond 1030 richchia 1030 gidan 1030 929 1030 nikolaos 1030 wee 1030 veicle 1030 kirchen st 1030 springer 1030 taxiway 1030 russell rd 1030 magistralnaia ul 1030 sp27 1030 s30 1030 brel 1030 cavalir 1030 fourmile 1031 hospice 1031 sorbirs 1031 cr 19 1031 asagi 1031 neville 1031 carn 1031 maynard 1031 eo8a 1031 albatros 1031 rossiiskaia 1031 clinton st 1031 sundown 1031 kuhn 1032 conego 1032 stephani 1032 squaw cr 1032 zbvtynsqy 1032 av de verdun 1032 valparaiso 1032 v carolina 1032 tenth 1032 isinina 1032 synagogu 1032 980 1032 horner 1032 roll 1032 shore rd 1032 deans 1032 educativo 1032 sharqi 1032 brabant 1032 hire 1033 odawara 1033 bahr 1033 mirage 1033 wyzwolenia 1033 welt 1033 dive 1033 savon 1033 b 83 1033 armady 1033 sarsfild 1033 168th 1033 winters 1034 vauban 1034 musiqu 1034 clavel 1034 merkez 1034 georgen 1034 ulysses 1034 tobias 1034 manga 1034 cleveland av 1034 stochod 1034 d 124 1035 vid 1035 sokol 1035 sugar cr 1035 foli 1035 janet 1035 laburnum 1035 simon bolivar 1035 technician 1035 khngngnyphthuuddhghlllng 1035 ferin 1035 paulista 1035 012 1035 roadhouse 1035 jujuy 1035 minsk 1035 b 26 1035 e 123 1035 slater 1035 r st pirre 1036 carrion 1036 c 29 1036 grantham 1036 melvin 1036 austerlitz 1036 geang 1036 laboratoire 1036 college rd 1036 915 1036 bottle 1036 gor 1036 gastronom 1036 aparcaminto 1036 poik 1036 hoog st 1036 amerigo 1037 adolphe 1037 loust 1037 d 94 1037 midlands 1037 khlyl 1037 haydn st 1037 summers 1037 veterinaire 1037 amandirs 1037 sangu 1037 oxbow 1037 sp40 1037 kebangsaan 1038 village dr 1038 576 1038 mechanic 1038 914 1038 paxton 1038 930 1038 n14 1038 aval 1038 dortmund 1038 orange st 1038 frio 1038 brant 1038 monza 1038 chiara 1039 mex 200 1039 mitchell st 1039 bet 1039 margrit 1039 chirniakhovskogo 1039 pas 1039 jc 1039 pimentel 1039 beausejour 1039 r voltaire 1039 sonder 1039 sp35 1039 lar 1040 olmedo 1040 duna 1040 riverbend 1040 sunrise dr 1040 balefg 1040 mrs 1040 meadow dr 1040 1 29 1040 belleza 1040 shino 1040 ionia 1041 palme 1041 bockler 1041 amador 1041 dhr 1041 gird 1041 960 1041 e rd 1042 dagi 1042 dentistry 1042 kovil 1042 758 1042 lori 1042 king rd 1042 pineda 1042 bathurst 1042 leen 1042 amedeo 1042 jesu 1043 wolff 1043 bric 1043 jing ha xian 1043 566 1043 suvbatukusu 1043 hutton 1043 ss106 1043 samson 1043 23a 1043 mhsn 1043 appalachian trl 1043 ziab 1043 dao56 1043 shrewsbury 1043 depuration 1043 zaliv 1043 loge 1043 luisen st 1044 cheltenham 1044 scarlet 1044 krebsbach 1044 hilltop rd 1044 a31 1044 nadrazi 1044 nelson rd 1044 dicks 1044 cantonale 1044 campanile 1044 oberdan 1044 noodle 1044 ibarra 1044 rozhdistva 1044 martinho 1044 secondo 1044 wurzburger 1044 hinton 1044 rozsa 1044 s29 1045 muro 1045 iasli 1045 mississippi r 1045 jung 1045 bartok 1045 irmaos 1045 senator 1045 dorn 1045 raffale 1045 c 30 1045 massa 1045 ronchi 1045 zi2 1045 simeon 1045 ctr av 1045 kenny 1045 rodez 1046 lif 1046 spokane 1046 banco popular 1046 inntal autobahn 1046 iana 1046 sprint 1046 adventista 1046 kangaroo 1046 szopena 1046 r du puits 1046 tirimok 1046 pacific av 1046 blanches 1046 cra 5 1046 29n 1047 kanaaldijk 1047 heaven 1047 n 611 1047 madani 1047 borsellino 1047 cil 1047 trillium 1047 mena 1047 e 38 1047 correctional 1047 prospikt mira 1048 generala wladyslawa sikorskigo 1048 stump 1048 mig 1048 cerrito 1048 bobby 1048 646 1048 environmental 1048 567 1048 hedge 1048 halsted 1048 wick 1049 petrolimex 1049 murray st 1049 judy 1049 589 1049 aranha 1049 montfort 1049 seng 1049 2001 1049 pride 1049 rakoczi ferenc u 1049 innes 1049 prist 1049 chipotle 1049 gifts 1050 usine 1050 d5 1050 bankia 1050 khngngiinyuunniikosodd 1050 ep4 1050 deck 1050 lorme 1050 yen 1050 filia 1050 paivakoti 1050 holzweg 1050 sklep spozywczy 1050 av du general leclerc 1050 kiss 1051 bore 1051 dotorukohisiyotupu 1051 boom 1051 diniz 1051 e 27 1051 chester rd 1051 porcupine 1051 strickland 1051 miasta 1051 deresi 1051 stroimatirialy 1052 avsw 1052 friuli 1052 loft 1052 cake 1052 aquatic 1052 ny 17 1052 reese 1052 bistrot 1052 selecta 1052 chatillon 1052 zac 1052 vilikaia 1052 n 21 1052 489 1052 accuil 1052 joana 1052 doze 1052 turia 1052 716 1053 osada 1053 luce 1053 shanno 1053 alumni 1053 abate 1053 reka 1053 southviw 1053 willoughby 1053 488 1053 v francesco petrarca 1053 leland 1053 linton 1053 hotel de ville 1053 harrison av 1054 k 50 1054 ukrsibbank 1054 belair 1054 mid st 1054 etoile 1054 panificio 1054 ze2 1054 plaines 1054 huy 1055 ferrara 1055 wellesley 1055 bingham 1055 shenyang 1055 167th 1055 hillside rd 1055 verts 1055 04k 1055 huddersfild 1055 thompson cr 1055 e 11th st 1055 georgi 1055 ethel 1055 autov ruta de plata 1055 pl du general de gaulle 1055 collet 1055 smokey 1056 s38 1056 redmond 1056 sh1 1056 eucalyptus 1056 seniorenzentrum 1056 caletera 1056 e3 1056 195th 1056 sosnovka 1056 wye 1056 women 1056 gail 1056 d 93 1056 chute 1057 evangelisch 1057 girolamo 1057 mvd 1057 b 243 1057 metropole 1057 william flinn hwy 1057 b 39 1057 ul matrosova 1057 tikhaia 1057 gemeente 1057 sosnovi 1057 pushkina st 1057 us 93 1057 thomas rd 1057 frobel 1057 voinni 1057 butter 1058 recanto 1058 accs rd 1058 wesola 1058 potsdamer 1058 zachodnia 1058 celestino 1058 oxford rd 1058 wickham 1058 gharbi 1058 unnamed 1058 triunfo 1059 printing 1059 d 84 1059 av de constitucion 1059 khalefld 1059 stamford 1059 lugo 1059 integral 1059 lund 1059 fou 1059 fleetwood 1059 bashnift 1059 1914 1059 providencia 1059 autov del noro ste 1059 gluck 1059 laghi 1059 9e 1060 jackson rd 1060 bama 1060 glinki 1060 nahal 1060 sz 1060 blumenweg 1060 obb 1060 hines 1060 wahdat 1060 age 1060 borki 1060 wspolna 1060 iz 1061 sabina 1061 1 49 1061 anderungsschneiderei 1061 glover 1061 strom 1061 svetog 1062 caro 1062 softball 1062 sora 1062 keswick 1062 erfurt 1062 vorosmarty 1062 haupt pl 1062 administrative 1062 walloni 1062 ney 1062 vasut 1063 r des bleuts 1063 smile 1063 evaristo 1063 pyongyang 1063 mercure 1063 genevive 1063 num 1063 colbert 1063 mid cr 1063 slaskich 1063 d 130 1063 poblacion 1064 almeria 1064 639 1064 communication 1064 atrium 1064 shooting 1064 custom 1064 drama 1064 ashfild 1064 igrexa 1064 bungalows 1064 slobodka 1065 kerry 1065 wheat 1065 mola 1065 rolf 1065 628 1065 g108 1065 c 35 1065 germano 1065 nikole 1065 raj 1065 courtland 1065 solis 1065 vaughan 1065 16 de septimbre 1065 electronic 1066 tvorchistva 1066 tala 1066 2100 1066 kardynala 1066 ostring 1066 zarichna 1066 bittencourt 1066 cherry av 1066 aachener 1066 summit st 1066 violeta 1066 maggi 1066 riba 1066 fl a1a 1067 garay 1067 1900 1067 australe 1067 beato 1067 jack 1 box 1067 rwd 1067 grady 1067 norre 1067 loteria 1067 tanqu 1067 marszalka jozefa pilsudskigo 1067 hesse 1068 anhangura 1068 panera 1068 e5 1068 smithfild 1068 naka 1068 heijn 1068 681 1068 sr 11 1068 gage 1069 galena 1069 englewood 1069 s 9th st 1069 kanjo 1069 second av 1069 buddha 1069 logging 1069 jr lu er dao ben xian 1069 saigon 1069 dannunzio 1069 puti 1070 repos 1070 schloss bg 1070 apa 1070 stanley st 1070 zhong zheng lu 1070 hwy 97 1070 arok 1070 685 1070 yu zhan luan huang yuki ze 1070 hwd 1070 ud 1070 tm 1070 przychodnia 1070 amtsgericht 1070 jembatan 1070 leonards 1070 schlossweg 1070 chang shen gao su 1070 a47 1070 hayy 1070 g204 1070 guarda 1070 memphis 1070 highland rd 1071 nga 1071 cleaning 1071 magde 1071 parma 1071 taverne 1071 olavo 1071 steve 1071 next 1072 levy 1072 espanha 1072 woodland rd 1072 dkhtr 1072 zrt 1072 588 1072 rantarata 1072 canadian national 1072 kossuth u 1072 gn rd 1073 profesora 1073 ruili 1073 harden 1073 koopirativ 1073 gladys 1073 dvur 1073 reymonta 1073 kt 1073 pakistan 1073 blasco 1074 primitive 1074 kalman 1074 white st 1074 grillhutte 1074 gracia 1074 yeuda 1075 troitsy 1075 rua rui barbosa 1075 ul voroshilova 1075 dellindustria 1075 robert bosch st 1075 paredes 1075 verizon 1075 gudang 1075 fixme 1075 bowers 1075 gdn st 1075 mhp 1075 applebees 1075 sp22 1075 pina 1076 ldera 1076 olivar 1076 animas 1076 librairi 1076 hirro 1076 iakuba 1076 1083 1076 langudoc 1077 embankment 1077 1 295 1077 pl du marche 1077 change 1077 d 940 1077 tesoro 1077 oldenburger 1077 mittlere 1077 dauphine 1077 plate 1077 wad 1078 gp 1078 silver st 1078 eleanor 1078 boxwood 1078 pesca 1078 pha 1078 aleflgrby 1078 banqu postale 1079 uyng 1079 birgarten 1079 moises 1079 sot 1079 731 1079 estanislao 1079 d 80 1079 lenox 1079 tennisclub 1079 grille 1080 grazia 1080 cappuccini 1080 vilanova 1080 check 1080 westmoreland 1080 mares 1080 sh 6 1080 tx 6 1080 newberry 1080 n 57 1080 maranhao 1080 asa 1080 proctor 1080 forest st 1080 izmir 1080 biznis 1081 hobbs 1081 nashville 1081 riad 1081 1 2 1081 sigma 1081 wexford 1081 chudotvortsa 1081 melissa 1081 mirni 1081 colby 1081 c 37 1081 stnw 1081 a35 1082 peripheriqu 1082 laleflh 1082 alter postweg 1082 civile 1082 st1 1082 zen 1082 ler 1082 300th 1082 sudbury 1082 healthcare 1082 gos 1082 helio 1082 shkola 2 1083 sh6 1083 orti 1083 miracle 1083 d 73 1083 friars 1083 ecole elementaire 1083 b 33 1083 kuo 1083 novaia pochta 1 1083 aris 1083 3801 1083 d 91 1083 jesse 1084 kruisweg 1084 dresdner st 1084 bf pl 1084 takeaway 1084 muirfild 1084 cofe 1084 westlake 1084 judith 1084 wattle 1084 mandarin 1084 tambo 1084 bridgeport 1084 lope 1085 summit av 1085 us 16 1085 varennes 1085 neuhaus 1085 senna 1085 wirzbowa 1085 fsr 1085 kolej 1086 m 06 1086 sparta 1086 potato 1086 saule 1086 liceum 1086 occidentale 1086 816 1086 amicis 1086 staszica 1086 18 36 1086 ringvej 1086 wetlands 1086 jeffrey 1086 d 612 1086 mercir 1086 berken 1086 hamel 1086 cement 1087 009 1087 guangzhou 1087 pasquale 1087 frankreich 1087 dongha 1087 pendleton 1087 v primo maggio 1087 s 8th st 1088 eglise notre dame 1088 gm 1088 chadwick 1088 materna 1088 mittelschule 1088 bord 1088 easy st 1088 g8511 1088 huis 1088 10k 1088 resources 1088 oneida 1089 natchez 1089 leighton 1089 tamaris 1089 bandira 1089 cabrillo 1089 nationalpark 1089 saudade 1089 rainer 1089 chrysler 1089 remo 1089 ingles 1089 glenmore 1089 slide 1089 alfaro 1089 ensenada 1089 gradinita 1089 us 25 1089 congregation 1090 tokaido sinkansen 1090 bucuresti 1090 vorstadt 1090 n 550 1090 belfast 1090 brookviw 1090 2020 1090 dearborn 1090 s western av 1090 gospodnia 1090 caney 1090 takko 1090 v fratelli cervi 1090 kane 1090 woda 1090 veterans memorial hwy 1090 chalets 1090 dhy 1090 r des erables 1091 782 1091 ministris 1091 elan 1091 deerwood 1091 insurgentes 1091 rowy 1091 christmas 1091 virkh 1091 planina 1091 ferre 1091 osceola 1091 ul ordzhonikidzi 1091 univirsam 1091 rp 1091 dufferin 1091 blackwell 1091 cain 1091 n 11th st 1092 canary 1092 n 420 1092 rives 1092 palomas 1092 kpr 1092 lutz 1092 boarding 1092 obstetrics 1092 cot 1092 us 285 1092 sarl 1092 premium 1092 jessica 1092 inmaculada 1093 indio 1093 halde 1093 berthelot 1093 falkenweg 1093 feliciano 1093 ancint 1093 ecureuils 1093 bianchi 1093 muhlengraben 1093 salzburger 1093 riding 1093 walefd 1094 brigitte 1094 aus 1094 dwight 1094 granitsa 1094 nacion 1094 quvedo 1094 astoria 1094 glycines 1094 longs 1094 perch 1094 qarn 1095 sp68 1095 rn12 1095 auxiliadora 1095 gagarina ul 1095 salomon 1095 lipova 1095 iwa 1095 gottfrid 1095 freirr 1095 fournil 1095 fu man namazu xian shuanghwan luan huang yuki ze 1096 compania 1096 ss 16 adriatica 1096 k 40 1096 too 1096 hillcrest av 1096 kotsiubinskogo 1096 cruce ruta 5 1096 e 10th st 1096 branden 1096 r de forge 1096 zhong shan lu 1096 k 32 1096 sparks 1096 powerline 1096 741 1096 eisen 1097 och 1097 vives 1097 eo3 1097 francis st 1097 v castello 1097 stifter 1097 jackson av 1097 boisko 1097 salah 1097 leman 1097 zakladni 1097 arruda 1097 simi 1097 kraftwerk 1098 immobilin 1098 fishermans 1098 pedregal 1098 benso 1098 jama 1098 moccasin 1098 buozzi 1098 loups 1098 qur st 1098 bata 1098 cr 20 1098 parrocchiale 1098 cowan 1098 seeweg 1099 loan 1099 e 712 1099 carnegi 1099 softbank 1099 boi 1099 navas 1099 montclair 1099 zavodska 1099 tartu 1099 batalla 1099 calabria 1099 mzrainte 1099 br 163 1099 pollos 1100 gynecology 1100 b 12 1100 komsomolskaya st 1100 kom 1100 hauser 1100 wood ln 1100 lillian 1100 bocage 1100 til 1100 chiya 1100 ludalt 1100 cairn 1100 santissima 1100 hardin 1100 taksi 1100 b 61 1101 krasnoi biloi 1101 us 99 psh 1 1101 2009 1101 lhasa bahn 1101 cristiano 1101 hugelgrab 1101 bicentenario 1101 988 1101 qingzang railway 1101 xiv 1101 al jana pawla 2 1101 piccadilly 1101 ecke 1101 gort 1102 autostrada bursztynowa 1102 lavenir 1102 ctra panamericana 1102 bromley 1102 heer st 1102 londres 1102 beograd 1102 stsw 1102 qlainh 1102 080 1102 cumbres 1103 richmond rd 1103 morley 1103 carrascal 1103 s isidro 1103 espino 1103 monks 1103 diablo 1103 gyeongbu exp n 1103 brookdale 1103 leys 1103 keskus 1103 orchard ln 1103 pee 1103 carry 1103 chaumont 1103 witt 1104 alfiri 1104 eglinton 1104 transfer 1104 wiley 1104 neumann 1104 palackeo 1104 pompirs 1104 ascaill 1104 linzer 1104 autostradale 1104 havana 1104 ardmore 1104 carolyn 1104 kiur 1105 daimler st 1105 franche 1105 bunga 1105 vis 1105 pennington 1105 pviramaraathamik 1105 munchener st 1105 nizalizhnosti 1105 li qun luan huang yuki ze 1105 prefectural 1105 pavon 1105 andriia 1105 c 27 1105 creative 1105 brossolette 1106 muang 1106 westridge 1106 825 1106 tambak 1106 jadwigi 1106 apotheek 1106 max planck st 1106 sherwood dr 1106 stewarts 1106 hydro 1106 malik 1106 bhaile 1106 qala 1106 ching 1106 narodnaia 1106 taxis 1106 yorktown 1107 roberts rd 1107 a 29 1107 mikhailovka 1107 marinho 1107 1600 1107 salefhte 1107 roost 1107 lda 1107 sil 1107 connaught 1107 sardegna 1107 kellogg 1108 gerhart hauptmann st 1108 radial 1108 klondike 1108 olivia 1108 bower 1108 149th 1108 154th 1108 cr 18 1108 patra 1108 cinemas 1109 soler 1109 gdanska 1109 kostil 1109 qingzang 1109 wills 1109 742 1109 sp29 1109 socidad 1109 univ dr 1109 physical 1109 helmut 1109 auweg 1110 jnwby 1110 cleveland st 1110 strecke 1110 riverwalk 1110 dero 1110 eiffel 1110 hwy 17 1110 boss 1110 s303 1110 starr 1110 tamiami 1110 bwlwalefr 1111 chhalefrm 1111 ishim 1111 polnocna 1111 petla 1111 langford 1111 tsb 1111 kenton 1111 matsuya 1111 realty 1111 partyzantow 1112 jewell 1112 wsi 1112 v circonvallazione 1112 gruber 1112 ww 1112 rio negro 1112 ferrovia jonica 1112 almendros 1112 russian 1112 gr rd 1112 st marys church 1113 maki 1113 zeller 1113 duran 1113 lhota 1113 caleta 1113 castelli 1113 noname 1113 modesto 1113 pamela 1113 vladimir 1113 pecheurs 1113 catholiqu 1113 jinggangao exp 1113 498 1113 antirrio ioannina 1113 skinner 1113 jinggangao 1113 cotswold 1113 steam 1114 marshall st 1114 zoll 1114 v s francesco 1114 vilela 1114 finn 1114 humboldt st 1114 dickinson 1114 farmer 1114 tiro 1114 mecanica 1114 westwood dr 1114 alhambra 1115 mira ul 1115 europcar 1115 laredo 1115 cento 1115 prazska 1115 hohle 1115 libknecht 1115 jeova 1115 eki 1115 siu 1115 676 1115 carrillo 1115 sherbrooke 1115 rodney 1115 overton 1115 amar 1115 hatchery 1116 martin luther st 1116 studis 1116 morrow 1116 enea 1116 decharge 1116 sp20 1116 755 1116 buses 1116 avtomagistrala 1116 cornelius 1116 antler 1117 280th 1117 meidoorn 1117 hlavni 1117 bikeway 1117 litoral 1117 antiqus 1117 kecamatan 1117 buro 1117 medi 1117 baix 1117 petofi u 1117 lux 1117 ceara 1117 lynwood 1117 mathiu 1117 deak 1118 amin 1118 rua f 1118 780 1118 ul turginiva 1118 badajoz 1118 a58 1118 gene 1118 pay 1118 points 1118 v firenze 1118 vintimille 1119 pyramid 1119 m 03 1119 khb 1119 voss 1119 d 906 1119 jr sanyo hauptlini 1119 ainysalef 1119 hout 1119 natali 1119 calvert 1119 tesla 1120 posilok 1120 stift 1120 mex 15 1120 kulturi 1120 xxi 1120 rt nationale 1 1120 939 1121 caribbean 1121 r du port 1121 kyoto 1121 medicina 1121 susquhanna 1121 yamazaki 1121 nile 1121 842 1121 foley 1122 anderson st 1122 ulan 1122 indipendenza 1122 cornelia 1122 rabochii 1122 guthri 1122 glenco 1122 kalna 1122 arndt 1122 otil 1123 freiit 1123 doi 1123 s202 1123 noisetirs 1123 8e 1123 153rd 1123 br 262 1123 wetering 1123 1940 1123 kohl 1123 dvt 1123 hohl 1123 tut 1124 bernmobil 1124 schleife 1124 habitat 1124 ov 1124 159th 1124 geschaftsstelle 1124 bpi 1124 neus 1125 sp34 1125 skazka 1125 candlewood 1125 d 110 1125 aleflainzyz 1125 ramada 1125 prospect av 1125 pamiatka 1125 paramount 1125 torrey 1125 mackay 1126 carters 1126 balfour 1126 7b 1126 privokzalnaia ul 1126 cezanne 1126 gillis 1126 hobart 1126 whitman 1126 campi 1126 flat cr 1126 vrouw 1126 c 28 1127 molodiozhni 1127 xiaridalg 1127 kirkland 1127 kiivskaia ul 1127 cadorna 1127 ctr rd 1127 uri 1128 tenmile 1128 bookstore 1128 pirogova 1128 frey 1128 residencias 1128 grenze 1128 sinna 1128 aleksandar 1128 hay cr 1128 samara 1128 aleflainlyalef 1128 meyers 1128 data 1129 corinth 1129 tsiolkovskogo 1129 pyhrn autobahn 1129 moltke 1129 nabrezi 1129 bel air 1129 948 1129 821 1129 anns 1129 pct 1129 dabrowa 1130 boczna 1130 anglais 1130 schmitt 1130 kelsey 1130 kastaninallee 1130 d 79 1130 jr sanyo main ln 1130 neuville 1130 rua duqu de caxias 1130 oakfild 1130 padilla 1130 galvez 1131 evangelischer 1131 cr 13 1131 winton 1131 ah45 1131 axa 1131 campbell rd 1131 houghton 1131 ianki 1131 scott rd 1131 parcare 1131 r des genets 1132 naturpark 1132 151st 1132 tossal 1132 exhibition 1132 157th 1132 haul 1132 sp32 1132 kurze st 1132 peres 1132 warren st 1133 stol 1133 cr 14 1133 portales 1133 cedars 1133 job 1133 eemaliges 1133 pkld 1133 spbu 1133 visinniaia ul 1133 greenviw 1133 hurtado 1133 hadi 1133 senor 1133 reisen 1133 pine dr 1134 hey 1134 legalite 1134 m31 1134 chuo exp 1134 ives 1134 bassett 1134 gyeongbugosokdoro 1134 indoor 1134 northviw 1134 164th 1134 amos 1134 theresa 1134 camellia 1135 777 1135 smoky 1135 sunset ln 1135 robinson rd 1135 manfred 1135 sipen 1135 winfild 1135 richnoi 1135 ul uritskogo 1135 sr 2 1135 p4 1136 bandeirantes 1136 sandalwood 1136 chartres 1136 richka 1136 suite 1136 yoshinoya 1136 733 1137 vinne 1137 advance 1137 teile 1137 stations pln 1137 balnr 1137 mly 1137 v aurelia 1137 6340 1137 normal 1137 schwabischer 1138 yu in neng cu luan huang yuki ze 1138 traversire 1138 kung 1138 herring 1138 mule 1138 textil 1138 biotop 1138 aparcabicis 1138 chak 1138 11th av 1138 fontes 1138 vallees 1139 tampa 1139 campagne 1139 kabir 1139 comedor 1139 forty 1140 possum 1140 natura 1140 lg 1140 lyndon 1140 mjtmain 1140 national rt 2 1140 e 134 1140 savage 1140 747 1140 kestrel 1140 pemukiman 1140 hodges 1140 caldas 1140 plastic 1140 hercules 1141 aw 1141 flinn 1141 memet 1141 sp24 1141 ss9 1141 italiana 1141 wrzosowa 1141 edna 1141 d 127 1141 e 571 1141 masonic 1141 allees 1141 guinea 1141 chalk 1141 wigury 1142 pitrovskogo 1142 immaculate 1142 naves 1142 bullock 1142 jazmin 1142 palmar 1142 sp28 1142 973 1142 alban 1142 stefana zeromskigo 1143 v silvio pellico 1143 longfellow 1143 e 14th st 1143 tha 1143 nadizhda 1143 548 1143 dubrava 1144 hamm 1144 shangridalg 1144 cherrywood 1144 dickens 1144 asri 1144 europaweg 1144 909 1145 linke 1145 tropa 1145 yonge 1145 clark rd 1146 papin 1146 calcada 1146 drayton 1146 thatched roof 1146 svitlaia ul 1146 frindly 1146 communautaire 1146 edgewood dr 1146 pineta 1146 saskatchewan 1146 suq 1146 kollwitz 1146 planche 1146 makhfar 1146 decathlon 1146 covert 1146 yukon 1146 rino 1147 oak rd 1147 terezinha 1147 image 1147 eastern av 1147 fleury 1147 rum 1147 caledonia 1147 petri 1147 amg 1147 ol 1147 dua 1147 zimmermann 1147 vorovskogo 1147 ubayd 1147 louisville 1147 728 1148 orpi 1148 elisa 1148 ritchi 1148 valley vw dr 1148 dominiqu 1148 31a 1148 branka 1148 luno 1148 marsala 1148 mattei 1148 belgin 1149 network 1149 autoservice 1149 boden 1149 barneage 1149 bnzyn 1149 ntt 1149 b 16 1149 longu 1149 urbana 1149 florencio 1149 bliss 1149 sauveur 1149 arenales 1149 juanita 1149 grecia 1149 seikomato 1150 av c 1150 rocky cr 1150 simao 1150 llobregat 1150 syngl 1150 kiosco 1150 gwangju 1150 umar 1150 260th 1150 cheng kun xian 1150 ili 1150 parquo 1150 muslim 1150 aldama 1151 computers 1151 cumbre 1151 charco 1151 bizet 1151 germania 1151 campsa 1151 kelley 1151 viktoria 1151 barnard 1151 wet 1152 cayetano 1152 reabilitation 1152 r haute 1152 thirs 1152 amado 1152 s av 1152 copec 1152 baita 1152 cordeiro 1152 v italia 1152 brent 1153 culver 1153 enero 1153 guys 1153 930000 1153 alefdalefrh 1153 158th 1153 color 1153 ann st 1154 tallinn 1154 smith cr 1154 adana 1154 lube 1154 22a 1154 putra 1155 hofladen 1155 kevin 1155 ikatirin 1155 guo dao1 hao xian 1155 norway 1155 acosta 1155 wqwd 1155 tifgarage 1155 obuvi 1155 dresdener st 1156 jing hu gao su gong lu 1156 futebol 1156 tampereen 1156 mojon 1156 691 1156 rua quatro 1156 siam 1156 mckinney 1156 1 wisengrund 1156 vinogradnaia ul 1156 new york state thruway 1156 jasmin 1156 sonnenhang 1156 dusty 1157 marketing 1157 n 340a 1157 mt zion church 1157 us 74 1157 dao8 1157 zrini 1158 amt 1158 palmerston 1158 pyhrn 1158 sachs 1158 creamery 1158 cycleway 1158 jungbu 1158 oakviw 1158 ticket 1158 s19 1158 fernao 1158 sulzbach 1158 einheit 1158 rqm 1158 pks 1158 denki 1158 black r 1159 bradshaw 1159 r des roses 1159 ipswich 1159 beaute 1159 republika 1159 ss7 1159 tafel 1159 1700 1159 ecolirs 1159 dobra 1159 bim 1159 co rd 2 1159 svaty 1159 feurwerhaus 1159 aptos 1159 fenton 1159 ferdinando 1160 biriozovaia ul 1160 brj 1160 cercle 1160 sta lucia 1160 northampton 1160 crss rd 1160 tes 1160 magallanes 1160 n 332 1160 cr 17 1160 kht 1160 hohen st 1160 magnus 1161 vinto 1161 fichten 1161 g 3 1161 n 12th st 1161 gordon st 1161 664 1161 palestra 1161 fairviw rd 1161 wertstoffhof 1161 011 1161 tasso 1161 stokhid 1161 zwirki 1162 ligne lyon marseille 1162 kilinskigo 1162 sim 1162 marius 1162 farm rd 1162 skolni 1162 compass 1162 vasile 1163 kiivska 1163 raccordement 1163 westfalen 1163 zhukovskogo 1163 blackhawk 1163 a 52 1163 langevin 1163 koshivogo 1163 horse cr 1163 supercenter 1163 goshen 1163 killarney 1164 warga 1164 charity 1164 cros 1164 b 299 1164 greenleaf 1164 ind dr 1164 biuro 1164 139th 1164 josipa 1164 942 1164 frazir 1164 musikschule 1165 771 1165 v palmiro togliatti 1165 tama 1165 mccormick 1165 1960 1165 g72 1165 braunschweig 1165 railway st 1165 oliga 1165 ekspriss 1165 270th 1165 disel st 1165 bkhsh 1166 preem 1166 cypress st 1166 doktora 1166 anibal 1166 jr dong bei xian 1166 taiwan 1166 rowe 1166 pionirskii 1166 roz 1167 auckland 1167 eugenia 1167 algonquin 1167 toren 1167 mascagni 1167 migul hidalgo 1167 inntal 1167 citta 1167 woodland av 1167 brana 1167 ewing 1167 dorleans 1168 bethel church 1168 giotto 1168 a 46 1168 ulster 1168 darling 1168 zox 1168 chesapeake forest lands 1168 jakarta 1168 rua 6 1168 3807 1168 sektion 1168 catawba 1168 pl de constitucion 1169 pflegeeim 1169 c d 1169 walker rd 1169 windward 1169 vespucci 1169 aleflshrqy 1169 feu 1169 fronton 1169 raposo 1169 norde 1169 burbank 1170 hillsborough 1170 taho 1170 munro 1170 809 1170 miss 1170 meredith 1170 021 1170 mijska 1171 halefjy 1171 autoroute est oust alefltryq aleflsyalefr shrq grb 1171 pagnol 1171 autostrada del brennero 1171 verlaine 1171 dow 1171 kunst 1171 militaire 1171 ss18 1172 pitir 1172 d 907 1172 naranjo 1172 gunter 1172 sturzo 1172 zhong guo dao 1172 flood 1172 v emilia 1172 lakewood dr 1172 bros 1172 pkt 1172 european 1173 c3 1173 dacha 1173 banner 1173 buxton 1173 sp41 1173 brewster 1173 tulpen st 1173 finken 1173 breton 1173 r de letang 1173 willowbrook 1173 nwr 1174 adalbert 1174 adria 1174 v gorizia 1174 walther 1174 nivskogo 1174 dezembro 1174 hillcrest rd 1174 1 495 1174 hinterm 1175 konig st 1175 adige 1175 westover 1175 srvirama 1175 shirokaia 1175 musashino 1175 homewood 1175 stratton 1175 d 125 1175 selma 1175 merle 1175 lauferstein 1176 treff 1176 linin olitsa 1176 asse 1176 hooper 1176 baia 1176 davila 1177 tanya 1177 juzgado 1177 bilain 1177 pm 1177 arodrom 1177 sorozo 1177 dormitory 1177 88n 1177 seminario 1177 s32 1177 tren 1177 gabor 1178 fairbanks 1178 peloponnese 1178 pirikristok 1178 martin rd 1178 baptista 1178 99e 1178 regione 1178 1150 1178 colinas 1178 cra 7 1178 rabat 1179 hatfild 1179 elk cr 1179 sp25 1179 pje 1179 mameli 1179 919 1179 cr 11 1179 gsm 1179 pung 1179 concha 1179 rea 1179 khristo 1179 lios 1180 camelia 1180 752 1180 gemeinschaftspraxis 1180 hameenlinnan 1180 buttu 1180 kaple 1180 flinders 1180 winslow 1180 fundacion 1180 pusto 1180 hol 1180 botanical 1180 mix 1180 nakhimova 1181 young st 1181 telegraph rd 1181 ernst thalmann st 1181 dez 1181 edmund 1181 mahdi 1181 mira st 1181 weiler 1181 rn 1 1181 143rd 1182 stadtpark 1182 shkola 1 1182 865 1182 norbert 1182 chu guang 1182 vilikoi 1182 sand st 1182 malcolm 1182 871 1182 806 1182 petronas 1183 waller 1183 robins 1183 verwaltung 1183 hideaway 1183 fc mitre 1183 bol 1183 a mkt 1184 warm 1184 hobby 1184 allen rd 1184 finanzamt 1184 shade 1184 875 1184 valdivia 1184 496 1184 mqhalef 1184 diversion 1184 departamentos 1184 winds 1185 autobidea 1185 delikatesy 1185 potosi 1185 n16 1185 solferino 1185 phelps 1185 belgica 1185 brookville lake fee title bdy 1185 outer ring rd 1185 andad 1186 ul komarova 1186 nogal 1186 r du general leclerc 1186 smpn 1186 charmilles 1186 erfurter 1186 32k 1186 limes 1186 asis 1186 matteo 1186 yucca 1186 v martiri della liberta 1186 parkovi 1186 janka 1186 larami 1186 722 1186 v isonzo 1186 central st 1186 section 2 1186 honam exp 1186 broom 1187 dorfe 1187 hatch 1187 kazakhstan 1187 neil 1187 cantabria 1187 guan yu zi dong ch dao 1187 colin 1187 blaine 1187 alte post st 1187 eiken 1187 kingsbury 1187 doire 1187 ramo 1187 excelsior 1187 montauban 1187 seit 1187 holiness 1187 panoramica 1187 maso 1187 fria 1188 conception 1188 peral 1188 w 9th st 1188 a29 1188 coldwater 1188 claus 1188 veronica 1188 inland 1188 bone 1189 lance 1189 kennedy rd 1189 1c 1189 vov 1189 fournir 1189 r notre dame 1189 hinterer 1189 perrin 1189 sr 1 1189 montpellir 1189 maiden 1190 greenbelt 1190 haines 1190 seaside 1190 brandywine 1190 papeleria 1190 enfants 1190 ministro 1191 ep2 1191 pivo 1191 cy 1191 jonathan 1191 winthrop 1191 patriot 1191 cr dr 1191 wholesale 1191 744 1191 murirs 1191 kama 1191 cele 1192 loco 1192 726 1192 dantas 1192 br 230 1192 monastyr 1192 hartmann 1192 oaklands 1192 pomona 1192 nido 1193 hillsboro 1193 novara 1193 kumano 1193 hays 1193 fontan 1193 vespucio 1193 salta 1193 hri 1193 yellowstone 1193 galloway 1193 arias 1193 yainqb 1194 768 1194 townhomes 1194 a 33 1194 premire 1194 nives 1194 rbyn 1194 clay st 1194 kommunarov 1194 mccoy 1194 nz 1194 plantes 1194 einaudi 1195 los pinos 1195 zyx 1195 moore st 1195 bia 1195 apostol 1195 bethesda 1195 lagir 1195 work 1195 bacon 1195 dadao 1195 gem 1195 zahradni 1196 boot 1196 johnson cr 1196 old mill rd 1196 minor 1196 ravin 1196 clemens 1196 ainlyalef 1196 neo 1196 nenni 1196 feria 1196 azinda 1197 old us 99 1197 holloway 1197 ringweg 1197 hera 1197 us 277 1197 palestine 1197 blancas 1197 indianapolis 1197 loi 1198 whiskey 1198 face 1198 nouveau 1198 fell 1198 moonlight 1198 us 91 1198 asema 1198 b 55 1198 neuapostolische 1199 haarstudio 1199 transportnaia 1199 cairoli 1199 avtomobilnaia 1199 cub 1199 kinderhaus 1199 canadian r 1199 ets 1199 hackberry 1199 risco 1200 maddalena 1200 garni 1200 bori 1200 australia post 1200 mint 1200 mink 1200 barthelemy 1200 d 74 1200 v giovanni xxiii 1200 lhasa 1200 alligator 1200 sobiskigo 1201 tercera 1201 sheraton 1201 baird 1201 k 39 1201 dachi 1201 hmzte 1201 1400 1201 rua sete de setembro 1201 k 28 1202 irtysh 1202 bibliothek 1202 ribas 1202 eldridge 1202 selo 1202 beatrice 1202 longhai ln 1202 elmhurst 1202 menez 1202 collingwood 1202 beke u 1202 festival 1202 carbon 1202 657 1203 024 1203 pecos 1203 eastside 1203 yeni 1203 buonarroti 1203 viktoriia 1204 e 9th st 1204 mid rd 1204 schutzhutte 1204 donizetti 1204 yeongdong exp 1204 westautobahn 1204 bnk 1204 newlands 1204 ulmer 1204 ettore 1204 cr 6 1204 quintino 1204 neubau 1204 kurpark 1204 bell st 1204 brake 1204 baren 1205 laurel st 1205 vilika 1205 qimmat 1205 maha 1206 higura 1206 viadotto 1206 mulberry st 1206 d400 1206 146th 1206 gino 1206 ames 1206 g210 1207 e 23 1207 holbrook 1207 pa 8 1207 hilda 1207 buchhandlung 1207 us 33 1207 holunderweg 1207 d 123 1207 cardiff 1207 springwood 1207 modena 1208 sp18 1208 klinichiskaia 1208 berkley 1208 cliffs 1208 cindy 1209 sp30 1209 kompleks 1209 t3 1209 justin 1209 krug 1209 sparda 1209 arlindo 1209 traffic 1209 communications 1209 rampa 1209 lug 1210 av de liberation 1210 999 1210 saline 1210 maximo 1211 shmidta 1211 raintree 1211 metsa 1211 023 1211 japan 1211 aan 1212 banana 1212 carls 1212 rit 1212 rosal 1212 coma 1212 oddzial 1212 lewis st 1212 luhli 1212 northridge 1213 lincoln rd 1213 udine 1213 judas 1213 g316 1213 serenissima 1213 rozana 1213 162nd 1213 shywy 1213 single 1213 quit 1213 b 22 1213 beal 1213 stall 1214 co op 1214 halfway 1214 us 65 1214 jj 1214 sylvia 1214 nuo 1214 rupert 1214 lavoisir 1214 vv 1215 elms 1215 lake av 1215 akazinweg 1215 akademi 1215 shalom 1215 hokkaido 1215 haas 1215 cedarwood 1216 belfort 1216 bratskaia 1216 reichenbach 1216 678 1216 sp26 1216 340a 1216 partizan 1216 jr ji shi xian 1216 scarborough 1217 section 1 1217 gall 1217 vinogradnaia 1217 goodman 1217 dellinfanzia 1217 mistsivogo 1217 pl de espana 1217 bermuda 1218 harz 1218 582 1218 fif 1218 ivanho 1218 tirgarten 1218 hwy 11 1218 v lombardia 1218 breitscheid 1218 d 108 1218 595 1218 gyeongbuseon 1218 boise 1218 piana 1219 test 1219 koltso 1219 tsarkva 1219 conselheiro 1219 maldonado 1219 5 de mayo 1220 3806 1220 labrador 1220 g105 1220 quad 1220 abou 1220 first av 1220 cuartel 1220 touche 1220 montello 1220 stan 1220 811 1220 laluan 1221 padua 1221 willow dr 1221 cerrillo 1221 kerkweg 1221 k 35 1221 bni 1221 schron 1222 inglewood 1222 w 10th st 1222 stiftung 1222 jewel 1222 khlib 1222 mensa 1223 dickson 1223 vostok 1223 oso 1223 crockett 1223 avtomagistral tiugoku 1223 zags 1223 crestviw dr 1223 chrobrego 1224 hannoversche 1224 n 85 1224 g107 1224 r 21 1224 rua santo antonio 1224 d 83 1224 aula 1224 pizzo 1224 longhai 1224 chopina 1224 adobe 1224 rua sao jose 1224 adi 1224 cerca 1224 peralta 1224 zhun 1224 hall st 1224 buttes 1224 woodford 1225 national rt 1 1225 chiang 1225 schwalbenweg 1225 vial 1225 lagerhaus 1225 carrefour express 1225 185th 1225 millpond 1225 cornerstone 1226 tiberina 1226 dosso 1226 muir 1226 schreinerei 1226 douro 1226 912 1226 polova 1227 chacabuco 1227 brandy 1227 heuweg 1227 tiugoku 1227 trujillo 1227 beuken 1227 promyshlinnaia ul 1227 rankin 1227 creuse 1227 evzoni 1227 brahms 1228 932 1228 qinhuangdao 1228 klang 1228 gifhorn 1228 cyclable 1228 poole 1228 vali 1228 aleflkbyr 1228 crazy 1228 serenity 1228 936 1229 156th 1229 gedenkstein 1229 carmine 1229 natwest 1229 bobs 1229 sadovaya 1229 d 66 1229 visinniaia 1229 engen 1230 fest 1230 479 1230 barrel 1230 country club rd 1230 gables 1230 m 1 1230 natal 1231 k 24 1231 e 462 1231 hameen 1231 carpet 1231 galen 1231 brgy 1231 20th st 1231 569 1232 bastion 1232 leagu 1232 rejon 1232 ayt 1232 barrera 1232 escondido 1232 shoppers 1232 cornish 1232 cowboy 1232 blick 1232 guildford 1232 bahnhofs 1232 salerno 1232 schlosspark 1233 medica 1233 after 1233 oleander 1233 laden 1233 weisser 1233 lay 1233 avtomagistral sanhardio 1233 caserne 1234 aleman 1234 carleton 1234 chugoku exp 1234 fin 1234 geral 1234 chat 1234 trans canada 1234 822 1234 g80 1235 881 1235 hide 1235 feeder 1235 arzteaus 1235 garner 1235 rowland 1236 argos 1236 lecia 1236 bachgasse 1236 carlson 1236 mis 1236 r lenine 1236 sura 1236 equstrian 1236 basso 1236 dollar general 1236 innovation 1236 komsomolskaya 1237 hoang 1237 ferris 1237 ginsterweg 1237 struma 1237 summerfild 1237 hokurikuzidousiyadou 1237 nose 1237 maza 1237 rochelle 1237 trassa 1237 pradera 1237 ceska sporitelna 1238 villirs 1238 quito 1238 ykhm 1238 johannis 1238 carnes 1238 byvshii 1238 escolar 1239 nalefsr 1239 grant av 1239 barley 1239 ohg 1239 chamber 1239 hongha 1239 hinden bg st 1239 pmp 1239 alvear 1239 goodwill 1240 hen 1240 moser 1240 bader 1240 586 1240 qalefain 1240 fraga 1240 huit 1241 direccion 1241 934 1241 nixon 1241 skyway 1241 class 1241 av charles de gaulle 1241 mains 1241 b7 1241 venice 1241 catfish 1241 kupaly 1241 newbury 1242 swbr 1242 taunus 1242 r des champs 1242 appleton 1242 hugel 1242 ojo 1242 story 1242 lauri 1242 orchard rd 1242 rand 1242 krishna 1242 arma dei carabiniri 1242 lorenz 1242 germany 1242 bourbon 1242 shrtte 1242 rahman 1242 chiropractic 1242 sapporo 1242 marii konopnickij 1242 basses 1242 sou 1243 kosmodimianskoi 1243 rakoczi u 1243 brea 1243 venustiano 1243 err 1243 walnut av 1243 bilinvistbank 1243 ncleo 1243 swedbank 1243 hadley 1243 dalam 1243 belem 1243 us 78 1244 neve 1244 farmhouse 1244 659 1244 ivo 1244 butterfild 1244 735 1244 gould 1244 saind 1244 rosalia 1244 jr shan in xian 1244 eliza 1244 sr2 1244 canas 1245 rozen 1245 sviaznoi 1245 iminia 1245 gasolinera 1245 e 29 1245 tepe 1245 674 1245 molle 1245 pyrgos 1245 montenegro 1246 moskvy 1246 fortunato 1246 ochoa 1246 gk 1246 grenz 1247 ul maksima gorkogo 1247 a39 1247 gaidara 1247 fonda 1247 k 34 1247 n 19 1247 cloverdale 1247 d 114 1248 michelet 1248 fausto 1248 maximilian 1248 kasseler 1248 oblastnaia 1248 transports 1248 salao 1249 hh 1249 mahanaviramadaviramara raaajaviramamaaaraviramagavirama 1249 raaajaviramamaaaraviramagavirama 1249 r nationale 1249 honduras 1249 qunha 1249 ved 1249 szpital 1249 dacquisto 1250 ch dexploitation 1250 daang 1250 vtb 1250 katherine 1250 setany 1250 oficial 1250 potraviny 1250 somme 1250 polizia 1250 b 49 1250 krylova 1250 tomei kosoku doro 1251 esprit 1251 ah1 ah2 1251 campbell st 1251 toumeikousokudouro 1251 petro canada 1251 kil 1251 nurnberger st 1251 college av 1252 asociacion 1252 amendola 1252 emt 1252 dukes 1252 pinsons 1252 zufahrt 1252 alefljzalefyr 1252 elvira 1252 heinrich heine st 1252 mingo 1252 kur 1253 portes 1253 bremer st 1253 vest 1253 lazaro cardenas 1253 kolasa 1253 daevsviramathaaan 1253 bur 1253 victoire 1253 vicente gurrero 1253 vasilia 1253 valley dr 1253 whitetail 1253 civico 1254 haltestelle 1254 cuarto 1255 montevideo 1255 blackwood 1255 mikolaja kopernika 1255 covenant 1255 estados 1255 veracruz 1255 obrin 1255 hawks 1255 burgers 1255 energi 1255 tobacco 1256 darlington 1256 rot 1256 manzano 1256 bogota 1256 guynemer 1257 ciro 1257 gurin 1257 shelley 1257 gorges 1257 moret 1257 stonewall 1257 rosselli 1257 shmalefly 1257 witosa 1257 717 1257 brennerautobahn 1257 vet 1257 valentino 1258 keisei 1258 d 72 1258 altamira 1258 corn 1258 sally 1258 sp16 1258 aleflslalefm 1258 bonhoffer 1259 sapphire 1259 wadsworth 1259 vigo 1259 shinensananxanvinenvinin 1259 drop 1259 duval 1259 miliu 1259 19th st 1260 anker 1260 ul lunacharskogo 1260 hana 1260 europa ln 1260 wisniowa 1260 copeland 1260 k 37 1260 g205 1261 ellsworth 1261 bombeiros 1261 e washington st 1261 lisova 1261 miditsinskii 1261 dub 1261 kruidvat 1261 rijn 1262 glavni 1262 n vi 1262 r gambetta 1262 violetas 1262 grandi 1262 665 1262 cargo 1262 xi chuan 1262 pura 1262 wog 1263 chugoku 1263 fish cr 1263 natsionalni 1263 haz 1263 rua tiradentes 1263 parkovka 1263 hampden 1263 canadian pacific railway 1263 kubitschek 1264 bilarus 1264 friseursalon 1264 conseil 1264 flavio 1264 vancouver 1264 schwarze 1265 ganges 1265 physiotherapi 1265 istasyonu 1265 basisschool 1265 hyatt 1265 xino 1265 irun 1265 boyer 1265 alden 1266 sand cr 1266 niccolo 1266 estacio 1266 dix 1266 chang shen gao su gong lu 1266 r du ctr 1266 cr 15 1267 nazareth 1267 michelle 1267 672 1267 shire 1267 557 1267 719 1268 roza 1268 v giacomo puccini 1268 rema 1268 taurn autobahn 1268 dum 1268 blazes 1268 first presbyterian church 1268 cornelis 1268 wolfs 1268 kentucky frid chicken 1268 2200 1269 bahnweg 1269 kauppa 1269 bana 1269 141st 1269 raffallo 1269 karlsruhe 1269 liszt 1269 demotiko 1269 furte 1269 natur 1269 kepler 1269 prata 1270 uchastkovi 1270 entree 1270 r des chenes 1270 andrzeja 1270 vazquz 1270 lepine 1270 hungry 1270 alexander st 1270 133rd 1270 aleflalef 1270 bogdana khmilnitskogo vulitsia 1271 862 1271 hau 1271 danile 1271 eloy 1271 passos 1271 api 1271 sanyo exp 1271 mittlerer 1271 scotch 1271 clements 1271 monet 1271 maritta 1272 bluffs 1272 721 1272 cr 25 1272 d 109 1272 mesnil 1272 turnberry 1272 loyola 1273 chili 1273 w washington st 1273 walkers 1273 margaritas 1273 sp46 1273 d 81 1274 adriano 1274 v staz 1274 stokes 1274 ligne de paris est a mulhouse ville 1274 ret 1274 chevalir 1274 hamra 1275 geschwister scholl st 1275 cartagena 1275 foothill bd 1275 straight 1275 spoldzilczy 1275 phong 1275 greenbrir 1275 conti 1275 buchen st 1275 bramble 1275 fildstone 1276 ober df 1276 altstadt 1276 unidos 1276 pia 1276 921 1276 monde 1276 especial 1276 biala 1276 ira 1276 talweg 1276 holm 1276 selkirk 1276 sophia 1277 rettungswache 1277 pizarro 1277 mahanaviramadaviramara 1277 kastanje 1277 yosemite 1277 aubin 1277 bruder 1277 708 1277 applewood 1278 sabor 1278 timothy 1278 konrad adenaur st 1278 gorodishchi 1278 yoga 1278 marquis 1278 hushan exp 1278 magasin 1279 murphys 1279 grenzstein 1279 seibu 1279 030 1279 r basse 1279 planes 1279 brunner 1280 valverde 1280 vance 1280 682 1280 girona 1280 931 1280 vicinal 1280 borde 1281 jovana 1281 barnett 1281 romeo 1281 forcella 1281 naranjos 1281 a21 1281 bonanza 1281 garza 1281 kirch bg 1282 fabrik st 1282 walls 1282 southampton 1282 divizii 1282 bruck 1282 copy 1282 evergreen dr 1282 povorot 1282 daira 1282 midland main ln 1282 verdes 1283 menor 1283 dab 1283 ufficio 1283 sion 1283 citgo 1283 magnolia av 1283 turkey cr 1283 bracken 1284 v s martino 1284 jetty 1284 camacho 1284 brandao 1284 hq 1285 ober df st 1285 war memorial 1285 khawr 1285 1500 1285 hiawatha 1285 olivo 1285 antunes 1285 grimm 1286 ferraz 1286 parrocchia 1286 gurion 1286 n 13 1286 duomo 1286 transporte 1286 sancho 1286 romerweg 1286 dz 1287 ulisses 1287 kleinen 1287 schmalspurbahn 1287 a 28 1287 mahogany 1287 gala 1287 rua 5 1287 pressoir 1287 godoy 1288 593 1288 otis 1288 wilde 1288 558 1288 tommy 1288 sp23 1288 canyon cr 1289 waldeck 1289 mikoli 1289 haller 1289 ufer st 1289 eloi 1289 frunze 1289 st martin 1290 wethouder 1290 geneve 1290 periphereiake 1290 thanon 1290 e 73 1290 oxford st 1290 d 77 1290 v xxv aprile 1290 nen 1291 sana 1291 knolls 1291 fairgrounds 1291 gonzalo 1291 tate 1291 wschodnia 1291 fuha 1291 dirk rossmann gmbh 1292 letoile 1292 savoi 1292 dyer 1292 gra rd 1292 penon 1292 magistrale 1292 calumet 1292 tangno 1292 clot 1292 worthington 1293 mcintosh 1293 sp14 1293 hamlet 1293 hohlweg 1293 cia 1293 toilets 1293 sosnovaia ul 1293 sundance 1293 sewage 1294 rn1 1294 piccolo 1294 ves 1294 turati 1294 caminos 1294 mex 45 1294 dc 1294 cyr 1295 weingarten 1295 tandir 1295 alencar 1295 franklin av 1295 salazar 1295 plaing 1295 chica 1295 augsburger 1296 pfarrgasse 1296 av 1 1296 k 36 1296 shenandoah 1296 ep3 1296 v nazario sauro 1296 comanche 1296 antonigo 1297 c iglesia 1297 lt cr 1297 oswald 1297 trudovaia ul 1297 olmos 1298 lacerda 1298 tabakwaren 1298 igarape 1298 rua e 1298 152nd 1298 overpass 1298 juice 1298 suzanne 1299 d 107 1299 glstalefn 1299 mag 1299 campillo 1299 sebastin 1299 gornaia 1299 v sandro pertini 1299 patricio 1299 lavalle 1300 britannia 1300 18th st 1300 us 270 1301 ano 1301 ox 1301 millbrook 1301 sporting 1301 volodimira 1301 lesni 1301 776 1301 deodoro 1301 sportheim 1301 constantin 1301 d 75 1302 sau 1302 hernan 1302 southfild 1302 vella 1303 jazirat 1303 trikala 1303 sac 1303 woodruff 1303 habib 1303 cunca 1303 a 14 1303 grunen 1303 fix 1303 av b 1304 olden 1304 gian 1304 st 1 1304 toukaidousinkansen 1304 kwik 1304 547 1304 heras 1304 bara 1305 rinok 1305 coiffeur 1305 biagio 1305 barracks 1305 nook 1305 fairviw av 1305 aout 1305 away 1306 foothills 1306 hushan 1306 movistar 1306 dore 1306 logis 1306 jeunes 1307 acqua 1307 maritsa 1307 jal 1307 v armando diaz 1307 conad 1307 3810 1307 ambroise 1307 stanica 1307 pellico 1307 lira 1307 nextbike 1307 sheppard 1307 song shan zi dong ch dao 1308 materska 1308 eje 1308 tina 1308 monterrey 1308 zhiloi 1308 hillsdale 1308 skol 1308 allen st 1308 tur 1308 shree 1308 tia 1309 bdo 1309 jericho 1309 sent 1309 travnia 1309 mision 1309 iuriia 1309 velke 1309 akadimiia 1309 tyre 1310 nike 1310 kap 1310 jb 1310 spital 1310 watch 1310 dajeon 1310 kb 1310 ripley 1311 724 1311 peck 1311 barbe 1311 ywlalef 1312 moskovskogo 1312 brennerbahn 1312 d 71 1312 v della repubblica 1312 abi 1312 woodviw 1312 luci 1312 oakmont 1312 ware 1312 rohrbach 1313 elm av 1313 775 1313 ch des vignes 1313 644 1313 private rd 1313 douglas st 1313 danube 1313 mtrio 1313 r du four 1313 7e 1313 gewerbe st 1313 175th 1314 restaurace 1314 brdo 1314 kurgan 1314 fleur 1314 wilcox 1314 cristal 1315 fougeres 1315 cr 9 1315 070 1315 sportivni 1315 s17 1315 shepherds 1315 vela 1316 d 106 1316 us 322 1316 obecni 1316 yvsp 1316 pko bp 1316 kha 1316 games 1316 ainbyd 1316 pop 1316 galaxy 1317 eisenbahn st 1317 palomino 1317 busbahnhof 1317 souto 1317 sociale 1317 bag 1317 s20 1317 1 85 1318 r emile zola 1318 g1501 1318 marias 1318 reception 1318 tobaccoland 1318 mikes 1318 stroitilstvo 1318 cedar dr 1318 arodrome 1319 lins 1319 atkinson 1319 wellington st 1319 mnkhm 1319 cheshire 1319 albergu 1319 gade 1319 singel 1319 250th 1319 rouges 1319 e 48 1319 iran 1319 galli 1320 592 1320 corrintes 1320 stables 1320 omer 1320 columbine 1320 916 1320 jungbunaryuk exp 1320 rua tres 1321 espresso 1321 hammock 1321 olitsa 1321 iubiliini 1321 e 65 e 71 1321 barrire 1321 jeux 1321 porsche 1321 technologis 1321 hop 1321 valli 1322 chasse 1322 sacre 1322 grain 1322 fi 1322 bosqut 1322 mungammad 1323 sridnii 1323 kuh 1323 1006 1323 sigfrid 1323 bastos 1323 pl de gare 1323 sus 1323 blenheim 1324 jager st 1324 tischlerei 1324 map 1324 naturreservat 1324 bayside 1324 regiment 1324 southwood 1324 mobility genossenschaft 1324 k 22 1324 landkreis 1325 nanjiang 1325 rokiv 1325 bachweg 1325 pawnee 1325 odakyu 1325 rathaus pl 1326 jensen 1326 vinna 1326 lauderdale 1326 avtostantsiia 1326 ferree 1326 clean 1326 a 30 1326 c 24 1326 siviro 1326 647 1326 18a 1326 branche 1326 broughton 1326 gongno 1326 d 111 1326 cummings 1326 sabino 1327 vaughn 1327 sabine 1327 sig 1327 gioacchino 1327 belgrave 1327 edwin 1327 aubepines 1327 tous 1327 java 1327 tecnologico 1327 industrille 1327 gong chuan 1328 s 7th st 1328 yamaha 1328 yamada 1328 olmo 1328 kosumo 1328 mang 1329 sachsen 1329 etorb 1329 stonebridge 1329 br 153 1329 depot st 1329 chamberlain 1329 willow rd 1329 derry 1329 hereford 1329 cork 1329 fischbach 1329 simos 1329 764 1329 francesa 1329 cell 1330 expo 1330 aquino 1330 mep 1330 msrf 1330 thoma 1330 flaminia 1330 ulm 1331 minigolf 1331 n 120 1331 chateaubriand 1331 landgraben 1331 geronimo 1331 braurei 1331 dump 1331 colfax 1331 comprensivo 1331 herren 1331 przemyslowa 1332 dole 1332 chisholm 1332 cyber 1332 rolland 1332 emery 1332 kelurahan 1332 1 59 1332 monkey 1332 rwdte 1333 correo 1333 ridgecrest 1333 parkland 1333 ria 1333 cordillera 1333 michigan av 1333 dao yang zi dong ch dao 1334 gibbs 1334 pasa 1334 budionnogo 1334 539 1334 bonito 1334 b 62 1334 d 938 1334 encina 1334 cascades 1334 barbers 1335 e 10 1335 663 1335 666 1335 9th av 1335 blackstone 1335 d010 1335 drift 1336 105 01 1336 001000 1336 verlengde 1336 trida 1336 roun 1336 likarnia 1336 rn3 1336 b 15 1337 busway 1337 dorozhnaia ul 1337 hohe st 1337 cody 1337 tsing 1337 gobirno 1337 professeur 1337 costco 1337 calgary 1337 weir st 1337 sucker 1337 esquina 1337 barco 1337 service rd 1337 viddilinnia 1338 buckley 1338 seb 1338 dla 1338 rujm 1338 a57 1338 dabrowskigo 1338 cara 1338 4b 1339 christoph 1339 shaaale 1339 165th 1339 franko 1339 figuira 1339 lassomption 1339 iskusstv 1339 ogrod 1339 mamedialtaasattaiungaasat 1340 mister 1340 tremont 1340 korn 1340 emanul 1340 a 43 1340 addition 1340 halefj 1340 673 1340 narrows 1340 ei 1340 pala 1340 mens 1340 r de paris 1340 rockwood 1340 stones 1341 shhrkh 1341 centar 1341 tulul 1341 parkweg 1341 772 1341 bilinskogo 1341 poludniowa 1341 garazh 1341 rijksstraatweg 1341 valladolid 1342 aleflhsyn 1342 draw 1342 visitors 1342 budalg 1342 carmelo 1342 kwai 1342 faure 1342 a 40 1343 luisen 1343 hortensias 1343 italiano 1343 kathe 1343 ul kosmonavtov 1343 mcgregor 1343 b 180 1344 d 61 1344 565 1344 pulperia 1344 pineviw 1344 libknikhta 1344 pedras 1345 aur 1345 divino 1345 vilikii 1345 pine av 1345 greek 1345 cass 1345 buchholz 1345 zamkowa 1345 sagewerk 1346 utja 1346 atalaya 1346 schneise 1346 k 26 1347 dresdener 1347 cng 1348 spr 1348 b 13 1348 victoria av 1348 beverley 1348 evangelista 1348 alexandru 1348 equipment 1349 csx 1349 n34 1349 winkler 1349 tra 1349 goods 1349 teich st 1349 mathias 1350 folly 1350 magistralnaia 1350 tucuman 1351 listopada 1351 contra 1351 sp21 1351 larry 1351 k 19 1351 francisco 1 madero 1351 belgi 1351 poppy 1351 cassa 1351 newark 1351 rabin 1351 centenary 1351 bangkok 1352 joli 1352 crcs 1352 forstweg 1352 thornhill 1352 sport ln 1352 basilio 1352 556 1352 edelweiss 1352 nabirizhna vulitsia 1352 madeira 1352 santandrea 1352 chaco 1353 linares 1353 hamilton st 1353 stable 1353 sicula 1353 r de foret 1353 kobe 1353 bezerra 1353 b 31 1353 pisa 1354 jungbunaryuk 1354 rontgen 1354 nora 1354 remedios 1354 lavanderia 1354 137th 1354 5b 1355 forskola 1355 ch de halage 1355 bps 1355 laureles 1355 15a 1355 duke st 1356 schleuse 1356 jones st 1356 quattro 1356 prime 1356 auchan 1356 arundel 1356 sp7 1356 a 62 1357 sagrado 1357 navigation 1357 mercedes benz 1357 denominacao 1357 n 88 1357 tereza 1357 d 102 1358 canal st 1358 condominiums 1358 138th 1358 690 1358 n13 1358 jonas 1358 dene 1359 di1 1359 plaisance 1359 catering 1359 westchester 1359 us 63 1360 agriculture 1360 turo 1360 tripoli 1360 b 28 1360 yong tai wen gao su 1360 ludwika 1360 palmiro 1361 578 1361 huta 1361 consulate 1361 sahara 1362 canteen 1362 766 1362 nach 1362 955 1362 a 27 1362 walker st 1362 emlekmu 1362 topolowa 1362 hansa 1363 xiong ye ji dao guo dao42 hao 1363 marlin 1363 60k 1363 brauhaus 1363 talleres 1363 ore 1363 gosudarstvinnoi 1363 dorfgemeinschaftshaus 1363 arthur st 1363 ingram 1363 memorial dr 1363 hugh 1363 kemp 1363 indigena 1363 munchener 1364 tax 1364 cr 10 1364 k 31 1364 nic 1364 banos 1364 shuai 1364 laurentius 1364 yokosuka 1364 alte land st 1364 newell 1364 e 81 1364 928 1364 superstore 1364 kundenpark 1365 tesco express 1365 d 116 1365 narciso 1365 lasalle 1365 clock 1366 crabtree 1366 brittany 1366 everest 1366 unidas 1366 albans 1366 v giuseppe di vittorio 1366 barranca 1366 caribe 1366 22k 1366 markham 1367 governors 1367 thompson rd 1367 n 9th st 1367 reifen 1367 quinn 1367 saba 1367 horacio 1367 pou 1367 rsc 1367 drew 1368 cilo 1368 bohaterow 1368 jason 1368 zilina vulitsia 1368 obrazovatilnoi 1368 custer 1368 us 290 1368 kiwi 1369 barroso 1369 merritt 1369 kirkevej 1369 ramona 1369 evangelisches 1369 erenmal 1369 eis 1369 hochschule 1369 mubles 1370 e 37 1370 haim 1370 damm st 1370 s26 1370 nicaragua 1370 v 25 aprile 1370 seri 1371 twenty 1371 760 1371 kinotiatr 1371 lancin 1371 812 1371 albino 1371 762 1371 anny 1372 ul lomonosova 1372 farm trk 1372 biograd bar 1372 pertamina 1372 hangzhou 1372 deichmann 1372 c major 1372 sena 1372 courtney 1373 trap 1373 kant st 1373 dirt 1373 a 67 1373 hamilton rd 1373 stomatologiia 1373 bergeri 1374 distribuidor 1374 armii krajowej 1375 m 14 1375 mwsalef 1375 riverviw dr 1375 tomahawk 1375 coubertin 1375 ah47 1375 fillmore 1375 raadhuis 1375 elite 1376 polyvalente 1376 706 1376 lpg 1376 hiking 1377 brod 1377 walsh 1377 rua 4 1377 forest av 1377 has 1377 a 81 1377 r du calvaire 1377 e 8th st 1377 chick 1377 vallone 1378 balefb 1378 dd 1378 tanah 1378 esther 1378 forest dr 1378 jalur 1378 nin 1378 palos 1378 lorne 1378 v s rocco 1378 gminy 1378 v risorgimento 1378 yongtaiwen 1379 frankfurt 1379 chacara 1379 grape 1379 philips 1379 amigos 1379 yongtaiwen exp 1379 w av 1380 calera 1380 observatory 1380 lininskii 1380 nation 1380 ditii 1380 jiu zhou zi dong ch dao 1380 scala 1380 massey 1380 tras 1380 922 1380 wilbur 1380 e 119 1381 ggmbh 1381 ber 1381 frenes 1381 n 12 1381 comunitario 1381 d 120 1381 stark 1381 lluis 1381 rising 1382 rockwell 1382 alte st 1382 sandy cr 1383 labreuvoir 1383 resource 1383 diane 1383 fista 1383 moskovskoi 1383 147th 1383 cherry ln 1383 graben st 1383 hal 1384 bola 1384 theodor heuss st 1384 us 460 1384 knollwood 1384 halden 1384 a bg 1384 seebach 1384 parafia 1384 thunderbird 1384 marken 1385 hare 1385 sophi 1385 645 1385 calvo 1385 mendonca 1385 amy 1385 rb 1385 weissen 1385 d 118 1385 chocolate 1386 a 93 1386 brativ 1386 budinok 1386 gibraltar 1386 av central 1386 plas 1386 yrvshlym 1386 osidla 1386 departamento de educacion 1386 optika 1387 yat 1387 ca 82 1387 laszlo 1387 fornace 1387 rota 1387 537 1387 shek 1387 lahn 1388 chatsworth 1388 ata 1388 silk 1388 478 1388 guatemala 1389 macil 1389 spinney 1389 toros 1389 friden st 1389 hartley 1389 pacific coast hwy 1389 ely 1389 citizens 1390 culturel 1390 racecourse 1390 ct st 1390 142nd 1390 strandvej 1390 tay 1390 moskovskii prospikt 1391 aro 1391 zal 1391 siyoukasen 1392 linn 1392 723 1392 westring 1392 c 26 1392 us 278 1392 k 25 1392 boca de incendio 1392 shokasen 1392 melchor 1393 f1 1393 zhiliznodorozhni 1393 brook st 1393 vian 1393 chirnyshivskogo 1393 tulpen 1393 a90 1393 arno 1394 d st 1394 mitsubishi 1394 luiza 1394 r2 1394 iglesias 1394 naar 1394 rosemont 1394 unger 1394 anchita 1394 fairmount 1395 segovia 1395 edward st 1395 autostrada del mediterraneo 1395 oakwood dr 1395 cret 1396 s101 1396 skovvej 1396 aaron 1396 vaart 1396 suki jia 1396 bake 1396 d 105 1396 xiao fang shuan 1396 shakespeare 1396 sl 1397 hp 1397 fang huo shui cao 1397 holden 1397 715 1397 p3 1397 aliksandrovka 1397 knapp 1397 country ln 1397 fridhofskapelle 1397 ljubljana 1397 10th av 1397 baumgarten 1398 coconut 1398 revere 1398 immanul 1398 da gu chuan 1398 augustine 1398 ukraini 1399 jefferson av 1399 bruxelles 1399 n11 1399 begonia 1399 c c 1399 olive st 1400 a19 1400 dennys 1401 marigold 1401 johanna 1401 svitlaia 1401 leonhard 1401 butternut 1402 garfild av 1402 kapolna 1402 botschaft 1402 charmes 1402 d 68 1402 kleingartenanlage 1402 c nuva 1403 jozsef attila u 1403 beit 1403 cascada 1403 n 10th st 1403 skyviw 1403 weidenweg 1403 coteaux 1404 bus stop 1404 r des vergers 1404 b4 1404 d 112 1404 bagno 1405 pappelweg 1405 chou 1405 restoran 1405 qarat 1405 appia 1406 nordring 1406 krolowej 1406 debussy 1406 force 1406 jw 1406 dan in luan huang yuki ze 1406 b 101 1407 stay 1407 dalefnshgalefh 1407 cougar 1407 s28 1407 crab 1407 aleflmlk 1407 percorso 1408 wc 1408 nor 1408 ost st 1408 snowmobile 1408 alefsmalefainyl 1408 voluntarios 1408 horno 1408 stuttgarter st 1408 moses 1408 karditsa 1409 ceinture 1409 poirir 1409 ok 3 1409 pzta 1409 highviw 1409 barbusse 1409 g85 1410 vtb24 1410 kosoku 1410 decker 1410 ginger 1410 stil 1410 sp13 1410 bozhiii 1410 russkii 1410 jarvis 1410 mair 1410 torg 1411 oiseaux 1411 fry 1411 vocational 1411 kolner st 1411 aba 1411 safety 1411 230th 1411 dreve 1412 mollevej 1412 avnw 1412 786 1412 toni 1412 leao 1412 olivirs 1412 ligne de paris a strasbourg 1412 renoir 1412 prevert 1412 mast 1412 538 1413 westbury 1413 fire hydrant 1414 helens 1414 obregon 1414 mole 1414 penny mkt 1414 greystone 1415 grays 1415 pasadena 1415 larchenweg 1415 134th 1415 nider 1415 otero 1415 prospekt 1415 155th 1415 om 1416 mesto 1416 kostel 1416 jozef 1416 agzs 1416 brewer 1416 heide st 1416 niuw st 1417 partner 1417 nils 1417 vivir 1417 castellana 1417 ofis 1418 sp6 1418 a1a 1418 voinam 1418 elkhorn 1418 utah 1418 tamar 1419 uber 1419 ry 1419 e 56 1420 quiroga 1420 817 1420 call 1420 leicester 1420 alcides 1420 josephine 1420 w 8th st 1420 intermediate 1420 shivchinko ul 1420 nom 1421 taurn 1421 patis 1421 revolucion 1421 byrd 1421 hilltop dr 1421 mula 1421 from 1421 caballero 1421 messe 1421 sp12 1421 univ av 1422 qrn 1422 briggs 1422 magnolias 1422 lv 1422 enirgitikov 1422 c 32 1423 pronto 1423 apartman 1423 amalia 1423 montebello 1423 res rd 1423 us 231 1423 121st 1423 fortaleza 1423 ilce 1424 willow ln 1424 optical 1424 b 169 1424 hisn 1424 bk ln 1424 hutchinson 1425 tiglio 1425 uzhd 1425 estrela 1425 extremadura 1425 portela 1425 gvryvn 1425 136th 1426 d 119 1426 r4 1426 karim 1426 redbud 1426 willows 1426 barrir 1426 trung 1427 parikmakhirskaia 1427 ministere 1427 marvin 1427 gilmore 1427 lavka 1427 durer 1427 butterfly 1427 virginia av 1427 shilds 1427 m8 1427 b171 1427 doncaster 1427 poligon 1427 danila 1427 hutten 1427 rider 1428 fortin 1428 resistenza 1428 chiltern 1429 r des mesanges 1429 vp 1429 johnny 1429 shet 1430 original 1430 cassin 1430 17th st 1430 6e 1431 dong bei xin gan xian 1431 kolping st 1431 tohoku shinkansen 1431 erreka 1431 bank st 1431 concorde 1431 barclay 1431 porvenir 1431 glenviw 1431 hurtas 1431 clough 1431 zeromskigo 1431 joachim 1431 ludwig st 1431 jami 1432 tt 1432 woodhaven 1432 pelham 1432 incendio 1432 lamb 1433 pauline 1433 ovoda 1433 canaan 1433 prito 1433 chapel ln 1433 trasa 1433 masters 1434 moran 1434 barranc 1434 structure 1434 dijon 1434 bradesco 1434 nes 1434 jour 1435 v ugo foscolo 1435 antiga 1435 slf 1435 calais 1436 hokuriku exp 1436 sadi 1436 metairi 1436 d 113 1436 pisgah 1436 martires 1436 dumas 1436 492 1436 lobos 1436 overland 1436 timberline 1437 wings 1437 upland 1437 parroquial 1437 mtry 1437 keian 1437 r du cimetire 1437 salengro 1437 ddat 1437 bou 1437 m 8 1438 grazi 1438 davao 1438 marquz 1438 capo 1438 006 1438 devant 1438 mercy 1438 severo 1438 zoi 1439 d 82 1439 guvara 1439 distribuidora 1439 alicia 1439 otavio 1439 coca 1439 oswaldo 1439 narodni 1439 kultur 1440 velka 1440 magdeburger 1440 rondweg 1440 logistik 1440 preschool 1440 resto 1440 firestone 1441 908 1441 bandeira 1441 jamison 1441 etterem 1441 zaria 1441 sicilia 1441 393 1441 znachinnia 1441 embalse 1442 universidade 1442 sluzhba 1442 moreau 1442 hogan 1442 neri 1442 abt 1442 mutual 1442 xudal 1443 forst st 1443 noyers 1443 palmeras 1443 lor 1443 greenhouse 1443 obi 1443 dessus 1443 raoul 1443 sp11 1443 part 1444 brewing 1444 asahikawa 1444 epiceri 1444 kyushu 1444 amanda 1444 magenta 1445 cr 5 1445 candy 1445 anderson rd 1445 steps 1445 a 13 1445 couvent 1445 tov 1445 sepulveda 1445 slymalefn 1445 sp9 1446 alefwlalefd 1446 digu 1447 melville 1447 k 23 1447 rodoviaria 1447 nelson st 1447 662 1447 tilmana 1447 besar 1447 bukhta 1448 gusto 1448 mibil 1448 t2 1448 magyar 1448 morena 1449 konstantinou 1449 gara 1449 tete 1449 stor g 1449 ivana franka vulitsia 1450 calvin 1450 skiforeningen 1450 maiak 1450 cr 3 1450 berlioz 1450 rampe 1450 brush cr 1450 r de liberte 1451 ainqbte 1451 agung 1451 bila 1451 motos 1451 cuauhtemoc 1451 lautoroute 1451 ristoran 1452 lola 1452 imre 1452 smoke 1452 rubio 1452 michit 1452 123rd 1452 maitland 1452 ah61 1452 cafe amazon 1452 tigre 1453 131st 1453 gorizia 1453 baker st 1453 luky 1453 schwarzer weg 1453 flag 1454 brisbane 1454 k 27 1455 n 8th st 1455 iu 1455 e 63 1455 remington 1456 agostinho 1456 ulmenweg 1456 quinto 1456 cuchilla 1456 cosmo 1457 meadowviw 1457 pfarrhaus 1457 ejercito 1457 varela 1457 bazaar 1457 mello 1457 miller st 1457 antonia 1457 yamanote 1457 638 1457 pais 1457 s 6th st 1457 reagan 1458 bgyn 1458 minimarket 1458 tarn 1458 woodcrest 1458 c 23 1458 hajj 1458 usadba 1458 240th 1458 castor 1458 rua um 1459 563 1459 finance 1459 miners 1459 erwin 1459 jail 1459 fndq 1459 lleida 1459 isonzo 1459 silvestre 1459 heidel 1460 s12 1460 strilka 1460 faustino 1460 zolotoi 1460 zygmunta 1460 cr 12 1461 landmark 1461 bay st 1461 blanchard 1461 hof st 1461 dubois 1461 564 1461 ino 1461 arany janos u 1461 giordano 1461 wangdah 1461 bz 1461 ramen 1462 tajo 1462 puch 1462 techniqu 1462 associacao 1462 1050 1462 numero 1462 karl marx st 1462 nicole 1462 albergo 1462 av de leurope 1462 lomo 1462 hallenbad 1463 stroitilnaia ul 1463 mother 1463 svateo 1463 prati 1463 furmanova 1463 schwarzen 1463 alpen 1464 woodfild 1464 butcher 1464 plius 1464 ness 1464 pitts 1464 barlow 1464 thang 1464 ivrosit 1464 guo dao1 hao 1464 d 45 1465 orint 1465 perimetral 1465 dining 1466 soldado 1466 yates 1466 dogwood dr 1466 napir 1466 ah 1466 d 115 1467 pimonte 1467 products 1467 tomei exp 1467 ul krupskoi 1467 sviato 1467 hempstead 1468 ayacucho 1468 albert schweitzer st 1468 e 09 1468 sr 18 1468 ambassade 1468 swirkowa 1468 molire 1469 kochosu 1469 shasta 1469 misto 1469 plank 1469 lom 1469 mcleod 1469 bamboo 1470 xv 1470 alexis 1470 rozc 1470 cervi 1470 dino 1470 haar 1470 prokuratura 1470 kleingartenverein 1470 motorcycle 1471 padaria 1471 148th 1471 heim 1472 wards 1472 vaillant 1472 hafen st 1472 moi 1472 przy 1472 niang 1472 ebro 1472 rifle 1472 lerchen 1472 boggy 1473 fay 1473 kura 1473 erlen 1473 d 69 1473 eugen 1473 943 1473 hillside av 1473 v iv novembre 1473 070000 1474 portsmouth 1474 zhongshan 1474 april 1474 anz 1474 cr 7 1474 seattle 1474 juillet 1474 praza 1474 yeongdong 1475 cup 1475 entuziastov 1475 holme 1475 hilaire 1475 tarragona 1475 matamoros 1475 atlantic av 1475 utama 1475 harrow 1476 trudovaia 1476 waldemar 1476 xiantuop 1476 compound 1476 darby 1476 eo2 1476 kerr 1476 neal 1476 krym 1476 allegheny 1476 paulino 1476 rua dois 1476 gite 1476 3000 1476 autov del cantabrico 1477 734 1477 hofen 1477 klooster 1477 desembargador 1477 nipodleglosci 1477 ligne de noisy sec a strasbourg 1477 210th 1477 springhill 1477 wilkinson 1477 770 1477 alberti 1477 otter cr 1477 jungang exp 1477 saw 1477 a50 1477 stagecoach 1478 history 1478 divide 1478 choctaw 1478 globe 1478 e w hwy algeria 1478 ikony 1479 c 22 1479 wolf cr 1479 moli 1479 idemitsu 1479 lirios 1479 tertre 1479 randweg 1479 ascot 1479 fridhofsweg 1480 savings 1480 765 1480 a hang 1480 melton 1480 sp31 1480 thong 1480 franken st 1480 schwarzwald 1480 wilder 1480 129th 1481 greco 1481 expert 1481 shevchenka 1481 397 1481 watt 1481 924 1481 cortez 1481 durand 1481 konopnickij 1481 sp5 1482 d 56 1482 couto 1482 khalaf 1482 hellweg 1482 hickory ln 1482 loc 1483 s lorenzo 1483 3 maja 1483 alexandrou 1483 viaduto 1483 allianz 1483 us 220 1483 simens st 1483 sobu 1483 carpark 1483 burnham 1484 madera 1484 cordova 1484 palmeiras 1485 begin 1485 mace 1485 hok 1485 khalefn 1485 colline 1485 ltda 1485 margarets 1486 r des peuplirs 1486 lumire 1486 remy 1486 floral 1487 roda 1487 bilorusnift 1487 truman 1487 tancsics 1487 girtsina 1487 19a 1487 francaise 1487 tarasa shivchinka vulitsia 1487 grossen 1487 karang 1487 s jingnoripaku 1488 rotunda 1488 n 83 1488 pid 1488 dune 1488 padang 1488 privokzalnaia 1488 musica 1488 asturias 1488 viaduc 1488 klinikum 1489 parra 1489 harley 1489 kirchenweg 1489 gerhart 1489 choice 1489 limon 1490 whitewater 1490 s 5th st 1490 gato 1490 g76 1490 toys 1490 ausbau 1490 fussball 1490 asbury 1491 lenine 1491 arnaldo 1491 permanent 1491 dresdner 1492 mustafa 1492 shoals 1493 minerva 1493 roshcha 1493 us 95 1493 a 71 1493 landfill 1493 ligne plm de paris a lyon 1493 58k 1493 cedar av 1493 a24 1494 aussere 1494 mtn rd 1494 policing 1494 treasure 1494 quens rd 1494 pondok 1494 mannheim 1494 taylor rd 1495 direct 1495 wroclawska 1495 machine 1495 salvo 1495 lui 1495 hove 1495 9 de julio 1496 grube 1496 haynes 1496 loh 1496 audubon 1497 irwin 1497 cairo 1497 philippine 1497 467 1497 aldi sud 1497 klonowa 1497 03k 1497 ayala 1497 jamestown 1498 d 49 1498 narrow 1498 basilica 1498 kuala 1498 sinai 1498 koloni 1498 woodrow 1498 woody 1499 republik 1499 liningradskaia ul 1499 hon 1499 144th 1499 jerry 1499 york st 1499 alefltryq aleflsyalefr shrq grb 1500 crooked cr 1500 avelino 1500 gastillo 1500 iroquois 1500 126th 1500 laurir 1500 titova 1500 taunton 1500 brok 1501 r jules fy 1501 moselle 1501 split 1501 tikhnikum 1502 lucy 1502 jsr 1502 cine 1503 d 104 1503 bruckner 1503 garonne 1503 luth 1503 lungomare 1503 atb 1503 hrtsl 1503 linz 1503 1300 1503 hu shan gao su 1503 brassens 1503 villers 1504 jerzego 1504 winery 1504 henley 1504 hollis 1504 fossa 1504 lina 1504 logistics 1505 charlton 1505 fung 1505 artes 1505 ideal 1505 dinas 1505 napa 1506 av a 1506 ita 1506 mezzo 1506 marian 1506 ampere 1506 gh 1506 lines 1506 singngiyah 1507 sapins 1507 s luis 1507 pellegrini 1507 rf 1507 galgen 1507 rcda 1508 guo dao9 hao 1508 korut 1508 publiczna 1508 mediathequ 1508 steig 2 1508 harwood 1508 levante 1508 daosta 1509 wbk 1509 solomon 1509 blubell 1509 d 46 1509 ole 1509 pl mayor 1509 vrh 1509 alefwl 1510 brule 1510 ballard 1510 muzykalnaia 1510 bozej 1510 chiornaia 1510 country club dr 1510 tabak 1510 maple ln 1511 piao 1511 berwick 1512 boreios odikos axonas kretes 1512 namha exp 1512 r du chateau deau 1512 annexe 1512 scotiabank 1512 d 64 1513 paulus 1513 odikos 1513 flyover 1513 v venezia 1513 saunders 1513 tlwl 1513 agencia 1513 008 1513 eichendorff st 1514 ataturk 1514 je 1514 salgado 1514 mat 1514 pittsburgh 1514 rondon 1514 budapest 1515 shyte 1515 rua 3 1515 paper 1515 sommer 1515 bayberry 1515 flur st 1515 pgind 1515 lester 1516 laz 1516 clovis 1516 cic 1516 vell 1516 axonas 1517 corrales 1517 us 83 1517 anadolu 1517 brunnenweg 1517 935 1518 cou 1518 unterm 1518 nelkenweg 1518 uch 1518 eich 1518 garnet 1519 covington 1519 fichtenweg 1519 lininskaia 1519 universitario 1520 wyszynskigo 1520 euronet sp z o o 1520 osnovna 1520 paco 1520 irvine 1520 gris 1520 rose st 1520 ruhr 1521 cad 1521 rama 1521 russell st 1521 richard wagner st 1521 vishniovaia ul 1521 miska 1522 june 1522 shatt 1522 boreios 1522 athens thessaloniki evzonoi 1523 545 1523 posten 1523 salt cr 1523 mwy 1 athens thessaloniki evzonoi 1523 rn40 1523 jol 1524 gagarina vulitsia 1524 hmd 1524 monsenor 1524 schroder 1524 740 1524 ankara 1524 cincinnati 1525 markus 1525 libig 1525 bury 1525 ostanovka 1525 alfa bank 1526 718 1526 mnzl 1526 corp 1526 carey 1526 settlers 1526 correios 1526 ministop 1526 brescia 1526 e av 1526 a41 1526 kiivskaia 1526 baixo 1527 laurirs 1527 shelton 1527 salim 1528 indonesia 1528 h m 1528 oka 1528 study 1528 cluain 1528 marcelo 1528 special 1529 pirres 1529 lombardia 1529 wong 1529 kin 1529 reeves 1530 d 70 1530 d 137 1530 b 20 1530 woodbridge 1531 lan xin xian 1531 velo 1531 bikes 1531 whittir 1532 jr zhong yang xian 1532 tetsudo 1532 belgrade 1532 villar 1532 williams rd 1533 arma 1533 serrat 1533 00 1533 loisirs 1533 e40 1533 brown rd 1534 pabellon 1534 title 1534 technik 1534 congo 1534 guo dao42 hao 1534 valentine 1534 duro 1534 lindsey 1535 mull 1535 joliot 1535 volvo 1535 acude 1535 seal 1535 tanner 1535 mosel 1535 autoroute du soleil 1536 stationsweg 1536 sonora 1536 neustadter 1536 cotes 1536 dantut 1536 thomson 1537 argine 1537 bilaia 1537 burgweg 1537 egg 1537 387 1537 pipe 1537 cola 1537 neruda 1537 sunoco 1537 doyle 1538 ursula 1538 enterprises 1538 rua sao paulo 1538 rdalef 1538 wildflower 1538 aleflhalefj 1538 paradiso 1539 d 58 1539 andover 1539 hunyadi 1539 olivir 1539 gogh 1539 daily 1539 stein st 1539 ahorn st 1539 13a 1539 cedros 1539 dark 1539 euclides 1539 wisteria 1539 mona 1539 jokai 1539 127th 1540 ywsf 1540 zhong shan gao su gong lu 1540 massimo 1540 mnr rd 1540 metrobus 1540 banqu populaire 1541 inca 1541 popova 1541 malvern 1541 pfarramt 1541 tanzox 1541 ezbet 1541 pick 1542 primeiro 1542 ul engilsa 1542 kliuch 1542 monsenhor 1542 diamante 1542 573 1542 20a 1542 commission 1542 sudeten st 1543 kenneth 1543 536 1543 autostrada adriatica 1543 tar 1543 sawyer 1543 alvares 1544 rn7 1544 haza 1544 recreational 1544 eddy 1544 wireless 1544 lisesi 1544 cay 1544 ris 1544 wilmington 1544 templo 1544 severn 1545 martini 1545 rundweg 1545 siti 1545 cristovao 1545 taberna 1545 greenhill 1545 crkva 1546 ded 1546 wolfe 1546 n10 1546 party 1546 coutinho 1546 pavlova 1546 3b 1546 biudzhitnoi 1546 ss3bis 1546 puntal 1547 athletic 1547 av de republiqu 1547 sandweg 1547 648 1547 707 1547 dogwood ln 1547 jolly 1548 neur weg 1548 529 1548 d 47 1548 oskar 1548 laval 1548 look 1549 714 1549 pita 1549 d 54 1549 kato 1549 puskesmas 1549 pasticceria 1549 woodridge 1549 distribution 1550 bashnia 1550 dayr 1550 doris 1550 molinos 1550 fly 1550 lagrange 1550 door 1551 ari 1551 d 51 1551 super u 1551 stroitilni 1551 d 101 1552 483 1552 prefecture 1552 493 1552 falling 1552 hydrant 1552 teso 1553 zeppelin st 1553 putnam 1553 hebron 1553 ciclovia 1553 anlage 1553 raphal 1553 genossenschaft 1553 sta cruz 1554 ul chikhova 1554 raionni 1554 boloto 1554 timiriaziva 1554 jara 1554 ah4 1554 398 1554 lune 1554 rite aid 1555 misericordia 1555 rossiia 1555 a25 1555 gordo 1555 mclean 1556 sumner 1556 us 421 1556 kohler 1556 oblastnoi 1556 lift 1556 dreef 1557 foire 1557 osh 1557 sp17 1557 guten bg st 1557 muhlgasse 1557 579 1557 shaft 1557 beulah 1558 alberdi 1558 s rd 1558 830 1558 blau 1558 motta 1558 treze 1558 mizuho 1558 wer 1559 razina 1559 lemos 1559 geo 1559 molodizhna vulitsia 1559 mayflower 1559 220th 1559 sonic 1559 professionnel 1559 09780 1559 rashid 1560 lisi ukrainki vulitsia 1560 berliner ring 1560 garda 1560 arcos 1560 avtosirvis 1560 rad 1560 leader 1560 intersport 1560 lois 1561 av jean jaures 1561 liberty st 1561 creux 1561 kvetna 1561 uchilishchi 1561 prato 1561 671 1561 rua d 1561 christina 1561 meuse 1562 vino 1562 turun 1562 homer 1562 rjm 1563 saturn 1564 trou 1564 ul kuibyshiva 1564 mazda 1564 lazare 1564 128th 1564 economica 1565 alcala 1565 westbrook 1565 sauce 1565 huckleberry 1565 fauvettes 1565 lovers 1565 asilo 1566 sugarloaf 1566 ivanovka 1566 g320 1566 burr 1566 bdy rd 1566 dorozhnaia 1566 promyshlinnaia 1567 backhaus 1567 ah34 1567 pila 1567 761 1567 longwood 1567 redonda 1567 francesc 1567 stephenson 1567 bridgewater 1567 breite st 1567 dicimbre 1568 ul ostrovskogo 1568 collir 1568 japanese 1568 haag 1568 nathan 1568 coolidge 1568 harmon 1568 renard 1569 chin 1569 sanford 1569 gewerbe 1569 shkilna vulitsia 1569 francisca 1569 rat 1569 powder 1569 schone 1570 wurz 1570 regens 1570 creu 1570 youngs 1570 jingnoripaku 1571 v monte grappa 1571 tony 1571 plot 1571 a 61 1571 wojcicha 1571 maharlika hwy 1571 vaz 1571 hall rd 1572 au st 1572 daodalg 1572 volodarskogo 1572 ferinhaus 1572 seca 1572 c2 1572 bout 1572 orthodox 1572 lungo 1573 grunwaldzka 1573 show 1573 mystic 1573 c b 1573 941 1573 k 21 1573 660 1573 kroger 1574 adh 1574 eight 1574 bourne 1575 seat 1575 offramp 1575 philipp 1575 sp19 1576 roten 1576 kings rd 1576 gimnasio 1576 arbys 1576 rumah penduduk 1576 togliatti 1576 050 1576 ul shivchinko 1577 rozy 1577 monarch 1577 quntin 1577 stettiner st 1577 maya 1577 spokojna 1577 bancroft 1577 rast 1578 hannah 1578 ashwood 1578 dexter 1578 korner 1578 crrdo 1578 anse 1578 dali 1578 orchard st 1579 residences 1579 safari 1579 r de po ste 1579 jana pawla 2 1579 triple 1579 mesquite 1579 belgiqu 1579 cicha 1579 bark 1580 mosquito 1580 okrug 1580 podgornaia ul 1580 119th 1580 meade 1580 8th av 1580 carrington 1581 mariposa 1581 sauces 1581 chapel st 1581 ghr 1581 womens 1581 s n 1582 mahall 1582 ruiny 1582 burnett 1582 d3 1582 rutherford 1583 reya 1583 bonnet 1583 saone 1584 617 1584 mckay 1584 kit 1584 585 1584 sudeten 1584 vip 1584 k 20 1585 aradi 1585 periferico 1585 privat 1585 gagarin 1585 aliakmonas 1585 jurgen 1585 freight 1586 rachel 1587 752000 1587 tennyson 1587 d 59 1587 ecole maternelle 1587 dames 1587 d 103 1587 spozywczy 1587 tribunal 1587 hoofdweg 1588 ormes 1588 goodwin 1588 perla 1588 hinden 1588 industriale 1588 weide 1588 petrarca 1588 553 1589 reit 1589 autos 1589 ludovico 1589 eschenweg 1589 primeveres 1589 kipling 1590 asda 1590 trinta 1590 amor 1590 sobor 1590 basket 1590 stony cr 1591 rec 1591 a34 1591 lhopital 1591 ponta 1591 b 173 1591 drummond 1592 653 1592 salas 1592 dn6 1592 us 26 1593 universitaire 1593 mandela 1593 ruine 1594 v torino 1594 sears 1594 hauptmann 1594 gin 1595 co rd 1595 goias 1595 khwr 1595 704 1595 rebecca 1595 loja 1595 psh 1 1596 hing 1596 protection 1596 grosso 1596 juliusza slowackigo 1596 potomac 1597 salam 1597 schulgasse 1597 b 75 1598 mkhlp 1598 pitt 1598 milana 1598 kasztanowa 1599 palomar 1599 sp10 1599 us 9 1599 minisutotupu 1599 halles 1599 lind 1600 835 1600 wyndham 1600 dimitrova 1600 14a 1600 socorro 1600 a18 1600 serres 1601 fundo 1601 chain 1601 gumes 1601 e4 1601 av s martin 1602 pant 1602 percy 1602 484 1602 wilson av 1602 schuman 1602 alcantara 1602 aldeia 1602 york rd 1602 k 30 1603 arapaho 1603 riverdale 1603 mdr 1604 penzion 1604 groot 1604 meson 1604 coopers 1604 engine 1604 menezes 1604 severino 1604 k 15 1604 27k 1604 nations 1604 maris 1604 junio 1604 millenium 1605 rua 2 1605 paiva 1605 seminary 1605 w 7th st 1605 ella 1606 craft 1606 genesee 1606 balsa 1606 drum 1606 august bebel st 1606 e 7th st 1606 longitudinal 1606 e 51 1606 kbysh 1606 rote 1606 sh 2 1606 feldweg 1607 uzytek 1607 alpini 1607 gauge 1607 d 62 1607 sonne 1607 nightingale 1607 lindustri 1607 cooks 1608 804 1608 obrazovaniia 1608 colectora 1608 magnolia st 1608 ul 8 marta 1609 merrill 1609 vosges 1609 halte 1609 schwarz 1609 velazquz 1610 v 4 novembre 1610 goya 1610 gleis 1610 song wu 1611 palacios 1611 helsingin 1611 rough 1611 marcus 1611 scuole 1612 welch 1612 panoramaweg 1612 113th 1612 long branch 1612 mike 1613 ashby 1613 mbh 1613 dillon 1613 murillo 1613 642 1613 624 1613 bh 1613 sr 7 1614 fayette 1614 cobblestone 1614 jennifer 1614 mcdowell 1614 gendarmeri nationale 1614 routire 1614 dipo 1614 intendente 1615 islam 1615 salvation 1615 stoianka 1615 vereinsheim 1615 still 1616 gerald 1616 buffalo cr 1616 universitaria 1616 rex 1616 jerome 1616 jeu 1616 tecnico 1617 kaiser st 1617 perron 1618 mini mkt 1618 pochtovaia ul 1618 gereja 1618 springdale 1619 ley 1619 bandera blanca 1619 matias 1619 peaks 1620 pantai 1620 southside 1620 aston 1620 merkezi 1621 hun 1621 phillip 1621 inter 1621 mqbrte 1621 nimes 1621 pompidou 1621 malefrkt 1621 marcelino 1621 zob 1622 dulce 1622 secret 1622 plm 1622 miai 1622 quarry rd 1622 liberdade 1622 nurnberger 1623 siquira 1623 velky 1623 ligne de paris montparnasse a brest 1623 flug 1624 an post 1624 autostrada del sole 1624 pope 1624 bwlk 1624 polski 1624 grafton 1624 pommirs 1624 kpp 1624 c 21 1624 zamek 1625 kiraly 1625 us 395 1625 nagoya 1625 use 1625 litiia 1625 keys 1626 aussicht 1626 rezerwat 1626 giardini 1626 allan 1627 howard st 1627 kanaal 1627 braz 1628 islamic 1629 813 1629 kokudo 1629 earth 1629 dalla 1629 robert koch st 1629 pumping 1630 alb 1630 bri rd 1630 mkhfr 1630 struga 1630 s francisco 1631 sta fe 1631 rialto 1631 dundee 1631 omega 1631 kingswood 1631 staw 1631 virge 1631 lava 1632 gebel 1632 s migul 1632 ambulance 1633 falcone 1633 boucher 1633 oder 1633 rch 1633 orquideas 1633 griffith 1633 lenin st 1633 581 1633 luc 1634 apostolic 1634 preto 1634 derwent 1634 851 1634 achille 1635 emily 1635 khmyny 1635 partizanska 1635 progress 1635 itau 1636 jugend 1637 cr 4 1637 sauro 1637 16a 1637 sp15 1637 murcia 1637 fap 1638 rioja 1638 1 77 1638 macedo 1638 kimball 1638 kirkwood 1638 suisse 1638 r du parc 1638 etangs 1638 dinh 1639 pikarnia 1639 oliva 1639 schuh 1639 reino 1639 brow 1640 algeri 1640 vital 1640 aguiar 1640 marqutte 1640 ng 1640 sraid 1640 casablanca 1640 nicolau 1641 commerzbank 1641 hoffman 1641 vir 1641 prison 1641 mahmud 1642 migros 1642 minzoni 1642 lavender 1642 sunkus 1642 champlain 1643 communal 1643 kebap 1643 rye 1644 berri 1644 caracas 1645 znqte 1645 oak av 1645 countryside 1645 industris 1646 gheorghe 1646 action 1646 augs 1646 adirondack pk 1646 brooke 1646 stn st 1646 irrigation 1646 hill rd 1646 sycamore st 1647 popolare 1647 a40 1647 145th 1647 d 55 1648 rosengarten 1648 sinkansen 1648 sheriffs 1648 camus 1648 beech st 1648 117th 1649 miqul 1649 versailles 1649 steige 1649 mediterranee 1649 pennsylvania av 1649 renaissance 1649 rego 1650 constant 1650 levant 1650 belleville 1651 v giovanni pascoli 1651 naftal 1651 469 1651 c 31 1651 electrical 1651 jones rd 1651 abdullah 1651 coqulicots 1651 mhdy 1651 delgado 1651 alte df st 1652 zand 1652 rutland 1652 lipa 1652 angus 1652 124th 1652 tiatr 1652 intersection 1652 ferndale 1652 bs 1653 waverley 1653 bruyere 1653 bundes st 1653 meat 1653 584 1653 davenport 1653 725 1653 tower rd 1653 schnellfahrstrecke 1653 c 19 1653 394 1653 1 76 1653 industriweg 1654 biograd 1654 oja 1654 harlem 1655 paddy 1655 chaux 1655 mauricio 1656 cavendish 1656 tancredo 1656 skoda 1656 905 1656 cora 1656 tip 1656 gonzales 1656 pliazh 1656 roter 1656 highland dr 1657 cr 8 1657 k 17 1657 maple dr 1657 extended 1657 hoofd st 1657 antirrio 1657 joshua 1657 399 1657 liuksim 1658 kingsley 1658 bianca 1658 planck 1658 sonnen st 1658 solano 1658 fliss 1658 mobel 1659 we 1659 hauptschule 1659 396 1659 wester 1659 lindenallee 1659 press 1660 sonnenweg 1660 cel 1661 lockwood 1661 ch du moulin 1662 honore 1662 e6 1662 fortune 1662 v giacomo leopardi 1662 903 1663 r de liberation 1663 ruben 1663 opera 1663 enlace 1663 lauro 1663 mercadona 1663 diramazione 1664 sola 1664 d 86 1665 d 65 1665 alefralefdy 1665 terminus 1665 tracy 1665 pitch 1665 713 1665 take 1666 makdonalds 1666 farmington 1666 liutenant 1666 brookville 1666 chata 1667 ponto 1667 d 53 1667 steig 1 1667 lana 1667 ponts 1668 underwood 1668 gerais 1668 wola 1668 hoog 1668 leal 1668 911 1669 left 1669 hardt 1669 wyoming 1669 doc 1670 d2 1670 welsh 1670 fat 1670 farias 1670 puma 1670 643 1670 590 1670 beaverdam cr 1670 pink 1671 shing 1671 lige 1671 bursztynowa 1671 tepesi 1672 dachnaia ul 1672 hom 1672 munchner 1672 sosnovaia 1672 kommune 1672 bldgs 1673 alberta 1673 tryn 1673 654 1673 cantr 1673 703 1674 scotts 1674 fun 1674 16th st 1674 goroda 1674 ciclabile 1674 larkspur 1674 riga 1674 blqu 1674 museu 1674 doma 1674 miro 1675 brian 1675 segura 1675 e 6th st 1675 k 29 1675 camp cr 1675 sandstone 1675 piaskowa 1676 lva 1676 1075 1676 nestor 1677 swm 1677 commerciale 1677 saxon 1677 isabella 1677 vic 1677 rennes 1678 krone 1678 e 19 1678 turismo 1679 decatur 1679 s50 1679 mb 1679 ibanez 1679 v papa giovanni xxiii 1680 loon 1680 oak dr 1680 perth 1680 caritas 1680 patria 1680 frederico 1681 komenskeo 1681 perumahan 1681 lcl 1681 kelapa 1681 juscelino 1681 thw 1682 aleflsyd 1682 hibiscus 1682 temeto 1682 rias 1682 eglise st pirre 1683 capitaine 1683 122nd 1683 lindenhof 1683 shoppu 1683 thalmann 1683 n6 1684 davis rd 1684 5e 1684 pestalozzi st 1684 girard 1684 macon 1684 ul suvorova 1684 moores 1685 agra 1686 maisons 1686 wayside 1686 e 46 1686 fo u 1686 biriozovaia 1687 cincias 1687 zdanii 1687 elsa 1687 d 60 1688 platanes 1688 cattle 1688 matsuyama 1688 rosso 1688 jonquilles 1688 aleksandra 1688 shkolni piriulok 1688 altalanos 1688 ul nikrasova 1688 quartz 1688 christophe 1688 114th 1688 s 1st st 1688 ute 1688 ocampo 1689 ah42 1689 montserrat 1689 raduga 1689 tabac 1689 gift 1689 pony 1690 rodnik 1690 arenal 1690 g18 1690 waterside 1690 asian 1691 wilshire 1691 retiro 1691 candelaria 1691 bart 1691 nogales 1691 tsikh 1692 eichen st 1692 malo 1692 c st 1693 chaves 1693 s3 1693 palms 1693 haj 1694 manila 1694 cura 1694 1 69 1694 fold 1694 heilig 1694 ikea 1694 stuttgarter 1695 harav 1695 jebel 1696 politsii 1696 ptilo 1696 duca 1696 389 1696 bunavista 1696 airstrip 1696 schon 1697 avtovokzal 1697 jennings 1697 920 1697 kei 1697 with 1697 eko 1697 803 1697 k 18 1697 publa 1698 soria 1698 balboa 1698 planta 1699 veld 1699 stoke 1699 cmentarna 1699 plc 1699 rath 1699 toms 1700 906 1700 watkins 1700 pavillion 1700 763 1700 sims 1700 ses 1701 neighborhood 1701 nebraska 1701 chataignirs 1701 renhomu 1701 none 1701 nina 1702 sportpark 1702 migafon 1702 705 1702 hmyd 1703 temporary 1703 hs2 1703 yhvdh 1703 tiffany 1703 lotto 1704 chowk 1704 jeans 1704 deu 1705 bonner 1705 ep1 1705 frontir 1705 church of christ 1706 633 1706 hin 1706 sainyd 1706 d 63 1706 carrefour mkt 1706 salado 1706 immobilir 1706 d 48 1707 s6 1707 kombinat 1707 132nd 1707 regi 1708 eduard 1708 us 412 1708 coat 1709 oak ln 1709 sampaio 1709 deep cr 1709 938 1709 adrian 1710 financial 1710 central telephoniqu 1710 bolz pl 1710 suffolk 1710 bala 1710 e w hwy 1710 anglican 1710 ukrposhta 1710 dwor 1710 549 1711 start 1711 haydn 1711 piro 1711 lantern 1711 bowl 1711 luisa 1712 tivoli 1712 qasr 1712 rudnia 1712 stephan 1712 opposite 1712 805 1713 cid 1713 coppice 1713 jacaranda 1713 kyle 1713 nero 1713 namha 1713 pive 1713 sheldon 1714 profesor 1714 harrington 1714 dalsace 1714 solnichni 1714 730 1714 kgv 1714 dike 1714 mdrsh 1715 onramp 1715 collection 1715 tramway 1715 alexandria 1715 stadtwerke 1715 suki 1716 vilar 1716 616 1716 solo 1716 1001 1717 jamaica 1717 sovkhoznaia ul 1718 maipu 1718 otoyolu 1718 casey 1718 comendador 1720 boris 1720 qalefrte 1720 brady 1721 remparts 1721 engels 1721 consultorio 1721 bif 1721 conference 1721 casanova 1721 citrus 1722 dunbar 1722 kam 1722 picard 1723 mur 1723 stadio 1723 135th 1723 patch 1723 g7011 1723 municipios 1724 gewerbepark 1724 bertha 1724 shore ln 1724 schweiz 1724 oshchadbank 1724 bulu 1724 gyeongbu hsl 1725 schubert st 1725 yellowhead hwy 1725 tsvity 1725 forster 1725 buy 1725 leone 1725 pascual 1726 wilson rd 1726 mori 1726 mkad 1726 hogar 1726 industry 1726 navarra 1727 burgess 1727 reformed 1727 1deg 1727 rizal 1727 denton 1728 chesterfild 1728 tel 1728 condor 1728 camelot 1728 1 55 1728 dept 1729 havre 1729 cache 1729 graveyard 1729 qa 1729 lansdowne 1730 ancins 1730 g104 1730 spice 1731 dorothy 1731 fontane 1731 landwer 1731 hazelwood 1731 thorpe 1732 obkhod 1732 witte 1732 pola 1732 b 87 1732 davis st 1732 brands 1732 nf 1732 veiga 1733 kurze 1733 nagornaia ul 1734 gobernador 1734 810 1734 poco 1734 madame 1734 oakley 1735 agricultural 1735 shahid 1735 bivio 1736 not 1736 quarters 1736 sr11 1736 crosby 1736 grushivskogo 1736 dir 1736 darwin 1736 shan yang xin gan xian 1736 abbe 1736 118th 1736 modern 1736 opal 1736 thistle 1737 lok 1737 ul druzhby 1737 hog 1737 floresta 1738 636 1738 steg 1738 n5 1739 pulaski 1739 trenton 1739 lahden 1739 k 14 1740 kruger 1740 railway av 1740 cuvas 1740 houses 1740 dispansir 1741 print 1741 sy 1742 steiner 1742 sulphur 1742 jednota 1743 bunos aires 1743 forras 1744 corps 1744 lotos 1744 seaviw 1744 ups 1744 2eme 1744 sberbank 1745 servicios 1745 kenilworth 1745 kagoshima 1745 e 08 1746 werkstatt 1746 e 30 ah6 1746 intirnatsionalnaia ul 1746 salles 1746 626 1747 chopin 1747 professional 1747 d 41 1747 quiroz 1747 r de verdun 1747 har 1747 s 4th st 1748 hain 1748 matthias 1748 mack 1749 c 25 1749 grouse 1749 a 38 1749 clifford 1749 fedex 1749 yrigoyen 1750 usa 1750 chevrolet 1750 arden 1750 456 1750 kretes 1750 alvarado 1750 wabash 1750 padova 1751 a23 1751 carrires 1751 tsvitochnaia ul 1751 a 20 1752 w rd 1752 kenwood 1752 zhuo 1752 lt r 1753 iga 1753 dario 1753 skyline dr 1753 mintaqat 1753 truda 1753 colli 1754 kekurinituku 1755 blind 1755 chif 1755 adj 1755 mainzer 1756 25 de mayo 1756 filiale 1756 dan in luan huang xin 1756 bahnstrecke 1756 fridens st 1756 furnace 1757 woodbury 1757 fabio 1757 reiseburo 1757 25a 1757 sp8 1758 us 202 1758 458 1758 credito 1758 r des pres 1758 comisaria 1758 downing 1758 miru vulitsia 1759 hess 1759 r neuve 1759 ovido 1759 sasso 1759 evangelica 1759 bonn 1759 mm 1760 stroitilnaia 1760 brisas 1760 constitution 1760 elephant 1760 mateos 1760 zdorovia 1761 rivoliutsii 1761 willard 1761 hoi 1761 velho 1761 grune 1761 catedral 1762 markit 1762 sovitskii 1762 claudia 1762 pershing 1763 chef 1763 d 42 1763 martina 1763 bro 1763 guard 1764 940 1764 stambanan 1764 linina ul 1765 soda 1765 catania 1765 gasuto 1765 kompaniia 1766 khlf 1766 w 6th st 1766 navy 1766 alegria 1766 c 20 1767 hamad 1767 r du 19 mars 1962 1767 shah 1767 bayshore 1767 vishniovaia 1767 sanhardio 1767 s10 1768 palaia 1768 tio 1768 minh 1768 mobility 1769 fernwood 1769 pinecrest 1769 atlantiqu 1769 albuqurqu 1770 isa 1770 produktovi 1771 dozsa gyorgy u 1771 pidmont 1771 jenny 1771 109th 1771 200th 1771 walters 1772 deutsche bank 1772 mila 1772 malta 1772 dao42 1772 cr rd 1772 wellness 1772 larisa 1773 stuttgart 1773 nail 1773 a28 1773 khashm 1773 morse 1774 woolworths 1774 b 14 1774 vall 1774 sirgiia 1774 bouleaux 1775 lowe 1775 pochtovaia 1775 d 67 1775 kutuzova 1775 bridges 1776 foscolo 1776 niva 1777 pardo 1777 wilton 1777 melody 1778 17a 1778 e 5th st 1778 leng 1778 najswitszej 1779 dao9 1779 browning 1779 ohiggins 1780 rosemary 1780 lilly 1780 xxv 1780 hanna 1781 masseria 1781 b 71 1781 morrisons 1782 watts 1782 sra 1782 fee 1782 adventure 1782 ady endre u 1782 r3 1782 sandy ln 1783 lee st 1783 kebun 1783 kafe 1783 us 13 1783 upton 1783 crtjo 1784 madison av 1784 zilina 1784 bolshii 1785 launch 1785 higgins 1786 greenwich 1786 di2 1786 zhong yang zi dong ch dao 1787 forges 1787 giles 1787 pare 1787 pepe 1787 substation 1787 turginiva 1788 lumber 1788 amsterdam 1788 villaggio 1788 banco do brasil 1788 barca 1788 deutsche bahn 1789 s 2nd st 1789 frazione 1789 a 45 1789 us 51 1789 bab 1789 avis 1789 orr 1790 wein bg st 1790 nagy 1790 amir 1791 graniczna 1791 zapadnaia ul 1791 ditrich 1791 beaufort 1791 d 43 1792 ruins 1792 v galileo galilei 1792 swiss 1792 619 1792 ukraina 1792 mary st 1793 barrington 1793 urbano 1793 950 1793 eastbound 1794 pater 1794 d100 1794 astrid 1794 116th 1794 sandwich 1794 doce 1795 otp 1795 mayfair 1795 rio grande 1795 sandra 1795 sas 1796 espirito 1796 livestock 1796 543 1797 brk 1797 g312 1797 parallelweg 1797 liningradskaia 1797 friden 1798 s 3rd st 1799 buckhorn 1799 akacjowa 1799 yankee 1799 ara 1799 trafalgar 1799 parking lot 1800 osorio 1800 miklos 1800 kommunistichiskaia ul 1801 pizzaria 1801 velez 1801 alem 1801 snyder 1801 shd 1802 447 1802 musa 1802 taylor st 1803 veterinaria 1803 aguilar 1803 007 1803 campana 1804 guadalajara 1804 marka 1804 avellaneda 1804 sport pl 1804 us 82 1804 voor 1805 bonne 1805 g318 1805 memory 1805 kapel 1806 cheval 1806 yuba 1806 schumann 1807 galicia 1807 elb 1807 unidade 1807 hawr 1807 n 4 1807 720 1807 c mayor 1807 beth 1807 m 2 1808 books 1808 cable 1808 fleischerei 1808 oakridge 1808 mimosas 1809 weiden 1809 condominium 1809 catalunya 1809 lacs 1810 711 1810 v giosu carducci 1810 levi 1811 farma 1811 nara 1811 w 5th st 1812 bat 1812 gawa 1812 staples 1812 hag 1812 morin 1812 mancha 1812 sharp 1812 belen 1813 tsv 1813 tome 1813 laboratorio 1813 12a 1813 matthew 1813 electoral 1813 switej 1813 428 1814 salle des fetes 1814 westbound 1815 flughafen 1815 steen 1815 model 1815 convention 1815 jeep 1815 pairc 1816 prescott 1816 952 1816 schoolhouse 1816 wynd 1816 braun 1817 therese 1817 barr 1817 d 44 1817 lager 1818 sf 1818 sviatoi 1818 branca 1818 fra 1818 educacao 1818 cedro 1818 bahndamm 1819 worcester 1819 slovenska 1819 breuil 1819 beatrix 1819 e 442 1820 sans 1820 mulino 1820 cova 1820 487 1820 caterina 1820 c1 1820 maggiore 1820 t1 1820 eichendorff 1821 575 1821 telephoniqu 1822 a 10 1822 702 1822 gerhard 1822 western av 1823 neptune 1823 carniceria 1823 khshm 1823 vit 1823 velocidad 1824 podgornaia 1824 dairy quen 1824 kloster st 1825 brug 1825 saar 1825 santanna 1825 g1 1825 serena 1825 pratt 1826 karel 1826 saavedra 1826 jinghu 1826 delhi 1827 e 105 ah8 1827 presse 1827 ros 1827 710 1827 valea 1827 voroshilova 1827 gatve 1827 bert 1827 dorset 1827 maxi 1828 shoreline 1828 piliakalnis 1828 peacock 1829 davids 1829 mitterrand 1829 frog 1829 chaikovskogo 1829 tommaso 1830 tulpenweg 1830 rathaus st 1831 toscana 1831 monnet 1831 hansen 1831 cam 1831 hanson 1831 taylors 1832 lorca 1832 seasons 1832 dolce 1833 giorgi 1833 655 1833 ismail 1833 northside 1833 alain 1833 gunther 1833 gedung 1833 buda 1834 mercato 1834 cabot 1834 institution 1834 us 98 1834 uni 1834 ul svobody 1835 francais 1836 e leclerc 1836 addison 1837 montagu 1837 kifernweg 1837 hirondelles 1838 lake dr 1838 budget 1838 spot 1838 614 1838 tsdng 1839 salman 1839 corazon 1840 inferiore 1840 falken 1840 gorod 1841 bray 1841 miller rd 1842 c 18 1842 mauro 1842 kreuz st 1842 marker 1842 rus 1843 irma 1843 water tank 1843 africa 1844 grosvenor 1844 bogen 1844 ostre 1845 a32 1845 normandy 1845 olivos 1845 velasco 1845 aldea 1845 argentino 1846 vivaldi 1846 transportation 1846 peoria 1847 colina 1847 luca 1847 gelateria 1847 blackburn 1847 nicolo 1848 661 1848 northbound 1849 cone 1849 hurricane 1849 photo 1849 figuroa 1849 a26 1849 mud cr 1850 powers 1850 camera 1850 infant 1850 anillo 1851 quaker 1851 eaux 1851 cimarron 1852 ker 1852 alefldyn 1852 intirnatsionalnaia 1852 k 9 1853 bloomfild 1853 111th 1854 piper 1854 louisiana 1854 vestre 1854 full 1855 yew 1855 lakeland 1855 mshh 1855 soledad 1856 hintere 1856 dora 1856 101st 1856 r de paix 1857 graves 1857 rispubliki 1857 sal 1857 carver 1858 ural 1858 segundo 1858 joffre 1858 delicias 1858 ralph 1859 digital 1859 w 1st st 1859 46k 1859 rer 1860 s pedro 1860 e 12 1860 r du 8 mai 1945 1860 pagoda 1860 artigas 1860 peachtree 1861 komsomolska vulitsia 1862 regionale 1862 cyu 1862 new st 1862 forbes 1862 silver cr 1862 mktb 1863 d 40 1863 govt 1863 brown st 1863 hoher 1864 wincentego 1864 bertrand 1864 nazario 1865 1 84 1865 ferrer 1866 528 1866 williams st 1866 unity 1866 valero 1867 dong bei zi dong ch dao 1867 angels 1867 meeting 1868 583 1868 windermere 1869 ly 1869 s5 1869 dartmouth 1869 compton 1869 buhl 1869 ronco 1870 forno 1870 linwood 1870 princesa 1870 dominion 1870 home depot 1870 gron 1870 montparnasse 1870 greenbriar 1870 plessis 1870 zs 1871 henryka sinkiwicza 1871 sakura 1871 fawy dr 1872 children 1872 johnson st 1873 anni 1873 monticello 1873 a52 1873 rooms 1874 alicante 1874 quinze 1874 907 1874 beauliu 1874 ki 1874 kopernika 1875 kr 1875 sella 1875 laboratory 1875 mal 1876 hickory st 1876 stirling 1876 prospect st 1876 mendez 1876 gonzaga 1876 steele 1877 cr 2 1877 ssh 1877 fabrik 1877 sud st 1877 abbott 1877 g35 1877 iso 1877 scott st 1878 vasil 1878 oriole 1878 canon 1879 shbyl yshralefl 1880 debowa 1880 tap 1880 hautes 1880 xiong ye ji dao 1880 vogel 1880 nurul 1880 chau 1880 vogelsang 1881 mahallesi 1882 005 1882 d 52 1882 townline 1882 khyym 1883 cedar ln 1883 persimmon 1883 legacy 1884 d 57 1884 fairmont 1884 easton 1884 sobrinho 1884 zan 1885 palefrkh 1885 ireland 1886 moody 1886 ecuador 1886 majestic 1887 compj 1888 368 1888 pozzo 1888 cassia 1888 bandar 1888 southbound 1888 swallow 1888 noisy 1889 caceres 1889 s7 1889 harrison st 1889 ussuri 1889 dance 1890 dream 1890 vasconcelos 1891 kingdom hall of jeovahs witnesses 1891 ouled 1891 ceip 1891 sequoia 1891 luxem 1891 hoffmann 1891 chips 1891 marlborough 1891 zhiliznaia 1892 carmo 1892 evelyn 1892 n 6th st 1892 609 1892 bartlett 1892 qia 1892 n12 1892 marszalka 1892 nilo 1893 sturgeon 1893 battery 1893 piotra 1894 eel 1894 kazmunaigaz 1894 viking 1894 d 117 1894 dick 1895 brookwood 1895 g109 1895 violettes 1895 k 13 1895 platte 1896 ponce 1896 troncal 1896 pway 1896 pl de republiqu 1897 tecnica 1897 us 71 1897 kruis 1898 caribou 1898 kapellen st 1898 kis 1898 us 54 1898 cappella 1898 pirvomaiskii 1898 blackberry 1898 flur 1899 merced 1899 v alessandro volta 1899 v nazionale 1899 waterhole 1900 46n 1900 topaz 1901 aleflwtnyte 1901 sovkhoznaia 1901 mulhouse 1901 needles 1901 sisters 1901 zurich 1902 olga 1902 keikyu 1902 vinas 1902 educativa 1902 gore 1903 oyster 1903 outdoor 1904 jura 1904 priure 1904 schwimmbad 1904 449 1905 college st 1905 raionnaia 1906 petites 1906 ruchii 1906 951 1906 monts 1906 visconde 1906 bidea 1907 nicholson 1907 sady 1907 cromwell 1907 kommunistichiskaia 1908 hubbard 1909 outubro 1909 ss11 1909 86k 1909 steak 1910 beijing 1911 gilles 1911 amaro 1911 chance 1911 bellini 1912 aleflhsn 1912 atlas 1913 wood st 1913 augustin 1913 uritskogo 1913 arboretum 1913 division st 1913 coronation 1914 clearviw 1915 pertini 1915 avtozapchasti 1915 doctors 1915 125th 1916 k 11 1916 shrkte 1916 agias 1916 congress 1916 sioux 1916 juli 1916 alouttes 1917 land st 1917 bahn st 1919 gs 1919 r jean moulin 1920 thi 1920 pota 1920 luzia 1920 bway st 1920 shossiinaia ul 1921 cruzeiro 1921 97th 1921 almond 1921 alef 1922 malaga 1922 lmg 1922 n 3 1922 e 57 1922 deich 1922 borough 1923 smk 1923 geschwister 1923 secours 1923 burnside 1923 phone 1923 rua c 1923 shuiku 1924 figuiredo 1925 insel 1925 corniche 1925 rustic 1925 globus 1926 meisenweg 1926 karl st 1926 viola 1927 celso 1927 skate 1928 93rd 1928 twy 1928 kastaninweg 1928 moritz 1928 osage 1928 ypf 1928 meadowlark 1929 manhattan 1929 detroit 1929 g20 1929 grau 1929 485 1930 prados 1931 jln 1931 15th st 1931 tattoo 1931 fc roca 1932 borne 1932 641 1933 co operative food 1933 pch 1933 calhoun 1933 1944 1933 tnk 1934 conrad 1934 marga 1934 baikalo amurskaia magistral bam 1934 chino 1935 smiths 1935 esch 1935 arany 1935 greenville 1935 pound 1935 us 67 1936 vas 1936 fiat 1936 nuovo 1936 cycles 1937 shrqy 1937 primary school 1937 us 81 1937 41a 1937 property 1938 maas 1938 virgin 1938 song shan dao 1939 blaise 1939 ishan 1939 theodore 1939 brenner 1939 1 65 1939 herzog 1939 pearson 1939 bem 1939 sudring 1940 martin st 1940 klinika 1940 carranza 1940 abad 1941 tlwy 1941 bil 1941 viva 1942 060 1942 379 1942 hunting 1943 muhl st 1943 r des vignes 1943 molodizhna 1944 racine 1944 nepal 1944 combes 1945 aurelio 1945 awo 1945 fasanenweg 1945 miraflores 1946 mustang 1946 larga 1946 emiliano zapata 1946 clarendon 1946 garrison 1947 stanford 1947 cornell 1947 landgasthof 1947 silskii 1947 daimler 1948 williamson 1948 mtr 1948 grizzly 1949 sikorskigo 1949 wedgewood 1949 c real 1949 philadelphia 1950 oito 1950 brigada 1950 dijk 1951 542 1952 1 winkel 1952 kommun 1952 541 1952 partizanskaia ul 1952 457 1953 bells 1953 joyce 1953 pra 1953 hansalini 1953 autokinetodromos pathe 1953 e 261 1953 capri 1954 travis 1954 701 1954 leona 1955 106th 1955 simply 1955 drogeri 1955 lamar 1956 zhovtniva vulitsia 1956 r de lecole 1956 cardenal 1957 noi 1957 zabka 1957 achter 1958 hard 1958 prospikt linina 1958 inverness 1958 jidoshado 1959 hayden 1959 sparrow 1959 slowackigo 1959 pleasant st 1960 squirrel 1961 amherst 1961 academia 1962 monro st 1962 georgiou 1962 supplis 1963 w coast main ln 1963 180th 1963 whiteall 1963 a 23 1963 spacerowa 1964 bianco 1964 g50 1964 ledge 1964 forks 1964 476 1964 richter 1965 studios 1965 woodbine 1965 bellavista 1965 dnt oslo og omegn 1965 buffet 1966 clark st 1966 optica 1966 szecheni 1967 534 1967 ferinwohnung 1967 herz 1967 inacio 1967 drei 1967 erin 1967 lys 1967 distrito 1967 oratorio 1967 vt 1968 antiguo 1968 trift 1968 zhukova 1968 kendall 1969 190th 1969 94th 1969 e 95 1969 e 1st st 1969 r du pont 1969 mere 1969 629 1970 bergweg 1971 marriott 1971 359 1972 n 7th st 1972 e 97 1972 exxon 1972 cill 1973 permai 1973 secondaire 1973 cajal 1973 salamanca 1973 shack 1974 posada 1974 nobel 1974 peri 1975 cachoira 1975 kali 1975 tsvitochnaia 1976 df pl 1976 us 15 1976 slmalefn 1976 rc 1977 lena 1977 tengah 1977 quensway 1977 matriz 1977 yr 1977 v cavour 1978 crater 1978 beke 1979 sosnowa 1979 bnp paribas 1979 workshop 1979 s antonio 1979 graaf 1979 sarai 1980 tokaido hauptlini 1981 618 1982 04n 1982 couture 1982 562 1983 ordzhonikidzi 1983 antica 1983 levee 1983 johnson rd 1983 89th 1983 coronado 1984 n h 1984 mhor 1984 landevej 1984 hamid 1984 680 1984 474 1985 hillside dr 1985 hoofd 1985 lamartine 1985 66n 1986 luciano 1986 miramar 1986 546 1987 univ of cambridge 1987 ring rd 1988 province 1988 fu er mo sha gao su gong lu 1988 randall 1988 neun 1989 verein 1989 bangunan 1989 n 340 1989 fiori 1990 453 1990 meir 1990 footpath 1990 sable 1990 seohaan exp 1990 cr 1 1991 kramer 1991 credit mutul 1991 matiri 1991 pacifico 1991 henry st 1991 combattants 1991 northfild 1992 tui 1992 asuncion 1993 romaine 1993 cresent 1994 strato 1994 meer 1994 floyd 1994 ymca 1994 nabirizhna 1994 congregational 1994 westbahn 1995 regis 1995 cedres 1996 primero 1997 marin st 1997 oranje 1997 gali 1998 hermitage 1998 bartolomeo 1998 gourmet 1998 bowen 1999 gaz 1999 danils 1999 contorno 1999 schmid 1999 leroy 2000 driving 2000 associates 2000 glas 2000 stillwater 2000 fil 2000 554 2001 warung 2001 mos 2001 daono 2002 cool 2002 rty 2002 pineurst 2002 e 34 2002 noir 2002 a22 2002 kreis 2003 ahorn 2003 e coast main ln 2004 naval 2004 hopewell 2005 universite 2005 durango 2006 beltway 2006 uhland st 2007 right 2007 ogden 2007 bch rd 2007 seohaan 2008 agriturismo 2008 galp 2008 goat 2009 sovit 2009 konditorei 2009 speedway 2010 camelias 2010 mia 2010 ein 2010 passerelle 2010 chaparral 2011 martyrs 2011 jumbo 2011 champion 2011 tannen 2011 milwaukee 2011 arcadia 2013 grby 2013 novy 2013 arpad 2013 gardenia 2013 municipalidad 2014 burgerhaus 2015 victoria rd 2015 guzman 2015 united states postal service 2015 reuter 2015 k 16 2015 n 5th st 2015 hotels 2015 florist 2016 75k 2016 hicks 2016 affairs 2016 tenis 2016 auditorium 2016 ah14 2016 m7 2017 bussteig 2017 aziz 2017 reine 2017 cart 2018 sidney 2018 strp 2018 suzuki 2019 twns 2019 cicero 2019 huber 2019 luka 2020 pessoa 2020 ortsmitte 2020 zealand 2021 439 2021 bishops 2021 ruda 2021 refugio 2021 barreto 2021 plateia 2021 13th st 2021 zhovtniva 2022 14th st 2022 ploshchadka 2022 sporitelna 2022 wah 2023 antoni 2023 d 36 2023 haji 2024 sar 2024 040 2024 etsu 2025 alliance 2025 baum 2025 nha 2025 hegy 2026 sebastiano 2026 deutsches 2026 forestry 2026 info 2026 shossiinaia 2027 tilleul 2027 renato 2027 115th 2027 koopirativnaia ul 2028 ironwood 2028 postnl 2028 eric 2029 future 2029 ait 2029 llc 2030 omegn 2030 complexe 2030 464 2030 szabadsag 2030 933 2030 aquduct 2030 pini 2031 910 2031 kolner 2032 sons 2032 ss13 2032 572 2033 foyer 2033 a st 2033 p2 2033 r des tilleuls 2033 raccoon 2033 sandpiper 2033 rosirs 2034 sta rosa 2034 executive 2034 dusun 2035 root 2036 hurst 2036 spoor 2036 frost 2037 k 10 2037 philip 2038 gbr 2038 khribit 2038 dayton 2038 lindsay 2038 102nd 2039 eiche 2039 starbucks coffee 2040 engel 2040 v enrico fermi 2040 rira 2040 aster 2040 nunez 2041 98th 2042 bench 2042 lakeshore dr 2042 hoch st 2043 peel 2043 convent 2044 bains 2044 sihiy 2045 4e 2045 vom 2045 wanderpark 2045 cabane 2045 kazim 2046 517 2046 estacionamento 2046 massachusetts 2046 bug 2046 bwstalefn 2047 infante 2047 kant 2047 larch 2047 airfild 2047 xiii 2048 ranger 2048 taverna 2049 us 22 2049 oziornaia ul 2050 medeiros 2051 nice 2051 dim 2051 us 61 2051 orts st 2051 rowan 2051 k 12 2052 uchastok 2052 sanz 2053 book 2053 7th av 2053 491 2053 ochotnicza 2054 s2 2054 cheung 2054 da yan gao su 2054 sadova vulitsia 2054 casal 2055 coruna 2055 627 2055 granville 2055 connecticut 2056 ah7 2056 thorn 2057 lons 2057 maritime 2057 sma 2057 lukes 2058 637 2058 gostinitsa 2059 avery 2059 dussel 2059 r des acacias 2059 donato 2059 sfs 2059 lunacharskogo 2060 e 42 2060 rich 2060 e 87 2060 bound 2060 malvinas 2061 loreto 2061 plebania 2062 milan 2062 ospedale 2063 manning 2063 rhodes 2063 dvwy 2064 residencia 2064 107th 2064 istanbul 2064 blubird 2064 telefonica 2064 steakhouse 2064 pampa 2065 hook 2066 ebenezer 2066 halage 2067 ap 7 2067 balai 2067 d 33 2068 marathon 2068 us 169 2068 ica 2068 tail 2068 1 81 2069 richnaia ul 2069 sables 2070 358 2070 gabrile 2071 hondo 2071 sudbahn 2072 casale 2072 xa 2072 wb 2072 lazo 2073 gaosu 2073 rembrandt 2073 vintage 2073 antonius 2073 simone 2073 safeway 2073 baxter 2074 robbins 2074 winer st 2074 black cr 2074 lubeck 2074 indigo 2075 thuringer 2075 teal 2075 cruces 2076 elliot 2076 christine 2076 halls 2077 zara 2077 kwiatowa 2077 aviation 2077 knights 2078 486 2078 112th 2078 krakowska 2078 a 44 2078 naturschutzgebit 2079 kalefzm 2079 plana 2079 sixth 2079 d 38 2079 mohammed 2080 molenweg 2080 494 2080 zhisenv 2080 ul michurina 2080 biblioteka 2080 pidras 2081 pyrenees 2081 hartford 2082 wojska polskigo 2082 communes 2082 sutherland 2082 sadok 2082 lucin 2082 outeiro 2082 expreso 2082 ouro 2083 oy 2083 binh 2083 dn1 2083 classic 2084 florian 2084 thruway 2084 newtown 2084 rubens 2085 nagornaia 2085 v trento 2085 tsintralna vulitsia 2086 sunflower 2087 westside 2087 langer 2087 ah43 2087 landhaus 2087 keiin 2087 dart 2087 ems 2087 grappa 2088 handel 2089 ul maiakovskogo 2089 heinz 2089 stettiner 2089 d 37 2089 flowers 2089 sp2 2089 rai 2090 repustos 2090 438 2091 scoala 2091 peoples 2092 pvt 2093 27a 2093 pepper 2093 gard 2093 jr tokaido shinkansen 2094 funeral 2094 fruit 2095 vulitsa linina 2095 shelby 2095 pho 2095 coon 2095 us 84 2095 850 2096 632 2096 monsignor 2096 dahlia 2096 mixte 2096 hassan 2096 dexploitation 2096 nadrazni 2096 zilioni 2096 verne 2096 madison st 2097 kimberly 2097 genova 2098 477 2098 matos 2098 egnatia mwy 2098 mara 2099 payne 2099 praceta 2100 levada 2100 fuchs 2100 koopirativnaia 2101 leger 2101 arenas 2101 athina thessaloniki evzonoi 2101 corporate 2102 magic 2102 shkilna 2103 cheyenne 2103 ditalia 2104 dorfbach 2104 blum 2104 dundas 2104 hoyo 2104 k 8 2104 livingston 2104 yacht 2104 berge 2105 canti 2105 ul lirmontova 2105 w 4th st 2106 pasta 2106 billy 2106 baby 2107 bleuts 2107 544 2108 arroio 2108 manoir 2108 panda 2109 fliderweg 2109 camii 2109 tell 2109 hillcrest dr 2110 parki 2110 majada 2111 trafik 2111 konigsberger st 2111 dluga 2111 lisle 2111 kiln 2112 projetada 2112 alan 2112 rome 2112 rode 2112 hertz 2113 beauregard 2113 linia 2113 labbaye 2113 longviw 2113 regal 2113 giosu 2114 annes 2115 zhongno 2115 670 2115 danziger st 2115 ul svirdlova 2116 n 5 2116 metal 2116 podere 2117 d 50 2118 508 2118 fleuri 2118 sn 2118 noire 2118 garland 2119 woodstock 2119 v milano 2119 nazarene 2120 lino 2120 kralja 2120 colombir 2121 574 2121 rnde 2121 kotovskogo 2121 ngo 2122 shos 2122 n 1st st 2123 dh 2123 roaring 2123 652 2123 deportiva 2124 tobu 2124 clarks 2125 balmoral 2126 socite generale 2126 idaho 2127 fleet 2127 cuatro 2127 qalat 2127 lest 2127 backer 2127 ee 2128 e 4th st 2128 cream 2128 agro 2128 last 2129 dyr 2129 r1 2129 belo 2129 clarke 2129 eli 2129 juliusza 2129 souvenir 2129 birigovaia ul 2130 sp4 2130 e 3rd st 2130 garrett 2131 cabrera 2131 milk 2131 qur 2131 eureka 2132 moskva 2132 1200 2132 640 2132 kia 2132 6th av 2133 k 6 2133 sportif 2133 essen 2134 school ln 2134 108th 2134 culture 2134 monaco 2134 interna 2134 reforma 2135 surf 2135 mercury 2136 realschule 2136 norris 2136 barre 2137 bassin 2137 seafood 2137 erg 2137 presbytere 2138 france telecom 2138 gruner weg 2138 481 2138 caves 2139 ul gogolia 2139 thomas st 2139 zimnik 2139 master 2139 waterfront 2140 bates 2140 004 2141 sheikh 2141 katy 2141 strauss 2142 sta maria 2142 tuileri 2142 grounds 2143 deposito 2143 schulzentrum 2143 mermoz 2144 venelle 2144 hull 2144 dupont 2144 costanera 2144 osprey 2145 roux 2145 bakers 2145 valta 2146 benoit 2147 sdng 2147 ramsey 2147 giant 2147 jimmy 2148 merlin 2148 patricks 2148 postale 2148 bonni 2148 glowna 2148 romain 2149 massage 2149 kalefny 2150 vokzalnaia ul 2150 acker 2152 jasim 2152 rojas 2153 protoka 2154 keith 2155 langley 2155 amazon 2155 raleigh 2155 ingeniro 2156 departementale 2156 montrose 2156 yorkshire 2157 tito 2157 us 34 2157 teodoro 2158 902 2158 zeppelin 2158 stephen 2159 1 ah26 2159 rbra 2159 www 2160 stockton 2160 josefa 2161 karir 2161 drosselweg 2161 roth 2161 postbank 2162 puccini 2162 derrire 2163 virgilio 2163 michelangelo 2163 uniao 2163 roble 2164 vaux 2164 tiradentes 2164 sablons 2164 messina 2165 welcome 2165 katholische 2165 partizanskaia 2166 uganda 2166 korean 2166 lon 2166 ditskii sad 2167 bentley 2167 komsomolskii 2167 bashi 2167 rua b 2167 leau 2167 mz 2168 hummingbird 2168 whites 2168 glory 2168 mota 2169 spirit 2169 99th 2169 petrobras 2169 e 43 2170 conway 2170 mstshfalef 2170 dispensary 2170 bari 2171 days 2171 925 2171 mio 2172 r du lavoir 2172 bruhl 2173 353 2173 kn 2173 pq 2173 form 2173 storm 2173 mockingbird 2173 druzhba 2174 w 2nd st 2174 lake rd 2175 kingsway 2175 rattlesnake 2176 hyeon 2176 391 2176 terme 2176 risorgimento 2176 bungalow 2177 170th 2177 brigadir 2177 facultad 2178 dom kultury 2178 ctyd 2179 stations st 2179 us 85 2180 mariscal 2180 edgewater 2181 mist 2181 paraguay 2181 disem 2181 calderon 2181 pearl st 2182 mackenzi 2182 e 44 2182 kuibyshiva 2183 trees 2183 bismarck st 2184 lombard 2185 bowman 2185 wall st 2185 grave 2185 bbq 2185 vo 2185 437 2186 paloma 2186 arkansas 2186 schutzenhaus 2186 a16 2187 qsr 2187 rabochaia ul 2187 lz 2188 birken st 2188 r de chapelle 2188 aqua 2188 mad 2188 islas 2189 vladimira 2189 bair 2189 villanuva 2189 extra 2190 emergency 2190 commune 2190 cecil 2190 munitsipalnoi 2190 exeter 2191 northwood 2192 e 31 2192 sportivnaia ul 2192 negra 2192 kft 2192 abhainn 2193 k 7 2193 jonica 2193 rua 1 2193 lucio 2193 dana 2193 651 2193 621 2194 b 10 2194 ditiachii 2195 running 2195 violet 2196 ortega 2196 apartmani 2196 kettle 2196 103rd 2197 universal 2197 var 2197 gemeindeamt 2197 artur 2197 lung 2197 kirchengemeinde 2197 lau 2198 betty 2198 franciszka 2198 sananjhananraon 2199 sinkiwicza 2199 bac 2199 vitnam 2200 libraminto 2200 smith rd 2200 ton 2200 a anger 2200 aliksandra 2202 d 24 2202 aguila 2202 donna 2202 pkp 2202 us 301 2202 vin 2202 co ln rd 2202 team 2202 105th 2202 korea 2203 karen 2203 sadovi 2204 rite 2204 519 2204 niger 2205 alejandro 2205 shrine 2206 cod 2206 jadranska magistrala 2206 warschaur al 2206 tabernacle 2207 us 281 2208 mestre 2209 mogila 2209 pc 2210 captain 2211 calder 2212 avondale 2212 agia 2212 elementare 2212 526 2212 tsirkva 2213 hus 2214 lakeside dr 2214 cornwall 2214 r des lilas 2214 lancinne 2214 luxembourg 2215 saloon 2216 ashford 2216 novoi 2217 roja 2217 neustadt 2217 petofi sandor u 2217 marronnirs 2217 perimeter 2218 westviw 2218 qino 2219 us 45 2219 dachnaia 2219 rod 2220 446 2221 sleepy 2221 psh 2222 dong hai dao 2222 b 19 2222 early 2222 floriano 2222 fuller 2223 kirby 2223 bzrgralefh 2223 a27 2223 garde 2224 qing cang t lu 2224 pembroke 2225 dewey 2225 aguirre 2225 gaspar 2225 thunder 2226 mittelweg 2226 535 2226 pozarna 2227 caxias 2227 sweetwater 2227 paint 2228 mariana 2228 fernand 2228 bulivar 2228 nava 2230 dot 2232 milford 2233 brasilia 2233 formosa 2234 chemins 2234 claremont 2235 matki 2235 mahalle 2235 night 2235 cerisirs 2235 valles 2235 afon 2236 rn9 2236 howe 2236 625 2237 sandro 2237 lara 2238 hyundai 2238 born 2238 tropical 2238 mp 2239 cava 2240 prefeitura 2240 sinclair 2240 woodland dr 2241 brzozowa 2241 newman 2241 inlet 2241 1 maja 2242 balsam 2242 julho 2242 nikola 2243 cmentarz parafialny 2243 pine cr 2243 komsomolska 2244 dil 2244 tau 2244 boone 2244 sm 2245 athina 2245 bluberry 2246 dome 2247 n av 2247 rico 2247 367 2247 towne 2248 cnoc 2249 ur 2249 krigerdenkmal 2249 bello 2250 walden 2250 net 2251 vents 2251 559 2251 comunidad 2251 a 31 2251 v cristoforo colombo 2252 bc 2252 skvir 2252 como 2252 stevenson 2253 poznanska 2253 benito juarez 2253 524 2254 shamrock 2254 broken 2254 caldwell 2254 fryderyka 2255 lourenco 2255 parkwood 2255 383 2255 royale 2256 e2 2256 municipality 2257 dent 2257 susan 2257 cunningham 2257 grone 2258 mons 2259 retorno 2259 s juan 2259 gissen 2259 nichols 2260 n4 2260 berkeley 2261 barrett 2262 tsar 2262 joban 2262 baikalo amurskaia magistral 2262 cutoff 2263 oktiabrskii 2263 anatole 2263 shosi 2263 pascoli 2264 admiral 2264 452 2264 sem 2264 cabanas 2264 mayer 2264 alaska 2264 n 4th st 2265 r du general de gaulle 2265 kino 2265 gral 2265 514 2265 half 2266 kreuzweg 2266 lessing st 2266 avtomoika 2267 parsons 2267 attila 2268 baikalo 2268 m 10 2268 lerchenweg 2269 qubec 2269 mexican 2269 michala 2269 quintana 2269 conte 2270 italian 2270 moda 2271 81st 2271 802 2272 qmte 2272 gay 2272 muhlgraben 2272 ffw 2272 reedy 2272 roanoke 2273 sp3 2273 grenzweg 2273 faria 2274 simmons 2274 buchen 2274 38n 2274 carre 2275 usfs 2276 int 2277 wkwy 2277 kasteel 2277 baikal amur mainline 2278 tassigny 2278 isola 2279 owen 2279 lowen 2279 ib 2280 chimney 2280 garazhi 2280 zhui 2281 taft 2281 rotes 2281 nh48 2281 brushy 2281 298 2282 nord st 2282 myers 2283 840 2284 pad 2284 raccordo 2285 gazprom 2285 shanghai 2286 terrasse 2286 automotive 2286 watershed 2287 73rd 2288 parkhaus 2288 blake 2288 viwpoint 2288 omar 2289 ffcc belgrano 2290 id 2291 fowler 2291 olde 2292 industria 2292 615 2292 444 2293 us 441 2293 tol 2293 cabezo 2294 d 34 2294 navarro 2294 378 2295 us 87 2296 pilgrim 2296 mtainm 2297 bread 2297 comercio 2297 mountains 2297 weser 2297 459 2298 77th 2298 molen st 2299 buch 2301 b2 2301 lattre 2301 osteria 2301 catano 2302 mura 2303 ul stroitilii 2303 juin 2303 freire 2303 elbe 2304 bright 2305 tiroler 2306 od 2306 pirimogi 2306 oktyabrskaya st 2306 cadiz 2306 uhland 2306 wade 2306 bremen 2307 aleflmrkz 2308 richnaia 2309 eldorado 2309 leopardi 2309 opet 2309 rimont 2309 penduduk 2311 ministerio 2311 pour 2312 ritter 2312 prima 2312 kingfisher 2312 long hai xian 2313 eco 2314 b3 2314 gosudarstvinni 2314 av de gare 2314 bassa 2314 famiri mato 2314 poczta polska 2315 e 2nd st 2315 archer 2316 electronics 2316 us 2 2316 bosc 2317 waverly 2317 patton 2318 bog 2318 lakowa 2318 ship 2318 coteau 2319 lavoro 2319 dominos pizza 2319 mlynska 2320 sosh 2320 millers 2321 clermont 2321 beg 2322 stanton 2322 governor 2322 751 2322 rua a 2323 12th st 2323 d 29 2323 355 2323 famiri 2323 gory 2323 telephone 2324 schafer 2324 c 17 2324 91st 2324 internet 2325 denver 2326 truong 2326 k 4 2327 breakfast 2327 calea 2328 nash 2329 brito 2329 mercer 2329 lowell 2330 n 3rd st 2330 driftwood 2331 shady ln 2331 direction 2331 lowes 2331 transcanada hwy 2332 cmdt 2332 horton 2332 kani 2332 lanes 2333 lim 2333 serre 2333 96th 2333 frans 2333 nizhniaia 2334 363 2334 bebel 2334 cottonwood cr 2334 libertador 2335 dunes 2335 biloi 2335 kara 2336 sud autobahn 2336 sirvis 2336 gull 2336 sbirbank rossii 2336 nl 2336 88th 2338 dulo 2339 kamin 2339 clear cr 2339 grun 2340 kirchen 2340 march 2340 wijska 2340 gabrila 2340 steinweg 2340 b 51 2341 vodafone 2341 norwich 2342 454 2342 schumacher 2342 clare 2343 sanders 2345 benedetto 2345 shoal 2346 bolton 2346 segunda 2347 eemalige 2347 lagos 2347 lebanon 2348 savannah 2349 benson 2349 eagles 2350 telegraph 2350 maintenance 2350 vers 2350 leipziger st 2350 b 54 2350 us 10 2350 dwm 2351 argyle 2351 barrage 2351 nh27 2351 aleflshy 2352 dorchester 2352 linin uramy 2352 oziornaia 2353 obispo 2353 dworcowa 2353 gary 2353 d 27 2354 466 2354 jaume 2355 shkolni 2356 todd 2356 nishi 2357 s jose 2357 deer cr 2357 pear 2357 gimnazjum 2358 morningside 2359 bonifacio 2359 frontera 2359 automobile 2359 11a 2359 staroi 2360 rossa 2360 nascimento 2360 horizonte 2360 boucheri 2360 kintetsu 2360 vimuzu 2361 armand 2361 jr shan yang ben xian 2362 angela 2362 wilson st 2362 nova poshta 2363 635 2363 algeria 2363 erables 2363 school rd 2363 esmeralda 2363 vittoria 2365 europe 2365 hume 2365 locust st 2366 buisson 2366 quincy 2366 ul frunzi 2366 voltaire 2367 bryan 2368 1 64 2369 zapadni 2369 atlanta 2369 schulhaus 2369 scotland 2370 bogoroditsy 2370 a38 2370 adams st 2371 473 2371 alamos 2371 675 2372 abdul 2372 bicycle 2373 volcan 2373 atlantico 2373 p1 2373 charli 2374 pestalozzi 2374 self 2375 frinds 2375 nn 2376 ul chkalova 2376 allison 2377 covered 2377 riva 2377 hubertus 2377 iles 2377 niagara 2379 midi 2379 alton 2379 1e 2379 69th 2379 elliott 2379 perrache 2379 riacho 2380 appalachian 2380 richards 2380 pk pl 2380 autov del mediterraneo 2381 chavez 2382 622 2382 harold 2382 privata 2382 horne 2382 bean 2383 76th 2383 ronald 2383 s11 2384 rodolfo 2385 morais 2385 n 2nd st 2386 bauhof 2386 rabobank 2387 brock 2387 kancelaria 2387 coin 2387 hangar 2387 kirkko 2387 raja 2388 grenoble 2388 estacionaminto 2388 dominguz 2388 under 2389 juana 2389 ethnike 2390 606 2390 redondo 2390 av brasil 2391 kern 2392 seco 2392 parkovaia ul 2392 92nd 2393 http 2393 charter 2394 369 2394 mah 2394 104th 2395 aparecida 2396 gorodok 2396 video 2396 scholl 2397 d8 2397 mesjid 2398 karola 2398 heideweg 2399 limestone 2400 altos 2400 tourisme 2400 guillaume 2400 a30 2401 ainmr 2401 granges 2401 praspikt 2401 esperanca 2402 m 4 2402 allotments 2403 intirnat 2403 tc 2403 transcanada 2404 beni 2404 crestwood 2405 dresden 2405 alefyshalefn 2405 mille 2406 eisenhower 2407 436 2407 cuisine 2407 lage 2408 1 44 2408 bridle 2408 jbalefl 2409 be 2409 tam 2410 gasteaus 2410 save 2410 global 2410 m 7 2411 steinbruch 2412 firma 2412 konigsberger 2412 libre 2413 pathe 2413 giardino 2413 greater 2414 516 2415 ez 2415 stipana 2416 vatutina 2417 bilbao 2417 warschaur 2418 1o 2419 us 29 2419 r st 2419 465 2420 kaufland 2421 418 2421 pyeong 2422 grant st 2423 d 23 2424 455 2425 project 2425 wanderweg 2425 sousa 2425 nouvelle 2426 hermosa 2426 piatirochka 2426 poplar st 2427 communaute 2427 albrecht 2428 gandhi 2429 gr army of republic hwy 2429 elizabeth st 2429 1918 2430 jardines 2430 rosedale 2430 419 2431 qiang 2431 571 2432 zigelei 2432 campsite 2434 eglise st martin 2435 dunant 2436 n 20 2436 funtes 2437 vent 2437 orts 2437 velha 2438 charleston 2438 crss st 2438 d 39 2438 ejido 2438 parkowa 2439 mukhaviramay 2439 wells fargo 2439 490 2439 fam 2439 sams 2439 s1 2440 krajowej 2440 oakdale 2441 deportivo 2441 resistance 2441 386 2442 hammond 2442 ottawa 2443 troy 2443 wheel 2443 giulio 2443 produkti 2444 wilhelm st 2444 sydlyte 2445 seymour 2445 1 25 2445 sussex 2445 pascal 2445 marti 2446 c 15 2446 ligne principale tokaido 2446 stacja 2446 cn 2446 brandt 2447 vidal 2447 vostochni 2447 lake st 2447 k 5 2448 sofia 2448 faro 2449 veterinary 2449 comandante 2449 sviatogo 2449 ger 2449 ady 2449 shawnee 2451 dong ming gao su dao lu 2451 morales 2452 kapellenweg 2452 albert st 2453 e 79 2453 nan jiang xian 2453 tak 2453 castell 2453 pousada 2453 reid 2454 vokzal 2454 solutions 2455 dennis 2456 oktyabrskaya 2457 e 115 2457 first baptist church 2458 570 2458 piter 2458 catarina 2458 deputado 2458 hawkins 2459 progreso 2460 prong 2460 populaire 2461 jasper 2461 bank of america 2461 xx 2462 zhong guo zi dong ch dao 2462 521 2463 lemon 2464 leandro 2464 birigovaia 2465 hat 2465 selva 2465 petroleum 2466 moos 2466 arm 2467 dyke 2467 r jean jaures 2468 arodromo 2468 osaka 2468 osborne 2469 statale 2469 b 9 2469 cno real 2470 346 2470 74th 2471 jalefsm 2471 coles 2471 franken 2471 smart 2472 biao 2473 c 16 2473 guilherme 2473 ninos 2473 recycling 2473 362 2474 uk 2474 pinnacle 2474 s8 2475 over 2475 pansion 2475 604 2476 fondo 2477 n 630 2477 d 32 2478 dolphin 2478 willi 2479 388 2479 429 2480 camin 2480 wren 2480 390 2482 n 10 2482 lyons 2482 honeysuckle 2482 boy 2483 w 3rd st 2484 ellen 2485 frid 2485 sdot 2486 560 2486 simpang 2487 sources 2487 chasovnia 2488 pamplona 2490 alabama 2490 steel 2490 deus 2491 bremer 2491 cemetary 2491 bolz 2491 richland 2492 johan 2492 us 36 2492 quick 2492 malga 2493 devonshire 2493 connection 2493 chippewa 2493 bud 2493 ulrich 2493 sali 2494 shivchinka vulitsia 2494 bergen 2495 rosales 2495 328 2495 d 35 2495 neckar 2495 kua 2496 adama mickiwicza 2496 target 2497 85th 2498 mhmwd 2498 georgetown 2499 pozharnaia 2500 grotta 2500 801 2502 cabinet 2502 ferrari 2502 just 2503 orchid 2503 dawn 2503 garth 2505 medio 2505 ep 2506 pasture 2506 vokzalnaia 2506 jenkins 2506 eastwood 2506 correia 2507 wasserturm 2507 patricia 2507 vine st 2508 aroporto 2509 750 2510 berkshire 2511 v leonardo da vinci 2512 skala 2513 ndr 2513 birch st 2514 ayuntaminto de madrid 2514 fresno 2514 leipzig 2514 townsend 2514 liniia tokaido 2514 kotilnaia 2514 tokaidolinjen 2515 nikolaia 2515 saleflh 2515 rodeo 2515 antioch 2515 jibal 2516 urad 2516 signal 2517 offices 2517 tenente 2517 linea principale tokaido 2517 b 96 2517 n7 2517 zola 2518 brookfild 2518 b 5 2518 penas 2518 panther 2519 danziger 2519 2611606 2519 quarter 2519 superiore 2520 hli 2521 geneva 2521 faculty 2522 mimosa 2522 518 2522 forestale 2523 gn st 2523 guo dao2 hao 2523 d 28 2523 breslaur st 2523 finca 2524 608 2524 sacred 2524 dalmatina 2524 rodrigo 2524 johnston 2524 623 2525 silo 2525 bb 2525 sydney 2525 witnesses 2525 brewery 2525 devils 2525 rive 2525 camillo 2525 petro 2525 mckenzi 2525 peterson 2525 g60 2527 duc 2527 steinbach 2527 willow st 2527 dee 2528 stor 2528 pfarrkirche 2529 fundamental 2529 nursing 2530 metz 2530 n8 2531 bili 2531 e 71 2531 almacen 2531 arte 2532 schacht 2532 flamingo 2533 339 2534 hopkins 2534 noguira 2535 b 85 2535 shan yang zi dong ch dao 2536 annex 2537 alex 2537 bomberos 2538 vito 2538 heuss 2538 smith st 2538 lynch 2539 howell 2539 langen 2540 rossini 2540 wilhelmina 2540 abbas 2541 tunis 2541 margherita 2541 eix 2542 tanglewood 2542 phu 2543 catolica 2544 abraham 2544 junta 2544 burgos 2545 fairfax 2545 d 26 2545 lead 2545 561 2545 140th 2546 g22 2547 tannenweg 2547 korte 2548 kk 2549 libreria 2549 fabrika 2549 jadranska 2550 macedonia 2550 burlington northern sta fe r 2550 pak 2551 miles 2551 deutsche telekom 2551 fast 2552 exupery 2553 us 31 2553 eb 2554 ss12 2555 160th 2555 big cr 2555 felice 2555 tokaido honsen 2556 n 634 2556 kay 2556 hoover 2556 dolmen 2557 g75 2557 terres 2557 83rd 2557 palmetto 2558 viana 2558 uruguay 2558 tsun 2559 abreu 2559 sportowa 2560 rh st 2560 820 2560 lido 2561 wildcat 2564 hampshire 2565 jidosha 2565 ptt 2566 crril 2567 fortuna 2567 3e 2568 pasir 2568 mans 2568 dirk 2568 us 89 2569 colombia 2569 sargento 2570 mhl 2570 lab 2570 macarthur 2571 edmond 2571 kaminka 2571 sklad 2572 366 2572 zilona 2572 11k 2572 witten 2572 discovery 2573 systems 2573 middleton 2573 d 30 2573 ridgeviw 2573 95th 2573 buckeye 2574 krasnaia ul 2574 dnt 2574 m 5 2574 pacific hwy 2574 dunn 2575 e 52 2575 engilsa 2575 kosmonavtov 2576 woodward 2576 margarita 2576 iar 2577 verona 2578 matrosova 2578 caja 2579 356 2579 gates 2579 torgovi 2579 sitesi 2579 sdr 2580 296 2580 jr tokaido honsen 2580 farmacias 2581 greene 2581 nur 2581 andes 2581 mango 2581 rousseau 2582 federation 2582 ines 2583 downtown 2583 robles 2584 rental 2586 virkhniaia 2586 barrow 2586 viii 2586 d 16 2587 bak 2587 baja 2587 607 2587 rata 2588 slate 2588 ytskhq 2588 maloi 2588 dudley 2588 urquiza 2590 e 49 2590 463 2590 emmanul 2590 said 2591 555 2591 527 2592 collado 2592 klaus 2592 ul dzirzhinskogo 2592 valley rd 2595 melbourne 2595 lote 2595 forest rd 2595 ada 2596 coll 2596 money 2598 osrodek 2599 esc 2599 peatonal 2599 v alessandro manzoni 2601 lees 2601 snake 2601 potter 2602 22n 2602 426 2604 443 2604 150th 2605 zhk 2605 baseball 2605 luke 2606 patisseri 2606 platform 2609 abel 2609 hwr 2609 straz 2609 k 3 2610 61st 2610 serrano 2610 jzyrte 2611 nol 2611 humberto 2611 tanjung 2612 c 14 2613 camara 2613 tim hortons 2613 jalefdte 2613 oster 2614 swq 2614 briand 2614 cure 2616 bali 2616 v cesare battisti 2617 vermont 2617 n 6 2618 r des jardins 2618 cem 2618 volga 2618 d 22 2620 gospel 2620 ss16 2620 hillviw 2620 jasmine 2621 ix 2622 avto 2622 glasgow 2623 booth 2623 otdykha 2624 hendrik 2624 zongtuop 2624 440 2624 68th 2625 dvorits 2625 borda 2625 polideportivo 2625 mineral 2625 noro 2625 lessing 2625 sarah 2625 amaral 2625 lobo 2626 dworzec 2626 zaklad 2628 baseline 2628 79th 2628 carducci 2628 pigeon 2630 halifax 2631 wasserwerk 2631 jupiter 2631 brigade 2631 waterford 2632 kolping 2632 ace 2632 cloud 2632 bania 2632 finch 2632 v alcide de gasperi 2633 pollo 2633 level 2633 era 2633 abajo 2634 ash st 2634 anchor 2635 bustan 2635 koscilna 2636 saratoga 2636 osp 2637 86th 2638 broad st 2638 vicarage 2638 mgr 2639 nine 2639 jeovahs 2639 n rd 2639 mijski 2640 reserva 2640 dunkin donuts 2640 rtt 2640 dong hai dao xin gan xian 2640 pinar 2640 burke 2641 willy 2641 506 2641 cobb 2641 pat 2641 heine 2641 130th 2643 friseur 2643 fermi 2643 valentin 2643 orion 2644 rapids 2645 gijon 2647 us 69 2647 cranberry 2648 brooklyn 2648 frankfurter st 2648 sergio 2648 pochtovoi 2649 rhin 2649 quality 2649 oblast 2649 kolonii 2650 roi 2650 dodge 2651 av du general de gaulle 2651 348 2651 peluquria 2651 fit 2651 fridrich st 2651 eneosu 2651 cortes 2651 tavares 2651 reading 2652 tung 2652 abzweigung 2653 cantabrico 2653 vul 2653 bpost 2653 ot 2654 m6 2654 382 2654 sportivo 2654 claro 2654 gardner 2654 koltsivaia 2655 volkswagen 2655 comfort 2655 princes hwy 2655 muzeum 2656 mlyn 2656 613 2656 tucker 2657 rv 2657 gol 2657 amurskaia 2658 sara 2658 ang 2658 schmide 2658 vallon 2658 mansfild 2659 1100 2659 bun 2660 84th 2660 vlt 2661 551 2661 krotka 2662 seminole 2663 knight 2663 gartenweg 2663 powstancow 2664 361 2664 cristina 2664 amelia 2665 hofer 2666 ginirala 2666 jr tokaido main ln 2666 aid 2666 diksi 2666 dalton 2666 64th 2666 americo 2667 r victor hugo 2667 weiss 2667 patio 2667 vineyard 2667 kreissparkasse 2668 nea 2670 shuanghwan 2671 284 2672 vallejo 2672 tulip 2672 boots 2672 rapid 2672 461 2673 roc 2674 whitney 2676 toilet 2676 euro 2676 nedre 2676 lesli 2676 neck 2677 rabochaia 2677 labbe 2678 byron 2678 reno 2679 hoa 2679 caisse depargne 2679 amazonas 2679 292 2680 10a 2680 grassy 2682 departamento 2682 surrey 2683 solar 2683 tara 2684 practice 2684 deerfild 2684 sankanonlasan 2684 seis 2685 rugby 2686 only 2686 451 2686 nurn 2687 1 94 2687 canto 2687 jacks 2689 dorp 2689 lark 2690 kik 2690 zapadnaia 2690 forte 2691 multi 2692 australia 2692 pice 2693 sainsburys 2693 e 28 2693 sht 2693 control 2694 gustavo 2694 hr 2694 barker 2695 stock 2695 app 2696 macdonald 2696 albion 2696 dvd 2696 radianska vulitsia 2697 ashland 2697 495 2698 kaplica 2699 gloucester 2699 bmw 2699 planet 2700 kafr 2700 468 2701 chung 2701 gutirrez 2701 riley 2702 maharlika 2702 dalefr 2702 531 2703 pires 2704 sacramento 2705 carol 2705 wakefild 2706 tamarack 2706 mutul 2707 audi 2707 cedar cr 2708 475 2708 bandera 2708 tsvmt 2709 fregusia 2709 roches 2709 warszawska 2710 us 14 2711 rossi 2712 v tri ste 2712 novimbre 2713 normandi 2713 mitro 2713 austria 2714 11th st 2714 rent 2714 staraia 2714 venta 2715 384 2715 reef 2718 montreal 2719 445 2721 nil 2721 prud 2722 highland av 2722 authority 2723 mama 2723 tsintralna 2724 galilei 2724 diner 2725 lama 2725 mntqte 2725 william st 2727 roof 2727 oao sbirbank rossii 2727 rodovia governador mario covas 2727 533 2728 712 2729 otdil 2729 kapellen 2730 78th 2730 rifugio 2731 hung 2731 munoz 2731 pilot 2731 unicredit 2731 trailhead 2732 franklin st 2732 franka 2733 vr 2733 k 2 2733 linina vulitsia 2734 kirk 2734 santi 2734 energy 2735 hub 2735 foundation 2736 423 2736 ioannina 2736 buno 2736 commonwealth 2736 vergers 2736 kok 2737 shops 2737 kea 2738 abalefr 2739 gladstone 2739 wok 2739 hoge 2739 comte 2740 rectory 2740 sanatorii 2740 marble 2740 2b 2741 helene 2741 napoleon 2742 everett 2743 wentworth 2744 estancia 2744 sekolah 2745 ponds 2745 aceq 2745 genets 2745 headquarters 2746 presa 2746 n9 2746 gorki 2747 mandir 2748 e 59 2748 dutra 2749 hipolito 2749 c 13 2749 fifth 2749 edith 2750 beira 2750 adirondack 2751 357 2751 d 21 2752 inner 2752 mundo 2752 carneiro 2753 eau 2754 far 2755 tabor 2755 senador 2755 66th 2756 424 2757 caroline 2757 r de republiqu 2757 ferro 2757 71st 2758 lapangan 2758 frances 2758 clube 2759 kfr 2759 snd 2759 oji 2760 rika 2761 nikolaus 2761 ainbalefs 2762 euclid 2762 pays 2762 new rd 2762 vegas 2764 finkenweg 2765 african 2765 seine 2766 talbot 2766 etxea 2766 herman 2768 cham 2768 blumen st 2769 mateo 2769 tunel 2770 374 2770 lisa 2771 purple 2771 523 2772 gmina 2772 keystone 2773 cuba 2774 82nd 2774 pk dr 2775 benton 2775 brandon 2775 dead 2776 sopra 2777 433 2777 td 2778 alphonse 2778 fridrich ebert st 2779 010 2779 rescu 2779 jcdecaux 2779 munster 2779 child 2780 337 2780 leopoldo 2781 ds 2781 416 2782 d 18 2783 aropurto 2783 hala 2783 obwodnica 2783 lomonosova 2784 kath 2784 frtg rd 2785 salto 2785 brick 2785 xiao huo shuan 2785 sakuruk 2786 78n 2787 lisnoi 2788 horst 2788 020 2788 448 2789 mawatha 2789 trent 2790 state st 2790 e 66 2791 lenin 2791 camargo 2792 huilyp 2792 sylvan 2793 salinas 2793 coulee 2793 pacheco 2793 ukrainki 2795 kolkhoznaia ul 2796 llanos 2797 67th 2797 kocesi 2798 harper 2798 1 15 2799 jeronimo 2799 guillermo 2800 osvaldo 2800 dk 2800 jungang 2801 umbria 2803 barclays 2803 580 2803 chris 2803 croissant 2804 us 77 2804 n3 2804 battle 2805 cir k 2805 90th 2805 breslaur 2806 87th 2806 sh 1 2806 roson 2807 lloyds 2807 434 2808 un 2808 thanh 2809 cs 2809 weaver 2810 bent 2810 toronto 2810 472 2812 shed 2812 avila 2812 krupskoi 2813 hardy 2813 allah 2813 336 2813 hannover 2813 kossuth lajos u 2813 16k 2814 randolph 2814 komarova 2814 civic 2815 tokaido shinkansen 2816 paribas 2816 system 2816 ion 2816 v vittorio veneto 2817 a11 2817 shchorsa 2817 clearwater 2819 gazpromnift 2819 misty 2820 rs 2821 ferguson 2821 g42 2822 peixoto 2822 news 2823 buckingham 2824 mesanges 2824 552 2824 377 2825 oklahoma 2825 liverpool 2825 agnes 2825 376 2826 washington av 2827 brink 2827 bolivia 2827 hortons 2827 primera 2828 maxwell 2828 n 1 2829 631 2829 moto 2829 trinidad 2830 parkovaia 2831 65th 2831 batu 2831 times 2831 fling 2832 ashton 2832 victoria st 2832 autokinetodromos 1 2833 petits 2834 endre 2835 albu 2835 rosenweg 2836 monastery 2837 lisboa 2837 lincoln av 2838 shykh 2838 krasnoi 2841 abar 2841 eras 2842 granj 2843 passagem 2843 ignacego 2844 newcastle 2844 transport 2845 residential 2846 d 25 2848 moines 2848 creche 2849 bra 2849 swift 2849 obshchiobrazovatilnaia 2850 siniri 2852 rosnift 2852 minami 2853 shbyl 2853 liquor 2853 clemente 2853 madero 2854 highfild 2854 fetes 2854 metropolitan 2854 panama 2855 ala 2856 e 62 2856 gartnerei 2858 hirsch 2858 stefano 2858 radweg 2858 nissan 2858 rica 2858 d 31 2859 rds 2859 railroad av 2859 e 85 2860 oval 2860 montes 2860 trade 2861 erich 2861 540 2863 ath 2863 wolnosci 2863 fine 2864 carrire 2864 yellowhead 2864 eni 2864 laundry 2864 shepherd 2866 beethoven st 2866 chatham 2867 groupement des mousqutaires 2867 sanctuary 2867 gamle 2868 ceska posta s p 2868 camilo 2869 elgin 2870 wis 2871 dunkin 2872 bidronka 2872 venus 2872 nfd 2872 jerusalem 2872 dolni 2873 cabeza 2874 vision 2874 fosses 2874 abzw 2874 mound 2876 a 66 2876 maine 2878 esteban 2878 ahmed 2879 g6 2879 matthews 2880 willis 2880 honam 2880 picasso 2880 earl 2880 krankenhaus 2881 cerrada 2882 59th 2882 basica 2883 indian cr 2885 agence 2888 jalefmain 2888 hastings 2889 kazimirza 2890 tennessee 2890 rochester 2890 jefferson st 2891 internal 2892 marzo 2892 cartir 2892 grotte 2895 eaton 2896 els 2896 villeneuve 2896 wolfgang 2896 pol 2897 pal 2897 cepsa 2898 gebaude 2898 univirsitit 2898 koln 2899 kim 2900 soccer 2900 us 19 2900 perkins 2902 nikrasova 2902 fitzgerald 2902 evangelical 2902 mississippi 2903 brunswick 2903 whdte 2904 niuw 2904 volksschule 2904 482 2905 mayfild 2905 sands 2905 cala 2907 faydat 2907 414 2907 rid 2908 saules 2908 edgar 2908 58th 2909 snack 2910 salvatore 2910 internacional 2910 dsa 2911 freeman 2911 326 2911 343 2912 filial 2912 rocca 2913 sucre 2914 gc 2914 allt 2914 german 2915 style 2915 isolato 2916 dao1 2916 bow 2916 baden 2916 sin nombre 2917 owens 2918 chikhova 2918 buchanan 2918 magalhas 2919 piano 2919 c a 2919 clarence 2919 security 2920 creekside 2920 deutschland 2921 lugar 2922 citron 2922 technical 2922 nantes 2924 sloboda 2924 breite 2924 rj 2925 v piave 2925 arizona 2926 shirley 2926 62nd 2927 pb 2927 ramirez 2928 v giuseppe verdi 2928 ensino 2929 muddy 2930 cliniqu 2931 72nd 2931 elder 2932 squaw 2932 venezula 2933 vecchio 2934 dozsa 2934 salz 2934 zun 2935 herrera 2936 champagne 2936 monterey 2936 fred 2937 bei lu zi dong ch dao 2938 meadowbrook 2940 hector 2940 s p 2940 kirchgasse 2940 romano 2940 drake 2941 andalucia 2941 d 19 2941 meadow ln 2941 gn ln 2942 sebunirebunziyapan 2942 forsthaus 2943 chelsea 2943 raimundo 2943 treatment 2943 badger 2943 gaststatte 2944 jacobs 2945 bern 2945 ravel 2946 organismo descentralizado de secretaria de educacion publica 2947 braga 2947 drove 2947 hora 2947 pain 2947 zamora 2947 ui 2948 worth 2951 schutzen st 2951 mega 2951 cecilia 2951 b 7 2952 327 2952 lions 2953 blair 2954 aleflsyalefr 2955 dolores 2955 329 2956 philippe 2957 ram 2958 mol 2958 aristide 2959 us 59 2959 harding 2959 moskovskaia ul 2960 901 2960 humboldt 2960 435 2961 paddock 2961 isaac 2961 sede 2961 donau 2962 azevedo 2962 enel 2962 markaz 2962 80th 2963 lincoln st 2963 bethleem 2963 maryland 2965 wal 2965 beaverdam 2965 bairro 2965 mac 2965 nassau 2966 334 2967 thames 2967 birchwood 2968 getulio 2969 hahn 2970 autoroute est oust 2970 612 2971 robertson 2971 depargne 2972 schwarzer 2973 jade 2973 postweg 2973 legion 2973 peugeot 2974 100th 2974 509 2974 lts 2975 janeiro 2975 dorps st 2977 v europa 2977 out 2977 d 17 2978 pelican 2978 tamoil 2979 smp 2979 geng 2979 comune 2980 oxxo 2980 barry 2980 cabana 2980 coto 2980 links 2980 cars 2981 m5 2982 53rd 2983 sheep 2984 radar 2984 ovest 2984 ls 2985 norma 2986 511 2987 raven 2988 rivadavia 2988 clyde 2989 silska 2989 v antonio gramsci 2990 horizon 2991 weingut 2991 centenario 2992 claudio 2992 grocery 2994 512 2995 greens 2995 bbva 2995 groupement 2996 gr r 2996 calvario 2996 kolkhoznaia 2996 fonseca 2996 adelaide 2996 tiger 2997 57th 2997 9a 2998 settembre 2999 pentecostal 2999 g55 3000 sp1 3000 chandler 3001 shrq 3001 brand 3003 expwy 3003 teninte 3003 emil 3004 christopher 3004 cidade 3005 privee 3005 prisviatoi 3005 millennium 3006 plain 3006 jalefdh 3007 20n 3008 lhotel 3009 5th av 3009 carpenter 3009 maple av 3009 003 3009 d 11 3010 opel 3010 maplewood 3010 602 3012 madeleine 3013 chast 3013 75th 3014 brucken st 3015 erlenweg 3015 motte 3015 usps 3015 aa 3019 uchrizhdinii 3020 fletcher 3020 adventist 3020 mode 3020 ss1 3021 leeds 3022 ibis 3023 mittel st 3023 291 3023 toulouse 3024 dutch 3024 dorado 3024 taco bell 3024 orleans 3025 will 3025 bethany 3026 mousqutaires 3026 guten 3029 us 17 3029 hammer 3030 532 3031 530 3033 horni 3034 110th 3034 kids 3035 513 3036 banks 3036 g56 3036 373 3037 grupo 3038 gurra 3040 galileo 3040 lloyd 3040 puy 3040 roch 3040 waterfall 3040 d 9 3041 vigne 3041 41k 3042 dao2 3043 aurelia 3043 continental 3044 hanover 3044 rosen st 3045 ul pobidy 3045 norwood 3045 muhlenbach 3046 n 7 3047 brentwood 3049 427 3049 spruce st 3049 grade 3049 kirch pl 3049 schnell 3050 bismarck 3051 mines 3052 65k 3052 v giacomo matteotti 3054 bond 3056 e 01 3056 margurite 3057 vostochnaia ul 3057 cultura 3057 br 116 3058 flint 3059 sullivan 3059 bnp 3059 reina 3061 litsii 3062 gallo 3062 wisconsin 3062 settlement 3063 kfz 3063 lisi 3064 clubhouse 3065 rgwy 3065 antelope 3066 lilac 3066 koulu 3066 crestviw 3066 yves 3067 dry cr 3067 claire 3067 hamburger 3068 speed 3068 clemenceau 3070 lazy 3070 vija 3071 hrb 3071 us 24 3071 gammel 3071 small 3071 wisengrund 3072 birken 3073 statu 3074 moura 3075 empire 3076 beau 3077 darc 3077 hohenweg 3078 apollo 3078 hale 3078 830000 3079 k 1 3079 becker 3081 bee 3081 alefl 3081 ponderosa 3081 us 52 3081 d 20 3083 fydte 3083 cash 3083 charles st 3084 349 3084 breeze 3086 ufer 3086 molina 3087 sente 3087 posiolok 3088 basketball 3088 joy 3088 268 3089 cir dr 3089 body 3089 oknoname 3090 wv 3090 gorka 3090 lile 3091 nkhl 3091 us 90 3092 eck 3092 b st 3093 gatano 3093 mwy 1 3093 mckinley 3095 hermes 3095 former 3096 kollidzh 3096 poczta 3096 upravlinii 3096 mozart st 3097 sodu 3098 voda 3099 onze 3099 345 3099 408 3099 panny 3100 bldg 3100 leipziger 3101 shiloh 3103 650 3104 szkol 3105 ns 3106 kyrka 3106 quatro 3106 mohawk 3107 288 3108 kinder 3109 bruch 3109 curry 3109 sitio 3110 hancock 3110 short st 3111 wagon 3111 poliana 3111 regency 3111 javir 3111 radianska 3111 gsk 3112 ffordd 3113 297 3114 salisbury 3114 qlainte 3114 briarwood 3115 ts 3116 freibad 3119 375 3120 e1 3120 billa 3122 british 3122 marshala 3122 addaviramadda 3122 279 3123 510 3123 primavera 3123 4th av 3123 hsl 3124 bajo 3125 estado 3125 kolejowa 3126 mon 3127 universitat 3127 race 3129 toro 3132 missouri 3132 nao 3133 corners 3133 agency 3133 alonso 3134 wales 3136 a 5 3137 estero 3138 nancy 3139 c 11 3139 martha 3141 page 3141 kota 3141 jimenez 3141 sportivnaia 3141 twrs 3141 coleman 3141 ard 3142 409 3142 rabbit 3142 puig 3142 freedom 3143 tw 3143 pfarrer 3143 bio 3144 cnvto 3146 10th st 3146 verger 3146 ridgewood 3146 d 14 3148 turnhalle 3148 anita 3149 pace 3150 347 3150 ontario 3150 mickiwicza 3151 gemeindeaus 3151 simens 3152 camden 3153 hawthorn 3153 princeton 3155 1b 3156 provincialeweg 3157 365 3159 baur 3159 stipnaia ul 3160 jager 3161 parafialny 3161 brasseri 3161 r pasteur 3162 grb 3163 v aldo moro 3163 63rd 3164 amber 3166 james st 3166 ahornweg 3167 garenne 3167 abandoned 3167 auberge 3168 cactus 3168 vina 3168 irish 3169 tolstogo 3169 walking 3169 familia 3169 john st 3169 josep 3170 colho 3170 hsbc 3171 beaver cr 3172 linha de agua 3172 terry 3173 us 27 3173 bstalefn 3174 repubblica 3175 2e 3176 g3 3176 graz 3178 503 3178 eiscafe 3179 warner 3179 bibliothequ 3179 raiffeisen st 3179 ul chapaiva 3179 carnot 3179 coventry 3179 a sport pl 3181 ruth 3181 hwa 3181 a bf 3182 tomei 3183 post office 3183 d 12 3183 gorge 3183 biyeob 3184 ugo 3184 carroll 3184 czada 3185 foto 3185 scout 3187 392 3187 irving 3188 54th 3189 morton 3189 barra 3190 provincia 3191 homestay 3192 strawberry 3192 tadeusza kosciuszki 3195 boyd 3196 rivers 3198 pozarowy 3198 mill ln 3199 alpes 3200 70th 3201 385 3201 standard 3201 alm 3201 d 15 3201 fremont 3201 interior 3202 lis 3203 tech 3203 olympia 3204 monti 3205 ciag 3205 gregory 3206 neu st 3206 knox 3207 morrison 3207 iskola 3207 tre 3207 dragon 3208 pic 3209 foch 3209 pth 3209 priory 3209 gustave 3211 persiaran 3211 zavodskaia ul 3213 605 3214 alba 3214 alamo 3215 housing 3215 neto 3215 wise 3216 lande 3216 mill rd 3218 living 3218 b 4 3219 bleu 3220 xii 3220 274 3220 helen 3223 tudor 3224 ministry 3224 alcalde 3225 marne 3226 schulweg 3228 fontaines 3228 blas 3230 hermanos 3230 secundaria 3232 berger 3233 spanish 3233 lucky 3233 concordia 3233 dvor 3233 non 3234 e 54 3235 apteka 3235 davidson 3236 tokyo 3236 lotus 3237 zh 3238 daisy 3238 poli 3239 sari 3240 stern 3241 winding 3242 julius 3242 bodega 3243 bretagne 3243 railroad st 3244 286 3245 thornton 3247 603 3248 hem 3249 prive 3249 lebuhraya 3251 nova poshta 1 3251 a15 3252 bologna 3252 g4 3252 baikal 3253 v giuseppe mazzini 3253 55th 3253 sotto 3253 schubert 3254 union st 3256 chambers 3258 d 8 3259 ceska posta 3260 mou 3261 boleslawa 3262 facility 3262 mikolaja 3263 ust 3263 phase 3263 capilla 3267 ostrovskogo 3267 dol 3267 manzana 3268 hyde 3269 adenaur 3271 8a 3273 carlisle 3273 eichenweg 3274 corona 3276 198 3276 dojazd 3277 principe 3277 sharon 3277 515 3279 sale 3279 e 58 3279 muhlweg 3279 gerard 3280 monica 3280 a 9 3280 pet 3282 bryn 3284 276 3284 petron 3284 izbat 3285 fca 3287 1962 3287 novembro 3288 relais 3288 kaya 3288 tsintralni 3289 fleurs 3290 120th 3290 jet 3290 silvio 3290 ql 3292 hlu 3293 forrest 3293 lindenweg 3293 mialy 3294 train 3298 bruyeres 3299 us 64 3299 pavillon 3299 ul gorkogo 3300 region 3302 atelir 3303 correa 3303 324 3303 sayid 3304 antigua 3305 ch rural 3305 trading 3305 avalon 3306 3rd av 3307 380 3308 vulta 3309 santantonio 3309 assis 3309 iowa 3309 rynek 3310 335 3311 flats 3311 mistral 3311 con 3312 b 8 3312 beek 3312 stora 3312 cristoforo 3313 doner 3313 blackwater 3315 iuzhnaia ul 3315 zongho 3315 montessori 3316 deportes 3316 baba 3318 beim 3318 fellowship 3318 piscine 3319 vasco 3319 a13 3320 us 23 3320 suarez 3322 dp 3322 kerk st 3323 capitan 3325 fourth 3325 griffin 3326 lakeviw dr 3326 webb 3328 hurta 3328 birmingham 3328 publiqu 3328 cane 3329 lirmontova 3329 505 3330 lilla 3330 trlr 3330 taller 3332 cardenas 3332 cui 3333 brothers 3334 372 3334 ogrodowa 3335 m4 3336 devon 3339 maksima 3340 aragon 3341 brunnen st 3342 ptge 3342 honey 3343 aroport 3344 ff 3344 obshchizhitii 3345 athena thessalonike euzonoi 3345 sauna 3345 ty 3346 jackson st 3346 easy 3347 euzonoi 3347 scinces 3347 salita 3347 iubiliinaia ul 3349 see st 3350 partridge 3351 lenina st 3351 ryan 3352 justo 3354 courts 3354 pedra 3354 diana 3355 nottingham 3356 sawmill 3357 jane 3358 v giuseppe garibaldi 3358 gimnaziia 3359 moras 3360 hagen 3361 gai 3362 ipiranga 3362 khmilnitskogo 3363 unidad 3363 nonghyup 3364 baan 3364 bible 3365 strad 3366 d 10 3367 gama 3368 vitoria 3369 fahrschule 3369 febrero 3370 popular 3373 nunes 3373 pilsudskigo 3374 crd 3374 ca 1 3376 pko 3376 donald 3377 insurance 3377 522 3378 hood 3379 alcide 3380 406 3381 us 287 3381 cps 3381 sderot 3381 duqu 3382 cherry st 3383 descentralizado 3383 josephs 3385 dumont 3388 ploshcha 3389 oslo 3390 acorn 3390 oficina 3392 jakob 3393 1 20 3394 battista 3394 statoil 3395 504 3395 buchenweg 3395 closed 3398 stipnaia 3398 hauts 3402 organismo 3402 sheffild 3402 lei 3403 akadimika 3405 moose 3406 hollywood 3408 galleria 3411 suvorova 3412 ortiz 3413 elektro 3414 harry 3414 bankomat 3415 cleaners 3415 pebble 3415 dui 3416 carabiniri 3417 prefeito 3417 e 39 3418 56th 3420 sankusu 3421 higher 3421 westminster 3422 pinewood 3422 338 3425 318 3427 coco 3428 bautista 3428 einstein 3428 425 3428 quen st 3429 discount 3433 ul karla marksa 3433 owl 3434 rim 3434 dixon 3434 battisti 3435 veg 3435 kh 3436 alliia 3436 name 3438 loup 3439 s martin 3440 cabral 3440 dm 3440 alefbralefhym 3442 win 3443 noble 3443 emiliano 3443 heer 3444 justice 3444 sawah 3445 barao 3445 vii 3446 nd 3446 323 3447 estacao 3448 hoch 3448 aguas 3448 clayton 3449 srl 3449 piney 3449 dios 3450 bos 3452 praxis 3453 estrella 3453 273 3454 nevada 3454 burnt 3454 send 3454 sivirni 3454 disel 3455 1 35 3455 morelos 3458 pomnik przyrody 3459 moskovskii 3459 candido 3459 hl 3460 lord 3461 grandes 3462 v dante alighiri 3462 opp 3464 drug 3464 9th st 3464 ovre 3464 gill 3465 werk 3466 52nd 3467 bald 3468 arrowhead 3471 nsg 3474 371 3475 dawson 3477 gyorgy 3477 romer st 3477 pinheiro 3477 pos 3478 hent 3479 sto 3479 capitol 3480 edinburgh 3480 baron 3481 dominos 3481 conceicao 3481 servicio 3482 gr av 3482 cway 3483 apache 3483 fawn 3484 rei 3484 vanha 3486 medina 3488 peuplirs 3488 sunset dr 3488 liceo 3488 glenn 3489 burns 3489 brest 3489 o2 3489 crane 3489 donuts 3490 mann 3494 bryant 3495 432 3496 giroiv 3496 avia 3496 peace 3498 turm 3498 jos 3499 irene 3500 monteiro 3502 tyler 3502 marino 3503 plymouth 3504 299 3504 huron 3505 507 3505 amselweg 3505 jezioro 3507 catalina 3507 351 3508 fray 3508 julian 3508 husayn 3509 crossroads 3510 greenfild 3510 rocade 3511 gusthouse 3512 hafen 3512 mit 3513 silveira 3514 rakoczi 3515 convenince 3518 capitao 3518 toko 3519 m3 3519 nou 3520 svirdlova 3521 portage 3521 parkside 3524 highlands 3525 capela 3527 magdalena 3527 mendoza 3527 americas 3528 oost 3529 texaco 3529 wendys 3529 mikhaila 3529 ramal 3531 pemex 3531 bayviw 3531 arriba 3531 peninsula 3532 rain 3533 chevron 3533 municipale 3534 bgm 3536 51st 3536 gaston 3537 stafford 3538 197 3538 tram 3540 lipowa 3541 525 3541 gymnase 3542 dello 3545 unknown 3547 woodlawn 3547 stratford 3548 alexandra 3548 d 6 3550 frederick 3551 septimbre 3553 shib 3554 terrain 3554 hughes 3554 hopital 3554 arch 3555 jacinto 3555 chapman 3556 rosewood 3557 ken 3557 parana 3557 471 3558 dorps 3560 seneca 3564 azalea 3565 musee 3565 kampung 3566 daro 3566 415 3568 denkmal 3569 gr st 3569 ir 3571 winston 3572 regent 3572 ilha 3572 emerson 3573 alpha 3573 octubre 3573 pao 3575 vida 3575 fargo 3577 po ste italiane 3577 publico 3577 krasnoarmiiskaia ul 3578 th 3579 eisenbahn 3580 480 3581 romero 3582 dell 3583 ina 3583 wojska 3583 kui 3583 iuzhni 3584 diu 3585 fg 3586 agosto 3586 sevilla 3587 klaranlage 3587 terre 3588 sydy 3588 stella 3589 cannon 3590 hohen 3590 325 3591 canada post 3593 klinik 3594 d 13 3596 yale 3596 prat 3597 courthouse 3600 leigh 3600 przyrody 3602 stage 3603 swimming 3604 vita 3604 fishing 3605 hand 3605 46th 3605 49th 3606 agiou 3606 druzhby 3607 rvra 3608 optik 3609 cemetery rd 3610 harvard 3610 wheeler 3612 us 62 3613 620 3614 hill st 3615 sovetskaya st 3615 julin 3616 westfild 3618 ravine 3619 circular 3621 zhiliznodorozhnaia ul 3621 emma 3624 forestire 3625 evzonoi 3625 pintor 3627 stefan 3627 rock cr 3628 melrose 3628 cho 3630 n 2 3630 marginal 3630 kust 3630 bartolome 3631 przedszkole 3632 lam 3633 geb 3634 60th 3635 camille 3635 armstrong 3638 kuang 3638 bonita 3638 visitor 3638 geraldo 3639 play 3639 d 5 3639 medico 3639 reynolds 3640 solnichnaia ul 3640 norfolk 3641 d 4 3642 polk 3643 fridhof st 3643 arc 3644 past 3644 waldweg 3645 bundes 3645 table 3647 carson 3647 soto 3648 gurrero 3650 nicholas 3652 vu 3653 cervantes 3654 palermo 3655 e 16 3657 kut 3658 ran 3658 fraser 3659 leite 3660 pl de mairi 3662 sonnen 3663 urzad 3664 pheasant 3665 r du stade 3666 farmers 3667 freitas 3667 frederic 3668 essex 3669 entd 3669 saude 3670 lourdes 3670 palmas 3670 spring st 3671 socite 3671 gambetta 3671 bnsf 3673 weston 3673 leisure 3674 bogdana 3674 simpson 3675 solana 3678 forst 3681 rocher 3681 whispering 3681 nh44 3681 bourgogne 3683 nazionale 3683 elmwood 3685 foods 3686 miami 3688 pipeline 3688 eczanesi 3689 crow 3689 cox 3690 istituto 3691 269 3691 cardoso 3691 selatan 3691 zapata 3693 r de mairi 3694 7a 3697 ribeirao 3698 gros 3699 repair 3702 47th 3702 eye 3702 frnt st 3703 din 3704 provence 3704 arrow 3705 199 3706 maiakovskogo 3707 prtal 3708 virkhnii 3708 magistrala 3710 chuang 3710 glass 3712 eva 3713 galeri 3713 ve 3715 jaime 3715 bed 3715 m2 3716 napoli 3716 salida 3717 dublin 3718 342 3718 364 3719 riverside dr 3720 holt 3720 nhj 3721 cabo 3721 sina 3722 vera 3723 sg 3726 hubert 3726 guy 3727 stroitilii 3728 g40 3728 sovetskaya 3730 staff 3730 trento 3730 stuart 3730 mansion 3730 chestnut st 3731 moulins 3733 beechwood 3733 shainb 3734 palo 3735 pastor 3735 norton 3735 holmes 3736 2nd av 3736 orlando 3737 fiume 3738 sunnyside 3739 barat 3739 341 3739 viille 3740 feather 3741 db 3741 marx 3741 lily 3742 gua 3742 c 10 3742 tupik 3743 283 3743 euronet 3744 gibson 3745 272 3747 nv 3750 vd 3751 us 11 3752 colonel 3752 cat 3753 auburn 3753 francia 3753 herbert 3756 1er 3756 fridens 3757 uno 3757 294 3758 vrch 3760 gta 3762 gravel 3762 381 3763 frindship 3763 brucken 3763 boys 3763 259 3764 torino 3765 gasperi 3765 professora 3768 319 3769 pei 3769 holz 3770 mtb 3771 462 3771 church of jesus christ of latter day saints 3771 parcheggio 3772 g45 3774 344 3774 frankfurter 3775 wasser 3778 lambert 3779 lick 3779 manzoni 3780 us 66 3781 buren 3783 coyote 3785 guardia 3785 istvan 3787 trg 3787 skole 3788 militar 3789 athens 3792 r de fontaine 3792 354 3793 curtis 3793 imbiss 3794 independence 3795 tj 3797 piave 3797 castel 3798 1st av 3799 gramsci 3799 c 9 3799 nant 3801 adler 3801 main rd 3802 6a 3803 lecole 3805 ecole primaire 3806 wma 3809 covas 3810 pionirskaia ul 3810 yshralefl 3810 oi 3811 rush 3814 jubilee 3815 aberdeen 3816 george st 3817 khan 3817 scolaire 3819 higashi 3819 520 3819 moskovskaia 3819 ne corridor 3820 trattoria 3821 kensington 3821 gun 3822 alvarez 3822 b 1 3823 293 3823 cotton 3823 sloneczna 3823 cricket 3823 parkviw 3825 mora 3826 sho 3826 soil 3826 london rd 3827 foot 3828 370 3831 413 3831 baldwin 3832 intermarche 3832 beaumont 3834 jim 3834 deux 3836 us 75 3839 indiana 3839 youth 3840 rtda 3842 thatched 3844 pidra 3844 cuo 3845 e 105 3847 borges 3847 furniture 3848 lorraine 3848 casas 3849 nizhnii 3850 oasis 3851 petra 3852 mcdonald 3852 circonvallazione 3852 krasnoarmiiskaia 3853 spring cr 3853 learning 3854 ibrahim 3855 eichen 3855 lough 3855 seventh 3857 48th 3857 cvs 3857 loire 3858 emilia 3859 pasar 3860 ventura 3861 adolf 3862 a20 3864 gewerbegebit 3864 pirce 3864 249 3866 clement 3866 shannon 3866 italiane 3866 gloria 3868 utara 3869 7 11 3870 graf 3870 cathedral 3871 stephens 3871 196 3874 schneider 3874 duarte 3876 rocks 3877 six 3878 heol 3878 craig 3879 cow 3881 cavo 3881 beer 3884 busch 3884 computer 3886 rt transcanadinne 3891 189 3895 love 3899 post st 3901 caisse 3903 266 3910 tarasa 3912 acre 3913 d 7 3913 bill 3914 qy 3915 jozsef 3916 fond 3919 nove 3923 eri 3923 sandor 3925 monument aux morts 3925 richardson 3927 down 3928 chico 3928 board 3931 267 3932 khwchh 3932 263 3935 domenico 3936 e 41 3938 cw 3939 lomas 3942 sivirnaia ul 3943 munchen 3946 alvaro 3946 nombre 3946 g2 3946 raul 3948 lighthouse 3949 benz 3949 peru 3952 quartir 3953 bernhard 3953 roland 3956 e 04 3956 par 3956 reyes 3957 alla 3959 bear cr 3959 fulton 3961 warwick 3964 schweitzer 3964 panamericana 3965 rios 3966 forum 3970 e 67 3970 lateral 3975 paix 3975 pv 3980 pot 3981 sang 3981 mallard 3983 pilar 3984 konig 3986 punto 3987 setembro 3989 tk 3990 iubiliinaia 3990 servidao 3990 firenze 3991 wisen st 3991 anthony 3992 samul 3992 kst 3993 evangelische 3993 gustav 3993 nb 3995 balka 3995 vreda 3995 glendale 3996 gogolia 3999 circuito 4002 szkolna 4003 kp 4003 238 4003 nino 4004 stolovaia 4006 quatre 4007 c 12 4008 rocco 4009 zavodskaia 4010 rbla 4010 kingdom 4010 meridian 4011 tankstelle 4012 willow cr 4012 ra 4014 peach 4014 walton 4017 41st 4019 dal 4019 zoo 4021 huntington 4022 zeng 4024 pk ln 4025 derby 4028 278 4033 g70 4033 zespol 4034 lagoon 4034 palais 4034 turtle 4035 us 12 4035 waters 4036 game 4037 sub 4037 torrent 4038 henryka 4042 179 4043 194 4043 e 4 4044 sadova 4045 193 4046 vostochnaia 4046 concepcion 4047 midway 4048 43rd 4048 transcanadinne 4048 pblo 4048 248 4049 nails 4049 alfa 4050 minnesota 4050 prol 4051 45th 4051 filippo 4051 brennero 4051 zakaznik 4053 44th 4053 refuge 4054 beverly 4055 502 4055 prolitarskaia ul 4055 roses 4056 rijksweg 4057 polskigo 4059 schools 4061 svobody 4062 nest 4063 albany 4067 8th st 4068 ndeg 4069 isle 4069 bento 4072 002 4072 futbol 4074 314 4075 ort 4077 dar 4079 kurt 4079 shaykh 4080 jay 4081 plains 4083 waterloo 4083 bradford 4084 burton 4084 student 4090 grass 4091 metzgerei 4092 barangay 4093 quadra 4093 windy 4095 bom 4096 latter 4096 baltimore 4096 333 4096 fresh 4098 negeri 4098 e 06 4099 dolina 4100 paradis 4102 fall 4103 boca 4103 podhardizd 4103 hostal 4103 ralefs 4104 elena 4104 cuva 4104 fatima 4108 cole 4108 417 4109 glebe 4109 football 4109 administration 4109 352 4112 coastal 4114 jersey 4114 caduti 4115 cinco 4116 estadual 4117 vijo 4117 hy 4119 258 4120 century 4121 namazu 4121 bordeaux 4122 245 4122 rivera 4126 murphy 4126 poghots 4128 chui 4130 primrose 4131 wesley 4131 sterling 4132 247 4132 b 6 4133 athena 4134 franca 4135 317 4135 271 4135 bravo 4137 kentucky 4138 coach 4139 hokuriku 4139 neves 4141 pionirskaia 4142 50th 4142 sebunirebun 4144 r des ecoles 4146 great wall of china 4147 d 2 4147 carriage 4148 ernest 4150 santana 4150 walgreens 4152 lock 4156 afonso 4156 double 4156 agricola 4157 mts 4157 c 8 4160 chinesische maur 4162 cvn 4163 ribeira 4166 rdge rd 4171 snow 4172 gruner 4173 belt 4173 900 4173 harvey 4176 37th 4178 bilarusbank 4180 lgv 4181 assembly 4182 kosciuszki 4183 ruby 4183 rossmann 4184 venezia 4184 800 4188 pecan 4189 panaderia 4200 bottom 4200 canterbury 4201 e 77 4201 senda 4201 ferdinand 4202 andrew 4202 altes 4205 emanule 4209 264 4211 calvary 4211 dover 4213 zui 4213 bunos 4214 tire 4214 marg 4215 chinesische 4216 crawford 4216 b 2 4221 sole 4223 surgery 4224 espace 4225 muhl 4226 brush 4226 mead 4232 madre 4232 shores 4232 acacia 4234 d 3 4236 windmill 4238 ksidza 4239 fischer 4240 edouard 4241 volunteer 4241 variante 4242 time 4242 l st 4244 gum 4244 koch 4245 margaret 4246 saddle 4246 stoney 4247 42nd 4249 ancin 4249 kindertagesstatte 4250 landes 4252 saozhup 4252 adriatica 4252 mittel 4253 257 4254 634 4254 lagoa 4256 law 4257 makudonarudo 4257 tir 4257 xavir 4260 cooperative 4260 422 4260 246 4263 imam 4264 biantel 4264 tts 4264 miru 4265 carlton 4267 1 75 4267 ruiz 4269 reis 4270 ski 4272 leopold 4273 pla 4274 carr 4275 yuansenv 4277 zhiliznodorozhnaia 4280 cedar st 4281 iuzhnaia 4283 610 4284 durham 4284 sideroad 4284 patrick 4287 oktiabria 4287 pau 4288 porter 4288 stores 4292 construction 4292 kansas 4293 cordoba 4294 muhlen st 4295 jahn st 4295 olympic 4295 paraiso 4296 solnichnaia 4296 credit agricole 4298 289 4298 chile 4299 feld st 4301 601 4302 freres 4305 chinese 4305 39th 4307 tirra 4307 dzirzhinskogo 4308 shuan 4308 chesapeake 4309 entre 4310 michurina 4311 premir 4311 helena 4312 meyer 4312 sos 4312 schloss st 4314 pro 4315 minas 4315 lugovaia ul 4319 grey 4320 downs 4320 missionary 4321 jeanne 4322 dock 4323 laura 4323 tourist 4324 capital 4325 umberto 4325 bob 4326 leurope 4327 piatiorochka 4329 edison 4329 ebert 4331 julia 4334 montagne 4335 pavilion 4337 groupe 4337 bass 4337 ul kalinina 4337 470 4338 450 4339 almirante 4339 405 4343 d1 4343 bernardino 4344 wat 4345 mainline 4345 sunshine 4345 keller 4345 253 4350 lime 4351 duke 4352 henriqu 4352 duncan 4353 1 5 4353 bath 4356 dia 4357 schutzen 4358 church ln 4358 282 4358 cemiterio 4358 407 4361 michals 4361 aprile 4361 cavour 4361 cal 4364 ful 4365 460 4366 hernandez 4366 bach st 4367 urban 4368 illinois 4369 barnes 4374 avtomagistral 4376 theater 4378 liberte 4378 295 4380 sanyo shinkansen 4380 gregorio 4384 princess 4385 mohamed 4389 armii 4391 brookside 4392 n2 4392 pitra 4393 sb 4395 juliana 4398 training 4402 pump 4402 vinte 4403 360 4404 moreira 4404 tours 4404 alighiri 4404 water st 4404 tim 4406 patterson 4407 off 4407 neur 4407 winchester 4411 parroquia 4413 dois 4414 morro 4419 lea 4420 norman 4422 phonix 4422 227 4424 andreas 4424 dallas 4425 mhtte 4429 omv 4429 caixa 4432 442 4432 granada 4432 clair 4432 centennial 4436 pinos 4436 hunt 4437 237 4439 195 4439 rada 4439 a8 4440 key 4442 411 4442 picnic 4444 kirke 4445 retail 4445 prolitarskaia 4447 wareouse 4449 corporation 4450 sultan 4452 molen 4452 flower 4453 petofi 4454 187 4456 g65 4458 b 27 4459 sarminto 4460 e 6 4460 marks 4461 chlet 4462 pizza hut 4462 traversa 4465 blossom 4465 186 4468 lakewood 4469 benedito 4472 cant 4473 webster 4475 whrf 4476 mitte 4478 bennett 4481 thessalonike 4482 armando 4483 alma 4483 girls 4486 echo 4486 flor 4487 28n 4491 toyota 4492 lenina 4496 glenwood 4498 bosch 4502 wayne 4502 lazaro 4507 350 4508 lexington 4508 a14 4512 244 4512 santu 4516 piscina 4519 br 101 4520 correos 4520 caltex 4520 431 4521 ten 4521 esp 4524 rw 4524 38th 4527 192 4530 romana 4531 shun 4540 berliner st 4542 marc 4542 postal 4545 adama 4546 timur 4547 308 4547 307 4550 mobil 4552 childrens 4553 wisenweg 4554 b1 4555 conde 4556 skyline 4559 yuhag 4559 315 4559 lajos 4559 browns 4561 faith 4563 grandviw 4565 ak 4566 schmidt 4568 sheridan 4571 guido 4573 ahmad 4574 tavern 4576 space 4576 sante 4577 1 70 4579 parks 4580 patak 4580 mato 4581 georgia 4582 adam 4582 r du chateau 4586 tah 1 4587 c 7 4587 cunha 4590 woodside 4594 hbf 4594 alice 4595 lands 4598 teixeira 4599 famili 4601 isral 4604 sunny 4605 alder 4605 wind 4607 oregon 4607 liberation 4608 mor 4608 hayes 4608 ivan 4610 hutte 4612 gothe st 4612 grands 4614 bothar 4615 dakota 4618 supply 4623 somerset 4625 us 80 4626 cano 4627 woodlands 4628 mosque 4629 briar 4629 abzweig 4631 providence 4633 taxi 4637 mastro 4639 marseille 4641 walmart 4641 grund 4642 eneos 4643 331 4647 orlen 4648 rosas 4650 charlotte 4652 kebab 4652 leonard 4652 coal 4654 aleflshykh 4657 d 1 4657 teatro 4658 430 4658 pins 4659 4a 4659 33rd 4664 portland 4668 harvest 4670 q8 4670 ja 4673 foothill 4673 gendarmeri 4674 402 4675 baserria 4678 gym 4680 elias 4682 gg 4684 239 4684 elisabeth 4684 third 4685 federico 4686 coiffure 4689 commons 4691 baru 4692 arturo 4693 airport rd 4693 germain 4694 dit 4694 werner 4698 kafi 4698 engenheiro 4702 321 4703 harmony 4703 mydalefn 4703 granite 4704 silsovit 4704 heros 4706 cold 4709 alefbn 4711 liberta 4712 stevens 4713 316 4713 frunzi 4714 404 4715 pk rd 4719 tf 4720 sklep 4721 228 4723 oblasti 4725 jaya 4726 boa 4726 gorodskaia 4726 oliver 4727 chkalova 4730 oz 4732 clifton 4732 alpe 4736 melo 4737 esperanza 4738 glacir 4741 luz 4742 fashion 4744 michele 4746 augu 4746 slough 4747 wing 4748 outer 4749 zuid 4752 ball 4754 e st 4755 plaine 4756 bras 4758 fernandes 4759 bri st 4761 g30 4763 bam 4764 monumento 4765 vor 4766 rondo 4770 177 4771 188 4778 pen 4779 trakt 4780 araujo 4781 ah6 4784 banka 4785 vereador 4785 administratsiia 4786 neuf 4788 dln 4789 cima 4790 256 4792 trout 4793 262 4795 gut 4795 avon 4796 zarichnaia ul 4796 fir 4797 dhsa 4797 secretaria 4798 shaw 4798 etinne 4800 podstawowa 4801 tal st 4802 castilla 4808 ride 4810 hay 4813 vincenzo 4815 westwood 4815 nh 4817 gale 4819 henderson 4821 shadow 4822 morning 4823 ellis 4824 king st 4824 salmon 4824 stari 4824 catherine 4824 ul pushkina 4829 maio 4831 brooks 4836 enriqu 4837 320 4838 edgewood 4839 lawn 4839 166 4842 anger 4845 beethoven 4846 getranke 4847 franco 4848 plage 4848 zaragoza 4848 schiller st 4849 konrad 4852 mill st 4853 arco 4853 balefnkh 4858 mys 4859 bedford 4861 music 4861 ceng 4862 243 4863 hazel 4864 wine 4865 cultural 4872 homes 4874 bowling 4874 236 4876 poggio 4876 plat 4878 gyo 4880 central av 4880 verdun 4881 mer 4883 lilas 4883 com 4884 combe 4885 421 4891 ic 4893 alegre 4894 engineering 4894 ainzbte 4904 live 4905 noord 4905 bau 4913 fleming 4914 stations 4914 dixi 4914 soares 4916 senior 4919 urochishchi 4920 guterweg 4920 bradley 4923 denis 4925 34th 4926 polo 4926 zentrum 4928 gilbert 4930 pere 4930 exchange 4931 31st 4932 1000 4935 dlia 4936 teich 4938 crooked 4940 ernesto 4940 khrbte 4943 autumn 4944 35th 4947 ah8 4947 2000 4947 bw 4952 raiffeisenbank 4956 7th st 4958 395 4961 janos 4962 ers 4962 watson 4964 calvaire 4964 us 60 4966 electric 4966 krasni 4967 auk 4968 306 4968 ll 4970 privatbank 4970 5a 4972 hawk 4972 nguyen 4974 asia 4975 chenes 4976 bloco 4976 36th 4977 sor 4979 domingos 4980 carmel 4980 a10 4981 volta 4983 us 70 4983 negro 4986 bunker 4993 research 5002 martino 5003 deli 5004 sporthalle 5005 martiri 5007 cristobal 5008 cameron 5009 lugovaia 5012 bishop 5014 winer 5015 dollar 5015 my 5016 polivaia ul 5016 morts 5017 550 5020 c 6 5021 302 5023 romer 5023 rao 5023 miranda 5024 ah3 5027 sherman 5029 159 5034 borgo 5036 ras 5040 giorgio 5042 hoya 5042 it 5044 provinciale 5044 kompliks 5048 batiment 5049 182 5051 preston 5052 england 5054 parts 5054 ferreteria 5059 association 5060 newport 5060 bv 5065 hunters 5066 oakland 5066 heather 5068 drk 5074 184 5076 agios 5083 matteotti 5084 fr 5085 fosse 5086 guimaras 5086 alpine 5087 more 5087 copper 5087 e 05 5087 cancha 5092 mkt pl 5092 wilderness 5093 277 5096 wo 5099 boulangeri 5101 barton 5104 40th 5106 178 5107 azs 5108 332 5110 ferenc 5110 a7 5113 rolling 5115 animal 5119 hi 5121 log 5122 611 5127 adolfo 5128 v guglilmo marconi 5130 hemlock 5131 penn 5131 uramy 5137 fernandez 5137 gl 5138 brighton 5138 winkel 5138 khirbat 5141 castelo 5141 bor 5145 intg 5145 chome 5146 ashley 5149 nj 5150 174 5150 muhlenweg 5152 michigan 5153 reed 5154 us 99 5155 ayn 5156 andrade 5157 sales 5158 sage 5159 storage 5163 us 20 5164 nm 5165 hardware 5168 shivchinko 5169 primo 5170 dhl 5170 313 5172 arlington 5172 horsesho 5176 operative 5176 254 5176 falcon 5178 imini 5179 172 5180 sviazi 5180 delaware 5180 near 5186 lancaster 5187 rhone 5191 shinomontazh 5193 mazzini 5194 bosqu 5200 hilton 5204 shivchinka 5205 312 5208 linde 5209 r c 5210 doro 5211 travel 5212 fer 5212 1 80 5214 sutton 5215 allende 5215 ev 5216 beacon 5217 420 5217 ffcc 5219 trunk 5220 powell 5221 chene 5221 traverse 5222 dove 5225 216 5226 llano 5226 blm 5226 wildwood 5226 kan 5227 cut 5228 meng 5228 isidro 5229 kloster 5230 bela 5231 tea 5231 233 5231 cinema 5233 re 5235 victory 5237 plata 5238 ice 5239 palmer 5242 huai 5244 garfild 5246 libertad 5249 myrtle 5249 r principale 5250 sete 5251 barber 5255 mir 5257 a9 5260 estadio 5260 ah26 5262 630 5262 pawla 5264 polska 5266 colombo 5266 foster 5270 indah 5270 church rd 5280 cafeteria 5286 265 5289 toledo 5291 ul kirova 5292 403 5293 english 5296 217 5298 our 5301 eugenio 5303 alfonso 5303 mendes 5311 bt 5314 412 5318 e 25 5318 oscar 5319 sir 5320 alefllh 5323 mitre 5327 superior 5327 exit 5327 basn 5330 hasan 5336 industri st 5336 gly 5337 ray 5339 neuve 5340 publica 5342 soleil 5342 234 5342 taco 5348 honda 5350 goose 5351 tangenziale 5352 bazar 5352 pennsylvania 5353 letang 5357 seoul 5359 bahia 5360 hot 5368 eugene 5368 rosen 5371 willem 5372 agustin 5373 32nd 5374 strasbourg 5374 coral 5374 punkt 5376 midland 5379 flora 5382 vinci 5386 kula 5389 connector 5392 700 5394 zarichnaia 5394 hauptlini 5394 generale 5395 leo 5395 kossuth 5399 tilleuls 5399 information 5402 305 5403 sala 5403 maggio 5403 252 5404 zilionaia ul 5405 zuoteo 5409 bukit 5411 alexandre 5412 r de gare 5415 azul 5416 supermercado 5417 polna 5420 source 5423 kao 5425 a 3 5427 gateway 5429 constitucion 5430 sai 5430 tadeusza 5433 benjamin 5434 renault 5447 ccvcn 5449 edwards 5455 clover 5458 285 5458 elementaire 5459 comunale 5459 bibliotika 5460 ctr st 5463 ko 5464 outlet 5464 scolo 5468 laurent 5468 works 5468 s st 5469 crral 5477 logan 5484 29th 5486 fairfild 5491 pian 5495 droga 5495 school st 5507 buna 5510 241 5511 louise 5515 zhe 5517 226 5520 apartment 5521 parti 5522 168 5522 aires 5523 arnold 5532 a12 5533 graham 5534 arbor 5536 montana 5543 rruga 5545 mina 5546 1 40 5548 stefana 5550 pp 5551 pg 5552 wallace 5553 us 40 5554 275 5557 sivirnaia 5561 terra 5562 otter 5565 rybnik 5565 mill cr 5566 330 5568 linin 5568 agricole 5568 sri 5573 stony 5574 design 5576 242 5577 clinton 5581 hawthorne 5581 mata 5583 thessaloniki 5584 maia 5584 weber 5587 raiona 5589 kladbishchi 5592 bailey 5592 eg 5592 columbus 5592 croce 5593 ida 5598 441 5600 dean 5602 estatal 5602 a 6 5604 abc 5605 vicinale 5606 pio 5607 syd 5608 verdi 5611 linda 5613 185 5618 kamp 5620 concord 5622 boston 5626 sect 5626 polivaia 5627 emerald 5633 lakeshore 5633 butler 5633 caminho 5634 florence 5635 gulf 5638 abril 5640 a 4 5640 nuova 5644 heron 5645 grote 5650 us 30 5651 pozo 5652 koz 5653 enterprise 5655 fisher 5655 og 5659 open 5661 cycle 5664 223 5667 162 5667 india 5669 e 35 5670 ostrov 5672 304 5673 pauls 5677 kingston 5679 shao 5691 paula 5692 us 41 5692 socity 5692 gori 5695 barros 5699 muzii 5705 edge 5712 gyeongbu 5712 angeles 5714 peron 5714 spencer 5716 ancinne 5716 ceska 5716 322 5718 carter 5718 princes 5722 riverviw 5722 cjto 5723 lafayette 5725 shelter 5726 case 5726 oro 5726 dang 5732 410 5735 transit 5741 aldo 5741 holland 5742 portugal 5742 veneto 5748 gross 5749 ramp 5749 208 5750 gray 5752 cumberland 5755 lopes 5756 pulau 5759 218 5762 g5 5764 mg 5766 sweet 5774 moon 5778 krasnaia 5783 free 5786 marii 5786 rcon 5787 montgomery 5788 trois 5789 jiron 5791 klub 5792 cesar 5796 imperial 5799 gulch 5802 interstate 5814 c 5 5817 229 5818 batista 5821 channel 5823 ed 5823 chapaiva 5826 bolivar 5827 a 2 5828 wright 5828 marta 5829 praia 5830 room 5831 delta 5832 regina 5836 1 95 5836 alefhmd 5836 banqu 5838 167 5840 rewe 5843 164 5844 colon 5844 lone 5848 235 5848 bapti 5848 technology 5851 stand 5857 alternate 5858 260 5858 mercedes 5859 147 5861 vecchia 5861 lynn 5869 walnut st 5870 nicola 5871 lucas 5874 cc 5875 joan 5875 sdn 5875 ohio 5875 gorodskoi 5876 theodor 5877 limited 5877 taman 5878 ul gagarina 5879 w main st 5880 lounge 5884 309 5887 e 20 5890 muhlbach 5894 mud 5900 dor 5902 amur 5903 r du moulin 5904 pk av 5907 tinda 5907 riu 5911 6th st 5913 plum 5919 hidalgo 5919 chicago 5923 fair 5923 146 5925 duck 5925 mexico 5926 hilltop 5928 tah 5930 milton 5935 baza 5937 civil 5937 181 5945 nustra 5947 1945 5948 vargas 5950 birkenweg 5953 manchester 5963 jahn 5968 gothe 5968 diao 5971 desa 5973 khyalefbalefn 5975 e main st 5976 switego 5978 ain 5979 repsol 5979 palazzo 5979 niuwe 5981 wisen 5987 149 5988 fritz 5993 bon 6001 26th 6002 310 6002 sanchez 6003 255 6004 501 6008 malaia 6008 basse 6013 ricardo 6013 mozart 6014 a 1 6029 juarez 6035 c 4 6037 bristol 6043 overlook 6044 senora 6051 311 6057 gomez 6057 rocha 6065 abalefd 6065 jacob 6066 biserica 6068 ramos 6070 aral 6074 churchill 6074 templom 6074 261 6076 hsyn 6077 houston 6080 171 6084 27th 6085 heart 6086 marcos 6086 219 6088 kuai 6090 homestead 6090 154 6098 acesso 6098 280 6100 mikroraion 6102 roy 6107 ah2 6108 aurora 6109 pomnik 6111 desert 6116 trans canada hwy 6117 pino 6120 motors 6121 n1 6121 universidad 6122 horn 6122 kaido 6125 176 6126 atlantic 6127 maur 6127 chicken 6128 rbhddh 6129 szent 6132 303 6133 28th 6135 trails 6137 kri 6139 251 6146 austin 6146 fern 6150 boulder 6151 rudolf 6152 farms 6157 hameau 6157 por 6157 embassy 6159 goncalves 6160 mulberry 6162 cardinal 6163 leaf 6163 157 6163 hunter 6165 navajo 6165 texas 6168 andrea 6170 republic 6171 belmont 6171 drain 6173 kaiser 6174 bruce 6177 panorama 6180 independencia 6181 good 6182 rainbow 6182 muhammad 6186 xxiii 6186 episcopal 6187 marais 6188 collins 6191 quinta 6201 seven eleven 6201 palac 6201 pole 6202 stanley 6203 sherwood 6205 guadalupe 6205 motor 6208 148 6208 spil 6212 nagar 6213 1 10 6214 zilionaia 6214 five 6219 kirchweg 6221 cabin 6221 lini 6225 209 6228 225 6231 cascade 6233 g15 6236 forge 6238 hart 6238 g25 6238 grande r 6242 oud 6242 shin 6244 trust 6248 207 6248 novaia ul 6249 cherokee 6249 ecoles 6254 colonial 6259 apt 6260 arms 6262 khram 6263 fountain 6263 mrdor 6265 telecom 6269 argentina 6275 mason 6278 144 6279 ile 6282 viira 6284 ditskaia 6286 rynok 6298 preserve 6300 poninte 6303 lion 6303 oakwood 6309 139 6310 mrkz 6311 roger 6311 wahdah 6312 centrale 6317 30th 6318 salud 6326 n st 6333 bureau 6336 french 6337 us 101 6337 blanche 6342 aleflwhdte 6344 roberts 6348 281 6348 colle 6352 washington st 6357 price 6369 tohoku 6372 ibn 6372 b 3 6374 pirvomaiskaia ul 6398 fawy 6407 winter 6409 weir 6412 bruno 6412 independent 6413 heliport 6417 turner 6424 burlington 6431 m1 6435 young 6440 emilio 6440 caffe 6441 turkey 6442 a6 6445 edeka 6447 lesna 6447 schiller 6448 joaquin 6452 augusta 6452 domaine 6456 bull 6456 cesare 6459 ciudad 6460 ermita 6463 media 6470 gi 6473 light 6473 vc 6475 happy 6484 us 50 6486 gymnasium 6489 acacias 6493 truck 6499 clinica 6507 blanco 6514 pico 6515 bayt 6515 marion 6516 redwood 6518 alefltryq 6524 sihiyah 6525 steig 6530 branco 6532 3a 6533 arab 6536 kultury 6538 army 6541 scince 6542 biblioteca 6543 6550 iris 6551 springfild 6554 143 6556 sous 6556 palma 6557 gust 6559 shdrvt 6564 beco 6572 iron 6573 kon 6574 phillips 6578 roberto 6578 ayuntaminto 6579 aleflshyte 6580 156 6580 kiosk 6583 frei 6583 bird 6589 war 6590 215 6591 primaria 6592 eden 6592 maple st 6597 fratelli 6604 moreno 6607 lavoir 6608 martinez 6612 rnd 6613 sky 6623 chai 6632 blumen 6633 pri 6635 uliza 6641 radio 6643 evans 6645 policia 6648 rogers 6652 sungai 6653 xuan 6654 w st 6655 240 6657 cap 6660 hinter 6663 ka 6666 may 6670 islands 6672 vernon 6676 mediterraneo 6676 clay 6683 rita 6683 ferrocarril 6683 teresa 6686 concession 6694 passo 6695 jogi 6696 cape 6697 me 6700 mayor 6701 rey 6704 dairy 6705 bush 6708 luna 6708 schwarzwaldverein 6710 211 6711 kelly 6715 jaures 6715 antoine 6720 vallee 6721 155 6722 shaib 6722 infantil 6724 mark 6726 vert 6726 foret 6726 173 6726 korpus 6727 fonte 6732 wein 6733 rou 6738 tomas 6738 chong 6740 banca 6740 224 6741 egnatia odos 6742 cooper 6745 sam 6750 r rd 6753 rice 6762 25th 6762 buck 6763 sebastiao 6763 abbey 6773 belvedere 6774 summer 6776 a 8 6776 161 6776 juniper 6777 roche 6778 fontana 6780 142 6782 zang 6783 ivy 6785 moss 6792 carolina 6793 nuvo 6794 heath 6794 tin 6795 165 6801 vine 6804 museo 6806 zha 6808 moro 6817 claude 6821 wald st 6822 benito 6826 n main st 6826 font 6826 pai 6830 stantsiia 6841 familymart 6843 600 6845 penny 6846 andres 6847 szkola 6848 parada 6854 perry 6854 lisnaia ul 6855 ou 6855 vlls 6857 tn 6866 deep 6866 isabel 6868 gallery 6868 sidi 6869 limite 6871 porte 6872 plus 6873 sin 6876 boat 6877 290 6886 roca 6889 baile 6891 anton 6892 bayou 6896 s main st 6900 miao 6901 johannes 6902 231 6907 mars 6916 etang 6919 roqu 6921 raymond 6923 custa 6926 felipe 6928 mdrste 6932 lucia 6934 gonzalez 6934 270 6936 aire 6936 education 6937 leonardo 6942 pirvomaiskaia 6942 muhlen 6946 lost 6946 arena 6949 madonna 6949 eduardo 6951 145 6953 principal 6955 triq 6957 bani 6960 c 3 6965 c 2 6971 bernard 6976 alefmalefm 6977 milano 6979 maja 6980 filho 6981 kv 6981 hsn 6984 ruo 6989 stewart 6996 urb 6997 5th st 6999 murray 6999 filds 7001 scenic 7002 alt 7005 ober 7017 175 7020 221 7024 plan 7027 roundabout 7032 wladyslawa 7039 ainrb 7043 novo 7044 morris 7048 183 7049 petrol 7051 contrada 7055 rm 7057 shainyb 7058 barbosa 7060 knoll 7063 chester 7073 232 7076 gran 7076 rest 7083 mosqu 7083 214 7083 cristo 7087 august 7095 greenwood 7099 localita 7104 qin 7108 kitchen 7108 espana 7109 ham 7112 c 1 7120 berry 7121 comercial 7125 cleveland 7126 pl de leglise 7132 222 7136 mariano 7137 tv 7141 163 7144 tom 7145 edward 7161 marksa 7164 1 90 7164 niu 7165 kochasi 7167 dante 7173 kirch st 7174 cotts 7174 pe 7175 aspen 7179 bourg 7184 jordan 7187 montee 7196 bernardo 7196 nabirizhnaia ul 7196 knob 7215 posto 7215 power 7218 marshall 7219 cliff 7226 pine st 7231 213 7234 governador 7234 bella 7240 tor 7247 keng 7268 128 7276 wagner 7278 newton 7281 alfredo 7281 sidlung 7281 haute 7284 gla 7286 deau 7296 komsomolskaia ul 7299 perez 7299 191 7302 union pacific railroad 7302 historic 7306 lit 7310 diaz 7322 paso 7325 lakeside 7326 theatre 7327 jr dong hai dao ben xian 7330 sridniaia 7332 sec 7344 23rd 7348 204 7360 swan 7360 verte 7365 hudson 7367 sebastian 7368 thai 7369 instituto 7370 gordon 7376 133 7394 angelo 7400 barcelona 7403 son 7404 ferme 7404 mkt st 7423 blwalefr 7423 kent 7424 autokinetodromos 7426 cp 7429 almeida 7430 bolshaia 7442 burn 7466 rodriguz 7473 rancho 7476 206 7478 salt 7479 communale 7485 augusto 7492 alessandro 7495 curi 7499 management 7500 ring st 7501 colorado 7507 johann 7507 pension 7511 berlin 7514 shhyd 7517 hohe 7519 elm st 7520 unit 7526 klein 7531 tuan 7535 tesco 7538 22nd 7541 gunung 7545 novi 7549 e 18 7553 belgrano 7554 250 7555 pamiatnik 7557 enrico 7558 robinson 7559 001 7571 halle 7572 educacion 7572 340 7575 peng 7577 carvalho 7578 sushi 7579 dog 7579 212 7586 major 7600 eem 7602 kol 7606 21st 7615 unter 7615 colony 7618 chao 7619 07 7626 papa 7631 rosario 7643 blanca 7650 municipio 7660 stn rd 7664 mil 7665 castello 7670 ann 7672 dun 7673 bel 7676 burger king 7680 privada 7685 yellow 7692 skola 7693 yd 7696 georg 7697 um 7699 california 7706 valencia 7706 vignes 7710 xiu 7713 cu 7721 cmentarz 7726 rh 7728 raya 7735 ah5 7735 blanc 7740 trval 7743 cook 7747 lady 7748 beck 7750 marqus 7754 castillo 7757 ul mira 7760 203 7767 stanislawa 7769 brunnen 7777 dias 7782 gasthof 7785 ernst 7790 ac 7790 holy 7804 ristorante 7814 oak st 7816 vej 7825 lun 7829 190 7829 staz 7833 paolo 7836 hacinda 7841 op 7843 robin 7846 pearl 7848 bethel 7849 agip 7860 cft 7862 pioneer 7865 social 7873 machado 7875 bolnitsa 7876 kantor 7879 well 7880 paradise 7893 126 7896 np 7900 crs 7907 205 7908 boutiqu 7908 para 7909 guglilmo 7910 fosso 7918 rr 7919 beauty 7921 24th 7934 elk 7936 republica 7941 molino 7944 sunrise 7947 fitness 7952 marine 7958 gemeinde 7965 corridor 7968 most 7988 158 7990 base 7996 fo 7996 cz kct 7998 fs 8006 qubrada 8007 komsomolskaia 8014 local 8016 orinte 8023 kun 8023 morgan 8038 chen 8042 marcel 8049 08 8050 170 8054 parker 8062 230 8070 netto 8071 mian 8074 153 8075 dale 8082 damm 8088 137 8098 e 90 8120 ap 8138 crown 8140 287 8144 ignacio 8146 clara 8148 campos 8156 suites 8157 life 8168 presbyterian 8169 152 8200 cjon 8201 toll 8212 cave 8212 roggia 8214 pochta rossii 8216 bny 8226 chang cheng 8228 clear 8235 129 8239 136 8244 500 8247 hole 8249 bakery 8261 pista 8264 florida 8264 tryq 8274 gorkogo 8283 ip 8284 oxford 8292 kapelle 8293 corrego 8297 mitchell 8299 states 8305 fc 8308 vincent 8321 or 8335 141 8342 monro 8354 crystal 8357 playground 8360 yagateteu 8360 abd 8363 dogwood 8370 pinto 8371 mali 8372 pines 8375 sugar 8379 locust 8386 raion 8386 honsen 8391 kalinina 8392 wellington 8398 raiffeisen 8403 moore 8421 palace 8422 condominio 8423 cementerio 8435 box 8441 russell 8456 institute 8460 quail 8462 kerk 8470 ainyn 8470 bellevu 8474 barn 8483 factory 8486 commerce 8488 generala 8488 carmen 8492 novembre 8493 republiqu 8497 138 8503 salem 8508 2a 8513 pir 8513 domingo 8516 169 8517 e 15 8517 bolshoi 8520 loch 8534 135 8545 4th st 8546 lisnaia 8551 kita 8551 marin 8553 transsibirskaia magistral 8555 muller 8556 gomes 8557 parco 8559 19th 8559 hampton 8562 best 8563 roman 8567 transsibirskaia 8576 cimitero 8577 lorong 8583 american 8583 trva 8595 lkt 8599 carl 8608 walter 8609 emile 8612 virgen 8613 20th 8614 em 8618 e 70 8621 pasteur 8623 playa 8627 niao 8645 e 50 8648 autohaus 8656 huamirimato 8657 merc 8662 bosco 8663 arts 8664 vla 8668 supermarket 8680 pobidy 8690 natural 8692 muhle 8694 kfc 8700 hostel 8706 127 8707 hermann 8709 nu 8709 short 8710 jardim 8711 trans siberian railway 8714 gap 8718 isla 8750 mn 8755 angel 8757 alfred 8759 het 8759 oktiabrskaia ul 8763 oao 8768 marconi 8773 jardins 8782 nuva 8784 hillcrest 8789 131 8790 siberian 8791 sul 8792 heide 8794 lycee 8794 nian 8797 chuo 8801 jack 8804 bloc 8806 09 8814 bistro 8817 stadium 8826 401 8840 spar 8841 grace 8844 nossa 8850 timber 8852 grnd 8852 casino 8858 doroga 8862 saints 8874 cz 8879 richmond 8881 cct 8884 0 8889 ludwig 8896 sncf 8911 torres 8934 santander 8937 america 8940 two 8948 rui 8952 us 1 8959 maurice 8960 vega 8961 howard 8964 alle 8968 e 65 8977 andrews 8982 301 8985 industri 9006 garibaldi 9019 frank 9023 ronda 9038 second 9038 molodiozhnaia ul 9043 campground 9048 soi 9049 auzoa 9064 zavod 9074 rumah 9076 ost 9080 kang 9089 wells 9090 holiday 9093 linden st 9094 francis 9095 baker 9098 zion 9098 felix 9100 220 9107 us 6 9110 wildlife 9116 stream 9120 ribeiro 9128 greenway 9128 rsviramate 9134 warren 9139 estcn 9157 linha 9162 a5 9165 151 9175 a 7 9179 egnatia 9181 zone 9183 rene 9188 evergreen 9199 branc 9206 harrison 9214 stadion 9214 lorenzo 9215 digo 9218 atm 9224 campbell 9226 rodrigus 9234 plantation 9243 quens 9249 shkolnaia ul 9273 molodiozhnaia 9295 pk st 9296 senhora 9298 punta 9302 harbour 9310 veterans 9312 diamond 9318 mr 9322 put 9326 complex 9330 italia 9337 three 9337 pin 9362 18th 9378 98 9379 windsor 9385 moor 9397 000 9404 alten 9404 danil 9407 a3 9411 pushkina 9416 deutsche telekom ag 9417 124 9419 ent 9430 josef 9430 ab 9435 plant 9438 sd 9452 rovn 9453 134 9455 hidden 9493 cambridge 9497 orintal 9513 karla 9524 gasthaus 9524 3rd st 9535 apple 9548 ks 9551 roosevelt 9554 garten st 9569 1st st 9569 sui 9579 virginia 9580 world 9588 dou 9605 barbara 9627 fei 9661 flores 9671 butte 9695 trace 9700 sentiro 9700 maternelle 9711 kirova 9714 oktiabrskaia 9720 132 9722 lawrence 9737 210 9738 silskoi 9740 pit 9740 row 9743 hillside 9746 tal 9773 franz 9786 dona 9790 simon 9796 harris 9800 poliklinika 9802 council 9806 sadovaia ul 9808 walker 9819 government 9824 400 9844 mobile 9856 ah1 9857 stein 9865 buffalo 9873 e 80 9875 prince 9886 marco 9901 chez 9901 igreja 9903 rond 9910 sanyo 9914 anne 9919 belle 9925 jozefa 9930 rafal 9935 otdilinii 9941 champs 9958 201 9965 lawson 9977 fairviw 9979 scuola 9993 marche 9995 mills 9999 17th 10007 sycamore 10007 poshta 10009 ocean 10022 michel 10022 dels 10025 e 30 10033 bdy 10042 posta 10049 ross 10053 nai 10055 wild 10064 rouge 10065 cour 10081 feld 10093 cottonwood 10094 cascina 10109 zao 10109 cong 10140 institut 10142 123 10142 diag 10148 cruce 10157 e 75 10169 hugo 10179 e 55 10184 neng 10192 beech 10196 vico 10203 commercial 10208 teng 10226 god 10227 pw 10234 centrum 10236 fazenda 10248 byr 10265 shady 10267 snt 10270 zona 10270 richard 10277 columbia 10309 magnit 10317 degli 10319 restaurante 10327 thompson 10349 douglas 10352 viux 10387 lewis 10390 resid 10394 allen 10409 conservation 10412 autobahn 10429 olive 10434 haut 10438 v roma 10441 kai 10457 docteur 10469 2nd st 10506 pub 10509 broad 10531 dirivnia 10539 bas 10540 130 10562 prado 10566 lutheran 10597 marsh 10597 e 40 10614 e 22 10624 inc 10632 jo 10643 shkolnaia 10650 parish 10655 canale 10678 per 10678 tres 10682 lyon 10688 quarry 10696 lakeviw 10700 gold 10707 ivana 10723 anderson 10724 alexander 10726 tour 10728 ainly 10746 shopping 10760 202 10766 160 10778 kreuz 10798 group 10801 300 10801 125 10810 118 10815 freiwillige 10818 e 60 10842 wai 10843 aptika 10847 tsintralnaia ul 10865 leclerc 10887 iv 10901 kong 10906 sea 10912 canadian 10922 tsirkov 10943 wolf 10944 grant 10959 division 10972 wi 10992 arthur 10996 16th 10997 graben 11019 elizabeth 11023 prospect 11025 flat 11038 122 11055 mesa 11081 180 11088 peters 11090 volksbank 11096 fernando 11125 alberto 11125 studio 11134 porto 11148 140 11154 spa 11167 150 11177 ana 11181 cao 11183 nicolas 11191 london 11194 grundschule 11208 mc 11211 brasil 11224 michal 11226 trinity 11229 gora 11236 otto 11246 martins 11248 produkty 11252 ferrovia 11272 114 11275 joaquim 11290 art 11299 oil 11328 recreation 11354 serra 11358 tri 11361 heinrich 11372 gabril 11381 cote 11387 msjd 11388 magnolia 11390 paulo 11391 course 11398 az 11416 paz 11417 starbucks 11423 ditch 11426 section 11433 03 11436 hans 11454 zhai 11455 md 11457 chun 11502 hamilton 11517 nursery 11521 luther 11537 luiz 11557 jorge 11615 tiao 11619 marys 11630 kct 11648 vi 11649 119 11681 twin 11686 alves 11694 109 11696 jules 11712 mare 11748 giacomo 11750 presidente 11760 lukoil 11761 salvador 11764 aldi 11765 nc 11770 palm 11777 lima 11781 mission 11786 nabirizhnaia 11794 wash 11803 gaulle 11806 frnt 11821 coronel 11824 posilinii 11858 castro 11861 magistral 11864 scott 11867 pnte 11885 medicine 11891 pde 11892 anna 11896 lopez 11907 mhmd 11925 r de leglise 11935 church st 11959 gasse 11984 13th 12001 ramon 12012 one 12013 pitro 12020 summit 12050 gagarina 12054 jnc 12069 nationale 12091 hair 12108 vittorio 12119 seven 12120 sadovaia 12121 tp 12139 border 12151 cypress 12156 nature 12165 francois 12166 heritage 12171 gou 12174 ward 12174 souza 12236 15th 12241 ferreira 12244 berliner 12259 spruce 12261 06 12287 117 12287 care 12317 holly 12333 four 12339 zavulak 12353 luigi 12355 121 12368 schul st 12370 nelson 12385 madison 12410 carlo 12424 kanal 12427 trrnt 12438 head 12454 14th 12469 poplar 12472 89 12475 europa 12476 bike 12478 mini 12508 petite 12520 principale 12527 clark 12528 puits 12541 ml 12553 a4 12559 pointe 12564 liberty 12576 hut 12581 ooo 12585 hope 12586 108 12591 biag 12591 bend 12612 ok 12616 nad 12619 bir 12663 pablo 12673 oude 12686 lk 12704 jan 12726 backerei 12739 fe 12743 kosciol 12744 salle 12763 x 12770 sun 12775 tee 12787 rocky 12795 93 12800 regional 12807 adams 12818 haven 12826 henry 12831 111 12834 dry 12862 wald 12928 santiago 12949 high st 12959 86 12964 stade 12992 woodland 12997 frtg 13072 1a 13087 pochta 13093 es 13105 harbor 13111 200 13120 madrid 13127 monument 13129 max 13136 ainbd 13141 wall 13153 federal transferido 13159 shinkansen 13164 eastern 13168 acres 13192 pool 13193 by 13201 97 13228 bk 13276 alter 13286 107 13297 bis 13308 112 13311 manol 13327 kg 13345 zai 13348 tennis 13353 on 13370 umm 13372 coop 13375 notre 13386 qaryat 13391 alta 13395 transferido 13448 cimetire 13463 liao 13465 wilhelm 13468 88 13476 spur 13502 mayo 13511 services 13521 bluff 13534 rail 13538 julio 13540 bn 13543 linea 13546 96 13558 agua 13568 novaia 13576 secondary 13590 106 13595 duo 13611 e 45 13623 see 13648 116 13650 pereira 13657 orange 13679 113 13723 acces 13736 miller 13751 cite 13764 top 13777 credit 13781 mas 13799 metro 13860 ul linina 13889 southern 13955 liniia 13959 ha 13995 horse 14006 sovitskaia ul 14013 pena 14029 deng 14070 estates 14080 campus 14095 mall 14100 mario 14110 depot 14110 12th 14119 andre 14125 sandy 14180 tpk 14191 congpateu 14198 can 14200 china 14206 oust 14224 lidl 14243 ali 14244 sol 14251 laguna 14266 byt 14281 crst 14289 johns 14332 91 14339 stadt 14380 cth 14401 loma 14403 total 14422 francesco 14431 ldg 14431 garcia 14457 david 14482 dame 14519 carrefour 14520 jefferson 14530 lakes 14541 champ 14548 103 14550 05 14554 tsintralnaia 14554 rathaus 14593 bg st 14596 prom 14606 taylor 14606 jacqus 14610 tou 14651 mdws 14665 04 14668 luo 14732 120 14738 ford 14744 schul 14764 115 14779 11th 14779 pta 14791 super 14807 esso 14820 beaver 14846 cm 14859 ni 14887 family 14888 jun 14958 air 14959 sbwy 14961 gra 14988 garage 14989 mwy 14991 company 15014 riverside 15016 pharmacy 15017 jdin 15021 84 15041 tx 15062 quen 15068 sovitskaia 15086 ai 15086 shuang 15109 funte 15128 sentir 15132 mao 15148 chestnut 15184 northern 15230 oziro 15237 pto 15271 jones 15288 marechal 15299 hickory 15307 ky 15313 ltd 15319 prairi 15327 henri 15359 edifc 15365 78 15368 garten 15394 xiong 15410 zhang 15424 primaire 15442 chapelle 15450 william 15450 fy 15461 110 15515 76 15542 burger 15550 kirch 15587 hang 15620 for 15647 dori 15659 franklin 15713 davis 15736 alefm 15755 masjid 15757 birch 15764 schule 15792 mira 15801 ad 15822 auf 15839 telekom 15841 victor 15851 sports 15879 lago 15892 france 15912 hou 15918 fur 15948 resort 15967 fridrich 15969 02 16045 os 16092 jl 16112 motel 16171 rulle 16196 vicente 16274 92 16275 brown 16276 nam 16282 mary 16357 rang 16374 kennedy 16392 rossii 16394 sirra 16403 mai 16461 private 16483 bp 16498 dai 16508 york 16526 junior 16610 dem 16612 accs 16616 87 16618 74 16633 huo 16651 williams 16673 83 16714 laurel 16720 marina 16725 peter 16753 prof 16773 82 16775 ta 16805 golden 16808 lao 16824 102 16852 neu 16858 leon 16888 sebun irebun 16890 69 16896 odos 16902 pre 16931 feurwer 16932 94 16982 day 17030 verde 17035 bahn 17037 escola 17040 tao 17130 chaussee 17148 academy 17172 coast 17191 shore 17193 104 17223 irebun 17227 grill 17250 gil 17299 karl 17365 schloss 17371 ao 17376 pan 17380 fen 17380 68 17488 10th 17515 man 17559 food 17623 lotissement 17623 kings 17652 pizzeria 17669 catholic 17679 highland 17681 bway 17698 christian 17733 sebun 17748 sv 17777 terminal 17795 side 17799 torre 17821 79 17832 zhuan 17845 gan 17857 kl 17935 end 17939 gen 17945 apts 17969 72 17986 swamp 18022 johnson 18034 jesus 18062 stwg 18067 jana 18073 zou 18075 jackson 18085 tall 18094 9th 18102 zu 18103 col 18118 qryte 18155 sand 18186 bell 18197 star 18367 coffee 18367 tsintr 18431 fa 18482 sparkasse 18490 lee 18514 mar 18531 intl 18589 padre 18626 cd 18628 banco 18638 lot 18663 peak 18668 kharunchinan 18676 maison 18711 cnr 18727 colegio 18730 cai 18768 health 18773 han 18822 ville 18845 heng 18849 magazin 18859 farmacia 18868 73 18910 pharmaci 18925 a2 18944 christ 18950 sunset 18993 dental 18994 wa 19046 fontaine 19094 eagle 19121 nahr 19130 autoroute 19141 81 19156 wilson 19248 kirche 19259 oliveira 19292 cami 19322 mex 19325 95 19337 silver 19346 stone 19398 campg 19457 deer 19465 tuo 19520 potok 19588 tr 19598 falls 19611 99 19690 nr 19733 pres 19737 apotheke 19747 salon 19803 wang 19828 real 19846 gui 19857 tongri 19864 sant 19990 105 20053 km 20057 byp 20087 63 20196 paris 20199 hof 20219 roma 20220 gas 20248 orchard 20316 cherry 20339 cang 20420 qiu 20421 bear 20488 cottage 20565 fish 20585 methodist 20611 hei 20625 sc 20670 ila 20676 alam 20682 8th 20697 linden 20710 mart 20767 abu 20787 fridhof 20791 zhao 20825 nacional 20827 fox 20869 53 20890 7 eleven 21015 santos 21034 tl 21084 woods 21111 sbirbank 21148 lan 21225 fl 21230 chisa 21289 trans 21325 georges 21339 56 21354 shossi 21371 ling 21527 47 21570 castle 21646 ro 21695 liang 21739 48 21742 deutsche post ag 21806 tun 21823 ti 21861 robert 21999 site 22042 croix 22071 walnut 22109 voi 22157 ku 22191 clos 22207 57 22220 yolu 22268 lian 22382 oaks 22406 alefbw 22413 santo 22425 85 22431 vila 22433 alto 22455 royal mail 22459 joseph 22482 ranch 22559 100 22590 ex 22591 58 22598 ny 22622 67 22676 men 22686 lincoln 22698 59 22742 smith 22749 proizd 22770 rosa 22793 bin 22797 rn 22809 61 22943 ind 22956 ft 23013 federal 23112 64 23292 pod 23405 val 23469 autostrada 23471 aux 23509 gare 23530 pleasant 23535 77 23599 chan 23691 petit 23694 provincial 23704 blk 23721 mari 23767 7th 23812 df st 23906 46 23941 ru 23973 iglesia 24010 manul 24017 department 24027 hts 24037 escula 24065 temple 24126 von 24236 george 24238 mus 24243 express 24275 zur 24277 bf st 24334 nong 24462 leglise 24465 elm 24484 zheng 24555 yun 24563 lo 24580 duan 24590 mnr 24590 shou 24633 rose 24694 tu 24769 autov 24772 doutor 24775 james 24776 ms 24795 kindergarten 24807 cha 24834 so 24965 arry 25038 rural 25188 54 25352 71 25362 49 25372 victoria 25373 tokaido 25374 min 25498 ya 25519 mcdonalds 25532 western 25637 lodge 25764 vulica 25862 port 25909 pu 25916 parc 25920 nhr 25943 xun 25947 bing 25957 thomas 25977 police 26007 development 26198 mail 26234 ss 26235 lou 26272 first 26384 glen 26402 sen 26501 praca 26562 great 26704 chu 26738 mairi 26742 costa 26766 62 26814 linina 26851 sport 26954 6th 26994 albert 27041 mid 27109 med 27190 luis 27284 ditskii 27304 louis 27315 ar 27367 wen 27380 cruz 27564 car 27661 43 27719 united 27764 migul 27834 po ste 27955 38 28034 pacific 28055 silva 28060 indian 28137 37 28187 ash 28213 ge 28217 fm 28289 giovanni 28308 carlos 28356 eleven 28358 rivire 28429 giuseppe 28629 tower 28689 land 28718 39 28773 sha 28793 canada 28821 52 29048 path 29140 stop 29219 51 29251 valle 29255 tank 29459 feng 29475 zhuang 29552 delle 29559 yuki 29561 pi 29606 black 29615 main st 29615 fild 29647 provulok 29735 lang 29988 nova 30275 5th 30439 lai 30468 home 30553 number 30841 pln 30869 norte 30969 chateau 31005 country 31137 ut 31138 42 31157 willow 31224 ing 31252 alte 31262 washington 31324 bach 31401 residence 31417 haus 31433 66 31469 zhan 31495 sad 31541 library 31641 90 31677 65 31679 hollow 31759 fan 31777 au 31828 01 31888 charles 31908 district 31960 public 32008 chapel 32219 rodovia 32287 golf 32328 44 32430 maple 32464 canyon 32513 huan 32526 a1 32605 ren 32607 36 32651 pont 32671 camp 32699 luan 32727 tree 32746 hills 32852 springs 32854 bl 32968 bai 33054 jabal 33224 nei 33241 deutsche post 33284 crss 33336 up 33421 33 33429 lr 33446 go 33491 41 33684 pr 33804 pqu 33815 te 33825 don 33858 bang 33873 town 33970 nord 33971 trk 33982 blu 34198 pa 34486 wlk 34562 mine 34590 4th 34616 yong 34666 32 34698 sud 34850 fwy 35052 dom 35084 zhou 35204 water 35221 zuo 35310 ke 35425 mei 35452 bois 35619 ci 35667 34 35764 cerro 35787 pizza 35851 eglise 35880 est 36076 hong 36127 ruta 36199 at 36250 vsta 36335 55 36356 railroad 36523 estda 36571 75 36587 pirre 36647 jbl 36709 moulin 36711 yao 36718 tang 36915 dan 37000 ia 37050 liu 37065 she 37152 80 37450 kj 37799 wan 38034 3rd 38101 univ 38304 inn 38352 101 38386 meadow 38445 shell 38518 john 38651 chi 38902 cedar 38927 70 38993 jiao 39047 tan 39564 29 39660 lin 39807 haupt st 39850 fire 39867 big 39989 1st 40093 paul 40325 q 40411 shkola 40538 guang 40751 zhen 40796 white 40864 wood 41246 psaje 41393 to 41429 xia 41541 mun 41573 prospikt 41627 clinic 41731 60 41766 high 41803 no 41890 2nd 42062 travessa 42121 joao 42221 baptist 42330 ding 42547 hs 42649 mo 42664 subashiri 42946 kou 42991 union 43101 red 43110 pedro 43133 vulitsa 43278 28 43318 centro 43549 area 43697 31 43754 grande 43777 fang 43797 shop 44139 ping 44225 bian 44268 26 44914 shu 44945 suo 44969 45 45093 ba 45139 king 45400 ps 45517 dei 45891 song 45915 airport 45915 bo 46143 as 46583 j 46759 sheng 46913 comm 46971 royal 47011 35 47126 memorial 47231 ag 47261 rock 47375 wei 47422 ming 47531 twp 47709 ligne 48058 run 48153 ca 48587 h 48838 bf 49002 qing 49106 bch 49383 haupt 49593 qun 49638 francisco 49774 historical 50153 27 50197 sur 50222 auto 50309 subdivision 50333 shui 50358 sh 50614 ecole 50720 huang 50815 sq 50999 tong 51054 shen 51085 juan 51506 casa 51658 spring 51698 f 51887 ring 51990 hosp 52226 bi 52273 lac 52298 23 52469 bu 52502 parking 52537 50 52748 jiang 52810 pond 52906 mi 52975 monte 52983 in 53365 zong 53683 service 54145 24 54306 rnge 54330 hua 54648 dos 54690 hui 54778 21 54840 sao 54843 shalefrain 54949 villa 54974 railway 55004 ma 55286 college 55538 wadi 55696 sp 55918 jian 56759 central 56798 gmbh 57668 building 57941 restaurant 57995 qian 58235 canal 58349 primary 58355 pine 58939 fork 59062 bus 59092 walefdy 59701 40 59916 martin 59929 state 60068 bao 60070 yang 60348 30 60532 general 61243 della 61416 hai 61518 ri 61538 lp 62036 z 62227 22 62480 elementary 62555 sk 63126 gang 63300 village 63354 jean 63390 cno 63692 19 63828 qiao 63997 res 64000 ban 64142 bay 64306 tai 64343 cty 64656 25 64886 ruisseau 65062 cun 66188 18 66430 shang 66598 yan 67479 17 68301 dam 68764 zhu 69205 16 69497 ben 69679 long 70045 rong 70327 mill 70565 br 70863 jiu 71012 si 71116 brook 71129 za 71491 bri 71830 office 71965 lt 72733 new 73340 13 73893 guan 73951 deutsche 74130 qu 74204 vw 74909 ctra 75117 is 75214 bar 75258 14 75360 er 75636 gn 76708 20 76850 antonio 76980 jia 77146 dian 77214 cra 77620 mtn 78205 maria 78369 club 78614 hu 79849 cv 80840 cafe 80861 national 81218 sr 81383 you 81623 pwy 81763 cheng 81948 qi 81973 oak 82366 xing 82697 jr 85189 mu 85596 15 85937 mt 86408 hall 86508 pt 86936 gdn 87358 12 89973 farm 90082 zhi 92470 jin 92996 ting 93199 ju 93546 ye 94266 forest 94898 valley 95558 piriulok 95976 rdge 95987 gu 96835 nan 96955 11 100782 mkt 102885 9 103700 xu 105576 xiang 105992 branch 106901 ter 106992 bei 107237 jalan 107405 o 111032 jose 111122 10 112150 wu 114385 df 115113 bank 116575 an 118367 po 118808 main 119153 sta 119997 8 120303 hotel 124402 les 124562 las 124958 imp 125292 zi 125401 xin 126669 stn 127305 chang 128223 ze 130400 m 130476 rio 131521 exp 132573 jing 134771 post 135622 weg 135769 fu 136761 xiao 137133 ho 137474 hao 138644 old 139352 se 140655 6 141057 su 143572 tian 145728 he 146233 xi 147676 ste 148083 guo 148686 do 148931 cemetery 152551 zhong 157373 sw 157423 los 158575 5 159912 li 160414 7 161870 i 163043 ne 164323 shan 170441 yu 173470 gao 173761 4 174622 gr 180258 p 181854 of 182568 gong 184716 hill 202083 ji 202858 dong 206101 yuan 214501 u 215042 cir 217761 ctr 221309 di 224884 vulitsia 229773 3 236911 shi 238272 hwy 243335 trl 246437 lake 251993 dao 258484 rt 280539 us 285677 church 294961 l 297582 g 299909 b 308815 k 309866 school 315017 co 318714 wy 318861 bd 328499 bg 348255 del 352599 da 360873 2 361083 lu 369480 chuan 400442 pk 406423 ch 439801 t 452465 al 454893 xian 501470 des 522073 w 585861 du 594005 d 656627 pl 693404 ct 703008 a 790949 1 812319 ul 927384 rua 943026 n 994578 e 1001521 s 1009933 cr 1030312 c 1234820 ln 1313273 v 1485853 dr 1579142 r 1682785 av 1996028 de 2473104 rd 3585705 st 5557484 \. -- -- PostgreSQL database dump complete -- -- prefill word table select count(precompute_words(v)) from (select distinct svals(name) as v from place) as w where v is not null; select count(getorcreate_housenumber_id(make_standard_name(v))) from (select distinct address->'housenumber' as v from place where address ? 'housenumber') as w; -- copy the word frequencies update word set search_name_count = count from word_frequencies wf where wf.word_token = word.word_token; -- and drop the temporary frequency table again drop table word_frequencies; ================================================ FILE: docs/admin/Advanced-Installations.md ================================================ # Advanced installations This page contains instructions for setting up multiple countries in your Nominatim database. It is assumed that you have already successfully installed the Nominatim software itself, if not return to the [installation page](Installation.md). ## Importing with a database user without superuser rights Nominatim usually creates its own PostgreSQL database at the beginning of the import process. This makes usage easier for the user but means that the database user doing the import needs the appropriate rights. If you prefer to run the import with a database user with limited rights, you can do so by changing the import process as follows: 1. Run the command for database preparation with a database user with superuser rights. For example, to use a db user 'dbadmin' for a database 'nominatim', execute: ``` NOMINATIM_DATABASE_DSN="pgsql:dbname=nominatim;user=dbadmin" nominatim import --prepare-database ``` 2. Grant the import user the right to create tables. For example, foe user 'import-user': ``` psql -d nominatim -c 'GRANT CREATE ON SCHEMA public TO "import-user"' ``` 3. Now run the reminder of the import with the import user: ``` NOMINATIM_DATABASE_DSN="pgsql:dbname=nominatim;user=import-user" nominatim import --continue import-from-file --osm-file file.pbf ``` ## Importing multiple regions (without updates) To import multiple regions in your database you can simply give multiple OSM files to the import command: ``` nominatim import --osm-file file1.pbf --osm-file file2.pbf ``` If you already have imported a file and want to add another one, you can use the add-data function to import the additional data as follows: ``` nominatim add-data --file nominatim refresh --postcodes nominatim index -j ``` Please note that adding additional data is always significantly slower than the original import. ## Importing multiple regions (with updates) If you want to import multiple regions _and_ be able to keep them up-to-date with updates, then you can use the scripts provided in the `utils` directory. These scripts will set up an `update` directory in your project directory, which has the following structure: ```bash update ├── europe │ ├── andorra │ │ └── sequence.state │ └── monaco │ └── sequence.state └── tmp └── europe ├── andorra-latest.osm.pbf └── monaco-latest.osm.pbf ``` The `sequence.state` files contain the sequence ID for each region. They will be used by pyosmium to get updates. The `tmp` folder is used for import dump and can be deleted once the import is complete. ### Setting up multiple regions Create a project directory as described for the [simple import](Import.md#creating-the-project-directory). If necessary, you can also add an `.env` configuration with customized options. In particular, you need to make sure that `NOMINATIM_REPLICATION_UPDATE_INTERVAL` and `NOMINATIM_REPLICATION_RECHECK_INTERVAL` are set according to the update interval of the extract server you use. Copy the scripts `utils/import_multiple_regions.sh` and `utils/update_database.sh` into the project directory. Now customize both files as per your requirements 1. List of countries. e.g. COUNTRIES="europe/monaco europe/andorra" 2. URL to the service providing the extracts and updates. eg: BASEURL="https://download.geofabrik.de" DOWNCOUNTRYPOSTFIX="-latest.osm.pbf" 5. Followup in the update script can be set according to your installation. E.g. for Photon, FOLLOWUP="curl http://localhost:2322/nominatim-update" will handle the indexing. To start the initial import, change into the project directory and run ``` bash import_multiple_regions.sh ``` ### Updating the database Change into the project directory and run the following command: bash update_database.sh This will get diffs from the replication server, import diffs and index the database. The default replication server in the script ([Geofabrik](https://download.geofabrik.de)) provides daily updates. ## Using an external PostgreSQL database You can install Nominatim using a database that runs on a different server. Simply point the configuration variable `NOMINATIM_DATABASE_DSN` to the server and follow the standard import documentation. The import will be faster, if the import is run directly from the database machine. You can easily switch to a different machine for the query frontend after the import. ## Moving the database to another machine For some configurations it may be useful to run the import on one machine, then move the database to another machine and run the Nominatim service from there. For example, you might want to use a large machine to be able to run the import quickly but only want a smaller machine for production because there is not so much load. Or you might want to do the import once and then replicate the database to many machines. The important thing to keep in mind when transferring the Nominatim installation is that you need to transfer the database _and the project directory_. Both parts are essential for your installation. The Nominatim database can be transferred using the `pg_dump`/`pg_restore` tool. Make sure to use the same version of PostgreSQL and PostGIS on source and target machine. !!! note Before creating a dump of your Nominatim database, consider running `nominatim freeze` first. Your database looses the ability to receive further data updates but the resulting database is only about a third of the size of a full database. Next install nominatim-api on the target machine by following the standard installation instructions. Again, make sure to use the same version as the source machine. Create a project directory on your destination machine and set up the `.env` file to match the configuration on the source machine. That's all. ================================================ FILE: docs/admin/Deployment-Python.md ================================================ # Deploying the Nominatim Python frontend Nominatim can be run as a Python-based [ASGI web application](https://asgi.readthedocs.io/en/latest/). You have the choice between [Falcon](https://falcon.readthedocs.io/en/stable/) and [Starlette](https://www.starlette.io/) as the ASGI framework. This section gives a quick overview on how to configure Nginx to serve Nominatim. Please refer to the documentation of [Nginx](https://nginx.org/en/docs/) for background information on how to configure it. ### Installing the required packages !!! warning ASGI support in gunicorn requires at least version 25.0. If you need to work with an older version of gunicorn, please refer to [older Nominatim deployment documentation](https://nominatim.org/release-docs/5.2/admin/Deployment-Python/) to learn how to run gunicorn with uvicorn. The Nominatim frontend is best run from its own virtual environment. If you have already created one for the database backend during the [installation](Installation.md#building-nominatim), you can use that. Otherwise create one now with: ```sh sudo apt-get install virtualenv virtualenv /srv/nominatim-venv ``` The Nominatim frontend is contained in the 'nominatim-api' package. To install directly from the source tree run: ```sh cd Nominatim /srv/nominatim-venv/bin/pip install packaging/nominatim-api ``` The recommended way to deploy a Python ASGI application is to run the [gunicorn](https://gunicorn.org/) HTTP server. We use Falcon here as the web framework. Add the necessary packages to your virtual environment: ``` sh /srv/nominatim-venv/bin/pip install falcon gunicorn ``` ### Setting up Nominatim as a systemd job !!! Note These instructions assume your Nominatim project directory is located in `/srv/nominatim-project`. If you have put it somewhere else, you need to adjust the commands and configuration accordingly. Next you need to set up the service that runs the Nominatim frontend. This is easiest done with a systemd job. First you need to tell systemd to create a socket file to be used by gunicorn. Create the following file `/etc/systemd/system/nominatim.socket`: ``` systemd [Unit] Description=Gunicorn socket for Nominatim [Socket] ListenStream=/run/nominatim.sock SocketUser=www-data [Install] WantedBy=multi-user.target ``` Now you can add the systemd service for Nominatim itself. Create the following file `/etc/systemd/system/nominatim.service`: ``` systemd [Unit] Description=Nominatim running as a gunicorn application After=network.target Requires=nominatim.socket [Service] Type=simple User=www-data Group=www-data WorkingDirectory=/srv/nominatim-project ExecStart=/srv/nominatim-venv/bin/gunicorn -b unix:/run/nominatim.sock -w 4 --worker-class asgi --protocol uwsgi --worker-connections 1000 "nominatim_api.server.falcon.server:run_wsgi()" ExecReload=/bin/kill -s HUP $MAINPID PrivateTmp=true TimeoutStopSec=5 KillMode=mixed [Install] WantedBy=multi-user.target ``` This sets up gunicorn with 4 workers (`-w 4` in ExecStart). Each worker runs its own Python process using [`NOMINATIM_API_POOL_SIZE`](../customize/Settings.md#nominatim_api_pool_size) connections to the database to serve requests in parallel. The parameter `--worker-connections` restricts how many requests gunicorn will queue for each worker. This can help distribute work better when the server is under high load. Make the new services known to systemd and start it: ``` sh sudo systemctl daemon-reload sudo systemctl enable nominatim.socket sudo systemctl start nominatim.socket sudo systemctl enable nominatim.service sudo systemctl start nominatim.service ``` This sets the service up so that Nominatim is automatically started on reboot. ### Configuring nginx To make the service available to the world, you need to proxy it through nginx. We use the binary uwsgi protocol to speed up communication between nginx and gunicorn. Add the following definition to the default configuration: ``` nginx upstream nominatim_service { server unix:/run/nominatim.sock fail_timeout=0; } server { listen 80; listen [::]:80; root /var/www/html; index /search; location / { uwsgi_pass nominatim_service; include uwsgi_params; } } ``` Reload nginx with ``` sudo systemctl reload nginx ``` and you should be able to see the status of your server under `http://localhost/status`. ================================================ FILE: docs/admin/Faq.md ================================================ # Troubleshooting Nominatim Installations ## Installation Issues ### Can a stopped/killed import process be resumed? "I accidentally killed the import process after it has been running for many hours. Can it be resumed?" It is possible if the import already got to the indexing stage. Check the last line of output that was logged before the process was killed. If it looks like this: Done 844 in 13 @ 64.923080 per second - Rank 26 ETA (seconds): 7.886255 then you can resume with the following command: ```sh nominatim import --continue indexing ``` If the reported rank is 26 or higher, you can also safely add `--index-noanalyse`. ### PostgreSQL crashed "invalid page in block" Usually serious problem, can be a hardware issue, not all data written to disc for example. Check PostgreSQL log file and search PostgreSQL issues/mailing list for hints. If it happened during index creation you can try rerunning the step with ```sh nominatim import --continue indexing ``` Otherwise it's best to start the full setup from the beginning. ### I see the error: "ERROR: mmap (remap) failed" This may be a simple out-of-memory error. Try reducing the memory used for `--osm2pgsql-cache`. Also make sure that overcommitting memory is allowed: `cat /proc/sys/vm/overcommit_memory` should print 0 or 1. If you are using a flatnode file, then it may also be that the underlying filesystem does not fully support 'mmap'. A notable candidate is virtualbox's vboxfs. ### nominatim UPDATE failed: ERROR: buffer 179261 is not owned by resource owner Portal Several users [reported this](https://github.com/openstreetmap/Nominatim/issues/1168) during the initial import of the database. It's something PostgreSQL internal Nominatim doesn't control. And PostgreSQL forums suggest it's threading related but definitely some kind of crash of a process. Users reported either rebooting the server, different hardware or just trying the import again worked. ### The website shows: "Could not get word tokens" The server cannot access your database. Add `&debug=1` to your URL to get the full error message. ### Website reports "DB Error: insufficient permissions" The user the webserver, e.g. Apache, runs under needs to have access to the Nominatim database. You can find the user like [this](https://serverfault.com/questions/125865/finding-out-what-user-apache-is-running-as), for default Ubuntu operating system for example it's `www-data`. 1. Repeat the `createuser` step of the installation instructions. 2. Give the user permission to existing tables ``` GRANT usage ON SCHEMA public TO "www-data"; GRANT SELECT ON ALL TABLES IN SCHEMA public TO "www-data"; ``` ### Setup fails with "DB Error: extension not found" Make sure you have the PostgreSQL extensions "hstore" and "postgis" installed. See the installation instructions for a full list of required packages. ### UnicodeEncodeError: 'ascii' codec can't encode character Make sure that the operating system's locale is UTF-8. With some prebuilt images (e.g. LXC containers from Proxmox, see [discussion](https://github.com/osm-search/Nominatim/discussions/2343)) or images that optimize for size it might be missing. On Ubuntu you can check the locale is installed: ``` grep UTF-8 /etc/default/locale ``` And install it using ``` dpkg-reconfigure locales ``` ### I forgot to delete the flatnodes file before starting an import. That's fine. For each import the flatnodes file get overwritten. See [https://help.openstreetmap.org/questions/52419/nominatim-flatnode-storage](https://help.openstreetmap.org/questions/52419/nominatim-flatnode-storage) for more information. ## Running your own instance ### Can I import negative OSM ids into Nominatim? No, negative IDs are no longer supported by osm2pgsql. You can use large 64-bit IDs that are guaranteed not to clash with OSM IDs. However, you will not able to use a flatnode file with them. ================================================ FILE: docs/admin/Import.md ================================================ # Importing the Database The following instructions explain how to create a Nominatim database from an OSM planet file. It is assumed that you have already successfully installed the Nominatim software itself and the `nominatim` tool can be found in your `PATH`. If this is not the case, return to the [installation page](Installation.md). ## Creating the project directory Before you start the import, you should create a project directory for your new database installation. This directory receives all data that is related to a single Nominatim setup: configuration, extra data, etc. Create a project directory apart from the Nominatim software and change into the directory: ``` mkdir ~/nominatim-project cd ~/nominatim-project ``` In the following, we refer to the project directory as `$PROJECT_DIR`. To be able to copy&paste instructions, you can export the appropriate variable: ``` export PROJECT_DIR=~/nominatim-project ``` The Nominatim tool assumes per default that the current working directory is the project directory but you may explicitly state a different directory using the `--project-dir` parameter. The following instructions assume that you run all commands from the project directory. !!! tip "Migration Tip" Nominatim used to be run directly from the build directory until version 3.6. Essentially, the build directory functioned as the project directory for the database installation. This setup still works and can be useful for development purposes. It is not recommended anymore for production setups. Create a project directory that is separate from the Nominatim software. ### Configuration setup in `.env` The Nominatim server can be customized via an `.env` configuration file in the project directory. This is a file in [dotenv](https://github.com/theskumar/python-dotenv) format which looks the same as variable settings in a standard shell environment. You can also set the same configuration via environment variables. All settings have a `NOMINATIM_` prefix to avoid conflicts with other environment variables. There are lots of configuration settings you can tweak. A full reference can be found in the chapter [Configuration Settings](../customize/Settings.md). Most should have a sensible default. #### Flatnode files If you plan to import a large dataset (e.g. Europe, North America, planet), you should also enable flatnode storage of node locations. With this setting enabled, node coordinates are stored in a simple file instead of the database. This will save you import time and disk storage. Add to your `.env`: NOMINATIM_FLATNODE_FILE="/path/to/flatnode.file" Replace the second part with a suitable path on your system and make sure the directory exists. There should be at least 75GB of free space. ## Downloading additional data ### Wikipedia/Wikidata rankings Wikipedia can be used as an optional auxiliary data source to help indicate the importance of OSM features. Nominatim will work without this information but it will improve the quality of the results if this is installed. This data is available as a binary download. Put it into your project directory: cd $PROJECT_DIR wget https://nominatim.org/data/wikimedia-importance.csv.gz wget -O secondary_importance.sql.gz https://nominatim.org/data/wikimedia-secondary-importance.sql.gz The files are about 400MB and add around 4GB to the Nominatim database. For more information about importance, see [Importance Customization](../customize/Importance.md). !!! tip If you forgot to download the wikipedia rankings, then you can also add importances after the import. Download the SQL files, then run `nominatim refresh --wiki-data --secondary-importance --importance`. Updating importances for a planet will take a couple of hours. ### External postcodes Nominatim can use postcodes from an external source to improve searching with postcodes. We provide precomputed postcodes sets for the US (using TIGER data) and the UK (using the [CodePoint OpenData set](https://osdatahub.os.uk/downloads/open/CodePointOpen). This data can be optionally downloaded into the project directory: cd $PROJECT_DIR wget https://nominatim.org/data/gb_postcodes.csv.gz wget https://nominatim.org/data/us_postcodes.csv.gz You can also add your own custom postcode sources, see [Customization of postcodes](../customize/Postcodes.md). ## Choosing the data to import In its default setup Nominatim is configured to import the full OSM data set for the entire planet. Such a setup requires a powerful machine with at least 64GB of RAM and around 900GB of SSD hard disks. Depending on your use case there are various ways to reduce the amount of data imported. This section discusses these methods. They can also be combined. ### Using an extract If you only need geocoding for a smaller region, then precomputed OSM extracts are a good way to reduce the database size and import time. [Geofabrik](https://download.geofabrik.de) offers extracts for most countries. They even have daily updates which can be used with the update process described [in the next section](Update.md). There are also [other providers for extracts](https://wiki.openstreetmap.org/wiki/Planet.osm#Downloading). Please be aware that some extracts are not cut exactly along the country boundaries. As a result some parts of the boundary may be missing which means that Nominatim cannot compute the areas for some administrative areas. ### Dropping Data Required for Dynamic Updates About half of the data in Nominatim's database is not really used for serving the API. It is only there to allow the data to be updated from the latest changes from OSM. For many uses these dynamic updates are not really required. If you don't plan to apply updates, you can run the import with the `--no-updates` parameter. This will drop the dynamic part of the database as soon as it is not required anymore. You can also drop the dynamic part later using the following command: ``` nominatim freeze ``` Note that you still need to provide for sufficient disk space for the initial import. So this option is particularly interesting if you plan to transfer the database or reuse the space later. !!! warning The data structure for updates are also required when adding additional data after the import, for example [TIGER housenumber data](../customize/Tiger.md). If you plan to use those, you must not use the `--no-updates` parameter. Do a normal import, add the external data and once you are done with everything run `nominatim freeze`. ### Reverse-only Imports If you only want to use the Nominatim database for reverse lookups or if you plan to use the installation only for exports to a [photon](https://photon.komoot.io/) database, then you can set up a database without search indexes. Add `--reverse-only` to your setup command above. This saves about 5% of disk space, import time won't be significant faster. ### Filtering Imported Data Nominatim normally sets up a full search database containing administrative boundaries, places, streets, addresses and POI data. There are also other import styles available which only read selected data: * **admin** Only import administrative boundaries and places. * **street** Like the admin style but also adds streets. * **address** Import all data necessary to compute addresses down to house number level. * **full** Default style that also includes points of interest. * **extratags** Like the full style but also adds most of the OSM tags into the extratags column. The style can be changed with the configuration `NOMINATIM_IMPORT_STYLE`. To give you an idea of the impact of using the different styles, the table below gives rough estimates of the final database size after import of a 2020 planet and after using the `--drop` option. It also shows the time needed for the import on a machine with 64GB RAM, 4 CPUS and NVME disks. Note that the given sizes are just an estimate meant for comparison of style requirements. Your planet import is likely to be larger as the OSM data grows with time. style | Import time | DB size | after drop ----------|--------------|------------|------------ admin | 4h | 215 GB | 20 GB street | 22h | 440 GB | 185 GB address | 36h | 545 GB | 260 GB full | 54h | 640 GB | 330 GB extratags | 54h | 650 GB | 340 GB You can also customize the styles further. A [description of the style format](../customize/Import-Styles.md) can be found in the customization guide. ## Initial import of the data !!! danger "Important" First try the import with a small extract, for example from [Geofabrik](https://download.geofabrik.de). Download the data to import. Then issue the following command from the **project directory** to start the import: ```sh nominatim import --osm-file 2>&1 | tee setup.log ``` The **project directory** is the one that you have set up at the beginning. See [creating the project directory](#creating-the-project-directory). ### Notes on full planet imports Even on a perfectly configured machine the import of a full planet takes around 2 days. Once you see messages with `Rank .. ETA` appear, the indexing process has started. This part takes the most time. There are 30 ranks to process. Rank 26 and 30 are the most complex. They take each about a third of the total import time. If you have not reached rank 26 after two days of import, it is worth revisiting your system configuration as it may not be optimal for the import. ### Notes on memory usage In the first step of the import Nominatim uses [osm2pgsql](https://osm2pgsql.org) to load the OSM data into the PostgreSQL database. This step is very demanding in terms of RAM usage. osm2pgsql and PostgreSQL are running in parallel at this point. PostgreSQL blocks at least the part of RAM that has been configured with the `shared_buffers` parameter during [PostgreSQL tuning](Installation.md#tuning-the-postgresql-database) and needs some memory on top of that. osm2pgsql needs at least 2GB of RAM for its internal data structures, potentially more when it has to process very large relations. In addition it needs to maintain a cache for node locations. The size of this cache can be configured with the parameter `--osm2pgsql-cache`. When importing with a flatnode file, it is best to disable the node cache completely and leave the memory for the flatnode file. Nominatim will do this by default, so you do not need to configure anything in this case. For imports without a flatnode file, set `--osm2pgsql-cache` approximately to the size of the OSM pbf file you are importing. The size needs to be given in MB. Make sure you leave enough RAM for PostgreSQL and osm2pgsql as mentioned above. If the system starts swapping or you are getting out-of-memory errors, reduce the cache size or even consider using a flatnode file. ### Testing the installation Run this script to verify that all required tables and indices got created successfully. ```sh nominatim admin --check-database ``` If you have installed the `nominatim-api` package, then you can try out your installation by executing a simple query on the command line: ``` sh nominatim search --query Berlin ``` or, when you have a reverse-only installation: ``` sh nominatim reverse --lat 51 --lon 45 ``` If you want to run Nominatim as a service, make sure you have installed the right packages as per [Installation](Installation.md#software). #### Testing the Python frontend To run the test server against the Python frontend, you must choose a web framework to use, either starlette or falcon. Make sure the appropriate packages are installed. Then run ``` sh nominatim serve ``` or, if you prefer to use Starlette instead of Falcon as webserver, ``` sh nominatim serve --engine starlette ``` Go to `http://localhost:8088/status` and you should see the message `OK`. You can also run a search query, e.g. `http://localhost:8088/search?q=Berlin` or, for reverse-only installations a reverse query, e.g. `http://localhost:8088/reverse?lat=27.1750090510034&lon=78.04209025`. Do not use this test server in production. To run Nominatim via webservers like Apache or nginx, please continue reading [Deploy the Python frontend](Deployment-Python.md). ## Enabling search by category phrases To be able to search for places by their type using [special phrases](https://wiki.openstreetmap.org/wiki/Nominatim/Special_Phrases) you also need to import these key phrases like this: ```sh nominatim special-phrases --import-from-wiki ``` Note that this command downloads the phrases from the wiki link above. You need internet access for the step. You can also import special phrases from a csv file, for more information please see the [Customization part](../customize/Special-Phrases.md). ================================================ FILE: docs/admin/Installation.md ================================================ # Basic Installation This page contains generic installation instructions for Nominatim and its prerequisites. There are also step-by-step instructions available for the following operating systems: * [Ubuntu 24.04](Install-on-Ubuntu-24.md) * [Ubuntu 22.04](Install-on-Ubuntu-22.md) These OS-specific instructions can also be found in executable form in the `vagrant/` directory. Users have created instructions for other frameworks. We haven't tested those and can't offer support. * [Docker](https://github.com/mediagis/nominatim-docker) * [Docker on Kubernetes](https://github.com/peter-evans/nominatim-k8s) * [Kubernetes with Helm](https://github.com/robjuz/helm-charts/blob/master/charts/nominatim/README.md) * [Ansible](https://github.com/synthesio/infra-ansible-nominatim) ## Prerequisites ### Software For running Nominatim: * [PostgreSQL](https://www.postgresql.org) (12+ will work, 13+ strongly recommended) * [PostGIS](https://postgis.net) (3.0+ will work, 3.2+ strongly recommended) * [osm2pgsql](https://osm2pgsql.org) (1.8+) * [Python 3](https://www.python.org/) (3.9+) Furthermore the following Python libraries are required: * [Psycopg3](https://www.psycopg.org) * [Python Dotenv](https://github.com/theskumar/python-dotenv) * [psutil](https://github.com/giampaolo/psutil) * [Jinja2](https://palletsprojects.com/p/jinja/) * [PyICU](https://pypi.org/project/PyICU/) * [PyYaml](https://pyyaml.org/) (5.1+) * [mwparserfromhell](https://github.com/earwig/mwparserfromhell/) These will be installed automatically when using pip installation. For running continuous updates: * [pyosmium](https://osmcode.org/pyosmium/) For running the Python frontend: * [SQLAlchemy](https://www.sqlalchemy.org/) (1.4.31+ with greenlet support) * [asyncpg](https://magicstack.github.io/asyncpg) (0.8+, only when using SQLAlchemy < 2.0) * one of the following web frameworks: * [falcon](https://falconframework.org/) (3.0+) * [starlette](https://www.starlette.io/) * [uvicorn](https://www.uvicorn.org/) For dependencies for running tests and building documentation, see the [Development section](../develop/Development-Environment.md). ### Hardware A minimum of 2GB of RAM is required or installation will fail. For a full planet import 128GB of RAM or more are strongly recommended. Do not report out of memory problems if you have less than 64GB RAM. For a full planet install you will need at least 1TB of hard disk space. Take into account that the OSM database is growing fast. Fast disks are essential. Using NVME disks is recommended. Even on a well configured machine the import of a full planet takes around 2.5 days. When using traditional SSDs, 4-5 days are more realistic. ## Tuning the PostgreSQL database You might want to tune your PostgreSQL installation so that the later steps make best use of your hardware. You should tune the following parameters in your `postgresql.conf` file. shared_buffers = 2GB maintenance_work_mem = (10GB) autovacuum_work_mem = 2GB work_mem = (50MB) synchronous_commit = off max_wal_size = 1GB checkpoint_timeout = 60min checkpoint_completion_target = 0.9 random_page_cost = 1.0 wal_level = minimal max_wal_senders = 0 The numbers in brackets behind some parameters seem to work fine for 128GB RAM machine. Adjust to your setup. A higher number for `max_wal_size` means that PostgreSQL needs to run checkpoints less often but it does require the additional space on your disk. Autovacuum must not be switched off because it ensures that the tables are frequently analysed. If your machine has very little memory, you might consider setting: autovacuum_max_workers = 1 and even reduce `autovacuum_work_mem` further. This will reduce the amount of memory that autovacuum takes away from the import process. ## Installing the latest release Nominatim is easiest installed directly from Pypi. Make sure you have installed osm2pgsql, PostgreSQL/PostGIS and libICU together with its header files. Then you can install Nominatim with: pip install nominatim-db nominatim-api ## Downloading and building Nominatim ### Downloading the latest release You can download the [latest release from nominatim.org](https://nominatim.org/downloads/). The release contains all necessary files. Just unpack it. ### Downloading the latest development version If you want to install latest development version from github: ``` git clone https://github.com/osm-search/Nominatim.git ``` The development version does not include the country grid. Download it separately: ``` wget -O Nominatim/data/country_osm_grid.sql.gz https://nominatim.org/data/country_grid.sql.gz ``` ### Building Nominatim Nominatim is easiest to run from its own virtual environment. To create one, run: sudo apt-get install virtualenv virtualenv /srv/nominatim-venv To install Nominatim directly from the source tree into the virtual environment, run: /srv/nominatim-venv/bin/pip install packaging/nominatim-{db,api} Now continue with [importing the database](Import.md). ================================================ FILE: docs/admin/Maintenance.md ================================================ This chapter describes the various operations the Nominatim database administrator may use to clean and maintain the database. None of these operations is mandatory but they may help improve the performance and accuracy of results. ## Updating postcodes Command: `nominatim refresh --postcodes` Postcode centroids (aka 'calculated postcodes') are generated by looking at all postcodes of a country, grouping them and calculating the geometric centroid. There is currently no logic to deal with extreme outliers (typos or other mistakes in OSM data). There is also no check if a postcodes adheres to a country's format, e.g. if Swiss postcodes are 4 digits. When running regular updates, postcodes results can be improved by running this command on a regular basis. Note that only the postcode table and the postcode search terms are updated. The postcode that is assigned to each place is only updated when the place is updated. The command takes around 70min to run on the planet and needs ca. 40GB of temporary disk space. ## Updating word counts Command: `nominatim refresh --word-counts` Nominatim keeps frequency statistics about all search terms it indexes. These statistics are currently used to optimise queries to the database. Thus better statistics mean better performance. Word counts are created once after import and are usually sufficient even when running regular updates. You might want to rerun the statistics computation when adding larger amounts of new data, for example, when adding an additional country via `nominatim add-data`. ## Forcing recomputation of places and areas Command: `nominatim refresh --data-object [NWR] --data-area [NWR]` When running replication updates, Nominatim tries to recompute the search and address information for all places that are affected by a change. But it needs to restrict the total number of changes to make sure it can keep up with the minutely updates. Therefore it will refrain from propagating changes that affect a lot of objects. The administrator may force an update of places in the database. `nominatim refresh --data-object` invalidates a single OSM object. `nominatim refresh --data-area` invalidates an OSM object and all dependent objects. That are usually the places that inside its area or around the center of the object. Both commands expect the OSM object as an argument of the form OSM type + OSM id. The type must be `N` (node), `W` (way) or `R` (relation). After invalidating the object, indexing must be run again. If continuous update are running in the background, the objects will be recomputed together with the next round of updates. Otherwise you need to run `nominatim index` to finish the recomputation. ## Removing large deleted objects Command: `nominatim admin --clean-deleted ` Nominatim refuses to delete very large areas because often these deletions are accidental and are reverted within hours. Instead the deletions are logged in the `import_polygon_delete` table and left to the administrator to clean up. To run this command you will need to pass a PostgreSQL time interval. For example to delete any objects that have been deleted more than a month ago you would run: `nominatim admin --clean-deleted '1 month'` ================================================ FILE: docs/admin/Migration.md ================================================ # Database Migrations Nominatim offers automatic migrations for versions 4.3+. Please follow the following steps: * Stop any updates that are potentially running * Update the backend: `pip install -U nominatim-db` * Go to your project directory and run `nominatim admin --migrate` * Update the frontend: `pip install -U nominatim-api` * (optionally) Restart updates Below you find additional migrations and hints about other structural and breaking changes. **Please read them before running the migration.** !!! note If you are migrating from a version <4.3, you need to install 4.3 and migrate to 4.3 first. Then you can migrate to the current version. It is strongly recommended to do a reimport instead. ## 5.2.0 -> 5.3.0 #### Expensive table migrations This version introduces new tables for postcodes, interpolations and associatedStreet relations. Migration is possible but will take a bit longer. It is recommended to take Nominatim offline while migrating to 5.3. ## 5.1.0 -> 5.2.0 ### Lua import style: required extratags removed Tags that are required by Nominatim as extratags are now always included independent of what is defined in the style. The line flex.add_for_extratags('required') is no longer required in custom styles and will throw an error. Simply remove the line from your style. ## 4.5.0 -> 5.0.0 ### PHP frontend removed The PHP frontend has been completely removed. Please switch to the Python frontend. Without the PHP code, the `nominatim refresh --website` command is no longer needed. It currently omits a warning and does otherwise nothing. It will be removed in later versions of Nominatim. So make sure you remove it from your scripts. ### CMake building removed Nominatim can now only be installed via pip. Please follow the installation instructions for the current version to change to pip. ### osm2pgsql no longer vendored in Nominatim no longer ships its own version of osm2pgsql. Please install a stock version of osm2pgsql from your distribution. See the [installation instruction for osm2pgsql](https://osm2pgsql.org/doc/install.html) for details. A minimum version of 1.8 is required. The current stable versions of Ubuntu and Debian already ship with an appropriate versions. For older installations, you may have to compile a newer osm2pgsql yourself. ### Legacy tokenizer removed The `legacy` tokenizer is no longer enabled. This tokenizer has been superseded by the `ICU` tokenizer a long time ago. In the unlikely case that your database still uses the `legacy` tokenizer, you must reimport your database. ### osm2pgsql style overhauled There are some fundamental changes to how customized osm2pgsql styles should be written. The changes are mostly backwards compatible, i.e. custom styles should still work with the new implementation. The only exception is a customization of the `process_tags()` function. This function is no longer considered public and neither are the helper functions used in it. They currently still work but will be removed at some point. If you have been making changes to `process_tags`, please review your style and try to switch to the new convenience functions. For more information on the changes, see the [pull request](https://github.com/osm-search/Nominatim/pull/3615) and read the new [customization documentation](https://nominatim.org/release-docs/latest/customize/Import-Styles/). ## 4.4.0 -> 4.5.0 ### New structure for Python packages The nominatim Python package has been split into `nominatim-db` and `nominatim-api`. Any imports need to be adapted accordingly. If you are running the Python frontend, change the server module from `nominatim.server.falcon.server` to `nominatim_api.server.falcon.server`. If you are using the Nominatim library, all imports need to be changed from `nominatim.api.` to `nominatim_api.`. If you have written custom tokenizers or sanitizers, the appropriate modules are now found in `nominatim_db`. ## 4.2.0 -> 4.3.0 ### New indexes for reverse lookup The reverse lookup algorithm has changed slightly to improve performance. This change needs a different index in the database. The required index will be automatically build during migration. Until the new index is available performance of the /reverse endpoint is significantly reduced. You should therefore either remove traffic from the machine before attempting a version update or create the index manually **before** starting the update using the following SQL: ```sql CREATE INDEX IF NOT EXISTS idx_placex_geometry_reverse_lookupPlaceNode ON placex USING gist (ST_Buffer(geometry, reverse_place_diameter(rank_search))) WHERE rank_address between 4 and 25 AND type != 'postcode' AND name is not null AND linked_place_id is null AND osm_type = 'N'; ``` ## 4.0.0 -> 4.1.0 ### ICU tokenizer is the new default Nominatim now installs the [ICU tokenizer](../customize/Tokenizers.md#icu-tokenizer) by default. This only has an effect on newly installed databases. When updating older databases, it keeps its installed tokenizer. If you still run with the legacy tokenizer, make sure to compile Nominatim with the PostgreSQL module, see [Installation](Installation.md#building-nominatim). ### geocodejson output changed The `type` field of the geocodejson output has changed. It now contains the address class of the object instead of the value of the OSM tag. If your client has used the `type` field, switch them to read `osm_value` instead. ## 3.7.0 -> 4.0.0 ### NOMINATIM_PHRASE_CONFIG removed Custom blacklist configurations for special phrases now need to be handed with the `--config` parameter to `nominatim special-phrases`. Alternatively you can put your custom configuration in the project directory in a file named `phrase-settings.json`. Version 3.8 also removes the automatic converter for the php format of the configuration in older versions. If you are updating from Nominatim < 3.7 and still work with a custom `phrase-settings.php`, you need to manually convert it into a json format. ### PHP utils removed The old PHP utils have now been removed completely. You need to switch to the appropriate functions of the nominatim command line tool. See [Introducing `nominatim` command line tool](#introducing-nominatim-command-line-tool) below. ## 3.6.0 -> 3.7.0 ### New format and name of configuration file The configuration for an import is now saved in a `.env` file in the project directory. This file follows the dotenv format. For more information, see the [installation chapter](Import.md#configuration-setup-in-env). To migrate to the new system, create a new project directory, add the `.env` file and port your custom configuration from `settings/local.php`. Most settings are named similar and only have received a `NOMINATIM_` prefix. Use the default settings in `settings/env.defaults` as a reference. ### New location for data files External data files for Wikipedia importance, postcodes etc. are no longer expected to reside in the source tree by default. Instead they will be searched in the project directory. If you have an automated setup script you must either adapt the download location or explicitly set the location of the files to the old place in your `.env`. ### Introducing `nominatim` command line tool The various php utilities have been replaced with a single `nominatim` command line tool. Make sure to adapt any scripts. There is no direct 1:1 matching between the old utilities and the commands of nominatim CLI. The following list gives you a list of nominatim sub-commands that contain functionality of each script: * ./utils/setup.php: `import`, `freeze`, `refresh` * ./utils/update.php: `replication`, `add-data`, `index`, `refresh` * ./utils/specialphrases.php: `special-phrases` * ./utils/check_import_finished.php: `admin` * ./utils/warm.php: `admin` * ./utils/export.php: `export` Try `nominatim --help` for more information about each subcommand. `./utils/query.php` no longer exists in its old form. `nominatim search` provides a replacement but returns different output. ### Switch to normalized house numbers The housenumber column in the placex table uses now normalized version. The automatic migration step will convert the column but this may take a very long time. It is advisable to take the machine offline while doing that. ## 3.5.0 -> 3.6.0 ### Change of layout of search_name_* tables The table need a different index for nearest place lookup. Recreate the indexes using the following shell script: ```bash for table in `psql -d nominatim -c "SELECT tablename FROM pg_tables WHERE tablename LIKE 'search_name_%'" -tA | grep -v search_name_blank`; do psql -d nominatim -c "DROP INDEX idx_${table}_centroid_place; CREATE INDEX idx_${table}_centroid_place ON ${table} USING gist (centroid) WHERE ((address_rank >= 2) AND (address_rank <= 25)); DROP INDEX idx_${table}_centroid_street; CREATE INDEX idx_${table}_centroid_street ON ${table} USING gist (centroid) WHERE ((address_rank >= 26) AND (address_rank <= 27))"; done ``` ### Removal of html output The debugging UI is no longer directly provided with Nominatim. Instead we now provide a simple Javascript application. Please refer to [Setting up the Nominatim UI](Setup-Nominatim-UI.md) for details on how to set up the UI. The icons served together with the API responses have been moved to the nominatim-ui project as well. If you want to keep the `icon` field in the response, you need to set `CONST_MapIcon_URL` to the URL of the `/mapicon` directory of nominatim-ui. ### Change order during indexing When reindexing places during updates, there is now a different order used which needs a different database index. Create it with the following SQL command: ```sql CREATE INDEX idx_placex_pendingsector_rank_address ON placex USING BTREE (rank_address, geometry_sector) WHERE indexed_status > 0; ``` You can then drop the old index with: ```sql DROP INDEX idx_placex_pendingsector; ``` ### Unused index This index has been unused ever since the query using it was changed two years ago. Saves about 12GB on a planet installation. ```sql DROP INDEX idx_placex_geometry_reverse_lookupPoint; ``` ### Switching to dotenv As part of the work changing the configuration format, the configuration for the website is now using a separate configuration file. To create the configuration file, run the following command after updating: ```sh ./utils/setup.php --setup-website ``` ### Update SQL code To update the SQL code to the leatest version run: ``` ./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions ``` ## 3.4.0 -> 3.5.0 ### New Wikipedia/Wikidata importance tables The `wikipedia_*` tables have a new format that also includes references to Wikidata. You need to update the computation functions and the tables as follows: * download the new Wikipedia tables as described in the import section * reimport the tables: `./utils/setup.php --import-wikipedia-articles` * update the functions: `./utils/setup.php --create-functions --enable-diff-updates` * create a new lookup index: ```sql CREATE INDEX idx_placex_wikidata ON placex USING BTREE ((extratags -> 'wikidata')) WHERE extratags ? 'wikidata' AND class = 'place' AND osm_type = 'N' AND rank_search < 26; ``` * compute importance: `./utils/update.php --recompute-importance` The last step takes about 10 hours on the full planet. Remove one function (it will be recreated in the next step): ```sql DROP FUNCTION create_country(hstore,character varying); ``` Finally, update all SQL functions: ```sh ./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions ``` ## 3.3.0 -> 3.4.0 ### Reorganisation of location_area_country table The table `location_area_country` has been optimized. You need to switch to the new format when you run updates. While updates are disabled, run the following SQL commands: ```sql CREATE TABLE location_area_country_new AS SELECT place_id, country_code, geometry FROM location_area_country; DROP TABLE location_area_country; ALTER TABLE location_area_country_new RENAME TO location_area_country; CREATE INDEX idx_location_area_country_geometry ON location_area_country USING GIST (geometry); CREATE INDEX idx_location_area_country_place_id ON location_area_country USING BTREE (place_id); ``` Finally, update all SQL functions: ```sh ./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions ``` ## 3.2.0 -> 3.3.0 ### New database connection string (DSN) format Previously database connection setting (`CONST_Database_DSN` in `settings/*.php`) had the format * (simple) `pgsql://@/nominatim` * (complex) `pgsql://johndoe:secret@machine1.domain.com:1234/db1` The new format is * (simple) `pgsql:dbname=nominatim` * (complex) `pgsql:dbname=db1;host=machine1.domain.com;port=1234;user=johndoe;password=secret` ### Natural Earth country boundaries no longer needed as fallback ```sql DROP TABLE country_naturalearthdata; ``` Finally, update all SQL functions: ```sh ./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions ``` ### Configurable Address Levels The new configurable address levels require a new table. Create it with the following command: ```sh ./utils/update.php --update-address-levels ``` ## 3.1.0 -> 3.2.0 ### New reverse algorithm The reverse algorithm has changed and requires new indexes. Run the following SQL statements to create the indexes: ```sql CREATE INDEX idx_placex_geometry_reverse_lookupPoint ON placex USING gist (geometry) WHERE (name IS NOT null or housenumber IS NOT null or rank_address BETWEEN 26 AND 27) AND class NOT IN ('railway','tunnel','bridge','man_made') AND rank_address >= 26 AND indexed_status = 0 AND linked_place_id IS null; CREATE INDEX idx_placex_geometry_reverse_lookupPolygon ON placex USING gist (geometry) WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon') AND rank_address between 4 and 25 AND type != 'postcode' AND name is not null AND indexed_status = 0 AND linked_place_id is null; CREATE INDEX idx_placex_geometry_reverse_placeNode ON placex USING gist (geometry) WHERE osm_type = 'N' AND rank_search between 5 and 25 AND class = 'place' AND type != 'postcode' AND name is not null AND indexed_status = 0 AND linked_place_id is null; ``` You also need to grant the website user access to the `country_osm_grid` table: ```sql GRANT SELECT ON table country_osm_grid to "www-user"; ``` Replace the `www-user` with the user name of your website server if necessary. You can now drop the unused indexes: ```sql DROP INDEX idx_placex_reverse_geometry; ``` Finally, update all SQL functions: ```sh ./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions ``` ## 3.0.0 -> 3.1.0 ### Postcode Table A new separate table for artificially computed postcode centroids was introduced. Migration to the new format is possible but **not recommended**. Create postcode table and indexes, running the following SQL statements: ```sql CREATE TABLE location_postcode (place_id BIGINT, parent_place_id BIGINT, rank_search SMALLINT, rank_address SMALLINT, indexed_status SMALLINT, indexed_date TIMESTAMP, country_code varchar(2), postcode TEXT, geometry GEOMETRY(Geometry, 4326)); CREATE INDEX idx_postcode_geometry ON location_postcode USING GIST (geometry); CREATE UNIQUE INDEX idx_postcode_id ON location_postcode USING BTREE (place_id); CREATE INDEX idx_postcode_postcode ON location_postcode USING BTREE (postcode); GRANT SELECT ON location_postcode TO "www-data"; DROP TYPE IF EXISTS nearfeaturecentr CASCADE; CREATE TYPE nearfeaturecentr AS ( place_id BIGINT, keywords int[], rank_address smallint, rank_search smallint, distance float, isguess boolean, postcode TEXT, centroid GEOMETRY ); ``` Add postcode column to `location_area` tables with SQL statement: ```sql ALTER TABLE location_area ADD COLUMN postcode TEXT; ``` Then reimport the functions: ```sh ./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions ``` Create appropriate triggers with SQL: ```sql CREATE TRIGGER location_postcode_before_update BEFORE UPDATE ON location_postcode FOR EACH ROW EXECUTE PROCEDURE postcode_update(); ``` Finally populate the postcode table (will take a while): ```sh ./utils/setup.php --calculate-postcodes --index --index-noanalyse ``` This will create a working database. You may also delete the old artificial postcodes now. Note that this may be expensive and is not absolutely necessary. The following SQL statement will remove them: ```sql DELETE FROM place_addressline a USING placex p WHERE a.address_place_id = p.place_id and p.osm_type = 'P'; ALTER TABLE placex DISABLE TRIGGER USER; DELETE FROM placex WHERE osm_type = 'P'; ALTER TABLE placex ENABLE TRIGGER USER; ``` ================================================ FILE: docs/admin/Setup-Nominatim-UI.md ================================================ # Setting up the Nominatim UI Nominatim is a search API, it does not provide a website interface on its own. [nominatim-ui](https://github.com/osm-search/nominatim-ui) offers a small website for testing your setup and inspecting the database content. This section provides a quick start how to use nominatim-ui with your installation. For more details, please also have a look at the [README of nominatim-ui](https://github.com/osm-search/nominatim-ui/blob/master/README.md). ## Installing nominatim-ui We provide regular releases of nominatim-ui that contain the packaged website. They do not need any special installation. Just download, configure and run it. Grab the latest release from [nominatim-ui's Github release page](https://github.com/osm-search/nominatim-ui/releases) and unpack it. You can use `nominatim-ui-x.x.x.tar.gz` or `nominatim-ui-x.x.x.zip`. Next you need to adapt the UI to your installation. Custom settings need to be put into `dist/theme/config.theme.js`. At a minimum you need to set `Nominatim_API_Endpoint` to point to your Nominatim installation: cd nominatim-ui echo "Nominatim_Config.Nominatim_API_Endpoint='https://myserver.org/nominatim/';" > dist/theme/config.theme.js For the full set of available settings, have a look at `dist/config.defaults.js`. Then you can just test it locally by spinning up a webserver in the `dist` directory. For example, with Python: cd nominatim-ui/dist python3 -m http.server 8765 The website is now available at `http://localhost:8765`. ## Forwarding searches to nominatim-ui Nominatim used to provide the search interface directly by itself when `format=html` was requested. For the `/search` endpoint this even used to be the default. The following section describes how to set up Apache or nginx, so that your users are forwarded to nominatim-ui when they go to a URL that formerly presented the UI. ### Setting up forwarding in Nginx First of all make nominatim-ui available under `/ui` on your webserver: ``` nginx server { # Here is the Nominatim setup as described in the Installation section location /ui/ { alias /dist/; index index.html; } } ``` Now we need to find out if a URL should be forwarded to the UI. Add the following `map` commands *outside* the server section: ``` nginx # Inspect the format parameter in the query arguments. We are interested # if it is set to html or something else or if it is missing completely. map $args $format { default default; ~(^|&)format=html(&|$) html; ~(^|&)format= other; } # Determine from the URI and the format parameter above if forwarding is needed. map $uri/$format $forward_to_ui { default 0; # no forwarding by default ~/search.*/default 1; # Use this line only, if search should go to UI by default. ~/reverse.*/html 1; # Forward API calls that UI supports, when ~/status.*/html 1; # format=html is explicitly requested. ~/search.*/html 1; ~/details.*/html 1; } ``` The `$forward_to_ui` parameter can now be used to conditionally forward the calls: ``` nginx location / { if ($forward_to_ui) { rewrite ^(/[^/.]*) https://$http_host/ui$1.html redirect; } # proxy_pass commands } ``` Reload nginx and the UI should be available. ### Setting up forwarding in Apache First of all make nominatim-ui available in the `ui/` subdirectory where Nominatim is installed. For example, given you have set up an alias under `nominatim` like this: ``` apache Alias /nominatim /home/vagrant/build/website ``` you need to insert the following rules for nominatim-ui before that alias: ``` DirectoryIndex search.html Require all granted Alias /nominatim/ui /home/vagrant/nominatim-ui/dist ``` Replace `/home/vagrant/nominatim-ui` with the directory where you have cloned nominatim-ui. !!! important The alias for nominatim-ui must come before the alias for the Nominatim website directory. To set up forwarding, the Apache rewrite module is needed. Enable it with: ``` sh sudo a2enmod rewrite ``` Then add rewrite rules to the `Directory` directive of the Nominatim website directory like this: ``` apache Options FollowSymLinks MultiViews AddType text/html .php Require all granted RewriteEngine On # This must correspond to the URL where nominatim can be found. RewriteBase "/nominatim/" # If no endpoint is given, then use search. RewriteRule ^(/|$) "search" # If format-html is explicitly requested, forward to the UI. RewriteCond %{QUERY_STRING} "format=html" RewriteRule ^([^/.]+) ui/$1.html [R,END] # Optionally: if no format parameter is there then forward /search. RewriteCond %{QUERY_STRING} "!format=" RewriteCond %{REQUEST_URI} "/search" RewriteRule ^([^/.]+) ui/$1.html [R,END] ``` Restart Apache and the UI should be available. ================================================ FILE: docs/admin/Update.md ================================================ # Updating the Database There are many different ways to update your Nominatim database. The following section describes how to keep it up-to-date using an [online replication service for OpenStreetMap data](https://wiki.openstreetmap.org/wiki/Planet.osm/diffs) For a list of other methods to add or update data see the output of `nominatim add-data --help`. !!! important If you have configured a flatnode file for the import, then you need to keep this flatnode file around for updates. ### Installing the newest version of Pyosmium The replication process uses [Pyosmium](https://docs.osmcode.org/pyosmium/latest/updating_osm_data.html) to download update data from the server. It is recommended to install Pyosmium via pip. Run (as the same user who will later run the updates): ```sh pip3 install --user osmium ``` ### Setting up the update process Next the update process needs to be initialised. By default Nominatim is configured to update using the global minutely diffs. If you want a different update source you will need to add some settings to `.env`. For example, to use the daily country extracts diffs for Ireland from Geofabrik add the following: # base URL of the replication service NOMINATIM_REPLICATION_URL="https://download.geofabrik.de/europe/ireland-and-northern-ireland-updates" # How often upstream publishes diffs (in seconds) NOMINATIM_REPLICATION_UPDATE_INTERVAL=86400 # How long to sleep if no update found yet (in seconds) NOMINATIM_REPLICATION_RECHECK_INTERVAL=900 To set up the update process now run the following command: nominatim replication --init It outputs the date where updates will start. Recheck that this date is what you expect. The `replication --init` command needs to be rerun whenever the replication service is changed. ### Updating Nominatim Nominatim supports different modes how to retrieve the update data from the server. Which one you want to use depends on your exact setup and how often you want to retrieve updates. These instructions are for using a single source of updates. If you have imported multiple country extracts and want to keep them up-to-date, [Advanced installations section](Advanced-Installations.md) contains instructions to set up and update multiple country extracts. #### One-time mode When the `--once` parameter is given, then Nominatim will download exactly one batch of updates and then exit. This one-time mode still respects the `NOMINATIM_REPLICATION_UPDATE_INTERVAL` that you have set. If according to the update interval no new data has been published yet, it will go to sleep until the next expected update and only then attempt to download the next batch. The one-time mode is particularly useful if you want to run updates continuously but need to schedule other work in between updates. For example, you might want to regularly recompute postcodes -- a process that must not be run while updates are in progress. An update script refreshing postcodes regularly might look like this: ```sh #!/bin/bash # Switch to your project directory. cd /srv/nominatim while true; do nominatim replication --once if [ -f "/srv/nominatim/schedule-maintenance" ]; then rm /srv/nominatim/schedule-maintenance nominatim refresh --postcodes fi done ``` A cron job then creates the file `/srv/nominatim/schedule-maintenance` once per night. ##### One-time mode with systemd You can run the one-time mode with a systemd timer & service. Create a timer description like `/etc/systemd/system/nominatim-updates.timer`: ``` [Unit] Description=Timer to start updates of Nominatim [Timer] OnActiveSec=2 OnUnitActiveSec=1min Unit=nominatim-updates.service [Install] WantedBy=multi-user.target ``` `OnUnitActiveSec` defines how often the individual update command is run. Then add a service definition for the timer in `/etc/systemd/system/nominatim-updates.service`: ``` [Unit] Description=Single updates of Nominatim [Service] WorkingDirectory=/srv/nominatim-project ExecStart=/srv/nominatim-venv/bin/nominatim replication --once StandardOutput=journald StandardError=inherit User=nominatim Group=nominatim Type=simple [Install] WantedBy=multi-user.target ``` Replace the `WorkingDirectory` with your project directory. `ExecStart` points to the nominatim binary that was installed in your virtualenv earlier. Finally, you might need to adapt user and group names as required. Now activate the service and start the updates: ``` sudo systemctl daemon-reload sudo systemctl enable nominatim-updates.timer sudo systemctl start nominatim-updates.timer ``` You can stop future data updates while allowing any current, in-progress update steps to finish, by running `sudo systemctl stop nominatim-updates.timer` and waiting until `nominatim-updates.service` isn't running (`sudo systemctl is-active nominatim-updates.service`). To check the output from the update process, use journalctl: `journalctl -u nominatim-updates.service` #### Catch-up mode With the `--catch-up` parameter, Nominatim will immediately try to download all changes from the server until the database is up-to-date. The catch-up mode still respects the parameter `NOMINATIM_REPLICATION_MAX_DIFF`. It downloads and applies the changes in appropriate batches until all is done. The catch-up mode is foremost useful to bring the database up to date after the initial import. Give that the service usually is not in production at this point, you can temporarily be a bit more generous with the batch size and number of threads you use for the updates by running catch-up like this: ``` cd /srv/nominatim-project NOMINATIM_REPLICATION_MAX_DIFF=5000 nominatim replication --catch-up --threads 15 ``` The catch-up mode is also useful when you want to apply updates at a lower frequency than what the source publishes. You can set up a cron job to run replication catch-up at whatever interval you desire. !!! hint When running scheduled updates with catch-up, it is a good idea to choose a replication source with an update frequency that is an order of magnitude lower. For example, if you want to update once a day, use an hourly updated source. This ensures that you don't miss an entire day of updates when the source is unexpectedly late to publish its update. If you want to use the source with the same update frequency (e.g. a daily updated source with daily updates), use the once mode together with a frequently run systemd script as described above. It ensures to re-request the newest update until they have been published. #### Continuous updates !!! danger This mode is no longer recommended to use and will removed in future releases. systemd is much better suited for running regular updates. Please refer to the setup instructions for running one-time mode with systemd above. This is the easiest mode. Simply run the replication command without any parameters: nominatim replication The update application keeps running forever and retrieves and applies new updates from the server as they are published. ================================================ FILE: docs/api/Details.md ================================================ # Place details Show all details about a single place saved in the database. This API endpoint is meant for visual inspection of the data in the database, mainly together with [Nominatim-UI](https://github.com/osm-search/nominatim-ui/). The parameters of the endpoint and the output may change occasionally between versions of Nominatim. Do not rely on the output in scripts or applications. !!! warning The details endpoint at https://nominatim.openstreetmap.org may not used in scripts or bots at all. See [Nominatim Usage Policy](https://operations.osmfoundation.org/policies/nominatim/). The details API supports the following two request formats: ``` xml https://nominatim.openstreetmap.org/details?osmtype=[N|W|R]&osmid=&class= ``` `osmtype` and `osmid` are required parameters. The type is one of node (N), way (W) or relation (R). The id must be a number. The `class` parameter is optional and allows to distinguish between entries, when the corresponding OSM object has more than one main tag. For example, when a place is tagged with `tourism=hotel` and `amenity=restaurant`, there will be two place entries in Nominatim, one for a restaurant, one for a hotel. You need to specify `class=tourism` or `class=amentity` to get exactly the one you want. If there are multiple places in the database but the `class` parameter is left out, then one of the places will be chosen at random and displayed. ``` xml https://nominatim.openstreetmap.org/details?place_id= ``` Place IDs are assigned sequentially during Nominatim data import. The ID for a place is different between Nominatim installation (servers) and changes when data gets reimported. Therefore it cannot be used as a permanent id and shouldn't be used in bug reports. !!! danger "Deprecation warning" The API can also be used with the URL `https://nominatim.openstreetmap.org/details.php`. This is now deprecated and will be removed in future versions. ## Parameters This section lists additional optional parameters. ### Output format | Parameter | Value | Default | |-----------| ----- | ------- | | json_callback | function name | _unset_ | When set, then JSON output will be wrapped in a callback function with the given name. See [JSONP](https://en.wikipedia.org/wiki/JSONP) for more information. ### Output details | Parameter | Value | Default | |-----------| ----- | ------- | | addressdetails | 0 or 1 | 0 | When set to 1, include a breakdown of the address into elements. | Parameter | Value | Default | |-----------| ----- | ------- | | keywords | 0 or 1 | 0 | When set to 1, include a list of name keywords and address keywords in the result. | Parameter | Value | Default | |-----------| ----- | ------- | | linkedplaces | 0 or 1 | 1 | Include details of places that are linked with this one. Places get linked together when they are different forms of the same physical object. Nominatim links two kinds of objects together: place nodes get linked with the corresponding administrative boundaries. Waterway relations get linked together with their members. | Parameter | Value | Default | |-----------| ----- | ------- | | hierarchy | 0 or 1 | 0 | Include details of POIs and address that depend on the place. Only POIs that use this place to determine their address will be returned. | Parameter | Value | Default | |-----------| ----- | ------- | | group_hierarchy | 0 or 1 | 0 | When set to 1, the output of the address hierarchy will be grouped by type. | Parameter | Value | Default | |-----------| ----- | ------- | | polygon_geojson | 0 or 1 | 0 | Include geometry of result. | Parameter | Value | Default | |-----------| ----- | ------- | | entrances | 0 or 1 | 0 | When set to 1, include the tagged entrances in the result. ### Language of results | Parameter | Value | Default | |-----------| ----- | ------- | | accept-language | browser language string | content of "Accept-Language" HTTP header | Preferred language order for showing search results. This may either be a simple comma-separated list of language codes or have the same format as the ["Accept-Language" HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language). ## Examples ##### JSON [https://nominatim.openstreetmap.org/details?osmtype=W&osmid=38210407&format=json](https://nominatim.openstreetmap.org/details?osmtype=W&osmid=38210407&format=json) ```json { "place_id": 85993608, "parent_place_id": 72765313, "osm_type": "W", "osm_id": 38210407, "category": "place", "type": "square", "admin_level": "15", "localname": "Pariser Platz", "names": { "name": "Pariser Platz", "name:be": "Парыжская плошча", "name:de": "Pariser Platz", "name:es": "Plaza de París", "name:he": "פאריזר פלאץ", "name:ko": "파리저 광장", "name:la": "Forum Parisinum", "name:ru": "Парижская площадь", "name:uk": "Паризька площа", "name:zh": "巴黎廣場" }, "addresstags": { "postcode": "10117" }, "housenumber": null, "calculated_postcode": "10117", "country_code": "de", "indexed_date": "2018-08-18T17:02:45+00:00", "importance": 0.339401620591472, "calculated_importance": 0.339401620591472, "extratags": { "wikidata": "Q156716", "wikipedia": "de:Pariser Platz" }, "calculated_wikipedia": "de:Pariser_Platz", "rank_address": 30, "rank_search": 30, "isarea": true, "centroid": { "type": "Point", "coordinates": [ 13.3786822618517, 52.5163654 ] }, "geometry": { "type": "Point", "coordinates": [ 13.3786822618517, 52.5163654 ] } } ``` ================================================ FILE: docs/api/Faq.md ================================================ # Frequently Asked Questions ## API Results #### 1. The address of my search results contains far-away places that don't belong there. Nominatim computes the address from two sources in the OpenStreetMap data: from administrative boundaries and from place nodes. Boundaries are the more useful source. They precisely describe an area. So it is very clear for Nominatim if a point belongs to an area or not. Place nodes are more complicated. These are only points without any precise extent. So Nominatim has to take a guess and assume that an address belongs to the closest place node it can find. In an ideal world, Nominatim would not need the place nodes but there are many places on earth where there are no precise boundaries available for all parts that make up an address. This is in particular true for the more local address parts, like villages and suburbs. Therefore it is not possible to completely dismiss place nodes. And sometimes they sneak in where they don't belong. As a OpenStreetMap mapper, you can improve the situation in two ways: if you see a place node for which already an administrative area exists, then you should _link_ the two by adding the node with a 'label' role to the boundary relation. If there is no administrative area, you can add the approximate extent of the place and tag it place= as well. #### 2. When doing reverse search, the address details have parts that don't contain the point I was looking up. There is a common misconception how the reverse API call works in Nominatim. Reverse does not give you the address of the point you asked for. Reverse returns the closest object to the point you asked for and then returns the address of that object. Now, if you are close to a border, then the closest object may be across that border. When Nominatim then returns the address, it contains the county/state/country across the border. #### 3. I get different counties/states/countries when I change the zoom parameter in the reverse query. How is that possible? This is basically the same problem as in the previous answer. The zoom level influences at which [search rank](../customize/Ranking.md#search-rank) Nominatim starts looking for the closest object. So the closest house number maybe on one side of the border while the closest street is on the other. As the address details contain the address of the closest object found, you might sometimes get one result, sometimes the other for the closest point. #### 4. Can you return the continent? Nominatim assigns each map feature one country. Those outside any administrative boundaries are assigned a special no-country. Continents or other super-national administrations (e.g. European Union, NATO, Custom unions) are not supported, see also [Administrative Boundary](https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative#Super-national_administrations). #### 5. Can you return the timezone? See this separate OpenStreetMap-based project [Timezone Boundary Builder](https://github.com/evansiroky/timezone-boundary-builder). #### 6. I want to download a list of streets/restaurants of a city/region The [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API) is more suited for these kinds of queries. That said if you installed your own Nominatim instance you can use the `nominatim export` PHP script as basis to return such lists. #### 7. My result has a wrong postcode. Where does it come from? Most places in OSM don't have a postcode, so Nominatim tries to interpolate one. It first look at all the places that make up the address of the place. If one of them has a postcode defined, this is the one to be used. When none of the address parts has a postcode either, Nominatim interpolates one from the surrounding objects. If the postcode is for your result is one, then most of the time there is an OSM object with the wrong postcode nearby. To find the bad postcode, go to [https://nominatim.openstreetmap.org](https://nominatim.openstreetmap.org) and search for your place. When you have found it, click on the 'details' link under the result to go to the details page. There is a field 'Computed Postcode' which should display the bad postcode. Click on the 'how?' link. A small explanation text appears. It contains a link to a query for Overpass Turbo. Click on that and you get a map with all places in the area that have the bad postcode. If none is displayed, zoom the map out a bit and then click on 'Run'. Now go to [OpenStreetMap](https://openstreetmap.org) and fix the error you have just found. It will take at least a day for Nominatim to catch up with your data fix. Sometimes longer, depending on how much editing activity is in the area. ================================================ FILE: docs/api/Lookup.md ================================================ # Address lookup The lookup API allows to query the address and other details of one or multiple OSM objects like node, way or relation. ## Endpoint The lookup API has the following format: ``` https://nominatim.openstreetmap.org/lookup?osm_ids=[N|W|R],…,…,& ``` `osm_ids` is mandatory and must contain a comma-separated list of OSM ids each prefixed with its type, one of node(N), way(W) or relation(R). Up to 50 ids can be queried at the same time. !!! danger "Deprecation warning" The API can also be used with the URL `https://nominatim.openstreetmap.org/lookup.php`. This is now deprecated and will be removed in future versions. ## Parameters This section lists additional optional parameters. ### Output format | Parameter | Value | Default | |-----------| ----- | ------- | | format | one of: `xml`, `json`, `jsonv2`, `geojson`, `geocodejson` | `jsonv2` | See [Place Output Formats](Output.md) for details on each format. | Parameter | Value | Default | |-----------| ----- | ------- | | json_callback | function name | _unset_ | When given, then JSON output will be wrapped in a callback function with the given name. See [JSONP](https://en.wikipedia.org/wiki/JSONP) for more information. Only has an effect for JSON output formats. ### Output details | Parameter | Value | Default | |-----------| ----- | ------- | | addressdetails | 0 or 1 | 1 | When set to 1, include a breakdown of the address into elements. The exact content of the address breakdown depends on the output format. !!! tip If you are interested in a stable classification of address categories (suburb, city, state, etc), have a look at the `geocodejson` format. All other formats return classifications according to OSM tagging. There is a much larger set of categories and they are not always consistent, which makes them very hard to work with. | Parameter | Value | Default | |-----------| ----- | ------- | | extratags | 0 or 1 | 0 | When set to 1, the response include any additional information in the result that is available in the database, e.g. wikipedia link, opening hours. | Parameter | Value | Default | |-----------| ----- | ------- | | namedetails | 0 or 1 | 0 | When set to 1, include a full list of names for the result. These may include language variants, older names, references and brand. | Parameter | Value | Default | |-----------| ----- | ------- | | entrances | 0 or 1 | 0 | When set to 1, include the tagged entrances in the result. ### Language of results | Parameter | Value | Default | |-----------| ----- | ------- | | accept-language | browser language string | content of "Accept-Language" HTTP header | Preferred language order for showing search results. This may either be a simple comma-separated list of language codes or have the same format as the ["Accept-Language" HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language). !!! tip First-time users of Nominatim tend to be confused that they get different results when using Nominatim in the browser versus in a command-line tool like wget or curl. The command-line tools usually don't send any Accept-Language header, prompting Nominatim to show results in the local language. Browsers on the contrary always send the currently chosen browser language. ### Polygon output | Parameter | Value | Default | |-----------| ----- | ------- | | polygon_geojson | 0 or 1 | 0 | | polygon_kml | 0 or 1 | 0 | | polygon_svg | 0 or 1 | 0 | | polygon_text | 0 or 1 | 0 | Add the full geometry of the place to the result output. Output formats in GeoJSON, KML, SVG or WKT are supported. Only one of these options can be used at a time. | Parameter | Value | Default | |-----------| ----- | ------- | | polygon_threshold | floating-point number | 0.0 | When one of the polygon_* outputs is chosen, return a simplified version of the output geometry. The parameter describes the tolerance in degrees with which the geometry may differ from the original geometry. Topology is preserved in the geometry. ### Other | Parameter | Value | Default | |-----------| ----- | ------- | | email | valid email address | _unset_ | If you are making large numbers of request please include an appropriate email address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details. | Parameter | Value | Default | |-----------| ----- | ------- | | debug | 0 or 1 | 0 | Output assorted developer debug information. Data on internals of Nominatim's "search loop" logic, and SQL queries. The output is HTML format. This overrides the specified machine readable format. ## Examples ##### XML [https://nominatim.openstreetmap.org/lookup?osm_ids=R146656,W104393803,N240109189](https://nominatim.openstreetmap.org/lookup?osm_ids=R146656,W50637691,N240109189) ```xml Manchester Greater Manchester North West England England United Kingdom gb Brandenburger Tor Brandenburger Straße Historische Innenstadt Potsdam Brandenburg 14467 Germany de Berlin Berlin 10178 Germany de ``` ##### JSON with extratags [https://nominatim.openstreetmap.org/lookup?osm_ids=W50637691&format=json&extratags=1](https://nominatim.openstreetmap.org/lookup?osm_ids=W50637691&format=json&extratags=1) ```json [ { "place_id": 115462561, "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "osm_type": "way", "osm_id": 50637691, "boundingbox": [ "52.3994612", "52.3996426", "13.0479574", "13.0481754" ], "lat": "52.399550700000006", "lon": "13.048066846939687", "display_name": "Brandenburger Tor, Brandenburger Straße, Historische Innenstadt, Innenstadt, Potsdam, Brandenburg, 14467, Germany", "class": "tourism", "type": "attraction", "importance": 0.2940287400552381, "address": { "tourism": "Brandenburger Tor", "road": "Brandenburger Straße", "suburb": "Historische Innenstadt", "city": "Potsdam", "state": "Brandenburg", "postcode": "14467", "country": "Germany", "country_code": "de" }, "extratags": { "image": "http://commons.wikimedia.org/wiki/File:Potsdam_brandenburger_tor.jpg", "heritage": "4", "wikidata": "Q695045", "architect": "Carl von Gontard;Georg Christian Unger", "wikipedia": "de:Brandenburger Tor (Potsdam)", "wheelchair": "yes", "description": "Kleines Brandenburger Tor in Potsdam", "heritage:website": "http://www.bldam-brandenburg.de/images/stories/PDF/DML%202012/04-p-internet-13.pdf", "heritage:operator": "bldam", "architect:wikidata": "Q68768;Q95223", "year_of_construction": "1771" } } ] ``` ================================================ FILE: docs/api/Output.md ================================================ # Place Output The [/reverse](Reverse.md), [/search](Search.md) and [/lookup](Lookup.md) API calls produce very similar output which is explained in this section. There is one section for each format. The format correspond to what was selected via the `format` parameter. ## JSON The JSON format returns an array of places (for search and lookup) or a single place (for reverse) of the following format: ``` { "place_id": 100149, "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "osm_type": "node", "osm_id": "107775", "boundingbox": ["51.3473219", "51.6673219", "-0.2876474", "0.0323526"], "lat": "51.5073219", "lon": "-0.1276474", "display_name": "London, Greater London, England, SW1A 2DU, United Kingdom", "class": "place", "type": "city", "importance": 0.9654895765402, "icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png", "address": { "city": "London", "state_district": "Greater London", "state": "England", "ISO3166-2-lvl4": "GB-ENG", "postcode": "SW1A 2DU", "country": "United Kingdom", "country_code": "gb" }, "extratags": { "capital": "yes", "website": "http://www.london.gov.uk", "wikidata": "Q84", "wikipedia": "en:London", "population": "8416535" } } ``` The possible fields are: * `place_id` - reference to the Nominatim internal database ID ([see notes](#place_id-is-not-a-persistent-id)) * `osm_type`, `osm_id` - reference to the OSM object ([see notes](#osm-reference)) * `boundingbox` - area of corner coordinates ([see notes](#boundingbox)) * `lat`, `lon` - latitude and longitude of the centroid of the object * `display_name` - full comma-separated address * `class`, `type` - key and value of the main OSM tag * `importance` - computed importance rank * `icon` - link to class icon (if available) * `address` - dictionary of address details (only with `addressdetails=1`, [see notes](#addressdetails)) * `extratags` - dictionary with additional useful tags like website or maxspeed (only with `extratags=1`) * `namedetails` - dictionary with full list of available names including ref etc. * `geojson`, `svg`, `geotext`, `geokml` - full geometry (only with the appropriate `polygon_*` parameter) * `entrances` - array of objects representing tagged entrances for the object, or null if none are found (only with `entrances=1`) ## JSONv2 This is the same as the JSON format with two changes: * `class` renamed to `category` * additional field `place_rank` with the search rank of the object ## GeoJSON This format follows the [RFC7946](https://geojson.org). Every feature includes a bounding box (`bbox`). The properties object has the following fields: * `place_id` - reference to the Nominatim internal database ID ([see notes](#place_id-is-not-a-persistent-id)) * `osm_type`, `osm_id` - reference to the OSM object ([see notes](#osm-reference)) * `category`, `type` - key and value of the main OSM tag * `display_name` - full comma-separated address * `place_rank` - class search rank * `importance` - computed importance rank * `icon` - link to class icon (if available) * `address` - dictionary of address details (only with `addressdetails=1`, [see notes](#addressdetails)) * `extratags` - dictionary with additional useful tags like `website` or `maxspeed` (only with `extratags=1`) * `namedetails` - dictionary with full list of available names including ref etc. * `entrances` - array of objects representing tagged entrances for the object, or null if none are found (only with `entrances=1`) Use `polygon_geojson` to output the full geometry of the object instead of the centroid. ## GeocodeJSON The GeocodeJSON format follows the [GeocodeJSON spec 0.1.0](https://github.com/geocoders/geocodejson-spec). The following feature attributes are implemented: * `osm_type`, `osm_id` - reference to the OSM object (unofficial extension, [see notes](#osm-reference)) * `type` - the 'address level' of the object ('house', 'street', `district`, `city`, `county`, `state`, `country`, `locality`) * `osm_key`- key of the main tag of the OSM object (e.g. boundary, highway, amenity) * `osm_value` - value of the main tag of the OSM object (e.g. residential, restaurant) * `label` - full comma-separated address * `name` - localised name of the place * `housenumber`, `street`, `locality`, `district`, `postcode`, `city`, `county`, `state`, `country` - provided when it can be determined from the address (only with `addressdetails=1`) * `admin` - list of localised names of administrative boundaries (only with `addressdetails=1`) * `extra` - dictionary with additional useful tags like `website` or `maxspeed` (only with `extratags=1`) * `entrances` - array of objects representing tagged entrances for the object, or null if none are found (only with `entrances=1`) Use `polygon_geojson` to output the full geometry of the object instead of the centroid. ## XML The XML response returns one or more place objects in slightly different formats depending on the API call. ### Reverse ``` Bavaria, Germany Bavaria DE-BY Germany de ``` The attributes of the outer `reversegeocode` element return generic information about the query, including the time when the response was sent (in UTC), attribution to OSM and the original querystring. The place information can be found in the `result` element. The attributes of that element contain: * `place_id` - reference to the Nominatim internal database ID ([see notes](#place_id-is-not-a-persistent-id)) * `osm_type`, `osm_id` - reference to the OSM object ([see notes](#osm-reference)) * `ref` - content of `ref` tag if it exists * `lat`, `lon` - latitude and longitude of the centroid of the object * `boundingbox` - comma-separated list of corner coordinates ([see notes](#boundingbox)) The full address of the result can be found in the content of the `result` element as a comma-separated list. Additional information requested with `addressdetails=1`, `extratags=1`, `namedetails=1`, and `entrances=1` can be found in extra elements. ### Search and Lookup ``` London Greater London England GB-ENG SW1A 2DU United Kingdom gb ``` The attributes of the outer `searchresults` or `lookupresults` element return generic information about the query: * `timestamp` - UTC time when the response was sent * `attribution` - OSM licensing information * `querystring` - original query * `polygon` - true when extra geometry information was requested * `exclude_place_ids` - IDs of places that should be ignored in a follow-up request (OSM IDs where possible, otherwise `place_id`s) * `more_url` - search call that will yield additional results for the query just sent The place information can be found in the `place` elements, of which there may be more than one. The attributes of that element contain: * `place_id` - reference to the Nominatim internal database ID ([see notes](#place_id-is-not-a-persistent-id)) * `osm_type`, `osm_id` - reference to the OSM object ([see notes](#osm-reference)) * `ref` - content of `ref` tag if it exists * `lat`, `lon` - latitude and longitude of the centroid of the object * `boundingbox` - comma-separated list of corner coordinates ([see notes](#boundingbox)) * `place_rank` - class [search rank](../customize/Ranking.md#search-rank) * `address_rank` - place [address rank](../customize/Ranking.md#address-rank) * `display_name` - full comma-separated address * `class`, `type` - key and value of the main OSM tag * `importance` - computed importance rank * `icon` - link to class icon (if available) When `addressdetails=1` is requested, the localised address parts appear as subelements with the type of the address part. Additional information requested with `extratags=1`, `namedetails=1`, and `entrances=1` can be found in extra elements as sub-element of `extratags`, `namedetails`, and `entrances` respectively. ## Notes on field values ### place_id is not a persistent id The `place_id` is an internal identifier that is assigned data is imported into a Nominatim database. The same OSM object will have a different value on another server. It may even change its ID on the same server when it is removed and reimported while updating the database with fresh OSM data. It is thus not useful to treat it as permanent for later use. The combination `osm_type`+`osm_id` is slightly better but remember in OpenStreetMap mappers can delete, split, recreate places (and those get a new `osm_id`), there is no link between those old and new ids. Places can also change their meaning without changing their `osm_id`, e.g. when a restaurant is retagged as supermarket. For a more in-depth discussion see [Permanent ID](https://wiki.openstreetmap.org/wiki/Permanent_ID). If you need an ID that is consistent over multiple installations of Nominatim, then you should use the combination of `osm_type`+`osm_id`+`class`. ### OSM reference Nominatim may sometimes return special objects that do not correspond directly to an object in OpenStreetMap. These are: * **Postcodes**. Nominatim returns an postcode point created from all mapped postcodes of the same name. The class and type of these object is `place=postcdode`. No `osm_type` and `osm_id` are included in the result. * **Housenumber interpolations**. Nominatim returns a single interpolated housenumber from the interpolation way. The class and type are `place=house` and `osm_type` and `osm_id` correspond to the interpolation way in OSM. * **TIGER housenumber.** Nominatim returns a single interpolated housenumber from the TIGER data. The class and type are `place=house` and `osm_type` and `osm_id` correspond to the street mentioned in the result. Please note that the `osm_type` and `osm_id` returned may be changed in the future. You should not expect to only find `node`, `way` and `relation` for the type. ### boundingbox Comma separated list of min latitude, max latitude, min longitude, max longitude. The whole planet would be `-90,90,-180,180`. Can be used to pan and center the map on the result, for example with leafletjs mapping library `map.fitBounds([[bbox[0],bbox[2]],[bbox[1],bbox[3]]], {padding: [20, 20], maxzoom: 16});` Bounds crossing the antimeridian have a min latitude -180 and max latitude 180, essentially covering the entire planet (see [issue 184](https://github.com/openstreetmap/Nominatim/issues/184)). ### addressdetails Address details in the xml and json formats return a list of names together with a designation label. Per default the following labels may appear: * continent * country, country_code * region, state, state_district, county, ISO3166-2-lvl * municipality, city, town, village * city_district, district, borough, suburb, subdivision * hamlet, croft, isolated_dwelling * neighbourhood, allotments, quarter * city_block, residential, farm, farmyard, industrial, commercial, retail * road * house_number, house_name * emergency, historic, military, natural, landuse, place, railway, man_made, aerialway, boundary, amenity, aeroway, club, craft, leisure, office, mountain_pass, shop, tourism, bridge, tunnel, waterway * postcode They roughly correspond to the classification of the OpenStreetMap data according to either the `place` tag or the main key of the object. ### entrances Entrance details in the xml and json formats return the latitude and longitude of the entrance, the osm node ID, the [type of entrance](https://wiki.openstreetmap.org/wiki/Key:entrance), and any extra tags associated with the entrance node. * osm_id * type * lat * lon * extratags They roughly correspond to the classification of the OpenStreetMap data according to either the `place` tag or the main key of the object. #### Example ##### JSON [https://nominatim.openstreetmap.org/details?osmtype=W&osmid=32619803&entrances=1&format=json](https://nominatim.openstreetmap.org/details?osmtype=W&osmid=32619803&entrances=1&format=json) ```json { "place_id": 124325848, "parent_place_id": 123936289, "osm_type": "W", "osm_id": 32619803, "category": "shop", "type": "supermarket", "admin_level": 15, "localname": "PENNY", ... "entrances": [ { "osm_id": 1733488238, "type": "yes", "lat": "51.0466704", "lon": "12.8077106", "extratags": { "foot": "yes" } }, { "osm_id": 1733488256, "type": "main", "lat": "51.0467197", "lon": "12.8078448", "extratags": { "foot": "yes" } }, { "osm_id": 1733498087, "type": "exit", "lat": "51.0467081", "lon": "12.8078131", "extratags": { "foot": "yes" } }, { "osm_id": 7914950851, "type": "service", "lat": "51.0468487", "lon": "12.8075876", "extratags": { "access": "delivery" } } ] } ``` ================================================ FILE: docs/api/Overview.md ================================================ This section describes the API V1 of the Nominatim web service. The service offers the following endpoints: * __[/search](Search.md)__ - search OSM objects by name or type * __[/reverse](Reverse.md)__ - search OSM object by their location * __[/lookup](Lookup.md)__ - look up address details for OSM objects by their ID * __[/status](Status.md)__ - query the status of the server * __/deletable__ - list objects that have been deleted in OSM but are held back in Nominatim in case the deletion was accidental * __/polygons__ - list of broken polygons detected by Nominatim * __[/details](Details.md)__ - show internal details for an object (for debugging only) ================================================ FILE: docs/api/Reverse.md ================================================ # Reverse Geocoding Reverse geocoding generates an address from a coordinate given as latitude and longitude. ## How it works The reverse geocoding API does not exactly compute the address for the coordinate it receives. It works by finding the closest suitable OSM object and returning its address information. This may occasionally lead to unexpected results. First of all, Nominatim only includes OSM objects in its index that are suitable for searching. Small, unnamed paths for example are missing from the database and can therefore not be used for reverse geocoding either. The other issue to be aware of is that the closest OSM object may not always have a similar enough address to the coordinate you were requesting. For example, in dense city areas it may belong to a completely different street. ## Endpoint The main format of the reverse API is ``` https://nominatim.openstreetmap.org/reverse?lat=&lon=& ``` where `lat` and `lon` are latitude and longitude of a coordinate in WGS84 projection. The API returns exactly one result or an error when the coordinate is in an area with no OSM data coverage. !!! tip The reverse API allows a lookup of object by coordinate. If you want to look up an object by ID, use the [Address Lookup API](Lookup.md) instead. !!! danger "Deprecation warning" The API can also be used with the URL `https://nominatim.openstreetmap.org/reverse.php`. This is now deprecated and will be removed in future versions. ## Parameters This section lists additional parameters to further influence the output. ### Output format | Parameter | Value | Default | |-----------| ----- | ------- | | format | one of: `xml`, `json`, `jsonv2`, `geojson`, `geocodejson` | `xml` | See [Place Output Formats](Output.md) for details on each format. | Parameter | Value | Default | |-----------| ----- | ------- | | json_callback | function name | _unset_ | When given, then JSON output will be wrapped in a callback function with the given name. See [JSONP](https://en.wikipedia.org/wiki/JSONP) for more information. Only has an effect for JSON output formats. ### Output details | Parameter | Value | Default | |-----------| ----- | ------- | | addressdetails | 0 or 1 | 1 | When set to 1, include a breakdown of the address into elements. The exact content of the address breakdown depends on the output format. !!! tip If you are interested in a stable classification of address categories (suburb, city, state, etc), have a look at the `geocodejson` format. All other formats return classifications according to OSM tagging. There is a much larger set of categories and they are not always consistent, which makes them very hard to work with. | Parameter | Value | Default | |-----------| ----- | ------- | | extratags | 0 or 1 | 0 | When set to 1, the response include any additional information in the result that is available in the database, e.g. wikipedia link, opening hours. | Parameter | Value | Default | |-----------| ----- | ------- | | namedetails | 0 or 1 | 0 | When set to 1, include a full list of names for the result. These may include language variants, older names, references and brand. | Parameter | Value | Default | |-----------| ----- | ------- | | entrances | 0 or 1 | 0 | When set to 1, include the tagged entrances in the result. ### Language of results | Parameter | Value | Default | |-----------| ----- | ------- | | accept-language | browser language string | content of "Accept-Language" HTTP header | Preferred language order for showing search results. This may either be a simple comma-separated list of language codes or have the same format as the ["Accept-Language" HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language). !!! tip First-time users of Nominatim tend to be confused that they get different results when using Nominatim in the browser versus in a command-line tool like wget or curl. The command-line tools usually don't send any Accept-Language header, prompting Nominatim to show results in the local language. Browsers on the contrary always send the currently chosen browser language. ### Result restriction | Parameter | Value | Default | |-----------| ----- | ------- | | zoom | 0-18 | 18 | Level of detail required for the address. This is a number that corresponds roughly to the zoom level used in XYZ tile sources in frameworks like Leaflet.js, Openlayers etc. In terms of address details the zoom levels are as follows: zoom | address detail -----|--------------- 3 | country 5 | state 8 | county 10 | city 12 | town / borough 13 | village / suburb 14 | neighbourhood 15 | any settlement 16 | major streets 17 | major and minor streets 18 | building | Parameter | Value | Default | |-----------| ----- | ------- | | layer | comma-separated list of: `address`, `poi`, `railway`, `natural`, `manmade` | `address,poi` | The layer filter allows to select places by themes. The `address` layer contains all places that make up an address: address points with house numbers, streets, inhabited places (suburbs, villages, cities, states etc.) and administrative boundaries. The `poi` layer selects all point of interest. This includes classic points of interest like restaurants, shops, hotels but also less obvious features like recycling bins, guideposts or benches. The `railway` layer includes railway infrastructure like tracks. Note that in Nominatim's standard configuration, only very few railway features are imported into the database. The `natural` layer collects features like rivers, lakes and mountains while the `manmade` layer functions as a catch-all for features not covered by the other layers. ### Polygon output | Parameter | Value | Default | |-----------| ----- | ------- | | polygon_geojson | 0 or 1 | 0 | | polygon_kml | 0 or 1 | 0 | | polygon_svg | 0 or 1 | 0 | | polygon_text | 0 or 1 | 0 | Add the full geometry of the place to the result output. Output formats in GeoJSON, KML, SVG or WKT are supported. Only one of these options can be used at a time. | Parameter | Value | Default | |-----------| ----- | ------- | | polygon_threshold | floating-point number | 0.0 | When one of the polygon_* outputs is chosen, return a simplified version of the output geometry. The parameter describes the tolerance in degrees with which the geometry may differ from the original geometry. Topology is preserved in the geometry. ### Other | Parameter | Value | Default | |-----------| ----- | ------- | | email | valid email address | _unset_ | If you are making large numbers of request please include an appropriate email address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details. | Parameter | Value | Default | |-----------| ----- | ------- | | debug | 0 or 1 | 0 | Output assorted developer debug information. Data on internals of Nominatim's "search loop" logic, and SQL queries. The output is HTML format. This overrides the specified machine readable format. ## Examples * [https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1&layer=address](https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1&layer=address) ```xml 135, Pilkington Avenue, Wylde Green, City of Birmingham, West Midlands (county), B72, United Kingdom 135 Pilkington Avenue Wylde Green Sutton Coldfield City of Birmingham West Midlands (county) B72 United Kingdom gb ``` ##### Example with `format=jsonv2` * [https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521&layer=address](https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521&layer=address) ```json { "place_id":"134140761", "licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/www.openstreetmap.org\/copyright", "osm_type":"way", "osm_id":"280940520", "lat":"-34.4391708", "lon":"-58.7064573", "place_rank":"26", "category":"highway", "type":"motorway", "importance":"0.1", "addresstype":"road", "display_name":"Autopista Pedro Eugenio Aramburu, El Triángulo, Partido de Malvinas Argentinas, Buenos Aires, 1.619, Argentina", "name":"Autopista Pedro Eugenio Aramburu", "address":{ "road":"Autopista Pedro Eugenio Aramburu", "village":"El Triángulo", "state_district":"Partido de Malvinas Argentinas", "state":"Buenos Aires", "postcode":"1.619", "country":"Argentina", "country_code":"ar" }, "boundingbox":["-34.44159","-34.4370994","-58.7086067","-58.7044712"] } ``` ##### Example with `format=geojson` * [https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989&layer=address](https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989&layer=address) ```json { "type": "FeatureCollection", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "features": [ { "type": "Feature", "properties": { "place_id": "18512203", "osm_type": "node", "osm_id": "1704756187", "place_rank": "30", "category": "place", "type": "house", "importance": "0", "addresstype": "place", "name": null, "display_name": "71, Via Guglielmo Marconi, Saragozza-Porto, Bologna, BO, Emilia-Romagna, 40122, Italy", "address": { "house_number": "71", "road": "Via Guglielmo Marconi", "suburb": "Saragozza-Porto", "city": "Bologna", "county": "BO", "state": "Emilia-Romagna", "postcode": "40122", "country": "Italy", "country_code": "it" } }, "bbox": [ 11.3397676, 44.5014307, 11.3399676, 44.5016307 ], "geometry": { "type": "Point", "coordinates": [ 11.3398676, 44.5015307 ] } } ] } ``` ##### Example with `format=geocodejson` [https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663&layer=address](https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663&layer=address) ```json { "type": "FeatureCollection", "geocoding": { "version": "0.1.0", "attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "licence": "ODbL", "query": "60.229917843587,11.16630979382" }, "features": { "type": "Feature", "properties": { "geocoding": { "place_id": "42700574", "osm_type": "node", "osm_id": "3110596255", "type": "house", "accuracy": 0, "label": "1, Løvenbergvegen, Mogreina, Ullensaker, Akershus, 2054, Norway", "name": null, "housenumber": "1", "street": "Løvenbergvegen", "postcode": "2054", "county": "Akershus", "country": "Norway", "admin": { "level7": "Ullensaker", "level4": "Akershus", "level2": "Norway" } } }, "geometry": { "type": "Point", "coordinates": [ 11.1658572, 60.2301296 ] } } } ``` ================================================ FILE: docs/api/Search.md ================================================ # Search queries The search API allows you to look up a location from a textual description or address. Nominatim supports structured and free-form search queries. The search query may also contain [special phrases](https://wiki.openstreetmap.org/wiki/Nominatim/Special_Phrases) which are translated into specific OpenStreetMap (OSM) tags (e.g. Pub => `amenity=pub`). This can be used to narrow down the kind of objects to be returned. !!! note Special phrases are not suitable to query all objects of a certain type in an area. Nominatim will always just return a collection of the best matches. To download OSM data by object type, use the [Overpass API](https://overpass-api.de/). ## Endpoint The search API has the following format: ``` https://nominatim.openstreetmap.org/search? ``` !!! danger "Deprecation warning" The API can also be used with the URL `https://nominatim.openstreetmap.org/search.php`. This is now deprecated and will be removed in future versions. The query term can be given in two different forms: free-form or structured. ### Free-form query | Parameter | Value | |-----------| ----- | | q | Free-form query string to search for | In this form, the query can be unstructured. Free-form queries are processed first left-to-right and then right-to-left if that fails. So you may search for [pilkington avenue, birmingham](https://nominatim.openstreetmap.org/search?q=pilkington+avenue,birmingham) as well as for [birmingham, pilkington avenue](https://nominatim.openstreetmap.org/search?q=birmingham,+pilkington+avenue). Commas are optional, but improve performance by reducing the complexity of the search. The free-form may also contain special phrases to describe the type of place to be returned or a coordinate to search close to a position. ### Structured query | Parameter | Value | |----------- | ----- | | amenity | name and/or type of POI | | street | housenumber and streetname | | city | city | | county | county | | state | state | | country | country | | postalcode | postal code | The structured form of the search query allows to lookup up an address that is already split into its components. Each parameter represents a field of the address. All parameters are optional. You should only use the ones that are relevant for the address you want to geocode. !!! Attention Cannot be combined with the `q=` parameter. Newer versions of the API will return an error if you do so. Older versions simply return unexpected results. ## Parameters The following parameters can be used to further restrict the search and change the output. They are usable for both forms of the search query. ### Output format | Parameter | Value | Default | |-----------| ----- | ------- | | format | one of: `xml`, `json`, `jsonv2`, `geojson`, `geocodejson` | `jsonv2` | See [Place Output Formats](Output.md) for details on each format. !!! note The Nominatim service at [https://nominatim.openstreetmap.org](https://nominatim.openstreetmap.org) has a different default behaviour for historical reasons. When the `format` parameter is omitted, the request will be forwarded to the Web UI. | Parameter | Value | Default | |-----------| ----- | ------- | | json_callback | function name | _unset_ | When given, then JSON output will be wrapped in a callback function with the given name. See [JSONP](https://en.wikipedia.org/wiki/JSONP) for more information. Only has an effect for JSON output formats. | Parameter | Value | Default | |-----------| ----- | ------- | | limit | number | 10 | Limit the maximum number of returned results. Cannot be more than 40. Nominatim may decide to return less results than given, if additional results do not sufficiently match the query. ### Output details | Parameter | Value | Default | |-----------| ----- | ------- | | addressdetails | 0 or 1 | 0 | When set to 1, include a breakdown of the address into elements. The exact content of the address breakdown depends on the output format. !!! tip If you are interested in a stable classification of address categories (suburb, city, state, etc), have a look at the `geocodejson` format. All other formats return classifications according to OSM tagging. There is a much larger set of categories and they are not always consistent, which makes them very hard to work with. | Parameter | Value | Default | |-----------| ----- | ------- | | extratags | 0 or 1 | 0 | When set to 1, the response include any additional information in the result that is available in the database, e.g. wikipedia link, opening hours. | Parameter | Value | Default | |-----------| ----- | ------- | | namedetails | 0 or 1 | 0 | When set to 1, include a full list of names for the result. These may include language variants, older names, references and brand. | Parameter | Value | Default | |-----------| ----- | ------- | | entrances | 0 or 1 | 0 | When set to 1, include the tagged entrances in the result. ### Language of results | Parameter | Value | Default | |-----------| ----- | ------- | | accept-language | browser language string | content of "Accept-Language" HTTP header | Preferred language order for showing search results. This may either be a simple comma-separated list of language codes or have the same format as the ["Accept-Language" HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language). !!! tip First-time users of Nominatim tend to be confused that they get different results when using Nominatim in the browser versus in a command-line tool like wget or curl. The command-line tools usually don't send any Accept-Language header, prompting Nominatim to show results in the local language. Browsers on the contrary always send the currently chosen browser language. ### Result restriction There are two ways to influence the results. *Filters* exclude certain kinds of results completely. *Boost parameters* only change the order of the results and thus give a preference to some results over others. | Parameter | Value | Default | |-----------| ----- | ------- | | countrycodes | comma-separated list of country codes | _unset_ | Filter that limits the search results to one or more countries. The country code must be the [ISO 3166-1alpha2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code of the country, e.g. `gb` for the United Kingdom, `de` for Germany. Each place in Nominatim is assigned to one country code based on OSM country boundaries. In rare cases a place may not be in any country at all, for example, when it is in international waters. These places are also excluded when the filter is set. !!! Note This parameter should not be confused with the 'country' parameter of the structured query. The 'country' parameter contains a search term and will be handled with some fuzziness. The `countrycodes` parameter is a hard filter and as such should be preferred. Having both parameters in the same query will work. If the parameters contradict each other, the search will come up empty. | Parameter | Value | Default | |-----------| ----- | ------- | | layer | comma-separated list of: `address`, `poi`, `railway`, `natural`, `manmade` | _unset_ (no restriction) | The layer filter allows to select places by themes. The `address` layer contains all places that make up an address: address points with house numbers, streets, inhabited places (suburbs, villages, cities, states tec.) and administrative boundaries. The `poi` layer selects all point of interest. This includes classic POIs like restaurants, shops, hotels but also less obvious features like recycling bins, guideposts or benches. The `railway` layer includes railway infrastructure like tracks. Note that in Nominatim's standard configuration, only very few railway features are imported into the database. The `natural` layer collects features like rivers, lakes and mountains while the `manmade` layer functions as a catch-all for features not covered by the other layers. | Parameter | Value | Default | |-----------| ----- | ------- | | featureType | one of: `country`, `state`, `city`, `settlement` | _unset_ | The featureType allows to have a more fine-grained selection for places from the address layer. Results can be restricted to places that make up the 'state', 'country' or 'city' part of an address. A featureType of `settlement` selects any human inhabited feature from 'state' down to 'neighbourhood'. When featureType is set, then results are automatically restricted to the address layer (see above). !!! tip Instead of using the featureType filters `country`, `state` or `city`, you can also use a structured query without the finer-grained parameters amenity or street. | Parameter | Value | Default | |-----------| ----- | ------- | | exclude_place_ids | comma-separated list of ids (OSM IDs where possible, otherwise place_ids) | If you do not want certain OSM objects to appear in the search result, give a comma separated list of the ids you want to skip. Each entry may be one of: * a Nominatim internal `place_id` (for example `125279639`) * an OSM object reference in the form `` where `` is one of `N` (node), `W` (way) or `R` (relation), for example `N107775` Usage of OSM IDs is recommended because they are server independent. `place_id`s are stil required for results without an OSM object reference (for example, postcodes and countries). When a street is excluded via its OSM ID, then interpolations and TIGER data derived from that street are excluded as well. This can be used to retrieve additional search results. For example, if a previous query only returned a few results, then including those here would cause the search to return other, less accurate, matches (if possible). | Parameter | Value | Default | |-----------| ----- | ------- | | viewbox | `,,,` | _unset_ | Boost parameter which focuses the search on the given area. Any two corner points of the box are accepted as long as they make a proper box. `x` is longitude, `y` is latitude. | Parameter | Value | Default | |-----------| ----- | ------- | | bounded | 0 or 1 | 0 | When set to 1, then it turns the 'viewbox' parameter (see above) into a filter parameter, excluding any results outside the viewbox. When `bounded=1` is given and the viewbox is small enough, then an amenity-only search is allowed. Give the special keyword for the amenity in square brackets, e.g. `[pub]` and a selection of objects of this type is returned. There is no guarantee that the result returns all objects in the area. ### Polygon output | Parameter | Value | Default | |-----------| ----- | ------- | | polygon_geojson | 0 or 1 | 0 | | polygon_kml | 0 or 1 | 0 | | polygon_svg | 0 or 1 | 0 | | polygon_text | 0 or 1 | 0 | Add the full geometry of the place to the result output. Output formats in GeoJSON, KML, SVG or WKT are supported. Only one of these options can be used at a time. | Parameter | Value | Default | |-----------| ----- | ------- | | polygon_threshold | floating-point number | 0.0 | When one of the polygon_* outputs is chosen, return a simplified version of the output geometry. The parameter describes the tolerance in degrees with which the geometry may differ from the original geometry. Topology is preserved in the geometry. ### Other | Parameter | Value | Default | |-----------| ----- | ------- | | email | valid email address | _unset_ | If you are making large numbers of request please include an appropriate email address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details. | Parameter | Value | Default | |-----------| ----- | ------- | | dedupe | 0 or 1 | 1 | Sometimes you have several objects in OSM identifying the same place or object in reality. The simplest case is a street being split into many different OSM ways due to different characteristics. Nominatim will attempt to detect such duplicates and only return one match. Setting this parameter to 0 disables this deduplication mechanism and ensures that all results are returned. | Parameter | Value | Default | |-----------| ----- | ------- | | debug | 0 or 1 | 0 | Output assorted developer debug information. Data on internals of Nominatim's "search loop" logic, and SQL queries. The output is HTML format. This overrides the specified machine readable format. ## Examples ##### XML with KML polygon * [https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon_kml=1&addressdetails=1](https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon_kml=1&addressdetails=1) ```xml -1.816513,52.5487566 -1.816434,52.5487473 -1.816429,52.5487629 -1.8163717,52.5487561 -1.8163464,52.5488346 -1.8164599,52.5488481 -1.8164685,52.5488213 -1.8164913,52.548824 -1.816513,52.5487566 135 Pilkington Avenue Maney Sutton Coldfield Wylde Green Birmingham GB-BIR West Midlands Combined Authority England GB-ENG B72 1LH United Kingdom gb ``` ##### JSON with SVG polygon [https://nominatim.openstreetmap.org/search?q=Unter%20den%20Linden%201%20Berlin&format=json&addressdetails=1&limit=1&polygon_svg=1](https://nominatim.openstreetmap.org/search?q=Unter%20den%20Linden%201%20Berlin&format=json&addressdetails=1&limit=1&polygon_svg=1) ```json [ { "address": { "ISO3166-2-lvl4": "DE-BE", "borough": "Mitte", "city": "Berlin", "country": "Deutschland", "country_code": "de", "historic": "Kommandantenhaus", "house_number": "1", "neighbourhood": "Friedrichswerder", "postcode": "10117", "road": "Unter den Linden", "suburb": "Mitte" }, "boundingbox": [ "52.5170798", "52.5173311", "13.3975116", "13.3981577" ], "class": "historic", "display_name": "Kommandantenhaus, 1, Unter den Linden, Friedrichswerder, Mitte, Berlin, 10117, Deutschland", "importance": 0.8135042058306902, "lat": "52.51720765", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "lon": "13.397834399325466", "osm_id": 15976890, "osm_type": "way", "place_id": 108681845, "svg": "M 13.3975116 -52.5172905 L 13.397549 -52.5170798 13.397715 -52.5170906 13.3977122 -52.5171064 13.3977392 -52.5171086 13.3977417 -52.5170924 13.3979655 -52.5171069 13.3979623 -52.5171233 13.3979893 -52.5171248 13.3979922 -52.5171093 13.3981577 -52.5171203 13.398121 -52.5173311 13.3978115 -52.5173103 Z", "type": "house" } ] ``` ##### JSON with address details [https://nominatim.openstreetmap.org/search?addressdetails=1&q=bakery+in+berlin+wedding&format=jsonv2&limit=1](https://nominatim.openstreetmap.org/search?addressdetails=1&q=bakery+in+berlin+wedding&format=jsonv2&limit=1) ```json [ { "address": { "ISO3166-2-lvl4": "DE-BE", "borough": "Mitte", "city": "Berlin", "country": "Deutschland", "country_code": "de", "neighbourhood": "Sprengelkiez", "postcode": "13347", "road": "Lindower Straße", "shop": "Ditsch", "suburb": "Wedding" }, "addresstype": "shop", "boundingbox": [ "52.5427201", "52.5427654", "13.3668619", "13.3669442" ], "category": "shop", "display_name": "Ditsch, Lindower Straße, Sprengelkiez, Wedding, Mitte, Berlin, 13347, Deutschland", "importance": 9.99999999995449e-06, "lat": "52.54274275", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright", "lon": "13.36690305710228", "name": "Ditsch", "osm_id": 437595031, "osm_type": "way", "place_id": 204751033, "place_rank": 30, "type": "bakery" } ] ``` ##### GeoJSON [https://nominatim.openstreetmap.org/search?q=17+Strada+Pictor+Alexandru+Romano%2C+Bukarest&format=geojson](https://nominatim.openstreetmap.org/search?q=17+Strada+Pictor+Alexandru+Romano%2C+Bukarest&format=geojson) ```json { "type": "FeatureCollection", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "features": [ { "type": "Feature", "properties": { "place_id": "35811445", "osm_type": "node", "osm_id": "2846295644", "display_name": "17, Strada Pictor Alexandru Romano, Bukarest, Bucharest, Sector 2, Bucharest, 023964, Romania", "place_rank": "30", "category": "place", "type": "house", "importance": 0.62025 }, "bbox": [ 26.1156689, 44.4354754, 26.1157689, 44.4355754 ], "geometry": { "type": "Point", "coordinates": [ 26.1157189, 44.4355254 ] } } ] } ``` ##### GeocodeJSON [https://nominatim.openstreetmap.org/search?q=%CE%91%CE%B3%CE%AF%CE%B1+%CE%A4%CF%81%CE%B9%CE%AC%CE%B4%CE%B1%2C+%CE%91%CE%B4%CF%89%CE%BD%CE%B9%CE%B4%CE%BF%CF%82%2C+Athens%2C+Greece&format=geocodejson](https://nominatim.openstreetmap.org/search?q=%CE%91%CE%B3%CE%AF%CE%B1+%CE%A4%CF%81%CE%B9%CE%AC%CE%B4%CE%B1%2C+%CE%91%CE%B4%CF%89%CE%BD%CE%B9%CE%B4%CE%BF%CF%82%2C+Athens%2C+Greece&format=geocodejson) ```json { "type": "FeatureCollection", "geocoding": { "version": "0.1.0", "attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "licence": "ODbL", "query": "Αγία Τριάδα, Αδωνιδος, Athens, Greece" }, "features": [ { "type": "Feature", "properties": { "geocoding": { "type": "place_of_worship", "label": "Αγία Τριάδα, Αδωνιδος, Άγιος Νικόλαος, 5º Δημοτικό Διαμέρισμα Αθηνών, Athens, Municipality of Athens, Regional Unit of Central Athens, Region of Attica, Attica, 11472, Greece", "name": "Αγία Τριάδα", "admin": null } }, "geometry": { "type": "Point", "coordinates": [ 23.72949633941, 38.0051697 ] } } ] } ``` ================================================ FILE: docs/api/Status.md ================================================ # Status Report on the state of the service and database. Useful for checking if the service is up and running. The JSON output also reports when the database was last updated. ## Endpoint The status API has the following format: ``` https://nominatim.openstreetmap.org/status ``` !!! danger "Deprecation warning" The API can also be used with the URL `https://nominatim.openstreetmap.org/status.php`. This is now deprecated and will be removed in future versions. ## Parameters The status endpoint takes a single optional parameter: | Parameter | Value | Default | |-----------| ----- | ------- | | format | one of: `text`, `json` | 'text' | Selects the output format. See below. ## Output #### Text format When everything is okay, a status code 200 is returned and a simple message: `OK` On error it will return HTTP status code 500 and print a detailed error message, e.g. `ERROR: Database connection failed`. #### JSON format Always returns a HTTP code 200, when the status call could be executed. On success a JSON dictionary with the following structure is returned: ```json { "status": 0, "message": "OK", "data_updated": "2020-05-04T14:47:00+00:00", "software_version": "3.6.0-0", "database_version": "3.6.0-0" } ``` The `software_version` field contains the version of Nominatim used to serve the API. The `database_version` field contains the version of the data format in the database. On error will return a shorter JSON dictionary with the error message and status only, e.g. ```json { "status": 700, "message": "Database connection failed" } ``` ================================================ FILE: docs/customize/Country-Settings.md ================================================ # Customizing Per-Country Data Whenever an OSM is imported into Nominatim, the object is first assigned a country. Nominatim can use this information to adapt various aspects of the address computation to the local customs of the country. This section explains how country assignment works and the principal per-country localizations. ## Country assignment Countries are assigned on the basis of country data from the OpenStreetMap input data itself. Countries are expected to be tagged according to the [administrative boundary schema](https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative): a OSM relation with `boundary=administrative` and `admin_level=2`. Nominatim uses the country code to distinguish the countries. If there is no country data available for a point, then Nominatim uses the fallback data imported from `data/country_osm_grid.sql.gz`. This was computed from OSM data as well but is guaranteed to cover all countries. Some OSM objects may also be located outside any country, for example a buoy in the middle of the ocean. These object do not get any country assigned and get a default treatment when it comes to localized handling of data. ## Per-country settings ### Global country settings The main place to configure settings per country is the file `settings/country_settings.yaml`. This file has one section per country that is recognised by Nominatim. Each section is tagged with the country code (in lower case) and contains the different localization information. Only countries which are listed in this file are taken into account for computations. For example, the section for Andorra looks like this: ``` partition: 35 languages: ca names: !include country-names/ad.yaml postcode: pattern: "(ddd)" output: AD\1 ``` The individual settings are described below. #### `partition` Nominatim internally splits the data into multiple tables to improve performance. The partition number tells Nominatim into which table to put the country. This is purely internal management and has no effect on the output data. The default is to have one partition per country. #### `languages` A comma-separated list of ISO-639 language codes of default languages in the country. These are the languages used in name tags without a language suffix. Note that this is not necessarily the same as the list of official languages in the country. There may be officially recognised languages in a country which are only ever used in name tags with the appropriate language suffixes. Conversely, a non-official language may appear a lot in the name tags, for example when used as an unofficial Lingua Franca. List the languages in order of frequency of appearance with the most frequently used language first. It is not recommended to add languages when there are only very few occurrences. If only one language is listed, then Nominatim will 'auto-complete' the language of names without an explicit language-suffix. #### `names` List of names of the country and its translations. These names are used as a baseline. It is always possible to search countries by the given names, no matter what other names are in the OSM data. They are also used as a fallback when a needed translation is not available. !!! Note The list of names per country is currently fairly large because Nominatim supports translations in many languages per default. That is why the name lists have been separated out into extra files. You can find the name lists in the file `settings/country-names/.yaml`. The names section in the main country settings file only refers to these files via the special `!include` directive. #### `postcode` Describes the format of the postcode that is in use in the country. When a country has no official postcodes, set this to no. Example: ``` ae: postcode: no ``` When a country has a postcode, you need to state the postcode pattern and the default output format. Example: ``` bm: postcode: pattern: "(ll)[ -]?(dd)" output: \1 \2 ``` The **pattern** is a regular expression that describes the possible formats accepted as a postcode. The pattern follows the standard syntax for [regular expressions in Python](https://docs.python.org/3/library/re.html#regular-expression-syntax) with two extra shortcuts: `d` is a shortcut for a single digit([0-9]) and `l` for a single ASCII letter ([A-Z]). Use match groups to indicate groups in the postcode that may optionally be separated with a space or a hyphen. For example, the postcode for Bermuda above always consists of two letters and two digits. They may optionally be separated by a space or hyphen. That means that Nominatim will consider `AB56`, `AB 56` and `AB-56` spelling variants for one and the same postcode. Never add the country code in front of the postcode pattern. Nominatim will automatically accept variants with a country code prefix for all postcodes. The **output** field is an optional field that describes what the canonical spelling of the postcode should be. The format is the [regular expression expand syntax](https://docs.python.org/3/library/re.html#re.Match.expand) referring back to the bracket groups in the pattern. Most simple postcodes only have one spelling variant. In that case, the **output** can be omitted. The postcode will simply be used as is. In the Bermuda example above, the canonical spelling would be to have a space between letters and digits. !!! Warning When your postcode pattern covers multiple variants of the postcode, then you must explicitly state the canonical output or Nominatim will not handle the variations correctly. ### Other country-specific configuration There are some other configuration files where you can set localized settings according to the assigned country. These are: * [Place ranking configuration](Ranking.md) Please see the linked documentation sections for more information. ================================================ FILE: docs/customize/Import-Styles.md ================================================ # Configuring the Import of OSM data In the very first step of a Nominatim import, OSM data is loaded into the database. Nominatim uses [osm2pgsql](https://osm2pgsql.org) for this task. It comes with a [flex style](https://osm2pgsql.org/doc/manual.html#the-flex-output) specifically tailored to filter and convert OSM data into Nominatim's internal data representation. Nominatim ships with a few preset configurations for this import, each results in a geocoding database of different detail. The [Import section](../admin/Import.md#filtering-imported-data) explains these default configurations in detail. If you want to have more control over which OSM data is added to the database, you can also create your own custom style. Create a new lua style file, put it into your project directory and then set `NOMINATIM_IMPORT_STYLE` to the name of the file. Custom style files can be used to modify the existing preset configurations or to implement your own configuration from scratch. The remainder of the page describes how the flex style works and how to customize it. ## The `flex-base` lua module The core of Nominatim's flex import configuration is the `flex-base` module. It defines the table layout used by Nominatim and provides standard implementations for the import callbacks that help with customizing how OSM tags are used by Nominatim. Every custom style must include this module to make sure that the correct tables are created. Thus start your custom style as follows: ``` lua local flex = require('flex-base') ``` ### Using preset configurations If you want to start with one of the existing presets, then you can import its settings using the `load_topic()` function: ``` lua local flex = require('flex-base') flex.load_topic('streets') ``` The `load_topic` function takes an optional second configuration parameter. The available options are explained in the [themepark section](#using-osm2pgsql-themepark). Available topics are: `admin`, `street`, `address`, `full`. These topic correspond to the [import styles](../admin/Import.md#filtering-imported-data) you can choose during import. To start with the 'extratags' style, use the `full` topic with the appropriate config parameter: ``` lua flex.load_topic('full', {with_extratags = true}) ``` !!! note You can also directly import the preset style files, e.g. `local flex = require('import-street')`. It is not possible to set extra configuration this way. ### How processing works When Nominatim processes an OSM object, it looks for four kinds of tags: The _main tags_ classify what kind of place the OSM object represents. One OSM object can have more than one main tag. In such case one database entry is created for each main tag. _Name tags_ represent searchable names of the place. _Address tags_ are used to compute the address information of the place. Address tags are used for searching and for creating a display name of the place. _Extra tags_ are any tags that are not directly related to search but contain interesting additional information. These are just saved in the database and may be returned with the result [on request](../api/Search.md#output-details). !!! danger Some tags in the extratags category are used by Nominatim to better classify the place. These tags will always be added, independent of any settings in the style. Configuring the style means deciding which key and/or key/value is used in which category. ## Changing the recognized tags The flex style offers a number of functions to set the classification of each OSM tag. Most of these functions can also take a preset string instead of a tag description. These presets describe common configurations that are also used in the definition of the predefined styles. This section lists the configuration functions and the accepted presets. #### Key match lists Some of the following functions take _key match lists_. These lists can contain three kinds of strings to match against tag keys: A string that ends in an asterisk `*` is a prefix match and accordingly matches against any key that starts with the given string (minus the `*`). A suffix match can be defined similarly with a string that starts with a `*`. Any other string is matched exactly against tag keys. ### Main tags `set/modify_main_tags()` allow to define which tags are used as main tags. It takes a lua table parameter which defines for keys and key/value combinations, how they are classified. The following classifications are recognized: | classification | meaning | | :-------------- | :------ | | always | Unconditionally use this tag as a main tag. | | named | Consider as main tag, when the object has a primary name (see [names](#name-tags) below) | | named_with_key | Consider as main tag, when the object has a primary name with a domain prefix. For example, if the main tag is `bridge=yes`, then it will only be added as an extra entry, if there is a tag `bridge:name[:XXX]` for the same object. If this property is set, all names that are not domain-specific are ignored. | | fallback | Consider as main tag only when no other main tag was found. Fallback always implies `named`, i.e. fallbacks are only tried for objects with primary names. | | postcode_area | Tag indicates a postcode area. Copy area into the table of postcodes but only when the object is a relation and has a postcode tagged. | | delete | Completely ignore the tag in any further processing | | extra | Move the tag to extratags and then ignore it for further processing | | ``| Advanced handling, see [below](#advanced-main-tag-handling) | Each key in the table parameter defines an OSM tag key. The value may be directly a classification as described above. Then the tag will be considered a main tag for any possible value that is not further defined. To further restrict which values are acceptable, give a table with the permitted values and their kind of main tag. If the table contains a simple value without key, then this is used as default for values that are not listed. `set_main_tags()` will completely replace the current main tag configuration with the new configuration. `modify_main_tags()` will merge the new configuration with the existing one. Merging is done at value level. For example, when the current setting is `highway = {'always', primary = 'named'}`, then `set_main_tags{highway = 'delete'}` will result in a rule `highway = {'delete', primary = 'named'}`. !!! example ``` lua local flex = require('import-full') flex.set_main_tags{ boundary = {administrative = 'named'}, highway = {'always', street_lamp = 'named', no = 'delete'}, landuse = 'fallback' } ``` In this example an object with a `boundary` tag will only be included when it has a value of `administrative`. Objects with `highway` tags are always included with two exceptions: the troll tag `highway=no` is deleted on the spot. And when the value is `street_lamp` then the object must also have a name, to be included. Finally, if a `landuse` tag is present then it will be used independently of the concrete value when neither boundary nor highway tags were found and the object is named. ##### Presets | Name | Description | | :----- | :---------- | | admin | Basic tag set collecting places and administrative boundaries. This set is needed also to ensure proper address computation and should therefore always be present. You can disable selected place types like `place=locality` after adding this set, if they are not relevant for your use case. | | all_boundaries | Extends the set of recognized boundaries and places to all available ones. | | natural | Tags for natural features like rivers and mountain peaks. | | street/default | Tags for streets. Major streets are always included, minor ones only when they have a name. | | street/car | Tags for all streets that can be used by a motor vehicle. | | street/all | Includes all highway features named and unnamed. | | poi/delete | Adds most POI features with and without name. Some frequent but very domain-specific values are excluded by deleting them. | | poi/extra | Like 'poi/delete' but excluded values are moved to extratags. | ##### Advanced main tag handling The groups described above are in fact only a preset for a filtering function that is used to make the final decision how a pre-selected main tag is entered into Nominatim's internal table. To further customize handling you may also supply your own filtering function. The function takes up to three parameters: a Place object of the object being processed, the key of the main tag and the value of the main tag. The function may return one of three values: * `nil` or `false` causes the entry to be ignored * the Place object causes the place to be added as is * `Place.copy(names=..., address=..., extratags=...) causes the place to be enter into the database but with name/address/extratags set to the given different values. The Place object has some read-only values that can be used to determine the handling: * **object** is the original OSM object data handed in by osm2pgsql * **admin_level** is the content of the admin_level tag, parsed into an integer and normalized to a value between 0 and 15 * **has_name** is a boolean indicating if the object has a primary name tag * **names** is a table with the collected list of name tags * **address** is a table with the collected list of address tags * **extratags** is a table with the collected list of additional tags to save !!! example ``` lua local flex = require('flex-base') flex.add_topic('street') local function no_sidewalks(place, k, v) if place.object.tags.footway == 'sidewalk' then return false end -- default behaviour is to have all footways return place end flex.modify_main_tags(highway = {'footway' = no_sidewalks} ``` This script adds a custom handler for `highway=footway`. It only includes them in the database, when the object doesn't have a tag `footway=sidewalk` indicating that it is just part of a larger street which should already be indexed. Note that it is not necessary to check the key and value of the main tag because the function is only used for the specific main tag. ### Ignored tags The function `ignore_keys()` sets the `delete` classification for keys. This function takes a _key match list_ so that it is possible to exclude groups of keys. Note that full matches always take precedence over suffix matches, which in turn take precedence over prefix matches. !!! example ``` lua local flex = require('flex-base') flex.add_topic('admin') flex.ignore_keys{'old_name', 'old_name:*'} ``` This example uses the `admin` preset with the exception that names that are no longer are in current use, are ignored. ##### Presets | Name | Description | | :----- | :---------- | | metatags | Tags with meta information about the OSM tag like source, notes and import sources. | | name | Non-names that actually describe properties or name parts. These names can throw off search and should always be removed. | | address | Extra `addr:*` tags that are not useful for Nominatim. | ### Tags for `extratags` The function `add_for_extratags()` sets the `extra` classification for keys. This function takes a _key match list_ so that it is possible to move groups of keys to extratags. Note that full matches always take precedence over suffix matches, which in turn take precedence over prefix matches. !!! example ``` lua local flex = require('flex-base') flex.add_topic('street') flex.add_for_extratags{'surface', 'access', 'vehicle', 'maxspeed'} ``` This example uses the `street` preset but adds a couple of tags that are of interest about the condition of the street. ##### Presets Accepts all [presets from ignored tags](#presets_1). ### General pre-filtering _(deprecated)_ `set_prefilters()` allows to set the `delete` and `extra` classification for main tags. This function removes all previously set main tags with `delete` and `extra` classification and then adds the newly defined tags. `set_prefilters()` takes a table with four optional fields: * __delete_keys__ is a _key match list_ for tags that should be deleted * __delete_tags__ contains a table of tag keys pointing to a list of tag values. Tags with matching key/value pairs are deleted. * __extra_keys__ is a _key match list_ for tags which should be saved into extratags * __extra_tags__ contains a table of tag keys pointing to a list of tag values. Tags with matching key/value pairs are moved to extratags. !!! danger "Deprecation warning" Use of this function should be replaced with `modify_main_tags()` to set the data from `delete_tags` and `extra_tags`, with `ignore_keys()` for the `delete_keys` parameter and with `add_for_extratags()` for the `extra_keys` parameter. ### Name tags `set/modify_name_tags()` allow to define the tags used for naming places. Name tags can only be selected by their keys. The import script distinguishes between primary and auxiliary names. A primary name is the given name of a place. Having a primary name makes a place _named_. This is important for main tags that are only included when a name is present. Auxiliary names are identifiers like references. They may be searched for but should not be included on their own. The functions take a table with two optional fields `main` and `extra`. They take _key match lists_ for primary and auxiliary names respectively. A third field `house` can contain tags for names that appear in place of house numbers in addresses. This field can only contain complete key names. 'house tags' are special in that they cause the OSM object to be added to the database independently of the presence of other main tags. `set_name_tags()` overwrites the current configuration, while `modify_name_tags()` replaces the fields that are given. (Be aware that the fields are replaced as a whole. `main = {'foo_name'}` will cause `foo_name` to become the only recognized primary name. Any previously defined primary names are forgotten.) !!! example ``` lua local flex = require('flex-base') flex.set_main_tags{highway = {traffic_light = 'named'}} flex.set_name_tags{main = {'name', 'name:*'}, extra = {'ref'} } ``` This example creates a search index over traffic lights but will only include those that have a common name and not those which just have some reference ID from the city. ##### Presets | Name | Description | | :----- | :---------- | | core | Basic set of recognized names for all places. | | address | Additional names useful when indexing full addresses. | | poi | Extended set of recognized names for pois. Use on top of the core set. | ### Address tags `set/modify_address_tags()` defines the tags that will be used to build up the address of an object. Address tags can only be chosen by their key. The functions take a table with arbitrary fields, each defining a key list or _key match list_. Some fields have a special meaning: | Field | Type | Description | | :---------| :-------- | :-----------| | main | key list | Tags that make a full address object out of the OSM object. This is usually the house number or variants thereof. If a main address tag appears, then the object will always be included, if necessary with a fallback of `place=house`. If the key has a prefix of `addr:` or `is_in:` this will be stripped. | | extra | key match list | Supplementary tags for addresses, tags like `addr:street`, `addr:city` etc. If the key has a prefix of `addr:` or `is_in:` this will be stripped. | | interpolation | key list | Tags that identify address interpolation lines. | | country | key match list | Tags that may contain the country the place is in. The first found value with a two-letter code will be accepted, all other values are discarded. | | _other_ | key match list | Summary field. If a key matches the key match list, then its value will be added to the address tags with the name of the field as key. If multiple tags match, then an arbitrary one wins. | `set_address_tags()` overwrites the current configuration, while `modify_address_tags()` replaces the fields that are given. (Be aware that the fields are replaced as a whole.) !!! example ``` lua local flex = require('import-full') flex.set_address_tags{ main = {'addr:housenumber'}, extra = {'addr:*'}, postcode = {'postal_code', 'postcode', 'addr:postcode'}, country = {'country_code', 'ISO3166-1'} } ``` In this example all tags which begin with `addr:` will be saved in the address tag list. If one of the tags is `addr:housenumber`, the object will fall back to be entered as a `place=house` in the database unless there is another interested main tag to be found. Tags with keys `country_code` and `ISO3166-1` are saved with their value under `country` in the address tag list. The same thing happens to postcodes, they will always be saved under the key `postcode` thus normalizing the multitude of keys that are used in the OSM database. ##### Presets | Name | Description | | :----- | :---------- | | core | Basic set of tags needed to recognize address relationship for any place. Always include this. | | houses | Additional set of tags needed to recognize proper addresses | ### Handling of unclassified tags `set_unused_handling()` defines what to do with tags that remain after all tags have been classified using the functions above. There are two ways in which the function can be used: `set_unused_handling(delete_keys = ..., delete_tags = ...)` deletes all keys that match the descriptions in the parameters and moves all remaining tags into the extratags list. `set_unused_handling(extra_keys = ..., extra_tags = ...)` moves all tags matching the parameters into the extratags list and then deletes the remaining tags. For the format of the parameters see the description in `set_prefilters()` above. When no special handling is set, then unused tags will be discarded with one exception: place tags are kept in extratags for administrative boundaries. When using a custom setting, you should also make sure that the place tag is added for extratags. !!! example ``` lua local flex = require('import-full') flex.set_address_tags{ main = {'addr:housenumber'}, extra = {'addr:*', 'tiger:county'} } flex.set_unused_handling{delete_keys = {'tiger:*'}} ``` In this example all remaining tags except those beginning with `tiger:` are moved to the extratags list. Note that it is not possible to already delete the tiger tags with `set_prefilters()` because that would remove tiger:county before the address tags are processed. ## Filling additional tables Most of the OSM objects are saved in the main `place` table for further processing. In addition to that, there are some smaller tables that save specialised information. The content of these tables can be customized as well. ### Entrance table The table `place_entrance` saves information about OSM nodes that represent an entrance. This data is later mingled with buildings and other areas and can be returned [on request](../api/Search.md#output-details). The table saves the type of entrance as well as a set of custom extra tags. The function `set_entrance_filter()` can be used to customize the table's content. When called without any parameter, then filling the entrance table will be disabled. When called with a preset name, the appropriate preset will be applied. To create a custom configuration, call the function with a table with the following fields: * __main_tags__ is a list of tags that mark an entrance node. The value of the first tag found in the list will be used as the entrance type. * __extra_include__ is an optional list of tags to be added to the extratags for this entrance. When left out, all tags except for the ones defined in 'main_tags' will be included. To disable saving of extra tags, set this to the empty list. * __extra_exclude__ defines an optional list of tags to drop before including the remaining tags as extratags. Note that the tags defined in 'main_tags' will always be excluded, independently of this setting. To have even more fine-grained control over the output, you can also hand in a callback for processing entrance information. The callback function receives a single parameter, the [osm2pgsql object](https://osm2pgsql.org/doc/manual.html#processing-callbacks). This object itself must not be modified. The callback should return either `nil` when the object is not an entrance. Or it returns a table with a mandatory `entrance` field containing a string with the type of entrance and an optional `extratags` field with a simple key-value table of extra information. ##### Presets | Name | Description | | :----- | :---------- | | default | Standard configuration used with `full` and `extratags` styles. | ## Customizing osm2pgsql callbacks osm2pgsql expects the flex style to implement three callbacks, one process function per OSM type. If you want to implement special handling for certain OSM types, you can override the default implementations provided by the flex-base module. ### Enabling additional relation types OSM relations can represent very diverse [types of real-world objects](https://wiki.openstreetmap.org/wiki/Key:type). To be able to process them correctly, Nominatim needs to understand how to create a geometry for each type. By default, the script knows how to process relations of type `multipolygon`, `boundary` and `waterway`. All other relation types are ignored. To add other types relations, set `RELATION_TYPES` for the type to the kind of geometry that should be created. The following kinds of geometries can be used: * __relation_as_multipolygon__ creates a (Multi)Polygon from the ways in the relation. If the ways do not form a valid area, then the object is silently discarded. * __relation_as_multiline__ creates a (Multi)LineString from the ways in the relations. Ways are combined as much as possible without any regards to their order in the relation. !!! Example ``` lua local flex = require('import-full') flex.RELATION_TYPES['site'] = flex.relation_as_multipolygon ``` With this line relations of `type=site` will be included in the index according to main tags found. This only works when the site relation resolves to a valid area. Nodes in the site relation are not part of the geometry. ### Adding additional logic to processing functions The default processing functions are also exported by the flex-base module as `process_node`, `process_way` and `process_relation`. These can be used to implement your own processing functions with some additional processing logic. !!! Example ``` lua local flex = require('import-full') function osm2pgsql.process_relation(object) if object.tags.boundary ~= 'administrative' or object.tags.admin_level ~= '2' then flex.process_relation(object) end end ``` This example discards all country-level boundaries and uses standard handling for everything else. This can be useful if you want to use your own custom country boundaries. ### Customizing the main processing function !!! danger "Deprecation Warning" The style used to allow overwriting the internal processing function `process_tags()`. While this is currently still possible, it is no longer encouraged and may stop working in future versions. The internal `Place` class should now be considered read-only. ## Using osm2pgsql-themepark The Nominatim osm2pgsql style is designed so that it can also be used as a theme for [osm2pgsql-themepark](https://osm2pgsql.org/themepark/). This makes it easy to combine Nominatim with other projects like [openstreetmap-carto](https://github.com/gravitystorm/openstreetmap-carto) in the same database. To set up one of the preset styles, simply include a topic with the same name: ``` local themepark = require('themepark') themepark:add_topic('nominatim/address') ``` Themepark topics offer two configuration options: * **street_theme** allows to choose one of the sub topics for streets: * _default_ - include all major streets and named minor paths * _car_ - include all streets physically usable by cars * _all_ - include all major streets and minor paths * **with_extratags**, when set to a truthy value, then tags that are not specifically used for address or naming are added to the extratags column The customization functions described in the [Changing recognized tags](#changing-the-recognized-tags) section are available from the theme. To access the theme you need to explicitly initialize it. !!! Example ``` lua local themepark = require('themepark') themepark:add_topic('nominatim/full', {with_extratags = true}) local flex = themepark:init_theme('nominatim') flex.modify_main_tags{'amenity' = { 'waste_basket' = 'delete'} } ``` This example uses the full Nominatim configuration but disables importing waste baskets. You may also write a new configuration from scratch. Simply omit including a Nominatim topic and only call the required customization functions. Customizing the osm2pgsql processing functions as explained [above](#adding-additional-logic-to-processing-functions) is not possible when running under themepark. Instead include other topics that make the necessary modifications or add an additional processor before including the Nominatim topic. !!! Example ``` lua local themepark = require('themepark') local function discard_country_boundaries(object) if object.tags.boundary == 'administrative' and object.tags.admin_level == '2' then return 'stop' end end themepark:add_proc('relation', discard_country_boundaries) -- Order matters here. The topic needs to be added after the custom callback. themepark:add_topic('nominatim/full', {with_extratags = true}) ``` Discarding country-level boundaries when running under themepark. ## Changing the style of existing databases There is usually no issue changing the style of a database that is already imported and now kept up-to-date with change files. Just be aware that any change in the style applies to updates only. If you want to change the data that is already in the database, then a reimport is necessary. ================================================ FILE: docs/customize/Importance.md ================================================ ## Importance Search requests can yield multiple results which match equally well with the original query. In such case Nominatim needs to order the results according to a different criterion: importance. This is a measure for how likely it is that a user will search for a given place. This section explains the sources Nominatim uses for computing importance of a place and how to customize them. ### How importance is computed The main value for importance is derived from page ranking values for Wikipedia pages for a place. For places that do not have their own Wikipedia page, a formula is used that derives a static importance from the place's [search rank](../customize/Ranking.md#search-rank). In a second step, a secondary importance value is added which is meant to represent how well-known the general area is where the place is located. It functions as a tie-breaker between places with very similar primary importance values. nominatim.org has preprocessed importance tables for the [primary Wikipedia rankings](https://nominatim.org/data/wikimedia-importance.csv.gz) and for [secondary importance](https://nominatim.org/data/wikimedia-secondary-importance.sql.gz) based on Wikipedia importance of the administrative areas. The source code for creating these files is available in the Github projects [osm-search/wikipedia-wikidata](https://github.com/osm-search/wikipedia-wikidata) and [osm-search/secondary-importance](https://github.com/osm-search/secondary-importance). ### Customizing secondary importance The secondary importance is implemented as a simple [Postgis raster](https://postgis.net/docs/raster.html) table, where Nominatim looks up the value for the coordinates of the centroid of a place. You can provide your own secondary importance raster in form of an SQL file named `secondary_importance.sql.gz` in your project directory. The SQL file needs to drop and (re)create a table `secondary_importance` which must as a minimum contain a column `rast` of type `raster`. The raster must be in EPSG:4326 and contain 16bit unsigned ints (`raster_constraint_pixel_types(rast) = '{16BUI}'). Any other columns in the table will be ignored. You must furthermore create an index as follows: ``` CREATE INDEX ON secondary_importance USING gist(ST_ConvexHull(gist)) ``` The following raster2pgsql command will create a table from a tiff file that conforms to the requirements: ``` raster2pgsql -I -C -Y -d -t 128x128 input.tiff public.secondary_importance ``` ================================================ FILE: docs/customize/Overview.md ================================================ Nominatim comes with a predefined set of configuration options that should work for most standard installations. If you have special requirements, there are many places where the configuration can be adapted. This chapter describes the following configurable parts: * [Global Settings](Settings.md) has a detailed description of all parameters that can be set in your local `.env` configuration * [Import styles](Import-Styles.md) explains how to write your own import style in order to control what kind of OSM data will be imported * [API Result Formatting](Result-Formatting.md) shows how to change the output of the Nominatim API * [Place ranking](Ranking.md) describes the configuration around classifing places in terms of their importance and their role in an address * [Tokenizers](Tokenizers.md) describes the configuration of the module responsible for analysing and indexing names * [Special Phrases](Special-Phrases.md) are common nouns or phrases that can be used in search to identify a class of places There are also guides for adding the following external data: * [US house numbers from the TIGER dataset](Tiger.md) * [External postcodes](Postcodes.md) ================================================ FILE: docs/customize/Postcodes.md ================================================ # External postcode data Nominatim creates a table of known postcode centroids during import. This table is used for searches of postcodes and for adding postcodes to places where the OSM data does not provide one. These postcode centroids are mainly computed from the OSM data itself. In addition, Nominatim supports reading postcode information from an external CSV file, to supplement the postcodes that are missing in OSM. To enable external postcode support, simply put one CSV file per country into your project directory and name it `_postcodes.csv`. `` must be the two-letter country code for which to apply the file. The file may also be gzipped. Then it must be called `_postcodes.csv.gz`. The CSV file must use commas as a delimiter and have a header line. Nominatim expects three columns to be present: `postcode`, `lat` and `lon`. All other columns are ignored. `lon` and `lat` must describe the x and y coordinates of the postcode centroids in WGS84. The postcode files are loaded only when there is data for the given country in your database. For example, if there is a `us_postcodes.csv` file in your project directory but you import only an excerpt of Italy, then the US postcodes will simply be ignored. As a rule, the external postcode data should be put into the project directory **before** starting the initial import. Still, you can add, remove and update the external postcode data at any time. Simply run: ``` nominatim refresh --postcodes ``` to make the changes visible in your database. Be aware, however, that the changes only have an immediate effect on searches for postcodes. Postcodes that were added to places are only updated, when they are reindexed. That usually happens only during replication updates. ================================================ FILE: docs/customize/Ranking.md ================================================ # Place Ranking in Nominatim Nominatim uses two metrics to rank a place: search rank and address rank. This chapter explains what place ranking means and how it can be customized. ## Search rank The search rank describes the extent and importance of a place. It is used when ranking search results. Simply put, if there are two results for a search query which are otherwise equal, then the result with the _lower_ search rank will be appear higher in the result list. Search ranks are not so important these days because many well-known places use the Wikipedia importance ranking instead. The following table gives an overview of the kind of features that Nominatim expects for each rank: rank | typical place types | extent -------|---------------------------------|------- 1-3 | oceans, continents | - 4 | countries | - 5-9 | states, regions, provinces | - 10-12 | counties | - 13-16 | cities, municipalities, islands | 15 km 17-18 | towns, boroughs | 4 km 19 | villages, suburbs | 2 km 20 | hamlets, farms, neighbourhoods | 1 km 21-25 | isolated dwellings, city blocks | 500 m The extent column describes how far a feature is assumed to reach when it is mapped only as a point. Larger features like countries and states are usually available with their exact area in the OpenStreetMap data. That is why no extent is given. ## Address rank The address rank describes where a place shows up in an address hierarchy. Usually only administrative boundaries and place nodes and areas are eligible to be part of an address. Places that should not appear in the address must have an address rank of 0. The following table gives an overview how ranks are mapped to address parts: rank | address part -------------|------------- 1-3 | _unused_ 4 | country 5-9 | state 10-12 | county 13-16 | city 17-21 | suburb 22-24 | neighbourhood 25 | squares, farms, localities 26-27 | street 28-30 | POI/house number The country rank 4 usually doesn't show up in the address parts of an object. The country is determined indirectly from the country code. Ranks 5-24 can be assigned more or less freely. They make up the major part of the address. Rank 25 is also an addressing rank but it is special because while it can be the parent to a POI with an addr:place of the same name, it cannot be a parent to streets. Use it for place features that are technically on the same level as a street (e.g. squares, city blocks) or for places that should not normally appear in an address unless explicitly tagged so (e.g place=locality which should be uninhabited and as such not addressable). The street ranks 26 and 27 are handled slightly differently. Only one object from these ranks shows up in an address. For POI level objects like shops, buildings or house numbers always use rank 30. Ranks 28 is reserved for house number interpolations. 29 is for internal use only. ## Rank configuration Search and address ranks are assigned to a place when it is first imported into the database. There are a few hard-coded rules for the assignment: * postcodes follow special rules according to their length * boundaries that are not areas and railway=rail are dropped completely * the following are always search rank 30 and address rank 0: * highway nodes * landuse that is not an area Other than that, the ranks can be freely assigned via the JSON file according to their type and the country they are in. The name of the config file to be used can be changed with the setting `NOMINATIM_ADDRESS_LEVEL_CONFIG`. The address level configuration must consist of an array of configuration entries, each containing a tag definition and an optional country array: ``` [ { "tags" : { "place" : { "county" : 12, "city" : 16, }, "landuse" : { "residential" : 22, "" : 30 } } }, { "countries" : [ "ca", "us" ], "tags" : { "boundary" : { "administrative8" : 18, "administrative9" : 20 }, "landuse" : { "residential" : [22, 0] } } } ] ``` The `countries` field contains a list of countries (as ISO 3166-1 alpha 2 code) for which the definition applies. When the field is omitted, then the definition is used as a fallback, when nothing more specific for a given country exists. `tags` contains the ranks for key/value pairs. The ranks can be either a single number, in which case they are the search and address rank, or an array of search and address rank (in that order). The value may be left empty. Then the rank is used when no more specific value is found for the given key. Countries and key/value combination may appear in multiple definitions. Just make sure that each combination of country/key/value appears only once per file. Otherwise the import will fail with a UNIQUE INDEX constraint violation on import. ================================================ FILE: docs/customize/Result-Formatting.md ================================================ # Changing the Appearance of Results in the Server API The Nominatim Server API offers a number of formatting options that present search results in [different output formats](../api/Output.md). These results only contain a subset of all the information that Nominatim has about the result. This page explains how to adapt the result output or add additional result formatting. ## Defining custom result formatting To change the result output, you need to place a file `api/v1/format.py` into your project directory. This file needs to define a single variable `dispatch` containing a [FormatDispatcher](#formatdispatcher). This class serves to collect the functions for formatting the different result types and offers helper functions to apply the formatters. There are two ways to define the `dispatch` variable. If you want to reuse the default output formatting and just make some changes or add an additional format type, then import the dispatch object from the default API: ``` python from nominatim_api.v1.format import dispatch as dispatch ``` If you prefer to define a completely new result output, then you can create an empty dispatcher object: ``` python from nominatim_api import FormatDispatcher dispatch = FormatDispatcher() ``` ## The formatting function The dispatcher organises the formatting functions by format and result type. The format corresponds to the `format` parameter of the API. It can contain one of the predefined format names or you can invent your own new format. API calls return data classes or an array of a data class which represent the result. You need to make sure there are formatters defined for the following result types: * StatusResult (single object, returned by `/status`) * DetailedResult (single object, returned by `/details`) * SearchResults (list of objects, returned by `/search`) * ReverseResults (list of objects, returned by `/reverse` and `/lookup`) * RawDataList (simple object, returned by `/deletable` and `/polygons`) A formatter function has the following signature: ``` python def format_func(result: ResultType, options: Mapping[str, Any]) -> str ``` The options dictionary contains additional information about the original query. See the [reference below](#options-for-different-result-types) about the possible options. To set the result formatter for a certain result type and format, you need to write the format function and decorate it with the [`format_func`](#nominatim_api.FormatDispatcher.format_func) decorator. For example, let us extend the result for the status call in text format and add the server URL. Such a formatter would look like this: ``` python from nominatim_api import StatusResult @dispatch.format_func(StatusResult, 'text') def _format_status_text(result, _): header = 'Status for server nominatim.openstreetmap.org' if result.status: return f"{header}\n\nERROR: {result.message}" return f"{header}\n\nOK" ``` If your dispatcher is derived from the default one, then this definition will overwrite the original formatter function. This way it is possible to customize the output of selected results. ## Adding new formats You may also define a completely different output format. This is as simple as adding formatting functions for all result types using the custom format name: ``` python from nominatim_api import StatusResult @dispatch.format_func(StatusResult, 'chatty') def _format_status_text(result, _): if result.status: return f"The server is currently not running. {result.message}" return "Good news! The server is running just fine." ``` That's all. Nominatim will automatically pick up the new format name and will allow the user to use it. There is no need to implement formatter functions for all the result types, when you invent a new one. The available formats will be determined for each API endpoint separately. To find out which formats are available, you can use the `--list-formats` option of the CLI tool: ``` me@machine:planet-project$ nominatim status --list-formats 2024-08-16 19:54:00: Using project directory: /home/nominatim/planet-project text json chatty debug me@machine:planet-project$ ``` The `debug` format listed in the last line will always appear. It is a special format that enables debug output via the command line (the same as the `debug=1` parameter enables for the server API). To not clash with this built-in function, you shouldn't name your own format 'debug'. ### Content type of new formats All responses will be returned with the content type application/json by default. If your format produces a different content type, you need to configure the content type with the `set_content_type()` function. For example, the 'chatty' format above returns just simple text. So the content type should be set up as: ``` python from nominatim_api.server.content_types import CONTENT_TEXT dispatch.set_content_type('chatty', CONTENT_TEXT) ``` The `content_types` module used above provides constants for the most frequent content types. You set the content type to an arbitrary string, if the content type you need is not available. ## Formatting error messages Any exception thrown during processing of a request is given to a special error formatting function. It takes the requested content type, the status code and the error message. It should return the error message in a form appropriate for the given content type. You can overwrite the default formatting function with the decorator `error_format_func`: ``` python import nominatim_api.server.content_types as ct @dispatch.error_format_func def _format_error(content_type: str, msg: str, status: int) -> str: if content_type == ct.CONTENT_XML: return f""" {msg} """ if content_type == ct.CONTENT_JSON: return f'"{msg}"' return f"ERROR: {msg}" ``` ## Debugging custom formatters The easiest way to try out your custom formatter is by using the Nominatim CLI commands. Custom formats can be chosen with the `--format` parameter: ``` me@machine:planet-project$ nominatim status --format chatty 2024-08-16 19:54:00: Using project directory: /home/nominatim/planet-project Good news! The server is running just fine. me@machine:planet-project$ ``` They will also emit full error messages when there is a problem with the code you need to debug. !!! danger In some cases, when you make an error with your import statement, the CLI will not give you an error but instead tell you, that the API commands are no longer available: me@machine: nominatim status usage: nominatim [-h] [--version] {import,freeze,replication,special-phrases,add-data,index,refresh,admin} ... nominatim: error: argument subcommand: invalid choice: 'status' This happens because the CLI tool is meant to still work when the nominatim-api package is not installed. Import errors involving `nominatim_api` are interpreted as "package not installed". Use the help command to find out which is the offending import that could not be found: me@machine: nominatim -h ... [other help text] ... Nominatim API package not found (was looking for module: nominatim_api.xxx). ## Reference ### FormatDispatcher ::: nominatim_api.FormatDispatcher options: heading_level: 6 group_by_category: False ### JsonWriter ::: nominatim_api.utils.json_writer.JsonWriter options: heading_level: 6 group_by_category: False ### Options for different result types This section lists the options that may be handed in with the different result types in the v1 version of the Nominatim API. #### StatusResult _None._ #### DetailedResult | Option | Description | |-----------------|-------------| | locales | [Locales](../library/Result-Handling.md#locale) object for the requested language(s) | | group_hierarchy | Setting of [group_hierarchy](../api/Details.md#output-details) parameter | | icon_base_url | (optional) URL pointing to icons as set in [NOMINATIM_MAPICON_URL](Settings.md#nominatim_mapicon_url) | #### SearchResults | Option | Description | |-----------------|-------------| | query | Original query string | | more_url | URL for requesting additional results for the same query | | exclude_place_ids | List of IDs already returned (OSM IDs where possible, otherwise `place_id`s) | | viewbox | Setting of [viewbox](../api/Search.md#result-restriction) parameter | | extratags | Setting of [extratags](../api/Search.md#output-details) parameter | | namedetails | Setting of [namedetails](../api/Search.md#output-details) parameter | | addressdetails | Setting of [addressdetails](../api/Search.md#output-details) parameter | #### ReverseResults | Option | Description | |-----------------|-------------| | query | Original query string | | extratags | Setting of [extratags](../api/Search.md#output-details) parameter | | namedetails | Setting of [namedetails](../api/Search.md#output-details) parameter | | addressdetails | Setting of [addressdetails](../api/Search.md#output-details) parameter | #### RawDataList _None._ ================================================ FILE: docs/customize/SQLite.md ================================================ A Nominatim database can be converted into an SQLite database and used as a read-only source for geocoding queries. This sections describes how to create and use an SQLite database. !!! danger This feature is in an experimental state at the moment. Use at your own risk. ## Installing prerequisites To use a SQLite database, you need to install: * SQLite (>= 3.30) * Spatialite (> 5.0.0) * aiosqlite On Ubuntu/Debian, you can run: sudo apt install sqlite3 libsqlite3-mod-spatialite libspatialite7 Install the aiosqlite Python package in your virtual environment: /srv/nominatim-venv/bin/pip install aiosqlite ## Creating a new SQLite database Nominatim cannot import directly into SQLite database. Instead you have to first create a geocoding database in PostgreSQL by running a [regular Nominatim import](../admin/Import.md). Once this is done, the database can be converted to SQLite with nominatim convert -o mydb.sqlite This will create a database where all geocoding functions are available. Depending on what functions you need, the database can be made smaller: * `--without-reverse` omits indexes only needed for reverse geocoding * `--without-search` omit tables and indexes used for forward search * `--without-details` leaves out extra information only available in the details API ## Using an SQLite database Once you have created the database, you can use it by simply pointing the database DSN to the SQLite file: NOMINATIM_DATABASE_DSN=sqlite:dbname=mydb.sqlite Please note that SQLite support is only available for the Python frontend. To use the test server with an SQLite database, you therefore need to switch the frontend engine: nominatim serve --engine falcon You need to install falcon or starlette for this, depending on which engine you choose. The CLI query commands and the library interface already use the new Python frontend and therefore work right out of the box. ================================================ FILE: docs/customize/Settings.md ================================================ This section provides a reference of all configuration parameters that can be used with Nominatim. # Configuring Nominatim Nominatim uses [dotenv](https://github.com/theskumar/python-dotenv) to manage its configuration settings. There are two means to set configuration variables: through an `.env` configuration file or through an environment variable. The `.env` configuration file needs to be placed into the [project directory](../admin/Import.md#creating-the-project-directory). It must contain configuration parameters in `=` format. Please refer to the dotenv documentation for details. The configuration options may also be set in the form of shell environment variables. This is particularly useful, when you want to temporarily change a configuration option. For example, to force the replication serve to download the next change, you can temporarily disable the update interval: NOMINATIM_REPLICATION_UPDATE_INTERVAL=0 nominatim replication --once If a configuration option is defined through .env file and environment variable, then the latter takes precedence. ## Configuration Parameter Reference ### Import and Database Settings #### NOMINATIM_DATABASE_DSN | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Database connection string | | **Format:** | string: `pgsql:=;=;...` | | **Default:** | pgsql:dbname=nominatim | | **After Changes:** | run `nominatim refresh --website` | Sets the connection parameters for the Nominatim database. At a minimum the name of the database (`dbname`) is required. You can set any additional parameter that is understood by libpq. See the [Postgres documentation](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS) for a full list. !!! note It is usually recommended not to set the password directly in this configuration parameter. Use a [password file](https://www.postgresql.org/docs/current/libpq-pgpass.html) instead. #### NOMINATIM_DATABASE_WEBUSER | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Database query user | | **Format:** | string | | **Default:** | www-data | | **After Changes:** | cannot be changed after import | Defines the name of the database user that will run search queries. Usually this is the user under which the webserver is executed. The Postgres user needs to be set up before starting the import. Nominatim grants minimal rights to this user to all tables that are needed for running geocoding queries. #### NOMINATIM_TOKENIZER | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Tokenizer used for normalizing and parsing queries and names | | **Format:** | string | | **Default:** | icu | | **After Changes:** | cannot be changed after import | Sets the tokenizer type to use for the import. For more information on available tokenizers and how they are configured, see [Tokenizers](../customize/Tokenizers.md). #### NOMINATIM_TOKENIZER_CONFIG | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Configuration file for the tokenizer | | **Format:** | path | | **Default:** | _empty_ (default file depends on tokenizer) | | **After Changes:** | see documentation for each tokenizer | Points to the file with additional configuration for the tokenizer. See the [Tokenizer](../customize/Tokenizers.md) descriptions for details on the file format. If a relative path is given, then the file is searched first relative to the project directory and then in the global settings directory. #### NOMINATIM_LIMIT_REINDEXING | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Avoid invalidating large areas | | **Format:** | bool | | **Default:** | yes | Nominatim computes the address of each place at indexing time. This has the advantage to make search faster but also means that more objects needs to be invalidated when the data changes. For example, changing the name of the state of Florida would require recomputing every single address point in the state to make the new name searchable in conjunction with addresses. Setting this option to 'yes' means that Nominatim skips reindexing of contained objects when the area becomes too large. #### NOMINATIM_LANGUAGES | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Restrict search languages | | **Format:** | string: comma-separated list of language codes | | **Default:** | _empty_ | Normally Nominatim will include all language variants of name:XX in the search index. Set this to a comma separated list of language codes, to restrict import to a subset of languages. Currently only affects the initial import of country names and special phrases. #### NOMINATIM_USE_US_TIGER_DATA | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Enable searching for Tiger house number data | | **Format:** | boolean | | **Default:** | no | | **After Changes:** | run `nominatim refresh --functions` | When this setting is enabled, search and reverse queries also take data from [Tiger house number data](Tiger.md) into account. #### NOMINATIM_OSM2PGSQL_BINARY | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Location of the osm2pgsql binary | | **Format:** | path | | **Default:** | _empty_ (use binary shipped with Nominatim) | | **Comment:** | EXPERT ONLY | Nominatim uses [osm2pgsql](https://osm2pgsql.org) to load the OSM data initially into the database. Nominatim comes bundled with a version of osm2pgsql that is guaranteed to be compatible. Use this setting to use a different binary instead. You should do this only when you know exactly what you are doing. If the osm2pgsql version is not compatible, then the result is undefined. #### NOMINATIM_WIKIPEDIA_DATA_PATH | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Directory with the wikipedia importance data | | **Format:** | path | | **Default:** | _empty_ (project directory) | Set a custom location for the [wikipedia ranking file](../admin/Import.md#wikipediawikidata-rankings). When unset, Nominatim expects the data to be saved in the project directory. #### NOMINATIM_ADDRESS_LEVEL_CONFIG | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Configuration file for rank assignments | | **Format:** | path | | **Default:** | address-levels.json | The _address level configuration_ defines the rank assignments for places. See [Place Ranking](Ranking.md) for a detailed explanation what rank assignments are and what the configuration file must look like. When a relative path is given, then the file is searched first relative to the project directory and then in the global settings directory. #### NOMINATIM_IMPORT_STYLE | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Configuration to use for the initial OSM data import | | **Format:** | string or path | | **Default:** | extratags | The _style configuration_ describes which OSM objects and tags are taken into consideration for the search database. Nominatim comes with a set of pre-configured styles, that may be configured here. You can also write your own custom style and point the setting to the file with the style. When a relative path is given, then the style file is searched first relative to the project directory and then in the global settings directory. See [Import Styles](Import-Styles.md) for more information on the available internal styles and the format of the configuration file. #### NOMINATIM_FLATNODE_FILE | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Location of osm2pgsql flatnode file | | **Format:** | path | | **Default:** | _empty_ (do not use a flatnote file) | | **After Changes:** | Only change when moving the file physically. | The `osm2pgsql flatnode file` is file that efficiently stores geographic location for OSM nodes. For larger imports it can significantly speed up the import. When this option is unset, then osm2pgsql uses a PsotgreSQL table to store the locations. When a relative path is given, then the flatnode file is created/searched relative to the project directory. !!! warning The flatnode file is not only used during the initial import but also when adding new data with `nominatim add-data` or `nominatim replication`. Make sure you keep the flatnode file around and this setting unmodified, if you plan to add more data or run regular updates. #### NOMINATIM_TABLESPACE_* | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Group of settings for distributing the database over tablespaces | | **Format:** | string | | **Default:** | _empty_ (do not use a table space) | | **After Changes:** | no effect after initial import | Nominatim allows to distribute the search database over up to 10 different [PostgreSQL tablespaces](https://www.postgresql.org/docs/current/manage-ag-tablespaces.html). If you use this option, make sure that the tablespaces exist before starting the import. The available tablespace groups are: NOMINATIM_TABLESPACE_SEARCH_DATA : Data used by the geocoding frontend. NOMINATIM_TABLESPACE_SEARCH_INDEX : Indexes used by the geocoding frontend. NOMINATIM_TABLESPACE_OSM_DATA : Raw OSM data cache used for import and updates. NOMINATIM_TABLESPACE_OSM_INDEX : Indexes on the raw OSM data cache. NOMINATIM_TABLESPACE_PLACE_DATA : Data table with the pre-filtered but still unprocessed OSM data. Used only during imports and updates. NOMINATIM_TABLESPACE_PLACE_INDEX : Indexes on raw data table. Used only during imports and updates. NOMINATIM_TABLESPACE_ADDRESS_DATA : Data tables used for computing search terms and addresses of places during import and updates. NOMINATIM_TABLESPACE_ADDRESS_INDEX : Indexes on the data tables for search term and address computation. Used only for import and updates. NOMINATIM_TABLESPACE_AUX_DATA : Auxiliary data tables for non-OSM data, e.g. for Tiger house number data. NOMINATIM_TABLESPACE_AUX_INDEX : Indexes on auxiliary data tables. ### Replication Update Settings #### NOMINATIM_REPLICATION_URL | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Base URL of the replication service | | **Format:** | url | | **Default:** | https://planet.openstreetmap.org/replication/minute | | **After Changes:** | run `nominatim replication --init` | Replication services deliver updates to OSM data. Use this setting to choose which replication service to use. See [Updates](../admin/Update.md) for more information on how to set up regular updates. #### NOMINATIM_REPLICATION_MAX_DIFF | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Maximum amount of data to download per update cycle (in MB) | | **Format:** | integer | | **Default:** | 50 | | **After Changes:** | restart the replication process | At each update cycle Nominatim downloads diffs until either no more diffs are available on the server (i.e. the database is up-to-date) or the limit given in this setting is exceeded. Nominatim guarantees to downloads at least one diff, if one is available, no matter how small the setting. The default for this setting is fairly conservative because Nominatim keeps all data downloaded in one cycle in RAM. Using large values in a production server may interfere badly with the search frontend because it evicts data from RAM that is needed for speedy answers to incoming requests. It is usually a better idea to keep this setting lower and run multiple update cycles to catch up with updates. When catching up in non-production mode, for example after the initial import, the setting can easily be changed temporarily on the command line: NOMINATIM_REPLICATION_MAX_DIFF=3000 nominatim replication #### NOMINATIM_REPLICATION_UPDATE_INTERVAL | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Publication interval of the replication service (in seconds) | | **Format:** | integer | | **Default:** | 75 | | **After Changes:** | restart the replication process | This setting determines when Nominatim will attempt to download again a new update. The time is computed from the publication date of the last diff downloaded. Setting this to a slightly higher value than the actual publication interval avoids unnecessary rechecks. #### NOMINATIM_REPLICATION_RECHECK_INTERVAL | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Wait time to recheck for a pending update (in seconds) | | **Format:** | integer | | **Default:** | 60 | | **After Changes:** | restart the replication process | When replication updates are run in continuous mode (using `nominatim replication`), this setting determines how long Nominatim waits until it looks for updates again when updates were not available on the server. Note that this is different from [NOMINATIM_REPLICATION_UPDATE_INTERVAL](#nominatim_replication_update_interval). Nominatim will never attempt to query for new updates for UPDATE_INTERVAL seconds after the current database date. Only after the update interval has passed it asks for new data. If then no new data is found, it waits for RECHECK_INTERVAL seconds before it attempts again. ### API Settings #### NOMINATIM_CORS_NOACCESSCONTROL | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Send permissive CORS access headers | | **Format:** | boolean | | **Default:** | yes | | **After Changes:** | run `nominatim refresh --website` | When this setting is enabled, API HTTP responses include the HTTP [CORS](https://en.wikipedia.org/wiki/CORS) headers `access-control-allow-origin: *` and `access-control-allow-methods: OPTIONS,GET`. #### NOMINATIM_MAPICON_URL | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | URL prefix for static icon images | | **Format:** | url | | **Default:** | _empty_ | | **After Changes:** | run `nominatim refresh --website` | When a mapicon URL is configured, then Nominatim includes an additional `icon` field in the responses, pointing to an appropriate icon for the place type. Map icons used to be included in Nominatim itself but now have moved to the [nominatim-ui](https://github.com/osm-search/nominatim-ui/) project. If you want the URL to be included in API responses, make the `/mapicon` directory of the project available under a public URL and point this setting to the directory. #### NOMINATIM_DEFAULT_LANGUAGE | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Language of responses when no language is requested | | **Format:** | language code | | **Default:** | _empty_ (use the local language of the feature) | | **After Changes:** | run `nominatim refresh --website` | Nominatim localizes the place names in responses when the corresponding translation is available. Users can request a custom language setting through the HTTP accept-languages header or through the explicit parameter [accept-languages](../api/Search.md#language-of-results). If neither is given, it falls back to this setting. If the setting is also empty, then the local languages (in OSM: the name tag without any language suffix) is used. #### NOMINATIM_LOOKUP_MAX_COUNT | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Maximum number of OSM ids accepted by /lookup | | **Format:** | integer | | **Default:** | 50 | | **After Changes:** | run `nominatim refresh --website` | The /lookup point accepts list of ids to look up address details for. This setting restricts the number of places a user may look up with a single request. #### NOMINATIM_POLYGON_OUTPUT_MAX_TYPES | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Number of different geometry formats that may be returned | | **Format:** | integer | | **Default:** | 1 | | **After Changes:** | run `nominatim refresh --website` | Nominatim supports returning full geometries of places. The geometries may be requested in different formats with one of the [`polygon_*` parameters](../api/Search.md#polygon-output). Use this setting to restrict the number of geometry types that may be requested with a single query. Setting this parameter to 0 disables polygon output completely. #### NOMINATIM_SEARCH_WITHIN_COUNTRIES | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Disable search for elements that are not in the country grid | | **Format:** | boolean | | **Default:** | no | | **After Changes:** | run `nominatim refresh --website` | Enable to search elements just within countries. When enabled, if, despite not finding a point within the static grid of countries, it finds a geometry of a region, do not return the geometry. Return "Unable to geocode" instead. #### NOMINATIM_SERVE_LEGACY_URLS | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Enable serving via URLs with a .php suffix | | **Format:** | boolean | | **Default:** | yes | | **Comment:** | Python frontend only | When enabled, then endpoints are reachable as `/` as well as `/.php`. This can be useful when you want to be backwards-compatible with previous versions of Nominatim. #### NOMINATIM_API_POOL_SIZE | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Number of parallel database connections per worker | | **Format:** | number | | **Default:** | 10 | | **Comment:** | Python frontend only | Sets the maximum number of database connections available for a single instance of Nominatim. When configuring the maximum number of connections that your PostgreSQL database can handle, you need at least `NOMINATIM_API_POOL_SIZE` * `` connections. For configuring the number of workers, refer to the section about [Deploying the Python frontend](../admin/Deployment-Python.md). #### NOMINATIM_QUERY_TIMEOUT | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Timeout for SQL queries to the database | | **Format:** | number (seconds) | | **Default:** | 10 | | **Comment:** | Python frontend only | When this timeout is set, then all SQL queries that run longer than the specified numbers of seconds will be cancelled and the user receives a timeout exceptions. Users of the API see a 503 HTTP error. The timeout does ont apply when using the [low-level DB access](../library/Low-Level-DB-Access.md) of the library. A timeout can be manually set, if required. #### NOMINATIM_REQUEST_TIMEOUT | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Timeout for search queries | | **Format:** | number (seconds) | | **Default:** | 60 | | **Comment:** | Python frontend only | When this timeout is set, a search query will finish sending queries to the database after the timeout has passed and immediately return the 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_FILE | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Log requests into a file | | **Format:** | path | | **Default:** | _empty_ (logging disabled) | Enable logging of requests into a file with this setting by setting the log file where to log to. A relative file name is assumed to be relative to the project directory. The format of the log output can be set with NOMINATIM_LOG_FORMAT. #### NOMINATIM_LOG_FORMAT | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Log requests into a file | | **Format:** | [Python String Format](https://docs.python.org/3/library/string.html#formatstrings) string | | **Default:** | `[{start}] {total_time:.4f} {results_total} {endpoint} "{query_string}"` | Describes the content of a log line for a single request. The format must be readable by Python's format function. Nominatim provides a number of metrics than can be logged. The default set of metrics is the following: /// html | div.simple-table | name | type | Description | | --------------- | ------ | ------------| | start | time | Point in time when the request arrived. | | end | time | Point in time when the request was done. | | query_start | time | Point in time when processing started. | | total_time | float | Total time in seconds to handle the request. | | wait_time | float | Time in seconds the request waited for a database connection to be available. | | query_time | float | Total time in seconds to process the request once a connection was available. | | results_total | int | Number of results found. | | endpoint | string | API endpoint used. | | query_string | string | Raw query string received. | /// Variables of type 'time' contain a UTC timestamp string in ISO format. Nominatim also exposes additional metrics to help with development. These are subject to change between versions: /// html | div.simple-table | name | type | Description | | ------------------------- | ------ | ------------| | search_rounds | int | Total number of searches executed for the request. | | search_min_penalty | float | Minimal possible penalty for the request. | | search_first_result_round | int | Number of first search to yield any result. | | search_min_result_penalty | float | Minimal penalty by a result found. | | search_best_penalty_round | int | Search round that yielded the best penalty result. | /// #### NOMINATIM_DEBUG_SQL | Summary | | | -------------- | --------------------------------------------------- | | **Description:** | Enable printing of raw SQL by SQLAlchemy | | **Format:** | boolean | | **Default:** | no | | **Comment:** | **For developers only.** | This settings enables [SQL debugging](https://docs.sqlalchemy.org/en/20/core/engines.html#dbengine-logging) by SQLAlchemy. This can be helpful when debugging some bugs with internal query handling. It should only be used together with the CLI query functions. Enabling it for server mode may have unintended consequences. Use the `debug` parameter instead, which prints information on how the search is executed including SQL statements. ================================================ FILE: docs/customize/Special-Phrases.md ================================================ # Special phrases ## Importing OSM user-maintained special phrases As described in the [Import section](../admin/Import.md), it is possible to import special phrases from the wiki with the following command: ```sh nominatim special-phrases --import-from-wiki ``` ## Importing custom special phrases Special phrases may also be imported from any custom CSV file. The file needs to have a header line, use comma as delimiter and define the following columns: * **phrase**: the keyword to look for * **class**: key of the main tag of the place to find (see [Import styles](Import-Styles.md#how-processing-works) * **type**: value of the main tag * **operator**: type of special phrase, may be one of: * *in*: place is within the place defined by the search term (e.g. "_Hotels in_ Berlin") * *near*: place is near the place defined by the search term (e.g. "_bus stops near_ Big Ben") * *named*: special phrase is a classifier (e.g. "_hotel_ California") * *-*: unspecified, can be any of the above If the file contains any other columns, then they are silently ignored To import the CSV file, use the following command: ```sh nominatim special-phrases --import-from-csv ``` Note that the two previous import commands will update the phrases from your database. This means that if you import some phrases from a CSV file, only the phrases present in the CSV file will be kept in the database. All other phrases will be removed. If you want to only add new phrases and not update the other ones you can add the argument `--no-replace` to the import command. For example: ```sh nominatim special-phrases --import-from-csv --no-replace ``` This will add the phrases present in the CSV file into the database without removing the other ones. ================================================ FILE: docs/customize/Tiger.md ================================================ # Installing TIGER housenumber data for the US Nominatim is able to use the official [TIGER](https://www.census.gov/geographies/mapping-files/time-series/geo/tiger-line-file.html) address set to complement the OSM house number data in the US. You can add TIGER data to your own Nominatim instance by following these steps. The entire US adds about 10GB to your database. 1. Get preprocessed TIGER data: cd $PROJECT_DIR wget https://nominatim.org/data/tiger-nominatim-preprocessed-latest.csv.tar.gz 2. Import the data into your Nominatim database: nominatim add-data --tiger-data tiger-nominatim-preprocessed-latest.csv.tar.gz 3. Enable use of the Tiger data in your existing `.env` file by adding: echo NOMINATIM_USE_US_TIGER_DATA=yes >> .env 4. Apply the new settings: nominatim refresh --functions --website See the [TIGER-data project](https://github.com/osm-search/TIGER-data) for more information on how the data got preprocessed. ================================================ FILE: docs/customize/Tokenizers.md ================================================ # Tokenizers The tokenizer module in Nominatim is responsible for analysing the names given to OSM objects and the terms of an incoming query in order to make sure, they can be matched appropriately. Nominatim currently offers only one tokenizer module, the ICU tokenizer. This section describes the tokenizer and how it can be configured. !!! important The selection of tokenizer is tied to a database installation. You need to choose and configure the tokenizer before starting the initial import. Once the import is done, you cannot switch to another tokenizer anymore. Reconfiguring the chosen tokenizer is very limited as well. See the comments in each tokenizer section. ## ICU tokenizer The ICU tokenizer uses the [ICU library](http://site.icu-project.org/) to normalize names and queries. It also offers configurable decomposition and abbreviation handling. This tokenizer is currently the default. To enable the tokenizer add the following line to your project configuration: ``` NOMINATIM_TOKENIZER=icu ``` ### How it works On import the tokenizer processes names in the following three stages: 1. During the **Sanitizer step** incoming names are cleaned up and converted to **full names**. This step can be used to regularize spelling, split multi-name tags into their parts and tag names with additional attributes. See the [Sanitizers section](#sanitizers) below for available cleaning routines. 2. The **Normalization** part removes all information from the full names that are not relevant for search. 3. The **Token analysis** step takes the normalized full names and creates all transliterated variants under which the name should be searchable. See the [Token analysis](#token-analysis) section below for more information. During query time, the tokeinzer is responsible for processing incoming queries. This happens in two stages: 1. During **query preprocessing** the incoming text is split into name chunks and normalised. This usually means applying the same normalisation as during the import process but may involve other processing like, for example, word break detection. 2. The **token analysis** step breaks down the query parts into tokens, looks them up in the database and assigns them possible functions and probabilities. Query processing can be further customized while the rest of the analysis is hard-coded. ### Configuration The ICU tokenizer is configured using a YAML file which can be configured using `NOMINATIM_TOKENIZER_CONFIG`. The configuration is read on import and then saved as part of the internal database status. Later changes to the variable have no effect. Here is an example configuration file: ``` yaml query-preprocessing: - step: split_japanese_phrases - step: regex_replace replacements: - pattern: https?://[^\s]* # Filter URLs starting with http or https replace: '' - step: normalize normalization: - ":: lower ()" - "ß > 'ss'" # German szet is unambiguously equal to double ss transliteration: - !include /etc/nominatim/icu-rules/extended-unicode-to-asccii.yaml - ":: Ascii ()" sanitizers: - step: split-name-list token-analysis: - analyzer: generic variants: - !include icu-rules/variants-ca.yaml - words: - road -> rd - bridge -> bdge,br,brdg,bri,brg mutations: - pattern: 'ä' replacements: ['ä', 'ae'] ``` The configuration file contains five sections: `query-preprocessing`, `normalization`, `transliteration`, `sanitizers` and `token-analysis`. #### Query preprocessing The section for `query-preprocessing` defines an ordered list of functions that are applied to the query before the token analysis. The following is a list of preprocessors that are shipped with Nominatim. ##### normalize ::: nominatim_api.query_preprocessing.normalize options: members: False heading_level: 6 docstring_section_style: spacy ##### regex-replace ::: nominatim_api.query_preprocessing.regex_replace options: members: False heading_level: 6 docstring_section_style: spacy description: This option runs any given regex pattern on the input and replaces values accordingly replacements: - pattern: regex pattern replace: string to replace with #### Normalization and Transliteration The normalization and transliteration sections each define a set of ICU rules that are applied to the names. The **normalization** rules are applied after sanitation. They should remove any information that is not relevant for search at all. Usual rules to be applied here are: lower-casing, removing of special characters, cleanup of spaces. The **transliteration** rules are applied at the end of the tokenization process to transfer the name into an ASCII representation. Transliteration can be useful to allow for further fuzzy matching, especially between different scripts. Each section must contain a list of [ICU transformation rules](https://unicode-org.github.io/icu/userguide/transforms/general/rules.html). The rules are applied in the order in which they appear in the file. You can also include additional rules from external yaml file using the `!include` tag. The included file must contain a valid YAML list of ICU rules and may again include other files. !!! warning The ICU rule syntax contains special characters that conflict with the YAML syntax. You should therefore always enclose the ICU rules in double-quotes. #### Sanitizers The sanitizers section defines an ordered list of functions that are applied to the name and address tags before they are further processed by the tokenizer. They allows to clean up the tagging and bring it to a standardized form more suitable for building the search index. !!! hint Sanitizers only have an effect on how the search index is built. They do not change the information about each place that is saved in the database. In particular, they have no influence on how the results are displayed. The returned results always show the original information as stored in the OpenStreetMap database. Each entry contains information of a sanitizer to be applied. It has a mandatory parameter `step` which gives the name of the sanitizer. Depending on the type, it may have additional parameters to configure its operation. The order of the list matters. The sanitizers are applied exactly in the order that is configured. Each sanitizer works on the results of the previous one. The following is a list of sanitizers that are shipped with Nominatim. ##### split-name-list ::: nominatim_db.tokenizer.sanitizers.split_name_list options: members: False heading_level: 6 docstring_section_style: spacy ##### strip-brace-terms ::: nominatim_db.tokenizer.sanitizers.strip_brace_terms options: members: False heading_level: 6 docstring_section_style: spacy ##### tag-analyzer-by-language ::: nominatim_db.tokenizer.sanitizers.tag_analyzer_by_language options: members: False heading_level: 6 docstring_section_style: spacy ##### clean-housenumbers ::: nominatim_db.tokenizer.sanitizers.clean_housenumbers options: members: False heading_level: 6 docstring_section_style: spacy ##### clean-postcodes ::: nominatim_db.tokenizer.sanitizers.clean_postcodes options: members: False heading_level: 6 docstring_section_style: spacy ##### clean-tiger-tags ::: nominatim_db.tokenizer.sanitizers.clean_tiger_tags options: members: False heading_level: 6 docstring_section_style: spacy #### delete-tags ::: nominatim_db.tokenizer.sanitizers.delete_tags options: members: False heading_level: 6 docstring_section_style: spacy #### tag-japanese ::: nominatim_db.tokenizer.sanitizers.tag_japanese options: members: False heading_level: 6 docstring_section_style: spacy #### Token Analysis Token analyzers take a full name and transform it into one or more normalized form that are then saved in the search index. In its simplest form, the analyzer only applies the transliteration rules. More complex analyzers create additional spelling variants of a name. This is useful to handle decomposition and abbreviation. The ICU tokenizer may use different analyzers for different names. To select the analyzer to be used, the name must be tagged with the `analyzer` attribute by a sanitizer (see for example the [tag-analyzer-by-language sanitizer](#tag-analyzer-by-language)). The token-analysis section contains the list of configured analyzers. Each analyzer must have an `id` parameter that uniquely identifies the analyzer. The only exception is the default analyzer that is used when no special analyzer was selected. There are analysers with special ids: * '@housenumber'. If an analyzer with that name is present, it is used for normalization of house numbers. * '@potcode'. If an analyzer with that name is present, it is used for normalization of postcodes. Different analyzer implementations may exist. To select the implementation, the `analyzer` parameter must be set. The different implementations are described in the following. ##### Generic token analyzer The generic analyzer `generic` is able to create variants from a list of given abbreviation and decomposition replacements and introduce spelling variations. ###### Variants The optional 'variants' section defines lists of replacements which create alternative spellings of a name. To create the variants, a name is scanned from left to right and the longest matching replacement is applied until the end of the string is reached. The variants section must contain a list of replacement groups. Each group defines a set of properties that describes where the replacements are applicable. In addition, the word section defines the list of replacements to be made. The basic replacement description is of the form: ``` [,[...]] => [,[...]] ``` The left side contains one or more `source` terms to be replaced. The right side lists one or more replacements. Each source is replaced with each replacement term. !!! tip The source and target terms are internally normalized using the normalization rules given in the configuration. This ensures that the strings match as expected. In fact, it is better to use unnormalized words in the configuration because then it is possible to change the rules for normalization later without having to adapt the variant rules. ###### Decomposition In its standard form, only full words match against the source. There is a special notation to match the prefix and suffix of a word: ``` yaml - ~strasse => str # matches "strasse" as full word and in suffix position - hinter~ => hntr # matches "hinter" as full word and in prefix position ``` There is no facility to match a string in the middle of the word. The suffix and prefix notation automatically trigger the decomposition mode: two variants are created for each replacement, one with the replacement attached to the word and one separate. So in above example, the tokenization of "hauptstrasse" will create the variants "hauptstr" and "haupt str". Similarly, the name "rote strasse" triggers the variants "rote str" and "rotestr". By having decomposition work both ways, it is sufficient to create the variants at index time. The variant rules are not applied at query time. To avoid automatic decomposition, use the '|' notation: ``` yaml - ~strasse |=> str ``` simply changes "hauptstrasse" to "hauptstr" and "rote strasse" to "rote str". ###### Initial and final terms It is also possible to restrict replacements to the beginning and end of a name: ``` yaml - ^south => s # matches only at the beginning of the name - road$ => rd # matches only at the end of the name ``` So the first example would trigger a replacement for "south 45th street" but not for "the south beach restaurant". ###### Replacements vs. variants The replacement syntax `source => target` works as a pure replacement. It changes the name instead of creating a variant. To create an additional version, you'd have to write `source => source,target`. As this is a frequent case, there is a shortcut notation for it: ``` [,[...]] -> [,[...]] ``` The simple arrow causes an additional variant to be added. Note that decomposition has an effect here on the source as well. So a rule ``` yaml - "~strasse -> str" ``` means that for a word like `hauptstrasse` four variants are created: `hauptstrasse`, `haupt strasse`, `hauptstr` and `haupt str`. ###### Mutations The 'mutation' section in the configuration describes an additional set of replacements to be applied after the variants have been computed. Each mutation is described by two parameters: `pattern` and `replacements`. The pattern must contain a single regular expression to search for in the variant name. The regular expressions need to follow the syntax for [Python regular expressions](file:///usr/share/doc/python3-doc/html/library/re.html#regular-expression-syntax). Capturing groups are not permitted. `replacements` must contain a list of strings that the pattern should be replaced with. Each occurrence of the pattern is replaced with all given replacements. Be mindful of combinatorial explosion of variants. ###### Modes The generic analyser supports a special mode `variant-only`. When configured then it consumes the input token and emits only variants (if any exist). Enable the mode by adding: ``` mode: variant-only ``` to the analyser configuration. ##### Housenumber token analyzer The analyzer `housenumbers` is purpose-made to analyze house numbers. It creates variants with optional spaces between numbers and letters. Thus, house numbers of the form '3 a', '3A', '3-A' etc. are all considered equivalent. The analyzer cannot be customized. ##### Postcode token analyzer The analyzer `postcodes` is pupose-made to analyze postcodes. It supports a 'lookup' variant of the token, which produces variants with optional spaces. Use together with the clean-postcodes sanitizer. The analyzer cannot be customized. ### Reconfiguration Changing the configuration after the import is currently not possible, although this feature may be added at a later time. ================================================ FILE: docs/develop/Database-Layout.md ================================================ # Database Layout ### Import tables OSM data is initially imported using [osm2pgsql](https://osm2pgsql.org). Nominatim uses a custom flex style to create the initial import tables. The import process creates the following tables: ![osm2pgsql tables](osm2pgsql-tables.svg) The `planet_osm_*` tables are the usual backing tables for OSM data. Note that Nominatim uses them to look up special relations and to find nodes on ways. Apart from those the osm2pgsql import produces three tables as output. The **place_postcode** table collects postcode information that is not already present on an object in the place table. That is for one thing [postcode area relations](https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dpostal_code) and for another objects with a postcode tag but no other tagging that qualifies it for inclusion into the geocoding database. The table has the following fields: * `osm_type` - kind of OSM object (**N** - node, **W** - way, **R** - relation) * `osm_id` - original OSM ID * `postcode` - postcode as extacted from the `postcal_code` tag * `country_code` - computed country code for this postcode. This field functions as a cache and is only computed when the table is used for the computation of the final postcodes. * `centroid` - centroid of the object * `geometry` - the full geometry of the area for postcode areas only The **place_interpolation** table holds all [address interpolation lines](https://wiki.openstreetmap.org/wiki/Addresses#Interpolation) and has the following fields: * `osm_id` - original OSM ID * `type` - type of interpolation as extracted from the `addr:interpolation` tag * `address` - any other `addr:*` tags * `nodes` - list of OSM nodes contained in this interpolation, needed to compute the involved housenumbers later * `geometry` - the linestring for the interpolation (in WSG84) The **place** table holds all other OSM object that are interesting and has the following fields: * `osm_type` - kind of OSM object (**N** - node, **W** - way, **R** - relation) * `osm_id` - original OSM ID * `class` - key of principal tag defining the object type * `type` - value of principal tag defining the object type * `name` - collection of tags that contain a name or reference * `admin_level` - numerical value of the tagged administrative level * `address` - collection of tags defining the address of an object * `extratags` - collection of additional interesting tags that are not directly relevant for searching * `geometry` - geometry of the object (in WGS84) A single OSM object may appear multiple times in this table when it is tagged with multiple tags that may constitute a principal tag. Take for example a motorway bridge. In OSM, this would be a way which is tagged with `highway=motorway` and `bridge=yes`. This way would appear in the `place` table once with `class` of `highway` and once with a `class` of `bridge`. Thus the *unique key* for `place` is (`osm_type`, `osm_id`, `class`). How raw OSM tags are mapped to the columns in the place table is to a certain degree configurable. See [Customizing Import Styles](../customize/Import-Styles.md) for more information. ### Search tables The following tables carry all information needed to do the search: ![search tables](search-tables.svg) The **placex** table is the central table that saves all information about the searchable places in Nominatim. In simpler terms, the `placex` table can be seen as the final, processed version of OSM data that is ready for search. While the `place` table contains raw imported data, `placex` stores enriched and indexed data that includes ranking, hierarchy (parent-child relationships), and computed metadata such as importance and postcode. Most search queries in Nominatim ultimately read from this table, making it the core table for forward and reverse geocoding. The basic columns are the same as for the place table and have the same meaning. The placex tables adds the following additional columns: * `place_id` - the internal unique ID to identify the place * `partition` - the id to use with partitioned tables (see below) * `geometry_sector` - a location hash used for geographically close ordering * `parent_place_id` - the next higher place in the address hierarchy, only relevant for POI-type places (with rank 30) * `linked_place_id` - place ID of the place this object has been merged with. When this ID is set, then the place is invisible for search. * `importance` - measure how well known the place is * `rank_search`, `rank_address` - search and address rank (see [Customizing ranking](../customize/Ranking.md) * `wikipedia` - the wikipedia page used for computing the importance of the place * `country_code` - the country the place is located in * `housenumber` - normalized housenumber, if the place has one * `postcode` - computed postcode for the place * `indexed_status` - processing status of the place (0 - ready, 1 - freshly inserted, 2 - needs updating, 100 - needs deletion) * `indexed_date` - timestamp when the place was processed last * `centroid` - a point feature for the place * `token_info` - a dummy field used to inject information from the tokenizer into the indexing process For implementation details, see the SQL definition in `lib-sql/tables/placex.sql` and the SQLAlchemy schema in `src/nominatim_api/sql/sqlalchemy_schema.py`. The **location_property_osmline** table is a special table for [address interpolations](https://wiki.openstreetmap.org/wiki/Addresses#Using_interpolation). The columns have the same meaning and use as the columns with the same name in the placex table. Only the following columns are special: * `startnumber`, `endnumber` and `step` - beginning and end of the number range for the interpolation and the increment steps * `type` - a string to indicate the interval between the numbers as imported from the OSM `addr:interpolation` tag; valid values are `odd`, `even`, `all` or a single digit number; interpolations with other values are silently dropped Address interpolations are always ways in OSM, which is why there is no column `osm_type`. The **location_postcodes** table holds computed postcode assembled from the postcode information available in OSM. When a postcode has a postcode area relation, then the table stores its full geometry. For all other postcode the centroid is computed using the position of all OSM object that reference the same postoce. The `osm_id` field can be used to distinguish the two. When set, it refers to the OSM relation with the postcode area. The meaning of the columns in the table is again the same as that of the placex table. Every place needs an address, a set of surrounding places that describe the location of the place. The set of address places is made up of OSM places themselves. The **place_addressline** table cross-references for each place all the places that make up its address. Two columns define the address relation: * `place_id` - reference to the place being addressed * `address_place_id` - reference to the place serving as an address part The most of the columns cache information from the placex entry of the address part. The exceptions are: * `fromarea` - is true if the address part has an area geometry and can therefore be considered preceise * `isaddress` - is true if the address part should show up in the address output. Sometimes there are multiple places competing for for same address type (e.g. multiple cities) and this field resolves the tie. The **search_name** table contains the search index proper. It saves for each place the terms with which the place can be found. The terms are split into the name itself and all terms that make up the address. The table mirrors some of the columns from placex for faster lookup. Search terms are not saved as strings. Each term is assigned an integer and those integers are saved in the name and address vectors of the search_name table. The **word** table serves as the lookup table from string to such a word ID. The exact content of the word table depends on the [tokenizer](Tokenizers.md) used. ## Address computation tables Next to the main search tables, there is a set of secondary helper tables used to compute the address relations between places. These tables are partitioned. Each country is assigned a partition number in the country_name table (see below) and the data is then split between a set of tables, one for each partition. Note that Nominatim still manually manages partitioned tables. Native support for partitions in PostgreSQL only became usable with version 13. It will be a little while before Nominatim drops support for older versions. ![address tables](address-tables.svg) The **search_name_X** tables are used to look up streets that appear in the `addr:street` tag. The **location_area_large_X** tables are used to look up larger areas (administrative boundaries and place nodes) either through their geographic closeness or through `addr:*` entries. The **location_road_X** tables are used to find the closest street for a dependent place. All three table cache specific information from the placex table for their selected subset of places: * `keywords` and `name_vector` contain lists of term ids (from the word table) that the full name of the place should match against * `isguess` is true for places that are not described by an area All other columns reflect their counterpart in the placex table. ## Static data tables Nominatim also creates a number of static tables at import: * `nominatim_properties` saves settings that must not be changed after import * `address_levels` save the rank information from the [ranking configuration](../customize/Ranking.md) * `country_name` contains a fallback of names for all countries, their default languages and saves the assignment of countries to partitions. * `country_osm_grid` provides a fallback for country geometries ## Auxiliary data tables Finally there are some table for auxiliary data: * `location_property_tiger` - saves housenumber from the Tiger import. Its layout is similar to that of `location_propoerty_osmline`. * `place_class_*` tables are helper tables to facilitate lookup of POIs by their class and type. They exist because it is not possible to create combined indexes with geometries. ================================================ FILE: docs/develop/Development-Environment.md ================================================ # Setting up Nominatim for Development This chapter gives an overview how to set up Nominatim for development and how to run tests. !!! Important This guide assumes you develop under the latest version of Debian/Ubuntu. You can of course also use your favourite distribution. You just might have to adapt the commands below slightly, in particular the commands for installing additional software. ## Installing Nominatim The first step is to install Nominatim itself. Please follow the installation instructions in the [Admin section](../admin/Installation.md). You don't need to set up a webserver for development, the webserver that can be started via `nominatim serve` is sufficient. If you want to run Nominatim in a VM via Vagrant, use the default `ubuntu24` setup. Vagrant's libvirt provider runs out-of-the-box under Ubuntu. You also need to install an NFS daemon to enable directory sharing between host and guest. The following packages should get you started: sudo apt install vagrant vagrant-libvirt libvirt-daemon nfs-kernel-server ## Prerequisites for testing and documentation The Nominatim test suite consists of behavioural tests (using pytest-bdd) and unit tests (using pytest). It has the following additional requirements: * [flake8](https://flake8.pycqa.org/en/stable/) (CI always runs the latest version from pip) * [mypy](http://mypy-lang.org/) (plus typing information for external libs) * [Python Typing Extensions](https://github.com/python/typing_extensions) (for Python < 3.9) * [pytest](https://pytest.org) * [pytest-asyncio](https://pytest-asyncio.readthedocs.io) * [pytest-bdd](https://pytest-bdd.readthedocs.io) For testing the Python search frontend, you need to install extra dependencies depending on your choice of webserver framework: * [httpx](https://www.python-httpx.org/) (Starlette only) * [asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan) (Starlette only) The documentation is built with mkdocs: * [mkdocs](https://www.mkdocs.org/) >= 1.1.2 * [mkdocstrings](https://mkdocstrings.github.io/) >= 0.25 * [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) * [mkdocs-gen-files](https://oprypin.github.io/mkdocs-gen-files/) ### Installing prerequisites on Ubuntu/Debian The Python tools should always be run with the most recent version. The easiest way, to handle these Python dependencies is to run your development from within a virtual environment. ```sh sudo apt install build-essential libsqlite3-mod-spatialite osm2pgsql \ postgresql-postgis postgresql-postgis-scripts \ pkg-config libicu-dev virtualenv ``` To set up the virtual environment with all necessary packages run: ```sh virtualenv ~/nominatim-dev-venv ~/nominatim-dev-venv/bin/pip install\ psutil 'psycopg[binary]' PyICU SQLAlchemy \ python-dotenv jinja2 pyYAML \ mkdocs 'mkdocstrings[python]' mkdocs-gen-files mkdocs-material \ pytest pytest-asyncio pytest-bdd flake8 \ types-jinja2 types-markupsafe types-psutil types-psycopg2 \ types-pygments types-pyyaml types-requests types-ujson \ types-urllib3 typing-extensions gunicorn falcon starlette \ uvicorn mypy osmium aiosqlite mwparserfromhell ``` Now enter the virtual environment whenever you want to develop: ```sh . ~/nominatim-dev-venv/bin/activate ``` ### Running Nominatim during development The source code for Nominatim can be found in the `src` directory and can be run in-place. The source directory features a special script `nominatim-cli.py` which does the same as the installed 'nominatim' binary but executes against the code in the source tree. For example: ``` me@machine:~$ cd Nominatim me@machine:~Nominatim$ ./nominatim-cli.py --version Nominatim version 5.1.0-0 ``` Make sure you have activated the virtual environment holding all necessary dependencies. ## Executing Tests All tests are located in the `/test` directory. To run all tests, run make from the source root: ```sh make tests ``` There are also make targets for executing only parts of the test suite. For example to run linting only use: ```sh make lint ``` The possible testing targets are: mypy, lint, pytest, bdd. For more information about the structure of the tests and how to change and extend the test suite, see the [Testing chapter](Testing.md). ## Documentation Pages The [Nominatim documentation](https://nominatim.org/release-docs/develop/) is built using the [MkDocs](https://www.mkdocs.org/) static site generation framework. The master branch is automatically deployed every night on [https://nominatim.org/release-docs/develop/](https://nominatim.org/release-docs/develop/) To build the documentation run ``` make doc ``` For local testing, you can start webserver: ``` build> make serve-doc [server:296] Serving on http://127.0.0.1:8000 [handlers:62] Start watching changes ``` If you develop inside a Vagrant virtual machine, use a port that is forwarded to your host: ``` build> mkdocs serve --dev-addr 0.0.0.0:8088 [server:296] Serving on http://0.0.0.0:8088 [handlers:62] Start watching changes ``` ================================================ FILE: docs/develop/ICU-Tokenizer-Modules.md ================================================ # Writing custom sanitizer and token analysis modules for the ICU tokenizer The [ICU tokenizer](../customize/Tokenizers.md#icu-tokenizer) provides a highly customizable method to pre-process and normalize the name information of the input data before it is added to the search index. It comes with a selection of sanitizers and token analyzers which you can use to adapt your installation to your needs. If the provided modules are not enough, you can also provide your own implementations. This section describes the API of sanitizers and token analysis. !!! warning This API is currently in early alpha status. While this API is meant to be a public API on which other sanitizers and token analyzers may be implemented, it is not guaranteed to be stable at the moment. ## Using non-standard modules Sanitizer names (in the `step` property), token analysis names (in the `analyzer`) and query preprocessor names (in the `step` property) may refer to externally supplied modules. There are two ways to include external modules: through a library or from the project directory. To include a module from a library, use the absolute import path as name and make sure the library can be found in your PYTHONPATH. To use a custom module without creating a library, you can put the module somewhere in your project directory and then use the relative path to the file. Include the whole name of the file including the `.py` ending. ## Custom query preprocessors A query preprocessor must export a single factory function `create` with the following signature: ``` python create(self, config: QueryConfig) -> Callable[[list[Phrase]], list[Phrase]] ``` The function receives the custom configuration for the preprocessor and returns a callable (function or class) with the actual preprocessing code. When a query comes in, then the callable gets a list of phrases and needs to return the transformed list of phrases. The list and phrases may be changed in place or a completely new list may be generated. The `QueryConfig` is a simple dictionary which contains all configuration options given in the yaml configuration of the ICU tokenizer. It is up to the function to interpret the values. A `nominatim_api.search.Phrase` describes a part of the query that contains one or more independent search terms. Breaking a query into phrases helps reducing the number of possible tokens Nominatim has to take into account. However a phrase break is definitive: a multi-term search word cannot go over a phrase break. A Phrase object has two fields: * `ptype` further refines the type of phrase (see list below) * `text` contains the query text for the phrase The order of phrases matters to Nominatim when doing further processing. Thus, while you may split or join phrases, you should not reorder them unless you really know what you are doing. Phrase types can further help narrowing down how the tokens in the phrase are interpreted. The following phrase types are known: | Name | Description | |----------------|-------------| | PHRASE_ANY | No specific designation (i.e. source is free-form query) | | PHRASE_AMENITY | Contains name or type of a POI | | PHRASE_STREET | Contains a street name optionally with a housenumber | | PHRASE_CITY | Contains the postal city | | PHRASE_COUNTY | Contains the equivalent of a county | | PHRASE_STATE | Contains a state or province | | PHRASE_POSTCODE| Contains a postal code | | PHRASE_COUNTRY | Contains the country name or code | ## Custom sanitizer modules A sanitizer module must export a single factory function `create` with the following signature: ``` python def create(config: SanitizerConfig) -> Callable[[ProcessInfo], None] ``` The function receives the custom configuration for the sanitizer and must return a callable (function or class) that transforms the name and address terms of a place. When a place is processed, then a `ProcessInfo` object is created from the information that was queried from the database. This object is sequentially handed to each configured sanitizer, so that each sanitizer receives the result of processing from the previous sanitizer. After the last sanitizer is finished, the resulting name and address lists are forwarded to the token analysis module. Sanitizer functions are instantiated once and then called for each place that is imported or updated. They don't need to be thread-safe. If multi-threading is used, each thread creates their own instance of the function. ### Sanitizer configuration ::: nominatim_db.tokenizer.sanitizers.config.SanitizerConfig options: heading_level: 6 ### The main filter function of the sanitizer The filter function receives a single object of type `ProcessInfo` which has with three members: * `place: PlaceInfo`: read-only information about the place being processed. See PlaceInfo below. * `names: List[PlaceName]`: The current list of names for the place. * `address: List[PlaceName]`: The current list of address names for the place. While the `place` member is provided for information only, the `names` and `address` lists are meant to be manipulated by the sanitizer. It may add and remove entries, change information within a single entry (for example by adding extra attributes) or completely replace the list with a different one. #### PlaceInfo - information about the place ::: nominatim_db.data.place_info.PlaceInfo options: heading_level: 6 #### PlaceName - extended naming information ::: nominatim_db.data.place_name.PlaceName options: heading_level: 6 ### Example: Filter for US street prefixes The following sanitizer removes the directional prefixes from street names in the US: !!! example ``` python import re def _filter_function(obj): if obj.place.country_code == 'us' \ and obj.place.rank_address >= 26 and obj.place.rank_address <= 27: for name in obj.names: name.name = re.sub(r'^(north|south|west|east) ', '', name.name, flags=re.IGNORECASE) def create(config): return _filter_function ``` This is the most simple form of a sanitizer module. If defines a single filter function and implements the required `create()` function by returning the filter. The filter function first checks if the object is interesting for the sanitizer. Namely it checks if the place is in the US (through `country_code`) and it the place is a street (a `rank_address` of 26 or 27). If the conditions are met, then it goes through all available names and removes any leading directional prefix using a simple regular expression. Save the source code in a file in your project directory, for example as `us_streets.py`. Then you can use the sanitizer in your `icu_tokenizer.yaml`: ``` yaml ... sanitizers: - step: us_streets.py ... ``` !!! warning This example is just a simplified show case on how to create a sanitizer. It is not really meant for real-world use: while the sanitizer would correctly transform `West 5th Street` into `5th Street`. it would also shorten a simple `North Street` to `Street`. For more sanitizer examples, have a look at the sanitizers provided by Nominatim. They can be found in the directory [`src/nominatim_db/tokenizer/sanitizers`](https://github.com/osm-search/Nominatim/tree/master/src/nominatim_db/tokenizer/sanitizers). ## Custom token analysis module ::: nominatim_db.tokenizer.token_analysis.base.AnalysisModule options: heading_level: 6 ::: nominatim_db.tokenizer.token_analysis.base.Analyzer options: heading_level: 6 ### Example: Creating acronym variants for long names The following example of a token analysis module creates acronyms from very long names and adds them as a variant: ``` python class AcronymMaker: """ This class is the actual analyzer. """ def __init__(self, norm, trans): self.norm = norm self.trans = trans def get_canonical_id(self, name): # In simple cases, the normalized name can be used as a canonical id. return self.norm.transliterate(name.name).strip() def compute_variants(self, name): # The transliterated form of the name always makes up a variant. variants = [self.trans.transliterate(name)] # Only create acronyms from very long words. if len(name) > 20: # Take the first letter from each word to form the acronym. acronym = ''.join(w[0] for w in name.split()) # If that leds to an acronym with at least three letters, # add the resulting acronym as a variant. if len(acronym) > 2: # Never forget to transliterate the variants before returning them. variants.append(self.trans.transliterate(acronym)) return variants # The following two functions are the module interface. def configure(rules, normalizer, transliterator): # There is no configuration to parse and no data to set up. # Just return an empty configuration. return None def create(normalizer, transliterator, config): # Return a new instance of our token analysis class above. return AcronymMaker(normalizer, transliterator) ``` Given the name `Trans-Siberian Railway`, the code above would return the full name `Trans-Siberian Railway` and the acronym `TSR` as variant, so that searching would work for both. ## Sanitizers vs. Token analysis - what to use for variants? It is not always clear when to implement variations in the sanitizer and when to write a token analysis module. Just take the acronym example above: it would also have been possible to write a sanitizer which adds the acronym as an additional name to the name list. The result would have been similar. So which should be used when? The most important thing to keep in mind is that variants created by the token analysis are only saved in the word lookup table. They do not need extra space in the search index. If there are many spelling variations, this can mean quite a significant amount of space is saved. When creating additional names with a sanitizer, these names are completely independent. In particular, they can be fed into different token analysis modules. This gives a much greater flexibility but at the price that the additional names increase the size of the search index. ================================================ FILE: docs/develop/Indexing.md ================================================ # Indexing Places In Nominatim, the word __indexing__ refers to the process that takes the raw OpenStreetMap data from the place table, enriches it with address information and creates the search indexes. This section explains the basic data flow. ## Initial import After osm2pgsql has loaded the raw OSM data into the place table, the data is copied to the final search tables placex and location_property_osmline. While they are copied, some basic properties are added: * country_code, geometry_sector and partition * initial search and address rank In addition the column `indexed_status` is set to `1` marking the place as one that needs to be indexed. All this happens in the triggers `placex_insert` and `osmline_insert`. ## Indexing The main work horse of the data import is the indexing step, where Nominatim takes every place from the placex and location_property_osmline tables where the indexed_status != 0 and computes the search terms and the address parts of the place. The indexing happens in three major steps: 1. **Data preparation** - The indexer gets the data for the place to be indexed from the database. 2. **Search name processing** - The prepared data is given to the tokenizer which computes the search terms from the names and potentially other information. 3. **Address processing** - The indexer then hands the prepared data and the tokenizer information back to the database via an `INSERT` statement which also sets the indexed_status to `0`. This triggers the update triggers `placex_update`/`osmline_update` which do the work of computing address parts and filling all the search tables. When computing the address terms of a place, Nominatim relies on the processed search names of all the address parts. That is why places are processed in rank order, from smallest rank to largest. To ensure correct handling of linked place nodes, administrative boundaries are processed before all other places. Apart from these restrictions, each place can be indexed independently from the others. This allows a large degree of parallelization during the indexing. It also means that the indexing process can be interrupted at any time and will simply pick up where it left of when restarted. ### Data preparation The data preparation step computes and retrieves all data for a place that might be needed for the next step of processing the search name. That includes * location information (country code) * place classification (class, type, ranks) * names (including names of linked places) * address information (`addr:*` tags) Data preparation is implemented in pl/PgSQL mostly in the functions `placex_indexing_prepare()` and `get_interpolation_address()`. #### `addr:*` tag inheritance Nominatim has limited support for inheriting address tags from a building to POIs inside the building. This only works when the address tags are on the building outline. Any rank 30 object inside such a building or on its outline inherits all address tags when it does not have any address tags of its own. The inheritance is computed in the data preparation step. ### Search name processing The prepared place information is handed to the tokenizer next. This is a Python module responsible for processing the names from both name and address terms and building up the word index from them. The process is explained in more detail in the [Tokenizer chapter](Tokenizers.md). ### Address processing Finally, the preprocessed place information and the results of the search name processing are written back to the database. At this point the update trigger of the placex/location_property_osmline tables take over and fill all the dependent tables. This makes up the most work-intensive part of the indexing. Nominatim distinguishes between dependent and independent places. **Dependent places** are all places on rank 30: house numbers, POIs etc. These places don't have a full address of their own. Instead they are attached to a parent street or place and use the information of the parent for searching and displaying information. Everything else are **independent places**: streets, parks, water bodies, suburbs, cities, states etc. They receive a full address on their own. The address processing for both types of places is very different. #### Independent places To compute the address of an independent place Nominatim searches for all places that cover the place to compute the address for at least partially. For places with an area, that area is used to check for coverage. For place nodes an artificial square area is computed according to the rank of the place. The lower the rank the lager the area. The `location_area_large_X` tables are there to facilitate the lookup. All places that can function as the address of another place are saved in those tables. `addr:*` and `isin:*` tags are taken into account to compute the address, too. Nominatim will give preference to places with the same name as in these tags when looking for places in the vicinity. If there are no matching place names at all, then the tags are at least added to the search index. That means that the names will not be shown in the result as the 'address' of the place, but searching by them still works. Independent places are always added to the global search index `search_name`. #### Dependent places Dependent places skip the full address computation for performance reasons. Instead they just find a parent place to attach themselves to. ![parenting of dependent places](parenting-flow.svg) By default a POI or house number will be attached to the closest street. That can be any major or minor street indexed by Nominatim. In the default configuration that means that it can attach itself to a footway but only when it has a name. When the dependent place has an `addr:street` tag, then Nominatim will first try to find a street with the same name before falling back to the closest street. There are also addresses in OSM, where the housenumber does not belong to a street at all. These have an `addr:place` tag. For these places, Nominatim tries to find a place with the given name in the indexed places with an address rank between 16 and 25. If none is found, then the dependent place is attached to the closest place in that category and the addr:place name is added as *unlisted* place, which indicates to Nominatim that it needs to add it to the address output, no matter what. This special case is necessary to cover addresses that don't really refer to an existing object. When an address has both the `addr:street` and `addr:place` tag, then Nominatim assumes that the `addr:place` tag in fact should be the city part of the address and give the POI the usual street number address. Dependent places are only added to the global search index `search_name` when they have either a name themselves or when they have address tags that are not covered by the places that make up their address. The latter ensures that addresses are always searchable by those address tags. ================================================ FILE: docs/develop/Testing.md ================================================ # Nominatim Test Suite This chapter describes the tests in the `/test` directory, how they are structured and how to extend them. For a quick introduction on how to run the tests, see the [Development setup chapter](Development-Environment.md). ## Overall structure There are two kind of tests in this test suite. There are functional tests which test the API interface using a BDD test framework and there are unit tests for the Python code. This test directory is structured as follows: ``` -+- bdd Functional API tests | \ | +- steps Step implementations for test descriptions | +- osm2pgsql Tests for data import via osm2pgsql | +- db Tests for internal data processing on import and update | +- api Tests for API endpoints (search, reverse, etc.) | +- python Python unit tests +- testdb Base data for generating API test database +- testdata Additional test data used by unit tests ``` ## Python Unit Tests (`test/python`) Unit tests for Python code can be found in the `python/` directory. The goal is to have complete coverage of the Python library in `nominatim`. To execute the tests run py.test-3 test/python or pytest test/python The name of the pytest binary depends on your installation. ## BDD Functional Tests (`test/bdd`) Functional tests are written as BDD instructions. For more information on the philosophy of BDD testing, read the Wikipedia article on [Behaviour-driven development](https://en.wikipedia.org/wiki/Behavior-driven_development). ### General Usage To run the functional tests, do pytest test/bdd You can run a single feature file using expression matching: pytest test/bdd -k osm2pgsql/import/entrances.feature This even works for running single tests by adding the line number of the scenario header like that: pytest test/bdd -k 'osm2pgsql/import/entrances.feature and L4' The BDD tests create databases for the tests. You can set name of the databases through configuration variables in your `pytest.ini`: * `nominatim_test_db` defines the name of the temporary database created for a single test (default: `test_nominatim`) * `nominatim_api_test_db` defines the name of the database containing the API test data, see also below (default: `test_api_nominatim`) * `nominatim_template_db` defines the name of the template database used for creating the temporary test databases. It contains some static setup which usually doesn't change between imports of OSM data (default: `test_template_nominatim`) To change other connection parameters for the PostgreSQL database, use the [libpq enivronment variables](https://www.postgresql.org/docs/current/libpq-envars.html). Never set a password through these variables. Use a [password file](https://www.postgresql.org/docs/current/libpq-pgpass.html) instead. The API test database and the template database are only created once and then left untouched. This is usually what you want because it speeds up subsequent runs of BDD tests. If you do change code that has an influence on the content of these databases, you can run pytest with the `--nominatim-purge` parameter and the databases will be dropped and recreated from scratch. When running the BDD tests with make (using `make tests` or `make bdd`), then the databases will always be purged. The temporary test database is usually dropped directly after the test, so it does not take up unnecessary space. If you want to keep the database around, for example while debugging a specific BDD test, use the parameter `--nominatim-keep-db`. ### API Tests (`test/bdd/api`) These tests are meant to test the different API endpoints and their parameters. They require to import several datasets into a test database. This is normally done automatically during setup of the test. The API test database is then kept around and reused in subsequent runs of behave. Use `--nominatim-purge` to force a reimport of the database. The official test dataset is saved in the file `test/testdb/apidb-test-data.pbf` and compromises the following data: * Geofabrik extract of Liechtenstein * extract of Autauga country, Alabama, US (for tests against Tiger data) * additional data from `test/testdb/additional_api_test.data.osm` API tests should only be testing the functionality of the website frontend code. Most tests should be formulated as BDD DB creation tests (see below) instead. ### DB Creation Tests (`test/bdd/db`) These tests check the import and update of the Nominatim database. They do not test the correctness of osm2pgsql. Each test will write some data into the `place` table (and optionally the `planet_osm_*` tables if required) and then run Nominatim's processing functions on that. These tests use the template database and create temporary test databases for each test. ### Import Tests (`test/bdd/osm2pgsql`) These tests check that data is imported correctly into the place table. These tests also use the template database and create temporary test databases for each test. ================================================ FILE: docs/develop/Tokenizers.md ================================================ # Tokenizers The tokenizer is the component of Nominatim that is responsible for analysing names of OSM objects and queries. Nominatim provides different tokenizers that use different strategies for normalisation. This page describes how tokenizers are expected to work and the public API that needs to be implemented when creating a new tokenizer. For information on how to configure a specific tokenizer for a database see the [tokenizer chapter in the Customization Guide](../customize/Tokenizers.md). ## Generic Architecture ### About Search Tokens Search in Nominatim is organised around search tokens. Such a token represents string that can be part of the search query. Tokens are used so that the search index does not need to be organised around strings. Instead the database saves for each place which tokens match this place's name, address, house number etc. To be able to distinguish between these different types of information stored with the place, a search token also always has a certain type: name, house number, postcode etc. During search an incoming query is transformed into a ordered list of such search tokens (or rather many lists, see below) and this list is then converted into a database query to find the right place. It is the core task of the tokenizer to create, manage and assign the search tokens. The tokenizer is involved in two distinct operations: * __at import time__: scanning names of OSM objects, normalizing them and building up the list of search tokens. * __at query time__: scanning the query and returning the appropriate search tokens. ### Importing The indexer is responsible to enrich an OSM object (or place) with all data required for geocoding. It is split into two parts: the controller collects the places that require updating, enriches the place information as required and hands the place to Postgresql. The collector is part of the Nominatim library written in Python. Within Postgresql, the `placex_update` trigger is responsible to fill out all secondary tables with extra geocoding information. This part is written in PL/pgSQL. The tokenizer is involved in both parts. When the indexer prepares a place, it hands it over to the tokenizer to inspect the names and create all the search tokens applicable for the place. This usually involves updating the tokenizer's internal token lists and creating a list of all token IDs for the specific place. This list is later needed in the PL/pgSQL part where the indexer needs to add the token IDs to the appropriate search tables. To be able to communicate the list between the Python part and the pl/pgSQL trigger, the `placex` table contains a special JSONB column `token_info` which is there for the exclusive use of the tokenizer. The Python part of the tokenizer returns a structured information about the tokens of a place to the indexer which converts it to JSON and inserts it into the `token_info` column. The content of the column is then handed to the PL/pqSQL callbacks of the tokenizer which extracts the required information. Usually the tokenizer then removes all information from the `token_info` structure, so that no information is ever persistently saved in the table. All information that went in should have been processed after all and put into secondary tables. This is however not a hard requirement. If the tokenizer needs to store additional information about a place permanently, it may do so in the `token_info` column. It just may never execute searches over it and consequently not create any special indexes on it. ### Querying At query time, Nominatim builds up multiple _interpretations_ of the search query. Each of these interpretations is tried against the database in order of the likelihood with which they match to the search query. The first interpretation that yields results wins. The interpretations are encapsulated in the `SearchDescription` class. An instance of this class is created by applying a sequence of _search tokens_ to an initially empty SearchDescription. It is the responsibility of the tokenizer to parse the search query and derive all possible sequences of search tokens. To that end the tokenizer needs to parse the search query and look up matching words in its own data structures. ## Tokenizer API The following section describes the functions that need to be implemented for a custom tokenizer implementation. !!! warning This API is currently in early alpha status. While this API is meant to be a public API on which other tokenizers may be implemented, the API is far away from being stable at the moment. ### Directory Structure Nominatim expects two files containing the Python part of the implementation: * `src/nominatim_db/tokenizer/_tokenizer.py` contains the tokenizer code used during import and * `src/nominatim_api/search/_tokenizer.py` has the code used during query time. `` is a unique name for the tokenizer consisting of only lower-case letters, digits and underscore. A tokenizer also needs to install some SQL functions. By convention, these should be placed in `lib-sql/tokenizer`. If the tokenizer has a default configuration file, this should be saved in `settings/_tokenizer.`. ### Configuration and Persistence Tokenizers may define custom settings for their configuration. All settings must be prefixed with `NOMINATIM_TOKENIZER_`. Settings may be transient or persistent. Transient settings are loaded from the configuration file when Nominatim is started and may thus be changed at any time. Persistent settings are tied to a database installation and must only be read during installation time. If they are needed for the runtime then they must be saved into the `nominatim_properties` table and later loaded from there. ### The Python modules #### `src/nominatim_db/tokenizer/` The import Python module is expected to export a single factory function: ```python def create(dsn: str, data_dir: Path) -> AbstractTokenizer ``` The `dsn` parameter contains the DSN of the Nominatim database. The `data_dir` is a directory in the project directory that the tokenizer may use to save database-specific data. The function must return the instance of the tokenizer class as defined below. #### `src/nominatim_api/search/` The query-time Python module must also export a factory function: ``` python def create_query_analyzer(conn: SearchConnection) -> AbstractQueryAnalyzer ``` The `conn` parameter contains the current search connection. See the [library documentation](../library/Low-Level-DB-Access.md#searchconnection-class) for details on the class. The function must return the instance of the tokenizer class as defined below. ### Python Tokenizer Class All tokenizers must inherit from `nominatim_db.tokenizer.base.AbstractTokenizer` and implement the abstract functions defined there. ::: nominatim_db.tokenizer.base.AbstractTokenizer options: heading_level: 6 ### Python Analyzer Class ::: nominatim_db.tokenizer.base.AbstractAnalyzer options: heading_level: 6 ### Python Query Analyzer Class ::: nominatim_api.search.query_analyzer_factory.AbstractQueryAnalyzer options: heading_level: 6 ### PL/pgSQL Functions The tokenizer must provide access functions for the `token_info` column to the indexer which extracts the necessary information for the global search tables. If the tokenizer needs additional SQL functions for private use, then these functions must be prefixed with `token_` in order to ensure that there are no naming conflicts with the SQL indexer code. The following functions are expected: ```sql FUNCTION token_get_name_search_tokens(info JSONB) RETURNS INTEGER[] ``` Return an array of token IDs of search terms that should match the name(s) for the given place. These tokens are used to look up the place by name and, where the place functions as part of an address for another place, by address. Must return NULL when the place has no name. ```sql FUNCTION token_get_name_match_tokens(info JSONB) RETURNS INTEGER[] ``` Return an array of token IDs of full names of the place that should be used to match addresses. The list of match tokens is usually more strict than search tokens as it is used to find a match between two OSM tag values which are expected to contain matching full names. Partial terms should not be used for match tokens. Must return NULL when the place has no name. ```sql FUNCTION token_get_housenumber_search_tokens(info JSONB) RETURNS INTEGER[] ``` Return an array of token IDs of house number tokens that apply to the place. Note that a place may have multiple house numbers, for example when apartments each have their own number. Must be NULL when the place has no house numbers. ```sql FUNCTION token_normalized_housenumber(info JSONB) RETURNS TEXT ``` Return the house number(s) in the normalized form that can be matched against a house number token text. If a place has multiple house numbers they must be listed with a semicolon as delimiter. Must be NULL when the place has no house numbers. ```sql FUNCTION token_is_street_address(info JSONB) RETURNS BOOLEAN ``` Return true if this is an object that should be parented against a street. Only relevant for objects with address rank 30. ```sql FUNCTION token_has_addr_street(info JSONB) RETURNS BOOLEAN ``` Return true if there are street names to match against for finding the parent of the object. ```sql FUNCTION token_has_addr_place(info JSONB) RETURNS BOOLEAN ``` Return true if there are place names to match against for finding the parent of the object. ```sql FUNCTION token_matches_street(info JSONB, street_tokens INTEGER[]) RETURNS BOOLEAN ``` Check if the given tokens (previously saved from `token_get_name_match_tokens()`) match against the `addr:street` tag name. Must return either NULL or FALSE when the place has no `addr:street` tag. ```sql FUNCTION token_matches_place(info JSONB, place_tokens INTEGER[]) RETURNS BOOLEAN ``` Check if the given tokens (previously saved from `token_get_name_match_tokens()`) match against the `addr:place` tag name. Must return either NULL or FALSE when the place has no `addr:place` tag. ```sql FUNCTION token_addr_place_search_tokens(info JSONB) RETURNS INTEGER[] ``` Return the search token IDs extracted from the `addr:place` tag. These tokens are used for searches by address when no matching place can be found in the database. Must be NULL when the place has no `addr:place` tag. ```sql FUNCTION token_get_address_keys(info JSONB) RETURNS SETOF TEXT ``` Return the set of keys for which address information is provided. This should correspond to the list of (relevant) `addr:*` tags with the `addr:` prefix removed or the keys used in the `address` dictionary of the place info. ```sql FUNCTION token_get_address_search_tokens(info JSONB, key TEXT) RETURNS INTEGER[] ``` Return the array of search tokens for the given address part. `key` can be expected to be one of those returned with `token_get_address_keys()`. The search tokens are added to the address search vector of the place, when no corresponding OSM object could be found for the given address part from which to copy the name information. ```sql FUNCTION token_matches_address(info JSONB, key TEXT, tokens INTEGER[]) ``` Check if the given tokens match against the address part `key`. __Warning:__ the tokens that are handed in are the lists previously saved from `token_get_name_search_tokens()`, _not_ from the match token list. This is an historical oddity which will be fixed at some point in the future. Currently, tokenizers are encouraged to make sure that matching works against both the search token list and the match token list. ```sql FUNCTION token_get_postcode(info JSONB) RETURNS TEXT ``` Return the postcode for the object, if any exists. The postcode must be in the form that should also be presented to the end-user. ```sql FUNCTION token_strip_info(info JSONB) RETURNS JSONB ``` Return the part of the `token_info` field that should be stored in the database permanently. The indexer calls this function when all processing is done and replaces the content of the `token_info` column with the returned value before the trigger stores the information in the database. May return NULL if no information should be stored permanently. ================================================ FILE: docs/develop/address-tables.plantuml ================================================ @startuml skinparam monochrome true skinparam ObjectFontStyle bold map search_name_X { place_id => BIGINT address_rank => SMALLINT name_vector => INT[] centroid => GEOMETRY } map location_area_large_X { place_id => BIGINT keywords => INT[] partition => SMALLINT rank_search => SMALLINT rank_address => SMALLINT country_code => VARCHR(2) isguess => BOOLEAN postcode => TEXT centroid => POINT geometry => GEOMETRY } map location_road_X { place_id => BIGINT partition => SMALLINT country_code => VARCHR(2) geometry => GEOMETRY } search_name_X -[hidden]> location_area_large_X location_area_large_X -[hidden]> location_road_X @enduml ================================================ FILE: docs/develop/data-sources.md ================================================ # Additional Data Sources This guide explains how data sources other than OpenStreetMap mentioned in the install instructions got obtained and converted. ## Country grid Nominatim uses pre-generated country borders data. In case one imports only a subset of a country. And to assign each place a partition. Nominatim database tables are split into partitions for performance. More details in [osm-search/country-grid-data](https://github.com/osm-search/country-grid-data). ## US Census TIGER For the United States you can choose to import additional street-level data. The data isn't mixed into OSM data but queried as fallback when no OSM result can be found. More details in [osm-search/TIGER-data](https://github.com/osm-search/TIGER-data). ## GB postcodes For Great Britain you can choose to import Royalmail postcode centroids. More details in [osm-search/gb-postcode-data](https://github.com/osm-search/gb-postcode-data). ## Wikipedia & Wikidata rankings Nominatim can import "importance" data of place names. This greatly improves ranking of results. More details in [osm-search/wikipedia-wikidata](https://github.com/osm-search/wikipedia-wikidata). ================================================ FILE: docs/develop/osm2pgsql-tables.plantuml ================================================ @startuml skinparam monochrome true skinparam ObjectFontStyle bold map planet_osm_nodes #eee { id => BIGINT lat => INT lon => INT } map planet_osm_ways #eee { id => BIGINT nodes => BIGINT[] tags => TEXT[] } map planet_osm_rels #eee { id => BIGINT parts => BIGINT[] members => TEXT[] tags => TEXT[] way_off => SMALLINT rel_off => SMALLINT } map place { osm_type => CHAR(1) osm_id => BIGINT class => TEXT type => TEXT name => HSTORE address => HSTORE extratags => HSTORE admin_level => SMALLINT geometry => GEOMETRY } map place_postcode { osm_type => CHAR(1) osm_id => BIGINT postcode => TEXT country_code => TEXT centroid => GEOMETRY geometry => GEOMETRY } map place_interpolation { osm_id => BIGINT type => TEXT address => HSTORE nodes => BIGINT[] geometry => GEOMETRY } planet_osm_nodes -[hidden]> planet_osm_ways planet_osm_ways -[hidden]> planet_osm_rels planet_osm_ways -[hidden]-> place place -[hidden]-> place_postcode place -[hidden]-> place_interpolation planet_osm_nodes::id <- planet_osm_ways::nodes planet_osm_nodes::id <- place_interpolation::nodes @enduml ================================================ FILE: docs/develop/overview.md ================================================ # Basic Architecture Nominatim provides geocoding based on OpenStreetMap data. It uses a PostgreSQL database as a backend for storing the data. There are three basic parts to Nominatim's architecture: the data import, the address computation and the search frontend. The __data import__ stage reads the raw OSM data and extracts all information that is useful for geocoding. This part is done by osm2pgsql, the same tool that can also be used to import a rendering database. It uses the special flex output style defined in the directory `/lib-lua`. The result of the import can be found in the database table `place`. The __address computation__ or __indexing__ stage takes the data from `place` and adds additional information needed for geocoding. It ranks the places by importance, links objects that belong together and computes addresses and the search index. Most of this work is done in PL/pgSQL via database triggers and can be found in the files in the `sql/functions/` directory. The __search frontend__ implements the actual API. It takes search and reverse geocoding queries from the user, looks up the data and returns the results in the requested format. This part is located in the `nominatim-api` package. The source code can be found in `src/nominatim_api`. ================================================ FILE: docs/develop/parenting-flow.plantuml ================================================ @startuml skinparam monochrome true start if (has 'addr:street'?) then (yes) if (street with that name\n nearby?) then (yes) :**Use closest street** **with same name**; kill else (no) :** Use closest**\n**street**; kill endif elseif (has 'addr:place'?) then (yes) if (place with that name\n nearby?) then (yes) :**Use closest place** **with same name**; kill else (no) :add addr:place to address; :**Use closest place**\n**rank 16 to 25**; kill endif else (otherwise) :**Use closest**\n**street**; kill endif @enduml ================================================ FILE: docs/develop/search-tables.plantuml ================================================ @startuml skinparam monochrome true skinparam ObjectFontStyle bold left to right direction map placex { place_id => BIGINT osm_type => CHAR(1) osm_id => BIGINT class => TEXT type => TEXT name => HSTORE address => HSTORE extratags => HSTORE admin_level => SMALLINT partition => SMALLINT geometry_sector => INT parent_place_id => BIGINT linked_place_id => BIGINT importance => DOUBLE rank_search => SMALLINT rank_address => SMALLINT wikipedia => TEXT country_code => VARCHAR(2) housenumber => TEXT postcode => TEXT indexed_status => SMALLINT indexed_date => TIMESTAMP centroid => GEOMETRY geometry => GEOMETRY token_info JSONB } map search_name { place_id => BIGINT importance => DOUBLE search_rank => SMALLINT address_rank => SMALLINT name_vector => INT[] nameaddress_vector => INT[] country_code => VARCHAR(2) centroid => GEOMETRY } map word { word_id => INT word_token => TEXT ... => } map location_property_osmline { place_id => BIGINT osm_id => BIGINT type => TEXT startnumber => INT endnumber => INT step => int address => HSTORE geometry_sector => INT parent_place_id => BIGINT country_code => VARCHAR(2) postcode => text indexed_status => SMALLINT indexed_date => TIMESTAMP linegeo => GEOMETRY token_info JSONB } map place_addressline { place_id => BIGINT address_place_id => BIGINT distance => DOUBLE cached_rank_address => SMALLINT fromarea => BOOLEAN isaddress => BOOLEAN } map location_postcodes { place_id => BIGINT osm_id => BIGINT postcode => TEXT country_code => TEXT parent_place_id => BIGINT rank_search => SMALLINT indexed_status => SMALLINT indexed_date => TIMESTAMP geometry => GEOMETRY centroid -> GEOMETRY } placex::place_id <-- search_name::place_id placex::place_id <-- place_addressline::place_id placex::place_id <-- place_addressline::address_place_id search_name::name_vector --> word::word_id search_name::nameaddress_vector --> word::word_id place_addressline -[hidden]> location_property_osmline search_name -[hidden]> place_addressline location_property_osmline -[hidden]-> location_postcodes @enduml ================================================ FILE: docs/extra.css ================================================ .toctree-l3 { display: none!important } .md-content { max-width: 800px } table { margin-bottom: 12pt } th, td { padding: 1pt 12pt; } th { background-color: #eee; } .doc-object h6 { margin-bottom: 0.8em; font-size: 130%; } .doc-object { margin-bottom: 1.3em; } .doc-children .doc-contents { margin-left: 3em; } .md-footer__inner { display: none; } .headerlink { filter: grayscale(100%); font-size: 80%; } .simple-table table:not([class]) th, .simple-table table:not([class]) td { padding: 2px 4px; background: white; } ================================================ FILE: docs/index.md ================================================ Nominatim (from the Latin, 'by name') is a tool to search OSM data by name and address and to generate synthetic addresses of OSM points (reverse geocoding). It has also limited capability to search features by their type (pubs, hotels, churches, etc). This guide comes in five parts: * __[API reference](api/Overview.md)__ for users of Nominatim * __[Administration Guide](admin/Installation.md)__ for those who want to install their own Nominatim server * __[Customization Guide](customize/Overview.md)__ for those who want to adapt their own installation to their special requirements * __[Library Guide](library/Getting-Started.md)__ for Python developers who want to use Nominatim as a library in their project * __[Developer's Guide](develop/overview.md)__ for developers of the software ================================================ FILE: docs/library/Configuration.md ================================================ # Configuration When using Nominatim through the library, it can be configured in exactly the same way as when running as a service. You may instantiate the library against the [project directory](../admin/Import.md#creating-the-project-directory) of your Nominatim installation. It contains all files belonging to the Nominatim instance. This may include an `.env` file with configuration options. Setting configuration parameters via environment variables works as well. Alternatively to using the operating system's environment, a set of configuration parameters may also be passed to the Nomiantim API object. Configuration options are resolved in the following order: * from the OS environment (or the dictionary given in `environ`, (see NominatimAPI.md#nominatim.api.core.NominatimAPI.__init__) * from the .env file in the project directory of the installation * from the default installation in the configuration directory For more information on configuration via dotenv and a list of possible configuration parameters, see the [Configuration page](../customize/Settings.md). ## `Configuration` class ::: nominatim_api.Configuration options: members: - get_bool - get_int - get_str_list - get_path heading_level: 6 show_signature_annotations: True ================================================ FILE: docs/library/Getting-Started.md ================================================ # Getting Started The Nominatim search frontend is implemented as a Python library and can as such directly be used in Python scripts and applications. You don't need to set up a web frontend and access it through HTTP calls. The library gives direct access to the Nominatim database through similar search functions as offered by the web API. In addition, it will give you a more complete and detailed view on the search objects stored in the database. !!! warning The Nominatim library is used for accessing a local Nominatim database. It is not meant to be used against web services of Nominatim like the one on https://nominatim.openstreetmap.org. If you need a Python library to access these web services, have a look at [GeoPy](https://geopy.readthedocs.io). Don't forget to consult the usage policy of the service you want to use before accessing such a web service. ## Installation To use the Nominatim library, you need access to a local Nominatim database. Follow the [installation](../admin/Installation.md) and [import](../admin/Import.md) instructions to set up your database. The Nominatim frontend library is contained in the Python package `nominatim-api`. You can install the latest released version directly from pip: pip install nominatim-api To install the package from the source tree directly, run: pip install packaging/nominatim-api Usually you would want to run this in a virtual environment. ## A simple search example To query the Nominatim database you need to first set up a connection. This is done by creating an Nominatim API object. This object exposes all the search functions of Nominatim that are also known from its web API. This code snippet implements a simple search for the town of 'Brugge': !!! example === "NominatimAPIAsync" ``` python import asyncio import nominatim_api as napi async def search(query): async with napi.NominatimAPIAsync() as api: return await api.search(query) results = asyncio.run(search('Brugge')) if not results: print('Cannot find Brugge') else: print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}') ``` === "NominatimAPI" ``` python import nominatim_api as napi with napi.NominatimAPI() as api: results = api.search('Brugge') if not results: print('Cannot find Brugge') else: print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}') ``` The Nominatim library is designed around [asyncio](https://docs.python.org/3/library/asyncio.html). `NominatimAPIAsync` provides you with an interface of coroutines. If you have many requests to make, coroutines can speed up your applications significantly. For smaller scripts there is also a synchronous wrapper around the API. By using `NominatimAPI`, you get exactly the same interface using classic functions. The examples in this chapter will always show-case both implementations. The documentation itself will usually refer only to 'Nominatim API class' when both flavours are meant. If a functionality is available only for the synchronous or asynchronous version, this will be explicitly mentioned. ## Defining which database to use The [Configuration](../admin/Import.md#configuration-setup-in-env) section explains how Nominatim is configured using the [dotenv](https://github.com/theskumar/python-dotenv) library. The same configuration mechanism is used with the Nominatim API library. You should therefore be sure you are familiar with the section. There are three different ways, how configuration options can be set for a 'Nominatim API class'. When you have set up your Nominatim database, you have normally created a [project directory](../admin/Import.md#creating-the-project-directory) which stores the various configuration and customization files that Nominatim needs. You may pass the location of the project directory to your 'Nominatim API class' constructor and it will read the .env file in the directory and set the configuration accordingly. Here is the simple search example, using the configuration from a pre-defined project directory in `/srv/nominatim-project`: !!! example === "NominatimAPIAsync" ``` python import asyncio import nominatim_api as napi async def search(query): async with napi.NominatimAPIAsync('/srv/nominatim-project') as api: return await api.search(query) results = asyncio.run(search('Brugge')) if not results: print('Cannot find Brugge') else: print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}') ``` === "NominatimAPI" ``` python import nominatim_api as napi with napi.NominatimAPI('/srv/nominatim-project') as api: results = api.search('Brugge') if not results: print('Cannot find Brugge') else: print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}') ``` You may also configure Nominatim by setting environment variables. Normally Nominatim will check the operating system environment. Lets say you want to look up 'Brugge' in the special database named 'belgium' instead of the standard 'nominatim' database. You can run the example script above like this: ``` NOMINATIM_DATABASE_DSN=pgsql:dbname=belgium python3 example.py ``` The third option to configure the library is to hand in the configuration parameters into the 'Nominatim API class'. Changing the database would look like this: !!! example === "NominatimAPIAsync" ``` python import asyncio import nominatim_api as napi config_params = { 'NOMINATIM_DATABASE_DSN': 'pgsql:dbname=belgium' } async def search(query): async with napi.NominatimAPIAsync(environ=config_params) as api: return await api.search(query) results = asyncio.run(search('Brugge')) ``` === "NominatimAPI" ``` python import nominatim_api as napi config_params = { 'NOMINATIM_DATABASE_DSN': 'pgsql:dbname=belgium' } with napi.NominatimAPI(environ=config_params) as api: results = api.search('Brugge') ``` When the `environ` parameter is given, then only configuration variables from this dictionary will be used. The operating system's environment variables will be ignored. ## Presenting results to humans All search functions return full result objects from the database. Such a result object contains lots of details: names, address information, OSM tags etc. This gives you lots of flexibility what to do with the results. One of the most common things to get is some kind of human-readable label that describes the result in a compact form. Usually this would be the name of the object and some parts of the address to explain where in the world it is. To create such a label, you need two things: * the address details of the place * all names for the label adapted to the language you wish to use for display Again searching for 'Brugge', this time with a nicely formatted result: !!! example === "NominatimAPIAsync" ``` python import asyncio import nominatim_api as napi async def search(query): async with napi.NominatimAPIAsync() as api: return await api.search(query, address_details=True) results = asyncio.run(search('Brugge')) locale = napi.Locales(['fr', 'en']) for i, result in enumerate(results): address_parts = result.address_rows.localize(locale) print(f"{i + 1}. {', '.join(address_parts)}") ``` === "NominatimAPI" ``` python import nominatim_api as napi with napi.NominatimAPI() as api: results = api.search('Brugge', address_details=True) locale = napi.Locales(['fr', 'en']) for i, result in enumerate(results): address_parts = result.address_rows.localize(locale) print(f"{i + 1}. {', '.join(address_parts)}") ``` To request information about the address of a result, add the optional parameter 'address_details' to your search: ``` python >>> results = api.search('Brugge', address_details=True) ``` An additional field `address_rows` will set in results that are returned. It contains a list of all places that make up the address of the place. For simplicity, this includes name and house number of the place itself. With the names in this list it is possible to create a human-readable description of the result. To do that, you first need to decide in which language the results should be presented. As with the names in the result itself, the places in `address_rows` contain all possible name translation for each row. The library has a helper class `Locales` which helps extracting a name of a place in the preferred language. It takes a single parameter with a list of language codes in the order of preference. So ``` python locale = napi.Locales(['fr', 'en']) ``` creates a helper class that returns the name preferably in French. If that is not possible, it tries English and eventually falls back to the default `name` or `ref`. The `Locales` object can be applied to a name dictionary to return the best-matching name out of it: ``` python >>> print(locale.display_name(results[0].names)) 'Brugges' ``` The `address_row` field has a helper function to compute the display name for each Address Line component based on its `local_name` field. This is then utilized by the overall `result` object, which has a helper function to apply the function to all its ‘address_row’ members and saves the result in the `locale_name` field. However, in order to set this `local_name` field in a preferred language, you must use the `Locales` object which contains the function `localize_results`, which explicitly sets each `local_name field`. ``` python >>> Locales().localize_results(results) >>> address_parts = results[0].address_rows >>> print(', '.join(address_parts)) Bruges, Flandre-Occidentale, Flandre, Belgique ``` This is a fairly simple way to create a human-readable description. The place information in `address_rows` contains further information about each place. For example, which OSM `admin_level` was used, what category the place belongs to or what rank Nominatim has assigned. Use this to adapt the output to local address formats. For more information on address rows, see [detailed address description](Result-Handling.md#detailed-address-description). ================================================ FILE: docs/library/Input-Parameter-Types.md ================================================ # Input Parameter Types This page describes in more detail some of the input parameter types used in the query functions of the API object. ## Place identification The [details](NominatimAPI.md#nominatim_api.NominatimAPI.details) and [lookup](NominatimAPI.md#nominatim_api.NominatimAPI.lookup) functions require references to places in the database. Below the possible types for place identification are listed. All types are dataclasses. ### PlaceID ::: nominatim_api.PlaceID options: heading_level: 6 ### OsmID ::: nominatim_api.OsmID options: heading_level: 6 ## Geometry types ::: nominatim_api.GeometryFormat options: heading_level: 6 members_order: source ## Geometry input ### Point ::: nominatim_api.Point options: heading_level: 6 show_signature_annotations: True ### Bbox ::: nominatim_api.Bbox options: heading_level: 6 show_signature_annotations: True members_order: source group_by_category: False ## Layers Layers allow to restrict the search result to thematic groups. This is orthogonal to restriction by address ranks, which groups places by their geographic extent. ::: nominatim_api.DataLayer options: heading_level: 6 members_order: source ================================================ FILE: docs/library/Low-Level-DB-Access.md ================================================ # Low-level connections The `NominatimAPIAsync` class allows to directly access the underlying database connection to explore the raw data. Nominatim uses [SQLAlchemy](https://docs.sqlalchemy.org/) for building queries. Please refer to the documentation of the library to understand how to write SQL. To get access to a search connection, use the `begin()` function of your API object. This returns a `SearchConnection` object described below wrapped in a context manager. Its `t` property has definitions for all Nominatim search tables. For an overview of available tables, refer to the [Development Layout](../develop/Database-Layout.md) in in the development chapter. Note that only tables that are needed for search are accessible as SQLAlchemy tables. !!! warning The database layout is not part of the API definition and may change without notice. If you play with the low-level access functions, you need to be prepared for such changes. Here is a simple example, which prints how many places are available in the placex table: ``` import asyncio import sqlalchemy as sa from nominatim_api import NominatimAPIAsync async def print_table_size(): api = NominatimAPIAsync() async with api.begin() as conn: cnt = await conn.scalar(sa.select(sa.func.count()).select_from(conn.t.placex)) print(f'placex table has {cnt} rows.') asyncio.run(print_table_size()) ``` !!! warning Low-level connections may only be used to read data from the database. Do not use it to add or modify data or you might break Nominatim's normal functions. ## SearchConnection class ::: nominatim_api.SearchConnection options: members: - scalar - execute - get_class_table - get_db_property - get_property heading_level: 6 ================================================ FILE: docs/library/NominatimAPI.md ================================================ # The Nominatim API classes The API classes are the core object of the search library. Always instantiate one of these classes first. The API classes are **not threadsafe**. You need to instantiate a separate instance for each thread. ### NominatimAPI ::: nominatim_api.NominatimAPI options: members: - __init__ - config - close - status - details - lookup - reverse - search - search_address - search_category heading_level: 6 group_by_category: False ### NominatimAPIAsync ::: nominatim_api.NominatimAPIAsync options: members: - __init__ - setup_database - close - begin heading_level: 6 group_by_category: False ================================================ FILE: docs/library/Result-Handling.md ================================================ # Result handling The search functions of the Nominatim API always return a result object with the raw information about the place that is available in the database. This section discusses data types used in the results and utility functions that allow further processing of the results. ## Result fields ### Sources Nominatim takes the result data from multiple sources. The `source_table` field in the result describes, from which source the result was retrieved. ::: nominatim_api.SourceTable options: heading_level: 6 members_order: source ### Detailed address description When the `address_details` parameter is set, then functions return not only information about the result place but also about the place that make up the address. This information is almost always required when you want to present the user with a human-readable description of the result. See also [Localization](#localization) below. The address details are available in the `address_rows` field as a ordered list of `AddressLine` objects with the country information last. The list also contains the result place itself and some artificial entries, for example, for the house number or the country code. This makes processing and creating a full address easier. ::: nominatim_api.AddressLine options: heading_level: 6 members_order: source ### Detailed search terms The `details` function can return detailed information about which search terms may be used to find a place, when the `keywords` parameter is set. Search terms are split into terms for the name of the place and search terms for its address. ::: nominatim_api.WordInfo options: heading_level: 6 ## Localization Results are always returned with the full list of available names. However, the default `locale_name` must be explicitly set using the `localize` function within `Locales`. This parses through the full list of available names to find the one most preferred by the user. Once this is set, the user can simply use the `display_name` field within a `Result` object to retrive the localized name. ### Locale ::: nominatim_api.Locales options: heading_level: 6 ================================================ FILE: docs/mk_install_instructions.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. from pathlib import Path import mkdocs_gen_files VAGRANT_PATH = Path(__file__, '..', '..', 'vagrant').resolve() for infile in VAGRANT_PATH.glob('Install-on-*.sh'): outfile = f"admin/{infile.stem}.md" title = infile.stem.replace('-', ' ') with mkdocs_gen_files.open(outfile, "w", encoding='utf-8') as outfd, \ infile.open(encoding='utf-8') as infd: print("#", title, file=outfd) has_empty = False for line in infd: line = line.rstrip() docpos = line.find('#DOCS:') if docpos >= 0: line = line[docpos + 6:] elif line == '#' or line.startswith('#!'): line = '' elif line.startswith('# '): line = line[2:] if line or not has_empty: print(line, file=outfd) has_empty = not bool(line) mkdocs_gen_files.set_edit_path(outfile, "docs/mk_install_instructions.py") ================================================ FILE: docs/styles.css ================================================ .codehilite .hll { background-color: #ffffcc } .codehilite { background: #f0f0f0; } .codehilite .c { color: #60a0b0; font-style: italic } /* Comment */ .codehilite .err { /* border: 1px solid #FF0000 */ } /* Error */ .codehilite .k { color: #007020; font-weight: bold } /* Keyword */ .codehilite .o { color: #666666 } /* Operator */ .codehilite .ch { color: #60a0b0; font-style: italic } /* Comment.Hashbang */ .codehilite .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */ .codehilite .cp { color: #007020 } /* Comment.Preproc */ .codehilite .cpf { color: #60a0b0; font-style: italic } /* Comment.PreprocFile */ .codehilite .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */ .codehilite .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */ .codehilite .gd { color: #A00000 } /* Generic.Deleted */ .codehilite .ge { font-style: italic } /* Generic.Emph */ .codehilite .gr { color: #FF0000 } /* Generic.Error */ .codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .codehilite .gi { color: #00A000 } /* Generic.Inserted */ .codehilite .go { color: #888888 } /* Generic.Output */ .codehilite .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .codehilite .gs { font-weight: bold } /* Generic.Strong */ .codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .codehilite .gt { color: #0044DD } /* Generic.Traceback */ .codehilite .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ .codehilite .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ .codehilite .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ .codehilite .kp { color: #007020 } /* Keyword.Pseudo */ .codehilite .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ .codehilite .kt { color: #902000 } /* Keyword.Type */ .codehilite .m { color: #40a070 } /* Literal.Number */ .codehilite .s { color: #4070a0 } /* Literal.String */ .codehilite .na { color: #4070a0 } /* Name.Attribute */ .codehilite .nb { color: #007020 } /* Name.Builtin */ .codehilite .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ .codehilite .no { color: #60add5 } /* Name.Constant */ .codehilite .nd { color: #555555; font-weight: bold } /* Name.Decorator */ .codehilite .ni { color: #d55537; font-weight: bold } /* Name.Entity */ .codehilite .ne { color: #007020 } /* Name.Exception */ .codehilite .nf { color: #06287e } /* Name.Function */ .codehilite .nl { color: #002070; font-weight: bold } /* Name.Label */ .codehilite .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .codehilite .nt { color: #062873; font-weight: bold } /* Name.Tag */ .codehilite .nv { color: #bb60d5 } /* Name.Variable */ .codehilite .ow { color: #007020; font-weight: bold } /* Operator.Word */ .codehilite .w { color: #bbbbbb } /* Text.Whitespace */ .codehilite .mb { color: #40a070 } /* Literal.Number.Bin */ .codehilite .mf { color: #40a070 } /* Literal.Number.Float */ .codehilite .mh { color: #40a070 } /* Literal.Number.Hex */ .codehilite .mi { color: #40a070 } /* Literal.Number.Integer */ .codehilite .mo { color: #40a070 } /* Literal.Number.Oct */ .codehilite .sa { color: #4070a0 } /* Literal.String.Affix */ .codehilite .sb { color: #4070a0 } /* Literal.String.Backtick */ .codehilite .sc { color: #4070a0 } /* Literal.String.Char */ .codehilite .dl { color: #4070a0 } /* Literal.String.Delimiter */ .codehilite .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ .codehilite .s2 { color: #4070a0 } /* Literal.String.Double */ .codehilite .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ .codehilite .sh { color: #4070a0 } /* Literal.String.Heredoc */ .codehilite .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ .codehilite .sx { color: #c65d09 } /* Literal.String.Other */ .codehilite .sr { color: #235388 } /* Literal.String.Regex */ .codehilite .s1 { color: #4070a0 } /* Literal.String.Single */ .codehilite .ss { color: #517918 } /* Literal.String.Symbol */ .codehilite .bp { color: #007020 } /* Name.Builtin.Pseudo */ .codehilite .fm { color: #06287e } /* Name.Function.Magic */ .codehilite .vc { color: #bb60d5 } /* Name.Variable.Class */ .codehilite .vg { color: #bb60d5 } /* Name.Variable.Global */ .codehilite .vi { color: #bb60d5 } /* Name.Variable.Instance */ .codehilite .vm { color: #bb60d5 } /* Name.Variable.Magic */ .codehilite .il { color: #40a070 } /* Literal.Number.Integer.Long */ ================================================ FILE: lib-lua/flex-base.lua ================================================ -- This is just an alias for the Nominatim themepark theme module local flex = require('themes/nominatim/init') function flex.load_topic(name, cfg) local topic_file = debug.getinfo(1, "S").source:sub(2):match("(.*/)") .. 'themes/nominatim/topics/'.. name .. '.lua' if topic_file == nil then error('Cannot find topic: ' .. name) end loadfile(topic_file)(nil, flex, cfg or {}) end return flex ================================================ FILE: lib-lua/import-address.lua ================================================ -- This is just an alias for the Nominatim themepark address topic local flex = require('flex-base') flex.load_topic('address') return flex ================================================ FILE: lib-lua/import-admin.lua ================================================ -- This is just an alias for the Nominatim themepark admin topic local flex = require('flex-base') flex.load_topic('admin') return flex ================================================ FILE: lib-lua/import-extratags.lua ================================================ -- This is just an alias for the Nominatim themepark full topic local flex = require('flex-base') flex.load_topic('full', {with_extratags = true}) return flex ================================================ FILE: lib-lua/import-full.lua ================================================ -- This is just an alias for the Nominatim themepark full topic local flex = require('flex-base') flex.load_topic('full') return flex ================================================ FILE: lib-lua/import-street.lua ================================================ -- This is just an alias for the Nominatim themepark street topic local flex = require('flex-base') flex.load_topic('street') return flex ================================================ FILE: lib-lua/taginfo.lua ================================================ -- Prints taginfo project description in the standard output -- -- create fake "osm2pgsql" table for flex-base, originally created by the main C++ program osm2pgsql = {} function osm2pgsql.define_table(...) end -- provide path to flex-style lua file package.path = arg[0]:match("(.*/)") .. "?.lua;" .. package.path local flex = require('import-' .. (arg[1] or 'extratags')) local json = require ('dkjson') local NAME_DESCRIPTIONS = { 'Searchable auxiliary name of the place', main = 'Searchable primary name of the place', house = 'House name part of an address, searchable' } local ADDRESS_DESCRIPTIONS = { 'Used to determine the address of a place', main = 'Primary key for an address point', postcode = 'Used to determine the postcode of a place', country = 'Used to determine country of a place (only if written as two-letter code)', interpolation = 'Primary key for an address interpolation line' } ------------ helper functions --------------------- -- Sets the key order for the resulting JSON table local function set_keyorder(table, order) setmetatable(table, { __jsonorder = order }) end local function get_key_description(key, description) local desc = {} desc.key = key desc.description = description set_keyorder(desc, {'key', 'description'}) return desc end local function get_key_value_description(key, value, description) local desc = {key = key, value = value, description = description} set_keyorder(desc, {'key', 'value', 'description'}) return desc end local function group_table_to_keys(tags, data, descriptions) for group, values in pairs(data) do local desc = descriptions[group] or descriptions[1] for _, key in pairs(values) do if key:sub(1, 1) ~= '*' and key:sub(#key, #key) ~= '*' then table.insert(tags, get_key_description(key, desc)) end end end end -- Prints the collected tags in the required format in JSON local function print_taginfo() local taginfo = flex.get_taginfo() local tags = {} for k, values in pairs(taginfo.main) do if values[1] == nil or values[1] == 'delete' or values[1] == 'extra' then for v, group in pairs(values) do if type(v) == 'string' and group ~= 'delete' and group ~= 'extra' then local text = 'POI/feature in the search database' if type(group) ~= 'function' then text = 'Fallback ' .. text end table.insert(tags, get_key_value_description(k, v, text)) end end elseif type(values[1]) == 'function' or values[1] == 'fallback' then local desc = 'POI/feature in the search database' if values[1] == 'fallback' then desc = 'Fallback ' .. desc end local excp = {} for v, group in pairs(values) do if group == 'delete' or group == 'extra' then table.insert(excp, v) end end if next(excp) ~= nil then desc = desc .. string.format(' (except for values: %s)', table.concat(excp, ', ')) end table.insert(tags, get_key_description(k, desc)) end end group_table_to_keys(tags, taginfo.name, NAME_DESCRIPTIONS) group_table_to_keys(tags, taginfo.address, ADDRESS_DESCRIPTIONS) local format = { data_format = 1, data_url = 'https://nominatim.openstreetmap.org/taginfo.json', project = { name = 'Nominatim', description = 'OSM search engine.', project_url = 'https://nominatim.openstreetmap.org', doc_url = 'https://nominatim.org/release-docs/develop/', contact_name = 'Sarah Hoffmann', contact_email = 'lonvia@denofr.de' } } format.tags = tags set_keyorder(format, {'data_format', 'data_url', 'project', 'tags'}) set_keyorder(format.project, {'name', 'description', 'project_url', 'doc_url', 'contact_name', 'contact_email'}) print(json.encode(format)) end print_taginfo() ================================================ FILE: lib-lua/themes/nominatim/init.lua ================================================ -- Nominatim themepark theme. -- -- The Nominatim theme creates a fixed set of import tables for use with -- Nominatim. Creation and object processing are directly controlled by -- the theme. Topics provide preset configurations. You should add exactly -- one topic to your project. -- -- The theme also exports a number of functions that can be used to configure -- its behaviour. These may be directly called in the style file after -- importing the theme: -- -- local nominatim = themepark:init_theme('nominatim') -- nominatim.set_main_tags{boundary = 'always'} -- -- This allows to write your own configuration from scratch. You can also -- use it to customize topics. In that case, first add the topic, then -- change the configuration: -- -- themepark:add_topic('nominatim/full') -- local nominatim = themepark:init_theme('nominatim') -- nominatim.ignore_tags{'amenity'} local module = {} local MAIN_KEYS = {admin_level = {'delete'}} local PRE_FILTER = {prefix = {}, suffix = {}} local NAMES = {} local NAME_FILTER = nil local ADDRESS_TAGS = {} local ADDRESS_FILTER = nil local EXTRATAGS_FILTER local REQUIRED_EXTRATAGS_FILTER local POSTCODE_FALLBACK = true local ENTRANCE_FUNCTION = nil -- This file can also be directly require'd instead of running it under -- the themepark framework. In that case the first parameter is usually -- the module name. Lets check for that, so that further down we can call -- the low-level osm2pgsql functions instead of themepark functions. local themepark = ... if type(themepark) ~= 'table' then themepark = nil end -- The place tables carry the raw OSM information. local table_definitions = { place = { ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' }, columns = { { column = 'class', type = 'text', not_null = true }, { column = 'type', type = 'text', not_null = true }, { column = 'admin_level', type = 'smallint' }, { column = 'name', type = 'hstore' }, { column = 'address', type = 'hstore' }, { column = 'extratags', type = 'hstore' }, { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true }, }, indexes = {} }, place_entrance = { ids = { type = 'node', id_column = 'osm_id' }, columns = { { column = 'type', type = 'text', not_null = true }, { column = 'extratags', type = 'hstore' }, { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true } }, indexes = {} }, place_postcode = { ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' }, columns = { { column = 'postcode', type = 'text', not_null = true }, { column = 'country_code', type = 'text' }, { column = 'centroid', type = 'point', projection = 'WGS84', not_null = true }, { column = 'geometry', type = 'geometry', projection = 'WGS84' } }, indexes = { { column = 'postcode', method = 'btree' } } }, place_interpolation = { ids = { type = 'way', id_column = 'osm_id' }, columns = { { column = 'type', type = 'text', not_null = true }, { column = 'address', type = 'hstore' }, { column = 'nodes', type = 'text', sql_type = 'bigint[]', not_null = true }, { column = 'geometry', type = 'linestring', projection = 'WGS84', not_null = true }, }, indexes = { { column = 'nodes', method = 'gin' } } }, place_associated_street = { ids = { type = 'relation', id_column = 'relation_id' }, columns = { { column = 'member_type', type = 'text', not_null = true }, { column = 'member_id', type = 'int8', not_null = true }, { column = 'member_role', type = 'text', not_null = true } }, indexes = { { column = { 'member_type', 'member_id' }, method = 'btree' } } } } local insert_row = {} local script_path = debug.getinfo(1, "S").source:match("@?(.*/)") local PRESETS = loadfile(script_path .. 'presets.lua')() for table_name, table_definition in pairs(table_definitions) do table_definition.name = table_name table_definition.data_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_DATA") table_definition.index_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_INDEX") if themepark then themepark:add_table(table_definition) insert_row[table_name] = function(columns) themepark:insert(table_name, columns, {}, {}) end else local place_table = osm2pgsql.define_table(table_definition) insert_row[table_name] = function(columns) place_table:insert(columns) end end end ------------ Geometry functions for relations --------------------- function module.relation_as_multipolygon(o) return o:as_multipolygon() end function module.relation_as_multiline(o) return o:as_multilinestring():line_merge() end module.RELATION_TYPES = { multipolygon = module.relation_as_multipolygon, boundary = module.relation_as_multipolygon, waterway = module.relation_as_multiline } --------- Built-in place transformation functions -------------------------- local PlaceTransform = {} -- Special transform meanings which are interpreted elsewhere PlaceTransform.fallback = 'fallback' PlaceTransform.postcode_area = 'postcode_area' PlaceTransform.delete = 'delete' PlaceTransform.extra = 'extra' -- always: unconditionally use that place function PlaceTransform.always(place) return place end -- never: unconditionally drop the place function PlaceTransform.never() return nil end -- named: use the place if it has a fully-qualified name function PlaceTransform.named(place) if place.has_name then return place end end -- named_with_key: use place if there is a name with the main key prefix function PlaceTransform.named_with_key(place, k) local names = {} local prefix = k .. ':name' for namek, namev in pairs(place.intags) do if namek:sub(1, #prefix) == prefix and (#namek == #prefix or namek:sub(#prefix + 1, #prefix + 1) == ':') then names[namek:sub(#k + 2)] = namev end end if next(names) ~= nil then return place:clone{names=names} end end -- Special transform used with address fallbacks: ignore all names -- except for those marked as being part of the address. local function address_fallback(place) if next(place.names) == nil or NAMES.house == nil then return place end local names = {} for k, v in pairs(place.names) do if NAME_FILTER(k, v) == 'house' then names[k] = v end end return place:clone{names=names} end ----------------- other helper functions ----------------------------- local function lookup_prefilter_classification(k, v) -- full matches local desc = MAIN_KEYS[k] local fullmatch = desc and (desc[v] or desc[1]) if fullmatch ~= nil then return fullmatch end -- suffixes for slen, slist in pairs(PRE_FILTER.suffix) do if #k >= slen then local group = slist[k:sub(-slen)] if group ~= nil then return group end end end -- prefixes for slen, slist in pairs(PRE_FILTER.prefix) do if #k >= slen then local group = slist[k:sub(1, slen)] if group ~= nil then return group end end end end local function merge_filters_into_main(group, keys, tags) if keys ~= nil then for _, key in pairs(keys) do -- ignore suffix and prefix matches if key:sub(1, 1) ~= '*' and key:sub(#key, #key) ~= '*' then if MAIN_KEYS[key] == nil then MAIN_KEYS[key] = {} end MAIN_KEYS[key][1] = group end end end if tags ~= nil then for key, values in pairs(tags) do if MAIN_KEYS[key] == nil then MAIN_KEYS[key] = {} end for _, v in pairs(values) do MAIN_KEYS[key][v] = group end end end end local function remove_group_from_main(group) for key, values in pairs(MAIN_KEYS) do for _, ttype in pairs(values) do if ttype == group then values[ttype] = nil end end if next(values) == nil then MAIN_KEYS[key] = nil end end end local function add_pre_filter(data) for group, keys in pairs(data) do for _, key in pairs(keys) do local klen = #key - 1 if key:sub(1, 1) == '*' then if klen > 0 then if PRE_FILTER.suffix[klen] == nil then PRE_FILTER.suffix[klen] = {} end PRE_FILTER.suffix[klen][key:sub(2)] = group end elseif key:sub(#key, #key) == '*' then if PRE_FILTER.prefix[klen] == nil then PRE_FILTER.prefix[klen] = {} end PRE_FILTER.prefix[klen][key:sub(1, klen)] = group end end end end ------------- Place class ------------------------------------------ local Place = {} Place.__index = Place function Place.new(object, geom_func) local self = setmetatable({}, Place) self.object = object self.geom_func = geom_func self.admin_level = tonumber(self.object.tags.admin_level or 15) or 15 if self.admin_level == nil or self.admin_level <= 0 or self.admin_level > 15 or math.floor(self.admin_level) ~= self.admin_level then self.admin_level = 15 end self.num_entries = 0 self.has_name = false self.names = {} self.address = {} self.extratags = {} self.intags = {} local has_main_tags = false for k, v in pairs(self.object.tags) do local group = lookup_prefilter_classification(k, v) if group == 'extra' then self.extratags[k] = v elseif group ~= 'delete' then self.intags[k] = v if group ~= nil then has_main_tags = true end end end if not has_main_tags then -- no interesting tags, don't bother processing self.intags = {} end return self end function Place:clean(data) for k, v in pairs(self.intags) do if data.delete ~= nil and data.delete(k, v) then self.intags[k] = nil elseif data.extra ~= nil and data.extra(k, v) then self.extratags[k] = v self.intags[k] = nil end end end function Place:delete(data) if data.match ~= nil then for k, v in pairs(self.intags) do if data.match(k, v) then self.intags[k] = nil end end end end function Place:grab_extratags(data) local count = 0 if data.match ~= nil then for k, v in pairs(self.intags) do if data.match(k, v) then self.intags[k] = nil self.extratags[k] = v count = count + 1 end end end return count end local function strip_address_prefix(k) if k:sub(1, 5) == 'addr:' then return k:sub(6) end if k:sub(1, 6) == 'is_in:' then return k:sub(7) end return k end function Place:grab_address_parts(data) local count = 0 if data.groups ~= nil then for k, v in pairs(self.intags) do local atype = data.groups(k, v) if atype ~= nil then if atype == 'main' then self.has_name = true self.address[strip_address_prefix(k)] = v count = count + 1 elseif atype == 'extra' then self.address[strip_address_prefix(k)] = v else self.address[atype] = v end self.intags[k] = nil end end end return count end function Place:grab_name_parts(data) local fallback = nil if data.groups ~= nil then for k, v in pairs(self.intags) do local atype = data.groups(k, v) if atype ~= nil then self.names[k] = v self.intags[k] = nil if atype == 'main' then self.has_name = true elseif atype == 'house' then self.has_name = true fallback = {'place', 'house', address_fallback} end end end end return fallback end function Place:write_place(k, v, mfunc) v = v or self.intags[k] if v == nil then return 0 end local place = mfunc(self, k, v) if place then local res = place:write_row(k, v) self.num_entries = self.num_entries + res return res end return 0 end function Place:geometry_is_valid() if self.geometry == nil then self.geometry = self.geom_func(self.object) if self.geometry == nil or self.geometry:is_null() then self.geometry = false return false end return true end return self.geometry ~= false end function Place:write_row(k, v) if not self:geometry_is_valid() then return 0 end local extra = EXTRATAGS_FILTER(self, k, v) or {} for tk, tv in pairs(self.object.tags) do if REQUIRED_EXTRATAGS_FILTER(tk, tv) and extra[tk] == nil then extra[tk] = tv end end if extra and next(extra) == nil then extra = nil end insert_row.place{ class = k, type = v, admin_level = self.admin_level, name = next(self.names) and self.names, address = next(self.address) and self.address, extratags = extra, geometry = self.geometry } return 1 end function Place:clone(data) local cp = setmetatable({}, Place) cp.object = self.object cp.geometry = data.geometry or self.geometry cp.geom_func = self.geom_func cp.intags = data.intags or self.intags cp.admin_level = data.admin_level or self.admin_level cp.names = data.names or self.names cp.address = data.address or self.address cp.extratags = data.extratags or self.extratags return cp end function module.tag_match(data) if data == nil or next(data) == nil then return nil end local fullmatches = {} local key_prefixes = {} local key_suffixes = {} if data.keys ~= nil then for _, key in pairs(data.keys) do if key:sub(1, 1) == '*' then if #key > 1 then if key_suffixes[#key - 1] == nil then key_suffixes[#key - 1] = {} end key_suffixes[#key - 1][key:sub(2)] = true end elseif key:sub(#key, #key) == '*' then if key_prefixes[#key - 1] == nil then key_prefixes[#key - 1] = {} end key_prefixes[#key - 1][key:sub(1, #key - 1)] = true else fullmatches[key] = true end end end if data.tags ~= nil then for k, vlist in pairs(data.tags) do if fullmatches[k] == nil then fullmatches[k] = {} for _, v in pairs(vlist) do fullmatches[k][v] = true end end end end return function (k, v) if fullmatches[k] ~= nil and (fullmatches[k] == true or fullmatches[k][v] ~= nil) then return true end for slen, slist in pairs(key_suffixes) do if #k >= slen and slist[k:sub(-slen)] ~= nil then return true end end for slen, slist in pairs(key_prefixes) do if #k >= slen and slist[k:sub(1, slen)] ~= nil then return true end end return false end end function module.tag_group(data) if data == nil or next(data) == nil then return nil end local fullmatches = {} local key_prefixes = {} local key_suffixes = {} for group, tags in pairs(data) do for _, key in pairs(tags) do if key:sub(1, 1) == '*' then if #key > 1 then if key_suffixes[#key - 1] == nil then key_suffixes[#key - 1] = {} end key_suffixes[#key - 1][key:sub(2)] = group end elseif key:sub(#key, #key) == '*' then if key_prefixes[#key - 1] == nil then key_prefixes[#key - 1] = {} end key_prefixes[#key - 1][key:sub(1, #key - 1)] = group else fullmatches[key] = group end end end return function (k) local val = fullmatches[k] if val ~= nil then return val end for slen, slist in pairs(key_suffixes) do if #k >= slen then val = slist[k:sub(-slen)] if val ~= nil then return val end end end for slen, slist in pairs(key_prefixes) do if #k >= slen then val = slist[k:sub(1, slen)] if val ~= nil then return val end end end end end -- Returns prefix part of the keys, and reject suffix matching keys local function process_key(key) if key:sub(1, 1) == '*' then return nil end if key:sub(#key, #key) == '*' then return key:sub(1, #key - 2) end return key end -- Process functions for all data types function module.process_node(object) if ENTRANCE_FUNCTION ~= nil then local entrance_info = ENTRANCE_FUNCTION(object) if entrance_info ~= nil then insert_row.place_entrance{ type = entrance_info.entrance, extratags = entrance_info.extratags, geometry = object:as_point() } end end local function geom_func(o) return o:as_point() end module.process_tags(Place.new(object, geom_func)) end function module.process_way(object) local function geom_func(o) local geom = o:as_polygon() if geom:is_null() then geom = o:as_linestring() if geom:is_null() or geom:length() > 30 then return nil end end return geom end module.process_tags(Place.new(object, geom_func)) end function module.process_relation(object) if object.tags.type == 'associatedStreet' then local has_street = false for _, member in ipairs(object.members) do -- Streets can only be ways or relations, not nodes if member.role == 'street' and (member.type == 'w' or member.type == 'r') then has_street = true break end end if has_street then for _, member in ipairs(object.members) do -- Only insert streets that are ways or relations if member.role == 'street' and (member.type == 'w' or member.type == 'r') then insert_row.place_associated_street{ member_type = member.type:upper(), member_id = member.ref, member_role = member.role } elseif member.role == 'house' then insert_row.place_associated_street{ member_type = member.type:upper(), member_id = member.ref, member_role = member.role } end end end end local geom_func = module.RELATION_TYPES[object.tags.type] if geom_func ~= nil then module.process_tags(Place.new(object, geom_func)) end end -- The process functions are used by default by osm2pgsql. if themepark then themepark:add_proc('node', module.process_node) themepark:add_proc('way', module.process_way) themepark:add_proc('relation', module.process_relation) else osm2pgsql.process_node = module.process_node osm2pgsql.process_way = module.process_way osm2pgsql.process_relation = module.process_relation end function module.process_tags(o) if next(o.intags) == nil then return -- shortcut when pre-filtering has removed all tags end -- Exception for boundary/place double tagging if o.intags.boundary == 'administrative' then o:grab_extratags{match = function (k, v) return k == 'place' and v:sub(1,3) ~= 'isl' end} end -- name keys local fallback = o:grab_name_parts{groups=NAME_FILTER} -- address keys if o:grab_address_parts{groups=ADDRESS_FILTER} > 0 and fallback == nil then fallback = {'place', 'house', address_fallback} end if o.address.country ~= nil and #o.address.country ~= 2 then o.address['country'] = nil end if o.address.interpolation ~= nil and o.address.housenumber == nil and o.object.type == 'way' and o.object.nodes ~= nil then local extra_addr = nil for k, v in pairs(o.address) do if k ~= 'interpolation' then if extra_addr == nil then extra_addr = {} end extra_addr[k] = v end end insert_row.place_interpolation{ type = o.address.interpolation, address = extra_addr, nodes = '{' .. table.concat(o.object.nodes, ',') .. '}', geometry = o.object:as_linestring() } end -- collect main keys local postcode_collect = false for k, v in pairs(o.intags) do local ktable = MAIN_KEYS[k] if ktable then local ktype = ktable[v] or ktable[1] if type(ktype) == 'function' then o:write_place(k, v, ktype) elseif ktype == 'postcode_area' then postcode_collect = true if o.object.type == 'relation' and o.address.postcode ~= nil and o:geometry_is_valid() then insert_row.place_postcode{ postcode = o.address.postcode, centroid = o.geometry:centroid(), geometry = o.geometry } end elseif ktype == 'fallback' and o.has_name then fallback = {k, v, PlaceTransform.always} end end end if o.num_entries == 0 then if fallback ~= nil then o:write_place(fallback[1], fallback[2], fallback[3]) elseif POSTCODE_FALLBACK and not postcode_collect and o.address.postcode ~= nil and o:geometry_is_valid() then insert_row.place_postcode{ postcode = o.address.postcode, centroid = o.geometry:centroid() } end end end --------- Extratags post-processing functions --------------- local function default_extratags_filter(p, k) return p.extratags end EXTRATAGS_FILTER = default_extratags_filter REQUIRED_EXTRATAGS_FILTER = module.tag_match(PRESETS.EXTRATAGS) --------- Convenience functions for simple style configuration ----------------- function module.set_prefilters(data) remove_group_from_main('delete') merge_filters_into_main('delete', data.delete_keys, data.delete_tags) remove_group_from_main('extra') merge_filters_into_main('extra', data.extra_keys, data.extra_tags) PRE_FILTER = {prefix = {}, suffix = {}} add_pre_filter{delete = data.delete_keys, extra = data.extra_keys} end function module.ignore_keys(data) if type(data) == 'string' then local preset = data data = PRESETS.IGNORE_KEYS[data] if data == nil then error('Unknown preset for ignored keys: ' .. preset) end end merge_filters_into_main('delete', data) add_pre_filter{delete = data} end function module.add_for_extratags(data) if type(data) == 'string' then local preset = data data = PRESETS.IGNORE_KEYS[data] if data == nil then error('Unknown preset for extratags: ' .. preset) end end merge_filters_into_main('extra', data) add_pre_filter{extra = data} end function module.set_main_tags(data) for key, values in pairs(MAIN_KEYS) do for _, ttype in pairs(values) do if ttype == 'fallback' or type(ttype) == 'function' then values[ttype] = nil end end if next(values) == nil then MAIN_KEYS[key] = nil end end module.modify_main_tags(data) end function module.modify_main_tags(data) if type(data) == 'string' then local preset = data if data:sub(1, 7) == 'street/' then data = PRESETS.MAIN_TAGS_STREETS[data:sub(8)] elseif data:sub(1, 4) == 'poi/' then data = PRESETS.MAIN_TAGS_POIS(data:sub(5)) else data = PRESETS.MAIN_TAGS[data] end if data == nil then error('Unknown preset for main tags: ' .. preset) end end for k, v in pairs(data) do if MAIN_KEYS[k] == nil then MAIN_KEYS[k] = {} end if type(v) == 'function' then MAIN_KEYS[k][1] = v elseif type(v) == 'string' then MAIN_KEYS[k][1] = PlaceTransform[v] elseif type(v) == 'table' then for subk, subv in pairs(v) do if type(subv) == 'function' then MAIN_KEYS[k][subk] = subv else MAIN_KEYS[k][subk] = PlaceTransform[subv] end end end end end function module.modify_name_tags(data) if type(data) == 'string' then local preset = data data = PRESETS.NAME_TAGS[data] if data == nil then error('Unknown preset for name keys: ' .. preset) end end for k,v in pairs(data) do if next(v) then NAMES[k] = v else NAMES[k] = nil end end NAME_FILTER = module.tag_group(NAMES) remove_group_from_main('fallback:name') if data.house ~= nil then merge_filters_into_main('fallback:name', data.house) end end function module.set_name_tags(data) NAMES = {} module.modify_name_tags(data) end function module.set_address_tags(data) ADDRESS_TAGS = {} module.modify_address_tags(data) end function module.modify_address_tags(data) if type(data) == 'string' then local preset = data data = PRESETS.ADDRESS_TAGS[data] if data == nil then error('Unknown preset for address keys: ' .. preset) end end for k, v in pairs(data) do if k == 'postcode_fallback' then POSTCODE_FALLBACK = v elseif next(v) == nil then ADDRESS_TAGS[k] = nil else ADDRESS_TAGS[k] = v end end ADDRESS_FILTER = module.tag_group(ADDRESS_TAGS) remove_group_from_main('fallback:address') merge_filters_into_main('fallback:address', data.main) merge_filters_into_main('fallback:address', data.interpolation) remove_group_from_main('fallback:postcode') if POSTCODE_FALLBACK then merge_filters_into_main('fallback:postcode', data.postcode) end end function module.set_address_tags(data) ADDRESS_TAGS_SOURCE = {} module.modify_address_tags(data) end function module.set_postcode_fallback(enable) if POSTCODE_FALLBACK ~= enable then remove_group_from_main('fallback:postcode') if enable then merge_filters_into_main('fallback:postcode', ADDRESS_TAGS.postcode) end end POSTCODE_FALLBACK = enable end function module.set_unused_handling(data) if type(data) == 'function' then EXTRATAGS_FILTER = data elseif data == nil then EXTRATAGS_FILTER = default_extratags_filter elseif data.extra_keys == nil and data.extra_tags == nil then local delfilter = module.tag_match{keys = data.delete_keys, tags = data.delete_tags} EXTRATAGS_FILTER = function (p, k) local extra = {} for kin, vin in pairs(p.intags) do if kin ~= k and not delfilter(kin, vin) then extra[kin] = vin end end if next(extra) == nil then return p.extratags end for kextra, vextra in pairs(p.extratags) do extra[kextra] = vextra end return extra end elseif data.delete_keys == nil and data.delete_tags == nil then local incfilter = module.tag_match{keys = data.extra_keys, tags = data.extra_tags} EXTRATAGS_FILTER = function (p, k) local extra = {} for kin, vin in pairs(p.intags) do if kin ~= k and incfilter(kin, vin) then extra[kin] = vin end end if next(extra) == nil then return p.extratags end for kextra, vextra in pairs(p.extratags) do extra[kextra] = vextra end return extra end else error("unused handler can have only 'extra_keys' or 'delete_keys' set.") end end function module.set_relation_types(data) module.RELATION_TYPES = {} for k, v in data do if v == 'multipolygon' then module.RELATION_TYPES[k] = module.relation_as_multipolygon elseif v == 'multiline' then module.RELATION_TYPES[k] = module.relation_as_multiline end end end function module.set_entrance_filter(data) if data == nil or type(data) == 'function' then ENTRANCE_FUNCTION = data return nil end if type(data) == 'string' then local preset = data data = PRESETS.ENTRANCE_TABLE[data] if data == nil then error('Unknown preset for entrance table: ' .. preset) end end ENTRANCE_FUNCTION = nil if data.main_tags ~= nil and next(data.main_tags) ~= nil then if data.extra_include ~= nil and next(data.extra_include) == nil then -- shortcut: no extra tags requested ENTRANCE_FUNCTION = function(o) for _, v in ipairs(data.main_tags) do if o.tags[v] ~= nil then return {entrance = o.tags[v]} end end return nil end else if data.extra_include ~= nil then local tags = {} for _, v in pairs(data.extra_include) do tags[v] = true end if data.extra_exclude ~= nil then for _, v in pairs(data.extra_exclude) do tags[v] = nil end end for _, v in pairs(data.main_tags) do tags[v] = nil end ENTRANCE_FUNCTION = function(o) for _, v in ipairs(data.main_tags) do if o.tags[v] ~= nil then local entrance = o.tags[v] local extra = {} for k, v in pairs(tags) do extra[k] = o.tags[k] end if next(extra) == nil then extra = nil end return {entrance = entrance, extratags = extra} end end return nil end else local notags = {} if data.extra_exclude ~= nil then for _, v in pairs(data.extra_exclude) do notags[v] = 1 end end for _, v in pairs(data.main_tags) do notags[v] = 1 end ENTRANCE_FUNCTION = function(o) for _, v in ipairs(data.main_tags) do if o.tags[v] ~= nil then local entrance = o.tags[v] local extra = {} for k, v in pairs(o.tags) do if notags[k] ~= 1 then extra[k] = v end end if next(extra) == nil then extra = nil end return {entrance = entrance, extratags = extra} end end return nil end end end end end function module.get_taginfo() return {main = MAIN_KEYS, name = NAMES, address = ADDRESS_TAGS} end return module ================================================ FILE: lib-lua/themes/nominatim/presets.lua ================================================ -- Defines defaults used in the topic definitions. local module = {} -- Helper functions local function group_merge(group1, group2) for name, values in pairs(group2) do if group1[name] == nil then group1[name] = values else for _, v in pairs(values) do table.insert(group1[name], v) end end end return group1 end -- Customized main tag filter functions local EXCLUDED_FOOTWAYS = { sidewalk = 1, crossing = 1, link = 1, traffic_aisle } local function filter_footways(place) if place.has_name then local footway = place.object.tags.footway if footway == nil or EXCLUDED_FOOTWAYS[footway] ~= 1 then return place end end return false end local function include_when_tag_present(key, value, named) if named then return function(place) if place.has_name and place.intags[key] == value then return place end return false end else return function(place) if place.intags[key] == value then return place end return false end end end local function exclude_when_key_present(key, named) if named then return function(place) if place.has_name and place.intags[key] == nil then return place end return false end else return function(place) if place.intags[key] == nil then return place end return false end end end local function lock_transform(place) if place.object.tags.waterway ~= nil then local name = place.object.tags.lock_name if name ~= nil then return place:clone{names={name=name, ref=place.object.tags.lock_ref}} end end return false end -- Main tag definition module.MAIN_TAGS = {} module.MAIN_TAGS.admin = { boundary = {administrative = 'named'}, landuse = {residential = 'fallback', farm = 'fallback', farmyard = 'fallback', industrial = 'fallback', commercial = 'fallback', allotments = 'fallback', retail = 'fallback'}, place = {county = 'always', district = 'always', municipality = 'always', city = 'always', town = 'always', borough = 'always', village = 'always', suburb = 'always', hamlet = 'always', croft = 'always', subdivision = 'always', allotments = 'always', neighbourhood = 'always', quarter = 'always', isolated_dwelling = 'always', farm = 'always', city_block = 'always', locality = 'always'} } module.MAIN_TAGS.all_boundaries = { boundary = {'named', place = 'delete', land_area = 'delete', protected_area = 'fallback', postal_code = 'postcode_area'}, landuse = 'fallback', place = 'always' } module.MAIN_TAGS.natural = { waterway = {'named', riverbank = 'delete'}, natural = {'named', yes = 'delete', no = 'delete', coastline = 'delete', saddle = 'fallback', water = exclude_when_key_present('water', true)}, mountain_pass = {'always', no = 'delete'}, water = {include_when_tag_present('natural', 'water', true), river = 'never', stream = 'never', canal = 'never', ditch = 'never', drain = 'never', fish_pass = 'never', yes = 'delete', intermittent = 'delete', tidal = 'delete' } } module.MAIN_TAGS_POIS = function (group) group = group or 'delete' return { aerialway = {'always', no = group, pylon = group}, aeroway = {'always', no = group}, amenity = {'always', no = group, parking_space = group, parking_entrance = group, waste_disposal = group, hunting_stand = group}, building = {'fallback', no = group}, bridge = {'named_with_key', no = group}, club = {'always', no = group}, craft = {'always', no = group}, emergency = {'always', no = group, yes = group, fire_hydrant = group}, healthcare = {'fallback', yes = group, no = group}, highway = {'always', no = group, turning_circle = group, mini_roundabout = group, noexit = group, crossing = group, give_way = group, stop = group, turning_loop = group, passing_place = group, street_lamp = 'named', traffic_signals = 'named'}, historic = {'fallback', yes = group, no = group}, information = {include_when_tag_present('tourism', 'information'), yes = 'delete', route_marker = 'never', trail_blaze = 'never'}, junction = {'fallback', no = group}, landuse = {cemetery = 'always'}, leisure = {'always', nature_reserve = 'named', swimming_pool = 'named', garden = 'named', common = 'named', no = group}, lock = {yes = lock_transform}, man_made = {pier = 'always', tower = 'always', bridge = 'always', works = 'named', water_tower = 'always', dyke = 'named', adit = 'named', lighthouse = 'always', watermill = 'always', tunnel = 'always'}, military = {'always', yes = group, no = group}, office = {'always', no = group}, railway = {'named', rail = group, no = group, abandoned = group, disused = group, razed = group, level_crossing = group, switch = group, signal = group, buffer_stop = group}, shop = {'always', no = group}, tourism = {'always', attraction = 'fallback', no = group, yes = group, information = exclude_when_key_present('information')}, tunnel = {'named_with_key', no = group} } end module.MAIN_TAGS_STREETS = {} module.MAIN_TAGS_STREETS.default = { place = {square = 'always'}, highway = {motorway = 'always', trunk = 'always', primary = 'always', secondary = 'always', tertiary = 'always', unclassified = 'always', residential = 'always', road = 'always', living_street = 'always', pedestrian = 'always', service = 'named', cycleway = 'named', path = 'named', footway = filter_footways, steps = 'named', bridleway = 'named', track = 'named', motorway_link = 'named', trunk_link = 'named', primary_link = 'named', secondary_link = 'named', tertiary_link = 'named'} } module.MAIN_TAGS_STREETS.car = { place = {square = 'always'}, highway = {motorway = 'always', trunk = 'always', primary = 'always', secondary = 'always', tertiary = 'always', unclassified = 'always', residential = 'always', road = 'always', living_street = 'always', service = 'always', track = 'always', motorway_link = 'always', trunk_link = 'always', primary_link = 'always', secondary_link = 'always', tertiary_link = 'always'} } module.MAIN_TAGS_STREETS.all = { place = {square = 'always'}, highway = {motorway = 'always', trunk = 'always', primary = 'always', secondary = 'always', tertiary = 'always', unclassified = 'always', residential = 'always', road = 'always', living_street = 'always', pedestrian = 'always', service = 'always', cycleway = 'always', path = 'always', footway = 'always', steps = 'always', bridleway = 'always', track = 'always', motorway_link = 'always', trunk_link = 'always', primary_link = 'always', secondary_link = 'always', tertiary_link = 'always'} } -- name tags module.NAME_TAGS = {} module.NAME_TAGS.core = {main = {'name', 'name:*', 'int_name', 'int_name:*', 'reg_name', 'reg_name:*', 'loc_name', 'loc_name:*', 'old_name', 'old_name:*', 'alt_name', 'alt_name:*', 'alt_name_*', 'official_name', 'official_name:*', 'place_name', 'place_name:*', 'short_name', 'short_name:*'}, extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref', 'loc_ref', 'old_ref', 'ISO3166-2'} } module.NAME_TAGS.address = {house = {'addr:housename'}} module.NAME_TAGS.poi = group_merge({main = {'brand'}, extra = {'iata', 'icao', 'faa'}}, module.NAME_TAGS.core) -- Address tagging module.ADDRESS_TAGS = {} module.ADDRESS_TAGS.core = { extra = {'addr:*', 'is_in:*', 'tiger:county'}, postcode = {'postal_code', 'postcode', 'addr:postcode', 'tiger:zip_left', 'tiger:zip_right'}, country = {'country_code', 'ISO3166-1', 'addr:country_code', 'is_in:country_code', 'addr:country', 'is_in:country'} } module.ADDRESS_TAGS.houses = { main = {'addr:housenumber', 'addr:conscriptionnumber', 'addr:streetnumber'}, interpolation = {'addr:interpolation'} } -- Ignored tags (prefiltered away) module.IGNORE_KEYS = {} module.IGNORE_KEYS.metatags = {'note', 'note:*', 'source', 'source:*', '*source', 'attribution', 'comment', 'fixme', 'created_by', 'tiger:cfcc', 'tiger:reviewed', 'nysgissam:*', 'NHD:*', 'nhd:*', 'gnis:*', 'geobase:*', 'yh:*', 'osak:*', 'naptan:*', 'CLC:*', 'import', 'it:fvg:*', 'lacounty:*', 'ref:linz:*', 'survey:*', 'ref:bygningsnr', 'ref:ruian:*', 'building:ruian:type', 'type', 'is_in:postcode'} module.IGNORE_KEYS.name = {'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*', 'name:etymology', 'name:etymology:*', 'name:signed', 'name:botanical'} module.IGNORE_KEYS.address = {'addr:street:*', 'addr:city:*', 'addr:district:*', 'addr:province:*', 'addr:subdistrict:*', 'addr:place:*', 'addr:TW:dataset'} -- INTERNAL: Required extra tags module.EXTRATAGS = {keys = {'wikipedia', 'wikipedia:*', 'wikidata', 'capital'}} -- Defaults for the entrance table module.ENTRANCE_TABLE = {} module.ENTRANCE_TABLE.default = {main_tags = {'entrance', 'routing:entrance'}, extra_exclude = module.IGNORE_KEYS.metatags} return module ================================================ FILE: lib-lua/themes/nominatim/topics/address.lua ================================================ local _, flex, cfg = ... flex.set_main_tags('admin') flex.modify_main_tags('street/' .. (cfg.street_theme or 'default')) flex.modify_main_tags{boundary = {postal_code = 'always'}} flex.set_name_tags('core') flex.modify_name_tags('address') flex.set_address_tags('core') flex.modify_address_tags('houses') flex.ignore_keys('metatags') if cfg.with_extratags then flex.set_unused_handling{delete_keys = {'tiger:*'}} flex.add_for_extratags('name') flex.add_for_extratags('address') else flex.ignore_keys('name') flex.ignore_keys('address') end ================================================ FILE: lib-lua/themes/nominatim/topics/admin.lua ================================================ local _, flex, cfg = ... flex.set_main_tags('admin') flex.set_name_tags('core') flex.set_address_tags('core') flex.set_postcode_fallback(false) flex.ignore_keys('metatags') if cfg.with_extratags then flex.set_unused_handling{delete_keys = {'tiger:*'}} flex.add_for_extratags('name') flex.add_for_extratags('address') else flex.ignore_keys('name') flex.ignore_keys('address') end ================================================ FILE: lib-lua/themes/nominatim/topics/full.lua ================================================ local _, flex, cfg = ... local group if cfg.with_extratags then group = 'extra' else group = 'delete' end flex.set_main_tags('all_boundaries') flex.modify_main_tags('natural') flex.modify_main_tags('street/' .. (cfg.street_theme or 'default')) flex.modify_main_tags('poi/' .. group) flex.set_name_tags('core') flex.modify_name_tags('address') flex.modify_name_tags('poi') flex.set_address_tags('core') flex.modify_address_tags('houses') flex.ignore_keys('metatags') if cfg.with_extratags then flex.set_unused_handling{delete_keys = {'tiger:*'}} flex.add_for_extratags('name') flex.add_for_extratags('address') else flex.ignore_keys('name') flex.ignore_keys('address') end flex.set_entrance_filter('default') ================================================ FILE: lib-lua/themes/nominatim/topics/street.lua ================================================ local _, flex, cfg = ... flex.set_main_tags('admin') flex.modify_main_tags('street/' .. (cfg.street_theme or 'default')) flex.modify_main_tags{boundary = {postal_code = 'always'}} flex.set_name_tags('core') flex.set_address_tags('core') flex.set_postcode_fallback(false) flex.ignore_keys('metatags') if cfg.with_extratags then flex.set_unused_handling{delete_keys = {'tiger:*'}} flex.add_for_extratags('name') flex.add_for_extratags('address') else flex.ignore_keys('name') flex.ignore_keys('address') end ================================================ FILE: lib-sql/functions/associated_street_triggers.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Trigger functions for associated street relations. -- Invalidates house members of associatedStreet relations -- whenever the place_associated_street table is modified. -- osm2pgsql flex handles updates as DELETE-all + re-INSERT, so each -- row-level trigger call covers exactly one member. CREATE OR REPLACE FUNCTION invalidate_associated_street_members() RETURNS TRIGGER AS $$ DECLARE object RECORD; BEGIN IF TG_OP = 'DELETE' THEN object := OLD; ELSE object := NEW; END IF; IF object.member_role = 'house' THEN {% for otype in ('N', 'W', 'R') %} IF object.member_type = '{{ otype }}' THEN UPDATE placex SET indexed_status = 2 WHERE osm_type = '{{ otype }}' AND osm_id = object.member_id AND indexed_status = 0; END IF; {% endfor %} END IF; RETURN object; END; $$ LANGUAGE plpgsql; ================================================ FILE: lib-sql/functions/importance.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Functions for interpreting wkipedia/wikidata tags and computing importance. DROP TYPE IF EXISTS wikipedia_article_match CASCADE; CREATE TYPE wikipedia_article_match as ( language TEXT, title TEXT, importance FLOAT ); DROP TYPE IF EXISTS place_importance CASCADE; CREATE TYPE place_importance as ( importance FLOAT, wikipedia TEXT ); {% if 'wikimedia_importance' in db.tables %} CREATE OR REPLACE FUNCTION get_wikipedia_match(extratags HSTORE, country_code varchar(2)) RETURNS wikipedia_article_match AS $$ DECLARE i INT; wiki_article_title TEXT; wiki_article_language TEXT; result wikipedia_article_match; entry RECORD; BEGIN IF extratags ? 'wikipedia' and strpos(extratags->'wikipedia', ':') IN (3,4) THEN wiki_article_language := lower(trim(split_part(extratags->'wikipedia', ':', 1))); wiki_article_title := trim(substr(extratags->'wikipedia', strpos(extratags->'wikipedia', ':') + 1)); FOR result IN SELECT language, title, importance FROM wikimedia_importance WHERE language = wiki_article_language and title = replace(wiki_article_title, ' ', '_') LOOP RETURN result; END LOOP; END IF; FOREACH wiki_article_language IN ARRAY ARRAY['ar','bg','ca','cs','da','de','en','es','eo','eu','fa','fr','ko','hi','hr','id','it','he','lt','hu','ms','nl','ja','no','pl','pt','kk','ro','ru','sk','sl','sr','fi','sv','tr','uk','vi','vo','war','zh'] LOOP IF extratags ? ('wikipedia:' || wiki_article_language) THEN wiki_article_title := extratags->('wikipedia:' || wiki_article_language); FOR result IN SELECT language, title, importance FROM wikimedia_importance WHERE language = wiki_article_language and title = replace(wiki_article_title, ' ', '_') LOOP RETURN result; END LOOP; END IF; END LOOP; RETURN NULL; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; {% else %} -- See: http://stackoverflow.com/questions/6410088/how-can-i-mimic-the-php-urldecode-function-in-postgresql CREATE OR REPLACE FUNCTION decode_url_part(p varchar) RETURNS varchar AS $$ SELECT convert_from(CAST(E'\\x' || array_to_string(ARRAY( SELECT CASE WHEN length(r.m[1]) = 1 THEN encode(convert_to(r.m[1], 'SQL_ASCII'), 'hex') ELSE substring(r.m[1] from 2 for 2) END FROM regexp_matches($1, '%[0-9a-f][0-9a-f]|.', 'gi') AS r(m) ), '') AS bytea), 'UTF8'); $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; CREATE OR REPLACE FUNCTION catch_decode_url_part(p varchar) RETURNS varchar AS $$ DECLARE BEGIN RETURN decode_url_part(p); EXCEPTION WHEN others THEN return null; END; $$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE; CREATE OR REPLACE FUNCTION get_wikipedia_match(extratags HSTORE, country_code varchar(2)) RETURNS wikipedia_article_match AS $$ DECLARE langs TEXT[]; i INT; wiki_article TEXT; wiki_article_title TEXT; wiki_article_language TEXT; result wikipedia_article_match; BEGIN langs := ARRAY['english','country','ar','bg','ca','cs','da','de','en','es','eo','eu','fa','fr','ko','hi','hr','id','it','he','lt','hu','ms','nl','ja','no','pl','pt','kk','ro','ru','sk','sl','sr','fi','sv','tr','uk','vi','vo','war','zh']; i := 1; WHILE langs[i] IS NOT NULL LOOP wiki_article := extratags->(case when langs[i] in ('english','country') THEN 'wikipedia' ELSE 'wikipedia:'||langs[i] END); IF wiki_article is not null THEN wiki_article := replace(wiki_article,' ','_'); IF strpos(wiki_article, ':') IN (3,4) THEN wiki_article_language := lower(trim(split_part(wiki_article, ':', 1))); wiki_article_title := trim(substr(wiki_article, strpos(wiki_article, ':')+1)); ELSE wiki_article_title := trim(wiki_article); wiki_article_language := CASE WHEN langs[i] = 'english' THEN 'en' WHEN langs[i] = 'country' THEN get_country_language_code(country_code) ELSE langs[i] END; END IF; select wikipedia_article.language,wikipedia_article.title,wikipedia_article.importance from wikipedia_article where language = wiki_article_language and (title = wiki_article_title OR title = catch_decode_url_part(wiki_article_title) OR title = replace(catch_decode_url_part(wiki_article_title),E'\\','')) UNION ALL select wikipedia_article.language,wikipedia_article.title,wikipedia_article.importance from wikipedia_redirect join wikipedia_article on (wikipedia_redirect.language = wikipedia_article.language and wikipedia_redirect.to_title = wikipedia_article.title) where wikipedia_redirect.language = wiki_article_language and (from_title = wiki_article_title OR from_title = catch_decode_url_part(wiki_article_title) OR from_title = replace(catch_decode_url_part(wiki_article_title),E'\\','')) order by importance desc limit 1 INTO result; IF result.language is not null THEN return result; END IF; END IF; i := i + 1; END LOOP; RETURN NULL; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; {% endif %} CREATE OR REPLACE FUNCTION compute_importance(extratags HSTORE, country_code varchar(2), rank_search SMALLINT, centroid GEOMETRY) RETURNS place_importance AS $$ DECLARE match RECORD; result place_importance; osm_views_exists BIGINT; views BIGINT; BEGIN -- add importance by wikipedia article if the place has one FOR match IN SELECT * FROM get_wikipedia_match(extratags, country_code) WHERE language is not NULL LOOP result.importance := match.importance; result.wikipedia := match.language || ':' || match.title; RETURN result; END LOOP; -- Nothing? Then try with the wikidata tag. IF extratags ? 'wikidata' THEN FOR match IN {% if 'wikimedia_importance' in db.tables %} SELECT * FROM wikimedia_importance WHERE wikidata = extratags->'wikidata' LIMIT 1 {% else %} SELECT * FROM wikipedia_article WHERE wd_page_title = extratags->'wikidata' ORDER BY language = 'en' DESC, langcount DESC LIMIT 1 {% endif %} LOOP result.importance := match.importance; result.wikipedia := match.language || ':' || match.title; RETURN result; END LOOP; END IF; -- Still nothing? Fall back to a default. result.importance := 0.40001 - (rank_search::float / 75); {% if 'secondary_importance' in db.tables %} FOR match IN SELECT ST_Value(rast, centroid) as importance FROM secondary_importance WHERE ST_Intersects(ST_ConvexHull(rast), centroid) LIMIT 1 LOOP IF match.importance is not NULL THEN -- Secondary importance as tie breaker with 0.0001 weight. result.importance := result.importance + match.importance::float / 655350000; END IF; END LOOP; {% endif %} RETURN result; END; $$ LANGUAGE plpgsql PARALLEL SAFE; ================================================ FILE: lib-sql/functions/interpolation.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Functions for address interpolation objects in location_property_osmline. CREATE OR REPLACE FUNCTION place_interpolation_insert() RETURNS TRIGGER AS $$ DECLARE existing RECORD; existingplacex BIGINT[]; BEGIN IF NOT (NEW.type in ('odd', 'even', 'all') OR NEW.type similar to '[1-9]') THEN -- the new interpolation is illegal, simply remove existing entries DELETE FROM location_property_osmline o WHERE o.osm_id = NEW.osm_id; RETURN NULL; END IF; -- Remove the place from the list of places to be deleted DELETE FROM place_interpolation_to_be_deleted pdel WHERE pdel.osm_id = NEW.osm_id; SELECT * INTO existing FROM place_interpolation p WHERE p.osm_id = NEW.osm_id; -- Get the existing entry from the interpolation table. SELECT array_agg(place_id) INTO existingplacex FROM location_property_osmline o WHERE o.osm_id = NEW.osm_id; IF array_length(existingplacex, 1) is NULL THEN INSERT INTO location_property_osmline (osm_id, type, address, linegeo) VALUES (NEW.osm_id, NEW.type, NEW.address, NEW.geometry); ELSE -- Update the interpolation table: -- The first entry gets the original data, all other entries -- are removed and will be recreated on indexing. -- (An interpolation can be split up, if it has more than 2 address nodes) -- Update unconditionally here as the changes might be coming from the -- nodes on the interpolation. UPDATE location_property_osmline SET type = NEW.type, address = NEW.address, linegeo = NEW.geometry, startnumber = null, indexed_status = 1 WHERE place_id = existingplacex[1]; IF array_length(existingplacex, 1) > 1 THEN DELETE FROM location_property_osmline WHERE place_id = any(existingplacex[2:]); END IF; END IF; -- need to invalidate nodes because they might copy address info IF NEW.address is not NULL AND (existing.osm_id is NULL OR coalesce(existing.address, ''::hstore) != NEW.address) THEN UPDATE placex SET indexed_status = 2 WHERE osm_type = 'N' AND osm_id = ANY(NEW.nodes) AND indexed_status = 0; END IF; -- finally update/insert place_interpolation itself IF existing.osm_id is not NULL THEN -- Always updates as the nodes with the housenumber might be the reason -- for the change. UPDATE place_interpolation p SET type = NEW.type, address = NEW.address, nodes = NEW.nodes, geometry = NEW.geometry WHERE p.osm_id = NEW.osm_id; RETURN NULL; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_interpolation_delete() RETURNS TRIGGER AS $$ DECLARE deferred BOOLEAN; BEGIN {% if debug %}RAISE WARNING 'Delete for interpolation %', OLD.osm_id;{% endif %} INSERT INTO place_interpolation_to_be_deleted (osm_id) VALUES(OLD.osm_id); RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION get_interpolation_address(in_address HSTORE, wayid BIGINT) RETURNS HSTORE AS $$ DECLARE location RECORD; waynodes BIGINT[]; BEGIN IF in_address ? 'street' or in_address ? 'place' THEN RETURN in_address; END IF; SELECT nodes INTO waynodes FROM place_interpolation WHERE osm_id = wayid; IF array_upper(waynodes, 1) IS NOT NULL THEN FOR location IN SELECT placex.address, placex.osm_id FROM placex WHERE osm_type = 'N' and osm_id = ANY(waynodes) and placex.address is not null and (placex.address ? 'street' or placex.address ? 'place') and indexed_status < 100 LOOP -- mark it as a derived address RETURN location.address || coalesce(in_address, ''::hstore) || hstore('_inherited', ''); END LOOP; END IF; RETURN in_address; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; -- find the parent road of the cut road parts CREATE OR REPLACE FUNCTION get_interpolation_parent(token_info JSONB, partition SMALLINT, centroid GEOMETRY, geom GEOMETRY) RETURNS BIGINT AS $$ DECLARE parent_place_id BIGINT; location RECORD; BEGIN parent_place_id := find_parent_for_address(token_info, partition, centroid); IF parent_place_id is null THEN FOR location IN SELECT place_id FROM placex WHERE ST_DWithin(geom, placex.geometry, 0.001) and placex.rank_search = 26 and placex.osm_type = 'W' -- needed for index selection ORDER BY CASE WHEN ST_GeometryType(geom) = 'ST_Line' THEN (ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,0))+ ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,0.5))+ ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,1))) ELSE ST_distance(placex.geometry, geom) END ASC LIMIT 1 LOOP parent_place_id := location.place_id; END LOOP; END IF; RETURN parent_place_id; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION osmline_insert() RETURNS TRIGGER AS $$ DECLARE centroid GEOMETRY; BEGIN NEW.place_id := nextval('seq_place'); NEW.indexed_date := now(); IF NEW.indexed_status IS NULL THEN IF NOT(NEW.type in ('odd', 'even', 'all') OR NEW.type similar to '[1-9]') THEN -- alphabetic interpolation is not supported RETURN NULL; END IF; centroid := get_center_point(NEW.linegeo); NEW.indexed_status := 1; --STATUS_NEW NEW.country_code := lower(get_country_code(centroid)); NEW.partition := get_partition(NEW.country_code); NEW.geometry_sector := geometry_sector(NEW.partition, centroid); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION osmline_update() RETURNS TRIGGER AS $$ DECLARE waynodes BIGINT[]; prevnode RECORD; nextnode RECORD; startnumber INTEGER; endnumber INTEGER; newstart INTEGER; newend INTEGER; moddiff SMALLINT; linegeo GEOMETRY; splitpoint FLOAT; sectiongeo GEOMETRY; postcode TEXT; stepmod SMALLINT; BEGIN -- deferred delete IF OLD.indexed_status = 100 THEN delete from location_property_osmline where place_id = OLD.place_id; RETURN NULL; END IF; IF NEW.indexed_status != 0 OR OLD.indexed_status = 0 THEN RETURN NEW; END IF; NEW.parent_place_id := get_interpolation_parent(NEW.token_info, NEW.partition, get_center_point(NEW.linegeo), NEW.linegeo); NEW.token_info := token_strip_info(NEW.token_info); IF NEW.address ? '_inherited' THEN NEW.address := NULL; END IF; -- If the line was newly inserted, split the line as necessary. IF NEW.parent_place_id is not NULL AND NEW.startnumber is NULL THEN IF NEW.type in ('odd', 'even') THEN NEW.step := 2; stepmod := CASE WHEN NEW.type = 'odd' THEN 1 ELSE 0 END; ELSE NEW.step := CASE WHEN NEW.type = 'all' THEN 1 ELSE (NEW.type)::SMALLINT END; stepmod := NULL; END IF; SELECT nodes INTO waynodes FROM place_interpolation WHERE osm_id = NEW.osm_id; IF array_upper(waynodes, 1) IS NULL THEN RETURN NEW; END IF; linegeo := null; SELECT null::integer as hnr INTO prevnode; -- Go through all nodes on the interpolation line that have a housenumber. FOR nextnode IN SELECT DISTINCT ON (nodeidpos) osm_id, address, geometry, -- Take the postcode from the node only if it has a housenumber itself. -- Note that there is a corner-case where the node has a wrongly -- formatted postcode and therefore 'postcode' contains a derived -- variant. CASE WHEN address ? 'postcode' THEN placex.postcode ELSE NULL::text END as postcode, (address->'housenumber')::integer as hnr FROM placex, generate_series(1, array_upper(waynodes, 1)) nodeidpos WHERE osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT and address is not NULL and address ? 'housenumber' and address->'housenumber' ~ '^[0-9]{1,6}$' and ST_Distance(NEW.linegeo, geometry) < 0.0005 ORDER BY nodeidpos LOOP {% if debug %}RAISE WARNING 'processing point % (%)', nextnode.hnr, ST_AsText(nextnode.geometry);{% endif %} IF linegeo is null THEN linegeo := NEW.linegeo; ELSE splitpoint := ST_LineLocatePoint(linegeo, nextnode.geometry); IF splitpoint = 0 THEN -- Corner case where the splitpoint falls on the first point -- and thus would not return a geometry. Skip that section. sectiongeo := NULL; ELSEIF splitpoint = 1 THEN -- Point is at the end of the line. sectiongeo := linegeo; linegeo := NULL; ELSE -- Split the line. sectiongeo := ST_LineSubstring(linegeo, 0, splitpoint); linegeo := ST_LineSubstring(linegeo, splitpoint, 1); END IF; END IF; IF prevnode.hnr is not null -- Check if there are housenumbers to interpolate between the -- regularly mapped housenumbers. -- (Conveniently also fails if one of the house numbers is not a number.) and abs(prevnode.hnr - nextnode.hnr) > NEW.step -- If the interpolation geometry is broken or two nodes are at the -- same place, then splitting might produce a point. Ignore that. and ST_GeometryType(sectiongeo) = 'ST_LineString' THEN IF prevnode.hnr < nextnode.hnr THEN startnumber := prevnode.hnr; endnumber := nextnode.hnr; ELSE startnumber := nextnode.hnr; endnumber := prevnode.hnr; sectiongeo := ST_Reverse(sectiongeo); END IF; -- Adjust the interpolation, so that only inner housenumbers -- are taken into account. IF stepmod is null THEN newstart := startnumber + NEW.step; ELSE newstart := startnumber + 1; moddiff := newstart % NEW.step - stepmod; IF moddiff < 0 THEN newstart := newstart + (NEW.step + moddiff); ELSE newstart := newstart + moddiff; END IF; END IF; newend := newstart + ((endnumber - 1 - newstart) / NEW.step) * NEW.step; -- If newstart and newend are the same, then this returns a point. sectiongeo := ST_LineSubstring(sectiongeo, (newstart - startnumber)::float / (endnumber - startnumber)::float, (newend - startnumber)::float / (endnumber - startnumber)::float); startnumber := newstart; endnumber := newend; -- determine postcode postcode := coalesce(prevnode.postcode, nextnode.postcode, postcode); IF postcode is NULL and NEW.parent_place_id > 0 THEN SELECT placex.postcode FROM placex WHERE place_id = NEW.parent_place_id INTO postcode; END IF; IF postcode is NULL THEN postcode := get_nearest_postcode(NEW.country_code, nextnode.geometry); END IF; -- Add the interpolation. If this is the first segment, just modify -- the interpolation to be inserted, otherwise add an additional one -- (marking it indexed already). IF NEW.startnumber IS NULL THEN NEW.startnumber := startnumber; NEW.endnumber := endnumber; NEW.linegeo := ST_ReducePrecision(sectiongeo, 0.0000001); NEW.postcode := postcode; ELSE INSERT INTO location_property_osmline (linegeo, partition, osm_id, parent_place_id, startnumber, endnumber, step, type, address, postcode, country_code, geometry_sector, indexed_status) VALUES (ST_ReducePrecision(sectiongeo, 0.0000001), NEW.partition, NEW.osm_id, NEW.parent_place_id, startnumber, endnumber, NEW.step, NEW.type, NEW.address, postcode, NEW.country_code, NEW.geometry_sector, 0); END IF; END IF; -- early break if we are out of line string, -- might happen when a line string loops back on itself IF linegeo is null or ST_GeometryType(linegeo) != 'ST_LineString' THEN RETURN NEW; END IF; prevnode := nextnode; END LOOP; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; ================================================ FILE: lib-sql/functions/partition-functions.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TYPE IF EXISTS nearfeaturecentr CASCADE; CREATE TYPE nearfeaturecentr AS ( place_id BIGINT, keywords int[], rank_address smallint, rank_search smallint, distance float, isguess boolean, postcode TEXT, centroid GEOMETRY ); CREATE OR REPLACE function getNearFeatures(in_partition INTEGER, feature GEOMETRY, feature_centroid GEOMETRY, maxrank INTEGER) RETURNS setof nearfeaturecentr AS $$ DECLARE r nearfeaturecentr%rowtype; BEGIN {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN FOR r IN SELECT place_id, keywords, rank_address, rank_search, CASE WHEN isguess THEN ST_Distance(feature, centroid) ELSE min(ST_Distance(feature_centroid, geometry)) -- tie breaker when distance is the same (i.e. way is on boundary) + 0.00001 * ST_Distance(feature, centroid) END as distance, isguess, postcode, centroid FROM location_area_large_{{ partition }} WHERE geometry && feature AND CASE WHEN ST_Dimension(feature) = 0 THEN _ST_Covers(geometry, feature) WHEN ST_Dimension(feature) = 2 THEN ST_Relate(geometry, feature, 'T********') ELSE ST_NPoints(ST_Intersection(geometry, feature)) > 1 END AND rank_address < maxrank -- Postcodes currently still use rank_search to define for which -- features they are relevant. AND not (rank_address in (5, 11) and rank_search > maxrank) GROUP BY place_id, keywords, rank_address, rank_search, isguess, postcode, centroid LOOP RETURN NEXT r; END LOOP; RETURN; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; END $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION get_address_place(in_partition SMALLINT, feature GEOMETRY, from_rank SMALLINT, to_rank SMALLINT, extent FLOAT, token_info JSONB, key TEXT) RETURNS nearfeaturecentr AS $$ DECLARE r nearfeaturecentr%rowtype; BEGIN {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN SELECT place_id, keywords, rank_address, rank_search, min(ST_Distance(feature, centroid)) as distance, isguess, postcode, centroid INTO r FROM location_area_large_{{ partition }} WHERE geometry && ST_Expand(feature, extent) AND rank_address between from_rank and to_rank AND token_matches_address(token_info, key, keywords) GROUP BY place_id, keywords, rank_address, rank_search, isguess, postcode, centroid ORDER BY bool_or(ST_Intersects(geometry, feature)) DESC, distance LIMIT 1; RETURN r; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; create or replace function deleteLocationArea(in_partition INTEGER, in_place_id BIGINT, in_rank_search INTEGER) RETURNS BOOLEAN AS $$ DECLARE BEGIN IF in_rank_search <= 4 THEN DELETE from location_area_country WHERE place_id = in_place_id; RETURN TRUE; END IF; {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN DELETE from location_area_large_{{ partition }} WHERE place_id = in_place_id; RETURN TRUE; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; RETURN FALSE; END $$ LANGUAGE plpgsql; create or replace function insertLocationAreaLarge( in_partition INTEGER, in_place_id BIGINT, in_country_code VARCHAR(2), in_keywords INTEGER[], in_rank_search INTEGER, in_rank_address INTEGER, in_estimate BOOLEAN, postcode TEXT, in_centroid GEOMETRY, in_geometry GEOMETRY) RETURNS BOOLEAN AS $$ DECLARE BEGIN IF in_rank_address = 0 THEN RETURN TRUE; END IF; IF in_rank_search <= 4 THEN IF not in_estimate and in_country_code is not NULL THEN INSERT INTO location_area_country (place_id, country_code, geometry) (SELECT in_place_id, in_country_code, geom FROM split_geometry(in_geometry) as geom); END IF; RETURN TRUE; END IF; {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN INSERT INTO location_area_large_{{ partition }} (partition, place_id, country_code, keywords, rank_search, rank_address, isguess, postcode, centroid, geometry) (SELECT in_partition, in_place_id, in_country_code, in_keywords, in_rank_search, in_rank_address, in_estimate, postcode, in_centroid, geom FROM split_geometry(in_geometry) as geom); RETURN TRUE; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; RETURN FALSE; END $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION getNearestNamedRoadPlaceId(in_partition INTEGER, point GEOMETRY, token_info JSONB) RETURNS BIGINT AS $$ DECLARE parent BIGINT; BEGIN IF not token_has_addr_street(token_info) THEN RETURN NULL; END IF; {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN SELECT place_id FROM search_name_{{ partition }} INTO parent WHERE token_matches_street(token_info, name_vector) AND centroid && ST_Expand(point, 0.015) AND address_rank between 26 and 27 ORDER BY ST_Distance(centroid, point) ASC limit 1; RETURN parent; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; END $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION getNearestNamedPlacePlaceId(in_partition INTEGER, point GEOMETRY, token_info JSONB) RETURNS BIGINT AS $$ DECLARE parent BIGINT; BEGIN IF not token_has_addr_place(token_info) THEN RETURN NULL; END IF; {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN SELECT place_id INTO parent FROM search_name_{{ partition }} WHERE token_matches_place(token_info, name_vector) AND centroid && ST_Expand(point, 0.04) AND address_rank between 16 and 25 ORDER BY ST_Distance(centroid, point) ASC limit 1; RETURN parent; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; END $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; create or replace function insertSearchName( in_partition INTEGER, in_place_id BIGINT, in_name_vector INTEGER[], in_rank_search INTEGER, in_rank_address INTEGER, in_geometry GEOMETRY) RETURNS BOOLEAN AS $$ DECLARE BEGIN {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN IF in_rank_address > 0 THEN INSERT INTO search_name_{{ partition }} (place_id, address_rank, name_vector, centroid) values (in_place_id, in_rank_address, in_name_vector, in_geometry); END IF; RETURN TRUE; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; RETURN FALSE; END $$ LANGUAGE plpgsql; create or replace function deleteSearchName(in_partition INTEGER, in_place_id BIGINT) RETURNS BOOLEAN AS $$ DECLARE BEGIN {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN DELETE from search_name_{{ partition }} WHERE place_id = in_place_id; RETURN TRUE; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; RETURN FALSE; END $$ LANGUAGE plpgsql; create or replace function insertLocationRoad( in_partition INTEGER, in_place_id BIGINT, in_country_code VARCHAR(2), in_geometry GEOMETRY) RETURNS BOOLEAN AS $$ DECLARE BEGIN {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN INSERT INTO location_road_{{ partition }} (partition, place_id, country_code, geometry) values (in_partition, in_place_id, in_country_code, in_geometry); RETURN TRUE; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; RETURN FALSE; END $$ LANGUAGE plpgsql; create or replace function deleteRoad(in_partition INTEGER, in_place_id BIGINT) RETURNS BOOLEAN AS $$ DECLARE BEGIN {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN DELETE FROM location_road_{{ partition }} where place_id = in_place_id; RETURN TRUE; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; RETURN FALSE; END $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION getNearestRoadPlaceId(in_partition INTEGER, point GEOMETRY) RETURNS BIGINT AS $$ DECLARE r RECORD; search_diameter FLOAT; BEGIN {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN search_diameter := 0.00005; WHILE search_diameter < 0.1 LOOP FOR r IN SELECT place_id FROM location_road_{{ partition }} WHERE ST_DWithin(geometry, point, search_diameter) ORDER BY ST_Distance(geometry, point) ASC limit 1 LOOP RETURN r.place_id; END LOOP; search_diameter := search_diameter * 2; END LOOP; RETURN NULL; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; END $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION getNearestParallelRoadFeature(in_partition INTEGER, line GEOMETRY) RETURNS BIGINT AS $$ DECLARE r RECORD; search_diameter FLOAT; p1 GEOMETRY; p2 GEOMETRY; p3 GEOMETRY; BEGIN IF ST_GeometryType(line) not in ('ST_LineString') THEN RETURN NULL; END IF; p1 := ST_LineInterpolatePoint(line,0); p2 := ST_LineInterpolatePoint(line,0.5); p3 := ST_LineInterpolatePoint(line,1); {% for partition in db.partitions %} IF in_partition = {{ partition }} THEN search_diameter := 0.0005; WHILE search_diameter < 0.01 LOOP FOR r IN SELECT place_id FROM location_road_{{ partition }} WHERE ST_DWithin(line, geometry, search_diameter) ORDER BY (ST_distance(geometry, p1)+ ST_distance(geometry, p2)+ ST_distance(geometry, p3)) ASC limit 1 LOOP RETURN r.place_id; END LOOP; search_diameter := search_diameter * 2; END LOOP; RETURN NULL; END IF; {% endfor %} RAISE EXCEPTION 'Unknown partition %', in_partition; END $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; ================================================ FILE: lib-sql/functions/place_triggers.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. CREATE OR REPLACE FUNCTION place_insert() RETURNS TRIGGER AS $$ DECLARE existing RECORD; existingplacex RECORD; newplacex RECORD; is_area BOOLEAN; existing_is_area BOOLEAN; area_size FLOAT; address_rank SMALLINT; search_rank SMALLINT; BEGIN {% if debug %} RAISE WARNING 'place_insert: % % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type,st_area(NEW.geometry); {% endif %} -- Filter tuples with bad geometries. IF ST_IsEmpty(NEW.geometry) OR NOT ST_IsValid(NEW.geometry) THEN INSERT INTO import_polygon_error (osm_type, osm_id, class, type, name, country_code, updated, errormessage, prevgeometry, newgeometry) VALUES (NEW.osm_type, NEW.osm_id, NEW.class, NEW.type, NEW.name, NEW.address->'country', now(), ST_IsValidReason(NEW.geometry), null, NEW.geometry); {% if debug %} RAISE WARNING 'Invalid Geometry: % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type; {% endif %} RETURN null; END IF; -- Remove the place from the list of places to be deleted DELETE FROM place_to_be_deleted pdel WHERE pdel.osm_type = NEW.osm_type and pdel.osm_id = NEW.osm_id and pdel.class = NEW.class and pdel.type = NEW.type; -- Have we already done this place? SELECT * INTO existing FROM place WHERE osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type = NEW.type; {% if debug %}RAISE WARNING 'Existing: %',existing.osm_id;{% endif %} IF existing.osm_type IS NULL THEN DELETE FROM place where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class; END IF; -- Remove any old logged data. DELETE from import_polygon_error where osm_type = NEW.osm_type and osm_id = NEW.osm_id; DELETE from import_polygon_delete where osm_type = NEW.osm_type and osm_id = NEW.osm_id; is_area := ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon'); IF is_area THEN area_size = ST_Area(NEW.geometry); END IF; -- When an area is changed from large to small: log and discard change IF existing.geometry is not null AND ST_IsValid(existing.geometry) AND ST_Area(existing.geometry) > 0.02 AND is_area AND area_size < ST_Area(existing.geometry) * 0.5 THEN INSERT INTO import_polygon_error (osm_type, osm_id, class, type, name, country_code, updated, errormessage, prevgeometry, newgeometry) VALUES (NEW.osm_type, NEW.osm_id, NEW.class, NEW.type, NEW.name, NEW.address->'country', now(), 'Area reduced from '||st_area(existing.geometry)||' to '||st_area(NEW.geometry), existing.geometry, NEW.geometry); RETURN null; END IF; -- Get the existing placex entry. SELECT * INTO existingplacex FROM placex WHERE osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type = NEW.type; {% if debug %}RAISE WARNING 'Existing PlaceX: %',existingplacex.place_id;{% endif %} IF existingplacex.osm_type IS NULL THEN -- Inserting a new placex. FOR newplacex IN INSERT INTO placex (osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry) VALUES (NEW.osm_type, NEW.osm_id, NEW.class, NEW.type, NEW.name, NEW.admin_level, NEW.address, NEW.extratags, NEW.geometry) RETURNING rank_address LOOP PERFORM update_invalidate_for_new_place(newplacex.rank_address, is_area, area_size, NEW.osm_Type, NEW.geometry); END LOOP; ELSE -- Modify an existing placex. IF is_rankable_place(NEW.osm_type, NEW.class, NEW.admin_level, NEW.name, NEW.extratags, is_area) THEN -- Recompute the ranks to look out for changes. -- We use the old country assignment here which is good enough for the -- purpose. SELECT * INTO search_rank, address_rank FROM compute_place_rank(existingplacex.country_code, CASE WHEN is_area THEN 'A' ELSE NEW.osm_type END, NEW.class, NEW.type, NEW.admin_level, (NEW.extratags->'capital') = 'yes', NEW.address->'postcode'); existing_is_area := ST_GeometryType(existingplacex.geometry) in ('ST_Polygon','ST_MultiPolygon'); IF (address_rank BETWEEN 4 and 27 AND (existingplacex.rank_address <= 0 OR existingplacex.rank_address > 27)) OR (not existing_is_area and is_area) THEN -- object newly relevant for addressing, invalidate new version PERFORM update_invalidate_for_new_place(address_rank, is_area, area_size, NEW.osm_type, NEW.geometry); ELSEIF (existingplacex.rank_address BETWEEN 4 and 27 AND (address_rank <= 0 OR address_rank > 27)) OR (existing_is_area and not is_area) THEN -- object no longer relevant for addressing, invalidate old version PERFORM update_invalidate_for_new_place(existingplacex.rank_address, existing_is_area, null, existingplacex.osm_type, existingplacex.geometry); ELSEIF address_rank BETWEEN 4 and 27 THEN IF coalesce(existing.name, ''::hstore) != coalesce(NEW.name, ''::hstore) OR existing.admin_level != NEW.admin_level THEN -- Name changes may have an effect on searchable objects and parenting -- for new and old areas. PERFORM update_invalidate_for_new_place(address_rank, is_area, null, NEW.osm_type, CASE WHEN is_area and existing_is_area THEN ST_Union(NEW.geometry, existingplacex.geometry) ELSE ST_Collect(NEW.geometry, existingplacex.geometry) END); ELSEIF existingplacex.geometry::text != NEW.geometry::text THEN -- Geometry change, just invalidate the changed areas. -- Changes of other geometry types are currently ignored. IF is_area and existing_is_area THEN PERFORM update_invalidate_for_new_place(address_rank, true, null, NEW.osm_type, ST_SymDifference(existingplacex.geometry, NEW.geometry)); END IF; END IF; END IF; -- Invalidate linked places: they potentially get a new name and addresses. IF existingplacex.linked_place_id is not NULL THEN UPDATE placex x SET name = p.name, extratags = p.extratags, indexed_status = 2 FROM place p WHERE x.place_id = existingplacex.linked_place_id and x.indexed_status = 0 and x.osm_type = p.osm_type and x.osm_id = p.osm_id and x.class = p.class; END IF; -- update placex in place UPDATE placex SET name = NEW.name, address = NEW.address, parent_place_id = null, extratags = NEW.extratags, admin_level = NEW.admin_level, rank_address = address_rank, rank_search = search_rank, indexed_status = 2, geometry = CASE WHEN address_rank = 0 THEN simplify_large_polygons(NEW.geometry) ELSE NEW.geometry END WHERE place_id = existingplacex.place_id; ELSE -- New place is not really valid, remove the placex entry UPDATE placex SET indexed_status = 100 WHERE place_id = existingplacex.place_id; END IF; -- When an existing way is updated, recalculate entrances IF existingplacex.osm_type = 'W' and (existingplacex.rank_search > 27 or existingplacex.class IN ('landuse', 'leisure')) THEN PERFORM place_update_entrances(existingplacex.place_id, existingplacex.osm_id); END IF; END IF; -- Finally update place itself. IF existing.osm_type is not NULL THEN -- If there is already an entry in place, just update that, if necessary. IF coalesce(existing.name, ''::hstore) != coalesce(NEW.name, ''::hstore) or coalesce(existing.address, ''::hstore) != coalesce(NEW.address, ''::hstore) or coalesce(existing.extratags, ''::hstore) != coalesce(NEW.extratags, ''::hstore) or coalesce(existing.admin_level, 15) != coalesce(NEW.admin_level, 15) or existing.geometry::text != NEW.geometry::text THEN UPDATE place SET name = NEW.name, address = NEW.address, extratags = NEW.extratags, admin_level = NEW.admin_level, geometry = NEW.geometry WHERE osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type = NEW.type; END IF; RETURN NULL; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_delete() RETURNS TRIGGER AS $$ DECLARE deferred BOOLEAN; BEGIN {% if debug %}RAISE WARNING 'Delete for % % %/%', OLD.osm_type, OLD.osm_id, OLD.class, OLD.type;{% endif %} deferred := ST_IsValid(OLD.geometry) and ST_Area(OLD.geometry) > 2; IF deferred THEN SELECT bool_or(not (rank_address = 0 or rank_address > 25)) INTO deferred FROM placex WHERE osm_type = OLD.osm_type and osm_id = OLD.osm_id and class = OLD.class and type = OLD.type; END IF; INSERT INTO place_to_be_deleted (osm_type, osm_id, class, type, deferred) VALUES(OLD.osm_type, OLD.osm_id, OLD.class, OLD.type, COALESCE(deferred, FALSE)); RETURN NULL; END; $$ LANGUAGE plpgsql; ================================================ FILE: lib-sql/functions/placex_triggers.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Trigger functions for the placex table. -- Information returned by update preparation. DROP TYPE IF EXISTS prepare_update_info CASCADE; CREATE TYPE prepare_update_info AS ( name HSTORE, address HSTORE, rank_address SMALLINT, country_code TEXT, class TEXT, type TEXT, linked_place_id BIGINT, centroid_x float, centroid_y float ); -- Retrieve the data needed by the indexer for updating the place. CREATE OR REPLACE FUNCTION placex_indexing_prepare(p placex) RETURNS prepare_update_info AS $$ DECLARE location RECORD; result prepare_update_info; extra_names HSTORE; default_language VARCHAR(10); BEGIN IF not p.address ? '_inherited' THEN result.address := p.address; END IF; -- For POI nodes, check if the address should be derived from a surrounding -- building. IF p.rank_search = 30 AND p.osm_type = 'N' THEN IF p.address is null THEN -- The additional && condition works around the misguided query -- planner of postgis 3.0. SELECT placex.address || hstore('_inherited', '') INTO result.address FROM placex WHERE ST_Covers(geometry, p.centroid) and geometry && p.centroid and placex.address is not null and (placex.address ? 'housenumber' or placex.address ? 'street' or placex.address ? 'place') and rank_search = 30 AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') LIMIT 1; ELSE -- See if we can inherit additional address tags from an interpolation. -- These will become permanent. FOR location IN SELECT address FROM place_interpolation WHERE ARRAY[p.osm_id] && place_interpolation.nodes AND address is not NULL LOOP result.address := location.address || result.address; END LOOP; END IF; END IF; -- remove internal and derived names result.address := result.address - '_unlisted_place'::TEXT; SELECT hstore(array_agg(key), array_agg(value)) INTO result.name FROM each(p.name) WHERE key not like '\_%'; result.class := p.class; result.type := p.type; result.country_code := p.country_code; result.rank_address := p.rank_address; result.centroid_x := ST_X(p.centroid); result.centroid_y := ST_Y(p.centroid); -- Names of linked places need to be merged in, so search for a linkable -- place already here. SELECT * INTO location FROM find_linked_place(p); IF location.place_id is not NULL THEN result.linked_place_id := location.place_id; IF location.name is not NULL THEN {% if debug %}RAISE WARNING 'Names original: %, location: %', result.name, location.name;{% endif %} -- Add the linked-place (e.g. city) name as a searchable placename in the default language (if any) default_language := get_country_language_code(location.country_code); IF default_language is not NULL AND location.name ? 'name' AND NOT location.name ? ('name:' || default_language) THEN location.name := location.name || hstore('name:' || default_language, location.name->'name'); END IF; -- Add all names from the place nodes that deviate from the name -- in the relation with the prefix '_place_'. Deviation means that -- either the value is different or a given key is missing completely IF result.name is null THEN SELECT hstore(array_agg('_place_' || key), array_agg(value)) INTO result.name FROM each(location.name); ELSE SELECT hstore(array_agg('_place_' || key), array_agg(value)) INTO extra_names FROM each(location.name - result.name); {% if debug %}RAISE WARNING 'Extra names: %', extra_names;{% endif %} IF extra_names is not null THEN result.name := result.name || extra_names; END IF; END IF; {% if debug %}RAISE WARNING 'Final names: %', result.name;{% endif %} END IF; END IF; RETURN result; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION find_associated_street(poi_osm_type CHAR(1), poi_osm_id BIGINT, bbox GEOMETRY) RETURNS BIGINT AS $$ DECLARE parent RECORD; result BIGINT; distance FLOAT; new_distance FLOAT; waygeom GEOMETRY; BEGIN -- Use UNION of two queries (one for Ways, one for Relations) to allow -- PostgreSQL to use partial indexes on placex.osm_type. FOR parent IN SELECT p.place_id, p.geometry FROM place_associated_street h JOIN place_associated_street s ON h.relation_id = s.relation_id AND s.member_role = 'street' JOIN placex p ON p.osm_type = 'W' AND p.osm_id = s.member_id AND p.name IS NOT NULL AND p.rank_search BETWEEN 26 AND 27 WHERE h.member_type = poi_osm_type AND h.member_id = poi_osm_id AND h.member_role = 'house' AND s.member_type = 'W' UNION ALL SELECT p.place_id, p.geometry FROM place_associated_street h JOIN place_associated_street s ON h.relation_id = s.relation_id AND s.member_role = 'street' JOIN placex p ON p.osm_type = 'R' AND p.osm_id = s.member_id AND p.name IS NOT NULL AND p.rank_search BETWEEN 26 AND 27 WHERE h.member_type = poi_osm_type AND h.member_id = poi_osm_id AND h.member_role = 'house' AND s.member_type = 'R' LOOP -- Find the closest 'street' member. -- Avoid distance computation for the frequent case where there is -- only one street member. IF waygeom IS NULL THEN result := parent.place_id; waygeom := parent.geometry; ELSE distance := coalesce(distance, ST_Distance(waygeom, bbox)); new_distance := ST_Distance(parent.geometry, bbox); IF new_distance < distance THEN distance := new_distance; result := parent.place_id; waygeom := parent.geometry; END IF; END IF; END LOOP; RETURN result; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; -- Find the parent road of a POI. -- -- \returns Place ID of parent object or NULL if none -- -- Copy data from linked items (POIs on ways, addr:street links, relations). -- CREATE OR REPLACE FUNCTION find_parent_for_poi(poi_osm_type CHAR(1), poi_osm_id BIGINT, poi_partition SMALLINT, bbox GEOMETRY, token_info JSONB, is_place_addr BOOLEAN) RETURNS BIGINT AS $$ DECLARE parent_place_id BIGINT DEFAULT NULL; location RECORD; BEGIN {% if debug %}RAISE WARNING 'finding street for % %', poi_osm_type, poi_osm_id;{% endif %} -- Is this object part of an associatedStreet relation? parent_place_id := find_associated_street(poi_osm_type, poi_osm_id, bbox); IF parent_place_id is null THEN parent_place_id := find_parent_for_address(token_info, poi_partition, bbox); END IF; IF parent_place_id is null and poi_osm_type = 'N' THEN FOR location IN SELECT p.place_id, p.osm_id, p.rank_search, p.address, coalesce(p.centroid, ST_Centroid(p.geometry)) as centroid FROM placex p, planet_osm_ways w WHERE p.osm_type = 'W' and p.rank_search >= 26 and p.geometry && bbox and w.id = p.osm_id and poi_osm_id = any(w.nodes) LOOP {% if debug %}RAISE WARNING 'Node is part of way % ', location.osm_id;{% endif %} -- Way IS a road then we are on it - that must be our road IF location.rank_search < 28 THEN {% if debug %}RAISE WARNING 'node in way that is a street %',location;{% endif %} RETURN location.place_id; END IF; parent_place_id := find_associated_street('W', location.osm_id, bbox); END LOOP; END IF; IF parent_place_id is NULL THEN IF is_place_addr THEN -- The address is attached to a place we don't know. -- Instead simply use the containing area with the largest rank. FOR location IN SELECT place_id FROM placex WHERE bbox && geometry AND _ST_Covers(geometry, ST_Centroid(bbox)) AND rank_address between 5 and 25 AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') ORDER BY rank_address desc LOOP RETURN location.place_id; END LOOP; ELSEIF ST_Area(bbox) < 0.005 THEN -- for smaller features get the nearest road SELECT getNearestRoadPlaceId(poi_partition, bbox) INTO parent_place_id; {% if debug %}RAISE WARNING 'Checked for nearest way (%)', parent_place_id;{% endif %} ELSE -- for larger features simply find the area with the largest rank that -- contains the bbox, only use addressable features FOR location IN SELECT place_id FROM placex WHERE bbox && geometry AND _ST_Covers(geometry, ST_Centroid(bbox)) AND rank_address between 5 and 25 AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') ORDER BY rank_address desc LOOP RETURN location.place_id; END LOOP; END IF; END IF; RETURN parent_place_id; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; -- Try to find a linked place for the given object. CREATE OR REPLACE FUNCTION find_linked_place(bnd placex) RETURNS placex AS $$ DECLARE {% if db.middle_db_format == '1' %} relation_members TEXT[]; {% else %} relation_members JSONB; {% endif %} rel_member RECORD; linked_placex placex%ROWTYPE; bnd_name TEXT; BEGIN IF bnd.rank_search >= 26 or bnd.rank_address = 0 or ST_GeometryType(bnd.geometry) NOT IN ('ST_Polygon','ST_MultiPolygon') THEN RETURN NULL; END IF; IF bnd.osm_type = 'R' THEN -- see if we have any special relation members SELECT members FROM planet_osm_rels WHERE id = bnd.osm_id INTO relation_members; {% if debug %}RAISE WARNING 'Got relation members';{% endif %} -- Search for relation members with role 'lable'. IF relation_members IS NOT NULL THEN FOR rel_member IN SELECT get_rel_node_members(relation_members, ARRAY['label']) as member LOOP {% if debug %}RAISE WARNING 'Found label member %', rel_member.member;{% endif %} FOR linked_placex IN SELECT * from placex WHERE osm_type = 'N' and osm_id = rel_member.member and class = 'place' LOOP {% if debug %}RAISE WARNING 'Linked label member';{% endif %} RETURN linked_placex; END LOOP; END LOOP; END IF; END IF; IF bnd.name ? 'name' THEN bnd_name := lower(bnd.name->'name'); IF bnd_name = '' THEN bnd_name := NULL; END IF; END IF; IF bnd.extratags ? 'wikidata' THEN FOR linked_placex IN SELECT * FROM placex WHERE placex.class = 'place' AND placex.osm_type = 'N' AND placex.extratags ? 'wikidata' -- needed to select right index AND placex.extratags->'wikidata' = bnd.extratags->'wikidata' AND (placex.linked_place_id is null or placex.linked_place_id = bnd.place_id) AND placex.rank_search < 26 AND _st_covers(bnd.geometry, placex.geometry) ORDER BY lower(name->'name') = bnd_name desc LOOP {% if debug %}RAISE WARNING 'Found wikidata-matching place node %', linked_placex.osm_id;{% endif %} RETURN linked_placex; END LOOP; END IF; -- If extratags has a place tag, look for linked nodes by their place type. -- Area and node still have to have the same name. IF bnd.extratags ? 'place' and bnd_name is not null THEN FOR linked_placex IN SELECT * FROM placex WHERE (position(lower(name->'name') in bnd_name) > 0 OR position(bnd_name in lower(name->'name')) > 0) AND placex.class = 'place' AND placex.type = bnd.extratags->'place' AND placex.osm_type = 'N' AND (placex.linked_place_id is null or placex.linked_place_id = bnd.place_id) AND placex.rank_search < 26 -- needed to select the right index AND (NOT placex.extratags ? 'wikidata' OR NOT bnd.extratags ? 'wikidata' OR placex.extratags->'wikidata' = bnd.extratags->'wikidata') AND ST_Covers(bnd.geometry, placex.geometry) LOOP {% if debug %}RAISE WARNING 'Found type-matching place node %', linked_placex.osm_id;{% endif %} RETURN linked_placex; END LOOP; END IF; -- Name searches can be done for ways as well as relations IF bnd_name is not null THEN {% if debug %}RAISE WARNING 'Looking for nodes with matching names';{% endif %} FOR linked_placex IN SELECT placex.* from placex WHERE lower(name->'name') = bnd_name AND ((bnd.rank_address > 0 and bnd.rank_address = (compute_place_rank(placex.country_code, 'N', placex.class, placex.type, 15::SMALLINT, false, placex.postcode)).address_rank) OR (bnd.rank_address = 0 and placex.rank_search = bnd.rank_search)) AND placex.osm_type = 'N' AND placex.class = 'place' AND (placex.linked_place_id is null or placex.linked_place_id = bnd.place_id) AND placex.rank_search < 26 -- needed to select the right index AND (placex.extratags->'wikidata' is null OR bnd.extratags->'wikidata' is null OR placex.extratags->'wikidata' = bnd.extratags->'wikidata') AND ST_Covers(bnd.geometry, placex.geometry) LOOP {% if debug %}RAISE WARNING 'Found matching place node %', linked_placex.osm_id;{% endif %} RETURN linked_placex; END LOOP; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION create_poi_search_terms(obj_place_id BIGINT, in_partition SMALLINT, parent_place_id BIGINT, is_place_addr BOOLEAN, country TEXT, token_info JSONB, geometry GEOMETRY, OUT name_vector INTEGER[], OUT nameaddress_vector INTEGER[]) AS $$ DECLARE parent_name_vector INTEGER[]; parent_address_vector INTEGER[]; addr_place_ids INTEGER[]; hnr_vector INTEGER[]; addr_item RECORD; addr_place RECORD; parent_address_place_ids BIGINT[]; BEGIN nameaddress_vector := '{}'::INTEGER[]; SELECT s.name_vector, s.nameaddress_vector INTO parent_name_vector, parent_address_vector FROM search_name s WHERE s.place_id = parent_place_id; FOR addr_item IN SELECT ranks.*, key, token_get_address_search_tokens(token_info, key) as search_tokens FROM token_get_address_keys(token_info) as key, LATERAL get_addr_tag_rank(key, country) as ranks WHERE not token_get_address_search_tokens(token_info, key) <@ parent_address_vector LOOP addr_place := get_address_place(in_partition, geometry, addr_item.from_rank, addr_item.to_rank, addr_item.extent, token_info, addr_item.key); IF addr_place is null THEN -- No place found in OSM that matches. Make it at least searchable. nameaddress_vector := array_merge(nameaddress_vector, addr_item.search_tokens); ELSE IF parent_address_place_ids is null THEN SELECT array_agg(parent_place_id) INTO parent_address_place_ids FROM place_addressline WHERE place_id = parent_place_id; END IF; -- If the parent already lists the place in place_address line, then we -- are done. Otherwise, add its own place_address line. IF not parent_address_place_ids @> ARRAY[addr_place.place_id] THEN nameaddress_vector := array_merge(nameaddress_vector, addr_place.keywords); INSERT INTO place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address) VALUES (obj_place_id, addr_place.place_id, not addr_place.isguess, true, addr_place.distance, addr_place.rank_address); END IF; END IF; END LOOP; name_vector := COALESCE(token_get_name_search_tokens(token_info), '{}'::INTEGER[]); -- Check if the parent covers all address terms. -- If not, create a search name entry with the house number as the name. -- This is unusual for the search_name table but prevents that the place -- is returned when we only search for the street/place. hnr_vector := token_get_housenumber_search_tokens(token_info); IF hnr_vector is not null and not nameaddress_vector <@ parent_address_vector THEN name_vector := array_merge(name_vector, hnr_vector); END IF; -- Cheating here by not recomputing all terms but simply using the ones -- from the parent object. nameaddress_vector := array_merge(nameaddress_vector, parent_name_vector); nameaddress_vector := array_merge(nameaddress_vector, parent_address_vector); -- make sure addr:place terms are always searchable IF is_place_addr THEN addr_place_ids := token_addr_place_search_tokens(token_info); IF hnr_vector is not null AND not addr_place_ids <@ parent_name_vector THEN name_vector := array_merge(name_vector, hnr_vector); END IF; nameaddress_vector := array_merge(nameaddress_vector, addr_place_ids); END IF; END; $$ LANGUAGE plpgsql; -- Insert address of a place into the place_addressline table. -- -- \param obj_place_id Place_id of the place to compute the address for. -- \param partition Partition number where the place is in. -- \param maxrank Rank of the place. All address features must have -- a search rank lower than the given rank. -- \param address Address terms for the place. -- \param geometry Geometry to which the address objects should be close. -- -- \retval parent_place_id Place_id of the address object that is the direct -- ancestor. -- \retval postcode Postcode computed from the address. This is the -- addr:postcode of one of the address objects. If -- more than one of has a postcode, the highest ranking -- one is used. May be NULL. -- \retval nameaddress_vector Search terms for the address. This is the sum -- of name terms of all address objects. CREATE OR REPLACE FUNCTION insert_addresslines(obj_place_id BIGINT, partition SMALLINT, maxrank SMALLINT, token_info JSONB, geometry GEOMETRY, centroid GEOMETRY, country TEXT, OUT parent_place_id BIGINT, OUT postcode TEXT, OUT nameaddress_vector INT[]) AS $$ DECLARE address_havelevel BOOLEAN[]; place_min_distance FLOAT[]; location_isaddress BOOLEAN; current_boundary GEOMETRY := NULL; current_node_area GEOMETRY := NULL; parent_place_rank INT := 0; addr_place_ids BIGINT[] := '{}'::int[]; new_address_vector INT[]; location RECORD; BEGIN parent_place_id := 0; nameaddress_vector := '{}'::int[]; address_havelevel := array_fill(false, ARRAY[maxrank]); place_min_distance := array_fill(1.0, ARRAY[maxrank]); FOR location IN SELECT apl.*, key FROM (SELECT extra.*, key FROM token_get_address_keys(token_info) as key, LATERAL get_addr_tag_rank(key, country) as extra) x, LATERAL get_address_place(partition, geometry, from_rank, to_rank, extent, token_info, key) as apl ORDER BY rank_address, distance, isguess desc LOOP IF location.place_id is null THEN {% if not db.reverse_only %} nameaddress_vector := array_merge(nameaddress_vector, token_get_address_search_tokens(token_info, location.key)); {% endif %} ELSE {% if not db.reverse_only %} nameaddress_vector := array_merge(nameaddress_vector, location.keywords::INTEGER[]); {% endif %} location_isaddress := not address_havelevel[location.rank_address]; IF not address_havelevel[location.rank_address] THEN address_havelevel[location.rank_address] := true; IF parent_place_rank < location.rank_address THEN parent_place_id := location.place_id; parent_place_rank := location.rank_address; END IF; END IF; IF location.isguess and location.distance < place_min_distance[location.rank_address] THEN place_min_distance[location.rank_address] := location.distance; END IF; INSERT INTO place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address) VALUES (obj_place_id, location.place_id, not location.isguess, true, location.distance, location.rank_address); addr_place_ids := addr_place_ids || location.place_id; END IF; END LOOP; FOR location IN SELECT * FROM getNearFeatures(partition, geometry, centroid, maxrank) WHERE not addr_place_ids @> ARRAY[place_id] ORDER BY rank_address, isguess asc, distance * CASE WHEN rank_address = 16 AND rank_search = 15 THEN 0.2 WHEN rank_address = 16 AND rank_search = 16 THEN 0.25 WHEN rank_address = 16 AND rank_search = 18 THEN 0.5 ELSE 1 END ASC LOOP -- Ignore all place nodes that do not fit in a lower level boundary. CONTINUE WHEN location.isguess and current_boundary is not NULL and not ST_Contains(current_boundary, location.centroid); -- If this is the first item in the rank, then assume it is the address. location_isaddress := not address_havelevel[location.rank_address]; -- Ignore guessed places when they are too far away compared to similar closer ones. IF location.isguess THEN CONTINUE WHEN not location_isaddress AND location.distance > 2 * place_min_distance[location.rank_address]; IF location.distance < place_min_distance[location.rank_address] THEN place_min_distance[location.rank_address] := location.distance; END IF; END IF; -- Further sanity checks to ensure that the address forms a sane hierarchy. IF location_isaddress THEN IF location.isguess and current_node_area is not NULL THEN location_isaddress := ST_Contains(current_node_area, location.centroid); END IF; IF not location.isguess and current_boundary is not NULL and location.rank_address != 11 AND location.rank_address != 5 THEN location_isaddress := ST_Contains(current_boundary, location.centroid); END IF; END IF; IF location_isaddress THEN address_havelevel[location.rank_address] := true; parent_place_id := location.place_id; -- Set postcode if we have one. -- (Returned will be the highest ranking one.) IF location.postcode is not NULL THEN postcode = location.postcode; END IF; -- Recompute the areas we need for hierarchy sanity checks. IF location.rank_address != 11 AND location.rank_address != 5 THEN IF location.isguess THEN current_node_area := place_node_fuzzy_area(location.centroid, location.rank_search); ELSE current_node_area := NULL; SELECT p.geometry FROM placex p WHERE p.place_id = location.place_id INTO current_boundary; END IF; END IF; END IF; -- Add it to the list of search terms {% if not db.reverse_only %} IF location.rank_address != 11 AND location.rank_address != 5 THEN nameaddress_vector := array_merge(nameaddress_vector, location.keywords::integer[]); END IF; {% endif %} INSERT INTO place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address) VALUES (obj_place_id, location.place_id, not location.isguess, location_isaddress, location.distance, location.rank_address); END LOOP; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION placex_insert() RETURNS TRIGGER AS $$ DECLARE postcode TEXT; result INT; is_area BOOLEAN; country_code VARCHAR(2); diameter FLOAT; classtable TEXT; BEGIN {% if debug %}RAISE WARNING '% % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type;{% endif %} NEW.place_id := nextval('seq_place'); NEW.indexed_status := 1; --STATUS_NEW NEW.centroid := get_center_point(NEW.geometry); NEW.country_code := lower(get_country_code(NEW.centroid)); NEW.partition := get_partition(NEW.country_code); NEW.geometry_sector := geometry_sector(NEW.partition, NEW.centroid); IF NEW.osm_type = 'X' THEN -- E'X'ternal records should already be in the right format so do nothing ELSE is_area := ST_GeometryType(NEW.geometry) IN ('ST_Polygon','ST_MultiPolygon'); IF NOT is_rankable_place(NEW.osm_type, NEW.class, NEW.admin_level, NEW.name, NEW.extratags, is_area) THEN RETURN NULL; END IF; SELECT * INTO NEW.rank_search, NEW.rank_address FROM compute_place_rank(NEW.country_code, CASE WHEN is_area THEN 'A' ELSE NEW.osm_type END, NEW.class, NEW.type, NEW.admin_level, (NEW.extratags->'capital') = 'yes', NEW.address->'postcode'); -- a country code make no sense below rank 4 (country) IF NEW.rank_search < 4 THEN NEW.country_code := NULL; END IF; -- Simplify polygons with a very large memory footprint when they -- do not take part in address computation. IF NEW.rank_address = 0 THEN NEW.geometry := simplify_large_polygons(NEW.geometry); END IF; END IF; IF NEW.importance IS NULL THEN NEW.importance := 0.40001 - (NEW.rank_search::float / 75); END IF; {% if debug %}RAISE WARNING 'placex_insert:END: % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type;{% endif %} {% if not disable_diff_updates %} -- The following is not needed until doing diff updates, and slows the main index process down -- add to tables for special search classtable := 'place_classtype_' || NEW.class || '_' || NEW.type; SELECT count(*) INTO result FROM pg_tables WHERE classtable NOT SIMILAR TO '%\W%' AND tablename = classtable and schemaname = current_schema(); IF result > 0 THEN EXECUTE 'INSERT INTO ' || classtable::regclass || ' (place_id, centroid) VALUES ($1,$2)' USING NEW.place_id, NEW.centroid; END IF; {% endif %} -- not disable_diff_updates RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION placex_update() RETURNS TRIGGER AS $$ DECLARE i INTEGER; location RECORD; {% if db.middle_db_format == '1' %} relation_members TEXT[]; {% else %} relation_member JSONB; {% endif %} geom GEOMETRY; parent_address_level SMALLINT; place_address_level SMALLINT; max_rank SMALLINT; name_vector INTEGER[]; nameaddress_vector INTEGER[]; addr_nameaddress_vector INTEGER[]; linked_place BIGINT; linked_node_id BIGINT; linked_importance FLOAT; linked_wikipedia TEXT; is_place_address BOOLEAN; result BOOLEAN; BEGIN -- deferred delete IF OLD.indexed_status = 100 THEN {% if debug %}RAISE WARNING 'placex_update delete % %',NEW.osm_type,NEW.osm_id;{% endif %} delete from placex where place_id = OLD.place_id; RETURN NULL; END IF; IF NEW.indexed_status != 0 OR OLD.indexed_status = 0 THEN RETURN NEW; END IF; {% if debug %}RAISE WARNING 'placex_update % % (%)',NEW.osm_type,NEW.osm_id,NEW.place_id;{% endif %} NEW.indexed_date = now(); IF OLD.indexed_status > 1 THEN {% if 'search_name' in db.tables %} DELETE from search_name WHERE place_id = NEW.place_id; {% endif %} result := deleteSearchName(NEW.partition, NEW.place_id); DELETE FROM place_addressline WHERE place_id = NEW.place_id; result := deleteRoad(NEW.partition, NEW.place_id); result := deleteLocationArea(NEW.partition, NEW.place_id, NEW.rank_search); END IF; NEW.extratags := NEW.extratags - 'linked_place'::TEXT; IF NEW.extratags = ''::hstore THEN NEW.extratags := NULL; END IF; -- NEW.linked_place_id contains the precomputed linkee. Save this and restore -- the previous link status. linked_place := NEW.linked_place_id; NEW.linked_place_id := OLD.linked_place_id; -- Remove linkage, if we have computed a different new linkee. IF OLD.indexed_status > 1 THEN UPDATE placex SET linked_place_id = null, indexed_status = CASE WHEN indexed_status = 0 THEN 2 ELSE indexed_status END WHERE linked_place_id = NEW.place_id and (linked_place is null or place_id != linked_place); END IF; -- Compute a preliminary centroid. NEW.centroid := get_center_point(NEW.geometry); -- Record the entrance node locations IF NEW.osm_type = 'W' and (NEW.rank_search > 27 or NEW.class IN ('landuse', 'leisure')) THEN PERFORM place_update_entrances(NEW.place_id, NEW.osm_id); END IF; -- recalculate country and partition IF NEW.rank_search = 4 AND NEW.address is not NULL AND NEW.address ? 'country' THEN -- for countries, believe the mapped country code, -- so that we remain in the right partition if the boundaries -- suddenly expand. NEW.country_code := lower(NEW.address->'country'); NEW.partition := get_partition(lower(NEW.country_code)); IF NEW.partition = 0 THEN NEW.country_code := lower(get_country_code(NEW.centroid)); NEW.partition := get_partition(NEW.country_code); END IF; ELSE IF NEW.rank_search >= 4 THEN NEW.country_code := lower(get_country_code(NEW.centroid)); ELSE NEW.country_code := NULL; END IF; NEW.partition := get_partition(NEW.country_code); END IF; {% if debug %}RAISE WARNING 'Country updated: "%"', NEW.country_code;{% endif %} -- recompute the ranks, they might change when linking changes SELECT * INTO NEW.rank_search, NEW.rank_address FROM compute_place_rank(NEW.country_code, CASE WHEN ST_GeometryType(NEW.geometry) IN ('ST_Polygon','ST_MultiPolygon') THEN 'A' ELSE NEW.osm_type END, NEW.class, NEW.type, NEW.admin_level, (NEW.extratags->'capital') = 'yes', NEW.address->'postcode'); -- Short-cut out for linked places. Note that this must happen after the -- address rank has been recomputed. The linking might nullify a shift in -- address rank. IF NEW.linked_place_id is not null THEN NEW.token_info := null; {% if debug %}RAISE WARNING 'place already linked to %', OLD.linked_place_id;{% endif %} RETURN NEW; END IF; -- We must always increase the address level relative to the admin boundary. IF NEW.class = 'boundary' and NEW.type = 'administrative' and NEW.osm_type = 'R' and NEW.rank_address > 0 THEN -- First, check that admin boundaries do not overtake each other rank-wise. parent_address_level := 3; FOR location IN SELECT rank_address, (CASE WHEN extratags ? 'wikidata' and NEW.extratags ? 'wikidata' and extratags->'wikidata' = NEW.extratags->'wikidata' THEN ST_Equals(geometry, NEW.geometry) ELSE false END) as is_same FROM placex WHERE osm_type = 'R' and class = 'boundary' and type = 'administrative' and admin_level < NEW.admin_level and admin_level > 3 and rank_address between 1 and 25 -- for index selection and ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') -- for index selection and geometry && NEW.centroid and _ST_Covers(geometry, NEW.centroid) ORDER BY admin_level desc LIMIT 1 LOOP IF location.is_same THEN -- Looks like the same boundary is replicated on multiple admin_levels. -- Usual tagging in Poland. Remove our boundary from addresses. NEW.rank_address := 0; ELSE parent_address_level := location.rank_address; IF location.rank_address >= NEW.rank_address THEN IF location.rank_address >= 24 THEN NEW.rank_address := 25; ELSE NEW.rank_address := location.rank_address + 2; END IF; END IF; END IF; END LOOP; IF NEW.rank_address > 9 THEN -- Second check that the boundary is not completely contained in a -- place area with a equal or higher address rank. FOR location IN SELECT rank_address FROM placex, LATERAL compute_place_rank(country_code, 'A', class, type, admin_level, False, null) prank WHERE class = 'place' and rank_address between 1 and 23 and prank.address_rank >= NEW.rank_address and ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') -- select right index and ST_Contains(geometry, NEW.geometry) and not ST_Equals(geometry, NEW.geometry) ORDER BY prank.address_rank desc LIMIT 1 LOOP NEW.rank_address := location.rank_address + 2; END LOOP; END IF; ELSEIF NEW.class = 'place' and ST_GeometryType(NEW.geometry) in ('ST_Polygon', 'ST_MultiPolygon') and NEW.rank_address between 16 and 23 THEN -- For place areas make sure they are not completely contained in an area -- with a equal or higher address rank. FOR location IN SELECT rank_address FROM placex, LATERAL compute_place_rank(country_code, 'A', class, type, admin_level, False, null) prank WHERE prank.address_rank < 24 and rank_address between 1 and 25 -- select right index and ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') -- select right index and prank.address_rank >= NEW.rank_address and ST_Contains(geometry, NEW.geometry) and not ST_Equals(geometry, NEW.geometry) ORDER BY prank.address_rank desc LIMIT 1 LOOP NEW.rank_address := location.rank_address + 2; END LOOP; ELSEIF NEW.class = 'place' and NEW.osm_type = 'N' and NEW.rank_address between 16 and 23 THEN -- If a place node is contained in an admin or place boundary with the same -- address level and has not been linked, then make the node a subpart -- by increasing the address rank (city level and above). FOR location IN SELECT rank_address FROM placex, LATERAL compute_place_rank(country_code, 'A', class, type, admin_level, False, null) prank WHERE osm_type = 'R' and rank_address between 1 and 25 -- select right index and ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') -- select right index and ((class = 'place' and prank.address_rank = NEW.rank_address) or (class = 'boundary' and rank_address = NEW.rank_address)) and geometry && NEW.centroid and _ST_Covers(geometry, NEW.centroid) LIMIT 1 LOOP NEW.rank_address = NEW.rank_address + 2; END LOOP; ELSE parent_address_level := 3; END IF; NEW.housenumber := token_normalized_housenumber(NEW.token_info); NEW.postcode := null; -- waterway ways are linked when they are part of a relation and have the same class/type IF NEW.osm_type = 'R' and NEW.class = 'waterway' THEN {% if db.middle_db_format == '1' %} FOR relation_members IN select members from planet_osm_rels r where r.id = NEW.osm_id and r.parts != array[]::bigint[] LOOP FOR i IN 1..array_upper(relation_members, 1) BY 2 LOOP IF relation_members[i+1] in ('', 'main_stream', 'side_stream') AND substring(relation_members[i],1,1) = 'w' THEN {% if debug %}RAISE WARNING 'waterway parent %, child %/%', NEW.osm_id, i, relation_members[i];{% endif %} FOR linked_node_id IN SELECT place_id FROM placex WHERE osm_type = 'W' and osm_id = substring(relation_members[i],2,200)::bigint and class = NEW.class and type in ('river', 'stream', 'canal', 'drain', 'ditch') and ( relation_members[i+1] != 'side_stream' or NEW.name->'name' = name->'name') LOOP UPDATE placex SET linked_place_id = NEW.place_id WHERE place_id = linked_node_id; {% if 'search_name' in db.tables %} IF OLD.indexed_status > 1 THEN DELETE FROM search_name WHERE place_id = linked_node_id; END IF; {% endif %} END LOOP; END IF; END LOOP; END LOOP; {% else %} FOR relation_member IN SELECT value FROM planet_osm_rels r, LATERAL jsonb_array_elements(r.members) WHERE r.id = NEW.osm_id LOOP IF relation_member->>'role' IN ('', 'main_stream', 'side_stream') and relation_member->>'type' = 'W' THEN {% if debug %}RAISE WARNING 'waterway parent %, child %', NEW.osm_id, relation_member;{% endif %} FOR linked_node_id IN SELECT place_id FROM placex WHERE osm_type = 'W' and osm_id = (relation_member->>'ref')::bigint and class = NEW.class and type in ('river', 'stream', 'canal', 'drain', 'ditch') and (relation_member->>'role' != 'side_stream' or NEW.name->'name' = name->'name') LOOP UPDATE placex SET linked_place_id = NEW.place_id WHERE place_id = linked_node_id; {% if 'search_name' in db.tables %} DELETE FROM search_name WHERE place_id = linked_node_id; {% endif %} END LOOP; END IF; END LOOP; {% endif %} {% if debug %}RAISE WARNING 'Waterway processed';{% endif %} END IF; SELECT wikipedia, importance INTO NEW.wikipedia, NEW.importance FROM compute_importance(NEW.extratags, NEW.country_code, NEW.rank_search, NEW.centroid); {% if debug %}RAISE WARNING 'Importance computed from wikipedia: %', NEW.importance;{% endif %} -- --------------------------------------------------------------------------- -- For low level elements we inherit from our parent road IF NEW.rank_search > 27 THEN {% if debug %}RAISE WARNING 'finding street for % %', NEW.osm_type, NEW.osm_id;{% endif %} NEW.parent_place_id := null; is_place_address := not token_is_street_address(NEW.token_info); -- We have to find our parent road. NEW.parent_place_id := find_parent_for_poi(NEW.osm_type, NEW.osm_id, NEW.partition, ST_Envelope(NEW.geometry), NEW.token_info, is_place_address); -- If we found the road take a shortcut here. -- Otherwise fall back to the full address getting method below. IF NEW.parent_place_id is not null THEN -- Get the details of the parent road SELECT p.country_code, p.postcode, p.name FROM placex p WHERE p.place_id = NEW.parent_place_id INTO location; IF is_place_address and NEW.address ? 'place' THEN -- Check if the addr:place tag is part of the parent name SELECT count(*) INTO i FROM svals(location.name) AS pname WHERE pname = NEW.address->'place'; IF i = 0 THEN NEW.address = NEW.address || hstore('_unlisted_place', NEW.address->'place'); END IF; END IF; NEW.country_code := location.country_code; {% if debug %}RAISE WARNING 'Got parent details from search name';{% endif %} -- determine postcode NEW.postcode := coalesce(token_get_postcode(NEW.token_info), location.postcode, get_nearest_postcode(NEW.country_code, NEW.centroid)); IF NEW.name is not NULL THEN NEW.name := add_default_place_name(NEW.country_code, NEW.name); END IF; {% if not db.reverse_only %} IF NEW.name is not NULL OR NEW.address is not NULL THEN SELECT * INTO name_vector, nameaddress_vector FROM create_poi_search_terms(NEW.place_id, NEW.partition, NEW.parent_place_id, is_place_address, NEW.country_code, NEW.token_info, NEW.centroid); IF array_length(name_vector, 1) is not NULL THEN INSERT INTO search_name (place_id, address_rank, importance, country_code, name_vector, nameaddress_vector, centroid) VALUES (NEW.place_id, NEW.rank_address, NEW.importance, NEW.country_code, name_vector, nameaddress_vector, NEW.centroid); {% if debug %}RAISE WARNING 'Place added to search table';{% endif %} END IF; END IF; {% endif %} NEW.token_info := token_strip_info(NEW.token_info); RETURN NEW; END IF; END IF; -- --------------------------------------------------------------------------- -- Full indexing {% if debug %}RAISE WARNING 'Using full index mode for % %', NEW.osm_type, NEW.osm_id;{% endif %} IF linked_place is not null THEN -- Recompute the ranks here as the ones from the linked place might -- have been shifted to accommodate surrounding boundaries. SELECT place_id, osm_id, class, type, extratags, rank_search, centroid, geometry, (compute_place_rank(country_code, osm_type, class, type, admin_level, (extratags->'capital') = 'yes', null)).* INTO location FROM placex WHERE place_id = linked_place; {% if debug %}RAISE WARNING 'Linked %', location;{% endif %} -- Use the linked point as the centre point of the geometry, -- but only if it is within the area of the boundary. geom := coalesce(location.centroid, ST_Centroid(location.geometry)); IF geom is not NULL AND ST_Within(geom, NEW.geometry) THEN NEW.centroid := geom; END IF; {% if debug %}RAISE WARNING 'parent address: % rank address: %', parent_address_level, location.address_rank;{% endif %} IF location.address_rank > parent_address_level and location.address_rank < 26 THEN NEW.rank_address := location.address_rank; END IF; -- merge in extra tags NEW.extratags := hstore('linked_' || location.class, location.type) || coalesce(location.extratags, ''::hstore) || coalesce(NEW.extratags, ''::hstore); -- mark the linked place (excludes from search results) -- Force reindexing to remove any traces from the search indexes and -- reset the address rank if necessary. UPDATE placex set linked_place_id = NEW.place_id, indexed_status = 2 WHERE place_id = location.place_id; SELECT wikipedia, importance FROM compute_importance(location.extratags, NEW.country_code, location.rank_search, NEW.centroid) INTO linked_wikipedia,linked_importance; -- Use the maximum importance if one could be computed from the linked object. IF linked_importance is not null AND (NEW.importance is null or NEW.importance < linked_importance) THEN NEW.importance := linked_importance; END IF; ELSE -- No linked place? As a last resort check if the boundary is tagged with -- a place type and adapt the rank address. IF NEW.rank_address between 4 and 25 and NEW.extratags ? 'place' THEN SELECT address_rank INTO place_address_level FROM compute_place_rank(NEW.country_code, 'A', 'place', NEW.extratags->'place', 0::SMALLINT, False, null); IF place_address_level > parent_address_level and place_address_level < 26 THEN NEW.rank_address := place_address_level; END IF; END IF; END IF; {% if not disable_diff_updates %} IF OLD.rank_address != NEW.rank_address THEN -- After a rank shift all addresses containing us must be updated. UPDATE placex p SET indexed_status = 2 FROM place_addressline pa WHERE pa.address_place_id = NEW.place_id and p.place_id = pa.place_id and p.indexed_status = 0 and p.rank_address between 4 and 25; END IF; {% endif %} IF NEW.admin_level = 2 AND NEW.class = 'boundary' AND NEW.type = 'administrative' AND NEW.country_code IS NOT NULL AND NEW.osm_type = 'R' THEN -- Update the list of country names. -- Only take the name from the largest area for the given country code -- in the hope that this is the authoritative one. -- Also replace any old names so that all mapping mistakes can -- be fixed through regular OSM updates. FOR location IN SELECT osm_id FROM placex WHERE rank_search = 4 and osm_type = 'R' and country_code = NEW.country_code ORDER BY ST_Area(geometry) desc LIMIT 1 LOOP IF location.osm_id = NEW.osm_id THEN {% if debug %}RAISE WARNING 'Updating names for country ''%'' with: %', NEW.country_code, NEW.name;{% endif %} UPDATE country_name SET derived_name = NEW.name WHERE country_code = NEW.country_code; END IF; END LOOP; END IF; -- For linear features we need the full geometry for determining the address -- because they may go through several administrative entities. Otherwise use -- the centroid for performance reasons. IF ST_GeometryType(NEW.geometry) in ('ST_LineString', 'ST_MultiLineString') THEN geom := NEW.geometry; ELSE geom := NEW.centroid; END IF; IF NEW.rank_address = 0 THEN max_rank := geometry_to_rank(NEW.rank_search, NEW.geometry, NEW.country_code); -- Rank 0 features may also span multiple administrative areas (e.g. lakes) -- so use the geometry here too. Just make sure the areas don't become too -- large. IF NEW.class = 'natural' or max_rank > 10 THEN geom := NEW.geometry; END IF; ELSEIF NEW.rank_address > 25 THEN max_rank := 25; ELSE max_rank := NEW.rank_address; END IF; SELECT * FROM insert_addresslines(NEW.place_id, NEW.partition, max_rank, NEW.token_info, geom, NEW.centroid, NEW.country_code) INTO NEW.parent_place_id, NEW.postcode, nameaddress_vector; {% if debug %}RAISE WARNING 'RETURN insert_addresslines: %, %, %', NEW.parent_place_id, NEW.postcode, nameaddress_vector;{% endif %} NEW.postcode := coalesce(token_get_postcode(NEW.token_info), NEW.postcode); -- if we have a name add this to the name search table name_vector := token_get_name_search_tokens(NEW.token_info); IF array_length(name_vector, 1) is not NULL THEN -- Initialise the name vector using our name NEW.name := add_default_place_name(NEW.country_code, NEW.name); IF NEW.rank_search <= 25 and NEW.rank_address > 0 THEN result := add_location(NEW.place_id, NEW.country_code, NEW.partition, name_vector, NEW.rank_search, NEW.rank_address, NEW.postcode, NEW.geometry, NEW.centroid); {% if debug %}RAISE WARNING 'added to location (full)';{% endif %} END IF; IF NEW.rank_search between 26 and 27 and NEW.class = 'highway' THEN result := insertLocationRoad(NEW.partition, NEW.place_id, NEW.country_code, NEW.geometry); {% if debug %}RAISE WARNING 'insert into road location table (full)';{% endif %} END IF; IF NEW.rank_address between 16 and 27 THEN result := insertSearchName(NEW.partition, NEW.place_id, token_get_name_match_tokens(NEW.token_info), NEW.rank_search, NEW.rank_address, NEW.geometry); END IF; {% if debug %}RAISE WARNING 'added to search name (full)';{% endif %} {% if not db.reverse_only %} INSERT INTO search_name (place_id, address_rank, importance, country_code, name_vector, nameaddress_vector, centroid) VALUES (NEW.place_id, NEW.rank_address, NEW.importance, NEW.country_code, name_vector, nameaddress_vector, NEW.centroid); {% endif %} END IF; IF NEW.postcode is null AND NEW.rank_search > 8 AND (NEW.rank_address > 0 OR ST_GeometryType(NEW.geometry) not in ('ST_LineString','ST_MultiLineString') OR ST_Length(NEW.geometry) < 0.02) THEN NEW.postcode := get_nearest_postcode(NEW.country_code, CASE WHEN NEW.rank_address > 25 THEN NEW.centroid ELSE NEW.geometry END); END IF; {% if debug %}RAISE WARNING 'place update % % finished.', NEW.osm_type, NEW.osm_id;{% endif %} NEW.token_info := token_strip_info(NEW.token_info); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION placex_delete() RETURNS TRIGGER AS $$ DECLARE b BOOLEAN; result INT; classtable TEXT; BEGIN -- RAISE WARNING 'placex_delete % %',OLD.osm_type,OLD.osm_id; IF OLD.linked_place_id is null THEN UPDATE placex SET linked_place_id = NULL, indexed_status = CASE WHEN indexed_status = 0 THEN 2 ELSE indexed_status END WHERE linked_place_id = OLD.place_id; ELSE update placex set indexed_status = 2 where place_id = OLD.linked_place_id and indexed_status = 0; END IF; IF OLD.rank_address < 30 THEN -- mark everything linked to this place for re-indexing {% if debug %}RAISE WARNING 'placex_delete:03 % %',OLD.osm_type,OLD.osm_id;{% endif %} UPDATE placex set indexed_status = 2 from place_addressline where address_place_id = OLD.place_id and placex.place_id = place_addressline.place_id and indexed_status = 0 and place_addressline.isaddress; {% if debug %}RAISE WARNING 'placex_delete:04 % %',OLD.osm_type,OLD.osm_id;{% endif %} DELETE FROM place_addressline where address_place_id = OLD.place_id; {% if debug %}RAISE WARNING 'placex_delete:05 % %',OLD.osm_type,OLD.osm_id;{% endif %} b := deleteRoad(OLD.partition, OLD.place_id); {% if debug %}RAISE WARNING 'placex_delete:06 % %',OLD.osm_type,OLD.osm_id;{% endif %} update placex set indexed_status = 2 where parent_place_id = OLD.place_id and indexed_status = 0; {% if debug %}RAISE WARNING 'placex_delete:07 % %',OLD.osm_type,OLD.osm_id;{% endif %} -- reparenting also for OSM Interpolation Lines (and for Tiger?) update location_property_osmline set indexed_status = 2 where indexed_status = 0 and parent_place_id = OLD.place_id; UPDATE location_postcodes SET indexed_status = 2 WHERE parent_place_id = OLD.place_id; END IF; {% if debug %}RAISE WARNING 'placex_delete:08 % %',OLD.osm_type,OLD.osm_id;{% endif %} IF OLD.rank_address < 26 THEN b := deleteLocationArea(OLD.partition, OLD.place_id, OLD.rank_search); END IF; {% if debug %}RAISE WARNING 'placex_delete:09 % %',OLD.osm_type,OLD.osm_id;{% endif %} IF OLD.name is not null THEN {% if 'search_name' in db.tables %} DELETE from search_name WHERE place_id = OLD.place_id; {% endif %} b := deleteSearchName(OLD.partition, OLD.place_id); END IF; {% if debug %}RAISE WARNING 'placex_delete:10 % %',OLD.osm_type,OLD.osm_id;{% endif %} DELETE FROM place_addressline where place_id = OLD.place_id; {% if debug %}RAISE WARNING 'placex_delete:11 % %',OLD.osm_type,OLD.osm_id;{% endif %} -- remove from tables for special search classtable := 'place_classtype_' || OLD.class || '_' || OLD.type; SELECT count(*) INTO result FROM pg_tables WHERE classtable NOT SIMILAR TO '%\W%' AND tablename = classtable and schemaname = current_schema(); IF result > 0 THEN EXECUTE 'DELETE FROM ' || classtable::regclass || ' WHERE place_id = $1' USING OLD.place_id; END IF; {% if debug %}RAISE WARNING 'placex_delete:12 % %',OLD.osm_type,OLD.osm_id;{% endif %} RETURN OLD; END; $$ LANGUAGE plpgsql; ================================================ FILE: lib-sql/functions/postcode_triggers.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2025 by the Nominatim developer community. -- For a full list of authors see the git log. -- Trigger functions for location_postcodes table. -- Trigger for updates of location_postcode -- -- Computes the parent object the postcode most likely refers to. -- This will be the place that determines the address displayed when -- searching for this postcode. CREATE OR REPLACE FUNCTION postcodes_update() RETURNS TRIGGER AS $$ DECLARE partition SMALLINT; location RECORD; BEGIN IF NEW.indexed_status != 0 OR OLD.indexed_status = 0 THEN RETURN NEW; END IF; NEW.indexed_date = now(); partition := get_partition(NEW.country_code); NEW.parent_place_id = 0; FOR location IN SELECT place_id FROM getNearFeatures(partition, NEW.centroid, NEW.centroid, NEW.rank_search) WHERE NOT isguess ORDER BY rank_address DESC, distance asc LIMIT 1 LOOP NEW.parent_place_id = location.place_id; END LOOP; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION postcodes_delete() RETURNS TRIGGER AS $$ BEGIN {% if not disable_diff_updates %} UPDATE placex p SET indexed_status = 2 WHERE p.postcode = OLD.postcode AND ST_Intersects(OLD.geometry, p.geometry) AND indexed_status = 0; {% endif %} RETURN OLD; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION postcodes_insert() RETURNS TRIGGER AS $$ DECLARE existing RECORD; BEGIN IF NEW.osm_id is not NULL THEN -- postcode area, remove existing from same OSM object SELECT * INTO existing FROM location_postcodes p WHERE p.osm_id = NEW.osm_id; IF existing.place_id is not NULL THEN IF existing.postcode != NEW.postcode or existing.country_code != NEW.country_code THEN DELETE FROM location_postcodes p WHERE p.osm_id = NEW.osm_id; existing := NULL; END IF; END IF; END IF; IF existing is NULL THEN SELECT * INTO existing FROM location_postcodes p WHERE p.country_code = NEW.country_code AND p.postcode = NEW.postcode; IF existing.postcode is NULL THEN {% if not disable_diff_updates %} UPDATE placex p SET indexed_status = 2 WHERE ST_Intersects(NEW.geometry, p.geometry) AND indexed_status = 0 AND p.rank_address >= 22 AND not p.address ? 'postcode'; {% endif %} -- new entry, just insert NEW.indexed_status := 1; NEW.place_id := nextval('seq_place'); RETURN NEW; END IF; END IF; -- update: only when there are changes IF coalesce(NEW.osm_id, -1) != coalesce(existing.osm_id, -1) OR (NEW.osm_id is not null AND NEW.geometry::text != existing.geometry::text) OR (NEW.osm_id is null AND (abs(ST_X(existing.centroid) - ST_X(NEW.centroid)) > 0.0000001 OR abs(ST_Y(existing.centroid) - ST_Y(NEW.centroid)) > 0.0000001)) THEN {% if not disable_diff_updates %} UPDATE placex p SET indexed_status = 2 WHERE ST_Intersects(ST_Difference(NEW.geometry, existing.geometry), p.geometry) AND indexed_status = 0 AND p.rank_address >= 22 AND not p.address ? 'postcode'; UPDATE placex p SET indexed_status = 2 WHERE ST_Intersects(ST_Difference(existing.geometry, NEW.geometry), p.geometry) AND indexed_status = 0 AND p.postcode = OLD.postcode; {% endif %} UPDATE location_postcodes p SET osm_id = NEW.osm_id, indexed_status = 2, centroid = NEW.centroid, geometry = NEW.geometry WHERE p.country_code = NEW.country_code AND p.postcode = NEW.postcode; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; ================================================ FILE: lib-sql/functions/ranking.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Functions related to search and address ranks -- Check if a place is rankable at all. -- These really should be dropped at the lua level eventually. CREATE OR REPLACE FUNCTION is_rankable_place(osm_type TEXT, class TEXT, admin_level SMALLINT, name HSTORE, extratags HSTORE, is_area BOOLEAN) RETURNS BOOLEAN AS $$ BEGIN IF class = 'highway' AND is_area AND name is null AND extratags ? 'area' AND extratags->'area' = 'yes' THEN RETURN FALSE; END IF; IF class = 'boundary' THEN IF NOT is_area OR (admin_level <= 4 AND osm_type = 'W') THEN RETURN FALSE; END IF; END IF; RETURN TRUE; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; -- Return an approximate search radius according to the search rank. CREATE OR REPLACE FUNCTION reverse_place_diameter(rank_search SMALLINT) RETURNS FLOAT AS $$ BEGIN IF rank_search <= 4 THEN RETURN 5.0; ELSIF rank_search <= 8 THEN RETURN 1.8; ELSIF rank_search <= 12 THEN RETURN 0.6; ELSIF rank_search <= 17 THEN RETURN 0.16; ELSIF rank_search <= 18 THEN RETURN 0.08; ELSIF rank_search <= 19 THEN RETURN 0.04; END IF; RETURN 0.02; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; -- Return an approximate update radius according to the search rank. CREATE OR REPLACE FUNCTION update_place_diameter(rank_search SMALLINT) RETURNS FLOAT AS $$ BEGIN -- postcodes IF rank_search = 11 or rank_search = 5 THEN RETURN 0.05; -- anything higher than city is effectively ignored (polygon required) ELSIF rank_search < 16 THEN RETURN 0; ELSIF rank_search < 18 THEN RETURN 0.1; ELSIF rank_search < 20 THEN RETURN 0.05; ELSIF rank_search = 21 THEN RETURN 0.001; ELSIF rank_search < 24 THEN RETURN 0.02; ELSIF rank_search < 26 THEN RETURN 0.002; ELSIF rank_search < 28 THEN RETURN 0.001; END IF; RETURN 0; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; -- Compute a base address rank from the extent of the given geometry. -- -- This is all simple guess work. We don't need particularly good estimates -- here. This just avoids to have very high ranked address parts in features -- that span very large areas (or vice versa). CREATE OR REPLACE FUNCTION geometry_to_rank(search_rank SMALLINT, geometry GEOMETRY, country_code TEXT) RETURNS SMALLINT AS $$ DECLARE area FLOAT; BEGIN IF ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') THEN area := ST_Area(geometry); ELSIF ST_GeometryType(geometry) in ('ST_LineString','ST_MultiLineString') THEN area := (ST_Length(geometry)^2) * 0.1; ELSE RETURN search_rank; END IF; -- adjust for the fact that countries come in different sizes IF country_code IN ('ca', 'au', 'ru') THEN area := area / 5; ELSIF country_code IN ('br', 'kz', 'cn', 'us', 'ne', 'gb', 'za', 'sa', 'id', 'eh', 'ml', 'tm') THEN area := area / 3; ELSIF country_code IN ('bo', 'ar', 'sd', 'mn', 'in', 'et', 'cd', 'mz', 'ly', 'cl', 'zm') THEN area := area / 2; ELSIF country_code IN ('sg', 'ws', 'st', 'kn') THEN area := area * 5; ELSIF country_code IN ('dm', 'mt', 'lc', 'gg', 'sc', 'nr') THEN area := area * 20; END IF; IF area > 1 THEN RETURN 7; ELSIF area > 0.1 THEN RETURN 9; ELSIF area > 0.01 THEN RETURN 13; ELSIF area > 0.001 THEN RETURN 17; ELSIF area > 0.0001 THEN RETURN 19; ELSIF area > 0.000005 THEN RETURN 21; END IF; RETURN 23; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; -- Get standard search and address rank for an object. -- -- \param country Two-letter country code where the object is in. -- \param extended_type OSM type (N, W, R) or area type (A). -- \param place_class Class (or tag key) of object. -- \param place_type Type (or tag value) of object. -- \param admin_level Value of admin_level tag. -- \param is_major If true, boost search rank by one. -- \param postcode Value of addr:postcode tag. -- \param[out] search_rank Computed search rank. -- \param[out] address_rank Computed address rank. -- CREATE OR REPLACE FUNCTION compute_place_rank(country VARCHAR(2), extended_type VARCHAR(1), place_class TEXT, place_type TEXT, admin_level SMALLINT, is_major BOOLEAN, postcode TEXT, OUT search_rank SMALLINT, OUT address_rank SMALLINT) AS $$ DECLARE classtype TEXT; BEGIN IF extended_type = 'N' AND place_class = 'highway' THEN search_rank = 30; address_rank = 30; ELSEIF place_class = 'landuse' AND extended_type != 'A' THEN search_rank = 30; address_rank = 30; ELSE IF place_class = 'boundary' and place_type = 'administrative' THEN classtype = place_type || admin_level::TEXT; ELSE classtype = place_type; END IF; SELECT l.rank_search, l.rank_address INTO search_rank, address_rank FROM address_levels l WHERE (l.country_code = country or l.country_code is NULL) AND l.class = place_class AND (l.type = classtype or l.type is NULL) ORDER BY l.country_code, l.class, l.type LIMIT 1; IF search_rank is NULL OR address_rank is NULL THEN search_rank := 30; address_rank := 30; END IF; -- some postcorrections IF place_class = 'waterway' AND extended_type = 'R' THEN -- Slightly promote waterway relations so that they are processed -- before their members. search_rank := search_rank - 1; END IF; IF is_major THEN search_rank := search_rank - 1; END IF; END IF; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION get_addr_tag_rank(key TEXT, country TEXT, OUT from_rank SMALLINT, OUT to_rank SMALLINT, OUT extent FLOAT) AS $$ DECLARE ranks RECORD; BEGIN from_rank := null; FOR ranks IN SELECT * FROM (SELECT l.rank_search, l.rank_address FROM address_levels l WHERE (l.country_code = country or l.country_code is NULL) AND l.class = 'place' AND l.type = key ORDER BY l.country_code LIMIT 1) r WHERE rank_address > 0 LOOP extent := reverse_place_diameter(ranks.rank_search); IF ranks.rank_address <= 4 THEN from_rank := 4; to_rank := 4; ELSEIF ranks.rank_address <= 9 THEN from_rank := 5; to_rank := 9; ELSEIF ranks.rank_address <= 12 THEN from_rank := 10; to_rank := 12; ELSEIF ranks.rank_address <= 16 THEN from_rank := 13; to_rank := 16; ELSEIF ranks.rank_address <= 21 THEN from_rank := 17; to_rank := 21; ELSEIF ranks.rank_address <= 24 THEN from_rank := 22; to_rank := 24; ELSE from_rank := 25; to_rank := 25; END IF; END LOOP; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION weigh_search(search_vector INT[], rankings TEXT, def_weight FLOAT) RETURNS FLOAT AS $$ DECLARE rank JSON; BEGIN FOR rank IN SELECT * FROM json_array_elements(rankings::JSON) LOOP IF true = ALL(SELECT x::int = ANY(search_vector) FROM json_array_elements_text(rank->1) as x) THEN RETURN (rank->>0)::float; END IF; END LOOP; RETURN def_weight; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; ================================================ FILE: lib-sql/functions/updates.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Helper functions for updates. -- Invalidate all placex affected by inserting a new place into the placex table. CREATE OR REPLACE FUNCTION update_invalidate_for_new_place(address_rank SMALLINT, is_area BOOLEAN, area_size FLOAT, otype TEXT, newgeom GEOMETRY) RETURNS BOOLEAN AS $$ DECLARE diameter FLOAT; BEGIN diameter := update_place_diameter(address_rank); is_area := COALESCE(is_area, ST_GeometryType(newgeom) in ('ST_Polygon','ST_MultiPolygon')); -- Update dependencies for address parts. -- Leaving countries out here because a newly inserted country is -- more likely incorrect mapping. IF address_rank BETWEEN 4 AND 25 THEN IF is_area THEN IF COALESCE(area_size, ST_Area(newgeom)) <= 2.0 THEN UPDATE placex SET indexed_status = 2 WHERE ST_Intersects(newgeom, placex.geometry) AND indexed_status = 0 AND ((rank_address = 0 and rank_search > address_rank) or rank_address > address_rank or (osm_type = 'N' and rank_address between 4 and 25)) AND (rank_search < 28 or name is not null); END IF; ELSEIF otype = 'N' THEN IF diameter > 0 THEN UPDATE placex SET indexed_status = 2 WHERE ST_DWithin(newgeom, placex.geometry, diameter) AND indexed_status = 0 AND ((rank_address = 0 and rank_search > address_rank) or rank_address > address_rank or (osm_type = 'N' and rank_address between 4 and 25)) AND (rank_search < 28 or name is not null); END IF; END IF; -- Addressable places may cause reparenting of addr:place-based addresses. IF address_rank BETWEEN 16 AND 25 THEN UPDATE placex SET indexed_status = 2 WHERE indexed_status = 0 AND rank_search > 27 AND address ? 'place' AND ST_DWithin(newgeom, placex.geometry, diameter); END IF; END IF; -- Roads may cause reparenting for POI places IF address_rank BETWEEN 26 AND 27 THEN UPDATE placex SET indexed_status = 2 WHERE indexed_status = 0 AND rank_search > 27 AND ST_DWithin(newgeom, placex.geometry, diameter); UPDATE location_property_osmline SET indexed_status = 2 WHERE indexed_status = 0 AND startnumber is not null AND ST_DWithin(newgeom, linegeo, diameter); END IF; RETURN TRUE; END; $$ LANGUAGE plpgsql; ================================================ FILE: lib-sql/functions/utils.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Assorted helper functions for the triggers. CREATE OR REPLACE FUNCTION get_center_point(place GEOMETRY) RETURNS GEOMETRY AS $$ DECLARE geom_type TEXT; BEGIN geom_type := ST_GeometryType(place); IF geom_type = 'ST_Point' THEN RETURN place; END IF; IF geom_type = 'ST_LineString' THEN RETURN ST_ReducePrecision(ST_LineInterpolatePoint(place, 0.5), 0.0000001); END IF; RETURN ST_ReducePrecision(ST_PointOnSurface(place), 0.0000001); END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION geometry_sector(partition INTEGER, place GEOMETRY) RETURNS INTEGER AS $$ BEGIN RETURN (partition*1000000) + (500-ST_X(place)::INTEGER)*1000 + (500-ST_Y(place)::INTEGER); END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION array_merge(a INTEGER[], b INTEGER[]) RETURNS INTEGER[] AS $$ DECLARE i INTEGER; r INTEGER[]; BEGIN IF array_upper(a, 1) IS NULL THEN RETURN COALESCE(b, '{}'::INTEGER[]); END IF; IF array_upper(b, 1) IS NULL THEN RETURN COALESCE(a, '{}'::INTEGER[]); END IF; r := a; FOR i IN 1..array_upper(b, 1) LOOP IF NOT (ARRAY[b[i]] <@ r) THEN r := r || b[i]; END IF; END LOOP; RETURN r; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; -- Return the node members with a given label from a relation member list -- as a set. -- -- \param members Member list in osm2pgsql middle format. -- \param memberLabels Array of labels to accept. -- -- \returns Set of OSM ids of nodes that are found. -- CREATE OR REPLACE FUNCTION get_rel_node_members(members TEXT[], memberLabels TEXT[]) RETURNS SETOF BIGINT AS $$ DECLARE i INTEGER; BEGIN FOR i IN 1..ARRAY_UPPER(members,1) BY 2 LOOP IF members[i+1] = ANY(memberLabels) AND upper(substring(members[i], 1, 1))::char(1) = 'N' THEN RETURN NEXT substring(members[i], 2)::bigint; END IF; END LOOP; RETURN; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION get_rel_node_members(members JSONB, memberLabels TEXT[]) RETURNS SETOF BIGINT AS $$ DECLARE member JSONB; BEGIN FOR member IN SELECT * FROM jsonb_array_elements(members) LOOP IF member->>'type' = 'N' and member->>'role' = ANY(memberLabels) THEN RETURN NEXT (member->>'ref')::bigint; END IF; END LOOP; RETURN; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; -- Copy 'name' to or from the default language. -- -- \param country_code Country code of the object being named. -- \param[inout] name List of names of the object. -- -- If the country named by country_code has a single default language, -- then a `name` tag is copied to `name:` if this tag does -- not yet exist and vice versa. CREATE OR REPLACE FUNCTION add_default_place_name(country_code VARCHAR(2), INOUT name HSTORE) AS $$ DECLARE default_language VARCHAR(10); BEGIN IF name is not null AND array_upper(akeys(name),1) > 1 THEN default_language := get_country_language_code(country_code); IF default_language IS NOT NULL THEN IF name ? 'name' AND NOT name ? ('name:'||default_language) THEN name := name || hstore(('name:'||default_language), (name -> 'name')); ELSEIF name ? ('name:'||default_language) AND NOT name ? 'name' THEN name := name || hstore('name', (name -> ('name:'||default_language))); END IF; END IF; END IF; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; -- Find the best-matching postcode for the given geometry CREATE OR REPLACE FUNCTION get_nearest_postcode(country VARCHAR(2), geom GEOMETRY) RETURNS TEXT AS $$ DECLARE outcode TEXT; cnt INTEGER; location RECORD; BEGIN -- If the geometry is an area then only one postcode must be within -- that area, otherwise consider the area as not having a postcode. IF ST_GeometryType(geom) in ('ST_Polygon','ST_MultiPolygon') THEN SELECT min(postcode), count(*) FROM (SELECT postcode FROM location_postcodes WHERE ST_Contains(geom, location_postcodes.centroid) AND country_code = country LIMIT 2) sub INTO outcode, cnt; IF cnt = 1 THEN RETURN outcode; END IF; RETURN null; END IF; -- Otherwise: be fully within the coverage area of a postcode FOR location IN SELECT postcode FROM location_postcodes p WHERE ST_Covers(p.geometry, geom) AND p.country_code = country ORDER BY osm_id is null, ST_Distance(p.centroid, geom) LIMIT 1 LOOP RETURN location.postcode; END LOOP; RETURN NULL; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION get_country_code(place geometry) RETURNS TEXT AS $$ DECLARE nearcountry RECORD; countries TEXT[]; BEGIN -- RAISE WARNING 'get_country_code, start: %', ST_AsText(place); -- Try for a OSM polygon SELECT array_agg(country_code) FROM location_area_country WHERE country_code is not null and st_covers(geometry, place) INTO countries; IF array_length(countries, 1) = 1 THEN RETURN countries[1]; END IF; IF array_length(countries, 1) > 1 THEN -- more than one country found, confirm against the fallback data what to choose FOR nearcountry IN SELECT country_code FROM country_osm_grid WHERE ST_Covers(geometry, place) AND country_code = ANY(countries) ORDER BY area ASC LOOP RETURN nearcountry.country_code; END LOOP; -- Still nothing? Choose the country code with the smallest partition number. -- And failing that, just go by the alphabet. FOR nearcountry IN SELECT cc, (SELECT partition FROM country_name WHERE country_code = cc) as partition FROM unnest(countries) cc ORDER BY partition, cc LOOP RETURN nearcountry.cc; END LOOP; -- Should never be reached. RETURN countries[1]; END IF; -- RAISE WARNING 'osm fallback: %', ST_AsText(place); -- Try for OSM fallback data -- The order is to deal with places like HongKong that are 'states' within another polygon FOR nearcountry IN SELECT country_code from country_osm_grid WHERE st_covers(geometry, place) order by area asc limit 1 LOOP RETURN nearcountry.country_code; END LOOP; -- RAISE WARNING 'near osm fallback: %', ST_AsText(place); RETURN NULL; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION get_country_language_code(search_country_code VARCHAR(2)) RETURNS TEXT AS $$ DECLARE nearcountry RECORD; BEGIN FOR nearcountry IN SELECT distinct country_default_language_code from country_name WHERE country_code = search_country_code limit 1 LOOP RETURN lower(nearcountry.country_default_language_code); END LOOP; RETURN NULL; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION get_partition(in_country_code VARCHAR(10)) RETURNS INTEGER AS $$ DECLARE nearcountry RECORD; BEGIN FOR nearcountry IN SELECT partition from country_name where country_code = in_country_code LOOP RETURN nearcountry.partition; END LOOP; RETURN 0; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; -- Find the parent of an address with addr:street/addr:place tag. -- -- \param token_info Naming info with the address information. -- \param partition Partition where to search the parent. -- \param centroid Location of the address. -- -- \return Place ID of the parent if one was found, NULL otherwise. CREATE OR REPLACE FUNCTION find_parent_for_address(token_info JSONB, partition SMALLINT, centroid GEOMETRY) RETURNS BIGINT AS $$ DECLARE parent_place_id BIGINT; BEGIN -- Check for addr:street attributes parent_place_id := getNearestNamedRoadPlaceId(partition, centroid, token_info); IF parent_place_id is not null THEN {% if debug %}RAISE WARNING 'Get parent from addr:street: %', parent_place_id;{% endif %} RETURN parent_place_id; END IF; -- Check for addr:place attributes. parent_place_id := getNearestNamedPlacePlaceId(partition, centroid, token_info); {% if debug %}RAISE WARNING 'Get parent from addr:place: %', parent_place_id;{% endif %} RETURN parent_place_id; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION delete_location(OLD_place_id BIGINT) RETURNS BOOLEAN AS $$ DECLARE BEGIN DELETE FROM location_area where place_id = OLD_place_id; -- TODO:location_area RETURN true; END; $$ LANGUAGE plpgsql; -- Return the bounding box of the geometry buffered by the given number -- of meters. CREATE OR REPLACE FUNCTION expand_by_meters(geom GEOMETRY, meters FLOAT) RETURNS GEOMETRY AS $$ SELECT ST_Envelope(ST_Buffer(geom::geography, meters, 1)::geometry) $$ LANGUAGE sql IMMUTABLE PARALLEL SAFE; -- Create a bounding box with an extent computed from the radius (in meters) -- which in turn is derived from the given search rank. CREATE OR REPLACE FUNCTION place_node_fuzzy_area(geom GEOMETRY, rank_search INTEGER) RETURNS GEOMETRY AS $$ DECLARE radius FLOAT := 500; BEGIN IF rank_search <= 16 THEN -- city radius := 15000; ELSIF rank_search <= 18 THEN -- town radius := 4000; ELSIF rank_search <= 19 THEN -- village radius := 2000; ELSIF rank_search <= 20 THEN -- hamlet radius := 1000; END IF; RETURN expand_by_meters(geom, radius); END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION add_location(place_id BIGINT, country_code varchar(2), partition INTEGER, keywords INTEGER[], rank_search INTEGER, rank_address INTEGER, in_postcode TEXT, geometry GEOMETRY, centroid GEOMETRY) RETURNS BOOLEAN AS $$ DECLARE postcode TEXT; BEGIN -- add postcode only if it contains a single entry, i.e. ignore postcode lists postcode := NULL; IF in_postcode is not null AND in_postcode not similar to '%(,|;)%' THEN postcode := upper(trim (in_postcode)); END IF; IF ST_Dimension(geometry) = 2 THEN RETURN insertLocationAreaLarge(partition, place_id, country_code, keywords, rank_search, rank_address, false, postcode, centroid, geometry); END IF; IF ST_Dimension(geometry) = 0 THEN RETURN insertLocationAreaLarge(partition, place_id, country_code, keywords, rank_search, rank_address, true, postcode, centroid, place_node_fuzzy_area(geometry, rank_search)); END IF; RETURN false; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION quad_split_geometry(geometry GEOMETRY, maxarea FLOAT, maxdepth INTEGER) RETURNS SETOF GEOMETRY AS $$ DECLARE xmin FLOAT; ymin FLOAT; xmax FLOAT; ymax FLOAT; xmid FLOAT; ymid FLOAT; secgeo GEOMETRY; secbox GEOMETRY; seg INTEGER; geo RECORD; area FLOAT; remainingdepth INTEGER; BEGIN -- RAISE WARNING 'quad_split_geometry: maxarea=%, depth=%',maxarea,maxdepth; IF not ST_IsValid(geometry) THEN RETURN; END IF; IF ST_Dimension(geometry) != 2 OR maxdepth <= 1 THEN RETURN NEXT geometry; RETURN; END IF; remainingdepth := maxdepth - 1; area := ST_AREA(geometry); IF area < maxarea THEN RETURN NEXT geometry; RETURN; END IF; xmin := st_xmin(geometry); xmax := st_xmax(geometry); ymin := st_ymin(geometry); ymax := st_ymax(geometry); secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(ymin,xmin),ST_Point(ymax,xmax)),4326); -- if the geometry completely covers the box don't bother to slice any more IF ST_AREA(secbox) = area THEN RETURN NEXT geometry; RETURN; END IF; xmid := (xmin+xmax)/2; ymid := (ymin+ymax)/2; FOR seg IN 1..4 LOOP IF seg = 1 THEN secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(xmin,ymin),ST_Point(xmid,ymid)),4326); END IF; IF seg = 2 THEN secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(xmin,ymid),ST_Point(xmid,ymax)),4326); END IF; IF seg = 3 THEN secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(xmid,ymin),ST_Point(xmax,ymid)),4326); END IF; IF seg = 4 THEN secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(xmid,ymid),ST_Point(xmax,ymax)),4326); END IF; secgeo := st_intersection(geometry, secbox); IF NOT ST_IsEmpty(secgeo) AND ST_Dimension(secgeo) = 2 THEN FOR geo IN SELECT quad_split_geometry(secgeo, maxarea, remainingdepth) as geom LOOP IF NOT ST_IsEmpty(geo.geom) AND ST_Dimension(geo.geom) = 2 THEN RETURN NEXT geo.geom; END IF; END LOOP; END IF; END LOOP; RETURN; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION split_geometry(geometry GEOMETRY) RETURNS SETOF GEOMETRY AS $$ DECLARE geo RECORD; BEGIN IF ST_GeometryType(geometry) = 'ST_MultiPolygon' and ST_Area(geometry) * 10 > ST_Area(Box2D(geometry)) THEN FOR geo IN SELECT quad_split_geometry(g, 0.25, 20) as geom FROM (SELECT (ST_Dump(geometry)).geom::geometry(Polygon, 4326) AS g) xx LOOP RETURN NEXT geo.geom; END LOOP; ELSE FOR geo IN SELECT quad_split_geometry(geometry, 0.25, 20) as geom LOOP RETURN NEXT geo.geom; END LOOP; END IF; RETURN; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION simplify_large_polygons(geometry GEOMETRY) RETURNS GEOMETRY AS $$ BEGIN IF ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') and ST_MemSize(geometry) > 3000000 THEN geometry := ST_SimplifyPreserveTopology(geometry, 0.0001); END IF; RETURN geometry; END; $$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION place_force_delete(placeid BIGINT) RETURNS BOOLEAN AS $$ DECLARE osmid BIGINT; osmtype character(1); pclass text; ptype text; BEGIN SELECT osm_type, osm_id, class, type FROM placex WHERE place_id = placeid INTO osmtype, osmid, pclass, ptype; DELETE FROM import_polygon_delete where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype; DELETE FROM import_polygon_error where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype; -- force delete by directly entering it into the to-be-deleted table INSERT INTO place_to_be_deleted (osm_type, osm_id, class, type, deferred) VALUES(osmtype, osmid, pclass, ptype, false); PERFORM flush_deleted_places(); RETURN TRUE; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_force_update(placeid BIGINT) RETURNS BOOLEAN AS $$ DECLARE placegeom GEOMETRY; geom GEOMETRY; diameter FLOAT; rank SMALLINT; BEGIN UPDATE placex SET indexed_status = 2 WHERE place_id = placeid; SELECT geometry, rank_address INTO placegeom, rank FROM placex WHERE place_id = placeid; IF placegeom IS NOT NULL AND ST_IsValid(placegeom) THEN IF ST_GeometryType(placegeom) in ('ST_Polygon','ST_MultiPolygon') AND rank > 0 THEN FOR geom IN SELECT split_geometry(placegeom) LOOP UPDATE placex SET indexed_status = 2 WHERE ST_Intersects(geom, placex.geometry) and indexed_status = 0 and ((rank_address = 0 and rank_search > rank) or rank_address > rank) and (rank_search < 28 or name is not null or (rank >= 16 and address ? 'place')); END LOOP; ELSE diameter := update_place_diameter(rank); IF diameter > 0 THEN IF rank >= 26 THEN -- roads may cause reparenting for >27 rank places update placex set indexed_status = 2 where indexed_status = 0 and rank_search > rank and ST_DWithin(placex.geometry, placegeom, diameter); ELSEIF rank >= 16 THEN -- up to rank 16, street-less addresses may need reparenting update placex set indexed_status = 2 where indexed_status = 0 and rank_search > rank and ST_DWithin(placex.geometry, placegeom, diameter) and (rank_search < 28 or name is not null or address ? 'place'); ELSE -- for all other places the search terms may change as well update placex set indexed_status = 2 where indexed_status = 0 and rank_search > rank and ST_DWithin(placex.geometry, placegeom, diameter) and (rank_search < 28 or name is not null); END IF; END IF; END IF; RETURN TRUE; END IF; RETURN FALSE; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION flush_deleted_places() RETURNS INTEGER AS $$ BEGIN -- deleting large polygons can have a massive effect on the system - require manual intervention to let them through INSERT INTO import_polygon_delete (osm_type, osm_id, class, type) SELECT osm_type, osm_id, class, type FROM place_to_be_deleted WHERE deferred; -- delete from place table ALTER TABLE place DISABLE TRIGGER place_before_delete; DELETE FROM place USING place_to_be_deleted WHERE place.osm_type = place_to_be_deleted.osm_type and place.osm_id = place_to_be_deleted.osm_id and place.class = place_to_be_deleted.class and place.type = place_to_be_deleted.type and not deferred; ALTER TABLE place ENABLE TRIGGER place_before_delete; -- Mark for delete in the placex table UPDATE placex SET indexed_status = 100 FROM place_to_be_deleted WHERE placex.osm_type = 'N' and place_to_be_deleted.osm_type = 'N' and placex.osm_id = place_to_be_deleted.osm_id and placex.class = place_to_be_deleted.class and placex.type = place_to_be_deleted.type and not deferred; UPDATE placex SET indexed_status = 100 FROM place_to_be_deleted WHERE placex.osm_type = 'W' and place_to_be_deleted.osm_type = 'W' and placex.osm_id = place_to_be_deleted.osm_id and placex.class = place_to_be_deleted.class and placex.type = place_to_be_deleted.type and not deferred; UPDATE placex SET indexed_status = 100 FROM place_to_be_deleted WHERE placex.osm_type = 'R' and place_to_be_deleted.osm_type = 'R' and placex.osm_id = place_to_be_deleted.osm_id and placex.class = place_to_be_deleted.class and placex.type = place_to_be_deleted.type and not deferred; -- Clear todo list. TRUNCATE TABLE place_to_be_deleted; -- delete from place_interpolation table ALTER TABLE place_interpolation DISABLE TRIGGER place_interpolation_before_delete; DELETE FROM place_interpolation p USING place_interpolation_to_be_deleted d WHERE p.osm_id = d.osm_id; ALTER TABLE place_interpolation ENABLE TRIGGER place_interpolation_before_delete; UPDATE location_property_osmline o SET indexed_status = 100 FROM place_interpolation_to_be_deleted d WHERE o.osm_id = d.osm_id; TRUNCATE TABLE place_interpolation_to_be_deleted; RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_update_entrances(placeid BIGINT, osmid BIGINT) RETURNS INTEGER AS $$ DECLARE entrance RECORD; osm_ids BIGINT[]; BEGIN osm_ids := '{}'; FOR entrance in SELECT osm_id, type, geometry, extratags FROM place_entrance WHERE osm_id IN (SELECT unnest(nodes) FROM planet_osm_ways WHERE id=osmid) LOOP osm_ids := array_append(osm_ids, entrance.osm_id); INSERT INTO placex_entrance (place_id, osm_id, type, location, extratags) VALUES (placeid, entrance.osm_id, entrance.type, entrance.geometry, entrance.extratags) ON CONFLICT (place_id, osm_id) DO UPDATE SET type = excluded.type, location = excluded.location, extratags = excluded.extratags; END LOOP; IF array_length(osm_ids, 1) > 0 THEN DELETE FROM placex_entrance WHERE place_id=placeid AND NOT osm_id=ANY(osm_ids); ELSE DELETE FROM placex_entrance WHERE place_id=placeid; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; ================================================ FILE: lib-sql/functions.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. {% include('functions/utils.sql') %} {% include('functions/ranking.sql') %} {% include('functions/importance.sql') %} {% include('functions/interpolation.sql') %} {% include('functions/updates.sql') %} {% if 'place' in db.tables %} {% include 'functions/place_triggers.sql' %} {% endif %} {% if 'placex' in db.tables %} {% include 'functions/placex_triggers.sql' %} {% endif %} {% if 'location_postcodes' in db.tables %} {% include 'functions/postcode_triggers.sql' %} {% endif %} {% include('functions/partition-functions.sql') %} {% include('functions/associated_street_triggers.sql') %} ================================================ FILE: lib-sql/grants.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- -- Grant read-only access to the web user for all Nominatim tables. -- Core tables GRANT SELECT ON import_status TO "{{config.DATABASE_WEBUSER}}"; GRANT SELECT ON country_name TO "{{config.DATABASE_WEBUSER}}"; GRANT SELECT ON nominatim_properties TO "{{config.DATABASE_WEBUSER}}"; -- Location tables GRANT SELECT ON location_property_tiger TO "{{config.DATABASE_WEBUSER}}"; GRANT SELECT ON location_property_osmline TO "{{config.DATABASE_WEBUSER}}"; GRANT SELECT ON location_postcodes TO "{{config.DATABASE_WEBUSER}}"; -- Search tables {% if not db.reverse_only %} GRANT SELECT ON search_name TO "{{config.DATABASE_WEBUSER}}"; {% endif %} -- Main place tables GRANT SELECT ON placex TO "{{config.DATABASE_WEBUSER}}"; GRANT SELECT ON place_addressline TO "{{config.DATABASE_WEBUSER}}"; GRANT SELECT ON placex_entrance TO "{{config.DATABASE_WEBUSER}}"; -- Error/delete tracking tables GRANT SELECT ON import_polygon_error TO "{{config.DATABASE_WEBUSER}}"; GRANT SELECT ON import_polygon_delete TO "{{config.DATABASE_WEBUSER}}"; -- Country grid GRANT SELECT ON country_osm_grid TO "{{config.DATABASE_WEBUSER}}"; -- Tokenizer tables (word table) {% if 'word' in db.tables %} GRANT SELECT ON word TO "{{config.DATABASE_WEBUSER}}"; {% endif %} -- Special phrase tables {% for table in db.tables %} {% if table.startswith('place_classtype_') %} GRANT SELECT ON {{ table }} TO "{{config.DATABASE_WEBUSER}}"; {% endif %} {% endfor %} ================================================ FILE: lib-sql/indices.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Indices used only during search and update. -- These indices are created only after the indexing process is done. CREATE INDEX IF NOT EXISTS idx_place_addressline_address_place_id ON place_addressline USING BTREE (address_place_id) {{db.tablespace.search_index}}; --- CREATE INDEX IF NOT EXISTS idx_placex_rank_search ON placex USING BTREE (rank_search) {{db.tablespace.search_index}}; --- CREATE INDEX IF NOT EXISTS idx_placex_rank_address ON placex USING BTREE (rank_address) {{db.tablespace.search_index}}; --- CREATE INDEX IF NOT EXISTS idx_placex_parent_place_id ON placex USING BTREE (parent_place_id) {{db.tablespace.search_index}} WHERE parent_place_id IS NOT NULL; --- CREATE INDEX IF NOT EXISTS idx_placex_geometry ON placex USING GIST (geometry) {{db.tablespace.search_index}}; --- -- Index is needed during import but can be dropped as soon as a full -- geometry index is in place. The partial index is almost as big as the full -- index. DROP INDEX IF EXISTS idx_placex_geometry_lower_rank_ways; --- CREATE INDEX IF NOT EXISTS idx_placex_geometry_reverse_lookupPolygon ON placex USING gist (geometry) {{db.tablespace.search_index}} WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon') AND rank_address between 4 and 25 AND name is not null AND indexed_status = 0 AND linked_place_id is null; --- -- used in reverse large area lookup CREATE INDEX IF NOT EXISTS idx_placex_geometry_reverse_lookupPlaceNode ON placex USING gist (ST_Buffer(geometry, reverse_place_diameter(rank_search))) {{db.tablespace.search_index}} WHERE rank_address between 4 and 25 AND name is not null AND linked_place_id is null AND osm_type = 'N'; --- CREATE INDEX IF NOT EXISTS idx_osmline_parent_place_id ON location_property_osmline USING BTREE (parent_place_id) {{db.tablespace.search_index}} WHERE parent_place_id is not null; --- CREATE INDEX IF NOT EXISTS idx_osmline_parent_osm_id ON location_property_osmline USING BTREE (osm_id) {{db.tablespace.search_index}}; {% if drop %} --- DROP INDEX IF EXISTS idx_placex_geometry_address_area_candidates; DROP INDEX IF EXISTS idx_placex_geometry_buildings; DROP INDEX IF EXISTS idx_placex_wikidata; DROP INDEX IF EXISTS idx_placex_rank_address_sector; DROP INDEX IF EXISTS idx_placex_rank_boundaries_sector; {% else %} -- Indices only needed for updating. --- CREATE INDEX IF NOT EXISTS idx_location_area_country_place_id ON location_area_country USING BTREE (place_id) {{db.tablespace.address_index}}; --- CREATE UNIQUE INDEX IF NOT EXISTS idx_place_osm_unique ON place USING btree(osm_id, osm_type, class, type) {{db.tablespace.address_index}}; --- -- Table needed for running updates with osm2pgsql on place. CREATE TABLE IF NOT EXISTS place_to_be_deleted ( osm_type CHAR(1) NOT NULL, osm_id BIGINT NOT NULL, class TEXT NOT NULL, type TEXT NOT NULL, deferred BOOLEAN NOT NULL ); CREATE TABLE IF NOT EXISTS place_interpolation_to_be_deleted ( osm_id BIGINT NOT NULL ); --- CREATE INDEX IF NOT EXISTS idx_location_postcodes_parent_place_id ON location_postcodes USING BTREE (parent_place_id) {{db.tablespace.address_index}}; {% endif %} -- Indices only needed for search. {% if 'search_name' in db.tables %} --- CREATE INDEX IF NOT EXISTS idx_search_name_nameaddress_vector ON search_name USING GIN (nameaddress_vector) WITH (fastupdate = off) {{db.tablespace.search_index}}; --- CREATE INDEX IF NOT EXISTS idx_search_name_name_vector ON search_name USING GIN (name_vector) WITH (fastupdate = off) {{db.tablespace.search_index}}; --- CREATE INDEX IF NOT EXISTS idx_search_name_centroid ON search_name USING GIST (centroid) {{db.tablespace.search_index}}; --- CREATE INDEX IF NOT EXISTS idx_placex_housenumber ON placex USING btree (parent_place_id) INCLUDE (housenumber) {{db.tablespace.search_index}} WHERE housenumber is not null; --- CREATE INDEX IF NOT EXISTS idx_osmline_parent_osm_id_with_hnr ON location_property_osmline USING btree(parent_place_id) INCLUDE (startnumber, endnumber) {{db.tablespace.search_index}} WHERE startnumber is not null; {% endif %} ================================================ FILE: lib-sql/partition-tables.src.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. drop table IF EXISTS search_name_blank CASCADE; CREATE TABLE search_name_blank ( place_id BIGINT NOT NULL, address_rank smallint NOT NULL, name_vector integer[] NOT NULL, centroid GEOMETRY(Geometry, 4326) NOT NULL ); {% for partition in db.partitions %} CREATE TABLE location_area_large_{{ partition }} () INHERITS (location_area_large) {{db.tablespace.address_data}}; CREATE INDEX idx_location_area_large_{{ partition }}_place_id ON location_area_large_{{ partition }} USING BTREE (place_id) {{db.tablespace.address_index}}; CREATE INDEX idx_location_area_large_{{ partition }}_geometry ON location_area_large_{{ partition }} USING GIST (geometry) {{db.tablespace.address_index}}; CREATE TABLE search_name_{{ partition }} () INHERITS (search_name_blank) {{db.tablespace.address_data}}; CREATE UNIQUE INDEX idx_search_name_{{ partition }}_place_id ON search_name_{{ partition }} USING BTREE (place_id) {{db.tablespace.address_index}}; CREATE INDEX idx_search_name_{{ partition }}_centroid_street ON search_name_{{ partition }} USING GIST (centroid) {{db.tablespace.address_index}} WHERE address_rank between 26 and 27; CREATE INDEX idx_search_name_{{ partition }}_centroid_place ON search_name_{{ partition }} USING GIST (centroid) {{db.tablespace.address_index}} WHERE address_rank between 2 and 25; DROP TABLE IF EXISTS location_road_{{ partition }}; CREATE TABLE location_road_{{ partition }} ( place_id BIGINT NOT NULL, partition SMALLINT NOT NULL, country_code VARCHAR(2), geometry GEOMETRY(Geometry, 4326) NOT NULL ) {{db.tablespace.address_data}}; CREATE INDEX idx_location_road_{{ partition }}_geometry ON location_road_{{ partition }} USING GIST (geometry) {{db.tablespace.address_index}}; CREATE UNIQUE INDEX idx_location_road_{{ partition }}_place_id ON location_road_{{ partition }} USING BTREE (place_id) {{db.tablespace.address_index}}; {% endfor %} ================================================ FILE: lib-sql/postcode_tables.sql ================================================ -- 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. DROP TABLE IF EXISTS gb_postcode; CREATE TABLE gb_postcode ( id integer, postcode character varying(9), geometry geometry, CONSTRAINT enforce_dims_geometry CHECK ((st_ndims(geometry) = 2)), CONSTRAINT enforce_srid_geometry CHECK ((st_srid(geometry) = 4326)) ); DROP TABLE IF EXISTS us_postcode; CREATE TABLE us_postcode ( postcode text, x double precision, y double precision ); ================================================ FILE: lib-sql/table-triggers.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- insert creates the location tables, creates location indexes if indexed == true CREATE TRIGGER placex_before_insert BEFORE INSERT ON placex FOR EACH ROW EXECUTE PROCEDURE placex_insert(); CREATE TRIGGER osmline_before_insert BEFORE INSERT ON location_property_osmline FOR EACH ROW EXECUTE PROCEDURE osmline_insert(); -- update insert creates the location tables CREATE TRIGGER placex_before_update BEFORE UPDATE ON placex FOR EACH ROW EXECUTE PROCEDURE placex_update(); CREATE TRIGGER osmline_before_update BEFORE UPDATE ON location_property_osmline FOR EACH ROW EXECUTE PROCEDURE osmline_update(); -- diff update triggers CREATE TRIGGER placex_before_delete AFTER DELETE ON placex FOR EACH ROW EXECUTE PROCEDURE placex_delete(); CREATE TRIGGER place_before_delete BEFORE DELETE ON place FOR EACH ROW EXECUTE PROCEDURE place_delete(); CREATE TRIGGER place_before_insert BEFORE INSERT ON place FOR EACH ROW EXECUTE PROCEDURE place_insert(); CREATE TRIGGER location_postcode_before_update BEFORE UPDATE ON location_postcodes FOR EACH ROW EXECUTE PROCEDURE postcodes_update(); CREATE TRIGGER location_postcodes_before_delete BEFORE DELETE ON location_postcodes FOR EACH ROW EXECUTE PROCEDURE postcodes_delete(); CREATE TRIGGER location_postcodes_before_insert BEFORE INSERT ON location_postcodes FOR EACH ROW EXECUTE PROCEDURE postcodes_insert(); CREATE TRIGGER place_interpolation_before_insert BEFORE INSERT ON place_interpolation FOR EACH ROW EXECUTE PROCEDURE place_interpolation_insert(); CREATE TRIGGER place_interpolation_before_delete BEFORE DELETE ON place_interpolation FOR EACH ROW EXECUTE PROCEDURE place_interpolation_delete(); -- Propagate changes to associatedStreet relations to house members -- so that the indexer re-computes their parent_place_id. CREATE TRIGGER place_associated_street_update AFTER INSERT OR DELETE ON place_associated_street FOR EACH ROW EXECUTE FUNCTION invalidate_associated_street_members(); ================================================ FILE: lib-sql/tables/addressline.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TABLE IF EXISTS place_addressline; CREATE TABLE place_addressline ( place_id BIGINT NOT NULL, address_place_id BIGINT NOT NULL, distance FLOAT NOT NULL, cached_rank_address SMALLINT NOT NULL, fromarea boolean NOT NULL, isaddress boolean NOT NULL ) {{db.tablespace.search_data}}; CREATE INDEX idx_place_addressline_place_id ON place_addressline USING BTREE (place_id) {{db.tablespace.search_index}}; ================================================ FILE: lib-sql/tables/entrance.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Table to store location of entrance nodes DROP TABLE IF EXISTS placex_entrance; CREATE TABLE placex_entrance ( place_id BIGINT NOT NULL, osm_id BIGINT NOT NULL, type TEXT NOT NULL, location GEOMETRY(Point, 4326) NOT NULL, extratags HSTORE ); CREATE UNIQUE INDEX idx_placex_entrance_place_id_osm_id ON placex_entrance USING BTREE (place_id, osm_id) {{db.tablespace.search_index}}; ================================================ FILE: lib-sql/tables/import_reports.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TABLE IF EXISTS import_polygon_error; CREATE TABLE import_polygon_error ( osm_id BIGINT, osm_type CHAR(1), class TEXT NOT NULL, type TEXT NOT NULL, name HSTORE, country_code varchar(2), updated timestamp, errormessage text, prevgeometry GEOMETRY(Geometry, 4326), newgeometry GEOMETRY(Geometry, 4326) ); CREATE INDEX idx_import_polygon_error_osmid ON import_polygon_error USING BTREE (osm_type, osm_id); DROP TABLE IF EXISTS import_polygon_delete; CREATE TABLE import_polygon_delete ( osm_id BIGINT, osm_type CHAR(1), class TEXT NOT NULL, type TEXT NOT NULL ); CREATE INDEX idx_import_polygon_delete_osmid ON import_polygon_delete USING BTREE (osm_type, osm_id); ================================================ FILE: lib-sql/tables/importance_tables.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. {% if 'wikimedia_importance' not in db.tables and 'wikipedia_article' not in db.tables %} -- create dummy tables here if nothing was imported CREATE TABLE wikimedia_importance ( language TEXT NOT NULL, title TEXT NOT NULL, importance double precision NOT NULL, wikidata TEXT ) {{db.tablespace.address_data}}; {% endif %} ================================================ FILE: lib-sql/tables/interpolation.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TABLE IF EXISTS location_property_osmline; CREATE TABLE location_property_osmline ( place_id BIGINT NOT NULL, osm_id BIGINT NOT NULL, parent_place_id BIGINT, geometry_sector INTEGER NOT NULL, indexed_date TIMESTAMP, type TEXT, startnumber INTEGER, endnumber INTEGER, step SMALLINT, partition SMALLINT NOT NULL, indexed_status SMALLINT NOT NULL, linegeo GEOMETRY(Geometry, 4326) NOT NULL, address HSTORE, token_info JSONB, -- custom column for tokenizer use only postcode TEXT, country_code VARCHAR(2) ){{db.tablespace.search_data}}; CREATE UNIQUE INDEX idx_osmline_place_id ON location_property_osmline USING BTREE (place_id) {{db.tablespace.search_index}}; CREATE INDEX idx_osmline_geometry_sector ON location_property_osmline USING BTREE (geometry_sector) {{db.tablespace.address_index}}; CREATE INDEX idx_osmline_linegeo ON location_property_osmline USING GIST (linegeo) {{db.tablespace.search_index}} WHERE startnumber is not null; ================================================ FILE: lib-sql/tables/location_area.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TABLE IF EXISTS location_area CASCADE; CREATE TABLE location_area ( place_id BIGINT NOT NULL, keywords INTEGER[] NOT NULL, partition SMALLINT NOT NULL, rank_search SMALLINT NOT NULL, rank_address SMALLINT NOT NULL, country_code VARCHAR(2), isguess BOOL NOT NULL, postcode TEXT, centroid GEOMETRY(Point, 4326) NOT NULL, geometry GEOMETRY(Geometry, 4326) NOT NULL ); CREATE TABLE location_area_large () INHERITS (location_area); DROP TABLE IF EXISTS location_area_country; CREATE TABLE location_area_country ( place_id BIGINT NOT NULL, country_code varchar(2) NOT NULL, geometry GEOMETRY(Geometry, 4326) NOT NULL ) {{db.tablespace.address_data}}; CREATE INDEX idx_location_area_country_geometry ON location_area_country USING GIST (geometry) {{db.tablespace.address_index}}; ================================================ FILE: lib-sql/tables/nominatim_properties.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TABLE IF EXISTS nominatim_properties; CREATE TABLE nominatim_properties ( property TEXT NOT NULL, value TEXT ); ================================================ FILE: lib-sql/tables/placex.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- placex - main table for searchable places DROP TABLE IF EXISTS placex; CREATE TABLE placex ( place_id BIGINT NOT NULL, parent_place_id BIGINT, linked_place_id BIGINT, importance FLOAT NOT NULL, indexed_date TIMESTAMP, geometry_sector INTEGER NOT NULL, rank_address SMALLINT NOT NULL, rank_search SMALLINT NOT NULL, partition SMALLINT NOT NULL, indexed_status SMALLINT NOT NULL, LIKE place INCLUDING CONSTRAINTS, wikipedia TEXT, -- calculated wikipedia article name (language:title) token_info JSONB, -- custom column for tokenizer use only country_code varchar(2), housenumber TEXT, postcode TEXT, centroid GEOMETRY(Geometry, 4326) NOT NULL ) {{db.tablespace.search_data}}; CREATE UNIQUE INDEX idx_place_id ON placex USING BTREE (place_id) {{db.tablespace.search_index}}; {% for osm_type in ('N', 'W', 'R') %} CREATE INDEX idx_placex_osmid_{{osm_type | lower}} ON placex USING BTREE (osm_id) {{db.tablespace.search_index}} WHERE osm_type = '{{osm_type}}'; {% endfor %} -- Usage: - removing linkage status on update -- - lookup linked places for /details CREATE INDEX idx_placex_linked_place_id ON placex USING BTREE (linked_place_id) {{db.tablespace.address_index}} WHERE linked_place_id IS NOT NULL; -- Usage: - check that admin boundaries do not overtake each other rank-wise -- - check that place node in a admin boundary with the same address level -- - boundary is not completely contained in a place area -- - parenting of large-area or unparentable features CREATE INDEX idx_placex_geometry_address_area_candidates ON placex USING gist (geometry) {{db.tablespace.address_index}} WHERE rank_address between 1 and 25 and ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon'); -- Usage: - POI is within building with housenumber CREATE INDEX idx_placex_geometry_buildings ON placex USING SPGIST (geometry) {{db.tablespace.address_index}} WHERE address is not null and rank_search = 30 and ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon'); -- Usage: - linking of similar named places to boundaries -- - linking of place nodes with same type to boundaries CREATE INDEX idx_placex_geometry_placenode ON placex USING SPGIST (geometry) {{db.tablespace.address_index}} WHERE osm_type = 'N' and rank_search < 26 and class = 'place'; -- Usage: - is node part of a way? -- - find parent of interpolation spatially CREATE INDEX idx_placex_geometry_lower_rank_ways ON placex USING SPGIST (geometry) {{db.tablespace.address_index}} WHERE osm_type = 'W' and rank_search >= 26; -- Usage: - linking place nodes by wikidata tag to boundaries CREATE INDEX idx_placex_wikidata on placex USING BTREE ((extratags -> 'wikidata')) {{db.tablespace.address_index}} WHERE extratags ? 'wikidata' and class = 'place' and osm_type = 'N' and rank_search < 26; -- The following two indexes function as a todo list for indexing. CREATE INDEX idx_placex_rank_address_sector ON placex USING BTREE (rank_address, geometry_sector) {{db.tablespace.address_index}} WHERE indexed_status > 0; CREATE INDEX idx_placex_rank_boundaries_sector ON placex USING BTREE (rank_search, geometry_sector) {{db.tablespace.address_index}} WHERE class = 'boundary' and type = 'administrative' and indexed_status > 0; ================================================ FILE: lib-sql/tables/postcodes.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TABLE IF EXISTS location_postcodes; CREATE TABLE location_postcodes ( place_id BIGINT NOT NULL, parent_place_id BIGINT, osm_id BIGINT, rank_search SMALLINT NOT NULL, indexed_status SMALLINT NOT NULL, indexed_date TIMESTAMP, country_code varchar(2) NOT NULL, postcode TEXT NOT NULL, centroid GEOMETRY(Geometry, 4326) NOT NULL, geometry GEOMETRY(Geometry, 4326) NOT NULL ); CREATE UNIQUE INDEX idx_location_postcodes_id ON location_postcodes USING BTREE (place_id) {{db.tablespace.search_index}}; CREATE INDEX idx_location_postcodes_geometry ON location_postcodes USING GIST (geometry) {{db.tablespace.search_index}}; CREATE INDEX idx_location_postcodes_centroid ON location_postcodes USING GIST (centroid) {{db.tablespace.search_index}}; CREATE INDEX IF NOT EXISTS idx_location_postcodes_postcode ON location_postcodes USING BTREE (postcode, country_code) {{db.tablespace.search_index}}; CREATE INDEX IF NOT EXISTS idx_location_postcodes_osmid ON location_postcodes USING BTREE (osm_id) {{db.tablespace.search_index}}; ================================================ FILE: lib-sql/tables/search_name.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TABLE IF EXISTS search_name; {% if not create_reverse_only %} CREATE TABLE search_name ( place_id BIGINT NOT NULL, importance FLOAT NOT NULL, address_rank SMALLINT NOT NULL, name_vector integer[] NOT NULL, nameaddress_vector integer[] NOT NULL, country_code varchar(2), centroid GEOMETRY(Geometry, 4326) NOT NULL ) {{db.tablespace.search_data}}; CREATE UNIQUE INDEX idx_search_name_place_id ON search_name USING BTREE (place_id) {{db.tablespace.search_index}}; {% endif %} ================================================ FILE: lib-sql/tables/status.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TABLE IF EXISTS import_status; CREATE TABLE import_status ( lastimportdate TIMESTAMP WITH TIME ZONE NOT NULL, sequence_id INTEGER, indexed BOOLEAN ); DROP TABLE IF EXISTS import_osmosis_log; CREATE TABLE import_osmosis_log ( batchend TIMESTAMP, batchseq INTEGER, batchsize BIGINT, starttime TIMESTAMP, endtime TIMESTAMP, event TEXT ); ================================================ FILE: lib-sql/tables/tiger.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP TABLE IF EXISTS location_property_tiger; CREATE TABLE location_property_tiger ( place_id BIGINT NOT NULL, parent_place_id BIGINT, startnumber INTEGER NOT NULL, endnumber INTEGER NOT NULL, step SMALLINT NOT NULL, partition SMALLINT NOT NULL, linegeo GEOMETRY NOT NULL, postcode TEXT); ================================================ FILE: lib-sql/tables.sql ================================================ -- SPDX-License-Identifier: GPL-2.0-only -- -- This file is part of Nominatim. (https://nominatim.org) -- -- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. DROP SEQUENCE IF EXISTS seq_place; CREATE SEQUENCE seq_place start 1; {% include('tables/status.sql') %} {% include('tables/nominatim_properties.sql') %} {% include('tables/location_area.sql') %} {% include('tables/tiger.sql') %} {% include('tables/interpolation.sql') %} {% include('tables/search_name.sql') %} {% include('tables/addressline.sql') %} {% include('tables/placex.sql') %} {% include('tables/postcodes.sql') %} {% include('tables/entrance.sql') %} {% include('tables/import_reports.sql') %} {% include('tables/importance_tables.sql') %} ================================================ FILE: lib-sql/tiger_import_finish.sql ================================================ -- 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. --index only on parent_place_id CREATE INDEX IF NOT EXISTS idx_location_property_tiger_parent_place_id_imp ON location_property_tiger_import (parent_place_id) INCLUDE (startnumber, endnumber, step) {{db.tablespace.aux_index}}; CREATE UNIQUE INDEX IF NOT EXISTS idx_location_property_tiger_place_id_imp ON location_property_tiger_import (place_id) {{db.tablespace.aux_index}}; GRANT SELECT ON location_property_tiger_import TO "{{config.DATABASE_WEBUSER}}"; DROP TABLE IF EXISTS location_property_tiger; ALTER TABLE location_property_tiger_import RENAME TO location_property_tiger; ALTER INDEX IF EXISTS idx_location_property_tiger_parent_place_id_imp RENAME TO idx_location_property_tiger_housenumber_parent_place_id; ALTER INDEX IF EXISTS idx_location_property_tiger_place_id_imp RENAME TO idx_location_property_tiger_place_id; DROP FUNCTION tiger_line_import (linegeo GEOMETRY, in_startnumber INTEGER, in_endnumber INTEGER, interpolationtype TEXT, token_info JSONB, in_postcode TEXT); ================================================ FILE: lib-sql/tiger_import_start.sql ================================================ -- 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. DROP TABLE IF EXISTS location_property_tiger_import; CREATE TABLE location_property_tiger_import ( linegeo GEOMETRY, place_id BIGINT, partition INTEGER, parent_place_id BIGINT, startnumber INTEGER, endnumber INTEGER, step SMALLINT, postcode TEXT); -- Lookup functions for tiger import when update -- tables are dropped (see gh-issue #2463) CREATE OR REPLACE FUNCTION getNearestNamedRoadPlaceIdSlow(in_centroid GEOMETRY, in_token_info JSONB) RETURNS BIGINT AS $$ DECLARE out_place_id BIGINT; BEGIN SELECT place_id INTO out_place_id FROM search_name WHERE -- finds rows where name_vector shares elements with search tokens. token_matches_street(in_token_info, name_vector) -- limits search area AND centroid && ST_Expand(in_centroid, 0.015) AND address_rank BETWEEN 26 AND 27 ORDER BY ST_Distance(centroid, in_centroid) ASC LIMIT 1; RETURN out_place_id; END $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION getNearestParallelRoadFeatureSlow(line GEOMETRY) RETURNS BIGINT AS $$ DECLARE r RECORD; search_diameter FLOAT; p1 GEOMETRY; p2 GEOMETRY; p3 GEOMETRY; BEGIN IF ST_GeometryType(line) not in ('ST_LineString') THEN RETURN NULL; END IF; p1 := ST_LineInterpolatePoint(line,0); p2 := ST_LineInterpolatePoint(line,0.5); p3 := ST_LineInterpolatePoint(line,1); search_diameter := 0.0005; WHILE search_diameter < 0.01 LOOP FOR r IN SELECT place_id FROM placex WHERE ST_DWithin(line, geometry, search_diameter) AND rank_address BETWEEN 26 AND 27 ORDER BY (ST_distance(geometry, p1)+ ST_distance(geometry, p2)+ ST_distance(geometry, p3)) ASC limit 1 LOOP RETURN r.place_id; END LOOP; search_diameter := search_diameter * 2; END LOOP; RETURN NULL; END $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION getNearestRoadPlaceIdSlow(point GEOMETRY) RETURNS BIGINT AS $$ DECLARE r RECORD; search_diameter FLOAT; BEGIN search_diameter := 0.00005; WHILE search_diameter < 0.1 LOOP FOR r IN SELECT place_id FROM placex WHERE ST_DWithin(geometry, point, search_diameter) AND rank_address BETWEEN 26 AND 27 ORDER BY ST_Distance(geometry, point) ASC limit 1 LOOP RETURN r.place_id; END LOOP; search_diameter := search_diameter * 2; END LOOP; RETURN NULL; END $$ LANGUAGE plpgsql; -- Tiger import function CREATE OR REPLACE FUNCTION tiger_line_import(linegeo GEOMETRY, in_startnumber INTEGER, in_endnumber INTEGER, interpolationtype TEXT, token_info JSONB, in_postcode TEXT) RETURNS INTEGER AS $$ DECLARE startnumber INTEGER; endnumber INTEGER; stepsize INTEGER; numberrange INTEGER; place_centroid GEOMETRY; out_partition INTEGER; out_parent_place_id BIGINT; location RECORD; BEGIN IF in_endnumber > in_startnumber THEN startnumber := in_startnumber; endnumber := in_endnumber; ELSE startnumber := in_endnumber; endnumber := in_startnumber; linegeo := ST_Reverse(linegeo); END IF; IF startnumber < 0 THEN RAISE WARNING 'Negative house number range (% to %)', startnumber, endnumber; RETURN 0; END IF; numberrange := endnumber - startnumber; IF (interpolationtype = 'odd' AND startnumber % 2 = 0) OR (interpolationtype = 'even' AND startnumber % 2 = 1) THEN startnumber := startnumber + 1; stepsize := 2; ELSE IF (interpolationtype = 'odd' OR interpolationtype = 'even') THEN stepsize := 2; ELSE -- everything else assumed to be 'all' stepsize := 1; END IF; END IF; -- Filter out really broken tiger data IF numberrange > 0 and numberrange::float/stepsize::float > 500 and ST_length(linegeo)/(numberrange::float/stepsize::float) < 0.000001 THEN RAISE WARNING 'Road too short for number range % to % (%)',startnumber,endnumber, ST_length(linegeo)/(numberrange::float/stepsize::float); RETURN 0; END IF; place_centroid := ST_Centroid(linegeo); out_partition := get_partition('us'); -- HYBRID LOOKUP LOGIC (see gh-issue #2463) -- if partition tables exist, use them for fast spatial lookups {% if 'location_road_0' in db.tables %} out_parent_place_id := getNearestNamedRoadPlaceId(out_partition, place_centroid, token_info); IF out_parent_place_id IS NULL THEN SELECT getNearestParallelRoadFeature(out_partition, linegeo) INTO out_parent_place_id; END IF; IF out_parent_place_id IS NULL THEN SELECT getNearestRoadPlaceId(out_partition, place_centroid) INTO out_parent_place_id; END IF; -- When updatable information has been dropped: -- Partition tables no longer exist, but search_name still persists. {% elif 'search_name' in db.tables %} -- Fallback: Look up in 'search_name' table -- though spatial lookups here can be slower. out_parent_place_id := getNearestNamedRoadPlaceIdSlow(place_centroid, token_info); IF out_parent_place_id IS NULL THEN out_parent_place_id := getNearestParallelRoadFeatureSlow(linegeo); END IF; IF out_parent_place_id IS NULL THEN out_parent_place_id := getNearestRoadPlaceIdSlow(place_centroid); END IF; {% endif %} -- If parent was found, insert street(line) into import table IF out_parent_place_id IS NOT NULL THEN INSERT INTO location_property_tiger_import (linegeo, place_id, partition, parent_place_id, startnumber, endnumber, step, postcode) VALUES (linegeo, nextval('seq_place'), out_partition, out_parent_place_id, startnumber, endnumber, stepsize, in_postcode); RETURN 1; END IF; RETURN 0; END; $$ LANGUAGE plpgsql; ================================================ FILE: lib-sql/tokenizer/icu_tokenizer.sql ================================================ -- 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. -- Get tokens used for searching the given place. -- -- These are the tokens that will be saved in the search_name table. CREATE OR REPLACE FUNCTION token_get_name_search_tokens(info JSONB) RETURNS INTEGER[] AS $$ SELECT (info->>'names')::INTEGER[] $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; -- Get tokens for matching the place name against others. -- -- This should usually be restricted to full name tokens. CREATE OR REPLACE FUNCTION token_get_name_match_tokens(info JSONB) RETURNS INTEGER[] AS $$ SELECT (info->>'names')::INTEGER[] $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; -- Return the housenumber tokens applicable for the place. CREATE OR REPLACE FUNCTION token_get_housenumber_search_tokens(info JSONB) RETURNS INTEGER[] AS $$ SELECT (info->>'hnr_tokens')::INTEGER[] $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; -- Return the housenumber in the form that it can be matched during search. CREATE OR REPLACE FUNCTION token_normalized_housenumber(info JSONB) RETURNS TEXT AS $$ SELECT info->>'hnr'; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_is_street_address(info JSONB) RETURNS BOOLEAN AS $$ SELECT info->>'street' is not null or info->>'place' is null; $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_has_addr_street(info JSONB) RETURNS BOOLEAN AS $$ SELECT info->>'street' is not null and info->>'street' != '{}'; $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_has_addr_place(info JSONB) RETURNS BOOLEAN AS $$ SELECT info->>'place' is not null; $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_matches_street(info JSONB, street_tokens INTEGER[]) RETURNS BOOLEAN AS $$ SELECT (info->>'street')::INTEGER[] && street_tokens $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_matches_place(info JSONB, place_tokens INTEGER[]) RETURNS BOOLEAN AS $$ SELECT (info->>'place')::INTEGER[] <@ place_tokens $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_addr_place_search_tokens(info JSONB) RETURNS INTEGER[] AS $$ SELECT (info->>'place')::INTEGER[] $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_get_address_keys(info JSONB) RETURNS SETOF TEXT AS $$ SELECT * FROM jsonb_object_keys(info->'addr'); $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_get_address_search_tokens(info JSONB, key TEXT) RETURNS INTEGER[] AS $$ SELECT (info->'addr'->>key)::INTEGER[]; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_matches_address(info JSONB, key TEXT, tokens INTEGER[]) RETURNS BOOLEAN AS $$ SELECT (info->'addr'->>key)::INTEGER[] <@ tokens; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; CREATE OR REPLACE FUNCTION token_get_postcode(info JSONB) RETURNS TEXT AS $$ SELECT info->>'postcode'; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; -- Return token info that should be saved permanently in the database. CREATE OR REPLACE FUNCTION token_strip_info(info JSONB) RETURNS JSONB AS $$ SELECT NULL::JSONB; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; --------------- private functions ---------------------------------------------- CREATE OR REPLACE FUNCTION getorcreate_full_word(norm_term TEXT, lookup_terms TEXT[], OUT full_token INT, OUT partial_tokens INT[]) AS $$ DECLARE partial_terms TEXT[] = '{}'::TEXT[]; term TEXT; term_id INTEGER; BEGIN SELECT min(word_id) INTO full_token FROM word WHERE word = norm_term and type = 'W'; IF full_token IS NULL THEN full_token := nextval('seq_word'); INSERT INTO word (word_id, word_token, type, word) SELECT full_token, lookup_term, 'W', norm_term FROM unnest(lookup_terms) as lookup_term; END IF; FOR term IN SELECT unnest(string_to_array(unnest(lookup_terms), ' ')) LOOP term := trim(term); IF NOT (ARRAY[term] <@ partial_terms) THEN partial_terms := partial_terms || term; END IF; END LOOP; partial_tokens := '{}'::INT[]; FOR term IN SELECT unnest(partial_terms) LOOP SELECT min(word_id) INTO term_id FROM word WHERE word_token = term and type = 'w'; IF term_id IS NULL THEN term_id := nextval('seq_word'); INSERT INTO word (word_id, word_token, type) VALUES (term_id, term, 'w'); END IF; partial_tokens := array_merge(partial_tokens, ARRAY[term_id]); END LOOP; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION getorcreate_full_word(norm_term TEXT, lookup_terms TEXT[], lookup_norm_terms TEXT[], OUT full_token INT, OUT partial_tokens INT[]) AS $$ DECLARE partial_terms TEXT[] = '{}'::TEXT[]; term TEXT; term_id INTEGER; BEGIN SELECT min(word_id) INTO full_token FROM word WHERE word = norm_term and type = 'W'; IF full_token IS NULL THEN full_token := nextval('seq_word'); IF lookup_norm_terms IS NULL THEN INSERT INTO word (word_id, word_token, type, word) SELECT full_token, lookup_term, 'W', norm_term FROM unnest(lookup_terms) as lookup_term; ELSE INSERT INTO word (word_id, word_token, type, word, info) SELECT full_token, t.lookup, 'W', norm_term, CASE WHEN norm_term = t.norm THEN null ELSE json_build_object('lookup', t.norm) END FROM unnest(lookup_terms, lookup_norm_terms) as t(lookup, norm); END IF; END IF; FOR term IN SELECT unnest(string_to_array(unnest(lookup_terms), ' ')) LOOP term := trim(term); IF NOT (ARRAY[term] <@ partial_terms) THEN partial_terms := partial_terms || term; END IF; END LOOP; partial_tokens := '{}'::INT[]; FOR term IN SELECT unnest(partial_terms) LOOP SELECT min(word_id) INTO term_id FROM word WHERE word_token = term and type = 'w'; IF term_id IS NULL THEN term_id := nextval('seq_word'); INSERT INTO word (word_id, word_token, type) VALUES (term_id, term, 'w'); END IF; partial_tokens := array_merge(partial_tokens, ARRAY[term_id]); END LOOP; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION getorcreate_partial_word(partial TEXT) RETURNS INTEGER AS $$ DECLARE token INTEGER; BEGIN SELECT min(word_id) INTO token FROM word WHERE word_token = partial and type = 'w'; IF token IS NULL THEN token := nextval('seq_word'); INSERT INTO word (word_id, word_token, type, info) VALUES (token, partial, 'w', json_build_object('count', 0)); END IF; RETURN token; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION getorcreate_hnr_id(lookup_term TEXT) RETURNS INTEGER AS $$ DECLARE return_id INTEGER; BEGIN SELECT min(word_id) INTO return_id FROM word WHERE word_token = lookup_term and type = 'H'; IF return_id IS NULL THEN return_id := nextval('seq_word'); INSERT INTO word (word_id, word_token, type) VALUES (return_id, lookup_term, 'H'); END IF; RETURN return_id; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION create_analyzed_hnr_id(norm_term TEXT, lookup_terms TEXT[]) RETURNS INTEGER AS $$ DECLARE return_id INTEGER; BEGIN SELECT min(word_id) INTO return_id FROM word WHERE word = norm_term and type = 'H'; IF return_id IS NULL THEN return_id := nextval('seq_word'); INSERT INTO word (word_id, word_token, type, word, info) SELECT return_id, lookup_term, 'H', norm_term, json_build_object('lookup', lookup_terms[1]) FROM unnest(lookup_terms) as lookup_term; END IF; RETURN return_id; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION create_postcode_word(postcode TEXT, lookup_terms TEXT[]) RETURNS BOOLEAN AS $$ DECLARE existing INTEGER; BEGIN SELECT count(*) INTO existing FROM word WHERE word = postcode and type = 'P'; IF existing > 0 THEN RETURN TRUE; END IF; -- postcodes don't need word ids INSERT INTO word (word_token, type, word) SELECT lookup_term, 'P', postcode FROM unnest(lookup_terms) as lookup_term; RETURN FALSE; END; $$ LANGUAGE plpgsql; ================================================ FILE: man/create-manpage.py ================================================ import sys import os from pathlib import Path sys.path.append(str(Path(__file__, '..', '..', 'src').resolve())) from nominatim_db.cli import get_set_parser def get_parser(): parser = get_set_parser() return parser.parser ================================================ FILE: man/nominatim.1 ================================================ .TH nominatim "1" Manual .SH NAME nominatim .SH SYNOPSIS .B nominatim [-h] [--version] {import,freeze,replication,special-phrases,add-data,index,refresh,admin,export,convert,serve,search,reverse,lookup,details,status} ... .SH DESCRIPTION Command\-line tools for importing, updating, administrating and .br querying the Nominatim database. .br .SH OPTIONS .TP \fB\-\-version\fR Print Nominatim version and exit .SS \fBSub-commands\fR .TP \fBnominatim\fR \fI\,import\/\fR Create a new Nominatim database from an OSM file. .TP \fBnominatim\fR \fI\,freeze\/\fR Make database read-only. .TP \fBnominatim\fR \fI\,replication\/\fR Update the database using an online replication service. .TP \fBnominatim\fR \fI\,special-phrases\/\fR Import special phrases. .TP \fBnominatim\fR \fI\,add-data\/\fR Add additional data from a file or an online source. .TP \fBnominatim\fR \fI\,index\/\fR Reindex all new and modified data. .TP \fBnominatim\fR \fI\,refresh\/\fR Recompute auxiliary data used by the indexing process. .TP \fBnominatim\fR \fI\,admin\/\fR Analyse and maintain the database. .TP \fBnominatim\fR \fI\,export\/\fR Export places as CSV file from the database. .TP \fBnominatim\fR \fI\,convert\/\fR Convert an existing database into a different format. (EXPERIMENTAL) .TP \fBnominatim\fR \fI\,serve\/\fR Start a simple web server for serving the API. .TP \fBnominatim\fR \fI\,search\/\fR Execute a search query. .TP \fBnominatim\fR \fI\,reverse\/\fR Execute API reverse query. .TP \fBnominatim\fR \fI\,lookup\/\fR Execute API lookup query. .TP \fBnominatim\fR \fI\,details\/\fR Execute API details query. .TP \fBnominatim\fR \fI\,status\/\fR .SH OPTIONS 'nominatim import' usage: nominatim import [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--osm-file FILE] [--continue {import-from-file,load-data,indexing,db-postprocess}] [--osm2pgsql-cache SIZE] [--reverse-only] [--no-partitions] [--no-updates] [--offline] [--ignore-errors] [--index-noanalyse] [--prepare-database] Create a new Nominatim database from an OSM file. .br .br This sub\-command sets up a new Nominatim database from scratch starting .br with creating a new database in Postgresql. The user running this command .br needs superuser rights on the database. .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-osm\-file\fR FILE OSM file to be imported (repeat for importing multiple files) .TP \fB\-\-continue\fR {import\-from\-file,load\-data,indexing,db\-postprocess} Continue an import that was interrupted .TP \fB\-\-osm2pgsql\-cache\fR SIZE Size of cache to be used by osm2pgsql (in MB) .TP \fB\-\-reverse\-only\fR Do not create tables and indexes for searching .TP \fB\-\-no\-partitions\fR Do not partition search indices (speeds up import of single country extracts) .TP \fB\-\-no\-updates\fR Do not keep tables that are only needed for updating the database later .TP \fB\-\-offline\fR Do not attempt to load any additional data from the internet .TP \fB\-\-ignore\-errors\fR Continue import even when errors in SQL are present .TP \fB\-\-index\-noanalyse\fR Do not perform analyse operations during index (expert only) .TP \fB\-\-prepare\-database\fR Create the database but do not import any data .SH OPTIONS 'nominatim freeze' usage: nominatim freeze [-h] [-q] [-v] [--project-dir DIR] [-j NUM] Make database read\-only. .br .br About half of data in the Nominatim database is kept only to be able to .br keep the data up\-to\-date with new changes made in OpenStreetMap. This .br command drops all this data and only keeps the part needed for geocoding .br itself. .br .br This command has the same effect as the `\-\-no\-updates` option for imports. .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .SH OPTIONS 'nominatim replication' usage: nominatim replication [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--init] [--no-update-functions] [--check-for-updates] [--once] [--catch-up] [--no-index] [--osm2pgsql-cache SIZE] [--socket-timeout SOCKET_TIMEOUT] Update the database using an online replication service. .br .br An OSM replication service is an online service that provides regular .br updates (OSM diff files) for the planet or update they provide. The OSMF .br provides the primary replication service for the full planet at .br https://planet.osm.org/replication/ but there are other providers of .br extracts of OSM data who provide such a service as well. .br .br This sub\-command allows to set up such a replication service and download .br and import updates at regular intervals. You need to call '\-\-init' once to .br set up the process or whenever you change the replication configuration .br parameters. Without any arguments, the sub\-command will go into a loop and .br continuously apply updates as they become available. Giving `\-\-once` just .br downloads and imports the next batch of updates. .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-init\fR Initialise the update process .TP \fB\-\-no\-update\-functions\fR Do not update the trigger function to support differential updates (EXPERT) .TP \fB\-\-check\-for\-updates\fR Check if new updates are available and exit .TP \fB\-\-once\fR Download and apply updates only once. When not set, updates are continuously applied .TP \fB\-\-catch\-up\fR Download and apply updates until no new data is available on the server .TP \fB\-\-no\-index\fR Do not index the new data. Only usable together with \-\-once .TP \fB\-\-osm2pgsql\-cache\fR SIZE Size of cache to be used by osm2pgsql (in MB) .TP \fB\-\-socket\-timeout\fR \fI\,SOCKET_TIMEOUT\/\fR Set timeout for file downloads .SH OPTIONS 'nominatim special-phrases' usage: nominatim special-phrases [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--import-from-wiki] [--import-from-csv FILE] [--no-replace] Import special phrases. .br .br Special phrases are search terms that narrow down the type of object .br that should be searched. For example, you might want to search for .br 'Hotels in Barcelona'. The OSM wiki has a selection of special phrases .br in many languages, which can be imported with this command. .br .br You can also provide your own phrases in a CSV file. The file needs to have .br the following five columns: .br * phrase \- the term expected for searching .br * class \- the OSM tag key of the object type .br * type \- the OSM tag value of the object type .br * operator \- the kind of search to be done (one of: in, near, name, \-) .br * plural \- whether the term is a plural or not (Y/N) .br .br An example file can be found in the Nominatim sources at .br 'test/testdb/full_en_phrases_test.csv'. .br .br The import can be further configured to ignore specific key/value pairs. .br This is particularly useful when importing phrases from the wiki. The .br default configuration excludes some very common tags like building=yes. .br The configuration can be customized by putting a file `phrase\-settings.json` .br with custom rules into the project directory or by using the `\-\-config` .br option to point to another configuration file. .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-import\-from\-wiki\fR Import special phrases from the OSM wiki to the database .TP \fB\-\-import\-from\-csv\fR FILE Import special phrases from a CSV file .TP \fB\-\-no\-replace\fR Keep the old phrases and only add the new ones .SH OPTIONS 'nominatim add-data' usage: nominatim add-data [-h] [-q] [-v] [--project-dir DIR] [-j NUM] (--file FILE | --diff FILE | --node ID | --way ID | --relation ID | --tiger-data DIR) [--use-main-api] [--osm2pgsql-cache SIZE] [--socket-timeout SOCKET_TIMEOUT] Add additional data from a file or an online source. .br .br This command allows to add or update the search data in the database. .br The data can come either from an OSM file or single OSM objects can .br directly be downloaded from the OSM API. This function only loads the .br data into the database. Afterwards it still needs to be integrated .br in the search index. Use the `nominatim index` command for that. .br .br The command can also be used to add external non\-OSM data to the .br database. At the moment the only supported format is TIGER housenumber .br data. See the online documentation at .br https://nominatim.org/release\-docs/latest/customize/Tiger/ .br for more information. .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-file\fR FILE Import data from an OSM file or diff file .TP \fB\-\-diff\fR FILE Import data from an OSM diff file (deprecated: use \-\-file) .TP \fB\-\-node\fR ID Import a single node from the API .TP \fB\-\-way\fR ID Import a single way from the API .TP \fB\-\-relation\fR ID Import a single relation from the API .TP \fB\-\-tiger\-data\fR DIR Add housenumbers from the US TIGER census database .TP \fB\-\-use\-main\-api\fR Use OSM API instead of Overpass to download objects .TP \fB\-\-osm2pgsql\-cache\fR SIZE Size of cache to be used by osm2pgsql (in MB) .TP \fB\-\-socket\-timeout\fR \fI\,SOCKET_TIMEOUT\/\fR Set timeout for file downloads .SH OPTIONS 'nominatim index' usage: nominatim index [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--boundaries-only] [--no-boundaries] [--minrank RANK] [--maxrank RANK] Reindex all new and modified data. .br .br Indexing is the process of computing the address and search terms for .br the places in the database. Every time data is added or changed, indexing .br needs to be run. Imports and replication updates automatically take care .br of indexing. For other cases, this function allows to run indexing manually. .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-boundaries\-only\fR Index only administrative boundaries. .TP \fB\-\-no\-boundaries\fR Index everything except administrative boundaries. .TP \fB\-\-minrank\fR RANK, \fB\-r\fR RANK Minimum/starting rank .TP \fB\-\-maxrank\fR RANK, \fB\-R\fR RANK Maximum/finishing rank .SH OPTIONS 'nominatim refresh' usage: nominatim refresh [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--postcodes] [--word-tokens] [--word-counts] [--address-levels] [--functions] [--wiki-data] [--secondary-importance] [--importance] [--website] [--data-object OBJECT] [--data-area OBJECT] [--no-diff-updates] [--enable-debug-statements] Recompute auxiliary data used by the indexing process. .br .br This sub\-commands updates various static data and functions in the database. .br It usually needs to be run after changing various aspects of the .br configuration. The configuration documentation will mention the exact .br command to use in such case. .br .br Warning: the 'update' command must not be run in parallel with other update .br commands like 'replication' or 'add\-data'. .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-postcodes\fR Update postcode centroid table .TP \fB\-\-word\-tokens\fR Clean up search terms .TP \fB\-\-word\-counts\fR Compute frequency of full\-word search terms .TP \fB\-\-address\-levels\fR Reimport address level configuration .TP \fB\-\-functions\fR Update the PL/pgSQL functions in the database .TP \fB\-\-wiki\-data\fR Update Wikipedia/data importance numbers .TP \fB\-\-secondary\-importance\fR Update secondary importance raster data .TP \fB\-\-importance\fR Recompute place importances (expensive!) .TP \fB\-\-website\fR Refresh the directory that serves the scripts for the web API .TP \fB\-\-data\-object\fR OBJECT Mark the given OSM object as requiring an update (format: [NWR]) .TP \fB\-\-data\-area\fR OBJECT Mark the area around the given OSM object as requiring an update (format: [NWR]) .TP \fB\-\-no\-diff\-updates\fR Do not enable code for propagating updates .TP \fB\-\-enable\-debug\-statements\fR Enable debug warning statements in functions .SH OPTIONS 'nominatim admin' usage: nominatim admin [-h] [-q] [-v] [--project-dir DIR] [-j NUM] (--warm | --check-database | --migrate | --analyse-indexing | --collect-os-info | --clean-deleted AGE) [--search-only] [--reverse-only] [--osm-id OSM_ID | --place-id PLACE_ID] Analyse and maintain the database. .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-warm\fR Warm database caches for search and reverse queries .TP \fB\-\-check\-database\fR Check that the database is complete and operational .TP \fB\-\-migrate\fR Migrate the database to a new software version .TP \fB\-\-analyse\-indexing\fR Print performance analysis of the indexing process .TP \fB\-\-collect\-os\-info\fR Generate a report about the host system information .TP \fB\-\-clean\-deleted\fR AGE Clean up deleted relations .TP \fB\-\-search\-only\fR Only pre\-warm tables for search queries .TP \fB\-\-reverse\-only\fR Only pre\-warm tables for reverse queries .TP \fB\-\-osm\-id\fR \fI\,OSM_ID\/\fR Analyse indexing of the given OSM object .TP \fB\-\-place\-id\fR \fI\,PLACE_ID\/\fR Analyse indexing of the given Nominatim object .SH OPTIONS 'nominatim export' usage: nominatim export [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--output-type {country,state,county,city,suburb,street,path}] [--output-format OUTPUT_FORMAT] [--language LANGUAGE] [--restrict-to-country COUNTRY_CODE] [--restrict-to-osm-node ID] [--restrict-to-osm-way ID] [--restrict-to-osm-relation ID] Export places as CSV file from the database. .br .br .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-output\-type\fR {country,state,county,city,suburb,street,path} Type of places to output (default: street) .TP \fB\-\-output\-format\fR \fI\,OUTPUT_FORMAT\/\fR Semicolon\-separated list of address types (see \-\-output\-type). Additionally accepts:placeid,postcode .TP \fB\-\-language\fR \fI\,LANGUAGE\/\fR Preferred language for output (use local name, if omitted) .TP \fB\-\-restrict\-to\-country\fR COUNTRY_CODE Export only objects within country .TP \fB\-\-restrict\-to\-osm\-node\fR ID Export only children of this OSM node .TP \fB\-\-restrict\-to\-osm\-way\fR ID Export only children of this OSM way .TP \fB\-\-restrict\-to\-osm\-relation\fR ID Export only children of this OSM relation .SH OPTIONS 'nominatim convert' usage: nominatim convert [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--format {sqlite}] --output OUTPUT [--with-reverse] [--with-search] [--with-details] Convert an existing database into a different format. (EXPERIMENTAL) .br .br Dump a read\-only version of the database in a different format. .br At the moment only a SQLite database suitable for reverse lookup .br can be created. .br .TP \fB\-\-format\fR {sqlite} Format of the output database (must be sqlite currently) .TP \fB\-\-output\fR \fI\,OUTPUT\/\fR, \fB\-o\fR \fI\,OUTPUT\/\fR File to write the database to. .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-with\-reverse\fR, \fB\-\-without\-reverse\fR Enable/disable support for reverse and lookup API (default: enabled) .TP \fB\-\-with\-search\fR, \fB\-\-without\-search\fR Enable/disable support for search API (default: disabled) .TP \fB\-\-with\-details\fR, \fB\-\-without\-details\fR Enable/disable support for details API (default: enabled) .SH OPTIONS 'nominatim serve' usage: nominatim serve [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--server SERVER] [--engine {php,falcon,starlette}] Start a simple web server for serving the API. .br .br This command starts a built\-in webserver to serve the website .br from the current project directory. This webserver is only suitable .br for testing and development. Do not use it in production setups! .br .br There are different webservers available. The default 'php' engine .br runs the classic PHP frontend. The other engines are Python servers .br which run the new Python frontend code. This is highly experimental .br at the moment and may not include the full API. .br .br By the default, the webserver can be accessed at: http://127.0.0.1:8088 .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-server\fR \fI\,SERVER\/\fR The address the server will listen to. .TP \fB\-\-engine\fR {php,falcon,starlette} Webserver framework to run. (default: falcon) .SH OPTIONS 'nominatim search' usage: nominatim search [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--query QUERY] [--amenity AMENITY] [--street STREET] [--city CITY] [--county COUNTY] [--state STATE] [--country COUNTRY] [--postalcode POSTALCODE] [--format {xml,geojson,geocodejson,json,jsonv2,debug}] [--addressdetails] [--extratags] [--namedetails] [--lang LANGS] [--polygon-output {geojson,kml,svg,text}] [--polygon-threshold TOLERANCE] [--countrycodes CC,..] [--exclude_place_ids ID,..] [--limit LIMIT] [--viewbox X1,Y1,X2,Y2] [--bounded] [--no-dedupe] Execute a search query. .br .br This command works exactly the same as if calling the /search endpoint on .br the web API. See the online documentation for more details on the .br various parameters: .br https://nominatim.org/release\-docs/latest/api/Search/ .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-query\fR \fI\,QUERY\/\fR Free\-form query string .TP \fB\-\-amenity\fR \fI\,AMENITY\/\fR Structured query: name and/or type of POI .TP \fB\-\-street\fR \fI\,STREET\/\fR Structured query: housenumber and street .TP \fB\-\-city\fR \fI\,CITY\/\fR Structured query: city, town or village .TP \fB\-\-county\fR \fI\,COUNTY\/\fR Structured query: county .TP \fB\-\-state\fR \fI\,STATE\/\fR Structured query: state .TP \fB\-\-country\fR \fI\,COUNTRY\/\fR Structured query: country .TP \fB\-\-postalcode\fR \fI\,POSTALCODE\/\fR Structured query: postcode .TP \fB\-\-format\fR {xml,geojson,geocodejson,json,jsonv2,debug} Format of result .TP \fB\-\-addressdetails\fR Include a breakdown of the address into elements .TP \fB\-\-extratags\fR Include additional information if available (e.g. wikipedia link, opening hours) .TP \fB\-\-namedetails\fR Include a list of alternative names .TP \fB\-\-lang\fR LANGS, \fB\-\-accept\-language\fR LANGS Preferred language order for presenting search results .TP \fB\-\-polygon\-output\fR {geojson,kml,svg,text} Output geometry of results as a GeoJSON, KML, SVG or WKT .TP \fB\-\-polygon\-threshold\fR TOLERANCE Simplify output geometry.Parameter is difference tolerance in degrees. .TP \fB\-\-countrycodes\fR CC,.. Limit search results to one or more countries .TP \fB\-\-exclude_place_ids\fR ID,.. List of search object to be excluded. Each object can be a Nominatim \fBplace_id\fR or an OSM object in the form \fB\fR .TP \fB\-\-limit\fR \fI\,LIMIT\/\fR Limit the number of returned results .TP \fB\-\-viewbox\fR X1,Y1,X2,Y2 Preferred area to find search results .TP \fB\-\-bounded\fR Strictly restrict results to viewbox area .TP \fB\-\-no\-dedupe\fR Do not remove duplicates from the result list .SH OPTIONS 'nominatim reverse' usage: nominatim reverse [-h] [-q] [-v] [--project-dir DIR] [-j NUM] --lat LAT --lon LON [--zoom ZOOM] [--layer LAYER] [--format {xml,geojson,geocodejson,json,jsonv2,debug}] [--addressdetails] [--extratags] [--namedetails] [--lang LANGS] [--polygon-output {geojson,kml,svg,text}] [--polygon-threshold TOLERANCE] Execute API reverse query. .br .br This command works exactly the same as if calling the /reverse endpoint on .br the web API. See the online documentation for more details on the .br various parameters: .br https://nominatim.org/release\-docs/latest/api/Reverse/ .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-lat\fR \fI\,LAT\/\fR Latitude of coordinate to look up (in WGS84) .TP \fB\-\-lon\fR \fI\,LON\/\fR Longitude of coordinate to look up (in WGS84) .TP \fB\-\-zoom\fR \fI\,ZOOM\/\fR Level of detail required for the address .TP \fB\-\-layer\fR LAYER OSM id to lookup in format (may be repeated) .TP \fB\-\-format\fR {xml,geojson,geocodejson,json,jsonv2,debug} Format of result .TP \fB\-\-addressdetails\fR Include a breakdown of the address into elements .TP \fB\-\-extratags\fR Include additional information if available (e.g. wikipedia link, opening hours) .TP \fB\-\-namedetails\fR Include a list of alternative names .TP \fB\-\-lang\fR LANGS, \fB\-\-accept\-language\fR LANGS Preferred language order for presenting search results .TP \fB\-\-polygon\-output\fR {geojson,kml,svg,text} Output geometry of results as a GeoJSON, KML, SVG or WKT .TP \fB\-\-polygon\-threshold\fR TOLERANCE Simplify output geometry.Parameter is difference tolerance in degrees. .SH OPTIONS 'nominatim lookup' usage: nominatim lookup [-h] [-q] [-v] [--project-dir DIR] [-j NUM] --id OSMID [--format {xml,geojson,geocodejson,json,jsonv2,debug}] [--addressdetails] [--extratags] [--namedetails] [--lang LANGS] [--polygon-output {geojson,kml,svg,text}] [--polygon-threshold TOLERANCE] Execute API lookup query. .br .br This command works exactly the same as if calling the /lookup endpoint on .br the web API. See the online documentation for more details on the .br various parameters: .br https://nominatim.org/release\-docs/latest/api/Lookup/ .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-id\fR OSMID OSM id to lookup in format (may be repeated) .TP \fB\-\-format\fR {xml,geojson,geocodejson,json,jsonv2,debug} Format of result .TP \fB\-\-addressdetails\fR Include a breakdown of the address into elements .TP \fB\-\-extratags\fR Include additional information if available (e.g. wikipedia link, opening hours) .TP \fB\-\-namedetails\fR Include a list of alternative names .TP \fB\-\-lang\fR LANGS, \fB\-\-accept\-language\fR LANGS Preferred language order for presenting search results .TP \fB\-\-polygon\-output\fR {geojson,kml,svg,text} Output geometry of results as a GeoJSON, KML, SVG or WKT .TP \fB\-\-polygon\-threshold\fR TOLERANCE Simplify output geometry.Parameter is difference tolerance in degrees. .SH OPTIONS 'nominatim details' usage: nominatim details [-h] [-q] [-v] [--project-dir DIR] [-j NUM] (--node NODE | --way WAY | --relation RELATION | --place_id PLACE_ID) [--class OBJECT_CLASS] [--addressdetails] [--keywords] [--linkedplaces] [--hierarchy] [--group_hierarchy] [--polygon_geojson] [--lang LANGS] Execute API details query. .br .br This command works exactly the same as if calling the /details endpoint on .br the web API. See the online documentation for more details on the .br various parameters: .br https://nominatim.org/release\-docs/latest/api/Details/ .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-node\fR \fI\,NODE\/\fR, \fB\-n\fR \fI\,NODE\/\fR Look up the OSM node with the given ID. .TP \fB\-\-way\fR \fI\,WAY\/\fR, \fB\-w\fR \fI\,WAY\/\fR Look up the OSM way with the given ID. .TP \fB\-\-relation\fR \fI\,RELATION\/\fR, \fB\-r\fR \fI\,RELATION\/\fR Look up the OSM relation with the given ID. .TP \fB\-\-place_id\fR \fI\,PLACE_ID\/\fR, \fB\-p\fR \fI\,PLACE_ID\/\fR Database internal identifier of the OSM object to look up .TP \fB\-\-class\fR \fI\,OBJECT_CLASS\/\fR Class type to disambiguated multiple entries of the same object. .TP \fB\-\-addressdetails\fR Include a breakdown of the address into elements .TP \fB\-\-keywords\fR Include a list of name keywords and address keywords .TP \fB\-\-linkedplaces\fR Include a details of places that are linked with this one .TP \fB\-\-hierarchy\fR Include details of places lower in the address hierarchy .TP \fB\-\-group_hierarchy\fR Group the places by type .TP \fB\-\-polygon_geojson\fR Include geometry of result .TP \fB\-\-lang\fR LANGS, \fB\-\-accept\-language\fR LANGS Preferred language order for presenting search results .SH OPTIONS 'nominatim status' usage: nominatim status [-h] [-q] [-v] [--project-dir DIR] [-j NUM] [--format {text,json}] Execute API status query. .br .br This command works exactly the same as if calling the /status endpoint on .br the web API. See the online documentation for more details on the .br various parameters: .br https://nominatim.org/release\-docs/latest/api/Status/ .br .TP \fB\-q\fR, \fB\-\-quiet\fR Print only error messages .TP \fB\-v\fR, \fB\-\-verbose\fR Increase verboseness of output .TP \fB\-\-project\-dir\fR DIR Base directory of the Nominatim installation (default:.) .TP \fB\-j\fR NUM, \fB\-\-threads\fR NUM Number of parallel threads to use .TP \fB\-\-format\fR {text,json} Format of result .SH AUTHORS .B Nominatim was written by the Nominatim developer community . .SH DISTRIBUTION The latest version of Nominatim may be downloaded from .UR https://nominatim.org .UE ================================================ FILE: mkdocs.yml ================================================ site_name: Nominatim Manual theme: font: false name: material features: - navigation.tabs - toc.integrate plugins: - privacy copyright: Copyright © Nominatim developer community docs_dir: docs site_url: https://nominatim.org repo_url: https://github.com/openstreetmap/Nominatim nav: - 'Introduction' : 'index.md' - 'API Reference': - 'Overview': 'api/Overview.md' - 'Search': 'api/Search.md' - 'Reverse': 'api/Reverse.md' - 'Address Lookup': 'api/Lookup.md' - 'Details' : 'api/Details.md' - 'Status' : 'api/Status.md' - 'Place Output Formats': 'api/Output.md' - 'FAQ': 'api/Faq.md' - 'Administration Guide': - 'Basic Installation': 'admin/Installation.md' - 'Import' : 'admin/Import.md' - 'Update' : 'admin/Update.md' - 'Deploy' : 'admin/Deployment-Python.md' - 'Nominatim UI' : 'admin/Setup-Nominatim-UI.md' - 'Advanced Installations' : 'admin/Advanced-Installations.md' - 'Maintenance' : 'admin/Maintenance.md' - 'Migration from older Versions' : 'admin/Migration.md' - 'Troubleshooting' : 'admin/Faq.md' - 'Installation on Ubuntu 22' : 'admin/Install-on-Ubuntu-22.md' - 'Installation on Ubuntu 24' : 'admin/Install-on-Ubuntu-24.md' - 'Customization Guide': - 'Overview': 'customize/Overview.md' - 'Import Styles': 'customize/Import-Styles.md' - 'Configuration Settings': 'customize/Settings.md' - 'API Result Formatting': 'customize/Result-Formatting.md' - 'Per-Country Data': 'customize/Country-Settings.md' - 'Place Ranking' : 'customize/Ranking.md' - 'Importance' : 'customize/Importance.md' - 'Tokenizers' : 'customize/Tokenizers.md' - 'Special Phrases': 'customize/Special-Phrases.md' - 'External data: US housenumbers from TIGER': 'customize/Tiger.md' - 'External data: Postcodes': 'customize/Postcodes.md' - 'Conversion to SQLite': 'customize/SQLite.md' - 'Library Guide': - 'Getting Started': 'library/Getting-Started.md' - 'Nominatim API class': 'library/NominatimAPI.md' - 'Configuration': 'library/Configuration.md' - 'Input Parameter Types': 'library/Input-Parameter-Types.md' - 'Result Handling': 'library/Result-Handling.md' - 'Low-level DB Access': 'library/Low-Level-DB-Access.md' - 'Developers Guide': - 'Architecture Overview' : 'develop/overview.md' - 'Database Layout' : 'develop/Database-Layout.md' - 'Indexing' : 'develop/Indexing.md' - 'Tokenizers' : 'develop/Tokenizers.md' - 'Custom modules for ICU tokenizer': 'develop/ICU-Tokenizer-Modules.md' - 'Setup for Development' : 'develop/Development-Environment.md' - 'Testing' : 'develop/Testing.md' - 'External Data Sources': 'develop/data-sources.md' markdown_extensions: - codehilite - admonition - pymdownx.superfences - pymdownx.blocks.html - pymdownx.tabbed: alternate_style: true - def_list - toc: toc_depth: 4 permalink: 🔗 extra_css: [extra.css, styles.css] exclude_docs: | mk_install_instructions.py site_dir: site-html plugins: - search - mkdocstrings: handlers: python: paths: ["src"] options: show_source: False show_bases: False - gen-files: scripts: - docs/mk_install_instructions.py ================================================ FILE: munin/nominatim_importlag ================================================ #!/bin/sh # # Plugin to monitor the age of the imported data in the rendering db # # Can be configured through libpq environment variables, for example # PGUSER, PGDATABASE, etc. See man page of psql for more information # # To configure munin for a default installation, add these lines to # the file /etc/munin/plugin-conf.d/munin-node or in any file in the # directory /etc/munin/plugin-conf.d/ # # [nominatim_*] # user www-data # env.PGUSER www-data # env.PGPORT 5432 # env.PGDATABASE nominatim # env.age_warning 21600 # env.age_critical 86400 # Parameters: # # config (required) # autoconf (optional - used by munin-config) # . $MUNIN_LIBDIR/plugins/plugin.sh if [ "$1" = "config" ]; then echo 'graph_title Data import lag' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel minutes' echo 'graph_category nominatim' echo 'age.label DB import age' echo 'age.type GAUGE' echo 'age.cdef age,60,/' print_warning age print_critical age exit 0 fi delay=`psql -c "copy (select extract(epoch from timezone('utc', now())-lastimportdate)::int from import_status) to stdout"` echo "age.value $delay" ================================================ FILE: munin/nominatim_query_speed ================================================ #!/usr/bin/perl -w # Plugin to monitor response time of search queries. # # Based on a plugin by Dalibo 2007 # Based on a plugin (postgres_block_read_) from Bjrn Ruberg # # Licenced under GPL v2. # # Usage: # # If required, give username, password and/or Postgresql server # host through environment variables. See man page of psql for # more info. # # Parameters: # # config (required) # # Magic markers #%# family=auto #%# capabilities=autoconf use strict; use DBI; use vars qw ( $debug $configure ); use constant _PGMINI => 70400; my $dbhost = $ENV{'PGHOST'} || ''; my $dbname = $ENV{'PGDATABASE'} || 'nominatim'; my $dbuser = $ENV{'PGUSER'} || 'postgres'; my $dbport = $ENV{'PGPORT'} || '5432'; my $dsn = "DBI:Pg:dbname=$dbname"; $dsn .=";host=$dbhost;port=$dbport" if $dbhost; my $pg_server_version; if (exists $ARGV[0]) { if ($ARGV[0] eq 'autoconf') { # Check for DBD::Pg if (! eval "require DBD::Pg;") { print "no (DBD::Pg not found)"; exit 1; } my $dbh = DBI->connect ($dsn, $dbuser, '', {RaiseError =>1}); if ($dbh) { $pg_server_version = $dbh->{'pg_server_version'}; if ($pg_server_version < (_PGMINI)) { $pg_server_version =~ /(\d)(\d){2,2}(\d){2,2}/; print "PostgreSQL Server version " . (_PGMINI) . " or above is needed. Current is $1.$2.$3 \n"; exit 1; } print "yes\n"; exit 0; } else { print "no Unable to access Database $dbname on host $dbhost as user $dbuser.\nError returned was: ". $DBI::errstr; exit 1; } } elsif ($ARGV[0] eq 'debug') { # Set debug flag $debug = 1; } elsif ($ARGV[0] eq 'config') { # Set config flag $configure = 1; } } print "# $dsn\n" if $debug; my $dbh = DBI->connect ($dsn, $dbuser, '', {RaiseError =>1}); die ("no Unable to access Database $dbname on host $dbhost as user $dbuser.\nError returned was: ". $DBI::errstr."\n") unless($dbh); $pg_server_version = $dbh->{'pg_server_version'}; if ($configure) { print "graph_title Total Nominatim response time\n"; print "graph_vlabel Time to response\n"; print "graph_category Nominatim \n"; print "graph_period minute\n"; print "graph_args --base 1000\n"; print "avg.label Average time to response\n"; print "avg.draw LINE\n"; print "avg.type GAUGE\n"; print "avg.min 0\n"; print "avg.info Moving 5 minute average time to perform search\n"; print "avg.label Average time to response\n"; print "min.label Fastest time to response\n"; print "min.draw LINE\n"; print "min.type GAUGE\n"; print "min.min 0\n"; print "min.info Fastest query in last 5 minutes\n"; print "max.label Slowest time to response\n"; print "max.draw LINE\n"; print "max.type GAUGE\n"; print "max.min 0\n"; print "max.info Slowest query in last 5 minutes\n"; } else { my $sql = "select TO_CHAR(avg(endtime-starttime),'SS.MS'),TO_CHAR(min(endtime-starttime),'SS.MS'),TO_CHAR(max(endtime-starttime),'SS.MS') from new_query_log where starttime > 'now'::timestamp - '5 minutes'::interval"; print "# $sql\n" if $debug; my $sth = $dbh->prepare($sql); $sth->execute(); printf ("# Rows: %d\n", $sth->rows) if $debug; if ($sth->rows > 0) { my ($avg, $min, $max) = $sth->fetchrow_array(); print "avg.value $avg\n"; print "min.value $min\n"; print "max.value $max\n"; } } exit 0; ================================================ FILE: munin/nominatim_requests ================================================ #!/bin/sh # # Plugin to monitor the types of requests made to the API # # Can be configured through libpq environment variables, for example # PGUSER, PGDATABASE, etc. See man page of psql for more information. # # Parameters: # # config (required) # autoconf (optional - used by munin-config) # if [ "$1" = "config" ]; then echo 'graph_title Requests by API call' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel requests per minute' echo 'graph_category nominatim' echo 'z1.label reverse' echo 'z1.draw AREA' echo 'z1.type GAUGE' echo 'z2.label search (successful)' echo 'z2.draw STACK' echo 'z2.type GAUGE' echo 'z3.label search (no result)' echo 'z3.draw STACK' echo 'z3.type GAUGE' echo 'z4.label details' echo 'z4.draw STACK' echo 'z4.type GAUGE' exit 0 fi query="select count(*)/5.0 from new_query_log where starttime > (now() - interval '5 min') and " reverse=`psql -c "copy ($query type='reverse') to stdout"` searchy=`psql -c "copy ($query type='search' and results>0) to stdout"` searchn=`psql -c "copy ($query type='search' and results=0) to stdout"` details=`psql -c "copy ($query type='details') to stdout"` echo "z1.value $reverse" echo "z2.value $searchy" echo "z3.value $searchn" echo "z4.value $details" ================================================ FILE: nominatim-cli.py ================================================ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Helper script for development to run nominatim from the source directory. """ from pathlib import Path import sys sys.path.insert(1, str((Path(__file__) / '..' / 'src').resolve())) from nominatim_db import cli exit(cli.nominatim()) ================================================ FILE: packaging/nominatim-api/COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: packaging/nominatim-api/README.md ================================================ # Nominatim - Frontend Library Nominatim is a tool to search OpenStreetMap data by name and address (geocoding) and to generate synthetic addresses of OSM points (reverse geocoding). This module implements the library for searching a Nominatim database imported with the [`nominatim-db`](https://pypi.org/project/nominatim-db/) package. ## Installation To install the Nominatim API from pypi, run: pip install nominatim-api ## Running a Nominatim server You need Falcon or Starlette to run Nominatim as a service, as well as an ASGI-capable server like uvicorn. To install them from pypi run: pip install falcon uvicorn You need to have a Nominatim database imported with the 'nominatim-db' package. Go to the project directory, then run uvicorn as: uvicorn --factory nominatim_api.server.falcon.server:run_wsgi ## Documentation The full documentation for the Nominatim library can be found at: https://nominatim.org/release-docs/latest/library/Getting-Started/ The v1 API of the server is documented at: https://nominatim.org/release-docs/latest/api/Overview/ ## License The source code is available under a GPLv3 license. ================================================ FILE: packaging/nominatim-api/extra_src/paths.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Path settings for extra data used by Nominatim. """ from pathlib import Path DATA_DIR = None SQLLIB_DIR = None LUALIB_DIR = None CONFIG_DIR = (Path(__file__) / '..' / 'resources' / 'settings').resolve() ================================================ FILE: packaging/nominatim-api/pyproject.toml ================================================ [project] name = "nominatim-api" description = "A tool for building a database of OpenStreetMap for geocoding and for searching the database. Search library." readme = "README.md" requires-python = ">=3.9" license = 'GPL-3.0-or-later' maintainers = [ { name = "Sarah Hoffmann", email = "lonvia@denofr.de" }, { name = "Marc Tobias", email = "mtmail-cpan@gmx.net" } ] keywords = [ "geocoding", "OpenStreetMap", "search" ] classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: GNU General Public License (GPL)", "Operating System :: OS Independent", ] dependencies = [ "async-timeout", "python-dotenv", "pyYAML>=5.1", "SQLAlchemy[asyncio]>=1.4.31", "psycopg", "PyICU" ] dynamic = ["version"] [project.urls] Homepage = "https://nominatim.org" Documentation = "https://nominatim.org/release-docs/latest/" Issues = "https://github.com/osm-search/Nominatim/issues" Repository = "https://github.com/osm-search/Nominatim" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.version] path = "src/nominatim_api/version.py" pattern = "NOMINATIM_API_VERSION = '(?P[^']+)'" [tool.hatch.build.targets.sdist] include = [ "src/nominatim_api", "src/nominatim_db/config.py", "settings", "extra_src/paths.py" ] exclude = [ "src/nominatim_api/config.py" ] [tool.hatch.build.targets.wheel] packages = ["src/nominatim_api"] [tool.hatch.build.targets.wheel.force-include] "src/nominatim_db/config.py" = "nominatim_api/config.py" "extra_src/paths.py" = "nominatim_api/paths.py" "settings" = "nominatim_api/resources/settings" ================================================ FILE: packaging/nominatim-db/COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: packaging/nominatim-db/README.md ================================================ # Nominatim - DB Backend Nominatim is a tool to search OpenStreetMap data by name and address (geocoding) and to generate synthetic addresses of OSM points (reverse geocoding). This module implements the database backend for Nominatim and the command-line tool for importing and maintaining the database. ## Installation ### Prerequisites Nominatim requires [osm2pgsql](https://osm2pgsql.org/) (>=1.8) for reading OSM data and [PostgreSQL](https://www.postgresql.org/) (>=9.6) to store the data. On Ubuntu (>=23.04) and Debian (when using backports), you can install them with: sudo apt-get install osm2pgsql postgresql-postgis ### Installation from pypi To install Nominatim from pypi, run: pip install nominatim-db ## Quick start First create a project directory for your new Nominatim database, which is the space for additional configuration and customization: mkdir nominatim-project Make sure you run all nominatim commands from within the project directory: cd nominatim-project Download an appropriate data extract, for example from [Geofabrik](https://download.geofabrik.de/) and import the file: nominatim import --osm-file You will need to install the [`nominatim-api`](https://pypi.org/project/nominatim-api/) package to query the database. ## Documentation A HTML version of the documentation can be found at https://nominatim.org/release-docs/latest/ . ## License The source code is available under a GPLv3 license. ================================================ FILE: packaging/nominatim-db/extra_src/nominatim_db/paths.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Path settings for extra data used by Nominatim. """ from pathlib import Path DATA_DIR = (Path(__file__) / '..' / 'resources').resolve() SQLLIB_DIR = (DATA_DIR / 'lib-sql') LUALIB_DIR = (DATA_DIR / 'lib-lua') CONFIG_DIR = (DATA_DIR / 'settings') ================================================ FILE: packaging/nominatim-db/pyproject.toml ================================================ [project] name = "nominatim-db" description = "A tool for building a database of OpenStreetMap for geocoding and for searching the database. Database backend." readme = "README.md" requires-python = ">=3.9" license = 'GPL-3.0-or-later' maintainers = [ { name = "Sarah Hoffmann", email = "lonvia@denofr.de" }, { name = "Marc Tobias", email = "mtmail-cpan@gmx.net" } ] keywords = [ "geocoding", "OpenStreetMap", "search" ] classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: GNU General Public License (GPL)", "Operating System :: OS Independent", ] dependencies = [ "psycopg != 3.3.0", "python-dotenv", "jinja2", "pyYAML>=5.1", "psutil", "PyICU", "mwparserfromhell" ] dynamic = ["version"] [project.urls] Homepage = "https://nominatim.org" Documentation = "https://nominatim.org/release-docs/latest/" Issues = "https://github.com/osm-search/Nominatim/issues" Repository = "https://github.com/osm-search/Nominatim" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.version] path = "src/nominatim_db/version.py" pattern = "NOMINATIM_VERSION = parse_version.'(?P[^-]+)" [tool.hatch.build.targets.sdist] include = [ "src/nominatim_db", "scripts", "lib-sql/**/*.sql", "lib-lua/**/*.lua", "settings", "data/words.sql", "extra_src/nominatim_db/paths.py" ] artifacts = [ "data/country_osm_grid.sql.gz" ] exclude = [ "src/nominatim_db/paths.py" ] [tool.hatch.build.targets.wheel] packages = ["src/nominatim_db"] [tool.hatch.build.targets.wheel.shared-scripts] "scripts" = "/" [tool.hatch.build.targets.wheel.force-include] "lib-sql" = "nominatim_db/resources/lib-sql" "lib-lua" = "nominatim_db/resources/lib-lua" "settings" = "nominatim_db/resources/settings" "data/country_osm_grid.sql.gz" = "nominatim_db/resources/country_osm_grid.sql.gz" "data/words.sql" = "nominatim_db/resources/words.sql" "extra_src/nominatim_db/paths.py" = "nominatim_db/paths.py" ================================================ FILE: packaging/nominatim-db/scripts/nominatim ================================================ #!python3 from nominatim_db import cli exit(cli.nominatim()) ================================================ FILE: settings/address-levels.json ================================================ [ { "tags" : { "place" : { "sea" : [2, 0], "island" : [17, 0], "islet" : [20, 0], "continent" : [2, 0], "country" : [4, 0], "state" : [8, 0], "province" : [8, 0], "region" : [18, 0], "county" : 12, "district" : 12, "municipality" : 14, "city" : 16, "town" : [18, 16], "borough" : 18, "village" : [19, 16], "suburb" : [19, 20], "hamlet" : 20, "croft" : 20, "subdivision" : 22, "allotments" : 22, "neighbourhood" : 24, "quarter" : [20, 22], "isolated_dwelling" : [22, 25], "farm" : [22, 25], "city_block" : 25, "mountain_pass" : 25, "square" : 25, "locality" : 25, "houses" : [28, 0], "house" : 30, "" : [22, 0] }, "boundary" : { "administrative2" : 4, "administrative3" : 6, "administrative4" : 8, "administrative5" : 10, "administrative6" : 12, "administrative7" : 14, "administrative8" : 16, "administrative9" : 18, "administrative10" : 20, "administrative11" : 22, "administrative12" : 24, "" : [25, 0] }, "landuse" : { "residential" : 24, "farm" : 24, "farmyard" : 24, "industrial" : 24, "commercial" : 24, "allotments" : 24, "retail" : 24, "" : [24, 0] }, "leisure" : { "park" : [24, 0], "nature_reserve" : [24, 0], "garden": [25, 0], "common": [25, 0] }, "natural" : { "peak" : [18, 0], "volcano" : [18, 0], "mountain_range" : [18, 0], "sea" : [4, 0], "tree" : 30, "spring" : 30, "cave_entrance" : 30, "geyser" : 30, "hot_spring" : 30, "rock" : 30, "stone" : 30, "" : [22, 0] }, "water" : { "lake" : [20, 0], "reservoir" : [20, 0], "wastewater" : [24, 0], "pond" : [24, 0], "fountain" : [24, 0], "" : [22, 0] }, "waterway" : { "river" : [19, 0], "stream" : [22, 0], "ditch" : [22, 0], "drain" : [22, 0], "" : [20, 0] }, "highway" : { "" : 30, "service" : 27, "cycleway" : 27, "path" : 27, "footway" : 27, "steps" : 27, "bridleway" : 27, "motorway_link" : 27, "primary_link" : 27, "trunk_link" : 27, "secondary_link" : 27, "tertiary_link" : 27, "residential" : 26, "track" : 26, "unclassified" : 26, "tertiary" : 26, "secondary" : 26, "primary" : 26, "living_street" : 26, "trunk" : 26, "motorway" : 26, "pedestrian" : 26, "road" : 26, "construction" : 26 }, "mountain_pass" : { "" : [20, 0] }, "historic" : { "neighbourhood" : [22, 0] } } }, { "countries" : [ "au" ], "tags" : { "boundary" : { "administrative6": [12, 0] } } }, { "countries" : [ "ca" ], "tags" : { "place" : { "county" : [12, 0] } } }, { "countries": [ "cz" ], "tags" : { "boundary" : { "administrative5": 12, "administrative6": [13, 0], "administrative7": [14, 0], "administrative8": 14, "administrative9": 15, "administrative10": 16 } } }, { "countries" : [ "de" ], "tags" : { "place" : { "region" : [10, 0], "county" : [12, 0] }, "boundary" : { "administrative5" : [10, 0] } } }, { "countries" : [ "be" ], "tags" : { "boundary" : { "administrative3" : [5, 0], "administrative4" : 6, "administrative5" : [7, 0], "administrative6" : 8, "administrative7" : 12, "administrative8" : 14, "administrative9" : 16, "administrative10" : 18 } } }, { "countries" : [ "br" ], "tags" : { "boundary" : { "administrative5" : [10, 0], "administrative6" : [12, 0], "administrative7" : [14, 0] } } }, { "countries" : ["se", "no"], "tags" : { "place" : { }, "boundary" : { "administrative3" : 8, "administrative4" : 12 } } }, { "countries" : ["id"], "tags" : { "place" : { "municipality" : 18 }, "boundary" : { "administrative5" : 12, "administrative6" : 14, "administrative7" : 16, "administrative8" : 20, "administrative9" : 22, "administrative10" : 24 } } }, { "countries" : ["ru"], "tags" : { "place" : { "municipality" : 18 }, "boundary" : { "administrative5" : [10, 0], "administrative7" : [13, 0], "administrative8" : 14 } } }, { "countries" : [ "nl" ], "tags" : { "boundary" : { "administrative7" : [13, 0], "administrative8" : 14, "administrative9" : [15, 0], "administrative10" : 16 } } }, { "countries" : ["es"], "tags" : { "place" : { "province" : 10, "civil_parish" : 18 }, "boundary" : { "administrative5" : [10, 0], "administrative6" : 10, "administrative7" : 12, "administrative10" : 22 } } }, { "countries" : ["sa"], "tags" : { "place" : { "province" : 12, "municipality" : 18 } } }, { "countries" : ["sk"], "tags" : { "boundary" : { "administrative5" : [10, 0], "administrative6" : 11, "administrative7" : [11, 0], "administrative8" : 12, "administrative9" : 16, "administrative10" : 18, "administrative11" : 20 } } }, { "countries" : ["jp"], "tags" : { "boundary" : { "administrative7" : 16, "administrative8" : 18, "administrative9" : 20, "administrative10" : 22, "administrative11" : 24 } } } ] ================================================ FILE: settings/country-names/ad.yaml ================================================ name: default: Andorra ab: Андора af: Andorra ak: Andora am: አንዶራ an: Andorra ar: أندورا av: Андо́рра az: Andorra ba: Андо́рра be: Андо́ра bg: Андора bh: अंडोरा bi: Andorra bm: Andɔr bn: অ্যান্ডোরা bo: ཨན་ཌོར་ར། br: Andorra bs: Andora ca: Andorra ce: Андо́рра co: Andorra cs: Andorra cu: Андо́ра cv: Андорра cy: Andorra da: Andorra de: Andorra dv: އެންޑޯރާ dz: ཨཱན་དོ་ར ee: Andorra el: Ανδόρρα en: Andorra eo: Andoro es: Andorra et: Andorra eu: Andorra fa: آندورا ff: Anndoora fi: Andorra fo: Andorra fr: Andorre fy: Andorra ga: Andóra gd: Andorra gl: Andorra gn: Andorra gu: એન્ડોરા gv: Andorra ha: Andorra he: אַנְדּוֹרָה hi: अण्डोरा hr: Andora ht: Andora hu: Andorra hy: Անդորրա ia: Andorra id: Andorra ie: Andorra ig: Andorra io: Andora is: Andorra it: Andorra ja: アンドラ jv: Andorra ka: ანდორა kg: Andora ki: Andora kk: Андорра kl: Andorra km: អង់ដូរ៉ា kn: ಅಂಡೋರ ko: 안도라 ks: اٮ۪نڑورا ku: Andorra kv: Андорра kw: Andorra ky: Андорра la: Andorra lb: Andorra lg: Andora li: Andorra ln: Andora lo: ອັງດອກ lt: Andora lv: Andora mg: Andora mi: Andorra mk: Андора ml: അൻഡോറ mn: Андорра mr: आंदोरा ms: Andorra mt: Andorra my: အင်ဒိုရာနိုင်ငံ na: Andorra ne: एण्डोरा nl: Andorra "no": Andorra nv: Andówa oc: Andòrra om: Andooraa or: ଆଣ୍ଡୋରା os: Андоррæ pa: ਅੰਡੋਰਾ pi: अंडोरा pl: Andora ps: اندورا pt: Andorra qu: Andurra rm: Andorra rn: Andora ro: Andorra ru: Андорра rw: Andora sa: अण्डोरा sc: Andorra sd: انڊورا se: Andorra sg: Andôro sh: Andora si: ඇන්ඩෝරා sk: Andorra sl: Andora sm: Andora sn: Andorra so: Andorra sq: Andorra sr: Андора ss: iYandola su: Andorra sv: Andorra sw: Andorra ta: அண்டோரா te: అండొర్రా tg: Андорра th: อันดอร์รา ti: አንዶራ tk: Andorra tl: Andorra to: ʻEnitola tr: Andorra ts: Andorra tt: Андорра tw: Andorra ug: ئاندوررا uk: Андо́рра ur: انڈورا uz: Andorra vi: Andorra vo: Landorän wo: Andoor yi: אַנדאָרע yo: Àndórà zh: 安道爾 zu: I-Andora ================================================ FILE: settings/country-names/ae.yaml ================================================ name: default: الإمارات العربية المتحدة af: Verenigde Arabiese Emirate am: የተባበሩት የዓረብ ግዛቶች an: Emiratos Arabes Unitos ar: الإمارات العربية المتحدة av: ГIарабазул Цолъарал Имаратал az: Birləşmiş Ərəb Əmirlikləri be: Аб'яднаныя Арабскія Эміраты bg: Обединени арабски емирства bn: সংযুক্ত আরব আমিরাত bo: ཡི་ནེ་ཁྲེ་ཀྲི་ཨ་རབ་ཨེ་མེ་རེ་ཁྲི། br: Emirelezhioù Arab Unanet bs: Ujedinjeni Arapski Emirati ca: Emirats Àrabs Units ce: Ӏарбийн Цхьаьнакхетта Эмираташ cs: Spojené arabské emiráty cy: Yr Emiradau Arabaidd Unedig da: Forenede Arabiske Emirater de: Vereinigte Arabische Emirate dv: އެކުވެރި ޢަރަބި އިމާރާތު dz: ཡུ་ནའི་ཊེཊ་ཨ་རབ་ཨེ་མི་རེཊསི་ el: Ηνωμένα Αραβικά Εμιράτα en: United Arab Emirates eo: Unuiĝintaj Arabaj Emirlandoj es: Emiratos Árabes Unidos et: Araabia Ühendemiraadid eu: Arabiar Emirerri Batuak fa: امارات متحده عربی fi: Yhdistyneet arabiemiirikunnat fo: Sameindu Emirríkini fr: Émirats arabes unis fy: Feriene Arabyske Emiraten ga: Aontas na nÉimíríochtaí Arabacha gd: Na h-Iomaratan Arabach Aonaichte gl: Emiratos Árabes Unidos gn: Aravia Emiryvy Joaju gv: Ny h-Emmiraidyn Arabagh Unnaneysit he: איחוד האמירויות הערביות hi: संयुक्त अरब अमीरात hr: Ujedinjeni Arapski Emirati ht: Emira Arab Ini hu: Egyesült Arab Emírségek hy: Արաբական Միացյալ Էմիրություններ ia: Emiratos Arabe Unite id: Uni Emirat Arab ie: Unit Arabic Emiratus io: Unionita Araba Emirati is: Sameinuðu arabísku furstadæmin it: Emirati Arabi Uniti ja: アラブ首長国連邦 jv: Uni Emirat Arab ka: არაბთა გაერთიანებული საამიროები ki: United Arab Emirates kk: Біріккен Араб Әмірліктері kl: United Arab Emirates kn: ಸಂಯುಕ್ತ ಅರಬ ಅಮೀರಾತ ko: 아랍에미리트 ku: Mîrnişînên Erebî yên Yekbûyî kv: Ӧтувтчӧм Араб Эмиратъяс kw: Pennternasedh Unys Arabek ky: Бириккен Араб Эмирликтери la: Phylarchiarum Arabicarum Confoederatio lb: Vereenegt Arabesch Emiraten li: Vereinegde Arabische Emiraote lt: Jungtiniai Arabų Emyratai lv: Apvienotie Arābu Emirāti mi: Te Kotahitanga o ngā Whenua Ārapi mk: Обединети Арапски Емирати ml: ഐക്യ അറബ് എമിറേറ്റുകൾ mn: Арабын Нэгдсэн Эмирт Улс mr: संयुक्त अरब अमिराती ms: Emiriah Arab Bersatu mt: Emirati Għarab Magħquda my: အာရပ်စော်ဘွားများပြည်ထောင်စုနိုင်ငံ na: Emireitit Arabiya nb: De forente arabiske emirater ne: संयुक्त अरब इमिरेट्स nl: Verenigde Arabische Emiraten nn: Dei sameinte arabiske emirata "no": De forente arabiske emirater nv: Ásáí Bikéyah Yázhí Ałhidadiidzooígíí oc: Emirats Arabis Units or: ୟୁନାଇଟେଡ଼ ଆରବ ଏମିରଟ os: Араббы Иугонд Эмираттæ pa: ਸੰਯੁਕਤ ਅਰਬ ਇਮਰਾਤ pl: Emiraty Arabskie ps: عربي متحده امارات pt: Emirados Árabes Unidos qu: Hukllachasqa Arab Imiratukuna ro: Emiratele Arabe Unite ru: Объединённые Арабские Эмираты rw: Nyarabu Zunze Ubumwe sd: گڏيل عرب امارات se: Ovttastuvvan arábaemiráhtat sh: Ujedinjeni Arapski Emirati sk: Spojené arabské emiráty sl: Združeni arabski emirati so: Imaaraatka Carabta sq: Emiratet e Bashkuara Arabe sr: Уједињени Арапски Емирати ss: Bunye bema-Arabhu su: Uni Émirat Arab sv: Förenade Arabemiraten sw: Falme za Kiarabu ta: ஐக்கிய அரபு அமீரகம் te: యునైటెడ్ అరబ్ ఎమిరేట్స్ tg: Аморати Муттаҳидаи Араб th: สหรัฐอาหรับเอมิเรตส์ tk: Birleşen Arap Emirlikleri tl: United Arab Emirates tr: Birleşik Arap Emirlikleri tt: Берләшкән Гарәп Әмирлекләре ug: ئەرەب بىرلەشمە خەلىپىلىكى uk: Об'єднані Арабські Емірати ur: متحدہ عرب امارات uz: Birlashgan Arab Amirliklari vi: Các Tiểu vương quốc Ả Rập Thống nhất vo: Lemiräns Larabik Pebalöl wo: Imaaraat yu Araab yu Bennoo yi yi: פאראייניגטע אראבישע עמיראטן yo: Àwọn Ẹ́mírétì Árábù Aṣọ̀kan zh: 阿拉伯联合酋长国 ================================================ FILE: settings/country-names/af.yaml ================================================ name: default: افغانستان af: Afghanistan ak: Afganistan am: አፍጋኒስታን an: Afganistán ar: أفغانستان as: আফগানিস্তান az: Əfqanıstan ba: Афғанстан be: Афганістан bg: Афганистан bh: अफ़ग़ानिस्तान bm: Afiganistaŋ bn: আফগানিস্তান bo: ཨ་ཧྥུའུ་རྒན་སི་ཐན། br: Afghanistan bs: Afganistan ca: Afganistan ce: АфгIанистан ch: Afghanistan co: Afghanistan cs: Afghánistán cv: Афганистан cy: Affganistan da: Afghanistan de: Afghanistan dv: އަފްޣާނިސްތާން dz: ཨཕ་ག་ནིསི་ཏཱན་ ee: Afghanistan nutome el: Αφγανιστάν en: Afghanistan eo: Afganio es: Afganistán et: Afganistan eu: Afganistan fa: افغانستان ff: Afganistaan fi: Afganistan fo: Afghanistan fr: Afghanistan fy: Afganistan ga: An Afganastáin gd: Afganastan gl: Afganistán - افغانستان gn: Ahyganitã gu: અફઘાનિસ્તાન gv: Yn Afghanistaan ha: Afghanistan he: אפגניסטן hi: अफ़्गानिस्तान hr: Afganistan ht: Afganistan hu: Afganisztán hy: Աֆղանստան ia: Afghanistan id: Afganistan ie: Afghanistan io: Afganistan is: Afganistan it: Afghanistan iu: ᐊᑉᒐᓂᔅᑕᓐ ja: アフガニスタン jv: Afganistan ka: ავღანეთი kg: Afghanistan ki: Afghanistan kk: Ауғанстан kl: Afghanistan km: សាធារណរដ្ឋឥស្លាមអាហ្វហ្កានីស្ថាន kn: ಅಫ್ಘಾನಿಸ್ತಾನ ko: 아프가니스탄 ks: اَفغانَستان ku: Afxanistan kv: Афганистан kw: Afghanistan ky: Ооганстан la: Afgania lb: Afghanistan lg: Afaganisitani li: Afghanistan ln: Afganistáni lo: ອັບການິສະຖານ lt: Afganistanas lv: Afganistāna mg: Afganistana mi: Āwhekenetāna mk: Авганистан ml: അഫ്ഗാനിസ്താൻ mn: Афганистан mo: Афганистан mr: अफगाणिस्तान ms: Afghanistan mt: Afganistan my: အာဖဂန်နစ္စတန်နိုင်ငံ na: Apeganitan ne: अफगानिस्तान nl: Afghanistan "no": Afghanistan oc: Afganistan om: Afgaanistaan or: ଆଫଗାନିସ୍ତାନ os: Афгъанистан pa: ਅਫ਼ਗ਼ਾਨਿਸਤਾਨ pi: अफगानस्थान pl: Afganistan ps: افغانستان pt: Afeganistão qu: Afgansuyu rm: Afganistan rn: Afuganisitani ro: Afganistan ru: Афганистан rw: Afuganisitani sa: अफगानस्थान sc: Afganistàn sd: افغانستان se: Afganistána sg: Faganïta, Afganïstäan sh: Afganistan si: ඇෆ්ගනිස්ථානය sk: Afganistan sl: Afganistan sm: Afakanisitana sn: Afghanistan so: Afgaanistan sq: Afganistani sr: Авганистан ss: I-Afugani su: Apganistan sv: Afghanistan sw: Afghanistan ta: ஆப்கானித்தான் te: ఆఫ్ఘనిస్తాన్ tg: Афғонистон th: ประเทศอัฟกานิสถาน ti: አፍጋኒስታን tk: Owganystan tl: Apganistan to: ʻAfukanisitani tr: Afganistan tt: Әфганстан ug: ئافغانىستان uk: Афганістан ur: افغانستان uz: Afgʻoniston vi: Afghanistan vo: Lafganistän wo: Afganistaan yi: אפגאניסטאן yo: Afghanístàn za: Afghanistan zh: 阿富汗 zu: I-Afganistani ================================================ FILE: settings/country-names/ag.yaml ================================================ name: default: Antigua and Barbuda af: Antigua en Barbuda ak: Antigua ne Baabuda am: አንቲጋ እና ባርቡዳ an: Antigua y Barbuda ar: أنتيغوا وباربودا as: এণ্টিগুৱা আৰু বাৰ্বুডা ay: Antiwa wan Barbuda az: Antiqua və Barbuda ba: Антигуа һәм Барбуда be: Антыгуа́ і Барбу́да bg: Антигуа и Барбуда bm: Antiga-ni-Barbuda bn: অ্যান্টিগুয়া ও বার্বুডা bo: ཨེན་ཊི་གུའ་དང་བར་བུ་ཌ། br: Antigua ha Barbuda bs: Antigva i Barbuda ca: Antigua i Barbuda ce: Анти́гуа а Барбу́да cs: Antigua a Barbuda cy: Antigwa a Barbiwda da: Antigua og Barbuda de: Antigua und Barbuda dv: އެންޓިގުއާ އަދި ބާބިއުޑާ dz: ཨན་ཊི་གུ་ཝ་ ཨེནཌ་ བྷར་བྷུ་ཌ ee: Antigua and Barbuda el: Αντίγκουα και Μπαρμπούντα en: Antigua and Barbuda eo: Antigvo-Barbudo es: Antigua y Barbuda et: Antigua ja Barbuda eu: Antigua eta Barbuda fa: آنتیگوا و باربودا ff: Antiguwaa e Barbudaa fi: Antigua ja Barbuda fo: Antigua og Barbuda fr: Antigua-et-Barbuda fy: Antigûa en Barbûda ga: Antigua agus Barbúda gd: Antigua agus Barbuda gl: Antiga e Barbuda gn: Antigua ha Barbuda gu: એન્ટીગુઆ અને બરબુડા gv: Antigua as Barbuda ha: Antigwa da Barbuba he: אנטיגואה וברבודה hi: अण्टीगुआ और बारबूडा hr: Antigva i Barbuda ht: Antigwa ak Babouda hu: Antigua és Barbuda hy: Անտիգուա և Բարբուդա ia: Antigua e Barbuda id: Antigua dan Barbuda ie: Antigua e Barbuda io: Antigua e Barbuda is: Antígva og Barbúda it: Antigua e Barbuda ja: アンティグア・バーブーダ jv: Antigua lan Barbuda ka: ანტიგუა და ბარბუდა ki: Antigua na Barbuda kk: Антигуа және Барбуда kl: Antigua aamma Barbuda km: អង់ទីគ័រ និង​បាបុយដា kn: ಆಂಟಿಗುವ ಮತ್ತು ಬಾರ್ಬುಡ ko: 앤티가 바부다 ks: اٮ۪نٹِگُوا تہٕ باربوڑا ku: Antîgua û Berbûda kw: Antiga ha Barbuda ky: Антигуа жана Барбуда la: Antiqua et Barbuda lb: Antigua a Barbuda lg: Antigwa ne Barabuda li: Antigua en Barbuda ln: Antigua mpé Barbuda lo: ແອນຕີກາວ ແລະ ບາບູດາ lt: Antigva ir Barbuda lv: Antigva un Barbuda mg: Antigua sy Barbuda mk: Антигва и Барбуда ml: ആന്റിഗ്വയ ആന്റ് ബാർബൂഡ mn: Антигуа ба Барбуда mr: अँटिगा आणि बार्बुडा ms: Antigua dan Barbuda mt: Antigwa u Barbuda my: အင်တီဂွါနှင့် ဘာဘူဒါ na: Antigua me Barbuda ne: एन्टिगुआ र बर्बुडा nl: Antigua en Barbuda "no": Antigua og Barbuda nv: Antíígwa dóó Hashkʼaan Bikéyah Yázhí oc: Antigua e Barbuda om: Antiiguwaa fi Baarbuudaa or: ଆଣ୍ଟିଗୁଆ ଓ ବରବୁଡା os: Антигуæ æмæ Барбудæ pa: ਐਂਟੀਗੁਆ ਅਤੇ ਬਰਬੂਡਾ pl: Antigua i Barbuda pt: Antígua e Barbuda qu: Antiwa Barbudawan rm: Antigua e Barbuda ro: Antigua și Barbuda ru: Антигуа и Барбуда rw: Antigwa na Baribuda sa: अण्टीग्वा sc: Antigua e Barbuda se: Antigua ja Barbuda sh: Antigva i Barbuda si: ඇන්ටිගුවා සහ බාබියුඩා sk: Antigua a Barbuda sl: Antigva in Barbuda sn: Antigua and Barbuda so: Antigua iyo Barbuda sq: Antigua dhe Barbuda sr: Антигва и Барбуда ss: iBhabudi ne Anithikhu su: Antigua jeung Barbuda sv: Antigua och Barbuda sw: Antigua na Barbuda ta: அன்டிகுவா பர்புடா te: ఆంటిగ్వా మరియు బార్బుడా tg: Антигуаю Барбуда th: แอนติกาและบาร์บูดา ti: አንቲጓ እና ባሩዳ tl: Antigua at Barbuda to: Anitikua mo Palaputa tr: Antigua ve Barbuda tt: Анти́гуа һәм Барбу́да ug: ئانتىگۇئا ۋە باربۇدا uk: Анти́гуа і Барбу́да ur: اینٹیگوا و باربوڈا uz: Antigua va Barbuda vi: Antigua và Barbuda vo: Lantigeän e Barbudeän wo: Antigua ak Barbuda yo: Ántígúà àti Bàrbúdà za: Antigua caeuq Barbuda zh: 安提瓜和巴布达 zu: i-Antigua and Barbuda ================================================ FILE: settings/country-names/ai.yaml ================================================ name: default: Anguilla af: Anguilla an: Anguila ar: أنجويلا az: Angilya ba: Ангилья be: Ангілья bg: Ангуила br: Anguilla bs: Angvila ca: Anguilla cs: Anguilla cy: Anguilla da: Anguilla de: Anguilla dv: އަންގީލާ el: Ανγκουίλα en: Anguilla eo: Angvilo es: Anguila et: Anguilla eu: Aingira fa: آنگویلا fi: Anguilla fr: Anguilla ga: Angaíle gl: Anguila he: אנגווילה hi: अंगुइला hr: Angvila hu: Anguilla hy: Անգիլիա id: Anguilla io: Anguila is: Angvilla it: Anguilla ja: アンギラ jv: Anguilla ka: ანგილია ko: 앵귈라 kw: Angwilla lb: Anguilla li: Anguilla lt: Angilija lv: Angilja mk: Ангвила mn: Ангилья mr: अँग्विला ms: Anguilla ne: एन्गुला nl: Anguilla "no": Anguilla oc: Anguilla pa: ਐਂਗੁਈਲਾ pl: Anguilla pt: Anguilla ro: Anguilla ru: Ангилья rw: Angwiya sh: Angvila sk: Anguilla sl: Angvila sq: Anguilla sr: Ангвила su: Anguilla sv: Anguilla sw: Anguilla ta: அங்கியுலா th: แองกวิลลา tl: Anggilya tr: Anguilla ug: ئانگۋىللا uk: Ангілья ur: اینگویلا uz: Angilya vi: Anguilla wo: Angila yo: Àngúíllà zh: 安圭拉 ================================================ FILE: settings/country-names/al.yaml ================================================ name: default: Shqipëria ab: Арнауыҭтәыла af: Albanië ak: Albania am: አልባኒያ an: Albania ar: ألبانيا as: আলবেনিয়া av: Алба́ния ay: Alwaña az: Albaniya ba: Алба́ния be: Алба́нія bg: Албания bh: अल्बानिया bi: Albania bm: Alibani bn: আলবেনিয়া bo: ཨལ་པ་ཉི་ཡ། br: Albania bs: Albanija ca: Albània ce: Алба́ни ch: Albania co: Albania cs: Albánie cu: Алванїꙗ cv: Албани cy: Albania da: Albanien de: Albanien dv: އަލްބޭނިއާ dz: ཨཱལ་བེ་ནི་ཡ ee: Albania el: Αλβανία en: Albania eo: Albanio es: Albania et: Albaania eu: Albania fa: آلبانی ff: Albaniya fi: Albania fo: Albania fr: Albanie fy: Albaanje ga: An Albáin gd: Albàinia gl: Albania gn: Avaña gu: આલ્બેનિયા gv: Yn Albaan ha: Albaniya he: אַלְבַּנְיָה hi: अल्बानिया hr: Albanija ht: Albani hu: Albánia hy: Ալբանիա ia: Albania id: Albania ie: Albania ig: Albania io: Albania is: Albanía it: Albania iu: ᐊᓪᐹᓂᐊ ja: アルバニア jv: Albania ka: ალბანეთი kg: Albania ki: Arũmbĩnia kk: Албания kl: Albania km: អាល់បានី kn: ಅಲ್ಬೇನಿಯ ko: 알바니아 ks: اٮ۪لبانِیا ku: Albanya kv: Албания kw: Albani ky: Албания la: Albania lb: Albanien lg: Alibaniya li: Albanië ln: Albania lo: ແອລເບເນຍ lt: Albanija lv: Albānija mg: Albania mi: Arapeinia mk: Албанија ml: അൽബേനിയ mn: Албани mr: आल्बेनिया ms: Albania mt: Albanija my: အယ်လ်ဘေးနီးယား၏ na: Arbainiya ne: अल्बानिया nl: Albanië "no": Albania nv: Dziłigaii Bikéyah oc: Albania om: Albeeniyaa or: ଆଲବାନିଆ os: Албани pa: ਅਲਬਾਨੀਆ pi: अल्बानिया pl: Albania ps: البانیا pt: Albânia qu: Albanya rm: Albania rn: Alubaniya ro: Albania ru: Албания rw: Alubaniya sa: अल्बेनिया sc: Albania sd: البانيا se: Albánia sg: Albanïi sh: Albanija si: ඇල්බේනියාව sk: Albánsko sl: Albanija sm: Albania sn: Albania so: Albania sq: Shqipëria sr: Албанија ss: I-Alibheniya st: Albania su: Albania sv: Albanien sw: Albania ta: அல்பேனியா te: అల్బేనియా tg: Албания th: ประเทศแอลเบเนีย ti: አልባኒያ tk: Albaniýa tl: Albanya to: ʻAlipania tr: Arnavutluk ts: Albania tt: Албания tw: Albania ty: Arapania ug: ئالبانىيە uk: Алба́нія ur: البانیا uz: Albaniya vi: Albania vo: Lalbanän wa: Albaneye wo: Albaani yi: אלבאניע yo: Albáníà za: Albania zh: 阿爾巴尼亞 zu: I-Albaniya ================================================ FILE: settings/country-names/am.yaml ================================================ name: default: Հայաստան ab: Ермантәыла af: Armenië ak: Armenia am: አርመኒያ an: Armenia ar: أرمينيا as: আৰ্মেনিয়া av: ЦIамухъ az: Ermənistan ba: Әрмәнстан be: Арме́нія bg: Армения bh: आर्मीनिया bi: Armenia bm: Arimeni bn: আর্মেনিয়া bo: ཨར་མེ་ནི་ཡ། br: Armenia bs: Ermenija ca: Armènia ce: Эрмалойчоь ch: Armenia co: Armenia cs: Arménie cu: Армєні́ꙗ cv: Эрмени cy: Armenia da: Armenien de: Armenien dv: އަރުމީނިއާ dz: ཨར་མི་ནི་ཡ ee: Armenia el: Αρμενία en: Armenia eo: Armenio es: Armenia et: Armeenia eu: Armenia fa: ارمنستان ff: Armaaniya fi: Armenia fo: Armenia fr: Arménie fy: Armeenje ga: An Airméin gd: Airmeinia gl: Armenia gn: Aymenia gu: આર્મેનિયા gv: Armeain ha: Armeniya he: אַרְמֶנְיָה hi: आर्मीनिया hr: Armenija ht: Ameni hu: Örményország hy: Հայաստան ia: Armenia id: Armenia ie: Armenia io: Armenia is: Armenía it: Armenia ja: アルメニア jv: Armènia ka: სომხეთი kg: Armenia ki: Armenia kk: Армения kl: Armenia km: អាមេនី kn: ಆರ್ಮೇನಿಯ ko: 아르메니아 ks: اَرمانِیا ku: Ermenistan kv: Армения kw: Armeni ky: Армения la: Armenia lb: Armenien lg: Arameniya li: Armenië ln: Armeni lo: ປະເທດອາກເມນີ lt: Armėnija lv: Armēnija mg: Armenia mi: Āmenia mk: Ерменија ml: അർമേനിയ mn: Армен mo: Армения mr: आर्मेनिया ms: Armenia mt: Armenja my: အာမေးနီးယားနိုင်ငံ na: Arminiya ne: आर्मेनिया nl: Armenië "no": Armenia nv: Aooméénii Bikéyah oc: Armenia om: Armeeniyaa or: ଆର୍ମେନିଆ os: Сомихыстон pa: ਅਰਮੀਨੀਆ pi: आर्मीनिया pl: Armenia ps: ارمنستان pt: Arménia qu: Arminya rm: Armenia rn: Arumeniya ro: Armenia ru: Армения rw: Arumeniya sa: आर्मीनिया sc: Armènia sd: آرمينيا se: Armenia sg: Armenïi sh: Jermenija si: ආර්මේනියාව sk: Arménsko sl: Armenija sm: Amenia sn: Armenia so: Armania sq: Armenia sr: Јерменија ss: iArimeniya su: Arménia sv: Armenien sw: Armenia ta: ஆர்மீனியா te: ఆర్మేనియా tg: Арманистон th: อาร์มีเนีย ti: አርሜኒያ tk: Ermenistan tl: Armenya to: ʻĀminia tr: Ermenistan ts: Armenia tt: Әрмәнстан tw: Aminia ty: Āmenia ug: ئەرمېنىيە uk: Вірме́нія ur: آرمینیا uz: Armaniston vi: Armenia vo: Larmeniyän wo: Armeeni yi: אַרמעניע yo: Arméníà za: Armenia zh: 亞美尼亞 zu: i-Armenia ================================================ FILE: settings/country-names/ao.yaml ================================================ name: default: Angola af: Angola am: አንጎላ an: Angola ar: أنغولا az: Anqola ba: Анго́ла be: Анго́ла bg: Ангола bh: अंगोला bm: Angola bn: অ্যাঙ্গোলা bo: ཨང་གོ་ལ། br: Angola bs: Angola ca: Angola ce: Анго́ла co: Angola cs: Angola cv: Ангола cy: Angola da: Angola de: Angola dv: އެންގޯލާ dz: ཨང་གྷོ་ལ ee: Aŋgola el: Ανγκόλα en: Angola eo: Angolo es: Angola et: Angola eu: Angola fa: آنگولا ff: Anngolaa fi: Angola fo: Angola fr: Angola fy: Angoala ga: Angóla gd: Angola gl: Angola gn: Angola gu: અંગોલા gv: Angoley ha: Angola he: אנגולה hi: अंगोला hr: Angola ht: Angola hu: Angola hy: Անգոլա ia: Angola id: Angola ie: Angola ig: Angola io: Angola is: Angóla it: Angola ja: アンゴラ jv: Angola ka: ანგოლა kg: Ngola ki: Angola kk: Ангола km: អង់ហ្គោឡា kn: ಅಂಗೋಲ ko: 앙골라 ks: اینٛگولا ku: Angola kw: Angola ky: Ангола la: Angolia lb: Angola lg: Angola li: Angola ln: Angola lo: ອັງໂກລາ lt: Angola lv: Angola mg: Angola mi: Anakora mk: Ангола ml: അംഗോള mn: Ангол mr: अँगोला ms: Angola mt: Angola my: အင်ဂိုလာနိုင်ငံ na: Angora ne: अंगोला nl: Angola "no": Angola nv: Angóola ny: Angola oc: Angòla om: Angoolaa or: ଆଙ୍ଗୋଲା os: Анголæ pa: ਅੰਗੋਲਾ pi: अंगोला pl: Angola ps: آنګولا pt: Angola qu: Angula rm: Angola ro: Angola ru: Ангола rw: Angola sa: अङ्गोला sc: Angola se: Angola sg: Angoläa sh: Angola si: ඇන්ගෝලාව sk: Angola sl: Angola sn: Angola so: Angola sq: Angola sr: Ангола ss: I-Angola st: Angola su: Angola sv: Angola sw: Angola ta: அங்கோலா te: అంగోలా tg: Ангола th: ประเทศแองโกลา ti: ኣንጎላ tk: Angola tl: Anggola to: ʻEnikola tr: Angola ts: Angola tt: Анго́ла ug: ئانگولا uk: Анґо́ла ur: انگولہ uz: Angola vi: Angola vo: Langolän wo: Angolaa yi: אַנגאלאַ yo: Àngólà za: Angola zh: 安哥拉 zu: I-Angola ================================================ FILE: settings/country-names/ar.yaml ================================================ name: default: Argentina af: Argentinië ak: Agyɛntina am: አርጀንቲና an: Archentina ar: الأرجنتين as: আৰ্জেণ্টিনা av: Аргентина ay: Arxintina az: Argentina ba: Аргенти́на be: Аргенці́на bg: Аржентѝна bh: अर्जेन्टीना bi: Argentina bm: Argentina bn: আর্জেন্টিনা bo: ཨར་ཇེན་ཊི་ན། br: Arc'hantina bs: Argentina ca: Argentina ce: Аргенти́на ch: Argentina co: Argentina cs: Argentina cv: Аргентина cy: Ariannin da: Argentina de: Argentinien dv: އާޖެންޓީނާ dz: ཨར་ཇེན་ཊི་ན ee: Argentina el: Αργεντινή en: Argentina eo: Argentino es: Argentina et: Argentina eu: Argentina fa: آرژانتین ff: Arjantiin fi: Argentiina fj: Argentina fo: Argentina fr: Argentine fy: Argentynje ga: an Airgintín gd: Argentina gl: Arxentina gn: Argentina gu: આર્જેન્ટીના gv: yn Argenteen ha: Argentina he: ארגנטינה hi: अर्जेण्टीना hr: Argentina ht: Ajantin hu: Argentína hy: Արգենտինա ia: Argentina id: Argentina ie: Argentina ig: Argentina io: Arjentinia is: Argentína it: Argentina ja: アルゼンチン jv: Argèntina ka: არგენტინა kg: Arhentina ki: Argentina kk: Аргентина kl: Argentina km: អាហ្សង់ទីន kn: ಅರ್ಜೆಂಟೀನ ko: 아르헨티나 ks: أرجَنٹینا ku: Arjentîn kv: Аргентина kw: Arghantina ky: Аргентина la: Argentina lb: Argentinien lg: Arigentina li: Argentinië ln: Argentina lo: ອາກຊັງຕີນ lt: Argentina lv: Argentīna mg: Arjentina mi: Āketina mk: Аргентина ml: അർജന്റീന mn: Аргентин mr: आर्जेन्टिना ms: Argentina mt: Arġentina my: အာဂျင်တီးနားနိုင်ငံ na: Ardjentina ne: अर्जेन्टिना nl: Argentinië "no": Argentina nv: Béésh Łigaii Bikéyah oc: Argentina om: Arjentiinaa or: ଆର୍ଜେଣ୍ଟିନା os: Аргентинæ pa: ਅਰਜਨਟੀਨਾ pi: अर्जन्टीना pl: Argentyna ps: ارجنټاین pt: Argentina qu: Arhintina rm: Argentinia rn: Argentine ro: Argentina ru: Аргентина rw: Arijantine sa: अर्जण्टिना sc: Argentina se: Argentiinná sg: Arzantîna sh: Argentina si: ආජන්ටිනාව sk: Argentína sl: Argentina sm: Atenitina sn: Argentina so: Arjantiina sq: Argjentina sr: Аргентина su: Argéntina sv: Argentina sw: Argentina ta: அர்கெந்தீனா te: అర్జెంటీనా tg: Аргенти́на th: อาร์เจนตินา ti: አርጀንቲና tk: Argentina tl: Arhentina to: ʻAsenitina tr: Arjantin ts: Arjentina tt: Аргенти́на tw: Argentina ty: ’Atetina ug: ئارگېنتىنا uk: Аргенти́на ur: ارجنٹائن uz: Argentina vi: Argentina vo: Largäntän wa: Årdjintene wo: Arsantin yi: אַרגענטינע yo: Argẹntínà za: Argentina zh: 阿根廷 zu: i-Argentina ================================================ FILE: settings/country-names/at.yaml ================================================ name: default: Österreich ab: Австриа af: Oostenryk ak: Austria am: ኦስትሪያ an: Austria ar: النمسا as: অষ্ট্ৰিয়া av: Авустур ay: Austriya az: Avstriya ba: Австрия be: Аўстрыя bg: Австрия bh: ऑस्ट्रिया bi: Austria bn: অস্ট্রিয়া bo: ཨོ་སི་ཐྲི་ཡ། br: Aostria bs: Austrija ca: Àustria ce: Австри ch: Austria co: Austria cs: Rakousko cu: Аѵстрїꙗ cv: Австри cy: Awstria da: Østrig de: Österreich dv: އޮސްޓްރިއާ dz: ཨས་ཊི་ཡ ee: Austria el: Αυστρία en: Austria eo: Aŭstrio es: Austria et: Austria eu: Austria fa: اتریش ff: Otiris fi: Itävalta fo: Eysturríki fr: Autriche fy: Eastenryk ga: An Ostair gd: An Ostair gl: Austria gn: Auteria gu: ઑસ્ટ્રિયા gv: Yn Austeyr ha: Austriya he: אוסטריה hi: ऑस्ट्रिया hr: Austrija ht: Otrich hu: Ausztria hy: Ավստրիա ia: Austria id: Austria ie: Austria ig: Austria io: Austria is: Austurríki it: Austria ja: オーストリア jv: Austria ka: ავსტრია kg: Autriche kk: Аустрия Республикасы kl: Østrigi kn: ಆಸ್ಟ್ರಿಯ ko: 오스트리아 ku: Awistriya kv: Австрия kw: Estrych ky: Австрия la: Austria lb: Éisträich li: Oosteriek ln: Otrish lo: ປະເທດໂອຕະລິດ lt: Austrija lv: Austrija mg: Aotrisy mi: Ateria mk: Австрија ml: ഓസ്ട്രിയ mn: Австри mr: ऑस्ट्रिया ms: Austria mt: Awstrija my: သြစတြီးယားနိုင်ငံ na: Oteriya nb: Østerrike ne: अष्ट्रीया nl: Oostenrijk nn: Austerrike "no": Østerrike nv: Óóswiya oc: Àustria om: Oostiriyaa or: ଅଷ୍ଟ୍ରିଆ os: Австри pa: ਆਸਟਰੀਆ pi: आस्ट्रिया pl: Austria ps: اتريش pt: Áustria qu: Awstiriya rm: Austria ro: Austria ru: Австрия rw: Ositiriya sa: आस्ट्रिया sc: Àustria sd: آسٽريا se: Nuortariika sh: Austrija si: ඔස්ට්රියාව sk: Rakúsko sl: Avstrija sm: Austilia sn: Austria so: Austriya sq: Austria sr: Аустрија ss: IYosithiya st: Austria su: Austria sv: Österrike sw: Austria ta: ஆஸ்திரியா te: ఆస్ట్రియా tg: Утриш th: ประเทศออสเตรีย tk: Awstriýa tl: Austria tr: Avusturya ts: Austria tt: Австрия tw: Austria ug: ئاۋستىرىيە uk: Австрія ur: آسٹریا uz: Avstriya vi: Áo vo: Lösterän wa: Otriche wo: Otris yi: עסטרייך yo: Austríà za: Audeihleih zh: 奧地利 zu: I-Ostriya ================================================ FILE: settings/country-names/au.yaml ================================================ name: default: Australia ab: Австралиа af: Australië am: አውስትራልያ an: Australia ar: أستراليا as: অষ্ট্ৰেলিয়া ay: Australla az: Avstraliya ba: Австралия be: Аўстра́лія bg: Австралия bh: ऑस्ट्रेलिया bi: Ostrelia bm: Ostralia bn: অস্ট্রেলিয়া bo: ཨོ་སེ་ཐེ་ལི་ཡ། br: Aostralia bs: Australija ca: Austràlia ce: Австра́ли co: Australia cs: Austrálie cu: Аѵстралїꙗ cv: Австрали cy: Awstralia da: Australien de: Australien dv: އޮސްޓަރުލިޔާ dz: ཨས་ཊེཡེ་ལི་ཡ ee: Australia el: Αυστραλία en: Australia eo: Aŭstralio es: Australia et: Austraalia eu: Australia fa: استرالیا fi: Australia fo: Avstralia fr: Australie fy: Austraalje ga: Astráil gd: Astràilia gl: Australia gn: Autaralia gu: ઑસ્ટ્રેલિયા gv: Austrail ha: Asturaliya he: אוסטרליה hi: ऑस्ट्रेलिया hr: Australija ht: Ostrali hu: Ausztrália hy: Ավստրալիա ia: Australia id: Australia ie: Australia ig: Ostraliya io: Australia is: Ástralía it: Australia iu: ᐊᔅᑦᕌᓕᐊ ja: オーストラリア jv: Ostrali ka: ავსტრალია kg: Australia ki: Australia kk: Аустралия kl: Australia km: អូស្ត្រាលី kn: ಆಸ್ಟ್ರೇಲಿಯ ko: 오스트레일리아 ku: Awistralya kv: Австралия kw: Ostrali ky: Австралия la: Australia lb: Australien li: Australië lo: ປະເທດອົດສະຕາລີ lt: Australija lv: Austrālija mg: Aostralia mi: Ahitereiria mk: Австралија ml: ഓസ്ട്രേലിയ mn: Австрали mr: ऑस्ट्रेलिया ms: Australia mt: Awstralja my: ဩစတြေးလျနိုင်ငံ na: Otereiriya ne: अष्ट्रेलिया nl: Australië "no": Australia nv: Nahatʼeʼiitsoh Bikéyah oc: Austràlia om: Awustireliyaa or: ଅଷ୍ଟ୍ରେଲିଆ os: Австрали pa: ਆਸਟ੍ਰੇਲੀਆ pi: आस्ट्रेलिया pl: Australia ps: آسټراليا pt: Austrália qu: Awstralya rm: Australia rn: Australiya ro: Australia ru: Австралия rw: Ositaraliya sa: आस्ट्रेलिया sc: Austràlia sd: آسٽريليا se: Australia sg: Ostralïi sh: Australija si: ඕස්ට්‍රේලියාව sk: Austrália sl: Avstralija sm: Ausetalia sn: Australia so: Australia sq: Australia sr: Аустралија su: Australia sv: Australien sw: Australia ta: ஆஸ்திரேலியா te: ఆస్ట్రేలియా tg: Австралия th: ออสเตรเลีย ti: ኣውስትራሊያ tk: Awstraliýa tl: Australya tr: Avustralya tt: Австралия tw: Australia ty: Autereraria ug: ئاۋستىرالىيە uk: Австра́лія ur: آسٹریلیا uz: Avstraliya vi: Úc vo: Laustralän wa: Ostraleye wo: Óstraali yi: אויסטראַליע yo: Austrálíà za: Audaihleihya zh: 澳大利亚 zu: I-Ostreliya ================================================ FILE: settings/country-names/az.yaml ================================================ name: default: Azərbaycan ab: Азербаиџьан af: Azerbeidjan ak: Azerbaijan am: አዘርባይጃን an: Azerbaichán ar: أذربيجان as: আজেৰবাইজান av: Азарбижан az: Azərbaycan ba: Әзербайжан be: Азербайджа́н bg: Азербайджан bh: अज़रबैजान bi: Azerbaijan bm: Azɛrbayjaŋ bn: আজারবাইজান bo: ཨ་ཛར་བཡི་ཇན། br: Azerbaidjan bs: Azerbejdžan ca: Azerbaidjan ce: Азербайджа́н co: Azerbaighjan cs: Ázerbájdžán cu: Аꙁѣрбаича́нъ cv: Азербайджан cy: Aserbaijan da: Aserbajdsjan de: Aserbaidschan dv: އަޒަރުބައިޖާން dz: ཨཛར་བཡེ་ཇན། ee: Azerbaijan el: Αζερμπαϊτζάν en: Azerbaijan eo: Azerbajĝano es: Azerbaiyán et: Aserbaidžaan eu: Azerbaijan fa: آذربایجان ff: Aserbayjan fi: Azerbaidžan fo: Aserbadsjan fr: Azerbaïdjan fy: Azerbeidzjan ga: an Asarbaiseáin gd: Asarbaideàn gl: Acerbaixán gn: Aservaijã gu: અઝેરબીજાન gv: yn Asserbajaan ha: Azerbaijan he: אָזַרְבַּיְיגָ'ן hi: अज़रबैजान hr: Azerbajdžan ht: Azerbaydjan hu: Azerbajdzsán hy: Ադրբեջան ia: Azerbaidzhan id: Azerbaijan ie: Azerbaidjan io: Azerbaijan is: Aserbaísjan it: Azerbaigian ja: アゼルバイジャン jv: Azerbaijan ka: აზერბაიჯანი kg: Azerbaidjan ki: Azerbaijan kk: Әзірбайжан kl: Aserbajdsjan km: អាស៊ែបៃហ្សង់ kn: ಅಜೆರ್ಬೈಜಾನ್ ko: 아제르바이잔 ks: آزَرباجان ku: Azerbaycan kv: Азербайджа́н kw: Azerbayjan ky: Азербайжан la: Atropatene lb: Aserbaidschan lg: Azerebayijaani li: Azerbaidzjan ln: Azɛrbayzáni lo: ປະເທດອາແຊກບາຍຊັງ lt: Azerbaidžanas lv: Azerbaidžāna mg: Azerbaijana mi: Atepaihānia mk: Азербејџан ml: അസർബെയ്ജാൻ mn: Азербайжан mo: Азербаиӂан mr: अझरबैजान ms: Azerbaijan mt: Ażerbajġan my: အဇာဘိုင်ဂျန်နိုင်ငံ na: Aderbaidjan ne: अजरबैजान nl: Azerbeidzjan "no": Aserbajdsjan nv: Azééwii Bikéyah ny: Azerbaijan oc: Azerbaitjan om: Azarbaajan or: ଆଜରବାଇଜାନ os: Азербайджан pa: ਅਜ਼ਰਬਾਈਜਾਨ pi: अजर्बैजान pl: Azerbejdżan ps: آزربایجان pt: Azerbaijão qu: Asarsuyu rm: Aserbeidschan ro: Azerbaidjan ru: Азербайджан rw: Azeribayijani sa: अजर्बैजान sc: Azerbaigián se: Azerbaižan sg: Zerebaidyäan sh: Azerbejdžan si: අසර්බයිජානය sk: Azerbajdžan sl: Azerbajdžan sm: Azerbaijin sn: Azerbaijan so: Aserbiijaan sq: Azerbajxhani sr: Азербејџан ss: iZebajani su: Azerbaijan sv: Azerbajdzjan sw: Azerbaijan ta: அசர்பைஜான் te: అజర్‌బైజాన్ tg: Озарбойҷон th: อาเซอร์ไบจาน ti: አዘርባጃን tk: Azerbaýjan tl: Aserbayan to: ʻAsapaisani tr: Azerbaycan ts: Azerbaijan tt: Азәрбайҗан tw: Azerbaijan ty: Atepaihānia ug: ئەزەربەيجان uk: Азербайджа́н ur: آذربائیجان uz: Ozarbayjon vi: Azerbaijan vo: Lasärbäcän wo: Aserbayjaan yi: אַזערבײַדזשאַן yo: Azerbaijan za: Ahsehbaiqgyangh zh: 阿塞拜疆 zu: Azerbaijan ================================================ FILE: settings/country-names/ba.yaml ================================================ name: default: Bosna i Hercegovina / Босна и Херцеговина af: Bosnië en Herzegowina ak: Bosnia and Herzegovina am: ቦስኒያና ሄርጸጎቪና an: Bosnia y Herzegovina ar: البوسنة والهرسك av: Бо́сния ва Герцегови́на az: Bosniya və Herseqovina ba: Босния һәм Герцеговина be: Бо́снія і Герцагаві́на bg: Босна и Херцеговина bh: बोस्निया आ हर्जेगोविना bi: Bosnia mo Herzegovina bm: Bozni-Ɛrizigovini bn: বসনিয়া ও হার্জেগোভিনা bo: བྷོསུ་ནིཡ་དང་ཧར་ཛེ་གྷོ་ཝི་ན། br: Bosnia-ha-Herzegovina bs: Bosna i Hercegovina ca: Bòsnia i Hercegovina ce: Босни а Герцеговина а co: Bosnia è Erzegovina cs: Bosna a Hercegovina cu: Босна́ cv: Босни тата Герцеговина cy: Bosnia a Hercegovina da: Bosnien og Hercegovina de: Bosnien und Herzegowina dv: ބޮސްނިޔާ އެންޑް ހެރްޒިގޮވީނާ ee: Bosnia and Herzegovina el: Βοσνία και Ερζεγοβίνη en: Bosnia and Herzegovina eo: Bosnio kaj Hercegovino es: Bosnia y Herzegovina et: Bosnia ja Hertsegoviina eu: Bosnia eta Herzegovinako fa: بوسنی و هرزگوین ff: Bosniya e Herzegovina fi: Bosnia ja Hertsegovina fo: Bosnia og Hersegovina fr: Bosnie-et-Herzégovine fy: Bosnje-Hertsegovina ga: an Bhoisnia-Heirseagaivéin gd: Bosna agus Hearsagobhana gl: Bosnia e Hercegovina gn: Vonia ha Hesegovina gu: બોસ્નિયા અને હર્ઝેગોવિના gv: Bosnia as Herzegovina ha: Bosnia da Herzegovina he: בּוֹסְנִיָּה וְהֶרְצֶגוֹבִינָה hi: बॉस्निया और हर्ज़ेगोविना hr: Bosna i Hercegovina ht: Bosni ak Erzegovin hu: Bosznia-Hercegovina hy: Բոսնիա և Հերցեգովինա ia: Bosnia e Herzegovina id: Bosnia dan Herzegovina ie: Bosnia e Herzegovina ig: Bosnia na Herzegovina io: Bosnia e Herzegovina is: Bosnía og Hersegóvína it: Bosnia ed Erzegovina ja: ボスニア・ヘルツェゴビナ jv: Bosnia lan Hércegovina ka: ბოსნია და ჰერცეგოვინა kg: Bosna ki: Mbocinia na Hecengobina kk: Босния және Герцеговина kl: Bosnia-Hercegovina km: បូស្ន៉ី kn: ಬೊಸ್ನಿಯ ಮತ್ತು ಹೆರ್ಜೆಗೊವಿನ ko: 보스니아 헤르체고비나 ks: बास्निया ku: Bosniya û Herzegovîna kv: Босния да Герцеговина kw: Bosni–Hercegovina ky: Босния жана Герцеговина la: Bosnia et Herzegovina lb: Bosnien an Herzegowina li: Bosnië en Herzegovina ln: Bosnia na Erzegovina lo: ປະເທດບົດສະນີແຮກເຊໂກວີນ lt: Bosnija ir Hercegovina lv: Bosnija un Hercegovina mg: Bosnia sy Herzegovina mi: Pōngia-Herekōmina mk: Босна и Херцеговина ml: ബോസ്നിയ ആന്റ് ഹെർസെഗോവിന mn: Босни ба Херцеговина mr: बॉस्निया आणि हर्झगोव्हिना ms: Bosnia dan Herzegovina mt: Bożnija u Ħerżegovina my: ဘော့စနီးယားနှင့် ဟာဇီဂိုးဗီးနားနိုင်ငံ na: Boteniya me Erdegobina ne: बोस्निया र हर्जगोभिना nl: Bosnië en Herzegovina "no": Bosnia-Hercegovina nv: Bosna dóó Hetsog Bikéyah oc: Bòsnia e Ercegovina om: Boosniyaa fi Herzegoviinaa or: ବୋସନିଆ ଓ ହରଜଗୋଭିନା os: Босни æмæ Герцеговинæ pa: ਬੋਸਨੀਆ ਅਤੇ ਹਰਜ਼ੇਗੋਵੀਨਾ pi: बास्निया pl: Bośnia i Hercegowina ps: بوسنيا او هېرزګوينا pt: Bósnia e Herzegovina qu: Busna-Hirsiquwina rm: Bosnia-Erzegovina rn: Bosiniya na Herigozevine ro: Bosnia și Herțegovina ru: Босния и Герцеговина rw: Bosiniya na Herizegovina sa: बास्निया sc: Bòsnia e Erzegòvina se: Bosnia ja Hercegovina sg: Bosnïi na Herzegovînni sh: Bosna i Hercegovina si: බොස්නියා සහ හර්සගෝවිනා sk: Bosna a Hercegovina sl: Bosna in Hercegovina sm: Bosnia ma Herzegovina sn: Bosnia and Herzegovina so: Bosniya iyo Herzegovina sq: Bosnja dhe Hercegovina sr: Босна и Херцеговина ss: iBhosinya ne Hezegovi su: Bosnia jeung Hérzégovina sv: Bosnien och Hercegovina sw: Bosnia na Herzegovina ta: பொசுனியாவும் எர்செகோவினாவும் te: బోస్నియా మరియు హెర్జెగొవీనా tg: Босния ва Ҳерсеговина th: บอสเนียและเฮอร์เซโกวีนา ti: ቦስኒያ እና ሄርዞጎቪኒያ tk: Bosniýa we Gersegowina tl: Bosnia at Herzegovina to: Posinia mo Hesikōvinia tr: Bosna-Hersek ts: Bosnia and Herzegovina tt: Бо́сния һәм Герцегови́на ty: Pōtinia-Heretōvina ug: بوسنىيە ۋە ھېرسېگوۋىنا uk: Бо́снія і Герцеґови́на ur: بوسنیا و ہرزیگووینا uz: Bosniya va Gersegovina vi: Bosnia và Herzegovina vo: Bosnän e Härzegovän wa: Bosneye wo: Bosni Hersegowin yi: באסניע און הערצעגאווינע yo: Bósníà àti Hẹrjẹgòfínà zh: 波斯尼亞和黑塞哥維那 zu: IBhosniya neHerzegovina ================================================ FILE: settings/country-names/bb.yaml ================================================ name: default: Barbados af: Barbados am: ባርቤዶስ an: Barbados ar: باربادوس ay: Barbados az: Barbados ba: Барба́дос be: Барба́дас bg: Барбадос bn: বার্বাডোস bo: བར་བ་ཌོ་སི། br: Barbados bs: Barbados ca: Barbados ce: Барбадос cs: Barbados cy: Barbados da: Barbados de: Barbados dv: ބާބަޑޮސް ee: Barbados el: Μπαρμπάντος en: Barbados eo: Barbado es: Barbados et: Barbados eu: Barbados fa: باربادوس fi: Barbados fo: Barbados fr: Barbade fy: Barbados ga: Barbadós gd: Barbados gl: Barbados gn: Varvado gv: Barbados he: ברבדוס hi: बारबाडोस hr: Barbados ht: Lababad hu: Barbados hy: Բարբադոսը ia: Barbados id: Barbados ie: Barbados ig: Barbados io: Barbados is: Barbados it: Barbados ja: バルバドス jv: Barbadhos ka: ბარბადოსი kk: Барба́дос kn: ಬಾರ್ಬಡೋಸ್ ko: 바베이도스 ku: Barbados kv: Барбадос kw: Barbados ky: Барбадос la: Barbata lb: Barbados li: Barbados ln: Barbados lt: Barbadòsas lv: Barbadosa mg: Barbady mk: Барбадос ml: ബർബാഡോസ് mn: Барбадос mr: बार्बाडोस ms: Barbados mt: Barbados my: ဘာဘေးဒိုးစ်နိုင်ငံ na: Barbadot ne: बार्बाडोस nl: Barbados "no": Barbados oc: Barbados om: Baarbeedoos or: ବାରବାଡୋସ os: Барбадос pa: ਬਾਰਬਾਡੋਸ pi: बार्बाडोस pl: Barbados ps: بارباډوس pt: Barbados rm: Barbados ro: Barbados ru: Барбадос rw: Barubadosi sa: बार्बाडोस se: Barbados sh: Barbados sk: Barbados sl: Barbados sn: Barbados so: Barbados sq: Barbadosi sr: Барбадос ss: iBhadosi su: Barbados sv: Barbados sw: Barbados ta: பார்படோசு te: బార్బడోస్ tg: Барбáдос th: บาร์เบโดส tl: Barbados tr: Barbados tt: Барба́дос ug: باربادوس uk: Барба́дос ur: بارباڈوس uz: Barbados vi: Barbados vo: Barbadeän wa: Barbåde wo: Barbados yi: באַרבאַדאס yo: Bárbádọ̀s zh: 巴巴多斯 ================================================ FILE: settings/country-names/bd.yaml ================================================ name: default: Bangladesh af: Bangladesj ar: بنغلاديش az: Banqladeş be: Бангладэш br: Bangladesh ca: Bangla Desh da: Bangladesh de: Bangladesch el: Μπανγκλαντές en: Bangladesh eo: Bangladeŝo fa: بنگلادش fi: Bangladesh fr: Bangladesh fy: Banglades ga: An Bhangladéis gd: Bangladais he: בנגלדש hr: Bangladeš hu: Banglades is: Bangladess it: Bangladesh ja: バングラデシュ la: Bangladesha lb: Bangladesch li: Bangladesj lt: Bangladešas lv: Bangladeša mn: Бангладеш "no": Bangladesh pl: Bangladesz pt: Bangladesh ru: Бангладеш sl: Bangladeš sv: Bangladesh th: ประเทศบังกลาเทศ tr: Bangladeş uk: Бангладеш zh: 孟加拉国 ================================================ FILE: settings/country-names/be.yaml ================================================ name: default: België / Belgique / Belgien af: België ak: Belgium am: ቤልጅግ an: Belchica ar: بلجيكا av: Бе́льгия ay: Bilkiya az: Belçika ba: Бе́льгия be: Бeльгія bg: Белгия bh: बेल्जियम bi: Beljiom bm: Bɛliziki bn: বেলজিয়াম bo: པེར་ཅིན། br: Belgia bs: Belgija ca: Bèlgica ce: Бельги co: Belgica cs: Belgie cu: Бє́лгїѥ cv: Бельги cy: Gwlad Belg da: Belgien de: Belgien dv: ބެލްޖިއަމް dz: བེལ་ཇིཡམ ee: Belgium el: Βέλγιο en: Belgium eo: Belgio es: Bélgica et: Belgia eu: Belgika fa: بلژیک ff: Beljik fi: Belgia fo: Belgia fr: Belgique fy: Belgje ga: an Bheilg gd: a' Bheilg gl: Bélxica gn: Véyhika gu: બેલ્જિયમ gv: Velg ha: Beljik he: בֶּלְגְיָה hi: बेल्जियम hr: Belgija ht: Bèljik hu: Belgium hy: Բելգիա ia: Belgica id: Belgia ie: Belgia ig: Belgium io: Belgia is: Belgía it: Belgio ja: ベルギー jv: Bèlgia ka: ბელგია kg: Belezi ki: Ubelgiji kk: Бельгия kl: Belgia km: បែលហ្ស៉ិក kn: ಬೆಲ್ಜಿಯಂ ko: 벨기에 ks: बेल्जियम ku: Belçîka kv: Бельгия kw: Pow Belg ky: Бельгия la: Belgica lb: Belsch lg: Bubirigi li: Belsj ln: Bɛ́ljika lo: ປະເທດແບນຊິກ lt: Belgija lv: Beļģija mg: Belzika mi: Pehiamu mk: Белгија ml: ബെൽജിയം mn: Бельги mr: बेल्जियम ms: Belgium mt: Belġju my: ဘယ်လ်ဂျီယမ်နိုင်ငံ na: Berdjiyum ne: बेल्जियम nl: België "no": Belgia oc: Belgica om: Beeljiyeem or: ବେଲଜିଅମ os: Бельги pa: ਬੈਲਜੀਅਮ pi: बेल्जियम pl: Belgia ps: بلجیم pt: Bélgica qu: Bilhika rm: Belgia rn: Ububirigi ro: Belgia ru: Бельгия rw: Ububiligi sa: बेल्जियम sc: Bèlgiu sd: بيلجيم se: Belgia sg: Bêleze sh: Belgija si: බෙල්ජියම් sk: Belgicko sl: Bélgija sm: Peleseuma sn: Belgium so: Beljim sq: Belgjika sr: Белгија ss: iBhelijiyamu su: Bélgia sv: Belgien sw: Ubelgiji ta: பெல்ஜியம் te: బెల్జియం tg: Белгия th: เบลเยียม ti: ቤልጄም tk: Belgiýa tl: Belhika to: Pelesiume tr: Belçika ts: Belgium tt: Бе́льгия tw: Belgium ug: بېلگىيە uk: Бе́льгія ur: بلجئیم uz: Belgiya vi: Bỉ vo: Belgän wa: Beldjike wo: Belsik yi: בעלגיע yo: Bẹ́ljíọ̀m zh: 比利時 zu: i-Belgium ================================================ FILE: settings/country-names/bf.yaml ================================================ name: default: Burkina Faso af: Burkina Faso ak: Burkina Faso am: ቡርኪና ፋሶ an: Burkina Faso ar: بوركينا فاسو az: Burkina-Faso ba: Буркина́-Фасо́ be: Буркіна́-Фасо́ bg: Буркина Фасо bh: बुर्किना फासो bm: Burukina Faso bn: বুর্কিনা ফাসো bo: བུར་ཀི་ན་ཕ་སོ། br: Burkina Faso bs: Burkina Faso ca: Burkina Faso ce: Буркина-Фасо cs: Burkina Faso cv: Буркина Фасо cy: Bwrcina Ffaso da: Burkina Faso de: Burkina Faso dv: ބުރުކީނާ ފާސޯ dz: བྷར་ཀི་ན་ ཕེ་སོ ee: Burkina Faso el: Μπουρκίνα Φάσο en: Burkina Faso eo: Burkina Faso es: Burkina Faso et: Burkina Faso eu: Burkina Faso fa: بورکینافاسو ff: Burkina Faso fi: Burkina Faso fo: Burkina Faso fr: Burkina Faso fy: Boerkina Faso ga: Buircíne Fasó gd: Buirciona Faso gl: Burquina Faso gu: બુર્કિના ફાસો gv: Burkina Faso ha: Burkina faso he: בורקינה פאסו hi: बुर्किना फासो hr: Burkina Faso ht: Boukinafaso hu: Burkina Faso hy: Բուրկինա Ֆասո ia: Burkina Faso id: Burkina Faso ie: Burkina Faso ig: Burkina Faso io: Burkina Faso is: Búrkína Fasó it: Burkina Faso ja: ブルキナファソ jv: Burkina Faso ka: ბურკინა-ფასო kg: Burkina Faso ki: Burkina Faso kk: Буркина Фасо km: ប៊ូរគីណាហ្វាសូ kn: ಬುರ್ಕೀನ ಫಾಸೊ ko: 부르키나파소 ks: بُرکِنا فیسو ku: Burkîna Faso kw: Burkina Faso ky: Буркина-Фасо la: Burkina Faso lb: Burkina Faso lg: Burkina Faso li: Burkina Faso ln: Burkina Faso lo: ເບີກິນາຟາໂຊ lt: Burkina Fasas lv: Burkinafaso mg: Borkina Faso mk: Буркина Фасо ml: ബർക്കിനാ ഫാസോ mn: Буркина Фасо mr: बर्किना फासो ms: Burkina Faso mt: Burkina Faso my: ဘာကီးနားဖားဆိုနိုင်ငံ na: Burkinabato ne: बुर्किना फासो nl: Burkina Faso "no": Burkina Faso oc: Burkina Faso om: Burkinaa Faasoo or: ବୁରକିନା ଫାସୋ os: Буркина́-Фасо́ pa: ਬੁਰਕੀਨਾ ਫ਼ਾਸੋ pi: बुर्कीना-फासो pl: Burkina Faso ps: بورکینا فاسو pt: Burquina Faso qu: Burkina Phasu rm: Burkina Faso rn: Burukina Faso ro: Burkina Faso ru: Буркина-Фасо rw: Burukina Faso sa: बुर्कीना-फासो sc: Burkina Faso se: Burkina Faso sg: Burkina Faso sh: Burkina Faso si: බර්කිනා ෆාසෝ sk: Burkina Faso sl: Burkina Faso sm: Burkina Faso sn: Burkina Faso so: Burkina Faso sq: Burkina Faso sr: Буркина Фасо ss: iBhukhina-Faso st: Burkina Faso su: Burkina Faso sv: Burkina Faso sw: Burkina Faso ta: புர்க்கினா பாசோ te: బర్కీనా ఫాసో tg: Буркина Фасо th: บูร์กินาฟาโซ ti: ቡርኪና ፋሶ tk: Burkina-Faso tl: Burkina Faso to: Pekano Faso tr: Burkina Faso ts: Burkina Faso tt: Буркина́ Фасо́ ug: بۇركىنا فاسو uk: Буркіна́-Фасо́ ur: برکینا فاسو uz: Burkina Faso vi: Buốc-ki-na Pha-xô vo: Burkinän wo: Burkinaa Faaso yi: בורקינע פֿאַסא yo: Bùrkínà Fasò zh: 布基纳法索 zu: iBukhina Faso ================================================ FILE: settings/country-names/bg.yaml ================================================ name: default: Бългaрия ab: Болга́риа af: Bulgarye ak: Bulgaria am: ቡልጋሪያ an: Bulgaria ar: بلغاريا av: Болга́рия ay: Bulkariya az: Bolqarıstan ba: Болга́рия be: Балга́рыя bg: България bh: बुल्गारिया bi: Bulgaria bn: বুলগেরিয়া bo: པུ་ར་ག་རི་ཡ། br: Bulgaria bs: Bugarska ca: Bulgària ce: Болга́ри ch: Bulgaria co: Bulgaria cs: Bulharsko cu: Блъгарїꙗ cv: Пăлхарĕ cy: Bwlgaria da: Bulgarien de: Bulgarien dv: ބަލްގޭރިއާ ee: Bulgaria el: Βουλγαρία en: Bulgaria eo: Bulgario es: Bulgaria et: Bulgaaria eu: Bulgaria fa: بلغارستان ff: Bulgariya fi: Bulgaria fo: Bulgaria fr: Bulgarie fy: Bulgarije ga: an Bhulgáir gd: Bulgàiria gl: Bulgaria gn: Vugaria gu: બલ્ગેરિયા gv: yn Vulgeyr ha: Bulgairiya he: בּוּלְגַרְיָה hi: बुल्गारिया hr: Bugarska ht: Bilgari hu: Bulgária hy: Բուլղարիա ia: Bulgaria id: Bulgaria ie: Bulgaria ig: Bulgaria io: Bulgaria is: Búlgaría it: Bulgaria ja: ブルガリア jv: Bulgaria ka: ბულგარეთი kg: Balgaria kk: Болгария kl: Bulgaria kn: ಬಲ್ಗೇರಿಯ ko: 불가리아 ku: Bulgaristan kv: Болгария kw: Bulgari ky: Болгария la: Bulgaria lb: Bulgarien li: Bölgarije ln: Bulgaria lo: ປະເທດບູນກາລີ lt: Bulgarija lv: Bulgārija mg: Biolgaria mi: Purukāria mk: Бугарија ml: ബൾഗേറിയ mn: Болгар mr: बल्गेरिया ms: Bulgaria mt: Bulgarija my: ဘူလ်ဂေးရီးယားနိုင်ငံ na: Borgeriya ne: बुल्गेरिया nl: Bulgarije "no": Bulgaria nv: Bálgaa Bikéyah oc: Bulgaria om: Bulgeeriyaa or: ବୁଲଗାରିଆ os: Болгари pa: ਬੁਲਗਾਰੀਆ pi: बुल्गारिया pl: Bułgaria ps: بلغاریا pt: Bulgária qu: Bulgarya rm: Bulgaria rn: Bulgaria ro: Bulgaria ru: Болгария rw: Bulugariya sa: बुल्गारिया sc: Bulgaria sd: بلغاريه se: Bulgária sg: Bulugarïi sh: Bugarska si: බල්ගේරියාව sk: Bulharsko sl: Bolgarija sm: Bultalia sn: Bulgaria so: Bulgaria sq: Bullgaria sr: Бугарска ss: iBhulgariya su: Bulgaria sv: Bulgarien sw: Bulgaria ta: பல்கேரியா te: బల్గేరియా tg: Булғористон th: บัลแกเรีย tk: Bolgariýa tl: Bulgarya tr: Bulgaristan ts: Bulgaria tt: Болгария tw: Bulgaria ug: بۇلغارىيە uk: Болга́рія ur: بلغاریہ uz: Bolgariya vi: Bulgaria vo: Bulgarän wa: Bulgåreye wo: Bulgaari yi: בולגאַריע yo: Bùlgáríà zh: 保加利亚 zu: IBulgariya ================================================ FILE: settings/country-names/bh.yaml ================================================ name: default: البحرين af: Bahrein ak: Baren am: ባሕሬን an: Bahrein ar: البحرين as: বাহৰেইন az: Bəhreyn ba: Бахре́йн be: Бахрэ́йн bg: Бахрейн bh: बहरीन bm: Bareyini bn: বাহরাইন bo: བྷཧ་རཡིན། br: Bahrein bs: Bahrein ca: Bahrain ce: Бахьрайн cs: Bahrajn cv: Бахрейн cy: Bahrein da: Bahrain de: Bahrain dv: ބަޙްރައިން dz: བཧ་རེན་ ee: Bahrain nutome el: Μπαχρέιν en: Bahrain eo: Barejno es: Baréin et: Bahrein eu: Bahrain fa: بحرین ff: Bahreyn fi: Bahrain fo: Barein fr: Bahreïn fy: Bachrein ga: Bairéin gd: Bachrain gl: Bahrein gn: Varéĩ gu: બહેરીન gv: Bahrain ha: Baharain he: בחריין hi: बहरीन hr: Bahrein ht: Barayn hu: Bahrein hy: Բահրեյն ia: Bahrain id: Bahrain ie: Bahrain io: Bahrain is: Barein it: Bahrein ja: バーレーン jv: Bahrain ka: ბაჰრეინი kg: Bahrain ki: Bahrain kk: Бахрейн kl: Bahrain km: បារ៉ែន kn: ಬಹರೇನ್ ko: 바레인 ks: بحریٖن ku: Behreyn kv: Бахрейн kw: Bahreyn ky: Бахре́йн la: Baharina lb: Bahrain lg: Baareeni li: Bahrein ln: Bahrein lo: ບາເລນ lt: Bahreinas lv: Bahreina mg: Baharainy mk: Бахреин ml: ബഹ്‌റൈൻ mn: Бахрейн mr: बहरैन ms: Bahrain mt: Baħrejn my: ဘာရိန်းနိုင်ငံ na: Bahrain ne: बहराइन nl: Bahrein "no": Bahrain oc: Bahrayn om: Baahireen or: ବାହାରିନ୍ os: Бахрейн pa: ਬਹਿਰੀਨ pi: बहरैन pl: Bahrajn ps: بحرین pt: Bahrein qu: Bahrayn rm: Bahrain rn: Bahareyini ro: Bahrain ru: Бахрейн rw: Bahirayini sa: बहरैन sd: بحرين se: Bahrain sg: Bahrâina sh: Bahrein si: බහරේන් sk: Bahrajn sl: Bahrájn sm: Bahrain sn: Bahrain so: Baxrayn sq: Bahraini sr: Бахреин ss: iBhaharayi su: Bahrain sv: Bahrain sw: Bahrain ta: பகுரைன் te: బహ్రయిన్ tg: Баҳрайн th: บาห์เรน ti: ባህሬን tk: Bahreýn tl: Bahrain to: Paleini tr: Bahreyn tt: Бәхрәйн ug: بەھرېيىن uk: Бахре́йн ur: بحرین uz: Baxreyn vi: Bahrain vo: Bahruäns wo: Bahrayni yi: באכריין yo: Báháráìnì zh: 巴林 zu: i-Bahrain ================================================ FILE: settings/country-names/bi.yaml ================================================ name: default: Burundi af: Burundi am: ቡሩንዲ an: Burundi ar: بوروندي az: Burundi ba: Буру́нди be: Буру́ндзі bg: Бурунди bh: बुरुंडी bm: Burundi bn: বুরুন্ডি bo: བུ་རུན་ཌི། br: Burundi bs: Burundi ca: Burundi ce: Бурунди cs: Burundi cv: Бурунди cy: Bwrwndi da: Burundi de: Burundi dv: ބުރުންޑީ dz: བྷུ་རུན་ཌི ee: Burundi el: Μπουρούντι en: Burundi eo: Burundo es: Burundí et: Burundi eu: Burundi fa: بوروندی ff: Burunndi fi: Burundi fo: Burundi fr: Burundi fy: Boerûndy ga: an Bhurúin gd: Burundaidh gl: Burundi gn: Vurundi gu: બુરુંડી gv: Burundee ha: Burundi he: בורונדי hi: बुरुण्डी hr: Burundi ht: Bouroundi hu: Burundi hy: Բուրունդի ia: Burundi id: Burundi ie: Burundi ig: Burundi io: Burundi is: Búrúndí it: Burundi ja: ブルンジ jv: Burundi ka: ბურუნდი kg: Burundi ki: Burundi kk: Бурунди km: ប៊ូរុនឌី kn: ಬುರುಂಡಿ ko: 부룬디 ks: بورَنڈِ ku: Burundî kw: Burundi ky: Бурунди la: Burundia lb: Burundi lg: Burundi li: Boeroendi ln: Burundi lo: ບູລັນດີ lt: Burundis lv: Burundi mg: Borondy mk: Бурунди ml: ബറുണ്ടി mn: Бурунди mr: बुरुंडी ms: Burundi mt: Burundi my: ဘူရွန်ဒီနိုင်ငံ na: Burundi ne: बुरूण्डी nl: Burundi "no": Burundi oc: Burundi om: Buruundii or: ବୁରୁଣ୍ଡି os: Буру́нди pa: ਬੁਰੂੰਡੀ pi: बुरुंडी pl: Burundi ps: برونډي pt: Burundi qu: Burundi rm: Burundi rn: Uburundi ro: Burundi ru: Бурунди rw: Uburundi sa: बुरुंडी sc: Burundi se: Burundi sg: Burundïi sh: Burundi si: බුරුන්ඩි sk: Burundi sl: Burundi sn: Burundi so: Burundi sq: Burundi sr: Бурунди ss: iBurundi st: Burundi su: Burundi sv: Burundi sw: Burundi ta: புருண்டி te: బురుండి tg: Бурунди th: บุรุนดี ti: ብሩንዲ tk: Burundi tl: Burundi to: Pelaniti tr: Burundi ts: Burundi tt: Буру́нди ug: بۇرۇندى uk: Буру́нді ur: برونڈی uz: Burundi vi: Burundi vo: Rundiyän wa: Bouroundi wo: Buruundi yi: בורונדי yo: Bùrúndì zh: 布隆迪 zu: iBurundi ================================================ FILE: settings/country-names/bj.yaml ================================================ name: default: Bénin af: Benin ak: Bɛnin am: ቤኒን an: Benín ar: بنين az: Benin ba: Бенин be: Бені́н bg: Бенин bh: बेनिन bm: Benɛn bn: বেনিন bo: བེ་ནིན། br: Benin bs: Benin ca: Benín ce: Бени́н cs: Benin cv: Бенин cy: Benin da: Benin de: Benin dv: ބެނީން dz: བཱེ་ནིན། ee: Benɛ̃ el: Μπενίν en: Benin eo: Benino es: Benín et: Benin eu: Benin fa: بنین ff: Benen fi: Benin fo: Benin fr: Bénin fy: Benyn ga: Beinin gd: Beinin gl: Benin gn: Mbenĩ gu: બેનિન gv: Benin ha: Benin he: בנין hi: बेनिन hr: Benin ht: Benen hu: Benin hy: Բենին ia: Benin id: Benin ie: Benin ig: Benin io: Benin is: Benín it: Benin ja: ベナン jv: Bénin ka: ბენინი kg: Benin ki: Benin kk: Бени́н kl: Benin km: បេណាំង kn: ಬೆನಿನ್ ko: 베냉 ks: بِنِن ku: Bênîn kw: Benin ky: Бени́н la: Beninum lb: Benin lg: Benin li: Benin ln: Benin lo: ເບນິນ lt: Beninas lv: Benina mg: Benina mi: Pēnina mk: Бенин ml: ബെനിൻ mn: Бенин mr: बेनिन ms: Benin mt: Benin my: ဘီနင်နိုင်ငံ na: Benin ne: बेनिन nl: Benin "no": Benin oc: Benin om: Beeniin or: ବେନିନ os: Бенин pa: ਬੇਨਿਨ pi: बेनिन pl: Benin ps: بېنين pt: Benim qu: Binin rm: Benin rn: Bene ro: Benin ru: Бенин rw: Bene sa: बेनिन् sc: Benin sd: بينن se: Benin sg: Benëen sh: Benin si: බෙනින් sk: Benin sl: Benin sn: Benin so: Benin sq: Benini sr: Бенин ss: iBhenini st: Benin su: Bénin sv: Benin sw: Benin ta: பெனின் te: బెనిన్ tg: Бенѝн th: เบนิน ti: ቤኒን tk: Benin tl: Benin to: Penini tr: Benin ts: Benin tt: Бени́н ug: بېنىن uk: Бені́н ur: بینن uz: Benin vi: Bénin vo: Beninän wo: Bene yi: בענין yo: Benin zh: 贝宁 zu: IBenini ================================================ FILE: settings/country-names/bm.yaml ================================================ name: default: Bermuda ar: برمودا be: Бярмуды br: Bermudez ca: Bermudes cs: Bermudy da: Bermuda de: Bermuda el: Βερμούδες en: Bermuda eo: Bermudoj fa: برمودا fi: Bermuda fr: Bermudes ga: Beirmiúda gl: Bermudas he: ברמודה hr: Bermudski Otoci hu: Bermuda id: Bermuda is: Bermúda it: Bermuda lt: Bermuda lv: Bermudu salas mk: Бермуда mn: Бермудын Арал "no": Bermuda oc: Bermudas pl: Bermudy pt: Bermudas ru: Бермудские острова sl: Bermudi sr: Бермуди sv: Bermuda th: เบอร์มิวดา tr: Bermuda uk: Бермудські острови uz: Bermud orollari zh: 百慕大 ================================================ FILE: settings/country-names/bn.yaml ================================================ name: default: Brunei af: Broenei ak: Brunae am: ብሩናይ an: Brunei ar: بروناي as: ব্ৰুণেই az: Bruney ba: Бруней be: Бруне́й bg: Бруней bh: ब्रूनेई bm: Burinɛyi bn: ব্রুনাই bo: བྷི་རུ་ནི། br: Brunei bs: Brunej ca: Brunei ce: Бруней cs: Brunej cv: Бруней cy: Brwnei da: Brunei de: Brunei dv: ބުރުނައީ dz: བྷྲུ་ནའི el: Μπρουνέι en: Brunei eo: Brunejo es: Brunéi et: Brunei eu: Brunei fa: برونئی ff: Burnaay fi: Brunei fo: Brunei fr: Brunei fy: Brûnei ga: Brúiné gd: Brùnaigh gl: Brunei gn: Mburunéi gu: બ્રુનેઈ gv: Brunei ha: Brunei he: ברוניי hi: ब्रुनेई hr: Brunej ht: Brouney hu: Brunei hy: Բրունեյ ia: Brunei id: Brunei ie: Brunei ik: Brunei io: Brunei is: Brúnei it: Brunei ja: ブルネイ jv: Brunéi ka: ბრუნეი kg: Brunei ki: Brunei kk: Бруней kl: Brunei km: ប្រទេសប៊្រុយណេ kn: ಬ್ರುನೈ ko: 브루나이 ks: بُرنٔے ku: Brûney kv: Бруней kw: Bruney ky: Бруней la: Bruneium lb: Brunei li: Brunei ln: Brunei lo: ບູໄນ lt: Brunėjus lv: Bruneja mg: Boroney mi: Poronai mk: Брунеј ml: ബ്രൂണൈ mn: Бруней mr: ब्रुनेई ms: Brunei mt: Brunej my: ဘရူနိုင်းနိုင်ငံ na: Brunei ne: ब्रुनेई nl: Brunei "no": Brunei ny: Brunei oc: Brunei om: Birunaay or: ବ୍ରୁନେଇ os: Бруней pa: ਬਰੂਨਾਈ pi: ब्रूनै pl: Brunei ps: برونای pt: Brunei qu: Bruniy rm: Brunei ro: Brunei ru: Бруней rw: Buruneyi sa: ब्रूनै sd: برونئي se: Brunei sg: Brunêi sh: Brunej si: බෲනායි sk: Brunej sl: Brunej sn: Brunei so: Barunay sq: Brunei sr: Брунеј ss: iBhruneyi su: Brunéi sv: Brunei sw: Brunei ta: புரூணை te: బ్రూనై tg: Бруней th: บรูไน ti: ብሩኒ tk: Bruneý tl: Brunei to: Pulunei tr: Brunei tt: Бруне́й ug: برۇنېي uk: Бруне́й ur: برونائی uz: Bruney vi: Brunei vo: Brunän yi: ברוניי yo: Brunei za: Vwnzlaiz zh: 文莱 zu: i-Brunei ================================================ FILE: settings/country-names/bo.yaml ================================================ name: default: Bolivia ab: Боли́виа af: Bolivië am: ቦሊቪያ an: Bolivia ar: بوليفيا ay: Wuliwya az: Boliviya ba: Боливия be: Балі́вія bg: Боливия bh: बोलीविया bi: Bolivia bm: Bolivia bn: বলিভিয়া bo: བྷོ་ལི་ཝི་ཡ། br: Bolivia bs: Bolivija ca: Bolívia ce: Боливи co: Bolivia cs: Bolívie cv: Боливи cy: Bolifia da: Bolivia de: Bolivien dv: ބޮލީވިއާ dz: བོ་ལི་ཝིཡ ee: Bolivia el: Βολιβία en: Bolivia eo: Bolivio es: Bolivia et: Boliivia eu: Bolivia fa: بولیوی ff: Boliwii fi: Bolivia fo: Bolivia fr: Bolivie fy: Bolivia ga: an Bholaiv gd: Boilibhia gl: Bolivia gn: Volívia gu: બોલીવિયા gv: yn Volivia ha: Bolibiya he: בוליביה hi: बोलिविया hr: Bolivija ht: Bolivi hu: Bolívia hy: Բոլիվիան ia: Bolivia id: Bolivia ie: Bolivia ig: Bolivia io: Bolivia is: Bólivía it: Bolivia ja: ボリビア jv: Bolivia ka: ბოლივია kg: Bolivia ki: Bolivia kk: Боли́вия kl: Bolivia km: បូលីវី kn: ಬೊಲಿವಿಯ ko: 볼리비아 ks: بولِوِیا ku: Bolîvya kw: Bolivi ky: Боливия la: Bolivia lb: Bolivien lg: Boliviya li: Bolivia ln: Bolivia lo: ໂບລີວີ lt: Bolivija lv: Bolīvija mg: Bolivia mi: Poriwia mk: Боливија ml: ബൊളീവിയ mn: Боливи mr: बोलिव्हिया ms: Bolivia mt: Bolivja my: ဘိုလီးဗီးယားနိုင်ငံ na: Boribiya ne: बोलिभिया nl: Bolivia "no": Bolivia nv: Bolíbiya oc: Bolívia om: Boliiviyaa or: ବୋଲିଭିଆ os: Боливи pa: ਬੋਲੀਵੀਆ pi: बोलिविया pl: Boliwia ps: بولېویا pt: Bolívia qu: Wuliwiya rm: Bolivia rn: Boliviya ro: Bolivia ru: Боливия rw: Boliviya sa: बोलिविया sc: Bolìvia se: Bolivia sg: Bolivïi sh: Bolivija si: බොලීවියාව sk: Bolívia sl: Bolivija sm: Polevia sn: Bolivia so: Boliifia sq: Bolivia sr: Боливија ss: IBholiviya su: Bolivia sv: Bolivia sw: Bolivia ta: பொலிவியா te: బొలీవియా tg: Боливия th: โบลิเวีย ti: ቦሊቪያ tk: Boliwiýa tl: Bolivia to: Polīvia tr: Bolivya tt: Боли́вия ug: بولىۋىيە uk: Болі́вія ur: بولیویا uz: Boliviya vi: Bolivia vo: Bolivän wa: Boliveye wo: Boliibi yi: באליוויע yo: Bòlífíà za: Bolivia zh: 玻利维亚 zu: i-Bolivia ================================================ FILE: settings/country-names/br.yaml ================================================ name: default: Brasil af: Brasilië ak: Brazil am: ብራዚል an: Brasil ar: البَرَازِيل as: ব্ৰাজিল ay: Wrasil az: Braziliya ba: Брази́лия be: Бразі́лія bg: Бразилия bh: ब्राज़ील bi: Brazil bm: Brazil bn: ব্রাজিল bo: པུ་རུ་ཟིལ། br: Brazil bs: Brazil ca: Brasil ce: Брази́ли ch: Brazil co: Brasile cs: Brazílie cu: Браꙁїлі́ꙗ cv: Бразили cy: Brasil da: Brasilien de: Brasilien dv: ބުރެޒިލް dz: བཱརཱ་ཛིལ་ ee: Brazil el: Βραζιλία en: Brazil eo: Brazilo es: Brasil et: Brasiilia eu: Brasil fa: برزیل ff: Barazil fi: Brasilia fj: Brazil fo: Brasilia fr: Brésil fy: Brazylje ga: an Bhrasaíl gd: Braisil gl: Brasil gn: Pindoráma gu: બ્રાઝિલ gv: yn Vrasseel ha: Brazil he: ברזיל hi: ब्रास़ील hr: Brazil ht: Brezil hu: Brazília hy: Բրազիլիա ia: Brasil id: Brasil ie: Brasil ig: Brazil io: Brazilia is: Brasilía it: Brasile ja: ブラジル jv: Brasil ka: ბრაზილია kg: Brazilia ki: Brazil kk: Бразилия kl: Brazil km: ប្រេស៊ីល kn: ಬ್ರೆಜಿಲ್ ko: 브라질 ks: برٛازِل ku: Brazîl kv: Бразилия kw: Brasil ky: Бразилия la: Brasilia lb: Brasilien lg: Buraziiri li: Braziel ln: Brazil lo: ບະເຣຊິນ lt: Brazilija lv: Brazīlija mg: Brazila mi: Parīhi mk: Бразил ml: ബ്രസീൽ mn: Бразил mr: ब्राझील ms: Brazil mt: Brażil my: ဘရာဇီးနိုင်ငံ na: Bradir ne: ब्राजिल nl: Brazilië "no": Brasil nv: Bwazííl oc: Brasil om: Biraazil or: ବ୍ରାଜିଲ os: Бразили pa: ਬ੍ਰਾਜ਼ੀਲ pi: ब्रासील pl: Brazylia ps: برازیل pt: Brasil qu: Prasil rm: Brasilia rn: Brazil ro: Brazilia ru: Бразилия rw: Burezile sa: ब्रासील sc: Brasile sd: برازيل se: Brasilia sh: Brazil si: බ්රසීලය sk: Brazília sl: Brazilija sm: Pasila sn: Brazil so: Barasiil sq: Brazili sr: Бразил st: Brazil su: Brasil sv: Brasilien sw: Brazil ta: பிரேசில் te: బ్రెజిల్ tg: Бразилия th: บราซิล ti: ብራዚል tk: Braziliýa tl: Brasil to: Palāsili tr: Brezilya tt: Брази́лия tw: Brazil ty: Parīihi ug: برازىلىيە uk: Брази́лія ur: برازیل uz: Braziliya ve: Brazil vi: Brasil vo: Brasilän wa: Braezi wo: Breesil xh: Brasil yi: בראַזיל yo: Brasil za: Bahsih zh: 巴西 zu: IBrazili ================================================ FILE: settings/country-names/bs.yaml ================================================ name: default: The Bahamas af: Bahamas ak: Bahama am: ባሃማስ an: Bahamas ar: جزر باهاماس ay: Bahamas az: Bahamalar ba: Багама утрауҙары be: Бага́мскія Астравы́ bg: Бахамските острови bm: Bahamasi bn: বাহামা দ্বীপপুঞ্জ bo: བ་ཧ་མཱ་སི། br: Bahamas bs: Bahami ca: Bahames ce: Багама cs: Bahamy cv: Пахамсем cy: Y Bahamas da: Bahamas de: Die Bahamas dv: ބަހާމަސް dz: བཱ་ཧ་མས྄། ee: Bahamas el: Μπαχάμες en: The Bahamas eo: La Bahamoj es: Las Bahamas et: Bahama eu: Bahamak fa: باهاما ff: Bahamaas fi: Bahama fo: Bahamaoyggjar fr: Les Bahamas fy: De Bahama's ga: na Bahámaí gd: na Bahàmas gl: Bahamas gn: Vaamakuéra gu: બહામાસ gv: ny Bahamaghyn he: איי בהאמה hi: बहामस hr: Bahami ht: Il Bahamas hu: Bahama-szigetek hy: Բահամները ia: Bahamas id: Bahama io: Bahama is: Bahamaeyjar it: Le Bahamas ja: バハマ jv: Bahama ka: ბაჰამის კუნძულები ki: Bahama kk: Багам аралдары km: បាហាម៉ា kn: ಬಹಾಮಾಸ್ ko: 바하마 ks: بَہامَس ku: Bahama kw: Ynysow Bahama ky: Багама аралдары la: Bahamanae lb: D'Bahamas lg: Bahamasi li: De Bahama's ln: Bahamasɛ lo: ບາຮາມມາສ lt: Bahamos lv: Bahamas mg: Bahamasy mk: Бахами ml: ബഹാമാസ് mn: Багамын Арлууд mr: बहामास ms: Bahamas mt: Il-Baħamas my: ဘဟားမားနိုင်ငံ na: Bahamat ne: बहामस nl: Bahama's "no": Bahamas oc: Las Bahamas om: Bahamaas or: ବାହାମା os: Багамтæ pa: ਬਹਾਮਾਸ pi: बहामास pl: Bahamy pt: Bahamas qu: Bahamakuna rm: Bahamas rn: Bahamasi ro: Bahamas ru: Багамские Острова rw: Bahamasi sa: बहामास se: Bahamasullot sg: Bahâmasa sh: Bahami si: බහාමාස් sk: Bahamy sl: Bahami sn: The Bahamas so: Bahamas sq: Bahamet sr: Бахаме ss: iBhahamasi su: Bahama sv: Bahamas sw: Bahamas ta: பாகாமாசு te: బహామాస్ tg: Баҳамас th: ประเทศบาฮามาส ti: ባሃማስ tk: Bagama Adalary tl: Bahamas to: Pahama tr: Bahamalar tt: Багамалар ug: باھاما uk: Багамські Острови ur: بہاماس uz: Bagam orollari vi: Bahamas vo: Bahamuäns wo: Bahamas yi: די באַהאַמאַס yo: Àwọn Bàhámà za: Bahamas zh: 巴哈马 zu: i-Bahamas ================================================ FILE: settings/country-names/bt.yaml ================================================ name: default: འབྲུག་ཡུལ་ ab: Бутан af: Bhoetan ak: Butan am: ቡታን an: Bután ar: بوتان as: ভূটান az: Butan ba: Бута́н be: Бута́н bg: Бутан bh: भूटान bi: Bhutan bn: ভুটান bo: འབྲུག་ཡུལ། br: Bhoutan bs: Butan ca: Bhutan ce: Бутан cs: Bhútán cv: Бутан cy: Bhwtan da: Bhutan de: Bhutan dv: ބޫޓާން dz: འབྲུགཡུལ་ el: Μπουτάν en: Bhutan eo: Butano es: Bután et: Bhutan eu: Bhutan fa: بوتان ff: Butaan fi: Bhutan fo: Butan fr: Bhoutan fy: Bûtan ga: an Bhútáin gd: Butàn gl: Bután gn: Vutã gu: ભૂતાન gv: yn Vutaan ha: Bhutan he: בהוטן hi: भूटान hr: Butan ht: Boutan hu: Bhután hy: Բութան ia: Bhutan id: Bhutan ie: Bhutan io: Bhutan is: Bútan it: Bhutan ja: ブータン jv: Bhutan ka: ბჰუტანი kg: Bhutan ki: Bhutan kk: Бутан kl: Bhutan km: ប៊ូតាន kn: ಭೂತಾನ್ ko: 부탄 ks: بوٗٹان ku: Bûtan kv: Бутан kw: Bhoutan ky: Бутан la: Butania lb: Bhutan lg: Butaani li: Bhutan ln: Butáni lo: ບູຕານ lt: Butanas lv: Butāna mg: Botàna mi: Putāna mk: Бутан ml: ഭൂട്ടാൻ mn: Бутан mr: भूतान ms: Bhutan mt: Butan my: ဘူတန်နိုင်ငံ na: Butan ne: भूटान nl: Bhutan "no": Bhutan nv: Bikéyah oc: Botan om: Buutan or: ଭୁଟାନ os: Бутан pa: ਭੂਟਾਨ pi: भूटान pl: Bhutan ps: بوتان pt: Butão qu: Butan rm: Bhutan rn: Butani ro: Bhutan ru: Бутан rw: Butani sa: भूटान se: Bhutan sg: Butäan sh: Butan si: භූතානය sk: Bhután sl: Butan sn: Bhutan so: Butaan sq: Butani sr: Бутан ss: iBhuthani su: Butan sv: Bhutan sw: Bhutan ta: பூட்டான் te: భూటాన్ tg: Бутан th: ประเทศภูฏาน ti: ቡህታን tk: Butan tl: Butan to: Pūtani tr: Bhutan tt: Бутан tw: Butan ug: بۇتان uk: Бута́н ur: بھوٹان uz: Butan vi: Bhutan vo: Butän wo: Butaan yi: בהוטאן yo: Bhùtán za: Budanh zh: 不丹 zu: i-Bhutan ================================================ FILE: settings/country-names/bw.yaml ================================================ name: default: Botswana af: Botswana ak: Bɔtswana am: ቦትስዋና an: Botswana ar: بوتسوانا az: Botsvana ba: Ботсвана be: Батсва́на bg: Ботсвана bh: बोत्सवाना bm: Botswana bn: বতসোয়ানা bo: བྷོ་ཙི་ཝ་ན། br: Botswana bs: Bocvana ca: Botswana ce: Ботсвана cs: Botswana cv: Ботсвана cy: Botswana da: Botswana de: Botsuana dv: ބޮޓުސްވާނާ dz: བྷོཙ་ཝ་ན ee: Botswana el: Μποτσουάνα en: Botswana eo: Bocvano es: Botsuana et: Botswana eu: Botswana fa: بوتسوانا ff: Botswana fi: Botswana fo: Botsvana fr: Botswana fy: Botswana ga: an Bhotsuáin gd: Botsuana gl: Botswana gn: Votusuana gu: બોત્સ્વાના gv: yn Votswaan ha: Botswana he: בּוֹטְסְוָאנָה hi: बोत्सवाना hr: Bocvana ht: Botswana hu: Botswana hy: Բոտսվանա ia: Botswana id: Botswana ie: Botswana ig: Botswana io: Botswana is: Botsvana it: Botswana ja: ボツワナ jv: Botswana ka: ბოტსვანა kg: Botswana ki: Botswana kk: Ботсвана km: បុតស្វាណា kn: ಬೋಟ್ಸ್ವಾನ ko: 보츠와나 ks: بوتَسوانا ku: Botswana kw: Botswana ky: Ботсвана la: Botsuana lb: Botswana lg: Botswana li: Botswana ln: Botswana lo: ບອດສະວານາ lt: Botsvana lv: Botsvana mg: Bôtsoàna mi: Poriwana mk: Боцвана ml: ബോട്സ്വാന mn: Ботсвана mr: बोत्स्वाना ms: Botswana mt: Botswana my: ဘော့ဆွာနာ သမ္မတနိုင်ငံ na: Botwana ne: बोत्स्वाना nl: Botswana "no": Botswana nv: Tswana Dineʼé Bikéyah oc: Botswana om: Bootiswaanaa or: ବୋସଚୱାନା os: Ботсванæ pa: ਬੋਤਸਵਾਨਾ pi: बोत्सवाना pl: Botswana ps: بوتسوانا pt: Botsuana qu: Butswana rm: Botswana ro: Botswana ru: Ботсвана rw: Botswana sa: बोत्सवाना sc: Botzuana se: Botswana sg: Botswana sh: Bocvana si: බොට්ස්වානා sk: Botswana sl: Bocvána sn: Botswana so: Botswana sq: Bocvana sr: Боцвана ss: ÉButjwána st: Botswana su: Botswana sv: Botswana sw: Botswana ta: போட்சுவானா te: బోత్సువానా tg: Ботсвана th: บอตสวานา ti: ቦትስዋና tk: Botswana tl: Botswana tn: Botswana to: Potisiuana tr: Botsvana ts: Botswana tt: Ботсва́на ug: بوتسۋانا uk: Ботсва́на ur: بوٹسوانا uz: Botsvana ve: Vhu-Tswana vi: Bốt-xoa-na vo: Zvanän wa: Boswana wo: Botswana yi: באצוואַנע yo: Bòtswánà zh: 波札那 zu: i-Botswana ================================================ FILE: settings/country-names/by.yaml ================================================ name: default: Беларусь af: Wit-Rusland am: ቤላሩስ an: Belarrusia ar: روسيا البيضاء az: Belarus be: Беларусь bg: Беларус bi: Belarus bn: বেলারুশ bo: བྷེ་ལ་རསུ། br: Belarus bs: Bjelorusija ca: Bielorússia ce: Белорусси co: Bielorussia cs: Bělorusko cu: Бѣла Роусь cv: Беларуç cy: Belarws da: Hviderusland de: Belarus dv: ބެލަރޫސް dz: བེ་ལ་རུ་སུ། ee: Belarus el: Λευκορωσία en: Belarus eo: Belorusio es: Bielorrusia et: Valgevene eu: Bielorrusia fa: بلاروس ff: Belaruusiya fi: Valko-Venäjä fo: Hvítarussland fr: Biélorussie fy: Wyt-Ruslân ga: An Bhealarúis gd: A' Bhealaruis gl: Bielorrusia gn: Vielorrusia gu: બેલારુસ gv: Yn Velaroosh he: בלארוס hi: बेलारूस hr: Bjelorusija ht: Byelorisi hu: Fehéroroszország hy: Բելառուս ia: Bielorussia id: Belarus ie: Bielorussia io: Bielorusia is: Hvíta-Rússland it: Bielorussia ja: ベラルーシ jv: Bélarus ka: ბელარუსი kg: Belarusia kk: Беларусь kl: Hvideruslandi kn: ಬೆಲಾರುಸ್ ko: 벨라루스 ku: Belarûs kv: Беларусь kw: Belarussi ky: Беларусия la: Ruthenia Alba lb: Wäissrussland li: Wit-Rusland ln: Bielorusia lt: Baltarusija lv: Baltkrievija mi: Pērara mk: Белорусија ml: ബെലാറുസ് mn: Беларусь mr: बेलारूस ms: Belarus mt: Belarus my: ဘီလာရုဇ်နိုင်ငံ na: Berarut ne: बेलारुस nl: Wit-Rusland "no": Belarus oc: Bielorussia or: ବେଲାଋଷ os: Белорусси pl: Białoruś ps: بېلاروس pt: Bielorrússia qu: Bilarus rm: Bielorussia ro: Belarus ru: Беларусь rw: Belarusi sa: बेलारूस se: Vilges-Ruošša sh: Belorusija si: බෙලාරස් sk: Bielorusko sl: Belorusija so: Belarus sq: Bjellorusia sr: Белорусија ss: IBhelalasi su: Bélarus sv: Belarus sw: Belarus ta: பெலருஸ் te: బెలారస్ tg: Беларус th: ประเทศเบลารุส tk: Belorussiýa tl: Biyelorusya tr: Beyaz Rusya ts: Belarus tt: Беларусия ty: Belarus ug: بېلورۇسسىيە uk: Білорусь ur: بیلاروس uz: Belarus vi: Belarus vo: Belarusän wo: Belaarus yi: בעלארוס yo: Bẹ̀lárùs zh: 白罗斯/白羅斯 zu: IBelarusi ================================================ FILE: settings/country-names/bz.yaml ================================================ name: default: Belize af: Belize ak: Beliz am: ቤሊዝ an: Belize ar: بليز ay: Wilisi az: Beliz ba: Белиз be: Белі́з bg: Белиз bm: Belizi bn: বেলিজ bo: བེ་ལི་ཛི། br: Belize bs: Beliz ca: Belize ce: Бели́з cs: Belize cv: Белиз cy: Belîs da: Belize de: Belize dv: ބެލީޒު dz: བེ་ལིཛ། ee: Belize el: Μπελίζ en: Belize eo: Belizo es: Belice et: Belize eu: Belize fa: بلیز ff: Beliise fi: Belize fo: Belis fr: Belize fy: Belize ga: an Bheilís gd: Beilise gl: Belice gn: Mbelise gu: બેલીઝ gv: yn Veleesh ha: Beliz he: בליז hi: बेलीज़ hr: Belize ht: Beliz hu: Belize hy: Բելիզ ia: Belize id: Belize ie: Belize io: Belize is: Belís it: Belize ja: ベリーズ jv: Belize ka: ბელიზი kk: Бели́з km: បេលីហ្ស kn: ಬೆಲೀಜ್ ko: 벨리즈 ks: بیلِج ku: Belîze kw: Belisa ky: Белиз la: Beliza lb: Belize li: Belize ln: Belize lo: ເບຊີວ lt: Belizas lv: Beliza mg: Belizy mk: Белизе ml: ബെലീസ് mn: Белиз mr: बेलिझ ms: Belize mt: Beliże my: ဘလိဇ် na: Berij ne: बेलिज nl: Belize "no": Belize oc: Belize om: Beliiz or: ବେଲିଜ os: Белиз pa: ਬੇਲੀਜ਼ pi: बेलीज pl: Belize ps: بېلیز pt: Belize qu: Bilisi rm: Belize ro: Belize ru: Белиз rw: Belize sa: बेलीज sc: Belize se: Belize sg: Belîzi sh: Belize si: බෙලීස් sk: Belize sl: Belize sm: Pelisi sn: Belize so: Belise sq: Beliza sr: Белизе ss: iBhelizi su: Bélis sv: Belize sw: Belize ta: பெலீசு te: బెలిజ్ tg: Белѝз th: เบลีซ ti: ቤሊዘ tl: Belise to: Pelise tr: Belize tt: Бели́з ug: بېلىز uk: Белі́з ur: بیلیز uz: Beliz vi: Belize vo: Belisän wo: Beliis yi: בעליז yo: Bẹ̀lísè za: Belize zh: 伯利兹 zu: Belize ================================================ FILE: settings/country-names/ca.yaml ================================================ name: default: Canada ab: Канада af: Kanada am: ካናዳ an: Canadá ar: كندا as: কানাডা av: Канада ay: Kanada az: Kanada ba: Канада be: Канада bg: Канада bi: Kanada bm: Kanada bn: কানাডা bo: ཁ་ན་ཌ། br: Kanada bs: Kanada ca: Canadà ce: Канада co: Canadà cs: Kanada cu: Канада cv: Канада cy: Canada da: Canada de: Kanada dv: ކެނެޑާ dz: ཀེ་ན་ཌ་ ee: Canada el: Καναδάς en: Canada eo: Kanado es: Canadá et: Kanada eu: Kanada fa: کانادا fi: Kanada fo: Kanada fr: Canada fy: Kanada ga: Ceanada gd: Canada gl: Canadá gn: Kanatã gu: કેનેડા gv: Yn Chanadey ha: Kanada he: קנדה hi: कनाडा hr: Kanada ht: Kanada hu: Kanada hy: Կանադա ia: Canada id: Kanada ie: Canada ig: Kánada ik: Kanada io: Kanada is: Kanada it: Canada iu: ᑲᓇᑕ ja: カナダ jv: Kanada ka: კანადა ki: Canada kk: Канада kl: Canada km: កាណាដា kn: ಕೆನಡಾ ko: 캐나다 ku: Kanada kv: Канада kw: Kanada ky: Канада la: Canada lb: Kanada li: Canada ln: Kanadá lt: Kanada lv: Kanāda mg: Kanada mi: Kānata mk: Канада ml: കാനഡ mn: Канад mr: कॅनडा ms: Kanada mt: Kanada my: ကနေဒါနိုင်ငံ na: Kanada ne: क्यानाडा nl: Canada "no": Canada nv: Deeteel Bikéyah oc: Canadà om: Canada or: କାନାଡ଼ା os: Канадæ pa: ਕੈਨੇਡਾ pl: Kanada ps: کاناډا pt: Canadá qu: Kanada rm: Canada rn: Kanada ro: Canada ru: Канада rw: Kanada sa: केनडा sc: Canada se: Kanáda sg: Kanadäa sh: Kanada sk: Kanada sl: Kanada sn: Canada so: Kanada sq: Kanadaja sr: Канада ss: IKhanada su: Kanada sv: Kanada sw: Kanada ta: கனடா te: కెనడా tg: Канада th: ประเทศแคนาดา tk: Kanada tl: Kanada tr: Kanada ts: Canada tt: Канада tw: Kanada ty: Tanata ug: كانادا uk: Канада ur: کینیڈا uz: Kanada vi: Canada vo: Kanadän wa: Canada wo: Kanadaa yi: קאנאדע yo: Kánádà zh: 加拿大 zu: IKhanada ================================================ FILE: settings/country-names/cd.yaml ================================================ name: default: République démocratique du Congo af: Demokratiese Republiek van die Kongo am: ኮንጎ ዲሞክራሲያዊ ሪፐብሊክ an: Republica Democratica d'o Congo ar: جمهورية الكونغو الديمقراطية az: Konqo Demokratik Respublikası be: Дэмакратычная Рэспубліка Конга bg: Демократична република Конго bm: Kongo ka Bɛjɛfanga Fasojamana bn: গণতান্ত্রিক কঙ্গো প্রজাতন্ত্র bo: ཀོང་གོ་མི་དམངས་དམངས་གཙོ། br: Republik Demokratel Kongo bs: Demokratska Republika Kongo ca: República Democràtica del Congo ce: Халкъа куьйгаллийца йолу Пачхьалкх Конго cs: Demokratická republika Kongo cv: Конго Демократиллĕ Республики cy: Gweriniaeth Ddemocrataidd Congo da: Demokratiske Republik Congo de: Demokratische Republik Kongo dv: ކޮންގޯ (ދިމިޤްރާޠީ ޖުމްހޫރިއްޔާ) el: Λαϊκή Δημοκρατία του Κονγκό en: Democratic Republic of the Congo eo: Demokratia Respubliko Kongo es: República Democrática del Congo et: Kongo Demokraatlik Vabariik eu: Kongoko Errepublika Demokratikoa fa: جمهوری دموکراتیک کنگو fi: Kongon demokraattinen tasavalta fo: Fólkaræðiliga Lýðveldið Kongo fr: République démocratique du Congo fy: Demokratyske Republyk Kongo ga: Poblacht Dhaonlathach an Chongó gd: Poblachd Dheamocrach na Congo gl: República Democrática do Congo gn: Tetã Jekopytyjoja Kongo gv: Pobblaght Gheynlagh ny Congo ha: Jamhuriyar dimokuradiyya Kwango he: הרפובליקה הדמוקרטית של קונגו hi: कांगो लोकतान्त्रिक गणराज्य hr: Demokratska Republika Kongo hu: Kongói Demokratikus Köztársaság hy: Կոնգոյի Դեմոկրատական Հանրապետություն ia: Republica Democratic del Congo id: Republik Demokratik Kongo ie: Democratic Republic de Congo io: Demokratial Republiko Kongo is: Lýðræðislega Lýðveldið Kongó it: Repubblica Democratica del Congo ja: コンゴ民主共和国 jv: Republik Dhémokratis Kongo ka: კონგოს დემოკრატიული რესპუბლიკა kg: Repubilika ya Kongo ya Dimokalasi kk: Конго Демократиялық Республикасы kn: ಕಾಂಗೋ ಪ್ರಜಾಸತ್ತಾತ್ಮಕ ಗಣರಾಜ್ಯ ko: 콩고 민주 공화국 ku: Kongoya Demokratîk kw: Repoblek Werinel Kongo la: Respublica Democratica Congensis lb: Demokratesch Republik Kongo li: Democratische Rippebliek vaan de Kongo ln: Republiki ya Kongó Demokratiki lt: Kongo Demokratinė Respublika lv: Kongo Demokrātiskā Republika mg: Repoblika Demokratikan'i Kongo mi: Te Whenua o Kōngo mk: Демократска Република Конго ml: ഡെമോക്രാറ്റിക് റിപബ്ലിക്ക് ഓഫ് കോംഗോ mn: Бүгд Найрамдах Ардчилсан Конго Улс mr: काँगोचे लोकशाही प्रजासत्ताक ms: Republik Demokratik Congo my: ကွန်ဂိုဒီမိုကရက်တစ်သမ္မတနိုင်ငံ na: Ripubrikit Engame Kongo ne: प्रजातान्त्रिक गणतन्त्र कंगो nl: Democratische Republiek Congo "no": Den demokratiske republikken Kongo nv: Kéyah Káango Shádiʼááhjí Siʼánígíí oc: Republica Democratica de Còngo or: କଙ୍ଗୋ os: Конгойы Демократон Республикæ pa: ਕਾਂਗੋ ਲੋਕਤੰਤਰੀ ਗਣਰਾਜ pl: Demokratyczna Republika Konga pt: República Democrática do Congo qu: Kungu Runakamaq Republika ro: Republica Democrată Congo ru: Демократическая Республика Конго rw: Repubulika Iharanira Demokarasi ya Kongo sc: Repùbrica Democràtiga de su Congo se: Kongo demokráhtalaš dásseváldi sg: Kùodùorùosêse tî Ngunuhalëzo tî Kongö sh: Demokratska Republika Kongo si: කොංගෝ ප්රජාතන්ත්රවාදී ජනරජය sk: Kongo (býv. Zair) sl: Demokratična republika Kongo sn: Democratic Republic of the Congo so: Jamhuuriyada Dimuqaraadiga Kongo sq: Republika Demokratike e Kongos sr: Демократска Република Конго ss: IKhongo su: Républik Démokratik Kongo sv: Demokratiska Republiken Kongo sw: Jamhuri ya Kidemokrasia ya Kongo ta: காங்கோ மக்களாட்சிக் குடியரசு th: สาธารณรัฐประชาธิปไตยคองโก tk: Kongo Demokratik Respublikasy tl: Demokratikong Republika ng Konggo tr: Demokratik Kongo Cumhuriyeti ts: Democratic Republic of the Congo tt: Конго Демократик Җөмһүрияте ug: كونگو دېموكراتىك جۇمھۇرىيىتى uk: Демократична Республіка Конго ur: جمہوری جمہوریہ کانگو uz: Kongo DR vi: Cộng hòa Dân chủ Congo vo: Kongoän (Repüblikän Demokratik) wa: Republike democratike do Congo wo: Republik Demokaraatik bu Kongóo yi: דעמאקראטישע רעפובליק פון קאנגא yo: Orílẹ̀-èdè Olómìnira Olóṣèlú ilẹ̀ Kóngò za: Ganggoj Minzcuj Gunghozgoz zh: 刚果民主共和国 zu: IRiphabliki Labantu weKongo ================================================ FILE: settings/country-names/cf.yaml ================================================ name: default: Ködörösêse tî Bêafrîka - République Centrafricaine af: Sentraal-Afrikaanse Republiek am: የመካከለኛው አፍሪካ ሪፐብሊክ an: Republica Centroafricana ar: جمهورية أفريقيا الوسطى az: Mərkəzi Afrika Respublikası be: Цэнтральна-Афрыканская Рэспубліка bg: Централноафриканска република bm: Cema Afrika Fasojamana bn: মধ্য আফ্রিকান প্রজাতন্ত্র bo: དབུས་ཨ་ཧྥེ་རི་ཁན་རི་པཔ་ལིཀ། br: Republik Kreizafrikan bs: Centralnoafrička Republika ca: República Centreafricana ce: Юккъерчу Африкин Республика cs: Středoafrická republika cy: Gweriniaeth Canolbarth Affrica da: Centralafrikanske Republik de: Zentralafrikanische Republik dv: މެދުތެރޭ އެފްރިކާގެ ޖުމްހޫރިއްޔާ ee: Central African Republic el: Κεντροαφρικανική Δημοκρατία en: Central African Republic eo: Centr-Afriko es: República Centroafricana et: Kesk-Aafrika Vabariik eu: Afrika Erdiko Errepublika fa: جمهوری آفریقای مرکزی fi: Keski-Afrikan tasavalta fo: Miðafrikalýðveldið fr: République Centrafricaine fy: Sintraal-Afrikaanske Republyk ga: Poblacht na hAfraice Láir gd: Poblachd Meadhan Afraga gl: República Centroafricana gv: Pobblaght yn Affrick Veanagh he: הרפובליקה המרכז-אפריקאית hi: मध्य अफ़्रीकी गणराज्य hr: Srednjoafrička Republika ht: Repiblik santafrik hu: Közép-afrikai Köztársaság hy: Կենտրոնաաֆրիկյան Հանրապետություն ia: Republica Centroafrican id: Republik Afrika Tengah ie: Central African Republic io: Centrafrika is: Mið-Afríkulýðveldið it: Repubblica Centrafricana ja: 中央アフリカ共和国 jv: Republik Afrika Tengah ka: ცენტრალური აფრიკის რესპუბლიკა kg: Repubilika ya Afelika ya Kati kk: Орталық Африка Республикасы kn: ಮಧ್ಯ ಆಫ್ರಿಕಾದ ಗಣರಾಜ್ಯ ko: 중앙아프리카 공화국 ks: مرکٔزی اَفریٖکی جموٗریَت ku: Komara Afrîkaya Navend kw: Centrafrika la: Respublica Africae Mediae lb: Zentralafrikanesch Republik li: Centraal Afrika ln: Santrafríka lt: Centrinės Afrikos Respublika lv: Centrālāfrikas Republika mi: Te Puku o Āwherika mk: Централноафриканска Република ml: മദ്ധ്യ ആഫ്രിക്കൻ റിപ്പബ്ലിക്ക് mn: Төв Африкийн Бүгд Найрамдах Улс mr: मध्य आफ्रिकेचे प्रजासत्ताक ms: Republik Afrika Tengah mt: Repubblika Ċentru-Afrikana my: ဗဟိုအာဖရိကသမ္မတနိုင်ငံ na: Ripubrikin Aprika Yugaga nb: Den sentralafrikanske republikk ne: मध्य अफ्रिकी गणतन्त्र nl: Centraal-Afrikaanse Republiek nn: Den sentralafrikanske republikken "no": Den sentralafrikanske republikk nv: Naakaii Łizhinii Bikéyah Beʼałnííʼ oc: Centreafrica or: ସେଣ୍ଟରାଲ ଆଫ୍ରିକାନ ରିପବ୍ଲିକ os: Централон Африкæйы Республикæ pa: ਮੱਧ ਅਫ਼ਰੀਕੀ ਗਣਰਾਜ pl: Republika Środkowoafrykańska ps: د منځنی افريقا ولسمشريزه pt: República da África Central qu: Chawpi Aphrika Republika ro: Republica Centrafricană ru: Центральноафриканская Республика rw: Repubulika ya Santara Afurika sa: केन्द्रीय अफ्रीका गणराज्यम् sc: Tzentràfrica se: Gaska-Afrihká dásseváldi sg: Ködörösêse tî Bêafrîka sh: Srednjoafrička Republika sk: Stredoafrická republika sl: Srednjeafriška republika sn: Central African Republic so: Jamhuuriyadda Bartamaha Afrika sq: Republika e Afrikës Qendrore sr: Централноафричка Република ss: Umkhatsi we-Afrikha su: Républik Afrika Tengah sv: Centralafrikanska republiken sw: Jamhuri ya Afrika ya Kati ta: மத்திய ஆப்பிரிக்கக் குடியரசு te: సెంట్రల్ ఆఫ్రికన్ రిపబ్లిక్ tg: Ҷумҳурии Африқои Марказӣ th: สาธารณรัฐแอฟริกากลาง tk: Merkezi Afrika Respublikasy tl: Republika ng Gitnang Aprika tr: Orta Afrika Cumhuriyeti ts: Afrika Wale Xikarhi tt: Үзәк Африка Җөмһүрияте ug: ئوتتۇرا ئافرىقا جۇمھۇرىيىتى uk: Центральноафриканська Республіка ur: وسطی افریقی جمہوریہ uz: Markaziy Afrika Respublikasi vi: Cộng hòa Trung Phi vo: Zänoda-Frikop wo: Réewum Diggu Afrig yo: Orílẹ̀-èdè Olómìnira Àrin Áfríkà zh: 中非共和國 zu: Central African Republic ================================================ FILE: settings/country-names/cg.yaml ================================================ name: default: Congo af: Republiek van die Kongo am: ኮንጎ ሪፐብሊክ an: Republica d'o Congo ar: جمهورية الكونغو az: Konqo Respublikası ba: Республика Конго be: Рэспубліка Конга bg: Република Конго bm: Kongo-Brazaville bn: কঙ্গো প্রজাতন্ত্র bo: ཀོང་གོ་སྤྱི་མཐུན་རྒྱལ་ཁབ། br: Republik Kongo bs: Republika Kongo ca: República del Congo ce: Республика Конго cs: Republika Kongo cv: Конго Республики cy: Gweriniaeth y Congo da: Republikken Congo de: Republik Kongo dv: ކޮންގޯ (ޖުމްހޫރިއްޔާ) el: Δημοκρατία του Κονγκό en: Congo-Brazzaville eo: Respubliko Kongo es: República del Congo et: Kongo Vabariik eu: Kongoko Errepublika fa: جمهوری کنگو fi: Kongon tasavalta fr: Congo-Brazzaville fy: Republyk Kongo ga: Poblacht an Chongó gd: A' Chongo gl: Congo gv: Pobblaght ny Congo he: הרפובליקה של קונגו hi: कांगो गणराज्य hr: Republika Kongo ht: Kongo (Brazavil) hu: Kongói Köztársaság hy: Կոնգոյի Հանրապետություն ia: Republica del Congo id: Republik Kongo ie: Republic de Congo io: Republiko Kongo is: Vestur-Kongó it: Repubblica del Congo ja: コンゴ共和国 jv: Republik Kongo ka: კონგოს რესპუბლიკა kg: Repubilika ya Kôngo kk: Конго Республикасы ko: 콩고 공화국 ku: Komara Kongoyê kw: Repoblek Kongo la: Respublica Congensis lb: Republik Kongo li: Kongo-Brazzaville ln: Kongó-Brazzaville lt: Kongo Respublika lv: Kongo Republika mk: Република Конго ml: റിപ്പബ്ലിക്ക് ഓഫ് കോംഗോ mn: Бүгд Найрамдах Конго Улс mr: काँगोचे प्रजासत्ताक ms: Republik Congo my: ကွန်ဂိုသမ္မတနိုင်ငံ na: Ripubrikin Kongo ne: कङ्गो nl: Congo-Brazzaville "no": Republikken Kongo nv: Kéyah Káango Náhookǫsjí Siʼánígíí oc: Republica de Còngo os: Конгойы Республикæ pa: ਕਾਂਗੋ ਗਣਰਾਜ pl: Kongo ps: د کانګو جمهوريت pt: República do Congo qu: Kungu Republika ro: Republica Congo ru: Республика Конго rw: Kongo sc: Repùbrica de su Congo se: Kongo dásseváldi sg: Ködörösêse tî Kongöo sh: Republika Kongo si: කොංගෝ ජනරජය sk: Kongo (Brazzaville) sl: Republika Kongo sn: Republic of the Congo so: Jamhuuriyadda Kongo sq: Republika e Kongos sr: Република Конго ss: Tibuse weKhongo su: Républik Kongo sv: Republiken Kongo sw: Jamhuri ya Kongo ta: கொங்கோ குடியரசு te: కాంగో రిపబ్లిక్ tg: Ҷумҳӯрии Конго th: สาธารณรัฐคองโก tk: Kongo Respublikasy tl: Republika ng Konggo tr: Kongo Cumhuriyeti ts: Riphabliki ya Congo tt: Конго Җөмһүрияте ug: كونگو جۇمھۇرىيىتى uk: Республіка Конго ur: جمہوریہ کانگو uz: Kongo Respublikasi vi: Cộng hòa Congo vo: Kongoän (Repüblikän) wo: Kongóo-Brasaawiil yi: רעפובליק פון קאנגא yo: Orílẹ̀-èdè Olómìnira ilẹ̀ Kóngò zh: 刚果共和国 zu: IKongo ================================================ FILE: settings/country-names/ch.yaml ================================================ name: default: Schweiz/Suisse/Svizzera/Svizra af: Switserland am: ስዊዘርላንድ an: Suiza ar: سويسرا az: İsveçrə ba: Швейцария be: Швейцарыя bg: Швейцария bi: Switzerland bn: Switzerland bo: སུད་སི། br: Suis bs: Švicarska ca: Suïssa ce: Швейцари co: Svizzera cs: Švýcarsko cu: Свицєра cv: Швейцари cy: Y Swistir da: Schweiz de: Schweiz dv: ސުވިޒަލޭންޑު dz: སུའིཊ་ཛར་ལེན་ ee: Switzerland el: Ελβετία en: Switzerland eo: Svislando es: Suiza et: Šveits eu: Suitza fa: سوئیس fi: Sveitsi fo: Sveis fr: Suisse fy: Switserlân ga: An Eilbhéis gd: An Eilbheis gl: Suíza gn: Suisa gu: સ્વિત્ઝરલેન્ડ gv: Yn Elveeish he: שוויץ hi: स्विट्ज़रलैंड hr: Švicarska ht: Swis hu: Svájc hy: Շվեյցարիա ia: Suissa id: Swiss ie: Svissia io: Suisia is: Sviss it: Svizzera ja: スイス jv: Swiss ka: შვეიცარია kg: Suisi kk: Швейцария kl: Schweizi kn: ಸ್ವಿಟ್ಜರ್ಲ್ಯಾಂಡ್ ko: 스위스 ku: Swîsre kv: Швейцария kw: Swistir ky: Швейцария la: Helvetia lb: Schwäiz li: Zweitserland ln: Swisi lt: Šveicarija lv: Šveice mg: Soisa mi: Huiterangi mk: Швајцарија ml: സ്വിറ്റ്സര്ലാന്റ് mn: Швейцари mr: स्वित्झर्लंड ms: Switzerland mt: Żvizzera my: ဆွစ်ဇာလန်နိုင်ငံ na: Switzerland ne: स्विजरल्याण्ड nl: Zwitserland "no": Sveits nv: Swis Bikéyah oc: Soïssa or: ସୁଇଜରଲ୍ୟାଣ୍ଡ os: Швейцари pa: ਸਵਿਟਜ਼ਰਲੈਂਡ pl: Szwajcaria ps: سويس pt: Suíça qu: Suwisa rm: Svizra ro: Elveţia ru: Швейцария rw: Ubusuwisi sa: स्विटजरलैंड sc: Isvìtzera se: Šveica sh: Švajcarska si: ස්විට්සර්ලන්තය sk: Švajčiarsko sl: Švica so: Iswiizerlaan sq: Zvicra sr: Швајцарска ss: ISwizalandi su: Suis sv: Schweiz sw: Uswisi ta: சுவிட்சர்லாந்து te: స్విట్జర్లాండ్ tg: Швейтсария th: ประเทศสวิตเซอร์แลนด์ tk: Şweýsariýa tl: Suwisa tr: İsviçre tt: Швейцария ug: Shwétsariye uk: Швейцарія ur: سویٹزر لینڈ uz: Shveysariya vi: Thụy Sĩ vo: Jveizän wo: Suwis yi: שווייץ yo: Swítsàlandì za: Nyeiqswq zh: 瑞士 ================================================ FILE: settings/country-names/ci.yaml ================================================ name: default: Côte d’Ivoire af: Ivoorkus am: ኮት ዲቯር an: Costa de Vori ar: ساحل العاج az: Fil Dişi Sahili be: Кот-д'Івуар bg: Кот д'Ивоар bm: Côte d'Ivoire bn: কোত দিভোয়ার bo: ཀོ་ཊི་ཌི་ཨའི་བོ་རི། br: Aod an Olifant bs: Obala Slonovače ca: Costa d'Ivori ce: Кот-д’Ивуар cs: Pobřeží slonoviny cv: Кот-д'Ивуар cy: Côte d'Ivoire da: Elfenbenskysten de: Elfenbeinküste dv: އައިވަރީ ކޯސްޓު ee: Côte d'Ivoire el: Ακτή Ελεφαντοστού en: Côte d'Ivoire eo: Ebur-Bordo es: Costa de Marfil et: Elevandiluurannik eu: Boli Kosta fa: ساحل عاج ff: Kodduwaar fi: Norsunluurannikko fo: Fílabeinsstrondin fr: Côte d’Ivoire fy: Ivoarkust ga: An Cósta Eabhair gd: Costa Ìbhri gl: Costa do Marfil gv: Yn Clyst Iuaagagh he: חוף השנהב hi: कोत द'ईवोआर hr: Obala Bjelokosti ht: Kòt divwa hu: Elefántcsontpart hy: Կոտ դ'Իվուար ia: Costa de Ebore id: Pantai Gading ie: Costa de Ivor io: Ivora Rivo is: Fílabeinsströndin it: Costa d'Avorio ja: コートジボワール jv: Panté Gadhing ka: კოტ-დ’ივუარი kg: Côte d'Ivoire ki: Côte d'Ivoire kk: Кот-д-Ивуар kn: ಕೋತ್ ದ್'ಇವಾರ್ ko: 코트디부아르 ku: Peravê Diranfîl kw: Côte d'Ivoire la: Litus Eburneum lb: Elfebeeküst li: Ivoorkös ln: Kotdivuar lt: Dramblio Kaulo Krantas lv: Kotdivuāra mg: Côte d'Ivoire mi: Te Tai Rei mk: Брег на Слоновата Коска ml: ഐവറി കോസ്റ്റ് mn: Кот д'Ивуар mr: कोत द'ईवोआर ms: Ivory Coast mt: Kosta tal-Avorju my: အိုင်ဗရီကို့စ်နိုင်ငံ na: Aibori Kot nb: Elfenbenskysten ne: आइभरी कोस्ट nl: Ivoorkust nn: Elfenbeinskysten "no": Elfenbenskysten nv: Chį́į́h Yee Adilohii Bideeʼ Tónteel Bibąąh oc: Còsta d'Evòri or: ଆଇଭେରି କୋଷ୍ଟ os: Кот-д’Ивуар pa: ਦੰਦ ਖੰਡ ਤਟ pl: Wybrzeże Kości Słoniowej ps: عاج ساحل pt: Costa do Marfim qu: Marphil Chala rm: Costa d'Ivur ro: Coasta de Fildeș ru: Кот-д’Ивуар rw: Kote Divuwari sa: कोट ऐवरी sc: Costa de Avòriu se: Elefántačalánriddu sg: Kôdivüära sh: Obala Slonovače si: අයිවරි කෝස්ට් sk: Pobrežie Slonoviny sl: Slonokoščena obala sn: Côte d'Ivoire so: Xeebta Foolmaroodi sq: Bregu i Fildishtë sr: Обала Слоноваче ss: Lugu Lwempondvondlovu su: Basisir Gading sv: Elfenbenskusten sw: Cote d'Ivoire ta: கோட் டிவார் te: కోటె డి ఐవొరి tg: Кот-д'Ивуар th: ประเทศโกตดิวัวร์ tk: Kot-d’Iwuar tl: Baybaying Garing tr: Fildişi Sahili ts: Côte d'Ivoire tt: Кот-д’Ивуар ug: پىل چىشى قىرغىغى uk: Кот-д'Івуар ur: کوت داوواغ uz: Kot-d‘ivuar vi: Bờ Biển Ngà vo: Viorajolän wo: Kodiwaar yi: בארטן פון העלפאנדביין yo: Côte d'Ivoire zh: 科特迪瓦 zu: Ugu Emhlophe ================================================ FILE: settings/country-names/ck.yaml ================================================ name: default: Kūki 'Āirani af: Cookeilande ak: Kook Nsupɔw am: ኩክ ደሴቶች an: islas Cook ar: جزر كوك az: Kuk adaları ba: Кук Утрауҙары be: Астравы Кука bg: Острови Кук bm: Kuki Gun bn: কুক দ্বীপপুঞ্জ bo: ཀཱུག གླིང་ཕྲེན་རྒྱལ་ཁབ། br: Inizi Cook bs: Cookova Ostrva ca: Illes Cook cs: Cookovy ostrovy cy: Ynysoedd Cook da: Cook-øerne de: Cookinseln dv: ކުކް ޖަޒީރާ dz: ཀུག་གླིང་ཚོམ ee: Kook ƒudomekpo nutome el: Νήσοι Κουκ en: Cook Islands eo: Kukinsuloj es: Islas Cook et: Cooki saared eu: Cook uharteak fa: جزایر کوک ff: Duuɗe Kuuk fi: Cookinsaaret fo: Cooksoyggjarnar fr: Îles Cook fy: de Cookeilannen ga: Oileáin Cook gd: Eileanan Cook gl: Illas Cook gu: કુક આઇલેન્ડ્સ ha: Tsibiran Kuku he: איי קוּק hi: कुक द्वीपसमूह hr: Cookovo Otočje hu: Cook-szigetek hy: Կուկի կղզիներ ia: Insulas Cook id: Kepulauan Cook io: Insuli Cook is: Cooks-eyjar it: Isole Cook ja: クック諸島 jv: Kapuloan Cook ka: კუკის კუნძულები ki: Visiwa vya Cook kk: Кук аралдары kl: Cook qeqertaq km: កោះ​ខូក kn: ಕುಕ್ ದ್ವೀಪಗಳು ko: 쿡 제도 ks: کُک جٔزیٖرٕ ku: Giravên Cook kw: Ynysow Cook ky: Кук аралдары la: Insulae de Cook lb: Cookinseln lg: Bizinga bya Kkuki li: Cookeilenj ln: Bisanga bya Kookɛ lo: ຫມູ່ເກາະຄຸກ lt: Kuko Salos lv: Kuka Salas mg: Nosy Kook mi: Kuki Airani mk: Кукови Острови ml: കൂക്ക് ദ്വീപുകൾ mn: Күүкийн Арлууд mr: कूक द्वीपसमूह ms: Kepulauan Cook mt: Gżejjer Cook my: ကွတ် ကျွန်းစု nb: Cookøyene ne: कुक टापु nl: Cookeilanden nn: Cookøyane "no": Cookøyene oc: Illas Cook or: କୁକ୍ ଆଇସଲ୍ୟାଣ୍ଡ os: Кучы сакъадæхтæ pa: ਕੁੱਕ ਟਾਪੂ pl: Wyspy Cooka pt: Ilhas Cook rm: Inslas Cook rn: Izinga rya Kuku ro: Insulele Cook ru: Острова Кука rw: Ibirwa bya Kuke se: Cook-sullot sg: âzûâ Kûku sh: Kukova Ostrva si: කුක් දිවයින් sk: Cookove ostrovy sl: Cookovi otoki sn: Zvitsuwa zveCook so: Jaziiradda Cook sq: Ishulli Kuk sr: Кукова Острва su: Kapuloan Cook sv: Cooköarna sw: Visiwa vya Cook ta: குக் தீவுகள் te: కుక్ దీవులు th: หมู่เกาะคุก ti: ኩክ ደሴቶች tl: Kapuluang Cook to: ʻOtumotu Kuki tr: Cook Adaları tt: Кук утраулары ug: Kuk Taqim Aralliri uk: Острови́ Ку́ка ur: جزائر کک uz: Kuk orollari vi: Quần đảo Cook wo: Dunu Kook yo: Orílẹ́ède Etíokun Kùúkù zh: 庫克群島 zu: i-Cook Islands ================================================ FILE: settings/country-names/cl.yaml ================================================ name: default: Chile ab: Чили af: Chili ak: Chile am: ቺሌ an: Chile ar: تشيلي as: চিলি av: Чили ay: Chili az: Çili ba: Чи́ли be: Чылі bg: Чили bh: चिली bi: Chile bm: Chile bn: চিলি bo: ཅི་ལི། br: Chile bs: Čile ca: Xile ce: Чили ch: Chile co: Cile cs: Chile cu: Чилє cv: Чили cy: Chile da: Chile de: Chile dv: ޗިލީ dz: ཅི་ལེ ee: Chile el: Χιλή en: Chile eo: Ĉilio es: Chile et: Tšiili eu: Txile fa: شیلی ff: Ciile fi: Chile fj: Chile fo: Kili fr: Chili fy: Sily ga: an tSile gd: an t-Sile gl: Chile gn: Chile gu: ચીલી gv: yn Çhillee ha: Chile he: צ'ילה hi: चिली hr: Čile ht: Chili hu: Chile hy: Չիլի ia: Chile id: Chili ie: Chile ig: Chile ik: Cili io: Chili is: Síle it: Cile iu: ᓯᓕ ja: チリ jv: Chili ka: ჩილე kg: Chile ki: Chile kk: Чили kl: Chile km: ឈីលី kn: ಚಿಲಿ ko: 칠레 ks: چِلی ku: Şîle kv: Чили kw: Chile ky: Чили la: Chilia lb: Chile lg: Chile li: Chili ln: Shili lo: ປະເທດຈີເລ lt: Čilė lv: Čīle mg: Silia mi: Hiri mk: Чиле ml: ചിലി mn: Чили mr: चिली ms: Chile mt: Ċili my: ချီလီနိုင်ငံ na: Tsire ne: चिली nl: Chili "no": Chile nv: Chíilii ny: Chile oc: Chile om: Chiilii or: ଚିଲି os: Чили pa: ਚਿਲੀ pi: चिले pl: Chile ps: چېلي pt: Chile qu: Chili rm: Chile rn: Chile ro: Chile ru: Чили rw: Shili sa: चिले sc: Cile sd: چلي se: Chile sg: Shilïi sh: Čile si: චිලි sk: Čile sl: Čile sm: Shili sn: Chile so: Jili sq: Kilia sr: Чиле ss: iShile st: Chile su: Cilé sv: Chile sw: Chile ta: சிலி te: చిలీ tg: Чиле th: ชิลี ti: ቺሌ tk: Çili tl: Chile tn: Chile to: Sile tr: Şili ts: Chile tt: Чи́ли tw: Kyili ty: Tīri ug: چىلى uk: Чи́лі ur: چلی uz: Chili ve: Shile vi: Chi-lê vo: Cilän wa: Tchili wo: Ciili xh: iChile yi: טשילע yo: Tsílè za: Chile zh: 智利 zu: ITshile ================================================ FILE: settings/country-names/cm.yaml ================================================ name: default: Cameroun af: Kameroen am: ካሜሩን an: Camerún ar: الكاميرون az: Kamerun ba: Камерун be: Камерун bg: Камерун bm: Kamerun bn: ক্যামেরুন bo: ཁམ་མུ་རོན། br: Kameroun bs: Kamerun ca: Camerun cs: Kamerun cv: Камерун cy: Camerŵn da: Cameroun de: Kamerun dv: ކެމަރޫން ee: Cameroon el: Καμερούν en: Cameroon eo: Kameruno es: Camerún et: Kamerun eu: Kamerun fa: کامرون ff: Kamerun fi: Kamerun fo: Kamerun fr: Cameroun fy: Kameroen ga: Camarún gd: Camarun gl: Camerún gv: Cameroon ha: Kameru he: קמרון hi: कैमरुन hr: Kamerun ht: Kamewoun hu: Kamerun hy: Կամերուն ia: Camerun id: Kamerun ie: Cameroon ig: Kameroon io: Kamerun is: Kamerún it: Camerun ja: カメルーン jv: Kamerun ka: კამერუნი kg: Kamerun ki: Cameroon kk: Камерун kn: ಕ್ಯಾಮರೂನ್ ko: 카메룬 ks: کیٚمِروٗن ku: Kamerûn kw: Kameroun la: Cameronia lb: Kamerun li: Kameroen ln: Kamerun lt: Kamerūnas lv: Kamerūna mi: Kamarūna mk: Камерун ml: കാമറൂൺ mn: Камерун mr: कामेरून ms: Cameroon mt: Kamerun my: ကင်မရွန်းနိုင်ငံ na: Kamerun ne: क्यामेरून nl: Kameroen "no": Kamerun nv: Táłtłʼááh Chʼosh Daadánígíí Bitooh oc: Cameron or: କାମେରୁନ os: Камерун pa: ਕੈਮਰੂਨ pl: Kamerun ps: کامرون pt: Camarões qu: Kamirun rm: Camerun ro: Camerun ru: Камерун rw: Kameruni sa: केमेरून sc: Camerùn se: Kamerun sg: Kamerün sh: Kamerun sk: Kamerun sl: Kamerun sn: Cameroon so: Kamiruun sq: Kameruni sr: Камерун ss: IKhameruni su: Kamerun sv: Kamerun sw: Kamerun ta: கமரூன் te: కామెరూన్ tg: Камерун th: ประเทศแคเมอรูน tk: Kamerun tl: Kamerun tr: Kamerun ts: Cameroon tt: Камерун ug: كامېرۇن uk: Камерун ur: کیمرون uz: Kamerun vi: Cameroon vo: Kamerunän wo: Kamerun yi: קאמערון yo: Kamẹrúùn zh: 喀麦隆 zu: IKamerooni ================================================ FILE: settings/country-names/cn.yaml ================================================ name: default: 中国 ab: Чынҭ af: China ak: China am: የቻይና an: China ar: الصين as: চীন av: Чин ay: China az: Çin ba: Ҡытай be: Кітай bg: Китай bh: चीन bi: Jaena bm: China bn: গণচীন bo: ཀྲུང་གོ། br: Sina bs: Kina ca: Xina ce: Цийн-мохк ch: China co: China cs: Čína cu: Срѣдинꙗнє cv: Китай cy: Tsieina da: Kina de: China dv: ސީނުކަރަ dz: རྒྱ་ནག ee: China el: Κίνα en: China eo: Ĉinio es: China et: Hiina eu: Txina fa: چین ff: Ciina fi: Kiina fj: Jaina fo: Kina fr: Chine fy: Sina ga: Síne gd: Sìona gl: China gn: Chína gu: ચીન gv: Sheen ha: Sin he: סין hi: चीनी hr: Kina ht: Chin hu: Kína hy: Չինաստան ia: China id: Tiongkok ie: China ig: Chaina ik: China io: Chinia is: Kína it: Cina iu: ᓴᐃᓇ ja: 中国 jv: Cina ka: ჩინეთი kg: Sina ki: China kk: Қытай kl: Kina km: ចិន kn: ಚೀನಿ ko: 중국 ks: چیٖن ku: Çîn kv: Китай kw: China ky: Кытай la: Sinae lb: China lg: Cayina li: China ln: Sína lo: ປະເທດຈີນ lt: Kinija lv: Ķīna mg: Sina mi: Haina mk: Кина ml: ചീന mn: Хятад mr: चीन ms: China mt: Ċina my: တရုတ် na: Tsiene ne: चीन nl: China "no": Kina nv: Tsiiʼyishbizhí Dineʼé Bikéyah ny: China oc: China om: Chaayinaan or: ଚୀନ os: Китай pa: ਚੀਨ pl: Chiny ps: چين pt: China qu: Chunkuk rm: China rn: Ubushinwa ro: China ru: Китай rw: Ubushinwa sc: Cina sd: چين se: Kiinná sg: Sînä sh: Kina si: චීනය sk: Čína sl: Kitájska sm: Saina sn: China so: Shiinaha sq: Kina sr: Кина ss: iShayina st: Tjhaena su: Tiongkok sv: Kina sw: China ta: சீனா te: చైనా tg: Хито́й th: ประเทศจีน ti: የቻይና tk: Hytaý tl: Tsina tn: China to: Siaina tr: Çin ts: Chayina tt: Кытай tw: China ty: Tinitō ug: جۇڭخۇا خەلق جۇمھۇرىيىتى uk: Кита́йська ur: چین uz: Xitoy ve: China vi: Trung Quốc vo: Tsyinän wo: Siin xh: IShayina yi: כינע yo: Ṣáínà za: Cunghgoz zh: 中国 zu: IShayina ================================================ FILE: settings/country-names/co.yaml ================================================ name: default: Colombia af: Colombia am: ኮሎምቢያ an: Colombia ar: كولومبيا ay: Kuluwya az: Kolumbiya ba: Колумбия be: Калумбія bg: Колумбия bi: Colombia bn: কলম্বিয়া bo: ཁོ་ལོམ་བི་ཡ། br: Kolombia bs: Kolumbija ca: Colòmbia ce: Колумби co: Colombia cs: Kolumbie cv: Колумби cy: Colombia da: Colombia de: Kolumbien dv: ކޮލަންބިއާ dz: ཀོ་ལོམ་བི་ཡ ee: Colombia el: Κολομβία en: Colombia eo: Kolombio es: Colombia et: Colombia eu: Kolonbia fa: کلمبیا fi: Kolumbia fo: Kolumbia fr: Colombie fy: Kolombia ga: An Cholóim gd: Coloimbia gl: Colombia gn: Kolómbia gu: કોલમ્બિયા gv: Yn Cholombey he: קולומביה hi: कोलम्बिया hr: Kolumbija ht: Kolonbi hu: Kolumbia hy: Կոլումբիա ia: Colombia id: Kolombia ie: Columbia io: Kolumbia is: Kólumbía it: Colombia ja: コロンビア jv: Kolombia ka: კოლუმბია kk: Колумбия Республикасы kl: Colombia km: កូឡុំប៊ី kn: ಕೊಲೊಂಬಿಯ ko: 콜롬비아 ku: Kolombiya kw: Kolombi la: Columbia lb: Kolumbien li: Colombia ln: Kolombi lt: Kolumbija lv: Kolumbija mg: Kolombia mi: Koromōpia mk: Колумбија ml: കൊളംബിയ mn: Колумб mr: कोलंबिया ms: Colombia mt: Kolombja my: ကိုလံဘီယာနိုင်ငံ na: Korombiya ne: कोलम्बिया nl: Colombia "no": Colombia nv: Kolámbiya oc: Colómbia om: Colombia or: କଲୋମ୍ବିଆ os: Колумби pa: ਕੋਲੰਬੀਆ pl: Kolumbia pt: Colômbia qu: Kulumbya rm: Columbia ro: Columbia ru: Колумбия rw: Kolombiya sa: कोलोम्बिया se: Colombia sh: Kolumbija sk: Kolumbia sl: Kolumbija so: Kolombiya sq: Kolumbia sr: Колумбија su: Kolombia sv: Colombia sw: Kolombia ta: கொலொம்பியா te: కొలంబియా tg: Кулумбия th: ประเทศโคลอมเบีย tl: Colombia tr: Kolombiya ts: Colombia tt: Колумбия ug: كولومبىيە uk: Колумбія ur: کولمبیا uz: Kolumbiya vi: Colombia vo: Kolumbän wo: Koloombi xh: IsiColombia yi: קאלאמביע yo: Kòlómbìà zh: 哥伦比亚 zu: IKolombiya ================================================ FILE: settings/country-names/cr.yaml ================================================ name: default: Costa Rica ab: Коста-Рика af: Costa Rica am: ኮስታ ሪካ an: Costa Rica ar: كوستاريكا ay: Kustarika az: Kosta-Rika ba: Коста-Рика be: Коста-Рыка bg: Коста Рика bh: कोस्टा रिका bm: Kosta Rika bn: কোস্টা রিকা bo: ཁོ་ས་ཏ་རི་ཁ br: Costa Rica bs: Kostarika ca: Costa Rica ce: Коста-Рика cs: Kostarika cy: Costa Rica da: Costa Rica de: Costa Rica dv: ކޮސްޓަރީކާ ee: Costa Rica el: Κόστα Ρίκα en: Costa Rica eo: Kostariko es: Costa Rica et: Costa Rica eu: Costa Rica fa: کاستاریکا fi: Costa Rica fo: Kosta Rika fr: Costa Rica fy: Kosta Rika ga: Cósta Ríce gd: Costa Rica gl: Costa Rica gn: Kosta Rrika gv: Yn Coose Berçhagh he: קוסטה ריקה hi: कोस्टा रीका hr: Kostarika ht: Kostarika hu: Costa Rica hy: Կոստա Ռիկա ia: Costa Rica id: Kosta Rika ie: Costa Rica io: Kosta Rika is: Kosta Ríka it: Costa Rica ja: コスタリカ jv: Kosta Rika ka: კოსტა-რიკა kk: Коста-Рика kn: ಕೋಸ್ಟಾ ರಿಕ ko: 코스타리카 ku: Kosta Rîka kv: Коста-Рика kw: Kosta Rika ky: Коста-Рика la: Ora Opulenta lb: Costa Rica li: Costa Rica ln: Kosta Rika lo: ປະເທດກົດສະຕາລິກາ lt: Kosta Rika lv: Kostarika mg: Costa Rica mi: Koto Rika mk: Костарика ml: കോസ്റ്റ റീക്ക mn: Коста-Рика mr: कोस्टा रिका ms: Costa Rica mt: Kosta Rika my: ကော့စတာရီကာနိုင်ငံ na: Kosta Rika ne: कोस्टारिका nl: Costa Rica "no": Costa Rica oc: Còsta Rica om: Kostaa Rikaa or: କୋଷ୍ଟାରିକା os: Коста-Рикæ pa: ਕੋਸਤਾ ਰੀਕਾ pi: कोस्टा रीका pl: Kostaryka ps: کوسټاریکا pt: Costa Rica qu: Kustarika ro: Costa Rica ru: Коста-Рика rw: Kosita Rika sa: कोस्टा रीका se: Costa Rica sh: Kostarika si: කෝස්ට රිකා sk: Kostarika sl: Kostarika sm: Kosta Rika so: Kosta Rika sq: Kosta Rika sr: Костарика ss: IKhosta-Likha su: Kosta Rika sv: Costa Rica sw: Kosta Rika ta: கோஸ்ட்டா ரிக்கா te: కోస్టారీకా tg: Коста Рика th: ประเทศคอสตาริกา tk: Kosta-Rika tl: Costa Rica tr: Kosta Rika tt: Коста-Рика ug: كوستارىكا uk: Коста-Рика ur: کوسٹاریکا uz: Kosta Rika vi: Costa Rica vo: Kostarikän wa: Costa Rica wo: Kosta Riika yi: קאסטא ריקא yo: Kóstá Rikà zh: 哥斯达黎加 zu: Costa Rica ================================================ FILE: settings/country-names/cu.yaml ================================================ name: default: Cuba af: Kuba am: ኩባ an: Cuba ar: كوبا ay: Kuba az: Kuba ba: Куба be: Куба bg: Куба bm: Kuba bn: কিউবা bo: ཀུ་བ། br: Kuba bs: Kuba ca: Cuba ce: Куба cs: Kuba cv: Куба cy: Cuba da: Cuba de: Kuba dv: ކިއުބާ ee: Cuba el: Κούβα en: Cuba eo: Kubo es: Cuba et: Kuuba eu: Kuba fa: کوبا fi: Kuuba fo: Kuba fr: Cuba fy: Kuba ga: Cúba gd: Cùba gl: Cuba gn: Kuva gv: Yn Choobey he: קובה hi: क्यूबा hr: Kuba ht: Kiba hu: Kuba hy: Կուբա ia: Cuba id: Kuba io: Kuba is: Kúba it: Cuba iu: ᖂᐹ ja: キューバ jv: Kuba ka: კუბა kk: Куба km: គុយបា kn: ಕ್ಯೂಬಾ ko: 쿠바 ku: Kûba kv: Куба kw: Kuba la: Cuba lb: Kuba li: Cuba ln: Kuba lt: Kuba lv: Kuba mi: Kiupa mk: Куба ml: ക്യൂബ mn: Куба mr: क्युबा ms: Cuba mt: Kuba my: ကျူးဘားနိုင်ငံ na: Kiuba ne: क्युबा nl: Cuba "no": Cuba nv: Kyóoba oc: Cuba or: କ୍ୟୁବା os: Кубæ pa: ਕਿਊਬਾ pl: Kuba pt: Cuba qu: Kuba ro: Cuba ru: Куба rw: Kiba sa: क्यूबा sc: Cuba se: Cuba sh: Kuba si: කියුබාව sk: Kuba sl: Kuba so: Kuuba sq: Kuba sr: Куба ss: IKhuyubha su: Kuba sv: Kuba sw: Kuba ta: கூபா te: క్యూబా tg: Кубо th: ประเทศคิวบา tk: Kuba tl: Kuba tr: Küba tt: Куба ug: كۇبا uk: Куба ur: کیوبا uz: Kuba vi: Cuba vo: Kubeän wa: Couba wo: Kubaa yi: קובא yo: Kúbà za: Gujbah zh: 古巴 ================================================ FILE: settings/country-names/cv.yaml ================================================ name: default: Cabo Verde af: Kaap Verde ar: الرأس الأخضر be: Каба-Вердэ br: Kab Glas ca: Cap Verd cs: Kapverdy cy: Cabo Verde da: Kap Verde de: Kap Verde el: Πράσινο Ακρωτήριο en: Cape Verde eo: Kaboverdo es: Cabo Verde et: Roheneemesaared fa: کیپ ورد fi: Kap Verde fr: Cap-Vert fy: Kaapverdje ga: Rinn Verde gd: Cape Verde he: כף ורדה hr: Zelenortski Otoci hu: Zöld-foki Köztársaság id: Tanjung Verde io: Kabo Verda is: Grænhöfðaeyjar it: Capo Verde la: Promontorium Viride lb: Kap Verde li: Kaapverdië lt: Žaliasis Kyšulys lv: Kaboverde mi: Te Kūrae Matomato mk: Зелен ’Рт mn: Кабо-Верде nl: Kaapverdië "no": Kapp Verde pl: Republika Zielonego Przylądka pt: Cabo Verde ru: Кабо-Верде se: Kap Verde sl: Zelenortski otoki sr: Зеленортска Острва sv: Kap Verde ta: கேப் வர்டி th: ประเทศเคปเวิร์ด tr: Yeşil Burun Adaları uk: Кабо-Верде uz: Kabo-Verde vo: Kabovärduäns zh: 佛得角 ================================================ FILE: settings/country-names/cy.yaml ================================================ name: default: Κύπρος - Kıbrıs af: Siprus am: ቆጵሮስ an: Chipre ar: قبرص az: Kipr be: Кіпр bg: Кипър bi: Cyprus bn: সাইপ্রাস bo: སེ་པི་རི་སི། br: Kiprenez bs: Kipar ca: Xipre ce: Кипр co: Cipru cs: Kypr cu: Кѷпръ cv: Кипр cy: Cyprus da: Cypern de: Zypern ee: Cyprus el: Κύπρος en: Cyprus eo: Kipro es: Chipre et: Küpros eu: Zipre fa: قبرس fi: Kypros fo: Kýpros fr: Chypre fy: Syprus ga: An Chipir gd: Cìopras gl: Chipre gn: Chipre gu: સાયપ્રસ gv: Yn Cheeprey he: קפריסין hi: साइप्रस hr: Cipar ht: Chip hu: Ciprus hy: Կիպրոս ia: Cypro id: Siprus ie: Cypria io: Chipro is: Kýpur it: Cipro ja: キプロス jv: Siprus ka: კვიპროსი kg: Kipros kk: Кипр kl: Cyperni kn: ಕಿಪ್ರೊಸ್ ko: 키프로스 ku: Kîpros kv: Кипр kw: Kobros la: Cyprus lb: Zypern li: Cyprus lt: Kipras lv: Kipra mg: Kipra mi: Haipara mk: Кипар ml: സൈപ്രസ് mn: Кипр mr: सायप्रस ms: Cyprus mt: Ċipru my: ဆိုက်ပရပ်စ်နိုင်ငံ na: Taiprus ne: साइप्रस nl: Cyprus "no": Kypros nv: Béésh Łichíiʼii Bikéyah oc: Chipre or: ସାଇପ୍ରସ os: Кипр pa: ਸਾਇਪ੍ਰਸ pl: Cypr pt: Chipre qu: Kipru rm: Cipra ro: Cipru ru: Кипр rw: Shipure sc: Tzipru se: Kypros sh: Cipar si: සයිප්‍රස් sk: Cyprus sl: Ciper sm: Cyprus so: Qabrus sq: Qiproja sr: Кипар ss: ISayiphro su: Siprus sv: Cypern sw: Kupro ta: சைப்பிரஸ் te: సైప్రస్ tg: Кипр th: ประเทศไซปรัส tk: Kipr tl: Tsipre tr: Kıbrıs tt: Кипр ug: سىپرۇس uk: Кіпр ur: قبرص uz: Qibris vi: Síp vo: Sipreän wo: Ciipër yi: קיפראס yo: Kíprù zh: 赛普勒斯/塞浦路斯/塞浦路斯 ================================================ FILE: settings/country-names/cz.yaml ================================================ name: default: Česko ab: Чехиа af: Tsjeggië am: ቼክ ሪፐብሊክ an: Republica Checa ar: التشيك av: Чехия ay: Chexya Ripublika az: Çexiya ba: Чехия be: Чэхія bg: Чешка република bh: चेक रिपब्लिक bi: Czech Republic bn: চেক প্রজাতন্ত্র bo: ཅེ་ཁེ། br: Tchekia bs: Češka ca: Txèquia ce: Чехи ch: Chekia co: Cechia cs: Česko cu: Чєшьско cv: Чех Республики cy: Gweriniaeth Tsiec da: Tjekkiet de: Tschechien dv: ޗެކް ޖުމްހޫރިއްޔާ dz: ཅེཀ་རི་པབ་ལིཀ་ ee: Czech Republic el: Τσεχία en: Czechia eo: Ĉeĥio es: Chequia et: Tšehhi eu: Txekia fa: جمهوری چک ff: Cekiya fi: Tšekki fo: Kekkia fr: Tchéquie fy: Tsjechje ga: An tSeic gd: An t-Seic gl: República Checa gn: Cheko gv: Yn Phobblaght Çheck ha: Kazech he: צכיה hi: चेक गणराज्य hr: Češka ht: Repiblik tchèk hu: Csehország hy: Չեխիա ia: Republica Chec id: Republik Ceko ie: Tchechia ig: Chekia ik: Czech Republic io: Chekia is: Tékkland it: Repubblica Ceca ja: チェコ jv: Céko ka: ჩეხეთი kg: République tchèque kk: Чехия kl: Tjekkia kn: ಜೆಕ್ ಗಣರಾಜ್ಯ ko: 체코 ku: Komara Çekî kv: Чехия kw: Pow Chek ky: Чехия la: Cechia lb: Tschechien li: Tsjechië ln: Republíki Sheki lo: ສາທາລະນະລັດແຊັກ lt: Čekija lv: Čehija mg: Tsekia mi: Tīekia mk: Чешка ml: ചെക്ക് റിപ്പബ്ലിക്ക് mn: Бүгд Найрамдах Чех Улс mr: चेक प्रजासत्ताक ms: Republik Czech mt: Repubblika Ċeka my: ချက်သမ္မတနိုင်ငံ na: Ripubrikin Tsiek ne: चेक गणतन्त्र nl: Tsjechië "no": Tsjekkia nv: Chek Bikéyah oc: Republica Chèca om: Cheekiyaa or: ଚେକ ରିପବ୍ଲିକ os: Чехи pa: ਚੈੱਕ ਗਣਰਾਜ pi: चेक रिपब्लिक pl: Czechy ps: چېک جمهوريت pt: Tcheca qu: Chiksuyu rm: Tschechia ro: Cehia ru: Чехия rw: Cekiya sa: ज़ेक् रिपब्लिक sc: Repùblica Ceca sd: جمهوريه چيڪ se: Čeahkka sh: Češka Republika si: චෙක් ජනරජය sk: Česko sl: Češka sm: Czechs Republic sn: Czechia so: Jamhuuriyadda Tashik sq: Republika Çeke sr: Чешка ss: Shekhi su: Céko sv: Tjeckien sw: Ucheki ta: செக் குடியரசு te: చెక్ రిపబ్లిక్ tg: Чехия th: สาธารณรัฐเช็ก tk: Çehiýa tl: Republikang Tseko tr: Çek Cumhuriyeti ts: Czech Republic tt: Чехия ty: Czech Republic ug: چېخ جۇمھۇرىيەتى uk: Чехія ur: چیک جمہوریہ uz: Chexiya vi: Cộng hòa Séc vo: Tsyegän wa: Tchekeye wo: Réewum Cek yi: טשעכיי yo: Tsẹ́kì Olómìnira za: Ciethaek zh: 捷克 zu: ITsheki ================================================ FILE: settings/country-names/de.yaml ================================================ name: default: Deutschland ab: Алмантәыла af: Duitsland ak: Germany am: ጀርመን an: Alemanya ar: ألمانيا av: Алмания ay: Alimaña az: Almaniya ba: Германия be: Германія bg: Германия bi: Germany bm: Jermani bn: জার্মানি bo: འཇར་མན། br: Alamagn bs: Njemačka ca: Alemanya ce: Германи co: Germania cs: Německo cu: Нѣмьци cv: Германи cy: Yr Almaen da: Tyskland de: Deutschland dv: ޖަރުމަނުވިލާތް dz: ཇཱར་མ་ནི ee: Germany el: Γερμανία en: Germany eo: Germanio es: Alemania et: Saksamaa eu: Alemania fa: آلمان ff: Almaanya fi: Saksa fo: Týskland fr: Allemagne fy: Dútslân ga: An Ghearmáin gd: A' Ghearmailt gl: Alemaña gn: Alemaña gu: જર્મની gv: Yn Ghermaan ha: Jamus he: גרמניה hi: जर्मनी hr: Njemačka ht: Almay hu: Németország hy: Գերմանիա ia: Germania id: Jerman ie: Germania ig: Jémanị io: Germania is: Þýskaland it: Germania iu: ᔮᒪᓂ ja: ドイツ jv: Jerman ka: გერმანია kg: Alemanyi ki: Germany kk: Германия Федеративтік Республикасы kl: Tyskit Nunaat km: អាល្លឺម៉ង់ kn: ಜರ್ಮನಿ ko: 독일 ks: جرمٔنی ku: Almanya kv: Германия kw: Almayn ky: Германия la: Germania lb: Däitschland lg: Girimane li: Duutsjlandj ln: Alémani lo: ປະເທດເຢັຽລະມັນ lt: Vokietija lv: Vācija mg: Alemaina mi: Tiamani mk: Германија ml: ജർമ്മനി mn: Герман mo: Ӂермания mr: जर्मनी ms: Jerman mt: Ġermanja my: ဂျာမနီနိုင်ငံ na: Djermani ne: जर्मनी nl: Duitsland "no": Tyskland nv: Béésh Bichʼahii Bikéyah oc: Alemanha or: ଜର୍ମାନୀ os: Герман pa: ਜਰਮਨੀ pl: Niemcy ps: آلمان pt: Alemanha qu: Alimanya rm: Germania rn: Ubudagi ro: Germania ru: Германия rw: Ubudage sa: जर्मनी sc: Germània se: Duiska sh: Nemačka si: ජර්මනිය sk: Nemecko sl: Nemčija sm: Siamani so: Jarmalka sq: Gjermania sr: Немачка ss: IJalimane su: Jérman sv: Tyskland sw: Ujerumani ta: செருமனி te: జర్మనీ tg: Олмон th: ประเทศเยอรมนี ti: ጀርመን tk: Germaniýa tl: Alemanya tr: Almanya ts: Jarimani tt: Алмания tw: Gyaaman ty: Heremani ug: گېرمانىيە uk: Німеччина ur: جرمنی uz: Olmoniya vi: Đức vo: Deutän wa: Almagne wo: Almaañ xh: IJamani yi: דייטשלאנד yo: Jẹ́mánì za: Dwzgoz zh: 德国 zu: IJalimani ================================================ FILE: settings/country-names/dj.yaml ================================================ name: default: Djibouti جيبوتي af: Djiboeti am: ጅቡቲ an: Chibuti ar: جيبوتي az: Cibuti ba: Джибути be: Джыбуці bg: Джибути bn: জিবুতি bo: ཇི་བའོ་ཊི། br: Djibouti bs: Džibuti ca: Djibouti ce: Джибути cs: Džibutsko cv: Джибути cy: Djibouti da: Djibouti de: Dschibuti dv: ޖިބުތީ ee: Djibouti el: Τζιμπουτί en: Djibouti eo: Ĝibutio es: Yibuti et: Djibouti eu: Djibuti fa: جیبوتی fi: Djibouti fr: Djibouti fy: Dzjibûty ga: Diobúití gd: Diobùtaidh gl: Xibutí gv: Djibouti he: ג'יבוטי hi: जिबूती hr: Džibuti ht: Djibouti hu: Dzsibuti hy: Ջիբութի ia: Djibouti id: Djibouti ie: Djibouti io: Djibuti is: Djíbútí it: Gibuti ja: ジブチ jv: Djibouti ka: ჯიბუტი kg: Djibuti ki: Djibouti kk: Джибути ko: 지부티 ku: Cîbûtî kw: Jibouti la: Gibutum lb: Dschibuti li: Djibouti ln: Djibuti lt: Džibutis lv: Džibutija mg: Jibotia mi: Tipūti mk: Џибути ml: ജിബൂട്ടി mn: Джибути mr: जिबूती ms: Djibouti mt: Ġibuti my: ဂျီဘူတီနိုင်ငံ na: Djibuti ne: जिबुटी nl: Djibouti "no": Djibouti nv: Jibótii oc: Jiboti om: Jibuutii or: ଡିଜିବୋଇଟି os: Джибути pa: ਜਿਬੂਤੀ pl: Dżibuti ps: جېبوتي pt: Djibouti qu: Yiwuti ro: Djibouti ru: Джибути rw: Jibuti sa: जिबूटी sc: Gibuti se: Djibouti sg: Dibutùii sh: Džibuti sk: Džibutsko sl: Džibuti sn: Djibouti so: Jabuuti sq: Xhibuti sr: Џибути ss: IJibhuthi su: Djibouti sv: Djibouti sw: Jibuti ta: சீபூத்தீ te: జిబౌటి tg: Ҷибути th: ประเทศจิบูตี ti: ጂቡቲ tk: Jibuti tl: Hiboti tr: Cibuti ts: Djibouti tt: Җибути ug: جىبۇتى uk: Джибуті ur: جبوتی uz: Jibuti vi: Djibouti vo: Cibutän wo: Jibuti yi: דזשיבוטי yo: Djìbútì zh: 吉布提 zu: IJibuthi ================================================ FILE: settings/country-names/dk.yaml ================================================ name: default: Danmark af: Denemarke am: ዴንማርክ an: Dinamarca ar: الدنمارك az: Danimarka ba: Дания be: Данія bg: Дания bi: Denmark bn: ডেনমার্ক bo: དན་མྲག br: Danmark bs: Danska ca: Dinamarca ce: Дани co: Danimarca cs: Dánsko cu: Данїꙗ cv: Дани cy: Denmarc da: Danmark de: Dänemark dv: ޑެންމާކު dz: ཌེན་མཱཀ་ ee: Denmark el: Δανία en: Denmark eo: Danio es: Dinamarca et: Taani eu: Danimarka fa: دانمارک fi: Tanska fo: Danmark fr: Danemark fy: Denemark ga: An Danmhairg gd: An Danmhairg gl: Dinamarca gn: Ndinamayka gv: Yn Danvarg he: דנמרק hi: डेनमार्क hr: Danska ht: Danmak hu: Dánia hy: Դանիա ia: Danmark id: Denmark ie: Dania io: Dania is: Danmörk it: Danimarca ja: デンマーク jv: Denmark ka: დანია kg: Danemark ki: Denmark kk: Дания kl: Danmarki kn: ಡೆನ್ಮಾರ್ಕ್ ko: 덴마크 ku: Danîmarka kv: Дания kw: Danmark ky: Дания la: Dania lb: Dänemark lg: Denimaaka li: Denemarke ln: Danemark lt: Danija lv: Dānija mg: Danmarka mi: Tenemāka mk: Данска ml: ഡെന്മാർക്ക് mn: Дани mr: डेन्मार्क ms: Denmark mt: Danimarka my: ဒိန်းမတ်နိုင်ငံ na: Denemark ne: डेनमार्क nl: Denemarken "no": Danmark nv: Déinish Dineʼé Bikéyah oc: Danemarc or: ଡେନମାର୍କ os: Дани pl: Dania ps: ډېنمارک pt: Dinamarca qu: Dansuyu rm: Danemarc rn: Danemarke ro: Danemarca ru: Дания rw: Danimarike sa: डेनमार्क sc: Danimarca se: Dánmárku sh: Danska si: ඩෙන්මාර්කය sk: Dánsko sl: Danska so: Denmark sq: Danimarka sr: Данска ss: IDenimakhi st: Denmark su: Dénmark sv: Danmark sw: Denmark ta: டென்மார்க் te: డెన్మార్క్ tg: Дания th: ประเทศเดนมาร์ก tk: Daniýa tl: Dinamarka tr: Danimarka tt: Дания ug: دانىيە uk: Данія ur: ڈنمارک uz: Daniya vi: Đan Mạch vo: Danän wo: Danmaark yi: דענמארק yo: Dẹ́nmárkì zh: 丹麦 zu: IDenimaki ================================================ FILE: settings/country-names/dm.yaml ================================================ name: default: Dominica af: Dominika ar: دومينيكا be: Дамініка br: Republik Dominikan cs: Dominika de: Dominica el: Δομινίκα en: Dominica eo: Dominiko es: Dominica eu: Dominika fa: دومینیکا fi: Dominica fr: Dominique fy: Dominika ga: Doiminice gd: Doiminicia he: דומיניקה hr: Dominika hu: Dominikai Közösség id: Dominika is: Dóminíka it: Dominìca ja: ドミニカ lt: Dominika lv: Dominika mn: Доминика nl: Dominica "no": Dominica pl: Dominika pt: Dominica ru: Доминика se: Dominica sk: Dominika sl: Dominika sv: Dominica th: ประเทศโดมินิกา tr: Dominika uk: Домініка vo: Dominikeän zh: 多米尼克 ================================================ FILE: settings/country-names/do.yaml ================================================ name: default: República Dominicana af: Dominikaanse Republiek am: ዶሚኒካን ሪፐብሊክ an: Republica Dominicana ar: جمهورية الدومينيكان ay: Republika Duminikana az: Dominikan Respublikası be: Дамініканская Рэспубліка bg: Доминиканска република bn: ডোমিনিকান প্রজাতন্ত্র bo: ཌོ་མི་ནི་ཀན་སྤྱི་མཐུན་རྒྱལ་ཁབ། br: Republik Dominikan bs: Dominikanska Republika ca: República Dominicana ce: Доминикан Пачхьалкх cs: Dominikánská republika cy: Gweriniaeth Dominica da: Dominikanske Republik de: Dominikanische Republik dv: ޑޮމިނިކަން ޖުމްހޫރިއްޔާ ee: Dominican Republic el: Δομινικανή Δημοκρατία en: Dominican Republic eo: Domingo es: República Dominicana et: Dominikaani Vabariik eu: Dominikar Errepublika fa: جمهوری دومینیکن fi: Dominikaaninen tasavalta fo: Dominikanalýðveldið fr: République dominicaine fy: Dominikaanske Republyk ga: An Phoblacht Dhoiminiceach gd: Poblachd Dhoiminicia gl: República Dominicana gv: Yn Phobblaght Ghominicagh he: הרפובליקה הדומיניקנית hi: डोमिनिकन गणराज्य hr: Dominikanska Republika ht: Dominikani hu: Dominikai Köztársaság hy: Դոմինիկյան Հանրապետություն ia: Republica Dominican id: Republik Dominika io: Dominikana Republiko is: Dóminíska lýðveldið it: Repubblica Dominicana ja: ドミニカ共和国 jv: Republik Dominika ka: დომინიკელთა რესპუბლიკა kk: Доминикан Республикасы kn: ಡೊಮಿನಿಕ ಗಣರಾಜ್ಯ ko: 도미니카 공화국 ku: Komara Domînîk kw: Repoblek Dhominikanek la: Respublica Dominicana lb: Dominikanesch Republik li: Dominicaanse Rippubliek ln: Republiki Dominikani lt: Dominikos Respublika lv: Dominikāna mi: Te Whenua Tominika mk: Доминиканска Република ml: ഡൊമനിക്കൻ റിപ്പബ്ലിക് mn: Доминиканы Бүгд Найрамдах Улс mr: डॉमिनिकन प्रजासत्ताक ms: Republik Dominika mt: Repubblika Dominikana my: ဒိုမီနီကန်သမ္မတနိုင်ငံ na: Ripubrikin Dominika nb: Den dominikanske republikk ne: डोमिनिकन गणतन्त्र nl: Dominicaanse Republiek nn: Den dominikanske republikken "no": Den dominikanske republikk nv: Domingo Bikéyah oc: Republica Dominicana or: ଡୋମେନିକାନ ରିପବ୍ଲିକ os: Доминиканæйы Республикæ pa: ਡੋਮਿਨਿਕਾਈ ਗਣਰਾਜ pl: Dominikana ps: ډومنيکان جمهوريت pt: República Dominicana qu: Duminikana ro: Republica Dominicană ru: Доминиканская Республика rw: Repubulika ya Dominikani sa: डोमोनिकन रिपब्लिक se: Dominikána dásseváldi sh: Dominikanska Republika sk: Dominikánska republika sl: Dominikanska republika sq: Republika Dominikane sr: Доминиканска Република ss: Tibuse weDomonokha su: Républik Dominika sv: Dominikanska Republiken sw: Jamhuri ya Dominika ta: டொமினிக்கன் குடியரசு te: డొమెనికన్ రిపబ్లిక్ tg: Ҷумҳурии Доминикана th: สาธารณรัฐโดมินิกัน tl: Republikang Dominikano tr: Dominik Cumhuriyeti tt: Доминикан Җөмһүрияте ug: دومىنىكان جۇمھۇرىيەتى uk: Домініканська Республіка ur: جمہوریہ ڈومینیکن uz: Dominika Respublikasi vi: Cộng hòa Dominica vo: Sandominän wo: Réewum Dominik yi: דאמיניקאנישע רעפובליק yo: Orílẹ̀òmìnira Dómíníkì zh: 多明尼加共和國 ================================================ FILE: settings/country-names/dz.yaml ================================================ name: default: Algérie / ⵍⵣⵣⴰⵢⴻⵔ / الجزائر af: Algerië ak: Algeria am: አልጄሪያ an: Alcheria ar: الجزائر as: আলজেৰিয়া ay: Alhirya az: Əlcəzair ba: Алжи́р be: Алжы́р bg: Алжѝр bh: अल्जीरिया bm: Algeri bn: আলজেরিয়া bo: ཨཱལ་ཇི་རི་ཡ། br: Aljeria bs: Alžir ca: Algèria ce: Алжи́р cs: Alžírsko cv: Алжир cy: Algeria da: Algeriet de: Algerien dv: ޖަޒާއިރު dz: ཨཱལ་ཇི་རི་ཡ ee: Ɔldzeria el: Αλγερία en: Algeria eo: Alĝerio es: Argelia et: Alžeeria eu: Aljeria fa: الجزایر ff: Aljeri fi: Algeria fo: Algeria fr: Algérie fy: Algerije ga: An Ailgéir gd: Aildiria gl: Alxeria gn: Ayhelia gu: અલ્જીરિયા gv: Yn Algear ha: Aljeriya he: אלג'יריה hi: अल्जीरिया hr: Alžir ht: Aljeri hu: Algéria hy: Ալժիր ia: Algeria id: Aljazair ie: Algeria ig: Algeria io: Aljeria is: Alsír it: Algeria ja: アルジェリア jv: Aljazair ka: ალჟირი kg: Algeria ki: Algeria kk: Алжир kl: Algeriet km: ប្រទេសអាល់ហ្សេរី kn: ಅಲ್ಜೀರಿಯ ko: 알제리 ks: اٮ۪لجیرِیا ku: Cezayir kw: Aljeri ky: Алжир la: Algerium lb: Algerien lg: Algeria li: Algerieë ln: Aljeria lo: ແອລຈິເລຍ lt: Alžyras lv: Alžīrija mg: Aljeria mi: Aratiria mk: Алжир ml: അൾജീറിയ mn: Алжир mr: अल्जीरिया ms: Algeria mt: Alġerija my: အယ်လ်ဂျီးရီးယားနိုင်ငံ na: Ardjiriya ne: अल्जेरिया nl: Algerije "no": Algerie nv: Aljííya ny: Algeria oc: Argeria om: Aljeeriyaa or: ଆଲଜେରିଆ os: Алжир pa: ਅਲਜੀਰੀਆ pi: अल्जीरिया pl: Algieria ps: الجزاير pt: Argélia qu: Alhirya rm: Algeria rn: Alijeriya ro: Algeria ru: Алжир rw: Aligeriya sa: अल्जीरिया sc: Algerìa sd: الجزائر se: Algeria sg: Alazëri sh: Alžir si: ඇල්ජීරියාව sk: Alžírsko sl: Alžirija sm: Algeria sn: Algeria so: Aljeeriya sq: Algjeria sr: Алжир ss: i-Alijeriya st: Algeria su: Aljazair sv: Algeriet sw: Algeria ta: அல்சீரியா te: అల్జీరియా tg: Алҷазоир th: ประเทศแอลจีเรีย ti: አልጀሪያ tk: Alžir tl: Arhelya to: ʻAisilia tr: Cezayir ts: Algeriya tt: Әлҗәзаи́р tw: Algeria ug: ئالجىرىيە uk: Алжир ur: الجزائر uz: Jazoir vi: Algérie vo: Laljerän wa: Aldjereye wo: Alseeri xh: I-Algeria yi: אַלזשיר yo: Àlgéríà za: Algeria zh: 阿尔及利亚 zu: iAljiriya ================================================ FILE: settings/country-names/ec.yaml ================================================ name: default: Ecuador am: ኤኳዶር an: Ecuador ar: الإكوادور ay: Ikwadur az: Ekvador ba: Эквадор be: Эквадор bg: Еквадор bn: ইকুয়েডর bo: ཨེ་ཁོའོ་ཌོར། br: Ecuador bs: Ekvador ca: Equador ce: Эквадор cs: Ekvádor cv: Эквадор cy: Ecuador da: Ecuador de: Ecuador dv: އިކުއެޑޯރު ee: Ecuador el: Ισημερινός (χώρα) en: Ecuador eo: Ekvadoro es: Ecuador et: Ecuador eu: Ekuador fa: اکوادور fi: Ecuador fo: Ekvador fr: Équateur fy: Ekwador ga: Eacuadór gd: Eacuador gl: Ecuador gn: Ekuator gu: ઈક્વેડોર gv: Ecuador he: אקוודור hi: ईक्वाडोर hr: Ekvador ht: Ekwatè hu: Ecuador hy: Էկվադոր ia: Ecuador id: Ekuador ie: Ecuador io: Equador is: Ekvador it: Ecuador ja: エクアドル jv: Ekuador ka: ეკვადორი ki: Ecuador kk: Эквадор kl: Ecuador kn: ಎಕ್ವಡಾರ್ ko: 에콰도르 ku: Ekuador kw: Pow Ekwadorel la: Aequatoria lb: Ecuador li: Ecuador ln: Republíki ya Ekwatéli lt: Ekvadoras lv: Ekvadora mi: Ekuatoa mk: Еквадор ml: ഇക്വഡോർ mn: Эквадор mr: इक्वेडोर ms: Ecuador mt: Ekwador my: အီကွေဒေါနိုင်ငံ na: Ekwador ne: इक्वेडर nl: Ecuador "no": Ecuador nv: Kéyah Nahasdzáán Ałníiʼgi Siʼánígíí oc: Eqüator or: ଏକ୍ଵାଡୋର os: Эквадор pa: ਏਕੁਆਦੋਰ pl: Ekwador ps: اېکوادور pt: Equador qu: Ikwadur rm: Ecuador ro: Ecuador ru: Эквадор rw: Ekwadoro sa: एक्वाडोर se: Ecuador sh: Ekvador sk: Ekvádor sl: Ekvador so: Ikwadoor sq: Ekuadori sr: Еквадор su: Ékuador sv: Ecuador sw: Ekuador ta: எக்குவடோர் te: ఈక్వడార్ tg: Экуадор th: ประเทศเอกวาดอร์ tl: Ecuador tr: Ekvador tt: Эквадор ug: ئېكۋادور uk: Еквадор ur: ایکواڈور uz: Ekvador vi: Ecuador vo: Lekvadorän wo: Ekwadoor yi: עקוואדאר yo: Ẹ̀kùàdọ̀r zh: 厄瓜多尔 ================================================ FILE: settings/country-names/ee.yaml ================================================ name: default: Eesti af: Estland am: ኤስቶኒያ an: Estonia ar: إستونيا az: Estoniya ba: Эстония be: Эстонія bg: Естония bi: Estonia bm: Estonia bn: ইস্তোনিয়া bo: ཨིསུ་ཊོ་ནིཡ། br: Estonia bs: Estonija ca: Estònia ce: Эстони co: Estonia cs: Estonsko cu: Єсть cv: Эстони cy: Estonia da: Estland de: Estland dz: ཨིསི་ཊོ་ནི་ཡ། ee: Estonia el: Εσθονία en: Estonia eo: Estonio es: Estonia et: Eesti eu: Estonia fa: استونی ff: Estoniya fi: Viro fo: Estland fr: Estonie fy: Estlân ga: An Eastóin gd: Eastòinia gl: Estonia gn: Etoña gu: ઈસ્ટોનિયા gv: Yn Estaan he: אסטוניה hi: एस्टोनिया hr: Estonija ht: Estoni hu: Észtország hy: Էստոնիա ia: Estonia id: Estonia ie: Estonia io: Estonia is: Eistland it: Estonia ja: エストニア jv: Estonia ka: ესტონეთი kg: Estonia kk: Эстония kl: Estlandi kn: ಎಸ್ಟೊನಿಯ ko: 에스토니아 ku: Estonya kv: Эстония kw: Estoni ky: Эстония la: Estonia lb: Estland li: Esland ln: Estonia lt: Estija lv: Igaunija mg: Estonia mi: Etonia mk: Естонија ml: എസ്റ്റോണിയ mn: Эстон mr: एस्टोनिया ms: Estonia mt: Estonja my: အက်စ်တိုးနီးယားနိုင်ငံ na: Etoniya ne: इस्टोनिया nl: Estland "no": Estland nv: Ééstii Bikéyah oc: Estònia or: ଏଷ୍ଟୋନିଆ os: Эстони pa: ਇਸਤੋਨੀਆ pl: Estonia ps: اېسټونيا pt: Estónia qu: Istunya ro: Estonia ru: Эстония rw: Esitoniya sa: एस्टोनिया sc: Estònia se: Estteeana sh: Estonija si: එස්ටෝනියා sk: Estónsko sl: Estonija so: Estoniya sq: Estonia sr: Естонија ss: IWestoniya st: Estonia su: Éstonia sv: Estland sw: Estonia ta: எசுத்தோனியா te: ఎస్టోనియా tg: Эстония th: ประเทศเอสโตเนีย tk: Estoniýa tl: Estonia tr: Estonya tt: Эстония ug: ئېستونىيە uk: Естонія ur: استونیا uz: Estoniya ve: Estonia vi: Estonia vo: Lestiyän wo: Estooni yi: עסטלאנד yo: Estóníà zh: 爱沙尼亚 zu: I-Estoniya ================================================ FILE: settings/country-names/eg.yaml ================================================ name: default: مصر ab: Мы́сры af: Egipte ak: Igyipt am: ግብፅ an: Echipto ar: مصر as: ইজিপ্ত av: Еги́пет ay: Iqiqtu az: Misir ba: Мысыр be: Егі́пет bg: Египет bh: मिस्र bm: Misra bn: মিশর bo: ཨའི་ཅི། br: Egipt bs: Egipat ca: Egipte ce: Мисар ch: Ehiptu cs: Egypt cu: Єгѷптъ cy: Yr Aifft da: Egypten de: Ägypten dv: މިޞްރު ee: Edzipti el: Αίγυπτος en: Egypt eo: Egiptio es: Egipto et: Egiptus eu: Egipto fa: مصر ff: Ejipt fi: Egypti fj: Ijipta fo: Egyptaland fr: Égypte fy: Egypte ga: An Éigipt gd: An Èipheit gl: Exipto gn: Ehipto gu: ઇજિપ્ત gv: Yn Egypt ha: Misra he: מצרים hi: मिस्र hr: Egipat ht: Ejip hu: Egyiptom hy: Եգիպտոս ia: Egypto id: Mesir ie: Egiptia ig: Egypt io: Egiptia is: Egyptaland it: Egitto ja: エジプト jv: Mesir ka: ეგვიპტე kg: Misiri ki: Egypt kk: Мысыр kl: Egypt km: អេហ៊្សីប kn: ಈಜಿಪ್ಟ್ ko: 이집트 ks: مِسر ku: Misir kv: Египет kw: Ejyp ky: Египет la: Aegyptus lb: Egypten lg: Egypt li: Egypte ln: Ejipte lo: ອີຢິບ lt: Egiptas lv: Ēģipte mg: Ejipta mi: Īhipa mk: Египет ml: ഈജിപ്ത് mn: Египет mr: इजिप्त ms: Mesir mt: Eġittu my: အီဂျစ်နိုင်ငံ na: Idjipt ne: मिश्र nl: Egypte "no": Egypt nv: Ííjip oc: Egipte om: Ijiipti or: ଇଜିପ୍ଟ os: Мысыр pa: ਮਿਸਰ pi: ईजिप्ट pl: Egipt ps: مصر pt: Egito qu: Ihiptu rm: Egipta rn: Egipto ro: Egipt ru: Египет sa: ईजिप्तदेशः sc: Egittu sd: مصر se: Egypta sg: Kâmitâ sh: Egipat si: ඊජීප්තුව sk: Egypt sl: Egipt sm: Aikupito sn: Egypt so: Masar sq: Egjipti sr: Египат ss: iGibhithe st: Egepeta su: Mesir sv: Egypten sw: Misri ta: எகிப்து te: ఈజిప్టు tg: Миср th: ประเทศอียิปต์ ti: ግብጽ tk: Müsür tl: Ehipto to: ʻIsipite tr: Mısır ts: Gibita tt: Мисы́р tw: Egypt ty: ’Aifiti ug: مىسىر uk: Єги́пет ur: مصر uz: Misr vi: Ai Cập vo: Lägüptän wa: Edjipe wo: Isipt yi: עגיפטן yo: Ẹ́gíptì za: Aehciz zh: 埃及 zu: iGibhithe ================================================ FILE: settings/country-names/eh.yaml ================================================ name: default: الجمهورية العربية الصحراوية الديمقراطية ar: الجمهورية العربية الصحراوية الديمقراطية cs: Saharská arabská demokratická republika de: Demokratische Arabische Republik Sahara el: Δημοκρατία της Σαχάρας en: Sahrawi Arab Democratic Republic es: República Árabe Saharaui Democrática fa: جمهوری دموکراتیک عربی صحرا fr: République arabe sahraouie démocratique it: Repubblica Araba Democratica dei Sahrawi lt: Sacharos Arabų Demokratinė Respublika nb: Den saharawiske arabiske demokratiske republikk nl: Arabische Democratische Republiek Sahara nn: Den saharawiske arabiske demokratiske republikken "no": Den saharawiske arabiske demokratiske republikk pt: República Árabe Saaraui Democrática ru: Сахарская Арабская Демократическая Республика ur: صحراوی عرب عوامی جمہوریہ zh: 撒拉威阿拉伯民主共和國 ================================================ FILE: settings/country-names/er.yaml ================================================ name: default: ኤርትራ Eritrea إرتريا af: Eritrea am: ኤርትራ an: Eritrea ar: إرتريا az: Eritreya ba: Эритрея be: Эрытрэя bg: Еритрея bm: Eritrea bn: ইরিত্রিয়া bo: ཨི་རི་ཏྲའ། br: Eritrea bs: Eritreja ca: Eritrea ce: Эритре cs: Eritrea cu: Єрѷѳрїꙗ cy: Eritrea da: Eritrea de: Eritrea dv: އެރިތުރިއާ ee: Eritrea el: Ερυθραία en: Eritrea eo: Eritreo es: Eritrea et: Eritrea eu: Eritrea fa: اریتره fi: Eritrea fo: Eritrea fr: Érythrée fy: Eritreä ga: An Eiritré gd: Eartra gl: Eritrea gn: Erityrea gv: Eritrea he: אריתריאה hi: इरित्रिया hr: Eritreja ht: Eritre hu: Eritrea hy: Էրիթրեա ia: Eritrea id: Eritrea ie: Eritréa io: Eritrea is: Erítrea it: Eritrea ja: エリトリア jv: Eritrea ka: ერიტრეა kg: Eritrea kk: Эритрея kn: ಎರಿಟ್ರಿಯ ko: 에리트레아 ku: Erîtrea kw: Eritrea ky: Эритрея la: Erythraea lb: Eritrea li: Eritrea ln: Elitré lt: Eritrėja lv: Eritreja mi: Eritēria mk: Еритреја ml: എരിട്രിയ mn: Эритрей mr: इरिट्रिया ms: Eritrea mt: Eritrea my: အီရီထရီးယားနိုင်ငံ na: Eritrea ne: एरिट्रिया nl: Eritrea "no": Eritrea oc: Eritrèa om: Eritrea or: ଇରିଟ୍ରିଆ os: Эритрей pa: ਇਰੀਤਰੀਆ pl: Erytrea ps: اریتره pt: Eritreia qu: Iritriya ro: Eritreea ru: Эритрея rw: Eritereya sa: इरीट्रिया sc: Eritrea se: Eritrea sg: Eritrëe sh: Eritreja sk: Eritrea sl: Eritreja sn: Eritrea so: Eratareya sq: Eritrea sr: Еритреја ss: IRitheya st: Eritrea su: Éritréa sv: Eritrea sw: Eritrea ta: எரித்திரியா te: ఎరిత్రియా tg: Эритрея th: ประเทศเอริเทรีย ti: ኤርትራ tk: Eritreýa tl: Eritrea tr: Eritre ts: Eritreya tt: Эритрея ug: ئېرىترېيە uk: Еритрея ur: اریتریا uz: Eritriya vi: Eritrea vo: Lerüträn wo: Eritere yi: עריטרעא yo: Ẹritrẹ́à zh: 厄立特里亚 zu: I-Eritrea ================================================ FILE: settings/country-names/es.yaml ================================================ name: default: España af: Spanje am: እስፓንያ an: Espanya ar: إسبانيا ay: Ispaña az: İspaniya ba: Испания be: Іспанія bg: Испания bi: Spain bn: স্পেন bo: ཞི་པན་ཡ། br: Spagn bs: Španija ca: Espanya ce: Испани ch: España co: Spagna cs: Španělsko cu: Їспанїꙗ cv: Испани cy: Sbaen da: Spanien de: Spanien dv: އިސްޕެއިން dz: སིཔཱེན་ ee: Spain el: Ισπανία en: Spain eo: Hispanio es: España et: Hispaania eu: Espainia fa: اسپانیا fi: Espanja fo: Spania fr: Espagne fy: Spanje ga: An Spáinn gd: An Spàinn gl: España gn: España gu: સ્પેન gv: Yn Spaainey he: ספרד hi: स्पेन hr: Španjolska ht: Espay hu: Spanyolország hy: Իսպանիա ia: Espania id: Spanyol ie: Hispania ig: Spain ik: Spaña io: Hispania is: Spánn it: Spagna iu: ᓯᐸᐃᓐ ja: スペイン jv: Spanyol ka: ესპანეთი kg: Espania kk: Испания kl: Spania km: អេស្ប៉ាញ kn: ಸ್ಪೇನ್ ko: 스페인 ku: Spanya kv: Испания kw: Spayn ky: Испания la: Hispania lb: Spuenien li: Spanje ln: Espania lt: Ispanija lv: Spānija mg: Espaina mi: Peina mk: Шпанија ml: സ്പെയിൻ mn: Испани mr: स्पेन ms: Sepanyol mt: Spanja my: စပိန်နိုင်ငံ na: Pain ne: स्पेन nl: Spanje "no": Spania nv: Dibé Diníí Bikéyah oc: Espanha or: ସ୍ପେନ os: Испани pa: ਸਪੇਨ pl: Hiszpania ps: اسپانیا pt: Espanha qu: Ispaña rm: Spagna rn: Esipanye ro: Spania ru: Испания rw: Esipanye sa: स्पेन् sc: Ispagna se: Espánnja sh: Španija sk: Španielsko sl: Španija sm: Spania so: Isbania sq: Spanja sr: Шпанија ss: Sipeyini st: Spain su: Spanyol sv: Spanien sw: Hispania ta: எசுப்பானியா te: స్పెయిన్ tg: Испониё th: ประเทศสเปน tk: Ispaniýa tl: Espanya to: Sepeni tr: İspanya ts: Spaniya tt: Испания ty: Paniora ug: ئىسپانىيە uk: Іспанія ur: ہسپانیہ uz: Ispaniya vi: Tây Ban Nha vo: Spanyän wa: Espagne wo: Ispaañ yi: שפאניע yo: Spéìn za: Sihbanhyaz zh: 西班牙 zu: ISpeyini ================================================ FILE: settings/country-names/et.yaml ================================================ name: default: ኢትዮጵያ af: Ethiopië ak: Ithiopia am: ኢትዮጵያ an: Etiopia ar: إثيوبيا av: Хабашистан az: Efiopiya ba: Эфио́пия be: Эфіопія bg: Етиопия bh: इथियोपिया bm: Etiopia bn: ইথিওপিয়া bo: ཨི་ཐི་ཨོ་པི་ཡ། br: Etiopia bs: Etiopija ca: Etiòpia ce: Эфио́пи cs: Etiopie cu: Єѳїопі́ꙗ cv: Эфиопи cy: Ethiopia da: Etiopien de: Äthiopien dv: ޙަބުޝްކަރަ dz: ཨི་ཐི་ཡོ་པི་ཡ ee: Etiopia el: Αιθιοπία en: Ethiopia eo: Etiopio es: Etiopía et: Etioopia eu: Etiopia fa: اتیوپی fi: Etiopia fo: Etiopia fr: Éthiopie fy: Etioopje ga: an Aetóip gd: An Aetiòp gl: Etiopía - Ityop'iya gn: Etiopia gu: ઇથિયોપિયા gv: yn Eetoip ha: Ethiopia he: אתיופיה hi: इथियोपिया hr: Etiopija ht: Etyopi hu: Etiópia hy: Եթովպիա ia: Ethiopia id: Ethiopia ie: Etiopia ig: Ethiopia io: Etiopia is: Eþíópía it: Etiopia ja: エチオピア jv: Étiopia ka: ეთიოპია kg: Itiopia ki: Ethiopia kk: Ефиопия km: អេត្យូពី kn: ಇತಿಯೋಪಿಯ ko: 에티오피아 ks: اِتھوپِیا ku: Etiyopya kv: Эфиопия kw: Ethiopi ky: Эфиопия la: Aethiopia lb: Ethiopien lg: Ethiopia li: Ethiopië ln: Etiopi lo: ເອທີໂອເປຍ lt: Etiopija lv: Etiopija mg: Etiopia mi: Etiopia mk: Етиопија ml: എത്യോപ്യ mn: Этиоп mr: इथियोपिया ms: Habsyah mt: Etjopja my: အီသီယိုးပီးယားနိုင်ငံ na: Itiyopiya ne: इथियोपिया nl: Ethiopië "no": Etiopia nv: Iithiyópya oc: Etiopia om: Itoophiyaa or: ଇଥିଓପିଆ os: Эфио́пи pa: ਇਥੋਪੀਆ pi: ईथ्योपिया pl: Etiopia ps: اېتوپیا pt: Etiópia qu: Ithiyupya rm: Etiopia ro: Etiopia ru: Эфиопия rw: Etiyopiya sa: ईथ्योपिया sc: Etiòpia se: Etiopia sg: Etiopïi sh: Etiopija si: ඉතියෝපියාව sk: Etiópia sl: Etiópija sm: Ethiopia sn: Ethiopia so: Itoobiya sq: Etiopia sr: Етиопија ss: iTopiya st: Ethiopia su: Étiopia sv: Etiopien sw: Ethiopia ta: எத்தியோப்பியா te: ఇథియోపియా tg: Эфиопия th: เอธิโอเปีย ti: ኢትዮጵያ tk: Efiopiýa tl: Etiyopiya to: ʻItiōpea tr: Etiyopya ts: Topiya tt: Хәбәшстан ug: ئېفىئوپىيە uk: Ефіо́пія ur: ایتھوپیا uz: Efiopiya vi: Ethiopia vo: Lätiopän wa: Etiopeye wo: Ecoopi xh: iTopiya yi: עטיאָפּיע yo: Ethiópíà za: Ethiopia zh: 埃塞俄比亚 zu: i-Ithiopia ================================================ FILE: settings/country-names/fi.yaml ================================================ name: default: Suomi ab: Суоми af: Finland am: ፊንላንድ an: Finlandia ar: فنلندا ay: Phini suyu az: Finlandiya ba: Финляндия be: Фінляндыя bg: Финландия bi: Finland bm: Finland bn: ফিনল্যান্ড bo: ཧྥིན་ལན br: Finland bs: Finska ca: Finlàndia ce: Финлянди co: Finlandia cs: Finsko cu: Соумь cv: Финлянди cy: Y Ffindir da: Finland de: Finnland dv: ފިންލޭންޑު dz: ཕིན་ལེནཌ་ ee: Finland el: Φινλανδία en: Finland eo: Finnlando es: Finlandia et: Soome eu: Finlandia fa: فنلاند fi: Suomi fo: Finnland fr: Finlande fy: Finlân ga: An Fhionlainn gd: Suòmaidh gl: Finlandia gn: Hĩlandia gu: ફીનલેંડ gv: Finnlynn he: פינלנד hi: फ़िनलैंड hr: Finska ht: Fenlann hu: Finnország hy: Ֆինլանդիա ia: Finlandia id: Finlandia ie: Finland ig: Finland io: Finlando is: Finnland it: Finlandia iu: ᐃᓐᓚᓐᑦ ja: フィンランド jv: Finlandia ka: ფინეთი kg: Finlandi ki: Finland kk: Финландия kl: Finlandi kn: ಫಿನ್‍ಲ್ಯಾಂಡ್ ko: 핀란드 ks: فننش ku: Fînland kv: Финляндия kw: Pow Finn ky: Финляндия la: Finnia lb: Finnland lg: Finilandi li: Finland ln: Finilanda lt: Suomija lv: Somija mg: Finlandy mi: Whinarana mk: Финска ml: ഫിൻലാന്റ് mn: Финлянд mr: फिनलंड ms: Finland mt: Finlandja my: ဖင်လန်နိုင်ငံ na: Finland ne: फिनल्याण्ड nl: Finland "no": Finland nv: Nahoditsʼǫʼłání oc: Finlàndia or: ଫିନଲ୍ୟାଣ୍ଡ os: Финлянди pa: ਫ਼ਿਨਲੈਂਡ pl: Finlandia ps: فېنلانډ pt: Finlândia qu: Phinsuyu rm: Finlanda ro: Finlanda ru: Финляндия rw: Finilande sa: फिन्लैंड sc: Finlandia se: Suopma sh: Finska si: ෆින්ලන්තය sk: Fínsko sl: Finska sm: Finalagi so: Finland sq: Finlanda sr: Финска ss: IFini st: Finland su: Finlandia sv: Finland sw: Ufini ta: பின்லாந்து te: ఫిన్లెండ్ tg: Финланд th: ประเทศฟินแลนด์ tk: Finlýandiýa tl: Pinlandiya tr: Finlandiya tt: Finlândiä ug: فىنلاندىيە uk: Фінляндія ur: فن لینڈ uz: Finlandiya vi: Phần Lan vo: Suomiyän wa: Finlande wo: Finlaand yi: פינלאנד yo: Finlandi za: Finlan zh: 芬兰 zu: IFinlandi ================================================ FILE: settings/country-names/fj.yaml ================================================ name: default: Viti af: Fidji am: ፊጂ an: Fichi ar: فيجي az: Fici ba: Фиджи be: Фіджы bg: Фиджи bh: फिजी bn: ফিজি bo: ཧྥི་ཇི། br: Fidji bs: Fidži ca: Fiji ce: Фиджи cs: Fidži cv: Фиджи cy: Fiji da: Fiji de: Fidschi dv: ފިޖީ el: Φίτζι en: Fiji eo: Fiĝioj es: Fiyi et: Fidži eu: Fiji fa: فیجی ff: Fiiji fi: Fidži fj: Viti fo: Fiji fr: Fidji fy: Fidzjy ga: Fidsí gd: Fìdi gl: Fidxi - Viti gv: Fiji he: פיג'י hi: फ़िजी hr: Fidži ht: Fidji hu: Fidzsi-szigetek hy: Ֆիջի ia: Fiji id: Fiji io: Fidji is: Fídjieyjar it: Figi ja: フィジー jv: Fiji ka: ფიჯი kk: Фиджи kn: ಫಿಜಿ ko: 피지 ku: Fîjî kw: Fiji la: Viti lb: Fidschi li: Fiji lt: Fidžis lv: Fidži mi: Whītī mk: Фиџи ml: ഫിജി mn: Фижи mr: फिजी ms: Fiji mt: Fiġi my: ဖီဂျီနိုင်ငံ na: Bidji ne: फिजी nl: Fiji "no": Fiji nv: Fííjii oc: Fiji or: ଫିଜି os: Фиджи pa: ਫ਼ਿਜੀ pl: Fidżi pt: Fiji qu: Phiyi ro: Fiji ru: Фиджи rw: Fiji sa: फिजी se: Fiži sg: Fidyïi sh: Fidži si: ෆීජි sk: Fidži sl: Fidži sm: Fiti so: Fiji sq: Fixhi sr: Фиџи su: Fiji sv: Fiji sw: Fiji ta: பிஜி te: ఫిజీ tg: Фиҷи th: ประเทศฟิจิ tl: Piyi to: Fisi tr: Fiji tt: Fiji ug: فىجى uk: Фіджі ur: فجی uz: Fiji vi: Fiji vo: Ficiyuäns wo: Fiiji yi: פידזשי yo: Fíjì zh: 斐濟 zu: IFiji ================================================ FILE: settings/country-names/fk.yaml ================================================ name: default: Falkland Islands af: Falklandeilande ak: Fɔlkman Aeland am: ፎክላንድ ደሴቶች (ኢስላስ ማልቪናስ) ar: جزر الفولكلاند az: Folkland Adaları be: Фалклендскія астравы bg: Фолкландски острови (Малвини) bm: Maluwini Gun bn: ফকল্যান্ড আইল্যান্ড (ইসল্যাস মাসভেনিস) bo: ཕལྐ་ལནྜ་གླིང་ཕྲན། br: Inizi Falkland (Inizi Maloù) bs: Folklandska ostrva (Malvinska ostrva) ca: Illes Malvines cs: Falklandy cy: Ynysoedd y Falklands da: Falklandsøerne de: Falklandinseln dz: ཕལྐ་ལནྜ་གླིང་ཚོམ (ཨིས་ལཱས་མལ་བི་ཎཱས) ee: Falkland ƒudomekpowo (Islas Malvinas) nutome el: Νήσοι Φώκλαντ en: Falkland Islands eo: Falklandoj es: Islas Malvinas et: Falklandi saared eu: Falkland uharteak (Malvina uharteak) fa: جزایر فالکلند ff: Duuɗe Falkland fi: Falklandinsaaret fo: Falklandsoyggjarnar fr: Îles Malouines (Îles Falkland) fy: Falklâneilannen ga: Oileáin Fháclainne gd: Na h-Eileanan Fàclainn gl: Illas Malvinas gu: ફૉકલેન્ડ આઇલેન્ડ્સ gv: Ny Malveenaghyn ha: Tsibiran Falkilan he: איי פוקלנד (מלווינס) hi: फ़ॉकलैंड आइलैंड (इज्लास माल्विनास) hr: Falklandski Otoci hu: Falkland-szigetek id: Kepulauan Falkland is: Falklandseyjar it: Isole Falkland ja: フォークランド諸島 (マルビナス諸島) ka: ფალკლენდის კუნძულები ki: Visiwa vya Falkland kl: Falklandi qeqertaq km: កោះ​ហ្វក់ឡែន (Islas Malvinas) kn: ಫಾಲ್ಕ್‌ಲ್ಯಾಂಡ್ ದ್ವೀಪಗಳು (ಇಸ್ಲಾಸ್ ಮಾಲ್ವಿನಸ್) ko: 포클랜드 제도 (말비나스 군도) ks: فٕلاکلینٛڑ جٔزیٖرٕ ku: Giravên Malvîn la: Insulae Malvinae lb: Falkland Inselen lg: Bizinga by'eFalikalandi ln: Bisanga bya Falkland lo: ຫມູ່ເກາະຟອກແລນ (ອິສລາສ ມາວິລນາສ) lt: Folklando (Malvinų) Salos lv: Folklenda salas mg: Nosy Falkand mk: Фолкландски Острови ml: ഫോക്ൿലൻഡ് ദ്വീപുകൾ (മൽവിനാസ്) mn: Фолклендийн Арлууд mr: फॉकलंड बेटे (इस्लास मालविनास) ms: Kepulauan Falkland (Islas Malvinas) mt: Falkland Islands my: ဖောက်ကလန် ကျွန်းစု nb: Falklandsøyene ne: फक्ल्याण्ड टापुहरू (इज्लास माल्भिनास) nl: Falklandeilanden nn: Falklandsøyane "no": Falklandsøyene or: ଫଲ୍କଲ୍ୟାଣ୍ଡ ଦ୍ବୀପପୁଞ୍ଜ pl: Falklandy pt: Ilhas Malvinas rm: Inslas dal Falkland rn: Izinga rya Filikilandi ro: Insulele Falkland ru: Фолклендские острова se: Falklandsullot sg: Âzûâ tî Mälüîni si: ෆෝක්ලන්ත දූපත් (අයිලස් මල්වියනාස්) sk: Falklandy sl: Falklandi sn: Zvitsuwa zveFalklands so: Jaziiradaha Fooklaan sr: Фокландска (Малвинска) Острва sv: Falklandsöarna sw: Visiwa vya Falklandi (Islas Malvinas) ta: ஃபாக்லாந்து தீவுகள் (இஸ்லாஸ் மால்வினஸ்) te: ఫాక్ లేండ్ దీవులు (ఇస్లాస్ మాల్వినాస్) th: หมู่เกาะฟอล์กแลนด์ ti: የፎልክላንድ ደሴቶች tl: Kapuluang Falkland (Kapuluang Malvinas) to: ʻOtumotu Fokuleni tr: Falkland Adaları tt: Фолкленд утраулары uk: Фолклендські острови ur: فاکلینڈ آئلینڈز (ازلاس مالوینس) vi: Quần đảo Falkland yo: Orílẹ́ède Etikun Fakalandi zh: 福克兰群岛 zu: i-Falkland Islands (i-Islas Malvinas) ================================================ FILE: settings/country-names/fm.yaml ================================================ name: default: Micronesia af: Mikronesië ar: ميكرونيسيا be: Мікранэзія br: Mikronezia ca: Micronèsia cs: Mikronésie da: Mikronesien de: Föderierte Staaten von Mikronesien en: Federated States of Micronesia eo: Mikronezio es: Estados Federados de Micronesia et: Mikroneesia fa: میکرونزی fi: Mikronesia fr: États fédérés de Micronésie fy: Mikroneezje ga: An Mhicrinéis gd: Na Meanbh Eileanan he: מיקרונזיה hr: Mikronezija hu: Mikronézia ia: Micronesia id: Mikronesia io: Mikronezia is: Míkrónesía lb: Mikronesien li: Micronesië lt: Mikronezija lv: Mikronēzija mn: Микронези nl: Micronesia "no": Mikronesiaføderasjonen oc: Estats Federats de Micronesia pl: Mikronezja ru: Федеративные Штаты Микронезии se: Mikronesia sk: Mikronézia sl: Mikronezija sr: Микронезија sv: Mikronesiens federerade stater ta: மைக்குரோனீசியக் கூட்டு நாடுகள் th: ประเทศไมโครนีเซีย tr: Mikronezya uk: Федеративні Штати Мікронезії vi: Liên bang Micronesia vo: Smala-Seanuäns zh: 密克罗尼西亚 / 密克羅尼西亞 ================================================ FILE: settings/country-names/fo.yaml ================================================ name: default: Føroyar af: Faroëreilande am: ፋሮ ደሴቶች an: Islas Feroe ar: جزر فارو az: Farer adaları be: Фарэрскія астравы bg: Ферьорски острови bi: Faroe aelan br: Faero bs: Farska ostrva ca: Illes Fèroe ce: Фарерийн гІайренаш cs: Faerské ostrovy cy: Føroyar da: Færøerne de: Färöer dv: ފަރޮއޭ ޖަޒީރާ ee: Faroe Islands el: Νήσοι Φερόες en: Faroe Islands eo: Ferooj es: Islas Feroe et: Fääri saared eu: Faroeak fa: جزایر فارو fi: Färsaaret fo: Føroyar fr: Îles Féroé fy: Faeröer ga: Oileáin Fharó gd: Na h-Eileanan Fàro gl: Illas Feroe gu: ફરો દ્વિપસમૂહ gv: Ellanyn ny Geyrragh he: איי פארו hi: फ़रो द्वीपसमूह hr: Ferojski Otoci hu: Feröer hy: Ֆարերյան կղզիներ ia: Insulas Feroe id: Kepulauan Faroe io: Faero is: Færeyjar it: Fær Øer ja: フェロー諸島 jv: Kapuloan Faroe ka: ფარერის კუნძულები kk: Фарер аралдары kl: Savalimmiut ko: 페로 제도 ku: Giravên Feroe kv: Фарер діяс kw: Ynysow Faroe la: Faeroae insulae li: Faeröer lt: Farerai lv: Fēru Salas mi: Moutere Faroe mk: Фарски Острови mn: Фарерын арлууд mr: फेरो द्वीपसमूह ms: Kepulauan Faroe mt: Gżejjer Faroe nb: Færøyene ne: फरोइ टापु nl: Faeröer nn: Færøyane "no": Færøyene oc: Illas Feròe os: Фареры сакъадæхтæ pl: Wyspy Owcze pt: Ilhas Feroe qu: Pharuy rm: Inslas Feroe ro: Insulele Feroe ru: Фарерские острова rw: Ibirwa bya Farowe se: Fearsullot sh: Farski Otoci si: ෆාරෝ දිවයින් sk: Faerské ostrovy sl: Ferski otoki so: Jasiiradaha Feroe sq: Ishujt Faroe sr: Фарска Острва su: Kapuloan Faroe sv: Färöarna sw: Visiwa vya Faroe ta: பரோயே தீவுகள் tg: Ҷазираҳои Фаро th: หมู่เกาะแฟโร tl: Kapuluang Peroe tr: Faroe Adaları tt: Фарер утраулары uk: Фарерські острови ur: جزائرفارو vi: Quần đảo Faroe wo: Duni Faarow yo: Àwọn Erékùṣù Fàróè zh: 法罗群岛 ================================================ FILE: settings/country-names/fr.yaml ================================================ name: default: France af: Frankryk ak: Frɛnkyeman am: ፈረንሣይ an: Francia ar: فرنسا as: ফ্ৰান্স ay: Phransiya az: Fransa ba: Франция be: Францыя bg: Франция bi: Franis bm: France bn: ফ্রান্স bo: ཧྥ་རན་སི། br: Bro-C'hall bs: Francuska ca: França ce: Франци co: Francia cs: Francie cu: Франкїꙗ cv: Франци cy: Ffrainc da: Frankrig de: Frankreich dv: ފަރަންސޭސިވިލާތް dz: ཕརཱནསི་ ee: France el: Γαλλία en: France eo: Francio es: Francia et: Prantsusmaa eu: Frantzia fa: فرانسه ff: Faransi fi: Ranska fo: Frakland fr: France fy: Frankryk ga: An Fhrainc gd: An Fhraing gl: Francia gn: Hyãsia gu: ફ્રાન્સ gv: Yn Rank ha: Faransa he: צרפת hi: फ़्रान्स hr: Francuska ht: Frans hu: Franciaország hy: Ֆրանսիա ia: Francia id: Perancis ie: Francia io: Francia is: Frakkland it: Francia ja: フランス jv: Prancis ka: საფრანგეთი kg: Fwalansa ki: Ubaranja kk: Франция kl: Frankrigi km: បារាំង kn: ಫ್ರಾನ್ಸ್ ko: 프랑스 ks: فرٛانس ku: Fransa kv: Франция kw: Pow Frynk ky: Франция la: Francia lb: Frankräich lg: Bufalansa li: Frankriek ln: Falansia lo: ປະເທດຝະລັ່ງ lt: Prancūzija lv: Francija mg: Frantsa mi: Wīwī mk: Франција ml: ഫ്രാൻസ് mn: Франц mo: Франца mr: फ्रान्स ms: Perancis mt: Franza my: ပြင်သစ်နိုင်ငံ na: Prant ne: फ्रान्स nl: Frankrijk "no": Frankrike nv: Dáághahii Dineʼé Bikéyah oc: França or: ଫ୍ରାନ୍ସ os: Франц pa: ਫ੍ਰਾਂਸ pl: Francja ps: فرانسه pt: França qu: Ransiya rm: Frantscha rn: Francia ro: Franța ru: Франция rw: Ubufaransa sa: फ्रान्सदेशः sc: Frantza se: Frankriika sg: Farânzi sh: Francuska si: ප්රංශය sk: Francúzsko sl: Francija sm: Farani so: Faransiiska sq: Franca sr: Француска ss: IFulansi st: Fora su: Perancis sv: Frankrike sw: Ufaransa ta: பிரான்சு te: ఫ్రాన్స్ tg: Фаронса th: ประเทศฝรั่งเศส ti: ፈረንሳይ tk: Fransiýa tl: Pransiya to: Falanisē tr: Fransa tt: Франция ty: Farāni ug: فرانسىيە uk: Франція ur: فرانس uz: Fransiya vi: Pháp vo: Fransän wa: France wo: Faraas yi: פראנקרייך yo: Fránsì za: Fazgoz zh: 法國/法国 zu: IFulansi ================================================ FILE: settings/country-names/ga.yaml ================================================ name: default: Gabon af: Gaboen am: ጋቦን an: Gabón ar: الغابون az: Qabon ba: Габон be: Габон bg: Габон bm: Gabon bn: গাবন bo: གེ་བོན། br: Gabon bs: Gabon ca: Gabon ce: Габон cs: Gabon cv: Габон cy: Gabon da: Gabon de: Gabun dv: ގެބޯން dz: གྷེ་བྷོན el: Γκαμπόν en: Gabon eo: Gabono es: Gabón et: Gabon eu: Gabon fa: گابن ff: Gabon fi: Gabon fr: Gabon fy: Gabon ga: An Ghabúin gd: Gabon gl: Gabón gv: Gabon he: גבון hi: गबॉन hr: Gabon ht: Gabon hu: Gabon hy: Գաբոն ia: Gabon id: Gabon ie: Gabon io: Gabon is: Gabon it: Gabon ja: ガボン jv: Gabon ka: გაბონი kg: Ngabu ki: Gabon kk: Габон kn: ಗಬೊನ್ ko: 가봉 ks: گیبان ku: Gabon kw: Gabon la: Gabonia lb: Gabun li: Gabon ln: Gabɔ́ lt: Gabonas lv: Gabona mi: Kāpona mk: Габон ml: ഗാബോൺ mn: Габон mr: गॅबन ms: Gabon mt: Gabon my: ဂါဘွန်နိုင်ငံ na: Gabun ne: गाबोन nl: Gabon "no": Gabon nv: Gabǫ́ǫ́ oc: Gabon or: ଗାବୋନ os: Габон pa: ਗਬਾਨ pl: Gabon ps: ګابن pt: Gabão qu: Gabun rm: Gabun ro: Gabon ru: Габон rw: Gabon sa: गाबोन sc: Gabòn se: Gabon sg: Gaböon sh: Gabon si: ගැබොන් sk: Gabon sl: Gabon sn: Gabon so: Gabon sq: Gaboni sr: Габон ss: IGabhoni st: Gabon su: Gabon sv: Gabon sw: Gabon ta: காபோன் te: గబాన్ tg: Габон th: ประเทศกาบอง tk: Gabon tl: Gabon tr: Gabon ts: Gaboni tt: Габон ug: گابون uk: Габон ur: گیبون uz: Gabon vi: Gabon vo: Gabunän wo: Gaboŋ yi: גאבאן yo: Gàbọ̀n zh: 加蓬 zu: IGaboni ================================================ FILE: settings/country-names/gb.yaml ================================================ short_name: default: UK name: default: United Kingdom ab: Британиа Ду af: Verenigde Koninkryk ak: United Kingdom am: ዩናይትድ ኪንግደም an: Reino Unito ar: المملكة المتحدة as: যুক্তৰাজ্য av: Кӏудабиритан az: Böyük Britaniya ba: Бөйөк Британия be: Вялікабрытанія bg: Обединено кралство Великобритания и Северна Ирландия bh: यूनाइटेड किंगडम bi: Unaeted Kingdom bn: যুক্তরাজ্য bo: དབྱིན་ཇི་མཉམ་འབྲེལ། br: Rouantelezh-Unanet bs: Ujedinjeno Kraljevstvo Velike Britanije i Sjeverne Irske ca: Regne Unit ce: Йоккха Британи ch: Reinu Unidu co: Regnu Unitu cs: Spojené království cu: Вєлика Британїꙗ cv: Аслă Британи cy: Deyrnas Unedig da: Storbritannien de: Vereinigtes Königreich dv: ޔުނައިޓެޑް ކިންގްޑަމް dz: ཡུ་ནའི་ཊེཊ་ཀིང་ཌམ ee: United Kingdom el: Ηνωμένο Βασίλειο en: United Kingdom eo: Unuiĝinta Reĝlando es: Reino Unido et: Suurbritannia eu: Erresuma Batua fa: بریتانیا ff: Biritaani-Mawndi fi: Yhdistynyt kuningaskunta fo: Stóra Bretland fr: Royaume-Uni fy: Feriene Keninkryk ga: An Ríocht Aontaithe gd: An Rìoghachd Aonaichte gl: Reino Unido gn: Tavetã Joaju gu: યુનાઇટેડ કિંગડમ gv: Reeriaght Unnaneysit ha: Birtaniya he: הממלכה המאוחדת hi: यूनाइटेड किंगडम hr: Ujedinjeno Kraljevstvo ht: Wayòm Ini hu: Egyesült Királyság hy: Միացյալ Թագավորություն ia: Regno Unite id: Britania Raya ie: Reyatu Unit ig: Obodoézè Nà Ofú io: Unionita Rejio is: Bretland it: Regno Unito iu: ᑐᓗᐃᑦ ᓄᓈᑦ ja: イギリス jv: Britania Raya ka: გაერთიანებული სამეფო kg: Royaume-Uni kk: Ұлыбритания kl: Tuluit Nunaat km: រាជាណាចក្ររួម kn: ಯುನೈಟೆಡ್ ಕಿಂಗ್‌ಡಂ ko: 영국 ku: Keyaniya Yekbûyî kv: Ыджыд Британия kw: Ruwvaneth Unys ky: Улуу Британия жана Түндүк Ирландия la: Britanniarum Regnum lb: Groussbritannien an Nordirland li: Vereineg Keuninkriek ln: Ingɛlɛ́tɛlɛ lo: ສະຫະຣາຊອານາຈັກ lt: Jungtinė Karalystė lv: Apvienotā Karaliste mg: Fanjakana Mitambatra mi: Kīngitanga Kotahi mk: Обединето Кралство ml: യുണൈറ്റഡ് കിങ്ഡം mn: Их Британи mr: युनायटेड किंग्डम ms: United Kingdom mt: Renju Unit my: ယူနိုက်တက်ကင်းဒမ်းနိုင်ငံ na: Ingerand ne: संयुक्त अधिराज्य nl: Verenigd Koninkrijk "no": Storbritannia nv: Tótaʼ Dineʼé Bikéyah oc: Reialme Unit om: Yunaayitid Kingidem or: ଯୁକ୍ତରାଜ୍ୟ os: Стыр Британи pa: ਸੰਯੁਕਤ ਬਾਦਸ਼ਾਹੀ pl: Wielka Brytania ps: بريتانيا pt: Reino Unido qu: Hukllachasqa Qhapaq Suyu rm: Reginavel Unì ro: Regatul Unit al Marii Britanii și al Irlandei de Nord ru: Великобритания rw: Ubwongereza sa: संयुक्त अधिराज्य sc: Rennu Auniadu sd: گڏيل بادشاھت se: Ovttastuvvan gonagasriika sh: Ujedinjeno Kraljevstvo si: එක්සත් රාජධානිය sk: Spojené kráľovstvo sl: Združeno kraljestvo Velike Britanije in Severne Irske sm: Peretānia sn: United Kingdom so: Midowga boqortooyada Britan sq: Mbretëria e Bashkuar e Britanisë dhe Irlandës së Veriut sr: Уједињено Краљевство ss: United Kingdom su: Britania sv: Storbritannien sw: Ufalme wa Muungano ta: ஐக்கிய இராச்சியம் te: యునైటెడ్ కింగ్‌డమ్ tg: Подшоҳии Муттаҳида th: สหราชอาณาจักร tk: Birleşen Patyşalyk tl: Nagkakaisang Kaharian to: Pilitānia tr: Birleşik Krallık tt: Бөекбритания tw: United Kingdom ty: Paratāne ug: بۈيۈك بېرىتانىيە uk: Велика Британія ur: برطانیہ uz: Birlashgan Qirollik vi: Vương quốc Anh vo: Regän Pebalöl wa: Rweyåme-Uni wo: Nguur-Yu-Bennoo yi: פאראייניגטע קעניגרייך yo: Ilẹ̀ọba Aṣọ̀kan za: Yinghgoz zh: 英国/英國 zu: Umbuso Ohlangeneyo ================================================ FILE: settings/country-names/gd.yaml ================================================ name: default: Grenada ar: غرينادا be: Грэнада br: Grenada cs: Grenada da: Grenada de: Grenada el: Γρενάδα en: Grenada eo: Grenado es: Granada fa: گرنادا fi: Grenada fr: Grenade fy: Grenada ga: Greanáda gd: Greanada he: גרנדה hr: Grenada hu: Grenada is: Grenada it: Grenada ja: グレナダ la: Granata lb: Granada lv: Grenāda mn: Гренада nl: Grenada "no": Grenada pl: Granada pt: Granada ru: Гренада se: Grenada sl: Grenada sv: Grenada ta: கிரெனடா th: ประเทศเกรเนดา uk: Гренада vo: Grenadeän zh: 格林纳达 ================================================ FILE: settings/country-names/ge.yaml ================================================ name: default: საქართველო ab: Қырҭтәыла af: Georgië am: ጂዮርጂያ an: Cheorchia ar: جورجيا av: Гуржи az: Gürcüstan be: Грузія bg: Грузия bn: জর্জিয়া (রাষ্ট্র) bo: འཇོར་ཇི་ཡ། (རྒྱལ་ཁབ།) br: Jorjia (bro) bs: Gruzija ca: Geòrgia ce: Гуьржийчоь cs: Gruzie cu: Гєѡргїꙗ cv: Грузи cy: Georgia da: Georgien de: Georgien dv: ޖޯޖިޔާ (ޤައުމު) dz: ཇཽ་ཇཱ ee: Georgia el: Γεωργία en: Georgia eo: Kartvelio es: Georgia et: Gruusia eu: Georgia fa: گرجستان fi: Georgia fo: Georgia (land) fr: Géorgie fy: Geörgje ga: An tSeoirsia gd: A' Chairtbheil gl: Xeorxia - საქართველო gu: જ્યોર્જીયા (દેશ) gv: Yn Çhorshey he: גאורגיה hi: जॉर्जिया hr: Gruzija ht: Jeoji hu: Grúzia hy: Վրաստան ia: Georgia id: Georgia ie: Georgia io: Gruzia is: Georgía it: Georgia ja: グルジア jv: Georgia ka: საქართველო kk: Грузия kn: ಜಾರ್ಜಿಯ ko: 조지아 (국가) ku: Gurcistan kv: Грузия kw: Pow Grouzi ky: Грузия la: Georgia lb: Georgien li: Georgië lt: Gruzija lv: Gruzija mi: Hōria mk: Грузија ml: ജോർജ്ജിയ mn: Гүрж mr: जॉर्जिया ms: Georgia mt: Ġeorġja my: ဂျော်ဂျီယာနိုင်ငံ na: Djiordjiya ne: जर्जिया (देश) nl: Georgië "no": Georgia nv: Jóojah (Kéyah) oc: Georgia (Caucàs) or: ଜର୍ଜିଆ os: Гуырдзыстон pl: Gruzja ps: ګرجستان pt: Geórgia qu: Kartulsuyu ro: Georgia ru: Грузия rw: Geworugiya sc: Georgia se: Georgia sh: Gruzija si: ජෝර්ජියා (රට) sk: Gruzínsko sl: Gruzija so: Joorjiya sq: Gjeorgjia sr: Грузија ss: IJojiya su: Géorgia sv: Georgien sw: Georgia (nchi) ta: சியார்சியா (நாடு) te: జార్జియా (దేశం) tg: Гурҷистон th: ประเทศจอร์เจีย tk: Gruziýa tl: Heorhiya tr: Gürcistan tt: Гөрҗистан ug: گرۇزىيە uk: Грузія ur: جارجیا uz: Gurjiston vi: Gruzia vo: Grusiyän wo: Jeoorji yi: גרוזיע yo: Georgia zh: 格鲁吉亚 ================================================ FILE: settings/country-names/gg.yaml ================================================ name: default: Guernsey af: Guernsey an: Guernési ar: غيرنزي az: Gernsi be: Гернсi bg: Гърнси br: Gwernenez bs: Guernsey ca: Guernsey cs: Guernsey cy: Ynys y Garn da: Guernsey de: Guernsey dv: ގުއާންސޭ el: Γουερνισία en: Guernsey eo: Gernezejo es: Guernsey et: Guernsey eu: Guernesey fa: گرنزی fi: Guernsey fr: Guernesey fy: Guernsey ga: Geansaí gd: Guernsey gl: Guernsey gv: Guernsey he: גרנזי hi: ग्वेर्नसे hr: Guernsey hu: Guernsey hy: Գերնսի id: Guernsey io: Guernsey is: Guernsey it: Guernsey ja: ガーンジー jv: Guernsey ka: გერნზი kn: ಗುರ್ನ್‌ಸಿ ko: 건지 섬 kw: Gwernenys la: Lisia lb: Guernsey li: Guernsey lt: Gernsis lv: Gērnsija mi: Kōnihi mk: Гернзи mn: Гернси mr: गर्न्सी ms: Guernsey ne: गुर्न्जी nl: Guernsey "no": Guernsey oc: Guernesey os: Гернси pa: ਗਰਨਜ਼ੇ pl: Guernsey pt: Guernsey ro: Guernsey ru: Гернси rw: Gwasi sh: Guernsey sk: Guernsey sl: Guernsey sq: Guernsey sr: Гернзи su: Guernsey sv: Guernsey sw: Guernsey ta: குயெர்ன்சி tg: Гернси th: เกิร์นซีย์ tl: Gernesey tr: Guernsey tt: Гернси uk: Гернсі ur: گرنزی vi: Guernsey yo: Guernsey zh: 根西島 ================================================ FILE: settings/country-names/gh.yaml ================================================ name: default: Ghana af: Ghana ak: Ghana am: ጋና an: Ghana ar: غانا az: Qana ba: Гана be: Гана bg: Гана bm: Gana bn: ঘানা bo: ཀ་ན། br: Ghana bs: Gana ca: Ghana ce: Гана co: Ghana cs: Ghana cv: Гана cy: Ghana da: Ghana de: Ghana dv: ގާނާ ee: Ghana el: Γκάνα en: Ghana eo: Ganao es: Ghana et: Ghana eu: Ghana fa: غنا ff: Gana fi: Ghana fo: Gana fr: Ghana fy: Gana ga: Gána gd: Gàna gl: Gana gu: ઘાના gv: Yn Ghaney he: גאנה hi: घाना hr: Gana ht: Gana hu: Ghána hy: Գանա ia: Ghana id: Ghana ie: Ghana ig: Ghana io: Ghana is: Gana it: Ghana ja: ガーナ jv: Ghana ka: განა kg: Gana ki: Ghana kk: Гана kn: ಘಾನಾ ko: 가나 ku: Gana kw: Ghana ky: Гана la: Gana lb: Ghana li: Ghana ln: Ghana lt: Gana lv: Gana mg: Ghana mi: Kāna mk: Гана ml: ഘാന mn: Гана mr: घाना ms: Ghana mt: Gana my: ဂါနာနိုင်ငံ na: Gana ne: घाना nl: Ghana "no": Ghana nv: Gáana oc: Ghana or: ଘାନା os: Ганæ pa: ਘਾਨਾ pl: Ghana ps: ګانا pt: Gana qu: Gana rm: Ghana ro: Ghana ru: Гана rw: Gana sa: घाना sc: Ghana se: Ghana sg: Ganäa sh: Gana sk: Ghana sl: Gana sn: Ghana so: Gaana sq: Gana sr: Гана ss: IGana su: Ghana sv: Ghana sw: Ghana ta: கானா te: ఘనా tg: Гана th: ประเทศกานา tk: Gana tl: Ghana tr: Gana ts: Ghana tt: Гана tw: Ghana ug: گانا جۇمھۇرىيىتى uk: Гана ur: گھانا uz: Gana vi: Ghana vo: Ganän wa: Gana wo: Gana yi: גהאנע yo: Ghánà zh: 加纳 zu: IGana ================================================ FILE: settings/country-names/gi.yaml ================================================ name: default: Gibraltar af: Gibraltar an: Chibraltar ar: جبل طارق az: Cəbəli-Tariq ba: Гибралтар be: Гібралтар bg: Гибралтар bn: জিব্রাল্টার br: Jibraltar bs: Gibraltar ca: Gibraltar cs: Gibraltar cv: Гибралтар cy: Gibraltar da: Gibraltar de: Gibraltar dv: ޖަބަލްޠާރިޤު el: Γιβραλτάρ en: Gibraltar eo: Ĝibraltaro es: Gibraltar et: Gibraltar eu: Gibraltar fa: جبل طارق fi: Gibraltar fo: Gibraltar fr: Gibraltar fy: Gibraltar ga: Giobráltar gd: Giobraltair gl: Xibraltar gv: Gibraaltar he: גיברלטר hi: जिब्राल्टर hr: Gibraltar hu: Gibraltár hy: Ջիբրալթար ia: Gibraltar id: Gibraltar ie: Gibraltar io: Gibraltar is: Gíbraltar it: Gibilterra ja: ジブラルタル jv: Gibraltar ka: გიბრალტარი kk: Гибралтар ko: 지브롤터 ku: Cîbraltar kw: Jibraltar la: Gibraltar lb: Gibraltar li: Gibraltar lt: Gibraltaras lv: Gibraltārs mi: Kāmaka mk: Гибралтар mn: Гибралтар mr: जिब्राल्टर ms: Gibraltar my: ဂျီဘရော်လ်တာ ne: गिब्राल्टार nl: Gibraltar "no": Gibraltar oc: Gibartar or: ଜିବ୍ରାଲେଟର os: Гибралтар pa: ਜਿਬਰਾਲਟਰ pl: Gibraltar pt: Gibraltar ro: Gibraltar ru: Гибралтар rw: Giburalitari sh: Gibraltar sk: Gibraltár sl: Gibraltar so: Jibraltaar sq: Gjibraltari sr: Гибралтар su: Gibraltar sv: Gibraltar sw: Gibraltar ta: ஜிப்ரால்ட்டர் tg: Гибралтар th: ยิบรอลตาร์ tl: Hibraltar tr: Cebelitarık tt: Гибралтар uk: Гібралтар ur: جبل الطارق vi: Gibraltar wa: Djibraltar wo: Jibraltaar yi: גיבראלטאר yo: Gibraltar zh: 直布罗陀 ================================================ FILE: settings/country-names/gl.yaml ================================================ name: default: Kalaallit Nunaat af: Groenland am: ግሪንላንድ an: Gronlandia ar: غرينلاند az: Qrenlandiya be: Грэнландыя bg: Гренландия bn: গ্রিনল্যান্ড bo: གེ་རེན་ལ br: Greunland bs: Grenland ca: Grenlàndia cs: Grónsko cv: Гренланди cy: Yr Ynys Las da: Grønland de: Grönland dv: ގުރީންލޭންޑު el: Γροιλανδία en: Greenland eo: Gronlando es: Groenlandia et: Gröönimaa eu: Groenlandia fa: گرینلند fi: Grönlanti fo: Grønland fr: Groenland fy: Grienlân ga: An Ghraonlainn gd: A' Ghraonlainn gl: Groenlandia gn: Kyoẽlándia gv: Greenlynn he: גרינלנד hi: ग्रीनलैण्ड hr: Grenland hu: Grönland hy: Գրենլանդիա ia: Groenlandia id: Greenland ie: Grenland io: Grenlando is: Grænland it: Groenlandia iu: ᐊᑯᑭᑦᑐᑦ ja: グリーンランド jv: Greenland ka: გრენლანდია kk: Гренландия kl: Kalaallit Nunaat kn: ಗ್ರೀನ್‍ಲ್ಯಾಂಡ್ ko: 그린란드 ku: Grînlenda kv: Гренландия kw: Greunland la: Groenlandia lb: Grönland li: Greunland ln: Groenlandi lt: Grenlandija lv: Grenlande mg: Groenlanda mi: Greenland mk: Гренланд ml: ഗ്രീൻലാൻഡ് mn: Гренланд mr: ग्रीनलँड ms: Greenland my: ဂရင်းလန်ကျွန်း ne: ग्रीनल्याण्ड nl: Groenland "no": Grønland nv: Haʼaʼaahjí Hakʼaz Dineʼé Bikéyah oc: Groenlàndia or: ଗ୍ରୀନଲାଣ୍ଡ os: Гренланди pa: ਗਰੀਨਲੈਂਡ pl: Grenlandia pt: Gronelândia qu: Kalalit Nunat ro: Groenlanda ru: Гренландия rw: Goronulande se: Kalaallit Nunaat sh: Grenland sk: Grónsko sl: Grenlandija so: Griinland sq: Groenlanda sr: Гренланд su: Lemah Héjo sv: Grönland sw: Greenland ta: கிறீன்லாந்து te: గ్రీన్‌లాండ్ tg: Гренландия th: กรีนแลนด์ tl: Lupanlunti tr: Grönland tt: Гренландия ug: گرېنلاندىيە uk: Ґренландія ur: گرین لینڈ vi: Greenland vo: Gröneän wo: Groenlaand yi: גרינלאנד yo: Grínlándì zh: 格陵兰 ================================================ FILE: settings/country-names/gm.yaml ================================================ name: default: Gambia af: Gambië am: ጋምቢያ an: Gambia ar: غامبيا az: Qambiya ba: Гамбия be: Гамбія bg: Гамбия bm: Gambia bn: গাম্বিয়া bo: གེམ་བྷི་ཡ། br: Gambia bs: Gambija ca: Gàmbia ce: Гамби cs: Gambie cv: Гамби cy: Y Gambia da: Gambia de: Gambia dv: ގެމްބިއާ dz: གྷེམ་བི་ཡ ee: Gambia el: Γκάμπια en: The Gambia eo: Gambio es: Gambia et: Gambia eu: Gambia fa: گامبیا ff: Gammbi fi: Gambia fo: Gambia fr: Gambie fy: Gambia ga: An Ghaimbia gd: A' Ghaimbia gl: Gambia gv: Yn Ghambia he: גמביה hi: गाम्बिया hr: Gambija ht: Ganbi hu: Gambia hy: Գամբիա ia: Gambia id: Gambia ie: The Gambia io: Gambia is: Gambía it: Gambia ja: ガンビア jv: Gambia ka: გამბია kg: Gambia ki: Gambia kk: Гамбия ko: 감비아 ku: Gambiya kw: Gambi ky: Гамбия la: Gambia lb: Gambia li: Gambia ln: Gambi lt: Gambija lv: Gambija mk: Гамбија ml: ഗാംബിയ mn: Гамби mr: गांबिया ms: Gambia mt: Gambja my: ဂမ်ဘီယာနိုင်ငံ na: Gambiya ne: गाम्बिया nl: Gambia "no": Gambia nv: Géémbiya oc: Gàmbia or: ଗାମ୍ବିଆ os: Гамби pa: ਗਾਂਬੀਆ pl: Gambia ps: ګامبيا pt: Gâmbia qu: Gambya rm: Gambia ro: Gambia ru: Гамбия rw: Gambiya sa: गाम्बिया sc: Gàmbia sd: گيمبيا se: Gambia sg: Gambïi sh: Gambija sk: Gambia sl: Gambija sn: Gambia so: Gambia sq: Gambia sr: Гамбија ss: IGambiya st: Gambia su: Gambia sv: Gambia sw: Gambia ta: காம்பியா te: గాంబియా tg: Гамбия th: ประเทศแกมเบีย tk: Gambiýa tl: Ang Gambiya tr: Gambiya ts: Gambiya tt: Гамбия ug: گامبىيە uk: Гамбія ur: گیمبیا uz: Gambia vi: Gambia vo: Gambiyän wo: Gàmbi yi: די גאמביע yo: Gámbíà zh: 冈比亚 zu: IGambia ================================================ FILE: settings/country-names/gn.yaml ================================================ name: default: Guinée af: Guinee am: ጊኔ an: Guinea ar: غينيا az: Qvineya ba: Гвинея be: Гвінея bg: Гвинея bm: Gine bn: গিনি bo: གི་ནེ། br: Ginea bs: Gvineja ca: República de Guinea ce: Гвине cs: Guinea cv: Гвиней cy: Guinée da: Guinea de: Guinea dv: ގީނިއާ ee: Guinea el: Γουινέα en: Guinea eo: Gvineo es: Guinea et: Guinea eu: Ginea fa: گینه ff: Gine fi: Guinea fo: Guinea fr: Guinée fy: Guinee ga: An Ghuine gd: Gini gl: Guinea gv: Yn Ghuinea ha: Gine he: גינאה hi: गिनी hr: Gvineja ht: Gine hu: Guinea hy: Գվինեա ia: Guinea id: Guinea ie: Guinea io: Guinea is: Gínea it: Guinea ja: ギニア jv: Guinea ka: გვინეა kg: Ginea kk: Гвинея ko: 기니 ku: Gîne kw: Gyni ky: Гвинея la: Guinea lb: Guinea li: Guinee ln: Gine-Konakry lt: Gvinėja lv: Gvineja mi: Kini mk: Гвинеја ml: ഗിനി mn: Гвиней mr: गिनी ms: Guinea my: ဂီနီနိုင်ငံ na: Gini ne: गिनी nl: Guinee "no": Guinea nv: Gíní oc: Guinèa or: ଗିନି os: Гвиней pa: ਗਿਨੀ pl: Gwinea pt: Guiné qu: Khiniya ro: Guineea ru: Гвинея rw: Gineya sa: गिनी sc: Guinea se: Guinea sg: Ginëe sh: Gvineja sk: Guinea sl: Gvineja sm: Guinea sn: Guinea so: Guinea sq: Guinea sr: Гвинеја ss: IGiniya st: Guinea su: Guinéa sv: Guinea sw: Guinea ta: கினி tg: Гвинея th: ประเทศกินี tk: Gwineýa tl: Ginea tr: Gine ts: Gineya tt: Гвинея ug: ۋىنېيە uk: Ґвінея ur: جمہوریہ گنی uz: Gvineya vi: Guinée vo: Gineyän wo: Gine yi: גינע yo: Guinea zh: 几内亚 zu: IGini ================================================ FILE: settings/country-names/gq.yaml ================================================ name: default: Guinea Ecuatorial af: Ekwatoriaal-Guinee ak: Gini Ikuweta am: ኢኳቶሪያል ጊኔ an: Guinea Equatorial ar: غينيا الاستوائية az: Ekvatorial Qvineya ba: Экватор Гвинеяһы be: Экватарыяльная Гвінея bg: Екваториална Гвинея bh: भूमध्यरेखीय गिनी bm: Cɛmajan Gine bn: বিষুবীয় গিনি bo: ཨི་ཁུའ་ཊོ་རལ་གི་ནེ། br: Ginea ar C'heheder bs: Ekvatorijalna Gvineja ca: Guinea Equatorial ce: Дуьнена асанан Гвине co: Guinea Equatoriale cs: Rovníková Guinea cv: Экваториаллă Гвиней cy: Gini Gyhydeddol da: Ækvatorialguinea de: Äquatorialguinea dv: އިކުއެޓޯރިއަލް ގިނީ dz: ཨེ་ཀུ་ཊོ་རེལ་ གི་ནི ee: Gini Ekwatoria el: Ισημερινή Γουινέα en: Equatorial Guinea eo: Ekvatora Gvineo es: Guinea Ecuatorial et: Ekvatoriaal-Guinea eu: Ekuatore Ginea fa: گینه استوایی ff: Gine Ekwatoriyal fi: Päiväntasaajan Guinea fo: Ekvatorguinea fr: Guinée équatoriale fy: Ekwatoriaal-Guinee ga: an Ghuine Mheánchriosach gd: Gini Mheadhan-Chriosach gl: Guinea Ecuatorial gu: ઇક્વેટોરિયલ ગિની gv: Guinea Chryss ny Cruinney ha: Gini Ikwatoriya he: גינאה המשוונית hi: भूमध्यरेखीय गिनी hr: Ekvatorska Gvineja ht: Gine ekwateryal hu: Egyenlítői-Guinea hy: Հասարակածային Գվինեա ia: Guinea Equatorial id: Guinea Khatulistiwa ie: Equatorial Guinéa ig: Equatorial Guinea io: Equatorala Guinea is: Miðbaugs-Gínea it: Guinea Equatoriale ja: 赤道ギニア jv: Guinea Khatulistiwa ka: ეკვატორული გვინეა kg: Ginea ya Ekwatelo ki: Ginekweta kk: Экваторлық Гвинея km: ហ្គីណេអេក្វាទ័រ kn: ವಿಷುವದ್ರೇಖೆಯ ಗಿನಿ ko: 적도 기니 ks: اِکوِٹورِیَل گِنی ku: Gîneya Rojbendî kw: Gyni Ekwadoriel la: Guinea Aequatorensis lb: Equatorialguinea lg: Equatorial Guinea li: Equatoriaal Guinee ln: Gine-Ekwatorial lo: ອີຄົວໂທເລຍກີເນຍ lt: Pusiaujo Gvinėja lv: Ekvatoriālā Gvineja mg: Ginea Ekoatorialy mi: Kini Ekuatoria mk: Екваторска Гвинеја ml: ഇക്വറ്റോറിയൽ ഗിനി mn: Экваторын Гвиней mr: इक्वेटोरीयल गिनी ms: Guinea Khatulistiwa mt: Gwinea Ekwatorjali my: အီကွေတာဂီနီနိုင်ငံ na: Gini t Ekwador ne: इक्वेटोरियल गिनी nl: Equatoriaal Guinee "no": Ekvatorial-Guinea nv: Gíní Nahasdzáán Ałníiʼgi Siʼánígíí oc: Guinèa Eqüatoriala om: Iqu’aatoriyaal Giinii or: ବିଷୁବରେଖୀୟ ଗିନି os: Экваториалон Гвиней pa: ਭੂ-ਮੱਧ ਰੇਖਾਈ ਗਿਨੀ pl: Gwinea Równikowa ps: اېکواټوريال ګوينا pt: Guiné Equatorial qu: Chawpipacha Khiniya rm: Guinea Equatoriala ro: Guineea Ecuatorială ru: Экваториальная Гвинея rw: Gineya Ekwatoriyale sc: Guinea Ecuadoriale se: Ekvatoriála Guinea sg: Ginëe tî Ekuatëre sh: Ekvatorijalna Gvineja si: සමක ගිනියාව sk: Rovníková Guinea sl: Ekvatorialna Gvineja sm: Kini Ekuatoria sn: Equatorial Guinea so: Ikweetiga Guinea sq: Guinea Ekuatoriale sr: Екваторијална Гвинеја st: Equatorial Guinea su: Guinéa Khatulistiwa sv: Ekvatorialguinea sw: Guinea ya Ikweta ta: எக்குவடோரியல் கினி te: ఎక్వేటోరియాల్ గినియా tg: Гвинеяи Истивоӣ th: อิเควทอเรียลกินี ti: ኢኳቶሪያል ጊኒ tk: Ekwatorial Gwineýa tl: Gineang Ekwatoriyal to: ʻEkueta Kini tr: Ekvator Ginesi ts: Gineya ya le Nkaveni tt: Экваториа́ль Гвине́я ug: ئېكۋاتور گۋىنېيىسى uk: Екваторіа́льна Ґвіне́я ur: استوائی گنی uz: Ekvatorli Gvineya vi: Guinea Xích Đạo vo: Kveatora-Gineyän wo: Gineg yamoo yi: עקוואטארישע גינע yo: Guinea Alágedeméjì zh: 赤道几内亚 zu: iGini Enkabazwe ================================================ FILE: settings/country-names/gr.yaml ================================================ name: default: Ελλάς ab: Барзентәыла af: Griekeland am: ግሪክ an: Grecia ar: اليونان ay: Grisya az: Yunanıstan ba: Греция be: Грэцыя bg: Гърция bi: Greece bn: গ্রিস bo: ཀེ་རི་སི། br: Gres bs: Grčka ca: Grècia ce: Греци co: Grecia cs: Řecko cu: Грьци cv: Греци cy: Gwlad Groeg da: Grækenland de: Griechenland dv: ޔޫނާން dz: གྲྀས ee: Greece el: Ελλάς en: Greece eo: Grekio es: Grecia et: Kreeka eu: Grezia fa: یونان fi: Kreikka fo: Grikkaland fr: Grèce fy: Grikelân ga: An Ghréig gd: A' Ghrèig gl: Grecia gn: Gyresia gu: ગ્રીસ gv: Yn Ghreag he: יוון hi: यूनान hr: Grčka ht: Grès hu: Görögország hy: Հունաստան ia: Grecia id: Yunani ie: Grecia io: Grekia is: Grikkland it: Grecia ja: ギリシャ jv: Yunani ka: საბერძნეთი kg: Gelesi ki: Ngiriki kk: Грекия kl: Grækerit Nunaat kn: ಗ್ರೀಸ್ ko: 그리스 ku: Yewnanistan kv: Эллада kw: Pow Grek ky: Греция la: Graecia lb: Griicheland lg: Buyonaani li: Griekeland ln: Gresi lt: Graikija lv: Grieķija mg: Grisy mi: Kirihi mk: Грција ml: ഗ്രീസ് mn: Грек mr: ग्रीस ms: Greece mt: Greċja my: ဂရိနိုင်ငံ na: Grit ne: ग्रीस nl: Griekenland "no": Hellas nv: Gwíík Dineʼé Bikéyah oc: Grècia or: ଗ୍ରୀସ os: Грекъ pa: ਯੂਨਾਨ pl: Grecja ps: يونان pt: Grécia qu: Grisya rm: Grezia rn: Grecia ro: Grecia ru: Греция rw: Ubugereki sa: यवनदेशः sc: Grèghia se: Greika sh: Grčka si: ග්‍රීසිය sk: Grécko sl: Grčija sm: Kuliti so: Giriig sq: Greqia sr: Грчка ss: IGrikhi su: Yunani sv: Grekland sw: Ugiriki ta: கிரேக்க நாடு te: గ్రీస్ tg: Юнон th: ประเทศกรีซ tk: Gresiýa tl: Gresya tr: Yunanistan tt: Греция ug: Grétsiye uk: Греція ur: یونان uz: Yunoniston vi: Hy Lạp vo: Grikän wo: Girees yi: גריכנלאנד yo: Gríìsì zh: 希腊 zu: IGreki ================================================ FILE: settings/country-names/gs.yaml ================================================ name: default: South Georgia and the South Sandwich Islands af: Suid-Georgië en die Suidelike Sandwich-eilande ar: جزر جورجيا الجنوبية وساندويتش الجنوبية az: Cənubi Georgiya və Cənubi Sandviç adaları be: Паўднёвая Джорджыя і Паўднёвыя Сандвічавы астравы bg: Южна Джорджия и Южни Сандвичеви острови br: Inizi Georgia ar Su hag Inizi Sandwich ar Su bs: Južna Džordžija i Južna Sendvička Ostrva ca: Illes Geòrgia del Sud i Sandwich del Sud cs: Jižní Georgie a Jižní Sandwichovy ostrovy da: South Georgia og South Sandwich Islands de: Südgeorgien und die Südlichen Sandwichinseln el: Νήσοι Νότια Γεωργία και Νότιες Σάντουιτς en: South Georgia and the South Sandwich Islands eo: Sud-Georgio kaj Sud-Sandviĉinsuloj es: Islas Georgias del Sur y Sandwich del Sur et: Lõuna-Georgia ja Lõuna-Sandwichi saared eu: Hegoaldeko Georgiak eta Hegoaldeko Sandwich uharteak fa: جزایر جورجیای جنوبی و ساندویچ جنوبی fi: Etelä-Georgia ja Eteläiset Sandwichsaaret fo: Suðursandwichoyggjar og Suðurgeorgia fr: Géorgie du Sud-et-les Îles Sandwich du Sud fy: Súd-Georgje en de Súdlike Sandwicheilannen gd: Seòirsia-a-Deas is na h-Eileanan Shandwich-a-Deas gl: Illas Xeorxia do Sur e Sandwich do Sur he: איי ג'ורג'יה הדרומית ואיי סנדוויץ' הדרומיים hi: दक्षिण जॉर्जिया एवं दक्षिण सैंडविच द्वीप समूह hr: Južna Georgija i Otočje Južni Sandwich hu: Déli-Georgia és Déli-Sandwich-szigetek id: Georgia Selatan dan Kepulauan Sandwich Selatan is: Suður-Georgía og Suður-Sandvíkureyjar it: Georgia del Sud e Isole Sandwich Australi ja: サウスジョージア・サウスサンドウィッチ諸島 ko: 사우스조지아 사우스샌드위치 제도 kw: Jeorji Dheghow hag Ynysow Sandwich Deghow lt: Pietų Džordžija ir Pietų Sandvičo salos lv: Dienviddžordžija un Dienvidsendviču salas mk: Јужна Џорџија и Јужни Сендвички Острови mr: साउथ जॉर्जिया व साउथ सँडविच द्वीपसमूह ms: Georgia Selatan dan Kepulauan Sandwich Selatan nb: Sør-Georgia og Sør-Sandwichøyene nl: Zuid-Georgia en de Zuidelijke Sandwicheilanden nn: Sør-Georgia og Sør-Sandwichøyane "no": Sør-Georgia og Sør-Sandwichøyene os: Хуссар Джорджи æмæ Хуссар Сандвичы сакъадæхтæ pl: Georgia Południowa i Sandwich Południowy pt: Ilhas Geórgia do Sul e Sanduíche do Sul ro: Georgia de Sud şi Insulele Sandwich de Sud ru: Южная Георгия и Южные Сандвичевы Острова se: Lulli Georgia ja Lulli Sandwich-sullot sk: Južná Georgia a Južné Sandwichove ostrovy sl: Južna Georgija in Južni Sandwichevi otoki sq: Gjeorgjia Jugore dhe Ishujt Jugorë Sanduiç sr: Јужна Џорџија и Јужна Сендвичка Острва sv: Sydgeorgien och Sydsandwichöarna ta: தெற்கு யோர்சியா மற்றும் தெற்கு சண்ட்விச் தீவுகள் th: เกาะเซาท์จอร์เจียและหมู่เกาะเซาท์แซนด์วิช tr: Güney Georgia ve Güney Sandwich Adaları uk: Південна Джорджія та Південні Сандвічеві острови vi: Quần đảo Nam Georgia và Nam Sandwich wo: Jeoorji gu Bëj-saalum ak Duni Islaand yi Bëj-saalum zh: 南喬治亞和南桑威奇群島 ================================================ FILE: settings/country-names/gt.yaml ================================================ name: default: Guatemala af: Guatemala am: ጓቴማላ an: Guatemala ar: غواتيمالا ay: Watimala az: Qvatemala ba: Гватемала be: Гватэмала bg: Гватемала bm: Gatemala bn: গুয়াতেমালা bo: གུ་ཝེ་ཐི་མ་ལ། br: Guatemala bs: Gvatemala ca: Guatemala ce: Гватемала cs: Guatemala cy: Guatemala da: Guatemala de: Guatemala dv: ގުއަޓެމާލާ ee: Guatemala el: Γουατεμάλα en: Guatemala eo: Gvatemalo es: Guatemala et: Guatemala eu: Guatemala fa: گواتمالا fi: Guatemala fo: Guatemala fr: Guatemala fy: Gûatemala ga: Guatamala gd: Guatamala gl: Guatemala gn: Guatemala gv: Yn Ghuatemaley he: גואטמלה hi: ग्वाटेमाला hr: Gvatemala ht: Gwatemala hu: Guatemala hy: Գվատեմալա ia: Guatemala id: Guatemala ie: Guatemala io: Guatemala is: Gvatemala it: Guatemala ja: グアテマラ jv: Guatemala ka: გვატემალა kk: Гватемала km: ហ្គាតេម៉ាឡា kn: ಗ್ವಾಟೆಮಾಲ ko: 과테말라 ku: Guatemala kw: Gwatemala la: Guatimalia lb: Guatemala li: Guatemala ln: Gwatemala lt: Gvatemala lv: Gvatemala mg: Goatemala mi: Kuatamāra mk: Гватемала ml: ഗ്വാട്ടിമാല mn: Гватемал mr: ग्वातेमाला ms: Guatemala mt: Gwatemala my: ဂွါတီမာလာနိုင်ငံ na: Guatemara ne: ग्वाटेमाला nl: Guatemala "no": Guatemala oc: Guatemala os: Гватемалæ pa: ਗੁਆਤੇਮਾਲਾ pl: Gwatemala ps: ګواتمالا pt: Guatemala qu: Watimala ro: Guatemala ru: Гватемала rw: Gwatemala sa: ग्वाटेमाला se: Guatemala sh: Gvatemala sk: Guatemala sl: Gvatemala sm: Tuatemala so: Guatemala sq: Guatemala sr: Гватемала ss: IGwathamala su: Guatémala sv: Guatemala sw: Guatemala ta: குவாத்தமாலா tg: Гуатемала th: ประเทศกัวเตมาลา tl: Guwatemala tr: Guatemala tt: Гватемала ug: گۋاتېمالا uk: Гватемала ur: گوئٹے مالا uz: Gvatemala vi: Guatemala vo: Gvatemän wo: Guwaatemala yi: גוואטעמאלע yo: Guatẹmálà zh: 危地马拉 zu: Guatemala ================================================ FILE: settings/country-names/gw.yaml ================================================ name: default: Guiné-Bissau af: Guinee-Bissau am: ጊኔ-ቢሳው an: Guinea-Bissau ar: غينيا بيساو az: Qvineya-Bisau ba: Гвинея-Бисау be: Гвінея-Бісау bg: Гвинея-Бисау bm: Gine Bisau bn: গিনি-বিসাউ bo: གི་ནེ་-བི་སའོ། br: Ginea-Bissau bs: Gvineja-Bisau ca: Guinea Bissau ce: Гвине-Бисау co: Guinea-Bissau cs: Guinea-Bissau cv: Гвиней-Бисау cy: Guiné-Bissau da: Guinea-Bissau de: Guinea-Bissau dv: ގީނީ-ބިސާއޫ ee: Guinea-Bissau el: Γουινέα-Μπισσάου en: Guinea-Bissau eo: Gvineo Bisaŭa es: Guinea-Bisáu et: Guinea-Bissau eu: Ginea-Bissau fa: گینه بیسائو ff: Gine-Bisaawo fi: Guinea-Bissau fo: Guinea-Bissau fr: Guinée-Bissau fy: Guinee-Bissau ga: Guine-Bissau gd: Gini-Bissau gl: Guinea-Bisau gv: Yn Ghuinea-Bissau he: גינאה ביסאו hi: गिनी-बिसाऊ hr: Gvineja Bisau ht: Gine-Bisao hu: Bissau-Guinea hy: Գվինեա Բիսաու ia: Guinea-Bissau id: Guinea-Bissau ie: Guinea-Bissau io: Guinea Bisau is: Gínea-Bissá it: Guinea-Bissau ja: ギニアビサウ jv: Guinea-Bissau ka: გვინეა-ბისაუ kg: Ginea-Bisau kk: Гвинея-Бисау ko: 기니비사우 ku: Gîne-Bissau kw: Gyni-Bissaw ky: Гвинея-Бисау la: Guinea Bissaviensis lb: Guinea-Bissau li: Guinee-Bissau ln: Gine-Bisau lt: Bisau Gvinėja lv: Gvineja-Bisava mi: Kini Pihō mk: Гвинеја-Бисау ml: ഗിനി-ബിസൗ mn: Гвиней-Бисау mr: गिनी-बिसाउ ms: Guinea-Bissau my: ဂင်းနီဗစ်ဆောနိုင်ငံ na: Gini-Bitau ne: गिनी-बिसाउ nl: Guinee-Bissau "no": Guinea-Bissau nv: Gíní Bisó oc: Guinèa Bissau os: Гвиней-Бисау pa: ਗਿਨੀ-ਬਿਸਾਊ pl: Gwinea Bissau pt: Guiné-Bissau qu: Khiniya-Wisaw ro: Guineea-Bissau ru: Гвинея-Бисау rw: Gineya-Biso sc: Guinea-Bissau se: Guinea-Bissau sg: Ginëe-Bisau sh: Gvineja Bisau sk: Guinea-Bissau sl: Gvineja Bissau sn: Guinea-Bissau so: Guinea-Bissau sq: Guinea Bisau sr: Гвинеја Бисао ss: IGiniya-Bhasawu st: Guinea-Bissau su: Guinéa-Bissau sv: Guinea-Bissau sw: Guinea Bisau ta: கினி-பிசாவு tg: Гвинея Бисау th: ประเทศกินี-บิสเซา tk: Gwineýa-Bisau tl: Ginea-Bissau tr: Gine-Bissau ts: Guinea-Bissau tt: Гвинея-Бисау ug: گۋىنېيە بىسسائۇ uk: Гвінея-Бісау ur: گنی بساؤ uz: Gvineya-Bissau vi: Guiné-Bissau vo: Gineyän-Bisauän wo: Gine Bisaawóo yi: גינע-ביסאו yo: Guinea-Bissau zh: 幾內亞比索 zu: IGini Bisawu ================================================ FILE: settings/country-names/gy.yaml ================================================ name: default: Guyana af: Guyana am: ጋያና an: Guyana ar: غويانا ay: Wayana az: Qayana ba: Гайана be: Гаяна bg: Гвиана bn: গায়ানা bo: གུ་ཡ་ན། br: Guyana bs: Gvajana ca: Guyana ce: Гайана cs: Guyana cv: Гайана cy: Guyana da: Guyana de: Guyana dv: ގުޔާނާ ee: Guyana el: Γουιάνα en: Guyana eo: Gujano es: Guyana et: Guyana eu: Guyana fa: گویان fi: Guyana fo: Gujana fr: Guyana fy: Guyana ga: An Ghuáin gd: Guiana gl: Güiana gn: Gujána gu: ગુયાના gv: Geeaaney he: גיאנה hi: गयाना hr: Gvajana ht: Giyàn hu: Guyana hy: Գայանա ia: Guyana id: Guyana ie: Guyana io: Guyana is: Gvæjana it: Guyana ja: ガイアナ jv: Guyana ka: გაიანა kk: Гайана kl: Guyana kn: ಗಯಾನ ko: 가이아나 ku: Guyana kw: Gwayana la: Guiana lb: Guyana li: Guyana ln: Gwiana lt: Gajana lv: Gajāna mi: Kaiana mk: Гвајана ml: ഗയാന mn: Гайана mr: गयाना ms: Guyana mt: Gujana my: ဂိုင်ယာနာနိုင်ငံ na: Guyana ne: गुयना nl: Guyana "no": Guyana oc: Guyana or: ଗାଇଓନା os: Гайанæ pa: ਗੁਇਆਨਾ pl: Gujana ps: ګويانا pt: Guiana qu: Wayana ro: Guyana ru: Гайана rw: Giyana sa: गयाना sc: Guyana se: Guyana sh: Gvajana sk: Guyana sl: Gvajana so: Guyana sq: Guajana sr: Гвајана su: Guyana sv: Guyana sw: Guyana ta: கயானா te: గయానా th: ประเทศกายอานา tl: Guyana tr: Guyana tt: Гайана ug: گايانا uk: Гаяна ur: گیانا uz: Gayana vi: Guyana vo: Gvayän wo: Guyaana yi: גויאנע yo: Gùyánà zh: 圭亚那 ================================================ FILE: settings/country-names/hn.yaml ================================================ name: default: Honduras af: Honduras am: ሆንዱራስ an: Honduras ar: هندوراس ay: Honduras az: Honduras ba: Гондурас be: Гандурас bg: Хондурас bm: Honduras bn: হন্ডুরাস bo: ཧོང་དུ་ར་སི། br: Honduras bs: Honduras ca: Hondures ce: Гондурас cs: Honduras cu: Ондоурасъ cy: Honduras da: Honduras de: Honduras dv: ހޮންޑިއުރަސް el: Ονδούρα en: Honduras eo: Honduro es: Honduras et: Honduras eu: Honduras fa: هندوراس fi: Honduras fo: Honduras fr: Honduras fy: Hondueras ga: Hondúras gd: Hondùras gl: Honduras gn: Honduras gu: હોન્ડુરાસ gv: Ny Hondooraghyn he: הונדורס hi: हौण्डुरस hr: Honduras ht: Ondiras hu: Honduras hy: Հոնդուրաս ia: Honduras id: Honduras ie: Honduras io: Honduras is: Hondúras it: Honduras ja: ホンジュラス jv: Honduras ka: ჰონდურასი kk: Гондурас ko: 온두라스 ku: Hondûras kw: Hondouras ky: Гондурас la: Honduria lb: Honduras li: Honduras ln: Honduras lt: Hondūras lv: Hondurasa mk: Хондурас ml: ഹോണ്ടുറാസ് mn: Гондурас mr: होन्डुरास ms: Honduras mt: Ħonduras my: ဟွန်ဒူးရပ်စ်နိုင်ငံ na: Ondurat ne: होण्डुरस nl: Honduras "no": Honduras oc: Honduras or: ହୋଣ୍ଡାରୁସ os: Гондурас pa: ਹਾਂਡੂਰਾਸ pl: Honduras ps: هندوراس pt: Honduras qu: Unduras rn: Honduras ro: Honduras ru: Гондурас rw: Hondurasi sa: हांडूरस se: Honduras sh: Honduras sk: Honduras sl: Honduras sm: Honilagi sq: Hondurasi sr: Хондурас ss: IHondulasi su: Honduras sv: Honduras sw: Honduras ta: ஹொண்டுராஸ் te: హోండురాస్ tg: Ҳондурас th: ประเทศฮอนดูรัส tl: Honduras tr: Honduras tt: Гондурас ug: ھوندۇراس uk: Гондурас ur: ہونڈوراس uz: Gonduras vi: Honduras vo: Hondurän wa: Hondourasse wo: Onduras yi: האנדוראס yo: Họ̀ndúràs zh: 洪都拉斯 zu: Honduras ================================================ FILE: settings/country-names/hr.yaml ================================================ name: default: Hrvatska ab: Хорватиа af: Kroasië am: ክሮኤሽያ an: Croacia ar: كرواتيا az: Xorvatiya ba: Хорватия be: Харватыя bg: Хърватия bi: Croatia bn: ক্রোয়েশিয়া bo: ཁུརོ་ཤི་ཡ། br: Kroatia bs: Hrvatska ca: Cròacia ce: Хорвати cs: Chorvatsko cu: Хръватьска cy: Croatia da: Kroatien de: Kroatien dv: ކުރޮއޭޝިއާ ee: Croatia el: Κροατία en: Croatia eo: Kroatio es: Croacia et: Horvaatia eu: Kroazia fa: کرواسی fi: Kroatia fo: Kroatia fr: Croatie fy: Kroaasje ga: An Chróit gd: Croàisia gl: Croacia gn: Kyoasia gu: ક્રોએશિયા gv: Yn Chroit he: קרואטיה hi: क्रोएशिया hr: Hrvatska ht: Kroasi hu: Horvátország hy: Խորվաթիա ia: Croatia id: Kroasia ie: Croatia io: Kroatia is: Króatía it: Croazia ja: クロアチア jv: Kroasia ka: ხორვატია kg: Kroatia kk: Хорватия kl: Kroatia kn: ಕ್ರೊಯೆಶಿಯ ko: 크로아티아 ku: Kroatya kv: Хорватия kw: Kroati ky: Хорватия la: Croatia lb: Kroatien li: Kroatië ln: Kroasia lt: Kroatija lv: Horvātija mi: Koroātia mk: Хрватска ml: ക്രൊയേഷ്യ mn: Хорват mr: क्रोएशिया ms: Croatia mt: Kroazja my: ခရိုအေးရှားနိုင်ငံ na: Kroaitsiya ne: क्रोएशिया nl: Kroatië "no": Kroatia nv: Kwóóʼad Bikéyah oc: Croàcia or: କ୍ରୋଏସିଆ os: Хорвати pa: ਕ੍ਰੋਏਸ਼ੀਆ pl: Chorwacja ps: کروواسيا pt: Croácia qu: Hurwatsuyu rn: Croacia ro: Croația ru: Хорватия rw: Korowatiya sa: क्रोएशिया sc: Croatzia se: Kroatia sg: Kroasïi sh: Hrvatska sk: Chorvátsko sl: Hrvaška sm: Croatia so: Kroatia sq: Kroacia sr: Хрватска ss: IKhuroshiya st: Kroatia su: Kroasia sv: Kroatien sw: Kroatia ta: குரோவாசியா te: క్రొయేషియా tg: Хорватия th: ประเทศโครเอเชีย tk: Horwatiýa tl: Kroasya tr: Hırvatistan tt: Хорватия ug: كرودىيە uk: Хорватія ur: کروشیا uz: Xorvatiya vi: Croatia vo: Kroasän wa: Crowaceye wo: Korwaasi yi: קראאטיע yo: Kroatíà za: Gwzlozdiya zh: 克羅地亞 ================================================ FILE: settings/country-names/ht.yaml ================================================ name: default: Ayiti af: Haïti am: ሃይቲ an: Haití ar: هايتي ay: Ayti az: Haiti Respublikası be: Гаіці bg: Хаити bm: Ayiti bn: হাইতি bo: ཧའི་ཏི། br: Republik Haiti bs: Haiti ca: Haití ce: Гаити Пачхьалкх co: Haiti cs: Haiti cv: Гаити cy: Haiti da: Haiti de: Haiti dv: ހެއިޓީ ee: Haiti el: Αϊτή en: Haiti eo: Haitio es: Haití et: Haiti eu: Haiti fa: هائیتی fi: Haiti fo: Haiti fr: Haïti fy: Haïty ga: Háítí gd: Haiti gl: Haití - Haïti gu: હૈતી gv: Haiti he: האיטי hi: हैती hr: Haiti ht: Ayiti hu: Haiti hy: Հաիթի ia: Haiti id: Haiti io: Haiti is: Haítí it: Haiti ja: ハイチ jv: Haiti ka: ჰაიტი kg: Ayiti ki: Haiti kk: Гаити ko: 아이티 ku: Haîtî kw: Hayti la: Haitia lb: Haiti li: Haïti ln: Ayiti lt: Haitis lv: Haiti mg: Haiti mk: Хаити ml: ഹെയ്റ്റി mn: Гайти mr: हैती ms: Haiti mt: Ħaiti my: ဟေတီနိုင်ငံ na: Aiti ne: हाइटी nl: Haïti "no": Haiti nv: Héítii oc: Haití (estat) or: ହିଟି os: Гаити pa: ਹੈਤੀ pl: Haiti ps: هایتي pt: Haiti qu: Ayti (mama llaqta) ro: Haiti ru: Гаити rw: Hayiti se: Haiti sh: Haiti si: හෙයිටි sk: Haiti sl: Haiti so: Haiti sq: Haitia sr: Хаити ss: IHayithi su: Haiti sv: Haiti sw: Haiti ta: எயிட்டி th: ประเทศเฮติ tl: Hayti tr: Haiti tt: Гаити ug: ھايتى uk: Гаїті ur: ہیٹی uz: Gaiti vi: Haiti vo: Haitiyän wa: Ayiti wo: Ayiti yi: האיטי yo: Hàítì zh: 海地 ================================================ FILE: settings/country-names/hu.yaml ================================================ name: default: Magyarország ab: Мадиартәыла af: Hongarye am: ሀንጋሪ an: Hongría ar: المجر ay: Hunkariya az: Macarıstan ba: Венгрия be: Венгрыя bg: Унгария bi: Hungary bn: হাঙ্গেরি bo: ཧུང་གྷ་རི། br: Hungaria bs: Mađarska ca: Hongria ce: Венгри co: Ungheria cs: Maďarsko cu: Ѫгри cv: Венгри cy: Hwngari da: Ungarn de: Ungarn dv: ހަންގޭރީ ee: Hungary el: Ουγγαρία en: Hungary eo: Hungario es: Hungría et: Ungari eu: Hungaria fa: مجارستان ff: Hunngariya fi: Unkari fo: Ungarn fr: Hongrie fy: Hongarije ga: An Ungáir gd: An Ungair gl: Hungría gn: Hungyria gu: હંગેરી gv: Yn Ungaar he: הונגריה hi: हंगरी hr: Mađarska ht: Ongri hu: Magyarország hy: Հունգարիա ia: Hungaria id: Hongaria ie: Hungaria io: Hungaria is: Ungverjaland it: Ungheria ja: ハンガリー jv: Hongaria ka: უნგრეთი kg: Hongrie ki: Macartsa kk: Мажарстан kl: Ungarni km: ប្រទេសហុងគ្រី ko: 헝가리 ku: Macaristan kv: Мадьяр Му kw: Hungari ky: Мажарстан la: Hungaria lb: Ungarn li: Hongarieë ln: Ungri lt: Vengrija lv: Ungārija mi: Hanekari mk: Унгарија ml: ഹംഗറി mn: Унгар mr: हंगेरी ms: Hungary mt: Ungerija my: ဟန်ဂေရီနိုင်ငံ na: Ungari ne: हंगेरी nl: Hongarije "no": Ungarn nv: Hángewii oc: Ongria or: ହଙ୍ଗେରୀ os: Венгри pa: ਹੰਗਰੀ pl: Węgry ps: هنګري pt: Hungria qu: Unriya rm: Ungaria ro: Ungaria ru: Венгрия rw: Hongiriya sa: हंगरी se: Ungára sh: Mađarska si: හන්ගේරියානු සමුහාණ්ඩුව sk: Maďarsko sl: Madžarska sm: Hungary sn: Hungary so: Hungaria sq: Hungaria sr: Мађарска ss: IHangareyi st: Hungary su: Hungaria sv: Ungern sw: Hungaria ta: அங்கேரி te: హంగేరి tg: Маҷористон th: ประเทศฮังการี tk: Wengriýa tl: Unggarya tr: Macaristan ts: Hungary tt: Маҗарстан ug: ۋېنگرىيە uk: Угорщина ur: مجارستان uz: Mojariston vi: Hungary vo: Macarän wo: Ongiri yi: אונגארן yo: Húngárì zh: 匈牙利 ================================================ FILE: settings/country-names/id.yaml ================================================ name: default: Indonesia af: Indonesië ak: Indonesia am: ኢንዶኔዥያ an: Indonesia ar: إندونيسيا ay: Indunisya az: İndoneziya ba: Индонезия be: Інданезія bg: Индонезия bi: Indonesia bn: ইন্দোনেশিয়া bo: ཨིན་རྡུ་ནི་ཤིས་ཡ། br: Indonezia bs: Indonezija ca: Indonèsia ce: Индонези ch: Indonesia co: Indunesia cs: Indonésie cu: Їндонисїꙗ cv: Индонези cy: Indonesia da: Indonesien de: Indonesien dv: އިންޑޮނޭޝިޔާ el: Ινδονησία en: Indonesia eo: Indonezio es: Indonesia et: Indoneesia eu: Indonesia fa: اندونزی fi: Indonesia fo: Indonesia fr: Indonésie fy: Yndoneezje ga: An Indinéis gd: An Innd-Innse gl: Indonesia gn: Indonesia gu: ઈંડોનેશિયા gv: Yn Indoneesh he: אינדונזיה hi: इंडोनेशिया hr: Indonezija ht: Endonezi hu: Indonézia hy: Ինդոնեզիա ia: Indonesia id: Indonesia ie: Indonesia io: Indonezia is: Indónesía it: Indonesia iu: ᐄᓅᓯᐊ ja: インドネシア jv: Indonésia ka: ინდონეზია ki: Indonesia kk: Индонезия kl: Indonesia km: ឥណ្ឌូនេស៊ី kn: ಇಂಡೋನೇಷ್ಯಾ ko: 인도네시아 ku: Îndonezya kv: Индонезия kw: Indonesi ky: Индонезия la: Indonesia lb: Indonesien li: Indonesië ln: Indonezí lt: Indonezija lv: Indonēzija mg: Indonezia mi: Initonīhia mk: Индонезија ml: ഇന്തോനേഷ്യ mn: Индонез mr: इंडोनेशिया ms: Indonesia mt: Indoneżja my: အင်ဒိုနီးရှားနိုင်ငံ na: Indonitsiya ne: इण्डोनेशिया nl: Indonesië "no": Indonesia nv: Indoníízha oc: Indonesia om: Indoneeshiyaa or: ଇଣ୍ଡୋନେସିଆ os: Индонези pa: ਇੰਡੋਨੇਸ਼ੀਆ pl: Indonezja ps: اندونيزيا pt: Indonésia qu: Indunisya rm: Indonesia ro: Indonezia ru: Индонезия rw: Indonesiya sa: इण्डोनेशिया se: Indonesia sg: Ênndonezïi sh: Indonezija si: ඉන්දුනීසියාව sk: Indonézia sl: Indonezija sm: Indyunisia so: Indunisiya sq: Indonezia sr: Индонезија ss: INdonesiya su: Indonésia sv: Indonesien sw: Indonesia ta: இந்தோனேசியா te: ఇండోనేషియా tg: Индонезия th: ประเทศอินโดนีเซีย tk: Indoneziýa tl: Indonesya tr: Endonezya tt: Индонезия ug: ھىندونېزىيە uk: Індонезія ur: انڈونیشیا uz: Indoneziya vi: Indonesia vo: Lindäna-Seänuäns wa: Indonezeye wo: Endoneesi yi: אינדאנעזיע yo: Indonésíà za: Yindunizsihya zh: 印度尼西亚 / 印度尼西亞 ================================================ FILE: settings/country-names/ie.yaml ================================================ name: default: Éire / Ireland af: Ierland ak: Aereland am: አየርላንድ an: Irlanda ar: أيرلندا ay: Irlandiya az: İrlandiya ba: Ирландия be: Ірландыя bg: Ирландия bi: Ireland bm: Irilandi bn: আয়ারল্যান্ড bo: ཨ་ཡར་ལནཌ། br: Iwerzhon bs: Irska ca: Irlanda ce: Ирланди co: Irlanda cs: Irsko cu: Єирь cv: Ирланди Республики cy: Iwerddon da: Irland de: Irland dv: އަޔަލޭންޑުގެ ޖުމްހޫރިއްޔާ dz: ཨཱ་ཡ་ལེནཌ ee: Ireland nutome el: Ιρλανδία en: Ireland eo: Irlando es: Irlanda et: Iirimaa eu: Irlanda fa: ایرلند ff: Irlannda fi: Irlanti fo: Írland fr: Irlande fy: Ierlân ga: Éire gd: Èirinn gl: Irlanda gn: Ilandia gu: આયર્લેંડ gv: Nerin ha: Ayalan he: אירלנד hi: आयरलैंड hr: Irska ht: Ilann hu: Írország hy: Իռլանդիա ia: Irlanda id: Irlandia ie: Irland io: Irlando is: Írland it: Irlanda ja: アイルランド jv: Republik Irlandia ka: ირლანდია ki: Ayalandi kk: Ирландия kl: Irlandi km: អៀរឡង់ kn: ಐರ್ಲೆಂಡ್ ko: 아일랜드 ks: اَیَرلینٛڑ ku: Îrland kv: Ирландия kw: Repoblek Wordhen ky: Ирландия la: Hibernia lb: Irland lg: Ayalandi li: Ierland ln: Irelandɛ lo: ໄອແລນ lt: Airija lv: Īrija mg: Irlandy mi: Airangi mk: Ирска ml: അയർലാൻഡ് mn: Ирланд mr: आयर्लंड ms: Ireland mt: Irlanda my: အိုင်ယာလန် na: Ripubrikit Airerand ne: आइरल्याण्ड nl: Ierland "no": Irland nv: Bitsiighaʼ Łichííʼí Bikéyah oc: Republica d'Irlanda or: ଆୟରଲ୍ୟାଣ୍ଡ os: Ирланди pl: Irlandia ps: د آيرلېنډ جمهوريت pt: Irlanda qu: Ilanda rm: Irlanda rn: Irilandi ro: Irlanda ru: Ирландия rw: Irilande sc: Irlanda se: Irlánda sg: Irlânde sh: Irska si: අයර්ලන්තය sk: Írsko sl: Irska sn: Ireland so: Ayrlaand sq: Irlandë sr: Ирска ss: IYalendi sv: Irland sw: Ayalandi ta: அயர்லாந்து te: ఐర్ లాండ్ tg: Ҷумҳурии Ирландия th: สาธารณรัฐไอร์แลนด์ ti: አየርላንድ tk: Irlandiýa tl: Irlanda to: ʻAealani tr: İrlanda ts: Ireland tt: Ирландия uk: Ірландія ur: آئرلینڈ uz: Irlandiya vi: Ireland vo: Lireyän wa: Irlande yi: אירלאנד yo: Orílẹ́ède Ailandi za: Aiwjlanz Gunghozgoz zh: 愛爾蘭 zu: i-Ireland ================================================ FILE: settings/country-names/il.yaml ================================================ name: default: ישראל af: Israel am: እስራኤል an: Israel ar: إسرائيل av: Гlизраил az: İsrail ba: Израиль be: Ізраіль bg: Израел bm: Israil bn: ইসরায়েল bo: ཨི་སི་ར་ཨེལ། br: Israel bs: Izrael ca: Israel ce: Израиль co: Israele cs: Izrael cu: Їꙁдраил҄ь cv: Израиль cy: Israel da: Israel de: Israel dv: އިސްރާއީލު dz: ཨིཛ་རཱེལ་ el: Ισραήλ en: Israel eo: Israelo es: Israel et: Iisrael eu: Israel fa: اسرائیل fi: Israel fj: Isireli fo: Ísrael fr: Israël fy: Israel ga: Iosrael gd: Iosrael gl: Israel gn: Israel gu: ઈઝરાયલ gv: Israel ha: Isra'ila he: ישראל hi: इज़राइल hr: Izrael ht: Izrayèl hu: Izrael hy: Իսրայել ia: Israel id: Israel ie: Israel ig: Israel io: Israel is: Ísrael it: Israele ja: イスラエル jv: Israèl ka: ისრაელი kg: Israel kk: Израиль kl: Israel kn: ಇಸ್ರೇಲ್ ko: 이스라엘 ku: Îsraêl kv: Израиль kw: Ysrael ky: Израиль la: Israel lb: Israel li: Israël ln: Israel lt: Izraelis lv: Izraēla mi: Iharaira mk: Израел ml: ഇസ്രയേൽ mn: Израйль mr: इस्रायल ms: Israel mt: Iżrael my: အစ္စရေးနိုင်ငံ na: Iteraer ne: इजरायल nl: Israël "no": Israel nv: Ízrel Bikéyah oc: Israèl or: ଇସ୍ରାଏଲ os: Израиль pa: ਇਜ਼ਰਾਇਲ pl: Izrael ps: اسرائيل pt: Israel qu: Israyil ro: Israel ru: Израиль rw: Isirayeli sa: इस्रेल sc: Israele se: Israel sh: Izrael si: ඊශ්‍රායලය sk: Izrael sl: Izrael sm: Israel so: Israaiil sq: Izraeli sr: Израел ss: Ka-Israyeli sv: Israel sw: Israel ta: இசுரேல் te: ఇజ్రాయిల్ tg: Исроил th: ประเทศอิสราเอล tk: Ysraýyl tl: Israel tr: İsrail tt: Исраил ug: ئىسرائىلىيە uk: Ізраїль ur: اسرائیل uz: Isroil vi: Israel vo: Yisraelän wa: Israyel wo: Israayil yi: ישראל yo: Ísráẹ́lì zh: 以色列 zu: Isreyili ================================================ FILE: settings/country-names/im.yaml ================================================ name: default: Isle of Man af: Eiland Man an: Isla de Man ar: جزيرة مان az: Men adası be: Востраў Мэн bg: Ман bn: আইল অফ ম্যান br: Manav bs: Ostrvo Man ca: Illa de Man cs: Man cv: Мэн утравĕ cy: Ynys Manaw da: Isle of Man de: Insel Man dv: އައިޒަލް އޮފް މޭން el: Νήσος του Μαν en: Isle of Man eo: Manksinsulo es: Isla de Man et: Man eu: Man fa: جزیره من fi: Mansaari fo: Mann fr: Île de Man fy: Man ga: Oileán Mhanann gd: Eilean Mhanainn gl: Illa de Man gv: Ellan Vannin he: האי מאן hi: आइल ऑफ़ मैन hr: Otok Man hu: Man hy: Մեն կղզի ia: Insula de Man id: Pulau Man io: Man-Insulo is: Mön it: Isola di Man ja: マン島 jv: Pulo Man ka: მენი ko: 맨 섬 kw: Ynys Manow la: Monapia li: Man lt: Meno Sala lv: Menas Sala mi: Motu o Man mk: Ман ml: ഐൽ ഒഫ് മാൻ mr: आईल ऑफ मान ms: Isle of Man nl: Eiland Man "no": Man oc: Illa de Man os: Мэн pa: ਮੈਨ ਟਾਪੂ pl: Wyspa Man pt: Ilha de Man qu: Man wat'a ro: Insula Man ru: Остров Мэн rw: Ikirwa cya Man sh: Otok Man sk: Ostrov Man sl: Otok Man sq: Ishulli i Njeriut sr: Острво Ман su: Pulo Man sv: Isle of Man sw: Isle of Man ta: மாண் தீவு th: เกาะแมน tl: Pulo ng Man tr: Man Adası tt: Мэн утравы uk: Острів Мен ur: آئل آف مین vi: Đảo Man vo: Mäneän yo: Erékùṣù ilẹ̀ Man zh: 马恩岛 ================================================ FILE: settings/country-names/in.yaml ================================================ name: default: India af: Indië ar: الهند be: Індыя br: India da: Indien de: Indien el: Ινδία en: India eo: Barato fa: هند fi: Intia fr: Inde fy: Yndia ga: An India gd: Na h-Innseachan he: הודו hi: भारत hu: India is: Indland it: India iu: ᐃᓐᑎᐊ/intia ja: インド lb: Indien lt: Indija lv: Indija mn: Энэтхэг "no": India pl: Indie ru: Индия sl: Indija sv: Indien th: ประเทศอินเดีย tr: Hindistan uk: Індія vi: Ấn Độ zh: 印度 ================================================ FILE: settings/country-names/io.yaml ================================================ name: default: British Indian Ocean Territory af: Britse Indiese Oseaan Gebied ak: Britenfo Hɔn Man Wɔ India Po No Mu am: የብሪታኒያ ህንድ ውቂያኖስ ግዛት an: Territorio Britanico de l'Ozián Indico ar: إقليم المحيط الهندي البريطاني az: Britaniya-Hindistan Okeanik territoriyası be: Брытанскія Індыйска-акіянскія тэрыторыі bg: Британска индоокеанска територия bm: Angilɛ ka ɛndu dugukolo bn: ব্রিটিশ ভারত মহাসাগরীয় অঞ্চল br: Tiriad Meurvor Indez Breizh-Veur bs: Britanski teritorij Indijskog okeana ca: Territori Britànic de l'Oceà Índic cs: Britské indickooceánské území cy: Tiriogaeth Prydain yng Nghefnfor India da: Britiske Indiske Ocean de: Britisches Territorium im Indischen Ozean dz: བྲི་ཊིཤ་རྒྱ་གར་གྱི་རྒྱ་མཚོ་ས་ཁོངས ee: Britaintɔwo ƒe india ƒudome nutome el: Βρετανικό Έδαφος Ινδικού Ωκεανού en: British Indian Ocean Territory eo: Brita Hindoceana Teritorio es: Territorio Británico del Océano Índico et: Briti India ookeani ala eu: Indiako Ozeanoko Britainiar Lurraldea fa: مستعمره‌های بریتانیا در اقیانوس هند ff: Keeriindi britaani to maayo enndo fi: Brittiläinen Intian valtameren alue fo: Bretsku Indiahavsoyggjarnar fr: Territoire britannique de l'Océan Indien fy: Britske Yndyske-Oseaanterritoarium ga: Críoch Aigéan Indiach na Breataine gd: Ranntair Breatannach Cuan nan Innseachan gl: Territorio Británico do Océano Índico gu: બ્રિટિશ ઇન્ડિયન ઓશન ટેરિટરી ha: Yankin Birtaniya Na Tekun Indiya he: טריטוריה בריטית באוקיאנוס ההודי hi: ब्रिटिश हिंद महासागर क्षेत्र hr: Britanski Indijskooceanski Teritoriji hu: Brit Indiai-óceáni Terület ia: Territorio oceanic britanno-indian id: Teritorial Britania di Samudra Hindia io: Britaniana teritorio en Indiana Oceano is: Breska Indlandshafseyjar it: Territorio britannico dell'oceano Indiano ja: イギリス領インド洋地域 ka: ბრიტანეთის ინდოეთის ოკეანის ტერიტორია ki: Eneo la Uingereza katika Bahari Hindi km: ដែន​មហា​សមុទ្រ​ឥណ្ឌា ចក្រភព​អង់គ្លេស kn: ಬ್ರಿಟೀಶ್ ಇಂಡಿಯನ್ ಮಹಾಸಾಗರ ಪ್ರದೇಶ ko: 영국령 인도양 지역 ks: برطانوی بحرِ ہِنٛدۍ علاقہٕ ku: Xakên Brîtanyayê yên Okyanûsa Hindî lg: Bizinga by'eCago ln: Mabelé ya Angɛlɛtɛ́lɛ na mbú ya Indiya lo: ບຣິດທິສອິນດ່ຽນໂອຊ່ຽນເທີຣິທໍຣີ lt: Indijos vandenyno Britų sritis lv: Indijas okeāna Britu teritorija mg: Faridranomasina indiana britanika mk: Британска Индоокеанска Територија ml: ബ്രിട്ടീഷ് ഇന്ത്യൻ മഹാസമുദ്ര പ്രദേശം mn: Британийн Энэтхэгийн Далайн Нутаг дэвсгэр mr: ब्रिटीश हिंदी महासागर क्षेत्र ms: Wilayah Lautan Hindi British my: ဗြိတိသျှ အိန္ဒြိယ သမုဒ္ဒရာ ပိုင်နက် ne: बेलायती हिन्द महासागर क्षेत्र nl: Brits Territorium in de Indische Oceaan "no": Det britiske territoriet i Indiahavet or: ବ୍ରିଟିଶ୍ ଭାରତୀୟ ସାମୁଦ୍ରିକ କ୍ଷେତ୍ର pl: Brytyjskie Terytorium Oceanu Indyjskiego pt: Território Britânico do Oceano Índico rm: Territori Britannic en l'Ocean Indic rn: Intara y'Ubwongereza yo mu birwa by'Abahindi ro: Teritoriul Britanic din Oceanul Indian ru: Британская Территория в Индийском Океане sg: Sêse tî Anglëe na Ngûyämä tî Ênnde sh: Britanski teritorij Indijskog oceana si: බ්‍රිතාන්‍ය ඉන්දීය සාගර ප්‍රාන්තය sk: Britské indickooceánske územie sl: Britanski teritorij v Indijskem oceanu sr: Британска територија Индијског океана sv: Brittiska territoriet i Indiska oceanen sw: Eneo la Uingereza katika Bahari Hindi ta: பிரித்தானிய இந்தியப் பெருங்கடல் மண்டலம் te: బ్రిటిష్ భారతీయ ఓషన్ ప్రాంతం th: บริติชอินเดียนโอเชียนเทร์ริทอรี ti: የብሪታኒያ ህንድ ውቂያኖስ ግዛት to: Potu fonua moana ʻInitia fakapilitānia tr: Britanya Hint Okyanusu Toprakları uk: Британська територія в Індійському океані ur: برطانوی ہندوستانی سمندری خطہ vi: Lãnh thổ Ấn Độ Dương thuộc Anh yo: Orílẹ́ède Etíkun Índíánì ti Ìlú Bírítísì zh: 英属印度洋领地 zu: i-British Indian Ocean Territory ================================================ FILE: settings/country-names/iq.yaml ================================================ name: default: العراق af: Irak am: ኢራቅ an: Iraq ar: العراق av: ГӀиракъ az: İraq be: Ірак bg: Ирак bn: ইরাক bo: ཡི་ལའ་ཁོ། br: Irak bs: Irak ca: Iraq ce: Ӏиракъ cs: Irák cv: Ирак cy: Irac da: Irak de: Irak dv: ޢިރާޤު el: Ιράκ en: Iraq eo: Irako es: Irak et: Iraak eu: Irak fa: عراق fi: Irak fo: Irak fr: Irak fy: Irak ga: An Iaráic gd: Ioràc gl: Iraq - العراق gu: ઈરાક gv: Yn Earack he: עיראק hi: इराक़ hr: Irak ht: Irak hu: Irak hy: Իրաք ia: Irak id: Irak ie: Irak io: Irak is: Írak it: Iraq iu: ᐃᕉᒃ ja: イラク jv: Irak ka: ერაყი ki: Iraq kk: Ирак kl: Iraq kn: ಇರಾಕ್ ko: 이라크 ku: Iraq kv: Ирак kw: Irak ky: Ирак la: Iracum lb: Irak li: Irak ln: Irak lt: Irakas lv: Irāka mi: Irāki mk: Ирак ml: ഇറാഖ് mn: Ирак mr: इराक ms: Iraq mt: Iraq my: အီရတ်နိုင်ငံ na: Irak ne: ईराक nl: Irak "no": Irak nv: Iiwááʼ oc: Iraq or: ଇରାକ os: Ирак pa: ਇਰਾਕ pl: Irak ps: عراق pt: Iraque qu: Iraq ro: Irak ru: Ирак rw: Irake sa: ईराक se: Irak sh: Irak si: ඉරාක ජනරජය sk: Irak sl: Irak so: Ciraaq sq: Iraku sr: Ирак ss: I-Irakhi su: Irak sv: Irak sw: Iraq ta: ஈராக் te: ఇరాక్ tg: Ироқ th: ประเทศอิรัก tk: Yrak tl: Irak tr: Irak tt: Гыйрак tw: Irak ug: ئىراق uk: Ірак ur: عراق uz: Iroq vi: Iraq vo: Lirakän wa: Irak wo: Iraak yi: איראק yo: Irak zh: 伊拉克 zu: I-Iraki ================================================ FILE: settings/country-names/ir.yaml ================================================ name: default: ایران af: Iran am: ፋርስ an: Irán ar: إیران az: İran ba: Иран be: Іран bg: Иран bn: ইরান bo: ཡི་ལང་། br: Iran bs: Iran ca: Iran ce: Иран co: Iranu cs: Írán cy: Iran da: Iran de: Iran dv: އީރާން dz: ཨི་རཱན་ el: Ιράν en: Iran eo: Irano es: Irán et: Iraan eu: Iran fa: ایران fi: Iran fo: Iran fr: Iran fy: Iran ga: An Iaráin gd: Ioràn gl: Irán - ایران gn: Irán gu: ઈરાન gv: Yn Eeraan ha: Iran he: איראן hi: ईरान hr: Iran ht: Iran hu: Irán hy: Իրան ia: Iran id: Iran ie: Iran io: Iran is: Íran it: Iran ja: イラン jv: Iran ka: ირანი kk: Иран kl: Iran km: អ៊ីរ៉ង់ kn: ಇರಾನ್ ko: 이란 ks: ایٖران ku: Îran kv: Иран kw: Iran ky: Иран la: Irania lb: Iran li: Iran ln: Iran lt: Iranas lv: Irāna mg: Iran mi: Irāna mk: Иран ml: ഇറാൻ mn: Иран mr: इराण ms: Iran mt: Iran my: အီရန်နိုင်ငံ na: Iran ne: इरान nl: Iran "no": Iran nv: Iiwą́ą́ oc: Iran or: ଇରାନ os: Иран pa: ਇਰਾਨ pl: Iran ps: ایران pt: Irão qu: Iran rm: Iran ro: Iran ru: Иран rw: Irani sa: ईरान sc: Iran se: Iran sh: Iran si: ඉරානය sk: Irán sl: Iran so: Iiraan sq: Irani sr: Иран ss: I-Irani su: Iran sv: Iran sw: Uajemi ta: ஈரான் te: ఇరాన్ tg: Эрон th: ประเทศอิหร่าน tk: Eýran tl: Iran tr: İran tt: Иран tw: Iran ug: ئىران uk: Іран ur: ایران uz: Eron vi: Iran vo: Lirän wa: Iran wo: Iraan yi: איראן yo: Ìránì za: Iran zh: 伊朗 zu: I-Irani ================================================ FILE: settings/country-names/is.yaml ================================================ name: default: Ísland af: Ysland am: አይስላንድ an: Islandia ar: آيسلندا az: İslandiya ba: Исландия be: Ісландыя bg: Исландия bi: Iceland bm: Aisland bn: আইসল্যান্ড bo: ཨཡིསུ་ལེན་ཌ། br: Island bs: Island ca: Islàndia ce: Исланди co: Islanda cs: Island cu: Исландъ cv: Исланди cy: Gwlad yr Iâ da: Island de: Island dv: އައިސްލަންޑަން dz: ཨའིསི་ལེནཌ་ ee: Iceland el: Ισλανδία en: Iceland eo: Islando es: Islandia et: Island eu: Islandia fa: ایسلند ff: Islannda fi: Islanti fo: Ísland fr: Islande fy: Yslân ga: An Íoslainn gd: Innis Tìle gl: Islandia gn: Iylanda gu: આઈસલેંડ gv: Yn Eeslynn he: איסלנד hi: आइसलैण्ड hr: Island ht: Islann hu: Izland hy: Իսլանդիա ia: Islanda id: Islandia ie: Island io: Islando is: Ísland it: Islanda ja: アイスランド jv: Islandia ka: ისლანდია kg: Islande kk: Исландия kl: Islandi km: ប្រទេសអាយឡែន kn: ಐಸ್‍ಲ್ಯಾಂಡ್ ko: 아이슬란드 ku: Îslenda kv: Исландия kw: Island ky: Исландия la: Islandia lb: Island lg: Isilandi li: Iesland ln: Islandi lo: ອິດສະລັງ lt: Islandija lv: Islande mi: Tiorangi mk: Исланд ml: ഐസ്‌ലാന്റ് mn: Исланд mr: आइसलँड ms: Iceland mt: Iżlanda my: အိုက်စလန်နိုင်ငံ na: Aiterand ne: आइसल्याण्ड nl: IJsland "no": Island nv: Tin Bikéyah oc: Islàndia or: ଆଇସଲ୍ୟାଣ୍ଡ os: Исланди pa: ਆਈਸਲੈਂਡ pl: Islandia ps: آيسلېنډ pt: Islândia qu: Islandya rm: Islanda ro: Islanda ru: Исландия rw: Isilande sa: आइसलैंड sc: Islanda se: Islánda sg: Islânde sh: Island si: අයිස්ලන්තය sk: Island sl: Islandija sm: Aiselani so: Island sq: Islanda sr: Исланд ss: Echweni st: Iceland su: Islandia sv: Island sw: Iceland ta: ஐசுலாந்து te: ఐస్‌లాండ్ tg: Исландия th: ประเทศไอซ์แลนด์ tk: Islandiýa tl: Lupangyelo tr: İzlanda tt: Исландия ug: ئىسلاندىيە uk: Ісландія ur: آئس لینڈ uz: Islandiya vi: Iceland vo: Lisladeän wa: Izlande wo: Islaand yi: איסלאנד yo: Íslándì zh: 冰岛 zu: I-Ayisilandi ================================================ FILE: settings/country-names/it.yaml ================================================ name: default: Italia ab: Италиа af: Italië ak: Italy am: ጣልያን an: Italia ar: إيطاليا as: ইটালী ay: Italiya az: İtaliya ba: Италия be: Італія bg: Италия bi: Itali bn: ইতালি bo: ཨི་ཏ་ལི། br: Italia bs: Italija ca: Itàlia ce: Итали co: Italia cs: Itálie cu: Италїꙗ cv: Итали cy: Yr Eidal da: Italien de: Italien dv: އިޓަލީވިލާތް dz: ཨྀཊ་ལི་ ee: Italy el: Ιταλία en: Italy eo: Italio es: Italia et: Itaalia eu: Italia fa: ایتالیا ff: Italiya fi: Italia fo: Italia fr: Italie fy: Itaalje ga: An Iodáil gd: An Eadailt gl: Italia gn: Itália gu: ઈટલી gv: Yn Iddaal he: איטליה hi: इटली hr: Italija ht: Itali hu: Olaszország hy: Իտալիա ia: Italia id: Italia ie: Italia io: Italia is: Ítalía it: Italia ja: イタリア jv: Italia ka: იტალია kg: Italia ki: Italia kk: Италия kl: Italia km: អ៊ីតាលី kn: ಇಟಲಿ ko: 이탈리아 ku: Îtalya kv: Италия kw: Itali ky: Италия la: Italia lb: Italien lg: Yitale li: Italië ln: Italya lo: ອິຕາລີ lt: Italija lv: Itālija mg: Italia mi: Itari mk: Италија ml: ഇറ്റലി mn: Итали mr: इटली ms: Itali mt: Italja my: အီတလီနိုင်ငံ na: Itari ne: इटाली nl: Italië "no": Italia nv: Ídelii oc: Itàlia or: ଇଟାଲୀ os: Итали pa: ਇਟਲੀ pl: Włochy ps: اېټاليا pt: Itália qu: Italya rm: Italia ro: Italia ru: Италия rw: Ubutaliyani sa: इटली sc: Itàlia se: Itália sg: Italùii sh: Italija sk: Taliansko sl: Italija sm: Italia so: Talyaaniga sq: Italia sr: Италија ss: INtaliyane su: Italia sv: Italien sw: Italia ta: இத்தாலி te: ఇటలీ tg: Итолиё th: ประเทศอิตาลี tk: Italiýa tl: Italya tr: İtalya tt: Италия tw: Italy ty: ’Itāria ug: ئىتالىيە uk: Італія ur: اطالیہ uz: Italiya ve: Italy vi: Ý vo: Litaliyän wa: Itåleye wo: Itaali yi: איטאליע yo: Itálíà zh: 意大利 zu: ITaliya ================================================ FILE: settings/country-names/je.yaml ================================================ name: default: Jersey af: Jersey an: Jèrri ar: جيرزي az: Cersi be: Джэрсi bg: Джърси br: Jerzenez bs: Jersey ca: Jersey cs: Jersey cy: Jersey da: Jersey de: Jersey dv: ޖާސޭ el: Υερσέη en: Jersey eo: Ĵerzejo es: Jersey et: Jersey eu: Jersey fa: جرزی fi: Jersey fr: Jersey ga: Geirsí gd: Jersey gl: Illa de Jersey gv: Jersee he: ג'רזי hr: Jersey hu: Jersey hy: Ջերսի id: Jersey io: Jersey is: Jersey it: Isola di Jersey ja: ジャージー jv: Jersey ka: ჯერზი kn: ಜರ್ಸಿ ko: 저지 섬 kw: Jersi la: Caesarea Insula li: Jersey lt: Džersis lv: Džērsija mi: Tōrehe mn: Жерси mr: जर्सी ms: Jersey ne: जर्सी nl: Jersey "no": Jersey oc: Jersei os: Джерси pa: ਜਰਸੀ pl: Jersey pt: Jersey ro: Insula Jersey ru: Джерси rw: Jersey sh: Jersey sk: Jersey sl: Jersey sq: Jersey sr: Џерзи su: Jersey sv: Jersey sw: Jersey ta: யேர்சி te: జెర్సీ th: เจอร์ซีย์ tl: Hersey tr: Jersey tt: Җерси uk: Джерсі ur: جرزی vi: Jersey yo: Jersey zh: 澤西島 ================================================ FILE: settings/country-names/jm.yaml ================================================ name: default: Jamaica af: Jamaika ar: جامايكا be: Ямайка br: Jamaika cs: Jamajka de: Jamaika el: Ιαμαϊκή en: Jamaica eo: Jamajko fa: جامائیکا fi: Jamaika fr: Jamaïque fy: Jamaika ga: Iamáice gd: Iaimeuca he: ג'מייקה hr: Jamajka hu: Jamaica id: Jamaika io: Jamaika is: Jamaíka it: Giamaica ja: ジャマイカ ku: Camayka la: Iamaica lb: Jamaika lt: Jamaika lv: Jamaika mn: Ямайка nl: Jamaica "no": Jamaica oc: Jamaica pl: Jamajka pt: Jamaica ru: Ямайка se: Jamaica sk: Jamajka sl: Jamajka sv: Jamaica ta: ஜமேக்கா th: ประเทศจาเมกา tr: Jamaika uk: Ямайка vo: Camekeän zh: 牙买加 ================================================ FILE: settings/country-names/jo.yaml ================================================ name: default: الأردن af: Jordanië am: ጆርዳን an: Chordania ar: الأردن az: İordaniya ba: Иордания be: Іарданія bg: Йордания bm: Ordon bn: জর্দান bo: རྗོར་ཌན། br: Jordania bs: Jordan ca: Jordània cs: Jordánsko cv: Иордани cy: Gwlad Iorddonen da: Jordan de: Jordanien dv: އުރުދުން el: Ιορδανία en: Jordan eo: Jordanio es: Jordania et: Jordaania eu: Jordania fa: اردن fi: Jordania fo: Jordan fr: Jordanie fy: Jordaanje ga: An Iordáin gd: Iòrdan gl: Xordania gu: જૉર્ડન gv: Yn Jordaan he: ירדן hi: जॉर्डन hr: Jordan ht: Jòdani hu: Jordánia hy: Հորդանան ia: Jordania id: Yordania ie: Jordania io: Jordania is: Jórdanía it: Giordania ja: ヨルダン jv: Yordania ka: იორდანია kk: Иордания kl: Jordan km: ហ្សកដង់ ko: 요르단 ks: اردن ku: Urdun kv: Иордания kw: Jordan la: Iordania lb: Jordanien li: Jordanië ln: Zordaní lt: Jordanija lv: Jordānija mi: Hōrano mk: Јордан ml: ജോർദാൻ mn: Йордан mr: जॉर्डन ms: Jordan mt: Ġordan my: ဂျော်ဒန်နိုင်ငံ na: Djordan nl: Jordanië "no": Jordan nv: Jóoʼdan oc: Jordania or: ଜୋର୍ଡାନ os: Иордани pa: ਜਾਰਡਨ pl: Jordania ps: اردن pt: Jordânia qu: Hurdanya ro: Iordania ru: Иордания rw: Yorudani sa: जार्डन se: Jordania sh: Jordan sk: Jordánsko sl: Jordanija so: Urdun sq: Jordania sr: Јордан ss: IJoridane su: Yordania sv: Jordanien sw: Yordani ta: ஜோர்தான் te: జోర్డాన్ th: ประเทศจอร์แดน tk: Iordaniýa tl: Hordan tr: Ürdün tt: Үрдүн ug: ئىئوردانىيە uk: Йорданія ur: اردن uz: Iordaniya vi: Jordan vo: Yordän wo: Jordaani yi: יארדאניע yo: Jọ́rdánì zh: 约旦/約旦 ================================================ FILE: settings/country-names/jp.yaml ================================================ name: default: 日本 ab: Иапониа af: Japan am: ጃፓን an: Chapón ar: اليابان as: জাপান ay: Nihun az: Yaponiya ba: Япония be: Японія bg: Япония bn: জাপান bo: རི་པིན། br: Japan bs: Japan ca: Japó ce: Япони ch: Chapan cs: Japonsko cu: Ꙗпѡнїꙗ cv: Япони cy: Japan da: Japan de: Japan dv: ޖަޕާނު dz: ཇཱ་པཱན། ee: Japan el: Ιαπωνία en: Japan eo: Japanio es: Japón et: Jaapan eu: Japonia fa: ژاپن fi: Japani fo: Japan fr: Japon fy: Japan ga: An tSeapáin gd: An t-Seapan gl: Xapón gn: Hapõ gu: જાપાન gv: Yn Çhapaan ha: Japan he: יפן hi: जापान hr: Japan ht: Japon hu: Japán hy: Ճապոնիա ia: Japon id: Jepang ie: Japan io: Japonia is: Japan it: Giappone iu: ᓃᑉᐊᓐ ja: 日本 jv: Jepang ka: იაპონია ki: Japan kk: Жапония kl: Japani km: ជប៉ុន kn: ಜಪಾನ್ ko: 일본 ks: जापान ku: Japon kv: Япония kw: Nihon ky: Жапония la: Iaponia lb: Japan li: Japan ln: Zapɔ́ lo: ປະເທດຍີ່ປຸ່ນ lt: Japonija lv: Japāna mg: Japana mi: Hapani mk: Јапонија ml: ജപ്പാൻ mn: Япон mo: Жапония mr: जपान ms: Jepun mt: Ġappun my: ဂျပန်နိုင်ငံ na: Djapan ne: जापान nl: Japan "no": Japan nv: Binaʼadaałtzózí Dinéʼiʼ Bikéyah oc: Japon om: Jaappaan or: ଜାପାନ os: Япон pa: ਜਪਾਨ pl: Japonia ps: جاپان pt: Japão qu: Nihun rm: Giapun ro: Japonia ru: Япония rw: Ubuyapani sa: सूर्यमूल sc: Giapone sd: جاپان se: Japána sh: Japan si: ජපානය sk: Japonsko sl: Japonska sm: Iapani so: Jabaan sq: Japonia sr: Јапан ss: IJaphani su: Jepang sv: Japan sw: Japani ta: ஜப்பான் te: జపాన్ tg: Жопун th: ประเทศญี่ปุ่น ti: ጃፓን tk: Ýaponiýa tl: Hapon tr: Japonya tt: Япония tw: Yapan ty: Tāpōnē ug: ياپونىيە uk: Японія ur: جاپان uz: Yaponiya vi: Nhật Bản vo: Yapän wo: Sapoŋ yi: יאפאן yo: Japan za: Nditbonj zh: 日本 zu: IJapani ================================================ FILE: settings/country-names/ke.yaml ================================================ name: default: Kenya af: Kenia am: ኬንያ an: Kenia ar: كينيا az: Keniya ba: Кения be: Кенія bg: Кения bm: Kenya bn: কেনিয়া bo: ཁེ་ནི་ཡ། br: Kenya bs: Kenija ca: Kenya ce: Кени cs: Keňa cv: Кени cy: Kenya da: Kenya de: Kenia dv: ކެންޔާ dz: ཀེ་ནི་ཡ་ el: Κένυα en: Kenya eo: Kenjo es: Kenia et: Keenia eu: Kenya fa: کنیا ff: Kenya fi: Kenia fo: Kenja fr: Kenya fy: Kenya ga: An Chéinia gd: A' Cheinia gl: Quenia - Kenya gu: કેન્યા gv: Yn Cheinney ha: Kenya he: קניה hi: कीनिया hr: Kenija ht: Kenya hu: Kenya hy: Քենիա ia: Kenya id: Kenya ie: Kenia io: Kenia is: Kenía it: Kenya ja: ケニア jv: Kénya ka: კენია kg: Kenya ki: Kenya kk: Кения kl: Kenya kn: ಕೀನ್ಯಾ ko: 케냐 ku: Kenya kw: Kenya la: Kenia lb: Kenia lg: Kenya li: Kenia ln: Kénya lt: Kenija lv: Kenija mg: Kenia mi: Kēnia mk: Кенија ml: കെനിയ mn: Кени mr: केनिया ms: Kenya mt: Kenja my: ကင်ညာနိုင်ငံ na: Keniya nl: Kenia "no": Kenya nv: Kénya oc: Kenya om: Keeniyaa or: କେନିଆ os: Кени pa: ਕੀਨੀਆ pl: Kenia ps: کېنيا pt: Quénia qu: Kinya ro: Kenya ru: Кения rw: Kenya sa: केन्या sc: Kènya se: Kenia sg: Kenyäa sh: Kenija sk: Keňa sl: Kenija sn: Kenya so: Kiinya sq: Kenia sr: Кенија ss: IKheniya su: Kénya sv: Kenya sw: Kenya ta: கென்யா te: కెన్యా tg: Кения th: ประเทศเคนยา ti: ኬንያ tk: Keniýa tl: Kenya tr: Kenya ts: Kenya tt: Кения ug: كېنىيە uk: Кенія ur: کینیا uz: Keniya vi: Kenya vo: Kenyän wo: Keeñaa yi: קעניע yo: Kẹ́nyà zh: 肯尼亚 zu: IKenya ================================================ FILE: settings/country-names/kg.yaml ================================================ name: default: Кыргызстан ab: Ҟырҕызсҭан af: Kirgisië am: ኪርጊዝስታን an: Kirguizistán ar: قرغيزستان az: Qırğızıstan ba: Ҡырғыҙстан be: Кыргызстан bg: Киргизстан bn: কিরগিজিস্তান bo: ཀེ་རེ་གེཛུ་སུཏེན། br: Kirgizstan bs: Kirgistan ca: Kirguizistan ce: Киргизи cs: Kyrgyzstán cv: Киргизи cy: Kyrgyzstan da: Kirgisistan de: Kirgisistan dv: ކިރިގިސްތާން ee: Kyrgyzstan el: Κιργιζία en: Kyrgyzstan eo: Kirgizio es: Kirguistán et: Kõrgõzstan eu: Kirgizistan fa: قرقیزستان fi: Kirgisia fr: Kirghizistan fy: Kirgyzje ga: An Chirgeastáin gd: Cìorgastan gl: Kirguizistán gu: કિર્ગિસ્તાન gv: Yn Chyrgistaan he: קירגיזסטן hi: किर्गिज़स्तान hr: Kirgistan ht: Kirgistan hu: Kirgizisztán hy: Ղրղզստան ia: Kyrgyzistan id: Kirgizstan ie: Kirgizistan io: Kirgizistan is: Kirgisistan it: Kirghizistan ja: キルギス jv: Kirgizstan ka: ყირგიზეთი kk: Қырғызстан km: កៀហ៊្សីស៊ីស្ថាន kn: ಕಿರ್ಗಿಸ್ಥಾನ್ ko: 키르기스스탄 ks: کرغیزستان ku: Qirgizistan kv: Кыргызстан kw: Pow Kyrgys ky: Кыргызстан la: Chirgisia lb: Kirgisistan li: Kirgizië ln: Kirghizistáni lt: Kirgizija lv: Kirgizstāna mk: Киргистан ml: കിർഗ്ഗിസ്ഥാൻ mn: Кыргызстан mr: किर्गिझस्तान ms: Kyrgyzstan my: ကာဂျစ္စတန်နိုင်ငံ na: Kirgitan nl: Kirgizië "no": Kirgisistan nv: Kíígiz Bikéyah oc: Quirguizstan or: କିରଗିଜସ୍ତାନ os: Хъиргъиз pa: ਕਿਰਗਿਜ਼ਸਤਾਨ pl: Kirgistan ps: قرغزستان pt: Quirguistão qu: Kirkisuyu ro: Kârgâzstan ru: Киргизия rw: Kirigizisitani sa: किरगिस्थान sd: ڪِرگزِستانُ se: Kirgisistan sh: Kirgistan sk: Kirgizsko sl: Kirgizistan so: Qargistan sq: Kirgistani sr: Киргистан ss: IKhirigi su: Kirgistan sv: Kirgizistan sw: Kirgizia ta: கிர்கிசுத்தான் te: కిర్గిజిస్తాన్ tg: Қирғизистон th: ประเทศคีร์กีซสถาน tk: Gyrgyzystan tl: Kirgistan tr: Kırgızistan tt: Кыргызстан ug: قىرغىزىستان uk: Киргизстан ur: کرغیزستان uz: Qirgʻiziston vi: Kyrgyzstan vo: Kirgistän wo: Kirgistaan yi: קירגיזסטאן yo: Kirgistani zh: 吉尔吉斯斯坦 ================================================ FILE: settings/country-names/kh.yaml ================================================ name: default: ព្រះរាជាណាចក្រ​កម្ពុជា af: Kambodja am: ካምቦዲያ an: Cambocha ar: كمبوديا az: Kamboca ba: Камбоджа be: Камбоджа bg: Камбоджа bn: কম্বোডিয়া bo: ཁམ་པོ་ཛ། br: Kambodja bs: Kambodža ca: Cambodja ce: Камбоджа cs: Kambodža cv: Камбоджа cy: Cambodia da: Cambodja de: Kambodscha dv: ކެންބޯޑިއާ dz: ཀམ་བོ་ཌི་ཡ་ el: Καμπότζη en: Cambodia eo: Kamboĝo es: Camboya et: Kambodža eu: Kanbodia fa: کامبوج fi: Kambodža fo: Kambodja fr: Cambodge fy: Kambodja ga: An Chambóid gd: Cambuidea gl: Camboxa gn: Kamboja gu: કમ્બોડીયા gv: Yn Chamboyd ha: Kambodiya he: קמבודיה hi: कम्बोडिया hr: Kambodža ht: Kanbòdj hu: Kambodzsa hy: Կամբոջա ia: Cambodgia id: Kamboja ie: Cambodja io: Kambodja is: Kambódía it: Cambogia ja: カンボジア jv: Kamboja ka: კამბოჯა kk: Камбоджа km: ព្រះរាជាណាចក្រកម្ពុជា kn: ಕಾಂಬೋಡಿಯ ko: 캄보디아 ku: Kamboca kv: Камбоджа kw: Kamboji la: Cambosia lb: Kambodscha li: Cambodja ln: Kamboji lo: ປະເທດກຳປູເຈຍ lt: Kambodža lv: Kambodža mg: Kambodia mi: Kamapōtia mk: Камбоџа ml: കംബോഡിയ mn: Камбож mr: कंबोडिया ms: Kemboja mt: Kambodja my: ကမ္ဘောဒီးယားနိုင်ငံ na: Kambodja nl: Cambodja "no": Kambodsja oc: Cambòtja or: କମ୍ବୋଡ଼ିଆ os: Камбоджæ pa: ਕੰਬੋਡੀਆ pl: Kambodża ps: کمبودیا pt: Camboja qu: Kambuya rm: Cambodscha ro: Cambodgia ru: Камбоджа rw: Kambodiya se: Kambodja sh: Kambodža si: කාම්බෝජය sk: Kambodža sl: Kambodža so: Kambodiya sq: Kamboxhia sr: Камбоџа ss: IKhambodiya su: Kamboja sv: Kambodja sw: Kamboja ta: கம்போடியா te: కంబోడియా tg: Камбоҷа th: ประเทศกัมพูชา tk: Kamboçiýa tl: Kamboya tr: Kamboçya tt: Камбоҗа ug: كامبوجا uk: Камбоджа ur: کمبوڈیا uz: Kambodja vi: Campuchia vo: Kambocän wo: Kamboodi yi: קאמבאדיע yo: Kàmbódíà zh: 柬埔寨 ================================================ FILE: settings/country-names/ki.yaml ================================================ name: default: Kiribati ar: كيريباس be: Кірыбаці br: Kiribati da: Kiribati el: Κιριμπάτι en: Kiribati eo: Kiribato fa: کیریباتی fi: Kiribati fr: Kiribati fy: Kiribaty ga: Cireabaití he: קיריבטי hu: Kiribati is: Kíribatí li: Kiribati-eilen lt: Kiribatis lv: Kiribati mn: Кирибати "no": Kiribati pl: Kiribati ru: Кирибати sv: Kiribati th: ประเทศคิริบาส uk: Кірибаті zh: 基里巴斯 ================================================ FILE: settings/country-names/km.yaml ================================================ name: default: Comores Komori جزر القمر af: Komore ar: جزر القمر br: Komorez ca: Comores cs: Komory cy: Comoros da: Comorerne de: Komoren el: Κομόρες en: Comoros eo: Komoroj es: Comoras et: Komoorid eu: Komoreak fa: اتحاد قمر fi: Komorit fr: Comores fy: de Komoaren ga: Oileáin Chomóra gd: Comòras he: קומורו hr: Komori hu: Comore-szigetek ia: Comoros id: Komoro io: Komori is: Kómoreyjar it: Comore ja: コモロ la: Insulae Comorianae lb: Komoren lt: Komorai lv: Komoru salas mk: Комори mn: Коморын арлууд nb: Komorene nl: Comoren nn: Komorane "no": Komorene pl: Komory ps: قمرټاپوګان pt: Comores ru: Коморские острова se: Komorosullot sk: Komory sl: Komori sr: Комори sv: Komorerna ta: கொமொரோசு th: ประเทศคอโมโรส tr: Komorlar ug: كومور ئاراللىرى uk: Коморські Острови ur: اتحاد القمری vi: Comoros vo: Komoruäns zh: 科摩洛 ================================================ FILE: settings/country-names/kn.yaml ================================================ name: default: Saint Kitts and Nevis af: Sint Kitts en Nevis ar: سانت كيتس ونيفس be: Святы Кітс і Невіс br: Saint Kitts-ha-Nevis ca: Saint Kitts i Nevis cs: Svatý Kryštof a Nevis cy: Saint Kitts a Nevis da: Saint Kitts og Nevis de: St. Kitts und Nevis el: Άγιος Χριστόφορος και Νέβις en: Saint Kitts and Nevis eo: Sankta Kristoforo kaj Neviso es: San Cristóbal y Nieves et: Saint Kitts ja Nevis fa: سنت کیتس و نویس fi: Saint Kitts ja Nevis fr: Saint-Christophe-et-Niévès fy: Sint Kitts en Nevis ga: San Críostóir-Nimheas gd: Naomh Crìstean agus Nibheis he: סנט קיטס ונוויס hr: Sveti Kristofor i Nevis hu: Saint Kitts és Nevis ia: Sancte Christophoro e Nevis id: Saint Kitts dan Nevis io: Santa Kitts e Nevis is: Sankti Kristófer og Nevis it: Saint Kitts e Nevis lb: Saint Kitts a Nevis li: Saint-Kitts lt: Sent Kitsas ir Nevis mk: Свети Кристифер и Невис mn: Сент-Киттс ба Невис nl: Saint Kitts en Nevis "no": Saint Kitts og Nevis pl: Saint Kitts i Nevis pt: São Cristóvão e Nevis ru: Сент-Китс и Невис se: Saint Kitts ja Nevis sl: Sveti Krištof in Nevis sv: Saint Kitts och Nevis ta: செயிண்ட் கிட்சும் நெவிசும் th: ประเทศเซนต์คิตส์และเนวิส tr: Saint Kitts ve Nevis uk: Сент-Кіттс і Невіс vi: Saint Kitts và Nevis vo: Sankiteän e Neviseän zh: 圣基茨和尼维斯 ================================================ FILE: settings/country-names/kp.yaml ================================================ name: default: 조선민주주의인민공화국 af: Noord-Korea am: ስሜን ኮርያ an: Coreya d'o Norte ar: كوريا الشمالية as: উত্তৰ কোৰিয়া az: Koreya Xalq Demokratik Respublikası ba: Корея Халыҡ-Демократик Республикаһы be: Карэйская Народна-Дэмакратычная Рэспубліка bg: Северна Корея bh: उत्तर कोरिया bn: উত্তর কোরিয়া bo: བྱང་ཀོ་རི་ཡ། br: Republik Poblel ha Demokratel Korea bs: Sjeverna Koreja ca: Corea del Nord ce: Корейн Халкъан-Демократин Республика cs: Severní Korea cy: Gogledd Corea da: Nordkorea de: Nordkorea dv: އުތުރު ކޮރެއާ dz: བྱང་ཀོ་རི་ཡ་ ee: North Korea el: Βόρεια Κορέα en: North Korea eo: Nord-Koreio es: Corea del Norte et: Põhja-Korea eu: Ipar Korea fa: کره شمالی fi: Pohjois-Korea fo: Norðurkorea fr: Corée du Nord fy: Noard-Koreä ga: An Chóiré Thuaidh gd: Coirèa a Tuath gl: Corea do Norte gn: Yvate Koréa gu: ઉત્તર કોરિયા gv: Yn Chorea Twoaie ha: Koriya ta Arewa he: קוריאה הצפונית hi: उत्तर कोरिया hr: Sjeverna Koreja ht: Kore dinò hu: Koreai Népi Demokratikus Köztársaság (Észak-Korea) hy: Կորեայի Ժողովրդա-Դեմոկրատական Հանրապետություն ia: Corea del Nord id: Korea Utara ie: Nord-Korea ik: Nigiq Korea io: Nord-Korea is: Norður-Kórea it: Corea del Nord ja: 朝鮮民主主義人民共和国 jv: Koréa Lor ka: კორეის სახალხო დემოკრატიული რესპუბლიკა kg: Hangu ya Node ki: North Korea kk: Корей Халық Демократиялық Республикасы kl: Korea Avannarleq km: កូរ៉េខាងជើង kn: ಉತ್ತರ ಕೊರಿಯಾ ko: 조선민주주의인민공화국 ku: Korêya Bakur kv: Корея Войтыр Демократия Республика kw: Korea Gledh ky: Корей Эл-Демократиялык Республикасы la: Res publica popularis democratica Coreana lb: Demokratesch Volleksrepublik Korea li: Noord-Korea ln: Koré ya Nola lo: ປະເທດເກົາຫຼີເໜືອ lt: Šiaurės Korėja lv: Ziemeļkoreja mg: Korea Avaratra mi: Kōrea-ki-te-raki mk: Демократска Народна Република Кореја ml: ഉത്തര കൊറിയ mn: Умард Солонгос mr: उत्तर कोरिया ms: Korea Utara mt: Korea ta' Fuq my: ကိုရီးယား ဒီမိုကရက်တစ် ပြည်သူ့သမ္မတနိုင်ငံ na: Ripubrikit Engame Korea ne: उत्तर कोरिया nl: Noord-Korea "no": Nord-Korea nv: Kolíya Bikéyah Náhookǫsjí Siʼánígíí oc: Corèa del Nòrd om: Kooriyaa Kaabaa or: ପୂର୍ବ କୋରିଆ os: Корейы Адæмон Демократон Республикæ pa: ਉੱਤਰੀ ਕੋਰੀਆ pl: Korea Północna ps: شمالي کوريا pt: Coreia do Norte qu: Chusun Runakapaq Runallaqta Republika ro: Coreea de Nord ru: Северная Корея rw: Koreya y’Amajyaruguru sa: उत्तर कोरिया sc: Corea de su Norte sd: اتر ڪوريا se: Davvi-Korea sh: Demokratska Narodna Republika Koreja si: උතුරු කොරියාව sk: Kórejská ľudovodemokratická republika sl: Severna Koreja sn: North Korea so: Waqooyiga Kuuriya sq: Koreja e Veriut sr: Северна Кореја ss: IKhoriya leseNyakatfo su: Koréa Kalér sv: Nordkorea sw: Korea Kaskazini ta: வட கொரியா te: ఉత్తర కొరియా tg: Кореяи Шимолӣ th: ประเทศเกาหลีเหนือ tk: Demirgazyk Koreýa tl: Hilagang Korea tr: Kuzey Kore ts: North Korea tt: Корея Халык Демократик Җөмһүрияте ug: شىمالىي چاۋشيەن uk: Корейська Народно-Демократична Республіка ur: شمالی کوریا uz: Koreya Xalq Demokratik Respublikasi vi: Bắc Triều Tiên vo: Nolüda-Koreyän wo: Kore gu Bëj-gànnaar xh: Coreia do Twoaie yi: צפון קארעע yo: Kòréà Àríwá za: Cauzsenh Minzcujcujyi Yinzminz Gunghozgoz zh: 朝鲜民主主义人民共和国 ================================================ FILE: settings/country-names/kr.yaml ================================================ name: default: 대한민국 af: Suid-Korea ak: Anaafo Koria am: ደቡብ ኮርያ an: Corea d'o Sur ar: كوريا الجنوبية as: দক্ষিণ কোৰিয়া az: Cənubi Koreya ba: Көньяҡ Корея be: Паўднёвая Карэя bg: Южна Корея bi: Saot Koria bn: দক্ষিণ কোরিয়া bo: ལྷོ་ཀོ་རི་ཡ། br: Korea ar Su bs: Južna Koreja ca: Corea del Sud ce: Къилба Корей cs: Jižní Korea cy: De Corea da: Sydkorea de: Südkorea dv: ދެކުނު ކޮރެއާ dz: ལྷོ་ཀོ་རི་ཡ་ ee: South Korea el: Νότια Κορέα en: South Korea eo: Sud-Koreio es: Corea del Sur et: Lõuna-Korea eu: Hego Korea fa: کره جنوبی fi: Etelä-Korea fo: Suðurkorea fr: Corée du Sud fy: Súd-Korea ga: An Chóiré Theas gd: Coirea a Deas gl: Corea do Sur gn: Yvykorea gu: દક્ષિણ કોરિયા gv: Yn Chorea Yiass he: קוריאה הדרומית hi: दक्षिण कोरिया hr: Južna Koreja ht: Kore disid hu: Dél-Korea hy: Հարավային Կորեա ia: Corea del Sud id: Korea Selatan ie: Sud-Korea io: Sud-Korea is: Suður-Kórea it: Corea del Sud ja: 大韓民国 jv: Koréa Kidul ka: სამხრეთი კორეა ki: South Korea kk: Оңтүстік Корея kl: Korea Kujalleq km: កូរ៉េខាងត្បូង kn: ದಕ್ಷಿಣ ಕೊರಿಯಾ ko: 대한민국 ks: दक्षिण कोरिया ku: Korêya Başûr kv: Корея Республика kw: Korea Dhyhow ky: Корея Республикасы la: Corea Meridionalis lb: Südkorea li: Zuud-Korea ln: Kore ya Sidi lo: ປະເທດເກົາຫຼີໃຕ້ lt: Pietų Korėja lv: Dienvidkoreja mg: Korea Atsimo mi: Kōrea-ki-te-tonga mk: Република Кореја ml: ദക്ഷിണ കൊറിയ mn: Өмнөд Солонгос mr: दक्षिण कोरिया ms: Korea Selatan mt: Korea t'Isfel my: တောင်ကိုရီးယားနိုင်ငံ na: Ripubrikin Korea ne: दक्षिण कोरिया nl: Zuid-Korea "no": Sør-Korea nv: Kolíya Bikéyah Shádiʼááhjí Siʼánígíí oc: Corèa del Sud or: ଦକ୍ଷିଣ କୋରିଆ os: Хуссар Корей pl: Korea Południowa ps: سوېلي کوريا pt: Coreia do Sul qu: Urin Kuriya rm: Corea dal sid ro: Coreea de Sud ru: Республика Корея rw: Koreya y’Amajyepfo sa: दक्षिण कोरिया sc: Corea de su Sud se: Lulli-Korea sg: Korëe tî Mbongo sh: Južna Koreja si: දකුණු කොරියාව sk: Južná Kórea sl: Južna Koreja sm: Kolea i Saute sn: Korea, South so: Koonfur Kuuriya sq: Koreja e Jugut sr: Јужна Кореја ss: IKhoriya leseNingizimu su: Koréa Kidul sv: Sydkorea sw: Korea Kusini ta: தென்கொரியா te: దక్షిణ కొరియా th: ประเทศเกาหลีใต้ tk: Günorta Koreýa tl: Timog Korea tr: Güney Kore tt: Көньяк Корея ug: جەنۇبىي چاۋشيەن uk: Південна Корея ur: جنوبی کوریا uz: Janubiy Koreya vi: Hàn Quốc vo: Sulüda-Koreyän wo: Kore gu Bëj-saalum yi: דרום קארעע yo: Kòréà Gúúsù za: Hanzgoz zh: 韩国/南韓 ================================================ FILE: settings/country-names/kw.yaml ================================================ name: default: الكويت af: Koeweit am: ኩዌት an: Kuwait ar: الكويت az: Küveyt ba: Күвейт be: Кувейт bg: Кувейт bn: কুয়েত bo: ཁུ་ཝི་ཐི། br: Koweit bs: Kuvajt ca: Kuwait ce: Кувейт cs: Kuvajt cv: Кувейт cy: Kuwait da: Kuwait de: Kuwait dv: ކުވެއިތު dz: ཀུ་ཝེཊ་ el: Κουβέιτ en: Kuwait eo: Kuvajto es: Kuwait et: Kuveit eu: Kuwait fa: کویت fi: Kuwait fo: Kuveit fr: Koweït fy: Koeweit ga: Cuáit gd: Cubhait gl: Kuwait gu: કુવૈત gv: Yn Choowait he: כווית hi: कुवैत hr: Kuvajt ht: Kowet hu: Kuvait hy: Քուվեյթ ia: Kuwait id: Kuwait ie: Kuwait io: Kuwait is: Kúveit it: Kuwait ja: クウェート jv: Kuwait ka: ქუვეითი kk: Кувейт kl: Kuwait km: គុយវ៉ែត kn: ಕುವೈತ್ ko: 쿠웨이트 ku: Kuweyt kv: Кувейт kw: Koweyt la: Cuvaitum lb: Kuwait li: Koeweit ln: Koweit lt: Kuveitas lv: Kuveita mk: Кувајт ml: കുവൈറ്റ് mn: Кувейт mr: कुवेत ms: Kuwait my: ကူဝိတ်နိုင်ငံ na: Kuwait ne: कुवेत nl: Koeweit "no": Kuwait nv: Kóóweiʼ oc: Kowait or: କୁଏତ os: Кувейт pl: Kuwejt ps: کوېټ pt: Kuwait qu: Kuwait ro: Kuweit ru: Кувейт rw: Koweti sa: कुवैत se: Kuwait sh: Kuvajt sk: Kuvajt sl: Kuvajt so: Kuwayt sq: Kuvajti sr: Кувајт ss: IKhuwathi su: Kuwait sv: Kuwait sw: Kuwait ta: குவைத் te: కువైట్ tg: Кувайт th: ประเทศคูเวต tk: Kuweýt tl: Kuwait tr: Kuveyt tt: Күвәйт ug: كۇۋەيت uk: Кувейт ur: کویت uz: Quvayt vi: Kuwait vo: Kovätän wo: Kowet yi: קואווייט yo: Kuwaiti zh: 科威特 ================================================ FILE: settings/country-names/ky.yaml ================================================ name: default: Cayman Islands af: Kaaimanseilande ak: Kemanfo Islands am: ካይማን ደሴቶች ar: جزر الكايمان az: Kayman Adaları be: Кайманавы астравы bg: Кайманови острови bm: Bama Gun bn: কেম্যান দ্বীপপুঞ্জ bo: ཁེ་མེན་གླིང་ཕྲན། br: Inizi Cayman bs: Kajmanska Ostrva ca: Illes Caiman cs: Kajmanské ostrovy cy: Ynysoedd Cayman da: Caymanøerne de: Kaimaninseln dz: ཁེ་མེན་གླིང་ཚོམ ee: Kayman ƒudomekpowo nutome el: Νήσοι Κέιμαν en: Cayman Islands eo: Kajmana Insularo es: Islas Caimán et: Kaimanisaared eu: Kaiman Uharteak fa: جزایر کایمان ff: Duuɗe Kaymaa fi: Caymansaaret fo: Caymanoyggjarnar fr: Îles Caïmans fy: de Kaaimaneilannen ga: Oileáin Cayman gd: Na h-Eileanan Caimean gl: Illas Caimán gu: કેયમેન આઇલૅંડ્સ gv: Ellanyn Cayman ha: Tsibiran Kaiman he: איי קיימן hi: केमैन द्वीपसमूह hr: Kajmanski Otoci hu: Kajmán-szigetek ia: Insulas de Caiman id: Kepulauan Cayman io: Insuli Kaiman is: Caymaneyjar it: Isole Cayman ja: ケイマン諸島 ka: კაიმანის კუნძულები ki: Visiwa vya Kayman km: កោះ​កៃម៉ង់ kn: ಕೇಮನ್ ದ್ವೀಪಗಳು ko: 케이맨제도 ks: کیمَن جٔزیٖرٕ ku: Giravên Caymanê lb: Kaimaninselen lg: Bizinga ebya Kayimaani ln: Bisanga bya Kayíma lo: ຄາຍແມນ ອິດແລນ lt: Kaimanų salos lv: Kaimanu salas mg: Nosy Kayman mk: Кајмански Острови ml: കേയ്മാൻ ദ്വീപുകൾ mn: Кайманы Арлууд mr: केमन बेटे mt: Gżejjer Kajmani my: ကေမန် ကျွန်းစု nb: Caymanøyene ne: केयमान टापु nl: Kaaimaneilanden nn: Caymanøyane "no": Caymanøyene or: କେମ୍ୟାନ୍ ଦ୍ବୀପପୁଞ୍ଜ pl: Kajmany pt: Ilhas Cayman rm: Inslas Cayman rn: Ibirwa bya Keyimani ro: Insulele Cayman ru: Острова Кайман se: Cayman-sullot sg: Âzûâ Ngundë, Kaimäni si: කේමන් දූපත් sk: Kajmanie ostrovy sl: Kajmanski otoki sn: Zvitsuwa zveCayman sr: Кајманска Острва sv: Caymanöarna sw: Visiwa vya Kayman ta: கேமென் தீவுகள் te: కేమాన్ దీవులు th: หมู่เกาะเคย์แมน ti: ካይማን ደሴቶች to: ʻOtumotu Keimeni tr: Kayman Adaları uk: Кайманові острови ur: کیمین آئلینڈز vi: Quần đảo Cayman yo: Orílẹ́ède Etíokun Kámánì zh: 开曼群岛 zu: i-Cayman Islands ================================================ FILE: settings/country-names/kz.yaml ================================================ name: default: Қазақстан ab: Ҟазаҟсҭан af: Kasakstan am: ካዛክስታን an: Cazaquistán ar: كازاخستان av: Хъазахъистан az: Qazaxıstan ba: Ҡаҙағстан be: Казахстан bg: Казахстан bi: Kazakhstan bn: কাজাখস্তান bo: ཀཛ་ཀིསུ་གཏན། br: Kazakstan bs: Kazahstan ca: Kazakhstan ce: Кхазакхстан cs: Kazachstán cu: Каꙁахстанъ cv: Казахстан cy: Kazakstan da: Kasakhstan de: Kasachstan dv: ކަޒަކިސްތާން ee: Kazakhstan el: Καζακστάν en: Kazakhstan eo: Kazaĥio es: Kazajistán et: Kasahstan eu: Kazakhstan fa: قزاقستان fi: Kazakstan fo: Kasakstan fr: Kazakhstan fy: Kazakstan ga: An Chasacstáin gd: Casachstàn gl: Casaquistán gn: Kazajistán gu: કઝાકિસ્તાન gv: Yn Chassaghstaan he: קזחסטן hi: कज़ाख़िस्तान hr: Kazahstan ht: Kazakstan hu: Kazahsztán hy: Ղազախստան ia: Kazakhstan id: Kazakhstan ie: Kazakstan io: Kazakstan is: Kasakstan it: Kazakistan ja: カザフスタン jv: Kazakhstan ka: ყაზახეთი kg: Kazakhstan kk: Қазақстан kl: Kasakhstani km: កាសាក់ស្ថាន kn: ಕಜಾಕಸ್ಥಾನ್ ko: 카자흐스탄 ku: Qazaxistan kv: Казахстан kw: Pow Kazagh ky: Казакстан Республикасы la: Kazachstania lb: Kasachstan li: Kazachstan ln: Kazakstáni lt: Kazachstanas lv: Kazahstāna mg: Kazakstàna mi: Katatānga mk: Казахстан ml: ഖസാഖ്സ്ഥാൻ mn: Казахстан mr: कझाकस्तान ms: Kazakhstan mt: Każakistan my: ကာဇက်စတန်နိုင်ငံ na: Kadaketan nl: Kazachstan "no": Kasakhstan nv: Kʼazah Bikéyah oc: Cazacstan or: କାଜାଖସ୍ତାନ os: Хъазахстан pa: ਕਜਾਖਸਤਾਨ pl: Kazachstan ps: قزاقستان pt: Cazaquistão qu: Qasaqsuyu rm: Kasachstan ro: Kazahstan ru: Казахстан rw: Kazakisitani sa: कजाकस्थान se: Kazakstan sh: Kazahstan si: කසක්ස්තානය sk: Kazachstan sl: Kazahstan so: Kasakhstan sq: Kazakistani sr: Казахстан ss: IKhazakhi su: Kazastan sv: Kazakhstan sw: Kazakhstan ta: கசக்ஸ்தான் te: కజకస్తాన్ tg: Қазоқистон th: ประเทศคาซัคสถาน tk: Gazagystan tl: Kazakhstan tr: Kazakistan tt: Казакъстан ug: قازاقىستان uk: Казахстан ur: قازقستان uz: Qozogʻiston vi: Kazakhstan vo: Kazakistän wo: Kasakistaan yi: קאזאכסטאן yo: Kàsàkstán za: Hahsazgwswhdanj zh: 哈萨克斯坦/哈薩克 ================================================ FILE: settings/country-names/la.yaml ================================================ name: default: ປະເທດລາວ af: Laos am: ላዎስ an: Laos ar: لاوس az: Laos ba: Лаос be: Лаос bg: Лаос bi: Laos bn: লাওস bo: ལའོ་སུ། br: Laos bs: Laos ca: Laos ce: Лаос cs: Laos cv: Лаос cy: Laos da: Laos de: Laos dv: ލާއޯސް el: Λάος en: Laos eo: Laoso es: Laos et: Laos eu: Laos fa: لائوس fi: Laos fo: Laos fr: Laos fy: Laos ga: Laos gd: Làthos gl: Laos gu: લાઓસ gv: Yn Laos he: לאוס hi: लाओस hr: Laos ht: Laos hu: Laosz hy: Լաոս ia: Laos id: Laos ie: Laos io: Laos is: Laos it: Laos ja: ラオス jv: Laos ka: ლაოსი ki: Laos kk: Лаос km: ឡាវ kn: ಲಾವೋಸ್ ko: 라오스 ku: Laos kv: Лаос kw: Pow Lao la: Laotia lb: Laos li: Laos ln: Laos lo: ປະເທດລາວ lt: Laosas lv: Laosa mi: Rāoho mk: Лаос ml: ലാവോസ് mn: Лаос mr: लाओस ms: Laos my: လာအိုနိုင်ငံ na: Raot nl: Laos "no": Laos nv: Lááʼos oc: Laos or: ଲାଓସ os: Лаос pa: ਲਾਓਸ pl: Laos ps: لاووس pt: Laos qu: Law suyu ro: Laos ru: Лаос rw: Lawosi sa: लाओस se: Laos sh: Laos si: ලාඕසය sk: Laos sl: Laos so: Laos sq: Laosi sr: Лаос ss: ILawoso su: Laos sv: Laos sw: Laos ta: லாவோஸ் te: లావోస్ tg: Лаос th: ประเทศลาว tk: Laos tl: Laos tr: Laos tt: Лаос ug: لائوس uk: Лаос ur: لاؤس uz: Laos vi: Lào vo: Laosän wo: Réewum Lawos yo: Láọ̀s za: Lao zh: 老挝/寮國 ================================================ FILE: settings/country-names/lb.yaml ================================================ name: default: لبنان af: Libanon am: ሊባኖስ an: Liban ar: لبنان az: Livan ba: Ливан be: Ліван bg: Ливан bm: Lubenan bn: লেবানন bo: ལེ་པ་ནོན། br: Liban bs: Liban ca: Líban ce: Ливан cs: Libanon cv: Ливан cy: Libanus da: Libanon de: Libanon dv: ލުބުނާން dz: ལེ་བཱ་ནཱོན་ el: Λίβανος en: Lebanon eo: Libano es: Líbano et: Liibanon eu: Libano fa: لبنان fi: Libanon fo: Libanon fr: Liban fy: Libanon ga: An Liobáin gd: Leabanon gl: Líbano gn: Lívano gu: લેબેનાન gv: Yn Livaan he: לבנון hi: लेबनान hr: Libanon ht: Liban hu: Libanon hy: Լիբանան ia: Libano id: Lebanon ie: Libano io: Libano is: Líbanon it: Libano ja: レバノン jv: Libanon ka: ლიბანი kk: Ливан kl: Lebanon km: លីបង់ ko: 레바논 ku: Libnan kv: Ливан kw: Lebnon ky: Ливан la: Libanus lb: Libanon li: Libanon lt: Libanas lv: Libāna mi: Repanona mk: Либан ml: ലെബനാൻ mn: Ливан mr: लेबेनॉन ms: Lubnan mt: Libanu my: လက်ဘနွန်နိုင်ငံ na: Ribanon nl: Libanon "no": Libanon nv: Łíbanoo oc: Liban or: ଲେବନାନ os: Ливан pa: ਲਿਬਨਾਨ pl: Liban ps: لېبنان pt: Líbano qu: Libanu ro: Liban ru: Ливан rw: Libani sa: लेबनान se: Libanon sh: Libanon sk: Libanon sl: Libanon so: Lubnaan sq: Libani sr: Либан ss: ILibhanoni su: Libanon sv: Libanon sw: Lebanoni ta: லெபனான் te: లెబనాన్ tg: Лубнон th: ประเทศเลบานอน tk: Liwan tl: Libano tr: Lübnan tt: Ливан ug: لىۋان uk: Ліван ur: لبنان uz: Livan vi: Liban vo: Libanän wo: Libaa yi: לבנון yo: Lẹ́bánọ́nì zh: 黎巴嫩 ================================================ FILE: settings/country-names/lc.yaml ================================================ name: default: Saint Lucia af: Sint Lucia ar: سانت لوسيا be: Святая Лусія br: Saint Lucia cs: Svatá Lucie da: Saint Lucia de: St. Lucia el: Αγία Λουκία en: Saint Lucia eo: Sankta Lucio es: Santa Lucía fa: سنت لوسیا fi: Saint Lucia fr: Sainte-Lucie fy: Sint Lusia ga: San Lucia gd: Naomh Lùisia he: סנט לוסיה hr: Sveta Lucija hu: Saint Lucia is: Sankti Lúsía it: Santa Lucia ja: セントルシア la: Sancta Lucia li: Santa Lucia lt: Sent Lusija lv: Sentlūsija mn: Сент Люсиа nl: Saint Lucia "no": Saint Lucia pl: Saint Lucia pt: Santa Lúcia ru: Сент-Люсия se: Saint Lucia sk: Svätá Lucia sl: Sveta Lucija sv: Saint Lucia ta: செயிண்ட் லூசியா th: ประเทศเซนต์ลูเซีย uk: Сент-Люсія vo: Sanlusiyän zh: 圣卢西亚岛 ================================================ FILE: settings/country-names/li.yaml ================================================ name: default: Liechtenstein ar: ليختنشتاين be: Лiхтэнштэйн br: Liechtenstein bs: Lihtenštajn cs: Lichtenštejnsko de: Liechtenstein el: Λίχτενσταϊν en: Liechtenstein eo: Liĥtenŝtejno fa: لیختن‌اشتاین fi: Liechtenstein fo: Liktinstein fr: Liechtenstein fy: Lychtenstein ga: Lichtinstéin he: ליכטנשטיין hr: Lihtenštajn hu: Liechtenstein ia: Liechtenstein io: Liechtenstein is: Liechtenstein it: Liechtenstein ja: リヒテンシュタイン ku: Lînxiniştayn la: Lichtenstenum lt: Lichtenšteinas lv: Lihtenšteina mi: Rīkeneteina mk: Лихтенштајн mn: Лихтенштейн nl: Liechtenstein "no": Liechtenstein pl: Liechtenstein ru: Лихтенштейн se: Liechtenstein sk: Lichtenštajnsko sl: Lihtenštajn sr: Лихтенштајн sv: Liechtenstein ta: லீக்கின்ஸ்டைன் th: ประเทศลิกเตนสไตน์ tr: Lihtenştayn uk: Ліхтенштейн vo: Ligtänstän zh: 列支敦士登 ================================================ FILE: settings/country-names/lk.yaml ================================================ name: default: ශ්‍රී ලංකාව இலங்கை af: Sri Lanka am: ሽሪ ላንካ an: Sri Lanka ar: سريلانكا az: Şri-Lanka ba: Шри-Ланка be: Шры-Ланка bg: Шри Ланка bn: শ্রীলঙ্কা bo: སེང་ག་གླིང་། br: Sri Lanka bs: Šri Lanka ca: Sri Lanka cs: Šrí Lanka cv: Шри-Ланка cy: Sri Lanka da: Sri Lanka de: Sri Lanka dv: އޮޅުދޫކަރަ dz: ཤྲཱྀ་ལངཀ་ el: Σρι Λάνκα en: Sri Lanka eo: Sri-Lanko es: Sri Lanka et: Sri Lanka eu: Sri Lanka fa: سری‌لانکا fi: Sri Lanka fo: Sri Lanka fr: Sri Lanka fy: Sry Lanka ga: Srí Lanca gd: Sri Lanca gl: Sri Lanka - Sri Lankā gu: શ્રીલંકા gv: Sri Lanka he: סרי לנקה hi: श्रीलंका hr: Šri Lanka ht: Srilanka hu: Srí Lanka hy: Շրի Լանկա ia: Sri Lanka id: Sri Lanka ie: Sri Lanka io: Sri Lanka is: Srí Lanka it: Sri Lanka ja: スリランカ jv: Sri Lanka ka: შრი-ლანკა ki: Sri Lanka kk: Шри-Ланка km: ស្រីលង្កា kn: ಶ್ರೀಲಂಕಾ ko: 스리랑카 ku: Srî Lanka kv: Шри-Ланка kw: Shri Lanka la: Taprobane lb: Sri Lanka li: Sri Lanka ln: Sri Lanka lt: Šri Lanka lv: Šrilanka mg: Sri Lanka mi: Hīraka mk: Шри Ланка ml: ശ്രീലങ്ക mn: Шри Ланка mr: श्रीलंका ms: Sri Lanka mt: Sri Lanka my: သီရိလင်္ကာနိုင်ငံ na: Sri Lanka ne: श्रीलंका nl: Sri Lanka "no": Sri Lanka nv: Swii Lankʼa oc: Sri Lanka om: Sirilaankaa or: ଶ୍ରୀଲଙ୍କା os: Шри-Ланка pa: ਸ੍ਰੀਲੰਕਾ pl: Sri Lanka ps: سریلانکا pt: Seri-Lanca qu: Sri Lanka ro: Sri Lanka ru: Шри-Ланка rw: Siri Lanka sa: श्रीलङ्का se: Sri Lanka sh: Šri Lanka si: ශ්‍රී ලංකාව sk: Srí Lanka sl: Šrilanka sm: Sri Lanka so: Siri Lanka sq: Sri Lanka sr: Шри Ланка ss: Siri Lanka su: Sri Langka sv: Sri Lanka sw: Sri Lanka ta: இலங்கை te: శ్రీలంక tg: Шри-Ланка th: ประเทศศรีลังกา tk: Şri-Lanka tl: Sri Lanka tr: Sri Lanka tt: Шри-Ланка ug: سرىلانكا uk: Шрі-Ланка ur: سری لنکا uz: Shri-Lanka vi: Sri Lanka vo: Sri-Lankän wo: Siri Laanka yi: סרי לאנקא yo: Sri Lanka zh: 斯里蘭卡 ================================================ FILE: settings/country-names/lr.yaml ================================================ name: default: Liberia af: Liberië am: ላይቤሪያ an: Liberia ar: ليبيريا az: Liberiya ba: Либерия be: Ліберыя bg: Либерия bm: Liberia bn: লাইবেরিয়া bo: ལི་བེ་རི་ཡ། br: Liberia bs: Liberija ca: Libèria ce: Либери cs: Libérie cv: Либери cy: Liberia da: Liberia de: Liberia dv: ލައިބީރިއާ ee: Liberia el: Λιβερία en: Liberia eo: Liberio es: Liberia et: Libeeria eu: Liberia fa: لیبریا ff: Labiriyaa fi: Liberia fo: Liberia fr: Liberia fy: Libearia ga: An Libéir gd: Libèiria gl: Liberia gv: Yn Laibeer he: ליבריה hi: लाइबेरिया hr: Liberija ht: Liberya hu: Libéria hy: Լիբերիա ia: Liberia id: Liberia ie: Liberia io: Liberia is: Líbería it: Liberia ja: リベリア jv: Liberia ka: ლიბერია kg: Liberia kk: Либерия kn: ಲೈಬೀರಿಯ ko: 라이베리아 ku: Lîberya kw: Liberi la: Liberia lb: Liberia li: Liberia ln: Liberia lt: Liberija lv: Libērija mi: Raipiri mk: Либерија ml: ലൈബീരിയ mn: Либери mr: लायबेरिया ms: Liberia mt: Liberja my: လိုက်ဘေးရီးယားနိုင်ငံ nl: Liberia "no": Liberia oc: Libèria or: ଲାଇବେରିଆ os: Либери pl: Liberia ps: لايبېريا pt: Libéria qu: Libirya rm: Liberia ro: Liberia ru: Либерия rw: Liberiya sa: लायबीरिया sc: Libèria se: Liberia sg: Liberïa sh: Liberija sk: Libéria sl: Liberija sn: Liberia so: Liberia sq: Liberia sr: Либерија ss: ILibheriya st: Liberia su: Liberia sv: Liberia sw: Liberia ta: லைபீரியா tg: Либерия th: ประเทศไลบีเรีย tk: Liberiýa tl: Liberia tr: Liberya ts: Layiberiya tt: Либерия ug: لىبېرىيە uk: Ліберія ur: لائبیریا uz: Liberiya vi: Liberia vo: Liberän wo: Libeeria yi: ליבעריע yo: Làìbéríà zh: 利比里亚 zu: ILiberia ================================================ FILE: settings/country-names/ls.yaml ================================================ name: default: Lesotho af: Lesotho am: ሌሶቶ an: Lesoto ar: ليسوتو az: Lesoto ba: Лесото be: Лесота bg: Лесото bm: Lesoto bn: লেসোথো bo: ལི་སོ་ཐོ། br: Lesotho bs: Lesoto ca: Lesotho ce: Лесото cs: Lesotho cv: Лесото cy: Lesotho da: Lesotho de: Lesotho dv: ލެސޯތޯ ee: Lesotho el: Λεσότο en: Lesotho eo: Lesoto es: Lesoto et: Lesotho eu: Lesotho fa: لسوتو fi: Lesotho fo: Lesoto fr: Lesotho fy: Lesoto ga: Leosóta gd: Leasoto gl: Lesoto gv: Lesoto he: לסוטו hi: लेसोथो hr: Lesoto ht: Lezoto hu: Lesotho hy: Լեսոթո ia: Lesotho id: Lesotho ie: Lesotho io: Lesotho is: Lesótó it: Lesotho ja: レソト jv: Lesotho ka: ლესოთო kg: Lesotho kk: Лесото kn: ಲೆಸೊಥೊ ko: 레소토 ku: Lesoto kw: Lesotho la: Lesothum lb: Lesotho li: Lesotho ln: Lesoto lt: Lesotas lv: Lesoto mi: Teroto mk: Лесото ml: ലെസോത്തോ mn: Лесото mr: लेसोथो ms: Lesotho mt: Lesoto my: လီဆိုသိုနိုင်ငံ na: Resoto nl: Lesotho "no": Lesotho nv: Sotho Dineʼé Bikéyah oc: Lesotho or: ଲିସୁଟୁ os: Лесото pa: ਲਿਸੋਥੋ pl: Lesotho pt: Lesoto qu: Suthusuyu ro: Lesotho ru: Лесото rw: Lesoto sa: लेसोथो se: Lesotho sg: Lesôtho sh: Lesoto sk: Lesotho sl: Lesoto sn: Lesotho so: Lesotho sq: Lesoto sr: Лесото ss: ÉLusûtfu st: Lesotho su: Lésotho sv: Lesotho sw: Lesotho ta: லெசோத்தோ tg: Лесото th: ประเทศเลโซโท tk: Lesoto tl: Lesoto tn: Lesotho tr: Lesotho ts: Lesotho tt: Лесото ug: لېسوتو uk: Лесото ur: لیسوتھو uz: Lesoto ve: Li-Sotho vi: Lesotho vo: Lesotän wo: Lesoto yi: לעסאטא yo: Lèsóthò zh: 莱索托/賴索托 zu: OSotho ================================================ FILE: settings/country-names/lt.yaml ================================================ name: default: Lietuva ab: Литва af: Litaue ak: Lituania am: ሊትዌኒያ an: Lituania ar: ليتوانيا ay: Lituaña az: Litva ba: Литва be: Літва bg: Литва bi: Litwania bn: লিথুয়ানিয়া bo: ལི་ཐུ་ཨེ་ནི་ཡ། br: Lituania bs: Litvanija ca: Lituània ce: Литва ch: Lituania co: Lituania cs: Litva cu: Литъва cv: Литва cy: Lithwania da: Litauen de: Litauen dv: ލިތުއޭނިއާ dz: ལི་ཐུ་ནི་ཡ། ee: Lithuania el: Λιθουανία en: Lithuania eo: Litovio es: Lituania et: Leedu eu: Lituania fa: لیتوانی ff: Lituwaniya fi: Liettua fj: Lituani fo: Litava fr: Lituanie fy: Litouwen ga: An Liotuáin gd: Liotuàinia gl: Lituania - Lietuva gn: Lituaña gu: લિથુઆનિયા gv: Yn Litaan he: ליטא hi: लिथुआनिया hr: Litva ht: Lityani hu: Litvánia hy: Լիտվա ia: Lituania id: Lituania ie: Lituania io: Lituania is: Litháen it: Lituania iu: ᓕᐋᑐᕙ ja: リトアニア jv: Lituania ka: ლიტვა kg: Lietuva ki: Lithuania kk: Литва kl: Litaueni ko: 리투아니아 ku: Lîtvanya kv: Литва kw: Lithouani ky: Литва la: Lituania lb: Litauen lg: Lithueenia li: Litauwe ln: Litwani lt: Lietuva lv: Lietuva mg: Litoania mi: Rituānia mk: Литванија ml: ലിത്വാനിയ mn: Литва mr: लिथुएनिया ms: Lithuania mt: Litwanja my: လစ်သူယေးနီးယားနိုင်ငံ na: Rituainiya ne: लिथुआनिया nl: Litouwen "no": Litauen nv: Łitʼoowę́ęya ny: Lithuania oc: Lituània or: ଲିଥୁଆନିଆ os: Литва pl: Litwa ps: لېتوانيا pt: Lituânia qu: Lituwa rm: Lituania rn: Lituania ro: Lituania ru: Литва rw: Lituwaniya sa: लेतुवा sc: Lituània se: Lietuva sg: Lituanïi sh: Litvanija sk: Litva sl: Litva so: Lithuaniya sq: Lituania sr: Литванија ss: Lithuwani st: Lituania su: Lituania sv: Litauen sw: Lituanya ta: லித்துவேனியா te: లిథువేనియా tg: Литва th: ประเทศลิทัวเนีย tk: Litwa tl: Litwaniya tr: Litvanya ts: Lithuania tt: Литва ug: لىتۋا uk: Литва ur: لتھووینیا uz: Litva ve: Lituania vi: Litva vo: Lietuvän wa: Litwaneye wo: Lituwaani yi: ליטע yo: Lituéníà zh: 立陶宛 zu: ILithuwaniya ================================================ FILE: settings/country-names/lu.yaml ================================================ name: default: Lëtzebuerg af: Luxemburg am: ሉክሰምበርግ an: Luxemburgo ar: لوكسمبورغ az: Lüksemburq ba: Люксембург be: Люксембург bg: Люксембург bi: Luxembourg bn: লুক্সেমবুর্গ bo: ལུ་སེམ་བའུརག br: Luksembourg bs: Luksemburg ca: Luxemburg ce: Люксембург co: Lussemburgu cs: Lucembursko cu: Люѯємбоургъ cv: Люксембург cy: Lwcsembwrg da: Luxembourg de: Luxemburg ee: Luxembourg el: Λουξεμβούργο en: Luxembourg eo: Luksemburgio es: Luxemburgo et: Luksemburg eu: Luxenburgo fa: لوکزامبورگ fi: Luxemburg fo: Luksemburg fr: Luxembourg fy: Lúksemboarch ga: Lucsamburg gd: Lucsamburg gl: Luxemburgo gn: Luxemburgo gu: લક્ઝેમ્બર્ગ gv: Lucsemburg he: לוקסמבורג hi: लक्ज़मबर्ग hr: Luksemburg ht: Liksanbou hu: Luxemburg hy: Լյուքսեմբուրգ ia: Luxemburg id: Luksemburg ie: Luxemburgia io: Luxemburgia is: Lúxemborg it: Lussemburgo ja: ルクセンブルク jv: Luksemburg ka: ლუქსემბურგი kg: Luxembourg kk: Люксембург kl: Luxembourg ko: 룩셈부르크 ku: Lûksembûrg kv: Люксембург kw: Lushaborg ky: Люксембург la: Luxemburgum lb: Lëtzebuerg li: Luxembörg ln: Luksamburg lt: Liuksemburgas lv: Luksemburga mg: Loksemborga mi: Rakapuō mk: Луксембург ml: ലക്സംബർഗ് mn: Люксембург mr: लक्झेंबर्ग ms: Luxembourg mt: Lussemburgu my: လူဇင်ဘတ်နိုင်ငံ na: Ruketemburg ne: लक्जेम्बर्ग nl: Luxemburg "no": Luxembourg nv: Látsębooʼ oc: Luxemborg or: ଲକ୍ସମବର୍ଗ os: Люксембург pa: ਲਕਸਮਬਰਗ pl: Luksemburg ps: لوګزامبورګ pt: Luxemburgo qu: Luksimbur rm: Luxemburg ro: Luxemburg ru: Люксембург rw: Lugizamburu sa: लक्सम्बर्ग sc: Lussemburgu se: Luxemburg sh: Luksemburg sk: Luxembursko sl: Luksemburg so: Luksemburg sq: Luksemburgu sr: Луксембург ss: Lusembogu su: Luksemburg sv: Luxemburg sw: Luxemburg ta: லக்சம்பர்க் tg: Люксембург th: ประเทศลักเซมเบิร์ก tk: Lýuksemburg tl: Luxembourg tr: Lüksemburg tt: Люксембург ug: ليۇكسېمبۇرگ uk: Люксембург ur: لکسمبرگ uz: Luksemburg vi: Luxembourg vo: Luxämburgän wa: Grande-Dutcheye do Lussimbork wo: Luksambuur yi: לוקסעמבורג yo: Lúksẹ́mbọ̀rg zh: 盧森堡 ================================================ FILE: settings/country-names/lv.yaml ================================================ name: default: Latvija ab: Латвиа af: Letland ak: Latvia am: ላትቪያ an: Letonia ar: لاتفيا av: Латвия az: Latviya ba: Латвия be: Латвія bg: Латвия bh: लैटविया bi: Latvia bn: লাতভিয়া bo: ལ་ཊ་ཝིཡ། br: Latvia bs: Latvija ca: Letònia ce: Латви ch: Letonia co: Lettonia cs: Lotyšsko cu: Латвїꙗ cv: Латви cy: Latfia da: Letland de: Lettland dv: ލެޓުވިއާ dz: ལེཊི་བི་ཡ། ee: Latvia el: Λετονία en: Latvia eo: Latvio es: Letonia et: Läti eu: Letonia fa: لتونی ff: Latvia fi: Latvia fo: Lettland fr: Lettonie fy: Letlân ga: An Laitvia gd: An Laitbhe gl: Letonia gn: Letoña gu: લાટવિયા gv: Yn Latvey ha: Laitfiya he: לטביה hi: लातविया hr: Latvija ht: Letoni hu: Lettország hy: Լատվիա ia: Latvia id: Latvia ie: Latvia ig: Latvia io: Latvia is: Lettland it: Lettonia ja: ラトビア jv: Latvia ka: ლატვია kg: Latvia kk: Латвия kl: Letlandi kn: ಲಾಟ್ವಿಯ ko: 라트비아 ku: Letonya kv: Латвия kw: Latvi ky: Латвия la: Lettonia lb: Lettland lg: Latvia li: Letland ln: Letoni lo: ປະເທດລັດເວຍ lt: Latvija lv: Latvija mg: Latvia mi: Rāwhia mk: Летонија ml: ലാത്‌വിയ mn: Латви mr: लात्व्हिया ms: Latvia mt: Latvja my: လတ်ဗီယာနိုင်ငံ na: Ratebiya ne: लात्भिया nl: Letland "no": Latvia nv: Létbiiya oc: Letònia om: Laativiyaa or: ଲାଟଭିଆ os: Латви pa: ਲਾਤਵੀਆ pi: लाट्विया pl: Łotwa pt: Letónia qu: Litunya rm: Lettonia ro: Letonia ru: Латвия rw: Lativiya sa: लाट्विया sc: Lettonia se: Latvia sh: Letonija si: ලැට්වියාව sk: Lotyšsko sl: Latvija sm: Lativia sn: Latvia so: Latfiya sq: Letonia sr: Летонија ss: ILathiviya st: Latvia su: Latvia sv: Lettland sw: Latvia ta: லாத்வியா te: లాట్వియా tg: Латвия th: ประเทศลัตเวีย tk: Latwiýa tl: Latbiya tr: Letonya ts: Latvia tt: Латвия tw: Latvia ug: لاتۋىيە uk: Латвія ur: لٹویا uz: Latviya vi: Latvia vo: Latviyän wa: Letoneye wo: Letóoni yi: לעטלאנד yo: Látfíà za: Latvia zh: 拉脫維亞 zu: ILatviya ================================================ FILE: settings/country-names/ly.yaml ================================================ name: default: ليبيا af: Libië am: ሊቢያ an: Libia ar: ليبيا az: Liviya ba: Ливия be: Лівія bg: Либия bm: Libya bn: লিবিয়া bo: ལི་པི་ཡ། br: Libia bs: Libija ca: Líbia ce: Ливи cs: Libye cv: Ливи cy: Libya da: Libyen de: Libyen dv: ލީބިޔާ ee: Libya el: Λιβύη en: Libya eo: Libio es: Libia et: Liibüa eu: Libia fa: لیبی fi: Libya fo: Libya fr: Libye fy: Lybje ga: An Libia gd: Libia gl: Libia gn: Livia gv: Yn Leeb ha: Libya he: לוב hi: लीबिया hr: Libija ht: Libi hu: Líbia hy: Լիբիա ia: Libya id: Libya ie: Libya io: Libia is: Líbýa it: Libia ja: リビア jv: Libya ka: ლიბია kg: Libia kk: Ливия kl: Libya kn: ಲಿಬಿಯಾ ko: 리비아 ks: لِبیا ku: Lîbya kw: Libi ky: Ливия la: Libya lb: Libyen li: Libië ln: Libîya lt: Libija lv: Lībija mg: Libia mi: Rīpia mk: Либија ml: ലിബിയ mn: Ливи mr: लीबिया ms: Libya mt: Libja my: လစ်ဗျားနိုင်ငံ ne: लिबिया nl: Libië "no": Libya nv: Łíbya oc: Libia or: ଲିବିଆ os: Ливи pa: ਲੀਬੀਆ pl: Libia ps: لېبيا pt: Líbia qu: Libya ro: Libia ru: Ливия rw: Libiya sa: लिबिया sc: Lìbia se: Libya sg: Libïi sh: Libija si: ලිබියාව sk: Líbya sl: Libija sm: Libya sn: Libya so: Libiya sq: Libia sr: Либија ss: ILibhiya su: Libya sv: Libyen sw: Libya ta: லிபியா te: లిబియా tg: Либия th: ประเทศลิเบีย ti: ሊቢያ tk: Liwiýa tl: Libya tr: Libya ts: Libiya tt: Ливия ug: لىۋىيە uk: Лівія ur: لیبیا uz: Liviya vi: Libya vo: Lübän wa: Libeye wo: Libi yi: ליביע yo: Líbyà zh: 利比亚 zu: ILibiya ================================================ FILE: settings/country-names/ma.yaml ================================================ name: default: Maroc ⵍⵎⵖⵔⵉⴱ المغرب af: Marokko am: ሞሮኮ an: Marruecos ar: المغرب az: Mərakeş ba: Марокко be: Марока bg: Мароко bm: Maroko bn: মরোক্কো bo: མོ་རོ་ཁོ། br: Maroko bs: Maroko ca: Marroc ce: Марокко cs: Maroko cv: Марокко cy: Moroco da: Marokko de: Marokko dv: މައުރިބު el: Μαρόκο en: Morocco eo: Maroko es: Marruecos et: Maroko eu: Maroko fa: مراکش fi: Marokko fo: Marokko fr: Maroc fy: Marokko ga: Maracó gd: Maroco gl: Marrocos gv: Yn Varoc he: מרוקו hi: मोरक्को hr: Maroko ht: Mawòk hu: Marokkó hy: Մարոկկո ia: Marocco id: Maroko ie: Morocco io: Maroko is: Marokkó it: Marocco ja: モロッコ jv: Maroko ka: მაროკო kg: Maroko kk: Мағрибия kn: ಮೊರಾಕೊ ko: 모로코 ks: मोराको ku: Maroko kw: Marokk ky: Марокко la: Marocum lb: Marokko li: Marokko ln: Marɔkɛ lt: Marokas lv: Maroka mg: Marôka mi: Marako mk: Мароко ml: മൊറോക്കൊ mn: Марокко mr: मोरोक्को ms: Maghribi mt: Marokk my: မော်ရိုကိုနိုင်ငံ nl: Marokko "no": Marokko nv: Moroko oc: Marròc or: ମୋରୋକୋ os: Марокко pa: ਮੋਰਾਕੋ pl: Maroko ps: مراکش pt: Marrocos qu: Maruku ro: Maroc ru: Марокко rw: Maroke sa: मोराको sc: Marocco se: Marokko sg: Marôko sh: Maroko sk: Maroko sl: Maroko sn: Morocco so: Marooko sq: Maroku sr: Мароко ss: IMorokho su: Maroko sv: Marocko sw: Moroko ta: மொரோக்கோ te: మొరాకో tg: Марокаш th: ประเทศโมร็อกโก ti: ሞሮኮ tk: Marokko tl: Maruekos tr: Fas ts: Morocco tt: Марокко ug: ماراكەش uk: Марокко ur: مراکش uz: Marokash vi: Maroc vo: Marokän wa: Marok wo: Marok yi: מאראקא yo: Mòrókò zh: 摩洛哥 zu: IMorokho ================================================ FILE: settings/country-names/mc.yaml ================================================ name: default: Monaco af: Monaco ak: Mɔnako am: ሞናኮ an: Múnegu ar: موناكو az: Monako ba: Монако be: Манака bg: Монако bi: Monaco bm: Monako bn: মোনাকো bo: མོ་ན་ཁོ། br: Monako bs: Monako ca: Mònaco ce: Монако cs: Monako cu: Монако cv: Монако cy: Monaco da: Monaco de: Monaco dv: މޮނާކޯ dz: མོ་ན་ཀོ ee: Monaco el: Μονακό en: Monaco eo: Monako es: Mónaco et: Monaco eu: Monako fa: موناکو ff: Monaakoo fi: Monaco fo: Monako fr: Monaco fy: Monako ga: Monacó gd: Monaco gl: Mónaco gu: મોનૅકો gv: Monaco ha: Monako he: מונקו hi: मोनैको hr: Monako ht: Monako hu: Monaco hy: Մոնակո ia: Monaco id: Monako ie: Mónaco io: Monako is: Mónakó it: Monaco ja: モナコ jv: Monako ka: მონაკო kg: Monako ki: Monako kk: Монако kl: Monaco km: ម៉ូណាកូ kn: ಮೊನಾಕೊ ko: 모나코 ks: موناکو ku: Monako kv: Монако kw: Monako la: Monoecus lb: Monaco lg: Monako li: Monaco ln: Monaco lo: ໂມນາໂກ lt: Monakas lv: Monako mg: Mônakô mi: Manako mk: Монако ml: മൊണാക്കോ mn: Монако mr: मोनॅको ms: Monaco mt: Monako my: နိုင်ငံ na: Monako ne: मोनाको nl: Monaco "no": Monaco oc: Mónegue or: ମୋନାକୋ os: Монако pa: ਮੋਨਾਕੋ pl: Monako pt: Mónaco qu: Munaku rm: Monaco rn: Monako ro: Monaco ru: Монако rw: Monako sa: मोनाको se: Monaco sg: Monaköo sh: Monako si: මොනාකෝව sk: Monako sl: Monako so: Monako sq: Monako sr: Монако ss: IMonakho su: Monako sv: Monaco sw: Monako ta: மொனாக்கோ te: మొనాకో tg: Монако th: ประเทศโมนาโก ti: ሞናኮ tk: Monako tl: Monako to: Manako tr: Monako tt: Монако tw: Monako ug: موناكو uk: Монако ur: موناکو uz: Monako vi: Monaco vo: Monakän wo: Monaako yi: מאנאקא yo: Mónakò zh: 摩纳哥 zu: i-Monaco ================================================ FILE: settings/country-names/md.yaml ================================================ name: default: Moldova ab: Молдова af: Moldowa am: ሞልዶቫ an: Moldavia ar: مولدوفا az: Moldova ba: Молдова be: Малдова bg: Молдова bi: Moldova bn: মলদোভা bo: མོལ་དོ་ཝ། br: Moldova bs: Moldavija ca: Moldàvia ce: Молдави co: Moldavia cs: Moldavsko cu: Молдова cv: Молдави cy: Moldofa da: Moldova de: Moldawien dv: މޮލްޑޯވާ ee: Moldova el: Μολδαβία en: Moldova eo: Moldavio es: Moldavia et: Moldova eu: Moldavia fa: مولداوی fi: Moldova fo: Moldova fr: Moldavie fy: Moldaavje ga: An Mholdóiv gd: Moldàibhia gl: Moldavia gu: મોલ્દોવા gv: Moldova he: מולדובה hi: मॉल्डोवा hr: Moldova ht: Moldavi hu: Moldova hy: Մոլդովա ia: Moldova id: Moldova ie: Moldavia io: Moldova is: Moldóva it: Moldavia ja: モルドバ jv: Moldova ka: მოლდოვა kg: Moldova kk: Молдова kl: Moldova kn: ಮಾಲ್ಡೋವ ko: 몰도바 ku: Moldova kv: Молдова kw: Moldova ky: Молдова la: Moldavia lb: Moldawien li: Moldavië lt: Moldavija lv: Moldova mi: Te Whenua o Morotawa mk: Молдавија ml: മൊൾഡോവ mn: Молдав mo: Мoldova mr: मोल्दोव्हा ms: Moldova mt: Moldova my: မော်လ်ဒိုဗာနိုင်ငံ na: Mordowa ne: मोल्दोवा nl: Moldavië "no": Moldova oc: Moldàvia or: ମାଲଡୋଭା os: Молдави pa: ਮੋਲਦੋਵਾ pl: Mołdawia pt: Moldávia qu: Mulduwa ro: Moldova ru: Молдавия rw: Molidova sa: मोल्दोवा se: Moldova sh: Moldavija sk: Moldavsko sl: Moldavija so: Moldofa sq: Moldavia sr: Молдавија ss: IMolidiva st: Moldova su: Moldova sv: Moldavien sw: Moldova ta: மல்தோவா te: మోల్డోవా tg: Молдова th: ประเทศมอลโดวา tk: Moldawiýa tl: Moldavia tr: Moldova tt: Молдавия ug: مولدوۋا uk: Молдова ur: مالدووا uz: Moldova vi: Moldova vo: Moldovän wo: Moldaawi yi: מאלדאווע yo: Móldófà zh: 摩尔多瓦 ================================================ FILE: settings/country-names/me.yaml ================================================ name: default: Crna Gora / Црна Гора af: Montenegro am: ሞንቴኔግሮ an: Montenegro ar: الجبل الأسود av: Чеэраб Меэр az: Monteneqro ba: Черногория be: Чарнагорыя bg: Черна гора bi: Montenegro bn: মন্টিনিগ্রো bo: མོན་ཊེནིག་རོ། br: Montenegro bs: Crna Gora ca: Montenegro ce: Iаьржалаьмни cs: Černá Hora cu: Чрьна Гора cv: Черногори cy: Montenegro da: Montenegro de: Montenegro dv: މޮންޓެނީގުރޯ ee: Montenegro el: Μαυροβούνιο en: Montenegro eo: Montenegro es: Montenegro et: Montenegro eu: Montenegro fa: مونته‌نگرو fi: Montenegro fo: Montenegro fr: Monténégro fy: Montenegro ga: Montainéagró gd: Am Monadh Neagrach gl: Montenegro gu: મોન્ટેનીગ્રો gv: Montenegro he: מונטנגרו hr: Crna Gora ht: Montenegwo hu: Montenegró hy: Չեռնոգորիա ia: Montenegro id: Montenegro ie: Montenegro io: Montenegro is: Svartfjallaland it: Montenegro ja: モンテネグロ jv: Montenégro ka: ჩერნოგორია kg: Monte Negro kk: Черногория kl: Montenegro kn: ಮಾಂಟೆನೆಗ್ರೊ ko: 몬테네그로 ku: Montenegro kv: Черногория kw: Montenegro ky: Монтенегро la: Mons Niger lb: Montenegro li: Montenegro ln: Montenegro lt: Juodkalnija lv: Melnkalne mi: Monotenīkoro mk: Црна Гора ml: മോണ്ടെനെഗ്രൊ mn: Монтенегро mr: माँटेनिग्रो ms: Montenegro mt: Montenegro my: မွန်တီနီဂရိုးနိုင်ငံ na: Montenegro ne: मोन्टेनेग्रो nl: Montenegro "no": Montenegro nv: Dziłizhin Bikéyah oc: Montenegro or: ମୋଣ୍ଟେନେଗ୍ରୋ os: Черногори pa: ਮੋਂਟੇਨੇਗਰੋ pl: Czarnogóra ps: مانتېنېګرو pt: Montenegro qu: Yanaurqu ro: Muntenegru ru: Черногория rw: Montenegoro sc: Montenegro se: Montenegro sh: Crna Gora sk: Čierna Hora sl: Črna gora sm: Montenegro so: Montenegro sq: Mali i Zi sr: Црна Гора ss: IMonthenekho su: Monténégro sv: Montenegro sw: Montenegro ta: மொண்டெனேகுரோ tg: Монтенегро th: ประเทศมอนเตเนโกร tk: Çernogoriýa tl: Montenegro tr: Karadağ tt: Монтенегро ug: چېرنوگورىيە uk: Чорногорія ur: مونٹینیگرو uz: Chernogoriya vi: Montenegro vo: Montenegrän wo: Montenegro yi: מאנטענעגרא yo: Montenẹ́grò zh: 蒙特內哥羅 ================================================ FILE: settings/country-names/mg.yaml ================================================ name: default: Madagasikara af: Madagaskar am: ማዳጋስካር an: Madagascar ar: مدغشقر az: Madaqaskar ba: Мадагаскар be: Мадагаскар bg: Мадагаскар bm: Madagaskar bn: মাদাগাস্কার bo: མ་ད་གཱ་སི་ཀར། br: Madagaskar bs: Madagaskar ca: Madagascar ce: Мадагаскар cs: Madagaskar cv: Мадагаскар cy: Madagascar da: Madagaskar de: Madagaskar dv: މަޑަގަސްކަރަ ee: Madagascar el: Μαδαγασκάρη en: Madagascar eo: Madagaskaro es: Madagascar et: Madagaskar eu: Madagaskar fa: ماداگاسکار fi: Madagaskar fj: Madagasikar fo: Madagaskar fr: Madagascar fy: Madagaskar ga: Madagascar gd: Madagascar gl: Madagascar gn: Madagaka gu: મડાગાસ્કર gv: Madagascar he: מדגסקר hi: मेडागास्कर hr: Madagaskar ht: Madagaskar hu: Madagaszkár hy: Մադագասկար ia: Madagascar id: Madagaskar ie: Madagascar io: Madagaskar is: Madagaskar it: Madagascar ja: マダガスカル jv: Madagaskar ka: მადაგასკარი kg: Malagasi ki: Madagascar kk: Мадагаскар kl: Madagascar kn: ಮಡಾಗಾಸಿಕರ ko: 마다가스카르 ku: Madagaskar kw: Madagaskar ky: Мадагаскар la: Madagascaria lb: Madagaskar li: Madagaskar ln: Madagasikari lt: Madagaskaras lv: Madagaskara mg: Madagasikara mi: Marakāhia mk: Мадагаскар ml: മഡഗാസ്കർ mn: Мадагаскар mr: मादागास्कर ms: Madagaskar mt: Madagaskar my: မဒါဂတ်စကားနိုင်ငံ nl: Madagaskar "no": Madagaskar nv: Madaʼgéésgáá oc: Madagascar om: Madagascar or: ମେଡାଗାସ୍କର os: Мадагаскар pa: ਮਾਦਾਗਾਸਕਰ pl: Madagaskar ps: مادغاسکر pt: Madagáscar qu: Madagaskar ro: Madagascar ru: Мадагаскар rw: Madagasikari sa: मडगास्कर sc: Madagascàr se: Madagaskar sg: Madagaskära sh: Madagaskar si: මැඩගස්කරය sk: Madagaskar sl: Madagaskar sm: Madagascar sn: Madagascar so: Madagaskar sq: Madagaskari sr: Мадагаскар ss: IMadagasikha su: Madagaskar sv: Madagaskar sw: Madagaska ta: மடகாசுகர் te: మడగాస్కర్ tg: Мадагаскар th: ประเทศมาดากัสการ์ ti: ማዳጋስካር tk: Madagaskar tl: Madagaskar tr: Madagaskar ts: Madagascar tt: Мадагаскар ty: Madagascar ug: ماداگاسكار uk: Мадагаскар ur: مڈغاسکر uz: Madagaskar vi: Madagascar vo: Malagaseän wo: Madagaskaar yi: מאדאגאסקאר yo: Madagáskàr zh: 马达加斯加 zu: IMadagasika ================================================ FILE: settings/country-names/mh.yaml ================================================ name: default: Ṃajeḷ af: Marshalleilande ar: جزر مارشال be: Маршалавы астравы br: Inizi Marshall ca: Illes Marshall cy: Ynysoedd Marshall da: Marshalløerne de: Marshallinseln en: Marshall Islands eo: Marŝaloj es: Islas Marshall et: Marshalli Saared fa: جزایر مارشال fi: Marshallinsaaret fr: Îles Marshall fy: de Marshalleilannen ga: Oileáin Marshall gd: Na h-Eileanan Mharshall he: איי מרשל hr: Maršalovi Otoci hu: Marshall-szigetek ia: Insulas Marshall id: Kepulauan Marshall io: Insuli Marshall is: Marshalleyjar it: Isole Marshall la: Insulae Marsalienses lb: Marshallinselen li: Marshall-eilen lt: Maršalo salos lv: Māršala salas mk: Маршалски Острови mn: Маршаллын арлууд nb: Marshalløyene nl: Marshalleilanden nn: Marshalløyane "no": Marshalløyene oc: Illas Marshall pl: Wyspy Marshalla pt: Ilhas Marshall ru: Маршалловы Острова se: Marshallsullot sk: Mashallove ostrovy sl: Marshallovi otoki sv: Marshallöarna ta: மார்சல் தீவுகள் th: สาธารณรัฐหมู่เกาะมาร์แชลล์ tr: Marshall Adaları uk: Маршаллові Острови vi: Quần đảo Marshall vo: Marjaluäns zh: 马绍尔群岛 ================================================ FILE: settings/country-names/mk.yaml ================================================ name: default: Северна Македонија af: Masedonië ak: Masedonia am: ማሴዶንያ an: Republica de Macedonia ar: شمال مقدونيا az: Masedoniya ba: Македония Республикаһы be: Македонія bg: Северна Македония bi: Macedonia bm: Macedɔni bn: মেসিডোনিয়া[FYROM] bo: མ་སེ་ཌོ་ནིཡ། (རྒྱལ་ཁབ།) br: Makedonia bs: Sjeverna Makedonija ca: Macedònia del Nord ce: Македони cs: Severní Makedonie cu: Макєдонїꙗ cv: Македони Республики cy: Gogledd Macedonia da: Nordmakedonien de: Nordmazedonien dv: މެސެޑޯނިއާ dz: མ་སེ་ཌོ་ནི་ཡ་ [ཡུ་གོ་སླཱ་བི་ཡ] ee: Makedonia el: Βόρεια Μακεδονία en: North Macedonia eo: Nord-Makedonio es: Macedonia del Norte et: Põhja-Makedoonia eu: Ipar Mazedonia fa: مقدونیه شمالی ff: Meceduwaan fi: Makedonia fo: Makedónia fr: Macédoine du Nord fy: Noard-Masedoanje ga: An Mhacadóin gd: Masadoinia a Tuath gl: Macedonia do Norte gn: Masendoña gu: મેસેડોનિયા gv: Massadoan Hwoaie ha: Masedoniya he: מקדוניה הצפונית hi: उत्तर मेसीडोनिया hr: Sjeverna Makedonija ht: Repiblik d Masedoni hu: Észak-Macedónia hy: Մակեդոնիայի Հանրապետություն ia: Macedonia id: Republik Makedonia ie: Macedonia io: Republiko Macedonia is: Makedónía it: Macedonia del Nord ja: 北マケドニア jv: Républik Makedonia ka: მაკედონია kg: Makedonia ki: Masedonia kk: Македония Республикасы kl: Makedonia km: ម៉ាសេដ្វាន kn: ಮ್ಯಾಸಿಡೋನಿಯ ko: 북마케도니아 ks: مٮ۪سوڑونِیا ku: Komara Makedonyayê kv: Македония Республика kw: Repoblek Makedoni la: Macedonia Septentrionalis lb: Nordmazedonien lg: Masedoniya li: Noord-Macedonië ln: Masedoni lo: ມາຊີໂດເນຍ lt: Šiaurės Makedonija lv: Ziemeļmaķedonija mg: Makedonia mi: Makerōnia mk: Северна Македонија ml: റിപ്പബ്ലിക് ഓഫ് മാസിഡോണിയ mn: Бүгд Найрамдах Македон Улс mr: मॅसिडोनिया ms: Republik Macedonia mt: Repubblika tal-Maċedonja my: မက်စီဒိုးနီးယားနိုင်ငံ na: Matedoniya ne: म्यासेडोनिया nl: Noord-Macedonië "no": Nord-Makedonia oc: Republica de Macedònia or: ମାସିଡୋନିଆ os: Республикæ Македони pa: ਮਕਦੂਨੀਆ ਗਣਰਾਜ pl: Macedonia Północna ps: د مقدونيې ولسمشريزه pt: Macedónia do Norte qu: Makidunya rm: Macedonia rn: Masedoniya ro: Macedonia de Nord ru: Северная Македония rw: Masedoniya sa: मेसेडोनिया sc: Matzedònia se: Davvi-Makedonia sg: Maseduäni sh: Severna Makedonija si: මැසිඩෝනියාන [FYROM] sk: Severné Macedónsko sl: Makedonija sn: Macedonia so: Makedonia sq: Maqedonia e Veriut sr: Северна Македонија ss: IMakhedoniya su: Makédonia sv: Nordmakedonien sw: Jamhuri ya Masedonia ta: மாக்கடோனியக் குடியரசு te: మేసిడోనియా tg: Ҷумҳурии Мақдуния th: ประเทศมาซิโดเนีย ti: ማከዶኒያ tk: Makedoniýa Respublikasy tl: Republika ng Masedonya to: Masitōnia [FYROM] tr: Kuzey Makedonya tt: Македония Җөмһүрияте ug: ماكېدونىيە uk: Північна Македонія ur: شمالی مقدونیہ uz: Makedoniya Respublikasi vi: Bắc Macedonia vo: Nolüda-Makedoniyän wo: Réewum Maseduwaan yi: רעפובליק פון מאקעדאניע yo: Orílẹ̀-èdè Olómìnira ilẹ̀ Makẹdóníà zh: 北马其顿共和国 zu: isi-Macedonia [FYROM] ================================================ FILE: settings/country-names/ml.yaml ================================================ name: default: Mali af: Mali am: ማሊ an: Mali ar: مالي az: Mali ba: Мали be: Малі bg: Мали bm: Mali bn: মালি bo: མ་ལི། br: Mali bs: Mali ca: Mali ce: Мали cs: Mali cv: Мали cy: Mali da: Mali de: Mali dv: މާލީ ee: Mali el: Μάλι en: Mali eo: Malio es: Malí et: Mali eu: Mali fa: مالی ff: Maali fi: Mali fo: Mali fr: Mali fy: Maly ga: Mailí gd: Màili gl: Malí gv: Malee ha: Mali he: מאלי hi: माली hr: Mali ht: Mali hu: Mali hy: Մալի ia: Mali id: Mali ie: Mali io: Mali is: Malí it: Mali ja: マリ共和国 jv: Mali ka: მალი kg: Mali kk: Мали kn: ಮಾಲಿ ko: 말리 ku: Malî kw: Mali la: Malium lb: Mali li: Mali ln: Mali lt: Malis lv: Mali mi: Māri mk: Мали ml: മാലി mn: Мали mr: माली ms: Mali mt: Mali my: မာလီနိုင်ငံ nl: Mali "no": Mali oc: Mali or: ମାଲି os: Мали pa: ਮਾਲੀ pl: Mali ps: مالي pt: Mali qu: Mali ro: Mali ru: Мали rw: Mali sa: माली sc: Mali se: Mali sg: Malïi sh: Mali si: මාලි sk: Mali sl: Mali sn: Mali so: Maali sq: Mali sr: Мали ss: IMali st: Mali su: Mali sv: Mali sw: Mali ta: மாலி tg: Малӣ th: ประเทศมาลี ti: ማሊ tk: Mali tl: Mali tr: Mali ts: Mali tt: Мали ug: مالى uk: Малі ur: مالی uz: Mali vi: Mali vo: Maliyän wa: Mali wo: Mali yi: מאלי yo: Málì zh: 马里共和国 zu: IMali ================================================ FILE: settings/country-names/mm.yaml ================================================ name: default: မြန်မာ af: Mianmar am: ምየንማ an: Myanmar ar: بورما az: Myanma ba: Мьянма be: М'янма bg: Мианмар bi: Burma bn: মায়ানমার bo: བྷར་མ། br: Myanmar bs: Mijanmar ca: Myanmar ce: Мьянма cs: Myanmar cv: Мьянма cy: Myanmar da: Myanmar de: Myanmar dv: ބަރުމާ dz: མེ་མར་ el: Μιανμάρ en: Myanmar eo: Birmo / Mjanmao es: Myanmar et: Birma eu: Myanmar fa: میانمار fi: Myanmar fo: Burma fr: Birmanie fy: Birma ga: Maenmar gd: Mianmar gl: Myanmar gu: બર્મા gv: Myanmar he: מיאנמר hi: म्यान्मार hr: Mijanmar ht: Bimani hu: Mianmar hy: Մյանմա ia: Birmania id: Myanmar ie: Myanmar io: Myanmar is: Búrma it: Birmania ja: ミャンマー jv: Myanmar ka: მიანმარი ki: Myanmar kk: Мианма km: មីយ៉ាន់ម៉ា kn: ಮಯನ್ಮಾರ್ ko: 미얀마 ku: Myanmar kv: Мьянма kw: Byrmani ky: Мьянма la: Birmania lb: Myanmar li: Myanmar lo: ປະເທດມຽນມາ lt: Mianmaras lv: Mjanma mg: Myanmara mi: Pēma mk: Мјанмар ml: മ്യാന്മാർ mn: Мьянмар mr: म्यानमार ms: Myanmar mt: Myanmar my: မြန်မာ na: Miyanmar ne: म्यानमार nl: Myanmar "no": Myanmar oc: Birmania or: ବର୍ମା os: Мьянмæ pa: ਮਿਆਂਮਾਰ pl: Mjanma ps: ميانمار pt: Birmânia qu: Myanmar ro: Birmania ru: Мьянма rw: Mayanimari sa: म्यान्मार sc: Birmania se: Myanmar sh: Mianmar si: බුරුමය sk: Mjanmarsko sl: Mjanmar so: Myanmar sq: Birmania sr: Мјанмар ss: IMayanima su: Myanmar sv: Myanmar sw: Myanmar ta: மியான்மர் te: మయన్మార్ tg: Мянма th: ประเทศพม่า tk: Mýanma tl: Myanmar tr: Myanmar tt: Мьянма ug: بىرما uk: М'янма ur: میانمار uz: Birma vi: Myanma vo: Mianmarän wo: Miyanmaar yi: מיאנמאר yo: Myanmar za: Mienjdien zh: 缅甸 ================================================ FILE: settings/country-names/mn.yaml ================================================ name: default: Монгол улс ᠮᠤᠩᠭᠤᠯ ᠤᠯᠤᠰ af: Mongolië am: ሞንጎልያ an: Mongolia ar: منغوليا as: মঙ্গোলিয়া az: Monqolustan ba: Монголия be: Манголія bg: Монголия bn: মঙ্গোলিয়া bo: མོང་གོལ། br: Mongolia bs: Mongolija ca: Mongòlia ce: Монголи cs: Mongolsko cu: Мѫголи cv: Монголи cy: Mongolia da: Mongoliet de: Mongolei dv: މޮންގޯލިއާ dz: སོག་པོ་ ee: Mongolia el: Μογγολία en: Mongolia eo: Mongolio es: Mongolia et: Mongoolia eu: Mongolia fa: مغولستان fi: Mongolia fo: Mongolia fr: Mongolie fy: Mongoalje ga: An Mhongóil gd: Mongòilia gl: Mongolia gn: Mongolia gu: મંગોલિયા gv: Yn Vongoil he: מונגוליה hi: मंगोलिया hr: Mongolija ht: Mongoli hu: Mongólia hy: Մոնղոլիա ia: Mongolia id: Mongolia ie: Mongolia io: Mongolia is: Mongólía it: Mongolia ja: モンゴル国 jv: Mongolia ka: მონღოლეთი kk: Моңғолия kl: Mongolia km: ម៉ុងហ្គោលី kn: ಮಂಗೋಲಿಯ ko: 몽골 ku: Mongolya kv: Монголия kw: Mongoli ky: Моңголстан la: Mongolia lb: Mongolei li: Mongolië ln: Mongolí lt: Mongolija lv: Mongolija mi: Mongōria mk: Монголија ml: മംഗോളിയ mn: Монгол улс ᠮᠤᠩᠭᠤᠯ ᠤᠯᠤᠰ mo: Монголия mr: मंगोलिया ms: Mongolia my: မွန်ဂိုးလီးယားနိုင်ငံ na: Mongoriya ne: मङ्गोलिया nl: Mongolië "no": Mongolia nv: Chʼah Diʼilii Bikéyah oc: Mongolia or: ମଙ୍ଗୋଲିଆ os: Мангол pa: ਮੰਗੋਲੀਆ pl: Mongolia ps: مغولستان pt: Mongólia qu: Mungul suyu ro: Mongolia ru: Монголия rw: Mongoliya sa: मंगोलिया se: Mongolia sh: Mongolija si: මොංගෝලියාව sk: Mongolsko sl: Mongolija sm: Mogitolia so: Mongolia sq: Mongolia sr: Монголија ss: IMongoliya su: Mongolia sv: Mongoliet sw: Mongolia ta: மங்கோலியா te: మంగోలియా tg: Муғулистон th: ประเทศมองโกเลีย tk: Mongoliýa tl: Monggolya tr: Moğolistan tt: Монголия ug: موڭغۇلىيە uk: Монголія ur: منگولیا uz: Moʻgʻuliston vi: Mông Cổ vo: Mongolän wo: Mongoli yi: מאנגאליי yo: Mòngólíà za: Mungzguj zh: 蒙古国 ================================================ FILE: settings/country-names/mr.yaml ================================================ name: default: موريتانيا af: Mauritanië am: ሞሪታኒያ an: Mauritania ar: موريتانيا az: Mavritaniya be: Маўрытанія bg: Мавритания bm: Moritani bn: মৌরিতানিয়া bo: མའུ་རི་ཏ་ནི་ཡ། br: Maouritania bs: Mauritanija ca: Mauritània ce: Мавритани cs: Mauritánie cv: Мавритани cy: Mauritania da: Mauretanien de: Mauretanien dv: މޮރިޓާނިއާ ee: Mauritania el: Μαυριτανία en: Mauritania eo: Maŭritanio es: Mauritania et: Mauritaania eu: Mauritania fa: موریتانی ff: Muritani fi: Mauritania fo: Móritania fr: Mauritanie fy: Mauritaanje ga: An Mháratáin gd: Moratainia gl: Mauritania gv: Yn Varitaan ha: Muritaniya he: מאוריטניה hi: मॉरीतानिया hr: Mauretanija ht: Moritani hu: Mauritánia hy: Մավրիտանիա ia: Mauritania id: Mauritania ie: Mauritania io: Mauritania is: Máritanía it: Mauritania ja: モーリタニア jv: Mauritania ka: მავრიტანია kg: Muritania kk: Мавритания kn: ಮಾರಿಟಾನಿಯ ko: 모리타니 ku: Morîtanya kw: Moritani la: Mauritania lb: Mauretanien li: Mauritanië ln: Moritani lt: Mauritanija lv: Mauritānija mg: Maoritania mi: Mauritānia mk: Мавританија ml: മൗറിത്താനിയ mn: Мавритан mr: मॉरिटानिया ms: Mauritania mt: Mawritanja my: မော်ရီတေးနီးယားနိုင်ငံ nl: Mauritanië "no": Mauritania nv: Moowitéínya oc: Mauritània or: ମୋରିତାନିଆ os: Мавритани pa: ਮੌਰੀਤਾਨੀਆ pl: Mauretania ps: موریتاني pt: Mauritânia qu: Muritanya ro: Mauritania ru: Мавритания rw: Moritaniya sa: मारिटेनिया sc: Mauritània se: Mauritánia sg: Moritanïi sk: Mauritánia sl: Mavretanija sn: Mauritania so: Mauritania sq: Mauritania sr: Мауританија ss: IMolithaniya st: Mauritania su: Mauritania sv: Mauretanien sw: Mauritania ta: மூரித்தானியா te: మౌరిటానియ tg: Мавритания th: ประเทศมอริเตเนีย ti: ሞሪታኒያ tk: Mawritaniýa tl: Mauritania tr: Moritanya ts: Maritana tt: Мавритания ug: ماۋرىتانىيە uk: Мавританія ur: موریتانیہ uz: Mavritaniya vi: Mauritanie vo: Moritän wa: Moritanreye wo: Gànnaar xh: Mauritania yi: מאריטאניע yo: Mauritáníà zh: 毛里塔尼亚 zu: IMoritaniya ================================================ FILE: settings/country-names/ms.yaml ================================================ name: default: Montserrat af: Montserrat an: Isla de Montserrat ar: مونتسرات az: Montserrat ba: Монтсеррат be: Мантсерат bg: Монсерат br: Enez Montserrat bs: Monserat ca: Illa de Montserrat cs: Montserrat cy: Montserrat da: Montserrat de: Montserrat dv: މޮންސެރާޓު el: Μοντσερράτ en: Montserrat eo: Moncerato es: Montserrat et: Montserrat eu: Montserrat fa: مونتسرات fi: Montserrat fr: Montserrat ga: Montsarat gl: Illa Montserrat he: מונטסראט hi: मॉण्टसेराट hr: Montserrat ht: Montserrat hu: Montserrat hy: Մոնտսերատ id: Montserrat io: Montserrat is: Montserrat it: Montserrat ja: モントセラト jv: Montserrat ka: მონსერატი ko: 몬트세랫 kw: Montserrat la: Montserrat lb: Montserrat li: Montserrat lt: Montseratas lv: Montserrata mk: Монтсерат mn: Монтсеррат mr: माँटसेराट ms: Montserrat nl: Montserrat "no": Montserrat oc: Montserrat pa: ਮਾਂਟਸਰਾਤ pl: Montserrat pt: Montserrat ro: Montserrat ru: Монтсеррат rw: Monserati sh: Montserrat sk: Montserrat sl: Montserrat sq: Montserrati sr: Монтсерат su: Montserrat sv: Montserrat ta: மொன்செராட் th: มอนต์เซอร์รัต tl: Montserrat tr: Montserrat ug: مونتسەررات uk: Монтсеррат ur: مانٹسریٹ vi: Montserrat wo: Montserrat yo: Montserrat zh: 蒙塞拉特島 ================================================ FILE: settings/country-names/mt.yaml ================================================ name: default: Malta ak: Mɔlta am: ማልታ ar: مالطا be: Мальта bg: Малта bm: Malti bn: মাল্টা bo: མལ་ཊ། br: Malta ca: Malta cs: Malta de: Malta dz: མཱལ་ཊ ee: Malta nutome el: Μάλτα en: Malta eo: Malto es: Malta fa: مالت ff: Malte fi: Malta fr: Malte ga: Málta gu: માલ્ટા gv: Yn Valta he: מלטה hi: माल्टा hr: Malta hu: Málta hy: Մալթա ia: Malta io: Malta is: Malta it: Malta ja: マルタ ka: მალტა km: ម៉ាល់តា kn: ಮಾಲ್ಟಾ ko: 몰타 ks: مالٹا ku: Malta la: Melita lg: Malita ln: Malitɛ lo: ມັນຕາ lv: Malta mk: Малта ml: മാൾട്ട mn: Мальта mr: माल्टा ne: माल्टा "no": Malta or: ମାଲ୍ଟା pl: Malta rn: Malita ru: Мальта se: Malta sg: Mâlta si: මෝල්ටාව sk: Malta so: Maalda sq: Maltë sr: Малта sv: Malta ta: மால்டா te: మాల్టా tg: Малта th: ประเทศมอลตา ti: ማልታ to: Malita uk: Мальта ur: مالٹا vi: Malta vo: Malteän yo: Orílẹ́ède Malata zh: 马尔他 zu: i-Malta ================================================ FILE: settings/country-names/mu.yaml ================================================ name: default: Mauritius af: Mauritius am: ሞሪሸስ an: Mauricio ar: موريشيوس az: Mavriki be: Маўрыкій bg: Мавриций bn: মরিশাস bo: མའུ་རི་ཊིའུ་སུ། br: Maoris bs: Mauricijus ca: Maurici cs: Mauricius cv: Маврики da: Mauritius de: Mauritius dv: މޮރިޝަސް el: Μαυρίκιος en: Mauritius eo: Maŭricio es: Mauricio eu: Maurizio fa: موریس fi: Mauritius fo: Móritius fr: Maurice fy: Mauritsius ga: Oileán Mhuirís gd: Na h-Eileanan Mhoiriseas gl: Mauricio - Maurice gv: Ellan Wirrish he: מאוריציוס hi: मॉरिशस hr: Mauricijus ht: Moris hu: Mauritius hy: Մավրիկիոս ia: Mauritio io: Maurico is: Máritíus ja: モーリシャス ka: მავრიკი kk: Маврикий kn: ಮಾರಿಷಸ್ ko: 모리셔스 ku: Maurîtius kw: Ynys Morrys la: Mauritia ln: Morisi lt: Mauricijus lv: Maurīcija mg: Maorisy mk: Маврициус ml: മൗറീഷ്യസ് mn: Маврикий mr: मॉरिशस mt: Mawrizji my: မောရစ်ရှနိုင်ငံ ne: मौरिसस nl: Mauritius "no": Mauritius oc: Maurici or: ମରିସସ os: Маврикий pa: ਮਾਰੀਸ਼ਸ pl: Mauritius ps: ماوریتوس pt: Maurícia qu: Mawrisyu ru: Маврикий rw: Morise sa: मारिषस् sc: Mauritzius se: Mauritius sg: Mörîsi si: මුරුසි සමුහාණ්ඩුව sk: Maurícius sl: Mavricij sq: Mauritiusi sr: Маурицијус ss: IMorishiyasi sv: Mauritius sw: Morisi ta: மொரிசியசு te: మారిషస్ tg: Маврикия th: ประเทศมอริเชียส tk: Mawrikiý tl: Maurisyo tt: Маврикий ug: ماۋرىتىئۇس uk: Маврикій ur: موریشس uz: Mavritsiya vo: Moriseän wo: Móoris yo: Mọ́rísì zh: 毛里求斯 zu: IMorishisi ================================================ FILE: settings/country-names/mv.yaml ================================================ name: default: ދިވެހިރާއްޖެ af: Maldive am: ማልዲቭስ ar: ملديف az: Maldiv be: Мальдывы bg: Малдиви bm: Maldivi bn: মালদ্বীপ bo: མལ་དྭིབ། br: Maldivez bs: Maldivi ca: Maldives cs: Maledivy da: Maldiverne de: Malediven dv: ދިވެހިރާއްޖެ dz: མཱལ་དིབས ee: maldivesdukɔ el: Μαλδίβες en: Maldives eo: Maldivoj es: Maldivas et: Maldiivid eu: Maldivak fa: مالدیو ff: Maldiiwe fi: Malediivit fo: Maldivuoyggjarnar fr: Maldives fy: de Maldiven ga: Oileáin Mhaildíve gd: Na h-Eileanan Mhaladaibh gl: Maldivas gu: માલદિવ્સ gv: Ny Maldeevaghyn ha: Maldibi he: מלדיבים hi: मालदीव hr: Maldivi hu: Maldív-szigetek hy: Մալդիվներ id: Maladewa ig: Maldivesa io: Maldivi is: Maldíveyjar it: Maldive ka: მალდივის კუნძულები ki: Modivu km: ម៉ាល់ឌីវ kn: ಮಾಲ್ಡಿವ್ಸ್ ks: مالدیٖو ku: Maldîv la: Insulae Maldivae lb: Malediven lg: Bizinga by'eMalidive ln: Madívɛ lo: ມັນດິຟ lt: Maldyvai lv: Maldīvija mg: Maldiva mk: Малдиви ml: മാലിദ്വീപ് mn: Мальдив mr: मालदीव्ज ms: Maldiv my: မော်လဒိုက် nb: Maldivene ne: माल्दिभ्स nl: Malediven nn: Maldivane "no": Maldivene or: ମାଳଦ୍ବୀପ pl: Malediwy pt: Maldivas rm: Maldivas rn: Moludave ro: Maldive ru: Мальдивы se: Malediivvat sg: Maldîva si: මාල දිවයින sk: Maledivy sl: Maldivi so: Maaldiqeen sq: Maldivit sr: Малдиви sv: Maldiverna sw: Modivu ta: மாலத்தீவு te: మాల్దీవులు th: ประเทศมัลดีฟส์ ti: ማልዲቭስ to: Malativisi tr: Maldivler tt: Мальдивлар uk: Мальдіви ur: مالدیو vo: Maldivuäns yo: Orílẹ́ède Maladifi zh: 马尔代夫/馬爾地夫 zu: i-Maldives ================================================ FILE: settings/country-names/mw.yaml ================================================ name: default: Malawi af: Malawi am: ማላዊ an: Malawi ar: مالاوي az: Malavi ba: Малави be: Малаві bg: Малави bm: Malawi bn: মালাউই bo: མ་ལ་ཝི། br: Malawi bs: Malavi ca: Malawi ce: Малави cs: Malawi cv: Малави cy: Malawi da: Malawi de: Malawi dv: މަލާވީ ee: Malawi el: Μαλάουι en: Malawi eo: Malavio es: Malaui et: Malawi eu: Malawi fa: مالاوی fi: Malawi fo: Malavi fr: Malawi fy: Malawy ga: An Mhaláiv gd: Malabhaidh gl: Malaui - Malawi gv: Malawi he: מלאווי hi: मलावी hr: Malavi ht: Malawi hu: Malawi hy: Մալավի ia: Malawi id: Malawi ie: Malawi io: Malawi is: Malaví it: Malawi ja: マラウイ jv: Malawi ka: მალავი kg: Malawi ki: Malawi kk: Малави kn: ಮಲಾವಿ ko: 말라위 ku: Malavî kw: Malawi la: Malavium lb: Malawi li: Malawi ln: Malawi lt: Malavis lv: Malāvija mg: Malawi mi: Marāwi mk: Малави ml: മലാവി mn: Малави mr: मलावी ms: Malawi mt: Malawi my: မာလဝီနိုင်ငံ nl: Malawi "no": Malawi nv: Malááwii ny: Malaŵi oc: Malawi os: Малави pa: ਮਲਾਵੀ pl: Malawi ps: مالاوي pt: Malawi qu: Malawi ro: Malawi ru: Малави rw: Malawi sa: मलावी sc: Malawi se: Malawi sg: Malawïi sh: Malavi sk: Malawi sl: Malavi sn: Malawi so: Malaawi sq: Malavi sr: Малави ss: IMalawi su: Malawi sv: Malawi sw: Malawi ta: மலாவி tg: Малави th: ประเทศมาลาวี ti: ማላዊ tk: Malawi tl: Malawi tr: Malavi ts: Malawi ug: مالاۋى uk: Малаві ur: ملاوی uz: Malavi vi: Malawi vo: Malaviyän wo: Malawi yi: מאלאווי yo: Màláwì zh: 马拉维 zu: IMalawi ================================================ FILE: settings/country-names/mx.yaml ================================================ name: default: México af: Meksiko am: ሜክሲኮ an: Mexico ar: المكسيك ay: Mïxiku az: Meksika ba: Мексика be: Мексіка bg: Мексико bi: Mexico bm: Mɛkisiki bn: মেক্সিকো bo: མེག་སི་ཀོ། br: Mec'hiko bs: Meksiko ca: Mèxic ce: Мексика co: Messicu cs: Mexiko cv: Мексика cy: Mexico da: Mexico de: Mexiko dv: މެކްސިކޯ dz: མེཀ་སི་ཀོ ee: Mexico el: Μεξικό en: Mexico eo: Meksiko es: México et: Mehhiko eu: Mexiko fa: مکزیک fi: Meksiko fo: Meksiko fr: Mexique fy: Meksiko ga: Meicsiceo gd: Meagsago gl: México gn: Méjiko gu: મેક્સિકો gv: Meksico he: מקסיקו hi: मेक्सिको hr: Meksiko ht: Meksik hu: Mexikó hy: Մեքսիկա ia: Mexico id: Meksiko ik: Mexiqo io: Mexikia is: Mexíkó it: Messico iu: ᒦᒃᓰᖂ ja: メキシコ jv: Mèksiko ka: მექსიკა kk: Мексика kl: Mexico km: ម៉ិកស៊ិក kn: ಮೆಕ್ಸಿಕೋ ko: 멕시코 ku: Meksîk kv: Мексика kw: Meksiko ky: Мексика la: Mexicum lb: Mexiko li: Mexico ln: Mexiko lt: Meksika lv: Meksika mg: Meksika mi: Mēhiko mk: Мексико ml: മെക്സിക്കോ mn: Мексик mr: मेक्सिको ms: Mexico mt: Messiku my: မက္ကဆီကိုနိုင်ငံ na: Meketiko ne: मेक्सिको nl: Mexico "no": Mexico nv: Méhigo oc: Mexic or: ମେକ୍ସିକୋ os: Мексикæ pa: ਮੈਕਸੀਕੋ pl: Meksyk pt: México qu: Mishiku rm: Mexico ro: Mexic ru: Мексика rw: Megizike sc: Mèssico se: Meksiko sh: Meksiko sk: Mexiko sl: Mehika sm: Mexico sn: Mexico so: Meksiko sq: Meksika sr: Мексико ss: IMekisikho su: Méksiko sv: Mexico sw: Mexiko ta: மெக்சிக்கோ te: మెక్సికో tg: Мексика th: ประเทศเม็กซิโก tk: Meksika tl: Mehiko tr: Meksika tt: Мексика tw: Mexico ug: مېكسىكا uk: Мексика ur: میکسیکو uz: Meksika vi: México vo: Mäxikän wo: Meksik yi: מעקסיקא yo: Mẹ́ksíkò zh: 墨西哥 zu: IMekisiko ================================================ FILE: settings/country-names/my.yaml ================================================ name: default: Malaysia af: Maleisië am: ማሌዢያ an: Malaisia ar: ماليزيا az: Malayziya ba: Малайзия be: Малайзія bg: Малайзия bn: মালয়েশিয়া bo: མ་ལ་ཤི་ཡ། br: Malaysia bs: Malezija ca: Malàisia ce: Малайзи cs: Malajsie cv: Малайзи cy: Malaysia da: Malaysia de: Malaysia dv: މެލޭޝިޔާ dz: མ་ལེ་ཤི་ཡ་ el: Μαλαισία en: Malaysia eo: Malajzio es: Malasia et: Malaisia eu: Malaysia fa: مالزی fi: Malesia fo: Maleisia fr: Malaisie fy: Maleizje ga: An Mhalaeisia gd: Malaidhsia gl: Malaisia gu: મલેશિયા gv: Yn Valaysia he: מלזיה hi: मलेशिया hr: Malezija ht: Malezi hu: Malajzia hy: Մալազիա ia: Malaysia id: Malaysia ie: Malaysia io: Malaizia is: Malasía it: Malesia ja: マレーシア jv: Malaysia ka: მალაიზია ki: Malaysia kk: Малайзия km: ម៉ាឡេស៊ី kn: ಮಲೇಶಿಯ ko: 말레이시아 ku: Malezya kv: Малайзия kw: Malaysi ky: Малайзия la: Malaesia lb: Malaysia li: Maleisië lo: ປະເທດມາເລເຊຍ lt: Malaizija lv: Malaizija mi: Marēhia mk: Малезија ml: മലേഷ്യ mn: Малайз mr: मलेशिया ms: Maleisië mt: Malażja my: မလေးရှားနိုင်ငံ na: Maraidja ne: मलेशिया nl: Maleisië "no": Malaysia nv: Maléízha oc: Malàisia or: ମାଲେସିଆ os: Малайзи pa: ਮਲੇਸ਼ੀਆ pl: Malezja pt: Malásia qu: Malasya ro: Malaezia ru: Малайзия rw: Malesiya sa: मलयेशिया se: Malesia sg: Malezïi sh: Malezija sk: Malajzia sl: Malezija so: Malaysiya sq: Malajzia sr: Малезија ss: IMaleshiya su: Malaysia sv: Malaysia sw: Malaysia ta: மலேசியா te: మలేషియా tg: Малайзия th: ประเทศมาเลเซีย tk: Malaýziýa tl: Malasya tr: Malezya tt: Малайзия tw: Malaysia ug: مالايسىيا uk: Малайзія ur: ملائیشیا uz: Malayziya vi: Malaysia vo: Malaysiyän wo: Maleesi yi: מאלייזיע yo: Malaysia za: Majlaizsihya zh: 马来西亚 ================================================ FILE: settings/country-names/mz.yaml ================================================ name: default: Moçambique af: Mosambiek am: ሞዛምቢክ an: Mozambique ar: موزمبيق az: Mozambik ba: Мозамбик be: Мазамбік bg: Мозамбик bm: Mozanbik bn: মোজাম্বিক bo: མོ་ཛམ་བིཀ། br: Mozambik bs: Mozambik ca: Moçambic ce: Мозамбик co: Mozambicu cs: Mosambik cv: Мозамбик cy: Mosambic da: Mozambique de: Mosambik dv: މުސިންބީ el: Μοζαμβίκη en: Mozambique eo: Mozambiko es: Mozambique et: Mosambiik eu: Mozambike fa: موزامبیک fi: Mosambik fo: Mosambik fr: Mozambique fy: Mozambyk ga: Poblacht Mhósaimbíc gd: Mòsaimbic gl: Mozambique gn: Mosambike gv: Yn Vosambeeck he: מוזמביק hi: मोज़ाम्बीक hr: Mozambik ht: Mozanbik hu: Mozambik hy: Մոզամբիկ ia: Mozambique id: Mozambik ie: Mozambique io: Mozambik is: Mósambík it: Mozambico ja: モザンビーク jv: Mozambik ka: მოზამბიკი kg: Musambiki kk: Мозамбик kn: ಮೊಜಾಂಬಿಕ್ ko: 모잠비크 ku: Mozambîk kw: Mosambik la: Mozambicum lb: Mosambik li: Mozambique ln: Mozambíki lt: Mozambikas lv: Mozambika mg: Mozambika mi: Mohapiki mk: Мозамбик ml: മൊസാംബിക്ക് mn: Мозамбик mr: मोझांबिक ms: Mozambique mt: Możambik my: မိုဇမ်ဘစ်နိုင်ငံ nl: Mozambique "no": Mosambik ny: Mozambique oc: Moçambic or: ମୋଜାମ୍ବିକ os: Мозамбик pa: ਮੋਜ਼ੈਂਬੀਕ pl: Mozambik ps: موزمبيق pt: Moçambique qu: Musambik ro: Mozambic ru: Мозамбик rw: Mozambike sa: मोजम्बीक sc: Mozambico se: Mosambik sg: Mözämbîka sh: Mozambik sk: Mozambik sl: Mozambik sn: Mozambique so: Musanbiig sq: Mozambiku sr: Мозамбик ss: IMozambikhi su: Mosambik sv: Moçambique sw: Msumbiji ta: மொசாம்பிக் tg: Мозамбик th: ประเทศโมซัมบิก ti: ሞዛምቢክ tk: Mozambik tl: Musambik tr: Mozambik ts: Mozambique ug: موزامبىك uk: Мозамбік ur: موزمبیق uz: Mozambik ve: Mozambikwi vi: Mozambique vo: Mozambikän wo: Mosambik yi: מאזאמביק yo: Mòsámbìk zh: 莫桑比克 zu: IMozambiki ================================================ FILE: settings/country-names/na.yaml ================================================ name: default: Namibia af: Namibië am: ናሚቢያ an: Namibia ar: ناميبيا az: Namibiya ba: Намибия be: Намібія bg: Намибия bm: Namibia bn: নামিবিয়া bo: ན་མི་བྷི་ཡ། br: Namibia bs: Namibija ca: Namíbia ce: Намиби cs: Namibie cv: Намиби cy: Namibia da: Namibia de: Namibia el: Ναμίμπια en: Namibia eo: Namibio es: Namibia et: Namiibia eu: Namibia fa: نامیبیا fi: Namibia fo: Namibia fr: Namibie fy: Namybje ga: An Namaib gd: Naimibia gl: Namibia gv: Yn Nameeb he: נמיביה hi: नामीबिया hr: Namibija ht: Namibi hu: Namíbia hy: Նամիբիա ia: Namibia id: Namibia ie: Namibia io: Namibia is: Namibía it: Namibia ja: ナミビア jv: Namibia ka: ნამიბია kg: Namibia ki: Namibia kk: Намибия kn: ನಮೀಬಿಯ ko: 나미비아 ku: Namîbya kw: Namibi la: Namibia lb: Namibien li: Namibië ln: Namibia lt: Namibija lv: Namībija mg: Namibia mi: Namīpia mk: Намибија ml: നമീബിയ mn: Намиби mr: नामिबिया ms: Namibia mt: Namibja my: နမီးဘီးယားနိုင်ငံ nl: Namibië "no": Namibia nv: Namííbya oc: Namibia or: ନାମିବିଆ os: Намиби pa: ਨਮੀਬੀਆ pl: Namibia ps: نېمبیا pt: Namíbia qu: Namiwya rm: Namibia ro: Namibia ru: Намибия rw: Namibiya sa: नमीबिया sc: Namìbia se: Namibia sg: Namibùii sh: Namibija sk: Namíbia sl: Namibija sn: Namibia so: Nambiya sq: Namibia sr: Намибија ss: INamibiya st: Namibia su: Namibia sv: Namibia sw: Namibia ta: நமீபியா te: నమీబియా tg: Намибия th: ประเทศนามิเบีย ti: ናሚቢያ tk: Namibiýa tl: Namibya tn: Namibia tr: Namibya ts: Namibia tt: Намибия ug: نامىبىيە uk: Намібія ur: نمیبیا uz: Namibiya ve: Namibia vi: Namibia vo: Namibiyän wo: Namibi yi: נאמיביע yo: Nàmíbíà zh: 纳米比亚 zu: INamibhiya ================================================ FILE: settings/country-names/ne.yaml ================================================ name: default: Niger af: Niger am: ኒጄር an: Nícher ar: النيجر az: Niger ba: Нигер be: Нігер bg: Нигер bm: Nijer bn: নাইজার bo: ནི་ཇར། br: Niger bs: Niger ca: Níger ce: Нигер cs: Niger cy: Niger da: Niger de: Niger dv: ނީޖަރު ee: Niger el: Νίγηρας en: Niger eo: Niĝero es: Níger et: Niger eu: Niger fa: نیجر fi: Niger fo: Niger fr: Niger fy: Niger ga: An Nígir gd: Nìgeir gl: Níxer gu: નાઈજર gv: Yn Neegeyr ha: Nijar he: ניז'ר hi: नाइजर hr: Niger ht: Nijè hu: Niger hy: Նիգեր ia: Niger id: Niger ie: Niger ig: Niger io: Nijer is: Níger it: Niger ja: ニジェール jv: Niger ka: ნიგერი kg: Nijer kk: Нигер ko: 니제르 ku: Nîjer kw: Pow Nijer la: Niger lb: Niger li: Niger ln: Nizer lt: Nigeris lv: Nigēra mi: Ngāika mk: Нигер ml: നീഷർ mn: Нигер mr: नायजर ms: Niger mt: Niġer my: နိုင်ဂျာနိုင်ငံ nl: Niger "no": Niger oc: Nigèr or: ନାଇଜର os: Нигер pa: ਨਾਈਜਰ pl: Niger ps: نایجېر pt: Níger qu: Niqir ro: Niger ru: Нигер rw: Nigeri sa: नीजे sc: Nixèr se: Niger sg: Nizëre sh: Niger si: නයිජර් sk: Niger sl: Niger sn: Niger so: Nayjar sq: Nigeri sr: Нигер ss: INayijari su: Niger sv: Niger sw: Niger ta: நைஜர் te: నైజర్ tg: Ниҷер th: ประเทศไนเจอร์ ti: ናይጀር tk: Niger tl: Niyer tr: Nijer ts: Niger ug: نىگېر uk: Нігер ur: نائجر uz: Niger vi: Niger vo: Nigerän wo: Niseer yi: ניזשער yo: Nìjẹ̀r zh: 尼日尔 zu: INayighe ================================================ FILE: settings/country-names/ng.yaml ================================================ name: default: Nigeria af: Nigerië ak: Nigeria am: ናይጄሪያ an: Nicheria ar: نيجيريا az: Nigeriya ba: Нигерия be: Нігерыя bg: Нигерия bm: Nijeria bn: নাইজেরিয়া bo: ནི་ཇི་རི་ཡ། br: Nigeria bs: Nigerija ca: Nigèria ce: Нигери cs: Nigérie cv: Нигери cy: Nigeria da: Nigeria de: Nigeria dv: ނައިޖީރިއާ ee: Nigeria el: Νιγηρία en: Nigeria eo: Niĝerio es: Nigeria et: Nigeeria eu: Nigeria fa: نیجریه ff: Niiseriya fi: Nigeria fo: Nigeria fr: Nigéria fy: Nigearia ga: An Nigéir gd: Nìgeiria gl: Nixeria gn: Niheria gu: નાઈજેરિયા gv: Yn Naigeer ha: Nijeriya he: ניגריה hi: नाईजीरिया hr: Nigerija ht: Nijerya hu: Nigéria hy: Նիգերիա ia: Nigeria id: Nigeria ie: Nigeria ig: Naigeria io: Nigeria is: Nígería it: Nigeria ja: ナイジェリア jv: Nigeria ka: ნიგერია kg: Nizeria kk: Нигерия kn: ನೈಜೀರಿಯ ko: 나이지리아 ks: نایجیرِیا ku: Nîjerya kw: Nijeri la: Nigeria lb: Nigeria li: Nigeria ln: Nizeria lt: Nigerija lv: Nigērija mg: Nizeria mi: Ngāitiria mk: Нигерија ml: നൈജീരിയ mn: Нигери mr: नायजेरिया ms: Nigeria mt: Niġerja my: နိုင်ဂျီးရီးယားနိုင်ငံ ne: नाइजेरिया nl: Nigeria "no": Nigeria oc: Nigèria or: ନାଇଜେରିଆ os: Нигери pa: ਨਾਈਜੀਰੀਆ pl: Nigeria ps: نايجېريا pt: Nigéria qu: Niqirya ro: Nigeria ru: Нигерия rw: Nijeriya sa: नैजीरिया sc: Nigéria se: Nigeria sg: Nizerïa sh: Nigerija sk: Nigéria sl: Nigerija sn: Nigeria so: Nayjeeriya sq: Nigeria sr: Нигерија ss: INayijeriya su: Nigeria sv: Nigeria sw: Nigeria ta: நைஜீரியா te: నైజీరియా tg: Ниҷерия th: ประเทศไนจีเรีย ti: ናይጂሪያ tk: Nigeriýa tl: Nigerya tr: Nijerya ts: Nigeria tt: Нигерия ug: نىگېرىيە uk: Нігерія ur: نائجیریا uz: Nigeriya vi: Nigeria vo: Nigeriyän wa: Nidjeria xh: INigeria yi: ניגעריע yo: Nàìjíríà za: Nizywlihya zh: 奈及利亞 zu: INigeria ================================================ FILE: settings/country-names/ni.yaml ================================================ name: default: Nicaragua af: Nicaragua am: ኒካራጓ an: Nicaragua ar: نيكاراجوا ay: Nikarwa az: Nikaraqua ba: Никарагуа be: Нікарагуа bg: Никарагуа bm: Nikaragwa bn: নিকারাগুয়া bo: ནི་ཀ་ར་གུ་ཝ། br: Nicaragua bs: Nikaragva ca: Nicaragua ce: Никарагуа cs: Nikaragua cy: Nicaragua da: Nicaragua de: Nicaragua dv: ނިކަރާގުއާ ee: Nicaragua el: Νικαράγουα en: Nicaragua eo: Nikaragvo es: Nicaragua et: Nicaragua eu: Nikaragua fa: نیکاراگوئه fi: Nicaragua fo: Nikaragua fr: Nicaragua fy: Nikaragûa ga: Nicearagua gd: Niocaragua gl: Nicaragua gn: Nikaragua gv: Yn Nickeraag he: ניקרגואה hi: निकारागुआ hr: Nikaragva ht: Nikaragwa hu: Nicaragua hy: Նիկարագուա ia: Nicaragua id: Nikaragua ie: Nicaragua io: Nikaragua is: Níkaragva it: Nicaragua ja: ニカラグア jv: Nikaragua ka: ნიკარაგუა kk: Никарагуа ko: 니카라과 ku: Nîkaragua kv: Никарагуа kw: Nikaragwa la: Nicaragua lb: Nicaragua li: Nicaragua ln: Nikalagwa lt: Nikaragva lv: Nikaragva mg: Nicaragua mi: Nikarāhua mk: Никарагва ml: നിക്കരാഗ്വ mn: Никарагуа mr: निकाराग्वा ms: Nicaragua mt: Nikaragwa my: နီကာရာဂွါနိုင်ငံ ne: निकाराग्वा nl: Nicaragua "no": Nicaragua oc: Nicaragua or: ନିକାରାଗୁଆ os: Никарагуæ pa: ਨਿਕਾਰਾਗੁਆ pl: Nikaragua ps: نیکاراګوا pt: Nicarágua qu: Nikarawa ro: Nicaragua ru: Никарагуа rw: Nikaragwa sa: निकारगुवा se: Nicaragua sh: Nikaragva sk: Nikaragua sl: Nikaragva so: Nikaragua sq: Nikaraguaja sr: Никарагва ss: INikhalaga su: Nikaragua sv: Nicaragua sw: Nikaragua ta: நிக்கராகுவா tg: Никарагуа th: ประเทศนิการากัว tl: Nicaragua tr: Nikaragua tt: Никарагуа ug: نىكاراگۇئا uk: Нікарагуа ur: نکاراگوا uz: Nikaragua vi: Nicaragua vo: Nikaraguvän wo: Nikaraaguwa yi: ניקאראגוא yo: Nikarágúà zh: 尼加拉瓜 zu: Nicaragua ================================================ FILE: settings/country-names/nl.yaml ================================================ name: default: Nederland af: Nederland am: ሆላንድ an: Países Baixos ar: هولندا ay: Aynacha Jach'a Markanaka az: Niderland ba: Нидерландтар be: Нідэрланды bg: Нидерландия bh: एम्सटर्डम bi: Netherlands bn: নেদারল্যান্ডস bo: ཧོ་ལན། br: Izelvroioù bs: Holandija ca: Països Baixos ce: Нидерландаш co: Paesi Bassi cs: Nizozemsko cu: Нидєрландꙑ cv: Нидерландсем cy: Yr Iseldiroedd da: Holland de: Niederlande dv: ނެދަލޭންޑު dz: ནེ་དར་ལེནཌསི་ ee: Netherlands el: Κάτω Χώρες en: Netherlands eo: Nederlando es: Países Bajos et: Holland eu: Herbehereak fa: هلند ff: Holannda fi: Alankomaat fo: Niðurlond fr: Pays-Bas fy: Nederlân ga: An Ísiltír gd: Na Tìrean Ìsle gl: Países Baixos gn: Tetãnguéra Yvýi gu: નેધરલેંડ gv: Yn Çheer Injil he: הולנד hi: नीदरलैण्ड hr: Nizozemska ht: Peyiba hu: Hollandia hy: Նիդերլանդներ ia: Pais Basse id: Belanda ie: Nederland io: Nederlando is: Holland it: Paesi Bassi ja: オランダ jv: Walanda ka: ნიდერლანდი kg: Pays-Bas kk: Нидерланд kl: Hollandi kn: ನೆದರ್‍ಲ್ಯಾಂಡ್ಸ್ ko: 네덜란드 ks: नेदरलैंड्स ku: Holenda kv: Нидерландъяс kw: Iseldiryow ky: Нидерландтар la: Nederlandia lb: Holland li: Nederland ln: Holanda lt: Nyderlandai lv: Nīderlande mi: Hōrana mk: Холандија ml: നെതർലന്റ്സ് mn: Нидерланд mr: नेदरलँड्स ms: Belanda mt: Pajjiżi l-Baxxi my: နယ်သာလန်နိုင်ငံ na: Eben Eyong ne: नेदरल्याण्ड्स nl: Nederland "no": Nederland nv: Tsin Bikeeʼ Dineʼé Bikéyah oc: Païses Basses os: Нидерландтæ pa: ਨੀਦਰਲੈਂਡ pl: Królestwo Niderlandów ps: هالنډ pt: Países Baixos qu: Urasuyu rm: Pajais Bass ro: Țările de Jos ru: Нидерланды rw: Ubuholandi sa: नेदरलैंड्स sc: Logos Bascios se: Vuolleeatnamat sh: Holandija si: නෙදර්ලන්තය sk: Holandsko sl: Nizozemska so: Holland sq: Holanda sr: Холандија ss: IDashi st: Hôlanê su: Walanda sv: Nederländerna sw: Uholanzi ta: நெதர்லாந்து te: నెదర్లాండ్ tg: Нидерланд th: ประเทศเนเธอร์แลนด์ tk: Niderlandlar tl: Olanda to: Holani tr: Hollanda tt: Нидерландлар ug: گوللاندىيە uk: Нідерланди ur: ہالینڈ uz: Niderlandlar vi: Hà Lan vo: Nedän wa: Bas Payis wo: Olaand xh: ENetherlands yi: האלאנד yo: Nẹ́dálándì zh: 荷蘭 zu: Netherlands ================================================ FILE: settings/country-names/no.yaml ================================================ name: default: Norge af: Noorweë am: ኖርዌይ an: Noruega ar: النرويج ay: Nurweka az: Norveç ba: Норвегия be: Нарвегія bg: Норвегия bi: Norway bm: Norwij bn: নরওয়ে bo: ནོར་ཝེ། br: Norvegia bs: Norveška ca: Noruega ce: Норвеги co: Nurvegia cs: Norsko cu: Норєгъ cv: Норвеги cy: Norwy da: Norge de: Norwegen dv: ނޯވޭ dz: ནོ་ཝེ་ ee: Norway el: Νορβηγία en: Norway eo: Norvegio es: Noruega et: Norra eu: Norvegia fa: نروژ fi: Norja fo: Noregur fr: Norvège fy: Noarwegen ga: An Iorua gd: Nirribhidh gl: Noruega gn: Noruega gu: નોર્વે gv: Norlynn he: נורבגיה hi: नॉर्वे hr: Norveška ht: Nòvèj hu: Norvégia hy: Նորվեգիա ia: Norvegia id: Norwegia ie: Norvegia io: Norvegia is: Noregur it: Norvegia iu: ᓄᕐᒋ ja: ノルウェー jv: Norwégia ka: ნორვეგია kg: Norge kk: Норвегия kl: Norge kn: ನಾರ್ವೇ ko: 노르웨이 ku: Norwêc kv: Норвегия kw: Norgagh ky: Норвегия la: Norvegia lb: Norwegen li: Noorwege ln: Norvej lt: Norvegija lv: Norvēģija mg: Norvezy mi: Nōwei mk: Норвешка ml: നോർവെ mn: Норвеги mr: नॉर्वे ms: Norway mt: Norveġja my: နော်ဝေနိုင်ငံ na: Norwei nb: Norge ne: नर्वे nl: Noorwegen nn: Noreg "no": Norge oc: Norvègia om: Norway or: ନରୱେ os: Норвеги pa: ਨਾਰਵੇ pl: Norwegia ps: ناروې pt: Noruega qu: Nurwiga rm: Norvegia rn: Noruega ro: Norvegia ru: Норвегия rw: Noruveje sa: नार्वे sc: Norvegia se: Norga sh: Norveška sk: Nórsko sl: Norveška so: Norway sq: Norvegjia sr: Норвешка ss: INoweyi su: Norwégia sv: Norge sw: Norwei ta: நோர்வே te: నార్వే tg: Норвегия th: ประเทศนอร์เวย์ tk: Norwegiýa tl: Noruwega tr: Norveç ts: Norge tt: Норвегия ug: نورۋېگىيە uk: Норвегія ur: ناروے uz: Norvegiya vi: Na Uy vo: Norgän wa: Norvedje wo: Noorwees yi: נארוועגיע yo: Nọ́rwèy za: Nozveih zh: 挪威 zu: INoki ================================================ FILE: settings/country-names/np.yaml ================================================ name: default: Nepal ar: نيبال be: Непал br: Nepal el: Νεπάλ en: Nepal eo: Nepalo fa: نپال fi: Nepal fr: Népal ga: Neipeal gd: Neapàl he: נפאל hu: Nepál is: Nepal it: Nepal ja: ネパール la: Nepalia lt: Nepalas lv: Nepāla mn: Балба ne: नेपाल "no": Nepal pl: Nepal ru: Непал sv: Nepal th: ประเทศเนปาล uk: Непал zh: 尼泊尔 ================================================ FILE: settings/country-names/nr.yaml ================================================ name: default: Naoero am: ናውሩ ar: ناورو be: Наўру bg: Науру bn: নাউরু br: Nauru cs: Nauru da: Nauru de: Nauru dv: ނައުރޫ el: Ναουρού en: Nauru eo: Nauro fa: نائورو fi: Nauru fr: Nauru fy: Naurû ga: Nárú he: נאורו hi: नाउरु hr: Nauru hu: Nauru is: Nárú ja: ナウル li: Naoeroe lv: Nauru ml: നൗറു mn: Науру mt: Nawru "no": Nauru pl: Nauru ru: Науру se: Nauru sk: Nauru sq: Naurua sv: Nauru ta: நவூரு th: ประเทศนาอูรู uk: Науру ur: ناورو vo: Naureän zh: 諾魯 ================================================ FILE: settings/country-names/nu.yaml ================================================ name: default: Niuē ar: نييوي be: Ніуэ bg: Ниуе br: Niue cs: Niue da: Niue de: Niue dv: ނީއު el: Νιούε en: Niue eo: Niuo es: Niue fa: نیووی fi: Niue fr: Niue fy: Niûé ga: Niue he: ניואה hr: Niue hu: Niue is: Niue ja: ニウエ ka: ნიუე kk: Ниуе ko: 니우에 ku: Niûe lt: Niujė lv: Niue mk: Ниуе ml: നിയുവെ mn: Ниуэ mr: न्युए "no": Niue os: Ниуэ pl: Niue ru: Ниуэ rw: Niyuwe sk: Niue sr: Нијуе sv: Niue ta: நியுவே th: นีอูเอ ug: Niué Arili uk: Ніуе ur: نیووے wo: Niwe zh: 紐埃 ================================================ FILE: settings/country-names/nz.yaml ================================================ name: default: New Zealand / Aotearoa af: Nieu-Seeland am: ኒው ዚላንድ an: Nueva Zelanda ar: نيوزيلندا az: Yeni Zelandiya ba: Яңы Зеландия be: Новая Зеландыя bg: Нова Зеландия bi: Niu Zilan bm: New Zealand bn: নিউজিল্যান্ড bo: ནིའུ་ཛི་ལེན་ཌི། br: Zeland-Nevez bs: Novi Zeland ca: Nova Zelanda cs: Nový Zéland cv: Çĕнĕ Зеланди cy: Seland Newydd da: New Zealand de: Neuseeland dv: ނިއުޒިލޭންޑު dz: ནིའུ་ཛི་ལེནཌ་ el: Νέα Ζηλανδία en: New Zealand eo: Nov-Zelando es: Nueva Zelanda et: Uus-Meremaa eu: Zeelanda Berria fa: نیوزیلند fi: Uusi-Seelanti fo: Nýsæland fr: Nouvelle-Zélande fy: Nij-Seelân ga: An Nua-Shéalainn gd: Sealainn Nuadh gl: Nova Zelandia gv: Yn Teelynn Noa he: ניו זילנד hi: न्यूज़ीलैण्ड hr: Novi Zeland ht: Nouvèl Zelann hu: Új-Zéland hy: Նոր Զելանդիա ia: Nove Zelanda id: Selandia Baru ie: Nov-Zeland io: Nova-Zelando is: Nýja-Sjáland it: Nuova Zelanda ja: ニュージーランド jv: Sélandia Anyar ka: ახალი ზელანდია kk: Жаңа Зеландия kl: New Zealand km: នូវែលសេឡង់ kn: ನ್ಯೂ ಜೀಲ್ಯಾಂಡ್ ko: 뉴질랜드 ku: Nû Zelenda kv: Выль Зеландия kw: Mordir Nowydh la: Nova Zelandia lb: Neiséiland li: Nui-Zieland lo: ປະເທດນູແວນ ເຊລັງ lt: Naujoji Zelandija lv: Jaunzēlande mi: Aotearoa mk: Нов Зеланд ml: ന്യൂസീലൻഡ് mn: Шинэ Зеланд mr: न्यू झीलँड ms: New Zealand mt: New Zealand my: နယူးဇီလန်နိုင်ငံ na: Niu Djiran ne: न्यू जील्याण्ड nl: Nieuw-Zeeland "no": New Zealand oc: Nòva Zelanda or: ନିଉଜିଲ୍ୟାଣ୍ଡ os: Ног Зеланди pa: ਨਿਊਜ਼ੀਲੈਂਡ pl: Nowa Zelandia pt: Nova Zelândia qu: Musuq Silanda rm: Nova Zelanda ro: Noua Zeelandă ru: Новая Зеландия rw: Nuveli Zelande sa: न्यू-जीलैंड se: Aotearoa sh: Novi Zeland sk: Nový Zéland sl: Nova Zelandija sm: Niu Sila so: New Zealand sq: Zelanda e Re sr: Нови Зеланд su: Selandia Anyar sv: Nya Zeeland sw: New Zealand ta: நியூசிலாந்து te: న్యూజీలాండ్ tg: Зеландияи Нав th: ประเทศนิวซีแลนด์ tk: Täze Zelandiýa tl: New Zealand tr: Yeni Zelanda tt: Яңа Зеландия ty: Aotearoa ug: يېڭى زېلاندىيە uk: Нова Зеландія ur: نیوزی لینڈ uz: Yangi Zelandiya vi: New Zealand vo: Nula-Seleäns wa: Nouve Zelande wo: Seland-Gu-Bees yi: ניו זילאנד yo: New Zealand za: Saen Saelanz zh: 新西兰 zu: INyuzilandi ================================================ FILE: settings/country-names/om.yaml ================================================ name: default: عمان af: Oman am: ኦማን an: Omán ar: عمان az: Oman ba: Оман be: Аман bg: Оман bn: ওমান bo: ཨོ་མན། br: Oman bs: Oman ca: Oman ce: Оман cs: Omán cv: Оман cy: Oman da: Oman de: Oman dv: ޢުމާން el: Ομάν en: Oman eo: Omano es: Omán et: Omaan eu: Oman fa: عمان fi: Oman fr: Oman fy: Oman ga: Óman gd: Omàn gl: Omán gn: Omã gu: ઓમાન gv: Yn Omaan he: עומאן hi: ओमान hr: Oman ht: Omàn hu: Omán hy: Օման ia: Oman id: Oman ie: Oman io: Oman is: Óman it: Oman ja: オマーン jv: Oman ka: ომანი kk: Оман kl: Oman kn: ಒಮಾನ್ ko: 오만 ku: Oman kv: Оман kw: Oman la: Omania lb: Oman li: Omaan ln: Oman lt: Omanas lv: Omāna mi: Omāna mk: Оман ml: ഒമാൻ mn: Оман mr: ओमान ms: Oman my: အိုမန်နိုင်ငံ na: Oman nl: Oman "no": Oman nv: Omą́ą́ oc: Oman or: ଓମାନ os: Оман pa: ਓਮਾਨ pl: Oman ps: عمان pt: Omã qu: Uman ro: Oman ru: Оман rw: Omani sa: ओमान se: Oman sh: Oman si: ඕමානය sk: Omán sl: Oman so: Cumaan sq: Omani sr: Оман ss: IMani su: Oman sv: Oman sw: Omani ta: ஓமான் te: ఒమన్ tg: Оман th: ประเทศโอมาน tk: Oman tl: Oman tr: Umman tt: Оман ug: ئومان uk: Оман ur: سلطنت عمان uz: Oman vi: Oman vo: Lomän wo: Omaan yi: אמאן yo: Oman zh: 阿曼 ================================================ FILE: settings/country-names/pa.yaml ================================================ name: default: Panamá af: Panama am: ፓናማ an: Panamá ar: بنما ay: Panama az: Panama ba: Панама be: Панама bg: Панама bm: Panama bn: পানামা bo: པ་ན་མ། br: Panamá bs: Panama ca: Panamà ce: Панама cs: Panama cv: Панама cy: Panama da: Panama de: Panama dv: ޕެނަމާ ee: Panama el: Παναμάς en: Panama eo: Panamo es: Panamá et: Panama eu: Panama fa: پاناما fi: Panama fo: Panama fr: Panamá fy: Panama ga: Panama gd: Panama gl: Panamá gn: Panama gu: પનામા gv: Yn Phanamaa he: פנמה hi: पनामा hr: Panama ht: Panama hu: Panama hy: Պանամա ia: Panama id: Panama ie: Panamá io: Panama is: Panama it: Panamá ja: パナマ jv: Panama ka: პანამა ki: Panama kk: Панама kn: ಪನಾಮಾ ko: 파나마 ku: Panama kw: Panama la: Panama lb: Panama li: Panama ln: Panama lt: Panama lv: Panama mg: Panama mi: Panama mk: Панама ml: പനാമ mn: Панам mr: पनामा ms: Panama mt: Panama my: ပနားမားနိုင်ငံ ne: पानामा nl: Panama "no": Panama oc: Panamà or: ପାନାମା os: Панамæ pa: ਪਨਾਮਾ pl: Panama ps: پاناما pt: Panamá qu: Panama ro: Panama ru: Панама rw: Panama sa: पानामा se: Panama sh: Panama sk: Panama sl: Panama so: Banama sq: Panamaja sr: Панама ss: IPhanama su: Panama sv: Panama sw: Panama ta: பனாமா te: పనామా tg: Панама th: ประเทศปานามา tl: Panama tr: Panama tt: Панама ug: پاناما uk: Панама ur: پاناما uz: Panama vi: Panama vo: Panamän wo: Panamaa yi: פאנאמא yo: Panamá zh: 巴拿马 zu: Panama ================================================ FILE: settings/country-names/pe.yaml ================================================ name: default: Perú ab: Перу af: Peru ak: Peru am: ፔሩ an: Perú ar: بيرو ay: Piruw az: Peru ba: Перу be: Перу bg: Перу bi: Peru bm: Peru bn: পেরু bo: པེ་རུ། br: Perou bs: Peru ca: Perú ce: Перу ch: Perú co: Perù cs: Peru cv: Перу cy: Periw da: Peru de: Peru dv: ޕެރޫ ee: Peru el: Περού en: Peru eo: Peruo es: Perú et: Peruu eu: Peru fa: پرو ff: Peru fi: Peru fo: Peru fr: Pérou fy: Perû ga: Peiriú gd: Pearù gl: Perú gn: Perũ gu: પેરુ gv: Yn Pheroo he: פרו hi: पेरू hr: Peru ht: Pewou hu: Peru hy: Պերու ia: Peru id: Peru ie: Perú io: Peru is: Perú it: Perù iu: ᐱᕉ ja: ペルー jv: Peru ka: პერუ ki: Peru kk: Перу kl: Peru kn: ಪೆರು ko: 페루 ku: Perû kv: Перу kw: Perou ky: Перу la: Peruvia lb: Peru li: Peru ln: Peru lt: Peru lv: Peru mi: Perū mk: Перу ml: പെറു mn: Перу mr: पेरू ms: Peru mt: Perù my: ပီရူးနိုင်ငံ na: Peru ne: पेरू nl: Peru "no": Peru oc: Peró or: ପେରୁ os: Перу pa: ਪੇਰੂ pl: Peru ps: پيرو pt: Peru qu: Piruw rm: Peru ro: Peru ru: Перу rw: Peru sa: पेरु se: Peru sh: Peru si: පේරූ sk: Peru sl: Peru sn: Peru so: Peru sq: Peruja sr: Перу ss: IPheru su: Péru sv: Peru sw: Peru ta: பெரு te: పెరూ tg: Перу th: ประเทศเปรู tk: Peru tl: Peru tr: Peru tt: Перу ug: پېرۇ uk: Перу ur: پیرو uz: Peru vi: Peru vo: Peruvän wo: Peru yi: פערו yo: Perú zh: 秘鲁 zu: Peru ================================================ FILE: settings/country-names/pg.yaml ================================================ name: default: Papua Niugini af: Papoea-Nieu-Guinee am: ፓፑዋ ኒው ጊኒ an: Papua y Nueva Guinea ar: بابوا غينيا الجديدة az: Papua-Yeni Qvineya ba: Папуа — Яңы Гвинея be: Папуа — Новая Гвінея bg: Папуа Нова Гвинея bi: Papua New Guinea bn: পাপুয়া নিউ গিনি bo: པ་པུ་འ་ནིའུ་གི་ནེ། br: Papoua Ginea-Nevez bs: Papua Nova Gvineja ca: Papua Nova Guinea ce: Папуа — Керла Гвине cs: Papua Nová Guinea cu: Папоуа · Нова Гвинєꙗ cy: Papua Guinea Newydd da: Papua Ny Guinea de: Papua-Neuguinea dv: ޕަޕުއާ ނިއު ގިނީ el: Παπούα Νέα Γουινέα en: Papua New Guinea eo: Papuo-Nov-Gvineo es: Papua-Nueva Guinea et: Paapua Uus-Guinea eu: Papua Ginea Berria fa: پاپوآ گینه نو fi: Papua-Uusi-Guinea fo: Papua Nýguinea fr: Papouasie-Nouvelle-Guinée fy: Papoea Nij-Guineä ga: Nua-Ghuine Phapua gd: Gini Nuadh Phaputhach gl: Papúa Nova Guinea gv: Papooey Guinea Noa he: פפואה גינאה החדשה hi: पापुआ न्यू गिनी hr: Papua Nova Gvineja ht: Papwazi-Nouvèl-Gine hu: Pápua Új-Guinea hy: Պապուա Նոր Գվինեա ia: Papua Nove Guinea id: Papua Nugini io: Papua-Nova-Guinea is: Papúa Nýja-Gínea it: Papua Nuova Guinea ja: パプアニューギニア jv: Papua Nugini ka: პაპუა-ახალი გვინეა kk: Папуа Жаңа Гвинея kn: ಪಾಪುಅ ನ್ಯೂ ಗಿನಿ ko: 파푸아 뉴기니 ku: Papua Gîneya Nû ky: Папуа-Жаӊы-Гвинея. la: Papua Nova Guinea lb: Papua-Neiguinea li: Papoea-Nui-Guinea lt: Papua Naujoji Gvinėja lv: Papua-Jaungvineja mi: Papua Nūkini mk: Папуа Нова Гвинеја ml: പാപുവ ന്യൂ ഗിനിയ mn: Папуа-Шинэ Гвиней mr: पापुआ न्यू गिनी ms: Papua New Guinea mt: Papwa Gwinea l-Ġdida my: ပါပူအာနယူးဂီနီနိုင်ငံ na: Papua New Guinea nl: Papoea-Nieuw-Guinea "no": Papua Ny-Guinea nv: Páápowa Bigíní Ániidí oc: Papoa-Nòva Guinèa os: Папуæ — Ног Гвиней pa: ਪਾਪੂਆ ਨਿਊ ਗਿਨੀ pl: Papua-Nowa Gwinea pt: Papua-Nova Guiné qu: Papwa Ñukini ro: Papua Noua Guinee ru: Папуа - Новая Гвинея rw: Papuwa Nuveli Gineya sa: पपुवा न्यू गिनी se: Papua-Ođđa-Guinea sg: Papû Finî Ginëe sh: Papua Nova Gvineja sk: Papua-Nová Guinea sl: Papua Nova Gvineja sm: Papua sq: Papua Guinea e Re sr: Папуа Нова Гвинеја su: Papua Nugini sv: Papua Nya Guinea sw: Papua Guinea Mpya ta: பப்புவா நியூ கினி th: ประเทศปาปัวนิวกินี tl: Papua Bagong Ginea tr: Papua Yeni Gine tt: Папуа — Яңа Гвинея ug: پاپۇئا يېڭى گۋىنېيە uk: Папуа Нова Ґвінея ur: پاپوا نیو گنی uz: Papua Yangi Gvineya vi: Papua New Guinea vo: Papuveän Nula-Gineyän wo: Papuwaasi-Gine-Gu-Bees yo: Papua Guinea Titun zh: 巴布亚新几内亚 / 巴布亞紐幾內亞 / 巴布亞新畿內亞 ================================================ FILE: settings/country-names/ph.yaml ================================================ name: default: Philippines af: Filippyne am: ፊሊፒንስ an: Filipinas ar: الفلبين az: Filippin be: Філіпіны bg: Филипини bn: ফিলিপাইন bo: ཧྥི་ལི་པིན། br: Filipinez bs: Filipini ca: Filipines ce: Филиппин cs: Filipíny cv: Филиппин cy: Pilipinas da: Filippinerne de: Philippinen dv: ފިލިޕީންސް el: Φιλιππίνες en: Philippines eo: Filipinoj es: Filipinas et: Filipiinid eu: Filipinak fa: فیلیپین fi: Filippiinit fo: Filipsoyggjar fr: Philippines fy: Filipinen ga: Na hOileáin Fhilipíneacha gd: Na h-Eileanan Filipineach gl: Filipinas gn: Filipina gu: ફીલીપાઈન્સ gv: Ny h-Ellanyn Philippeenagh he: הפיליפינים hi: फ़िलीपीन्स hr: Filipini ht: Filipin hu: Fülöp-szigetek hy: Ֆիլիպիններ ia: Philippinas id: Filipina ie: Filipines io: Filipini is: Filippseyjar it: Filippine ja: フィリピン jv: Filipina ka: ფილიპინები kk: Филиппиндер kl: Philippines km: ហ្វីលីពីន kn: ಫಿಲಿಪ್ಪೀನ್ಸ್ ko: 필리핀 ku: Filîpîn kv: Филиппинъяс kw: Filipinys la: Philippinae lb: Philippinen li: Filipiene lt: Filipinai lv: Filipīnas mg: Filipina mi: Piripīni mk: Филипини ml: ഫിലിപ്പീൻസ് mn: Филиппин mr: फिलिपाईन्स ms: Filipina my: ဖိလစ်ပိုင်နိုင်ငံ na: Eben Piripin nb: Filippinene ne: फिलिपिन्स nl: Filipijnen nn: Filippinane "no": Filippinene nv: Kéyah Dańlíinii oc: Filipinas or: ଫିଲିପାଇନ୍ସ os: Филиппинтæ pa: ਫ਼ਿਲਪੀਨਜ਼ pl: Filipiny ps: فلېپين pt: Filipinas qu: Philipinakuna ro: Filipine ru: Филиппины rw: Filipine sa: कलिंगद्वीप se: Filippiinnat sg: Filipîni sh: Filipini si: පිලිපීනය sk: Filipíny sl: Filipini sm: Filipaina so: Filibiin sq: Filipinet sr: Филипини ss: IFiliphayi su: Pilipina sv: Filippinerna sw: Ufilipino ta: பிலிப்பீன்சு te: ఫిలిప్పీన్స్ tg: Филиппин th: ประเทศฟิลิปปินส์ tk: Filippinler tl: Pilipinas tr: Filipinler tt: Филиппиннар ug: فىلىپپىن uk: Філіппіни ur: فلپائن uz: Filippin vi: Philippines vo: Filipuäns wa: Filipenes wo: Filipiin yi: פיליפינען yo: Filipínì za: Feihlizbinh zh: 菲律宾 / 菲律賓 ================================================ FILE: settings/country-names/pk.yaml ================================================ name: default: پاکستان af: Pakistan am: ፓኪስታን an: Pakistán ar: باكستان as: পাকিস্তান az: Pakistan ba: Пакистан be: Пакістан bg: Пакистан bh: पाकिस्तान bn: পাকিস্তান bo: པ་ཀི་སི་ཐན br: Pakistan bs: Pakistan ca: Pakistan ce: Пакистан cs: Pákistán cv: Пакистан cy: Pakistan da: Pakistan de: Pakistan dv: ޕާކިސްތާން el: Πακιστάν en: Pakistan eo: Pakistano es: Pakistán et: Pakistan eu: Pakistan fa: پاکستان fi: Pakistan fo: Pakistan fr: Pakistan fy: Pakistan ga: An Phacastáin gd: Pagastàn gl: Paquistán gn: Pakistán gu: પાકિસ્તાન gv: Yn Phakistaan ha: Pakistan he: פקיסטן hi: पाकिस्तान hr: Pakistan ht: Pakistan hu: Pakisztán hy: Պակիստան ia: Pakistan id: Pakistan ie: Pakistan io: Pakistan is: Pakistan it: Pakistan iu: ᐸᑭᔅᑕᓐ ja: パキスタン jv: Pakistan ka: პაკისტანი ki: Pakistan kk: Пәкістан kl: Pakistan km: ប៉ាគីស្ថាន kn: ಪಾಕಿಸ್ತಾನ ko: 파키스탄 ks: پاکِستان ku: Pakistan kv: Пакистан kw: Pakistan ky: Пакистан la: Pakistania lb: Pakistan li: Pakistan ln: Pakistáni lt: Pakistanas lv: Pakistāna mi: Pakitāne mk: Пакистан ml: പാകിസ്താൻ mn: Пакистан mr: पाकिस्तान ms: Pakistan mt: Pakistan my: ပါကစ္စတန်နိုင်ငံ na: Pakistan ne: पाकिस्तान nl: Pakistan "no": Pakistan nv: Eʼeʼaahjí Naakaii Dootłʼizhí Bikéyah oc: Paquistan or: ପାକିସ୍ଥାନ os: Пакистан pa: ਪਾਕਿਸਤਾਨ pl: Pakistan ps: پاکستان pt: Paquistão qu: Pakistan ro: Pakistan ru: Пакистан rw: Pakisitani sa: पाकिस्तानम् sd: پاڪستان se: Pakistan sh: Pakistan si: පාකිස්ථානය sk: Pakistan sl: Pakistan so: Bakistaan sq: Pakistani sr: Пакистан ss: IPhakhistani su: Pakistan sv: Pakistan sw: Pakistan ta: பாக்கித்தான் te: పాకిస్తాన్ tg: Покистон th: ประเทศปากีสถาน tk: Päkistan tl: Pakistan tr: Pakistan tt: Пакъстан ug: پاكىستان uk: Пакистан ur: ‏پاکستان‎ uz: Pokiston vi: Pakistan vo: Pakistän wa: Pakistan wo: Pakistaan yi: פאקיסטאן yo: Pakístàn zh: 巴基斯坦 zu: IPakistani ================================================ FILE: settings/country-names/pl.yaml ================================================ name: default: Polska ab: Полша af: Pole ak: Poland am: ፖላንድ an: Polonia ar: بولندا ay: Puluña az: Polşa ba: Польша be: Польшча bg: Полша bi: Poland bn: পোল্যান্ড bo: ཕོ་ལན། br: Polonia bs: Poljska ca: Polònia ce: Польша co: Polonia cs: Polsko cu: Пол҄ьска cv: Польша cy: Gwlad Pwyl da: Polen de: Polen dv: ޕޮލެންޑު ee: Poland el: Πολωνία en: Poland eo: Pollando es: Polonia et: Poola eu: Polonia fa: لهستان ff: Poloonya fi: Puola fo: Pólland fr: Pologne fy: Poalen ga: An Pholainn gd: A' Phòlainn gl: Polonia gn: Poloña gu: પોલેંડ gv: Yn Pholynn ha: Poland he: פולין hi: पोलैंड hr: Poljska ht: Polòy hu: Lengyelország hy: Լեհաստան ia: Polonia id: Polandia ie: Polonia ig: Poland io: Polonia is: Pólland it: Polonia iu: ᐳᓚᓐᑦ ja: ポーランド jv: Polandia ka: პოლონეთი kg: Pologne kk: Польша kl: Poleni km: ប្រទេសប៉ូឡូញ kn: ಪೋಲೆಂಡ್ ko: 폴란드 ku: Polonya kv: Польша kw: Poloni ky: Польша la: Polonia lb: Polen lg: Bupoolo li: Pole ln: Poloni lt: Lenkija lv: Polija mg: Polonia mi: Pōrana mk: Полска ml: പോളണ്ട് mn: Польш mr: पोलंड ms: Poland mt: Polonja my: ပိုလန်နိုင်ငံ na: Poran ne: पोल्याण्ड nl: Polen "no": Polen nv: Póolish Dineʼé Bikéyah oc: Polonha or: ପୋଲାଣ୍ଡ os: Польшæ pa: ਪੋਲੈਂਡ pl: Polska ps: پولنډ pt: Polónia qu: Pulunya rm: Pologna rn: Polonia ro: Polonia ru: Польша rw: Polonye sa: पोलैंड sc: Polonia se: Polska sg: Pölôni sh: Poljska si: පෝලන්තය sk: Poľsko sl: Poljska sm: Polagi so: Boland sq: Polonia sr: Пољска ss: IPholandi st: Poland su: Polandia sv: Polen sw: Poland ta: போலந்து te: పోలాండ్ tg: Лаҳистон th: ประเทศโปแลนด์ tk: Polşa tl: Polonya tr: Polonya tt: Польша ug: پولشا uk: Польща ur: پولینڈ uz: Polsha vi: Ba Lan vo: Polän wa: Pologne wo: Poloñ yi: פוילן yo: Polandi zh: 波兰 zu: IPolandi ================================================ FILE: settings/country-names/pn.yaml ================================================ name: default: Pitcairn Islands ar: بتكايرن be: Піткерн br: Inizi Pitcairn ca: Illes Pitcairn cs: Pitcairnovy ostrovy cy: Ynysoedd Pitcairn da: Pitcairn de: Pitcairninseln el: Νήσοι Πίτκαιρν en: Pitcairn Islands eo: Pitkarna Insularo es: Islas Pitcairn fa: پیت‌کرن fi: Pitcairn fr: Îles Pitcairn fy: de Pitcairneilannen ga: Oileán Pitcairn gd: Eilean Pitcairn he: פיטקרן hr: Pitcairnovo Otočje hu: Pitcairn-szigetek id: Kepulauan Pitcairn is: Pitcairn it: Isole Pitcairn la: Pitcairn Insulae lt: Pitkerno salos lv: Pitkērna mi: Pitikeina mk: Питкерн mn: Питкэрн Арлууд nb: Pitcairnøyene nl: Pitcairneilanden nn: Pitcairn "no": Pitcairnøyene pl: Wyspy Pitcairn ru: Острова Питкэрн sl: Pitcairnovi otoki sv: Pitcairnöarna th: หมู่เกาะพิตแคร์น tr: Pitcairn Adaları uk: Острови Піткерн vi: Quần đảo Pitcairn zh: 皮特凯恩群岛 ================================================ FILE: settings/country-names/ps.yaml ================================================ name: default: الأراضي الفلسطينية en: Palestinian Territories "no": Det palestinske området ================================================ FILE: settings/country-names/pt.yaml ================================================ name: default: Portugal af: Portugal am: ፖርቱጋል an: Portugal ar: البرتغال ay: Purtuwal az: Portuqaliya ba: Португалия be: Партугалія bg: Португалия bi: Portugal bn: পর্তুগাল bo: པོ་ཅུ་གྷལ། br: Portugal bs: Portugal ca: Portugal ce: Португали co: Portugallu cs: Portugalsko cu: Портогалїꙗ cv: Португали cy: Portiwgal da: Portugal de: Portugal dv: ޕޯޗުގަލް dz: པོར་ཅུ་གལ་ ee: Portugal el: Πορτογαλία en: Portugal eo: Portugalio es: Portugal et: Portugal eu: Portugal fa: پرتغال fi: Portugali fo: Portugal fr: Portugal fy: Portegal ga: An Phortaingéil gd: A' Phortagail gl: Portugal gn: Poytuga gu: પોર્ટુગલ gv: Yn Phortiugal he: פורטוגל hi: पुर्तगाल hr: Portugal ht: Pòtigal hu: Portugália hy: Պորտուգալիա ia: Portugal id: Portugal ie: Portugal io: Portugal is: Portúgal it: Portogallo ja: ポルトガル jv: Portugal ka: პორტუგალია kg: Mputulukesi kk: Португалия kl: Portugal km: ប្រទេស ព័រទុយហ្គាល់ kn: ಪೋರ್ಚುಗಲ್ ko: 포르투갈 ku: Portûgal kv: Португалия kw: Portyngal ky: Португалия la: Portugallia lb: Portugal li: Portugal ln: Pulutugal lt: Portugalija lv: Portugāle mg: Pôrtogaly mi: Potukara mk: Португалија ml: പോർച്ചുഗൽ mn: Португал mr: पोर्तुगाल ms: Portugal mt: Portugall my: ပေါ်တူဂီနိုင်ငံ na: Portsiugar ne: पोर्चुगल nl: Portugal "no": Portugal oc: Portugal or: ପର୍ତ୍ତୁଗାଲ os: Португали pl: Portugalia ps: پرتګال pt: Portugal qu: Purtugal rm: Portugal rn: Portugal ro: Portugalia ru: Португалия rw: Porutigali sa: पुर्तगाल sc: Portogallo se: Portugal sh: Portugal si: පෘතුගාලය sk: Portugalsko sl: Portugalska sm: Portugal so: Bortuqaal sq: Portugalia sr: Португалија ss: IPhuthukezi st: Portugal su: Portugal sv: Portugal sw: Ureno ta: போர்த்துகல் te: పోర్చుగల్ tg: Португалия th: ประเทศโปรตุเกส tk: Portugaliýa tl: Portugal tr: Portekiz tt: Португалия ty: Pōtītī ug: Portugaliye uk: Португалія ur: پرتگال uz: Portugaliya vi: Bồ Đào Nha vo: Portugän wa: Portugal wo: Portugaal yi: פארטוגאל yo: Pọ́rtúgàl zh: 葡萄牙 zu: IPhothugali ================================================ FILE: settings/country-names/pw.yaml ================================================ name: default: Belau ar: بالاو cs: Palau de: Palau en: Palau eo: Palaŭo fr: Palaos hr: Palau ia: Palau io: Palau lt: Palau mi: Pārau "no": Palau oc: Belau pl: Palau ru: Палау se: Palau sk: Palau sl: Palau sr: Палау ta: பலாவு uk: Палау vi: Palau vo: Palauäns zh: 帕劳 / 帛琉 ================================================ FILE: settings/country-names/py.yaml ================================================ name: default: Paraguay af: Paraguay am: ፓራጓይ an: Paraguai ar: باراغواي ay: Parawayi az: Paraqvay ba: Парагвай be: Парагвай bg: Парагвай bi: Paraguay bn: প্যারাগুয়ে bo: པ་ར་གུ་ཡེ། br: Paraguay bs: Paragvaj ca: Paraguai ce: Парагвай co: Paraguay cs: Paraguay cv: Парагвай cy: Paraguay da: Paraguay de: Paraguay dv: ޕެރަގުއޭ ee: Paraguay el: Παραγουάη en: Paraguay eo: Paragvajo es: Paraguay et: Paraguay eu: Paraguai fa: پاراگوئه fi: Paraguay fo: Paraguei fr: Paraguay fy: Paraguay ga: Paragua gd: Paraguaidh gl: Paraguai gn: Paraguái gv: Yn Pharaguay he: פרגוואי hi: पैराग्वे hr: Paragvaj ht: Paragwe hu: Paraguay hy: Պարագվայ ia: Paraguay id: Paraguay ie: Paraguay io: Paraguay is: Paragvæ it: Paraguay ja: パラグアイ jv: Paraguay ka: პარაგვაი kk: Парагвай kl: Paraguay kn: ಪೆರಗ್ವೆ ko: 파라과이 ku: Paraguay kw: Paragway la: Paraquaria lb: Paraguay li: Paraguay ln: Palagway lt: Paragvajus lv: Paragvaja mk: Парагвај ml: പരഗ്വെ mn: Парагвай mr: पेराग्वे ms: Paraguay mt: Paragwaj my: ပါရာဂွေးနိုင်ငံ ne: पाराग्वे nl: Paraguay "no": Paraguay oc: Paraguai or: ପାରାଗୁଏ os: Парагвай pa: ਪੈਰਾਗੁਏ pl: Paragwaj pt: Paraguai qu: Parawayi rm: Paraguay ro: Paraguay ru: Парагвай rw: Paragwe sa: पेरेग्वाय se: Paraguay sh: Paragvaj sk: Paraguaj sl: Paragvaj so: Paraguay sq: Paraguaji sr: Парагвај su: Paraguay sv: Paraguay sw: Paraguay ta: பரகுவை tg: Парагвай th: ประเทศปารากวัย tl: Paraguay tr: Paraguay tt: Парагвай ug: پاراگۋاي uk: Парагвай ur: پیراگوئے uz: Paragvay vi: Paraguay vo: Paragvän wo: Paraguwaay yi: פאראגוויי yo: Paragúáì zh: 巴拉圭 ================================================ FILE: settings/country-names/qa.yaml ================================================ name: default: قطر af: Katar am: ቃጣር an: Qatar ar: قطر as: কাটাৰ az: Qətər be: Катар bg: Катар bn: কাতার bo: ཁ་ཏར། br: Katar bs: Katar ca: Qatar ce: Катар cs: Katar cv: Катар cy: Qatar da: Qatar de: Katar dv: ޤަޠަރު dz: ཀ་ཏར་ ee: Qatar el: Κατάρ en: Qatar eo: Kataro es: Catar et: Katar eu: Qatar fa: قطر fi: Qatar fo: Katar fr: Qatar fy: Katar ga: Catar gd: Catar gl: Qatar - قطر gu: કતાર (અરબસ્તાન) gv: Yn Chatar he: קטאר hi: क़तर hr: Katar ht: Katar hu: Katar hy: Քաթար id: Qatar ie: Katar io: Katar is: Katar it: Qatar ja: カタール jv: Qatar ka: კატარი kk: Катар kl: Qatar kn: ಕಟಾರ್ ko: 카타르 ku: Qeter kv: Катар kw: Katar la: Quataria lb: Katar li: Katar ln: Katar lt: Kataras lv: Katara mi: Katā mk: Катар ml: ഖത്തർ mn: Катар mr: कतार ms: Qatar mt: Qatar my: ကာတာနိုင်ငံ na: Qatar nl: Qatar "no": Qatar nv: Kʼatár oc: Qatar or: କତର os: Катар pa: ਕਤਰ pl: Katar ps: قطر pt: Catar qu: Qatar ro: Qatar ru: Катар rw: Katari sa: कतार se: Qatar sk: Katar (štát) sl: Katar so: Qatar sq: Katari sr: Катар ss: IKhathari su: Qatar sv: Qatar sw: Qatar ta: கத்தார் te: కతర్ tg: Қатар th: ประเทศกาตาร์ tk: Katar tl: Katar tr: Katar tt: Катар ug: قاتار uk: Катар ur: قطر uz: Qatar vi: Qatar vo: Katarän wo: Kataar yi: קאטאר yo: Katar zh: 卡塔尔 ================================================ FILE: settings/country-names/ro.yaml ================================================ name: default: România af: Roemenië am: ሮማንያ an: Rumanía ar: رومانيا ay: Rumanya az: Rumıniya ba: Румыния be: Румынія bg: Румъния bi: Romania bn: রোমানিয়া bo: རོ་མ་ནི་ཡ། br: Roumania bs: Rumunija ca: Romania ce: Румыни co: Romania cs: Rumunsko cu: Роумꙑнїꙗ cv: Румыни cy: Rwmania da: Rumænien de: Rumänien dv: ރުމޭނިއާ ee: Romania el: Ρουμανία en: Romania eo: Rumanio es: Rumanía et: Rumeenia eu: Errumania fa: رومانی fi: Romania fo: Rumenia fr: Roumanie fy: Roemeenje ga: An Rómáin gd: Romàinia gl: Romanía gn: Rrumaña gu: રોમાનિયા gv: Yn Romaan he: רומניה hi: रोमानिया hr: Rumunjska ht: Woumani hu: Románia hy: Ռումինիա ia: Romania id: Rumania ie: Rumania io: Rumania is: Rúmenía it: Romania ja: ルーマニア jv: Rumania ka: რუმინეთი kg: Romania kk: Румыния kl: Rumænia kn: ರೊಮಾನಿಯ ko: 루마니아 ku: Romanya kv: Румыния kw: Roumani ky: Румыния la: Romania lb: Rumänien li: Roemenië ln: Rumania lt: Rumunija lv: Rumānija mi: Romeinia mk: Романија ml: റൊമാനിയ mn: Румын mr: रोमेनिया ms: Romania mt: Rumanija my: ရိုမေးနီးယားနိုင်ငံ na: Romania ne: रोमानिया nl: Roemenië "no": Romania nv: Wooméiniya oc: Romania or: ରୋମାନିଆ os: Румыни pa: ਰੋਮਾਨੀਆ pl: Rumunia ps: رومانیا pt: Roménia qu: Rumanya rm: Rumenia ro: România ru: Румыния rw: Romaniya sc: Romania se: Romania sh: Rumunija si: රුමේනියාව sk: Rumunsko sl: Romunija sm: Romania so: Romania sq: Rumania sr: Румунија ss: IRomaniya su: Romania sv: Rumänien sw: Romania ta: உருமேனியா te: రొమేనియా tg: Руминия th: ประเทศโรมาเนีย tk: Rumyniýa tl: Rumanya tr: Romanya tt: Румыния ug: Ruminiye uk: Румунія ur: رومانیہ uz: Ruminiya vi: Romania vo: Rumän wa: Roumaneye wo: Romaani yi: רומעניע yo: Románíà zh: 羅馬尼亞 ================================================ FILE: settings/country-names/rs.yaml ================================================ name: default: Србија af: Serwië am: ሰርቢያ an: Serbia ar: صربيا ay: Sirwiya az: Serbiya ba: Сербия be: Сербія bg: Сърбия bi: Serbia bn: সার্বিয়া bo: སེར་བྷི་ཡ། br: Serbia bs: Srbija ca: Sèrbia ce: Серби co: Serbia cs: Srbsko cu: Срьбїꙗ cv: Серби cy: Serbia da: Serbien de: Serbien dv: ސާބިއާ ee: Serbia el: Σερβία en: Serbia eo: Serbio es: Serbia et: Serbia eu: Serbia fa: صربستان fi: Serbia fo: Serbia fr: Serbie fy: Servje ga: An tSeirbia gd: An t-Sèirb gl: Serbia gn: Sevia gv: Yn Serb he: סרביה hi: सर्बिया hr: Srbija ht: Sèbi hu: Szerbia hy: Սերբիա ia: Serbia id: Serbia ie: Serbia io: Serbia is: Serbía it: Serbia ja: セルビア jv: Serbia ka: სერბეთი kg: Serbie kk: Сербия kl: Serbia ko: 세르비아 ku: Serbistan kv: Сербия kw: Serbi ky: Сербия la: Serbia lb: Serbien li: Servië ln: Serbia lt: Serbija lv: Serbija mg: Serbia mi: Serbia mk: Србија ml: സെർബിയ mn: Серби mr: सर्बिया ms: Serbia mt: Serbja my: ဆားဘီးယားနိုင်ငံ na: Terbiya ne: सर्बिया nl: Servië "no": Serbia oc: Serbia or: ସର୍ବିଆ os: Серби pa: ਸਰਬੀਆ pl: Serbia ps: سربيا pt: Sérvia qu: Sirbya rm: Serbia ro: Serbia ru: Сербия rw: Seribiya sa: सर्बिया sc: Sèrbia se: Serbia sh: Srbija sk: Srbsko sl: Srbija sm: Serbia so: Serbia sq: Serbia sr: Србија ss: ISebhiya st: Serbia su: Sérbia sv: Serbien sw: Serbia ta: செர்பியா te: సెర్బియా tg: Сербия th: ประเทศเซอร์เบีย tk: Serbiýa tl: Serbiya tr: Sırbistan tt: Сербия tw: Serbia ty: Terepia ug: Sérbiye uk: Сербія ur: سربیا uz: Serbiya vi: Serbia vo: Särbän wo: Seerbi yi: סערביע yo: Sérbíà zh: 塞爾維亞 zu: ISerbiya ================================================ FILE: settings/country-names/ru.yaml ================================================ name: default: Россия ab: Урыстәыла af: Rusland ak: Russia am: ሩሲያ an: Rusia ar: روسيا as: ৰুশিযা av: Россиялъул Федерация ay: Rusiya az: Rusiya ba: Рәсәй be: Расія bg: Русия bi: Rusia bm: Risila bn: রাশিয়া bo: ཨུ་རུ་སུ། br: Rusia bs: Rusija ca: Rússia ce: Росси ch: Russia co: Russia cs: Rusko cu: Рѡсїꙗ cv: Раççей Патшалăхĕ cy: Rwsia da: Rusland de: Russland dv: ރޫސީވިލާތް dz: ར་ཤི་ཡཱན་ཕེ་ཌི་རེ་ཤཱན ee: Russia el: Ρωσία en: Russia eo: Rusio es: Rusia et: Venemaa eu: Errusia fa: روسیه ff: Roosiya fi: Venäjä fo: Russland fr: Russie fy: Ruslân ga: An Rúis gd: An Ruis gl: Rusia gn: Rrúsia gu: રશિયા gv: Yn Roosh ha: Rasha he: רוסיה hi: रूस hr: Ruska Federacija ht: Risi hu: Oroszország hy: Ռուսաստան ia: Russia id: Rusia ie: Russia ig: Mpaghara Russia ik: Russia io: Rusia is: Rússland it: Russia iu: ᐅᓛᓴ ja: ロシア jv: Rusia ka: რუსეთი kg: Rusia ki: Russia kk: Ресей kl: Ruslandi km: រុស្ស៊ី kn: ರಷ್ಯಾ ko: 러시아 ks: روٗس ku: Rûsya kv: Рочму kw: Russi ky: Орусия la: Russia lb: Russland lg: Rwasha li: Rusland ln: Rusí lo: ລັດເຊັຽ lt: Rusija lv: Krievija mg: Rosia mi: Ruhia mk: Русија ml: റഷ്യ mn: Оросын Холбооны Улс mo: Русия mr: रशिया ms: Rusia mt: Russja my: ရုရှားနိုင်ငံ na: Ratsiya ne: रुस nl: Rusland "no": Russland nv: Biʼééʼ Łichííʼí Bikéyah ny: Russia oc: Russia om: Raashiyaa or: ଋଷିଆ os: Уæрæсе pa: ਰੂਸ pl: Rosja ps: روسیه pt: Rússia qu: Rusiya rm: Russia rn: Uburusiya ro: Rusia ru: Россия rw: Uburusiya sc: Rùssia sd: روس se: Ruošša sg: Rusïi sh: Rusija si: රුසියාව sk: Rusko sl: Rusija sm: Lusia sn: Russia so: Ruushka sq: Rusia sr: Русија ss: IRashiya st: Russia su: Rusia sv: Ryssland sw: Shirikisho la Urusi ta: உருசியா te: రష్యా tg: Русия th: ประเทศรัสเซีย ti: ራሻ tk: Russiýa tl: Pederasyong Ruso to: Lūsia tr: Rusya Federasyonu ts: Russia tt: Русия tw: Russia ty: Rūtia ug: روسىيە uk: Росія ur: روس uz: Rossiya Federatsiyasi ve: Rashia vi: Liên bang Nga vo: Rusän wa: Federåcion d' Rûsseye wo: Federaasioŋ bu Riisi xh: IRashiya yi: רוסלאנד yo: Rọ́síà za: Ezlozswh Lienzbangh zh: 俄罗斯/俄羅斯 zu: IRashiya ================================================ FILE: settings/country-names/rw.yaml ================================================ name: default: Rwanda af: Rwanda am: ሩዋንዳ an: Ruanda ar: رواندا az: Ruanda ba: Руанда be: Руанда bg: Руанда bm: Rwanda bn: রুয়ান্ডা bo: རུ་ཝན་ཌ། br: Rwanda bs: Ruanda ca: Rwanda ce: Руанда cs: Rwanda cv: Руанда cy: Rwanda da: Rwanda de: Ruanda dv: ރުވާންޑާ ee: Rwanda el: Ρουάντα en: Rwanda eo: Ruando es: Ruanda et: Rwanda eu: Ruanda fa: رواندا fi: Ruanda fo: Ruanda fr: Rwanda fy: Rûanda ga: Ruanda gd: Rubhanda gl: Ruanda gv: Rooandey he: רואנדה hi: रवाण्डा hr: Ruanda ht: Rwanda hu: Ruanda hy: Ռուանդա ia: Ruanda id: Rwanda ie: Rwanda io: Ruanda is: Rúanda it: Ruanda ja: ルワンダ jv: Rwanda ka: რუანდა kg: Rwanda ki: Rwanda kk: Руанда kn: ರ್ವಾಂಡ ko: 르완다 ku: Rwanda kw: Rwanda la: Ruanda lb: Ruanda li: Rwanda ln: Rwanda lt: Ruanda lv: Ruanda mk: Руанда ml: റുവാണ്ട mn: Руанда mr: ऱ्वान्डा ms: Rwanda mt: Rwanda my: ရဝမ်ဒါနိုင်ငံ ne: रुवाण्डा nl: Rwanda "no": Rwanda nv: Wánda Dineʼé Bikéyah oc: Rwanda or: ରଵାଣ୍ଡା os: Руандæ pa: ਰਵਾਂਡਾ pl: Rwanda ps: راونډا pt: Ruanda qu: Rwanda rn: Rwanda ro: Rwanda ru: Руанда rw: Rwanda sa: रवाण्डा sc: Ruanda se: Rwanda sg: Ruandäa sh: Ruanda si: රුවන්ඩා ජනරජය sk: Rwanda sl: Ruanda sn: Rwanda so: Ruwanda sq: Ruanda sr: Руанда ss: IRuwanda su: Rwanda sv: Rwanda sw: Rwanda ta: ருவாண்டா te: రువాండా tg: Руанда th: ประเทศรวันดา ti: ሩዋንዳ tk: Ruanda tl: Ruwanda tr: Ruanda ts: Rwanda tt: Руанда ug: رۋاندا uk: Руанда ur: روانڈا uz: Ruanda vi: Rwanda vo: Ruandän wo: Ruwandaa yi: רוואנדע yo: Rùwándà zh: 卢旺达 zu: IRuwanda ================================================ FILE: settings/country-names/sa.yaml ================================================ name: default: السعودية af: Saoedi-Arabië am: ሳዑዲ አረቢያ an: Arabia Saudí ar: السعودية av: СагIудиязул ГIарабия az: Səudiyyə Ərəbistanı ba: Сәғүд Ғәрәбстаны be: Саудаўская Аравія bg: Саудитска Арабия bm: Saudia Arabu ka Faamamara bn: সৌদি আরব bo: སའུ་དི་ཨ་ར་པི་ཡ། br: Arabia Saoudat bs: Saudijska Arabija ca: Aràbia Saudita ce: СаӀудийн Ӏаьрбийчоь cs: Saúdská Arábie cv: Сауд Аравийĕ cy: Saudi Arabia da: Saudi-Arabien de: Saudi-Arabien dv: ސައޫދީ އަރަބިއްޔާ dz: སའུ་དི་ཨེ་ར་སྦི་ཡ་ el: Σαουδική Αραβία en: Saudi Arabia eo: Sauda Arabio es: Arabia Saudita et: Saudi Araabia eu: Saudi Arabia fa: عربستان سعودی fi: Saudi-Arabia fo: Saudi-Arabia fr: Arabie saoudite fy: Saûdy-Araabje ga: An Araib Shádach gd: Aràibia nan Sabhd gl: Arabia Saudí gn: Aravia Saudíta gu: સાઉદી અરેબિયા gv: Yn Araab Saudi he: ערב הסעודית hi: सउदी अरब hr: Saudijska Arabija ht: Arabi Sawoudit hu: Szaúd-Arábia hy: Սաուդյան Արաբիա ia: Arabia Saudita id: Arab Saudi ie: Saudi Arabia io: Saudia Arabia is: Sádí-Arabía it: Arabia Saudita ja: サウジアラビア jv: Arab Saudi ka: საუდის არაბეთი kk: Сауд Арабиясы kl: Saudi Arabia km: អារ៉ាប៊ីសាអូឌីត kn: ಸೌದಿ ಅರೆಬಿಯ ko: 사우디아라비아 ku: Erebistana Siyûdî kv: Саудса Аравия kw: Arabi Saoudek ky: Сауд Арабия Падышалыгы la: Arabia Saudiana lb: Saudi-Arabien li: Saoedi-Arabië ln: Saudi Arabia lt: Saudo Arabija lv: Saūda Arābija mk: Саудиска Арабија ml: സൗദി അറേബ്യ mn: Саудын Араб mr: सौदी अरेबिया ms: Arab Saudi mt: Arabja Sawdija my: ဆော်ဒီအာရေဗျနိုင်ငံ na: Taudiarabiya ne: साउदी अरब nl: Saoedi-Arabië "no": Saudi-Arabia nv: Ásáí Bikéyah Saʼoodí oc: Arabia Saudita or: ସାଉଦି ଆରବ os: Сауды Арави pa: ਸਉਦੀ ਅਰਬ pl: Arabia Saudyjska ps: سعودي عربستان pt: Arábia Saudita qu: Sawud Arabya rm: Arabia Saudita ro: Arabia Saudită ru: Саудовская Аравия rw: Arabiya Sawudite sa: सऊदी अरब se: Saudi-Arábia sh: Saudijska Arabija si: සවුදි අරාබියාව sk: Saudská Arábia sl: Saudova Arabija so: Sacuudi Carabiya sq: Arabia Saudite sr: Саудијска Арабија ss: I-Arabhiya su: Saudi Arabia sv: Saudiarabien sw: Saudia ta: சவூதி அரேபியா te: సౌదీ అరేబియా tg: Арабистони Саудӣ th: ประเทศซาอุดีอาระเบีย tk: Saud Arabystany tl: Arabyang Saudi tr: Suudi Arabistan tt: Согуд Гарәбстаны ug: سەئۇدى ئەرەبىستان uk: Саудівська Аравія ur: سعودی عرب uz: Saudiya Arabistoni vi: Ả Rập Saudi vo: Sauda-Larabän wo: Araabi Sawdit yi: סאודי אראביע yo: Sáúdí Arábíà zh: 沙特阿拉伯 ================================================ FILE: settings/country-names/sb.yaml ================================================ name: default: Solomon Islands af: Solomoneilande am: ሰለሞን ደሴቶች an: Islas Salomón ar: جزر سليمان az: Solomon adaları be: Саламонавы Астравы bg: Соломонови острови bn: সলোমন দ্বীপপুঞ্জ bo: སོ་ལོ་མོན་གླིང་ཕྲན་ཚོ་ཁག br: Inizi Solomon bs: Solomonska Ostrva ca: Salomó ce: Соломонан гӀайреш cs: Šalomounovy ostrovy cv: Соломон Утравĕсем cy: Ynysoedd Solomon da: Salomonøerne de: Salomonen dv: ސޮލޮމޮން ޖަޒީރާ el: Νήσοι Σολομώντα en: Solomon Islands eo: Salomonoj es: Islas Salomón et: Saalomoni Saared eu: Salomon Uharteak fa: جزایر سلیمان fi: Salomonsaaret fo: Sálomonoyggjarnar fr: Salomon fy: Salomonseilannen ga: Oileáin Sholamón gd: Na h-Eileanan Sholaimh gl: Illas Salomón gv: Ny h-Ellanyn Holomon he: איי שלמה hi: सोलोमन द्वीपसमूह hr: Solomonski Otoci ht: Salomon hu: Salamon-szigetek hy: Սողոմոնյան Կղզիներ id: Kepulauan Solomon io: Salomon Insuli is: Salómonseyjar it: Isole Salomone ja: ソロモン諸島 jv: Kapuloan Solomon ka: სოლომონის კუნძულები kk: Соломон Аралдары kn: ಸಾಲೊಮನ್ ದ್ವೀಪಗಳು ko: 솔로몬 제도 ku: Giravên Salomon kw: Ynysow Salamon la: Insulae Salomonis lb: Salomonen li: Salomonseilen lt: Saliamono Salos lv: Zālamana Salas mk: Соломонски Острови ml: സോളമൻ ദ്വീപുകൾ mr: सॉलोमन द्वीपसमूह ms: Kepulauan Solomon mt: Gżejjer Solomon my: ဆော်လမွန်အိုင်းလန်းနိုင်ငံ nb: Salomonøyene ne: सोलोमन द्धीप nl: Salomonseilanden nn: Salomonøyane "no": Salomonøyene nv: Sólomon Tó Bináhaazyínígíí oc: Illas Salamon or: ସୋଲୋମନ ଆୟରଲ୍ୟାଣ୍ଡ os: Соломоны сакъадæхтæ pa: ਸੋਲੋਮਨ ਟਾਪੂ pl: Wyspy Salomona pt: Ilhas Salomão qu: Salumun wat'akuna ro: Insulele Solomon ru: Соломоновы Острова rw: Ibirwa bya Solomoni sa: सोलोमन-द्वीप se: Salomonsullot sh: Solomonski Otoci si: සොලමන් දුපත් sk: Šalamúnove ostrovy sl: Salomonovi otoki sm: Solomon Islands so: Jasiiradaha Solomon sq: Ishujt Solomon sr: Соломонска Острва su: Kapuloan Solomon sv: Salomonöarna sw: Visiwa vya Solomon ta: சொலமன் தீவுகள் te: సోలమన్ దీవులు tg: Ҷазираҳои Соломон th: หมู่เกาะโซโลมอน tl: Kapuluang Solomon tr: Solomon Adaları ug: سولومون تاقىم ئاراللىرى uk: Соломонові Острови ur: جزائر سلیمان uz: Solomon Orollari vi: Quần đảo Solomon vo: Solomonuäns wo: Duni Solomon yo: Àwọn Erékùsù Sólómọ́nì zh: 所罗门群岛 ================================================ FILE: settings/country-names/sc.yaml ================================================ name: default: Sesel af: Seychelle am: ሲሸልስ an: Seychelles ar: سيشيل az: Seyşel adaları be: Сейшэльскія Астравы bg: Сейшели bm: Seshel bn: সেশেল bo: སེ་ཞེལ། br: Sechelez bs: Sejšeli ca: Seychelles ce: Сейшелан гlайреш cs: Seychely cv: Сейшел утравĕсем cy: Seychelles da: Seychellerne de: Seychellen dv: ތޭންގަދީބު el: Σεϋχέλλες en: Seychelles eo: Sejŝeloj es: Seychelles et: Seišellid eu: Seychelleak fa: سیشل fi: Seychellit fo: Seyskelloyggjar fr: Seychelles fy: Seysjellen ga: Na Séiséil gd: Na h-Eileanan Sheiseall gl: Seixeles - Seychelles gv: Ny h-Ellanyn Heshell he: סיישל hi: सेशेल्स hr: Sejšeli ht: Sechèl hu: Seychelle-szigetek ia: Seychelles id: Seychelles ie: Seychelles io: Seycheli is: Seychelles-eyjar it: Seychelles ja: セーシェル jv: Seychelles ka: სეიშელის კუნძულები kg: Seychell kk: Сейшель Аралдары kn: ಸೆಶೆಲ್ಸ್ ko: 세이셸 ku: Seyşel kw: Seychellys la: Insulae Seisellenses lb: Seychellen li: Seychelle ln: Seyshel lt: Seišeliai lv: Seišelas mg: Seychelles mk: Сејшели ml: സെയ്‌ഷെൽസ് mn: Сейшелийн арлууд mr: सेशेल्स ms: Seychelles mt: Seychelles my: ဆေးရှဲနိုင်ငံ nb: Seychellene nl: Seychellen nn: Seychellane "no": Seychellene nv: Seishel oc: Seichèlas or: ସେସେଲଜ os: Сейшелтæ pa: ਸੇਸ਼ੈਲ pl: Seszele ps: سیشل pt: Seychelles qu: Sichillakuna rm: Seychellas ro: Seychelles ru: Сейшелы rw: Seyishele sa: सेशेल sc: Seychelles se: Seychellat sg: Sëyshêle sk: Seychely sl: Sejšeli sn: Seychelles so: Seyshelles sq: Sejshellet sr: Сејшели ss: ISeyishelesi su: Seychéll sv: Seychellerna sw: Shelisheli ta: சீசெல்சு tg: Ҷазираҳои Сейшел th: ประเทศเซเชลส์ tk: Seýşel Adalary tl: Seyshels tr: Seyşeller ts: Seychelles tt: Сейшел утраулары ug: سېيشېل ئاراللىرى uk: Сейшельські Острови ur: سیچیلیس uz: Seyshell orollari vi: Seychelles vo: Säceluäns wo: Seysel yo: Ṣèíhẹ́lẹ́sì zh: 塞舌尔 zu: IsiSeyisheli ================================================ FILE: settings/country-names/sd.yaml ================================================ name: default: السودان af: Soedan ak: Sudan am: ሱዳን an: Sudán ar: السودان az: Sudan ba: Судан be: Судан bg: Судан bm: Sudan bn: সুদান bo: སུའུ་ཏན། br: Soudan bs: Sudan ca: Sudan ce: Судан cs: Súdán cv: Судан cy: Sudan da: Sudan de: Sudan dv: ސޫދާން ee: Sudan el: Σουδάν en: Sudan eo: Sudano es: Sudán et: Sudaan eu: Sudan fa: سودان fi: Sudan fo: Sudan fr: Soudan fy: Sûdan ga: An tSúdáin gd: Sudan gl: Sudán gn: Sudano gu: સુદાન gv: Yn Toodaan ha: Sudan he: סודאן hi: सूडान hr: Sudan ht: Soudan hu: Szudán hy: Սուդան ia: Sudan id: Sudan ie: Sudan ig: Sudan io: Sudan is: Súdan it: Sudan ja: スーダン jv: Sudan ka: სუდანი kg: Sudan ki: Sũdana kk: Судан kn: ಸುಡಾನ್ ko: 수단 ku: Sûdan kw: Soudan ky: Судан la: Sudania lb: Sudan li: Soedan ln: Sudani lt: Sudanas lv: Sudāna mi: Hūtāne mk: Судан ml: സുഡാൻ mn: Судан mr: सुदान ms: Sudan mt: Sudan my: ဆူဒန်နိုင်ငံ na: Tudan nl: Soedan "no": Sudan nv: Soodą́ą ny: Sudan oc: Sodan om: Sudaan or: ସୁଦାନ os: Судан pa: ਸੁਡਾਨ pl: Sudan ps: سوډان pt: Sudão qu: Sudan rm: Sudan ro: Sudan ru: Судан rw: Sudani sa: सूडान sc: Sudan sd: سوڊان se: Sudan sg: Sudäan sh: Sudan sk: Sudán sl: Sudan sm: Sudan sn: Sudan so: Suudaan sq: Sudani sr: Судан ss: ISudani su: Sudan sv: Sudan sw: Sudan ta: சூடான் te: సూడాన్ tg: Судон th: ประเทศซูดาน ti: ሱዳን tk: Sudan tl: Sudan tr: Sudan ts: Sudan tt: Судан ug: سۇدان uk: Судан ur: سوڈان uz: Sudan vi: Sudan vo: Sudän wo: Sudaan yi: סודאן yo: Sudan zh: 苏丹共和国 ================================================ FILE: settings/country-names/se.yaml ================================================ name: default: Sverige ab: Швециа af: Swede ak: Sweden am: ስዊድን an: Suecia ar: السويد az: İsveç ba: Швеция be: Швецыя bg: Швеция bi: Sweden bm: Swedi bn: সুইডেন bo: སི་ཝེ་དེན། br: Sveden bs: Švedska ca: Suècia ce: Швеци co: Svezia cs: Švédsko cu: Свєньско cv: Швеци cy: Sweden da: Sverige de: Schweden dv: ސުވިޑަން dz: སུའི་ཌན་ ee: Sweden el: Σουηδία en: Sweden eo: Svedio es: Suecia et: Rootsi eu: Suedia fa: سوئد fi: Ruotsi fo: Svøríki fr: Suède fy: Sweden ga: An tSualainn gd: An t-Suain gl: Suecia gn: Suesia gu: સ્વિડન gv: Yn Toolynn he: שבדיה hi: स्वीडन hr: Švedska ht: Syèd hu: Svédország hy: Շվեդիա ia: Svedia id: Swedia ie: Svedia io: Suedia is: Svíþjóð it: Svezia iu: ᔅᕗᕆᑭ ja: スウェーデン jv: Swédia ka: შვედეთი kg: Suedi ki: Sweden kk: Швеция kl: Svenskit Nunaat km: ស៊ុយអែត kn: ಸ್ವೀಡನ್ ko: 스웨덴 ku: Swêd kv: Швеция kw: Swedherwyk ky: Швеция la: Suecia lb: Schweden lg: Swiiden li: Zwede ln: Swédi lt: Švedija lv: Zviedrija mg: Soeda mi: Huītene mk: Шведска ml: സ്വീഡൻ mn: Швед mr: स्वीडन ms: Sweden mt: Żvezja my: ဆွီဒင်နိုင်ငံ na: Widen ne: स्वीडेन nl: Zweden "no": Sverige oc: Suècia or: ସ୍ଵିଡେନ os: Швеци pa: ਸਵੀਡਨ pl: Szwecja ps: سويډن pt: Suécia qu: Suwidsuyu rm: Svezia ro: Suedia ru: Швеция rw: Suwede sa: स्वीडन sc: Isvetzia se: Ruoŧŧa sh: Švedska sk: Švédsko sl: Švedska sm: Sweden so: Iswiidhan sq: Suedia sr: Шведска ss: ISwideni su: Swédia sv: Sverige sw: Uswidi ta: சுவீடன் te: స్వీడన్ tg: Шветсия th: ประเทศสวีเดน tk: Şwesiýa tl: Suwesya tr: İsveç tt: Швеция tw: Sweden ug: شۋېتسىيە uk: Швеція ur: سویڈن uz: Shvetsiya vi: Thụy Điển vo: Svedän wa: Suwedwesse wo: Suweed yi: שוועדן yo: Swídìn zh: 瑞典 zu: ISwidi ================================================ FILE: settings/country-names/sg.yaml ================================================ name: default: Singapore af: Singapoer am: ሲንጋፖር an: Singapur ar: سنغافورة az: Sinqapur ba: Сингапур be: Сінгапур bg: Сингапур bi: Singapore bn: সিঙ্গাপুর bo: སེང་ག་ཕོར། br: Singapour bs: Singapur ca: Singapur ce: Сингапур cs: Singapur cv: Сингапур cy: Singapore da: Singapore de: Singapur dv: ސިންގަޕޫރު el: Σιγκαπούρη en: Singapore eo: Singapuro es: Singapur et: Singapur eu: Singapur fa: سنگاپور ff: Sinngapuur fi: Singapore fo: Singapor fr: Singapour fy: Singapoer ga: Singeapór gd: Singeapòr gl: Singapur gu: સિંગાપુર gv: Singapore ha: Singapore he: סינגפור hi: सिंगापुर hr: Singapur ht: Sengapou hu: Szingapúr hy: Սինգապուրի Հանրապետություն ia: Singapur id: Singapura ie: Singapor io: Singapur is: Singapúr it: Singapore ja: シンガポール jv: Singapura ka: სინგაპური ki: Singapore kk: Сингапур kl: Singapore km: សិង្ហបុរី kn: ಸಿಂಗಾಪುರ್ ko: 싱가포르 ku: Singapûr kv: Сингапур kw: Singapour ky: Сингапур la: Singapura lb: Singapur li: Singapore ln: Singapur lt: Singapūras lv: Singapūra mg: Singapaoro mi: Hingapoa mk: Сингапур ml: സിംഗപ്പൂർ mn: Сингапур mr: सिंगापूर ms: Singapura mt: Singapor my: စင်ကာပူနိုင်ငံ na: Tsingapoar ne: सिंगापुर nl: Singapore "no": Singapore nv: Sį́ʼgaboo oc: Singapor or: ସିଙ୍ଗାପୁର os: Сингапур pa: ਸਿੰਘਾਪੁਰ pl: Singapur ps: سينګاپور pt: Singapura qu: Singapur ro: Singapore ru: Сингапур rw: Singapore sa: सिङ्गापुरम् se: Singapore sg: Sïngäpûru sh: Singapur sk: Singapur sl: Singapur sm: Sigapoa so: Singabuur sq: Singapori sr: Сингапур ss: ISingapholo su: Singapura sv: Singapore sw: Singapuri ta: சிங்கப்பூர் te: సింగపూరు tg: Сингапур th: ประเทศสิงคโปร์ tk: Singapur tl: Singapore tr: Singapur tt: Сингапур ug: سىنگاپور uk: Сінгапур ur: سنگاپور uz: Singapur vi: Singapore vo: Singapurän wo: Singapoor yi: סינגאפור yo: Singapore zh: 新加坡 ================================================ FILE: settings/country-names/sh.yaml ================================================ name: default: Saint Helena, Ascension and Tristan da Cunha ar: سانت هيلينا، أسينسيون وتريستان دا كونها cs: Svatá Helena, Ascension a Tristan da Cunha de: St. Helena, Ascension und Tristan da Cunha el: Αγία Ελένη, Ασενσιόν και Τριστάν ντα Κούνια en: Saint Helena, Ascension and Tristan da Cunha eo: Sankta Heleno, Ascension kaj Tristan da Cunha es: Santa Elena, Ascensión y Tristán de Acuña fa: سنت هلنا، اسنشن و تریستان دا کونا fr: Sainte-Hélène, Ascension et Tristan da Cunha hr: Sveta Helena hu: Szent Ilona, Ascension és Tristan da Cunha it: Sant'Elena, Ascensione e Tristan da Cunha lt: Šventoji Elena, Dangun Žengimo ir Tristanas da Kunja mk: Света Елена, Успение и Тристан да Куња nl: Sint-Helena, Ascension en Tristan da Cunha "no": Sankt Helena, Ascension og Tristan da Cunha pt: Santa Helena, Ascensão e Tristão da Cunha ru: Острова Святой Елены, Вознесения и Тристан-да-Кунья sk: Svätá Helena, Ascension a Tristan da Cunha sr: Света Хелена, Асенсион, и Тристан да Куња tr: Saint Helena, Ascension ve Tristan da Cunha uk: Острови Святої Єлени, Вознесіння і Тристан-да-Кунья vi: Saint Helena, Ascension và Tristan da Cunha zh: 圣赫勒拿、阿森松和特里斯坦-达库尼亚 ================================================ FILE: settings/country-names/si.yaml ================================================ name: default: Slovenija af: Slowenië am: ስሎቬኒያ an: Eslovenia ar: سلوفينيا az: Sloveniya ba: Словения be: Славенія bg: Словения bi: Slovenia bn: স্লোভেনিয়া bo: སི་ལོ་ཝེ་ནི་ཡ། br: Slovenia bs: Slovenija ca: Eslovènia ce: Словени co: Sluvenia cs: Slovinsko cu: Їллѷрїиска Словѣнїꙗ cv: Словени cy: Slofenia da: Slovenien de: Slowenien dv: ސުލޮވީނިއާ ee: Slovenia el: Σλοβενία en: Slovenia eo: Slovenio es: Eslovenia et: Sloveenia eu: Eslovenia fa: اسلوونی fi: Slovenia fo: Slovenia fr: Slovénie fy: Sloveenje ga: An tSlóivéin gd: Sloibhìnia gl: Eslovenia gn: Eloveña gu: સ્લોવેનિયા gv: Yn Clovean he: סלובניה hi: स्लोवेनिया hr: Slovenija ht: Sloveni hu: Szlovénia hy: Սլովենիա ia: Slovenia id: Slovenia ie: Slovenia io: Slovenia is: Slóvenía it: Slovenia ja: スロベニア jv: Slovénia ka: სლოვენია kg: Slovenia kk: Словения kl: Slovenia ko: 슬로베니아 ku: Slovenya kv: Словения kw: Sloveni la: Slovenia lb: Slowenien li: Slovenië ln: Slovenia lt: Slovėnija lv: Slovēnija mi: Horowinia mk: Словенија ml: സ്ലൊവീന്യ mn: Словен mr: स्लोव्हेनिया ms: Slovenia mt: Slovenja my: ဆလိုဗေးနီးယားနိုင်ငံ na: Tsirobeniya ne: स्लोभेनिया nl: Slovenië "no": Slovenia nv: Słobíín Bikéyah oc: Eslovènia or: ସ୍ଲୋଭେନିଆ os: Словени pl: Słowenia ps: سلووانیا pt: Eslovénia qu: Isluwinya rm: Slovenia ro: Slovenia ru: Словения rw: Siloveniya sa: स्लोवीनिया sc: Islovènia se: Slovenia sh: Slovenija sk: Slovinsko sl: Slovenija sm: Slovenia so: Isloveeniya sq: Sllovenia sr: Словенија ss: Siloveni st: Slovenia su: Slovénia sv: Slovenien sw: Slovenia ta: சுலோவீனியா te: స్లోవేనియా tg: Словения th: ประเทศสโลวีเนีย tk: Sloweniýa tl: Eslobenya tr: Slovenya tt: Словения ug: Slowéniye uk: Словенія ur: سلووینیا uz: Sloveniya vi: Slovenia vo: Sloveniyän wo: Esloweeni yi: סלאוועניע yo: Sloféníà zh: 斯洛文尼亞 ================================================ FILE: settings/country-names/sk.yaml ================================================ name: default: Slovensko af: Slowakye am: ስሎቫኪያ an: Eslovaquia ar: سلوفاكيا az: Slovakiya ba: Словакия be: Славакія bg: Словакия bi: Slovakia bn: স্লোভাকিয়া bo: སི་ལོ་ཕ་ཁེ། br: Slovakia bs: Slovačka ca: Eslovàquia ce: Словаки co: Sluvacchia cs: Slovensko cu: Словѣньско cv: Словаки cy: Slofacia da: Slovakiet de: Slowakei dv: ސުލޮވާކިއާ ee: Slovakia el: Σλοβακία en: Slovakia eo: Slovakio es: Eslovaquia et: Slovakkia eu: Eslovakia fa: اسلواکی fi: Slovakia fo: Slovakia fr: Slovaquie fy: Slowakije ga: An tSlóvaic gd: An t-Slòbhac gl: Eslovaquia gn: Elovakia gu: સ્લોવાકિયા gv: Yn Clovack he: סלובקיה hi: स्लोवाकिया hr: Slovačka ht: Slovaki hu: Szlovákia hy: Սլովակիա ia: Slovachia id: Slowakia ie: Slovacia io: Slovakia is: Slóvakía it: Slovacchia ja: スロバキア jv: Slowakia ka: სლოვაკეთი kg: Slovakia kk: Словакия kl: Slovakia ko: 슬로바키아 ku: Slovakya kv: Словакия kw: Slovaki ky: Словакия la: Slovacia lb: Slowakei li: Slowakieë ln: Slovakia lt: Slovakija lv: Slovākija mi: Horowākia mk: Словачка ml: സ്ലോവാക്യ mn: Словак mr: स्लोव्हाकिया ms: Slovakia mt: Slovakkja my: ဆလိုဗားကီးယားနိုင်ငံ na: Slowakia ne: स्लोभाकिया nl: Slowakije "no": Slovakia nv: Słóbaʼ Bikéyah oc: Eslovaquia or: ସ୍ଲୋଭାକିଆ os: Словаки pl: Słowacja ps: سلواکيا pt: Eslováquia qu: Isluwakya rm: Slovachia ro: Slovacia ru: Словакия rw: Silovakiya sa: स्लोवाकिया se: Slovakia sh: Slovačka sk: Slovensko sl: Slovaška so: Islofaakiya sq: Sllovakia sr: Словачка ss: Silovakhi st: Slovakia su: Slovakia sv: Slovakien sw: Slovakia ta: சிலோவாக்கியா tg: Словакия th: ประเทศสโลวาเกีย tk: Slowakiýa tl: Eslobakya tr: Slovakya tt: Словакия ug: سلوۋاكىيە uk: Словаччина ur: سلوواکیہ uz: Slovakiya vi: Slovakia vo: Slovakiyän wo: Eslowaaki yi: סלאוואקיי yo: Slofákíà zh: 斯洛伐克 zu: ISlovaki ================================================ FILE: settings/country-names/sl.yaml ================================================ name: default: Sierra Leone af: Sierra Leone ak: Sierra Leone am: ሴየራ ሌዎን an: Sierra Leone ar: سيراليون az: Syerra-Leone ba: Сьерра-Леоне be: Сьера-Леонэ bg: Сиера Леоне bm: Sera Leon bn: সিয়েরা লিওন bo: སི་ར་ལེ་ཨོན། br: Sierra Leone bs: Sijera Leone ca: Sierra Leone ce: Сьерра-Леоне cs: Sierra Leone cy: Sierra Leone da: Sierra Leone de: Sierra Leone dv: ސެރެލިއޯން ee: Sierra Leone el: Σιέρα Λεόνε en: Sierra Leone eo: Siera-Leono es: Sierra Leona et: Sierra Leone eu: Sierra Leona fa: سیرالئون ff: Sarliyon fi: Sierra Leone fo: Sierra Leona fr: Sierra Leone fy: Sierra Leöane ga: Siarra Leon gd: Siarra Leòmhann gl: Serra Leoa gv: Sierra Leone he: סיירה לאונה hi: सिएरा लियोन hr: Sijera Leone ht: Syera Leòn hu: Sierra Leone hy: Սիերա Լեոնե id: Sierra Leone ie: Sierra Leone io: Sierra Leone is: Síerra Leóne it: Sierra Leone ja: シエラレオネ jv: Sierra Leone ka: სიერა-ლეონე kg: Siera Leone kk: Сиерра-Леоне kn: ಸಿಯೆರ್ರಾ ಲಿಯೋನ್ ko: 시에라리온 ku: Sierra Leone kw: Sierra Leon la: Mons Leoninus lb: Sierra Leone li: Sierra Leone ln: Sierra Leone lt: Siera Leonė lv: Sjerraleone mi: Te Araone mk: Сиера Леоне ml: സീറാ ലിയോൺ mn: Сьерра-Леоне mr: सियेरा लिओन ms: Sierra Leone mt: Sierra Leone my: ဆီရာလီယွန်နိုင်ငံ nl: Sierra Leone "no": Sierra Leone nv: Náshdóítsoh Bitsiijįʼ Daditłʼooʼígíí Bidził oc: Sierra Leone or: ସିଏରା ଲିଓନ os: Сьеррæ-Леоне pa: ਸਿਏਰਾ ਲਿਓਨ pl: Sierra Leone ps: سېرالیون pt: Serra Leoa qu: Liyun Urqu ro: Sierra Leone ru: Сьерра-Леоне rw: Siyera Lewone sa: सियारा-लियोन sc: Sierra Leone se: Sierra Leone sg: Sierä-Leône sh: Sijera Leone si: සියෙරා ලියෝන් sk: Sierra Leone sl: Sierra Leone sn: Sierra Leone so: Sierra Leone sq: Siera Leone sr: Сијера Леоне ss: ISiriya-Liyoni su: Sierra Leone sv: Sierra Leone sw: Sierra Leone ta: சியேரா லியோனி tg: Сиерра Леоне th: ประเทศเซียร์ราลีโอน tk: Sýerra-Leone tl: Bulubunduking Leona tr: Sierra Leone ts: Siyera Liyona tw: Sierra Leone ug: سيېررا لېئونې uk: Сьєрра-Леоне ur: سیرالیون uz: Syerra Leone vi: Sierra Leone vo: Sieraleonän wo: Siraa Leyoon yi: סיערע לעאנע yo: Siẹrra Léònè zh: 塞拉利昂 zu: ISiera Liyoni ================================================ FILE: settings/country-names/sm.yaml ================================================ name: default: San Marino ar: سان مارينو be: Сан-Марына bg: Сан Марино br: San Marino cs: San Marino da: San Marino de: San Marino el: Άγιος Μαρίνος en: San Marino eo: San-Marino fa: سان مارینو fi: San Marino fr: Saint-Marin fy: San Marino ga: San Mairíne he: סן מרינו hr: San Marino hu: San Marino ia: San Marino io: San Marino is: San Marínó it: San Marino la: Sancti Marini lt: San Marinas mi: Hato Marino mn: Сан-Марино nl: San Marino "no": San Marino pl: San Marino pt: San Marino ru: Сан-Марино se: San Marino sk: San Maríno sv: San Marino ta: சான் மரீனோ th: ประเทศซานมารีโน uk: Сан-Маріно vo: Sanmarinän zh: 圣马力诺 ================================================ FILE: settings/country-names/sn.yaml ================================================ name: default: Sénégal af: Senegal am: ሴኔጋል an: Senegal ar: السنغال az: Seneqal ba: Сенегал be: Сенегал bg: Сенегал bm: Senegal bn: সেনেগাল bo: སི་ནི་གཱལ། br: Senegal bs: Senegal ca: Senegal ce: Сенегал cs: Senegal cv: Сенегал cy: Sénégal da: Senegal de: Senegal dv: ސެނެގާލް ee: Senegal el: Σενεγάλη en: Senegal eo: Senegalo es: Senegal et: Senegal eu: Senegal fa: سنگال ff: Senegaal fi: Senegal fo: Senegal fr: Sénégal fy: Senegal ga: An tSeineagáil gd: Seanagal gl: Senegal gv: Yn Tenegaal ha: Senegal he: סנגל hi: सेनेगल hr: Senegal ht: Senegal hu: Szenegál hy: Սենեգալ ia: Senegal id: Senegal ie: Senegal io: Senegal is: Senegal it: Senegal ja: セネガル jv: Senegal ka: სენეგალი kg: Senangale kk: Сенегал kn: ಸೆನೆಗಲ್ ko: 세네갈 ku: Senegal kw: Senegal ky: Сенегал la: Senegalia lb: Senegal li: Senegal ln: Senegale lt: Senegalas lv: Senegāla mi: Henekara mk: Сенегал ml: സെനെഗൽ mn: Сенегал mr: सेनेगाल ms: Senegal mt: Senegal my: ဆီနီဂေါနိုင်ငံ na: Senegal nl: Senegal "no": Senegal oc: Senegal or: ସେନେଗାଲ os: Сенегал pa: ਸੇਨੇਗਲ pl: Senegal ps: سېنېګال pt: Senegal qu: Sinigal ro: Senegal ru: Сенегал rw: Senegali sa: सेनेगल sc: Senegàl sd: سينيگال se: Senegal sg: Senegäle sh: Senegal sk: Senegal sl: Senegal sn: Senegal so: Sinigaal sq: Senegali sr: Сенегал st: Senegal su: Sénégal sv: Senegal sw: Senegal ta: செனிகல் te: సెనెగల్ tg: Сенегал th: ประเทศเซเนกัล ti: ሴኔጋል tk: Senegal tl: Senegal tr: Senegal ts: Senegal tt: Сенегал ug: سېنېگال uk: Сенегал ur: سینیگال uz: Senegal vi: Sénégal vo: Senegalän wa: Senegal wo: Senegaal yi: סענעגאל yo: Sẹ̀nẹ̀gàl zh: 塞内加尔 zu: ISenegal ================================================ FILE: settings/country-names/so.yaml ================================================ name: default: Soomaaliya الصومال af: Somalië am: ሶማሊያ an: Somalia ar: الصومال az: Somali ba: Сомали be: Самалі bg: Сомалия bn: সোমালিয়া bo: སོ་མ་ལི། br: Somalia bs: Somalija ca: Somàlia ce: Сомали cs: Somálsko cy: Somalia da: Somalia de: Somalia dv: ސޯމާލިއާ ee: Somalia el: Σομαλία en: Somalia eo: Somalio es: Somalia et: Somaalia eu: Somalia fa: سومالی fi: Somalia fo: Somalia fr: Somalie fy: Somaalje ga: An tSomáil gd: Somàilia gl: Somalia gv: Yn Tomaal he: סומליה hi: सोमालिया hr: Somalija ht: Somali hu: Szomália hy: Սոմալի ia: Somalia id: Somalia ie: Somalia io: Somalia is: Sómalía it: Somalia ja: ソマリア jv: Somalia ka: სომალი kg: Sômâlia ki: Somalia kk: Сомали kn: ಸೊಮಾಲಿಯ ko: 소말리아 ku: Somalya kw: Somali la: Somalia lb: Somalia li: Somalië ln: Somalia lt: Somalis lv: Somālija mg: Somalia mi: Hūmārie mk: Сомалија ml: സൊമാലിയ mn: Сомали mr: सोमालिया प्रजासत्ताक ms: Somalia mt: Somalja my: ဆိုမာလီယာနိုင်ငံ na: Tomariya nl: Somalië "no": Somalia nv: Soomáálii Bikéyah oc: Somalia om: Somaaliyaa or: ସୋମାଲିଆ os: Сомали pa: ਸੋਮਾਲੀਆ pl: Somalia ps: سومالیا pt: Somália qu: Sumalya rm: Somalia ro: Somalia ru: Сомали rw: Somaliya sa: सोमालिया sc: Somàlia se: Somália sg: Somalïi sh: Somalija si: සෝමාලියාව sk: Somálsko sl: Somalija sm: Somalia sn: Somalia so: Soomaaliya sq: Somalia sr: Сомалија ss: ISomaliya st: Somalia su: Somalia sv: Somalia sw: Somalia ta: சோமாலியா te: సొమాలియా tg: Сумолӣ th: ประเทศโซมาเลีย ti: ሶማሊያ tk: Somali tl: Somalya tr: Somali ts: Somalia tt: Сомали ug: سومالى uk: Сомалі ur: صومالیہ uz: Somali vi: Somalia vo: Somalän wo: Somali yi: סאמאליע yo: Sòmálíà zh: 索马里 zu: ISomalia ================================================ FILE: settings/country-names/sr.yaml ================================================ name: default: Suriname af: Suriname am: ሱሪናም an: Surinam ar: سورينام ay: Suriname az: Surinam ba: Суринам be: Сурынам bg: Суринам bn: সুরিনাম bo: སུ་རི་ནེམ། br: Surinam bs: Surinam ca: Surinam ce: Суринам cs: Surinam cv: Суринам cy: Suriname da: Surinam de: Suriname dv: ސުރިނާމް ee: Suriname el: Σουρινάμ en: Suriname eo: Surinamo es: Surinam et: Suriname eu: Surinam fa: سورینام fi: Suriname fo: Surinam fr: Suriname fy: Suriname ga: Suranam gd: Suranam gl: Suriname gn: Surinam gu: સુરીનામ gv: Yn Toorinam he: סורינאם hi: सूरीनाम hr: Surinam ht: Sirinam hu: Suriname hy: Սուրինամ id: Suriname ie: Surinam io: Surinam is: Súrínam it: Suriname ja: スリナム jv: Suriname ka: სურინამი kk: Суринам kl: Suriname kn: ಸುರಿನಾಮ್ ko: 수리남 ku: Sûrînam kw: Surinam la: Surinamia lb: Surinam li: Suriname ln: Suriname lt: Surinamas lv: Surinama mi: Hurināme mk: Суринам ml: സുരിനാം mn: Суринам mr: सुरिनाम ms: Suriname mt: Surinam my: ဆူရာနမ်နိုင်ငံ nl: Suriname "no": Surinam nv: Sówinam oc: Surinam or: ସରିନମ os: Суринам pa: ਸੂਰੀਨਾਮ pl: Surinam ps: سورينام pt: Suriname qu: Surinam rm: Suriname ro: Surinam ru: Суринам rw: Surinamu se: Suriname sh: Surinam sk: Surinam sl: Surinam so: Surinam sq: Surinami sr: Суринам su: Suriname sv: Surinam sw: Surinam ta: சுரிநாம் te: సురినామ్ th: ประเทศซูรินาเม tl: Surinam tr: Surinam tt: Суринам ug: سۇرىنام uk: Суринам ur: سرینام uz: Surinam vi: Suriname vo: Surinän wo: Surinaam yi: סורינאם yo: Sùrìnámù zh: 蘇利南 ================================================ FILE: settings/country-names/ss.yaml ================================================ name: default: South Sudan af: Suid-Soedan am: ደቡብ ሱዳን an: Sudán d'o Sud ar: جنوب السودان av: Югалъулаб Судан az: Cənubi Sudan be: Паўднёвы Судан bg: Южен Судан bn: দক্ষিণ সুদান br: Soudan ar Su bs: Južni Sudan ca: Sudan del Sud cs: Jižní Súdán cv: Кăнтăр Судан cy: De Sudan da: Sydsudan de: Südsudan el: Νότιο Σουδάν en: South Sudan eo: Suda Sudano es: Sudán del Sur et: Lõuna-Sudaan eu: Hego Sudan fa: سودان جنوبی fi: Etelä-Sudan fo: Suðursudan fr: Sud-Soudan fy: Súd-Sûdan ga: An tSúdáin Theas gd: Sudàn a Deas gl: Sudán do Sur gv: Yn Toodaan Yiass he: דרום סודאן hi: दक्षिण सूडान hr: Južni Sudan hu: Dél-Szudán hy: Հարավային Սուդան ia: Sudan del Sud id: Sudan Selatan ie: Sud-Sudan io: Sud-Sudan is: Suður-Súdan it: Sudan del Sud ja: 南部スーダン jv: Sudan Kidul ka: სამხრეთი სუდანი kk: Оңтүстік Судан ko: 남수단 ku: Sûdana Başûr kv: Лунвыв Судан kw: Soudan Soth ky: Түштүк Судан la: Sudania Australis lb: Südsudan li: Zuid-Soedan lt: Pietų Sudanas lv: Dienvidsudāna mk: Јужен Судан ml: ദക്ഷിണ സുഡാൻ mr: दक्षिण सुदान ms: Sudan Selatan my: တောင်ဆူဒန်နိုင်ငံ na: South Sudan nl: Zuid-Soedan "no": Sør-Sudan nv: Shádiʼááhjí Soodą́ą oc: Sodan del Sud or: ଦକ୍ଷିନ ସୁଦାନ pa: ਦੱਖਣੀ ਸੁਡਾਨ pl: Sudan Południowy ps: سوېلي سوډان pt: Sudão do Sul qu: Urin Sudan rm: Sudan dal Sid ro: Sudanul de Sud ru: Южный Судан sc: Sudan Meridionali se: Lulli-Sudan sg: Sudäan-Mbongo sh: Južni Sudan sk: Južný Sudán sl: Južni Sudan sn: South Sudan so: Koonfur Suudaan sq: Sudani i Jugut sr: Јужни Судан su: Sudan Kidul sv: Sydsudan sw: Sudan Kusini ta: தெற்கு சூடான் te: దక్షిణ సూడాన్ th: ประเทศเซาท์ซูดาน tk: Günorta Sudan tl: Timog Sudan tr: Güney Sudan tt: Көньяк Судан uk: Південний Судан ur: جنوبی سوڈان uz: Janubiy Sudan vi: Nam Sudan vo: Sulüda-Sudän yi: דרום סודאן yo: Gúúsù Sudan zh: 南蘇丹 zu: ISudan yaseNingizimu ================================================ FILE: settings/country-names/st.yaml ================================================ name: default: São Tomé e Príncipe af: São Tomé en Principe ar: سان تومي وبرينسيبي br: São Tomé ha Príncipe ca: São Tomé i Príncipe cs: Svatý Tomáš a Princův ostrov cy: São Tomé a Príncipe da: São Tomé og Príncipe de: São Tomé und Príncipe el: Σάο Τομέ και Πρίνσιπε en: São Tomé and Príncipe eo: Sao-Tomeo kaj Principeo es: Santo Tomé y Príncipe et: São Tomé ja Príncipe fa: سائوتومه و پرینسیپ fi: São Tomé ja Príncipe fr: Sao Tomé-et-Principe fy: Sao Tomee en Prinsipe ga: São Tomé agus Príncipe gd: São Tomé agus Príncipe he: סאו טומה ופרינסיפה hr: Sveti Toma i Princip hu: São Tomé és Príncipe id: Sao Tome dan Principe io: San-Tome e Principe is: Saó Tóme og Prinsípe la: Insulae Sancti Thomae et Principis lb: São Tomé a Príncipe li: São Tomé en Príncipe lt: Sao Tomė ir Prinsipė mn: Сан-Томе ба Принсипи nl: Sao Tomé en Principe "no": São Tomé og Príncipe pl: Wyspy Świętego Tomasza i Książęca pt: São Tomé e Príncipe ru: Сан-Томе и Принсипи se: São Tomé ja Príncipe sk: Svätý Tomáš a Princov ostrov sl: Sveti Tomaž in Princ sr: Сао Томе и Принсипе sv: São Tomé och Príncipe ta: சாவோ தொமே மற்றும் பிரின்சிப்பி th: ประเทศเซาตูเมและปรินซิปี tr: São Tomé ve Príncipe uk: Сан-Томе і Принсіпі vi: São Tomé và Príncipe vo: Saluda-Tomeän e Prinsipeän zh: 圣多美和普林西比 ================================================ FILE: settings/country-names/sv.yaml ================================================ name: default: El Salvador af: El Salvador am: ኤል ሳልቫዶር an: El Salvador ar: السلفادور ay: El Salvador az: Salvador ba: Сальвадор be: Сальвадор bg: Салвадор bm: El Salvador bn: এল সালভাদোর bo: ཨིལ་སལ་ཝ་ཌོར། br: El Salvador bs: El Salvador ca: El Salvador ce: Сальвадор cs: Salvador cy: El Salvador da: El Salvador de: El Salvador dv: އެލްސެލްވަޑޯރު ee: El Salvador el: Ελ Σαλβαδόρ en: El Salvador eo: Salvadoro es: República de El Salvador et: El Salvador eu: El Salvador fa: السالوادور fi: El Salvador fo: El Salvador fr: Salvador fy: El Salvador ga: An tSalvadóir gd: El Salbhador gl: O Salvador gn: Salvador gv: Yn Salvador he: אל סלוודור hi: अल साल्वाडोर hr: Salvador ht: Salvadò hu: Salvador hy: Սալվադոր ia: El Salvador id: El Salvador io: Salvadoria is: El Salvador it: El Salvador ja: エルサルバドル jv: El Salvador ka: სალვადორი kk: Эль-Сальвадор Республикасы kn: ಎಲ್ ಸಾಲ್ವಡಾರ್ ko: 엘살바도르 ku: El Salvador kw: El Salvador la: Salvatoria lb: El Salvador li: El Salvador ln: El Salvador lt: Salvadoras lv: Salvadora mi: Te Whakaora mk: Ел Салвадор ml: എൽ സാൽവദോർ mn: Эль Сальвадор mr: एल साल्व्हाडोर ms: El Salvador mt: El Salvador my: အယ်ဆာဗေဒိုနိုင်ငံ na: Ersarbador ne: एल साल्भादोर nl: El Salvador "no": El Salvador oc: Lo Salvador or: ଏଲ ସାଲଭାଡୋର os: Сальвадор pa: ਸਾਲਵਾਦੋਰ pl: Salwador ps: اېلسلوادور pt: El Salvador qu: Salwadur ro: El Salvador ru: Сальвадор rw: Saluvadoro sa: एल-साल्वाडोर se: El Salvador sh: Salvador si: එල් සැල්ව‍ඩෝ sk: Salvádor sl: Salvador so: El Salfador sq: Salvadori sr: Салвадор ss: ISalivado su: Él Salvador sv: El Salvador sw: El Salvador ta: எல் சால்வடோர் tg: Салвадор th: ประเทศเอลซัลวาดอร์ tl: El Salvador tr: El Salvador tt: Сальвадор ug: ئەل سالۋادور uk: Сальвадор ur: ایل سیلواڈور uz: El Salvador vi: El Salvador vo: Salvadorän wo: Salbadoor yi: על סאלוואדאר yo: El Salfador zh: 萨尔瓦多 zu: El Salvador ================================================ FILE: settings/country-names/sy.yaml ================================================ name: default: سوريا af: Sirië am: ሶርያ an: Siria ar: سوريا av: Сирия az: Suriya ba: Сирия be: Сірыя bg: Сирия bm: Suriya bn: সিরিয়া bo: སི་རི་ཡ། br: Siria bs: Sirija ca: República Àrab Siriana ce: Шема cs: Sýrie cu: Сѷрїꙗ cv: Сири cy: Syria da: Syrien de: Syrien dv: ސޫރިޔާ el: Συρία en: Syria eo: Sirio es: Siria et: Süüria eu: Siria fa: سوریه fi: Syyria fo: Sýria fr: Syrie fy: Syrje ga: An tSiria gd: Siria gl: Siria - سورية gu: સિરિયા gv: Yn Teer he: סוריה hi: सीरिया hr: Sirija ht: Siri hu: Szíria hy: Սիրիա ia: Syria id: Suriah ie: Siria io: Siria is: Sýrland it: Siria ja: シリア jv: Suriah ka: სირია ki: Syria kk: Сирия kl: Syria kn: ಸಿರಿಯಾ ko: 시리아 ku: Sûrî kv: Сирия kw: Syri ky: Сирия la: Syria lb: Syrien li: Syrië ln: Sirí lt: Sirija lv: Sīrija mg: Siria mi: Hiria mk: Сирија ml: സിറിയ mn: Сири mr: सीरिया ms: Syria my: ဆီးရီးယားနိုင်ငံ na: Syria ne: सीरिया nl: Syrië "no": Syria nv: Sííwiya oc: Siria or: ସିରିଆ os: Сири pa: ਸੀਰੀਆ pl: Syria ps: سوريه pt: Síria qu: Sirya ro: Siria ru: Сирия rw: Siriya sa: सिरिया sc: Siria sd: شام se: Syria sh: Sirija sk: Sýria sl: Sirija so: Suuriya sq: Siria sr: Сирија ss: ISiriya su: Suriah sv: Syrien sw: Syria ta: சிரியா te: సిరియా tg: Сурия th: ประเทศซีเรีย tk: Siriýa tl: Sirya tr: Suriye tt: Сүрия ug: سۇرىيە uk: Сирія ur: شام uz: Suriya vi: Syria vo: Süriyän wa: Sireye wo: Siri yi: סיריע yo: Síríà zh: 叙利亚/敘利亞 ================================================ FILE: settings/country-names/sz.yaml ================================================ name: default: eSwatini af: Swaziland am: ስዋዚላንድ an: Swazilandia ar: سوازيلاند ay: Suasïsuiu az: Svazilend ba: Свазиленд be: Эсваціні bg: Есватини bm: Swazilandi bn: সোয়াজিল্যান্ড bo: སུ་ཝ་ཛི་ལནཌ། br: Swaziland bs: Svazilend ca: Swazilàndia cs: Svazijsko cy: Gwlad Swazi da: Swaziland de: Eswatini dv: ސުވާޒިލޭންޑު dz: སུ་ཝ་ཛི་ལེནཌ ee: Swaziland el: Σουαζιλάνδη en: Eswatini eo: Svazilando es: Suazilandia et: Svaasimaa eu: Swazilandia fa: سوازیلند ff: Swaasilannda fi: Swazimaa fo: Svasiland fr: Swaziland fy: Swazilân ga: An tSuasalainn gd: Dùthaich nan Suasaidh gl: Suacilandia - Swaziland gu: સ્વાઝીલેન્ડ gv: Yn Çheer Swassee ha: Suwazilan he: סווזילנד hi: स्वाज़ीलैण्ड hr: Svazi ht: Swazilann hu: Szváziföld hy: Սվազիլենդ ia: Swazilandia id: Swaziland ie: Swaziland io: Swazilando is: Svasíland it: Swaziland ja: スワジランド jv: Swaziland ka: სვაზილენდი kg: Swati ki: Uswazi kk: Суазиланд kl: Swazilandi km: សូហ្ស៉ីឡង់ kn: ಸ್ವಾಜಿಲ್ಯಾಂಡ್ ko: 스와질란드 ks: سُوزِلینٛڑ ku: Swaziland kw: Pow Swati la: Swazia lb: Swasiland lg: Swazirandi li: Swaziland ln: Swaziland lo: ສະວາຊິແລນ lt: Svazilandas lv: Svazilenda mg: Soazilandy mi: Warerangi mk: Свазиленд ml: സ്വാസിലാന്റ് mn: Свазиланд mr: स्वाझीलँड ms: Swaziland mt: Sważiland my: ဆွာဇီလန်နိုင်ငံ ne: स्वाजिल्याण्ड nl: Swaziland "no": Eswatini nv: Swáazi Dineʼé Bikéyah oc: Swaziland or: ସ୍ଵାଜିଲ୍ୟାଣ୍ଡ os: Свазиленд pa: ਸਵਾਜ਼ੀਲੈਂਡ pl: Eswatini ps: سوازيلانډ pt: eSwatini qu: Swasisuyu rn: Suwazilandi ro: eSwatini ru: Эсватини rw: Swazilande sa: स्वाजीलैंड sc: Swaziland se: Swazieana sg: Swäzïlânde sh: Svazilend si: ස්වාසිලන්තය sk: Svazijsko sl: Esvatini sn: Swaziland so: Swasiland sq: Suazilendi sr: Есватини ss: eSwatini su: Swaziland sv: Swaziland sw: Uswazi ta: சுவாசிலாந்து te: స్వాజీలేండ్ tg: Свазиленд th: ประเทศสวาซิแลนด์ ti: ስዋዚላንድ tk: Swazilend tl: Suwasilandya to: Suasileni tr: Esvatini ts: Swaziland tt: Свазиленд ug: سۋازىلېند uk: Есватіні ur: سوازی لینڈ uz: Svazilend ve: Swazini vi: Eswatini vo: Svasiyän wo: Suwaasilaand yi: סוואזילאנד yo: Swásílándì zh: 斯威士兰/斯威士蘭/史瓦濟蘭 zu: ISwazilandi ================================================ FILE: settings/country-names/tc.yaml ================================================ name: default: Turks and Caicos Islands af: Turks en Caicos Eilande an: Islas Turcas e Caicos ar: جزر تركس وكايكوس bg: Търкс и Кайкос bn: টার্কস্‌ ও কেইকোস দ্বীপপুঞ্জ br: Inizi Turks ha Caicos bs: Ostrva Turks i Caicos ca: Illes Turks i Caicos cs: Turks a Caicos cv: Тĕркс тата Кайкос cy: Ynysoedd Turks a Caicos da: Turks- og Caicosøerne de: Turks- und Caicosinseln dv: ޓާކަސް އަދި ކައިކޯ ޖަޒީރާ el: Τερκς και Κάικος en: Turks and Caicos Islands eo: Turkoj kaj Kajkoj es: Islas Turcas y Caicos et: Turks ja Caicos eu: Turks eta Caicos uharteak fa: جزایر تورکس و کایکوس fi: Turks- ja Caicossaaret fr: Îles Turques-et-Caïques fy: Turks- en Kaikoseilannen ga: Oileáin na dTurcach agus Caicos gd: Oileáin na dTurcach agus Caicos gl: Turks e Caicos he: איי טרקס וקייקוס hi: टर्क्स और केकोस द्वीप-समूह hr: Otočje Turks i Caicos hu: Turks- és Caicos-szigetek id: Kepulauan Turks dan Caicos io: Turks e Kaikos-Insuli is: Turks- og Caicoseyjar it: Turks e Caicos ja: タークス・カイコス諸島 jv: Kapuloan Turks lan Caicos ko: 터크스 케이커스 제도 kw: Turks ha Kaykos lt: Terksas ir Kaikosas lv: Tērksas un Kaikosas mn: Туркс ба Кайкосын Арлууд mr: टर्क्स आणि कैकास द्वीपसमूह ms: Kepulauan Turks dan Caicos nb: Turks- og Caicosøyene nl: Turks- en Caicoseilanden nn: Turks- og Caicosøyane "no": Turks- og Caicosøyene pl: Turks i Caicos pt: Ilhas Turcas e Caicos ro: Insulele Turks şi Caicos ru: Тёркс и Кайкос sh: Turks i Caicos Otoci sk: Turks a Caicos sl: Otoki Turks in Caicos sq: Turks dhe Kaikos sr: Туркс и Кајкос sv: Turks- och Caicosöarna sw: Visiwa vya Turks na Caicos ta: துர்கசு கைகோசு தீவுகள் th: หมู่เกาะเติร์กและไคคอส tr: Turks ve Caicos Adaları ug: Turks we Kaykos Taqim Aralliri uk: Острови Теркс і Кайкос vi: Quần đảo Turks và Caicos wo: Turks and Caicos Islands zh: 特克斯和凱科斯群島 ================================================ FILE: settings/country-names/td.yaml ================================================ name: default: Tchad تشاد af: Tsjad am: ቻድ an: Chad ar: تشاد az: Çad ba: Чад be: Чад bg: Чад bm: Cad bn: চাদ bo: ཆཱ་ཌ། br: Tchad bs: Čad ca: Txad ce: Чад cs: Čad cv: Чад cy: Tchad da: Tchad de: Tschad dv: ޝާދު ee: Chad el: Τσαντ en: Chad eo: Ĉado es: Chad et: Tšaad eu: Txad fa: چاد fi: Tšad fo: Kjad fr: Tchad fy: Tsjaad ga: Sead gd: An t-Siad gl: Chad - Tchad gu: ચૅડ gv: Shad ha: Cadi he: צ'אד hi: चाड hr: Čad ht: Tchad hu: Csád hy: Չադ ia: Tchad id: Chad ie: Chad ig: Chad io: Chad is: Tsjad it: Ciad ja: チャド jv: Chad ka: ჩადი kg: Tasadi kk: Чад ko: 차드 ku: Çad kw: Chad la: Tzadia lb: Tschad li: Tsjaad ln: Tshadi lt: Čadas lv: Čada mi: Kāta mk: Чад ml: ഛാഡ് mn: Чад mr: चाड ms: Chad mt: Ċad my: ချဒ်သမ္မတနိုင်ငံ na: Tsiad nl: Tsjaad "no": Tsjad oc: Chad or: ଚାଡ os: Чад pa: ਚਾਡ pl: Czad ps: چاډ pt: Chade qu: Chad rm: Tschad ro: Ciad ru: Чад rw: Cade sa: चाड sc: Chad se: Chad sg: Sâde sh: Čad si: චෑඩ් sk: Čad sl: Čad sn: Chad so: Jad sq: Çadi sr: Чад ss: IShedi su: Chad sv: Tchad sw: Chad ta: சாட் te: చాద్ tg: Чад th: ประเทศชาด ti: ቻድ tk: Çad tl: Tsad tr: Çad ts: Chad tt: Чад ug: چاد uk: Чад ur: چاڈ uz: Chad vi: Tchad vo: Tjadän wa: Tchad wo: Cadd yi: טשאד yo: Tsad zh: 乍得 zu: ITshedi ================================================ FILE: settings/country-names/tg.yaml ================================================ name: default: Togo af: Togo ak: Togo am: ቶጎ an: Togo ar: توغو az: Toqo ba: Того be: Тога bg: Того bm: Togo bn: টোগো bo: ཊོ་གོ br: Togo bs: Togo ca: Togo ce: Того cs: Togo cv: Того cy: Togo da: Togo de: Togo dv: ޓޯގޯ ee: Togo el: Τόγκο en: Togo eo: Togolando es: Togo et: Togo eu: Togo fa: توگو fi: Togo fr: Togo fy: Togo ga: Tóga gd: Tògo gl: Togo gu: ટોગો gv: Yn Togo ha: Togo he: טוגו hi: टोगो hr: Togo ht: Togo hu: Togo hy: Տոգո id: Togo ie: Togo ig: Togo io: Togo is: Tógó it: Togo ja: トーゴ jv: Togo ka: ტოგო kk: Того kn: ಟೊಗೊ ko: 토고 ku: Togo kw: Togo la: Togum lb: Togo li: Togo ln: Togo lt: Togas lv: Togo mi: Toko mk: Того ml: ടോഗോ mn: Того mr: टोगो ms: Togo mt: Togo my: တိုဂိုနိုင်ငံ na: Togo nl: Togo "no": Togo nv: Tʼóogo oc: Tògo or: ଟୋଗୋ os: Того pa: ਟੋਗੋ pl: Togo pt: Togo qu: Tugu rm: Togo ro: Togo ru: Того rw: Togo sa: टोगो sc: Togo se: Togo sg: Togö sh: Togo si: ටෝගෝ sk: Togo sl: Togo sn: Togo so: Togo sq: Togo sr: Того ss: IThogo st: Togo su: Togo sv: Togo sw: Togo ta: டோகோ tg: Того th: ประเทศโตโก ti: ቶጎ tk: Togo tl: Togo tr: Togo ts: Togo tw: Togo ug: توگو uk: Того ur: ٹوگو uz: Togo vi: Togo vo: Togoän wo: Togóo yi: טאגא yo: Tógò zh: 多哥 zu: ITogo ================================================ FILE: settings/country-names/th.yaml ================================================ name: default: ประเทศไทย af: Thailand am: ታይላንድ an: Tailandia ar: تايلاند as: থাইলেণ্ড az: Tailand ba: Таиланд be: Тайланд bg: Тайланд bn: থাইল্যান্ড bo: ཐའི་ལེན། br: Thailand bs: Tajland ca: Tailàndia ce: Таиланд cs: Thajsko cv: Таиланд cy: Gwlad Thai da: Thailand de: Thailand dv: ސިޔާމު dz: ཐཱའི་ལེནཌ el: Ταϊλάνδη en: Thailand eo: Tajlando es: Tailandia et: Tai eu: Thailandia fa: تایلند fi: Thaimaa fo: Teiland fr: Thaïlande fy: Tailân ga: An Téalainn gd: Dùthaich nan Tàidh gl: Tailandia gn: Tailandia gu: થાઇલેન્ડ gv: Yn Çheer Thai he: תאילנד hi: थाईलैण्ड hr: Tajland ht: Tayilann hu: Thaiföld hy: Թաիլանդ ia: Thailanda id: Thailand ie: Thailand io: Tailando is: Taíland it: Thailandia ja: タイ王国 jv: Thailand ka: ტაილანდი ki: Thailand kk: Тайланд kl: Thailandi km: ថៃ kn: ಥೈಲ್ಯಾಂಡ್ ko: 태국 ks: تھائی لینڈ ku: Taylenda kv: Таиланд kw: Pow Tay la: Thailandia lb: Thailand li: Thailand lo: ປະເທດໄທ lt: Tailandas lv: Taizeme mg: Tailandy mi: Tairana mk: Тајланд ml: തായ്‌ലാന്റ് mn: Тайланд mr: थायलंड ms: Thailand my: ထိုင်းနိုင်ငံ na: Thailand ne: थाइल्याण्ड nl: Thailand "no": Thailand nv: Tʼáí Bikéyah oc: Tailàndia or: ଥାଇଲାଣ୍ଡ os: Таиланд pa: ਥਾਈਲੈਂਡ pl: Tajlandia ps: تايلنډ pt: Tailândia qu: Thaysuyu ro: Thailanda ru: Таиланд rw: Tayilande sa: श्यामदेश sd: ٿائيلينڊ se: Thaieana sg: Tailânde sh: Tajland sk: Thajsko sl: Tajska so: Tayland sq: Tajlanda sr: Тајланд ss: IThayilandi su: Thailand sv: Thailand sw: Uthai ta: தாய்லாந்து te: థాయిలాండ్ tg: Таиланд th: Muang Thai tk: Tailand tl: Taylandiya tr: Tayland tt: Таиланд tw: Thailand ug: تايلاند uk: Таїланд ur: تھائی لینڈ uz: Tailand vi: Thái Lan vo: Tayän wo: Taaylaand yi: טיילאנד yo: Tháílàndì za: Daigoz zh: 泰国 ================================================ FILE: settings/country-names/tj.yaml ================================================ name: default: Тоҷикистон af: Tadjikistan am: ታጂኪስታን an: Tachiquistán ar: طاجيكستان ay: Tayiksuyu az: Tacikistan ba: Тажикстан be: Таджыкістан bg: Таджикистан bn: তাজিকিস্তান bo: ཐ་ཇི་ཁེ་སི་ཏན། br: Tadjikistan bs: Tadžikistan ca: Tadjikistan ce: Таджикистан cs: Tádžikistán cu: Таджикистанъ cv: Таджикистан cy: Tajikistan da: Tadsjikistan de: Tadschikistan dv: ތަޖިކިސްތާން dz: ཏ་ཇག་ཀིསི་ཏཱན་ ee: Tajikistan el: Τατζικιστάν en: Tajikistan eo: Taĝikio es: Tayikistán et: Tadžikistan eu: Tajikistan fa: تاجیکستان fi: Tadžikistan fo: Tadsjikistan fr: Tadjikistan fy: Tadzjikistan ga: An Táidsíceastáin gd: Taidigeastàn gl: Taxiquistán gu: તાજિકિસ્તાન gv: Yn Tajikistaan he: טג'יקיסטן hi: ताजिकिस्तान hr: Tadžikistan ht: Tadjikistan hu: Tádzsikisztán hy: Տաջիկստան ia: Tajikistan id: Tajikistan ie: Tadjikistan io: Tajikistan is: Tadsjikistan it: Tagikistan ja: タジキスタン jv: Tajikistan ka: ტაჯიკეთი kk: Тәжікстан km: តាជីគីស្ថាន kn: ತಜಿಕಿಸ್ತಾನ್ ko: 타지키스탄 ku: Tacîkistan kv: Таджикистан kw: Pow Tajik ky: Тажикстан la: Tadzikistania lb: Tadjikistan li: Tadzjikistan lt: Tadžikija lv: Tadžikistāna mk: Таџикистан ml: താജിക്കിസ്ഥാൻ mn: Тажикистан mr: ताजिकिस्तान ms: Tajikistan my: တာဂျစ်ကစ္စတန်နိုင်ငံ na: Tadjikitan nl: Tadzjikistan "no": Tadsjikistan nv: Tʼajiʼ Bikéyah oc: Tatgiquistan or: ତାଜିକିସ୍ତାନ os: Таджикистан pl: Tadżykistan ps: تاجيکستان pt: Tajiquistão qu: Tayiksuyu ro: Tadjikistan ru: Таджикистан rw: Tajikisitani sa: ताजिकिस्थान se: Tažikistan sh: Tadžikistan si: ටජිකිස්ථාන් sk: Tadžikistan sl: Tadžikistan so: Tadsjikistan sq: Taxhikistani sr: Таџикистан ss: IThajiki su: Tajikistan sv: Tadzjikistan sw: Tajikistan ta: தஜிகிஸ்தான் te: తజికిస్తాన్ tg: Тоҷикистон th: ประเทศทาจิกิสถาน tk: Täjigistan tl: Tayikistan tr: Tacikistan tt: Таҗикстан ug: تاجىكىستان uk: Таджикистан ur: تاجکستان uz: Tojikiston vi: Tajikistan vo: Tacikistän wo: Tajikistaan yi: טאדזשיקיסטאן yo: Tajikistan zh: 塔吉克斯坦 ================================================ FILE: settings/country-names/tk.yaml ================================================ name: default: Tokelau am: ቶክላው ar: توكلو be: Такелаў bg: Токелау bm: Tokelo bn: টোকেলাউ br: Tokelau ca: Tokelau cs: Tokelau da: Tokelau de: Tokelau dz: ཏོ་ཀེ་ལའུ་ མཚོ་གླིང ee: Tokelau nutome el: Τοκελάου en: Tokelau eo: Tokelao es: Tokelau fa: توکلائو ff: Tokelaaw fi: Tokelau fr: Tokelau fy: Tokelaû ga: Oileáin Tócalá gu: ટોકેલાઉ ha: Takelau he: טוקלאו hi: तोकेलाउ hr: Tokelau hu: Tokelau-szigetek is: Tókelá ja: トケラウ km: តូខេឡៅ kn: ಟೊಕೆಲಾವ್ ko: 토켈라우 ks: توکیلاو ku: Tokelau lg: Tokelawu lo: ໂຕເກເລົາ lt: Tokelau lv: Tokelau mg: Tokelao mk: Токелау ml: ടൊകെലാവു mn: Токелау mr: तोकेलाउ mt: Tokelaw ne: तोगो "no": Tokelau or: ଟୋକେଲାଉ pl: Tokelau pt: Toquelau rn: Tokelawu ru: Токелау si: ටොකලාවු sk: Tokelau sr: Токелау sv: Tokelauöarna ta: டோகேலோ te: టోకేలావ్ th: โตเกเลา ti: ቶክላው uk: Токелау ur: ٹوکیلاؤ vi: Tokelau yo: Orílẹ́ède Tokelau zh: 托克劳 zu: i-Tokelau ================================================ FILE: settings/country-names/tl.yaml ================================================ name: default: Timór Lorosa'e af: Oos-Timor ar: جمهورية تيمور br: Timor ar Reter ca: Timor Oriental cs: Východní Timor cy: Dwyrain Timor da: Østtimor de: Osttimor el: Ανατολικό Τιμόρ en: East Timor eo: Orienta Timoro es: Timor Oriental et: Ida-Timor fa: تیمور شرقی fi: Itä-Timor fr: Timor oriental fy: East-Timor ga: An Tíomór Thoir gd: Tiomor an Ear he: מזרח טימור hr: Istočni Timor hu: Kelet-Timor id: Timor Leste is: Austur-Tímor it: Timor Est la: Timoria Orientalis li: Oos-Timor lt: Rytų Timoras mn: Зүүн Тимор ms: Timor Timur nb: Øst-Timor nl: Oost-Timor nn: Aust-Timor "no": Øst-Timor pl: Timor Wschodni pt: Timor-Leste ru: Восточный Тимор se: Nuorta-Timor sk: Východný Timor sl: Vzhodni Timor sv: Östtimor ta: கிழக்குத் திமோர் th: ประเทศติมอร์ตะวันออก tl: Silangan Timor tr: Doğu Timor tt: Көнчыгыш Тимор uk: Східний Тимор vi: Đông Timor zh: 东帝汶 / 東帝汶 ================================================ FILE: settings/country-names/tm.yaml ================================================ name: default: Türkmenistan af: Turkmenistan am: ቱርክመኒስታን an: Turkmenistán ar: تركمانستان av: Туркменистан az: Türkmənistan ba: Төрөкмәнстан be: Туркменістан bg: Туркменистан bn: তুর্কমেনিস্তান bo: ཏུརཀ་མེ་ནི་སུཏན། br: Turkmenistan bs: Turkmenistan ca: Turkmenistan co: Turkmenistan cs: Turkmenistán cv: Туркменистан cy: Turkmenistan da: Turkmenistan de: Turkmenistan dv: ތުރުކުމެނިސްތާން ee: Turkmenistan el: Τουρκμενιστάν en: Turkmenistan eo: Turkmenio es: Turkmenistán et: Türkmenistan eu: Turkmenistan fa: ترکمنستان fi: Turkmenistan fo: Turkmenistan fr: Turkménistan fy: Turkmenistan ga: An Tuircméanastáin gd: Turcmanastàn gl: Turcomenistán gu: તુર્કમેનિસ્તાન gv: Yn Turkmenistaan he: טורקמניסטן hi: तुर्कमेनिस्तान hr: Turkmenistan ht: Tirkmenistan hu: Türkmenisztán hy: Թուրքմենստան ia: Turkmenistan id: Turkmenistan ie: Turkmenistan io: Turkmenistan is: Túrkmenistan it: Turkmenistan ja: トルクメニスタン jv: Turkmenistan ka: თურქმენეთი kk: Түрікменстан km: តួរមិនីស្ថាន kn: ತುರ್ಕಮೆನಿಸ್ತಾನ್ ko: 투르크메니스탄 ku: Tirkmenistan kv: Туркменистан kw: Pow Turkmen ky: Түркмөнстан la: Turcomannia li: Turkmenistan ln: Turkmenistáni lt: Turkmėnija lv: Turkmenistāna mk: Туркменистан ml: തുർക്‌മെനിസ്ഥാൻ mn: Туркменистан mr: तुर्कमेनिस्तान ms: Turkmenistan my: တာ့ခ်မင်နစ္စတန်နိုင်ငံ na: Turkmenistan ne: तुर्कमेनिस्तान nl: Turkmenistan "no": Turkmenistan nv: Tʼóokmen Bikéyah oc: Turcmenistan or: ତୁର୍କମେନିସ୍ଥାନ os: Туркмени pa: ਤੁਰਕਮੇਨਸਤਾਨ pl: Turkmenistan ps: ترکمنستان pt: Turquemenistão qu: Turkminsuyu ro: Turkmenistan ru: Туркменистан rw: Turukimenisitani sa: तुर्कमिनिस्थान se: Turkmenistan sh: Turkmenistan sk: Turkménsko sl: Turkmenistan so: Turkmenistan sq: Turkmenistani sr: Туркменистан ss: IThumekhi su: Turkménistan sv: Turkmenistan sw: Turkmenistan ta: துருக்மெனிஸ்தான் te: తుర్కమేనిస్తాన్ tg: Туркманистон th: ประเทศเติร์กเมนิสถาน tk: Türkmenistan tl: Turkmenistan tr: Türkmenistan tt: Төрекмәнстан ug: تۈركمەنىستان uk: Туркменістан ur: ترکمانستان uz: Turkmaniston vi: Turkmenistan vo: Turkmenän wo: Turkumenistaan yi: טורקמעניסטאן yo: Turkmẹ́nìstán zh: 土库曼斯坦 ================================================ FILE: settings/country-names/tn.yaml ================================================ name: default: تونس af: Tunisië am: ቱኒዚያ an: Tunicia ar: تونس az: Tunis ba: Тунис be: Туніс bg: Тунис bm: Tunizi bn: তিউনিসিয়া bo: ཏུ་ནི་ཤི་ཡ། br: Tunizia bs: Tunis ca: Tunísia ce: Тунис cs: Tunisko cv: Тунис cy: Tunisia da: Tunesien de: Tunesien dv: ތޫނިސް ee: Tunisia el: Τυνησία en: Tunisia eo: Tunizio es: Túnez et: Tuneesia eu: Tunisia fa: تونس fi: Tunisia fo: Tunesia fr: Tunisie fy: Tuneezje ga: An Túinéis gd: Tuinisia gl: Tunisia gu: ટ્યુનિશિયા gv: Yn Tooneesh he: תוניסיה hi: ट्यूनिशिया hr: Tunis ht: Tinizi hu: Tunézia hy: Թունիս id: Tunisia ie: Tunisia io: Tunizia is: Túnis it: Tunisia ja: チュニジア jv: Tunisia ka: ტუნისი kg: Tunisia kk: Тунис kn: ಟುನೀಶಿಯ ko: 튀니지 ks: تونس ku: Tûnis kw: Tunisi ky: Тунис la: Tunesia lb: Tunesien li: Tunesië ln: Tunisia lt: Tunisas lv: Tunisija mg: Tonizia mi: Tūnihia mk: Тунис ml: ടുണീഷ്യ mn: Тунис mr: ट्युनिसिया ms: Tunisia mt: Tuneżija my: တူနီးရှားနိုင်ငံ nl: Tunesië "no": Tunisia oc: Tunisia or: ଟ୍ୟୁନିସିଆ os: Тунис pa: ਤੁਨੀਸੀਆ pl: Tunezja ps: تونس pt: Tunísia qu: Tunisya rm: Tunesia ro: Tunisia ru: Тунис rw: Tunisiya sa: टुनिशिया sc: Tunisia se: Tunisia sg: Tunizïi sh: Tunis sk: Tunisko sl: Tunizija sn: Tunisia so: Tunisiya sq: Tunizia sr: Тунис ss: IThunisiya su: Tunisia sv: Tunisien sw: Tunisia ta: துனீசியா te: ట్యునీషియా tg: Тунис th: ประเทศตูนิเซีย ti: ቱኒዢያ tk: Tunis tl: Tunisya tr: Tunus ts: Tunisia tt: Тунис ug: تۇنىس uk: Туніс ur: تونس uz: Tunis vi: Tunisia vo: Tünisän wa: Tunizeye wo: Tiniisi yi: טוניסיע yo: Tùnísíà zh: 突尼西亞 zu: ITunisia ================================================ FILE: settings/country-names/to.yaml ================================================ name: default: Tonga af: Tonga am: ቶንጋ an: Tonga ar: تونجا az: Tonqa ba: Тонга be: Тонга bg: Тонга bn: টোঙ্গা bo: ཊོང་ག br: Tonga bs: Tonga ca: Tonga ce: Тонга cs: Tonga cu: Тонга cy: Tonga da: Tonga de: Tonga dv: ޓޮންގާ el: Τόνγκα en: Tonga eo: Tongo es: Tonga et: Tonga eu: Tonga fa: تونگا fi: Tonga fo: Tonga fr: Tonga fy: Tonga ga: Tonga gd: Tonga gl: Tonga gv: Yn Tongey he: טונגה hi: टोंगा hr: Tonga ht: Tonga hu: Tonga hy: Տոնգա ia: Tonga id: Tonga io: Tonga is: Tonga it: Tonga ja: トンガ jv: Tonga ka: ტონგა kk: Тонга kn: ಟೋಂಗಾ ko: 통가 ku: Tonga kw: Tonga la: Tonga lb: Tonga li: Tonga lt: Tonga lv: Tonga mk: Тонга ml: ടോങ്ക mn: Тонга mr: टोंगा ms: Tonga mt: Tonga my: တုံဂါနိုင်ငံ nl: Tonga "no": Tonga nv: Tʼónga oc: Tònga or: ଟୋଙ୍ଗା os: Тонгæ pa: ਟੋਂਗਾ pl: Tonga pt: Tonga qu: Tunqa rm: Tonga ro: Tonga ru: Тонга rw: Tonga sa: टोंगा se: Tonga sh: Tonga sk: Tonga sl: Tonga sm: Toga sq: Tonga sr: Тонга su: Tonga sv: Tonga sw: Tonga ta: தொங்கா tg: Тонга th: ประเทศตองกา tl: Tonga to: Tonga tr: Tonga ts: Tonga tw: Tonga ty: To’a ug: Ton’ga uk: Тонга ur: ٹونگا uz: Tonga vi: Tonga vo: Tonguäns wo: Tonga yo: Tóngà zh: 東加 ================================================ FILE: settings/country-names/tr.yaml ================================================ name: default: Türkiye ab: Ҭырқәтәыла af: Turkye ak: Turki am: ቱርክ an: Turquía ar: تركيا av: Туркия ay: Turkiya az: Türkiyə ba: Төркиә be: Турцыя bg: Турция bi: Turkey bn: তুরস bo: ཏུར་ཀི། br: Turkia bs: Turska ca: Turquia ce: Туркойчоь cs: Turecko cu: Тѷрци cv: Турци cy: Twrci da: Tyrkiet de: Türkei dv: ތުރުކީވިލާތް ee: Turkey el: Τουρκία en: Turkey eo: Turkio es: Turquía et: Türgi eu: Turkia fa: ترکیه fi: Turkki fo: Turkaland fr: Turquie fy: Turkije ga: An Tuirc gd: An Tuirc gl: Turquía gn: Tuykia gu: તુર્કસ્તાન gv: Yn Turkee ha: Turkiyya he: טורקיה hi: तुर्की hr: Turska ht: Tiki hu: Törökország hy: Թուրքիա ia: Turchia id: Turki ie: Turcia ik: Turkey io: Turkia is: Tyrkland it: Turchia ja: トルコ jv: Turki ka: თურქეთი kg: Turki ki: Turkey kk: Түркия kl: Tyrkia kn: ಟರ್ಕಿ ko: 터키 ku: Tirkiye kv: Турция kw: Turki ky: Түркия Республикасы la: Turcia lb: Tierkei lg: Buturuki li: Turkije ln: Turkí lt: Turkija lv: Turcija mg: Torkia mi: Tākei mk: Турција ml: തുർക്കി mn: Турк mr: तुर्कस्तान ms: Turki mt: Turkija my: တူရကီနိုင်ငံ na: Terki ne: टर्की nl: Turkije "no": Tyrkia nv: Tʼóok Bikéyah oc: Turquia or: ତୁର୍କୀ os: Турк pa: ਤੁਰਕੀ pl: Turcja ps: تورکيه pt: Turquia qu: Turkiya rm: Tirchia rn: Turukiya ro: Turcia ru: Турция rw: Turukiya sa: तुर्की sc: Turkia se: Durka sh: Turska si: තුර්කිය sk: Turecko sl: Turčija so: Turki sq: Turqia sr: Турска ss: IThekhi su: Turki sv: Turkiet sw: Uturuki ta: துருக்கி te: టర్కీ tg: Туркия th: ประเทศตุรกี tk: Türkiýe tl: Turkiya tr: Türkiye tt: Төркия tw: Turkey ug: تۈركىيە جۇمھۇرىيىتى uk: Туреччина ur: ترکی uz: Turkiya vi: Thổ Nhĩ Kỳ vo: Türkän wa: Tourkeye wo: Tirki yi: טערקיי yo: Túrkì za: Dujwjgiz zh: 土耳其 zu: ITheki ================================================ FILE: settings/country-names/tt.yaml ================================================ name: default: Trinidad and Tobago af: Trinidad en Tobago ar: ترينيداد وتوباغو be: Трынідад і Табага br: Trinidad-ha-Tobago ca: Trinitat i Tobago cs: Trinidad a Tobago cy: Trinidad a Tobago da: Trinidad og Tobago de: Trinidad und Tobago en: Trinidad and Tobago eo: Trinidado kaj Tobago es: Trinidad y Tobago et: Trinidad ja Tobago fa: ترینیداد و توباگو fi: Trinidad ja Tobago fr: Trinité-et-Tobago fy: Trinidad en Tobago ga: Oileán na Tríonóide agus Tobága gd: Trianaid agus Tobago he: טרינידד וטובגו hr: Trinidad i Tobago hu: Trinidad és Tobago id: Trinidad dan Tobago is: Trínidad og Tóbagó it: Trinidad e Tobago ja: トリニダード・トバゴ la: Trinitas et Tabacum lt: Trinidadas ir Tobagas mn: Тринидад ба Тобаго nl: Trinidad en Tobago "no": Trinidad og Tobago pl: Trynidad i Tobago pt: Trindade e Tobago ru: Тринидад и Тобаго se: Trinidad ja Tobago sk: Trinidad a Tobago sl: Trinidad in Tobago sv: Trinidad och Tobago ta: டிரினிடாட் மற்றும் டொபாகோ th: ประเทศตรินิแดดและโตเบโก tr: Trinidad ve Tobago uk: Тринідад і Тобаго vi: Trinidad và Tobago vo: Trinidadeän e Tobageäns zh: 特立尼达和多巴哥 ================================================ FILE: settings/country-names/tv.yaml ================================================ name: default: Tuvalu am: ቱቫሉ ar: توفالو be: Тувалу bg: Тувалу bn: টুভালু bo: ཐུ་ཝ་ལུ། br: Tuvalu ca: Tuvalu cs: Tuvalu cy: Twfalw da: Tuvalu de: Tuvalu dz: ཏུ་ཝ་ལུ ee: Tuvalu nutome el: Τουβαλού en: Tuvalu eo: Tuvalo es: Tuvalu fa: تووالو ff: Tuwaluu fi: Tuvalu fr: Tuvalu fy: Tûvalû ga: Tuvalu gd: Tubhalu gu: તુવાલુ gv: Tuvalu ha: Tubalu he: טובלו hi: तुवालु hr: Tuvalu hu: Tuvalu hy: Տուվալու is: Túvalú ja: ツバル ka: ტუვალუ km: ទូវ៉ាលូ kn: ಟುವಾಲು ko: 투발루 ks: توٗوالوٗ ku: Tûvalû lo: ຕູວາລູ lt: Tuvalu lv: Tuvalu mg: Tovalò mk: Тувалу ml: തുവാലു mn: Тувалу mr: टुवालु ne: तुभालु "no": Tuvalu oc: Tuvalu or: ଟୁଭାଲୁ pl: Tuvalu pt: Tuvalu ru: Тувалу se: Tuvalu sg: Tüvalü si: ටුවාලූ sk: Tuvalu sr: Тувалу sv: Tuvalu ta: துவாலூ te: టువాలు th: ประเทศตูวาลู ti: ቱቫሉ to: Tūvalu uk: Тувалу ur: ٹووالو vo: Tuvaluäns yo: Orílẹ́ède Tufalu zh: 吐瓦鲁 zu: i-Tuvalu ================================================ FILE: settings/country-names/tw.yaml ================================================ name: default: 臺灣 af: Taiwan ak: Taiwan am: ታይዋን an: Taiwán ar: تايوان as: টাইৱান az: Tayvan be: Тайв́ань bg: Тайван bh: ताइवान bm: Tayiwani bn: তাইওয়ান bo: ཐེ་ཝན br: Taiwan bs: Tajvan ca: Taiwan ce: Тайвань cs: Tchaj-wan cv: Тайва́нь cy: Taiwan da: Taiwan de: Taiwan dv: ޖުމްހޫރީ ޗައިނާ dz: ཏའི་ཝཱན་ ee: Taiwan el: Ταϊβάν en: Taiwan eo: Tajvano es: Taiwán et: Taiwan eu: Taiwan fa: تایوان ff: Taywaan fi: Taiwan fo: Teivan fr: Taïwan fy: Taiwan ga: an Téaváin gd: Taidh-Bhàn gl: Taiwán gu: તાઇવાન gv: yn Taiwaan he: טאיוואן hi: ताइवान hr: Tajvan ht: Taywann hu: Tajvan hy: Թայվան ia: Taiwan id: Taiwan ie: Taiwan io: Taiwan is: Taívan it: Taiwan ja: 台湾 jv: Taiwan ka: ტაივანი ki: Taiwani kk: Тайвань kl: Taiwan km: តៃវ៉ាន់ kn: ಟೈವಾನ್ ko: 타이완 ku: Taywan kw: Taywan ky: Тайвань la: Taivania lb: Taiwan lg: Tayiwani li: Taiwan ln: Taiwan lo: ໄຕ້ຫວັນ lt: Taivanas lv: Taivāna mg: Taiwan mi: Taiwana mk: Тајван ml: തായ്വാൻ mn: Тайвань mr: तैवान ms: Taiwan mt: Tajwan my: ထိုင်ဝမ် ne: ताइवान nl: Taiwan "no": Taiwan oc: Taiwan or: ତାଇୱାନ os: Тайван pa: ਤਾਈਵਾਨ pi: तैवान pl: Tajwan ps: تایوان pt: Taiwan qu: Taiwán rm: Taiwan rn: Tayiwani ro: Taiwan ru: Тайвань rw: Tayiwani sa: तैवान se: Taiwan sg: Tâiwâni sh: Tajvan si: තායිවානය sk: Taiwan sl: Tajvan sn: Taiwan so: Taywan sq: Tajvani sr: Тајван ss: iThayiwani su: Tiongkok sv: Taiwan sw: Taiwan te: తైవాన్ tg: Тайва́н th: ไต้หวัน tk: Taýwan tl: Taiwan to: Taiuani tr: Tayvan tt: Тайва́нь ug: تەيۋەن uk: Тайва́нь ur: تائیوان uz: Tayvan vi: Đài Loan vo: Tayvän wo: Taaywaan yi: טייוואַן yo: Taiwan za: Daizvanh zh: 臺灣 zu: i-Taiwan ================================================ FILE: settings/country-names/tz.yaml ================================================ name: default: Tanzania af: Tanzanië ak: Tanzania am: ታንዛኒያ an: Tanzania ar: تنزانيا az: Tanzaniya ba: Танзания be: Танзанія bg: Танзания bm: Tanzania bn: তানজানিয়া bo: ཏན་ཛ་ནི་ཡ། br: Tanzania bs: Tanzanija ca: Tanzània ce: Танзани cs: Tanzanie cv: Танзани cy: Tanzania da: Tanzania de: Tansania dv: ޓެންޒޭނިއާ ee: Tanzania el: Τανζανία en: Tanzania eo: Tanzanio es: Tanzania et: Tansaania eu: Tanzania fa: تانزانیا fi: Tansania fo: Tansania fr: Tanzanie fy: Tanzania ga: An Tansáin gd: Tansainìa gl: Tanzania gu: ટાન્ઝાનિયા gv: Tanzania he: טנזניה hi: तंज़ानिया hr: Tanzanija ht: Tanzani hu: Tanzánia hy: Տանզանիա ia: Tanzania id: Tanzania ie: Tanzania io: Tanzania is: Tansanía it: Tanzania ja: タンザニア jv: Tanzania ka: ტანზანია kg: Tanzania ki: Tanzania kk: Танзания kl: Tanzania kn: ಟಾಂಜಾನಿಯ ko: 탄자니아 ku: Tanzanya kw: Tanzania la: Tanzania lb: Tansania li: Tanzania ln: Tanzania lt: Tanzanija lv: Tanzānija mg: Tanzania mi: Tānahia mk: Танзанија ml: ടാൻസാനിയ mn: Танзани mr: टांझानिया ms: Tanzania mt: Tanżanija my: တန်ဇေးနီးယားနိုင်ငံ ne: तन्जानिया nl: Tanzania "no": Tanzania nv: Tʼanzanííya ny: Tanzania oc: Tanzania om: Tanzania or: ତାଞ୍ଜାନିଆ os: Танзани pa: ਤਨਜ਼ਾਨੀਆ pl: Tanzania pt: Tanzânia qu: Tansanya rm: Tansania rn: Tanzaniya ro: Tanzania ru: Танзания rw: Tanzaniya sa: टंजानिया sc: Tanzània se: Tanzania sg: Tanzanïi sh: Tanzanija sk: Tanzánia sl: Tanzanija sn: Tanzania so: Tansaaniya sq: Tanzania sr: Танзанија ss: IThanzaniya st: Tanzania su: Tanzania sv: Tanzania sw: Tanzania ta: தன்சானியா te: టాంజానియా tg: Танзания th: ประเทศแทนซาเนีย ti: ታንዛኒያ tk: Tanzaniýa tl: Tanzania tr: Tanzanya ts: Tanzania tt: Танзания tw: Tanzania ug: تانزانىيە uk: Танзанія ur: تنزانیہ uz: Tanzaniya vi: Tanzania vo: Tansanän wo: Tansani yi: טאנזאניע yo: Tànsáníà zh: 坦桑尼亚 zu: ITanzania ================================================ FILE: settings/country-names/ua.yaml ================================================ name: default: Україна ab: Украина af: Oekraïne am: ዩክሬን an: Ucraína ar: أوكرانيا az: Ukrayna ba: Украина be: Украіна bg: Украйна bi: Ukraine bn: ইউক্রেন bo: ཨུཀ་རཡི་ནི། br: Ukraina bs: Ukrajina ca: Ucraïna ce: Украина cs: Ukrajina cu: Оукраина cv: Украина cy: Wcráin da: Ukraine de: Ukraine dv: ޔުކްރެއިން ee: Ukraine el: Ουκρανία en: Ukraine eo: Ukrainio es: Ucrania et: Ukraina eu: Ukraina fa: اوکراین fi: Ukraina fo: Ukraina fr: Ukraine fy: Oekraïne ga: An Úcráin gd: An Ucràin gl: Ucraína gn: Ukyaña gu: યુક્રેન gv: Yn Ookraan he: אוקראינה hi: युक्रेन hr: Ukrajina ht: Ikrèn hu: Ukrajna hy: Ուկրաինա ia: Ukraina id: Ukraina ie: Ucraina ig: Yukrain io: Ukrainia is: Úkraína it: Ucraina ja: ウクライナ jv: Ukraina ka: უკრაინა kg: Ukrayina kk: Украина kl: Ukraine km: អ៊ុយក្រែន kn: ಯುಕ್ರೇನ್ ko: 우크라이나 ku: Ûkrayna kv: Украина kw: Ukrayn ky: Украина la: Ucraina lb: Ukrain li: Oekraïne ln: Ukraine lt: Ukraina lv: Ukraina mg: Okraina mi: Ūkareinga mk: Украина ml: ഉക്രൈൻ mn: Украйн mr: युक्रेन ms: Ukraine mt: Ukrajna my: ယူကရိန်းနိုင်ငံ na: Ukraine ne: युक्रेन nl: Oekraïne "no": Ukraina nv: Yóókwein oc: Ucraïna or: ୟୁକ୍ରେନ os: Украинæ pa: ਯੂਕ੍ਰੇਨ pl: Ukraina ps: اوکراین pt: Ucrânia qu: Ukranya rm: Ucraina ro: Ucraina ru: Украина rw: Ikerene sc: Ucraina se: Ukraina sh: Ukrajina sk: Ukrajina sl: Ukrajina sm: Ukraine sn: Ukraine so: Ukrain sq: Ukraina sr: Украјина ss: IYukhureni su: Ukraina sv: Ukraina sw: Ukraine ta: உக்ரைன் te: ఉక్రెయిన్ tg: Украйина th: ประเทศยูเครน tk: Ukraina tl: Ukranya tr: Ukrayna ts: Ukraine tt: Украина tw: Ukraine ug: ئۇكرائىنا uk: Україна ur: یوکرین uz: Ukraina vi: Ukraina vo: Lukrayän wa: Oucrinne wo: Ukreen yi: אוקראינע yo: Ukréìn zh: 乌克兰/烏克蘭 ================================================ FILE: settings/country-names/ug.yaml ================================================ name: default: Uganda af: Uganda am: ዩጋንዳ an: Uganda ar: أوغندا az: Uqanda ba: Уганда be: Уганда bg: Уганда bm: Uganda bn: উগান্ডা bo: ཨུ་གན་ད། br: Ouganda bs: Uganda ca: Uganda ce: Уганда cs: Uganda cv: Уганда cy: Uganda da: Uganda de: Uganda dv: ޔުގެންޑާ ee: Uganda el: Ουγκάντα en: Uganda eo: Ugando es: Uganda et: Uganda eu: Uganda fa: اوگاندا fi: Uganda fo: Uganda fr: Ouganda fy: Uganda ga: Uganda gd: Uganda gl: Uganda gv: Ooganda ha: Uganda he: אוגנדה hi: युगाण्डा hr: Uganda ht: Ouganda hu: Uganda hy: Ուգանդա ia: Uganda id: Uganda ie: Uganda io: Uganda is: Úganda it: Uganda ja: ウガンダ jv: Uganda ka: უგანდა kg: Uganda ki: Uganda kk: Уганда kl: Uganda kn: ಉಗಾಂಡ ko: 우간다 ku: Ûganda kw: Ouganda la: Uganda lb: Uganda lg: Yuganda li: Oeganda ln: Uganda lt: Uganda lv: Uganda mi: Ukānga mk: Уганда ml: ഉഗാണ്ട mn: Уганда mr: युगांडा ms: Uganda mt: Uganda my: ယူဂန်းဒါးနိုင်ငံ nl: Oeganda "no": Uganda nv: Yogénda oc: Oganda or: ଉଗାଣ୍ଡା os: Угандæ pa: ਯੁਗਾਂਡਾ pl: Uganda pt: Uganda qu: Uganda rm: Uganda ro: Uganda ru: Уганда rw: Ubugande sc: Uganda se: Uganda sg: Ugandäa sh: Uganda sk: Uganda sl: Uganda sn: Uganda so: Yugandha sq: Uganda sr: Уганда ss: IBuganda su: Uganda sv: Uganda sw: Uganda ta: உகாண்டா te: ఉగాండా tg: Уганда th: ประเทศยูกันดา tk: Uganda tl: Uganda tr: Uganda ts: Uganda tt: Уганда ug: ئۇگاندا uk: Уганда ur: یوگنڈا uz: Uganda vi: Uganda vo: Lugandayän wo: Ugandaa yi: אוגאנדע yo: Ùgándà zh: 乌干达 zu: IYuganda ================================================ FILE: settings/country-names/us.yaml ================================================ short_name: default: USA name: default: United States ab: Америка Еиду Аштатқәа af: Verenigde State van Amerika am: የተባበሩት የአሜሪካ ግዛቶች an: Estatos Unitos d'America ar: الولايات المتّحدة الأمريكيّة as: মাৰ্কিন যুক্তৰাষ্ট্ৰ av: Америкалъул Цолъарал Штатал ay: Istadus Unidus az: Amerika Birləşmiş Ştatları ba: Америка Ҡушма Штаттары be: Злучаныя Штаты Амерыкі bg: Съединени американски щати bi: Yunaeted Stet blong Amerika bm: Amerika ka Kelenyalen Jamanaw bn: মার্কিন যুক্তরাষ্ট্র bo: ཨ་མེ་རི་ཁ་རྒྱལ་ཕྲན་མཉམ་འབྲེལ་རྒྱལ་ཁབ། br: Stadoù-Unanet Amerika bs: Sjedinjene Američke Države ca: Estats Units d'Amèrica ce: Iамерка пачхьалк co: Stati Uniti d'America cs: Spojené státy americké cu: Амєрика́ньскꙑ Ѥдьнѥнꙑ́ Дрьжа́вꙑ cv: Америкăри Пĕрлешӳллĕ Штатсем cy: Unol Daleithiau America da: Amerikas Forenede Stater de: Vereinigte Staaten von Amerika dv: އެމެރިކާ dz: ཡུ་ནའིཊེཊ་སི་ཊེས ee: United States el: Ηνωμένες Πολιτείες της Αμερικής en: United States eo: Usono es: Estados Unidos de América et: Ameerika Ühendriigid eu: Ameriketako Estatu Batuak fa: ایالات متحده آمریکا ff: Dowlaaji Dentuɗi fi: Amerikan yhdysvallat fo: Sambandsríkið Amerika fr: États-Unis d'Amérique fy: Feriene Steaten fan Amearika ga: Stáit Aontaithe Mheiriceá gd: Na Stàitean Aonaichte gl: Estados Unidos de América gn: Tetã peteĩ reko Amérikagua gu: સંયુક્ત રાજ્ય અમેરિકા gv: Steatyn Unnaneysit America ha: kunkiyar taraiyar Amurika he: ארצות הברית hi: संयुक्त राज्य अमेरिका hr: Sjedinjene Američke Države ht: Etazini hu: Amerikai Egyesült Államok hy: Ամերիկայի Միացյալ Նահանգներ ia: Statos Unite de America id: Amerika Serikat ie: Unit States de America ig: Njikota Obodo Amerika ik: United States of America io: Unionita Stati di Amerika is: Bandaríki Norður-Ameríku it: Stati Uniti d'America iu: ᐊᒥᐊᓕᑲ ja: アメリカ合衆国 jv: Amérika Sarékat ka: ამერიკის შეერთებული შტატები ki: United States kk: Америка Құрама Штаттары kl: Naalagaaffeqatigiit km: សហរដ្ឋអាមេរិក kn: ಅಮೇರಿಕ ಸಂಯುಕ್ತ ಸಂಸ್ಥಾನ ko: 미국 ks: संयुक्त राज्य अमेरिका ku: Dewletên Yekbûyî yên Amerîkayê kv: Америкаса Ӧтувтӧм Штатъяс kw: Statys Unys Amerika ky: Америка Кошмо Штаттары la: Civitates Foederatae Americae lb: Vereenegt Staate vun Amerika lg: Amereka li: Vereinegde State van Amerika ln: Lisangá lya Ameríka lo: ສະຫະລັດອາເມລິກາ lt: Jungtinės Amerikos Valstijos lv: Amerikas Savienotās Valstis mg: Etazonia mi: Amerika mk: Соединети Американски Држави ml: അമേരിക്കന് ഐക്യനാടുകള് mn: Америкийн Нэгдсэн Улс mo: Стателе Уните але Америчий mr: अमेरिकेची संयुक्त संस्थाने ms: Amerika Syarikat mt: Stati Uniti tal-Amerika my: အမေရိကန်ပြည်ထောင်စု na: Eben Merika nb: USA ne: संयुक्त राज्य अमेरिका nl: Verenigde Staten nn: USA "no": Amerikas forente stater nv: Wááshindoon Bikéyah Ałhidadiidzooígíí oc: Estats Units d'America om: USA or: ଯୁକ୍ତରାଷ୍ଟ୍ର ଆମେରିକା os: Америкæйы Иугонд Штаттæ pa: ਸੰਯੁਕਤ ਰਾਜ ਅਮਰੀਕਾ pl: Stany Zjednoczone ps: د امريکا متحده ايالات pt: Estados Unidos da América qu: Hukllachasqa Amirika Suyukuna rm: Stadis Unids da l'America rn: Leta Zunze Ubumwe za Amerika ro: Statele Unite ale Americii ru: Соединённые Штаты Америки rw: Leta Zunze Ubumwe z’Amerika sa: संयुक्तानि राज्यानि sc: Istados Unidos de America sd: آمريڪا se: Amerihká ovttastuvvan stáhtat sg: ÂKödörö-ôko tî Amerîka sh: Sjedinjene Američke Države si: අ'මෙරිකාවේ එක්සත් රාජ්යයන් sk: Spojené štáty americké sl: Združene države Amerike sm: Iunaite Sitete o Amerika sn: United States of America so: Mareykanka sq: Shtetet e Bashkuara të Amerikës sr: Сједињене Америчке Државе ss: IMelika su: Amérika Sarikat sv: Amerikas Förenta Stater sw: Muungano wa Madola ya Amerika ta: அமெரிக்க ஐக்கிய நாடு te: అమెరికా సంయుక్త రాష్ట్రాలు tg: Иёлоти Муттаҳидаи Амрико th: สหรัฐอเมริกา tk: Amerikanyň Birleşen Ştatlary tl: Estados Unidos tn: USA to: Puleʻanga Fakataha 'o 'Amelika tr: Amerika Birleşik Devletleri ts: United States tt: Америка Кушма Штатлары tw: USA ty: Fenua Marite ug: ئامېرىكا قوشما شتاتلىرى uk: Сполучені Штати Америки ur: ریاستہائے متحدہ امریکہ uz: Amerika Qoʻshma Shtatlari vi: Hoa Kỳ vo: Lamerikän wa: Estats Unis d' Amerike wo: Diwaan-yu-Bennoo yu Aamerig xh: IYunayithedi Steyitsi yi: פאראייניקטע שטאטן פון אמעריקע yo: Àwọn Ìpínlẹ̀ Aṣọ̀kan Amẹ́ríkà za: Meijgoz zh: 美利坚合众国/美利堅合眾國 zu: IMelika ================================================ FILE: settings/country-names/uy.yaml ================================================ name: default: Uruguay af: Uruguay ak: Yurugwae am: ኡራጓይ an: Uruguai ar: أوروغواي ay: Uruwayi az: Uruqvay ba: Уругвай be: Уругвай bg: Уругвай bi: Uruguay bm: Urugwayi bn: উরুগুয়ে bo: ཨུ་རུ་གུ་ཡེ། br: Uruguay bs: Urugvaj ca: Uruguai ce: Уругвай co: Uruguay cs: Uruguay cu: Оуроугваи cv: Уругвай cy: Uruguay da: Uruguay de: Uruguay dv: އުރުގުއޭ dz: ཡུ་རུ་གུ་ཝའི ee: uruguaydukɔ el: Ουρουγουάη en: Uruguay eo: Urugvajo es: Uruguay et: Uruguay eu: Uruguai fa: اروگوئه ff: Uruguwaay fi: Uruguay fo: Uruguei fr: Uruguay fy: Urûguay ga: Uragua gd: Uruguaidh gl: Uruguai gn: Uruguái gu: ઉરુગ્વે gv: Yn Ooraguay ha: Yurugai he: אורוגוואי hi: उरुग्वे hr: Urugvaj ht: Irigwe hu: Uruguay hy: Ուրուգվայ ia: Uruguay id: Uruguay ie: Uruguay io: Uruguay is: Úrúgvæ it: Uruguay ja: ウルグアイ jv: Uruguay ka: ურუგვაი ki: Urugwai kk: Уругвай kl: Uruguay km: អ៊ុយរុយហ្គាយ kn: ಉರುಗ್ವೆ ko: 우루과이 ks: یوٗروگے ku: Ûrûguay kw: Urugway la: Uraquaria lb: Uruguay lg: Wurugwayi li: Urugay ln: Ulugwai lo: ອຸລຸກວຍ lt: Urugvajus lv: Urugvaja mg: Orogoay mi: Urukoi mk: Уругвај ml: ഉറുഗ്വേ mn: Уругвай mr: उरुग्वे ms: Uruguay mt: Urugwaj my: ဥရုဂွေးနိုင်ငံ ne: उरुग्वाइ nl: Uruguay "no": Uruguay nv: Táłtłʼááh Chʼosh Bitooh (Kéyah Dah Siʼánígíí) oc: Uruguai or: ଉରୁଗୁଏ os: Уругвай pa: ਉਰੂਗੁਏ pl: Urugwaj ps: یوروګوای pt: Uruguai qu: Uruwayi rm: Uruguay rn: Irigwe ro: Uruguay ru: Уругвай rw: Irigwe sa: उरुग्वाय sc: Uruguay se: Uruguay sg: Uruguëe sh: Urugvaj si: උරුගුවායි sk: Uruguaj sl: Urugvaj so: Uruguwaay sq: Uruguaji sr: Уругвај su: Uruguay sv: Uruguay sw: Urugwai ta: உருகுவை te: ఉరుగువే tg: Уругуай th: ประเทศอุรุกวัย ti: ኡራጓይ tl: Urugway to: ʻUlukuei tr: Uruguay tt: Уругвай ug: ئۇرۇگۋاي uk: Уругвай ur: یوراگوئے uz: Urugvay vi: Uruguay vo: Luruguyän wa: Ourougway wo: Uruguwaay yi: אורוגוויי yo: Urugúáì zh: 乌拉圭 zu: i-Uruguay ================================================ FILE: settings/country-names/uz.yaml ================================================ name: default: Oʻzbekiston af: Oesbekistan am: ኡዝቤኪስታን an: Uzbekistán ar: أوزبكستان az: Özbəkistan ba: Үзбәкстан be: Узбекістан bg: Узбекистан bn: উজবেকিস্তান bo: ཨུ་ཟ་བྷེ་ཁི་སི་ཐན། br: Ouzbekistan bs: Uzbekistan ca: Uzbekistan ce: Узбекистан cs: Uzbekistán cv: Ӳспекстан cy: Uzbekistan da: Usbekistan de: Usbekistan dv: އުޒްބެކިސްތާން dz: ཨུཛ་བེ་ཀིསི་ཏཱན་ ee: Uzbekistan el: Ουζμπεκιστάν en: Uzbekistan eo: Uzbekio es: Uzbekistán et: Usbekistan eu: Uzbekistan fa: ازبکستان fi: Uzbekistan fo: Usbekistan fr: Ouzbékistan fy: Oezbekistan ga: An Úisbéiceastáin gd: Usbagastàn gl: Uzbequistán gu: ઉઝબેકિસ્તાન gv: Yn Oosbeckistaan he: אוזבקיסטן hi: उज़्बेकिस्तान hr: Uzbekistan ht: Ouzbekistan hu: Üzbegisztán hy: Ուզբեկստան ia: Uzbekistan id: Uzbekistan ie: Uzbekistan io: Uzbekistan is: Úsbekistan it: Uzbekistan ja: ウズベキスタン jv: Uzbekistan ka: უზბეკეთი kk: Өзбекстан kn: ಉಜ್ಬೇಕಿಸ್ಥಾನ್ ko: 우즈베키스탄 ku: Ûzbêkistan kv: Узбекистан kw: Pow Ousbek ky: Өзбекстан la: Uzbecia lb: Usbekistan li: Oesbekistan ln: Uzbekistáni lt: Uzbekija lv: Uzbekistāna mk: Узбекистан ml: ഉസ്ബെക്കിസ്ഥാൻ mn: Узбекистан mr: उझबेकिस्तान ms: Uzbekistan my: ဥဇဘက်ကစ္စတန်နိုင်ငံ na: Uzbekistan nl: Oezbekistan "no": Usbekistan nv: Ózbeʼ Bikéyah oc: Ozbequistan or: ଉଜବେକିସ୍ଥାନ os: Узбекистан pa: ਉਜ਼ਬੇਕਿਸਤਾਨ pl: Uzbekistan ps: اوزبکستان pt: Uzbequistão qu: Usbiksuyu ro: Uzbekistan ru: Узбекистан rw: Uzubekisitani sa: उजबेकिस्थान sd: ازبڪستان se: Uzbekistan sh: Uzbekistan sk: Uzbekistan sl: Uzbekistan so: Usbekistan sq: Uzbekistani sr: Узбекистан ss: IZubekhi su: Uzbékistan sv: Uzbekistan sw: Uzbekistan ta: உசுபெக்கிசுத்தான் te: ఉజ్బెకిస్తాన్ tg: Ӯзбакистон th: ประเทศอุซเบกิสถาน tk: Özbegistan tl: Uzbekistan tr: Özbekistan tt: Үзбәкстан ug: ئۆزبېكىستان uk: Узбекистан ur: ازبکستان uz: Oʻzbekiston vi: Uzbekistan vo: Lusbekän wo: Usbekistaan yi: אוזבעקיסטאן yo: Ùsbẹ̀kìstán zh: 乌兹别克斯坦 ================================================ FILE: settings/country-names/va.yaml ================================================ name: default: Civitas Vaticana af: Vatikaanstad am: ቫቲካን ከተማ an: Ciudat d'o Vaticano ar: الفاتيكان az: Vatikan ba: Ватикан be: Ватыкан bg: Ватикан bi: Vatican Siti bn: ভ্যাটিকান সিটি bo: ཝ་ཏི་ཀན་གྲོང་ཁྱོར། br: Keoded ar Vatikan bs: Vatikan ca: Ciutat del Vaticà ce: Ватикан co: Cità di u Vaticanu cs: Vatikán cu: Ватиканъ cv: Ватикан cy: Y Fatican da: Vatikanstaten de: Staat Vatikanstadt dv: ވެޓިކަން ސިޓީ dz: བ་ཊི་ཀཱན། ee: Vatican City el: Βατικανό en: Vatican City eo: Vatikano es: Ciudad del Vaticano et: Vatikan eu: Vatikano Hiria fa: واتیکان fi: Vatikaanivaltio fo: Vatikanið fr: Cité du Vatican fy: Fatikaanstêd ga: Cathair na Vatacáine gd: Cathair na Bhatacain gl: Cidade do Vaticano gn: Táva Vatikano gv: Ard-valley yn Phaab he: קריית הוותיקן hi: वैटिकन सिटी hr: Vatikan ht: Vatikan hu: Vatikán hy: Վատիկան ia: Citate Vatican id: Vatikan ie: Vaticano io: Vatikano is: Vatíkanið it: Città del Vaticano ja: バチカン jv: Vatikan ka: ვატიკანი kg: Vatican kk: Ватикан kl: Vatikani km: បុរីវ៉ាទីកង់ kn: ವ್ಯಾಟಿಕನ್ ನಗರ ko: 바티칸 시국 ku: Vatîkan kv: Ватикан kw: Cita Vatikan ky: Ватикан la: Status Civitatis Vaticanæ lb: Vatikanstad li: Vaticaanstad ln: Vatikáni lt: Vatikanas lv: Vatikāns mi: Poho o Pita mk: Ватикан ml: വത്തിക്കാൻ നഗരം mn: Ватикан mr: व्हॅटिकन सिटी ms: Kota Vatican mt: Belt tal-Vatikan my: ဗာတီကန်စီးတီး na: Batikan ne: भ्याटिकन सिटी nl: Vaticaanstad "no": Vatikanstaten nv: Bádikin Sídii oc: Vatican or: ଭାଟିକାନ ସିଟି os: Ватикан pa: ਵੈਟੀਕਨ ਸ਼ਹਿਰ pl: Watykan ps: واټيکان ښار pt: Vaticano qu: Watikanu llaqta rm: Citad dal Vatican ro: Vatican ru: Ватикан rw: Vatikani sa: वैटिकन sc: Tzitade de su Vaticanu se: Vatikána sh: Vatikan si: වතිකානුව sk: Vatikán sl: Vatikan so: Faatikan sq: Vatikani sr: Ватикан ss: IVathikhi su: Vatikan sv: Vatikanstaten sw: Vatikani ta: வத்திக்கான் நகர் te: వాటికన్ నగరం tg: Вотикон th: นครรัฐวาติกัน tk: Watikan tl: Lungsod ng Vaticano tr: Vatikan tt: Ватикан ug: Watikan Shehiri uk: Ватикан ur: ویٹیکن سٹی uz: Vatikan shahri vi: Thành Vatican vo: Vatikän wo: Watikaa yi: וואטיקאן yo: Ìlú Fatikan zh: 梵蒂冈 zu: Indolobha yaseVathikhani ================================================ FILE: settings/country-names/vc.yaml ================================================ name: default: Saint Vincent and the Grenadines af: Sint Vincent en die Grenadines ar: سانت فنسنت وجزر غرينادين be: Святы Вінцэнт і Грэнадзіны br: Sant-Visant hag ar Grenadinez ca: Saint Vincent i les Grenadines cs: Svatý Vincent a Grenadiny cy: Saint Vincent a'r Grenadines da: Saint Vincent og Grenadinerne de: St. Vincent und die Grenadinen el: Άγιος Βικέντιος και Γρεναδίνες en: Saint Vincent and the Grenadines eo: Sankta Vincento kaj Grenadinoj es: San Vicente y las Granadinas fa: سنت وینسنت و گرنادین fi: Saint Vincent ja Grenadiinit fr: Saint-Vincent-et-les Grenadines fy: Sint Finsint en de Grenadinen ga: San Uinseann agus na Greanáidíní gd: Naomh Bhionsant agus Eileanan Greanadach he: סנט וינסנט והגרנדינים hr: Sveti Vincent i Grenadini hu: Saint Vincent és a Grenadine-szigetek id: Saint Vincent dan Grenadines is: Sankti Vinsent og Grenadíneyjar it: Saint Vincent e Grenadine ja: セントビンセント及びグレナディーン諸島 lb: Saint Vincent an d’Grenadinen li: Saint-Vincent lt: Sent Vinsentas ir Grenadinai mn: Сент-Винсент ба Гренадин nb: Saint Vincent og Grenadinene nl: Saint Vincent en de Grenadines nn: Saint Vincent og Grenadinane "no": Saint Vincent og Grenadinene pl: Saint Vincent i Grenadyny pt: São Vicente e Granadinas ru: Сент-Винсент и Гренадины se: Saint Vincent ja Grenadiinnat sk: Svätý Vincent a Grenadíny sl: Sveti Vincencij in Grenadine sv: Saint Vincent och Grenadinerna ta: செயின்ட் வின்செண்டு மற்றும் கிரெனடீன்கள் th: ประเทศเซนต์วินเซนต์และเกรนาดีนส์ tr: Saint Vincent ve Grenadinler uk: Сент-Вінсент і Гренадини vi: Saint Vincent và Grenadines vo: Saluda-Vinsenteän e Grenadineäns zh: 圣文森特和格林纳丁斯 ================================================ FILE: settings/country-names/ve.yaml ================================================ name: default: Venezuela af: Venezuela am: ቬኔዝዌላ an: Venezuela ar: فنزويلا av: Венесуэла ay: Winïxwila az: Venesuela ba: Венесуэла be: Венесуэла bg: Венецуела bi: Venezuela bm: Venezuela bn: ভেনেজুয়েলা bo: ཝེ་ནེ་ཟུའེ་ལ། br: Venezuela bs: Venecuela ca: Veneçuela ce: Венесуэла co: Venezuela cs: Venezuela cv: Венесуэла cy: Venezuela da: Venezuela de: Venezuela dv: ވެނެޒުއޭލާ ee: Venezuela el: Βενεζουέλα en: Venezuela eo: Venezuelo es: Venezuela et: Venezuela eu: Venezuela fa: ونزوئلا fi: Venezuela fo: Venesuela fr: Venezuela fy: Fenezuëla ga: Veiniséala gd: A' Bheiniseala gl: Venezuela gn: Venesuéla gu: વેનેઝુએલા gv: Yn Veneswaaley he: ונצואלה hi: वेनेज़ुएला hr: Venezuela ht: Venezwela hu: Venezuela hy: Վենեսուելա ia: Venezuela id: Venezuela ie: Venezuela io: Venezuela is: Venesúela it: Venezuela ja: ベネズエラ jv: Venezuela ka: ვენესუელა kk: Венесуэла kn: ವೆನೆಜುವೆಲಾ ko: 베네수엘라 ku: Venezuela kw: Veneswela la: Venetiola lb: Venezuela li: Venezuela ln: Venezwela lt: Venesuela lv: Venecuēla mg: Venezoela mi: Wenehūera mk: Венецуела ml: വെനിസ്വേല mn: Венесуэл mr: व्हेनेझुएला ms: Venezuela mt: Veneżwela my: ဗင်နီဇွဲလားနိုင်ငံ ne: भेनेजुएला nl: Venezuela "no": Venezuela nv: Táłkááʼ Bighan Dineʼé Bikéyah oc: Veneçuèla or: ଭେନେଜୁଏଲା os: Венесуэлæ pa: ਵੈਨੇਜ਼ੁਏਲਾ pl: Wenezuela pt: Venezuela qu: Winisuyla rm: Venezuela rn: Venezuela ro: Venezuela ru: Венесуэла rw: Venezuwela sa: वेनेज्वेला se: Venezuela sh: Venezuela sk: Venezuela sl: Venezuela sm: Venesuela so: Fanansuwela sq: Venezuela sr: Венецуела su: Vénézuéla sv: Venezuela sw: Venezuela ta: வெனிசுவேலா tg: Венесуэла th: ประเทศเวเนซุเอลา tk: Wenesuela tl: Venezuela tr: Venezuela tt: Венесуэла ug: ۋېنېسۇئېلا uk: Венесуела ur: وینیزویلا uz: Venesuela ve: Venezuela vi: Venezuela vo: Venesolän wa: Venezwela wo: Benesuwela yi: ווענעזועלע yo: Fenesuela zh: 委內瑞拉 zu: Venezuela ================================================ FILE: settings/country-names/vg.yaml ================================================ name: default: British Virgin Islands af: Brits-Maagde-eilande ar: جزر فيرجن البريطانية be: Брытанскія Вірджынскія астравы br: Inizi Gwerc’h Breizhveuriat ca: Illes Verges Britàniques cs: Britské Panenské ostrovy da: Britiske Jomfruøer de: Britische Jungferninseln en: British Virgin Islands eo: Britaj Virgulinsuloj es: Islas Vírgenes Británicas et: Briti Neitsisaared eu: Birjina uharte britainiarrak fa: جزایر ویرجین بریتانیا fi: Brittiläiset Neitsytsaaret fr: Îles Vierges britanniques ga: Oileáin Bhriotanacha na Maighdean he: איי הבתולה הבריטיים hr: Britanski Djevičanski Otoci hu: Brit Virgin-szigetek id: Kepulauan Virgin Britania Raya is: Bresku Jómfrúaeyjar it: Isole Vergini Britanniche ja: イギリス領ヴァージン諸島 la: Britannicae Virginis Insulae lb: Britesch Joffereninselen lt: Britų Mergelių salos lv: Britu Virdžīnu salas mk: Британски Девствени Острови mn: Виржиний Арлууд, Британийн nb: De britiske jomfruøyene nl: Britse Maagdeneilanden nn: Dei britiske Jomfruøyane "no": De britiske jomfruøyene pl: Brytyjskie Wyspy Dziewicze pt: Ilhas Virgens Britânicas ru: Британские Виргинские острова sk: Britské Panenské ostrovy sl: Britanski Deviški otoki sr: Британска Девичанска Острва sv: Brittiska Jungfruöarna th: หมู่เกาะบริติชเวอร์จิน tr: Birleşik Krallık Virgin Adaları uk: Британські Віргінські острови vi: Quần đảo Virgin thuộc Anh zh: 英属维尔京群岛 ================================================ FILE: settings/country-names/vn.yaml ================================================ name: default: Việt Nam af: Viëtnam ak: Vietnam am: ቬት ናም an: Vietnam ar: فيتنام av: Вьетнам ay: Vietnam az: Vyetnam ba: Вьетнам be: В'етнам bg: Виетнам bi: Vietnam bm: Vietnam bn: ভিয়েতনাম bo: ཝི་ཏི་ནམ། br: Viêt Nam bs: Vijetnam ca: Vietnam ce: Вьетнам ch: Vietnam cs: Vietnam cv: Вьетнам cy: Fietnam da: Vietnam de: Vietnam dv: ވިއެޓުނާމު ee: Vietnam el: Βιετνάμ en: Vietnam eo: Vjetnamio es: Vietnam et: Vietnam eu: Vietnam fa: ویتنام ff: Vietnam fi: Vietnam fo: Vjetnam fr: Viêt Nam fy: Fjetnam ga: Vítneam gd: Bhiet-Nam gl: Vietnam gn: Vietnam gu: વિયેતનામ gv: Yn Vietnam he: וייטנאם hi: वियतनाम hr: Vijetnam ht: Vyetnam hu: Vietnám hy: Վիետնամ ia: Vietnam id: Vietnam ie: Viet-Nam ik: Vietnam io: Vietnam is: Víetnam it: Vietnam ja: ベトナム jv: Viètnam ka: ვიეტნამი kg: Vietnam ki: Vietnam kk: Вьетнам kl: Vietnam km: វៀតណាម kn: ವಿಯೆಟ್ನಾಮ್ ko: 베트남 ku: Viyetnam kv: Вьетнам kw: Vietnam ky: Вьетнам la: Vietnamia lb: Vietnam li: Vietnam ln: Vietnami lo: ປະເທດຫວຽດນາມ lt: Vietnamas lv: Vjetnama mg: Vietnam mi: Witināma mk: Виетнам ml: വിയറ്റ്നാം mn: Вьетнам mr: व्हियेतनाम ms: Vietnam mt: Vjetnam my: ဗီယက်နမ်နိုင်ငံ na: Bitinam ne: भियतनाम nl: Vietnam "no": Vietnam nv: Biʼednam oc: Vietnam om: Veetinaam or: ଭିଏତନାମ os: Вьетнам pa: ਵੀਅਤਨਾਮ pl: Wietnam pt: Vietname qu: Witnam rm: Vietnam rn: Vietnam ro: Vietnam ru: Вьетнам rw: Viyetinamu sa: चम्पादेश sc: Vietnam se: Vietnam sh: Vijetnam si: වියට්නාමය sk: Vietnam sl: Vietnam sm: Vietnam sn: Vietnam so: Fiyetnam sq: Vietnami sr: Вијетнам ss: IViyethina st: Vietnam su: Viétnam sv: Vietnam sw: Vietnam ta: வியட்நாம் te: వియత్నాం tg: Ветнам th: ประเทศเวียดนาม tk: Wýetnam tl: Vietnam tr: Vietnam tt: Вьетнам tw: Vietnam ug: ۋيېتنام uk: В'єтнам ur: ویتنام uz: Vyetnam ve: Viëtnam vi: Việt Nam vo: Vietnamän wa: Vietnam wo: Wiyetnaam yi: וויעטנאם yo: Fiẹtnám za: Yiednamz zh: 越南 zu: IViyetnami ================================================ FILE: settings/country-names/vu.yaml ================================================ name: default: Vanuatu ab: Вануату ar: فانواتو be: Вануату bg: Вануату bn: ভানুয়াটু bo: ཝ་ནུའ་ཏུ། br: Vanuatu ce: Вануату cs: Vanuatu da: Vanuatu de: Vanuatu dv: ވަނުއާޓޫ el: Βανουάτου en: Vanuatu eo: Vanuatuo es: Vanuatu fa: وانواتو fi: Vanuatu fr: Vanuatu fy: Fanuatû ga: Vanuatú gd: Bhanuatu he: ונואטו hi: वानूअतु hr: Vanuatu ht: Vanwatou hu: Vanuatu hy: Վանուատու is: Vanúatú ja: バヌアツ ka: ვანუატუ kk: Вануату kn: ವನುವಾಟು ko: 바누아투 ku: Vanûatû ky: Вануату lt: Vanuatu mk: Вануату ml: വാനുവാടു mn: Вануату mr: व्हानुआतू mt: Vanwatu my: ဗနွားတူနိုင်ငံ na: Banuatu ne: भानुअटु "no": Vanuatu nv: Banoʼáátʼoo oc: Vanuatu or: ଭାନୁଆଟୁ os: Вануату pa: ਵਨੁਆਤੂ pl: Vanuatu pt: Vanuatu qu: Wanwatu ru: Вануату rw: Vanuwatu se: Vanuatu sk: Vanuatu sr: Вануату sv: Vanuatu ta: வனுவாட்டு te: వనాటు tg: Вануату th: ประเทศวานูอาตู tt: Вануату ug: ۋانۇئاتۇ uk: Вануату ur: وانواتو vo: Vanuatuäns wo: Wanuatu yo: Fanuatu zh: 瓦努阿图 ================================================ FILE: settings/country-names/ws.yaml ================================================ name: default: Sāmoa am: ሳሞአ ar: ساموا be: Самоа bg: Самоа bm: Samowa bn: সামোয়া bo: ནུ་བ་ས་མོ་འ། br: Samoa ca: Samoa cs: Samoa da: Samoa de: Samoa dz: ས་མོ་ཨ ee: Samoa nutome el: Σαμόα en: Samoa eo: Samoo es: Samoa et: Samoa fa: ساموآ ff: Samowaa fi: Samoa fo: Sámoa fr: Samoa fy: Samoä ga: Samó gd: Samotha gu: સમોઆ gv: Samoa ha: Samowa he: סמואה hi: समोआ hr: Samoa hu: Szamoa hy: Սամոա ia: Samoa io: Samoa is: Samóa it: Samoa ja: サモア ka: სამოა km: សាមូអា kn: ಸಮೋವಾ ko: 사모아 ks: سیمووا ku: Samoa lg: Samowa li: Samoa lo: ຊາມົວ lv: Samoa mi: Hāmoa mk: Самоа ml: സാമോവ mn: Самоа mr: सामोआ ne: सामोआ nl: Samoa "no": Samoa or: ସାମୋଆ pl: Samoa rn: Samowa ru: Самоа se: Samoa sg: Samoäa si: සැමෝවා sk: Samoa sl: Samoa sr: Самоа sv: Samoa ta: சமோவா te: సమోవా th: ประเทศซามัว ti: ሳሞአ to: Haʻamoa uk: Самоа ur: ساموآ vo: Samovuäns yo: Orílẹ́ède Samọ zh: 萨摩亚 zu: i-Samoa ================================================ FILE: settings/country-names/xk.yaml ================================================ name: default: Kosova / Kosovo af: Kosovo am: ኮሶቮ an: Kosovo ar: كوسوفو ay: Kusuwu az: Kosovo ba: Косово be: Косава bg: Косово bn: কসোভো bo: ཁོ་སོ་ཝོ། br: Kosovo bs: Kosovo ca: Kosovo ce: Косово cs: Kosovo cv: Косово cy: Cosofo da: Kosovo de: Kosovo el: Κοσσυφοπέδιο en: Kosovo eo: Kosovo es: Kosovo et: Kosovo eu: Kosovo fa: کوزوو fi: Kosovo fo: Kosovo fr: Kosovo fy: Kosovo ga: An Chosaiv gd: Cosobho gl: Kosovo gn: Kosovo gv: Kosovo he: קוסובו hi: कोसोवो गणराज्य hr: Kosovo hu: Koszovó hy: Կոսովոն ia: Kosovo id: Kosovo ie: Kosovo ig: Kosovo io: Kosovo is: Kosóvó it: Kosovo ja: コソボ jv: Kosovo ka: კოსოვო kk: Косово kl: Kosovo kn: ಕೊಸೊವೊ ko: 코소보 ku: Kosova kv: Косово kw: Kosova ky: Косово la: Kosovia lb: Kosovo li: Kosovo lo: ປະເທດໂກໂຊໂວ lt: Kosovas lv: Kosova mk: Косово ml: കൊസോവോ mn: Косово mr: कोसोव्हो ms: Kosovo mt: Kosovo my: ကိုဆိုဗို na: Kosovo nl: Kosovo "no": Kosovo ny: Kosovo oc: Kosova os: Косово pa: ਕੋਸੋਵੋ ਗਣਰਾਜ pl: Kosowo pt: Kosovo qu: Kusuwu rm: Cosovo ro: Kosovo ru: Косово rw: Kosovo sc: Kòssovu sh: Kosovo si: කොසෝවො sk: Kosovo sl: Kosovo sm: Kosovo sn: Kosovo so: Kosofo sq: Kosovo sr: Косово su: Kosovo sv: Kosovo sw: Kosovo ta: கொசோவோ te: కొసావో th: ประเทศคอซอวอ tl: Kosovo tr: Kosova ts: Kosovo tt: Косово Җөмһүрияте uk: Косово ur: کوسووہ uz: Kosovo vi: Kosovo vo: Kosovän yo: Kósófò zh: 科索沃 ================================================ FILE: settings/country-names/ye.yaml ================================================ name: default: اليمن af: Jemen am: የመን (አገር) an: Yemen ar: اليمن az: Yəmən ba: Йемен be: Емен bg: Йемен bn: ইয়েমেন bo: ཡེ་མེན། br: Yemen bs: Jemen ca: Iemen ce: Йемен co: Yemen cs: Jemen cv: Йемен cy: Yemen da: Yemen de: Jemen dv: ޔަމަން el: Υεμένη en: Yemen eo: Jemeno es: Yemen et: Jeemen eu: Yemen fa: یمن fi: Jemen fo: Jemen fr: Yémen fy: Jemen ga: Poblacht Éimin gd: Iemen gl: Iemen - اليمن gn: Jemẽ gu: યેમેન gv: Yn Yeaman he: תימן hi: यमन hr: Jemen ht: Yemèn hu: Jemen hy: Եմեն ia: Yemen id: Yaman ie: Yemen io: Yemen is: Jemen it: Yemen ja: イエメン jv: Yaman ka: იემენი ki: Yemen kk: Йемен kl: Yemen kn: ಯೆಮೆನ್ ko: 예멘 ks: یَمَن ku: Yemen kv: Йемен kw: Yemen ky: Йемен la: Iemenia lb: Jemen li: Jeme ln: Yeméni lt: Jemenas lv: Jemena mk: Јемен ml: യെമൻ mn: Йемен mr: येमेनचे प्रजासत्ताक ms: Yaman mt: Jemen my: ယီမင်နိုင်ငံ na: Yemen ne: गणतन्त्र यमन nl: Jemen "no": Jemen nv: Shádiʼááhjí Ásáí Bikéyah oc: Iemèn or: ୟେମେନ os: Йемен pa: ਯਮਨ pl: Jemen ps: یمن pt: Iémen qu: Yaman ro: Yemen ru: Йемен rw: Yemeni sa: यमन sd: يمن se: Jemen sh: Jemen sk: Jemen sl: Jemen so: Yemen sq: Jemeni sr: Јемен ss: IYemeni su: Yaman sv: Jemen sw: Yemen ta: யெமன் te: యెమన్ tg: Яман th: ประเทศเยเมน tk: Ýemen tl: Yemen tr: Yemen tt: Йәмән ug: يەمەن uk: Ємен ur: یمن uz: Yaman vi: Yemen vo: Yämän wo: Yaman yi: תימן yo: Yemen zh: 也门 zu: IYemen ================================================ FILE: settings/country-names/za.yaml ================================================ name: default: South Africa af: Suid-Afrika ak: Afrika Anaafo am: ደቡብ አፍሪካ an: Sudafrica ar: جنوب أفريقيا az: Cənub Afrika be: Паўднёва-Афрыканская Рэспубліка bg: Южна Африка bm: Worodugu Afriki bn: দক্ষিণ আফ্রিকা bo: ལྷོ་ ཨཕྲི་ཀ། br: Suafrika bs: Južna Afrika ca: Sud-àfrica ce: Къилба-Африкин Республика cs: Jižní Afrika cv: Кăнтăр Африка cy: De Affrica da: Sydafrika de: Südafrika dv: ދެކުނު އެފްރިކާ dz: སཱའུཐ་ ཨཕ་རི་ཀ ee: Anyiehe Afrika nutome el: Νότια Αφρική en: South Africa eo: Sud-Afriko es: Sudáfrica et: Lõuna-Aafrika Vabariik eu: Hegoafrika fa: آفریقای جنوبی ff: Afrik bŋ Worgo fi: Etelä-Afrikka fo: Suðurafrika fr: Afrique du Sud fy: Súd-Afrika ga: An Afraic Theas gd: Afraga a Deas gl: Suráfrica gn: Yvyafrika gu: દક્ષિણ આફ્રિકા gv: Yn Affrick Yiass ha: Afirka ta Kudu he: דרום אפריקה hi: दक्षिण अफ़्रीका hr: Južna Afrika ht: Afrik disid hu: Dél-Afrika hy: Հարավային Աֆրիկա ia: Africa del Sud id: Afrika Selatan ie: Sudafrica io: Sud-Afrika is: Suður-Afríka it: Sudafrica ja: 南アフリカ jv: Afrika Kidul ka: სამხრეთ აფრიკა kg: Afelika ya Sudi ki: Abĩrika Kusini kk: Оңтүстік Африка km: អាព្រិក​ភាគ​ខាង​ត្បូង kn: ದಕ್ಷಿಣ ಆಫ್ರಿಕಾ ko: 남아프리카 ks: جَنوٗبی اَفریٖکا ku: Afrîkaya Başûr kv: Лунвыв Африкаса Республика kw: Afrika Dhyhow ky: Африкаанс тили la: Africa Australis lb: Südafrika lg: Sawusafirika li: Zuud-Afrika ln: Sidafríka lo: ອາຟະລິກາໃຕ້ lt: Pietų Afrika lv: Dienvidāfrika mg: Afrika Atsimo mi: Awherika ki te Tonga mk: Јужна Африка ml: ദക്ഷിണാഫ്രിക്ക mn: Өмнөд Африк mr: दक्षिण अफ्रिका ms: Afrika Selatan mt: Afrika t’Isfel my: တောင်အာဖရိက ne: दक्षिण अफ्रिका nl: Zuid-Afrika "no": Sør-Afrika nv: Kéyah Naakai Łizhinii Bikéyah Shádiʼááhjí Siʼánígíí ny: South Africa oc: Sud d'Africa or: ଦକ୍ଷିଣ ଆଫ୍ରିକା os: Хуссар Африкæйы pa: ਦੱਖਣੀ ਅਫ਼ਰੀਕਾ pl: Republika Południowej Afryki ps: سویلي افریقا pt: África do Sul qu: Urin Aphrika rm: Africa dal Sid rn: Afurika y'Epfo ro: Africa de Sud ru: Южная Африка rw: Afurika y’Epfo sa: दक्षिण-आफ्रिका sc: Sudàfrica se: Lulli-Afrihká sg: Afrîka-Mbongo sh: Južna Afrika si: දකුණු අප්‍රිකාව sk: Južná Afrika sl: Južna Afrika sn: South Africa so: Koonfur Afrika sq: Afrika e Jugut sr: Јужна Африка ss: INingizimu Afrika st: Afrika Borwa su: Afrika Kidul sv: Sydafrika sw: Afrika Kusini ta: தென் ஆப்பிரிக்கா te: దక్షిణ ఆఫ్రికా రాజ్యం tg: Африқои Ҷанубӣ th: แอฟริกาใต้ ti: ደቡብ አፍሪካ tk: Günorta Afrika tl: Timog Aprika tn: Aferika Borwa to: ʻAfilika tonga tr: Güney Afrika ts: Afrika-Dzonga tt: Көньяк Африка ug: جەنۇبىي ئافرىقا uk: Південна Африка ur: جنوبی افریقہ uz: Janubiy Afrika ve: Afurika Tshipembe vi: Nam Phi vo: Sulüdafrikän wa: Nonne-Afrike wo: Afrig gu Bëj-saalum xh: uMzantsi Afrika yi: דרום אפריקע yo: Gúúsù Áfríkà zh: 南非 zu: iNingizimu Afrika ================================================ FILE: settings/country-names/zm.yaml ================================================ name: default: Zambia af: Zambië am: ዛምቢያ an: Zambia ar: زامبيا az: Zambiya ba: Замбия be: Замбія bg: Замбия bm: Zanbia bn: জাম্বিয়া bo: ཛམ་བི་ཡ། br: Zambia bs: Zambija ca: Zàmbia ce: Замби cs: Zambie cv: Замби cy: Zambia da: Zambia de: Sambia dv: ޒެމްބިއާ ee: Zambia el: Ζάμπια en: Zambia eo: Zambio es: Zambia et: Sambia eu: Zambia fa: زامبیا fi: Sambia fo: Sambia fr: Zambie fy: Sambia ga: An tSaimbia gd: Saimbia gl: Zambia gv: Yn Tambia he: זמביה hi: ज़ाम्बिया hr: Zambija ht: Zanbi hu: Zambia hy: Զամբիա id: Zambia ie: Zambia io: Zambia is: Sambía it: Zambia ja: ザンビア jv: Zambia ka: ზამბია kg: Zambia kk: Замбия km: ប្រទេសហ្សំប៊ី ko: 잠비아 ku: Zambiya kw: Zambi la: Zambia lb: Sambia li: Zambia ln: Zambia lt: Zambija lv: Zambija mi: Tāmipia mk: Замбија ml: സാംബിയ mn: Замби mr: झांबिया ms: Zambia mt: Żambja my: ဇမ်ဘီယာနိုင်ငံ nl: Zambia "no": Zambia ny: Zambia oc: Zambia or: ଜାମ୍ବିଆ os: Замби pa: ਜ਼ਾਂਬੀਆ pl: Zambia ps: زېمبيا pt: Zâmbia qu: Sambya ro: Zambia ru: Замбия rw: Zambiya sa: जाम्बिया sc: Zàmbia se: Zambia sg: Zambïi sh: Zambija sk: Zambia sl: Zambija sn: Zambia so: Saambiya sq: Zambia sr: Замбија ss: IZambiya su: Zambia sv: Zambia sw: Zambia ta: சாம்பியா te: జాంబియా tg: Замбия th: ประเทศแซมเบีย ti: ዛምቢያ tk: Zambiýa tl: Sambya tn: Zambia tr: Zambiya ts: Zambia tt: Замбия ug: زامبىيە uk: Замбія ur: زیمبیا uz: Zambiya vi: Zambia vo: Zambiyän wo: Saambi yi: זאמביע yo: Sámbíà zh: 赞比亚 zu: IZambiya ================================================ FILE: settings/country-names/zw.yaml ================================================ name: default: Zimbabwe af: Zimbabwe am: ዚምባብዌ an: Zimbabwe ar: زيمبابوي az: Zimbabve ba: Зимбабве be: Зімбабвэ bg: Зимбабве bm: Zimbabwe bn: জিম্বাবুয়ে bo: ཛིམ་བབ་ཝི། br: Zimbabwe bs: Zimbabve ca: Zimbabwe ce: Зимбабве cs: Zimbabwe cv: Зимбабве cy: Zimbabwe da: Zimbabwe de: Simbabwe dv: ޒިމްބާބުވޭ dz: ཛིམ་བབ་ཝེ་ ee: Zimbabwe el: Ζιμπάμπουε en: Zimbabwe eo: Zimbabvo es: Zimbabue et: Zimbabwe eu: Zimbabwe fa: زیمبابوه fi: Zimbabwe fo: Simbabvi fr: Zimbabwe fy: Simbabwe ga: An tSiombáib gd: Siombabue gl: Cimbabue gv: Yn Çhimbabwe he: זימבבואה hi: ज़िम्बाब्वे hr: Zimbabve ht: Zimbabwe hu: Zimbabwe hy: Զիմբաբվե id: Zimbabwe ie: Zimbabwe io: Zimbabwe is: Simbabve it: Zimbabwe ja: ジンバブエ jv: Zimbabwe ka: ზიმბაბვე kg: Zimbabwe ki: Zimbabwe kk: Зимбабве kn: ಜಿಂಬಾಬ್ವೆ ko: 짐바브웨 ku: Zîmbabwe kw: Zimbabwe la: Zimbabua lb: Simbabwe li: Zimbabwe ln: Zimbabwe lt: Zimbabvė lv: Zimbabve mi: Timuwawe mk: Зимбабве ml: സിംബാബ്വെ mn: Зимбабве mr: झिंबाब्वे ms: Zimbabwe mt: Żimbabwe my: ဇင်ဘာဘွေနိုင်ငံ ne: जिम्बाब्वे nl: Zimbabwe "no": Zimbabwe nv: Hooghan Tsé Bee Ádaalyaaí Bikéyah ny: Zimbabwe oc: Zimbabwe or: ଜିମ୍ବାୱେ os: Зимбабве pa: ਜ਼ਿੰਬਾਬਵੇ pl: Zimbabwe ps: زېمبابوې pt: Zimbabué qu: Simbabwi ro: Zimbabwe ru: Зимбабве rw: Zimbabwe sa: जिम्बाबवे sc: Zimbabwe se: Zimbabwe sg: Zimbäbwe sh: Zimbabve si: සිම්බාබ්වේ sk: Zimbabwe sl: Zimbabve sn: Zimbabwe so: Simbaabwi sq: Zimbabveja sr: Зимбабве ss: IZimbabhwe su: Zimbabwé sv: Zimbabwe sw: Zimbabwe ta: சிம்பாப்வே te: జింబాబ్వే tg: Зимбабве th: ประเทศซิมบับเว ti: ዚምባብዌ tk: Zimbabwe tl: Simbabwe tn: Zimbabwe tr: Zimbabve ts: Zimbabwe tt: Зимбабве ug: زىمبابۋې uk: Зімбабве ur: زمبابوے uz: Zimbabve ve: Zimbagwe vi: Zimbabwe vo: Zimbabiyän wo: Simbaawee yi: זימבאבווע yo: Sìmbábúè zh: 辛巴威 zu: IZimbabwe ================================================ FILE: settings/country_settings.yaml ================================================ # Andorra (Andorra) ad: partition: 35 languages: ca names: !include country-names/ad.yaml postcode: pattern: "(ddd)" output: AD\1 # United Arab Emirates (الإمارات العربية المتحدة) ae: partition: 83 languages: ar names: !include country-names/ae.yaml postcode: no # Afghanistan (افغانستان) af: partition: 30 languages: fa, ps names: !include country-names/af.yaml postcode: pattern: "dddd" # Antigua and Barbuda (Antigua and Barbuda) ag: partition: 205 languages: en names: !include country-names/ag.yaml postcode: no # Anguilla (Anguilla) ai: partition: 175 languages: en names: !include country-names/ai.yaml postcode: pattern: "2640" output: AI-2640 # Albania (Shqipëria) al: partition: 9 languages: sq names: !include country-names/al.yaml postcode: pattern: "dddd" # Armenia (Հայաստան) am: partition: 33 languages: hy names: !include country-names/am.yaml postcode: pattern: "dddd" # Angola (Angola) ao: partition: 85 languages: pt names: !include country-names/ao.yaml postcode: no # Argentina (Argentina) ar: partition: 39 languages: es names: !include country-names/ar.yaml postcode: pattern: "l?dddd(?:lll)?" # Austria (Österreich) at: partition: 245 languages: de names: !include country-names/at.yaml postcode: pattern: "dddd" # Australia (Australia) au: partition: 139 languages: en names: !include country-names/au.yaml postcode: pattern: "dddd" # Azerbaijan (Azərbaycan) az: partition: 119 languages: az names: !include country-names/az.yaml postcode: pattern: "dddd" # Bosnia and Herzegovina (Bosna i Hercegovina / Босна и Херцеговина) ba: partition: 6 languages: bs, hr, sr names: !include country-names/ba.yaml postcode: pattern: "ddddd" # Barbados (Barbados) bb: partition: 206 languages: en names: !include country-names/bb.yaml postcode: pattern: "(ddddd)" output: BB\1 # Bangladesh (Bangladesh) bd: partition: 158 languages: bn names: !include country-names/bd.yaml postcode: pattern: "dddd" # Belgium (België / Belgique / Belgien) be: partition: 15 languages: nl, fr, de names: !include country-names/be.yaml postcode: pattern: "dddd" # Burkina Faso (Burkina Faso) bf: partition: 225 languages: fr names: !include country-names/bf.yaml postcode: no # Bulgaria (Бългaрия) bg: partition: 140 languages: bg names: !include country-names/bg.yaml postcode: pattern: "dddd" # Bahrain (البحرين) bh: partition: 62 languages: ar names: !include country-names/bh.yaml postcode: pattern: "d?ddd" # Burundi (Burundi) bi: partition: 61 languages: fr names: !include country-names/bi.yaml postcode: no # Benin (Bénin) bj: partition: 224 languages: fr names: !include country-names/bj.yaml postcode: no # Bermuda (Bermuda) bm: partition: 176 languages: en names: !include country-names/bm.yaml postcode: pattern: "(ll)[ -]?(dd)" output: \1 \2 # Brunei (Brunei) bn: partition: 86 languages: ms names: !include country-names/bn.yaml postcode: pattern: "(ll) ?(dddd)" output: \1\2 # Bolivia (Bolivia) bo: partition: 120 languages: es, qu, gn, ay names: !include country-names/bo.yaml postcode: no # Brazil (Brasil) br: partition: 121 languages: pt names: !include country-names/br.yaml postcode: pattern: "(ddddd)-?(ddd)" output: \1-\2 # The Bahamas (The Bahamas) bs: partition: 207 languages: en names: !include country-names/bs.yaml postcode: no # Bhutan (འབྲུག་ཡུལ་) bt: partition: 87 languages: dz names: !include country-names/bt.yaml postcode: pattern: "ddddd" # Botswana (Botswana) bw: partition: 122 languages: en, tn names: !include country-names/bw.yaml postcode: no # Belarus (Беларусь) by: partition: 40 languages: be, ru names: !include country-names/by.yaml postcode: pattern: "dddddd" # Belize (Belize) bz: partition: 208 languages: en names: !include country-names/bz.yaml postcode: no # Canada (Canada) ca: partition: 244 languages: en, fr names: !include country-names/ca.yaml postcode: pattern: "(ldl) ?(dld)" output: \1 \2 # Democratic Republic of the Congo (République démocratique du Congo) cd: partition: 229 languages: fr names: !include country-names/cd.yaml postcode: no # Central African Republic (Ködörösêse tî Bêafrîka - République Centrafricaine) cf: partition: 227 languages: fr, sg names: !include country-names/cf.yaml postcode: no # Congo-Brazzaville (Congo) cg: partition: 230 languages: fr names: !include country-names/cg.yaml postcode: no # Switzerland (Schweiz/Suisse/Svizzera/Svizra) ch: partition: 5 languages: de, fr, it, rm names: !include country-names/ch.yaml postcode: pattern: "dddd" extent: 3000 # Côte d'Ivoire (Côte d’Ivoire) ci: partition: 228 languages: fr names: !include country-names/ci.yaml postcode: no # Cook Islands (Kūki 'Āirani) ck: partition: 41 languages: en, rar names: !include country-names/ck.yaml postcode: no # Chile (Chile) cl: partition: 88 languages: es names: !include country-names/cl.yaml postcode: pattern: "ddddddd" # Cameroon (Cameroun) cm: partition: 141 languages: fr, en names: !include country-names/cm.yaml postcode: no # China (中国) cn: partition: 117 languages: zh names: !include country-names/cn.yaml postcode: pattern: "dddddd" # Colombia (Colombia) co: partition: 133 languages: es names: !include country-names/co.yaml postcode: pattern: "dddddd" # Costa Rica (Costa Rica) cr: partition: 64 languages: es names: !include country-names/cr.yaml postcode: pattern: "ddddd" # Cuba (Cuba) cu: partition: 42 languages: es names: !include country-names/cu.yaml postcode: pattern: "ddddd" # Cape Verde (Cabo Verde) cv: partition: 89 languages: pt names: !include country-names/cv.yaml postcode: pattern: "dddd" # Cyprus (Κύπρος - Kıbrıs) cy: partition: 114 languages: el, tr names: !include country-names/cy.yaml postcode: pattern: "(?:99|d)ddd" # Czechia (Česko) cz: partition: 124 languages: cs names: !include country-names/cz.yaml postcode: pattern: "(ddd) ?(dd)" output: \1 \2 # Germany (Deutschland) de: partition: 3 languages: de names: !include country-names/de.yaml postcode: pattern: "ddddd" # Djibouti (Djibouti جيبوتي) dj: partition: 43 languages: fr, ar, so, aa names: !include country-names/dj.yaml postcode: no # Denmark (Danmark) dk: partition: 160 languages: da names: !include country-names/dk.yaml postcode: pattern: "dddd" # Dominica (Dominica) dm: partition: 209 languages: en names: !include country-names/dm.yaml postcode: no # Dominican Republic (República Dominicana) do: partition: 37 languages: es names: !include country-names/do.yaml postcode: pattern: "ddddd" # Algeria (Algérie / ⵍⵣⵣⴰⵢⴻⵔ / الجزائر) dz: partition: 19 languages: ar, ber, fr names: !include country-names/dz.yaml postcode: pattern: "ddddd" # Ecuador (Ecuador) ec: partition: 78 languages: es names: !include country-names/ec.yaml postcode: pattern: "dddddd" # Estonia (Eesti) ee: partition: 125 languages: et names: !include country-names/ee.yaml postcode: pattern: "ddddd" extent: 3000 # Egypt (مصر) eg: partition: 16 languages: ar names: !include country-names/eg.yaml postcode: pattern: "ddddd" # Sahrawi Arab Democratic Republic (الجمهورية العربية الصحراوية الديمقراطية) eh: partition: 186 languages: ar, es, fr names: !include country-names/eh.yaml # Eritrea (ኤርትራ Eritrea إرتريا) er: partition: 142 languages: ti, ar, en names: !include country-names/er.yaml postcode: no # Spain (España) es: partition: 31 languages: es, ast, ca, eu, gl names: !include country-names/es.yaml postcode: pattern: "ddddd" # Ethiopia (ኢትዮጵያ) et: partition: 90 languages: am, om names: !include country-names/et.yaml postcode: pattern: "dddd" # Finland (Suomi) fi: partition: 20 languages: fi, sv, se names: !include country-names/fi.yaml postcode: pattern: "ddddd" # Fiji (Viti) fj: partition: 210 languages: en names: !include country-names/fj.yaml postcode: no # Falkland Islands (Falkland Islands) fk: partition: 91 languages: en names: !include country-names/fk.yaml postcode: pattern: "FIQQ 1ZZ" # Federated States of Micronesia (Micronesia) fm: partition: 217 languages: en names: !include country-names/fm.yaml postcode: pattern: "ddddd" # Faroe Islands (Føroyar) fo: partition: 10 languages: fo, da names: !include country-names/fo.yaml postcode: pattern: "ddd" # France (France) fr: partition: 4 languages: fr names: !include country-names/fr.yaml postcode: pattern: "ddddd" # Gabon (Gabon) ga: partition: 239 languages: fr names: !include country-names/ga.yaml postcode: no # United Kingdom (United Kingdom) gb: partition: 1 languages: en names: !include country-names/gb.yaml postcode: pattern: "(l?ld[A-Z0-9]?) ?(dll)" output: \1 \2 extent: 700 # Grenada (Grenada) gd: partition: 143 languages: en names: !include country-names/gd.yaml postcode: no # Georgia (საქართველო) ge: partition: 21 languages: ka names: !include country-names/ge.yaml postcode: pattern: "dddd" # Guernsey (Guernsey) gg: partition: 77 languages: en names: !include country-names/gg.yaml postcode: pattern: "(GYdd?) ?(dll)" output: \1 \2 extent: 1000 # Ghana (Ghana) gh: partition: 211 languages: en names: !include country-names/gh.yaml postcode: pattern: "ll-d?ddd-dddd" # Gibraltar (Gibraltar) gi: partition: 138 languages: en names: !include country-names/gi.yaml postcode: pattern: "(GX11) ?(1AA)" output: GX11 1AA # Greenland (Kalaallit Nunaat) gl: partition: 111 languages: kl, da names: !include country-names/gl.yaml postcode: pattern: "dddd" # The Gambia (Gambia) gm: partition: 212 languages: en names: !include country-names/gm.yaml postcode: no # Guinea (Guinée) gn: partition: 240 languages: fr names: !include country-names/gn.yaml postcode: pattern: "ddd" # Equatorial Guinea (Guinea Ecuatorial) gq: partition: 12 languages: es, fr, pt names: !include country-names/gq.yaml postcode: no # Greece (Ελλάς) gr: partition: 22 languages: el names: !include country-names/gr.yaml postcode: pattern: "(ddd) ?(dd)" output: \1 \2 # South Georgia and the South Sandwich Islands (South Georgia and the South Sandwich Islands) gs: partition: 44 languages: en names: !include country-names/gs.yaml postcode: pattern: "(SIQQ) ?(1ZZ)" output: \1 \2 # Guatemala (Guatemala) gt: partition: 57 languages: es names: !include country-names/gt.yaml postcode: pattern: "ddddd" # Guinea-Bissau (Guiné-Bissau) gw: partition: 8 languages: pt names: !include country-names/gw.yaml postcode: pattern: "dddd" # Guyana (Guyana) gy: partition: 213 languages: en names: !include country-names/gy.yaml postcode: no # Honduras (Honduras) hn: partition: 56 languages: es names: !include country-names/hn.yaml postcode: pattern: "ddddd" # Croatia (Hrvatska) hr: partition: 92 languages: hr names: !include country-names/hr.yaml postcode: pattern: "ddddd" # Haiti (Ayiti) ht: partition: 29 languages: fr, ht names: !include country-names/ht.yaml postcode: pattern: "dddd" # Hungary (Magyarország) hu: partition: 45 languages: hu names: !include country-names/hu.yaml postcode: pattern: "dddd" # Indonesia (Indonesia) id: partition: 110 languages: id names: !include country-names/id.yaml postcode: pattern: "ddddd" # Ireland (Éire / Ireland) ie: partition: 46 languages: en, ga names: !include country-names/ie.yaml postcode: pattern: "(ldd) ?([0123456789ACDEFHKNPRTVWXY]{4})" output: \1 \2 extent: 50 # Israel (ישראל) il: partition: 65 languages: he names: !include country-names/il.yaml postcode: pattern: "ddddddd" # Isle of Man (Isle of Man) im: partition: 190 languages: en names: !include country-names/im.yaml postcode: pattern: "(IMdd?) ?(dll)" output: \1 \2 extent: 700 # India (India) in: partition: 128 languages: hi, en names: !include country-names/in.yaml postcode: pattern: "(ddd) ?(ddd)" output: \1\2 # British Indian Ocean Territory (British Indian Ocean Territory) io: partition: 13 languages: en names: !include country-names/io.yaml postcode: pattern: "(BBND) ?(1ZZ)" output: \1 \2 # Iraq (العراق) iq: partition: 144 languages: ar, ku names: !include country-names/iq.yaml postcode: pattern: "ddddd" # Iran (ایران) ir: partition: 80 languages: fa names: !include country-names/ir.yaml postcode: pattern: "(ddddd)[-_ ]?(ddddd)" output: \1-\2 # Iceland (Ísland) is: partition: 134 languages: is names: !include country-names/is.yaml postcode: pattern: "ddd" # Italy (Italia) it: partition: 28 languages: it, de, fr names: !include country-names/it.yaml postcode: pattern: "ddddd" # Jersey (Jersey) je: partition: 123 languages: en names: !include country-names/je.yaml postcode: pattern: "(JEdd?) ?(dll)" output: \1 \2 # Jamaica (Jamaica) jm: partition: 214 languages: en names: !include country-names/jm.yaml postcode: no # Jordan (الأردن) jo: partition: 17 languages: ar names: !include country-names/jo.yaml postcode: pattern: "ddddd" # Japan (日本) jp: partition: 11 languages: ja names: !include country-names/jp.yaml postcode: pattern: "(ddd)-?(dddd)" output: \1-\2 extent: 3000 # Kenya (Kenya) ke: partition: 126 languages: sw, en names: !include country-names/ke.yaml postcode: pattern: "ddddd" # Kyrgyzstan (Кыргызстан) kg: partition: 93 languages: ky, ru names: !include country-names/kg.yaml postcode: pattern: "dddddd" # Cambodia (ព្រះរាជាណាចក្រ​កម្ពុជា) kh: partition: 159 languages: km names: !include country-names/kh.yaml postcode: pattern: "dddddd" # Kiribati (Kiribati) ki: partition: 215 languages: en names: !include country-names/ki.yaml postcode: no # Comoros (Comores Komori جزر القمر) km: partition: 47 languages: ar, fr, sw names: !include country-names/km.yaml postcode: no # Saint Kitts and Nevis (Saint Kitts and Nevis) kn: partition: 84 languages: en names: !include country-names/kn.yaml postcode: pattern: "dddd" # North Korea (조선민주주의인민공화국) kp: partition: 48 languages: ko names: !include country-names/kp.yaml postcode: no # South Korea (대한민국) kr: partition: 49 languages: ko names: !include country-names/kr.yaml postcode: pattern: "ddddd" # Kuwait (الكويت) kw: partition: 127 languages: ar names: !include country-names/kw.yaml postcode: pattern: "ddddd" # Cayman Islands (Cayman Islands) ky: partition: 38 languages: en names: !include country-names/ky.yaml postcode: pattern: "(d)-(dddd)" output: KY\1-\2 # Kazakhstan (Қазақстан) kz: partition: 94 languages: kk, ru names: !include country-names/kz.yaml postcode: pattern: "(?:lddldld|dddddd)" # Laos (ປະເທດລາວ) la: partition: 145 languages: lo names: !include country-names/la.yaml postcode: pattern: "ddddd" # Lebanon (لبنان) lb: partition: 66 languages: ar, fr names: !include country-names/lb.yaml postcode: pattern: "(dddd)(?: ?dddd)?" # Saint Lucia (Saint Lucia) lc: partition: 146 languages: en names: !include country-names/lc.yaml postcode: pattern: "(dd) ?(ddd)" output: LC\1 \2 # Liechtenstein (Liechtenstein) li: partition: 246 languages: de names: !include country-names/li.yaml postcode: pattern: "dddd" extent: 4000 # Sri Lanka (ශ්‍රී ලංකාව இலங்கை) lk: partition: 95 languages: si, ta names: !include country-names/lk.yaml postcode: pattern: "ddddd" # Liberia (Liberia) lr: partition: 216 languages: en names: !include country-names/lr.yaml postcode: pattern: "dddd" # Lesotho (Lesotho) ls: partition: 136 languages: en, st names: !include country-names/ls.yaml postcode: pattern: "ddd" # Lithuania (Lietuva) lt: partition: 67 languages: lt names: !include country-names/lt.yaml postcode: pattern: "ddddd" # Luxembourg (Lëtzebuerg) lu: partition: 74 languages: lb, fr, de names: !include country-names/lu.yaml postcode: pattern: "dddd" extent: 1000 # Latvia (Latvija) lv: partition: 162 languages: lv names: !include country-names/lv.yaml postcode: pattern: "(dddd)" output: LV-\1 # Libya (ليبيا) ly: partition: 163 languages: ar names: !include country-names/ly.yaml postcode: no # Morocco (Maroc ⵍⵎⵖⵔⵉⴱ المغرب) ma: partition: 23 languages: fr, zgh, ar names: !include country-names/ma.yaml postcode: pattern: "ddddd" # Monaco (Monaco) mc: partition: 242 languages: fr names: !include country-names/mc.yaml postcode: pattern: "980dd" # Moldova (Moldova) md: partition: 147 languages: ro, ru, uk names: !include country-names/md.yaml postcode: pattern: "(dddd)" output: MD-\1 # Montenegro (Crna Gora / Црна Гора) me: partition: 180 languages: srp, sr, hr, bs, sq names: !include country-names/me.yaml postcode: pattern: "ddddd" # Madagascar (Madagasikara) mg: partition: 164 languages: mg, fr names: !include country-names/mg.yaml postcode: pattern: "ddd" # Marshall Islands (Ṃajeḷ) mh: partition: 105 languages: en, mh names: !include country-names/mh.yaml postcode: pattern: "ddddd" # North Macedonia (Северна Македонија) mk: partition: 69 languages: mk names: !include country-names/mk.yaml postcode: pattern: "dddd" # Mali (Mali) ml: partition: 241 languages: fr names: !include country-names/ml.yaml postcode: no # Myanmar (မြန်မာ) mm: partition: 148 languages: my names: !include country-names/mm.yaml postcode: pattern: "ddddd" # Mongolia (Монгол улс ᠮᠤᠩᠭᠤᠯ ᠤᠯᠤᠰ) mn: partition: 167 languages: mn names: !include country-names/mn.yaml postcode: pattern: "ddddd" # Mauritania (موريتانيا) mr: partition: 149 languages: ar, fr names: !include country-names/mr.yaml postcode: no # Montserrat (Montserrat) ms: partition: 73 languages: en names: !include country-names/ms.yaml # Malta (Malta) mt: partition: 165 languages: mt, en names: !include country-names/mt.yaml postcode: pattern: "(lll) ?(dddd)" output: \1 \2 # Mauritius (Mauritius) mu: partition: 150 languages: mfe, fr, en names: !include country-names/mu.yaml postcode: pattern: "ddddd" # Maldives (ދިވެހިރާއްޖެ) mv: partition: 96 languages: dv names: !include country-names/mv.yaml postcode: pattern: "ddddd" # Malawi (Malawi) mw: partition: 97 languages: en, ny names: !include country-names/mw.yaml postcode: no # Mexico (México) mx: partition: 166 languages: es names: !include country-names/mx.yaml postcode: pattern: "ddddd" # Malaysia (Malaysia) my: partition: 7 languages: ms names: !include country-names/my.yaml postcode: pattern: "ddddd" # Mozambique (Moçambique) mz: partition: 98 languages: pt names: !include country-names/mz.yaml postcode: pattern: "(dddd)(?:-dd)?" # Namibia (Namibia) na: partition: 99 languages: en, sf, de names: !include country-names/na.yaml postcode: pattern: "ddddd" # Niger (Niger) ne: partition: 226 languages: fr names: !include country-names/ne.yaml postcode: pattern: "dddd" # Nigeria (Nigeria) ng: partition: 218 languages: en names: !include country-names/ng.yaml postcode: pattern: "dddddd" # Nicaragua (Nicaragua) ni: partition: 151 languages: es names: !include country-names/ni.yaml postcode: pattern: "ddddd" # Netherlands (Nederland) nl: partition: 63 languages: nl names: !include country-names/nl.yaml postcode: pattern: "(dddd) ?(ll)" output: \1 \2 extent: 800 # Norway (Norge) "no": partition: 60 languages: nb, nn, no, se names: !include country-names/no.yaml postcode: pattern: "dddd" # Nepal (Nepal) np: partition: 50 languages: ne names: !include country-names/np.yaml postcode: pattern: "ddddd" # Nauru (Naoero) nr: partition: 70 languages: na, en names: !include country-names/nr.yaml postcode: no # Niue (Niuē) nu: partition: 178 languages: niu, en names: !include country-names/nu.yaml postcode: no # New Zealand (New Zealand / Aotearoa) nz: partition: 27 languages: mi, en names: !include country-names/nz.yaml postcode: pattern: "dddd" # Oman (عمان) om: partition: 137 languages: ar names: !include country-names/om.yaml postcode: pattern: "ddd" # Panama (Panamá) pa: partition: 152 languages: es names: !include country-names/pa.yaml postcode: pattern: "dddd" # Peru (Perú) pe: partition: 51 languages: es names: !include country-names/pe.yaml postcode: pattern: "ddddd" # Papua New Guinea (Papua Niugini) pg: partition: 71 languages: en, tpi, ho names: !include country-names/pg.yaml postcode: pattern: "ddd" # Philippines (Philippines) ph: partition: 26 languages: en, tl names: !include country-names/ph.yaml postcode: pattern: "dddd" # Pakistan (پاکستان) pk: partition: 14 languages: en, ur, pnb, sd, ps, bal names: !include country-names/pk.yaml postcode: pattern: "ddddd" # Poland (Polska) pl: partition: 168 languages: pl names: !include country-names/pl.yaml postcode: pattern: "(dd)[ -]?(ddd)" output: \1-\2 # Pitcairn Islands (Pitcairn Islands) pn: partition: 113 languages: en, pih names: !include country-names/pn.yaml postcode: pattern: "(PCRN) ?(1ZZ)" output: \1 \2 # Palestinian Territory (Palestinian Territory) ps: partition: 194 languages: ar, he names: !include country-names/ps.yaml postcode: pattern: "ddd" # Portugal (Portugal) pt: partition: 34 languages: pt names: !include country-names/pt.yaml postcode: pattern: "dddd(?:-ddd)?" extent: 1000 # Palau (Belau) pw: partition: 195 languages: en, pau, ja, sov, tox names: !include country-names/pw.yaml postcode: pattern: "969(39|40)" # Paraguay (Paraguay) py: partition: 101 languages: es, gn names: !include country-names/py.yaml postcode: pattern: "dddddd" # Qatar (قطر) qa: partition: 169 languages: ar names: !include country-names/qa.yaml postcode: no # Romania (România) ro: partition: 170 languages: ro names: !include country-names/ro.yaml postcode: pattern: "dddddd" extent: 2500 # Serbia (Србија) rs: partition: 59 languages: sr names: !include country-names/rs.yaml postcode: pattern: "ddddd" # Russia (Россия) ru: partition: 135 languages: ru names: !include country-names/ru.yaml postcode: pattern: "dddddd" # Rwanda (Rwanda) rw: partition: 102 languages: rw, fr, en names: !include country-names/rw.yaml postcode: no # Saudi Arabia (السعودية) sa: partition: 52 languages: ar names: !include country-names/sa.yaml postcode: pattern: "ddddd(?:-dddd)?" # Solomon Islands (Solomon Islands) sb: partition: 201 languages: en names: !include country-names/sb.yaml postcode: no # Seychelles (Sesel) sc: partition: 79 languages: fr, en, crs names: !include country-names/sc.yaml postcode: no # Sudan (السودان) sd: partition: 72 languages: ar, en names: !include country-names/sd.yaml postcode: pattern: "ddddd" # Sweden (Sverige) se: partition: 112 languages: sv names: !include country-names/se.yaml postcode: pattern: "(ddd) ?(dd)" output: \1 \2 # Singapore (Singapore) sg: partition: 115 languages: zh-hans, en, ms, ta names: !include country-names/sg.yaml postcode: pattern: "dddddd" # Saint Helena, Ascension and Tristan da Cunha (Saint Helena, Ascension and Tristan da Cunha) sh: partition: 196 languages: en names: !include country-names/sh.yaml postcode: pattern: "(ASCN|STHL|TDCU) ?(1ZZ)" output: \1 \2 # Slovenia (Slovenija) si: partition: 36 languages: sl names: !include country-names/si.yaml postcode: pattern: "dddd" # Slovakia (Slovensko) sk: partition: 172 languages: sk names: !include country-names/sk.yaml postcode: pattern: "(ddd) ?(dd)" output: \1 \2 # Sierra Leone (Sierra Leone) sl: partition: 219 languages: en names: !include country-names/sl.yaml postcode: no # San Marino (San Marino) sm: partition: 153 languages: it names: !include country-names/sm.yaml postcode: pattern: "4789d" # Senegal (Sénégal) sn: partition: 237 languages: fr names: !include country-names/sn.yaml postcode: pattern: "ddddd" # Somalia (Soomaaliya الصومال) so: partition: 154 languages: so, ar names: !include country-names/so.yaml postcode: pattern: "(ll) ?(ddddd)" output: \1 \2 # Suriname (Suriname) sr: partition: 24 languages: nl names: !include country-names/sr.yaml postcode: no # South Sudan (South Sudan) ss: partition: 247 languages: en names: !include country-names/ss.yaml postcode: no # São Tomé and Príncipe (São Tomé e Príncipe) st: partition: 53 languages: pt names: !include country-names/st.yaml postcode: no # El Salvador (El Salvador) sv: partition: 103 languages: es names: !include country-names/sv.yaml postcode: pattern: "dddd" # Syria (سوريا) sy: partition: 104 languages: ar names: !include country-names/sy.yaml postcode: no # Eswatini (eSwatini) sz: partition: 82 languages: en, ss names: !include country-names/sz.yaml postcode: pattern: "lddd" # Turks and Caicos Islands (Turks and Caicos Islands) tc: partition: 106 languages: en names: !include country-names/tc.yaml postcode: pattern: "(TKCA) ?(1ZZ)" output: \1 \2 # Chad (Tchad تشاد) td: partition: 68 languages: fr, ar names: !include country-names/td.yaml postcode: no # Togo (Togo) tg: partition: 243 languages: fr names: !include country-names/tg.yaml postcode: no # Thailand (ประเทศไทย) th: partition: 32 languages: th names: !include country-names/th.yaml postcode: pattern: "ddddd" # Tajikistan (Тоҷикистон) tj: partition: 129 languages: tg, ru names: !include country-names/tj.yaml postcode: pattern: "dddddd" # Tokelau (Tokelau) tk: partition: 179 languages: tkl, en, sm names: !include country-names/tk.yaml postcode: no # East Timor (Timór Lorosa'e) tl: partition: 161 languages: pt, tet names: !include country-names/tl.yaml postcode: no # Turkmenistan (Türkmenistan) tm: partition: 54 languages: tk names: !include country-names/tm.yaml postcode: pattern: "dddddd" # Tunisia (تونس) tn: partition: 18 languages: ar, fr names: !include country-names/tn.yaml postcode: pattern: "dddd" # Tonga (Tonga) to: partition: 220 languages: en names: !include country-names/to.yaml postcode: no # Turkey (Türkiye) tr: partition: 81 languages: tr names: !include country-names/tr.yaml postcode: pattern: "ddddd" # Trinidad and Tobago (Trinidad and Tobago) tt: partition: 221 languages: en names: !include country-names/tt.yaml postcode: pattern: "dddddd" # Tuvalu (Tuvalu) tv: partition: 156 languages: en names: !include country-names/tv.yaml postcode: no # Taiwan (臺灣) tw: partition: 25 languages: zh-hant names: !include country-names/tw.yaml postcode: pattern: "ddd(?:ddd?)?" # Tanzania (Tanzania) tz: partition: 130 languages: sw, en names: !include country-names/tz.yaml postcode: pattern: "ddddd" # Ukraine (Україна) ua: partition: 173 languages: uk names: !include country-names/ua.yaml postcode: pattern: "d?ddddd" # Uganda (Uganda) ug: partition: 155 languages: en, sw names: !include country-names/ug.yaml postcode: no # United States (United States) us: partition: 2 languages: en names: !include country-names/us.yaml postcode: pattern: "(ddddd)(?:-dddd)?" output: \1 # Uruguay (Uruguay) uy: partition: 174 languages: es names: !include country-names/uy.yaml postcode: pattern: "ddddd" # Uzbekistan (Oʻzbekiston) uz: partition: 157 languages: uz, kaa names: !include country-names/uz.yaml postcode: pattern: "dddddd" # Vatican City (Civitas Vaticana) va: partition: 107 languages: it names: !include country-names/va.yaml postcode: pattern: "00120" # Saint Vincent and the Grenadines (Saint Vincent and the Grenadines) vc: partition: 171 languages: en names: !include country-names/vc.yaml postcode: pattern: "(dddd)" output: VC\1 # Venezuela (Venezuela) ve: partition: 108 languages: es names: !include country-names/ve.yaml postcode: pattern: "dddd" # British Virgin Islands (British Virgin Islands) vg: partition: 109 languages: en names: !include country-names/vg.yaml postcode: pattern: "(dddd)" output: VG\1 # Vietnam (Việt Nam) vn: partition: 75 languages: vi names: !include country-names/vn.yaml postcode: pattern: "ddddd" # Vanuatu (Vanuatu) vu: partition: 116 languages: bi, en, fr names: !include country-names/vu.yaml postcode: no # Samoa (Sāmoa) ws: partition: 131 languages: sm, en names: !include country-names/ws.yaml # Kosovo (Kosova / Kosovo) xk: partition: 59 languages: sq, sr names: !include country-names/xk.yaml postcode: pattern: "ddddd" # Yemen (اليمن) ye: partition: 55 languages: ar names: !include country-names/ye.yaml postcode: no # South Africa (South Africa) za: partition: 76 languages: en, af, st, tn, xh, zu names: !include country-names/za.yaml postcode: pattern: "dddd" # Zambia (Zambia) zm: partition: 222 languages: en names: !include country-names/zm.yaml postcode: pattern: "dddd" # Zimbabwe (Zimbabwe) zw: partition: 223 languages: en, sn, nd names: !include country-names/zw.yaml postcode: no ================================================ FILE: settings/env.defaults ================================================ # .env # Default configuration settings for Nominatim. # This file uses the dotenv format. # Database connection string. # Add host, port, user etc through additional semicolon-separated attributes. # e.g. ;host=...;port=...;user=...;password=... NOMINATIM_DATABASE_DSN="pgsql:dbname=nominatim" # Database web user. # Nominatim sets up read-only access for this user during installation. NOMINATIM_DATABASE_WEBUSER="www-data" # Tokenizer used for normalizing and parsing queries and names. # The tokenizer is set up during import and cannot be changed afterwards # without a reimport. # Currently available tokenizers: icu, legacy NOMINATIM_TOKENIZER="icu" # If true, admin level changes on places with many contained children are blocked. NOMINATIM_LIMIT_REINDEXING=yes # Restrict search languages. # Normally Nominatim will include all language variants of name:XX # in the search index. Set this to a comma separated list of language # codes, to restrict import to a subset of languages. # Currently only affects the initial import of country names and special phrases. NOMINATIM_LANGUAGES= # Configuration file for the tokenizer. # The content depends on the tokenizer used. If left empty the default settings # for the chosen tokenizer will be used. The configuration can only be set # on import and not be changed afterwards. NOMINATIM_TOKENIZER_CONFIG= # Search in the Tiger house number data for the US. # Note: The tables must already exist or queries will throw errors. # Changing this value requires to run ./utils/setup --create-functions. NOMINATIM_USE_US_TIGER_DATA=no # Location of the osm2pgsql binary. # EXPERT ONLY. You should usually use the supplied osm2pgsql. NOMINATIM_OSM2PGSQL_BINARY= # Directory where to find pre-computed Wikipedia importance files. # When unset, the data is expected to be located in the project directory. NOMINATIM_WIKIPEDIA_DATA_PATH= # Configuration file for rank assignments. NOMINATIM_ADDRESS_LEVEL_CONFIG=address-levels.json # Configuration file for OSM data import. # This may either be the name of one of an internal style or point # to a file with a custom style. # Internal styles are: admin, street, address, full, extratags NOMINATIM_IMPORT_STYLE=extratags # Location of the flatnode file used by osm2pgsql to store node locations. # When unset, osm2pgsql stores the location in the PostgreSQL database. This # is especially useful for imports of larger areas, like continents or the # full planet. The file needs at least 100GB storage. NOMINATIM_FLATNODE_FILE= ### Tablespace settings # # The following settings allow to move parts of the database tables into # different tablespaces. This is especially interesting if you have disks # with different speeds. When unset, the default tablespace is used. # Only has an effect during import. # Tablespace used for tables used when searching. NOMINATIM_TABLESPACE_SEARCH_DATA= # Tablespace used for indexes used when searching. NOMINATIM_TABLESPACE_SEARCH_INDEX= # Tablespace used for the OSM data cache tables. Used for import and update only. NOMINATIM_TABLESPACE_OSM_DATA= # Tablespace used for the OSM data cache indexes. Used for import and update only. NOMINATIM_TABLESPACE_OSM_INDEX= # Tablespace used for place import table. Used for import and update only. NOMINATIM_TABLESPACE_PLACE_DATA= # Tablespace used for place import indexes. Used for import and update only. NOMINATIM_TABLESPACE_PLACE_INDEX= # Tablespace for tables used during address computation. Used for import and update only. NOMINATIM_TABLESPACE_ADDRESS_DATA= # Tablespace for indexes used during address computation. Used for import and update only. NOMINATIM_TABLESPACE_ADDRESS_INDEX= # Tablespace for tables for auxiliary data, e.g. TIGER data, postcodes. NOMINATIM_TABLESPACE_AUX_DATA= # Tablespace for indexes for auxiliary data, e.g. TIGER data, postcodes. NOMINATIM_TABLESPACE_AUX_INDEX= ### Replication settings # # The following settings control where and how updates for the database are # retrieved. # # # Base URL of replication service. # A replication service provides change files of OSM data at regular intervals. # These are used to keep the database up to date. Per default it points to # the minutely updates for the main OSM database. There are other services # geared towards larger update intervals or data extracts. # Changing this value requires to rerun 'nominatim replication --init'. NOMINATIM_REPLICATION_URL="https://planet.openstreetmap.org/replication/minute" # Maximum amount of data to download per batch. # Size is in MB. NOMINATIM_REPLICATION_MAX_DIFF=50 # Publication interval of the replication service (in seconds). # Determines when Nominatim will attempt again to download again a new # update. The time is computed from the publication date of the last diff # downloaded. Setting this to a slightly higher value than the actual # publication interval avoids unnecessary rechecks. NOMINATIM_REPLICATION_UPDATE_INTERVAL=75 # Wait time to recheck for a pending update (in seconds). # Time to wait after an expected update was not available on the server. NOMINATIM_REPLICATION_RECHECK_INTERVAL=60 ### API settings # # The following settings configure the API responses. # Send permissive CORS access headers. # When enabled, send CORS headers to allow access to everybody. NOMINATIM_CORS_NOACCESSCONTROL=yes # URL for static icon images. # Set this to the /mapicon directory of your nominatim-ui to enable returning # icon URLs with the results. NOMINATIM_MAPICON_URL= # Language to assume when no particular language is requested. # When unset, the local language (i.e. the name tag without suffix) will be used. NOMINATIM_DEFAULT_LANGUAGE= # Maximum number of OSM ids accepted by /lookup. NOMINATIM_LOOKUP_MAX_COUNT=50 # Number of different geometry formats that may be queried in parallel. # Set to zero to disable polygon output. NOMINATIM_POLYGON_OUTPUT_MAX_TYPES=1 # Offer backwards compatible PHP URLs. # When running one of the Python enignes, they will add endpoint aliases # under .php NOMINATIM_SERVE_LEGACY_URLS=yes # Maximum number of DB connections a single API object can use. # When running Nominatim as a server, then this is the maximum number # of connections _per worker_. NOMINATIM_API_POOL_SIZE=5 # Timeout is seconds after which a single query to the database is cancelled. # The caller receives a TimeoutError (or HTTP 503), when a query times out. # When empty, then timeouts are disabled. NOMINATIM_QUERY_TIMEOUT=10 # Maximum time a single request is allowed to take. If the timeout is exceeded # before the request is able to obtain a database connection from the # connection pool, a TimeoutError (or HTTP 503) is thrown. If the timeout # is exceeded while the search is ongoing, all results already found will # be returned. # When empty, then timeouts are disabled. NOMINATIM_REQUEST_TIMEOUT=60 # Search elements just within countries # If, despite not finding a point within the static grid of countries, it # finds a geometry of a region, do not return the geometry. Return "Unable # 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. # # Enable logging of requests into a file. # To enable logging set this setting to the file to log to. NOMINATIM_LOG_FILE= # Set the output format of the query log. # This is a string following the Python String Format syntax, # see https://docs.python.org/3/library/string.html#formatstrings. # For possible replacement values, see the full documentation at # https://nominatim.org/release-docs/latest/customize/Settings/ NOMINATIM_LOG_FORMAT='[{start}] {total_time:.4f} {results_total} {endpoint} "{query_string}"' # Echo raw SQL from SQLAlchemy statements. # EXPERT: Works only in command line/library use. NOMINATIM_DEBUG_SQL=no ================================================ FILE: settings/icu-rules/extended-unicode-to-asccii.yaml ================================================ - "'ł' > 'l'" - "'ª' > 'a'" - "'µ' > 'u'" - "'º' > 'o'" - "'Ƅ' > '6'" - "'ƅ' > '6'" - "'Ɔ' > 'o'" - "'ƍ' > 'd'" - "'Ǝ' > '3'" - "'Ɣ' > 'g'" - "'ƛ' > 'l'" - "'Ɯ' > 'w'" - "'Ɵ' > 'o'" - "'Ʀ' > 'yr'" - "'Ƨ' > '2'" - "'ƨ' > '2'" - "'Ʃ' > 'sh'" - "'ƪ' > 'sh'" - "'Ʊ' > 'y'" - "'Ʒ' > 'zh'" - "'Ƹ' > 'zh'" - "'ƹ' > 'zh'" - "'ƺ' > 'zh'" - "'ƻ' > '2'" - "'Ƽ' > '5'" - "'ƽ' > '5'" - "'ƾ' > 'ts'" - "'ƿ' > 'w'" - "'Ƕ' > 'hv'" - "'Ƿ' > 'w'" - "'Ȝ' > 'y'" - "'ȝ' > 'y'" - "'Ƞ' > 'n'" - "'Ȣ' > 'ou'" - "'ȣ' > 'ou'" - "'Ʌ' > 'v'" - "'Ɋ' > 'q'" - "'ɋ' > 'q'" - "'ɐ' > 'a'" - "'ɑ' > 'a'" - "'ɒ' > 'a'" - "'ɔ' > 'o'" - "'ɘ' > 'e'" - "'ɜ' > 'e'" - "'ɝ' > 'e'" - "'ɞ' > 'e'" - "'ɣ' > 'g'" - "'ɤ' > 'u'" - "'ɥ' > 'y'" - "'ɩ' > 'i'" - "'ɮ' > 'lz'" - "'ɯ' > 'w'" - "'ɰ' > 'w'" - "'ɵ' > 'o'" - "'ɷ' > 'o'" - "'ɸ' > 'f'" - "'ɹ' > 'r'" - "'ɺ' > 'r'" - "'ɻ' > 'r'" - "'ɿ' > 'r'" - "'ʁ' > 'r'" - "'ʃ' > 's'" - "'ʄ' > 'j'" - "'ʅ' > 's'" - "'ʆ' > 's'" - "'ʇ' > 't'" - "'ʊ' > 'u'" - "'ʍ' > 'w'" - "'ʎ' > 'y'" - "'ʒ' > 'z'" - "'ʓ' > 'z'" - "'ʗ' > 'c'" - "'ʚ' > 'e'" - "'ʞ' > 'k'" - "'ʤ' > 'dz'" - "'ʧ' > 'ts'" - "'ʨ' > 'tc'" - "'ʩ' > 'fn'" - "'ʬ' > 'ww'" - "'ʮ' > 'h'" - "'ʯ' > 'h'" - "'ʰ' > 'k'" - "'ʱ' > 'h'" - "'ʲ' > 'j'" - "'ʳ' > 'r'" - "'ʴ' > 'r'" - "'ʵ' > 'r'" - "'ʶ' > 'r'" - "'ʷ' > 'w'" - "'ʸ' > 'y'" - "'ˇ' > 'v'" - "'ˉ' > ' '" - "'ˊ' > ' '" - "'ˌ' > ' '" - "'ˎ' > ' '" - "'ˏ' > ' '" - "'ˑ' > ' '" - "'ˠ' > 'g'" - "'ˡ' > 'l'" - "'ˢ' > 's'" - "'ˣ' > 'x'" - "'ˬ' > 'v'" - "'Ͱ' > 'heta'" - "'ͱ' > 'heta'" - "'Ͳ' > 'sampi'" - "'ͳ' > 'sampi'" - "'ϗ' > ' '" - "'Ϙ' > 'koppa'" - "'ϙ' > 'koppa'" - "'Ϛ' > 'st'" - "'ϛ' > 'st'" - "'Ϝ' > 'w'" - "'ϝ' > 'w'" - "'Ϟ' > 'q'" - "'ϟ' > 'q'" - "'Ϡ' > 'sp'" - "'ϡ' > 'sp'" - "'Ϣ' > 'sh'" - "'ϣ' > 'sh'" - "'Ϥ' > 'f'" - "'ϥ' > 'f'" - "'Ϧ' > 'kh'" - "'ϧ' > 'kh'" - "'Ϩ' > 'h'" - "'ϩ' > 'h'" - "'Ϫ' > 'g'" - "'ϫ' > 'g'" - "'Ϭ' > 'ch'" - "'ϭ' > 'ch'" - "'Ϯ' > 'ti'" - "'ϯ' > 'ti'" - "'Ѡ' > 'o'" - "'ѡ' > 'o'" - "'Ѣ' > 'e'" - "'ѣ' > 'e'" - "'Ѥ' > 'ie'" - "'ѥ' > 'ie'" - "'Ѧ' > 'e'" - "'ѧ' > 'e'" - "'Ѩ' > 'ie'" - "'ѩ' > 'ie'" - "'Ѫ' > 'o'" - "'ѫ' > 'o'" - "'Ѭ' > 'io'" - "'ѭ' > 'io'" - "'Ѯ' > 'ks'" - "'ѯ' > 'ks'" - "'Ѱ' > 'ps'" - "'ѱ' > 'ps'" - "'Ѳ' > 'f'" - "'ѳ' > 'f'" - "'Ѵ' > 'y'" - "'ѵ' > 'y'" - "'Ѷ' > 'y'" - "'ѷ' > 'y'" - "'Ѹ' > 'u'" - "'ѹ' > 'u'" - "'Ѻ' > 'o'" - "'ѻ' > 'o'" - "'Ѽ' > 'o'" - "'ѽ' > 'o'" - "'Ѿ' > 'ot'" - "'ѿ' > 'ot'" - "'Ҁ' > 'q'" - "'ҁ' > 'q'" - "'Ҋ' > 'i'" - "'ҋ' > 'i'" - "'Ҏ' > 'r'" - "'ҏ' > 'r'" - "'Җ' > 'zh'" - "'җ' > 'zh'" - "'Ҝ' > 'k'" - "'ҝ' > 'k'" - "'Ҟ' > 'k'" - "'ҟ' > 'k'" - "'Ҡ' > 'k'" - "'ҡ' > 'k'" - "'Ң' > 'n'" - "'ң' > 'n'" - "'Ҥ' > 'ng'" - "'ҥ' > 'ng'" - "'Ҧ' > 'p'" - "'ҧ' > 'p'" - "'Ҩ' > 'kh'" - "'ҩ' > 'kh'" - "'Ҫ' > 's'" - "'ҫ' > 's'" - "'Ҭ' > 't'" - "'ҭ' > 't'" - "'Ү' > 'u'" - "'ү' > 'u'" - "'Ұ' > 'u'" - "'ұ' > 'u'" - "'Ҳ' > 'kh'" - "'ҳ' > 'kh'" - "'Ҵ' > 'tts'" - "'ҵ' > 'tts'" - "'Ҷ' > 'ch'" - "'ҷ' > 'ch'" - "'Ҹ' > 'ch'" - "'ҹ' > 'ch'" - "'Һ' > 'h'" - "'һ' > 'h'" - "'Ҽ' > 'ch'" - "'ҽ' > 'ch'" - "'Ҿ' > 'ch'" - "'ҿ' > 'ch'" - "'Ӄ' > 'k'" - "'ӄ' > 'k'" - "'Ӆ' > 'el'" - "'ӆ' > 'el'" - "'Ӈ' > 'n'" - "'ӈ' > 'n'" - "'Ӊ' > 'en'" - "'ӊ' > 'en'" - "'Ӌ' > 'ch'" - "'ӌ' > 'ch'" - "'Ӎ' > 'em'" - "'ӎ' > 'em'" - "'ӏ' > 'palochka'" - "'Ӡ' > 'dz'" - "'ӡ' > 'dz'" - "'Ө' > 'o'" - "'ө' > 'o'" - "'Ӫ' > 'o'" - "'ӫ' > 'o'" - "'Ӷ' > 'ghe'" - "'ӷ' > 'ghe'" - "'Ӻ' > 'ghe'" - "'ӻ' > 'ghe'" - "'Ӽ' > 'ha'" - "'ӽ' > 'ha'" - "'Ӿ' > 'ha'" - "'ӿ' > 'ha'" - "'Ԁ' > 'de'" - "'ԁ' > 'de'" - "'Ԃ' > 'dje'" - "'ԃ' > 'dje'" - "'Ԅ' > 'zje'" - "'ԅ' > 'zje'" - "'Ԇ' > 'dzje'" - "'ԇ' > 'dzje'" - "'Ԉ' > 'lje'" - "'ԉ' > 'lje'" - "'Ԋ' > 'nje'" - "'ԋ' > 'nje'" - "'Ԍ' > 'sje'" - "'ԍ' > 'sje'" - "'Ԏ' > 'tje'" - "'ԏ' > 'tje'" - "'Ԑ' > 'ze'" - "'ԑ' > 'ze'" - "'Ԓ' > 'el'" - "'ԓ' > 'el'" - "'Ԕ' > 'lha'" - "'ԕ' > 'lha'" - "'Ԗ' > 'rha'" - "'ԗ' > 'rha'" - "'Ԙ' > 'yae'" - "'ԙ' > 'yae'" - "'Ԛ' > 'qa'" - "'ԛ' > 'qa'" - "'Ԝ' > 'we'" - "'ԝ' > 'we'" - "'Ԟ' > 'aleut'" - "'ԟ' > 'aleut'" - "'Ԡ' > 'el'" - "'ԡ' > 'el'" - "'Ԣ' > 'en'" - "'ԣ' > 'en'" - "'ՙ' > 'left'" - "'ػ' > 'keheh'" - "'ؼ' > 'keheh'" - "'ٮ' > 'beh'" - "'ٯ' > 'qaf'" - "'ٱ' > 'alef'" - "'ٲ' > 'alef'" - "'ٳ' > 'alef'" - "'ٴ' > 'high'" - "'ٹ' > 'tt'" - "'ٺ' > 'tth'" - "'ٻ' > 'b'" - "'ټ' > 't'" - "'ٽ' > 't'" - "'ٿ' > 'th'" - "'ڀ' > 'bh'" - "'ځ' > 'hah'" - "'ڂ' > 'h'" - "'ڃ' > 'ny'" - "'ڄ' > 'dy'" - "'څ' > 'h'" - "'ڇ' > 'cch'" - "'ڈ' > 'dd'" - "'ډ' > 'd'" - "'ڊ' > 'd'" - "'ڋ' > 'dt'" - "'ڌ' > 'dh'" - "'ڍ' > 'ddh'" - "'ڎ' > 'd'" - "'ڏ' > 'd'" - "'ڐ' > 'd'" - "'ڑ' > 'rr'" - "'ڒ' > 'r'" - "'ړ' > 'r'" - "'ڔ' > 'r'" - "'ڕ' > 'r'" - "'ږ' > 'r'" - "'ڗ' > 'r'" - "'ڙ' > 'r'" - "'ڛ' > 's'" - "'ڜ' > 's'" - "'ڝ' > 's'" - "'ڞ' > 's'" - "'ڟ' > 't'" - "'ڠ' > 'gh'" - "'ڡ' > 'f'" - "'ڢ' > 'f'" - "'ڣ' > 'f'" - "'ڥ' > 'f'" - "'ڦ' > 'ph'" - "'ڧ' > 'q'" - "'ڨ' > 'q'" - "'ڪ' > 'k'" - "'ګ' > 'k'" - "'ڬ' > 'k'" - "'ڮ' > 'k'" - "'ڰ' > 'g'" - "'ڱ' > 'n'" - "'ڲ' > 'g'" - "'ڳ' > 'g'" - "'ڴ' > 'g'" - "'ڵ' > 'l'" - "'ڶ' > 'l'" - "'ڷ' > 'l'" - "'ڸ' > 'l'" - "'ڹ' > 'n'" - "'ں' > 'n'" - "'ڻ' > 'n'" - "'ڼ' > 'n'" - "'ڽ' > 'n'" - "'ھ' > 'h'" - "'ڿ' > 'ch'" - "'ہ' > 'h'" - "'ۃ' > 'teh'" - "'ۄ' > 'w'" - "'ۅ' > 'oe'" - "'ۆ' > 'oe'" - "'ۇ' > 'u'" - "'ۈ' > 'yu'" - "'ۉ' > 'yu'" - "'ۊ' > 'w'" - "'ۍ' > 'y'" - "'ێ' > 'y'" - "'ۏ' > 'w'" - "'ې' > 'e'" - "'ۑ' > 'yeh'" - "'ے' > 'y'" - "'ە' > 'ae'" - "'ۮ' > 'dal'" - "'ۯ' > 'reh'" - "'ۺ' > 'sh'" - "'ۻ' > 'd'" - "'ۼ' > 'gh'" - "'ۿ' > 'heh'" - "'ݐ' > 'beh'" - "'ݑ' > 'beh'" - "'ݒ' > 'beh'" - "'ݓ' > 'beh'" - "'ݔ' > 'beh'" - "'ݕ' > 'beh'" - "'ݖ' > 'beh'" - "'ݗ' > 'hah'" - "'ݘ' > 'hah'" - "'ݙ' > 'dal'" - "'ݚ' > 'dal'" - "'ݛ' > 'reh'" - "'ݜ' > 'seen'" - "'ݝ' > 'ain'" - "'ݞ' > 'ain'" - "'ݟ' > 'ain'" - "'ݠ' > 'feh'" - "'ݡ' > 'feh'" - "'ݢ' > 'keheh'" - "'ݣ' > 'keheh'" - "'ݤ' > 'keheh'" - "'ݥ' > 'meem'" - "'ݦ' > 'meem'" - "'ݧ' > 'noon'" - "'ݨ' > 'noon'" - "'ݩ' > 'noon'" - "'ݪ' > 'lam'" - "'ݫ' > 'reh'" - "'ݬ' > 'reh'" - "'ݭ' > 'seen'" - "'ݮ' > 'hah'" - "'ݯ' > 'hah'" - "'ݰ' > 'seen'" - "'ݱ' > 'reh'" - "'ݲ' > 'hah'" - "'ݳ' > 'alef'" - "'ݴ' > 'alef'" - "'ݸ' > 'waw'" - "'ݹ' > 'waw'" - "'ݺ' > 'yeh'" - "'ݻ' > 'yeh'" - "'ݼ' > 'hah'" - "'ݽ' > 'seen'" - "'ݾ' > 'seen'" - "'ݿ' > 'kaf'" - "'ޜ' > 'z'" - "'ޡ' > 'z'" - "'ޥ' > 'w'" - "'ޱ' > 'naa'" - "'ߊ' > 'a'" - "'ߋ' > 'ee'" - "'ߌ' > 'i'" - "'ߍ' > 'e'" - "'ߎ' > 'u'" - "'ߏ' > 'oo'" - "'ߐ' > 'o'" - "'ߑ' > 'dagbasinna'" - "'ߒ' > 'n'" - "'ߓ' > 'ba'" - "'ߔ' > 'pa'" - "'ߕ' > 'ta'" - "'ߖ' > 'ja'" - "'ߗ' > 'cha'" - "'ߘ' > 'da'" - "'ߙ' > 'ra'" - "'ߚ' > 'rra'" - "'ߛ' > 'sa'" - "'ߜ' > 'gba'" - "'ߝ' > 'fa'" - "'ߞ' > 'ka'" - "'ߟ' > 'la'" - "'ߠ' > 'na'" - "'ߡ' > 'ma'" - "'ߢ' > 'nya'" - "'ߣ' > 'na'" - "'ߤ' > 'ha'" - "'ߥ' > 'wa'" - "'ߦ' > 'ya'" - "'ߧ' > 'nya'" - "'ߨ' > 'jona'" - "'ߩ' > 'jona'" - "'ߪ' > 'jona'" - "'ॱ' > 'high'" - "'ॲ' > 'candra'" - "'ॻ' > 'gga'" - "'ॼ' > 'jja'" - "'ॾ' > 'ddda'" - "'ॿ' > 'bba'" - "'ௐ' > 'aum'" - "'ఽ' > 'avagraha'" - "'ౘ' > 'tsa'" - "'ౙ' > 'dza'" - "'ೱ' > 'jihvamuliya'" - "'ೲ' > 'upadhmaniya'" - "'ഽ' > 'avagraha'" - "'අ' > 'a'" - "'ආ' > 'aa'" - "'ඇ' > 'i'" - "'ඈ' > 'ii'" - "'ඉ' > 'u'" - "'ඊ' > 'uu'" - "'උ' > 'r'" - "'ඌ' > 'l'" - "'ඍ' > 'iruyanna'" - "'ඎ' > 'e'" - "'ඏ' > 'ee'" - "'ඐ' > 'ai'" - "'එ' > 'eyanna'" - "'ඒ' > 'o'" - "'ඓ' > 'oo'" - "'ඔ' > 'au'" - "'ඕ' > 'k'" - "'ඖ' > 'kh'" - "'ක' > 'c'" - "'ඛ' > 'ch'" - "'ග' > 'j'" - "'ඝ' > 'jh'" - "'ඞ' > 'ny'" - "'ඟ' > 'tt'" - "'ච' > 'tth'" - "'ඡ' > 'dd'" - "'ජ' > 'ddh'" - "'ඣ' > 'nn'" - "'ඤ' > 't'" - "'ඥ' > 'th'" - "'ඦ' > 'd'" - "'ට' > 'dh'" - "'ඨ' > 'n'" - "'ඩ' > 'alpapraana'" - "'ඪ' > 'p'" - "'ණ' > 'ph'" - "'ඬ' > 'b'" - "'ත' > 'bh'" - "'ථ' > 'm'" - "'ද' > 'y'" - "'ධ' > 'r'" - "'න' > 'rr'" - "'ඳ' > 'll'" - "'ප' > 'alpapraana'" - "'ඵ' > 'v'" - "'බ' > 'sh'" - "'භ' > 'ss'" - "'ම' > 's'" - "'ඹ' > 'h'" - "'ය' > 'yayanna'" - "'ර' > 'rayanna'" - "'ල' > 'dantaja'" - "'ව' > 'ii'" - "'ශ' > 'u'" - "'ෂ' > 'uu'" - "'ස' > 'r'" - "'හ' > 'rr'" - "'ළ' > 'muurdhaja'" - "'ෆ' > 'e'" - "'ກ' > 'ko'" - "'ຂ' > 'n'" - "'ຄ' > 'kho'" - "'ງ' > 'ae'" - "'ຈ' > 'aae'" - "'ຊ' > 'ii'" - "'ຍ' > 'r'" - "'ດ' > 'o'" - "'ຕ' > 'oo'" - "'ຖ' > 'au'" - "'ທ' > 'tho'" - "'ນ' > 'no'" - "'ບ' > 'k'" - "'ປ' > 'kh'" - "'ຜ' > 'g'" - "'ຝ' > 'gh'" - "'ພ' > 'ng'" - "'ຟ' > 'nng'" - "'ມ' > 'ch'" - "'ຢ' > 'j'" - "'ຣ' > 'jh'" - "'ລ' > 'jny'" - "'ວ' > 'tt'" - "'ສ' > 'ddh'" - "'ຫ' > 'nn'" - "'ອ' > 't'" - "'ຮ' > 'th'" - "'ຯ' > 'd'" - "'ະ' > 'dh'" - "'າ' > 'aa'" - "'ຳ' > 'nd'" - "'ຽ' > 'l'" - "'ເ' > 'v'" - "'ແ' > 'sh'" - "'ໂ' > 'ss'" - "'ໃ' > 's'" - "'ໄ' > 'h'" - "'ໆ' > 'f'" - "'ໜ' > 'o'" - "'ໝ' > 'oo'" - "'ໞ' > 'au'" - "'ໟ' > 'l'" - "'ༀ' > 'om'" - "'ཀ' > 'e'" - "'ཁ' > 'ae'" - "'ག' > 'o'" - "'གྷ' > 'ai'" - "'ང' > 'ai'" - "'ཅ' > 'ao'" - "'ཆ' > 'cha'" - "'ཇ' > 'ja'" - "'ཉ' > 'nya'" - "'ཊ' > 'tta'" - "'ཋ' > 'ttha'" - "'ཌ' > 'dda'" - "'ཌྷ' > 'm'" - "'ཎ' > 'nna'" - "'ཏ' > 'ta'" - "'ཐ' > 'tha'" - "'ད' > 'da'" - "'དྷ' > 'dha'" - "'ན' > 'na'" - "'པ' > 'pa'" - "'ཕ' > 'pha'" - "'བ' > 'ba'" - "'བྷ' > 'bha'" - "'མ' > 'ma'" - "'ཙ' > 'tsa'" - "'ཚ' > 'tsha'" - "'ཛ' > 'dza'" - "'ཛྷ' > 'dzha'" - "'ཝ' > 'wa'" - "'ཞ' > 'zha'" - "'ཟ' > 'za'" - "'འ' > '-a'" - "'ཡ' > 'ya'" - "'ར' > 'ra'" - "'ལ' > 'la'" - "'ཤ' > 'sha'" - "'ཥ' > 'ssa'" - "'ས' > 'sa'" - "'ཧ' > 'ha'" - "'ཨ' > 'a'" - "'ཀྵ' > 'kssa'" - "'ཫ' > 'kka'" - "'ཬ' > 'rra'" - "'ྈ' > 'ch'" - "'ྉ' > 'mchu'" - "'ྊ' > 's'" - "'ྋ' > 'gru'" - "'က' > 'aum'" - "'ခ' > 'kha'" - "'ဂ' > 'ga'" - "'ဃ' > 'gha'" - "'င' > 'nga'" - "'စ' > 'ca'" - "'ဆ' > 'cha'" - "'ဇ' > 'ja'" - "'ဈ' > 'jha'" - "'ဉ' > 'nya'" - "'ည' > 'nnya'" - "'ဋ' > 'tta'" - "'ဌ' > 'ttha'" - "'ဍ' > 'dda'" - "'ဎ' > 'ddha'" - "'ဏ' > 'nna'" - "'တ' > 'ta'" - "'ထ' > 'tha'" - "'ဒ' > 'da'" - "'ဓ' > 'dha'" - "'န' > 'na'" - "'ပ' > 'pa'" - "'ဖ' > 'pha'" - "'ဗ' > 'ba'" - "'ဘ' > 'bha'" - "'မ' > 'ma'" - "'ယ' > 'ya'" - "'ရ' > 'ra'" - "'လ' > 'la'" - "'ဝ' > 'wa'" - "'သ' > 'sa'" - "'ဟ' > 'ha'" - "'ဠ' > 'lla'" - "'အ' > 'a'" - "'ဢ' > 'shan'" - "'ဣ' > 'i'" - "'ဤ' > 'ii'" - "'ဥ' > 'u'" - "'ဦ' > 'uu'" - "'ဧ' > 'e'" - "'ဨ' > 'mon'" - "'ဩ' > 'o'" - "'ဪ' > 'au'" - "'ၐ' > 'th'" - "'ၑ' > 'd'" - "'ၒ' > 'dh'" - "'ၓ' > 'n'" - "'ၔ' > 'p'" - "'ၕ' > 'ph'" - "'ၚ' > 'tsh'" - "'ၛ' > 'dz'" - "'ၜ' > 'dzh'" - "'ၝ' > 'w'" - "'ၡ' > 'y'" - "'ၥ' > 'ssh'" - "'ၦ' > 's'" - "'ၵ' > 'uu'" - "'ၶ' > 'r'" - "'ၷ' > 'rr'" - "'ၸ' > 'l'" - "'ၹ' > 'll'" - "'ၺ' > 'e'" - "'ၻ' > 'ee'" - "'ၼ' > 'o'" - "'ၽ' > 'oo'" - "'ၾ' > 'm'" - "'ၿ' > 'h'" - "'ႀ' > 'i'" - "'ႁ' > 'ii'" - "'ႎ' > 'rumai'" - "'Ⴀ' > 'th'" - "'Ⴁ' > 'd'" - "'Ⴂ' > 'dh'" - "'Ⴃ' > 'n'" - "'Ⴄ' > 'p'" - "'Ⴅ' > 'ph'" - "'Ⴆ' > 'b'" - "'Ⴇ' > 'bh'" - "'Ⴈ' > 'm'" - "'Ⴉ' > 'ts'" - "'Ⴊ' > 'tsh'" - "'Ⴋ' > 'dz'" - "'Ⴌ' > 'dzh'" - "'Ⴍ' > 'w'" - "'Ⴎ' > 'zh'" - "'Ⴏ' > 'z'" - "'Ⴐ' > 'rae'" - "'Ⴑ' > 'y'" - "'Ⴒ' > 'r'" - "'Ⴓ' > 'l'" - "'Ⴔ' > 'sh'" - "'Ⴕ' > 'ss'" - "'Ⴖ' > 's'" - "'Ⴗ' > 'h'" - "'Ⴘ' > 'a'" - "'Ⴙ' > 'kss'" - "'Ⴚ' > 'w'" - "'Ⴛ' > 'y'" - "'Ⴜ' > 'r'" - "'Ⴞ' > 'x'" - "'Ⴟ' > 'jhan'" - "'Ⴠ' > 'hae'" - "'Ⴡ' > 'he'" - "'Ⴢ' > 'hie'" - "'Ⴣ' > 'we'" - "'Ⴤ' > 'har'" - "'Ⴥ' > 'hoe'" - "'ჱ' > 'he'" - "'ჲ' > 'hie'" - "'ჵ' > 'hoe'" - "'ჶ' > 'fi'" - "'ჷ' > 'yn'" - "'ჸ' > 'elifi'" - "'ჹ' > 'gan'" - "'ჺ' > 'ain'" - "'ᄓ' > 'dh'" - "'ᄔ' > 'n'" - "'ᄕ' > 'p'" - "'ᄖ' > 'ph'" - "'ᄗ' > 'b'" - "'ᄘ' > 'bh'" - "'ᄙ' > 'm'" - "'ᄚ' > 'y'" - "'ᄛ' > 'r'" - "'ᄜ' > 'l'" - "'ᄝ' > 'w'" - "'ᄞ' > 's'" - "'ᄟ' > 'h'" - "'ᄠ' > 'll'" - "'ᄡ' > 'a'" - "'ᄣ' > 'i'" - "'ᄤ' > 'ii'" - "'ᄥ' > 'u'" - "'ᄦ' > 'uu'" - "'ᄧ' > 'e'" - "'ᄩ' > 'o'" - "'ᄪ' > 'au'" - "'ᄬ' > 'aa'" - "'ᄭ' > 'i'" - "'ᄮ' > 'ii'" - "'ᄯ' > 'u'" - "'ᄰ' > 'uu'" - "'ᄱ' > 'e'" - "'ᄲ' > 'ai'" - "'ᄶ' > 'n'" - "'ᅌ' > 'n'" - "'ᅍ' > 'r'" - "'ᅎ' > 'l'" - "'ᅏ' > 'e'" - "'ᅐ' > 'sh'" - "'ᅑ' > 'ss'" - "'ᅒ' > 'r'" - "'ᅓ' > 'rr'" - "'ᅔ' > 'l'" - "'ᅕ' > 'll'" - "'ᅖ' > 'r'" - "'ᅗ' > 'rr'" - "'ᅘ' > 'l'" - "'ᅙ' > 'll'" - "'ᅶ' > 'a-o'" - "'ᅷ' > 'a-u'" - "'ᅸ' > 'ya-o'" - "'ᅹ' > 'ya-yo'" - "'ᅺ' > 'eo-o'" - "'ᅻ' > 'eo-u'" - "'ᅼ' > 'eo-eu'" - "'ᅽ' > 'yeo-o'" - "'ᅾ' > 'yeo-u'" - "'ᅿ' > 'o-eo'" - "'ᆀ' > 'o-e'" - "'ᆁ' > 'o-ye'" - "'ᆂ' > 'o-o'" - "'ᆃ' > 'o-u'" - "'ᆄ' > 'yo-ya'" - "'ᆅ' > 'yo-yae'" - "'ᆆ' > 'yo-yeo'" - "'ᆇ' > 'yo-o'" - "'ᆈ' > 'yo-i'" - "'ᆉ' > 'u-a'" - "'ᆊ' > 'u-ae'" - "'ᆋ' > 'u-eo-eu'" - "'ᆌ' > 'u-ye'" - "'ᆍ' > 'u-u'" - "'ᆎ' > 'yu-a'" - "'ᆏ' > 'yu-eo'" - "'ᆐ' > 'yu-e'" - "'ᆑ' > 'yu-yeo'" - "'ᆒ' > 'yu-ye'" - "'ᆓ' > 'yu-u'" - "'ᆔ' > 'yu-i'" - "'ᆕ' > 'eu-u'" - "'ᆖ' > 'eu-eu'" - "'ᆗ' > 'yi-u'" - "'ᆘ' > 'i-a'" - "'ᆙ' > 'i-ya'" - "'ᆚ' > 'i-o'" - "'ᆛ' > 'i-u'" - "'ᆜ' > 'i-eu'" - "'ᆝ' > 'i-araea'" - "'ᆞ' > 'araea'" - "'ᆟ' > 'araea-eo'" - "'ᆠ' > 'a'" - "'ᆡ' > 'b'" - "'ᆢ' > 'g'" - "'ᆣ' > 'd'" - "'ᆤ' > 'e'" - "'ᆥ' > 'v'" - "'ᆦ' > 'z'" - "'ᆧ' > 't'" - "'ᇃ' > 'w'" - "'ᇄ' > 'xh'" - "'ᇅ' > 'oe'" - "'ᇆ' > 'nieun-tikeut'" - "'ᇇ' > 'nieun-sios'" - "'ᇈ' > 'nieun-pansios'" - "'ᇉ' > 'nieun-thieuth'" - "'ᇊ' > 'tikeut-kiyeok'" - "'ᇋ' > 'tikeut-rieul'" - "'ᇌ' > 'rieul-kiyeok-sios'" - "'ᇍ' > 'rieul-nieun'" - "'ᇎ' > 'rieul-tikeut'" - "'ᇏ' > 'rieul-tikeut-hieuh'" - "'ᇐ' > 'a'" - "'ᇑ' > 'b'" - "'ᇒ' > 'g'" - "'ᇓ' > 'd'" - "'ᇔ' > 'e'" - "'ᇕ' > 'v'" - "'ᇖ' > 'z'" - "'ᇗ' > 't'" - "'ᇘ' > 'i'" - "'ᇙ' > 'k'" - "'ᇚ' > 'l'" - "'ᇛ' > 'm'" - "'ᇜ' > 'n'" - "'ᇝ' > 'o'" - "'ᇞ' > 'p'" - "'ᇟ' > 'zh'" - "'ᇠ' > 'r'" - "'ᇡ' > 's'" - "'ᇢ' > 't'" - "'ᇣ' > 'u'" - "'ᇤ' > 'p'" - "'ᇥ' > 'k'" - "'ᇦ' > 'g'" - "'ᇧ' > 'q'" - "'ᇨ' > 'sh'" - "'ᇩ' > 'ch'" - "'ᇪ' > 'c'" - "'ᇫ' > 'z'" - "'ᇬ' > 'c'" - "'ᇭ' > 'ch'" - "'ᇮ' > 'x'" - "'ᇯ' > 'j'" - "'ᇰ' > 'h'" - "'ᇱ' > 'e'" - "'ᇲ' > 'y'" - "'ᇳ' > 'w'" - "'ᇴ' > 'xh'" - "'ᇵ' > 'oe'" - "'ᇶ' > 'f'" - "'ᇷ' > 'hieuh-mieum'" - "'ᇸ' > 'hieuh-pieup'" - "'ᇹ' > 'yeorinhieuh'" - "'ሀ' > 'g'" - "'ሁ' > 'gg'" - "'ሂ' > 'n'" - "'ሃ' > 'd'" - "'ሄ' > 'dd'" - "'ህ' > 'r'" - "'ሆ' > 'm'" - "'ሇ' > 'b'" - "'ለ' > 'bb'" - "'ሉ' > 's'" - "'ሊ' > 'ss'" - "'ላ' > 'laa'" - "'ሌ' > 'j'" - "'ል' > 'jj'" - "'ሎ' > 'c'" - "'ሏ' > 'k'" - "'ሐ' > 't'" - "'ሑ' > 'p'" - "'ሒ' > 'h'" - "'ሓ' > 'ng'" - "'ሔ' > 'nn'" - "'ሕ' > 'nd'" - "'ሖ' > 'nb'" - "'ሗ' > 'dg'" - "'መ' > 'rn'" - "'ሙ' > 'rr'" - "'ሚ' > 'rh'" - "'ማ' > 'rn'" - "'ሜ' > 'mb'" - "'ም' > 'mn'" - "'ሞ' > 'bg'" - "'ሟ' > 'bn'" - "'ሠ' > 'sza'" - "'ሡ' > 'bs'" - "'ሢ' > 'bsg'" - "'ሣ' > 'bst'" - "'ሤ' > 'bsb'" - "'ሥ' > 'bss'" - "'ሦ' > 'bsj'" - "'ሧ' > 'bj'" - "'ረ' > 'bc'" - "'ሩ' > 'bt'" - "'ሪ' > 'bp'" - "'ራ' > 'bn'" - "'ሬ' > 'bbn'" - "'ር' > 'sg'" - "'ሮ' > 'sn'" - "'ሯ' > 'sd'" - "'ሰ' > 'sr'" - "'ሱ' > 'sm'" - "'ሲ' > 'sb'" - "'ሳ' > 'sbg'" - "'ሴ' > 'sss'" - "'ስ' > 's'" - "'ሶ' > 'sj'" - "'ሷ' > 'sc'" - "'ሸ' > 'sk'" - "'ሹ' > 'st'" - "'ሺ' > 'sp'" - "'ሻ' > 'sh'" - "'ሼ' > 'shee'" - "'ሽ' > 'she'" - "'ሾ' > 'sho'" - "'ሿ' > 'shwa'" - "'ቀ' > 'z'" - "'ቁ' > 'g'" - "'ቂ' > 'd'" - "'ቃ' > 'm'" - "'ቄ' > 'b'" - "'ቅ' > 's'" - "'ቆ' > 'z'" - "'ቇ' > 'qoa'" - "'ቈ' > 'j'" - "'ቊ' > 't'" - "'ቋ' > 'p'" - "'ቌ' > 'n'" - "'ቍ' > 'j'" - "'ቐ' > 'qha'" - "'ቑ' > 'qhu'" - "'ቒ' > 'ck'" - "'ቓ' > 'ch'" - "'ቔ' > 'qhee'" - "'ቕ' > 'qhe'" - "'ቖ' > 'pb'" - "'ቘ' > 'hh'" - "'ቚ' > 'qhwi'" - "'ቛ' > 'qhwaa'" - "'ቜ' > 'qhwee'" - "'ቝ' > 'qhwe'" - "'በ' > 'ba'" - "'ቡ' > 'a'" - "'ቢ' > 'ae'" - "'ባ' > 'ya'" - "'ቤ' > 'yae'" - "'ብ' > 'eo'" - "'ቦ' > 'e'" - "'ቧ' > 'yeo'" - "'ቨ' > 'ye'" - "'ቩ' > 'o'" - "'ቪ' > 'wa'" - "'ቫ' > 'wae'" - "'ቬ' > 'oe'" - "'ቭ' > 'yo'" - "'ቮ' > 'u'" - "'ቯ' > 'weo'" - "'ተ' > 'we'" - "'ቱ' > 'wi'" - "'ቲ' > 'yu'" - "'ታ' > 'eu'" - "'ቴ' > 'yi'" - "'ት' > 'i'" - "'ቶ' > 'a-o'" - "'ቷ' > 'a-u'" - "'ቸ' > 'ya-o'" - "'ቹ' > 'ya-yo'" - "'ቺ' > 'eo-o'" - "'ቻ' > 'eo-u'" - "'ቼ' > 'eo-eu'" - "'ች' > 'yeo-o'" - "'ቾ' > 'yeo-u'" - "'ቿ' > 'o-eo'" - "'ኀ' > 'o-e'" - "'ኁ' > 'o-ye'" - "'ኂ' > 'o-o'" - "'ኃ' > 'o-u'" - "'ኄ' > 'yo-ya'" - "'ኅ' > 'yo-yae'" - "'ኆ' > 'yo-yeo'" - "'ኇ' > 'yo-o'" - "'ኈ' > 'yo-i'" - "'ኊ' > 'u-ae'" - "'ኋ' > 'u-eo-eu'" - "'ኌ' > 'u-ye'" - "'ኍ' > 'u-u'" - "'ነ' > 'yu-e'" - "'ኑ' > 'yu-yeo'" - "'ኒ' > 'yu-ye'" - "'ና' > 'yu-u'" - "'ኔ' > 'yu-i'" - "'ን' > 'eu-u'" - "'ኖ' > 'eu-eu'" - "'ኗ' > 'yi-u'" - "'ኘ' > 'i-a'" - "'ኙ' > 'i-ya'" - "'ኚ' > 'i-o'" - "'ኛ' > 'i-u'" - "'ኜ' > 'i-eu'" - "'ኝ' > 'i-u'" - "'ኞ' > 'u'" - "'ኟ' > 'u-eo'" - "'አ' > 'u-u'" - "'ኡ' > 'u-i'" - "'ኢ' > 'uu'" - "'ኣ' > 'aa'" - "'ኤ' > 'ee'" - "'እ' > 'e'" - "'ኦ' > 'o'" - "'ኧ' > 'wa'" - "'ከ' > 'g'" - "'ኩ' > 'gg'" - "'ኪ' > 'gs'" - "'ካ' > 'n'" - "'ኬ' > 'nj'" - "'ክ' > 'nh'" - "'ኮ' > 'd'" - "'ኯ' > 'l'" - "'ኰ' > 'lg'" - "'ኲ' > 'lb'" - "'ኳ' > 'ls'" - "'ኴ' > 'lt'" - "'ኵ' > 'lp'" - "'ኸ' > 'b'" - "'ኹ' > 'bs'" - "'ኺ' > 's'" - "'ኻ' > 'ss'" - "'ኼ' > 'ng'" - "'ኽ' > 'j'" - "'ኾ' > 'c'" - "'ዀ' > 't'" - "'ዂ' > 'h'" - "'ዃ' > 'gl'" - "'ዄ' > 'gsg'" - "'ዅ' > 'ng'" - "'ወ' > 'nz'" - "'ዉ' > 'nt'" - "'ዊ' > 'dg'" - "'ዋ' > 'tl'" - "'ዌ' > 'lgs'" - "'ው' > 'ln'" - "'ዎ' > 'ld'" - "'ዏ' > 'lth'" - "'ዐ' > 'll'" - "'ዑ' > 'lmg'" - "'ዒ' > 'lms'" - "'ዓ' > 'lbs'" - "'ዔ' > 'lbh'" - "'ዕ' > 'rnp'" - "'ዖ' > 'lss'" - "'ዘ' > 'lk'" - "'ዙ' > 'lq'" - "'ዚ' > 'mg'" - "'ዛ' > 'ml'" - "'ዜ' > 'mb'" - "'ዝ' > 'ms'" - "'ዞ' > 'mss'" - "'ዟ' > 'mz'" - "'ዠ' > 'mc'" - "'ዡ' > 'mh'" - "'ዢ' > 'mn'" - "'ዣ' > 'bl'" - "'ዤ' > 'bp'" - "'ዥ' > 'ph'" - "'ዦ' > 'pn'" - "'ዧ' > 'sg'" - "'የ' > 'sd'" - "'ዩ' > 'sl'" - "'ዪ' > 'sb'" - "'ያ' > 'z'" - "'ዬ' > 'g'" - "'ይ' > 'ss'" - "'ዮ' > 'yo'" - "'ዯ' > 'kh'" - "'ደ' > 'n'" - "'ዱ' > 'ns'" - "'ዲ' > 'nz'" - "'ዳ' > 'pb'" - "'ዴ' > 'pn'" - "'ድ' > 'hn'" - "'ዶ' > 'hl'" - "'ዷ' > 'hm'" - "'ዸ' > 'hb'" - "'ዹ' > 'q'" - "'ዺ' > 'ddi'" - "'ዻ' > 'ddaa'" - "'ዼ' > 'ddee'" - "'ዽ' > 'dde'" - "'ዾ' > 'ddo'" - "'ዿ' > 'ddwa'" - "'ጀ' > 'ha'" - "'ጁ' > 'hu'" - "'ጂ' > 'hi'" - "'ጃ' > 'haa'" - "'ጄ' > 'hee'" - "'ጅ' > 'he'" - "'ጆ' > 'ho'" - "'ጇ' > 'jwa'" - "'ገ' > 'la'" - "'ጉ' > 'lu'" - "'ጊ' > 'li'" - "'ጋ' > 'laa'" - "'ጌ' > 'lee'" - "'ግ' > 'le'" - "'ጎ' > 'lo'" - "'ጏ' > 'lwa'" - "'ጐ' > 'hha'" - "'ጒ' > 'hhi'" - "'ጓ' > 'hhaa'" - "'ጔ' > 'hhee'" - "'ጕ' > 'hhe'" - "'ጘ' > 'ma'" - "'ጙ' > 'mu'" - "'ጚ' > 'mi'" - "'ጛ' > 'maa'" - "'ጜ' > 'mee'" - "'ጝ' > 'me'" - "'ጞ' > 'mo'" - "'ጟ' > 'mwa'" - "'ጠ' > 'sza'" - "'ጡ' > 'szu'" - "'ጢ' > 'szi'" - "'ጣ' > 'szaa'" - "'ጤ' > 'szee'" - "'ጥ' > 'sze'" - "'ጦ' > 'szo'" - "'ጧ' > 'szwa'" - "'ጨ' > 'ra'" - "'ጩ' > 'ru'" - "'ጪ' > 'ri'" - "'ጫ' > 'raa'" - "'ጬ' > 'ree'" - "'ጭ' > 're'" - "'ጮ' > 'ro'" - "'ጯ' > 'rwa'" - "'ጰ' > 'sa'" - "'ጱ' > 'su'" - "'ጲ' > 'si'" - "'ጳ' > 'saa'" - "'ጴ' > 'see'" - "'ጵ' > 'se'" - "'ጶ' > 'so'" - "'ጷ' > 'swa'" - "'ጸ' > 'sha'" - "'ጹ' > 'shu'" - "'ጺ' > 'shi'" - "'ጻ' > 'shaa'" - "'ጼ' > 'shee'" - "'ጽ' > 'she'" - "'ጾ' > 'sho'" - "'ጿ' > 'shwa'" - "'ፀ' > 'qa'" - "'ፁ' > 'qu'" - "'ፂ' > 'qi'" - "'ፃ' > 'qaa'" - "'ፄ' > 'qee'" - "'ፅ' > 'qe'" - "'ፆ' > 'qo'" - "'ፇ' > 'tzoa'" - "'ፈ' > 'qwa'" - "'ፉ' > 'fu'" - "'ፊ' > 'qwi'" - "'ፋ' > 'qwaa'" - "'ፌ' > 'qwee'" - "'ፍ' > 'qwe'" - "'ፎ' > 'fo'" - "'ፏ' > 'fwa'" - "'ፐ' > 'qha'" - "'ፑ' > 'qhu'" - "'ፒ' > 'qhi'" - "'ፓ' > 'qhaa'" - "'ፔ' > 'qhee'" - "'ፕ' > 'qhe'" - "'ፖ' > 'qho'" - "'ፗ' > 'pwa'" - "'ፘ' > 'qhwa'" - "'ፙ' > 'mya'" - "'ፚ' > 'qhwi'" - "'ᎀ' > 'xa'" - "'ᎁ' > 'xu'" - "'ᎂ' > 'xi'" - "'ᎃ' > 'xaa'" - "'ᎄ' > 'xee'" - "'ᎅ' > 'xe'" - "'ᎆ' > 'xo'" - "'ᎇ' > 'bwe'" - "'ᎈ' > 'xwa'" - "'ᎉ' > 'fwi'" - "'ᎊ' > 'xwi'" - "'ᎋ' > 'xwaa'" - "'ᎌ' > 'xwee'" - "'ᎍ' > 'xwe'" - "'ᎎ' > 'pwee'" - "'ᎏ' > 'pwe'" - "'Ꭰ' > 'a'" - "'Ꭱ' > 'e'" - "'Ꭲ' > 'i'" - "'Ꭳ' > 'o'" - "'Ꭴ' > 'u'" - "'Ꭵ' > 'v'" - "'Ꭶ' > 'ga'" - "'Ꭷ' > 'ka'" - "'Ꭸ' > 'ka'" - "'Ꭹ' > 'ku'" - "'Ꭺ' > 'ki'" - "'Ꭻ' > 'kaa'" - "'Ꭼ' > 'kee'" - "'Ꭽ' > 'ke'" - "'Ꭾ' > 'ko'" - "'Ꭿ' > 'hi'" - "'Ꮀ' > 'kwa'" - "'Ꮁ' > 'hu'" - "'Ꮂ' > 'kwi'" - "'Ꮃ' > 'kwaa'" - "'Ꮄ' > 'kwee'" - "'Ꮅ' > 'kwe'" - "'Ꮆ' > 'lo'" - "'Ꮇ' > 'lu'" - "'Ꮈ' > 'kxa'" - "'Ꮉ' > 'kxu'" - "'Ꮊ' > 'kxi'" - "'Ꮋ' > 'kxaa'" - "'Ꮌ' > 'kxee'" - "'Ꮍ' > 'kxe'" - "'Ꮎ' > 'kxo'" - "'Ꮏ' > 'hna'" - "'Ꮐ' > 'kxwa'" - "'Ꮑ' > 'ne'" - "'Ꮒ' > 'kxwi'" - "'Ꮓ' > 'kxwaa'" - "'Ꮔ' > 'kxwee'" - "'Ꮕ' > 'kxwe'" - "'Ꮖ' > 'qua'" - "'Ꮗ' > 'que'" - "'Ꮘ' > 'wa'" - "'Ꮙ' > 'wu'" - "'Ꮚ' > 'wi'" - "'Ꮛ' > 'waa'" - "'Ꮜ' > 'wee'" - "'Ꮝ' > 'we'" - "'Ꮞ' > 'wo'" - "'Ꮟ' > 'si'" - "'Ꮠ' > 'so'" - "'Ꮡ' > 'su'" - "'Ꮢ' > 'sv'" - "'Ꮣ' > 'da'" - "'Ꮤ' > 'ta'" - "'Ꮥ' > 'de'" - "'Ꮦ' > 'te'" - "'Ꮧ' > 'di'" - "'Ꮨ' > 'za'" - "'Ꮩ' > 'zu'" - "'Ꮪ' > 'zi'" - "'Ꮫ' > 'zaa'" - "'Ꮬ' > 'zee'" - "'Ꮭ' > 'ze'" - "'Ꮮ' > 'zo'" - "'Ꮯ' > 'zwa'" - "'Ꮰ' > 'zha'" - "'Ꮱ' > 'zhu'" - "'Ꮲ' > 'zhi'" - "'Ꮳ' > 'zhaa'" - "'Ꮴ' > 'zhee'" - "'Ꮵ' > 'zhe'" - "'Ꮶ' > 'zho'" - "'Ꮷ' > 'zhwa'" - "'Ꮸ' > 'ya'" - "'Ꮹ' > 'yu'" - "'Ꮺ' > 'yi'" - "'Ꮻ' > 'yaa'" - "'Ꮼ' > 'yee'" - "'Ꮽ' > 'ye'" - "'Ꮾ' > 'yo'" - "'Ꮿ' > 'ya'" - "'Ᏸ' > 'da'" - "'Ᏹ' > 'du'" - "'Ᏺ' > 'di'" - "'Ᏻ' > 'daa'" - "'Ᏼ' > 'dee'" - "'Ᏽ' > 'de'" - "'ᏸ' > 'dda'" - "'ᏹ' > 'ddu'" - "'ᏺ' > 'ddi'" - "'ᏻ' > 'ddaa'" - "'ᏼ' > 'ddee'" - "'ᏽ' > 'dde'" - "'ᐁ' > 'ju'" - "'ᐂ' > 'ji'" - "'ᐃ' > 'jaa'" - "'ᐄ' > 'jee'" - "'ᐅ' > 'je'" - "'ᐆ' > 'jo'" - "'ᐇ' > 'jwa'" - "'ᐈ' > 'ga'" - "'ᐉ' > 'gu'" - "'ᐊ' > 'gi'" - "'ᐋ' > 'gaa'" - "'ᐌ' > 'gee'" - "'ᐍ' > 'ge'" - "'ᐎ' > 'go'" - "'ᐐ' > 'gwa'" - "'ᐒ' > 'gwi'" - "'ᐓ' > 'gwaa'" - "'ᐔ' > 'gwee'" - "'ᐕ' > 'gwe'" - "'ᐘ' > 'gga'" - "'ᐙ' > 'ggu'" - "'ᐚ' > 'ggi'" - "'ᐛ' > 'ggaa'" - "'ᐜ' > 'ggee'" - "'ᐝ' > 'gge'" - "'ᐞ' > 'ggo'" - "'ᐠ' > 'tha'" - "'ᐡ' > 'thu'" - "'ᐢ' > 'thi'" - "'ᐣ' > 'thaa'" - "'ᐤ' > 'thee'" - "'ᐥ' > 'the'" - "'ᐦ' > 'tho'" - "'ᐧ' > 'thwa'" - "'ᐨ' > 'cha'" - "'ᐩ' > 'chu'" - "'ᐪ' > 'chi'" - "'ᐫ' > 'chaa'" - "'ᐬ' > 'chee'" - "'ᐭ' > 'che'" - "'ᐮ' > 'cho'" - "'ᐯ' > 'chwa'" - "'ᐰ' > 'pha'" - "'ᐱ' > 'phu'" - "'ᐲ' > 'phi'" - "'ᐳ' > 'phaa'" - "'ᐴ' > 'phee'" - "'ᐵ' > 'phe'" - "'ᐶ' > 'pho'" - "'ᐷ' > 'phwa'" - "'ᐸ' > 'tsa'" - "'ᐹ' > 'tsu'" - "'ᐺ' > 'tsi'" - "'ᐻ' > 'tsaa'" - "'ᐼ' > 'tsee'" - "'ᐽ' > 'tse'" - "'ᐾ' > 'tso'" - "'ᐿ' > 'tswa'" - "'ᑀ' > 'tza'" - "'ᑁ' > 'tzu'" - "'ᑂ' > 'tzi'" - "'ᑃ' > 'tzaa'" - "'ᑄ' > 'tzee'" - "'ᑅ' > 'tze'" - "'ᑆ' > 'tzo'" - "'ᑈ' > 'fa'" - "'ᑉ' > 'fu'" - "'ᑊ' > 'fi'" - "'ᑋ' > 'faa'" - "'ᑌ' > 'fee'" - "'ᑍ' > 'fe'" - "'ᑎ' > 'fo'" - "'ᑏ' > 'fwa'" - "'ᑐ' > 'pa'" - "'ᑑ' > 'pu'" - "'ᑒ' > 'pi'" - "'ᑓ' > 'paa'" - "'ᑔ' > 'pee'" - "'ᑕ' > 'pe'" - "'ᑖ' > 'po'" - "'ᑗ' > 'pwa'" - "'ᑘ' > 'rya'" - "'ᑙ' > 'mya'" - "'ᑚ' > 'fya'" - "'ᒠ' > 'a'" - "'ᒡ' > 'e'" - "'ᒢ' > 'i'" - "'ᒣ' > 'o'" - "'ᒤ' > 'u'" - "'ᒥ' > 'v'" - "'ᒦ' > 'ga'" - "'ᒧ' > 'ka'" - "'ᒨ' > 'ge'" - "'ᒩ' > 'gi'" - "'ᒪ' > 'go'" - "'ᒫ' > 'gu'" - "'ᒬ' > 'gv'" - "'ᒭ' > 'ha'" - "'ᒮ' > 'he'" - "'ᒯ' > 'hi'" - "'ᒰ' > 'ho'" - "'ᒱ' > 'hu'" - "'ᒲ' > 'hv'" - "'ᒳ' > 'la'" - "'ᒴ' > 'le'" - "'ᒵ' > 'li'" - "'ᒶ' > 'lo'" - "'ᒷ' > 'lu'" - "'ᒸ' > 'lv'" - "'ᒹ' > 'ma'" - "'ᒺ' > 'me'" - "'ᒻ' > 'mi'" - "'ᒼ' > 'mo'" - "'ᒽ' > 'mu'" - "'ᒾ' > 'na'" - "'ᒿ' > 'hna'" - "'ᓀ' > 'nah'" - "'ᓁ' > 'ne'" - "'ᓂ' > 'ni'" - "'ᓃ' > 'no'" - "'ᓄ' > 'nu'" - "'ᓅ' > 'nv'" - "'ᓆ' > 'qua'" - "'ᓇ' > 'que'" - "'ᓈ' > 'qui'" - "'ᓉ' > 'quo'" - "'ᓊ' > 'quu'" - "'ᓋ' > 'quv'" - "'ᓌ' > 'sa'" - "'ᓍ' > 's'" - "'ᓎ' > 'se'" - "'ᓏ' > 'si'" - "'ᓐ' > 'so'" - "'ᓑ' > 'su'" - "'ᓒ' > 'sv'" - "'ᓓ' > 'da'" - "'ᓔ' > 'ta'" - "'ᓕ' > 'de'" - "'ᓖ' > 'te'" - "'ᓗ' > 'di'" - "'ᓘ' > 'ti'" - "'ᓙ' > 'do'" - "'ᓚ' > 'du'" - "'ᓛ' > 'dv'" - "'ᓜ' > 'dla'" - "'ᓝ' > 'tla'" - "'ᓞ' > 'tle'" - "'ᓟ' > 'tli'" - "'ᓠ' > 'tlo'" - "'ᓡ' > 'tlu'" - "'ᓢ' > 'tlv'" - "'ᓣ' > 'tsa'" - "'ᓤ' > 'tse'" - "'ᓥ' > 'tsi'" - "'ᓦ' > 'tso'" - "'ᓧ' > 'tsu'" - "'ᓨ' > 'tsv'" - "'ᓩ' > 'wa'" - "'ᓪ' > 'we'" - "'ᓫ' > 'wi'" - "'ᓬ' > 'wo'" - "'ᓭ' > 'wu'" - "'ᓮ' > 'wv'" - "'ᓯ' > 'ya'" - "'ᓰ' > 'ye'" - "'ᓱ' > 'yi'" - "'ᓲ' > 'yo'" - "'ᓳ' > 'yu'" - "'ᓴ' > 'yv'" - "'ᔁ' > 'e'" - "'ᔂ' > 'aai'" - "'ᔃ' > 'i'" - "'ᔄ' > 'ii'" - "'ᔅ' > 'o'" - "'ᔆ' > 'oo'" - "'ᔇ' > 'oo'" - "'ᔈ' > 'ee'" - "'ᔉ' > 'i'" - "'ᔊ' > 'a'" - "'ᔋ' > 'aa'" - "'ᔌ' > 'we'" - "'ᔍ' > 'we'" - "'ᔎ' > 'wi'" - "'ᔏ' > 'wi'" - "'ᔐ' > 'wii'" - "'ᔑ' > 'wii'" - "'ᔒ' > 'wo'" - "'ᔓ' > 'wo'" - "'ᔔ' > 'woo'" - "'ᔕ' > 'woo'" - "'ᔖ' > 'woo'" - "'ᔗ' > 'wa'" - "'ᔘ' > 'wa'" - "'ᔙ' > 'waa'" - "'ᔚ' > 'waa'" - "'ᔛ' > 'waa'" - "'ᔜ' > 'ai'" - "'ᔝ' > 'w'" - "'ᔟ' > 't'" - "'ᔠ' > 'k'" - "'ᔡ' > 'sh'" - "'ᔢ' > 's'" - "'ᔣ' > 'n'" - "'ᔤ' > 'w'" - "'ᔥ' > 'n'" - "'ᔧ' > 'w'" - "'ᔨ' > 'c'" - "'ᔪ' > 'l'" - "'ᔫ' > 'en'" - "'ᔬ' > 'in'" - "'ᔭ' > 'on'" - "'ᔮ' > 'an'" - "'ᔯ' > 'pe'" - "'ᔰ' > 'paai'" - "'ᔱ' > 'pi'" - "'ᔲ' > 'pii'" - "'ᔳ' > 'po'" - "'ᔴ' > 'poo'" - "'ᔵ' > 'poo'" - "'ᔶ' > 'hee'" - "'ᔷ' > 'hi'" - "'ᔸ' > 'pa'" - "'ᔹ' > 'paa'" - "'ᔺ' > 'pwe'" - "'ᔻ' > 'pwe'" - "'ᔼ' > 'pwi'" - "'ᔽ' > 'pwi'" - "'ᔾ' > 'pwii'" - "'ᔿ' > 'pwii'" - "'ᕀ' > 'pwo'" - "'ᕁ' > 'pwo'" - "'ᕂ' > 'pwoo'" - "'ᕃ' > 'pwoo'" - "'ᕄ' > 'pwa'" - "'ᕅ' > 'pwa'" - "'ᕆ' > 'pwaa'" - "'ᕇ' > 'pwaa'" - "'ᕈ' > 'pwaa'" - "'ᕉ' > 'p'" - "'ᕊ' > 'p'" - "'ᕋ' > 'h'" - "'ᕌ' > 'te'" - "'ᕍ' > 'taai'" - "'ᕎ' > 'ti'" - "'ᕏ' > 'tii'" - "'ᕐ' > 'to'" - "'ᕑ' > 'too'" - "'ᕒ' > 'too'" - "'ᕓ' > 'dee'" - "'ᕔ' > 'di'" - "'ᕕ' > 'ta'" - "'ᕖ' > 'taa'" - "'ᕗ' > 'twe'" - "'ᕘ' > 'twe'" - "'ᕙ' > 'twi'" - "'ᕚ' > 'twi'" - "'ᕛ' > 'twii'" - "'ᕜ' > 'twii'" - "'ᕝ' > 'two'" - "'ᕞ' > 'two'" - "'ᕟ' > 'twoo'" - "'ᕠ' > 'twoo'" - "'ᕡ' > 'twa'" - "'ᕢ' > 'twa'" - "'ᕣ' > 'twaa'" - "'ᕤ' > 'twaa'" - "'ᕥ' > 'twaa'" - "'ᕦ' > 't'" - "'ᕧ' > 'tte'" - "'ᕨ' > 'tti'" - "'ᕩ' > 'tto'" - "'ᕪ' > 'tta'" - "'ᕫ' > 'ke'" - "'ᕬ' > 'kaai'" - "'ᕭ' > 'ki'" - "'ᕮ' > 'kii'" - "'ᕯ' > 'ko'" - "'ᕰ' > 'koo'" - "'ᕱ' > 'koo'" - "'ᕲ' > 'ka'" - "'ᕳ' > 'kaa'" - "'ᕴ' > 'kwe'" - "'ᕵ' > 'kwe'" - "'ᕶ' > 'kwi'" - "'ᕷ' > 'kwi'" - "'ᕸ' > 'kwii'" - "'ᕹ' > 'kwii'" - "'ᕺ' > 'kwo'" - "'ᕻ' > 'kwo'" - "'ᕼ' > 'kwoo'" - "'ᕽ' > 'kwoo'" - "'ᕾ' > 'kwa'" - "'ᕿ' > 'kwa'" - "'ᖀ' > 'kwaa'" - "'ᖁ' > 'kwaa'" - "'ᖂ' > 'kwaa'" - "'ᖃ' > 'k'" - "'ᖄ' > 'kw'" - "'ᖅ' > 'keh'" - "'ᖆ' > 'kih'" - "'ᖇ' > 'koh'" - "'ᖈ' > 'kah'" - "'ᖉ' > 'ce'" - "'ᖊ' > 'caai'" - "'ᖋ' > 'ci'" - "'ᖌ' > 'cii'" - "'ᖍ' > 'co'" - "'ᖎ' > 'coo'" - "'ᖏ' > 'coo'" - "'ᖐ' > 'ca'" - "'ᖑ' > 'caa'" - "'ᖒ' > 'cwe'" - "'ᖓ' > 'cwe'" - "'ᖔ' > 'cwi'" - "'ᖕ' > 'cwi'" - "'ᖖ' > 'cwii'" - "'ᖗ' > 'cwii'" - "'ᖘ' > 'cwo'" - "'ᖙ' > 'cwo'" - "'ᖚ' > 'cwoo'" - "'ᖛ' > 'cwoo'" - "'ᖜ' > 'cwa'" - "'ᖝ' > 'cwa'" - "'ᖞ' > 'cwaa'" - "'ᖟ' > 'cwaa'" - "'ᖠ' > 'cwaa'" - "'ᖡ' > 'c'" - "'ᖢ' > 'th'" - "'ᖣ' > 'me'" - "'ᖤ' > 'maai'" - "'ᖥ' > 'mi'" - "'ᖦ' > 'mii'" - "'ᖧ' > 'mo'" - "'ᖨ' > 'moo'" - "'ᖩ' > 'moo'" - "'ᖪ' > 'ma'" - "'ᖫ' > 'maa'" - "'ᖬ' > 'mwe'" - "'ᖭ' > 'mwe'" - "'ᖮ' > 'mwi'" - "'ᖯ' > 'mwi'" - "'ᖰ' > 'mwii'" - "'ᖱ' > 'mwii'" - "'ᖲ' > 'mwo'" - "'ᖳ' > 'mwo'" - "'ᖴ' > 'mwoo'" - "'ᖵ' > 'mwoo'" - "'ᖶ' > 'mwa'" - "'ᖷ' > 'mwa'" - "'ᖸ' > 'mwaa'" - "'ᖹ' > 'mwaa'" - "'ᖺ' > 'mwaa'" - "'ᖻ' > 'm'" - "'ᖼ' > 'm'" - "'ᖽ' > 'mh'" - "'ᖾ' > 'm'" - "'ᖿ' > 'm'" - "'ᗀ' > 'ne'" - "'ᗁ' > 'naai'" - "'ᗂ' > 'ni'" - "'ᗃ' > 'nii'" - "'ᗄ' > 'no'" - "'ᗅ' > 'noo'" - "'ᗆ' > 'noo'" - "'ᗇ' > 'na'" - "'ᗈ' > 'naa'" - "'ᗉ' > 'nwe'" - "'ᗊ' > 'nwe'" - "'ᗋ' > 'nwa'" - "'ᗌ' > 'nwa'" - "'ᗍ' > 'nwaa'" - "'ᗎ' > 'nwaa'" - "'ᗏ' > 'nwaa'" - "'ᗐ' > 'n'" - "'ᗑ' > 'ng'" - "'ᗒ' > 'nh'" - "'ᗓ' > 'le'" - "'ᗔ' > 'laai'" - "'ᗕ' > 'li'" - "'ᗖ' > 'lii'" - "'ᗗ' > 'lo'" - "'ᗘ' > 'loo'" - "'ᗙ' > 'loo'" - "'ᗚ' > 'la'" - "'ᗛ' > 'laa'" - "'ᗜ' > 'lwe'" - "'ᗝ' > 'lwe'" - "'ᗞ' > 'lwi'" - "'ᗟ' > 'lwi'" - "'ᗠ' > 'lwii'" - "'ᗡ' > 'lwii'" - "'ᗢ' > 'lwo'" - "'ᗣ' > 'lwo'" - "'ᗤ' > 'lwoo'" - "'ᗥ' > 'lwoo'" - "'ᗦ' > 'lwa'" - "'ᗧ' > 'lwa'" - "'ᗨ' > 'lwaa'" - "'ᗩ' > 'lwaa'" - "'ᗪ' > 'l'" - "'ᗫ' > 'l'" - "'ᗬ' > 'l'" - "'ᗭ' > 'se'" - "'ᗮ' > 'saai'" - "'ᗯ' > 'si'" - "'ᗰ' > 'sii'" - "'ᗱ' > 'so'" - "'ᗲ' > 'soo'" - "'ᗳ' > 'soo'" - "'ᗴ' > 'sa'" - "'ᗵ' > 'saa'" - "'ᗶ' > 'swe'" - "'ᗷ' > 'swe'" - "'ᗸ' > 'swi'" - "'ᗹ' > 'swi'" - "'ᗺ' > 'swii'" - "'ᗻ' > 'swii'" - "'ᗼ' > 'swo'" - "'ᗽ' > 'swo'" - "'ᗾ' > 'swoo'" - "'ᗿ' > 'swoo'" - "'ᘀ' > 'swa'" - "'ᘁ' > 'swa'" - "'ᘂ' > 'swaa'" - "'ᘃ' > 'swaa'" - "'ᘄ' > 'swaa'" - "'ᘅ' > 's'" - "'ᘆ' > 's'" - "'ᘇ' > 'sw'" - "'ᘈ' > 's'" - "'ᘉ' > 'sk'" - "'ᘊ' > 'skw'" - "'ᘋ' > 'sw'" - "'ᘌ' > 'spwa'" - "'ᘍ' > 'stwa'" - "'ᘎ' > 'skwa'" - "'ᘏ' > 'scwa'" - "'ᘐ' > 'she'" - "'ᘑ' > 'shi'" - "'ᘒ' > 'shii'" - "'ᘓ' > 'sho'" - "'ᘔ' > 'shoo'" - "'ᘕ' > 'sha'" - "'ᘖ' > 'shaa'" - "'ᘗ' > 'shwe'" - "'ᘘ' > 'shwe'" - "'ᘙ' > 'shwi'" - "'ᘚ' > 'shwi'" - "'ᘛ' > 'shwii'" - "'ᘜ' > 'shwii'" - "'ᘝ' > 'shwo'" - "'ᘞ' > 'shwo'" - "'ᘟ' > 'shwoo'" - "'ᘠ' > 'shwoo'" - "'ᘡ' > 'shwa'" - "'ᘢ' > 'shwa'" - "'ᘣ' > 'shwaa'" - "'ᘤ' > 'shwaa'" - "'ᘥ' > 'sh'" - "'ᘦ' > 'ye'" - "'ᘧ' > 'yaai'" - "'ᘨ' > 'yi'" - "'ᘩ' > 'yii'" - "'ᘪ' > 'yo'" - "'ᘫ' > 'yoo'" - "'ᘬ' > 'yoo'" - "'ᘭ' > 'ya'" - "'ᘮ' > 'yaa'" - "'ᘯ' > 'ywe'" - "'ᘰ' > 'ywe'" - "'ᘱ' > 'ywi'" - "'ᘲ' > 'ywi'" - "'ᘳ' > 'ywii'" - "'ᘴ' > 'ywii'" - "'ᘵ' > 'ywo'" - "'ᘶ' > 'ywo'" - "'ᘷ' > 'ywoo'" - "'ᘸ' > 'ywoo'" - "'ᘹ' > 'ywa'" - "'ᘺ' > 'ywa'" - "'ᘻ' > 'ywaa'" - "'ᘼ' > 'ywaa'" - "'ᘽ' > 'ywaa'" - "'ᘾ' > 'y'" - "'ᘿ' > 'y'" - "'ᙀ' > 'y'" - "'ᙁ' > 'yi'" - "'ᙂ' > 're'" - "'ᙃ' > 're'" - "'ᙄ' > 'le'" - "'ᙅ' > 'raai'" - "'ᙆ' > 'ri'" - "'ᙇ' > 'rii'" - "'ᙈ' > 'ro'" - "'ᙉ' > 'roo'" - "'ᙊ' > 'lo'" - "'ᙋ' > 'ra'" - "'ᙌ' > 'raa'" - "'ᙍ' > 'la'" - "'ᙎ' > 'rwaa'" - "'ᙏ' > 'rwaa'" - "'ᙐ' > 'r'" - "'ᙑ' > 'r'" - "'ᙒ' > 'r'" - "'ᙓ' > 'fe'" - "'ᙔ' > 'faai'" - "'ᙕ' > 'fi'" - "'ᙖ' > 'fii'" - "'ᙗ' > 'fo'" - "'ᙘ' > 'foo'" - "'ᙙ' > 'fa'" - "'ᙚ' > 'faa'" - "'ᙛ' > 'fwaa'" - "'ᙜ' > 'fwaa'" - "'ᙝ' > 'f'" - "'ᙞ' > 'the'" - "'ᙟ' > 'the'" - "'ᙠ' > 'thi'" - "'ᙡ' > 'thi'" - "'ᙢ' > 'thii'" - "'ᙣ' > 'thii'" - "'ᙤ' > 'tho'" - "'ᙥ' > 'thoo'" - "'ᙦ' > 'tha'" - "'ᙧ' > 'thaa'" - "'ᙨ' > 'thwaa'" - "'ᙩ' > 'thwaa'" - "'ᙪ' > 'th'" - "'ᙫ' > 'tthe'" - "'ᙬ' > 'tthi'" - "'ᙯ' > 'tth'" - "'ᙰ' > 'tye'" - "'ᙱ' > 'tyi'" - "'ᙲ' > 'tyo'" - "'ᙳ' > 'tya'" - "'ᙴ' > 'he'" - "'ᙵ' > 'hi'" - "'ᙶ' > 'hii'" - "'ᙷ' > 'ho'" - "'ᙸ' > 'hoo'" - "'ᙹ' > 'ha'" - "'ᙺ' > 'haa'" - "'ᙻ' > 'h'" - "'ᙼ' > 'h'" - "'ᙽ' > 'hk'" - "'ᙾ' > 'qaai'" - "'ᙿ' > 'qi'" - "'ᚁ' > 'qo'" - "'ᚂ' > 'qoo'" - "'ᚃ' > 'qa'" - "'ᚄ' > 'qaa'" - "'ᚅ' > 'q'" - "'ᚆ' > 'tlhe'" - "'ᚇ' > 'tlhi'" - "'ᚈ' > 'tlho'" - "'ᚉ' > 'tlha'" - "'ᚊ' > 're'" - "'ᚋ' > 'ri'" - "'ᚌ' > 'ro'" - "'ᚍ' > 'ra'" - "'ᚎ' > 'ngaai'" - "'ᚏ' > 'ngi'" - "'ᚐ' > 'ngii'" - "'ᚑ' > 'ngo'" - "'ᚒ' > 'ngoo'" - "'ᚓ' > 'nga'" - "'ᚔ' > 'ngaa'" - "'ᚕ' > 'ng'" - "'ᚖ' > 'nng'" - "'ᚗ' > 'she'" - "'ᚘ' > 'shi'" - "'ᚙ' > 'sho'" - "'ᚚ' > 'sha'" - "'ᚠ' > 'lhi'" - "'ᚡ' > 'lhii'" - "'ᚢ' > 'lho'" - "'ᚣ' > 'lhoo'" - "'ᚤ' > 'lha'" - "'ᚥ' > 'lhaa'" - "'ᚦ' > 'lh'" - "'ᚧ' > 'the'" - "'ᚨ' > 'thi'" - "'ᚩ' > 'thii'" - "'ᚪ' > 'tho'" - "'ᚫ' > 'thoo'" - "'ᚬ' > 'tha'" - "'ᚭ' > 'thaa'" - "'ᚮ' > 'th'" - "'ᚯ' > 'b'" - "'ᚰ' > 'e'" - "'ᚱ' > 'i'" - "'ᚲ' > 'o'" - "'ᚳ' > 'a'" - "'ᚴ' > 'we'" - "'ᚵ' > 'wi'" - "'ᚶ' > 'wo'" - "'ᚷ' > 'wa'" - "'ᚸ' > 'ne'" - "'ᚹ' > 'ni'" - "'ᚺ' > 'no'" - "'ᚻ' > 'na'" - "'ᚼ' > 'ke'" - "'ᚽ' > 'ki'" - "'ᚾ' > 'ko'" - "'ᚿ' > 'ka'" - "'ᛀ' > 'he'" - "'ᛁ' > 'hi'" - "'ᛂ' > 'ho'" - "'ᛃ' > 'ha'" - "'ᛄ' > 'ghu'" - "'ᛅ' > 'gho'" - "'ᛆ' > 'ghe'" - "'ᛇ' > 'ghee'" - "'ᛈ' > 'ghi'" - "'ᛉ' > 'gha'" - "'ᛊ' > 'ru'" - "'ᛋ' > 'ro'" - "'ᛌ' > 're'" - "'ᛍ' > 'ree'" - "'ᛎ' > 'ri'" - "'ᛏ' > 'ra'" - "'ᛐ' > 'wu'" - "'ᛑ' > 'wo'" - "'ᛒ' > 'we'" - "'ᛓ' > 'wee'" - "'ᛔ' > 'wi'" - "'ᛕ' > 'wa'" - "'ᛖ' > 'hwu'" - "'ᛗ' > 'hwo'" - "'ᛘ' > 'hwe'" - "'ᛙ' > 'hwee'" - "'ᛚ' > 'hwi'" - "'ᛛ' > 'hwa'" - "'ᛜ' > 'thu'" - "'ᛝ' > 'tho'" - "'ᛞ' > 'the'" - "'ᛟ' > 'thee'" - "'ᛠ' > 'thi'" - "'ᛡ' > 'tha'" - "'ᛢ' > 'ttu'" - "'ᛣ' > 'tto'" - "'ᛤ' > 'tte'" - "'ᛥ' > 'ttee'" - "'ᛦ' > 'tti'" - "'ᛧ' > 'tta'" - "'ᛨ' > 'pu'" - "'ᛩ' > 'po'" - "'ᛪ' > 'pe'" - "'ᛱ' > 'ge'" - "'ᛲ' > 'gee'" - "'ᛳ' > 'gi'" - "'ᛴ' > 'ga'" - "'ᛵ' > 'khu'" - "'ᛶ' > 'kho'" - "'ᛷ' > 'khe'" - "'ᛸ' > 'khee'" - "'ᜀ' > 'kka'" - "'ᜁ' > 'kk'" - "'ᜂ' > 'nu'" - "'ᜃ' > 'no'" - "'ᜄ' > 'ne'" - "'ᜅ' > 'nee'" - "'ᜆ' > 'ni'" - "'ᜇ' > 'na'" - "'ᜈ' > 'mu'" - "'ᜉ' > 'mo'" - "'ᜊ' > 'me'" - "'ᜋ' > 'mee'" - "'ᜌ' > 'mi'" - "'ᜎ' > 'yu'" - "'ᜏ' > 'yo'" - "'ᜐ' > 'ye'" - "'ᜑ' > 'yee'" - "'ᜠ' > 'jji'" - "'ᜡ' > 'jja'" - "'ᜢ' > 'lu'" - "'ᜣ' > 'lo'" - "'ᜤ' > 'le'" - "'ᜥ' > 'lee'" - "'ᜦ' > 'li'" - "'ᜧ' > 'la'" - "'ᜨ' > 'dlu'" - "'ᜩ' > 'dlo'" - "'ᜪ' > 'dle'" - "'ᜫ' > 'dlee'" - "'ᜬ' > 'dli'" - "'ᜭ' > 'dla'" - "'ᜮ' > 'lhu'" - "'ᜯ' > 'lho'" - "'ᜰ' > 'lhe'" - "'ᜱ' > 'lhee'" - "'ᝀ' > 'zu'" - "'ᝁ' > 'zo'" - "'ᝂ' > 'ze'" - "'ᝃ' > 'zee'" - "'ᝄ' > 'zi'" - "'ᝅ' > 'za'" - "'ᝆ' > 'z'" - "'ᝇ' > 'z'" - "'ᝈ' > 'dzu'" - "'ᝉ' > 'dzo'" - "'ᝊ' > 'dze'" - "'ᝋ' > 'dzee'" - "'ᝌ' > 'dzi'" - "'ᝍ' > 'dza'" - "'ᝎ' > 'su'" - "'ᝏ' > 'so'" - "'ᝐ' > 'se'" - "'ᝑ' > 'see'" - "'ᝠ' > 'tsa'" - "'ᝡ' > 'chu'" - "'ᝢ' > 'cho'" - "'ᝣ' > 'che'" - "'ᝤ' > 'chee'" - "'ᝥ' > 'chi'" - "'ᝦ' > 'cha'" - "'ᝧ' > 'ttsu'" - "'ᝨ' > 'ttso'" - "'ᝩ' > 'ttse'" - "'ᝪ' > 'ttsee'" - "'ᝫ' > 'ttsi'" - "'ᝬ' > 'ttsa'" - "'ᝮ' > 'la'" - "'ᝯ' > 'qai'" - "'ᝰ' > 'ngai'" - "'ក' > 'ka'" - "'ខ' > 'b'" - "'គ' > 'l'" - "'ឃ' > 'f'" - "'ង' > 's'" - "'ច' > 'n'" - "'ឆ' > 'h'" - "'ជ' > 'd'" - "'ឈ' > 't'" - "'ញ' > 'c'" - "'ដ' > 'q'" - "'ឋ' > 'm'" - "'ឌ' > 'g'" - "'ឍ' > 'ng'" - "'ណ' > 'z'" - "'ត' > 'r'" - "'ថ' > 'a'" - "'ទ' > 'o'" - "'ធ' > 'u'" - "'ន' > 'e'" - "'ប' > 'i'" - "'ផ' > 'ch'" - "'ព' > 'th'" - "'ភ' > 'ph'" - "'ម' > 'p'" - "'យ' > 'x'" - "'រ' > 'p'" - "'ល' > 'lo'" - "'វ' > 'vo'" - "'ឝ' > 'sha'" - "'ឞ' > 'sso'" - "'ស' > 'sa'" - "'ហ' > 'f'" - "'ឡ' > 'v'" - "'អ' > 'u'" - "'ឣ' > 'yr'" - "'ឤ' > 'y'" - "'ឥ' > 'w'" - "'ឦ' > 'th'" - "'ឧ' > 'th'" - "'ឨ' > 'a'" - "'ឩ' > 'o'" - "'ឪ' > 'ac'" - "'ឫ' > 'ae'" - "'ឬ' > 'o'" - "'ឭ' > 'o'" - "'ឮ' > 'o'" - "'ឯ' > 'oe'" - "'ឰ' > 'on'" - "'ឱ' > 'r'" - "'ឲ' > 'k'" - "'ឳ' > 'c'" - "'ៗ' > 'm'" - "'ៜ' > 'ng'" - "'ᠠ' > 'a'" - "'ᠡ' > 'e'" - "'ᠢ' > 'i'" - "'ᠣ' > 'o'" - "'ᠤ' > 'u'" - "'ᠥ' > 'oe'" - "'ᠦ' > 'ue'" - "'ᠧ' > 'ee'" - "'ᠨ' > 'na'" - "'ᠩ' > 'ang'" - "'ᠪ' > 'ba'" - "'ᠫ' > 'pa'" - "'ᠬ' > 'qa'" - "'ᠭ' > 'ga'" - "'ᠮ' > 'ma'" - "'ᠯ' > 'la'" - "'ᠰ' > 'sa'" - "'ᠱ' > 'sha'" - "'ᠲ' > 'ta'" - "'ᠳ' > 'da'" - "'ᠴ' > 'cha'" - "'ᠵ' > 'ja'" - "'ᠶ' > 'ya'" - "'ᠷ' > 'ra'" - "'ᠸ' > 'wa'" - "'ᠹ' > 'fa'" - "'ᠺ' > 'ka'" - "'ᠻ' > 'kha'" - "'ᠼ' > 'tsa'" - "'ᠽ' > 'za'" - "'ᠾ' > 'haa'" - "'ᠿ' > 'zra'" - "'ᡀ' > 'lha'" - "'ᡁ' > 'zhi'" - "'ᡂ' > 'chi'" - "'ᢀ' > 'k'" - "'ᢁ' > 'kh'" - "'ᢂ' > 'g'" - "'ᢃ' > 'gh'" - "'ᢄ' > 'ng'" - "'ᢇ' > 'j'" - "'ᢈ' > 'jh'" - "'ᢉ' > 'ny'" - "'ᢊ' > 't'" - "'ᢋ' > 'tth'" - "'ᢌ' > 'd'" - "'ᢍ' > 'ddh'" - "'ᢎ' > 'nn'" - "'ᢏ' > 't'" - "'ᢐ' > 'th'" - "'ᢑ' > 'd'" - "'ᢒ' > 'dh'" - "'ᢓ' > 'n'" - "'ᢔ' > 'p'" - "'ᢕ' > 'ph'" - "'ᢖ' > 'b'" - "'ᢗ' > 'bh'" - "'ᢘ' > 'm'" - "'ᢙ' > 'y'" - "'ᢚ' > 'r'" - "'ᢛ' > 'l'" - "'ᢜ' > 'v'" - "'ᢝ' > 'sh'" - "'ᢞ' > 'ss'" - "'ᢟ' > 's'" - "'ᢠ' > 'h'" - "'ᢡ' > 'l'" - "'ᢢ' > 'q'" - "'ᢣ' > 'a'" - "'ᢤ' > 'aa'" - "'ᢥ' > 'i'" - "'ᢦ' > 'ii'" - "'ᢧ' > 'u'" - "'ᢨ' > 'uk'" - "'ᢪ' > 'uuv'" - "'ᢰ' > 'ai'" - "'ᢱ' > 'oo'" - "'ᢲ' > 'oo'" - "'ᢳ' > 'au'" - "'ᢴ' > 'a'" - "'ᢵ' > 'aa'" - "'ᢶ' > 'aa'" - "'ᢷ' > 'i'" - "'ᢸ' > 'ii'" - "'ᢹ' > 'y'" - "'ᢺ' > 'yy'" - "'ᢻ' > 'u'" - "'ᢼ' > 'uu'" - "'ᢽ' > 'ua'" - "'ᢾ' > 'oe'" - "'ᢿ' > 'ya'" - "'ᣀ' > 'ie'" - "'ᣁ' > 'e'" - "'ᣂ' > 'ae'" - "'ᣃ' > 'ai'" - "'ᣄ' > 'oo'" - "'ᣅ' > 'au'" - "'ᣆ' > 'm'" - "'ᣇ' > 'h'" - "'ᣈ' > 'a'" - "'ᣌ' > 'r'" - "'ᣛ' > 'kr'" - "'ᤁ' > 'ka'" - "'ᤂ' > 'kha'" - "'ᤃ' > 'ga'" - "'ᤄ' > 'gha'" - "'ᤅ' > 'nga'" - "'ᤆ' > 'ca'" - "'ᤇ' > 'cha'" - "'ᤈ' > 'ja'" - "'ᤉ' > 'jha'" - "'ᤊ' > 'yan'" - "'ᤋ' > 'ta'" - "'ᤌ' > 'tha'" - "'ᤍ' > 'da'" - "'ᤎ' > 'dha'" - "'ᤏ' > 'na'" - "'ᤐ' > 'pa'" - "'ᤑ' > 'pha'" - "'ᤒ' > 'ba'" - "'ᤓ' > 'bha'" - "'ᤔ' > 'ma'" - "'ᤕ' > 'ya'" - "'ᤖ' > 'ra'" - "'ᤗ' > 'la'" - "'ᤘ' > 'wa'" - "'ᤙ' > 'sha'" - "'ᤚ' > 'ssa'" - "'ᤛ' > 'sa'" - "'ᤜ' > 'ha'" - "'ᥐ' > 'ka'" - "'ᥑ' > 'xa'" - "'ᥒ' > 'nga'" - "'ᥓ' > 'tsa'" - "'ᥔ' > 'sa'" - "'ᥕ' > 'ya'" - "'ᥖ' > 'ta'" - "'ᥗ' > 'tha'" - "'ᥘ' > 'la'" - "'ᥙ' > 'pa'" - "'ᥚ' > 'pha'" - "'ᥛ' > 'ma'" - "'ᥜ' > 'fa'" - "'ᥝ' > 'va'" - "'ᥞ' > 'ha'" - "'ᥟ' > 'qa'" - "'ᥠ' > 'kha'" - "'ᥡ' > 'tsha'" - "'ᥢ' > 'na'" - "'ᥣ' > 'a'" - "'ᥤ' > 'i'" - "'ᥥ' > 'ee'" - "'ᥦ' > 'eh'" - "'ᥧ' > 'u'" - "'ᥨ' > 'oo'" - "'ᥩ' > 'o'" - "'ᥪ' > 'ue'" - "'ᥫ' > 'e'" - "'ᥬ' > 'aue'" - "'ᥭ' > 'ai'" - "'ᦁ' > 'qa'" - "'ᦅ' > 'ka'" - "'ᦆ' > 'xa'" - "'ᦇ' > 'nga'" - "'ᦋ' > 'tsa'" - "'ᦌ' > 'sa'" - "'ᦍ' > 'ya'" - "'ᦑ' > 'ta'" - "'ᦒ' > 'tha'" - "'ᦓ' > 'na'" - "'ᦗ' > 'pa'" - "'ᦘ' > 'pha'" - "'ᦙ' > 'ma'" - "'ᦝ' > 'fa'" - "'ᦞ' > 'va'" - "'ᦟ' > 'la'" - "'ᦣ' > 'ha'" - "'ᦤ' > 'da'" - "'ᦥ' > 'ba'" - "'ᦨ' > 'kva'" - "'ᦩ' > 'xva'" - "'ᦱ' > 'aa'" - "'ᦲ' > 'ii'" - "'ᦳ' > 'u'" - "'ᦴ' > 'uu'" - "'ᦵ' > 'e'" - "'ᦶ' > 'ae'" - "'ᦷ' > 'o'" - "'ᦸ' > 'oa'" - "'ᦹ' > 'ue'" - "'ᦺ' > 'ay'" - "'ᦻ' > 'aay'" - "'ᦼ' > 'uy'" - "'ᦽ' > 'oy'" - "'ᦾ' > 'oay'" - "'ᦿ' > 'uey'" - "'ᧀ' > 'iy'" - "'ᨀ' > 'ka'" - "'ᨁ' > 'ga'" - "'ᨂ' > 'nga'" - "'ᨃ' > 'ngka'" - "'ᨄ' > 'pa'" - "'ᨅ' > 'ba'" - "'ᨆ' > 'ma'" - "'ᨇ' > 'mpa'" - "'ᨈ' > 'ta'" - "'ᨉ' > 'da'" - "'ᨊ' > 'na'" - "'ᨋ' > 'nra'" - "'ᨌ' > 'ca'" - "'ᨍ' > 'ja'" - "'ᨎ' > 'nya'" - "'ᨏ' > 'nyca'" - "'ᨐ' > 'ya'" - "'ᨑ' > 'ra'" - "'ᨒ' > 'la'" - "'ᨓ' > 'va'" - "'ᨔ' > 'sa'" - "'ᨕ' > 'a'" - "'ᨖ' > 'ha'" - "'ᬅ' > 'akara'" - "'ᬆ' > 'akara'" - "'ᬇ' > 'ikara'" - "'ᬈ' > 'ikara'" - "'ᬉ' > 'ukara'" - "'ᬊ' > 'ukara'" - "'ᬋ' > 'ra'" - "'ᬌ' > 'ra'" - "'ᬍ' > 'la'" - "'ᬎ' > 'la'" - "'ᬏ' > 'ekara'" - "'ᬐ' > 'aikara'" - "'ᬑ' > 'okara'" - "'ᬒ' > 'okara'" - "'ᬓ' > 'ka'" - "'ᬔ' > 'ka'" - "'ᬕ' > 'ga'" - "'ᬖ' > 'ga'" - "'ᬗ' > 'nga'" - "'ᬘ' > 'ca'" - "'ᬙ' > 'ca'" - "'ᬚ' > 'ja'" - "'ᬛ' > 'ja'" - "'ᬜ' > 'nya'" - "'ᬝ' > 'ta'" - "'ᬞ' > 'ta'" - "'ᬟ' > 'da'" - "'ᬠ' > 'da'" - "'ᬡ' > 'na'" - "'ᬢ' > 'ta'" - "'ᬣ' > 'ta'" - "'ᬤ' > 'da'" - "'ᬥ' > 'da'" - "'ᬦ' > 'na'" - "'ᬧ' > 'pa'" - "'ᬨ' > 'pa'" - "'ᬩ' > 'ba'" - "'ᬪ' > 'ba'" - "'ᬫ' > 'ma'" - "'ᬬ' > 'ya'" - "'ᬭ' > 'ra'" - "'ᬮ' > 'la'" - "'ᬯ' > 'wa'" - "'ᬰ' > 'sa'" - "'ᬱ' > 'sa'" - "'ᬲ' > 'sa'" - "'ᬳ' > 'ha'" - "'ᭅ' > 'kaf'" - "'ᭆ' > 'khot'" - "'ᭇ' > 'tzir'" - "'ᭈ' > 'ef'" - "'ᭉ' > 've'" - "'ᭊ' > 'zal'" - "'ᭋ' > 'asyura'" - "'ᮃ' > 'a'" - "'ᮄ' > 'i'" - "'ᮅ' > 'u'" - "'ᮆ' > 'ae'" - "'ᮇ' > 'o'" - "'ᮈ' > 'e'" - "'ᮉ' > 'eu'" - "'ᮊ' > 'ka'" - "'ᮋ' > 'qa'" - "'ᮌ' > 'ga'" - "'ᮍ' > 'nga'" - "'ᮎ' > 'ca'" - "'ᮏ' > 'ja'" - "'ᮐ' > 'za'" - "'ᮑ' > 'nya'" - "'ᮒ' > 'ta'" - "'ᮓ' > 'da'" - "'ᮔ' > 'na'" - "'ᮕ' > 'pa'" - "'ᮖ' > 'fa'" - "'ᮗ' > 'va'" - "'ᮘ' > 'ba'" - "'ᮙ' > 'ma'" - "'ᮚ' > 'ya'" - "'ᮛ' > 'ra'" - "'ᮜ' > 'la'" - "'ᮝ' > 'wa'" - "'ᮞ' > 'sa'" - "'ᮟ' > 'xa'" - "'ᮠ' > 'ha'" - "'ᮮ' > 'kha'" - "'ᮯ' > 'sya'" - "'ᰀ' > 'ka'" - "'ᰁ' > 'kla'" - "'ᰂ' > 'kha'" - "'ᰃ' > 'ga'" - "'ᰄ' > 'gla'" - "'ᰅ' > 'nga'" - "'ᰆ' > 'ca'" - "'ᰇ' > 'cha'" - "'ᰈ' > 'ja'" - "'ᰉ' > 'nya'" - "'ᰊ' > 'ta'" - "'ᰋ' > 'tha'" - "'ᰌ' > 'da'" - "'ᰍ' > 'na'" - "'ᰎ' > 'pa'" - "'ᰏ' > 'pla'" - "'ᰐ' > 'pha'" - "'ᰑ' > 'fa'" - "'ᰒ' > 'fla'" - "'ᰓ' > 'ba'" - "'ᰔ' > 'bla'" - "'ᰕ' > 'ma'" - "'ᰖ' > 'mla'" - "'ᰗ' > 'tsa'" - "'ᰘ' > 'tsha'" - "'ᰙ' > 'dza'" - "'ᰚ' > 'ya'" - "'ᰛ' > 'ra'" - "'ᰜ' > 'la'" - "'ᰝ' > 'ha'" - "'ᰞ' > 'hla'" - "'ᰟ' > 'va'" - "'ᰠ' > 'sa'" - "'ᰡ' > 'sha'" - "'ᰢ' > 'wa'" - "'ᰣ' > 'a'" - "'ᱍ' > 'tta'" - "'ᱎ' > 'ttha'" - "'ᱏ' > 'dda'" - "'ᱚ' > 'la'" - "'ᱛ' > 'at'" - "'ᱜ' > 'ag'" - "'ᱝ' > 'ang'" - "'ᱞ' > 'al'" - "'ᱟ' > 'laa'" - "'ᱠ' > 'aak'" - "'ᱡ' > 'aaj'" - "'ᱢ' > 'aam'" - "'ᱣ' > 'aaw'" - "'ᱤ' > 'li'" - "'ᱥ' > 'is'" - "'ᱦ' > 'ih'" - "'ᱧ' > 'iny'" - "'ᱨ' > 'ir'" - "'ᱩ' > 'lu'" - "'ᱪ' > 'uc'" - "'ᱫ' > 'ud'" - "'ᱬ' > 'unn'" - "'ᱭ' > 'uy'" - "'ᱮ' > 'le'" - "'ᱯ' > 'ep'" - "'ᱰ' > 'edd'" - "'ᱱ' > 'en'" - "'ᱲ' > 'err'" - "'ᱳ' > 'lo'" - "'ᱴ' > 'ott'" - "'ᱵ' > 'ob'" - "'ᱶ' > 'ov'" - "'ᱷ' > 'oh'" - "'ᴂ' > 'ae'" - "'ᴉ' > 'i'" - "'ᴔ' > 'oe'" - "'ᴥ' > 'ain'" - "'ᵃ' > 'a'" - "'ᵇ' > 'b'" - "'ᵈ' > 'd'" - "'ᵉ' > 'e'" - "'ᵍ' > 'g'" - "'ᵏ' > 'k'" - "'ᵐ' > 'm'" - "'ᵑ' > 'eng'" - "'ᵒ' > 'o'" - "'ᵖ' > 'p'" - "'ᵗ' > 't'" - "'ᵘ' > 'u'" - "'ᵛ' > 'v'" - "'ᵜ' > 'ain'" - "'ᵝ' > 'beta'" - "'ᵞ' > 'greek'" - "'ᵟ' > 'delta'" - "'ᵠ' > 'greek'" - "'ᵡ' > 'chi'" - "'ᵢ' > 'i'" - "'ᵣ' > 'r'" - "'ᵤ' > 'u'" - "'ᵥ' > 'v'" - "'ᵦ' > 'beta'" - "'ᵧ' > 'gamma'" - "'ᵨ' > 'rho'" - "'ᵩ' > 'phi'" - "'ᵪ' > 'chi'" - "'ᵷ' > 'g'" - "'ᵿ' > 'upsilon'" - "'ᶋ' > 'esh'" - "'ᶐ' > 'alpha'" - "'ᶗ' > 'o'" - "'ᶘ' > 'esh'" - "'ᶚ' > 'ezh'" - "'ᶜ' > 'c'" - "'ᶝ' > 'c'" - "'ᶞ' > 'eth'" - "'ᶠ' > 'f'" - "'ᶤ' > 'i'" - "'ᶥ' > 'iota'" - "'ᶨ' > 'j'" - "'ᶩ' > 'l'" - "'ᶪ' > 'l'" - "'ᶬ' > 'm'" - "'ᶮ' > 'n'" - "'ᶯ' > 'n'" - "'ᶲ' > 'phi'" - "'ᶳ' > 's'" - "'ᶴ' > 'esh'" - "'ᶵ' > 't'" - "'ᶶ' > 'u'" - "'ᶷ' > 'upsilon'" - "'ᶹ' > 'v'" - "'ᶻ' > 'z'" - "'ᶼ' > 'z'" - "'ᶽ' > 'z'" - "'ᶾ' > 'ezh'" - "'ᶿ' > 'theta'" - "'ẟ' > 'ddh'" - "'ⁱ' > 'i'" - "'ⁿ' > 'n'" - "'ₐ' > 'a'" - "'ₑ' > 'e'" - "'ₒ' > 'o'" - "'ₓ' > 'x'" - "'ↄ' > 'c'" - "'Ⰰ' > 'azu'" - "'Ⰱ' > 'buky'" - "'Ⰲ' > 'vede'" - "'Ⰳ' > 'glagoli'" - "'Ⰴ' > 'dobro'" - "'Ⰵ' > 'yestu'" - "'Ⰶ' > 'zhivete'" - "'Ⰷ' > 'dzelo'" - "'Ⰸ' > 'zemlja'" - "'Ⰹ' > 'izhe'" - "'Ⰺ' > 'initial'" - "'Ⰻ' > 'i'" - "'Ⰼ' > 'djervi'" - "'Ⰽ' > 'kako'" - "'Ⰾ' > 'ljudije'" - "'Ⰿ' > 'myslite'" - "'Ⱀ' > 'nashi'" - "'Ⱁ' > 'onu'" - "'Ⱂ' > 'pokoji'" - "'Ⱃ' > 'ritsi'" - "'Ⱄ' > 'slovo'" - "'Ⱅ' > 'tvrido'" - "'Ⱆ' > 'uku'" - "'Ⱇ' > 'fritu'" - "'Ⱈ' > 'heru'" - "'Ⱉ' > 'otu'" - "'Ⱊ' > 'pe'" - "'Ⱋ' > 'shta'" - "'Ⱌ' > 'tsi'" - "'Ⱍ' > 'chrivi'" - "'Ⱎ' > 'sha'" - "'Ⱏ' > 'yeru'" - "'Ⱐ' > 'yeri'" - "'Ⱑ' > 'yati'" - "'Ⱓ' > 'yu'" - "'Ⱔ' > 'yus'" - "'Ⱕ' > 'yus'" - "'Ⱖ' > 'yo'" - "'Ⱚ' > 'fita'" - "'Ⱛ' > 'izhitsa'" - "'Ⱜ' > 'shtapic'" - "'Ⱝ' > 'trokutasti'" - "'Ⱞ' > 'latinate'" - "'ⰰ' > 'azu'" - "'ⰱ' > 'buky'" - "'ⰲ' > 'vede'" - "'ⰳ' > 'glagoli'" - "'ⰴ' > 'dobro'" - "'ⰵ' > 'yestu'" - "'ⰶ' > 'zhivete'" - "'ⰷ' > 'dzelo'" - "'ⰸ' > 'zemlja'" - "'ⰹ' > 'izhe'" - "'ⰺ' > 'initial'" - "'ⰻ' > 'i'" - "'ⰼ' > 'djervi'" - "'ⰽ' > 'kako'" - "'ⰾ' > 'ljudije'" - "'ⰿ' > 'myslite'" - "'ⱀ' > 'nashi'" - "'ⱁ' > 'onu'" - "'ⱂ' > 'pokoji'" - "'ⱃ' > 'ritsi'" - "'ⱄ' > 'slovo'" - "'ⱅ' > 'tvrido'" - "'ⱆ' > 'uku'" - "'ⱇ' > 'fritu'" - "'ⱈ' > 'heru'" - "'ⱉ' > 'otu'" - "'ⱊ' > 'pe'" - "'ⱋ' > 'shta'" - "'ⱌ' > 'tsi'" - "'ⱍ' > 'chrivi'" - "'ⱎ' > 'sha'" - "'ⱏ' > 'yeru'" - "'ⱐ' > 'yeri'" - "'ⱑ' > 'yati'" - "'ⱓ' > 'yu'" - "'ⱔ' > 'yus'" - "'ⱕ' > 'yus'" - "'ⱖ' > 'yo'" - "'ⱚ' > 'fita'" - "'ⱛ' > 'izhitsa'" - "'ⱜ' > 'shtapic'" - "'ⱝ' > 'trokutasti'" - "'ⱞ' > 'latinate'" - "'Ⱡ' > 'l'" - "'ⱡ' > 'l'" - "'Ɫ' > 'l'" - "'Ᵽ' > 'p'" - "'Ɽ' > 'r'" - "'ⱥ' > 'a'" - "'ⱦ' > 't'" - "'Ⱨ' > 'h'" - "'ⱨ' > 'h'" - "'Ⱪ' > 'k'" - "'ⱪ' > 'k'" - "'Ⱬ' > 'z'" - "'ⱬ' > 'z'" - "'Ɑ' > 'alpha'" - "'Ɱ' > 'm'" - "'Ɐ' > 'a'" - "'ⱱ' > 'v'" - "'Ⱳ' > 'w'" - "'ⱳ' > 'w'" - "'ⱴ' > 'v'" - "'ⱸ' > 'e'" - "'ⱹ' > 'r'" - "'ⱺ' > 'o'" - "'ⱼ' > 'j'" - "'Ⲁ' > 'alfa'" - "'ⲁ' > 'alfa'" - "'Ⲃ' > 'vida'" - "'ⲃ' > 'vida'" - "'Ⲅ' > 'gamma'" - "'ⲅ' > 'gamma'" - "'Ⲇ' > 'dalda'" - "'ⲇ' > 'dalda'" - "'Ⲉ' > 'eie'" - "'ⲉ' > 'eie'" - "'Ⲋ' > 'sou'" - "'ⲋ' > 'sou'" - "'Ⲍ' > 'zata'" - "'ⲍ' > 'zata'" - "'Ⲏ' > 'hate'" - "'ⲏ' > 'hate'" - "'Ⲑ' > 'thethe'" - "'ⲑ' > 'thethe'" - "'Ⲓ' > 'iauda'" - "'ⲓ' > 'iauda'" - "'Ⲕ' > 'kapa'" - "'ⲕ' > 'kapa'" - "'Ⲗ' > 'laula'" - "'ⲗ' > 'laula'" - "'Ⲙ' > 'mi'" - "'ⲙ' > 'mi'" - "'Ⲛ' > 'ni'" - "'ⲛ' > 'ni'" - "'Ⲝ' > 'ksi'" - "'ⲝ' > 'ksi'" - "'Ⲟ' > 'o'" - "'ⲟ' > 'o'" - "'Ⲡ' > 'pi'" - "'ⲡ' > 'pi'" - "'Ⲣ' > 'ro'" - "'ⲣ' > 'ro'" - "'Ⲥ' > 'sima'" - "'ⲥ' > 'sima'" - "'Ⲧ' > 'tau'" - "'ⲧ' > 'tau'" - "'Ⲩ' > 'ua'" - "'ⲩ' > 'ua'" - "'Ⲫ' > 'fi'" - "'ⲫ' > 'fi'" - "'Ⲭ' > 'khi'" - "'ⲭ' > 'khi'" - "'Ⲯ' > 'psi'" - "'ⲯ' > 'psi'" - "'Ⲱ' > 'oou'" - "'ⲱ' > 'oou'" - "'Ⳁ' > 'sampi'" - "'ⳁ' > 'sampi'" - "'ⴀ' > 'an'" - "'ⴁ' > 'ban'" - "'ⴂ' > 'gan'" - "'ⴃ' > 'don'" - "'ⴄ' > 'en'" - "'ⴅ' > 'vin'" - "'ⴆ' > 'zen'" - "'ⴇ' > 'tan'" - "'ⴈ' > 'in'" - "'ⴉ' > 'kan'" - "'ⴊ' > 'las'" - "'ⴋ' > 'man'" - "'ⴌ' > 'nar'" - "'ⴍ' > 'on'" - "'ⴎ' > 'par'" - "'ⴏ' > 'zhar'" - "'ⴐ' > 'rae'" - "'ⴑ' > 'san'" - "'ⴒ' > 'tar'" - "'ⴓ' > 'un'" - "'ⴔ' > 'phar'" - "'ⴕ' > 'khar'" - "'ⴖ' > 'ghan'" - "'ⴗ' > 'qar'" - "'ⴘ' > 'shin'" - "'ⴙ' > 'chin'" - "'ⴚ' > 'can'" - "'ⴛ' > 'jil'" - "'ⴜ' > 'cil'" - "'ⴝ' > 'char'" - "'ⴞ' > 'xan'" - "'ⴟ' > 'jhan'" - "'ⴠ' > 'hae'" - "'ⴡ' > 'he'" - "'ⴢ' > 'hie'" - "'ⴣ' > 'we'" - "'ⴤ' > 'har'" - "'ⴥ' > 'hoe'" - "'ⴰ' > 'ya'" - "'ⴱ' > 'yab'" - "'ⴲ' > 'yabh'" - "'ⴳ' > 'yag'" - "'ⴴ' > 'yaghh'" - "'ⴶ' > 'yaj'" - "'ⴷ' > 'yad'" - "'ⴸ' > 'yadh'" - "'ⴹ' > 'yadd'" - "'ⴺ' > 'yaddh'" - "'ⴻ' > 'yey'" - "'ⴼ' > 'yaf'" - "'ⴽ' > 'yak'" - "'ⴿ' > 'yakhh'" - "'ⵀ' > 'yah'" - "'ⵃ' > 'yahh'" - "'ⵄ' > 'yaa'" - "'ⵅ' > 'yakh'" - "'ⵇ' > 'yaq'" - "'ⵉ' > 'yi'" - "'ⵊ' > 'yazh'" - "'ⵋ' > 'ahaggar'" - "'ⵍ' > 'yal'" - "'ⵎ' > 'yam'" - "'ⵏ' > 'yan'" - "'ⵒ' > 'yap'" - "'ⵓ' > 'yu'" - "'ⵔ' > 'yar'" - "'ⵕ' > 'yarr'" - "'ⵖ' > 'yagh'" - "'ⵘ' > 'ayer'" - "'ⵙ' > 'yas'" - "'ⵚ' > 'yass'" - "'ⵛ' > 'yash'" - "'ⵜ' > 'yat'" - "'ⵝ' > 'yath'" - "'ⵞ' > 'yach'" - "'ⵟ' > 'yatt'" - "'ⵠ' > 'yav'" - "'ⵡ' > 'yaw'" - "'ⵢ' > 'yay'" - "'ⵣ' > 'yaz'" - "'ⵤ' > 'tawellemet'" - "'ⵥ' > 'yazz'" - "'ⶀ' > 'loa'" - "'ⶁ' > 'moa'" - "'ⶂ' > 'roa'" - "'ⶃ' > 'soa'" - "'ⶄ' > 'shoa'" - "'ⶅ' > 'boa'" - "'ⶆ' > 'toa'" - "'ⶇ' > 'coa'" - "'ⶈ' > 'noa'" - "'ⶉ' > 'nyoa'" - "'ⶊ' > 'oa'" - "'ⶋ' > 'zoa'" - "'ⶌ' > 'doa'" - "'ⶍ' > 'ddoa'" - "'ⶎ' > 'joa'" - "'ⶏ' > 'thoa'" - "'ⶐ' > 'choa'" - "'ⶑ' > 'phoa'" - "'ⶒ' > 'poa'" - "'ⶓ' > 'ggwa'" - "'ⶔ' > 'ggwi'" - "'ⶕ' > 'ggwee'" - "'ⶖ' > 'ggwe'" - "'ⶠ' > 'ssa'" - "'ⶡ' > 'ssu'" - "'ⶢ' > 'ssi'" - "'ⶣ' > 'ssaa'" - "'ⶤ' > 'ssee'" - "'ⶥ' > 'sse'" - "'ⶦ' > 'sso'" - "'ⶨ' > 'cca'" - "'ⶩ' > 'ccu'" - "'ⶪ' > 'cci'" - "'ⶫ' > 'ccaa'" - "'ⶬ' > 'ccee'" - "'ⶭ' > 'cce'" - "'ⶮ' > 'cco'" - "'ⶰ' > 'zza'" - "'ⶱ' > 'zzu'" - "'ⶲ' > 'zzi'" - "'ⶳ' > 'zzaa'" - "'ⶴ' > 'zzee'" - "'ⶵ' > 'zze'" - "'ⶶ' > 'zzo'" - "'ⶸ' > 'ccha'" - "'ⶹ' > 'cchu'" - "'ⶺ' > 'cchi'" - "'ⶻ' > 'cchaa'" - "'ⶼ' > 'cchee'" - "'ⶽ' > 'cche'" - "'ⶾ' > 'ccho'" - "'ⷀ' > 'qya'" - "'ⷁ' > 'qyu'" - "'ⷂ' > 'qyi'" - "'ⷃ' > 'qyaa'" - "'ⷄ' > 'qyee'" - "'ⷅ' > 'qye'" - "'ⷆ' > 'qyo'" - "'ⷈ' > 'kya'" - "'ⷉ' > 'kyu'" - "'ⷊ' > 'kyi'" - "'ⷋ' > 'kyaa'" - "'ⷌ' > 'kyee'" - "'ⷍ' > 'kye'" - "'ⷎ' > 'kyo'" - "'ⷐ' > 'xya'" - "'ⷑ' > 'xyu'" - "'ⷒ' > 'xyi'" - "'ⷓ' > 'xyaa'" - "'ⷔ' > 'xyee'" - "'ⷕ' > 'xye'" - "'ⷖ' > 'xyo'" - "'ⷘ' > 'gya'" - "'ⷙ' > 'gyu'" - "'ⷚ' > 'gyi'" - "'ⷛ' > 'gyaa'" - "'ⷜ' > 'gyee'" - "'ⷝ' > 'gye'" - "'ⷞ' > 'gyo'" - "'ゕ' > 'ka'" - "'ゖ' > 'ke'" - "'ㄪ' > 'v'" - "'ㄫ' > 'ng'" - "'ㄬ' > 'gn'" - "'ㄭ' > 'ih'" - "'ㅀ' > 'rieul-hieuh'" - "'ㅄ' > 'pieup-sios'" - "'ㅥ' > 'ssangnieun'" - "'ㅦ' > 'nieun-tikeut'" - "'ㅧ' > 'nieun-sios'" - "'ㅨ' > 'nieun-pansios'" - "'ㅩ' > 'rieul-kiyeok-sios'" - "'ㅪ' > 'rieul-tikeut'" - "'ㅫ' > 'rieul-pieup-sios'" - "'ㅬ' > 'rieul-pansios'" - "'ㅭ' > 'rieul-yeorinhieuh'" - "'ㅮ' > 'mieum-pieup'" - "'ㅯ' > 'mieum-sios'" - "'ㅰ' > 'mieum-pansios'" - "'ㅱ' > 'kapyeounmieum'" - "'ㅲ' > 'pieup-kiyeok'" - "'ㅳ' > 'pieup-tikeut'" - "'ㅴ' > 'pieup-sios-kiyeok'" - "'ㅵ' > 'pieup-sios-tikeut'" - "'ㅶ' > 'pieup-cieuc'" - "'ㅷ' > 'pieup-thieuth'" - "'ㅸ' > 'kapyeounpieup'" - "'ㅹ' > 'kapyeounssangpieup'" - "'ㅺ' > 'sios-kiyeok'" - "'ㅻ' > 'sios-nieun'" - "'ㅼ' > 'sios-tikeut'" - "'ㅽ' > 'sios-pieup'" - "'ㅾ' > 'sios-cieuc'" - "'ㅿ' > 'pansios'" - "'ㆀ' > 'ssangieung'" - "'ㆁ' > 'yesieung'" - "'ㆂ' > 'yesieung-sios'" - "'ㆃ' > 'yesieung-pansios'" - "'ㆄ' > 'kapyeounphieuph'" - "'ㆅ' > 'ssanghieuh'" - "'ㆆ' > 'yeorinhieuh'" - "'ㆇ' > 'yo-ya'" - "'ㆈ' > 'yo-yae'" - "'ㆉ' > 'yo-i'" - "'ㆊ' > 'yu-yeo'" - "'ㆋ' > 'yu-ye'" - "'ㆌ' > 'yu-i'" - "'ㆍ' > 'araea'" - "'ㆎ' > 'araeae'" - "'ㆠ' > 'bu'" - "'ㆡ' > 'zi'" - "'ㆢ' > 'ji'" - "'ㆣ' > 'gu'" - "'ㆤ' > 'ee'" - "'ㆥ' > 'enn'" - "'ㆦ' > 'oo'" - "'ㆧ' > 'onn'" - "'ㆨ' > 'ir'" - "'ㆩ' > 'ann'" - "'ㆪ' > 'inn'" - "'ㆫ' > 'unn'" - "'ㆬ' > 'im'" - "'ㆭ' > 'ngg'" - "'ㆮ' > 'ainn'" - "'ㆯ' > 'aunn'" - "'ㆰ' > 'am'" - "'ㆱ' > 'om'" - "'ㆲ' > 'ong'" - "'ㆳ' > 'innn'" - "'ㆴ' > 'p'" - "'ㆵ' > 't'" - "'ㆶ' > 'k'" - "'ㆷ' > 'h'" - "'ㇰ' > 'ku'" - "'ㇱ' > 'si'" - "'ㇲ' > 'su'" - "'ㇳ' > 'to'" - "'ㇴ' > 'nu'" - "'ㇵ' > 'ha'" - "'ㇶ' > 'hi'" - "'ㇷ' > 'hu'" - "'ㇸ' > 'he'" - "'ㇹ' > 'ho'" - "'ㇺ' > 'mu'" - "'ㇻ' > 'ra'" - "'ㇼ' > 'ri'" - "'ㇽ' > 'ru'" - "'ㇾ' > 're'" - "'ㇿ' > 'ro'" - "'兙' > ' shi'" - "'兡' > ' bai'" - "'嗧' > ' jia'" - "'瓧' > ' seng'" - "'瓰' > ' bo'" - "'瓱' > ' gu'" - "'瓼' > ' feng'" - "'甅' > ' dang'" - "'龦' > ' ze'" - "'龧' > ' qie'" - "'龨' > ' tuo'" - "'龩' > ' luo'" - "'龪' > ' dan'" - "'龫' > ' xiao'" - "'龬' > ' ruo'" - "'龭' > ' jian'" - "'龮' > ' xuan'" - "'龯' > ' bian'" - "'龰' > ' sun'" - "'龱' > ' xiang'" - "'龲' > ' xian'" - "'龳' > ' ping'" - "'龴' > ' zhen'" - "'龵' > ' sheng'" - "'龶' > ' hu'" - "'龷' > ' shi'" - "'龸' > ' zhu'" - "'龹' > ' yue'" - "'龺' > ' chun'" - "'龻' > ' lu'" - "'龼' > ' wu'" - "'龽' > ' dong'" - "'龾' > ' xiao'" - "'龿' > ' ji'" - "'鿀' > ' jie'" - "'鿁' > ' huang'" - "'鿂' > ' xing'" - "'鿄' > ' fan'" - "'鿅' > ' chui'" - "'鿆' > ' zhuan'" - "'鿇' > ' pian'" - "'鿈' > ' feng'" - "'鿉' > ' zhu'" - "'鿊' > ' hong'" - "'鿋' > ' qie'" - "'鿌' > ' hou'" - "'鿑' > ' kui'" - "'鿒' > ' sik'" - "'鿓' > ' lou'" - "'鿖' > ' tang'" - "'鿗' > ' yue'" - "'鿘' > ' chou'" - "'鿙' > ' gao'" - "'鿚' > ' fei'" - "'鿛' > ' ruo'" - "'鿜' > ' zheng'" - "'鿝' > ' gou'" - "'鿞' > ' nie'" - "'鿟' > ' qian'" - "'鿠' > ' xiao'" - "'鿡' > ' cuan'" - "'鿢' > ' gong'" - "'鿣' > ' pang'" - "'鿤' > ' du'" - "'鿥' > ' li'" - "'鿦' > ' bi'" - "'鿧' > ' zhuo'" - "'鿨' > ' chu'" - "'鿩' > ' shai'" - "'鿪' > ' chi'" - "'鿮' > ' lan'" - "'鿯' > ' jian'" - "'ꀀ' > ' ze'" - "'ꀁ' > ' xi'" - "'ꀂ' > ' guo'" - "'ꀃ' > ' yi'" - "'ꀄ' > ' hu'" - "'ꀅ' > ' chan'" - "'ꀆ' > ' kou'" - "'ꀇ' > ' cu'" - "'ꀈ' > ' ping'" - "'ꀉ' > ' chou'" - "'ꀊ' > ' ji'" - "'ꀋ' > ' gui'" - "'ꀌ' > ' su'" - "'ꀍ' > ' lou'" - "'ꀎ' > ' zha'" - "'ꀏ' > ' lu'" - "'ꀐ' > ' nian'" - "'ꀑ' > ' suo'" - "'ꀒ' > ' cuan'" - "'ꀓ' > ' sasara'" - "'ꀔ' > ' suo'" - "'ꀕ' > ' le'" - "'ꀖ' > ' duan'" - "'ꀗ' > ' yana'" - "'ꀘ' > ' xiao'" - "'ꀙ' > ' bo'" - "'ꀚ' > ' mi'" - "'ꀛ' > ' si'" - "'ꀜ' > ' dang'" - "'ꀝ' > ' liao'" - "'ꀞ' > ' dan'" - "'ꀟ' > ' dian'" - "'ꀠ' > ' fu'" - "'ꀡ' > ' jian'" - "'ꀢ' > ' min'" - "'ꀣ' > ' kui'" - "'ꀤ' > ' dai'" - "'ꀥ' > ' qiao'" - "'ꀦ' > ' deng'" - "'ꀧ' > ' huang'" - "'ꀨ' > ' sun'" - "'ꀩ' > ' lao'" - "'ꀪ' > ' zan'" - "'ꀫ' > ' xiao'" - "'ꀬ' > ' du'" - "'ꀭ' > ' shi'" - "'ꀮ' > ' zan'" - "'ꀯ' > 'bup'" - "'ꀰ' > ' pai'" - "'ꀱ' > ' hata'" - "'ꀲ' > ' pai'" - "'ꀳ' > ' gan'" - "'ꀴ' > ' ju'" - "'ꀵ' > ' du'" - "'ꀶ' > ' lu'" - "'ꀷ' > ' yan'" - "'ꀸ' > ' bo'" - "'ꀹ' > ' dang'" - "'ꀺ' > ' sai'" - "'ꀻ' > ' ke'" - "'ꀼ' > ' long'" - "'ꀽ' > ' qian'" - "'ꀾ' > ' lian'" - "'ꀿ' > ' bo'" - "'ꁀ' > ' zhou'" - "'ꁁ' > ' lai'" - "'ꁂ' > 'pap'" - "'ꁃ' > ' lan'" - "'ꁄ' > ' kui'" - "'ꁅ' > ' yu'" - "'ꁆ' > ' yue'" - "'ꁇ' > ' hao'" - "'ꁈ' > ' zhen'" - "'ꁉ' > ' tai'" - "'ꁊ' > ' ti'" - "'ꁋ' > ' mi'" - "'ꁌ' > ' chou'" - "'ꁍ' > ' ji'" - "'ꁎ' > 'purx'" - "'ꁏ' > ' hata'" - "'ꁐ' > ' teng'" - "'ꁑ' > ' zhuan'" - "'ꁒ' > ' zhou'" - "'ꁓ' > ' fan'" - "'ꁔ' > ' sou'" - "'ꁕ' > ' zhou'" - "'ꁖ' > ' kuji'" - "'ꁗ' > ' zhuo'" - "'ꁘ' > ' teng'" - "'ꁙ' > ' lu'" - "'ꁚ' > ' lu'" - "'ꁛ' > ' jian'" - "'ꁜ' > ' tuo'" - "'ꁝ' > ' ying'" - "'ꁞ' > ' yu'" - "'ꁟ' > ' lai'" - "'ꁠ' > ' long'" - "'ꁡ' > ' shinshi'" - "'ꁢ' > ' lian'" - "'ꁣ' > ' lan'" - "'ꁤ' > ' qian'" - "'ꁥ' > ' yue'" - "'ꁦ' > ' zhong'" - "'ꁧ' > ' qu'" - "'ꁨ' > ' lian'" - "'ꁩ' > ' bian'" - "'ꁪ' > ' duan'" - "'ꁫ' > ' zuan'" - "'ꁬ' > ' li'" - "'ꁭ' > ' si'" - "'ꁮ' > ' luo'" - "'ꁯ' > ' ying'" - "'ꁰ' > ' yue'" - "'ꁱ' > ' zhuo'" - "'ꁲ' > ' xu'" - "'ꁳ' > ' mi'" - "'ꁴ' > ' di'" - "'ꁵ' > ' fan'" - "'ꁶ' > ' shen'" - "'ꁷ' > ' zhe'" - "'ꁸ' > ' shen'" - "'ꁹ' > ' nu'" - "'ꁺ' > ' xie'" - "'ꁻ' > ' lei'" - "'ꁼ' > ' xian'" - "'ꁽ' > ' zi'" - "'ꁾ' > ' ni'" - "'ꁿ' > ' cun'" - "'ꂀ' > 'nbap'" - "'ꂁ' > ' qian'" - "'ꂂ' > ' kume'" - "'ꂃ' > ' bi'" - "'ꂄ' > ' ban'" - "'ꂅ' > ' wu'" - "'ꂆ' > ' sha'" - "'ꂇ' > ' kang'" - "'ꂈ' > ' rou'" - "'ꂉ' > ' fen'" - "'ꂊ' > ' bi'" - "'ꂋ' > ' cui'" - "'ꂌ' > 'nbyx'" - "'ꂍ' > ' li'" - "'ꂎ' > ' chi'" - "'ꂏ' > ' nukamiso'" - "'ꂐ' > ' ro'" - "'ꂑ' > ' ba'" - "'ꂒ' > ' li'" - "'ꂓ' > ' gan'" - "'ꂔ' > ' ju'" - "'ꂕ' > ' po'" - "'ꂖ' > ' mo'" - "'ꂗ' > ' cu'" - "'ꂘ' > ' nian'" - "'ꂙ' > ' zhou'" - "'ꂚ' > ' li'" - "'ꂛ' > ' su'" - "'ꂜ' > ' tiao'" - "'ꂝ' > ' li'" - "'ꂞ' > ' qi'" - "'ꂟ' > ' su'" - "'ꂠ' > ' hong'" - "'ꂡ' > ' tong'" - "'ꂢ' > ' zi'" - "'ꂣ' > ' ce'" - "'ꂤ' > ' yue'" - "'ꂥ' > ' zhou'" - "'ꂦ' > ' lin'" - "'ꂧ' > ' zhuang'" - "'ꂨ' > ' bai'" - "'ꂩ' > 'hmyx'" - "'ꂪ' > ' fen'" - "'ꂫ' > ' ji'" - "'ꂬ' > 'hmyrx'" - "'ꂭ' > ' sukumo'" - "'ꂮ' > ' liang'" - "'ꂯ' > ' xian'" - "'ꂰ' > ' fu'" - "'ꂱ' > ' liang'" - "'ꂲ' > ' can'" - "'ꂳ' > ' geng'" - "'ꂴ' > ' li'" - "'ꂵ' > ' yue'" - "'ꂶ' > ' lu'" - "'ꂷ' > ' ju'" - "'ꂸ' > ' qi'" - "'ꂹ' > ' cui'" - "'ꂺ' > ' bai'" - "'ꂻ' > ' zhang'" - "'ꂼ' > ' lin'" - "'ꂽ' > ' zong'" - "'ꂾ' > ' jing'" - "'ꂿ' > ' guo'" - "'ꃀ' > ' kouji'" - "'ꃁ' > ' san'" - "'ꃂ' > ' san'" - "'ꃃ' > ' tang'" - "'ꃄ' > ' bian'" - "'ꃅ' > ' rou'" - "'ꃆ' > ' mian'" - "'ꃇ' > ' hou'" - "'ꃈ' > ' xu'" - "'ꃉ' > ' zong'" - "'ꃊ' > ' hu'" - "'ꃋ' > ' jian'" - "'ꃌ' > ' zan'" - "'ꃍ' > ' ci'" - "'ꃎ' > ' li'" - "'ꃏ' > ' xie'" - "'ꃐ' > ' fu'" - "'ꃑ' > ' ni'" - "'ꃒ' > ' bei'" - "'ꃓ' > ' gu'" - "'ꃔ' > ' xiu'" - "'ꃕ' > ' gao'" - "'ꃖ' > ' tang'" - "'ꃗ' > ' qiu'" - "'ꃘ' > ' sukumo'" - "'ꃙ' > ' cao'" - "'ꃚ' > ' zhuang'" - "'ꃛ' > ' tang'" - "'ꃜ' > ' mi'" - "'ꃝ' > ' san'" - "'ꃞ' > ' fen'" - "'ꃟ' > ' zao'" - "'ꃠ' > ' kang'" - "'ꃡ' > ' jiang'" - "'ꃢ' > ' mo'" - "'ꃣ' > ' san'" - "'ꃤ' > ' san'" - "'ꃥ' > ' nuo'" - "'ꃦ' > ' xi'" - "'ꃧ' > ' liang'" - "'ꃨ' > ' jiang'" - "'ꃩ' > ' kuai'" - "'ꃪ' > ' bo'" - "'ꃫ' > ' huan'" - "'ꃬ' > 'va'" - "'ꃭ' > ' zong'" - "'ꃮ' > ' xian'" - "'ꃯ' > ' nuo'" - "'ꃰ' > ' tuan'" - "'ꃱ' > ' nie'" - "'ꃲ' > ' li'" - "'ꃳ' > ' zuo'" - "'ꃴ' > ' di'" - "'ꃵ' > ' nie'" - "'ꃶ' > ' tiao'" - "'ꃷ' > ' lan'" - "'ꃸ' > ' mi'" - "'ꃹ' > ' jiao'" - "'ꃺ' > ' jiu'" - "'ꃻ' > ' xi'" - "'ꃼ' > ' gong'" - "'ꃽ' > ' zheng'" - "'ꃾ' > ' jiu'" - "'ꃿ' > ' you'" - "'ꄀ' > ' ji'" - "'ꄁ' > ' cha'" - "'ꄂ' > ' zhou'" - "'ꄃ' > ' xun'" - "'ꄄ' > ' yue'" - "'ꄅ' > ' hong'" - "'ꄆ' > ' yu'" - "'ꄇ' > ' he'" - "'ꄈ' > ' wan'" - "'ꄉ' > ' ren'" - "'ꄊ' > ' wen'" - "'ꄋ' > ' wen'" - "'ꄌ' > ' qiu'" - "'ꄍ' > ' na'" - "'ꄎ' > ' zi'" - "'ꄏ' > ' tou'" - "'ꄐ' > ' niu'" - "'ꄑ' > ' fou'" - "'ꄒ' > ' jie'" - "'ꄓ' > ' shu'" - "'ꄔ' > ' chun'" - "'ꄕ' > ' pi'" - "'ꄖ' > ' yin'" - "'ꄗ' > ' sha'" - "'ꄘ' > ' hong'" - "'ꄙ' > ' zhi'" - "'ꄚ' > ' ji'" - "'ꄛ' > ' fen'" - "'ꄜ' > ' yun'" - "'ꄝ' > ' ren'" - "'ꄞ' > ' dan'" - "'ꄟ' > ' jin'" - "'ꄠ' > ' su'" - "'ꄡ' > ' fang'" - "'ꄢ' > ' suo'" - "'ꄣ' > ' cui'" - "'ꄤ' > ' jiu'" - "'ꄥ' > ' zha'" - "'ꄦ' > ' kinu'" - "'ꄧ' > ' jin'" - "'ꄨ' > ' fu'" - "'ꄩ' > ' zhi'" - "'ꄪ' > ' ci'" - "'ꄫ' > ' zi'" - "'ꄬ' > ' chou'" - "'ꄭ' > ' hong'" - "'ꄮ' > ' zha'" - "'ꄯ' > ' lei'" - "'ꄰ' > ' xi'" - "'ꄱ' > ' fu'" - "'ꄲ' > ' xie'" - "'ꄳ' > ' shen'" - "'ꄴ' > ' bei'" - "'ꄵ' > ' zhu'" - "'ꄶ' > ' qu'" - "'ꄷ' > ' ling'" - "'ꄸ' > ' zhu'" - "'ꄹ' > ' shao'" - "'ꄺ' > ' gan'" - "'ꄻ' > ' yang'" - "'ꄼ' > ' fu'" - "'ꄽ' > ' tuo'" - "'ꄾ' > ' zhen'" - "'ꄿ' > ' dai'" - "'ꅀ' > ' zhuo'" - "'ꅁ' > ' shi'" - "'ꅂ' > ' zhong'" - "'ꅃ' > ' xian'" - "'ꅄ' > ' zu'" - "'ꅅ' > ' jiong'" - "'ꅆ' > ' ban'" - "'ꅇ' > ' ju'" - "'ꅈ' > ' mo'" - "'ꅉ' > ' shu'" - "'ꅊ' > ' zui'" - "'ꅋ' > ' wata'" - "'ꅌ' > ' jing'" - "'ꅍ' > ' ren'" - "'ꅎ' > ' heng'" - "'ꅏ' > ' xie'" - "'ꅐ' > ' jie'" - "'ꅑ' > ' zhu'" - "'ꅒ' > ' chou'" - "'ꅓ' > ' gua'" - "'ꅔ' > ' bai'" - "'ꅕ' > ' jue'" - "'ꅖ' > ' kuang'" - "'ꅗ' > ' hu'" - "'ꅘ' > ' ci'" - "'ꅙ' > ' geng'" - "'ꅚ' > ' geng'" - "'ꅛ' > ' tao'" - "'ꅜ' > ' xie'" - "'ꅝ' > ' ku'" - "'ꅞ' > ' jiao'" - "'ꅟ' > ' quan'" - "'ꅠ' > ' gai'" - "'ꅡ' > ' luo'" - "'ꅢ' > ' xuan'" - "'ꅣ' > ' bing'" - "'ꅤ' > ' xian'" - "'ꅥ' > ' fu'" - "'ꅦ' > ' gei'" - "'ꅧ' > ' tong'" - "'ꅨ' > ' rong'" - "'ꅩ' > ' tiao'" - "'ꅪ' > ' yin'" - "'ꅫ' > ' lei'" - "'ꅬ' > ' xie'" - "'ꅭ' > ' quan'" - "'ꅮ' > ' xu'" - "'ꅯ' > ' lun'" - "'ꅰ' > ' die'" - "'ꅱ' > ' tong'" - "'ꅲ' > ' si'" - "'ꅳ' > ' jiang'" - "'ꅴ' > ' xiang'" - "'ꅵ' > ' hui'" - "'ꅶ' > ' jue'" - "'ꅷ' > ' zhi'" - "'ꅸ' > ' jian'" - "'ꅹ' > ' juan'" - "'ꅺ' > ' chi'" - "'ꅻ' > ' mian'" - "'ꅼ' > ' zhen'" - "'ꅽ' > ' lu'" - "'ꅾ' > ' cheng'" - "'ꅿ' > ' qiu'" - "'ꆀ' > ' shu'" - "'ꆁ' > ' bang'" - "'ꆂ' > ' tong'" - "'ꆃ' > ' xiao'" - "'ꆄ' > ' wan'" - "'ꆅ' > ' qin'" - "'ꆆ' > ' geng'" - "'ꆇ' > ' xiu'" - "'ꆈ' > ' ti'" - "'ꆉ' > ' xiu'" - "'ꆊ' > ' xie'" - "'ꆋ' > ' hong'" - "'ꆌ' > ' xi'" - "'ꆍ' > ' fu'" - "'ꆎ' > ' ting'" - "'ꆏ' > ' sui'" - "'ꆐ' > ' dui'" - "'ꆑ' > ' kun'" - "'ꆒ' > ' fu'" - "'ꆓ' > ' jing'" - "'ꆔ' > ' hu'" - "'ꆕ' > ' zhi'" - "'ꆖ' > ' yan'" - "'ꆗ' > ' jiong'" - "'ꆘ' > ' feng'" - "'ꆙ' > ' ji'" - "'ꆚ' > ' sok'" - "'ꆛ' > ' kase'" - "'ꆜ' > ' zong'" - "'ꆝ' > ' lin'" - "'ꆞ' > ' duo'" - "'ꆟ' > ' li'" - "'ꆠ' > ' lu'" - "'ꆡ' > ' liang'" - "'ꆢ' > ' chou'" - "'ꆣ' > ' quan'" - "'ꆤ' > ' shao'" - "'ꆥ' > ' qi'" - "'ꆦ' > ' qi'" - "'ꆧ' > ' zhun'" - "'ꆨ' > ' qi'" - "'ꆩ' > ' wan'" - "'ꆪ' > ' qian'" - "'ꆫ' > ' xian'" - "'ꆬ' > ' shou'" - "'ꆭ' > ' wei'" - "'ꆮ' > ' qi'" - "'ꆯ' > ' tao'" - "'ꆰ' > ' wan'" - "'ꆱ' > ' gang'" - "'ꆲ' > ' wang'" - "'ꆳ' > ' beng'" - "'ꆴ' > ' zhui'" - "'ꆵ' > ' cai'" - "'ꆶ' > ' guo'" - "'ꆷ' > ' cui'" - "'ꆸ' > ' lun'" - "'ꆹ' > ' liu'" - "'ꆺ' > ' qi'" - "'ꆻ' > ' zhan'" - "'ꆼ' > ' bei'" - "'ꆽ' > ' chuo'" - "'ꆾ' > ' ling'" - "'ꆿ' > ' mian'" - "'ꇀ' > ' qi'" - "'ꇁ' > ' qie'" - "'ꇂ' > ' tan'" - "'ꇃ' > ' zong'" - "'ꇄ' > ' gun'" - "'ꇅ' > ' zou'" - "'ꇆ' > ' yi'" - "'ꇇ' > ' zi'" - "'ꇈ' > ' xing'" - "'ꇉ' > ' liang'" - "'ꇊ' > ' jin'" - "'ꇋ' > ' fei'" - "'ꇌ' > ' rui'" - "'ꇍ' > ' min'" - "'ꇎ' > ' yu'" - "'ꇏ' > ' zong'" - "'ꇐ' > ' fan'" - "'ꇑ' > ' lu'" - "'ꇒ' > ' xu'" - "'ꇓ' > ' yingl'" - "'ꇔ' > ' zhang'" - "'ꇕ' > ' kasuri'" - "'ꇖ' > ' xu'" - "'ꇗ' > ' xiang'" - "'ꇘ' > ' jian'" - "'ꇙ' > ' ke'" - "'ꇚ' > ' xian'" - "'ꇛ' > ' ruan'" - "'ꇜ' > ' mian'" - "'ꇝ' > ' qi'" - "'ꇞ' > ' duan'" - "'ꇟ' > ' zhong'" - "'ꇠ' > ' di'" - "'ꇡ' > ' min'" - "'ꇢ' > ' miao'" - "'ꇣ' > ' yuan'" - "'ꇤ' > ' xie'" - "'ꇥ' > ' bao'" - "'ꇦ' > ' si'" - "'ꇧ' > ' qiu'" - "'ꇨ' > ' bian'" - "'ꇩ' > ' huan'" - "'ꇪ' > ' geng'" - "'ꇫ' > ' cong'" - "'ꇬ' > ' mian'" - "'ꇭ' > ' wei'" - "'ꇮ' > ' fu'" - "'ꇯ' > ' wei'" - "'ꇰ' > ' yu'" - "'ꇱ' > ' gou'" - "'ꇲ' > ' miao'" - "'ꇳ' > ' xie'" - "'ꇴ' > ' lian'" - "'ꇵ' > ' zong'" - "'ꇶ' > ' bian'" - "'ꇷ' > ' yun'" - "'ꇸ' > ' yin'" - "'ꇹ' > ' ti'" - "'ꇺ' > ' gua'" - "'ꇻ' > ' zhi'" - "'ꇼ' > ' yun'" - "'ꇽ' > ' cheng'" - "'ꇾ' > ' chan'" - "'ꇿ' > ' dai'" - "'ꈀ' > ' xia'" - "'ꈁ' > ' yuan'" - "'ꈂ' > ' zong'" - "'ꈃ' > ' xu'" - "'ꈄ' > ' nawa'" - "'ꈅ' > ' odoshi'" - "'ꈆ' > ' geng'" - "'ꈇ' > ' sen'" - "'ꈈ' > ' ying'" - "'ꈉ' > ' jin'" - "'ꈊ' > ' yi'" - "'ꈋ' > ' zhui'" - "'ꈌ' > ' ni'" - "'ꈍ' > ' bang'" - "'ꈎ' > ' gu'" - "'ꈏ' > ' pan'" - "'ꈐ' > ' zhou'" - "'ꈑ' > ' jian'" - "'ꈒ' > ' cuo'" - "'ꈓ' > ' quan'" - "'ꈔ' > ' shuang'" - "'ꈕ' > ' yun'" - "'ꈖ' > ' xia'" - "'ꈗ' > ' shuai'" - "'ꈘ' > ' xi'" - "'ꈙ' > ' rong'" - "'ꈚ' > ' tao'" - "'ꈛ' > ' fu'" - "'ꈜ' > ' yun'" - "'ꈝ' > ' zhen'" - "'ꈞ' > ' gao'" - "'ꈟ' > ' ru'" - "'ꈠ' > ' hu'" - "'ꈡ' > ' zai'" - "'ꈢ' > ' teng'" - "'ꈣ' > ' xian'" - "'ꈤ' > ' su'" - "'ꈥ' > ' zhen'" - "'ꈦ' > ' zong'" - "'ꈧ' > ' tao'" - "'ꈨ' > ' horo'" - "'ꈩ' > ' cai'" - "'ꈪ' > ' bi'" - "'ꈫ' > ' feng'" - "'ꈬ' > ' cu'" - "'ꈭ' > ' li'" - "'ꈮ' > ' suo'" - "'ꈯ' > ' yin'" - "'ꈰ' > ' xi'" - "'ꈱ' > ' zong'" - "'ꈲ' > ' lei'" - "'ꈳ' > ' zhuan'" - "'ꈴ' > ' qian'" - "'ꈵ' > ' man'" - "'ꈶ' > ' zhi'" - "'ꈷ' > ' lu'" - "'ꈸ' > ' mo'" - "'ꈹ' > ' piao'" - "'ꈺ' > ' lian'" - "'ꈻ' > ' mi'" - "'ꈼ' > ' xuan'" - "'ꈽ' > ' zong'" - "'ꈾ' > ' ji'" - "'ꈿ' > ' shan'" - "'ꉀ' > ' sui'" - "'ꉁ' > ' fan'" - "'ꉂ' > ' shuai'" - "'ꉃ' > ' beng'" - "'ꉄ' > ' yi'" - "'ꉅ' > ' sao'" - "'ꉆ' > ' mou'" - "'ꉇ' > ' zhou'" - "'ꉈ' > ' qiang'" - "'ꉉ' > ' hun'" - "'ꉊ' > ' sem'" - "'ꉋ' > ' xi'" - "'ꉌ' > ' jung'" - "'ꉍ' > ' xiu'" - "'ꉎ' > ' ran'" - "'ꉏ' > ' xuan'" - "'ꉐ' > ' hui'" - "'ꉑ' > ' qiao'" - "'ꉒ' > ' zeng'" - "'ꉓ' > ' zuo'" - "'ꉔ' > ' zhi'" - "'ꉕ' > ' shan'" - "'ꉖ' > ' san'" - "'ꉗ' > ' lin'" - "'ꉘ' > ' yu'" - "'ꉙ' > ' fan'" - "'ꉚ' > ' liao'" - "'ꉛ' > ' chuo'" - "'ꉜ' > ' zun'" - "'ꉝ' > ' jian'" - "'ꉞ' > ' rao'" - "'ꉟ' > ' chan'" - "'ꉠ' > ' rui'" - "'ꉡ' > ' xiu'" - "'ꉢ' > ' hui'" - "'ꉣ' > ' hua'" - "'ꉤ' > ' zuan'" - "'ꉥ' > ' xi'" - "'ꉦ' > ' qiang'" - "'ꉧ' > ' un'" - "'ꉨ' > ' da'" - "'ꉩ' > ' sheng'" - "'ꉪ' > ' hui'" - "'ꉫ' > ' xi'" - "'ꉬ' > ' se'" - "'ꉭ' > ' jian'" - "'ꉮ' > ' jiang'" - "'ꉯ' > ' huan'" - "'ꉰ' > ' zao'" - "'ꉱ' > ' cong'" - "'ꉲ' > ' jie'" - "'ꉳ' > ' jiao'" - "'ꉴ' > ' bo'" - "'ꉵ' > ' chan'" - "'ꉶ' > ' yi'" - "'ꉷ' > ' nao'" - "'ꉸ' > ' sui'" - "'ꉹ' > ' yi'" - "'ꉺ' > ' shai'" - "'ꉻ' > ' xu'" - "'ꉼ' > ' ji'" - "'ꉽ' > ' bin'" - "'ꉾ' > ' qian'" - "'ꉿ' > ' lan'" - "'ꊀ' > ' pu'" - "'ꊁ' > ' xun'" - "'ꊂ' > ' zuan'" - "'ꊃ' > ' qi'" - "'ꊄ' > ' peng'" - "'ꊅ' > ' li'" - "'ꊆ' > ' mo'" - "'ꊇ' > ' lei'" - "'ꊈ' > ' xie'" - "'ꊉ' > ' zuan'" - "'ꊊ' > ' kuang'" - "'ꊋ' > ' you'" - "'ꊌ' > ' xu'" - "'ꊍ' > ' lei'" - "'ꊎ' > ' xian'" - "'ꊏ' > ' chan'" - "'ꊐ' > ' kou'" - "'ꊑ' > ' lu'" - "'ꊒ' > ' chan'" - "'ꊓ' > ' ying'" - "'ꊔ' > ' cai'" - "'ꊕ' > ' xiang'" - "'ꊖ' > ' xian'" - "'ꊗ' > ' zui'" - "'ꊘ' > ' zuan'" - "'ꊙ' > ' luo'" - "'ꊚ' > ' xi'" - "'ꊛ' > ' dao'" - "'ꊜ' > ' lan'" - "'ꊝ' > ' lei'" - "'ꊞ' > ' lian'" - "'ꊟ' > ' si'" - "'ꊠ' > ' jiu'" - "'ꊡ' > ' yu'" - "'ꊢ' > ' hong'" - "'ꊣ' > ' zhou'" - "'ꊤ' > ' xian'" - "'ꊥ' > ' he'" - "'ꊦ' > ' yue'" - "'ꊧ' > ' ji'" - "'ꊨ' > ' wan'" - "'ꊩ' > ' kuang'" - "'ꊪ' > ' ji'" - "'ꊫ' > ' ren'" - "'ꊬ' > ' wei'" - "'ꊭ' > ' yun'" - "'ꊮ' > ' hong'" - "'ꊯ' > ' chun'" - "'ꊰ' > ' pi'" - "'ꊱ' > ' sha'" - "'ꊲ' > ' gang'" - "'ꊳ' > ' na'" - "'ꊴ' > ' ren'" - "'ꊵ' > ' zong'" - "'ꊶ' > ' lun'" - "'ꊷ' > ' fen'" - "'ꊸ' > ' zhi'" - "'ꊹ' > ' wen'" - "'ꊺ' > ' fang'" - "'ꊻ' > ' zhu'" - "'ꊼ' > ' yin'" - "'ꊽ' > ' niu'" - "'ꊾ' > ' shu'" - "'ꊿ' > ' xian'" - "'ꋀ' > ' gan'" - "'ꋁ' > ' xie'" - "'ꋂ' > ' fu'" - "'ꋃ' > ' lian'" - "'ꋄ' > ' zu'" - "'ꋅ' > ' shen'" - "'ꋆ' > ' xi'" - "'ꋇ' > ' zhi'" - "'ꋈ' > ' zhong'" - "'ꋉ' > ' zhou'" - "'ꋊ' > ' ban'" - "'ꋋ' > ' fu'" - "'ꋌ' > ' zhuo'" - "'ꋍ' > ' shao'" - "'ꋎ' > ' yi'" - "'ꋏ' > ' jing'" - "'ꋐ' > ' dai'" - "'ꋑ' > ' bang'" - "'ꋒ' > ' rong'" - "'ꋓ' > ' jie'" - "'ꋔ' > ' ku'" - "'ꋕ' > ' rao'" - "'ꋖ' > ' die'" - "'ꋗ' > ' heng'" - "'ꋘ' > ' hui'" - "'ꋙ' > ' gei'" - "'ꋚ' > ' xuan'" - "'ꋛ' > ' jiang'" - "'ꋜ' > ' luo'" - "'ꋝ' > ' jue'" - "'ꋞ' > ' jiao'" - "'ꋟ' > ' tong'" - "'ꋠ' > ' geng'" - "'ꋡ' > ' xiao'" - "'ꋢ' > ' juan'" - "'ꋣ' > ' xiu'" - "'ꋤ' > ' xi'" - "'ꋥ' > ' sui'" - "'ꋦ' > ' tao'" - "'ꋧ' > ' ji'" - "'ꋨ' > ' ti'" - "'ꋩ' > ' ji'" - "'ꋪ' > ' xu'" - "'ꋫ' > ' ling'" - "'ꋬ' > 'zzyr'" - "'ꋭ' > ' xu'" - "'ꋮ' > ' qi'" - "'ꋯ' > ' fei'" - "'ꋰ' > ' chuo'" - "'ꋱ' > ' zhang'" - "'ꋲ' > ' gun'" - "'ꋳ' > ' sheng'" - "'ꋴ' > ' wei'" - "'ꋵ' > ' mian'" - "'ꋶ' > ' shou'" - "'ꋷ' > ' beng'" - "'ꋸ' > ' chou'" - "'ꋹ' > ' tao'" - "'ꋺ' > ' liu'" - "'ꋻ' > ' quan'" - "'ꋼ' > ' zong'" - "'ꋽ' > ' zhan'" - "'ꋾ' > ' wan'" - "'ꋿ' > ' lu'" - "'ꌀ' > ' zhui'" - "'ꌁ' > ' zi'" - "'ꌂ' > ' ke'" - "'ꌃ' > ' xiang'" - "'ꌄ' > ' jian'" - "'ꌅ' > ' mian'" - "'ꌆ' > ' lan'" - "'ꌇ' > ' ti'" - "'ꌈ' > ' miao'" - "'ꌉ' > ' qi'" - "'ꌊ' > ' yun'" - "'ꌋ' > ' hui'" - "'ꌌ' > ' si'" - "'ꌍ' > ' duo'" - "'ꌎ' > ' duan'" - "'ꌏ' > ' bian'" - "'ꌐ' > ' xian'" - "'ꌑ' > ' gou'" - "'ꌒ' > ' zhui'" - "'ꌓ' > ' huan'" - "'ꌔ' > ' di'" - "'ꌕ' > ' lu'" - "'ꌖ' > ' bian'" - "'ꌗ' > ' min'" - "'ꌘ' > ' yuan'" - "'ꌙ' > ' jin'" - "'ꌚ' > ' fu'" - "'ꌛ' > ' ru'" - "'ꌜ' > ' zhen'" - "'ꌝ' > ' feng'" - "'ꌞ' > ' shuai'" - "'ꌟ' > ' gao'" - "'ꌠ' > ' chan'" - "'ꌡ' > ' li'" - "'ꌢ' > ' yi'" - "'ꌣ' > ' jian'" - "'ꌤ' > ' bin'" - "'ꌥ' > ' piao'" - "'ꌦ' > ' man'" - "'ꌧ' > ' lei'" - "'ꌨ' > ' ying'" - "'ꌩ' > ' suo'" - "'ꌪ' > ' mou'" - "'ꌫ' > ' sao'" - "'ꌬ' > ' xie'" - "'ꌭ' > ' liao'" - "'ꌮ' > ' shan'" - "'ꌯ' > ' zeng'" - "'ꌰ' > ' jiang'" - "'ꌱ' > ' qian'" - "'ꌲ' > ' zao'" - "'ꌳ' > ' huan'" - "'ꌴ' > ' jiao'" - "'ꌵ' > ' zuan'" - "'ꌶ' > ' fou'" - "'ꌷ' > ' xie'" - "'ꌸ' > ' gang'" - "'ꌹ' > ' fou'" - "'ꌺ' > ' que'" - "'ꌻ' > ' fou'" - "'ꌼ' > ' kaakeru'" - "'ꌽ' > ' bo'" - "'ꌾ' > ' ping'" - "'ꌿ' > ' hou'" - "'ꍀ' > 'ssyt'" - "'ꍁ' > ' gang'" - "'ꍂ' > ' ying'" - "'ꍃ' > ' ying'" - "'ꍄ' > ' qing'" - "'ꍅ' > ' xia'" - "'ꍆ' > ' guan'" - "'ꍇ' > ' zun'" - "'ꍈ' > ' tan'" - "'ꍉ' > ' chang'" - "'ꍊ' > ' qi'" - "'ꍋ' > ' weng'" - "'ꍌ' > ' ying'" - "'ꍍ' > ' lei'" - "'ꍎ' > ' tan'" - "'ꍏ' > ' lu'" - "'ꍐ' > ' guan'" - "'ꍑ' > ' wang'" - "'ꍒ' > ' wang'" - "'ꍓ' > ' gang'" - "'ꍔ' > ' wang'" - "'ꍕ' > ' han'" - "'ꍖ' > 'zhux'" - "'ꍗ' > ' luo'" - "'ꍘ' > ' fu'" - "'ꍙ' > ' mi'" - "'ꍚ' > ' fa'" - "'ꍛ' > ' gu'" - "'ꍜ' > ' zhu'" - "'ꍝ' > ' ju'" - "'ꍞ' > ' mao'" - "'ꍟ' > ' gu'" - "'ꍠ' > ' min'" - "'ꍡ' > ' gang'" - "'ꍢ' > ' ba'" - "'ꍣ' > ' gua'" - "'ꍤ' > ' ti'" - "'ꍥ' > ' juan'" - "'ꍦ' > ' fu'" - "'ꍧ' > ' lin'" - "'ꍨ' > ' yan'" - "'ꍩ' > ' zhao'" - "'ꍪ' > ' zui'" - "'ꍫ' > ' gua'" - "'ꍬ' > ' zhuo'" - "'ꍭ' > ' yu'" - "'ꍮ' > ' zhi'" - "'ꍯ' > ' an'" - "'ꍰ' > ' fa'" - "'ꍱ' > ' nan'" - "'ꍲ' > ' shu'" - "'ꍳ' > ' si'" - "'ꍴ' > ' pi'" - "'ꍵ' > ' ma'" - "'ꍶ' > ' liu'" - "'ꍷ' > ' ba'" - "'ꍸ' > ' fa'" - "'ꍹ' > ' li'" - "'ꍺ' > ' chao'" - "'ꍻ' > ' wei'" - "'ꍼ' > ' bi'" - "'ꍽ' > ' ji'" - "'ꍾ' > ' zeng'" - "'ꍿ' > ' tong'" - "'ꎀ' > ' liu'" - "'ꎁ' > ' ji'" - "'ꎂ' > ' juan'" - "'ꎃ' > ' mi'" - "'ꎄ' > ' zhao'" - "'ꎅ' > ' luo'" - "'ꎆ' > ' pi'" - "'ꎇ' > ' ji'" - "'ꎈ' > ' ji'" - "'ꎉ' > ' luan'" - "'ꎊ' > ' yang'" - "'ꎋ' > ' mie'" - "'ꎌ' > ' qiang'" - "'ꎍ' > ' ta'" - "'ꎎ' > ' mei'" - "'ꎏ' > ' yang'" - "'ꎐ' > ' you'" - "'ꎑ' > ' you'" - "'ꎒ' > ' fen'" - "'ꎓ' > ' ba'" - "'ꎔ' > ' gao'" - "'ꎕ' > ' yang'" - "'ꎖ' > ' gu'" - "'ꎗ' > ' qiang'" - "'ꎘ' > ' zang'" - "'ꎙ' > ' gao'" - "'ꎚ' > ' ling'" - "'ꎛ' > ' yi'" - "'ꎜ' > ' zhu'" - "'ꎝ' > ' di'" - "'ꎞ' > ' xiu'" - "'ꎟ' > ' qian'" - "'ꎠ' > ' yi'" - "'ꎡ' > ' xian'" - "'ꎢ' > ' rong'" - "'ꎣ' > ' qun'" - "'ꎤ' > ' qun'" - "'ꎥ' > ' qian'" - "'ꎦ' > ' huan'" - "'ꎧ' > ' zui'" - "'ꎨ' > ' xian'" - "'ꎩ' > ' yi'" - "'ꎪ' > ' yashinau'" - "'ꎫ' > ' qiang'" - "'ꎬ' > ' xian'" - "'ꎭ' > ' yu'" - "'ꎮ' > ' geng'" - "'ꎯ' > ' jie'" - "'ꎰ' > ' tang'" - "'ꎱ' > ' yuan'" - "'ꎲ' > ' xi'" - "'ꎳ' > ' fan'" - "'ꎴ' > ' shan'" - "'ꎵ' > ' fen'" - "'ꎶ' > ' shan'" - "'ꎷ' > ' lian'" - "'ꎸ' > ' lei'" - "'ꎹ' > ' geng'" - "'ꎺ' > ' nou'" - "'ꎻ' > ' qiang'" - "'ꎼ' > ' chan'" - "'ꎽ' > ' yu'" - "'ꎾ' > ' gong'" - "'ꎿ' > ' yi'" - "'ꏀ' > ' chong'" - "'ꏁ' > ' weng'" - "'ꏂ' > ' fen'" - "'ꏃ' > ' hong'" - "'ꏄ' > ' chi'" - "'ꏅ' > ' chi'" - "'ꏆ' > ' cui'" - "'ꏇ' > ' fu'" - "'ꏈ' > ' xia'" - "'ꏉ' > ' pen'" - "'ꏊ' > ' yi'" - "'ꏋ' > ' la'" - "'ꏌ' > ' yi'" - "'ꏍ' > ' pi'" - "'ꏎ' > ' ling'" - "'ꏏ' > ' liu'" - "'ꏐ' > ' zhi'" - "'ꏑ' > ' qu'" - "'ꏒ' > ' xi'" - "'ꏓ' > ' xie'" - "'ꏔ' > ' xiang'" - "'ꏕ' > ' xi'" - "'ꏖ' > ' xi'" - "'ꏗ' > ' qi'" - "'ꏘ' > ' qiao'" - "'ꏙ' > ' hui'" - "'ꏚ' > ' hui'" - "'ꏛ' > ' xiao'" - "'ꏜ' > ' se'" - "'ꏝ' > ' hong'" - "'ꏞ' > ' jiang'" - "'ꏟ' > ' di'" - "'ꏠ' > ' cui'" - "'ꏡ' > ' fei'" - "'ꏢ' > ' tao'" - "'ꏣ' > ' sha'" - "'ꏤ' > ' chi'" - "'ꏥ' > ' zhu'" - "'ꏦ' > ' jian'" - "'ꏧ' > ' xuan'" - "'ꏨ' > ' shi'" - "'ꏩ' > ' pian'" - "'ꏪ' > ' zong'" - "'ꏫ' > ' wan'" - "'ꏬ' > ' hui'" - "'ꏭ' > ' hou'" - "'ꏮ' > ' he'" - "'ꏯ' > ' he'" - "'ꏰ' > ' han'" - "'ꏱ' > ' ao'" - "'ꏲ' > ' piao'" - "'ꏳ' > ' yi'" - "'ꏴ' > ' lian'" - "'ꏵ' > ' qu'" - "'ꏶ' > 'jyt'" - "'ꏷ' > ' lin'" - "'ꏸ' > ' pen'" - "'ꏹ' > ' qiao'" - "'ꏺ' > ' ao'" - "'ꏻ' > ' fan'" - "'ꏼ' > ' yi'" - "'ꏽ' > ' hui'" - "'ꏾ' > ' xuan'" - "'ꏿ' > ' dao'" - "'ꐀ' > ' yao'" - "'ꐁ' > ' lao'" - "'ꐂ' > 'qie'" - "'ꐃ' > ' kao'" - "'ꐄ' > ' mao'" - "'ꐅ' > ' zhe'" - "'ꐆ' > ' qi'" - "'ꐇ' > ' gou'" - "'ꐈ' > ' gou'" - "'ꐉ' > ' gou'" - "'ꐊ' > ' die'" - "'ꐋ' > ' die'" - "'ꐌ' > ' er'" - "'ꐍ' > ' shua'" - "'ꐎ' > ' ruan'" - "'ꐏ' > ' er'" - "'ꐐ' > ' nai'" - "'ꐑ' > ' zhuan'" - "'ꐒ' > ' lei'" - "'ꐓ' > ' ting'" - "'ꐔ' > ' zi'" - "'ꐕ' > ' geng'" - "'ꐖ' > ' chao'" - "'ꐗ' > ' hao'" - "'ꐘ' > ' yun'" - "'ꐙ' > ' pa'" - "'ꐚ' > ' pi'" - "'ꐛ' > ' chi'" - "'ꐜ' > ' si'" - "'ꐝ' > ' chu'" - "'ꐞ' > ' jia'" - "'ꐟ' > ' ju'" - "'ꐠ' > ' he'" - "'ꐡ' > ' chu'" - "'ꐢ' > ' lao'" - "'ꐣ' > ' lun'" - "'ꐤ' > ' ji'" - "'ꐥ' > ' tang'" - "'ꐦ' > ' ou'" - "'ꐧ' > ' lou'" - "'ꐨ' > ' nou'" - "'ꐩ' > ' gou'" - "'ꐪ' > ' pang'" - "'ꐫ' > ' ze'" - "'ꐬ' > ' lou'" - "'ꐭ' > ' ji'" - "'ꐮ' > ' lao'" - "'ꐯ' > ' huo'" - "'ꐰ' > ' you'" - "'ꐱ' > ' mo'" - "'ꐲ' > ' huai'" - "'ꐳ' > ' er'" - "'ꐴ' > ' zhe'" - "'ꐵ' > ' ting'" - "'ꐶ' > ' ye'" - "'ꐷ' > ' da'" - "'ꐸ' > ' song'" - "'ꐹ' > ' qin'" - "'ꐺ' > ' yun'" - "'ꐻ' > ' chi'" - "'ꐼ' > ' dan'" - "'ꐽ' > ' dan'" - "'ꐾ' > ' hong'" - "'ꐿ' > ' geng'" - "'ꑀ' > ' zhi'" - "'ꑁ' > 'njup'" - "'ꑂ' > ' nie'" - "'ꑃ' > ' dan'" - "'ꑄ' > ' zhen'" - "'ꑅ' > ' che'" - "'ꑆ' > ' ling'" - "'ꑇ' > ' zheng'" - "'ꑈ' > ' you'" - "'ꑉ' > ' wa'" - "'ꑊ' > ' liao'" - "'ꑋ' > ' long'" - "'ꑌ' > ' zhi'" - "'ꑍ' > ' ning'" - "'ꑎ' > ' tiao'" - "'ꑏ' > ' er'" - "'ꑐ' > ' ya'" - "'ꑑ' > ' die'" - "'ꑒ' > ' gua'" - "'ꑓ' > 'nyuo'" - "'ꑔ' > ' lian'" - "'ꑕ' > ' hao'" - "'ꑖ' > ' sheng'" - "'ꑗ' > ' lie'" - "'ꑘ' > ' pin'" - "'ꑙ' > ' jing'" - "'ꑚ' > ' ju'" - "'ꑛ' > ' bi'" - "'ꑜ' > ' di'" - "'ꑝ' > ' guo'" - "'ꑞ' > ' wen'" - "'ꑟ' > ' xu'" - "'ꑠ' > ' ping'" - "'ꑡ' > ' cong'" - "'ꑢ' > ' shikato'" - "'ꑣ' > 'xie'" - "'ꑤ' > ' ting'" - "'ꑥ' > ' yu'" - "'ꑦ' > ' cong'" - "'ꑧ' > ' kui'" - "'ꑨ' > ' tsuraneru'" - "'ꑩ' > ' kui'" - "'ꑪ' > ' cong'" - "'ꑫ' > ' lian'" - "'ꑬ' > ' weng'" - "'ꑭ' > ' kui'" - "'ꑮ' > ' lian'" - "'ꑯ' > ' lian'" - "'ꑰ' > ' cong'" - "'ꑱ' > ' ao'" - "'ꑲ' > ' sheng'" - "'ꑳ' > ' song'" - "'ꑴ' > ' ting'" - "'ꑵ' > ' kui'" - "'ꑶ' > ' nie'" - "'ꑷ' > ' zhi'" - "'ꑸ' > ' dan'" - "'ꑹ' > ' ning'" - "'ꑺ' > ' qie'" - "'ꑻ' > ' ji'" - "'ꑼ' > ' ting'" - "'ꑽ' > ' ting'" - "'ꑾ' > ' long'" - "'ꑿ' > ' yu'" - "'ꒀ' > ' yu'" - "'ꒁ' > ' zhao'" - "'ꒂ' > ' si'" - "'ꒃ' > ' su'" - "'ꒄ' > ' yi'" - "'ꒅ' > ' su'" - "'ꒆ' > ' si'" - "'ꒇ' > ' zhao'" - "'ꒈ' > ' zhao'" - "'ꒉ' > ' rou'" - "'ꒊ' > ' yi'" - "'ꒋ' > ' le'" - "'ꒌ' > ' ji'" - "'ꓐ' > ' ku'" - "'ꓑ' > ' zhi'" - "'ꓒ' > ' ni'" - "'ꓓ' > ' ping'" - "'ꓔ' > ' zi'" - "'ꓕ' > ' fu'" - "'ꓖ' > ' pang'" - "'ꓗ' > ' zhen'" - "'ꓘ' > ' xian'" - "'ꓙ' > ' zuo'" - "'ꓚ' > ' pei'" - "'ꓛ' > ' jia'" - "'ꓜ' > ' sheng'" - "'ꓝ' > ' zhi'" - "'ꓞ' > ' bao'" - "'ꓟ' > ' mu'" - "'ꓠ' > ' qu'" - "'ꓡ' > ' hu'" - "'ꓢ' > ' ke'" - "'ꓣ' > ' yi'" - "'ꓤ' > ' yin'" - "'ꓥ' > ' xu'" - "'ꓦ' > ' yang'" - "'ꓧ' > ' long'" - "'ꓨ' > ' dong'" - "'ꓩ' > ' ka'" - "'ꓪ' > ' lu'" - "'ꓫ' > ' jing'" - "'ꓬ' > ' nu'" - "'ꓭ' > ' yan'" - "'ꓮ' > ' pang'" - "'ꓯ' > ' kua'" - "'ꓰ' > ' yi'" - "'ꓱ' > ' guang'" - "'ꓲ' > ' gai'" - "'ꓳ' > ' ge'" - "'ꓴ' > ' dong'" - "'ꓵ' > ' zhi'" - "'ꓶ' > ' xiao'" - "'ꓷ' > ' xiong'" - "'ꓸ' > ' xiong'" - "'ꓹ' > ' er'" - "'ꓺ' > ' e'" - "'ꓻ' > ' xing'" - "'ꓼ' > ' pian'" - "'ꓽ' > ' neng'" - "'ꔀ' > 'ee'" - "'ꔁ' > 'een'" - "'ꔂ' > 'hee'" - "'ꔃ' > 'wee'" - "'ꔄ' > 'ween'" - "'ꔅ' > 'pee'" - "'ꔆ' > 'bhee'" - "'ꔇ' > 'bee'" - "'ꔈ' > 'mbee'" - "'ꔉ' > 'kpee'" - "'ꔊ' > 'mgbee'" - "'ꔋ' > 'gbee'" - "'ꔌ' > 'fee'" - "'ꔍ' > 'vee'" - "'ꔎ' > 'tee'" - "'ꔏ' > 'thee'" - "'ꔐ' > 'dhee'" - "'ꔑ' > 'dhhee'" - "'ꔒ' > 'lee'" - "'ꔓ' > 'ree'" - "'ꔔ' > 'dee'" - "'ꔕ' > 'ndee'" - "'ꔖ' > 'see'" - "'ꔗ' > 'shee'" - "'ꔘ' > 'zee'" - "'ꔙ' > 'zhee'" - "'ꔚ' > 'cee'" - "'ꔛ' > 'jee'" - "'ꔜ' > 'njee'" - "'ꔝ' > 'yee'" - "'ꔞ' > 'kee'" - "'ꔟ' > 'nggee'" - "'ꔠ' > 'gee'" - "'ꔡ' > 'mee'" - "'ꔢ' > 'nee'" - "'ꔣ' > 'nyee'" - "'ꔤ' > 'i'" - "'ꔥ' > 'in'" - "'ꔦ' > 'hi'" - "'ꔧ' > 'hin'" - "'ꔨ' > 'wi'" - "'ꔩ' > 'win'" - "'ꔪ' > 'pi'" - "'ꔫ' > 'bhi'" - "'ꔬ' > 'bi'" - "'ꔭ' > 'mbi'" - "'ꔮ' > 'kpi'" - "'ꔯ' > 'mgbi'" - "'ꔰ' > 'gbi'" - "'ꔱ' > 'fi'" - "'ꔲ' > 'vi'" - "'ꔳ' > 'ti'" - "'ꔴ' > 'thi'" - "'ꔵ' > 'dhi'" - "'ꔶ' > 'dhhi'" - "'ꔷ' > 'li'" - "'ꔸ' > 'ri'" - "'ꔹ' > 'di'" - "'ꔺ' > 'ndi'" - "'ꔻ' > 'si'" - "'ꔼ' > 'shi'" - "'ꔽ' > 'zi'" - "'ꔾ' > 'zhi'" - "'ꔿ' > 'ci'" - "'ꕀ' > 'ji'" - "'ꕁ' > 'nji'" - "'ꕂ' > 'yi'" - "'ꕃ' > 'ki'" - "'ꕄ' > 'nggi'" - "'ꕅ' > 'gi'" - "'ꕆ' > 'mi'" - "'ꕇ' > 'ni'" - "'ꕈ' > 'nyi'" - "'ꕉ' > 'a'" - "'ꕊ' > 'an'" - "'ꕋ' > 'ngan'" - "'ꕌ' > 'ha'" - "'ꕍ' > 'han'" - "'ꕎ' > 'wa'" - "'ꕏ' > 'wan'" - "'ꕐ' > 'pa'" - "'ꕑ' > 'bha'" - "'ꕒ' > 'ba'" - "'ꕓ' > 'mba'" - "'ꕔ' > 'kpa'" - "'ꕕ' > 'kpan'" - "'ꕖ' > 'mgba'" - "'ꕗ' > 'gba'" - "'ꕘ' > 'fa'" - "'ꕙ' > 'va'" - "'ꕚ' > 'ta'" - "'ꕛ' > 'tha'" - "'ꕜ' > 'dha'" - "'ꕝ' > 'dhha'" - "'ꕞ' > 'la'" - "'ꕟ' > 'ra'" - "'ꕠ' > 'da'" - "'ꕡ' > 'nda'" - "'ꕢ' > 'sa'" - "'ꕣ' > 'sha'" - "'ꕤ' > 'za'" - "'ꕥ' > 'zha'" - "'ꕦ' > 'ca'" - "'ꕧ' > 'ja'" - "'ꕨ' > 'nja'" - "'ꕩ' > 'ya'" - "'ꕪ' > 'ka'" - "'ꕫ' > 'kan'" - "'ꕬ' > 'ngga'" - "'ꕭ' > 'ga'" - "'ꕮ' > 'ma'" - "'ꕯ' > 'na'" - "'ꕰ' > 'nya'" - "'ꕱ' > 'oo'" - "'ꕲ' > 'oon'" - "'ꕳ' > 'hoo'" - "'ꕴ' > 'woo'" - "'ꕵ' > 'woon'" - "'ꕶ' > 'poo'" - "'ꕷ' > 'bhoo'" - "'ꕸ' > 'boo'" - "'ꕹ' > 'mboo'" - "'ꕺ' > 'kpoo'" - "'ꕻ' > 'mgboo'" - "'ꕼ' > 'gboo'" - "'ꕽ' > 'foo'" - "'ꕾ' > 'voo'" - "'ꕿ' > 'too'" - "'ꖀ' > 'thoo'" - "'ꖁ' > 'dhoo'" - "'ꖂ' > 'dhhoo'" - "'ꖃ' > 'loo'" - "'ꖄ' > 'roo'" - "'ꖅ' > 'doo'" - "'ꖆ' > 'ndoo'" - "'ꖇ' > 'soo'" - "'ꖈ' > 'shoo'" - "'ꖉ' > 'zoo'" - "'ꖊ' > 'zhoo'" - "'ꖋ' > 'coo'" - "'ꖌ' > 'joo'" - "'ꖍ' > 'njoo'" - "'ꖎ' > 'yoo'" - "'ꖏ' > 'koo'" - "'ꖐ' > 'nggoo'" - "'ꖑ' > 'goo'" - "'ꖒ' > 'moo'" - "'ꖓ' > 'noo'" - "'ꖔ' > 'nyoo'" - "'ꖕ' > 'u'" - "'ꖖ' > 'un'" - "'ꖗ' > 'hu'" - "'ꖘ' > 'hun'" - "'ꖙ' > 'wu'" - "'ꖚ' > 'wun'" - "'ꖛ' > 'pu'" - "'ꖜ' > 'bhu'" - "'ꖝ' > 'bu'" - "'ꖞ' > 'mbu'" - "'ꖟ' > 'kpu'" - "'ꖠ' > 'mgbu'" - "'ꖡ' > 'gbu'" - "'ꖢ' > 'fu'" - "'ꖣ' > 'vu'" - "'ꖤ' > 'tu'" - "'ꖥ' > 'thu'" - "'ꖦ' > 'dhu'" - "'ꖧ' > 'dhhu'" - "'ꖨ' > 'lu'" - "'ꖩ' > 'ru'" - "'ꖪ' > 'du'" - "'ꖫ' > 'ndu'" - "'ꖬ' > 'su'" - "'ꖭ' > 'shu'" - "'ꖮ' > 'zu'" - "'ꖯ' > 'zhu'" - "'ꖰ' > 'cu'" - "'ꖱ' > 'ju'" - "'ꖲ' > 'nju'" - "'ꖳ' > 'yu'" - "'ꖴ' > 'ku'" - "'ꖵ' > 'nggu'" - "'ꖶ' > 'gu'" - "'ꖷ' > 'mu'" - "'ꖸ' > 'nu'" - "'ꖹ' > 'nyu'" - "'ꖺ' > 'o'" - "'ꖻ' > 'on'" - "'ꖼ' > 'ngon'" - "'ꖽ' > 'ho'" - "'ꖾ' > 'hon'" - "'ꖿ' > 'wo'" - "'ꗀ' > 'won'" - "'ꗁ' > 'po'" - "'ꗂ' > 'bho'" - "'ꗃ' > 'bo'" - "'ꗄ' > 'mbo'" - "'ꗅ' > 'kpo'" - "'ꗆ' > 'mgbo'" - "'ꗇ' > 'gbo'" - "'ꗈ' > 'gbon'" - "'ꗉ' > 'fo'" - "'ꗊ' > 'vo'" - "'ꗋ' > 'to'" - "'ꗌ' > 'tho'" - "'ꗍ' > 'dho'" - "'ꗎ' > 'dhho'" - "'ꗏ' > 'lo'" - "'ꗐ' > 'ro'" - "'ꗑ' > 'do'" - "'ꗒ' > 'ndo'" - "'ꗓ' > 'so'" - "'ꗔ' > 'sho'" - "'ꗕ' > 'zo'" - "'ꗖ' > 'zho'" - "'ꗗ' > 'co'" - "'ꗘ' > 'jo'" - "'ꗙ' > 'njo'" - "'ꗚ' > 'yo'" - "'ꗛ' > 'ko'" - "'ꗜ' > 'nggo'" - "'ꗝ' > 'go'" - "'ꗞ' > 'mo'" - "'ꗟ' > 'no'" - "'ꗠ' > 'nyo'" - "'ꗡ' > 'e'" - "'ꗢ' > 'en'" - "'ꗣ' > 'ngen'" - "'ꗤ' > 'he'" - "'ꗥ' > 'hen'" - "'ꗦ' > 'we'" - "'ꗧ' > 'wen'" - "'ꗨ' > 'pe'" - "'ꗩ' > 'bhe'" - "'ꗪ' > 'be'" - "'ꗫ' > 'mbe'" - "'ꗬ' > 'kpe'" - "'ꗭ' > 'kpen'" - "'ꗮ' > 'mgbe'" - "'ꗯ' > 'gbe'" - "'ꗰ' > 'gben'" - "'ꗱ' > 'fe'" - "'ꗲ' > 've'" - "'ꗳ' > 'te'" - "'ꗴ' > 'the'" - "'ꗵ' > 'dhe'" - "'ꗶ' > 'dhhe'" - "'ꗷ' > 'le'" - "'ꗸ' > 're'" - "'ꗹ' > 'de'" - "'ꗺ' > 'nde'" - "'ꗻ' > 'se'" - "'ꗼ' > 'she'" - "'ꗽ' > 'ze'" - "'ꗾ' > 'zhe'" - "'ꗿ' > 'ce'" - "'ꘀ' > 'je'" - "'ꘁ' > 'nje'" - "'ꘂ' > 'ye'" - "'ꘃ' > 'ke'" - "'ꘄ' > 'ngge'" - "'ꘅ' > 'nggen'" - "'ꘆ' > 'ge'" - "'ꘇ' > 'gen'" - "'ꘈ' > 'me'" - "'ꘉ' > 'ne'" - "'ꘊ' > 'nye'" - "'ꘋ' > 'ng'" - "'ꘐ' > 'ndole'" - "'ꘑ' > 'ndole'" - "'ꘒ' > 'ndole'" - "'ꘪ' > 'ndole'" - "'ꘫ' > 'ndole'" - "'Ꙁ' > 'zemlya'" - "'ꙁ' > 'zemlya'" - "'Ꙃ' > 'dzelo'" - "'ꙃ' > 'dzelo'" - "'Ꙅ' > 'dze'" - "'ꙅ' > 'dze'" - "'Ꙇ' > 'iota'" - "'ꙇ' > 'iota'" - "'Ꙉ' > 'djerv'" - "'ꙉ' > 'djerv'" - "'Ꙑ' > 'yeru'" - "'ꙑ' > 'yeru'" - "'Ꙕ' > 'yu'" - "'ꙕ' > 'yu'" - "'Ꙟ' > 'yn'" - "'ꙟ' > 'yn'" - "'Ꚁ' > 'dwe'" - "'ꚁ' > 'dwe'" - "'Ꚃ' > 'dzwe'" - "'ꚃ' > 'dzwe'" - "'Ꚅ' > 'zhwe'" - "'ꚅ' > 'zhwe'" - "'Ꚇ' > 'cche'" - "'ꚇ' > 'cche'" - "'Ꚉ' > 'dzze'" - "'ꚉ' > 'dzze'" - "'Ꚋ' > 'te'" - "'ꚋ' > 'te'" - "'Ꚍ' > 'twe'" - "'ꚍ' > 'twe'" - "'Ꚏ' > 'tswe'" - "'ꚏ' > 'tswe'" - "'Ꚑ' > 'tsse'" - "'ꚑ' > 'tsse'" - "'Ꚓ' > 'tche'" - "'ꚓ' > 'tche'" - "'Ꚕ' > 'hwe'" - "'ꚕ' > 'hwe'" - "'Ꚗ' > 'shwe'" - "'ꚗ' > 'shwe'" - "'Ꜧ' > 'heng'" - "'ꜧ' > 'heng'" - "'Ꜩ' > 'tz'" - "'ꜩ' > 'tz'" - "'Ꜫ' > 'tresillo'" - "'ꜫ' > 'tresillo'" - "'Ꜭ' > 'cuatrillo'" - "'ꜭ' > 'cuatrillo'" - "'Ꜯ' > 'cuatrillo'" - "'ꜯ' > 'cuatrillo'" - "'Ꜳ' > 'aa'" - "'ꜳ' > 'aa'" - "'Ꜵ' > 'ao'" - "'ꜵ' > 'ao'" - "'Ꜷ' > 'au'" - "'ꜷ' > 'au'" - "'Ꜹ' > 'av'" - "'ꜹ' > 'av'" - "'Ꜻ' > 'av'" - "'ꜻ' > 'av'" - "'Ꜽ' > 'ay'" - "'ꜽ' > 'ay'" - "'Ꜿ' > 'c'" - "'ꜿ' > 'c'" - "'Ꝁ' > 'k'" - "'ꝁ' > 'k'" - "'Ꝃ' > 'k'" - "'ꝃ' > 'k'" - "'Ꝅ' > 'k'" - "'ꝅ' > 'k'" - "'Ꝉ' > 'l'" - "'ꝉ' > 'l'" - "'Ꝋ' > 'o'" - "'ꝋ' > 'o'" - "'Ꝍ' > 'o'" - "'ꝍ' > 'o'" - "'Ꝏ' > 'oo'" - "'ꝏ' > 'oo'" - "'Ꝑ' > 'p'" - "'ꝑ' > 'p'" - "'Ꝓ' > 'p'" - "'ꝓ' > 'p'" - "'Ꝕ' > 'p'" - "'ꝕ' > 'p'" - "'Ꝗ' > 'q'" - "'ꝗ' > 'q'" - "'Ꝙ' > 'q'" - "'ꝙ' > 'q'" - "'Ꝛ' > 'r'" - "'ꝛ' > 'r'" - "'Ꝝ' > 'rum'" - "'ꝝ' > 'rum'" - "'Ꝟ' > 'v'" - "'ꝟ' > 'v'" - "'Ꝡ' > 'vy'" - "'ꝡ' > 'vy'" - "'Ꝥ' > 'thorn'" - "'ꝥ' > 'thorn'" - "'Ꝧ' > 'thorn'" - "'ꝧ' > 'thorn'" - "'Ꝩ' > 'vend'" - "'ꝩ' > 'vend'" - "'Ꝫ' > 'et'" - "'ꝫ' > 'et'" - "'Ꝭ' > 'is'" - "'ꝭ' > 'is'" - "'Ꝯ' > 'con'" - "'ꝯ' > 'con'" - "'ꝰ' > 'us'" - "'ꝱ' > 'dum'" - "'ꝲ' > 'lum'" - "'ꝳ' > 'mum'" - "'ꝴ' > 'num'" - "'ꝵ' > 'rum'" - "'ꝷ' > 'tum'" - "'ꝸ' > 'um'" - "'Ꞁ' > 'l'" - "'ꞁ' > 'l'" - "'ꟻ' > 'f'" - "'ꟼ' > 'p'" - "'ꟽ' > 'm'" - "'ꟾ' > 'i'" - "'ꟿ' > 'm'" - "'ꠀ' > 'a'" - "'ꠁ' > 'i'" - "'ꠃ' > 'u'" - "'ꠄ' > 'e'" - "'ꠅ' > 'o'" - "'ꠇ' > 'ko'" - "'ꠈ' > 'kho'" - "'ꠉ' > 'go'" - "'ꠊ' > 'gho'" - "'ꠌ' > 'co'" - "'ꠍ' > 'cho'" - "'ꠎ' > 'jo'" - "'ꠏ' > 'jho'" - "'ꠐ' > 'tto'" - "'ꠑ' > 'ttho'" - "'ꠒ' > 'ddo'" - "'ꠓ' > 'ddho'" - "'ꠔ' > 'to'" - "'ꠕ' > 'tho'" - "'ꠖ' > 'do'" - "'ꠗ' > 'dho'" - "'ꠘ' > 'no'" - "'ꠙ' > 'po'" - "'ꠚ' > 'pho'" - "'ꠛ' > 'bo'" - "'ꠜ' > 'bho'" - "'ꠝ' > 'mo'" - "'ꠞ' > 'ro'" - "'ꠟ' > 'lo'" - "'ꠠ' > 'rro'" - "'ꠡ' > 'so'" - "'ꠢ' > 'ho'" - "'ꡀ' > 'ka'" - "'ꡁ' > 'kha'" - "'ꡂ' > 'ga'" - "'ꡃ' > 'nga'" - "'ꡄ' > 'ca'" - "'ꡅ' > 'cha'" - "'ꡆ' > 'ja'" - "'ꡇ' > 'nya'" - "'ꡈ' > 'ta'" - "'ꡉ' > 'tha'" - "'ꡊ' > 'da'" - "'ꡋ' > 'na'" - "'ꡌ' > 'pa'" - "'ꡍ' > 'pha'" - "'ꡎ' > 'ba'" - "'ꡏ' > 'ma'" - "'ꡐ' > 'tsa'" - "'ꡑ' > 'tsha'" - "'ꡒ' > 'dza'" - "'ꡓ' > 'wa'" - "'ꡔ' > 'zha'" - "'ꡕ' > 'za'" - "'ꡖ' > 'a'" - "'ꡗ' > 'ya'" - "'ꡘ' > 'ra'" - "'ꡙ' > 'la'" - "'ꡚ' > 'sha'" - "'ꡛ' > 'sa'" - "'ꡜ' > 'ha'" - "'ꡝ' > 'a'" - "'ꡞ' > 'i'" - "'ꡟ' > 'u'" - "'ꡠ' > 'e'" - "'ꡡ' > 'o'" - "'ꡢ' > 'qa'" - "'ꡣ' > 'xa'" - "'ꡤ' > 'fa'" - "'ꡥ' > 'gga'" - "'ꡦ' > 'ee'" - "'ꡧ' > 'wa'" - "'ꡨ' > 'ya'" - "'ꡩ' > 'tta'" - "'ꡪ' > 'ttha'" - "'ꡫ' > 'dda'" - "'ꡬ' > 'nna'" - "'ꡱ' > 'ra'" - "'ꡲ' > 'ra'" - "'ꡳ' > 'candrabindu'" - "'ꢂ' > 'a'" - "'ꢃ' > 'aa'" - "'ꢄ' > 'i'" - "'ꢅ' > 'ii'" - "'ꢆ' > 'u'" - "'ꢇ' > 'uu'" - "'ꢈ' > 'r'" - "'ꢉ' > 'rr'" - "'ꢊ' > 'l'" - "'ꢋ' > 'll'" - "'ꢌ' > 'e'" - "'ꢍ' > 'ee'" - "'ꢎ' > 'ai'" - "'ꢏ' > 'o'" - "'ꢐ' > 'oo'" - "'ꢑ' > 'au'" - "'ꢒ' > 'ka'" - "'ꢓ' > 'kha'" - "'ꢔ' > 'ga'" - "'ꢕ' > 'gha'" - "'ꢖ' > 'nga'" - "'ꢗ' > 'ca'" - "'ꢘ' > 'cha'" - "'ꢙ' > 'ja'" - "'ꢚ' > 'jha'" - "'ꢛ' > 'nya'" - "'ꢜ' > 'tta'" - "'ꢝ' > 'ttha'" - "'ꢞ' > 'dda'" - "'ꢟ' > 'ddha'" - "'ꢠ' > 'nna'" - "'ꢡ' > 'ta'" - "'ꢢ' > 'tha'" - "'ꢣ' > 'da'" - "'ꢤ' > 'dha'" - "'ꢥ' > 'na'" - "'ꢦ' > 'pa'" - "'ꢧ' > 'pha'" - "'ꢨ' > 'ba'" - "'ꢩ' > 'bha'" - "'ꢪ' > 'ma'" - "'ꢫ' > 'ya'" - "'ꢬ' > 'ra'" - "'ꢭ' > 'la'" - "'ꢮ' > 'va'" - "'ꢯ' > 'sha'" - "'ꢰ' > 'ssa'" - "'ꢱ' > 'sa'" - "'ꢲ' > 'ha'" - "'ꢳ' > 'lla'" - "'ꤊ' > 'ka'" - "'ꤋ' > 'kha'" - "'ꤌ' > 'ga'" - "'ꤍ' > 'nga'" - "'ꤎ' > 'sa'" - "'ꤏ' > 'sha'" - "'ꤐ' > 'za'" - "'ꤑ' > 'nya'" - "'ꤒ' > 'ta'" - "'ꤓ' > 'hta'" - "'ꤔ' > 'na'" - "'ꤕ' > 'pa'" - "'ꤖ' > 'pha'" - "'ꤗ' > 'ma'" - "'ꤘ' > 'da'" - "'ꤙ' > 'ba'" - "'ꤚ' > 'ra'" - "'ꤛ' > 'ya'" - "'ꤜ' > 'la'" - "'ꤝ' > 'wa'" - "'ꤞ' > 'tha'" - "'ꤟ' > 'ha'" - "'ꤠ' > 'va'" - "'ꤡ' > 'ca'" - "'ꤢ' > 'a'" - "'ꤣ' > 'oe'" - "'ꤤ' > 'i'" - "'ꤥ' > 'oo'" - "'ꤰ' > 'ka'" - "'ꤱ' > 'ga'" - "'ꤲ' > 'nga'" - "'ꤳ' > 'ta'" - "'ꤴ' > 'da'" - "'ꤵ' > 'na'" - "'ꤶ' > 'pa'" - "'ꤷ' > 'ba'" - "'ꤸ' > 'ma'" - "'ꤹ' > 'ca'" - "'ꤺ' > 'ja'" - "'ꤻ' > 'nya'" - "'ꤼ' > 'sa'" - "'ꤽ' > 'ra'" - "'ꤾ' > 'la'" - "'ꤿ' > 'ya'" - "'ꥀ' > 'wa'" - "'ꥁ' > 'ha'" - "'ꥂ' > 'mba'" - "'ꥃ' > 'ngga'" - "'ꥄ' > 'nda'" - "'ꥅ' > 'nyja'" - "'ꥆ' > 'a'" - "'ꨀ' > 'a'" - "'ꨁ' > 'i'" - "'ꨂ' > 'u'" - "'ꨃ' > 'e'" - "'ꨄ' > 'ai'" - "'ꨅ' > 'o'" - "'ꨆ' > 'ka'" - "'ꨇ' > 'kha'" - "'ꨈ' > 'ga'" - "'ꨉ' > 'gha'" - "'ꨊ' > 'ngue'" - "'ꨋ' > 'nga'" - "'ꨌ' > 'cha'" - "'ꨍ' > 'chha'" - "'ꨎ' > 'ja'" - "'ꨏ' > 'jha'" - "'ꨐ' > 'nhue'" - "'ꨑ' > 'nha'" - "'ꨒ' > 'nhja'" - "'ꨓ' > 'ta'" - "'ꨔ' > 'tha'" - "'ꨕ' > 'da'" - "'ꨖ' > 'dha'" - "'ꨗ' > 'nue'" - "'ꨘ' > 'na'" - "'ꨙ' > 'dda'" - "'ꨚ' > 'pa'" - "'ꨛ' > 'ppa'" - "'ꨜ' > 'pha'" - "'ꨝ' > 'ba'" - "'ꨞ' > 'bha'" - "'ꨟ' > 'mue'" - "'ꨠ' > 'ma'" - "'ꨡ' > 'bba'" - "'ꨢ' > 'ya'" - "'ꨣ' > 'ra'" - "'ꨤ' > 'la'" - "'ꨥ' > 'va'" - "'ꨦ' > 'ssa'" - "'ꨧ' > 'sa'" - "'ꨨ' > 'ha'" - "'ힰ' > 'gyeol'" - "'ힱ' > 'gyeolg'" - "'ힲ' > 'gyeolm'" - "'ힳ' > 'gyeolb'" - "'ힴ' > 'gyeols'" - "'ힵ' > 'gyeolt'" - "'ힶ' > 'gyeolp'" - "'ힷ' > 'gyeolh'" - "'ힸ' > 'gyeom'" - "'ힹ' > 'gyeob'" - "'ힺ' > 'gyeobs'" - "'ힻ' > 'gyeos'" - "'ힼ' > 'gyeoss'" - "'ힽ' > 'gyeong'" - "'ힾ' > 'gyeoj'" - "'ힿ' > 'gyeoc'" - "'ퟀ' > 'gyeok'" - "'ퟁ' > 'gyeot'" - "'ퟂ' > 'gyeop'" - "'ퟃ' > 'gyeoh'" - "'ퟄ' > 'gye'" - "'ퟅ' > 'gyeg'" - "'ퟆ' > 'gyegg'" - "'ퟋ' > 'gyed'" - "'ퟌ' > 'gyel'" - "'ퟍ' > 'gyelg'" - "'ퟎ' > 'gyelm'" - "'ퟏ' > 'gyelb'" - "'ퟐ' > 'gyels'" - "'ퟑ' > 'gyelt'" - "'ퟒ' > 'gyelp'" - "'ퟓ' > 'gyelh'" - "'ퟔ' > 'gyem'" - "'ퟕ' > 'gyeb'" - "'ퟖ' > 'gyebs'" - "'ퟗ' > 'gyes'" - "'ퟘ' > 'gyess'" - "'ퟙ' > 'gyeng'" - "'ퟚ' > 'gyej'" - "'ퟛ' > 'gyec'" - "'ퟜ' > 'gyek'" - "'ퟝ' > 'gyet'" - "'ퟞ' > 'gyep'" - "'ퟟ' > 'gyeh'" - "'ퟠ' > 'go'" - "'ퟡ' > 'gog'" - "'ퟢ' > 'gogg'" - "'ퟣ' > 'gogs'" - "'ퟤ' > 'gon'" - "'ퟥ' > 'gonj'" - "'ퟦ' > 'gonh'" - "'ퟧ' > 'god'" - "'ퟨ' > 'gol'" - "'ퟩ' > 'golg'" - "'ퟪ' > 'golm'" - "'ퟫ' > 'golb'" - "'ퟬ' > 'gols'" - "'ퟭ' > 'golt'" - "'ퟮ' > 'golp'" - "'ퟯ' > 'golh'" - "'ퟰ' > 'gom'" - "'ퟱ' > 'gob'" - "'ퟲ' > 'gobs'" - "'ퟳ' > 'gos'" - "'ퟴ' > 'goss'" - "'ퟵ' > 'gong'" - "'ퟶ' > 'goj'" - "'ퟷ' > 'goc'" - "'ퟸ' > 'gok'" - "'ퟹ' > 'got'" - "'ퟺ' > 'gop'" - "'ퟻ' > 'goh'" - "'﨎' > 'geuj'" - "'﨏' > 'geuc'" - "'﨑' > 'geut'" - "'﨓' > 'geuh'" - "'﨔' > 'gyi'" - "'﨟' > 'gyilb'" - "'﨡' > 'gyilt'" - "'﨣' > 'gyilh'" - "'﨤' > 'gyim'" - "'﨧' > 'gyis'" - "'﨨' > 'gyiss'" - "'﨩' > 'gying'" - "'ﬓ' > 'ggyegs'" - "'ﬔ' > 'ggyen'" - "'ﬕ' > 'ggyenj'" - "'ﬖ' > 'ggyenh'" - "'ﬗ' > 'ggyed'" - "'ﹳ' > 'nwih'" - "'ー' > 'de'" - "'゙' > 'dyeobs'" - "'゚' > 'dyeos'" - "'ᅠ' > 'dyeoss'" - "'ᄚ' > 'dyel'" - "'ᄡ' > 'dyels'" ================================================ FILE: settings/icu-rules/unicode-digits-to-decimal.yaml ================================================ - "[𞥐𐒠߀𖭐꤀𖩠𑓐𑑐𑋰𑄶꩐꘠᱀᭐᮰᠐០᥆༠໐꧰႐᪐᪀᧐𑵐꯰᱐𑱐𑜰𑛀𑙐𑇐꧐꣐෦𑁦0𝟶𝟘𝟬𝟎𝟢₀⓿⓪⁰零] > 0" - "[𞥑𐒡߁𖭑꤁𖩡𑓑𑑑𑋱𑄷꩑꘡᱁᭑᮱᠑១᥇༡໑꧱႑᪑᪁᧑𑵑꯱᱑𑱑𑜱𑛁𑙑𑇑꧑꣑෧𑁧1𝟷𝟙𝟭𝟏𝟣₁¹①⑴⒈❶➀➊⓵一] > 1" - "[𞥒𐒢߂𖭒꤂𖩢𑓒𑑒𑋲𑄸꩒꘢᱂᭒᮲᠒២᥈༢໒꧲႒᪒᪂᧒𑵒꯲᱒𑱒𑜲𑛂𑙒𑇒꧒꣒෨𑁨2𝟸𝟚𝟮𝟐𝟤₂²②⑵⒉❷➁➋⓶二] > 2" - "[𞥓𐒣߃𖭓꤃𖩣𑓓𑑓𑋳𑄹꩓꘣᱃᭓᮳᠓៣᥉༣໓꧳႓᪓᪃᧓𑵓꯳᱓𑱓𑜳𑛃𑙓𑇓꧓꣓෩𑁩3𝟹𝟛𝟯𝟑𝟥₃³③⑶⒊❸➂➌⓷三] > 3" - "[𞥔𐒤߄𖭔꤄𖩤𑓔𑑔𑋴𑄺꩔꘤᱄᭔᮴᠔៤᥊༤໔꧴႔᪔᪄᧔𑵔꯴᱔𑱔𑜴𑛄𑙔𑇔꧔꣔෪𑁪4𝟺𝟜𝟰𝟒𝟦₄⁴④⑷⒋❹➃➍⓸四] > 4" - "[𞥕𐒥߅𖭕꤅𖩥𑓕𑑕𑋵𑄻꩕꘥᱅᭕᮵᠕៥᥋༥໕꧵႕᪕᪅᧕𑵕꯵᱕𑱕𑜵𑛅𑙕𑇕꧕꣕෫𑁫5𝟻𝟝𝟱𝟓𝟧₅⁵⑤⑸⒌❺➄➎⓹五] > 5" - "[𞥖𐒦߆𖭖꤆𖩦𑓖𑑖𑋶𑄼꩖꘦᱆᭖᮶᠖៦᥌༦໖꧶႖᪖᪆᧖𑵖꯶᱖𑱖𑜶𑛆𑙖𑇖꧖꣖෬𑁬6𝟼𝟞𝟲𝟔𝟨₆⁶⑥⑹⒍❻➅➏⓺六] > 6" - "[𞥗𐒧߇𖭗꤇𖩧𑓗𑑗𑋷𑄽꩗꘧᱇᭗᮷᠗៧᥍༧໗꧷႗᪗᪇᧗𑵗꯷᱗𑱗𑜷𑛇𑙗𑇗꧗꣗෭𑁭7𝟽𝟟𝟳𝟕𝟩₇⁷⑦⑺⒎❼➆➐⓻七] > 7" - "[𞥘𐒨߈𖭘꤈𖩨𑓘𑑘𑋸𑄾꩘꘨᱈᭘᮸᠘៨᥎༨໘꧸႘᪘᪈᧘𑵘꯸᱘𑱘𑜸𑛈𑙘𑇘꧘꣘෮𑁮8𝟾𝟠𝟴𝟖𝟪₈⁸⑧⑻⒏❽➇➑⓼八] > 8" - "[𞥙𐒩߉𖭙꤉𖩩𑓙𑑙𑋹𑄿꩙꘩᱉᭙᮹᠙៩᥏༩໙꧹႙᪙᪉᧙𑵙꯹᱙𑱙𑜹𑛉𑙙𑇙꧙꣙෯𑁯9𝟿𝟡𝟵𝟗𝟫₉⁹⑨⑼⒐❾➈➒⓽九] > 9" - "[𑜺⑩⑽⒑❿➉➓⓾十] > '10'" - "[⑪⑾⒒⓫] > '11'" - "[⑫⑿⒓⓬] > '12'" - "[⑬⒀⒔⓭] > '13'" - "[⑭⒁⒕⓮] > '14'" - "[⑮⒂⒖⓯] > '15'" - "[⑯⒃⒗⓰] > '16'" - "[⑰⒄⒘⓱] > '17'" - "[⑱⒅⒙⓲] > '18'" - "[⑲⒆⒚⓳] > '19'" - "[𑜻⑳⒇⒛⓴] > '20'" - "⅐ > ' 1/7'" - "⅑ > ' 1/9'" - "⅒ > ' 1/10'" ================================================ FILE: settings/icu-rules/variants-bg.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#.D0.91.D1.8A.D0.BB.D0.B3.D0.B0.D1.80.D1.81.D0.BA.D0.B8_.D0.B5.D0.B7.D0.B8.D0.BA_-_Bulgarian - lang: bg words: - Блок -> бл - Булевард -> бул - Вход -> вх - Генерал -> ген - Град -> гр - Доктор -> д-р - Доцент -> доц - Капитан -> кап - Митрополит -> мит - Площад -> пл - Професор -> проф - Свети -> Св - Улица -> ул - Село -> с - Квартал -> кв - Жилищен Комплекс -> ж к ================================================ FILE: settings/icu-rules/variants-ca.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Catal.C3.A0_-_Catalan - lang: ca words: - aparcament -> aparc - apartament -> apmt - apartat -> apt - àtic -> àt - autopista -> auto - autopista -> autop - autovia -> autov - avinguda -> av - avinguda -> avd - avinguda -> avda - baixada -> bda - baixos -> bxs - barranc -> bnc - barri -> b - barriada -> b - biblioteca -> bibl - bloc -> bl - carrer -> c - carreró -> cró - carretera -> ctra - cantonada -> cant - cementiri -> cem - cinturó -> cint - codi postal -> CP - collegi -> coll - collegi públic -> CP - comissaria -> com - convent -> convt - correus -> corr - districte -> distr - drecera -> drec - dreta -> dta - entrada -> entr - entresòl -> entl - escala -> esc - escola -> esc - escola universitària -> EU - església -> esgl - estació -> est - estacionament -> estac - facultat -> fac - finca -> fca - habitació -> hab - hospital -> hosp - hotel -> H - monestir -> mtir - monument -> mon - mossèn -> Mn - municipal -> mpal - museu -> mus - nacional -> nac - nombre -> nre - número -> núm - número -> n - sense número -> s/n - parada -> par - passadís -> pdís - passatge -> ptge - passeig -> pg - pavelló -> pav - plaça -> pl - plaça -> pça - planta -> pl - població -> pobl - polígon -> pol - polígon industrial -> PI - polígon industrial -> pol ind - porta -> pta - portal -> ptal - principal -> pral - pujada -> pda - punt quilomètric -> PK - rambla -> rbla - ronda -> rda - sagrada -> sgda - sagrat -> sgt - sant -> st - santa -> sta - sobreàtic -> s/àt - travessera -> trav - travessia -> trv - travessia -> trav - urbanització -> urb - sortida -> sort - via -> v ================================================ FILE: settings/icu-rules/variants-cs.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Cesky_-_Czech - lang: cs words: - Ulice -> Ul - Třída -> Tř - Náměstí -> Nám ================================================ FILE: settings/icu-rules/variants-da.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Dansk_-_Danish - lang: da words: - Lille -> Ll - Nordre -> Ndr - Nørre -> Nr - Søndre, Sønder -> Sdr - Store -> St - Gammel,Gamle -> Gl - ~hal => hal - ~hallen => hallen - ~hallerne => hallerne ================================================ FILE: settings/icu-rules/variants-de.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Deutsch_-_German - lang: de words: - am -> a - an der -> a d - Allgemeines Krankenhaus -> AKH - Altstoffsammelzentrum -> ASZ - auf der -> a d - ~bach -> B - Bad -> B - Bahnhof -> Bhf - Bayerisch, Bayerische, Bayerischer, Bayerisches -> Bayer - Berg -> B - ~berg |-> bg - Bezirk -> Bez - ~brücke -> Br - Bundesgymnasium -> BG - Bundespolizeidirektion -> BPD - Bundesrealgymnasium -> BRG - ~burg |-> bg - burgenländische,burgenländischer,burgenländisches -> bgld - Bürgermeister -> Bgm - Chaussee -> Ch - Deutsche, Deutscher, Deutsches -> dt - Deutscher Alpenverein -> DAV - Deutsch -> Dt - ~denkmal -> Dkm - Dorf -> Df - ~dorf |-> df - Doktor -> Dr - ehemalige, ehemaliger, ehemaliges -> ehem - Fabrik -> Fb - Fachhochschule -> FH - Freiwillige Feuerwehr -> FF - Forsthaus -> Fh - ~gasse |-> g - Gasthaus -> Gh - Gasthof -> Ghf - Gemeinde -> Gde - Graben -> Gr - Großer, Große, Großes -> Gr, G - Gymnasium und Realgymnasium -> GRG - Handelsakademie -> HAK - Handelsschule -> HASCH - Haltestelle -> Hst - Hauptbahnhof -> Hbf - Haus -> Hs - Heilige, Heiliger, Heiliges -> Hl - Hintere, Hinterer, Hinteres -> Ht, Hint - Hohe, Hoher, Hohes -> H - ~höhle -> H - Höhere Technische Lehranstalt -> HTL - ~hütte -> Htt - im -> i - in -> i - in der -> i d - Ingenieur -> Ing - Internationale, Internationaler, Internationales -> Int - Jagdhaus -> Jh - Jagdhütte -> Jhtt - Kapelle -> Kap, Kpl - Katastralgemeinde -> KG - Kläranlage -> KA - Kleiner, Kleine, Kleines -> kl - Klein~ -> Kl. - Kleingartenanlage -> KGA - Kleingartenverein -> KGV - Kogel -> Kg - ~kogel |-> kg - Konzentrationslager -> KZ, KL - Krankenhaus -> KH - ~kreuz |-> kz - Landeskrankenhaus -> LKH - Maria -> Ma - Magister -> Mag - Magistratsabteilung -> MA - Markt -> Mkt - Müllverbrennungsanlage -> MVA - Nationalpark -> NP - Naturschutzgebiet -> NSG - Neue Mittelschule -> NMS - Niedere, Niederer, Niederes -> Nd - Niederösterreich -> NÖ - nördliche, nördlicher, nördliches -> nördl - Nummer -> Nr - ob -> o - Oberer, Obere, Oberes -> ob - Ober~ -> Ob - Österreichischer Alpenverein -> ÖAV - Österreichischer Gebirgsverein -> ÖGV - Österreichischer Touristenklub -> ÖTK - östliche, östlicher, östliches -> östl - Pater -> P - Pfad -> P - Platz -> Pl - ~platz$ -> pl - Professor -> Prof - Quelle -> Q, Qu - Reservoir -> Res - Rhein -> Rh - Rundwanderweg -> RWW - Ruine -> R - Sandgrube, Schottergrube -> SG - Sankt -> St - Schloss -> Schl - See -> S - ~siedlung -> sdlg - Sozialmedizinisches Zentrum -> SMZ - ~Spitze -> Sp - Steinbruch -> Stb - ~stiege -> stg - ~strasse -> str - südliche, südlicher, südliches -> südl - Unterer, Untere, Unteres -> u, unt - Unter~ -> U - Teich -> T - Technische Universität -> TU - Truppenübungsplatz -> TÜPL, TÜPl - Unfallkrankenhaus -> UKH - ~universität -> uni - verfallen -> verf - von -> v - Vordere, Vorderer, Vorderes -> Vd, Vord - Vorder… -> Vd, Vord - von der -> v d - vor der -> v d - Volksschule -> VS - Wald -> W - Wasserfall -> Wsf, Wssf - ~weg$ -> w - westliche, westlicher, westliches -> westl - Wiener -> Wr - ~wiese$ -> ws - Wirtschaftsuniversität -> WU - Wirtshaus -> Wh - zum -> z ================================================ FILE: settings/icu-rules/variants-el.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#.CE.95.CE.BB.CE.BB.CE.B7.CE.BD.CE.B9.CE.BA.CE.AC_-_Greek - lang: el words: - Αγίας -> Αγ - Αγίου -> Αγ - Αγίων -> Αγ - Αδελφοί -> Αφοί - Αδελφών -> Αφών - Αλέξανδρου -> Αλ - Ανώτατο Τεχνολογικό Εκπαιδευτικό Ίδρυμα -> ΑΤΕΙ - Αστυνομικό Τμήμα -> ΑΤ - Βασιλέως -> Β - Βασιλέως -> Βασ - Βασιλίσσης -> Β - Βασιλίσσης -> Βασ - Γρηγορίου -> Γρ - Δήμος -> Δ - Δημοτικό Σχολείο -> ΔΣ - Δημοτικό Σχολείο -> Δημ Σχ - Εθνάρχου -> Εθν - Εθνική -> Εθν - Εθνικής -> Εθν - Ελευθέριος -> Ελ - Ελευθερίου -> Ελ - Ελληνικά Ταχυδρομεία -> ΕΛΤΑ - Θεσσαλονίκης -> Θεσ/νίκης - Ιερά Μονή -> Ι Μ - Ιερός Ναός -> Ι Ν - Κτίριο -> Κτ - Κωνσταντίνου -> Κων/νου - Λεωφόρος -> Λ - Λεωφόρος -> Λεωφ - Λίμνη -> Λ - Νέα -> Ν - Νέες -> Ν - Νέο -> Ν - Νέοι -> Ν - Νέος -> Ν - Νησί -> Ν - Νομός -> Ν - Όρος -> Όρ - Παλαιά -> Π - Παλαιές -> Π - Παλαιό -> Π - Παλαιοί -> Π - Παλαιός -> Π - Πανεπιστήμιο -> ΑΕΙ - Πανεπιστήμιο -> Παν - Πλατεία -> Πλ - Ποταμός -> Π - Ποταμός -> Ποτ - Στρατηγού -> Στρ - Ταχυδρομείο -> ΕΛΤΑ - Τεχνολογικό Εκπαιδευτικό Ίδρυμα -> ΤΕΙ ================================================ FILE: settings/icu-rules/variants-en.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#English # Source: https://pe.usps.com/text/pub28/28apc_002.htm - lang: en words: - Access -> Accs - Air Force Base -> AFB - Air National Guard Base -> ANGB - Airport -> Aprt - Alley -> Al,All,Ally,Aly - Alleyway -> Alwy - Amble -> Ambl - Anex -> Anx - Apartments -> Apts - Approach -> Apch,App - Arcade -> Arc - Arterial -> Artl - Artery -> Arty - Avenue -> Av,Ave - Back -> Bk - Banan -> Ba - Basin -> Basn,Bsn - Bayou -> Byu - Beach -> Bch - Bend -> Bnd - Block -> Blk - Bluff -> Blf - Bluffs -> Blfs - Boardwalk -> Bwlk - Bottom -> Btm - Boulevard -> Blvd,Bvd - Boundary -> Bdy - Bowl -> Bl - Brace -> Br - Brae -> Br - Branch -> Br - Break -> Brk - Bridge$ -> Bdge,Br,Brdg,Brg,Bri - Broadway -> Bdwy,Bway,Bwy - Brook -> Brk - Brooks -> Brks - Brow -> Brw - Buildings -> Bldgs,Bldngs - Business -> Bus - Burg -> Bg - Burgs -> Bgs - Bypass -> Bps,Byp,Bypa - Byway -> Bywy - Camp -> Cp - Canyon -> Cyn - Cape -> Cpe - Caravan -> Cvn - Causeway -> Caus,Cswy,Cway - Center,Centre -> Cen,Ctr - Centers -> Ctrs - Central -> Ctrl - Centreway -> Cnwy - Chase -> Ch - Church -> Ch - Circle -> Cir - Circles -> Cirs - Circuit -> Cct,Ci - Circus -> Crc,Crcs - City -> Cty - Cliff -> Clf - Cliffs -> Clfs - Close -> Cl - Club -> Clb - Common -> Cmn,Comm - Commons -> Cmns - Community -> Comm - Concourse -> Cnc - Concourse -> Con - Copse -> Cps - Corner -> Cor,Cnr,Crn - Corners -> Cors - Corso -> Cso - Cottages -> Cotts - County -> Co - County Road -> CR - County Route -> CR - Course -> Crse - Court -> Crt,Ct - Courts -> Cts - Courtyard -> Cyd - Courtyard -> Ctyd - Cove$ -> Ce,Cov,Cv - Coves -> Cvs - Creek$ -> Ck,Cr,Crk - Crescent -> Cr - Crescent -> Cres - Crest -> Crst,Cst - Croft -> Cft - Cross -> Cs,Crss - Crossing -> Crsg,Csg,Xing - Crossroad -> Crd,Xrd - Crossroads -> Xrds - Crossway -> Cowy - Cul-de-sac -> Cds,Csac - Curve -> Cve,Curv - Cutting -> Cutt - Dale -> Dle - Dam -> Dm - Deviation -> Devn - Distributor -> Dstr - Divide -> Dv - Down -> Dn - Downs -> Dn - Drive -> Dr,Drv,Dv - Drives -> Drs - Drive-In => Drive-In # prevent abbreviation here - Driveway -> Drwy,Dvwy,Dwy - East -> E - Edge -> Edg - Elbow -> Elb - Entrance -> Ent - Esplanade -> Esp - Estate -> Est - Estates -> Ests - Expressway -> Exp,Expy,Expwy,Xway - Extension -> Ex - Extensions -> Exts - Fairway -> Fawy,Fy - Falls -> Fls - Father -> Fr - Ferry -> Fy,Fry - Field -> Fd,Fld - Fields -> Flds - Fire Track -> Ftrk - Firetrail -> Fit - Flat -> Fl,Flt - Flats -> Flts - Follow -> Folw - Footway -> Ftwy - Ford -> Frd - Fords -> Frds - Foreshore -> Fshr - Forest -> Frst - Forest Service Road -> FSR - Forge -> Frg - Forges -> Frgs - Formation -> Form - Fork -> Frk - Forks -> Frks - Fort -> Ft - Freeway -> Frwy,Fwy - Front -> Frnt - Frontage -> Fr,Frtg - Garden -> Gdn - Gardens -> Gdn,Gdns - Gate,Gates -> Ga,Gte - Gateway -> Gwy,Gtwy - George -> Geo - Glade$ -> Gl,Gld,Glde - Glen -> Gln - Glens -> Glns - Grange -> Gra - Green -> Gn,Grn - Greens -> Grns - Ground -> Grnd - Grove$ -> Gr,Gro,Grv - Groves -> Grvs - Grovet -> Gr - Gully -> Gly - Harbor -> Hbr,Harbour - Harbors -> Hbrs - Harbour -> Hbr,Harbor - Haven -> Hvn - Head -> Hd - Heads -> Hd - Heights -> Hgts,Ht,Hts - High School -> HS - Highroad -> Hird,Hrd - Highway -> Hwy - Hill -> Hl - Hills -> Hl,Hls - Hollow -> Holw - Hospital -> Hosp - House -> Ho,Hse - Industrial -> Ind - Inlet -> Inlt - Interchange -> Intg - International -> Intl - Island -> I,Is - Islands -> Iss - Junction -> Jct,Jctn,Jnc - Junctions -> Jcts - Junior -> Jr - Key -> Ky - Keys -> Kys - Knoll -> Knl - Knolls -> Knls - Lagoon -> Lgn - Lake -> Lk - Lakes -> L,Lks - Landing -> Ldg,Lndg - Lane -> La,Ln - Laneway -> Lnwy - Light -> Lgt - Lights -> Lgts - Line -> Ln - Link -> Lk - Little -> Lit,Lt - Loaf -> Lf - Lock -> Lck - Locks -> Lcks - Lodge -> Ldg - Lookout -> Lkt - Loop -> Lp - Lower -> Low,Lr,Lwr - Mall -> Ml - Manor -> Mnr - Manors -> Mnrs - Mansions -> Mans - Market -> Mkt - Meadow -> Mdw - Meadows -> Mdw,Mdws - Mead -> Md - Meander -> Mdr,Mndr,Mr - Medical -> Med - Memorial -> Mem - Mews -> Mw - Middle -> Mid - Middle School -> MS - Mile -> Mi - Military -> Mil - Mill -> Ml - Mills -> Mls - Mission -> Msn - Motorway -> Mtwy,Mwy - Mount -> Mt - Mountain -> Mtn - Mountains$ -> Mtn,Mtns - Municipal -> Mun - Museum -> Mus - National Park -> NP - National Recreation Area -> NRA - National Wildlife Refuge Area -> NWRA - Neck -> Nck - Nook -> Nk - North -> N - Northeast -> NE - Northwest -> NW - Orchard -> Orch - Outlook -> Out,Otlk - Overpass -> Opas - Parade -> Pde - Paradise -> Pdse - Park -> Pk - Parklands -> Pkld - Parkway -> Pkwy,Pky,Pwy - Parkways -> Pkwy - Pass -> Ps - Passage -> Psge - Pathway -> Phwy,Pway,Pwy - Piazza -> Piaz - Pike -> Pk - Pine -> Pne - Pines -> Pnes - Place -> Pl - Plain -> Pl,Pln - Plains -> Pl,Plns - Plateau -> Plat - Plaza -> Pl,Plz,Plza - Pocket -> Pkt - Point -> Pnt,Pt - Points -> Pts - Port -> Prt,Pt - Ports -> Prts - Post Office -> PO - Prairie -> Pr - Precinct -> Pct - Promenade -> Prm,Prom - Quadrangle -> Qdgl - Quadrant -> Qdrt,Qd - Quay -> Qy - Quays -> Qy - Quays -> Qys - Radial -> Radl - Ramble -> Ra - Ramble -> Rmbl - Ranch -> Rnch - Range -> Rge,Rnge - Rapid -> Rpd - Rapids -> Rpds - Reach -> Rch - Reservation -> Res - Reserve -> Res - Reservoir -> Res - Rest -> Rst - Retreat -> Rt,Rtt - Return -> Rtn - Ridge -> Rdg,Rdge - Ridges -> Rdgs - Ridgeway -> Rgwy - Right of Way -> Rowy - Rise -> Ri - ^River -> R,Riv,Rvr - River$ -> R,Riv,Rvr - Riverway -> Rvwy - Riviera -> Rvra - Road -> Rd - Roads -> Rds - Roadside -> Rdsd - Roadway -> Rdwy,Rdy - Rocks -> Rks - Ronde -> Rnde - Rosebowl -> Rsbl - Rotary -> Rty - Round -> Rnd - Route -> Rt,Rte - Saint -> St - Saints -> SS - Senior -> Sr - Serviceway -> Swy,Svwy - Shoal -> Shl - Shore -> Shr - Shores -> Shrs - Shunt -> Shun - Siding -> Sdng - Sister -> Sr - Skyway -> Skwy - Slope -> Slpe - Sound -> Snd - South -> S,Sth - Southeast -> SE - Southwest -> SW - Spring -> Spg - Springs -> Spgs - Spurs -> Spur - Square -> Sq - Squares -> Sqs - Stairway -> Strwy - State Highway -> SH,SHwy - State Route -> SR - Station -> Sta,Stn - Strand -> Sd,Stra - Stravenue -> Stra - Stream -> Strm - Street -> St - Streets -> Sts - Strip -> Strp - Subway -> Sbwy - Summit -> Smt - Tarn -> Tn - Terminal -> Term - Terrace -> Tce,Ter,Terr - Thoroughfare -> Thfr,Thor - Throughway -> Trwy - Tollway -> Tlwy,Twy - Towers -> Twrs - Township -> Twp - Trace -> Trce - Track -> Tr,Trak,Trk - Trafficway -> Trfy - Trail -> Trl - Trailer -> Trlr - Triangle -> Tri - Trunkway -> Tkwy - Tunnel -> Tun,Tunl - Turn -> Tn,Trn - Turnpike -> Tpk,Tpke - Underpass -> Upas,Ups - Union -> Un - Unions -> Uns - University -> Uni,Univ - Upper -> Up - Upper -> Upr - Vale -> Va - Valley -> Vly - Valley -> Vy - Valleys -> Vlys - Viaduct$ -> Vdct,Via,Viad - View -> Vw - Views -> Vws - Village -> Vill,Vlg - Villages -> Vlgs - Villas -> Vlls - Ville -> Vl - Vista -> Vis,Vst,Vsta - Walk -> Wk,Wlk - Walks -> Walk - Walkway -> Wkwy,Wky - Waters -> Wtr - Way -> Wy - Well -> Wl - Wells -> Wls - West -> W - Wharf -> Whrf - William -> Wm - Wynd -> Wyn - Yard -> Yd - lang: en country: ca words: - Circuit -> CIRCT - Concession -> CONC - Corners -> CRNRS - Crossing -> CROSS - Diversion -> DIVERS - Esplanade -> ESPL - Extension -> EXTEN - Grounds -> GRNDS - Harbour -> HARBR - Highlands -> HGHLDS - Landing -> LANDNG - Limits -> LMTS - Lookout -> LKOUT - Orchard -> ORCH - Parkway -> PKY - Passage -> PASS - Pathway -> PTWAY - Private -> PVT - Range -> RG - Subdivision -> SUBDIV - Terrace -> TERR - Townline -> TLINE - Turnabout -> TRNABT - Village -> VILLGE - lang: en country: ph words: - Apartment -> Apt - Barangay -> Brgy - Barangay -> Bgy - Building -> Bldg - Commission -> Comm - Compound -> Cmpd - Compound -> Cpd - Cooperative -> Coop - Department -> Dept - Department -> Dep't - General -> Gen - Governor -> Gov - National -> Nat'l - National High School -> NHS - Philippine -> Phil - Police Community Precinct -> PCP - Province -> Prov - Senior High School -> SHS - Subdivision -> Subd - lang: en country: pk words: - Bazaar -> Bazar - Bazaar -> Bzr - Block -> B - Cantonment -> Cant - Cantonment -> Cantt - Chak -> Ch - Chak -> Ck - Chak -> Chk - Co-operative Housing Society -> CHS - Cooperative Housing Society -> CHS - Commercial -> Com - Commercial -> Comm - Defense Housing Authority -> DHA - Dera Ghazi -> DG - District -> Dist - District -> Distt - District Council -> DC - Extension -> Ext - Federal Government Public School -> FGPS - General Bus Stand -> GBS - General Post Office -> GPO - Government -> Govt - Government Elementary School -> GES - Government Primary School -> GPS - Government Girls Primary School -> GGPS - Government Girls High School -> GGHS - Government High School -> GHS - Gwadar Port Authority -> GPA - Highway -> Hi - Industrial Trading Estate -> ITE - International -> Int - Khasra -> Kh - Khasra -> Kha - Local Government -> LG - Local Government Unit -> LGU - Main Road -> MR - Markaz -> Mkz - Mauza -> Mz - Mauza -> Mza - Metropolitan Corporation -> MC - Mohammad -> Moh - Muhammad -> Moh - Mohammad -> Mohd - Muhammad -> Mohd - Mohammad Ali -> MA - Muhammad Ali -> MA - Mouza -> Mz - Mouza -> Mza - National Highway -> NH - National Highway Authority -> NHA - Ring Road -> RR - Sector -> Sec - Sector -> Sect - Sector -> Sectr - Service -> Ser - Service -> Serv - Taluka Municipal Administration -> TMA - Tehsil Municipal Administration -> TMA - Union Council -> UC ================================================ FILE: settings/icu-rules/variants-es.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Espa.C3.B1ol_-_Spanish - lang: es words: - Acequia -> Aceq - Alameda -> Alam - Alquería -> Alque - Andador -> Andad - Angosta -> Angta - Apartamento -> Apto - Apartamentos -> Aptos - Apeadero -> Apdro - Arboleda -> Arb - Arrabal -> Arral - Arroyo -> Arry - Asociación de Vecinos -> A VV - Asociación Vecinal -> A V - Autopista -> Auto - Autovía -> Autov - Avenida -> Av - Avenida -> Avd - Avenida -> Avda - Balneario -> Balnr - Banda -> B - Banda -> Bda - Barranco -> Branc - Barranquil -> Bqllo - Barriada -> Barda - Barrio -> B.º - Barrio -> Bo - Bloque -> Blq - Bulevar -> Blvr - Boulevard -> Blvd - Calle -> C - Calle -> Cl - Calleja -> Cllja - Callejón -> Callej - Callejón -> Cjón - Callejón -> Cllón - Callejuela -> Cjla - Callizo -> Cllzo - Calzada -> Czada - Camino -> Cno - Camino -> Cmno - Camino hondo -> C H - Camino nuevo -> C N - Camino viejo -> C V - Camping -> Campg - Cantera -> Cantr - Cantina -> Canti - Cantón -> Cant - Carrera -> Cra - Carrero -> Cro - Carretera -> Ctra - Carreterín -> Ctrin - Carretil -> Crtil - Caserío -> Csrio - Centro Integrado de Formación Profesional -> CIFP - Cinturón -> Cint - Circunvalación -> Ccvcn - Cobertizo -> Cbtiz - Colegio de Educación Especial -> CEE - Colegio de Educación Infantil -> CEI - Colegio de Educación Infantil y Primaria -> CEIP - Colegio Rural Agrupado -> CRA - Colonia -> Col - Complejo -> Compj - Conjunto -> Cjto - Convento -> Cnvto - Cooperativa -> Coop - Corralillo -> Crrlo - Corredor -> Crrdo - Cortijo -> Crtjo - Costanilla -> Cstan - Costera -> Coste - Dehesa -> Dhsa - Demarcación -> Demar - Diagonal -> Diag - Diseminado -> Disem - Doctor -> Dr - Doctora -> Dra - Edificio -> Edif - Empresa -> Empr - Entrada -> Entd - Escalera -> Esca - Escalinata -> Escal - Espalda -> Eslda - Estación -> Estcn - Estrada -> Estda - Explanada -> Expla - Extramuros -> Extrm - Extrarradio -> Extrr - Fábrica -> Fca - Fábrica -> Fbrca - Ferrocarril -> F C - Ferrocarriles -> FF CC - Galería -> Gale - Glorieta -> Gta - Gran Vía -> G V - Hipódromo -> Hipód - Instituto de Educación Secundaria -> IES - Jardín -> Jdín - Llanura -> Llnra - Lote -> Lt - Malecón -> Malec - Manzana -> Mz - Mercado -> Merc - Mirador -> Mrdor - Monasterio -> Mtrio - Nuestra Señora -> N.ª S.ª - Nuestra Señora -> Ntr.ª Sr.ª - Nuestra Señora -> Ntra Sra - Palacio -> Palac - Pantano -> Pant - Parque -> Pque - Particular -> Parti - Partida -> Ptda - Pasadizo -> Pzo - Pasaje -> Psje - Paseo -> P.º - Paseo marítimo -> P.º mar - Pasillo -> Psllo - Plaza -> Pl - Plaza -> Pza - Plazoleta -> Pzta - Plazuela -> Plzla - Poblado -> Pbdo - Polígono -> Políg - Polígono industrial -> Pg ind - Pórtico -> Prtco - Portillo -> Ptilo - Prazuela -> Przla - Prolongación -> Prol - Pueblo -> Pblo - Puente -> Pte - Puerta -> Pta - Puerto -> Pto - Punto kilométrico -> P k - Rambla -> Rbla - Residencial -> Resid - Ribera -> Rbra - Rincón -> Rcón - Rinconada -> Rcda - Rotonda -> Rtda - San -> S - Sanatorio -> Sanat - Santa -> Sta - Santo -> Sto - Santas -> Stas - Santos -> Stos - Santuario -> Santu - Sector -> Sect - Sendera -> Sedra - Sendero -> Send - Torrente -> Trrnt - Tránsito -> Tráns - Transversal -> Trval - Trasera -> Tras - Travesía -> Trva - Urbanización -> Urb - Vecindario -> Vecin - Viaducto -> Vcto - Viviendas -> Vvdas ================================================ FILE: settings/icu-rules/variants-et.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Eesti_-_Estonian - lang: et words: - Maantee -> mnt - Puiestee -> pst - Raudtee -> rdt - Raudteejaam -> rdtj - Tänav -> tn ================================================ FILE: settings/icu-rules/variants-eu.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Euskara_-_Basque - lang: eu words: - Etorbidea -> Etorb - Errepidea -> Err - Kalea -> K ================================================ FILE: settings/icu-rules/variants-fi.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Suomi_-_Finnish - lang: fi words: - ~alue -> al - ~asema -> as - ~aukio -> auk - ~kaari -> kri - ~katu -> k - ~kuja -> kj - ~kylä -> kl - ~penger -> pgr - ~polku -> p - ~puistikko -> pko - ~puisto -> ps - ~raitti -> r - ~rautatieasema -> ras - ~ranta -> rt - ~rinne -> rn - ~taival -> tvl - ~tie -> t - tienhaara -> th - ~tori -> tr - ~väylä -> vlä ================================================ FILE: settings/icu-rules/variants-fr.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Fran.C3.A7ais_-_French - lang: fr words: - Abbaye -> ABE - Agglomération -> AGL - Aires -> AIRE - Allée -> ALL - Allées -> ALL - Ancien chemin -> ACH - Ancienne route -> ART - Anciennes routes -> ART - Arcade -> ARC - Arcades -> ARC - Autoroute -> AUT - Avenue -> AV - Barrière -> BRE - Barrières -> BRE - Bas chemin -> BCH - Bastide -> BSTD - Baston -> BAST - Béguinage -> BEGI - Béguinages -> BEGI - Berge -> BER - Berges -> BER - Bois -> BOIS - Boucle -> BCLE - Boulevard -> BD - Bourg -> BRG - Butte -> BUT - Cités -> CITE - Côteau -> COTE - Campagne -> CGNE - Camping -> CPG - Carreau -> CAU - Carrefour -> CAR - Carrière -> CARE - Carrières -> CARE - Carré -> CARR - Castel -> CST - Cavée -> CAV - Central -> CTRE - Centre -> CTRE - Chalet -> CHL - Chapelle -> CHP - Charmille -> CHI - Chaussée -> CHS - Chaussées -> CHS - Chemin -> Ch - Chemin -> CHE - Chemin vicinal -> CHV - Cheminement -> CHEM - Cheminements -> CHEM - Chemins -> CHE - Chemins vicinaux -> CHV - Château -> CHT - Cloître -> CLOI - Colline -> COLI - Collines -> COLI - Contour -> CTR - Corniche -> COR - Corniches -> COR - Cottage -> COTT - Cottages -> COTT - Cours -> CRS - Darse -> DARS - Degré -> DEG - Degrés -> DEG - Descente -> DSG - Descentes -> DSG - Digue -> DIG - Digues -> DIG - Domaine -> DOM - Domaines -> DOM - Écluse -> ECL - Écluses -> ECL - Église -> EGL - Enceinte -> EN - Enclave -> ENV - Enclos -> ENC - Escalier -> ESC - Escaliers -> ESC - Espace -> ESPA - Esplanade -> ESP - Esplanades -> ESP - Faubourg -> FG - Ferme -> FRM - Fermes -> FRM - Fontaine -> FON - Forum -> FORM - Fosse -> FOS - Fosses -> FOS - Foyer -> FOYR - Galerie -> GAL - Galeries -> GAL - Garenne -> GARN - Grand boulevard -> GBD - Grand ensemble -> GDEN - Grand’rue -> GR - Grande rue -> GR - Grandes rues -> GR - Grands ensembles -> GDEN - Grille -> GRI - Grimpette -> GRIM - Groupe -> GPE - Groupement -> GPT - Groupes -> GPE - Halle -> HLE - Halles -> HLE - Hameau -> HAM - Hameaux -> HAM - Haut chemin -> HCH - Hauts chemins -> HCH - Hippodrome -> HIP - Immeuble -> IMM - Immeubles -> IMM - Impasse -> IMP - Impasses -> IMP - Jardin -> JARD - Jardins -> JARD - Jetée -> JTE - Jetées -> JTE - Levée -> LEVE - Lieu-dit -> LD - Lotissement -> LOT - Lotissements -> LOT - Maison forestière -> MF - Manoir -> MAN - Marche -> MAR - Marches -> MAR - Maréchal -> MAL - Monseigneur -> Mgr - Mont -> Mt - Montée -> MTE - Montées -> MTE - Moulin -> MLN - Moulins -> MLN - Musée -> MUS - Métro -> MET - Métro -> MÉT - Nouvelle route -> NTE - Palais -> PAL - Parking -> PKG - Parvis -> PRV - Passage -> PAS - Passage à niveau -> PN - Passe -> PASS - Passerelle -> PLE - Passerelles -> PLE - Passes -> PASS - Patio -> PAT - Pavillon -> PAV - Pavillons -> PAV - Petit chemin -> PCH - Petite allée -> PTA - Petite avenue -> PAE - Petite impasse -> PIM - Petite route -> PRT - Petite rue -> PTR - Petites allées -> PTA - Place -> PL - Placis -> PLCI - Plage -> PLAG - Plages -> PLAG - Plaine -> PLN - Plateau -> PLT - Plateaux -> PLT - Pointe -> PNT - Porche -> PCH - Porte -> PTE - Portique -> PORQ - Portiques -> PORQ - Poterne -> POT - Pourtour -> POUR - Presqu’île -> PRQ - Promenade -> PROM - Périphérique -> PERI - Péristyle -> PSTY - Quai -> QU - Quartier -> QUA - Raccourci -> RAC - Raidillon -> RAID - Rampe -> RPE - Rempart -> REM - Rocade -> ROC - Rond point -> RPT - Roquet -> ROQT - Rotonde -> RTD - Route -> RTE - Routes -> RTE - Rue -> R - Rue -> R - Ruelle -> RLE - Ruelles -> RLE - Rues -> R - Résidence -> RES - Résidences -> RES - Saint -> St - Sainte -> Ste - Sente -> SEN - Sentes -> SEN - Sentier -> SEN - Sentiers -> SEN - Square -> SQ - Stade -> STDE - Station -> STA - Terrain -> TRN - Terrasse -> TSSE - Terrasses -> TSSE - Terre plein -> TPL - Tertre -> TRT - Tertres -> TRT - Traverse -> TRA - Vallon -> VAL - Vallée -> VAL - Venelle -> VEN - Venelles -> VEN - Vieille route -> VTE - Vieux chemin -> VCHE - Villa -> VLA - Village -> VGE - Villages -> VGE - Villas -> VLA - Voie -> VOI - Voies -> VOI - Zone artisanale -> ZA - Zone d'aménagement concerté -> ZAC - Zone d'aménagement différé -> ZAD - Zone industrielle -> ZI - Zone à urbaniser en priorité -> ZUP - lang: fr country: ca words: - Boulevard -> BOUL - Carré -> CAR - Carrefour -> CARREF - Centre -> C - Chemin -> CH - Croissant -> CROIS - Diversion -> DIVERS - Échangeur -> ÉCH - Esplanade -> ESPL - Passage -> PASS - Plateau -> PLAT - Rond-point -> RDPT - Sentier -> SENT - Subdivision -> SUBDIV - Terrasse -> TSSE - Village -> VILLGE ================================================ FILE: settings/icu-rules/variants-gl.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Galego_-_Galician - lang: gl words: - Asociación Veciñal -> A V - Asociación de Veciños -> A VV - Avenida -> Av - Avenida -> Avda - Centro Integrado de Formación Profesional -> CIFP - Colexio de Educación Especial -> CEE - Colexio de Educación Infantil -> CEI - Colexio de Educación Infantil e Primaria -> CEIP - Colexio Rural Agrupado -> CRA - Doutor -> Dr - Doutora -> Dra - Edificio -> Edif - Estrada -> Estda - Ferrocarril -> F C - Ferrocarrís -> FF CC - Instituto de Educación Secundaria -> IES - Rúa -> R/ - San -> S - Santa -> Sta - Santo -> Sto - Santas -> Stas - Santos -> Stos - Señora -> Sra - Urbanización -> Urb ================================================ FILE: settings/icu-rules/variants-hu.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Magyar_-_Hungarian - lang: hu words: - utca -> u ================================================ FILE: settings/icu-rules/variants-it.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Italiano_-_Italian - lang: it words: - Calle -> C.le - Campo -> C.po - Cascina -> C.na - Cinque -> 5 - Corso -> C.so - Corte -> C.te - Decima -> X - Decimo -> X - Due -> 2 - Fondamenta -> F.ta - Largo -> L.go - Località -> Loc - Lungomare -> L.mare - Nona -> IX - Nono -> IX - Nove -> 9 - Otto -> 8 - Ottava -> VIII - Ottavo -> VIII - Piazza -> P.za - Piazza -> P.zza - Piazzale -> P.le - Piazzetta -> P.ta - Ponte -> P.te - Porta -> P.ta - Prima -> I - Primo -> I - Primo -> 1 - Quarta -> IV - Quarto -> IV - Quattro -> IV - Quattro -> 4 - Quinta -> V - Quinto -> V - Salizada -> S.da - San -> S - Santa -> S - Santo -> S - Sant' -> S - Santi -> SS - Santissima -> SS.ma - Santissime -> SS.me - Santissimi -> SS.mi - Santissimo -> SS.mo - Seconda -> II - Secondo -> II - Sei -> 6 - Sesta -> VI - Sesto -> VI - Sette -> 7 - Settima -> VII - Settimo -> VII - Stazione -> Staz - Strada Comunale -> SC - Strada Provinciale -> SP - Strada Regionale -> SR - Strada Statale -> SS - Terzo -> III - Terza -> III - Tre -> 3 - Trenta -> XXX - Un -> 1 - Una -> 1 - Venti -> XX - Venti -> 20 - Venticinque -> XXV - Venticinque -> 25 - Ventiquattro -> XXIV - Ventitreesimo -> XXIII - Via -> V - Viale -> V.le - Vico -> V.co - Vicolo -> V.lo ================================================ FILE: settings/icu-rules/variants-mg.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Malagasy_-_Malgache - lang: mg words: - Ambato -> Ato - Ambinany -> Any - Ambodi -> Adi - Ambohi -> Ahi - Ambohitr' -> Atr' - Ambony -> Ani - Ampasi -> Asi - Andoha -> Aha - Andrano -> Ano ================================================ FILE: settings/icu-rules/variants-ms.yaml ================================================ # Malay language abbreviations for Nominatim # # Primary source for Malaysia: # JUPEM (Jabatan Ukur dan Pemetaan Malaysia) Toponymic Guidelines # "Abbreviations of Geographical Terms and Other Words Used in Maps" # # Secondary sources: # https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Bahasa_Melayu_-_Malay # https://wiki.openstreetmap.org/wiki/Malaysia/Abbreviations - lang: ms words: - Jalan -> Jln - Lorong -> Lrg,Lor - Lebuh -> Lbh - Simpang -> Spg - Kampung -> Kg - Kampong -> Kg - Taman -> Tmn - Bangunan -> Bgn - Bukit -> Bkt - Batu -> Bt - Sungai -> Sg - Pulau -> P,Pl - Tanjung -> Tg - Tanjong -> Tg - Teluk -> Tlk - Telok -> Tlk - Gunung -> G,Gn - Gunong -> G,Gn - Haji -> Hj - Hajjah -> Hjh - Abdul -> Abd - lang: ms country: my words: - Persiaran -> Psrn - Lebuhraya -> Lbh.Raya - Bandar -> Bdr - Kondominium -> Kondo - Bahagian -> Bhg - Blok -> Blk - Kuala -> K,Kl - Alur -> Alr - Bakau -> Bak - Hulu -> H - Hilir -> Hr - Jeram -> J,Jr - Lembah -> Lmbh - Padang -> Pdg - Parit -> Pt - Permatang -> Pmtg - Pelabuhan -> Plbh - Kepulauan -> Kep - Ladang -> Ldg - Lapangan -> Lpg - Gosong -> Gsg - Sekolah kebangsaan -> SK - Sekolah menengah -> SM - Sekolah menengah kebangsaan -> SMK - Sekolah rendah -> SR - Hospital -> Hosp - Dispensari -> Disp - lang: ms country: bn words: - Pengiran -> Pg - Awang -> Awg - Dayang -> Dyg - lang: ms country: sg words: - Bukit -> Bt - Lorong -> Lor ================================================ FILE: settings/icu-rules/variants-nl.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Nederlands_-_Dutch - lang: nl words: - Broeder -> Br - Burgemeester -> Burg - Commandant -> Cmdt - Doctor -> dr - Dokter -> Dr - Dominee -> ds - Gebroeders -> Gebr - Generaal -> Gen - ~gracht -> gr - Ingenieur -> ir - Jonkheer -> Jhr - Kolonel -> Kol - Kanunnik -> Kan - Kardinaal -> Kard - Kort(e) -> Kte, K - Koning -> Kon - Koningin -> Kon - ~laan -> ln - Lange -> L - Luitenant -> Luit - ~markt -> mkt - Meester -> Mr, mr - Mejuffrouw -> Mej - Mevrouw -> Mevr - Minister -> Min - Monseigneur -> Mgr - Noordzijde -> NZ, N Z - Oostzijde -> OZ, O Z - Onze-Lieve-Vrouw,Onze-Lieve-Vrouwe -> O L V, OLV - Pastoor -> Past - ~plein -> pln - President -> Pres - Prins -> Pr - Prinses -> Pr - Professor -> Prof - ~singel -> sngl - ~straat -> str - ~steenweg -> stwg - Sint -> St - Van -> V - Van De -> V D, vd - Van Den -> V D, vd - Van Der -> V D, vd - Verlengde -> Verl - ~vliet -> vlt - Vrouwe -> Vr - ~weg -> wg - Westzijde -> WZ, W Z - Zuidzijde -> ZZ, Z Z - Zuster -> Zr ================================================ FILE: settings/icu-rules/variants-no.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Norsk_-_Norwegian - lang: "no" words: # convert between Nynorsk and Bookmal here - ~vei, ~veg -> v,vei,veg - ~veien, ~vegen -> vn,veien,vegen # convert between the two female forms - gate, gaten, gata -> g,gt - plass, plassen -> pl - sving, svingen -> sv ================================================ FILE: settings/icu-rules/variants-pl.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Polski_.E2.80.93_Polish - lang: pl words: - Aleja, Aleje, Alei, Alejach, Aleją -> al - Ulica, Ulice, Ulicą, Ulicy -> ul - Plac, Placu, Placem -> pl - Wybrzeże, Wybrzeża, Wybrzeżem -> wyb - Bulwar -> bulw - Dolny, Dolna, Dolne -> Dln - Drugi, Druga, Drugie -> 2 - Drugi, Druga, Drugie -> II - Duży, Duża, Duże -> Dz - Duży, Duża, Duże -> Dż - Górny, Górna, Górne -> Grn - Kolonia -> kol - koło, kolo -> k - Mały, Mała, Małe -> Ml - Mały, Mała, Małe -> Mł - Mazowiecka, Mazowiecki, Mazowieckie -> maz - Miasto -> m - Nowy, Nowa, Nowe -> Nw - Nowy, Nowa, Nowe -> N - Osiedle, Osiedlu -> os - Pierwszy, Pierwsza, Pierwsze -> 1 - Pierwszy, Pierwsza, Pierwsze -> I - Szkoła Podstawowa -> SP - Stary, Stara, Stare -> St - Stary, Stara, Stare -> Str - Trzeci, Trzecia, Trzecie -> III - Trzeci, Trzecia, Trzecie -> 3 - Wielki, Wielka, Wielkie -> Wlk - Wielkopolski, Wielkopolska, Wielkopolskie -> wlkp - Województwo, Województwie -> woj - kardynała, kardynał -> kard - pułkownika, pułkownik -> płk - marszałka, marszałek -> marsz - generała, generał -> gen - Świętego, Świętej, Świętych, święty, święta, święci -> św - Świętych, święci -> śś - Ojców -> oo - Błogosławionego, Błogosławionej, Błogosławionych, błogosławiony, błogosławiona, błogosławieni -> bł - księdza, ksiądz -> ks - księcia, książe -> ks - doktora, doktor -> dr - majora, major -> mjr - biskupa, biskup -> bpa - biskupa, biskup -> bp - rotmistrza, rotmistrz -> rotm - profesora, profesor -> prof - hrabiego, hrabiny, hrabia, hrabina -> hr - porucznika, porucznik -> por - podpułkownika, podpułkownik -> ppłk - pułkownika, pułkownik -> płk - podporucznika, podporucznik -> ppor - porucznika, porucznik -> por - marszałka, marszałek -> marsz - chorążego, chorąży -> chor - szeregowego, szeregowego -> szer - kaprala, kapral -> kpr - plutonowego, plutonowy -> plut - kapitana, kapitan -> kpt - admirała, admirał -> adm - wiceadmirała, wiceadmirał -> wadm - kontradmirała, kontradmirał -> kontradm - batalionów, bataliony -> bat - batalionu, batalion -> bat ================================================ FILE: settings/icu-rules/variants-pt.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Portugu.C3.AAs_-_Portuguese - lang: pt words: - Associação -> Ass - Alameda -> Al - Alferes -> Alf - Almirante -> Alm - Arquitecto -> Arq - Arquitecto -> Arqº - Arquiteto -> Arq - Arquiteto -> Arqº - Auto-estrada -> A - Avenida -> Av - Avenida -> Avª - Azinhaga -> Az - Bairro -> B - Bairro -> Bº - Bairro -> Br - Beco -> Bc - Beco -> Bco - Bloco -> Bl - Bombeiros Voluntários -> BV - Bombeiros Voluntários -> B.V - Brigadeiro -> Brg - Cacique -> Cac - Calçada -> Cc - Calçadinha -> Ccnh - Câmara Municipal -> CM - Câmara Municipal -> C.M - Caminho -> Cam - Capitão -> Cap - Casal -> Csl - Cave -> Cv - Centro Comercial -> CC - Centro Comercial -> C.C - Ciclo do Ensino Básico -> CEB - Ciclo do Ensino Básico -> C.E.B - Ciclo do Ensino Básico -> C. E. B - Comandante -> Cmdt - Comendador -> Comend - Companhia -> Cª - Conselheiro -> Cons - Coronel -> Cor - Coronel -> Cel - Corte -> C.te - De -> D´ - De -> D' - Departamento -> Dept - Deputado -> Dep - Direito -> Dto - Dom -> D - Dona -> D - Dona -> Dª - Doutor -> Dr - Doutora -> Dr - Doutora -> Drª - Doutora -> Dra - Duque -> Dq - Edifício -> Ed - Edifício -> Edf - Embaixador -> Emb - Empresa Pública -> EP - Empresa Pública -> E.P - Enfermeiro -> Enfo - Enfermeiro -> Enfº - Enfermeiro -> Enf - Engenheiro -> Eng - Engenheiro -> Engº - Engenheira -> Eng - Engenheira -> Engª - Escadas -> Esc - Escadinhas -> Escnh - Escola Básica -> EB - Escola Básica -> E.B - Esquerdo -> Esq - Estação de Tratamento de Águas Residuais -> ETAR - Estação de Tratamento de Águas Residuais -> E.T.A.R - Estrada -> Estr - Estrada Municipal -> EM - Estrada Nacional -> EN - Estrada Regional -> ER - Frei -> Fr - Frente -> Ft - Futebol Clube -> FC - Futebol Clube -> F.C - Guarda Nacional Republicana -> GNR - Guarda Nacional Republicana -> G.N.R - General -> Gen - General -> Gal - Habitação -> Hab - Infante -> Inf - Instituto -> Inst - Irmã -> Ima - Irmã -> Imª - Irmã -> Im - Irmão -> Imo - Irmão -> Imº - Irmão -> Im - Itinerário Complementar -> IC - Itinerário Principal -> IP - Jardim -> Jrd - Júnior -> Jr - Largo -> Lg - Limitada -> Lda - Loja -> Lj - Lote -> Lt - Loteamento -> Loteam - Lugar -> Lg - Lugar -> Lug - Maestro -> Mto - Major -> Maj - Marechal -> Mal - Marquês -> Mq - Madre -> Me - Mestre -> Me - Ministério -> Min - Monsenhor -> Mons - Municipal -> M - Nacional -> N - Nossa -> N - Nossa -> Nª - Nossa Senhora -> Ns - Nosso -> N - Número -> N - Número -> Nº - Padre -> Pe - Parque -> Pq - Particular -> Part - Pátio -> Pto - Pavilhão -> Pav - Polícia de Segurança Pública -> PSP - Polícia de Segurança Pública -> P.S.P - Polícia Judiciária -> PJ - Polícia Judiciária -> P.J - Praça -> Pc - Praça -> Pç - Praça -> Pr - Praceta -> Pct - Praceta -> Pctª - Presidente -> Presid - Primeiro -> 1º - Professor -> Prof - Professora -> Prof - Professora -> Profª - Projectada -> Proj - Projetada -> Proj - Prolongamento -> Prolng - Quadra -> Q - Quadra -> Qd - Quinta -> Qta - Regional -> R - Rés-do-chão -> R/c - Rés-do-chão -> Rc - Rotunda -> Rot - Ribeira -> Rª - Ribeira -> Rib - Ribeira -> Ribª - Rio -> R - Rua -> R - Santa -> Sta - Santa -> Stª - Santo -> St - Santo -> Sto - Santo -> Stº - São -> S - Sargento -> Sarg - Sem Número -> S/n - Sem Número -> Sn - Senhor -> S - Senhor -> Sr - Senhora -> S - Senhora -> Sª - Senhora -> Srª - Senhora -> Sr.ª - Senhora -> S.ra - Senhora -> Sra - Sobre-Loja -> Slj - Sociedade -> Soc - Sociedade Anónima -> SA - Sociedade Anónima -> S.A - Sport Clube -> SC - Sport Clube -> S.C - Sub-Cave -> Scv - Superquadra -> Sq - Tenente -> Ten - Torre -> Tr - Transversal -> Transv - Travessa -> Trav - Travessa -> Trv - Travessa -> Tv - Universidade -> Univ - Urbanização -> Urb - Vila -> Vl - Visconde -> Visc - Vivenda -> Vv - Zona -> Zn ================================================ FILE: settings/icu-rules/variants-ro.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Rom.C3.A2n.C4.83_-_Romanian - lang: ro words: - Aleea -> ale - Aleea -> al - Bulevardul -> bulevard - Bulevardul -> bulev - Bulevardul -> b-dul - Bulevardul -> blvd - Bulevardul -> blv - Bulevardul -> bdul - Bulevardul -> bul - Bulevardul -> bd - Calea -> cal - Fundătura -> fnd - Fundacul -> fdc - Intrarea -> intr - Intrarea -> int - Piața -> p-ța - Piața -> pța - Strada -> stra - Strada -> str - Stradela -> str-la - Stradela -> sdla - Șoseaua -> sos - Splaiul -> sp - Splaiul -> splaiul - Splaiul -> spl - Vârful -> virful - Vârful -> virf - Vârful -> varf - Vârful -> vf - Muntele -> m-tele - Muntele -> m-te - Muntele -> mnt - Muntele -> mt ================================================ FILE: settings/icu-rules/variants-ru.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#.D0.A0.D1.83.D1.81.D1.81.D0.BA.D0.B8.D0.B9_-_Russian # Source: https://www.plantarium.ru/page/help/topic/abbreviations.html # Source: https://dic.academic.ru/dic.nsf/ruwiki/1871310 - lang: ru words: - Академик, Академика -> Ак - акционерное общество -> АО - аллея -> ал - архипелаг -> арх - атомная электростанция -> АЭС - аэродром -> аэрд - аэропорт -> аэрп - Башкирский, Башкирская, Башкирское, Башкирские -> Баш, Башк, Башкир - Белый, Белая, Белое. Белые -> Бел - болото -> бол - больница -> больн - Большой, Большая, Большое, Большие -> Б, Бол - брод -> бр - бульвар -> бул - бухта -> бух - бывший, бывшая, бывшее, бывшие -> бывш - Великий, Великая, Великое, Великие -> Вел - Верхний, Верхняя, Верхнее, Верхние -> В, Верх - водокачка -> вдкч - водопад -> вдп - водохранилище -> вдхр - вокзал -> вкз, вокз - Восточный, Восточная, Восточное, Восточные -> В, Вост - вулкан -> влк - гидроэлектростанция -> ГЭС - гора -> г - город -> г - дворец культуры, дом культуры -> ДК - дворец спорта -> ДС - деревня -> д, дер - детский оздоровительный лагерь -> ДОЛ - дом -> д - дом отдыха -> Д О - железная дорога -> ж д - железнодорожный, железнодорожная, железнодорожное -> ж-д - железобетонных изделий -> ЖБИ - жилой комплекс -> ЖК - завод -> з-д - закрытое административно-территориальное образование -> ЗАТО - залив -> зал - Западный, Западная, Западное, Западные -> З, Зап, Запад - заповедник -> запов - имени -> им - институт -> инст - исправительная колония -> ИК - километр -> км - Красный, Красная, Красное, Красные -> Кр, Крас - лагерь -> лаг - Левый, Левая,Левое, Левые -> Л, Лев - ледник -> ледн - лесничество -> леснич - лесной, лесная, лесное -> лес - линия электропередачи -> ЛЭП - Малый, Малая, Малое, Малые -> М, Мал - Мордовский, Мордовская, Мордовское, Мордовские -> Мордов - морской, морская, морское -> мор - Московский, Московская, Московское, Московские -> Мос, Моск - мыс -> м - набережная -> наб - Нижний, Нижняя, Нижнее, Нижние -> Ниж, Н - Новый, Новая, Новое, Новые -> Нов, Н - обгонный пункт -> обг п - область -> обл - озеро -> оз - особо охраняемая природная территория -> ООПТ - остановочный пункт -> о п - остров -> о - острова -> о-ва - парк культуры и отдыха -> ПКиО - перевал -> пер - переулок -> пер - пещера -> пещ - пионерский лагерь -> пионерлаг - платформа -> пл, платф - площадь -> пл - подсобное хозяйство -> подсоб хоз - полуостров -> п-ов - посёлок -> пос, п - посёлок городского типа -> п г т, пгт - Правый, Правая, Правое, Правые -> П, Пр, Прав - проезд -> пр - проспект -> просп - пруд -> пр - пустыня -> пуст - разъезд -> рзд - район -> р-н - резинотехнических изделий -> РТИ - река -> р - речной, речная, речное -> реч, речн - Российский, Российская, Российское, Российские -> Рос - Русский, Русская, Русское, Русские -> Рус, Русск - ручей -> руч - садовое некоммерческое товарищество -> СНТ - садовые участки -> сад уч - санаторий -> сан - сарай -> сар - Северный, Северная, Северное, Северные -> С, Сев - село -> с - Сибирский, Сибирская, Сибирское, Сибирские -> Сиб - Советский, Советская, Советское, Советские -> Сов - совхоз -> свх - Сортировочный, Сортировочная, Сортировочное, Сортировочные -> Сорт - станция -> ст - Старый, Старая, Среднее, Средние -> Ср - Татарский, Татарская, Татарское, Татарские -> Тат, Татар - теплоэлекстростанция -> ТЭС - теплоэлектроцентраль -> ТЭЦ - техникум -> техн - тоннель, туннель -> тун - тупик -> туп - улица -> ул - Уральский, Уральская, Уральское, Уральские -> Ур, Урал - урочище -> ур - хозяйство -> хоз, хоз-во - хребет -> хр - хутор -> хут - Чёрный, Чёрная, Чёрное, Чёрные -> Черн - Чувашский, Чувашская, Чувашское, Чувашские -> Чуваш - шахта -> шах - школа -> шк - шоссе -> ш - элеватор -> элев - Южный, Южная, Южное, Южные -> Ю, Юж, Южн ================================================ FILE: settings/icu-rules/variants-sk.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Slovensky_-_Slovak - lang: sk words: - Ulica -> Ul - Námestie -> Nám - Svätého, Svätej -> Sv - Generála -> Gen - Armádneho generála -> Arm gen - Doktora, Doktorky -> Dr - Inžiniera, Inžinierky -> Ing - Majora -> Mjr - Profesora, Profesorky -> Prof - Československej -> Čsl - Plukovníka -> Plk - Podplukovníka -> Pplk - Kapitána -> Kpt - Poručíka -> Por - Podporučíka -> Ppor - Sídlisko -> Sídl - Nábrežie -> Nábr ================================================ FILE: settings/icu-rules/variants-sl.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Sloven.C5.A1.C4.8Dina_-_Slovenian - lang: sl words: - Cesta -> C - Gasilski Dom -> GD - Osnovna šola -> OŠ - Prostovoljno Gasilsko Društvo -> PGD - Savinjski -> Savinj - Slovenskih -> Slov - Spodnja -> Sp - Spodnje -> Sp - Spodnji -> Sp - Srednja -> Sr - Srednje -> Sr - Srednji -> Sr - Sveta -> Sv - Svete -> Sv - Sveti -> Sv - Svetega -> Sv - Šent -> Št - Ulica -> Ul - Velika -> V - Velike -> V - Veliki -> V - Veliko -> V - Velikem -> V - Velika -> Vel - Velike -> Vel - Veliki -> Vel - Veliko -> Vel - Velikem -> Vel - Zdravstveni dom -> ZD - Zgornja -> Zg - Zgornje -> Zg - Zgornji -> Zg ================================================ FILE: settings/icu-rules/variants-sv.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Svenska_-_Swedish - lang: sv words: - ~väg, ~vägen -> v - ~gatan, ~gata -> g - ~gränd, ~gränden -> gr - gamla -> G:la - södra -> s - södra -> s:a - norra -> n - norra -> n:a - östra -> ö - östra -> ö:a - västra -> v - västra -> v:a - ~stig, ~stigen -> st - sankt -> s:t - sankta -> s:ta - ~plats, ~platsen -> pl - lilla -> l - stora -> st ================================================ FILE: settings/icu-rules/variants-tr.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#T.C3.BCrk.C3.A7e_-_Turkish - lang: tr words: - Sokak -> Sk - Sokak -> Sok - Sokağı -> Sk - Sokağı -> Sok - Cadde -> Cd - Caddesi -> Cd - Bulvar -> Bl - Bulvar -> Blv - Bulvarı -> Bl - Mahalle -> Mh - Mahalle -> Mah ================================================ FILE: settings/icu-rules/variants-uk.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#.D0.A3.D0.BA.D1.80.D0.B0.D1.97.D0.BD.D1.81.D1.8C.D0.BA.D0.B0_-_Ukrainian - lang: uk words: - бульвар -> бул - дорога -> дор - провулок -> пров - площа -> пл - проспект -> просп - шосе -> ш - вулиця -> вул ================================================ FILE: settings/icu-rules/variants-vi.yaml ================================================ # Source: https://wiki.openstreetmap.org/wiki/Name_finder:Abbreviations#Ti.E1.BA.BFng_Vi.E1.BB.87t_.E2.80.93_Vietnamese - lang: vi words: - Thành phố -> TP - Thị xã -> TX - Thị trấn -> TT - Quận -> Q - Phường -> P - Phường -> Ph - Quốc lộ -> QL - Tỉnh lộ -> TL - Đại lộ -> ĐL - Đường -> Đ - Công trường -> CT - Quảng trường -> QT - Sân bay -> SB - Sân bay quốc tế -> SBQT - Phi trường -> PT - Đường sắt -> ĐS - Trung tâm -> TT - Trung tâm Thương mại -> TTTM - Khách sạn -> KS - Khách sạn -> K/S - Bưu điện -> BĐ - Đại học -> ĐH - Cao đẳng -> CĐ - Trung học Phổ thông -> THPT - Trung học Cơ sở -> THCS - Tiểu học -> TH - Khu công nghiệp -> KCN - Khu nghỉ mát -> KNM - Khu du lịch -> KDL - Công viên văn hóa -> CVVH - Công viên -> CV - Vươn quốc gia -> VQG - Viện bảo tàng -> VBT - Sân vận động -> SVĐ - Nhà thi đấu -> NTĐ - Câu lạc bộ -> CLB - Nhà thờ -> NT - Nhà hát -> NH - Rạp hát -> RH - Công ty -> Cty - Tổng công ty -> TCty - Tổng công ty -> TCT - Công ty cổ phần -> CTCP - Công ty cổ phần -> Cty CP - Căn cứ không quân -> CCKQ ================================================ FILE: settings/icu_tokenizer.yaml ================================================ query-preprocessing: - step: split_japanese_phrases - step: normalize normalization: - ":: lower ()" - ":: Hans-Hant" - !include icu-rules/unicode-digits-to-decimal.yaml - "'№' > 'no'" - "'n°' > 'no'" - "'nº' > 'no'" - "ª > a" - "º > o" - "[[:Punctuation:][:Symbol:][\u02bc] - [-:]]+ > '-'" - "ß > 'ss'" # German szet is unambiguously equal to double ss - "[^[:alnum:] [:Canonical_Combining_Class=Virama:] [:Space:] [-:]] >" - "[:Lm:] >" - ":: [[:Number:]] Latin ()" - ":: [[:Number:]] Ascii ();" - ":: [[:Number:]] NFD ();" - "[[:Nonspacing Mark:] [:Cf:]] >;" - "[-:]?[:Space:]+[-:]? > ' '" transliteration: - "[-:] > ' '" - ":: Latin ()" - !include icu-rules/extended-unicode-to-asccii.yaml - ":: Ascii ()" - ":: NFD ()" - ":: lower ()" - "[^a-z0-9[:Space:]] >" - ":: NFC ()" - "[:Space:]+ > ' '" sanitizers: - step: clean-housenumbers filter-kind: - housenumber - conscriptionnumber - streetnumber convert-to-name: - (\A|.*,)[^\d,]{3,}(,.*|\Z) - step: clean-postcodes convert-to-address: yes default-pattern: "[A-Z0-9- ]{3,12}" - step: clean-tiger-tags - step: split-name-list delimiters: ; - step: strip-brace-terms - step: tag-analyzer-by-language filter-kind: [".*name.*"] whitelist: [bg,ca,cs,da,de,el,en,es,et,eu,fi,fr,gl,hu,it,ja,mg,ms,nl,"no",pl,pt,ro,ru,sk,sl,sv,tr,uk,vi] use-defaults: all mode: append - step: tag-japanese token-analysis: - analyzer: generic - id: "@housenumber" analyzer: housenumbers - id: "@postcode" analyzer: postcodes - id: bg analyzer: generic mode: variant-only variants: - !include icu-rules/variants-bg.yaml - id: ca analyzer: generic mode: variant-only variants: - !include icu-rules/variants-ca.yaml - id: cs analyzer: generic mode: variant-only variants: - !include icu-rules/variants-cs.yaml - id: da analyzer: generic mode: variant-only variants: - !include icu-rules/variants-da.yaml - id: de analyzer: generic mode: variant-only variants: - !include icu-rules/variants-de.yaml mutations: - pattern: ä replacements: ["ä", "ae"] - pattern: ö replacements: ["ö", "oe"] - pattern: ü replacements: ["ü", "ue"] - id: el analyzer: generic mode: variant-only variants: - !include icu-rules/variants-el.yaml - id: en analyzer: generic mode: variant-only variants: - !include icu-rules/variants-en.yaml - id: es analyzer: generic mode: variant-only variants: - !include icu-rules/variants-es.yaml - id: et analyzer: generic mode: variant-only variants: - !include icu-rules/variants-et.yaml - id: eu analyzer: generic mode: variant-only variants: - !include icu-rules/variants-eu.yaml - id: fi analyzer: generic mode: variant-only variants: - !include icu-rules/variants-fi.yaml - id: fr analyzer: generic mode: variant-only variants: - !include icu-rules/variants-fr.yaml - id: gl analyzer: generic mode: variant-only variants: - !include icu-rules/variants-gl.yaml - id: hu analyzer: generic mode: variant-only variants: - !include icu-rules/variants-hu.yaml - id: it analyzer: generic mode: variant-only variants: - !include icu-rules/variants-it.yaml - id: mg analyzer: generic mode: variant-only variants: - !include icu-rules/variants-mg.yaml - id: ms analyzer: generic mode: variant-only variants: - !include icu-rules/variants-ms.yaml - id: nl analyzer: generic mode: variant-only variants: - !include icu-rules/variants-nl.yaml - id: "no" analyzer: generic mode: variant-only variants: - !include icu-rules/variants-no.yaml - id: pl analyzer: generic mode: variant-only variants: - !include icu-rules/variants-pl.yaml - id: pt analyzer: generic mode: variant-only variants: - !include icu-rules/variants-pt.yaml - id: ro analyzer: generic mode: variant-only variants: - !include icu-rules/variants-ro.yaml - id: ru analyzer: generic mode: variant-only variants: - !include icu-rules/variants-ru.yaml - id: sk analyzer: generic mode: variant-only variants: - !include icu-rules/variants-sk.yaml - id: sl analyzer: generic mode: variant-only variants: - !include icu-rules/variants-sl.yaml - id: sv analyzer: generic mode: variant-only variants: - !include icu-rules/variants-sv.yaml - id: tr analyzer: generic mode: variant-only variants: - !include icu-rules/variants-tr.yaml - id: uk analyzer: generic mode: variant-only variants: - !include icu-rules/variants-uk.yaml - id: vi analyzer: generic mode: variant-only variants: - !include icu-rules/variants-vi.yaml ================================================ FILE: settings/phrase-settings.json ================================================ { "Comments": [ "Black list correspond to class/type combinations to exclude", "If a class is in the white list then all types will", "be ignored except the ones given in the list.", "Also use this list to exclude an entire class from special phrases." ], "blackList": { "boundary": [ "administrative" ], "place": [ "house", "houses" ] }, "whiteList": { "highway": [ "bus_stop", "rest_area", "raceway'" ], "building": [] } } ================================================ FILE: src/nominatim_api/__init__.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ The public interface of the Nominatim library. Classes and functions defined in this file are considered stable. Always import from this file, not from the source files directly. """ from .errors import (UsageError as UsageError) from .config import (Configuration as Configuration) from .core import (NominatimAPI as NominatimAPI, NominatimAPIAsync as NominatimAPIAsync) from .connection import (SearchConnection as SearchConnection) from .status import (StatusResult as StatusResult) from .types import (PlaceID as PlaceID, OsmID as OsmID, PlaceRef as PlaceRef, Point as Point, Bbox as Bbox, GeometryFormat as GeometryFormat, DataLayer as DataLayer, QueryStatistics as QueryStatistics) from .results import (SourceTable as SourceTable, AddressLine as AddressLine, AddressLines as AddressLines, WordInfo as WordInfo, WordInfos as WordInfos, DetailedResult as DetailedResult, ReverseResult as ReverseResult, ReverseResults as ReverseResults, SearchResult as SearchResult, SearchResults as SearchResults) from .localization import (Locales as Locales) from .result_formatting import (FormatDispatcher as FormatDispatcher, load_format_dispatcher as load_format_dispatcher) from .version import NOMINATIM_API_VERSION as __version__ ================================================ FILE: src/nominatim_api/config.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. # This file is just a placeholder to make the config module available # during development. It will be replaced by nominatim_db/config.py on # installation. # flake8: noqa from nominatim_db.config import * ================================================ FILE: src/nominatim_api/connection.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Extended SQLAlchemy connection class that also includes access to the schema. """ from typing import cast, Any, Mapping, Sequence, Union, Dict, Optional, Set, \ Awaitable, Callable, TypeVar import asyncio import sqlalchemy as sa from sqlalchemy.ext.asyncio import AsyncConnection from .typing import SaFromClause from .sql.sqlalchemy_schema import SearchTables from .sql.sqlalchemy_types import Geometry from .logging import log from .config import Configuration T = TypeVar('T') class SearchConnection: """ An extended SQLAlchemy connection class, that also contains the table definitions. The underlying asynchronous SQLAlchemy connection can be accessed with the 'connection' property. The 't' property is the collection of Nominatim tables. """ def __init__(self, conn: AsyncConnection, tables: SearchTables, properties: Dict[str, Any], config: Configuration) -> None: self.connection = conn self.t = tables self.config = config self._property_cache = properties self._classtables: Optional[Set[str]] = None self.query_timeout: Optional[int] = None def set_query_timeout(self, timeout: Optional[int]) -> None: """ Set the timeout after which a query over this connection is cancelled. """ self.query_timeout = timeout async def scalar(self, sql: sa.sql.base.Executable, params: Union[Mapping[str, Any], None] = None) -> Any: """ Execute a 'scalar()' query on the connection. """ log().sql(self.connection, sql, params) return await asyncio.wait_for(self.connection.scalar(sql, params), self.query_timeout) async def execute(self, sql: 'sa.Executable', params: Union[Mapping[str, Any], Sequence[Mapping[str, Any]], None] = None ) -> 'sa.Result[Any]': """ Execute a 'execute()' query on the connection. """ log().sql(self.connection, sql, params) return await asyncio.wait_for(self.connection.execute(sql, params), self.query_timeout) async def get_property(self, name: str, cached: bool = True) -> str: """ Get a property from Nominatim's property table. Property values are normally cached so that they are only retrieved from the database when they are queried for the first time with this function. Set 'cached' to False to force reading the property from the database. Raises a ValueError if the property does not exist. """ lookup_name = f'DBPROP:{name}' if cached and lookup_name in self._property_cache: return cast(str, self._property_cache[lookup_name]) sql = sa.select(self.t.properties.c.value)\ .where(self.t.properties.c.property == name) value = await self.connection.scalar(sql) if value is None: raise ValueError(f"Property '{name}' not found in database.") self._property_cache[lookup_name] = cast(str, value) return cast(str, value) async def get_db_property(self, name: str) -> Any: """ Get a setting from the database. At the moment, only 'server_version', the version of the database software, can be retrieved with this function. Raises a ValueError if the property does not exist. """ if name != 'server_version': raise ValueError(f"DB setting '{name}' not found in database.") return self._property_cache['DB:server_version'] async def get_cached_value(self, group: str, name: str, factory: Callable[[], Awaitable[T]]) -> T: """ Access the cache for this Nominatim instance. Each cache value needs to belong to a group and have a name. This function is for internal API use only. `factory` is an async callback function that produces the value if it is not already cached. Returns the cached value or the result of factory (also caching the result). """ full_name = f'{group}:{name}' if full_name in self._property_cache: return cast(T, self._property_cache[full_name]) value = await factory() self._property_cache[full_name] = value return value async def get_class_table(self, cls: str, typ: str) -> Optional[SaFromClause]: """ Lookup up if there is a classtype table for the given category and return a SQLAlchemy table for it, if it exists. """ if self._classtables is None: res = await self.execute(sa.text("""SELECT tablename FROM pg_tables WHERE tablename LIKE 'place_classtype_%' """)) self._classtables = {r[0] for r in res} tablename = f"place_classtype_{cls}_{typ}" if tablename not in self._classtables: return None if tablename in self.t.meta.tables: return self.t.meta.tables[tablename] return sa.Table(tablename, self.t.meta, sa.Column('place_id', sa.BigInteger), sa.Column('centroid', Geometry)) ================================================ FILE: src/nominatim_api/core.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of classes for API access via libraries. """ from typing import Mapping, Optional, Any, AsyncIterator, Dict, Sequence, List, \ Union, Tuple, cast import asyncio import sys import contextlib from pathlib import Path if sys.version_info >= (3, 11): from asyncio import timeout_at else: from async_timeout import timeout_at import sqlalchemy as sa import sqlalchemy.ext.asyncio as sa_asyncio from .errors import UsageError from .sql.sqlalchemy_schema import SearchTables from .sql.async_core_library import PGCORE_LIB, PGCORE_ERROR from .config import Configuration from .sql import sqlite_functions, sqlalchemy_functions # noqa from .connection import SearchConnection from .status import get_status, StatusResult from .lookup import get_places, get_detailed_place from .reverse import ReverseGeocoder from .timeout import Timeout from . import search as nsearch from . import types as ntyp from .results import DetailedResult, ReverseResult, SearchResults class NominatimAPIAsync: """ The main frontend to the Nominatim database implements the functions for lookup, forward and reverse geocoding using asynchronous functions. This class shares most of the functions with its synchronous version. There are some additional functions or parameters, which are documented below. This class should usually be used as a context manager in 'with' context. """ def __init__(self, project_dir: Optional[Union[str, Path]] = None, environ: Optional[Mapping[str, str]] = None, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: """ Initiate a new frontend object with synchronous API functions. Parameters: project_dir: Path to the [project directory](../admin/Import.md#creating-the-project-directory) of the local Nominatim installation. environ: Mapping of [configuration parameters](../customize/Settings.md). When set, replaces any configuration via environment variables. Settings in this mapping also have precedence over any parameters found in the `.env` file of the project directory. loop: The asyncio event loop that will be used when calling functions. Only needed, when a custom event loop is used and the Python version is 3.9 or earlier. """ self.config = Configuration(project_dir, environ) self.query_timeout = self.config.get_int('QUERY_TIMEOUT') \ if self.config.QUERY_TIMEOUT else None self.request_timeout = self.config.get_int('REQUEST_TIMEOUT') \ if self.config.REQUEST_TIMEOUT else None self.reverse_restrict_to_country_area = self.config.get_bool('SEARCH_WITHIN_COUNTRIES') self.server_version = 0 if sys.version_info >= (3, 10): self._engine_lock = asyncio.Lock() else: self._engine_lock = asyncio.Lock(loop=loop) self._engine: Optional[sa_asyncio.AsyncEngine] = None self._tables: Optional[SearchTables] = None self._property_cache: Dict[str, Any] = {'DB:server_version': 0} async def setup_database(self) -> None: """ Set up the SQL engine and connections. This function will be implicitly called when the database is accessed for the first time. You may also call it explicitly to avoid that the first call is delayed by the setup. """ async with self._engine_lock: if self._engine: return extra_args: Dict[str, Any] = {'future': True, 'echo': self.config.get_bool('DEBUG_SQL')} if self.config.get_int('API_POOL_SIZE') == 0: extra_args['poolclass'] = sa.pool.NullPool else: extra_args['poolclass'] = sa.pool.AsyncAdaptedQueuePool extra_args['max_overflow'] = 0 extra_args['pool_size'] = self.config.get_int('API_POOL_SIZE') is_sqlite = self.config.DATABASE_DSN.startswith('sqlite:') if is_sqlite: params = dict((p.split('=', 1) for p in self.config.DATABASE_DSN[7:].split(';'))) dburl = sa.engine.URL.create('sqlite+aiosqlite', database=params.get('dbname')) if not ('NOMINATIM_DATABASE_RW' in self.config.environ and self.config.get_bool('DATABASE_RW')) \ and not Path(params.get('dbname', '')).is_file(): raise UsageError(f"SQlite database '{params.get('dbname')}' does not exist.") else: dsn = self.config.get_database_params() query = {k: str(v) for k, v in dsn.items() if k not in ('user', 'password', 'dbname', 'host', 'port')} dburl = sa.engine.URL.create( f'postgresql+{PGCORE_LIB}', database=cast(str, dsn.get('dbname')), username=cast(str, dsn.get('user')), password=cast(str, dsn.get('password')), host=cast(str, dsn.get('host')), port=int(cast(str, dsn['port'])) if 'port' in dsn else None, query=query) engine = sa_asyncio.create_async_engine(dburl, **extra_args) if is_sqlite: server_version = 0 @sa.event.listens_for(engine.sync_engine, "connect") def _on_sqlite_connect(dbapi_con: Any, _: Any) -> None: dbapi_con.run_async(lambda conn: conn.enable_load_extension(True)) sqlite_functions.install_custom_functions(dbapi_con) cursor = dbapi_con.cursor() cursor.execute("SELECT load_extension('mod_spatialite')") cursor.execute('SELECT SetDecimalPrecision(7)') dbapi_con.run_async(lambda conn: conn.enable_load_extension(False)) else: try: async with engine.begin() as conn: result = await conn.scalar(sa.text('SHOW server_version_num')) server_version = int(result) await conn.execute(sa.text("SET jit_above_cost TO '-1'")) await conn.execute(sa.text( "SET max_parallel_workers_per_gather TO '0'")) except (PGCORE_ERROR, sa.exc.OperationalError): server_version = 0 @sa.event.listens_for(engine.sync_engine, "connect") def _on_connect(dbapi_con: Any, _: Any) -> None: cursor = dbapi_con.cursor() cursor.execute("SET jit_above_cost TO '-1'") cursor.execute("SET max_parallel_workers_per_gather TO '0'") self._property_cache['DB:server_version'] = server_version self._tables = SearchTables(sa.MetaData()) self._engine = engine async def close(self) -> None: """ Close all active connections to the database. The NominatimAPIAsync object remains usable after closing. If a new API functions is called, new connections are created. """ if self._engine is not None: await self._engine.dispose() async def __aenter__(self) -> 'NominatimAPIAsync': return self async def __aexit__(self, *_: Any) -> None: await self.close() @contextlib.asynccontextmanager async def begin(self, abs_timeout: Optional[float] = None) -> AsyncIterator[SearchConnection]: """ Create a new connection with automatic transaction handling. This function may be used to get low-level access to the database. Refer to the documentation of SQLAlchemy for details how to use the connection object. You may optionally give an absolute timeout until when to wait for a connection to become available. """ if self._engine is None: await self.setup_database() assert self._engine is not None assert self._tables is not None async with timeout_at(abs_timeout), self._engine.begin() as conn: yield SearchConnection(conn, self._tables, self._property_cache, self.config) async def status(self) -> StatusResult: """ Return the status of the database. """ timeout = Timeout(self.request_timeout) try: async with self.begin(abs_timeout=timeout.abs) as conn: conn.set_query_timeout(self.query_timeout) status = await get_status(conn) except (PGCORE_ERROR, sa.exc.OperationalError): return StatusResult(700, 'Database connection failed') return status async def details(self, place: ntyp.PlaceRef, **params: Any) -> Optional[DetailedResult]: """ Get detailed information about a place in the database. Returns None if there is no entry under the given ID. """ timeout = Timeout(self.request_timeout) details = ntyp.LookupDetails.from_kwargs(params) with details.query_stats as qs: async with self.begin(abs_timeout=timeout.abs) as conn: qs.log_time('start_query') conn.set_query_timeout(self.query_timeout) if details.keywords: await nsearch.make_query_analyzer(conn) return await get_detailed_place(conn, place, details) async def lookup(self, places: Sequence[ntyp.PlaceRef], **params: Any) -> SearchResults: """ Get simple information about a list of places. Returns a list of place information for all IDs that were found. """ timeout = Timeout(self.request_timeout) details = ntyp.LookupDetails.from_kwargs(params) with details.query_stats as qs: async with self.begin(abs_timeout=timeout.abs) as conn: qs.log_time('start_query') conn.set_query_timeout(self.query_timeout) if details.keywords: await nsearch.make_query_analyzer(conn) return await get_places(conn, places, details) async def reverse(self, coord: ntyp.AnyPoint, **params: Any) -> Optional[ReverseResult]: """ Find a place by its coordinates. Also known as reverse geocoding. Returns the closest result that can be found or None if no place matches the given criteria. """ # The following negation handles NaN correctly. Don't change. if not abs(coord[0]) <= 180 or not abs(coord[1]) <= 90: # There are no results to be expected outside valid coordinates. return None timeout = Timeout(self.request_timeout) details = ntyp.ReverseDetails.from_kwargs(params) with details.query_stats as qs: async with self.begin(abs_timeout=timeout.abs) as conn: qs.log_time('start_query') conn.set_query_timeout(self.query_timeout) if details.keywords: await nsearch.make_query_analyzer(conn) geocoder = ReverseGeocoder(conn, details, self.reverse_restrict_to_country_area) return await geocoder.lookup(coord) async def search(self, query: str, **params: Any) -> SearchResults: """ Find a place by free-text search. Also known as forward geocoding. """ timeout = Timeout(self.request_timeout) details = ntyp.SearchDetails.from_kwargs(params) with details.query_stats as qs: query = query.strip() if not query: raise UsageError('Nothing to search for.') async with self.begin(abs_timeout=timeout.abs) as conn: qs.log_time('start_query') conn.set_query_timeout(self.query_timeout) geocoder = nsearch.ForwardGeocoder(conn, details, timeout) phrases = [nsearch.Phrase(nsearch.PHRASE_ANY, p.strip()) for p in query.split(',')] return await geocoder.lookup(phrases) async def search_address(self, amenity: Optional[str] = None, street: Optional[str] = None, city: Optional[str] = None, county: Optional[str] = None, state: Optional[str] = None, country: Optional[str] = None, postalcode: Optional[str] = None, **params: Any) -> SearchResults: """ Find an address using structured search. """ timeout = Timeout(self.request_timeout) details = ntyp.SearchDetails.from_kwargs(params) with details.query_stats as qs: phrases: List[nsearch.Phrase] = [] if amenity: phrases.append(nsearch.Phrase(nsearch.PHRASE_AMENITY, amenity)) if street: phrases.append(nsearch.Phrase(nsearch.PHRASE_STREET, street)) if city: phrases.append(nsearch.Phrase(nsearch.PHRASE_CITY, city)) if county: phrases.append(nsearch.Phrase(nsearch.PHRASE_COUNTY, county)) if state: phrases.append(nsearch.Phrase(nsearch.PHRASE_STATE, state)) if postalcode: phrases.append(nsearch.Phrase(nsearch.PHRASE_POSTCODE, postalcode)) if country: phrases.append(nsearch.Phrase(nsearch.PHRASE_COUNTRY, country)) if not phrases: raise UsageError('Nothing to search for.') if amenity or street: details.restrict_min_max_rank(26, 30) elif city: details.restrict_min_max_rank(13, 25) elif county: details.restrict_min_max_rank(10, 12) elif state: details.restrict_min_max_rank(5, 9) elif postalcode: details.restrict_min_max_rank(5, 11) else: details.restrict_min_max_rank(4, 4) if details.layers is None: details.layers = ntyp.DataLayer.ADDRESS if amenity: details.layers |= ntyp.DataLayer.POI async with self.begin(abs_timeout=timeout.abs) as conn: qs.log_time('start_query') conn.set_query_timeout(self.query_timeout) geocoder = nsearch.ForwardGeocoder(conn, details, timeout) return await geocoder.lookup(phrases) async def search_category(self, categories: List[Tuple[str, str]], near_query: Optional[str] = None, **params: Any) -> SearchResults: """ Find an object of a certain category near another place. The near place may either be given as an unstructured search query in itself or as coordinates. """ timeout = Timeout(self.request_timeout) details = ntyp.SearchDetails.from_kwargs(params) with details.query_stats as qs: if not categories: return SearchResults() async with self.begin(abs_timeout=timeout.abs) as conn: qs.log_time('start_query') conn.set_query_timeout(self.query_timeout) if near_query: phrases = [nsearch.Phrase(nsearch.PHRASE_ANY, p) for p in near_query.split(',')] else: phrases = [] if details.keywords: await nsearch.make_query_analyzer(conn) geocoder = nsearch.ForwardGeocoder(conn, details, timeout) return await geocoder.lookup_pois(categories, phrases) class NominatimAPI: """ This class provides a thin synchronous wrapper around the asynchronous Nominatim functions. It creates its own event loop and runs each synchronous function call to completion using that loop. This class should usually be used as a context manager in 'with' context. """ def __init__(self, project_dir: Optional[Union[str, Path]] = None, environ: Optional[Mapping[str, str]] = None) -> None: """ Initiate a new frontend object with synchronous API functions. Parameters: project_dir: Path to the [project directory](../admin/Import.md#creating-the-project-directory) of the local Nominatim installation. environ: Mapping of [configuration parameters](../customize/Settings.md). When set, replaces any configuration via environment variables. Settings in this mapping also have precedence over any parameters found in the `.env` file of the project directory. """ self._loop = asyncio.new_event_loop() self._async_api = NominatimAPIAsync(project_dir, environ, loop=self._loop) def close(self) -> None: """ Close all active connections to the database. This function also closes the asynchronous worker loop making the NominatimAPI object unusable. """ if not self._loop.is_closed(): self._loop.run_until_complete(self._async_api.close()) self._loop.close() def __enter__(self) -> 'NominatimAPI': return self def __exit__(self, *_: Any) -> None: self.close() @property def config(self) -> Configuration: """ Provide read-only access to the [configuration](Configuration.md) used by the API. """ return self._async_api.config def status(self) -> StatusResult: """ Return the status of the database as a dataclass object with the fields described below. Returns: status(int): A status code as described on the status page. message(str): Either 'OK' or a human-readable message of the problem encountered. software_version(tuple): A tuple with the version of the Nominatim library consisting of (major, minor, patch, db-patch) version. database_version(tuple): A tuple with the version of the library which was used for the import or last migration. Also consists of (major, minor, patch, db-patch). data_updated(datetime): Timestamp with the age of the data. """ return self._loop.run_until_complete(self._async_api.status()) def details(self, place: ntyp.PlaceRef, **params: Any) -> Optional[DetailedResult]: """ Get detailed information about a place in the database. The result is a dataclass object with the fields described below or `None` if the place could not be found in the database. Parameters: place: Description of the place to look up. See [Place identification](Input-Parameter-Types.md#place-identification) for the various ways to reference a place. Other parameters: geometry_output (enum): Add the full geometry of the place to the result. Multiple formats may be selected. Note that geometries can become quite large. (Default: none) geometry_simplification (float): Simplification factor to use on the geometries before returning them. The factor expresses the tolerance in degrees from which the geometry may differ. Topology is preserved. (Default: 0.0) address_details (bool): Add detailed information about the places that make up the address of the requested object. (Default: False) linked_places (bool): Add detailed information about the places that link to the result. (Default: False) parented_places (bool): Add detailed information about all places for which the requested object is a parent, i.e. all places for which the object provides the address details. Only POI places can have parents. (Default: False) keywords (bool): Add detailed information about the search terms used for this place. query_stats (QueryStatistics): When given collects statistics about the query execution. Returns: source_table (enum): Data source of the place. See below for possible values. category (tuple): A tuple of two strings with the primary OSM tag and value. centroid (Point): Point position of the place. place_id (Optional[int]): Internal ID of the place. This ID may differ for the same place between different installations. parent_place_id (Optional(int]): Internal ID of the parent of this place. Only meaning full for POI-like objects (places with a rank_address of 30). linked_place_id (Optional[int]): Internal ID of the place this object links to. When this ID is set then there is no guarantee that the rest of the result information is complete. admin_level (int): Value of the `admin_level` OSM tag. Only meaningful for administrative boundary objects. indexed_date (datetime): Timestamp when the place was last updated. osm_object (Optional[tuple]): OSM type and ID of the place, if available. names (Optional[dict]): Dictionary of names of the place. Keys are usually the corresponding OSM tag keys. address (Optional[dict]): Dictionary of address parts directly attributed to the place. Keys are usually the corresponding OSM tag keys with the `addr:` prefix removed. extratags (Optional[dict]): Dictionary of additional attributes for the place. Usually OSM tag keys and values. housenumber (Optional[str]): House number of the place, normalised for lookup. To get the house number in its original spelling, use `address['housenumber']`. postcode (Optional[str]): Computed postcode for the place. To get directly attributed postcodes, use `address['postcode']` instead. wikipedia (Optional[str]): Reference to a wikipedia site for the place. The string has the format :. rank_address (int): [Address rank](../customize/Ranking.md#address-rank). rank_search (int): [Search rank](../customize/Ranking.md#search-rank). importance (Optional[float]): Relative importance of the place. This is a measure how likely the place will be searched for. country_code (Optional[str]): Country the feature is in as ISO 3166-1 alpha-2 country code. address_rows (Optional[AddressLines]): List of places that make up the computed address. `None` when `address_details` parameter was False. linked_rows (Optional[AddressLines]): List of places that link to the object. `None` when `linked_places` parameter was False. parented_rows (Optional[AddressLines]): List of direct children of the place. `None` when `parented_places` parameter was False. name_keywords (Optional[WordInfos]): List of search words for the name of the place. `None` when `keywords` parameter is set to False. address_keywords (Optional[WordInfos]): List of search word for the address of the place. `None` when `keywords` parameter is set to False. geometry (dict): Dictionary containing the full geometry of the place in the formats requested in the `geometry_output` parameter. """ return self._loop.run_until_complete(self._async_api.details(place, **params)) def lookup(self, places: Sequence[ntyp.PlaceRef], **params: Any) -> SearchResults: """ Get simple information about a list of places. Returns a list of place information for all IDs that were found. Each result is a dataclass with the fields detailed below. Parameters: places: List of descriptions of the place to look up. See [Place identification](Input-Parameter-Types.md#place-identification) for the various ways to reference a place. Other parameters: geometry_output (enum): Add the full geometry of the place to the result. Multiple formats may be selected. Note that geometries can become quite large. (Default: none) geometry_simplification (float): Simplification factor to use on the geometries before returning them. The factor expresses the tolerance in degrees from which the geometry may differ. Topology is preserved. (Default: 0.0) address_details (bool): Add detailed information about the places that make up the address of the requested object. (Default: False) linked_places (bool): Add detailed information about the places that link to the result. (Default: False) parented_places (bool): Add detailed information about all places for which the requested object is a parent, i.e. all places for which the object provides the address details. Only POI places can have parents. (Default: False) keywords (bool): Add detailed information about the search terms used for this place. query_stats (QueryStatistics): When given collects statistics about the query execution. Returns: source_table (enum): Data source of the place. See below for possible values. category (tuple): A tuple of two strings with the primary OSM tag and value. centroid (Point): Point position of the place. place_id (Optional[int]): Internal ID of the place. This ID may differ for the same place between different installations. osm_object (Optional[tuple]): OSM type and ID of the place, if available. names (Optional[dict]): Dictionary of names of the place. Keys are usually the corresponding OSM tag keys. address (Optional[dict]): Dictionary of address parts directly attributed to the place. Keys are usually the corresponding OSM tag keys with the `addr:` prefix removed. extratags (Optional[dict]): Dictionary of additional attributes for the place. Usually OSM tag keys and values. housenumber (Optional[str]): House number of the place, normalised for lookup. To get the house number in its original spelling, use `address['housenumber']`. postcode (Optional[str]): Computed postcode for the place. To get directly attributed postcodes, use `address['postcode']` instead. wikipedia (Optional[str]): Reference to a wikipedia site for the place. The string has the format :. rank_address (int): [Address rank](../customize/Ranking.md#address-rank). rank_search (int): [Search rank](../customize/Ranking.md#search-rank). importance (Optional[float]): Relative importance of the place. This is a measure how likely the place will be searched for. country_code (Optional[str]): Country the feature is in as ISO 3166-1 alpha-2 country code. address_rows (Optional[AddressLines]): List of places that make up the computed address. `None` when `address_details` parameter was False. linked_rows (Optional[AddressLines]): List of places that link to the object. `None` when `linked_places` parameter was False. parented_rows (Optional[AddressLines]): List of direct children of the place. `None` when `parented_places` parameter was False. name_keywords (Optional[WordInfos]): List of search words for the name of the place. `None` when `keywords` parameter is set to False. address_keywords (Optional[WordInfos]): List of search word for the address of the place. `None` when `keywords` parameter is set to False. bbox (Bbox): Bounding box of the full geometry of the place. If the place is a single point, then the size of the bounding box is guessed according to the type of place. geometry (dict): Dictionary containing the full geometry of the place in the formats requested in the `geometry_output` parameter. """ return self._loop.run_until_complete(self._async_api.lookup(places, **params)) def reverse(self, coord: ntyp.AnyPoint, **params: Any) -> Optional[ReverseResult]: """ Find a place by its coordinates. Also known as reverse geocoding. Returns the closest result that can be found or `None` if no place matches the given criteria. The result is a dataclass with the fields as detailed below. Parameters: coord: Coordinate to lookup the place for as a Point or a tuple (x, y). Must be in WGS84 projection. Other parameters: max_rank (int): Highest address rank to return. Can be used to restrict search to streets or settlements. layers (enum): Defines the kind of data to take into account. See description of layers below. (Default: addresses and POIs) geometry_output (enum): Add the full geometry of the place to the result. Multiple formats may be selected. Note that geometries can become quite large. (Default: none) geometry_simplification (float): Simplification factor to use on the geometries before returning them. The factor expresses the tolerance in degrees from which the geometry may differ. Topology is preserved. (Default: 0.0) address_details (bool): Add detailed information about the places that make up the address of the requested object. (Default: False) linked_places (bool): Add detailed information about the places that link to the result. (Default: False) parented_places (bool): Add detailed information about all places for which the requested object is a parent, i.e. all places for which the object provides the address details. Only POI places can have parents. (Default: False) keywords (bool): Add detailed information about the search terms used for this place. query_stats (QueryStatistics): When given collects statistics about the query execution. Returns: source_table (enum): Data source of the place. See below for possible values. category (tuple): A tuple of two strings with the primary OSM tag and value. centroid (Point): Point position of the place. place_id (Optional[int]): Internal ID of the place. This ID may differ for the same place between different installations. osm_object (Optional[tuple]): OSM type and ID of the place, if available. names (Optional[dict]): Dictionary of names of the place. Keys are usually the corresponding OSM tag keys. address (Optional[dict]): Dictionary of address parts directly attributed to the place. Keys are usually the corresponding OSM tag keys with the `addr:` prefix removed. extratags (Optional[dict]): Dictionary of additional attributes for the place. Usually OSM tag keys and values. housenumber (Optional[str]): House number of the place, normalised for lookup. To get the house number in its original spelling, use `address['housenumber']`. postcode (Optional[str]): Computed postcode for the place. To get directly attributed postcodes, use `address['postcode']` instead. wikipedia (Optional[str]): Reference to a wikipedia site for the place. The string has the format :. rank_address (int): [Address rank](../customize/Ranking.md#address-rank). rank_search (int): [Search rank](../customize/Ranking.md#search-rank). importance (Optional[float]): Relative importance of the place. This is a measure how likely the place will be searched for. country_code (Optional[str]): Country the feature is in as ISO 3166-1 alpha-2 country code. address_rows (Optional[AddressLines]): List of places that make up the computed address. `None` when `address_details` parameter was False. linked_rows (Optional[AddressLines]): List of places that link to the object. `None` when `linked_places` parameter was False. parented_rows (Optional[AddressLines]): List of direct children of the place. `None` when `parented_places` parameter was False. name_keywords (Optional[WordInfos]): List of search words for the name of the place. `None` when `keywords` parameter is set to False. address_keywords (Optional[WordInfos]): List of search word for the address of the place. `None` when `keywords` parameter is set to False. bbox (Bbox): Bounding box of the full geometry of the place. If the place is a single point, then the size of the bounding box is guessed according to the type of place. geometry (dict): Dictionary containing the full geometry of the place in the formats requested in the `geometry_output` parameter. distance (Optional[float]): Distance in degree from the input point. """ return self._loop.run_until_complete(self._async_api.reverse(coord, **params)) def search(self, query: str, **params: Any) -> SearchResults: """ Find a place by free-text search. Also known as forward geocoding. Parameters: query: Free-form text query searching for a place. Other parameters: max_results (int): Maximum number of results to return. The actual number of results may be less. (Default: 10) min_rank (int): Lowest permissible rank for the result. For addressable places this is the minimum [address rank](../customize/Ranking.md#address-rank). For all other places the [search rank](../customize/Ranking.md#search-rank) is used. max_rank (int): Highest permissible rank for the result. See min_rank above. layers (enum): Defines the kind of data to take into account. See [layers section](Input-Parameter-Types.md#layers) for details. (Default: addresses and POIs) countries (list[str]): Restrict search to countries with the given ISO 3166-1 alpha-2 country code. An empty list (the default) disables this filter. excluded (list[int | str]): A list of internal Nominatim IDs or OSM IDs to exclude from the search. viewbox (Optional[Bbox]): Bounding box of an area to focus search on. bounded_viewbox (bool): Consider the bounding box given in `viewbox` as a filter and return only results within the bounding box. near (Optional[Point]): Focus search around the given point and return results ordered by distance to the given point. near_radius (Optional[float]): Restrict results to results within the given distance in degrees of `near` point. Ignored, when `near` is not set. categories (list[tuple]): Restrict search to places of the given categories. The category is the main OSM tag assigned to each place. An empty list (the default) disables this filter. geometry_output (enum): Add the full geometry of the place to the result. Multiple formats may be selected. Note that geometries can become quite large. (Default: none) geometry_simplification (float): Simplification factor to use on the geometries before returning them. The factor expresses the tolerance in degrees from which the geometry may differ. Topology is preserved. (Default: 0.0) address_details (bool): Add detailed information about the places that make up the address of the requested object. (Default: False) linked_places (bool): Add detailed information about the places that link to the result. (Default: False) parented_places (bool): Add detailed information about all places for which the requested object is a parent, i.e. all places for which the object provides the address details. Only POI places can have parents. (Default: False) keywords (bool): Add detailed information about the search terms used for this place. query_stats (QueryStatistics): When given collects statistics about the query execution. Returns: source_table (enum): Data source of the place. See below for possible values. category (tuple): A tuple of two strings with the primary OSM tag and value. centroid (Point): Point position of the place. place_id (Optional[int]): Internal ID of the place. This ID may differ for the same place between different installations. osm_object (Optional[tuple]): OSM type and ID of the place, if available. names (Optional[dict]): Dictionary of names of the place. Keys are usually the corresponding OSM tag keys. address (Optional[dict]): Dictionary of address parts directly attributed to the place. Keys are usually the corresponding OSM tag keys with the `addr:` prefix removed. extratags (Optional[dict]): Dictionary of additional attributes for the place. Usually OSM tag keys and values. housenumber (Optional[str]): House number of the place, normalised for lookup. To get the house number in its original spelling, use `address['housenumber']`. postcode (Optional[str]): Computed postcode for the place. To get directly attributed postcodes, use `address['postcode']` instead. wikipedia (Optional[str]): Reference to a wikipedia site for the place. The string has the format :. rank_address (int): [Address rank](../customize/Ranking.md#address-rank). rank_search (int): [Search rank](../customize/Ranking.md#search-rank). importance (Optional[float]): Relative importance of the place. This is a measure how likely the place will be searched for. country_code (Optional[str]): Country the feature is in as ISO 3166-1 alpha-2 country code. address_rows (Optional[AddressLines]): List of places that make up the computed address. `None` when `address_details` parameter was False. linked_rows (Optional[AddressLines]): List of places that link to the object. `None` when `linked_places` parameter was False. parented_rows (Optional[AddressLines]): List of direct children of the place. `None` when `parented_places` parameter was False. name_keywords (Optional[WordInfos]): List of search words for the name of the place. `None` when `keywords` parameter is set to False. address_keywords (Optional[WordInfos]): List of search word for the address of the place. `None` when `keywords` parameter is set to False. bbox (Bbox): Bounding box of the full geometry of the place. If the place is a single point, then the size of the bounding box is guessed according to the type of place. geometry (dict): Dictionary containing the full geometry of the place in the formats requested in the `geometry_output` parameter. """ return self._loop.run_until_complete( self._async_api.search(query, **params)) def search_address(self, amenity: Optional[str] = None, street: Optional[str] = None, city: Optional[str] = None, county: Optional[str] = None, state: Optional[str] = None, country: Optional[str] = None, postalcode: Optional[str] = None, **params: Any) -> SearchResults: """ Find an address using structured search. Parameters: amenity: Name of a POI. street: Street and optionally housenumber of the address. If the address does not have a street, then the place the housenumber references to. city: Postal city of the address. county: County equivalent of the address. Does not exist in all jurisdictions. state: State or province of the address. country: Country with its full name or its ISO 3166-1 alpha-2 country code. Do not use together with the country_code filter. postalcode: Post code or ZIP for the place. Other parameters: max_results (int): Maximum number of results to return. The actual number of results may be less. (Default: 10) min_rank (int): Lowest permissible rank for the result. For addressable places this is the minimum [address rank](../customize/Ranking.md#address-rank). For all other places the [search rank](../customize/Ranking.md#search-rank) is used. max_rank (int): Highest permissible rank for the result. See min_rank above. layers (enum): Defines the kind of data to take into account. See [layers section](Input-Parameter-Types.md#layers) for details. (Default: addresses and POIs) countries (list[str]): Restrict search to countries with the given ISO 3166-1 alpha-2 country code. An empty list (the default) disables this filter. Do not use, when the country parameter is used. excluded (list[int | str]): A list of internal Nominatim IDs or OSM IDs to exclude from the search. viewbox (Optional[Bbox]): Bounding box of an area to focus search on. bounded_viewbox (bool): Consider the bounding box given in `viewbox` as a filter and return only results within the bounding box. near (Optional[Point]): Focus search around the given point and return results ordered by distance to the given point. near_radius (Optional[float]): Restrict results to results within the given distance in degrees of `near` point. Ignored, when `near` is not set. categories (list[tuple]): Restrict search to places of the given categories. The category is the main OSM tag assigned to each place. An empty list (the default) disables this filter. geometry_output (enum): Add the full geometry of the place to the result. Multiple formats may be selected. Note that geometries can become quite large. (Default: none) geometry_simplification (float): Simplification factor to use on the geometries before returning them. The factor expresses the tolerance in degrees from which the geometry may differ. Topology is preserved. (Default: 0.0) address_details (bool): Add detailed information about the places that make up the address of the requested object. (Default: False) linked_places (bool): Add detailed information about the places that link to the result. (Default: False) parented_places (bool): Add detailed information about all places for which the requested object is a parent, i.e. all places for which the object provides the address details. Only POI places can have parents. (Default: False) keywords (bool): Add detailed information about the search terms used for this place. query_stats (QueryStatistics): When given collects statistics about the query execution. Returns: source_table (enum): Data source of the place. See below for possible values. category (tuple): A tuple of two strings with the primary OSM tag and value. centroid (Point): Point position of the place. place_id (Optional[int]): Internal ID of the place. This ID may differ for the same place between different installations. osm_object (Optional[tuple]): OSM type and ID of the place, if available. names (Optional[dict]): Dictionary of names of the place. Keys are usually the corresponding OSM tag keys. address (Optional[dict]): Dictionary of address parts directly attributed to the place. Keys are usually the corresponding OSM tag keys with the `addr:` prefix removed. extratags (Optional[dict]): Dictionary of additional attributes for the place. Usually OSM tag keys and values. housenumber (Optional[str]): House number of the place, normalised for lookup. To get the house number in its original spelling, use `address['housenumber']`. postcode (Optional[str]): Computed postcode for the place. To get directly attributed postcodes, use `address['postcode']` instead. wikipedia (Optional[str]): Reference to a wikipedia site for the place. The string has the format :. rank_address (int): [Address rank](../customize/Ranking.md#address-rank). rank_search (int): [Search rank](../customize/Ranking.md#search-rank). importance (Optional[float]): Relative importance of the place. This is a measure how likely the place will be searched for. country_code (Optional[str]): Country the feature is in as ISO 3166-1 alpha-2 country code. address_rows (Optional[AddressLines]): List of places that make up the computed address. `None` when `address_details` parameter was False. linked_rows (Optional[AddressLines]): List of places that link to the object. `None` when `linked_places` parameter was False. parented_rows (Optional[AddressLines]): List of direct children of the place. `None` when `parented_places` parameter was False. name_keywords (Optional[WordInfos]): List of search words for the name of the place. `None` when `keywords` parameter is set to False. address_keywords (Optional[WordInfos]): List of search word for the address of the place. `None` when `keywords` parameter is set to False. bbox (Bbox): Bounding box of the full geometry of the place. If the place is a single point, then the size of the bounding box is guessed according to the type of place. geometry (dict): Dictionary containing the full geometry of the place in the formats requested in the `geometry_output` parameter. """ return self._loop.run_until_complete( self._async_api.search_address(amenity, street, city, county, state, country, postalcode, **params)) def search_category(self, categories: List[Tuple[str, str]], near_query: Optional[str] = None, **params: Any) -> SearchResults: """ Find an object of a certain category near another place. The near place may either be given as an unstructured search query in itself or as a geographic area through the viewbox or near parameters. Parameters: categories: Restrict search to places of the given categories. The category is the main OSM tag assigned to each place. near_query: Optional free-text query to define the are to restrict search to. Other parameters: max_results (int): Maximum number of results to return. The actual number of results may be less. (Default: 10) min_rank (int): Lowest permissible rank for the result. For addressable places this is the minimum [address rank](../customize/Ranking.md#address-rank). For all other places the [search rank](../customize/Ranking.md#search-rank) is used. max_rank (int): Highest permissible rank for the result. See min_rank above. layers (enum): Defines the kind of data to take into account. See [layers section](Input-Parameter-Types.md#layers) for details. (Default: addresses and POIs) countries (list[str]): Restrict search to countries with the given ISO 3166-1 alpha-2 country code. An empty list (the default) disables this filter. excluded (list[int | str]): A list of internal Nominatim IDs or OSM IDs to exclude from the search. viewbox (Optional[Bbox]): Bounding box of an area to focus search on. bounded_viewbox (bool): Consider the bounding box given in `viewbox` as a filter and return only results within the bounding box. near (Optional[Point]): Focus search around the given point and return results ordered by distance to the given point. near_radius (Optional[float]): Restrict results to results within the given distance in degrees of `near` point. Ignored, when `near` is not set. geometry_output (enum): Add the full geometry of the place to the result. Multiple formats may be selected. Note that geometries can become quite large. (Default: none) geometry_simplification (float): Simplification factor to use on the geometries before returning them. The factor expresses the tolerance in degrees from which the geometry may differ. Topology is preserved. (Default: 0.0) address_details (bool): Add detailed information about the places that make up the address of the requested object. (Default: False) linked_places (bool): Add detailed information about the places that link to the result. (Default: False) parented_places (bool): Add detailed information about all places for which the requested object is a parent, i.e. all places for which the object provides the address details. Only POI places can have parents. (Default: False) keywords (bool): Add detailed information about the search terms used for this place. query_stats (QueryStatistics): When given collects statistics about the query execution. Returns: source_table (enum): Data source of the place. See below for possible values. category (tuple): A tuple of two strings with the primary OSM tag and value. centroid (Point): Point position of the place. place_id (Optional[int]): Internal ID of the place. This ID may differ for the same place between different installations. osm_object (Optional[tuple]): OSM type and ID of the place, if available. names (Optional[dict]): Dictionary of names of the place. Keys are usually the corresponding OSM tag keys. address (Optional[dict]): Dictionary of address parts directly attributed to the place. Keys are usually the corresponding OSM tag keys with the `addr:` prefix removed. extratags (Optional[dict]): Dictionary of additional attributes for the place. Usually OSM tag keys and values. housenumber (Optional[str]): House number of the place, normalised for lookup. To get the house number in its original spelling, use `address['housenumber']`. postcode (Optional[str]): Computed postcode for the place. To get directly attributed postcodes, use `address['postcode']` instead. wikipedia (Optional[str]): Reference to a wikipedia site for the place. The string has the format :. rank_address (int): [Address rank](../customize/Ranking.md#address-rank). rank_search (int): [Search rank](../customize/Ranking.md#search-rank). importance (Optional[float]): Relative importance of the place. This is a measure how likely the place will be searched for. country_code (Optional[str]): Country the feature is in as ISO 3166-1 alpha-2 country code. address_rows (Optional[AddressLines]): List of places that make up the computed address. `None` when `address_details` parameter was False. linked_rows (Optional[AddressLines]): List of places that link to the object. `None` when `linked_places` parameter was False. parented_rows (Optional[AddressLines]): List of direct children of the place. `None` when `parented_places` parameter was False. name_keywords (Optional[WordInfos]): List of search words for the name of the place. `None` when `keywords` parameter is set to False. address_keywords (Optional[WordInfos]): List of search word for the address of the place. `None` when `keywords` parameter is set to False. bbox (Bbox): Bounding box of the full geometry of the place. If the place is a single point, then the size of the bounding box is guessed according to the type of place. geometry (dict): Dictionary containing the full geometry of the place in the formats requested in the `geometry_output` parameter. """ return self._loop.run_until_complete( self._async_api.search_category(categories, near_query, **params)) ================================================ FILE: src/nominatim_api/errors.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Custom exception and error classes for Nominatim. """ class UsageError(Exception): """ An error raised because of bad user input. This error will usually not cause a stack trace to be printed unless debugging is enabled. """ ================================================ FILE: src/nominatim_api/localization.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Helper functions for localizing names of results. """ from typing import Mapping, List, Optional from .results import AddressLines, BaseResultT import re class Locales: """ Helper class for localization of names. It takes a list of language prefixes in their order of preferred usage and comma separated name keys (Configuration.OUTPUT_NAMES). """ def __init__(self, langs: Optional[List[str]] = None, names: str = 'name:XX,name') -> None: self.languages = langs or [] self.name_tags: List[str] = [] parts = names.split(',') if names else [] 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 def _add_tags(self, *tags: str) -> None: for tag in tags: self.name_tags.append(tag) self.name_tags.append(f"_place_{tag}") def _add_lang_tags(self, *tags: str) -> None: for tag in tags: for lang in self.languages: self.name_tags.append(f"{tag}:{lang}") self.name_tags.append(f"_place_{tag}:{lang}") def display_name(self, names: Optional[Mapping[str, str]]) -> str: """ Return the best matching name from a dictionary of names containing different name variants. If 'names' is null or empty, an empty string is returned. If no appropriate localization is found, the first name is returned. """ if not names: return '' if len(names) > 1: for tag in self.name_tags: if tag in names: return names[tag] # Nothing? Return any of the other names as a default. return next(iter(names.values())) @staticmethod def from_accept_languages(langstr: str, names: str = 'name:XX,name') -> 'Locales': """ Create a localization object from a language list in the format of HTTP accept-languages header. The functions tries to be forgiving of format errors by first splitting the string into comma-separated parts and then parsing each description separately. Badly formatted parts are then ignored. """ # split string into languages candidates = [] for desc in langstr.split(','): m = re.fullmatch(r'\s*([a-z_-]+)(?:;\s*q\s*=\s*([01](?:\.\d+)?))?\s*', desc, flags=re.I) if m: candidates.append((m[1], float(m[2] or 1.0))) # sort the results by the weight of each language (preserving order). candidates.sort(reverse=True, key=lambda e: e[1]) # If a language has a region variant, also add the language without # variant but only if it isn't already in the list to not mess up the weight. languages = [] for lid, _ in candidates: languages.append(lid) parts = lid.split('-', 1) if len(parts) > 1 and all(c[0] != parts[0] for c in candidates): languages.append(parts[0]) return Locales(languages, names) def localize(self, lines: AddressLines) -> None: """ Sets the local name of address parts according to the chosen locale. Only address parts that are marked as isaddress are localized. AddressLines should be modified in place. """ for line in lines: if line.isaddress and line.names: line.local_name = self.display_name(line.names) def localize_results(self, results: List[BaseResultT]) -> None: """ Set the local name of results according to the chosen locale. """ for result in results: result.locale_name = self.display_name(result.names) if result.address_rows: self.localize(result.address_rows) ================================================ FILE: src/nominatim_api/logging.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Functions for specialised logging with HTML output. """ from typing import Any, Iterator, Optional, List, Tuple, cast, Union, Mapping, Sequence from contextvars import ContextVar import datetime as dt import textwrap import io import re import html import sqlalchemy as sa from sqlalchemy.ext.asyncio import AsyncConnection try: from pygments import highlight from pygments.lexers import PythonLexer, PostgresLexer from pygments.formatters import HtmlFormatter CODE_HIGHLIGHT = True except ModuleNotFoundError: CODE_HIGHLIGHT = False def _debug_name(res: Any) -> str: if res.names: return cast(str, res.names.get('name', next(iter(res.names.values())))) return f"Hnr {res.housenumber}" if res.housenumber is not None else '[NONE]' class BaseLogger: """ Interface for logging function. The base implementation does nothing. Overwrite the functions in derived classes which implement logging functionality. """ def get_buffer(self) -> str: """ Return the current content of the log buffer. """ return '' def function(self, func: str, **kwargs: Any) -> None: """ Start a new debug chapter for the given function and its parameters. """ def section(self, heading: str) -> None: """ Start a new section with the given title. """ def comment(self, text: str) -> None: """ Add a simple comment to the debug output. """ def var_dump(self, heading: str, var: Any) -> None: """ Print the content of the variable to the debug output prefixed by the given heading. """ def table_dump(self, heading: str, rows: Iterator[Optional[List[Any]]]) -> None: """ Print the table generated by the generator function. """ def result_dump(self, heading: str, results: Iterator[Tuple[Any, Any]]) -> None: """ Print a list of search results generated by the generator function. """ def sql(self, conn: AsyncConnection, statement: 'sa.Executable', params: Union[Mapping[str, Any], Sequence[Mapping[str, Any]], None]) -> None: """ Print the SQL for the given statement. """ def format_sql(self, conn: AsyncConnection, statement: 'sa.Executable', extra_params: Union[Mapping[str, Any], Sequence[Mapping[str, Any]], None] ) -> str: """ Return the compiled version of the statement. """ compiled = cast('sa.ClauseElement', statement).compile(conn.sync_engine) params = dict(compiled.params) if isinstance(extra_params, Mapping): for k, v in extra_params.items(): if hasattr(v, 'to_wkt'): params[k] = v.to_wkt() elif isinstance(v, (int, float)): params[k] = v else: params[k] = str(v) elif isinstance(extra_params, Sequence) and extra_params: for k in extra_params[0]: params[k] = f':{k}' sqlstr = str(compiled) if conn.dialect.name == 'postgresql': if sa.__version__.startswith('1'): try: sqlstr = re.sub(r'__\[POSTCOMPILE_[^]]*\]', '%s', sqlstr) return sqlstr % tuple((repr(params.get(name, None)) for name in compiled.positiontup)) # type: ignore except TypeError: return sqlstr sqlstr = re.sub(r'__\[POSTCOMPILE_([^]]*)\]', r'%(\1)s', sqlstr) return sqlstr % params assert conn.dialect.name == 'sqlite' # params in positional order pparams = (repr(params.get(name, None)) for name in compiled.positiontup) # type: ignore sqlstr = re.sub(r'__\[POSTCOMPILE_([^]]*)\]', '?', sqlstr) sqlstr = re.sub(r"\?", lambda m: next(pparams), sqlstr) return sqlstr class HTMLLogger(BaseLogger): """ Logger that formats messages in HTML. """ def __init__(self) -> None: self.buffer = io.StringIO() def _timestamp(self) -> None: self._write(f'

[{dt.datetime.now()}]

') def get_buffer(self) -> str: return HTML_HEADER + self.buffer.getvalue() + HTML_FOOTER def function(self, func: str, **kwargs: Any) -> None: self._timestamp() self._write(f"

Debug output for {func}()

\n

Parameters:

") for name, value in kwargs.items(): self._write(f'
{name}
{self._python_var(value)}
') self._write('

') def section(self, heading: str) -> None: self._timestamp() self._write(f"

{heading}

") def comment(self, text: str) -> None: self._timestamp() self._write(f"

{text}

") def var_dump(self, heading: str, var: Any) -> None: self._timestamp() if callable(var): var = var() self._write(f'
{heading}
{self._python_var(var)}') def table_dump(self, heading: str, rows: Iterator[Optional[List[Any]]]) -> None: self._timestamp() head = next(rows) assert head self._write(f'') for cell in head: self._write(f'') self._write('') for row in rows: if row is not None: self._write('') for cell in row: self._write(f'') self._write('') self._write('
{heading}
{cell}
{cell}
') def result_dump(self, heading: str, results: Iterator[Tuple[Any, Any]]) -> None: """ Print a list of search results generated by the generator function. """ self._timestamp() def format_osm(osm_object: Optional[Tuple[str, int]]) -> str: if not osm_object: return '-' t, i = osm_object if t == 'N': fullt = 'node' elif t == 'W': fullt = 'way' elif t == 'R': fullt = 'relation' else: return f'{t}{i}' return f'{t}{i}' self._write(f'
{heading}

') total = 0 for rank, res in results: self._write(f'
[{rank:.3f}]
{res.source_table.name}(') self._write(f"{_debug_name(res)}, type=({','.join(res.category)}), ") self._write(f"rank={res.rank_address}, ") self._write(f"osm={format_osm(res.osm_object)}, ") self._write(f'cc={res.country_code}, ') self._write(f'importance={res.importance or float("nan"):.5f})
') total += 1 self._write(f'
TOTAL: {total}

') def sql(self, conn: AsyncConnection, statement: 'sa.Executable', params: Union[Mapping[str, Any], Sequence[Mapping[str, Any]], None]) -> None: self._timestamp() sqlstr = self.format_sql(conn, statement, params) if CODE_HIGHLIGHT: sqlstr = highlight(sqlstr, PostgresLexer(), HtmlFormatter(nowrap=True, lineseparator='
')) self._write(f'
{sqlstr}
') else: self._write(f'{html.escape(sqlstr)}') def _python_var(self, var: Any) -> str: if CODE_HIGHLIGHT: fmt = highlight(str(var), PythonLexer(), HtmlFormatter(nowrap=True)) return f'
{fmt}
' return f'{html.escape(str(var))}' def _write(self, text: str) -> None: """ Add the raw text to the debug output. """ self.buffer.write(text) class TextLogger(BaseLogger): """ Logger creating output suitable for the console. """ def __init__(self) -> None: self.buffer = io.StringIO() def _timestamp(self) -> None: self._write(f'[{dt.datetime.now()}]\n') def get_buffer(self) -> str: return self.buffer.getvalue() def function(self, func: str, **kwargs: Any) -> None: self._write(f"#### Debug output for {func}()\n\nParameters:\n") for name, value in kwargs.items(): self._write(f' {name}: {self._python_var(value)}\n') self._write('\n') def section(self, heading: str) -> None: self._timestamp() self._write(f"\n# {heading}\n\n") def comment(self, text: str) -> None: self._write(f"{text}\n") def var_dump(self, heading: str, var: Any) -> None: if callable(var): var = var() self._write(f'{heading}:\n {self._python_var(var)}\n\n') def table_dump(self, heading: str, rows: Iterator[Optional[List[Any]]]) -> None: self._write(f'{heading}:\n') data = [list(map(self._python_var, row)) if row else None for row in rows] assert data[0] is not None num_cols = len(data[0]) maxlens = [max(len(d[i]) for d in data if d) for i in range(num_cols)] tablewidth = sum(maxlens) + 3 * num_cols + 1 row_format = '| ' + ' | '.join(f'{{:<{ln}}}' for ln in maxlens) + ' |\n' self._write('-'*tablewidth + '\n') self._write(row_format.format(*data[0])) self._write('-'*tablewidth + '\n') for row in data[1:]: if row: self._write(row_format.format(*row)) else: self._write('-'*tablewidth + '\n') if data[-1]: self._write('-'*tablewidth + '\n') def result_dump(self, heading: str, results: Iterator[Tuple[Any, Any]]) -> None: self._timestamp() self._write(f'{heading}:\n') total = 0 for rank, res in results: self._write(f'[{rank:.3f}] {res.source_table.name}(') self._write(f"{_debug_name(res)}, type=({','.join(res.category)}), ") self._write(f"rank={res.rank_address}, ") self._write(f"osm={''.join(map(str, res.osm_object or []))}, ") self._write(f'cc={res.country_code}, ') self._write(f'importance={res.importance or float("NaN"):.5f})\n') total += 1 self._write(f'TOTAL: {total}\n\n') def sql(self, conn: AsyncConnection, statement: 'sa.Executable', params: Union[Mapping[str, Any], Sequence[Mapping[str, Any]], None]) -> None: self._timestamp() sqlstr = '\n| '.join(textwrap.wrap(self.format_sql(conn, statement, params), width=78)) self._write(f"| {sqlstr}\n\n") def _python_var(self, var: Any) -> str: return str(var) def _write(self, text: str) -> None: self.buffer.write(text) logger: ContextVar[BaseLogger] = ContextVar('logger', default=BaseLogger()) def set_log_output(fmt: str) -> None: """ Enable collecting debug information. """ if fmt == 'html': logger.set(HTMLLogger()) elif fmt == 'text': logger.set(TextLogger()) else: logger.set(BaseLogger()) def log() -> BaseLogger: """ Return the logger for the current context. """ return logger.get() def get_and_disable() -> str: """ Return the current content of the debug buffer and disable logging. """ buf = logger.get().get_buffer() logger.set(BaseLogger()) return buf HTML_HEADER: str = """ Nominatim - Debug """ HTML_FOOTER: str = "" ================================================ FILE: src/nominatim_api/lookup.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of place lookup by ID (doing many places at once). """ from typing import Optional, Callable, Type, Iterable, Tuple, Union from dataclasses import dataclass import datetime as dt import sqlalchemy as sa from .typing import SaColumn, SaRow, SaSelect from .connection import SearchConnection from .logging import log from . import types as ntyp from . import results as nres RowFunc = Callable[[SaRow, Type[nres.BaseResultT]], nres.BaseResultT] GEOMETRY_TYPE_MAP = { 'POINT': 'ST_Point', 'MULTIPOINT': 'ST_MultiPoint', 'LINESTRING': 'ST_LineString', 'MULTILINESTRING': 'ST_MultiLineString', 'POLYGON': 'ST_Polygon', 'MULTIPOLYGON': 'ST_MultiPolygon', 'GEOMETRYCOLLECTION': 'ST_GeometryCollection' } @dataclass class LookupTuple: """ Data class saving the SQL result for a single lookup. """ pid: ntyp.PlaceRef result: Optional[nres.SearchResult] = None class LookupCollector: """ Result collector for the simple lookup. Allows for lookup of multiple places simultaneously. """ def __init__(self, places: Iterable[ntyp.PlaceRef], details: ntyp.LookupDetails) -> None: self.details = details self.lookups = [LookupTuple(p) for p in places] def get_results(self) -> nres.SearchResults: """ Return the list of results available. """ return nres.SearchResults(p.result for p in self.lookups if p.result is not None) async def add_rows_from_sql(self, conn: SearchConnection, sql: SaSelect, col: SaColumn, row_func: RowFunc[nres.SearchResult]) -> bool: if self.details.geometry_output: if self.details.geometry_simplification > 0.0: col = sa.func.ST_SimplifyPreserveTopology( col, self.details.geometry_simplification) if self.details.geometry_output & ntyp.GeometryFormat.GEOJSON: sql = sql.add_columns(sa.func.ST_AsGeoJSON(col, 7).label('geometry_geojson')) if self.details.geometry_output & ntyp.GeometryFormat.TEXT: sql = sql.add_columns(sa.func.ST_AsText(col).label('geometry_text')) if self.details.geometry_output & ntyp.GeometryFormat.KML: sql = sql.add_columns(sa.func.ST_AsKML(col, 7).label('geometry_kml')) if self.details.geometry_output & ntyp.GeometryFormat.SVG: sql = sql.add_columns(sa.func.ST_AsSVG(col, 0, 7).label('geometry_svg')) for row in await conn.execute(sql): result = row_func(row, nres.SearchResult) if hasattr(row, 'bbox'): result.bbox = ntyp.Bbox.from_wkb(row.bbox) if self.lookups[row._idx].result is None: self.lookups[row._idx].result = result return all(p.result is not None for p in self.lookups) def enumerate_free_place_ids(self) -> Iterable[Tuple[int, ntyp.PlaceID]]: return ((i, p.pid) for i, p in enumerate(self.lookups) if p.result is None and isinstance(p.pid, ntyp.PlaceID)) def enumerate_free_osm_ids(self) -> Iterable[Tuple[int, ntyp.OsmID]]: return ((i, p.pid) for i, p in enumerate(self.lookups) if p.result is None and isinstance(p.pid, ntyp.OsmID)) class DetailedCollector: """ Result collector for detailed lookup. Only one place at the time may be looked up. """ def __init__(self, place: ntyp.PlaceRef, with_geometry: bool) -> None: self.with_geometry = with_geometry self.place = place self.result: Optional[nres.DetailedResult] = None async def add_rows_from_sql(self, conn: SearchConnection, sql: SaSelect, col: SaColumn, row_func: RowFunc[nres.DetailedResult]) -> bool: if self.with_geometry: sql = sql.add_columns( sa.func.ST_AsGeoJSON( sa.case((sa.func.ST_NPoints(col) > 5000, sa.func.ST_SimplifyPreserveTopology(col, 0.0001)), else_=col), 7).label('geometry_geojson')) else: sql = sql.add_columns(sa.func.ST_GeometryType(col).label('geometry_type')) for row in await conn.execute(sql): self.result = row_func(row, nres.DetailedResult) # add missing details if 'type' in self.result.geometry: self.result.geometry['type'] = \ GEOMETRY_TYPE_MAP.get(self.result.geometry['type'], self.result.geometry['type']) indexed_date = getattr(row, 'indexed_date', None) if indexed_date is not None: self.result.indexed_date = indexed_date.replace(tzinfo=dt.timezone.utc) return True # Nothing found. return False def enumerate_free_place_ids(self) -> Iterable[Tuple[int, ntyp.PlaceID]]: if self.result is None and isinstance(self.place, ntyp.PlaceID): return [(0, self.place)] return [] def enumerate_free_osm_ids(self) -> Iterable[Tuple[int, ntyp.OsmID]]: if self.result is None and isinstance(self.place, ntyp.OsmID): return [(0, self.place)] return [] Collector = Union[LookupCollector, DetailedCollector] async def get_detailed_place(conn: SearchConnection, place: ntyp.PlaceRef, details: ntyp.LookupDetails) -> Optional[nres.DetailedResult]: """ Retrieve a place with additional details from the database. """ log().function('get_detailed_place', place=place, details=details) if details.geometry_output and details.geometry_output != ntyp.GeometryFormat.GEOJSON: raise ValueError("lookup only supports geojosn polygon output.") collector = DetailedCollector(place, bool(details.geometry_output & ntyp.GeometryFormat.GEOJSON)) for func in (find_in_placex, find_in_osmline, find_in_postcode, find_in_tiger): if await func(conn, collector): break if collector.result is not None: await nres.add_result_details(conn, [collector.result], details) return collector.result async def get_places(conn: SearchConnection, places: Iterable[ntyp.PlaceRef], details: ntyp.LookupDetails) -> nres.SearchResults: """ Retrieve a list of places as simple search results from the database. """ log().function('get_places', places=places, details=details) collector = LookupCollector(places, details) for func in (find_in_placex, find_in_osmline, find_in_postcode, find_in_tiger): if await func(conn, collector): break results = collector.get_results() await nres.add_result_details(conn, results, details) return results async def find_in_placex(conn: SearchConnection, collector: Collector) -> bool: """ Search for the given places in the main placex table. """ log().section("Find in placex table") t = conn.t.placex sql = sa.select(t.c.place_id, t.c.osm_type, t.c.osm_id, t.c.name, t.c.class_, t.c.type, t.c.admin_level, t.c.address, t.c.extratags, t.c.housenumber, t.c.postcode, t.c.country_code, t.c.importance, t.c.wikipedia, t.c.indexed_date, t.c.parent_place_id, t.c.rank_address, t.c.rank_search, t.c.linked_place_id, t.c.geometry.ST_Expand(0).label('bbox'), t.c.centroid) for osm_type in ('N', 'W', 'R'): osm_ids = [{'i': i, 'oi': p.osm_id, 'oc': p.osm_class or ''} for i, p in collector.enumerate_free_osm_ids() if p.osm_type == osm_type] if osm_ids: oid_tab = sa.func.JsonArrayEach(sa.type_coerce(osm_ids, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) psql = sql.add_columns(oid_tab.c.value['i'].as_integer().label('_idx'))\ .where(t.c.osm_type == osm_type)\ .where(t.c.osm_id == oid_tab.c.value['oi'].as_string().cast(sa.BigInteger))\ .where(sa.or_(oid_tab.c.value['oc'].as_string() == '', oid_tab.c.value['oc'].as_string() == t.c.class_))\ .order_by(t.c.class_) if await collector.add_rows_from_sql(conn, psql, t.c.geometry, nres.create_from_placex_row): return True place_ids = [{'i': i, 'id': p.place_id} for i, p in collector.enumerate_free_place_ids()] if place_ids: pid_tab = sa.func.JsonArrayEach(sa.type_coerce(place_ids, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) psql = sql.add_columns(pid_tab.c.value['i'].as_integer().label('_idx'))\ .where(t.c.place_id == pid_tab.c.value['id'].as_string().cast(sa.BigInteger)) return await collector.add_rows_from_sql(conn, psql, t.c.geometry, nres.create_from_placex_row) return False async def find_in_osmline(conn: SearchConnection, collector: Collector) -> bool: """ Search for the given places in the table for address interpolations. Return true when all places have been resolved. """ log().section("Find in interpolation table") t = conn.t.osmline sql = sa.select(t.c.place_id, t.c.osm_id, t.c.parent_place_id, t.c.indexed_date, t.c.startnumber, t.c.endnumber, t.c.step, t.c.address, t.c.postcode, t.c.country_code, t.c.linegeo.ST_Centroid().label('centroid')) osm_ids = [{'i': i, 'oi': p.osm_id, 'oc': p.class_as_housenumber()} for i, p in collector.enumerate_free_osm_ids() if p.osm_type == 'W'] if osm_ids: oid_tab = sa.func.JsonArrayEach(sa.type_coerce(osm_ids, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) psql = sql.add_columns(oid_tab.c.value['i'].as_integer().label('_idx'))\ .where(t.c.osm_id == oid_tab.c.value['oi'].as_string().cast(sa.BigInteger))\ .order_by(sa.func.greatest(0, oid_tab.c.value['oc'].as_integer() - t.c.endnumber, t.c.startnumber - oid_tab.c.value['oc'].as_integer())) if await collector.add_rows_from_sql(conn, psql, t.c.linegeo, nres.create_from_osmline_row): return True place_ids = [{'i': i, 'id': p.place_id} for i, p in collector.enumerate_free_place_ids()] if place_ids: pid_tab = sa.func.JsonArrayEach(sa.type_coerce(place_ids, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) psql = sql.add_columns(pid_tab.c.value['i'].label('_idx'))\ .where(t.c.place_id == pid_tab.c.value['id'].as_string().cast(sa.BigInteger)) return await collector.add_rows_from_sql(conn, psql, t.c.linegeo, nres.create_from_osmline_row) return False async def find_in_postcode(conn: SearchConnection, collector: Collector) -> bool: """ Search for the given places in the postcode table. Return true when all places have been resolved. """ log().section("Find in postcode table") place_ids = [{'i': i, 'id': p.place_id} for i, p in collector.enumerate_free_place_ids()] if place_ids: pid_tab = sa.func.JsonArrayEach(sa.type_coerce(place_ids, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) t = conn.t.postcode sql = sa.select(pid_tab.c.value['i'].as_integer().label('_idx'), t.c.osm_id, t.c.place_id, t.c.parent_place_id, t.c.rank_search, t.c.indexed_date, t.c.postcode, t.c.country_code, t.c.centroid)\ .where(t.c.place_id == pid_tab.c.value['id'].as_string().cast(sa.BigInteger)) if await collector.add_rows_from_sql(conn, sql, t.c.geometry, nres.create_from_postcode_row): return True osm_ids = [{'i': i, 'oi': p.osm_id} for i, p in collector.enumerate_free_osm_ids() if p.osm_type == 'R'] if osm_ids: pid_tab = sa.func.JsonArrayEach(sa.type_coerce(osm_ids, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) t = conn.t.postcode sql = sa.select(pid_tab.c.value['i'].as_integer().label('_idx'), t.c.osm_id, t.c.place_id, t.c.parent_place_id, t.c.rank_search, t.c.indexed_date, t.c.postcode, t.c.country_code, t.c.centroid)\ .where(t.c.osm_id == pid_tab.c.value['oi'].as_string().cast(sa.BigInteger)) return await collector.add_rows_from_sql(conn, sql, t.c.geometry, nres.create_from_postcode_row) return False async def find_in_tiger(conn: SearchConnection, collector: Collector) -> bool: """ Search for the given places in the TIGER address table. Return true when all places have been resolved. """ log().section("Find in tiger table") place_ids = [{'i': i, 'id': p.place_id} for i, p in collector.enumerate_free_place_ids()] if place_ids: pid_tab = sa.func.JsonArrayEach(sa.type_coerce(place_ids, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) t = conn.t.tiger parent = conn.t.placex sql = sa.select(pid_tab.c.value['i'].as_integer().label('_idx'), t.c.place_id, t.c.parent_place_id, parent.c.osm_type, parent.c.osm_id, t.c.startnumber, t.c.endnumber, t.c.step, t.c.postcode, t.c.linegeo.ST_Centroid().label('centroid'))\ .join(parent, t.c.parent_place_id == parent.c.place_id, isouter=True)\ .where(t.c.place_id == pid_tab.c.value['id'].as_string().cast(sa.BigInteger)) return await collector.add_rows_from_sql(conn, sql, t.c.linegeo, nres.create_from_tiger_row) return False ================================================ FILE: src/nominatim_api/py.typed ================================================ ================================================ FILE: src/nominatim_api/query_preprocessing/__init__.py ================================================ ================================================ FILE: src/nominatim_api/query_preprocessing/base.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Common data types and protocols for preprocessing. """ from typing import List, Callable from ..typing import Protocol from ..search import query as qmod from .config import QueryConfig QueryProcessingFunc = Callable[[List[qmod.Phrase]], List[qmod.Phrase]] class QueryHandler(Protocol): """ Protocol for query modules. """ def create(self, config: QueryConfig) -> QueryProcessingFunc: """ Create a function for sanitizing a place. Arguments: config: A dictionary with the additional configuration options specified in the tokenizer configuration normalizer: A instance to transliterate text Return: The result is a list modified by the preprocessor. """ pass ================================================ FILE: src/nominatim_api/query_preprocessing/config.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Configuration for Sanitizers. """ from typing import Any, TYPE_CHECKING from collections import UserDict # working around missing generics in Python < 3.8 # See https://github.com/python/typing/issues/60#issuecomment-869757075 if TYPE_CHECKING: _BaseUserDict = UserDict[str, Any] else: _BaseUserDict = UserDict class QueryConfig(_BaseUserDict): """ The `QueryConfig` class is a read-only dictionary with configuration options for the preprocessor. In addition to the usual dictionary functions, the class provides accessors to standard preprocessor options that are used by many of the preprocessors. """ def set_normalizer(self, normalizer: Any) -> 'QueryConfig': """ Set the normalizer function to be used. """ self['_normalizer'] = normalizer return self ================================================ FILE: src/nominatim_api/query_preprocessing/normalize.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Normalize query text using the same ICU normalization rules that are applied during import. If a phrase becomes empty because the normalization removes all terms, then the phrase is deleted. This preprocessor does not come with any extra information. Instead it will use the configuration from the `normalization` section. """ from typing import cast from .config import QueryConfig from .base import QueryProcessingFunc from ..search.query import Phrase def create(config: QueryConfig) -> QueryProcessingFunc: normalizer = config.get('_normalizer') if not normalizer: return lambda p: p return lambda phrases: list( filter(lambda p: p.text, (Phrase(p.ptype, cast(str, normalizer.transliterate(p.text)).strip('-: ')) for p in phrases))) ================================================ FILE: src/nominatim_api/query_preprocessing/regex_replace.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ This preprocessor replaces values in a given input based on pre-defined regex rules. Arguments: pattern: Regex pattern to be applied on the input replace: The string that it is to be replaced with """ from typing import List import re from .config import QueryConfig from .base import QueryProcessingFunc from ..search.query import Phrase class _GenericPreprocessing: """Perform replacements to input phrases using custom regex patterns.""" def __init__(self, config: QueryConfig) -> None: """Initialise the _GenericPreprocessing class with patterns from the ICU config file.""" self.config = config match_patterns = self.config.get('replacements', 'Key not found') self.compiled_patterns = [ (re.compile(item['pattern']), item['replace']) for item in match_patterns ] def split_phrase(self, phrase: Phrase) -> Phrase: """This function performs replacements on the given text using regex patterns.""" for item in self.compiled_patterns: phrase.text = item[0].sub(item[1], phrase.text) return phrase def __call__(self, phrases: List[Phrase]) -> List[Phrase]: """ Return the final Phrase list. Returns an empty list if there is nothing left after split_phrase. """ result = [p for p in map(self.split_phrase, phrases) if p.text.strip()] return result def create(config: QueryConfig) -> QueryProcessingFunc: """ Create a function for generic preprocessing.""" return _GenericPreprocessing(config) ================================================ FILE: src/nominatim_api/query_preprocessing/split_japanese_phrases.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ This file divides Japanese addresses into three categories: prefecture, municipality, and other. The division is not strict but simple using these keywords. """ from typing import List import re from .config import QueryConfig from .base import QueryProcessingFunc from ..search.query import Phrase MATCH_PATTERNS = [ r''' (...??[都都道府県縣]) # [group1] prefecture (.+?[市区區町村]) # [group2] municipalities (city/wards/towns/villages) (.+) # [group3] other words ''', r''' (...??[都都道府県縣]) # [group1] prefecture (.+) # [group3] other words ''', r''' (.+?[市区區町村]) # [group2] municipalities (city/wards/towns/villages) (.+) # [group3] other words ''' ] class _JapanesePreprocessing: def __init__(self, config: QueryConfig) -> None: self.config = config def split_phrase(self, phrase: Phrase) -> Phrase: """ This function performs a division on the given text using a regular expression. """ for pattern in MATCH_PATTERNS: result = re.match(pattern, phrase.text, re.VERBOSE) if result is not None: return Phrase(phrase.ptype, ':'.join(result.groups())) return phrase def __call__(self, phrases: List[Phrase]) -> List[Phrase]: """Split a Japanese address using japanese_tokenizer. """ return [self.split_phrase(p) for p in phrases] def create(config: QueryConfig) -> QueryProcessingFunc: """ Create a function of japanese preprocessing. """ return _JapanesePreprocessing(config) ================================================ FILE: src/nominatim_api/result_formatting.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Helper classes and functions for formatting results into API responses. """ from typing import Type, TypeVar, Dict, List, Callable, Any, Mapping, Optional, cast from collections import defaultdict from pathlib import Path import importlib.util from .server.content_types import CONTENT_JSON T = TypeVar('T') FormatFunc = Callable[[T, Mapping[str, Any]], str] ErrorFormatFunc = Callable[[str, str, int], str] class FormatDispatcher: """ Container for formatting functions for results. Functions can conveniently be added by using decorated functions. """ def __init__(self, content_types: Optional[Mapping[str, str]] = None) -> None: self.error_handler: ErrorFormatFunc = lambda ct, msg, status: f"ERROR {status}: {msg}" self.content_types: Dict[str, str] = {} if content_types: self.content_types.update(content_types) self.format_functions: Dict[Type[Any], Dict[str, FormatFunc[Any]]] = defaultdict(dict) def format_func(self, result_class: Type[T], fmt: str) -> Callable[[FormatFunc[T]], FormatFunc[T]]: """ Decorator for a function that formats a given type of result into the selected format. """ def decorator(func: FormatFunc[T]) -> FormatFunc[T]: self.format_functions[result_class][fmt] = func return func return decorator def error_format_func(self, func: ErrorFormatFunc) -> ErrorFormatFunc: """ Decorator for a function that formats error messages. There is only one error formatter per dispatcher. Using the decorator repeatedly will overwrite previous functions. """ self.error_handler = func return func def list_formats(self, result_type: Type[Any]) -> List[str]: """ Return a list of formats supported by this formatter. """ return list(self.format_functions[result_type].keys()) def supports_format(self, result_type: Type[Any], fmt: str) -> bool: """ Check if the given format is supported by this formatter. """ return fmt in self.format_functions[result_type] def format_result(self, result: Any, fmt: str, options: Mapping[str, Any]) -> str: """ Convert the given result into a string using the given format. The format is expected to be in the list returned by `list_formats()`. """ return self.format_functions[type(result)][fmt](result, options) def format_error(self, content_type: str, msg: str, status: int) -> str: """ Convert the given error message into a response string taking the requested content_type into account. Change the format using the error_format_func decorator. """ return self.error_handler(content_type, msg, status) def set_content_type(self, fmt: str, content_type: str) -> None: """ Set the content type for the given format. This is the string that will be returned in the Content-Type header of the HTML response, when the given format is chosen. """ self.content_types[fmt] = content_type def get_content_type(self, fmt: str) -> str: """ Return the content type for the given format. If no explicit content type has been defined, then JSON format is assumed. """ return self.content_types.get(fmt, CONTENT_JSON) def load_format_dispatcher(api_name: str, project_dir: Optional[Path]) -> FormatDispatcher: """ Load the dispatcher for the given API. The function first tries to find a module api//format.py in the project directory. This file must export a single variable `dispatcher`. If the function does not exist, the default formatter is loaded. """ if project_dir is not None: priv_module = project_dir / 'api' / api_name / 'format.py' if priv_module.is_file(): spec = importlib.util.spec_from_file_location(f'api.{api_name},format', str(priv_module)) if spec: module = importlib.util.module_from_spec(spec) # Do not add to global modules because there is no standard # module name that Python can resolve. assert spec.loader is not None spec.loader.exec_module(module) return cast(FormatDispatcher, module.dispatch) return cast(FormatDispatcher, importlib.import_module(f'nominatim_api.{api_name}.format').dispatch) ================================================ FILE: src/nominatim_api/results.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Dataclasses for search results and helper functions to fill them. Data classes are part of the public API while the functions are for internal use only. That's why they are implemented as free-standing functions instead of member functions. """ from typing import ( Optional, Tuple, Dict, Sequence, TypeVar, Type, List, cast, Callable ) import enum import dataclasses import datetime as dt import sqlalchemy as sa from .typing import SaSelect, SaRow from .sql.sqlalchemy_types import Geometry from .types import Point, Bbox, LookupDetails, EntranceDetails from .connection import SearchConnection from .logging import log # This file defines complex result data classes. def _mingle_name_tags(names: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]: """ Mix-in names from linked places, so that they show up as standard names where necessary. """ if not names: return None out = {} for k, v in names.items(): if k.startswith('_place_'): outkey = k[7:] out[k if outkey in names else outkey] = v else: out[k] = v return out class SourceTable(enum.Enum): """ The `SourceTable` type lists the possible sources a result can have. """ PLACEX = 1 """ The placex table is the main source for result usually containing OSM data. """ OSMLINE = 2 """ The osmline table contains address interpolations from OSM data. Interpolation addresses are always approximate. The OSM id in the result refers to the OSM way with the interpolation line object. """ TIGER = 3 """ TIGER address data contains US addresses imported on the side, see [Installing TIGER data](../customize/Tiger.md). TIGER address are also interpolations. The addresses always refer to a street from OSM data. The OSM id in the result refers to that street. """ POSTCODE = 4 """ The postcode table contains artificial centroids for postcodes, computed from the postcodes available with address points. Results are always approximate. """ COUNTRY = 5 """ The country table provides a fallback, when country data is missing in the OSM data. """ @dataclasses.dataclass class AddressLine: """ The `AddressLine` may contain the following fields about a related place and its function as an address object. Most fields are optional. Their presence depends on the kind and function of the address part. """ category: Tuple[str, str] """ Main category of the place, described by a key-value pair. """ names: Dict[str, str] """ All available names for the place including references, alternative names and translations. """ fromarea: bool """ If true, then the exact area of the place is known. Without area information, Nominatim has to make an educated guess if an address belongs to one place or another. """ isaddress: bool """ If true, this place should be considered for the final address display. Nominatim will sometimes include more than one candidate for the address in the list when it cannot reliably determine where the place belongs. It will consider names of all candidates when searching but when displaying the result, only the most likely candidate should be shown. """ rank_address: int """ [Address rank](../customize/Ranking.md#address-rank) of the place. """ distance: float """ Distance in degrees between the result place and this address part. """ place_id: Optional[int] = None """ Internal ID of the place. """ osm_object: Optional[Tuple[str, int]] = None """ OSM type and ID of the place, if such an object exists. """ extratags: Optional[Dict[str, str]] = None """ Any extra information available about the place. This is a dictionary that usually contains OSM tag key-value pairs. """ admin_level: Optional[int] = None """ The administrative level of a boundary as tagged in the input data. This field is only meaningful for places of the category (boundary, administrative). """ local_name: Optional[str] = None """ Place holder for localization of this address part. See [Localization](Result-Handling.md#localization) below. """ @property def display_name(self) -> Optional[str]: """ Dynamically compute the display name for the Address Line component """ if self.local_name: return self.local_name elif 'name' in self.names: return self.names['name'] elif self.names: return next(iter(self.names.values()), None) return None class AddressLines(List[AddressLine]): """ A wrapper around a list of AddressLine objects.""" @dataclasses.dataclass class WordInfo: """ Each entry in the list of search terms contains the following detailed information. """ word_id: int """ Internal identifier for the word. """ word_token: str """ Normalised and transliterated form of the word. This form is used for searching. """ word: Optional[str] = None """ Untransliterated form, if available. """ WordInfos = Sequence[WordInfo] @dataclasses.dataclass class BaseResult: """ Data class collecting information common to all types of search results. """ source_table: SourceTable category: Tuple[str, str] centroid: Point place_id: Optional[int] = None osm_object: Optional[Tuple[str, int]] = None parent_place_id: Optional[int] = None linked_place_id: Optional[int] = None admin_level: int = 15 locale_name: Optional[str] = None names: Optional[Dict[str, str]] = None address: Optional[Dict[str, str]] = None extratags: Optional[Dict[str, str]] = None housenumber: Optional[str] = None postcode: Optional[str] = None wikipedia: Optional[str] = None rank_address: int = 30 rank_search: int = 30 importance: Optional[float] = None country_code: Optional[str] = None address_rows: Optional[AddressLines] = None linked_rows: Optional[AddressLines] = None parented_rows: Optional[AddressLines] = None name_keywords: Optional[WordInfos] = None address_keywords: Optional[WordInfos] = None entrances: Optional[List[EntranceDetails]] = None geometry: Dict[str, str] = dataclasses.field(default_factory=dict) @property def lat(self) -> float: """ Get the latitude (or y) of the center point of the place. """ return self.centroid[1] @property def lon(self) -> float: """ Get the longitude (or x) of the center point of the place. """ return self.centroid[0] @property def display_name(self) -> Optional[str]: """ Dynamically compute the display name for the result place and, if available, its address information.. """ if self.address_rows: # if this is true we need additional processing label_parts: List[str] = [] for line in self.address_rows: # assume locale_name is set by external formatter if line.isaddress and line.names: address_name = line.display_name if address_name and (not label_parts or label_parts[-1] != address_name): label_parts.append(address_name) if label_parts: return ', '.join(label_parts) # Now adding additional information for reranking if self.locale_name: return self.locale_name elif self.names and 'name' in self.names: return self.names['name'] elif self.names: return next(iter(self.names.values())) elif self.housenumber: return self.housenumber return None def calculated_importance(self) -> float: """ Get a valid importance value. This is either the stored importance of the value or an artificial value computed from the place's search rank. """ return self.importance or (0.40001 - (self.rank_search/75.0)) BaseResultT = TypeVar('BaseResultT', bound=BaseResult) @dataclasses.dataclass class DetailedResult(BaseResult): """ A search result with more internal information from the database added. """ indexed_date: Optional[dt.datetime] = None @dataclasses.dataclass class ReverseResult(BaseResult): """ A search result for reverse geocoding. """ distance: Optional[float] = None bbox: Optional[Bbox] = None class ReverseResults(List[ReverseResult]): """ Sequence of reverse lookup results ordered by distance. May be empty when no result was found. """ @dataclasses.dataclass class SearchResult(BaseResult): """ A search result for forward geocoding. """ bbox: Optional[Bbox] = None accuracy: float = 0.0 @property def ranking(self) -> float: """ Return the ranking, a combined measure of accuracy and importance. """ return (self.accuracy if self.accuracy is not None else 1) \ - self.calculated_importance() class SearchResults(List[SearchResult]): """ Sequence of forward lookup results ordered by relevance. May be empty when no result was found. """ def _filter_geometries(row: SaRow) -> Dict[str, str]: return {k[9:]: v for k, v in row._mapping.items() if k.startswith('geometry_')} def create_from_placex_row(row: SaRow, class_type: Type[BaseResultT]) -> BaseResultT: """ Construct a new result and add the data from the result row from the placex table. 'class_type' defines the type of result to return. Returns None if the row is None. """ return class_type(source_table=SourceTable.PLACEX, place_id=row.place_id, osm_object=(row.osm_type, row.osm_id), category=(row.class_, row.type), parent_place_id=row.parent_place_id, linked_place_id=getattr(row, 'linked_place_id', None), admin_level=getattr(row, 'admin_level', 15), names=_mingle_name_tags(row.name), address=row.address, extratags=row.extratags, housenumber=row.housenumber, postcode=row.postcode, wikipedia=row.wikipedia, rank_address=row.rank_address, rank_search=row.rank_search, importance=row.importance, country_code=row.country_code, centroid=Point.from_wkb(row.centroid), geometry=_filter_geometries(row)) def create_from_osmline_row(row: SaRow, class_type: Type[BaseResultT]) -> BaseResultT: """ Construct a new result and add the data from the result row from the address interpolation table osmline. 'class_type' defines the type of result to return. Returns None if the row is None. If the row contains a housenumber, then the housenumber is filled out. Otherwise the result contains the interpolation information in extratags. """ hnr = getattr(row, 'housenumber', None) res = class_type(source_table=SourceTable.OSMLINE, place_id=row.place_id, parent_place_id=row.parent_place_id, osm_object=('W', row.osm_id), category=('place', 'houses' if hnr is None else 'house'), address=row.address, postcode=row.postcode, country_code=row.country_code, centroid=Point.from_wkb(row.centroid), geometry=_filter_geometries(row)) if hnr is None: res.extratags = {'startnumber': str(row.startnumber), 'endnumber': str(row.endnumber), 'step': str(row.step)} else: res.housenumber = str(hnr) return res def create_from_tiger_row(row: SaRow, class_type: Type[BaseResultT], osm_type: Optional[str] = None, osm_id: Optional[int] = None) -> BaseResultT: """ Construct a new result and add the data from the result row from the Tiger data interpolation table. 'class_type' defines the type of result to return. Returns None if the row is None. If the row contains a housenumber, then the housenumber is filled out. Otherwise the result contains the interpolation information in extratags. """ hnr = getattr(row, 'housenumber', None) res = class_type(source_table=SourceTable.TIGER, place_id=row.place_id, parent_place_id=row.parent_place_id, osm_object=(osm_type or row.osm_type, osm_id or row.osm_id), category=('place', 'houses' if hnr is None else 'house'), postcode=row.postcode, country_code='us', centroid=Point.from_wkb(row.centroid), geometry=_filter_geometries(row)) if hnr is None: res.extratags = {'startnumber': str(row.startnumber), 'endnumber': str(row.endnumber), 'step': str(row.step)} else: res.housenumber = str(hnr) return res def create_from_postcode_row(row: SaRow, class_type: Type[BaseResultT]) -> BaseResultT: """ Construct a new result and add the data from the result row from the postcode table. 'class_type' defines the type of result to return. Returns None if the row is None. """ return class_type(source_table=SourceTable.POSTCODE, place_id=row.place_id, osm_object=None if row.osm_id is None else ('R', row.osm_id), parent_place_id=row.parent_place_id, category=(('place', 'postcode') if row.osm_id is None else ('boundary', 'postal_code')), names={'ref': row.postcode}, rank_search=row.rank_search, rank_address=5, country_code=row.country_code, centroid=Point.from_wkb(row.centroid), geometry=_filter_geometries(row)) def create_from_country_row(row: SaRow, class_type: Type[BaseResultT]) -> BaseResultT: """ Construct a new result and add the data from the result row from the fallback country tables. 'class_type' defines the type of result to return. Returns None if the row is None. """ return class_type(source_table=SourceTable.COUNTRY, category=('place', 'country'), centroid=Point.from_wkb(row.centroid), names=row.name, rank_address=4, rank_search=4, country_code=row.country_code, geometry=_filter_geometries(row)) async def add_result_details(conn: SearchConnection, results: List[BaseResultT], details: LookupDetails) -> None: """ Retrieve more details from the database according to the parameters specified in 'details'. """ if results: log().section('Query details for result') if details.address_details: log().comment('Query address details') await complete_address_details(conn, results) if details.linked_places: log().comment('Query linked places') for result in results: await complete_linked_places(conn, result) if details.parented_places: log().comment('Query parent places') for result in results: await complete_parented_places(conn, result) if details.entrances: log().comment('Query entrances details') await complete_entrances_details(conn, results) if details.keywords: log().comment('Query keywords') for result in results: await complete_keywords(conn, result) def _result_row_to_address_row(row: SaRow, isaddress: Optional[bool] = None) -> AddressLine: """ Create a new AddressLine from the results of a database query. """ extratags: Dict[str, str] = getattr(row, 'extratags', {}) or {} if 'linked_place' in extratags: extratags['place'] = extratags['linked_place'] names = _mingle_name_tags(row.name) or {} if getattr(row, 'housenumber', None) is not None: names['housenumber'] = row.housenumber if isaddress is None: isaddress = getattr(row, 'isaddress', True) return AddressLine(place_id=row.place_id, osm_object=None if row.osm_type is None else (row.osm_type, row.osm_id), category=(getattr(row, 'class'), row.type), names=names, extratags=extratags, admin_level=row.admin_level, fromarea=row.fromarea, isaddress=isaddress, rank_address=row.rank_address, distance=row.distance) def _get_address_lookup_id(result: BaseResultT) -> int: assert result.place_id if result.source_table != SourceTable.PLACEX or result.rank_search > 27: return result.parent_place_id or result.place_id return result.linked_place_id or result.place_id async def _finalize_entry(conn: SearchConnection, result: BaseResultT) -> None: assert result.address_rows is not None postcode = result.postcode or (result.address and result.address.get('postcode')) if postcode and ',' not in postcode and ';' not in postcode: result.address_rows.append(AddressLine( category=('place', 'postcode'), names={'ref': postcode}, fromarea=False, isaddress=True, rank_address=5, distance=0.0)) if result.country_code: async def _get_country_names() -> Optional[Dict[str, str]]: t = conn.t.country_name sql = sa.select(t.c.name, t.c.derived_name)\ .where(t.c.country_code == result.country_code) for cres in await conn.execute(sql): names = cast(Dict[str, str], cres[0]) if cres[1]: names.update(cast(Dict[str, str], cres[1])) return names return None country_names = await conn.get_cached_value('COUNTRY_NAME', result.country_code, _get_country_names) if country_names: result.address_rows.append(AddressLine( category=('place', 'country'), names=country_names, fromarea=False, isaddress=True, rank_address=4, distance=0.0)) result.address_rows.append(AddressLine( category=('place', 'country_code'), names={'ref': result.country_code}, extratags={}, fromarea=True, isaddress=False, rank_address=4, distance=0.0)) def _setup_address_details(result: BaseResultT) -> None: """ Retrieve information about places that make up the address of the result. """ result.address_rows = AddressLines() if result.names: result.address_rows.append(AddressLine( place_id=result.place_id, osm_object=result.osm_object, category=result.category, names=result.names, extratags=result.extratags or {}, admin_level=result.admin_level, fromarea=True, isaddress=True, rank_address=result.rank_address, distance=0.0)) if result.source_table == SourceTable.PLACEX and result.address: housenumber = result.address.get('housenumber')\ or result.address.get('streetnumber')\ or result.address.get('conscriptionnumber') elif result.housenumber: housenumber = result.housenumber else: housenumber = None if housenumber: result.address_rows.append(AddressLine( category=('place', 'house_number'), names={'ref': housenumber}, fromarea=True, isaddress=True, rank_address=28, distance=0)) if result.address and '_unlisted_place' in result.address: result.address_rows.append(AddressLine( category=('place', 'locality'), names={'name': result.address['_unlisted_place']}, fromarea=False, isaddress=True, rank_address=25, distance=0)) async def complete_address_details(conn: SearchConnection, results: List[BaseResultT]) -> None: """ Retrieve information about places that make up the address of the result. """ for result in results: _setup_address_details(result) # Lookup entries from place_address line lookup_ids = [{'pid': r.place_id, 'lid': _get_address_lookup_id(r), 'names': list(r.address.values()) if r.address else [], 'c': ('SRID=4326;' + r.centroid.to_wkt()) if r.centroid else ''} for r in results if r.place_id] if not lookup_ids: return ltab = sa.func.JsonArrayEach(sa.type_coerce(lookup_ids, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) t = conn.t.placex taddr = conn.t.addressline sql = sa.select(ltab.c.value['pid'].as_integer().label('src_place_id'), t.c.place_id, t.c.osm_type, t.c.osm_id, t.c.name, t.c.class_, t.c.type, t.c.extratags, t.c.admin_level, taddr.c.fromarea, sa.case((t.c.rank_address == 11, 5), else_=t.c.rank_address).label('rank_address'), taddr.c.distance, t.c.country_code, t.c.postcode)\ .join(taddr, sa.or_(taddr.c.place_id == ltab.c.value['pid'].as_integer(), taddr.c.place_id == ltab.c.value['lid'].as_integer()))\ .join(t, taddr.c.address_place_id == t.c.place_id)\ .order_by('src_place_id')\ .order_by(sa.column('rank_address').desc())\ .order_by((taddr.c.place_id == ltab.c.value['pid'].as_integer()).desc())\ .order_by(sa.case((sa.func.CrosscheckNames(t.c.name, ltab.c.value['names']), 2), (taddr.c.isaddress, 0), (sa.and_(taddr.c.fromarea, t.c.geometry.ST_Contains( sa.func.ST_GeomFromEWKT( ltab.c.value['c'].as_string()))), 1), else_=-1).desc())\ .order_by(taddr.c.fromarea.desc())\ .order_by(taddr.c.distance.desc())\ .order_by(t.c.rank_search.desc()) current_result = None current_rank_address = -1 for row in await conn.execute(sql): if current_result is None or row.src_place_id != current_result.place_id: current_result = next((r for r in results if r.place_id == row.src_place_id), None) assert current_result is not None current_rank_address = -1 location_isaddress = row.rank_address != current_rank_address if current_result.country_code is None and row.country_code: current_result.country_code = row.country_code assert current_result.address_rows is not None current_result.address_rows.append(_result_row_to_address_row(row, location_isaddress)) current_rank_address = row.rank_address for result in results: await _finalize_entry(conn, result) # Finally add the record for the parent entry where necessary. parent_lookup_ids = list(filter(lambda e: e['pid'] != e['lid'], lookup_ids)) if parent_lookup_ids: ltab = sa.func.JsonArrayEach(sa.type_coerce(parent_lookup_ids, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) sql = sa.select(ltab.c.value['pid'].as_integer().label('src_place_id'), t.c.place_id, t.c.osm_type, t.c.osm_id, t.c.name, t.c.class_, t.c.type, t.c.extratags, t.c.admin_level, t.c.rank_address)\ .where(t.c.place_id == ltab.c.value['lid'].as_integer()) for row in await conn.execute(sql): current_result = next((r for r in results if r.place_id == row.src_place_id), None) assert current_result is not None assert current_result.address_rows is not None current_result.address_rows.append(AddressLine( place_id=row.place_id, osm_object=(row.osm_type, row.osm_id), category=(row.class_, row.type), names=row.name, extratags=row.extratags or {}, admin_level=row.admin_level, fromarea=True, isaddress=True, rank_address=row.rank_address, distance=0.0)) # Now sort everything def mk_sort_key(place_id: Optional[int]) -> Callable[[AddressLine], Tuple[bool, int, bool]]: return lambda a: (a.place_id != place_id, -a.rank_address, a.isaddress) for result in results: assert result.address_rows is not None result.address_rows.sort(key=mk_sort_key(result.place_id)) def _placex_select_address_row(conn: SearchConnection, centroid: Point) -> SaSelect: t = conn.t.placex return sa.select(t.c.place_id, t.c.osm_type, t.c.osm_id, t.c.name, t.c.class_.label('class'), t.c.type, t.c.admin_level, t.c.housenumber, t.c.geometry.is_area().label('fromarea'), t.c.rank_address, t.c.geometry.distance_spheroid( sa.bindparam('centroid', value=centroid, type_=Geometry)).label('distance')) async def complete_linked_places(conn: SearchConnection, result: BaseResult) -> None: """ Retrieve information about places that link to the result. """ result.linked_rows = AddressLines() if result.source_table != SourceTable.PLACEX: return sql = _placex_select_address_row(conn, result.centroid)\ .where(conn.t.placex.c.linked_place_id == result.place_id) for row in await conn.execute(sql): result.linked_rows.append(_result_row_to_address_row(row)) async def complete_entrances_details(conn: SearchConnection, results: List[BaseResultT]) -> None: """ Retrieve information about tagged entrances for the given results. """ place_ids = (r.place_id for r in results if r.source_table == SourceTable.PLACEX) t = conn.t.placex_entrance sql = sa.select(t.c.place_id, t.c.osm_id, t.c.type, t.c.location, t.c.extratags)\ .where(t.c.place_id.in_(place_ids)) current_result = None for row in await conn.execute(sql): if current_result is None or row.place_id != current_result.place_id: current_result = next((r for r in results if r.place_id == row.place_id), None) assert current_result is not None if current_result.entrances is None: current_result.entrances = [] current_result.entrances.append(EntranceDetails( osm_id=row.osm_id, type=row.type, location=Point.from_wkb(row.location), extratags=row.extratags, )) async def complete_keywords(conn: SearchConnection, result: BaseResult) -> None: """ Retrieve information about the search terms used for this place. Requires that the query analyzer was initialised to get access to the word table. """ t = conn.t.search_name sql = sa.select(t.c.name_vector, t.c.nameaddress_vector)\ .where(t.c.place_id == result.place_id) result.name_keywords = [] result.address_keywords = [] t = conn.t.meta.tables['word'] sel = sa.select(t.c.word_id, t.c.word_token, t.c.word) for name_tokens, address_tokens in await conn.execute(sql): for row in await conn.execute(sel.where(t.c.word_id.in_(name_tokens))): result.name_keywords.append(WordInfo(*row)) for row in await conn.execute(sel.where(t.c.word_id.in_(address_tokens))): result.address_keywords.append(WordInfo(*row)) async def complete_parented_places(conn: SearchConnection, result: BaseResult) -> None: """ Retrieve information about places that the result provides the address for. """ result.parented_rows = AddressLines() if result.source_table != SourceTable.PLACEX: return sql = _placex_select_address_row(conn, result.centroid)\ .where(conn.t.placex.c.parent_place_id == result.place_id)\ .where(conn.t.placex.c.rank_search == 30) for row in await conn.execute(sql): result.parented_rows.append(_result_row_to_address_row(row)) ================================================ FILE: src/nominatim_api/reverse.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of reverse geocoding. """ from typing import Optional, List, Callable, Type, Tuple, Dict, Any, cast, Union import functools import sqlalchemy as sa from .typing import SaColumn, SaSelect, SaFromClause, SaLabel, SaRow, \ SaBind, SaLambdaSelect from .sql.sqlalchemy_types import Geometry from .connection import SearchConnection from . import results as nres from .logging import log from .types import AnyPoint, DataLayer, ReverseDetails, GeometryFormat, Bbox RowFunc = Callable[[SaRow, Type[nres.ReverseResult]], nres.ReverseResult] WKT_PARAM: SaBind = sa.bindparam('wkt', type_=Geometry) MAX_RANK_PARAM: SaBind = sa.bindparam('max_rank') def no_index(expr: SaColumn) -> SaColumn: """ Wrap the given expression, so that the query planner will refrain from using the expression for index lookup. """ return sa.func.coalesce(sa.null(), expr) def _select_from_placex(t: SaFromClause, use_wkt: bool = True) -> SaSelect: """ Create a select statement with the columns relevant for reverse results. """ if not use_wkt: distance = t.c.distance centroid = t.c.centroid else: distance = t.c.geometry.ST_Distance(WKT_PARAM) centroid = sa.case((t.c.geometry.is_line_like(), t.c.geometry.ST_ClosestPoint(WKT_PARAM)), else_=t.c.centroid).label('centroid') return sa.select(t.c.place_id, t.c.osm_type, t.c.osm_id, t.c.name, t.c.class_, t.c.type, t.c.address, t.c.extratags, t.c.housenumber, t.c.postcode, t.c.country_code, t.c.importance, t.c.wikipedia, t.c.parent_place_id, t.c.rank_address, t.c.rank_search, centroid, t.c.linked_place_id, t.c.admin_level, distance.label('distance'), t.c.geometry.ST_Expand(0).label('bbox')) def _interpolated_housenumber(table: SaFromClause) -> SaLabel: return sa.cast(table.c.startnumber + sa.func.round(((table.c.endnumber - table.c.startnumber) * table.c.position) / table.c.step) * table.c.step, sa.Integer).label('housenumber') def _interpolated_position(table: SaFromClause) -> SaLabel: fac = sa.cast(table.c.step, sa.Float) / (table.c.endnumber - table.c.startnumber) rounded_pos = sa.func.round(table.c.position / fac) * fac return sa.case( (table.c.endnumber == table.c.startnumber, table.c.linegeo.ST_Centroid()), else_=table.c.linegeo.ST_LineInterpolatePoint(rounded_pos)).label('centroid') def _locate_interpolation(table: SaFromClause) -> SaLabel: """ Given a position, locate the closest point on the line. """ return sa.case((table.c.linegeo.is_line_like(), table.c.linegeo.ST_LineLocatePoint(WKT_PARAM)), else_=0).label('position') def _get_closest(*rows: Optional[SaRow]) -> Optional[SaRow]: return min(rows, key=lambda row: 1000 if row is None else row.distance) class ReverseGeocoder: """ Class implementing the logic for looking up a place from a coordinate. """ def __init__(self, conn: SearchConnection, params: ReverseDetails, restrict_to_country_areas: bool = False) -> None: self.conn = conn self.params = params self.restrict_to_country_areas = restrict_to_country_areas self.bind_params: Dict[str, Any] = {'max_rank': params.max_rank} @property def max_rank(self) -> int: """ Return the maximum configured rank. """ return self.params.max_rank def has_geometries(self) -> bool: """ Check if any geometries are requested. """ return bool(self.params.geometry_output) def layer_enabled(self, *layer: DataLayer) -> bool: """ Return true when any of the given layer types are requested. """ return any(self.params.layers & ly for ly in layer) def layer_disabled(self, *layer: DataLayer) -> bool: """ Return true when none of the given layer types is requested. """ return not any(self.params.layers & ly for ly in layer) def has_feature_layers(self) -> bool: """ Return true if any layer other than ADDRESS or POI is requested. """ return self.layer_enabled(DataLayer.RAILWAY, DataLayer.MANMADE, DataLayer.NATURAL) def _add_geometry_columns(self, sql: SaLambdaSelect, col: SaColumn) -> SaSelect: out = [] if self.params.geometry_simplification > 0.0: col = sa.func.ST_SimplifyPreserveTopology(col, self.params.geometry_simplification) if self.params.geometry_output & GeometryFormat.GEOJSON: out.append(sa.func.ST_AsGeoJSON(col, 7).label('geometry_geojson')) if self.params.geometry_output & GeometryFormat.TEXT: out.append(sa.func.ST_AsText(col).label('geometry_text')) if self.params.geometry_output & GeometryFormat.KML: out.append(sa.func.ST_AsKML(col, 7).label('geometry_kml')) if self.params.geometry_output & GeometryFormat.SVG: out.append(sa.func.ST_AsSVG(col, 0, 7).label('geometry_svg')) return sql.add_columns(*out) def _filter_by_layer(self, table: SaFromClause) -> SaColumn: if self.layer_enabled(DataLayer.MANMADE): exclude = [] if self.layer_disabled(DataLayer.RAILWAY): exclude.append('railway') if self.layer_disabled(DataLayer.NATURAL): exclude.extend(('natural', 'water', 'waterway')) return table.c.class_.not_in(tuple(exclude)) include = [] if self.layer_enabled(DataLayer.RAILWAY): include.append('railway') if self.layer_enabled(DataLayer.NATURAL): include.extend(('natural', 'water', 'waterway')) return table.c.class_.in_(tuple(include)) async def _find_closest_street_or_pois(self, distance: float, fuzziness: float) -> list[SaRow]: """ Look up the closest rank 26+ place in the database. The function finds the object that is closest to the reverse search point as well as all objects within 'fuzziness' distance to that best result. """ t = self.conn.t.placex # PostgreSQL must not get the distance as a parameter because # there is a danger it won't be able to properly estimate index use # when used with prepared statements diststr = sa.text(f"{distance + fuzziness}") sql: SaLambdaSelect = sa.lambda_stmt( lambda: _select_from_placex(t) .where(t.c.geometry.within_distance(WKT_PARAM, diststr)) .where(t.c.indexed_status == 0) .where(t.c.linked_place_id == None) .where(sa.or_(sa.not_(t.c.geometry.is_area()), t.c.centroid.ST_Distance(WKT_PARAM) < diststr))) if self.has_geometries(): sql = self._add_geometry_columns(sql, t.c.geometry) restrict: List[Union[SaColumn, Callable[[], SaColumn]]] = [] if self.layer_enabled(DataLayer.ADDRESS): max_rank = min(29, self.max_rank) restrict.append(lambda: no_index(t.c.rank_address).between(26, max_rank)) if self.max_rank == 30: restrict.append(lambda: sa.func.IsAddressPoint(t)) if self.layer_enabled(DataLayer.POI) and self.max_rank == 30: restrict.append(lambda: sa.and_(no_index(t.c.rank_search) == 30, t.c.class_.not_in(('place', 'building')), sa.not_(t.c.geometry.is_line_like()))) if self.has_feature_layers(): restrict.append(sa.and_(no_index(t.c.rank_search).between(26, MAX_RANK_PARAM), no_index(t.c.rank_address) == 0, self._filter_by_layer(t))) if not restrict: return [] inner = sql.where(sa.or_(*restrict)) \ .add_columns(t.c.geometry.label('_geometry')) \ .subquery() # Use a window function to get the closest results to the best result. windowed = sa.select(inner, sa.func.first_value(inner.c.distance) .over(order_by=inner.c.distance) .label('_min_distance'), sa.func.first_value( sa.case((inner.c.rank_search <= 27, inner.c._geometry.ST_ClosestPoint(WKT_PARAM)), else_=None)) .over(order_by=inner.c.distance) .label('_closest_point'), sa.func.first_value(sa.case((sa.or_(inner.c.rank_search <= 27, inner.c.osm_type == 'N'), None), else_=inner.c._geometry)) .over(order_by=inner.c.distance) .label('_best_geometry')) \ .subquery() outer = sa.select(*(c for c in windowed.c if not c.key.startswith('_')), sa.case((sa.or_(windowed.c._closest_point == None, windowed.c.housenumber == None), None), else_=windowed.c.centroid.ST_Distance(windowed.c._closest_point)) .label('distance_from_best'), sa.case((sa.or_(windowed.c._best_geometry == None, windowed.c.rank_search <= 27, windowed.c.osm_type != 'N'), False), else_=windowed.c.centroid.ST_CoveredBy(windowed.c._best_geometry)) .label('best_inside')) \ .where(windowed.c.distance < windowed.c._min_distance + fuzziness) \ .order_by(windowed.c.distance) return list(await self.conn.execute(outer, self.bind_params)) async def _find_housenumber_for_street(self, parent_place_id: int) -> Optional[SaRow]: t = self.conn.t.placex def _base_query() -> SaSelect: return _select_from_placex(t)\ .where(t.c.geometry.within_distance(WKT_PARAM, 0.001))\ .where(t.c.parent_place_id == parent_place_id)\ .where(sa.func.IsAddressPoint(t))\ .where(t.c.indexed_status == 0)\ .where(t.c.linked_place_id == None)\ .order_by('distance')\ .limit(1) sql: SaLambdaSelect if self.has_geometries(): sql = self._add_geometry_columns(_base_query(), t.c.geometry) else: sql = sa.lambda_stmt(_base_query) return (await self.conn.execute(sql, self.bind_params)).one_or_none() async def _find_interpolation_for_street(self, parent_place_id: Optional[int], distance: float) -> Optional[SaRow]: t = self.conn.t.osmline sql = sa.select(t, t.c.linegeo.ST_Distance(WKT_PARAM).label('distance'), _locate_interpolation(t))\ .where(t.c.linegeo.within_distance(WKT_PARAM, distance))\ .where(t.c.startnumber != None)\ .order_by('distance')\ .limit(1) if parent_place_id is not None: sql = sql.where(t.c.parent_place_id == parent_place_id) inner = sql.subquery('ipol') sql = sa.select(inner.c.place_id, inner.c.osm_id, inner.c.parent_place_id, inner.c.address, _interpolated_housenumber(inner), _interpolated_position(inner), inner.c.postcode, inner.c.country_code, inner.c.distance) if self.has_geometries(): sub = sql.subquery('geom') sql = self._add_geometry_columns(sa.select(sub), sub.c.centroid) return (await self.conn.execute(sql, self.bind_params)).one_or_none() async def _find_tiger_number_for_street(self, parent_place_id: int) -> Optional[SaRow]: t = self.conn.t.tiger def _base_query() -> SaSelect: inner = sa.select(t, t.c.linegeo.ST_Distance(WKT_PARAM).label('distance'), _locate_interpolation(t))\ .where(t.c.linegeo.within_distance(WKT_PARAM, 0.001))\ .where(t.c.parent_place_id == parent_place_id)\ .order_by('distance')\ .limit(1)\ .subquery('tiger') return sa.select(inner.c.place_id, inner.c.parent_place_id, _interpolated_housenumber(inner), _interpolated_position(inner), inner.c.postcode, inner.c.distance) sql: SaLambdaSelect if self.has_geometries(): sub = _base_query().subquery('geom') sql = self._add_geometry_columns(sa.select(sub), sub.c.centroid) else: sql = sa.lambda_stmt(_base_query) return (await self.conn.execute(sql, self.bind_params)).one_or_none() async def lookup_street_poi(self) -> Tuple[Optional[SaRow], RowFunc]: """ Find a street or POI/address for the given WKT point. """ log().section('Reverse lookup on street/address level') row_func: RowFunc = nres.create_from_placex_row distance = 0.006 result = None hnr_distance = None parent_street = None for row in await self._find_closest_street_or_pois(distance, 0.001): if result is None: log().var_dump('Closest result', row) result = row if self.max_rank > 27 \ and self.layer_enabled(DataLayer.ADDRESS) \ and result.rank_address <= 27: parent_street = result.place_id distance = 0.001 else: distance = row.distance # If the closest result was a street but an address was requested, # see if we can refine the result with a housenumber closeby. elif parent_street is not None \ and row.distance_from_best is not None \ and row.distance_from_best < 0.001 \ and (hnr_distance is None or hnr_distance > row.distance_from_best) \ and row.parent_place_id == parent_street: log().var_dump('Housenumber to closest result', row) result = row hnr_distance = row.distance_from_best distance = row.distance # If the closest object is inside an area, then check if there is # a POI nearby and return that with preference. elif result.osm_type != 'N' and result.rank_search > 27 \ and result.distance == 0 \ and row.best_inside: log().var_dump('POI near closest result area', row) result = row break # it can't get better than that, everything else is farther away # For the US also check the TIGER data, when no housenumber/POI was found. if result is not None and parent_street is not None and hnr_distance is None \ and result.country_code == 'us': log().comment('Find TIGER housenumber for street') addr_row = await self._find_tiger_number_for_street(parent_street) log().var_dump('Result (street Tiger housenumber)', addr_row) if addr_row is not None: row_func = cast(RowFunc, functools.partial(nres.create_from_tiger_row, osm_type=row.osm_type, osm_id=row.osm_id)) result = addr_row # Check for an interpolation that is either closer than our result # or belongs to a close street found. # No point in doing this when the result is already inside a building, # i.e. when the distance is already 0. if self.max_rank > 27 and self.layer_enabled(DataLayer.ADDRESS) and distance > 0: log().comment('Find interpolation for street') addr_row = await self._find_interpolation_for_street(parent_street, distance) log().var_dump('Result (street interpolation)', addr_row) if addr_row is not None: return addr_row, nres.create_from_osmline_row return result, row_func async def _lookup_area_address(self) -> Optional[SaRow]: """ Lookup large addressable areas for the given WKT point. """ log().comment('Reverse lookup by larger address area features') t = self.conn.t.placex def _base_query() -> SaSelect: # The inner SQL brings results in the right order, so that # later only a minimum of results needs to be checked with ST_Contains. inner = sa.select(t, sa.literal(0.0).label('distance'))\ .where(t.c.rank_search.between(5, MAX_RANK_PARAM))\ .where(t.c.rank_address != 5)\ .where(t.c.rank_address != 11)\ .where(t.c.geometry.intersects(WKT_PARAM))\ .where(sa.func.PlacexGeometryReverseLookuppolygon())\ .order_by(sa.desc(t.c.rank_search))\ .limit(50)\ .subquery('area') return _select_from_placex(inner, False)\ .where(inner.c.geometry.ST_Contains(WKT_PARAM))\ .order_by(sa.desc(inner.c.rank_search))\ .limit(1) sql: SaLambdaSelect = sa.lambda_stmt(_base_query) if self.has_geometries(): sql = self._add_geometry_columns(sql, sa.literal_column('area.geometry')) address_row = (await self.conn.execute(sql, self.bind_params)).one_or_none() log().var_dump('Result (area)', address_row) if address_row is not None and address_row.rank_search < self.max_rank: log().comment('Search for better matching place nodes inside the area') address_rank = address_row.rank_search address_id = address_row.place_id def _place_inside_area_query() -> SaSelect: inner = \ sa.select(t, t.c.geometry.ST_Distance(WKT_PARAM).label('distance'))\ .where(t.c.rank_search > address_rank)\ .where(t.c.rank_search <= MAX_RANK_PARAM)\ .where(t.c.indexed_status == 0)\ .where(sa.func.IntersectsReverseDistance(t, WKT_PARAM))\ .order_by(sa.desc(t.c.rank_search))\ .limit(50)\ .subquery('places') touter = t.alias('outer') return _select_from_placex(inner, False)\ .join(touter, touter.c.geometry.ST_Contains(inner.c.geometry))\ .where(touter.c.place_id == address_id)\ .where(sa.func.IsBelowReverseDistance(inner.c.distance, inner.c.rank_search))\ .order_by(sa.desc(inner.c.rank_search), inner.c.distance)\ .limit(1) if self.has_geometries(): sql = self._add_geometry_columns(_place_inside_area_query(), sa.literal_column('places.geometry')) else: sql = sa.lambda_stmt(_place_inside_area_query) place_address_row = (await self.conn.execute(sql, self.bind_params)).one_or_none() log().var_dump('Result (place node)', place_address_row) if place_address_row is not None: return place_address_row return address_row async def _lookup_area_others(self) -> Optional[SaRow]: t = self.conn.t.placex inner = sa.select(t, t.c.geometry.ST_Distance(WKT_PARAM).label('distance'))\ .where(t.c.rank_address == 0)\ .where(t.c.rank_search.between(5, MAX_RANK_PARAM))\ .where(t.c.name != None)\ .where(t.c.indexed_status == 0)\ .where(t.c.linked_place_id == None)\ .where(self._filter_by_layer(t))\ .where(t.c.geometry.intersects(sa.func.ST_Expand(WKT_PARAM, 0.007)))\ .order_by(sa.desc(t.c.rank_search))\ .order_by('distance')\ .limit(50)\ .subquery() sql = _select_from_placex(inner, False)\ .where(sa.or_(sa.not_(inner.c.geometry.is_area()), inner.c.geometry.ST_Contains(WKT_PARAM)))\ .order_by(sa.desc(inner.c.rank_search), inner.c.distance)\ .limit(1) if self.has_geometries(): sql = self._add_geometry_columns(sql, inner.c.geometry) row = (await self.conn.execute(sql, self.bind_params)).one_or_none() log().var_dump('Result (non-address feature)', row) return row async def lookup_area(self) -> Optional[SaRow]: """ Lookup large areas for the current search. """ log().section('Reverse lookup by larger area features') if self.layer_enabled(DataLayer.ADDRESS): address_row = await self._lookup_area_address() else: address_row = None if self.has_feature_layers(): other_row = await self._lookup_area_others() else: other_row = None return _get_closest(address_row, other_row) async def lookup_country_codes(self) -> List[str]: """ Lookup the country for the current search. """ log().section('Reverse lookup by country code') t = self.conn.t.country_grid sql = sa.select(t.c.country_code).distinct()\ .where(t.c.geometry.ST_Contains(WKT_PARAM)) ccodes = [cast(str, r[0]) for r in await self.conn.execute(sql, self.bind_params)] log().var_dump('Country codes', ccodes) return ccodes async def lookup_country(self, ccodes: List[str]) -> Tuple[Optional[SaRow], RowFunc]: """ Lookup the country for the current search. """ row_func = nres.create_from_placex_row if not ccodes: ccodes = await self.lookup_country_codes() if not ccodes: return None, row_func t = self.conn.t.placex if self.max_rank > 4: log().comment('Search for place nodes in country') def _base_query() -> SaSelect: inner = sa.select(t, t.c.geometry.ST_Distance(WKT_PARAM).label('distance'))\ .where(t.c.rank_search > 4)\ .where(t.c.rank_search <= MAX_RANK_PARAM)\ .where(t.c.indexed_status == 0)\ .where(t.c.country_code.in_(ccodes))\ .where(sa.func.IntersectsReverseDistance(t, WKT_PARAM))\ .order_by(sa.desc(t.c.rank_search))\ .limit(50)\ .subquery('area') return _select_from_placex(inner, False)\ .where(sa.func.IsBelowReverseDistance(inner.c.distance, inner.c.rank_search))\ .order_by(sa.desc(inner.c.rank_search), inner.c.distance)\ .limit(1) sql: SaLambdaSelect if self.has_geometries(): sql = self._add_geometry_columns(_base_query(), sa.literal_column('area.geometry')) else: sql = sa.lambda_stmt(_base_query) address_row = (await self.conn.execute(sql, self.bind_params)).one_or_none() log().var_dump('Result (addressable place node)', address_row) else: address_row = None if address_row is None: # Still nothing, then return a country with the appropriate country code. def _country_base_query() -> SaSelect: return _select_from_placex(t)\ .where(t.c.country_code.in_(ccodes))\ .where(t.c.rank_address == 4)\ .where(t.c.rank_search == 4)\ .where(t.c.linked_place_id == None)\ .order_by('distance')\ .limit(1) if self.has_geometries(): sql = self._add_geometry_columns(_country_base_query(), t.c.geometry) else: sql = sa.lambda_stmt(_country_base_query) address_row = (await self.conn.execute(sql, self.bind_params)).one_or_none() if address_row is None: # finally fall back to country table t = self.conn.t.country_name tgrid = self.conn.t.country_grid sql = sa.select(tgrid.c.country_code, tgrid.c.geometry.ST_Centroid().ST_Collect().ST_Centroid() .label('centroid'), tgrid.c.geometry.ST_Collect().ST_Expand(0).label('bbox'))\ .where(tgrid.c.country_code.in_(ccodes))\ .group_by(tgrid.c.country_code) sub = sql.subquery('grid') sql = sa.select(t.c.country_code, t.c.name.merge(t.c.derived_name).label('name'), sub.c.centroid, sub.c.bbox)\ .join(sub, t.c.country_code == sub.c.country_code)\ .order_by(t.c.country_code)\ .limit(1) sql = self._add_geometry_columns(sql, sub.c.centroid) address_row = (await self.conn.execute(sql, self.bind_params)).one_or_none() row_func = nres.create_from_country_row return address_row, row_func async def lookup(self, coord: AnyPoint) -> Optional[nres.ReverseResult]: """ Look up a single coordinate. Returns the place information, if a place was found near the coordinates or None otherwise. """ log().function('reverse_lookup', coord=coord, params=self.params) self.bind_params['wkt'] = f'POINT({coord[0]} {coord[1]})' row: Optional[SaRow] = None row_func: RowFunc = nres.create_from_placex_row if self.max_rank >= 26: row, tmp_row_func = await self.lookup_street_poi() if row is not None: row_func = tmp_row_func if row is None: if self.restrict_to_country_areas: ccodes = await self.lookup_country_codes() if not ccodes: return None else: ccodes = [] if self.max_rank > 4: row = await self.lookup_area() if row is None and self.layer_enabled(DataLayer.ADDRESS): row, row_func = await self.lookup_country(ccodes) if row is None: return None result = row_func(row, nres.ReverseResult) result.distance = getattr(row, 'distance', 0) if hasattr(row, 'bbox'): result.bbox = Bbox.from_wkb(row.bbox) await nres.add_result_details(self.conn, [result], self.params) return result ================================================ FILE: src/nominatim_api/search/__init__.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Module for forward search. """ from .geocoder import (ForwardGeocoder as ForwardGeocoder) from .query import (Phrase as Phrase, PHRASE_ANY as PHRASE_ANY, PHRASE_AMENITY as PHRASE_AMENITY, PHRASE_STREET as PHRASE_STREET, PHRASE_CITY as PHRASE_CITY, PHRASE_COUNTY as PHRASE_COUNTY, PHRASE_STATE as PHRASE_STATE, PHRASE_POSTCODE as PHRASE_POSTCODE, PHRASE_COUNTRY as PHRASE_COUNTRY) from .query_analyzer_factory import (make_query_analyzer as make_query_analyzer) ================================================ FILE: src/nominatim_api/search/db_search_builder.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Conversion from token assignment to an abstract DB search. """ from typing import Optional, List, Tuple, Iterator, Dict import heapq from ..types import SearchDetails, DataLayer from . import query as qmod from .token_assignment import TokenAssignment from . import db_search_fields as dbf from . import db_searches as dbs from . import db_search_lookups as lookups def wrap_near_search(categories: List[Tuple[str, str]], search: dbs.AbstractSearch) -> dbs.NearSearch: """ Create a new search that wraps the given search in a search for near places of the given category. """ return dbs.NearSearch(penalty=search.penalty, categories=dbf.WeightedCategories(categories, [0.0] * len(categories)), search=search) def build_poi_search(category: List[Tuple[str, str]], countries: Optional[List[str]]) -> dbs.PoiSearch: """ Create a new search for places by the given category, possibly constraint to the given countries. """ if countries: ccs = dbf.WeightedStrings(countries, [0.0] * len(countries)) else: ccs = dbf.WeightedStrings([], []) class _PoiData(dbf.SearchData): penalty = 0.0 qualifiers = dbf.WeightedCategories(category, [0.0] * len(category)) countries = ccs return dbs.PoiSearch(_PoiData()) class SearchBuilder: """ Build the abstract search queries from token assignments. """ def __init__(self, query: qmod.QueryStruct, details: SearchDetails) -> None: self.query = query self.details = details @property def configured_for_country(self) -> bool: """ Return true if the search details are configured to allow countries in the result. """ return self.details.min_rank <= 4 and self.details.max_rank >= 4 \ and self.details.layer_enabled(DataLayer.ADDRESS) @property def configured_for_postcode(self) -> bool: """ Return true if the search details are configured to allow postcodes in the result. """ return self.details.min_rank <= 5 and self.details.max_rank >= 11\ and self.details.layer_enabled(DataLayer.ADDRESS) @property def configured_for_housenumbers(self) -> bool: """ Return true if the search details are configured to allow addresses in the result. """ return self.details.max_rank >= 30 \ and self.details.layer_enabled(DataLayer.ADDRESS) def build(self, assignment: TokenAssignment) -> Iterator[dbs.AbstractSearch]: """ Yield all possible abstract searches for the given token assignment. """ sdata = self.get_search_data(assignment) if sdata is None: return near_items = self.get_near_items(assignment) if near_items is not None and not near_items: return # impossible combination of near items and category parameter if assignment.name is None: if near_items and not sdata.postcodes: sdata.qualifiers = near_items near_items = None builder = self.build_poi_search(sdata) elif assignment.housenumber: hnr_tokens = self.query.get_tokens(assignment.housenumber, qmod.TOKEN_HOUSENUMBER) builder = self.build_housenumber_search(sdata, hnr_tokens, assignment.address) else: builder = self.build_special_search(sdata, assignment.address, bool(near_items)) else: builder = self.build_name_search(sdata, assignment.name, assignment.address, bool(near_items)) if near_items: penalty = min(near_items.penalties) near_items.penalties = [p - penalty for p in near_items.penalties] for search in builder: search_penalty = search.penalty search.penalty = 0.0 yield dbs.NearSearch(penalty + assignment.penalty + search_penalty, near_items, search) else: for search in builder: search.penalty += assignment.penalty yield search def build_poi_search(self, sdata: dbf.SearchData) -> Iterator[dbs.AbstractSearch]: """ Build abstract search query for a simple category search. This kind of search requires an additional geographic constraint. """ if not sdata.housenumbers \ and ((self.details.viewbox and self.details.bounded_viewbox) or self.details.near): yield dbs.PoiSearch(sdata) def build_special_search(self, sdata: dbf.SearchData, address: List[qmod.TokenRange], is_category: bool) -> Iterator[dbs.AbstractSearch]: """ Build abstract search queries for searches that do not involve a named place. """ if sdata.qualifiers: # No special searches over qualifiers supported. return if sdata.countries and not address and not sdata.postcodes \ and self.configured_for_country: yield dbs.CountrySearch(sdata) if sdata.postcodes and (is_category or self.configured_for_postcode): penalty = 0.0 if sdata.countries else 0.1 if address: sdata.lookups = [dbf.FieldLookup('nameaddress_vector', [t.token for r in address for t in self.query.iter_partials(r)], lookups.Restrict)] yield dbs.PostcodeSearch(penalty, sdata) def build_housenumber_search(self, sdata: dbf.SearchData, hnrs: List[qmod.Token], address: List[qmod.TokenRange]) -> Iterator[dbs.AbstractSearch]: """ Build a simple address search for special entries where the housenumber is the main name token. """ partials = dbf.CountedTokenIDs((t for trange in address for t in self.query.iter_partials(trange)), 'addr_count') if not partials: # can happen when none of the partials is indexed return expected_count = sum(t.count for t in hnrs) hnr_tokens = [t.token for t in hnrs] if expected_count < 10000: sdata.lookups = [dbf.FieldLookup('name_vector', hnr_tokens, lookups.LookupAny), dbf.FieldLookup('nameaddress_vector', partials.get_tokens(), lookups.Restrict)] else: split = partials.get_num_lookup_tokens(20000, 5) if split > 0: sdata.lookups = partials.split_lookup(split, 'nameaddress_vector') sdata.lookups.append( dbf.FieldLookup('name_vector', hnr_tokens, lookups.Restrict)) expected_count = partials.min_count() / (5**(split - 1)) else: addr_fulls = [t.token for t in self.query.get_tokens(address[0], qmod.TOKEN_WORD)] if len(addr_fulls) > 5: return sdata.lookups = [ dbf.FieldLookup('name_vector', hnr_tokens, lookups.LookupAny), dbf.FieldLookup('nameaddress_vector', addr_fulls, lookups.LookupAny)] sdata.housenumbers = dbf.WeightedStrings([], []) yield dbs.PlaceSearch(0.0, sdata, expected_count, True) def build_name_search(self, sdata: dbf.SearchData, name: qmod.TokenRange, address: List[qmod.TokenRange], is_category: bool) -> Iterator[dbs.AbstractSearch]: """ Build abstract search queries for simple name or address searches. """ if is_category or not sdata.housenumbers or self.configured_for_housenumbers: ranking = self.get_name_ranking(name) name_penalty = ranking.normalize_penalty() if ranking.rankings: sdata.rankings.append(ranking) for penalty, count, lookup in self.yield_lookups(name, address): sdata.lookups = lookup if sdata.housenumbers: yield dbs.AddressSearch(penalty + name_penalty, sdata, count, bool(address)) else: yield dbs.PlaceSearch(penalty + name_penalty, sdata, count, bool(address)) def yield_lookups(self, name: qmod.TokenRange, address: List[qmod.TokenRange] ) -> Iterator[Tuple[float, int, List[dbf.FieldLookup]]]: """ Yield all variants how the given name and address should best be searched for. This takes into account how frequent the terms are and tries to find a lookup that optimizes index use. """ name_partials = dbf.CountedTokenIDs(self.query.iter_partials(name)) addr_partials = dbf.CountedTokenIDs((t for r in address for t in self.query.iter_partials(r)), 'addr_count') if not addr_partials: yield from self.yield_name_only_lookups(name_partials, name) else: yield from self.yield_address_lookups(name_partials, addr_partials, name) def yield_name_only_lookups(self, partials: dbf.CountedTokenIDs, name: qmod.TokenRange ) -> Iterator[Tuple[float, int, List[dbf.FieldLookup]]]: """ Yield the best lookup for a name-only search. """ split = partials.get_num_lookup_tokens(30000, 6) if split > 0: yield 0.0, partials.expected_for_all_search(5), \ partials.split_lookup(split, 'name_vector') else: # lots of results expected: try lookup by full names first name_fulls = list(filter(lambda t: t.count < 50000, self.query.get_tokens(name, qmod.TOKEN_WORD))) if name_fulls: yield 0.0, sum(t.count for t in name_fulls), \ dbf.lookup_by_any_name([t.token for t in name_fulls], [], []) # look the name up by its partials exp_count = partials.expected_for_all_search(5) if exp_count < 50000: yield 1.0, exp_count, \ [dbf.FieldLookup('name_vector', partials.get_tokens(), lookups.LookupAll)] def yield_address_lookups(self, name_partials: dbf.CountedTokenIDs, addr_partials: dbf.CountedTokenIDs, name: qmod.TokenRange, ) -> Iterator[Tuple[float, int, List[dbf.FieldLookup]]]: penalty = 0.0 # extra penalty name_split = name_partials.get_num_lookup_tokens(20000, 6) addr_split = addr_partials.get_num_lookup_tokens(10000, 3) if name_split < 0 and addr_split < 0: # Partial term too frequent. Try looking up by rare full names first. name_fulls = self.query.get_tokens(name, qmod.TOKEN_WORD) if name_fulls: fulls_count = sum(t.count for t in name_fulls) if fulls_count < 80000: yield 0.0, fulls_count, \ dbf.lookup_by_any_name([t.token for t in name_fulls], addr_partials.get_tokens(), []) penalty += 0.2 penalty += 0.4 name_split = name_partials.get_num_lookup_tokens(50000, 10) addr_split = addr_partials.get_num_lookup_tokens(30000, 5) if name_split > 0 \ and (addr_split < 0 or name_partials.min_count() <= addr_partials.min_count()): # lookup by name lookup = name_partials.split_lookup(name_split, 'name_vector') lookup.append(dbf.FieldLookup('nameaddress_vector', addr_partials.get_tokens(), lookups.Restrict)) yield penalty, name_partials.expected_for_all_search(5), lookup elif addr_split > 0: # lookup by address lookup = addr_partials.split_lookup(addr_split, 'nameaddress_vector') lookup.append(dbf.FieldLookup('name_vector', name_partials.get_tokens(), lookups.Restrict)) yield penalty, addr_partials.expected_for_all_search(3), lookup elif len(name_partials) > 1: penalty += 0.5 # To catch remaining results, lookup by name and address # We only do this if there is a reasonable number of results expected. exp_count = min(name_partials.min_count(), addr_partials.min_count()) exp_count = int(exp_count / (min(3, len(name_partials)) + min(3, len(addr_partials)))) if exp_count < 50000: lookup = name_partials.split_lookup(3, 'name_vector') lookup.extend(addr_partials.split_lookup(3, 'nameaddress_vector')) yield penalty, exp_count, lookup def get_name_ranking(self, trange: qmod.TokenRange, db_field: str = 'name_vector') -> dbf.FieldRanking: """ Create a ranking expression for a name term in the given range. """ name_fulls = self.query.get_tokens(trange, qmod.TOKEN_WORD) full_word_penalty = self.query.get_in_word_penalty(trange) ranks = [dbf.RankedTokens(t.penalty + full_word_penalty, [t.token]) for t in name_fulls] ranks.sort(key=lambda r: r.penalty) # Fallback, sum of penalty for partials default = sum(t.penalty for t in self.query.iter_partials(trange)) + 0.2 default += sum(n.word_break_penalty for n in self.query.nodes[trange.start + 1:trange.end]) return dbf.FieldRanking(db_field, default, ranks) def get_addr_ranking(self, trange: qmod.TokenRange) -> dbf.FieldRanking: """ Create a list of ranking expressions for an address term for the given ranges. """ todo: List[Tuple[int, int, dbf.RankedTokens]] = [] heapq.heappush(todo, (0, trange.start, dbf.RankedTokens(0.0, []))) ranks: List[dbf.RankedTokens] = [] while todo: _, pos, rank = heapq.heappop(todo) # partial node partial = self.query.nodes[pos].partial if partial is not None: if pos + 1 < trange.end: penalty = rank.penalty + partial.penalty \ + self.query.nodes[pos + 1].word_break_penalty heapq.heappush(todo, (-(pos + 1), pos + 1, dbf.RankedTokens(penalty, rank.tokens))) else: ranks.append(dbf.RankedTokens(rank.penalty + partial.penalty, rank.tokens)) # full words for tlist in self.query.nodes[pos].starting: if tlist.ttype == qmod.TOKEN_WORD: if tlist.end < trange.end: chgpenalty = self.query.nodes[tlist.end].word_break_penalty \ + self.query.get_in_word_penalty( qmod.TokenRange(pos, tlist.end)) for t in tlist.tokens: heapq.heappush(todo, (-tlist.end, tlist.end, rank.with_token(t, chgpenalty))) elif tlist.end == trange.end: chgpenalty = self.query.get_in_word_penalty( qmod.TokenRange(pos, tlist.end)) ranks.extend(rank.with_token(t, chgpenalty) for t in tlist.tokens) if len(ranks) >= 10: # Too many variants, bail out and only add # Worst-case Fallback: sum of penalty of partials default = sum(t.penalty for t in self.query.iter_partials(trange)) + 0.2 default += sum(n.word_break_penalty for n in self.query.nodes[trange.start + 1:trange.end]) ranks.append(dbf.RankedTokens(rank.penalty + default, [])) # Bail out of outer loop break ranks.sort(key=lambda r: len(r.tokens)) default = ranks[0].penalty + 0.3 del ranks[0] ranks.sort(key=lambda r: r.penalty) return dbf.FieldRanking('nameaddress_vector', default, ranks) def get_search_data(self, assignment: TokenAssignment) -> Optional[dbf.SearchData]: """ Collect the tokens for the non-name search fields in the assignment. """ sdata = dbf.SearchData() sdata.penalty = assignment.penalty if assignment.country: tokens = self.get_country_tokens(assignment.country) if not tokens: return None sdata.set_countries(tokens) sdata.penalty += self.query.get_in_word_penalty(assignment.country) elif self.details.countries: sdata.countries = dbf.WeightedStrings(self.details.countries, [0.0] * len(self.details.countries)) if assignment.housenumber: sdata.set_strings('housenumbers', self.query.get_tokens(assignment.housenumber, qmod.TOKEN_HOUSENUMBER)) sdata.penalty += self.query.get_in_word_penalty(assignment.housenumber) if assignment.postcode: sdata.set_strings('postcodes', self.query.get_tokens(assignment.postcode, qmod.TOKEN_POSTCODE)) sdata.penalty += self.query.get_in_word_penalty(assignment.postcode) if assignment.qualifier: tokens = self.get_qualifier_tokens(assignment.qualifier) if not tokens: return None sdata.set_qualifiers(tokens) sdata.penalty += self.query.get_in_word_penalty(assignment.qualifier) elif self.details.categories: sdata.qualifiers = dbf.WeightedCategories(self.details.categories, [0.0] * len(self.details.categories)) if assignment.address: sdata.set_ranking([self.get_addr_ranking(r) for r in assignment.address]) else: sdata.rankings = [] return sdata def get_country_tokens(self, trange: qmod.TokenRange) -> List[qmod.Token]: """ Return the list of country tokens for the given range, optionally filtered by the country list from the details parameters. """ tokens = self.query.get_tokens(trange, qmod.TOKEN_COUNTRY) if self.details.countries: tokens = [t for t in tokens if t.get_country() in self.details.countries] return tokens def get_qualifier_tokens(self, trange: qmod.TokenRange) -> List[qmod.Token]: """ Return the list of qualifier tokens for the given range, optionally filtered by the qualifier list from the details parameters. """ tokens = self.query.get_tokens(trange, qmod.TOKEN_QUALIFIER) if self.details.categories: tokens = [t for t in tokens if t.get_category() in self.details.categories] return tokens def get_near_items(self, assignment: TokenAssignment) -> Optional[dbf.WeightedCategories]: """ Collect tokens for near items search or use the categories requested per parameter. Returns None if no category search is requested. """ if assignment.near_item: tokens: Dict[Tuple[str, str], float] = {} for t in self.query.get_tokens(assignment.near_item, qmod.TOKEN_NEAR_ITEM): cat = t.get_category() # The category of a near search will be that of near_item. # Thus, if search is restricted to a category parameter, # the two sets must intersect. if (not self.details.categories or cat in self.details.categories)\ and t.penalty < tokens.get(cat, 1000.0): tokens[cat] = t.penalty return dbf.WeightedCategories(list(tokens.keys()), list(tokens.values())) return None ================================================ FILE: src/nominatim_api/search/db_search_fields.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Data structures for more complex fields in abstract search descriptions. """ from typing import List, Tuple, Iterator, Dict, Type, cast import dataclasses import sqlalchemy as sa from ..typing import SaFromClause, SaColumn, SaExpression from ..utils.json_writer import JsonWriter from .query import Token from . import db_search_lookups as lookups class CountedTokenIDs: """ A list of token IDs with their respective counts, sorted from least frequent to most frequent. If a token count is one, then statistics are likely to be unavailable and a relatively high count is assumed instead. """ def __init__(self, tokens: Iterator[Token], count_column: str = 'count'): self.tokens = list({(cast(int, getattr(t, count_column)), t.token) for t in tokens}) self.tokens.sort(key=lambda t: t[0] if t[0] > 1 else 100000) def __len__(self) -> int: return len(self.tokens) def get_num_lookup_tokens(self, limit: int, fac: int) -> int: """ Suggest the number of tokens to be used for an index lookup. The idea here is to use as few items as possible while making sure the number of rows returned stays below 'limit' which makes recheck of the returned rows more expensive than adding another item for the index lookup. 'fac' is the factor by which the limit is increased every time a lookup item is added. If the list of tokens doesn't seem suitable at all for index lookup, -1 is returned. """ length = len(self.tokens) min_count = self.tokens[0][0] if min_count == 1: return min(length, 3) # no statistics available, use index for i in range(min(length, 3)): if min_count < limit: return i + 1 limit = limit * fac return -1 def min_count(self) -> int: return self.tokens[0][0] def expected_for_all_search(self, fac: int = 5) -> int: return int(self.tokens[0][0] / (fac**(len(self.tokens) - 1))) def get_tokens(self) -> List[int]: return [t[1] for t in self.tokens] def get_head_tokens(self, num_tokens: int) -> List[int]: return [t[1] for t in self.tokens[:num_tokens]] def get_tail_tokens(self, first: int) -> List[int]: return [t[1] for t in self.tokens[first:]] def split_lookup(self, split: int, column: str) -> 'List[FieldLookup]': lookup = [FieldLookup(column, self.get_head_tokens(split), lookups.LookupAll)] if split < len(self.tokens): lookup.append(FieldLookup(column, self.get_tail_tokens(split), lookups.Restrict)) return lookup @dataclasses.dataclass class WeightedStrings: """ A list of strings together with a penalty. """ values: List[str] penalties: List[float] def __bool__(self) -> bool: return bool(self.values) def __iter__(self) -> Iterator[Tuple[str, float]]: return iter(zip(self.values, self.penalties)) def get_penalty(self, value: str, default: float = 1000.0) -> float: """ Get the penalty for the given value. Returns the given default if the value does not exist. """ try: return self.penalties[self.values.index(value)] except ValueError: pass return default @dataclasses.dataclass class WeightedCategories: """ A list of class/type tuples together with a penalty. """ values: List[Tuple[str, str]] penalties: List[float] def __bool__(self) -> bool: return bool(self.values) def __iter__(self) -> Iterator[Tuple[Tuple[str, str], float]]: return iter(zip(self.values, self.penalties)) def get_penalty(self, value: Tuple[str, str], default: float = 1000.0) -> float: """ Get the penalty for the given value. Returns the given default if the value does not exist. """ try: return self.penalties[self.values.index(value)] except ValueError: pass return default def sql_restrict(self, table: SaFromClause) -> SaExpression: """ Return an SQLAlcheny expression that restricts the class and type columns of the given table to the values in the list. Must not be used with an empty list. """ assert self.values if len(self.values) == 1: return sa.and_(table.c.class_ == self.values[0][0], table.c.type == self.values[0][1]) return sa.or_(*(sa.and_(table.c.class_ == c, table.c.type == t) for c, t in self.values)) @dataclasses.dataclass(order=True) class RankedTokens: """ List of tokens together with the penalty of using it. """ penalty: float tokens: List[int] def with_token(self, t: Token, transition_penalty: float) -> 'RankedTokens': """ Create a new RankedTokens list with the given token appended. The tokens penalty as well as the given transition penalty are added to the overall penalty. """ return RankedTokens(self.penalty + t.penalty + transition_penalty, self.tokens + [t.token]) @dataclasses.dataclass class FieldRanking: """ A list of rankings to be applied sequentially until one matches. The matched ranking determines the penalty. If none matches a default penalty is applied. """ column: str default: float rankings: List[RankedTokens] def normalize_penalty(self) -> float: """ Reduce the default and ranking penalties, such that the minimum penalty is 0. Return the penalty that was subtracted. """ if self.rankings: min_penalty = min(self.default, min(r.penalty for r in self.rankings)) else: min_penalty = self.default if min_penalty > 0.0: self.default -= min_penalty for ranking in self.rankings: ranking.penalty -= min_penalty return min_penalty def sql_penalty(self, table: SaFromClause) -> SaColumn: """ Create an SQL expression for the rankings. """ assert self.rankings rout = JsonWriter().start_array() for rank in self.rankings: rout.start_array().value(rank.penalty).next() rout.start_array() for token in rank.tokens: rout.value(token).next() rout.end_array() rout.end_array().next() rout.end_array() return sa.func.weigh_search(table.c[self.column], rout(), self.default) @dataclasses.dataclass class FieldLookup: """ A list of tokens to be searched for. The column names the database column to search in and the lookup_type the operator that is applied. 'lookup_all' requires all tokens to match. 'lookup_any' requires one of the tokens to match. 'restrict' requires to match all tokens but avoids the use of indexes. """ column: str tokens: List[int] lookup_type: Type[lookups.LookupType] def sql_condition(self, table: SaFromClause) -> SaColumn: """ Create an SQL expression for the given match condition. """ return self.lookup_type(table, self.column, self.tokens) class SearchData: """ Search fields derived from query and token assignment to be used with the SQL queries. """ penalty: float lookups: List[FieldLookup] = [] rankings: List[FieldRanking] housenumbers: WeightedStrings = WeightedStrings([], []) postcodes: WeightedStrings = WeightedStrings([], []) countries: WeightedStrings = WeightedStrings([], []) qualifiers: WeightedCategories = WeightedCategories([], []) def set_strings(self, field: str, tokens: List[Token]) -> None: """ Set on of the WeightedStrings properties from the given token list. Adapt the global penalty, so that the minimum penalty is 0. """ if tokens: min_penalty = min(t.penalty for t in tokens) self.penalty += min_penalty wstrs = WeightedStrings([t.lookup_word for t in tokens], [t.penalty - min_penalty for t in tokens]) setattr(self, field, wstrs) def set_countries(self, tokens: List[Token]) -> None: """ Set the WeightedStrings properties for countries. Multiple entries for the same country are deduplicated and the minimum penalty is used. Adapts the global penalty, so that the minimum penalty is 0. """ if tokens: min_penalty = min(t.penalty for t in tokens) self.penalty += min_penalty countries: dict[str, float] = {} for t in tokens: cc = t.get_country() countries[cc] = min(t.penalty - min_penalty, countries.get(cc, 10000)) self.countries = WeightedStrings(list(countries.keys()), list(countries.values())) def set_qualifiers(self, tokens: List[Token]) -> None: """ Set the qulaifier field from the given tokens. """ if tokens: categories: Dict[Tuple[str, str], float] = {} min_penalty = 1000.0 for t in tokens: min_penalty = min(min_penalty, t.penalty) cat = t.get_category() if t.penalty < categories.get(cat, 1000.0): categories[cat] = t.penalty self.penalty += min_penalty self.qualifiers = WeightedCategories(list(categories.keys()), list(categories.values())) def set_ranking(self, rankings: List[FieldRanking]) -> None: """ Set the list of rankings and normalize the ranking. """ self.rankings = [] for ranking in rankings: if ranking.rankings: self.penalty += ranking.normalize_penalty() self.rankings.append(ranking) else: self.penalty += ranking.default def lookup_by_any_name(name_tokens: List[int], addr_restrict_tokens: List[int], addr_lookup_tokens: List[int]) -> List[FieldLookup]: """ Create a lookup list where name tokens are looked up via index and only one of the name tokens must be present. Potential address tokens are used to restrict the search further. """ lookup = [FieldLookup('name_vector', name_tokens, lookups.LookupAny)] if addr_restrict_tokens: lookup.append(FieldLookup('nameaddress_vector', addr_restrict_tokens, lookups.Restrict)) if addr_lookup_tokens: lookup.append(FieldLookup('nameaddress_vector', addr_lookup_tokens, lookups.LookupAll)) return lookup ================================================ FILE: src/nominatim_api/search/db_search_lookups.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of lookup functions for the search_name table. """ from typing import List, Any import sqlalchemy as sa from sqlalchemy.ext.compiler import compiles from ..typing import SaFromClause from ..sql.sqlalchemy_types import IntArray LookupType = sa.sql.expression.FunctionElement[Any] class LookupAll(LookupType): """ Find all entries in search_name table that contain all of a given list of tokens using an index for the search. """ inherit_cache = True def __init__(self, table: SaFromClause, column: str, tokens: List[int]) -> None: super().__init__(table.c.place_id, getattr(table.c, column), column, sa.type_coerce(tokens, IntArray)) @compiles(LookupAll) def _default_lookup_all(element: LookupAll, compiler: 'sa.Compiled', **kw: Any) -> str: _, col, _, tokens = list(element.clauses) return "(%s @> %s)" % (compiler.process(col, **kw), compiler.process(tokens, **kw)) @compiles(LookupAll, 'sqlite') def _sqlite_lookup_all(element: LookupAll, compiler: 'sa.Compiled', **kw: Any) -> str: place, col, colname, tokens = list(element.clauses) return "(%s IN (SELECT CAST(value as bigint) FROM"\ " (SELECT array_intersect_fuzzy(places) as p FROM"\ " (SELECT places FROM reverse_search_name"\ " WHERE word IN (SELECT value FROM json_each('[' || %s || ']'))"\ " AND column = %s"\ " ORDER BY length(places)) as x) as u,"\ " json_each('[' || u.p || ']'))"\ " AND array_contains(%s, %s))"\ % (compiler.process(place, **kw), compiler.process(tokens, **kw), compiler.process(colname, **kw), compiler.process(col, **kw), compiler.process(tokens, **kw)) class LookupAny(LookupType): """ Find all entries that contain at least one of the given tokens. Use an index for the search. """ inherit_cache = True def __init__(self, table: SaFromClause, column: str, tokens: List[int]) -> None: super().__init__(table.c.place_id, getattr(table.c, column), column, sa.type_coerce(tokens, IntArray)) @compiles(LookupAny) def _default_lookup_any(element: LookupAny, compiler: 'sa.Compiled', **kw: Any) -> str: _, col, _, tokens = list(element.clauses) return "(%s && %s)" % (compiler.process(col, **kw), compiler.process(tokens, **kw)) @compiles(LookupAny, 'sqlite') def _sqlite_lookup_any(element: LookupAny, compiler: 'sa.Compiled', **kw: Any) -> str: place, _, colname, tokens = list(element.clauses) return "%s IN (SELECT CAST(value as bigint) FROM"\ " (SELECT array_union(places) as p FROM reverse_search_name"\ " WHERE word IN (SELECT value FROM json_each('[' || %s || ']'))"\ " AND column = %s) as u,"\ " json_each('[' || u.p || ']'))" % (compiler.process(place, **kw), compiler.process(tokens, **kw), compiler.process(colname, **kw)) class Restrict(LookupType): """ Find all entries that contain all of the given tokens. Do not use an index for the search. """ inherit_cache = True def __init__(self, table: SaFromClause, column: str, tokens: List[int]) -> None: super().__init__(getattr(table.c, column), sa.type_coerce(tokens, IntArray)) @compiles(Restrict) def _default_restrict(element: Restrict, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "(coalesce(null, %s) @> %s)" % (compiler.process(arg1, **kw), compiler.process(arg2, **kw)) @compiles(Restrict, 'sqlite') def _sqlite_restrict(element: Restrict, compiler: 'sa.Compiled', **kw: Any) -> str: return "array_contains(%s)" % compiler.process(element.clauses, **kw) ================================================ FILE: src/nominatim_api/search/db_searches/__init__.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Module implementing the actual database accesses for forward search. """ from .base import AbstractSearch as AbstractSearch from .near_search import NearSearch as NearSearch from .poi_search import PoiSearch as PoiSearch from .country_search import CountrySearch as CountrySearch from .postcode_search import PostcodeSearch as PostcodeSearch from .place_search import PlaceSearch as PlaceSearch from .address_search import AddressSearch as AddressSearch ================================================ FILE: src/nominatim_api/search/db_searches/address_search.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2026 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of search for an address (search with housenumber). """ from typing import cast, List, AsyncIterator import sqlalchemy as sa from . import base from ...typing import SaBind, SaExpression, SaColumn, SaFromClause, SaScalarSelect from ...types import SearchDetails, Bbox from ...sql.sqlalchemy_types import Geometry from ...connection import SearchConnection from ... import results as nres from ..db_search_fields import SearchData LIMIT_PARAM: SaBind = sa.bindparam('limit') MIN_RANK_PARAM: SaBind = sa.bindparam('min_rank') MAX_RANK_PARAM: SaBind = sa.bindparam('max_rank') VIEWBOX_PARAM: SaBind = sa.bindparam('viewbox', type_=Geometry) VIEWBOX2_PARAM: SaBind = sa.bindparam('viewbox2', type_=Geometry) NEAR_PARAM: SaBind = sa.bindparam('near', type_=Geometry) NEAR_RADIUS_PARAM: SaBind = sa.bindparam('near_radius') COUNTRIES_PARAM: SaBind = sa.bindparam('countries') def _int_list_to_subquery(inp: List[int]) -> 'sa.Subquery': """ Create a subselect that returns the given list of integers as rows in the column 'nr'. """ vtab = sa.func.JsonArrayEach(sa.type_coerce(inp, sa.JSON))\ .table_valued(sa.column('value', type_=sa.JSON)) return sa.select(sa.cast(sa.cast(vtab.c.value, sa.Text), sa.Integer).label('nr')).subquery() def _interpolated_position(table: SaFromClause, nr: SaColumn) -> SaColumn: pos = sa.cast(nr - table.c.startnumber, sa.Float) / (table.c.endnumber - table.c.startnumber) return sa.case( (table.c.endnumber == table.c.startnumber, table.c.linegeo.ST_Centroid()), else_=table.c.linegeo.ST_LineInterpolatePoint(pos)).label('centroid') def _make_interpolation_subquery(table: SaFromClause, inner: SaFromClause, numerals: List[int], details: SearchDetails) -> SaScalarSelect: all_ids = sa.func.ArrayAgg(table.c.place_id) sql = sa.select(all_ids).where(table.c.parent_place_id == inner.c.place_id) if len(numerals) == 1: sql = sql.where(sa.between(numerals[0], table.c.startnumber, table.c.endnumber))\ .where((numerals[0] - table.c.startnumber) % table.c.step == 0) else: sql = sql.where(sa.or_( *(sa.and_(sa.between(n, table.c.startnumber, table.c.endnumber), (n - table.c.startnumber) % table.c.step == 0) for n in numerals))) if details.excluded: sql = sql.where(base.exclude_places(table)) sql = sql.where(table.c.parent_place_id.not_in(sa.bindparam('excluded'))) return sql.scalar_subquery() async def _get_placex_housenumbers(conn: SearchConnection, place_ids: List[int], details: SearchDetails) -> AsyncIterator[nres.SearchResult]: t = conn.t.placex sql = base.select_placex(t).add_columns(t.c.importance)\ .where(t.c.place_id.in_(place_ids)) if details.geometry_output: sql = base.add_geometry_columns(sql, t.c.geometry, details) for row in await conn.execute(sql): result = nres.create_from_placex_row(row, nres.SearchResult) result.bbox = Bbox.from_wkb(row.bbox) yield result async def _get_osmline(conn: SearchConnection, place_ids: List[int], numerals: List[int], details: SearchDetails) -> AsyncIterator[nres.SearchResult]: t = conn.t.osmline values = _int_list_to_subquery(numerals) sql = sa.select(t.c.place_id, t.c.osm_id, t.c.parent_place_id, t.c.address, values.c.nr.label('housenumber'), _interpolated_position(t, values.c.nr), t.c.postcode, t.c.country_code)\ .where(t.c.place_id.in_(place_ids))\ .join(values, values.c.nr.between(t.c.startnumber, t.c.endnumber)) if details.geometry_output: sub = sql.subquery() sql = base.add_geometry_columns(sa.select(sub), sub.c.centroid, details) for row in await conn.execute(sql): yield nres.create_from_osmline_row(row, nres.SearchResult) async def _get_tiger(conn: SearchConnection, place_ids: List[int], numerals: List[int], osm_id: int, details: SearchDetails) -> AsyncIterator[nres.SearchResult]: t = conn.t.tiger values = _int_list_to_subquery(numerals) sql = sa.select(t.c.place_id, t.c.parent_place_id, sa.literal('W').label('osm_type'), sa.literal(osm_id).label('osm_id'), values.c.nr.label('housenumber'), _interpolated_position(t, values.c.nr), t.c.postcode)\ .where(t.c.place_id.in_(place_ids))\ .join(values, values.c.nr.between(t.c.startnumber, t.c.endnumber)) if details.geometry_output: sub = sql.subquery() sql = base.add_geometry_columns(sa.select(sub), sub.c.centroid, details) for row in await conn.execute(sql): yield nres.create_from_tiger_row(row, nres.SearchResult) class AddressSearch(base.AbstractSearch): """ Generic search for an address or named place. """ SEARCH_PRIO = 1 def __init__(self, extra_penalty: float, sdata: SearchData, expected_count: int, has_address_terms: bool) -> None: assert sdata.housenumbers super().__init__(sdata.penalty + extra_penalty) self.countries = sdata.countries self.postcodes = sdata.postcodes self.housenumbers = sdata.housenumbers self.qualifiers = sdata.qualifiers self.lookups = sdata.lookups self.rankings = sdata.rankings self.expected_count = expected_count self.has_address_terms = has_address_terms def _inner_search_name_cte(self, conn: SearchConnection, details: SearchDetails) -> 'sa.CTE': """ Create a subquery that preselects the rows in the search_name table. """ t = conn.t.search_name penalty: SaExpression = sa.literal(self.penalty) for ranking in self.rankings: penalty += ranking.sql_penalty(t) sql = sa.select(t.c.place_id, penalty.label('penalty')) for lookup in self.lookups: sql = sql.where(lookup.sql_condition(t)) if self.countries: sql = sql.where(t.c.country_code.in_(self.countries.values)) if self.postcodes: if self.expected_count > 10000: tpc = conn.t.postcode sql = sql.where(sa.select(tpc.c.postcode) .where(tpc.c.postcode.in_(self.postcodes.values)) .where(tpc.c.country_code == t.c.country_code) .where(t.c.centroid.intersects(tpc.c.geometry, use_index=False)) .exists()) if details.viewbox is not None: if details.bounded_viewbox: sql = sql.where(t.c.centroid .intersects(VIEWBOX_PARAM, use_index=details.viewbox.area < 0.2)) if details.near is not None and details.near_radius is not None: if details.near_radius < 0.1: sql = sql.where(t.c.centroid.within_distance(NEAR_PARAM, NEAR_RADIUS_PARAM)) else: sql = sql.where(t.c.centroid .ST_Distance(NEAR_PARAM) < NEAR_RADIUS_PARAM) if self.has_address_terms: sql = sql.where(t.c.address_rank.between(16, 30)) else: # If no further address terms are given, then the base street must # be in the name. No search for named POIs with the given house number. sql = sql.where(t.c.address_rank.between(16, 27)) inner = sql.limit(10000).order_by(sa.desc(sa.text('importance'))).subquery() sql = sa.select(inner.c.place_id, inner.c.penalty) return sql.cte('searches') async def lookup(self, conn: SearchConnection, details: SearchDetails) -> nres.SearchResults: """ Find results for the search in the database. """ t = conn.t.placex tsearch = self._inner_search_name_cte(conn, details) sql = base.select_placex(t).join(tsearch, t.c.place_id == tsearch.c.place_id) if details.geometry_output: sql = base.add_geometry_columns(sql, t.c.geometry, details) penalty: SaExpression = tsearch.c.penalty if self.postcodes: tpc = conn.t.postcode pcs = self.postcodes.values pc_near = sa.select(sa.func.min(tpc.c.centroid.ST_Distance(t.c.centroid) * (tpc.c.rank_search - 19)))\ .where(tpc.c.postcode.in_(pcs))\ .where(tpc.c.country_code == t.c.country_code)\ .scalar_subquery() penalty += sa.case((t.c.postcode.in_(pcs), 0.0), else_=sa.func.coalesce(pc_near, cast(SaColumn, 2.0))) if details.viewbox is not None and not details.bounded_viewbox: penalty += sa.case((t.c.geometry.intersects(VIEWBOX_PARAM, use_index=False), 0.0), (t.c.geometry.intersects(VIEWBOX2_PARAM, use_index=False), 0.5), else_=1.0) if details.near is not None: sql = sql.add_columns((-t.c.centroid.ST_Distance(NEAR_PARAM)) .label('importance')) sql = sql.order_by(sa.desc(sa.text('importance'))) else: sql = sql.order_by(penalty - t.c.importance) sql = sql.add_columns(t.c.importance) sql = sql.add_columns(penalty.label('accuracy'))\ .order_by(sa.text('accuracy')) hnr_list = '|'.join(self.housenumbers.values) if self.has_address_terms: sql = sql.where(sa.or_(t.c.rank_address < 30, sa.func.RegexpWord(hnr_list, t.c.housenumber))) inner = sql.subquery() # Housenumbers from placex thnr = conn.t.placex.alias('hnr') pid_list = sa.func.ArrayAgg(thnr.c.place_id) place_sql = sa.select(pid_list)\ .where(thnr.c.parent_place_id == inner.c.place_id)\ .where(sa.func.RegexpWord(hnr_list, thnr.c.housenumber))\ .where(thnr.c.linked_place_id == None)\ .where(thnr.c.indexed_status == 0) if details.excluded: place_sql = place_sql.where(thnr.c.place_id.not_in(sa.bindparam('excluded'))) if self.qualifiers: place_sql = place_sql.where(self.qualifiers.sql_restrict(thnr)) numerals = [int(n) for n in self.housenumbers.values if n.isdigit() and len(n) < 8] interpol_sql: SaColumn tiger_sql: SaColumn if numerals and \ (not self.qualifiers or ('place', 'house') in self.qualifiers.values): # Housenumbers from interpolations interpol_sql = _make_interpolation_subquery(conn.t.osmline, inner, numerals, details) # Housenumbers from Tiger tiger_sql = sa.case((inner.c.country_code == 'us', _make_interpolation_subquery(conn.t.tiger, inner, numerals, details) ), else_=None) else: interpol_sql = sa.null() tiger_sql = sa.null() unsort = sa.select(inner, place_sql.scalar_subquery().label('placex_hnr'), interpol_sql.label('interpol_hnr'), tiger_sql.label('tiger_hnr')).subquery('unsort') sql = sa.select(unsort)\ .order_by(unsort.c.accuracy + sa.case((unsort.c.placex_hnr != None, 0), (unsort.c.interpol_hnr != None, 0), (unsort.c.tiger_hnr != None, 0), else_=1), sa.case((unsort.c.placex_hnr != None, 1), (unsort.c.interpol_hnr != None, 2), (unsort.c.tiger_hnr != None, 3), else_=4)) sql = sql.limit(LIMIT_PARAM) bind_params = { 'limit': details.max_results, 'min_rank': details.min_rank, 'max_rank': details.max_rank, 'viewbox': details.viewbox, 'viewbox2': details.viewbox_x2, 'near': details.near, 'near_radius': details.near_radius, 'excluded': details.excluded_place_ids, 'countries': details.countries } results = nres.SearchResults() for row in await conn.execute(sql, bind_params): result = nres.create_from_placex_row(row, nres.SearchResult) result.bbox = Bbox.from_wkb(row.bbox) result.accuracy = row.accuracy if row.rank_address < 30: if row.placex_hnr: subs = _get_placex_housenumbers(conn, row.placex_hnr, details) elif row.interpol_hnr: subs = _get_osmline(conn, row.interpol_hnr, numerals, details) elif row.tiger_hnr: subs = _get_tiger(conn, row.tiger_hnr, numerals, row.osm_id, details) else: subs = None if subs is not None: async for sub in subs: assert sub.housenumber sub.accuracy = result.accuracy if not any(nr in self.housenumbers.values for nr in sub.housenumber.split(';')): sub.accuracy += 0.6 results.append(sub) # Only add the street as a result, if it meets all other # filter conditions. if (not details.excluded or result.place_id not in details.excluded_place_ids)\ and (not self.qualifiers or result.category in self.qualifiers.values)\ and result.rank_address >= details.min_rank: result.accuracy += 1.0 # penalty for missing housenumber results.append(result) else: results.append(result) return results ================================================ FILE: src/nominatim_api/search/db_searches/base.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Interface for classes implementing a database search. """ from typing import Callable, List import abc import sqlalchemy as sa from ...typing import SaFromClause, SaSelect, SaColumn, SaExpression, SaLambdaSelect from ...sql.sqlalchemy_types import Geometry from ...connection import SearchConnection from ...types import SearchDetails, DataLayer, GeometryFormat from ...results import SearchResults class AbstractSearch(abc.ABC): """ Encapuslation of a single lookup in the database. """ SEARCH_PRIO: int = 2 def __init__(self, penalty: float) -> None: self.penalty = penalty @abc.abstractmethod async def lookup(self, conn: SearchConnection, details: SearchDetails) -> SearchResults: """ Find results for the search in the database. """ def select_placex(t: SaFromClause) -> SaSelect: """ Return the basic select query for placex which returns all fields necessary to fill a Nominatim result. 't' must either be the placex table or a subquery returning appropriate fields from a placex-related query. """ return sa.select(t.c.place_id, t.c.osm_type, t.c.osm_id, t.c.name, t.c.class_, t.c.type, t.c.address, t.c.extratags, t.c.housenumber, t.c.postcode, t.c.country_code, t.c.wikipedia, t.c.parent_place_id, t.c.rank_address, t.c.rank_search, t.c.linked_place_id, t.c.admin_level, t.c.centroid, t.c.geometry.ST_Expand(0).label('bbox')) def exclude_places(t: SaFromClause) -> Callable[[], SaExpression]: """ Return an expression to exclude place IDs and OSM IDs from the list in the SearchDetails. Requires the excluded IDs to be supplied as a bind parameter in SQL. """ return lambda: t.c.place_id.not_in(sa.bindparam('excluded')) def filter_by_layer(table: SaFromClause, layers: DataLayer) -> SaColumn: """ Return an expression that filters the given table by layers. """ orexpr: List[SaExpression] = [] if layers & DataLayer.ADDRESS and layers & DataLayer.POI: orexpr.append(no_index(table.c.rank_address).between(1, 30)) elif layers & DataLayer.ADDRESS: orexpr.append(no_index(table.c.rank_address).between(1, 29)) orexpr.append(sa.func.IsAddressPoint(table)) elif layers & DataLayer.POI: orexpr.append(sa.and_(no_index(table.c.rank_address) == 30, table.c.class_.not_in(('place', 'building')))) if layers & DataLayer.MANMADE: exclude = [] if not layers & DataLayer.RAILWAY: exclude.append('railway') if not layers & DataLayer.NATURAL: exclude.extend(('natural', 'water', 'waterway')) orexpr.append(sa.and_(table.c.class_.not_in(tuple(exclude)), no_index(table.c.rank_address) == 0)) else: include = [] if layers & DataLayer.RAILWAY: include.append('railway') if layers & DataLayer.NATURAL: include.extend(('natural', 'water', 'waterway')) orexpr.append(sa.and_(table.c.class_.in_(tuple(include)), no_index(table.c.rank_address) == 0)) if len(orexpr) == 1: return orexpr[0] return sa.or_(*orexpr) def no_index(expr: SaColumn) -> SaColumn: """ Wrap the given expression, so that the query planner will refrain from using the expression for index lookup. """ return sa.func.coalesce(sa.null(), expr) def filter_by_area(sql: SaSelect, t: SaFromClause, details: SearchDetails, avoid_index: bool = False) -> SaSelect: """ Apply SQL statements for filtering by viewbox and near point, if applicable. """ if details.near is not None and details.near_radius is not None: if details.near_radius < 0.1 and not avoid_index: sql = sql.where( t.c.geometry.within_distance(sa.bindparam('near', type_=Geometry), sa.bindparam('near_radius'))) else: sql = sql.where( t.c.geometry.ST_Distance( sa.bindparam('near', type_=Geometry)) <= sa.bindparam('near_radius')) if details.viewbox is not None and details.bounded_viewbox: sql = sql.where(t.c.geometry.intersects(sa.bindparam('viewbox', type_=Geometry), use_index=not avoid_index and details.viewbox.area < 0.2)) return sql def add_geometry_columns(sql: SaLambdaSelect, col: SaColumn, details: SearchDetails) -> SaSelect: """ Add columns for requested geometry formats and return the new query. """ out = [] if details.geometry_simplification > 0.0: col = sa.func.ST_SimplifyPreserveTopology(col, details.geometry_simplification) if details.geometry_output & GeometryFormat.GEOJSON: out.append(sa.func.ST_AsGeoJSON(col, 7).label('geometry_geojson')) if details.geometry_output & GeometryFormat.TEXT: out.append(sa.func.ST_AsText(col).label('geometry_text')) if details.geometry_output & GeometryFormat.KML: out.append(sa.func.ST_AsKML(col, 7).label('geometry_kml')) if details.geometry_output & GeometryFormat.SVG: out.append(sa.func.ST_AsSVG(col, 0, 7).label('geometry_svg')) return sql.add_columns(*out) ================================================ FILE: src/nominatim_api/search/db_searches/country_search.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of searches for a country. """ import sqlalchemy as sa from . import base from ..db_search_fields import SearchData from ... import results as nres from ...connection import SearchConnection from ...types import SearchDetails, Bbox class CountrySearch(base.AbstractSearch): """ Search for a country name or country code. """ SEARCH_PRIO = 0 def __init__(self, sdata: SearchData) -> None: super().__init__(sdata.penalty) self.countries = sdata.countries async def lookup(self, conn: SearchConnection, details: SearchDetails) -> nres.SearchResults: """ Find results for the search in the database. """ t = conn.t.placex ccodes = self.countries.values sql = base.select_placex(t)\ .add_columns(t.c.importance)\ .where(t.c.country_code.in_(ccodes))\ .where(t.c.rank_address == 4) if details.geometry_output: sql = base.add_geometry_columns(sql, t.c.geometry, details) if details.excluded: sql = sql.where(base.exclude_places(t)) sql = base.filter_by_area(sql, t, details) bind_params = { 'excluded': details.excluded_place_ids, 'viewbox': details.viewbox, 'near': details.near, 'near_radius': details.near_radius } results = nres.SearchResults() for row in await conn.execute(sql, bind_params): result = nres.create_from_placex_row(row, nres.SearchResult) result.accuracy = self.penalty + self.countries.get_penalty(row.country_code, 5.0) result.bbox = Bbox.from_wkb(row.bbox) results.append(result) if not results: results = await self.lookup_in_country_table(conn, details) if results: details.min_rank = min(5, details.max_rank) details.max_rank = min(25, details.max_rank) return results async def lookup_in_country_table(self, conn: SearchConnection, details: SearchDetails) -> nres.SearchResults: """ Look up the country in the fallback country tables. """ # Avoid the fallback search when this is a more search. Country results # usually are in the first batch of results and it is not possible # to exclude these fallbacks. if details.excluded: return nres.SearchResults() t = conn.t.country_name tgrid = conn.t.country_grid sql = sa.select(tgrid.c.country_code, tgrid.c.geometry.ST_Centroid().ST_Collect().ST_Centroid() .label('centroid'), tgrid.c.geometry.ST_Collect().ST_Expand(0).label('bbox'))\ .where(tgrid.c.country_code.in_(self.countries.values))\ .group_by(tgrid.c.country_code) sql = base.filter_by_area(sql, tgrid, details, avoid_index=True) sub = sql.subquery('grid') sql = sa.select(t.c.country_code, t.c.name.merge(t.c.derived_name).label('name'), sub.c.centroid, sub.c.bbox)\ .join(sub, t.c.country_code == sub.c.country_code) if details.geometry_output: sql = base.add_geometry_columns(sql, sub.c.centroid, details) bind_params = { 'viewbox': details.viewbox, 'near': details.near, 'near_radius': details.near_radius } results = nres.SearchResults() for row in await conn.execute(sql, bind_params): result = nres.create_from_country_row(row, nres.SearchResult) result.bbox = Bbox.from_wkb(row.bbox) result.accuracy = self.penalty + self.countries.get_penalty(row.country_code, 5.0) results.append(result) return results ================================================ FILE: src/nominatim_api/search/db_searches/near_search.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of a category search around a place. """ from typing import List, Tuple import sqlalchemy as sa from . import base from ...typing import SaBind from ...types import SearchDetails, Bbox from ...connection import SearchConnection from ... import results as nres from ..db_search_fields import WeightedCategories LIMIT_PARAM: SaBind = sa.bindparam('limit') MIN_RANK_PARAM: SaBind = sa.bindparam('min_rank') MAX_RANK_PARAM: SaBind = sa.bindparam('max_rank') COUNTRIES_PARAM: SaBind = sa.bindparam('countries') class NearSearch(base.AbstractSearch): """ Category search of a place type near the result of another search. """ def __init__(self, penalty: float, categories: WeightedCategories, search: base.AbstractSearch) -> None: super().__init__(penalty) self.search = search self.categories = categories async def lookup(self, conn: SearchConnection, details: SearchDetails) -> nres.SearchResults: """ Find results for the search in the database. """ results = nres.SearchResults() base = await self.search.lookup(conn, details) if not base: return results base.sort(key=lambda r: (r.accuracy, r.rank_search)) max_accuracy = base[0].accuracy + 0.5 if base[0].rank_address == 0: min_rank = 0 max_rank = 0 elif base[0].rank_address < 26: min_rank = 1 max_rank = min(25, base[0].rank_address + 4) else: min_rank = 26 max_rank = 30 base = nres.SearchResults(r for r in base if (r.source_table == nres.SourceTable.PLACEX and r.accuracy <= max_accuracy and r.bbox and r.bbox.area < 20 and r.rank_address >= min_rank and r.rank_address <= max_rank)) if base: baseids = [b.place_id for b in base[:5] if b.place_id] for category, penalty in self.categories: await self.lookup_category(results, conn, baseids, category, penalty, details) if len(results) >= details.max_results: break return results async def lookup_category(self, results: nres.SearchResults, conn: SearchConnection, ids: List[int], category: Tuple[str, str], penalty: float, details: SearchDetails) -> None: """ Find places of the given category near the list of place ids and add the results to 'results'. """ table = await conn.get_class_table(*category) tgeom = conn.t.placex.alias('pgeom') if table is None: # No classtype table available, do a simplified lookup in placex. table = conn.t.placex sql = sa.select(table.c.place_id, sa.func.min(tgeom.c.centroid.ST_Distance(table.c.centroid)) .label('dist'))\ .join(tgeom, table.c.geometry.intersects(tgeom.c.centroid.ST_Expand(0.01)))\ .where(table.c.class_ == category[0])\ .where(table.c.type == category[1]) else: # Use classtype table. We can afford to use a larger # radius for the lookup. sql = sa.select(table.c.place_id, sa.func.min(tgeom.c.centroid.ST_Distance(table.c.centroid)) .label('dist'))\ .join(tgeom, table.c.centroid.ST_CoveredBy( sa.case((sa.and_(tgeom.c.rank_address > 9, tgeom.c.geometry.is_area()), tgeom.c.geometry), else_=tgeom.c.centroid.ST_Expand(0.05)))) inner = sql.where(tgeom.c.place_id.in_(ids))\ .group_by(table.c.place_id).subquery() t = conn.t.placex sql = base.select_placex(t).add_columns((-inner.c.dist).label('importance'))\ .join(inner, inner.c.place_id == t.c.place_id)\ .order_by(inner.c.dist) sql = sql.where(base.no_index(t.c.rank_address).between(MIN_RANK_PARAM, MAX_RANK_PARAM)) if details.countries: sql = sql.where(t.c.country_code.in_(COUNTRIES_PARAM)) if details.excluded: sql = sql.where(base.exclude_places(t)) if details.layers is not None: sql = sql.where(base.filter_by_layer(t, details.layers)) sql = sql.limit(LIMIT_PARAM) bind_params = {'limit': details.max_results, 'min_rank': details.min_rank, 'max_rank': details.max_rank, 'excluded': details.excluded_place_ids, 'countries': details.countries} for row in await conn.execute(sql, bind_params): result = nres.create_from_placex_row(row, nres.SearchResult) result.accuracy = self.penalty + penalty result.bbox = Bbox.from_wkb(row.bbox) results.append(result) ================================================ FILE: src/nominatim_api/search/db_searches/place_search.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2026 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of search for a named place (without housenumber). """ from typing import cast import sqlalchemy as sa from . import base from ...typing import SaBind, SaExpression, SaColumn from ...types import SearchDetails, Bbox from ...sql.sqlalchemy_types import Geometry from ...connection import SearchConnection from ... import results as nres from ..db_search_fields import SearchData LIMIT_PARAM: SaBind = sa.bindparam('limit') MIN_RANK_PARAM: SaBind = sa.bindparam('min_rank') MAX_RANK_PARAM: SaBind = sa.bindparam('max_rank') VIEWBOX_PARAM: SaBind = sa.bindparam('viewbox', type_=Geometry) VIEWBOX2_PARAM: SaBind = sa.bindparam('viewbox2', type_=Geometry) NEAR_PARAM: SaBind = sa.bindparam('near', type_=Geometry) NEAR_RADIUS_PARAM: SaBind = sa.bindparam('near_radius') COUNTRIES_PARAM: SaBind = sa.bindparam('countries') class PlaceSearch(base.AbstractSearch): """ Generic search for a named place. """ SEARCH_PRIO = 1 def __init__(self, extra_penalty: float, sdata: SearchData, expected_count: int, has_address_terms: bool) -> None: assert not sdata.housenumbers super().__init__(sdata.penalty + extra_penalty) self.countries = sdata.countries self.postcodes = sdata.postcodes self.qualifiers = sdata.qualifiers self.lookups = sdata.lookups self.rankings = sdata.rankings self.expected_count = expected_count self.has_address_terms = has_address_terms def _inner_search_name_cte(self, conn: SearchConnection, details: SearchDetails) -> 'sa.CTE': """ Create a subquery that preselects the rows in the search_name table. """ t = conn.t.search_name penalty: SaExpression = sa.literal(self.penalty) for ranking in self.rankings: penalty += ranking.sql_penalty(t) sql = sa.select(t.c.place_id, t.c.importance) for lookup in self.lookups: sql = sql.where(lookup.sql_condition(t)) if self.countries: sql = sql.where(t.c.country_code.in_(self.countries.values)) if self.postcodes: # if a postcode is given, don't search for state or country level objects sql = sql.where(t.c.address_rank > 9) if self.expected_count > 10000: # Many results expected. Restrict by postcode. tpc = conn.t.postcode sql = sql.where(sa.select(tpc.c.postcode) .where(tpc.c.postcode.in_(self.postcodes.values)) .where(t.c.centroid.intersects(tpc.c.geometry, use_index=False)) .exists()) if details.viewbox is not None: if details.bounded_viewbox: sql = sql.where(t.c.centroid .intersects(VIEWBOX_PARAM, use_index=details.viewbox.area < 0.2)) else: penalty += sa.case((t.c.centroid.intersects(VIEWBOX_PARAM, use_index=False), 0.0), (t.c.centroid.intersects(VIEWBOX2_PARAM, use_index=False), 0.5), else_=1.0) if details.near is not None and details.near_radius is not None: if details.near_radius < 0.1: sql = sql.where(t.c.centroid.within_distance(NEAR_PARAM, NEAR_RADIUS_PARAM)) else: sql = sql.where(t.c.centroid .ST_Distance(NEAR_PARAM) < NEAR_RADIUS_PARAM) if details.excluded: sql = sql.where(base.exclude_places(t)) # Do not restrict ranks too much yet because rank restriction # currently also depends on search_rank to account for state-cities # like Berlin. if details.max_rank < 26: sql = sql.where(t.c.address_rank < 26) elif details.max_rank < 30: sql = sql.where(t.c.address_rank < MAX_RANK_PARAM) sql = sql.add_columns(penalty.label('penalty')) inner = sql.limit(5000 if self.qualifiers else 1000)\ .order_by(sa.desc(sa.text('importance')))\ .subquery() sql = sa.select(inner.c.place_id, inner.c.importance, inner.c.penalty) # If the query has no geographic preference, # preselect most important items to restrict the number of places # that need to be looked up in placex. if (details.viewbox is None or not details.bounded_viewbox)\ and (details.near is None or details.near_radius is None)\ and not self.qualifiers: sql = sql.add_columns(sa.func.first_value(inner.c.penalty - inner.c.importance) .over(order_by=inner.c.penalty - inner.c.importance) .label('min_penalty')) inner = sql.subquery() sql = sa.select(inner.c.place_id, inner.c.penalty)\ .where(inner.c.penalty - inner.c.importance < inner.c.min_penalty + 0.5) return sql.cte('searches') async def lookup(self, conn: SearchConnection, details: SearchDetails) -> nres.SearchResults: """ Find results for the search in the database. """ t = conn.t.placex tsearch = self._inner_search_name_cte(conn, details) sql = base.select_placex(t).join(tsearch, t.c.place_id == tsearch.c.place_id) if details.geometry_output: sql = base.add_geometry_columns(sql, t.c.geometry, details) penalty: SaExpression = tsearch.c.penalty if self.postcodes: if self.has_address_terms: tpc = conn.t.postcode pcs = self.postcodes.values pc_near = sa.select(sa.func.min(tpc.c.centroid.ST_Distance(t.c.centroid)))\ .where(tpc.c.postcode.in_(pcs))\ .scalar_subquery() penalty += sa.case((t.c.postcode.in_(pcs), 0.0), else_=sa.func.coalesce(pc_near, cast(SaColumn, 2.0))) else: # High penalty if the postcode is not an exact match. # The postcode search needs to get priority here. penalty += sa.case((t.c.postcode.in_(self.postcodes.values), 0.0), else_=1.0) if details.near is not None: sql = sql.add_columns((-t.c.centroid.ST_Distance(NEAR_PARAM)) .label('importance')) sql = sql.order_by(sa.desc(sa.text('importance'))) else: sql = sql.order_by(penalty - t.c.importance) sql = sql.add_columns(t.c.importance) if details.min_rank > 0: sql = sql.where(sa.or_(t.c.rank_address >= MIN_RANK_PARAM, t.c.rank_search >= MIN_RANK_PARAM)) if details.max_rank < 30: sql = sql.where(sa.or_(t.c.rank_address <= MAX_RANK_PARAM, t.c.rank_search <= MAX_RANK_PARAM)) sql = sql.add_columns(penalty.label('accuracy'))\ .order_by(sa.text('accuracy')) sql = sql.where(t.c.linked_place_id == None)\ .where(t.c.indexed_status == 0) if self.qualifiers: sql = sql.where(self.qualifiers.sql_restrict(t)) if details.layers is not None: sql = sql.where(base.filter_by_layer(t, details.layers)) sql = sql.limit(LIMIT_PARAM) bind_params = { 'limit': details.max_results, 'min_rank': details.min_rank, 'max_rank': details.max_rank, 'viewbox': details.viewbox, 'viewbox2': details.viewbox_x2, 'near': details.near, 'near_radius': details.near_radius, 'excluded': details.excluded_place_ids, 'countries': details.countries } results = nres.SearchResults() for row in await conn.execute(sql, bind_params): result = nres.create_from_placex_row(row, nres.SearchResult) result.bbox = Bbox.from_wkb(row.bbox) result.accuracy = row.accuracy results.append(result) return results ================================================ FILE: src/nominatim_api/search/db_searches/poi_search.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of category search. """ from typing import List import sqlalchemy as sa from . import base from ..db_search_fields import SearchData from ... import results as nres from ...typing import SaBind, SaRow, SaSelect, SaLambdaSelect from ...sql.sqlalchemy_types import Geometry from ...connection import SearchConnection from ...types import SearchDetails, Bbox LIMIT_PARAM: SaBind = sa.bindparam('limit') VIEWBOX_PARAM: SaBind = sa.bindparam('viewbox', type_=Geometry) NEAR_PARAM: SaBind = sa.bindparam('near', type_=Geometry) NEAR_RADIUS_PARAM: SaBind = sa.bindparam('near_radius') class PoiSearch(base.AbstractSearch): """ Category search in a geographic area. """ def __init__(self, sdata: SearchData) -> None: super().__init__(sdata.penalty) self.qualifiers = sdata.qualifiers self.countries = sdata.countries async def lookup(self, conn: SearchConnection, details: SearchDetails) -> nres.SearchResults: """ Find results for the search in the database. """ bind_params = { 'limit': details.max_results, 'viewbox': details.viewbox, 'near': details.near, 'near_radius': details.near_radius, 'excluded': details.excluded_place_ids } t = conn.t.placex rows: List[SaRow] = [] if details.near and details.near_radius is not None and details.near_radius < 0.2: # simply search in placex table def _base_query() -> SaSelect: return base.select_placex(t) \ .add_columns((-t.c.centroid.ST_Distance(NEAR_PARAM)) .label('importance'))\ .where(t.c.linked_place_id == None) \ .where(t.c.geometry.within_distance(NEAR_PARAM, NEAR_RADIUS_PARAM)) \ .order_by(t.c.centroid.ST_Distance(NEAR_PARAM)) \ .limit(LIMIT_PARAM) classtype = self.qualifiers.values if len(classtype) == 1: cclass, ctype = classtype[0] sql: SaLambdaSelect = sa.lambda_stmt( lambda: _base_query().where(t.c.class_ == cclass) .where(t.c.type == ctype)) else: sql = _base_query().where(sa.or_(*(sa.and_(t.c.class_ == cls, t.c.type == typ) for cls, typ in classtype))) if self.countries: sql = sql.where(t.c.country_code.in_(self.countries.values)) if details.viewbox is not None and details.bounded_viewbox: sql = sql.where(t.c.geometry.intersects(VIEWBOX_PARAM)) if details.excluded: sql = sql.where(base.exclude_places(t)) rows.extend(await conn.execute(sql, bind_params)) else: # use the class type tables for category in self.qualifiers.values: table = await conn.get_class_table(*category) if table is not None: sql = base.select_placex(t)\ .add_columns(t.c.importance)\ .join(table, t.c.place_id == table.c.place_id)\ .where(t.c.class_ == category[0])\ .where(t.c.type == category[1]) if details.viewbox is not None and details.bounded_viewbox: sql = sql.where(table.c.centroid.intersects(VIEWBOX_PARAM)) if details.near and details.near_radius is not None: sql = sql.order_by(table.c.centroid.ST_Distance(NEAR_PARAM))\ .where(table.c.centroid.within_distance(NEAR_PARAM, NEAR_RADIUS_PARAM)) if self.countries: sql = sql.where(t.c.country_code.in_(self.countries.values)) if details.excluded: sql = sql.where(base.exclude_places(t)) sql = sql.limit(LIMIT_PARAM) rows.extend(await conn.execute(sql, bind_params)) results = nres.SearchResults() for row in rows: result = nres.create_from_placex_row(row, nres.SearchResult) result.accuracy = self.penalty + self.qualifiers.get_penalty((row.class_, row.type)) result.bbox = Bbox.from_wkb(row.bbox) results.append(result) return results ================================================ FILE: src/nominatim_api/search/db_searches/postcode_search.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of search for a postcode. """ import sqlalchemy as sa from . import base from ...typing import SaBind, SaExpression from ...sql.sqlalchemy_types import Geometry, IntArray from ...connection import SearchConnection from ...types import SearchDetails, Bbox from ... import results as nres from ..db_search_fields import SearchData LIMIT_PARAM: SaBind = sa.bindparam('limit') VIEWBOX_PARAM: SaBind = sa.bindparam('viewbox', type_=Geometry) VIEWBOX2_PARAM: SaBind = sa.bindparam('viewbox2', type_=Geometry) NEAR_PARAM: SaBind = sa.bindparam('near', type_=Geometry) class PostcodeSearch(base.AbstractSearch): """ Search for a postcode. """ def __init__(self, extra_penalty: float, sdata: SearchData) -> None: super().__init__(sdata.penalty + extra_penalty) self.countries = sdata.countries self.postcodes = sdata.postcodes self.lookups = sdata.lookups self.rankings = sdata.rankings async def lookup(self, conn: SearchConnection, details: SearchDetails) -> nres.SearchResults: """ Find results for the search in the database. """ t = conn.t.postcode pcs = self.postcodes.values sql = sa.select(t.c.place_id, t.c.parent_place_id, t.c.osm_id, t.c.rank_search, t.c.postcode, t.c.country_code, t.c.centroid, t.c.geometry.ST_Expand(0).label('bbox'))\ .where(t.c.postcode.in_(pcs)) if details.geometry_output: pcgeom = sa.case((sa.func.ST_NPoints(t.c.geometry) > 5, t.c.geometry), else_=t.c.centroid) sql = base.add_geometry_columns(sql, pcgeom, details) penalty: SaExpression = sa.literal(self.penalty) if details.viewbox is not None and not details.bounded_viewbox: penalty += sa.case((t.c.geometry.intersects(VIEWBOX_PARAM), 0.0), (t.c.geometry.intersects(VIEWBOX2_PARAM), 0.5), else_=1.0) if details.near is not None: sql = sql.order_by(t.c.centroid.ST_Distance(NEAR_PARAM)) sql = base.filter_by_area(sql, t, details) if self.countries: sql = sql.where(t.c.country_code.in_(self.countries.values)) if details.excluded: sql = sql.where(base.exclude_places(t)) if self.lookups: assert len(self.lookups) == 1 tsearch = conn.t.search_name sql = sql.where(tsearch.c.place_id == t.c.parent_place_id)\ .where((tsearch.c.name_vector + tsearch.c.nameaddress_vector) .contains(sa.type_coerce(self.lookups[0].tokens, IntArray))) # Do NOT add rerank penalties based on the address terms. # The standard rerank penalty only checks the address vector # while terms may appear in name and address vector. This would # lead to overly high penalties. # We assume that a postcode is precise enough to not require # additional full name matches. penalty += sa.case(*((t.c.postcode == v, p) for v, p in self.postcodes), else_=1.0) sql = sql.add_columns(penalty.label('accuracy')) sql = sql.order_by('accuracy').limit(LIMIT_PARAM) bind_params = { 'limit': details.max_results, 'viewbox': details.viewbox, 'viewbox2': details.viewbox_x2, 'near': details.near, 'near_radius': details.near_radius, 'excluded': details.excluded_place_ids } results = nres.SearchResults() for row in await conn.execute(sql, bind_params): result = nres.create_from_postcode_row(row, nres.SearchResult) result.bbox = Bbox.from_wkb(row.bbox) result.accuracy = row.accuracy results.append(result) return results ================================================ FILE: src/nominatim_api/search/geocoder.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Public interface to the search code. """ from typing import List, Any, Optional, Iterator, Tuple, Dict import itertools import re import difflib import sqlalchemy as sa from ..connection import SearchConnection from ..types import PlaceRef, SearchDetails, PlaceID, OsmID from ..results import SearchResult, SearchResults, add_result_details from ..timeout import Timeout from ..logging import log from .token_assignment import yield_token_assignments from .db_search_builder import SearchBuilder, build_poi_search, wrap_near_search from .db_searches import AbstractSearch from .query_analyzer_factory import make_query_analyzer, AbstractQueryAnalyzer from .query import Phrase, QueryStruct class ForwardGeocoder: """ Main class responsible for place search. """ def __init__(self, conn: SearchConnection, params: SearchDetails, timeout: Timeout) -> None: self.conn = conn self.params = params self.timeout = timeout self.query_analyzer: Optional[AbstractQueryAnalyzer] = None @property def limit(self) -> int: """ Return the configured maximum number of search results. """ return self.params.max_results async def _resolve_excluded_osm_ids(self) -> None: """ Resolve any OsmID entries in the excluded list to PlaceID entries by looking them up in the placex and location_postcode table. """ excluded = self.params.excluded if not excluded or all(isinstance(e, PlaceID) for e in excluded): return place_ids: List[PlaceRef] = [e for e in excluded if isinstance(e, PlaceID)] osm_ids = [e for e in excluded if isinstance(e, OsmID)] if osm_ids: t = self.conn.t.placex conditions = [ sa.and_(t.c.osm_type == oid.osm_type, t.c.osm_id == oid.osm_id) for oid in osm_ids ] sql = sa.select(t.c.place_id).where(sa.or_(*conditions)) place_ids.extend(PlaceID(row.place_id) for row in await self.conn.execute(sql)) relation_ids = [oid.osm_id for oid in osm_ids if oid.osm_type == 'R'] if relation_ids: p = self.conn.t.postcode sql = sa.select(p.c.place_id).where(p.c.osm_id.in_(relation_ids)) place_ids.extend(PlaceID(row.place_id) for row in await self.conn.execute(sql)) self.params.excluded = place_ids async def build_searches(self, phrases: List[Phrase]) -> Tuple[QueryStruct, List[AbstractSearch]]: """ Analyse the query and return the tokenized query and list of possible searches over it. """ if self.query_analyzer is None: self.query_analyzer = await make_query_analyzer(self.conn) query = await self.query_analyzer.analyze_query(phrases) query.compute_direction_penalty() log().var_dump('Query direction penalty', lambda: f"[{'LR' if query.dir_penalty < 0 else 'RL'}] {query.dir_penalty}") searches: List[AbstractSearch] = [] if query.num_token_slots() > 0: # 2. Compute all possible search interpretations log().section('Compute abstract searches') search_builder = SearchBuilder(query, self.params) num_searches = 0 for assignment in yield_token_assignments(query): searches.extend(search_builder.build(assignment)) if num_searches < len(searches): log().table_dump('Searches for assignment', _dump_searches(searches, query, num_searches)) num_searches = len(searches) searches.sort(key=lambda s: (s.penalty, s.SEARCH_PRIO)) return query, searches async def execute_searches(self, query: QueryStruct, searches: List[AbstractSearch]) -> SearchResults: """ Run the abstract searches against the database until a result is found. """ log().section('Execute database searches') results: Dict[Any, SearchResult] = {} qs = self.params.query_stats qs['search_min_penalty'] = round(searches[0].penalty, 2) min_ranking = searches[0].penalty + 1.5 prev_penalty = 0.0 for i, search in enumerate(searches): if search.penalty > prev_penalty and (search.penalty > min_ranking or i > 15): break log().table_dump(f"{i + 1}. Search", _dump_searches([search], query)) log().var_dump('Params', self.params) lookup_results = await search.lookup(self.conn, self.params) for result in lookup_results: rhash = (result.source_table, result.place_id, result.housenumber, result.country_code) prevresult = results.get(rhash) if prevresult: prevresult.accuracy = min(prevresult.accuracy, result.accuracy) else: if not results: qs['search_first_result_round'] = i spenalty = round(search.penalty, 2) if 'search_min_result_penalty' not in qs or \ spenalty < qs['search_min_result_penalty']: qs['search_min_result_penalty'] = spenalty qs['search_best_penalty_round'] = i results[rhash] = result min_ranking = min(min_ranking, search.penalty + 0.4, result.accuracy + 0.1) log().result_dump('Results', ((r.accuracy, r) for r in lookup_results)) prev_penalty = search.penalty if self.timeout.is_elapsed(): break qs['search_rounds'] = i + 1 return SearchResults(results.values()) def pre_filter_results(self, results: SearchResults) -> SearchResults: """ Remove results that are significantly worse than the best match. """ if results: max_ranking = min(r.ranking for r in results) + 0.5 results = SearchResults(r for r in results if r.ranking < max_ranking) return results def sort_and_cut_results(self, results: SearchResults) -> SearchResults: """ Remove badly matching results, sort by ranking and limit to the configured number of results. """ results.sort(key=lambda r: (r.ranking, 0 if r.bbox is None else -r.bbox.area)) final = SearchResults() min_rank = results[0].rank_search min_ranking = results[0].ranking for r in results: if r.ranking + 0.03 * (r.rank_search - min_rank) < min_ranking + 0.5: final.append(r) min_rank = min(r.rank_search, min_rank) if len(final) == self.limit: break return final def rerank_by_query(self, query: QueryStruct, results: SearchResults) -> None: """ Adjust the accuracy of the localized result according to how well they match the original query. """ assert self.query_analyzer is not None qwords = [word for phrase in query.source for word in re.split('[-,: ]+', phrase.text) if word] if not qwords: return for result in results: # Negative importance indicates ordering by distance, which is # more important than word matching. if not result.display_name\ or (result.importance is not None and result.importance < 0): continue distance = 0.0 norm = self.query_analyzer.normalize_text(' '.join((result.display_name, result.country_code or ''))) words = set((w for w in re.split('[-,: ]+', norm) if w)) if not words: continue for qword in qwords: # only add distance penalty if there is no perfect match if qword not in words: wdist = max(difflib.SequenceMatcher(a=qword, b=w).quick_ratio() for w in words) distance += len(qword) if wdist < 0.4 else 1 # Countries with high importance can dominate results when matched # via an alternate-language name. Apply a language-aware penalty # to offset this. if result.rank_address == 4: if self.params.locales and result.names: loc_names = [result.names[t] for t in self.params.locales.name_tags if t in result.names] if loc_names: norm_loc = self.query_analyzer.normalize_text(' '.join(loc_names)) loc_words = set(w for w in re.split('[-,: ]+', norm_loc) if w) if loc_words and loc_words.isdisjoint(qwords): result.accuracy += result.calculated_importance() * 0.5 else: distance *= 2 result.accuracy += distance * 0.3 / sum(len(w) for w in qwords) async def lookup_pois(self, categories: List[Tuple[str, str]], phrases: List[Phrase]) -> SearchResults: """ Look up places by category. If phrase is given, a place search over the phrase will be executed first and places close to the results returned. """ log().function('forward_lookup_pois', categories=categories, params=self.params) await self._resolve_excluded_osm_ids() if phrases: query, searches = await self.build_searches(phrases) if query: searches = [wrap_near_search(categories, s) for s in searches[:50]] results = await self.execute_searches(query, searches) results = self.pre_filter_results(results) await add_result_details(self.conn, results, self.params) log().result_dump('Preliminary Results', ((r.accuracy, r) for r in results)) results = self.sort_and_cut_results(results) else: results = SearchResults() else: search = build_poi_search(categories, self.params.countries) results = await search.lookup(self.conn, self.params) await add_result_details(self.conn, results, self.params) log().result_dump('Final Results', ((r.accuracy, r) for r in results)) return results async def lookup(self, phrases: List[Phrase]) -> SearchResults: """ Look up a single free-text query. """ log().function('forward_lookup', phrases=phrases, params=self.params) results = SearchResults() if self.params.is_impossible(): return results await self._resolve_excluded_osm_ids() query, searches = await self.build_searches(phrases) if searches: # Execute SQL until an appropriate result is found. results = await self.execute_searches(query, searches[:50]) results = self.pre_filter_results(results) await add_result_details(self.conn, results, self.params) log().result_dump('Preliminary Results', ((r.accuracy, r) for r in results)) if len(results) > 1: self.rerank_by_query(query, results) log().result_dump('Results after reranking', ((r.accuracy, r) for r in results)) results = self.sort_and_cut_results(results) log().result_dump('Final Results', ((r.accuracy, r) for r in results)) return results def _dump_searches(searches: List[AbstractSearch], query: QueryStruct, start: int = 0) -> Iterator[Optional[List[Any]]]: yield ['Penalty', 'Lookups', 'Housenr', 'Postcode', 'Countries', 'Qualifier', 'Catgeory', 'Rankings'] def tk(tl: List[int]) -> str: tstr = [f"{query.find_lookup_word_by_id(t)}({t})" for t in tl] return f"[{','.join(tstr)}]" def fmt_ranking(f: Any) -> str: if not f: return '' ranks = ','.join((f"{tk(r.tokens)}^{r.penalty:.3g}" for r in f.rankings)) if len(ranks) > 100: ranks = ranks[:100] + '...' return f"{f.column}({ranks},def={f.default:.3g})" def fmt_lookup(lk: Any) -> str: if not lk: return '' return f"{lk.lookup_type.__name__}({lk.column}{tk(lk.tokens)})" def fmt_cstr(c: Any) -> str: if not c: return '' return f'{c[0]}^{c[1]}' for search in searches[start:]: fields = ('lookups', 'rankings', 'countries', 'housenumbers', 'postcodes', 'qualifiers') if hasattr(search, 'search'): iters = itertools.zip_longest([f"{search.penalty:.3g}"], *(getattr(search.search, attr, []) for attr in fields), getattr(search, 'categories', []), fillvalue='') else: iters = itertools.zip_longest([f"{search.penalty:.3g}"], *(getattr(search, attr, []) for attr in fields), [], fillvalue='') for penalty, lookup, rank, cc, hnr, pc, qual, cat in iters: yield [penalty, fmt_lookup(lookup), fmt_cstr(hnr), fmt_cstr(pc), fmt_cstr(cc), fmt_cstr(qual), fmt_cstr(cat), fmt_ranking(rank)] yield None ================================================ FILE: src/nominatim_api/search/icu_tokenizer.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of query analysis for the ICU tokenizer. """ from typing import Tuple, Dict, List, Optional, Iterator, Any, cast import dataclasses import difflib import re from itertools import zip_longest from icu import Transliterator import sqlalchemy as sa from ..errors import UsageError from ..typing import SaRow from ..sql.sqlalchemy_types import Json from ..connection import SearchConnection from ..logging import log from . import query as qmod from ..query_preprocessing.config import QueryConfig from ..query_preprocessing.base import QueryProcessingFunc from .query_analyzer_factory import AbstractQueryAnalyzer from .postcode_parser import PostcodeParser DB_TO_TOKEN_TYPE = { 'W': qmod.TOKEN_WORD, 'w': qmod.TOKEN_PARTIAL, 'H': qmod.TOKEN_HOUSENUMBER, 'P': qmod.TOKEN_POSTCODE, 'C': qmod.TOKEN_COUNTRY } PENALTY_BREAK = { qmod.BREAK_START: -0.5, qmod.BREAK_END: -0.5, qmod.BREAK_PHRASE: -0.5, qmod.BREAK_SOFT_PHRASE: -0.5, qmod.BREAK_WORD: 0.1, qmod.BREAK_PART: 0.2, qmod.BREAK_TOKEN: 0.4 } @dataclasses.dataclass class ICUToken(qmod.Token): """ Specialised token for ICU tokenizer. """ word_token: str info: Optional[Dict[str, Any]] def get_category(self) -> Tuple[str, str]: assert self.info return self.info.get('class', ''), self.info.get('type', '') def get_country(self) -> str: assert self.info return cast(str, self.info.get('cc', '')) def match_penalty(self, norm: str) -> float: """ Check how well the token matches the given normalized string and add a penalty, if necessary. """ if not self.lookup_word: return 0.0 seq = difflib.SequenceMatcher(a=self.lookup_word, b=norm) distance = 0 for tag, afrom, ato, bfrom, bto in seq.get_opcodes(): if tag in ('delete', 'insert') and (afrom == 0 or ato == len(self.lookup_word)): distance += 1 elif tag == 'replace': distance += max((ato-afrom), (bto-bfrom)) elif tag != 'equal': distance += abs((ato-afrom) - (bto-bfrom)) return (distance/len(self.lookup_word)) @staticmethod def from_db_row(row: SaRow) -> 'ICUToken': """ Create a ICUToken from the row of the word table. """ count = 1 if row.info is None else row.info.get('count', 1) addr_count = 1 if row.info is None else row.info.get('addr_count', 1) penalty = 0.0 if row.type == 'w': penalty += 0.3 elif row.type == 'W': if len(row.word_token) == 1 and row.word_token == row.word: penalty += 0.2 if row.word.isdigit() else 0.3 elif row.type == 'H': penalty += sum(0.1 for c in row.word_token if c != ' ' and not c.isdigit()) if all(not c.isdigit() for c in row.word_token): penalty += 0.2 * (len(row.word_token) - 1) elif row.type == 'C': if len(row.word_token) == 1: penalty += 0.3 if row.info is None: lookup_word = row.word else: lookup_word = row.info.get('lookup', row.word) if lookup_word: lookup_word = lookup_word.split('@', 1)[0] else: lookup_word = row.word_token return ICUToken(penalty=penalty, token=row.word_id, count=max(1, count), lookup_word=lookup_word, word_token=row.word_token, info=row.info, addr_count=max(1, addr_count)) @dataclasses.dataclass class ICUAnalyzerConfig: postcode_parser: PostcodeParser normalizer: Transliterator transliterator: Transliterator preprocessors: List[QueryProcessingFunc] @staticmethod async def create(conn: SearchConnection) -> 'ICUAnalyzerConfig': rules = await conn.get_property('tokenizer_import_normalisation') normalizer = Transliterator.createFromRules("normalization", rules) rules = await conn.get_property('tokenizer_import_transliteration') transliterator = Transliterator.createFromRules("transliteration", rules) preprocessing_rules = conn.config.load_sub_configuration('icu_tokenizer.yaml', config='TOKENIZER_CONFIG')\ .get('query-preprocessing', []) preprocessors: List[QueryProcessingFunc] = [] for func in preprocessing_rules: if 'step' not in func: raise UsageError("Preprocessing rule is missing the 'step' attribute.") if not isinstance(func['step'], str): raise UsageError("'step' attribute must be a simple string.") module = conn.config.load_plugin_module( func['step'], 'nominatim_api.query_preprocessing') preprocessors.append( module.create(QueryConfig(func).set_normalizer(normalizer))) return ICUAnalyzerConfig(PostcodeParser(conn.config), normalizer, transliterator, preprocessors) class ICUQueryAnalyzer(AbstractQueryAnalyzer): """ Converter for query strings into a tokenized query using the tokens created by a ICU tokenizer. """ def __init__(self, conn: SearchConnection, config: ICUAnalyzerConfig) -> None: self.conn = conn self.postcode_parser = config.postcode_parser self.normalizer = config.normalizer self.transliterator = config.transliterator self.preprocessors = config.preprocessors async def analyze_query(self, phrases: List[qmod.Phrase]) -> qmod.QueryStruct: """ Analyze the given list of phrases and return the tokenized query. """ log().section('Analyze query (using ICU tokenizer)') for func in self.preprocessors: phrases = func(phrases) query = qmod.QueryStruct(phrases) log().var_dump('Normalized query', query.source) if not query.source: return query self.split_query(query) log().var_dump('Transliterated query', lambda: ''.join(f"{n.term_lookup}{n.btype}" for n in query.nodes) + ' / ' + ''.join(f"{n.term_normalized}{n.btype}" for n in query.nodes)) words = query.extract_words() for row in await self.lookup_in_db(list(words.keys())): for trange in words[row.word_token]: # Create a new token for each position because the token # penalty can vary depending on the position in the query. # (See rerank_tokens() below.) token = ICUToken.from_db_row(row) if row.type == 'S': if row.info['op'] in ('in', 'near'): if trange.start == 0: query.add_token(trange, qmod.TOKEN_NEAR_ITEM, token) else: if trange.start == 0 and trange.end == query.num_token_slots(): query.add_token(trange, qmod.TOKEN_NEAR_ITEM, token) else: query.add_token(trange, qmod.TOKEN_QUALIFIER, token) else: query.add_token(trange, DB_TO_TOKEN_TYPE[row.type], token) self.add_extra_tokens(query) for start, end, pc in self.postcode_parser.parse(query): term = ' '.join(n.term_lookup for n in query.nodes[start + 1:end + 1]) query.add_token(qmod.TokenRange(start, end), qmod.TOKEN_POSTCODE, ICUToken(penalty=0.0, token=0, count=1, addr_count=1, lookup_word=pc, word_token=term, info=None)) self.rerank_tokens(query) self.compute_break_penalties(query) log().table_dump('Word tokens', _dump_word_tokens(query)) return query def normalize_text(self, text: str) -> str: """ Bring the given text into a normalized form. That is the standardized form search will work with. All information removed at this stage is inevitably lost. """ return cast(str, self.normalizer.transliterate(text)).strip('-: ') def split_transliteration(self, trans: str, word: str) -> list[tuple[str, str]]: """ Split the given transliteration string into sub-words and return them together with the original part of the word. """ subwords = trans.split(' ') if len(subwords) == 1: return [(trans, word)] tlist = [] titer = filter(None, subwords) current_trans: Optional[str] = next(titer) assert current_trans current_word = '' for letter in word: current_word += letter if self.transliterator.transliterate(current_word).rstrip() == current_trans: tlist.append((current_trans, current_word)) current_trans = next(titer, None) if current_trans is None: return tlist current_word = '' if current_word: tlist.append((current_trans, current_word)) return tlist def split_query(self, query: qmod.QueryStruct) -> None: """ Transliterate the phrases and split them into tokens. """ for phrase in query.source: query.nodes[-1].ptype = phrase.ptype phrase_split = re.split('([ :-])', phrase.text) # The zip construct will give us the pairs of word/break from # the regular expression split. As the split array ends on the # final word, we simply use the fillvalue to even out the list and # add the phrase break at the end. for word, breakchar in zip_longest(*[iter(phrase_split)]*2, fillvalue=','): if not word: continue if trans := self.transliterator.transliterate(word): for term, term_word in self.split_transliteration(trans, word): if term: query.add_node(qmod.BREAK_TOKEN, phrase.ptype, term, term_word) query.nodes[-1].btype = breakchar query.nodes[-1].btype = qmod.BREAK_END async def lookup_in_db(self, words: List[str]) -> 'sa.Result[Any]': """ Return the token information from the database for the given word tokens. This function excludes postcode tokens """ t = self.conn.t.meta.tables['word'] return await self.conn.execute(t.select() .where(t.c.word_token.in_(words)) .where(t.c.type != 'P')) def add_extra_tokens(self, query: qmod.QueryStruct) -> None: """ Add tokens to query that are not saved in the database. """ need_hnr = False for i, node in enumerate(query.nodes): is_full_token = node.btype not in (qmod.BREAK_TOKEN, qmod.BREAK_PART) if need_hnr and is_full_token \ and len(node.term_normalized) <= 4 and node.term_normalized.isdigit(): query.add_token(qmod.TokenRange(i-1, i), qmod.TOKEN_HOUSENUMBER, ICUToken(penalty=0.2, token=0, count=1, addr_count=1, lookup_word=node.term_lookup, word_token=node.term_lookup, info=None)) need_hnr = is_full_token and not node.has_tokens(i+1, qmod.TOKEN_HOUSENUMBER) def rerank_tokens(self, query: qmod.QueryStruct) -> None: """ Add penalties to tokens that depend on presence of other token. """ for start, end, tlist in query.iter_tokens_by_edge(): if len(tlist) > 1: # If it looks like a Postcode, give preference. if qmod.TOKEN_POSTCODE in tlist: for ttype, tokens in tlist.items(): if ttype != qmod.TOKEN_POSTCODE and \ (ttype != qmod.TOKEN_HOUSENUMBER or start + 1 > end or len(query.nodes[end].term_lookup) > 4): for token in tokens: token.penalty += 0.39 if (start + 1 == end): if partial := query.nodes[start].partial: partial.penalty += 0.39 # If it looks like a simple housenumber, prefer that. if qmod.TOKEN_HOUSENUMBER in tlist: hnr_lookup = tlist[qmod.TOKEN_HOUSENUMBER][0].lookup_word if len(hnr_lookup) <= 3 and any(c.isdigit() for c in hnr_lookup): penalty = 0.5 - tlist[qmod.TOKEN_HOUSENUMBER][0].penalty for ttype, tokens in tlist.items(): if ttype != qmod.TOKEN_HOUSENUMBER: for token in tokens: token.penalty += penalty if (start + 1 == end): if partial := query.nodes[start].partial: partial.penalty += penalty # rerank tokens against the normalized form norm = ''.join(f"{n.term_normalized}{'' if n.btype == qmod.BREAK_TOKEN else ' '}" for n in query.nodes[start + 1:end + 1]).strip() for ttype, tokens in tlist.items(): for token in tokens: itok = cast(ICUToken, token) itok.penalty += itok.match_penalty(norm) * \ (1 if ttype in (qmod.TOKEN_WORD, qmod.TOKEN_PARTIAL) else 2) def compute_break_penalties(self, query: qmod.QueryStruct) -> None: """ Set the break penalties for the nodes in the query. """ for node in query.nodes: node.penalty = PENALTY_BREAK[node.btype] def _dump_word_tokens(query: qmod.QueryStruct) -> Iterator[List[Any]]: yield ['type', 'from', 'to', 'token', 'word_token', 'lookup_word', 'penalty', 'count', 'info'] for i, node in enumerate(query.nodes): if node.partial is not None: t = cast(ICUToken, node.partial) yield [qmod.TOKEN_PARTIAL, str(i), str(i + 1), t.token, t.word_token, t.lookup_word, t.penalty, t.count, t.info] for tlist in node.starting: for token in tlist.tokens: t = cast(ICUToken, token) yield [tlist.ttype, str(i), str(tlist.end), t.token, t.word_token or '', t.lookup_word or '', t.penalty, t.count, t.info] async def create_query_analyzer(conn: SearchConnection) -> AbstractQueryAnalyzer: """ Create and set up a new query analyzer for a database based on the ICU tokenizer. """ async def _get_config() -> ICUAnalyzerConfig: if 'word' not in conn.t.meta.tables: sa.Table('word', conn.t.meta, sa.Column('word_id', sa.Integer), sa.Column('word_token', sa.Text, nullable=False), sa.Column('type', sa.Text, nullable=False), sa.Column('word', sa.Text), sa.Column('info', Json)) return await ICUAnalyzerConfig.create(conn) config = await conn.get_cached_value('ICUTOK', 'config', _get_config) return ICUQueryAnalyzer(conn, config) ================================================ FILE: src/nominatim_api/search/postcode_parser.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Handling of arbitrary postcode tokens in tokenized query string. """ from typing import Tuple, Set, Dict, List import re from collections import defaultdict import yaml from ..config import Configuration from . import query as qmod class PostcodeParser: """ Pattern-based parser for postcodes in tokenized queries. The postcode patterns are read from the country configuration. The parser does currently not return country restrictions. """ def __init__(self, config: Configuration) -> None: # skip over includes here to avoid loading the complete country name data yaml.add_constructor('!include', lambda loader, node: [], Loader=yaml.SafeLoader) cdata = yaml.safe_load(config.find_config_file('country_settings.yaml') .read_text(encoding='utf-8')) unique_patterns: Dict[str, Dict[str, List[str]]] = {} for cc, data in cdata.items(): if data.get('postcode'): pat = data['postcode']['pattern'].replace('d', '[0-9]').replace('l', '[A-Z]') out = data['postcode'].get('output') if pat not in unique_patterns: unique_patterns[pat] = defaultdict(list) unique_patterns[pat][out].append(cc.upper()) self.global_pattern = re.compile( '(?:(?P[A-Z][A-Z])(?P[ -]?))?(?P(?:(?:' + ')|(?:'.join(unique_patterns) + '))[:, >].*)') self.local_patterns = [(re.compile(f"{pat}[:, >]"), list(info.items())) for pat, info in unique_patterns.items()] def parse(self, query: qmod.QueryStruct) -> Set[Tuple[int, int, str]]: """ Parse postcodes in the given list of query tokens taking into account the list of breaks from the nodes. The result is a sequence of tuples with [start node id, end node id, postcode token] """ nodes = query.nodes outcodes: Set[Tuple[int, int, str]] = set() terms = [n.term_normalized.upper() + n.btype for n in nodes] for i in range(query.num_token_slots()): if nodes[i].btype in '<,: ' and nodes[i + 1].btype != '`' \ and (i == 0 or nodes[i - 1].ptype != qmod.PHRASE_POSTCODE): if nodes[i].ptype == qmod.PHRASE_ANY: word = terms[i + 1] if word[-1] in ' -' and nodes[i + 2].btype != '`' \ and nodes[i + 1].ptype == qmod.PHRASE_ANY: word += terms[i + 2] if word[-1] in ' -' and nodes[i + 3].btype != '`' \ and nodes[i + 2].ptype == qmod.PHRASE_ANY: word += terms[i + 3] self._match_word(word, i, False, outcodes) elif nodes[i].ptype == qmod.PHRASE_POSTCODE: word = terms[i + 1] for j in range(i + 1, query.num_token_slots()): if nodes[j].ptype != qmod.PHRASE_POSTCODE: break word += terms[j + 1] self._match_word(word, i, True, outcodes) return outcodes def _match_word(self, word: str, pos: int, fullmatch: bool, outcodes: Set[Tuple[int, int, str]]) -> None: # Use global pattern to check for presence of any postcode. m = self.global_pattern.fullmatch(word) if m: # If there was a match, check against each pattern separately # because multiple patterns might be machting at the end. cc = m.group('cc') pc_word = m.group('pc') cc_spaces = len(m.group('space') or '') for pattern, info in self.local_patterns: lm = pattern.fullmatch(pc_word) if fullmatch else pattern.match(pc_word) if lm: trange = (pos, pos + cc_spaces + sum(c in ' ,-:>' for c in lm.group(0))) for out, out_ccs in info: if cc is None or cc in out_ccs: if out: outcodes.add((*trange, lm.expand(out))) else: outcodes.add((*trange, lm.group(0)[:-1])) ================================================ FILE: src/nominatim_api/search/query.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Datastructures for a tokenized query. """ from typing import Dict, List, Tuple, Optional, Iterator from abc import ABC, abstractmethod from collections import defaultdict import dataclasses # Precomputed denominator for the computation of the linear regression slope # used to determine the query direction. # The x value for the regression computation will be the position of the # token in the query. Thus we know the x values will be [0, query length). # As the denominator only depends on the x values, we can pre-compute here # the denominator to use for a given query length. # Note that query length of two or less is special cased and will not use # the values from this array. Thus it is not a problem that they are 0. LINFAC = [i * (sum(si * si for si in range(i)) - (i - 1) * i * (i - 1) / 4) for i in range(50)] BreakType = str """ Type of break between tokens. """ BREAK_START = '<' """ Begin of the query. """ BREAK_END = '>' """ End of the query. """ BREAK_PHRASE = ',' """ Hard break between two phrases. Address parts cannot cross hard phrase boundaries.""" BREAK_SOFT_PHRASE = ':' """ Likely break between two phrases. Address parts should not cross soft phrase boundaries. Soft breaks can be inserted by a preprocessor that is analysing the input string. """ BREAK_WORD = ' ' """ Break between words. """ BREAK_PART = '-' """ Break inside a word, for example a hyphen or apostrophe. """ BREAK_TOKEN = '`' """ Break created as a result of tokenization. This may happen in languages without spaces between words. """ TokenType = str """ Type of token. """ TOKEN_WORD = 'W' """ Full name of a place. """ TOKEN_PARTIAL = 'w' """ Word term without breaks, does not necessarily represent a full name. """ TOKEN_HOUSENUMBER = 'H' """ Housenumber term. """ TOKEN_POSTCODE = 'P' """ Postal code term. """ TOKEN_COUNTRY = 'C' """ Country name or reference. """ TOKEN_QUALIFIER = 'Q' """ Special term used together with name (e.g. _Hotel_ Bellevue). """ TOKEN_NEAR_ITEM = 'N' """ Special term used as searchable object(e.g. supermarket in ...). """ PhraseType = int """ Designation of a phrase. """ PHRASE_ANY = 0 """ No specific designation (i.e. source is free-form query). """ PHRASE_AMENITY = 1 """ Contains name or type of a POI. """ PHRASE_STREET = 2 """ Contains a street name optionally with a housenumber. """ PHRASE_CITY = 3 """ Contains the postal city. """ PHRASE_COUNTY = 4 """ Contains the equivalent of a county. """ PHRASE_STATE = 5 """ Contains a state or province. """ PHRASE_POSTCODE = 6 """ Contains a postal code. """ PHRASE_COUNTRY = 7 """ Contains the country name or code. """ def _phrase_compatible_with(ptype: PhraseType, ttype: TokenType, is_full_phrase: bool) -> bool: """ Check if the given token type can be used with the phrase type. """ if ptype == PHRASE_ANY: return not is_full_phrase or ttype != TOKEN_QUALIFIER if ptype == PHRASE_AMENITY: return ttype in (TOKEN_WORD, TOKEN_PARTIAL)\ or (is_full_phrase and ttype == TOKEN_NEAR_ITEM)\ or (not is_full_phrase and ttype == TOKEN_QUALIFIER) if ptype == PHRASE_STREET: return ttype in (TOKEN_WORD, TOKEN_PARTIAL, TOKEN_HOUSENUMBER) if ptype == PHRASE_POSTCODE: return ttype == TOKEN_POSTCODE if ptype == PHRASE_COUNTRY: return ttype == TOKEN_COUNTRY return ttype in (TOKEN_WORD, TOKEN_PARTIAL) @dataclasses.dataclass class Token(ABC): """ Base type for tokens. Specific query analyzers must implement the concrete token class. """ penalty: float token: int count: int addr_count: int lookup_word: str @abstractmethod def get_category(self) -> Tuple[str, str]: """ Return the category restriction for qualifier terms and category objects. """ @abstractmethod def get_country(self) -> str: """ Return the country code this token is associated with (currently for country tokens only). """ @dataclasses.dataclass class TokenRange: """ Indexes of query nodes over which a token spans. """ start: int end: int def __lt__(self, other: 'TokenRange') -> bool: return self.end <= other.start def __le__(self, other: 'TokenRange') -> bool: return NotImplemented def __gt__(self, other: 'TokenRange') -> bool: return self.start >= other.end def __ge__(self, other: 'TokenRange') -> bool: return NotImplemented def replace_start(self, new_start: int) -> 'TokenRange': """ Return a new token range with the new start. """ return TokenRange(new_start, self.end) def replace_end(self, new_end: int) -> 'TokenRange': """ Return a new token range with the new end. """ return TokenRange(self.start, new_end) def split(self, index: int) -> Tuple['TokenRange', 'TokenRange']: """ Split the span into two spans at the given index. The index must be within the span. """ return self.replace_end(index), self.replace_start(index) @dataclasses.dataclass class TokenList: """ List of all tokens of a given type going from one breakpoint to another. """ end: int ttype: TokenType tokens: List[Token] def add_penalty(self, penalty: float) -> None: """ Add the given penalty to all tokens in the list. """ for token in self.tokens: token.penalty += penalty @dataclasses.dataclass class QueryNode: """ A node of the query representing a break between terms. The node also contains information on the source term ending at the node. The tokens are created from this information. """ btype: BreakType ptype: PhraseType penalty: float """ Penalty for having a word break at this position. The penalty may be negative, when a word break is more likely than continuing the word after the node. """ term_lookup: str """ Transliterated term ending at this node. """ term_normalized: str """ Normalised form of term ending at this node. """ starting: List[TokenList] = dataclasses.field(default_factory=list) """ List of all full tokens starting at this node. """ partial: Optional[Token] = None """ Base token going to the next node. May be None when the query has parts for which no words are known. Note that the query may still be parsable when there are other types of tokens spanning over the gap. """ @property def word_break_penalty(self) -> float: """ Penalty to apply when a words ends at this node. """ return max(0, self.penalty) @property def word_continuation_penalty(self) -> float: """ Penalty to apply when a word continues over this node (i.e. is a multi-term word). """ return max(0, -self.penalty) def name_address_ratio(self) -> float: """ Return the probability that the partial token belonging to this node forms part of a name (as opposed of part of the address). """ if self.partial is None: return 0.5 return self.partial.count / (self.partial.count + self.partial.addr_count) def has_tokens(self, end: int, *ttypes: TokenType) -> bool: """ Check if there are tokens of the given types ending at the given node. """ return any(tl.end == end and tl.ttype in ttypes for tl in self.starting) def get_tokens(self, end: int, ttype: TokenType) -> Optional[List[Token]]: """ Get the list of tokens of the given type starting at this node and ending at the node 'end'. Returns 'None' if no such tokens exist. """ for tlist in self.starting: if tlist.end == end and tlist.ttype == ttype: return tlist.tokens return None @dataclasses.dataclass class Phrase: """ A normalized query part. Phrases may be typed which means that they then represent a specific part of the address. """ ptype: PhraseType text: str class QueryStruct: """ A tokenized search query together with the normalized source from which the tokens have been parsed. The query contains a list of nodes that represent the breaks between words. Tokens span between nodes, which don't necessarily need to be direct neighbours. Thus the query is represented as a directed acyclic graph. A query also has a direction penalty 'dir_penalty'. This describes the likelihood if the query should be read from left-to-right or vice versa. A negative 'dir_penalty' should be read as a penalty on right-to-left reading, while a positive value represents a penalty for left-to-right reading. The default value is 0, which is equivalent to having no information about the reading. When created, a query contains a single node: the start of the query. Further nodes can be added by appending to 'nodes'. """ def __init__(self, source: List[Phrase]) -> None: self.source = source self.dir_penalty = 0.0 self.nodes: List[QueryNode] = \ [QueryNode(BREAK_START, source[0].ptype if source else PHRASE_ANY, 0.0, '', '')] def num_token_slots(self) -> int: """ Return the length of the query in vertice steps. """ return len(self.nodes) - 1 def add_node(self, btype: BreakType, ptype: PhraseType, term_lookup: str = '', term_normalized: str = '') -> None: """ Append a new break node with the given break type. The phrase type denotes the type for any tokens starting at the node. """ self.nodes.append(QueryNode(btype, ptype, 0.0, term_lookup, term_normalized)) def add_token(self, trange: TokenRange, ttype: TokenType, token: Token) -> None: """ Add a token to the query. 'start' and 'end' are the indexes of the nodes from which to which the token spans. The indexes must exist and are expected to be in the same phrase. 'ttype' denotes the type of the token and 'token' the token to be inserted. If the token type is not compatible with the phrase it should be added to, then the token is silently dropped. """ snode = self.nodes[trange.start] if ttype == TOKEN_PARTIAL: assert snode.partial is None if _phrase_compatible_with(snode.ptype, TOKEN_PARTIAL, False): snode.partial = token else: full_phrase = snode.btype in (BREAK_START, BREAK_PHRASE)\ and self.nodes[trange.end].btype in (BREAK_PHRASE, BREAK_END) if _phrase_compatible_with(snode.ptype, ttype, full_phrase): tlist = snode.get_tokens(trange.end, ttype) if tlist is None: snode.starting.append(TokenList(trange.end, ttype, [token])) else: tlist.append(token) def compute_direction_penalty(self) -> None: """ Recompute the direction probability from the partial tokens of each node. """ n = len(self.nodes) - 1 if n <= 1 or n >= 50: self.dir_penalty = 0 elif n == 2: self.dir_penalty = (self.nodes[1].name_address_ratio() - self.nodes[0].name_address_ratio()) / 3 else: ratios = [n.name_address_ratio() for n in self.nodes[:-1]] self.dir_penalty = (n * sum(i * r for i, r in enumerate(ratios)) - sum(ratios) * n * (n - 1) / 2) / LINFAC[n] def get_tokens(self, trange: TokenRange, ttype: TokenType) -> List[Token]: """ Get the list of tokens of a given type, spanning the given nodes. The nodes must exist. If no tokens exist, an empty list is returned. Cannot be used to get the partial token. """ assert ttype != TOKEN_PARTIAL return self.nodes[trange.start].get_tokens(trange.end, ttype) or [] def get_in_word_penalty(self, trange: TokenRange) -> float: """ Gets the sum of penalties for all token transitions within the given range. """ return sum(n.word_continuation_penalty for n in self.nodes[trange.start + 1:trange.end]) def iter_partials(self, trange: TokenRange) -> Iterator[Token]: """ Iterate over the partial tokens between the given nodes. Missing partials are ignored. """ return (n.partial for n in self.nodes[trange.start:trange.end] if n.partial is not None) def iter_tokens_by_edge(self) -> Iterator[Tuple[int, int, Dict[TokenType, List[Token]]]]: """ Iterator over all tokens except partial ones grouped by edge. Returns the start and end node indexes and a dictionary of list of tokens by token type. """ for i, node in enumerate(self.nodes): by_end: Dict[int, Dict[TokenType, List[Token]]] = defaultdict(dict) for tlist in node.starting: by_end[tlist.end][tlist.ttype] = tlist.tokens for end, endlist in by_end.items(): yield i, end, endlist def find_lookup_word_by_id(self, token: int) -> str: """ Find the first token with the given token ID and return its lookup word. Returns 'None' if no such token exists. The function is very slow and must only be used for debugging. """ for node in self.nodes: if node.partial is not None and node.partial.token == token: return f"[P]{node.partial.lookup_word}" for tlist in node.starting: for t in tlist.tokens: if t.token == token: return f"[{tlist.ttype}]{t.lookup_word}" return 'None' def extract_words(self, start: int = 0, endpos: Optional[int] = None) -> Dict[str, List[TokenRange]]: """ Add all combinations of words that can be formed from the terms between the given start and endnode. The terms are joined with spaces for each break. Words can never go across a BREAK_PHRASE. The functions returns a dictionary of possible words with their position within the query. """ if endpos is None: endpos = len(self.nodes) words: Dict[str, List[TokenRange]] = defaultdict(list) for first, first_node in enumerate(self.nodes[start + 1:endpos], start): word = first_node.term_lookup words[word].append(TokenRange(first, first + 1)) if first_node.btype != BREAK_PHRASE: max_last = min(first + 20, endpos) for last, last_node in enumerate(self.nodes[first + 2:max_last], first + 2): word = ' '.join((word, last_node.term_lookup)) words[word].append(TokenRange(first, last)) if last_node.btype == BREAK_PHRASE: break return words ================================================ FILE: src/nominatim_api/search/query_analyzer_factory.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Factory for creating a query analyzer for the configured tokenizer. """ from typing import List, cast, TYPE_CHECKING from abc import ABC, abstractmethod from pathlib import Path import importlib from ..logging import log from ..connection import SearchConnection if TYPE_CHECKING: from .query import Phrase, QueryStruct class AbstractQueryAnalyzer(ABC): """ Class for analysing incoming queries. Query analyzers are tied to the tokenizer used on import. """ @abstractmethod async def analyze_query(self, phrases: List['Phrase']) -> 'QueryStruct': """ Analyze the given phrases and return the tokenized query. """ @abstractmethod def normalize_text(self, text: str) -> str: """ Bring the given text into a normalized form. That is the standardized form search will work with. All information removed at this stage is inevitably lost. """ async def make_query_analyzer(conn: SearchConnection) -> AbstractQueryAnalyzer: """ Create a query analyzer for the tokenizer used by the database. """ name = await conn.get_property('tokenizer') src_file = Path(__file__).parent / f'{name}_tokenizer.py' if not src_file.is_file(): log().comment(f"No tokenizer named '{name}' available. Database not set up properly.") raise RuntimeError('Tokenizer not found') module = importlib.import_module(f'nominatim_api.search.{name}_tokenizer') return cast(AbstractQueryAnalyzer, await module.create_query_analyzer(conn)) ================================================ FILE: src/nominatim_api/search/token_assignment.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Create query interpretations where each vertice in the query is assigned a specific function (expressed as a token type). """ from typing import Optional, List, Iterator import dataclasses from ..logging import log from . import query as qmod @dataclasses.dataclass class TypedRange: """ A token range for a specific type of tokens. """ ttype: qmod.TokenType trange: qmod.TokenRange TypedRangeSeq = List[TypedRange] @dataclasses.dataclass class TokenAssignment: """ Representation of a possible assignment of token types to the tokens in a tokenized query. """ penalty: float = 0.0 name: Optional[qmod.TokenRange] = None address: List[qmod.TokenRange] = dataclasses.field(default_factory=list) housenumber: Optional[qmod.TokenRange] = None postcode: Optional[qmod.TokenRange] = None country: Optional[qmod.TokenRange] = None near_item: Optional[qmod.TokenRange] = None qualifier: Optional[qmod.TokenRange] = None @staticmethod def from_ranges(ranges: TypedRangeSeq) -> 'TokenAssignment': """ Create a new token assignment from a sequence of typed spans. """ out = TokenAssignment() for token in ranges: if token.ttype == qmod.TOKEN_PARTIAL: out.address.append(token.trange) elif token.ttype == qmod.TOKEN_HOUSENUMBER: out.housenumber = token.trange elif token.ttype == qmod.TOKEN_POSTCODE: out.postcode = token.trange elif token.ttype == qmod.TOKEN_COUNTRY: out.country = token.trange elif token.ttype == qmod.TOKEN_NEAR_ITEM: out.near_item = token.trange elif token.ttype == qmod.TOKEN_QUALIFIER: out.qualifier = token.trange return out class _TokenSequence: """ Working state used to put together the token assignments. Represents an intermediate state while traversing the tokenized query. """ def __init__(self, seq: TypedRangeSeq, direction: int = 0, penalty: float = 0.0) -> None: self.seq = seq self.direction = direction self.penalty = penalty def __str__(self) -> str: seq = ''.join(f'[{r.trange.start} - {r.trange.end}: {r.ttype}]' for r in self.seq) return f'{seq} (dir: {self.direction}, penalty: {self.penalty})' @property def end_pos(self) -> int: """ Return the index of the global end of the current sequence. """ return self.seq[-1].trange.end if self.seq else 0 def has_types(self, *ttypes: qmod.TokenType) -> bool: """ Check if the current sequence contains any typed ranges of the given types. """ return any(s.ttype in ttypes for s in self.seq) def is_final(self) -> bool: """ Return true when the sequence cannot be extended by any form of token anymore. """ # Country and category must be the final term for left-to-right return len(self.seq) > 1 and \ self.seq[-1].ttype in (qmod.TOKEN_COUNTRY, qmod.TOKEN_NEAR_ITEM) def appendable(self, ttype: qmod.TokenType) -> Optional[int]: """ Check if the give token type is appendable to the existing sequence. Returns None if the token type is not appendable, otherwise the new direction of the sequence after adding such a type. The token is not added. """ if ttype == qmod.TOKEN_WORD: return None if not self.seq: # Append unconditionally to the empty list if ttype == qmod.TOKEN_COUNTRY: return -1 if ttype in (qmod.TOKEN_HOUSENUMBER, qmod.TOKEN_QUALIFIER): return 1 return self.direction # Name tokens are always acceptable and don't change direction if ttype == qmod.TOKEN_PARTIAL: # qualifiers cannot appear in the middle of the query. They need # to be near the next phrase. if self.direction == -1 \ and any(t.ttype == qmod.TOKEN_QUALIFIER for t in self.seq[:-1]): return None return self.direction # Other tokens may only appear once if self.has_types(ttype): return None if ttype == qmod.TOKEN_HOUSENUMBER: if self.direction == 1: if len(self.seq) == 1 and self.seq[0].ttype == qmod.TOKEN_QUALIFIER: return None if len(self.seq) > 2 \ or self.has_types(qmod.TOKEN_POSTCODE, qmod.TOKEN_COUNTRY): return None # direction left-to-right: housenumber must come before anything elif (self.direction == -1 or self.has_types(qmod.TOKEN_POSTCODE, qmod.TOKEN_COUNTRY)): return -1 # force direction right-to-left if after other terms return self.direction if ttype == qmod.TOKEN_POSTCODE: if self.direction == -1: if self.has_types(qmod.TOKEN_HOUSENUMBER, qmod.TOKEN_QUALIFIER): return None return -1 if self.direction == 1: return None if self.has_types(qmod.TOKEN_COUNTRY) else 1 if self.has_types(qmod.TOKEN_HOUSENUMBER, qmod.TOKEN_QUALIFIER): return 1 return self.direction if ttype == qmod.TOKEN_COUNTRY: return None if self.direction == -1 else 1 if ttype == qmod.TOKEN_NEAR_ITEM: return self.direction if ttype == qmod.TOKEN_QUALIFIER: if self.direction == 1: if (len(self.seq) == 1 and self.seq[0].ttype in (qmod.TOKEN_PARTIAL, qmod.TOKEN_NEAR_ITEM)) \ or (len(self.seq) == 2 and self.seq[0].ttype == qmod.TOKEN_NEAR_ITEM and self.seq[1].ttype == qmod.TOKEN_PARTIAL): return 1 return None if self.direction == -1: return -1 tempseq = self.seq[1:] if self.seq[0].ttype == qmod.TOKEN_NEAR_ITEM else self.seq if len(tempseq) == 0: return 1 if len(tempseq) == 1 and self.seq[0].ttype == qmod.TOKEN_HOUSENUMBER: return None if len(tempseq) > 1 or self.has_types(qmod.TOKEN_POSTCODE, qmod.TOKEN_COUNTRY): return -1 return 0 return None def advance(self, ttype: qmod.TokenType, end_pos: int, force_break: bool, break_penalty: float) -> Optional['_TokenSequence']: """ Return a new token sequence state with the given token type extended. """ newdir = self.appendable(ttype) if newdir is None: return None if not self.seq: newseq = [TypedRange(ttype, qmod.TokenRange(0, end_pos))] new_penalty = 0.0 else: last = self.seq[-1] if not force_break and last.ttype == ttype: # extend the existing range newseq = self.seq[:-1] + [TypedRange(ttype, last.trange.replace_end(end_pos))] new_penalty = 0.0 else: # start a new range newseq = list(self.seq) + [TypedRange(ttype, qmod.TokenRange(last.trange.end, end_pos))] new_penalty = break_penalty return _TokenSequence(newseq, newdir, self.penalty + new_penalty) def _adapt_penalty_from_priors(self, priors: int, new_dir: int) -> bool: if priors >= 2: if self.direction == 0: self.direction = new_dir else: if priors == 2: self.penalty += 0.8 else: return False return True def recheck_sequence(self) -> bool: """ Check that the sequence is a fully valid token assignment and adapt direction and penalties further if necessary. This function catches some impossible assignments that need forward context and can therefore not be excluded when building the assignment. """ # housenumbers may not be further than 2 words from the beginning. # If there are two words in front, give it a penalty. hnrpos = next((i for i, tr in enumerate(self.seq) if tr.ttype == qmod.TOKEN_HOUSENUMBER), None) if hnrpos is not None: if self.direction != -1: priors = sum(1 for t in self.seq[:hnrpos] if t.ttype == qmod.TOKEN_PARTIAL) if not self._adapt_penalty_from_priors(priors, -1): return False if self.direction != 1: priors = sum(1 for t in self.seq[hnrpos+1:] if t.ttype == qmod.TOKEN_PARTIAL) if not self._adapt_penalty_from_priors(priors, 1): return False if any(t.ttype == qmod.TOKEN_NEAR_ITEM for t in self.seq): self.penalty += 1.0 return True def _get_assignments_postcode(self, base: TokenAssignment, query_len: int) -> Iterator[TokenAssignment]: """ Yield possible assignments of Postcode searches with an address component. """ assert base.postcode is not None if (base.postcode.start == 0 and self.direction != -1)\ or (base.postcode.end == query_len and self.direction != 1): log().comment('postcode search') #
, should give preference to address search if base.postcode.start == 0: penalty = self.penalty else: penalty = self.penalty + 0.1 penalty += 0.1 * max(0, len(base.address) - 1) yield dataclasses.replace(base, penalty=penalty) def _get_assignments_address_forward(self, base: TokenAssignment, query: qmod.QueryStruct) -> Iterator[TokenAssignment]: """ Yield possible assignments of address searches with left-to-right reading. """ first = base.address[0] # The postcode must come after the name. if base.postcode and base.postcode < first: log().var_dump('skip forward', (base.postcode, first)) return penalty = self.penalty if not base.country and self.direction == 1 and query.dir_penalty > 0: penalty += query.dir_penalty log().comment('first word = name') yield dataclasses.replace(base, penalty=penalty, name=first, address=base.address[1:]) # To paraphrase: # * if another name term comes after the first one and before the # housenumber # * a qualifier comes after the name # * the containing phrase is strictly typed if (base.housenumber and first.end < base.housenumber.start)\ or (base.qualifier and base.qualifier > first)\ or (query.nodes[first.start].ptype != qmod.PHRASE_ANY): return # Penalty for: # * , , , ... # * queries that are comma-separated if (base.housenumber and base.housenumber > first) or len(query.source) > 1: penalty += 0.25 if self.direction == 0 and query.dir_penalty > 0: penalty += query.dir_penalty for i in range(first.start + 1, first.end): name, addr = first.split(i) log().comment(f'split first word = name ({i - first.start})') yield dataclasses.replace(base, name=name, address=[addr] + base.address[1:], penalty=penalty + query.nodes[i].word_break_penalty) def _get_assignments_address_backward(self, base: TokenAssignment, query: qmod.QueryStruct) -> Iterator[TokenAssignment]: """ Yield possible assignments of address searches with right-to-left reading. """ last = base.address[-1] # The postcode must come before the name for backward direction. if base.postcode and base.postcode > last: log().var_dump('skip backward', (base.postcode, last)) return penalty = self.penalty if not base.country and self.direction == -1 and query.dir_penalty < 0: penalty -= query.dir_penalty if self.direction == -1 or len(base.address) > 1 or base.postcode: log().comment('last word = name') yield dataclasses.replace(base, penalty=penalty, name=last, address=base.address[:-1]) # To paraphrase: # * if another name term comes before the last one and after the # housenumber # * a qualifier comes before the name # * the containing phrase is strictly typed if (base.housenumber and last.start > base.housenumber.end)\ or (base.qualifier and base.qualifier < last)\ or (query.nodes[last.start].ptype != qmod.PHRASE_ANY): return if base.housenumber and base.housenumber < last: penalty += 0.4 if len(query.source) > 1: penalty += 0.25 if self.direction == 0 and query.dir_penalty < 0: penalty -= query.dir_penalty for i in range(last.start + 1, last.end): addr, name = last.split(i) log().comment(f'split last word = name ({i - last.start})') yield dataclasses.replace(base, name=name, address=base.address[:-1] + [addr], penalty=penalty + query.nodes[i].word_break_penalty) def get_assignments(self, query: qmod.QueryStruct) -> Iterator[TokenAssignment]: """ Yield possible assignments for the current sequence. This function splits up general name assignments into name and address and yields all possible variants of that. """ base = TokenAssignment.from_ranges(self.seq) num_addr_tokens = sum(t.end - t.start for t in base.address) if num_addr_tokens > 50: return # Postcode search (postcode-only search is covered in next case) if base.postcode is not None and base.address: yield from self._get_assignments_postcode(base, query.num_token_slots()) # Postcode or country-only search if not base.address: if not base.housenumber and (base.postcode or base.country or base.near_item): log().comment('postcode/country search') yield dataclasses.replace(base, penalty=self.penalty) else: # ,
should give preference to postcode search if base.postcode and base.postcode.start == 0: self.penalty += 0.1 min_penalty = self.penalty + 2.0 # Left-to-right reading of the address if self.direction != -1: for result in self._get_assignments_address_forward(base, query): min_penalty = min(min_penalty, result.penalty) yield result # Right-to-left reading of the address if self.direction != 1: for result in self._get_assignments_address_backward(base, query): min_penalty = min(min_penalty, result.penalty) yield result # variant for special housenumber searches if base.housenumber and not base.qualifier: yield dataclasses.replace(base, penalty=min_penalty + 0.1) def yield_token_assignments(query: qmod.QueryStruct) -> Iterator[TokenAssignment]: """ Return possible word type assignments to word positions. The assignments are computed from the concrete tokens listed in the tokenized query. The result includes the penalty for transitions from one word type to another. It does not include penalties for transitions within a type. """ todo = [_TokenSequence([], direction=0 if query.source[0].ptype == qmod.PHRASE_ANY else 1)] while todo: state = todo.pop() node = query.nodes[state.end_pos] for tlist in node.starting: yield from _append_state_to_todo( query, todo, state.advance(tlist.ttype, tlist.end, True, node.word_break_penalty)) if node.partial is not None: yield from _append_state_to_todo( query, todo, state.advance(qmod.TOKEN_PARTIAL, state.end_pos + 1, node.btype == qmod.BREAK_PHRASE, node.word_break_penalty)) def _append_state_to_todo(query: qmod.QueryStruct, todo: List[_TokenSequence], newstate: Optional[_TokenSequence]) -> Iterator[TokenAssignment]: if newstate is not None: if newstate.end_pos == query.num_token_slots(): if newstate.recheck_sequence(): log().var_dump('Assignment', newstate) yield from newstate.get_assignments(query) elif not newstate.is_final(): todo.append(newstate) ================================================ FILE: src/nominatim_api/server/__init__.py ================================================ ================================================ FILE: src/nominatim_api/server/asgi_adaptor.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Base abstraction for implementing based on different ASGI frameworks. """ from typing import Optional, Any, NoReturn, Callable import abc import math from ..config import Configuration from ..core import NominatimAPIAsync from ..types import QueryStatistics from ..result_formatting import FormatDispatcher from .content_types import CONTENT_TEXT class ASGIAdaptor(abc.ABC): """ Adapter class for the different ASGI frameworks. Wraps functionality over concrete requests and responses. """ content_type: str = CONTENT_TEXT @abc.abstractmethod def get(self, name: str, default: Optional[str] = None) -> Optional[str]: """ Return an input parameter as a string. If the parameter was not provided, return the 'default' value. """ @abc.abstractmethod def get_header(self, name: str, default: Optional[str] = None) -> Optional[str]: """ Return a HTTP header parameter as a string. If the parameter was not provided, return the 'default' value. """ @abc.abstractmethod def error(self, msg: str, status: int = 400) -> Exception: """ Construct an appropriate exception from the given error message. The exception must result in a HTTP error with the given status. """ @abc.abstractmethod def create_response(self, status: int, output: str, num_results: int) -> Any: """ Create a response from the given parameters. The result will be returned by the endpoint functions. The adaptor may also return None when the response is created internally with some different means. The response must return the HTTP given status code 'status', set the HTTP content-type headers to the string provided and the body of the response to 'output'. """ @abc.abstractmethod def base_uri(self) -> str: """ Return the URI of the original request. """ @abc.abstractmethod def config(self) -> Configuration: """ Return the current configuration object. """ @abc.abstractmethod def formatting(self) -> FormatDispatcher: """ Return the formatting object to use. """ @abc.abstractmethod def query_stats(self) -> Optional[QueryStatistics]: """ Return the object for saving query statistics or None if no statistics are required. """ def get_int(self, name: str, default: Optional[int] = None) -> int: """ Return an input parameter as an int. Raises an exception if the parameter is given but not in an integer format. If 'default' is given, then it will be returned when the parameter is missing completely. When 'default' is None, an error will be raised on a missing parameter. """ value = self.get(name) if value is None: if default is not None: return default self.raise_error(f"Parameter '{name}' missing.") try: intval = int(value) except ValueError: self.raise_error(f"Parameter '{name}' must be a number.") return intval def get_float(self, name: str, default: Optional[float] = None) -> float: """ Return an input parameter as a flaoting-point number. Raises an exception if the parameter is given but not in an float format. If 'default' is given, then it will be returned when the parameter is missing completely. When 'default' is None, an error will be raised on a missing parameter. """ value = self.get(name) if value is None: if default is not None: return default self.raise_error(f"Parameter '{name}' missing.") try: fval = float(value) except ValueError: self.raise_error(f"Parameter '{name}' must be a number.") if math.isnan(fval) or math.isinf(fval): self.raise_error(f"Parameter '{name}' must be a number.") return fval def get_bool(self, name: str, default: Optional[bool] = None) -> bool: """ Return an input parameter as bool. Only '0' is accepted as an input for 'false' all other inputs will be interpreted as 'true'. If 'default' is given, then it will be returned when the parameter is missing completely. When 'default' is None, an error will be raised on a missing parameter. """ value = self.get(name) if value is None: if default is not None: return default self.raise_error(f"Parameter '{name}' missing.") return value != '0' def raise_error(self, msg: str, status: int = 400) -> NoReturn: """ Raise an exception resulting in the given HTTP status and message. The message will be formatted according to the output format chosen by the request. """ raise self.error(self.formatting().format_error(self.content_type, msg, status), status) EndpointFunc = Callable[[NominatimAPIAsync, ASGIAdaptor], Any] ================================================ FILE: src/nominatim_api/server/content_types.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Constants for various content types for server responses. """ CONTENT_TEXT = 'text/plain; charset=utf-8' CONTENT_XML = 'text/xml; charset=utf-8' CONTENT_HTML = 'text/html; charset=utf-8' CONTENT_JSON = 'application/json; charset=utf-8' ================================================ FILE: src/nominatim_api/server/falcon/__init__.py ================================================ ================================================ FILE: src/nominatim_api/server/falcon/server.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Server implementation using the falcon webserver framework. """ from __future__ import annotations from typing import Optional, Mapping, Any, List, cast from pathlib import Path import asyncio import datetime as dt from falcon.asgi import App, Request, Response from ...config import Configuration from ...core import NominatimAPIAsync from ...types import QueryStatistics from ... import v1 as api_impl from ...result_formatting import FormatDispatcher, load_format_dispatcher from ... import logging as loglib from ..asgi_adaptor import ASGIAdaptor, EndpointFunc class HTTPNominatimError(Exception): """ A special exception class for errors raised during processing. """ def __init__(self, msg: str, status: int, content_type: str) -> None: self.msg = msg self.status = status self.content_type = content_type async def nominatim_error_handler(req: Request, resp: Response, exception: HTTPNominatimError, _: Any) -> None: """ Special error handler that passes message and content type as per exception info. """ resp.status = exception.status resp.text = exception.msg resp.content_type = exception.content_type async def timeout_error_handler(req: Request, resp: Response, exception: TimeoutError, _: Any) -> None: """ Special error handler that passes message and content type as per exception info. """ resp.status = 503 loglib.log().comment('Aborted: Query took too long to process.') logdata = loglib.get_and_disable() if logdata: resp.text = logdata resp.content_type = 'text/html; charset=utf-8' else: resp.text = "Query took too long to process." resp.content_type = 'text/plain; charset=utf-8' class ParamWrapper(ASGIAdaptor): """ Adaptor class for server glue to Falcon framework. """ def __init__(self, req: Request, resp: Response, config: Configuration, formatter: FormatDispatcher) -> None: self.request = req self.response = resp self._config = config self._formatter = formatter def get(self, name: str, default: Optional[str] = None) -> Optional[str]: return self.request.get_param(name, default=default) def get_header(self, name: str, default: Optional[str] = None) -> Optional[str]: return self.request.get_header(name, default=default) def error(self, msg: str, status: int = 400) -> HTTPNominatimError: return HTTPNominatimError(msg, status, self.content_type) def create_response(self, status: int, output: str, num_results: int) -> None: self.response.context.num_results = num_results self.response.status = status self.response.text = output self.response.content_type = self.content_type def base_uri(self) -> str: return self.request.forwarded_prefix def config(self) -> Configuration: return self._config def formatting(self) -> FormatDispatcher: return self._formatter def query_stats(self) -> Optional[QueryStatistics]: return cast(Optional[QueryStatistics], getattr(self.request.context, 'query_stats', None)) class EndpointWrapper: """ Converter for server glue endpoint functions to Falcon request handlers. """ def __init__(self, name: str, func: EndpointFunc, api: NominatimAPIAsync, formatter: FormatDispatcher) -> None: self.name = name self.func = func self.api = api self.formatter = formatter async def on_get(self, req: Request, resp: Response) -> None: """ Implementation of the endpoint. """ await self.func(self.api, ParamWrapper(req, resp, self.api.config, self.formatter)) class FileLoggingMiddleware: """ Middleware to log selected requests into a file. """ def __init__(self, file_name: str, logstr: str): self.logstr = logstr + '\n' self.fd = open(file_name, 'a', buffering=1, encoding='utf8') async def process_request(self, req: Request, _: Response) -> None: """ Callback before the request starts timing. """ req.context.query_stats = QueryStatistics() async def process_response(self, req: Request, resp: Response, resource: Optional[EndpointWrapper], req_succeeded: bool) -> None: """ Callback after requests writes to the logfile. It only writes logs for successful requests for search, reverse and lookup. """ qs = req.context.query_stats if not req_succeeded or 'start' not in qs\ or resource is None or resp.status != 200\ or resource.name not in ('reverse', 'search', 'lookup', 'details'): return qs['endpoint'] = resource.name qs['query_string'] = req.scope['query_string'].decode('utf8') qs['results_total'] = getattr(resp.context, 'num_results', 0) for param in ('start', 'end', 'start_query'): if isinstance(qs.get(param), dt.datetime): qs[param] = qs[param].replace(tzinfo=None)\ .isoformat(sep=' ', timespec='milliseconds') self.fd.write(self.logstr.format_map(qs)) class APIMiddleware: """ Middleware managing the Nominatim database connection. """ def __init__(self, project_dir: Path, environ: Optional[Mapping[str, str]]) -> None: self.api = NominatimAPIAsync(project_dir, environ) self.app: Optional[App[Request, Response]] = None @property def config(self) -> Configuration: """ Get the configuration for Nominatim. """ return self.api.config def set_app(self, app: App[Request, Response]) -> None: """ Set the Falcon application this middleware is connected to. """ self.app = app async def process_startup(self, *_: Any) -> None: """ Process the ASGI lifespan startup event. """ assert self.app is not None legacy_urls = self.api.config.get_bool('SERVE_LEGACY_URLS') formatter = load_format_dispatcher('v1', self.api.config.project_dir) for name, func in await api_impl.get_routes(self.api): endpoint = EndpointWrapper(name, func, self.api, formatter) # If func is a LazySearchEndpoint, give it a reference to wrapper # so it can replace wrapper.func dynamically if hasattr(func, 'set_wrapper'): func.set_wrapper(endpoint) self.app.add_route(f"/{name}", endpoint) if legacy_urls: self.app.add_route(f"/{name}.php", endpoint) async def process_shutdown(self, *_: Any) -> None: """Process the ASGI lifespan shutdown event. """ await self.api.close() def get_application(project_dir: Path, environ: Optional[Mapping[str, str]] = None) -> App[Request, Response]: """ Create a Nominatim Falcon ASGI application. """ apimw = APIMiddleware(project_dir, environ) middleware: List[Any] = [apimw] log_file = apimw.config.LOG_FILE if log_file: middleware.append(FileLoggingMiddleware(log_file, apimw.config.LOG_FORMAT)) app = App(cors_enable=apimw.config.get_bool('CORS_NOACCESSCONTROL'), middleware=middleware) apimw.set_app(app) app.add_error_handler(HTTPNominatimError, nominatim_error_handler) app.add_error_handler(TimeoutError, timeout_error_handler) # different from TimeoutError in Python <= 3.10 app.add_error_handler(asyncio.TimeoutError, timeout_error_handler) # type: ignore[arg-type] return app def run_wsgi() -> App[Request, Response]: """ Entry point for uvicorn. Make sure uvicorn is run from the project directory. """ return get_application(Path('.')) ================================================ FILE: src/nominatim_api/server/starlette/__init__.py ================================================ ================================================ FILE: src/nominatim_api/server/starlette/server.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2026 by the Nominatim developer community. # For a full list of authors see the git log. """ Server implementation using the starlette webserver framework. """ from typing import Any, Optional, Mapping, Callable, cast, Coroutine, Dict, \ Awaitable, AsyncIterator from pathlib import Path import asyncio import contextlib import datetime as dt from starlette.applications import Starlette from starlette.routing import Route from starlette.exceptions import HTTPException from starlette.responses import Response, PlainTextResponse, HTMLResponse from starlette.requests import Request from starlette.middleware import Middleware from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint from starlette.middleware.cors import CORSMiddleware from ...config import Configuration from ...core import NominatimAPIAsync from ...types import QueryStatistics from ... import v1 as api_impl from ...result_formatting import FormatDispatcher, load_format_dispatcher from ..asgi_adaptor import ASGIAdaptor, EndpointFunc from ... import logging as loglib class ParamWrapper(ASGIAdaptor): """ Adaptor class for server glue to Starlette framework. """ def __init__(self, request: Request) -> None: self.request = request def get(self, name: str, default: Optional[str] = None) -> Optional[str]: return self.request.query_params.get(name, default) def get_header(self, name: str, default: Optional[str] = None) -> Optional[str]: return self.request.headers.get(name, default) def error(self, msg: str, status: int = 400) -> HTTPException: return HTTPException(status, detail=msg, headers={'content-type': self.content_type}) def create_response(self, status: int, output: str, num_results: int) -> Response: setattr(self.request.state, 'num_results', num_results) return Response(output, status_code=status, media_type=self.content_type) def base_uri(self) -> str: scheme = self.request.url.scheme host = self.request.url.hostname port = self.request.url.port root = self.request.scope['root_path'] if (scheme == 'http' and port == 80) or (scheme == 'https' and port == 443): port = None if port is not None: return f"{scheme}://{host}:{port}{root}" return f"{scheme}://{host}{root}" def config(self) -> Configuration: return cast(Configuration, self.request.app.state.API.config) def formatting(self) -> FormatDispatcher: return cast(FormatDispatcher, self.request.app.state.formatter) def query_stats(self) -> Optional[QueryStatistics]: return cast(Optional[QueryStatistics], getattr(self.request.state, 'query_stats', None)) def _wrap_endpoint(func: EndpointFunc)\ -> Callable[[Request], Coroutine[Any, Any, Response]]: async def _callback(request: Request) -> Response: return cast(Response, await func(request.app.state.API, ParamWrapper(request))) return _callback class FileLoggingMiddleware(BaseHTTPMiddleware): """ Middleware to log selected requests into a file. """ def __init__(self, app: Starlette, file_name: str = '', logstr: str = ''): super().__init__(app) self.fd = open(file_name, 'a', buffering=1, encoding='utf8') self.logstr = logstr + '\n' async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: qs = QueryStatistics() setattr(request.state, 'query_stats', qs) response = await call_next(request) if response.status_code != 200 or 'start' not in qs: return response for endpoint in ('reverse', 'search', 'lookup', 'details'): if request.url.path.startswith('/' + endpoint): qs['endpoint'] = endpoint break else: return response qs['query_string'] = request.scope['query_string'].decode('utf8') qs['results_total'] = getattr(request.state, 'num_results', 0) for param in ('start', 'end', 'start_query'): if isinstance(qs.get(param), dt.datetime): qs[param] = qs[param].replace(tzinfo=None)\ .isoformat(sep=' ', timespec='milliseconds') self.fd.write(self.logstr.format_map(qs)) return response async def timeout_error(request: Request, _: Exception) -> Response: """ Error handler for query timeouts. """ loglib.log().comment('Aborted: Query took too long to process.') logdata = loglib.get_and_disable() if logdata: return HTMLResponse(logdata) return PlainTextResponse("Query took too long to process.", status_code=503) def get_application(project_dir: Path, environ: Optional[Mapping[str, str]] = None, debug: bool = True) -> Starlette: """ Create a Nominatim falcon ASGI application. """ config = Configuration(project_dir, environ) middleware = [] if config.get_bool('CORS_NOACCESSCONTROL'): middleware.append(Middleware(CORSMiddleware, allow_origins=['*'], allow_methods=['GET', 'OPTIONS'], max_age=86400)) log_file = config.LOG_FILE if log_file: middleware.append(Middleware(FileLoggingMiddleware, file_name=log_file, # type: ignore logstr=config.LOG_FORMAT)) exceptions: Dict[Any, Callable[[Request, Exception], Awaitable[Response]]] = { TimeoutError: timeout_error, asyncio.TimeoutError: timeout_error } @contextlib.asynccontextmanager async def lifespan(app: Starlette) -> AsyncIterator[Any]: app.state.API = NominatimAPIAsync(project_dir, environ) config = app.state.API.config legacy_urls = config.get_bool('SERVE_LEGACY_URLS') for name, func in await api_impl.get_routes(app.state.API): endpoint = _wrap_endpoint(func) app.routes.append(Route(f"/{name}", endpoint=endpoint)) if legacy_urls: app.routes.append(Route(f"/{name}.php", endpoint=endpoint)) yield await app.state.API.close() app = Starlette(debug=debug, middleware=middleware, exception_handlers=exceptions, lifespan=lifespan) app.state.formatter = load_format_dispatcher('v1', project_dir) return app def run_wsgi() -> Starlette: """ Entry point for uvicorn. """ return get_application(Path('.'), debug=False) ================================================ FILE: src/nominatim_api/sql/__init__.py ================================================ ================================================ FILE: src/nominatim_api/sql/async_core_library.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Import the base library to use with asynchronous SQLAlchemy. """ from typing import Any # flake8: noqa try: import sqlalchemy.dialects.postgresql.psycopg import psycopg PGCORE_LIB = 'psycopg' PGCORE_ERROR: Any = psycopg.Error except ModuleNotFoundError: import sqlalchemy.dialects.postgresql.asyncpg import asyncpg PGCORE_LIB = 'asyncpg' PGCORE_ERROR = asyncpg.PostgresError ================================================ FILE: src/nominatim_api/sql/sqlalchemy_functions.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Custom functions and expressions for SQLAlchemy. """ from __future__ import annotations from typing import Any import sqlalchemy as sa from sqlalchemy.ext.compiler import compiles from ..typing import SaColumn class PlacexGeometryReverseLookuppolygon(sa.sql.functions.GenericFunction[Any]): """ Check for conditions that allow partial index use on 'idx_placex_geometry_reverse_lookupPolygon'. Needs to be constant, so that the query planner picks them up correctly in prepared statements. """ name = 'PlacexGeometryReverseLookuppolygon' inherit_cache = True @compiles(PlacexGeometryReverseLookuppolygon) def _default_intersects(element: PlacexGeometryReverseLookuppolygon, compiler: 'sa.Compiled', **kw: Any) -> str: return ("(ST_GeometryType(placex.geometry) in ('ST_Polygon', 'ST_MultiPolygon')" " AND placex.rank_address between 4 and 25" " AND placex.name is not null" " AND placex.indexed_status = 0" " AND placex.linked_place_id is null)") @compiles(PlacexGeometryReverseLookuppolygon, 'sqlite') def _sqlite_intersects(element: PlacexGeometryReverseLookuppolygon, compiler: 'sa.Compiled', **kw: Any) -> str: return ("(ST_GeometryType(placex.geometry) in ('POLYGON', 'MULTIPOLYGON')" " AND placex.rank_address between 4 and 25" " AND placex.name is not null" " AND placex.indexed_status = 0" " AND placex.linked_place_id is null)") class IntersectsReverseDistance(sa.sql.functions.GenericFunction[Any]): name = 'IntersectsReverseDistance' inherit_cache = True def __init__(self, table: sa.Table, geom: SaColumn) -> None: super().__init__(table.c.geometry, table.c.rank_search, geom) self.tablename = table.name @compiles(IntersectsReverseDistance) def default_reverse_place_diameter(element: IntersectsReverseDistance, compiler: 'sa.Compiled', **kw: Any) -> str: table = element.tablename return f"({table}.rank_address between 4 and 25"\ f" AND {table}.name is not null"\ f" AND {table}.linked_place_id is null"\ f" AND {table}.osm_type = 'N'" + \ " AND ST_Buffer(%s, reverse_place_diameter(%s)) && %s)" \ % tuple(map(lambda c: compiler.process(c, **kw), element.clauses)) @compiles(IntersectsReverseDistance, 'sqlite') def sqlite_reverse_place_diameter(element: IntersectsReverseDistance, compiler: 'sa.Compiled', **kw: Any) -> str: geom1, rank, geom2 = list(element.clauses) table = element.tablename return (f"({table}.rank_address between 4 and 25" f" AND {table}.name is not null" f" AND {table}.linked_place_id is null" f" AND {table}.osm_type = 'N'" " AND MbrIntersects(%s, ST_Expand(%s, 14.0 * exp(-0.2 * %s) - 0.03))" f" AND {table}.place_id IN" " (SELECT place_id FROM placex_place_node_areas" " WHERE ROWID IN (SELECT ROWID FROM SpatialIndex" " WHERE f_table_name = 'placex_place_node_areas'" " AND search_frame = %s)))") % ( compiler.process(geom1, **kw), compiler.process(geom2, **kw), compiler.process(rank, **kw), compiler.process(geom2, **kw)) class IsBelowReverseDistance(sa.sql.functions.GenericFunction[Any]): name = 'IsBelowReverseDistance' inherit_cache = True @compiles(IsBelowReverseDistance) def default_is_below_reverse_distance(element: IsBelowReverseDistance, compiler: 'sa.Compiled', **kw: Any) -> str: dist, rank = list(element.clauses) return "%s < reverse_place_diameter(%s)" % (compiler.process(dist, **kw), compiler.process(rank, **kw)) @compiles(IsBelowReverseDistance, 'sqlite') def sqlite_is_below_reverse_distance(element: IsBelowReverseDistance, compiler: 'sa.Compiled', **kw: Any) -> str: dist, rank = list(element.clauses) return "%s < 14.0 * exp(-0.2 * %s) - 0.03" % (compiler.process(dist, **kw), compiler.process(rank, **kw)) class IsAddressPoint(sa.sql.functions.GenericFunction[Any]): name = 'IsAddressPoint' inherit_cache = True def __init__(self, table: sa.Table) -> None: super().__init__(table.c.rank_address, table.c.housenumber, table.c.name, table.c.address) @compiles(IsAddressPoint) def default_is_address_point(element: IsAddressPoint, compiler: 'sa.Compiled', **kw: Any) -> str: rank, hnr, name, address = list(element.clauses) return "(%s = 30 AND (%s IS NULL OR NOT %s ? '_inherited')" \ " AND (%s IS NOT NULL OR %s ? 'addr:housename'))" % ( compiler.process(rank, **kw), compiler.process(address, **kw), compiler.process(address, **kw), compiler.process(hnr, **kw), compiler.process(name, **kw)) @compiles(IsAddressPoint, 'sqlite') def sqlite_is_address_point(element: IsAddressPoint, compiler: 'sa.Compiled', **kw: Any) -> str: rank, hnr, name, address = list(element.clauses) return "(%s = 30 AND json_extract(%s, '$._inherited') IS NULL" \ " AND coalesce(%s, json_extract(%s, '$.addr:housename')) IS NOT NULL)" % ( compiler.process(rank, **kw), compiler.process(address, **kw), compiler.process(hnr, **kw), compiler.process(name, **kw)) class CrosscheckNames(sa.sql.functions.GenericFunction[Any]): """ Check if in the given list of names in parameters 1 any of the names from the JSON array in parameter 2 are contained. """ name = 'CrosscheckNames' inherit_cache = True @compiles(CrosscheckNames) def compile_crosscheck_names(element: CrosscheckNames, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "coalesce(avals(%s) && ARRAY(SELECT * FROM json_array_elements_text(%s)), false)" % ( compiler.process(arg1, **kw), compiler.process(arg2, **kw)) @compiles(CrosscheckNames, 'sqlite') def compile_sqlite_crosscheck_names(element: CrosscheckNames, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "EXISTS(SELECT *"\ " FROM json_each(%s) as name, json_each(%s) as match_name"\ " WHERE name.value = match_name.value)"\ % (compiler.process(arg1, **kw), compiler.process(arg2, **kw)) class JsonArrayEach(sa.sql.functions.GenericFunction[Any]): """ Return elements of a json array as a set. """ name = 'JsonArrayEach' inherit_cache = True @compiles(JsonArrayEach) def default_json_array_each(element: JsonArrayEach, compiler: 'sa.Compiled', **kw: Any) -> str: return "json_array_elements(%s)" % compiler.process(element.clauses, **kw) @compiles(JsonArrayEach, 'sqlite') def sqlite_json_array_each(element: JsonArrayEach, compiler: 'sa.Compiled', **kw: Any) -> str: return "json_each(%s)" % compiler.process(element.clauses, **kw) class Greatest(sa.sql.functions.GenericFunction[Any]): """ Function to compute maximum of all its input parameters. """ name = 'greatest' inherit_cache = True @compiles(Greatest, 'sqlite') def sqlite_greatest(element: Greatest, compiler: 'sa.Compiled', **kw: Any) -> str: return "max(%s)" % compiler.process(element.clauses, **kw) class RegexpWord(sa.sql.functions.GenericFunction[Any]): """ Check if a full word is in a given string. """ name = 'RegexpWord' inherit_cache = True @compiles(RegexpWord, 'postgresql') def postgres_regexp_nocase(element: RegexpWord, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "%s ~* ('\\m(' || %s || ')\\M')::text" \ % (compiler.process(arg2, **kw), compiler.process(arg1, **kw)) @compiles(RegexpWord, 'sqlite') def sqlite_regexp_nocase(element: RegexpWord, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "regexp('\\b(' || %s || ')\\b', %s)"\ % (compiler.process(arg1, **kw), compiler.process(arg2, **kw)) ================================================ FILE: src/nominatim_api/sql/sqlalchemy_schema.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2026 by the Nominatim developer community. # For a full list of authors see the git log. """ SQLAlchemy definitions for all tables used by the frontend. """ import sqlalchemy as sa from .sqlalchemy_types import Geometry, KeyValueStore, IntArray class SearchTables: """ Data class that holds the tables of the Nominatim database. This schema strictly reflects the read-access view of the database. Any data used for updates only will not be visible. """ def __init__(self, meta: sa.MetaData) -> None: self.meta = meta self.import_status = sa.Table( 'import_status', meta, sa.Column('lastimportdate', sa.DateTime(True), nullable=False), sa.Column('sequence_id', sa.Integer), sa.Column('indexed', sa.Boolean)) self.properties = sa.Table( 'nominatim_properties', meta, sa.Column('property', sa.Text, nullable=False), sa.Column('value', sa.Text)) self.placex = sa.Table( 'placex', meta, sa.Column('place_id', sa.BigInteger, nullable=False), sa.Column('parent_place_id', sa.BigInteger), sa.Column('linked_place_id', sa.BigInteger), sa.Column('importance', sa.Float, nullable=False), sa.Column('indexed_date', sa.DateTime), sa.Column('rank_address', sa.SmallInteger, nullable=False), sa.Column('rank_search', sa.SmallInteger, nullable=False), sa.Column('indexed_status', sa.SmallInteger, nullable=False), sa.Column('osm_type', sa.String(1), nullable=False), sa.Column('osm_id', sa.BigInteger, nullable=False), sa.Column('class', sa.Text, nullable=False, key='class_'), sa.Column('type', sa.Text, nullable=False), sa.Column('admin_level', sa.SmallInteger), sa.Column('name', KeyValueStore), sa.Column('address', KeyValueStore), sa.Column('extratags', KeyValueStore), sa.Column('geometry', Geometry, nullable=False), sa.Column('wikipedia', sa.Text), sa.Column('country_code', sa.String(2)), sa.Column('housenumber', sa.Text), sa.Column('postcode', sa.Text), sa.Column('centroid', Geometry, nullable=False)) self.addressline = sa.Table( 'place_addressline', meta, sa.Column('place_id', sa.BigInteger, nullable=False), sa.Column('address_place_id', sa.BigInteger, nullable=False), sa.Column('distance', sa.Float, nullable=False), sa.Column('fromarea', sa.Boolean, nullable=False), sa.Column('isaddress', sa.Boolean, nullable=False)) self.postcode = sa.Table( 'location_postcodes', meta, sa.Column('place_id', sa.BigInteger, nullable=False), sa.Column('parent_place_id', sa.BigInteger), sa.Column('osm_id', sa.BigInteger), sa.Column('rank_search', sa.SmallInteger, nullable=False), sa.Column('indexed_status', sa.SmallInteger, nullable=False), sa.Column('indexed_date', sa.DateTime), sa.Column('country_code', sa.String(2), nullable=False), sa.Column('postcode', sa.Text, nullable=False), sa.Column('centroid', Geometry, nullable=False), sa.Column('geometry', Geometry, nullable=False)) self.osmline = sa.Table( 'location_property_osmline', meta, sa.Column('place_id', sa.BigInteger, nullable=False), sa.Column('osm_id', sa.BigInteger, nullable=False), sa.Column('parent_place_id', sa.BigInteger), sa.Column('indexed_date', sa.DateTime), sa.Column('startnumber', sa.Integer), sa.Column('endnumber', sa.Integer), sa.Column('step', sa.SmallInteger), sa.Column('indexed_status', sa.SmallInteger, nullable=False), sa.Column('linegeo', Geometry, nullable=False), sa.Column('address', KeyValueStore), sa.Column('postcode', sa.Text), sa.Column('country_code', sa.String(2))) self.country_name = sa.Table( 'country_name', meta, sa.Column('country_code', sa.String(2)), sa.Column('name', KeyValueStore), sa.Column('derived_name', KeyValueStore), sa.Column('partition', sa.Integer)) self.country_grid = sa.Table( 'country_osm_grid', meta, sa.Column('country_code', sa.String(2)), sa.Column('area', sa.Float), sa.Column('geometry', Geometry)) # The following tables are not necessarily present. self.search_name = sa.Table( 'search_name', meta, sa.Column('place_id', sa.BigInteger, nullable=False), sa.Column('importance', sa.Float, nullable=False), sa.Column('address_rank', sa.SmallInteger, nullable=False), sa.Column('name_vector', IntArray, nullable=False), sa.Column('nameaddress_vector', IntArray, nullable=False), sa.Column('country_code', sa.String(2)), sa.Column('centroid', Geometry, nullable=False)) self.tiger = sa.Table( 'location_property_tiger', meta, sa.Column('place_id', sa.BigInteger, nullable=False), sa.Column('parent_place_id', sa.BigInteger), sa.Column('startnumber', sa.Integer, nullable=False), sa.Column('endnumber', sa.Integer, nullable=False), sa.Column('step', sa.SmallInteger, nullable=False), sa.Column('linegeo', Geometry, nullable=False), sa.Column('postcode', sa.Text)) self.placex_entrance = sa.Table( 'placex_entrance', meta, sa.Column('place_id', sa.BigInteger, nullable=False), sa.Column('osm_id', sa.BigInteger, nullable=False), sa.Column('type', sa.Text, nullable=False), sa.Column('location', Geometry, nullable=False), sa.Column('extratags', KeyValueStore)) ================================================ FILE: src/nominatim_api/sql/sqlalchemy_types/__init__.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Module with custom types for SQLAlchemy """ from .geometry import (Geometry as Geometry) from .int_array import (IntArray as IntArray) from .key_value import (KeyValueStore as KeyValueStore) from .json import (Json as Json) ================================================ FILE: src/nominatim_api/sql/sqlalchemy_types/geometry.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Custom types for SQLAlchemy. """ from __future__ import annotations from typing import Callable, Any, cast import sqlalchemy as sa from sqlalchemy.ext.compiler import compiles from sqlalchemy import types from ...typing import SaColumn, SaBind class Geometry_DistanceSpheroid(sa.sql.expression.FunctionElement[float]): """ Function to compute the spherical distance in meters. """ type = sa.Float() name = 'Geometry_DistanceSpheroid' inherit_cache = True @compiles(Geometry_DistanceSpheroid) def _default_distance_spheroid(element: Geometry_DistanceSpheroid, compiler: 'sa.Compiled', **kw: Any) -> str: return "ST_DistanceSpheroid(%s,"\ " 'SPHEROID[\"WGS 84\",6378137,298.257223563, AUTHORITY[\"EPSG\",\"7030\"]]')"\ % compiler.process(element.clauses, **kw) @compiles(Geometry_DistanceSpheroid, 'sqlite') def _spatialite_distance_spheroid(element: Geometry_DistanceSpheroid, compiler: 'sa.Compiled', **kw: Any) -> str: return "COALESCE(Distance(%s, true), 0.0)" % compiler.process(element.clauses, **kw) class Geometry_IsLineLike(sa.sql.expression.FunctionElement[Any]): """ Check if the geometry is a line or multiline. """ name = 'Geometry_IsLineLike' inherit_cache = True @compiles(Geometry_IsLineLike) def _default_is_line_like(element: Geometry_IsLineLike, compiler: 'sa.Compiled', **kw: Any) -> str: return "ST_GeometryType(%s) IN ('ST_LineString', 'ST_MultiLineString')" % \ compiler.process(element.clauses, **kw) @compiles(Geometry_IsLineLike, 'sqlite') def _sqlite_is_line_like(element: Geometry_IsLineLike, compiler: 'sa.Compiled', **kw: Any) -> str: return "ST_GeometryType(%s) IN ('LINESTRING', 'MULTILINESTRING')" % \ compiler.process(element.clauses, **kw) class Geometry_IsAreaLike(sa.sql.expression.FunctionElement[Any]): """ Check if the geometry is a polygon or multipolygon. """ name = 'Geometry_IsLineLike' inherit_cache = True @compiles(Geometry_IsAreaLike) def _default_is_area_like(element: Geometry_IsAreaLike, compiler: 'sa.Compiled', **kw: Any) -> str: return "ST_GeometryType(%s) IN ('ST_Polygon', 'ST_MultiPolygon')" % \ compiler.process(element.clauses, **kw) @compiles(Geometry_IsAreaLike, 'sqlite') def _sqlite_is_area_like(element: Geometry_IsAreaLike, compiler: 'sa.Compiled', **kw: Any) -> str: return "ST_GeometryType(%s) IN ('POLYGON', 'MULTIPOLYGON')" % \ compiler.process(element.clauses, **kw) class Geometry_IntersectsBbox(sa.sql.expression.FunctionElement[Any]): """ Check if the bounding boxes of the given geometries intersect. """ name = 'Geometry_IntersectsBbox' inherit_cache = True @compiles(Geometry_IntersectsBbox) def _default_intersects(element: Geometry_IntersectsBbox, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "%s && %s" % (compiler.process(arg1, **kw), compiler.process(arg2, **kw)) @compiles(Geometry_IntersectsBbox, 'sqlite') def _sqlite_intersects(element: Geometry_IntersectsBbox, compiler: 'sa.Compiled', **kw: Any) -> str: return "MbrIntersects(%s) = 1" % compiler.process(element.clauses, **kw) class Geometry_ColumnIntersectsBbox(sa.sql.expression.FunctionElement[Any]): """ Check if the bounding box of the geometry intersects with the given table column, using the spatial index for the column. The index must exist or the query may return nothing. """ name = 'Geometry_ColumnIntersectsBbox' inherit_cache = True @compiles(Geometry_ColumnIntersectsBbox) def default_intersects_column(element: Geometry_ColumnIntersectsBbox, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "%s && %s" % (compiler.process(arg1, **kw), compiler.process(arg2, **kw)) @compiles(Geometry_ColumnIntersectsBbox, 'sqlite') def spatialite_intersects_column(element: Geometry_ColumnIntersectsBbox, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "MbrIntersects(%s, %s) = 1 and "\ "%s.ROWID IN (SELECT ROWID FROM SpatialIndex "\ " WHERE f_table_name = '%s' AND f_geometry_column = '%s' "\ " AND search_frame = %s)"\ % (compiler.process(arg1, **kw), compiler.process(arg2, **kw), arg1.table.name, arg1.table.name, arg1.name, compiler.process(arg2, **kw)) class Geometry_ColumnDWithin(sa.sql.expression.FunctionElement[Any]): """ Check if the geometry is within the distance of the given table column, using the spatial index for the column. The index must exist or the query may return nothing. """ name = 'Geometry_ColumnDWithin' inherit_cache = True @compiles(Geometry_ColumnDWithin) def default_dwithin_column(element: Geometry_ColumnDWithin, compiler: 'sa.Compiled', **kw: Any) -> str: return "ST_DWithin(%s)" % compiler.process(element.clauses, **kw) @compiles(Geometry_ColumnDWithin, 'sqlite') def spatialite_dwithin_column(element: Geometry_ColumnDWithin, compiler: 'sa.Compiled', **kw: Any) -> str: geom1, geom2, dist = list(element.clauses) return "ST_Distance(%s, %s) < %s and "\ "%s.ROWID IN (SELECT ROWID FROM SpatialIndex "\ " WHERE f_table_name = '%s' AND f_geometry_column = '%s' "\ " AND search_frame = ST_Expand(%s, %s))"\ % (compiler.process(geom1, **kw), compiler.process(geom2, **kw), compiler.process(dist, **kw), geom1.table.name, geom1.table.name, geom1.name, compiler.process(geom2, **kw), compiler.process(dist, **kw)) class Geometry(types.UserDefinedType): # type: ignore[type-arg] """ Simplified type decorator for PostGIS geometry. This type only supports geometries in 4326 projection. """ cache_ok = True def __init__(self, subtype: str = 'Geometry'): self.subtype = subtype def get_col_spec(self, **_: Any) -> str: return f'GEOMETRY({self.subtype}, 4326)' def bind_processor(self, dialect: 'sa.Dialect') -> Callable[[Any], str]: def process(value: Any) -> str: if value is None: return 'null' if isinstance(value, str): return value return cast(str, value.to_wkt()) return process def result_processor(self, dialect: 'sa.Dialect', coltype: object) -> Callable[[Any], str]: def process(value: Any) -> str: assert isinstance(value, str) return value return process def column_expression(self, col: SaColumn) -> SaColumn: return sa.func.ST_AsEWKB(col) def bind_expression(self, bindvalue: SaBind) -> SaColumn: return sa.func.ST_GeomFromText(bindvalue, sa.text('4326'), type_=self) class comparator_factory(types.UserDefinedType.Comparator): # type: ignore[type-arg] def intersects(self, other: SaColumn, use_index: bool = True) -> 'sa.Operators': if not use_index: return Geometry_IntersectsBbox(sa.func.coalesce(sa.null(), self.expr), other) if isinstance(self.expr, sa.Column): return Geometry_ColumnIntersectsBbox(self.expr, other) return Geometry_IntersectsBbox(self.expr, other) def is_line_like(self) -> SaColumn: return Geometry_IsLineLike(self) def is_area(self) -> SaColumn: return Geometry_IsAreaLike(self) def within_distance(self, other: SaColumn, distance: SaColumn) -> SaColumn: if isinstance(self.expr, sa.Column): return Geometry_ColumnDWithin(self.expr, other, distance) return self.ST_Distance(other) < distance def ST_Distance(self, other: SaColumn) -> SaColumn: return sa.func.ST_Distance(self, other, type_=sa.Float) def ST_Contains(self, other: SaColumn) -> SaColumn: return sa.func.ST_Contains(self, other, type_=sa.Boolean) def ST_CoveredBy(self, other: SaColumn) -> SaColumn: return sa.func.ST_CoveredBy(self, other, type_=sa.Boolean) def ST_ClosestPoint(self, other: SaColumn) -> SaColumn: return sa.func.coalesce(sa.func.ST_ClosestPoint(self, other, type_=Geometry), other) def ST_Buffer(self, other: SaColumn) -> SaColumn: return sa.func.ST_Buffer(self, other, type_=Geometry) def ST_Expand(self, other: SaColumn) -> SaColumn: return sa.func.ST_Expand(self, other, type_=Geometry) def ST_Collect(self) -> SaColumn: return sa.func.ST_Collect(self, type_=Geometry) def ST_Centroid(self) -> SaColumn: return sa.func.ST_Centroid(self, type_=Geometry) def ST_LineInterpolatePoint(self, other: SaColumn) -> SaColumn: return sa.func.ST_LineInterpolatePoint(self, other, type_=Geometry) def ST_LineLocatePoint(self, other: SaColumn) -> SaColumn: return sa.func.ST_LineLocatePoint(self, other, type_=sa.Float) def distance_spheroid(self, other: SaColumn) -> SaColumn: return Geometry_DistanceSpheroid(self, other) @compiles(Geometry, 'sqlite') def get_col_spec(self, *args, **kwargs): # type: ignore[no-untyped-def] return 'GEOMETRY' SQLITE_FUNCTION_ALIAS = ( ('ST_AsEWKB', sa.Text, 'AsEWKB'), ('ST_GeomFromEWKT', Geometry, 'GeomFromEWKT'), ('ST_AsGeoJSON', sa.Text, 'AsGeoJSON'), ('ST_AsKML', sa.Text, 'AsKML'), ('ST_AsSVG', sa.Text, 'AsSVG'), ('ST_LineLocatePoint', sa.Float, 'ST_Line_Locate_Point'), ('ST_LineInterpolatePoint', sa.Float, 'ST_Line_Interpolate_Point'), ) def _add_function_alias(func: str, ftype: type, alias: str) -> None: _FuncDef = type(func, (sa.sql.functions.GenericFunction, ), { "type": ftype(), "name": func, "identifier": func, "inherit_cache": True}) func_templ = f"{alias}(%s)" def _sqlite_impl(element: Any, compiler: Any, **kw: Any) -> Any: return func_templ % compiler.process(element.clauses, **kw) compiles(_FuncDef, 'sqlite')(_sqlite_impl) for alias in SQLITE_FUNCTION_ALIAS: _add_function_alias(*alias) ================================================ FILE: src/nominatim_api/sql/sqlalchemy_types/int_array.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2026 by the Nominatim developer community. # For a full list of authors see the git log. """ Custom type for an array of integers. """ from typing import Any, List, Optional import sqlalchemy as sa from sqlalchemy.ext.compiler import compiles from sqlalchemy.dialects.postgresql import ARRAY from ...typing import SaDialect, SaColumn class IntList(sa.types.TypeDecorator[Any]): """ A list of integers saved as a text of comma-separated numbers. """ impl = sa.types.Unicode cache_ok = True def process_bind_param(self, value: Optional[Any], dialect: 'sa.Dialect') -> Optional[str]: if value is None: return None assert isinstance(value, list) return ','.join(map(str, value)) def process_result_value(self, value: Optional[Any], dialect: SaDialect) -> Optional[List[int]]: return [int(v) for v in value.split(',')] if value is not None else None class IntArray(sa.types.TypeDecorator[Any]): """ Dialect-independent list of integers. """ impl = IntList cache_ok = True def load_dialect_impl(self, dialect: SaDialect) -> sa.types.TypeEngine[Any]: if dialect.name == 'postgresql': return ARRAY(sa.Integer()) return IntList() class comparator_factory(sa.types.UserDefinedType.Comparator): # type: ignore[type-arg] def __add__(self, other: SaColumn) -> 'sa.ColumnOperators': """ Concate the array with the given array. If one of the operants is null, the value of the other will be returned. """ return ArrayCat(self.expr, other) def contains(self, other: SaColumn, **kwargs: Any) -> 'sa.ColumnOperators': """ Return true if the array contains all the value of the argument array. """ return ArrayContains(self.expr, other) class ArrayAgg(sa.sql.functions.GenericFunction[Any]): """ Aggregate function to collect elements in an array. """ type = IntArray() identifier = 'ArrayAgg' name = 'array_agg' inherit_cache = True @compiles(ArrayAgg, 'sqlite') def sqlite_array_agg(element: ArrayAgg, compiler: 'sa.Compiled', **kw: Any) -> str: return "group_concat(%s, ',')" % compiler.process(element.clauses, **kw) class ArrayContains(sa.sql.expression.FunctionElement[Any]): """ Function to check if an array is fully contained in another. """ name = 'ArrayContains' inherit_cache = True @compiles(ArrayContains) def generic_array_contains(element: ArrayContains, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "(%s @> %s)" % (compiler.process(arg1, **kw), compiler.process(arg2, **kw)) @compiles(ArrayContains, 'sqlite') def sqlite_array_contains(element: ArrayContains, compiler: 'sa.Compiled', **kw: Any) -> str: return "array_contains(%s)" % compiler.process(element.clauses, **kw) class ArrayCat(sa.sql.expression.FunctionElement[Any]): """ Function to check if an array is fully contained in another. """ type = IntArray() identifier = 'ArrayCat' inherit_cache = True @compiles(ArrayCat) def generic_array_cat(element: ArrayCat, compiler: 'sa.Compiled', **kw: Any) -> str: return "array_cat(%s)" % compiler.process(element.clauses, **kw) @compiles(ArrayCat, 'sqlite') def sqlite_array_cat(element: ArrayCat, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "(%s || ',' || %s)" % (compiler.process(arg1, **kw), compiler.process(arg2, **kw)) ================================================ FILE: src/nominatim_api/sql/sqlalchemy_types/json.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Common json type for different dialects. """ from typing import Any import sqlalchemy as sa from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.sqlite import JSON as sqlite_json from ...typing import SaDialect class Json(sa.types.TypeDecorator[Any]): """ Dialect-independent type for JSON. """ impl = sa.types.JSON cache_ok = True def load_dialect_impl(self, dialect: SaDialect) -> sa.types.TypeEngine[Any]: if dialect.name == 'postgresql': return JSONB(none_as_null=True) return sqlite_json(none_as_null=True) ================================================ FILE: src/nominatim_api/sql/sqlalchemy_types/key_value.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ A custom type that implements a simple key-value store of strings. """ from typing import Any import sqlalchemy as sa from sqlalchemy.ext.compiler import compiles from sqlalchemy.dialects.postgresql import HSTORE from sqlalchemy.dialects.sqlite import JSON as sqlite_json from ...typing import SaDialect, SaColumn class KeyValueStore(sa.types.TypeDecorator[Any]): """ Dialect-independent type of a simple key-value store of strings. """ impl = HSTORE cache_ok = True def load_dialect_impl(self, dialect: SaDialect) -> sa.types.TypeEngine[Any]: if dialect.name == 'postgresql': return HSTORE() # type: ignore[no-untyped-call] return sqlite_json(none_as_null=True) class comparator_factory(sa.types.UserDefinedType.Comparator): # type: ignore[type-arg] def merge(self, other: SaColumn) -> 'sa.Operators': """ Merge the values from the given KeyValueStore into this one, overwriting values where necessary. When the argument is null, nothing happens. """ return KeyValueConcat(self.expr, other) class KeyValueConcat(sa.sql.expression.FunctionElement[Any]): """ Return the merged key-value store from the input parameters. """ type = KeyValueStore() name = 'JsonConcat' inherit_cache = True @compiles(KeyValueConcat) def default_json_concat(element: KeyValueConcat, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "(%s || coalesce(%s, ''::hstore))"\ % (compiler.process(arg1, **kw), compiler.process(arg2, **kw)) @compiles(KeyValueConcat, 'sqlite') def sqlite_json_concat(element: KeyValueConcat, compiler: 'sa.Compiled', **kw: Any) -> str: arg1, arg2 = list(element.clauses) return "json_patch(%s, coalesce(%s, '{}'))"\ % (compiler.process(arg1, **kw), compiler.process(arg2, **kw)) ================================================ FILE: src/nominatim_api/sql/sqlite_functions.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Custom functions for SQLite. """ from typing import cast, Optional, Set, Any import json def weigh_search(search_vector: Optional[str], rankings: str, default: float) -> float: """ Custom weight function for search results. """ if search_vector is not None: svec = [int(x) for x in search_vector.split(',')] for rank in json.loads(rankings): if all(r in svec for r in rank[1]): return cast(float, rank[0]) return default class ArrayIntersectFuzzy: """ Compute the array of common elements of all input integer arrays. Very large input parameters may be ignored to speed up computation. Therefore, the result is a superset of common elements. Input and output arrays are given as comma-separated lists. """ def __init__(self) -> None: self.first = '' self.values: Optional[Set[int]] = None def step(self, value: Optional[str]) -> None: """ Add the next array to the intersection. """ if value is not None: if not self.first: self.first = value elif len(value) < 10000000: if self.values is None: self.values = {int(x) for x in self.first.split(',')} self.values.intersection_update((int(x) for x in value.split(','))) def finalize(self) -> str: """ Return the final result. """ if self.values is not None: return ','.join(map(str, self.values)) return self.first class ArrayUnion: """ Compute the set of all elements of the input integer arrays. Input and output arrays are given as strings of comma-separated lists. """ def __init__(self) -> None: self.values: Optional[Set[str]] = None def step(self, value: Optional[str]) -> None: """ Add the next array to the union. """ if value is not None: if self.values is None: self.values = set(value.split(',')) else: self.values.update(value.split(',')) def finalize(self) -> str: """ Return the final result. """ return '' if self.values is None else ','.join(self.values) def array_contains(container: Optional[str], containee: Optional[str]) -> Optional[bool]: """ Is the array 'containee' completely contained in array 'container'. """ if container is None or containee is None: return None vset = container.split(',') return all(v in vset for v in containee.split(',')) def array_pair_contains(container1: Optional[str], container2: Optional[str], containee: Optional[str]) -> Optional[bool]: """ Is the array 'containee' completely contained in the union of array 'container1' and array 'container2'. """ if container1 is None or container2 is None or containee is None: return None vset = container1.split(',') + container2.split(',') return all(v in vset for v in containee.split(',')) def install_custom_functions(conn: Any) -> None: """ Install helper functions for Nominatim into the given SQLite database connection. """ conn.create_function('weigh_search', 3, weigh_search, deterministic=True) conn.create_function('array_contains', 2, array_contains, deterministic=True) conn.create_function('array_pair_contains', 3, array_pair_contains, deterministic=True) _create_aggregate(conn, 'array_intersect_fuzzy', 1, ArrayIntersectFuzzy) _create_aggregate(conn, 'array_union', 1, ArrayUnion) async def _make_aggregate(aioconn: Any, *args: Any) -> None: await aioconn._execute(aioconn._conn.create_aggregate, *args) def _create_aggregate(conn: Any, name: str, nargs: int, aggregate: Any) -> None: try: conn.await_(_make_aggregate(conn._connection, name, nargs, aggregate)) except Exception as error: conn._handle_exception(error) ================================================ FILE: src/nominatim_api/status.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Classes and function related to status call. """ from typing import Optional import datetime as dt import dataclasses import sqlalchemy as sa from .connection import SearchConnection from .version import NOMINATIM_API_VERSION @dataclasses.dataclass class StatusResult: """ Result of a call to the status API. """ status: int message: str software_version = NOMINATIM_API_VERSION data_updated: Optional[dt.datetime] = None database_version: Optional[str] = None async def get_status(conn: SearchConnection) -> StatusResult: """ Execute a status API call. """ status = StatusResult(0, 'OK') # Last update date sql = sa.select(conn.t.import_status.c.lastimportdate).limit(1) status.data_updated = await conn.scalar(sql) if status.data_updated is not None: if status.data_updated.tzinfo is None: status.data_updated = status.data_updated.replace(tzinfo=dt.timezone.utc) else: status.data_updated = status.data_updated.astimezone(dt.timezone.utc) # Database version try: status.database_version = await conn.get_property('database_version') except ValueError: pass return status ================================================ FILE: src/nominatim_api/timeout.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Helpers for handling of timeouts for request. """ from typing import Union, Optional import asyncio class Timeout: """ A class that provides helper functions to ensure a given timeout is respected. Can only be used from coroutines. """ def __init__(self, timeout: Optional[Union[int, float]]) -> None: self.abs = None if timeout is None else asyncio.get_running_loop().time() + timeout def is_elapsed(self) -> bool: """ Check if the timeout has already passed. """ return (self.abs is not None) and (asyncio.get_running_loop().time() >= self.abs) ================================================ FILE: src/nominatim_api/types.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Complex datatypes used by the Nominatim API. """ from typing import Optional, Union, Tuple, NamedTuple, TypeVar, Type, Dict, \ Any, List, Sequence, TYPE_CHECKING from collections import abc import dataclasses import datetime as dt import enum import math from struct import unpack from binascii import unhexlify if TYPE_CHECKING: from .localization import Locales from .errors import UsageError @dataclasses.dataclass class PlaceID: """ Reference a place by Nominatim's internal ID. A PlaceID may reference place from the main table placex, from the interpolation tables or the postcode tables. Place IDs are not stable between installations. You may use this type theefore only with place IDs obtained from the same database. """ place_id: int """ The internal ID of the place to reference. """ def __str__(self) -> str: return str(self.place_id) @dataclasses.dataclass class OsmID: """ Reference a place by its OSM ID and potentially the basic category. The OSM ID may refer to places in the main table placex and OSM interpolation lines. """ osm_type: str """ OSM type of the object. Must be one of `N`(node), `W`(way) or `R`(relation). """ osm_id: int """ The OSM ID of the object. """ osm_class: Optional[str] = None """ The same OSM object may appear multiple times in the database under different categories. The optional class parameter allows to distinguish the different categories and corresponds to the key part of the category. If there are multiple objects in the database and `osm_class` is left out, then one of the objects is returned at random. """ def __str__(self) -> str: return f"{self.osm_type}{self.osm_id}" def __post_init__(self) -> None: if self.osm_type not in ('N', 'W', 'R'): raise ValueError(f"Illegal OSM type '{self.osm_type}'. Must be one of N, W, R.") def class_as_housenumber(self) -> Optional[int]: """ Interpret the class property as a housenumber and return it. If the OSM ID points to an interpolation, then the class may be a number pointing to the exact number requested. This function returns the housenumber as an int, if class is set and is a number. """ if self.osm_class and self.osm_class.isdigit(): return int(self.osm_class) return None PlaceRef = Union[PlaceID, OsmID] class Point(NamedTuple): """ A geographic point in WGS84 projection. """ x: float y: float @property def lat(self) -> float: """ Return the latitude of the point. """ return self.y @property def lon(self) -> float: """ Return the longitude of the point. """ return self.x def to_geojson(self) -> str: """ Return the point in GeoJSON format. """ return f'{{"type": "Point","coordinates": [{self.x}, {self.y}]}}' @staticmethod def from_wkb(wkb: Union[str, bytes]) -> 'Point': """ Create a point from EWKB as returned from the database. """ if isinstance(wkb, str): wkb = unhexlify(wkb) if len(wkb) != 25: raise ValueError(f"Point wkb has unexpected length {len(wkb)}") if wkb[0] == 0: gtype, srid, x, y = unpack('>iidd', wkb[1:]) elif wkb[0] == 1: gtype, srid, x, y = unpack(' 'Point': """ Create a point from an input parameter. The parameter may be given as a point, a string or a sequence of strings or floats. Raises a UsageError if the format is not correct. """ if isinstance(inp, Point): return inp seq: Sequence[str] if isinstance(inp, str): seq = inp.split(',') elif isinstance(inp, abc.Sequence): seq = inp if len(seq) != 2: raise UsageError('Point parameter needs 2 coordinates.') try: x, y = filter(math.isfinite, map(float, seq)) except ValueError as exc: raise UsageError('Point parameter needs to be numbers.') from exc if not -180 <= x <= 180 or not -90 <= y <= 90.0: raise UsageError('Point coordinates invalid.') return Point(x, y) def to_wkt(self) -> str: """ Return the WKT representation of the point. """ return f'POINT({self.x} {self.y})' AnyPoint = Union[Point, Tuple[float, float]] WKB_BBOX_HEADER_LE = b'\x01\x03\x00\x00\x20\xE6\x10\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00' WKB_BBOX_HEADER_BE = b'\x00\x20\x00\x00\x03\x00\x00\x10\xe6\x00\x00\x00\x01\x00\x00\x00\x05' class Bbox: """ A bounding box in WGS84 projection. The coordinates are available as an array in the 'coord' property in the order (minx, miny, maxx, maxy). """ def __init__(self, minx: float, miny: float, maxx: float, maxy: float) -> None: """ Create a new bounding box with the given coordinates in WGS84 projection. """ self.coords = (minx, miny, maxx, maxy) @property def minlat(self) -> float: """ Southern-most latitude, corresponding to the minimum y coordinate. """ return self.coords[1] @property def maxlat(self) -> float: """ Northern-most latitude, corresponding to the maximum y coordinate. """ return self.coords[3] @property def minlon(self) -> float: """ Western-most longitude, corresponding to the minimum x coordinate. """ return self.coords[0] @property def maxlon(self) -> float: """ Eastern-most longitude, corresponding to the maximum x coordinate. """ return self.coords[2] @property def area(self) -> float: """ Return the area of the box in WGS84. """ return (self.coords[2] - self.coords[0]) * (self.coords[3] - self.coords[1]) def contains(self, pt: Point) -> bool: """ Check if the point is inside or on the boundary of the box. """ return self.coords[0] <= pt[0] and self.coords[1] <= pt[1]\ and self.coords[2] >= pt[0] and self.coords[3] >= pt[1] def to_wkt(self) -> str: """ Return the WKT representation of the Bbox. This is a simple polygon with four points. """ return 'POLYGON(({0} {1},{0} {3},{2} {3},{2} {1},{0} {1}))'\ .format(*self.coords) @staticmethod def from_wkb(wkb: Union[None, str, bytes]) -> 'Optional[Bbox]': """ Create a Bbox from a bounding box polygon as returned by the database. Returns `None` if the input value is None. """ if wkb is None: return None if isinstance(wkb, str): wkb = unhexlify(wkb) if len(wkb) != 97: raise ValueError("WKB must be a bounding box polygon") if wkb.startswith(WKB_BBOX_HEADER_LE): x1, y1, _, _, x2, y2 = unpack('dddddd', wkb[17:65]) else: raise ValueError("WKB has wrong header") return Bbox(min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)) @staticmethod def from_point(pt: Point, buffer: float) -> 'Bbox': """ Return a Bbox around the point with the buffer added to all sides. """ return Bbox(pt[0] - buffer, pt[1] - buffer, pt[0] + buffer, pt[1] + buffer) @staticmethod def from_param(inp: Any) -> 'Bbox': """ Return a Bbox from an input parameter. The box may be given as a Bbox, a string or a list or strings or integer. Raises a UsageError if the format is incorrect. """ if isinstance(inp, Bbox): return inp seq: Sequence[str] if isinstance(inp, str): seq = inp.split(',') elif isinstance(inp, abc.Sequence): seq = inp if len(seq) != 4: raise UsageError('Bounding box parameter needs 4 coordinates.') try: x1, y1, x2, y2 = filter(math.isfinite, map(float, seq)) except ValueError as exc: raise UsageError('Bounding box parameter needs to be numbers.') from exc x1 = min(180, max(-180, x1)) x2 = min(180, max(-180, x2)) y1 = min(90, max(-90, y1)) y2 = min(90, max(-90, y2)) if x1 == x2 or y1 == y2: raise UsageError('Bounding box with invalid parameters.') return Bbox(min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)) class GeometryFormat(enum.Flag): """ All search functions support returning the full geometry of a place in various formats. The internal geometry is converted by PostGIS to the desired format and then returned as a string. It is possible to request multiple formats at the same time. """ NONE = 0 """ No geometry requested. Alias for a empty flag. """ GEOJSON = enum.auto() """ [GeoJSON](https://geojson.org/) format """ KML = enum.auto() """ [KML](https://en.wikipedia.org/wiki/Keyhole_Markup_Language) format """ SVG = enum.auto() """ [SVG](http://www.w3.org/TR/SVG/paths.html) format """ TEXT = enum.auto() """ [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) format """ class DataLayer(enum.Flag): """ The `DataLayer` flag type defines the layers that can be selected for reverse and forward search. """ ADDRESS = enum.auto() """ The address layer contains all places relevant for addresses: fully qualified addresses with a house number (or a house name equivalent, for some addresses) and places that can be part of an address like roads, cities, states. """ POI = enum.auto() """ Layer for points of interest like shops, restaurants but also recycling bins or postboxes. """ RAILWAY = enum.auto() """ Layer with railway features including tracks and other infrastructure. Note that in Nominatim's standard configuration, only very few railway features are imported into the database. Thus a custom configuration is required to make full use of this layer. """ NATURAL = enum.auto() """ Layer with natural features like rivers, lakes and mountains. """ MANMADE = enum.auto() """ Layer with other human-made features and boundaries. This layer is the catch-all and includes all features not covered by the other layers. A typical example for this layer are national park boundaries. """ class QueryStatistics(dict[str, Any]): """ A specialised dictionary for collecting query statistics. """ def __enter__(self) -> 'QueryStatistics': self.log_time('start') return self def __exit__(self, *_: Any) -> None: self.log_time('end') self['total_time'] = (self['end'] - self['start']).total_seconds() if 'start_query' in self: self['wait_time'] = (self['start_query'] - self['start']).total_seconds() else: self['wait_time'] = self['total_time'] self['start_query'] = self['end'] self['query_time'] = self['total_time'] - self['wait_time'] def __missing__(self, key: str) -> str: return '' def log_time(self, key: str) -> None: self[key] = dt.datetime.now(tz=dt.timezone.utc) class NoQueryStats: """ Null object to use, when no query statistics are requested. """ def __enter__(self) -> 'NoQueryStats': return self def __exit__(self, *_: Any) -> None: pass def __setitem__(self, key: str, value: Any) -> None: pass def __getitem__(self, key: str) -> Any: return None def __contains__(self, key: str, default: Any = None) -> bool: return False def log_time(self, key: str) -> None: pass def format_country(cc: Any) -> List[str]: """ Extract a list of country codes from the input which may be either a string or list of strings. Filters out all values that are not a two-letter string. """ clist: Sequence[str] if isinstance(cc, str): clist = cc.split(',') elif isinstance(cc, abc.Sequence): clist = cc else: raise UsageError("Parameter 'country' needs to be a comma-separated list " "or a Python list of strings.") return [cc.lower() for cc in clist if isinstance(cc, str) and len(cc) == 2] def format_excluded(ids: Any) -> List[PlaceRef]: """ Extract a list of place IDs and OSM IDs from the input. """ if not ids: return [] plist: Sequence[str] if isinstance(ids, str): plist = [s.strip() for s in ids.split(',')] elif isinstance(ids, abc.Sequence): plist = ids else: raise UsageError("Parameter 'excluded' needs to be a comma-separated list " "or a Python list of place IDs or OSM IDs.") result: List[PlaceRef] = [] for i in plist: if not i: continue if isinstance(i, (PlaceID, OsmID)): result.append(i) elif isinstance(i, int): if i > 0: result.append(PlaceID(i)) elif isinstance(i, str): if i.isdigit(): if int(i) > 0: result.append(PlaceID(int(i))) elif len(i) > 1 and i[0].upper() in ('N', 'W', 'R') and i[1:].isdigit(): if int(i[1:]) > 0: result.append(OsmID(i[0].upper(), int(i[1:]))) else: raise UsageError(f"Invalid exclude ID: {i}") else: raise UsageError("Parameter 'excluded' contains invalid types.") return result def format_categories(categories: List[Tuple[str, str]]) -> List[Tuple[str, str]]: """ Extract a list of categories. Currently a noop. """ return categories TParam = TypeVar('TParam', bound='LookupDetails') @dataclasses.dataclass class LookupDetails: """ Collection of parameters that define which kind of details are returned with a lookup or details result. """ geometry_output: GeometryFormat = GeometryFormat.NONE """ Add the full geometry of the place to the result. Multiple formats may be selected. Note that geometries can become quite large. """ address_details: bool = False """ Get detailed information on the places that make up the address for the result. """ linked_places: bool = False """ Get detailed information on the places that link to the result. """ parented_places: bool = False """ Get detailed information on all places that this place is a parent for, i.e. all places for which it provides the address details. Only POI places can have parents. """ entrances: bool = False """ Get detailed information about the tagged entrances for the result. """ keywords: bool = False """ Add information about the search terms used for this place. """ geometry_simplification: float = 0.0 """ Simplification factor for a geometry in degrees WGS. A factor of 0.0 means the original geometry is kept. The higher the value, the more the geometry gets simplified. """ query_stats: Union[QueryStatistics, NoQueryStats] = \ dataclasses.field(default_factory=NoQueryStats) """ Optional QueryStatistics object collecting information about runtime behaviour of the call. """ @classmethod def from_kwargs(cls: Type[TParam], kwargs: Dict[str, Any]) -> TParam: """ Load the data fields of the class from a dictionary. Unknown entries in the dictionary are ignored, missing ones get the default setting. The function supports type checking and throws a UsageError when the value does not fit. """ def _check_field(v: Any, field: 'dataclasses.Field[Any]') -> Any: if v is None: return field.default_factory() \ if field.default_factory != dataclasses.MISSING \ else field.default if field.metadata and 'transform' in field.metadata: return field.metadata['transform'](v) if not isinstance(v, field.type): # type: ignore[arg-type] raise UsageError(f"Parameter '{field.name}' needs to be of {field.type!s}.") return v return cls(**{f.name: _check_field(kwargs[f.name], f) for f in dataclasses.fields(cls) if f.name in kwargs}) @dataclasses.dataclass class ReverseDetails(LookupDetails): """ Collection of parameters for the reverse call. """ max_rank: int = dataclasses.field(default=30, metadata={'transform': lambda v: max(0, min(v, 30))}) """ Highest address rank to return. """ layers: DataLayer = DataLayer.ADDRESS | DataLayer.POI """ Filter which kind of data to include. """ @dataclasses.dataclass class SearchDetails(LookupDetails): """ Collection of parameters for the search call. """ max_results: int = 10 """ Maximum number of results to be returned. The actual number of results may be less. """ min_rank: int = dataclasses.field(default=0, metadata={'transform': lambda v: max(0, min(v, 30))}) """ Lowest address rank to return. """ max_rank: int = dataclasses.field(default=30, metadata={'transform': lambda v: max(0, min(v, 30))}) """ Highest address rank to return. """ layers: Optional[DataLayer] = dataclasses.field(default=None, metadata={'transform': lambda r: r}) """ Filter which kind of data to include. When 'None' (the default) then filtering by layers is disabled. """ countries: List[str] = dataclasses.field(default_factory=list, metadata={'transform': format_country}) """ Restrict search results to the given countries. An empty list (the default) will disable this filter. """ excluded: List[PlaceRef] = dataclasses.field(default_factory=list, metadata={'transform': format_excluded}) """ List of OSM objects to exclude from the results, provided as either internal Place IDs or OSM IDs. """ viewbox: Optional[Bbox] = dataclasses.field(default=None, metadata={'transform': Bbox.from_param}) """ Focus the search on a given map area. """ bounded_viewbox: bool = False """ Use 'viewbox' as a filter and restrict results to places within the given area. """ near: Optional[Point] = dataclasses.field(default=None, metadata={'transform': Point.from_param}) """ Order results by distance to the given point. """ near_radius: Optional[float] = dataclasses.field(default=None, metadata={'transform': lambda r: r}) """ Use near point as a filter and drop results outside the given radius. Radius is given in degrees WSG84. """ categories: List[Tuple[str, str]] = dataclasses.field(default_factory=list, metadata={'transform': format_categories}) """ Restrict search to places with one of the given class/type categories. An empty list (the default) will disable this filter. """ viewbox_x2: Optional[Bbox] = None locales: Optional['Locales'] = dataclasses.field( default=None, metadata={'transform': lambda v: v}) """ Locale preferences of the caller. Used during result re-ranking to prefer results that match the caller's locale over results that only match in an alternate language. """ def __post_init__(self) -> None: if self.viewbox is not None: xext = (self.viewbox.maxlon - self.viewbox.minlon)/2 yext = (self.viewbox.maxlat - self.viewbox.minlat)/2 self.viewbox_x2 = Bbox(self.viewbox.minlon - xext, self.viewbox.minlat - yext, self.viewbox.maxlon + xext, self.viewbox.maxlat + yext) @property def excluded_place_ids(self) -> List[int]: """ Return excluded entries as a plain list of place ID integers. Only includes PlaceID entries. Returns [0] if empty to ensure SQL NOT IN clauses work correctly. """ return [e.place_id for e in self.excluded if isinstance(e, PlaceID)] or [0] def restrict_min_max_rank(self, new_min: int, new_max: int) -> None: """ Change the min_rank and max_rank fields to respect the given boundaries. """ assert new_min <= new_max self.min_rank = max(self.min_rank, new_min) self.max_rank = min(self.max_rank, new_max) def is_impossible(self) -> bool: """ Check if the parameter configuration is contradictionary and cannot yield any results. """ return (self.min_rank > self.max_rank or (self.bounded_viewbox and self.viewbox is not None and self.near is not None and self.viewbox.contains(self.near)) or (self.layers is not None and not self.layers) or (self.max_rank <= 4 and self.layers is not None and not self.layers & DataLayer.ADDRESS)) def layer_enabled(self, layer: DataLayer) -> bool: """ Check if the given layer has been chosen. Also returns true when layer restriction has been disabled completely. """ return self.layers is None or bool(self.layers & layer) @dataclasses.dataclass class EntranceDetails: """ Reference a place by its OSM ID and potentially the basic category. The OSM ID may refer to places in the main table placex and OSM interpolation lines. """ osm_id: int """ The OSM ID of the object. """ type: str """ The value of the OSM entrance tag (i.e. yes, main, secondary, etc.). """ location: Point """ The location of the entrance node. """ extratags: Dict[str, str] """ The other tags associated with the entrance node. """ ================================================ FILE: src/nominatim_api/typing.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Type definitions for typing annotations. Complex type definitions are moved here, to keep the source files readable. """ from typing import Union, TYPE_CHECKING # flake8: noqa # SQLAlchemy introduced generic types in version 2.0 making typing # incompatible with older versions. Add wrappers here so we don't have # to litter the code with bare-string types. if TYPE_CHECKING: from typing import Any import sqlalchemy as sa import os from typing_extensions import (TypeAlias as TypeAlias, Protocol as Protocol) else: TypeAlias = str Protocol = object StrPath = Union[str, 'os.PathLike[str]'] SaLambdaSelect: TypeAlias = 'Union[sa.Select[Any], sa.StatementLambdaElement]' SaSelect: TypeAlias = 'sa.Select[Any]' SaScalarSelect: TypeAlias = 'sa.ScalarSelect[Any]' SaRow: TypeAlias = 'sa.Row[Any]' SaColumn: TypeAlias = 'sa.ColumnElement[Any]' SaExpression: TypeAlias = 'sa.ColumnElement[bool]' SaLabel: TypeAlias = 'sa.Label[Any]' SaFromClause: TypeAlias = 'sa.FromClause' SaSelectable: TypeAlias = 'sa.Selectable' SaBind: TypeAlias = 'sa.BindParameter[Any]' SaDialect: TypeAlias = 'sa.Dialect' ================================================ FILE: src/nominatim_api/utils/__init__.py ================================================ ================================================ FILE: src/nominatim_api/utils/json_writer.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Streaming JSON encoder. """ from typing import Any, TypeVar, Optional, Callable import io try: import ujson as json except ModuleNotFoundError: import json # type: ignore[no-redef] T = TypeVar('T') class JsonWriter: """ JSON encoder that renders the output directly into an output stream. This is a very simple writer which produces JSON in a compact as possible form. The writer does not check for syntactic correctness. It is the responsibility of the caller to call the write functions in an order that produces correct JSON. All functions return the writer object itself so that function calls can be chained. """ def __init__(self) -> None: self.data = io.StringIO() self.pending = '' def __call__(self) -> str: """ Return the rendered JSON content as a string. The writer remains usable after calling this function. """ if self.pending: assert self.pending in (']', '}') self.data.write(self.pending) self.pending = '' return self.data.getvalue() def start_object(self) -> 'JsonWriter': """ Write the open bracket of a JSON object. """ if self.pending: self.data.write(self.pending) self.pending = '{' return self def end_object(self) -> 'JsonWriter': """ Write the closing bracket of a JSON object. """ assert self.pending in (',', '{', '') if self.pending == '{': self.data.write(self.pending) self.pending = '}' return self def start_array(self) -> 'JsonWriter': """ Write the opening bracket of a JSON array. """ if self.pending: self.data.write(self.pending) self.pending = '[' return self def end_array(self) -> 'JsonWriter': """ Write the closing bracket of a JSON array. """ assert self.pending in (',', '[', ']', ')', '') if self.pending not in (',', ''): self.data.write(self.pending) self.pending = ']' return self def key(self, name: str) -> 'JsonWriter': """ Write the key string of a JSON object. """ assert self.pending self.data.write(self.pending) self.data.write(json.dumps(name, ensure_ascii=False)) self.pending = ':' return self def value(self, value: Any) -> 'JsonWriter': """ Write out a value as JSON. The function uses the json.dumps() function for encoding the JSON. Thus any value that can be encoded by that function is permissible here. """ return self.raw(json.dumps(value, ensure_ascii=False)) def float(self, value: float, precision: int) -> 'JsonWriter': """ Write out a float value with the given precision. """ return self.raw(f"{value:0.{precision}f}") def next(self) -> 'JsonWriter': """ Write out a delimiter comma between JSON object or array elements. """ if self.pending: self.data.write(self.pending) self.pending = ',' return self def raw(self, raw_json: str) -> 'JsonWriter': """ Write out the given value as is. This function is useful if a value is already available in JSON format. """ if self.pending: self.data.write(self.pending) self.pending = '' self.data.write(raw_json) return self def keyval(self, key: str, value: Any) -> 'JsonWriter': """ Write out an object element with the given key and value. This is a shortcut for calling 'key()', 'value()' and 'next()'. """ self.key(key) self.value(value) return self.next() def keyval_not_none(self, key: str, value: Optional[T], transform: Optional[Callable[[T], Any]] = None) -> 'JsonWriter': """ Write out an object element only if the value is not None. If 'transform' is given, it must be a function that takes the value type and returns a JSON encodable type. The transform function will be called before the value is written out. """ if value is not None: self.key(key) self.value(transform(value) if transform else value) self.next() return self ================================================ FILE: src/nominatim_api/v1/__init__.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of API version v1 (aka the legacy version). """ from .server_glue import get_routes as get_routes ================================================ FILE: src/nominatim_api/v1/classtypes.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Hard-coded information about tag categories. These tables have been copied verbatim from the old PHP code. For future version a more flexible formatting is required. """ from typing import Tuple, Optional, Mapping, Union from ..results import ReverseResult, SearchResult from ..types import Bbox def get_label_tag(category: Tuple[str, str], extratags: Optional[Mapping[str, str]], rank: int, country: Optional[str]) -> str: """ Create a label tag for the given place that can be used as an XML name. """ if category in (('place', 'postcode'), ('boundary', 'postal_code')): label = 'postcode' elif rank < 26 and extratags and 'place' in extratags: label = extratags['place'] elif rank < 26 and extratags and 'linked_place' in extratags: label = extratags['linked_place'] elif category == ('boundary', 'administrative'): label = ADMIN_LABELS.get((country or '', rank // 2))\ or ADMIN_LABELS.get(('', rank // 2))\ or 'Administrative' elif rank < 26: label = category[1] if category[1] != 'yes' else category[0] elif rank < 28: label = 'road' elif (category[0] == 'place' and category[1] in ('house_number', 'house_name', 'country_code')): label = category[1] else: label = category[0] return label.lower().replace(' ', '_') def bbox_from_result(result: Union[ReverseResult, SearchResult]) -> Bbox: """ Compute a bounding box for the result. For ways and relations a given boundingbox is used. For all other object, a box is computed around the centroid according to dimensions derived from the search rank. """ if result.category == ('place', 'postcode') and result.bbox is None: return Bbox.from_point(result.centroid, 0.05 - 0.012 * (result.rank_search - 21)) if (result.osm_object and result.osm_object[0] == 'N') or result.bbox is None: extent = NODE_EXTENT.get(result.category, 0.00005) return Bbox.from_point(result.centroid, extent) return result.bbox OSM_ATTRIBUTION = 'Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright' OSM_TYPE_NAME = { 'N': 'node', 'W': 'way', 'R': 'relation' } ADMIN_LABELS = { ('', 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', 3): 'State', ('no', 4): 'County', ('se', 3): 'State', ('se', 4): 'County' } ICONS = { ('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' } NODE_EXTENT = { ('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 } ================================================ FILE: src/nominatim_api/v1/format.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Output formatters for API version v1. """ from typing import List, Dict, Mapping, Any import collections import datetime as dt from ..utils.json_writer import JsonWriter from ..status import StatusResult from ..results import DetailedResult, ReverseResults, SearchResults, \ AddressLines, AddressLine from ..localization import Locales from ..result_formatting import FormatDispatcher from .classtypes import ICONS from . import format_json, format_xml from .helpers import _add_admin_level from .. import logging as loglib from ..server import content_types as ct class RawDataList(List[Dict[str, Any]]): """ Data type for formatting raw data lists 'as is' in json. """ dispatch = FormatDispatcher({'text': ct.CONTENT_TEXT, 'xml': ct.CONTENT_XML, 'debug': ct.CONTENT_HTML}) @dispatch.error_format_func def _format_error(content_type: str, msg: str, status: int) -> str: if content_type == ct.CONTENT_XML: return f""" {status} {msg} """ if content_type == ct.CONTENT_JSON: return f"""{{"error":{{"code":{status},"message":"{msg}"}}}}""" if content_type == ct.CONTENT_HTML: loglib.log().section('Execution error') loglib.log().var_dump('Status', status) loglib.log().var_dump('Message', msg) return loglib.get_and_disable() return f"ERROR {status}: {msg}" @dispatch.format_func(StatusResult, 'text') def _format_status_text(result: StatusResult, _: Mapping[str, Any]) -> str: if result.status: return f"ERROR: {result.message}" return 'OK' @dispatch.format_func(StatusResult, 'json') def _format_status_json(result: StatusResult, _: Mapping[str, Any]) -> str: out = JsonWriter() out.start_object()\ .keyval('status', result.status)\ .keyval('message', result.message)\ .keyval_not_none('data_updated', result.data_updated, lambda v: v.isoformat())\ .keyval('software_version', str(result.software_version))\ .keyval_not_none('database_version', result.database_version, str)\ .end_object() return out() def _add_address_row(writer: JsonWriter, row: AddressLine, locales: Locales) -> None: writer.start_object()\ .keyval('localname', locales.display_name(row.names))\ .keyval_not_none('place_id', row.place_id) if row.osm_object is not None: writer.keyval('osm_id', row.osm_object[1])\ .keyval('osm_type', row.osm_object[0]) if row.extratags: writer.keyval_not_none('place_type', row.extratags.get('place_type')) writer.keyval('class', row.category[0])\ .keyval('type', row.category[1])\ .keyval_not_none('admin_level', row.admin_level)\ .keyval('rank_address', row.rank_address)\ .keyval('distance', row.distance)\ .keyval('isaddress', row.isaddress)\ .end_object() def _add_address_rows(writer: JsonWriter, section: str, rows: AddressLines, locales: Locales) -> None: writer.key(section).start_array() for row in rows: _add_address_row(writer, row, locales) writer.next() writer.end_array().next() def _add_parent_rows_grouped(writer: JsonWriter, rows: AddressLines, locales: Locales) -> None: # group by category type data = collections.defaultdict(list) for row in rows: sub = JsonWriter() _add_address_row(sub, row, locales) data[row.category[1]].append(sub()) writer.key('hierarchy').start_object() for group, grouped in data.items(): writer.key(group).start_array() grouped.sort() # sorts alphabetically by local name for line in grouped: writer.raw(line).next() writer.end_array().next() writer.end_object().next() @dispatch.format_func(DetailedResult, 'json') def _format_details_json(result: DetailedResult, options: Mapping[str, Any]) -> str: locales = options.get('locales') or Locales() geom = result.geometry.get('geojson') centroid = result.centroid.to_geojson() out = JsonWriter() out.start_object()\ .keyval_not_none('place_id', result.place_id)\ .keyval_not_none('parent_place_id', result.parent_place_id) if result.osm_object is not None: out.keyval('osm_type', result.osm_object[0])\ .keyval('osm_id', result.osm_object[1]) out.keyval('category', result.category[0])\ .keyval('type', result.category[1])\ .keyval('admin_level', result.admin_level)\ .keyval('localname', result.locale_name or '')\ .keyval('names', result.names or {})\ .keyval('addresstags', result.address or {})\ .keyval_not_none('housenumber', result.housenumber)\ .keyval_not_none('calculated_postcode', result.postcode)\ .keyval_not_none('country_code', result.country_code)\ .keyval_not_none('indexed_date', result.indexed_date, lambda v: v.isoformat())\ .keyval_not_none('importance', result.importance)\ .keyval('calculated_importance', result.calculated_importance())\ .keyval('extratags', _add_admin_level(result) or {})\ .keyval_not_none('calculated_wikipedia', result.wikipedia)\ .keyval('rank_address', result.rank_address)\ .keyval('rank_search', result.rank_search)\ .keyval('isarea', 'Polygon' in (geom or result.geometry.get('type') or ''))\ .key('centroid').raw(centroid).next()\ .key('geometry').raw(geom or centroid).next() if options.get('icon_base_url', None): icon = ICONS.get(result.category) if icon: out.keyval('icon', f"{options['icon_base_url']}/{icon}.p.20.png") if result.address_rows is not None: _add_address_rows(out, 'address', result.address_rows, locales) if result.linked_rows: _add_address_rows(out, 'linked_places', result.linked_rows, locales) if result.name_keywords is not None or result.address_keywords is not None: out.key('keywords').start_object() for sec, klist in (('name', result.name_keywords), ('address', result.address_keywords)): out.key(sec).start_array() for word in (klist or []): out.start_object()\ .keyval('id', word.word_id)\ .keyval('token', word.word_token)\ .end_object().next() out.end_array().next() out.end_object().next() if result.parented_rows is not None: if options.get('group_hierarchy', False): _add_parent_rows_grouped(out, result.parented_rows, locales) else: _add_address_rows(out, 'hierarchy', result.parented_rows, locales) if options.get('entrances', False): format_json.write_entrances(out, result.entrances) out.end_object() return out() @dispatch.format_func(ReverseResults, 'xml') def _format_reverse_xml(results: ReverseResults, options: Mapping[str, Any]) -> str: return format_xml.format_base_xml(results, options, True, 'reversegeocode', {'querystring': options.get('query', '')}) @dispatch.format_func(ReverseResults, 'geojson') def _format_reverse_geojson(results: ReverseResults, options: Mapping[str, Any]) -> str: return format_json.format_base_geojson(results, options, True) @dispatch.format_func(ReverseResults, 'geocodejson') def _format_reverse_geocodejson(results: ReverseResults, options: Mapping[str, Any]) -> str: return format_json.format_base_geocodejson(results, options, True) @dispatch.format_func(ReverseResults, 'json') def _format_reverse_json(results: ReverseResults, options: Mapping[str, Any]) -> str: return format_json.format_base_json(results, options, True, class_label='class') @dispatch.format_func(ReverseResults, 'jsonv2') def _format_reverse_jsonv2(results: ReverseResults, options: Mapping[str, Any]) -> str: return format_json.format_base_json(results, options, True, class_label='category') @dispatch.format_func(SearchResults, 'xml') def _format_search_xml(results: SearchResults, options: Mapping[str, Any]) -> str: extra = {'querystring': options.get('query', '')} for attr in ('more_url', 'exclude_place_ids', 'viewbox'): if options.get(attr): extra[attr] = options[attr] return format_xml.format_base_xml(results, options, False, 'searchresults', extra) @dispatch.format_func(SearchResults, 'geojson') def _format_search_geojson(results: SearchResults, options: Mapping[str, Any]) -> str: return format_json.format_base_geojson(results, options, False) @dispatch.format_func(SearchResults, 'geocodejson') def _format_search_geocodejson(results: SearchResults, options: Mapping[str, Any]) -> str: return format_json.format_base_geocodejson(results, options, False) @dispatch.format_func(SearchResults, 'json') def _format_search_json(results: SearchResults, options: Mapping[str, Any]) -> str: return format_json.format_base_json(results, options, False, class_label='class') @dispatch.format_func(SearchResults, 'jsonv2') def _format_search_jsonv2(results: SearchResults, options: Mapping[str, Any]) -> str: return format_json.format_base_json(results, options, False, class_label='category') @dispatch.format_func(RawDataList, 'json') def _format_raw_data_json(results: RawDataList, _: Mapping[str, Any]) -> str: out = JsonWriter() out.start_array() for res in results: out.start_object() for k, v in res.items(): if isinstance(v, dt.datetime): out.keyval(k, v.isoformat(sep=' ', timespec='seconds')) else: out.keyval(k, v) out.end_object().next() out.end_array() return out() ================================================ FILE: src/nominatim_api/v1/format_json.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Helper functions for output of results in json formats. """ from typing import Mapping, Any, Optional, Tuple, Union, List from ..utils.json_writer import JsonWriter from ..results import AddressLines, ReverseResults, SearchResults from . import classtypes as cl from .helpers import _add_admin_level from ..types import EntranceDetails def _write_osm_id(out: JsonWriter, osm_object: Optional[Tuple[str, int]]) -> None: if osm_object is not None: out.keyval_not_none('osm_type', cl.OSM_TYPE_NAME.get(osm_object[0], None))\ .keyval('osm_id', osm_object[1]) def _write_typed_address(out: JsonWriter, address: Optional[AddressLines], country_code: Optional[str]) -> None: parts = {} for line in (address or []): if line.isaddress: if line.local_name: label = cl.get_label_tag(line.category, line.extratags, line.rank_address, country_code) if label not in parts: parts[label] = line.local_name if line.names and 'ISO3166-2' in line.names and line.admin_level: parts[f"ISO3166-2-lvl{line.admin_level}"] = line.names['ISO3166-2'] for k, v in parts.items(): out.keyval(k, v) if country_code: out.keyval('country_code', country_code) def _write_geocodejson_address(out: JsonWriter, address: Optional[AddressLines], obj_place_id: Optional[int], country_code: Optional[str]) -> None: extra = {} for line in (address or []): if line.isaddress and line.local_name: if line.category[1] in ('postcode', 'postal_code'): out.keyval('postcode', line.local_name) elif line.category[1] == 'house_number': out.keyval('housenumber', line.local_name) elif ((obj_place_id is None or obj_place_id != line.place_id) and line.rank_address >= 4 and line.rank_address < 28): rank_name = GEOCODEJSON_RANKS[line.rank_address] if rank_name not in extra: extra[rank_name] = line.local_name for k, v in extra.items(): out.keyval(k, v) if country_code: out.keyval('country_code', country_code) def write_entrances(out: JsonWriter, entrances: Optional[List[EntranceDetails]]) -> None: if entrances is None: out.keyval('entrances', None) return out.key('entrances')\ .start_array() for entrance in entrances: out.start_object()\ .keyval('osm_id', entrance.osm_id)\ .keyval('type', entrance.type)\ .keyval('lat', f"{entrance.location.lat:0.7f}")\ .keyval('lon', f"{entrance.location.lon:0.7f}") if entrance.extratags: out.keyval('extratags', entrance.extratags) out.end_object().next() out.end_array().next() def format_base_json(results: Union[ReverseResults, SearchResults], options: Mapping[str, Any], simple: bool, class_label: str) -> str: """ Return the result list as a simple json string in custom Nominatim format. """ out = JsonWriter() if simple: if not results: return '{"error":"Unable to geocode"}' else: out.start_array() for result in results: out.start_object()\ .keyval_not_none('place_id', result.place_id)\ .keyval('licence', cl.OSM_ATTRIBUTION)\ _write_osm_id(out, result.osm_object) # lat and lon must be string values out.keyval('lat', f"{result.centroid.lat:0.7f}")\ .keyval('lon', f"{result.centroid.lon:0.7f}")\ .keyval(class_label, result.category[0])\ .keyval('type', result.category[1])\ .keyval('place_rank', result.rank_search)\ .keyval('importance', result.calculated_importance())\ .keyval('addresstype', cl.get_label_tag(result.category, result.extratags, result.rank_address, result.country_code))\ .keyval('name', result.locale_name or '')\ .keyval('display_name', result.display_name or '') if options.get('icon_base_url', None): icon = cl.ICONS.get(result.category) if icon: out.keyval('icon', f"{options['icon_base_url']}/{icon}.p.20.png") if options.get('addressdetails', False): out.key('address').start_object() _write_typed_address(out, result.address_rows, result.country_code) out.end_object().next() if options.get('entrances', False): write_entrances(out, result.entrances) if options.get('extratags', False): out.keyval('extratags', _add_admin_level(result)) if options.get('namedetails', False): out.keyval('namedetails', result.names) # must be string values bbox = cl.bbox_from_result(result) out.key('boundingbox').start_array()\ .value(f"{bbox.minlat:0.7f}").next()\ .value(f"{bbox.maxlat:0.7f}").next()\ .value(f"{bbox.minlon:0.7f}").next()\ .value(f"{bbox.maxlon:0.7f}").next()\ .end_array().next() if result.geometry: for key in ('text', 'kml'): out.keyval_not_none('geo' + key, result.geometry.get(key)) if 'geojson' in result.geometry: out.key('geojson').raw(result.geometry['geojson']).next() out.keyval_not_none('svg', result.geometry.get('svg')) out.end_object() if simple: return out() out.next() out.end_array() return out() def format_base_geojson(results: Union[ReverseResults, SearchResults], options: Mapping[str, Any], simple: bool) -> str: """ Return the result list as a geojson string. """ if not results and simple: return '{"error":"Unable to geocode"}' out = JsonWriter() out.start_object()\ .keyval('type', 'FeatureCollection')\ .keyval('licence', cl.OSM_ATTRIBUTION)\ .key('features').start_array() for result in results: out.start_object()\ .keyval('type', 'Feature')\ .key('properties').start_object() out.keyval_not_none('place_id', result.place_id) _write_osm_id(out, result.osm_object) out.keyval('place_rank', result.rank_search)\ .keyval('category', result.category[0])\ .keyval('type', result.category[1])\ .keyval('importance', result.calculated_importance())\ .keyval('addresstype', cl.get_label_tag(result.category, result.extratags, result.rank_address, result.country_code))\ .keyval('name', result.locale_name or '')\ .keyval('display_name', result.display_name or '') if options.get('addressdetails', False): out.key('address').start_object() _write_typed_address(out, result.address_rows, result.country_code) out.end_object().next() if options.get('entrances', False): write_entrances(out, result.entrances) if options.get('extratags', False): out.keyval('extratags', _add_admin_level(result)) if options.get('namedetails', False): out.keyval('namedetails', result.names) out.end_object().next() # properties out.key('bbox').start_array() for coord in cl.bbox_from_result(result).coords: out.float(coord, 7).next() out.end_array().next() out.key('geometry').raw(result.geometry.get('geojson') or result.centroid.to_geojson()).next() out.end_object().next() out.end_array().next().end_object() return out() def format_base_geocodejson(results: Union[ReverseResults, SearchResults], options: Mapping[str, Any], simple: bool) -> str: """ Return the result list as a geocodejson string. """ if not results and simple: return '{"error":"Unable to geocode"}' out = JsonWriter() out.start_object()\ .keyval('type', 'FeatureCollection')\ .key('geocoding').start_object()\ .keyval('version', '0.1.0')\ .keyval('attribution', cl.OSM_ATTRIBUTION)\ .keyval('licence', 'ODbL')\ .keyval_not_none('query', options.get('query'))\ .end_object().next()\ .key('features').start_array() for result in results: out.start_object()\ .keyval('type', 'Feature')\ .key('properties').start_object()\ .key('geocoding').start_object() out.keyval_not_none('place_id', result.place_id) _write_osm_id(out, result.osm_object) out.keyval('osm_key', result.category[0])\ .keyval('osm_value', result.category[1])\ .keyval('type', GEOCODEJSON_RANKS[max(3, min(28, result.rank_address))])\ .keyval_not_none('accuracy', getattr(result, 'distance', None), transform=int)\ .keyval('label', result.display_name or '')\ .keyval_not_none('name', result.locale_name or None)\ if options.get('addressdetails', False): _write_geocodejson_address(out, result.address_rows, result.place_id, result.country_code) out.key('admin').start_object() if result.address_rows: for line in result.address_rows: if line.isaddress and (line.admin_level or 15) < 15 and line.local_name \ and line.category[0] == 'boundary' and line.category[1] == 'administrative': out.keyval(f"level{line.admin_level}", line.local_name) out.end_object().next() if options.get('entrances', False): write_entrances(out, result.entrances) if options.get('extratags', False): out.keyval('extra', _add_admin_level(result)) out.end_object().next().end_object().next() out.key('geometry').raw(result.geometry.get('geojson') or result.centroid.to_geojson()).next() out.end_object().next() out.end_array().next().end_object() return out() GEOCODEJSON_RANKS = { 3: 'locality', 4: 'country', 5: 'state', 6: 'state', 7: 'state', 8: 'state', 9: 'state', 10: 'county', 11: 'county', 12: 'county', 13: 'city', 14: 'city', 15: 'city', 16: 'city', 17: 'district', 18: 'district', 19: 'district', 20: 'district', 21: 'district', 22: 'locality', 23: 'locality', 24: 'locality', 25: 'street', 26: 'street', 27: 'street', 28: 'house'} ================================================ FILE: src/nominatim_api/v1/format_xml.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Helper functions for output of results in XML format. """ from typing import Mapping, Any, Optional, Union import datetime as dt import xml.etree.ElementTree as ET from ..results import AddressLines, ReverseResult, ReverseResults, \ SearchResult, SearchResults from . import classtypes as cl from .helpers import _add_admin_level from ..types import EntranceDetails def _write_xml_address(root: ET.Element, address: AddressLines, country_code: Optional[str]) -> None: parts = {} for line in address: if line.isaddress: if line.local_name: label = cl.get_label_tag(line.category, line.extratags, line.rank_address, country_code) if label not in parts: parts[label] = line.local_name if line.names and 'ISO3166-2' in line.names and line.admin_level: parts[f"ISO3166-2-lvl{line.admin_level}"] = line.names['ISO3166-2'] for k, v in parts.items(): ET.SubElement(root, k).text = v if country_code: ET.SubElement(root, 'country_code').text = country_code def _create_base_entry(result: Union[ReverseResult, SearchResult], root: ET.Element, simple: bool) -> ET.Element: place = ET.SubElement(root, 'result' if simple else 'place') if result.place_id is not None: place.set('place_id', str(result.place_id)) if result.osm_object: osm_type = cl.OSM_TYPE_NAME.get(result.osm_object[0], None) if osm_type is not None: place.set('osm_type', osm_type) place.set('osm_id', str(result.osm_object[1])) if result.names and 'ref' in result.names: place.set('ref', result.names['ref']) elif result.locale_name: # bug reproduced from PHP place.set('ref', result.locale_name) place.set('lat', f"{result.centroid.lat:.7f}") place.set('lon', f"{result.centroid.lon:.7f}") bbox = cl.bbox_from_result(result) place.set('boundingbox', f"{bbox.minlat:.7f},{bbox.maxlat:.7f},{bbox.minlon:.7f},{bbox.maxlon:.7f}") place.set('place_rank', str(result.rank_search)) place.set('address_rank', str(result.rank_address)) if result.geometry: for key in ('text', 'svg'): if key in result.geometry: place.set('geo' + key, result.geometry[key]) if 'kml' in result.geometry: ET.SubElement(root if simple else place, 'geokml')\ .append(ET.fromstring(result.geometry['kml'])) if 'geojson' in result.geometry: place.set('geojson', result.geometry['geojson']) if simple: place.text = result.display_name or '' else: place.set('display_name', result.display_name or '') place.set('class', result.category[0]) place.set('type', result.category[1]) place.set('importance', str(result.calculated_importance())) return place def _create_entrance(root: ET.Element, entrance: EntranceDetails) -> None: entrance_node = ET.SubElement(root, 'entrance', attrib={ "osm_id": str(entrance.osm_id), "type": entrance.type, "lat": f"{entrance.location.lat:0.7f}", "lon": f"{entrance.location.lon:0.7f}", }) if entrance.extratags: for k, v in entrance.extratags.items(): ET.SubElement(entrance_node, 'tag', attrib={'key': k, 'value': v}) def format_base_xml(results: Union[ReverseResults, SearchResults], options: Mapping[str, Any], simple: bool, xml_root_tag: str, xml_extra_info: Mapping[str, str]) -> str: """ Format the result into an XML response. With 'simple' exactly one result will be output, otherwise a list. """ root = ET.Element(xml_root_tag) root.set('timestamp', dt.datetime.now(dt.timezone.utc).strftime('%a, %d %b %Y %H:%M:%S +00:00')) root.set('attribution', cl.OSM_ATTRIBUTION) for k, v in xml_extra_info.items(): root.set(k, v) if simple and not results: ET.SubElement(root, 'error').text = 'Unable to geocode' for result in results: place = _create_base_entry(result, root, simple) if not simple and options.get('icon_base_url', None): icon = cl.ICONS.get(result.category) if icon: place.set('icon', icon) if options.get('addressdetails', False) and result.address_rows: _write_xml_address(ET.SubElement(root, 'addressparts') if simple else place, result.address_rows, result.country_code) if options.get('extratags', False): eroot = ET.SubElement(root if simple else place, 'extratags') tags = _add_admin_level(result) if tags: for k, v in tags.items(): ET.SubElement(eroot, 'tag', attrib={'key': k, 'value': v}) if options.get('namedetails', False): eroot = ET.SubElement(root if simple else place, 'namedetails') if result.names: for k, v in result.names.items(): ET.SubElement(eroot, 'name', attrib={'desc': k}).text = v if options.get('entrances', False): eroot = ET.SubElement(root if simple else place, 'entrances') if result.entrances: for entrance in result.entrances: _create_entrance(eroot, entrance) return '\n' + ET.tostring(root, encoding='unicode') ================================================ FILE: src/nominatim_api/v1/helpers.py ================================================ # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2025 by the Nominatim developer community. # For a full list of authors see the git log. """ Helper function for parsing parameters and and outputting data specifically for the v1 version of the API. """ from typing import Tuple, Optional, Any, Dict, Iterable from itertools import chain import re from ..results import SearchResults, SourceTable, BaseResult from ..types import SearchDetails, GeometryFormat def _add_admin_level(result: BaseResult) -> Optional[Dict[str, str]]: """ Inject admin_level into extratags for boundary=administrative results. """ tags = result.extratags if result.category == ('boundary', 'administrative') and result.admin_level < 15: tags = dict(tags) if tags else {} tags['admin_level'] = str(result.admin_level) return tags REVERSE_MAX_RANKS = [2, 2, 2, # 0-2 Continent/Sea 4, 4, # 3-4 Country 8, # 5 State 10, 10, # 6-7 Region 12, 12, # 8-9 County 16, 17, # 10-11 City 18, # 12 Town 19, # 13 Village/Suburb 22, # 14 Hamlet/Neighbourhood 25, # 15 Localities 26, # 16 Major Streets 27, # 17 Minor Streets 30 # 18 Building ] def zoom_to_rank(zoom: int) -> int: """ Convert a zoom parameter into a rank according to the v1 API spec. """ return REVERSE_MAX_RANKS[max(0, min(18, zoom))] FEATURE_TYPE_TO_RANK: Dict[Optional[str], Tuple[int, int]] = { 'country': (4, 4), 'state': (8, 8), 'city': (14, 16), 'settlement': (8, 20) } def feature_type_to_rank(feature_type: Optional[str]) -> Tuple[int, int]: """ Convert a feature type parameter to a tuple of feature type name, minimum rank and maximum rank. """ return FEATURE_TYPE_TO_RANK.get(feature_type, (0, 30)) def extend_query_parts(queryparts: Dict[str, Any], details: Dict[str, Any], feature_type: Optional[str], namedetails: bool, extratags: bool, excluded: Iterable[str]) -> None: """ Add parameters from details dictionary to the query parts dictionary which is suitable as URL parameter dictionary. """ parsed = SearchDetails.from_kwargs(details) if parsed.geometry_output != GeometryFormat.NONE: if GeometryFormat.GEOJSON & parsed.geometry_output: queryparts['polygon_geojson'] = '1' if GeometryFormat.KML & parsed.geometry_output: queryparts['polygon_kml'] = '1' if GeometryFormat.SVG & parsed.geometry_output: queryparts['polygon_svg'] = '1' if GeometryFormat.TEXT & parsed.geometry_output: queryparts['polygon_text'] = '1' if parsed.address_details: queryparts['addressdetails'] = '1' if parsed.entrances: queryparts['entrances'] = '1' if namedetails: queryparts['namedetails'] = '1' if extratags: queryparts['extratags'] = '1' if parsed.geometry_simplification > 0.0: queryparts['polygon_threshold'] = f"{parsed.geometry_simplification:.6g}" if parsed.max_results != 10: queryparts['limit'] = str(parsed.max_results) if parsed.countries: queryparts['countrycodes'] = ','.join(parsed.countries) queryparts['exclude_place_ids'] = \ ','.join(chain(excluded, (str(e) for e in parsed.excluded))) if parsed.viewbox: queryparts['viewbox'] = ','.join(f"{c:.7g}" for c in parsed.viewbox.coords) if parsed.bounded_viewbox: queryparts['bounded'] = '1' if not details['dedupe']: queryparts['dedupe'] = '0' if feature_type in FEATURE_TYPE_TO_RANK: queryparts['featureType'] = feature_type def deduplicate_results(results: SearchResults, max_results: int) -> SearchResults: """ Remove results that look like duplicates. Two results are considered the same if they have the same OSM ID or if they have the same category, display name and rank. """ osm_ids_done = set() classification_done = set() deduped = SearchResults() for result in results: if result.source_table == SourceTable.PLACEX: classification = (result.osm_object[0] if result.osm_object else None, result.category, result.display_name, result.rank_address) if result.osm_object not in osm_ids_done \ and classification not in classification_done: deduped.append(result) osm_ids_done.add(result.osm_object) classification_done.add(classification) else: deduped.append(result) if len(deduped) >= max_results: break return deduped def _deg(axis: str) -> str: return f"(?P<{axis}_deg>\\d+\\.\\d+)°?" def _deg_min(axis: str) -> str: return f"(?P<{axis}_deg>\\d+)[°\\s]+(?P<{axis}_min>[\\d.]+)[′']*" def _deg_min_sec(axis: str) -> str: return f"(?P<{axis}_deg>\\d+)[°\\s]+(?P<{axis}_min>\\d+)[′'\\s]+(?P<{axis}_sec>[\\d.]+)[\"″]*" COORD_REGEX = [re.compile(r'(?:(?P
.*?)\s+)??' + r + r'(?:\s+(?P.*))?') for r in (
    r"(?P[NS])\s*" + _deg('lat') + r"[\s,]+" + r"(?P[EW])\s*" + _deg('lon'),
    _deg('lat') + r"\s*(?P[NS])[\s,]+" + _deg('lon') + r"\s*(?P[EW])",
    r"(?P[NS])\s*" + _deg_min('lat') + r"[\s,]+" + r"(?P[EW])\s*" + _deg_min('lon'),
    _deg_min('lat') + r"\s*(?P[NS])[\s,]+" + _deg_min('lon') + r"\s*(?P[EW])",
    r"(?P[NS])\s*" + _deg_min_sec('lat') + r"[\s,]+" + r"(?P[EW])\s*" + _deg_min_sec('lon'),
    _deg_min_sec('lat') + r"\s*(?P[NS])[\s,]+" + _deg_min_sec('lon') + r"\s*(?P[EW])",
    r"\[?(?P[+-]?\d+\.\d+)[\s,]+(?P[+-]?\d+\.\d+)\]?"
)]


def extract_coords_from_query(query: str) -> Tuple[str, Optional[float], Optional[float]]:
    """ Look for something that is formatted like a coordinate at the
        beginning or end of the query. If found, extract the coordinate and
        return the remaining query (or the empty string if the query
        consisted of nothing but a coordinate).

        Only the first match will be returned.
    """
    for regex in COORD_REGEX:
        match = regex.fullmatch(query)
        if match is None:
            continue
        groups = match.groupdict()
        if not groups['pre'] or not groups['post']:
            x = float(groups['lon_deg']) \
                + float(groups.get('lon_min', 0.0)) / 60.0 \
                + float(groups.get('lon_sec', 0.0)) / 3600.0
            if groups.get('ew') == 'W':
                x = -x
            y = float(groups['lat_deg']) \
                + float(groups.get('lat_min', 0.0)) / 60.0 \
                + float(groups.get('lat_sec', 0.0)) / 3600.0
            if groups.get('ns') == 'S':
                y = -y
            return groups['pre'] or groups['post'] or '', x, y

    return query, None, None


CATEGORY_REGEX = re.compile(r'(?P
.*?)\[(?P[a-zA-Z_]+)=(?P[a-zA-Z_]+)\](?P.*)')


def extract_category_from_query(query: str) -> Tuple[str, Optional[str], Optional[str]]:
    """ Extract a hidden category specification of the form '[key=value]' from
        the query. If found, extract key and value  and
        return the remaining query (or the empty string if the query
        consisted of nothing but a category).

        Only the first match will be returned.
    """
    match = CATEGORY_REGEX.search(query)
    if match is not None:
        return (match.group('pre').strip() + ' ' + match.group('post').strip()).strip(), \
               match.group('cls'), match.group('typ')

    return query, None, None


================================================
FILE: src/nominatim_api/v1/server_glue.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Generic part of the server implementation of the v1 API.
Combine with the scaffolding provided for the various Python ASGI frameworks.
"""
from typing import Optional, Any, Type, Dict, cast, Sequence, Tuple
from functools import reduce
import dataclasses
from urllib.parse import urlencode
import asyncio

import sqlalchemy as sa

from ..errors import UsageError
from .. import logging as loglib
from ..core import NominatimAPIAsync
from .format import RawDataList
from ..types import DataLayer, GeometryFormat, PlaceRef, PlaceID, OsmID, Point
from ..status import StatusResult
from ..results import DetailedResult, ReverseResults, SearchResult, SearchResults
from ..localization import Locales
from . import helpers
from ..server import content_types as ct
from ..server.asgi_adaptor import ASGIAdaptor, EndpointFunc
from ..sql.async_core_library import PGCORE_ERROR


def build_response(adaptor: ASGIAdaptor, output: str, status: int = 200,
                   num_results: int = 0) -> Any:
    """ Create a response from the given output. Wraps a JSONP function
        around the response, if necessary.
    """
    if adaptor.content_type == ct.CONTENT_JSON and status == 200:
        jsonp = adaptor.get('json_callback')
        if jsonp is not None:
            if any(not part.isidentifier() for part in jsonp.split('.')):
                adaptor.raise_error('Invalid json_callback value')
            output = f"{jsonp}({output})"
            adaptor.content_type = 'application/javascript; charset=utf-8'

    return adaptor.create_response(status, output, num_results)


def get_accepted_languages(adaptor: ASGIAdaptor) -> str:
    """ Return the accepted languages.
    """
    return adaptor.get('accept-language')\
        or adaptor.get_header('accept-language')\
        or adaptor.config().DEFAULT_LANGUAGE


def setup_debugging(adaptor: ASGIAdaptor) -> bool:
    """ Set up collection of debug information if requested.

        Return True when debugging was requested.
    """
    if adaptor.get_bool('debug', False):
        loglib.set_log_output('html')
        adaptor.content_type = ct.CONTENT_HTML
        return True

    return False


def get_layers(adaptor: ASGIAdaptor) -> Optional[DataLayer]:
    """ Return a parsed version of the layer parameter.
    """
    param = adaptor.get('layer', None)
    if param is None:
        return None

    return cast(DataLayer,
                reduce(DataLayer.__or__,
                       (getattr(DataLayer, s.upper()) for s in param.split(','))))


def parse_format(adaptor: ASGIAdaptor, result_type: Type[Any], default: str) -> str:
    """ Get and check the 'format' parameter and prepare the formatter.
        `result_type` is the type of result to be returned by the function
        and `default` the format value to assume when no parameter is present.
    """
    fmt = adaptor.get('format', default=default)
    assert fmt is not None

    formatting = adaptor.formatting()

    if not formatting.supports_format(result_type, fmt):
        adaptor.raise_error("Parameter 'format' must be one of: " +
                            ', '.join(formatting.list_formats(result_type)))

    adaptor.content_type = formatting.get_content_type(fmt)
    return fmt


def parse_geometry_details(adaptor: ASGIAdaptor, fmt: str) -> Dict[str, Any]:
    """ Create details structure from the supplied geometry parameters.
    """
    numgeoms = 0
    output = GeometryFormat.NONE
    if adaptor.get_bool('polygon_geojson', False):
        output |= GeometryFormat.GEOJSON
        numgeoms += 1
    if fmt not in ('geojson', 'geocodejson'):
        if adaptor.get_bool('polygon_text', False):
            output |= GeometryFormat.TEXT
            numgeoms += 1
        if adaptor.get_bool('polygon_kml', False):
            output |= GeometryFormat.KML
            numgeoms += 1
        if adaptor.get_bool('polygon_svg', False):
            output |= GeometryFormat.SVG
            numgeoms += 1

    if numgeoms > adaptor.config().get_int('POLYGON_OUTPUT_MAX_TYPES'):
        adaptor.raise_error('Too many polygon output options selected.')

    return {'address_details': True,
            'geometry_simplification': adaptor.get_float('polygon_threshold', 0.0),
            'geometry_output': output
            }


def has_search_name(conn: sa.engine.Connection) -> bool:
    """ Check if the search_name table exists in the database.
    """
    return sa.inspect(conn).has_table('search_name')


async def status_endpoint(api: NominatimAPIAsync, params: ASGIAdaptor) -> Any:
    """ Server glue for /status endpoint. See API docs for details.
    """
    result = await api.status()

    fmt = parse_format(params, StatusResult, 'text')

    if fmt == 'text' and result.status:
        status_code = 500
    else:
        status_code = 200

    return build_response(params, params.formatting().format_result(result, fmt, {}),
                          status=status_code)


async def details_endpoint(api: NominatimAPIAsync, params: ASGIAdaptor) -> Any:
    """ Server glue for /details endpoint. See API docs for details.
    """
    fmt = parse_format(params, DetailedResult, 'json')
    place_id = params.get_int('place_id', 0)
    place: PlaceRef
    if place_id:
        place = PlaceID(place_id)
    else:
        osmtype = params.get('osmtype')
        if osmtype is None:
            params.raise_error("Missing ID parameter 'place_id' or 'osmtype'.")
        place = OsmID(osmtype, params.get_int('osmid'), params.get('class'))

    debug = setup_debugging(params)

    result = await api.details(place,
                               address_details=params.get_bool('addressdetails', False),
                               entrances=params.get_bool('entrances', False),
                               linked_places=params.get_bool('linkedplaces', True),
                               parented_places=params.get_bool('hierarchy', False),
                               keywords=params.get_bool('keywords', False),
                               geometry_output=(GeometryFormat.GEOJSON
                                                if params.get_bool('polygon_geojson', False)
                                                else GeometryFormat.NONE),
                               query_stats=params.query_stats()
                               )

    if debug:
        return build_response(params, loglib.get_and_disable())

    if result is None:
        params.raise_error('No place with that OSM ID found.', status=404)

    locales = Locales.from_accept_languages(get_accepted_languages(params),
                                            params.config().OUTPUT_NAMES)
    locales.localize_results([result])

    output = params.formatting().format_result(
        result, fmt,
        {'locales': locales,
         'group_hierarchy': params.get_bool('group_hierarchy', False),
         'icon_base_url': params.config().MAPICON_URL,
         'entrances': params.get_bool('entrances', False),
         })

    return build_response(params, output, num_results=1)


async def reverse_endpoint(api: NominatimAPIAsync, params: ASGIAdaptor) -> Any:
    """ Server glue for /reverse endpoint. See API docs for details.
    """
    fmt = parse_format(params, ReverseResults, 'xml')
    debug = setup_debugging(params)
    coord = Point(params.get_float('lon'), params.get_float('lat'))

    details = parse_geometry_details(params, fmt)
    details['max_rank'] = helpers.zoom_to_rank(params.get_int('zoom', 18))
    details['layers'] = get_layers(params)
    details['query_stats'] = params.query_stats()
    details['entrances'] = params.get_bool('entrances', False)

    result = await api.reverse(coord, **details)

    if debug:
        return build_response(params, loglib.get_and_disable(), num_results=1 if result else 0)

    if fmt == 'xml':
        queryparts = {'lat': str(coord.lat), 'lon': str(coord.lon), 'format': 'xml'}
        zoom = params.get('zoom', None)
        if zoom:
            queryparts['zoom'] = zoom
        query = urlencode(queryparts)
    else:
        query = ''

    if result:
        Locales.from_accept_languages(get_accepted_languages(params),
                                      params.config().OUTPUT_NAMES).localize_results([result])

    fmt_options = {'query': query,
                   'extratags': params.get_bool('extratags', False),
                   'namedetails': params.get_bool('namedetails', False),
                   'entrances': params.get_bool('entrances', False),
                   'addressdetails': params.get_bool('addressdetails', True)}

    output = params.formatting().format_result(ReverseResults([result] if result else []),
                                               fmt, fmt_options)

    return build_response(params, output, num_results=1 if result else 0)


async def lookup_endpoint(api: NominatimAPIAsync, params: ASGIAdaptor) -> Any:
    """ Server glue for /lookup endpoint. See API docs for details.
    """
    fmt = parse_format(params, SearchResults, 'xml')
    debug = setup_debugging(params)
    details = parse_geometry_details(params, fmt)
    details['query_stats'] = params.query_stats()
    details['entrances'] = params.get_bool('entrances', False)

    places = []
    for oid in (params.get('osm_ids') or '').split(','):
        oid = oid.strip()
        if len(oid) > 1 and oid[0] in 'RNWrnw' and oid[1:].isdigit():
            places.append(OsmID(oid[0].upper(), int(oid[1:])))

    if len(places) > params.config().get_int('LOOKUP_MAX_COUNT'):
        params.raise_error('Too many object IDs.')

    if places:
        results = await api.lookup(places, **details)
    else:
        results = SearchResults()

    if debug:
        return build_response(params, loglib.get_and_disable(), num_results=len(results))

    Locales.from_accept_languages(get_accepted_languages(params),
                                  params.config().OUTPUT_NAMES).localize_results(results)

    fmt_options = {'extratags': params.get_bool('extratags', False),
                   'namedetails': params.get_bool('namedetails', False),
                   'entrances': params.get_bool('entrances', False),
                   'addressdetails': params.get_bool('addressdetails', True)}

    output = params.formatting().format_result(results, fmt, fmt_options)

    return build_response(params, output, num_results=len(results))


async def _unstructured_search(query: str, api: NominatimAPIAsync,
                               details: Dict[str, Any]) -> SearchResults:
    if not query:
        return SearchResults()

    # Extract special format for coordinates from query.
    query, x, y = helpers.extract_coords_from_query(query)
    if x is not None:
        assert y is not None
        details['near'] = Point(x, y)
        details['near_radius'] = 0.1

    # If no query is left, revert to reverse search.
    if x is not None and not query:
        result = await api.reverse(details['near'], **details)
        if not result:
            return SearchResults()

        return SearchResults(
                  [SearchResult(**{f.name: getattr(result, f.name)
                                   for f in dataclasses.fields(SearchResult)
                                   if hasattr(result, f.name)})])

    query, cls, typ = helpers.extract_category_from_query(query)
    if cls is not None:
        assert typ is not None
        return await api.search_category([(cls, typ)], near_query=query, **details)

    return await api.search(query, **details)


async def search_endpoint(api: NominatimAPIAsync, params: ASGIAdaptor) -> Any:
    """ Server glue for /search endpoint. See API docs for details.
    """
    fmt = parse_format(params, SearchResults, 'jsonv2')
    debug = setup_debugging(params)
    details = parse_geometry_details(params, fmt)

    details['query_stats'] = params.query_stats()
    details['countries'] = params.get('countrycodes', None)
    details['entrances'] = params.get_bool('entrances', False)
    details['excluded'] = params.get('exclude_place_ids', None)
    details['viewbox'] = params.get('viewbox', None) or params.get('viewboxlbrt', None)
    details['bounded_viewbox'] = params.get_bool('bounded', False)
    details['dedupe'] = params.get_bool('dedupe', True)

    max_results = max(1, min(50, params.get_int('limit', 10)))
    details['max_results'] = (max_results + min(10, max_results)
                              if details['dedupe'] else max_results)

    details['min_rank'], details['max_rank'] = \
        helpers.feature_type_to_rank(params.get('featureType', ''))
    if params.get('featureType', None) is not None:
        details['layers'] = DataLayer.ADDRESS
    else:
        details['layers'] = get_layers(params)
    details['locales'] = Locales.from_accept_languages(get_accepted_languages(params),
                                                       params.config().OUTPUT_NAMES)

    # unstructured query parameters
    query = params.get('q', None)
    # structured query parameters
    queryparts = {}
    for key in ('amenity', 'street', 'city', 'county', 'state', 'postalcode', 'country'):
        details[key] = params.get(key, None)
        if details[key]:
            queryparts[key] = details[key]

    try:
        if query is not None:
            if queryparts:
                params.raise_error("Structured query parameters"
                                   "(amenity, street, city, county, state, postalcode, country)"
                                   " cannot be used together with 'q' parameter.")
            queryparts['q'] = query
            results = await _unstructured_search(query, api, details)
        else:
            query = ', '.join(queryparts.values())

            results = await api.search_address(**details)
    except UsageError as err:
        params.raise_error(str(err))

    details['locales'].localize_results(results)

    if details['dedupe'] and len(results) > 1:
        results = helpers.deduplicate_results(results, max_results)

    if debug:
        return build_response(params, loglib.get_and_disable(), num_results=len(results))

    if fmt == 'xml':
        helpers.extend_query_parts(queryparts, details,
                                   params.get('featureType', ''),
                                   params.get_bool('namedetails', False),
                                   params.get_bool('extratags', False),
                                   (f"{r.osm_object[0]}{r.osm_object[1]}"
                                    if r.osm_object
                                    else str(r.place_id)
                                    for r in results
                                    if r.osm_object or r.place_id))
        queryparts['format'] = fmt

        moreurl = params.base_uri() + '/search?' + urlencode(queryparts)
    else:
        moreurl = ''

    fmt_options = {'query': query, 'more_url': moreurl,
                   'exclude_place_ids': queryparts.get('exclude_place_ids'),
                   'viewbox': queryparts.get('viewbox'),
                   'extratags': params.get_bool('extratags', False),
                   'namedetails': params.get_bool('namedetails', False),
                   'entrances': params.get_bool('entrances', False),
                   'addressdetails': params.get_bool('addressdetails', False)}

    output = params.formatting().format_result(results, fmt, fmt_options)

    return build_response(params, output, num_results=len(results))


async def deletable_endpoint(api: NominatimAPIAsync, params: ASGIAdaptor) -> Any:
    """ Server glue for /deletable endpoint.
        This is a special endpoint that shows polygons that have been
        deleted or are broken in the OSM data but are kept in the
        Nominatim database to minimize disruption.
    """
    fmt = parse_format(params, RawDataList, 'json')

    results = RawDataList()
    async with api.begin() as conn:
        for osm_type in ('N', 'W', 'R'):
            sql = sa.text(""" SELECT p.place_id, country_code,
                                     name->'name' as name, i.*
                              FROM placex p, import_polygon_delete i
                              WHERE i.osm_type = :osm_type
                                    AND p.osm_id = i.osm_id AND p.osm_type = :osm_type
                                    AND p.class = i.class AND p.type = i.type
                          """)
            results.extend(r._asdict() for r in await conn.execute(sql, {'osm_type': osm_type}))

    return build_response(params, params.formatting().format_result(results, fmt, {}))


async def polygons_endpoint(api: NominatimAPIAsync, params: ASGIAdaptor) -> Any:
    """ Server glue for /polygons endpoint.
        This is a special endpoint that shows polygons that have changed
        their size but are kept in the Nominatim database with their
        old area to minimize disruption.
    """
    fmt = parse_format(params, RawDataList, 'json')
    sql_params: Dict[str, Any] = {
        'days': params.get_int('days', -1),
        'cls': params.get('class')
    }
    reduced = params.get_bool('reduced', False)

    async with api.begin() as conn:
        sql = sa.select(sa.text("""osm_type, osm_id, class, type,
                                   name->'name' as name,
                                   country_code, errormessage, updated"""))\
                .select_from(sa.text('import_polygon_error'))
        if sql_params['days'] > 0:
            sql = sql.where(sa.text("updated > 'now'::timestamp - make_interval(days => :days)"))
        if reduced:
            sql = sql.where(sa.text("errormessage like 'Area reduced%'"))
        if sql_params['cls'] is not None:
            sql = sql.where(sa.text("class = :cls"))

        sql = sql.order_by(sa.literal_column('updated').desc()).limit(1000)

        results = RawDataList(r._asdict() for r in await conn.execute(sql, sql_params))

    return build_response(params, params.formatting().format_result(results, fmt, {}))


async def search_unavailable_endpoint(api: NominatimAPIAsync, params: ASGIAdaptor) -> Any:
    """ Server glue for /search endpoint in reverse-only mode.
        Returns 404 when search functionality is not available.
    """
    params.raise_error('Search not available (reverse-only mode)', 404)


class LazySearchEndpoint:
    """
    Lazy-loading search endpoint that replaces itself after first successful check.

    - Falcon: EndpointWrapper stores this instance in wrapper.func
      On first request, replace wrapper.func directly with real endpoint

    - Starlette: _wrap_endpoint wraps this instance in a callback
      store a delegate function and call it on subsequent requests
    """
    def __init__(self, api: NominatimAPIAsync, real_endpoint: EndpointFunc):
        self.api = api
        self.real_endpoint = real_endpoint
        self._lock = asyncio.Lock()
        self._wrapper: Any = None  # Store reference to Falcon's EndpointWrapper
        self._delegate: Optional[EndpointFunc] = None

    def set_wrapper(self, wrapper: Any) -> None:
        self._wrapper = wrapper

    async def __call__(self, api: NominatimAPIAsync, params: ASGIAdaptor) -> Any:
        if self._delegate is None:
            async with self._lock:
                # Double-check after acquiring lock (thread safety)
                if self._delegate is None:
                    try:
                        async with api.begin() as conn:
                            has_table = await conn.connection.run_sync(
                                has_search_name)

                        if has_table:
                            # For Starlette
                            self._delegate = self.real_endpoint
                            # For Falcon
                            if self._wrapper is not None:
                                self._wrapper.func = self.real_endpoint
                        else:
                            self._delegate = search_unavailable_endpoint
                            if self._wrapper is not None:
                                self._wrapper.func = search_unavailable_endpoint

                    except (PGCORE_ERROR, sa.exc.OperationalError, OSError):
                        # No _delegate set, so retry on next request
                        params.raise_error('Search temporarily unavailable', 503)

        return await self._delegate(api, params)


async def get_routes(api: NominatimAPIAsync) -> Sequence[Tuple[str, EndpointFunc]]:
    routes = [
        ('status', status_endpoint),
        ('details', details_endpoint),
        ('reverse', reverse_endpoint),
        ('lookup', lookup_endpoint),
        ('deletable', deletable_endpoint),
        ('polygons', polygons_endpoint),
    ]

    try:
        async with api.begin() as conn:
            if await conn.connection.run_sync(has_search_name):
                routes.append(('search', search_endpoint))
            else:
                routes.append(('search', search_unavailable_endpoint))
    except (PGCORE_ERROR, sa.exc.OperationalError, OSError):
        routes.append(('search', LazySearchEndpoint(api, search_endpoint)))

    return routes


================================================
FILE: src/nominatim_api/version.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Version information for the Nominatim API.
"""

NOMINATIM_API_VERSION = '5.3.0'


================================================
FILE: src/nominatim_db/__init__.py
================================================


================================================
FILE: src/nominatim_db/cli.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Command-line interface to the Nominatim functions for import, update,
database administration and querying.
"""
from typing import Optional, List, Mapping
import importlib
import logging
import sys
import argparse
import asyncio
from pathlib import Path

from .config import Configuration
from .errors import UsageError
from . import clicmd
from . import version
from .clicmd.args import NominatimArgs, Subcommand

LOG = logging.getLogger()


class CommandlineParser:
    """ Wraps some of the common functions for parsing the command line
        and setting up subcommands.
    """
    def __init__(self, prog: str, description: Optional[str]):
        self.parser = argparse.ArgumentParser(
            prog=prog,
            description=description,
            formatter_class=argparse.RawDescriptionHelpFormatter)

        self.subs = self.parser.add_subparsers(title='available commands',
                                               dest='subcommand')

        # Global arguments that only work if no sub-command given
        self.parser.add_argument('--version', action='store_true',
                                 help='Print Nominatim version and exit')

        # Arguments added to every sub-command
        self.default_args = argparse.ArgumentParser(add_help=False)
        group = self.default_args.add_argument_group('Default arguments')
        group.add_argument('-h', '--help', action='help',
                           help='Show this help message and exit')
        group.add_argument('-q', '--quiet', action='store_const', const=0,
                           dest='verbose', default=1,
                           help='Print only error messages')
        group.add_argument('-v', '--verbose', action='count', default=1,
                           help='Increase verboseness of output')
        group.add_argument('--project-dir', metavar='DIR', default='.',
                           help='Base directory of the Nominatim installation (default:.)')
        group.add_argument('-j', '--threads', metavar='NUM', type=int,
                           help='Number of parallel threads to use')

    def nominatim_version_text(self) -> str:
        """ Program name and version number as string
        """
        text = f'Nominatim version {version.NOMINATIM_VERSION!s}'
        if version.GIT_COMMIT_HASH is not None:
            text += f' ({version.GIT_COMMIT_HASH})'
        return text

    def add_subcommand(self, name: str, cmd: Subcommand) -> None:
        """ Add a subcommand to the parser. The subcommand must be a class
            with a function add_args() that adds the parameters for the
            subcommand and a run() function that executes the command.
        """
        assert cmd.__doc__ is not None

        parser = self.subs.add_parser(name, parents=[self.default_args],
                                      help=cmd.__doc__.split('\n', 1)[0],
                                      description=cmd.__doc__,
                                      formatter_class=argparse.RawDescriptionHelpFormatter,
                                      add_help=False)
        parser.set_defaults(command=cmd)
        cmd.add_args(parser)

    def run(self, cli_args: Optional[List[str]],
            environ: Optional[Mapping[str, str]]) -> int:
        """ Parse the command line arguments of the program and execute the
            appropriate subcommand.
        """
        args = NominatimArgs()
        try:
            self.parser.parse_args(args=cli_args, namespace=args)
        except SystemExit:
            return 1

        if args.version:
            print(self.nominatim_version_text())
            return 0

        if args.subcommand is None:
            self.parser.print_help()
            return 1

        args.project_dir = Path(args.project_dir).resolve()

        if cli_args is None:
            logging.basicConfig(stream=sys.stderr,
                                format='%(asctime)s: %(message)s',
                                datefmt='%Y-%m-%d %H:%M:%S',
                                level=max(4 - args.verbose, 1) * 10)

        args.config = Configuration(args.project_dir, environ=environ)

        log = logging.getLogger()
        log.warning('Using project directory: %s', str(args.project_dir))

        try:
            return args.command.run(args)
        except UsageError as exception:
            if log.isEnabledFor(logging.DEBUG):
                raise  # use Python's exception printing
            log.fatal('FATAL: %s', exception)

        # If we get here, then execution has failed in some way.
        return 1


# Subcommand classes
#
# Each class needs to implement two functions: add_args() adds the CLI parameters
# for the subfunction, run() executes the subcommand.
#
# The class documentation doubles as the help text for the command. The
# first line is also used in the summary when calling the program without
# a subcommand.
#
# No need to document the functions each time.
class AdminServe:
    """\
    Start a simple web server for serving the API.

    This command starts a built-in webserver to serve the website
    from the current project directory. This webserver is only suitable
    for testing and development. Do not use it in production setups!

    There are two different webserver implementations for Python available:
    falcon (the default) and starlette. You need to make sure the
    appropriate Python packages as well as the uvicorn package are
    installed to use this function.

    By the default, the webserver can be accessed at: http://127.0.0.1:8088
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Server arguments')
        group.add_argument('--server', default='127.0.0.1:8088',
                           help='The address the server will listen to.')
        group.add_argument('--engine', default='falcon',
                           choices=('falcon', 'starlette'),
                           help='Webserver framework to run. (default: falcon)')

    def run(self, args: NominatimArgs) -> int:
        asyncio.run(self.run_uvicorn(args))

        return 0

    async def run_uvicorn(self, args: NominatimArgs) -> None:
        import uvicorn

        server_info = args.server.split(':', 1)
        host = server_info[0]
        if len(server_info) > 1:
            if not server_info[1].isdigit():
                raise UsageError('Invalid format for --server parameter. Use :')
            port = int(server_info[1])
        else:
            port = 8088

        server_module = importlib.import_module(f'nominatim_api.server.{args.engine}.server')

        app = server_module.get_application(args.project_dir)

        config = uvicorn.Config(app, host=host, port=port)
        server = uvicorn.Server(config)
        await server.serve()


def get_set_parser() -> CommandlineParser:
    """\
    Initializes the parser and adds various subcommands for
    nominatim cli.
    """
    parser = CommandlineParser('nominatim', nominatim.__doc__)

    parser.add_subcommand('import', clicmd.SetupAll())
    parser.add_subcommand('freeze', clicmd.SetupFreeze())
    parser.add_subcommand('replication', clicmd.UpdateReplication())

    parser.add_subcommand('special-phrases', clicmd.ImportSpecialPhrases())

    parser.add_subcommand('add-data', clicmd.UpdateAddData())
    parser.add_subcommand('index', clicmd.UpdateIndex())
    parser.add_subcommand('refresh', clicmd.UpdateRefresh())

    parser.add_subcommand('admin', clicmd.AdminFuncs())

    try:
        exportcmd = importlib.import_module('nominatim_db.clicmd.export')
        apicmd = importlib.import_module('nominatim_db.clicmd.api')
        convertcmd = importlib.import_module('nominatim_db.clicmd.convert')

        parser.add_subcommand('export', exportcmd.QueryExport())
        parser.add_subcommand('convert', convertcmd.ConvertDB())
        parser.add_subcommand('serve', AdminServe())

        parser.add_subcommand('search', apicmd.APISearch())
        parser.add_subcommand('reverse', apicmd.APIReverse())
        parser.add_subcommand('lookup', apicmd.APILookup())
        parser.add_subcommand('details', apicmd.APIDetails())
        parser.add_subcommand('status', apicmd.APIStatus())
    except ModuleNotFoundError as ex:
        if not ex.name or 'nominatim_api' not in ex.name:
            raise ex

        parser.parser.epilog = \
            f'\n\nNominatim API package not found (was looking for module: {ex.name}).'\
            '\nThe following commands are not available:'\
            '\n    export, convert, serve, search, reverse, lookup, details, status'\
            "\n\nRun 'pip install nominatim-api' to install the package."

    return parser


def nominatim(cli_args: Optional[List[str]] = None,
              environ: Optional[Mapping[str, str]] = None) -> int:
    """\
    Command-line tools for importing, updating, administrating and
    querying the Nominatim database.

    'cli_args' is a list of parameters for the command to run. If not given,
    sys.args will be used.

    'environ' is the dictionary of environment variables containing the
    Nominatim configuration. When None, the os.environ is inherited.
    """
    return get_set_parser().run(cli_args=cli_args, environ=environ)


================================================
FILE: src/nominatim_db/clicmd/__init__.py
================================================
# SPDX-License-Identifier: GPL-2.0-only
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2023 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Subcommand definitions for the command-line tool.
"""

from .setup import SetupAll as SetupAll
from .replication import UpdateReplication as UpdateReplication
from .index import UpdateIndex as UpdateIndex
from .refresh import UpdateRefresh as UpdateRefresh
from .add_data import UpdateAddData as UpdateAddData
from .admin import AdminFuncs as AdminFuncs
from .freeze import SetupFreeze as SetupFreeze
from .special_phrases import ImportSpecialPhrases as ImportSpecialPhrases


================================================
FILE: src/nominatim_db/clicmd/add_data.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of the 'add-data' subcommand.
"""
from typing import cast
import argparse
import logging
import asyncio

import psutil

from .args import NominatimArgs
from ..db.connection import connect
from ..tools.freeze import is_frozen


LOG = logging.getLogger()


class UpdateAddData:
    """\
    Add additional data from a file or an online source.

    This command allows to add or update the search data in the database.
    The data can come either from an OSM file or single OSM objects can
    directly be downloaded from the OSM API. This function only loads the
    data into the database. Afterwards it still needs to be integrated
    in the search index. Use the `nominatim index` command for that.

    The command can also be used to add external non-OSM data to the
    database. At the moment the only supported format is TIGER housenumber
    data. See the online documentation at
    https://nominatim.org/release-docs/latest/customize/Tiger/
    for more information.
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group_name = parser.add_argument_group('Source')
        group1 = group_name.add_mutually_exclusive_group(required=True)
        group1.add_argument('--file', metavar='FILE',
                            help='Import data from an OSM file or diff file')
        group1.add_argument('--diff', metavar='FILE',
                            help='Import data from an OSM diff file (deprecated: use --file)')
        group1.add_argument('--node', metavar='ID', type=int,
                            help='Import a single node from the API')
        group1.add_argument('--way', metavar='ID', type=int,
                            help='Import a single way from the API')
        group1.add_argument('--relation', metavar='ID', type=int,
                            help='Import a single relation from the API')
        group1.add_argument('--tiger-data', metavar='DIR',
                            help='Add housenumbers from the US TIGER census database')
        group2 = parser.add_argument_group('Extra arguments')
        group2.add_argument('--use-main-api', action='store_true',
                            help='Use OSM API instead of Overpass to download objects')
        group2.add_argument('--osm2pgsql-cache', metavar='SIZE', type=int,
                            help='Size of cache to be used by osm2pgsql (in MB)')
        group2.add_argument('--socket-timeout', dest='socket_timeout', type=int, default=60,
                            help='Set timeout for file downloads')

    def run(self, args: NominatimArgs) -> int:
        from ..tools import add_osm_data

        if args.tiger_data:
            return asyncio.run(self._add_tiger_data(args))

        with connect(args.config.get_libpq_dsn()) as conn:
            if is_frozen(conn):
                print('Database is marked frozen. New data can\'t be added.')
                return 1

        osm2pgsql_params = args.osm2pgsql_options(default_cache=1000, default_threads=1)
        if args.file or args.diff:
            return add_osm_data.add_data_from_file(args.config.get_libpq_dsn(),
                                                   cast(str, args.file or args.diff),
                                                   osm2pgsql_params)

        if args.node:
            return add_osm_data.add_osm_object(args.config.get_libpq_dsn(),
                                               'node', args.node,
                                               args.use_main_api,
                                               osm2pgsql_params)

        if args.way:
            return add_osm_data.add_osm_object(args.config.get_libpq_dsn(),
                                               'way', args.way,
                                               args.use_main_api,
                                               osm2pgsql_params)

        if args.relation:
            return add_osm_data.add_osm_object(args.config.get_libpq_dsn(),
                                               'relation', args.relation,
                                               args.use_main_api,
                                               osm2pgsql_params)

        return 0

    async def _add_tiger_data(self, args: NominatimArgs) -> int:
        from ..tokenizer import factory as tokenizer_factory
        from ..tools import tiger_data

        assert args.tiger_data

        tokenizer = tokenizer_factory.get_tokenizer_for_db(args.config)
        return await tiger_data.add_tiger_data(args.tiger_data,
                                               args.config,
                                               args.threads or psutil.cpu_count() or 1,
                                               tokenizer)


================================================
FILE: src/nominatim_db/clicmd/admin.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of the 'admin' subcommand.
"""
import logging
import argparse
import random

from ..errors import UsageError
from ..db.connection import connect, table_exists
from .args import NominatimArgs


LOG = logging.getLogger()


class AdminFuncs:
    """\
    Analyse and maintain the database.
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Admin tasks')
        objs = group.add_mutually_exclusive_group(required=True)
        objs.add_argument('--warm', action='store_true',
                          help='Warm database caches for search and reverse queries')
        objs.add_argument('--check-database', action='store_true',
                          help='Check that the database is complete and operational')
        objs.add_argument('--migrate', action='store_true',
                          help='Migrate the database to a new software version')
        objs.add_argument('--analyse-indexing', action='store_true',
                          help='Print performance analysis of the indexing process')
        objs.add_argument('--collect-os-info', action="store_true",
                          help="Generate a report about the host system information")
        objs.add_argument('--clean-deleted', action='store', metavar='AGE',
                          help='Clean up deleted relations')
        group = parser.add_argument_group('Arguments for cache warming')
        group.add_argument('--search-only', action='store_const', dest='target',
                           const='search',
                           help="Only pre-warm tables for search queries")
        group.add_argument('--reverse-only', action='store_const', dest='target',
                           const='reverse',
                           help="Only pre-warm tables for reverse queries")
        group = parser.add_argument_group('Arguments for index anaysis')
        mgroup = group.add_mutually_exclusive_group()
        mgroup.add_argument('--osm-id', type=str,
                            help='Analyse indexing of the given OSM object')
        mgroup.add_argument('--place-id', type=int,
                            help='Analyse indexing of the given Nominatim object')

    def run(self, args: NominatimArgs) -> int:
        if args.warm:
            return self._warm(args)

        if args.check_database:
            LOG.warning('Checking database')
            from ..tools import check_database
            return check_database.check_database(args.config)

        if args.analyse_indexing:
            LOG.warning('Analysing performance of indexing function')
            from ..tools import admin
            admin.analyse_indexing(args.config, osm_id=args.osm_id, place_id=args.place_id)
            return 0

        if args.migrate:
            LOG.warning('Checking for necessary database migrations')
            from ..tools import migration
            return migration.migrate(args.config, args)

        if args.collect_os_info:
            LOG.warning("Reporting System Information")
            from ..tools import collect_os_info
            collect_os_info.report_system_information(args.config)
            return 0

        if args.clean_deleted:
            LOG.warning('Cleaning up deleted relations')
            from ..tools import admin
            admin.clean_deleted_relations(args.config, age=args.clean_deleted)
            return 0

        return 1

    def _warm(self, args: NominatimArgs) -> int:
        try:
            import nominatim_api as napi
        except ModuleNotFoundError as exp:
            raise UsageError("Warming requires nominatim API. "
                             "Install with 'pip install nominatim-api'.") from exp
        LOG.warning('Warming database caches')

        api = napi.NominatimAPI(args.project_dir)

        try:
            if args.target != 'search':
                for _ in range(1000):
                    api.reverse((random.uniform(-90, 90), random.uniform(-180, 180)),
                                address_details=True)

            if args.target != 'reverse':
                from ..tokenizer import factory as tokenizer_factory

                tokenizer = tokenizer_factory.get_tokenizer_for_db(args.config)
                with connect(args.config.get_libpq_dsn()) as conn:
                    if table_exists(conn, 'search_name'):
                        words = tokenizer.most_frequent_words(conn, 1000)
                    else:
                        words = []

                for word in words:
                    api.search(word)
        finally:
            api.close()

        return 0


================================================
FILE: src/nominatim_db/clicmd/api.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Subcommand definitions for API calls from the command line.
"""
from typing import Dict, Any, Optional, Type, Mapping
import argparse
import logging
import json
import sys
import pprint
from functools import reduce

import nominatim_api as napi
from nominatim_api.v1.helpers import zoom_to_rank, deduplicate_results
from nominatim_api.server.content_types import CONTENT_JSON
import nominatim_api.logging as loglib
from ..config import Configuration
from ..errors import UsageError
from .args import NominatimArgs


LOG = logging.getLogger()


STRUCTURED_QUERY = (
    ('amenity', 'name and/or type of POI'),
    ('street', 'housenumber and street'),
    ('city', 'city, town or village'),
    ('county', 'county'),
    ('state', 'state'),
    ('country', 'country'),
    ('postalcode', 'postcode')
)


EXTRADATA_PARAMS = (
    ('addressdetails', 'Include a breakdown of the address into elements'),
    ('extratags', ("Include additional information if available "
                   "(e.g. wikipedia link, opening hours)")),
    ('entrances', 'Include a list of tagged entrance nodes'),
    ('namedetails', 'Include a list of alternative names')
)


def _add_list_format(parser: argparse.ArgumentParser) -> None:
    group = parser.add_argument_group('Other options')
    group.add_argument('--list-formats', action='store_true',
                       help='List supported output formats and exit.')


def _add_api_output_arguments(parser: argparse.ArgumentParser) -> None:
    group = parser.add_argument_group('Output formatting')
    group.add_argument('--format', type=str, default='jsonv2',
                       help='Format of result (use --list-format to see supported formats)')
    for name, desc in EXTRADATA_PARAMS:
        group.add_argument('--' + name, action='store_true', help=desc)

    group.add_argument('--lang', '--accept-language', metavar='LANGS',
                       help='Preferred language order for presenting search results')
    group.add_argument('--polygon-output',
                       choices=['geojson', 'kml', 'svg', 'text'],
                       help='Output geometry of results as a GeoJSON, KML, SVG or WKT')
    group.add_argument('--polygon-threshold', type=float, default=0.0,
                       metavar='TOLERANCE',
                       help=("Simplify output geometry."
                             "Parameter is difference tolerance in degrees."))


def _get_geometry_output(args: NominatimArgs) -> napi.GeometryFormat:
    """ Get the requested geometry output format in a API-compatible
        format.
    """
    if not args.polygon_output:
        return napi.GeometryFormat.NONE
    if args.polygon_output == 'geojson':
        return napi.GeometryFormat.GEOJSON
    if args.polygon_output == 'kml':
        return napi.GeometryFormat.KML
    if args.polygon_output == 'svg':
        return napi.GeometryFormat.SVG
    if args.polygon_output == 'text':
        return napi.GeometryFormat.TEXT

    try:
        return napi.GeometryFormat[args.polygon_output.upper()]
    except KeyError as exp:
        raise UsageError(f"Unknown polygon output format '{args.polygon_output}'.") from exp


def _get_locales(args: NominatimArgs, config: Configuration) -> napi.Locales:
    """ Get the locales from the language parameter.
    """
    language = args.lang or config.DEFAULT_LANGUAGE
    output_names = config.OUTPUT_NAMES

    if language:
        return napi.Locales.from_accept_languages(language, output_names)

    return napi.Locales()


def _get_layers(args: NominatimArgs, default: Optional[napi.DataLayer]) -> Optional[napi.DataLayer]:
    """ Get the list of selected layers as a DataLayer enum.
    """
    if not args.layers:
        return default

    return reduce(napi.DataLayer.__or__,
                  (napi.DataLayer[s.upper()] for s in args.layers))


def _list_formats(formatter: napi.FormatDispatcher, rtype: Type[Any]) -> int:
    for fmt in formatter.list_formats(rtype):
        print(fmt)
    print('debug')
    print('raw')

    return 0


def _print_output(formatter: napi.FormatDispatcher, result: Any,
                  fmt: str, options: Mapping[str, Any]) -> None:

    if fmt == 'raw':
        pprint.pprint(result)
    else:
        output = formatter.format_result(result, fmt, options)
        if formatter.get_content_type(fmt) == CONTENT_JSON:
            # reformat the result, so it is pretty-printed
            try:
                json.dump(json.loads(output), sys.stdout, indent=4, ensure_ascii=False)
            except json.decoder.JSONDecodeError as err:
                # Catch the error here, so that data can be debugged,
                # when people are developing custom result formatters.
                LOG.fatal("Parsing json failed: %s\nUnformatted output:\n%s", err, output)
        else:
            sys.stdout.write(output)
        sys.stdout.write('\n')


class APISearch:
    """\
    Execute a search query.

    This command works exactly the same as if calling the /search endpoint on
    the web API. See the online documentation for more details on the
    various parameters:
    https://nominatim.org/release-docs/latest/api/Search/
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Query arguments')
        group.add_argument('--query',
                           help='Free-form query string')
        for name, desc in STRUCTURED_QUERY:
            group.add_argument('--' + name, help='Structured query: ' + desc)

        _add_api_output_arguments(parser)

        group = parser.add_argument_group('Result limitation')
        group.add_argument('--countrycodes', metavar='CC,..',
                           help='Limit search results to one or more countries')
        group.add_argument('--exclude_place_ids', metavar='ID,..',
                           help='List of search object to be excluded')
        group.add_argument('--limit', type=int, default=10,
                           help='Limit the number of returned results')
        group.add_argument('--viewbox', metavar='X1,Y1,X2,Y2',
                           help='Preferred area to find search results')
        group.add_argument('--bounded', action='store_true',
                           help='Strictly restrict results to viewbox area')
        group.add_argument('--layer', metavar='LAYER',
                           choices=[n.name.lower() for n in napi.DataLayer if n.name],
                           action='append', required=False, dest='layers',
                           help='Restrict results to one or more layers (may be repeated)')
        group.add_argument('--no-dedupe', action='store_false', dest='dedupe',
                           help='Do not remove duplicates from the result list')
        _add_list_format(parser)

    def run(self, args: NominatimArgs) -> int:
        formatter = napi.load_format_dispatcher('v1', args.project_dir)

        if args.list_formats:
            return _list_formats(formatter, napi.SearchResults)

        if args.format in ('debug', 'raw'):
            loglib.set_log_output('text')
        elif not formatter.supports_format(napi.SearchResults, args.format):
            raise UsageError(f"Unsupported format '{args.format}'. "
                             'Use --list-formats to see supported formats.')

        layers = _get_layers(args, None)

        try:
            with napi.NominatimAPI(args.project_dir) as api:
                params: Dict[str, Any] = {'max_results': args.limit + min(args.limit, 10),
                                          'address_details': True,  # needed for display name
                                          'geometry_output': _get_geometry_output(args),
                                          'geometry_simplification': args.polygon_threshold,
                                          'countries': args.countrycodes,
                                          'excluded': args.exclude_place_ids,
                                          'viewbox': args.viewbox,
                                          'bounded_viewbox': args.bounded,
                                          'layers': layers,
                                          'entrances': args.entrances,
                                          }

                if args.query:
                    results = api.search(args.query, **params)
                else:
                    results = api.search_address(amenity=args.amenity,
                                                 street=args.street,
                                                 city=args.city,
                                                 county=args.county,
                                                 state=args.state,
                                                 postalcode=args.postalcode,
                                                 country=args.country,
                                                 **params)
        except napi.UsageError as ex:
            raise UsageError(ex) from ex

        locales = _get_locales(args, api.config)
        locales.localize_results(results)

        if args.dedupe and len(results) > 1:
            results = deduplicate_results(results, args.limit)

        if args.format == 'debug':
            print(loglib.get_and_disable())
            return 0

        _print_output(formatter, results, args.format,
                      {'extratags': args.extratags,
                       'namedetails': args.namedetails,
                       'entrances': args.entrances,
                       'addressdetails': args.addressdetails})
        return 0


class APIReverse:
    """\
    Execute API reverse query.

    This command works exactly the same as if calling the /reverse endpoint on
    the web API. See the online documentation for more details on the
    various parameters:
    https://nominatim.org/release-docs/latest/api/Reverse/
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Query arguments')
        group.add_argument('--lat', type=float,
                           help='Latitude of coordinate to look up (in WGS84)')
        group.add_argument('--lon', type=float,
                           help='Longitude of coordinate to look up (in WGS84)')
        group.add_argument('--zoom', type=int,
                           help='Level of detail required for the address')
        group.add_argument('--layer', metavar='LAYER',
                           choices=[n.name.lower() for n in napi.DataLayer if n.name],
                           action='append', required=False, dest='layers',
                           help='Restrict results to one or more layers (may be repeated)')

        _add_api_output_arguments(parser)
        _add_list_format(parser)

    def run(self, args: NominatimArgs) -> int:
        formatter = napi.load_format_dispatcher('v1', args.project_dir)

        if args.list_formats:
            return _list_formats(formatter, napi.ReverseResults)

        if args.format in ('debug', 'raw'):
            loglib.set_log_output('text')
        elif not formatter.supports_format(napi.ReverseResults, args.format):
            raise UsageError(f"Unsupported format '{args.format}'. "
                             'Use --list-formats to see supported formats.')

        if args.lat is None or args.lon is None:
            raise UsageError("lat' and 'lon' parameters are required.")

        layers = _get_layers(args, napi.DataLayer.ADDRESS | napi.DataLayer.POI)

        try:
            with napi.NominatimAPI(args.project_dir) as api:
                result = api.reverse(napi.Point(args.lon, args.lat),
                                     max_rank=zoom_to_rank(args.zoom or 18),
                                     layers=layers,
                                     address_details=True,  # needed for display name
                                     geometry_output=_get_geometry_output(args),
                                     geometry_simplification=args.polygon_threshold)
        except napi.UsageError as ex:
            raise UsageError(ex) from ex

        if result is not None:
            locales = _get_locales(args, api.config)
            locales.localize_results([result])

        if args.format == 'debug':
            print(loglib.get_and_disable())
            return 0

        if result:
            _print_output(formatter, napi.ReverseResults([result]), args.format,
                          {'extratags': args.extratags,
                           'namedetails': args.namedetails,
                           'entrances': args.entrances,
                           'addressdetails': args.addressdetails})

            return 0

        LOG.error("Unable to geocode.")
        return 42


class APILookup:
    """\
    Execute API lookup query.

    This command works exactly the same as if calling the /lookup endpoint on
    the web API. See the online documentation for more details on the
    various parameters:
    https://nominatim.org/release-docs/latest/api/Lookup/
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Query arguments')
        group.add_argument('--id', metavar='OSMID',
                           action='append', dest='ids',
                           help='OSM id to lookup in format  (may be repeated)')

        _add_api_output_arguments(parser)
        _add_list_format(parser)

    def run(self, args: NominatimArgs) -> int:
        formatter = napi.load_format_dispatcher('v1', args.project_dir)

        if args.list_formats:
            return _list_formats(formatter, napi.ReverseResults)

        if args.format in ('debug', 'raw'):
            loglib.set_log_output('text')
        elif not formatter.supports_format(napi.ReverseResults, args.format):
            raise UsageError(f"Unsupported format '{args.format}'. "
                             'Use --list-formats to see supported formats.')

        if args.ids is None:
            raise UsageError("'id' parameter required.")

        places = [napi.OsmID(o[0], int(o[1:])) for o in args.ids]

        try:
            with napi.NominatimAPI(args.project_dir) as api:
                results = api.lookup(places,
                                     address_details=True,  # needed for display name
                                     geometry_output=_get_geometry_output(args),
                                     geometry_simplification=args.polygon_threshold or 0.0)
        except napi.UsageError as ex:
            raise UsageError(ex) from ex

        locales = _get_locales(args, api.config)
        locales.localize_results(results)

        if args.format == 'debug':
            print(loglib.get_and_disable())
            return 0

        _print_output(formatter, results, args.format,
                      {'extratags': args.extratags,
                       'namedetails': args.namedetails,
                       'entrances': args.entrances,
                       'addressdetails': args.addressdetails})
        return 0


class APIDetails:
    """\
    Execute API details query.

    This command works exactly the same as if calling the /details endpoint on
    the web API. See the online documentation for more details on the
    various parameters:
    https://nominatim.org/release-docs/latest/api/Details/
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Query arguments')
        group.add_argument('--node', '-n', type=int,
                           help="Look up the OSM node with the given ID.")
        group.add_argument('--way', '-w', type=int,
                           help="Look up the OSM way with the given ID.")
        group.add_argument('--relation', '-r', type=int,
                           help="Look up the OSM relation with the given ID.")
        group.add_argument('--place_id', '-p', type=int,
                           help='Database internal identifier of the OSM object to look up')
        group.add_argument('--class', dest='object_class',
                           help=("Class type to disambiguated multiple entries "
                                 "of the same object."))

        group = parser.add_argument_group('Output arguments')
        group.add_argument('--format', type=str, default='json',
                           help='Format of result (use --list-formats to see supported formats)')
        group.add_argument('--addressdetails', action='store_true',
                           help='Include a breakdown of the address into elements')
        group.add_argument('--keywords', action='store_true',
                           help='Include a list of name keywords and address keywords')
        group.add_argument('--linkedplaces', action='store_true',
                           help='Include a details of places that are linked with this one')
        group.add_argument('--entrances', action='store_true',
                           help='Include a list of tagged entrance nodes')
        group.add_argument('--hierarchy', action='store_true',
                           help='Include details of places lower in the address hierarchy')
        group.add_argument('--group_hierarchy', action='store_true',
                           help='Group the places by type')
        group.add_argument('--polygon_geojson', action='store_true',
                           help='Include geometry of result')
        group.add_argument('--lang', '--accept-language', metavar='LANGS',
                           help='Preferred language order for presenting search results')
        _add_list_format(parser)

    def run(self, args: NominatimArgs) -> int:
        formatter = napi.load_format_dispatcher('v1', args.project_dir)

        if args.list_formats:
            return _list_formats(formatter, napi.DetailedResult)

        if args.format in ('debug', 'raw'):
            loglib.set_log_output('text')
        elif not formatter.supports_format(napi.DetailedResult, args.format):
            raise UsageError(f"Unsupported format '{args.format}'. "
                             'Use --list-formats to see supported formats.')

        place: napi.PlaceRef
        if args.node:
            place = napi.OsmID('N', args.node, args.object_class)
        elif args.way:
            place = napi.OsmID('W', args.way, args.object_class)
        elif args.relation:
            place = napi.OsmID('R', args.relation, args.object_class)
        elif args.place_id is not None:
            place = napi.PlaceID(args.place_id)
        else:
            raise UsageError('One of the arguments --node/-n --way/-w '
                             '--relation/-r --place_id/-p is required/')

        try:
            with napi.NominatimAPI(args.project_dir) as api:
                result = api.details(place,
                                     address_details=args.addressdetails,
                                     entrances=args.entrances,
                                     linked_places=args.linkedplaces,
                                     parented_places=args.hierarchy,
                                     keywords=args.keywords,
                                     geometry_output=(napi.GeometryFormat.GEOJSON
                                                      if args.polygon_geojson
                                                      else napi.GeometryFormat.NONE))
        except napi.UsageError as ex:
            raise UsageError(ex) from ex

        if result is not None:
            locales = _get_locales(args, api.config)
            locales.localize_results([result])

        if args.format == 'debug':
            print(loglib.get_and_disable())
            return 0

        if result:
            _print_output(formatter, result, args.format or 'json',
                          {'group_hierarchy': args.group_hierarchy})
            return 0

        LOG.error("Object not found in database.")
        return 42


class APIStatus:
    """
    Execute API status query.

    This command works exactly the same as if calling the /status endpoint on
    the web API. See the online documentation for more details on the
    various parameters:
    https://nominatim.org/release-docs/latest/api/Status/
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('API parameters')
        group.add_argument('--format', type=str, default='text',
                           help='Format of result (use --list-formats to see supported formats)')
        _add_list_format(parser)

    def run(self, args: NominatimArgs) -> int:
        formatter = napi.load_format_dispatcher('v1', args.project_dir)

        if args.list_formats:
            return _list_formats(formatter, napi.StatusResult)

        if args.format in ('debug', 'raw'):
            loglib.set_log_output('text')
        elif not formatter.supports_format(napi.StatusResult, args.format):
            raise UsageError(f"Unsupported format '{args.format}'. "
                             'Use --list-formats to see supported formats.')

        try:
            with napi.NominatimAPI(args.project_dir) as api:
                status = api.status()
        except napi.UsageError as ex:
            raise UsageError(ex) from ex

        if args.format == 'debug':
            print(loglib.get_and_disable())
            return 0

        _print_output(formatter, status, args.format, {})

        return 0


================================================
FILE: src/nominatim_db/clicmd/args.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Provides custom functions over command-line arguments.
"""
from typing import Optional, List, Dict, Any, Sequence, Tuple
import argparse
import logging
from pathlib import Path

from ..errors import UsageError
from ..config import Configuration
from ..typing import Protocol


LOG = logging.getLogger()


class Subcommand(Protocol):
    """
    Interface to be implemented by classes implementing a CLI subcommand.
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        """
        Fill the given parser for the subcommand with the appropriate
        parameters.
        """

    def run(self, args: 'NominatimArgs') -> int:
        """
        Run the subcommand with the given parsed arguments.
        """


class NominatimArgs:
    """ Customized namespace class for the nominatim command line tool
        to receive the command-line arguments.
    """
    # Basic environment set by root program.
    config: Configuration
    project_dir: Path

    # Global switches
    version: bool
    subcommand: Optional[str]
    command: Subcommand

    # Shared parameters
    osm2pgsql_cache: Optional[int]
    socket_timeout: int

    # Arguments added to all subcommands.
    verbose: int
    threads: Optional[int]

    # Arguments to 'add-data'
    file: Optional[str]
    diff: Optional[str]
    node: Optional[int]
    way: Optional[int]
    relation: Optional[int]
    tiger_data: Optional[str]
    use_main_api: bool

    # Arguments to 'admin'
    warm: bool
    check_database: bool
    migrate: bool
    collect_os_info: bool
    clean_deleted: str
    analyse_indexing: bool
    target: Optional[str]
    osm_id: Optional[str]
    place_id: Optional[int]

    # Arguments to 'import'
    osm_file: List[str]
    continue_at: Optional[str]
    reverse_only: bool
    no_partitions: bool
    no_updates: bool
    offline: bool
    ignore_errors: bool
    index_noanalyse: bool
    prepare_database: bool

    # Arguments to 'index'
    boundaries_only: bool
    no_boundaries: bool
    minrank: int
    maxrank: int

    # Arguments to 'export'
    output_type: str
    output_format: str
    output_all_postcodes: bool
    language: Optional[str]
    restrict_to_country: Optional[str]

    # Arguments to 'convert'
    output: Path

    # Arguments to 'refresh'
    postcodes: bool
    word_tokens: bool
    word_counts: bool
    address_levels: bool
    functions: bool
    wiki_data: bool
    secondary_importance: bool
    importance: bool
    website: bool
    diffs: bool
    enable_debug_statements: bool
    data_object: Sequence[Tuple[str, int]]
    data_area: Sequence[Tuple[str, int]]
    ro_access: bool
    postcode_force_reimport: bool

    # Arguments to 'replication'
    init: bool
    update_functions: bool
    check_for_updates: bool
    once: bool
    catch_up: bool
    do_index: bool

    # Arguments to 'serve'
    server: str
    engine: str

    # Arguments to 'special-phrases
    import_from_wiki: bool
    import_from_csv: Optional[str]
    no_replace: bool
    min: int

    # Arguments to all query functions
    format: str
    list_formats: bool
    addressdetails: bool
    entrances: bool
    extratags: bool
    namedetails: bool
    lang: Optional[str]
    polygon_output: Optional[str]
    polygon_threshold: Optional[float]

    # Arguments to 'search'
    query: Optional[str]
    amenity: Optional[str]
    street: Optional[str]
    city: Optional[str]
    county: Optional[str]
    state: Optional[str]
    country: Optional[str]
    postalcode: Optional[str]
    countrycodes: Optional[str]
    exclude_place_ids: Optional[str]
    limit: int
    viewbox: Optional[str]
    bounded: bool
    dedupe: bool

    # Arguments to 'reverse'
    lat: float
    lon: float
    zoom: Optional[int]
    layers: Optional[Sequence[str]]

    # Arguments to 'lookup'
    ids: Sequence[str]

    # Arguments to 'details'
    object_class: Optional[str]
    linkedplaces: bool
    hierarchy: bool
    keywords: bool
    polygon_geojson: bool
    group_hierarchy: bool

    def osm2pgsql_options(self, default_cache: int,
                          default_threads: int) -> Dict[str, Any]:
        """ Return the standard osm2pgsql options that can be derived
            from the command line arguments. The resulting dict can be
            further customized and then used in `run_osm2pgsql()`.
        """
        return dict(osm2pgsql=self.config.OSM2PGSQL_BINARY,
                    osm2pgsql_cache=self.osm2pgsql_cache or default_cache,
                    osm2pgsql_style=self.config.get_import_style_file(),
                    osm2pgsql_style_path=self.config.lib_dir.lua,
                    threads=self.threads or default_threads,
                    dsn=self.config.get_libpq_dsn(),
                    flatnode_file=str(self.config.get_path('FLATNODE_FILE') or ''),
                    tablespaces=dict(slim_data=self.config.TABLESPACE_OSM_DATA,
                                     slim_index=self.config.TABLESPACE_OSM_INDEX,
                                     main_data=self.config.TABLESPACE_PLACE_DATA,
                                     main_index=self.config.TABLESPACE_PLACE_INDEX
                                     )
                    )

    def get_osm_file_list(self) -> Optional[List[Path]]:
        """ Return the --osm-file argument as a list of Paths or None
            if no argument was given. The function also checks if the files
            exist and raises a UsageError if one cannot be found.
        """
        if not self.osm_file:
            return None

        files = [Path(f) for f in self.osm_file]
        for fname in files:
            if not fname.is_file():
                LOG.fatal("OSM file '%s' does not exist.", fname)
                raise UsageError('Cannot access file.')

        return files


================================================
FILE: src/nominatim_db/clicmd/convert.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of the 'convert' subcommand.
"""
from typing import Set, Any, Union, Optional, Sequence
import argparse
import asyncio
from pathlib import Path

from ..errors import UsageError
from .args import NominatimArgs


class WithAction(argparse.Action):
    """ Special action that saves a list of flags, given on the command-line
        as `--with-foo` or `--without-foo`.
    """
    def __init__(self, option_strings: Sequence[str], dest: Any,
                 default: bool = True, **kwargs: Any) -> None:
        if 'nargs' in kwargs:
            raise ValueError("nargs not allowed.")
        if option_strings is None:
            raise ValueError("Positional parameter not allowed.")

        self.dest_set = kwargs.pop('dest_set')
        full_option_strings = []
        for opt in option_strings:
            if not opt.startswith('--'):
                raise ValueError("short-form options not allowed")
            if default:
                self.dest_set.add(opt[2:])
            full_option_strings.append(f"--with-{opt[2:]}")
            full_option_strings.append(f"--without-{opt[2:]}")

        super().__init__(full_option_strings, argparse.SUPPRESS, nargs=0, **kwargs)

    def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace,
                 values: Union[str, Sequence[Any], None],
                 option_string: Optional[str] = None) -> None:
        assert option_string
        if option_string.startswith('--with-'):
            self.dest_set.add(option_string[7:])
        if option_string.startswith('--without-'):
            self.dest_set.discard(option_string[10:])


class ConvertDB:
    """ Convert an existing database into a different format. (EXPERIMENTAL)

        Dump a read-only version of the database in a different format.
        At the moment only a SQLite database suitable for reverse lookup
        can be created.
    """

    def __init__(self) -> None:
        self.options: Set[str] = set()

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        parser.add_argument('--format', default='sqlite',
                            choices=('sqlite', ),
                            help='Format of the output database (must be sqlite currently)')
        parser.add_argument('--output', '-o', required=True, type=Path,
                            help='File to write the database to.')
        group = parser.add_argument_group('Switches to define database layout'
                                          '(currently no effect)')
        group.add_argument('--reverse', action=WithAction, dest_set=self.options, default=True,
                           help='Enable/disable support for reverse and lookup API'
                                ' (default: enabled)')
        group.add_argument('--search', action=WithAction, dest_set=self.options, default=True,
                           help='Enable/disable support for search API (default: disabled)')
        group.add_argument('--details', action=WithAction, dest_set=self.options, default=True,
                           help='Enable/disable support for details API (default: enabled)')

    def run(self, args: NominatimArgs) -> int:
        if args.output.exists():
            raise UsageError(f"File '{args.output}' already exists. Refusing to overwrite.")

        if args.format == 'sqlite':
            from ..tools import convert_sqlite

            asyncio.run(convert_sqlite.convert(args.project_dir, args.output, self.options))
            return 0

        return 1


================================================
FILE: src/nominatim_db/clicmd/export.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of the 'export' subcommand.
"""
from typing import Optional, List, cast
import logging
import argparse
import asyncio
import csv
import sys

import nominatim_api as napi
from nominatim_api.results import create_from_placex_row, ReverseResult, add_result_details
from nominatim_api.types import LookupDetails

import sqlalchemy as sa

from ..errors import UsageError
from .args import NominatimArgs


LOG = logging.getLogger()


RANK_RANGE_MAP = {
  'country': (4, 4),
  'state': (5, 9),
  'county': (10, 12),
  'city': (13, 16),
  'suburb': (17, 21),
  'street': (26, 26),
  'path': (27, 27)
}


RANK_TO_OUTPUT_MAP = {
    4: 'country',
    5: 'state', 6: 'state', 7: 'state', 8: 'state', 9: 'state',
    10: 'county', 11: 'county', 12: 'county',
    13: 'city', 14: 'city', 15: 'city', 16: 'city',
    17: 'suburb', 18: 'suburb', 19: 'suburb', 20: 'suburb', 21: 'suburb',
    26: 'street', 27: 'path'}


class QueryExport:
    """\
    Export places as CSV file from the database.


    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Output arguments')
        group.add_argument('--output-type', default='street',
                           choices=('country', 'state', 'county',
                                    'city', 'suburb', 'street', 'path'),
                           help='Type of places to output (default: street)')
        group.add_argument('--output-format',
                           default='street;suburb;city;county;state;country',
                           help=("Semicolon-separated list of address types "
                                 "(see --output-type). Additionally accepts:"
                                 "placeid,postcode"))
        group.add_argument('--language',
                           help=("Preferred language for output "
                                 "(use local name, if omitted)"))
        group = parser.add_argument_group('Filter arguments')
        group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
                           help='Export only objects within country')
        group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
                           dest='node',
                           help='Export only children of this OSM node')
        group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
                           dest='way',
                           help='Export only children of this OSM way')
        group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
                           dest='relation',
                           help='Export only children of this OSM relation')

    def run(self, args: NominatimArgs) -> int:
        return asyncio.run(export(args))


async def export(args: NominatimArgs) -> int:
    """ The actual export as a asynchronous function.
    """

    api = napi.NominatimAPIAsync(args.project_dir)

    try:
        output_range = RANK_RANGE_MAP[args.output_type]

        writer = init_csv_writer(args.output_format)

        async with api.begin() as conn, api.begin() as detail_conn:
            t = conn.t.placex

            sql = sa.select(t.c.place_id, t.c.parent_place_id,
                            t.c.osm_type, t.c.osm_id, t.c.name,
                            t.c.class_, t.c.type, t.c.admin_level,
                            t.c.address, t.c.extratags,
                            t.c.housenumber, t.c.postcode, t.c.country_code,
                            t.c.importance, t.c.wikipedia, t.c.indexed_date,
                            t.c.rank_address, t.c.rank_search,
                            t.c.centroid)\
                    .where(t.c.linked_place_id == None)\
                    .where(t.c.rank_address.between(*output_range))

            parent_place_id = await get_parent_id(conn, args.node, args.way, args.relation)
            if parent_place_id:
                taddr = conn.t.addressline

                sql = sql.join(taddr, taddr.c.place_id == t.c.place_id)\
                         .where(taddr.c.address_place_id == parent_place_id)\
                         .where(taddr.c.isaddress)

            if args.restrict_to_country:
                sql = sql.where(t.c.country_code == args.restrict_to_country.lower())

            results = []
            for row in await conn.execute(sql):
                result = create_from_placex_row(row, ReverseResult)
                if result is not None:
                    results.append(result)

                if len(results) == 1000:
                    await dump_results(detail_conn, results, writer, args.language)
                    results = []

            if results:
                await dump_results(detail_conn, results, writer, args.language)
    finally:
        await api.close()

    return 0


def init_csv_writer(output_format: str) -> 'csv.DictWriter[str]':
    fields = output_format.split(';')
    writer = csv.DictWriter(sys.stdout, fieldnames=fields, extrasaction='ignore')
    writer.writeheader()

    return writer


async def dump_results(conn: napi.SearchConnection,
                       results: List[ReverseResult],
                       writer: 'csv.DictWriter[str]',
                       lang: Optional[str]) -> None:
    await add_result_details(conn, results,
                             LookupDetails(address_details=True))

    locale = napi.Locales([lang] if lang else None, conn.config.OUTPUT_NAMES)
    locale.localize_results(results)

    for result in results:
        data = {'placeid': result.place_id,
                'postcode': result.postcode}

        for line in (result.address_rows or []):
            if line.isaddress and line.local_name:
                if line.category[1] == 'postcode':
                    data['postcode'] = line.local_name
                elif line.rank_address in RANK_TO_OUTPUT_MAP:
                    data[RANK_TO_OUTPUT_MAP[line.rank_address]] = line.local_name

        writer.writerow(data)


async def get_parent_id(conn: napi.SearchConnection, node_id: Optional[int],
                        way_id: Optional[int],
                        relation_id: Optional[int]) -> Optional[int]:
    """ Get the place ID for the given OSM object.
    """
    if node_id is not None:
        osm_type, osm_id = 'N', node_id
    elif way_id is not None:
        osm_type, osm_id = 'W', way_id
    elif relation_id is not None:
        osm_type, osm_id = 'R', relation_id
    else:
        return None

    t = conn.t.placex
    sql = sa.select(t.c.place_id).limit(1)\
            .where(t.c.osm_type == osm_type)\
            .where(t.c.osm_id == osm_id)\
            .where(t.c.rank_address > 0)\
            .order_by(t.c.rank_address)

    for result in await conn.execute(sql):
        return cast(int, result[0])

    raise UsageError(f'Cannot find a place {osm_type}{osm_id}.')


================================================
FILE: src/nominatim_db/clicmd/freeze.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of the 'freeze' subcommand.
"""
import argparse

from ..db.connection import connect
from .args import NominatimArgs


class SetupFreeze:
    """\
    Make database read-only.

    About half of data in the Nominatim database is kept only to be able to
    keep the data up-to-date with new changes made in OpenStreetMap. This
    command drops all this data and only keeps the part needed for geocoding
    itself.

    This command has the same effect as the `--no-updates` option for imports.
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        pass  # No options

    def run(self, args: NominatimArgs) -> int:
        from ..tools import freeze

        with connect(args.config.get_libpq_dsn()) as conn:
            freeze.drop_update_tables(conn)
        freeze.drop_flatnode_file(args.config.get_path('FLATNODE_FILE'))

        return 0


================================================
FILE: src/nominatim_db/clicmd/index.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of the 'index' subcommand.
"""
import argparse
import asyncio

import psutil

from ..db import status
from ..db.connection import connect
from .args import NominatimArgs


class UpdateIndex:
    """\
    Reindex all new and modified data.

    Indexing is the process of computing the address and search terms for
    the places in the database. Every time data is added or changed, indexing
    needs to be run. Imports and replication updates automatically take care
    of indexing. For other cases, this function allows to run indexing manually.
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Filter arguments')
        group.add_argument('--boundaries-only', action='store_true',
                           help="""Index only administrative boundaries.""")
        group.add_argument('--no-boundaries', action='store_true',
                           help="""Index everything except administrative boundaries.""")
        group.add_argument('--minrank', '-r', type=int, metavar='RANK', default=0,
                           help='Minimum/starting rank')
        group.add_argument('--maxrank', '-R', type=int, metavar='RANK', default=30,
                           help='Maximum/finishing rank')

    def run(self, args: NominatimArgs) -> int:
        asyncio.run(self._do_index(args))

        if not args.no_boundaries and not args.boundaries_only \
           and args.minrank == 0 and args.maxrank == 30:
            with connect(args.config.get_libpq_dsn()) as conn:
                status.set_indexed(conn, True)

        return 0

    async def _do_index(self, args: NominatimArgs) -> None:
        from ..tokenizer import factory as tokenizer_factory

        tokenizer = tokenizer_factory.get_tokenizer_for_db(args.config)
        from ..indexer.indexer import Indexer

        indexer = Indexer(args.config.get_libpq_dsn(), tokenizer,
                          args.threads or psutil.cpu_count() or 1)

        has_pending = True  # run at least once
        while has_pending:
            if not args.no_boundaries:
                await indexer.index_boundaries(args.minrank, args.maxrank)
            if not args.boundaries_only:
                await indexer.index_by_rank(args.minrank, args.maxrank)
                await indexer.index_postcodes()
            has_pending = indexer.has_pending(args.minrank, args.maxrank)


================================================
FILE: src/nominatim_db/clicmd/refresh.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of 'refresh' subcommand.
"""
from typing import Tuple, Optional
import argparse
import logging
from pathlib import Path
import asyncio

from ..config import Configuration
from ..db.connection import connect, table_exists
from ..tokenizer.base import AbstractTokenizer
from .args import NominatimArgs


LOG = logging.getLogger()


def _parse_osm_object(obj: str) -> Tuple[str, int]:
    """ Parse the given argument into a tuple of OSM type and ID.
        Raises an ArgumentError if the format is not recognized.
    """
    if len(obj) < 2 or obj[0].lower() not in 'nrw' or not obj[1:].isdigit():
        raise argparse.ArgumentTypeError("Cannot parse OSM ID. Expect format: [N|W|R].")

    return (obj[0].upper(), int(obj[1:]))


class UpdateRefresh:
    """\
    Recompute auxiliary data used by the indexing process.

    This sub-commands updates various static data and functions in the database.
    It usually needs to be run after changing various aspects of the
    configuration. The configuration documentation will mention the exact
    command to use in such case.

    Warning: the 'update' command must not be run in parallel with other update
             commands like 'replication' or 'add-data'.
    """
    def __init__(self) -> None:
        self.tokenizer: Optional[AbstractTokenizer] = None

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Data arguments')
        group.add_argument('--postcodes', action='store_true',
                           help='Update postcode centroid table')
        group.add_argument('--word-tokens', action='store_true',
                           help='Clean up search terms')
        group.add_argument('--word-counts', action='store_true',
                           help='Compute frequency of full-word search terms')
        group.add_argument('--address-levels', action='store_true',
                           help='Reimport address level configuration')
        group.add_argument('--functions', action='store_true',
                           help='Update the PL/pgSQL functions in the database')
        group.add_argument('--wiki-data', action='store_true',
                           help='Update Wikipedia/data importance numbers')
        group.add_argument('--secondary-importance', action='store_true',
                           help='Update secondary importance raster data')
        group.add_argument('--importance', action='store_true',
                           help='Recompute place importances (expensive!)')
        group.add_argument('--ro-access', action='store_true',
                           help='Grant read-only access to web user for all tables')
        group.add_argument('--website', action='store_true',
                           help='DEPRECATED. This function has no function anymore'
                                ' and will be removed in a future version.')
        group.add_argument('--data-object', action='append',
                           type=_parse_osm_object, metavar='OBJECT',
                           help='Mark the given OSM object as requiring an update'
                                ' (format: [NWR])')
        group.add_argument('--data-area', action='append',
                           type=_parse_osm_object, metavar='OBJECT',
                           help='Mark the area around the given OSM object as requiring an update'
                                ' (format: [NWR])')

        group = parser.add_argument_group('Arguments for function refresh')
        group.add_argument('--no-diff-updates', action='store_false', dest='diffs',
                           help='Do not enable code for propagating updates')
        group.add_argument('--enable-debug-statements', action='store_true',
                           help='Enable debug warning statements in functions')
        group = parser.add_argument_group('Arguments for postcode refresh')
        group.add_argument('--force-reimport', action='store_true',
                           dest='postcode_force_reimport',
                           help='Recompute the postcodes from scratch instead of updating')

    def run(self, args: NominatimArgs) -> int:
        from ..tools import refresh, postcodes
        from ..indexer.indexer import Indexer

        need_function_refresh = args.functions

        if args.postcodes:
            if postcodes.can_compute(args.config.get_libpq_dsn()):
                LOG.warning("Update postcodes centroid")
                tokenizer = self._get_tokenizer(args.config)
                postcodes.update_postcodes(args.config.get_libpq_dsn(),
                                           args.project_dir, tokenizer,
                                           force_reimport=args.postcode_force_reimport)
                indexer = Indexer(args.config.get_libpq_dsn(), tokenizer,
                                  args.threads or 1)
                asyncio.run(indexer.index_postcodes())
            else:
                LOG.error("The place table doesn't exist. "
                          "Postcode updates on a frozen database is not possible.")

        if args.word_tokens:
            LOG.warning('Updating word tokens')
            tokenizer = self._get_tokenizer(args.config)
            tokenizer.update_word_tokens()

        if args.word_counts:
            LOG.warning('Recompute word statistics')
            self._get_tokenizer(args.config).update_statistics(args.config,
                                                               threads=args.threads or 1)

        if args.address_levels:
            LOG.warning('Updating address levels')
            with connect(args.config.get_libpq_dsn()) as conn:
                refresh.load_address_levels_from_config(conn, args.config)

        # Attention: must come BEFORE functions
        if args.secondary_importance:
            with connect(args.config.get_libpq_dsn()) as conn:
                # If the table did not exist before, then the importance code
                # needs to be enabled.
                if not table_exists(conn, 'secondary_importance'):
                    args.functions = True

            LOG.warning('Import secondary importance raster data from %s', args.project_dir)
            if refresh.import_secondary_importance(args.config.get_libpq_dsn(),
                                                   args.project_dir) > 0:
                LOG.fatal('FATAL: Cannot update secondary importance raster data')
                return 1
            need_function_refresh = True

        if args.wiki_data:
            data_path = Path(args.config.WIKIPEDIA_DATA_PATH
                             or args.project_dir)
            LOG.warning('Import wikipedia article importance from %s', data_path)
            if refresh.import_wikipedia_articles(args.config.get_libpq_dsn(),
                                                 data_path) > 0:
                LOG.fatal('FATAL: Wikipedia importance file not found in %s', data_path)
                return 1
            need_function_refresh = True

        if need_function_refresh:
            LOG.warning('Create functions')
            with connect(args.config.get_libpq_dsn()) as conn:
                refresh.create_functions(conn, args.config,
                                         args.diffs, args.enable_debug_statements)
                self._get_tokenizer(args.config).update_sql_functions(args.config)

        # Attention: importance MUST come after wiki data import and after functions.
        if args.importance:
            LOG.warning('Update importance values for database')
            with connect(args.config.get_libpq_dsn()) as conn:
                refresh.recompute_importance(conn)

        if args.website:
            LOG.error('WARNING: Website setup is no longer required. '
                      'This function will be removed in future version of Nominatim.')

        if args.ro_access:
            from ..tools import admin
            LOG.warning('Grant read-only access to web user')
            admin.grant_ro_access(args.config.get_libpq_dsn(), args.config)

        if args.data_object or args.data_area:
            with connect(args.config.get_libpq_dsn()) as conn:
                for obj in args.data_object or []:
                    refresh.invalidate_osm_object(*obj, conn, recursive=False)
                for obj in args.data_area or []:
                    refresh.invalidate_osm_object(*obj, conn, recursive=True)
                conn.commit()

        return 0

    def _get_tokenizer(self, config: Configuration) -> AbstractTokenizer:
        if self.tokenizer is None:
            from ..tokenizer import factory as tokenizer_factory

            self.tokenizer = tokenizer_factory.get_tokenizer_for_db(config)

        return self.tokenizer


================================================
FILE: src/nominatim_db/clicmd/replication.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of the 'replication' sub-command.
"""
from typing import Optional
import argparse
import datetime as dt
import logging
import socket
import time
import asyncio

from ..db import status
from ..db.connection import connect
from ..errors import UsageError
from .args import NominatimArgs

LOG = logging.getLogger()


class UpdateReplication:
    """\
    Update the database using an online replication service.

    An OSM replication service is an online service that provides regular
    updates (OSM diff files) for the planet or update they provide. The OSMF
    provides the primary replication service for the full planet at
    https://planet.osm.org/replication/ but there are other providers of
    extracts of OSM data who provide such a service as well.

    This sub-command allows to set up such a replication service and download
    and import updates at regular intervals. You need to call '--init' once to
    set up the process or whenever you change the replication configuration
    parameters. Without any arguments, the sub-command will go into a loop and
    continuously apply updates as they become available. Giving `--once` just
    downloads and imports the next batch of updates.
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Arguments for initialisation')
        group.add_argument('--init', action='store_true',
                           help='Initialise the update process')
        group.add_argument('--no-update-functions', dest='update_functions',
                           action='store_false',
                           help="Do not update the trigger function to "
                                "support differential updates (EXPERT)")
        group = parser.add_argument_group('Arguments for updates')
        group.add_argument('--check-for-updates', action='store_true',
                           help='Check if new updates are available and exit')
        group.add_argument('--once', action='store_true',
                           help="Download and apply updates only once. When "
                                "not set, updates are continuously applied")
        group.add_argument('--catch-up', action='store_true',
                           help="Download and apply updates until no new "
                                "data is available on the server")
        group.add_argument('--no-index', action='store_false', dest='do_index',
                           help=("Do not index the new data. Only usable "
                                 "together with --once"))
        group.add_argument('--osm2pgsql-cache', metavar='SIZE', type=int,
                           help='Size of cache to be used by osm2pgsql (in MB)')
        group = parser.add_argument_group('Download parameters')
        group.add_argument('--socket-timeout', dest='socket_timeout', type=int, default=60,
                           help='Set timeout for file downloads')

    def _init_replication(self, args: NominatimArgs) -> int:
        from ..tools import replication, refresh

        LOG.warning("Initialising replication updates")
        with connect(args.config.get_libpq_dsn()) as conn:
            replication.init_replication(conn, base_url=args.config.REPLICATION_URL,
                                         socket_timeout=args.socket_timeout)
            if args.update_functions:
                LOG.warning("Create functions")
                refresh.create_functions(conn, args.config, True, False)
        return 0

    def _check_for_updates(self, args: NominatimArgs) -> int:
        from ..tools import replication

        with connect(args.config.get_libpq_dsn()) as conn:
            return replication.check_for_updates(conn, base_url=args.config.REPLICATION_URL,
                                                 socket_timeout=args.socket_timeout)

    def _report_update(self, batchdate: dt.datetime,
                       start_import: dt.datetime,
                       start_index: Optional[dt.datetime]) -> None:
        def round_time(delta: dt.timedelta) -> dt.timedelta:
            return dt.timedelta(seconds=int(delta.total_seconds()))

        end = dt.datetime.now(dt.timezone.utc)
        LOG.warning("Update completed. Import: %s. %sTotal: %s. Remaining backlog: %s.",
                    round_time((start_index or end) - start_import),
                    f"Indexing: {round_time(end - start_index)} " if start_index else '',
                    round_time(end - start_import),
                    round_time(end - batchdate))

    def _compute_update_interval(self, args: NominatimArgs) -> int:
        if args.catch_up:
            return 0

        update_interval = args.config.get_int('REPLICATION_UPDATE_INTERVAL')
        # Sanity check to not overwhelm the Geofabrik servers.
        if 'download.geofabrik.de' in args.config.REPLICATION_URL\
           and update_interval < 86400:
            LOG.fatal("Update interval too low for download.geofabrik.de.\n"
                      "Please check install documentation "
                      "(https://nominatim.org/release-docs/latest/admin/Update/#"
                      "setting-up-the-update-process).")
            raise UsageError("Invalid replication update interval setting.")

        return update_interval

    async def _update(self, args: NominatimArgs) -> None:
        from ..tools import replication
        from ..indexer.indexer import Indexer
        from ..tokenizer import factory as tokenizer_factory

        update_interval = self._compute_update_interval(args)

        params = args.osm2pgsql_options(default_cache=2000, default_threads=1)
        params.update(base_url=args.config.REPLICATION_URL,
                      update_interval=update_interval,
                      import_file=args.project_dir / 'osmosischange.osc',
                      max_diff_size=args.config.get_int('REPLICATION_MAX_DIFF'),
                      indexed_only=not args.once)

        if not args.once:
            if not args.do_index:
                LOG.fatal("Indexing cannot be disabled when running updates continuously.")
                raise UsageError("Bad argument '--no-index'.")
        recheck_interval = args.config.get_int('REPLICATION_RECHECK_INTERVAL')

        tokenizer = tokenizer_factory.get_tokenizer_for_db(args.config)
        indexer = Indexer(args.config.get_libpq_dsn(), tokenizer, args.threads or 1)

        dsn = args.config.get_libpq_dsn()

        while True:
            start = dt.datetime.now(dt.timezone.utc)
            state = replication.update(dsn, params, socket_timeout=args.socket_timeout)

            with connect(dsn) as conn:
                if state is not replication.UpdateState.NO_CHANGES:
                    status.log_status(conn, start, 'import')
                batchdate, _, _ = status.get_status(conn)
                conn.commit()

            if state is not replication.UpdateState.NO_CHANGES and args.do_index:
                index_start = dt.datetime.now(dt.timezone.utc)
                await indexer.index_full(analyse=False)

                with connect(dsn) as conn:
                    status.set_indexed(conn, True)
                    status.log_status(conn, index_start, 'index')
                    conn.commit()
            else:
                index_start = None

            if state is replication.UpdateState.NO_CHANGES and \
               args.catch_up or update_interval > 40*60:
                await indexer.index_full(analyse=False)

            if LOG.isEnabledFor(logging.WARNING):
                assert batchdate is not None
                self._report_update(batchdate, start, index_start)

            if args.once or (args.catch_up and state is replication.UpdateState.NO_CHANGES):
                break

            if state is replication.UpdateState.NO_CHANGES:
                LOG.warning("No new changes. Sleeping for %d sec.", recheck_interval)
                time.sleep(recheck_interval)

    def run(self, args: NominatimArgs) -> int:
        socket.setdefaulttimeout(args.socket_timeout)

        if args.init:
            return self._init_replication(args)

        if args.check_for_updates:
            return self._check_for_updates(args)

        asyncio.run(self._update(args))
        return 0


================================================
FILE: src/nominatim_db/clicmd/setup.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of the 'import' subcommand.
"""
from typing import Optional
import argparse
import logging
from pathlib import Path
import asyncio

import psutil

from ..errors import UsageError
from ..config import Configuration
from ..db.connection import connect
from ..db import status, properties
from ..tokenizer.base import AbstractTokenizer
from ..version import NOMINATIM_VERSION
from .args import NominatimArgs

import time

LOG = logging.getLogger()


class SetupAll:
    """\
    Create a new Nominatim database from an OSM file.

    This sub-command sets up a new Nominatim database from scratch starting
    with creating a new database in Postgresql. The user running this command
    needs superuser rights on the database.
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group1 = parser.add_argument_group('Required arguments')
        group1.add_argument('--osm-file', metavar='FILE', action='append',
                            help='OSM file to be imported'
                                 ' (repeat for importing multiple files)',
                            default=None)
        group1.add_argument('--continue', dest='continue_at',
                            choices=['import-from-file', 'load-data', 'indexing', 'db-postprocess'],
                            help='Continue an import that was interrupted',
                            default=None)
        group2 = parser.add_argument_group('Optional arguments')
        group2.add_argument('--osm2pgsql-cache', metavar='SIZE', type=int,
                            help='Size of cache to be used by osm2pgsql (in MB)')
        group2.add_argument('--reverse-only', action='store_true',
                            help='Do not create tables and indexes for searching')
        group2.add_argument('--no-partitions', action='store_true',
                            help="Do not partition search indices "
                                 "(speeds up import of single country extracts)")
        group2.add_argument('--no-updates', action='store_true',
                            help="Do not keep tables that are only needed for "
                                 "updating the database later")
        group2.add_argument('--offline', action='store_true',
                            help="Do not attempt to load any additional data from the internet")
        group3 = parser.add_argument_group('Expert options')
        group3.add_argument('--ignore-errors', action='store_true',
                            help='Continue import even when errors in SQL are present')
        group3.add_argument('--index-noanalyse', action='store_true',
                            help='Do not perform analyse operations during index (expert only)')
        group3.add_argument('--prepare-database', action='store_true',
                            help='Create the database but do not import any data')

    def run(self, args: NominatimArgs) -> int:
        if args.osm_file is None and args.continue_at is None and not args.prepare_database:
            raise UsageError("No input files (use --osm-file).")

        if args.osm_file is not None and args.continue_at not in ('import-from-file', None):
            raise UsageError(f"Cannot use --continue {args.continue_at} and --osm-file together.")

        if args.continue_at is not None and args.prepare_database:
            raise UsageError(
                "Cannot use --continue and --prepare-database together."
            )

        return asyncio.run(self.async_run(args))

    async def async_run(self, args: NominatimArgs) -> int:
        from ..data import country_info
        from ..tools import database_import, postcodes, freeze
        from ..indexer.indexer import Indexer

        start_time = time.time()

        num_threads = args.threads or psutil.cpu_count() or 1
        country_info.setup_country_config(args.config)

        if args.prepare_database or args.continue_at is None:
            LOG.warning('Creating database')
            database_import.setup_database_skeleton(args.config.get_libpq_dsn(),
                                                    rouser=args.config.DATABASE_WEBUSER)
            if args.prepare_database:
                return 0

        if args.continue_at in (None, 'import-from-file'):
            self._base_import(args)

        if args.continue_at in ('import-from-file', 'load-data', None):
            LOG.warning('Initialise tables')
            with connect(args.config.get_libpq_dsn()) as conn:
                database_import.truncate_data_tables(conn)

            LOG.warning('Load data into placex table')
            await database_import.load_data(args.config.get_libpq_dsn(), num_threads)

        LOG.warning("Setting up tokenizer")
        tokenizer = self._get_tokenizer(args.continue_at, args.config)

        if args.continue_at in ('import-from-file', 'load-data', None):
            LOG.warning('Calculate postcodes')
            postcodes.update_postcodes(args.config.get_libpq_dsn(),
                                       args.project_dir, tokenizer)

        if args.continue_at in ('import-from-file', 'load-data', 'indexing', None):
            LOG.warning('Indexing places')
            indexer = Indexer(args.config.get_libpq_dsn(), tokenizer, num_threads)
            await indexer.index_full(analyse=not args.index_noanalyse)

        LOG.warning('Post-process tables')
        with connect(args.config.get_libpq_dsn()) as conn:
            conn.autocommit = True
            await database_import.create_search_indices(conn, args.config,
                                                        drop=args.no_updates,
                                                        threads=num_threads)
            LOG.warning('Create search index for default country names.')
            conn.autocommit = False
            country_info.create_country_names(conn, tokenizer,
                                              args.config.get_str_list('LANGUAGES'))
            if args.no_updates:
                conn.autocommit = True
                freeze.drop_update_tables(conn)
        tokenizer.finalize_import(args.config)

        LOG.warning('Recompute word counts')
        tokenizer.update_statistics(args.config, threads=num_threads)

        end_time = time.time()
        elapsed = end_time - start_time
        LOG.warning(f'Import completed successfully in {elapsed:.2f} seconds.')

        self._finalize_database(args.config.get_libpq_dsn(), args.offline)

        return 0

    def _base_import(self, args: NominatimArgs) -> None:
        from ..tools import database_import, refresh
        from ..data import country_info

        files = args.get_osm_file_list()
        if not files:
            raise UsageError("No input files (use --osm-file).")

        if args.continue_at in ('import-from-file', None):
            # Check if the correct plugins are installed
            database_import.check_existing_database_plugins(args.config.get_libpq_dsn())
            LOG.warning('Setting up country tables')
            country_info.setup_country_tables(args.config.get_libpq_dsn(),
                                              args.config.lib_dir.data,
                                              args.no_partitions)

            LOG.warning('Importing OSM data file')
            database_import.import_osm_data(files,
                                            args.osm2pgsql_options(0, 1),
                                            drop=args.no_updates,
                                            ignore_errors=args.ignore_errors)

            LOG.warning('Importing wikipedia importance data')
            data_path = Path(args.config.WIKIPEDIA_DATA_PATH or args.project_dir)
            if refresh.import_wikipedia_articles(args.config.get_libpq_dsn(),
                                                 data_path) > 0:
                LOG.error('Wikipedia importance dump file not found. '
                          'Calculating importance values of locations will not '
                          'use Wikipedia importance data.')

            LOG.warning('Importing secondary importance raster data')
            if refresh.import_secondary_importance(args.config.get_libpq_dsn(),
                                                   args.project_dir) != 0:
                LOG.error('Secondary importance file not imported. '
                          'Falling back to default ranking.')

            self._setup_tables(args.config, args.reverse_only)

    def _setup_tables(self, config: Configuration, reverse_only: bool) -> None:
        """ Set up the basic database layout: tables, indexes and functions.
        """
        from ..tools import database_import, refresh

        with connect(config.get_libpq_dsn()) as conn:
            conn.autocommit = True
            LOG.warning('Create functions (1st pass)')
            refresh.create_functions(conn, config, False, False)
            LOG.warning('Create tables')
            database_import.create_tables(conn, config, reverse_only=reverse_only)
            refresh.load_address_levels_from_config(conn, config)
            LOG.warning('Create functions (2nd pass)')
            refresh.create_functions(conn, config, False, False)
            LOG.warning('Create table triggers')
            database_import.create_table_triggers(conn, config)
            LOG.warning('Create partition tables')
            database_import.create_partition_tables(conn, config)
            LOG.warning('Create functions (3rd pass)')
            refresh.create_functions(conn, config, False, False)

    def _get_tokenizer(self, continue_at: Optional[str],
                       config: Configuration) -> AbstractTokenizer:
        """ Set up a new tokenizer or load an already initialised one.
        """
        from ..tokenizer import factory as tokenizer_factory

        if continue_at in ('import-from-file', 'load-data', None):
            # (re)initialise the tokenizer data
            return tokenizer_factory.create_tokenizer(config)

        # just load the tokenizer
        return tokenizer_factory.get_tokenizer_for_db(config)

    def _finalize_database(self, dsn: str, offline: bool) -> None:
        """ Determine the database date and set the status accordingly.
        """
        with connect(dsn) as conn:
            properties.set_property(conn, 'database_version', str(NOMINATIM_VERSION))

            try:
                dbdate = status.compute_database_date(conn, offline)
                status.set_status(conn, dbdate)
                LOG.info('Database is at %s.', dbdate)
            except Exception as exc:
                LOG.error('Cannot determine date of database: %s', exc)


================================================
FILE: src/nominatim_db/clicmd/special_phrases.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
    Implementation of the 'special-phrases' command.
"""
import argparse
import logging
from pathlib import Path

from ..errors import UsageError
from ..db.connection import connect
from ..tools.special_phrases.sp_importer import SPImporter, SpecialPhraseLoader
from ..tools.special_phrases.sp_wiki_loader import SPWikiLoader
from ..tools.special_phrases.sp_csv_loader import SPCsvLoader
from .args import NominatimArgs


LOG = logging.getLogger()


class ImportSpecialPhrases:
    """\
    Import special phrases.

    Special phrases are search terms that narrow down the type of object
    that should be searched. For example, you might want to search for
    'Hotels in Barcelona'. The OSM wiki has a selection of special phrases
    in many languages, which can be imported with this command.

    You can also provide your own phrases in a CSV file. The file needs to have
    the following five columns:
     * phrase - the term expected for searching
     * class - the OSM tag key of the object type
     * type - the OSM tag value of the object type
     * operator - the kind of search to be done (one of: in, near, name, -)
     * plural - whether the term is a plural or not (Y/N)

    An example file can be found in the Nominatim sources at
    'test/testdb/full_en_phrases_test.csv'.

    The import can be further configured to ignore specific key/value pairs.
    This is particularly useful when importing phrases from the wiki. The
    default configuration excludes some very common tags like building=yes.
    The configuration can be customized by putting a file `phrase-settings.json`
    with custom rules into the project directory or by using the `--config`
    option to point to another configuration file.
    """

    def add_args(self, parser: argparse.ArgumentParser) -> None:
        group = parser.add_argument_group('Input arguments')
        group.add_argument('--import-from-wiki', action='store_true',
                           help='Import special phrases from the OSM wiki to the database')
        group.add_argument('--import-from-csv', metavar='FILE',
                           help='Import special phrases from a CSV file')
        group.add_argument('--no-replace', action='store_true',
                           help='Keep the old phrases and only add the new ones')
        group.add_argument('--min', type=int, default=0,
                           help='Restrict special phrases by minimum occurance')

    def run(self, args: NominatimArgs) -> int:

        if args.import_from_wiki:
            self.start_import(args, SPWikiLoader(args.config))

        if args.import_from_csv:
            if not Path(args.import_from_csv).is_file():
                LOG.fatal("CSV file '%s' does not exist.", args.import_from_csv)
                raise UsageError('Cannot access file.')

            self.start_import(args, SPCsvLoader(args.import_from_csv))

        return 0

    def start_import(self, args: NominatimArgs, loader: SpecialPhraseLoader) -> None:
        """
            Create the SPImporter object containing the right
            sp loader and then start the import of special phrases.
        """
        from ..tokenizer import factory as tokenizer_factory

        tokenizer = tokenizer_factory.get_tokenizer_for_db(args.config)
        should_replace = not args.no_replace
        min = args.min

        with connect(args.config.get_libpq_dsn()) as db_connection:
            SPImporter(
                args.config, db_connection, loader
            ).import_phrases(tokenizer, should_replace, min)


================================================
FILE: src/nominatim_db/config.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Nominatim configuration accessor.
"""
from typing import Union, Dict, Any, List, Mapping, Optional
import importlib.util
import logging
import os
import sys
import re
from pathlib import Path
import json
import yaml

from dotenv import dotenv_values

from psycopg.conninfo import conninfo_to_dict

from .typing import StrPath
from .errors import UsageError
from . import paths

LOG = logging.getLogger()
CONFIG_CACHE: Dict[str, Any] = {}


def flatten_config_list(content: Any, section: str = '') -> List[Any]:
    """ Flatten YAML configuration lists that contain include sections
        which are lists themselves.
    """
    if not content:
        return []

    if not isinstance(content, list):
        raise UsageError(f"List expected in section '{section}'.")

    output = []
    for ele in content:
        if isinstance(ele, list):
            output.extend(flatten_config_list(ele, section))
        else:
            output.append(ele)

    return output


class Configuration:
    """ This class wraps access to the configuration settings
        for the Nominatim instance in use.

        All Nominatim configuration options are prefixed with 'NOMINATIM_' to
        avoid conflicts with other environment variables. All settings can
        be accessed as properties of the class under the same name as the
        setting but with the `NOMINATIM_` prefix removed. In addition, there
        are accessor functions that convert the setting values to types
        other than string.
    """

    def __init__(self, project_dir: Optional[Union[Path, str]],
                 environ: Optional[Mapping[str, str]] = None) -> None:
        self.environ = os.environ if environ is None else environ
        self.config_dir = paths.CONFIG_DIR
        self._config = dotenv_values(str(self.config_dir / 'env.defaults'))
        if project_dir is not None:
            self.project_dir: Optional[Path] = Path(project_dir).resolve()
            if (self.project_dir / '.env').is_file():
                self._config.update(dotenv_values(str(self.project_dir / '.env')))
        else:
            self.project_dir = None

        class _LibDirs:
            sql = paths.SQLLIB_DIR
            lua = paths.LUALIB_DIR
            data = paths.DATA_DIR

        self.lib_dir = _LibDirs()
        self._private_plugins: Dict[str, object] = {}

        if re.fullmatch(r'[\w-]+', self.DATABASE_WEBUSER) is None:
            raise UsageError("Misconfigured DATABASE_WEBUSER. "
                             "Only alphnumberic characters, - and _ are allowed.")

    def set_libdirs(self, **kwargs: StrPath) -> None:
        """ Set paths to library functions and data.
        """
        for key, value in kwargs.items():
            setattr(self.lib_dir, key, None if value is None else Path(value))

    def __getattr__(self, name: str) -> str:
        name = 'NOMINATIM_' + name

        if name in self.environ:
            return self.environ[name]

        return self._config[name] or ''

    def get_bool(self, name: str) -> bool:
        """ Return the given configuration parameter as a boolean.

            Parameters:
              name: Name of the configuration parameter with the NOMINATIM_
                prefix removed.

            Returns:
              `True` for values of '1', 'yes' and 'true', `False` otherwise.
        """
        return getattr(self, name).lower() in ('1', 'yes', 'true')

    def get_int(self, name: str) -> int:
        """ Return the given configuration parameter as an int.

            Parameters:
              name: Name of the configuration parameter with the NOMINATIM_
                prefix removed.

            Returns:
              The configuration value converted to int.

            Raises:
              ValueError: when the value is not a number.
        """
        try:
            return int(getattr(self, name))
        except ValueError as exp:
            LOG.fatal("Invalid setting NOMINATIM_%s. Needs to be a number.", name)
            raise UsageError("Configuration error.") from exp

    def get_str_list(self, name: str) -> Optional[List[str]]:
        """ Return the given configuration parameter as a list of strings.
            The values are assumed to be given as a comma-sparated list and
            will be stripped before returning them.

            Parameters:
              name: Name of the configuration parameter with the NOMINATIM_
                prefix removed.

            Returns:
              (List[str]): The comma-split parameter as a list. The
                elements are stripped of leading and final spaces before
                being returned.
              (None): The configuration parameter was unset or empty.
        """
        raw = getattr(self, name)

        return [v.strip() for v in raw.split(',')] if raw else None

    def get_path(self, name: str) -> Optional[Path]:
        """ Return the given configuration parameter as a Path.

            Parameters:
              name: Name of the configuration parameter with the NOMINATIM_
                prefix removed.

            Returns:
              (Path): A Path object of the parameter value.
                  If a relative path is configured, then the function converts this
                  into an absolute path with the project directory as root path.
              (None): The configuration parameter was unset or empty.
        """
        value = getattr(self, name)
        if not value:
            return None

        cfgpath = Path(value)

        if not cfgpath.is_absolute():
            assert self.project_dir is not None
            cfgpath = self.project_dir / cfgpath

        return cfgpath.resolve()

    def get_libpq_dsn(self) -> str:
        """ Get configured database DSN converted into the key/value format
            understood by libpq and psycopg.
        """
        dsn = self.DATABASE_DSN

        def quote_param(param: str) -> str:
            key, val = param.split('=')
            val = val.replace('\\', '\\\\').replace("'", "\\'")
            if ' ' in val:
                val = "'" + val + "'"
            return key + '=' + val

        if dsn.startswith('pgsql:'):
            # Old PHP DSN format. Convert before returning.
            return ' '.join([quote_param(p) for p in dsn[6:].split(';')])

        return dsn

    def get_database_params(self) -> Mapping[str, Union[str, int, None]]:
        """ Get the configured parameters for the database connection
            as a mapping.
        """
        dsn = self.DATABASE_DSN

        if dsn.startswith('pgsql:'):
            return dict((p.split('=', 1) for p in dsn[6:].split(';')))

        return conninfo_to_dict(dsn)  # type: ignore

    def get_import_style_file(self) -> Path:
        """ Return the import style file as a path object. Translates the
            name of the standard styles automatically into a file in the
            config style.
        """
        style = getattr(self, 'IMPORT_STYLE')

        if style in ('admin', 'street', 'address', 'full', 'extratags'):
            return self.lib_dir.lua / f'import-{style}.lua'

        return self.find_config_file('', 'IMPORT_STYLE')

    def get_os_env(self) -> Dict[str, str]:
        """ Return a copy of the OS environment with the Nominatim configuration
            merged in.
        """
        env = {k: v for k, v in self._config.items() if v is not None}
        env.update(self.environ)

        return env

    def load_sub_configuration(self, filename: StrPath,
                               config: Optional[str] = None) -> Any:
        """ Load additional configuration from a file. `filename` is the name
            of the configuration file. The file is first searched in the
            project directory and then in the global settings directory.

            If `config` is set, then the name of the configuration file can
            be additionally given through a .env configuration option. When
            the option is set, then the file will be exclusively loaded as set:
            if the name is an absolute path, the file name is taken as is,
            if the name is relative, it is taken to be relative to the
            project directory.

            The format of the file is determined from the filename suffix.
            Currently only files with extension '.yaml' are supported.

            YAML files support a special '!include' construct. When the
            directive is given, the value is taken to be a filename, the file
            is loaded using this function and added at the position in the
            configuration tree.
        """
        configfile = self.find_config_file(filename, config)

        if str(configfile) in CONFIG_CACHE:
            return CONFIG_CACHE[str(configfile)]

        if configfile.suffix in ('.yaml', '.yml'):
            result = self._load_from_yaml(configfile)
        elif configfile.suffix == '.json':
            with configfile.open('r', encoding='utf-8') as cfg:
                result = json.load(cfg)
        else:
            raise UsageError(f"Config file '{configfile}' has unknown format.")

        CONFIG_CACHE[str(configfile)] = result
        return result

    def load_plugin_module(self, module_name: str, internal_path: str) -> Any:
        """ Load a Python module as a plugin.

            The module_name may have three variants:

            * A name without any '.' is assumed to be an internal module
              and will be searched relative to `internal_path`.
            * If the name ends in `.py`, module_name is assumed to be a
              file name relative to the project directory.
            * Any other name is assumed to be an absolute module name.

            In either of the variants the module name must start with a letter.
        """
        if not module_name or not module_name[0].isidentifier():
            raise UsageError(f'Invalid module name {module_name}')

        if '.' not in module_name:
            module_name = module_name.replace('-', '_')
            full_module = f'{internal_path}.{module_name}'
            return sys.modules.get(full_module) or importlib.import_module(full_module)

        if module_name.endswith('.py'):
            if self.project_dir is None or not (self.project_dir / module_name).exists():
                raise UsageError(f"Cannot find module '{module_name}' in project directory.")

            if module_name in self._private_plugins:
                return self._private_plugins[module_name]

            file_path = str(self.project_dir / module_name)
            spec = importlib.util.spec_from_file_location(module_name, file_path)
            if spec:
                module = importlib.util.module_from_spec(spec)
                # Do not add to global modules because there is no standard
                # module name that Python can resolve.
                self._private_plugins[module_name] = module
                assert spec.loader is not None
                spec.loader.exec_module(module)

                return module

        return sys.modules.get(module_name) or importlib.import_module(module_name)

    def find_config_file(self, filename: StrPath,
                         config: Optional[str] = None) -> Path:
        """ Resolve the location of a configuration file given a filename and
            an optional configuration option with the file name.
            Raises a UsageError when the file cannot be found or is not
            a regular file.
        """
        if config is not None:
            cfg_value = getattr(self, config)
            if cfg_value:
                cfg_filename = Path(cfg_value)

                if cfg_filename.is_absolute():
                    cfg_filename = cfg_filename.resolve()

                    if not cfg_filename.is_file():
                        LOG.fatal("Cannot find config file '%s'.", cfg_filename)
                        raise UsageError("Config file not found.")

                    return cfg_filename

                filename = cfg_filename

        search_paths = [self.project_dir, self.config_dir]
        for path in search_paths:
            if path is not None and (path / filename).is_file():
                return path / filename

        LOG.fatal("Configuration file '%s' not found.\nDirectories searched: %s",
                  filename, search_paths)
        raise UsageError("Config file not found.")

    def _load_from_yaml(self, cfgfile: Path) -> Any:
        """ Load a YAML configuration file. This installs a special handler that
            allows to include other YAML files using the '!include' operator.
        """
        yaml.add_constructor('!include', self._yaml_include_representer,
                             Loader=yaml.SafeLoader)
        return yaml.safe_load(cfgfile.read_text(encoding='utf-8'))

    def _yaml_include_representer(self, loader: Any, node: yaml.Node) -> Any:
        """ Handler for the '!include' operator in YAML files.

            When the filename is relative, then the file is first searched in the
            project directory and then in the global settings directory.
        """
        fname = loader.construct_scalar(node)

        if Path(fname).is_absolute():
            configfile = Path(fname)
        else:
            configfile = self.find_config_file(loader.construct_scalar(node))

        if configfile.suffix != '.yaml':
            LOG.fatal("Format error while reading '%s': only YAML format supported.",
                      configfile)
            raise UsageError("Cannot handle config file format.")

        return yaml.safe_load(configfile.read_text(encoding='utf-8'))


================================================
FILE: src/nominatim_db/data/__init__.py
================================================


================================================
FILE: src/nominatim_db/data/country_info.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for importing and managing static country information.
"""
from typing import Dict, Any, Iterable, Tuple, Optional, Container, overload
from pathlib import Path

from ..db import utils as db_utils
from ..db.connection import connect, Connection, register_hstore
from ..errors import UsageError
from ..config import Configuration
from ..tokenizer.base import AbstractTokenizer


def _flatten_name_list(names: Any) -> Dict[str, str]:
    if names is None:
        return {}

    if not isinstance(names, dict):
        raise UsageError("Expected key-value list for names in country_settings.py")

    flat = {}
    for prefix, remain in names.items():
        if isinstance(remain, str):
            flat[prefix] = remain
        elif not isinstance(remain, dict):
            raise UsageError("Entries in names must be key-value lists.")
        else:
            for suffix, name in remain.items():
                if suffix == 'default':
                    flat[prefix] = name
                else:
                    flat[f'{prefix}:{suffix}'] = name

    return flat


class _CountryInfo:
    """ Caches country-specific properties from the configuration file.
    """

    def __init__(self) -> None:
        self._info: Dict[str, Dict[str, Any]] = {}

    def load(self, config: Configuration) -> None:
        """ Load the country properties from the configuration files,
            if they are not loaded yet.
        """
        if not self._info:
            self._info = config.load_sub_configuration('country_settings.yaml')
            for prop in self._info.values():
                # Convert languages into a list for simpler handling.
                if 'languages' not in prop:
                    prop['languages'] = []
                elif not isinstance(prop['languages'], list):
                    prop['languages'] = [x.strip()
                                         for x in prop['languages'].split(',')]
                prop['names'] = _flatten_name_list(prop.get('names'))

    def items(self) -> Iterable[Tuple[str, Dict[str, Any]]]:
        """ Return tuples of (country_code, property dict) as iterable.
        """
        return self._info.items()

    def get(self, country_code: str) -> Dict[str, Any]:
        """ Get country information for the country with the given country code.
        """
        return self._info.get(country_code, {})


_COUNTRY_INFO = _CountryInfo()


def setup_country_config(config: Configuration) -> None:
    """ Load country properties from the configuration file.
        Needs to be called before using any other functions in this
        file.
    """
    _COUNTRY_INFO.load(config)


@overload
def iterate() -> Iterable[Tuple[str, Dict[str, Any]]]:
    ...


@overload
def iterate(prop: str) -> Iterable[Tuple[str, Any]]:
    ...


def iterate(prop: Optional[str] = None) -> Iterable[Tuple[str, Dict[str, Any]]]:
    """ Iterate over country code and properties.

        When `prop` is None, all countries are returned with their complete
        set of properties.

        If `prop` is given, then only countries are returned where the
        given property is set. The second item of the tuple contains only
        the content of the given property.
    """
    if prop is None:
        return _COUNTRY_INFO.items()

    return ((c, p[prop]) for c, p in _COUNTRY_INFO.items() if prop in p)


def setup_country_tables(dsn: str, sql_dir: Path, ignore_partitions: bool = False) -> None:
    """ Create and populate the tables with basic static data that provides
        the background for geocoding. Data is assumed to not yet exist.
    """
    db_utils.execute_file(dsn, sql_dir / 'country_osm_grid.sql.gz')

    params = []
    for ccode, props in _COUNTRY_INFO.items():
        if ccode is not None and props is not None:
            if ignore_partitions:
                partition = 0
            else:
                partition = props.get('partition', 0)
            lang = props['languages'][0] if len(
                props['languages']) == 1 else None

            params.append((ccode, props['names'], lang, partition))
    with connect(dsn) as conn:
        register_hstore(conn)
        with conn.cursor() as cur:
            cur.execute(
                """ CREATE TABLE public.country_name (
                        country_code character varying(2),
                        name public.hstore,
                        derived_name public.hstore,
                        country_default_language_code text,
                        partition integer
                    ); """)
            cur.executemany(
                """ INSERT INTO public.country_name
                    (country_code, name, country_default_language_code, partition)
                    VALUES (%s, %s, %s, %s)
                """, params)
        conn.commit()


def create_country_names(conn: Connection, tokenizer: AbstractTokenizer,
                         languages: Optional[Container[str]] = None) -> None:
    """ Add default country names to search index. `languages` is a comma-
        separated list of language codes as used in OSM. If `languages` is not
        empty then only name translations for the given languages are added
        to the index.
    """
    def _include_key(key: str) -> bool:
        return ':' not in key or not languages or \
               key[key.index(':') + 1:] in languages

    register_hstore(conn)
    with conn.cursor() as cur:
        cur.execute("""SELECT country_code, name FROM country_name
                       WHERE country_code is not null""")

        with tokenizer.name_analyzer() as analyzer:
            for code, name in cur:
                names = {'countrycode': code}

                # country names (only in languages as provided)
                if name:
                    names.update({k: v for k, v in name.items() if _include_key(k)})

                analyzer.add_country_names(code, names)

    conn.commit()


================================================
FILE: src/nominatim_db/data/place_info.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Wrapper around place information the indexer gets from the database and hands to
the tokenizer.
"""
from typing import Optional, Mapping, Any, Tuple, cast


class PlaceInfo:
    """ This data class contains all information the tokenizer can access
        about a place.
    """

    def __init__(self, info: Mapping[str, Any]) -> None:
        self._info = info

    @property
    def name(self) -> Optional[Mapping[str, str]]:
        """ A dictionary with the names of the place. Keys and values represent
            the full key and value of the corresponding OSM tag. Which tags
            are saved as names is determined by the import style.
            The property may be None if the place has no names.
        """
        return self._info.get('name')

    @property
    def address(self) -> Optional[Mapping[str, str]]:
        """ A dictionary with the address elements of the place. They key
            usually corresponds to the suffix part of the key of an OSM
            'addr:*' or 'isin:*' tag. There are also some special keys like
            `country` or `country_code` which merge OSM keys that contain
            the same information. See [Import Styles][1] for details.

            The property may be None if the place has no address information.

            [1]: ../customize/Import-Styles.md
        """
        return self._info.get('address')

    @property
    def country_code(self) -> Optional[str]:
        """ The country code of the country the place is in. Guaranteed
            to be a two-letter lower-case string. If the place is not inside
            any country, the property is set to None.
        """
        return self._info.get('country_code')

    @property
    def rank_address(self) -> int:
        """ The [rank address][1] before any rank correction is applied.

            [1]: ../customize/Ranking.md#address-rank
        """
        return cast(int, self._info.get('rank_address', 0))

    @property
    def centroid(self) -> Optional[Tuple[float, float]]:
        """ A center point of the place in WGS84. May be None when the
            geometry of the place is unknown.
        """
        x, y = self._info.get('centroid_x'), self._info.get('centroid_y')
        return None if x is None or y is None else (x, y)

    def is_a(self, key: str, value: str) -> bool:
        """ Set to True when the place's primary tag corresponds to the given
            key and value.
        """
        return self._info.get('class') == key and self._info.get('type') == value

    def is_country(self) -> bool:
        """ Set to True when the place is a valid country boundary.
        """
        return self.rank_address == 4 \
            and self.is_a('boundary', 'administrative') \
            and self.country_code is not None


================================================
FILE: src/nominatim_db/data/place_name.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Data class for a single name of a place.
"""
from typing import Optional, Dict, Mapping


class PlaceName:
    """ Each name and address part of a place is encapsulated in an object of
        this class. It saves not only the name proper but also describes the
        kind of name with two properties:

        * `kind` describes the name of the OSM key used without any suffixes
          (i.e. the part after the colon removed)
        * `suffix` contains the suffix of the OSM tag, if any. The suffix
          is the part of the key after the first colon.

        In addition to that, a name may have arbitrary additional attributes.
        How attributes are used, depends on the sanitizers and token analysers.
        The exception is the 'analyzer' attribute. This attribute determines
        which token analysis module will be used to finalize the treatment of
        names.
    """

    def __init__(self, name: str, kind: str, suffix: Optional[str]):
        self.name = name
        self.kind = kind
        self.suffix = suffix
        self.attr: Dict[str, str] = {}

    def __repr__(self) -> str:
        return f"PlaceName(name={self.name!r},kind={self.kind!r},suffix={self.suffix!r})"

    def clone(self, name: Optional[str] = None,
              kind: Optional[str] = None,
              suffix: Optional[str] = None,
              attr: Optional[Mapping[str, str]] = None) -> 'PlaceName':
        """ Create a deep copy of the place name, optionally with the
            given parameters replaced. In the attribute list only the given
            keys are updated. The list is not replaced completely.
            In particular, the function cannot to be used to remove an
            attribute from a place name.
        """
        newobj = PlaceName(name or self.name,
                           kind or self.kind,
                           suffix or self.suffix)

        newobj.attr.update(self.attr)
        if attr:
            newobj.attr.update(attr)

        return newobj

    def set_attr(self, key: str, value: str) -> None:
        """ Add the given property to the name. If the property was already
            set, then the value is overwritten.
        """
        self.attr[key] = value

    def get_attr(self, key: str, default: Optional[str] = None) -> Optional[str]:
        """ Return the given property or the value of 'default' if it
            is not set.
        """
        return self.attr.get(key, default)

    def has_attr(self, key: str) -> bool:
        """ Check if the given attribute is set.
        """
        return key in self.attr


================================================
FILE: src/nominatim_db/data/postcode_format.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for formatting postcodes according to their country-specific
format.
"""
from typing import Any, Mapping, Optional, Set, Match
import re

from ..errors import UsageError
from . import country_info


class CountryPostcodeMatcher:
    """ Matches and formats a postcode according to a format definition
        of the given country.
    """
    def __init__(self, country_code: str, config: Mapping[str, Any]) -> None:
        if 'pattern' not in config:
            raise UsageError("Field 'pattern' required for 'postcode' "
                             f"for country '{country_code}'")

        pc_pattern = config['pattern'].replace('d', '[0-9]').replace('l', '[A-Z]')

        self.norm_pattern = re.compile(f'\\s*(?:{country_code.upper()}[ -]?)?({pc_pattern})\\s*')
        self.pattern = re.compile(pc_pattern)

        # We want to exclude 0000, 00-000, 000 00 etc
        self.zero_pattern = re.compile(r'^[0\- ]+$')

        self.output = config.get('output', r'\g<0>')

    def match(self, postcode: str) -> Optional[Match[str]]:
        """ Match the given postcode against the postcode pattern for this
            matcher. Returns a `re.Match` object if the match was successful
            and None otherwise.
        """
        # Upper-case, strip spaces and leading country code.
        normalized = self.norm_pattern.fullmatch(postcode.upper())

        if normalized:
            match = self.pattern.fullmatch(normalized.group(1))
            if match and self.zero_pattern.match(match.string):
                return None
            return match

        return None

    def normalize(self, match: Match[str]) -> str:
        """ Return the default format of the postcode for the given match.
            `match` must be a `re.Match` object previously returned by
            `match()`
        """
        return match.expand(self.output)


class PostcodeFormatter:
    """ Container for different postcode formats of the world and
        access functions.
    """
    def __init__(self) -> None:
        # Objects without a country code can't have a postcode per definition.
        self.country_without_postcode: Set[Optional[str]] = {None}
        self.country_matcher = {}
        self.default_matcher = CountryPostcodeMatcher('', {'pattern': '.*'})
        self.postcode_extent: dict[Optional[str], int] = {}

        for ccode, prop in country_info.iterate('postcode'):
            if prop is False:
                self.country_without_postcode.add(ccode)
            elif isinstance(prop, dict):
                self.country_matcher[ccode] = CountryPostcodeMatcher(ccode, prop)
                if 'extent' in prop:
                    self.postcode_extent[ccode] = int(prop['extent'])
            else:
                raise UsageError(f"Invalid entry 'postcode' for country '{ccode}'")

    def set_default_pattern(self, pattern: str) -> None:
        """ Set the postcode match pattern to use, when a country does not
            have a specific pattern.
        """
        self.default_matcher = CountryPostcodeMatcher('', {'pattern': pattern})

    def get_matcher(self, country_code: Optional[str]) -> Optional[CountryPostcodeMatcher]:
        """ Return the CountryPostcodeMatcher for the given country.
            Returns None if the country doesn't have a postcode and the
            default matcher if there is no specific matcher configured for
            the country.
        """
        if country_code in self.country_without_postcode:
            return None

        assert country_code is not None

        return self.country_matcher.get(country_code, self.default_matcher)

    def match(self, country_code: Optional[str], postcode: str) -> Optional[Match[str]]:
        """ Match the given postcode against the postcode pattern for this
            matcher. Returns a `re.Match` object if the country has a pattern
            and the match was successful or None if the match failed.
        """
        if country_code in self.country_without_postcode:
            return None

        assert country_code is not None

        return self.country_matcher.get(country_code, self.default_matcher).match(postcode)

    def normalize(self, country_code: str, match: Match[str]) -> str:
        """ Return the default format of the postcode for the given match.
            `match` must be a `re.Match` object previously returned by
            `match()`
        """
        return self.country_matcher.get(country_code, self.default_matcher).normalize(match)

    def get_postcode_extent(self, country_code: Optional[str]) -> int:
        """ Return the extent (in m) to use for the given country. If no
            specific extent is set, then the default of 5km will be returned.
        """
        return self.postcode_extent.get(country_code, 5000)


================================================
FILE: src/nominatim_db/db/__init__.py
================================================


================================================
FILE: src/nominatim_db/db/connection.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Specialised connection and cursor functions.
"""
from typing import Optional, Any, Dict, Tuple
import logging
import os

import psycopg
import psycopg.types.hstore
from psycopg import sql as pysql

from ..typing import SysEnv
from ..errors import UsageError

LOG = logging.getLogger()

Cursor = psycopg.Cursor[Any]
Connection = psycopg.Connection[Any]


def execute_scalar(conn: Connection, sql: psycopg.abc.Query, args: Any = None) -> Any:
    """ Execute query that returns a single value. The value is returned.
        If the query yields more than one row, a ValueError is raised.
    """
    with conn.cursor(row_factory=psycopg.rows.tuple_row) as cur:
        cur.execute(sql, args)

        if cur.rowcount != 1:
            raise RuntimeError("Query did not return a single row.")

        result = cur.fetchone()

    assert result is not None
    return result[0]


def table_exists(conn: Connection, table: str) -> bool:
    """ Check that a table with the given name exists in the database.
    """
    num = execute_scalar(
        conn,
        """SELECT count(*) FROM pg_tables
           WHERE tablename = %s and schemaname = 'public'""", (table, ))
    return num == 1 if isinstance(num, int) else False


def table_has_column(conn: Connection, table: str, column: str) -> bool:
    """ Check if the table 'table' exists and has a column with name 'column'.
    """
    has_column = execute_scalar(conn,
                                """SELECT count(*) FROM information_schema.columns
                                   WHERE table_name = %s and column_name = %s""",
                                (table, column))
    return has_column > 0 if isinstance(has_column, int) else False


def index_exists(conn: Connection, index: str, table: Optional[str] = None) -> bool:
    """ Check that an index with the given name exists in the database.
        If table is not None then the index must relate to the given
        table.
    """
    with conn.cursor() as cur:
        cur.execute("""SELECT tablename FROM pg_indexes
                       WHERE indexname = %s and schemaname = 'public'""", (index, ))
        if cur.rowcount == 0:
            return False

        if table is not None:
            row = cur.fetchone()
            if row is None or not isinstance(row[0], str):
                return False
            return row[0] == table

    return True


def drop_tables(conn: Connection, *names: str,
                if_exists: bool = True, cascade: bool = False) -> None:
    """ Drop one or more tables with the given names.
        Set `if_exists` to False if a non-existent table should raise
        an exception instead of just being ignored. `cascade` will cause
        depended objects to be dropped as well.
        The caller needs to take care of committing the change.
    """
    sql = pysql.SQL('DROP TABLE%s{}%s' % (
                        ' IF EXISTS ' if if_exists else ' ',
                        ' CASCADE' if cascade else ''))

    with conn.cursor() as cur:
        for name in names:
            cur.execute(sql.format(pysql.Identifier(name)))


def server_version_tuple(conn: Connection) -> Tuple[int, int]:
    """ Return the server version as a tuple of (major, minor).
        Converts correctly for pre-10 and post-10 PostgreSQL versions.
    """
    version = conn.info.server_version
    major, minor = divmod(version, 10000)
    if major < 10:
        minor //= 100
    return major, minor


def postgis_version_tuple(conn: Connection) -> Tuple[int, int]:
    """ Return the postgis version installed in the database as a
        tuple of (major, minor). Assumes that the PostGIS extension
        has been installed already.
    """
    version = execute_scalar(conn, 'SELECT postgis_lib_version()')

    version_parts = version.split('.')
    if len(version_parts) < 2:
        raise UsageError(f"Error fetching Postgis version. Bad format: {version}")

    return (int(version_parts[0]), int(version_parts[1]))


def register_hstore(conn: Connection) -> None:
    """ Register the hstore type with psycopg for the connection.
    """
    info = psycopg.types.TypeInfo.fetch(conn, "hstore")
    if info is None:
        raise RuntimeError('Hstore extension is requested but not installed.')
    psycopg.types.hstore.register_hstore(info, conn)


def connect(dsn: str, **kwargs: Any) -> Connection:
    """ Open a connection to the database using the specialised connection
        factory. The returned object may be used in conjunction with 'with'.
        When used outside a context manager, use the `connection` attribute
        to get the connection.
    """
    try:
        return psycopg.connect(dsn, row_factory=psycopg.rows.namedtuple_row, **kwargs)
    except psycopg.OperationalError as err:
        raise UsageError(f"Cannot connect to database: {err}") from err


# Translation from PG connection string parameters to PG environment variables.
# Derived from https://www.postgresql.org/docs/current/libpq-envars.html.
_PG_CONNECTION_STRINGS = {
    'host': 'PGHOST',
    'hostaddr': 'PGHOSTADDR',
    'port': 'PGPORT',
    'dbname': 'PGDATABASE',
    'user': 'PGUSER',
    'password': 'PGPASSWORD',
    'passfile': 'PGPASSFILE',
    'channel_binding': 'PGCHANNELBINDING',
    'service': 'PGSERVICE',
    'options': 'PGOPTIONS',
    'application_name': 'PGAPPNAME',
    'sslmode': 'PGSSLMODE',
    'requiressl': 'PGREQUIRESSL',
    'sslcompression': 'PGSSLCOMPRESSION',
    'sslcert': 'PGSSLCERT',
    'sslkey': 'PGSSLKEY',
    'sslrootcert': 'PGSSLROOTCERT',
    'sslcrl': 'PGSSLCRL',
    'requirepeer': 'PGREQUIREPEER',
    'ssl_min_protocol_version': 'PGSSLMINPROTOCOLVERSION',
    'ssl_max_protocol_version': 'PGSSLMAXPROTOCOLVERSION',
    'gssencmode': 'PGGSSENCMODE',
    'krbsrvname': 'PGKRBSRVNAME',
    'gsslib': 'PGGSSLIB',
    'connect_timeout': 'PGCONNECT_TIMEOUT',
    'target_session_attrs': 'PGTARGETSESSIONATTRS',
}


def get_pg_env(dsn: str,
               base_env: Optional[SysEnv] = None) -> Dict[str, str]:
    """ Return a copy of `base_env` with the environment variables for
        PostgreSQL set up from the given database connection string.
        If `base_env` is None, then the OS environment is used as a base
        environment.
    """
    env = dict(base_env if base_env is not None else os.environ)

    for param, value in psycopg.conninfo.conninfo_to_dict(dsn).items():
        if param in _PG_CONNECTION_STRINGS:
            env[_PG_CONNECTION_STRINGS[param]] = str(value)
        else:
            LOG.error("Unknown connection parameter '%s' ignored.", param)

    return env


async def run_async_query(dsn: str, query: psycopg.abc.Query) -> None:
    """ Open a connection to the database and run a single query
        asynchronously.
    """
    async with await psycopg.AsyncConnection.connect(dsn) as aconn:
        await aconn.execute(query)


================================================
FILE: src/nominatim_db/db/properties.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Query and access functions for the in-database property table.
"""
from typing import Optional, cast

from .connection import Connection, table_exists


def set_property(conn: Connection, name: str, value: str) -> None:
    """ Add or replace the property with the given name.
    """
    with conn.cursor() as cur:
        cur.execute('SELECT value FROM nominatim_properties WHERE property = %s',
                    (name, ))

        if cur.rowcount == 0:
            sql = 'INSERT INTO nominatim_properties (value, property) VALUES (%s, %s)'
        else:
            sql = 'UPDATE nominatim_properties SET value = %s WHERE property = %s'

        cur.execute(sql, (value, name))
    conn.commit()


def get_property(conn: Connection, name: str) -> Optional[str]:
    """ Return the current value of the given property or None if the property
        is not set.
    """
    if not table_exists(conn, 'nominatim_properties'):
        return None

    with conn.cursor() as cur:
        cur.execute('SELECT value FROM nominatim_properties WHERE property = %s',
                    (name, ))

        if cur.rowcount == 0:
            return None

        result = cur.fetchone()
        assert result is not None

        return cast(Optional[str], result[0])


================================================
FILE: src/nominatim_db/db/query_pool.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
A connection pool that executes incoming queries in parallel.
"""
from typing import Any, Tuple, Optional
import asyncio
import logging
import time

import psycopg

LOG = logging.getLogger()

QueueItem = Optional[Tuple[psycopg.abc.Query, Any]]


class QueryPool:
    """ Pool to run SQL queries in parallel asynchronous execution.

        All queries are run in autocommit mode. If parallel execution leads
        to a deadlock, then the query is repeated.
        The results of the queries is discarded.
    """
    def __init__(self, dsn: str, pool_size: int = 1, **conn_args: Any) -> None:
        self.is_cancelled = False
        self.wait_time = 0.0
        self.query_queue: 'asyncio.Queue[QueueItem]' = asyncio.Queue(maxsize=2 * pool_size)

        self.pool = [asyncio.create_task(self._worker_loop_cancellable(dsn, **conn_args))
                     for _ in range(pool_size)]

    async def put_query(self, query: psycopg.abc.Query, params: Any) -> None:
        """ Schedule a query for execution.
        """
        if self.is_cancelled:
            self.clear_queue()
            await self.finish()
            return

        tstart = time.time()
        await self.query_queue.put((query, params))
        self.wait_time += time.time() - tstart
        await asyncio.sleep(0)

        if self.is_cancelled:
            self.clear_queue()
            await self.finish()

    async def finish(self) -> None:
        """ Wait for all queries to finish and close the pool.
        """
        for _ in self.pool:
            await self.query_queue.put(None)

        tstart = time.time()
        await asyncio.wait(self.pool)
        self.wait_time += time.time() - tstart

        for task in self.pool:
            excp = task.exception()
            if excp is not None:
                raise excp

    def clear_queue(self) -> None:
        """ Drop all items silently that might still be queued.
        """
        try:
            while True:
                self.query_queue.get_nowait()
        except asyncio.QueueEmpty:
            pass  # expected

    async def _worker_loop_cancellable(self, dsn: str, **conn_args: Any) -> None:
        try:
            await self._worker_loop(dsn, **conn_args)
        except Exception as e:
            # Make sure the exception is forwarded to the main function
            self.is_cancelled = True
            # clear the queue here to ensure that any put() that may be blocked returns
            self.clear_queue()
            raise e

    async def _worker_loop(self, dsn: str, **conn_args: Any) -> None:
        conn_args['autocommit'] = True
        aconn = await psycopg.AsyncConnection.connect(dsn, **conn_args)
        async with aconn:
            async with aconn.cursor() as cur:
                item = await self.query_queue.get()
                while item is not None:
                    try:
                        if item[1] is None:
                            await cur.execute(item[0])
                        else:
                            await cur.execute(item[0], item[1])

                        item = await self.query_queue.get()
                    except psycopg.errors.DeadlockDetected:
                        assert item is not None
                        LOG.info("Deadlock detected (sql = %s, params = %s), retry.",
                                 str(item[0]), str(item[1]))
                        # item is still valid here, causing a retry

    async def __aenter__(self) -> 'QueryPool':
        return self

    async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
        await self.finish()


================================================
FILE: src/nominatim_db/db/sql_preprocessor.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Preprocessing of SQL files.
"""
from typing import Set, Dict, Any, cast
import re

import jinja2

from .connection import Connection
from ..config import Configuration
from ..db.query_pool import QueryPool


def _get_partitions(conn: Connection) -> Set[int]:
    """ Get the set of partitions currently in use.
    """
    with conn.cursor() as cur:
        cur.execute('SELECT DISTINCT partition FROM country_name')
        partitions = set([0])
        for row in cur:
            partitions.add(row[0])

    return partitions


def _get_tables(conn: Connection) -> Set[str]:
    """ Return the set of tables currently in use.
    """
    with conn.cursor() as cur:
        cur.execute("SELECT tablename FROM pg_tables WHERE schemaname = 'public'")

        # paranoia check: make sure we don't get table names that cause
        # an SQL injection later
        return {row[0] for row in list(cur) if re.fullmatch(r'\w+', row[0])}


def _get_middle_db_format(conn: Connection, tables: Set[str]) -> str:
    """ Returns the version of the slim middle tables.
    """
    if 'osm2pgsql_properties' not in tables:
        return '1'

    with conn.cursor() as cur:
        cur.execute("SELECT value FROM osm2pgsql_properties WHERE property = 'db_format'")
        row = cur.fetchone()

        return cast(str, row[0]) if row is not None else '1'


def _setup_tablespace_sql(config: Configuration) -> Dict[str, str]:
    """ Returns a dict with tablespace expressions for the different tablespace
        kinds depending on whether a tablespace is configured or not.
    """
    out = {}
    for subset in ('ADDRESS', 'SEARCH', 'AUX'):
        for kind in ('DATA', 'INDEX'):
            tspace = getattr(config, f'TABLESPACE_{subset}_{kind}')
            if tspace:
                tspace = f'TABLESPACE "{tspace}"'
            out[f'{subset.lower()}_{kind.lower()}'] = tspace

    return out


def _setup_postgresql_features(conn: Connection) -> Dict[str, Any]:
    """ Set up a dictionary with various optional Postgresql/Postgis features that
        depend on the database version.
    """
    return {}


class SQLPreprocessor:
    """ A environment for preprocessing SQL files from the
        lib-sql directory.

        The preprocessor provides a number of default filters and variables.
        The variables may be overwritten when rendering an SQL file.

        The preprocessing is currently based on the jinja2 templating library
        and follows its syntax.
    """

    def __init__(self, conn: Connection, config: Configuration) -> None:
        self.env = jinja2.Environment(autoescape=False,
                                      loader=jinja2.FileSystemLoader(str(config.lib_dir.sql)))

        db_info: Dict[str, Any] = {}
        db_info['partitions'] = _get_partitions(conn)
        db_info['tables'] = _get_tables(conn)
        db_info['reverse_only'] = 'search_name' not in db_info['tables']
        db_info['tablespace'] = _setup_tablespace_sql(config)
        db_info['middle_db_format'] = _get_middle_db_format(conn, db_info['tables'])

        self.env.globals['config'] = config
        self.env.globals['db'] = db_info
        self.env.globals['postgres'] = _setup_postgresql_features(conn)

    def run_string(self, conn: Connection, template: str, **kwargs: Any) -> None:
        """ Execute the given SQL template string on the connection.
            The keyword arguments may supply additional parameters
            for preprocessing.
        """
        sql = self.env.from_string(template).render(**kwargs)

        with conn.cursor() as cur:
            cur.execute(sql)
        conn.commit()

    def run_sql_file(self, conn: Connection, name: str, **kwargs: Any) -> None:
        """ Execute the given SQL file on the connection. The keyword arguments
            may supply additional parameters for preprocessing.
        """
        sql = self.env.get_template(name).render(**kwargs)

        with conn.cursor() as cur:
            cur.execute(sql)
        conn.commit()

    async def run_parallel_sql_file(self, dsn: str, name: str, num_threads: int = 1,
                                    **kwargs: Any) -> None:
        """ Execute the given SQL files using parallel asynchronous connections.
            The keyword arguments may supply additional parameters for
            preprocessing.

            After preprocessing the SQL code is cut at lines containing only
            '---'. Each chunk is sent to one of the `num_threads` workers.
        """
        sql = self.env.get_template(name).render(**kwargs)

        parts = sql.split('\n---\n')

        async with QueryPool(dsn, num_threads) as pool:
            for part in parts:
                await pool.put_query(part, None)


================================================
FILE: src/nominatim_db/db/status.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Access and helper functions for the status and status log table.
"""
from typing import Optional, Tuple
import datetime as dt
import logging
import re

from .connection import Connection, table_exists, execute_scalar
from ..utils.url_utils import get_url
from ..errors import UsageError

LOG = logging.getLogger()
ISODATE_FORMAT = '%Y-%m-%dT%H:%M:%S'


def compute_database_date(conn: Connection, offline: bool = False) -> dt.datetime:
    """ Determine the date of the database from the newest object in the
        data base.
    """
    # If there is a date from osm2pgsql available, use that.
    if table_exists(conn, 'osm2pgsql_properties'):
        with conn.cursor() as cur:
            cur.execute(""" SELECT value FROM osm2pgsql_properties
                            WHERE property = 'current_timestamp' """)
            row = cur.fetchone()
            if row is not None:
                return dt.datetime.strptime(row[0], "%Y-%m-%dT%H:%M:%SZ")\
                                  .replace(tzinfo=dt.timezone.utc)

    if offline:
        raise UsageError("Cannot determine database date from data in offline mode.")

    # Else, find the node with the highest ID in the database
    if table_exists(conn, 'place'):
        osmid = execute_scalar(conn, "SELECT max(osm_id) FROM place WHERE osm_type='N'")
    else:
        osmid = execute_scalar(conn, "SELECT max(osm_id) FROM placex WHERE osm_type='N'")

    if osmid is None:
        LOG.fatal("No data found in the database.")
        raise UsageError("No data found in the database.")

    LOG.info("Using node id %d for timestamp lookup", osmid)
    # Get the node from the API to find the timestamp when it was created.
    node_url = f'https://www.openstreetmap.org/api/0.6/node/{osmid}/1'
    data = get_url(node_url)

    match = re.search(r'timestamp="((\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}))Z"', data)

    if match is None:
        LOG.fatal("The node data downloaded from the API does not contain valid data.\n"
                  "URL used: %s", node_url)
        raise UsageError("Bad API data.")

    LOG.debug("Found timestamp %s", match.group(1))

    return dt.datetime.strptime(match.group(1), ISODATE_FORMAT).replace(tzinfo=dt.timezone.utc)


def set_status(conn: Connection, date: Optional[dt.datetime],
               seq: Optional[int] = None, indexed: bool = True) -> None:
    """ Replace the current status with the given status. If date is `None`
        then only sequence and indexed will be updated as given. Otherwise
        the whole status is replaced.
        The change will be committed to the database.
    """
    assert date is None or date.tzinfo == dt.timezone.utc
    with conn.cursor() as cur:
        if date is None:
            cur.execute("UPDATE import_status set sequence_id = %s, indexed = %s",
                        (seq, indexed))
        else:
            cur.execute("TRUNCATE TABLE import_status")
            cur.execute("""INSERT INTO import_status (lastimportdate, sequence_id, indexed)
                           VALUES (%s, %s, %s)""", (date, seq, indexed))

    conn.commit()


def get_status(conn: Connection) -> Tuple[Optional[dt.datetime], Optional[int], Optional[bool]]:
    """ Return the current status as a triple of (date, sequence, indexed).
        If status has not been set up yet, a triple of None is returned.
    """
    with conn.cursor() as cur:
        cur.execute("SELECT * FROM import_status LIMIT 1")
        if cur.rowcount < 1:
            return None, None, None

        row = cur.fetchone()
        assert row
        return row.lastimportdate, row.sequence_id, row.indexed


def set_indexed(conn: Connection, state: bool) -> None:
    """ Set the indexed flag in the status table to the given state.
    """
    with conn.cursor() as cur:
        cur.execute("UPDATE import_status SET indexed = %s", (state, ))
    conn.commit()


def log_status(conn: Connection, start: dt.datetime,
               event: str, batchsize: Optional[int] = None) -> None:
    """ Write a new status line to the `import_osmosis_log` table.
    """
    with conn.cursor() as cur:
        cur.execute("""INSERT INTO import_osmosis_log
                       (batchend, batchseq, batchsize, starttime, endtime, event)
                       SELECT lastimportdate, sequence_id, %s, %s, now(), %s FROM import_status""",
                    (batchsize, start, event))
    conn.commit()


================================================
FILE: src/nominatim_db/db/utils.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Helper functions for handling DB accesses.
"""
from typing import IO, Optional, Union
import subprocess
import logging
import gzip
from pathlib import Path

from .connection import get_pg_env
from ..errors import UsageError

LOG = logging.getLogger()


def _pipe_to_proc(proc: 'subprocess.Popen[bytes]',
                  fdesc: Union[IO[bytes], gzip.GzipFile]) -> int:
    assert proc.stdin is not None
    chunk = fdesc.read(2048)
    while chunk and proc.poll() is None:
        try:
            proc.stdin.write(chunk)
        except BrokenPipeError as exc:
            raise UsageError("Failed to execute SQL file.") from exc
        chunk = fdesc.read(2048)

    return len(chunk)


def execute_file(dsn: str, fname: Path,
                 ignore_errors: bool = False,
                 pre_code: Optional[str] = None,
                 post_code: Optional[str] = None) -> None:
    """ Read an SQL file and run its contents against the given database
        using psql. Use `pre_code` and `post_code` to run extra commands
        before or after executing the file. The commands are run within the
        same session, so they may be used to wrap the file execution in a
        transaction.
    """
    cmd = ['psql']
    if not ignore_errors:
        cmd.extend(('-v', 'ON_ERROR_STOP=1'))
    if not LOG.isEnabledFor(logging.INFO):
        cmd.append('--quiet')

    with subprocess.Popen(cmd, env=get_pg_env(dsn), stdin=subprocess.PIPE) as proc:
        assert proc.stdin is not None
        try:
            if not LOG.isEnabledFor(logging.INFO):
                proc.stdin.write('set client_min_messages to WARNING;'.encode('utf-8'))

            if pre_code:
                proc.stdin.write((pre_code + ';').encode('utf-8'))

            if fname.suffix == '.gz':
                with gzip.open(str(fname), 'rb') as fdesc:
                    remain = _pipe_to_proc(proc, fdesc)
            else:
                with fname.open('rb') as fdesc:
                    remain = _pipe_to_proc(proc, fdesc)

            if remain == 0 and post_code:
                proc.stdin.write((';' + post_code).encode('utf-8'))
        finally:
            proc.stdin.close()
            ret = proc.wait()

    if ret != 0 or remain > 0:
        raise UsageError("Failed to execute SQL file.")


================================================
FILE: src/nominatim_db/errors.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Custom exception and error classes for Nominatim.
"""


class UsageError(Exception):
    """ An error raised because of bad user input. This error will usually
        not cause a stack trace to be printed unless debugging is enabled.
    """


================================================
FILE: src/nominatim_db/indexer/__init__.py
================================================


================================================
FILE: src/nominatim_db/indexer/indexer.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Main work horse for indexing (computing addresses) the database.
"""
from typing import cast, List, Any, Optional
import logging
import time

import psycopg

from ..db.connection import connect, execute_scalar
from ..db.query_pool import QueryPool
from ..tokenizer.base import AbstractTokenizer
from .progress import ProgressLogger
from . import runners

LOG = logging.getLogger()


class Indexer:
    """ Main indexing routine.
    """

    def __init__(self, dsn: str, tokenizer: AbstractTokenizer, num_threads: int):
        self.dsn = dsn
        self.tokenizer = tokenizer
        self.num_threads = num_threads

    def has_pending(self, minrank: int = 0, maxrank: int = 30) -> bool:
        """ Check if any data still needs indexing.
            This function must only be used after the import has finished.
            Otherwise it will be very expensive.
        """
        with connect(self.dsn) as conn:
            with conn.cursor() as cur:
                cur.execute(""" SELECT 'a'
                                  FROM placex
                                 WHERE rank_address BETWEEN %s AND %s
                                   AND indexed_status > 0
                                 LIMIT 1""",
                            (minrank, maxrank))
                return cur.rowcount > 0

    async def index_full(self, analyse: bool = True) -> None:
        """ Index the complete database. This will first index boundaries
            followed by all other objects. When `analyse` is True, then the
            database will be analysed at the appropriate places to
            ensure that database statistics are updated.
        """
        with connect(self.dsn) as conn:
            conn.autocommit = True

            def _analyze() -> None:
                if analyse:
                    with conn.cursor() as cur:
                        cur.execute('ANALYZE')

            while True:
                if await self.index_by_rank(1, 4) > 0:
                    _analyze()

                if await self.index_boundaries() > 100:
                    _analyze()

                if await self.index_by_rank(5, 25) > 100:
                    _analyze()

                if await self.index_by_rank(26, 30) > 1000:
                    _analyze()

                # Special case: rank zero depends on the previously-indexed [1..30] ranks
                await self.index_by_rank(0, 0)

                if await self.index_postcodes() > 100:
                    _analyze()

                if not self.has_pending():
                    break

    async def index_boundaries(self, minrank: int = 0, maxrank: int = 30) -> int:
        """ Index only administrative boundaries within the given rank range.
        """
        total = 0
        LOG.warning("Starting indexing boundaries using %s threads",
                    self.num_threads)

        minrank = max(minrank, 4)
        maxrank = min(maxrank, 25)

        # Precompute number of rows to process for all rows
        with connect(self.dsn) as conn:
            hstore_info = psycopg.types.TypeInfo.fetch(conn, "hstore")
            if hstore_info is None:
                raise RuntimeError('Hstore extension is requested but not installed.')
            psycopg.types.hstore.register_hstore(hstore_info)

            with conn.cursor() as cur:
                cur = conn.execute(""" SELECT rank_search, count(*)
                                       FROM placex
                                       WHERE rank_search between %s and %s
                                             AND class = 'boundary' and type = 'administrative'
                                             AND indexed_status > 0
                                       GROUP BY rank_search""",
                                   (minrank, maxrank))
                total_tuples = {row.rank_search: row.count for row in cur}

        with self.tokenizer.name_analyzer() as analyzer:
            for rank in range(minrank, maxrank + 1):
                total += await self._index(runners.BoundaryRunner(rank, analyzer),
                                           total_tuples=total_tuples.get(rank, 0))

        return total

    async def index_by_rank(self, minrank: int, maxrank: int) -> int:
        """ Index all entries of placex in the given rank range (inclusive)
            in order of their address rank.

            When rank 30 is requested then also interpolations and
            places with address rank 0 will be indexed.
        """
        total = 0
        maxrank = min(maxrank, 30)
        LOG.warning("Starting indexing rank (%i to %i) using %i threads",
                    minrank, maxrank, self.num_threads)

        # Precompute number of rows to process for all rows
        with connect(self.dsn) as conn:
            hstore_info = psycopg.types.TypeInfo.fetch(conn, "hstore")
            if hstore_info is None:
                raise RuntimeError('Hstore extension is requested but not installed.')
            psycopg.types.hstore.register_hstore(hstore_info)

            with conn.cursor() as cur:
                cur = conn.execute(""" SELECT rank_address, count(*)
                                       FROM placex
                                       WHERE rank_address between %s and %s
                                             AND indexed_status > 0
                                       GROUP BY rank_address""",
                                   (minrank, maxrank))
                total_tuples = {row.rank_address: row.count for row in cur}

        with self.tokenizer.name_analyzer() as analyzer:
            for rank in range(max(1, minrank), maxrank + 1):
                if rank >= 30:
                    batch = 20
                elif rank >= 26:
                    batch = 5
                else:
                    batch = 1
                total += await self._index(runners.RankRunner(rank, analyzer),
                                           batch=batch, total_tuples=total_tuples.get(rank, 0))

            # Special case: rank zero depends on ranks [1..30]
            if minrank == 0:
                total += await self._index(runners.RankRunner(0, analyzer))

            if maxrank == 30:
                total += await self._index(runners.InterpolationRunner(analyzer), batch=20)

        return total

    async def index_postcodes(self) -> int:
        """Index the entries of the location_postcodes table.
        """
        LOG.warning("Starting indexing postcodes using %s threads", self.num_threads)

        return await self._index(runners.PostcodeRunner(), batch=20)

    def update_status_table(self) -> None:
        """ Update the status in the status table to 'indexed'.
        """
        with connect(self.dsn) as conn:
            with conn.cursor() as cur:
                cur.execute('UPDATE import_status SET indexed = true')

            conn.commit()

    async def _index(self, runner: runners.Runner, batch: int = 1,
                     total_tuples: Optional[int] = None) -> int:
        """ Index a single rank or table. `runner` describes the SQL to use
            for indexing. `batch` describes the number of objects that
            should be processed with a single SQL statement.

            `total_tuples` may contain the total number of rows to process.
            When not supplied, the value will be computed using the
            appropriate runner function.
        """
        LOG.warning("Starting %s (using batch size %s)", runner.name(), batch)

        if total_tuples is None:
            total_tuples = self._prepare_indexing(runner)

        progress = ProgressLogger(runner.name(), total_tuples)

        if total_tuples > 0:
            async with await psycopg.AsyncConnection.connect(
                                 self.dsn, row_factory=psycopg.rows.dict_row) as aconn, \
                       QueryPool(self.dsn, self.num_threads, autocommit=True) as pool:
                fetcher_time = 0.0
                tstart = time.time()
                async with aconn.cursor(name='places') as cur:
                    query = runner.index_places_query(batch)
                    params: List[Any] = []
                    num_places = 0
                    async for place in cur.stream(runner.sql_get_objects()):
                        fetcher_time += time.time() - tstart

                        params.extend(runner.index_places_params(place))
                        num_places += 1

                        if num_places >= batch:
                            LOG.debug("Processing places: %s", str(params))
                            await pool.put_query(query, params)
                            progress.add(num_places)
                            params = []
                            num_places = 0

                        tstart = time.time()

                if num_places > 0:
                    await pool.put_query(runner.index_places_query(num_places), params)

            LOG.info("Wait time: fetcher: %.2fs,  pool: %.2fs",
                     fetcher_time, pool.wait_time)

        return progress.done()

    def _prepare_indexing(self, runner: runners.Runner) -> int:
        with connect(self.dsn) as conn:
            hstore_info = psycopg.types.TypeInfo.fetch(conn, "hstore")
            if hstore_info is None:
                raise RuntimeError('Hstore extension is requested but not installed.')
            psycopg.types.hstore.register_hstore(hstore_info)

            total_tuples = execute_scalar(conn, runner.sql_count_objects())
            LOG.debug("Total number of rows: %i", total_tuples)
        return cast(int, total_tuples)


================================================
FILE: src/nominatim_db/indexer/progress.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Helpers for progress logging.
"""
import logging
from datetime import datetime

LOG = logging.getLogger()

INITIAL_PROGRESS = 10


class ProgressLogger:
    """ Tracks and prints progress for the indexing process.
        `name` is the name of the indexing step being tracked.
        `total` sets up the total number of items that need processing.
        `log_interval` denotes the interval in seconds at which progress
        should be reported.
    """

    def __init__(self, name: str, total: int, log_interval: int = 1) -> None:
        self.name = name
        self.total_places = total
        self.done_places = 0
        self.rank_start_time = datetime.now()
        self.log_interval = log_interval
        self.next_info = INITIAL_PROGRESS if LOG.isEnabledFor(logging.WARNING) else total + 1

    def add(self, num: int = 1) -> None:
        """ Mark `num` places as processed. Print a log message if the
            logging is at least info and the log interval has passed.
        """
        self.done_places += num

        if self.done_places < self.next_info:
            return

        now = datetime.now()
        done_time = (now - self.rank_start_time).total_seconds()

        if done_time < 2:
            self.next_info = self.done_places + INITIAL_PROGRESS
            return

        places_per_sec = self.done_places / done_time
        eta = (self.total_places - self.done_places) / places_per_sec

        LOG.warning("Done %d in %.0f @ %.3f per second - %s ETA (seconds): %.2f",
                    self.done_places, done_time,
                    places_per_sec, self.name, eta)

        self.next_info += int(places_per_sec) * self.log_interval

    def done(self) -> int:
        """ Print final statistics about the progress.
        """
        rank_end_time = datetime.now()

        if rank_end_time == self.rank_start_time:
            diff_seconds = 0.0
            places_per_sec = float(self.done_places)
        else:
            diff_seconds = (rank_end_time - self.rank_start_time).total_seconds()
            places_per_sec = self.done_places / diff_seconds

        LOG.warning("Done %d/%d in %.0f @ %.3f per second - FINISHED %s\n",
                    self.done_places, self.total_places, diff_seconds,
                    places_per_sec, self.name)

        return self.done_places


================================================
FILE: src/nominatim_db/indexer/runners.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Mix-ins that provide the actual commands for the indexer for various indexing
tasks.
"""
from typing import Any, Sequence

from psycopg import sql as pysql
from psycopg.abc import Query
from psycopg.rows import DictRow
from psycopg.types.json import Json

from ..typing import Protocol
from ..data.place_info import PlaceInfo
from ..tokenizer.base import AbstractAnalyzer


def _mk_valuelist(template: str, num: int) -> pysql.Composed:
    return pysql.SQL(',').join([pysql.SQL(template)] * num)


def _analyze_place(place: DictRow, analyzer: AbstractAnalyzer) -> Json:
    return Json(analyzer.process_place(PlaceInfo(place)))


class Runner(Protocol):
    def name(self) -> str: ...
    def sql_count_objects(self) -> Query: ...
    def sql_get_objects(self) -> Query: ...
    def index_places_query(self, batch_size: int) -> Query: ...
    def index_places_params(self, place: DictRow) -> Sequence[Any]: ...


SELECT_SQL = pysql.SQL("""SELECT place_id, extra.*
                          FROM (SELECT * FROM placex {}) as px,
                          LATERAL placex_indexing_prepare(px) as extra """)
UPDATE_LINE = "(%s, %s::hstore, %s::hstore, %s::int, %s::jsonb)"


class AbstractPlacexRunner:
    """ Returns SQL commands for indexing of the placex table.
    """

    def __init__(self, rank: int, analyzer: AbstractAnalyzer) -> None:
        self.rank = rank
        self.analyzer = analyzer

    def index_places_query(self, batch_size: int) -> Query:
        return pysql.SQL(
            """ UPDATE placex
                SET indexed_status = 0, address = v.addr, token_info = v.ti,
                    name = v.name, linked_place_id = v.linked_place_id
                FROM (VALUES {}) as v(id, name, addr, linked_place_id, ti)
                WHERE place_id = v.id
            """).format(_mk_valuelist(UPDATE_LINE, batch_size))

    def index_places_params(self, place: DictRow) -> Sequence[Any]:
        return (place['place_id'],
                place['name'],
                place['address'],
                place['linked_place_id'],
                _analyze_place(place, self.analyzer))


class RankRunner(AbstractPlacexRunner):
    """ Returns SQL commands for indexing one rank within the placex table.
    """

    def name(self) -> str:
        return f"rank {self.rank}"

    def sql_count_objects(self) -> pysql.Composed:
        return pysql.SQL("""SELECT count(*) FROM placex
                            WHERE rank_address = {} and indexed_status > 0
                         """).format(pysql.Literal(self.rank))

    def sql_get_objects(self) -> pysql.Composed:
        return SELECT_SQL.format(pysql.SQL(
                """WHERE placex.indexed_status > 0 and placex.rank_address = {}
                   ORDER BY placex.geometry_sector
                """).format(pysql.Literal(self.rank)))


class BoundaryRunner(AbstractPlacexRunner):
    """ Returns SQL commands for indexing the administrative boundaries
        of a certain rank.
    """

    def name(self) -> str:
        return f"boundaries rank {self.rank}"

    def sql_count_objects(self) -> Query:
        return pysql.SQL("""SELECT count(*) FROM placex
                            WHERE indexed_status > 0
                              AND rank_search = {}
                              AND class = 'boundary' and type = 'administrative'
                         """).format(pysql.Literal(self.rank))

    def sql_get_objects(self) -> Query:
        return SELECT_SQL.format(pysql.SQL(
                """WHERE placex.indexed_status > 0 and placex.rank_search = {}
                         and placex.class = 'boundary' and placex.type = 'administrative'
                   ORDER BY placex.partition, placex.admin_level
                """).format(pysql.Literal(self.rank)))


class InterpolationRunner:
    """ Returns SQL commands for indexing the address interpolation table
        location_property_osmline.
    """

    def __init__(self, analyzer: AbstractAnalyzer) -> None:
        self.analyzer = analyzer

    def name(self) -> str:
        return "interpolation lines (location_property_osmline)"

    def sql_count_objects(self) -> Query:
        return """SELECT count(*) FROM location_property_osmline
                  WHERE indexed_status > 0"""

    def sql_get_objects(self) -> Query:
        return """SELECT place_id, get_interpolation_address(address, osm_id) as address
                  FROM location_property_osmline
                  WHERE indexed_status > 0
                  ORDER BY geometry_sector"""

    def index_places_query(self, batch_size: int) -> Query:
        return pysql.SQL("""UPDATE location_property_osmline
                            SET indexed_status = 0, address = v.addr, token_info = v.ti
                            FROM (VALUES {}) as v(id, addr, ti)
                            WHERE place_id = v.id
                         """).format(_mk_valuelist("(%s, %s::hstore, %s::jsonb)", batch_size))

    def index_places_params(self, place: DictRow) -> Sequence[Any]:
        return (place['place_id'], place['address'],
                _analyze_place(place, self.analyzer))


class PostcodeRunner(Runner):
    """ Provides the SQL commands for indexing the location_postcodes table.
    """

    def name(self) -> str:
        return "postcodes (location_postcodes)"

    def sql_count_objects(self) -> Query:
        return 'SELECT count(*) FROM location_postcodes WHERE indexed_status > 0'

    def sql_get_objects(self) -> Query:
        return """SELECT place_id FROM location_postcodes
                  WHERE indexed_status > 0
                  ORDER BY country_code, postcode"""

    def index_places_query(self, batch_size: int) -> Query:
        return pysql.SQL("""UPDATE location_postcodes SET indexed_status = 0
                                    WHERE place_id IN ({})""")\
                    .format(pysql.SQL(',').join((pysql.Placeholder() for _ in range(batch_size))))

    def index_places_params(self, place: DictRow) -> Sequence[Any]:
        return (place['place_id'], )


================================================
FILE: src/nominatim_db/paths.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Path settings for extra data used by Nominatim.
"""
from pathlib import Path

SQLLIB_DIR = (Path(__file__) / '..' / '..' / '..' / 'lib-sql').resolve()
LUALIB_DIR = (Path(__file__) / '..' / '..' / '..' / 'lib-lua').resolve()
DATA_DIR = (Path(__file__) / '..' / '..' / '..' / 'data').resolve()
CONFIG_DIR = (Path(__file__) / '..' / '..' / '..' / 'settings').resolve()


================================================
FILE: src/nominatim_db/tokenizer/__init__.py
================================================


================================================
FILE: src/nominatim_db/tokenizer/base.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Abstract class definitions for tokenizers. These base classes are here
mainly for documentation purposes.
"""
from abc import ABC, abstractmethod
from typing import List, Tuple, Dict, Any, Optional, Iterable

from ..typing import Protocol
from ..config import Configuration
from ..db.connection import Connection
from ..data.place_info import PlaceInfo


class AbstractAnalyzer(ABC):
    """ The analyzer provides the functions for analysing names and building
        the token database.

        Analyzers are instantiated on a per-thread base. Access to global data
        structures must be synchronised accordingly.
    """

    def __enter__(self) -> 'AbstractAnalyzer':
        return self

    def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
        self.close()

    @abstractmethod
    def close(self) -> None:
        """ Free all resources used by the analyzer.
        """

    @abstractmethod
    def get_word_token_info(self, words: List[str]) -> List[Tuple[str, str, Optional[int]]]:
        """ Return token information for the given list of words.

            The function is used for testing and debugging only
            and does not need to be particularly efficient.

            Arguments:
                words: A list of words to look up the tokens for.
                       If a word starts with # it is assumed to be a full name
                       otherwise is a partial term.

            Returns:
                The function returns the list of all tuples that could be
                    found for the given words. Each list entry is a tuple of
                    (original word, word token, word id).
        """

    @abstractmethod
    def normalize_postcode(self, postcode: str) -> str:
        """ Convert the postcode to its standardized form.

            This function must yield exactly the same result as the SQL function
            `token_normalized_postcode()`.

            Arguments:
                postcode: The postcode to be normalized.

            Returns:
                The given postcode after normalization.
        """

    @abstractmethod
    def update_postcodes_from_db(self) -> None:
        """ Update the tokenizer's postcode tokens from the current content
            of the `location_postcodes` table.
        """

    @abstractmethod
    def update_special_phrases(self,
                               phrases: Iterable[Tuple[str, str, str, str]],
                               should_replace: bool) -> None:
        """ Update the tokenizer's special phrase tokens from the given
            list of special phrases.

            Arguments:
                phrases: The new list of special phrases. Each entry is
                         a tuple of (phrase, class, type, operator).
                should_replace: If true, replace the current list of phrases.
                                When false, just add the given phrases to the
                                ones that already exist.
        """

    @abstractmethod
    def add_country_names(self, country_code: str, names: Dict[str, str]) -> None:
        """ Add the given names to the tokenizer's list of country tokens.

            Arguments:
                country_code: two-letter country code for the country the names
                              refer to.
                names: Dictionary of name type to name.
        """

    @abstractmethod
    def process_place(self, place: PlaceInfo) -> Any:
        """ Extract tokens for the given place and compute the
            information to be handed to the PL/pgSQL processor for building
            the search index.

            Arguments:
                place: Place information retrieved from the database.

            Returns:
                A JSON-serialisable structure that will be handed into
                    the database via the `token_info` field.
        """


class AbstractTokenizer(ABC):
    """ The tokenizer instance is the central instance of the tokenizer in
        the system. There will only be a single instance of the tokenizer
        active at any time.
    """

    @abstractmethod
    def init_new_db(self, config: Configuration, init_db: bool = True) -> None:
        """ Set up a new tokenizer for the database.

            The function should copy all necessary data into the project
            directory or save it in the property table to make sure that
            the tokenizer remains stable over updates.

            Arguments:
              config: Read-only object with configuration options.

              init_db: When set to False, then initialisation of database
                tables should be skipped. This option is only required for
                migration purposes and can be safely ignored by custom
                tokenizers.
        """

    @abstractmethod
    def init_from_project(self, config: Configuration) -> None:
        """ Initialise the tokenizer from an existing database setup.

            The function should load all previously saved configuration from
            the project directory and/or the property table.

            Arguments:
              config: Read-only object with configuration options.
        """

    @abstractmethod
    def finalize_import(self, config: Configuration) -> None:
        """ This function is called at the very end of an import when all
            data has been imported and indexed. The tokenizer may create
            at this point any additional indexes and data structures needed
            during query time.

            Arguments:
              config: Read-only object with configuration options.
        """

    @abstractmethod
    def update_sql_functions(self, config: Configuration) -> None:
        """ Update the SQL part of the tokenizer. This function is called
            automatically on migrations or may be called explicitly by the
            user through the `nominatim refresh --functions` command.

            The tokenizer must only update the code of the tokenizer. The
            data structures or data itself must not be changed by this function.

            Arguments:
              config: Read-only object with configuration options.
        """

    @abstractmethod
    def check_database(self, config: Configuration) -> Optional[str]:
        """ Check that the database is set up correctly and ready for being
            queried.

            Arguments:
              config: Read-only object with configuration options.

            Returns:
              If an issue was found, return an error message with the
                  description of the issue as well as hints for the user on
                  how to resolve the issue. If everything is okay, return `None`.
        """

    @abstractmethod
    def update_statistics(self, config: Configuration, threads: int = 1) -> None:
        """ Recompute any tokenizer statistics necessary for efficient lookup.
            This function is meant to be called from time to time by the user
            to improve performance. However, the tokenizer must not depend on
            it to be called in order to work.
        """

    @abstractmethod
    def update_word_tokens(self) -> None:
        """ Do house-keeping on the tokenizers internal data structures.
            Remove unused word tokens, resort data etc.
        """

    @abstractmethod
    def name_analyzer(self) -> AbstractAnalyzer:
        """ Create a new analyzer for tokenizing names and queries
            using this tokinzer. Analyzers are context managers and should
            be used accordingly:

            ```
            with tokenizer.name_analyzer() as analyzer:
                analyser.tokenize()
            ```

            When used outside the with construct, the caller must ensure to
            call the close() function before destructing the analyzer.
        """

    @abstractmethod
    def most_frequent_words(self, conn: Connection, num: int) -> List[str]:
        """ Return a list of the most frequent full words in the database.

            Arguments:
              conn: Open connection to the database which may be used to
                    retrieve the words.
              num: Maximum number of words to return.
        """


class TokenizerModule(Protocol):
    """ Interface that must be exported by modules that implement their
        own tokenizer.
    """

    def create(self, dsn: str) -> AbstractTokenizer:
        """ Factory for new tokenizers.
        """


================================================
FILE: src/nominatim_db/tokenizer/factory.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for creating a tokenizer or initialising the right one for an
existing database.

A tokenizer is something that is bound to the lifetime of a database. It
can be chosen and configured before the initial import but then needs to
be used consistently when querying and updating the database.

This module provides the functions to create and configure a new tokenizer
as well as instantiating the appropriate tokenizer for updating an existing
database.
"""
from typing import Optional
import logging
import importlib
from pathlib import Path

from ..errors import UsageError
from ..db import properties
from ..db.connection import connect
from ..config import Configuration
from ..tokenizer.base import AbstractTokenizer, TokenizerModule

LOG = logging.getLogger()


def _import_tokenizer(name: str) -> TokenizerModule:
    """ Load the tokenizer.py module from project directory.
    """
    src_file = Path(__file__).parent / (name + '_tokenizer.py')
    if not src_file.is_file():
        LOG.fatal("No tokenizer named '%s' available. "
                  "Check the setting of NOMINATIM_TOKENIZER.", name)
        raise UsageError('Tokenizer not found')

    return importlib.import_module('nominatim_db.tokenizer.' + name + '_tokenizer')


def create_tokenizer(config: Configuration, init_db: bool = True,
                     module_name: Optional[str] = None) -> AbstractTokenizer:
    """ Create a new tokenizer as defined by the given configuration.

        The tokenizer data and code is copied into the 'tokenizer' directory
        of the project directory and the tokenizer loaded from its new location.
    """
    if module_name is None:
        module_name = config.TOKENIZER

    # Import and initialize the tokenizer.
    tokenizer_module = _import_tokenizer(module_name)

    tokenizer = tokenizer_module.create(config.get_libpq_dsn())
    tokenizer.init_new_db(config, init_db=init_db)

    with connect(config.get_libpq_dsn()) as conn:
        properties.set_property(conn, 'tokenizer', module_name)

    return tokenizer


def get_tokenizer_for_db(config: Configuration) -> AbstractTokenizer:
    """ Instantiate a tokenizer for an existing database.

        The function looks up the appropriate tokenizer in the database
        and initialises it.
    """
    with connect(config.get_libpq_dsn()) as conn:
        name = properties.get_property(conn, 'tokenizer')

    if name is None:
        LOG.fatal("Tokenizer was not set up properly. Database property missing.")
        raise UsageError('Cannot initialize tokenizer.')

    tokenizer_module = _import_tokenizer(name)

    tokenizer = tokenizer_module.create(config.get_libpq_dsn())
    tokenizer.init_from_project(config)

    return tokenizer


================================================
FILE: src/nominatim_db/tokenizer/icu_rule_loader.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Helper class to create ICU rules from a configuration file.
"""
from typing import Mapping, Any, Dict, Optional
import io
import json
import logging

from icu import Transliterator

from ..config import flatten_config_list, Configuration
from ..db.properties import set_property, get_property
from ..db.connection import Connection
from ..errors import UsageError
from .place_sanitizer import PlaceSanitizer
from .icu_token_analysis import ICUTokenAnalysis
from .token_analysis.base import AnalysisModule, Analyzer
from ..data import country_info

LOG = logging.getLogger()

DBCFG_IMPORT_NORM_RULES = "tokenizer_import_normalisation"
DBCFG_IMPORT_TRANS_RULES = "tokenizer_import_transliteration"
DBCFG_IMPORT_ANALYSIS_RULES = "tokenizer_import_analysis_rules"


def _get_section(rules: Mapping[str, Any], section: str) -> Any:
    """ Get the section named 'section' from the rules. If the section does
        not exist, raise a usage error with a meaningful message.
    """
    if section not in rules:
        LOG.fatal("Section '%s' not found in tokenizer config.", section)
        raise UsageError("Syntax error in tokenizer configuration file.")

    return rules[section]


class ICURuleLoader:
    """ Compiler for ICU rules from a tokenizer configuration file.
    """

    def __init__(self, config: Configuration) -> None:
        self.config = config
        rules = config.load_sub_configuration('icu_tokenizer.yaml',
                                              config='TOKENIZER_CONFIG')

        # Make sure country information is available to analyzers and sanitizers.
        country_info.setup_country_config(config)

        self.normalization_rules = self._cfg_to_icu_rules(rules, 'normalization')
        self.transliteration_rules = self._cfg_to_icu_rules(rules, 'transliteration')
        self.analysis_rules = _get_section(rules, 'token-analysis')
        self._setup_analysis()

        # Load optional sanitizer rule set.
        self.sanitizer_rules = rules.get('sanitizers', [])

    def load_config_from_db(self, conn: Connection) -> None:
        """ Get previously saved parts of the configuration from the
            database.
        """
        rules = get_property(conn, DBCFG_IMPORT_NORM_RULES)
        if rules is not None:
            self.normalization_rules = rules

        rules = get_property(conn, DBCFG_IMPORT_TRANS_RULES)
        if rules is not None:
            self.transliteration_rules = rules

        rules = get_property(conn, DBCFG_IMPORT_ANALYSIS_RULES)
        if rules:
            self.analysis_rules = json.loads(rules)
        else:
            self.analysis_rules = []
        self._setup_analysis()

    def save_config_to_db(self, conn: Connection) -> None:
        """ Save the part of the configuration that cannot be changed into
            the database.
        """
        set_property(conn, DBCFG_IMPORT_NORM_RULES, self.normalization_rules)
        set_property(conn, DBCFG_IMPORT_TRANS_RULES, self.transliteration_rules)
        set_property(conn, DBCFG_IMPORT_ANALYSIS_RULES, json.dumps(self.analysis_rules))

    def make_sanitizer(self) -> PlaceSanitizer:
        """ Create a place sanitizer from the configured rules.
        """
        return PlaceSanitizer(self.sanitizer_rules, self.config)

    def make_token_analysis(self) -> ICUTokenAnalysis:
        """ Create a token analyser from the reviouly loaded rules.
        """
        return ICUTokenAnalysis(self.normalization_rules,
                                self.transliteration_rules, self.analysis)

    def get_search_rules(self) -> str:
        """ Return the ICU rules to be used during search.
            The rules combine normalization and transliteration.
        """
        # First apply the normalization rules.
        rules = io.StringIO()
        rules.write(self.normalization_rules)

        # Then add transliteration.
        rules.write(self.transliteration_rules)
        return rules.getvalue()

    def get_normalization_rules(self) -> str:
        """ Return rules for normalisation of a term.
        """
        return self.normalization_rules

    def get_transliteration_rules(self) -> str:
        """ Return the rules for converting a string into its asciii representation.
        """
        return self.transliteration_rules

    def _setup_analysis(self) -> None:
        """ Process the rules used for creating the various token analyzers.
        """
        self.analysis: Dict[Optional[str], TokenAnalyzerRule] = {}

        if not isinstance(self.analysis_rules, list):
            raise UsageError("Configuration section 'token-analysis' must be a list.")

        norm = Transliterator.createFromRules("rule_loader_normalization",
                                              self.normalization_rules)
        trans = Transliterator.createFromRules("rule_loader_transliteration",
                                               self.transliteration_rules)

        for section in self.analysis_rules:
            name = section.get('id', None)
            if name in self.analysis:
                if name is None:
                    LOG.fatal("ICU tokenizer configuration has two default token analyzers.")
                else:
                    LOG.fatal("ICU tokenizer configuration has two token "
                              "analyzers with id '%s'.", name)
                raise UsageError("Syntax error in ICU tokenizer config.")
            self.analysis[name] = TokenAnalyzerRule(section, norm, trans,
                                                    self.config)

    @staticmethod
    def _cfg_to_icu_rules(rules: Mapping[str, Any], section: str) -> str:
        """ Load an ICU ruleset from the given section. If the section is a
            simple string, it is interpreted as a file name and the rules are
            loaded verbatim from the given file. The filename is expected to be
            relative to the tokenizer rule file. If the section is a list then
            each line is assumed to be a rule. All rules are concatenated and returned.
        """
        content = _get_section(rules, section)

        if content is None:
            return ''

        return ';'.join(flatten_config_list(content, section)) + ';'


class TokenAnalyzerRule:
    """ Factory for a single analysis module. The class saves the configuration
        and creates a new token analyzer on request.
    """

    def __init__(self, rules: Mapping[str, Any],
                 normalizer: Any, transliterator: Any,
                 config: Configuration) -> None:
        analyzer_name = _get_section(rules, 'analyzer')
        if not analyzer_name or not isinstance(analyzer_name, str):
            raise UsageError("'analyzer' parameter needs to be simple string")

        self._analysis_mod: AnalysisModule = \
            config.load_plugin_module(analyzer_name, 'nominatim_db.tokenizer.token_analysis')

        self.config = self._analysis_mod.configure(rules, normalizer,
                                                   transliterator)

    def create(self, normalizer: Any, transliterator: Any) -> Analyzer:
        """ Create a new analyser instance for the given rule.
        """
        return self._analysis_mod.create(normalizer, transliterator, self.config)


================================================
FILE: src/nominatim_db/tokenizer/icu_token_analysis.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Container class collecting all components required to transform an OSM name
into a Nominatim token.
"""
from typing import Mapping, Optional, TYPE_CHECKING
from icu import Transliterator

from .token_analysis.base import Analyzer

if TYPE_CHECKING:
    from typing import Any  # noqa
    from .icu_rule_loader import TokenAnalyzerRule


class ICUTokenAnalysis:
    """ Container class collecting the transliterators and token analysis
        modules for a single Analyser instance.
    """

    def __init__(self, norm_rules: str, trans_rules: str,
                 analysis_rules: Mapping[Optional[str], 'TokenAnalyzerRule']):
        # additional break signs are not relevant during name analysis
        norm_rules += ";[[:Space:][-:]]+ > ' ';"
        self.normalizer = Transliterator.createFromRules("icu_normalization",
                                                         norm_rules)
        trans_rules += ";[:Space:]+ > ' '"
        self.to_ascii = Transliterator.createFromRules("icu_to_ascii",
                                                       trans_rules)
        self.search = Transliterator.createFromRules("icu_search",
                                                     norm_rules + trans_rules)

        self.analysis = {name: arules.create(self.normalizer, self.to_ascii)
                         for name, arules in analysis_rules.items()}

    def get_analyzer(self, name: Optional[str]) -> Analyzer:
        """ Return the given named analyzer. If no analyzer with that
            name exists, return the default analyzer.
        """
        return self.analysis.get(name) or self.analysis[None]


================================================
FILE: src/nominatim_db/tokenizer/icu_tokenizer.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Tokenizer implementing normalisation as used before Nominatim 4 but using
libICU instead of the PostgreSQL module.
"""
from typing import Optional, Sequence, List, Tuple, Mapping, Any, cast, \
                   Dict, Set, Iterable
import itertools
import logging

from psycopg.types.json import Jsonb
from psycopg import sql as pysql

from ..db.connection import connect, Connection, Cursor, \
                            drop_tables, table_exists, execute_scalar
from ..config import Configuration
from ..db.sql_preprocessor import SQLPreprocessor
from ..data.place_info import PlaceInfo
from ..data.place_name import PlaceName
from .icu_rule_loader import ICURuleLoader
from .place_sanitizer import PlaceSanitizer
from .icu_token_analysis import ICUTokenAnalysis
from .base import AbstractAnalyzer, AbstractTokenizer

DBCFG_TERM_NORMALIZATION = "tokenizer_term_normalization"

LOG = logging.getLogger()

WORD_TYPES = (('country_names', 'C'),
              ('postcodes', 'P'),
              ('full_word', 'W'),
              ('housenumbers', 'H'))


def create(dsn: str) -> 'ICUTokenizer':
    """ Create a new instance of the tokenizer provided by this module.
    """
    return ICUTokenizer(dsn)


class ICUTokenizer(AbstractTokenizer):
    """ This tokenizer uses libICU to convert names and queries to ASCII.
        Otherwise it uses the same algorithms and data structures as the
        normalization routines in Nominatim 3.
    """

    def __init__(self, dsn: str) -> None:
        self.dsn = dsn
        self.loader: Optional[ICURuleLoader] = None

    def init_new_db(self, config: Configuration, init_db: bool = True) -> None:
        """ Set up a new tokenizer for the database.

            This copies all necessary data in the project directory to make
            sure the tokenizer remains stable even over updates.
        """
        self.loader = ICURuleLoader(config)

        self._save_config()

        if init_db:
            self.update_sql_functions(config)
            self._setup_db_tables(config)
            self._create_base_indices(config, 'word')

    def init_from_project(self, config: Configuration) -> None:
        """ Initialise the tokenizer from the project directory.
        """
        self.loader = ICURuleLoader(config)

        with connect(self.dsn) as conn:
            self.loader.load_config_from_db(conn)

    def finalize_import(self, config: Configuration) -> None:
        """ Do any required postprocessing to make the tokenizer data ready
            for use.
        """
        self._create_lookup_indices(config, 'word')

    def update_sql_functions(self, config: Configuration) -> None:
        """ Reimport the SQL functions for this tokenizer.
        """
        with connect(self.dsn) as conn:
            sqlp = SQLPreprocessor(conn, config)
            sqlp.run_sql_file(conn, 'tokenizer/icu_tokenizer.sql')

    def check_database(self, config: Configuration) -> None:
        """ Check that the tokenizer is set up correctly.
        """
        # Will throw an error if there is an issue.
        self.init_from_project(config)

    def update_statistics(self, config: Configuration, threads: int = 2) -> None:
        """ Recompute frequencies for all name words.
        """
        with connect(self.dsn) as conn:
            if not table_exists(conn, 'search_name'):
                return

            with conn.cursor() as cur:
                cur.execute('ANALYSE search_name')
                if threads > 1:
                    cur.execute(pysql.SQL('SET max_parallel_workers_per_gather TO {}')
                                     .format(pysql.Literal(min(threads, 6),)))

                LOG.info('Computing word frequencies')
                drop_tables(conn, 'word_frequencies')
                cur.execute("""
                  CREATE TEMP TABLE word_frequencies AS
                  WITH word_freq AS MATERIALIZED (
                           SELECT unnest(name_vector) as id, count(*)
                                 FROM search_name GROUP BY id),
                       addr_freq AS MATERIALIZED (
                           SELECT unnest(nameaddress_vector) as id, count(*)
                                 FROM search_name GROUP BY id)
                  SELECT coalesce(a.id, w.id) as id,
                         (CASE WHEN w.count is null or w.count <= 1 THEN '{}'::JSONB
                              ELSE jsonb_build_object('count', w.count) END
                          ||
                          CASE WHEN a.count is null or a.count <= 1 THEN '{}'::JSONB
                              ELSE jsonb_build_object('addr_count', a.count) END) as info
                  FROM word_freq w FULL JOIN addr_freq a ON a.id = w.id;
                  """)
                cur.execute('CREATE UNIQUE INDEX ON word_frequencies(id) INCLUDE(info)')
                cur.execute('ANALYSE word_frequencies')
                LOG.info('Update word table with recomputed frequencies')
                drop_tables(conn, 'tmp_word')
                cur.execute("""CREATE TABLE tmp_word AS
                                SELECT word_id, word_token, type, word,
                                       coalesce(word.info, '{}'::jsonb)
                                       - 'count' - 'addr_count' ||
                                       coalesce(wf.info, '{}'::jsonb)
                                       as info
                                FROM word LEFT JOIN word_frequencies wf
                                     ON word.word_id = wf.id
                            """)
                drop_tables(conn, 'word_frequencies')

            with conn.cursor() as cur:
                cur.execute('SET max_parallel_workers_per_gather TO 0')

            sqlp = SQLPreprocessor(conn, config)
            sqlp.run_string(conn,
                            'GRANT SELECT ON tmp_word TO "{{config.DATABASE_WEBUSER}}"')
            conn.commit()
        self._create_base_indices(config, 'tmp_word')
        self._create_lookup_indices(config, 'tmp_word')
        self._move_temporary_word_table('tmp_word')

    def _cleanup_housenumbers(self) -> None:
        """ Remove unused house numbers.
        """
        with connect(self.dsn) as conn:
            if not table_exists(conn, 'search_name'):
                return
            with conn.cursor(name="hnr_counter") as cur:
                cur.execute("""SELECT DISTINCT word_id, coalesce(info->>'lookup', word_token)
                               FROM word
                               WHERE type = 'H'
                                 AND NOT EXISTS(SELECT * FROM search_name
                                                WHERE ARRAY[word.word_id] && name_vector)
                                 AND (char_length(coalesce(word, word_token)) > 6
                                      OR coalesce(word, word_token) not similar to '\\d+')
                            """)
                candidates = {token: wid for wid, token in cur}
            with conn.cursor(name="hnr_counter") as cur:
                cur.execute("""SELECT housenumber FROM placex
                               WHERE housenumber is not null
                                     AND (char_length(housenumber) > 6
                                          OR housenumber not similar to '\\d+')
                            """)
                for row in cur:
                    for hnr in row[0].split(';'):
                        candidates.pop(hnr, None)
            LOG.info("There are %s outdated housenumbers.", len(candidates))
            LOG.debug("Outdated housenumbers: %s", candidates.keys())
            if candidates:
                with conn.cursor() as cur:
                    cur.execute("""DELETE FROM word WHERE word_id = any(%s)""",
                                (list(candidates.values()), ))
                conn.commit()

    def update_word_tokens(self) -> None:
        """ Remove unused tokens.
        """
        LOG.warning("Cleaning up housenumber tokens.")
        self._cleanup_housenumbers()
        LOG.warning("Tokenizer house-keeping done.")

    def name_analyzer(self) -> 'ICUNameAnalyzer':
        """ Create a new analyzer for tokenizing names and queries
            using this tokinzer. Analyzers are context managers and should
            be used accordingly:

            ```
            with tokenizer.name_analyzer() as analyzer:
                analyser.tokenize()
            ```

            When used outside the with construct, the caller must ensure to
            call the close() function before destructing the analyzer.

            Analyzers are not thread-safe. You need to instantiate one per thread.
        """
        assert self.loader is not None
        return ICUNameAnalyzer(self.dsn, self.loader.make_sanitizer(),
                               self.loader.make_token_analysis())

    def most_frequent_words(self, conn: Connection, num: int) -> List[str]:
        """ Return a list of the `num` most frequent full words
            in the database.
        """
        with conn.cursor() as cur:
            cur.execute("""SELECT word, sum((info->>'count')::int) as count
                             FROM word WHERE type = 'W'
                             GROUP BY word
                             ORDER BY count DESC LIMIT %s""", (num,))
            return list(s[0].split('@')[0] for s in cur)

    def _save_config(self) -> None:
        """ Save the configuration that needs to remain stable for the given
            database as database properties.
        """
        assert self.loader is not None
        with connect(self.dsn) as conn:
            self.loader.save_config_to_db(conn)

    def _setup_db_tables(self, config: Configuration) -> None:
        """ Set up the word table and fill it with pre-computed word
            frequencies.
        """
        with connect(self.dsn) as conn:
            drop_tables(conn, 'word')
            sqlp = SQLPreprocessor(conn, config)
            sqlp.run_string(conn, """
                CREATE TABLE word (
                      word_id INTEGER,
                      word_token text NOT NULL,
                      type text NOT NULL,
                      word text,
                      info jsonb
                    ) {{db.tablespace.search_data}};
                GRANT SELECT ON word TO "{{config.DATABASE_WEBUSER}}";

                DROP SEQUENCE IF EXISTS seq_word;
                CREATE SEQUENCE seq_word start 1;
                GRANT SELECT ON seq_word to "{{config.DATABASE_WEBUSER}}";
            """)
            conn.commit()

    def _create_base_indices(self, config: Configuration, table_name: str) -> None:
        """ Set up the word table and fill it with pre-computed word
            frequencies.
        """
        with connect(self.dsn) as conn:
            sqlp = SQLPreprocessor(conn, config)
            sqlp.run_string(conn,
                            """CREATE INDEX idx_{{table_name}}_word_token ON {{table_name}}
                               USING BTREE (word_token) {{db.tablespace.search_index}}""",
                            table_name=table_name)
            for name, ctype in WORD_TYPES:
                sqlp.run_string(conn,
                                """CREATE INDEX idx_{{table_name}}_{{idx_name}} ON {{table_name}}
                                   USING BTREE (word) {{db.tablespace.address_index}}
                                   WHERE type = '{{column_type}}'
                                """,
                                table_name=table_name, idx_name=name,
                                column_type=ctype)
            conn.commit()

    def _create_lookup_indices(self, config: Configuration, table_name: str) -> None:
        """ Create additional indexes used when running the API.
        """
        with connect(self.dsn) as conn:
            sqlp = SQLPreprocessor(conn, config)
            # Index required for details lookup.
            sqlp.run_string(
                conn,
                """
                CREATE INDEX IF NOT EXISTS idx_{{table_name}}_word_id
                  ON {{table_name}} USING BTREE (word_id) {{db.tablespace.search_index}}
                """,
                table_name=table_name)
            conn.commit()

    def _move_temporary_word_table(self, old: str) -> None:
        """ Rename all tables and indexes used by the tokenizer.
        """
        with connect(self.dsn) as conn:
            drop_tables(conn, 'word')
            with conn.cursor() as cur:
                cur.execute(pysql.SQL("ALTER TABLE {} RENAME TO word")
                                 .format(pysql.Identifier(old)))
                for idx in ['word_token', 'word_id'] + [n[0] for n in WORD_TYPES]:
                    cur.execute(pysql.SQL("ALTER INDEX {} RENAME TO {}")
                                     .format(pysql.Identifier(f"idx_{old}_{idx}"),
                                             pysql.Identifier(f"idx_word_{idx}")))
            conn.commit()


class ICUNameAnalyzer(AbstractAnalyzer):
    """ The ICU analyzer uses the ICU library for splitting names.

        Each instance opens a connection to the database to request the
        normalization.
    """

    def __init__(self, dsn: str, sanitizer: PlaceSanitizer,
                 token_analysis: ICUTokenAnalysis) -> None:
        self.conn: Optional[Connection] = connect(dsn)
        self.conn.autocommit = True
        self.sanitizer = sanitizer
        self.token_analysis = token_analysis

        self._cache = _TokenCache()

    def close(self) -> None:
        """ Free all resources used by the analyzer.
        """
        if self.conn:
            self.conn.close()
            self.conn = None

    def _search_normalized(self, name: str) -> str:
        """ Return the search token transliteration of the given name.
        """
        return cast(str, self.token_analysis.search.transliterate(name)).strip()

    def _normalized(self, name: str) -> str:
        """ Return the normalized version of the given name with all
            non-relevant information removed.
        """
        return cast(str, self.token_analysis.normalizer.transliterate(name)).strip()

    def get_word_token_info(self, words: Sequence[str]) -> List[Tuple[str, str, Optional[int]]]:
        """ Return token information for the given list of words.
            If a word starts with # it is assumed to be a full name
            otherwise is a partial name.

            The function returns a list of tuples with
            (original word, word token, word id).

            The function is used for testing and debugging only
            and not necessarily efficient.
        """
        assert self.conn is not None
        full_tokens = {}
        partial_tokens = {}
        for word in words:
            if word.startswith('#'):
                full_tokens[word] = self._search_normalized(word[1:])
            else:
                partial_tokens[word] = self._search_normalized(word)

        with self.conn.cursor() as cur:
            cur.execute("""SELECT word_token, word_id
                            FROM word WHERE word_token = ANY(%s) and type = 'W'
                        """, (list(full_tokens.values()),))
            full_ids = {r[0]: cast(int, r[1]) for r in cur}
            cur.execute("""SELECT word_token, word_id
                            FROM word WHERE word_token = ANY(%s) and type = 'w'""",
                        (list(partial_tokens.values()),))
            part_ids = {r[0]: cast(int, r[1]) for r in cur}

        return [(k, v, full_ids.get(v, None)) for k, v in full_tokens.items()] \
            + [(k, v, part_ids.get(v, None)) for k, v in partial_tokens.items()]

    def normalize_postcode(self, postcode: str) -> str:
        """ Convert the postcode to a standardized form.

            This function must yield exactly the same result as the SQL function
            'token_normalized_postcode()'.
        """
        return postcode.strip().upper()

    def update_postcodes_from_db(self) -> None:
        """ Postcode update.

            Removes all postcodes from the word table because they are not
            needed. Postcodes are recognised by pattern.
        """
        assert self.conn is not None

        with self.conn.cursor() as cur:
            cur.execute("DELETE FROM word WHERE type = 'P'")

    def update_special_phrases(self, phrases: Iterable[Tuple[str, str, str, str]],
                               should_replace: bool) -> None:
        """ Replace the search index for special phrases with the new phrases.
            If `should_replace` is True, then the previous set of will be
            completely replaced. Otherwise the phrases are added to the
            already existing ones.
        """
        assert self.conn is not None
        norm_phrases = set(((self._normalized(p[0]), p[1], p[2], p[3])
                            for p in phrases))

        with self.conn.cursor() as cur:
            # Get the old phrases.
            existing_phrases = set()
            cur.execute("SELECT word, info FROM word WHERE type = 'S'")
            for word, info in cur:
                existing_phrases.add((word, info['class'], info['type'],
                                      info.get('op') or '-'))

            added = self._add_special_phrases(cur, norm_phrases, existing_phrases)
            if should_replace:
                deleted = self._remove_special_phrases(cur, norm_phrases,
                                                       existing_phrases)
            else:
                deleted = 0

        LOG.info("Total phrases: %s. Added: %s. Deleted: %s",
                 len(norm_phrases), added, deleted)

    def _add_special_phrases(self, cursor: Cursor,
                             new_phrases: Set[Tuple[str, str, str, str]],
                             existing_phrases: Set[Tuple[str, str, str, str]]) -> int:
        """ Add all phrases to the database that are not yet there.
        """
        to_add = new_phrases - existing_phrases

        added = 0
        with cursor.copy('COPY word(word_token, type, word, info) FROM STDIN') as copy:
            for word, cls, typ, oper in to_add:
                term = self._search_normalized(word)
                if term:
                    copy.write_row((term, 'S', word,
                                    Jsonb({'class': cls, 'type': typ,
                                           'op': oper if oper in ('in', 'near') else None})))
                    added += 1

        return added

    def _remove_special_phrases(self, cursor: Cursor,
                                new_phrases: Set[Tuple[str, str, str, str]],
                                existing_phrases: Set[Tuple[str, str, str, str]]) -> int:
        """ Remove all phrases from the database that are no longer in the
            new phrase list.
        """
        to_delete = existing_phrases - new_phrases

        if to_delete:
            cursor.executemany(
                """ DELETE FROM word
                      WHERE type = 'S' and word = %s
                            and info->>'class' = %s and info->>'type' = %s
                            and %s = coalesce(info->>'op', '-')
                """, to_delete)

        return len(to_delete)

    def add_country_names(self, country_code: str, names: Mapping[str, str]) -> None:
        """ Add default names for the given country to the search index.
        """
        # Make sure any name preprocessing for country names applies.
        info = PlaceInfo({'name': names, 'country_code': country_code,
                          'rank_address': 4, 'class': 'boundary',
                          'type': 'administrative'})
        self._add_country_full_names(country_code,
                                     self.sanitizer.process_names(info)[0],
                                     internal=True)

    def _add_country_full_names(self, country_code: str, names: Sequence[PlaceName],
                                internal: bool = False) -> None:
        """ Add names for the given country from an already sanitized
            name list.
        """
        assert self.conn is not None
        word_tokens = set()
        for name in names:
            norm_name = self._normalized(name.name)
            token_name = self._search_normalized(name.name)
            if norm_name and token_name:
                word_tokens.add((token_name, norm_name))

        with self.conn.cursor() as cur:
            # Get existing names
            cur.execute("""SELECT word_token,
                                  word as lookup,
                                  coalesce(info ? 'internal', false) as is_internal
                             FROM word
                             WHERE type = 'C' and info->>'cc' = %s""",
                        (country_code, ))
            # internal/external names
            existing_tokens: Dict[bool, Set[Tuple[str, str]]] = {True: set(), False: set()}
            for word in cur:
                existing_tokens[word[2]].add((word[0], word[1]))

            # Delete names that no longer exist.
            gone_tokens = existing_tokens[internal] - word_tokens
            if internal:
                gone_tokens.update(existing_tokens[False] & word_tokens)
            if gone_tokens:
                cur.execute("""DELETE FROM word
                               USING jsonb_array_elements(%s) as data
                               WHERE type = 'C' and info->>'cc' = %s
                                     and word_token = data->>0 and word = data->>1""",
                            (Jsonb(list(gone_tokens)), country_code))

            # Only add those names that are not yet in the list.
            new_tokens = word_tokens - existing_tokens[True]
            if not internal:
                new_tokens -= existing_tokens[False]
            if new_tokens:
                if internal:
                    sql = """INSERT INTO word (word_token, type, word, info)
                               (SELECT data->>0, 'C', data->>1,
                                       jsonb_build_object('internal', 'yes', 'cc', %s::text)
                                  FROM jsonb_array_elements(%s) as data)
                           """
                else:
                    sql = """INSERT INTO word (word_token, type, word, info)
                                   (SELECT data->>0, 'C', data->>1,
                                           jsonb_build_object('cc', %s::text)
                                    FROM  jsonb_array_elements(%s) as data)
                          """
                cur.execute(sql, (country_code, Jsonb(list(new_tokens))))

    def process_place(self, place: PlaceInfo) -> Mapping[str, Any]:
        """ Determine tokenizer information about the given place.

            Returns a JSON-serializable structure that will be handed into
            the database via the token_info field.
        """
        token_info = _TokenInfo()

        names, address = self.sanitizer.process_names(place)

        if names:
            token_info.set_names(*self._compute_name_tokens(names))

            if place.is_country():
                assert place.country_code is not None
                self._add_country_full_names(place.country_code, names)

        if address:
            self._process_place_address(token_info, address)

        return token_info.to_dict()

    def _process_place_address(self, token_info: '_TokenInfo',
                               address: Sequence[PlaceName]) -> None:
        for item in address:
            if item.kind == 'postcode':
                token_info.set_postcode(self._add_postcode(item))
            elif item.kind == 'housenumber':
                token_info.add_housenumber(*self._compute_housenumber_token(item))
            elif item.kind == 'street':
                token_info.add_street(self._retrieve_full_tokens(item.name))
            elif item.kind == 'place':
                if not item.suffix:
                    token_info.add_place(itertools.chain(*self._compute_name_tokens([item])))
            elif (not item.kind.startswith('_') and not item.suffix and
                  item.kind not in ('country', 'full', 'inclusion')):
                token_info.add_address_term(item.kind,
                                            itertools.chain(*self._compute_name_tokens([item])))

    def _compute_housenumber_token(self, hnr: PlaceName) -> Tuple[Optional[int], Optional[str]]:
        """ Normalize the housenumber and return the word token and the
            canonical form.
        """
        assert self.conn is not None
        analyzer = self.token_analysis.analysis.get('@housenumber')
        result: Tuple[Optional[int], Optional[str]] = (None, None)

        if analyzer is None:
            # When no custom analyzer is set, simply normalize and transliterate
            norm_name = self._search_normalized(hnr.name)
            if norm_name:
                result = self._cache.housenumbers.get(norm_name, result)
                if result[0] is None:
                    hid = execute_scalar(self.conn, "SELECT getorcreate_hnr_id(%s)", (norm_name, ))

                    result = hid, norm_name
                    self._cache.housenumbers[norm_name] = result
        else:
            # Otherwise use the analyzer to determine the canonical name.
            # Per convention we use the first variant as the 'lookup name', the
            # name that gets saved in the housenumber field of the place.
            word_id = analyzer.get_canonical_id(hnr)
            if word_id:
                result = self._cache.housenumbers.get(word_id, result)
                if result[0] is None:
                    varout = analyzer.compute_variants(word_id)
                    if isinstance(varout, tuple):
                        variants = varout[0]
                    else:
                        variants = varout
                    if variants:
                        hid = execute_scalar(self.conn, "SELECT create_analyzed_hnr_id(%s, %s)",
                                             (word_id, variants))
                        result = hid, variants[0]
                        self._cache.housenumbers[word_id] = result

        return result

    def _retrieve_full_tokens(self, name: str) -> List[int]:
        """ Get the full name token for the given name, if it exists.
            The name is only retrieved for the standard analyser.
        """
        assert self.conn is not None
        norm_name = self._search_normalized(name)

        # return cached if possible
        if norm_name in self._cache.fulls:
            return self._cache.fulls[norm_name]

        with self.conn.cursor() as cur:
            cur.execute("SELECT word_id FROM word WHERE word_token = %s and type = 'W'",
                        (norm_name, ))
            full = [row[0] for row in cur]

        self._cache.fulls[norm_name] = full

        return full

    def _compute_name_tokens(self, names: Sequence[PlaceName]) -> Tuple[Set[int], Set[int]]:
        """ Computes the full name and partial name tokens for the given
            dictionary of names.
        """
        assert self.conn is not None
        full_tokens: Set[int] = set()
        partial_tokens: Set[int] = set()

        for name in names:
            analyzer_id = name.get_attr('analyzer')
            analyzer = self.token_analysis.get_analyzer(analyzer_id)
            word_id = analyzer.get_canonical_id(name)
            if analyzer_id is None:
                token_id = word_id
            else:
                token_id = f'{word_id}@{analyzer_id}'

            full, part = self._cache.names.get(token_id, (None, None))
            if full is None:
                varset = analyzer.compute_variants(word_id)
                if isinstance(varset, tuple):
                    variants, lookups = varset
                else:
                    variants, lookups = varset, None
                if not variants:
                    continue

                with self.conn.cursor() as cur:
                    cur.execute("SELECT * FROM getorcreate_full_word(%s, %s, %s)",
                                (token_id, variants, lookups))
                    full, part = cast(Tuple[int, List[int]], cur.fetchone())

                self._cache.names[token_id] = (full, part)

            assert part is not None

            full_tokens.add(full)
            partial_tokens.update(part)

        return full_tokens, partial_tokens

    def _add_postcode(self, item: PlaceName) -> Optional[str]:
        """ Make sure the normalized postcode is present in the word table.
        """
        assert self.conn is not None
        analyzer = self.token_analysis.analysis.get('@postcode')

        if analyzer is None:
            return item.name.strip().upper()
        else:
            return analyzer.get_canonical_id(item)


class _TokenInfo:
    """ Collect token information to be sent back to the database.
    """
    def __init__(self) -> None:
        self.names: Optional[str] = None
        self.housenumbers: Set[str] = set()
        self.housenumber_tokens: Set[int] = set()
        self.street_tokens: Optional[Set[int]] = None
        self.place_tokens: Set[int] = set()
        self.address_tokens: Dict[str, str] = {}
        self.postcode: Optional[str] = None

    def _mk_array(self, tokens: Iterable[Any]) -> str:
        return f"{{{','.join((str(s) for s in tokens))}}}"

    def to_dict(self) -> Dict[str, Any]:
        """ Return the token information in database importable format.
        """
        out: Dict[str, Any] = {}

        if self.names:
            out['names'] = self.names

        if self.housenumbers:
            out['hnr'] = ';'.join(self.housenumbers)
            out['hnr_tokens'] = self._mk_array(self.housenumber_tokens)

        if self.street_tokens is not None:
            out['street'] = self._mk_array(self.street_tokens)

        if self.place_tokens:
            out['place'] = self._mk_array(self.place_tokens)

        if self.address_tokens:
            out['addr'] = self.address_tokens

        if self.postcode:
            out['postcode'] = self.postcode

        return out

    def set_names(self, fulls: Iterable[int], partials: Iterable[int]) -> None:
        """ Adds token information for the normalised names.
        """
        self.names = self._mk_array(itertools.chain(fulls, partials))

    def add_housenumber(self, token: Optional[int], hnr: Optional[str]) -> None:
        """ Extract housenumber information from a list of normalised
            housenumbers.
        """
        if token:
            assert hnr is not None
            self.housenumbers.add(hnr)
            self.housenumber_tokens.add(token)

    def add_street(self, tokens: Iterable[int]) -> None:
        """ Add addr:street match terms.
        """
        if self.street_tokens is None:
            self.street_tokens = set()
        self.street_tokens.update(tokens)

    def add_place(self, tokens: Iterable[int]) -> None:
        """ Add addr:place search and match terms.
        """
        self.place_tokens.update(tokens)

    def add_address_term(self, key: str, partials: Iterable[int]) -> None:
        """ Add additional address terms.
        """
        array = self._mk_array(partials)
        if len(array) > 2:
            self.address_tokens[key] = array

    def set_postcode(self, postcode: Optional[str]) -> None:
        """ Set the postcode to the given one.
        """
        self.postcode = postcode


class _TokenCache:
    """ Cache for token information to avoid repeated database queries.

        This cache is not thread-safe and needs to be instantiated per
        analyzer.
    """
    def __init__(self) -> None:
        self.names: Dict[str, Tuple[int, List[int]]] = {}
        self.partials: Dict[str, int] = {}
        self.fulls: Dict[str, List[int]] = {}
        self.housenumbers: Dict[str, Tuple[Optional[int], Optional[str]]] = {}


================================================
FILE: src/nominatim_db/tokenizer/place_sanitizer.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Handler for cleaning name and address tags in place information before it
is handed to the token analysis.
"""
from typing import Optional, List, Mapping, Sequence, Callable, Any, Tuple

from ..errors import UsageError
from ..config import Configuration
from .sanitizers.config import SanitizerConfig
from .sanitizers.base import SanitizerHandler, ProcessInfo
from ..data.place_name import PlaceName
from ..data.place_info import PlaceInfo


class PlaceSanitizer:
    """ Controller class which applies sanitizer functions on the place
        names and address before they are used by the token analysers.
    """

    def __init__(self, rules: Optional[Sequence[Mapping[str, Any]]],
                 config: Configuration) -> None:
        self.handlers: List[Callable[[ProcessInfo], None]] = []

        if rules:
            for func in rules:
                if 'step' not in func:
                    raise UsageError("Sanitizer rule is missing the 'step' attribute.")
                if not isinstance(func['step'], str):
                    raise UsageError("'step' attribute must be a simple string.")

                module: SanitizerHandler = \
                    config.load_plugin_module(func['step'], 'nominatim_db.tokenizer.sanitizers')

                self.handlers.append(module.create(SanitizerConfig(func)))

    def process_names(self, place: PlaceInfo) -> Tuple[List[PlaceName], List[PlaceName]]:
        """ Extract a sanitized list of names and address parts from the
            given place. The function returns a tuple
            (list of names, list of address names)
        """
        obj = ProcessInfo(place)

        for func in self.handlers:
            func(obj)

        return obj.names, obj.address


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/__init__.py
================================================


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/base.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Common data types and protocols for sanitizers.
"""
from typing import Optional, List, Mapping, Callable

from ...typing import Protocol, Final
from ...data.place_info import PlaceInfo
from ...data.place_name import PlaceName
from .config import SanitizerConfig


class ProcessInfo:
    """ Container class for information handed into to handler functions.
        The 'names' and 'address' members are mutable. A handler must change
        them by either modifying the lists place or replacing the old content
        with a new list.
    """

    def __init__(self, place: PlaceInfo):
        self.place: Final = place
        self.names = self._convert_name_dict(place.name)
        self.address = self._convert_name_dict(place.address)

    @staticmethod
    def _convert_name_dict(names: Optional[Mapping[str, str]]) -> List[PlaceName]:
        """ Convert a dictionary of names into a list of PlaceNames.
            The dictionary key is split into the primary part of the key
            and the suffix (the part after an optional colon).
        """
        out = []

        if names:
            for key, value in names.items():
                parts = key.split(':', 1)
                out.append(PlaceName(value.strip(),
                                     parts[0].strip(),
                                     parts[1].strip() if len(parts) > 1 else None))

        return out


class SanitizerHandler(Protocol):
    """ Protocol for sanitizer modules.
    """

    def create(self, config: SanitizerConfig) -> Callable[[ProcessInfo], None]:
        """
        Create a function for sanitizing a place.

        Arguments:
            config: A dictionary with the additional configuration options
                    specified in the tokenizer configuration

        Return:
            The result must be a callable that takes a place description
            and transforms name and address as required.
        """


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/clean_housenumbers.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Sanitizer that preprocesses address tags for house numbers. The sanitizer
allows to

* define which tags are to be considered house numbers (see 'filter-kind')
* split house number lists into individual numbers (see 'delimiters')
* expand interpolated house numbers

Arguments:
    delimiters: Define the set of characters to be used for
                splitting a list of house numbers into parts. (default: ',;')
    filter-kind: Define the address tags that are considered to be a
                 house number. Either takes a single string or a list of strings,
                 where each string is a regular expression. An address item
                 is considered a house number if the 'kind' fully matches any
                 of the given regular expressions. (default: 'housenumber')
    convert-to-name: Define house numbers that should be treated as a name
                     instead of a house number. Either takes a single string
                     or a list of strings, where each string is a regular
                     expression that must match the full house number value.
    expand-interpolations: When true, expand house number ranges to separate numbers
                           when an 'interpolation' is present. (default: true)

"""
from typing import Callable, Iterator, Iterable, Union
import re

from ...data.place_name import PlaceName
from .base import ProcessInfo
from .config import SanitizerConfig

RANGE_REGEX = re.compile(r'\d+-\d+')


class _HousenumberSanitizer:

    def __init__(self, config: SanitizerConfig) -> None:
        self.filter_kind = config.get_filter('filter-kind', ['housenumber'])
        self.split_regexp = config.get_delimiter()

        self.filter_name = config.get_filter('convert-to-name', 'FAIL_ALL')
        self.expand_interpolations = config.get_bool('expand-interpolations', True)

    def __call__(self, obj: ProcessInfo) -> None:
        if not obj.address:
            return

        itype: Union[int, str, None] = None
        if self.expand_interpolations:
            itype = next((i.name for i in obj.address if i.kind == 'interpolation'), None)
            if itype is not None:
                if itype == 'all':
                    itype = 1
                elif len(itype) == 1 and itype.isdigit():
                    itype = int(itype)
                elif itype not in ('odd', 'even'):
                    itype = None

        new_address: list[PlaceName] = []
        for item in obj.address:
            if self.filter_kind(item.kind):
                if itype is not None and RANGE_REGEX.fullmatch(item.name):
                    hnrs = self._expand_range(itype, item.name)
                    if hnrs:
                        new_address.extend(item.clone(kind='housenumber', name=str(hnr))
                                           for hnr in hnrs)
                        continue

                if self.filter_name(item.name):
                    obj.names.append(item.clone(kind='housenumber'))
                else:
                    new_address.extend(item.clone(kind='housenumber', name=n)
                                       for n in self.sanitize(item.name))
            elif item.kind != 'interpolation':
                # Ignore interpolation, otherwise don't touch other address items.
                new_address.append(item)

        obj.address = new_address

    def sanitize(self, value: str) -> Iterator[str]:
        """ Extract housenumbers in a regularized format from an OSM value.

            The function works as a generator that yields all valid housenumbers
            that can be created from the value.
        """
        for hnr in self.split_regexp.split(value):
            if hnr:
                yield from self._regularize(hnr)

    def _regularize(self, hnr: str) -> Iterator[str]:
        yield hnr

    def _expand_range(self, itype: Union[str, int], hnr: str) -> Iterable[int]:
        first, last = (int(i) for i in hnr.split('-'))

        if isinstance(itype, int):
            step = itype
        else:
            step = 2
            if (itype == 'even' and first % 2 == 1)\
               or (itype == 'odd' and first % 2 == 0):
                first += 1

        if (last + 1 - first) / step < 10:
            return range(first, last + 1, step)

        return []


def create(config: SanitizerConfig) -> Callable[[ProcessInfo], None]:
    """ Create a housenumber processing function.
    """

    return _HousenumberSanitizer(config)


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/clean_postcodes.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Sanitizer that filters postcodes by their officially allowed pattern.

Arguments:
    convert-to-address: If set to 'yes' (the default), then postcodes that do
                        not conform with their country-specific pattern are
                        converted to an address component. That means that
                        the postcode does not take part when computing the
                        postcode centroids of a country but is still searchable.
                        When set to 'no', non-conforming postcodes are not
                        searchable either.
    default-pattern:    Pattern to use, when there is none available for the
                        country in question. Warning: will not be used for
                        objects that have no country assigned. These are always
                        assumed to have no postcode.
"""
from typing import Callable, Optional, Tuple

from ...data.postcode_format import PostcodeFormatter
from .base import ProcessInfo
from .config import SanitizerConfig


class _PostcodeSanitizer:

    def __init__(self, config: SanitizerConfig) -> None:
        self.convert_to_address = config.get_bool('convert-to-address', True)
        self.matcher = PostcodeFormatter()

        default_pattern = config.get('default-pattern')
        if default_pattern is not None and isinstance(default_pattern, str):
            self.matcher.set_default_pattern(default_pattern)

    def __call__(self, obj: ProcessInfo) -> None:
        if not obj.address:
            return

        postcodes = ((i, o) for i, o in enumerate(obj.address) if o.kind == 'postcode')

        for pos, postcode in postcodes:
            formatted = self.scan(postcode.name, obj.place.country_code)

            if formatted is None:
                if self.convert_to_address:
                    postcode.kind = 'unofficial_postcode'
                else:
                    obj.address.pop(pos)
            else:
                postcode.name = formatted[0]
                postcode.set_attr('variant', formatted[1])

    def scan(self, postcode: str, country: Optional[str]) -> Optional[Tuple[str, str]]:
        """ Check the postcode for correct formatting and return the
            normalized version. Returns None if the postcode does not
            correspond to the official format of the given country.
        """
        match = self.matcher.match(country, postcode)
        if match is None:
            return None

        assert country is not None

        return self.matcher.normalize(country, match), \
            ' '.join(filter(lambda p: p is not None, match.groups()))


def create(config: SanitizerConfig) -> Callable[[ProcessInfo], None]:
    """ Create a function that filters postcodes by their officially allowed pattern.
    """

    return _PostcodeSanitizer(config)


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/clean_tiger_tags.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Sanitizer that preprocesses tags from the TIGER import.

It makes the following changes:

* remove state reference from tiger:county
"""
from typing import Callable
import re

from .base import ProcessInfo
from .config import SanitizerConfig

COUNTY_MATCH = re.compile('(.*), [A-Z][A-Z]')


def _clean_tiger_county(obj: ProcessInfo) -> None:
    """ Remove the state reference from tiger:county tags.

        This transforms a name like 'Hamilton, AL' into 'Hamilton'.
        If no state reference is detected at the end, the name is left as is.
    """
    if not obj.address:
        return

    for item in obj.address:
        if item.kind == 'tiger' and item.suffix == 'county':
            m = COUNTY_MATCH.fullmatch(item.name)
            if m:
                item.name = m[1]
            # Switch kind and suffix, the split left them reversed.
            item.kind = 'county'
            item.suffix = 'tiger'

            return


def create(_: SanitizerConfig) -> Callable[[ProcessInfo], None]:
    """ Create a function that preprocesses tags from the TIGER import.
    """
    return _clean_tiger_county


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/config.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Configuration for Sanitizers.
"""
from typing import Sequence, Union, Optional, Pattern, Callable, Any, TYPE_CHECKING
from collections import UserDict
import re

from ...errors import UsageError

# working around missing generics in Python < 3.8
# See https://github.com/python/typing/issues/60#issuecomment-869757075
if TYPE_CHECKING:
    _BaseUserDict = UserDict[str, Any]
else:
    _BaseUserDict = UserDict


class SanitizerConfig(_BaseUserDict):
    """ The `SanitizerConfig` class is a read-only dictionary
        with configuration options for the sanitizer.
        In addition to the usual dictionary functions, the class provides
        accessors to standard sanitizer options that are used by many of the
        sanitizers.
    """

    def get_string_list(self, param: str, default: Sequence[str] = tuple()) -> Sequence[str]:
        """ Extract a configuration parameter as a string list.

            Arguments:
                param: Name of the configuration parameter.
                default: Takes a tuple or list of strings which will
                         be returned if the parameter is missing in the
                         sanitizer configuration.
                         Note that if this default parameter is not
                         provided then an empty list is returned.

            Returns:
                If the parameter value is a simple string, it is returned as a
                    one-item list. If the parameter value does not exist, the given
                    default is returned. If the parameter value is a list, it is
                    checked to contain only strings before being returned.
        """
        values = self.data.get(param, None)

        if values is None:
            return list(default)

        if isinstance(values, str):
            return [values] if values else []

        if not isinstance(values, (list, tuple)):
            raise UsageError(f"Parameter '{param}' must be string or list of strings.")

        if any(not isinstance(value, str) for value in values):
            raise UsageError(f"Parameter '{param}' must be string or list of strings.")

        return values

    def get_bool(self, param: str, default: Optional[bool] = None) -> bool:
        """ Extract a configuration parameter as a boolean.

            Arguments:
                param: Name of the configuration parameter. The parameter must
                       contain one of the yaml boolean values or an
                       UsageError will be raised.
                default: Value to return, when the parameter is missing.
                         When set to `None`, the parameter must be defined.

            Returns:
                Boolean value of the given parameter.
        """
        value = self.data.get(param, default)

        if not isinstance(value, bool):
            raise UsageError(f"Parameter '{param}' must be a boolean value ('yes' or 'no').")

        return value

    def get_delimiter(self, default: str = ',;') -> Pattern[str]:
        """ Return the 'delimiters' parameter in the configuration as a
            compiled regular expression that can be used to split strings on
            these delimiters.

            Arguments:
                default: Delimiters to be used when 'delimiters' parameter
                         is not explicitly configured.

            Returns:
                A regular expression pattern which can be used to
                    split a string. The regular expression makes sure that the
                    resulting names are stripped and that repeated delimiters
                    are ignored. It may still create empty fields on occasion. The
                    code needs to filter those.
        """
        delimiter_set = set(self.data.get('delimiters', default))
        if not delimiter_set:
            raise UsageError("Empty 'delimiter' parameter not allowed for sanitizer.")

        return re.compile('\\s*[{}]+\\s*'.format(''.join('\\' + d for d in delimiter_set)))

    def get_filter(self, param: str, default: Union[str, Sequence[str]] = 'PASS_ALL'
                   ) -> Callable[[str], bool]:
        """ Returns a filter function for the given parameter of the sanitizer
            configuration.

            The value provided for the parameter in sanitizer configuration
            should be a string or list of strings, where each string is a regular
            expression. These regular expressions will later be used by the
            filter function to filter strings.

            Arguments:
                param: The parameter for which the filter function
                       will be created.
                default: Defines the behaviour of filter function if
                         parameter is missing in the sanitizer configuration.
                         Takes a string(PASS_ALL or FAIL_ALL) or a list of strings.
                         Any other value of string or an empty list is not allowed,
                         and will raise a ValueError. If the value is PASS_ALL, the filter
                         function will let all strings to pass, if the value is FAIL_ALL,
                         filter function will let no strings to pass.
                         If value provided is a list of strings each string
                         is treated as a regular expression. In this case these regular
                         expressions will be used by the filter function.
                         By default allow filter function to let all strings pass.

            Returns:
                A filter function that takes a target string as the argument and
                    returns True if it fully matches any of the regular expressions
                    otherwise returns False.
        """
        filters = self.get_string_list(param) or default

        if filters == 'PASS_ALL':
            return lambda _: True
        if filters == 'FAIL_ALL':
            return lambda _: False

        if filters and isinstance(filters, (list, tuple)):
            regexes = [re.compile(regex) for regex in filters]
            return lambda target: any(regex.fullmatch(target) for regex in regexes)

        raise ValueError("Default parameter must be a non-empty list or a string value \
                          ('PASS_ALL' or 'FAIL_ALL').")


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/delete_tags.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Sanitizer which prevents certain tags from getting into the search index.
It remove tags which matches all properties given below.


Arguments:
    type: Define which type of tags should be considered for removal.
          There are two types of tags 'name' and 'address' tags.
          Takes a string 'name' or 'address'. (default: 'name')

    filter-kind: Define which 'kind' of tags should be removed.
                 Takes a string or list of strings where each
                 string is a regular expression. A tag is considered
                 to be a candidate for removal if its 'kind' property
                 fully matches any of the given regular expressions.
                 Note that by default all 'kind' of tags are considered.

    suffix: Define the 'suffix' property of the tags which should be
            removed. Takes a string or list of strings where each
            string is a regular expression. A tag is considered to be a
            candidate for removal if its 'suffix' property fully
            matches any of the given regular expressions. Note that by
            default tags with any suffix value are considered including
            those which don't have a suffix at all.

    name: Define the 'name' property corresponding to the 'kind' property
          of the tag. Takes a string or list of strings where each string
          is a regular expression. A tag is considered to be a candidate
          for removal if its name fully matches any of the given regular
          expressions. Note that by default tags with any 'name' are
          considered.

    country_code: Define the country code of places whose tags should be
                  considered for removed. Takes a string or list of strings
                  where each string is a two-letter lower-case country code.
                  Note that by default tags of places with any country code
                  are considered including those which don't have a country
                  code at all.

    rank_address: Define the address rank of places whose tags should be
                  considered for removal. Takes a string or list of strings
                  where each string is a number or range of number or the
                  form -.
                  Note that default is '0-30', which means that tags of all
                  places are considered.
                  See https://nominatim.org/release-docs/latest/customize/Ranking/#address-rank
                  to learn more about address rank.


"""
from typing import Callable, List, Tuple, Sequence

from ...data.place_name import PlaceName
from .base import ProcessInfo
from .config import SanitizerConfig


class _TagSanitizer:

    def __init__(self, config: SanitizerConfig) -> None:
        self.type = config.get('type', 'name')
        self.filter_kind = config.get_filter('filter-kind')
        self.country_codes = config.get_string_list('country_code', [])
        self.filter_suffix = config.get_filter('suffix')
        self.filter_name = config.get_filter('name')
        self.allowed_ranks = self._set_allowed_ranks(
            config.get_string_list("rank_address", ["0-30"])
        )

        self.has_country_code = config.get('country_code', None) is not None

    def __call__(self, obj: ProcessInfo) -> None:
        tags = obj.names if self.type == 'name' else obj.address

        if not tags \
           or not self.allowed_ranks[obj.place.rank_address] \
           or self.has_country_code \
           and obj.place.country_code not in self.country_codes:
            return

        filtered_tags: List[PlaceName] = []

        for tag in tags:

            if not self.filter_kind(tag.kind) \
               or not self.filter_suffix(tag.suffix or '') \
               or not self.filter_name(tag.name):
                filtered_tags.append(tag)

        if self.type == 'name':
            obj.names = filtered_tags
        else:
            obj.address = filtered_tags

    def _set_allowed_ranks(self, ranks: Sequence[str]) -> Tuple[bool, ...]:
        """ Returns a tuple of 31 boolean values corresponding to the
            address ranks 0-30. Value at index 'i' is True if rank 'i'
            is present in the ranks or lies in the range of any of the
            ranks provided in the sanitizer configuration, otherwise
            the value is False.
        """
        allowed_ranks = [False] * 31

        for rank in ranks:
            intvl = [int(x) for x in rank.split('-')]

            start, end = intvl[0], intvl[0] if len(intvl) == 1 else intvl[1]

            for i in range(start, end + 1):
                allowed_ranks[i] = True

        return tuple(allowed_ranks)


def create(config: SanitizerConfig) -> Callable[[ProcessInfo], None]:
    """ Create a function to process removal of certain tags.
    """

    return _TagSanitizer(config)


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/split_name_list.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Sanitizer that splits lists of names into their components.

Arguments:
    delimiters: Define the set of characters to be used for
                splitting the list. (default: ',;')
"""
from typing import Callable

from .base import ProcessInfo
from .config import SanitizerConfig


def create(config: SanitizerConfig) -> Callable[[ProcessInfo], None]:
    """ Create a name processing function that splits name values with
        multiple values into their components.
    """
    regexp = config.get_delimiter()

    def _process(obj: ProcessInfo) -> None:
        if not obj.names:
            return

        new_names = []
        for name in obj.names:
            split_names = regexp.split(name.name)
            if len(split_names) == 1:
                new_names.append(name)
            else:
                new_names.extend(name.clone(name=n) for n in split_names if n)

        obj.names = new_names

    return _process


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/strip_brace_terms.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
This sanitizer creates additional name variants for names that have
addendums in brackets (e.g. "Halle (Saale)"). The additional variant contains
only the main name part with the bracket part removed.
"""
from typing import Callable

from .base import ProcessInfo
from .config import SanitizerConfig


def create(_: SanitizerConfig) -> Callable[[ProcessInfo], None]:
    """ Create a name processing function that creates additional name variants
        for bracket addendums.
    """
    def _process(obj: ProcessInfo) -> None:
        """ Add variants for names that have a bracket extension.
        """
        if obj.names:
            new_names = []
            for name in (n for n in obj.names if '(' in n.name):
                if ')' in name.name and not name.name.endswith(')'):
                    continue
                new_name = name.name.split('(')[0].strip()
                if new_name:
                    new_names.append(name.clone(name=new_name))

            obj.names.extend(new_names)

    return _process


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/tag_analyzer_by_language.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
This sanitizer sets the `analyzer` property depending on the
language of the tag. The language is taken from the suffix of the name.
If a name already has an analyzer tagged, then this is kept.

Arguments:

    filter-kind: Restrict the names the sanitizer should be applied to
                 the given tags. The parameter expects a list of
                 regular expressions which are matched against 'kind'.
                 Note that a match against the full string is expected.
    whitelist: Restrict the set of languages that should be tagged.
               Expects a list of acceptable suffixes. When unset,
               all 2- and 3-letter lower-case codes are accepted.
    use-defaults:  Configure what happens when the name has no suffix.
                   When set to 'all', a variant is created for
                   each of the default languages in the country
                   the feature is in. When set to 'mono', a variant is
                   only created, when exactly one language is spoken
                   in the country. The default is to do nothing with
                   the default languages of a country.
    mode: Define how the variants are created and may be 'replace' or
          'append'. When set to 'append' the original name (without
          any analyzer tagged) is retained. (default: replace)

"""
from typing import Callable, Dict, Optional, List

from ...data import country_info
from .base import ProcessInfo
from .config import SanitizerConfig


class _AnalyzerByLanguage:
    """ Processor for tagging the language of names in a place.
    """

    def __init__(self, config: SanitizerConfig) -> None:
        self.filter_kind = config.get_filter('filter-kind')
        self.replace = config.get('mode', 'replace') != 'append'
        self.whitelist = config.get('whitelist')

        self._compute_default_languages(config.get('use-defaults', 'no'))

    def _compute_default_languages(self, use_defaults: str) -> None:
        self.deflangs: Dict[Optional[str], List[str]] = {}

        if use_defaults in ('mono', 'all'):
            for ccode, clangs in country_info.iterate('languages'):
                if len(clangs) == 1 or use_defaults == 'all':
                    if self.whitelist:
                        self.deflangs[ccode] = [cl for cl in clangs if cl in self.whitelist]
                    else:
                        self.deflangs[ccode] = clangs

    def _suffix_matches(self, suffix: str) -> bool:
        if self.whitelist is None:
            return len(suffix) in (2, 3) and suffix.islower()

        return suffix in self.whitelist

    def __call__(self, obj: ProcessInfo) -> None:
        if not obj.names:
            return

        more_names = []

        for name in (n for n in obj.names
                     if not n.has_attr('analyzer') and self.filter_kind(n.kind)):
            if name.suffix:
                langs = [name.suffix] if self._suffix_matches(name.suffix) else None
            else:
                langs = self.deflangs.get(obj.place.country_code)

            if langs:
                if self.replace:
                    name.set_attr('analyzer', langs[0])
                else:
                    more_names.append(name.clone(attr={'analyzer': langs[0]}))

                more_names.extend(name.clone(attr={'analyzer': lg}) for lg in langs[1:])

        obj.names.extend(more_names)


def create(config: SanitizerConfig) -> Callable[[ProcessInfo], None]:
    """ Create a function that sets the analyzer property depending on the
        language of the tag.
    """
    return _AnalyzerByLanguage(config)


================================================
FILE: src/nominatim_db/tokenizer/sanitizers/tag_japanese.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
This sanitizer maps OSM data to Japanese block addresses.
It replaces blocknumber and housenumber with housenumber,
and quarter and neighbourhood with place.
"""


from typing import Callable
from typing import List, Optional

from .base import ProcessInfo
from .config import SanitizerConfig
from ...data.place_name import PlaceName


def create(_: SanitizerConfig) -> Callable[[ProcessInfo], None]:
    """Set up the sanitizer
    """
    return tag_japanese


def reconbine_housenumber(
    new_address: List[PlaceName],
    tmp_housenumber: Optional[str],
    tmp_blocknumber: Optional[str]
) -> List[PlaceName]:
    """ Recombine the tag of housenumber by using housenumber and blocknumber
    """
    if tmp_blocknumber and tmp_housenumber:
        new_address.append(
            PlaceName(
                kind='housenumber',
                name=f'{tmp_blocknumber}-{tmp_housenumber}',
                suffix=''
            )
        )
    elif tmp_blocknumber:
        new_address.append(
            PlaceName(
                kind='housenumber',
                name=tmp_blocknumber,
                suffix=''
            )
        )
    elif tmp_housenumber:
        new_address.append(
            PlaceName(
                kind='housenumber',
                name=tmp_housenumber,
                suffix=''
            )
        )
    return new_address


def reconbine_place(
    new_address: List[PlaceName],
    tmp_neighbourhood: Optional[str],
    tmp_quarter: Optional[str]
) -> List[PlaceName]:
    """ Recombine the tag of place by using neighbourhood and quarter
    """
    if tmp_neighbourhood and tmp_quarter:
        new_address.append(
            PlaceName(
                kind='place',
                name=f'{tmp_quarter}{tmp_neighbourhood}',
                suffix=''
            )
        )
    elif tmp_neighbourhood:
        new_address.append(
            PlaceName(
                kind='place',
                name=tmp_neighbourhood,
                suffix=''
            )
        )
    elif tmp_quarter:
        new_address.append(
            PlaceName(
                kind='place',
                name=tmp_quarter,
                suffix=''
            )
        )
    return new_address


def tag_japanese(obj: ProcessInfo) -> None:
    """Recombine kind of address
    """
    if obj.place.country_code != 'jp':
        return
    tmp_housenumber = None
    tmp_blocknumber = None
    tmp_neighbourhood = None
    tmp_quarter = None

    new_address = []
    for item in obj.address:
        if item.kind == 'housenumber':
            tmp_housenumber = item.name
        elif item.kind == 'block_number':
            tmp_blocknumber = item.name
        elif item.kind == 'neighbourhood':
            tmp_neighbourhood = item.name
        elif item.kind == 'quarter':
            tmp_quarter = item.name
        else:
            new_address.append(item)

    new_address = reconbine_housenumber(new_address, tmp_housenumber, tmp_blocknumber)
    new_address = reconbine_place(new_address, tmp_neighbourhood, tmp_quarter)

    obj.address = [item for item in new_address if item.name is not None]


================================================
FILE: src/nominatim_db/tokenizer/token_analysis/__init__.py
================================================


================================================
FILE: src/nominatim_db/tokenizer/token_analysis/base.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Common data types and protocols for analysers.
"""
from typing import Mapping, List, Any, Union, Tuple

from ...typing import Protocol
from ...data.place_name import PlaceName


class Analyzer(Protocol):
    """ The `create()` function of an analysis module needs to return an
        object that implements the following functions.
    """

    def get_canonical_id(self, name: PlaceName) -> str:
        """ Return the canonical form of the given name. The canonical ID must
            be unique (the same ID must always yield the same variants) and
            must be a form from which the variants can be derived.

            Arguments:
                name: Extended place name description as prepared by
                      the sanitizers.

            Returns:
                ID string with a canonical form of the name. The string may
                    be empty, when the analyzer cannot analyze the name at all,
                    for example because the character set in use does not match.
        """

    def compute_variants(self, canonical_id: str) -> Union[List[str], Tuple[List[str], List[str]]]:
        """ Compute the transliterated spelling variants for the given
            canonical ID.

            Arguments:
                canonical_id: ID string previously computed with
                              `get_canonical_id()`.

            Returns:
                A list of possible spelling variants. All strings must have
                    been transformed with the global normalizer and
                    transliterator ICU rules. Otherwise they cannot be matched
                    against the input by the query frontend.
                    The list may be empty, when there are no useful
                    spelling variants. This may happen when an analyzer only
                    usually outputs additional variants to the canonical spelling
                    and there are no such variants.
        """


class AnalysisModule(Protocol):
    """ The setup of the token analysis is split into two parts:
        configuration and analyser factory. A token analysis module must
        therefore implement the two functions here described.
    """

    def configure(self, rules: Mapping[str, Any],
                  normalizer: Any, transliterator: Any) -> Any:
        """ Prepare the configuration of the analysis module.
            This function should prepare all data that can be shared
            between instances of this analyser.

            Arguments:
                rules: A dictionary with the additional configuration options
                       as specified in the tokenizer configuration.
                normalizer: an ICU Transliterator with the compiled
                            global normalization rules.
                transliterator: an ICU Transliterator with the compiled
                                global transliteration rules.

            Returns:
                A data object with configuration data. This will be handed
                    as is into the `create()` function and may be
                    used freely by the analysis module as needed.
        """

    def create(self, normalizer: Any, transliterator: Any, config: Any) -> Analyzer:
        """ Create a new instance of the analyser.
            A separate instance of the analyser is created for each thread
            when used in multi-threading context.

            Arguments:
                normalizer: an ICU Transliterator with the compiled normalization
                            rules.
                transliterator: an ICU Transliterator with the compiled
                                transliteration rules.
                config: The object that was returned by the call to configure().

            Returns:
                A new analyzer instance. This must be an object that implements
                    the Analyzer protocol.
        """


================================================
FILE: src/nominatim_db/tokenizer/token_analysis/config_variants.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Parser for configuration for variants.
"""
from typing import Any, Iterator, Tuple, List, Optional, Set, NamedTuple
from collections import defaultdict
import itertools
import re

from ...config import flatten_config_list
from ...errors import UsageError


class ICUVariant(NamedTuple):
    """ A single replacement rule for variant creation.
    """
    source: str
    replacement: str


def get_variant_config(in_rules: Any,
                       normalizer: Any) -> Tuple[List[Tuple[str, List[str]]], str]:
    """ Convert the variant definition from the configuration into
        replacement sets.

        Returns a tuple containing the replacement set and the list of characters
        used in the replacements.
    """
    immediate = defaultdict(list)
    chars: Set[str] = set()

    if in_rules:
        vset: Set[ICUVariant] = set()
        rules = flatten_config_list(in_rules, 'variants')

        vmaker = _VariantMaker(normalizer)

        for section in rules:
            for rule in (section.get('words') or []):
                vset.update(vmaker.compute(rule))

        # Intermediate reorder by source. Also compute required character set.
        for variant in vset:
            if variant.source[-1] == ' ' and variant.replacement[-1] == ' ':
                replstr = variant.replacement[:-1]
            else:
                replstr = variant.replacement
            immediate[variant.source].append(replstr)
            chars.update(variant.source)

    return list(immediate.items()), ''.join(chars)


class _VariantMaker:
    """ Generator for all necessary ICUVariants from a single variant rule.

        All text in rules is normalized to make sure the variants match later.
    """

    def __init__(self, normalizer: Any) -> None:
        self.norm = normalizer

    def compute(self, rule: Any) -> Iterator[ICUVariant]:
        """ Generator for all ICUVariant tuples from a single variant rule.
        """
        parts = re.split(r'(\|)?([=-])>', rule)
        if len(parts) != 4:
            raise UsageError(f"Syntax error in variant rule: {rule}")

        decompose = parts[1] is None
        src_terms = [self._parse_variant_word(t) for t in parts[0].split(',')]
        repl_terms = (self.norm.transliterate(t).strip() for t in parts[3].split(','))

        # If the source should be kept, add a 1:1 replacement
        if parts[2] == '-':
            for src in src_terms:
                if src:
                    for froms, tos in _create_variants(*src, src[0], decompose):
                        yield ICUVariant(froms, tos)

        for src, repl in itertools.product(src_terms, repl_terms):
            if src and repl:
                for froms, tos in _create_variants(*src, repl, decompose):
                    yield ICUVariant(froms, tos)

    def _parse_variant_word(self, name: str) -> Optional[Tuple[str, str, str]]:
        name = name.strip()
        match = re.fullmatch(r'([~^]?)([^~$^]*)([~$]?)', name)
        if match is None or (match.group(1) == '~' and match.group(3) == '~'):
            raise UsageError(f"Invalid variant word descriptor '{name}'")
        norm_name = self.norm.transliterate(match.group(2)).strip()
        if not norm_name:
            return None

        return norm_name, match.group(1), match.group(3)


_FLAG_MATCH = {'^': '^ ',
               '$': ' ^',
               '': ' '}


def _create_variants(src: str, preflag: str, postflag: str,
                     repl: str, decompose: bool) -> Iterator[Tuple[str, str]]:
    if preflag == '~':
        postfix = _FLAG_MATCH[postflag]
        # suffix decomposition
        src = src + postfix
        repl = repl + postfix

        yield src, repl
        yield ' ' + src, ' ' + repl

        if decompose:
            yield src, ' ' + repl
            yield ' ' + src, repl
    elif postflag == '~':
        # prefix decomposition
        prefix = _FLAG_MATCH[preflag]
        src = prefix + src
        repl = prefix + repl

        yield src, repl
        yield src + ' ', repl + ' '

        if decompose:
            yield src, repl + ' '
            yield src + ' ', repl
    else:
        prefix = _FLAG_MATCH[preflag]
        postfix = _FLAG_MATCH[postflag]

        yield prefix + src + postfix, prefix + repl + postfix


================================================
FILE: src/nominatim_db/tokenizer/token_analysis/generic.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Generic processor for names that creates abbreviation variants.
"""
from typing import Mapping, Dict, Any, Iterable, Optional, List, cast, Tuple
import itertools

from ...errors import UsageError
from ...data.place_name import PlaceName
from .config_variants import get_variant_config
from .generic_mutation import MutationVariantGenerator
from .simple_trie import SimpleTrie

# Configuration section


def configure(rules: Mapping[str, Any], normalizer: Any, _: Any) -> Dict[str, Any]:
    """ Extract and preprocess the configuration for this module.
    """
    config: Dict[str, Any] = {}

    config['replacements'], _ = get_variant_config(rules.get('variants'), normalizer)
    config['variant_only'] = rules.get('mode', '') == 'variant-only'

    # parse mutation rules
    config['mutations'] = []
    for rule in rules.get('mutations', []):
        if 'pattern' not in rule:
            raise UsageError("Missing field 'pattern' in mutation configuration.")
        if not isinstance(rule['pattern'], str):
            raise UsageError("Field 'pattern' in mutation configuration "
                             "must be a simple text field.")
        if 'replacements' not in rule:
            raise UsageError("Missing field 'replacements' in mutation configuration.")
        if not isinstance(rule['replacements'], list):
            raise UsageError("Field 'replacements' in mutation configuration "
                             "must be a list of texts.")

        config['mutations'].append((rule['pattern'], rule['replacements']))

    return config


# Analysis section

def create(normalizer: Any, transliterator: Any,
           config: Mapping[str, Any]) -> 'GenericTokenAnalysis':
    """ Create a new token analysis instance for this module.
    """
    return GenericTokenAnalysis(normalizer, transliterator, config)


class GenericTokenAnalysis:
    """ Collects the different transformation rules for normalisation of names
        and provides the functions to apply the transformations.
    """

    def __init__(self, norm: Any, to_ascii: Any, config: Mapping[str, Any]) -> None:
        self.norm = norm
        self.to_ascii = to_ascii
        self.variant_only = config['variant_only']

        # Set up datrie
        self.replacements: Optional[SimpleTrie[List[str]]] = \
            SimpleTrie(config['replacements']) if config['replacements'] else None

        # set up mutation rules
        self.mutations = [MutationVariantGenerator(*cfg) for cfg in config['mutations']]

    def get_canonical_id(self, name: PlaceName) -> str:
        """ Return the normalized form of the name. This is the standard form
            from which possible variants for the name can be derived.
        """
        return cast(str, self.norm.transliterate(name.name)).strip()

    def compute_variants(self, norm_name: str) -> Tuple[List[str], List[str]]:
        """ Compute the spelling variants for the given normalized name
            and transliterate the result.
        """
        variants = self._generate_word_variants(norm_name)

        for mutation in self.mutations:
            variants = mutation.generate(variants)

        varset = set(map(str.strip, variants))
        if self.variant_only:
            varset.discard(norm_name)

        trans = []
        norm = []

        for var in varset:
            t = self.to_ascii.transliterate(var).strip()
            if t:
                trans.append(t)
                norm.append(var)

        return trans, norm

    def _generate_word_variants(self, norm_name: str) -> Iterable[str]:
        baseform = '^ ' + norm_name + ' ^'
        baselen = len(baseform)
        partials = ['']

        startpos = 0
        if self.replacements is not None:
            pos = 0
            force_space = False
            while pos < baselen:
                frm = pos
                repl, pos = self.replacements.longest_prefix(baseform, pos)
                if repl is not None:
                    done = baseform[startpos:frm]
                    partials = [v + done + r
                                for v, r in itertools.product(partials, repl)
                                if not force_space or r.startswith(' ')]
                    if len(partials) > 128:
                        # If too many variants are produced, they are unlikely
                        # to be helpful. Only use the original term.
                        startpos = 0
                        break
                    if baseform[pos - 1] == ' ':
                        pos -= 1
                        force_space = True
                    startpos = pos
                else:
                    pos += 1
                    force_space = False

        # No variants detected? Fast return.
        if startpos == 0:
            return (norm_name, )

        if startpos < baselen:
            return (part[1:] + baseform[startpos:-1] for part in partials)

        return (part[1:-1] for part in partials)


================================================
FILE: src/nominatim_db/tokenizer/token_analysis/generic_mutation.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Creator for mutation variants for the generic token analysis.
"""
from typing import Sequence, Iterable, Iterator, Tuple
import itertools
import logging
import re

from ...errors import UsageError

LOG = logging.getLogger()


def _zigzag(outer: Iterable[str], inner: Iterable[str]) -> Iterator[str]:
    return itertools.chain.from_iterable(itertools.zip_longest(outer, inner, fillvalue=''))


class MutationVariantGenerator:
    """ Generates name variants by applying a regular expression to the name
        and replacing it with one or more variants. When the regular expression
        matches more than once, each occurrence is replaced with all replacement
        patterns.
    """

    def __init__(self, pattern: str, replacements: Sequence[str]):
        self.pattern = re.compile(pattern)
        self.replacements = replacements

        if self.pattern.groups > 0:
            LOG.fatal("The mutation pattern %s contains a capturing group. "
                      "This is not allowed.", pattern)
            raise UsageError("Bad mutation pattern in configuration.")

    def generate(self, names: Iterable[str]) -> Iterator[str]:
        """ Generator function for the name variants. 'names' is an iterable
            over a set of names for which the variants are to be generated.
        """
        for name in names:
            parts = self.pattern.split(name)
            if len(parts) == 1:
                yield name
            else:
                for seps in self._fillers(len(parts)):
                    yield ''.join(_zigzag(parts, seps))

    def _fillers(self, num_parts: int) -> Iterator[Tuple[str, ...]]:
        """ Returns a generator for strings to join the given number of string
            parts in all possible combinations.
        """
        return itertools.product(self.replacements, repeat=num_parts - 1)


================================================
FILE: src/nominatim_db/tokenizer/token_analysis/housenumbers.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Specialized processor for housenumbers. Analyses common housenumber patterns
and creates variants for them.
"""
from typing import Any, List, cast
import re

from ...data.place_name import PlaceName
from .generic_mutation import MutationVariantGenerator

RE_NON_DIGIT = re.compile('[^0-9]')
RE_DIGIT_ALPHA = re.compile(r'(\d)\s*([^\d\s␣])')
RE_ALPHA_DIGIT = re.compile(r'([^\s\d␣])\s*(\d)')
RE_NAMED_PART = re.compile(r'[a-z]{4}')

# Configuration section


def configure(*_: Any) -> None:
    """ All behaviour is currently hard-coded.
    """
    return None

# Analysis section


def create(normalizer: Any, transliterator: Any, config: None) -> 'HousenumberTokenAnalysis':
    """ Create a new token analysis instance for this module.
    """
    return HousenumberTokenAnalysis(normalizer, transliterator)


class HousenumberTokenAnalysis:
    """ Detects common housenumber patterns and normalizes them.
    """
    def __init__(self, norm: Any, trans: Any) -> None:
        self.norm = norm
        self.trans = trans

        self.mutator = MutationVariantGenerator('␣', (' ', ''))

    def get_canonical_id(self, name: PlaceName) -> str:
        """ Return the normalized form of the housenumber.
        """
        # shortcut for number-only numbers, which make up 90% of the data.
        if RE_NON_DIGIT.search(name.name) is None:
            return name.name

        norm = cast(str, self.trans.transliterate(self.norm.transliterate(name.name)))
        # If there is a significant non-numeric part, use as is.
        if RE_NAMED_PART.search(norm) is None:
            # Otherwise add optional spaces between digits and letters.
            (norm_opt, cnt1) = RE_DIGIT_ALPHA.subn(r'\1␣\2', norm)
            (norm_opt, cnt2) = RE_ALPHA_DIGIT.subn(r'\1␣\2', norm_opt)
            # Avoid creating too many variants per number.
            if cnt1 + cnt2 <= 4:
                return norm_opt

        return norm

    def compute_variants(self, norm_name: str) -> List[str]:
        """ Compute the spelling variants for the given normalized housenumber.

            Generates variants for optional spaces (marked with '␣').
        """
        return list(self.mutator.generate([norm_name]))


================================================
FILE: src/nominatim_db/tokenizer/token_analysis/postcodes.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Specialized processor for postcodes. Supports a 'lookup' variant of the
token, which produces variants with optional spaces.
"""
from typing import Any, List

from ...data.place_name import PlaceName
from .generic_mutation import MutationVariantGenerator

# Configuration section


def configure(*_: Any) -> None:
    """ All behaviour is currently hard-coded.
    """
    return None

# Analysis section


def create(normalizer: Any, transliterator: Any, config: None) -> 'PostcodeTokenAnalysis':
    """ Create a new token analysis instance for this module.
    """
    return PostcodeTokenAnalysis(normalizer, transliterator)


class PostcodeTokenAnalysis:
    """ Special normalization and variant generation for postcodes.

        This analyser must not be used with anything but postcodes as
        it follows some special rules: the canonial ID is the form that
        is used for the output. `compute_variants` then needs to ensure that
        the generated variants once more follow the standard normalization
        and transliteration, so that postcodes are correctly recognised by
        the search algorithm.
    """
    def __init__(self, norm: Any, trans: Any) -> None:
        self.norm = norm
        self.trans = trans

        self.mutator = MutationVariantGenerator(' ', (' ', ''))

    def get_canonical_id(self, name: PlaceName) -> str:
        """ Return the standard form of the postcode.
        """
        return name.name.strip().upper()

    def compute_variants(self, norm_name: str) -> List[str]:
        """ Compute the spelling variants for the given normalized postcode.

            Takes the canonical form of the postcode, normalizes it using the
            standard rules and then creates variants of the result where
            all spaces are optional.
        """
        # Postcodes follow their own transliteration rules.
        # Make sure at this point, that the terms are normalized in a way
        # that they are searchable with the standard transliteration rules.
        return [self.trans.transliterate(term) for term in
                self.mutator.generate([self.norm.transliterate(norm_name)]) if term]


================================================
FILE: src/nominatim_db/tokenizer/token_analysis/simple_trie.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Simple dict-based implementation of a trie structure.
"""
from typing import TypeVar, Generic, Tuple, Optional, List, Dict
from collections import defaultdict

T = TypeVar('T')


class SimpleTrie(Generic[T]):
    """ A simple read-only trie structure.
        This structure supports examply one lookup operation,
        which is longest-prefix lookup.
    """

    def __init__(self, data: Optional[List[Tuple[str, T]]] = None) -> None:
        self._tree: Dict[str, 'SimpleTrie[T]'] = defaultdict(SimpleTrie[T])
        self._value: Optional[T] = None
        self._prefix = ''

        if data:
            for key, value in data:
                self._add(key, 0, value)

            self._make_compact()

    def _add(self, word: str, pos: int, value: T) -> None:
        """ (Internal) Add a sub-word to the trie.
            The word is added from index 'pos'. If the sub-word to add
            is empty, then the trie saves the given value.
        """
        if pos < len(word):
            self._tree[word[pos]]._add(word, pos + 1, value)
        else:
            self._value = value

    def _make_compact(self) -> None:
        """ (Internal) Compress tree where there is exactly one subtree
            and no value.

            Compression works recursively starting at the leaf.
        """
        for t in self._tree.values():
            t._make_compact()

        if len(self._tree) == 1 and self._value is None:
            assert not self._prefix
            for k, v in self._tree.items():
                self._prefix = k + v._prefix
                self._tree = v._tree
                self._value = v._value

    def longest_prefix(self, word: str, start: int = 0) -> Tuple[Optional[T], int]:
        """ Return the longest prefix match for the given word starting at
            the position 'start'.

            The function returns a tuple with the value for the longest match and
            the position of the word after the match. If no match was found at
            all, the function returns (None, start).
        """
        cur = self
        pos = start
        result: Tuple[Optional[T], int] = None, start

        while True:
            if cur._prefix:
                if not word.startswith(cur._prefix, pos):
                    return result
                pos += len(cur._prefix)

            if cur._value:
                result = cur._value, pos

            if pos >= len(word) or word[pos] not in cur._tree:
                return result

            cur = cur._tree[word[pos]]
            pos += 1


================================================
FILE: src/nominatim_db/tools/__init__.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Module with functions for importing, updating Nominatim databases
as well as general maintenance helpers.
"""


================================================
FILE: src/nominatim_db/tools/add_osm_data.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Function to add additional OSM data from a file or the API into the database.
"""
from typing import Any, MutableMapping
from pathlib import Path
import logging
import urllib

from ..db.connection import connect
from ..utils.url_utils import get_url
from .exec_utils import run_osm2pgsql

LOG = logging.getLogger()


def _run_osm2pgsql(dsn: str, options: MutableMapping[str, Any]) -> None:
    run_osm2pgsql(options)

    # Handle deletions
    with connect(dsn) as conn:
        with conn.cursor() as cur:
            cur.execute('SELECT flush_deleted_places()')
        conn.commit()


def add_data_from_file(dsn: str, fname: str, options: MutableMapping[str, Any]) -> int:
    """ Adds data from a OSM file to the database. The file may be a normal
        OSM file or a diff file in all formats supported by libosmium.
    """
    options['import_file'] = Path(fname)
    options['append'] = True
    _run_osm2pgsql(dsn, options)

    # No status update. We don't know where the file came from.
    return 0


def add_osm_object(dsn: str, osm_type: str, osm_id: int, use_main_api: bool,
                   options: MutableMapping[str, Any]) -> int:
    """ Add or update a single OSM object from the latest version of the
        API.
    """
    if use_main_api:
        base_url = f'https://www.openstreetmap.org/api/0.6/{osm_type}/{osm_id}'
        if osm_type in ('way', 'relation'):
            base_url += '/full'
    else:
        # use Overpass API
        if osm_type == 'node':
            data = f'node({osm_id});out meta;'
        elif osm_type == 'way':
            data = f'(way({osm_id});>;);out meta;'
        else:
            data = f'(rel(id:{osm_id});>;);out meta;'
        base_url = 'https://overpass-api.de/api/interpreter?' \
                   + urllib.parse.urlencode({'data': data})

    options['append'] = True
    options['import_data'] = get_url(base_url).encode('utf-8')

    _run_osm2pgsql(dsn, options)

    return 0


================================================
FILE: src/nominatim_db/tools/admin.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for database analysis and maintenance.
"""
from typing import Optional, Tuple, Any, cast
import logging

import psycopg
from psycopg.types.json import Json

from ..typing import DictCursorResult
from ..config import Configuration
from ..db.connection import connect, Cursor, register_hstore
from ..db.sql_preprocessor import SQLPreprocessor
from ..errors import UsageError
from ..tokenizer import factory as tokenizer_factory
from ..data.place_info import PlaceInfo

LOG = logging.getLogger()


def _get_place_info(cursor: Cursor, osm_id: Optional[str],
                    place_id: Optional[int]) -> DictCursorResult:
    sql = """SELECT place_id, extra.*
             FROM placex, LATERAL placex_indexing_prepare(placex) as extra
          """

    values: Tuple[Any, ...]
    if osm_id:
        osm_type = osm_id[0].upper()
        if osm_type not in 'NWR' or not osm_id[1:].isdigit():
            LOG.fatal('OSM ID must be of form . Got: %s', osm_id)
            raise UsageError("OSM ID parameter badly formatted")

        sql += ' WHERE placex.osm_type = %s AND placex.osm_id = %s'
        values = (osm_type, int(osm_id[1:]))
    elif place_id is not None:
        sql += ' WHERE placex.place_id = %s'
        values = (place_id, )
    else:
        LOG.fatal("No OSM object given to index.")
        raise UsageError("OSM object not found")

    cursor.execute(sql + ' LIMIT 1', values)

    if cursor.rowcount < 1:
        LOG.fatal("OSM object %s not found in database.", osm_id)
        raise UsageError("OSM object not found")

    return cast(DictCursorResult, cursor.fetchone())


def analyse_indexing(config: Configuration, osm_id: Optional[str] = None,
                     place_id: Optional[int] = None) -> None:
    """ Analyse indexing of a single Nominatim object.
    """
    with connect(config.get_libpq_dsn()) as conn:
        register_hstore(conn)
        with conn.cursor(row_factory=psycopg.rows.dict_row) as cur:
            place = _get_place_info(cur, osm_id, place_id)

            cur.execute("update placex set indexed_status = 2 where place_id = %s",
                        (place['place_id'], ))

            cur.execute("""SET auto_explain.log_min_duration = '0';
                           SET auto_explain.log_analyze = 'true';
                           SET auto_explain.log_nested_statements = 'true';
                           LOAD 'auto_explain';
                           SET client_min_messages = LOG;
                           SET log_min_messages = FATAL""")

            tokenizer = tokenizer_factory.get_tokenizer_for_db(config)

            # Enable printing of messages.
            conn.add_notice_handler(lambda diag: print(diag.message_primary))

            with tokenizer.name_analyzer() as analyzer:
                cur.execute("""UPDATE placex
                               SET indexed_status = 0, address = %s, token_info = %s,
                               name = %s, linked_place_id = %s
                               WHERE place_id = %s""",
                            (place['address'],
                             Json(analyzer.process_place(PlaceInfo(place))),
                             place['name'], place['linked_place_id'], place['place_id']))

        # we do not want to keep the results
        conn.rollback()


def clean_deleted_relations(config: Configuration, age: str) -> None:
    """ Clean deleted relations older than a given age
    """
    with connect(config.get_libpq_dsn()) as conn:
        with conn.cursor() as cur:
            try:
                cur.execute("""SELECT place_force_delete(p.place_id)
                            FROM import_polygon_delete d, placex p
                            WHERE p.osm_type = d.osm_type AND p.osm_id = d.osm_id
                            AND age(p.indexed_date) > %s::interval""",
                            (age, ))
            except psycopg.DataError as exc:
                raise UsageError('Invalid PostgreSQL time interval format') from exc
        conn.commit()


def grant_ro_access(dsn: str, config: Configuration) -> None:
    """ Grant read-only access to the web user for all Nominatim tables.
        This can be used to grant access to a different user after import.
    """
    with connect(dsn) as conn:
        sql = SQLPreprocessor(conn, config)
        sql.run_sql_file(conn, 'grants.sql')


================================================
FILE: src/nominatim_db/tools/check_database.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Collection of functions that check if the database is complete and functional.
"""
from typing import Callable, Optional, Any, Union, Tuple, Mapping, List
from enum import Enum
from textwrap import dedent

from ..config import Configuration
from ..db.connection import connect, Connection, \
                            index_exists, table_exists, execute_scalar
from ..db import properties
from ..errors import UsageError
from ..tokenizer import factory as tokenizer_factory
from . import freeze
from ..version import NOMINATIM_VERSION, parse_version

CHECKLIST = []


class CheckState(Enum):
    """ Possible states of a check. FATAL stops check execution entirely.
    """
    OK = 0
    FAIL = 1
    FATAL = 2
    NOT_APPLICABLE = 3
    WARN = 4


CheckResult = Union[CheckState, Tuple[CheckState, Mapping[str, Any]]]
CheckFunc = Callable[[Connection, Configuration], CheckResult]


def _check(hint: Optional[str] = None) -> Callable[[CheckFunc], CheckFunc]:
    """ Decorator for checks. It adds the function to the list of
        checks to execute and adds the code for printing progress messages.
    """
    def decorator(func: CheckFunc) -> CheckFunc:
        title = (func.__doc__ or '').split('\n', 1)[0].strip()

        def run_check(conn: Connection, config: Configuration) -> CheckState:
            print(title, end=' ... ')
            ret = func(conn, config)
            if isinstance(ret, tuple):
                ret, params = ret
            else:
                params = {}
            if ret == CheckState.OK:
                print('\033[92mOK\033[0m')
            elif ret == CheckState.WARN:
                print('\033[93mWARNING\033[0m')
                if hint:
                    print('')
                    print(dedent(hint.format(**params)))
            elif ret == CheckState.NOT_APPLICABLE:
                print('not applicable')
            else:
                print('\x1B[31mFailed\033[0m')
                if hint:
                    print(dedent(hint.format(**params)))
            return ret

        CHECKLIST.append(run_check)
        return run_check

    return decorator


class _BadConnection:

    def __init__(self, msg: str) -> None:
        self.msg = msg

    def close(self) -> None:
        """ Dummy function to provide the implementation.
        """


def check_database(config: Configuration) -> int:
    """ Run a number of checks on the database and return the status.
    """
    try:
        conn = connect(config.get_libpq_dsn())
    except UsageError as err:
        conn = _BadConnection(str(err))  # type: ignore[assignment]

    overall_result = 0
    for check in CHECKLIST:
        ret = check(conn, config)
        if ret == CheckState.FATAL:
            conn.close()
            return 1
        if ret in (CheckState.FATAL, CheckState.FAIL):
            overall_result = 1

    conn.close()
    return overall_result


def _get_indexes(conn: Connection) -> List[str]:
    indexes = ['idx_place_addressline_address_place_id',
               'idx_placex_rank_search',
               'idx_placex_rank_address',
               'idx_placex_parent_place_id',
               'idx_placex_geometry_reverse_lookupplacenode',
               'idx_placex_geometry_reverse_lookuppolygon',
               'idx_placex_geometry_placenode',
               'idx_osmline_parent_place_id',
               'idx_osmline_parent_osm_id',
               'idx_location_postcodes_id',
               'idx_location_postcodes_postcode'
               ]

    # These won't exist if --reverse-only import was used
    if table_exists(conn, 'search_name'):
        indexes.extend(('idx_search_name_nameaddress_vector',
                        'idx_search_name_name_vector',
                        'idx_search_name_centroid',
                        'idx_placex_housenumber',
                        'idx_osmline_parent_osm_id_with_hnr'))

    # These won't exist if --no-updates import was used
    if table_exists(conn, 'place'):
        indexes.extend(('idx_location_area_country_place_id',
                        'idx_place_osm_unique',
                        'idx_placex_rank_address_sector',
                        'idx_placex_rank_boundaries_sector'))

    return indexes


# CHECK FUNCTIONS
#
# Functions are executed in the order they appear here.

@_check(hint="""\
             {error}

             Hints:
             * Is the database server started?
             * Check the NOMINATIM_DATABASE_DSN variable in your local .env
             * Try connecting to the database with the same settings

             Project directory: {config.project_dir}
             Current setting of NOMINATIM_DATABASE_DSN: {config.DATABASE_DSN}
             """)
def check_connection(conn: Any, config: Configuration) -> CheckResult:
    """ Checking database connection
    """
    if isinstance(conn, _BadConnection):
        return CheckState.FATAL, dict(error=conn.msg, config=config)

    return CheckState.OK


@_check(hint="""\
             Database version ({db_version}) doesn't match Nominatim version ({nom_version})

             Hints:
             {instruction}

             Project directory: {config.project_dir}
             Current setting of NOMINATIM_DATABASE_DSN: {config.DATABASE_DSN}
             """)
def check_database_version(conn: Connection, config: Configuration) -> CheckResult:
    """ Checking database_version matches Nominatim software version
    """

    db_version_str = None
    if not table_exists(conn, 'nominatim_properties'):
        instruction = 'Are you connecting to the correct database?'
    else:
        db_version_str = properties.get_property(conn, 'database_version')

        if db_version_str is None:
            instruction = 'Database version not found. Did the import finish?'
        else:
            db_version = parse_version(db_version_str)

            if db_version == NOMINATIM_VERSION:
                return CheckState.OK

            instruction = (
                "Run migrations: 'nominatim admin --migrate'"
                if db_version < NOMINATIM_VERSION
                else 'You need to upgrade the Nominatim software.'
            ) + ' Check the Migration chapter of the Administration Guide.'

    return CheckState.FATAL, dict(db_version=db_version_str,
                                  nom_version=NOMINATIM_VERSION,
                                  instruction=instruction,
                                  config=config)


@_check(hint="""\
             placex table not found

             Hints:
             * Are you connecting to the correct database?
             * Did the import process finish without errors?

             Project directory: {config.project_dir}
             Current setting of NOMINATIM_DATABASE_DSN: {config.DATABASE_DSN}
             """)
def check_placex_table(conn: Connection, config: Configuration) -> CheckResult:
    """ Checking for placex table
    """
    if table_exists(conn, 'placex'):
        return CheckState.OK

    return CheckState.FATAL, dict(config=config)


@_check(hint="""placex table has no data. Did the import finish successfully?""")
def check_placex_size(conn: Connection, _: Configuration) -> CheckResult:
    """ Checking for placex content
    """
    cnt = execute_scalar(conn, 'SELECT count(*) FROM (SELECT * FROM placex LIMIT 100) x')

    return CheckState.OK if cnt > 0 else CheckState.FATAL


@_check(hint="""{msg}""")
def check_tokenizer(_: Connection, config: Configuration) -> CheckResult:
    """ Checking that tokenizer works
    """
    try:
        tokenizer = tokenizer_factory.get_tokenizer_for_db(config)
    except UsageError:
        return CheckState.FAIL, dict(msg="""\
            Cannot load tokenizer. Did the import finish successfully?""")

    result = tokenizer.check_database(config)

    if result is None:
        return CheckState.OK

    return CheckState.FAIL, dict(msg=result)


@_check(hint="""\
             Wikipedia/Wikidata importance tables missing.
             Quality of search results may be degraded. Reverse geocoding is unaffected.
             See https://nominatim.org/release-docs/latest/admin/Import/#wikipediawikidata-rankings
             """)
def check_existance_wikipedia(conn: Connection, _: Configuration) -> CheckResult:
    """ Checking for wikipedia/wikidata data
    """
    if not table_exists(conn, 'search_name') or not table_exists(conn, 'place'):
        return CheckState.NOT_APPLICABLE

    if table_exists(conn, 'wikimedia_importance'):
        cnt = execute_scalar(conn, 'SELECT count(*) FROM wikimedia_importance')
    else:
        cnt = execute_scalar(conn, 'SELECT count(*) FROM wikipedia_article')

    return CheckState.WARN if cnt == 0 else CheckState.OK


@_check(hint="""\
             The indexing didn't finish. {count} entries are not yet indexed.

             To index the remaining entries, run:   {index_cmd}
             """)
def check_indexing(conn: Connection, _: Configuration) -> CheckResult:
    """ Checking indexing status
    """
    cnt = execute_scalar(conn, 'SELECT count(*) FROM placex WHERE indexed_status > 0')

    if cnt == 0:
        return CheckState.OK

    if freeze.is_frozen(conn):
        index_cmd = """\
            Database is marked frozen, it cannot be updated.
            Low counts of unindexed places are fine."""
        return CheckState.WARN, dict(count=cnt, index_cmd=index_cmd)

    if index_exists(conn, 'idx_placex_rank_search'):
        # Likely just an interrupted update.
        index_cmd = 'nominatim index'
    else:
        # Looks like the import process got interrupted.
        index_cmd = 'nominatim import --continue indexing'

    return CheckState.FAIL, dict(count=cnt, index_cmd=index_cmd)


@_check(hint="""\
             The following indexes are missing:
               {indexes}

             Rerun the index creation with:   nominatim import --continue db-postprocess
             """)
def check_database_indexes(conn: Connection, _: Configuration) -> CheckResult:
    """ Checking that database indexes are complete
    """
    missing = []
    for index in _get_indexes(conn):
        if not index_exists(conn, index):
            missing.append(index)

    if missing:
        return CheckState.FAIL, dict(indexes='\n  '.join(missing))

    return CheckState.OK


@_check(hint="""\
             At least one index is invalid. That can happen, e.g. when index creation was
             disrupted and later restarted. You should delete the affected indices
             and recreate them.

             Invalid indexes:
               {indexes}
             """)
def check_database_index_valid(conn: Connection, _: Configuration) -> CheckResult:
    """ Checking that all database indexes are valid
    """
    with conn.cursor() as cur:
        cur.execute(""" SELECT relname FROM pg_class, pg_index
                        WHERE pg_index.indisvalid = false
                        AND pg_index.indexrelid = pg_class.oid""")

        broken = [c[0] for c in cur]

    if broken:
        return CheckState.FAIL, dict(indexes='\n  '.join(broken))

    return CheckState.OK


@_check(hint="""\
             {error}
             Run TIGER import again:   nominatim add-data --tiger-data 
             """)
def check_tiger_table(conn: Connection, config: Configuration) -> CheckResult:
    """ Checking TIGER external data table.
    """
    if not config.get_bool('USE_US_TIGER_DATA'):
        return CheckState.NOT_APPLICABLE

    if not table_exists(conn, 'location_property_tiger'):
        return CheckState.FAIL, dict(error='TIGER data table not found.')

    if execute_scalar(conn, 'SELECT count(*) FROM location_property_tiger') == 0:
        return CheckState.FAIL, dict(error='TIGER data table is empty.')

    return CheckState.OK


================================================
FILE: src/nominatim_db/tools/collect_os_info.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Collection of host system information including software versions, memory,
storage, and database configuration.
"""
import os
import subprocess
import sys
from pathlib import Path
from typing import List, Optional, Union

import psutil

from ..config import Configuration
from ..db.connection import connect, server_version_tuple, execute_scalar
from ..version import NOMINATIM_VERSION


def friendly_memory_string(mem: float) -> str:
    """Create a user friendly string for the amount of memory specified as mem"""
    mem_magnitude = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
    mag = 0
    # determine order of magnitude
    while mem > 1000:
        mem /= 1000
        mag += 1

    return f"{mem:.1f} {mem_magnitude[mag]}"


def run_command(cmd: Union[str, List[str]]) -> str:
    """Runs a command using the shell and returns the output from stdout"""
    try:
        if sys.version_info < (3, 7):
            cap_out = subprocess.run(cmd, stdout=subprocess.PIPE, check=False)
        else:
            cap_out = subprocess.run(cmd, capture_output=True, check=False)
        return cap_out.stdout.decode("utf-8")
    except FileNotFoundError:
        # non-Linux system should end up here
        return f"Unknown (unable to find the '{cmd}' command)"


def os_name_info() -> str:
    """Obtain Operating System Name (and possibly the version)"""
    os_info = None
    # man page os-release(5) details meaning of the fields
    if Path("/etc/os-release").is_file():
        os_info = from_file_find_line_portion(
            "/etc/os-release", "PRETTY_NAME", "=")
    # alternative location
    elif Path("/usr/lib/os-release").is_file():
        os_info = from_file_find_line_portion(
            "/usr/lib/os-release", "PRETTY_NAME", "="
        )

    # fallback on Python's os name
    if os_info is None or os_info == "":
        os_info = os.name

    # if the above is insufficient, take a look at neofetch's approach to OS detection
    return os_info


# Note: Intended to be used on informational files like /proc
def from_file_find_line_portion(
    filename: str, start: str, sep: str, fieldnum: int = 1
) -> Optional[str]:
    """open filename, finds the line starting with the 'start' string.
    Splits the line using separator and returns a "fieldnum" from the split."""
    with open(filename, encoding='utf8') as file:
        result = ""
        for line in file:
            if line.startswith(start):
                result = line.split(sep)[fieldnum].strip()
        return result


def get_postgresql_config(version: int) -> str:
    """Retrieve postgres configuration file"""
    try:
        with open(f"/etc/postgresql/{version}/main/postgresql.conf", encoding='utf8') as file:
            db_config = file.read()
            file.close()
            return db_config
    except IOError:
        return f"**Could not read '/etc/postgresql/{version}/main/postgresql.conf'**"


def report_system_information(config: Configuration) -> None:
    """Generate a report about the host system including software versions, memory,
    storage, and database configuration."""

    with connect(config.get_libpq_dsn(), dbname='postgres') as conn:
        postgresql_ver: str = '.'.join(map(str, server_version_tuple(conn)))

        with conn.cursor() as cur:
            cur.execute("SELECT datname FROM pg_catalog.pg_database WHERE datname=%s",
                        (config.get_database_params()['dbname'], ))
            nominatim_db_exists = cur.rowcount > 0

    if nominatim_db_exists:
        with connect(config.get_libpq_dsn()) as conn:
            postgis_ver: str = execute_scalar(conn, 'SELECT postgis_lib_version()')
    else:
        postgis_ver = "Unable to connect to database"

    postgresql_config: str = get_postgresql_config(int(float(postgresql_ver)))

    # Note: psutil.disk_partitions() is similar to run_command("lsblk")

    # Note: run_command("systemd-detect-virt") only works on Linux, on other OSes
    # should give a message: "Unknown (unable to find the 'systemd-detect-virt' command)"

    # Generates the Markdown report.

    report = f"""
    **Instructions**
    Use this information in your issue report at https://github.com/osm-search/Nominatim/issues
    Redirect the output to a file:
    $ ./collect_os_info.py > report.md


    **Software Environment:**
    - Python version: {sys.version}
    - Nominatim version: {NOMINATIM_VERSION!s}
    - PostgreSQL version: {postgresql_ver}
    - PostGIS version: {postgis_ver}
    - OS: {os_name_info()}


    **Hardware Configuration:**
    - RAM: {friendly_memory_string(psutil.virtual_memory().total)}
    - number of CPUs: {psutil.cpu_count(logical=False)}
    - bare metal/AWS/other cloud service (per systemd-detect-virt(1)):
        {run_command("systemd-detect-virt")}
    - type and size of disks:
    **`df -h` - df - report file system disk space usage: **
    ```
    {run_command(["df", "-h"])}
    ```

    **lsblk - list block devices: **
    ```
    {run_command("lsblk")}
    ```


    **Postgresql Configuration:**
    ```
    {postgresql_config}
    ```
    **Notes**
    Please add any notes about anything above anything above that is incorrect.
"""
    print(report)


================================================
FILE: src/nominatim_db/tools/convert_sqlite.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Exporting a Nominatim database to SQlite.
"""
from typing import Set, Any, Optional, Union
import datetime as dt
import logging
from pathlib import Path

import sqlalchemy as sa

import nominatim_api as napi
from nominatim_api.search.query_analyzer_factory import make_query_analyzer
from nominatim_api.typing import SaSelect, SaRow
from nominatim_api.sql.sqlalchemy_types import Geometry, IntArray

LOG = logging.getLogger()


async def convert(project_dir: Optional[Union[str, Path]],
                  outfile: Path, options: Set[str]) -> None:
    """ Export an existing database to sqlite. The resulting database
        will be usable against the Python frontend of Nominatim.
    """
    api = napi.NominatimAPIAsync(project_dir)

    try:
        outapi = napi.NominatimAPIAsync(project_dir,
                                        {'NOMINATIM_DATABASE_DSN': f"sqlite:dbname={outfile}",
                                         'NOMINATIM_DATABASE_RW': '1'})

        try:
            async with api.begin() as src, outapi.begin() as dest:
                writer = SqliteWriter(src, dest, options)
                await writer.write()
        finally:
            await outapi.close()
    finally:
        await api.close()


class SqliteWriter:
    """ Worker class which creates a new SQLite database.
    """

    def __init__(self, src: napi.SearchConnection,
                 dest: napi.SearchConnection, options: Set[str]) -> None:
        self.src = src
        self.dest = dest
        self.options = options

    async def write(self) -> None:
        """ Create the database structure and copy the data from
            the source database to the destination.
        """
        LOG.warning('Setting up spatialite')
        await self.dest.execute(sa.select(sa.func.InitSpatialMetaData(True, 'WGS84')))

        await self.create_tables()
        await self.copy_data()
        if 'search' in self.options:
            await self.create_word_table()
        await self.create_indexes()

    async def create_tables(self) -> None:
        """ Set up the database tables.
        """
        LOG.warning('Setting up tables')
        if 'search' not in self.options:
            self.dest.t.meta.remove(self.dest.t.search_name)
        else:
            await self.create_class_tables()

        await self.dest.connection.run_sync(self.dest.t.meta.create_all)

        # Convert all Geometry columns to Spatialite geometries
        for table in self.dest.t.meta.sorted_tables:
            for col in table.c:
                if isinstance(col.type, Geometry):
                    await self.dest.execute(sa.select(
                        sa.func.RecoverGeometryColumn(table.name, col.name, 4326,
                                                      col.type.subtype.upper(), 'XY')))

    async def create_class_tables(self) -> None:
        """ Set up the table that serve class/type-specific geometries.
        """
        sql = sa.text("""SELECT tablename FROM pg_tables
                         WHERE tablename LIKE 'place_classtype_%'""")
        for res in await self.src.execute(sql):
            for db in (self.src, self.dest):
                sa.Table(res[0], db.t.meta,
                         sa.Column('place_id', sa.BigInteger),
                         sa.Column('centroid', Geometry))

    async def create_word_table(self) -> None:
        """ Create the word table.
            This table needs the property information to determine the
            correct format. Therefore needs to be done after all other
            data has been copied.
        """
        await make_query_analyzer(self.src)
        await make_query_analyzer(self.dest)
        src = self.src.t.meta.tables['word']
        dest = self.dest.t.meta.tables['word']

        await self.dest.connection.run_sync(dest.create)

        LOG.warning("Copying word table")
        async_result = await self.src.connection.stream(sa.select(src))

        async for partition in async_result.partitions(10000):
            data = [{k: getattr(r, k) for k in r._fields} for r in partition]
            await self.dest.execute(dest.insert(), data)

        await self.dest.connection.run_sync(sa.Index('idx_word_woken', dest.c.word_token).create)

    async def copy_data(self) -> None:
        """ Copy data for all registered tables.
        """
        def _getfield(row: SaRow, key: str) -> Any:
            value = getattr(row, key)
            if isinstance(value, dt.datetime):
                if value.tzinfo is not None:
                    value = value.astimezone(dt.timezone.utc)
            return value

        for table in self.dest.t.meta.sorted_tables:
            LOG.warning("Copying '%s'", table.name)
            async_result = await self.src.connection.stream(self.select_from(table.name))

            async for partition in async_result.partitions(10000):
                data = [{('class_' if k == 'class' else k): _getfield(r, k)
                         for k in r._fields}
                        for r in partition]
                await self.dest.execute(table.insert(), data)

        # Set up a minimal copy of pg_tables used to look up the class tables later.
        pg_tables = sa.Table('pg_tables', self.dest.t.meta,
                             sa.Column('schemaname', sa.Text, default='public'),
                             sa.Column('tablename', sa.Text))
        await self.dest.connection.run_sync(pg_tables.create)
        data = [{'tablename': t} for t in self.dest.t.meta.tables]
        await self.dest.execute(pg_tables.insert().values(data))

    async def create_indexes(self) -> None:
        """ Add indexes necessary for the frontend.
        """
        # reverse place node lookup needs an extra table to simulate a
        # partial index with adaptive buffering.
        await self.dest.execute(sa.text(
            """ CREATE TABLE placex_place_node_areas AS
                  SELECT place_id, ST_Expand(geometry,
                                             14.0 * exp(-0.2 * rank_search) - 0.03) as geometry
                  FROM placex
                  WHERE rank_address between 5 and 25
                        and osm_type = 'N'
                        and linked_place_id is NULL """))
        await self.dest.execute(sa.select(
            sa.func.RecoverGeometryColumn('placex_place_node_areas', 'geometry',
                                          4326, 'GEOMETRY', 'XY')))
        await self.dest.execute(sa.select(sa.func.CreateSpatialIndex(
                                             'placex_place_node_areas', 'geometry')))

        # Remaining indexes.
        await self.create_spatial_index('country_grid', 'geometry')
        await self.create_spatial_index('placex', 'geometry')
        await self.create_spatial_index('osmline', 'linegeo')
        await self.create_spatial_index('tiger', 'linegeo')
        await self.create_index('placex', 'place_id')
        await self.create_index('placex', 'parent_place_id')
        await self.create_index('placex', 'rank_address')
        await self.create_index('addressline', 'place_id')
        await self.create_index('postcode', 'place_id')
        await self.create_index('osmline', 'place_id')
        await self.create_index('tiger', 'place_id')

        if 'search' in self.options:
            await self.create_spatial_index('postcode', 'geometry')
            await self.create_spatial_index('search_name', 'centroid')
            await self.create_index('search_name', 'place_id')
            await self.create_index('osmline', 'parent_place_id')
            await self.create_index('tiger', 'parent_place_id')
            await self.create_search_index()

            for t in self.dest.t.meta.tables:
                if t.startswith('place_classtype_'):
                    await self.dest.execute(sa.select(
                      sa.func.CreateSpatialIndex(t, 'centroid')))

    async def create_spatial_index(self, table: str, column: str) -> None:
        """ Create a spatial index on the given table and column.
        """
        await self.dest.execute(sa.select(
                  sa.func.CreateSpatialIndex(getattr(self.dest.t, table).name, column)))

    async def create_index(self, table_name: str, column: str) -> None:
        """ Create a simple index on the given table and column.
        """
        table = getattr(self.dest.t, table_name)
        await self.dest.connection.run_sync(
            sa.Index(f"idx_{table}_{column}", getattr(table.c, column)).create)

    async def create_search_index(self) -> None:
        """ Create the tables and indexes needed for word lookup.
        """
        LOG.warning("Creating reverse search table")
        rsn = sa.Table('reverse_search_name', self.dest.t.meta,
                       sa.Column('word', sa.Integer()),
                       sa.Column('column', sa.Text()),
                       sa.Column('places', IntArray))
        await self.dest.connection.run_sync(rsn.create)

        tsrc = self.src.t.search_name
        for column in ('name_vector', 'nameaddress_vector'):
            sql = sa.select(sa.func.unnest(getattr(tsrc.c, column)).label('word'),
                            sa.func.ArrayAgg(tsrc.c.place_id).label('places'))\
                    .group_by('word')

            async_result = await self.src.connection.stream(sql)
            async for partition in async_result.partitions(100):
                data = []
                for row in partition:
                    row.places.sort()
                    data.append({'word': row.word,
                                 'column': column,
                                 'places': row.places})
                await self.dest.execute(rsn.insert(), data)

        await self.dest.connection.run_sync(
            sa.Index('idx_reverse_search_name_word', rsn.c.word).create)

    def select_from(self, table: str) -> SaSelect:
        """ Create the SQL statement to select the source columns and rows.
        """
        columns = self.src.t.meta.tables[table].c

        if table == 'placex':
            # SQLite struggles with Geometries that are larger than 5MB,
            # so simplify those.
            return sa.select(*(c for c in columns if not isinstance(c.type, Geometry)),
                             sa.func.ST_AsText(columns.centroid).label('centroid'),
                             sa.func.ST_AsText(
                               sa.case((sa.func.ST_MemSize(columns.geometry) < 5000000,
                                        columns.geometry),
                                       else_=sa.func.ST_SimplifyPreserveTopology(
                                                columns.geometry, 0.0001)
                                       )).label('geometry'))

        sql = sa.select(*(sa.func.ST_AsText(c).label(c.name)
                        if isinstance(c.type, Geometry) else c for c in columns))

        return sql


================================================
FILE: src/nominatim_db/tools/database_import.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for setting up and importing a new Nominatim database.
"""
from typing import Tuple, Optional, Union, Sequence, MutableMapping, Any
import logging
import os
import subprocess
import asyncio
from pathlib import Path

import psutil
import psycopg
from psycopg import sql as pysql

from ..errors import UsageError
from ..config import Configuration
from ..db.connection import connect, get_pg_env, Connection, server_version_tuple, \
                            postgis_version_tuple, drop_tables, table_exists, execute_scalar
from ..db.sql_preprocessor import SQLPreprocessor
from ..db.query_pool import QueryPool
from .exec_utils import run_osm2pgsql
from ..version import POSTGRESQL_REQUIRED_VERSION, POSTGIS_REQUIRED_VERSION

LOG = logging.getLogger()


def _require_version(module: str, actual: Tuple[int, int], expected: Tuple[int, int]) -> None:
    """ Compares the version for the given module and raises an exception
        if the actual version is too old.
    """
    if actual < expected:
        LOG.fatal('Minimum supported version of %s is %d.%d. '
                  'Found version %d.%d.',
                  module, expected[0], expected[1], actual[0], actual[1])
        raise UsageError(f'{module} is too old.')


def _require_loaded(extension_name: str, conn: Connection) -> None:
    """ Check that the given extension is loaded. """
    with conn.cursor() as cur:
        cur.execute('SELECT * FROM pg_extension WHERE extname = %s', (extension_name, ))
        if cur.rowcount <= 0:
            LOG.fatal('Required module %s is not loaded.', extension_name)
            raise UsageError(f'{extension_name} is not loaded.')


def check_existing_database_plugins(dsn: str) -> None:
    """ Check that the database has the required plugins installed."""
    with connect(dsn) as conn:
        _require_version('PostgreSQL server',
                         server_version_tuple(conn),
                         POSTGRESQL_REQUIRED_VERSION)
        _require_version('PostGIS',
                         postgis_version_tuple(conn),
                         POSTGIS_REQUIRED_VERSION)
        _require_loaded('hstore', conn)


def setup_database_skeleton(dsn: str, rouser: Optional[str] = None) -> None:
    """ Create a new database for Nominatim and populate it with the
        essential extensions.

        The function fails when the database already exists or Postgresql or
        PostGIS versions are too old.

        Uses `createdb` to create the database.

        If 'rouser' is given, then the function also checks that the user
        with that given name exists.

        Requires superuser rights by the caller.
    """
    proc = subprocess.run(['createdb'], env=get_pg_env(dsn), check=False)

    if proc.returncode != 0:
        raise UsageError('Creating new database failed.')

    with connect(dsn) as conn:
        _require_version('PostgreSQL server',
                         server_version_tuple(conn),
                         POSTGRESQL_REQUIRED_VERSION)

        if rouser is not None:
            cnt = execute_scalar(conn, 'SELECT count(*) FROM pg_user where usename = %s',
                                 (rouser, ))
            if cnt == 0:
                LOG.fatal("Web user '%s' does not exist. Create it with:\n"
                          "\n      createuser %s", rouser, rouser)
                raise UsageError('Missing read-only user.')

        # Create extensions.
        with conn.cursor() as cur:
            cur.execute('CREATE EXTENSION IF NOT EXISTS hstore')
            cur.execute('CREATE EXTENSION IF NOT EXISTS postgis')
            cur.execute('CREATE EXTENSION IF NOT EXISTS postgis_raster')

        conn.commit()

        _require_version('PostGIS',
                         postgis_version_tuple(conn),
                         POSTGIS_REQUIRED_VERSION)


def import_osm_data(osm_files: Union[Path, Sequence[Path]],
                    options: MutableMapping[str, Any],
                    drop: bool = False, ignore_errors: bool = False) -> None:
    """ Import the given OSM files. 'options' contains the list of
        default settings for osm2pgsql.
    """
    options['import_file'] = osm_files
    options['append'] = False
    options['threads'] = 1

    if not options['flatnode_file'] and options['osm2pgsql_cache'] == 0:
        # Make some educated guesses about cache size based on the size
        # of the import file and the available memory.
        mem = psutil.virtual_memory()
        fsize = 0
        if isinstance(osm_files, list):
            for fname in osm_files:
                fsize += os.stat(str(fname)).st_size
        else:
            fsize = os.stat(str(osm_files)).st_size
        options['osm2pgsql_cache'] = int(min((mem.available + getattr(mem, 'cached', 0)) * 0.75,
                                             fsize * 2) / 1024 / 1024) + 1

    run_osm2pgsql(options)

    with connect(options['dsn']) as conn:
        if not ignore_errors:
            with conn.cursor() as cur:
                cur.execute('SELECT true FROM place LIMIT 1')
                if cur.rowcount == 0:
                    raise UsageError('No data imported by osm2pgsql.')

        if drop:
            drop_tables(conn, 'planet_osm_nodes')
            conn.commit()

    if drop and options['flatnode_file']:
        Path(options['flatnode_file']).unlink()


def create_tables(conn: Connection, config: Configuration, reverse_only: bool = False) -> None:
    """ Create the set of basic tables.
        When `reverse_only` is True, then the main table for searching will
        be skipped and only reverse search is possible.
    """
    SQLPreprocessor(conn, config).run_sql_file(conn, 'tables.sql',
                                               create_reverse_only=reverse_only)

    # reinitiate the preprocessor to get all the newly created tables
    SQLPreprocessor(conn, config).run_sql_file(conn, 'grants.sql')


def create_table_triggers(conn: Connection, config: Configuration) -> None:
    """ Create the triggers for the tables. The trigger functions must already
        have been imported with refresh.create_functions().
    """
    sql = SQLPreprocessor(conn, config)
    sql.run_sql_file(conn, 'table-triggers.sql')


def create_partition_tables(conn: Connection, config: Configuration) -> None:
    """ Create tables that have explicit partitioning.
    """
    sql = SQLPreprocessor(conn, config)
    sql.run_sql_file(conn, 'partition-tables.src.sql')


def truncate_data_tables(conn: Connection) -> None:
    """ Truncate all data tables to prepare for a fresh load.
    """
    with conn.cursor() as cur:
        cur.execute('TRUNCATE placex')
        cur.execute('TRUNCATE place_addressline')
        cur.execute('TRUNCATE location_area')
        cur.execute('TRUNCATE location_area_country')
        cur.execute('TRUNCATE location_property_tiger')
        cur.execute('TRUNCATE location_property_osmline')
        cur.execute('TRUNCATE location_postcodes')
        if table_exists(conn, 'search_name'):
            cur.execute('TRUNCATE search_name')
        cur.execute('DROP SEQUENCE IF EXISTS seq_place')
        cur.execute('CREATE SEQUENCE seq_place start 100000')

        cur.execute("""SELECT tablename FROM pg_tables
                       WHERE tablename LIKE 'location_road_%'""")

        for table in [r[0] for r in list(cur)]:
            cur.execute(pysql.SQL('TRUNCATE {}').format(pysql.Identifier(table)))

    conn.commit()


_COPY_COLUMNS = pysql.SQL(',').join(map(pysql.Identifier,
                                        ('osm_type', 'osm_id', 'class', 'type',
                                         'name', 'admin_level', 'address',
                                         'extratags', 'geometry')))


async def load_data(dsn: str, threads: int) -> None:
    """ Copy data into the word and placex table.
    """
    placex_threads = max(1, threads - 1)

    progress = asyncio.create_task(_progress_print())

    async with QueryPool(dsn, placex_threads + 1) as pool:
        # Copy data from place to placex in  chunks.
        for imod in range(placex_threads):
            await pool.put_query(
                pysql.SQL("""INSERT INTO placex ({columns})
                               SELECT {columns} FROM place
                                WHERE osm_id % {total} = {mod}
                          """).format(columns=_COPY_COLUMNS,
                                      total=pysql.Literal(placex_threads),
                                      mod=pysql.Literal(imod)), None)

        # Interpolations need to be copied separately
        await pool.put_query("""
                INSERT INTO location_property_osmline (osm_id, type, address, linegeo)
                  SELECT osm_id, type, address, geometry
                  FROM place_interpolation
                """, None)

    progress.cancel()

    async with await psycopg.AsyncConnection.connect(dsn) as aconn:
        await aconn.execute('ANALYSE')


async def _progress_print() -> None:
    while True:
        try:
            await asyncio.sleep(1)
        except asyncio.CancelledError:
            print('', flush=True)
            break
        print('.', end='', flush=True)


async def create_search_indices(conn: Connection, config: Configuration,
                                drop: bool = False, threads: int = 1) -> None:
    """ Create tables that have explicit partitioning.
    """

    # If index creation failed and left an index invalid, they need to be
    # cleaned out first, so that the script recreates them.
    with conn.cursor() as cur:
        cur.execute("""SELECT relname FROM pg_class, pg_index
                       WHERE pg_index.indisvalid = false
                             AND pg_index.indexrelid = pg_class.oid""")
        bad_indices = [row[0] for row in list(cur)]
        for idx in bad_indices:
            LOG.info("Drop invalid index %s.", idx)
            cur.execute(pysql.SQL('DROP INDEX {}').format(pysql.Identifier(idx)))
    conn.commit()

    sql = SQLPreprocessor(conn, config)

    await sql.run_parallel_sql_file(config.get_libpq_dsn(),
                                    'indices.sql', min(8, threads), drop=drop)


================================================
FILE: src/nominatim_db/tools/exec_utils.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Helper functions for executing external programs.
"""
from typing import Any, Mapping, List, Optional
import logging
import os
import re
import subprocess
import shutil

from ..db.connection import get_pg_env
from ..errors import UsageError
from ..version import OSM2PGSQL_REQUIRED_VERSION

LOG = logging.getLogger()


def run_osm2pgsql(options: Mapping[str, Any]) -> None:
    """ Run osm2pgsql with the given options.
    """
    _check_osm2pgsql_version(options['osm2pgsql'])

    env = get_pg_env(options['dsn'])

    cmd = [_find_osm2pgsql_cmd(options['osm2pgsql']),
           '--append' if options['append'] else '--create',
           '--slim',
           '--log-progress', 'true',
           '--number-processes', '1' if options['append'] else str(options['threads']),
           '--cache', str(options['osm2pgsql_cache']),
           '--style', str(options['osm2pgsql_style'])
           ]

    env['LUA_PATH'] = ';'.join((str(options['osm2pgsql_style_path'] / '?.lua'),
                                os.environ.get('LUA_PATH', ';')))
    env['THEMEPARK_PATH'] = str(options['osm2pgsql_style_path'] / 'themes')
    if 'THEMEPARK_PATH' in os.environ:
        env['THEMEPARK_PATH'] += ':' + os.environ['THEMEPARK_PATH']
    cmd.extend(('--output', 'flex'))

    for flavour in ('data', 'index'):
        if options['tablespaces'][f"main_{flavour}"]:
            env[f"NOMINATIM_TABLESPACE_PLACE_{flavour.upper()}"] = \
                options['tablespaces'][f"main_{flavour}"]

    if options['flatnode_file']:
        cmd.extend(('--flat-nodes', options['flatnode_file']))

    cmd.extend(_mk_tablespace_options('slim', options))

    if options.get('disable_jit', False):
        env['PGOPTIONS'] = '-c jit=off -c max_parallel_workers_per_gather=0'

    if 'import_data' in options:
        cmd.extend(('-r', 'xml', '-'))
    elif isinstance(options['import_file'], list):
        for fname in options['import_file']:
            cmd.append(str(fname))
    else:
        cmd.append(str(options['import_file']))

    subprocess.run(cmd, cwd=options.get('cwd', '.'),
                   input=options.get('import_data'),
                   env=env, check=True)


def _mk_tablespace_options(ttype: str, options: Mapping[str, Any]) -> List[str]:
    cmds: List[str] = []
    for flavour in ('data', 'index'):
        if options['tablespaces'][f"{ttype}_{flavour}"]:
            cmds.extend((f"--tablespace-{ttype}-{flavour}",
                         options['tablespaces'][f"{ttype}_{flavour}"]))

    return cmds


def _find_osm2pgsql_cmd(cmdline: Optional[str]) -> str:
    if cmdline:
        return cmdline

    in_path = shutil.which('osm2pgsql')
    if in_path is None:
        raise UsageError('osm2pgsql executable not found. Please install osm2pgsql first.')

    return str(in_path)


def _check_osm2pgsql_version(cmdline: Optional[str]) -> None:
    cmd = [_find_osm2pgsql_cmd(cmdline), '--version']

    result = subprocess.run(cmd, capture_output=True, check=True)

    if not result.stderr:
        raise UsageError("osm2pgsql does not print version information.")

    verinfo = result.stderr.decode('UTF-8')

    match = re.search(r'osm2pgsql version (\d+)\.(\d+)', verinfo)
    if match is None:
        raise UsageError(f"No version information found in output: {verinfo}")

    if (int(match[1]), int(match[2])) < OSM2PGSQL_REQUIRED_VERSION:
        raise UsageError(f"osm2pgsql is too old. Found version {match[1]}.{match[2]}. "
                         f"Need at least version {'.'.join(map(str, OSM2PGSQL_REQUIRED_VERSION))}.")


================================================
FILE: src/nominatim_db/tools/freeze.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for removing unnecessary data from the database.
"""
from typing import Optional
from pathlib import Path

from psycopg import sql as pysql

from ..db.connection import Connection, drop_tables, table_exists

UPDATE_TABLES = [
    'address_levels',
    'gb_postcode',
    'import_osmosis_log',
    'location_area%',
    'location_road%',
    'place',
    'place_associated_street',
    'place_entrance',
    'place_postcode',
    'planet_osm_%',
    'search_name_%',
    'us_postcode',
    'wikipedia_%'
]


def drop_update_tables(conn: Connection) -> None:
    """ Drop all tables only necessary for updating the database from
        OSM replication data.
    """
    parts = (pysql.SQL("(tablename LIKE {})").format(pysql.Literal(t)) for t in UPDATE_TABLES)

    with conn.cursor() as cur:
        cur.execute(pysql.SQL("SELECT tablename FROM pg_tables WHERE ")
                    + pysql.SQL(' or ').join(parts))
        tables = [r[0] for r in cur]

    drop_tables(conn, *tables, cascade=True)
    conn.commit()


def drop_flatnode_file(fpath: Optional[Path]) -> None:
    """ Remove the flatnode file if it exists.
    """
    if fpath and fpath.exists():
        fpath.unlink()


def is_frozen(conn: Connection) -> bool:
    """ Returns true if database is in a frozen state
    """
    return table_exists(conn, 'place') is False


================================================
FILE: src/nominatim_db/tools/migration.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for database migration to newer software versions.
"""
from typing import List, Tuple, Callable, Any
import logging

from ..errors import UsageError
from ..config import Configuration
from ..db import properties
from ..db.connection import connect, Connection, \
                            table_exists, register_hstore, table_has_column
from ..db.sql_preprocessor import SQLPreprocessor
from ..version import NominatimVersion, NOMINATIM_VERSION, parse_version
from ..tokenizer import factory as tokenizer_factory
from ..data.country_info import create_country_names, setup_country_config
from .freeze import is_frozen
from . import refresh

LOG = logging.getLogger()

_MIGRATION_FUNCTIONS: List[Tuple[NominatimVersion, Callable[..., None]]] = []


def migrate(config: Configuration, paths: Any) -> int:
    """ Check for the current database version and execute migrations,
        if necessary.
    """
    with connect(config.get_libpq_dsn()) as conn:
        register_hstore(conn)
        if table_exists(conn, 'nominatim_properties'):
            db_version_str = properties.get_property(conn, 'database_version')
        else:
            db_version_str = None

        if db_version_str is not None:
            db_version = parse_version(db_version_str)
        else:
            db_version = None

        if db_version is None or db_version < (4, 3, 0, 0):
            LOG.fatal('Your database version is older than 4.3. '
                      'Direct migration is not possible.\n'
                      'You should strongly consider a reimport. If that is not possible\n'
                      'please upgrade to 4.3 first and then to the newest version.')
            raise UsageError('Migration not possible.')

        if db_version == NOMINATIM_VERSION:
            LOG.warning("Database already at latest version (%s)", db_version_str)
            return 0

        LOG.info("Detected database version: %s", db_version_str)

        for version, func in _MIGRATION_FUNCTIONS:
            if db_version < version:
                title = func.__doc__ or ''
                LOG.warning("Running: %s (%s)", title.split('\n', 1)[0], version)
                kwargs = dict(conn=conn, config=config, paths=paths)
                func(**kwargs)
                conn.commit()

        LOG.warning('Updating SQL functions.')
        refresh.create_functions(conn, config)
        tokenizer = tokenizer_factory.get_tokenizer_for_db(config)
        tokenizer.update_sql_functions(config)

        properties.set_property(conn, 'database_version', str(NOMINATIM_VERSION))

        conn.commit()

    return 0


def _migration(major: int, minor: int, patch: int = 0,
               dbpatch: int = 0) -> Callable[[Callable[..., None]], Callable[..., None]]:
    """ Decorator for a single migration step. The parameters describe the
        version after which the migration is applicable, i.e before changing
        from the given version to the next, the migration is required.

        All migrations are run in the order in which they are defined in this
        file. Do not run global SQL scripts for migrations as you cannot be sure
        that these scripts do the same in later versions.

        Functions will always be reimported in full at the end of the migration
        process, so the migration functions may leave a temporary state behind
        there.
    """
    def decorator(func: Callable[..., None]) -> Callable[..., None]:
        version = NominatimVersion(major, minor, patch, dbpatch)
        _MIGRATION_FUNCTIONS.append((version, func))
        return func

    return decorator


@_migration(4, 4, 99, 0)
def create_postcode_area_lookup_index(conn: Connection, **_: Any) -> None:
    """ Create index needed for looking up postcode areas from postocde points.
    """
    with conn.cursor() as cur:
        cur.execute("""CREATE INDEX IF NOT EXISTS idx_placex_postcode_areas
                       ON placex USING BTREE (country_code, postcode)
                       WHERE osm_type = 'R' AND class = 'boundary' AND type = 'postal_code'
                    """)


@_migration(4, 4, 99, 1)
def create_postcode_parent_index(conn: Connection, **_: Any) -> None:
    """ Create index needed for updating postcodes when a parent changes.
    """
    if table_exists(conn, 'planet_osm_ways'):
        with conn.cursor() as cur:
            cur.execute("""CREATE INDEX IF NOT EXISTS
                             idx_location_postcode_parent_place_id
                             ON location_postcode USING BTREE (parent_place_id)""")


@_migration(5, 1, 99, 0)
def create_placex_entrance_table(conn: Connection, config: Configuration, **_: Any) -> None:
    """ Add the placex_entrance table to store linked-up entrance nodes
    """
    if not table_exists(conn, 'placex_entrance'):
        sqlp = SQLPreprocessor(conn, config)
        sqlp.run_string(conn, """
            -- Table to store location of entrance nodes
            CREATE TABLE placex_entrance (
              place_id BIGINT NOT NULL,
              osm_id BIGINT NOT NULL,
              type TEXT NOT NULL,
              location GEOMETRY(Point, 4326) NOT NULL,
              extratags HSTORE
              );
            CREATE UNIQUE INDEX idx_placex_entrance_place_id_osm_id ON placex_entrance
              USING BTREE (place_id, osm_id) {{db.tablespace.search_index}};
            GRANT SELECT ON placex_entrance TO "{{config.DATABASE_WEBUSER}}" ;
              """)


@_migration(5, 1, 99, 1)
def create_place_entrance_table(conn: Connection, config: Configuration, **_: Any) -> None:
    """ Add the place_entrance table to store incoming entrance nodes
    """
    if not table_exists(conn, 'place_entrance'):
        with conn.cursor() as cur:
            cur.execute("""
            -- Table to store location of entrance nodes
            CREATE TABLE place_entrance (
              osm_id BIGINT NOT NULL,
              type TEXT NOT NULL,
              extratags HSTORE,
              geometry GEOMETRY(Point, 4326) NOT NULL
              );
            CREATE UNIQUE INDEX place_entrance_osm_id_idx ON place_entrance
              USING BTREE (osm_id);
              """)


@_migration(5, 2, 99, 0)
def convert_country_tokens(conn: Connection, config: Configuration, **_: Any) -> None:
    """ Convert country word tokens

        Country tokens now save the country in the info field instead of the
        word. This migration removes all country tokens from the word table
        and reimports the default country name. This means that custom names
        are lost. If you need them back, invalidate the OSM objects containing
        the names by setting indexed_status to 2 and then reindex the database.
    """
    tokenizer = tokenizer_factory.get_tokenizer_for_db(config)
    # There is only one tokenizer at the time of migration, so we make
    # some assumptions here about the structure of the database. This will
    # fail if somebody has written a custom tokenizer.
    with conn.cursor() as cur:
        cur.execute("DELETE FROM word WHERE type = 'C'")
    conn.commit()

    setup_country_config(config)
    create_country_names(conn, tokenizer, config.get_str_list('LANGUAGES'))


@_migration(5, 2, 99, 1)
def create_place_postcode_table(conn: Connection, config: Configuration, **_: Any) -> None:
    """ Restructure postcode tables
    """
    sqlp = SQLPreprocessor(conn, config)
    mutable = not is_frozen(conn)
    has_place_table = table_exists(conn, 'place_postcode')
    has_postcode_table = table_exists(conn, 'location_postcodes')
    if mutable and not has_place_table:
        with conn.cursor() as cur:
            cur.execute(
                """
                CREATE TABLE place_postcode (
                    osm_type CHAR(1) NOT NULL,
                    osm_id BIGINT NOT NULL,
                    postcode TEXT NOT NULL,
                    country_code TEXT,
                    centroid GEOMETRY(Point, 4326) NOT NULL,
                    geometry GEOMETRY(Geometry, 4326)
                )
                """)
            # Move postcode points into the new table
            cur.execute("ALTER TABLE place DISABLE TRIGGER ALL")
            cur.execute(
                """
                WITH deleted AS (
                  DELETE FROM place
                  WHERE (class = 'place' AND type = 'postcode')
                        OR (osm_type = 'R'
                            AND class = 'boundary' AND type = 'postal_code')
                  RETURNING osm_type, osm_id, address->'postcode' as postcode,
                            ST_Centroid(geometry) as centroid,
                            (CASE WHEN class = 'place' THEN NULL ELSE geometry END) as geometry)
                INSERT INTO place_postcode (osm_type, osm_id, postcode, centroid, geometry)
                    (SELECT * FROM deleted
                     WHERE deleted.postcode is not NULL AND deleted.centroid is not NULL)
                """)
            cur.execute(
                """
                CREATE INDEX place_postcode_osm_id_idx ON place_postcode
                  USING BTREE (osm_type, osm_id)
                """)
            cur.execute(
                """
                CREATE INDEX place_postcode_postcode_idx ON place_postcode
                  USING BTREE (postcode)
                """)
            cur.execute("ALTER TABLE place ENABLE TRIGGER ALL")
    if not has_postcode_table:
        sqlp.run_sql_file(conn, 'functions/postcode_triggers.sql')
        with conn.cursor() as cur:
            # create a new location_postcode table which will replace the
            # old one atomically in the end
            cur.execute(
                """
                CREATE TABLE location_postcodes (
                    place_id BIGINT,
                    osm_id BIGINT,
                    rank_search SMALLINT,
                    parent_place_id BIGINT,
                    indexed_status SMALLINT,
                    indexed_date TIMESTAMP,
                    country_code VARCHAR(2),
                    postcode TEXT,
                    centroid Geometry(Point, 4326),
                    geometry Geometry(Geometry, 4326) NOT NULL
                )
                """)
            sqlp.run_string(conn,
                            'GRANT SELECT ON location_postcodes TO "{{config.DATABASE_WEBUSER}}"')
            # remove postcodes from the various auxiliary tables
            cur.execute(
                """
                DELETE FROM place_addressline
                  WHERE address_place_id = ANY(
                    SELECT place_id FROM placex
                      WHERE osm_type = 'R'
                            AND class = 'boundary' AND type = 'postal_code')
                """)
            if mutable:
                cur.execute(
                    """
                    SELECT deleteLocationArea(partition, place_id, rank_search),
                           deleteSearchName(partition, place_id)
                      FROM placex
                      WHERE osm_type = 'R' AND class = 'boundary' AND type = 'postal_code'
                    """)
            if table_exists(conn, 'search_name'):
                cur.execute(
                    """
                    DELETE FROM search_name
                      WHERE place_id = ANY(
                        SELECT place_id FROM placex
                          WHERE osm_type = 'R'
                                AND class = 'boundary' AND type = 'postal_code')
                    """)
            # move postcode areas from placex to location_postcodes
            # avoiding automatic invalidation
            cur.execute("ALTER TABLE placex DISABLE TRIGGER ALL")
            cur.execute(
                """
                WITH deleted AS (
                    DELETE FROM placex
                      WHERE osm_type = 'R'
                            AND class = 'boundary' AND type = 'postal_code'
                      RETURNING place_id, osm_id, rank_search, parent_place_id,
                                indexed_status, indexed_date,
                                country_code, postcode, centroid, geometry)
                INSERT INTO location_postcodes (SELECT * from deleted)
                """)
            cur.execute("ALTER TABLE placex ENABLE TRIGGER ALL")
            # remove any old postcode centroid that would overlap with areas
            cur.execute(
                """
                DELETE FROM location_postcode o USING location_postcodes n
                   WHERE o.country_code = n.country_code
                         AND o.postcode = n.postcode
                """)
            # copy over old postcodes
            cur.execute(
                """
                INSERT INTO location_postcodes
                    (SELECT place_id, NULL, rank_search, parent_place_id,
                            indexed_status, indexed_date, country_code,
                            postcode, geometry,
                            ST_Expand(geometry, 0.05)
                     FROM location_postcode)
                """)
            # add indexes and triggers
            cur.execute("""CREATE INDEX idx_location_postcodes_geometry
                           ON location_postcodes USING GIST(geometry)""")
            cur.execute("""CREATE INDEX idx_location_postcodes_id
                           ON location_postcodes USING BTREE(place_id)""")
            cur.execute("""CREATE INDEX idx_location_postcodes_osmid
                           ON location_postcodes USING BTREE(osm_id)""")
            cur.execute("""CREATE INDEX idx_location_postcodes_postcode
                           ON location_postcodes USING BTREE(postcode, country_code)""")
            cur.execute("""CREATE INDEX idx_location_postcodes_parent_place_id
                           ON location_postcodes USING BTREE(parent_place_id)""")
            cur.execute("""CREATE TRIGGER location_postcodes_before_update
                           BEFORE UPDATE ON location_postcodes
                           FOR EACH ROW EXECUTE PROCEDURE postcodes_update()""")
            cur.execute("""CREATE TRIGGER location_postcodes_before_delete
                           BEFORE DELETE ON location_postcodes
                           FOR EACH ROW EXECUTE PROCEDURE postcodes_delete()""")
            cur.execute("""CREATE TRIGGER location_postcodes_before_insert
                           BEFORE INSERT ON location_postcodes
                           FOR EACH ROW EXECUTE PROCEDURE postcodes_insert()""")
        sqlp.run_string(
            conn,
            """
            CREATE INDEX IF NOT EXISTS idx_placex_geometry_reverse_lookupPolygon_nopostcode
            ON placex USING gist (geometry) {{db.tablespace.search_index}}
            WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
            AND rank_address between 4 and 25
            AND name is not null AND linked_place_id is null;

            CREATE INDEX IF NOT EXISTS idx_placex_geometry_reverse_lookupPlaceNode_nopostcode
              ON placex USING gist (ST_Buffer(geometry, reverse_place_diameter(rank_search)))
              {{db.tablespace.search_index}}
              WHERE rank_address between 4 and 25
                AND name is not null AND linked_place_id is null AND osm_type = 'N';

            CREATE INDEX idx_placex_geometry_placenode_nopostcode ON placex
              USING SPGIST (geometry) {{db.tablespace.address_index}}
              WHERE osm_type = 'N' and rank_search < 26 and class = 'place';
            ANALYSE;
            """)


@_migration(5, 2, 99, 3)
def create_place_interpolation_table(conn: Connection, config: Configuration, **_: Any) -> None:
    """ Create place_interpolation table
    """
    sqlp = SQLPreprocessor(conn, config)
    mutable = not is_frozen(conn)
    has_place_table = table_exists(conn, 'place_interpolation')

    if mutable and not has_place_table:
        # create tables
        conn.execute("""
            CREATE TABLE place_interpolation (
              osm_id BIGINT NOT NULL,
              type TEXT NOT NULL,
              address HSTORE,
              nodes BIGINT[] NOT NULL,
              geometry GEOMETRY(LineString, 4326)
            );

            CREATE TABLE IF NOT EXISTS place_interpolation_to_be_deleted (
              osm_id BIGINT NOT NULL
            );
            """)
        # copy data over
        conn.execute("""
            ALTER TABLE place DISABLE TRIGGER ALL;

            WITH deleted AS (
              DELETE FROM place
                WHERE class='place' and type = 'houses'
                RETURNING osm_type, osm_id,
                          address->'interpolation' as itype,
                          address - 'interpolation'::TEXT as address,
                          geometry)
            INSERT INTO place_interpolation (osm_id, type, address, nodes, geometry)
              (SELECT d.osm_id, d.itype, d.address, p.nodes, d.geometry
                 FROM deleted d, planet_osm_ways p
                 WHERE osm_type = 'W'
                       AND d.osm_id = p.id
                       AND itype is not null
                       AND ST_GeometryType(geometry) = 'ST_LineString');

            ALTER TABLE place ENABLE TRIGGER ALL;
            """)

        # create indices
        conn.execute("""
            CREATE INDEX place_interpolation_nodes_idx ON place_interpolation
              USING gin(nodes);
            CREATE INDEX place_interpolation_osm_id_idx ON place_interpolation
              USING btree(osm_id);
            """)
        # create triggers
        sqlp.run_sql_file(conn, 'functions/interpolation.sql')
        conn.execute("""
            CREATE TRIGGER place_interpolation_before_insert BEFORE INSERT ON place_interpolation
              FOR EACH ROW EXECUTE PROCEDURE place_interpolation_insert();
            CREATE TRIGGER place_interpolation_before_delete BEFORE DELETE ON place_interpolation
              FOR EACH ROW EXECUTE PROCEDURE place_interpolation_delete();
            """)
        # mutate location_property_osmline table
        conn.execute("""
            ALTER TABLE location_property_osmline ADD COLUMN type TEXT;

            UPDATE location_property_osmline
              SET type = coalesce(address->'interpolation', 'all'),
                  address = address - 'interpolation'::TEXT;
            """)


@_migration(5, 2, 99, 4)
def backfill_importance(conn: Connection, **_: Any) -> None:
    """ Backfill missing importance values.
    """
    conn.execute("""UPDATE placex
                    SET importance = 0.40001 - (rank_search::float / 75)
                    WHERE importance is NULL OR importance <= 0
                 """)
    if table_exists(conn, 'search_name')\
       and table_has_column(conn, 'search_name', 'search_rank'):
        conn.execute("""UPDATE search_name
                        SET importance = 0.40001 - (search_rank::float / 75)
                        WHERE importance is NULL OR importance <= 0
                     """)
        conn.execute("ALTER TABLE search_name DROP COLUMN search_rank")


@_migration(5, 2, 99, 5)
def create_place_associated_street_table(conn: Connection, **_: Any) -> None:
    """ Create place_associated_street table for associatedStreet relations
    """
    if table_exists(conn, 'place_associated_street'):
        return

    with conn.cursor() as cur:
        cur.execute("""
            CREATE TABLE place_associated_street (
              relation_id BIGINT NOT NULL,
              member_type TEXT NOT NULL,
              member_id   BIGINT NOT NULL,
              member_role TEXT NOT NULL
            )
        """)

        # Check the osm2pgsql middle table format using the properties table.
        db_format = '1'
        if table_exists(conn, 'osm2pgsql_properties'):
            cur.execute("""
                SELECT value FROM osm2pgsql_properties
                WHERE property = 'db_format'
            """)
            row = cur.fetchone()
            if row is not None:
                db_format = row[0]

        # Populate from planet_osm_rels if the JSONB members format is in use.
        if db_format != '1':
            cur.execute("""
                INSERT INTO place_associated_street
                       (relation_id, member_type, member_id, member_role)
                SELECT id,
                       UPPER(x.type),
                       x.ref,
                       x.role
                  FROM planet_osm_rels,
                       LATERAL jsonb_to_recordset(members)
                         AS x(ref bigint, role text, type text)
                 WHERE tags->>'type' = 'associatedStreet'
                   AND x.role IN ('street', 'house')
            """)
        else:
            LOG.warning(
                '\n'
                'WARNING: Your database uses an old osm2pgsql middle table format.\n'
                'The place_associated_street table has been created but is empty.\n'
                'A full reimport is strongly recommended.\n'
            )

        cur.execute("""
            CREATE INDEX place_associated_street_member_idx
              ON place_associated_street USING BTREE (member_type, member_id);
            CREATE INDEX place_associated_street_relation_id_idx
              ON place_associated_street USING BTREE (relation_id);
        """)


@_migration(5, 3, 99, 0)
def create_associated_street_triggers(conn: Connection, config: Configuration, **_: Any) -> None:
    """ Create triggers for tables on associated street tables.
    """
    SQLPreprocessor(conn, config).run_sql_file(conn, 'functions/associated_street_triggers.sql')
    with conn.cursor() as cur:
        cur.execute("""CREATE OR REPLACE TRIGGER place_associated_street_update
                       AFTER INSERT OR DELETE ON place_associated_street
                       FOR EACH ROW EXECUTE FUNCTION invalidate_associated_street_members()""")


================================================
FILE: src/nominatim_db/tools/postcodes.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for importing, updating and otherwise maintaining the table
of artificial postcode centroids.
"""
from typing import Optional, Tuple, Dict, TextIO
from collections import defaultdict
from pathlib import Path
import csv
import gzip
import logging
from math import isfinite

from psycopg import sql as pysql

from ..db.connection import connect, Connection, table_exists
from ..utils.centroid import PointsCentroid
from ..data.postcode_format import PostcodeFormatter, CountryPostcodeMatcher
from ..tokenizer.base import AbstractAnalyzer, AbstractTokenizer

LOG = logging.getLogger()


def _to_float(numstr: str, max_value: float) -> float:
    """ Convert the number in string into a float. The number is expected
        to be in the range of [-max_value, max_value]. Otherwise rises a
        ValueError.
    """
    num = float(numstr)
    if not isfinite(num) or num <= -max_value or num >= max_value:
        raise ValueError()

    return num


def _extent_to_rank(extent: int) -> int:
    """ Guess a suitable search rank from the extent of a postcode.
    """
    if extent <= 100:
        return 25
    if extent <= 3000:
        return 23
    return 21


class _PostcodeCollector:
    """ Collector for postcodes of a single country.
    """

    def __init__(self, country: str, matcher: Optional[CountryPostcodeMatcher],
                 default_extent: int, exclude: set[str] = set()):
        self.country = country
        self.matcher = matcher
        self.extent = default_extent
        self.exclude = exclude
        self.collected: Dict[str, PointsCentroid] = defaultdict(PointsCentroid)
        self.normalization_cache: Optional[Tuple[str, Optional[str]]] = None

    def add(self, postcode: str, x: float, y: float) -> None:
        """ Add the given postcode to the collection cache. If the postcode
            already existed, it is overwritten with the new centroid.
        """
        if self.matcher is not None:
            normalized: Optional[str]
            if self.normalization_cache and self.normalization_cache[0] == postcode:
                normalized = self.normalization_cache[1]
            else:
                match = self.matcher.match(postcode)
                normalized = self.matcher.normalize(match) if match else None
                self.normalization_cache = (postcode, normalized)

            if normalized and normalized not in self.exclude:
                self.collected[normalized] += (x, y)

    def commit(self, conn: Connection, analyzer: AbstractAnalyzer,
               project_dir: Optional[Path], is_initial: bool) -> None:
        """ Update postcodes for the country from the postcodes selected so far.

            When 'project_dir' is set, then any postcode files found in this
            directory are taken into account as well.
        """
        if project_dir is not None:
            self._update_from_external(analyzer, project_dir)

        if is_initial:
            to_delete = []
        else:
            with conn.cursor() as cur:
                cur.execute("""SELECT postcode FROM location_postcodes
                               WHERE country_code = %s AND osm_id is null""",
                            (self.country, ))
                to_delete = [row[0] for row in cur if row[0] not in self.collected]

        to_add = [dict(zip(('pc', 'x', 'y'), (k, *v.centroid())))
                  for k, v in self.collected.items()]
        self.collected = defaultdict(PointsCentroid)

        LOG.info("Processing country '%s' (%s added, %s deleted).",
                 self.country, len(to_add), len(to_delete))

        with conn.cursor() as cur:
            if to_add:
                columns = ['country_code',
                           'rank_search',
                           'postcode',
                           'centroid',
                           'geometry']
                values = [pysql.Literal(self.country),
                          pysql.Literal(_extent_to_rank(self.extent)),
                          pysql.Placeholder('pc'),
                          pysql.SQL('ST_SetSRID(ST_MakePoint(%(x)s, %(y)s), 4326)'),
                          pysql.SQL("""expand_by_meters(
                                           ST_SetSRID(ST_MakePoint(%(x)s, %(y)s), 4326), {})""")
                               .format(pysql.Literal(self.extent))]
                if is_initial:
                    columns.extend(('place_id', 'indexed_status'))
                    values.extend((pysql.SQL("nextval('seq_place')"), pysql.Literal(1)))

                cur.executemany(pysql.SQL("INSERT INTO location_postcodes ({}) VALUES ({})")
                                     .format(pysql.SQL(',')
                                                  .join(pysql.Identifier(c) for c in columns),
                                             pysql.SQL(',').join(values)),
                                to_add)
            if to_delete:
                cur.execute("""DELETE FROM location_postcodes
                               WHERE country_code = %s and postcode = any(%s)
                                     AND osm_id is null
                            """, (self.country, to_delete))

    def _update_from_external(self, analyzer: AbstractAnalyzer, project_dir: Path) -> None:
        """ Look for an external postcode file for the active country in
            the project directory and add missing postcodes when found.
        """
        csvfile = self._open_external(project_dir)
        if csvfile is None:
            return

        try:
            reader = csv.DictReader(csvfile)
            for row in reader:
                if 'postcode' not in row or 'lat' not in row or 'lon' not in row:
                    LOG.warning("Bad format for external postcode file for country '%s'."
                                " Ignored.", self.country)
                    return
                postcode = analyzer.normalize_postcode(row['postcode'])
                if postcode not in self.collected:
                    try:
                        # Do float conversation separately, it might throw
                        centroid = (_to_float(row['lon'], 180),
                                    _to_float(row['lat'], 90))
                        self.collected[postcode] += centroid
                    except ValueError:
                        LOG.warning("Bad coordinates %s, %s in '%s' country postcode file.",
                                    row['lat'], row['lon'], self.country)

        finally:
            csvfile.close()

    def _open_external(self, project_dir: Path) -> Optional[TextIO]:
        fname = project_dir / f'{self.country}_postcodes.csv'

        if fname.is_file():
            LOG.info("Using external postcode file '%s'.", fname)
            return open(fname, 'r', encoding='utf-8')

        fname = project_dir / f'{self.country}_postcodes.csv.gz'

        if fname.is_file():
            LOG.info("Using external postcode file '%s'.", fname)
            return gzip.open(fname, 'rt', encoding='utf-8')

        return None


def update_postcodes(dsn: str, project_dir: Optional[Path],
                     tokenizer: AbstractTokenizer, force_reimport: bool = False) -> None:
    """ Update the table of postcodes from the input tables
        placex and place_postcode.
    """
    matcher = PostcodeFormatter()
    with tokenizer.name_analyzer() as analyzer:
        with connect(dsn) as conn:
            # Backfill country_code column where required
            conn.execute("""UPDATE place_postcode
                              SET country_code = get_country_code(centroid)
                              WHERE country_code is null
                         """)
            if force_reimport:
                conn.execute("TRUNCATE location_postcodes")
                is_initial = True
            else:
                is_initial = _is_postcode_table_empty(conn)
            if is_initial:
                conn.execute("""ALTER TABLE location_postcodes
                                DISABLE TRIGGER location_postcodes_before_insert""")
            # Now update first postcode areas
            _update_postcode_areas(conn, analyzer, matcher, is_initial)
            # Then fill with estimated postcode centroids from other info
            _update_guessed_postcode(conn, analyzer, matcher, project_dir, is_initial)
            if is_initial:
                conn.execute("""ALTER TABLE location_postcodes
                                ENABLE TRIGGER location_postcodes_before_insert""")
            conn.commit()

        analyzer.update_postcodes_from_db()


def _is_postcode_table_empty(conn: Connection) -> bool:
    """ Check if there are any entries in the location_postcodes table yet.
    """
    with conn.cursor() as cur:
        cur.execute("SELECT place_id FROM location_postcodes LIMIT 1")
        return cur.fetchone() is None


def _insert_postcode_areas(conn: Connection, country_code: str,
                           extent: int, pcs: list[dict[str, str]],
                           is_initial: bool) -> None:
    if pcs:
        with conn.cursor() as cur:
            columns = ['osm_id', 'country_code',
                       'rank_search', 'postcode',
                       'centroid', 'geometry']
            values = [pysql.Identifier('osm_id'), pysql.Identifier('country_code'),
                      pysql.Literal(_extent_to_rank(extent)), pysql.Placeholder('out'),
                      pysql.Identifier('centroid'), pysql.Identifier('geometry')]
            if is_initial:
                columns.extend(('place_id', 'indexed_status'))
                values.extend((pysql.SQL("nextval('seq_place')"), pysql.Literal(1)))

            cur.executemany(
                pysql.SQL(
                    """ INSERT INTO location_postcodes ({})
                            SELECT {} FROM place_postcode
                            WHERE osm_type = 'R'
                                  and country_code = {} and postcode = %(in)s
                                  and geometry is not null
                    """).format(pysql.SQL(',')
                                     .join(pysql.Identifier(c) for c in columns),
                                pysql.SQL(',').join(values),
                                pysql.Literal(country_code)),
                pcs)


def _update_postcode_areas(conn: Connection, analyzer: AbstractAnalyzer,
                           matcher: PostcodeFormatter, is_initial: bool) -> None:
    """ Update the postcode areas made from postcode boundaries.
    """
    # first delete all areas that have gone
    if not is_initial:
        conn.execute(""" DELETE FROM location_postcodes pc
                         WHERE pc.osm_id is not null
                           AND NOT EXISTS(
                                  SELECT * FROM place_postcode pp
                                  WHERE pp.osm_type = 'R' and pp.osm_id = pc.osm_id
                                        and geometry is not null)
                    """)
    # now insert all in country batches, triggers will ensure proper updates
    with conn.cursor() as cur:
        cur.execute(""" SELECT country_code, postcode FROM place_postcode
                        WHERE geometry is not null and osm_type = 'R'
                        ORDER BY country_code
                    """)
        country_code = None
        fmt = None
        pcs = []
        for cc, postcode in cur:
            if country_code is None:
                country_code = cc
                fmt = matcher.get_matcher(country_code)
            elif country_code != cc:
                _insert_postcode_areas(conn, country_code,
                                       matcher.get_postcode_extent(country_code), pcs,
                                       is_initial)
                country_code = cc
                fmt = matcher.get_matcher(country_code)
                pcs = []

            if fmt is not None:
                if (m := fmt.match(postcode)):
                    pcs.append({'out': fmt.normalize(m), 'in': postcode})

        if country_code is not None and pcs:
            _insert_postcode_areas(conn, country_code,
                                   matcher.get_postcode_extent(country_code), pcs,
                                   is_initial)


def _update_guessed_postcode(conn: Connection, analyzer: AbstractAnalyzer,
                             matcher: PostcodeFormatter, project_dir: Optional[Path],
                             is_initial: bool) -> None:
    """ Computes artificial postcode centroids from the placex table,
        potentially enhances it with external data and then updates the
        postcodes in the table 'location_postcodes'.
    """
    # First get the list of countries that currently have postcodes.
    # (Doing this before starting to insert, so it is fast on import.)
    if is_initial:
        todo_countries: set[str] = set()
    else:
        with conn.cursor() as cur:
            cur.execute("""SELECT DISTINCT country_code FROM location_postcodes
                            WHERE osm_id is null""")
            todo_countries = {row[0] for row in cur}

    # Next, get the list of postcodes that are already covered by areas.
    area_pcs = defaultdict(set)
    with conn.cursor() as cur:
        cur.execute("""SELECT country_code, postcode
                       FROM location_postcodes WHERE osm_id is not null
                       ORDER BY country_code""")
        for cc, pc in cur:
            area_pcs[cc].add(pc)

    # Create a temporary table which contains coverage of the postcode areas.
    with conn.cursor() as cur:
        cur.execute("DROP TABLE IF EXISTS _global_postcode_area")
        cur.execute("""CREATE TABLE _global_postcode_area AS
                       (SELECT ST_SubDivide(ST_SimplifyPreserveTopology(
                                              ST_Union(geometry), 0.00001), 128) as geometry
                        FROM place_postcode WHERE geometry is not null)
                    """)
        cur.execute("CREATE INDEX ON _global_postcode_area USING gist(geometry)")

    # Recompute the list of valid postcodes from placex.
    with conn.cursor(name="placex_postcodes") as cur:
        cur.execute("""
            SELECT country_code, postcode, ST_X(centroid), ST_Y(centroid)
              FROM (
                (SELECT country_code, address->'postcode' as postcode, centroid
                  FROM placex WHERE address ? 'postcode')
                UNION
                (SELECT country_code, postcode, centroid
                 FROM place_postcode WHERE geometry is null)
              ) x
              WHERE not postcode like '%,%' and not postcode like '%;%'
                    AND NOT EXISTS(SELECT * FROM _global_postcode_area g
                                   WHERE ST_Intersects(x.centroid, g.geometry))
              ORDER BY country_code""")

        collector = None

        for country, postcode, x, y in cur:
            if collector is None or country != collector.country:
                if collector is not None:
                    collector.commit(conn, analyzer, project_dir, is_initial)
                collector = _PostcodeCollector(country, matcher.get_matcher(country),
                                               matcher.get_postcode_extent(country),
                                               exclude=area_pcs[country])
                todo_countries.discard(country)
            collector.add(postcode, x, y)

        if collector is not None:
            collector.commit(conn, analyzer, project_dir, is_initial)

    # Now handle any countries that are only in the postcode table.
    for country in todo_countries:
        fmt = matcher.get_matcher(country)
        ext = matcher.get_postcode_extent(country)
        _PostcodeCollector(country, fmt, ext,
                           exclude=area_pcs[country]).commit(conn, analyzer, project_dir, False)

    conn.execute("DROP TABLE IF EXISTS _global_postcode_area")


def can_compute(dsn: str) -> bool:
    """ Check that the necessary tables exist so that postcodes can be computed.
    """
    with connect(dsn) as conn:
        return table_exists(conn, 'place_postcode')


================================================
FILE: src/nominatim_db/tools/refresh.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for bringing auxiliary data in the database up-to-date.
"""
from typing import MutableSequence, Tuple, Any, Mapping, Sequence, List
import csv
import gzip
import logging
from pathlib import Path

from psycopg import sql as pysql

from ..config import Configuration
from ..db.connection import Connection, connect, drop_tables
from ..db.utils import execute_file
from ..db.sql_preprocessor import SQLPreprocessor

LOG = logging.getLogger()

OSM_TYPE = {'N': 'node', 'W': 'way', 'R': 'relation'}


def _add_address_level_rows_from_entry(rows: MutableSequence[Tuple[Any, ...]],
                                       entry: Mapping[str, Any]) -> None:
    """ Converts a single entry from the JSON format for address rank
        descriptions into a flat format suitable for inserting into a
        PostgreSQL table and adds these lines to `rows`.
    """
    countries = entry.get('countries') or (None, )
    for key, values in entry['tags'].items():
        for value, ranks in values.items():
            if isinstance(ranks, list):
                rank_search, rank_address = ranks
            else:
                rank_search = rank_address = ranks
            if not value:
                value = None
            for country in countries:
                rows.append((country, key, value, rank_search, rank_address))


def load_address_levels(conn: Connection, table: str, levels: Sequence[Mapping[str, Any]]) -> None:
    """ Replace the `address_levels` table with the contents of `levels'.

        A new table is created any previously existing table is dropped.
        The table has the following columns:
            country, class, type, rank_search, rank_address
    """
    rows: List[Tuple[Any, ...]] = []
    for entry in levels:
        _add_address_level_rows_from_entry(rows, entry)

    drop_tables(conn, table)

    with conn.cursor() as cur:
        cur.execute(pysql.SQL("""CREATE TABLE {} (
                                        country_code varchar(2),
                                        class TEXT,
                                        type TEXT,
                                        rank_search SMALLINT,
                                        rank_address SMALLINT)
                              """).format(pysql.Identifier(table)))

        cur.executemany(pysql.SQL("INSERT INTO {} VALUES (%s, %s, %s, %s, %s)")
                             .format(pysql.Identifier(table)), rows)

        cur.execute(pysql.SQL('CREATE UNIQUE INDEX ON {} (country_code, class, type)')
                    .format(pysql.Identifier(table)))

    conn.commit()


def load_address_levels_from_config(conn: Connection, config: Configuration) -> None:
    """ Replace the `address_levels` table with the content as
        defined in the given configuration. Uses the parameter
        NOMINATIM_ADDRESS_LEVEL_CONFIG to determine the location of the
        configuration file.
    """
    cfg = config.load_sub_configuration('', config='ADDRESS_LEVEL_CONFIG')
    load_address_levels(conn, 'address_levels', cfg)


def create_functions(conn: Connection, config: Configuration,
                     enable_diff_updates: bool = True,
                     enable_debug: bool = False) -> None:
    """ (Re)create the PL/pgSQL functions.
    """
    sql = SQLPreprocessor(conn, config)

    sql.run_sql_file(conn, 'functions.sql',
                     disable_diff_updates=not enable_diff_updates,
                     debug=enable_debug)


def import_wikipedia_articles(dsn: str, data_path: Path, ignore_errors: bool = False) -> int:
    """ Replaces the wikipedia importance tables with new data.
        The import is run in a single transaction so that the new data
        is replace seamlessly.

        Returns 0 if all was well and 1 if the importance file could not
        be found. Throws an exception if there was an error reading the file.
    """
    if import_importance_csv(dsn, data_path / 'wikimedia-importance.csv.gz') == 0 \
       or import_importance_sql(dsn, data_path / 'wikimedia-importance.sql.gz',
                                ignore_errors) == 0:
        return 0

    return 1


def import_importance_csv(dsn: str, data_file: Path) -> int:
    """ Replace wikipedia importance table with data from a
        single CSV file.

        The file must be a gzipped CSV and have the following columns:
        language, title, importance, wikidata_id

        Other columns may be present but will be ignored.
    """
    if not data_file.exists():
        return 1

    # Only import the first occurrence of a wikidata ID.
    # This keeps indexes and table small.
    wd_done = set()

    with connect(dsn) as conn:
        drop_tables(conn, 'wikipedia_article', 'wikipedia_redirect', 'wikimedia_importance')
        with conn.cursor() as cur:
            cur.execute("""CREATE TABLE wikimedia_importance (
                             language TEXT NOT NULL,
                             title TEXT NOT NULL,
                             importance double precision NOT NULL,
                             wikidata TEXT
                           ) """)

            copy_cmd = """COPY wikimedia_importance(language, title, importance, wikidata)
                          FROM STDIN"""
            with gzip.open(
                    str(data_file), 'rt', encoding='utf-8') as fd, \
                    cur.copy(copy_cmd) as copy:
                for row in csv.DictReader(fd, delimiter='\t', quotechar='|'):
                    wd_id = int(row['wikidata_id'][1:])
                    copy.write_row((row['language'],
                                    row['title'],
                                    row['importance'],
                                    None if wd_id in wd_done else row['wikidata_id']))
                    wd_done.add(wd_id)

            cur.execute("""CREATE INDEX IF NOT EXISTS idx_wikimedia_importance_title
                           ON wikimedia_importance (title)""")
            cur.execute("""CREATE INDEX IF NOT EXISTS idx_wikimedia_importance_wikidata
                           ON wikimedia_importance (wikidata)
                           WHERE wikidata is not null""")

        conn.commit()

    return 0


def import_importance_sql(dsn: str, data_file: Path, ignore_errors: bool) -> int:
    """ Replace wikipedia importance table with data from an SQL file.
    """
    if not data_file.exists():
        return 1

    pre_code = """BEGIN;
                  DROP TABLE IF EXISTS "wikipedia_article";
                  DROP TABLE IF EXISTS "wikipedia_redirect";
                  DROP TABLE IF EXISTS "wikipedia_importance";
               """
    post_code = "COMMIT"
    execute_file(dsn, data_file, ignore_errors=ignore_errors,
                 pre_code=pre_code, post_code=post_code)

    return 0


def import_secondary_importance(dsn: str, data_path: Path, ignore_errors: bool = False) -> int:
    """ Replaces the secondary importance raster data table with new data.

        Returns 0 if all was well and 1 if the raster SQL file could not
        be found. Throws an exception if there was an error reading the file.
    """
    datafile = data_path / 'secondary_importance.sql.gz'
    if not datafile.exists():
        return 1

    execute_file(dsn, datafile, ignore_errors=ignore_errors)

    return 0


def recompute_importance(conn: Connection) -> None:
    """ Recompute wikipedia links and importance for all entries in placex.
        This is a long-running operations that must not be executed in
        parallel with updates.
    """
    with conn.cursor() as cur:
        cur.execute('ALTER TABLE placex DISABLE TRIGGER ALL')
        cur.execute("""
            UPDATE placex SET (wikipedia, importance) =
               (SELECT wikipedia, importance
                FROM compute_importance(extratags, country_code, rank_search, centroid))
            """)
        cur.execute("""
            UPDATE placex s SET wikipedia = d.wikipedia, importance = d.importance
             FROM placex d
             WHERE s.place_id = d.linked_place_id and d.wikipedia is not null
                   and (s.wikipedia is null or s.importance < d.importance);
            """)
        cur.execute("""
            UPDATE search_name s SET importance = p.importance
             FROM placex p
             WHERE s.place_id = p.place_id AND s.importance != p.importance
            """)

        cur.execute('ALTER TABLE placex ENABLE TRIGGER ALL')
    conn.commit()


def invalidate_osm_object(osm_type: str, osm_id: int, conn: Connection,
                          recursive: bool = True) -> None:
    """ Mark the given OSM object for reindexing. When 'recursive' is set
        to True (the default), then all dependent objects are marked for
        reindexing as well.

        'osm_type' must be on of 'N' (node), 'W' (way) or 'R' (relation).
        If the given object does not exist, then nothing happens.
    """
    assert osm_type in ('N', 'R', 'W')

    LOG.warning("Invalidating OSM %s %s%s.",
                OSM_TYPE[osm_type], osm_id,
                ' and its dependent places' if recursive else '')

    with conn.cursor() as cur:
        if recursive:
            sql = """SELECT place_force_update(place_id)
                     FROM placex WHERE osm_type = %s and osm_id = %s"""
        else:
            sql = """UPDATE placex SET indexed_status = 2
                     WHERE osm_type = %s and osm_id = %s"""

        cur.execute(sql, (osm_type, osm_id))


================================================
FILE: src/nominatim_db/tools/replication.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for updating a database from a replication source.
"""
from typing import ContextManager, MutableMapping, Any, Generator, cast, Iterator
from contextlib import contextmanager
import datetime as dt
from enum import Enum
import logging
import time
import types
import urllib.request as urlrequest

from ..errors import UsageError
from ..db import status
from ..db.connection import Connection, connect
from .exec_utils import run_osm2pgsql

try:
    from osmium.replication.server import ReplicationServer
    from osmium import WriteHandler
    from osmium import version as pyo_version
    import requests
except ModuleNotFoundError as exc:
    logging.getLogger().critical("pyosmium not installed. Replication functions not available.\n"
                                 "To install pyosmium via pip: pip install osmium")
    raise UsageError("replication tools not available") from exc

LOG = logging.getLogger()


def init_replication(conn: Connection, base_url: str,
                     socket_timeout: int = 60) -> None:
    """ Set up replication for the server at the given base URL.
    """
    LOG.info("Using replication source: %s", base_url)
    date = status.compute_database_date(conn)

    # margin of error to make sure we get all data
    date -= dt.timedelta(hours=3)

    with _make_replication_server(base_url, socket_timeout) as repl:
        seq = repl.timestamp_to_sequence(date)

    if seq is None:
        LOG.fatal("Cannot reach the configured replication service '%s'.\n"
                  "Does the URL point to a directory containing OSM update data?",
                  base_url)
        raise UsageError("Failed to reach replication service")

    status.set_status(conn, date=date, seq=seq)

    LOG.warning("Updates initialised at sequence %s (%s)", seq, date)


def check_for_updates(conn: Connection, base_url: str,
                      socket_timeout: int = 60) -> int:
    """ Check if new data is available from the replication service at the
        given base URL.
    """
    _, seq, _ = status.get_status(conn)

    if seq is None:
        LOG.error("Replication not set up. "
                  "Please run 'nominatim replication --init' first.")
        return 254

    with _make_replication_server(base_url, socket_timeout) as repl:
        state = repl.get_state_info()

    if state is None:
        LOG.error("Cannot get state for URL %s.", base_url)
        return 253

    if state.sequence <= seq:
        LOG.warning("Database is up to date.")
        return 2

    LOG.warning("New data available (%i => %i).", seq, state.sequence)
    return 0


class UpdateState(Enum):
    """ Possible states after an update has run.
    """

    UP_TO_DATE = 0
    MORE_PENDING = 2
    NO_CHANGES = 3


def update(dsn: str, options: MutableMapping[str, Any],
           socket_timeout: int = 60) -> UpdateState:
    """ Update database from the next batch of data. Returns the state of
        updates according to `UpdateState`.
    """
    with connect(dsn) as conn:
        startdate, startseq, indexed = status.get_status(conn)
        conn.commit()

    if startseq is None:
        LOG.error("Replication not set up. "
                  "Please run 'nominatim replication --init' first.")
        raise UsageError("Replication not set up.")

    assert startdate is not None

    if not indexed and options['indexed_only']:
        LOG.info("Skipping update. There is data that needs indexing.")
        return UpdateState.MORE_PENDING

    last_since_update = dt.datetime.now(dt.timezone.utc) - startdate
    update_interval = dt.timedelta(seconds=options['update_interval'])
    if last_since_update < update_interval:
        duration = (update_interval - last_since_update).seconds
        LOG.warning("Sleeping for %s sec before next update.", duration)
        time.sleep(duration)

    if options['import_file'].exists():
        options['import_file'].unlink()

    # Read updates into file.
    with _make_replication_server(options['base_url'], socket_timeout) as repl:
        outhandler = WriteHandler(str(options['import_file']))
        endseq = repl.apply_diffs(outhandler, startseq + 1,
                                  max_size=options['max_diff_size'] * 1024)
        outhandler.close()

        if endseq is None:
            return UpdateState.NO_CHANGES

        with connect(dsn) as conn:
            run_osm2pgsql_updates(conn, options)

            # Write the current status to the file
            endstate = repl.get_state_info(endseq)
            status.set_status(conn, endstate.timestamp if endstate else None,
                              seq=endseq, indexed=False)
            conn.commit()

    return UpdateState.UP_TO_DATE


def run_osm2pgsql_updates(conn: Connection, options: MutableMapping[str, Any]) -> None:
    """ Run osm2pgsql in append mode.
    """
    # Remove any stale deletion marks.
    with conn.cursor() as cur:
        cur.execute('TRUNCATE place_to_be_deleted')
    conn.commit()

    # Consume updates with osm2pgsql.
    options['append'] = True
    options['disable_jit'] = True
    run_osm2pgsql(options)

    # Handle deletions
    with conn.cursor() as cur:
        cur.execute('SELECT flush_deleted_places()')
    conn.commit()


def _make_replication_server(url: str, timeout: int) -> ContextManager[ReplicationServer]:
    """ Returns a ReplicationServer in form of a context manager.

        Creates a light wrapper around older versions of pyosmium that did
        not support the context manager interface.
    """
    if hasattr(ReplicationServer, '__enter__'):
        # Patches the open_url function for pyosmium >= 3.2
        # where the socket timeout is no longer respected.
        def patched_open_url(self: ReplicationServer, url: urlrequest.Request) -> Any:
            """ Download a resource from the given URL and return a byte sequence
                of the content.
            """
            headers = {"User-Agent": f"Nominatim (pyosmium/{pyo_version.pyosmium_release})"}

            if self.session is not None:
                return self.session.get(url.get_full_url(),
                                        headers=headers, timeout=timeout or None,
                                        stream=True)

            @contextmanager
            def _get_url_with_session() -> Iterator[requests.Response]:
                with requests.Session() as session:
                    request = session.get(url.get_full_url(),
                                          headers=headers, timeout=timeout or None,
                                          stream=True)
                    yield request

            return _get_url_with_session()

        repl = ReplicationServer(url)
        setattr(repl, 'open_url', types.MethodType(patched_open_url, repl))

        return cast(ContextManager[ReplicationServer], repl)

    @contextmanager
    def get_cm() -> Generator[ReplicationServer, None, None]:
        yield ReplicationServer(url)

    return get_cm()


================================================
FILE: src/nominatim_db/tools/special_phrases/__init__.py
================================================


================================================
FILE: src/nominatim_db/tools/special_phrases/importer_statistics.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
    Contains the class which handles statistics for the
    import of special phrases.
"""
import logging
LOG = logging.getLogger()


class SpecialPhrasesImporterStatistics():
    """
        Class handling statistics of the import
        process of special phrases.
    """
    def __init__(self) -> None:
        self._intialize_values()

    def _intialize_values(self) -> None:
        """
            Set all counts for the global
            import to 0.
        """
        self.tables_created = 0
        self.tables_deleted = 0
        self.tables_ignored = 0
        self.invalids = 0

    def notify_one_phrase_invalid(self) -> None:
        """
            Add +1 to the count of invalid entries
            fetched from the wiki.
        """
        self.invalids += 1

    def notify_one_table_created(self) -> None:
        """
            Add +1 to the count of created tables.
        """
        self.tables_created += 1

    def notify_one_table_deleted(self) -> None:
        """
            Add +1 to the count of deleted tables.
        """
        self.tables_deleted += 1

    def notify_one_table_ignored(self) -> None:
        """
            Add +1 to the count of ignored tables.
        """
        self.tables_ignored += 1

    def notify_import_done(self) -> None:
        """
            Print stats for the whole import process
            and reset all values.
        """
        LOG.info('====================================================================')
        LOG.info('Final statistics of the import:')
        LOG.info('- %s phrases were invalid.', self.invalids)
        if self.invalids > 0:
            LOG.info('  Those invalid phrases have been skipped.')
        LOG.info('- %s tables were ignored as they already exist on the database',
                 self.tables_ignored)
        LOG.info('- %s tables were created', self.tables_created)
        LOG.info('- %s tables were deleted from the database', self.tables_deleted)
        if self.tables_deleted > 0:
            LOG.info('  They were deleted as they are not valid anymore.')

        if self.invalids > 0:
            LOG.warning('%s phrases were invalid and have been skipped during the whole process.',
                        self.invalids)

        self._intialize_values()


================================================
FILE: src/nominatim_db/tools/special_phrases/sp_csv_loader.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
    Module containing the SPCsvLoader class.

    The class allows to load phrases from a csv file.
"""
from typing import Iterable
import csv
import os

from ...errors import UsageError
from .special_phrase import SpecialPhrase


class SPCsvLoader:
    """
        Handles loading of special phrases from external csv file.
    """
    def __init__(self, csv_path: str) -> None:
        self.csv_path = csv_path

    def generate_phrases(self) -> Iterable[SpecialPhrase]:
        """ Open and parse the given csv file.
            Create the corresponding SpecialPhrases.
        """
        self._check_csv_validity()

        with open(self.csv_path, encoding='utf-8') as fd:
            reader = csv.DictReader(fd, delimiter=',')
            for row in reader:
                yield SpecialPhrase(row['phrase'], row['class'], row['type'], row['operator'])

    def _check_csv_validity(self) -> None:
        """
            Check that the csv file has the right extension.
        """
        _, extension = os.path.splitext(self.csv_path)

        if extension != '.csv':
            raise UsageError(f'The file {self.csv_path} is not a csv file.')


================================================
FILE: src/nominatim_db/tools/special_phrases/sp_importer.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
    Module containing the class handling the import
    of the special phrases.

    Phrases are analyzed and imported into the database.

    The phrases already present in the database which are not
    valids anymore are removed.
"""
from typing import Iterable, Tuple, Mapping, Sequence, Optional, Set
import logging
import re
from psycopg.sql import Identifier, SQL

from ...typing import Protocol
from ...config import Configuration
from ...db.connection import Connection, drop_tables, index_exists
from .importer_statistics import SpecialPhrasesImporterStatistics
from .special_phrase import SpecialPhrase
from ...tokenizer.base import AbstractTokenizer

LOG = logging.getLogger()


def _classtype_table(phrase_class: str, phrase_type: str) -> str:
    """ Return the name of the table for the given class and type.
    """
    return f'place_classtype_{phrase_class}_{phrase_type}'


class SpecialPhraseLoader(Protocol):
    """ Protocol for classes implementing a loader for special phrases.
    """

    def generate_phrases(self) -> Iterable[SpecialPhrase]:
        """ Generates all special phrase terms this loader can produce.
        """


class SPImporter():
    """
        Class handling the process of special phrases importation into the database.

        Take a sp loader which load the phrases from an external source.
    """
    def __init__(self, config: Configuration, conn: Connection,
                 sp_loader: SpecialPhraseLoader) -> None:
        self.config = config
        self.db_connection = conn
        self.sp_loader = sp_loader
        self.statistics_handler = SpecialPhrasesImporterStatistics()
        self.black_list, self.white_list = self._load_white_and_black_lists()
        self.sanity_check_pattern = re.compile(r'^\w+$')
        # This set will contain all existing phrases to be added.
        # It contains tuples with the following format: (label, class, type, operator)
        self.word_phrases: Set[Tuple[str, str, str, str]] = set()
        # This set will contain all existing place_classtype tables which doesn't match any
        # special phrases class/type on the wiki.
        self.table_phrases_to_delete: Set[str] = set()

    def get_classtype_pairs(self, min: int = 0) -> Set[Tuple[str, str]]:
        """
            Returns list of allowed special phrases from the database,
            restricting to a list of combinations of classes and types
            which occur equal to or more than a specified amount of times.

            Default value for this is 0, which allows everything in database.
        """
        db_combinations = set()

        query = f"""
        SELECT class AS CLS, type AS typ
        FROM placex
        GROUP BY class, type
        HAVING COUNT(*) >= {min}
        """

        with self.db_connection.cursor() as db_cursor:
            db_cursor.execute(SQL(query))
            for row in db_cursor:
                db_combinations.add((row[0], row[1]))

        return db_combinations

    def import_phrases(self, tokenizer: AbstractTokenizer, should_replace: bool,
                       min: int = 0) -> None:
        """
            Iterate through all SpecialPhrases extracted from the
            loader and import them into the database.

            If should_replace is set to True only the loaded phrases
            will be kept into the database. All other phrases already
            in the database will be removed.
        """
        LOG.warning('Special phrases importation starting')
        self._fetch_existing_place_classtype_tables()

        # Store pairs of class/type for further processing
        class_type_pairs = set()

        for phrase in self.sp_loader.generate_phrases():
            result = self._process_phrase(phrase)
            if result:
                class_type_pairs.add(result)

        self._create_classtype_table_and_indexes(class_type_pairs, min)
        if should_replace:
            self._remove_non_existent_tables_from_db()

        self.db_connection.commit()

        with tokenizer.name_analyzer() as analyzer:
            analyzer.update_special_phrases(self.word_phrases, should_replace)

        LOG.warning('Import done.')
        self.statistics_handler.notify_import_done()

    def _fetch_existing_place_classtype_tables(self) -> None:
        """
            Fetch existing place_classtype tables.
            Fill the table_phrases_to_delete set of the class.
        """
        query = """
            SELECT table_name
            FROM information_schema.tables
            WHERE table_schema='public'
            AND table_name like 'place_classtype_%';
        """
        with self.db_connection.cursor() as db_cursor:
            db_cursor.execute(SQL(query))
            for row in db_cursor:
                self.table_phrases_to_delete.add(row[0])

    def _load_white_and_black_lists(self) \
            -> Tuple[Mapping[str, Sequence[str]], Mapping[str, Sequence[str]]]:
        """
            Load white and black lists from phrases-settings.json.
        """
        settings = self.config.load_sub_configuration('phrase-settings.json')

        return settings['blackList'], settings['whiteList']

    def _check_sanity(self, phrase: SpecialPhrase) -> bool:
        """
            Check sanity of given inputs in case somebody added garbage in the wiki.
            If a bad class/type is detected the system will exit with an error.
        """
        class_matchs = self.sanity_check_pattern.findall(phrase.p_class)
        type_matchs = self.sanity_check_pattern.findall(phrase.p_type)

        if not class_matchs or not type_matchs:
            LOG.warning("Bad class/type: %s=%s. It will not be imported",
                        phrase.p_class, phrase.p_type)
            return False
        return True

    def _process_phrase(self, phrase: SpecialPhrase) -> Optional[Tuple[str, str]]:
        """
            Processes the given phrase by checking black and white list
            and sanity.
            Return the class/type pair corresponding to the phrase.
        """

        # blacklisting: disallow certain class/type combinations
        if phrase.p_class in self.black_list.keys() \
           and phrase.p_type in self.black_list[phrase.p_class]:
            return None

        # whitelisting: if class is in whitelist, allow only tags in the list
        if phrase.p_class in self.white_list.keys() \
           and phrase.p_type not in self.white_list[phrase.p_class]:
            return None

        # sanity check, in case somebody added garbage in the wiki
        if not self._check_sanity(phrase):
            self.statistics_handler.notify_one_phrase_invalid()
            return None

        self.word_phrases.add((phrase.p_label, phrase.p_class,
                               phrase.p_type, phrase.p_operator))

        return (phrase.p_class, phrase.p_type)

    def _create_classtype_table_and_indexes(self,
                                            class_type_pairs: Iterable[Tuple[str, str]],
                                            min: int = 0) -> None:
        """
            Create table place_classtype for each given pair.
            Also create indexes on place_id and centroid.
        """
        LOG.warning('Create tables and indexes...')

        sql_tablespace = self.config.TABLESPACE_AUX_DATA
        if sql_tablespace:
            sql_tablespace = ' TABLESPACE ' + sql_tablespace

        with self.db_connection.cursor() as db_cursor:
            db_cursor.execute("CREATE INDEX idx_placex_classtype ON placex (class, type)")

        if min:
            allowed_special_phrases = self.get_classtype_pairs(min)

        for pair in class_type_pairs:
            phrase_class = pair[0]
            phrase_type = pair[1]

            # Will only filter if min is not 0
            if min and (phrase_class, phrase_type) not in allowed_special_phrases:
                LOG.warning("Skipping phrase %s=%s: not in allowed special phrases",
                            phrase_class, phrase_type)
                continue

            table_name = _classtype_table(phrase_class, phrase_type)

            if table_name in self.table_phrases_to_delete:
                self.statistics_handler.notify_one_table_ignored()
                # Remove this table from the ones to delete as it match a
                # class/type still existing on the special phrases of the wiki.
                self.table_phrases_to_delete.remove(table_name)
                # So don't need to create the table and indexes.
                continue

            # Table creation
            self._create_place_classtype_table(sql_tablespace, phrase_class, phrase_type)

            # Indexes creation
            self._create_place_classtype_indexes(sql_tablespace, phrase_class, phrase_type)

            # Grant access on read to the web user.
            self._grant_access_to_webuser(phrase_class, phrase_type)

            self.statistics_handler.notify_one_table_created()

        with self.db_connection.cursor() as db_cursor:
            db_cursor.execute("DROP INDEX idx_placex_classtype")

    def _create_place_classtype_table(self, sql_tablespace: str,
                                      phrase_class: str, phrase_type: str) -> None:
        """
            Create table place_classtype of the given phrase_class/phrase_type
            if doesn't exit.
        """
        table_name = _classtype_table(phrase_class, phrase_type)
        with self.db_connection.cursor() as cur:
            cur.execute(SQL("""CREATE TABLE IF NOT EXISTS {} {} AS
                                 SELECT place_id AS place_id,
                                        st_centroid(geometry) AS centroid
                                 FROM placex
                                 WHERE class = %s AND type = %s
                             """).format(Identifier(table_name), SQL(sql_tablespace)),
                        (phrase_class, phrase_type))

    def _create_place_classtype_indexes(self, sql_tablespace: str,
                                        phrase_class: str, phrase_type: str) -> None:
        """
            Create indexes on centroid and place_id for the place_classtype table.
        """
        index_prefix = f'idx_place_classtype_{phrase_class}_{phrase_type}_'
        base_table = _classtype_table(phrase_class, phrase_type)
        # Index on centroid
        if not index_exists(self.db_connection, index_prefix + 'centroid'):
            with self.db_connection.cursor() as db_cursor:
                db_cursor.execute(SQL("CREATE INDEX {} ON {} USING GIST (centroid) {}")
                                  .format(Identifier(index_prefix + 'centroid'),
                                          Identifier(base_table),
                                          SQL(sql_tablespace)))

        # Index on place_id
        if not index_exists(self.db_connection, index_prefix + 'place_id'):
            with self.db_connection.cursor() as db_cursor:
                db_cursor.execute(SQL("CREATE INDEX {} ON {} USING btree(place_id) {}")
                                  .format(Identifier(index_prefix + 'place_id'),
                                          Identifier(base_table),
                                          SQL(sql_tablespace)))

    def _grant_access_to_webuser(self, phrase_class: str, phrase_type: str) -> None:
        """
            Grant access on read to the table place_classtype for the webuser.
        """
        table_name = _classtype_table(phrase_class, phrase_type)
        with self.db_connection.cursor() as db_cursor:
            db_cursor.execute(SQL("""GRANT SELECT ON {} TO {}""")
                              .format(Identifier(table_name),
                                      Identifier(self.config.DATABASE_WEBUSER)))

    def _remove_non_existent_tables_from_db(self) -> None:
        """
            Remove special phrases which doesn't exist on the wiki anymore.
            Delete the place_classtype tables.
        """
        LOG.warning('Cleaning database...')

        # Delete place_classtype tables corresponding to class/type which
        # are not on the wiki anymore.
        drop_tables(self.db_connection, *self.table_phrases_to_delete)
        for _ in self.table_phrases_to_delete:
            self.statistics_handler.notify_one_table_deleted()


================================================
FILE: src/nominatim_db/tools/special_phrases/sp_wiki_loader.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
    Module containing the SPWikiLoader class.
"""
from typing import Iterable
import re
import logging

import mwparserfromhell

from ...config import Configuration
from ...utils.url_utils import get_url
from .special_phrase import SpecialPhrase

LOG = logging.getLogger()


def _get_wiki_content(lang: str) -> str:
    """
        Request and return the wiki page's content
        corresponding to special phrases for a given lang.
        Requested URL Example :
            https://wiki.openstreetmap.org/wiki/Special:Export/Nominatim/Special_Phrases/EN
    """
    url = 'https://wiki.openstreetmap.org/wiki/Special:Export/Nominatim/Special_Phrases/' \
          + lang.upper()
    return get_url(url)


class SPWikiLoader:
    """
        Handles loading of special phrases from the wiki.
    """
    def __init__(self, config: Configuration) -> None:
        self.config = config
        # Hack around a bug where building=yes was imported with quotes into the wiki
        self.type_fix_pattern = re.compile(r'\"|"')

        self.languages = self.config.get_str_list('LANGUAGES') or \
            ['af', 'ar', 'br', 'ca', 'cs', 'de', 'en', 'es',
             'et', 'eu', 'fa', 'fi', 'fr', 'gl', 'hr', 'hu',
             'ia', 'is', 'it', 'ja', 'mk', 'nl', 'no', 'pl',
             'ps', 'pt', 'ru', 'sk', 'sl', 'sv', 'uk', 'vi',
             'lv', 'tr']

    def generate_phrases(self) -> Iterable[SpecialPhrase]:
        """ Download the wiki pages for the configured languages
            and extract the phrases from the page.
        """
        for lang in self.languages:
            LOG.warning('Importing phrases for lang: %s...', lang)
            loaded_xml = _get_wiki_content(lang)

            wikicode = mwparserfromhell.parse(loaded_xml)

            for table in wikicode.filter_tags(matches=lambda t: t.tag == 'table'):
                for row in table.contents.filter_tags(matches=lambda t: t.tag == 'tr'):
                    cells = list(row.contents.filter_tags(matches=lambda t: t.tag == 'td'))

                    if len(cells) < 5:
                        continue

                    label = cells[0].contents.strip_code().strip()
                    cls = cells[1].contents.strip_code().strip()
                    typ = cells[2].contents.strip_code().strip()
                    operator = cells[3].contents.strip_code().strip()

                    yield SpecialPhrase(label,
                                        cls,
                                        self.type_fix_pattern.sub('', typ),
                                        operator)


================================================
FILE: src/nominatim_db/tools/special_phrases/special_phrase.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
    Module containing the class SpecialPhrase.

    This class is a model used to transfer a special phrase through
    the process of load and importation.
"""
from typing import Any


class SpecialPhrase:
    """
        Model representing a special phrase.
    """
    def __init__(self, p_label: str, p_class: str, p_type: str, p_operator: str) -> None:
        self.p_label = p_label.strip()
        self.p_class = p_class.strip()
        self.p_type = p_type.strip()
        # Needed if some operator in the wiki are not written in english
        p_operator = p_operator.strip().lower()
        self.p_operator = '-' if p_operator not in ('near', 'in') else p_operator

    def __eq__(self, other: Any) -> bool:
        if not isinstance(other, SpecialPhrase):
            return False

        return self.p_label == other.p_label \
            and self.p_class == other.p_class \
            and self.p_type == other.p_type \
            and self.p_operator == other.p_operator

    def __hash__(self) -> int:
        return hash((self.p_label, self.p_class, self.p_type, self.p_operator))


================================================
FILE: src/nominatim_db/tools/tiger_data.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for importing tiger data and handling tarbar and directory files
"""
from typing import Any, TextIO, List, Union, cast, Iterator, Dict
import csv
import io
import logging
import os
import tarfile

from psycopg.types.json import Json

from ..config import Configuration
from ..db.connection import connect, table_exists
from ..db.sql_preprocessor import SQLPreprocessor
from ..errors import UsageError
from ..db.query_pool import QueryPool
from ..data.place_info import PlaceInfo
from ..tokenizer.base import AbstractTokenizer

LOG = logging.getLogger()


class TigerInput:
    """ Context manager that goes through Tiger input files which may
        either be in a directory or gzipped together in a tar file.
    """

    def __init__(self, data_dir: str) -> None:
        self.tar_handle = None
        self.files: List[Union[str, tarfile.TarInfo]] = []

        if data_dir.endswith('.tar.gz'):
            try:
                self.tar_handle = tarfile.open(data_dir)
            except tarfile.ReadError as err:
                LOG.fatal("Cannot open '%s'. Is this a tar file?", data_dir)
                raise UsageError("Cannot open Tiger data file.") from err

            self.files = [i for i in self.tar_handle.getmembers() if i.name.endswith('.csv')]
            LOG.warning("Found %d CSV files in tarfile with path %s", len(self.files), data_dir)
        else:
            files = os.listdir(data_dir)
            self.files = [os.path.join(data_dir, i) for i in files if i.endswith('.csv')]
            LOG.warning("Found %d CSV files in path %s", len(self.files), data_dir)

        if not self.files:
            LOG.warning("Tiger data import selected but no files found at %s", data_dir)

    def __enter__(self) -> 'TigerInput':
        return self

    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
        if self.tar_handle:
            self.tar_handle.close()
            self.tar_handle = None

    def __bool__(self) -> bool:
        return bool(self.files)

    def get_file(self, fname: Union[str, tarfile.TarInfo]) -> TextIO:
        """ Return a file handle to the next file to be processed.
            Raises an IndexError if there is no file left.
        """
        if self.tar_handle is not None:
            extracted = self.tar_handle.extractfile(fname)
            assert extracted is not None
            return io.TextIOWrapper(extracted, encoding='utf-8')

        return open(cast(str, fname), encoding='utf-8')

    def __iter__(self) -> Iterator[Dict[str, Any]]:
        """ Iterate over the lines in each file.
        """
        for fname in self.files:
            fd = self.get_file(fname)
            yield from csv.DictReader(fd, delimiter=';')


async def add_tiger_data(data_dir: str, config: Configuration, threads: int,
                         tokenizer: AbstractTokenizer) -> int:
    """ Import tiger data from directory or tar file `data dir`.
    """
    dsn = config.get_libpq_dsn()

    with TigerInput(data_dir) as tar:
        if not tar:
            return 1

        with connect(dsn) as conn:
            sql = SQLPreprocessor(conn, config)

            if not table_exists(conn, 'search_name'):
                raise UsageError(
                    "Cannot perform tiger import: required tables are missing. "
                    "See https://github.com/osm-search/Nominatim/issues/2463 for details."
                )

            sql.run_sql_file(conn, 'tiger_import_start.sql')

        # Reading files and then for each file line handling
        # sql_query in  chunks.
        place_threads = max(1, threads - 1)

        async with QueryPool(dsn, place_threads, autocommit=True) as pool:
            with tokenizer.name_analyzer() as analyzer:
                for lineno, row in enumerate(tar, 1):
                    try:
                        address = dict(street=row['street'], postcode=row['postcode'])
                        args = ('SRID=4326;' + row['geometry'],
                                int(row['from']), int(row['to']), row['interpolation'],
                                Json(analyzer.process_place(PlaceInfo({'address': address}))),
                                analyzer.normalize_postcode(row['postcode']))
                    except ValueError:
                        continue

                    await pool.put_query(
                        """SELECT tiger_line_import(%s::GEOMETRY, %s::INT,
                                                    %s::INT, %s::TEXT, %s::JSONB, %s::TEXT)""",
                        args)

                    if not lineno % 1000:
                        print('.', end='', flush=True)

        print('', flush=True)

    LOG.warning("Creating indexes on Tiger data")
    with connect(dsn) as conn:
        sql = SQLPreprocessor(conn, config)
        sql.run_sql_file(conn, 'tiger_import_finish.sql')

    return 0


================================================
FILE: src/nominatim_db/typing.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Type definitions for typing annotations.

Complex type definitions are moved here, to keep the source files readable.
"""
from typing import Any, Union, Mapping, TypeVar, Sequence, TYPE_CHECKING


if TYPE_CHECKING:
    import os

StrPath = Union[str, 'os.PathLike[str]']

SysEnv = Mapping[str, str]

# psycopg-related types

T_ResultKey = TypeVar('T_ResultKey', int, str)


class DictCursorResult(Mapping[str, Any]):
    def __getitem__(self, x: Union[int, str]) -> Any: ...


DictCursorResults = Sequence[DictCursorResult]

# The following typing features require typing_extensions to work
# on all supported Python versions.
# Only require this for type checking but not for normal operations.

if TYPE_CHECKING:
    from typing_extensions import (Protocol as Protocol,
                                   Final as Final,
                                   TypedDict as TypedDict)
else:
    Protocol = object
    Final = 'Final'
    TypedDict = dict


================================================
FILE: src/nominatim_db/utils/__init__.py
================================================


================================================
FILE: src/nominatim_db/utils/centroid.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for computation of centroids.
"""
from typing import Tuple, Any
from collections.abc import Collection


class PointsCentroid:
    """ Centroid computation from single points using an online algorithm.
        More points may be added at any time.

        Coordinates are internally treated as a 7-digit fixed-point float
        (i.e. in OSM style).
    """

    def __init__(self) -> None:
        self.sum_x = 0
        self.sum_y = 0
        self.count = 0

    def centroid(self) -> Tuple[float, float]:
        """ Return the centroid of all points collected so far.
        """
        if self.count == 0:
            raise ValueError("No points available for centroid.")

        return (self.sum_x / self.count / 10_000_000,
                self.sum_y / self.count / 10_000_000)

    def __len__(self) -> int:
        return self.count

    def __iadd__(self, other: Any) -> 'PointsCentroid':
        if isinstance(other, Collection) and len(other) == 2:
            if all(isinstance(p, (float, int)) for p in other):
                x, y = other
                self.sum_x += int(x * 10_000_000)
                self.sum_y += int(y * 10_000_000)
                self.count += 1
                return self

        raise ValueError("Can only add 2-element tuples to centroid.")


================================================
FILE: src/nominatim_db/utils/url_utils.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Helper functions for accessing URL.
"""
from typing import IO  # noqa
import logging
import urllib.request as urlrequest

from ..version import NOMINATIM_VERSION

LOG = logging.getLogger()


def get_url(url: str) -> str:
    """ Get the contents from the given URL and return it as a UTF-8 string.

        This version makes sure that an appropriate user agent is sent.
    """
    headers = {"User-Agent": f"Nominatim/{NOMINATIM_VERSION!s}"}

    try:
        request = urlrequest.Request(url, headers=headers)
        with urlrequest.urlopen(request) as response:  # type: IO[bytes]
            return response.read().decode('utf-8')
    except Exception:
        LOG.fatal('Failed to load URL: %s', url)
        raise


================================================
FILE: src/nominatim_db/version.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2026 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Version information for Nominatim.
"""
from typing import NamedTuple, Optional


class NominatimVersion(NamedTuple):
    """ Version information for Nominatim. We follow semantic versioning.

        Major, minor and patch_level refer to the last released version.
        The database patch level tracks important changes between releases
        and must always be increased when there is a change to the database or code
        that requires a migration.

        When adding a migration on the development branch, raise the patch level
        to 99 to make sure that the migration is applied when updating from a
        patch release to the next minor version. Patch releases usually shouldn't
        have migrations in them. When they are needed, then make sure that the
        migration can be reapplied and set the migration version to the appropriate
        patch level when cherry-picking the commit with the migration.
    """

    major: int
    minor: int
    patch_level: int
    db_patch_level: int

    def __str__(self) -> str:
        if self.db_patch_level is None:
            return f"{self.major}.{self.minor}.{self.patch_level}"

        return f"{self.major}.{self.minor}.{self.patch_level}-{self.db_patch_level}"

    def release_version(self) -> str:
        """ Return the release version in semantic versioning format.

            The release version does not include the database patch version.
        """
        return f"{self.major}.{self.minor}.{self.patch_level}"


def parse_version(version: str) -> NominatimVersion:
    """ Parse a version string into a version consisting of a tuple of
        four ints: major, minor, patch level, database patch level

        This is the reverse operation of `version_str()`.
    """
    parts = version.split('.')
    return NominatimVersion(*[int(x) for x in parts[:2] + parts[2].split('-')])


NOMINATIM_VERSION = parse_version('5.3.99-0')

POSTGRESQL_REQUIRED_VERSION = (12, 0)
POSTGIS_REQUIRED_VERSION = (3, 0)
OSM2PGSQL_REQUIRED_VERSION = (1, 8)

# Cmake sets a variable @GIT_HASH@ by executing 'git --log'. It is not run
# on every execution of 'make'.
# cmake/tool-installed.tmpl is used to build the binary 'nominatim'. Inside
# there is a call to set the variable value below.
GIT_COMMIT_HASH: Optional[str] = None


================================================
FILE: test/bdd/conftest.py
================================================
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Fixtures for BDD test steps
"""
import sys
import json
import re
from pathlib import Path

import psycopg
from psycopg import sql as pysql

# always test against the source
SRC_DIR = (Path(__file__) / '..' / '..' / '..').resolve()
sys.path.insert(0, str(SRC_DIR / 'src'))

import pytest
from pytest_bdd.parsers import re as step_parse
from pytest_bdd import given, when, then, scenario
from pytest_bdd.feature import get_features

pytest.register_assert_rewrite('utils')

from utils.api_runner import APIRunner
from utils.api_result import APIResult
from utils.checks import ResultAttr, COMPARATOR_TERMS
from utils.geometry_alias import ALIASES
from utils.grid import Grid
from utils.db import DBManager

from nominatim_db.config import Configuration
from nominatim_db.data.country_info import setup_country_config


def _strlist(inp):
    return [s.strip() for s in inp.split(',')]


def _pretty_json(inp):
    return json.dumps(inp, indent=2)


def pytest_addoption(parser, pluginmanager):
    parser.addoption('--nominatim-purge', dest='NOMINATIM_PURGE', action='store_true',
                     help='Force recreation of test databases from scratch.')
    parser.addoption('--nominatim-keep-db', dest='NOMINATIM_KEEP_DB', action='store_true',
                     help='Do not drop the database after tests are finished.')
    parser.addoption('--nominatim-api-engine', dest='NOMINATIM_API_ENGINE',
                     default='falcon',
                     help='Chose the API engine to use when sending requests.')
    parser.addoption('--nominatim-tokenizer', dest='NOMINATIM_TOKENIZER',
                     metavar='TOKENIZER',
                     help='Use the specified tokenizer for importing data into '
                          'a Nominatim database.')

    parser.addini('nominatim_test_db', default='test_nominatim',
                  help='Name of the database used for running a single test.')
    parser.addini('nominatim_api_test_db', default='test_api_nominatim',
                  help='Name of the database for storing API test data.')
    parser.addini('nominatim_template_db', default='test_template_nominatim',
                  help='Name of database used as a template for test databases.')


@pytest.fixture
def datatable():
    """ Default fixture for datatables, so that their presence can be optional.
    """
    return None


@pytest.fixture
def node_grid():
    """ Default fixture for node grids. Nothing set.
    """
    return Grid([[]], None, None)


@pytest.fixture(scope='session', autouse=True)
def setup_country_info():
    setup_country_config(Configuration(None))


@pytest.fixture(scope='session')
def template_db(pytestconfig):
    """ Create a template database containing the extensions and base data
        needed by Nominatim. Using the template instead of doing the full
        setup can speed up the tests.

        The template database will only be created if it does not exist yet
        or a purge has been explicitly requested.
    """
    dbm = DBManager(purge=pytestconfig.option.NOMINATIM_PURGE)

    template_db = pytestconfig.getini('nominatim_template_db')

    template_config = Configuration(
        None, environ={'NOMINATIM_DATABASE_DSN': f"pgsql:dbname={template_db}"})

    dbm.setup_template_db(template_config)

    return template_db


@pytest.fixture
def def_config(pytestconfig):
    dbname = pytestconfig.getini('nominatim_test_db')

    return Configuration(None,
                         environ={'NOMINATIM_DATABASE_DSN': f"pgsql:dbname={dbname}"})


@pytest.fixture
def db(template_db, pytestconfig):
    """ Set up an empty database for use with osm2pgsql.
    """
    dbm = DBManager(purge=pytestconfig.option.NOMINATIM_PURGE)

    dbname = pytestconfig.getini('nominatim_test_db')

    dbm.create_db_from_template(dbname, template_db)

    yield dbname

    if not pytestconfig.option.NOMINATIM_KEEP_DB:
        dbm.drop_db(dbname)


@pytest.fixture
def db_conn(db, def_config):
    with psycopg.connect(def_config.get_libpq_dsn()) as conn:
        info = psycopg.types.TypeInfo.fetch(conn, "hstore")
        psycopg.types.hstore.register_hstore(info, conn)
        yield conn


@when(step_parse(r'reverse geocoding (?P[\d.-]*),(?P[\d.-]*)'),
      target_fixture='nominatim_result')
def reverse_geocode_via_api(test_config_env, pytestconfig, datatable, lat, lon):
    runner = APIRunner(test_config_env, pytestconfig.option.NOMINATIM_API_ENGINE)
    api_response = runner.run_step('reverse',
                                   {'lat': float(lat), 'lon': float(lon)},
                                   datatable, 'jsonv2', {})

    assert api_response.status == 200
    assert api_response.headers['content-type'] == 'application/json; charset=utf-8'

    result = APIResult('json', 'reverse', api_response.body)
    assert result.is_simple()

    assert isinstance(result.result['lat'], str)
    assert isinstance(result.result['lon'], str)
    result.result['centroid'] = f"POINT({result.result['lon']} {result.result['lat']})"

    return result


@when(step_parse(r'reverse geocoding at node (?P[\d]+)'),
      target_fixture='nominatim_result')
def reverse_geocode_via_api_and_grid(test_config_env, pytestconfig, node_grid, datatable, node):
    coords = node_grid.get(node)
    if coords is None:
        raise ValueError('Unknown node id')

    return reverse_geocode_via_api(test_config_env, pytestconfig, datatable, coords[1], coords[0])


@when(step_parse(r'geocoding(?: "(?P.*)")?'),
      target_fixture='nominatim_result')
def forward_geocode_via_api(test_config_env, pytestconfig, datatable, query):
    runner = APIRunner(test_config_env, pytestconfig.option.NOMINATIM_API_ENGINE)

    params = {'addressdetails': '1'}
    if query:
        params['q'] = query

    api_response = runner.run_step('search', params, datatable, 'jsonv2', {})

    assert api_response.status == 200
    assert api_response.headers['content-type'] == 'application/json; charset=utf-8'

    result = APIResult('json', 'search', api_response.body)
    assert not result.is_simple()

    for res in result.result:
        assert isinstance(res['lat'], str)
        assert isinstance(res['lon'], str)
        res['centroid'] = f"POINT({res['lon']} {res['lat']})"

    return result


@then(step_parse(r'(?P[a-z ]+) (?P\d+) results? (?:are|is) returned'),
      converters={'num': int})
def check_number_of_results(nominatim_result, op, num):
    assert not nominatim_result.is_simple()
    assert COMPARATOR_TERMS[op](num, len(nominatim_result))


@then(step_parse('the result metadata contains'))
def check_metadata_for_fields(nominatim_result, datatable):
    if datatable[0] == ['param', 'value']:
        pairs = datatable[1:]
    else:
        pairs = zip(datatable[0], datatable[1])

    for k, v in pairs:
        assert ResultAttr(nominatim_result.meta, k) == v


@then(step_parse('the result metadata has no attributes (?P.*)'),
      converters={'attributes': _strlist})
def check_metadata_for_field_presence(nominatim_result, attributes):
    assert all(a not in nominatim_result.meta for a in attributes), \
        f"Unexpectedly have one of the attributes '{attributes}' in\n" \
        f"{_pretty_json(nominatim_result.meta)}"


@then(step_parse(r'the result contains(?: in field (?P\S+))?'))
def check_result_for_fields(nominatim_result, datatable, node_grid, field):
    assert nominatim_result.is_simple()

    if datatable[0] == ['param', 'value']:
        pairs = datatable[1:]
    else:
        pairs = zip(datatable[0], datatable[1])

    prefix = field + '+' if field else ''

    for k, v in pairs:
        assert ResultAttr(nominatim_result.result, prefix + k, grid=node_grid) == v


@then(step_parse('the result has attributes (?P.*)'),
      converters={'attributes': _strlist})
def check_result_for_field_presence(nominatim_result, attributes):
    assert nominatim_result.is_simple()
    assert all(a in nominatim_result.result for a in attributes)


@then(step_parse('the result has no attributes (?P.*)'),
      converters={'attributes': _strlist})
def check_result_for_field_absence(nominatim_result, attributes):
    assert nominatim_result.is_simple()
    assert all(a not in nominatim_result.result for a in attributes)


@then(step_parse(
    r'the result contains array field (?P\S+) where element (?P\d+) contains'),
      converters={'num': int})
def check_result_array_field_for_attributes(nominatim_result, datatable, field, num):
    assert nominatim_result.is_simple()

    if datatable[0] == ['param', 'value']:
        pairs = datatable[1:]
    else:
        pairs = zip(datatable[0], datatable[1])

    prefix = f"{field}+{num}+"

    for k, v in pairs:
        assert ResultAttr(nominatim_result.result, prefix + k) == v


@then(step_parse('the result set contains(?P exactly)?'))
def check_result_list_match(nominatim_result, datatable, exact):
    assert not nominatim_result.is_simple()

    result_set = set(range(len(nominatim_result.result)))

    for row in datatable[1:]:
        for idx in result_set:
            for key, value in zip(datatable[0], row):
                if ResultAttr(nominatim_result.result[idx], key) != value:
                    break
            else:
                # found a match
                result_set.remove(idx)
                break
        else:
            assert False, f"Missing data row {row}. Full response:\n{nominatim_result}"

    if exact:
        assert not [nominatim_result.result[i] for i in result_set]


@then(step_parse('all results have attributes (?P.*)'),
      converters={'attributes': _strlist})
def check_all_results_for_field_presence(nominatim_result, attributes):
    assert not nominatim_result.is_simple()
    assert len(nominatim_result) > 0
    for res in nominatim_result.result:
        assert all(a in res for a in attributes), \
            f"Missing one of the attributes '{attributes}' in\n{_pretty_json(res)}"


@then(step_parse('all results have no attributes (?P.*)'),
      converters={'attributes': _strlist})
def check_all_result_for_field_absence(nominatim_result, attributes):
    assert not nominatim_result.is_simple()
    assert len(nominatim_result) > 0
    for res in nominatim_result.result:
        assert all(a not in res for a in attributes), \
            f"Unexpectedly have one of the attributes '{attributes}' in\n{_pretty_json(res)}"


@then(step_parse(r'all results contain(?: in field (?P\S+))?'))
def check_all_results_contain(nominatim_result, datatable, node_grid, field):
    assert not nominatim_result.is_simple()
    assert len(nominatim_result) > 0

    if datatable[0] == ['param', 'value']:
        pairs = datatable[1:]
    else:
        pairs = zip(datatable[0], datatable[1])

    prefix = field + '+' if field else ''

    for k, v in pairs:
        for r in nominatim_result.result:
            assert ResultAttr(r, prefix + k, grid=node_grid) == v


@then(step_parse(r'result (?P\d+) contains(?: in field (?P\S+))?'),
      converters={'num': int})
def check_specific_result_for_fields(nominatim_result, datatable, num, field):
    assert not nominatim_result.is_simple()
    assert len(nominatim_result) > num

    if datatable[0] == ['param', 'value']:
        pairs = datatable[1:]
    else:
        pairs = zip(datatable[0], datatable[1])

    prefix = field + '+' if field else ''

    for k, v in pairs:
        assert ResultAttr(nominatim_result.result[num], prefix + k) == v


@given(step_parse(r'the (?P[0-9.]+ )?grid(?: with origin (?P.*))?'),
       target_fixture='node_grid')
def set_node_grid(datatable, step, origin):
    if step is not None:
        step = float(step)

    if origin:
        if ',' in origin:
            coords = origin.split(',')
            if len(coords) != 2:
                raise RuntimeError('Grid origin expects origin with x,y coordinates.')
            origin = list(map(float, coords))
        elif origin in ALIASES:
            origin = ALIASES[origin]
        else:
            raise RuntimeError('Grid origin must be either coordinate or alias.')

    return Grid(datatable, step, origin)


@then(step_parse('(?Pplacex?) has no entry for '
                 r'(?P[NRW])(?P\d+)(?::(?P\S+))?'),
      converters={'osm_id': int})
def check_place_missing_lines(db_conn, table, osm_type, osm_id, osm_class):
    sql = pysql.SQL("""SELECT count(*) FROM {}
                       WHERE osm_type = %s and osm_id = %s""").format(pysql.Identifier(table))
    params = [osm_type, int(osm_id)]
    if osm_class:
        sql += pysql.SQL(' AND class = %s')
        params.append(osm_class)

    with db_conn.cursor() as cur:
        assert cur.execute(sql, params).fetchone()[0] == 0


if pytest.version_tuple >= (8, 0, 0):
    def pytest_pycollect_makemodule(module_path, parent):
        return BddTestCollector.from_parent(parent, path=module_path)


class BddTestCollector(pytest.Module):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def collect(self):
        for item in super().collect():
            yield item

        if hasattr(self.obj, 'PYTEST_BDD_SCENARIOS'):
            for path in self.obj.PYTEST_BDD_SCENARIOS:
                for feature in get_features([str(Path(self.path.parent, path).resolve())]):
                    yield FeatureFile.from_parent(self,
                                                  name=str(Path(path, feature.rel_filename)),
                                                  path=Path(feature.filename),
                                                  feature=feature)


# borrowed from pytest-bdd: src/pytest_bdd/scenario.py
def make_python_name(string: str) -> str:
    """Make python attribute name out of a given string."""
    string = re.sub(r"\W", "", string.replace(" ", "_"))
    return re.sub(r"^\d+_*", "", string).lower()


class FeatureFile(pytest.File):
    class obj:
        pass

    def __init__(self, feature, **kwargs):
        self.feature = feature
        super().__init__(**kwargs)

    def collect(self):
        for sname, sobject in self.feature.scenarios.items():
            class_name = f"L{sobject.line_number}"
            test_name = "test_" + make_python_name(sname)

            @scenario(self.feature.filename, sname)
            def _test():
                pass

            tclass = type(class_name, (),
                          {test_name: staticmethod(_test)})
            setattr(self.obj, class_name, tclass)

            yield pytest.Class.from_parent(self, name=class_name, obj=tclass)


================================================
FILE: test/bdd/features/api/details/language.feature
================================================
Feature: Localization of search results

    Scenario: default language
        When sending v1/details
          | osmtype | osmid   |
          | R       | 1155955 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | localname |
          | Liechtenstein |

    Scenario: accept-language first
        When sending v1/details
          | osmtype | osmid   | accept-language |
          | R       | 1155955 | zh,de |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | localname |
          | 列支敦士登 |

    Scenario: accept-language missing
        When sending v1/details
          | osmtype | osmid   | accept-language |
          | R       | 1155955 | xx,fr,en,de |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | localname |
          | Liechtenstein |

    Scenario: http accept language header first
        Given the HTTP header
          | accept-language |
          | fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
        When sending v1/details
          | osmtype | osmid   |
          | R       | 1155955 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | localname |
          | Liktinstein |

    Scenario: http accept language header and accept-language
        Given the HTTP header
          | accept-language |
          | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3 |
        When sending v1/details
          | osmtype | osmid   | accept-language |
          | R       | 1155955 | fo,en |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | localname |
          | Liktinstein |

    Scenario: http accept language header fallback
        Given the HTTP header
          | accept-language |
          | fo-ca,en-ca;q=0.5 |
        When sending v1/details
          | osmtype | osmid   |
          | R       | 1155955 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | localname |
          | Liktinstein |

    Scenario: http accept language header fallback (upper case)
        Given the HTTP header
          | accept-language |
          | fo-FR;q=0.8,en-ca;q=0.5 |
        When sending v1/details
          | osmtype | osmid   |
          | R       | 1155955 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | localname |
          | Liktinstein |


================================================
FILE: test/bdd/features/api/details/params.feature
================================================
Feature: Object details
    Testing different parameter options for details API.

    Scenario: Basic details
        When sending v1/details
          | osmtype | osmid |
          | W       | 297699560 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes geometry
        And the result has no attributes keywords,address,linked_places,parentof
        And the result contains
            | geometry+type  |
            | Point |

    Scenario: Basic details with pretty printing
        When sending v1/details
          | osmtype | osmid     | pretty |
          | W       | 297699560 | 1      |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes geometry
        And the result has no attributes keywords,address,linked_places,parentof

    Scenario: Details with addressdetails
        When sending v1/details
          | osmtype | osmid     | addressdetails |
          | W       | 297699560 | 1              |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes address

    Scenario: Details with entrances
        When sending v1/details
          | osmtype | osmid     | entrances |
          | W       | 429210603 | 1         |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains array field entrances where element 0 contains
          | osm_id     | type | lat        | lon       |
          | 6580031131 | yes  | 47.2489382 | 9.5284033 |

    Scenario: Details with linkedplaces
        When sending v1/details
          | osmtype | osmid  | linkedplaces |
          | R       | 123924 | 1            |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes linked_places

    Scenario: Details with hierarchy
        When sending v1/details
          | osmtype | osmid     | hierarchy |
          | W       | 297699560 | 1         |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes hierarchy

    Scenario: Details with grouped hierarchy
        When sending v1/details
          | osmtype | osmid     | hierarchy | group_hierarchy |
          | W       | 297699560 | 1         | 1               |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes hierarchy

    Scenario Outline: Details with keywords
        When sending v1/details
            | osmtype | osmid | keywords |
            |   |   | 1 |
        Then a HTTP 200 is returned
        Then the result is valid json
        And the result has attributes keywords

    Examples:
      | type | id |
      | W    | 297699560 |
      | W    | 243055645 |
      | W    | 243055716 |
      | W    | 43327921  |

    # ticket #1343
    Scenario: Details of a country with keywords
        When sending v1/details
            | osmtype | osmid   | keywords |
            | R       | 1155955 | 1 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes keywords

    Scenario Outline: Details with full geometry
        When sending v1/details
            | osmtype | osmid | polygon_geojson |
            |   |   | 1 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes geometry
        And the result contains
            | geometry+type |
            |  |

    Examples:
            | type | id        | geometry   |
            | W    | 297699560 | LineString |
            | W    | 243055645 | Polygon    |
            | W    | 243055716 | Polygon    |
            | W    | 43327921  | LineString |




================================================
FILE: test/bdd/features/api/details/simple.feature
================================================
Feature: Object details
    Check details page for correctness

    Scenario Outline: Details request with OSM id
        When sending v1/details
          | osmtype | osmid |
          |   |   |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
            | osm_type | osm_id |
            |    |  |

    Examples:
     | type | id |
     | N    | 5484325405 |
     | W    | 43327921 |
     | R    | 123924 |

    Scenario Outline: Details request with different class types for the same OSM id
        When sending v1/details
          | osmtype | osmid     | class   |
          | N       | 300209696 |  |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | osm_type | osm_id    | category |
          | N        | 300209696 |   |

    Examples:
     | class |
     | tourism |
     | mountain_pass |

    Scenario: Details request without osmtype
        When sending v1/details
          | osmid |
          |   |
        Then a HTTP 400 is returned
        And the result is valid json

    Scenario: Details request with unknown OSM id
        When sending v1/details
          | osmtype | osmid |
          | R       | 1     |
        Then a HTTP 404 is returned
        And the result is valid json

    Scenario: Details request with unknown class
        When sending v1/details
          | osmtype | osmid     | class   |
          | N       | 300209696 | highway |
        Then a HTTP 404 is returned
        And the result is valid json

    Scenario: Details for interpolation way return the interpolation
        When sending v1/details
          | osmtype | osmid |
          | W       | 1     |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | category | type   | osm_type | osm_id | admin_level |
          | place    | houses | W        | 1      | 15          |


    @skip
    Scenario: Details for interpolation way return the interpolation
        When sending details query for 112871
        Then the result is valid json
        And the result contains
            | category | type   | admin_level |
            | place    | houses | 15          |
        And result has not attributes osm_type,osm_id


    @skip
    Scenario: Details for postcode
        When sending details query for 112820
        Then the result is valid json
        And the result contains
            | category | type     | admin_level |
            | place    | postcode | 15          |
        And result has not attributes osm_type,osm_id


    Scenario Outline: Details debug output returns no errors
        When sending v1/details
          | osmtype | osmid | debug |
          |   |   | 1     |
        Then a HTTP 200 is returned
        And the result is valid html

    Examples:
     | type | id |
     | N    | 5484325405 |
     | W    | 43327921 |
     | R    | 123924 |



================================================
FILE: test/bdd/features/api/lookup/simple.feature
================================================
Feature: Tests for finding places by osm_type and osm_id
    Simple tests for response format.

    Scenario Outline: Address lookup for existing object
        When sending v1/lookup with format 
          | osm_ids |
          | N5484325405,W43327921,,R123924,X99,N0 |
        Then a HTTP 200 is returned
        And the result is valid 
        And exactly 3 results are returned

    Examples:
        | format      | outformat   |
        | xml         | xml         |
        | json        | json        |
        | jsonv2      | json        |
        | geojson     | geojson     |
        | geocodejson | geocodejson |

    Scenario: Address lookup for non-existing or invalid object
        When sending v1/lookup
          | osm_ids |
          | X99,,N0,nN158845944,ABC,,W9 |
        Then a HTTP 200 is returned
        And the result is valid xml
        And exactly 0 results are returned

    Scenario Outline: Boundingbox is returned
        When sending v1/lookup with format 
          | osm_ids |
          | N5484325405,W43327921 |
        Then the result is valid 
        And the result set contains exactly
          | object      | boundingbox!in_box |
          | N5484325405 | 47.135,47.14,9.52,9.525 |
          | W43327921   | 47.07,47.08,9.50,9.52   |

    Examples:
        | format      | outformat   |
        | xml         | xml         |
        | json        | json        |
        | jsonv2      | json        |
        | geojson     | geojson     |

    Scenario Outline: Lookup with entrances
        When sending v1/lookup with format 
          | osm_ids    | entrances |
          | W429210603 | 1         |
        Then a HTTP 200 is returned
        And the result is valid 
        And result 0 contains in field entrances+0
          | osm_id     | type | lat        | lon       |
          | 6580031131 | yes  | 47.2489382 | 9.5284033 |

        Examples:
          | format | outformat |
          | json   | json |
          | jsonv2 | json |
          | geojson | geojson |

    Scenario: Linked places return information from the linkee
        When sending v1/lookup with format geocodejson
          | osm_ids |
          | N1932181216 |
        Then the result is valid geocodejson
        And exactly 1 result is returned
        And all results contain
          | name  |
          | Vaduz |

    Scenario Outline: Force error by providing too many ids
        When sending v1/lookup with format 
          | osm_ids |
          | N1,N2,N3,N4,N5,N6,N7,N8,N9,N10,N11,N12,N13,N14,N15,N16,N17,N18,N19,N20,N21,N22,N23,N24,N25,N26,N27,N28,N29,N30,N31,N32,N33,N34,N35,N36,N37,N38,N39,N40,N41,N42,N43,N44,N45,N46,N47,N48,N49,N50,N51 |
        Then a HTTP 400 is returned
        And the result is valid 
        And the result contains
          | error+code | error+message |
          | 400        | Too many object IDs. |

    Examples:
        | format      | outformat   |
        | xml         | xml         |
        | json        | json        |
        | jsonv2      | json        |
        | geojson     | json        |
        | geocodejson | json        |


================================================
FILE: test/bdd/features/api/reverse/geometry.feature
================================================
Feature: Geometries for reverse geocoding
    Tests for returning geometries with reverse

    Scenario: Reverse - polygons are returned fully by default
        When sending v1/reverse
          | lat      | lon     | polygon_text |
          | 47.13803 | 9.52264 | 1            |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | geotext!fm |
          | POLYGON\(\(9.5225302 47.138066, ?9.5225348 47.1379282, ?9.5226142 47.1379294, ?9.5226143 47.1379257, ?9.522615 47.137917, ?9.5226225 47.1379098, ?9.5226334 47.1379052, ?9.5226461 47.1379037, ?9.5226588 47.1379056, ?9.5226693 47.1379107, ?9.5226762 47.1379181, ?9.5226762 47.1379268, ?9.5226761 47.1379308, ?9.5227366 47.1379317, ?9.5227352 47.1379753, ?9.5227608 47.1379757, ?9.5227595 47.1380148, ?9.5227355 47.1380145, ?9.5227337 47.1380692, ?9.5225302 47.138066\)\) |


    Scenario: Reverse - polygons can be slightly simplified
        When sending v1/reverse
          | lat      | lon     | polygon_text | polygon_threshold |
          | 47.13803 | 9.52264 | 1            | 0.00001            |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | geotext!fm |
          | POLYGON\(\(9.5225302 47.138066, ?9.5225348 47.1379282, ?9.5226142 47.1379294, ?9.5226225 47.1379098, ?9.5226588 47.1379056, ?9.5226761 47.1379308, ?9.5227366 47.1379317, ?9.5227352 47.1379753, ?9.5227608 47.1379757, ?9.5227595 47.1380148, ?9.5227355 47.1380145, ?9.5227337 47.1380692, ?9.5225302 47.138066\)\) |


    Scenario: Reverse - polygons can be much simplified
        When sending v1/reverse
          | lat      | lon     | polygon_text | polygon_threshold |
          | 47.13803 | 9.52264 | 1            | 0.9               |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | geotext!fm |
          | POLYGON\(\([0-9. ]+, ?[0-9. ]+, ?[0-9. ]+, ?[0-9. ]+(, ?[0-9. ]+)?\)\) |


    Scenario: Reverse - for polygons return the centroid as center point
        When sending v1/reverse
          | lat      | lon     |
          | 47.13836 | 9.52304 |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | lon       | lat        |
          | 9.5227108 | 47.1381805 |


    Scenario: Reverse - for streets return the closest point as center point
        When sending v1/reverse
          | lat      | lon     |
          | 47.13368 | 9.52942 |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | lon       | lat        |
          | 9.5294315 | 47.1336817 |


================================================
FILE: test/bdd/features/api/reverse/language.feature
================================================
Feature: Localization of reverse search results

    Scenario: Reverse - default language
        When sending v1/reverse with format jsonv2
          | lat   | lon  |
          | 47.14 | 9.55 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | address+country |
          | Liechtenstein |

    Scenario: Reverse - accept-language parameter
        When sending v1/reverse with format jsonv2
          | lat   | lon  | accept-language |
          | 47.14 | 9.55 | ja,en |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | address+country |
          | リヒテンシュタイン |

    Scenario: Reverse - HTTP accept language header
        Given the HTTP header
          | accept-language |
          | fo-ca,fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
        When sending v1/reverse with format jsonv2
          | lat   | lon  |
          | 47.14 | 9.55 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | address+country |
          | Liktinstein |

    Scenario: Reverse - accept-language parameter and HTTP header
        Given the HTTP header
          | accept-language |
          | fo-ca,fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
        When sending v1/reverse with format jsonv2
          | lat   | lon  | accept-language |
          | 47.14 | 9.55 | en |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | address+country |
          | Liechtenstein |


================================================
FILE: test/bdd/features/api/reverse/layers.feature
================================================
Feature: Layer parameter in reverse geocoding
    Testing correct function of layer selection while reverse geocoding

    Scenario: POIs are selected by default
        When reverse geocoding 47.14077,9.52414
        Then the result contains
          | category | type      |
          | tourism  | viewpoint |

    Scenario Outline: Same address level POI with different layers
        When reverse geocoding 47.14077,9.52414
          | layer   |
          |  |
        Then the result contains
          | category   |
          |  |

        Examples:
          | layer           | category |
          | address         | highway  |
          | poi,address     | tourism  |
          | address,poi     | tourism  |
          | natural         | waterway |
          | address,natural | highway  |
          | natural,poi     | tourism  |

     Scenario Outline: POIs are not selected without housenumber for address layer
        When reverse geocoding 47.13816,9.52168
          | layer   |
          |  |
        Then the result contains
          | category   | type   |
          |  |  |

        Examples:
          | layer       | category | type     |
          | address,poi | highway  | bus_stop |
          | address     | amenity  | parking  |

     Scenario: Between natural and low-zoom address prefer natural
         When reverse geocoding 47.13636,9.52094
           | layer           | zoom |
           | natural,address | 15   |
         Then the result contains
           | category |
           | waterway |

    Scenario Outline: Search for mountain peaks begins at level 12
        When reverse geocoding 47.08293,9.57109
          | layer   | zoom   |
          | natural |  |
        Then the result contains
          | category   | type   |
          |  |  |

        Examples:
          | zoom | category | type  |
          | 12   | natural  | peak  |
          | 13   | waterway | river |

     Scenario Outline: Reverse search with manmade layers
        When reverse geocoding 32.46904,-86.44439
          | layer   |
          |  |
        Then the result contains
          | category   | type   |
          |  |  |

        Examples:
          | layer           | category | type        |
          | manmade         | leisure  | park        |
          | address         | highway  | residential |
          | poi             | leisure  | pitch       |
          | natural         | waterway | river       |
          | natural,manmade | leisure  | park        |


================================================
FILE: test/bdd/features/api/reverse/queries.feature
================================================
Feature: Reverse geocoding
    Testing the reverse function

    Scenario: Reverse - Unknown countries fall back to default country grid
        When reverse geocoding 45.174,-103.072
        Then the result contains
          | category | type    | display_name |
          | place    | country | United States |

    Scenario: Reverse - No TIGER house number for zoom < 18
        When reverse geocoding 32.4752389363,-86.4810198619
          | zoom |
          | 17 |
        Then the result contains
          | osm_type | category |
          | way      | highway  |
        And the result contains in field address
          | road                | postcode | country_code |
          | Upper Kingston Road | 36067    | us |

    Scenario: Reverse - Address with non-numerical house number
        When reverse geocoding 47.107465,9.52838521614
        Then the result contains in field address
          | house_number | road |
          | 39A/B        | Dorfstrasse |

    Scenario: Reverse - Address with numerical house number
        When reverse geocoding 47.168440329479594,9.511551699184338
        Then the result contains in field address
          | house_number | road |
          | 6            | Schmedgässle |

    Scenario Outline: Reverse - Zoom levels below 5 result in country
        When reverse geocoding 47.16,9.51
         | zoom |
         |  |
        Then the result contains
         | display_name |
         | Liechtenstein |

        Examples:
             | zoom |
             | 0    |
             | 1    |
             | 2    |
             | 3    |
             | 4    |

    Scenario: Reverse - When on a street, the closest interpolation is shown
        When reverse geocoding 47.118457166193245,9.570678289621355
         | zoom |
         | 18 |
        Then the result contains
         | display_name |
         | 1021, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |

    # github 2214
    Scenario: Reverse - Interpolations do not override house numbers when they are closer
        When reverse geocoding 47.11778,9.57255
         | zoom |
         | 18 |
        Then the result contains
         | display_name |
         | 5, Grosssteg, Steg, Triesenberg, Oberland, 9497, Liechtenstein |

    Scenario: Reverse - Interpolations do not override house numbers when they are closer (2)
        When reverse geocoding 47.11834,9.57167
         | zoom |
         | 18 |
        Then the result contains
         | display_name |
         | 3, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |

    Scenario: Reverse - When on a street with zoom 18, the closest housenumber is returned
        When reverse geocoding 47.11755503977281,9.572722250405036
         | zoom |
         | 18 |
        Then the result contains in field address
         | house_number |
         | 7 |

    Scenario: Reverse - inherited address is shown by default
        When reverse geocoding 47.0629071,9.4879694
        Then the result contains
         | osm_type | category | display_name |
         | node     | office   | foo.li, 64, Hampfländer, Mäls, Balzers, Oberland, 9496, Liechtenstein |

    Scenario: Reverse - inherited address is not shown with address layer
        When reverse geocoding 47.0629071,9.4879694
         | layer |
         | address |
        Then the result contains
         | osm_type | category | display_name |
         | way      | building | 64, Hampfländer, Mäls, Balzers, Oberland, 9496, Liechtenstein |


================================================
FILE: test/bdd/features/api/reverse/v1_geocodejson.feature
================================================
Feature: Geocodejson for Reverse API
    Testing correctness of geocodejson output (API version v1).

    Scenario Outline: Reverse geocodejson - Simple with no results
        When sending v1/reverse with format geocodejson
          | lat   | lon   |
          |  |  |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | error |
          | Unable to geocode |

        Examples:
          | lat  | lon |
          | 0.0  | 0.0 |
          | 91.3 | 0.4    |
          | -700 | 0.4    |
          | 0.2  | 324.44 |
          | 0.2  | -180.4 |

    Scenario Outline: Reverse geocodejson - Simple OSM result
        When sending v1/reverse with format geocodejson
          | lat    | lon   | addressdetails |
          | 47.066 | 9.504 |   |
        Then a HTTP 200 is returned
        And the result is valid geocodejson with 1 result
        And the result metadata contains
          | version | licence | attribution!fm |
          | 0.1.0   | ODbL    | Data © OpenStreetMap contributors, ODbL 1.0. https?://osm.org/copyright |
        And all results have  country,postcode,county,city,district,street,housenumber,admin
        And all results contain
          | param               | value |
          | osm_type            | node |
          | osm_id              | 6522627624 |
          | osm_key             | shop |
          | osm_value           | bakery |
          | type                | house |
          | name                | Dorfbäckerei Herrmann |
          | label               | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
          | geojson+type        | Point |
          | geojson+coordinates | [9.5036065, 47.0660892] |

        Examples:
          | has_address | attributes     |
          | 1           | attributes     |
          | 0           | no attributes |

    Scenario: Reverse geocodejson - City housenumber-level address with street
        When sending v1/reverse with format geocodejson
          | lat        | lon        |
          | 47.1068011 | 9.52810091 |
        Then a HTTP 200 is returned
        And the result is valid geocodejson with 1 result
        And all results contain
          | housenumber | street    | postcode | city    | country |
          | 8           | Im Winkel | 9495     | Triesen | Liechtenstein |
         And all results contain
          | admin+level6 | admin+level8 |
          | Oberland     | Triesen      |

    Scenario: Reverse geocodejson - Town street-level address with street
        When sending v1/reverse with format geocodejson
          | lat    | lon   | zoom |
          | 47.066 | 9.504 | 16 |
        Then a HTTP 200 is returned
        And the result is valid geocodejson with 1 result
        And all results contain
          | name    | city    | postcode | country |
          | Gnetsch | Balzers | 9496     | Liechtenstein |

    Scenario: Reverse geocodejson - Poi street-level address with footway
        When sending v1/reverse with format geocodejson
          | lat      | lon     |
          | 47.06515 | 9.50083 |
        Then a HTTP 200 is returned
        And the result is valid geocodejson with 1 result
        And all results contain
          | street  | city    | postcode | country |
          | Burgweg | Balzers | 9496     | Liechtenstein |

    Scenario: Reverse geocodejson - City address with suburb
        When sending v1/reverse with format geocodejson
          | lat       | lon      |
          | 47.146861 | 9.511771 |
        Then a HTTP 200 is returned
        And the result is valid geocodejson with 1 result
        And all results contain
          | housenumber | street   | district | city  | postcode | country |
          | 5           | Lochgass | Ebenholz | Vaduz | 9490     | Liechtenstein |

    Scenario: Reverse geocodejson - Tiger address
        When sending v1/reverse with format geocodejson
          | lat           | lon            |
          | 32.4752389363 | -86.4810198619 |
        Then a HTTP 200 is returned
        And the result is valid geocodejson with 1 result
        And all results contain
         | osm_type | osm_id    | osm_key | osm_value | type  |
         | way      | 396009653 | place   | house     | house |
        And all results contain
         | housenumber | street              | city       | county         | postcode | country       |
         | 707         | Upper Kingston Road | Prattville | Autauga County | 36067    | United States |

    Scenario: Reverse geocodejson - Interpolation address
        When sending v1/reverse with format geocodejson
          | lat       | lon        |
          | 47.118533 | 9.57056562 |
        Then a HTTP 200 is returned
        And the result is valid geocodejson with 1 result
        And all results contain
          | osm_type | osm_id | osm_key | osm_value | type  |
          | way      | 1      | place   | house     | house |
        And all results contain
          | label |
          | 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
        And all results have no attributes name

    Scenario: Reverse geocodejson - Line geometry output is supported
        When sending v1/reverse with format geocodejson
          | lat      | lon     | polygon_geojson |
          | 47.06597 | 9.50467 | 1  |
        Then a HTTP 200 is returned
        And the result is valid geocodejson with 1 result
        And all results contain
          | geojson+type |
          | LineString   |

    Scenario Outline: Reverse geocodejson - Only geojson polygons are supported
        When sending v1/reverse with format geocodejson
          | lat      | lon     |  |
          | 47.06597 | 9.50467 | 1       |
        Then a HTTP 200 is returned
        And the result is valid geocodejson with 1 result
        And all results contain
          | geojson+type |
          | Point        |

        Examples:
          | param |
          | polygon_text |
          | polygon_svg  |
          | polygon_kml  |


================================================
FILE: test/bdd/features/api/reverse/v1_geojson.feature
================================================
Feature: Geojson for Reverse API
    Testing correctness of geojson output (API version v1).

    Scenario Outline: Reverse geojson - Simple with no results
        When sending v1/reverse with format geojson
          | lat   | lon   |
          |  |  |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | error |
          | Unable to geocode |

        Examples:
          | lat  | lon |
          | 0.0  | 0.0 |
          | 91.3 | 0.4    |
          | -700 | 0.4    |
          | 0.2  | 324.44 |
          | 0.2  | -180.4 |

    Scenario Outline: Reverse geojson - Simple OSM result
        When sending v1/reverse with format geojson
          | lat    | lon   | addressdetails |
          | 47.066 | 9.504 |   |
        Then a HTTP 200 is returned
        And the result is valid geojson with 1 result
        And the result metadata contains
          | licence!fm |
          | Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright |
        And all results have attributes place_id, importance
        And all results have  address
        And all results contain
          | param               | value |
          | osm_type            | node |
          | osm_id              | 6522627624 |
          | place_rank          | 30 |
          | category            | shop |
          | type                | bakery |
          | addresstype         | shop |
          | name                | Dorfbäckerei Herrmann |
          | display_name        | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
          | boundingbox         | [47.0660392, 47.0661392, 9.5035565, 9.5036565] |
          | geojson+type        | Point |
          | geojson+coordinates | [9.5036065, 47.0660892] |

        Examples:
          | has_address | attributes    |
          | 1           | attributes    |
          | 0           | no attributes |

    Scenario: Reverse geojson - Tiger address
        When sending v1/reverse with format geojson
          | lat           | lon            |
          | 32.4752389363 | -86.4810198619 |
        Then a HTTP 200 is returned
        And the result is valid geojson with 1 result
        And all results contain
          | osm_type | osm_id    | category | type  | addresstype  | place_rank |
          | way      | 396009653 | place    | house | place        | 30         |

    Scenario: Reverse geojson - Interpolation address
        When sending v1/reverse with format geojson
          | lat       | lon        |
          | 47.118533 | 9.57056562 |
        Then a HTTP 200 is returned
        And the result is valid geojson with 1 result
        And all results contain
          | osm_type | osm_id | place_rank | category | type    | addresstype |
          | way      | 1      | 30         | place    | house   | place       |
        And all results contain
          | boundingbox!in_box |
          | 47.118494, 47.118596, 9.570495, 9.570597 |
        And all results contain
          | display_name |
          | 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |

    Scenario: Reverse geojson - Line geometry output is supported
        When sending v1/reverse with format geojson
          | lat      | lon     | polygon_geojson |
          | 47.06597 | 9.50467 | 1               |
        Then a HTTP 200 is returned
        And the result is valid geojson with 1 result
        And all results contain
          | geojson+type |
          | LineString   |

    Scenario Outline: Reverse geojson - Only geojson polygons are supported
        When sending v1/reverse with format geojson
          | lat      | lon     |  |
          | 47.06597 | 9.50467 | 1       |
        Then a HTTP 200 is returned
        And the result is valid geojson with 1 result
        And all results contain
          | geojson+type |
          | Point |

        Examples:
          | param |
          | polygon_text |
          | polygon_svg  |
          | polygon_kml  |


================================================
FILE: test/bdd/features/api/reverse/v1_json.feature
================================================
Feature: Json output for Reverse API
    Testing correctness of json and jsonv2 output (API version v1).

    Scenario Outline: Reverse json - Simple with no results
        When sending v1/reverse with format json
          | lat   | lon   |
          |  |  |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | error |
          | Unable to geocode |
        When sending v1/reverse with format jsonv2
          | lat   | lon   |
          |  |  |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | error |
          | Unable to geocode |

        Examples:
          | lat  | lon |
          | 0.0  | 0.0 |
          | 91.3 | 0.4    |
          | -700 | 0.4    |
          | 0.2  | 324.44 |
          | 0.2  | -180.4 |

    Scenario Outline: Reverse json - OSM result with and without addresses
        When sending v1/reverse with format json
          | lat    | lon   | addressdetails |
          | 47.066 | 9.504 |   |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has  address
        When sending v1/reverse with format jsonv2
          | lat    | lon   | addressdetails |
          | 47.066 | 9.504 |   |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has  address

        Examples:
          | has_address | attributes    |
          | 1           | attributes    |
          | 0           | no attributes |

    Scenario Outline: Reverse json - Simple OSM result
        When sending v1/reverse with format 
          | lat    | lon   |
          | 47.066 | 9.504 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes place_id
        And the result contains
          | licence!fm |
          | Data © OpenStreetMap contributors, ODbL 1.0. https?://osm.org/copyright |
        And the result contains
          | osm_type | osm_id     |
          | node     | 6522627624 |
        And the result contains
          | lon       | lat        | boundingbox!in_box |
          | 9.5036065 | 47.0660892 | 47.0660391, 47.0661393, 9.5035564, 9.5036566 |
        And the result contains
          | display_name |
          | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
        And the result has no attributes namedetails,extratags

        Examples:
          | format |
          | json   |
          | jsonv2 |

    Scenario: Reverse json - Extra attributes of jsonv2 result
        When sending v1/reverse with format jsonv2
          | lat    | lon   |
          | 47.066 | 9.504 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result has attributes importance
        And the result contains
          | category | type   | name                  | place_rank | addresstype |
          | shop     | bakery | Dorfbäckerei Herrmann | 30         | shop        |

    Scenario: Reverse json - Tiger address
        When sending v1/reverse with format jsonv2
          | lat           | lon            |
          | 32.4752389363 | -86.4810198619 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | osm_type | osm_id    | category | type  | addresstype  |
          | way      | 396009653 | place    | house | place        |

    Scenario Outline: Reverse json - Interpolation address
        When sending v1/reverse with format 
          | lat       | lon        |
          | 47.118533 | 9.57056562 |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | osm_type | osm_id |
          | way      | 1      |
        And the result contains
          | lon       | lat        | boundingbox!in_box |
          | 9.5705467 | 47.1185454 | 47.118494, 47.118596, 9.570495, 9.570597 |
        And the result contains
          | display_name |
          | 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |

        Examples:
          | format |
          | json   |
          | jsonv2 |

    Scenario Outline: Reverse json - Output of geojson
        When sending v1/reverse with format 
          | lat      | lon     | polygon_geojson |
          | 47.06597 | 9.50467 | 1               |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | geojson+type | geojson+coordinates |
          | LineString   | [[9.5039353, 47.0657546], [9.5040437, 47.0657781], [9.5040808, 47.065787], [9.5054298, 47.0661407]] |

       Examples:
          | format |
          | json   |
          | jsonv2 |

    Scenario Outline: Reverse json - Output of WKT
        When sending v1/reverse with format 
          | lat      | lon     | polygon_text |
          | 47.06597 | 9.50467 | 1            |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | geotext!wkt |
          | 9.5039353 47.0657546, 9.5040437 47.0657781, 9.5040808 47.065787, 9.5054298 47.0661407 |

       Examples:
          | format |
          | json   |
          | jsonv2 |

    Scenario Outline: Reverse json - Output of SVG
       When sending v1/reverse with format 
          | lat      | lon     | polygon_svg |
          | 47.06597 | 9.50467 | 1           |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | svg |
          | M 9.5039353 -47.0657546 L 9.5040437 -47.0657781 9.5040808 -47.065787 9.5054298 -47.0661407 |

       Examples:
          | format |
          | json   |
          | jsonv2 |

    Scenario Outline: Reverse json - Output of KML
        When sending v1/reverse with format 
          | lat      | lon     | polygon_kml |
          | 47.06597 | 9.50467 | 1           |
        Then a HTTP 200 is returned
        And the result is valid json
        And the result contains
          | geokml!fm |
          | 9.5039\d*,47.0657\d* 9.5040\d*,47.0657\d* 9.5040\d*,47.065\d* 9.5054\d*,47.0661\d* |

       Examples:
          | format |
          | json   |
          | jsonv2 |


================================================
FILE: test/bdd/features/api/reverse/v1_params.feature
================================================
Feature: v1/reverse Parameter Tests
    Tests for parameter inputs for the v1 reverse endpoint.
    This file contains mostly bad parameter input. Valid parameters
    are tested in the format tests.

    Scenario: Bad format
        When sending v1/reverse
          | lat         | lon           | format |
          | 47.14122383 | 9.52169581334 | sdf |
        Then a HTTP 400 is returned

    Scenario: Missing lon parameter
        When sending v1/reverse
          | lat   |
          | 52.52 |
        Then a HTTP 400 is returned

    Scenario: Missing lat parameter
        When sending v1/reverse
          | lon |
          | 52.52 |
        Then a HTTP 400 is returned

    Scenario Outline: Bad format for lat or lon
        When sending v1/reverse
          | lat   | lon   |
          |  |  |
        Then a HTTP 400 is returned

        Examples:
          | lat      | lon |
          | 48.9660  | 8,4482 |
          | 48,9660  | 8.4482 |
          | 48,9660  | 8,4482 |
          | 48.966.0 | 8.4482 |
          | 48.966   | 8.448.2 |
          | Nan      | 8.448  |
          | 48.966   | Nan    |
          | Inf      | 5.6    |
          | 5.6      | -Inf   |
          |  | 3.4 |
          | 3.4 |  |
          | -45.3    | ;      |
          | gkjd     | 50     |

    Scenario: Non-numerical zoom levels return an error
        When sending v1/reverse
          | lat         | lon           | zoom |
          | 47.14122383 | 9.52169581334 | adfe |
        Then a HTTP 400 is returned

    Scenario Outline: Truthy values for boolean parameters
        When sending v1/reverse
          | lat         | lon           | addressdetails |
          | 47.14122383 | 9.52169581334 |  |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result has attributes address

        When sending v1/reverse
          | lat         | lon           | extratags |
          | 47.14122383 | 9.52169581334 |  |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result has attributes extratags

        When sending v1/reverse
          | lat         | lon           | namedetails |
          | 47.14122383 | 9.52169581334 |  |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result has attributes namedetails

        Examples:
          | value |
          | yes   |
          | no    |
          | -1    |
          | 100   |
          | false |
          | 00    |

    Scenario: Only one geometry can be requested
        When sending v1/reverse
          | lat         | lon           | polygon_text | polygon_svg |
          | 47.14122383 | 9.52169581334 | 1            | 1           |
        Then a HTTP 400 is returned

    Scenario Outline: Illegal jsonp are not allowed
        When sending v1/reverse with format json
          | lat         | lon           | json_callback |
          | 47.14122383 | 9.52169581334 |  |
        Then a HTTP 400 is returned

        Examples:
          | data |
          | 1asd |
          | bar(foo) |
          | XXX['bad'] |
          | foo; evil |

    Scenario Outline: Reverse debug mode produces valid HTML
        When sending v1/reverse
          | lat   | lon   | debug |
          |  |  | 1 |
        Then a HTTP 200 is returned
        And the result is valid html

        Examples:
          | lat      | lon     |
          | 0.0      | 0.0     |
          | 47.06645 | 9.56601 |
          | 47.14081 | 9.52267 |

    Scenario Outline: Full address display for city housenumber-level address with street
        When sending v1/reverse with format 
          | lat        | lon        |
          | 47.1068011 | 9.52810091 |
        Then a HTTP 200 is returned
        And the result is valid 
        And the result contains in field address
          | param          | value     |
          | house_number   | 8         |
          | road           | Im Winkel |
          | neighbourhood  | Oberdorf  |
          | village        | Triesen   |
          | ISO3166-2-lvl8 | LI-09     |
          | county         | Oberland  |
          | postcode       | 9495      |
          | country        | Liechtenstein |
          | country_code   | li        |

        Examples:
          | format  | outformat |
          | json    | json |
          | jsonv2  | json |
          | xml     | xml |

    Scenario Outline: Results with name details
        When sending v1/reverse with format 
          | lat      | lon     | zoom | namedetails |
          | 47.14052 | 9.52202 | 14   | 1           |
        Then a HTTP 200 is returned
        And the result is valid 
        And the result contains in field namedetails
          | name     |
          | Ebenholz |

        Examples:
          | format  | outformat |
          | json    | json |
          | jsonv2  | json |
          | xml     | xml |

    Scenario Outline: Results with extratags
        When sending v1/reverse with format 
          | lat      | lon     | zoom | extratags |
          | 47.14052 | 9.52202 | 14   | 1         |
        Then a HTTP 200 is returned
        And the result is valid 
        And the result contains in field extratags
          | wikidata |
          | Q4529531 |

        Examples:
          | format | outformat |
          | json   | json |
          | jsonv2 | json |
          | xml    | xml |

    Scenario Outline: Reverse with entrances
        When sending v1/reverse with format 
          | lat               | lon              | entrances | zoom |
          | 47.24942041089678 | 9.52854573737568 | 1         | 18   |
        Then a HTTP 200 is returned
        And the result is valid 
        And the result contains array field entrances where element 0 contains
          | osm_id     | type | lat        | lon       |
          | 6580031131 | yes  | 47.2489382 | 9.5284033 |

        Examples:
          | format | outformat |
          | json   | json |
          | jsonv2 | json |


================================================
FILE: test/bdd/features/api/reverse/v1_xml.feature
================================================
Feature: XML output for Reverse API
    Testing correctness of xml output (API version v1).

    Scenario Outline: Reverse XML - Simple reverse-geocoding with no results
        When sending v1/reverse
          | lat   | lon   |
          |  |  |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result has no attributes osm_type, address, extratags
        And the result contains
          | error |
          | Unable to geocode |

        Examples:
         | lat      | lon |
         | 0.0      | 0.0 |
         | 91.3     | 0.4    |
         | -700     | 0.4    |
         | 0.2      | 324.44 |
         | 0.2      | -180.4 |

    Scenario Outline: Reverse XML - OSM result with and without addresses
        When sending v1/reverse with format xml
          | lat    | lon   | addressdetails |
          | 47.066 | 9.504 |   |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result has attributes place_id
        And the result has  address
        And the result contains
          | osm_type | osm_id     | place_rank | address_rank |
          | node     | 6522627624 | 30         | 30           |
        And the result contains
          | lon       | lat        | boundingbox |
          | 9.5036065 | 47.0660892 | 47.0660392,47.0661392,9.5035565,9.5036565 |
        And the result contains
          | ref                   | display_name |
          | Dorfbäckerei Herrmann | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |

        Examples:
          | has_address | attributes     |
          | 1           | attributes     |
          | 0           | no attributes |

    Scenario: Reverse XML - Tiger address
        When sending v1/reverse with format xml
          | lat           | lon            |
          | 32.4752389363 | -86.4810198619 |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | osm_type | osm_id    | place_rank  | address_rank |
          | way      | 396009653 | 30          | 30           |
        And the result contains
          | lon         | lat        | boundingbox |
          | -86.4808553 | 32.4753580 | 32.4753080,32.4754080,-86.4809053,-86.4808053 |
        And the result contains
          | display_name |
          | 707, Upper Kingston Road, Upper Kingston, Prattville, Autauga County, 36067, United States |

    Scenario: Reverse XML - Interpolation address
        When sending v1/reverse with format xml
          | lat       | lon        |
          | 47.118533 | 9.57056562 |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | osm_type | osm_id | place_rank | address_rank |
          | way      | 1      | 30         | 30           |
        And the result contains
          | lon       | lat        | boundingbox |
          | 9.5705467 | 47.1185454 | 47.1184954,47.1185954,9.5704967,9.5705967 |
        And the result contains
          | display_name |
          | 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |

    Scenario: Reverse XML - Output of geojson
        When sending v1/reverse with format xml
          | lat      | lon     | polygon_geojson |
          | 47.06597 | 9.50467 | 1               |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | geojson |
          | {"type":"LineString","coordinates":[[9.5039353,47.0657546],[9.5040437,47.0657781],[9.5040808,47.065787],[9.5054298,47.0661407]]}  |

    Scenario: Reverse XML - Output of WKT
        When sending v1/reverse with format xml
          | lat      | lon     | polygon_text |
          | 47.06597 | 9.50467 | 1            |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | geotext!wkt |
          | 9.5039353 47.0657546, 9.5040437 47.0657781, 9.5040808 47.065787, 9.5054298 47.0661407 |

    Scenario: Reverse XML - Output of SVG
        When sending v1/reverse with format xml
          | lat      | lon     | polygon_svg |
          | 47.06597 | 9.50467 | 1           |
        Then a HTTP 200 is returned
        And the result is valid xml
        And the result contains
          | geosvg |
          | M 9.5039353 -47.0657546 L 9.5040437 -47.0657781 9.5040808 -47.065787 9.5054298 -47.0661407 |

    Scenario: Reverse XML - Output of KML
       When sending v1/reverse with format xml
          | lat      | lon     | polygon_kml |
          | 47.06597 | 9.50467 | 1           |
        Then a HTTP 200 is returned
        And the result is valid xml
       And the result contains
          | geokml!fm |
          | 9.5039\d*,47.0657\d* 9.5040\d*,47.0657\d* 9.5040\d*,47.065\d* 9.5054\d*,47.0661\d* |


================================================
FILE: test/bdd/features/api/search/language.feature
================================================
Feature: Localization of search results

    Scenario: Search - default language
        When sending v1/search
          | q |
          | Liechtenstein |
        Then a HTTP 200 is returned
        And the result is valid json
        And result 0 contains
          | display_name |
          | Liechtenstein |

    Scenario: Search - accept-language first
        When sending v1/search
          | q             | accept-language |
          | Liechtenstein | zh,de |
        Then a HTTP 200 is returned
        And the result is valid json
        And result 0 contains
          | display_name |
          | 列支敦士登 |

    Scenario: Search - accept-language missing
        When sending v1/search
          | q             | accept-language |
          | Liechtenstein | xx,fr,en,de |
        Then a HTTP 200 is returned
        And the result is valid json
        And result 0 contains
          | display_name |
          | Liechtenstein |

    Scenario: Search - http accept language header first
        Given the HTTP header
          | accept-language |
          | fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
        When sending v1/search
          | q |
          | Liechtenstein |
        Then a HTTP 200 is returned
        And the result is valid json
        And result 0 contains
          | display_name |
          | Liktinstein |

    Scenario: Search - http accept language header and accept-language
        Given the HTTP header
          | accept-language |
          | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3 |
        When sending v1/search
          | q | accept-language |
          | Liechtenstein | fo,en |
        Then a HTTP 200 is returned
        And the result is valid json
        And result 0 contains
          | display_name |
          | Liktinstein |

    Scenario: Search - http accept language header fallback
        Given the HTTP header
          | accept-language |
          | fo-ca,en-ca;q=0.5 |
        When sending v1/search
          | q |
          | Liechtenstein |
        Then a HTTP 200 is returned
        And the result is valid json
        And result 0 contains
          | display_name |
          | Liktinstein |

    Scenario: Search - http accept language header fallback (upper case)
        Given the HTTP header
          | accept-language |
          | fo-FR;q=0.8,en-ca;q=0.5 |
        When sending v1/search
          | q |
          | Liechtenstein |
        Then a HTTP 200 is returned
        And the result is valid json
        And result 0 contains
          | display_name |
          | Liktinstein |


================================================
FILE: test/bdd/features/api/search/params.feature
================================================
Feature: Search queries
    Testing different queries and parameters

    Scenario: Simple XML search
        When sending v1/search with format xml
          | q |
          | Schaan |
        Then a HTTP 200 is returned
        And the result is valid xml
        And all results have attributes place_id,osm_type,osm_id
        And all results have attributes place_rank,boundingbox
        And all results have attributes lat,lon,display_name
        And all results have attributes class,type,importance
        And all results have no attributes address
        And all results contain
          | boundingbox!in_box |
          | 46.5,47.5,9,10 |

    Scenario Outline: Simple JSON search
        When sending v1/search with format 
          | q |
          | Vaduz |
        Then a HTTP 200 is returned
        And the result is valid json
        And all results have attributes place_id,licence,,type
        And all results have attributes osm_type,osm_id,boundingbox
        And all results have attributes lat,lon,display_name,importance
        And all results have no attributes address
        And all results contain
          | boundingbox!in_box |
          | 46.5,47.5,9,10 |

        Examples:
          | format | cname    |
          | json   | class    |
          | jsonv2 | category |

    Scenario: Unknown formats returns a user error
        When sending v1/search with format x45
          | q |
          | Vaduz |
        Then a HTTP 400 is returned

    Scenario Outline: Search with addressdetails
        When sending v1/search with format 
          | q       | addressdetails |
          | Triesen | 1 |
        Then a HTTP 200 is returned
        And the result is valid 
        And result 0 contains in field address
          | param        | value |
          | village      | Triesen |
          | county       | Oberland |
          | postcode     | 9495 |
          | country      | Liechtenstein |
          | country_code | li |
          | ISO3166-2-lvl8 | LI-09 |

        Examples:
          | format | outformat |
          | json   | json |
          | jsonv2 | json |
          | geojson | geojson |
          | xml    | xml |

    Scenario Outline: Search with entrances
        When sending v1/search with format 
          | q                            | entrances |
          | Saint Joseph Catholic Church | 1 |
        Then a HTTP 200 is returned
        And the result is valid 

        Examples:
          | format | outformat |
          | json   | json |
          | jsonv2 | json |
          | geojson | geojson |
          | xml    | xml |

    Scenario: Coordinate search with addressdetails
        When geocoding "47.12400621,9.6047552"
          | accept-language |
          | en |
        Then all results contain
          | display_name |
          | Guschg, Valorschstrasse, Balzers, Oberland, 9497, Liechtenstein |

    Scenario: Address details with unknown class types
        When geocoding "Kloster St. Elisabeth"
        Then result 0 contains
          | category | type      | address+amenity |
          | amenity  | monastery | Kloster St. Elisabeth |

    Scenario: Disabling deduplication
        When geocoding "Malbunstr, Schaan"
        Then exactly 1 result is returned
        When geocoding "Malbunstr, Schaan"
          | dedupe |
          | 0 |
        Then exactly 4 results are returned

    Scenario: Search with bounded viewbox in right area
        When geocoding "post"
          | bounded | viewbox |
          | 1       |  9,47,10,48 |
        Then result 0 contains
          | address+town |
          | Vaduz |
        When geocoding "post"
          | bounded | viewbox |
          | 1       |  9.49712,47.17122,9.52605,47.16242 |
        Then result 0 contains
          | address+town |
          | Schaan |

    Scenario: Country search with bounded viewbox remain in the area
        When geocoding
          | bounded | viewbox                           | country |
          | 1       | 9.49712,47.17122,9.52605,47.16242 | de |
        Then exactly 0 results are returned

    Scenario: Search with bounded viewboxlbrt in right area
        When geocoding "bar"
          | bounded | viewboxlbrt |
          | 1       | 9.49712,47.16242,9.52605,47.17122 |
        Then all results contain
          | address+town |
          | Schaan |

    Scenario: No POI search with unbounded viewbox
        When geocoding "restaurant"
          | viewbox |
          | 9.93027,53.61634,10.10073,53.54500 |
        Then all results contain
          | display_name!fm |
          | .*[Rr]estaurant.* |

    Scenario: bounded search remains within viewbox, even with no results
         When geocoding "[restaurant]"
           | bounded | viewbox |
           | 1       | 43.5403125,-5.6563282,43.54285,-5.662003 |
        Then exactly 0 results are returned

    Scenario: bounded search remains within viewbox with results
        When geocoding "restaurant"
         | bounded | viewbox |
         | 1       | 9.49712,47.17122,9.52605,47.16242 |
        Then all results contain
         | boundingbox!in_box |
         | 47.16242,47.17122,9.49712,9.52605 |

    Scenario: Prefer results within viewbox
        When geocoding "Gässle"
          | accept-language | viewbox |
          | en              | 9.52413,47.10759,9.53140,47.10539 |
        Then result 0 contains
          | address+village |
          | Triesen |
        When geocoding "Gässle"
          | accept-language | viewbox |
          | en              | 9.45949,47.08421,9.54094,47.05466 |
        Then result 0 contains
          | address+town |
          | Balzers |

    Scenario: viewboxes cannot be points
        When sending v1/search
          | q   | viewbox |
          | foo | 1.01,34.6,1.01,34.6 |
        Then a HTTP 400 is returned

    Scenario Outline: viewbox must have four coordinate numbers
        When sending v1/search
          | q   | viewbox |
          | foo |  |
        Then a HTTP 400 is returned

    Examples:
        | viewbox |
        | 34      |
        | 0.003,-84.4 |
        | 5.2,4.5542,12.4 |
        | 23.1,-6,0.11,44.2,9.1 |

    Scenario Outline: viewboxlbrt must have four coordinate numbers
        When sending v1/search
          | q   | viewboxlbrt |
          | foo |  |
        Then a HTTP 400 is returned

    Examples:
        | viewbox |
        | 34      |
        | 0.003,-84.4 |
        | 5.2,4.5542,12.4 |
        | 23.1,-6,0.11,44.2,9.1 |

    Scenario: Overly large limit number for search results
        When geocoding "restaurant"
          | limit |
          | 1000 |
        Then exactly 35 results are returned

    Scenario: Limit number of non-duplicated search results
        When geocoding "landstr"
          | dedupe |
          | 0      |
        Then exactly 10 results are returned
        When geocoding "landstr"
          | limit | dedupe |
          | 4     | 0      |
        Then exactly 4 results are returned

    Scenario: Limit parameter must be a number
        When sending v1/search
          | q           | limit |
          | Blue Laguna | );    |
        Then a HTTP 400 is returned

    Scenario: Restrict to feature type country
        When geocoding "fürstentum"
          | featureType |
          | country |
        Then all results contain
          | place_rank |
          | 4 |

    Scenario: Restrict to feature type state
        When geocoding "Wangerberg"
        Then more than 0 results are returned
        When geocoding "Wangerberg"
          | featureType |
          | state |
        Then exactly 0 results are returned

    Scenario: Restrict to feature type city
        When geocoding "vaduz"
          | featureType |
          | state |
        Then exactly 0 results are returned
        When geocoding "vaduz"
          | featureType |
          | city |
        Then more than 0 results are returned
        Then all results contain
          | place_rank |
          | 16 |

    Scenario: Restrict to feature type settlement
        When geocoding "Malbun"
        Then result 1 contains
          | category |
          | landuse |
        When geocoding "Malbun"
          | featureType |
          | settlement |
        Then all results contain
          | category | type |
          | place    | village |

    Scenario: Exclude a specific place using an OSM ID
        When geocoding "Malbun"
        Then result 0 contains
          | category | type |
          | place | village |
        When geocoding "Malbun"
          | exclude_place_ids |
          | N347290636 |
        Then result 0 contains
          | category | 
          | landuse | 

    Scenario Outline: Search with polygon threshold (json)
        When sending v1/search with format json
          | q           | polygon_geojson | polygon_threshold |
          | Triesenberg | 1               | 
| Then a HTTP 200 is returned And the result is valid json And more than 0 results are returned And all results have attributes geojson Examples: | th | | -1 | | 0.0 | | 0.5 | | 999 | Scenario Outline: Search with polygon threshold (xml) When sending v1/search with format xml | q | polygon_geojson | polygon_threshold | | Triesenberg | 1 | | Then a HTTP 200 is returned And the result is valid xml And more than 0 results are returned And all results have attributes geojson Examples: | th | | -1 | | 0.0 | | 0.5 | | 999 | Scenario Outline: Search with invalid polygon threshold (xml) When sending v1/search with format xml | q | polygon_geojson | polygon_threshold | | Triesenberg | 1 | | Then a HTTP 400 is returned Examples: | th | | x | | ;; | | 1m | Scenario Outline: Search with extratags When sending v1/search with format | q | extratags | | Landstr | 1 | Then a HTTP 200 is returned And the result is valid And more than 0 results are returned Then all results have attributes extratags Examples: | format | outformat | | xml | xml | | json | json | | jsonv2 | json | | geojson | geojson | Scenario Outline: Search boundary=administrative with extratags=1 returns admin_level When sending v1/search with format | q | featureType | extratags | | Triesenberg | city | 1 | Then a HTTP 200 is returned And the result is valid And more than 0 results are returned And result 0 contains | | | | boundary | administrative | And result 0 contains in field | param | value | | admin_level | 8 | Examples: | format | outformat | cname | tname | ename | | xml | xml | class | type | extratags | | json | json | class | type | extratags | | jsonv2 | json | category | type | extratags | | geojson | geojson | category | type | extratags | | geocodejson | geocodejson | osm_key | osm_value | extra | Scenario Outline: Search with namedetails When sending v1/search with format | q | namedetails | | Landstr | 1 | Then a HTTP 200 is returned And the result is valid And more than 0 results are returned Then all results have attributes namedetails Examples: | format | outformat | | xml | xml | | json | json | | jsonv2 | json | | geojson | geojson | Scenario Outline: Search result with contains formatted geometry When sending v1/search with format | q | | | Triesenberg | 1 | Then a HTTP 200 is returned And the result is valid And more than 0 results are returned And all results have attributes Examples: | format | outformat | param | response_attribute | | xml | xml | polygon_text | geotext | | json | json | polygon_text | geotext | | jsonv2 | json | polygon_text | geotext | | xml | xml | polygon_svg | geosvg | | json | json | polygon_svg | svg | | jsonv2 | json | polygon_svg | svg | | xml | xml | polygon_kml | geokml | | json | json | polygon_kml | geokml | | jsonv2 | json | polygon_kml | geokml | | xml | xml | polygon_geojson | geojson | | json | json | polygon_geojson | geojson | | jsonv2 | json | polygon_geojson | geojson | | geojson | geojson | polygon_geojson | geojson | Scenario Outline: Search result in geojson format contains no non-geojson geometry When sending v1/search with format geojson | q | | | Triesenberg | 1 | Then a HTTP 200 is returned And the result is valid geojson And more than 0 results are returned And all results have no attributes Examples: | param | response_attribute | | polygon_text | geotext | | polygon_svg | svg | | polygon_kml | geokml | ================================================ FILE: test/bdd/features/api/search/postcode.feature ================================================ Feature: Searches with postcodes Various searches involving postcodes Scenario: US 5+4 ZIP codes are shortened to 5 ZIP codes if not found When geocoding "36067-1111, us" Then all results contain in field address | postcode | | 36067 | And all results contain | type | | postcode | Scenario: Postcode search with address When geocoding "9486, mauren" Then result 0 contains | type | | postcode | Scenario: Postcode search with country When geocoding "9486, li" Then all results contain in field address | country_code | | li | Scenario: Postcode search with country code restriction When geocoding "9490" | countrycodes | | li | Then all results contain in field address | country_code | | li | Scenario: Postcode search with bounded viewbox restriction When geocoding "9486" | bounded | viewbox | | 1 | 9.55,47.20,9.58,47.22 | Then all results contain in field address | postcode | | 9486 | When geocoding "9486" | bounded | viewbox | | 1 | 5.00,20.00,6.00,21.00 | Then exactly 0 result is returned Scenario: Postcode search with structured query When geocoding "" | postalcode | country | | 9490 | li | Then all results contain in field address | country_code | postcode | | li | 9490 | ================================================ FILE: test/bdd/features/api/search/queries.feature ================================================ Feature: Search queries Generic search result correctness Scenario: Search for natural object When geocoding "Samina" | accept-language | | en | Then result 0 contains | category | type | display_name | | waterway | river | Samina, Austria | Scenario: House number search for non-street address When geocoding "6 Silum, Liechtenstein" | accept-language | | en | Then result 0 contains in field address | param | value | | house_number | 6 | | village | Silum | | town | Triesenberg | | county | Oberland | | postcode | 9497 | | country | Liechtenstein | | country_code | li | | ISO3166-2-lvl8 | LI-10 | Scenario: Search for house number interpolation When geocoding "Grosssteg 1023, Triesenberg" | accept-language | | de | Then result 0 contains in field address | param | value | | house_number | 1023 | | road | Grosssteg | | village | Sücka | | postcode | 9497 | | town | Triesenberg | | country | Liechtenstein | | country_code | li | Scenario: With missing housenumber search falls back to road When geocoding "Bündaweg 555" Then result 0 contains in field address | param | value | | road | Bündaweg | | village | Silum | | postcode | 9497 | | county | Oberland | | town | Triesenberg | | country | Liechtenstein | | country_code | li | | ISO3166-2-lvl8 | LI-10 | And all results have no attributes address+house_number Scenario Outline: Housenumber 0 can be found When sending v1/search with format | q | addressdetails | | Gnalpstrasse 0 | 1 | Then a HTTP 200 is returned And the result is valid And all results contain | display_name!fm | address+house_number | | 0,.* | 0 | Examples: | format | outformat | | xml | xml | | json | json | | jsonv2 | json | | geojson | geojson | Scenario: TIGER house number When geocoding "697 Upper Kingston Road" Then all results contain | osm_type | display_name!fm | address+house_number | | way | 697,.* | 697 | Scenario: Search with class-type feature When geocoding "bars in ebenholz" Then all results contain | place_rank | | 30 | Scenario: Search with specific amenity When geocoding "[restaurant] Vaduz" Then all results contain | category | type | address+country | | amenity | restaurant | Liechtenstein | Scenario: Search with specific amenity also work in country When geocoding "restaurants in liechtenstein" Then all results contain | category | type | address+country | | amenity | restaurant | Liechtenstein | Scenario: Search with key-value amenity When geocoding "[club=scout] Vaduz" Then all results contain | category | type | | club | scout | Scenario: POI search near given coordinate When geocoding "restaurant near 47.16712,9.51100" Then all results contain | category | type | | amenity | restaurant | Scenario: Arbitrary key/value search near given coordinate When geocoding "[leisure=firepit] 47.150° N 9.5340493° E" Then all results contain | category | type | | leisure | firepit | Scenario: POI search in a bounded viewbox When geocoding "restaurants" | viewbox | bounded | | 9.50830,47.15253,9.52043,47.14866 | 1 | Then all results contain | category | type | | amenity | restaurant | Scenario Outline: Key/value search near given coordinate can be restricted to country When geocoding "[natural=peak] 47.06512,9.53965" | countrycodes | | | Then all results contain | address+country_code | | | Examples: | cc | | li | | ch | Scenario: Name search near given coordinate When geocoding "sporry" Then result 0 contains | address+town | | Vaduz | When geocoding "sporry, 47.10791,9.52676" Then result 0 contains | address+village | | Triesen | Scenario: Name search near given coordinate without result When geocoding "sporry, N 47 15 7 W 9 61 26" Then exactly 0 results are returned Scenario: Arbitrary key/value search near a road When geocoding "[amenity=drinking_water] Wissfläckaweg" Then all results contain | category | type | | amenity | drinking_water | Scenario: Ignore other country codes in structured search with country When geocoding | countrycodes | country | | li | de | Then exactly 0 results are returned Scenario: Ignore country searches when query is restricted to countries When geocoding "fr" Then all results contain | name | | France | When geocoding "fr" | countrycodes | | li | Then exactly 0 results are returned Scenario: Country searches only return results for the given country When geocoding "Ans Trail" | countrycodes | | li | Then all results contain | address+country_code | | li | # https://trac.openstreetmap.org/ticket/5094 Scenario: housenumbers are ordered by complete match first When geocoding "Austrasse 11, Vaduz" Then result 0 contains | address+house_number | | 11 | Scenario Outline: Coordinate searches with white spaces When geocoding "" Then the result set contains exactly | category | | water | Examples: | data | | sporry weiher, N 47.10791° E 9.52676° | | sporry weiher, N 47.10791° E 9.52676° | | sporry weiher , N 47.10791° E 9.52676° | | sporry weiher, N 47.10791° E 9.52676° | | sporry weiher , N 47.10791° E 9.52676° | Scenario: Searches with white spaces When geocoding "52 Bodastr , Triesenberg" Then all results contain | category | type | | highway | residential | # github #1949 Scenario: Addressdetails always return the place type When geocoding "Vaduz" Then result 0 contains | address+town | | Vaduz | ================================================ FILE: test/bdd/features/api/search/simple.feature ================================================ Feature: Simple Tests Simple tests for internal server errors and response format. Scenario Outline: Garbage Searches When sending v1/search | q | | | Then a HTTP 200 is returned And the result is valid json And exactly 0 results are returned Examples: | query | | New York, New York | | 12, Main Street, Houston | | München | | 東京都 | | hotels in sdfewf | | xywxkrf | | gh; foo() | | %#$@*&l;der#$! | | 234.23.14.5 | | aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus | | . | Scenario: Empty XML search When sending v1/search with format xml | q | | xnznxvcx | Then a HTTP 200 is returned And the result is valid xml Then the result metadata contains | param | value | | querystring | xnznxvcx | | more_url!fm | .*q=xnznxvcx.*format=xml | Scenario: Empty XML search with special XML characters When sending v1/search with format xml | q | | xfdghn&zxn"xvbyxcssdex | Then a HTTP 200 is returned And the result is valid xml Then the result metadata contains | param | value | | querystring | xfdghn&zxn"xvbyxcssdex | | more_url!fm | .*q=xfdghn%26zxn%22xvbyx%3Cvxx%3Ecssdex.*format=xml | Scenario: Empty XML search with viewbox When sending v1/search with format xml | q | viewbox | | xnznxvcx | 12,33,77,45.13 | Then a HTTP 200 is returned And the result is valid xml And the result metadata contains | param | value | | querystring | xnznxvcx | | viewbox | 12,33,77,45.13 | Scenario: Empty XML search with viewboxlbrt When sending v1/search with format xml | q | viewboxlbrt | | xnznxvcx | 12,34.13,77,45 | Then a HTTP 200 is returned And the result is valid xml And the result metadata contains | param | value | | querystring | xnznxvcx | | viewbox | 12,34.13,77,45 | Scenario: Empty XML search with viewboxlbrt and viewbox When sending v1/search with format xml | q | viewbox | viewboxblrt | | pub | 12,33,77,45.13 | 1,2,3,4 | Then a HTTP 200 is returned And the result is valid xml And the result metadata contains | param | value | | querystring | pub | | viewbox | 12,33,77,45.13 | Scenario: Empty XML search with excluded place ids When sending v1/search with format xml | q | exclude_place_ids | | jghrleoxsbwjer | 123,n456,W789 | Then a HTTP 200 is returned And the result is valid xml And the result metadata contains | param | value | | exclude_place_ids | 123,N456,W789 | Scenario: Empty XML search with bad excluded place ids When sending v1/search with format xml | q | exclude_place_ids | | jghrleoxsbwjer | , | Then a HTTP 200 is returned And the result is valid xml And the result metadata has no attributes exclude_place_ids Scenario: Search with invalid excluded place ids When sending v1/search with format json | q | exclude_place_ids | | Tokyo | | Then a HTTP 400 is returned And the result is valid json And the result contains | error+code | error+message | | 400 | Invalid exclude ID: | Examples: | ids | bad_id | | abc | abc | | X123 | X123 | | N:100 | N:100 | | -123 | -123 | | 123,abc,456 | abc | Scenario Outline: Wrapping of illegal jsonp search requests When sending v1/search with format json | q | json_callback | | Tokyo | | Then a HTTP 400 is returned And the result is valid json And the result contains | error+code | error+message | | 400 | Invalid json_callback value | Examples: | data | | 1asd | | bar(foo) | | XXX['bad'] | | foo; evil | | 234 | Scenario: Ignore jsonp parameter for anything but json When sending v1/search with format xml | q | json_callback | | Tokyo | 234 | Then a HTTP 200 is returned Then the result is valid xml Scenario Outline: Empty search for json like When sending v1/search with format | q | | YHlERzzx | Then a HTTP 200 is returned And the result is valid And exactly 0 results are returned Examples: | format | outformat | | json | json | | jsonv2 | json | | geojson | geojson | | geocodejson | geocodejson | Scenario: Search for non-existing coordinates When geocoding "-21.0,-33.0" Then exactly 0 results are returned Scenario: Country code selection is retained in more URL (#596) When sending v1/search with format xml | q | countrycodes | | Vaduz | pl,1,,invalid,undefined,%3Cb%3E,bo,, | Then a HTTP 200 is returned And the result is valid xml And the result metadata contains | more_url!fm | | .*&countrycodes=pl%2Cbo&.* | Scenario Outline: Search debug output does not return errors When sending v1/search | q | debug | | | 1 | Then a HTTP 200 is returned And the result is valid html Examples: | query | | Liechtenstein | | Triesen | | Pfarrkirche | | Landstr 27 Steinort, Triesenberg, 9495 | | 9497 | | restaurant in triesen | ================================================ FILE: test/bdd/features/api/search/structured.feature ================================================ Feature: Structured search queries Testing correctness of results with structured queries Scenario: Structured search for country only When geocoding | country | | Liechtenstein | Then all results contain in field address | country_code | country | | li | Liechtenstein | Scenario: Structured search for postcode only When geocoding | postalcode | | 9495 | Then all results contain | type!fm | address+postcode | | ^post(al_)?code | 9495 | Scenario: Structured search for street, postcode and country When sending v1/search with format xml | street | postalcode | country | | Old Palace Road | GU2 7UP | United Kingdom | Then a HTTP 200 is returned And the result is valid xml And the result metadata contains | querystring | | Old Palace Road, GU2 7UP, United Kingdom | Scenario: Structured search for street with housenumber, city and postcode When geocoding | street | city | postalcode | | 19 Am schrägen Weg | Vaduz | 9490 | Then all results contain in field address | house_number | road | | 19 | Am Schrägen Weg | Scenario: Structured search for street with housenumber, city and bad postcode When geocoding | street | city | postalcode | | 19 Am schrägen Weg | Vaduz | 9491 | Then all results contain in field address | house_number | road | | 19 | Am Schrägen Weg | Scenario: Structured search for amenity, city When geocoding | city | amenity | | Vaduz | bar | Then all results contain | address+country | category | type!fm | | Liechtenstein | amenity | (pub)\|(bar)\|(restaurant) | #176 Scenario: Structured search restricts rank When geocoding | city | | Steg | Then all results contain | addresstype | | village | #3651 Scenario: Structured search with surrounding extra characters When geocoding | street | city | postalcode | | "19 Am schrägen Weg" | "Vaduz" | "9491" | Then all results contain in field address | house_number | road | | 19 | Am Schrägen Weg | ================================================ FILE: test/bdd/features/api/search/v1_geocodejson.feature ================================================ Feature: Search API geocodejson output Testing correctness of geocodejson output. Scenario: Search geocodejson - City housenumber-level address with street When sending v1/search with format geocodejson | q | addressdetails | | Im Winkel 8, Triesen | 1 | Then a HTTP 200 is returned And the result is valid geocodejson And all results contain | housenumber | street | postcode | city | country | | 8 | Im Winkel | 9495 | Triesen | Liechtenstein | Scenario: Search geocodejson - Town street-level address with street When sending v1/search with format geocodejson | q | addressdetails | | Gnetsch, Balzers | 1 | Then a HTTP 200 is returned And the result is valid geocodejson And all results contain | name | city | postcode | country | | Gnetsch | Balzers | 9496 | Liechtenstein | Scenario: Search geocodejson - Town street-level address with footway When sending v1/search with format geocodejson | q | addressdetails | | 6000 jahre geschichte | 1 | Then a HTTP 200 is returned And the result is valid geocodejson And all results contain | street | city | postcode | country | | Burgweg | Balzers | 9496 | Liechtenstein | Scenario: Search geocodejson - City address with suburb When sending v1/search with format geocodejson | q | addressdetails | | Lochgass 5, Ebenholz, Vaduz | 1 | Then a HTTP 200 is returned And the result is valid geocodejson And all results contain | housenumber | street | district | city | postcode | country | | 5 | Lochgass | Ebenholz | Vaduz | 9490 | Liechtenstein | ================================================ FILE: test/bdd/features/api/status/failures.feature ================================================ Feature: Status queries against unknown database Testing status query Background: Given an unknown database Scenario: Failed status as text When sending v1/status Then a HTTP 500 is returned And the page content equals "ERROR: Database connection failed" Scenario: Failed status as json When sending v1/status with format json Then a HTTP 200 is returned And the result is valid json And the result contains | status!:d | message | | 700 | Database connection failed | And the result has no attributes data_updated ================================================ FILE: test/bdd/features/api/status/simple.feature ================================================ Feature: Status queries Testing status query Scenario: Status as text When sending v1/status Then a HTTP 200 is returned And the page content equals "OK" Scenario: Status as json When sending v1/status with format json Then a HTTP 200 is returned And the result is valid json And the result contains | status!:d | message | data_updated!fm | | 0 | OK | ....-..-..T..:..:...00:00 | ================================================ FILE: test/bdd/features/db/import/addressing.feature ================================================ Feature: Address computation Tests for filling of place_addressline Scenario: place nodes are added to the address when they are close enough Given the 0.002 grid | 2 | | | | | | 1 | | 3 | And the places | osm | class | type | name | geometry | | N1 | place | square | Square | 1 | | N2 | place | hamlet | West Farm | 2 | | N3 | place | hamlet | East Farm | 3 | When importing Then place_addressline contains exactly | object | address | fromarea | | N1 | N3 | False | When geocoding "Square" Then the result set contains | object | display_name | | N1 | Square, East Farm | Scenario: given two place nodes, the closer one wins for the address Given the grid | 2 | | | 1 | | 3 | And the named places | osm | class | type | geometry | | N1 | place | square | 1 | | N2 | place | hamlet | 2 | | N3 | place | hamlet | 3 | When importing Then place_addressline contains | object | address | fromarea | isaddress | | N1 | N3 | False | True | | N1 | N2 | False | False | Scenario: boundaries around the place are added to the address Given the grid | 1 | | 4 | | 7 | 10 | | 2 | | 5 | | 8 | 11 | | | | | | | | | | | | | | | | | | 6 | | 9 | | | | 99 | | | | | | 3 | | | | | 12 | And the named places | osm | class | type | admin | geometry | | R1 | boundary | administrative | 3 | (1,2,3,12,11,10,7,8,9,6,5,4,1) | | R2 | boundary | administrative | 4 | (2,3,12,11,8,9,6,5,2) | | N1 | place | square | 15 | 99 | When importing Then place_addressline contains | object | address | isaddress | | N1 | R1 | True | | N1 | R2 | True | Scenario: with boundaries of same rank the one with the closer centroid is preferred Given the grid | 1 | | | 3 | | 5 | | | 9 | | | | | | 2 | | | 4 | | 6 | And the named places | osm | class | type | admin | geometry | | R1 | boundary | administrative | 8 | (1,2,4,3,1) | | R2 | boundary | administrative | 8 | (1,2,6,5,1) | | N1 | place | square | 15 | 9 | When importing Then place_addressline contains | object | address | isaddress | | N1 | R1 | True | | N1 | R2 | False | Scenario: boundary areas are preferred over place nodes in the address Given the grid | 1 | | | | 10 | | 3 | | | 5 | | | | | | | | 6 | | | | | | | 2 | | | | 11 | | 4 | And the named places | osm | class | type | admin | geometry | | N1 | place | square | 15 | 5 | | N2 | place | city | 15 | 6 | | R1 | place | city | 8 | (1,2,4,3,1) | | R2 | boundary | administrative | 9 | (1,10,11,2,1) | When importing Then place_addressline contains | object | address | isaddress | cached_rank_address | | N1 | R1 | True | 16 | | N1 | R2 | True | 18 | | N1 | N2 | False | 18 | Scenario: place nodes outside a smaller ranked area are ignored Given the grid | 1 | | 2 | | | | 7 | | 9 | | 4 | | 3 | | And the named places | osm | class | type | admin | geometry | | N1 | place | square | 15 | 7 | | N2 | place | city | 15 | 9 | | R1 | place | city | 8 | (1,2,3,4,1) | When importing Then place_addressline contains exactly | object | address | isaddress | cached_rank_address | | N1 | R1 | True | 16 | Scenario: place nodes close enough to smaller ranked place nodes are included Given the 0.002 grid | 2 | | 3 | 1 | And the named places | osm | class | type | geometry | | N1 | place | square | 1 | | N2 | place | hamlet | 2 | | N3 | place | quarter | 3 | When importing Then place_addressline contains | object | address | fromarea | isaddress | | N1 | N2 | False | True | | N1 | N3 | False | True | Scenario: place nodes too far away from a smaller ranked place nodes are marked non-address Given the 0.002 grid | 2 | | | 1 | | 3 | And the named places | osm | class | type | geometry | | N1 | place | square | 1 | | N2 | place | hamlet | 2 | | N3 | place | quarter | 3 | When importing Then place_addressline contains | object | address | fromarea | isaddress | | N1 | N2 | False | True | | N1 | N3 | False | False | # github #121 Scenario: Roads crossing boundaries should contain both states Given the grid | 1 | | | 2 | | 3 | | | 7 | | | 8 | | | 4 | | | 5 | | 6 | And the named places | osm | class | type | geometry | | W1 | highway | road | 7, 8 | And the named places | osm | class | type | admin | geometry | | W10 | boundary | administrative | 5 | (1, 2, 5, 4, 1) | | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 2) | When importing Then place_addressline contains | object | address | cached_rank_address | | W1 | W10 | 10 | | W1 | W11 | 10 | Scenario: Roads following a boundary should contain both states Given the grid | 1 | | | 2 | | 3 | | | | 8 | 7 | | | | 4 | | | 5 | | 6 | And the named places | osm | class | type | geometry | | W1 | highway | road | 2, 7, 8 | And the named places | osm | class | type | admin | geometry | | W10 | boundary | administrative | 5 | (1, 2, 5, 4, 1) | | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 2) | When importing Then place_addressline contains | object | address | cached_rank_address | | W1 | W10 | 10 | | W1 | W11 | 10 | Scenario: Roads should not contain boundaries they touch in a end point Given the grid | 1 | | | 2 | | 3 | | | 7 | | 8 | | | | 4 | | | 5 | | 6 | And the named places | osm | class | type | geometry | | W1 | highway | road | 7, 8 | And the named places | osm | class | type | admin | geometry | | W10 | boundary | administrative | 5 | (1, 2, 8, 5, 4, 1) | | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 8, 2) | When importing Then place_addressline contains exactly | object | address | cached_rank_address | | W1 | W10 | 10 | Scenario: Roads should not contain boundaries they touch in a middle point Given the grid | 1 | | | 2 | | 3 | | | 7 | | 8 | | | | 4 | | 9 | 5 | | 6 | And the named places | osm | class | type | geometry | | W1 | highway | road | 7, 8, 9 | And the named places | osm | class | type | admin | geometry | | W10 | boundary | administrative | 5 | (1, 2, 8, 5, 4, 1) | | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 8, 2) | When importing Then place_addressline contains exactly | object | address | cached_rank_address | | W1 | W10 | 10 | Scenario: Locality points should contain all boundaries they touch Given the 0.001 grid | 1 | | | 2 | | 3 | | | | | 8 | | | | 4 | | | 5 | | 6 | And the named places | osm | class | type | geometry | | N1 | place | locality | 8 | And the named places | osm | class | type | admin | geometry | | W10 | boundary | administrative | 5 | (1, 2, 8, 5, 4, 1) | | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 8, 2) | When importing Then place_addressline contains | object | address | cached_rank_address | | N1 | W10 | 10 | | N1 | W11 | 10 | Scenario: Areas should not contain boundaries they touch Given the grid | 1 | | | 2 | | 3 | | | | | | | | | 4 | | | 5 | | 6 | And the named places | osm | class | type | geometry | | W1 | landuse | industrial | (1, 2, 5, 4, 1) | And the named places | osm | class | type | admin | geometry | | W10 | boundary | administrative | 5 | (2, 3, 6, 5, 2) | When importing Then place_addressline contains exactly | object | address | Scenario: buildings with only addr:postcodes do not appear in the address of a way Given the grid with origin DE | 1 | | | | | 8 | | 6 | | 2 | | | | | | | | | | | | | |13 | | | | | | | | | | 20| | | 21| | | | | | | | | | | | | | | | | | | | | | | | 9 | | | | | | 4 | | | | | | | 7 | | 3 | And the named places | osm | class | type | admin | addr+postcode | geometry | | R1 | boundary | administrative | 6 | 10000 | (1,2,3,4,1)| | R34 | boundary | administrative | 8 | 11200 | (1,6,7,4,1)| | R4 | boundary | administrative | 10 | 11230 | (1,8,9,4,1)| And the named places | osm | class | type | geometry | | W93 | highway | residential | 20,21 | And the postcodes | osm | postcode | centroid | | W22 | 11234 | 13 | When importing Then place_addressline contains exactly | object | address | | R4 | R1 | | R4 | R34 | | R34 | R1 | | W93 | R1 | | W93 | R34 | | W93 | R4 | Scenario: squares do not appear in the address of a street Given the grid | | 1 | | 2 | | | 8 | | | | 9 | | | 4 | | 3 | | And the named places | osm | class | type | geometry | | W1 | highway | residential | 8, 9 | | W2 | place | square | (1, 2, 3 ,4, 1) | When importing Then place_addressline contains exactly | object | address | Scenario: addr:* tags are honored even when a street is far away from the place Given the grid | 1 | | 2 | | | 5 | | | | | 8 | 9 | | | 4 | | 3 | | | 6 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | And the places | osm | class | type | addr+city | geometry | | W1 | highway | primary | Left | 8,9 | | W2 | highway | primary | Right | 8,9 | When importing Then place_addressline contains exactly | object | address | isaddress | | W1 | R1 | True | | W1 | R2 | False | | W2 | R2 | True | Scenario: addr:* tags are honored even when a POI is far away from the place Given the grid | 1 | | 2 | | | 5 | | | | | 8 | 9 | | | 4 | | 3 | | | 6 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | And the places | osm | class | type | name | addr+city | geometry | | W1 | highway | primary | Wonderway | Right | 8,9 | | N1 | amenity | cafe | Bolder | Left | 9 | When importing Then place_addressline contains exactly | object | address | isaddress | | W1 | R2 | True | | N1 | R1 | True | When geocoding "Bolder" Then the result set contains | object | display_name | | N1 | Bolder, Wonderway, Left | Scenario: addr:* tags do not produce addresslines when the parent has the address part Given the grid | 1 | | | 5 | | | 8 | 9 | | | 4 | | | 6 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 8 | Outer | (1,5,6,4,1) | And the places | osm | class | type | name | addr+city | geometry | | W1 | highway | primary | Wonderway | Outer | 8,9 | | N1 | amenity | cafe | Bolder | Outer | 9 | When importing Then place_addressline contains exactly | object | address | isaddress | | W1 | R1 | True | When geocoding "Bolder" Then the result set contains | object | display_name | | N1 | Bolder, Wonderway, Outer | Scenario: addr:* tags on outside do not produce addresslines when the parent has the address part Given the grid | 1 | | 2 | | | 5 | | | | | 8 | 9 | | | 4 | | 3 | | | 6 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | And the places | osm | class | type | name | addr+city | geometry | | W1 | highway | primary | Wonderway | Left | 8,9 | | N1 | amenity | cafe | Bolder | Left | 9 | When importing Then place_addressline contains exactly | object | address | isaddress | | W1 | R1 | True | | W1 | R2 | False | When geocoding "Bolder" Then the result set contains | object | display_name | | N1 | Bolder, Wonderway, Left | Scenario: POIs can correct address parts on the fly Given the grid | 1 | | | | 2 | | 5 | | | | | 9 | | 8 | | | 4 | | | | 3 | | 6 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | And the places | osm | class | type | name | geometry | | W1 | highway | primary | Wonderway | 2,3 | | N1 | amenity | cafe | Bolder | 9 | | N2 | amenity | cafe | Leftside | 8 | When importing Then place_addressline contains exactly | object | address | isaddress | | W1 | R1 | False | | W1 | R2 | True | When geocoding "Bolder" Then the result set contains | object | display_name | | N1 | Bolder, Wonderway, Left | When geocoding "Leftside" Then the result set contains | object | display_name | | N2 | Leftside, Wonderway, Right | Scenario: POIs can correct address parts on the fly (with partial unmatching address) Given the grid | 1 | | | | 2 | | 5 | | | | | 9 | | 8 | | | | 10| 11| | | 12| | | 4 | | | | 3 | | 6 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | And the places | osm | class | type | name | geometry | | W1 | highway | primary | Wonderway | 10,11,12 | And the places | osm | class | type | name | addr+suburb | geometry | | N1 | amenity | cafe | Bolder | Boring | 9 | | N2 | amenity | cafe | Leftside | Boring | 8 | When importing Then place_addressline contains exactly | object | address | isaddress | | W1 | R1 | True | | W1 | R2 | False | When geocoding "Bolder" Then the result set contains | object | display_name | | N1 | Bolder, Wonderway, Left | When geocoding "Leftside" Then the result set contains | object | display_name | | N2 | Leftside, Wonderway, Right | Scenario: POIs can correct address parts on the fly (with partial matching address) Given the grid | 1 | | | | 2 | | 5 | | | | | 9 | | 8 | | | | 10| 11| | | 12| | | 4 | | | | 3 | | 6 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | And the places | osm | class | type | name | geometry | | W1 | highway | primary | Wonderway | 10,11,12 | And the places | osm | class | type | name | addr+state | geometry | | N1 | amenity | cafe | Bolder | Left | 9 | | N2 | amenity | cafe | Leftside | Left | 8 | When importing Then place_addressline contains exactly | object | address | isaddress | | W1 | R1 | True | | W1 | R2 | False | When geocoding "Bolder" Then the result set contains | object | display_name | | N1 | Bolder, Wonderway, Left | When geocoding "Leftside" Then the result set contains | object | display_name | | N2 | Leftside, Wonderway, Left | Scenario: addr:* tags always match the closer area Given the grid | 1 | | | | 2 | | 5 | | | | | | | | | | 4 | | | | 3 | | 6 | | | 10| 11| | | | | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | | R2 | boundary | administrative | 8 | Left | (2,3,6,5,2) | And the places | osm | class | type | name | addr+city | geometry | | W1 | highway | primary | Wonderway | Left | 10,11 | When importing Then place_addressline contains exactly | object | address | | W1 | R1 | Scenario: Full name is prefered for unlisted addr:place tags Given the grid | | 1 | 2 | | | 8 | | | 9 | And the places | osm | class | type | name | geometry | | W10 | place | city | Away | (8,1,2,9,8) | And the places | osm | class | type | name | addr+city | geometry | | W1 | highway | residential | Royal Terrace | Gardens | 8,9 | And the places | osm | class | type | housenr | addr+place | geometry | extra+foo | | N1 | place | house | 1 | Royal Terrace Gardens | 1 | bar | And the places | osm | class | type | housenr | addr+street | geometry | | N2 | place | house | 2 | Royal Terrace | 2 | When importing When geocoding "1, Royal Terrace Gardens" Then result 0 contains | object | | N1 | ================================================ FILE: test/bdd/features/db/import/country.feature ================================================ Feature: Country handling Tests for import and use of country information Scenario: Country names from OSM country relations are added Given the places | osm | class | type | admin | name+name:xy | country | geometry | | R1 | boundary | administrative | 2 | Loudou | de | (9 52, 9 53, 10 52, 9 52) | Given the places | osm | class | type | name | geometry | | N1 | place | town | Wenig | country:de | When importing When geocoding "Wenig, Loudou" Then the result set contains | object | display_name | | N1 | Wenig, Deutschland | When geocoding "Wenig" | accept-language | | xy,en | Then the result set contains | object | display_name | | N1 | Wenig, Loudou | Scenario: OSM country relations outside expected boundaries are ignored for naming Given the grid | 1 | | 2 | | 4 | | 3 | Given the places | osm | class | type | admin | name+name:xy | country | geometry | | R1 | boundary | administrative | 2 | Loudou | de | (1,2,3,4,1) | Given the places | osm | class | type | name | geometry | | N1 | place | town | Wenig | country:de | When importing When geocoding "Wenig" | accept-language | | xy,en | Then the result set contains | object | display_name | | N1 | Wenig, Germany | Scenario: Pre-defined country names are used Given the grid with origin CH | 1 | Given the places | osm | class | type | name | geometry | | N1 | place | town | Ingb | 1 | When importing And geocoding "Ingb" | accept-language | | en,de | Then the result set contains | object | display_name | | N1 | Ingb, Switzerland | Scenario: For overlapping countries, pre-defined countries are tie-breakers Given the grid with origin US | 1 | | 2 | | 5 | | | 9 | | 8 | | | 4 | | 3 | | 6 | Given the named places | osm | class | type | admin | country | geometry | | R1 | boundary | administrative | 2 | de | (1,5,6,4,1) | | R2 | boundary | administrative | 2 | us | (1,2,3,4,1) | And the named places | osm | class | type | geometry | | N1 | place | town | 9 | | N2 | place | town | 8 | When importing Then placex contains | object | country_code | | N1 | us | | N2 | de | Scenario: For overlapping countries outside pre-define countries prefer smaller partition Given the grid with origin US | 1 | | 2 | | 5 | | | 9 | | 8 | | | 4 | | 3 | | 6 | Given the named places | osm | class | type | admin | country | geometry | | R1 | boundary | administrative | 2 | ch | (1,5,6,4,1) | | R2 | boundary | administrative | 2 | de | (1,2,3,4,1) | And the named places | osm | class | type | geometry | | N1 | place | town | 9 | | N2 | place | town | 8 | When importing Then placex contains | object | country_code | | N1 | de | | N2 | ch | ================================================ FILE: test/bdd/features/db/import/entrances.feature ================================================ Feature: Entrance nodes are recorded Test that imported entrance nodes are saved Scenario: A building with two entrances Given the grid | 1 | 2 | | 4 | 3 | Given the places | osm | class | type | geometry | extratags | | W1 | building | yes | (1,2,3,4,1) | | And the entrances | osm | type | geometry | extratags | | N1 | main | 1 | 'wheelchair': 'yes' | | N2 | yes | 3 | | And the ways | id | nodes | | 1 | 1,2,3,4,1 | When importing Then placex contains exactly | object | place_id | | W1 | 1 | Then placex_entrance contains exactly | place_id | osm_id | type | location!wkt | extratags | | 1 | 1 | main | 1 | {'wheelchair': 'yes'} | | 1 | 2 | yes | 3 | {} | ================================================ FILE: test/bdd/features/db/import/interpolation.feature ================================================ Feature: Import of address interpolations Tests that interpolated addresses are added correctly Scenario: Simple even interpolation line with two points and no street nearby Given the grid with origin 1,1 | 1 | | 9 | | 2 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,2 | 1,2 | When importing Then W1 expands to no interpolation Scenario: Simple even interpolation line with two points Given the grid with origin 1,1 | 1 | | 9 | | 2 | | 4 | | | | 5 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,2 | 1,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 4 | 9 | Scenario: Backwards even two point interpolation line Given the grid with origin 1,1 | 1 | 8 | 9 | 2 | | 4 | | | 5 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 8 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 2,1 | 2,1 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 6 | 8,9 | Scenario: Simple odd two point interpolation Given the grid with origin 1,1 | 1 | 8 | | | 9 | 2 | | 4 | | | | 5 | | Given the places | osm | class | type | housenr | | N1 | place | house | 1 | | N2 | place | house | 11 | And the interpolations | osm | type | geometry | nodes | | W1 | odd | 1,2 | 1,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 3 | 9 | 8,9 | Scenario: Simple all two point interpolation Given the grid with origin 1,1 | 1 | 8 | 9 | 2 | | 4 | | | 5 | Given the places | osm | class | type | housenr | | N1 | place | house | 1 | | N2 | place | house | 4 | And the interpolations | osm | type | geometry | nodes | | W1 | all | 1,2 | 1,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 2 | 3 | 8,9 | Scenario: Even two point interpolation line with intermediate empty node Given the grid | 1 | 8 | | 3 | 9 | 2 | | 4 | | | | 5 | | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 12 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,3,2 | 1,3,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 10 | 8,3,9 | Scenario: Even two point interpolation line with intermediate duplicated empty node Given the grid | 4 | | | | 5 | | 1 | 8 | 3 | 9 | 2 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 10 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,3,2 | 1,3,3,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 8 | 8,3,9 | Scenario: Simple even three point interpolation line Given the grid | 4 | | | | | | 5 | | 1 | 8 | | 9 | 3 | 7 | 2 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 14 | | N3 | place | house | 10 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,3,2 | 1,3,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 8 | 8,9 | | 12 | 12 | 7 | Scenario: Simple even four point interpolation line Given the grid | 1 | 10 | | 11 | 3 | | | | | | 12| | | | 4 | 13 | 2 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 14 | | N3 | place | house | 10 | | N4 | place | house | 18 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,3,2,4 | 1,3,2,4 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 1,3,2,4 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 8 | 10,11 | | 12 | 12 | 12 | | 16 | 16 | 13 | Scenario: Reverse simple even three point interpolation line Given the grid | 1 | 8 | | 9 | 3 | 7 | 2 | | 4 | | | | | | 5 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 14 | | N3 | place | house | 10 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 2,3,1 | 2,3,1 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 8 | 8,9 | | 12 | 12 | 7 | Scenario: Even three point interpolation line with odd center point Given the grid | 1 | | 10 | | 11 | 3 | 2 | | 4 | | | | | | 5 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 8 | | N3 | place | house | 7 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,3,2 | 1,3,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 6 | 10,11 | Scenario: Interpolation line with self-intersecting way Given the grid | 1 | 9 | 2 | | | | 8 | | | | 3 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | | N3 | place | house | 10 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,2,3,2 | 1,2,3,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 1,2,3 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 4 | 9 | | 8 | 8 | 8 | | 8 | 8 | 8 | Scenario: Interpolation line with self-intersecting way II Given the grid | 1 | 9 | 2 | | | | 3 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,2,3,2 | 1,2,3,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 1,2,3 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 4 | 9 | Scenario: addr:street on interpolation way Given the grid | | 1 | | 2 | | | 10 | | | | 11 | | 20 | | | | 21 | And the places | osm | class | type | housenr | geometry | | N1 | place | house | 2 | 1 | | N2 | place | house | 6 | 2 | | N3 | place | house | 12 | 1 | | N4 | place | house | 16 | 2 | And the interpolations | osm | type | street | nodes | geometry | nodes | | W10 | even | | 1,2 | 1,2 | 1,2 | | W11 | even | Cloud Street | 3,4 | 1,2 | 3,4 | And the places | osm | class | type | name | geometry | | W2 | highway | tertiary | Sun Way | 10,11 | | W3 | highway | tertiary | Cloud Street | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | | N3 | W3 | | N4 | W3 | Then W10 expands to interpolation | parent_place_id | start | end | | W2 | 4 | 4 | Then W11 expands to interpolation | parent_place_id | start | end | | W3 | 14 | 14 | When geocoding "16 Cloud Street" Then result 0 contains | object | | N4 | When geocoding "14 Cloud Street" Then result 0 contains | object | | W11 | Scenario: addr:street on housenumber way Given the grid | | 1 | | 2 | | | 10 | | | | 11 | | 20 | | | | 21 | And the places | osm | class | type | housenr | street | geometry | | N1 | place | house | 2 | | 1 | | N2 | place | house | 6 | | 2 | | N3 | place | house | 12 | Cloud Street | 1 | | N4 | place | house | 16 | Cloud Street | 2 | And the interpolations | osm | type | geometry | nodes | | W10 | even | 1,2 | 1,2 | | W11 | even | 1,2 | 3,4 | And the places | osm | class | type | name | geometry | | W2 | highway | tertiary | Sun Way | 10,11 | | W3 | highway | tertiary | Cloud Street | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | | N3 | W3 | | N4 | W3 | Then W10 expands to interpolation | parent_place_id | start | end | | W2 | 4 | 4 | Then W11 expands to interpolation | parent_place_id | start | end | | W3 | 14 | 14 | When geocoding "16 Cloud Street" Then result 0 contains | object | | N4 | When geocoding "14 Cloud Street" Then result 0 contains | object | | W11 | Scenario: Geometry of points and way don't match (github #253) Given the places | osm | class | type | housenr | geometry | | N1 | place | house | 10 | 144.9632341 -37.76163 | | N2 | place | house | 6 | 144.9630541 -37.7628174 | | N3 | shop | supermarket | 2 | 144.9629794 -37.7630755 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 144.9632341 -37.76163,144.9630541 -37.7628172,144.9629794 -37.7630755 | 1,2,3 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 144.9632341 -37.76163,144.9629794 -37.7630755 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 4 | 144.96301672 -37.76294644 | | 8 | 8 | 144.96314407 -37.762223692 | Scenario: Place with missing address information Given the grid | 1 | | 2 | | | 3 | | 4 | | | | | 5 | And the places | osm | class | type | housenr | | N1 | place | house | 23 | | N2 | amenity | school | | | N3 | place | house | 29 | And the interpolations | osm | type | geometry | nodes | | W1 | odd | 1,2,3 | 1,2,3 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 25 | 27 | 0.0000166 0,0.00002 0,0.0000333 0 | Scenario: Ways without node entries are ignored Given the interpolations | osm | type | geometry | nodes | | W1 | even | 1 1, 1 1.001 | 34,45 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 1 1, 1 1.001 | When importing Then W1 expands to no interpolation Scenario: Ways with nodes without housenumbers are ignored Given the grid | 1 | | 2 | | 4 | | 5 | Given the places | osm | class | type | | N1 | place | house | | N2 | place | house | Given the interpolations | osm | type | geometry | nodes | | W1 | even | 1,2 | 1,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to no interpolation Scenario: Two point interpolation starting at 0 Given the grid with origin 1,1 | 1 | 10 | | | 11 | 2 | | 4 | | | | | 5 | Given the places | osm | class | type | housenr | | N1 | place | house | 0 | | N2 | place | house | 10 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,2 | 1,2 | And the places | osm | class | type | name | geometry | | W10 | highway | residential | London Road |4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 2 | 8 | 10,11 | When reverse geocoding 1,1 Then the result contains | object | type | display_name | | N1 | house | 0, London Road | Scenario: Parenting of interpolation with additional tags Given the grid | 1 | | | | | | | | | | | | | | | 8 | | | 9 | | | | | | | | | | 2 | | | | | 3 | Given the places | osm | class | type | housenr | addr+street | | N8 | place | house | 10 | Horiz St | | N9 | place | house | 16 | Horiz St | And the places | osm | class | type | name | geometry | | W1 | highway | residential | Vert St | 1,2 | | W2 | highway | residential | Horiz St | 2,3 | And the interpolations | osm | type | addr+inclusion | geometry | nodes | | W10 | even | actual | 8,9 | 8,9 | When importing Then placex contains | object | parent_place_id | | N8 | W2 | | N9 | W2 | And W10 expands to interpolation | start | end | parent_place_id | | 12 | 14 | W2 | Scenario Outline: Bad interpolation values are ignored Given the grid with origin 1,1 | 1 | | 9 | | 2 | | 4 | | | | 5 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And the interpolations | osm | type | geometry | nodes | | W1 | | 1,2 | 1,2 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to no interpolation Examples: | value | | foo | | x | | 12-2 | Scenario: Interpolation line where points have been moved (Github #3022) Given the 0.00001 grid | 1 | | | | | | | | 2 | 3 | 9 | | | | | | | | 4 | Given the places | osm | class | type | housenr | geometry | | N1 | place | house | 2 | 1 | | N2 | place | house | 18 | 3 | | N3 | place | house | 24 | 9 | | N4 | place | house | 42 | 4 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,2,3,4 | 1,2,3,4 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 1,4 | When importing Then W1 expands to interpolation | start | end | | 4 | 16 | | 20 | 22 | | 26 | 40 | Scenario: Interpolation line with duplicated points Given the grid | 7 | 10 | 8 | 11 | 9 | | 4 | | | | 5 | Given the places | osm | class | type | housenr | geometry | | N1 | place | house | 2 | 7 | | N2 | place | house | 6 | 8 | | N3 | place | house | 10 | 8 | | N4 | place | house | 14 | 9 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 7,8,8,9 | 1,2,3,4 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 4,5 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 4 | 10 | | 12 | 12 | 11 | Scenario: Interpolaton line with broken way geometry (Github #2986) Given the grid | 1 | 8 | 10 | 11 | 9 | 2 | 3 | 4 | Given the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 8 | | N3 | place | house | 12 | | N4 | place | house | 14 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 8,9 | 1,8,9,2,3,4 | And the named places | osm | class | type | geometry | | W10 | highway | residential | 1,4 | When importing Then W1 expands to interpolation | start | end | geometry | | 4 | 6 | 10,11 | ================================================ FILE: test/bdd/features/db/import/linking.feature ================================================ Feature: Linking of places Tests for correctly determining linked places Scenario: Only address-describing places can be linked Given the grid | 1 | | | | 2 | | | | 9 | | | | 4 | | | | 3 | And the places | osm | class | type | name | geometry | | R13 | landuse | forest | Garbo | (1,2,3,4,1) | | N256 | natural | peak | Garbo | 9 | When importing Then placex contains | object | linked_place_id | | R13 | - | | N256 | - | Scenario: Waterways are linked when in waterway relations Given the grid | 1 | | | | 3 | 4 | | | | 6 | | | | 2 | | | 10 | | 5 | | | | | | | | | 11 | | | | | And the places | osm | class | type | name | geometry | | W1 | waterway | river | Rhein | 1,2,3 | | W2 | waterway | river | Rhein | 3,4,5 | | R13 | waterway | river | Rhein | 1,2,3,4,5,6 | | R23 | waterway | river | Limmat| 4,10,11 | And the relations | id | members | tags+type | | 13 | R23:tributary,W1,W2:main_stream | waterway | When importing Then placex contains | object | linked_place_id | | W1 | R13 | | W2 | R13 | | R13 | - | | R23 | - | When geocoding "rhein" Then the result set contains | object | | R13 | Scenario: Relations are not linked when in waterway relations Given the grid | 1 | | | | 3 | 4 | | | | 6 | | | | 2 | | | 10 | | 5 | | | | | | | | | 11 | | | | | And the places | osm | class | type | name | geometry | | W1 | waterway | stream | Rhein | 1,2,3,4 | | W2 | waterway | river | Rhein | 4,5,6 | | R1 | waterway | river | Rhein | 1,2,3,4 | | R2 | waterway | river | Limmat| 4,10,11 | And the relations | id | members | tags+type | | 1 | R2 | waterway | When importing Then placex contains | object | linked_place_id | | W1 | - | | W2 | - | | R1 | - | | R2 | - | When geocoding "rhein" Then result 0 contains | object | | R1 | And result 1 contains | object | | W2 | Scenario: Empty waterway relations are handled correctly Given the grid | 1 | | | | 3 | And the places | osm | class | type | name | geometry | | R1 | waterway | river | Rhein | 1,3 | And the relations | id | members | tags+type | | 1 | | waterway | When importing Then placex contains | object | linked_place_id | | R1 | - | Scenario: Waterways are not linked when the way type is not a river feature Given the grid | 1 | | 2 | | | | | | 3 | | 4 | And the places | osm | class | type | name | geometry | | W1 | waterway | lock | Rhein | 3,4 | | R1 | landuse | meadow | Rhein | (3,1,2,4,3) | And the relations | id | members | tags+type | | 1 | W1,W2 | multipolygon | When importing Then placex contains | object | linked_place_id | | W1 | - | | R1 | - | Scenario: Side streams are linked only when they have the same name Given the grid | | | | | 8 | | | | | 1 | | 2 | 3 | | 4 | 5 | 6| | | | | | | 9 | | | And the places | osm | class | type | name | geometry | | W1 | waterway | river | Rhein2 | 2,8,4 | | W2 | waterway | river | Rhein | 3,9,5 | | R1 | waterway | river | Rhein | 1,2,3,4,5,6 | And the relations | id | members | tags+type | | 1 | W1:side_stream,W2:side_stream,W3 | waterway | When importing Then placex contains | object | linked_place_id | | W1 | - | | W2 | R1 | When geocoding "rhein2" Then the result set contains | object | | W1 | # github #573 Scenario: Boundaries should only be linked to places Given the 0.05 grid | 1 | | 2 | | | 9 | | | 4 | | 3 | Given the named places | osm | class | type | extra+wikidata | admin | geometry | | R1 | boundary | administrative | 34 | 8 | (1,2,3,4,1) | And the named places | osm | class | type | | N9 | natural | island | | N9 | place | city | And the relations | id | members | | 1 | N9:label | When importing Then placex contains | object | linked_place_id | | N9:natural | - | | N9:place | R1 | Scenario: Nodes with 'role' label are always linked Given the 0.05 grid | 1 | | 2 | | | 9 | | | 4 | | 3 | Given the places | osm | class | type | admin | name | geometry | | R13 | boundary | administrative | 6 | Garbo | (1,2,3,4,1) | | N2 | place | hamlet | 15 | Vario | 9 | And the relations | id | members | tags+type | | 13 | N2:label | boundary | When importing Then placex contains | object | linked_place_id | | N2 | R13 | And placex contains | object | centroid!wkt | name+name | extratags+linked_place | | R13 | 9 | Garbo | hamlet | Scenario: Boundaries with place tags are linked against places with same type Given the 0.01 grid | 1 | | 2 | | | 9 | | | 4 | | 3 | Given the places | osm | class | type | admin | name | extra+place | geometry | | R13 | boundary | administrative | 4 | Berlin | city | (1,2,3,4,1) | And the places | osm | class | type | name | geometry | | N2 | place | city | Berlin | 9 | When importing Then placex contains | object | linked_place_id | | N2 | R13 | And placex contains | object | rank_address | | R13 | 16 | When geocoding "" | city | | Berlin | Then result 0 contains | object | | R13 | When geocoding "" | state | | Berlin | Then result 0 contains | object | | R13 | Scenario: Boundaries without place tags only link against same admin level Given the 0.05 grid | 1 | | 2 | | | 9 | | | 4 | | 3 | Given the places | osm | class | type | admin | name | geometry | | R13 | boundary | administrative | 4 | Berlin | (1,2,3,4,1) | And the places | osm | class | type | name | geometry | | N2 | place | city | Berlin | 9 | When importing Then placex contains | object | linked_place_id | | N2 | - | And placex contains | object | rank_address | | R13 | 8 | When geocoding "" | state | | Berlin | Then result 0 contains | object | | R13 | When geocoding "" | city | | Berlin | Then result 0 contains | object | | N2 | # github #1352 Scenario: Do not use linked centroid when it is outside the area Given the 0.05 grid | 1 | | 2 | | | | | | 9 | | 4 | | 3 | | Given the named places | osm | class | type | admin | geometry | | R13 | boundary | administrative | 4 | (1,2,3,4,1) | And the named places | osm | class | type | geometry | | N2 | place | city | 9 | And the relations | id | members | tags+type | | 13 | N2:label | boundary | When importing Then placex contains | object | linked_place_id | | N2 | R13 | And placex contains | object | centroid!in_box | | R13 | 0,0,0.1,0.1 | Scenario: Place nodes can only be linked once Given the 0.02 grid | 1 | | 2 | | 5 | | | 9 | | | | | 4 | | 3 | | 6 | Given the named places | osm | class | type | extra+wikidata | geometry | | N2 | place | city | Q1234 | 9 | And the named places | osm | class | type | extra+wikidata | admin | geometry | | R1 | boundary | administrative | Q1234 | 8 | (1,2,5,6,3,4,1) | | R2 | boundary | administrative | Q1234 | 9 | (1,2,3,4,1) | When importing Then placex contains | object | linked_place_id | | N2 | R1 | And placex contains | object | extratags!dict | | R1 | 'linked_place' : 'city', 'wikidata': 'Q1234' | | R2 | 'wikidata': 'Q1234' | Scenario: Boundaries without names inherit names from linked places Given the 0.05 grid | 1 | | 2 | | | 9 | | | 4 | | 3 | Given the places | osm | class | type | extra+wikidata | admin | geometry | | R1 | boundary | administrative | 34 | 8 | (1,2,3,4,1) | And the places | osm | class | type | name+name | | N9 | place | city | LabelPlace | And the relations | id | members | | 1 | N9:label | When importing Then placex contains | object | name+_place_name | | R1 | LabelPlace | Scenario: Boundaries and places do not link by name when wikidata mismatches Given the 0.05 grid | 1 | | 2 | | | 9 | | | 4 | | 3 | Given the places | osm | class | type | extra+wikidata | name | admin | geometry | | R1 | boundary | administrative | Q34 | Foo | 8 | (1,2,3,4,1) | And the places | osm | class | type | extra+wikidata | name | | N9 | place | city | Q35 | Foo | When importing Then placex contains | object | linked_place_id | | N9 | - | Scenario: Boundaries and places link by name when wikidata matches Given the 0.05 grid | 1 | | 2 | | | 9 | | | 4 | | 3 | Given the places | osm | class | type | extra+wikidata | name | admin | geometry | | R1 | boundary | administrative | Q34 | Foo | 8 | (1,2,3,4,1) | And the places | osm | class | type | extra+wikidata | name | | N9 | place | city | Q34 | Foo | When importing Then placex contains | object | linked_place_id | | N9 | R1 | @skip Scenario: Linked places expand default language names Given the grid | 1 | | 2 | | | 9 | | | 4 | | 3 | Given the places | osm | class | type | name+name | geometry | | N9 | place | city | Popayán | 9 | Given the places | osm | class | type | name+name | geometry | admin | | R1 | boundary | administrative | Perímetro Urbano Popayán | (1,2,3,4,1) | 8 | And the relations | id | members | | 1 | N9:label | When importing Then placex contains | object | linked_place_id | | N9:place | R1 | | R1:boundary | - | Then placex contains | object | name+_place_name | name+_place_name:es | | R1 | Popayán | Popayán | ================================================ FILE: test/bdd/features/db/import/naming.feature ================================================ Feature: Import and search of names Tests all naming related import issues Scenario: No copying name tag if only one name Given the places | osm | class | type | name+name | geometry | | N1 | place | locality | german | country:de | When importing Then placex contains | object | country_code | name+name | | N1 | de | german | Scenario: Copying name tag to default language if it does not exist Given the places | osm | class | type | name+name | name+name:fi | geometry | | N1 | place | locality | german | finnish | country:de | When importing Then placex contains | object | country_code | name+name | name+name:fi | name+name:de | | N1 | de | german | finnish | german | Scenario: Copying default language name tag to name if it does not exist Given the places | osm | class | type | name+name:de | name+name:fi | geometry | | N1 | place | locality | german | finnish | country:de | When importing Then placex contains | object | country_code | name+name | name+name:fi | name+name:de | | N1 | de | german | finnish | german | Scenario: Do not overwrite default language with name tag Given the places | osm | class | type | name+name | name+name:fi | name+name:de | geometry | | N1 | place | locality | german | finnish | local | country:de | When importing Then placex contains | object | country_code | name+name | name+name:fi | name+name:de | | N1 | de | german | finnish | local | Scenario Outline: Names in any script can be found Given the places | osm | class | type | name+name | | N1 | place | hamlet | | When importing And geocoding "" Then the result set contains | object | | N1 | Examples: | name | | Berlin | | 北京 | | Вологда | | Αθήνα | | القاهرة | | រាជធានីភ្នំពេញ | | 東京都 | | ပုဗ္ဗသီရိ | Scenario: German umlauts can be found when expanded Given the places | osm | class | type | name+name:de | | N1 | place | city | Münster | | N2 | place | city | Köln | | N3 | place | city | Gräfenroda | When importing When geocoding "münster" Then the result set contains | object | | N1 | When geocoding "muenster" Then the result set contains | object | | N1 | When geocoding "munster" Then the result set contains | object | | N1 | When geocoding "Köln" Then the result set contains | object | | N2 | When geocoding "Koeln" Then the result set contains | object | | N2 | When geocoding "Koln" Then the result set contains | object | | N2 | When geocoding "gräfenroda" Then the result set contains | object | | N3 | When geocoding "graefenroda" Then the result set contains | object | | N3 | When geocoding "grafenroda" Then the result set contains | object | | N3 | ================================================ FILE: test/bdd/features/db/import/parenting.feature ================================================ Feature: Parenting of objects Tests that the correct parent is chosen Scenario: Address inherits postcode from its street unless it has a postcode Given the grid with origin DE | 10 | | | | | 11 | | | | | | | | | | 1 | | 2 | | | And the places | osm | class | type | housenr | | N1 | place | house | 4 | And the places | osm | class | type | housenr | postcode | | N2 | place | house | 5 | 99999 | And the places | osm | class | type | name | postcode | geometry | | W1 | highway | residential | galoo | 12345 | 10,11 | When importing Then placex contains | object | parent_place_id | | N1 | W1 | | N2 | W1 | When geocoding "4 galoo" Then result 0 contains | object | display_name | | N1 | 4, galoo, 12345, Deutschland | When geocoding "5 galoo" Then result 0 contains | object | display_name | | N2 | 5, galoo, 99999, Deutschland | Scenario: Address without tags, closest street Given the grid | 10 | | | | | 11 | | | 1 | 2 | | | | | | | | 3 | 4 | | | 20 | | | | | 21 | And the places | osm | class | type | | N1 | place | house | | N2 | place | house | | N3 | place | house | | N4 | place | house | And the named places | osm | class | type | geometry | | W1 | highway | residential | 10,11 | | W2 | highway | residential | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W1 | | N2 | W1 | | N3 | W2 | | N4 | W2 | Scenario: Address without tags avoids unnamed streets Given the grid | 10 | | | | | 11 | | | 1 | 2 | | | | | | | | 3 | 4 | | | 20 | | | | | 21 | And the places | osm | class | type | | N1 | place | house | | N2 | place | house | | N3 | place | house | | N4 | place | house | And the places | osm | class | type | geometry | | W1 | highway | residential | 10,11 | And the named places | osm | class | type | geometry | | W2 | highway | residential | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | | N3 | W2 | | N4 | W2 | Scenario: addr:street tag parents to appropriately named street Given the grid | 10 | | | | | 11 | | | 1 | 2 | | | | | | | | 3 | 4 | | | 20 | | | | | 21 | And the places | osm | class | type | street| | N1 | place | house | south | | N2 | place | house | north | | N3 | place | house | south | | N4 | place | house | north | And the places | osm | class | type | name | geometry | | W1 | highway | residential | north | 10,11 | | W2 | highway | residential | south | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W1 | | N3 | W2 | | N4 | W1 | Scenario: addr:street tag parents to appropriately named street, locale names Given the grid | 10 | | | | | 11 | | | 1 | 2 | | | | | | | | 3 | 4 | | | 20 | | | | | 21 | And the places | osm | class | type | street| addr+street:de | | N1 | place | house | south | Süd | | N2 | place | house | north | Nord | | N3 | place | house | south | Süd | | N4 | place | house | north | Nord | And the places | osm | class | type | name | geometry | | W1 | highway | residential | Nord | 10,11 | | W2 | highway | residential | Süd | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W1 | | N3 | W2 | | N4 | W1 | Scenario: addr:street tag parents to appropriately named street with abbreviation Given the grid | 10 | | | | | 11 | | | 1 | 2 | | | | | | | | 3 | 4 | | | 20 | | | | | 21 | And the places | osm | class | type | street | | N1 | place | house | south st | | N2 | place | house | north st | | N3 | place | house | south st | | N4 | place | house | north st | And the places | osm | class | type | name+name:en | geometry | | W1 | highway | residential | north street | 10,11 | | W2 | highway | residential | south street | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W1 | | N3 | W2 | | N4 | W1 | Scenario: addr:street tag parents to next named street Given the grid | 10 | | | | | 11 | | | 1 | 2 | | | | | | | | 3 | 4 | | | 20 | | | | | 21 | And the places | osm | class | type | street | | N1 | place | house | abcdef | | N2 | place | house | abcdef | | N3 | place | house | abcdef | | N4 | place | house | abcdef | And the places | osm | class | type | name | geometry | | W1 | highway | residential | abcdef | 10,11 | | W2 | highway | residential | abcdef | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W1 | | N2 | W1 | | N3 | W2 | | N4 | W2 | Scenario: addr:street tag without appropriately named street Given the grid | 10 | | | | | 11 | | | 1 | | | | | | | | | 3 | | | | 20 | | | | | 21 | And the places | osm | class | type | street | | N1 | place | house | abcdef | | N3 | place | house | abcdef | And the places | osm | class | type | name | geometry | | W1 | highway | residential | abcde | 10,11 | | W2 | highway | residential | abcde | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W1 | | N3 | W2 | Scenario: addr:place address Given the grid | 10 | | | | | | 1 | | 2 | | 11 | | | | And the places | osm | class | type | addr_place | | N1 | place | house | myhamlet | And the places | osm | class | type | name | geometry | | N2 | place | hamlet | myhamlet | 2 | | W1 | highway | residential | myhamlet | 10,11 | When importing Then placex contains | object | parent_place_id | | N1 | N2 | Scenario: addr:street is preferred over addr:place Given the grid | 10 | | | | | | | 1 | 2 | | 11 | | | | And the places | osm | class | type | addr_place | street | | N1 | place | house | myhamlet | mystreet| And the places | osm | class | type | name | geometry | | N2 | place | hamlet | myhamlet | 2 | | W1 | highway | residential | mystreet | 10,11 | When importing Then placex contains | object | parent_place_id | | N1 | W1 | Scenario: House members in associatedStreet relation get correct parent Given the grid | 10 | | | | | 11 | | | 2 | | 3 | | | | | | | | | | | 12 | 1 | | | | | And the places | osm | class | type | | N1 | place | house | | N2 | place | house | | N3 | place | house | And the places | osm | class | type | name | geometry | | W1 | highway | residential | foo | 10,11 | | W2 | highway | service | bar | 10,12 | And the relations | id | members | tags+type | | 1 | W1:street,N1:house,N2:house,N3:house | associatedStreet | When importing Then placex contains | object | parent_place_id | | N1 | W1 | | N2 | W1 | | N3 | W1 | Scenario: Avoid unnamed streets in simple associated street relation Given the grid | 10 | | | | | 11 | | | 2 | | 3 | | | | | | | | | | | 12 | 1 | | | | | And the places | osm | class | type | | N1 | place | house | | N2 | place | house | | N3 | place | house | And the places | osm | class | type | geometry | | W2 | highway | residential | 10,12 | And the named places | osm | class | type | geometry | | W1 | highway | residential | 10,11 | And the relations | id | members | tags+type | | 1 | N1,N2,N3,W2:street,W1:street | associatedStreet | When importing Then placex contains | object | parent_place_id | | N1 | W1 | | N2 | W1 | | N3 | W1 | Scenario: Associated street relation overrides addr:street Given the grid | 10 | | | | 11 | | | | | | | | | | 1 | | | | | 20 | | 21 | | And the places | osm | class | type | street | | N1 | place | house | bar | And the places | osm | class | type | name | geometry | | W1 | highway | residential | foo | 10,11 | | W2 | highway | residential | bar | 20,21 | And the relations | id | members | tags+type | | 1 | W1:street,N1:house | associatedStreet | When importing Then placex contains | object | parent_place_id | | N1 | W1 | Scenario: Building without tags, closest street from center point Given the grid | 10 | | | | 11 | | | | 1 | 2 | | | 12 | | 4 | 3 | | And the named places | osm | class | type | geometry | | W1 | building | yes | (1,2,3,4,1) | | W2 | highway | primary | 10,11 | | W3 | highway | residential | 10,12 | When importing Then placex contains | object | parent_place_id | | W1 | W2 | Scenario: Building with addr:street tags Given the grid | 10 | | | | 11 | | | | 1 | 2 | | | 12 | | 4 | 3 | | And the named places | osm | class | type | street | geometry | | W1 | building | yes | foo | (1,2,3,4,1) | And the places | osm | class | type | name | geometry | | W2 | highway | primary | bar | 10,11 | | W3 | highway | residential | foo | 10,12 | When importing Then placex contains | object | parent_place_id | | W1 | W3 | Scenario: Building with addr:place tags Given the grid | 10 | | | | | | | 1 | 2 | | 9 | | 11 | 4 | 3 | | | And the places | osm | class | type | name | geometry | | N9 | place | village | bar | 9 | | W2 | highway | primary | bar | 10,11 | And the named places | osm | class | type | addr_place | geometry | | W1 | building | yes | bar | (1,2,3,4,1) | When importing Then placex contains | object | parent_place_id | | W1 | N9 | Scenario: Building in associated street relation Given the grid | 10 | | | | 11 | | | | 1 | 2 | | | 12 | | 4 | 3 | | And the named places | osm | class | type | geometry | | W1 | building | yes | (1,2,3,4,1) | And the places | osm | class | type | name | geometry | | W2 | highway | primary | bar | 10,11 | | W3 | highway | residential | foo | 10,12 | And the relations | id | members | tags+type | | 1 | W1:house,W3:street | associatedStreet | When importing Then placex contains | object | parent_place_id | | W1 | W3 | Scenario: Building in associated street relation overrides addr:street Given the grid | 10 | | | | 11 | | | | 1 | 2 | | | 12 | | 4 | 3 | | And the named places | osm | class | type | street | geometry | | W1 | building | yes | foo | (1,2,3,4,1) | And the places | osm | class | type | name | geometry | | W2 | highway | primary | bar | 10,11 | | W3 | highway | residential | foo | 10,12 | And the relations | id | members | tags+type | | 1 | W1:house,W2:street | associatedStreet | When importing Then placex contains | object | parent_place_id | | W1 | W2 | Scenario: Wrong member in associated street relation is ignored Given the grid | 10 | | | | | | | 11 | | | 1 | | 3 | 4 | | | | | | | | 6 | 5 | | | | And the named places | osm | class | type | geometry | | N1 | place | house | 11 | And the named places | osm | class | type | street | geometry | | W1 | building | yes | foo | (3,4,5,6,3) | And the places | osm | class | type | name | geometry | | W3 | highway | residential | foo | 10,11 | And the relations | id | members | tags+type | | 1 | N1:house,W1:street,W3:street | associatedStreet | When importing Then placex contains | object | parent_place_id | | N1 | W3 | Scenario: street member in associatedStreet relation can be a relation Given the grid | 1 | | | 2 | | 3 | | | 4 | | | | | | | | 9 | | | | 5 | | | 6 | And the places | osm | class | type | housenr | geometry | | N9 | place | house | 34 | 9 | And the named places | osm | class | type | name | geometry | | R14 | highway | pedestrian | Right St | (1,2,4,3,1) | | W14 | highway | pedestrian | Left St | 5,6 | And the relations | id | members | tags+type | | 1 | N9:house,R14:street | associatedStreet | When importing Then placex contains | object | parent_place_id | | N9 | R14 | Scenario: Choose closest street in associatedStreet relation Given the grid | 1 | | | | 3 | | 10 | | 11 | | 12 | And the places | osm | class | type | housenr | geometry | | N1 | place | house | 1 | 1 | | N3 | place | house | 3 | 3 | And the named places | osm | class | type | geometry | | W100 | highway | residential | 10,11 | | W101 | highway | residential | 11,12 | And the relations | id | members | tags+type | | 1 | N1:house,N3:house,W100:street,W101:street | associatedStreet | When importing Then placex contains | object | parent_place_id | | N1 | W100 | | N3 | W101 | Scenario: POIs in building inherit address Given the grid | 10 | | | | | | 11 | | | | 5 | 2 | 6 | | | | | | 3 | 1 | | | | | 12 | | 8 | | 7 | | | And the named places | osm | class | type | | N1 | amenity | bank | | N2 | shop | bakery | | N3 | shop | supermarket| And the places | osm | class | type | street | housenr | geometry | | W1 | building | yes | foo | 3 | (5,6,7,8,5) | And the places | osm | class | type | name | geometry | | W2 | highway | primary | bar | 10,11 | | W3 | highway | residential | foo | 10,12 | When importing Then placex contains | object | parent_place_id | housenumber | | W1 | W3 | 3 | | N1 | W3 | 3 | | N2 | W3 | 3 | | N3 | W3 | 3 | When geocoding "3, foo" Then the result set contains | address+house_number | | 3 | Scenario: POIs don't inherit from streets Given the grid | 10 | | | | 11 | | | 5 | 1 | 6 | | | | 8 | | 7 | | And the named places | osm | class | type | | N1 | amenity | bank | And the places | osm | class | type | name | street | housenr | geometry | | W1 | highway | path | bar | foo | 3 | (5,6,7,8,5) | And the places | osm | class | type | name | geometry | | W3 | highway | residential | foo | 10,11 | When importing Then placex contains | object | parent_place_id | housenumber | | N1 | W1 | None | Scenario: POIs with own address do not inherit building address Given the grid | 10 | | | | | | 11 | | | | 6 | 2 | 7 | | | | | | 3 | 1 | | 5 | 4 | | 12 | | 9 | | 8 | | | And the named places | osm | class | type | street | | N1 | amenity | bank | bar | And the named places | osm | class | type | housenr | | N2 | shop | bakery | 4 | And the named places | osm | class | type | addr_place | | N3 | shop | supermarket| nowhere | And the places | osm | class | type | name | | N4 | place | isolated_dwelling | theplace | | N5 | place | isolated_dwelling | nowhere | And the places | osm | class | type | addr_place | housenr | geometry | | W1 | building | yes | theplace | 3 | (6,7,8,9,6) | And the places | osm | class | type | name | geometry | | W2 | highway | primary | bar | 10,11 | | W3 | highway | residential | foo | 10,12 | When importing Then placex contains | object | parent_place_id | housenumber | | W1 | N4 | 3 | | N1 | W2 | None | | N2 | W2 | 4 | | N3 | N5 | None | Scenario: POIs parent a road if they are attached to it Given the grid | | 10 | | | 20 | 1 | 21 | | | 11 | | And the named places | osm | class | type | | N1 | highway | bus_stop | And the places | osm | class | type | name | geometry | | W1 | highway | secondary | North St | 10,11 | | W2 | highway | unclassified | South St | 20,1,21 | And the ways | id | nodes | | 1 | 10,11 | | 2 | 20,1,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | Scenario: POIs do not parent non-roads they are attached to Given the grid | 10 | | 1 | | 11 | | 30 | | 14 | | | | 15 | | | | 13 | | 2 | | 12 | | 31 | And the named places | osm | class | type | street | | N1 | highway | bus_stop | North St | | N2 | highway | bus_stop | South St | And the places | osm | class | type | name | geometry | | W1 | landuse | residential | North St | (14,15,12,2,13,14) | | W2 | waterway| river | South St | 10,1,11 | | W3 | highway | residential | foo | 30,31 | And the ways | id | nodes | | 1 | 10,11,12,2,13,10 | | 2 | 10,1,11 | When importing Then placex contains | object | parent_place_id | | N1 | W3 | | N2 | W3 | Scenario: POIs on building outlines inherit associated street relation Given the grid | 10 | | | | 11 | | | 5 | 1 | 6 | | | 12 | 8 | | 7 | | And the named places | osm | class | type | geometry | | N1 | place | house | 1 | | W1 | building | yes | (5,1,6,7,8,5)| And the places | osm | class | type | name | geometry | | W2 | highway | primary | bar | 10,11 | | W3 | highway | residential | foo | 10,12 | And the relations | id | members | tags+type | | 1 | W1:house,W3:street | associatedStreet | And the ways | id | nodes | | 1 | 5,1,6,7,8,5 | When importing Then placex contains | object | parent_place_id | | N1 | W3 | # github #1056 Scenario: Full names should be preferably matched for nearest road Given the grid | 1 | | 2 | 5 | | | | | | | 3 | | | 4 | | | 10| | | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Via Cavassico superiore | 1, 2 | | W3 | highway | residential | Via Cavassico superiore | 2, 5 | | W2 | highway | primary | Via Frazione Cavassico | 3, 4 | And the named places | osm | class | type | addr+street | | N10 | shop | yes | Via Cavassico superiore | When importing Then placex contains | object | parent_place_id | | N10 | W1 | Scenario: place=square may be parented via addr:place Given the grid | | | 9 | | | | | 5 | | 6 | | | | 8 | | 7 | | And the places | osm | class | type | name+name | geometry | | W2 | place | square | Foo pl | (5, 6, 7, 8, 5) | And the places | osm | class | type | name+name | housenr | addr_place | geometry | | N10 | shop | grocery | le shop | 5 | Foo pl | 9 | When importing Then placex contains | object | rank_address | | W2 | 25 | Then placex contains | object | parent_place_id | | N10 | W2 | ================================================ FILE: test/bdd/features/db/import/placex.feature ================================================ Feature: Import into placex Tests that data in placex is completed correctly. Scenario: No country code tag is available Given the named places | osm | class | type | geometry | | N1 | highway | primary | country:us | When importing Then placex contains | object | address | country_code | | N1 | - | us | Scenario: Location overwrites country code tag Given the named places | osm | class | type | country | geometry | | N1 | highway | primary | de | country:us | When importing Then placex contains | object | addr+country | country_code | | N1 | de | us | Scenario: Country code tag overwrites location for countries Given the named places | osm | class | type | admin | country | geometry | | R1 | boundary | administrative | 2 | de | (-100 40, -101 40, -101 41, -100 41, -100 40) | When importing Then placex contains | object | rank_search| addr+country | country_code | | R1 | 4 | de | de | Scenario: Illegal country code tag for countries is ignored Given the named places | osm | class | type | admin | country | geometry | | R1 | boundary | administrative | 2 | xx | (-100 40, -101 40, -101 41, -100 41, -100 40) | When importing Then placex contains | object | addr+country | country_code | | R1 | xx | us | Scenario: admin level is copied over Given the named places | osm | class | type | admin | | N1 | place | state | 3 | When importing Then placex contains | object | admin_level | | N1 | 3 | Scenario: search and address ranks for boundaries are correctly assigned Given the named places | osm | class | type | | N1 | boundary | administrative | And the named places | osm | class | type | geometry | | W10 | boundary | administrative | 10 10, 11 11 | And the named places | osm | class | type | admin | geometry | | R20 | boundary | administrative | 2 | (1 1, 2 2, 1 2, 1 1) | | R21 | boundary | administrative | 32 | (3 3, 4 4, 3 4, 3 3) | | R22 | boundary | nature_park | 6 | (0 0, 1 0, 0 1, 0 0) | | R23 | boundary | natural_reserve| 10 | (0 0, 1 1, 1 0, 0 0) | And the named places | osm | class | type | geometry | | R40 | place | country | (1 1, 2 2, 1 2, 1 1) | | R41 | place | state | (3 3, 4 4, 3 4, 3 3) | When importing Then placex has no entry for N1 And placex has no entry for W10 And placex contains | object | rank_search | rank_address | | R20 | 4 | 4 | | R21 | 25 | 0 | | R22 | 25 | 0 | | R23 | 25 | 0 | | R40 | 4 | 0 | | R41 | 8 | 0 | Scenario: search and address ranks for highways correctly assigned Given the grid | 10 | 1 | 11 | | 12 | | 13 | | 14 | | 15 | | 16 | And the places | osm | class | type | | N1 | highway | bus_stop | And the places | osm | class | type | geometry | | W1 | highway | primary | 10,11 | | W2 | highway | secondary | 11,12 | | W3 | highway | tertiary | 12,13 | | W4 | highway | residential | 13,14 | | W5 | highway | unclassified | 14,15 | | W6 | highway | something | 15,16 | When importing Then placex contains | object | rank_search | rank_address | | N1 | 30 | 30 | | W1 | 26 | 26 | | W2 | 26 | 26 | | W3 | 26 | 26 | | W4 | 26 | 26 | | W5 | 26 | 26 | | W6 | 30 | 30 | Scenario: rank and inclusion of landuses Given the 0.4 grid | 1 | 2 | | | | | | 5 | | 4 | 3 | | | | | | 6 | Given the named places | osm | class | type | | N2 | landuse | residential | And the named places | osm | class | type | geometry | | W2 | landuse | residential | 1,2,5 | | W4 | landuse | residential | (1,4,3,1) | | R2 | landuse | residential | (1,2,3,4,1) | | R3 | landuse | forrest | (1,5,6,4,1) | When importing Then placex contains | object | rank_search | rank_address | | N2 | 30 | 30 | | W2 | 30 | 30 | | W4 | 24 | 24 | | R2 | 24 | 24 | | R3 | 24 | 0 | Scenario: rank and inclusion of naturals Given the 0.4 grid | 1 | 2 | | | | | | 5 | | 4 | 3 | | | | | | 6 | Given the named places | osm | class | type | | N2 | natural | peak | | N4 | natural | volcano | | N5 | natural | foobar | And the named places | osm | class | type | geometry | | W2 | natural | mountain_range | 1,2,5 | | W3 | natural | foobar | 2,3 | | R3 | natural | volcano | (1,2,4,1) | | R4 | natural | foobar | (1,2,3,4,1) | | R5 | natural | sea | (1,2,5,6,3,4,1) | | R6 | natural | sea | (2,3,4,2) | When importing Then placex contains | object | rank_search | rank_address | | N2 | 18 | 0 | | N4 | 18 | 0 | | N5 | 22 | 0 | | W2 | 18 | 0 | | R3 | 18 | 0 | | R4 | 22 | 0 | | R5 | 4 | 0 | | R6 | 4 | 0 | | W3 | 22 | 0 | Scenario: boundary ways for countries and states are ignored Given the 0.3 grid | 1 | 2 | | 4 | 3 | Given the named places | osm | class | type | admin | geometry | | W4 | boundary | administrative | 2 | (1,2,3,4,1) | | R4 | boundary | administrative | 2 | (1,2,3,4,1) | | W5 | boundary | administrative | 3 | (1,2,3,4,1) | | R5 | boundary | administrative | 3 | (1,2,3,4,1) | | W6 | boundary | administrative | 4 | (1,2,3,4,1) | | R6 | boundary | administrative | 4 | (1,2,3,4,1) | | W7 | boundary | administrative | 5 | (1,2,3,4,1) | | R7 | boundary | administrative | 5 | (1,2,3,4,1) | When importing Then placex contains exactly | object | | R4 | | R5 | | R6 | | W7 | | R7 | ================================================ FILE: test/bdd/features/db/import/postcodes.feature ================================================ Feature: Import of postcodes Tests for postcode estimation Scenario: Postcodes on the object are preferred over those on the address Given the grid with origin FR | 1 | | | | 4 | | 6 | | 8 | | | 10 | | 11 | | | | | | | | | 22 | | | | | | | | 2 | | | | 3 | | 5 | | 7 | And the named places | osm | class | type | admin | addr+postcode | geometry | | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | | R4 | boundary | administrative | 10 | 11200 | (1,4,3,2,1) | And the named places | osm | class | type | addr+postcode | geometry | | W93 | highway | residential | 11250 | 10,11 | | N22 | building | yes | 11254 | 22 | When importing Then placex contains | object | postcode | | N22 | 11254 | | W93 | 11250 | | R4 | 11200 | | R34 | 11000 | | R1 | 10000 | Scenario: Postcodes from a road are inherited by an attached building Given the grid with origin DE | 10 | | | | 11 | | | 1 | 2 | | | | | 4 | 3 | | | And the named places | osm | class | type | addr+postcode | geometry | | W93 | highway | residential | 86034 | 10,11 | And the named places | osm | class | type | geometry | | W22 | building | yes | (1,2,3,4,1) | When importing Then placex contains | object | postcode | parent_place_id | | W22 | 86034 | W93 | Scenario: Postcodes from the lowest admin area are inherited by ways Given the grid with origin FR | 1 | | | | 4 | | 6 | | 8 | | | 10 | | 11 | | | | | | | 2 | | | | 3 | | 5 | | 7 | And the named places | osm | class | type | admin | addr+postcode | geometry | | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | | R4 | boundary | administrative | 10 | 11200 | (1,4,3,2,1) | And the named places | osm | class | type | geometry | | W93 | highway | residential | 10,11 | When importing Then placex contains | object | postcode | | W93 | 11200 | Scenario: Postcodes from the lowest admin area with postcode are inherited by ways Given the grid with origin FR | 1 | | | | 4 | | 6 | | 8 | | | 10 | | 11 | | | | | | | 2 | | | | 3 | | 5 | | 7 | And the named places | osm | class | type | admin | addr+postcode | geometry | | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | And the named places | osm | class | type | admin | geometry | | R4 | boundary | administrative | 10 | (1,4,3,2,1) | And the named places | osm | class | type | geometry | | W93 | highway | residential | 10,11 | When importing Then placex contains | object | postcode | parent_place_id | | W93 | 11000 | R4 | Scenario: Postcodes from the lowest admin area are inherited by buildings Given the grid with origin FR | 1 | | | | 4 | | 6 | | 8 | | | 10 | | 11 | | | | | | | | 13 | | 12 | | | | | | | 2 | | | | 3 | | 5 | | 7 | And the named places | osm | class | type | admin | addr+postcode | geometry | | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | | R4 | boundary | administrative | 10 | 11200 | (1,4,3,2,1) | And the named places | osm | class | type | geometry | | W22 | building | yes | (10,11,12,13,10) | When importing Then placex contains | object | postcode | | W22 | 11200 | Scenario: Roads get postcodes from nearby named buildings without other info Given the grid with origin US | 10 | | | | 11 | | | 1 | 2 | | | | | 4 | 3 | | | And the named places | osm | class | type | geometry | | W93 | highway | residential | 10,11 | And the named places | osm | class | type | addr+postcode | geometry | | W22 | building | yes | 45023 | (1,2,3,4,1) | When importing Then placex contains | object | postcode | | W93 | 45023 | Scenario: Road areas get postcodes from nearby named buildings without other info Given the grid with origin US | 10 | | | | 11 | | 13 | | | | 12 | | | 1 | 2 | | | | | 4 | 3 | | | And the named places | osm | class | type | geometry | | W93 | highway | pedestrian | (10,11,12,13,10) | And the named places | osm | class | type | addr+postcode | geometry | | W22 | building | yes | 45023 | (1,2,3,4,1) | When importing Then placex contains | object | postcode | | W93 | 45023 | Scenario: Roads get postcodes from nearby unnamed buildings without other info Given the grid with origin US | 10 | | | | 11 | | | 1 | | | | And the named places | osm | class | type | geometry | | W93 | highway | residential | 10,11 | And the postcodes | osm | postcode | centroid | | W22 | 45023 | 1 | When importing Then placex contains | object | postcode | | W93 | 45023 | Scenario: Postcodes from admin boundaries are preferred over estimated postcodes Given the grid with origin FR | 1 | | | | 4 | | 6 | | 8 | | | 10 | | 11 | | | | | | | | | 22 | | | | | | | | 2 | | | | 3 | | 5 | | 7 | And the named places | osm | class | type | admin | addr+postcode | geometry | | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | | R4 | boundary | administrative | 10 | 11200 | (1,4,3,2,1) | And the named places | osm | class | type | geometry | | W93 | highway | residential | 10,1 | And the named places | osm | class | type | addr+postcode | | N22 | building | yes | 45023 | When importing Then placex contains | object | postcode | | W93 | 11200 | Scenario: Postcodes are added to the postcode Given the places | osm | class | type | addr+postcode | addr+housenumber | geometry | | N34 | place | house | 01982 | 111 | country:de | When importing Then location_postcodes contains exactly | country_code | postcode | centroid!wkt | | de | 01982 | country:de | Scenario: Postcodes outside all countries are not added to the postcode table Given the places | osm | class | type | addr+postcode | addr+housenumber | addr+place | geometry | | N34 | place | house | 01982 | 111 | Null Island | 0 0.00001 | And the places | osm | class | type | name | geometry | | N1 | place | hamlet | Null Island | 0 0 | When importing Then location_postcodes contains exactly | place_id | When geocoding "111, 01982 Null Island" Then the result set contains | object | display_name | | N34 | 111, Null Island, 01982 | ================================================ FILE: test/bdd/features/db/import/rank_computation.feature ================================================ Feature: Rank assignment Tests for assignment of search and address ranks. Scenario: Ranks for place nodes are assigned according to their type Given the named places | osm | class | type | geometry | | N1 | foo | bar | 0 0 | | N11 | place | Continent | 0 0 | | N12 | place | continent | 0 0 | | N13 | place | sea | 0 0 | | N14 | place | country | 0 0 | | N15 | place | state | 0 0 | | N16 | place | region | 0 0 | | N17 | place | county | 0 0 | | N18 | place | city | 0 0 | | N19 | place | island | 0 0 | | N36 | place | house | 0 0 | And the named places | osm | class | type | extra+capital | geometry | | N101 | place | city | yes | 0 0 | When importing Then placex contains | object | rank_search | rank_address | | N1 | 30 | 30 | | N11 | 22 | 0 | | N12 | 2 | 0 | | N13 | 2 | 0 | | N14 | 4 | 0 | | N15 | 8 | 0 | | N16 | 18 | 0 | | N17 | 12 | 12 | | N18 | 16 | 16 | | N19 | 17 | 0 | | N101 | 15 | 16 | | N36 | 30 | 30 | Scenario: Ranks for boundaries are assigned according to admin level Given the named places | osm | class | type | admin | geometry | | R20 | boundary | administrative | 2 | (1 1, 2 2, 1 2, 1 1) | | R21 | boundary | administrative | 32 | (3 3, 4 4, 3 4, 3 3) | | R22 | boundary | administrative | 6 | (0 0, 1 0, 0 1, 0 0) | | R23 | boundary | administrative | 10 | (0 0, 1 1, 1 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R20 | 4 | 4 | | R21 | 25 | 0 | | R22 | 12 | 12 | | R23 | 20 | 20 | Scenario: Ranks for addressable boundaries with place assignment go with place address ranks if available Given the named places | osm | class | type | admin | extra+place | geometry | | R20 | boundary | administrative | 3 | state | (1 1, 2 2, 1 2, 1 1) | | R21 | boundary | administrative | 32 | suburb | (3 3, 4 4, 3 4, 3 3) | | R22 | boundary | administrative | 6 | town | (0 0, 1 0, 0 1, 0 0) | | R23 | boundary | administrative | 10 | village | (0 0, 1 1, 1 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R20 | 6 | 6 | | R21 | 25 | 0 | | R22 | 12 | 16 | | R23 | 20 | 16 | Scenario: Place address ranks cannot overtake a parent address rank Given the named places | osm | class | type | admin | extra+place | geometry | | R20 | boundary | administrative | 8 | town | (0 0, 0 2, 2 2, 2 0, 0 0) | | R21 | boundary | administrative | 9 | municipality | (0 0, 0 1, 1 1, 1 0, 0 0) | | R22 | boundary | administrative | 9 | suburb | (0 0, 0 1, 1 1, 1 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R20 | 16 | 16 | | R21 | 18 | 18 | | R22 | 18 | 20 | Then place_addressline contains | object | address | cached_rank_address | | R21 | R20 | 16 | | R22 | R20 | 16 | Scenario: Admin levels cannot overtake each other due to place address ranks Given the named places | osm | class | type | admin | extra+place | geometry | | R20 | boundary | administrative | 6 | town | (0 0, 0 2, 2 2, 2 0, 0 0) | | R21 | boundary | administrative | 8 | | (0 0, 0 1, 1 1, 1 0, 0 0) | | R22 | boundary | administrative | 8 | suburb | (0 0, 0 1, 1 1, 1 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R20 | 12 | 16 | | R21 | 16 | 18 | | R22 | 16 | 20 | Then place_addressline contains | object | address | cached_rank_address | | R21 | R20 | 16 | | R22 | R20 | 16 | Scenario: Admin levels cannot overtake each other due to place address ranks even when slightly misaligned Given the named places | osm | class | type | admin | extra+place | geometry | | R20 | boundary | administrative | 6 | town | (0 0, 0 2, 2 2, 2 0, 0 0) | | R21 | boundary | administrative | 8 | | (0 0, -0.0001 1, 1 1, 1 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R20 | 12 | 16 | | R21 | 16 | 18 | Then place_addressline contains | object | address | cached_rank_address | | R21 | R20 | 16 | Scenario: Admin levels must not be larger than 25 Given the named places | osm | class | type | admin | extra+place | geometry | | R20 | boundary | administrative | 6 | quarter | (0 0, 0 2, 2 2, 2 0, 0 0) | | R21 | boundary | administrative | 7 | | (0 0, 0 1, 1 1, 1 0, 0 0) | | R22 | boundary | administrative | 8 | | (0 0, 0 0.5, 0.5 0.5, 0.5 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R20 | 12 | 22 | | R21 | 14 | 24 | | R22 | 16 | 25 | Scenario: admin levels contained in a place area must not overtake address ranks Given the named places | osm | class | type | admin | geometry | | R10 | place | city | 15 | (0 0, 0 2, 2 0, 0 0) | | R20 | boundary | administrative | 6 | (0 0, 0 1, 1 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R10 | 16 | 16 | | R20 | 12 | 18 | Scenario: admin levels overlapping a place area are not demoted Given the named places | osm | class | type | admin | geometry | | R10 | place | city | 15 | (0 0, 0 2, 2 0, 0 0) | | R20 | boundary | administrative | 6 | (-1 0, 0 1, 1 0, -1 0) | When importing Then placex contains | object | rank_search | rank_address | | R10 | 16 | 16 | | R20 | 12 | 12 | Scenario: admin levels with equal area as a place area are not demoted Given the named places | osm | class | type | admin | geometry | | R10 | place | city | 15 | (0 0, 0 2, 2 0, 0 0) | | R20 | boundary | administrative | 6 | (0 0, 0 2, 2 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R10 | 16 | 16 | | R20 | 12 | 12 | Scenario: adjacent admin_levels are considered the same object when they have the same wikidata Given the named places | osm | class | type | admin | extra+wikidata | geometry | | N20 | place | square | 15 | Q123 | 0.1 0.1 | | R23 | boundary | administrative | 10 | Q444 | (0 0, 0 1, 1 1, 1 0, 0 0) | | R21 | boundary | administrative | 9 | Q444 | (0 0, 0 1, 1 1, 1 0, 0 0) | | R22 | boundary | administrative | 8 | Q444 | (0 0, 0 1, 1 1, 1 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R23 | 20 | 0 | | R21 | 18 | 0 | | R22 | 16 | 16 | Then place_addressline contains exactly | object | address | cached_rank_address | | N20 | R22 | 16 | Scenario: adjacent admin_levels are considered different objects when they have different wikidata Given the named places | osm | class | type | admin | extra+wikidata | geometry | | N20 | place | square | 15 | Q123 | 0.1 0.1 | | R21 | boundary | administrative | 9 | Q4441 | (0 0, 0 1, 1 1, 1 0, 0 0) | | R22 | boundary | administrative | 8 | Q444 | (0 0, 0 1, 1 1, 1 0, 0 0) | When importing Then placex contains | object | rank_search | rank_address | | R21 | 18 | 18 | | R22 | 16 | 16 | Then place_addressline contains | object | address | cached_rank_address | | N20 | R22 | 16 | | N20 | R21 | 18 | Scenario: Mixes of admin boundaries and place areas I Given the grid | 1 | | 10 | | | 2 | | | 9 | | | | | | 20| | 21 | | | | | 4 | | 11 | | | 3 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 5 | Greater London | (1,2,3,4,1) | | R2 | boundary | administrative | 8 | Kensington | (1,10,11,4,1) | And the places | osm | class | type | name | geometry | | R10 | place | city | London | (1,2,3,4,1) | | N9 | place | town | Fulham | 9 | | W1 | highway | residential | Lots Grove | 20,21 | When importing Then placex contains | object | rank_search | rank_address | | R1 | 10 | 10 | | R10 | 16 | 16 | | R2 | 16 | 18 | | N9 | 18 | 18 | And place_addressline contains | object | address | isaddress | cached_rank_address | | W1 | R1 | True | 10 | | W1 | R10 | True | 16 | | W1 | R2 | True | 18 | | W1 | N9 | False | 18 | Scenario: Mixes of admin boundaries and place areas II Given the grid | 1 | | 10 | | 5 | 2 | | | 9 | | | | | | 20| | 21 | | | | | 4 | | 11 | | 6 | 3 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 5 | Greater London | (1,2,3,4,1) | | R2 | boundary | administrative | 8 | London | (1,5,6,4,1) | And the places | osm | class | type | name | geometry | | R10 | place | city | Westminster | (1,10,11,4,1) | | N9 | place | town | Fulham | 9 | | W1 | highway | residential | Lots Grove | 20,21 | When importing Then placex contains | object | rank_search | rank_address | | R1 | 10 | 10 | | R2 | 16 | 16 | | R10 | 16 | 18 | | N9 | 18 | 18 | And place_addressline contains | object | address | isaddress | cached_rank_address | | W1 | R1 | True | 10 | | W1 | R10 | True | 18 | | W1 | R2 | True | 16 | | W1 | N9 | False | 18 | Scenario: POI nodes with place tags Given the places | osm | class | type | name | extratags | | N23 | amenity | playground | AB | "place": "city" | | N23 | place | city | AB | "amenity": "playground" | When importing Then placex contains exactly | object | rank_search | rank_address | | N23:amenity | 30 | 30 | | N23:place | 16 | 16 | Scenario: Address rank 25 is only used for addr:place Given the grid | 10 | 33 | 34 | 11 | Given the places | osm | class | type | name | | N10 | place | village | vil | | N11 | place | farm | farm | And the places | osm | class | type | name | geometry | | W1 | highway | residential | RD | 33,11 | And the places | osm | class | type | name | addr+farm | geometry | | W2 | highway | residential | RD2 | farm | 34,11 | And the places | osm | class | type | housenr | | N33 | place | house | 23 | And the places | osm | class | type | housenr | addr+place | | N34 | place | house | 23 | farm | When importing Then placex contains | object | parent_place_id | | N11 | N10 | | N33 | W1 | | N34 | N11 | And place_addressline contains | object | address | | W1 | N10 | | W2 | N10 | | W2 | N11 | ================================================ FILE: test/bdd/features/db/import/search_name.feature ================================================ Feature: Creation of search terms Tests that search_name table is filled correctly Scenario: Semicolon-separated names appear as separate full names Given the places | osm | class | type | name+alt_name | | N1 | place | city | New York; Big Apple | | N2 | place | town | New York Big Apple | When importing And geocoding "New York Big Apple" Then result 0 contains | object | | N2 | Scenario: Comma-separated names appear as a single full name Given the places | osm | class | type | name+name | | N1 | place | city | New York, Big Apple | | N2 | place | town | New York Big Apple | When importing And geocoding "New York Big Apple" Then result 0 contains | object | | N1 | Scenario: Name parts before brackets appear as full names Given the places | osm | class | type | name+name | | N1 | place | city | Halle (Saale) | | N2 | place | town | Halle | When importing And geocoding "Halle" Then result 0 contains | object | | N1 | When geocoding "Halle (Saale)" Then the result set contains | object | | N1 | Scenario: Unknown addr: tags can be found for unnamed POIs Given the grid | | 1 | | | | 10 | | | 11 | And the places | osm | class | type | housenr | addr+city | | N1 | place | house | 23 | Walltown | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Rose Street | 10,11 | When importing When geocoding "23 Rose Street, Walltown" Then the result set contains | object | display_name | | N1 | 23, Rose Street | When geocoding "Walltown, Rose Street 23" Then the result set contains | object | display_name | | N1 | 23, Rose Street | When geocoding "Rose Street 23, Walltown" Then the result set contains | object | display_name | | N1 | 23, Rose Street | Scenario: Searching for unknown addr: tags also works for multiple words Given the grid | | 1 | | | | 10 | | | 11 | And the places | osm | class | type | housenr | addr+city | | N1 | place | house | 23 | Little Big Town | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Rose Street | 10,11 | When importing When geocoding "23 Rose Street, Little Big Town" Then the result set contains | object | display_name | | N1 | 23, Rose Street | When geocoding "Rose Street 23, Little Big Town" Then the result set contains | object | display_name | | N1 | 23, Rose Street | When geocoding "Little big Town, Rose Street 23" Then the result set contains | object | display_name | | N1 | 23, Rose Street | Scenario: Unnamed POI can be found when it has known addr: tags Given the grid | | 1 | | | | 10 | | | 11 | And the places | osm | class | type | housenr | addr+city | | N1 | place | house | 23 | Walltown | And the places | osm | class | type | name+name | addr+city | geometry | | W1 | highway | residential | Rose Street | Walltown | 10,11 | When importing When geocoding "23 Rose Street, Walltown" Then the result set contains | object | display_name | | N1 | 23, Rose Street | Scenario: Unnamed POIs inherit parent name when unknown addr:place is present Given the grid | 100 | | | | | 101 | | | | 1 | | | | | 103 | 10 | | | 11 | 102 | And the places | osm | class | type | housenr | addr+place | | N1 | place | house | 23 | Walltown | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Rose Street | 10,11 | | R1 | place | city | Strange Town | (100,101,102,103,100) | When importing Then placex contains | object | parent_place_id | | N1 | R1 | When geocoding "23 Rose Street" Then all results contain | object | display_name | | W1 | Rose Street, Strange Town | When geocoding "23 Walltown, Strange Town" Then the result set contains | object | display_name | | N1 | 23, Walltown, Strange Town | When geocoding "Walltown 23, Strange Town" Then the result set contains | object | display_name | | N1 | 23, Walltown, Strange Town | When geocoding "Strange Town, Walltown 23" Then the result set contains | object | display_name | | N1 | 23, Walltown, Strange Town | Scenario: Named POIs can be searched by housenumber when unknown addr:place is present Given the grid | 100 | | | | | 101 | | | | 1 | | | | | 103 | 10 | | | 11 | 102 | And the places | osm | class | type | name | housenr | addr+place | | N1 | place | house | Blue house | 23 | Walltown | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Rose Street | 10,11 | | R1 | place | city | Strange Town | (100,101,102,103,100) | When importing When geocoding "23 Walltown, Strange Town" Then the result set contains | object | display_name | | N1 | Blue house, 23, Walltown, Strange Town | When geocoding "Walltown 23, Strange Town" Then the result set contains | object | display_name | | N1 | Blue house, 23, Walltown, Strange Town | When geocoding "Strange Town, Walltown 23" Then the result set contains | object | display_name | | N1 | Blue house, 23, Walltown, Strange Town | When geocoding "Strange Town, Walltown 23, Blue house" Then the result set contains | object | display_name | | N1 | Blue house, 23, Walltown, Strange Town | When geocoding "Strange Town, Walltown, Blue house" Then the result set contains | object | display_name | | N1 | Blue house, 23, Walltown, Strange Town | Scenario: Named POIs can be found when unknown multi-word addr:place is present Given the grid | 100 | | | | | 101 | | | | 1 | | | | | 103 | 10 | | | 11 | 102 | And the places | osm | class | type | name | housenr | addr+place | | N1 | place | house | Blue house | 23 | Moon sun | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Rose Street | 10,11 | | R1 | place | city | Strange Town | (100,101,102,103,100) | When importing When geocoding "23 Moon Sun, Strange Town" Then the result set contains | object | display_name | | N1 | Blue house, 23, Moon sun, Strange Town | When geocoding "Blue house, Moon Sun, Strange Town" Then the result set contains | object | display_name | | N1 | Blue house, 23, Moon sun, Strange Town | Scenario: Unnamed POIs doesn't inherit parent name when addr:place is present only in parent address Given the grid | 100 | | | | | 101 | | | | 1 | | | | | 103 | 10 | | | 11 | 102 | And the places | osm | class | type | housenr | addr+place | | N1 | place | house | 23 | Walltown | And the places | osm | class | type | name+name | addr+city | geometry | | W1 | highway | residential | Rose Street | Walltown | 10,11 | | R1 | place | suburb | Strange Town | Walltown | (100,101,102,103,100) | When importing When geocoding "23 Rose Street, Walltown" Then all results contain | object | display_name | | W1 | Rose Street, Strange Town | When geocoding "23 Walltown" Then all results contain | object | display_name | | N1 | 23, Walltown, Strange Town | Scenario: Unnamed POIs does inherit parent name when unknown addr:place and addr:street is present Given the grid | | 1 | | | | 10 | | | 11 | And the places | osm | class | type | housenr | addr+place | addr+street | | N1 | place | house | 23 | Walltown | Lily Street | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Rose Street | 10,11 | When importing When geocoding "23 Rose Street" Then the result set contains | object | display_name | | N1 | 23, Rose Street | When geocoding "23 Lily Street" Then exactly 0 results are returned Scenario: An unknown addr:street is ignored Given the grid | | 1 | | | | 10 | | | 11 | And the places | osm | class | type | housenr | addr+street | | N1 | place | house | 23 | Lily Street | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Rose Street | 10,11 | When importing When geocoding "23 Rose Street" Then the result set contains | object | display_name | | N1 | 23, Rose Street | When geocoding "23 Lily Street" Then exactly 0 results are returned Scenario: Named POIs can be found through unknown address tags Given the grid | | 1 | | | | 10 | | | 11 | And the places | osm | class | type | name+name | housenr | addr+city | | N1 | place | house | Green Moss | 26 | Walltown | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Rose Street | 10,11 | When importing When geocoding "Green Moss, Rose Street, Walltown" Then the result set contains | object | display_name | | N1 | Green Moss, 26, Rose Street | When geocoding "Green Moss, 26, Rose Street, Walltown" Then the result set contains | object | display_name | | N1 | Green Moss, 26, Rose Street | When geocoding "26, Rose Street, Walltown" Then the result set contains | object | display_name | | N1 | Green Moss, 26, Rose Street | When geocoding "Rose Street 26, Walltown" Then the result set contains | object | display_name | | N1 | Green Moss, 26, Rose Street | When geocoding "Walltown, Rose Street 26" Then the result set contains | object | display_name | | N1 | Green Moss, 26, Rose Street | Scenario: Named POI doesn't inherit parent name when addr:place is present only in parent address Given the grid | 100 | | | | | 101 | | | | 1 | | | | | 103 | 10 | | | 11 | 102 | And the places | osm | class | type | name+name | addr+place | | N1 | place | house | Green Moss | Walltown | And the places | osm | class | type | name+name | geometry | | W1 | highway | residential | Rose Street | 10,11 | | R1 | place | suburb | Strange Town | (100,101,102,103,100) | When importing When geocoding "Green Moss, Rose Street, Walltown" Then exactly 0 results are returned When geocoding "Green Moss, Walltown" Then the result set contains | object | display_name | | N1 | Green Moss, Walltown, Strange Town | Scenario: Named POIs inherit address from parent Given the grid | | 1 | | | | 10 | | | 11 | And the places | osm | class | type | name | geometry | | N1 | place | house | foo | 1 | | W1 | highway | residential | the road | 10,11 | When importing When geocoding "foo, the road" Then all results contain | object | | N1 | Scenario: Some addr: tags are added to address Given the grid | | 2 | 3 | | | 10 | | | 11 | And the places | osm | class | type | name | | N2 | place | city | bonn | | N3 | place | suburb | smalltown| And the places | osm | class | type | name | addr+city | addr+municipality | addr+suburb | geometry | | W1 | highway | service | the end | bonn | New York | Smalltown | 10,11 | When importing When geocoding "the end, new york, bonn, smalltown" Then all results contain | object | | W1 | Scenario: A known addr:* tag is added even if the name is unknown Given the grid | 10 | | | | 11 | And the places | osm | class | type | name | addr+city | geometry | | W1 | highway | residential | Road | Nandu | 10,11 | When importing And geocoding "Road, Nandu" Then all results contain | object | | W1 | Scenario: a linked place does not show up in search name Given the 0.01 grid | 10 | | 11 | | | 2 | | | 13 | | 12 | Given the places | osm | class | type | name | admin | geometry | | R13 | boundary | administrative | Roma | 9 | (10,11,12,13,10) | And the places | osm | class | type | name | | N2 | place | city | Cite | And the relations | id | members | tags+type | | 13 | N2:label | boundary | When importing Then placex contains | object | linked_place_id | | N2 | R13 | When geocoding "Cite" Then all results contain | object | | R13 | Scenario: a linked waterway does not show up in search name Given the grid | 1 | | 2 | | 3 | And the places | osm | class | type | name | geometry | | W1 | waterway | river | Rhein | 1,2 | | W2 | waterway | river | Rhein | 2,3 | | R13 | waterway | river | Rhein | 1,2,3 | And the relations | id | members | tags+type | | 13 | W1,W2:main_stream | waterway | When importing Then placex contains | object | linked_place_id | | W1 | R13 | | W2 | R13 | When geocoding "Rhein" Then all results contain | object | | R13 | ================================================ FILE: test/bdd/features/db/query/housenumbers.feature ================================================ Feature: Searching of house numbers Test for specialised treeatment of housenumbers Background: Given the grid | 1 | | 2 | | 3 | | | 9 | | | | | | | | | 4 | Scenario: A simple ascii digit housenumber is found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | 45 | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | North Road | 1,2,3 | When importing And geocoding "45, North Road" Then the result set contains | object | | N1 | When geocoding "North Road 45" Then the result set contains | object | | N1 | Scenario Outline: Numeral housenumbers in any script are found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | North Road | 1,2,3 | When importing And geocoding "45, North Road" Then the result set contains | object | | N1 | When geocoding "North Road ④⑤" Then the result set contains | object | | N1 | When geocoding "North Road 𑁪𑁫" Then the result set contains | object | | N1 | Examples: | number | | 45 | | ④⑤ | | 𑁪𑁫 | Scenario Outline: Each housenumber in a list is found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Multistr | 1,2,3 | When importing When geocoding "2 Multistr" Then the result set contains | object | | N1 | When geocoding "4 Multistr" Then the result set contains | object | | N1 | When geocoding "12 Multistr" Then the result set contains | object | | N1 | Examples: | hnrs | | 2;4;12 | | 2,4,12 | | 2, 4, 12 | Scenario Outline: Housenumber - letter combinations are found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Multistr | 1,2,3 | When importing When geocoding "2A Multistr" Then the result set contains | object | | N1 | When geocoding "2 a Multistr" Then the result set contains | object | | N1 | When geocoding "2-A Multistr" Then the result set contains | object | | N1 | When geocoding "Multistr 2 A" Then the result set contains | object | | N1 | Examples: | hnr | | 2a | | 2 A | | 2-a | | 2/A | Scenario Outline: Number - Number combinations as a housenumber are found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Chester St | 1,2,3 | When importing When geocoding "34-10 Chester St" Then the result set contains | object | | N1 | When geocoding "34/10 Chester St" Then the result set contains | object | | N1 | When geocoding "34 10 Chester St" Then the result set contains | object | | N1 | When geocoding "3410 Chester St" Then the result set contains | object | | W10 | Examples: | hnr | | 34-10 | | 34 10 | | 34/10 | Scenario Outline: a bis housenumber is found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Rue Paris | 1,2,3 | When importing When geocoding "Rue Paris 45bis" Then the result set contains | object | | N1 | When geocoding "Rue Paris 45 BIS" Then the result set contains | object | | N1 | When geocoding "Rue Paris 45BIS" Then the result set contains | object | | N1 | When geocoding "Rue Paris 45 bis" Then the result set contains | object | | N1 | Examples: | hnr | | 45bis | | 45BIS | | 45 BIS | | 45 bis | Scenario Outline: a ter housenumber is found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Rue du Berger | 1,2,3 | When importing When geocoding "Rue du Berger 45ter" Then the result set contains | object | | N1 | When geocoding "Rue du Berger 45 TER" Then the result set contains | object | | N1 | When geocoding "Rue du Berger 45TER" Then the result set contains | object | | N1 | When geocoding "Rue du Berger 45 ter" Then the result set contains | object | | N1 | Examples: | hnr | | 45ter | | 45TER | | 45 ter | | 45 TER | Scenario Outline: a number - letter - number combination housenumber is found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Herengracht | 1,2,3 | When importing When geocoding "501-H 1 Herengracht" Then the result set contains | object | | N1 | When geocoding "501H-1 Herengracht" Then the result set contains | object | | N1 | When geocoding "501H1 Herengracht" Then the result set contains | object | | N1 | When geocoding "501-H1 Herengracht" Then the result set contains | object | | N1 | Examples: | hnr | | 501 H1 | | 501H 1 | | 501/H/1 | | 501h1 | Scenario Outline: Russian housenumbers are found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Голубинская улица | 1,2,3 | When importing When geocoding "Голубинская улица 55к3" Then the result set contains | object | | N1 | When geocoding "Голубинская улица 55 k3" Then the result set contains | object | | N1 | When geocoding "Голубинская улица 55 к-3" Then the result set contains | object | | N1 | Examples: | hnr | | 55к3 | | 55 к3 | Scenario: A name mapped as a housenumber is found Given the places | osm | class | type | housenr | geometry | | N1 | building | yes | Warring | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Chester St | 1,2,3 | When importing When geocoding "Chester St Warring" Then the result set contains | object | | N1 | Scenario: A housenumber with interpolation is found Given the places | osm | class | type | housenr | addr+interpolation | geometry | | N1 | building | yes | 1-5 | odd | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Rue Paris | 1,2,3 | When importing When geocoding "Rue Paris 1" Then the result set contains | object | address+house_number | | N1 | 1-5 | When geocoding "Rue Paris 3" Then the result set contains | object | address+house_number | | N1 | 1-5 | When geocoding "Rue Paris 5" Then the result set contains | object | address+house_number | | N1 | 1-5 | When geocoding "Rue Paris 2" Then the result set contains | object | | W10 | Scenario: A housenumber with bad interpolation is ignored Given the places | osm | class | type | housenr | addr+interpolation | geometry | | N1 | building | yes | 1-5 | bad | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Rue Paris | 1,2,3 | When importing When geocoding "Rue Paris 1-5" Then the result set contains | object | address+house_number | | N1 | 1-5 | When geocoding "Rue Paris 3" Then the result set contains | object | | W10 | Scenario: A bad housenumber with a good interpolation is just a housenumber Given the places | osm | class | type | housenr | addr+interpolation | geometry | | N1 | building | yes | 1-100 | all | 9 | And the places | osm | class | type | name | geometry | | W10 | highway | path | Rue Paris | 1,2,3 | When importing When geocoding "Rue Paris 1-100" Then the result set contains | object | address+house_number | | N1 | 1-100 | When geocoding "Rue Paris 3" Then the result set contains | object | | W10 | ================================================ FILE: test/bdd/features/db/query/interpolation.feature ================================================ Feature: Query of address interpolations Tests that interpolated addresses can be queried correctly Background: Given the grid | 1 | | 2 | | 3 | | 10 | | 12 | | 13 | | 7 | | 8 | | 9 | Scenario: Find interpolations with single number Given the places | osm | class | type | name | geometry | | W10 | highway | primary | Nickway | 10,12,13 | And the interpolations | osm | type | geometry | nodes | | W1 | odd | 1,3 | 1,3 | And the places | osm | class | type | housenr | geometry | | N1 | place | house | 1 | 1 | | N3 | place | house | 5 | 3 | When importing When reverse geocoding at node 2 Then the result contains | display_name | | 3, Nickway | When geocoding "Nickway 3" Then all results contain | object | display_name | | W1 | 3, Nickway | Scenario: Find interpolations with multiple numbers Given the places | osm | class | type | name | geometry | | W10 | highway | primary | Nickway | 10,12,13 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,3 | 1,3 | And the places | osm | class | type | housenr | geometry | | N1 | place | house | 2 | 1 | | N3 | place | house | 18 | 3 | When importing When reverse geocoding at node 2 Then the result contains | display_name | centroid!wkt | | 10, Nickway | 2 | When geocoding "Nickway 10" Then all results contain | object | display_name | centroid!wkt | | W1 | 10, Nickway | 2 | Scenario: Interpolations are found according to their type Given the grid | 10 | | 11 | | 100 | | 101 | | 20 | | 21 | And the places | osm | class | type | name | geometry | | W100 | highway | residential | Ringstr | 100, 101 | And the interpolations | osm | type | geometry | nodes | | W10 | even | 10, 11 | 10, 11 | | W20 | odd | 20, 21 | 20, 21 | And the places | osm | class | type | housenr | geometry | | N10 | place | house | 10 | 10 | | N11 | place | house | 20 | 11 | | N20 | place | house | 11 | 20 | | N21 | place | house | 21 | 21 | When importing When geocoding "Ringstr 12" Then the result set contains | object | | W10 | When geocoding "Ringstr 13" Then the result set contains | object | | W20 | ================================================ FILE: test/bdd/features/db/query/japanese.feature ================================================ Feature: Searches in Japan Test specifically for searches of Japanese addresses and in Japanese language. Scenario: A block house-number is parented to the neighbourhood Given the grid with origin JP | 1 | | | | 2 | | | 3 | | | | | | | 9 | | | | | | | 6 | | And the places | osm | class | type | name | geometry | | W1 | highway | residential | 雉子橋通り | 1,2 | And the places | osm | class | type | housenr | addr+block_number | addr+neighbourhood | geometry | | N3 | amenity | restaurant | 2 | 6 | 2丁目 | 3 | And the places | osm | class | type | name | geometry | | N9 | place | neighbourhood | 2丁目 | 9 | And the places | osm | class | type | name | geometry | | N6 | place | quarter | 加瀬 | 6 | When importing Then placex contains | object | parent_place_id | | N3 | N9 | When geocoding "2丁目 6-2" Then all results contain | object | | N3 | ================================================ FILE: test/bdd/features/db/query/linking.feature ================================================ Feature: Searching linked places Tests that information from linked places can be searched correctly Scenario: Additional names from linked places are searchable Given the 0.1 grid | 10 | | 11 | | | 2 | | | 13 | | 12 | Given the places | osm | class | type | admin | name | geometry | | R13 | boundary | administrative | 6 | Garbo | (10,11,12,13,10) | Given the places | osm | class | type | admin | name+name:it | | N2 | place | hamlet | 15 | Vario | And the relations | id | members | tags+type | | 13 | N2:label | boundary | When importing Then placex contains | object | linked_place_id | | N2 | R13 | When geocoding "Vario" | namedetails | | 1 | Then all results contain | object | display_name | namedetails!dict | | R13 | Garbo | "name": "Garbo", "name:it": "Vario" | When geocoding "Vario" | accept-language | | it | Then all results contain | object | display_name | | R13 | Vario | Scenario: Differing names from linked places are searchable Given the 0.1 grid | 10 | | 11 | | | 2 | | | 13 | | 12 | Given the places | osm | class | type | admin | name | geometry | | R13 | boundary | administrative | 6 | Garbo | (10,11,12,13,10) | Given the places | osm | class | type | admin | name | | N2 | place | hamlet | 15 | Vario | And the relations | id | members | tags+type | | 13 | N2:label | boundary | When importing Then placex contains | object | linked_place_id | | N2 | R13 | When geocoding "Vario" | namedetails | | 1 | Then all results contain | object | display_name | namedetails!dict | | R13 | Garbo | "name": "Garbo", "_place_name": "Vario" | When geocoding "Garbo" Then all results contain | object | display_name | | R13 | Garbo | ================================================ FILE: test/bdd/features/db/query/normalization.feature ================================================ Feature: Import and search of names Tests all naming related issues: normalisation, abbreviations, internationalisation, etc. Scenario: non-latin scripts can be found Given the places | osm | class | type | name | | N1 | place | locality | Речицкий район | | N2 | place | locality | Refugio de montaña | | N3 | place | locality | 高槻市| | N4 | place | locality | الدوحة | When importing When geocoding "Речицкий район" Then result 0 contains | object | | N1 | When geocoding "Refugio de montaña" Then result 0 contains | object | | N2 | When geocoding "高槻市" Then result 0 contains | object | | N3 | When geocoding "الدوحة" Then result 0 contains | object | | N4 | Scenario: Case-insensitivity of search Given the places | osm | class | type | name | | N1 | place | locality | FooBar | When importing Then placex contains | object | class | type | name+name | | N1 | place | locality | FooBar | When geocoding "FooBar" Then result 0 contains | object | | N1 | When geocoding "foobar" Then result 0 contains | object | | N1 | When geocoding "fOObar" Then result 0 contains | object | | N1 | When geocoding "FOOBAR" Then result 0 contains | object | | N1 | Scenario: Multiple spaces in name Given the places | osm | class | type | name | | N1 | place | locality | one two three | When importing When geocoding "one two three" Then result 0 contains | object | | N1 | When geocoding "one two three" Then result 0 contains | object | | N1 | When geocoding "one two three" Then result 0 contains | object | | N1 | When geocoding " one two three" Then result 0 contains | object | | N1 | Scenario: Special characters in name Given the places | osm | class | type | name+name:de | | N1 | place | locality | Jim-Knopf-Straße | | N2 | place | locality | Smith/Weston | | N3 | place | locality | space mountain | | N4 | place | locality | space | | N5 | place | locality | mountain | When importing When geocoding "Jim-Knopf-Str" Then result 0 contains | object | | N1 | When geocoding "Jim Knopf-Str" Then result 0 contains | object | | N1 | When geocoding "Jim Knopf Str" Then result 0 contains | object | | N1 | When geocoding "Jim/Knopf-Str" Then result 0 contains | object | | N1 | When geocoding "Jim-Knopfstr" Then result 0 contains | object | | N1 | When geocoding "Smith/Weston" Then result 0 contains | object | | N2 | When geocoding "Smith Weston" Then result 0 contains | object | | N2 | When geocoding "Smith-Weston" Then result 0 contains | object | | N2 | When geocoding "space mountain" Then result 0 contains | object | | N3 | When geocoding "space-mountain" Then result 0 contains | object | | N3 | When geocoding "space/mountain" Then result 0 contains | object | | N3 | When geocoding "space\mountain" Then result 0 contains | object | | N3 | When geocoding "space(mountain)" Then result 0 contains | object | | N3 | Scenario: Landuse with name are found Given the grid | 1 | 2 | | 3 | | Given the places | osm | class | type | name | geometry | | R1 | natural | meadow | landuse1 | (1,2,3,1) | | R2 | landuse | industrial | landuse2 | (2,3,1,2) | When importing When geocoding "landuse1" Then result 0 contains | object | | R1 | When geocoding "landuse2" Then result 0 contains | object | | R2 | Scenario Outline: Housenumbers with special characters are found Given the grid | 1 | | | | 2 | | | | 9 | | | And the places | osm | class | type | name | geometry | | W1 | highway | primary | Main St | 1,2 | And the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | When importing And geocoding "Main St " Then result 0 contains | object | display_name | | N1 | , Main St | Examples: | nr | | 1 | | 3456 | | 1 a | | 56b | | 1 A | | 2號 | | 1Б | | 1 к1 | | 23-123 | Scenario Outline: Housenumbers in lists are found Given the grid | 1 | | | | 2 | | | | 9 | | | And the places | osm | class | type | name | geometry | | W1 | highway | primary | Main St | 1,2 | And the places | osm | class | type | housenr | geometry | | N1 | building | yes | | 9 | When importing And geocoding "Main St " Then result 0 contains | object | display_name | | N1 | , Main St | Examples: | nr-list | nr | | 1,2,3 | 1 | | 1,2,3 | 2 | | 1, 2, 3 | 3 | | 45 ;67;3 | 45 | | 45 ;67;3 | 67 | | 1a;1k | 1a | | 1a;1k | 1k | | 34/678 | 34 | | 34/678 | 678 | | 34/678 | 34/678 | ================================================ FILE: test/bdd/features/db/query/postcodes.feature ================================================ Feature: Querying fo postcode variants Scenario: Postcodes in Singapore (6-digit postcode) Given the grid with origin SG | 10 | | | | 11 | And the places | osm | class | type | name | addr+postcode | geometry | | W1 | highway | path | Lorang | 399174 | 10,11 | When importing When geocoding "399174" Then result 0 contains | type | display_name | | postcode | 399174, Singapore | Scenario Outline: Postcodes in the Netherlands (mixed postcode with spaces) Given the grid with origin NL | 10 | | | | 11 | And the places | osm | class | type | name | addr+postcode | geometry | | W1 | highway | path | De Weide | | 10,11 | When importing When geocoding "3993 DX" Then result 0 contains | type | display_name | | postcode | 3993 DX, Nederland | When geocoding "3993dx" Then result 0 contains | type | display_name | | postcode | 3993 DX, Nederland | Examples: | postcode | | 3993 DX | | 3993DX | | 3993 dx | Scenario: Postcodes in Singapore (6-digit postcode) Given the grid with origin SG | 10 | | | | 11 | And the places | osm | class | type | name | addr+postcode | geometry | | W1 | highway | path | Lorang | 399174 | 10,11 | When importing When geocoding "399174" Then result 0 contains | type | display_name | | postcode | 399174, Singapore | Scenario Outline: Postcodes in Andorra (with country code) Given the grid with origin AD | 10 | | | | 11 | And the places | osm | class | type | name | addr+postcode | geometry | | W1 | highway | path | Lorang | | 10,11 | When importing When geocoding "675" Then result 0 contains | type | display_name | | postcode | AD675, Andorra | When geocoding "AD675" Then result 0 contains | type | display_name | | postcode | AD675, Andorra | Examples: | postcode | | 675 | | AD 675 | | AD675 | Scenario: Different postcodes with the same normalization can both be found Given the places | osm | class | type | addr+postcode | addr+housenumber | geometry | | N34 | place | house | EH4 7EA | 111 | country:gb | | N35 | place | house | E4 7EA | 111 | country:gb | When importing Then location_postcodes contains exactly | country_code | postcode | centroid!wkt | | gb | EH4 7EA | country:gb | | gb | E4 7EA | country:gb | When geocoding "EH4 7EA" Then result 0 contains | type | display_name | | postcode | EH4 7EA, United Kingdom | When geocoding "E4 7EA" Then result 0 contains | type | display_name | | postcode | E4 7EA, United Kingdom | ================================================ FILE: test/bdd/features/db/query/reverse.feature ================================================ Feature: Reverse searches Test results of reverse queries Scenario: POI in POI area Given the 0.0001 grid with origin 1,1 | 1 | | | | | | | | 2 | | | 9 | | | | | | | | | 4 | | | | | | | | 3 | And the places | osm | class | type | geometry | | W1 | aeroway | terminal | (1,2,3,4,1) | | N9 | amenity | restaurant | 9 | When importing And reverse geocoding 1.0001,1.0001 Then the result contains | object | | N9 | When reverse geocoding 1.0003,1.0001 Then the result contains | object | | W1 | Scenario: Find closest housenumber for street matches Given the 0.0001 grid with origin 1,1 | | 1 | | | | | | 2 | | | 10 | | | 11 | And the places | osm | class | type | name | geometry | | W1 | highway | service | Goose Drive | 10,11 | | N2 | tourism | art_work | Beauty | 2 | And the places | osm | class | type | housenr | geometry | | N1 | place | house | 23 | 1 | When importing When reverse geocoding 1.0002,1.0002 Then the result contains | object | | N1 | ================================================ FILE: test/bdd/features/db/query/search_simple.feature ================================================ Feature: Searching of simple objects Testing simple stuff Scenario: Search for place node Given the places | osm | class | type | name+name | geometry | | N1 | place | village | Foo | 10.0 -10.0 | When importing And geocoding "Foo" Then result 0 contains | object | category | type | centroid!wkt | | N1 | place | village | 10 -10 | # github #1763 Scenario: Correct translation of highways under construction Given the grid | 1 | | | | 2 | | | | 9 | | | And the places | osm | class | type | name | geometry | | W1 | highway | construction | The build | 1,2 | | N1 | amenity | cafe | Bean | 9 | When importing And geocoding "Bean" Then result 0 contains in field address | amenity | road | | Bean | The build | Scenario: when missing housenumbers in search don't return a POI Given the places | osm | class | type | name | | N3 | amenity | restaurant | Wood Street | And the places | osm | class | type | name | housenr | | N20 | amenity | restaurant | Red Way | 34 | When importing And geocoding "Wood Street 45" Then exactly 0 results are returned When geocoding "Red Way 34" Then exactly 0 results are returned Scenario: when the housenumber is missing the street is still returned Given the grid | 1 | | 2 | Given the places | osm | class | type | name | geometry | | W1 | highway | residential | Wood Street | 1, 2 | When importing And geocoding "Wood Street" Then all results contain | object | | W1 | Scenario Outline: Special cased american states will be found Given the grid | 1 | | 2 | | | 10 | | | 4 | | 3 | Given the places | osm | class | type | admin | name | name+ref | geometry | | R1 | boundary | administrative | 4 | | | (1,2,3,4,1) | Given the places | osm | class | type | name | geometry | | N2 | place | town | | 10 | | N3 | place | city | | country:ca | When importing And geocoding ", " Then all results contain | object | | N2 | When geocoding ", " | accept-language | | en | Then all results contain | object | | N2 | Examples: | city | state | ref | | Chicago | Illinois | IL | | Auburn | Alabama | AL | | New Orleans | Louisiana | LA | # github #3210 Scenario: Country with alternate-language name does not dominate when locale differs Given the 1.0 grid with origin DE | 1 | | 2 | | | 10 | | | 4 | | 3 | Given the places | osm | class | type | admin | name+name | name+name:fi | name+name:de | country | geometry | | R1 | boundary | administrative | 2 | Turgei | Turgi | Testland | de | (1,2,3,4,1) | Given the places | osm | class | type | name+name | geometry | | N10 | place | village | Turgi | 10 | When importing And geocoding "Turgi" | accept-language | | de | Then result 0 contains | object | | N10 | ================================================ FILE: test/bdd/features/db/update/addressing.feature ================================================ Feature: Updates for address computation Tests for correctly updating address assignments on changes. Scenario: Address gets updated when an area is extended Given the 0.0001 grid | 1 | | 2 | | 3 | | 4 | | | | | 9 | | | | | 8 | | 7 | | 6 | | 5 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 6 | Fooo | (1,4,5,8,1) | | R2 | boundary | administrative | 8 | Barr | (3,4,5,6,3) | And the named places | osm | class | type | geometry | | N1 | place | suburb | 9 | When importing Then place_addressline contains exactly | object | address | | N1 | R1 | | R2 | R1 | When updating places | osm | class | type | admin | name | geometry | | R2 | boundary | administrative | 8 | Barr | (2,4,5,7,2) | Then place_addressline contains exactly | object | address | | N1 | R1 | | N1 | R2 | | R2 | R1 | Scenario: Address gets updated when an area is reduced Given the 0.0001 grid | 1 | | 2 | | 3 | | 4 | | | | | 9 | | | | | 8 | | 7 | | 6 | | 5 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 6 | Fooo | (1,4,5,8,1) | | R2 | boundary | administrative | 8 | Barr | (2,4,5,7,2) | And the named places | osm | class | type | geometry | | N1 | place | suburb | 9 | When importing Then place_addressline contains exactly | object | address | | N1 | R1 | | N1 | R2 | | R2 | R1 | When updating places | osm | class | type | admin | name | geometry | | R2 | boundary | administrative | 8 | Barr | (3,4,5,6,3) | Then place_addressline contains exactly | object | address | | N1 | R1 | | R2 | R1 | Scenario: Address gets updated when an area disappears Given the 0.0001 grid | 1 | | 2 | | 3 | | 4 | | | | | 9 | | | | | 8 | | 7 | | 6 | | 5 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 6 | Fooo | (1,4,5,8,1) | | R2 | boundary | administrative | 8 | Barr | (2,4,5,7,2) | And the named places | osm | class | type | geometry | | N1 | place | suburb | 9 | When importing Then place_addressline contains exactly | object | address | | N1 | R1 | | N1 | R2 | | R2 | R1 | When updating places | osm | class | type | admin | name | geometry | | R2 | boundary | administrative | 15 | Barr | (2,4,5,7,2) | Then place_addressline contains exactly | object | address | | N1 | R1 | | R2 | R1 | | R2 | N1 | Scenario: Address gets updated when the admin level changes Given the 0.0001 grid | 1 | | 2 | | 3 | | 4 | | | | | 9 | | | | | 8 | | 7 | | 6 | | 5 | And the places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 6 | Fooo | (1,4,5,8,1) | | R2 | boundary | administrative | 8 | Barr | (2,4,5,7,2) | And the named places | osm | class | type | geometry | | N1 | place | suburb | 9 | When importing Then place_addressline contains exactly | object | address | cached_rank_address | | N1 | R1 | 12 | | N1 | R2 | 16 | | R2 | R1 | 12 | When updating places | osm | class | type | admin | name | geometry | | R1 | boundary | administrative | 5 | Fooo | (1,4,5,8,1) | Then place_addressline contains exactly | object | address | cached_rank_address | | N1 | R1 | 10 | | N1 | R2 | 16 | | R2 | R1 | 10 | ================================================ FILE: test/bdd/features/db/update/country.feature ================================================ Feature: Country handling Tests for update of country information Background: Given the 1.0 grid with origin DE | 1 | | 2 | | | 10 | | | 4 | | 3 | Scenario: When country names are changed old ones are no longer searchable Given the places | osm | class | type | admin | name+name:xy | country | geometry | | R1 | boundary | administrative | 2 | Loudou | de | (1,2,3,4,1) | Given the places | osm | class | type | name | | N10 | place | town | Wenig | When importing When geocoding "Wenig, Loudou" Then all results contain | object | | N10 | When updating places | osm | class | type | admin | name+name:xy | country | geometry | | R1 | boundary | administrative | 2 | Germany | de | (1,2,3,4,1) | When geocoding "Wenig, Loudou" Then exactly 0 results are returned Scenario: When country names are deleted they are no longer searchable Given the places | osm | class | type | admin | name+name:xy | country | geometry | | R1 | boundary | administrative | 2 | Loudou | de | (1,2,3,4,1) | Given the places | osm | class | type | name | | N10 | place | town | Wenig | When importing When geocoding "Wenig, Loudou" Then all results contain | object | | N10 | When updating places | osm | class | type | admin | name+name:en | country | geometry | | R1 | boundary | administrative | 2 | Germany | de | (1,2,3,4,1) | When geocoding "Wenig, Loudou" Then exactly 0 results are returned When geocoding "Wenig" | accept-language | | xy,en | Then all results contain | object | display_name | | N10 | Wenig, Germany | Scenario: Default country names are always searchable Given the places | osm | class | type | name | | N10 | place | town | Wenig | When importing When geocoding "Wenig, Germany" Then all results contain | object | | N10 | When geocoding "Wenig, de" Then all results contain | object | | N10 | When updating places | osm | class | type | admin | name+name:en | country | geometry | | R1 | boundary | administrative | 2 | Lilly | de | (1,2,3,4,1) | When geocoding "Wenig, Germany" | accept-language | | en,de | Then all results contain | object | display_name | | N10 | Wenig, Lilly | When geocoding "Wenig, de" | accept-language | | en,de | Then all results contain | object | display_name | | N10 | Wenig, Lilly | Scenario: When a localised name is deleted, the standard name takes over Given the places | osm | class | type | admin | name+name:de | country | geometry | | R1 | boundary | administrative | 2 | Loudou | de | (1,2,3,4,1) | Given the places | osm | class | type | name | | N10 | place | town | Wenig | When importing When geocoding "Wenig, Loudou" | accept-language | | de,en | Then all results contain | object | display_name | | N10 | Wenig, Loudou | When updating places | osm | class | type | admin | name+name:en | country | geometry | | R1 | boundary | administrative | 2 | Germany | de | (1,2,3,4,1) | When geocoding "Wenig, Loudou" Then exactly 0 results are returned When geocoding "Wenig" | accept-language | | de,en | Then all results contain | object | display_name | | N10 | Wenig, Deutschland | ================================================ FILE: test/bdd/features/db/update/entrances.feature ================================================ Feature: Entrance nodes are recorded Test that updated entrance nodes are saved Scenario: A building with a newly tagged entrance Given the grid | 1 | 2 | | 4 | 3 | Given the places | osm | class | type | geometry | | W1 | building | yes | (1,2,3,4,1) | And the ways | id | nodes | | 1 | 1, 2, 3, 4, 1 | When importing Then placex contains exactly | object | place_id | | W1 | 1 | Then placex_entrance contains exactly | place_id | osm_id | type | location!wkt | extratags | When updating entrances | osm | type | geometry | | N1 | main | 1 | And updating places | osm | class | type | geometry | | W1 | building | yes | (1,2,3,4,1) | Then placex contains exactly | object | place_id | | W1 | 1 | Then placex_entrance contains exactly | place_id | osm_id | type | location!wkt | extratags | | 1 | 1 | main | 1 | - | Scenario: A building with a updated entrance node Given the grid | 1 | 2 | | 4 | 3 | Given the places | osm | class | type | geometry | | N1 | barrier | gate | 1 | | W1 | building | yes | (1,2,3,4,1) | And the ways | id | nodes | | 1 | 1, 2, 3, 4, 1 | When importing Then placex contains exactly | object | place_id | | N1 | 1 | | W1 | 2 | Then placex_entrance contains exactly | place_id | osm_id | type | location!wkt | extratags | When marking for delete N1 And updating entrances | osm | type | geometry | | N1 | main | 1 | And updating places | osm | class | type | geometry | | W1 | building | yes | (1,2,3,4,1) | Then placex contains exactly | object | place_id | | W1 | 2 | And placex_entrance contains exactly | place_id | osm_id | type | location!wkt | extratags | | 2 | 1 | main | 1 | - | Scenario: A building with a removed entrance Given the grid | 1 | 2 | | 4 | 3 | Given the places | osm | class | type | geometry | | W1 | building | yes | (1,2,3,4,1) | And the entrances | osm | type | geometry | | N1 | main | 1 | And the ways | id | nodes | | 1 | 1, 2, 3, 4, 1 | When importing Then placex contains exactly | object | place_id | | W1 | 1 | And placex_entrance contains exactly | place_id | osm_id | type | location!wkt | extratags | | 1 | 1 | main | 1 | - | When marking for delete N1 And updating places | osm | class | type | geometry | | W1 | building | yes | (1,2,3,4,1) | Then placex contains exactly | object | place_id | | W1 | 1 | And placex_entrance contains exactly | place_id | osm_id | type | location!wkt | extratags | Scenario: A building with a removed and remaining entrance Given the grid | 1 | 2 | | 4 | 3 | Given the places | osm | class | type | geometry | | W1 | building | yes | (1,2,3,4,1) | Given the entrances | osm | type | geometry | | N1 | main | 1 | | N3 | yes | 3 | And the ways | id | nodes | | 1 | 1, 2, 3, 4, 1 | When importing Then placex contains exactly | object | place_id | | W1 | 1 | And placex_entrance contains exactly | place_id | osm_id | type | location!wkt | extratags | | 1 | 1 | main | 1 | - | | 1 | 3 | yes | 3 | - | When marking for delete N1 And updating places | osm | class | type | geometry | | W1 | building | yes | (1,2,3,4,1) | Then placex contains exactly | object | place_id | | W1 | 1 | And placex_entrance contains exactly | place_id | osm_id | type | location!wkt | extratags | | 1 | 3 | yes | 3 | - | ================================================ FILE: test/bdd/features/db/update/interpolation.feature ================================================ Feature: Update of address interpolations Test the interpolated address are updated correctly Scenario: new interpolation added to existing street Given the grid | 10 | | | | 11 | | | 1 | 99 | 2 | | | | | | | | | 20 | | | | 21 | And the places | osm | class | type | name | geometry | | W2 | highway | unclassified | Sun Way | 10,11 | | W3 | highway | unclassified | Cloud Street | 20,21 | When importing Then W10 expands to no interpolation When updating places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And updating interpolations | osm | type | geometry | nodes | | W10 | even | 1,2 | 1,2 | Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | And W10 expands to interpolation | parent_place_id | start | end | geometry | | W2 | 4 | 4 | 99 | Scenario: addr:street added to interpolation Given the grid | 10 | | | | 11 | | | 1 | | 2 | | | | | | | | | 20 | | | | 21 | And the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And the interpolations | osm | type | geometry | nodes | | W10 | even | 1,2 | 1,2 | And the places | osm | class | type | name | geometry | | W2 | highway | unclassified | Sun Way | 10,11 | | W3 | highway | unclassified | Cloud Street | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | And W10 expands to interpolation | parent_place_id | start | end | | W2 | 4 | 4 | When updating interpolations | osm | type | street | nodes | geometry | | W10 | even | Cloud Street | 1,2 | 1,2 | Then placex contains | object | parent_place_id | | N1 | W3 | | N2 | W3 | And W10 expands to interpolation | parent_place_id | start | end | | W3 | 4 | 4 | Scenario: addr:street added to housenumbers Given the grid | 10 | | | | 11 | | | 1 | | 2 | | | | | | | | | 20 | | | | 21 | And the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And the interpolations | osm | type | geometry | nodes | | W10 | even | 1,2 | 1,2 | And the places | osm | class | type | name | geometry | | W2 | highway | unclassified | Sun Way | 10,11 | | W3 | highway | unclassified | Cloud Street | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | And W10 expands to interpolation | parent_place_id | start | end | | W2 | 4 | 4 | When updating places | osm | class | type | street | housenr | | N1 | place | house | Cloud Street| 2 | | N2 | place | house | Cloud Street| 6 | Then placex contains | object | parent_place_id | | N1 | W3 | | N2 | W3 | And W10 expands to interpolation | parent_place_id | start | end | | W3 | 4 | 4 | Scenario: interpolation tag removed Given the grid | 10 | | | | 11 | | | 1 | | 2 | | | | | | | | | 20 | | | | 21 | And the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And the interpolations | osm | type | geometry | nodes | | W10 | even | 1,2 | 1,2 | And the places | osm | class | type | name | geometry | | W2 | highway | unclassified | Sun Way | 10,11 | | W3 | highway | unclassified | Cloud Street | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | And W10 expands to interpolation | parent_place_id | start | end | | W2 | 4 | 4 | When marking for delete W10 Then W10 expands to no interpolation And placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | Scenario: referenced road added Given the grid | 10 | | | | 11 | | | 1 | | 2 | | | | | | | | | 20 | | | | 21 | And the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And the interpolations | osm | type | street | geometry | nodes | | W10 | even | Cloud Street| 1,2 | 1,2 | And the places | osm | class | type | name | geometry | | W2 | highway | unclassified | Sun Way | 10,11 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | And W10 expands to interpolation | parent_place_id | start | end | | W2 | 4 | 4 | When updating places | osm | class | type | name | geometry | | W3 | highway | unclassified | Cloud Street | 20,21 | Then placex contains | object | parent_place_id | | N1 | W3 | | N2 | W3 | And W10 expands to interpolation | parent_place_id | start | end | | W3 | 4 | 4 | Scenario: referenced road deleted Given the grid | 10 | | | | 11 | | | 1 | | 2 | | | | | | | | | 20 | | | | 21 | And the places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And the interpolations | osm | type | street | geometry | nodes | | W10 | even | Cloud Street| 1,2 | 1,2 | And the places | osm | class | type | name | geometry | | W2 | highway | unclassified | Sun Way | 10,11 | | W3 | highway | unclassified | Cloud Street | 20,21 | When importing Then placex contains | object | parent_place_id | | N1 | W3 | | N2 | W3 | And W10 expands to interpolation | parent_place_id | start | end | | W3 | 4 | 4 | When marking for delete W3 Then placex contains | object | parent_place_id | | N1 | W2 | | N2 | W2 | And W10 expands to interpolation | parent_place_id | start | end | | W2 | 4 | 4 | Scenario: housenumbers added to interpolation Given the grid | 10 | | | | 11 | | | 1 | | 2 | | And the places | osm | class | type | name | geometry | | W2 | highway | unclassified | Cloud Street | 10,11 | And the interpolations | osm | type | geometry | nodes | | W1 | even | 1,2 | 1,2 | When importing Then W1 expands to no interpolation When updating places | osm | class | type | housenr | | N1 | place | house | 2 | | N2 | place | house | 6 | And updating interpolations | osm | type | geometry | nodes | | W1 | even | 1,2 | 1,2 | Then W1 expands to interpolation | parent_place_id | start | end | | W2 | 4 | 4 | Scenario: housenumber added in middle of interpolation Given the grid | 1 | | | | | 2 | | 3 | | | 4 | | 5 | And the places | osm | class | type | name | geometry | | W1 | highway | unclassified | Cloud Street | 1, 2 | And the interpolations | osm | type | geometry | nodes | | W2 | even | 3,4,5 | 3,4,5 | And the places | osm | class | type | housenr | | N3 | place | house | 2 | | N5 | place | house | 10 | When importing Then W2 expands to interpolation | parent_place_id | start | end | | W1 | 4 | 8 | When updating places | osm | class | type | housenr | | N4 | place | house | 6 | And updating interpolations | osm | type | geometry | nodes | | W2 | even | 3,4,5 | 3,4,5 | Then W2 expands to interpolation | parent_place_id | start | end | | W1 | 4 | 4 | | W1 | 8 | 8 | Scenario: housenumber removed in middle of interpolation Given the grid | 1 | | | | | 2 | | 3 | | | 4 | | 5 | And the places | osm | class | type | name | geometry | | W1 | highway | unclassified | Cloud Street | 1, 2 | And the interpolations | osm | type | geometry | nodes | | W2 | even | 3,4,5 | 3,4,5 | And the places | osm | class | type | housenr | | N3 | place | house | 2 | | N4 | place | house | 6 | | N5 | place | house | 10 | When importing Then W2 expands to interpolation | parent_place_id | start | end | | W1 | 4 | 4 | | W1 | 8 | 8 | When marking for delete N4 And updating interpolations | osm | type | geometry | nodes | | W2 | even | 3,4,5 | 3,4,5 | Then W2 expands to interpolation | parent_place_id | start | end | | W1 | 4 | 8 | Scenario: Change the start housenumber Given the grid | 1 | | 2 | | 3 | | 4 | And the places | osm | class | type | name | geometry | | W1 | highway | unclassified | Cloud Street | 1, 2 | And the interpolations | osm | type | geometry | nodes | | W2 | even | 3,4 | 3,4 | And the places | osm | class | type | housenr | | N3 | place | house | 2 | | N4 | place | house | 6 | When importing Then W2 expands to interpolation | parent_place_id | start | end | | W1 | 4 | 4 | When updating places | osm | class | type | housenr | | N4 | place | house | 8 | And updating interpolations | osm | type | geometry | nodes | | W2 | even | 3,4 | 3,4 | Then W2 expands to interpolation | parent_place_id | start | end | | W1 | 4 | 6 | ================================================ FILE: test/bdd/features/db/update/linked_places.feature ================================================ Feature: Updates of linked places Tests that linked places are correctly added and deleted. Scenario: Linking is kept when boundary is updated Given the 0.1 grid | 10 | | 11 | | | 1 | | | 13 | | 12 | Given the places | osm | class | type | name | | N1 | place | city | foo | And the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | When importing Then placex contains | object | linked_place_id | | N1 | R1 | When updating places | osm | class | type | name | name+name:de | admin | geometry | | R1 | boundary | administrative | foo | Dingens | 8 | (10,11,12,13,10) | Then placex contains | object | linked_place_id | | N1 | R1 | Scenario: Add linked place when linking relation is renamed Given the 0.1 grid | 10 | | 11 | | | 1 | | | 13 | | 12 | Given the places | osm | class | type | name | | N1 | place | city | foo | And the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | When importing Then placex contains | object | linked_place_id | | N1 | R1 | When geocoding "foo" | dups | | 1 | Then all results contain | object | | R1 | When updating places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foobar | 8 | (10,11,12,13,10) | Then placex contains | object | linked_place_id | | N1 | - | When geocoding "foo" | dups | | 1 | Then all results contain | object | | N1 | Scenario: Add linked place when linking relation is removed Given the 0.1 grid | 10 | | 11 | | | 1 | | | 13 | | 12 | Given the places | osm | class | type | name | | N1 | place | city | foo | And the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | When importing And geocoding "foo" | dups | | 1 | Then all results contain | object | | R1 | When marking for delete R1 Then placex contains | object | linked_place_id | | N1 | - | When geocoding "foo" | dups | | 1 | Then all results contain | object | | N1 | Scenario: Remove linked place when linking relation is added Given the 0.1 grid | 10 | | 11 | | | 1 | | | 13 | | 12 | Given the places | osm | class | type | name | | N1 | place | city | foo | When importing And geocoding "foo" | dups | | 1 | Then all results contain | object | | N1 | When updating places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | Then placex contains | object | linked_place_id | | N1 | R1 | When geocoding "foo" | dups | | 1 | Then all results contain | object | | R1 | Scenario: Remove linked place when linking relation is renamed Given the 0.1 grid | 10 | | 11 | | | 1 | | | 13 | | 12 | Given the places | osm | class | type | name | | N1 | place | city | foo | And the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foobar | 8 | (10,11,12,13,10) | When importing And geocoding "foo" | dups | | 1 | Then all results contain | object | | N1 | When updating places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | Then placex contains | object | linked_place_id | | N1 | R1 | When geocoding "foo" | dups | | 1 | Then all results contain | object | | R1 | Scenario: Update linking relation when linkee name is updated Given the 0.1 grid | 10 | | 11 | | | 3 | | | 13 | | 12 | Given the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | rel | 8 | (10,11,12,13,10) | And the places | osm | class | type | name+name:de | | N3 | place | city | greeny | And the relations | id | members | | 1 | N3:label | When importing Then placex contains | object | linked_place_id | name+_place_name:de | | R1 | - | greeny | And placex contains | object | linked_place_id | name+name:de | | N3 | R1 | greeny | When updating places | osm | class | type | name+name:de | | N3 | place | city | newname | Then placex contains | object | linked_place_id | name+name:de | | N3 | R1 | newname | And placex contains | object | linked_place_id | name+_place_name:de | | R1 | - | newname | Scenario: Update linking relation when linkee name is deleted Given the 0.1 grid | 10 | | 11 | | | 3 | | | 13 | | 12 | Given the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | rel | 8 | (10,11,12,13,10) | And the places | osm | class | type | name | | N3 | place | city | greeny | And the relations | id | members | | 1 | N3:label | When importing Then placex contains | object | linked_place_id | name+_place_name | name+name | | R1 | - | greeny | rel | And placex contains | object | linked_place_id | name+name | | N3 | R1 | greeny | When geocoding "greeny" Then all results contain | object | | R1 | When updating places | osm | class | type | name+name:de | | N3 | place | city | depnt | Then placex contains | object | linked_place_id | name+name:de | | N3 | R1 | depnt | And placex contains | object | linked_place_id | name+_place_name:de | name+name | | R1 | - | depnt | rel | When geocoding "greeny" Then exactly 0 results are returned Scenario: Updating linkee extratags keeps linker's extratags Given the 0.1 grid | 10 | | 11 | | | 3 | | | 13 | | 12 | Given the named places | osm | class | type | extra+wikidata | admin | geometry | | R1 | boundary | administrative | 34 | 8 | (10,11,12,13,10) | And the named places | osm | class | type | | N3 | place | city | And the relations | id | members | | 1 | N3:label | When importing Then placex contains | object | extratags!dict | | R1 | 'wikidata' : '34', 'linked_place' : 'city' | When updating places | osm | class | type | name | extra+oneway | | N3 | place | city | newname | yes | Then placex contains | object | extratags!dict | | R1 | 'wikidata' : '34', 'oneway' : 'yes', 'linked_place' : 'city' | Scenario: Remove linked_place info when linkee is removed Given the 0.1 grid | 10 | | 11 | | | 1 | | | 13 | | 12 | Given the places | osm | class | type | name | | N1 | place | city | foo | And the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | When importing Then placex contains | object | extratags!dict | | R1 | 'linked_place' : 'city' | When marking for delete N1 Then placex contains | object | extratags | | R1 | - | Scenario: Update linked_place info when linkee type changes Given the 0.1 grid | 10 | | 11 | | | 1 | | | 13 | | 12 | Given the places | osm | class | type | name | | N1 | place | city | foo | And the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | When importing Then placex contains | object | extratags!dict | | R1 | 'linked_place' : 'city' | When updating places | osm | class | type | name | | N1 | place | town | foo | Then placex contains | object | extratags!dict | | R1 | 'linked_place' : 'town' | Scenario: Keep linking and ranks when place type changes Given the grid | 1 | | | 2 | | | | 9 | | | 4 | | | 3 | And the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foo | 8 | (1,2,3,4,1) | And the places | osm | class | type | name | geometry | | N1 | place | city | foo | 9 | When importing Then placex contains | object | linked_place_id | rank_address | | N1 | R1 | 16 | | R1 | - | 16 | When updating places | osm | class | type | name | geometry | | N1 | place | town | foo | 9 | Then placex contains | object | linked_place_id | rank_address | | N1 | R1 | 16 | | R1 | - | 16 | Scenario: Invalidate surrounding place nodes when place type changes Given the grid | 1 | | | 2 | | | 8 | 9 | | | 4 | | | 3 | And the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | foo | 8 | (1,2,3,4,1) | And the places | osm | class | type | name | geometry | | N1 | place | town | foo | 9 | | N2 | place | city | bar | 8 | And the relations | id | members | | 1 | N1:label | When importing Then placex contains | object | linked_place_id | rank_address | | N1 | R1 | 16 | | R1 | - | 16 | | N2 | - | 18 | When updating places | osm | class | type | name | geometry | | N1 | place | suburb | foo | 9 | Then placex contains | object | linked_place_id | rank_address | | N1 | R1 | 20 | | R1 | - | 20 | | N2 | - | 16 | ================================================ FILE: test/bdd/features/db/update/parenting.feature ================================================ Feature: Update parenting of objects Scenario: POI inside building inherits addr:street change Given the grid | 10 | | | | | | | 11 | | | | 5 | | | 6 | | | | | | | | | | | | | | | | | 1 | | | | | 12 | | 8 | | | 7 | | | And the named places | osm | class | type | | N1 | amenity | bank | And the places | osm | class | type | street | housenr | geometry | | W1 | building | yes | nowhere | 3 | (5,6,7,8,5) | And the places | osm | class | type | name | geometry | | W2 | highway | primary | bar | 10,11 | | W3 | highway | residential | foo | 10,12 | When importing Then placex contains | object | parent_place_id | housenumber | | W1 | W2 | 3 | | N1 | W2 | 3 | When updating places | osm | class | type | street | addr_place | housenr | geometry | | W1 | building | yes | foo | nowhere | 3 | (5,6,7,8,5) | And updating places | osm | class | type | name | | N1 | amenity | bank | well | Then placex contains | object | parent_place_id | housenumber | | W1 | W3 | 3 | | N1 | W3 | 3 | Scenario: Housenumber is reparented when street gets name matching addr:street Given the grid | 1 | | | 2 | | | 10 | | | | | | | | | 3 | | | 4 | And the places | osm | class | type | name | geometry | | W1 | highway | residential | A street | 1,2 | | W2 | highway | residential | B street | 3,4 | And the places | osm | class | type | housenr | street | geometry | | N1 | building | yes | 3 | X street | 10 | When importing Then placex contains | object | parent_place_id | | N1 | W1 | When updating places | osm | class | type | name | geometry | | W2 | highway | residential | X street | 3,4 | Then placex contains | object | parent_place_id | | N1 | W2 | Scenario: Housenumber is reparented when street looses name matching addr:street Given the grid | 1 | | | 2 | | | 10 | | | | | | | | | 3 | | | 4 | And the places | osm | class | type | name | geometry | | W1 | highway | residential | A street | 1,2 | | W2 | highway | residential | X street | 3,4 | And the places | osm | class | type | housenr | street | geometry | | N1 | building | yes | 3 | X street | 10 | When importing Then placex contains | object | parent_place_id | | N1 | W2 | When updating places | osm | class | type | name | geometry | | W2 | highway | residential | B street | 3,4 | Then placex contains | object | parent_place_id | | N1 | W1 | Scenario: Housenumber is reparented when street gets name matching addr:street Given the grid | 1 | | | 2 | | | 10 | | | | | | | | | 3 | | | 4 | And the places | osm | class | type | name | geometry | | W1 | highway | residential | A street | 1,2 | | W2 | highway | residential | B street | 3,4 | And the places | osm | class | type | housenr | street | geometry | | N1 | building | yes | 3 | X street | 10 | When importing Then placex contains | object | parent_place_id | | N1 | W1 | When updating places | osm | class | type | name | geometry | | W2 | highway | residential | X street | 3,4 | Then placex contains | object | parent_place_id | | N1 | W2 | Scenario: Housenumber is reparented when place is renamed to matching addr:place Given the grid | 1 | | | 2 | | | 10 | 4 | | | | | | | | | | 5 | | And the places | osm | class | type | name | geometry | | W1 | highway | residential | A street | 1,2 | | N5 | place | village | Bdorf | 5 | | N4 | place | village | Other | 4 | And the places | osm | class | type | housenr | addr_place | geometry | | N1 | building | yes | 3 | Cdorf | 10 | When importing Then placex contains | object | parent_place_id | | N1 | N4 | When updating places | osm | class | type | name | geometry | | N5 | place | village | Cdorf | 5 | Then placex contains | object | parent_place_id | | N1 | N5 | Scenario: Housenumber is reparented when it looses a matching addr:place Given the grid | 1 | | | 2 | | | 10 | 4 | | | | | | | | | | 5 | | And the places | osm | class | type | name | geometry | | W1 | highway | residential | A street | 1,2 | | N5 | place | village | Bdorf | 5 | | N4 | place | village | Other | 4 | And the places | osm | class | type | housenr | addr_place | geometry | | N1 | building | yes | 3 | Bdorf | 10 | When importing Then placex contains | object | parent_place_id | | N1 | N5 | When updating places | osm | class | type | name | geometry | | N5 | place | village | Cdorf | 5 | Then placex contains | object | parent_place_id | | N1 | N4 | Scenario: House re-parented when added to an associatedStreet relation Given the grid | 10 | | | | | 11 | | | 1 | | | | | | | | | | | | | 12 | | | | | | And the places | osm | class | type | | N1 | place | house | And the places | osm | class | type | name | geometry | | W1 | highway | residential | foo | 10,11 | | W2 | highway | residential | bar | 10,12 | And the relations | id | members | tags+type | | 1 | W1:street | associatedStreet | When importing Then placex contains | object | parent_place_id | | N1 | W1 | When updating relations | id | members | tags+type | | 1 | W2:street,N1:house | associatedStreet | Then placex contains | object | parent_place_id | | N1 | W2 | Scenario: House reverts to nearest street when removed from associatedStreet relation Given the grid | 10 | | | | | 11 | | | | | | | | | | | | | | | | | | | | | | | 12 | 1 | | | | | And the places | osm | class | type | | N1 | place | house | And the places | osm | class | type | name | geometry | | W1 | highway | residential | foo | 10,11 | | W2 | highway | residential | bar | 10,12 | And the relations | id | members | tags+type | | 1 | W1:street,N1:house | associatedStreet | When importing Then placex contains | object | parent_place_id | | N1 | W1 | When updating relations | id | members | tags+type | | 1 | W1:street | associatedStreet | Then placex contains | object | parent_place_id | | N1 | W2 | Scenario: Houses re-parented when street member of relation changes Given the grid | 10 | | | | | 11 | | | 1 | | | | | | | | | | | | | 12 | | | | | | And the places | osm | class | type | | N1 | place | house | And the places | osm | class | type | name | geometry | | W1 | highway | residential | foo | 10,11 | | W2 | highway | residential | bar | 10,12 | And the relations | id | members | tags+type | | 1 | W1:street,N1:house | associatedStreet | When importing Then placex contains | object | parent_place_id | | N1 | W1 | When updating relations | id | members | tags+type | | 1 | W2:street,N1:house | associatedStreet | Then placex contains | object | parent_place_id | | N1 | W2 | ================================================ FILE: test/bdd/features/db/update/postcode.feature ================================================ Feature: Update of postcode Tests for updating of data related to postcodes Scenario: Updating postcode in postcode boundaries without ref Given the grid with origin FR | 1 | | 2 | | | 9 | | | 4 | | 3 | Given the postcodes | osm | postcode | centroid | geometry | | R1 | 12345 | 9 | (1,2,3,4,1) | When importing And geocoding "12345" Then result 0 contains | object | | R1 | Given the postcodes | osm | postcode | centroid | geometry | | R1 | 54321 | 9 | (1,2,3,4,1) | When refreshing postcodes And geocoding "12345" Then exactly 0 results are returned When geocoding "54321" Then result 0 contains | object | | R1 | Scenario: A new postcode appears in the postcode table Given the places | osm | class | type | addr+postcode | addr+housenumber | geometry | | N34 | place | house | 01982 | 111 | country:de | When importing Then location_postcodes contains exactly | country_code | postcode | centroid!wkt | | de | 01982 | country:de | Given the postcodes | osm | postcode | centroid | | N66 | 99201 | country:fr | When updating places | osm | class | type | addr+postcode | addr+housenumber | geometry | | N35 | place | house | 4567 | 5 | country:ch | And refreshing postcodes Then location_postcodes contains exactly | country_code | postcode | centroid!wkt | | de | 01982 | country:de | | ch | 4567 | country:ch | | fr | 99201 | country:fr | Scenario: When the last postcode is deleted, it is deleted from postcode Given the places | osm | class | type | addr+postcode | addr+housenumber | geometry | | N34 | place | house | 01982 | 111 | country:de | | N35 | place | house | 4567 | 5 | country:ch | When importing And marking for delete N34 And refreshing postcodes Then location_postcodes contains exactly | country_code | postcode | centroid!wkt | | ch | 4567 | country:ch | Scenario: A postcode is not deleted from postcode when it exist in another country Given the places | osm | class | type | addr+postcode | addr+housenumber | geometry | | N34 | place | house | 01982 | 111 | country:de | | N35 | place | house | 01982 | 5 | country:fr | When importing And marking for delete N34 And refreshing postcodes Then location_postcodes contains exactly | country_code | postcode | centroid!wkt| | fr | 01982 | country:fr | Scenario: Updating a postcode is reflected in postcode table Given the places | osm | class | type | addr+postcode | geometry | | N34 | place | postcode | 01982 | country:de | When importing And updating places | osm | class | type | addr+postcode | geometry | | N34 | place | postcode | 20453 | country:de | And refreshing postcodes Then location_postcodes contains exactly | country_code | postcode | centroid!wkt | | de | 20453 | country:de | Scenario: When a parent is deleted, the postcode gets a new parent Given the grid with origin DE | 1 | | 3 | 4 | | | 9 | | | | 2 | | 5 | 6 | Given the places | osm | class | type | name | admin | geometry | | R1 | boundary | administrative | Big | 6 | (1,4,6,2,1) | | R2 | boundary | administrative | Small | 6 | (1,3,5,2,1) | Given the postcodes | osm | postcode | centroid | | N9 | 12345 | 9 | When importing Then location_postcodes contains exactly | postcode | centroid!wkt | parent_place_id | | 12345 | 9 | R2 | When marking for delete R2 Then location_postcodes contains exactly | country_code | postcode | centroid!wkt | parent_place_id | | de | 12345 | 9 | R1 | Scenario: When a postcode area appears, postcode points are shadowed Given the grid with origin DE | 1 | | 3 | | | | 9 | | 8 | | 2 | | 5 | | Given the postcodes | osm | postcode | centroid | | N92 | 44321 | 9 | | N4 | 00245 | 8 | When importing Then location_postcodes contains exactly | country_code | postcode | osm_id | centroid!wkt | | de | 44321 | - | 9 | | de | 00245 | - | 8 | Given the postcodes | osm | postcode | centroid | geometry | | R45 | 00245 | 9 | (1,3,5,2,1) | When refreshing postcodes Then location_postcodes contains exactly | country_code | postcode | osm_id | centroid!wkt | | de | 00245 | 45 | 9 | Scenario: When a postcode area disappears, postcode points are unshadowed Given the grid with origin DE | 1 | | 3 | | | | 9 | | 8 | | 2 | | 5 | | Given the postcodes | osm | postcode | centroid | geometry | | R45 | 00245 | 9 | (1,3,5,2,1) | Given the postcodes | osm | postcode | centroid | | N92 | 44321 | 9 | | N4 | 00245 | 8 | When importing Then location_postcodes contains exactly | country_code | postcode | osm_id | centroid!wkt | | de | 00245 | 45 | 9 | When marking for delete R45 And refreshing postcodes Then location_postcodes contains exactly | country_code | postcode | osm_id | centroid!wkt | | de | 44321 | - | 9 | | de | 00245 | - | 8 | ================================================ FILE: test/bdd/features/db/update/simple.feature ================================================ Feature: Update of simple objects Testing simple updating functionality Scenario: Do delete small boundary features Given the 1.0 grid | 1 | 2 | | 4 | 3 | Given the places | osm | class | type | admin | geometry | | R1 | boundary | administrative | 3 | (1,2,3,4,1) | When importing Then placex contains | object | rank_search | | R1 | 6 | When marking for delete R1 Then placex has no entry for R1 Scenario: Do not delete large boundary features Given the 2.0 grid | 1 | 2 | | 4 | 3 | Given the places | osm | class | type | admin | geometry | | R1 | boundary | administrative | 3 | (1,2,3,4,1) | When importing Then placex contains | object | rank_search | | R1 | 6 | When marking for delete R1 Then placex contains | object | rank_search | | R1 | 6 | Scenario: Do delete large features of low rank Given the 2.0 grid | 1 | 2 | | 4 | 3 | Given the named places | osm | class | type | geometry | | W1 | place | house | (1,2,3,4,1) | | R1 | natural | wood | (1,2,3,4,1) | | R2 | highway | residential | (1,2,3,4,1) | When importing Then placex contains | object | rank_address | | R1 | 0 | | R2 | 26 | | W1 | 30 | When marking for delete R1 And marking for delete R2 And marking for delete W1 Then placex has no entry for W1 Then placex has no entry for R1 Then placex has no entry for R2 Scenario: type mutation Given the places | osm | class | type | geometry | | N3 | shop | toys | 1 -1 | When importing Then placex contains | object | class | type | centroid!wkt | | N3 | shop | toys | 1 -1 | When updating places | osm | class | type | geometry | | N3 | shop | grocery | 1 -1 | Then placex contains | object | class | type | centroid!wkt | | N3 | shop | grocery | 1 -1 | Scenario: remove boundary when changing from polygon to way Given the grid | 1 | 2 | | 3 | 4 | And the places | osm | class | type | name | admin | geometry | | W1 | boundary | administrative | Haha | 5 | (1, 2, 4, 3, 1) | When importing Then placex contains | object | | W1 | When updating places | osm | class | type | name | admin | geometry | | W1 | boundary | administrative | Haha | 5 | 1, 2, 4, 3 | Then placex has no entry for W1 #895 Scenario: update rank when boundary is downgraded from admin to historic Given the grid | 1 | 2 | | 3 | 4 | And the places | osm | class | type | name | admin | geometry | | W1 | boundary | administrative | Haha | 5 | (1, 2, 4, 3, 1) | When importing Then placex contains | object | rank_address | | W1 | 10 | When updating places | osm | class | type | name | admin | geometry | | W1 | boundary | historic | Haha | 5 | (1, 2, 4, 3, 1) | Then placex contains | object | rank_address | | W1 | 0 | ================================================ FILE: test/bdd/features/osm2pgsql/import/broken.feature ================================================ Feature: Import of objects with broken geometries by osm2pgsql Scenario: Import way with double nodes When loading osm data """ n100 x0 y0 n101 x0 y0.1 n102 x0.1 y0.2 w1 Thighway=primary Nn100,n101,n101,n102 """ Then place contains | object | class | type | geometry!wkt | | W1 | highway | primary | 0 0, 0 0.1, 0.1 0.2 | Scenario: Import of ballon areas Given the grid | 2 | | 3 | | 1 | | 4 | | 5 | | | When loading osm data """ n1 n2 n3 n4 n5 w1 Thighway=unclassified Nn1,n2,n3,n4,n1,n5 w2 Thighway=unclassified Nn1,n2,n3,n4,n1 w3 Thighway=unclassified Nn1,n2,n3,n4,n3 """ Then place contains | object | geometry!wkt | | W1 | 1,2,3,4,1,5 | | W2 | (1,2,3,4,1) | | W3 | 1,2,3,4 | ================================================ FILE: test/bdd/features/osm2pgsql/import/custom_style.feature ================================================ Feature: Import with custom styles by osm2pgsql Tests for the example customizations given in the documentation. Scenario: Custom main tags (set new ones) Given the lua style file """ local flex = require('import-full') flex.set_main_tags{ boundary = {administrative = 'named'}, highway = {'always', street_lamp = 'named'}, landuse = 'fallback' } """ When loading osm data """ n10 Tboundary=administrative x0 y0 n11 Tboundary=administrative,name=Foo x0 y0 n12 Tboundary=electoral x0 y0 n13 Thighway=primary x0 y0 n14 Thighway=street_lamp x0 y0 n15 Thighway=primary,landuse=street x0 y0 """ Then place contains exactly | object | class | type | | N11 | boundary | administrative | | N13 | highway | primary | | N15 | highway | primary | Scenario: Custom main tags (modify existing) Given the lua style file """ local flex = require('import-full') flex.modify_main_tags{ amenity = {prison = 'delete'}, highway = {stop = 'named'}, aeroway = 'named' } """ When loading osm data """ n10 Tamenity=hotel x0 y0 n11 Tamenity=prison x0 y0 n12 Thighway=stop x0 y0 n13 Thighway=stop,name=BigStop x0 y0 n14 Thighway=give_way x0 y0 n15 Thighway=bus_stop x0 y0 n16 Taeroway=no,name=foo x0 y0 n17 Taeroway=taxiway,name=D15 x0 y0 """ Then place contains exactly | object | class | type | | N10 | amenity | hotel | | N13 | highway | stop | | N15 | highway | bus_stop | | N17 | aeroway | taxiway | Scenario: Prefiltering tags Given the lua style file """ local flex = require('import-full') flex.set_prefilters{ delete_keys = {'source', 'source:*'}, extra_tags = {amenity = {'yes', 'no'}} } flex.set_main_tags{ amenity = 'always', tourism = 'always' } """ When loading osm data """ n1 Tamenity=yes x0 y6 n2 Tamenity=hospital,source=survey x3 y6 n3 Ttourism=hotel,amenity=yes x0 y0 n4 Ttourism=hotel,amenity=telephone x0 y0 """ Then place contains exactly | object | class | extratags!dict | | N2 | amenity | - | | N3 | tourism | 'amenity': 'yes' | | N4 | tourism | - | | N4 | amenity | - | Scenario: Ignore some tags Given the lua style file """ local flex = require('import-extratags') flex.ignore_keys{'ref:*', 'surface'} """ When loading osm data """ n100 Thighway=residential,ref=34,ref:bodo=34,surface=gray,extra=1 x0 y0 """ Then place contains exactly | object | name!dict | extratags!dict | | N100 | 'ref' : '34' | 'extra': '1' | Scenario: Add for extratags Given the lua style file """ local flex = require('import-full') flex.add_for_extratags{'ref:*', 'surface'} """ When loading osm data """ n100 Thighway=residential,ref=34,ref:bodo=34,surface=gray,extra=1 x0 y0 """ Then place contains exactly | object | name!dict | extratags!dict | | N100 | 'ref' : '34' | 'ref:bodo': '34', 'surface': 'gray' | Scenario: Name tags Given the lua style file """ local flex = require('flex-base') flex.set_main_tags{highway = {traffic_light = 'named'}} flex.set_name_tags{main = {'name', 'name:*'}, extra = {'ref'} } """ When loading osm data """ n1 Thighway=stop,name=Something x0 y0 n2 Thighway=traffic_light,ref=453-4 x0 y0 n3 Thighway=traffic_light,name=Greens x0 y0 n4 Thighway=traffic_light,name=Red,ref=45 x0 y0 """ Then place contains exactly | object | class | name!dict | | N3 | highway | 'name': 'Greens' | | N4 | highway | 'name': 'Red', 'ref': '45' | Scenario: Modify name tags Given the lua style file """ local flex = require('import-full') flex.modify_name_tags{house = {}, extra = {'o'}} """ When loading osm data """ n1 Ttourism=hotel,ref=45,o=good n2 Taddr:housename=Old,addr:street=Away """ Then place contains exactly | object | class | name!dict | | N1 | tourism | 'o': 'good' | Scenario: Address tags Given the lua style file """ local flex = require('import-full') flex.set_address_tags{ main = {'addr:housenumber'}, extra = {'addr:*'}, postcode = {'postal_code', 'postcode', 'addr:postcode'}, country = {'country-code', 'ISO3166-1'} } """ When loading osm data """ n1 Ttourism=hotel,addr:street=Foo x0 y0 n2 Taddr:housenumber=23,addr:street=Budd,postal_code=5567 x0 y0 n3 Taddr:street=None,addr:city=Where x0 y0 """ Then place contains exactly | object | class | type | address!dict | | N1 | tourism | hotel | 'street': 'Foo' | | N2 | place | house | 'housenumber': '23', 'street': 'Budd', 'postcode': '5567' | Scenario: Modify address tags Given the lua style file """ local flex = require('import-full') flex.set_address_tags{ extra = {'addr:*'}, } """ When loading osm data """ n2 Taddr:housenumber=23,addr:street=Budd,is_in:city=Faraway,postal_code=5567 x0 y0 """ Then place contains exactly | object | class | type | address!dict | | N2 | place | house | 'housenumber': '23', 'street': 'Budd', 'postcode': '5567' | Scenario: Unused handling (delete) Given the lua style file """ local flex = require('import-full') flex.set_address_tags{ main = {'addr:housenumber'}, extra = {'addr:*', 'tiger:county'} } flex.set_unused_handling{delete_keys = {'tiger:*'}} """ When loading osm data """ n1 Ttourism=hotel,tiger:county=Fargo x0 y0 n2 Ttourism=hotel,tiger:xxd=56,else=other x0 y0 """ Then place contains exactly | object | class | type | address!dict | extratags!dict | | N1 | tourism | hotel | 'tiger:county': 'Fargo' | - | | N2 | tourism | hotel | - | 'else': 'other' | Scenario: Unused handling (extra) Given the lua style file """ local flex = require('flex-base') flex.set_main_tags{highway = 'always', wikipedia = 'extra'} flex.add_for_extratags{'wikipedia:*', 'wikidata'} flex.set_unused_handling{extra_keys = {'surface'}} """ When loading osm data """ n100 Thighway=path,foo=bar,wikipedia=en:Path x0 y0 n234 Thighway=path,surface=rough x0 y0 n445 Thighway=path,name=something x0 y0 n446 Thighway=path,wikipedia:en=Path,wikidata=Q23 x0 y0 n567 Thighway=path,surface=dirt,wikipedia:en=Path x0 y0 """ Then place contains exactly | object | class | type | extratags!dict | | N100 | highway | path | 'wikipedia': 'en:Path' | | N234 | highway | path | 'surface': 'rough' | | N445 | highway | path | - | | N446 | highway | path | 'wikipedia:en': 'Path', 'wikidata': 'Q23' | | N567 | highway | path | 'surface': 'dirt', 'wikipedia:en': 'Path' | Scenario: Additional relation types Given the lua style file """ local flex = require('import-full') flex.RELATION_TYPES['site'] = flex.relation_as_multipolygon """ And the grid | 1 | 2 | | 4 | 3 | When loading osm data """ n1 n2 n3 n4 w1 Nn1,n2,n3,n4,n1 r1 Ttype=multipolygon,amenity=school Mw1@ r2 Ttype=site,amenity=school Mw1@ """ Then place contains exactly | object | class | type | | R1 | amenity | school | | R2 | amenity | school | Scenario: Exclude country relations Given the lua style file """ local flex = require('import-full') function osm2pgsql.process_relation(object) if object.tags.boundary ~= 'administrative' or object.tags.admin_level ~= '2' then flex.process_relation(object) end end """ And the grid | 1 | 2 | | 4 | 3 | When loading osm data """ n1 n2 n3 n4 w1 Nn1,n2,n3,n4,n1 r1 Ttype=multipolygon,boundary=administrative,admin_level=4,name=Small Mw1@ r2 Ttype=multipolygon,boundary=administrative,admin_level=2,name=Big Mw1@ """ Then place contains exactly | object | class | type | | R1 | boundary | administrative | Scenario: Customize processing functions Given the lua style file """ local flex = require('import-full') local original_process_tags = flex.process_tags function flex.process_tags(o) if o.object.tags.highway ~= nil and o.object.tags.access == 'no' then return end original_process_tags(o) end """ When loading osm data """ n1 Thighway=residential x0 y0 n2 Thighway=residential,access=no x0 y0 """ Then place contains exactly | object | class | type | | N1 | highway | residential | ================================================ FILE: test/bdd/features/osm2pgsql/import/entrances.feature ================================================ Feature: Import of entrance objects by osm2pgsql Testing of correct setup of the entrance table Scenario: Import simple entrance When loading osm data """ n1 Tshop=sweets,entrance=yes,access=public x4.5 y-4 n2 Trouting:entrance=main x66.1 y0.1 n3 Tentrance=main,routing:entrance=foot x1 y2 n4 Thighway=bus_stop """ Then place contains exactly | object | class | type | | N1 | shop | sweets | | N4 | highway | bus_stop | And place_entrance contains exactly | osm_id | type | extratags!dict | geometry!wkt | | 1 | yes | 'shop': 'sweets', 'access': 'public' | 4.5 -4 | | 2 | main | - | 66.1 0.1 | | 3 | main | - | 1 2 | Scenario: Addresses and entrance information can exist on the same node When loading osm data """ n1 Taddr:housenumber=10,addr:street=North,entrance=main """ Then place contains exactly | object | class | type | address+housenumber | | N1 | place | house | 10 | And place_entrance contains exactly | osm_id | type | | 1 | main | Scenario Outline: Entrance import can be disabled Given the lua style file """ local flex = require('import-full') flex.set_entrance_filter """ When loading osm data """ n1 Tentrance=yes,access=public n2 Trouting:entrance=main """ Then place contains exactly | object | And place_entrance contains exactly | osm_id | Examples: | param | | () | | (nil) | | {} | | {include={'access'}} | | {main_tags={}} | Scenario: Entrance import can have custom main tags Given the lua style file """ local flex = require('import-full') flex.set_entrance_filter{main_tags = {'door'}} """ When loading osm data """ n1 Tentrance=yes,access=public n2 Tdoor=foot,entrance=yes """ Then place contains exactly | object | And place_entrance contains exactly | osm_id | type | extratags!dict | | 2 | foot | 'entrance': 'yes' | Scenario: Entrance import can have custom extra tags included Given the lua style file """ local flex = require('import-full') flex.set_entrance_filter{main_tags = {'entrance'}, extra_include = {'access'}} """ When loading osm data """ n1 Tentrance=yes,access=public,shop=newspaper n2 Tentrance=yes,shop=sweets """ Then place_entrance contains exactly | osm_id | type | extratags!dict | | 1 | yes | 'access': 'public' | | 2 | yes | - | Scenario: Entrance import can have custom extra tags excluded Given the lua style file """ local flex = require('import-full') flex.set_entrance_filter{main_tags = {'entrance', 'door'}, extra_exclude = {'shop'}} """ When loading osm data """ n1 Tentrance=yes,access=public,shop=newspaper n2 Tentrance=yes,door=yes,shop=sweets """ Then place_entrance contains exactly | osm_id | type | extratags!dict | | 1 | yes | 'access': 'public' | | 2 | yes | - | Scenario: Entrance import can have a custom function Given the lua style file """ local flex = require('import-full') flex.set_entrance_filter(function(object) return {entrance='always', extratags = {ref = '1'}} end) """ When loading osm data """ n1 Tentrance=yes,access=public,shop=newspaper n2 Tshop=sweets """ Then place_entrance contains exactly | osm_id | type | extratags!dict | | 1 | always | 'ref': '1' | | 2 | always | 'ref': '1' | ================================================ FILE: test/bdd/features/osm2pgsql/import/interpolation.feature ================================================ Feature: Import of interpolations Test if interpolation objects are correctly imported into the place_interpolation table Background: Given the grid | 1 | 2 | | 4 | 3 | Scenario: Simple address interpolations When loading osm data """ n1 n2 w13001 Taddr:interpolation=odd,addr:street=Blumenstrasse Nn1,n2 w13002 Taddr:interpolation=even,place=city Nn1,n2 w13003 Taddr:interpolation=odd Nn1,n1 """ Then place contains exactly | object | class | type | | W13002 | place | city | And place_interpolation contains exactly | osm_id | type | address!dict | nodes!ints | geometry!wkt | | 13001 | odd | "street": "Blumenstrasse" | 1,2 | 1,2 | | 13002 | even | - | 1,2 | 1,2 | Scenario: Address interpolation with housenumber When loading osm data """ n1 n2 n3 n4 w34 Taddr:interpolation=all,addr:housenumber=2-4,building=yes Nn1,n2,n3,n4,n1 w35 Taddr:interpolation=all,addr:housenumber=5,building=yes Nn1,n2,n3,n4,n1 w36 Taddr:interpolation=all,addr:housenumber=2a-c Nn1,n2,n3,n4,n1 """ Then place contains exactly | object | class | type | address!dict | | W35 | building | yes | "housenumber": "5", "interpolation": "all" | | W34 | building | yes | "housenumber": "2-4", "interpolation": "all" | | W36 | place | house | "housenumber": "2a-c", "interpolation": "all" | ================================================ FILE: test/bdd/features/osm2pgsql/import/relation.feature ================================================ Feature: Import of relations by osm2pgsql Testing specific relation problems related to members. Scenario: Don't import empty waterways When loading osm data """ n1 Tamenity=prison,name=foo r1 Ttype=waterway,waterway=river,name=XZ Mn1@ """ Then place has no entry for R1 ================================================ FILE: test/bdd/features/osm2pgsql/import/simple.feature ================================================ Feature: Import of simple objects by osm2pgsql Testing basic tagging in osm2pgsql imports. Scenario: Import simple objects When loading osm data """ n1 Tamenity=prison,name=foo x34.3 y-23 n100 x0 y0 n101 x0 y0.1 n102 x0.1 y0.2 n200 x0 y0 n201 x0 y1 n202 x1 y1 n203 x1 y0 w1 Tshop=toys,name=tata Nn100,n101,n102 w2 Tref=45 Nn200,n201,n202,n203,n200 r1 Ttype=multipolygon,tourism=hotel,name=XZ Mn1@,w2@ """ Then place contains exactly | object | class | type | name!dict | geometry!wkt | | N1 | amenity | prison | 'name' : 'foo' | 34.3 -23 | | W1 | shop | toys | 'name' : 'tata' | 0 0, 0 0.1, 0.1 0.2 | | R1 | tourism | hotel | 'name' : 'XZ' | (0 0, 0 1, 1 1, 1 0, 0 0) | Scenario: Import object with two main tags When loading osm data """ n1 Ttourism=hotel,amenity=restaurant,name=foo """ Then place contains exactly | object | class | type | name!dict | | N1 | tourism | hotel | 'name' : 'foo' | | N1 | amenity | restaurant | 'name' : 'foo' | Scenario: Import stand-alone house number with postcode When loading osm data """ n1 Taddr:housenumber=4,addr:postcode=3345 """ Then place contains exactly | object | class | type | | N1 | place | house | Scenario Outline: Tags used by Nominatim internally are always imported Given the lua style file """ local flex = require('import-