| """ |
| Location Intelligence service β estimates where an image was captured. |
| |
| Combines evidence from: |
| - GPS (from EXIF metadata, via cores.metadata) |
| - OCR text (language + keyword detection) |
| - License plate format |
| - Scene labels |
| - Detected objects (vehicle presence = street/road context) |
| |
| Returns a LocationEstimate with candidate countries, cities, and |
| conflicting evidence. No heavy geolocation models β pure heuristics. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import time |
| from typing import Optional |
|
|
| from cores.location import LocationEvidenceCollector, LocationEstimator |
| from cores.metadata import extract_all |
| from models.jobs import JobRequest |
| from models.reports import LocationEstimate |
| from pipeline import InputValidator, ImagePreprocessor, ImageHasher |
| from utils.logging import execution_context, new_execution_id |
|
|
|
|
| class LocationIntelligenceService: |
| """Estimates image capture location from multiple evidence sources.""" |
|
|
| def __init__( |
| self, |
| validator: InputValidator, |
| preprocessor: ImagePreprocessor, |
| hasher: ImageHasher, |
| ) -> None: |
| self._validator = validator |
| self._preprocessor = preprocessor |
| self._hasher = hasher |
|
|
| async def analyze(self, request: JobRequest) -> dict: |
| """Estimate location from an image.""" |
| eid = new_execution_id() |
| with execution_context(execution_id=eid, provider_id="location_intelligence_service"): |
| t0 = time.perf_counter() |
|
|
| vr = self._validator.validate( |
| image_url=request.image_url, |
| image_base64=request.image_base64, |
| ) |
| if not vr.valid: |
| return {"success": False, "error": vr.error, "error_type": "ValidationError"} |
|
|
| if vr.source == "url": |
| pre = self._preprocessor.from_url(request.image_url) |
| else: |
| pre = self._preprocessor.from_bytes(vr.image_bytes, vr.source) |
|
|
| original_bytes = pre.original_bytes or vr.image_bytes |
|
|
| collector = LocationEvidenceCollector() |
| gps: Optional[dict] = None |
|
|
| |
| if original_bytes: |
| meta = extract_all(original_bytes) |
| if meta.get("gps_coords"): |
| gps = meta["gps_coords"] |
| collector.add_gps(gps["lat"], gps["lon"]) |
|
|
| |
| |
| ocr_text = request.options.get("ocr_text", "") |
| if ocr_text: |
| collector.add_ocr_text(ocr_text) |
|
|
| |
| plate_text = request.options.get("plate_text", "") |
| if plate_text: |
| collector.add_plate(plate_text) |
|
|
| |
| scene_label = request.options.get("scene_label", "") |
| if scene_label: |
| collector.add_scene(scene_label) |
|
|
| |
| objects = request.options.get("objects", []) |
| if objects: |
| collector.add_detected_objects(objects) |
|
|
| |
| estimator = LocationEstimator() |
| estimate = estimator.estimate(collector.evidence, gps=gps) |
|
|
| elapsed = (time.perf_counter() - t0) * 1000.0 |
| estimate.elapsed_ms = round(elapsed, 3) |
|
|
| return { |
| "success": True, |
| "location_estimate": estimate.model_dump(), |
| "elapsed_ms": round(elapsed, 3), |
| } |
|
|