Compare commits

..

2 Commits

Author SHA1 Message Date
gitmuhammedalbayrak
4ba4c5ae0f feat: Redesign interactive map with a historical theme and enhance Kubernetes deployment robustness.
All checks were successful
Harita Build ve Deploy / build-and-deploy (push) Successful in 22s
2025-11-21 00:32:43 +03:00
gitmuhammedalbayrak
5cc2ccad7b Scale yeniden update. 2025-11-20 18:57:28 +03:00
2 changed files with 480 additions and 219 deletions

View File

@@ -23,6 +23,21 @@ spec:
imagePullPolicy: Always imagePullPolicy: Always
ports: ports:
- containerPort: 80 - containerPort: 80
# Kaynak limitleri eklemek best-practice'dir
resources:
requests:
memory: "32Mi"
cpu: "10m"
limits:
memory: "128Mi"
cpu: "100m"
# Uygulamanın ayakta olup olmadığını kontrol eder
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service

View File

@@ -1,154 +1,350 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="tr"> <html lang="tr">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Doğu Yönelimli Dünya Haritası</title> <title>Doğu Yönelimli Dünya Haritası | Tarihsel</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js"></script>
<!-- Cinzel Decorative for titles, IM Fell English for body to give antique feel -->
<link
href="https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@400;700;900&family=IM+Fell+English:ital@0;1&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style> <style>
body { :root {
margin: 0; /* Historical / Parchment Theme */
padding: 20px; --bg-color: #f0e6d2;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; /* Parchment light */
background: #f5f5f5; --bg-texture: url('https://www.transparenttextures.com/patterns/aged-paper.png');
display: flex; /* Optional texture feel via color/noise */
flex-direction: column;
align-items: center; --ocean-fill: #d4e4e8;
/* Faded vintage blue */
--land-fill: #e8dcc5;
/* Paper/Land color */
--land-stroke: #8b7355;
/* Brown ink */
--text-primary: #2c1810;
/* Dark brown ink */
--text-secondary: #5d4037;
/* Lighter brown */
--accent-color: #8b0000;
/* Deep red for cities/compass */
--graticule-stroke: #b0a493;
} }
#map-container { body {
background: white; margin: 0;
border-radius: 8px; padding: 0;
box-shadow: 0 4px 6px rgba(0,0,0,0.1); font-family: 'IM Fell English', serif;
padding: 30px; background-color: var(--bg-color);
max-width: 1400px; /* Simple noise texture for paper effect */
width: 100%; background-image: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.2)), url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100' height='100' filter='url(%23noise)' opacity='0.08'/%3E%3C/svg%3E");
color: var(--text-primary);
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Header directly on background */
header {
position: absolute;
top: 20px;
left: 40px;
z-index: 10;
pointer-events: none;
/* Let clicks pass through to map if needed */
} }
h1 { h1 {
text-align: center; margin: 0;
color: #2c3e50; font-family: 'Cinzel Decorative', cursive;
margin: 0 0 10px 0; font-weight: 700;
font-size: 28px; font-size: 2.5rem;
color: var(--text-primary);
text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.5);
letter-spacing: 1px;
} }
.subtitle { .subtitle {
text-align: center; font-size: 1.1rem;
color: #7f8c8d; color: var(--text-secondary);
margin: 0 0 20px 0; font-style: italic;
font-size: 14px; margin-top: 5px;
border-top: 1px solid var(--text-secondary);
display: inline-block;
padding-top: 5px;
}
#map-wrapper {
width: 100vw;
height: 100vh;
position: relative;
overflow: hidden;
cursor: grab;
}
#map-wrapper:active {
cursor: grabbing;
} }
svg { svg {
width: 100%;
height: 100%;
display: block; display: block;
margin: 0 auto; }
/* Map Styles */
.ocean-bg {
fill: var(--ocean-fill);
opacity: 0.4;
} }
.country { .country {
fill: #e8f4f8; fill: var(--land-fill);
stroke: #34495e; stroke: var(--land-stroke);
stroke-width: 0.5; stroke-width: 0.5;
transition: fill 0.3s; transition: fill 0.3s ease;
} }
.country:hover { .country:hover {
fill: #b3d9e6; fill: #dccbb0;
} /* Slightly darker parchment */
stroke: #5d4037;
.ocean { stroke-width: 1;
fill: #a8d5e2;
} }
.graticule { .graticule {
fill: none; fill: none;
stroke: #ddd; stroke: var(--graticule-stroke);
stroke-width: 0.5; stroke-width: 0.5;
stroke-opacity: 0.5; stroke-dasharray: 4, 4;
opacity: 0.6;
} }
.city { .city {
fill: #e74c3c; fill: var(--accent-color);
stroke: #fff;
stroke-width: 0.5;
} }
/* Removed city-pulse as requested */
.city-label { .city-label {
font-size: 9px; font-family: 'IM Fell English', serif;
fill: #2c3e50; font-size: 12px;
font-weight: 600; fill: var(--text-primary);
font-weight: bold;
text-shadow:
-1px -1px 0 #f0e6d2,
1px -1px 0 #f0e6d2,
-1px 1px 0 #f0e6d2,
1px 1px 0 #f0e6d2;
/* Halo effect for readability */
pointer-events: none; pointer-events: none;
} }
.ocean-label { .ocean-label {
font-size: 16px; font-family: 'Cinzel Decorative', cursive;
fill: #2980b9; font-size: 18px;
font-weight: 600; fill: #6b8c96;
font-style: italic; /* Muted blue text */
opacity: 0.6; font-weight: 700;
} opacity: 0.7;
letter-spacing: 3px;
.compass { pointer-events: none;
font-size: 14px;
font-weight: bold;
fill: #34495e;
} }
.continent-label { .continent-label {
font-size: 14px; font-family: 'Cinzel Decorative', cursive;
fill: #34495e; font-size: 22px;
fill: #5d4037;
/* Brown */
font-weight: 900;
opacity: 0.8;
letter-spacing: 4px;
pointer-events: none;
text-transform: uppercase;
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8);
}
/* Controls - Antique Style */
.controls {
position: absolute;
bottom: 40px;
right: 40px;
display: flex;
flex-direction: column;
gap: 10px;
z-index: 100;
}
.btn {
width: 45px;
height: 45px;
border-radius: 50%;
border: 2px solid var(--land-stroke);
background: #fdfbf7;
color: var(--text-primary);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
font-size: 1.2rem;
}
.btn:hover {
background: #e8dcc5;
transform: translateY(-2px);
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.2);
}
/* Tooltip - Parchment Style */
#tooltip {
position: absolute;
padding: 8px 12px;
background: #fdfbf7;
border: 1px solid var(--land-stroke);
border-radius: 4px;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s;
font-family: 'IM Fell English', serif;
font-size: 1rem;
color: var(--text-primary);
box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);
z-index: 1000;
}
/* Compass - Antique Rose */
.compass-group text {
font-family: 'Cinzel Decorative', cursive;
font-weight: 700; font-weight: 700;
opacity: 0.7; font-size: 14px;
}
.loading-overlay {
position: absolute;
inset: 0;
background: var(--bg-color);
display: flex;
justify-content: center;
align-items: center;
z-index: 200;
transition: opacity 0.8s ease;
}
.loader-text {
font-family: 'Cinzel Decorative', cursive;
font-size: 2rem;
color: var(--text-primary);
animation: fadePulse 2s infinite;
}
@keyframes fadePulse {
0% {
opacity: 0.4;
}
50% {
opacity: 1;
}
100% {
opacity: 0.4;
}
} }
</style> </style>
</head> </head>
<body> <body>
<div id="map-container">
<h1>Gerçek Dünya Haritası - Doğu Yönelimli </h1> <div class="loading-overlay" id="loader">
<p class="subtitle">Doğu Üstte | Eğitim & Profesyonel Kullanım</p> <div class="loader-text">Harita Hazırlanıyor...</div>
</div>
<header>
<h1>Doğu Yönelimli Dünya Haritası</h1>
<div class="subtitle">Tarihsel Perspektif</div>
</header>
<div id="map-wrapper">
<div id="tooltip"></div>
<svg id="map"></svg> <svg id="map"></svg>
</div> </div>
<div class="controls">
<button class="btn" id="zoom-in" title="Yakınlaştır"><i class="fa-solid fa-plus"></i></button>
<button class="btn" id="zoom-out" title="Uzaklaştır"><i class="fa-solid fa-minus"></i></button>
<button class="btn" id="reset" title="Sıfırla"><i class="fa-solid fa-compress"></i></button>
</div>
<script> <script>
const width = 1000; const mapContainer = document.getElementById('map-wrapper');
const height = 900; const width = mapContainer.clientWidth;
const height = mapContainer.clientHeight;
const svg = d3.select("#map") const svg = d3.select("#map")
.attr("width", width) .attr("width", "100%")
.attr("height", height); .attr("height", "100%")
.attr("viewBox", [0, 0, width, height]);
// Standard projection - we'll rotate the entire SVG group // Background for Ocean (Full SVG)
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("class", "ocean-bg");
// Group for map content (to be zoomed/panned)
const g = svg.append("g");
// Zoom behavior
const zoom = d3.zoom()
.scaleExtent([1, 8])
.on("zoom", (event) => {
g.attr("transform", event.transform);
// Keep markers consistent size visually, but let them scale a bit for effect
// or keep them fixed. For historical map, fixed stroke width is good.
const k = event.transform.k;
g.selectAll(".country").attr("stroke-width", 0.5 / k);
g.selectAll(".city").attr("r", 3 / k);
g.selectAll(".city").attr("stroke-width", 0.5 / k);
// Scale text slightly but not fully, so it doesn't get huge or tiny
// Actually for readability, let's keep text size relatively constant
g.selectAll(".city-label").attr("font-size", 12 / k);
g.selectAll(".continent-label").attr("font-size", 22 / k);
g.selectAll(".ocean-label").attr("font-size", 18 / k);
});
svg.call(zoom);
// Projection
const projection = d3.geoNaturalEarth1() const projection = d3.geoNaturalEarth1()
.scale(240) .scale(width / 5.5)
.center([0, 0]) .center([0, 0])
.translate([width / 2, height / 2]); .translate([width / 2, height / 2]);
const path = d3.geoPath().projection(projection); const path = d3.geoPath().projection(projection);
// Create graticule // Tooltip
const graticule = d3.geoGraticule(); const tooltip = d3.select("#tooltip");
// Ocean background // Data
svg.append("rect")
.attr("class", "ocean")
.attr("width", width)
.attr("height", height);
// Create a group that will be rotated (rotate -90 for East at top)
const mapGroup = svg.append("g")
.attr("transform", `rotate(-90, ${width/2}, ${height/2})`);
// Add graticule
mapGroup.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// Major cities with coordinates [longitude, latitude]
const cities = [ const cities = [
{ name: "Tokyo", coords: [139.69, 35.69] }, { name: "Tokyo", coords: [139.69, 35.69] },
{ name: "Delhi", coords: [77.10, 28.70] }, { name: "Delhi", coords: [77.10, 28.70] },
{ name: "Şangay", coords: [121.47, 31.23] }, { name: "Şangay", coords: [121.47, 31.23] },
{ name: "São Paulo", coords: [-46.63, -23.55] }, { name: "São Paulo", coords: [-46.63, -23.55] },
{name: "Mexico City", coords: [-99.13, 19.43]}, { name: "Meksiko", coords: [-99.13, 19.43] },
{ name: "Kahire", coords: [31.24, 30.04] }, { name: "Kahire", coords: [31.24, 30.04] },
{ name: "Mumbai", coords: [72.88, 19.08] }, { name: "Mumbai", coords: [72.88, 19.08] },
{ name: "Pekin", coords: [116.41, 39.90] }, { name: "Pekin", coords: [116.41, 39.90] },
@@ -166,149 +362,199 @@
{ name: "Cape Town", coords: [18.42, -33.92] } { name: "Cape Town", coords: [18.42, -33.92] }
]; ];
// Ocean labels
const oceans = [ const oceans = [
{name: "PASİFİK OKYANUSU", coords: [180, 0]}, { name: "Pasifik Okyanusu", coords: [180, 0] },
{name: "PASİFİK OKYANUSU", coords: [-160, 0]}, { name: "Pasifik Okyanusu", coords: [-160, 0] },
{name: "ATLANTİK OKYANUSU", coords: [-30, 15]}, { name: "Atlantik Okyanusu", coords: [-30, 15] },
{name: "HİNT OKYANUSU", coords: [80, -15]}, { name: "Hint Okyanusu", coords: [80, -15] },
{name: "ARKTİK OKYANUSU", coords: [0, 80]} { name: "Arktik Okyanusu", coords: [0, 80] }
]; ];
// Continent labels
const continents = [ const continents = [
{name: "ASYA", coords: [95, 35]}, { name: "Asya", coords: [95, 35] },
{name: "AFRİKA", coords: [20, 5]}, { name: "Afrika", coords: [20, 5] },
{name: "AVRUPA", coords: [25, 50]}, { name: "Avrupa", coords: [25, 50] },
{name: "KUZEY AMERİKA", coords: [-100, 45]}, { name: "Kuzey Amerika", coords: [-100, 45] },
{name: "GÜNEY AMERİKA", coords: [-60, -15]}, { name: "Güney Amerika", coords: [-60, -15] },
{name: "AVUSTRALYA", coords: [135, -25]}, { name: "Avustralya", coords: [135, -25] },
{name: "ANTARKTİKA", coords: [25, -80]} { name: "Antarktika", coords: [25, -80] }
]; ];
// Load and display world map // Load Data
d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json") d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json")
.then(world => { .then(world => {
const countries = topojson.feature(world, world.objects.countries); // Remove loader
document.getElementById('loader').style.opacity = 0;
setTimeout(() => document.getElementById('loader').remove(), 800);
// Draw countries const countries = topojson.feature(world, world.objects.countries);
mapGroup.append("g") const graticule = d3.geoGraticule();
.selectAll("path")
// Rotate Group for East Orientation (-90 degrees)
const rotatedGroup = g.append("g")
.attr("transform", `rotate(-90, ${width / 2}, ${height / 2})`);
// Graticule (Lines)
rotatedGroup.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// Countries
rotatedGroup.selectAll("path.country")
.data(countries.features) .data(countries.features)
.enter() .enter().append("path")
.append("path")
.attr("class", "country") .attr("class", "country")
.attr("d", path) .attr("d", path)
.append("title") .on("mouseover", function (event, d) {
.text(d => d.properties.name); d3.select(this).style("fill", "#dccbb0"); // Highlight
tooltip.transition().duration(200).style("opacity", 1);
tooltip.html(d.properties.name)
.style("left", (event.pageX + 15) + "px")
.style("top", (event.pageY - 15) + "px");
})
.on("mousemove", function (event) {
tooltip
.style("left", (event.pageX + 15) + "px")
.style("top", (event.pageY - 15) + "px");
})
.on("mouseout", function () {
d3.select(this).style("fill", null); // Reset
tooltip.transition().duration(500).style("opacity", 0);
});
// Add continent labels // Labels helper function
const continentGroup = mapGroup.append("g"); // Added 'pointer-events: none' in CSS to ensure they don't block mouse events
continents.forEach(continent => { const addLabels = (data, className, fontSize) => {
const coords = projection(continent.coords); const group = rotatedGroup.append("g");
data.forEach(item => {
const coords = projection(item.coords);
if (coords) { if (coords) {
continentGroup.append("text") group.append("text")
.attr("class", "continent-label") .attr("class", className)
.attr("x", coords[0]) .attr("x", coords[0])
.attr("y", coords[1]) .attr("y", coords[1])
.attr("text-anchor", "middle") .attr("text-anchor", "middle")
.attr("transform", `rotate(90, ${coords[0]}, ${coords[1]})`) .attr("transform", `rotate(90, ${coords[0]}, ${coords[1]})`)
.text(continent.name); .text(item.name);
} }
}); });
};
// Add ocean labels // Add labels AFTER countries so they are on top
const oceanGroup = mapGroup.append("g"); addLabels(oceans, "ocean-label");
oceans.forEach(ocean => { addLabels(continents, "continent-label");
const coords = projection(ocean.coords);
if (coords) {
oceanGroup.append("text")
.attr("class", "ocean-label")
.attr("x", coords[0])
.attr("y", coords[1])
.attr("text-anchor", "middle")
.attr("transform", `rotate(90, ${coords[0]}, ${coords[1]})`)
.text(ocean.name);
}
});
// Add cities
const cityGroup = mapGroup.append("g");
// Cities
const cityGroup = rotatedGroup.append("g");
cities.forEach(city => { cities.forEach(city => {
const coords = projection(city.coords); const coords = projection(city.coords);
if (coords) { if (coords) {
// Main city dot
cityGroup.append("circle") cityGroup.append("circle")
.attr("class", "city") .attr("class", "city")
.attr("cx", coords[0]) .attr("cx", coords[0])
.attr("cy", coords[1]) .attr("cy", coords[1])
.attr("r", 2.5); .attr("r", 3)
.on("mouseover", (event) => {
tooltip.transition().duration(200).style("opacity", 1);
tooltip.html(`<strong>${city.name}</strong>`)
.style("left", (event.pageX + 15) + "px")
.style("top", (event.pageY - 15) + "px");
})
.on("mouseout", () => {
tooltip.transition().duration(500).style("opacity", 0);
});
// City Label
cityGroup.append("text") cityGroup.append("text")
.attr("class", "city-label") .attr("class", "city-label")
.attr("x", coords[0]) .attr("x", coords[0])
.attr("y", coords[1]) .attr("y", coords[1] - 8)
.attr("text-anchor", "middle") .attr("text-anchor", "middle")
.attr("transform", `rotate(90, ${coords[0]}, ${coords[1]})`) .attr("transform", `rotate(90, ${coords[0]}, ${coords[1]})`)
.text(city.name); .text(city.name);
} }
}); });
// Add compass rose (not rotated, stays in normal orientation) // Compass (Static, Antique Style)
// East (D) is UP.
const compassGroup = svg.append("g") const compassGroup = svg.append("g")
.attr("transform", `translate(${width - 70}, 60)`); .attr("class", "compass-group")
.attr("transform", `translate(${width - 80}, 80)`);
// Compass Rose Background
compassGroup.append("circle") compassGroup.append("circle")
.attr("r", 35) .attr("r", 40)
.attr("fill", "white") .attr("fill", "#fdfbf7")
.attr("stroke", "#34495e") .attr("stroke", "#8b7355")
.attr("stroke-width", 2); .attr("stroke-width", 2);
// East at top // Decorative inner circle
compassGroup.append("text") compassGroup.append("circle")
.attr("class", "compass") .attr("r", 35)
.attr("text-anchor", "middle") .attr("fill", "none")
.attr("y", -40) .attr("stroke", "#8b7355")
.attr("fill", "#e74c3c") .attr("stroke-width", 1)
.text("D"); .attr("stroke-dasharray", "2,2");
// West at bottom const directions = [
compassGroup.append("text") { text: "D", x: 0, y: -22, color: "#8b0000" }, // East Top
.attr("class", "compass") { text: "B", x: 0, y: 32, color: "#5d4037" }, // West Bottom
.attr("text-anchor", "middle") { text: "K", x: -28, y: 5, color: "#5d4037" }, // North Left
.attr("y", 45) { text: "G", x: 28, y: 5, color: "#5d4037" } // South Right
.text("B"); ];
// North at left directions.forEach(d => {
compassGroup.append("text") compassGroup.append("text")
.attr("class", "compass") .attr("x", d.x)
.attr("y", d.y)
.attr("text-anchor", "middle") .attr("text-anchor", "middle")
.attr("x", -40) .attr("fill", d.color)
.attr("y", 5) .text(d.text);
.text("K");
// South at right
compassGroup.append("text")
.attr("class", "compass")
.attr("text-anchor", "middle")
.attr("x", 40)
.attr("y", 5)
.text("G");
// Compass arrow
compassGroup.append("path")
.attr("d", "M 0,-25 L -4,-15 L 4,-15 Z")
.attr("fill", "#e74c3c");
})
.catch(error => {
console.error("Harita yüklenirken hata:", error);
svg.append("text")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("text-anchor", "middle")
.attr("fill", "#e74c3c")
.text("Harita yüklenirken hata oluştu. Lütfen sayfayı yenileyin.");
}); });
// Compass Arrow (Fleur-de-lis style simplified)
compassGroup.append("path")
.attr("d", "M 0,-20 L -5,-5 L 0,0 L 5,-5 Z")
.attr("fill", "#8b0000");
compassGroup.append("path")
.attr("d", "M 0,20 L -5,5 L 0,0 L 5,5 Z")
.attr("fill", "#8b7355");
compassGroup.append("path")
.attr("d", "M -20,0 L -5,-5 L 0,0 L -5,5 Z")
.attr("fill", "#8b7355");
compassGroup.append("path")
.attr("d", "M 20,0 L 5,-5 L 0,0 L 5,5 Z")
.attr("fill", "#8b7355");
})
.catch(err => console.error(err));
// Control Event Listeners
document.getElementById('zoom-in').addEventListener('click', () => {
svg.transition().duration(750).call(zoom.scaleBy, 1.5);
});
document.getElementById('zoom-out').addEventListener('click', () => {
svg.transition().duration(750).call(zoom.scaleBy, 0.6);
});
document.getElementById('reset').addEventListener('click', () => {
svg.transition().duration(750).call(zoom.transform, d3.zoomIdentity);
});
window.addEventListener('resize', () => {
const newWidth = mapContainer.clientWidth;
const newHeight = mapContainer.clientHeight;
svg.attr("viewBox", [0, 0, newWidth, newHeight]);
projection.translate([newWidth / 2, newHeight / 2]);
});
</script> </script>
</body> </body>
</html> </html>