feat(maps): Phase 2 — markets hub country bubble map
Add Leaflet map to /markets with country-level bubbles sized by total_venues and colored by avg_market_score. Click navigates to country overview page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
<meta name="description" content="{{ t.markets_page_description }}">
|
||||
<meta property="og:title" content="{{ t.markets_page_og_title }} - {{ config.APP_NAME }}">
|
||||
<meta property="og:description" content="{{ t.markets_page_og_description }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='vendor/leaflet/leaflet.min.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@@ -15,6 +16,8 @@
|
||||
<p class="text-slate">{{ t.mkt_subheading }}</p>
|
||||
</header>
|
||||
|
||||
<div id="markets-map" style="height:420px; border-radius:12px;" class="mb-6"></div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="card mb-8">
|
||||
<div style="display: grid; grid-template-columns: 1fr auto auto; gap: 1rem; align-items: end;">
|
||||
@@ -62,3 +65,43 @@
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='vendor/leaflet/leaflet.min.js') }}"></script>
|
||||
<script>
|
||||
(function() {
|
||||
var map = L.map('markets-map', {scrollWheelZoom: false}).setView([48.5, 10], 4);
|
||||
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OSM</a> © <a href="https://carto.com/">CARTO</a>',
|
||||
maxZoom: 18
|
||||
}).addTo(map);
|
||||
|
||||
function scoreColor(score) {
|
||||
if (score >= 60) return '#16A34A';
|
||||
if (score >= 30) return '#D97706';
|
||||
return '#DC2626';
|
||||
}
|
||||
|
||||
fetch('/api/markets/countries.json')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (!data.length) return;
|
||||
var maxV = Math.max.apply(null, data.map(function(d) { return d.total_venues; }));
|
||||
var lang = document.documentElement.lang || 'en';
|
||||
data.forEach(function(c) {
|
||||
if (!c.lat || !c.lon) return;
|
||||
var radius = 6 + 22 * Math.sqrt(c.total_venues / maxV);
|
||||
var color = scoreColor(c.avg_market_score);
|
||||
L.circleMarker([c.lat, c.lon], {
|
||||
radius: radius,
|
||||
fillColor: color, color: color,
|
||||
fillOpacity: 0.6, opacity: 0.9, weight: 1
|
||||
})
|
||||
.bindTooltip(c.country_name_en + '<br>' + c.total_venues + ' venues in ' + c.city_count + ' cities', {className: 'map-tooltip'})
|
||||
.on('click', function() { window.location = '/' + lang + '/markets/' + c.country_slug; })
|
||||
.addTo(map);
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user