Build A Personal AI Agent – Episode 13
Geographic Maps: Plot Indicator Performance on Regional Maps
A Practical Guide for Evaluators and M&E Professionals
Why Geographic Mapping Matters
Geographic maps provide powerful visual insights into how program performance varies across regions. This tutorial shows you how to create interactive regional maps showing indicator performance, enabling geographic targeting and resource allocation decisions.
Part 1: Setting Up Geographic Visualization
Step 1: Install Required Libraries
pip install folium plotly pandas openpyxlLibrary Guide:
folium: Interactive leaflet maps
plotly: Choropleth and interactive maps
pandas: Data manipulation
Part 2: Geographic Data Configuration
# geo_config.py
import pandas as pd
import json
class GeoConfig:
"""Configuration for geographic mapping."""
# Color schemes
COLORS = {
'low': '#dc3545', # Red - below 70%
'medium': '#ffc107', # Yellow - 70-90%
'high': '#28a745' # Green - above 90%
}
# Map tile options
TILES = {
'openstreetmap': 'OpenStreetMap',
'cartodb_positron': 'CartoDB positron'
}
@staticmethod
def get_region_coordinates():
"""Get region center coordinates."""
return {
'North': {'lat': 12.5, 'lon': 25.5},
'South': {'lat': -2.5, 'lon': 22.5},
'East': {'lat': 4.5, 'lon': 32.0},
'West': {'lat': 4.5, 'lon': 8.0},
'Central': {'lat': 6.0, 'lon': 20.0}
}Part 3: Building the Geographic Visualizer
1. Folium Interactive Map
import folium
from folium import plugins
import pandas as pd
from datetime import datetime
class FoliumMapGenerator:
"""Generate interactive maps using Folium."""
def __init__(self):
self.map = None
def create_base_map(self, location=[5.0, 20.0], zoom_start=4):
"""Create base map layer."""
self.map = folium.Map(
location=location,
zoom_start=zoom_start,
tiles='OpenStreetMap'
)
return self.map
def add_marker_clusters(self, df, lat_col='latitude', lon_col='longitude'):
"""Add marker clusters for individual data points."""
if lat_col not in df.columns or lon_col not in df.columns:
return
marker_cluster = plugins.MarkerCluster().add_to(self.map)
for _, row in df.iterrows():
popup_text = self._create_popup(row)
folium.Marker(
location=[row[lat_col], row[lon_col]],
popup=folium.Popup(popup_text, max_width=300),
icon=folium.Icon(
color=self._get_color(row.get('achievement', 0)),
icon='info-sign'
)
).add_to(marker_cluster)
def _create_popup(self, row):
"""Create HTML popup for marker."""
html = f"""
{row.get(‘indicator_name’, ‘Unknown’)}
| Achievement: | {row.get(‘achievement’, 0):.1f}% |
| Status: | {row.get(‘status’, ‘N/A’)} |
| Target: | {row.get(‘target’, ‘N/A’)} |
| Actual: | {row.get(‘actual’, ‘N/A’)} |
“”” return html def _get_color(self, achievement): “””Get color based on achievement value.””” if achievement >= 90: return ‘green’ elif achievement >= 70: return ‘orange’ else: return ‘red’ def save_map(self, filename=None): “””Save map to HTML file.””” if filename is None: filename = f”regional_map_{datetime.now().strftime(‘%Y%m%d_%H%M’)}.html” self.map.save(filename) return filename
2. Plotly Choropleth Map
import plotly.express as px
import pandas as pd
class PlotlyMapGenerator:
"""Generate choropleth maps using Plotly."""
def create_choropleth(self, df, region_col='region', value_col='achievement'):
"""Create choropleth map."""
region_avg = df.groupby(region_col)[value_col].mean().round(1).reset_index()
fig = px.choropleth(
region_avg,
locations=region_col,
locationmode='country names',
color=value_col,
color_continuous_scale='RdYlGn',
range_color=[0, 100],
scope='world',
title='Performance by Region'
)
fig.update_layout(height=500, width=None)
return fig
def create_scatter_geo(self, df, lat_col='latitude', lon_col='longitude'):
"""Create scatter map with performance indicators."""
if lat_col not in df.columns or lon_col not in df.columns:
return None
fig = px.scatter_geo(
df,
lat=lat_col,
lon=lon_col,
color='status',
hover_name='indicator_name',
size='achievement',
size_max=30,
title='Performance by Location',
color_discrete_map={
'on_track': '#28a745',
'at_risk': '#ffc107',
'off_track': '#dc3545'
}
)
fig.update_layout(height=500)
return figPart 4: Geographic Data Processor
# geo_processor.py
import pandas as pd
class GeoDataProcessor:
"""Process and prepare geographic data for mapping."""
def assign_regions(self, df, location_col='location'):
"""Assign regions based on location."""
region_map = {
'Province A': 'North', 'Province B': 'North', 'Province C': 'North',
'Province D': 'South', 'Province E': 'South', 'Province F': 'South',
'Province G': 'East', 'Province H': 'East', 'Province I': 'East',
'Province J': 'West', 'Province K': 'West', 'Province L': 'West',
'Province M': 'Central', 'Province N': 'Central', 'Province O': 'Central'
}
if location_col in df.columns:
df['region'] = df[location_col].map(region_map)
return df
def aggregate_by_region(self, df, value_col='achievement'):
"""Aggregate data by region."""
if 'region' not in df.columns:
df = self.assign_regions(df)
return df.groupby('region')[value_col].mean().round(1).reset_index()
def calculate_metrics(self, df):
"""Calculate comprehensive metrics by region."""
if 'region' not in df.columns:
df = self.assign_regions(df)
metrics = df.groupby('region').agg({
'indicator_name': 'count',
'achievement': 'mean',
'target': 'sum',
'actual': 'sum'
}).round(1)
metrics.columns = ['indicator_count', 'avg_achievement', 'total_target', 'total_actual']
return metrics.reset_index()Part 5: Complete Geographic Reporting System
class GeographicReportSystem:
"""Complete geographic reporting and mapping system."""
def __init__(self, data_connector):
self.data_connector = data_connector
self.processor = GeoDataProcessor()
self.folium_map = FoliumMapGenerator()
self.plotly_map = PlotlyMapGenerator()
def load_data(self):
"""Load and prepare geographic data."""
df = self.data_connector.get_data()
if 'region' not in df.columns and 'location' in df.columns:
df = self.processor.assign_regions(df)
return df
def generate_report(self, output_dir='regional_reports'):
"""Generate regional performance report."""
import os
os.makedirs(output_dir, exist_ok=True)
df = self.load_data()
# Calculate regional metrics
metrics = self.processor.calculate_metrics(df)
metrics.to_csv(f"{output_dir}/region_metrics.csv", index=False)
# Generate maps
print("Generating maps...")
# Folium map
folium_map = self.folium_map.create_base_map()
if 'latitude' in df.columns and 'longitude' in df.columns:
self.folium_map.add_marker_clusters(df)
folium_file = self.folium_map.save_map(f"{output_dir}/interactive_map.html")
# Plotly choropleth
fig = self.plotly_map.create_choropleth(df, 'region', 'achievement')
fig.write_html(f"{output_dir}/choropleth_map.html")
# Plotly scatter
if 'latitude' in df.columns and 'longitude' in df.columns:
fig_scatter = self.plotly_map.create_scatter_geo(df)
if fig_scatter:
fig_scatter.write_html(f"{output_dir}/scatter_map.html")
# Generate ranking table
ranking = self._generate_ranking(df)
ranking.to_csv(f"{output_dir}/region_ranking.csv", index=False)
print(f"Reports generated in {output_dir}")
return {'output_dir': output_dir, 'metrics': metrics, 'ranking': ranking}
def _generate_ranking(self, df):
"""Generate region ranking."""
region_perf = df.groupby('region')['achievement'].mean().sort_values(ascending=False)
ranking = pd.DataFrame({
'region': region_perf.index,
'achievement': region_perf.values
})
ranking['rank'] = range(1, len(ranking) + 1)
return rankingPart 6: Complete Usage Example
import pandas as pd
import numpy as np
def create_sample_data():
"""Create sample geographic data."""
indicators = ['Vaccination', 'Education', 'Health', 'WASH', 'Nutrition']
regions = ['North', 'South', 'East', 'West', 'Central']
locations = ['Province A', 'Province B', 'Province C', 'Province D', 'Province E']
data = []
for region in regions:
for loc in locations:
for indicator in indicators:
achievement = np.random.uniform(50, 98)
target = np.random.randint(100, 500)
actual = int(target * achievement / 100)
if achievement >= 90:
status = 'on_track'
elif achievement >= 70:
status = 'at_risk'
else:
status = 'off_track'
data.append({
'indicator_name': indicator,
'location': loc,
'region': region,
'target': target,
'actual': actual,
'achievement': round(achievement, 1),
'status': status,
'latitude': np.random.uniform(-5, 15),
'longitude': np.random.uniform(8, 32),
'date': '2024-01-15'
})
df = pd.DataFrame(data)
df.to_excel('geographic_data.xlsx', index=False)
return df
# Main execution
def main():
# Create sample data
print("Creating sample data...")
df = create_sample_data()
print(f"Created {len(df)} records")
# Simple data connector
class SimpleConnector:
def get_data(self):
return pd.read_excel('geographic_data.xlsx')
connector = SimpleConnector()
# Generate report
system = GeographicReportSystem(connector)
results = system.generate_report()
print("\nRegion Metrics:")
print(results['metrics'].head())
print("\nReports saved in 'regional_reports' folder")
if __name__ == "__main__":
main()
Troubleshooting Geographic Mapping
| Issue | Solution |
|---|---|
| Map not showing | Check latitude/longitude columns exist |
| No regions displayed | Ensure region mapping is configured |
| Choropleth not rendering | Check region names match Plotly locations |
| Slow rendering | Reduce markers using clustering |
Best Practices for Geographic Mapping
- Accurate Coordinates: Use precise latitude/longitude for locations
- Consistent Regions: Use standard region names across datasets
- Color Meaning: Use green=good, yellow=warning, red=critical
- Interactive Features: Add popups with detailed data
- Mobile Responsive: Ensure maps work on all devices
Next Steps: Advanced Geographic Analysis
- Spatial Analysis: Identify clusters using spatial statistics
- Time-Series Maps: Animate performance over time
- Catchment Areas: Draw service catchment areas
- Multi-Layer Maps: Combine multiple data layers
Master Geographic Mapping for M&E
The AI Agents for Evaluators Certificate teaches you to build complete M&E solutions including geographic mapping, automated reporting, and stakeholder management.
What you will learn:
- Build interactive regional maps
- Analyze geographic performance patterns
- Create choropleth and scatter maps
- Integrate maps into reports
Course Features: 32 lectures · Lifetime access · Certificate included
Enroll Now – $249 Lifetime Access
Bundle with AI in M&E course and save 30%
This guide is part of the AI Agents for Evaluators series. Continue your learning journey with the full certificate course above.
