]> git.openstreetmap.org Git - nominatim.git/blob - docs/customize/Result-Formatting.md
improve docs on custom result formatters
[nominatim.git] / docs / customize / Result-Formatting.md
1 # Changing the Appearance of Results in the Server API
2
3 The Nominatim Server API offers a number of formatting options that
4 present search results in [different output formats](../api/Output.md).
5 These results only contain a subset of all the information that Nominatim
6 has about the result. This page explains how to adapt the result output
7 or add additional result formatting.
8
9 ## Defining custom result formatting
10
11 To change the result output, you need to place a file `api/v1/format.py`
12 into your project directory. This file needs to define a single variable
13 `dispatch` containing a [FormatDispatcher](#formatdispatcher). This class
14 serves to collect the functions for formatting the different result types
15 and offers helper functions to apply the formatters.
16
17 There are two ways to define the `dispatch` variable. If you want to reuse
18 the default output formatting and just make some changes or add an additional
19 format type, then import the dispatch object from the default API:
20
21 ``` python
22 from nominatim_api.v1.format import dispatch as dispatch
23 ```
24
25 If you prefer to define a completely new result output, then you can
26 create an empty dispatcher object:
27
28 ``` python
29 from nominatim_api import FormatDispatcher
30
31 dispatch = FormatDispatcher()
32 ```
33
34 ## The formatting function
35
36 The dispatcher organises the formatting functions by format and result type.
37 The format corresponds to the `format` parameter of the API. It can contain
38 one of the predefined format names or you can invent your own new format.
39
40 API calls return data classes or an array of a data class which represent
41 the result. You need to make sure there are formatters defined for the
42 following result types:
43
44 * StatusResult (single object, returned by `/status`)
45 * DetailedResult (single object, returned by `/details`)
46 * SearchResults (list of objects, returned by `/search`)
47 * ReverseResults (list of objects, returned by `/reverse` and `/lookup`)
48 * RawDataList (simple object, returned by `/deletable` and `/polygons`)
49
50 A formatter function has the following signature:
51
52 ``` python
53 def format_func(result: ResultType, options: Mapping[str, Any]) -> str
54 ```
55
56 The options dictionary contains additional information about the original
57 query. See the [reference below](#options-for-different-result-types)
58 about the possible options.
59
60 To set the result formatter for a certain result type and format, you need
61 to write the format function and decorate it with the
62 [`format_func`](#nominatim_api.FormatDispatcher.format_func)
63 decorator.
64
65 For example, let us extend the result for the status call in text format
66 and add the server URL. Such a formatter would look like this:
67
68 ``` python
69 from nominatim_api import StatusResult
70
71 @dispatch.format_func(StatusResult, 'text')
72 def _format_status_text(result, _):
73     header = 'Status for server nominatim.openstreetmap.org'
74     if result.status:
75         return f"{header}\n\nERROR: {result.message}"
76
77     return f"{header}\n\nOK"
78 ```
79
80 If your dispatcher is derived from the default one, then this definition
81 will overwrite the original formatter function. This way it is possible
82 to customize the output of selected results.
83
84 ## Adding new formats
85
86 You may also define a completely different output format. This is as simple
87 as adding formatting functions for all result types using the custom
88 format name:
89
90 ``` python
91 from nominatim_api import StatusResult
92
93 @dispatch.format_func(StatusResult, 'chatty')
94 def _format_status_text(result, _):
95     if result.status:
96         return f"The server is currently not running. {result.message}"
97
98     return "Good news! The server is running just fine."
99 ```
100
101 That's all. Nominatim will automatically pick up the new format name and
102 will allow the user to use it. There is no need to implement formatter
103 functions for all the result types, when you invent a new one. The
104 available formats will be determined for each API endpoint separately.
105 To find out which formats are available, you can use the `--list-formats`
106 option of the CLI tool:
107
108 ```
109 me@machine:planet-project$ nominatim status --list-formats
110 2024-08-16 19:54:00: Using project directory: /home/nominatim/planet-project
111 text
112 json
113 chatty
114 debug
115 me@machine:planet-project$
116 ```
117
118 The `debug` format listed in the last line will always appear. It is a
119 special format that enables debug output via the command line (the same
120 as the `debug=1` parameter enables for the server API). To not clash
121 with this built-in function, you shouldn't name your own format 'debug'.
122
123 ### Content type of new formats
124
125 All responses will be returned with the content type application/json by
126 default. If your format produces a different content type, you need
127 to configure the content type with the `set_content_type()` function.
128
129 For example, the 'chatty' format above returns just simple text. So the
130 content type should be set up as:
131
132 ``` python
133 from nominatim_api.server.content_types import CONTENT_TEXT
134
135 dispatch.set_content_type('chatty', CONTENT_TEXT)
136 ```
137
138 The `content_types` module used above provides constants for the most
139 frequent content types. You set the content type to an arbitrary string,
140 if the content type you need is not available.
141
142 ## Formatting error messages
143
144 Any exception thrown during processing of a request is given to
145 a special error formatting function. It takes the requested content type,
146 the status code and the error message. It should return the error message
147 in a form appropriate for the given content type.
148
149 You can overwrite the default formatting function with the decorator
150 `error_format_func`:
151
152 ``` python
153 import nominatim_api.server.content_types as ct
154
155 @dispatch.error_format_func
156 def _format_error(content_type: str, msg: str, status: int) -> str:
157     if content_type == ct.CONTENT_XML:
158         return f"""<?xml version="1.0" encoding="UTF-8" ?>
159                      <message>{msg}</message>
160                 """
161     if content_type == ct.CONTENT_JSON:
162         return f'"{msg}"'
163
164     return f"ERROR: {msg}"
165 ```
166
167
168 ## Debugging custom formatters
169
170 The easiest way to try out your custom formatter is by using the Nominatim
171 CLI commands. Custom formats can be chosen with the `--format` parameter:
172
173 ```
174 me@machine:planet-project$ nominatim status --format chatty
175 2024-08-16 19:54:00: Using project directory: /home/nominatim/planet-project
176 Good news! The server is running just fine.
177 me@machine:planet-project$
178 ```
179
180 They will also emit full error messages when there is a problem with the
181 code you need to debug.
182
183 ## Reference
184
185 ### FormatDispatcher
186
187 ::: nominatim_api.FormatDispatcher
188     options:
189         heading_level: 6
190         group_by_category: False
191
192 ### JsonWriter
193
194 ::: nominatim_api.utils.json_writer.JsonWriter
195     options:
196         heading_level: 6
197         group_by_category: False
198
199 ### Options for different result types
200
201 This section lists the options that may be handed in with the different result
202 types in the v1 version of the Nominatim API.
203
204 #### StatusResult
205
206 _None._
207
208 #### DetailedResult
209
210 | Option          | Description |
211 |-----------------|-------------|
212 | locales         | [Locale](../library/Result-Handling.md#locale) object for the requested language(s) |
213 | group_hierarchy | Setting of [group_hierarchy](../api/Details.md#output-details) parameter |
214 | icon_base_url   | (optional) URL pointing to icons as set in [NOMINATIM_MAPICON_URL](Settings.md#nominatim_mapicon_url) |
215
216 #### SearchResults
217
218 | Option          | Description |
219 |-----------------|-------------|
220 | query           | Original query string |
221 | more_url        | URL for requesting additional results for the same query |
222 | exclude_place_ids | List of place IDs already returned |
223 | viewbox         | Setting of [viewbox](../api/Search.md#result-restriction) parameter |
224 | extratags       | Setting of [extratags](../api/Search.md#output-details) parameter |
225 | namedetails     | Setting of [namedetails](../api/Search.md#output-details) parameter |
226 | addressdetails  | Setting of [addressdetails](../api/Search.md#output-details) parameter |
227
228 #### ReverseResults
229
230 | Option          | Description |
231 |-----------------|-------------|
232 | query           | Original query string |
233 | extratags       | Setting of [extratags](../api/Search.md#output-details) parameter |
234 | namedetails     | Setting of [namedetails](../api/Search.md#output-details) parameter |
235 | addressdetails  | Setting of [addressdetails](../api/Search.md#output-details) parameter |
236
237 #### RawDataList
238
239 _None._