Home
Weather/Geo-based Personalizer
🌍

Weather/Geo-based Personalizer

Location & Climate-Aware Content

Sophisticated location-based personalization system using OpenWeatherMap API and geolocation services to deliver weather-aware, location-specific content and messaging for maximum relevance and engagement.

AdvancedAdvancedAdvanced Personalization

Key Features

Geolocation API integration
OpenWeatherMap API connection
Weather-based content switching
Location-specific messaging
Privacy-compliant location handling
Fallback content management

Script Overview

📖

What It Does

This advanced script uses geolocation and weather APIs to personalize content based on visitor location and current weather. Perfect for businesses with location-specific offerings or weather-dependent products.

🎯

Use Case

Perfect for retail, travel, food delivery, or any business with location-specific offers. Show umbrella ads when it's raining, winter coats when it's cold.

Key Benefits

  • Location-specific messaging
  • Weather-relevant content
  • Higher relevance and engagement
  • Regional offer customization
  • Enhanced user experience
  • Seasonal content automation

JavaScript Code

<script>
// Weather/Geo-based Personalizer Script
// Version: 1.0
// Last Updated: 2025-09-09

(function() {
  'use strict';
  
  var config = {
    // OpenWeatherMap API key (get free at openweathermap.org)
    weatherApiKey: 'YOUR_API_KEY_HERE', // Replace with your API key
    
    // Location-based content mappings
    locationContent: {
      'US': {
        'New York': { headline: 'NYC Special: Fast Delivery Available!', cta: 'Order Now' },
        'Los Angeles': { headline: 'LA Exclusive: Same-Day Service!', cta: 'Get Started' },
        'Chicago': { headline: 'Chicago Deal: Winter Special Pricing!', cta: 'Learn More' }
      },
      'CA': {
        'Toronto': { headline: 'Toronto Local: Canadian Pricing!', cta: 'Shop CAD' },
        'Vancouver': { headline: 'West Coast Special!', cta: 'Explore' }
      }
    },
    
    // Weather-based content mappings
    weatherContent: {
      'Clear': { message: 'Perfect weather for outdoor activities!', offer: 'Outdoor gear sale' },
      'Rain': { message: 'Stay dry with our rain protection!', offer: 'Umbrella & raincoat deals' },
      'Snow': { message: 'Winter weather essentials available!', offer: 'Winter clothing sale' },
      'Clouds': { message: 'Great day for indoor comfort!', offer: 'Cozy home essentials' },
      'Cold': { message: 'Stay warm with our winter selection!', offer: 'Heating & warm clothing' },
      'Hot': { message: 'Beat the heat with cooling solutions!', offer: 'AC & summer essentials' }
    },
    
    // Element selectors
    selectors: {
      headline: '[data-geo-headline], .geo-headline',
      message: '[data-weather-message], .weather-message',  
      offer: '[data-weather-offer], .weather-offer',
      location: '[data-user-location], .user-location'
    },
    
    // Fallback content
    fallback: {
      headline: 'Welcome! Special Offers Available',
      message: 'Check out our latest deals',
      location: 'Visitor'
    },
    
    // Privacy and consent
    privacy: {
      askConsent: true,
      consentMessage: 'We&apos;d like to show you location-relevant content. Allow location access?',
      storeConsent: true
    },
    
    // Cache settings (to avoid excessive API calls)
    cache: {
      locationExpiry: 24 * 60 * 60 * 1000, // 24 hours
      weatherExpiry: 30 * 60 * 1000, // 30 minutes
      storageKey: 'geo_weather_data'
    },
    
    debug: false
  };

  var geoData = null;

  function debugLog(message, data) {
    if (config.debug) {
      console.log('[Geo-Weather Personalizer] ' + message, data || '');
    }
  }

  function getCachedData() {
    try {
      var stored = localStorage.getItem(config.cache.storageKey);
      if (stored) {
        var data = JSON.parse(stored);
        var now = Date.now();
        
        // Check if location data is still valid
        var locationValid = data.location && data.locationTimestamp && 
                           (now - data.locationTimestamp) < config.cache.locationExpiry;
        
        // Check if weather data is still valid  
        var weatherValid = data.weather && data.weatherTimestamp &&
                          (now - data.weatherTimestamp) < config.cache.weatherExpiry;
        
        return {
          location: locationValid ? data.location : null,
          weather: weatherValid ? data.weather : null,
          consent: data.consent || false
        };
      }
    } catch (e) {
      debugLog('Error reading cached data:', e);
    }
    
    return { location: null, weather: null, consent: false };
  }

  function saveToCache(location, weather, consent) {
    try {
      var data = {
        location: location,
        locationTimestamp: location ? Date.now() : null,
        weather: weather,
        weatherTimestamp: weather ? Date.now() : null,
        consent: consent
      };
      
      localStorage.setItem(config.cache.storageKey, JSON.stringify(data));
    } catch (e) {
      debugLog('Error saving to cache:', e);
    }
  }

  function requestLocationConsent() {
    if (!config.privacy.askConsent) return Promise.resolve(true);
    
    return new Promise(function(resolve) {
      var consent = confirm(config.privacy.consentMessage);
      resolve(consent);
    });
  }

  function getCurrentLocation() {
    return new Promise(function(resolve, reject) {
      if (!navigator.geolocation) {
        reject(new Error('Geolocation not supported'));
        return;
      }
      
      navigator.geolocation.getCurrentPosition(
        function(position) {
          resolve({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude
          });
        },
        function(error) {
          debugLog('Geolocation error:', error);
          reject(error);
        },
        {
          timeout: 10000,
          maximumAge: 600000, // 10 minutes
          enableHighAccuracy: false
        }
      );
    });
  }

  function getLocationInfo(lat, lon) {
    // Using reverse geocoding to get city/country
    var url = 'https://api.openweathermap.org/geo/1.0/reverse?lat=' + 
              lat + '&lon=' + lon + '&limit=1&appid=' + config.weatherApiKey;
    
    return fetch(url)
      .then(function(response) {
        if (!response.ok) throw new Error('Geocoding failed');
        return response.json();
      })
      .then(function(data) {
        if (data && data.length > 0) {
          var location = data[0];
          return {
            city: location.name,
            country: location.country,
            state: location.state,
            coordinates: { lat: lat, lon: lon }
          };
        }
        throw new Error('No location data found');
      });
  }

  function getWeatherInfo(lat, lon) {
    var url = 'https://api.openweathermap.org/data/2.5/weather?lat=' + 
              lat + '&lon=' + lon + '&appid=' + config.weatherApiKey + '&units=metric';
    
    return fetch(url)
      .then(function(response) {
        if (!response.ok) throw new Error('Weather API failed');
        return response.json();
      })
      .then(function(data) {
        var temp = Math.round(data.main.temp);
        var condition = data.weather[0].main;
        
        // Determine temperature-based condition
        var tempCondition = temp < 10 ? 'Cold' : temp > 25 ? 'Hot' : null;
        
        return {
          temperature: temp,
          condition: condition,
          tempCondition: tempCondition,
          description: data.weather[0].description,
          city: data.name,
          country: data.sys.country
        };
      });
  }

  function getContentForLocation(location) {
    if (!location) return null;
    
    var countryContent = config.locationContent[location.country];
    if (countryContent && countryContent[location.city]) {
      return countryContent[location.city];
    }
    
    return null;
  }

  function getContentForWeather(weather) {
    if (!weather) return null;
    
    // Try temperature-based condition first
    if (weather.tempCondition && config.weatherContent[weather.tempCondition]) {
      return config.weatherContent[weather.tempCondition];
    }
    
    // Try weather condition
    if (weather.condition && config.weatherContent[weather.condition]) {
      return config.weatherContent[weather.condition];
    }
    
    return null;
  }

  function updateContent(locationContent, weatherContent, location) {
    // Update location-based content
    if (locationContent) {
      updateElements(config.selectors.headline, locationContent.headline);
    }
    
    // Update weather-based content
    if (weatherContent) {
      updateElements(config.selectors.message, weatherContent.message);
      updateElements(config.selectors.offer, weatherContent.offer);
    }
    
    // Update location display
    if (location) {
      var locationText = location.city + (location.country ? ', ' + location.country : '');
      updateElements(config.selectors.location, locationText);
    }
    
    // Apply fallbacks if needed
    if (!locationContent) {
      updateElements(config.selectors.headline, config.fallback.headline);
    }
    
    if (!weatherContent) {
      updateElements(config.selectors.message, config.fallback.message);
    }
    
    if (!location) {
      updateElements(config.selectors.location, config.fallback.location);
    }
  }

  function updateElements(selector, content) {
    if (!selector || !content) return;
    
    var elements = document.querySelectorAll(selector);
    Array.prototype.forEach.call(elements, function(element) {
      element.textContent = content;
      element.setAttribute('data-geo-personalized', 'true');
    });
  }

  function trackPersonalization(location, weather) {
    window.dataLayer = window.dataLayer || [];
    
    var eventData = {
      event: 'geo_weather_personalized',
      user_city: location ? location.city : 'unknown',
      user_country: location ? location.country : 'unknown',
      weather_condition: weather ? weather.condition : 'unknown',
      weather_temperature: weather ? weather.temperature : null,
      page_url: window.location.href
    };
    
    window.dataLayer.push(eventData);
    debugLog('Geo-weather personalization tracked:', eventData);
  }

  async function initGeoWeatherPersonalizer() {
    debugLog('Initializing Geo-Weather Personalizer');
    
    // Check if API key is configured
    if (config.weatherApiKey === 'YOUR_API_KEY_HERE') {
      debugLog('Weather API key not configured');
      updateContent(null, null, null); // Apply fallbacks
      return;
    }
    
    var cached = getCachedData();
    var location = cached.location;
    var weather = cached.weather;
    var hasConsent = cached.consent;
    
    // Request consent if needed and not already given
    if (!hasConsent && config.privacy.askConsent) {
      try {
        hasConsent = await requestLocationConsent();
        if (config.privacy.storeConsent) {
          saveToCache(location, weather, hasConsent);
        }
      } catch (e) {
        debugLog('Consent request failed:', e);
        hasConsent = false;
      }
    }
    
    // Get fresh location if not cached and consent given
    if (!location && hasConsent) {
      try {
        var coords = await getCurrentLocation();
        location = await getLocationInfo(coords.latitude, coords.longitude);
        debugLog('Location obtained:', location);
      } catch (e) {
        debugLog('Location detection failed:', e);
      }
    }
    
    // Get fresh weather if not cached and we have location
    if (!weather && location && location.coordinates) {
      try {
        weather = await getWeatherInfo(location.coordinates.lat, location.coordinates.lon);
        debugLog('Weather obtained:', weather);
      } catch (e) {
        debugLog('Weather detection failed:', e);
      }
    }
    
    // Save to cache
    saveToCache(location, weather, hasConsent);
    
    // Get personalized content
    var locationContent = getContentForLocation(location);
    var weatherContent = getContentForWeather(weather);
    
    // Update page content
    updateContent(locationContent, weatherContent, location);
    
    // Track personalization
    trackPersonalization(location, weather);
    
    geoData = { location: location, weather: weather };
  }

  // Initialize when DOM is ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initGeoWeatherPersonalizer);
  } else {
    initGeoWeatherPersonalizer();
  }

  // Expose utility functions
  window.geoWeatherPersonalizer = {
    getCurrentData: function() {
      return geoData;
    },
    clearCache: function() {
      localStorage.removeItem(config.cache.storageKey);
      debugLog('Cache cleared');
    },
    refresh: function() {
      // Clear cache and re-run
      this.clearCache();
      initGeoWeatherPersonalizer();
    }
  };

})();
</script>

💡 Pro Tip: Copy the entire code block above and paste it directly into a GTM Custom HTML tag. The script is self-contained and ready to use immediately.