UNPKG

pulse-ai-utils

Version:

Utility functions and helpers for AI-powered applications

278 lines 9.61 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.LocationService = void 0; const area_coordinates_1 = require("../constants/area-coordinates"); const geo_search_1 = require("./geo-search"); const geohash = __importStar(require("ngeohash")); class LocationService { constructor() { this.areaCache = new Map(); } /** * Get location coordinates from area ID */ async getLocationFromArea(areaInput) { // Normalize area name const normalizedArea = this.normalizeAreaName(areaInput); // Check cache if (this.areaCache.has(normalizedArea)) { return this.areaCache.get(normalizedArea); } // Look up in coordinates const coords = area_coordinates_1.AREA_COORDINATES[normalizedArea]; if (coords) { const location = { lat: coords.lat, lng: coords.lng, area_id: normalizedArea }; this.areaCache.set(normalizedArea, location); return location; } // Try variations for (const [areaId, coords] of Object.entries(area_coordinates_1.AREA_COORDINATES)) { if (this.areaNamesMatch(areaInput, areaId)) { const location = { lat: coords.lat, lng: coords.lng, area_id: areaId }; this.areaCache.set(normalizedArea, location); return location; } } return null; } /** * Extract locations from content text and metadata */ async extractLocationsFromContent(content) { const locations = []; const foundAreas = new Map(); // area_id -> first position // Always include primary area first if (content.primary_area) { const primaryLoc = await this.getLocationFromArea(content.primary_area); if (primaryLoc) { locations.push({ ...primaryLoc, area_id: content.primary_area, relevance: 1.0 }); foundAreas.set(content.primary_area, 0); } } // Search for area mentions in text const searchText = `${content.title} ${content.description || ''}`.toLowerCase(); for (const [areaId, coords] of Object.entries(area_coordinates_1.AREA_COORDINATES)) { if (foundAreas.has(areaId)) continue; const areaName = areaId.replace(/-/g, ' '); const variations = this.getAreaVariations(areaId); for (const variation of variations) { const regex = new RegExp(`\\b${variation}\\b`, 'i'); const match = searchText.match(regex); if (match && match.index !== undefined) { foundAreas.set(areaId, match.index); break; } } } // Sort by position in text and add with decreasing relevance const sortedAreas = Array.from(foundAreas.entries()) .filter(([areaId]) => areaId !== content.primary_area) .sort(([, posA], [, posB]) => posA - posB); for (let i = 0; i < sortedAreas.length; i++) { const [areaId] = sortedAreas[i]; const coords = area_coordinates_1.AREA_COORDINATES[areaId]; locations.push({ lat: coords.lat, lng: coords.lng, area_id: areaId, relevance: 0.8 - (i * 0.1) }); } return locations; } /** * Get user location from IP address */ async getUserLocationFromIP(ip) { // In production, this would call an IP geolocation service // For now, return a mock response // Services to consider: ipapi.co, ip-api.com, ipgeolocation.io // Mock implementation if (ip.startsWith('72.') || ip.startsWith('73.')) { // Florida IPs return { lat: 27.9506, lng: -82.4572, city: 'Tampa', confidence: 0.7 }; } // Default to center of US return { lat: 39.8283, lng: -98.5795, city: 'Unknown', confidence: 0.1 }; } /** * Parse browser geolocation position */ parseGeolocationPosition(position) { return { lat: position.coords.latitude, lng: position.coords.longitude, accuracy: position.coords.accuracy }; } /** * Find nearest known area to coordinates */ findNearestKnownArea(lat, lng) { let nearest = { area_id: '', distance: Infinity, location: { lat: 0, lng: 0 } }; for (const [areaId, coords] of Object.entries(area_coordinates_1.AREA_COORDINATES)) { const distance = (0, geo_search_1.calculateDistance)(lat, lng, coords.lat, coords.lng); if (distance < nearest.distance) { nearest = { area_id: areaId, distance, location: { lat: coords.lat, lng: coords.lng, area_id: areaId } }; } } return nearest; } /** * Generate geohash for coordinates */ generateGeohash(lat, lng, precision = 6) { return geohash.encode(lat, lng, precision); } /** * Enrich content with location data */ async enrichContentWithLocation(content) { // Extract locations from content const locations = await this.extractLocationsFromContent(content); // Set primary location let primaryLocation; if (locations.length > 0) { primaryLocation = { lat: locations[0].lat, lng: locations[0].lng }; } else if (content.primary_area) { // Fallback to primary area const loc = await this.getLocationFromArea(content.primary_area); primaryLocation = loc || { lat: 0, lng: 0 }; } else { primaryLocation = { lat: 0, lng: 0 }; } // Generate geohash const geohash = this.generateGeohash(primaryLocation.lat, primaryLocation.lng); // Extract location names for indexing const locationNames = locations.map(loc => loc.area_id).filter(Boolean); return { ...content, locations, primary_location: primaryLocation, geohash, location_names: locationNames }; } /** * Normalize area name for matching */ normalizeAreaName(area) { return area .toLowerCase() .trim() .replace(/\./g, '') .replace(/\s+/g, '-') .replace(/^st-/, 'st-') .replace(/^saint-/, 'st-'); } /** * Check if two area names match */ areaNamesMatch(input, areaId) { const normalized = this.normalizeAreaName(input); return normalized === areaId; } /** * Get variations of area name for matching */ getAreaVariations(areaId) { const baseName = areaId.replace(/-/g, ' '); const variations = [baseName]; // Add variations if (areaId.startsWith('st-')) { variations.push(baseName.replace('st ', 'st. ')); variations.push(baseName.replace('st ', 'saint ')); } if (areaId.includes('-')) { // Handle hyphenated names variations.push(areaId); } // Add "town n country" special case if (areaId === 'town-n-country') { variations.push('town n country'); variations.push('town and country'); } return variations; } /** * Clear cache */ clearCache() { this.areaCache.clear(); } } exports.LocationService = LocationService; //# sourceMappingURL=location-service.js.map