Skip to main content

Production-ready, offline geo-intelligence library for resolving latitude/longitude to country, ISO codes, continent, and timezone information

Project description

geo-intel-offline

Python Version License Development Status

Production-ready, offline geo-intelligence library for resolving latitude/longitude coordinates to country, ISO codes, continent, timezone, and confidence scores. No API keys, no network requests, 100% deterministic.

๐Ÿ“‹ Table of Contents

  1. Why This Library Exists
  2. Features
  3. Installation
  4. Quick Start
  5. API Reference
  6. Examples
  7. Use Cases
  8. Advanced Usage
  9. Performance & Accuracy
  10. Architecture
  11. Troubleshooting
  12. Contributing
  13. Additional Resources

๐ŸŒŸ Why This Library Exists

Every developer working with geolocation has faced the same frustration: you need to know what country a set of coordinates belongs to, but all the solutions either cost money, require API keys, need constant internet connectivity, or have restrictive rate limits. What if you're building an offline application? What if you're processing millions of records and API costs become prohibitive? What if you need deterministic results without external dependencies?

We built geo-intel-offline to solve these real-world problems.

This library was born from the need for a reliable, fast, and completely free solution that works everywhereโ€”from edge devices in remote locations to high-throughput data processing pipelines. No subscriptions, no rate limits, no vendor lock-in. Just pure Python that does one thing exceptionally well: tell you where in the world a coordinate belongs.

Whether you're building a mobile app that works offline, processing billions of GPS logs, enriching datasets without external APIs, or creating applications for regions with unreliable internetโ€”this library empowers you to add geo-intelligence to your projects without compromise.

โœจ Features

Core Features

  • ๐Ÿš€ Fast: < 1ms per lookup, < 15MB memory footprint
  • ๐Ÿ“ฆ Offline: Zero network dependencies, works completely offline
  • ๐ŸŽฏ Accurate: 100% accuracy across 258 countries
  • ๐Ÿ”’ Deterministic: Same input always produces same output
  • ๐Ÿ—œ๏ธ Optimized: 66% size reduction with automatic compression
  • ๐ŸŒ Comprehensive: Supports all countries, continents, and territories
  • ๐ŸŽจ Clean API: Unified function for forward and reverse geocoding
  • ๐Ÿ”ง No Dependencies: Pure Python, no native extensions
  • ๐Ÿ’ฐ Free Forever: No API costs, no rate limits, no hidden fees

New Features (v1.5.0+)

  • ๐Ÿ“ Distance Calculation: Calculate distances between any two locations (coordinates, countries, continents) with automatic unit detection (km/miles)
  • ๐ŸŽฏ Geo-fencing: Monitor location proximity with state tracking (OUTSIDE, APPROACHING, INSIDE, LEAVING) and configurable alerts
  • ๐ŸŽฒ Random Coordinates: Generate random coordinates within countries, continents, or circular areas with point-in-polygon validation
  • ๐Ÿ”„ Multiple Algorithms: Haversine, Vincenty, and Spherical Law of Cosines for distance calculations
  • ๐ŸŒ Smart Unit Detection: Automatically detects km/miles based on country preferences (US, GB, LR, MM use miles)

๐Ÿ“ฆ Installation

From PyPI (Recommended)

pip install geo-intel-offline

From uv

uv pip install geo-intel-offline

From Source

git clone https://github.com/RRJena/geo-intel-offline.git
cd geo-intel-offline
pip install .

๐Ÿš€ Quick Start

Basic Usage

The resolve() function automatically detects forward or reverse geocoding based on arguments:

Forward Geocoding (Coordinates โ†’ Country):

from geo_intel_offline import resolve

result = resolve(40.7128, -74.0060)  # New York City

print(result.country)      # "United States of America"
print(result.iso2)         # "US"
print(result.iso3)         # "USA"
print(result.continent)    # "North America"
print(result.timezone)     # "America/New_York"
print(result.confidence)   # 0.98

Reverse Geocoding (Country โ†’ Coordinates):

from geo_intel_offline import resolve

# Just pass country name or ISO code as a string
result = resolve("United States")
print(result.latitude)     # Country centroid latitude
print(result.longitude)     # Country centroid longitude
print(result.iso2)         # "US"

# Works with ISO codes
result = resolve("US")   # ISO2 code
result = resolve("USA")  # ISO3 code

Step-by-Step Guide

Step 1: Install the Package

pip install geo-intel-offline

Step 2: Import and Use

from geo_intel_offline import resolve

# Forward geocoding: Resolve coordinates to country
result = resolve(51.5074, -0.1278)  # London, UK

# Access results as attributes
print(f"Country: {result.country}")
print(f"ISO2 Code: {result.iso2}")
print(f"ISO3 Code: {result.iso3}")
print(f"Continent: {result.continent}")
print(f"Timezone: {result.timezone}")
print(f"Confidence: {result.confidence:.2f}")

# Reverse geocoding: Resolve country to coordinates
result = resolve("United Kingdom")
print(f"UK centroid: ({result.latitude}, {result.longitude})")
print(f"ISO2: {result.iso2}")

Step 3: Handle Edge Cases

from geo_intel_offline import resolve

# Ocean locations (no country)
result = resolve(0.0, 0.0)  # Gulf of Guinea (ocean)
if result.country is None:
    print("No country found (likely ocean)")
    print(f"Confidence: {result.confidence}")  # Will be 0.0

# Border regions (may have lower confidence)
result = resolve(49.0, 8.2)  # Near France-Germany border
if result.confidence < 0.7:
    print(f"Low confidence: {result.confidence:.2f} (near border)")

๐Ÿ“š API Reference

resolve(*args, data_dir=None, countries=None, continents=None, exclude_countries=None)

Unified function for both forward and reverse geocoding. Automatically detects mode based on arguments.

Forward Geocoding (Coordinates โ†’ Country):

  • Pass two numeric arguments: resolve(lat, lon)
  • Example: resolve(40.7128, -74.0060)

Reverse Geocoding (Country โ†’ Coordinates):

  • Pass one string argument: resolve("Country Name") or resolve("ISO Code")
  • Example: resolve("United States") or resolve("US")

Parameters:

Positional Arguments:

  • Forward geocoding: (lat: float, lon: float) - Two numeric values
  • Reverse geocoding: (country: str) - One string (country name or ISO code)

Keyword Arguments:

  • data_dir (str, optional): Custom data directory path
  • countries (list[str], optional): List of ISO2 codes to load (modular format only, forward geocoding)
  • continents (list[str], optional): List of continent names to load (modular format only, forward geocoding)
  • exclude_countries (list[str], optional): List of ISO2 codes to exclude (modular format only, forward geocoding)

Returns:

  • Forward Geocoding: GeoIntelResult object with:

    • country (str | None): Country name
    • iso2 (str | None): ISO 3166-1 alpha-2 code
    • iso3 (str | None): ISO 3166-1 alpha-3 code
    • continent (str | None): Continent name
    • timezone (str | None): IANA timezone identifier
    • confidence (float): Confidence score (0.0 to 1.0)
  • Reverse Geocoding: ReverseGeoIntelResult object with:

    • latitude (float | None): Country centroid latitude
    • longitude (float | None): Country centroid longitude
    • country (str | None): Country name
    • iso2 (str | None): ISO 3166-1 alpha-2 code
    • iso3 (str | None): ISO 3166-1 alpha-3 code
    • continent (str | None): Continent name
    • timezone (str | None): IANA timezone identifier
    • confidence (float): Always 1.0 for exact country match

Methods:

  • to_dict(): Convert result to dictionary

Raises:

  • ValueError: If parameters are invalid or missing
  • FileNotFoundError: If data files are missing

Examples:

# Forward geocoding (coordinates โ†’ country)
result = resolve(40.7128, -74.0060)  # New York
print(result.country)  # "United States of America"
print(result.iso2)    # "US"

# Reverse geocoding (country โ†’ coordinates)
result = resolve("United States")
print(result.latitude, result.longitude)  # Country centroid coordinates
print(result.iso2)  # "US"

# Reverse geocoding with ISO codes
result = resolve("US")   # ISO2 code
result = resolve("USA")  # ISO3 code
result = resolve("JPN") # ISO3 code

# Forward geocoding with filters
result = resolve(40.7128, -74.0060, countries=["US", "CA"])
result = resolve(40.7128, -74.0060, continents=["North America"])

resolve_by_country(country_input, data_dir=None) โš ๏ธ Deprecated

Deprecated: Use resolve("Country Name") instead for consistency.

This function is kept for backward compatibility but the unified resolve() function is recommended.

Parameters:

  • country_input (str): Country name (e.g., "United States", "Japan") or ISO code (e.g., "US", "USA", "JP", "JPN")
  • data_dir (str, optional): Custom data directory path

Returns:

ReverseGeoIntelResult object (same as resolve("Country Name"))

Example:

# Old way (still works)
from geo_intel_offline import resolve_by_country
result = resolve_by_country("United States")

# New unified way (recommended)
from geo_intel_offline import resolve
result = resolve("United States")  # Just pass country name as string

Result Objects

GeoIntelResult

Result object returned by forward geocoding resolve(lat, lon).

Properties:

result.country      # Country name (str | None)
result.iso2         # ISO2 code (str | None)
result.iso3         # ISO3 code (str | None)
result.continent    # Continent name (str | None)
result.timezone     # Timezone (str | None)
result.confidence   # Confidence score (float, 0.0-1.0)

Methods:

result.to_dict()    # Convert to dictionary

ReverseGeoIntelResult

Result object returned by reverse geocoding resolve("Country Name").

Properties:

result.latitude     # Centroid latitude (float | None)
result.longitude    # Centroid longitude (float | None)
result.country      # Country name (str | None)
result.iso2         # ISO2 code (str | None)
result.iso3         # ISO3 code (str | None)
result.continent    # Continent name (str | None)
result.timezone     # Timezone (str | None)
result.confidence   # Confidence score (float, always 1.0)

Methods:

result.to_dict()    # Convert to dictionary

๐Ÿ“– Examples

Example 1: Resolve Multiple Locations

from geo_intel_offline import resolve

locations = [
    (40.7128, -74.0060, "New York"),
    (51.5074, -0.1278, "London"),
    (35.6762, 139.6503, "Tokyo"),
    (-33.8688, 151.2093, "Sydney"),
    (55.7558, 37.6173, "Moscow"),
]

for lat, lon, name in locations:
    result = resolve(lat, lon)
    print(f"{name}: {result.country} ({result.iso2}) - Confidence: {result.confidence:.2f}")

Output:

New York: United States of America (US) - Confidence: 0.98
London: United Kingdom (GB) - Confidence: 0.93
Tokyo: Japan (JP) - Confidence: 0.93
Sydney: Australia (AU) - Confidence: 0.80
Moscow: Russia (RU) - Confidence: 0.93

Example 2: Batch Processing

from geo_intel_offline import resolve
import time

coordinates = [
    (40.7128, -74.0060),
    (51.5074, -0.1278),
    (35.6762, 139.6503),
    # ... more coordinates
]

start = time.perf_counter()
results = [resolve(lat, lon) for lat, lon in coordinates]
end = time.perf_counter()

print(f"Processed {len(coordinates)} coordinates in {(end - start)*1000:.2f}ms")
print(f"Average: {(end - start)*1000/len(coordinates):.3f}ms per lookup")

Example 3: Dictionary Access

from geo_intel_offline import resolve

result = resolve(37.7749, -122.4194)  # San Francisco

# Access as dictionary
result_dict = result.to_dict()
print(result_dict)
# {
#     'country': 'United States of America',
#     'iso2': 'US',
#     'iso3': 'USA',
#     'continent': 'North America',
#     'timezone': 'America/Los_Angeles',
#     'confidence': 0.95
# }

# Or access as attributes
print(result.country)  # "United States of America"
print(result.iso2)     # "US"

Example 4: Filter by Confidence

from geo_intel_offline import resolve

def resolve_with_threshold(lat, lon, min_confidence=0.7):
    """Resolve coordinates with confidence threshold."""
    result = resolve(lat, lon)
    if result.confidence < min_confidence:
        return None, f"Low confidence: {result.confidence:.2f}"
    return result, None

result, error = resolve_with_threshold(40.7128, -74.0060, min_confidence=0.9)
if result:
    print(f"High confidence result: {result.country}")
else:
    print(f"Rejected: {error}")

Example 5: Error Handling

from geo_intel_offline import resolve

def safe_resolve(lat, lon):
    """Safely resolve coordinates with error handling."""
    try:
        result = resolve(lat, lon)
        if result.country is None:
            return {"error": "No country found", "confidence": result.confidence}
        return {
            "country": result.country,
            "iso2": result.iso2,
            "iso3": result.iso3,
            "continent": result.continent,
            "timezone": result.timezone,
            "confidence": result.confidence,
        }
    except ValueError as e:
        return {"error": f"Invalid coordinates: {e}"}
    except FileNotFoundError as e:
        return {"error": f"Data files not found: {e}"}

# Usage
result = safe_resolve(40.7128, -74.0060)
print(result)

Example 6: Reverse Geocoding

from geo_intel_offline import resolve

# Get country centroid coordinates
result = resolve("United States")
print(f"US centroid: ({result.latitude}, {result.longitude})")

# Works with ISO codes
us_coords = resolve("US")      # ISO2 code
jp_coords = resolve("JPN")     # ISO3 code
uk_coords = resolve("United Kingdom")  # Country name

# The function automatically detects reverse geocoding from a single string argument

Example 7: Distance Calculation

from geo_intel_offline import calculate_distance

# Distance between coordinates (auto-detects unit based on countries)
result = calculate_distance([40.7128, -74.0060], [34.0522, -118.2437])
print(f"{result.distance:.2f} {result.unit}")  # "2448.50 mile" (US locations default to miles)

# Distance between countries
result = calculate_distance("United States", "Canada")
print(f"{result.distance:.2f} {result.unit}")  # Auto-detects miles for US

# Force unit
result = calculate_distance("US", "CA", unit='km')
print(f"{result.distance:.2f} {result.unit}")  # "2271.31 km"

# Force calculation method
result = calculate_distance([40.7128, -74.0060], [34.0522, -118.2437], method='vincenty')
print(f"Method: {result.method}")  # "vincenty"

# Distance between continents
result = calculate_distance("North America", "Europe")
print(f"{result.distance:.2f} {result.unit}")

Example 8: Geo-fencing

from geo_intel_offline import check_geofence, GeofenceMonitor, GeofenceConfig

# Stateless check (one-off)
result = check_geofence(
    current_location=(40.7128, -74.0060),
    destination=(40.7130, -74.0060),
    radius=1000,  # 1000 meters
    radius_unit='m'
)
print(f"Inside: {result.is_inside}, Distance: {result.distance:.2f} {result.unit}")
print(f"State: {result.state}")  # OUTSIDE, APPROACHING, INSIDE, or LEAVING

# Stateful monitoring with alerts
config = GeofenceConfig(
    radius=1000,
    radius_unit='m',
    approaching_threshold_percent=10.0,  # Alert when 10% closer
    leaving_threshold_percent=10.0       # Alert when 10% farther
)
monitor = GeofenceMonitor(config)

# Check location multiple times to track movement
result1 = monitor.check((40.7128, -74.0060), (40.7130, -74.0060))
print(f"State: {result1.state}, Alerts: {len(result1.alerts)}")

# Move closer
result2 = monitor.check((40.7129, -74.0060), (40.7130, -74.0060))
for alert in result2.alerts:
    print(f"Alert: {alert.alert_type} - {alert.distance:.2f} {alert.unit}")

Example 9: Random Coordinates

from geo_intel_offline import (
    generate_random_coordinates_by_region,
    generate_random_coordinates_by_area
)

# Generate random coordinates within a country
result = generate_random_coordinates_by_region("United States", count=10, seed=42)
print(f"Generated {result.total_generated} coordinates in {result.region}")
for lat, lon in result.coordinates:
    print(f"  ({lat:.4f}, {lon:.4f})")

# Generate random coordinates within a continent
result = generate_random_coordinates_by_region("Europe", count=5, seed=42)

# Generate random coordinates within a circular area
result = generate_random_coordinates_by_area(
    center=(40.7128, -74.0060),  # NYC
    radius=10,                    # 10 km
    count=5,
    radius_unit='km',
    seed=42
)
print(f"Generated {result.total_generated} coordinates within {result.radius} {result.radius_unit}")

# All generated coordinates are validated (point-in-polygon for regions)

Example 10: Integration - All Features Together

from geo_intel_offline import (
    resolve,
    calculate_distance,
    check_geofence,
    generate_random_coordinates_by_region
)

# 1. Generate random coordinates in a country
coords = generate_random_coordinates_by_region("United States", count=3, seed=42)

# 2. Resolve each coordinate to verify it's in the country
for lat, lon in coords.coordinates:
    result = resolve(lat, lon)
    print(f"Coordinate ({lat:.4f}, {lon:.4f}) โ†’ {result.country} ({result.iso2})")

# 3. Calculate distance between generated coordinates
if len(coords.coordinates) >= 2:
    dist = calculate_distance(coords.coordinates[0], coords.coordinates[1])
    print(f"Distance: {dist.distance:.2f} {dist.unit}")

# 4. Check if coordinates are within geofence
if len(coords.coordinates) >= 2:
    geofence = check_geofence(
        coords.coordinates[0],
        coords.coordinates[1],
        radius=1000,
        radius_unit='km'
    )
    print(f"Geofence check: {geofence.is_inside}, State: {geofence.state}")

๐ŸŽฏ Use Cases

1. Mobile Applications

Offline-first apps that need to identify user location even without internet connectivity. Perfect for travel apps, fitness trackers, or field data collection tools that work in remote areas.

# Works offline - no internet needed!
from geo_intel_offline import resolve, check_geofence

def identify_user_country(lat, lon):
    result = resolve(lat, lon)
    return result.country  # Works even in airplane mode

# Geo-fencing for location-based notifications
def check_arrival(user_location, destination, radius_km=1):
    result = check_geofence(user_location, destination, radius_km * 1000, 'm')
    if result.is_inside:
        send_notification("You've arrived!")

2. Data Processing & Analytics

Batch processing of GPS logs, location data, or transaction records. Process millions of coordinates without API rate limits or costs.

# Process millions of records - no rate limits!
import pandas as pd
from geo_intel_offline import resolve

df = pd.read_csv('location_data.csv')
df['country'] = df.apply(
    lambda row: resolve(row['lat'], row['lon']).country,
    axis=1
)

3. IoT & Edge Devices

Edge computing applications where devices need geo-intelligence without cloud connectivity. Perfect for sensors, trackers, or embedded systems.

# Runs on Raspberry Pi, microcontrollers, edge devices
# No cloud dependency, minimal resources
result = resolve(sensor_lat, sensor_lon)
if result.country != 'US':
    trigger_alert()

4. API Alternatives & Rate Limit Avoidance

Replace expensive APIs or bypass rate limits. Perfect for applications that need high throughput or want to reduce infrastructure costs.

# Instead of: external_api.geocode(lat, lon)  # $0.005 per request
# Use: resolve(lat, lon)  # FREE, unlimited, instant

5. Geographic Data Enrichment

Enrich datasets with country information for analysis, visualization, or machine learning. No need to maintain external API connections or handle failures.

# Enrich logs, events, transactions with country data
events = load_events_from_database()
for event in events:
    event['country'] = resolve(event['lat'], event['lon']).iso2
    save_event(event)

6. Location-Based Features

Add geo-context to your applications: content localization, compliance checks, regional restrictions, or timezone-aware scheduling.

# Content localization based on location
result = resolve(user_lat, user_lon)
if result.continent == 'Europe':
    show_gdpr_banner()
elif result.country == 'US':
    show_us_specific_content()

7. Development & Testing

Local development and testing without needing API keys or internet connectivity. Great for CI/CD pipelines and automated testing.

# Test with real data - no mocks needed
def test_geocoding():
    result = resolve(40.7128, -74.0060)
    assert result.country == 'United States of America'
    assert result.iso2 == 'US'

8. Research & Academic Projects

Academic research that requires reproducible results without external API dependencies or costs that might limit research scope.

# Reproducible research - same results every time
# No API costs to worry about in grant proposals
results = [resolve(lat, lon) for lat, lon in research_coordinates]

# Generate test datasets with random coordinates
from geo_intel_offline import generate_random_coordinates_by_region
test_coords = generate_random_coordinates_by_region("United States", count=1000, seed=42)

9. Distance-Based Analytics & Routing

Calculate distances between locations for analytics, routing optimization, or proximity analysis.

from geo_intel_offline import calculate_distance

# Calculate distances between multiple locations
locations = [
    ("New York", [40.7128, -74.0060]),
    ("Los Angeles", [34.0522, -118.2437]),
    ("Chicago", [41.8781, -87.6298])
]

for i, (name1, coords1) in enumerate(locations):
    for name2, coords2 in locations[i+1:]:
        dist = calculate_distance(coords1, coords2)
        print(f"{name1} โ†’ {name2}: {dist.distance:.2f} {dist.unit}")

10. Location-Based Testing & Simulation

Generate test data with random coordinates for testing location-based features, simulating user movements, or creating synthetic datasets.

from geo_intel_offline import generate_random_coordinates_by_region, resolve

# Generate test users in different countries
test_users = []
for country in ["United States", "United Kingdom", "Japan"]:
    coords = generate_random_coordinates_by_region(country, count=100, seed=42)
    for lat, lon in coords.coordinates:
        result = resolve(lat, lon)
        test_users.append({
            "lat": lat,
            "lon": lon,
            "country": result.country,
            "iso2": result.iso2
        })

๐Ÿ”ง Advanced Usage

Modular Data Loading

For applications that only need specific regions, you can use modular data loading to reduce memory footprint:

from geo_intel_offline import resolve

# Load only specific countries (requires modular data format)
result = resolve(40.7128, -74.0060, countries=["US", "CA", "MX"])

# Load by continent
result = resolve(51.5074, -0.1278, continents=["Europe"])

# Exclude specific countries
result = resolve(35.6762, 139.6503, exclude_countries=["RU", "CN"])

Note: Modular data loading requires building data in modular format. See Building Custom Data below.

Building Custom Data (Advanced)

Prerequisites

  1. Download Natural Earth Admin 0 Countries GeoJSON:
    wget https://www.naturalearthdata.com/downloads/10m-cultural-vectors/10m-admin-0-countries/
    # Or use the provided script:
    bash scripts/download_natural_earth.sh
    

Build Full Dataset

# Build complete dataset with compression
python3 -m geo_intel_offline.data_builder \
    data_sources/ne_10m_admin_0_countries.geojson \
    geo_intel_offline/data

# Or use automated script
python3 -m geo_intel_offline.data_builder \
    data_sources/ne_10m_admin_0_countries.geojson \
    geo_intel_offline/data

Note: The build process automatically compresses data files, reducing size by ~66%.

Build Modular Dataset

# Build modular format (country-wise files)
python3 -m geo_intel_offline.data_builder_modular \
    data_sources/ne_10m_admin_0_countries.geojson \
    output_directory

# Build specific countries only
python3 -m geo_intel_offline.data_builder_modular \
    --countries US,CA,MX \
    data_sources/ne_10m_admin_0_countries.geojson \
    output_directory

# Build by continent
python3 -m geo_intel_offline.data_builder_modular \
    --continents "North America,Europe" \
    data_sources/ne_10m_admin_0_countries.geojson \
    output_directory

โšก Performance & Accuracy

Performance Benchmarks

  • Lookup Speed: < 1ms per resolution
  • Memory Footprint: < 15 MB (all data in memory)
  • Cold Start: ~100ms (initial data load)
  • Data Size: ~4 MB compressed (66% reduction)

Performance Test

from geo_intel_offline import resolve
import time

test_points = [
    (40.7128, -74.0060),   # NYC
    (51.5074, -0.1278),    # London
    (35.6762, 139.6503),   # Tokyo
    # ... more points
]

start = time.perf_counter()
for _ in range(100):
    for lat, lon in test_points:
        resolve(lat, lon)
end = time.perf_counter()

avg_time = ((end - start) / (100 * len(test_points))) * 1000
print(f"Average lookup time: {avg_time:.3f}ms")

Test Results

Comprehensive testing across 258 countries with perfect accuracy:

Forward Geocoding (Coordinates โ†’ Country)

  • Overall Accuracy: 100.00% (2,568 passed / 2,568 total test points)
  • Countries Tested: 258
  • Countries with 100% Accuracy: 258 (100.0%)
  • Countries with 90%+ Accuracy: 258 (100.0%)
  • Test Points: 2,568 (10 points per country, varies for small territories)

Reverse Geocoding (Country โ†’ Coordinates)

  • Overall Accuracy: 100.00% (730 passed / 730 total tests)
  • By Country Name: 258/258 (100.00%)
  • By ISO2 Code: 236/236 (100.00%)
  • By ISO3 Code: 236/236 (100.00%)

Key Highlights

โœ… 100% accuracy for forward geocoding across all 258 countries
โœ… 100% accuracy for reverse geocoding with all input methods
โœ… 258 countries fully supported including territories and disputed regions
โœ… Comprehensive coverage of all continents and major territories
โœ… Perfect accuracy achieved through rigorous testing and edge case handling

See TEST_RESULTS.md for detailed country-wise results, continent-level breakdowns, and comprehensive test methodology.

Understanding Confidence Scores

Confidence scores range from 0.0 to 1.0:

  • 0.9 - 1.0: High confidence (well within country boundaries)
  • 0.7 - 0.9: Good confidence (inside country, may be near border)
  • 0.5 - 0.7: Moderate confidence (near border or ambiguous region)
  • 0.0 - 0.5: Low confidence (likely ocean or disputed territory)
from geo_intel_offline import resolve

result = resolve(40.7128, -74.0060)  # NYC (center of country)
print(f"Confidence: {result.confidence:.2f}")  # ~0.98

result = resolve(49.0, 8.2)  # Near France-Germany border
print(f"Confidence: {result.confidence:.2f}")  # ~0.65-0.75

result = resolve(0.0, 0.0)  # Ocean
print(f"Confidence: {result.confidence:.2f}")  # 0.0

๐Ÿ—๏ธ Architecture

The library uses a hybrid three-stage resolution pipeline:

  1. Geohash Indexing: Fast spatial filtering to candidate countries
  2. Point-in-Polygon: Accurate geometric verification using ray casting
  3. Confidence Scoring: Distance-to-border calculation for certainty assessment

For detailed architecture documentation, see ARCHITECTURE.md.

โ“ Troubleshooting

Issue: "Data file not found"

Solution: Ensure data files are present in the package installation directory, or build custom data:

# Check if data files exist
ls geo_intel_offline/data/*.json.gz

# If missing, rebuild data
python3 -m geo_intel_offline.data_builder \
    path/to/geojson \
    geo_intel_offline/data

Issue: Low accuracy for specific locations

Possible causes:

  • Location is in ocean (no country)
  • Location is on border (ambiguous)
  • Location is in disputed territory

Solution: Check confidence score and handle edge cases:

result = resolve(lat, lon)
if result.confidence < 0.5:
    print("Low confidence - may be ocean or border region")

Issue: Memory usage higher than expected

Solution: Use modular data loading to load only needed countries:

# Instead of loading all countries
result = resolve(lat, lon)

# Load only needed countries
result = resolve(lat, lon, countries=["US", "CA"])

๐Ÿค Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

See CONTRIBUTING.md for detailed guidelines.

๐Ÿ“ License

MIT License - see LICENSE file for details.

๐Ÿ“š Additional Resources

๐Ÿ”— Links

๐Ÿ”„ How It Differs from Other Libraries

Comparison with Popular Geo Libraries

Feature geo-intel-offline geopy reverse-geocoder countrycode
Offline โœ… 100% offline โŒ Requires internet โœ… Offline โœ… Offline
API Keys โŒ Not required โŒ Required โŒ Not required โŒ Not required
Cost ๐Ÿ’ฐ Free forever ๐Ÿ’ฐ Paid APIs ๐Ÿ’ฐ Free ๐Ÿ’ฐ Free
Distance Calculation โœ… Multiple algorithms โœ… Yes โŒ No โŒ No
Geo-fencing โœ… State tracking โŒ No โŒ No โŒ No
Random Coordinates โœ… By region/area โŒ No โŒ No โŒ No
Unit Auto-detection โœ… km/miles by country โŒ Manual โŒ No โŒ No
Accuracy โœ… 100% (258 countries) โš ๏ธ Varies โš ๏ธ Varies โš ๏ธ Varies
Speed โœ… < 1ms โš ๏ธ Network latency โœ… Fast โœ… Fast
Deterministic โœ… Always โš ๏ธ May vary โœ… Yes โœ… Yes
Dependencies โœ… Zero โš ๏ธ External APIs โœ… Minimal โœ… Minimal

Key Advantages

  1. All-in-One Solution: Unlike other libraries that only do geocoding, geo-intel-offline provides:

    • Forward geocoding (coordinates โ†’ country)
    • Reverse geocoding (country โ†’ coordinates)
    • Distance calculations with multiple algorithms
    • Geo-fencing with state tracking
    • Random coordinate generation
  2. Smart Unit Detection: Automatically detects whether to use kilometers or miles based on country preferences (US, GB, LR, MM use miles, others use km)

  3. Multiple Distance Algorithms: Choose from Haversine (fast), Vincenty (most accurate), or Spherical Law of Cosines, with automatic selection based on distance

  4. Stateful Geo-fencing: Track movement direction (approaching/leaving) with configurable alerts, not just simple inside/outside checks

  5. Validated Random Coordinates: Generate random coordinates within regions with point-in-polygon validation, ensuring all coordinates are actually within the specified area

  6. Zero External Dependencies: Works completely offline with no network calls, API keys, or external services

  7. 100% Accuracy: Tested across all 258 countries with perfect accuracy

Limitations

  1. Country-Level Only: Provides country-level resolution, not city/address-level (by design for offline operation)

  2. Static Data: Uses pre-built polygon data from Natural Earth. For real-time boundary changes, data needs to be rebuilt

  3. Memory Usage: Loads all country polygons into memory (~15MB). For very memory-constrained environments, consider modular data loading

  4. No Elevation Data: Does not provide elevation or terrain information

  5. No Time Zone Calculations: Provides timezone identifiers but doesn't calculate current time or DST transitions

  6. Distance Algorithms: For very long distances (>10,000km), Vincenty may be slower than Haversine, but provides better accuracy

When to Use This Library

โœ… Use geo-intel-offline when:

  • You need offline functionality
  • You're processing large batches of coordinates
  • You need distance calculations with smart unit detection
  • You need geo-fencing with state tracking
  • You need to generate test data with random coordinates
  • You want to avoid API costs and rate limits
  • You need deterministic, reproducible results
  • You're building for edge devices or IoT

โŒ Don't use geo-intel-offline when:

  • You need city/street-level geocoding (use Google Maps API, OpenStreetMap Nominatim)
  • You need real-time boundary updates (use online services)
  • You need elevation or terrain data (use specialized elevation APIs)
  • You need address parsing or reverse geocoding to addresses (use specialized geocoding services)

๐Ÿ™ Acknowledgments

  • Data source: Natural Earth
  • Geohash implementation: Based on standard geohash algorithm
  • Point-in-Polygon: Ray casting algorithm
  • Distance algorithms: Haversine, Vincenty, and Spherical Law of Cosines

๐Ÿ‘จโ€๐Ÿ’ป Author

Rakesh Ranjan Jena


Made with โค๏ธ by Rakesh Ranjan Jena for the Python community

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

geo_intel_offline-1.5.0.tar.gz (4.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

geo_intel_offline-1.5.0-py3-none-any.whl (4.3 MB view details)

Uploaded Python 3

File details

Details for the file geo_intel_offline-1.5.0.tar.gz.

File metadata

  • Download URL: geo_intel_offline-1.5.0.tar.gz
  • Upload date:
  • Size: 4.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for geo_intel_offline-1.5.0.tar.gz
Algorithm Hash digest
SHA256 dd07e3e195eae18b6fde14781dc27743a876c6a51e4c757c959796e00a5687d2
MD5 786712d833ca5ad52e33ae7d83a8fd57
BLAKE2b-256 7af722c7750ff0c46a22ca19a1412745a5b62da21ff2cdc4c818339b90d3c312

See more details on using hashes here.

File details

Details for the file geo_intel_offline-1.5.0-py3-none-any.whl.

File metadata

File hashes

Hashes for geo_intel_offline-1.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 049aace09f079d9a44134a4b5667ff16871fe526c6023834c62fdf2cf7f676d1
MD5 256a348679f6ba498660ac954c61c2f0
BLAKE2b-256 4baf749eb44f3be41f1e5f8c18f1c3bc9bc815cb6cfac159d484b7aeafccf26a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page