158 lines
5.5 KiB
TypeScript
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;
|
|
}
|
|
}
|
|
}
|