diff --git a/planet_visualization.js b/planet_visualization.js index 78d613d..c0f4d2a 100644 --- a/planet_visualization.js +++ b/planet_visualization.js @@ -130,126 +130,64 @@ function init2DVisualization() { .attr('width', width) .attr('height', height); - // Add a background rectangle representing the ocean - d3Svg.append('rect') - .attr('width', width) - .attr('height', height) - .attr('fill', '#a4d1e9'); // Ocean blue - - // Create projection (Mercator) similar to standard world maps + // Create projection d3Projection = d3.geoMercator() .scale(width / (2 * Math.PI) * 0.9) - .translate([width / 2, height / 1.8]); + .translate([width / 2, height / 2]); // Create path generator d3Path = d3.geoPath() .projection(d3Projection); - // Draw graticule (grid lines) - const graticule = d3.geoGraticule() - .step([15, 15]); // Grid every 15 degrees + // Add a background rectangle representing the ocean + d3Svg.append('rect') + .attr('width', width) + .attr('height', height) + .attr('fill', '#a4d1e9'); - d3Svg.append('path') - .datum(graticule) - .attr('class', 'graticule') - .attr('d', d3Path) - .style('fill', 'none') - .style('stroke', '#ffffff') - .style('stroke-width', '0.5px') - .style('opacity', 0.5); - - // Draw equator - const equator = { - type: "LineString", - coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]] - }; - - d3Svg.append('path') - .datum(equator) - .attr('class', 'equator') - .attr('d', d3Path) - .style('fill', 'none') - .style('stroke', '#00ffff') - .style('stroke-width', '2px'); - - // Draw prime meridian (0° longitude) - // const primeMeridian = { - // type: "LineString", - // coordinates: [[0, -90], [0, -45], [0, 0], [0, 45], [0, 90]] - // }; - - // d3Svg.append('path') - // .datum(primeMeridian) - // .attr('class', 'prime-meridian') - // .attr('d', d3Path) - // .style('fill', 'none') - // .style('stroke', '#ff0000') - // .style('stroke-width', '2px'); - - // Draw continental outlines (simplified) - // This just adds some landmass-like shapes to make it feel more Earth-like - const mockContinents = [ - { // North America (simplified) - type: "Polygon", - coordinates: [[ - [-140, 70], [-120, 60], [-100, 50], [-80, 30], - [-90, 20], [-100, 15], [-120, 30], [-130, 50], [-140, 70] - ]] - }, - { // South America (simplified) - type: "Polygon", - coordinates: [[ - [-80, 10], [-60, 0], [-50, -10], [-60, -30], - [-70, -50], [-80, -30], [-90, -10], [-80, 10] - ]] - }, - { // Europe/Africa (very simplified) - type: "Polygon", - coordinates: [[ - [0, 60], [20, 40], [30, 30], [20, 10], [10, 0], - [20, -10], [30, -30], [20, -40], [0, -30], [-10, 0], [0, 60] - ]] - }, - { // Asia (simplified) - type: "Polygon", - coordinates: [[ - [30, 60], [60, 70], [100, 60], [130, 40], [110, 20], - [100, 0], [80, 10], [60, 30], [40, 40], [30, 60] - ]] - }, - { // Australia (simplified) - type: "Polygon", - coordinates: [[ - [110, -20], [130, -25], [140, -35], [130, -40], - [120, -35], [110, -30], [110, -20] - ]] - } - ]; - - // Add continent outlines with very low opacity to provide visual reference - mockContinents.forEach(continent => { + // Load and draw actual world map data + d3.json('https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json').then(world => { + // Draw countries + const countries = topojson.feature(world, world.objects.countries); + + d3Svg.append('g') + .attr('class', 'countries') + .selectAll('path') + .data(countries.features) + .enter() + .append('path') + .attr('d', d3Path) + .attr('fill', '#d3b683') + .attr('stroke', '#a89070') + .attr('stroke-width', 0.5) + .attr('opacity', 0.8); + + // Add graticule (grid lines) + const graticule = d3.geoGraticule() + .step([15, 15]); + d3Svg.append('path') - .datum(continent) - .attr('class', 'continent-outline') - .attr('d', d3Path); - }); - - // Add labels for orientation - const labels = [ - { text: "North Pole", coords: [0, 85], anchor: "middle" }, - { text: "South Pole", coords: [0, -85], anchor: "middle" }, - { text: "Equator", coords: [-170, 0], anchor: "start" }, - //{ text: "Prime Meridian", coords: [5, 45], anchor: "start" } - ]; - - labels.forEach(label => { - const [x, y] = d3Projection(label.coords); - d3Svg.append('text') - .attr('x', x) - .attr('y', y) - .attr('class', 'map-label') - .attr('text-anchor', label.anchor) - .text(label.text); + .datum(graticule) + .attr('class', 'graticule') + .attr('d', d3Path) + .style('fill', 'none') + .style('stroke', '#ffffff') + .style('stroke-width', '0.5px') + .style('opacity', 0.3); + + // Draw equator + const equator = { + type: "LineString", + coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]] + }; + + d3Svg.append('path') + .datum(equator) + .attr('class', 'equator') + .attr('d', d3Path) + .style('fill', 'none') + .style('stroke', '#00ffff') + .style('stroke-width', '2px'); }); } @@ -346,11 +284,16 @@ function createD3Map(points) { // Only draw the point if it projects properly const projected = d3Projection([lon, lat]); if (projected) { + // Calculate scale factor based on latitude to mimic Mercator distortion + // 1/cos(latitude in radians) gives us the characteristic Mercator scaling + const latRad = lat * Math.PI / 180; + const scaleFactor = Math.min(3, 1 / Math.cos(latRad)); // Cap at 3x to avoid extreme sizes + pointsGroup.append('circle') .attr('class', 'planet-point') .attr('cx', projected[0]) .attr('cy', projected[1]) - .attr('r', 1.5) + .attr('r', 1.5 * scaleFactor) // Scale radius by the Mercator factor .style('fill', `rgb(${point.color[0] * 255}, ${point.color[1] * 255}, ${point.color[2] * 255})`) .style('opacity', 0.8); }