---
title: Compute driving distances
summary: Compute driving distance and time between one or more address pairs via the Google Distance Matrix API.
status: draft
owner: SAIF.Core Documentation Team
last_reviewed: 2026-09-24
library: SAIF.Services.Web.Proxies
reference_type: package
operation_id: OP-WP-09
trigger: SAIF.Services.Legal.Subpoenas.SubpoenaService, through the injected IGoogleMapsApiServiceProxy
---

# Compute driving distances

`GoogleMapsApiServiceProxy` is the library's one hand-rolled REST/XML client: it queries the Google Distance Matrix API directly over `System.Net.WebRequest`, then exposes several distance-computation methods with different single-pair, one-to-many, and error-tolerance behaviors.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-WP-09 |
| **Trigger** | `SAIF.Services.Legal.Subpoenas.SubpoenaService`, through the constructor-injected `IGoogleMapsApiServiceProxy` |
| **Inputs** | Origin and destination address strings (single or multiple) |
| **Outputs** | A `DistanceResults`, a list of miles, or an `ErrorMessage` |
| **Primary components** | `GoogleMapsApiServiceProxy.QueryGoogleApi`, `GetDataBetweenTwoAddresses`, `GetMilesBetweenTwoAddresses`, `GetMilesFromManyToOneAddress`, `GetMilesFromOneToManyAddresses`, `GetMilesBetweenMultipleAddresses` |
| **Entry point** | `Services/SAIF.Services.Web.Proxies/GoogleMapsApiServiceProxy.cs` |

## Steps

1. Construct `GoogleMapsApiServiceProxy(ILogService, IEnvironmentService)`: guard both services ([WP-BR-001](../business-rules.md#wp-br-001-required-arguments-must-be-non-null-or-non-empty)), then read `googlemaps.apibaseuri` and `googlemaps.apikey` via `GetConfigSetting` ([WP-BR-020](../business-rules.md#wp-br-020-google-maps-base-uri-and-api-key-come-from-configuration)).
2. **`QueryGoogleApi(origins, destinations)` (private):** ([WP-BR-021](../business-rules.md#wp-br-021-distance-queries-are-pipe-delimited-with-fixed-units-and-timeout))
   1. Build the URL: `{baseuri}distancematrix/xml?units=imperial&language=en&origins=a|b&destinations=c|d&key={apikey}`; each address has `%3Cbr%3E` replaced by a space, and addresses are not URL-encoded.
   2. Send a `WebRequest` with a 5000 ms timeout, and load the response as an `XDocument`.
      - On failure: log via `ILogService.LogException("GoogleMapsApiServiceProxy", "QueryGoogleApi", "WebRequest", e)`, then throw `new Exception(e.Message)` — the original exception type and stack trace are lost*, and the response/stream are not disposed*.
3. **`GetDataBetweenTwoAddresses(o, d)`:**
   1. Query the API (step 2).
   2. If the first `element/status` is `"OK"`: set `Miles` from `distance/value` (meters ÷ 1609.34, rounded to 1 decimal — [WP-BR-022](../business-rules.md#wp-br-022-miles-are-computed-from-meters-at-a-fixed-conversion-factor)), `Distance` from `distance/text`, `Time` from `duration/text`, and `Seconds` from `duration/value`.
      - If parsing fails: throw `Exception("Google Maps API Service Error - unable to calculate mileage.")`.
   3. Otherwise: set `ErrorMessage` to the status message plus the status; nothing is thrown ([WP-BR-023](../business-rules.md#wp-br-023-a-non-ok-element-status-sets-an-error-message-without-throwing)).
4. **`GetMilesBetweenTwoAddresses(o, d)`:** query the API, then convert the first `distance/value` to miles; any failure throws the generic mileage error.
5. **`GetMilesFromManyToOneAddress(origins, destination)` / `GetMilesFromOneToManyAddresses(origin, destinations)`:** query the API, then take each `element`'s distance in miles.
   - A failing element yields 0 and does not throw ([WP-BR-024](../business-rules.md#wp-br-024-a-failing-element-contributes-zero-miles)).
6. **`GetMilesBetweenMultipleAddresses(origins, destinations)`:**
   1. If the origin and destination counts differ: throw before any query is sent ([WP-BR-025](../business-rules.md#wp-br-025-pairwise-distance-queries-require-equal-counts)).
   2. Query the full matrix (step 2).
   3. For row *i*, take element *i*, pairing origin *i* with destination *i* (the diagonal).
   4. A failing element yields 0 ([WP-BR-024](../business-rules.md#wp-br-024-a-failing-element-contributes-zero-miles)).

## Process flow

The following diagram visualizes the shared query path and its two error-handling behaviors (throw vs. error-in-result).

```mermaid
flowchart TD
	Start([Distance method invoked]) --> CountCheck{Multi-address:<br>counts equal?}
	CountCheck -->|No| Throw1([Throw before querying])
	CountCheck -->|Yes / n-a| Build["Build pipe-delimited,<br>non-encoded URL"]
	Build --> Web["Send WebRequest<br>(5000 ms timeout)"]
	Web -->|Exception| LogThrow["Log via ILogService,<br>throw new Exception(e.Message)"]
	Web -->|Success| Parse{Element status OK?}
	Parse -->|Yes| Convert["Convert meters to miles<br>(÷1609.34, round 1 decimal)"] --> Ret([Return result])
	Parse -->|No, single pair| Err(["Set ErrorMessage,<br>do not throw"])
	Parse -->|No, multi-address element| Zero(["Element contributes 0 miles"])
```

## Failure and edge-case behavior

- Mismatched origin/destination counts in `GetMilesBetweenMultipleAddresses` throw before any HTTP request is sent.
- Any network or parsing exception in `QueryGoogleApi` is logged, then rethrown as a generic `Exception` with only the original message — the original exception type and stack trace are lost*.
- A non-`"OK"` status for a single-pair query (`GetDataBetweenTwoAddresses`) sets `ErrorMessage` without throwing; the same condition for one element in a multi-address query yields 0 miles for that element rather than an error or a thrown exception.
- Addresses are not URL-encoded before being placed in the query string, which risks malformed requests or parameter injection from address text containing reserved characters*.
- The Google API key is sent in the request query string, so it can appear in proxy or server access logs*.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Sources
- `Services/SAIF.Services.Web.Proxies/GoogleMapsApiServiceProxy.cs`
