Add geocoding to C++ pipeline

This commit is contained in:
Aaron Po
2026-08-25 21:33:32 -04:00
parent 8a047e8d9f
commit 3d2b1549b2
21 changed files with 373 additions and 184 deletions

View File

@@ -13,6 +13,11 @@
// --- concurrency ---
#include "concurrency/bounded_channel.h"
// --- services: address ---
#include "services/address/address_service.h"
#include "services/address/mock_address_service.h"
#include "services/address/nominatim_address_service.h"
// --- data_generation ---
#include "data_generation/data_generator.h"
#include "data_generation/generation_json_validation.h"

View File

@@ -12,6 +12,7 @@
#include "data_generation/data_generator.h"
#include "data_model/generated_models.h"
#include "services/address/address_service.h"
#include "services/curated_data/curated_data_service.h"
#include "services/database/export_service.h"
#include "services/enrichment/enrichment_service.h"
@@ -34,6 +35,8 @@ class BiergartenPipelineOrchestrator {
* @param exporter Database backend for persisting generated records.
* @param curated_data_service Loads curated location, persona, and name
* data used to seed generation.
* @param address_service Resolves a street address for generated
* brewery coordinates.
* @param application_options CLI configuration and paths.
*/
BiergartenPipelineOrchestrator(
@@ -42,6 +45,7 @@ class BiergartenPipelineOrchestrator {
std::unique_ptr<DataGenerator> generator,
std::unique_ptr<IExportService> exporter,
std::unique_ptr<ICuratedDataService> curated_data_service,
std::unique_ptr<IAddressService> address_service,
const ApplicationOptions& application_options);
/**
@@ -78,6 +82,7 @@ class BiergartenPipelineOrchestrator {
*/
std::unique_ptr<IExportService> exporter_;
std::unique_ptr<ICuratedDataService> curated_data_service_;
std::unique_ptr<IAddressService> address_service_;
ApplicationOptions application_options_;

View File

@@ -95,6 +95,18 @@ struct BreweryAddress {
City city;
double longitude{};
double latitude{};
/**
* @brief Street address line (e.g. house number + road), resolved once
* via reverse geocoding at generation time.
*/
std::string address_line1{};
/**
* @brief Postal code, resolved once via reverse geocoding at generation
* time.
*/
std::string postal_code{};
};
/**

View File

@@ -0,0 +1,40 @@
#ifndef BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ADDRESS_ADDRESS_SERVICE_H_
#define BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ADDRESS_ADDRESS_SERVICE_H_
/**
* @file services/address/address_service.h
* @brief Abstraction for resolving a street address from coordinates.
*/
#include <optional>
#include <string>
/**
* @brief Street-level address resolved from a coordinate pair.
*/
struct AddressLookupResult {
std::string address_line1;
std::string postal_code;
};
/**
* @brief Interface for services that reverse-geocode coordinates into a
* street address.
*/
class IAddressService {
public:
virtual ~IAddressService() = default;
/**
* @brief Reverse-geocodes a coordinate pair into a street address.
*
* @param longitude Longitude of the point to resolve.
* @param latitude Latitude of the point to resolve.
* @return The resolved address, or std::nullopt if it could not be
* resolved.
*/
virtual std::optional<AddressLookupResult> ReverseGeocode(
double longitude, double latitude) = 0;
};
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ADDRESS_ADDRESS_SERVICE_H_

View File

@@ -0,0 +1,27 @@
#ifndef BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ADDRESS_MOCK_ADDRESS_SERVICE_H_
#define BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ADDRESS_MOCK_ADDRESS_SERVICE_H_
/**
* @file services/address/mock_address_service.h
* @brief Deterministic IAddressService used when network geocoding is
* disabled.
*/
#include <optional>
#include "services/address/address_service.h"
/**
* @brief Address service that returns a fixed placeholder address for any
* coordinate pair, without making network calls.
*/
class MockAddressService final : public IAddressService {
public:
std::optional<AddressLookupResult> ReverseGeocode(
double /*longitude*/, double /*latitude*/) override {
return AddressLookupResult{.address_line1 = "123 Mock Street",
.postal_code = "00000"};
}
};
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ADDRESS_MOCK_ADDRESS_SERVICE_H_

View File

@@ -0,0 +1,37 @@
#ifndef BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ADDRESS_NOMINATIM_ADDRESS_SERVICE_H_
#define BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ADDRESS_NOMINATIM_ADDRESS_SERVICE_H_
/**
* @file services/address/nominatim_address_service.h
* @brief Reverse geocoding backed by the public Nominatim API.
*/
#include <memory>
#include <optional>
#include "services/address/address_service.h"
#include "services/logging/logger.h"
#include "web_client/web_client.h"
/**
* @brief Reverse-geocodes coordinates into street addresses via the public
* Nominatim API (https://nominatim.openstreetmap.org).
*
* Required for API usage:
* - single identifying User-Agent and at most one request per
* second (https://operations.osmfoundation.org/policies/nominatim/).
*/
class NominatimAddressService final : public IAddressService {
public:
explicit NominatimAddressService(std::unique_ptr<WebClient> client,
std::shared_ptr<ILogger> logger);
std::optional<AddressLookupResult> ReverseGeocode(
double longitude, double latitude) override;
private:
std::unique_ptr<WebClient> client_;
std::shared_ptr<ILogger> logger_;
};
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ADDRESS_NOMINATIM_ADDRESS_SERVICE_H_

View File

@@ -56,6 +56,8 @@ CREATE TABLE IF NOT EXISTS brewery_addresses (
city_id INTEGER NOT NULL,
longitude REAL NOT NULL,
latitude REAL NOT NULL,
address_line1 TEXT NOT NULL,
postal_code TEXT NOT NULL,
FOREIGN KEY(brewery_id) REFERENCES breweries(id) ON DELETE CASCADE,
FOREIGN KEY(city_id) REFERENCES cities(id) ON DELETE CASCADE
);
@@ -131,8 +133,10 @@ INSERT INTO brewery_addresses (
brewery_id,
city_id,
longitude,
latitude
) VALUES (?, ?, ?, ?);
latitude,
address_line1,
postal_code
) VALUES (?, ?, ?, ?, ?, ?);
)sql";
inline constexpr std::string_view kInsertUserSql = R"sql(
@@ -184,6 +188,8 @@ enum BreweryAddressBindIndex {
kBreweryAddressCityIdBindIndex,
kBreweryAddressLongitudeBindIndex,
kBreweryAddressLatitudeBindIndex,
kBreweryAddressLine1BindIndex,
kBreweryAddressPostalCodeBindIndex,
};
enum UserBindIndex {