Compare commits
7 Commits
627439bdce
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f4a00a63a6 | |||
|
|
7f1eabfe88 | ||
|
|
4ba4c5ae0f | ||
|
|
5cc2ccad7b | ||
|
|
2eb2099887 | ||
|
|
a20089462a | ||
|
|
f83eea8ce2 |
@@ -6,10 +6,8 @@ jobs:
|
|||||||
build-and-deploy:
|
build-and-deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# KRİTİK: DOCKER KOMUTUNUN YOLUNU GÖSTEREN AYAR
|
# ⚠️ ÇÖZÜM: DOCKER_HOST ENV AYARINI KALDIRIYORUZ!
|
||||||
env:
|
# Runner, kendi iç mekanizmasıyla otomatik TCP bağlantısını kuracaktır (Sizin stabil ayarınız).
|
||||||
DOCKER_HOST: tcp://localhost:2375
|
|
||||||
DOCKER_TLS_CERTDIR: "" # Sertifika kontrolünü kapatır
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Kodu Çek (Checkout)
|
- name: Kodu Çek (Checkout)
|
||||||
@@ -28,7 +26,7 @@ jobs:
|
|||||||
username: gitea_admin
|
username: gitea_admin
|
||||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
# PUSH ADIMI: Digest'i çıktı olarak alıyoruz
|
# PUSH ADIMI: İmajın kimliğini (digest) çıktı olarak alıyoruz
|
||||||
- name: Docker Build ve Push
|
- name: Docker Build ve Push
|
||||||
id: docker_build
|
id: docker_build
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
@@ -40,9 +38,10 @@ jobs:
|
|||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# CD KISMI (Final - TLS Kontrolü Atlandı)
|
# CD KISMI (Final Çözüm)
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
- name: Kubectl Binary Kurulumu
|
- name: Kubectl Binary Kurulumu
|
||||||
|
# ARM64 uyumlu kubectl'i kurar
|
||||||
run: |
|
run: |
|
||||||
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl"
|
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl"
|
||||||
chmod +x kubectl
|
chmod +x kubectl
|
||||||
@@ -56,11 +55,10 @@ jobs:
|
|||||||
# 2. Yeni imaj Digest'ini alır
|
# 2. Yeni imaj Digest'ini alır
|
||||||
NEW_IMAGE_DIGEST="git.konstantiniyye.studio/gitea_admin/dogu@${{ steps.docker_build.outputs.digest }}"
|
NEW_IMAGE_DIGEST="git.konstantiniyye.studio/gitea_admin/dogu@${{ steps.docker_build.outputs.digest }}"
|
||||||
|
|
||||||
# 3. TLS kontrolünü atlayarak Deployment'ı günceller (Sizin Kubeconfig'inizdeki ayarı kullanır)
|
# 3. TLS kontrolünü atlayarak Deployment'ı günceller (Rancher Self-Signed Fix)
|
||||||
|
# NOT: Kubeconfig'inize insecure-skip-tls-verify: true eklediyseniz, bu satırlar çalışacaktır.
|
||||||
kubectl set image deployment/dogu-haritasi web=${NEW_IMAGE_DIGEST} \
|
kubectl set image deployment/dogu-haritasi web=${NEW_IMAGE_DIGEST} \
|
||||||
--kubeconfig=/tmp/kubeconfig.yaml -n default \
|
--kubeconfig=/tmp/kubeconfig.yaml -n harita
|
||||||
--insecure-skip-tls-verify # <--- Gerekli TLS atlama bayrağı
|
|
||||||
|
|
||||||
kubectl rollout restart deployment/dogu-haritasi \
|
kubectl rollout restart deployment/dogu-haritasi \
|
||||||
--kubeconfig=/tmp/kubeconfig.yaml -n default \
|
--kubeconfig=/tmp/kubeconfig.yaml -n harita
|
||||||
--insecure-skip-tls-verify
|
|
||||||
2
.vscode/settings.json
vendored
Normal file
2
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: dogu-haritasi
|
name: dogu-haritasi
|
||||||
namespace: default
|
namespace: harita
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
@@ -23,12 +23,27 @@ 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
|
||||||
metadata:
|
metadata:
|
||||||
name: dogu-haritasi-service
|
name: dogu-haritasi-service
|
||||||
namespace: default
|
namespace: harita
|
||||||
spec:
|
spec:
|
||||||
# Service objesine secrets eklenmez, sadece Pod'u seçer
|
# Service objesine secrets eklenmez, sadece Pod'u seçer
|
||||||
selector:
|
selector:
|
||||||
@@ -42,7 +57,7 @@ apiVersion: networking.k8s.io/v1
|
|||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: dogu-haritasi-ingress
|
name: dogu-haritasi-ingress
|
||||||
namespace: default
|
namespace: harita
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: "traefik"
|
kubernetes.io/ingress.class: "traefik"
|
||||||
spec:
|
spec:
|
||||||
|
|||||||
599
index.html
599
index.html
@@ -1,155 +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 ve 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(200)
|
.scale(width / 5.5)
|
||||||
.center([0, 0])
|
.center([0, 0])
|
||||||
.clipExtent([[0, 0], [width, height]])
|
|
||||||
.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] },
|
||||||
@@ -167,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>
|
||||||
Reference in New Issue
Block a user