mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-29 00:55:32 +00:00
296 lines
12 KiB
HTML
296 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Static SVG Map with Plotted Points</title>
|
|
<style>
|
|
body {
|
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
background-color: #f7fafc;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 2rem;
|
|
margin: 0;
|
|
color: #2d3748;
|
|
}
|
|
|
|
.header {
|
|
text-align: center;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 2.25rem;
|
|
font-weight: bold;
|
|
}
|
|
|
|
p {
|
|
color: #718096;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
/* 1. The main container needs a relative position */
|
|
.map-container {
|
|
position: relative;
|
|
/* This is the key for layering */
|
|
width: 100%;
|
|
max-width: 1000px;
|
|
border-radius: 0.75rem;
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
border: 1px solid #e2e8f0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.map-container img {
|
|
display: block;
|
|
/* Removes any bottom spacing */
|
|
width: 100%;
|
|
height: auto;
|
|
}
|
|
|
|
/* 2. The points container is layered directly on top */
|
|
.points-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
/* 3. Each point is a small, absolutely positioned div */
|
|
.map-point {
|
|
position: absolute;
|
|
width: 8px;
|
|
height: 8px;
|
|
background-color: #ef4444;
|
|
border: 1px solid #b91c1c;
|
|
border-radius: 50%;
|
|
/* The transform ensures the dot is centered on its coordinates */
|
|
transform: translate(-50%, -50%);
|
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div class="header">
|
|
<h1>Map of European Places</h1>
|
|
<p>A static projected SVG image with points layered on top.</p>
|
|
</div>
|
|
|
|
<div class="map-container">
|
|
<!-- The updated SVG map image -->
|
|
<img src="https://upload.wikimedia.org/wikipedia/commons/f/f7/Europe_laea_location_map.svg"
|
|
alt="Projected map of Europe">
|
|
|
|
<!-- This empty div will be populated with our points -->
|
|
<div id="points-container" class="points-container"></div>
|
|
</div>
|
|
|
|
<script>
|
|
const places = [
|
|
{"name": "Altdorf b.Nürnberg", "lat": "49.3875", "lng": "11.3572"},
|
|
{"name": "Altenburg", "lat": "50.98148", "lng": "12.43608"},
|
|
{"name": "Altona", "lat": "53.57358", "lng": "9.84241"},
|
|
{"name": "Altorf", "lat": "48.52222", "lng": "7.52833"},
|
|
{"name": "Amsterdam", "lat": "52.37403", "lng": "4.88969"},
|
|
{"name": "Ansbach", "lat": "49.3007", "lng": "10.5692"},
|
|
{"name": "Augsburg", "lat": "48.36733", "lng": "10.89213"},
|
|
{"name": "Aurich", "lat": "53.47187", "lng": "7.47753"},
|
|
{"name": "Bamberg", "lat": "49.89873", "lng": "10.90067"},
|
|
{"name": "Basel", "lat": "47.55773", "lng": "7.59361"},
|
|
{"name": "Bautzen/Budyšin", "lat": "51.18251", "lng": "14.4292"},
|
|
{"name": "Bayreuth", "lat": "49.9461", "lng": "11.57616"},
|
|
{"name": "Berlin", "lat": "52.52437", "lng": "13.41053"},
|
|
{"name": "Bernburg", "lat": "51.79464", "lng": "11.7401"},
|
|
{"name": "Bern", "lat": "46.94809", "lng": "7.44744"},
|
|
{"name": "Brandenburg an der Havel", "lat": "52.4189", "lng": "12.5228"},
|
|
{"name": "Braunschweig", "lat": "52.26471", "lng": "10.52333"},
|
|
{"name": "Bremen", "lat": "53.07582", "lng": "8.80717"},
|
|
{"name": "Wrocław", "lat": "51.1", "lng": "17.03333"},
|
|
{"name": "Bützow", "lat": "53.8173", "lng": "11.9992"},
|
|
{"name": "Celle", "lat": "52.61748", "lng": "10.08502"},
|
|
{"name": "Chemnitz", "lat": "50.83506", "lng": "12.92217"},
|
|
{"name": "Coburg", "lat": "50.25937", "lng": "10.96384"},
|
|
{"name": "Gdańsk", "lat": "54.35227", "lng": "18.64912"},
|
|
{"name": "The Hague", "lat": "52.07667", "lng": "4.29861"},
|
|
{"name": "Kreisfreie Stadt Dresden", "lat": "51.0833", "lng": "13.7666"},
|
|
{"name": "Eisenach", "lat": "50.97443", "lng": "10.33407"},
|
|
{"name": "Eisleben, Lutherstadt", "lat": "51.5258", "lng": "11.5345"},
|
|
{"name": "Erfurt", "lat": "50.97456", "lng": "11.02974"},
|
|
{"name": "Erlangen", "lat": "49.5888", "lng": "11.00977"},
|
|
{"name": "Flensburg", "lat": "54.78624", "lng": "9.43064"},
|
|
{"name": "Frankfurt am Main", "lat": "50.11035", "lng": "8.67185"},
|
|
{"name": "Frankfurt (Oder)", "lat": "52.34714", "lng": "14.55062"},
|
|
{"name": "Genève", "lat": "46.20576", "lng": "6.14161"},
|
|
{"name": "Gießen, Universitätsstadt", "lat": "50.58485", "lng": "8.67417"},
|
|
{"name": "Głogów", "lat": "51.66361", "lng": "16.0845"},
|
|
{"name": "Görlitz", "lat": "51.1503", "lng": "14.9829"},
|
|
{"name": "Göttingen", "lat": "51.51297", "lng": "9.95353"},
|
|
{"name": "Gotha", "lat": "50.94823", "lng": "10.70193"},
|
|
{"name": "Graz", "lat": "47.07493", "lng": "15.44089"},
|
|
{"name": "Greifswald", "lat": "54.08809", "lng": "13.38756"},
|
|
{"name": "Gemeente Haarlem", "lat": "52.38074", "lng": "4.644"},
|
|
{"name": "Halberstadt", "lat": "51.8883", "lng": "11.0572"},
|
|
{"name": "Halle (Saale)", "lat": "51.48158", "lng": "11.97947"},
|
|
{"name": "Hamburg", "lat": "53.55073", "lng": "9.99302"},
|
|
{"name": "Hamm", "lat": "51.68021", "lng": "7.81335"},
|
|
{"name": null, "lat": null, "lng": null},
|
|
{"name": "Hannover, Landeshauptstadt", "lat": "52.37362", "lng": "9.73711"},
|
|
{"name": "Aizpute", "lat": "56.72108", "lng": "21.60156"},
|
|
{"name": "Heilbronn", "lat": "49.1423", "lng": "9.22343"},
|
|
{"name": "Helmstedt", "lat": "52.2279", "lng": "11.00985"},
|
|
{"name": "Hildburghausen", "lat": "50.4265", "lng": "10.7259"},
|
|
{"name": "Hof", "lat": "50.32093", "lng": "11.9172"},
|
|
{"name": "Jena", "lat": "50.9326", "lng": "11.58678"},
|
|
{"name": "Karlsruhe", "lat": "49.0083", "lng": "8.39786"},
|
|
{"name": "Kassel", "lat": "51.31661", "lng": "9.49116"},
|
|
{"name": "Kaliningrad", "lat": "54.70649", "lng": "20.51095"},
|
|
{"name": "Kiel", "lat": "54.32133", "lng": "10.13489"},
|
|
{"name": "Kleve", "lat": "51.78655", "lng": "6.1375"},
|
|
{"name": "Köln", "lat": "50.93333", "lng": "6.95"},
|
|
{"name": "Copenhagen", "lat": "55.67594", "lng": "12.56553"},
|
|
{"name": "Bad Langensalza", "lat": "51.1111", "lng": "10.6542"},
|
|
{"name": "Lausanne", "lat": "46.52178", "lng": "6.633"},
|
|
{"name": "Leipzig", "lat": "51.33962", "lng": "12.37129"},
|
|
{"name": "Lemgo", "lat": "52.02768", "lng": "8.9043"},
|
|
{"name": "Legnica", "lat": "51.20473", "lng": "16.15834"},
|
|
{"name": "Lindau", "lat": "47.54612", "lng": "9.68431"},
|
|
{"name": "London", "lat": "51.49227", "lng": "-0.30864"},
|
|
{"name": "Lübeck, Hansestadt", "lat": "53.86893", "lng": "10.68729"},
|
|
{"name": "Lyon", "lat": "45.75889", "lng": "4.84139"},
|
|
{"name": "Magdeburg", "lat": "52.12773", "lng": "11.62916"},
|
|
{"name": "Mannheim", "lat": "49.4891", "lng": "8.46694"},
|
|
{"name": "Käärmetvaara", "lat": "65", "lng": "28.7"},
|
|
{"name": "Memmingen", "lat": "47.98257", "lng": "10.17254"},
|
|
{"name": "Minden", "lat": "52.28726", "lng": "8.92433"},
|
|
{"name": "Jelgava", "lat": "56.65", "lng": "23.71278"},
|
|
{"name": "Kreisfreie Stadt München", "lat": "48.15389", "lng": "11.54806"},
|
|
{"name": "Münster", "lat": "51.95973", "lng": "7.63137"},
|
|
{"name": "Neuchâtel", "lat": "46.99179", "lng": "6.931"},
|
|
{"name": "Nürnberg", "lat": "49.45421", "lng": "11.07752"},
|
|
{"name": "Odense Kommune", "lat": "55.3957", "lng": "10.37761"},
|
|
{"name": "Öhringen", "lat": "49.20117", "lng": "9.50217"},
|
|
{"name": "Paris", "lat": "48.8534", "lng": "2.3486"},
|
|
{"name": "Potsdam", "lat": "52.39886", "lng": "13.06566"},
|
|
{"name": "Prague", "lat": "50.08804", "lng": "14.42076"},
|
|
{"name": "Bratislava", "lat": "48.14816", "lng": "17.10674"},
|
|
{"name": "Pasłęk", "lat": "54.0611", "lng": "19.6668"},
|
|
{"name": "Quedlinburg", "lat": "51.79", "lng": "11.162"},
|
|
{"name": "Regensburg", "lat": "49.01681", "lng": "12.09536"},
|
|
{"name": "Rīga", "lat": "56.97778", "lng": "24.12167"},
|
|
{"name": "Rinteln", "lat": "52.1733", "lng": "9.11953"},
|
|
{"name": "Rostock", "lat": "54.0887", "lng": "12.14049"},
|
|
{"name": "Schleswig", "lat": "54.52021", "lng": "9.56829"},
|
|
{"name": "Schwabach", "lat": "49.60826", "lng": "10.99811"},
|
|
{"name": "Kreisfreie Stadt Schwerin", "lat": "53.63333", "lng": "11.41667"},
|
|
{"name": "Żary", "lat": "51.64205", "lng": "15.13727"},
|
|
{"name": "Stendal", "lat": "52.6052", "lng": "11.86043"},
|
|
{"name": "Szczecin", "lat": "53.42894", "lng": "14.55302"},
|
|
{"name": "Stockholm", "lat": "59.32938", "lng": "18.06871"},
|
|
{"name": "Sankt-Peterburg", "lat": "59.91667", "lng": "30.25"},
|
|
{"name": "Stralsund, Hansestadt", "lat": "54.30242", "lng": "13.09284"},
|
|
{"name": "Strasbourg", "lat": "48.58361", "lng": "7.74806"},
|
|
{"name": "Stuttgart", "lat": "48.78232", "lng": "9.17702"},
|
|
{"name": null, "lat": null, "lng": null},
|
|
{"name": "Ulm", "lat": "48.39841", "lng": "9.99155"},
|
|
{"name": "Warszawa", "lat": "52.2331", "lng": "21.0614"},
|
|
{"name": "Weimar", "lat": "50.98038", "lng": "11.3263"},
|
|
{"name": "Wien", "lat": "48.2082", "lng": "16.37169"},
|
|
{"name": "Wismar, Hansestadt", "lat": "53.9", "lng": "11.46667"},
|
|
{"name": "Wittenberg, Lutherstadt", "lat": "51.87437", "lng": "12.60603"},
|
|
{"name": "Wolfenbüttel", "lat": "52.1578", "lng": "10.5579"},
|
|
{"name": "Zelle", "lat": "51.15868", "lng": "4.77287"},
|
|
{"name": "Zerbst", "lat": "51.9598", "lng": "12.093"},
|
|
{"name": "Zittau", "lat": "50.9078", "lng": "14.8011"},
|
|
{"name": "Sulechów", "lat": "52.08362", "lng": "15.62513"},
|
|
{"name": "Bezirk Zürich", "lat": "47.3711", "lng": "8.54323"}
|
|
];
|
|
|
|
// The precise map extent in LAEA Europe (EPSG:3035) coordinates, in meters.
|
|
const MAP_EXTENT_METERS = {
|
|
xmin: 2555000,
|
|
ymin: 1350000,
|
|
xmax: 7405000,
|
|
ymax: 5500000
|
|
};
|
|
|
|
// Projection center data from the map's metadata.
|
|
const PROJECTION_CENTER = {
|
|
lon: 10, // 10° E
|
|
lat: 52 // 52° N
|
|
};
|
|
|
|
/**
|
|
* Converts latitude/longitude to a {x, y} percentage object using the precise
|
|
* LAEA Europe (EPSG:3035) projection formula and the map's exact extent.
|
|
* @param {number} lat The latitude of the point.
|
|
* @param {number} lng The longitude of the point.
|
|
* @returns {{x: number, y: number}|null} The x and y coordinates as percentages, or null if invalid.
|
|
*/
|
|
function convertLatLngToPercent(lat, lng) {
|
|
// Official projection parameters for EPSG:3035
|
|
const R = 6371000; // Earth radius approximation
|
|
const FE = 4321000; // False Easting
|
|
const FN = 3210000; // False Northing
|
|
|
|
// Convert projection center and point coordinates from degrees to radians
|
|
const lon_0_rad = PROJECTION_CENTER.lon * Math.PI / 180;
|
|
const lat_0_rad = PROJECTION_CENTER.lat * Math.PI / 180;
|
|
const lon_rad = lng * Math.PI / 180;
|
|
const lat_rad = lat * Math.PI / 180;
|
|
|
|
// Lambert Azimuthal Equal-Area projection formulas
|
|
const k_prime = Math.sqrt(2 / (1 + Math.sin(lat_0_rad) * Math.sin(lat_rad) + Math.cos(lat_0_rad) * Math.cos(lat_rad) * Math.cos(lon_rad - lon_0_rad)));
|
|
|
|
const x_proj = R * k_prime * Math.cos(lat_rad) * Math.sin(lon_rad - lon_0_rad);
|
|
const y_proj = R * k_prime * (Math.cos(lat_0_rad) * Math.sin(lat_rad) - Math.sin(lat_0_rad) * Math.cos(lat_rad) * Math.cos(lon_rad - lon_0_rad));
|
|
|
|
// Add false easting and northing to get the final projected coordinates in meters
|
|
const finalX = x_proj + FE;
|
|
const finalY = y_proj + FN;
|
|
|
|
// Now, convert the meter-based coordinates into a percentage of the map's extent
|
|
const mapWidthMeters = MAP_EXTENT_METERS.xmax - MAP_EXTENT_METERS.xmin;
|
|
const mapHeightMeters = MAP_EXTENT_METERS.ymax - MAP_EXTENT_METERS.ymin;
|
|
|
|
const xPercent = (finalX - MAP_EXTENT_METERS.xmin) / mapWidthMeters * 100;
|
|
// The Y-axis for CSS/SVG is inverted (0 is at the top), so we subtract from the max.
|
|
const yPercent = (MAP_EXTENT_METERS.ymax - finalY) / mapHeightMeters * 100;
|
|
|
|
return {x: xPercent, y: yPercent};
|
|
}
|
|
|
|
const pointsContainer = document.getElementById('points-container');
|
|
|
|
places.forEach(place => {
|
|
// Check for valid lat/lng and skip if null
|
|
if (place.lat && place.lng) {
|
|
const lat = parseFloat(place.lat);
|
|
const lng = parseFloat(place.lng);
|
|
|
|
// Convert the coordinates to percentages using the projection
|
|
const position = convertLatLngToPercent(lat, lng);
|
|
|
|
// Only draw points that are within the map's bounds
|
|
if (position.x >= 0 && position.x <= 100 && position.y >= 0 && position.y <= 100) {
|
|
const point = document.createElement('div');
|
|
point.className = 'map-point';
|
|
point.style.left = `${position.x}%`;
|
|
point.style.top = `${position.y}%`;
|
|
point.title = place.name; // Add a tooltip on hover
|
|
|
|
pointsContainer.appendChild(point);
|
|
}
|
|
}
|
|
});
|
|
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|