From c29d2206321c33ce4f1831668d29633d8f55ca65 Mon Sep 17 00:00:00 2001 From: Lisyonok04 <125237397+Lisyonok04@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:14:52 +0400 Subject: [PATCH 1/7] Add files via upload --- cities.json | 176 ++++++++++++++++++++++++++++++++ docker-compose.yaml | 8 ++ index.html | 93 +++++++++++++++++ script.js | 242 ++++++++++++++++++++++++++++++++++++++++++++ styles.css | 189 ++++++++++++++++++++++++++++++++++ 5 files changed, 708 insertions(+) create mode 100644 cities.json create mode 100644 docker-compose.yaml create mode 100644 index.html create mode 100644 script.js create mode 100644 styles.css diff --git a/cities.json b/cities.json new file mode 100644 index 00000000..80b04dfd --- /dev/null +++ b/cities.json @@ -0,0 +1,176 @@ +[ + { "name": "Москва", "lat": 55.7558, "lon": 37.6173, "population": 12655050 }, + { "name": "Санкт-Петербург", "lat": 59.9343, "lon": 30.3351, "population": 5384342 }, + { "name": "Новосибирск", "lat": 55.0084, "lon": 82.9357, "population": 1625631 }, + { "name": "Екатеринбург", "lat": 56.8389, "lon": 60.6057, "population": 1493749 }, + { "name": "Казань", "lat": 55.7961, "lon": 49.1064, "population": 1257391 }, + { "name": "Нижний Новгород", "lat": 56.2965, "lon": 43.9361, "population": 1252236 }, + { "name": "Челябинск", "lat": 55.1644, "lon": 61.4368, "population": 1196680 }, + { "name": "Самара", "lat": 53.1952, "lon": 50.1069, "population": 1156644 }, + { "name": "Омск", "lat": 54.9885, "lon": 73.3242, "population": 1154116 }, + { "name": "Ростов-на-Дону", "lat": 47.2357, "lon": 39.7015, "population": 1137904 }, + { "name": "Уфа", "lat": 54.7388, "lon": 55.9721, "population": 1128787 }, + { "name": "Красноярск", "lat": 56.0153, "lon": 92.8932, "population": 1093771 }, + { "name": "Воронеж", "lat": 51.6720, "lon": 39.1843, "population": 1058261 }, + { "name": "Пермь", "lat": 58.0105, "lon": 56.2502, "population": 1055397 }, + { "name": "Волгоград", "lat": 48.7080, "lon": 44.5133, "population": 1008998 }, + { "name": "Краснодар", "lat": 45.0355, "lon": 38.9753, "population": 948827 }, + { "name": "Саратов", "lat": 51.5924, "lon": 46.0348, "population": 901361 }, + { "name": "Тюмень", "lat": 57.1522, "lon": 65.5272, "population": 847488 }, + { "name": "Тольятти", "lat": 53.5303, "lon": 49.3461, "population": 699631 }, + { "name": "Ижевск", "lat": 56.8527, "lon": 53.2041, "population": 648944 }, + { "name": "Барнаул", "lat": 53.3606, "lon": 83.7636, "population": 630877 }, + { "name": "Ульяновск", "lat": 54.3141, "lon": 48.4031, "population": 623013 }, + { "name": "Иркутск", "lat": 52.2870, "lon": 104.2805, "population": 623562 }, + { "name": "Хабаровск", "lat": 48.4827, "lon": 135.0838, "population": 616372 }, + { "name": "Ярославль", "lat": 57.6261, "lon": 39.8845, "population": 608079 }, + { "name": "Владивосток", "lat": 43.1056, "lon": 131.8735, "population": 606589 }, + { "name": "Махачкала", "lat": 42.9849, "lon": 47.5047, "population": 603518 }, + { "name": "Томск", "lat": 56.4977, "lon": 84.9744, "population": 576624 }, + { "name": "Оренбург", "lat": 51.7727, "lon": 55.0988, "population": 572188 }, + { "name": "Кемерово", "lat": 55.3331, "lon": 86.0831, "population": 558973 }, + { "name": "Новокузнецк", "lat": 53.7557, "lon": 87.1099, "population": 547904 }, + { "name": "Рязань", "lat": 54.6269, "lon": 39.6916, "population": 539290 }, + { "name": "Астрахань", "lat": 46.3497, "lon": 48.0408, "population": 529793 }, + { "name": "Набережные Челны", "lat": 55.7251, "lon": 52.4069, "population": 533907 }, + { "name": "Пенза", "lat": 53.2001, "lon": 45.0000, "population": 520300 }, + { "name": "Липецк", "lat": 52.6031, "lon": 39.5708, "population": 508887 }, + { "name": "Киров", "lat": 58.6035, "lon": 49.6679, "population": 507155 }, + { "name": "Чебоксары", "lat": 56.1439, "lon": 47.2486, "population": 497266 }, + { "name": "Тула", "lat": 54.1931, "lon": 37.6172, "population": 475161 }, + { "name": "Калининград", "lat": 54.7104, "lon": 20.4522, "population": 482443 }, + { "name": "Балашиха", "lat": 55.7964, "lon": 37.9378, "population": 507692 }, + { "name": "Ставрополь", "lat": 45.0448, "lon": 41.9690, "population": 458784 }, + { "name": "Севастополь", "lat": 44.6167, "lon": 33.5254, "population": 449130 }, + { "name": "Улан-Удэ", "lat": 51.8272, "lon": 107.6063, "population": 439128 }, + { "name": "Тверь", "lat": 56.8584, "lon": 35.9176, "population": 424969 }, + { "name": "Магнитогорск", "lat": 53.4078, "lon": 59.0464, "population": 413351 }, + { "name": "Иваново", "lat": 56.9719, "lon": 40.9714, "population": 404300 }, + { "name": "Брянск", "lat": 53.2434, "lon": 34.3656, "population": 402300 }, + { "name": "Сочи", "lat": 43.6028, "lon": 39.7342, "population": 443562 }, + { "name": "Белгород", "lat": 50.5956, "lon": 36.5871, "population": 391554 }, + { "name": "Сургут", "lat": 61.2500, "lon": 73.4167, "population": 395705 }, + { "name": "Владимир", "lat": 56.1366, "lon": 40.3966, "population": 349951 }, + { "name": "Архангельск", "lat": 64.5401, "lon": 40.5433, "population": 348783 }, + { "name": "Нижний Тагил", "lat": 57.9197, "lon": 59.9650, "population": 355694 }, + { "name": "Чита", "lat": 52.0333, "lon": 113.5000, "population": 349005 }, + { "name": "Калуга", "lat": 54.5293, "lon": 36.2754, "population": 336726 }, + { "name": "Смоленск", "lat": 54.7818, "lon": 32.0401, "population": 326863 }, + { "name": "Курган", "lat": 55.4500, "lon": 65.3333, "population": 310951 }, + { "name": "Волжский", "lat": 48.7854, "lon": 44.7758, "population": 323293 }, + { "name": "Орёл", "lat": 52.9651, "lon": 36.0785, "population": 310755 }, + { "name": "Череповец", "lat": 59.1333, "lon": 37.9000, "population": 315744 }, + { "name": "Грозный", "lat": 43.3175, "lon": 45.6986, "population": 324370 }, + { "name": "Якутск", "lat": 62.0355, "lon": 129.6755, "population": 318457 }, + { "name": "Мурманск", "lat": 68.9585, "lon": 33.0827, "population": 295374 }, + { "name": "Петрозаводск", "lat": 61.7849, "lon": 34.3469, "population": 280662 }, + { "name": "Саранск", "lat": 54.1838, "lon": 45.1749, "population": 314789 }, + { "name": "Кострома", "lat": 57.7665, "lon": 40.9265, "population": 276662 }, + { "name": "Таганрог", "lat": 47.2362, "lon": 38.9349, "population": 245120 }, + { "name": "Комсомольск-на-Амуре", "lat": 50.5500, "lon": 137.0000, "population": 248280 }, + { "name": "Сыктывкар", "lat": 61.6681, "lon": 50.8357, "population": 235006 }, + { "name": "Нижневартовск", "lat": 60.9344, "lon": 76.5531, "population": 279988 }, + { "name": "Йошкар-Ола", "lat": 56.6372, "lon": 47.8908, "population": 279100 }, + { "name": "Новороссийск", "lat": 44.7231, "lon": 37.7686, "population": 261430 }, + { "name": "Дзержинск", "lat": 56.2333, "lon": 43.4667, "population": 230000 }, + { "name": "Нальчик", "lat": 43.4981, "lon": 43.6189, "population": 239300 }, + { "name": "Шахты", "lat": 47.7000, "lon": 40.2167, "population": 221845 }, + { "name": "Энгельс", "lat": 51.4833, "lon": 46.1167, "population": 222918 }, + { "name": "Рыбинск", "lat": 58.0500, "lon": 38.8500, "population": 182411 }, + { "name": "Норильск", "lat": 69.3558, "lon": 88.1893, "population": 175365 }, + { "name": "Альметьевск", "lat": 54.9000, "lon": 52.3000, "population": 155329 }, + { "name": "Псков", "lat": 57.8136, "lon": 28.3300, "population": 210501 }, + { "name": "Биийск", "lat": 52.5333, "lon": 85.3333, "population": 197164 }, + { "name": "Люберцы", "lat": 55.6758, "lon": 37.8933, "population": 201654 }, + { "name": "Прокопьевск", "lat": 53.9000, "lon": 86.7167, "population": 197164 }, + { "name": "Мытищи", "lat": 55.9116, "lon": 37.7307, "population": 223067 }, + { "name": "Златоуст", "lat": 55.1667, "lon": 59.6500, "population": 163367 }, + { "name": "Каменск-Уральский", "lat": 56.4167, "lon": 61.9333, "population": 168406 }, + { "name": "Подольск", "lat": 55.4297, "lon": 37.5447, "population": 187961 }, + { "name": "Петропавловск-Камчатский", "lat": 53.0446, "lon": 158.6504, "population": 179526 }, + { "name": "Сызрань", "lat": 53.1667, "lon": 48.4667, "population": 163913 }, + { "name": "Ачинск", "lat": 56.2667, "lon": 90.5000, "population": 107656 }, + { "name": "Новочеркасск", "lat": 47.4167, "lon": 40.1000, "population": 168746 }, + { "name": "Электросталь", "lat": 55.7894, "lon": 38.4469, "population": 158652 }, + { "name": "Первоуральск", "lat": 56.9000, "lon": 59.9500, "population": 123211 }, + { "name": "Одинцово", "lat": 55.6758, "lon": 37.2808, "population": 137041 }, + { "name": "Копейск", "lat": 55.1167, "lon": 61.6167, "population": 148872 }, + { "name": "Хасавюрт", "lat": 43.2500, "lon": 46.5833, "population": 143960 }, + { "name": "Нефтекамск", "lat": 56.0833, "lon": 54.2667, "population": 133997 }, + { "name": "Новочебоксарск", "lat": 56.1167, "lon": 47.5000, "population": 129082 }, + { "name": "Серпухов", "lat": 54.9167, "lon": 37.4167, "population": 126522 }, + { "name": "Невинномысск", "lat": 44.6333, "lon": 41.9333, "population": 118360 }, + { "name": "Дмитров", "lat": 56.3500, "lon": 37.5167, "population": 61305 }, + { "name": "Обнинск", "lat": 55.0833, "lon": 36.6167, "population": 120000 }, + { "name": "Ангарск", "lat": 52.5333, "lon": 103.9000, "population": 225400 }, + { "name": "Щёлково", "lat": 55.9167, "lon": 38.0333, "population": 124115 }, + { "name": "Батайск", "lat": 47.1333, "lon": 39.7500, "population": 123549 }, + { "name": "Кисловодск", "lat": 43.9000, "lon": 42.7167, "population": 128553 }, + { "name": "Орехово-Зуево", "lat": 55.8000, "lon": 38.9667, "population": 120670 }, + { "name": "Майкоп", "lat": 44.6000, "lon": 40.1000, "population": 144246 }, + { "name": "Пушкино", "lat": 56.0167, "lon": 37.8500, "population": 111000 }, + { "name": "Армавир", "lat": 44.9833, "lon": 41.1167, "population": 188832 }, + { "name": "Людино", "lat": 54.7000, "lon": 34.4500, "population": 77000 }, + { "name": "Кызыл", "lat": 51.7167, "lon": 94.4500, "population": 119633 }, + { "name": "Уссурийск", "lat": 43.8000, "lon": 131.9500, "population": 158016 }, + { "name": "Березники", "lat": 59.4167, "lon": 56.8167, "population": 141762 }, + { "name": "Салават", "lat": 53.3667, "lon": 55.9167, "population": 151100 }, + { "name": "Миасс", "lat": 55.0333, "lon": 60.1000, "population": 151751 }, + { "name": "Абакан", "lat": 53.7167, "lon": 91.4333, "population": 181700 }, + { "name": "Ноябрьск", "lat": 63.2000, "lon": 75.4500, "population": 110620 }, + { "name": "Ессентуки", "lat": 44.0500, "lon": 42.8667, "population": 111600 }, + { "name": "Железногорск", "lat": 56.2500, "lon": 93.5333, "population": 93734 }, + { "name": "Красногорск", "lat": 55.7333, "lon": 37.3333, "population": 179683 }, + { "name": "Новый Уренгой", "lat": 66.0833, "lon": 76.6333, "population": 118619 }, + { "name": "Муром", "lat": 55.5667, "lon": 42.0500, "population": 106200 }, + { "name": "Коломна", "lat": 55.0833, "lon": 38.7833, "population": 144589 }, + { "name": "Ковров", "lat": 56.3500, "lon": 41.3167, "population": 137111 }, + { "name": "Пятигорск", "lat": 44.0500, "lon": 43.0500, "population": 149696 }, + { "name": "Химки", "lat": 55.8972, "lon": 37.4297, "population": 259550 }, + { "name": "Троицк", "lat": 55.4833, "lon": 37.3000, "population": 61519 }, + { "name": "Реутов", "lat": 55.7667, "lon": 37.8667, "population": 107022 }, + { "name": "Долгопрудный", "lat": 55.9333, "lon": 37.5167, "population": 123000 }, + { "name": "Раменское", "lat": 55.5667, "lon": 38.2333, "population": 122000 }, + { "name": "Жуковский", "lat": 55.6000, "lon": 38.1167, "population": 107585 }, + { "name": "Лобня", "lat": 56.0167, "lon": 37.4833, "population": 87000 }, + { "name": "Клин", "lat": 56.3333, "lon": 36.7333, "population": 78000 }, + { "name": "Сергиев Посад", "lat": 56.3000, "lon": 38.1333, "population": 104000 }, + { "name": "Ногинск", "lat": 55.8667, "lon": 38.4333, "population": 101000 }, + { "name": "Егорьевск", "lat": 55.3833, "lon": 39.0333, "population": 70000 }, + { "name": "Ступино", "lat": 55.2833, "lon": 38.0833, "population": 66000 }, + { "name": "Воскресенск", "lat": 55.3167, "lon": 38.6667, "population": 91000 }, + { "name": "Дубна", "lat": 56.7333, "lon": 37.1667, "population": 75000 }, + { "name": "Фрязино", "lat": 55.9500, "lon": 38.0500, "population": 57000 }, + { "name": "Домодедово", "lat": 55.4333, "lon": 37.7667, "population": 125000 }, + { "name": "Истра", "lat": 55.9167, "lon": 36.8500, "population": 35000 }, + { "name": "Чехов", "lat": 55.1500, "lon": 37.4667, "population": 75000 }, + { "name": "Наро-Фоминск", "lat": 55.3833, "lon": 36.7333, "population": 64000 }, + { "name": "Павловский Посад", "lat": 55.7833, "lon": 38.6500, "population": 61000 }, + { "name": "Звенигород", "lat": 55.7333, "lon": 36.8500, "population": 18000 }, + { "name": "Руза", "lat": 55.7000, "lon": 36.1833, "population": 13000 }, + { "name": "Верея", "lat": 55.3500, "lon": 36.2000, "population": 12000 }, + { "name": "Можайск", "lat": 55.5000, "lon": 36.0333, "population": 30000 }, + { "name": "Волоколамск", "lat": 56.0333, "lon": 35.9500, "population": 21000 }, + { "name": "Шаховская", "lat": 56.0167, "lon": 35.5333, "population": 11000 }, + { "name": "Лотошино", "lat": 56.0833, "lon": 35.8000, "population": 8000 }, + { "name": "Талдом", "lat": 56.7333, "lon": 37.5333, "population": 13000 }, + { "name": "Яхрома", "lat": 56.2833, "lon": 37.4833, "population": 13000 }, + { "name": "Краснозаводск", "lat": 56.3167, "lon": 37.9000, "population": 13000 }, + { "name": "Пересвет", "lat": 56.2833, "lon": 38.1667, "population": 5000 }, + { "name": "Апрелевка", "lat": 55.4833, "lon": 37.2000, "population": 23000 }, + { "name": "Щербинка", "lat": 55.5000, "lon": 37.5667, "population": 28000 }, + { "name": "Московский", "lat": 55.6000, "lon": 37.3500, "population": 60000 }, + { "name": "Кокошкино", "lat": 55.6167, "lon": 37.2500, "population": 14000 }, + { "name": "Кремёнки", "lat": 55.3833, "lon": 37.4500, "population": 8000 }, + { "name": "Протвино", "lat": 54.8667, "lon": 37.2333, "population": 13000 }, + { "name": "Пущино", "lat": 54.8333, "lon": 37.6167, "population": 21000 }, + { "name": "Молодёжный", "lat": 55.5667, "lon": 37.2167, "population": 9000 }, + { "name": "Внуково", "lat": 55.6000, "lon": 37.3500, "population": 8000 }, + { "name": "Михайловское", "lat": 55.5500, "lon": 37.3000, "population": 7000 }, + { "name": "Филимонки", "lat": 55.5667, "lon": 37.3833, "population": 6000 }, + { "name": "Марушкино", "lat": 55.6167, "lon": 37.2833, "population": 5000 }, + { "name": "Румянцево", "lat": 55.6333, "lon": 37.3833, "population": 4000 }, + { "name": "Сосенки", "lat": 55.5833, "lon": 37.4167, "population": 3000 }, + { "name": "Газопровод", "lat": 55.6000, "lon": 37.4000, "population": 2000 }, + { "name": "Коммунарка", "lat": 55.5667, "lon": 37.5167, "population": 25000 } +] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..9c09c919 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,8 @@ +version: '3.8' +services: + web: + image: nginx:alpine + ports: + - "8080:80" + volumes: + - ./:/usr/share/nginx/html:ro diff --git a/index.html b/index.html new file mode 100644 index 00000000..cb4d0883 --- /dev/null +++ b/index.html @@ -0,0 +1,93 @@ + + + + + + Прогноз погоды на карте + + + + + + + + + + + + +
+ +
+
+
+ + +
+
+

🔍 Найти населённый пункт

+ +
+ ⚡ Кликните по маркеру на карте или найдите город через поиск +
+
+ +
+
🌤️ Выберите город на карте
+
+ +
+
+

🌡️ Температура (°C)

+ +
+
+

🌧️ Осадки (мм/сутки)

+ +
+
+

💨 Ветер (м/с)

+ +
+
+ + +
+
+ + + + + + + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 00000000..1dbefdaf --- /dev/null +++ b/script.js @@ -0,0 +1,242 @@ +// script.js – улучшенная версия с отладкой + +// ------------------- 1. ИНИЦИАЛИЗАЦИЯ КАРТЫ ------------------- +let map = L.map('map').setView([64.0, 100.0], 4); +L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { + attribution: '© OSM' +}).addTo(map); + +// ------------------- 2. ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ------------------- +let cities = []; // массив городов в формате {name, latitude, longitude} +let markers = {}; // объект для быстрого доступа к маркерам по имени +let activePopup = null; // для закрытия предыдущего попапа + +// ------------------- 3. ЗАГРУЗКА ГОРОДОВ (с fallback) ------------------- +async function loadCities() { + console.log("Загрузка данных о городах..."); + + // Пытаемся загрузить russia-cities.json + let data = null; + try { + const response = await fetch('russia-cities.json'); + if (response.ok) { + data = await response.json(); + console.log(`✅ Загружен russia-cities.json, количество записей: ${data.length}`); + } else { + console.warn("russia-cities.json не найден, пробуем cities.json"); + } + } catch (e) { + console.warn("Ошибка загрузки russia-cities.json", e); + } + + // Если не загрузился, пробуем cities.json (простой формат) + if (!data) { + try { + const response = await fetch('cities.json'); + if (response.ok) { + data = await response.json(); + console.log(`✅ Загружен cities.json, количество записей: ${data.length}`); + } + } catch (e) { + console.warn("cities.json не найден"); + } + } + + // Если данные есть – преобразуем в единый формат + if (data && Array.isArray(data)) { + // Определяем формат: если первый объект имеет поля coords.lat, то это формат russia-cities + if (data[0] && data[0].coords && typeof data[0].coords.lat === 'number') { + cities = data.map(city => ({ + name: city.name, + latitude: city.coords.lat, + longitude: city.coords.lon, + population: city.population + })); + console.log("✅ Преобразован формат russia-cities.json"); + } + // Если уже есть поля name, latitude, longitude + // СТАЛО + else if (data[0] && typeof data[0].lat === 'number' && typeof data[0].lon === 'number') { + cities = data.map(city => ({ + name: city.name, + latitude: city.lat, + longitude: city.lon, + population: city.population + })); + console.log("✅ Используется формат {name, lat, lon}"); + } +// Если есть поля latitude/longitude + else if (data[0] && typeof data[0].latitude === 'number' && typeof data[0].longitude === 'number') { + cities = data; + console.log("✅ Используется формат {name, latitude, longitude}"); + } + else { + console.error("❌ Неизвестный формат JSON. Ожидается массив с name/latitude/longitude или name/coords.lat/lon"); + cities = getFallbackCities(); + } + } else { + console.warn("⚠️ Нет загруженных данных, используем встроенный список из 5 городов"); + cities = getFallbackCities(); + } + + console.log(`Итоговое количество городов для отображения: ${cities.length}`); + addMarkersToMap(); +} + +// Встроенный список на случай, если JSON не загрузился (чтобы вы сразу увидели маркеры) +function getFallbackCities() { + return [ + { name: "Москва", latitude: 55.751244, longitude: 37.618423 }, + { name: "Санкт-Петербург", latitude: 59.931058, longitude: 30.360909 }, + { name: "Новосибирск", latitude: 55.030199, longitude: 82.920430 }, + { name: "Екатеринбург", latitude: 56.838011, longitude: 60.597474 }, + { name: "Казань", latitude: 55.796127, longitude: 49.106405 } + ]; +} + +// ------------------- 4. ДОБАВЛЕНИЕ МАРКЕРОВ НА КАРТУ ------------------- +function addMarkersToMap() { + // Очищаем старые маркеры, если есть + for (let key in markers) { + if (markers[key]) map.removeLayer(markers[key]); + } + markers = {}; + + cities.forEach(city => { + // Проверяем, что координаты корректны + if (!isFinite(city.latitude) || !isFinite(city.longitude)) { + console.warn(`Некорректные координаты для города ${city.name}`, city); + return; + } + + const marker = L.marker([city.latitude, city.longitude]).addTo(map); + marker.on('click', () => { + // Закрываем предыдущий попап, если он открыт + if (activePopup) map.closePopup(activePopup); + fetchWeather(city.latitude, city.longitude, city.name); + }); + + // Добавляем всплывающую подсказку при наведении (необязательно) + marker.bindTooltip(city.name, { sticky: true }); + + markers[city.name] = marker; + }); + console.log(`✅ Добавлено маркеров: ${Object.keys(markers).length}`); +} + +// ------------------- 5. ПОИСК ГОРОДА ------------------- +const searchInput = document.getElementById('city-search'); +const searchBtn = document.getElementById('search-btn'); + +function searchCity() { + const query = searchInput.value.trim().toLowerCase(); + if (!query) { + alert("Введите название города"); + return; + } + + // Ищем точное совпадение или начинающееся с запроса + const foundCity = cities.find(city => + city.name.toLowerCase() === query || + city.name.toLowerCase().startsWith(query) + ); + + if (foundCity) { + map.setView([foundCity.latitude, foundCity.longitude], 10); + // Имитируем клик по маркеру + if (markers[foundCity.name]) { + markers[foundCity.name].fire('click'); + } else { + // На случай, если маркер почему-то не создан + fetchWeather(foundCity.latitude, foundCity.longitude, foundCity.name); + } + } else { + alert(`Город "${searchInput.value}" не найден. Попробуйте: Москва, Санкт-Петербург, Новосибирск...`); + } +} + +searchBtn.addEventListener('click', searchCity); +searchInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') searchCity(); +}); + +// ------------------- 6. РАБОТА С ПОГОДНЫМ API ------------------- +const API_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzc1Mzg3MjA1LCJpYXQiOjE3NzUzODY5MDUsImp0aSI6ImYwNjQ2NzU1NDY2ZTRmMDk4YjJmZjc0ODJjYWU0OWMxIiwidXNlcl9pZCI6IjM2NTQifQ.TXWDYHAIz-VBflSnF4mbF79GGisZOTeg9y2ANTSw6vI'; +const API_URL = 'https://api.projecteol.ru/v1/forecast/'; + +async function fetchWeather(lat, lon, cityName) { + try { + document.getElementById('weather-title').innerHTML = `⏳ Загрузка погоды для ${cityName}...`; + + // Если нет токена – показываем демо-данные + if (API_TOKEN === 'ТВОЙ_УНИКАЛЬНЫЙ_ТОКЕН') { + console.warn("⚠️ Не задан API-ключ. Используем демонстрационные данные."); + showDemoWeather(cityName); + return; + } + + const url = `${API_URL}?lat=${lat}&lon=${lon}&token=${API_TOKEN}`; + const response = await fetch(url); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const data = await response.json(); + processWeatherData(data, cityName); + } catch (error) { + console.error('Ошибка API погоды:', error); + document.getElementById('weather-title').innerHTML = `❌ Ошибка загрузки для ${cityName}`; + showDemoWeather(cityName); // временно покажем демо-графики + } +} + +// Демо-данные, чтобы графики хоть как-то рисовались (пока нет токена) +function showDemoWeather(cityName) { + const dates = ['День 1', 'День 2', 'День 3', 'День 4', 'День 5']; + const temps = [5, 7, 6, 4, 3]; + const rains = [2, 0, 5, 1, 8]; + const winds = [4, 5, 3, 6, 7]; + document.getElementById('weather-title').innerHTML = `🌦️ Демо-прогноз: ${cityName}`; + updateChart('tempChart', dates, temps, 'Температура (°C)', 'rgba(255,99,132,0.2)', 'rgba(255,99,132,1)', 'line'); + updateChart('rainChart', dates, rains, 'Осадки (мм)', 'rgba(54,162,235,0.2)', 'rgba(54,162,235,1)', 'bar'); + updateChart('windChart', dates, winds, 'Ветер (м/с)', 'rgba(75,192,192,0.2)', 'rgba(75,192,192,1)', 'line'); +} + +function processWeatherData(data, cityName) { + // Здесь должен быть разбор реального ответа от projecteol + // Пока заглушка – вызываем демо + showDemoWeather(cityName); +} + +// ------------------- 7. ОТРИСОВКА ГРАФИКОВ ------------------- +function updateChart(chartId, labels, data, label, bgColor, borderColor, type) { + const ctx = document.getElementById(chartId).getContext('2d'); + if (window[chartId]) { + window[chartId].data.labels = labels; + window[chartId].data.datasets[0].data = data; + window[chartId].update(); + } else { + window[chartId] = new Chart(ctx, { + type: type, + data: { + labels: labels, + datasets: [{ + label: label, + data: data, + backgroundColor: bgColor, + borderColor: borderColor, + borderWidth: 2, + fill: true + }] + }, + options: { + responsive: true, + maintainAspectRatio: false + } + }); + } + // Убираем заглушку "нажмите на город" + const infoMsg = document.getElementById('info-message'); + if (infoMsg) infoMsg.style.display = 'none'; +} +// Инициализация карты и загрузка городов +document.addEventListener('DOMContentLoaded', () => { + loadCities(); // Это нужно добавить! +}); \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 00000000..427f15fb --- /dev/null +++ b/styles.css @@ -0,0 +1,189 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: #f0f2f5; + height: 100vh; + overflow: hidden; +} + +/* Основной контейнер: карта + панель */ +.app-container { + display: flex; + height: 100vh; + width: 100%; +} + +/* Левая часть — карта */ +.map-container { + flex: 2; + position: relative; + border-radius: 12px; + margin: 12px 0 12px 12px; + box-shadow: 0 4px 20px rgba(0,0,0,0.1); + overflow: hidden; +} + +#map { + height: 100%; + width: 100%; + border-radius: 12px; +} + +/* Правая панель — поиск + графики */ +.dashboard { + flex: 1.2; + background: white; + margin: 12px 12px 12px 0; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0,0,0,0.1); + display: flex; + flex-direction: column; + overflow-y: auto; + padding: 20px; +} + +/* Поиск */ +.search-section { + margin-bottom: 25px; + background: #f8f9fa; + padding: 15px; + border-radius: 16px; +} + +.search-section h3 { + margin-bottom: 12px; + color: #1e2a3a; + font-size: 1.2rem; +} + +.search-box { + display: flex; + gap: 10px; +} + +#city-search { + flex: 1; + padding: 12px 15px; + border: 1px solid #ddd; + border-radius: 40px; + font-size: 1rem; + outline: none; + transition: 0.2s; +} + +#city-search:focus { + border-color: #2c7da0; + box-shadow: 0 0 0 2px rgba(44,125,160,0.2); +} + +#search-btn { + background: #2c7da0; + color: white; + border: none; + padding: 0 25px; + border-radius: 40px; + font-weight: bold; + cursor: pointer; + transition: 0.2s; + font-size: 1rem; +} + +#search-btn:hover { + background: #1f5e7a; +} + +/* Заголовок погоды */ +.weather-header { + margin: 10px 0 20px 0; + text-align: center; +} + +#weather-title { + font-size: 1.5rem; + font-weight: 600; + color: #0b3b4f; + border-bottom: 3px solid #2c7da0; + display: inline-block; + padding-bottom: 5px; +} + +/* Блоки графиков */ +.charts-container { + display: flex; + flex-direction: column; + gap: 30px; +} + +.chart-card { + background: #ffffff; + border-radius: 20px; + padding: 15px; + box-shadow: 0 2px 8px rgba(0,0,0,0.05); + border: 1px solid #e9ecef; +} + +.chart-card h4 { + margin-bottom: 15px; + color: #2c3e50; + font-size: 1.1rem; + display: flex; + align-items: center; + gap: 8px; +} + +canvas { + max-height: 220px; + width: 100%; +} + +/* Информация при отсутствии данных */ +.placeholder-message { + text-align: center; + color: #6c757d; + padding: 40px 20px; + background: #f8f9fa; + border-radius: 20px; + margin-top: 20px; +} + +/* Адаптивность */ +@media (max-width: 800px) { + .app-container { + flex-direction: column; + } + .map-container { + flex: 1; + margin: 12px; + min-height: 40vh; + } + .dashboard { + flex: 1; + margin: 0 12px 12px 12px; + max-height: 50vh; + } +} + +/* Стили для всплывающих подсказок Leaflet (немного улучшим) */ +.leaflet-popup-content { + font-size: 14px; + font-weight: 500; + color: #1e4663; +} + +/* Скролл для панели */ +.dashboard::-webkit-scrollbar { + width: 6px; +} +.dashboard::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 10px; +} +.dashboard::-webkit-scrollbar-thumb { + background: #b9c4ce; + border-radius: 10px; +} \ No newline at end of file From af88ce64e43b706bef5ae741d325f4f4dc810d6e Mon Sep 17 00:00:00 2001 From: Mary Date: Mon, 6 Apr 2026 19:44:12 +0400 Subject: [PATCH 2/7] =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA=D0=B0?= =?UTF-8?q?=20=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D1=82=D1=8C=20=D1=87=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=20=D0=BF=D1=80=D0=BE=D0=BA=D1=81=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 4 +- nginx.conf | 39 +++++ proxy.py | 42 +++++ script.js | 76 ++++---- styles.css => style.css | 376 ++++++++++++++++++++-------------------- test.html | 54 ++++++ 6 files changed, 370 insertions(+), 221 deletions(-) create mode 100644 nginx.conf create mode 100644 proxy.py rename styles.css => style.css (94%) create mode 100644 test.html diff --git a/index.html b/index.html index cb4d0883..10fe24d7 100644 --- a/index.html +++ b/index.html @@ -8,12 +8,10 @@ - - - +
diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 00000000..3ea9499c --- /dev/null +++ b/nginx.conf @@ -0,0 +1,39 @@ +events { + worker_connections 1024; +} + +http { + server { + listen 80; + + # Статический контент (твой сайт) + location / { + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ /index.html; + } + + # Проксирование запросов к API + location /weather { + proxy_pass https://176.59.144.152/v1/forecast; # IP-адрес api.projecteol.ru + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Добавляем заголовки для CORS + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; + + # Обработка OPTIONS запросов + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + } + } +} \ No newline at end of file diff --git a/proxy.py b/proxy.py new file mode 100644 index 00000000..78ccc692 --- /dev/null +++ b/proxy.py @@ -0,0 +1,42 @@ +from flask import Flask, request, jsonify +from flask_cors import CORS +import requests +from datetime import datetime + +app = Flask(__name__) +CORS(app) + +@app.route('/weather', methods=['GET']) +def weather_proxy(): + lat = request.args.get('lat') + lon = request.args.get('lon') + token = request.args.get('token') + date = request.args.get('date', default=datetime.now().strftime('%Y-%m-%d')) + + if not lat or not lon or not token: + return jsonify({"error": "Missing parameters: lat, lon, token"}), 400 + + # Исправленный URL (соответствует реальному API) + target_url = f'https://projecteol.ru/ru/api/weather?lat={lat}&lon={lon}&date={date}&token={token}' + + try: + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + } + response = requests.get(target_url, headers=headers, timeout=10) + + return jsonify(response.json()), response.status_code, { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type' + } + + except Exception as e: + return jsonify({ + "error": "Proxy error", + "message": str(e), + "url": target_url + }), 500, {'Access-Control-Allow-Origin': '*'} + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8080, debug=True) # Важно: 0.0.0.0! \ No newline at end of file diff --git a/script.js b/script.js index 1dbefdaf..4ed7a1d0 100644 --- a/script.js +++ b/script.js @@ -18,30 +18,16 @@ async function loadCities() { // Пытаемся загрузить russia-cities.json let data = null; try { - const response = await fetch('russia-cities.json'); + const response = await fetch('cities.json'); if (response.ok) { data = await response.json(); - console.log(`✅ Загружен russia-cities.json, количество записей: ${data.length}`); - } else { - console.warn("russia-cities.json не найден, пробуем cities.json"); + console.log(`✅ Загружен cities.json, количество записей: ${data.length}`); } } catch (e) { - console.warn("Ошибка загрузки russia-cities.json", e); - } - - // Если не загрузился, пробуем cities.json (простой формат) - if (!data) { - try { - const response = await fetch('cities.json'); - if (response.ok) { - data = await response.json(); - console.log(`✅ Загружен cities.json, количество записей: ${data.length}`); - } - } catch (e) { console.warn("cities.json не найден"); - } } + // Если данные есть – преобразуем в единый формат if (data && Array.isArray(data)) { // Определяем формат: если первый объект имеет поля coords.lat, то это формат russia-cities @@ -163,19 +149,13 @@ searchInput.addEventListener('keypress', (e) => { // ------------------- 6. РАБОТА С ПОГОДНЫМ API ------------------- const API_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzc1Mzg3MjA1LCJpYXQiOjE3NzUzODY5MDUsImp0aSI6ImYwNjQ2NzU1NDY2ZTRmMDk4YjJmZjc0ODJjYWU0OWMxIiwidXNlcl9pZCI6IjM2NTQifQ.TXWDYHAIz-VBflSnF4mbF79GGisZOTeg9y2ANTSw6vI'; const API_URL = 'https://api.projecteol.ru/v1/forecast/'; - async function fetchWeather(lat, lon, cityName) { try { document.getElementById('weather-title').innerHTML = `⏳ Загрузка погоды для ${cityName}...`; - - // Если нет токена – показываем демо-данные - if (API_TOKEN === 'ТВОЙ_УНИКАЛЬНЫЙ_ТОКЕН') { - console.warn("⚠️ Не задан API-ключ. Используем демонстрационные данные."); - showDemoWeather(cityName); - return; - } - - const url = `${API_URL}?lat=${lat}&lon=${lon}&token=${API_TOKEN}`; + + //const url = `${API_URL}?lat=${lat}&lon=${lon}&token=${API_TOKEN}`; + const today = new Date().toISOString().split('T')[0]; + const url = `http://localhost:8080/weather?lat=${lat}&lon=${lon}&date=${today}&token=${API_TOKEN}`; const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); @@ -200,9 +180,45 @@ function showDemoWeather(cityName) { } function processWeatherData(data, cityName) { - // Здесь должен быть разбор реального ответа от projecteol - // Пока заглушка – вызываем демо - showDemoWeather(cityName); + // data - это массив почасовых прогнозов + if (!Array.isArray(data) || data.length === 0) { + console.error("Нет данных от API", data); + showDemoWeather(cityName); + return; + } + + // Группируем по дням + const daily = {}; + data.forEach(item => { + const date = item.dt_forecast.split(' ')[0]; // "2026-04-06" + if (!daily[date]) { + daily[date] = { temps: [], rains: [], winds: [] }; + } + daily[date].temps.push(item.temp_2_cel); + daily[date].rains.push(item.prate); + daily[date].winds.push(item.wind_speed_10); + }); + + // Вычисляем средние за день + const dates = []; + const temps = []; + const rains = []; + const winds = []; + + for (const [date, values] of Object.entries(daily)) { + dates.push(date); + const avgTemp = values.temps.reduce((a,b) => a+b,0) / values.temps.length; + temps.push(avgTemp.toFixed(1)); + const totalRain = values.rains.reduce((a,b) => a+b,0); + rains.push(totalRain.toFixed(1)); + const avgWind = values.winds.reduce((a,b) => a+b,0) / values.winds.length; + winds.push(avgWind.toFixed(1)); + } + + document.getElementById('weather-title').innerHTML = `🌤️ Прогноз погоды: ${cityName}`; + updateChart('tempChart', dates, temps, 'Температура (°C)', 'rgba(255,99,132,0.2)', 'rgba(255,99,132,1)', 'line'); + updateChart('rainChart', dates, rains, 'Осадки (мм)', 'rgba(54,162,235,0.2)', 'rgba(54,162,235,1)', 'bar'); + updateChart('windChart', dates, winds, 'Ветер (м/с)', 'rgba(75,192,192,0.2)', 'rgba(75,192,192,1)', 'line'); } // ------------------- 7. ОТРИСОВКА ГРАФИКОВ ------------------- diff --git a/styles.css b/style.css similarity index 94% rename from styles.css rename to style.css index 427f15fb..253be893 100644 --- a/styles.css +++ b/style.css @@ -1,189 +1,189 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - background: #f0f2f5; - height: 100vh; - overflow: hidden; -} - -/* Основной контейнер: карта + панель */ -.app-container { - display: flex; - height: 100vh; - width: 100%; -} - -/* Левая часть — карта */ -.map-container { - flex: 2; - position: relative; - border-radius: 12px; - margin: 12px 0 12px 12px; - box-shadow: 0 4px 20px rgba(0,0,0,0.1); - overflow: hidden; -} - -#map { - height: 100%; - width: 100%; - border-radius: 12px; -} - -/* Правая панель — поиск + графики */ -.dashboard { - flex: 1.2; - background: white; - margin: 12px 12px 12px 0; - border-radius: 12px; - box-shadow: 0 4px 20px rgba(0,0,0,0.1); - display: flex; - flex-direction: column; - overflow-y: auto; - padding: 20px; -} - -/* Поиск */ -.search-section { - margin-bottom: 25px; - background: #f8f9fa; - padding: 15px; - border-radius: 16px; -} - -.search-section h3 { - margin-bottom: 12px; - color: #1e2a3a; - font-size: 1.2rem; -} - -.search-box { - display: flex; - gap: 10px; -} - -#city-search { - flex: 1; - padding: 12px 15px; - border: 1px solid #ddd; - border-radius: 40px; - font-size: 1rem; - outline: none; - transition: 0.2s; -} - -#city-search:focus { - border-color: #2c7da0; - box-shadow: 0 0 0 2px rgba(44,125,160,0.2); -} - -#search-btn { - background: #2c7da0; - color: white; - border: none; - padding: 0 25px; - border-radius: 40px; - font-weight: bold; - cursor: pointer; - transition: 0.2s; - font-size: 1rem; -} - -#search-btn:hover { - background: #1f5e7a; -} - -/* Заголовок погоды */ -.weather-header { - margin: 10px 0 20px 0; - text-align: center; -} - -#weather-title { - font-size: 1.5rem; - font-weight: 600; - color: #0b3b4f; - border-bottom: 3px solid #2c7da0; - display: inline-block; - padding-bottom: 5px; -} - -/* Блоки графиков */ -.charts-container { - display: flex; - flex-direction: column; - gap: 30px; -} - -.chart-card { - background: #ffffff; - border-radius: 20px; - padding: 15px; - box-shadow: 0 2px 8px rgba(0,0,0,0.05); - border: 1px solid #e9ecef; -} - -.chart-card h4 { - margin-bottom: 15px; - color: #2c3e50; - font-size: 1.1rem; - display: flex; - align-items: center; - gap: 8px; -} - -canvas { - max-height: 220px; - width: 100%; -} - -/* Информация при отсутствии данных */ -.placeholder-message { - text-align: center; - color: #6c757d; - padding: 40px 20px; - background: #f8f9fa; - border-radius: 20px; - margin-top: 20px; -} - -/* Адаптивность */ -@media (max-width: 800px) { - .app-container { - flex-direction: column; - } - .map-container { - flex: 1; - margin: 12px; - min-height: 40vh; - } - .dashboard { - flex: 1; - margin: 0 12px 12px 12px; - max-height: 50vh; - } -} - -/* Стили для всплывающих подсказок Leaflet (немного улучшим) */ -.leaflet-popup-content { - font-size: 14px; - font-weight: 500; - color: #1e4663; -} - -/* Скролл для панели */ -.dashboard::-webkit-scrollbar { - width: 6px; -} -.dashboard::-webkit-scrollbar-track { - background: #f1f1f1; - border-radius: 10px; -} -.dashboard::-webkit-scrollbar-thumb { - background: #b9c4ce; - border-radius: 10px; +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: #f0f2f5; + height: 100vh; + overflow: hidden; +} + +/* Основной контейнер: карта + панель */ +.app-container { + display: flex; + height: 100vh; + width: 100%; +} + +/* Левая часть — карта */ +.map-container { + flex: 2; + position: relative; + border-radius: 12px; + margin: 12px 0 12px 12px; + box-shadow: 0 4px 20px rgba(0,0,0,0.1); + overflow: hidden; +} + +#map { + height: 100%; + width: 100%; + border-radius: 12px; +} + +/* Правая панель — поиск + графики */ +.dashboard { + flex: 1.2; + background: white; + margin: 12px 12px 12px 0; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0,0,0,0.1); + display: flex; + flex-direction: column; + overflow-y: auto; + padding: 20px; +} + +/* Поиск */ +.search-section { + margin-bottom: 25px; + background: #f8f9fa; + padding: 15px; + border-radius: 16px; +} + +.search-section h3 { + margin-bottom: 12px; + color: #1e2a3a; + font-size: 1.2rem; +} + +.search-box { + display: flex; + gap: 10px; +} + +#city-search { + flex: 1; + padding: 12px 15px; + border: 1px solid #ddd; + border-radius: 40px; + font-size: 1rem; + outline: none; + transition: 0.2s; +} + +#city-search:focus { + border-color: #2c7da0; + box-shadow: 0 0 0 2px rgba(44,125,160,0.2); +} + +#search-btn { + background: #2c7da0; + color: white; + border: none; + padding: 0 25px; + border-radius: 40px; + font-weight: bold; + cursor: pointer; + transition: 0.2s; + font-size: 1rem; +} + +#search-btn:hover { + background: #1f5e7a; +} + +/* Заголовок погоды */ +.weather-header { + margin: 10px 0 20px 0; + text-align: center; +} + +#weather-title { + font-size: 1.5rem; + font-weight: 600; + color: #0b3b4f; + border-bottom: 3px solid #2c7da0; + display: inline-block; + padding-bottom: 5px; +} + +/* Блоки графиков */ +.charts-container { + display: flex; + flex-direction: column; + gap: 30px; +} + +.chart-card { + background: #ffffff; + border-radius: 20px; + padding: 15px; + box-shadow: 0 2px 8px rgba(0,0,0,0.05); + border: 1px solid #e9ecef; +} + +.chart-card h4 { + margin-bottom: 15px; + color: #2c3e50; + font-size: 1.1rem; + display: flex; + align-items: center; + gap: 8px; +} + +canvas { + max-height: 220px; + width: 100%; +} + +/* Информация при отсутствии данных */ +.placeholder-message { + text-align: center; + color: #6c757d; + padding: 40px 20px; + background: #f8f9fa; + border-radius: 20px; + margin-top: 20px; +} + +/* Адаптивность */ +@media (max-width: 800px) { + .app-container { + flex-direction: column; + } + .map-container { + flex: 1; + margin: 12px; + min-height: 40vh; + } + .dashboard { + flex: 1; + margin: 0 12px 12px 12px; + max-height: 50vh; + } +} + +/* Стили для всплывающих подсказок Leaflet (немного улучшим) */ +.leaflet-popup-content { + font-size: 14px; + font-weight: 500; + color: #1e4663; +} + +/* Скролл для панели */ +.dashboard::-webkit-scrollbar { + width: 6px; +} +.dashboard::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 10px; +} +.dashboard::-webkit-scrollbar-thumb { + background: #b9c4ce; + border-radius: 10px; } \ No newline at end of file diff --git a/test.html b/test.html new file mode 100644 index 00000000..18ffbee5 --- /dev/null +++ b/test.html @@ -0,0 +1,54 @@ + + + + + Тест карты + Open-Meteo + + + + + + +
+
+ + + +
+ + + \ No newline at end of file From 27f844327c0943cdb22e34b1d8a4b7d402b86f06 Mon Sep 17 00:00:00 2001 From: Lisyonok04 <125237397+Lisyonok04@users.noreply.github.com> Date: Mon, 6 Apr 2026 19:59:06 +0400 Subject: [PATCH 3/7] Update README.md --- README.md | 46 ++-------------------------------------------- 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 163d41b9..07f45d02 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,4 @@ # Безопасность веб-приложений. Лабораторка №2 - -## Схема сдачи - -1. Получить задание -2. Сделать форк данного репозитория -3. Выполнить задание согласно полученному варианту -4. Сделать PR (pull request) в данный репозиторий -6. Исправить замечания после code review -7. Получить approve -8. Прийти на занятие и защитить работу - -Что нужно проявить в работе: -- умение разработать завершенное целое веб-приложение, с клиентской и серверной частями (допустимы открытые АПИ) -- навыки верстки на html в объеме 200-300 тегов -- навыки применения css для лейаута и стилизации, желательно с адаптацией к мобилке -- использование jQuery или аналогичных JS-фреймворков -- динамическая подгрузка контента -- динамическое изменение DOM и CSSOM - -Если у вас своя идея по заданию, то расскажите, обсудим и подкорректирую. - -## Вариант 1. Расписания - -Сделать аналог раздела https://ssau.ru/rasp?groupId=531030143 - -Какие нужны возможности: -- справочники групп, табличные данные по расписаниям добывать с настоящего сайта на серверной стороне приложения -- в клиентскую часть подгружать эти сведения динамически по JSON-API -- обеспечить возможность смотреть расписания в разрезе группы или препода -- обеспечить возможность выбора учебной недели (по умолчанию выбирается автоматически) - -## Вариант 2. Аналог Прибывалки для электричек - -Сделать веб-версию Прибывалки, только для электричек - -Какие нужны возможности: -- находить желаемую ЖД-станцию поиском по названию и по карте -- отображать расписания всех проходящих поездов через выбранную станцию -- отображать расписания для поездов между двумя станциями -- работа через АПИ Яндекс.Расписаний https://yandex.ru/dev/rasp/doc/ru/ (доступ получите сами) -- хорошая работа в условиях экрана смартфона -- бонус: функция "любимых остановок" - ## Вариант 3. Прогноз погоды Сделать одностраничный сайт с картой, на которой можно выбрать населенный пункт и получить прогноз погоды на несколько дней по нему. @@ -54,7 +11,8 @@ - можете реализовать с собственным серверным компонентом или придумать, как обойтись без него - +## Пояснительная заметка +В ходе написания лабораторной была предпринята попытка создать приложение с использованием предложенного в задании сайта - создан профиль, сгенерирован токен. Но, к сожалению, реализовать получение данных так и не получилось. Поэтому был использован сайт, где не требовалась подписка для получения данных о погодных условиях. From 8ced1ee84c4139cde3b70083c8a643878bf5a624 Mon Sep 17 00:00:00 2001 From: Mary Date: Mon, 6 Apr 2026 22:53:23 +0400 Subject: [PATCH 4/7] =?UTF-8?q?=D0=B5=D1=89=D0=B5=20=D0=BE=D0=B4=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA=D0=B0=20=D1=81?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B0=D1=82=D1=8C=20=D1=87=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B7=20=D1=81=D0=B2=D0=BE=D0=B9=20=D0=BF=D1=80=D0=BE=D0=BA?= =?UTF-8?q?=D1=81=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yaml | 2 +- proxy.py | 140 ++++++++++++++++++++++++++++++++++++++------ script.js | 2 +- test.html | 54 ----------------- 4 files changed, 125 insertions(+), 73 deletions(-) delete mode 100644 test.html diff --git a/docker-compose.yaml b/docker-compose.yaml index 9c09c919..c7dead5b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,6 +3,6 @@ services: web: image: nginx:alpine ports: - - "8080:80" + - "8081:80" volumes: - ./:/usr/share/nginx/html:ro diff --git a/proxy.py b/proxy.py index 78ccc692..8e6c6d82 100644 --- a/proxy.py +++ b/proxy.py @@ -1,42 +1,148 @@ -from flask import Flask, request, jsonify +""" +Прокси-сервер для получения прогноза погоды +Работает на: http://localhost:8080/weather?lat=...&lon=...&token=... +""" + +from flask import Flask, request, jsonify, make_response from flask_cors import CORS import requests from datetime import datetime +import logging + +# Настройка логирования +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) +# Создаем приложение Flask app = Flask(__name__) -CORS(app) -@app.route('/weather', methods=['GET']) +# Включаем CORS для всех маршрутов +CORS(app, resources={ + r"/weather": { + "origins": "*", # Разрешаем запросы с любого домена + "methods": ["GET", "OPTIONS"], + "allow_headers": ["Content-Type"] + } +}) + +# ДЕМО-ДАННЫЕ (на случай, если API недоступен) +DEMO_WEATHER_DATA = { + "city": "Москва", + "date": "2026-04-07", + "forecast": [ + {"day": "Пн", "temp": 12, "precipitation": 2, "wind": 4}, + {"day": "Вт", "temp": 15, "precipitation": 0, "wind": 3}, + {"day": "Ср", "temp": 18, "precipitation": 5, "wind": 6}, + {"day": "Чт", "temp": 14, "precipitation": 8, "wind": 7}, + {"day": "Пт", "temp": 16, "precipitation": 1, "wind": 5} + ] +} + +@app.route('/weather', methods=['GET', 'OPTIONS']) def weather_proxy(): + """ + Обработчик запросов к погодному API + Поддерживает: GET (основной запрос) и OPTIONS (предварительный CORS-запрос) + """ + + # Шаг 1: Обработка предварительного запроса (CORS preflight) + if request.method == 'OPTIONS': + logger.info("Получен OPTIONS-запрос (CORS preflight)") + return _build_cors_response(jsonify({"status": "ok"}), 200) + + # Шаг 2: Получаем параметры из запроса + logger.info("Получен GET-запрос к /weather") + lat = request.args.get('lat') lon = request.args.get('lon') token = request.args.get('token') date = request.args.get('date', default=datetime.now().strftime('%Y-%m-%d')) + # Шаг 3: Валидация параметров if not lat or not lon or not token: - return jsonify({"error": "Missing parameters: lat, lon, token"}), 400 + logger.warning(f"Отсутствуют обязательные параметры: lat={lat}, lon={lon}, token={token}") + return _build_cors_response( + jsonify({ + "error": "Missing required parameters", + "required": ["lat", "lon", "token"], + "received": {"lat": lat, "lon": lon, "token": "present" if token else None} + }), + 400 + ) + + logger.info(f"Запрос погоды: lat={lat}, lon={lon}, date={date}") - # Исправленный URL (соответствует реальному API) + # Шаг 4: Формируем URL для реального API target_url = f'https://projecteol.ru/ru/api/weather?lat={lat}&lon={lon}&date={date}&token={token}' + # Шаг 5: Делаем запрос к реальному API try: headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } + + logger.info(f"Отправляем запрос к: {target_url}") response = requests.get(target_url, headers=headers, timeout=10) - return jsonify(response.json()), response.status_code, { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, OPTIONS', - 'Access-Control-Allow-Headers': 'Content-Type' - } + # Шаг 6: Если API ответил успешно — возвращаем его данные + if response.status_code == 200: + logger.info(f"Успешный ответ от API: {response.status_code}") + return _build_cors_response(jsonify(response.json()), 200) + + # Шаг 7: Если API вернул ошибку — логируем и возвращаем демо-данные + logger.warning(f"API вернул ошибку: {response.status_code} - {response.text}") + logger.info("Возвращаем демо-данные как fallback") + return _build_cors_response(jsonify(DEMO_WEATHER_DATA), 200) + + # Шаг 8: Обработка исключений (сеть недоступна, таймаут и т.д.) + except requests.exceptions.RequestException as e: + logger.error(f"Ошибка при запросе к API: {str(e)}") + logger.info("Возвращаем демо-данные из-за ошибки сети") + return _build_cors_response(jsonify(DEMO_WEATHER_DATA), 200) + # Шаг 9: Любые другие исключения except Exception as e: - return jsonify({ - "error": "Proxy error", - "message": str(e), - "url": target_url - }), 500, {'Access-Control-Allow-Origin': '*'} + logger.exception(f"Неожиданная ошибка: {str(e)}") + return _build_cors_response( + jsonify({ + "error": "Internal server error", + "message": str(e) + }), + 500 + ) + +@app.route('/') +def home(): + """Корневая страница — информация о прокси""" + return """ +

🌤️ Прокси-сервер для прогноза погоды

+

Работает на порту 8080

+

Пример запроса:

+ http://localhost:8080/weather?lat=55.75&lon=37.62&token=ВАШ_ТОКЕН +

Для учебных целей возвращает демо-данные, если реальный API недоступен.

+ """, 200, {'Access-Control-Allow-Origin': '*'} + +def _build_cors_response(response, status_code): + """ + Добавляет CORS-заголовки к любому ответу + """ + response.status_code = status_code + response.headers['Access-Control-Allow-Origin'] = '*' + response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS' + response.headers['Access-Control-Allow-Headers'] = 'Content-Type' + response.headers['Access-Control-Max-Age'] = '3600' + return response if __name__ == '__main__': - app.run(host='0.0.0.0', port=8080, debug=True) # Важно: 0.0.0.0! \ No newline at end of file + logger.info("=" * 60) + logger.info("🚀 Запуск прокси-сервера для прогноза погоды") + logger.info("📍 Доступен по адресу: http://localhost:8080/weather") + logger.info("💡 Для теста открой в браузере:") + logger.info(" http://localhost:8080/weather?lat=55.75&lon=37.62&token=test") + logger.info("=" * 60) + + # Запускаем сервер + app.run(host='0.0.0.0', port=8080, debug=True) \ No newline at end of file diff --git a/script.js b/script.js index 4ed7a1d0..e288ff99 100644 --- a/script.js +++ b/script.js @@ -155,7 +155,7 @@ async function fetchWeather(lat, lon, cityName) { //const url = `${API_URL}?lat=${lat}&lon=${lon}&token=${API_TOKEN}`; const today = new Date().toISOString().split('T')[0]; - const url = `http://localhost:8080/weather?lat=${lat}&lon=${lon}&date=${today}&token=${API_TOKEN}`; + const url = `https://projecteol.ru/api/weather/?lat=${lat}&lon=${lon}&date=${today}&token=${API_TOKEN}`; const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); diff --git a/test.html b/test.html deleted file mode 100644 index 18ffbee5..00000000 --- a/test.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - Тест карты + Open-Meteo - - - - - - -
-
- - - -
- - - \ No newline at end of file From 9b0293f57fe625605baf8fc8e718d1c290daebf8 Mon Sep 17 00:00:00 2001 From: Mary Date: Tue, 7 Apr 2026 00:20:43 +0400 Subject: [PATCH 5/7] =?UTF-8?q?=D0=BD=D0=B5=20=D0=B2=D1=8B=D1=88=D0=BB?= =?UTF-8?q?=D0=BE,=20=D0=BF=D0=BE=D1=8D=D1=82=D0=BE=D0=BC=D1=83=20open=20w?= =?UTF-8?q?eathe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 42 +------- nginx.conf | 39 -------- proxy.py | 148 ---------------------------- script.js | 278 +++++++++++++++++------------------------------------ 4 files changed, 91 insertions(+), 416 deletions(-) delete mode 100644 nginx.conf delete mode 100644 proxy.py diff --git a/index.html b/index.html index 10fe24d7..6fe5d307 100644 --- a/index.html +++ b/index.html @@ -8,35 +8,31 @@ + + -
-
- -

🔍 Найти населённый пункт

⚡ Кликните по маркеру на карте или найдите город через поиск
-
🌤️ Выберите город на карте
-

🌡️ Температура (°C)

@@ -51,41 +47,11 @@

💨 Ветер (м/с)

-
- - - - - \ No newline at end of file diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index 3ea9499c..00000000 --- a/nginx.conf +++ /dev/null @@ -1,39 +0,0 @@ -events { - worker_connections 1024; -} - -http { - server { - listen 80; - - # Статический контент (твой сайт) - location / { - root /usr/share/nginx/html; - index index.html; - try_files $uri $uri/ /index.html; - } - - # Проксирование запросов к API - location /weather { - proxy_pass https://176.59.144.152/v1/forecast; # IP-адрес api.projecteol.ru - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # Добавляем заголовки для CORS - add_header 'Access-Control-Allow-Origin' '*'; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; - - # Обработка OPTIONS запросов - if ($request_method = 'OPTIONS') { - add_header 'Access-Control-Max-Age' 1728000; - add_header 'Content-Type' 'text/plain charset=UTF-8'; - add_header 'Content-Length' 0; - return 204; - } - } - } -} \ No newline at end of file diff --git a/proxy.py b/proxy.py deleted file mode 100644 index 8e6c6d82..00000000 --- a/proxy.py +++ /dev/null @@ -1,148 +0,0 @@ -""" -Прокси-сервер для получения прогноза погоды -Работает на: http://localhost:8080/weather?lat=...&lon=...&token=... -""" - -from flask import Flask, request, jsonify, make_response -from flask_cors import CORS -import requests -from datetime import datetime -import logging - -# Настройка логирования -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(levelname)s - %(message)s' -) -logger = logging.getLogger(__name__) - -# Создаем приложение Flask -app = Flask(__name__) - -# Включаем CORS для всех маршрутов -CORS(app, resources={ - r"/weather": { - "origins": "*", # Разрешаем запросы с любого домена - "methods": ["GET", "OPTIONS"], - "allow_headers": ["Content-Type"] - } -}) - -# ДЕМО-ДАННЫЕ (на случай, если API недоступен) -DEMO_WEATHER_DATA = { - "city": "Москва", - "date": "2026-04-07", - "forecast": [ - {"day": "Пн", "temp": 12, "precipitation": 2, "wind": 4}, - {"day": "Вт", "temp": 15, "precipitation": 0, "wind": 3}, - {"day": "Ср", "temp": 18, "precipitation": 5, "wind": 6}, - {"day": "Чт", "temp": 14, "precipitation": 8, "wind": 7}, - {"day": "Пт", "temp": 16, "precipitation": 1, "wind": 5} - ] -} - -@app.route('/weather', methods=['GET', 'OPTIONS']) -def weather_proxy(): - """ - Обработчик запросов к погодному API - Поддерживает: GET (основной запрос) и OPTIONS (предварительный CORS-запрос) - """ - - # Шаг 1: Обработка предварительного запроса (CORS preflight) - if request.method == 'OPTIONS': - logger.info("Получен OPTIONS-запрос (CORS preflight)") - return _build_cors_response(jsonify({"status": "ok"}), 200) - - # Шаг 2: Получаем параметры из запроса - logger.info("Получен GET-запрос к /weather") - - lat = request.args.get('lat') - lon = request.args.get('lon') - token = request.args.get('token') - date = request.args.get('date', default=datetime.now().strftime('%Y-%m-%d')) - - # Шаг 3: Валидация параметров - if not lat or not lon or not token: - logger.warning(f"Отсутствуют обязательные параметры: lat={lat}, lon={lon}, token={token}") - return _build_cors_response( - jsonify({ - "error": "Missing required parameters", - "required": ["lat", "lon", "token"], - "received": {"lat": lat, "lon": lon, "token": "present" if token else None} - }), - 400 - ) - - logger.info(f"Запрос погоды: lat={lat}, lon={lon}, date={date}") - - # Шаг 4: Формируем URL для реального API - target_url = f'https://projecteol.ru/ru/api/weather?lat={lat}&lon={lon}&date={date}&token={token}' - - # Шаг 5: Делаем запрос к реальному API - try: - headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' - } - - logger.info(f"Отправляем запрос к: {target_url}") - response = requests.get(target_url, headers=headers, timeout=10) - - # Шаг 6: Если API ответил успешно — возвращаем его данные - if response.status_code == 200: - logger.info(f"Успешный ответ от API: {response.status_code}") - return _build_cors_response(jsonify(response.json()), 200) - - # Шаг 7: Если API вернул ошибку — логируем и возвращаем демо-данные - logger.warning(f"API вернул ошибку: {response.status_code} - {response.text}") - logger.info("Возвращаем демо-данные как fallback") - return _build_cors_response(jsonify(DEMO_WEATHER_DATA), 200) - - # Шаг 8: Обработка исключений (сеть недоступна, таймаут и т.д.) - except requests.exceptions.RequestException as e: - logger.error(f"Ошибка при запросе к API: {str(e)}") - logger.info("Возвращаем демо-данные из-за ошибки сети") - return _build_cors_response(jsonify(DEMO_WEATHER_DATA), 200) - - # Шаг 9: Любые другие исключения - except Exception as e: - logger.exception(f"Неожиданная ошибка: {str(e)}") - return _build_cors_response( - jsonify({ - "error": "Internal server error", - "message": str(e) - }), - 500 - ) - -@app.route('/') -def home(): - """Корневая страница — информация о прокси""" - return """ -

🌤️ Прокси-сервер для прогноза погоды

-

Работает на порту 8080

-

Пример запроса:

- http://localhost:8080/weather?lat=55.75&lon=37.62&token=ВАШ_ТОКЕН -

Для учебных целей возвращает демо-данные, если реальный API недоступен.

- """, 200, {'Access-Control-Allow-Origin': '*'} - -def _build_cors_response(response, status_code): - """ - Добавляет CORS-заголовки к любому ответу - """ - response.status_code = status_code - response.headers['Access-Control-Allow-Origin'] = '*' - response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS' - response.headers['Access-Control-Allow-Headers'] = 'Content-Type' - response.headers['Access-Control-Max-Age'] = '3600' - return response - -if __name__ == '__main__': - logger.info("=" * 60) - logger.info("🚀 Запуск прокси-сервера для прогноза погоды") - logger.info("📍 Доступен по адресу: http://localhost:8080/weather") - logger.info("💡 Для теста открой в браузере:") - logger.info(" http://localhost:8080/weather?lat=55.75&lon=37.62&token=test") - logger.info("=" * 60) - - # Запускаем сервер - app.run(host='0.0.0.0', port=8080, debug=True) \ No newline at end of file diff --git a/script.js b/script.js index e288ff99..85ebf52c 100644 --- a/script.js +++ b/script.js @@ -1,235 +1,129 @@ -// script.js – улучшенная версия с отладкой - -// ------------------- 1. ИНИЦИАЛИЗАЦИЯ КАРТЫ ------------------- +// ------------------- 1. КАРТА ------------------- let map = L.map('map').setView([64.0, 100.0], 4); -L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { - attribution: '© OSM' -}).addTo(map); +L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png').addTo(map); -// ------------------- 2. ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ------------------- -let cities = []; // массив городов в формате {name, latitude, longitude} -let markers = {}; // объект для быстрого доступа к маркерам по имени -let activePopup = null; // для закрытия предыдущего попапа +let cities = []; +let markers = {}; -// ------------------- 3. ЗАГРУЗКА ГОРОДОВ (с fallback) ------------------- +// ------------------- 2. ЗАГРУЗКА ГОРОДОВ ------------------- async function loadCities() { - console.log("Загрузка данных о городах..."); - - // Пытаемся загрузить russia-cities.json - let data = null; + console.log("Загрузка городов..."); try { - const response = await fetch('cities.json'); - if (response.ok) { - data = await response.json(); - console.log(`✅ Загружен cities.json, количество записей: ${data.length}`); + const response = await fetch('russia-cities.json'); + if (!response.ok) throw new Error(); + const raw = await response.json(); + if (raw[0] && raw[0].coords) { + cities = raw.map(c => ({ name: c.name, latitude: c.coords.lat, longitude: c.coords.lon })); + } else throw new Error(); + } catch(e) { + console.warn("russia-cities.json не загружен, пробуем cities.json"); + try { + const response = await fetch('cities.json'); + const raw = await response.json(); + if (raw[0] && typeof raw[0].lat === 'number') { + cities = raw.map(c => ({ name: c.name, latitude: c.lat, longitude: c.lon })); + } else if (raw[0] && typeof raw[0].latitude === 'number') { + cities = raw.map(c => ({ name: c.name, latitude: c.latitude, longitude: c.longitude })); + } else throw new Error(); + } catch(err) { + console.warn("Используем fallback-список"); + cities = [ + { name: "Москва", latitude: 55.751244, longitude: 37.618423 }, + { name: "Санкт-Петербург", latitude: 59.931058, longitude: 30.360909 }, + { name: "Новосибирск", latitude: 55.030199, longitude: 82.920430 }, + { name: "Екатеринбург", latitude: 56.838011, longitude: 60.597474 }, + { name: "Казань", latitude: 55.796127, longitude: 49.106405 } + ]; } - } catch (e) { - console.warn("cities.json не найден"); } - - - // Если данные есть – преобразуем в единый формат - if (data && Array.isArray(data)) { - // Определяем формат: если первый объект имеет поля coords.lat, то это формат russia-cities - if (data[0] && data[0].coords && typeof data[0].coords.lat === 'number') { - cities = data.map(city => ({ - name: city.name, - latitude: city.coords.lat, - longitude: city.coords.lon, - population: city.population - })); - console.log("✅ Преобразован формат russia-cities.json"); - } - // Если уже есть поля name, latitude, longitude - // СТАЛО - else if (data[0] && typeof data[0].lat === 'number' && typeof data[0].lon === 'number') { - cities = data.map(city => ({ - name: city.name, - latitude: city.lat, - longitude: city.lon, - population: city.population - })); - console.log("✅ Используется формат {name, lat, lon}"); - } -// Если есть поля latitude/longitude - else if (data[0] && typeof data[0].latitude === 'number' && typeof data[0].longitude === 'number') { - cities = data; - console.log("✅ Используется формат {name, latitude, longitude}"); - } - else { - console.error("❌ Неизвестный формат JSON. Ожидается массив с name/latitude/longitude или name/coords.lat/lon"); - cities = getFallbackCities(); - } - } else { - console.warn("⚠️ Нет загруженных данных, используем встроенный список из 5 городов"); - cities = getFallbackCities(); - } - - console.log(`Итоговое количество городов для отображения: ${cities.length}`); + console.log(`Загружено городов: ${cities.length}`); addMarkersToMap(); } -// Встроенный список на случай, если JSON не загрузился (чтобы вы сразу увидели маркеры) -function getFallbackCities() { - return [ - { name: "Москва", latitude: 55.751244, longitude: 37.618423 }, - { name: "Санкт-Петербург", latitude: 59.931058, longitude: 30.360909 }, - { name: "Новосибирск", latitude: 55.030199, longitude: 82.920430 }, - { name: "Екатеринбург", latitude: 56.838011, longitude: 60.597474 }, - { name: "Казань", latitude: 55.796127, longitude: 49.106405 } - ]; -} - -// ------------------- 4. ДОБАВЛЕНИЕ МАРКЕРОВ НА КАРТУ ------------------- function addMarkersToMap() { - // Очищаем старые маркеры, если есть - for (let key in markers) { - if (markers[key]) map.removeLayer(markers[key]); - } + Object.values(markers).forEach(m => map.removeLayer(m)); markers = {}; - cities.forEach(city => { - // Проверяем, что координаты корректны - if (!isFinite(city.latitude) || !isFinite(city.longitude)) { - console.warn(`Некорректные координаты для города ${city.name}`, city); - return; - } - const marker = L.marker([city.latitude, city.longitude]).addTo(map); - marker.on('click', () => { - // Закрываем предыдущий попап, если он открыт - if (activePopup) map.closePopup(activePopup); - fetchWeather(city.latitude, city.longitude, city.name); - }); - - // Добавляем всплывающую подсказку при наведении (необязательно) - marker.bindTooltip(city.name, { sticky: true }); - + marker.on('click', () => fetchWeather(city.latitude, city.longitude, city.name)); + marker.bindTooltip(city.name); markers[city.name] = marker; }); - console.log(`✅ Добавлено маркеров: ${Object.keys(markers).length}`); + console.log(`Маркеров добавлено: ${Object.keys(markers).length}`); + if (cities.length) map.setView([cities[0].latitude, cities[0].longitude], 6); } -// ------------------- 5. ПОИСК ГОРОДА ------------------- +// ------------------- 3. ПОИСК ------------------- const searchInput = document.getElementById('city-search'); const searchBtn = document.getElementById('search-btn'); function searchCity() { const query = searchInput.value.trim().toLowerCase(); - if (!query) { - alert("Введите название города"); - return; - } - - // Ищем точное совпадение или начинающееся с запроса - const foundCity = cities.find(city => - city.name.toLowerCase() === query || - city.name.toLowerCase().startsWith(query) - ); - - if (foundCity) { - map.setView([foundCity.latitude, foundCity.longitude], 10); - // Имитируем клик по маркеру - if (markers[foundCity.name]) { - markers[foundCity.name].fire('click'); - } else { - // На случай, если маркер почему-то не создан - fetchWeather(foundCity.latitude, foundCity.longitude, foundCity.name); - } + if (!query) return alert("Введите название"); + const found = cities.find(c => c.name.toLowerCase() === query || c.name.toLowerCase().startsWith(query)); + if (found) { + map.setView([found.latitude, found.longitude], 10); + fetchWeather(found.latitude, found.longitude, found.name); } else { - alert(`Город "${searchInput.value}" не найден. Попробуйте: Москва, Санкт-Петербург, Новосибирск...`); + alert(`Город "${searchInput.value}" не найден`); } } - searchBtn.addEventListener('click', searchCity); -searchInput.addEventListener('keypress', (e) => { - if (e.key === 'Enter') searchCity(); -}); +searchInput.addEventListener('keypress', e => e.key === 'Enter' && searchCity()); -// ------------------- 6. РАБОТА С ПОГОДНЫМ API ------------------- -const API_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzc1Mzg3MjA1LCJpYXQiOjE3NzUzODY5MDUsImp0aSI6ImYwNjQ2NzU1NDY2ZTRmMDk4YjJmZjc0ODJjYWU0OWMxIiwidXNlcl9pZCI6IjM2NTQifQ.TXWDYHAIz-VBflSnF4mbF79GGisZOTeg9y2ANTSw6vI'; -const API_URL = 'https://api.projecteol.ru/v1/forecast/'; +// ------------------- 4. ПОГОДА (Open-Meteo) ------------------- async function fetchWeather(lat, lon, cityName) { try { document.getElementById('weather-title').innerHTML = `⏳ Загрузка погоды для ${cityName}...`; - - //const url = `${API_URL}?lat=${lat}&lon=${lon}&token=${API_TOKEN}`; - const today = new Date().toISOString().split('T')[0]; - const url = `https://projecteol.ru/api/weather/?lat=${lat}&lon=${lon}&date=${today}&token=${API_TOKEN}`; + const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max&timezone=auto&forecast_days=7`; const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); - processWeatherData(data, cityName); + + const dates = data.daily.time; + const temps = data.daily.temperature_2m_max; + const rains = data.daily.precipitation_sum; + const winds = data.daily.windspeed_10m_max; + + document.getElementById('weather-title').innerHTML = `🌤️ Прогноз погоды: ${cityName}`; + createOrUpdateChart('tempChart', dates, temps, 'Температура макс. (°C)', 'rgba(255,99,132,0.2)', 'rgba(255,99,132,1)', 'line'); + createOrUpdateChart('rainChart', dates, rains, 'Осадки (мм)', 'rgba(54,162,235,0.2)', 'rgba(54,162,235,1)', 'bar'); + createOrUpdateChart('windChart', dates, winds, 'Ветер макс. (м/с)', 'rgba(75,192,192,0.2)', 'rgba(75,192,192,1)', 'line'); } catch (error) { - console.error('Ошибка API погоды:', error); + console.error('Ошибка погоды:', error); document.getElementById('weather-title').innerHTML = `❌ Ошибка загрузки для ${cityName}`; - showDemoWeather(cityName); // временно покажем демо-графики + showDemoWeather(cityName); } } -// Демо-данные, чтобы графики хоть как-то рисовались (пока нет токена) function showDemoWeather(cityName) { - const dates = ['День 1', 'День 2', 'День 3', 'День 4', 'День 5']; - const temps = [5, 7, 6, 4, 3]; - const rains = [2, 0, 5, 1, 8]; - const winds = [4, 5, 3, 6, 7]; + const dates = ['День 1','День 2','День 3','День 4','День 5']; + const temps = [5,7,6,4,3]; + const rains = [2,0,5,1,8]; + const winds = [4,5,3,6,7]; document.getElementById('weather-title').innerHTML = `🌦️ Демо-прогноз: ${cityName}`; - updateChart('tempChart', dates, temps, 'Температура (°C)', 'rgba(255,99,132,0.2)', 'rgba(255,99,132,1)', 'line'); - updateChart('rainChart', dates, rains, 'Осадки (мм)', 'rgba(54,162,235,0.2)', 'rgba(54,162,235,1)', 'bar'); - updateChart('windChart', dates, winds, 'Ветер (м/с)', 'rgba(75,192,192,0.2)', 'rgba(75,192,192,1)', 'line'); + createOrUpdateChart('tempChart', dates, temps, 'Температура (°C)', 'rgba(255,99,132,0.2)', 'rgba(255,99,132,1)', 'line'); + createOrUpdateChart('rainChart', dates, rains, 'Осадки (мм)', 'rgba(54,162,235,0.2)', 'rgba(54,162,235,1)', 'bar'); + createOrUpdateChart('windChart', dates, winds, 'Ветер (м/с)', 'rgba(75,192,192,0.2)', 'rgba(75,192,192,1)', 'line'); } -function processWeatherData(data, cityName) { - // data - это массив почасовых прогнозов - if (!Array.isArray(data) || data.length === 0) { - console.error("Нет данных от API", data); - showDemoWeather(cityName); +// ------------------- 5. ГРАФИКИ (абсолютно надёжная версия) ------------------- +function createOrUpdateChart(chartId, labels, data, label, bgColor, borderColor, type) { + const canvas = document.getElementById(chartId); + if (!canvas) { + console.error(`Canvas элемент #${chartId} не найден`); return; } - - // Группируем по дням - const daily = {}; - data.forEach(item => { - const date = item.dt_forecast.split(' ')[0]; // "2026-04-06" - if (!daily[date]) { - daily[date] = { temps: [], rains: [], winds: [] }; - } - daily[date].temps.push(item.temp_2_cel); - daily[date].rains.push(item.prate); - daily[date].winds.push(item.wind_speed_10); - }); - - // Вычисляем средние за день - const dates = []; - const temps = []; - const rains = []; - const winds = []; - - for (const [date, values] of Object.entries(daily)) { - dates.push(date); - const avgTemp = values.temps.reduce((a,b) => a+b,0) / values.temps.length; - temps.push(avgTemp.toFixed(1)); - const totalRain = values.rains.reduce((a,b) => a+b,0); - rains.push(totalRain.toFixed(1)); - const avgWind = values.winds.reduce((a,b) => a+b,0) / values.winds.length; - winds.push(avgWind.toFixed(1)); + + // Проверяем, существует ли уже график и можно ли его обновить + if (window[chartId] && typeof window[chartId].destroy === 'function') { + window[chartId].destroy(); + window[chartId] = null; } - - document.getElementById('weather-title').innerHTML = `🌤️ Прогноз погоды: ${cityName}`; - updateChart('tempChart', dates, temps, 'Температура (°C)', 'rgba(255,99,132,0.2)', 'rgba(255,99,132,1)', 'line'); - updateChart('rainChart', dates, rains, 'Осадки (мм)', 'rgba(54,162,235,0.2)', 'rgba(54,162,235,1)', 'bar'); - updateChart('windChart', dates, winds, 'Ветер (м/с)', 'rgba(75,192,192,0.2)', 'rgba(75,192,192,1)', 'line'); -} - -// ------------------- 7. ОТРИСОВКА ГРАФИКОВ ------------------- -function updateChart(chartId, labels, data, label, bgColor, borderColor, type) { - const ctx = document.getElementById(chartId).getContext('2d'); - if (window[chartId]) { - window[chartId].data.labels = labels; - window[chartId].data.datasets[0].data = data; - window[chartId].update(); - } else { - window[chartId] = new Chart(ctx, { + + try { + // Создаём новый график + window[chartId] = new Chart(canvas.getContext('2d'), { type: type, data: { labels: labels, @@ -247,12 +141,14 @@ function updateChart(chartId, labels, data, label, bgColor, borderColor, type) { maintainAspectRatio: false } }); + } catch(e) { + console.error(`Ошибка создания графика ${chartId}:`, e); } - // Убираем заглушку "нажмите на город" + + // Скрываем заглушку const infoMsg = document.getElementById('info-message'); if (infoMsg) infoMsg.style.display = 'none'; } -// Инициализация карты и загрузка городов -document.addEventListener('DOMContentLoaded', () => { - loadCities(); // Это нужно добавить! -}); \ No newline at end of file + +// ------------------- 6. ЗАПУСК ------------------- +document.addEventListener('DOMContentLoaded', loadCities); \ No newline at end of file From 0893fff3e650afe9fbdca0ad54f304560db949cf Mon Sep 17 00:00:00 2001 From: Mary Date: Tue, 7 Apr 2026 20:45:40 +0400 Subject: [PATCH 6/7] =?UTF-8?q?=D0=BD=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C=D1=88?= =?UTF-8?q?=D0=BE=D0=B5=20=D0=BA=D0=BE=D1=81=D0=BC=D0=B5=D1=82=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cities.json | 6174 +++++++++++++++++++++++++++++++++++++++++-- docker-compose.yaml | 2 +- index.html | 4 +- script.js | 21 +- style.css | 12 +- 5 files changed, 6004 insertions(+), 209 deletions(-) diff --git a/cities.json b/cities.json index 80b04dfd..ea37ad93 100644 --- a/cities.json +++ b/cities.json @@ -1,176 +1,6002 @@ [ - { "name": "Москва", "lat": 55.7558, "lon": 37.6173, "population": 12655050 }, - { "name": "Санкт-Петербург", "lat": 59.9343, "lon": 30.3351, "population": 5384342 }, - { "name": "Новосибирск", "lat": 55.0084, "lon": 82.9357, "population": 1625631 }, - { "name": "Екатеринбург", "lat": 56.8389, "lon": 60.6057, "population": 1493749 }, - { "name": "Казань", "lat": 55.7961, "lon": 49.1064, "population": 1257391 }, - { "name": "Нижний Новгород", "lat": 56.2965, "lon": 43.9361, "population": 1252236 }, - { "name": "Челябинск", "lat": 55.1644, "lon": 61.4368, "population": 1196680 }, - { "name": "Самара", "lat": 53.1952, "lon": 50.1069, "population": 1156644 }, - { "name": "Омск", "lat": 54.9885, "lon": 73.3242, "population": 1154116 }, - { "name": "Ростов-на-Дону", "lat": 47.2357, "lon": 39.7015, "population": 1137904 }, - { "name": "Уфа", "lat": 54.7388, "lon": 55.9721, "population": 1128787 }, - { "name": "Красноярск", "lat": 56.0153, "lon": 92.8932, "population": 1093771 }, - { "name": "Воронеж", "lat": 51.6720, "lon": 39.1843, "population": 1058261 }, - { "name": "Пермь", "lat": 58.0105, "lon": 56.2502, "population": 1055397 }, - { "name": "Волгоград", "lat": 48.7080, "lon": 44.5133, "population": 1008998 }, - { "name": "Краснодар", "lat": 45.0355, "lon": 38.9753, "population": 948827 }, - { "name": "Саратов", "lat": 51.5924, "lon": 46.0348, "population": 901361 }, - { "name": "Тюмень", "lat": 57.1522, "lon": 65.5272, "population": 847488 }, - { "name": "Тольятти", "lat": 53.5303, "lon": 49.3461, "population": 699631 }, - { "name": "Ижевск", "lat": 56.8527, "lon": 53.2041, "population": 648944 }, - { "name": "Барнаул", "lat": 53.3606, "lon": 83.7636, "population": 630877 }, - { "name": "Ульяновск", "lat": 54.3141, "lon": 48.4031, "population": 623013 }, - { "name": "Иркутск", "lat": 52.2870, "lon": 104.2805, "population": 623562 }, - { "name": "Хабаровск", "lat": 48.4827, "lon": 135.0838, "population": 616372 }, - { "name": "Ярославль", "lat": 57.6261, "lon": 39.8845, "population": 608079 }, - { "name": "Владивосток", "lat": 43.1056, "lon": 131.8735, "population": 606589 }, - { "name": "Махачкала", "lat": 42.9849, "lon": 47.5047, "population": 603518 }, - { "name": "Томск", "lat": 56.4977, "lon": 84.9744, "population": 576624 }, - { "name": "Оренбург", "lat": 51.7727, "lon": 55.0988, "population": 572188 }, - { "name": "Кемерово", "lat": 55.3331, "lon": 86.0831, "population": 558973 }, - { "name": "Новокузнецк", "lat": 53.7557, "lon": 87.1099, "population": 547904 }, - { "name": "Рязань", "lat": 54.6269, "lon": 39.6916, "population": 539290 }, - { "name": "Астрахань", "lat": 46.3497, "lon": 48.0408, "population": 529793 }, - { "name": "Набережные Челны", "lat": 55.7251, "lon": 52.4069, "population": 533907 }, - { "name": "Пенза", "lat": 53.2001, "lon": 45.0000, "population": 520300 }, - { "name": "Липецк", "lat": 52.6031, "lon": 39.5708, "population": 508887 }, - { "name": "Киров", "lat": 58.6035, "lon": 49.6679, "population": 507155 }, - { "name": "Чебоксары", "lat": 56.1439, "lon": 47.2486, "population": 497266 }, - { "name": "Тула", "lat": 54.1931, "lon": 37.6172, "population": 475161 }, - { "name": "Калининград", "lat": 54.7104, "lon": 20.4522, "population": 482443 }, - { "name": "Балашиха", "lat": 55.7964, "lon": 37.9378, "population": 507692 }, - { "name": "Ставрополь", "lat": 45.0448, "lon": 41.9690, "population": 458784 }, - { "name": "Севастополь", "lat": 44.6167, "lon": 33.5254, "population": 449130 }, - { "name": "Улан-Удэ", "lat": 51.8272, "lon": 107.6063, "population": 439128 }, - { "name": "Тверь", "lat": 56.8584, "lon": 35.9176, "population": 424969 }, - { "name": "Магнитогорск", "lat": 53.4078, "lon": 59.0464, "population": 413351 }, - { "name": "Иваново", "lat": 56.9719, "lon": 40.9714, "population": 404300 }, - { "name": "Брянск", "lat": 53.2434, "lon": 34.3656, "population": 402300 }, - { "name": "Сочи", "lat": 43.6028, "lon": 39.7342, "population": 443562 }, - { "name": "Белгород", "lat": 50.5956, "lon": 36.5871, "population": 391554 }, - { "name": "Сургут", "lat": 61.2500, "lon": 73.4167, "population": 395705 }, - { "name": "Владимир", "lat": 56.1366, "lon": 40.3966, "population": 349951 }, - { "name": "Архангельск", "lat": 64.5401, "lon": 40.5433, "population": 348783 }, - { "name": "Нижний Тагил", "lat": 57.9197, "lon": 59.9650, "population": 355694 }, - { "name": "Чита", "lat": 52.0333, "lon": 113.5000, "population": 349005 }, - { "name": "Калуга", "lat": 54.5293, "lon": 36.2754, "population": 336726 }, - { "name": "Смоленск", "lat": 54.7818, "lon": 32.0401, "population": 326863 }, - { "name": "Курган", "lat": 55.4500, "lon": 65.3333, "population": 310951 }, - { "name": "Волжский", "lat": 48.7854, "lon": 44.7758, "population": 323293 }, - { "name": "Орёл", "lat": 52.9651, "lon": 36.0785, "population": 310755 }, - { "name": "Череповец", "lat": 59.1333, "lon": 37.9000, "population": 315744 }, - { "name": "Грозный", "lat": 43.3175, "lon": 45.6986, "population": 324370 }, - { "name": "Якутск", "lat": 62.0355, "lon": 129.6755, "population": 318457 }, - { "name": "Мурманск", "lat": 68.9585, "lon": 33.0827, "population": 295374 }, - { "name": "Петрозаводск", "lat": 61.7849, "lon": 34.3469, "population": 280662 }, - { "name": "Саранск", "lat": 54.1838, "lon": 45.1749, "population": 314789 }, - { "name": "Кострома", "lat": 57.7665, "lon": 40.9265, "population": 276662 }, - { "name": "Таганрог", "lat": 47.2362, "lon": 38.9349, "population": 245120 }, - { "name": "Комсомольск-на-Амуре", "lat": 50.5500, "lon": 137.0000, "population": 248280 }, - { "name": "Сыктывкар", "lat": 61.6681, "lon": 50.8357, "population": 235006 }, - { "name": "Нижневартовск", "lat": 60.9344, "lon": 76.5531, "population": 279988 }, - { "name": "Йошкар-Ола", "lat": 56.6372, "lon": 47.8908, "population": 279100 }, - { "name": "Новороссийск", "lat": 44.7231, "lon": 37.7686, "population": 261430 }, - { "name": "Дзержинск", "lat": 56.2333, "lon": 43.4667, "population": 230000 }, - { "name": "Нальчик", "lat": 43.4981, "lon": 43.6189, "population": 239300 }, - { "name": "Шахты", "lat": 47.7000, "lon": 40.2167, "population": 221845 }, - { "name": "Энгельс", "lat": 51.4833, "lon": 46.1167, "population": 222918 }, - { "name": "Рыбинск", "lat": 58.0500, "lon": 38.8500, "population": 182411 }, - { "name": "Норильск", "lat": 69.3558, "lon": 88.1893, "population": 175365 }, - { "name": "Альметьевск", "lat": 54.9000, "lon": 52.3000, "population": 155329 }, - { "name": "Псков", "lat": 57.8136, "lon": 28.3300, "population": 210501 }, - { "name": "Биийск", "lat": 52.5333, "lon": 85.3333, "population": 197164 }, - { "name": "Люберцы", "lat": 55.6758, "lon": 37.8933, "population": 201654 }, - { "name": "Прокопьевск", "lat": 53.9000, "lon": 86.7167, "population": 197164 }, - { "name": "Мытищи", "lat": 55.9116, "lon": 37.7307, "population": 223067 }, - { "name": "Златоуст", "lat": 55.1667, "lon": 59.6500, "population": 163367 }, - { "name": "Каменск-Уральский", "lat": 56.4167, "lon": 61.9333, "population": 168406 }, - { "name": "Подольск", "lat": 55.4297, "lon": 37.5447, "population": 187961 }, - { "name": "Петропавловск-Камчатский", "lat": 53.0446, "lon": 158.6504, "population": 179526 }, - { "name": "Сызрань", "lat": 53.1667, "lon": 48.4667, "population": 163913 }, - { "name": "Ачинск", "lat": 56.2667, "lon": 90.5000, "population": 107656 }, - { "name": "Новочеркасск", "lat": 47.4167, "lon": 40.1000, "population": 168746 }, - { "name": "Электросталь", "lat": 55.7894, "lon": 38.4469, "population": 158652 }, - { "name": "Первоуральск", "lat": 56.9000, "lon": 59.9500, "population": 123211 }, - { "name": "Одинцово", "lat": 55.6758, "lon": 37.2808, "population": 137041 }, - { "name": "Копейск", "lat": 55.1167, "lon": 61.6167, "population": 148872 }, - { "name": "Хасавюрт", "lat": 43.2500, "lon": 46.5833, "population": 143960 }, - { "name": "Нефтекамск", "lat": 56.0833, "lon": 54.2667, "population": 133997 }, - { "name": "Новочебоксарск", "lat": 56.1167, "lon": 47.5000, "population": 129082 }, - { "name": "Серпухов", "lat": 54.9167, "lon": 37.4167, "population": 126522 }, - { "name": "Невинномысск", "lat": 44.6333, "lon": 41.9333, "population": 118360 }, - { "name": "Дмитров", "lat": 56.3500, "lon": 37.5167, "population": 61305 }, - { "name": "Обнинск", "lat": 55.0833, "lon": 36.6167, "population": 120000 }, - { "name": "Ангарск", "lat": 52.5333, "lon": 103.9000, "population": 225400 }, - { "name": "Щёлково", "lat": 55.9167, "lon": 38.0333, "population": 124115 }, - { "name": "Батайск", "lat": 47.1333, "lon": 39.7500, "population": 123549 }, - { "name": "Кисловодск", "lat": 43.9000, "lon": 42.7167, "population": 128553 }, - { "name": "Орехово-Зуево", "lat": 55.8000, "lon": 38.9667, "population": 120670 }, - { "name": "Майкоп", "lat": 44.6000, "lon": 40.1000, "population": 144246 }, - { "name": "Пушкино", "lat": 56.0167, "lon": 37.8500, "population": 111000 }, - { "name": "Армавир", "lat": 44.9833, "lon": 41.1167, "population": 188832 }, - { "name": "Людино", "lat": 54.7000, "lon": 34.4500, "population": 77000 }, - { "name": "Кызыл", "lat": 51.7167, "lon": 94.4500, "population": 119633 }, - { "name": "Уссурийск", "lat": 43.8000, "lon": 131.9500, "population": 158016 }, - { "name": "Березники", "lat": 59.4167, "lon": 56.8167, "population": 141762 }, - { "name": "Салават", "lat": 53.3667, "lon": 55.9167, "population": 151100 }, - { "name": "Миасс", "lat": 55.0333, "lon": 60.1000, "population": 151751 }, - { "name": "Абакан", "lat": 53.7167, "lon": 91.4333, "population": 181700 }, - { "name": "Ноябрьск", "lat": 63.2000, "lon": 75.4500, "population": 110620 }, - { "name": "Ессентуки", "lat": 44.0500, "lon": 42.8667, "population": 111600 }, - { "name": "Железногорск", "lat": 56.2500, "lon": 93.5333, "population": 93734 }, - { "name": "Красногорск", "lat": 55.7333, "lon": 37.3333, "population": 179683 }, - { "name": "Новый Уренгой", "lat": 66.0833, "lon": 76.6333, "population": 118619 }, - { "name": "Муром", "lat": 55.5667, "lon": 42.0500, "population": 106200 }, - { "name": "Коломна", "lat": 55.0833, "lon": 38.7833, "population": 144589 }, - { "name": "Ковров", "lat": 56.3500, "lon": 41.3167, "population": 137111 }, - { "name": "Пятигорск", "lat": 44.0500, "lon": 43.0500, "population": 149696 }, - { "name": "Химки", "lat": 55.8972, "lon": 37.4297, "population": 259550 }, - { "name": "Троицк", "lat": 55.4833, "lon": 37.3000, "population": 61519 }, - { "name": "Реутов", "lat": 55.7667, "lon": 37.8667, "population": 107022 }, - { "name": "Долгопрудный", "lat": 55.9333, "lon": 37.5167, "population": 123000 }, - { "name": "Раменское", "lat": 55.5667, "lon": 38.2333, "population": 122000 }, - { "name": "Жуковский", "lat": 55.6000, "lon": 38.1167, "population": 107585 }, - { "name": "Лобня", "lat": 56.0167, "lon": 37.4833, "population": 87000 }, - { "name": "Клин", "lat": 56.3333, "lon": 36.7333, "population": 78000 }, - { "name": "Сергиев Посад", "lat": 56.3000, "lon": 38.1333, "population": 104000 }, - { "name": "Ногинск", "lat": 55.8667, "lon": 38.4333, "population": 101000 }, - { "name": "Егорьевск", "lat": 55.3833, "lon": 39.0333, "population": 70000 }, - { "name": "Ступино", "lat": 55.2833, "lon": 38.0833, "population": 66000 }, - { "name": "Воскресенск", "lat": 55.3167, "lon": 38.6667, "population": 91000 }, - { "name": "Дубна", "lat": 56.7333, "lon": 37.1667, "population": 75000 }, - { "name": "Фрязино", "lat": 55.9500, "lon": 38.0500, "population": 57000 }, - { "name": "Домодедово", "lat": 55.4333, "lon": 37.7667, "population": 125000 }, - { "name": "Истра", "lat": 55.9167, "lon": 36.8500, "population": 35000 }, - { "name": "Чехов", "lat": 55.1500, "lon": 37.4667, "population": 75000 }, - { "name": "Наро-Фоминск", "lat": 55.3833, "lon": 36.7333, "population": 64000 }, - { "name": "Павловский Посад", "lat": 55.7833, "lon": 38.6500, "population": 61000 }, - { "name": "Звенигород", "lat": 55.7333, "lon": 36.8500, "population": 18000 }, - { "name": "Руза", "lat": 55.7000, "lon": 36.1833, "population": 13000 }, - { "name": "Верея", "lat": 55.3500, "lon": 36.2000, "population": 12000 }, - { "name": "Можайск", "lat": 55.5000, "lon": 36.0333, "population": 30000 }, - { "name": "Волоколамск", "lat": 56.0333, "lon": 35.9500, "population": 21000 }, - { "name": "Шаховская", "lat": 56.0167, "lon": 35.5333, "population": 11000 }, - { "name": "Лотошино", "lat": 56.0833, "lon": 35.8000, "population": 8000 }, - { "name": "Талдом", "lat": 56.7333, "lon": 37.5333, "population": 13000 }, - { "name": "Яхрома", "lat": 56.2833, "lon": 37.4833, "population": 13000 }, - { "name": "Краснозаводск", "lat": 56.3167, "lon": 37.9000, "population": 13000 }, - { "name": "Пересвет", "lat": 56.2833, "lon": 38.1667, "population": 5000 }, - { "name": "Апрелевка", "lat": 55.4833, "lon": 37.2000, "population": 23000 }, - { "name": "Щербинка", "lat": 55.5000, "lon": 37.5667, "population": 28000 }, - { "name": "Московский", "lat": 55.6000, "lon": 37.3500, "population": 60000 }, - { "name": "Кокошкино", "lat": 55.6167, "lon": 37.2500, "population": 14000 }, - { "name": "Кремёнки", "lat": 55.3833, "lon": 37.4500, "population": 8000 }, - { "name": "Протвино", "lat": 54.8667, "lon": 37.2333, "population": 13000 }, - { "name": "Пущино", "lat": 54.8333, "lon": 37.6167, "population": 21000 }, - { "name": "Молодёжный", "lat": 55.5667, "lon": 37.2167, "population": 9000 }, - { "name": "Внуково", "lat": 55.6000, "lon": 37.3500, "population": 8000 }, - { "name": "Михайловское", "lat": 55.5500, "lon": 37.3000, "population": 7000 }, - { "name": "Филимонки", "lat": 55.5667, "lon": 37.3833, "population": 6000 }, - { "name": "Марушкино", "lat": 55.6167, "lon": 37.2833, "population": 5000 }, - { "name": "Румянцево", "lat": 55.6333, "lon": 37.3833, "population": 4000 }, - { "name": "Сосенки", "lat": 55.5833, "lon": 37.4167, "population": 3000 }, - { "name": "Газопровод", "lat": 55.6000, "lon": 37.4000, "population": 2000 }, - { "name": "Коммунарка", "lat": 55.5667, "lon": 37.5167, "population": 25000 } + { + "name": "Новосибирск", + "lat": 55.030187, + "lon": 82.920426, + "population": 1633595 + }, + { + "name": "Екатеринбург", + "lat": 56.8387061, + "lon": 60.6054943, + "population": 1544376 + }, + { + "name": "Казань", + "lat": 55.7944326, + "lon": 49.1115164, + "population": 1308660 + }, + { + "name": "Нижний Новгород", + "lat": 56.326794, + "lon": 44.006495, + "population": 1226076 + }, + { + "name": "Челябинск", + "lat": 55.1602292, + "lon": 61.4007614, + "population": 1189525 + }, + { + "name": "Красноярск", + "lat": 56.0095312, + "lon": 92.8525234, + "population": 1187771 + }, + { + "name": "Самара", + "lat": 53.1951774, + "lon": 50.1069625, + "population": 1173299 + }, + { + "name": "Уфа", + "lat": 54.734856, + "lon": 55.9577802, + "population": 1144809 + }, + { + "name": "Ростов-на-Дону", + "lat": 47.2180329, + "lon": 39.6902714, + "population": 1142162 + }, + { + "name": "Омск", + "lat": 54.9849581, + "lon": 73.3675684, + "population": 1125695 + }, + { + "name": "Краснодар", + "lat": 45.035453, + "lon": 38.975301, + "population": 1099344 + }, + { + "name": "Воронеж", + "lat": 51.6593943, + "lon": 39.196922, + "population": 1057681 + }, + { + "name": "Пермь", + "lat": 58.0102668, + "lon": 56.2342725, + "population": 1034002 + }, + { + "name": "Волгоград", + "lat": 48.7071653, + "lon": 44.5170212, + "population": 1028036 + }, + { + "name": "Саратов", + "lat": 51.533557, + "lon": 46.034257, + "population": 901361 + }, + { + "name": "Тюмень", + "lat": 57.1531178, + "lon": 65.5343535, + "population": 847488 + }, + { + "name": "Тольятти", + "lat": 53.5205003, + "lon": 49.3895011, + "population": 684709 + }, + { + "name": "Барнаул", + "lat": 53.3479774, + "lon": 83.7798977, + "population": 630877 + }, + { + "name": "Ижевск", + "lat": 56.8528384, + "lon": 53.211509, + "population": 623472 + }, + { + "name": "Махачкала", + "lat": 42.9849039, + "lon": 47.5045541, + "population": 623254 + }, + { + "name": "Хабаровск", + "lat": 48.4648124, + "lon": 135.0598539, + "population": 617441 + }, + { + "name": "Ульяновск", + "lat": 54.3079032, + "lon": 48.3748473, + "population": 617352 + }, + { + "name": "Иркутск", + "lat": 52.2863483, + "lon": 104.2806793, + "population": 617264 + }, + { + "name": "Владивосток", + "lat": 43.1163208, + "lon": 131.8824988, + "population": 603519 + }, + { + "name": "Ярославль", + "lat": 57.6216293, + "lon": 39.8978921, + "population": 577279 + }, + { + "name": "Кемерово", + "lat": 55.3910176, + "lon": 86.0469193, + "population": 557119 + }, + { + "name": "Томск", + "lat": 56.4846736, + "lon": 84.9482929, + "population": 556478 + }, + { + "name": "Набережные Челны", + "lat": 55.6948843, + "lon": 52.3279115, + "population": 548434 + }, + { + "name": "Ставрополь", + "lat": 45.0444563, + "lon": 41.9691349, + "population": 547443 + }, + { + "name": "Оренбург", + "lat": 51.7875335, + "lon": 55.1017803, + "population": 543654 + }, + { + "name": "Новокузнецк", + "lat": 53.7943459, + "lon": 87.2142908, + "population": 537480 + }, + { + "name": "Рязань", + "lat": 54.6255345, + "lon": 39.7359162, + "population": 528599 + }, + { + "name": "Балашиха", + "lat": 55.796991, + "lon": 37.938104, + "population": 520962 + }, + { + "name": "Пенза", + "lat": 53.1753947, + "lon": 45.0347408, + "population": 501109 + }, + { + "name": "Чебоксары", + "lat": 56.125485, + "lon": 47.377328, + "population": 497807 + }, + { + "name": "Липецк", + "lat": 52.6101389, + "lon": 39.5947287, + "population": 496403 + }, + { + "name": "Калининград", + "lat": 54.7074298, + "lon": 20.5073482, + "population": 490449 + }, + { + "name": "Астрахань", + "lat": 46.3656435, + "lon": 48.0559229, + "population": 475629 + }, + { + "name": "Тула", + "lat": 54.1918898, + "lon": 37.6153644, + "population": 473622 + }, + { + "name": "Киров", + "lat": 58.6034972, + "lon": 49.6680445, + "population": 468212 + }, + { + "name": "Сочи", + "lat": 43.5855182, + "lon": 39.7229922, + "population": 466078 + }, + { + "name": "Курск", + "lat": 51.7303825, + "lon": 36.1925546, + "population": 440052 + }, + { + "name": "Улан-Удэ", + "lat": 51.8334492, + "lon": 107.584068, + "population": 437565 + }, + { + "name": "Тверь", + "lat": 56.8586556, + "lon": 35.9118279, + "population": 416219 + }, + { + "name": "Магнитогорск", + "lat": 53.4072599, + "lon": 58.9791822, + "population": 410594 + }, + { + "name": "Сургут", + "lat": 61.2541492, + "lon": 73.396236, + "population": 396443 + }, + { + "name": "Брянск", + "lat": 53.2420053, + "lon": 34.3653002, + "population": 379152 + }, + { + "name": "Иваново", + "lat": 56.9994066, + "lon": 40.9729071, + "population": 361644 + }, + { + "name": "Якутск", + "lat": 62.0280605, + "lon": 129.7324858, + "population": 355443 + }, + { + "name": "Владимир", + "lat": 56.1280444, + "lon": 40.4082983, + "population": 349951 + }, + { + "name": "Симферополь", + "lat": 44.9482789, + "lon": 34.1001275, + "population": 340540 + }, + { + "name": "Белгород", + "lat": 50.5975582, + "lon": 36.5858422, + "population": 339978 + }, + { + "name": "Нижний Тагил", + "lat": 57.9102289, + "lon": 59.9814188, + "population": 338966 + }, + { + "name": "Калуга", + "lat": 54.506025, + "lon": 36.2516145, + "population": 337058 + }, + { + "name": "Чита", + "lat": 52.0338828, + "lon": 113.499487, + "population": 334427 + }, + { + "name": "Грозный", + "lat": 43.3178923, + "lon": 45.6981421, + "population": 328533 + }, + { + "name": "Волжский", + "lat": 48.7979775, + "lon": 44.7462572, + "population": 321479 + }, + { + "name": "город Смоленск", + "lat": 54.782635, + "lon": 32.045251, + "population": 316570 + }, + { + "name": "Подольск", + "lat": 55.4389616, + "lon": 37.5703052, + "population": 314934 + }, + { + "name": "Саранск", + "lat": 54.1808234, + "lon": 45.1863143, + "population": 314871 + }, + { + "name": "Вологда", + "lat": 59.220501, + "lon": 39.891523, + "population": 313944 + }, + { + "name": "Курган", + "lat": 55.4443683, + "lon": 65.3162142, + "population": 310911 + }, + { + "name": "Череповец", + "lat": 59.1268743, + "lon": 37.9090556, + "population": 305185 + }, + { + "name": "Орёл", + "lat": 52.9671928, + "lon": 36.0695725, + "population": 303169 + }, + { + "name": "Архангельск", + "lat": 64.53939, + "lon": 40.5170011, + "population": 301199 + }, + { + "name": "Владикавказ", + "lat": 43.020552, + "lon": 44.681905, + "population": 295830 + }, + { + "name": "Нижневартовск", + "lat": 60.9397399, + "lon": 76.5695505, + "population": 283256 + }, + { + "name": "Йошкар-Ола", + "lat": 56.6343266, + "lon": 47.8999167, + "population": 281248 + }, + { + "name": "Стерлитамак", + "lat": 53.6302062, + "lon": 55.9317313, + "population": 277410 + }, + { + "name": "Мурманск", + "lat": 68.970663, + "lon": 33.074918, + "population": 270384 + }, + { + "name": "Кострома", + "lat": 57.768, + "lon": 40.927, + "population": 267481 + }, + { + "name": "Новороссийск", + "lat": 44.7234226, + "lon": 37.7686906, + "population": 262293 + }, + { + "name": "Тамбов", + "lat": 52.7211576, + "lon": 41.4522156, + "population": 261803 + }, + { + "name": "Химки", + "lat": 55.8888467, + "lon": 37.430372, + "population": 257128 + }, + { + "name": "Мытищи", + "lat": 55.9104655, + "lon": 37.7363923, + "population": 255429 + }, + { + "name": "Нальчик", + "lat": 43.4846449, + "lon": 43.6070943, + "population": 247054 + }, + { + "name": "Таганрог", + "lat": 47.2096599, + "lon": 38.9352459, + "population": 245120 + }, + { + "name": "Нижнекамск", + "lat": 55.6312304, + "lon": 51.8144719, + "population": 241479 + }, + { + "name": "Благовещенск", + "lat": 50.290659, + "lon": 127.527198, + "population": 241437 + }, + { + "name": "Комсомольск-на-Амуре", + "lat": 50.5525092, + "lon": 136.9895507, + "population": 238505 + }, + { + "name": "Петрозаводск", + "lat": 61.7889739, + "lon": 34.3596538, + "population": 234897 + }, + { + "name": "Королёв", + "lat": 55.9161885, + "lon": 37.854422, + "population": 228095 + }, + { + "name": "Шахты", + "lat": 47.7085649, + "lon": 40.2158665, + "population": 226452 + }, + { + "name": "Энгельс", + "lat": 51.4855068, + "lon": 46.1267008, + "population": 225428 + }, + { + "name": "Великий Новгород", + "lat": 58.5214164, + "lon": 31.2754186, + "population": 224286 + }, + { + "name": "Люберцы", + "lat": 55.676499, + "lon": 37.898125, + "population": 224195 + }, + { + "name": "Братск", + "lat": 56.1514087, + "lon": 101.6341027, + "population": 224071 + }, + { + "name": "Старый Оскол", + "lat": 51.297974, + "lon": 37.833231, + "population": 221676 + }, + { + "name": "город Ангарск", + "lat": 52.544898, + "lon": 103.8884509, + "population": 221296 + }, + { + "name": "Сыктывкар", + "lat": 61.6687212, + "lon": 50.8356788, + "population": 220580 + }, + { + "name": "Дзержинск", + "lat": 56.2375332, + "lon": 43.4598979, + "population": 218630 + }, + { + "name": "Псков", + "lat": 57.819825, + "lon": 28.3950685, + "population": 193082 + }, + { + "name": "Орск", + "lat": 51.2292952, + "lon": 58.4752502, + "population": 189195 + }, + { + "name": "Красногорск", + "lat": 55.8318647, + "lon": 37.3294489, + "population": 187634 + }, + { + "name": "Армавир", + "lat": 45.0011356, + "lon": 41.1324283, + "population": 187177 + }, + { + "name": "Абакан", + "lat": 53.7223055, + "lon": 91.4436926, + "population": 184769 + }, + { + "name": "Балаково", + "lat": 52.0222817, + "lon": 47.7827796, + "population": 184466 + }, + { + "name": "Бийск", + "lat": 52.5392784, + "lon": 85.213843, + "population": 183852 + }, + { + "name": "Южно-Сахалинск", + "lat": 46.9667029, + "lon": 142.7212958, + "population": 181587 + }, + { + "name": "Одинцово", + "lat": 55.6790296, + "lon": 37.2635177, + "population": 180530 + }, + { + "name": "Уссурийск", + "lat": 43.8048438, + "lon": 131.9367538, + "population": 180393 + }, + { + "name": "Прокопьевск", + "lat": 53.8604228, + "lon": 86.7183302, + "population": 177819 + }, + { + "name": "Рыбинск", + "lat": 58.0484021, + "lon": 38.858475, + "population": 177295 + }, + { + "name": "Норильск", + "lat": 69.3489906, + "lon": 88.2009379, + "population": 174453 + }, + { + "name": "Волгодонск", + "lat": 47.5165757, + "lon": 42.1984573, + "population": 168048 + }, + { + "name": "Сызрань", + "lat": 53.1557162, + "lon": 48.4744217, + "population": 165725 + }, + { + "name": "Петропавловск-Камчатский", + "lat": 53.0370074, + "lon": 158.65595, + "population": 164900 + }, + { + "name": "Каменск-Уральский", + "lat": 56.4148539, + "lon": 61.9188448, + "population": 164192 + }, + { + "name": "Новочеркасск", + "lat": 47.4118469, + "lon": 40.1040646, + "population": 163674 + }, + { + "name": "Альметьевск", + "lat": 54.9014385, + "lon": 52.297123, + "population": 163512 + }, + { + "name": "Златоуст", + "lat": 55.1714213, + "lon": 59.6726478, + "population": 161774 + }, + { + "name": "Северодвинск", + "lat": 64.5625626, + "lon": 39.8182183, + "population": 157213 + }, + { + "name": "Хасавюрт", + "lat": 43.2504341, + "lon": 46.5850867, + "population": 155144 + }, + { + "name": "Керчь", + "lat": 45.3561252, + "lon": 36.4673129, + "population": 154621 + }, + { + "name": "Домодедово", + "lat": 55.436403, + "lon": 37.7666001, + "population": 152404 + }, + { + "name": "Салават", + "lat": 53.362823, + "lon": 55.9154367, + "population": 148575 + }, + { + "name": "Миасс", + "lat": 55.0552642, + "lon": 60.0791765, + "population": 147995 + }, + { + "name": "Копейск", + "lat": 55.1167381, + "lon": 61.6179305, + "population": 147806 + }, + { + "name": "Пятигорск", + "lat": 44.041234, + "lon": 43.0660504, + "population": 146473 + }, + { + "name": "Электросталь", + "lat": 55.784633, + "lon": 38.444699, + "population": 146403 + }, + { + "name": "Майкоп", + "lat": 44.609792, + "lon": 40.1005698, + "population": 143385 + }, + { + "name": "Находка", + "lat": 42.8111483, + "lon": 132.8844502, + "population": 139931 + }, + { + "name": "Березники", + "lat": 59.4079119, + "lon": 56.8039179, + "population": 138069 + }, + { + "name": "Коломна", + "lat": 55.1026926, + "lon": 38.7530213, + "population": 134850 + }, + { + "name": "Щёлково", + "lat": 55.9233436, + "lon": 37.9783923, + "population": 134211 + }, + { + "name": "Серпухов", + "lat": 54.9226176, + "lon": 37.4033624, + "population": 133793 + }, + { + "name": "Ковров", + "lat": 56.3554229, + "lon": 41.3171096, + "population": 132417 + }, + { + "name": "Нефтекамск", + "lat": 56.0883752, + "lon": 54.2482411, + "population": 131942 + }, + { + "name": "Кисловодск", + "lat": 43.9052745, + "lon": 42.7167964, + "population": 127521 + }, + { + "name": "Батайск", + "lat": 47.1382139, + "lon": 39.750662, + "population": 126988 + }, + { + "name": "Рубцовск", + "lat": 51.5012963, + "lon": 81.2078042, + "population": 126834 + }, + { + "name": "Обнинск", + "lat": 55.0944881, + "lon": 36.6121565, + "population": 125376 + }, + { + "name": "Кызыл", + "lat": 51.7190588, + "lon": 94.4376984, + "population": 125241 + }, + { + "name": "Дербент", + "lat": 42.0590354, + "lon": 48.2908599, + "population": 124953 + }, + { + "name": "Нефтеюганск", + "lat": 61.0881688, + "lon": 72.6163369, + "population": 124732 + }, + { + "name": "Назрань", + "lat": 43.2256598, + "lon": 44.7646901, + "population": 122350 + }, + { + "name": "Каспийск", + "lat": 42.8916263, + "lon": 47.6366936, + "population": 121140 + }, + { + "name": "Долгопрудный", + "lat": 55.9385975, + "lon": 37.5100337, + "population": 120907 + }, + { + "name": "Новочебоксарск", + "lat": 56.1095538, + "lon": 47.4791596, + "population": 120375 + }, + { + "name": "Новомосковск", + "lat": 54.010957, + "lon": 38.2915047, + "population": 119697 + }, + { + "name": "Ессентуки", + "lat": 44.0444693, + "lon": 42.8589782, + "population": 119658 + }, + { + "name": "Невинномысск", + "lat": 44.6226742, + "lon": 41.9476424, + "population": 117562 + }, + { + "name": "Октябрьский", + "lat": 54.4814637, + "lon": 53.4654836, + "population": 115557 + }, + { + "name": "Раменское", + "lat": 55.5495378, + "lon": 38.2728835, + "population": 114537 + }, + { + "name": "Первоуральск", + "lat": 56.9080096, + "lon": 59.9429936, + "population": 114450 + }, + { + "name": "Михайловск", + "lat": 45.1297467, + "lon": 42.0287477, + "population": 114133 + }, + { + "name": "Реутов", + "lat": 55.7582708, + "lon": 37.8618288, + "population": 113871 + }, + { + "name": "Черкесск", + "lat": 44.228353, + "lon": 42.048278, + "population": 113226 + }, + { + "name": "Жуковский", + "lat": 55.5999747, + "lon": 38.1224066, + "population": 111222 + }, + { + "name": "Димитровград", + "lat": 54.2168413, + "lon": 49.6262388, + "population": 110968 + }, + { + "name": "Пушкино", + "lat": 56.0104754, + "lon": 37.847161, + "population": 110868 + }, + { + "name": "Артем", + "lat": 43.3501868, + "lon": 132.1595835, + "population": 109556 + }, + { + "name": "Камышин", + "lat": 50.0652955, + "lon": 45.384535, + "population": 107927 + }, + { + "name": "Евпатория", + "lat": 45.190629, + "lon": 33.367634, + "population": 107877 + }, + { + "name": "Муром", + "lat": 55.563153, + "lon": 42.0230298, + "population": 107497 + }, + { + "name": "Ханты-Мансийск", + "lat": 61.0025598, + "lon": 69.0183366, + "population": 107473 + }, + { + "name": "Новый Уренгой", + "lat": 66.0839904, + "lon": 76.6810104, + "population": 107251 + }, + { + "name": "Северск", + "lat": 56.6032343, + "lon": 84.881027, + "population": 106648 + }, + { + "name": "Орехово-Зуево", + "lat": 55.8164825, + "lon": 38.9368137, + "population": 105745 + }, + { + "name": "Арзамас", + "lat": 55.3946343, + "lon": 43.8408135, + "population": 104908 + }, + { + "name": "Ногинск", + "lat": 55.8760353, + "lon": 38.4231801, + "population": 103891 + }, + { + "name": "Новошахтинск", + "lat": 47.7576743, + "lon": 39.9364541, + "population": 103480 + }, + { + "name": "Бердск", + "lat": 54.7582184, + "lon": 83.1071527, + "population": 102850 + }, + { + "name": "Элиста", + "lat": 46.30821, + "lon": 44.2702746, + "population": 102583 + }, + { + "name": "Сергиев Посад", + "lat": 56.3063854, + "lon": 38.1502956, + "population": 101756 + }, + { + "name": "Видное", + "lat": 55.5517857, + "lon": 37.7062236, + "population": 101490 + }, + { + "name": "Ачинск", + "lat": 56.2537315, + "lon": 90.4793969, + "population": 100621 + }, + { + "name": "Тобольск", + "lat": 58.2017257, + "lon": 68.2538473, + "population": 100352 + }, + { + "name": "Ноябрьск", + "lat": 63.1920786, + "lon": 75.4454918, + "population": 100188 + }, + { + "name": "Елец", + "lat": 52.6152577, + "lon": 38.528899, + "population": 99875 + }, + { + "name": "Зеленодольск", + "lat": 55.8465861, + "lon": 48.500966, + "population": 99137 + }, + { + "name": "Новокуйбышевск", + "lat": 53.0994719, + "lon": 49.9477255, + "population": 98306 + }, + { + "name": "Воткинск", + "lat": 57.0518828, + "lon": 53.9874914, + "population": 97471 + }, + { + "name": "Железногорск", + "lat": 52.3379241, + "lon": 35.3517678, + "population": 97038 + }, + { + "name": "Междуреченск", + "lat": 53.6865918, + "lon": 88.0702782, + "population": 96174 + }, + { + "name": "Воскресенск", + "lat": 55.3070155, + "lon": 38.7028385, + "population": 95495 + }, + { + "name": "Гатчина", + "lat": 59.5651362, + "lon": 30.1281234, + "population": 94377 + }, + { + "name": "Серов", + "lat": 59.6047459, + "lon": 60.5752389, + "population": 94211 + }, + { + "name": "Саров", + "lat": 54.922813, + "lon": 43.3447953, + "population": 93357 + }, + { + "name": "Ленинск-Кузнецкий", + "lat": 54.6674801, + "lon": 86.1796673, + "population": 92244 + }, + { + "name": "Сарапул", + "lat": 56.4616582, + "lon": 53.8036826, + "population": 91115 + }, + { + "name": "Магадан", + "lat": 59.5681947, + "lon": 150.8086939, + "population": 90757 + }, + { + "name": "Мичуринск", + "lat": 52.8913406, + "lon": 40.5103713, + "population": 90451 + }, + { + "name": "Соликамск", + "lat": 59.6483298, + "lon": 56.7710196, + "population": 89473 + }, + { + "name": "Мурино", + "lat": 60.0448984, + "lon": 30.4573012, + "population": 89083 + }, + { + "name": "Чехов", + "lat": 55.1506335, + "lon": 37.4532854, + "population": 89025 + }, + { + "name": "Клин", + "lat": 56.3424345, + "lon": 36.7240443, + "population": 88511 + }, + { + "name": "Бузулук", + "lat": 52.7881715, + "lon": 52.2623964, + "population": 88341 + }, + { + "name": "Глазов", + "lat": 58.1359039, + "lon": 52.6634921, + "population": 87762 + }, + { + "name": "Канск", + "lat": 56.2050971, + "lon": 95.7051994, + "population": 86816 + }, + { + "name": "Великие Луки", + "lat": 56.343703, + "lon": 30.515671, + "population": 86711 + }, + { + "name": "Каменск-Шахтинский", + "lat": 48.3204742, + "lon": 40.2689608, + "population": 86365 + }, + { + "name": "Губкин", + "lat": 51.2836227, + "lon": 37.5347995, + "population": 85225 + }, + { + "name": "Киселевск", + "lat": 54.0383268, + "lon": 86.6605444, + "population": 83431 + }, + { + "name": "Ейск", + "lat": 46.7114714, + "lon": 38.2764767, + "population": 82943 + }, + { + "name": "Ивантеевка", + "lat": 55.9741839, + "lon": 37.9208717, + "population": 82827 + }, + { + "name": "Лобня", + "lat": 56.0328978, + "lon": 37.4614808, + "population": 82764 + }, + { + "name": "Железногорск", + "lat": 56.2528775, + "lon": 93.532226, + "population": 82723 + }, + { + "name": "Азов", + "lat": 47.1122112, + "lon": 39.4233596, + "population": 81924 + }, + { + "name": "Анапа", + "lat": 44.8948984, + "lon": 37.3162896, + "population": 81863 + }, + { + "name": "Бугульма", + "lat": 54.5364489, + "lon": 52.7894083, + "population": 81677 + }, + { + "name": "Московский", + "lat": 55.597255, + "lon": 37.359448, + "population": 81309 + }, + { + "name": "Геленджик", + "lat": 44.5631183, + "lon": 38.0791049, + "population": 80204 + }, + { + "name": "Ухта", + "lat": 63.5563841, + "lon": 53.7013827, + "population": 79899 + }, + { + "name": "Юрга", + "lat": 55.7202352, + "lon": 84.8885927, + "population": 79693 + }, + { + "name": "Усть-Илимск", + "lat": 58.0189147, + "lon": 102.6855314, + "population": 79570 + }, + { + "name": "Всеволожск", + "lat": 60.0190286, + "lon": 30.6456654, + "population": 79038 + }, + { + "name": "Новоуральск", + "lat": 57.2472236, + "lon": 60.0955422, + "population": 78479 + }, + { + "name": "Кузнецк", + "lat": 53.1130584, + "lon": 46.6052642, + "population": 78390 + }, + { + "name": "Бор", + "lat": 56.3565886, + "lon": 44.0645547, + "population": 78372 + }, + { + "name": "Кинешма", + "lat": 57.442535, + "lon": 42.1689258, + "population": 77694 + }, + { + "name": "Озерск", + "lat": 55.7632143, + "lon": 60.7076521, + "population": 76896 + }, + { + "name": "Новотроицк", + "lat": 51.1963401, + "lon": 58.3017089, + "population": 75960 + }, + { + "name": "Кропоткин", + "lat": 45.4332835, + "lon": 40.5728945, + "population": 75858 + }, + { + "name": "Чайковский", + "lat": 56.7780683, + "lon": 54.1477844, + "population": 75837 + }, + { + "name": "Черногорск", + "lat": 53.8258374, + "lon": 91.3259463, + "population": 75745 + }, + { + "name": "Усолье-Сибирское", + "lat": 52.7566823, + "lon": 103.6387415, + "population": 74762 + }, + { + "name": "Ялта", + "lat": 44.4952969, + "lon": 34.1663516, + "population": 74652 + }, + { + "name": "Дубна", + "lat": 56.7416155, + "lon": 37.1756531, + "population": 74183 + }, + { + "name": "Балашов", + "lat": 51.5388261, + "lon": 43.1839668, + "population": 74057 + }, + { + "name": "посёлок Коммунарка", + "lat": 55.570388, + "lon": 37.475469, + "population": 73735 + }, + { + "name": "Елабуга", + "lat": 55.7566788, + "lon": 52.0545017, + "population": 73630 + }, + { + "name": "Новоалтайск", + "lat": 53.4120027, + "lon": 83.9309813, + "population": 73049 + }, + { + "name": "Выборг", + "lat": 60.7129589, + "lon": 28.7328799, + "population": 72530 + }, + { + "name": "Егорьевск", + "lat": 55.3831202, + "lon": 39.0358883, + "population": 71686 + }, + { + "name": "Верхняя Пышма", + "lat": 56.9758868, + "lon": 60.5649684, + "population": 71335 + }, + { + "name": "Наро-Фоминск", + "lat": 55.386185, + "lon": 36.734484, + "population": 71121 + }, + { + "name": "Минеральные Воды", + "lat": 44.2087295, + "lon": 43.1383447, + "population": 70485 + }, + { + "name": "Троицк", + "lat": 54.0843322, + "lon": 61.5587093, + "population": 70301 + }, + { + "name": "Чапаевск", + "lat": 52.929023, + "lon": 49.8673246, + "population": 70228 + }, + { + "name": "Минусинск", + "lat": 53.7104585, + "lon": 91.6871667, + "population": 70089 + }, + { + "name": "Биробиджан", + "lat": 48.7946217, + "lon": 132.9217228, + "population": 70064 + }, + { + "name": "Шадринск", + "lat": 56.0871041, + "lon": 63.6297172, + "population": 68609 + }, + { + "name": "Белово", + "lat": 54.4221568, + "lon": 86.3037406, + "population": 68542 + }, + { + "name": "Туймазы", + "lat": 54.5999395, + "lon": 53.6950191, + "population": 68349 + }, + { + "name": "Сертолово", + "lat": 60.1446697, + "lon": 30.209571, + "population": 68241 + }, + { + "name": "Буйнакск", + "lat": 42.8213541, + "lon": 47.116407, + "population": 68121 + }, + { + "name": "Ишим", + "lat": 56.1104734, + "lon": 69.4797226, + "population": 67614 + }, + { + "name": "Кирово-Чепецк", + "lat": 58.5560037, + "lon": 50.0317344, + "population": 66651 + }, + { + "name": "Анжеро-Судженск", + "lat": 56.0787454, + "lon": 86.0200409, + "population": 66583 + }, + { + "name": "Феодосия", + "lat": 45.0320187, + "lon": 35.382384, + "population": 66293 + }, + { + "name": "Дмитров", + "lat": 56.3476745, + "lon": 37.5266195, + "population": 65574 + }, + { + "name": "Сосновый Бор", + "lat": 59.904224, + "lon": 29.092209, + "population": 65367 + }, + { + "name": "Горно-Алтайск", + "lat": 51.9581885, + "lon": 85.9603499, + "population": 65342 + }, + { + "name": "Лыткарино", + "lat": 55.5778267, + "lon": 37.9034779, + "population": 65212 + }, + { + "name": "Павловский Посад", + "lat": 55.780723, + "lon": 38.6596371, + "population": 65098 + }, + { + "name": "Троицк", + "lat": 55.4844981, + "lon": 37.3067485, + "population": 65043 + }, + { + "name": "Белорецк", + "lat": 53.967626, + "lon": 58.4100433, + "population": 64525 + }, + { + "name": "Ступино", + "lat": 54.886274, + "lon": 38.078228, + "population": 64412 + }, + { + "name": "Гудермес", + "lat": 43.3518339, + "lon": 46.1034832, + "population": 64376 + }, + { + "name": "Ишимбай", + "lat": 53.4545688, + "lon": 56.0440089, + "population": 64041 + }, + { + "name": "Донской", + "lat": 53.9678617, + "lon": 38.3372254, + "population": 63837 + }, + { + "name": "Котельники", + "lat": 55.6599375, + "lon": 37.8631583, + "population": 63728 + }, + { + "name": "Кстово", + "lat": 56.1432079, + "lon": 44.1663972, + "population": 63646 + }, + { + "name": "Урус-Мартан", + "lat": 43.120175, + "lon": 45.539276, + "population": 63449 + }, + { + "name": "Георгиевск", + "lat": 44.14794, + "lon": 43.474254, + "population": 63221 + }, + { + "name": "Клинцы", + "lat": 52.752875, + "lon": 32.2337792, + "population": 63059 + }, + { + "name": "Нягань", + "lat": 62.1454886, + "lon": 65.3945936, + "population": 63034 + }, + { + "name": "Славянск-на-Кубани", + "lat": 45.2604694, + "lon": 38.1259659, + "population": 62985 + }, + { + "name": "Кунгур", + "lat": 57.4284056, + "lon": 56.9438416, + "population": 62673 + }, + { + "name": "город Сунжа", + "lat": 43.3203629, + "lon": 45.0477848, + "population": 62078 + }, + { + "name": "Туапсе", + "lat": 44.1104007, + "lon": 39.0825379, + "population": 61571 + }, + { + "name": "Когалым", + "lat": 62.2639797, + "lon": 74.482946, + "population": 61441 + }, + { + "name": "Белогорск", + "lat": 50.921219, + "lon": 128.4737866, + "population": 61440 + }, + { + "name": "Лениногорск", + "lat": 54.5966119, + "lon": 52.4433347, + "population": 60993 + }, + { + "name": "город Россошь", + "lat": 50.1701985, + "lon": 39.6226453, + "population": 60879 + }, + { + "name": "Алексин", + "lat": 54.5084968, + "lon": 37.0478364, + "population": 60842 + }, + { + "name": "Кудрово", + "lat": 59.9076688, + "lon": 30.5119611, + "population": 60791 + }, + { + "name": "Борисоглебск", + "lat": 51.3654207, + "lon": 42.1009542, + "population": 60687 + }, + { + "name": "Фрязино", + "lat": 55.9589291, + "lon": 38.0409827, + "population": 60580 + }, + { + "name": "Гуково", + "lat": 48.0450292, + "lon": 39.9485638, + "population": 60361 + }, + { + "name": "Ревда", + "lat": 56.7987642, + "lon": 59.9071644, + "population": 60200 + }, + { + "name": "Прохладный", + "lat": 43.7590107, + "lon": 44.010051, + "population": 59938 + }, + { + "name": "Березовский", + "lat": 56.9136462, + "lon": 60.7852371, + "population": 59698 + }, + { + "name": "Белебей", + "lat": 54.1034535, + "lon": 54.1112181, + "population": 59195 + }, + { + "name": "Чистополь", + "lat": 55.3699331, + "lon": 50.6285823, + "population": 58815 + }, + { + "name": "Заречный", + "lat": 53.1961885, + "lon": 45.1691136, + "population": 58510 + }, + { + "name": "Будённовск", + "lat": 44.781467, + "lon": 44.164948, + "population": 58103 + }, + { + "name": "Кумертау", + "lat": 52.756471, + "lon": 55.7970469, + "population": 57949 + }, + { + "name": "Сальск", + "lat": 46.475234, + "lon": 41.541039, + "population": 57937 + }, + { + "name": "Дзержинский", + "lat": 55.6241057, + "lon": 37.8441593, + "population": 57918 + }, + { + "name": "Лабинск", + "lat": 44.6354147, + "lon": 40.7244209, + "population": 57428 + }, + { + "name": "Асбест", + "lat": 57.0052534, + "lon": 61.4581769, + "population": 57317 + }, + { + "name": "Искитим", + "lat": 54.626704, + "lon": 83.2949994, + "population": 57147 + }, + { + "name": "Павлово", + "lat": 55.965103, + "lon": 43.064848, + "population": 57116 + }, + { + "name": "Александров", + "lat": 56.3918756, + "lon": 38.7110582, + "population": 57053 + }, + { + "name": "Воркута", + "lat": 67.497434, + "lon": 64.0611813, + "population": 56985 + }, + { + "name": "Щербинка", + "lat": 55.50868, + "lon": 37.5633253, + "population": 56531 + }, + { + "name": "Сибай", + "lat": 52.7204622, + "lon": 58.6663921, + "population": 56514 + }, + { + "name": "Мелеуз", + "lat": 52.9589874, + "lon": 55.9282514, + "population": 56505 + }, + { + "name": "Котлас", + "lat": 61.2528975, + "lon": 46.6332216, + "population": 56093 + }, + { + "name": "Михайловка", + "lat": 50.0708708, + "lon": 43.2401092, + "population": 56031 + }, + { + "name": "Избербаш", + "lat": 42.5652081, + "lon": 47.8710684, + "population": 55996 + }, + { + "name": "Краснотурьинск", + "lat": 59.7638319, + "lon": 60.1935754, + "population": 55875 + }, + { + "name": "Белореченск", + "lat": 44.7652692, + "lon": 39.8781164, + "population": 55870 + }, + { + "name": "Ржев", + "lat": 56.2629154, + "lon": 34.3290948, + "population": 55757 + }, + { + "name": "Лесосибирск", + "lat": 58.2216981, + "lon": 92.503692, + "population": 55730 + }, + { + "name": "Тихорецк", + "lat": 45.8546923, + "lon": 40.1258776, + "population": 55686 + }, + { + "name": "Тихвин", + "lat": 59.644619, + "lon": 33.54193, + "population": 55415 + }, + { + "name": "Шуя", + "lat": 56.8507513, + "lon": 41.3516168, + "population": 55225 + }, + { + "name": "Полевской", + "lat": 56.4958618, + "lon": 60.2366252, + "population": 55182 + }, + { + "name": "Щекино", + "lat": 54.0021718, + "lon": 37.5177177, + "population": 55109 + }, + { + "name": "Шали", + "lat": 43.1487442, + "lon": 45.9010465, + "population": 55054 + }, + { + "name": "Вольск", + "lat": 52.0459603, + "lon": 47.3873, + "population": 55035 + }, + { + "name": "Крымск", + "lat": 44.9343723, + "lon": 37.9854857, + "population": 54597 + }, + { + "name": "пгт Яблоновский", + "lat": 44.981266, + "lon": 38.936362, + "population": 54291 + }, + { + "name": "Зеленогорск", + "lat": 56.1133542, + "lon": 94.5889359, + "population": 54279 + }, + { + "name": "Лиски", + "lat": 50.9945381, + "lon": 39.5183454, + "population": 54147 + }, + { + "name": "Черемхово", + "lat": 53.1233358, + "lon": 103.1574108, + "population": 53958 + }, + { + "name": "Лысьва", + "lat": 58.0996742, + "lon": 57.8087369, + "population": 53855 + }, + { + "name": "Нерюнгри", + "lat": 56.6599502, + "lon": 124.7201885, + "population": 53409 + }, + { + "name": "Волжск", + "lat": 55.8623164, + "lon": 48.3715777, + "population": 53013 + }, + { + "name": "Мегион", + "lat": 61.0318864, + "lon": 76.102504, + "population": 52887 + }, + { + "name": "Вязьма", + "lat": 55.2116645, + "lon": 34.2952096, + "population": 51950 + }, + { + "name": "Тимашевск", + "lat": 45.6158949, + "lon": 38.9351457, + "population": 51858 + }, + { + "name": "Гусь-Хрустальный", + "lat": 55.6199041, + "lon": 40.6578219, + "population": 51552 + }, + { + "name": "Краснокаменск", + "lat": 50.09868, + "lon": 118.034118, + "population": 51137 + }, + { + "name": "Кириши", + "lat": 59.449699, + "lon": 32.008705, + "population": 51028 + }, + { + "name": "пгт Нахабино рп", + "lat": 55.840381, + "lon": 37.175899, + "population": 50916 + }, + { + "name": "Снежинск", + "lat": 56.0852853, + "lon": 60.73251, + "population": 50619 + }, + { + "name": "Жигулевск", + "lat": 53.4012735, + "lon": 49.4944877, + "population": 50466 + }, + { + "name": "Кизляр", + "lat": 43.8484072, + "lon": 46.7232534, + "population": 49999 + }, + { + "name": "Кингисепп", + "lat": 59.3741261, + "lon": 28.611223, + "population": 49716 + }, + { + "name": "Апатиты", + "lat": 67.567731, + "lon": 33.4067789, + "population": 49647 + }, + { + "name": "Узловая", + "lat": 53.9731953, + "lon": 38.1764288, + "population": 49427 + }, + { + "name": "Краснокамск", + "lat": 58.0820673, + "lon": 55.7478889, + "population": 48778 + }, + { + "name": "Балахна", + "lat": 56.4950365, + "lon": 43.575846, + "population": 48569 + }, + { + "name": "Свободный", + "lat": 51.3612532, + "lon": 128.122028, + "population": 48517 + }, + { + "name": "Солнечногорск", + "lat": 56.185102, + "lon": 36.977631, + "population": 48413 + }, + { + "name": "Аксай", + "lat": 47.2675888, + "lon": 39.8755792, + "population": 48372 + }, + { + "name": "Лесной", + "lat": 58.6349153, + "lon": 59.7980136, + "population": 48261 + }, + { + "name": "Арсеньев", + "lat": 44.1621626, + "lon": 133.2696658, + "population": 47937 + }, + { + "name": "Салехард", + "lat": 66.5493706, + "lon": 66.6084227, + "population": 47910 + }, + { + "name": "Боровичи", + "lat": 58.3840076, + "lon": 33.9176708, + "population": 47883 + }, + { + "name": "Рассказово", + "lat": 52.6538108, + "lon": 41.8743764, + "population": 47644 + }, + { + "name": "Курганинск", + "lat": 44.8877061, + "lon": 40.5914627, + "population": 47305 + }, + { + "name": "Отрадный", + "lat": 53.380127, + "lon": 51.3440385, + "population": 46984 + }, + { + "name": "Донецк", + "lat": 48.3350869, + "lon": 39.9459603, + "population": 46623 + }, + { + "name": "Надым", + "lat": 65.528146, + "lon": 72.514417, + "population": 45973 + }, + { + "name": "Кашира", + "lat": 54.8532793, + "lon": 38.1904181, + "population": 45922 + }, + { + "name": "Вышний Волочек", + "lat": 57.5684471, + "lon": 34.5404365, + "population": 45830 + }, + { + "name": "Чусовой", + "lat": 58.2975271, + "lon": 57.8193629, + "population": 45471 + }, + { + "name": "Рославль", + "lat": 53.9449217, + "lon": 32.847942, + "population": 45416 + }, + { + "name": "Назарово", + "lat": 56.0115024, + "lon": 90.4169443, + "population": 45333 + }, + { + "name": "Выкса", + "lat": 55.3206313, + "lon": 42.1679983, + "population": 45240 + }, + { + "name": "Саяногорск", + "lat": 53.1007127, + "lon": 91.4122756, + "population": 44872 + }, + { + "name": "Чебаркуль", + "lat": 54.9818594, + "lon": 60.3774264, + "population": 44693 + }, + { + "name": "Канаш", + "lat": 55.5068806, + "lon": 47.4917914, + "population": 44354 + }, + { + "name": "Можга", + "lat": 56.4427514, + "lon": 52.2137584, + "population": 44345 + }, + { + "name": "Бирск", + "lat": 55.4156358, + "lon": 55.5583061, + "population": 44295 + }, + { + "name": "Березовский", + "lat": 55.6693866, + "lon": 86.2745058, + "population": 44073 + }, + { + "name": "Грязи", + "lat": 52.4872943, + "lon": 39.9331152, + "population": 43908 + }, + { + "name": "Краснознаменск", + "lat": 55.5978331, + "lon": 37.039529, + "population": 43868 + }, + { + "name": "Бугуруслан", + "lat": 53.6522761, + "lon": 52.4326104, + "population": 43593 + }, + { + "name": "Радужный", + "lat": 62.1342204, + "lon": 77.458477, + "population": 43577 + }, + { + "name": "Ливны", + "lat": 52.4286428, + "lon": 37.6040201, + "population": 43549 + }, + { + "name": "Североморск", + "lat": 69.07651, + "lon": 33.4178343, + "population": 43327 + }, + { + "name": "Карабулак", + "lat": 43.3055756, + "lon": 44.9095113, + "population": 43037 + }, + { + "name": "Рузаевка", + "lat": 54.0582232, + "lon": 44.949083, + "population": 42989 + }, + { + "name": "Лангепас", + "lat": 61.2537123, + "lon": 75.1806961, + "population": 42701 + }, + { + "name": "Сатка", + "lat": 55.0404006, + "lon": 59.0288229, + "population": 42597 + }, + { + "name": "Шелехов", + "lat": 52.2101358, + "lon": 104.0973332, + "population": 41998 + }, + { + "name": "Куйбышев", + "lat": 55.445949, + "lon": 78.311108, + "population": 41946 + }, + { + "name": "Малоярославец", + "lat": 55.0177696, + "lon": 36.4633694, + "population": 41836 + }, + { + "name": "Кореновск", + "lat": 45.4642106, + "lon": 39.4589751, + "population": 41826 + }, + { + "name": "Большой Камень", + "lat": 43.1111521, + "lon": 132.3479458, + "population": 41825 + }, + { + "name": "станица Каневская", + "lat": 46.0847521, + "lon": 38.9719273, + "population": 41721 + }, + { + "name": "Аргун", + "lat": 43.2917917, + "lon": 45.8722923, + "population": 41622 + }, + { + "name": "Темрюк", + "lat": 45.2610243, + "lon": 37.4455473, + "population": 41608 + }, + { + "name": "Ярцево", + "lat": 55.0565468, + "lon": 32.6902745, + "population": 41452 + }, + { + "name": "Урай", + "lat": 60.1297244, + "lon": 64.8038858, + "population": 41315 + }, + { + "name": "Заринск", + "lat": 53.7064163, + "lon": 84.93148, + "population": 41272 + }, + { + "name": "Торжок", + "lat": 57.041349, + "lon": 34.9602076, + "population": 41116 + }, + { + "name": "Верхняя Салда", + "lat": 58.0466272, + "lon": 60.5560133, + "population": 41034 + }, + { + "name": "Лянтор", + "lat": 61.6392893, + "lon": 72.1793962, + "population": 40977 + }, + { + "name": "Горячий Ключ", + "lat": 44.6344393, + "lon": 39.1355285, + "population": 40903 + }, + { + "name": "Кимры", + "lat": 56.8732554, + "lon": 37.3557008, + "population": 40875 + }, + { + "name": "Мариинск", + "lat": 56.2128227, + "lon": 87.7454299, + "population": 40779 + }, + { + "name": "Белая Калитва", + "lat": 48.176882, + "lon": 40.8032881, + "population": 40448 + }, + { + "name": "Сосновоборск", + "lat": 56.120075, + "lon": 93.3354461, + "population": 40442 + }, + { + "name": "Осинники", + "lat": 53.598681, + "lon": 87.3371253, + "population": 40367 + }, + { + "name": "Курчатов", + "lat": 51.6605387, + "lon": 35.6571025, + "population": 40318 + }, + { + "name": "Апшеронск", + "lat": 44.4583753, + "lon": 39.7301418, + "population": 40289 + }, + { + "name": "Пыть-Ях", + "lat": 60.7586475, + "lon": 72.8366942, + "population": 40180 + }, + { + "name": "Усть-Лабинск", + "lat": 45.2226625, + "lon": 39.6929278, + "population": 40158 + }, + { + "name": "Пугачев", + "lat": 52.0159472, + "lon": 48.797175, + "population": 40127 + }, + { + "name": "Мыски", + "lat": 53.7126111, + "lon": 87.8056844, + "population": 40109 + }, + { + "name": "Мончегорск", + "lat": 67.9386512, + "lon": 32.9359042, + "population": 39962 + }, + { + "name": "Заинск", + "lat": 55.2988956, + "lon": 52.0063163, + "population": 39739 + }, + { + "name": "Шебекино", + "lat": 50.4004378, + "lon": 36.8878943, + "population": 39680 + }, + { + "name": "Тутаев", + "lat": 57.8674415, + "lon": 39.536868, + "population": 39643 + }, + { + "name": "Баксан", + "lat": 43.6819288, + "lon": 43.5344884, + "population": 39593 + }, + { + "name": "Абинск", + "lat": 44.8679116, + "lon": 38.1617987, + "population": 39511 + }, + { + "name": "Кольчугино", + "lat": 56.3328004, + "lon": 39.391268, + "population": 39410 + }, + { + "name": "Стрежевой", + "lat": 60.7327472, + "lon": 77.6039789, + "population": 39169 + }, + { + "name": "Моршанск", + "lat": 53.4436449, + "lon": 41.8115193, + "population": 39023 + }, + { + "name": "Советск", + "lat": 55.0811102, + "lon": 21.8887115, + "population": 38910 + }, + { + "name": "Ялуторовск", + "lat": 56.6546647, + "lon": 66.3121926, + "population": 38853 + }, + { + "name": "Новозыбков", + "lat": 52.5371414, + "lon": 31.9357361, + "population": 38680 + }, + { + "name": "Изобильный", + "lat": 45.3684383, + "lon": 41.7085873, + "population": 38614 + }, + { + "name": "Амурск", + "lat": 50.2345703, + "lon": 136.8791531, + "population": 38606 + }, + { + "name": "Волхов", + "lat": 59.9004589, + "lon": 32.3520685, + "population": 38511 + }, + { + "name": "Тулун", + "lat": 54.5570223, + "lon": 100.5779905, + "population": 38440 + }, + { + "name": "Луга", + "lat": 58.735207, + "lon": 29.847945, + "population": 38407 + }, + { + "name": "Сафоново", + "lat": 55.1200105, + "lon": 33.233658, + "population": 38403 + }, + { + "name": "Кизилюрт", + "lat": 43.2038278, + "lon": 46.872864, + "population": 38335 + }, + { + "name": "Югорск", + "lat": 61.3124125, + "lon": 63.3364948, + "population": 38238 + }, + { + "name": "Шатура", + "lat": 55.5778265, + "lon": 39.5445373, + "population": 38230 + }, + { + "name": "Ртищево", + "lat": 52.2616938, + "lon": 43.7842747, + "population": 37850 + }, + { + "name": "Переславль-Залесский", + "lat": 56.7360013, + "lon": 38.8543687, + "population": 37738 + }, + { + "name": "Протвино", + "lat": 54.8705841, + "lon": 37.2182922, + "population": 37735 + }, + { + "name": "Южноуральск", + "lat": 54.4490432, + "lon": 61.2581713, + "population": 37478 + }, + { + "name": "Истра", + "lat": 55.9061876, + "lon": 36.8600649, + "population": 37474 + }, + { + "name": "Качканар", + "lat": 58.7050979, + "lon": 59.4839558, + "population": 37307 + }, + { + "name": "Красноуфимск", + "lat": 56.612429, + "lon": 57.7635494, + "population": 37301 + }, + { + "name": "Коркино", + "lat": 54.8903864, + "lon": 61.4032939, + "population": 37224 + }, + { + "name": "Джанкой", + "lat": 45.7092913, + "lon": 34.3883718, + "population": 37014 + }, + { + "name": "Ирбит", + "lat": 57.683808, + "lon": 63.0576929, + "population": 37009 + }, + { + "name": "пгт Маркова рп", + "lat": 52.211106, + "lon": 104.207909, + "population": 36971 + }, + { + "name": "Мценск", + "lat": 53.2789769, + "lon": 36.5749963, + "population": 36960 + }, + { + "name": "Усть-Кут", + "lat": 56.780878, + "lon": 105.745373, + "population": 36918 + }, + { + "name": "Моздок", + "lat": 43.7472597, + "lon": 44.657072, + "population": 36784 + }, + { + "name": "Заволжье", + "lat": 56.6404919, + "lon": 43.3870929, + "population": 36763 + }, + { + "name": "Кинель", + "lat": 53.2209314, + "lon": 50.6343567, + "population": 36729 + }, + { + "name": "Урюпинск", + "lat": 50.7904909, + "lon": 42.0289034, + "population": 36669 + }, + { + "name": "Реж", + "lat": 57.3716798, + "lon": 61.38338, + "population": 36585 + }, + { + "name": "Алексеевка", + "lat": 50.6299988, + "lon": 38.6881066, + "population": 36578 + }, + { + "name": "станица Динская", + "lat": 45.2362774, + "lon": 39.2405427, + "population": 36576 + }, + { + "name": "Ефремов", + "lat": 53.137603, + "lon": 38.117673, + "population": 36545 + }, + { + "name": "Малгобек", + "lat": 43.5097018, + "lon": 44.5901112, + "population": 36480 + }, + { + "name": "Елизово", + "lat": 53.1829398, + "lon": 158.3884312, + "population": 36240 + }, + { + "name": "Вязники", + "lat": 56.2977573, + "lon": 42.268676, + "population": 36203 + }, + { + "name": "Алапаевск", + "lat": 57.8476947, + "lon": 61.6695786, + "population": 36189 + }, + { + "name": "Учалы", + "lat": 54.3066963, + "lon": 59.4127255, + "population": 36175 + }, + { + "name": "Черняховск", + "lat": 54.6245018, + "lon": 21.7969164, + "population": 36128 + }, + { + "name": "Кыштым", + "lat": 55.706159, + "lon": 60.5562529, + "population": 36045 + }, + { + "name": "пгт Горячеводский рп", + "lat": 44.0236208, + "lon": 43.0973777, + "population": 36032 + }, + { + "name": "Беслан", + "lat": 43.1936637, + "lon": 44.533792, + "population": 35929 + }, + { + "name": "Балаклава", + "lat": 44.501414, + "lon": 33.600037, + "population": 35919 + }, + { + "name": "Людиново", + "lat": 53.8701272, + "lon": 34.4385772, + "population": 35874 + }, + { + "name": "Звенигород", + "lat": 55.7295881, + "lon": 36.8553623, + "population": 35842 + }, + { + "name": "Спасск-Дальний", + "lat": 44.5900901, + "lon": 132.8158275, + "population": 35732 + }, + { + "name": "Светлоград", + "lat": 45.3286594, + "lon": 42.856594, + "population": 35703 + }, + { + "name": "Красный Сулин", + "lat": 47.883196, + "lon": 40.0781926, + "population": 35697 + }, + { + "name": "Фролово", + "lat": 49.7648209, + "lon": 43.6649431, + "population": 35661 + }, + { + "name": "Ахтубинск", + "lat": 48.2753708, + "lon": 46.1905298, + "population": 35635 + }, + { + "name": "Саянск", + "lat": 54.1108195, + "lon": 102.1802309, + "population": 35561 + }, + { + "name": "станица Ленинградская", + "lat": 46.3449638, + "lon": 39.3907816, + "population": 35561 + }, + { + "name": "Апрелевка", + "lat": 55.527617, + "lon": 37.0650837, + "population": 35514 + }, + { + "name": "Благовещенск", + "lat": 55.0498049, + "lon": 55.9553149, + "population": 35481 + }, + { + "name": "Лесозаводск", + "lat": 45.4780131, + "lon": 133.4185314, + "population": 35433 + }, + { + "name": "Печора", + "lat": 65.1486942, + "lon": 57.2239645, + "population": 35254 + }, + { + "name": "Богородск", + "lat": 56.1020008, + "lon": 43.5135913, + "population": 35068 + }, + { + "name": "Миллерово", + "lat": 48.9259954, + "lon": 40.3982659, + "population": 34841 + }, + { + "name": "Азнакаево", + "lat": 54.8597519, + "lon": 53.0744925, + "population": 34750 + }, + { + "name": "Сокол", + "lat": 59.4757853, + "lon": 40.1114848, + "population": 34742 + }, + { + "name": "деревня Ватутинки", + "lat": 55.4942411, + "lon": 37.3288668, + "population": 34697 + }, + { + "name": "Сланцы", + "lat": 59.1177053, + "lon": 28.0882334, + "population": 34628 + }, + { + "name": "Коряжма", + "lat": 61.2886248, + "lon": 47.1002365, + "population": 34523 + }, + { + "name": "Тайшет", + "lat": 55.9405482, + "lon": 98.0030449, + "population": 34491 + }, + { + "name": "Ликино-Дулёво", + "lat": 55.7077293, + "lon": 38.9577959, + "population": 34191 + }, + { + "name": "Тосно", + "lat": 59.540686, + "lon": 30.8777641, + "population": 34066 + }, + { + "name": "Мирный", + "lat": 62.5362564, + "lon": 113.9667642, + "population": 34045 + }, + { + "name": "Новокубанск", + "lat": 45.1036986, + "lon": 41.0475442, + "population": 34000 + }, + { + "name": "Нурлат", + "lat": 54.4281036, + "lon": 50.8049432, + "population": 33990 + }, + { + "name": "Шарыпово", + "lat": 55.5391274, + "lon": 89.1801041, + "population": 33961 + }, + { + "name": "Корсаков", + "lat": 46.6324255, + "lon": 142.7994957, + "population": 33950 + }, + { + "name": "Можайск", + "lat": 55.5069403, + "lon": 36.0239829, + "population": 33880 + }, + { + "name": "Партизанск", + "lat": 43.1504279, + "lon": 133.1666126, + "population": 33832 + }, + { + "name": "Дальнегорск", + "lat": 44.5541197, + "lon": 135.566156, + "population": 33655 + }, + { + "name": "Конаково", + "lat": 56.7275398, + "lon": 36.801348, + "population": 33560 + }, + { + "name": "Каменка", + "lat": 53.1855489, + "lon": 44.0468101, + "population": 33491 + }, + { + "name": "Гулькевичи", + "lat": 45.3605339, + "lon": 40.6918433, + "population": 33357 + }, + { + "name": "Новодвинск", + "lat": 64.4136473, + "lon": 40.8206783, + "population": 33294 + }, + { + "name": "Гай", + "lat": 51.4650759, + "lon": 58.4436431, + "population": 33280 + }, + { + "name": "Губкинский", + "lat": 64.4456403, + "lon": 76.4713798, + "population": 33273 + }, + { + "name": "деревня Рассказовка", + "lat": 55.6317853, + "lon": 37.3132339, + "population": 33259 + }, + { + "name": "Нарткала", + "lat": 43.5578132, + "lon": 43.8576082, + "population": 33203 + }, + { + "name": "Зеленокумск", + "lat": 44.4032361, + "lon": 43.8841172, + "population": 33187 + }, + { + "name": "пгт Приволжский рп", + "lat": 51.4109503, + "lon": 46.0481131, + "population": 33096 + }, + { + "name": "Валуйки", + "lat": 50.211172, + "lon": 38.0999436, + "population": 33032 + }, + { + "name": "Чернушка", + "lat": 56.515934, + "lon": 56.0764463, + "population": 32991 + }, + { + "name": "Тавда", + "lat": 58.0434995, + "lon": 65.274369, + "population": 32749 + }, + { + "name": "Сухой Лог", + "lat": 56.9074957, + "lon": 62.0357985, + "population": 32748 + }, + { + "name": "Углич", + "lat": 57.522461, + "lon": 38.3018763, + "population": 32719 + }, + { + "name": "Трехгорный", + "lat": 54.8177944, + "lon": 58.4463721, + "population": 32463 + }, + { + "name": "Камень-на-Оби", + "lat": 53.7914749, + "lon": 81.3545962, + "population": 32385 + }, + { + "name": "Алатырь", + "lat": 54.8372699, + "lon": 46.5339898, + "population": 32265 + }, + { + "name": "Кулебаки", + "lat": 55.4296716, + "lon": 42.5124575, + "population": 32184 + }, + { + "name": "Усинск", + "lat": 65.9940586, + "lon": 57.5569288, + "population": 32182 + }, + { + "name": "Острогожск", + "lat": 50.867943, + "lon": 39.0406177, + "population": 31699 + }, + { + "name": "станица Новотитаровская", + "lat": 45.2376345, + "lon": 38.9765189, + "population": 31520 + }, + { + "name": "Дагестанские Огни", + "lat": 42.1152854, + "lon": 48.193977, + "population": 31412 + }, + { + "name": "Алушта", + "lat": 44.6763996, + "lon": 34.4100439, + "population": 31364 + }, + { + "name": "Тейково", + "lat": 56.8542792, + "lon": 40.5353799, + "population": 31305 + }, + { + "name": "Дюртюли", + "lat": 55.4848147, + "lon": 54.8524883, + "population": 31185 + }, + { + "name": "село Иглино", + "lat": 54.834468, + "lon": 56.416165, + "population": 31169 + }, + { + "name": "Советский", + "lat": 61.3707009, + "lon": 63.5667177, + "population": 31138 + }, + { + "name": "Усть-Джегута", + "lat": 44.0838556, + "lon": 41.9710356, + "population": 31137 + }, + { + "name": "Приморско-Ахтарск", + "lat": 46.0515858, + "lon": 38.1704382, + "population": 31087 + }, + { + "name": "Фактория", + "lat": 64.53939, + "lon": 40.5170011, + "population": 31008 + }, + { + "name": "Кохма", + "lat": 56.9325054, + "lon": 41.0931324, + "population": 30940 + }, + { + "name": "Благодарный", + "lat": 45.098943, + "lon": 43.4306154, + "population": 30827 + }, + { + "name": "село Ачхой-Мартан", + "lat": 43.1872011, + "lon": 45.2863481, + "population": 30739 + }, + { + "name": "Дедовск", + "lat": 55.8704582, + "lon": 37.124447, + "population": 30731 + }, + { + "name": "Вичуга", + "lat": 57.2044884, + "lon": 41.9131422, + "population": 30694 + }, + { + "name": "город Нововоронеж", + "lat": 51.3091935, + "lon": 39.216289, + "population": 30658 + }, + { + "name": "Зима", + "lat": 53.9336544, + "lon": 102.0498973, + "population": 30640 + }, + { + "name": "станица Кущевская", + "lat": 46.569894, + "lon": 39.619836, + "population": 30375 + }, + { + "name": "Обь", + "lat": 54.9945378, + "lon": 82.6937661, + "population": 30369 + }, + { + "name": "станица Староминская", + "lat": 46.5331093, + "lon": 39.0535727, + "population": 30362 + }, + { + "name": "пгт Томилино рп", + "lat": 55.663107, + "lon": 37.949918, + "population": 30306 + }, + { + "name": "Сердобск", + "lat": 52.4697777, + "lon": 44.2123029, + "population": 30220 + }, + { + "name": "Богданович", + "lat": 56.7764214, + "lon": 62.0462888, + "population": 30142 + }, + { + "name": "Нижнеудинск", + "lat": 54.896942, + "lon": 99.031408, + "population": 29995 + }, + { + "name": "Электрогорск", + "lat": 55.8778372, + "lon": 38.7805887, + "population": 29982 + }, + { + "name": "Луховицы", + "lat": 54.7659596, + "lon": 39.2706433, + "population": 29889 + }, + { + "name": "Вятские Поляны", + "lat": 56.2285162, + "lon": 51.0616058, + "population": 29742 + }, + { + "name": "Фурманов", + "lat": 57.2537548, + "lon": 41.1054731, + "population": 29715 + }, + { + "name": "Борзя", + "lat": 50.3877136, + "lon": 116.5235118, + "population": 29596 + }, + { + "name": "Богородицк", + "lat": 53.7700594, + "lon": 38.1225087, + "population": 29560 + }, + { + "name": "станица Павловская", + "lat": 46.122016, + "lon": 39.781416, + "population": 29514 + }, + { + "name": "Гусев", + "lat": 54.5915197, + "lon": 22.1942896, + "population": 29234 + }, + { + "name": "Муравленко", + "lat": 63.795443, + "lon": 74.494826, + "population": 29233 + }, + { + "name": "Слободской", + "lat": 58.7312106, + "lon": 50.1669806, + "population": 29148 + }, + { + "name": "Кандалакша", + "lat": 67.1566641, + "lon": 32.4142961, + "population": 29138 + }, + { + "name": "пгт Краснообск рп", + "lat": 54.921237, + "lon": 82.991197, + "population": 29086 + }, + { + "name": "пгт Дрожжино рп", + "lat": 55.5277293, + "lon": 37.5937701, + "population": 29072 + }, + { + "name": "Балабаново", + "lat": 55.1774068, + "lon": 36.656839, + "population": 29029 + }, + { + "name": "Лосино-Петровский", + "lat": 55.869566, + "lon": 38.202852, + "population": 29000 + }, + { + "name": "Артемовский", + "lat": 57.3384593, + "lon": 61.8945883, + "population": 28943 + }, + { + "name": "Добрянка", + "lat": 58.542592, + "lon": 56.293958, + "population": 28782 + }, + { + "name": "Маркс", + "lat": 51.7133247, + "lon": 46.7400877, + "population": 28749 + }, + { + "name": "Великий Устюг", + "lat": 60.7602824, + "lon": 46.3053948, + "population": 28670 + }, + { + "name": "Городец", + "lat": 56.644865, + "lon": 43.4722664, + "population": 28660 + }, + { + "name": "Тында", + "lat": 55.1546593, + "lon": 124.7468001, + "population": 28625 + }, + { + "name": "Бахчисарай", + "lat": 44.751299, + "lon": 33.8750748, + "population": 28609 + }, + { + "name": "Сорочинск", + "lat": 52.4266473, + "lon": 53.1541824, + "population": 28478 + }, + { + "name": "Касимов", + "lat": 54.9373071, + "lon": 41.3913313, + "population": 28443 + }, + { + "name": "Кудымкар", + "lat": 59.0168409, + "lon": 54.6573613, + "population": 28293 + }, + { + "name": "пгт Власиха", + "lat": 55.680815, + "lon": 37.1902369, + "population": 28240 + }, + { + "name": "пгт Разумное", + "lat": 50.533611, + "lon": 36.689562, + "population": 28192 + }, + { + "name": "Ростов", + "lat": 57.2050397, + "lon": 39.4378513, + "population": 28122 + }, + { + "name": "Заречный", + "lat": 56.8103256, + "lon": 61.3380277, + "population": 28112 + }, + { + "name": "Киров", + "lat": 54.0789799, + "lon": 34.3076309, + "population": 28097 + }, + { + "name": "город Семилуки", + "lat": 51.6951946, + "lon": 39.0188991, + "population": 27938 + }, + { + "name": "Славгород", + "lat": 52.999413, + "lon": 78.6458451, + "population": 27900 + }, + { + "name": "Аша", + "lat": 54.990582, + "lon": 57.2784381, + "population": 27890 + }, + { + "name": "пгт Энем", + "lat": 44.9263995, + "lon": 38.9107709, + "population": 27717 + }, + { + "name": "Барабинск", + "lat": 55.3515169, + "lon": 78.3464604, + "population": 27648 + }, + { + "name": "Еманжелинск", + "lat": 54.7554485, + "lon": 61.3243638, + "population": 27632 + }, + { + "name": "посёлок Придорожный", + "lat": 53.0860041, + "lon": 50.1587223, + "population": 27515 + }, + { + "name": "пгт Иноземцево кп", + "lat": 44.0970899, + "lon": 43.0894193, + "population": 27500 + }, + { + "name": "Старая Русса", + "lat": 57.9907473, + "lon": 31.355452, + "population": 27487 + }, + { + "name": "Дивногорск", + "lat": 55.9577356, + "lon": 92.380129, + "population": 27477 + }, + { + "name": "Похвистнево", + "lat": 53.6497658, + "lon": 52.1234466, + "population": 27333 + }, + { + "name": "Киржач", + "lat": 56.1486461, + "lon": 38.8635967, + "population": 27318 + }, + { + "name": "Кушва", + "lat": 58.2825138, + "lon": 59.7647615, + "population": 27306 + }, + { + "name": "пгт Красково дп", + "lat": 55.65879, + "lon": 37.9885476, + "population": 27306 + }, + { + "name": "Мирный", + "lat": 62.764551, + "lon": 40.336094, + "population": 27262 + }, + { + "name": "Кировск", + "lat": 59.875234, + "lon": 30.9815172, + "population": 27238 + }, + { + "name": "село Экажево", + "lat": 43.2082923, + "lon": 44.8190051, + "population": 27224 + }, + { + "name": "Топки", + "lat": 55.2765207, + "lon": 85.6152094, + "population": 27158 + }, + { + "name": "Камышлов", + "lat": 56.8464908, + "lon": 62.7120309, + "population": 27117 + }, + { + "name": "Карталы", + "lat": 53.0537382, + "lon": 60.6477347, + "population": 27103 + }, + { + "name": "Заводоуковск", + "lat": 56.5028398, + "lon": 66.5513555, + "population": 27100 + }, + { + "name": "деревня Мисайлово", + "lat": 55.5639995, + "lon": 37.8255811, + "population": 27093 + }, + { + "name": "деревня Путилково", + "lat": 55.865253, + "lon": 37.392526, + "population": 27081 + }, + { + "name": "станица Северская", + "lat": 44.8562413, + "lon": 38.6959282, + "population": 26973 + }, + { + "name": "Тара", + "lat": 56.9159295, + "lon": 74.3648797, + "population": 26878 + }, + { + "name": "село Засечное", + "lat": 53.105904, + "lon": 45.0717034, + "population": 26878 + }, + { + "name": "Шумерля", + "lat": 55.5229338, + "lon": 46.3752894, + "population": 26873 + }, + { + "name": "Балтийск", + "lat": 54.6513858, + "lon": 19.9140642, + "population": 26796 + }, + { + "name": "Новоалександровск", + "lat": 45.4932983, + "lon": 41.2153595, + "population": 26767 + }, + { + "name": "Гурьевск", + "lat": 54.7705454, + "lon": 20.6039948, + "population": 26760 + }, + { + "name": "Котовск", + "lat": 52.5923425, + "lon": 41.5102238, + "population": 26694 + }, + { + "name": "село Кочубеевское", + "lat": 44.6907487, + "lon": 41.8242914, + "population": 26645 + }, + { + "name": "Майский", + "lat": 43.6283265, + "lon": 44.051839, + "population": 26632 + }, + { + "name": "Кувандык", + "lat": 51.4785827, + "lon": 57.3610986, + "population": 26596 + }, + { + "name": "Гагарин", + "lat": 55.5526733, + "lon": 34.9950319, + "population": 26500 + }, + { + "name": "Красноармейск", + "lat": 56.105426, + "lon": 38.140838, + "population": 26492 + }, + { + "name": "Кимовск", + "lat": 53.9698013, + "lon": 38.538127, + "population": 26475 + }, + { + "name": "Волоколамск", + "lat": 56.0356464, + "lon": 35.958479, + "population": 26389 + }, + { + "name": "Петровск", + "lat": 52.3094724, + "lon": 45.3851898, + "population": 26319 + }, + { + "name": "Соль-Илецк", + "lat": 51.1634754, + "lon": 54.9895396, + "population": 26149 + }, + { + "name": "Ипатово", + "lat": 45.7181195, + "lon": 42.8970759, + "population": 26122 + }, + { + "name": "Костомукша", + "lat": 64.5890912, + "lon": 30.6015362, + "population": 26048 + }, + { + "name": "пгт Малаховка рп", + "lat": 55.6452616, + "lon": 38.0042385, + "population": 25989 + }, + { + "name": "Удомля", + "lat": 57.8787641, + "lon": 35.0166208, + "population": 25950 + }, + { + "name": "Янаул", + "lat": 56.2649073, + "lon": 54.9297772, + "population": 25908 + }, + { + "name": "Карпинск", + "lat": 59.766559, + "lon": 60.0012125, + "population": 25879 + }, + { + "name": "Кондопога", + "lat": 62.2059903, + "lon": 34.2681816, + "population": 25851 + }, + { + "name": "Коммунар", + "lat": 59.6215741, + "lon": 30.3935534, + "population": 25793 + }, + { + "name": "Отрадное", + "lat": 59.7726485, + "lon": 30.798845, + "population": 25706 + }, + { + "name": "Холмск", + "lat": 47.0408656, + "lon": 142.0416132, + "population": 25677 + }, + { + "name": "станица Тбилисская", + "lat": 45.3644521, + "lon": 40.2016871, + "population": 25650 + }, + { + "name": "Полысаево", + "lat": 54.6054908, + "lon": 86.2809963, + "population": 25631 + }, + { + "name": "Красноперекопск", + "lat": 45.9536596, + "lon": 33.792071, + "population": 25569 + }, + { + "name": "Киреевск", + "lat": 53.9320407, + "lon": 37.9219941, + "population": 25560 + }, + { + "name": "пгт Мостовской", + "lat": 44.4144395, + "lon": 40.790292, + "population": 25548 + }, + { + "name": "Лабытнанги", + "lat": 66.6593565, + "lon": 66.3881811, + "population": 25501 + }, + { + "name": "село Александровское", + "lat": 44.7105748, + "lon": 43.0049484, + "population": 25479 + }, + { + "name": "город Десногорск", + "lat": 54.1463618, + "lon": 33.2834089, + "population": 25414 + }, + { + "name": "Алейск", + "lat": 52.4922482, + "lon": 82.7795177, + "population": 25380 + }, + { + "name": "Десногорск", + "lat": 54.1463618, + "lon": 33.2834089, + "population": 25345 + }, + { + "name": "Дятьково", + "lat": 53.5958143, + "lon": 34.355106, + "population": 25255 + }, + { + "name": "Скопин", + "lat": 53.8235282, + "lon": 39.5493392, + "population": 25238 + }, + { + "name": "пгт Калининец рп", + "lat": 55.559738, + "lon": 36.981914, + "population": 25082 + }, + { + "name": "Семенов", + "lat": 56.7890706, + "lon": 44.4902872, + "population": 25075 + }, + { + "name": "пгт Ильский", + "lat": 44.804549, + "lon": 38.546089, + "population": 24932 + }, + { + "name": "Асино", + "lat": 56.990811, + "lon": 86.1764966, + "population": 24913 + }, + { + "name": "Карасук", + "lat": 53.7343642, + "lon": 78.0422879, + "population": 24890 + }, + { + "name": "Кировск", + "lat": 67.6151587, + "lon": 33.6636568, + "population": 24857 + }, + { + "name": "станица Ессентукская", + "lat": 44.0267307, + "lon": 42.8764379, + "population": 24710 + }, + { + "name": "пгт Октябрьский рп", + "lat": 55.6110724, + "lon": 37.9737423, + "population": 24704 + }, + { + "name": "Знаменск", + "lat": 48.5866454, + "lon": 45.7369007, + "population": 24628 + }, + { + "name": "Гусиноозерск", + "lat": 51.2865987, + "lon": 106.5229836, + "population": 24451 + }, + { + "name": "пгт Пойковский", + "lat": 60.996548, + "lon": 71.902431, + "population": 24440 + }, + { + "name": "Североуральск", + "lat": 60.1533388, + "lon": 59.952553, + "population": 24428 + }, + { + "name": "Бутурлиновка", + "lat": 50.8312775, + "lon": 40.5977424, + "population": 24397 + }, + { + "name": "Озёры", + "lat": 54.8540209, + "lon": 38.5598706, + "population": 24359 + }, + { + "name": "Саки", + "lat": 45.13433, + "lon": 33.6032255, + "population": 24285 + }, + { + "name": "Калач-на-Дону", + "lat": 48.6889101, + "lon": 43.5307585, + "population": 24277 + }, + { + "name": "Унеча", + "lat": 52.8461971, + "lon": 32.6757398, + "population": 24274 + }, + { + "name": "Морозовск", + "lat": 48.351132, + "lon": 41.8307844, + "population": 24258 + }, + { + "name": "Северобайкальск", + "lat": 55.6355591, + "lon": 109.3361645, + "population": 24233 + }, + { + "name": "Советская Гавань", + "lat": 48.9663668, + "lon": 140.2852088, + "population": 24231 + }, + { + "name": "станица Полтавская", + "lat": 45.3654401, + "lon": 38.2109117, + "population": 24164 + }, + { + "name": "Родники", + "lat": 57.1024811, + "lon": 41.7298234, + "population": 24101 + }, + { + "name": "Зерноград", + "lat": 46.849613, + "lon": 40.3128993, + "population": 24076 + }, + { + "name": "пгт Северный", + "lat": 50.6774988, + "lon": 36.5607698, + "population": 24070 + }, + { + "name": "деревня Новое Девяткино", + "lat": 60.0572998, + "lon": 30.4762703, + "population": 23988 + }, + { + "name": "посёлок Тельмана", + "lat": 59.725444, + "lon": 30.612106, + "population": 23986 + }, + { + "name": "Карачаевск", + "lat": 43.7731804, + "lon": 41.9144084, + "population": 23867 + }, + { + "name": "станица Елизаветинская", + "lat": 45.0484348, + "lon": 38.7999276, + "population": 23841 + }, + { + "name": "Строитель", + "lat": 50.785172, + "lon": 36.486998, + "population": 23780 + }, + { + "name": "Татарск", + "lat": 55.2144722, + "lon": 75.9740764, + "population": 23711 + }, + { + "name": "Медногорск", + "lat": 51.4038655, + "lon": 57.5831571, + "population": 23693 + }, + { + "name": "пгт Федоровский", + "lat": 61.6484245, + "lon": 73.488652, + "population": 23614 + }, + { + "name": "Дальнереченск", + "lat": 45.9308807, + "lon": 133.7316844, + "population": 23613 + }, + { + "name": "пгт Афипский", + "lat": 44.9034044, + "lon": 38.8411512, + "population": 23592 + }, + { + "name": "Уварово", + "lat": 51.983084, + "lon": 42.261, + "population": 23584 + }, + { + "name": "Сегежа", + "lat": 63.7437691, + "lon": 34.3126737, + "population": 23543 + }, + { + "name": "Курчалой", + "lat": 43.2045354, + "lon": 46.0889297, + "population": 23425 + }, + { + "name": "Нарьян-Мар", + "lat": 67.6379742, + "lon": 53.0069529, + "population": 23399 + }, + { + "name": "Губаха", + "lat": 58.8370347, + "lon": 57.5544775, + "population": 23397 + }, + { + "name": "Среднеуральск", + "lat": 56.9919113, + "lon": 60.4771159, + "population": 23344 + }, + { + "name": "Кубинка", + "lat": 55.6790296, + "lon": 37.2635177, + "population": 23146 + }, + { + "name": "Нефтекумск", + "lat": 44.7545311, + "lon": 44.9865999, + "population": 23137 + }, + { + "name": "станица Троицкая", + "lat": 43.3060175, + "lon": 44.9893927, + "population": 23078 + }, + { + "name": "пгт Свердловский рп", + "lat": 55.9032059, + "lon": 38.1361082, + "population": 23058 + }, + { + "name": "станица Отрадная", + "lat": 44.39317, + "lon": 41.5156681, + "population": 23041 + }, + { + "name": "Верхний Уфалей", + "lat": 56.0487398, + "lon": 60.2320146, + "population": 22981 + }, + { + "name": "пгт Городище рп", + "lat": 48.8096322, + "lon": 44.4761656, + "population": 22905 + }, + { + "name": "Старая Купавна", + "lat": 55.810648, + "lon": 38.175624, + "population": 22898 + }, + { + "name": "Менделеевск", + "lat": 55.8951887, + "lon": 52.3143634, + "population": 22875 + }, + { + "name": "Железноводск", + "lat": 44.1320536, + "lon": 43.0304695, + "population": 22863 + }, + { + "name": "Голицыно", + "lat": 55.6189919, + "lon": 36.9856491, + "population": 22733 + }, + { + "name": "Аткарск", + "lat": 51.8737167, + "lon": 45.000324, + "population": 22709 + }, + { + "name": "пгт Тучково рп", + "lat": 55.6007848, + "lon": 36.4718788, + "population": 22657 + }, + { + "name": "рп Чишмы", + "lat": 54.5892603, + "lon": 55.3803504, + "population": 22597 + }, + { + "name": "Лермонтов", + "lat": 44.1053341, + "lon": 42.9731493, + "population": 22444 + }, + { + "name": "посёлок Бугры", + "lat": 60.0700161, + "lon": 30.3997764, + "population": 22428 + }, + { + "name": "город Павловск", + "lat": 50.4533503, + "lon": 40.1369424, + "population": 22384 + }, + { + "name": "Тайга", + "lat": 56.1723887, + "lon": 85.4268959, + "population": 22375 + }, + { + "name": "Никольское", + "lat": 59.7042138, + "lon": 30.7874065, + "population": 22355 + }, + { + "name": "Верещагино", + "lat": 58.0798499, + "lon": 54.6579923, + "population": 22239 + }, + { + "name": "Сосногорск", + "lat": 63.599118, + "lon": 53.8762442, + "population": 22189 + }, + { + "name": "Гурьевск", + "lat": 54.2859162, + "lon": 85.9477116, + "population": 22134 + }, + { + "name": "Хадыженск", + "lat": 44.412253, + "lon": 39.5320316, + "population": 22094 + }, + { + "name": "Невьянск", + "lat": 57.4912147, + "lon": 60.218324, + "population": 22061 + }, + { + "name": "Тырныауз", + "lat": 43.3981998, + "lon": 42.9214143, + "population": 22056 + }, + { + "name": "Котельниково", + "lat": 47.6310339, + "lon": 43.1330384, + "population": 22016 + }, + { + "name": "Таштагол", + "lat": 52.7594115, + "lon": 87.8476574, + "population": 21980 + }, + { + "name": "Давлеканово", + "lat": 54.222692, + "lon": 55.0311854, + "population": 21834 + }, + { + "name": "Бронницы", + "lat": 55.4254542, + "lon": 38.264161, + "population": 21831 + }, + { + "name": "Вилючинск", + "lat": 52.9302624, + "lon": 158.4057425, + "population": 21774 + }, + { + "name": "Калтан", + "lat": 53.521076, + "lon": 87.2771909, + "population": 21752 + }, + { + "name": "пгт Медведево", + "lat": 56.6332603, + "lon": 47.8032407, + "population": 21752 + }, + { + "name": "Вихоревка", + "lat": 56.1208112, + "lon": 101.1704184, + "population": 21719 + }, + { + "name": "Семикаракорск", + "lat": 47.517792, + "lon": 40.811485, + "population": 21719 + }, + { + "name": "Лысково", + "lat": 56.0262601, + "lon": 45.0357974, + "population": 21657 + }, + { + "name": "Бавлы", + "lat": 54.4063524, + "lon": 53.2458119, + "population": 21628 + }, + { + "name": "Сасово", + "lat": 54.3508047, + "lon": 41.9117508, + "population": 21628 + }, + { + "name": "Железногорск-Илимский", + "lat": 56.5847582, + "lon": 104.1142358, + "population": 21621 + }, + { + "name": "Вельск", + "lat": 61.0658321, + "lon": 42.1032464, + "population": 21613 + }, + { + "name": "Алдан", + "lat": 58.6095359, + "lon": 125.3817685, + "population": 21590 + }, + { + "name": "пгт Стройкерамика", + "lat": 53.2766484, + "lon": 50.3870415, + "population": 21567 + }, + { + "name": "Алагир", + "lat": 43.0416623, + "lon": 44.2197807, + "population": 21550 + }, + { + "name": "пгт Ахтырский", + "lat": 44.8503138, + "lon": 38.2998264, + "population": 21515 + }, + { + "name": "Красноуральск", + "lat": 58.3486753, + "lon": 60.0408929, + "population": 21507 + }, + { + "name": "Бежецк", + "lat": 57.7859527, + "lon": 36.6905192, + "population": 21466 + }, + { + "name": "Усть-Катав", + "lat": 54.9260247, + "lon": 58.1528932, + "population": 21439 + }, + { + "name": "Оленегорск", + "lat": 68.1420962, + "lon": 33.2669298, + "population": 21438 + }, + { + "name": "Рошаль", + "lat": 55.6632087, + "lon": 39.8656345, + "population": 21401 + }, + { + "name": "Ленск", + "lat": 60.7276227, + "lon": 114.9548502, + "population": 21392 + }, + { + "name": "пгт Излучинск", + "lat": 60.9530651, + "lon": 76.891202, + "population": 21389 + }, + { + "name": "Калачинск", + "lat": 55.059708, + "lon": 74.5655042, + "population": 21378 + }, + { + "name": "Красноармейск", + "lat": 51.0235479, + "lon": 45.6951366, + "population": 21350 + }, + { + "name": "Светлый", + "lat": 54.6773492, + "lon": 20.1356707, + "population": 21114 + }, + { + "name": "Рыбное", + "lat": 54.7256308, + "lon": 39.5135902, + "population": 21069 + }, + { + "name": "Котово", + "lat": 50.3205819, + "lon": 44.803057, + "population": 21028 + }, + { + "name": "Остров", + "lat": 57.3452065, + "lon": 28.3437983, + "population": 20923 + }, + { + "name": "Бобров", + "lat": 51.0902207, + "lon": 40.031849, + "population": 20871 + }, + { + "name": "пгт Кольцово рп", + "lat": 54.939754, + "lon": 83.189226, + "population": 20862 + }, + { + "name": "село Дыгулыбгей", + "lat": 43.6605157, + "lon": 43.5399799, + "population": 20852 + }, + { + "name": "Колпашево", + "lat": 58.3114151, + "lon": 82.9027717, + "population": 20824 + }, + { + "name": "Новопавловск", + "lat": 43.957372, + "lon": 43.631908, + "population": 20781 + }, + { + "name": "Тогучин", + "lat": 55.2251228, + "lon": 84.4103609, + "population": 20766 + }, + { + "name": "Зарайск", + "lat": 54.7624731, + "lon": 38.8851435, + "population": 20736 + }, + { + "name": "Чегем", + "lat": 43.567119, + "lon": 43.5866245, + "population": 20736 + }, + { + "name": "село Карабудахкент", + "lat": 42.7101719, + "lon": 47.5652001, + "population": 20710 + }, + { + "name": "Октябрьск", + "lat": 53.1641278, + "lon": 48.6707034, + "population": 20703 + }, + { + "name": "Армянск", + "lat": 46.1058917, + "lon": 33.6911918, + "population": 20692 + }, + { + "name": "Ряжск", + "lat": 53.7068457, + "lon": 40.0521364, + "population": 20634 + }, + { + "name": "Сысерть", + "lat": 56.5005302, + "lon": 60.8190383, + "population": 20634 + }, + { + "name": "Буй", + "lat": 58.4733865, + "lon": 41.5306164, + "population": 20564 + }, + { + "name": "пгт Безенчук", + "lat": 52.9844016, + "lon": 49.4332576, + "population": 20516 + }, + { + "name": "Исилькуль", + "lat": 54.9095226, + "lon": 71.2815165, + "population": 20515 + }, + { + "name": "Хотьково", + "lat": 56.2515987, + "lon": 37.9394556, + "population": 20466 + }, + { + "name": "Шарья", + "lat": 58.3760015, + "lon": 45.4060912, + "population": 20439 + }, + { + "name": "Арск", + "lat": 56.0913566, + "lon": 49.8769472, + "population": 20421 + }, + { + "name": "Нижний Ломов", + "lat": 53.5301144, + "lon": 43.6730824, + "population": 20421 + }, + { + "name": "Пикалево", + "lat": 59.5131477, + "lon": 34.1772986, + "population": 20388 + }, + { + "name": "Оха", + "lat": 53.5868177, + "lon": 142.9412704, + "population": 20357 + }, + { + "name": "пгт Монино рп", + "lat": 55.8393791, + "lon": 38.1953929, + "population": 20313 + }, + { + "name": "Инта", + "lat": 66.0367224, + "lon": 60.1153645, + "population": 20271 + }, + { + "name": "Сергач", + "lat": 55.5200817, + "lon": 45.4813786, + "population": 20256 + }, + { + "name": "Бологое", + "lat": 57.8855456, + "lon": 34.0537078, + "population": 20234 + }, + { + "name": "Котельнич", + "lat": 58.3034526, + "lon": 48.3475879, + "population": 20144 + }, + { + "name": "Лебедянь", + "lat": 53.0155445, + "lon": 39.143535, + "population": 20049 + }, + { + "name": "станица Зеленчукская", + "lat": 43.8608392, + "lon": 41.5827377, + "population": 20009 + }, + { + "name": "Белоярский", + "lat": 63.7120526, + "lon": 66.6773163, + "population": 19994 + }, + { + "name": "Агрыз", + "lat": 56.5234282, + "lon": 52.9943111, + "population": 19991 + }, + { + "name": "Нерехта", + "lat": 57.4543591, + "lon": 40.5725027, + "population": 19977 + }, + { + "name": "пгт Боровский рп", + "lat": 57.0437279, + "lon": 65.7219998, + "population": 19972 + }, + { + "name": "Буинск", + "lat": 54.964071, + "lon": 48.2901712, + "population": 19968 + }, + { + "name": "станица Выселки", + "lat": 45.5801087, + "lon": 39.6553854, + "population": 19955 + }, + { + "name": "Терек", + "lat": 43.4837944, + "lon": 44.140333, + "population": 19948 + }, + { + "name": "село Новая Усмань (часть 2)", + "lat": 51.6440564, + "lon": 39.4129162, + "population": 19939 + }, + { + "name": "Тарко-Сале", + "lat": 64.9117423, + "lon": 77.7610229, + "population": 19900 + }, + { + "name": "Черепаново", + "lat": 54.2206618, + "lon": 83.3724671, + "population": 19900 + }, + { + "name": "Никольск", + "lat": 53.7138244, + "lon": 46.0799725, + "population": 19873 + }, + { + "name": "Куровское", + "lat": 55.5790488, + "lon": 38.920837, + "population": 19857 + }, + { + "name": "Ковылкино", + "lat": 54.0391206, + "lon": 43.9191408, + "population": 19793 + }, + { + "name": "Козьмодемьянск", + "lat": 56.3333702, + "lon": 46.5465866, + "population": 19731 + }, + { + "name": "Данков", + "lat": 53.2576888, + "lon": 39.1456386, + "population": 19726 + }, + { + "name": "Фокино", + "lat": 42.9707585, + "lon": 132.4110104, + "population": 19711 + }, + { + "name": "пгт Каа-Хем", + "lat": 51.6974519, + "lon": 94.559472, + "population": 19686 + }, + { + "name": "Усмань", + "lat": 52.0444077, + "lon": 39.7263813, + "population": 19662 + }, + { + "name": "посёлок Строитель", + "lat": 52.6512001, + "lon": 41.4310661, + "population": 19647 + }, + { + "name": "Омутнинск", + "lat": 58.6698906, + "lon": 52.189375, + "population": 19629 + }, + { + "name": "рп Приютово", + "lat": 53.8951581, + "lon": 53.9369819, + "population": 19595 + }, + { + "name": "село Чалтырь", + "lat": 47.2826729, + "lon": 39.4992447, + "population": 19583 + }, + { + "name": "Пущино", + "lat": 54.8351813, + "lon": 37.6265045, + "population": 19578 + }, + { + "name": "Дудинка", + "lat": 69.4032072, + "lon": 86.1909075, + "population": 19556 + }, + { + "name": "поселок Трудовое", + "lat": 43.3002872, + "lon": 132.0632507, + "population": 19543 + }, + { + "name": "Черноголовка", + "lat": 56.0099863, + "lon": 38.3792964, + "population": 19530 + }, + { + "name": "Оса", + "lat": 57.2889708, + "lon": 55.468868, + "population": 19523 + }, + { + "name": "пгт Кокошкино дп", + "lat": 55.598467, + "lon": 37.167633, + "population": 19490 + }, + { + "name": "пгт Промышленная", + "lat": 54.916573, + "lon": 85.6376222, + "population": 19484 + }, + { + "name": "пгт Ленинкент", + "lat": 42.9703441, + "lon": 47.3535191, + "population": 19438 + }, + { + "name": "поселок Ойсхара", + "lat": 43.2627893, + "lon": 46.2498555, + "population": 19415 + }, + { + "name": "Зея", + "lat": 53.7340347, + "lon": 127.2657094, + "population": 19414 + }, + { + "name": "станица Суворовская", + "lat": 44.1975139, + "lon": 42.6499527, + "population": 19378 + }, + { + "name": "Зверево", + "lat": 48.0434167, + "lon": 40.1265252, + "population": 19353 + }, + { + "name": "поселок Игра", + "lat": 57.5401354, + "lon": 53.0861913, + "population": 19319 + }, + { + "name": "село Гойты", + "lat": 43.1609736, + "lon": 45.6212637, + "population": 19198 + }, + { + "name": "станица Брюховецкая", + "lat": 45.8020888, + "lon": 38.9971898, + "population": 19154 + }, + { + "name": "станица Незлобная", + "lat": 44.1168985, + "lon": 43.408563, + "population": 19068 + }, + { + "name": "пгт Березовка", + "lat": 56.037108, + "lon": 93.1266, + "population": 19059 + }, + { + "name": "поселок Ува", + "lat": 56.9820545, + "lon": 52.1873031, + "population": 19057 + }, + { + "name": "Арамиль", + "lat": 56.6915504, + "lon": 60.8441135, + "population": 19013 + }, + { + "name": "Пролетарск", + "lat": 46.7038132, + "lon": 41.7276002, + "population": 18983 + }, + { + "name": "Ардон", + "lat": 43.1755904, + "lon": 44.2956828, + "population": 18956 + }, + { + "name": "Лодейное Поле", + "lat": 60.7320844, + "lon": 33.5522164, + "population": 18905 + }, + { + "name": "посёлок Саракташ", + "lat": 51.7830951, + "lon": 56.3652582, + "population": 18789 + }, + { + "name": "Приозерск", + "lat": 61.03598, + "lon": 30.115586, + "population": 18777 + }, + { + "name": "станица Холмская", + "lat": 44.848128, + "lon": 38.3892456, + "population": 18699 + }, + { + "name": "Кировград", + "lat": 57.4298874, + "lon": 60.0624019, + "population": 18698 + }, + { + "name": "село Раевский", + "lat": 54.0650359, + "lon": 54.932837, + "population": 18698 + }, + { + "name": "Николаевск-на-Амуре", + "lat": 53.1461923, + "lon": 140.7110577, + "population": 18631 + }, + { + "name": "Нелидово", + "lat": 56.223391, + "lon": 32.7766208, + "population": 18603 + }, + { + "name": "пгт Свободы рп", + "lat": 44.0248348, + "lon": 43.0514483, + "population": 18582 + }, + { + "name": "станица Новопокровская", + "lat": 45.9538509, + "lon": 40.7070143, + "population": 18559 + }, + { + "name": "село Чигири", + "lat": 50.3391503, + "lon": 127.5076851, + "population": 18538 + }, + { + "name": "Харабали", + "lat": 47.4089236, + "lon": 47.2525264, + "population": 18514 + }, + { + "name": "Няндома", + "lat": 61.6654144, + "lon": 40.2063093, + "population": 18473 + }, + { + "name": "село Автуры", + "lat": 43.163267, + "lon": 46.0031899, + "population": 18446 + }, + { + "name": "Нижняя Тура", + "lat": 58.6310449, + "lon": 59.8520228, + "population": 18392 + }, + { + "name": "Пласт", + "lat": 54.3691735, + "lon": 60.815252, + "population": 18379 + }, + { + "name": "Новый Оскол", + "lat": 50.764439, + "lon": 37.863064, + "population": 18359 + }, + { + "name": "пгт Тарки", + "lat": 42.9445024, + "lon": 47.4957949, + "population": 18233 + }, + { + "name": "Суровикино", + "lat": 48.6189098, + "lon": 42.8542126, + "population": 18227 + }, + { + "name": "Боготол", + "lat": 56.2098683, + "lon": 89.5300867, + "population": 18206 + }, + { + "name": "рп Южный", + "lat": 53.2513304, + "lon": 83.6947313, + "population": 18098 + }, + { + "name": "Ершов", + "lat": 51.35079, + "lon": 48.2763191, + "population": 18095 + }, + { + "name": "Нефтегорск", + "lat": 52.79725, + "lon": 51.1638245, + "population": 18076 + }, + { + "name": "пгт Тальменка рп", + "lat": 53.8172662, + "lon": 83.5691716, + "population": 18070 + }, + { + "name": "Слюдянка", + "lat": 51.6563035, + "lon": 103.7186495, + "population": 18058 + }, + { + "name": "село Бабаюрт", + "lat": 43.6011227, + "lon": 46.7793044, + "population": 18039 + }, + { + "name": "станица Нестеровская", + "lat": 43.2381741, + "lon": 45.0491681, + "population": 17981 + }, + { + "name": "Электроугли", + "lat": 55.7170793, + "lon": 38.219367, + "population": 17944 + }, + { + "name": "Кукмор", + "lat": 56.1860939, + "lon": 50.8971592, + "population": 17886 + }, + { + "name": "Кяхта", + "lat": 50.3466144, + "lon": 106.453365, + "population": 17877 + }, + { + "name": "пгт Удельная дп", + "lat": 55.6349087, + "lon": 38.049353, + "population": 17846 + }, + { + "name": "Судак", + "lat": 44.8505071, + "lon": 34.9761502, + "population": 17834 + }, + { + "name": "Баймак", + "lat": 52.5913527, + "lon": 58.3111821, + "population": 17833 + }, + { + "name": "пгт Линево рп", + "lat": 54.4570315, + "lon": 83.3815692, + "population": 17779 + }, + { + "name": "посёлок завода Мосрентген", + "lat": 55.620947, + "lon": 37.473815, + "population": 17758 + }, + { + "name": "Покров", + "lat": 55.9167157, + "lon": 39.1733602, + "population": 17747 + }, + { + "name": "село Высокая Гора", + "lat": 55.912126, + "lon": 49.312767, + "population": 17736 + }, + { + "name": "Стародуб", + "lat": 52.5853129, + "lon": 32.7603416, + "population": 17687 + }, + { + "name": "Жуковка", + "lat": 53.5340871, + "lon": 33.7302336, + "population": 17628 + }, + { + "name": "Шахунья", + "lat": 57.6763123, + "lon": 46.6128512, + "population": 17626 + }, + { + "name": "Калач", + "lat": 50.4240228, + "lon": 41.0162465, + "population": 17624 + }, + { + "name": "Суворов", + "lat": 54.122088, + "lon": 36.490348, + "population": 17598 + }, + { + "name": "посёлок ВНИИССОК", + "lat": 55.6569429, + "lon": 37.2117743, + "population": 17597 + }, + { + "name": "Радужный", + "lat": 55.9960812, + "lon": 40.3322223, + "population": 17569 + }, + { + "name": "Льгов", + "lat": 51.65971, + "lon": 35.261144, + "population": 17557 + }, + { + "name": "Енисейск", + "lat": 58.4485678, + "lon": 92.1650748, + "population": 17537 + }, + { + "name": "село Белая Глина", + "lat": 46.0736706, + "lon": 40.8713114, + "population": 17470 + }, + { + "name": "Карачев", + "lat": 53.1296862, + "lon": 34.9887384, + "population": 17449 + }, + { + "name": "Белогорск", + "lat": 45.0570816, + "lon": 34.599941, + "population": 17445 + }, + { + "name": "Собинка", + "lat": 55.9938217, + "lon": 40.0178919, + "population": 17444 + }, + { + "name": "пгт Лучегорск", + "lat": 46.438443, + "lon": 134.289358, + "population": 17437 + }, + { + "name": "станица Медведовская", + "lat": 45.4491448, + "lon": 39.0101617, + "population": 17404 + }, + { + "name": "пгт Ванино", + "lat": 49.0909744, + "lon": 140.2563927, + "population": 17326 + }, + { + "name": "Талдом", + "lat": 56.7308601, + "lon": 37.5276018, + "population": 17317 + }, + { + "name": "пгт Васильево", + "lat": 55.8369276, + "lon": 48.7040265, + "population": 17286 + }, + { + "name": "Юрьев-Польский", + "lat": 56.4936868, + "lon": 39.6680526, + "population": 17276 + }, + { + "name": "Абдулино", + "lat": 53.6778052, + "lon": 53.6473384, + "population": 17274 + }, + { + "name": "станица Старощербиновская", + "lat": 46.6297567, + "lon": 38.6673969, + "population": 17251 + }, + { + "name": "Константиновск", + "lat": 47.5773447, + "lon": 41.0967357, + "population": 17207 + }, + { + "name": "село Бачи-Юрт", + "lat": 43.2209854, + "lon": 46.194994, + "population": 17173 + }, + { + "name": "Куса", + "lat": 55.3386556, + "lon": 59.4386134, + "population": 17136 + }, + { + "name": "пгт Коченево рп", + "lat": 55.019014, + "lon": 82.2060574, + "population": 17053 + }, + { + "name": "село Осиново", + "lat": 55.8775039, + "lon": 48.8897995, + "population": 16991 + }, + { + "name": "село Нижнее Казанище", + "lat": 42.760984, + "lon": 47.161627, + "population": 16984 + }, + { + "name": "Онега", + "lat": 63.9162842, + "lon": 38.0805165, + "population": 16947 + }, + { + "name": "Новомичуринск", + "lat": 54.037654, + "lon": 39.7466785, + "population": 16900 + }, + { + "name": "Плавск", + "lat": 53.7095277, + "lon": 37.2862058, + "population": 16893 + }, + { + "name": "село Майма", + "lat": 52.0037707, + "lon": 85.8962579, + "population": 16890 + }, + { + "name": "село Цоци-Юрт", + "lat": 43.2426159, + "lon": 46.0018219, + "population": 16890 + }, + { + "name": "пгт Янино-1", + "lat": 59.945394, + "lon": 30.5611967, + "population": 16886 + }, + { + "name": "деревня Сапроново", + "lat": 55.529323, + "lon": 37.712318, + "population": 16817 + }, + { + "name": "деревня Пыхтино", + "lat": 55.6209417, + "lon": 37.3002729, + "population": 16803 + }, + { + "name": "село Кантышево", + "lat": 43.2281058, + "lon": 44.6484198, + "population": 16783 + }, + { + "name": "Козельск", + "lat": 54.0348045, + "lon": 35.7806829, + "population": 16759 + }, + { + "name": "село Краснокумское", + "lat": 44.1748104, + "lon": 43.4967116, + "population": 16702 + }, + { + "name": "Нытва", + "lat": 57.9336576, + "lon": 55.3355911, + "population": 16675 + }, + { + "name": "Осташков", + "lat": 57.1457822, + "lon": 33.1116377, + "population": 16674 + }, + { + "name": "село Кинель-Черкассы", + "lat": 53.4640756, + "lon": 51.5193824, + "population": 16658 + }, + { + "name": "Зеленоградск", + "lat": 54.9599261, + "lon": 20.4752742, + "population": 16625 + }, + { + "name": "село Новая Усмань (часть 1)", + "lat": 51.6440564, + "lon": 39.4129162, + "population": 16601 + }, + { + "name": "пгт Шушенское", + "lat": 53.333753, + "lon": 91.935624, + "population": 16573 + }, + { + "name": "Туринск", + "lat": 58.0394946, + "lon": 63.6981776, + "population": 16561 + }, + { + "name": "Краснослободск", + "lat": 48.7068501, + "lon": 44.5631832, + "population": 16545 + }, + { + "name": "пгт Агинское", + "lat": 51.1036078, + "lon": 114.5378536, + "population": 16511 + }, + { + "name": "Нижняя Салда", + "lat": 58.0749078, + "lon": 60.7025219, + "population": 16505 + }, + { + "name": "Шимановск", + "lat": 52.005177, + "lon": 127.7005913, + "population": 16488 + }, + { + "name": "село Плиево", + "lat": 43.2854671, + "lon": 44.8359413, + "population": 16440 + }, + { + "name": "станица Гостагаевская", + "lat": 44.8948984, + "lon": 37.3162896, + "population": 16438 + }, + { + "name": "Яровое", + "lat": 52.9252719, + "lon": 78.5729219, + "population": 16424 + }, + { + "name": "город Поворино", + "lat": 51.195289, + "lon": 42.2473555, + "population": 16417 + }, + { + "name": "Бакал", + "lat": 54.9406707, + "lon": 58.8051112, + "population": 16345 + }, + { + "name": "Инза", + "lat": 53.8549261, + "lon": 46.353359, + "population": 16293 + }, + { + "name": "Шумиха", + "lat": 55.2281331, + "lon": 63.2901783, + "population": 16264 + }, + { + "name": "пгт Шексна", + "lat": 59.2361104, + "lon": 38.5112653, + "population": 16246 + }, + { + "name": "Бикин", + "lat": 46.8185692, + "lon": 134.2550619, + "population": 16240 + }, + { + "name": "Жуков", + "lat": 55.0302319, + "lon": 36.7393387, + "population": 16224 + }, + { + "name": "Светлогорск", + "lat": 54.9439073, + "lon": 20.1514612, + "population": 16207 + }, + { + "name": "Бокситогорск", + "lat": 59.473579, + "lon": 33.8457452, + "population": 16185 + }, + { + "name": "Кирсанов", + "lat": 52.6506577, + "lon": 42.7284824, + "population": 16164 + }, + { + "name": "пгт Рощино", + "lat": 60.2408076, + "lon": 29.6288048, + "population": 16162 + }, + { + "name": "Камызяк", + "lat": 46.1106454, + "lon": 48.0732549, + "population": 16154 + }, + { + "name": "пгт Белый Яр", + "lat": 61.260563, + "lon": 73.252472, + "population": 16141 + }, + { + "name": "пгт им. Воровского рп", + "lat": 55.7219506, + "lon": 38.3276944, + "population": 16127 + }, + { + "name": "Подпорожье", + "lat": 60.912778, + "lon": 34.1567171, + "population": 16123 + }, + { + "name": "посёлок Дубовое", + "lat": 50.5303214, + "lon": 36.5682872, + "population": 16101 + }, + { + "name": "Гаврилов-Ям", + "lat": 57.3091517, + "lon": 39.8544895, + "population": 16084 + }, + { + "name": "село Нартан", + "lat": 43.5054195, + "lon": 43.7016693, + "population": 16075 + }, + { + "name": "Покачи", + "lat": 61.7422837, + "lon": 75.5940895, + "population": 16040 + }, + { + "name": "Поронайск", + "lat": 49.2387506, + "lon": 143.1008839, + "population": 16026 + }, + { + "name": "Руза", + "lat": 55.7015428, + "lon": 36.1959789, + "population": 16014 + }, + { + "name": "Мензелинск", + "lat": 55.7270085, + "lon": 53.1005154, + "population": 16008 + }, + { + "name": "деревня Куюки", + "lat": 55.7128384, + "lon": 49.3565309, + "population": 15977 + }, + { + "name": "Иланский", + "lat": 56.2375329, + "lon": 96.0673849, + "population": 15945 + }, + { + "name": "посёлок Орловский", + "lat": 46.874451, + "lon": 42.05931, + "population": 15942 + }, + { + "name": "село Каякент", + "lat": 42.3862993, + "lon": 47.9054979, + "population": 15923 + }, + { + "name": "Сельцо", + "lat": 53.3739073, + "lon": 34.106007, + "population": 15906 + }, + { + "name": "станица Раевская", + "lat": 44.836305, + "lon": 37.5496436, + "population": 15902 + }, + { + "name": "село Учкекен", + "lat": 43.9390432, + "lon": 42.5155851, + "population": 15849 + }, + { + "name": "Райчихинск", + "lat": 49.7941325, + "lon": 129.411213, + "population": 15797 + }, + { + "name": "Ковдор", + "lat": 67.5662699, + "lon": 30.4741763, + "population": 15770 + }, + { + "name": "деревня Островцы", + "lat": 55.5912674, + "lon": 37.9926244, + "population": 15752 + }, + { + "name": "село Супсех", + "lat": 44.8948984, + "lon": 37.3162896, + "population": 15736 + }, + { + "name": "Кондрово", + "lat": 54.7959825, + "lon": 35.9275777, + "population": 15734 + }, + { + "name": "Мамадыш", + "lat": 55.7150947, + "lon": 51.4128069, + "population": 15726 + }, + { + "name": "Межгорье", + "lat": 54.2396811, + "lon": 57.9612743, + "population": 15697 + }, + { + "name": "посёлок Зимовники", + "lat": 47.1453719, + "lon": 42.4676643, + "population": 15691 + }, + { + "name": "пгт Емельяново", + "lat": 56.1686437, + "lon": 92.6866976, + "population": 15649 + }, + { + "name": "Болотное", + "lat": 55.6692803, + "lon": 84.3906602, + "population": 15644 + }, + { + "name": "Кизел", + "lat": 59.0512352, + "lon": 57.6471132, + "population": 15619 + }, + { + "name": "пгт Товарково", + "lat": 54.67724, + "lon": 35.942735, + "population": 15606 + }, + { + "name": "село Поселье", + "lat": 51.8057402, + "lon": 107.5376329, + "population": 15603 + }, + { + "name": "станица Анапская", + "lat": 44.8948984, + "lon": 37.3162896, + "population": 15593 + }, + { + "name": "пгт Кратово дп", + "lat": 55.5973949, + "lon": 38.1635172, + "population": 15565 + }, + { + "name": "пгт Семендер", + "lat": 42.9911799, + "lon": 47.4021163, + "population": 15565 + }, + { + "name": "Жирновск", + "lat": 50.9769212, + "lon": 44.7857873, + "population": 15555 + }, + { + "name": "посёлок Навля", + "lat": 52.8301087, + "lon": 34.5066338, + "population": 15536 + }, + { + "name": "пгт Заводской", + "lat": 43.1077853, + "lon": 44.6453692, + "population": 15508 + }, + { + "name": "Дегтярск", + "lat": 56.7048, + "lon": 60.0790826, + "population": 15497 + }, + { + "name": "Свирск", + "lat": 53.0838888, + "lon": 103.3413682, + "population": 15485 + }, + { + "name": "пгт Сузун рп", + "lat": 53.7891996, + "lon": 82.3161043, + "population": 15482 + }, + { + "name": "Ясный", + "lat": 51.0368874, + "lon": 59.8742748, + "population": 15471 + }, + { + "name": "село Подстепки", + "lat": 53.5179261, + "lon": 49.1533951, + "population": 15455 + }, + { + "name": "посёлок Зональная Станция", + "lat": 56.4268771, + "lon": 85.0227819, + "population": 15421 + }, + { + "name": "станица Варениковская", + "lat": 45.1198484, + "lon": 37.6385129, + "population": 15385 + }, + { + "name": "Касли", + "lat": 55.8869825, + "lon": 60.7422584, + "population": 15383 + }, + { + "name": "посёлок Усть-Ордынский", + "lat": 52.8052189, + "lon": 104.7536738, + "population": 15364 + }, + { + "name": "село Гехи", + "lat": 43.1606137, + "lon": 45.4742778, + "population": 15352 + }, + { + "name": "Новоаннинский", + "lat": 50.5295721, + "lon": 42.6666194, + "population": 15351 + }, + { + "name": "пгт Анна", + "lat": 51.489329, + "lon": 40.4242248, + "population": 15316 + }, + { + "name": "посёлок подсобного хозяйства \"Воскресенское\"", + "lat": 55.5296175, + "lon": 37.4458356, + "population": 15293 + }, + { + "name": "Нерчинск", + "lat": 51.9594649, + "lon": 116.5853127, + "population": 15290 + }, + { + "name": "село Ахты", + "lat": 41.4591981, + "lon": 47.7495262, + "population": 15285 + }, + { + "name": "Магас", + "lat": 43.1686871, + "lon": 44.8131101, + "population": 15271 + }, + { + "name": "Ясногорск", + "lat": 54.4794749, + "lon": 37.689663, + "population": 15269 + }, + { + "name": "Новоузенск", + "lat": 50.4551199, + "lon": 48.1411389, + "population": 15216 + }, + { + "name": "посёлок Знамя Октября", + "lat": 55.47574, + "lon": 37.538359, + "population": 15199 + }, + { + "name": "село Катар-Юрт", + "lat": 43.1688806, + "lon": 45.370387, + "population": 15199 + }, + { + "name": "пгт Запрудня рп", + "lat": 56.5609117, + "lon": 37.4336132, + "population": 15189 + }, + { + "name": "Бородино", + "lat": 55.9053464, + "lon": 94.9022323, + "population": 15174 + }, + { + "name": "пгт Рефтинский", + "lat": 57.0906513, + "lon": 61.6564554, + "population": 15164 + }, + { + "name": "пгт Софрино рп", + "lat": 56.1354141, + "lon": 37.9260237, + "population": 15136 + }, + { + "name": "пгт Вырица", + "lat": 59.4172905, + "lon": 30.3469408, + "population": 15086 + }, + { + "name": "Рыльск", + "lat": 51.568137, + "lon": 34.6802211, + "population": 15069 + }, + { + "name": "Купино", + "lat": 54.3661206, + "lon": 77.2973372, + "population": 15065 + }, + { + "name": "село Хомутово", + "lat": 52.4652523, + "lon": 104.3620353, + "population": 15064 + }, + { + "name": "пгт Большие Вязёмы рп", + "lat": 55.6291397, + "lon": 37.0053667, + "population": 15060 + }, + { + "name": "Петровск-Забайкальский", + "lat": 51.2748412, + "lon": 108.8468131, + "population": 15015 + }, + { + "name": "Почеп", + "lat": 52.9153976, + "lon": 33.4745131, + "population": 14991 + }, + { + "name": "Палласовка", + "lat": 50.0502023, + "lon": 46.8804085, + "population": 14966 + }, + { + "name": "Калининск", + "lat": 51.4993043, + "lon": 44.4710883, + "population": 14949 + }, + { + "name": "Щигры", + "lat": 51.8785827, + "lon": 36.8911689, + "population": 14927 + }, + { + "name": "Барыш", + "lat": 53.6534032, + "lon": 47.1180501, + "population": 14924 + }, + { + "name": "пгт Чернянка", + "lat": 50.940917, + "lon": 37.804134, + "population": 14896 + }, + { + "name": "Сортавала", + "lat": 61.7032224, + "lon": 30.6917823, + "population": 14867 + }, + { + "name": "пгт Грибановский", + "lat": 51.4564459, + "lon": 41.9799021, + "population": 14830 + }, + { + "name": "станица Васюринская", + "lat": 45.1181463, + "lon": 39.4216389, + "population": 14829 + }, + { + "name": "Талица", + "lat": 57.0123163, + "lon": 63.7320587, + "population": 14808 + }, + { + "name": "Куртамыш", + "lat": 54.9369493, + "lon": 64.4203207, + "population": 14806 + }, + { + "name": "Сухиничи", + "lat": 54.0973084, + "lon": 35.344484, + "population": 14806 + }, + { + "name": "Заполярный", + "lat": 69.4131822, + "lon": 30.7985579, + "population": 14791 + }, + { + "name": "посёлок Развилка", + "lat": 55.5910519, + "lon": 37.7529499, + "population": 14788 + }, + { + "name": "Дубовка", + "lat": 49.0554351, + "lon": 44.8269342, + "population": 14779 + }, + { + "name": "пгт Лесной Городок дп", + "lat": 55.636239, + "lon": 37.212612, + "population": 14765 + }, + { + "name": "Белокуриха", + "lat": 51.9961123, + "lon": 84.9839441, + "population": 14735 + }, + { + "name": "Цимлянск", + "lat": 47.6477932, + "lon": 42.092975, + "population": 14731 + }, + { + "name": "село Кулунда", + "lat": 52.5661304, + "lon": 78.9368917, + "population": 14708 + }, + { + "name": "пгт Новый Городок", + "lat": 54.3036194, + "lon": 86.2934438, + "population": 14691 + }, + { + "name": "Навашино", + "lat": 55.5438077, + "lon": 42.1887675, + "population": 14664 + }, + { + "name": "Катав-Ивановск", + "lat": 54.7520677, + "lon": 58.1984888, + "population": 14663 + }, + { + "name": "пгт Ильинский рп", + "lat": 55.6202604, + "lon": 38.1055975, + "population": 14645 + }, + { + "name": "Краснозаводск", + "lat": 56.4409201, + "lon": 38.2320198, + "population": 14639 + }, + { + "name": "деревня Кондратово", + "lat": 57.9760676, + "lon": 56.1090821, + "population": 14631 + }, + { + "name": "Советск", + "lat": 57.5841376, + "lon": 48.9589455, + "population": 14626 + }, + { + "name": "станица Егорлыкская", + "lat": 46.553251, + "lon": 40.683235, + "population": 14602 + }, + { + "name": "село Кулешовка", + "lat": 47.0857816, + "lon": 39.5270717, + "population": 14568 + }, + { + "name": "пгт Селятино рп", + "lat": 55.5141029, + "lon": 36.9798359, + "population": 14523 + }, + { + "name": "Грязовец", + "lat": 58.875883, + "lon": 40.2485948, + "population": 14505 + }, + { + "name": "Красновишерск", + "lat": 60.3903039, + "lon": 57.0535948, + "population": 14460 + }, + { + "name": "поселок Балезино", + "lat": 57.983878, + "lon": 53.012307, + "population": 14459 + }, + { + "name": "станица Гиагинская", + "lat": 44.8847971, + "lon": 40.0577305, + "population": 14445 + }, + { + "name": "село Алхан-Кала", + "lat": 43.2624396, + "lon": 45.534823, + "population": 14423 + }, + { + "name": "Очер", + "lat": 57.8852534, + "lon": 54.7161526, + "population": 14385 + }, + { + "name": "город Богучар", + "lat": 49.93528, + "lon": 40.5591512, + "population": 14370 + }, + { + "name": "Волгореченск", + "lat": 57.4424513, + "lon": 41.1592512, + "population": 14355 + }, + { + "name": "село Донское", + "lat": 45.4672559, + "lon": 41.989783, + "population": 14338 + }, + { + "name": "Приволжск", + "lat": 57.380718, + "lon": 41.2807571, + "population": 14332 + }, + { + "name": "станица Старовеличковская", + "lat": 45.4329681, + "lon": 38.7244163, + "population": 14310 + }, + { + "name": "Ивдель", + "lat": 60.6945985, + "lon": 60.4245258, + "population": 14306 + }, + { + "name": "Чудово", + "lat": 59.1248174, + "lon": 31.6866267, + "population": 14302 + }, + { + "name": "Красный Кут", + "lat": 50.9597049, + "lon": 46.9711885, + "population": 14296 + }, + { + "name": "Яранск", + "lat": 57.3041231, + "lon": 47.8478976, + "population": 14284 + }, + { + "name": "село Старые-Атаги", + "lat": 43.1229624, + "lon": 45.7414172, + "population": 14280 + }, + { + "name": "пгт Средняя Ахтуба рп", + "lat": 48.7136601, + "lon": 44.8600821, + "population": 14241 + }, + { + "name": "Агидель", + "lat": 55.8997421, + "lon": 53.9221357, + "population": 14219 + }, + { + "name": "посёлок Нижнесортымский", + "lat": 62.4428117, + "lon": 71.7652884, + "population": 14217 + }, + { + "name": "рп Усть-Абакан", + "lat": 53.8329508, + "lon": 91.3943386, + "population": 14210 + }, + { + "name": "село Красногвардейское", + "lat": 45.8450489, + "lon": 41.5144643, + "population": 14200 + }, + { + "name": "село Новый Хушет", + "lat": 42.8993406, + "lon": 47.5613354, + "population": 14180 + }, + { + "name": "Полярные Зори", + "lat": 67.3738152, + "lon": 32.4891688, + "population": 14146 + }, + { + "name": "посёлок Майский", + "lat": 50.5218213, + "lon": 36.4544411, + "population": 14143 + }, + { + "name": "Ужур", + "lat": 55.3142192, + "lon": 89.8334338, + "population": 14134 + }, + { + "name": "Шлиссельбург", + "lat": 59.9443081, + "lon": 31.0332835, + "population": 14131 + }, + { + "name": "Гвардейск", + "lat": 54.6588822, + "lon": 21.0500744, + "population": 14122 + }, + { + "name": "станица Калининская", + "lat": 45.4855794, + "lon": 38.6616721, + "population": 14121 + }, + { + "name": "Кашин", + "lat": 57.3600935, + "lon": 37.6118448, + "population": 14113 + } ] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index c7dead5b..9c09c919 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,6 +3,6 @@ services: web: image: nginx:alpine ports: - - "8081:80" + - "8080:80" volumes: - ./:/usr/share/nginx/html:ro diff --git a/index.html b/index.html index 6fe5d307..93e43f69 100644 --- a/index.html +++ b/index.html @@ -3,13 +3,11 @@ - Прогноз погоды на карте + Прогноз погоды - - diff --git a/script.js b/script.js index 85ebf52c..92d4c5ef 100644 --- a/script.js +++ b/script.js @@ -1,23 +1,12 @@ -// ------------------- 1. КАРТА ------------------- let map = L.map('map').setView([64.0, 100.0], 4); L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png').addTo(map); let cities = []; let markers = {}; -// ------------------- 2. ЗАГРУЗКА ГОРОДОВ ------------------- async function loadCities() { console.log("Загрузка городов..."); try { - const response = await fetch('russia-cities.json'); - if (!response.ok) throw new Error(); - const raw = await response.json(); - if (raw[0] && raw[0].coords) { - cities = raw.map(c => ({ name: c.name, latitude: c.coords.lat, longitude: c.coords.lon })); - } else throw new Error(); - } catch(e) { - console.warn("russia-cities.json не загружен, пробуем cities.json"); - try { const response = await fetch('cities.json'); const raw = await response.json(); if (raw[0] && typeof raw[0].lat === 'number') { @@ -35,7 +24,6 @@ async function loadCities() { { name: "Казань", latitude: 55.796127, longitude: 49.106405 } ]; } - } console.log(`Загружено городов: ${cities.length}`); addMarkersToMap(); } @@ -53,7 +41,6 @@ function addMarkersToMap() { if (cities.length) map.setView([cities[0].latitude, cities[0].longitude], 6); } -// ------------------- 3. ПОИСК ------------------- const searchInput = document.getElementById('city-search'); const searchBtn = document.getElementById('search-btn'); @@ -71,10 +58,9 @@ function searchCity() { searchBtn.addEventListener('click', searchCity); searchInput.addEventListener('keypress', e => e.key === 'Enter' && searchCity()); -// ------------------- 4. ПОГОДА (Open-Meteo) ------------------- async function fetchWeather(lat, lon, cityName) { try { - document.getElementById('weather-title').innerHTML = `⏳ Загрузка погоды для ${cityName}...`; + document.getElementById('weather-title').innerHTML = `⏳ Загрузка погоды для города ${cityName}...`; const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max&timezone=auto&forecast_days=7`; const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); @@ -107,7 +93,6 @@ function showDemoWeather(cityName) { createOrUpdateChart('windChart', dates, winds, 'Ветер (м/с)', 'rgba(75,192,192,0.2)', 'rgba(75,192,192,1)', 'line'); } -// ------------------- 5. ГРАФИКИ (абсолютно надёжная версия) ------------------- function createOrUpdateChart(chartId, labels, data, label, bgColor, borderColor, type) { const canvas = document.getElementById(chartId); if (!canvas) { @@ -115,14 +100,12 @@ function createOrUpdateChart(chartId, labels, data, label, bgColor, borderColor, return; } - // Проверяем, существует ли уже график и можно ли его обновить if (window[chartId] && typeof window[chartId].destroy === 'function') { window[chartId].destroy(); window[chartId] = null; } try { - // Создаём новый график window[chartId] = new Chart(canvas.getContext('2d'), { type: type, data: { @@ -145,10 +128,8 @@ function createOrUpdateChart(chartId, labels, data, label, bgColor, borderColor, console.error(`Ошибка создания графика ${chartId}:`, e); } - // Скрываем заглушку const infoMsg = document.getElementById('info-message'); if (infoMsg) infoMsg.style.display = 'none'; } -// ------------------- 6. ЗАПУСК ------------------- document.addEventListener('DOMContentLoaded', loadCities); \ No newline at end of file diff --git a/style.css b/style.css index 253be893..95cfd571 100644 --- a/style.css +++ b/style.css @@ -11,14 +11,12 @@ body { overflow: hidden; } -/* Основной контейнер: карта + панель */ .app-container { display: flex; height: 100vh; width: 100%; } -/* Левая часть — карта */ .map-container { flex: 2; position: relative; @@ -34,7 +32,6 @@ body { border-radius: 12px; } -/* Правая панель — поиск + графики */ .dashboard { flex: 1.2; background: white; @@ -47,7 +44,6 @@ body { padding: 20px; } -/* Поиск */ .search-section { margin-bottom: 25px; background: #f8f9fa; @@ -97,7 +93,6 @@ body { background: #1f5e7a; } -/* Заголовок погоды */ .weather-header { margin: 10px 0 20px 0; text-align: center; @@ -105,14 +100,13 @@ body { #weather-title { font-size: 1.5rem; - font-weight: 600; + font-weight: 700; color: #0b3b4f; border-bottom: 3px solid #2c7da0; display: inline-block; padding-bottom: 5px; } -/* Блоки графиков */ .charts-container { display: flex; flex-direction: column; @@ -141,7 +135,6 @@ canvas { width: 100%; } -/* Информация при отсутствии данных */ .placeholder-message { text-align: center; color: #6c757d; @@ -151,7 +144,6 @@ canvas { margin-top: 20px; } -/* Адаптивность */ @media (max-width: 800px) { .app-container { flex-direction: column; @@ -168,14 +160,12 @@ canvas { } } -/* Стили для всплывающих подсказок Leaflet (немного улучшим) */ .leaflet-popup-content { font-size: 14px; font-weight: 500; color: #1e4663; } -/* Скролл для панели */ .dashboard::-webkit-scrollbar { width: 6px; } From dcbaf63fa2cecf296e6f5b4f4a28cab22ebebe59 Mon Sep 17 00:00:00 2001 From: Mary Date: Tue, 7 Apr 2026 23:59:36 +0400 Subject: [PATCH 7/7] =?UTF-8?q?=D1=84=D0=B8=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D0=BE=D0=B5=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cities.json | 12 ++++++++++++ script.js | 13 +++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/cities.json b/cities.json index ea37ad93..1651c2dc 100644 --- a/cities.json +++ b/cities.json @@ -1,4 +1,16 @@ [ + { + "name": "Москва", + "lat": 55.7558, + "lon": 37.6173, + "population": 12655050 + }, + { + "name": "Санкт-Петербург", + "lat": 59.9343, + "lon": 30.3351, + "population": 5384342 + }, { "name": "Новосибирск", "lat": 55.030187, diff --git a/script.js b/script.js index 92d4c5ef..72725355 100644 --- a/script.js +++ b/script.js @@ -1,5 +1,7 @@ let map = L.map('map').setView([64.0, 100.0], 4); -L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png').addTo(map); +L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap' +}).addTo(map); let cities = []; let markers = {}; @@ -32,7 +34,14 @@ function addMarkersToMap() { Object.values(markers).forEach(m => map.removeLayer(m)); markers = {}; cities.forEach(city => { - const marker = L.marker([city.latitude, city.longitude]).addTo(map); + const marker = L.circleMarker([city.latitude, city.longitude], { + radius: 8, + fillColor: '#2c7da0', + color: '#0c4f6c', + weight: 1, + opacity: 1, + fillOpacity: 0.8 + }).addTo(map); marker.on('click', () => fetchWeather(city.latitude, city.longitude, city.name)); marker.bindTooltip(city.name); markers[city.name] = marker;