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
JavaScript
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;