]> git.openstreetmap.org Git - nominatim.git/blobdiff - docs/library/Getting-Started.md
Merge remote-tracking branch 'upstream/master'
[nominatim.git] / docs / library / Getting-Started.md
index 6c187839f30feb8ecf22c67d64e7553acd0e59ed..88f25eb6ae47f7da198b075713de421c533336b1 100644 (file)
@@ -3,8 +3,7 @@
 The Nominatim search frontend can directly be used as a Python library in
 scripts and applications. When you have imported your own Nominatim database,
 then it is no longer necessary to run a full web service for it and access
-the database through http requests. With the Nominatim library it is possible
-to access all search functionality directly from your Python code. There are
+the database through http requests. There are
 also less constraints on the kinds of data that can be accessed. The library
 allows to get access to more detailed information about the objects saved
 in the database.
@@ -14,8 +13,8 @@ in the database.
     be some smaller adjustments to the public interface until the next version.
 
     The library also misses a proper installation routine, so some manipulation
-    of the PYTHONPATH is required. Use is only recommended for advanced Python
-    programmers at the moment.
+    of the PYTHONPATH is required. At the moment, use is only recommended for
+    developers wit some experience in Python.
 
 ## Installation
 
@@ -30,57 +29,220 @@ under `/usr/local/share/nominatim/lib-python`. If you have installed
 Nominatim under a different prefix, adapt the `/usr/local/` part accordingly.
 You can also point the PYTHONPATH to the Nominatim source code.
 
-
 ### 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 knwon from its web API.
+search functions of Nominatim that are also known from its web API.
 
 This code snippet implements a simple search for the town if 'Brugge':
 
-=== "NominatimAPIAsync"
-    ```
-    from pathlib import Path
-    import asyncio
+!!! example
+    === "NominatimAPIAsync"
+        ``` python
+        from pathlib import Path
+        import asyncio
 
-    import nominatim.api as napi
+        import nominatim.api as napi
 
-    async def search(query):
-        api = napi.NominatimAPIAsync(Path('.'))
+        async def search(query):
+            api = napi.NominatimAPIAsync(Path('.'))
 
-        return await api.search(query)
+            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[1].centroid.y}')
-    ```
+        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"
-    ```
-    from pathlib import Path
+    === "NominatimAPI"
+        ``` python
+        from pathlib import Path
 
-    import nominatim.api as napi
+        import nominatim.api as napi
 
-    api = napi.NominatimAPI(Path('.'))
+        api = napi.NominatimAPI(Path('.'))
 
-    results = api.search('Brugge')
+        results = api.search('Brugge')
 
-    if not results:
-        print('Cannot find Brugge')
-    else:
-        print(f'Found a place at {results[0].centroid.x},{results[1].centroid.y}')
-    ```
+        if not results:
+            print('Cannot find Brugge')
+        else:
+            print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}')
+        ```
 
-The Nonminatim API comes in two flavours: synchronous and asynchronous.
-The complete Nominatim library is written so that it can work asynchronously.
+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 sychronous wrapper around the API.
+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.
+
+The constructor of the 'Nominatim API class' takes one mandatory parameter:
+the path to the [project directory](../admin/Import.md#creating-the-project-directory).
+You should have set up this directory as part of the Nominatim import.
+Any configuration found in the `.env` file in this directory will automatically
+used.
+
+Yo may also configure Nominatim be setting environment variables.
+Normally, Nominatim will check the operating system environment. This can be
+overwritten by giving the constructor a dictionary of configuration parameters.
+
+Let us look up 'Brugge' in the special database named 'belgium' instead of the
+standard 'nominatim' database:
+
+!!! example
+    === "NominatimAPIAsync"
+        ``` python
+        from pathlib import Path
+        import asyncio
+
+        import nominatim.api as napi
+
+        config_params = {
+            'NOMINATIM_DATABASE_DSN': 'pgsql:dbname=belgium'
+        }
+
+        async def search(query):
+            api = napi.NominatimAPIAsync(Path('.'), environ=config_params)
+
+            return await api.search(query)
+
+        results = asyncio.run(search('Brugge'))
+        ```
+
+    === "NominatimAPI"
+        ``` python
+        from pathlib import Path
+
+        import nominatim.api as napi
+
+        config_params = {
+            'NOMINATIM_DATABASE_DSN': 'pgsql:dbname=belgium'
+        }
+
+        api = napi.NominatimAPI(Path('.'), environ=config_params)
+
+        results = api.search('Brugge')
+        ```
+
+### Presenting results to humans
+
+All search functions return the raw results from the database. There is no
+full human-readable label. To create such a label, you need two things:
+
+* the address details of the place
+* adapt the result to the language you wish to use for display
+
+Again searching for 'Brugge', this time with a nicely formatted result:
+
+!!! example
+    === "NominatimAPIAsync"
+        ``` python
+        from pathlib import Path
+        import asyncio
+
+        import nominatim.api as napi
+
+        async def search(query):
+            api = napi.NominatimAPIAsync(Path('.'))
+
+            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
+        from pathlib import Path
+
+        import nominatim.api as napi
+
+        api = napi.NominatimAPI(Path('.'))
+
+        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 `Locale` 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.Locale(['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 Locale 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 apply the function to all
+its members and save the result in the `local_name` field. It also returns
+all the localized names as a convenient simple list. This list can be used
+to create a human-readable output:
+
+``` python
+>>> address_parts = results[0].address_rows.localize(locale)
+>>> 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 `adlin_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).