Add breaking changes and gencode

This commit is contained in:
Moises Leos
2025-08-27 02:00:32 -06:00
parent 58aae49391
commit 298faa23d6
7 changed files with 1034 additions and 14 deletions

View File

@@ -92,10 +92,23 @@
.attr("fill", "black")
.attr("color", "white");*/
lablels = getMarkerLabels(data);
// Call the update function
update();
// Update on map interaction
map.on("viewreset", update);
map.on("move", update);
map.on("moveend", update);
}
function getMarkerLabels(data){
// Add labels with background
const maxWidth = 100;
labels = svg.selectAll("g.label")
console.log("In markerlabels");
const markerLabels = svg.selectAll("g.label")
.data(data.features)
.enter()
.append("g")
@@ -166,14 +179,9 @@
g.attr("data-bbox-height", bbox.height);
});
// Call the update function
update();
// Update on map interaction
map.on("viewreset", update);
map.on("move", update);
map.on("moveend", update);
console.log("EIT");
console.log(markerLabels);
return markerLabels;
}
// Update d3 shapes' positions to the map's current state
@@ -196,15 +204,81 @@
//console.log(map.getZoom());
const labelNodes = labels.nodes();
const mergedGroups = [];
for(let i = 0; i < labelNodes.length; i++){
for(let j = i + 1; j < labelNodes.length; j++){
if(isOverlapping(labelNodes[i], labelNodes[j])){
console.log("OVERLAAAPPPP!!!");
console.log(labelNodes[i]);
console.log(labelNodes[j]);
const a = labelNodes[i];
let groupFound = false;
for (let group of mergedGroups) {
for (let j = 0; j < group.length; j++) {
if (isOverlapping(a, group[j])) {
console.log(a);
group.push(a);
groupFound = true;
break;
}
}
if (groupFound) break;
}
if (!groupFound) {
mergedGroups.push([a]);
}
}
// Step 2: Merge labels in overlapping groups
for (const group of mergedGroups) {
if (group.length === 1) continue;
// Gather merged text and coordinates
const names = group.map(el => el.querySelector("text").getAttribute("feature-name"));
const coords = group.map(el => {
const transform = el.getAttribute("transform");
const match = /translate\(([^,]+),\s*([^)]+)\)/.exec(transform);
return { x: +match[1], y: +match[2] };
});
// Average position
const avgX = coords.reduce((sum, c) => sum + c.x, 0) / coords.length;
const avgY = coords.reduce((sum, c) => sum + c.y, 0) / coords.length;
// Keep only the first label, update its text
const main = group[0];
const textEl = main.querySelector("text");
// Clear previous tspans or text
textEl.innerHTML = "";
// Create a new <tspan> per name, each on a new line
names.forEach((name, i) => {
const tspan = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
tspan.setAttribute("x", 0);
tspan.setAttribute("dy", i === 0 ? "0" : "1.2em"); // first line dy=0, next lines shift down
tspan.textContent = name;
textEl.appendChild(tspan);
});
main.setAttribute("transform", `translate(${avgX}, ${avgY})`);
// 3. Resize and reposition the <rect> background
const textBBox = textEl.getBBox();
const g = main; // the parent <g> element
const rect = g.querySelector("rect");
if (rect) {
rect.setAttribute("x", textBBox.x - 4);
rect.setAttribute("y", textBBox.y - 2);
rect.setAttribute("width", textBBox.width + 8);
rect.setAttribute("height", textBBox.height + 4);
}
// Optional: Update bbox height for offsetting in update()
g.setAttribute("data-bbox-height", textBBox.height);
// Hide others
for (let i = 1; i < group.length; i++) {
group[i].style.display = "none";
}
}
}

109
data/berlin-parks-new.json Normal file
View File

@@ -0,0 +1,109 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Interval [Start] Roll Out (last touch -> end of roll out)"
},
"geometry": {
"type": "Point",
"coordinates": [
13.40280532836914,
52.474311525275674
]
}
},
{
"type": "Feature",
"properties": {
"name": "Volskpark Friedrichshain"
},
"geometry": {
"type": "Point",
"coordinates": [
13.432846069335938,
52.527605775532116
]
}
},
{
"type": "Feature",
"properties": {
"name": "Großer Tiergarten"
},
"geometry": {
"type": "Point",
"coordinates": [
13.356628417968748,
52.51507176398948
]
}
},
{
"type": "Feature",
"properties": {
"name": "Park am Gleisdreieck"
},
"geometry": {
"type": "Point",
"coordinates": [
13.376026153564451,
52.49605502101946
]
}
},
{
"type": "Feature",
"properties": {
"name": "Volskpark Humboldthain"
},
"geometry": {
"type": "Point",
"coordinates": [
13.383750915527344,
52.54566934855766
]
}
},
{
"type": "Feature",
"properties": {
"name": "Görlitzer Park"
},
"geometry": {
"type": "Point",
"coordinates": [
13.435592651367188,
52.4976226461021
]
}
},
{
"type": "Feature",
"properties": {
"name": "Volkspark Prenzlazer Berg"
},
"geometry": {
"type": "Point",
"coordinates": [
13.46323013305664,
52.53700393747574
]
}
},
{
"type": "Feature",
"properties": {
"name": "Tierpark Berlin"
},
"geometry": {
"type": "Point",
"coordinates": [
13.528633117675781,
52.50347461927098
]
}
}
]
}

97
gemma.html Normal file
View File

@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mapbox GL JS + D3 Example</title>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css" rel="stylesheet" />
<script src="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js"></script>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { height: 1000px; width: 1600px; }
.map-overlay {
position: absolute;
top: 0;
left: 0;
pointer-events: none; /* Allow map interactions to pass through */
}
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1Ijoiam9yZGl0b3N0IiwiYSI6ImQtcVkyclEifQ.vwKrOGZoZSj3N-9MB6FF_A'; // Replace with your Mapbox access token
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://style/mapbox/light-v9',
center: [-74.0060, 40.7128], // New York City
zoom: 10
});
map.on('load', () => {
// Sample data (replace with your actual data)
const data = [
{ longitude: -74.0060, latitude: 40.7128, value: 10, label: "A" },
{ longitude: -73.9857, latitude: 40.7484, value: 20, label: "B" },
{ longitude: -73.9960, latitude: 40.7589, value: 15, label: "C" },
{ longitude: -73.9960, latitude: 40.7389, value: 25, label: "D"}
];
const canvas = map.getCanvas();
const svg = d3.select(canvas)
.append("svg")
.attr("class", "map-overlay")
.attr("width", canvas.width)
.attr("height", canvas.height);
// Create circles for each data point
const circles = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", (d) => map.project([d.longitude, d.latitude])[0])
.attr("cy", (d) => map.project([d.longitude, d.latitude])[1])
.attr("r", (d) => d.value / 5)
.attr("fill", "steelblue")
.attr("opacity", 0.7);
// Add labels to each circle
const labels = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("x", (d) => map.project([d.longitude, d.latitude])[0])
.attr("y", (d) => map.project([d.longitude, d.latitude])[1] - 10) // Adjust vertical position
.text((d) => d.label)
.attr("font-size", "12px")
.attr("font-family", "Arial")
.attr("fill", "white")
.attr("text-anchor", "middle");
// Update circle positions on map move
map.on('move', () => {
circles.each(function (d) {
const [x, y] = map.project([d.longitude, d.latitude]);
d3.select(this)
.attr("cx", x)
.attr("cy", y);
});
labels.each(function (d) {
const [x, y] = map.project([d.longitude, d.latitude]);
d3.select(this)
.attr("x", x)
.attr("y", y - 10);
});
});
});
</script>
</body>
</html>

78
gpttest.html Normal file
View File

@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Mapbox GL JS + D3.js Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://api.tiles.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.css" rel="stylesheet" />
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
/* SVG overlay on top of Mapbox canvas */
svg {
position: absolute;
top: 0;
left: 0;
pointer-events: none; /* so it doesn't block map interaction */
}
</style>
</head>
<body>
<div id="map"></div>
<script>
// 🔑 Replace with your own Mapbox access token
mapboxgl.accessToken = 'pk.eyJ1Ijoiam9yZGl0b3N0IiwiYSI6ImQtcVkyclEifQ.vwKrOGZoZSj3N-9MB6FF_A';
// Initialize Mapbox map
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v11',
center: [-122.4194, 37.7749], // San Francisco
zoom: 10
});
// Sample data: Some points in San Francisco
const data = [
{ name: 'Golden Gate Bridge', coords: [-122.4783, 37.8199] },
{ name: 'Alcatraz Island', coords: [-122.4231, 37.8267] },
{ name: 'Downtown SF', coords: [-122.4194, 37.7749] }
];
// Create SVG overlay once map is loaded
map.on('load', () => {
const container = map.getCanvasContainer();
const svg = d3.select(container).append('svg');
// Bind data to SVG circles
const circles = svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('r', 16)
.attr('fill', 'red');
/*.attr('stroke', 'white')
.attr('stroke-width', 2)
.attr('opacity', 0.8);*/
// Update function to project geo coords to screen coords
function updatePositions() {
circles
.attr('cx', d => map.project(d.coords).x)
.attr('cy', d => map.project(d.coords).y);
}
// Call update initially and on map move/zoom
updatePositions();
map.on('move', updatePositions);
map.on('zoom', updatePositions);
map.on('resize', updatePositions);
});
</script>
</body>
</html>

132
qwencoder-test.html Normal file
View File

@@ -0,0 +1,132 @@
<!DOCTYPE html >
<html>
<head>
<meta charset="utf-8" />
<title>mapboxgl.js + d3.js tutorial - 03</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<link href="https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.css" rel="stylesheet" />
<script src="https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style media="screen">
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
svg {
position: absolute;
width: 100%;
height: 100%;
}
.park-label {
font-family: Arial, sans-serif;
font-size: 12px;
fill: #333;
text-anchor: middle;
dominant-baseline: middle;
pointer-events: none;
opacity: 0.8;
}
.park-icon {
cursor: pointer;
transition: 0.3s opacity;
}
.park-icon:hover {
opacity: 1 !important;
}
</style>
</head>
<body>
<div id="map">
</div>
<script>
//////////////////
// Mapbox stuff
//////////////////
// Set-up map
mapboxgl.accessToken = 'pk.eyJ1Ijoiam9yZGl0b3N0IiwiYSI6ImQtcVkyclEifQ.vwKrOGZoZSj3N-9MB6FF_A';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/outdoors-v9',
zoom: 11.5,
center: [13.4426, 52.5100],
});
//////////////////////////
// Mapbox+D3 Connection
//////////////////////////
// Get Mapbox map canvas container
var canvas = map.getCanvasContainer();
// Overlay d3 on the map
var svg = d3.select(canvas).append("svg");
// Load map and dataset
map.on('load', function () {
d3.json("data/berlin-parks.json", function(err, data) {
drawData(data);
});
});
// Project GeoJSON coordinate to the map's current state
function project(d) {
return map.project(new mapboxgl.LngLat(+d[0], +d[1]));
}
//////////////
// D3 stuff
//////////////
// Draw GeoJSON data with d3
var parkElements;
function drawData(data) {
console.log("draw data");
// Add icons and labels
parkElements = svg.selectAll(".park-element")
.data(data.features)
.enter()
.append("g")
.attr("class", "park-element")
.attr("transform", function(d) {
var coord = project(d.geometry.coordinates);
return "translate(" + coord.x + "," + coord.y + ")";
})
.on("click", function(d) {
alert(d.properties.name);
});
// Add park icon (using a simple SVG path for a tree/leaf)
parkElements.append("path")
.attr("class", "park-icon")
.attr("d", "M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5z")
.attr("fill", "#4CAF50")
.attr("stroke", "#388E3C")
.attr("stroke-width", 1)
.attr("transform", "scale(0.7)")
.attr("opacity", 0.9);
// Add park name label
parkElements.append("text")
.attr("class", "park-label")
.text(function(d) {
return d.properties.name || "Unnamed Park";
})
.attr("dy", 25); // Position below the icon
// Call the update function
update();
// Update on map interaction
map.on("viewreset", update);
map.on("move", update);
map.on("moveend", update);
}
// Update d3 shapes' positions to the map's current state
function update() {
console.log("update");
svg.selectAll(".park-element")
.attr("transform", function(d) {
var coord = project(d.geometry.coordinates);
return "translate(" + coord.x + "," + coord.y + ")";
});
}
</script>
</body>
</html>

231
qwencoder.html Normal file
View File

@@ -0,0 +1,231 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mapbox GL JS + D3.js Integration</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);
}
</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>
<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");
// Add value labels
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")
.text(d => Math.round(d.population / 1000000) + "M");
}
// Create map markers with D3 processing
function createMarkers() {
const markers = [];
sampleData.forEach((city, index) => {
// Use D3 to process data for marker styling
const normalizedPopulation = city.population / maxPopulation;
// Create marker element
const el = document.createElement('div');
el.className = 'marker';
el.style.width = '20px';
el.style.height = '20px';
el.style.background = `rgba(59, 130, 246, ${normalizedPopulation})`;
el.style.borderRadius = '50%';
el.style.border = '2px solid white';
el.style.boxShadow = '0 0 5px rgba(0,0,0,0.5)';
// Add click interaction
el.addEventListener('click', () => {
alert(`${city.name}: ${city.population.toLocaleString()} people`);
});
// Create marker
const marker = new mapboxgl.Marker(el)
.setLngLat(city.coordinates)
.addTo(map);
markers.push(marker);
});
}
// Initialize everything when map is loaded
map.on('load', function () {
// Create D3 visualizations
createBarChart();
// Create map 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 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>

299
qwencoder2.html Normal file
View File

@@ -0,0 +1,299 @@
<!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>