What Is TeslaMate.Locations? Purpose and Implementation in the TeslaMate Elixir Codebase
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 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, 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.
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:
# Resolve an address from GPS coordinates
{:ok, address} = TeslaMate.Locations.find_address(%{latitude: 52.5200, longitude: 13.4050})
IO.inspect(address)
# 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)
# List all geofences ordered alphabetically
geofences = TeslaMate.Locations.list_geofences()
Enum.each(geofences, &IO.puts(&1.name))
# 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)
# Count charging processes inside a fence lacking cost data
count = TeslaMate.Locations.count_charging_processes_without_costs(geofence)
IO.puts("Unpriced processes: #{count}")
# 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– The central context module containing the public API and core logic for addresses and geofences.lib/teslamate/locations/address.ex– Ecto schema and changeset definitions for persisted address records.lib/teslamate/locations/geocoder.ex– Wrapper module for external Nominatim/OpenStreetMap reverse-lookup services.lib/teslamate/locations/geo_fence.ex– Ecto schema and changeset for user-defined geofence boundaries.lib/teslamate/custom_expressions.ex– Custom SQL fragments for spatial calculations, includingwithin_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/1andrefresh_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/1enable automated billing based on geofence-specific rates. - Custom Ecto expressions from
TeslaMate.CustomExpressionspower spatial queries without sacrificing database performance. - The module supports test mocking through the
@geocoderattribute, 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →