brails.utils.arcgisapi_service_helper module

This module defines a class for retrieving data from ArcGIS services APIs.

ArcgisAPIServiceHelper(api_endpoint_url)

A helper class for interacting with an ArcGIS API service.

class brails.utils.arcgisapi_service_helper.ArcgisAPIServiceHelper(api_endpoint_url: str)

Bases: object

A helper class for interacting with an ArcGIS API service.

This class provides methods to query the service, fetch element counts, split polygons into cells, and categorize cells based on their element count.

This class allows users to interact with an ArcGIS API endpoint to retrieve information about elements within specific polygons. It supports functionality for: - Retrieving the maximum number of elements that can be returned per query. - Categorizing and splitting polygon cells based on the element count. - Dividing polygons into smaller rectangular cells to ensure a balance of

elements per cell.

  • Fetching specified attribute data for given cells in parallel.

The class includes methods for making API requests with retry logic, handling transient network errors, and parsing the responses to determine element counts within specific regions.

Attributes:
api_endpoint_url (str):

The base URL of the ArcGIS API endpoint used for requests.

max_elements_per_cell (int):

The maximum number of elements the API allows per query, fetched from the ArcGIS service.

Methods:
fetch_max_records_per_query() -> int:

Retrieves the maximum number of records returned by the API in a single query.

categorize_and_split_cells(preliminary_cells: list[Polygon]

) -> tuple[list[Polygon], list[Polygon]]:

Categorizes and splits polygons based on their element count. Returns two lists: cells to keep and cells to split.

split_polygon_into_cells(bpoly: Polygon, element_count: int = -1,

plot_mesh: str = ‘’) -> list[Polygon]:

Divides a polygon into smaller cells if the element count exceeds the threshold.

download_attr_from_api(cell: Polygon,

requested_fields: list[str] | str) -> list:

Fetches specific attribute fields from the API for a given polygon.

get_element_counts(bpoly: Polygon) -> int:

Retrieves the count of elements within a given polygon’s bounding box.

fetch_data_for_cells(final_cells: list, download_func: Callable,

desc: str) -> dict:

Concurrently applies a download function to a list of cells with progress tracking.

Example Usage:

# Instantiate the helper class with an API endpoint: api_helper = ArcgisAPIServiceHelper(”https://api.example.com/query”)

# Fetch the maximum number of records per query: max_records = api_helper.fetch_max_records_per_query()

# Split polygons into cells based on element count: cells_to_keep, cells_to_split = api_helper.categorize_and_split_cells(

preliminary_cells)

Notes:
  • The class assumes a uniform distribution of elements across polygons when splitting.

  • The retry strategy uses exponential backoff to handle transient network issues.

  • The method get_element_counts provides a count of elements within the bounding box of a polygon, which is essential for categorizing and splitting polygons.

Raises:

ValueError: If an invalid Polygon or parameter is passed to a method. NotImplementedError: If the requested functionality is not supported by the API. HTTPError: If the HTTP request fails after retries.

categorize_and_split_cells(preliminary_cells: List[Polygon]) Tuple[List[Polygon], List[Polygon]]

Categorize/split a list of polygon cells based on their element count.

This method processes a list of polygons (preliminary_cells) by first obtaining the number of elements contained within each polygon. If a polygon contains more elements than the specified maximum allowed per cell (max_elements_per_cell), the polygon is split into smaller cells. The method returns two lists: - A list of cells that are kept as is (those that do not exceed the

element threshold).

  • A list of split cells (those that exceeded the element threshold).

Args:
preliminary_cells (list[Polygon]):

A list of Shapely Polygon objects representing the preliminary cells to be processed.

Returns:
tuple: A tuple containing two lists of Polygon objects:
  • The first list contains the cells to keep (those with elements <= max_elements_per_cell).

  • The second list contains the split cells (those with elements > max_elements_per_cell).

download_all_attr_for_region(region, plot_cells=False, task_description='Obtaining attributes for each cell')

Download all attribute data for the specified region.

This method:
  • Retrieves the boundary polygon of the region.

  • Splits region into smaller cells for manageable data querying.

  • Refines the mesh by recursively splitting oversized cells.

  • Optionally plots the final mesh.

  • Downloads data (e.g., bridge attributes) for each cell using a provided data-fetching function.

Args:
region:

A Region object that provides a .get_boundary() method returning a polygon, region name, and optional OSM ID.

plot_cells (bool, optional):

If True, generates and saves a visualization of the final meshed cells.

task_description (str, optional):

A message string that describes the task being performed, passed to the data-fetching method for logging or display.

Returns:
Tuple:
  • downloaded_data (Dict[Polygon, List[Dict[str, Any]]]):

    Mapping from each final mesh cell polygon to a list of attribute dictionaries (e.g., representing bridges or other assets).

  • final_cells (List[Polygon]):

    List of polygons representing the final meshed cells used for data querying.

download_all_attr_from_api(cell: Polygon) List[Dict[str, Any]]

Download attribute data for a given cell using the ArcGIS API.

This method queries the ArcGIS API for all available attributes within the specified polygon cell.

Args:
cell (Polygon):

A Shapely Polygon object representing the geographic cell for which to retrieve bridge data.

Returns:
List[Dict[str, Any]]:

A list of dictionaries, each representing the attribute data for a bridge within the cell. Each dictionary includes ‘geometry’ and ‘attributes’ keys as returned by the ArcGIS API.

download_attr_from_api(cell: Polygon, requested_fields: List[str] | str) List

Download specified fields from the API for a given cell.

Args:
cell (Polygon):

A Shapely Polygon object representing the area of interest.

requested_fields (list[str] or str):

A list of attribute names or the string ‘all’.

Returns:
list[dict]:

A list of features (attributes) fetched from the API.

Raises:
ValueError:

If the input ‘cell’ is not a valid Polygon or ‘requested_fields’ is not valid.

fetch_data_for_cells(final_cells: List[Any], download_func: Callable[[Any], Any], desc: str = 'Obtaining the attributes for each cell') Dict[Any, Any]

Download data for a list of cells in parallel using provided function.

Args:
final_cells (List[Any]):

List of cells to process.

download_func (Callable[[Any], Any]):

Function to download data for a single cell.

desc (str):

Description for the progress bar.

Returns:
Dict[Any, Any]:

Dictionary mapping each cell to its downloaded data (or None if failed).

fetch_max_records_per_query() int

Retrieve the maximum number of records returned by the API per query.

This function sends a request to the specified API endpoint and parses the response to determine the maximum number of records that can be returned in a single query. If the API does not provide this information or returns a value of 0, it raises an error.

Returns:
int:

The maximum number of elements the API allows per query.

Raises:
ValueError:

If the API returns a value of 0 for the maximum number of elements, indicating an issue with the API or its response.

HTTPError:

If the HTTP request fails (e.g., due to connectivity issues or a server error).

Example:
>>> api_tools = ArcgisAPIServiceHelper(
    "https://api.example.com/data/query")
>>> max_records = api_tools.fetch_max_records_per_query()
>>> print(max_records)
1000
Notes:
  • The function expects the API to return a JSON response containing a maxRecordCount key.

  • A retry strategy is implemented for the HTTPS request to handle transient network issues.

get_element_counts(bpoly: Polygon) int

Get the count of elements within the bounding box of the given polygon.

Args:
bpoly (Polygon):

The polygon marking the boundaries of a region.

Returns:
int:

The count of elements within the bounding box, or 0 if an error occurs.

split_polygon_into_cells(bpoly: Polygon, element_count: int = -1, plot_mesh: str = '') List[Polygon]

Divide a polygon into smaller cells based on its element count.

If the number of elements exceeds the specified threshold (max_elements_per_cell), the polygon will be divided into multiple rectangular cells with a margin of error. The grid is generated under the assumption that the elements are uniformly distributed across the polygon, and it can be split based on the ratio of element_count to max_elements_per_cell to get cells that do not exceed the max elements specified per cellapproximate balance. Please note that, this method does not guarantee that each cell will contain fewer than the specified maximum number of elements.

Args:
bpoly (Polygon):

The polygon to be split into rectangular cells.

element_count (int, optional):

The total number of elements in the polygon. If not provided, the method will compute this using get_element_counts.

plot_mesh (str, optional):

If provided, the generated mesh will be plotted using GeoTools.plot_polygon_cells.

Returns:
list:

A list of polygons (or bounding boxes) representing the rectangular grid cells that cover the input polygon.

Notes:
  • If the element count is below or equal to max_elements_per_cell, the entire polygon’s envelope is returned as a single cell.

  • If the element count exceeds max_elements_per_cell, the polygon is divided into smaller cells based on the bounding box aspect ratio.