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