Skip to content

Locations

Overview

The Locations app provides an intelligent, crowd-sourced geographic data management system for Jend-Z. It enables location-based content discovery while dramatically reducing geocoding API costs through smart boundary detection and community-driven location naming. The system combines traditional hierarchical location data with dynamic neighborhood boundaries and consensus-based location names.

Smart Location System

Key Features

  • Hybrid Geocoding: Reduces API calls by reusing existing location data for nearby users
  • Dynamic Boundaries: Automatically calculates neighborhood boundaries based on user distribution
  • Crowd-Sourced Naming: Community-driven location naming with weighted consensus
  • Quality Scoring: Confidence metrics based on user count and name agreement
  • Intelligent Fallbacks: Progressive location discovery with multiple hierarchy levels

How It Works

  1. First User in Area: Frontend geocoding creates initial location with default 500m radius
  2. Subsequent Users: System detects existing boundaries, suggests location without geocoding
  3. Name Consensus: Users suggest/correct names, system builds weighted consensus
  4. Boundary Evolution: Boundaries automatically adjust as more users join the area
  5. Quality Improvement: Confidence scores increase with user participation

Enhanced Data Model

Location Model

The Location model now includes both traditional geographic data and smart location features.

Traditional Geographic Fields

  • id (UUID): Unique identifier
  • name (CharField): Auto-generated display name (editable=False)
  • created_at (DateTimeField): Creation timestamp
  • longitude/latitude (FloatField): Original coordinate pair
  • Standard hierarchy: country, state, city, neighbourhood, etc. (17 levels)

Smart Location Extensions

Dynamic Boundaries:

  • center_latitude (FloatField): Calculated center of user cluster
  • center_longitude (FloatField): Calculated center of user cluster
  • estimated_radius (IntegerField): Dynamic radius in meters (200m-2km bounds)
  • last_boundary_update (DateTimeField): Timestamp of last recalculation

Consensus System:

  • name_suggestions (JSONField): Weighted vote tracking {"name": vote_weight}
  • total_users (IntegerField): Number of users in this location
  • confidence_score (FloatField): Overall location quality (0.0-1.0)

Quality Management:

  • needs_verification (BooleanField): Flagged for moderator review
  • is_auto_generated (BooleanField): Created without geocoding data
  • moderator_approved (BooleanField): Staff-approved location

Supporting Models

LocationNameSuggestion

Tracks individual name suggestions from users with detailed analytics.

{
  "id": "uuid",
  "location": "location-uuid",
  "user": "user-uuid",
  "suggested_name": "Downtown Lagos",
  "vote_weight": 1.2,
  "is_approved": false,
  "is_flagged": false,
  "created_at": "2024-01-15T10:30:00Z"
}

LocationModerationLog

Audit trail for all location modifications and administrative actions.

API Endpoints

Smart Location Management

1. Smart Location Update

Endpoint: POST /api/locations/locations/smart_update/

Description: Intelligent location update that reuses existing boundaries when possible.

Request Body:

{
  "latitude": 6.4302,
  "longitude": 3.4236,
  "suggested_name": "VI Business District", // Optional
  "geocoded_data": {
    // Optional - from frontend geocoding
    "address": {
      "country": "Nigeria",
      "state": "Lagos State",
      "city": "Lagos",
      "neighbourhood": "Victoria Island"
    }
  }
}

Response - Existing Location Found:

{
  "location": {
    "id": "location-uuid",
    "name": "Victoria Island",
    "confidence_score": 0.85,
    "estimated_radius": 750,
    "center_latitude": 6.4298,
    "center_longitude": 3.4238,
    "total_users": 23,
    "country": "Nigeria",
    "state": "Lagos State",
    "city": "Lagos",
    "neighbourhood": "Victoria Island"
  },
  "source": "existing",
  "message": "Found existing location: Victoria Island",
  "alternative_names": ["VI", "Victoria Island", "Lagos Island"],
  "name_suggestion": {
    "message": "Name suggestion recorded",
    "vote_weight": 1.2,
    "top_suggestions": ["Victoria Island", "VI Business District", "Lagos VI"]
  }
}

Response - New Location Created:

{
  "location": {
    "id": "new-location-uuid",
    "name": "VI Business District",
    "confidence_score": 0.3,
    "estimated_radius": 500,
    "center_latitude": 6.4302,
    "center_longitude": 3.4236,
    "total_users": 1,
    "is_auto_generated": false
  },
  "source": "new",
  "message": "Created new location",
  "needs_more_data": false
}

2. Suggest Location Name

Endpoint: POST /api/locations/locations/{location_id}/suggest_name/

Description: Community-driven location naming with consensus building.

Request Body:

{
  "name": "Lagos Business District"
}

Response - Name Accepted:

{
  "message": "Name suggestion accepted and location name updated",
  "old_name": "Victoria Island",
  "new_name": "Lagos Business District",
  "confidence": 0.87,
  "vote_weight": 1.2
}

Response - Suggestion Recorded:

{
  "message": "Name suggestion recorded",
  "current_name": "Victoria Island",
  "suggested_name": "Lagos Business District",
  "vote_weight": 1.2,
  "top_suggestions": [
    "Victoria Island",
    "Lagos Business District",
    "VI Downtown"
  ]
}

3. Get Location Name Suggestions

Endpoint: GET /api/locations/locations/{location_id}/name_suggestions/

Description: View community suggestions for a location with smart grouping to eliminate duplicates.

Query Parameters:

  • show_individual (optional): Set to true for paginated individual suggestions instead of grouped view

Default Response (Grouped View):

{
  "location_name": "Victoria Island",
  "confidence_score": 0.75,
  "total_suggestions": 15,
  "view_type": "grouped",
  "grouped_suggestions": [
    {
      "representative_name": "Victoria Island",
      "total_weight": 18.5,
      "suggestion_count": 8,
      "is_current_name": true,
      "suggestions": [
        {
          "id": "suggestion-uuid",
          "suggested_name": "Victoria Island",
          "user": {
            "username": "lagos_user",
            "display_name": "Lagos User"
          },
          "vote_weight": 2.1,
          "created_at": "2024-01-15T10:30:00Z"
        },
        {
          "id": "suggestion-uuid-2",
          "suggested_name": "victoria island", // Similar names grouped together
          "user": {
            "username": "local_resident",
            "display_name": "Local Resident"
          },
          "vote_weight": 1.8,
          "created_at": "2024-01-14T15:20:00Z"
        }
      ]
    },
    {
      "representative_name": "VI Business District",
      "total_weight": 12.3,
      "suggestion_count": 4,
      "is_current_name": false,
      "suggestions": [...]
    }
  ],
  "consensus_data": {
    "Victoria Island": 18.5,
    "VI Business District": 12.3,
    "Lagos VI": 8.7
  },
  "top_voted_names": [
    ["Victoria Island", 18.5],
    ["VI Business District", 12.3],
    ["Lagos VI", 8.7]
  ]
}

Individual Suggestions (Paginated): Endpoint: GET /api/locations/locations/{location_id}/name_suggestions/?show_individual=true

{
  "count": 47,
  "next": "http://api.example.com/api/locations/locations/{id}/name_suggestions/?show_individual=true&page=2",
  "previous": null,
  "results": {
    "location_name": "Victoria Island",
    "confidence_score": 0.75,
    "total_suggestions": 47,
    "view_type": "individual",
    "suggestions": [
      {
        "id": "suggestion-uuid",
        "suggested_name": "VI Business District",
        "user": {
          "username": "lagos_user",
          "display_name": "Lagos User"
        },
        "vote_weight": 1.5,
        "created_at": "2024-01-15T10:30:00Z",
        "is_current_name": false,
        "similarity_to_current": 65
      }
    ]
  }
}

Smart Grouping Features:

  • Fuzzy Matching: Similar names like "Downtown" and "Down Town" are grouped together
  • Representative Names: Shows the highest-weighted suggestion as the group representative
  • Vote Aggregation: Combines vote weights for similar suggestions
  • Duplicate Elimination: Users see clean, organized suggestions without confusion
  • Fallback Support: Works even without fuzzy matching library (exact matches only)
#### 4. Find Nearby Locations

**Endpoint:** `GET /api/locations/locations/nearby/`

**Description:** Discover existing locations near coordinates.

**Query Parameters:**
- `latitude` (required): Latitude coordinate
- `longitude` (required): Longitude coordinate
- `max_distance` (optional): Maximum distance in km (default: 5.0)

**Response:**
```json
{
  "count": 3,
  "locations": [
    {
      "id": "location-1",
      "name": "Victoria Island",
      "confidence_score": 0.85,
      "estimated_radius": 750,
      "distance_km": 0.8,
      "total_users": 23
    },
    {
      "id": "location-2",
      "name": "Ikoyi",
      "confidence_score": 0.72,
      "estimated_radius": 900,
      "distance_km": 2.1,
      "total_users": 18
    }
  ]
}

5. Search Locations

Endpoint: GET /api/locations/locations/search/

Description: Search locations with fuzzy name matching.

Query Parameters:

  • q (required): Search query
  • latitude (optional): For distance-based sorting
  • longitude (optional): For distance-based sorting

Response:

{
  "query": "victoria island",
  "count": 5,
  "locations": [
    {
      "id": "location-uuid",
      "name": "Victoria Island",
      "confidence_score": 0.85,
      "total_users": 23,
      "city": "Lagos",
      "state": "Lagos State"
    }
  ]
}

Traditional Location APIs

6. List All Locations

Endpoint: GET /api/locations/locations/

Enhanced Query Parameters:

  • Traditional filters: country, state, city, neighbourhood
  • Smart filters: min_confidence, needs_verification, order_by_confidence
  • Activity filters: order_by_posts, order_by_stores

Response includes confidence metrics:

{
  "count": 150,
  "results": [
    {
      "id": "location-uuid",
      "name": "Victoria Island",
      "confidence_score": 0.85,
      "is_high_confidence": true,
      "total_users": 23,
      "estimated_radius": 750,
      "top_suggestions": [
        { "name": "Victoria Island", "votes": 15.2 },
        { "name": "VI", "votes": 8.7 }
      ],
      "country": "Nigeria",
      "state": "Lagos State",
      "city": "Lagos",
      "neighbourhood": "Victoria Island",
      "post_count": 245,
      "store_count": 18
    }
  ]
}

Location Name Consensus System

How Name Consensus Works

Voting Weight Calculation

User votes are weighted based on several factors:

Base Factors:

  • Account Age: Newer users (1 day) get 0.1x weight, established users (2+ years) get up to 2.0x
  • Activity Level: Users with more posts get slight boost (max 1.5x)
  • Verification Status: Verified users get 1.2x multiplier
  • Very New Accounts: Accounts less than 7 days get reduced weight (0.5x)

Example Vote Weights:

  • New user (2 days old): 0.5
  • Regular user (6 months, some posts): 1.2
  • Active user (1 year, many posts, verified): 2.0
  • Very new user (few hours old): 0.1

Consensus Algorithm

Name Grouping:

  • Uses fuzzy string matching (85% similarity threshold)
  • Groups similar names: "Downtown Lagos" + "Down Town Lagos" = same group
  • Combines vote weights for similar names

Name Selection:

  • Location name updates when new consensus has >60% confidence
  • Requires significant improvement over current name (+10% confidence)
  • Most voted name in winning group becomes the location name

Quality Safeguards:

  • Profanity filtering and inappropriate content detection
  • Length limits (2-100 characters)
  • Character validation (letters, numbers, basic punctuation)
  • Staff moderation for disputed names

User Experience Flow

Suggesting Location Names

// Frontend integration for name suggestions
const suggestLocationName = async (locationId, suggestedName) => {
  try {
    const response = await api.post(
      `/api/locations/locations/${locationId}/suggest_name/`,
      { name: suggestedName }
    );

    if (response.data.message.includes("updated")) {
      showNotification("Location name updated! 🎉", "success");
    } else {
      showNotification("Thanks for the suggestion!", "info");
      showAlternatives(response.data.top_suggestions);
    }
  } catch (error) {
    handleNameSuggestionError(error);
  }
};

Displaying Name Suggestions

// Show alternative names to users
const showLocationWithAlternatives = (location) => {
  const alternatives = location.alternative_names || [];

  return (
    <div className="location-card">
      <h3>{location.name}</h3>
      <p className="confidence">
        Confidence: {(location.confidence_score * 100).toFixed(1)}%
      </p>

      {alternatives.length > 0 && (
        <div className="alternatives">
          <p>Also known as:</p>
          <ul>
            {alternatives.map((alt) => (
              <li key={alt}>{alt}</li>
            ))}
          </ul>
        </div>
      )}

      <button onClick={() => suggestName(location.id)}>
        Suggest Better Name
      </button>
    </div>
  );
};

Dynamic Boundary System

Boundary Calculation

Algorithm:

  1. User Clustering: Collect coordinates of all users in location
  2. Center Calculation: Compute centroid of user coordinates
  3. Radius Determination: Use 90th percentile distance from center
  4. Bounds Enforcement: Minimum 200m, maximum 2km radius
  5. Quality Update: Adjust confidence based on user count and consensus

Automatic Updates:

  • Triggered when new users join location
  • Recalculated when user count reaches thresholds (5, 10, 25, 50+)
  • Manual recalculation available for administrators

Integration with Content Discovery

Smart Post Filtering:

// Posts are automatically filtered by dynamic boundaries
const getLocalPosts = async (userLocation) => {
  // Backend automatically finds posts within the user's location boundary
  const response = await api.get(
    `/api/posts/posts/by-location/?location_id=${userLocation.id}`
  );
  return response.data.posts;
};

Boundary Visualization:

// Display location boundary on map
const showLocationBoundary = (location) => {
  const circle = new google.maps.Circle({
    center: {
      lat: location.center_latitude,
      lng: location.center_longitude,
    },
    radius: location.estimated_radius,
    fillColor: location.confidence_score > 0.7 ? "#00FF00" : "#FFA500",
    fillOpacity: 0.2,
    strokeColor: "#0066CC",
    strokeWeight: 2,
  });

  circle.setMap(map);
};

Administrative & Moderation Features

Location Quality Management

7. Flag Location for Review

Endpoint: POST /api/locations/locations/{location_id}/flag_for_review/

Request Body:

{
  "reason": "Inappropriate name suggestions"
}

8. Location Analytics (Staff Only)

Endpoint: GET /api/locations/locations/analytics/

Response:

{
  "summary": {
    "total_locations": 1250,
    "low_confidence_count": 45,
    "needs_verification_count": 12,
    "auto_generated_count": 234,
    "recent_suggestions_count": 67
  },
  "top_locations": [
    {
      "id": "location-uuid",
      "name": "Victoria Island",
      "total_users": 156,
      "confidence_score": 0.92
    }
  ]
}

Name Suggestion Management

9. Name Suggestion Analytics

Endpoint: GET /api/locations/suggestions/

Query Parameters:

  • location_id: Filter by location
  • my_suggestions=true: User's own suggestions only

Response:

{
  "count": 25,
  "results": [
    {
      "id": "suggestion-uuid",
      "suggested_name": "Lagos Business Hub",
      "location_name": "Victoria Island",
      "user": {
        "username": "local_expert",
        "display_name": "Local Expert"
      },
      "vote_weight": 2.1,
      "is_current_name": false,
      "similarity_to_current": 72,
      "created_at": "2024-01-15T10:30:00Z"
    }
  ]
}

Frontend Integration Patterns

Smart Location Detection

// Enhanced location detection with smart suggestions
const getLocationSmart = async () => {
  try {
    const position = await getCurrentPosition();
    const { latitude, longitude } = position.coords;

    // Try frontend geocoding first (optional, for better UX)
    let geocodedData = null;
    try {
      geocodedData = await frontendGeocode(latitude, longitude);
    } catch (geocodeError) {
      console.log("Frontend geocoding failed, backend will handle");
    }

    // Smart backend location update
    const response = await api.post("/api/locations/locations/smart_update/", {
      latitude,
      longitude,
      geocoded_data: geocodedData,
    });

    const locationData = response.data;

    // Handle different scenarios
    if (locationData.source === "existing") {
      showLocationConfirmation({
        location: locationData.location,
        alternatives: locationData.alternative_names,
        canSuggestName: true,
      });
    } else {
      showNewLocationCreated({
        location: locationData.location,
        needsMoreData: locationData.needs_more_data,
      });
    }

    return locationData.location;
  } catch (error) {
    handleLocationError(error);
  }
};

Location Name Suggestions UI

// UI for location name suggestions
const LocationNameEditor = ({ location, onUpdate }) => {
  const [suggestions, setSuggestions] = useState([]);
  const [newSuggestion, setNewSuggestion] = useState("");

  useEffect(() => {
    loadNameSuggestions();
  }, [location.id]);

  const loadNameSuggestions = async () => {
    const response = await api.get(
      `/api/locations/locations/${location.id}/name_suggestions/`
    );
    setSuggestions(response.data.suggestions);
  };

  const submitSuggestion = async () => {
    if (!newSuggestion.trim()) return;

    try {
      const response = await api.post(
        `/api/locations/locations/${location.id}/suggest_name/`,
        { name: newSuggestion }
      );

      showNotification(response.data.message);
      loadNameSuggestions();
      setNewSuggestion("");

      if (response.data.new_name) {
        onUpdate({ ...location, name: response.data.new_name });
      }
    } catch (error) {
      showError(error.response.data.error);
    }
  };

  return (
    <div className="location-name-editor">
      <h3>Location: {location.name}</h3>
      <p>Confidence: {(location.confidence_score * 100).toFixed(1)}%</p>

      <div className="suggestion-input">
        <input
          type="text"
          placeholder="Suggest a better name..."
          value={newSuggestion}
          onChange={(e) => setNewSuggestion(e.target.value)}
        />
        <button onClick={submitSuggestion}>Suggest</button>
      </div>

      {suggestions.length > 0 && (
        <div className="existing-suggestions">
          <h4>Community Suggestions:</h4>
          {suggestions.slice(0, 5).map((suggestion) => (
            <div key={suggestion.id} className="suggestion-item">
              <span className="name">{suggestion.suggested_name}</span>
              <span className="votes">
                ({suggestion.vote_weight.toFixed(1)} votes)
              </span>
              <span className="user">by {suggestion.user.display_name}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

Location Discovery

// Enhanced location search with smart suggestions
const LocationSearch = ({ onLocationSelect }) => {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);
  const [nearbyLocations, setNearbyLocations] = useState([]);

  const searchLocations = async (searchQuery) => {
    if (searchQuery.length < 2) return;

    const response = await api.get("/api/locations/locations/search/", {
      params: { q: searchQuery },
    });

    setResults(response.data.locations);
  };

  const findNearbyLocations = async () => {
    const position = await getCurrentPosition();
    const response = await api.get("/api/locations/locations/nearby/", {
      params: {
        latitude: position.coords.latitude,
        longitude: position.coords.longitude,
        max_distance: 10,
      },
    });

    setNearbyLocations(response.data.locations);
  };

  useEffect(() => {
    findNearbyLocations();
  }, []);

  return (
    <div className="location-search">
      <input
        type="text"
        placeholder="Search locations..."
        value={query}
        onChange={(e) => {
          setQuery(e.target.value);
          searchLocations(e.target.value);
        }}
      />

      {results.length > 0 && (
        <div className="search-results">
          {results.map((location) => (
            <LocationCard
              key={location.id}
              location={location}
              onSelect={onLocationSelect}
              showConfidence={true}
            />
          ))}
        </div>
      )}

      {nearbyLocations.length > 0 && (
        <div className="nearby-locations">
          <h4>Nearby Locations:</h4>
          {nearbyLocations.map((location) => (
            <LocationCard
              key={location.id}
              location={location}
              onSelect={onLocationSelect}
              showDistance={true}
            />
          ))}
        </div>
      )}
    </div>
  );
};

Error Handling

Smart Location API Errors

Location Update Errors:

{
  "error": "Valid latitude and longitude are required"
}

Name Suggestion Errors:

{
  "error": "Name contains inappropriate content"
}

Rate Limiting (if implemented):

{
  "error": "Too many suggestions. Please try again later.",
  "retry_after": 300
}

Frontend Error Handling

const handleLocationError = (error) => {
  if (error.response) {
    switch (error.response.status) {
      case 400:
        showError("Invalid location data. Please try again.");
        break;
      case 429:
        showError("Too many requests. Please wait a moment.");
        break;
      default:
        showError("Location update failed. Please try again.");
    }
  } else {
    showError("Network error. Please check your connection.");
  }
};

Performance Considerations

Caching Strategy

Location Data:

  • Cache frequently accessed locations (high user count)
  • Cache search results for common queries
  • Cache user's current location for session

Name Suggestions:

  • Cache top suggestions for popular locations
  • Invalidate cache when consensus changes

Database Optimization

Indexes:

  • Geographic coordinates for spatial queries
  • Confidence scores for quality filtering
  • User counts for popularity sorting

Query Optimization:

  • Use subqueries for count aggregations
  • Limit concurrent boundary recalculations
  • Batch process suggestion weight updates

Best Practices

Location Management

  1. Gradual Rollout: Deploy smart location features gradually
  2. Fallback Strategies: Always provide fallbacks for location detection failures
  3. User Education: Explain the community-driven naming system
  4. Quality Monitoring: Regular review of low-confidence locations

Name Suggestions

  1. Encourage Participation: Make name suggestion UI discoverable
  2. Show Progress: Display confidence scores and vote counts
  3. Handle Conflicts: Provide clear feedback when suggestions are similar
  4. Moderate Actively: Review flagged content promptly

Frontend Integration

  1. Progressive Enhancement: Smart features should enhance, not break, basic functionality
  2. Loading States: Show appropriate loading indicators during API calls
  3. Offline Handling: Cache location data for offline scenarios
  4. Accessibility: Ensure location UI is accessible to all users

The smart location system transforms static geographic data into a living, community-driven resource that becomes more accurate and valuable as your user base grows, while significantly reducing operational costs.