|
| 1 | +--- |
| 2 | +title: How to create Azure Maps applications using the Python REST SDK (preview) |
| 3 | +titleSuffix: Azure Maps |
| 4 | +description: How to develop applications that incorporate Azure Maps using the Python SDK Developers Guide. |
| 5 | +author: stevemunk |
| 6 | +ms.author: v-munksteve |
| 7 | +ms.date: 01/15/2021 |
| 8 | +ms.topic: how-to |
| 9 | +ms.service: azure-maps |
| 10 | +services: azure-maps |
| 11 | +--- |
| 12 | + |
| 13 | +# Python REST SDK Developers Guide (preview) |
| 14 | + |
| 15 | +The Azure Maps Python SDK can be integrated with Python applications and libraries to build map-related and location-aware applications. The Azure Maps Python SDK contains APIs for Search, Route, Render and Geolocation. These APIs support operations such as searching for an address, routing between different coordinates, obtaining the geo-location of a specific IP address. |
| 16 | + |
| 17 | +## Prerequisites |
| 18 | + |
| 19 | +- [Azure Maps account][Azure Maps account]. |
| 20 | +- [Subscription key][Subscription key] or other form of [authentication][authentication]. |
| 21 | +- Python on 3.7 or later. It's recommended to use the [latest release][python latest release]. For more information, see [Azure SDK for Python version support policy][support policy]. |
| 22 | + |
| 23 | +> [!TIP] |
| 24 | +> You can create an Azure Maps account programmatically, Here's an example using the Azure CLI: |
| 25 | +> |
| 26 | +> ```azurecli |
| 27 | +> az maps account create --kind "Gen2" --account-name "myMapAccountName" --resource-group "<resource group>" --sku "G2" |
| 28 | +> ``` |
| 29 | +
|
| 30 | +## Create a python project |
| 31 | +
|
| 32 | +The following example shows how to create a console program named `demo` with Python: |
| 33 | +
|
| 34 | +```powershell |
| 35 | +mkdir mapsDemo |
| 36 | +cd mapsDemo |
| 37 | +New-Item demo.py |
| 38 | +``` |
| 39 | +
|
| 40 | +### Install needed python packages |
| 41 | + |
| 42 | +Each service in Azure Maps is contained in its own package. When using the Azure Maps Python SDK, you can install just the packages of the services you need. |
| 43 | + |
| 44 | +Here we install the Azure Maps Search package. Since it’s still in public preview, you need to add the `--pre` flag: |
| 45 | + |
| 46 | +```powershell |
| 47 | +pip install azure-maps-search --pre |
| 48 | +``` |
| 49 | + |
| 50 | +### Azure Maps services |
| 51 | + |
| 52 | +Azure Maps Python SDK supports Python version 3.7 or later. For more information on future Python versions, see [Azure SDK for Python version support policy][support policy]. |
| 53 | + |
| 54 | +| Service Name | PyPi package | Samples | |
| 55 | +|----------------------------|-------------------------|--------------| |
| 56 | +| [Search][py search readme] | [azure-maps-search][py search package] | [search samples][py search sample] | |
| 57 | +| [Route][py route readme] | [azure-maps-route][py route package] | [route samples][py route sample] | |
| 58 | +| [Render][py render readme] | [azure-maps-render][py render package]|[render sample][py render sample] | |
| 59 | +| [Geolocation][py geolocation readme]|[azure-maps-geolocation][py geolocation package]|[geolocation sample][py geolocation sample] | |
| 60 | + |
| 61 | +## Create and authenticate a MapsSearchClient |
| 62 | + |
| 63 | +You'll need a `credential` object for authentication when creating the `MapsSearchClient` object used to access the Azure Maps search APIs. You can use either an Azure Active Directory (Azure AD) credential or an Azure subscription key to authenticate. For more information on authentication, see [Authentication with Azure Maps][authentication]. |
| 64 | + |
| 65 | +> [!TIP] |
| 66 | +> The`MapsSearchClient` is the primary interface for developers using the Azure Maps search library. See [Azure Maps Search package client library][Search package client library] to learn more about the search methods available. |
| 67 | +
|
| 68 | +### Using an Azure AD credential |
| 69 | + |
| 70 | +You can authenticate with Azure AD using the [Azure Identity package][Azure Identity package]. To use the [DefaultAzureCredential][defaultazurecredential] provider, you'll need to install the Azure Identity client package: |
| 71 | + |
| 72 | +```powershell |
| 73 | +pip install azure-identity |
| 74 | +``` |
| 75 | + |
| 76 | +You'll need to register the new Azure AD application and grant access to Azure Maps by assigning the required role to your service principal. For more information, see [Host a daemon on non-Azure resources][Host daemon]. During this process you'll get an Application (client) ID, a Directory (tenant) ID, and a client secret. Copy these values and store them in a secure place. You'll need them in the following steps. |
| 77 | + |
| 78 | +Next you'll need to specify the Azure Maps account you intend to use by specifying the maps’ client ID. The Azure Maps account client ID can be found in the Authentication sections of the Azure Maps account. For more information, see [View authentication details][View authentication details]. |
| 79 | + |
| 80 | +Set the values of the Application (client) ID, Directory (tenant) ID, and client secret of your Azure AD application, and the map resource’s client ID as environment variables: |
| 81 | + |
| 82 | +| Environment Variable | Description | |
| 83 | +|----------------------|---------------------------------------------------------------| |
| 84 | +| AZURE_CLIENT_ID | Application (client) ID in your registered application | |
| 85 | +| AZURE_CLIENT_SECRET | The value of the client secret in your registered application | |
| 86 | +| AZURE_TENANT_ID | Directory (tenant) ID in your registered application | |
| 87 | +| MAPS_CLIENT_ID | The client ID in your Azure Map account | |
| 88 | + |
| 89 | +Now you can create environment variables in PowerShell to store these values: |
| 90 | + |
| 91 | +```powershell |
| 92 | +$Env:AZURE_CLIENT_ID="Application (client) ID" |
| 93 | +$Env:AZURE_CLIENT_SECRET="your client secret" |
| 94 | +$Env:AZURE_TENANT_ID="your Directory (tenant) ID" |
| 95 | +$Env:MAPS_CLIENT_ID="your Azure Maps client ID" |
| 96 | +``` |
| 97 | + |
| 98 | +After setting up the environment variables, you can use them in your program to instantiate the `AzureMapsSearch` client. Create a file named *demo.py* and add the following: |
| 99 | + |
| 100 | +```Python |
| 101 | +import os |
| 102 | +from azure.identity import DefaultAzureCredential |
| 103 | +from azure.maps.search import MapsSearchClient |
| 104 | + |
| 105 | +credential = DefaultAzureCredential() |
| 106 | +maps_client_id = os.getenv("MAPS_CLIENT_ID") |
| 107 | +maps_search_client = MapsSearchClient( |
| 108 | + client_id=maps_client_id, |
| 109 | + credential=credential |
| 110 | +) |
| 111 | +``` |
| 112 | + |
| 113 | +> [!IMPORTANT] |
| 114 | +> The other environment variables created above, while not used in the code sample here, are required by `DefaultAzureCredential()`. If you do not set these environment variables correctly, using the same naming conventions, you will get run-time errors. For example, if your `AZURE_CLIENT_ID` is missing or invalid you will get an `InvalidAuthenticationTokenTenant` error. |
| 115 | +
|
| 116 | +### Using a subscription key credential |
| 117 | + |
| 118 | +You can authenticate with your Azure Maps subscription key. Your subscription key can be found in the **Authentication** section in the Azure Maps account as shown in the following screenshot: |
| 119 | + |
| 120 | +:::image type="content" source="./media/rest-sdk-dev-guides/subscription-key.png" alt-text="A screenshot showing the subscription key in the Authentication section of an Azure Maps account." lightbox="./media/rest-sdk-dev-guides/subscription-key.png"::: |
| 121 | + |
| 122 | +Now you can create environment variables in PowerShell to store the subscription key: |
| 123 | + |
| 124 | +```powershell |
| 125 | +$Env:SUBSCRIPTION_KEY="your subscription key" |
| 126 | +``` |
| 127 | + |
| 128 | +Once your environment variable is created, you can access it in your code. Create a file named *demo.py* and add the following: |
| 129 | + |
| 130 | +```Python |
| 131 | +import os |
| 132 | + |
| 133 | +from azure.core.credentials import AzureKeyCredential |
| 134 | +from azure.maps.search import MapsSearchClient |
| 135 | + |
| 136 | +# Use Azure Maps subscription key authentication |
| 137 | +subscription_key = os.getenv("SUBSCRIPTION_KEY") |
| 138 | +maps_search_client = MapsSearchClient( |
| 139 | + credential=AzureKeyCredential(subscription_key) |
| 140 | +) |
| 141 | +``` |
| 142 | + |
| 143 | +## Fuzzy Search an Entity |
| 144 | + |
| 145 | +The following code snippet demonstrates how, in a simple console application, to import the `Azure.Maps.Search` package and perform a fuzzy search on “Starbucks” near Seattle. This example uses subscription key credentials to authenticate MapsSearchClient. In `demo.py`: |
| 146 | + |
| 147 | +```Python |
| 148 | +import os |
| 149 | +from azure.core.credentials import AzureKeyCredential |
| 150 | +from azure.maps.search import MapsSearchClient |
| 151 | + |
| 152 | +def fuzzy_search(): |
| 153 | + # Use Azure Maps subscription key authentication |
| 154 | + subscription_key = os.getenv("SUBSCRIPTION_KEY") |
| 155 | + maps_search_client = MapsSearchClient( |
| 156 | + credential=AzureKeyCredential(subscription_key) |
| 157 | + ) |
| 158 | + result = maps_search_client.fuzzy_search( |
| 159 | + query="Starbucks", |
| 160 | + coordinates=(47.61010, -122.34255) |
| 161 | + ) |
| 162 | + # Print the search results |
| 163 | + print("Starbucks search result nearby Seattle:") |
| 164 | + for result_item in result.results: |
| 165 | + print(f"* {result_item.address.street_number } {result_item.address.street_name }") |
| 166 | + print(f" {result_item.address.municipality } {result_item.address.country_code } {result_item.address.postal_code }") |
| 167 | + print(f" Coordinate: {result_item.position.lat}, {result_item.position.lon}" |
| 168 | + ) |
| 169 | + |
| 170 | +if __name__ == '__main__': |
| 171 | + fuzzy_search() |
| 172 | +``` |
| 173 | + |
| 174 | +The sample code above instantiates `AzureKeyCredential` with the Azure Maps subscription key, then it to instantiate the `MapsSearchClient` object. The methods provided by `MapsSearchClient` forward the request to the Azure Maps REST endpoints. In the end, the program iterates through the results and prints the address and coordinates for each result. |
| 175 | + |
| 176 | +After finishing the program, run `python demo.py` from the project folder in PowerShell: |
| 177 | + |
| 178 | +```powershell |
| 179 | +python demo.py |
| 180 | +``` |
| 181 | + |
| 182 | +You should see a list of Starbucks address and coordinate results: |
| 183 | + |
| 184 | +```text |
| 185 | +* 1912 Pike Place |
| 186 | + Seattle US 98101 |
| 187 | + Coordinate: 47.61016, -122.34248 |
| 188 | +* 2118 Westlake Avenue |
| 189 | + Seattle US 98121 |
| 190 | + Coordinate: 47.61731, -122.33782 |
| 191 | +* 2601 Elliott Avenue |
| 192 | + Seattle US 98121 |
| 193 | + Coordinate: 47.61426, -122.35261 |
| 194 | +* 1730 Howell Street |
| 195 | + Seattle US 98101 |
| 196 | + Coordinate: 47.61716, -122.3298 |
| 197 | +* 220 1st Avenue South |
| 198 | + Seattle US 98104 |
| 199 | + Coordinate: 47.60027, -122.3338 |
| 200 | +* 400 Occidental Avenue South |
| 201 | + Seattle US 98104 |
| 202 | + Coordinate: 47.5991, -122.33278 |
| 203 | +* 1600 East Olive Way |
| 204 | + Seattle US 98102 |
| 205 | + Coordinate: 47.61948, -122.32505 |
| 206 | +* 500 Mercer Street |
| 207 | + Seattle US 98109 |
| 208 | + Coordinate: 47.62501, -122.34687 |
| 209 | +* 505 5Th Ave S |
| 210 | + Seattle US 98104 |
| 211 | + Coordinate: 47.59768, -122.32849 |
| 212 | +* 425 Queen Anne Avenue North |
| 213 | + Seattle US 98109 |
| 214 | + Coordinate: 47.62301, -122.3571 |
| 215 | +``` |
| 216 | + |
| 217 | +## Search an Address |
| 218 | + |
| 219 | +Call the `SearchAddress` method to get the coordinate of an address. Modify the Main program from the sample as follows: |
| 220 | + |
| 221 | +```Python |
| 222 | +import os |
| 223 | +from azure.core.credentials import AzureKeyCredential |
| 224 | +from azure.maps.search import MapsSearchClient |
| 225 | + |
| 226 | +def search_address(): |
| 227 | + subscription_key = os.getenv("SUBSCRIPTION_KEY") |
| 228 | + |
| 229 | + maps_search_client = MapsSearchClient( |
| 230 | + credential=AzureKeyCredential(subscription_key) |
| 231 | + ) |
| 232 | + |
| 233 | + result = maps_search_client.search_address( |
| 234 | + query="1301 Alaskan Way, Seattle, WA 98101, US" |
| 235 | + ) |
| 236 | + print(f"Coordinate: {result.results[0].position.lat}, {result.results[0].position.lon}") |
| 237 | + |
| 238 | +if __name__ == '__main__': |
| 239 | + search_address() |
| 240 | +``` |
| 241 | + |
| 242 | +Results returned by the `search_address()` method are ordered by confidence score and print the coordinates of the first result. |
| 243 | + |
| 244 | +## Batch reverse search |
| 245 | + |
| 246 | +Azure Maps Search also provides some batch query methods. These methods will return long-running operations (LRO) objects. The requests might not return all the results immediately, so users can choose to wait until completion or query the result periodically. The examples below demonstrate how to call the batched reverse search method. |
| 247 | + |
| 248 | +Since these return LRO objects, you'll need the `asyncio` method included in the `aiohttp` package: |
| 249 | + |
| 250 | +```powershell |
| 251 | +pip install aiohttp |
| 252 | +``` |
| 253 | + |
| 254 | +```Python |
| 255 | +import asyncio |
| 256 | +import os |
| 257 | +from azure.core.credentials import AzureKeyCredential |
| 258 | +from azure.maps.search.aio import MapsSearchClient |
| 259 | + |
| 260 | +async def begin_reverse_search_address_batch(): |
| 261 | + subscription_key = os.getenv("SUBSCRIPTION_KEY") |
| 262 | + |
| 263 | + maps_search_client = MapsSearchClient(AzureKeyCredential(subscription_key)) |
| 264 | + |
| 265 | + async with maps_search_client: |
| 266 | + result = await maps_search_client.begin_reverse_search_address_batch( |
| 267 | + search_queries = [ |
| 268 | + "148.858561,2.294911", |
| 269 | + "47.639765,-122.127896&radius=5000", |
| 270 | + "47.61559,-122.33817&radius=5000", |
| 271 | + ] |
| 272 | + ) |
| 273 | + print(f"Batch_id: {result.batch_id}") |
| 274 | + |
| 275 | +if __name__ == '__main__': |
| 276 | + # Special handle for Windows platform |
| 277 | + if os.name == 'nt': |
| 278 | + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) |
| 279 | + asyncio.run(begin_reverse_search_address_batch()) |
| 280 | +``` |
| 281 | + |
| 282 | +In the above example, three queries are passed to the batched reverse search request. To get the LRO results, the request will create a batch request with a batch ID as result that can be used to fetch batch response later. The LRO results will be cached on the server side for 14 days. |
| 283 | + |
| 284 | +The following example demonstrates the process of calling the batch ID and retrieving the operation results of the batch request: |
| 285 | + |
| 286 | +```python |
| 287 | +import asyncio |
| 288 | +import os |
| 289 | +from azure.core.credentials import AzureKeyCredential |
| 290 | +from azure.maps.search.aio import MapsSearchClient |
| 291 | + |
| 292 | +async def begin_reverse_search_address_batch(): |
| 293 | + subscription_key = os.getenv("SUBSCRIPTION_KEY") |
| 294 | + |
| 295 | + maps_search_client = MapsSearchClient(credential=AzureKeyCredential(subscription_key)) |
| 296 | + |
| 297 | + async with maps_search_client: |
| 298 | + result = await maps_search_client.begin_reverse_search_address_batch( |
| 299 | + search_queries = [ |
| 300 | + "148.858561,2.294911", |
| 301 | + "47.639765,-122.127896&radius=5000", |
| 302 | + "47.61559,-122.33817&radius=5000", |
| 303 | + ] |
| 304 | + ) |
| 305 | + return result |
| 306 | + |
| 307 | +async def begin_reverse_search_address_batch_with_id(batch_id): |
| 308 | + subscription_key = os.getenv("SUBSCRIPTION_KEY") |
| 309 | + maps_search_client = MapsSearchClient(credential=AzureKeyCredential(subscription_key)) |
| 310 | + async with maps_search_client: |
| 311 | + result = await maps_search_client.begin_reverse_search_address_batch( |
| 312 | + batch_id=batch_id, |
| 313 | + ) |
| 314 | + |
| 315 | + responses = result._polling_method._initial_response.context.get('deserialized_data') |
| 316 | + summary = responses['summary'] |
| 317 | + |
| 318 | + # Print Batch results |
| 319 | + idx = 1 |
| 320 | + print(f"Total Batch Requests: {summary['totalRequests']}, Total Successful Results: {summary['successfulRequests']}") |
| 321 | + for items in responses.get('batchItems'): |
| 322 | + if items['statusCode'] == 200: |
| 323 | + print(f"Request {idx} result:") |
| 324 | + for address in items['response']['addresses']: |
| 325 | + print(f" {address['address']['freeformAddress']}") |
| 326 | + else: |
| 327 | + print(f"Error in request {idx}: {items['response']['error']['message']}") |
| 328 | + idx += 1 |
| 329 | + |
| 330 | +async def main(): |
| 331 | + result = await begin_reverse_search_address_batch() |
| 332 | + await begin_reverse_search_address_batch_with_id(result.batch_id) |
| 333 | + |
| 334 | +if __name__ == '__main__': |
| 335 | + # Special handle for Windows platform |
| 336 | + if os.name == 'nt': |
| 337 | + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) |
| 338 | + asyncio.run(main()) |
| 339 | +``` |
| 340 | + |
| 341 | +## Additional information |
| 342 | + |
| 343 | +The [Azure Maps Search Package client library for Python][Search package client library] in the *Azure SDK for Python Preview* documentation. |
| 344 | + |
| 345 | +<!---------------------------------------------------------------------------------------------------------------> |
| 346 | +[Azure Maps account]: quick-demo-map-app.md#create-an-azure-maps-account |
| 347 | +[Subscription key]: quick-demo-map-app.md#get-the-primary-key-for-your-account |
| 348 | +[authentication]: azure-maps-authentication.md |
| 349 | + |
| 350 | +[Search package client library]: /python/api/overview/azure/maps-search-readme?view=azure-python-preview |
| 351 | +[python latest release]: https://www.python.org/downloads/ |
| 352 | + |
| 353 | +<!-- Python SDK Developers Guide ---> |
| 354 | +[support policy]: https://github.com/Azure/azure-sdk-for-python/wiki/Azure-SDKs-Python-version-support-policy |
| 355 | +[py search package]: https://pypi.org/project/azure-maps-search |
| 356 | +[py search readme]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/maps/azure-maps-search/README.md |
| 357 | +[py search sample]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/maps/azure-maps-search/samples |
| 358 | +[py route package]: https://pypi.org/project/azure-maps-route |
| 359 | +[py route readme]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/maps/azure-maps-route/README.md |
| 360 | +[py route sample]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/maps/azure-maps-route/samples |
| 361 | +[py render package]: https://pypi.org/project/azure-maps-render |
| 362 | +[py render readme]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/maps/azure-maps-render/README.md |
| 363 | +[py render sample]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/maps/azure-maps-render/samples |
| 364 | +[py geolocation package]: https://pypi.org/project/azure-maps-geolocation |
| 365 | +[py geolocation readme]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/maps/azure-maps-geolocation/README.md |
| 366 | +[py geolocation sample]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/maps/azure-maps-geolocation/samples |
| 367 | + |
| 368 | +<!--- Authentication ----> |
| 369 | +[Azure Identity package]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity |
| 370 | +[defaultazurecredential]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#defaultazurecredential |
| 371 | +[Host daemon]: ./how-to-secure-daemon-app.md#host-a-daemon-on-non-azure-resources |
| 372 | +[View authentication details]: how-to-manage-authentication.md#view-authentication-details |
0 commit comments