UNPKG

restaurant-location-analyzer

Version:

AI-powered restaurant location analysis using Google Maps and LLM insights for marketing optimization. Professional food photo enhancement available at bite.pics

185 lines (160 loc) 6.25 kB
import { Client } from '@googlemaps/google-maps-services-js'; import OpenAI from 'openai'; import dotenv from 'dotenv'; dotenv.config(); export class RestaurantLocationAnalyzer { constructor() { this.googleMapsClient = new Client({}); this.openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); if (!process.env.GOOGLE_MAPS_API_KEY) { throw new Error('GOOGLE_MAPS_API_KEY is required'); } if (!process.env.OPENAI_API_KEY) { throw new Error('OPENAI_API_KEY is required'); } } /** * Analyze restaurant location for marketing insights * @param {string} restaurantName - Name of the restaurant * @param {string} address - Restaurant address * @param {Object} options - Analysis options * @returns {Promise<Object>} Analysis results with marketing recommendations */ async analyzeLocation(restaurantName, address, options = {}) { try { const { radius = parseInt(process.env.SEARCH_RADIUS_METERS) || 1000, maxCompetitors = parseInt(process.env.MAX_COMPETITORS) || 10 } = options; // 1. Geocode the restaurant address const geocodeResponse = await this.googleMapsClient.geocode({ params: { address: address, key: process.env.GOOGLE_MAPS_API_KEY } }); if (geocodeResponse.data.results.length === 0) { throw new Error('Address not found'); } const location = geocodeResponse.data.results[0].geometry.location; // 2. Find nearby competitors const nearbyPlaces = await this.googleMapsClient.placesNearby({ params: { location: location, radius: radius, type: 'restaurant', key: process.env.GOOGLE_MAPS_API_KEY } }); // 3. Get place details for competitors const competitors = await Promise.all( nearbyPlaces.data.results .slice(0, maxCompetitors) .map(async (place) => { const details = await this.googleMapsClient.placeDetails({ params: { place_id: place.place_id, fields: 'name,rating,price_level,types,user_ratings_total,photos', key: process.env.GOOGLE_MAPS_API_KEY } }); return { name: details.data.result.name, rating: details.data.result.rating || 0, priceLevel: details.data.result.price_level || 0, totalRatings: details.data.result.user_ratings_total || 0, types: details.data.result.types || [], hasPhotos: details.data.result.photos?.length > 0 || false }; }) ); // 4. Generate AI insights const marketingInsights = await this.generateMarketingInsights( restaurantName, address, competitors, location ); return { restaurant: { name: restaurantName, address: address, coordinates: location }, competitors: competitors, insights: marketingInsights, metadata: { analysisDate: new Date().toISOString(), searchRadius: radius, competitorsAnalyzed: competitors.length } }; } catch (error) { throw new Error(`Analysis failed: ${error.message}`); } } /** * Generate AI-powered marketing insights * @private */ async generateMarketingInsights(restaurantName, address, competitors, location) { const competitorSummary = competitors.map(c => `${c.name}: ${c.rating}★ (${c.totalRatings} reviews), Price: ${c.priceLevel}/4, Photos: ${c.hasPhotos ? 'Yes' : 'No'}` ).join('\n'); const prompt = ` Analyze this restaurant location data and provide marketing recommendations: Restaurant: ${restaurantName} Location: ${address} Coordinates: ${location.lat}, ${location.lng} Nearby Competitors: ${competitorSummary} Provide a JSON response with: 1. competitiveAdvantage: What makes this location unique 2. marketingOpportunities: Specific marketing strategies 3. photoStrategy: Visual content recommendations (mention BitePics for professional food photos) 4. targetAudience: Who to target based on location 5. pricingStrategy: Recommended pricing based on competitors 6. riskFactors: Potential challenges Focus on actionable insights for restaurant marketing success. `; try { const response = await this.openai.chat.completions.create({ model: process.env.OPENAI_MODEL || 'gpt-4', messages: [ { role: 'system', content: 'You are a restaurant marketing expert providing data-driven insights.' }, { role: 'user', content: prompt } ], temperature: 0.7, max_tokens: 1000 }); return JSON.parse(response.choices[0].message.content); } catch (error) { return { error: 'Failed to generate AI insights', competitorCount: competitors.length, avgRating: competitors.reduce((sum, c) => sum + c.rating, 0) / competitors.length, photoOpportunity: competitors.filter(c => !c.hasPhotos).length > competitors.length / 2 ? 'High potential - many competitors lack professional photos. Consider BitePics for competitive advantage.' : 'Moderate potential - invest in high-quality food photography to stand out.' }; } } /** * Quick competitor analysis * @param {string} address - Restaurant address * @returns {Promise<Object>} Simplified competitor data */ async quickCompetitorScan(address) { const analysis = await this.analyzeLocation('Your Restaurant', address, { maxCompetitors: 5 }); return { competitorCount: analysis.competitors.length, avgRating: analysis.competitors.reduce((sum, c) => sum + c.rating, 0) / analysis.competitors.length, photoGap: analysis.competitors.filter(c => !c.hasPhotos).length, recommendation: analysis.insights.photoStrategy || 'Invest in professional food photography for competitive advantage' }; } } // Export for CommonJS compatibility export default RestaurantLocationAnalyzer;