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