

# Indexing location data
<a name="location-indexing-geoquery"></a>

You can use [AWS IoT fleet indexing](https://docs.aws.amazon.com//iot/latest/developerguide/iot-indexing.html) to index your devices' last sent location data and search for devices using geoqueries. This feature resolves device monitoring and management use cases such as location tracking and proximity search. Location indexing works similarly to other fleet indexing features, and with additional configurations to specify in your [thing indexing](managing-fleet-index.md). 

Common use cases include: search and aggregate devices located within desired geographic boundaries, get location specific insights using query terms related to device metadata and state from indexed data sources, provide a granular view such as filtering results to a specific geographic area to reduce rendering lags within your fleet monitoring maps and track last reported device location, and identify devices that are outside of the desired boundary limits and generate alarms using [fleet metrics](iot-fleet-metrics.md). To get started with location indexing and geoqueries, see [Getting started tutorial](location-indexing-tutorial.md).

## Supported data formats
<a name="location-indexing-format"></a>

AWS IoT fleet indexing supports the following location data formats:

1. 

**Well-known text representation of coordinate reference systems**

   A string that follows the [Geographic information - Well-known text representation of coordinate reference systems](https://docs.ogc.org/is/12-063r5/12-063r5.html) format. An example can be `"POINT(long lat)"`.

1. 

**A string that represents the coordinates**

   A string with the format of `"latitude, longitude"` or `"longitude, latitude"` . If you use `"longitude, latitude"`, you must also specify `order` in `geoLocations`. An example can be `"41.12,-71.34"`.

1. 

**An object of lat(latitude), lon(longitude) keys**

   This format is applicable to classic shadow and named shadow. Supported keys: `lat`, `latitude`, `lon`, `long`, `longitude`. An example can be `{"lat": 41.12, "lon": -71.34}`.

1. 

**An array that represents the coordinates**

   An array with the format `[lat,lon]` or `[lon,lat]`. If you use the format `[lon,lat]`, which is the same as the coordinates in [GeoJSON](https://geojson.org/) (applicable to classic shadow and named shadow), you must also specify `order` in `geoLocations`.

   An example can be:

   ```
   {
     "location": {
       "coordinates": [
         **Longitude**,
         **Latitude**
       ],
       "type": "Point",
       "properties": {
         "country": "United States",
         "city": "New York",
         "postalCode": "*****",
         "horizontalAccuracy": 20,
         "horizontalConfidenceLevel": 0.67,
         "state": "New York",
         "timestamp": "2023-01-04T20:59:13.024Z"
       }
     }
   }
   ```

## How to index location data
<a name="location-indexing-steps"></a>

The following steps show how to update indexing configuration for your location data and use geoqueries to search for devices.

1. 

**Know where your location data is stored**

   Fleet indexing currently supports indexing location data stored in classic shadows or named shadows.

1. 

**Use supported location data formats**

   Make sure your location data format follows one of the [Supported data formats](#location-indexing-format).

1. 

**Update indexing configuration**

   At a minimum need, enable thing (registry) indexing configuration. You must also enable indexing on classic shadow or named shadow that contain your location data. When updating your thing indexing, you should include your location data in the indexing configuration.

1. 

**Create and run geoqueries**

   Depending on your use cases, create geoqueries and run them to search for devices. The geoqeury you compose must follow the [Query syntax](https://docs.aws.amazon.com//iot/latest/developerguide/query-syntax.html). You can find some examples in [Example geoqueries](#location-indexing-geoqueries).

## Update thing indexing configuration
<a name="location-indexing-configuration"></a>

To index location data, you must update indexing configuration and include your location data. Depending on where your location data is stored, follow the steps to update your indexing configuration:

### Location data stored in classic shadows
<a name="location-indexing-shadow-configuration"></a>

If your location data is stored in a classic shadow, you must set `thingIndexingMode` to be `REGISTRY_AND_SHADOW` and specify your location data in the `geoLocations` fields (`name` and `order`) in [https://docs.aws.amazon.com//iot/latest/apireference/API_IndexingFilter.html](https://docs.aws.amazon.com//iot/latest/apireference/API_IndexingFilter.html).

In the following thing indexing configuration example, you specify the location data path `shadow.reported.coordinates` as `name` and `LonLat` as `order`.

```
{
	"thingIndexingMode": "REGISTRY_AND_SHADOW",
	"filter": {
		"geoLocations": [
			{
				"name": "shadow.reported.coordinates",
				"order": "LonLat"
			}
		]
	}
}
```
+ `thingIndexingMode`

  The indexing mode controls if registry or shadow is indexed. When `thingIndexingMode` is set to be `OFF`, thing indexing is disabled. 

  To index location data stored in a classic shadow, you must set `thingIndexingMode` to be `REGISTRY_AND_SHADOW`. For more information, see [Thing indexing modes](managing-index.md#index-mode).
+ `filter`

  Indexing filter provides additional selections for named shadows and geolocation data. For more information, see [Indexing filter](managing-index.md#thing-indexing-filter).
+ `geoLocations`

  The list of geolocation targets that you select to index. The default maximum number of geolocation targets for indexing is `1`. To increase the limit, see [AWS IoT Device Management Quotas](https://docs.aws.amazon.com//general/latest/gr/iot_device_management.html#fleet-indexing-limits).
+ `name`

  The name of the geolocation target field. An example value of `name` can be the location data path of your shadow: `shadow.reported.coordinates`.
+ `order`

  The order of the geolocation target field. Valid values: `LatLon` and `LonLat`. `LatLon` means latitude and longitude. `LonLat` means longitude and latitude. This field is optional. The default value is `LatLon`.

### Location data stored in named shadows
<a name="location-indexing-named-shadow-configuration"></a>

If your location data is stored in a named shadow, set `namedShadowIndexingMode` to be `ON`, add your named shadow name(s) to the `namedShadowNames` field in [https://docs.aws.amazon.com//iot/latest/apireference/API_IndexingFilter.html](https://docs.aws.amazon.com//iot/latest/apireference/API_IndexingFilter.html), and specify your location data path in the `geoLocations` field in [https://docs.aws.amazon.com//iot/latest/apireference/API_IndexingFilter.html](https://docs.aws.amazon.com//iot/latest/apireference/API_IndexingFilter.html).

In the following thing indexing configuration example, you specify the location data path `shadow.name.namedShadow1.reported.coordinates` as `name` and `LonLat` as `order`.

```
{
	"thingIndexingMode": "REGISTRY",
	"namedShadowIndexingMode": "ON",
	"filter": {
		"namedShadowNames": [
			"namedShadow1"
		],
		"geoLocations": [
			{
				"name": "shadow.name.namedShadow1.reported.coordinates",
				"order": "LonLat"
			}
		]
	}
}
```
+ `thingIndexingMode`

  The indexing mode controls if registry or shadow is indexed. When `thingIndexingMode` is set to be `OFF`, thing indexing is disabled. 

  To index location data stored in a named shadow, you must set `thingIndexingMode` to be `REGISTRY` (or `REGISTRY_AND_SHADOW`). For more information, see [Thing indexing modes](managing-index.md#index-mode).
+ `filter`

  Indexing filter provides additional selections for named shadows and geolocation data. For more information, see [Indexing filter](managing-index.md#thing-indexing-filter).
+ `geoLocations`

  The list of geolocation targets that you select to index. The default maximum number of geolocation targets for indexing is `1`. To increase the limit, see [AWS IoT Device Management Quotas](https://docs.aws.amazon.com//general/latest/gr/iot_device_management.html#fleet-indexing-limits).
+ `name`

  The name of the geolocation target field. An example value of `name` can be the location data path of your shadow: `shadow.name.namedShadow1.reported.coordinates`.
+ `order`

  The order of the geolocation target field. Valid values: `LatLon` and `LonLat`. `LatLon` means latitude and longitude. `LonLat` means longitude and latitude. This field is optional. The default value is `LatLon`.

## Example geoqueries
<a name="location-indexing-geoqueries"></a>

After you complete the indexing configuration for your location data, run geoqueries to search for devices. You can also combine your geoqueries with other query strings. For more information, see [Query syntax](query-syntax.md) and [Example thing queries](example-queries.md).

**Example query 1**

This example assumes the location data is stored in a named shadow `gps-tracker`. The output of this command is the list of devices that are within the radial distance of 15.5 km from the center point with coordinates (47.6204,-122.3491).

```
aws iot search-index --query-string \
"shadow.name.gps-tracker.reported.coordinates:geo_distance,47.6204,-122.3491,15.5km"
```

**Example query 2**

This example assumes the location data is stored in a classic shadow. The output of this command is the list of devices that are within the radial distance of 15.5 km from the center point with coordinates (47.6204,-122.3491).

```
aws iot search-index --query-string \
"shadow.reported.coordinates:geo_distance,47.6204,-122.3491,15.5km"
```

**Example query 3**

This example assumes the location data is stored in a classic shadow. The output of this command is the list of devices that are not connected and outside the radial distance of 15.5 km from the center point with coordinates (47.6204,-122.3491).

```
aws iot search-index --query-string \
"connectivity.connected:false AND (NOT shadow.reported.coordinates:geo_distance,47.6204,-122.3491,15.5km)"
```

# Getting started tutorial
<a name="location-indexing-tutorial"></a>

This tutorial demonstrates how to use [fleet indexing](iot-indexing.md) to [index your location data](location-indexing-geoquery.md). For simplicity, you create a thing to represent your device and store the location data in a named shadow, update thing indexing configuration for location indexing, and run example geoqueries to search for devices within a radial boundary.

This tutorial takes about 15 minutes to complete.

**Topics**
+ [Prerequisites](#location-indexing-tutorial-prerequisites)
+ [Create thing and shadow](#location-indexing-create-resources)
+ [Update thing indexing configuration](#location-indexing-update-configuration)
+ [Run geoquery](#location-indexing-run-geoquery)

## Prerequisites
<a name="location-indexing-tutorial-prerequisites"></a>
+ Install the latest version of [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html).
+ Familiarize yourself with [Location indexing and geoqueries](https://docs.aws.amazon.com/iot/latest/developerguide/location-indexing-geoquery.html), [Manage thing indexing](https://docs.aws.amazon.com/iot/latest/developerguide/managing-index.html), and [Query syntax](https://docs.aws.amazon.com/iot/latest/developerguide/query-syntax.html).

## Create thing and shadow
<a name="location-indexing-create-resources"></a>

You create a thing to represent your device, and a named shadow to store its location data (coordinates 47.61564, -122.33584).

1. Run the following command to create your thing that represents your bike named Bike-1. For more information about how to create a thing using AWS CLI, see [create-thing](https://docs.aws.amazon.com//cli/latest/reference/iot/create-thing.html) from *AWS CLI** Reference*.

   ```
   aws iot create-thing --thing-name "Bike-1" \
   --attribute-payload '{"attributes": {"model":"OEM-2302-12", "battery":"35", "acqDate":"06/09/23"}}'
   ```

   The output of this command can look like the following:

   ```
   {
       "thingName": "Bike-1",
       "thingArn": "arn:aws:iot:us-east-1:123456789012:thing/Bike-1",
       "thingId": "df9cf01d-b0c8-48fe-a2e2-e16cff6b23df"
   }
   ```

1. Run the following command to create a named shadow to store Bike-1's location data (coordinates 47.61564, -122.33584). For more information about how to create a named shadow using AWS CLI, see [update-thing-shadow](https://docs.aws.amazon.com//cli/latest/reference/iot-data/update-thing-shadow.html) from *AWS CLI** Reference*.

   ```
   aws iot-data update-thing-shadow \
   --thing-name Bike-1 \
   --shadow-name Bike1-shadow \
   --cli-binary-format raw-in-base64-out \
   --payload '{"state":{"reported":{"coordinates":{"lat": 47.6153, "lon": -122.3333}}}}' \
   "output.txt" \
   ```

   This command doesn't produce any output. To view the named shadow you created, you can run the [list-named-shadows-for-thing](https://docs.aws.amazon.com//cli/latest/reference/iot-data/list-named-shadows-for-thing.html) CLI command.

   ```
   aws iot-data list-named-shadows-for-thing --thing-name Bike-1
   ```

   The output of this command can look like the following:

   ```
   {
       "results": [
           "Bike1-shadow"
       ],
       "timestamp": 1699574309
   }
   ```

## Update thing indexing configuration
<a name="location-indexing-update-configuration"></a>

To index your location data, you must update your thing indexing configuration to include the location data. Because your location data is stored in a named shadow in this tutorial, set `thingIndexingMode` to be `REGISTRY` (at a minimum requirement), set `namedShadowIndexingMode` to be `ON`, and add your location data to the configuration. In this example, you must add the name of your named shadow and the shadow's location data path to `filter`.

1. Run the command to update your indexing configuration for location indexing.

   ```
   aws iot update-indexing-configuration --cli-input-json '{
   "thingIndexingConfiguration": { "thingIndexingMode": "REGISTRY",
   "thingConnectivityIndexingMode": "OFF",
   "deviceDefenderIndexingMode": "OFF",
   "namedShadowIndexingMode": "ON",
   "filter": {
       "namedShadowNames": ["Bike1-shadow"],
       "geoLocations":[{
           "name":"shadow.name.Bike1-shadow.reported.coordinates"
       }]
   },
   "customFields": [
   { "name":"attributes.battery",
   "type":"Number"}] } }'
   ```

   The command doesn't produce any output. You may need to wait for a moment until the update is complete. To check the status, run the [describe-index](https://docs.aws.amazon.com//cli/latest/reference/iot/describe-index.html) CLI command. If you see `indexStatus` shows: `ACTIVE`, your thing indexing update is complete.

1. Run the command to verify your indexing configuration. This step is optional.

   ```
   aws iot get-indexing-configuration
   ```

   The output can look like the following:

   ```
   {
       "thingIndexingConfiguration": {
           "thingIndexingMode": "REGISTRY",
           "thingConnectivityIndexingMode": "OFF",
           "deviceDefenderIndexingMode": "OFF",
           "namedShadowIndexingMode": "ON",
           "managedFields": [
               {
                   "name": "shadow.name.*.hasDelta",
                   "type": "Boolean"
               },
               {
                   "name": "registry.version",
                   "type": "Number"
               },
               {
                   "name": "registry.thingTypeName",
                   "type": "String"
               },
               {
                   "name": "registry.thingGroupNames",
                   "type": "String"
               },
               {
                   "name": "shadow.name.*.version",
                   "type": "Number"
               },
               {
                   "name": "thingName",
                   "type": "String"
               },
               {
                   "name": "thingId",
                   "type": "String"
               }
           ],
           "customFields": [
               {
                   "name": "attributes.battery",
                   "type": "Number"
               }
           ],
           "filter": {
               "namedShadowNames": [
                   "Bike1-shadow"
               ],
               "geoLocations": [
                   {
                       "name": "shadow.name.Bike1-shadow.reported.coordinates",
                       "order": "LatLon"
                   }
               ]
           }
       },
       "thingGroupIndexingConfiguration": {
           "thingGroupIndexingMode": "OFF"
       }
   }
   ```

## Run geoquery
<a name="location-indexing-run-geoquery"></a>

Now you have updated your thing indexing configuration to include the location data. Try to create some geoqueries and run them to see if you can get your desired search results. A geoquery must follow the [Query syntax](query-syntax.md). You can find some useful example geoqueries in [Example geoqueries](location-indexing-geoquery.md#location-indexing-geoqueries).

In the following example command, you use the geoquery `shadow.name.Bike1-shadow.reported.coordinates:geo_distance,47.6204,-122.3491,15.5km` to search for devices that are within the radial distance of 15.5 km from the center point with coordinates (47.6204,-122.3491). 

```
aws iot search-index --query-string "shadow.name.Bike1-shadow.reported.coordinates:geo_distance,47.6204,-122.3491,15.5km"
```

Because you have a device located at the coordinates "lat": 47.6153, "lon": -122.3333, which falls within the distance of 15.5 km of the center point, you should be able to see this device (Bike-1) in the output. The output can look like the following:

```
{
    "things": [
        {
            "thingName": "Bike-1",
            "thingId": "df9cf01d-b0c8-48fe-a2e2-e16cff6b23df",
            "attributes": {
                "acqDate": "06/09/23",
                "battery": "35",
                "model": "OEM-2302-12"
            },
            "shadow": "{\"reported\":{\"coordinates\":{\"lat\":47.6153,\"lon\":-122.3333}},\"metadata\":{\"reported\":{\"coordinates\":{\"lat\":{\"timestamp\":1699572906},\"lon\":{\"timestamp\":1699572906}}}},\"hasDelta\":false,\"version\":1}"
        }
    ]
}
```

For more information, see [Indexing location data](location-indexing-geoquery.md).