bizmatch-project/bizmatch/src/app/services/geo.service.ts

158 lines
5.5 KiB
TypeScript

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { lastValueFrom, Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
import { CityAndStateResult, CountyResult, GeoResult, IpInfo } from '../../../../bizmatch-server/src/models/main.model';
import { Place } from '../../../../bizmatch-server/src/models/server.model';
import { environment } from '../../environments/environment';
interface CachedBoundary {
data: any;
timestamp: number;
}
@Injectable({
providedIn: 'root',
})
export class GeoService {
private apiBaseUrl = environment.apiBaseUrl;
private baseUrl: string = 'https://nominatim.openstreetmap.org/search';
private fetchingData: Observable<IpInfo> | null = null;
private readonly storageKey = 'ipInfo';
private readonly boundaryStoragePrefix = 'nominatim_boundary_';
private readonly cacheExpiration = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds
constructor(private http: HttpClient) {}
/**
* Get cached boundary data from localStorage
*/
private getCachedBoundary(cacheKey: string): any | null {
try {
const cached = localStorage.getItem(this.boundaryStoragePrefix + cacheKey);
if (!cached) {
return null;
}
const cachedData: CachedBoundary = JSON.parse(cached);
const now = Date.now();
// Check if cache has expired
if (now - cachedData.timestamp > this.cacheExpiration) {
localStorage.removeItem(this.boundaryStoragePrefix + cacheKey);
return null;
}
return cachedData.data;
} catch (error) {
console.error('Error reading boundary cache:', error);
return null;
}
}
/**
* Save boundary data to localStorage
*/
private setCachedBoundary(cacheKey: string, data: any): void {
try {
const cachedData: CachedBoundary = {
data: data,
timestamp: Date.now()
};
localStorage.setItem(this.boundaryStoragePrefix + cacheKey, JSON.stringify(cachedData));
} catch (error) {
console.error('Error saving boundary cache:', error);
}
}
/**
* Clear all cached boundary data
*/
clearBoundaryCache(): void {
try {
const keys = Object.keys(localStorage);
keys.forEach(key => {
if (key.startsWith(this.boundaryStoragePrefix)) {
localStorage.removeItem(key);
}
});
} catch (error) {
console.error('Error clearing boundary cache:', error);
}
}
findCitiesStartingWith(prefix: string, state?: string): Observable<GeoResult[]> {
const stateString = state ? `/${state}` : '';
return this.http.get<GeoResult[]>(`${this.apiBaseUrl}/bizmatch/geo/${prefix}${stateString}`);
}
findCitiesAndStatesStartingWith(prefix: string): Observable<CityAndStateResult[]> {
return this.http.get<CityAndStateResult[]>(`${this.apiBaseUrl}/bizmatch/geo/citiesandstates/${prefix}`);
}
findCountiesStartingWith(prefix: string, states?: string[]): Observable<CountyResult[]> {
return this.http.post<CountyResult[]>(`${this.apiBaseUrl}/bizmatch/geo/counties`, { prefix, states });
}
findLocationStartingWith(prefix: string): Observable<Place[]> {
let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
return this.http.get(`${this.baseUrl}?q=${prefix},US&format=json&addressdetails=1&limit=5`, { headers }) as Observable<Place[]>;
}
getCityBoundary(cityName: string, state: string): Observable<any> {
const cacheKey = `city_${cityName}_${state}`.toLowerCase().replace(/\s+/g, '_');
// Check cache first
const cached = this.getCachedBoundary(cacheKey);
if (cached) {
return of(cached);
}
// If not in cache, fetch from API
const query = `${cityName}, ${state}, USA`;
let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
return this.http.get(`${this.baseUrl}?q=${encodeURIComponent(query)}&format=json&polygon_geojson=1&limit=1`, { headers }).pipe(
tap(data => this.setCachedBoundary(cacheKey, data))
);
}
getStateBoundary(state: string): Observable<any> {
const cacheKey = `state_${state}`.toLowerCase().replace(/\s+/g, '_');
// Check cache first
const cached = this.getCachedBoundary(cacheKey);
if (cached) {
return of(cached);
}
// If not in cache, fetch from API
const query = `${state}, USA`;
let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
return this.http.get(`${this.baseUrl}?q=${encodeURIComponent(query)}&format=json&polygon_geojson=1&limit=1&featuretype=state`, { headers }).pipe(
tap(data => this.setCachedBoundary(cacheKey, data))
);
}
private fetchIpAndGeoLocation(): Observable<IpInfo> {
return this.http.get<IpInfo>(`${this.apiBaseUrl}/bizmatch/geo/ipinfo/georesult/wysiwyg`);
}
async getIpInfo(): Promise<IpInfo | null> {
// Versuche zuerst, die Daten aus dem sessionStorage zu holen
const storedData = sessionStorage.getItem(this.storageKey);
if (storedData) {
return JSON.parse(storedData);
}
try {
// Wenn keine Daten im Storage, hole sie vom Server
const data = await lastValueFrom(this.http.get<IpInfo>(`${this.apiBaseUrl}/bizmatch/geo/ipinfo/georesult/wysiwyg`));
// Speichere die Daten im sessionStorage
sessionStorage.setItem(this.storageKey, JSON.stringify(data));
return data;
} catch (error) {
console.error('Error fetching IP info:', error);
return null;
}
}
}