map.js
const map = new maplibregl.Map({
container: 'map',
style: 'https://tiles.waymorphic.com/styles/positron',
center: [8, 53],
zoom: 2.9,
});
const cities = [
['London', 51.51, -0.13], ['Paris', 48.86, 2.35], ['Berlin', 52.52, 13.41], ['Madrid', 40.42, -3.70], ['Rome', 41.90, 12.50],
['Vienna', 48.21, 16.37], ['Warsaw', 52.23, 21.01], ['Stockholm', 59.33, 18.07], ['Oslo', 59.91, 10.75], ['Helsinki', 60.17, 24.94],
['Dublin', 53.35, -6.26], ['Lisbon', 38.72, -9.14], ['Athens', 37.98, 23.73], ['Istanbul', 41.01, 28.98], ['Kyiv', 50.45, 30.52],
['Moscow', 55.76, 37.62], ['Reykjavik', 64.15, -21.94], ['Zurich', 47.38, 8.54], ['Prague', 50.08, 14.44], ['Budapest', 47.50, 19.04],
];
map.on('load', async () => {
const url = 'https://api.open-meteo.com/v1/forecast?current=temperature_2m&latitude=' + cities.map(c => c[1]).join(',') + '&longitude=' + cities.map(c => c[2]).join(',');
const results = await (await fetch(url)).json();
const features = cities.map((c, i) => ({
type: 'Feature', geometry: { type: 'Point', coordinates: [c[2], c[1]] },
properties: { name: c[0], temp: results[i].current.temperature_2m },
}));
map.addSource('temps', { type: 'geojson', data: { type: 'FeatureCollection', features }, attribution: 'Weather data by Open-Meteo.com' });
map.addLayer({
id: 'temps', type: 'circle', source: 'temps',
paint: {
'circle-radius': 14,
'circle-color': ['interpolate', ['linear'], ['get', 'temp'], -10, '#1e3a8a', 0, '#60a5fa', 10, '#a7f3d0', 20, '#fde047', 30, '#f97316', 40, '#b91c1c'],
'circle-stroke-color': '#fff', 'circle-stroke-width': 2,
},
});
map.addLayer({
id: 'temp-labels', type: 'symbol', source: 'temps',
layout: { 'text-field': ['concat', ['to-string', ['round', ['get', 'temp']]], '°'], 'text-font': ['Noto Sans Bold'], 'text-size': 12, 'text-allow-overlap': true },
paint: { 'text-color': '#111' },
});
map.addLayer({
id: 'city-labels', type: 'symbol', source: 'temps',
layout: { 'text-field': ['get', 'name'], 'text-font': ['Noto Sans Regular'], 'text-size': 11, 'text-offset': [0, 1.5], 'text-anchor': 'top' },
paint: { 'text-halo-color': '#fff', 'text-halo-width': 1.5 },
});
});