l

Auto-Location Inference for Posts: Building a Smart Geofencing System

Auto-Location Inference for Posts: Building a Smart Geofencing System

In digital platforms where content relevance drives engagement, location-based geofencing plays a key role in connecting users with contextually meaningful information. It helps ensure that users see posts that are local, timely, and personally relevant—whether it is a nearby dog park meet-up, a local pet store promotion, or a real-time alert about a lost dog in the neighbourhood.

By automatically inferring and assigning location context, platforms can personalise feeds, improve discovery accuracy, and strengthen community interaction.

The Challenge

Building a context-aware posting system that personalises content by geography involves multiple challenges:

  • Users may not always provide precise or consistent location information
  • Posts might lack any geographic reference
  • Backups and fallbacks must handle missing or invalid data
  • Geofencing operations must remain performant at scale

Multi-Layer Location Inference

To address incomplete or missing location information, Hoomanely’s layered location inference pipeline guarantees a fallback location that ensures every post includes a meaningful geographic context.

Location Resolution Hierarchy

1. Post-Level Location

The system first checks whether a post includes valid location metadata:

from typing import Dict, Optional

VALID_INDIA_IDENTIFIERS = ["India", "IN", "in", "Bharat"]
DEFAULT_COUNTRY = "India"

def validate_location(location: Optional[Dict]) -> bool:
    """Validate if location data is complete and usable."""
    try:
        return (
            location is not None and 
            location.get('country') and 
            location['country'].strip() != ""
        )
    except (AttributeError, TypeError):
        return False

post_location = post_data.get('location')
has_valid_post_location = validate_location(post_location)

Location Data Storage

When creating a post, location data is stored in a standardised format in the MongoDB document. Each post's location is represented as a nested object with the following structure:

from typing import TypedDict

class Location(TypedDict):
    latitude: float   # Geographical latitude (e.g., 12.9716)
    longitude: float  # Geographical longitude (e.g., 77.5946)
    city: str        # City name
    country: str     # Country name
    zip_code: str    # Postal/ZIP code

This structured format ensures:

  • Consistent location representation across all posts
  • Easy querying for geofencing
  • Support for both precise coordinates and region-based filtering
  • Flexibility for future location-based features

During post creation, the location object is embedded directly within the post document, making queries and retrieval efficient without requiring additional lookups or joins.

2. User Profile Location

If the post's location is missing or incomplete, the system falls back to using the user's saved profile location:

user_location = user.get('location')
has_valid_user_location = validate_location(user_location)

3. IP-Based Location

When neither post nor profile location is available, IP-based geolocation acts as a fallback:

user_ip_location = user.get('ipLocation')
has_valid_ip_location = validate_location(user_ip_location)

Trade-offs: IP-based geolocation provides convenience but comes with limitations. It may be inaccurate for VPN users, mobile users roaming across regions, or corporate networks with centralised IP addresses. Additionally, it raises privacy considerations as IP tracking may be subject to data protection regulations in certain jurisdictions.

4. Default Location

Finally, a predefined default ensures that every post is tagged with a valid region. The default location falls back to India, defined as a system constant:

DEFAULT_LOCATION: Location = {
    'latitude': 12.9716,
    'longitude': 77.5946,
    'city': 'Bangalore',
    'country': DEFAULT_COUNTRY,
    'zip_code': '560001'
}

Trade-off: Using a default location ensures data completeness but may result in posts being shown to geographically irrelevant users. This is acceptable for general content but should be combined with other relevance signals like user interests or engagement patterns to maintain feed quality.

Smart Geofencing System

The geofencing component filters content based on location proximity while maintaining high responsiveness and efficiency.

Database-Level Geofencing

All geographic filters are executed at the database layer to optimize query performance:

from typing import List

def get_feed_with_geofencing(
    self, 
    user_country: str, 
    limit: int = 10
) -> List[Dict]:
    """
    Retrieve posts filtered by geographic location.
    
    Args:
        user_country: The user's country identifier
        limit: Maximum number of posts to return
        
    Returns:
        List of posts matching the geographic criteria
    """
    try:
        normalized_country = user_country.strip().lower()
        
        if normalized_country in [id.lower() for id in VALID_INDIA_IDENTIFIERS]:
            query = {
                "$or": [
                    {"location.country": {"$in": VALID_INDIA_IDENTIFIERS}},
                    {"location.country": {"$exists": False}},
                    {"location.country": {"$eq": ""}},
                    {"location.country": {"$eq": None}}
                ]
            }
        else:
            query = {
                "$and": [
                    {"location.country": {"$nin": VALID_INDIA_IDENTIFIERS}},
                    {"location.country": {"$exists": True}},
                    {"location.country": {"$ne": ""}},
                    {"location.country": {"$ne": None}}
                ]
            }
        
        return list(self.collection.find(query).limit(limit))
    except Exception as e:
        # Log error and return empty list to prevent service disruption
        logger.error(f"Geofencing query failed: {e}")
        return []

Database Indexing Strategy

To ensure optimal query performance, the following indexes are created on the posts collection:

# Index on country field for geofencing queries
db.posts.create_index([("location.country", 1)])

# Compound index on country and zip_code for priority posts
db.posts.create_index([
    ("location.country", 1),
    ("location.zip_code", 1)
])

Similar indexes are maintained on the user collection for profile location lookups:

# User profile location index
db.users.create_index([("location.country", 1)])

These indexes significantly reduce query execution time, especially for large collections, and support efficient filtering at the database level.

Special Handling for Priority Posts

Critical post types such as alerts or emergency broadcasts in Hoomanely app use zipcode-level targeting to ensure precise visibility within relevant areas:

def create_priority_post(
    self, 
    post_data: Dict, 
    zipcode: str
) -> Optional[str]:
    """
    Create a high-priority post with precise location targeting.
    
    Args:
        post_data: Post content and metadata
        zipcode: Target ZIP code for delivery
        
    Returns:
        Post ID if successful, None otherwise
    """
    try:
        post_data['is_priority'] = True
        if not post_data.get('location'):
            post_data['location'] = {
                'latitude': 0.0,
                'longitude': 0.0,
                'city': "",
                'country': DEFAULT_COUNTRY,
                'zip_code': zipcode
            }
        
        result = self.collection.insert_one(post_data)
        return str(result.inserted_id)
    except Exception as e:
        logger.error(f"Failed to create priority post: {e}")
        return None

Performance Optimisation

Key optimisations improve scalability and responsiveness:

  • Database-level filtering minimises unnecessary data transfer
  • Cached user location data prevents redundant lookups
  • Batch processing with cursor-based pagination improves throughput
  • Indexed queries ensure fast location-based filtering even at scale

Results and Impact

The optimised location inference framework helped achieve measurable improvements:

  • Every post now includes valid location context
  • Increased content relevance through region-specific feeds
  • Improved visibility for time-critical, locality-based posts
  • Fast query performance through strategic indexing

Key Takeaways

  • A layered approach ensures consistent location inference under all conditions
  • Server-level filtering enhances performance and scalability
  • Special handling for priority or emergency posts improves reliability
  • A standardized location schema simplifies querying and integration
  • Proper validation, error handling, and indexing are critical for production reliability

By implementing a structured, multi-layer inference model with standardised geofencing, Hoomanely delivers locally relevant, high-impact content experiences while maintaining reliable system performance.

Read more