92 lines
3.5 KiB
Python
92 lines
3.5 KiB
Python
import json
|
|
import pandas as pd
|
|
|
|
def generate_analysis_artifacts(df):
|
|
"""
|
|
Takes a DataFrame, performs analysis, and generates analysis.md and map.html.
|
|
"""
|
|
total_territories = df['TerritoryID'].nunique()
|
|
total_addresses = df['Address Count'].sum()
|
|
average_addresses = df['Address Count'].mean()
|
|
|
|
# Territories per Category
|
|
category_counts = df.groupby('CategoryCode')['TerritoryID'].nunique().reset_index(name='Count')
|
|
|
|
# Generate Markdown Report
|
|
report_content = f"""# Territory Analysis v1.2.0
|
|
|
|
## Summary
|
|
- Total Territories: {total_territories}
|
|
- Total Addresses: {total_addresses}
|
|
- Average Addresses per Territory: {average_addresses:.2f}
|
|
|
|
## Territories per Category
|
|
| CategoryCode | Count |
|
|
|--------------|-------|
|
|
"""
|
|
for _, row in category_counts.iterrows():
|
|
report_content += f"| {row['CategoryCode']} | {row['Count']} |\n"
|
|
|
|
with open('analysis.md', 'w') as f:
|
|
f.write(report_content)
|
|
|
|
# Prepare data for embedding in HTML's JavaScript
|
|
# Convert DataFrame to a list of dicts for JSON serialization
|
|
data_for_json = df.to_dict(orient='records')
|
|
min_addresses = df['Address Count'].min()
|
|
max_addresses = df['Address Count'].max()
|
|
|
|
with open('map.html', 'w') as f:
|
|
f.write(f'''
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Territory Map</title>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
|
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="map" style="width: 100vw; height: 100vh;"></div>
|
|
<script>
|
|
var map = L.map('map').setView([26.3, 127.8], 10);
|
|
L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
}}).addTo(map);
|
|
|
|
var territories = {json.dumps(data_for_json, indent=4)};
|
|
var minAddresses = {min_addresses};
|
|
var maxAddresses = {max_addresses};
|
|
|
|
function getColor(d) {{
|
|
// Handle division by zero if all counts are the same
|
|
var range = maxAddresses - minAddresses;
|
|
if (range === 0) return 'hsl(120, 100%, 50%)';
|
|
var ratio = (d - minAddresses) / range;
|
|
var hue = (1 - ratio) * 120;
|
|
return 'hsl(' + hue + ', 100%, 50%)';
|
|
}}
|
|
|
|
for (var i = 0; i < territories.length; i++) {{
|
|
var territory = territories[i];
|
|
if (territory.Boundary) {{
|
|
try {{
|
|
var boundary = JSON.parse('[' + territory.Boundary + ']');
|
|
var color = getColor(territory['Address Count']);
|
|
var polygon = L.polygon(boundary.map(p => [p[1], p[0]]), {{
|
|
fillColor: color,
|
|
color: "#000",
|
|
weight: 1,
|
|
fillOpacity: 0.7
|
|
}}).addTo(map);
|
|
polygon.bindPopup('<b>Territory ID:</b> ' + territory.TerritoryID + '<br><b>Territory Number:</b> ' + territory.CategoryCode + '-' + territory.Number + '<br><b>Address Count:</b> ' + territory['Address Count']);
|
|
}} catch(e) {{
|
|
console.error("Could not parse boundary for territory: " + territory.TerritoryID, e);
|
|
}}
|
|
}}
|
|
}}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
''') |