# What Is TeslaMate.Locations? Purpose and Implementation in the TeslaMate Elixir Codebase

> Discover the purpose of TeslaMate Locations, the central module for geographic data. Understand its role in reverse geocoding, geofence management, and spatial analytics within the TeslaMate application.

- Repository: [TeslaMate/teslamate](https://github.com/teslamate-org/teslamate)
- Tags: internals
- Published: 2026-06-18

---

**TeslaMate.Locations is the central context module that consolidates all geographic data operations, providing a unified API for reverse geocoding, geofence management, and spatial analytics in the TeslaMate application.**

TeslaMate.Locations serves as the primary interface for location-based functionality in the [teslamate-org/teslamate](https://github.com/teslamate-org/teslamate) repository. This module abstracts complex spatial operations—such as coordinate resolution and spatial relationship queries—into a clean API that higher-level components use without handling raw SQL or external geocoding services directly. Located at [`lib/teslamate/locations.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/locations.ex), it acts as the boundary layer between the database schemas and the business logic for mapping and billing.

## Core Responsibilities and Implementation

### Address Handling via Reverse Geocoding

The module centralizes **address resolution** through the `find_address/1` function, which accepts latitude and longitude coordinates and returns a persisted `%Address{}` struct. Internally, this function calls an external geocoding service (Nominatim/OpenStreetMap via the `Geocoder` module or its test mock) to perform reverse lookups, as defined by the module attribute `@geocoder` (lines 30-33). 

If an address exists for the coordinates, it returns the cached record; otherwise, `create_address/1` persists the new data. To maintain data freshness, `refresh_addresses/1` periodically re-queries the geocoder for stored addresses, ensuring the local database reflects current OpenStreetMap data (lines 35-48, 50-84).

### Geofence CRUD Operations and Spatial Linkage

`TeslaMate.Locations` manages user-defined geographic boundaries through standard **CRUD operations**: `list_geofences/0`, `create_geofence/1`, `update_geofence/2`, and `delete_geofence/1`. These thin wrappers around Ecto queries handle the persistence of `%GeoFence{}` structs defined in [`lib/teslamate/locations/geo_fence.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/locations/geo_fence.ex).

The critical spatial linkage occurs in `apply_geofence/2`, which executes raw SQL updates to automatically associate `drives` and `charging_processes` records with their nearest geofence by populating the appropriate `*_geofence_id` foreign keys (lines 28-58). The `find_geofence/1` function utilizes custom Ecto expressions from `TeslaMate.CustomExpressions` to determine if coordinates fall within a fence's radius using the `within_geofence?` helper.

### Cost Calculation Analytics

For billing integration, the module provides **geofence-related analytics** that bridge spatial data with financial calculations. `count_charging_processes_without_costs/1` constructs an Ecto query joining `charging_processes` with their positions to identify unpriced sessions within a specific geofence (lines 22-55).

The `calculate_charge_costs/1` function executes a custom SQL UPDATE statement that applies billing rules stored in the geofence record—such as per-kWh rates or flat session fees—to all associated charging processes, automating cost attribution based on physical location.

### Dependency Injection for Testing

The module implements **test isolation** through compile-time environment checks. When `Mix.env() == :test`, the `@geocoder` module attribute injects a mock implementation instead of the live Nominatim client, allowing unit tests to run without external API dependencies while preserving production behavior.

## Practical API Usage Examples

The following snippets demonstrate typical interactions with the `TeslaMate.Locations` API from an Elixir console or within another module:

```elixir

# Resolve an address from GPS coordinates

{:ok, address} = TeslaMate.Locations.find_address(%{latitude: 52.5200, longitude: 13.4050})
IO.inspect(address)

```

```elixir

# Create a new geofence (e.g., "Home" with a 200 m radius)

attrs = %{name: "Home", latitude: 52.5200, longitude: 13.4050, radius: 200}
{:ok, geofence} = TeslaMate.Locations.create_geofence(attrs)
IO.inspect(geofence)

```

```elixir

# List all geofences ordered alphabetically

geofences = TeslaMate.Locations.list_geofences()
Enum.each(geofences, &IO.puts(&1.name))

```

```elixir

# Find the geofence containing a specific point

%{latitude: lat, longitude: lng} = %{latitude: 52.5205, longitude: 13.4045}
geofence = TeslaMate.Locations.find_geofence(%{latitude: lat, longitude: lng})
IO.inspect(geofence)

```

```elixir

# Count charging processes inside a fence lacking cost data

count = TeslaMate.Locations.count_charging_processes_without_costs(geofence)
IO.puts("Unpriced processes: #{count}")

```

```elixir

# Trigger cost calculation for a specific fence's billing rules

:ok = TeslaMate.Locations.calculate_charge_costs(geofence)

```

## Key Source Files and Architecture

Understanding `TeslaMate.Locations` requires familiarity with its supporting modules:

- **[`lib/teslamate/locations.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/locations.ex)** – The central context module containing the public API and core logic for addresses and geofences.
- **[`lib/teslamate/locations/address.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/locations/address.ex)** – Ecto schema and changeset definitions for persisted address records.
- **[`lib/teslamate/locations/geocoder.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/locations/geocoder.ex)** – Wrapper module for external Nominatim/OpenStreetMap reverse-lookup services.
- **[`lib/teslamate/locations/geo_fence.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/locations/geo_fence.ex)** – Ecto schema and changeset for user-defined geofence boundaries.
- **[`lib/teslamate/custom_expressions.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/custom_expressions.ex)** – Custom SQL fragments for spatial calculations, including `within_geofence?` and distance functions used throughout the context.

## Summary

- **TeslaMate.Locations** is the primary Elixir context module for all geographic data operations in the TeslaMate ecosystem.
- It provides **reverse geocoding** via `find_address/1` and `refresh_addresses/1`, caching OpenStreetMap results to minimize API calls.
- The module handles **geofence lifecycle management** through CRUD functions and automatically links drives and charging processes to fences via `apply_geofence/2`.
- **Cost analytics** functions like `calculate_charge_costs/1` enable automated billing based on geofence-specific rates.
- **Custom Ecto expressions** from `TeslaMate.CustomExpressions` power spatial queries without sacrificing database performance.
- The module supports **test mocking** through the `@geocoder` attribute, ensuring reliable CI/CD pipelines.

## Frequently Asked Questions

### What is the primary purpose of TeslaMate.Locations?

TeslaMate.Locations is the central Elixir context module that consolidates all geographic data operations. It provides a clean boundary API for reverse geocoding coordinates, managing geofences, and calculating charging costs based on physical location, allowing other parts of the application to work with spatial data without handling raw SQL or external API calls.

### How does TeslaMate.Locations handle reverse geocoding?

The module uses the `find_address/1` function to query an external geocoding service (Nominatim/OpenStreetMap) via the injected `@geocoder` module. It caches results in the local database through `create_address/1` and periodically refreshes stored addresses using `refresh_addresses/1` to ensure data accuracy while minimizing redundant API requests.

### What is the relationship between geofences and charging processes?

Geofences define geographic boundaries with associated billing rules. The `apply_geofence/2` function runs raw SQL updates to automatically set `geofence_id` fields on `drives` and `charging_processes` records. Subsequently, `calculate_charge_costs/1` applies the geofence's billing configuration to compute costs for all associated charging sessions.

### How does the module support testing without external API dependencies?

The module uses a module attribute `@geocoder` that evaluates `Mix.env()` at compile time. When the environment is `:test`, it injects a mock geocoder module instead of the live Nominatim client, allowing tests to run offline with predictable responses while maintaining the same function signatures as production code.