Files
mapbox_gl_d3_playground/qwencoder2.html
2025-08-27 02:00:32 -06:00

299 lines
10 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mapbox GL JS + D3.js Integration - Hover Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.css" rel="stylesheet">
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
#info {
position: absolute;
top: 10px;
left: 10px;
background: white;
padding: 10px;
border-radius: 5px;
z-index: 1000;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
}
#chart {
position: absolute;
top: 10px;
right: 10px;
width: 300px;
height: 200px;
background: white;
padding: 10px;
border-radius: 5px;
z-index: 1000;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
}
.tooltip {
position: absolute;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 8px 12px;
border-radius: 4px;
font-size: 12px;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s;
z-index: 1001;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="info">
<h3>Mapbox + D3.js Demo</h3>
<p>Interactive map with D3 data visualization</p>
</div>
<div id="chart">
<h4>Population Distribution</h4>
<svg id="bar-chart" width="280" height="150"></svg>
</div>
<div id="tooltip" class="tooltip"></div>
<script>
// Initialize Mapbox GL JS
mapboxgl.accessToken = 'pk.eyJ1Ijoiam9yZGl0b3N0IiwiYSI6ImQtcVkyclEifQ.vwKrOGZoZSj3N-9MB6FF_A'; // Replace with your token
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [-100, 40],
zoom: 3
});
// Sample data - in real app this could come from an API or file
const sampleData = [
{ name: "New York", population: 8336817, coordinates: [-74.006, 40.7128] },
{ name: "Los Angeles", population: 3979576, coordinates: [-118.2437, 34.0522] },
{ name: "Chicago", population: 2746388, coordinates: [-87.6298, 41.8781] },
{ name: "Houston", population: 2320268, coordinates: [-95.3698, 29.7604] },
{ name: "Phoenix", population: 1680992, coordinates: [-112.0740, 33.4484] },
{ name: "Philadelphia", population: 1584064, coordinates: [-75.1652, 39.9526] }
];
// Use D3 to process and visualize data
const populationData = sampleData.map(d => d.population);
const maxPopulation = Math.max(...populationData);
// Create bar chart with D3
function createBarChart() {
const svg = d3.select("#bar-chart");
const width = +svg.attr("width");
const height = +svg.attr("height");
// Clear existing content
svg.selectAll("*").remove();
// Set up scales
const xScale = d3.scaleBand()
.domain(sampleData.map(d => d.name))
.range([0, width])
.padding(0.1);
const yScale = d3.scaleLinear()
.domain([0, maxPopulation])
.range([height, 0]);
// Create bars
svg.selectAll(".bar")
.data(sampleData)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", d => xScale(d.name))
.attr("width", xScale.bandwidth())
.attr("y", d => yScale(d.population))
.attr("height", d => height - yScale(d.population))
.attr("fill", "#3b82f6")
.attr("stroke", "#1e40af")
.attr("stroke-width", 1);
// Add value labels on bars
svg.selectAll(".label")
.data(sampleData)
.enter()
.append("text")
.attr("class", "label")
.attr("x", d => xScale(d.name) + xScale.bandwidth() / 2)
.attr("y", d => yScale(d.population) - 5)
.attr("text-anchor", "middle")
.style("font-size", "10px")
.style("fill", "#333")
.text(d => Math.round(d.population / 1000000) + "M");
}
// Create map markers with hover functionality
function createMarkers() {
const markers = [];
sampleData.forEach(city => {
// Create marker element
const el = document.createElement('div');
el.className = 'marker';
el.style.width = '20px';
el.style.height = '20px';
el.style.background = '#3b82f6';
el.style.border = '2px solid white';
el.style.borderRadius = '50%';
el.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
el.style.cursor = 'pointer';
// Create marker
const marker = new mapboxgl.Marker(el)
.setLngLat(city.coordinates)
.addTo(map);
markers.push({
marker: marker,
city: city
});
// Add hover event listeners
el.addEventListener('mouseenter', function(e) {
showTooltip(city, e);
});
el.addEventListener('mouseleave', function() {
hideTooltip();
});
});
return markers;
}
// Show tooltip on hover
function showTooltip(city, event) {
const tooltip = document.getElementById('tooltip');
tooltip.innerHTML = `
<strong>${city.name}</strong><br>
Population: ${city.population.toLocaleString()}<br>
Rank: ${(sampleData.findIndex(c => c.name === city.name) + 1).toLocaleString()}
`;
// Position tooltip near mouse
const rect = map.getContainer().getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
tooltip.style.left = (x + 10) + 'px';
tooltip.style.top = (y - 30) + 'px';
tooltip.style.opacity = 1;
}
// Hide tooltip
function hideTooltip() {
const tooltip = document.getElementById('tooltip');
tooltip.style.opacity = 0;
}
// Initialize everything when map is loaded
map.on('load', function () {
// Create D3 visualizations
createBarChart();
// Create map markers with hover functionality
const markers = createMarkers();
// Add a custom data layer using D3 processed data
const geojsonData = {
type: "FeatureCollection",
features: sampleData.map(city => ({
type: "Feature",
geometry: {
type: "Point",
coordinates: city.coordinates
},
properties: {
name: city.name,
population: city.population
}
}))
};
// Add source and layer for data visualization
map.addSource('cities', {
type: 'geojson',
data: geojsonData
});
map.addLayer({
id: 'cities-points',
type: 'circle',
source: 'cities',
paint: {
'circle-radius': [
'interpolate', ['linear'], ['get', 'population'],
1000000, 5,
8000000, 20
],
'circle-color': '#3b82f6',
'circle-stroke-width': 1,
'circle-stroke-color': '#ffffff'
}
});
// Add hover effect to map points
let hoveredCityId = null;
map.on('mousemove', function(e) {
const features = map.queryRenderedFeatures(e.point, {
layers: ['cities-points']
});
// Change cursor style
map.getContainer().style.cursor = features.length ? 'pointer' : '';
if (features.length) {
const city = features[0].properties;
showTooltip(city, e);
} else {
hideTooltip();
}
});
// Add a simple legend using D3
const legendData = [
{ label: "Small", population: 1000000 },
{ label: "Medium", population: 4000000 },
{ label: "Large", population: 8000000 }
];
const legendSvg = d3.select("#info")
.append("div")
.attr("id", "legend")
.style("margin-top", "10px");
legendSvg.selectAll("circle")
.data(legendData)
.enter()
.append("circle")
.attr("r", d => Math.sqrt(d.population / 100000))
.attr("cx", (d, i) => i * 30 + 10)
.attr("cy", 20)
.attr("fill", "#3b82f6");
});
// Handle window resize
window.addEventListener('resize', function() {
map.resize();
});
</script>
</body>
</html>