]> git.openstreetmap.org Git - nominatim.git/blob - docs/library/Getting-Started.md
Merge pull request #3499 from mtmail/add-data-warn-if-frozen
[nominatim.git] / docs / library / Getting-Started.md
1 # Getting Started
2
3 The Nominatim search frontend can directly be used as a Python library in
4 scripts and applications. When you have imported your own Nominatim database,
5 then it is no longer necessary to run a full web service for it and access
6 the database through http requests. There are
7 also less constraints on the kinds of data that can be accessed. The library
8 allows to get access to more detailed information about the objects saved
9 in the database.
10
11 !!! danger
12     The library interface is currently in an experimental stage. There might
13     be some smaller adjustments to the public interface until the next version.
14
15 ## Installation
16
17 To use the Nominatim library, you need access to a local Nominatim database.
18 Follow the [installation](../admin/Installation.md) and
19 [import](../admin/Import.md) instructions to set up your database.
20
21 The Nominatim frontend library is contained in the Python package `nominatim-api`.
22 To install the package from the source tree directly, run:
23
24     pip install packaging/nominatim-api
25
26 Usually you would want to run this in a virtual environment.
27
28 ### A simple search example
29
30 To query the Nominatim database you need to first set up a connection. This
31 is done by creating an Nominatim API object. This object exposes all the
32 search functions of Nominatim that are also known from its web API.
33
34 This code snippet implements a simple search for the town of 'Brugge':
35
36 !!! example
37     === "NominatimAPIAsync"
38         ``` python
39         from pathlib import Path
40         import asyncio
41
42         import nominatim_api as napi
43
44         async def search(query):
45             api = napi.NominatimAPIAsync(Path('.'))
46
47             return await api.search(query)
48
49         results = asyncio.run(search('Brugge'))
50         if not results:
51             print('Cannot find Brugge')
52         else:
53             print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}')
54         ```
55
56     === "NominatimAPI"
57         ``` python
58         from pathlib import Path
59
60         import nominatim_api as napi
61
62         api = napi.NominatimAPI(Path('.'))
63
64         results = api.search('Brugge')
65
66         if not results:
67             print('Cannot find Brugge')
68         else:
69             print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}')
70         ```
71
72 The Nominatim library is designed around
73 [asyncio](https://docs.python.org/3/library/asyncio.html). `NominatimAPIAsync`
74 provides you with an interface of coroutines.
75 If you have many requests to make, coroutines can speed up your applications
76 significantly.
77
78 For smaller scripts there is also a synchronous wrapper around the API. By
79 using `NominatimAPI`, you get exactly the same interface using classic functions.
80
81 The examples in this chapter will always show-case both
82 implementations. The documentation itself will usually refer only to
83 'Nominatim API class' when both flavours are meant. If a functionality is
84 available only for the synchronous or asynchronous version, this will be
85 explicitly mentioned.
86
87 ### Defining which database to use
88
89 The [Configuration](../admin/Import.md#configuration-setup-in-env)
90 section explains how Nominatim is configured using the
91 [dotenv](https://github.com/theskumar/python-dotenv) library.
92 The same configuration mechanism is used with the
93 Nominatim API library. You should therefore be sure you are familiar with
94 the section.
95
96 The constructor of the 'Nominatim API class' takes one mandatory parameter:
97 the path to the [project directory](../admin/Import.md#creating-the-project-directory).
98 You should have set up this directory as part of the Nominatim import.
99 Any configuration found in the `.env` file in this directory will automatically
100 used.
101
102 You may also configure Nominatim by setting environment variables.
103 Normally, Nominatim will check the operating system environment. This can be
104 overwritten by giving the constructor a dictionary of configuration parameters.
105
106 Let us look up 'Brugge' in the special database named 'belgium' instead of the
107 standard 'nominatim' database:
108
109 !!! example
110     === "NominatimAPIAsync"
111         ``` python
112         from pathlib import Path
113         import asyncio
114
115         import nominatim_api as napi
116
117         config_params = {
118             'NOMINATIM_DATABASE_DSN': 'pgsql:dbname=belgium'
119         }
120
121         async def search(query):
122             api = napi.NominatimAPIAsync(Path('.'), environ=config_params)
123
124             return await api.search(query)
125
126         results = asyncio.run(search('Brugge'))
127         ```
128
129     === "NominatimAPI"
130         ``` python
131         from pathlib import Path
132
133         import nominatim_api as napi
134
135         config_params = {
136             'NOMINATIM_DATABASE_DSN': 'pgsql:dbname=belgium'
137         }
138
139         api = napi.NominatimAPI(Path('.'), environ=config_params)
140
141         results = api.search('Brugge')
142         ```
143
144 ### Presenting results to humans
145
146 All search functions return the raw results from the database. There is no
147 full human-readable label. To create such a label, you need two things:
148
149 * the address details of the place
150 * adapt the result to the language you wish to use for display
151
152 Again searching for 'Brugge', this time with a nicely formatted result:
153
154 !!! example
155     === "NominatimAPIAsync"
156         ``` python
157         from pathlib import Path
158         import asyncio
159
160         import nominatim_api as napi
161
162         async def search(query):
163             api = napi.NominatimAPIAsync(Path('.'))
164
165             return await api.search(query, address_details=True)
166
167         results = asyncio.run(search('Brugge'))
168
169         locale = napi.Locales(['fr', 'en'])
170         for i, result in enumerate(results):
171             address_parts = result.address_rows.localize(locale)
172             print(f"{i + 1}. {', '.join(address_parts)}")
173         ```
174
175     === "NominatimAPI"
176         ``` python
177         from pathlib import Path
178
179         import nominatim_api as napi
180
181         api = napi.NominatimAPI(Path('.'))
182
183         results = api.search('Brugge', address_details=True)
184
185         locale = napi.Locales(['fr', 'en'])
186         for i, result in enumerate(results):
187             address_parts = result.address_rows.localize(locale)
188             print(f"{i + 1}. {', '.join(address_parts)}")
189         ```
190
191 To request information about the address of a result, add the optional
192 parameter 'address_details' to your search:
193
194 ``` python
195 >>> results = api.search('Brugge', address_details=True)
196 ```
197
198 An additional field `address_rows` will set in results that are returned.
199 It contains a list of all places that make up the address of the place. For
200 simplicity, this includes name and house number of the place itself. With
201 the names in this list it is possible to create a human-readable description
202 of the result. To do that, you first need to decide in which language the
203 results should be presented. As with the names in the result itself, the
204 places in `address_rows` contain all possible name translation for each row.
205
206 The library has a helper class `Locale` which helps extracting a name of a
207 place in the preferred language. It takes a single parameter with a list
208 of language codes in the order of preference. So
209
210 ``` python
211 locale = napi.Locale(['fr', 'en'])
212 ```
213
214 creates a helper class that returns the name preferably in French. If that is
215 not possible, it tries English and eventually falls back to the default `name`
216 or `ref`.
217
218 The `Locale` object can be applied to a name dictionary to return the best-matching
219 name out of it:
220
221 ``` python
222 >>> print(locale.display_name(results[0].names))
223 'Brugges'
224 ```
225
226 The `address_row` field has a helper function to apply the function to all
227 its members and save the result in the `local_name` field. It also returns
228 all the localized names as a convenient simple list. This list can be used
229 to create a human-readable output:
230
231 ``` python
232 >>> address_parts = results[0].address_rows.localize(locale)
233 >>> print(', '.join(address_parts))
234 Bruges, Flandre-Occidentale, Flandre, Belgique
235 ```
236
237 This is a fairly simple way to create a human-readable description. The
238 place information in `address_rows` contains further information about each
239 place. For example, which OSM `adlin_level` was used, what category the place
240 belongs to or what rank Nominatim has assigned. Use this to adapt the output
241 to local address formats.
242
243 For more information on address rows, see
244 [detailed address description](Result-Handling.md#detailed-address-description).