help@rskworld.in +91 93305 39277
RSK World
  • Home
  • Development
    • Web Development
    • Mobile Apps
    • Software
    • Games
    • Project
  • Technologies
    • Data Science
    • AI Development
    • Cloud Development
    • Blockchain
    • Cyber Security
    • Dev Tools
    • Testing Tools
  • About
  • Contact

Theme Settings

Color Scheme
Display Options
Font Size
100%
Back to Project
RSK World
weather-chatbot
/
utils
RSK World
weather-chatbot
Weather Chatbot - Python + Flask + OpenWeatherMap + OpenAI + Weather Forecast + Weather Alerts + Natural Language Processing
utils
  • __pycache__
  • __init__.py3.9 KB
  • advanced_nlp.py28.4 KB
  • analytics.py22 KB
  • auth.py17.8 KB
  • comparison.py24.2 KB
  • database.py24.6 KB
  • geolocation.py20.1 KB
  • multilang.py22 KB
  • notifications.py34.1 KB
  • rate_limiting.py24.3 KB
  • weather_maps.py29.6 KB
  • weather_utils.py12.9 KB
geolocation.py
utils/geolocation.py
Raw Download
Find: Go to:
#!/usr/bin/env python3
"""
Weather Chatbot Geolocation Module
===================================

Author: RSK World (https://rskworld.in)
Founded by: Molla Samser
Designer & Tester: Rima Khatun
Contact: +91 93305 39277, hello@rskworld.in, support@rskworld.in
Location: Nutanhat, Mongolkote, Purba Burdwan, West Bengal, India, 713147
Year: 2026

Description: Location detection and geolocation services
"""

import requests
import json
from typing import Dict, Optional, Tuple
from urllib.parse import quote

class GeolocationService:
    """Geolocation service for location-based weather services"""
    
    def __init__(self, ipinfo_api_key: str = None, openweather_api_key: str = None):
        self.ipinfo_api_key = ipinfo_api_key
        self.openweather_api_key = openweather_api_key
        self.ipinfo_url = "https://ipinfo.io"
        self.openweather_geo_url = "http://api.openweathermap.org/geo/1.0"
    
    def get_location_by_ip(self, ip_address: str = None) -> Dict:
        """
        Get location information by IP address.
        
        Args:
            ip_address: IP address (if None, uses current IP)
            
        Returns:
            Location information dictionary
        """
        try:
            url = f"{self.ipinfo_url}/{ip_address}" if ip_address else self.ipinfo_url
            params = {}
            
            if self.ipinfo_api_key:
                params['token'] = self.ipinfo_api_key
            
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            
            data = response.json()
            
            location_info = {
                'ip': data.get('ip'),
                'city': data.get('city'),
                'region': data.get('region'),
                'country': data.get('country'),
                'location': data.get('loc'),  # lat,lon
                'postal': data.get('postal'),
                'timezone': data.get('timezone'),
                'org': data.get('org'),  # ISP/organization
                'source': 'ipinfo'
            }
            
            # Parse coordinates
            if data.get('loc'):
                try:
                    lat, lon = data['loc'].split(',')
                    location_info['latitude'] = float(lat)
                    location_info['longitude'] = float(lon)
                except:
                    location_info['latitude'] = None
                    location_info['longitude'] = None
            
            return location_info
            
        except requests.exceptions.RequestException as e:
            return {'error': f'Failed to get location by IP: {str(e)}'}
        except Exception as e:
            return {'error': f'Error processing location data: {str(e)}'}
    
    def get_coordinates_by_city(self, city: str, country: str = None, state: str = None) -> Dict:
        """
        Get coordinates for a city using OpenWeatherMap Geocoding API.
        
        Args:
            city: City name
            country: Country code (optional)
            state: State code (optional, for US cities)
            
        Returns:
            Coordinates and location information
        """
        try:
            # Build query string
            query_parts = [city]
            if state:
                query_parts.append(state)
            if country:
                query_parts.append(country)
            
            query = ','.join(query_parts)
            
            url = f"{self.openweather_geo_url}/direct"
            params = {
                'q': query,
                'limit': 5,  # Get multiple results for disambiguation
                'appid': self.openweather_api_key
            }
            
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            
            data = response.json()
            
            if not data:
                return {'error': 'City not found'}
            
            # Return the first (most relevant) result
            result = data[0]
            
            return {
                'city': result.get('name'),
                'country': result.get('country'),
                'state': result.get('state'),
                'latitude': result.get('lat'),
                'longitude': result.get('lon'),
                'source': 'openweathermap',
                'alternatives': data[1:] if len(data) > 1 else []
            }
            
        except requests.exceptions.RequestException as e:
            return {'error': f'Failed to get coordinates: {str(e)}'}
        except Exception as e:
            return {'error': f'Error processing coordinates: {str(e)}'}
    
    def get_city_by_coordinates(self, lat: float, lon: float, limit: int = 1) -> Dict:
        """
        Get city information by coordinates using reverse geocoding.
        
        Args:
            lat: Latitude
            lon: Longitude
            limit: Number of results to return
            
        Returns:
            City information
        """
        try:
            url = f"{self.openweather_geo_url}/reverse"
            params = {
                'lat': lat,
                'lon': lon,
                'limit': limit,
                'appid': self.openweather_api_key
            }
            
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            
            data = response.json()
            
            if not data:
                return {'error': 'No location found for coordinates'}
            
            # Return the first result
            result = data[0]
            
            return {
                'city': result.get('name'),
                'country': result.get('country'),
                'state': result.get('state'),
                'latitude': result.get('lat'),
                'longitude': result.get('lon'),
                'source': 'openweathermap_reverse',
                'alternatives': data[1:] if len(data) > 1 else []
            }
            
        except requests.exceptions.RequestException as e:
            return {'error': f'Failed to get city by coordinates: {str(e)}'}
        except Exception as e:
            return {'error': f'Error processing reverse geocoding: {str(e)}'}
    
    def search_locations(self, query: str, limit: int = 5) -> Dict:
        """
        Search for locations by name.
        
        Args:
            query: Search query
            limit: Maximum number of results
            
        Returns:
            List of matching locations
        """
        try:
            url = f"{self.openweather_geo_url}/direct"
            params = {
                'q': query,
                'limit': limit,
                'appid': self.openweather_api_key
            }
            
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            
            data = response.json()
            
            if not data:
                return {'results': [], 'message': 'No locations found'}
            
            results = []
            for result in data:
                location = {
                    'city': result.get('name'),
                    'country': result.get('country'),
                    'state': result.get('state'),
                    'latitude': result.get('lat'),
                    'longitude': result.get('lon'),
                    'display_name': self._format_location_name(result)
                }
                results.append(location)
            
            return {
                'results': results,
                'count': len(results),
                'source': 'openweathermap'
            }
            
        except requests.exceptions.RequestException as e:
            return {'error': f'Failed to search locations: {str(e)}'}
        except Exception as e:
            return {'error': f'Error processing search: {str(e)}'}
    
    def _format_location_name(self, location_data: Dict) -> str:
        """Format location name for display"""
        parts = [location_data.get('name', '')]
        
        if location_data.get('state'):
            parts.append(location_data['state'])
        
        if location_data.get('country'):
            parts.append(location_data['country'])
        
        return ', '.join(parts)
    
    def calculate_distance(self, lat1: float, lon1: float, lat2: float, lon2: float) -> float:
        """
        Calculate distance between two coordinates using Haversine formula.
        
        Args:
            lat1, lon1: First point coordinates
            lat2, lon2: Second point coordinates
            
        Returns:
            Distance in kilometers
        """
        import math
        
        # Convert to radians
        lat1_rad = math.radians(lat1)
        lon1_rad = math.radians(lon1)
        lat2_rad = math.radians(lat2)
        lon2_rad = math.radians(lon2)
        
        # Haversine formula
        dlat = lat2_rad - lat1_rad
        dlon = lon2_rad - lon1_rad
        
        a = (math.sin(dlat/2)**2 + 
             math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon/2)**2)
        c = 2 * math.asin(math.sqrt(a))
        
        # Earth's radius in kilometers
        r = 6371
        
        return c * r
    
    def find_nearby_cities(self, lat: float, lon: float, radius_km: float = 50, limit: int = 10) -> Dict:
        """
        Find cities near a given location.
        
        Args:
            lat, lon: Center coordinates
            radius_km: Search radius in kilometers
            limit: Maximum number of results
            
        Returns:
            List of nearby cities with distances
        """
        try:
            # Get current city first
            current_city = self.get_city_by_coordinates(lat, lon, limit=1)
            
            if 'error' in current_city:
                return current_city
            
            # Search for nearby locations (this is a simplified approach)
            # In practice, you might want to use a dedicated geospatial database
            search_query = f"{current_city['city']}"
            
            search_results = self.search_locations(search_query, limit=limit * 2)
            
            if 'error' in search_results:
                return search_results
            
            # Calculate distances and filter by radius
            nearby_cities = []
            
            for location in search_results['results']:
                # Skip the exact same location
                if (abs(location['latitude'] - lat) < 0.01 and 
                    abs(location['longitude'] - lon) < 0.01):
                    continue
                
                distance = self.calculate_distance(
                    lat, lon, 
                    location['latitude'], location['longitude']
                )
                
                if distance <= radius_km:
                    location['distance_km'] = round(distance, 1)
                    nearby_cities.append(location)
            
            # Sort by distance and limit results
            nearby_cities.sort(key=lambda x: x['distance_km'])
            nearby_cities = nearby_cities[:limit]
            
            return {
                'center_location': current_city,
                'nearby_cities': nearby_cities,
                'radius_km': radius_km,
                'count': len(nearby_cities)
            }
            
        except Exception as e:
            return {'error': f'Error finding nearby cities: {str(e)}'}
    
    def get_timezone_info(self, lat: float, lon: float) -> Dict:
        """
        Get timezone information for coordinates.
        
        Args:
            lat, lon: Coordinates
            
        Returns:
            Timezone information
        """
        try:
            if not self.openweather_api_key:
                return {'error': 'OpenWeatherMap API key required'}
            
            url = f"http://api.openweathermap.org/data/2.5/weather"
            params = {
                'lat': lat,
                'lon': lon,
                'appid': self.openweather_api_key
            }
            
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            
            data = response.json()
            
            timezone_offset = data.get('timezone', 0)  # Offset in seconds
            
            # Convert to hours
            timezone_hours = timezone_offset / 3600
            
            # Determine timezone name (simplified)
            timezone_name = self._get_timezone_name(timezone_hours)
            
            return {
                'timezone_offset_seconds': timezone_offset,
                'timezone_offset_hours': timezone_hours,
                'timezone_name': timezone_name,
                'utc_offset': f"{'+' if timezone_hours >= 0 else ''}{timezone_hours}:00",
                'source': 'openweathermap'
            }
            
        except requests.exceptions.RequestException as e:
            return {'error': f'Failed to get timezone info: {str(e)}'}
        except Exception as e:
            return {'error': f'Error processing timezone: {str(e)}'}
    
    def _get_timezone_name(self, offset_hours: float) -> str:
        """Get timezone name from UTC offset (simplified)"""
        timezone_map = {
            -12: 'UTC-12 (Baker Island Time)',
            -11: 'UTC-11 (Niue Time)',
            -10: 'UTC-10 (Hawaii-Aleutian Standard Time)',
            -9: 'UTC-9 (Alaska Standard Time)',
            -8: 'UTC-8 (Pacific Standard Time)',
            -7: 'UTC-7 (Mountain Standard Time)',
            -6: 'UTC-6 (Central Standard Time)',
            -5: 'UTC-5 (Eastern Standard Time)',
            -4: 'UTC-4 (Atlantic Standard Time)',
            -3: 'UTC-3 (Brazil Time)',
            -2: 'UTC-2 (Fernando de Noronha Time)',
            -1: 'UTC-1 (Azores Time)',
            0: 'UTC (Greenwich Mean Time)',
            1: 'UTC+1 (Central European Time)',
            2: 'UTC+2 (Eastern European Time)',
            3: 'UTC+3 (Moscow Time)',
            4: 'UTC+4 (Gulf Standard Time)',
            5: 'UTC+5 (Pakistan Standard Time)',
            5.5: 'UTC+5:30 (India Standard Time)',
            6: 'UTC+6 (Bangladesh Standard Time)',
            7: 'UTC+7 (Indochina Time)',
            8: 'UTC+8 (China Standard Time)',
            9: 'UTC+9 (Japan Standard Time)',
            10: 'UTC+10 (Australian Eastern Time)',
            11: 'UTC+11 (Solomon Islands Time)',
            12: 'UTC+12 (New Zealand Time)',
        }
        
        # Find closest match
        closest_offset = min(timezone_map.keys(), key=lambda x: abs(x - offset_hours))
        
        if abs(closest_offset - offset_hours) < 0.5:
            return timezone_map[closest_offset]
        else:
            return f"UTC{offset_hours:+.1f}"
    
    def validate_coordinates(self, lat: float, lon: float) -> Dict:
        """
        Validate latitude and longitude coordinates.
        
        Args:
            lat: Latitude
            lon: Longitude
            
        Returns:
            Validation result
        """
        try:
            # Check ranges
            if not (-90 <= lat <= 90):
                return {'valid': False, 'error': 'Latitude must be between -90 and 90'}
            
            if not (-180 <= lon <= 180):
                return {'valid': False, 'error': 'Longitude must be between -180 and 180'}
            
            # Check data types
            if not isinstance(lat, (int, float)) or not isinstance(lon, (int, float)):
                return {'valid': False, 'error': 'Coordinates must be numeric'}
            
            # Check for NaN or infinity
            import math
            if math.isnan(lat) or math.isnan(lon) or math.isinf(lat) or math.isinf(lon):
                return {'valid': False, 'error': 'Invalid coordinate values'}
            
            return {
                'valid': True,
                'latitude': lat,
                'longitude': lon,
                'formatted': f"{lat:.6f}, {lon:.6f}"
            }
            
        except Exception as e:
            return {'valid': False, 'error': f'Error validating coordinates: {str(e)}'}
    
    def get_location_summary(self, ip_address: str = None) -> Dict:
        """
        Get comprehensive location summary.
        
        Args:
            ip_address: IP address (optional)
            
        Returns:
            Complete location information
        """
        try:
            # Get IP-based location
            ip_location = self.get_location_by_ip(ip_address)
            
            if 'error' in ip_location:
                return ip_location
            
            summary = {
                'ip_info': ip_location,
                'coordinates': {
                    'latitude': ip_location.get('latitude'),
                    'longitude': ip_location.get('longitude')
                },
                'address': {
                    'city': ip_location.get('city'),
                    'region': ip_location.get('region'),
                    'country': ip_location.get('country'),
                    'postal': ip_location.get('postal')
                },
                'network': {
                    'ip': ip_location.get('ip'),
                    'isp': ip_location.get('org'),
                    'timezone': ip_location.get('timezone')
                }
            }
            
            # Add timezone info if coordinates are available
            if ip_location.get('latitude') and ip_location.get('longitude'):
                timezone_info = self.get_timezone_info(
                    ip_location['latitude'], 
                    ip_location['longitude']
                )
                summary['timezone'] = timezone_info
            
            # Find nearby cities if coordinates are available
            if ip_location.get('latitude') and ip_location.get('longitude'):
                nearby = self.find_nearby_cities(
                    ip_location['latitude'], 
                    ip_location['longitude'], 
                    radius_km=25, 
                    limit=5
                )
                summary['nearby_cities'] = nearby
            
            return summary
            
        except Exception as e:
            return {'error': f'Error generating location summary: {str(e)}'}

# Utility functions for location data
def format_coordinates(lat: float, lon: float, format_type: str = 'decimal') -> str:
    """Format coordinates in different formats"""
    if format_type == 'decimal':
        return f"{lat:.6f}, {lon:.6f}"
    elif format_type == 'dms':
        return f"{_decimal_to_dms(lat, 'latitude')}, {_decimal_to_dms(lon, 'longitude')}"
    else:
        return f"{lat:.6f}, {lon:.6f}"

def _decimal_to_dms(decimal: float, coordinate_type: str) -> str:
    """Convert decimal coordinates to DMS format"""
    import math
    
    absolute = abs(decimal)
    degrees = int(absolute)
    minutes_float = (absolute - degrees) * 60
    minutes = int(minutes_float)
    seconds = (minutes_float - minutes) * 60
    
    direction = ''
    if coordinate_type == 'latitude':
        direction = 'N' if decimal >= 0 else 'S'
    else:  # longitude
        direction = 'E' if decimal >= 0 else 'W'
    
    return f"{degrees}°{minutes}'{seconds:.1f}\"{direction}"

def parse_coordinates(coord_string: str) -> Optional[Tuple[float, float]]:
    """Parse coordinate string and return (lat, lon) tuple"""
    try:
        # Remove whitespace and split
        parts = coord_string.strip().replace(' ', '').split(',')
        
        if len(parts) != 2:
            return None
        
        lat = float(parts[0])
        lon = float(parts[1])
        
        return (lat, lon)
        
    except:
        return None
564 lines•20.1 KB
python

About RSK World

Founded by Molla Samser, with Designer & Tester Rima Khatun, RSK World is your one-stop destination for free programming resources, source code, and development tools.

Founder: Molla Samser
Designer & Tester: Rima Khatun

Development

  • Game Development
  • Web Development
  • Mobile Development
  • AI Development
  • Development Tools

Legal

  • Terms & Conditions
  • Privacy Policy
  • Disclaimer

Contact Info

Nutanhat, Mongolkote
Purba Burdwan, West Bengal
India, 713147

+91 93305 39277

hello@rskworld.in
support@rskworld.in

© 2026 RSK World. All rights reserved.

Content used for educational purposes only. View Disclaimer