Documentation
fsds/spatial-azure-maps-geocoding-integration.md
Azure Maps Geocoding Integration for Spatial Component
Summary
Add geocoding functionality to the Spatial component using Azure Maps SDK to:
- Auto-geocode addresses on creation (fire-and-forget, fail silently)
- Manually geocode a specific address via API
- Batch geocode all addresses without coordinates (max 100 per request)
Key Decisions
- SDK:
Azure.Maps.SearchNuGet package withMapsSearchClient - Batch Limit: 100 addresses per API call
- Storage: Update BOTH
Address.Latitude/LongitudeAND createAzureAddressrecord - Config: Key Vault for production, appsettings.json fallback for local dev
Files to Create
1. Configuration Class
engines/spatial/src/Acsis.Dynaplex.Engines.Spatial/Configuration/AzureMapsConfiguration.cs
Enabled(bool) - master switchSubscriptionKey(string) - Azure Maps API keyBatchSize(int, default 100)AutoGeocodeOnCreate(bool, default true)
2. Service Interface
engines/spatial/src/Acsis.Dynaplex.Engines.Spatial.Abstractions/Services/IGeocodingService.cs
GeocodeAddressAsync(Guid addressId)- single addressGeocodeUnprocessedAddressesAsync(int? limit)- batchGeocodeAddressInBackground(Guid addressId, Guid tenantId)- fire-and-forget
3. Response DTOs
engines/spatial/src/Acsis.Dynaplex.Engines.Spatial.Abstractions/Responses/GeocodingResult.cs
GeocodingResult- single result with AddressId, Success, Lat/Long, Score, ErrorMessageBatchGeocodingResult- TotalRequested, Successful, Failed, Results list
4. Service Implementation
engines/spatial/src/Acsis.Dynaplex.Engines.Spatial/Services/AzureMapsGeocodingService.cs
- Inject
SpatialDb,IOptions<AzureMapsConfiguration>,ILogger,IServiceProvider - Create
MapsSearchClientwithAzureKeyCredential - Build query strings from address components (Line1, City, State, PostalCode, Country)
- Map Azure response to both
Address(lat/long) andAzureAddress(full details) - Fire-and-forget uses
Task.Runwith new scope (like Tn360SyncBackgroundService)
5. API Endpoints
engines/spatial/src/Acsis.Dynaplex.Engines.Spatial/ApiEngines/GeocodingApi.cs
POST /geocoding/address/{addressId:guid}- geocode single addressPOST /geocoding/batch?limit=N- batch geocode unprocessed addresses
Files to Modify
1. Directory.Packages.props (repo root)
Add: <PackageVersion Include="Azure.Maps.Search" Version="2.0.0-beta.5" />
2. Acsis.Dynaplex.Engines.Spatial.csproj
Add: <PackageReference Include="Azure.Maps.Search" />
3. Program.cs
engines/spatial/src/Acsis.Dynaplex.Engines.Spatial/Program.cs
After line ~67 (service registrations):
// Azure Maps Geocoding
var azureMapsConfig = builder.Configuration.GetSection(AzureMapsConfiguration.SECTION_NAME);
if (azureMapsConfig.Exists() && azureMapsConfig.Get<AzureMapsConfiguration>()?.Enabled == true)
{
builder.Services.Configure<AzureMapsConfiguration>(azureMapsConfig);
builder.Services.AddScoped<IGeocodingService, AzureMapsGeocodingService>();
}
In MapAcsisEndpoints call (~line 82):
Add GeocodingApi.MapGeocodingEndpoints
4. appsettings.Development.json
engines/spatial/src/Acsis.Dynaplex.Engines.Spatial/appsettings.Development.json
Add:
"AzureMaps": {
"Enabled": true,
"SubscriptionKey": "your-dev-key",
"BatchSize": 100,
"AutoGeocodeOnCreate": true
}
Auto-Geocoding Integration Point
When address creation is implemented (currently commented out in LocationsApi.cs), add after SaveChangesAsync:
geocodingService.GeocodeAddressInBackground(address.Id, address.TenantId);
The GeocodeAddressInBackground method:
- Checks if geocoding is enabled
- Creates new DI scope (avoids DbContext lifetime issues)
- Uses
_ = Task.Run(...)fire-and-forget pattern - Catches all exceptions, logs warning, never throws
Error Handling Strategy
| Scenario | Behavior |
|---|---|
| Geocoding disabled | Return early with message |
| Address not found | Return error result, log warning |
| Azure Maps API error | Return error result, log error |
| No results from API | Return error result, log info |
| Auto-geocode failure | Log warning only (never crash) |
| Batch item failure | Mark item failed, continue others |
Implementation Stages
Stage 1: Package and Configuration
- Add NuGet package to Directory.Packages.props
- Add package reference to Spatial.csproj
- Create
AzureMapsConfiguration.cs - Add config section to appsettings.Development.json
Stage 2: DTOs and Interface
- Create
GeocodingResult.csandBatchGeocodingResult.cs - Create
IGeocodingService.csinterface
Stage 3: Service Implementation
- Create
AzureMapsGeocodingService.cs - Implement single geocoding with Address + AzureAddress update
- Implement batch geocoding using
GetGeocodingBatch - Implement fire-and-forget background method
Stage 4: API Endpoints
- Create
GeocodingApi.cswith both endpoints - Register in Program.cs
Stage 5: Integration Testing
- Test single address geocoding
- Test batch geocoding
- Verify both Address and AzureAddress tables updated
Critical File Paths
engines/spatial/src/Acsis.Dynaplex.Engines.Spatial/Program.cs- service registrationengines/spatial/src/Acsis.Dynaplex.Engines.Spatial.Database/Address.cs- entity with Lat/Longengines/spatial/src/Acsis.Dynaplex.Engines.Spatial.Database/AzureAddress.cs- full geocoding detailsengines/iot/src/Acsis.Dynaplex.Engines.Iot/Program.cs:61-70- conditional registration patternengines/iot/src/Acsis.Dynaplex.Engines.Iot/Integrations/Emqx/EmqxApiClient.cs- external API pattern