142 lines
4.8 KiB
PHP
142 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* Forward-Geocoding via Google: "El Gaucho Wien" → lat/lng + sauberer Adresse.
|
|
* Wird genutzt wenn der LLM einen Ort nennt der nicht in der aktuellen POI-Liste ist.
|
|
*/
|
|
class FoxGeocodeController
|
|
{
|
|
public function __invoke(Request $request): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'query' => 'required|string|max:200',
|
|
'bias_lat' => 'nullable|numeric',
|
|
'bias_lng' => 'nullable|numeric',
|
|
]);
|
|
|
|
$key = config('services.google.maps_key');
|
|
if (empty($key)) {
|
|
return response()->json(['error' => 'Geocoding nicht konfiguriert'], 503);
|
|
}
|
|
|
|
$query = $request->string('query')->toString();
|
|
$biasLat = $request->filled('bias_lat') ? (float) $request->input('bias_lat') : null;
|
|
$biasLng = $request->filled('bias_lng') ? (float) $request->input('bias_lng') : null;
|
|
|
|
// 1. Versuch: Places Text Search - findet POIs anhand des Namens
|
|
$hit = $this->tryPlacesText($query, $biasLat, $biasLng, $key);
|
|
if ($hit) {
|
|
return response()->json($hit);
|
|
}
|
|
|
|
// 2. Fallback: Forward Geocoding - falls's eine Adresse ist
|
|
$hit = $this->tryGeocode($query, $biasLat, $biasLng, $key);
|
|
if ($hit) {
|
|
return response()->json($hit);
|
|
}
|
|
|
|
return response()->json(['error' => 'not_found'], 404);
|
|
}
|
|
|
|
protected function tryPlacesText(string $query, ?float $lat, ?float $lng, string $key): ?array
|
|
{
|
|
try {
|
|
$body = [
|
|
'textQuery' => $query,
|
|
'languageCode' => 'de',
|
|
'regionCode' => 'AT',
|
|
'maxResultCount' => 1,
|
|
];
|
|
if ($lat !== null && $lng !== null) {
|
|
$body['locationBias'] = [
|
|
'circle' => [
|
|
'center' => ['latitude' => $lat, 'longitude' => $lng],
|
|
'radius' => 20_000.0,
|
|
],
|
|
];
|
|
}
|
|
|
|
$response = Http::timeout(8)
|
|
->withHeaders([
|
|
'Content-Type' => 'application/json',
|
|
'X-Goog-Api-Key' => $key,
|
|
'X-Goog-FieldMask' => 'places.id,places.displayName,places.formattedAddress,places.location',
|
|
])
|
|
->post('https://places.googleapis.com/v1/places:searchText', $body);
|
|
|
|
if (! $response->successful()) {
|
|
Log::warning('Places-Text HTTP-Fehler', ['status' => $response->status(), 'body' => mb_substr($response->body(), 0, 200)]);
|
|
|
|
return null;
|
|
}
|
|
|
|
$place = $response->json('places.0');
|
|
if (! $place) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'name' => $place['displayName']['text'] ?? null,
|
|
'address' => $place['formattedAddress'] ?? '',
|
|
'lat' => $place['location']['latitude'] ?? null,
|
|
'lng' => $place['location']['longitude'] ?? null,
|
|
'place_id' => $place['id'] ?? null,
|
|
'source' => 'places_text',
|
|
];
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Places-Text exception', ['e' => $e->getMessage()]);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
protected function tryGeocode(string $query, ?float $lat, ?float $lng, string $key): ?array
|
|
{
|
|
$params = [
|
|
'address' => $query,
|
|
'language' => 'de',
|
|
'region' => 'at',
|
|
'key' => $key,
|
|
];
|
|
if ($lat !== null && $lng !== null) {
|
|
$params['bounds'] = sprintf('%f,%f|%f,%f', $lat - 0.1, $lng - 0.15, $lat + 0.1, $lng + 0.15);
|
|
}
|
|
|
|
try {
|
|
$response = Http::timeout(8)
|
|
->get('https://maps.googleapis.com/maps/api/geocode/json', $params);
|
|
|
|
if (! $response->successful()) {
|
|
return null;
|
|
}
|
|
|
|
$data = $response->json();
|
|
if (($data['status'] ?? null) !== 'OK' || empty($data['results'])) {
|
|
return null;
|
|
}
|
|
|
|
$r = $data['results'][0];
|
|
|
|
return [
|
|
'name' => $r['address_components'][0]['long_name'] ?? null,
|
|
'address' => $r['formatted_address'] ?? '',
|
|
'lat' => $r['geometry']['location']['lat'] ?? null,
|
|
'lng' => $r['geometry']['location']['lng'] ?? null,
|
|
'place_id' => $r['place_id'] ?? null,
|
|
'source' => 'geocode',
|
|
];
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Geocode exception', ['e' => $e->getMessage()]);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|