xaxaxax
This commit is contained in:
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.12
|
||||
418
README.md
Normal file
418
README.md
Normal file
@@ -0,0 +1,418 @@
|
||||
# 🎓 Карта образовательных учреждений Челябинска# 🎓 Карта колледжей и техникумов Челябинска# Карта колледжей Челябинска
|
||||
|
||||
|
||||
|
||||
Интерактивная карта университетов, институтов, колледжей, техникумов, лицеев и училищ города Челябинска с использованием Yandex Maps API.
|
||||
|
||||
|
||||
|
||||
## 🎯 Что это?Интерактивная карта учебных заведений среднего профессионального образования в Челябинске с использованием Yandex Maps API.Интерактивная карта колледжей Челябинска с использованием Yandex Maps API.
|
||||
|
||||
|
||||
|
||||
Этот проект создает интерактивную карту, на которой отмечены все основные образовательные учреждения Челябинска. Карта включает в себя информацию о:
|
||||
|
||||
|
||||
|
||||
- Университетах и институтах ## 📋 Описание## Описание
|
||||
|
||||
- Колледжах и техникумах
|
||||
|
||||
- Лицеях и училищах
|
||||
|
||||
- Специализациях учреждений
|
||||
|
||||
- Контактной информацииПриложение создает интерактивную карту с 27 колледжами и техникумами Челябинска, найденными через 2GIS Places API. Карта включает подробную информацию о каждом учебном заведении, их специальностях и контактах.Этот проект создает интерактивную веб-карту с метками всех основных колледжей Челябинска. Каждая метка содержит подробную информацию об учебном заведении, включая:
|
||||
|
||||
- Адресах и координатах
|
||||
|
||||
|
||||
|
||||
## 🗺️ Функциональность
|
||||
|
||||
## 🚀 Установка и запуск- Название колледжа
|
||||
|
||||
- **Интерактивная карта** - удобное перемещение и масштабирование
|
||||
|
||||
- **Фильтрация по специальностям** - быстрый поиск нужного направления- Адрес
|
||||
|
||||
- **Подробная информация** - всплывающие окна с данными об учреждениях
|
||||
|
||||
- **Адаптивный дизайн** - работает на всех устройствах1. Убедитесь, что установлен Python 3.8+ и uv:- Описание специальностей
|
||||
|
||||
- **Статистика** - подсчет найденных учреждений
|
||||
|
||||
```bash- Контактную информацию (телефон, веб-сайт)
|
||||
|
||||
## 🏛️ База данных
|
||||
|
||||
# Проверить установку uv- Координаты на карте
|
||||
|
||||
В проекте содержится информация о **72 образовательных учреждениях** Челябинска:
|
||||
|
||||
uv --version
|
||||
|
||||
### По типам:
|
||||
|
||||
- **Университеты**: 4 учреждения```## Возможности
|
||||
|
||||
- **Институты**: 5 учреждений
|
||||
|
||||
- **Академии**: 1 учреждение
|
||||
|
||||
- **Колледжи**: 50 учреждений
|
||||
|
||||
- **Техникумы**: 7 учреждений2. Установите зависимости:- 📍 10 колледжей с точными координатами
|
||||
|
||||
- **Лицеи**: 3 учреждения
|
||||
|
||||
- **Училища**: 2 учреждения```bash- 🗺️ Интерактивная карта на базе Yandex Maps API
|
||||
|
||||
|
||||
|
||||
### По специализациям:uv sync- 💬 Всплывающие окна с подробной информацией
|
||||
|
||||
- Техника и технологии
|
||||
|
||||
- Экономика и управление ```- 📱 Адаптивный дизайн для мобильных устройств
|
||||
|
||||
- Медицина и здравоохранение
|
||||
|
||||
- Педагогика и образование- 🔍 Поиск и масштабирование карты
|
||||
|
||||
- Строительство и архитектура
|
||||
|
||||
- Искусство и культура3. Запустите приложение:- 📋 Список всех колледжей под картой
|
||||
|
||||
- Транспорт и логистика
|
||||
|
||||
- Информационные технологии```bash
|
||||
|
||||
- Право и юриспруденция
|
||||
|
||||
- Пищевая промышленностьpython main.py## Установка и запуск
|
||||
|
||||
- Энергетика
|
||||
|
||||
- Сельское хозяйство```
|
||||
|
||||
- Металлургия
|
||||
|
||||
- Химия### Предварительные требования
|
||||
|
||||
- Спорт и физическая культура
|
||||
|
||||
4. Откроется браузер с интерактивной картой колледжей.
|
||||
|
||||
## 🚀 Как использовать
|
||||
|
||||
- Python 3.12 или выше
|
||||
|
||||
### 1. Установка зависимостей
|
||||
|
||||
## ✨ Функционал- Пакетный менеджер uv (или pip)
|
||||
|
||||
Убедитесь, что у вас установлен Python 3.8+ и uv:
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
|
||||
# Проверить установку uv- **Интерактивная карта** с 27 метками колледжей и техникумов### Установка зависимостей
|
||||
|
||||
uv --version
|
||||
|
||||
- **Фильтрация по специальностям**: строительство, машиностроение, энергетика, экономика, медицина, педагогика, питание, транспорт, IT, право
|
||||
|
||||
# Установить зависимости
|
||||
|
||||
uv sync- **Подробная информация** о каждом учебном заведении в всплывающих окнах```bash
|
||||
|
||||
```
|
||||
|
||||
- **Контактная информация**: телефоны и сайты (где доступно)# Используя uv (рекомендуется)
|
||||
|
||||
### 2. Запуск приложения
|
||||
|
||||
- **Адаптивный дизайн** для мобильных устройствuv sync
|
||||
|
||||
```bash
|
||||
|
||||
uv run python main.py- **Автоматическое масштабирование** карты под выбранные фильтры
|
||||
|
||||
```
|
||||
|
||||
# Или используя pip
|
||||
|
||||
Приложение:
|
||||
|
||||
1. Загрузит данные об образовательных учреждениях## 📁 Структура проектаpip install -r requirements.txt
|
||||
|
||||
2. Создаст HTML-файл с интерактивной картой
|
||||
|
||||
3. Покажет статистику по всем учреждениям```
|
||||
|
||||
4. Предложит открыть карту в браузере
|
||||
|
||||
```
|
||||
|
||||
### 3. Настройка API ключа (опционально)
|
||||
|
||||
map_yandex/### Получение API ключа Yandex Maps
|
||||
|
||||
Для полного функционала получите бесплатный API ключ Yandex Maps:
|
||||
|
||||
1. Перейдите на https://developer.tech.yandex.ru/├── main.py # Основной скрипт приложения
|
||||
|
||||
2. Зарегистрируйтесь и получите ключ
|
||||
|
||||
3. Введите ключ при запуске приложения├── map_template.html # HTML шаблон карты1. Перейдите на [Yandex Developer Console](https://developer.tech.yandex.ru/)
|
||||
|
||||
|
||||
|
||||
## 📁 Структура проекта├── colleges_data.json # База данных колледжей (27 учреждений)2. Создайте новый проект или выберите существующий
|
||||
|
||||
|
||||
|
||||
```├── colleges_data_backup.json # Резервная копия данных3. Подключите Maps JavaScript API 2.1
|
||||
|
||||
map_yandex/
|
||||
|
||||
├── main.py # Основное приложение├── pyproject.toml # Конфигурация проекта и зависимости4. Скопируйте полученный API ключ
|
||||
|
||||
├── map_template.html # HTML шаблон карты
|
||||
|
||||
├── colleges_data.json # База данных учреждений├── uv.lock # Заблокированные версии зависимостей
|
||||
|
||||
├── chelyabinsk_colleges_map.html # Готовая карта (создается автоматически)
|
||||
|
||||
├── pyproject.toml # Конфигурация проекта└── README.md # Документация### Запуск
|
||||
|
||||
└── README.md # Документация
|
||||
|
||||
``````
|
||||
|
||||
|
||||
|
||||
## 🎯 Основные учреждения```bash
|
||||
|
||||
|
||||
|
||||
### Университеты:## 🏫 База данныхpython main.py
|
||||
|
||||
- Южно-Уральский государственный университет (ЮУрГУ)
|
||||
|
||||
- Челябинский государственный университет (ЧелГУ)```
|
||||
|
||||
- Челябинский государственный педагогический университет (ЧГПУ)
|
||||
|
||||
- Уральский государственный университет физической культурыБаза содержит **27 колледжей и техникумов**, включая:
|
||||
|
||||
|
||||
|
||||
### Институты:Программа:
|
||||
|
||||
- Челябинский филиал РАНХиГС
|
||||
|
||||
- Челябинский институт путей сообщения**Колледжи (22):**1. Загрузит данные о колледжах
|
||||
|
||||
- Южно-Уральский институт управления и экономики
|
||||
|
||||
- Челябинский филиал РГСУ- Челябинский юридический колледж2. Выведет список всех колледжей в консоль
|
||||
|
||||
- Челябинский филиал МПСУ
|
||||
|
||||
- Южно-Уральский государственный колледж 3. Запросит API ключ Yandex Maps (можно пропустить)
|
||||
|
||||
### Ведущие колледжи:
|
||||
|
||||
- Челябинский государственный колледж индустрии питания и торговли- Челябинский педагогический колледж №1 и №24. Создаст HTML файл с картой
|
||||
|
||||
- Челябинский медицинский колледж
|
||||
|
||||
- Челябинский базовый медицинский колледж- Челябинский медицинский колледж5. Предложит открыть карту в браузере
|
||||
|
||||
- Челябинский педагогический колледж №1
|
||||
|
||||
- Челябинский юридический колледж- Челябинский энергетический колледж им. С. М. Кирова
|
||||
|
||||
- Челябинский машиностроительный колледж
|
||||
|
||||
- Челябинский металлургический колледж- Южно-Уральский государственный технический колледж## Структура проекта
|
||||
|
||||
- Челябинский энергетический колледж
|
||||
|
||||
- Челябинский строительный колледж- Челябинский государственный колледж индустрии питания и торговли
|
||||
|
||||
- И многие другие...
|
||||
|
||||
- И другие...```
|
||||
|
||||
## 🔧 Технические детали
|
||||
|
||||
map_yandex/
|
||||
|
||||
- **Python 3.8+** - основной язык разработки
|
||||
|
||||
- **Jinja2** - шаблонизация HTML**Техникумы (5):**├── main.py # Основной скрипт
|
||||
|
||||
- **Yandex Maps API 2.1** - интерактивная карта
|
||||
|
||||
- **HTML5/CSS3/JavaScript** - фронтенд- Челябинский автотранспортный техникум├── map_template.html # HTML шаблон карты
|
||||
|
||||
- **JSON** - хранение данных
|
||||
|
||||
- Челябинский механико-технологический техникум├── colleges_data.json # Данные о колледжах
|
||||
|
||||
## 📱 Совместимость
|
||||
|
||||
- Челябинский радиотехнический техникум├── pyproject.toml # Конфигурация проекта
|
||||
|
||||
Карта работает во всех современных браузерах:
|
||||
|
||||
- Chrome/Chromium- Челябинский государственный промышленно-гуманитарный техникум им. А.В. Яковлева├── README.md # Документация
|
||||
|
||||
- Firefox
|
||||
|
||||
- Safari- Техникум-интернат инвалидов им. И.И. Шуба└── chelyabinsk_colleges_map.html # Сгенерированная карта
|
||||
|
||||
- Edge
|
||||
|
||||
- Мобильные браузеры```
|
||||
|
||||
|
||||
|
||||
## 🌟 Особенности## 🛠 Технологии
|
||||
|
||||
|
||||
|
||||
- **Полная база данных** - 72 учреждения всех типов## Колледжи на карте
|
||||
|
||||
- **Точные координаты** - все адреса геолокализованы
|
||||
|
||||
- **Детальная информация** - специальности, контакты, описания- **Python 3.8+** - основной язык разработки
|
||||
|
||||
- **Удобная фильтрация** - поиск по направлениям подготовки
|
||||
|
||||
- **Адаптивный дизайн** - работает на всех устройствах- **Yandex Maps API 2.1** - картографический сервис1. **Челябинский государственный колледж индустрии питания и торговли**
|
||||
|
||||
- **Оффлайн-совместимость** - сохраненная карта работает без интернета
|
||||
|
||||
- **Jinja2** - шаблонизатор HTML - Специализация: общественное питание, торговля, гостиничный сервис
|
||||
|
||||
## 📊 Статистика
|
||||
|
||||
- **HTML5/CSS3/JavaScript** - фронтенд
|
||||
|
||||
- **72** образовательных учреждения
|
||||
|
||||
- **6** типов учреждений (университеты, институты, академии, колледжи, техникумы, лицеи, училища)- **2GIS Places API** - источник данных о колледжах2. **Челябинский государственный промышленно-гуманитарный техникум им. А.В. Яковлева**
|
||||
|
||||
- **15+** направлений специализации
|
||||
|
||||
- **100%** покрытие координатами- **uv** - современный менеджер пакетов Python - Специализация: машиностроение, металлургия, IT
|
||||
|
||||
- **Полная** контактная информация
|
||||
|
||||
|
||||
|
||||
## 🤝 Вклад в проект
|
||||
|
||||
## 📊 Данные3. **Челябинский техникум текстильной и легкой промышленности**
|
||||
|
||||
Если вы нашли ошибку или хотите добавить новое учреждение:
|
||||
|
||||
1. Создайте Issue - Специализация: текстиль, швейное производство, дизайн
|
||||
|
||||
2. Внесите изменения в `colleges_data.json`
|
||||
|
||||
3. Создайте Pull RequestДанные о колледжах получены через 2GIS Places API и включают:
|
||||
|
||||
|
||||
|
||||
## 📄 Лицензия- Названия учебных заведений4. **Челябинский медицинский колледж**
|
||||
|
||||
|
||||
|
||||
Проект распространяется под лицензией MIT. Данные об учебных заведениях собраны из открытых источников.- Координаты расположения - Специализация: медицина, здравоохранение
|
||||
|
||||
|
||||
|
||||
## 🆘 Поддержка- Специальности обучения (автоматически определяются по типу учреждения)
|
||||
|
||||
|
||||
|
||||
При возникновении вопросов:- Контактную информацию (где доступно)5. **Челябинский государственный колледж физической культуры**
|
||||
|
||||
1. Проверьте, что все зависимости установлены
|
||||
|
||||
2. Убедитесь, что Python версии 3.8+- Описания - Специализация: физкультура, спорт
|
||||
|
||||
3. Проверьте доступность интернета для загрузки карт Yandex
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Разработка6. **Челябинский техникум промышленности и городского хозяйства им. Я.П. Осадчего**
|
||||
|
||||
**Автор**: Создано с использованием данных 2GIS и Yandex Maps API
|
||||
|
||||
**Версия**: 2.0 (расширенная база данных) - Специализация: ЖКХ, строительство, энергетика
|
||||
|
||||
**Последнее обновление**: Сентябрь 2025
|
||||
Для разработки используется современный стек Python:
|
||||
|
||||
- `uv` для управления зависимостями7. **Челябинский автотранспортный техникум**
|
||||
|
||||
- `pyproject.toml` для конфигурации проекта - Специализация: автотранспорт, механика
|
||||
|
||||
- Автоматическая генерация HTML из шаблонов
|
||||
|
||||
8. **Челябинский педагогический колледж № 1**
|
||||
|
||||
## 📝 Лицензия - Специализация: педагогика, дошкольное образование
|
||||
|
||||
|
||||
|
||||
Проект создан в образовательных целях.9. **Челябинский радиотехнический техникум**
|
||||
- Специализация: радиоэлектроника, телекоммуникации
|
||||
|
||||
10. **Челябинский колледж комтехно**
|
||||
- Специализация: IT, экономика, коммерция
|
||||
|
||||
## Использование без API ключа
|
||||
|
||||
Карта будет работать и без API ключа Yandex Maps, но с ограничениями:
|
||||
- Возможны водяные знаки на карте
|
||||
- Ограничения по количеству запросов
|
||||
- Некоторые функции могут быть недоступны
|
||||
|
||||
Для полноценной работы рекомендуется получить бесплатный API ключ.
|
||||
|
||||
## Технические детали
|
||||
|
||||
- **Backend**: Python 3.12, Jinja2
|
||||
- **Frontend**: HTML5, CSS3, JavaScript, Yandex Maps API 2.1
|
||||
- **Данные**: JSON файл с координатами и описаниями колледжей
|
||||
- **Иконки**: Стандартные иконки Yandex Maps (образование)
|
||||
|
||||
## Настройка
|
||||
|
||||
Вы можете легко добавить новые колледжи, отредактировав файл `colleges_data.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Название колледжа",
|
||||
"address": "Адрес",
|
||||
"lat": 55.1234,
|
||||
"lon": 61.5678,
|
||||
"description": "Описание специальностей",
|
||||
"website": "http://example.com",
|
||||
"phone": "+7 (351) 123-45-67"
|
||||
}
|
||||
```
|
||||
|
||||
## Лицензия
|
||||
|
||||
Этот проект создан в образовательных целях. Данные о колледжах собраны из открытых источников.
|
||||
323
chelyabinsk_colleges_map.html
Normal file
323
chelyabinsk_colleges_map.html
Normal file
File diff suppressed because one or more lines are too long
443
chelyabinsk_education_map_2gis.html
Normal file
443
chelyabinsk_education_map_2gis.html
Normal file
File diff suppressed because one or more lines are too long
9569
chelyabinsk_educational_institutions.json
Normal file
9569
chelyabinsk_educational_institutions.json
Normal file
File diff suppressed because it is too large
Load Diff
9944
chelyabinsk_educational_map_static.html
Normal file
9944
chelyabinsk_educational_map_static.html
Normal file
File diff suppressed because it is too large
Load Diff
381
collect_educational_data.py
Normal file
381
collect_educational_data.py
Normal file
@@ -0,0 +1,381 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Сбор данных об учебных заведениях Челябинской области через бесплатные API
|
||||
Использует: OpenStreetMap Nominatim API, Overpass API для сбора данных
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from typing import List, Dict, Optional
|
||||
from urllib.parse import quote
|
||||
|
||||
|
||||
class EducationalInstitutionsCollector:
|
||||
"""Сборщик данных об учебных заведениях через бесплатные API"""
|
||||
|
||||
def __init__(self):
|
||||
self.nominatim_url = "https://nominatim.openstreetmap.org"
|
||||
self.overpass_url = "https://overpass-api.de/api/interpreter"
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({
|
||||
'User-Agent': 'Educational Institutions Collector 1.0 (educational purposes)'
|
||||
})
|
||||
self.results = []
|
||||
|
||||
def search_nominatim(self, query: str, region: str = "Челябинская область") -> List[Dict]:
|
||||
"""Поиск через Nominatim API"""
|
||||
print(f"Поиск через Nominatim: {query}")
|
||||
|
||||
params = {
|
||||
'q': f"{query}, {region}",
|
||||
'format': 'json',
|
||||
'limit': 50,
|
||||
'addressdetails': 1,
|
||||
'extratags': 1,
|
||||
'countrycodes': 'ru'
|
||||
}
|
||||
|
||||
try:
|
||||
response = self.session.get(f"{self.nominatim_url}/search", params=params)
|
||||
response.raise_for_status()
|
||||
time.sleep(1) # Уважаем лимиты API
|
||||
|
||||
data = response.json()
|
||||
print(f"Найдено {len(data)} результатов")
|
||||
return data
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Ошибка запроса к Nominatim: {e}")
|
||||
return []
|
||||
|
||||
def search_overpass(self, amenity_type: str) -> List[Dict]:
|
||||
"""Поиск через Overpass API (OpenStreetMap)"""
|
||||
print(f"Поиск через Overpass API: {amenity_type}")
|
||||
|
||||
# Bounding box для Челябинской области (приблизительно)
|
||||
bbox = "53.5,58.0,56.5,63.5" # south,west,north,east
|
||||
|
||||
query = f"""
|
||||
[out:json][timeout:60];
|
||||
(
|
||||
node["amenity"="{amenity_type}"]({bbox});
|
||||
way["amenity"="{amenity_type}"]({bbox});
|
||||
relation["amenity"="{amenity_type}"]({bbox});
|
||||
);
|
||||
out center meta;
|
||||
"""
|
||||
|
||||
try:
|
||||
response = self.session.post(self.overpass_url, data=query)
|
||||
response.raise_for_status()
|
||||
time.sleep(2) # Уважаем лимиты API
|
||||
|
||||
data = response.json()
|
||||
elements = data.get('elements', [])
|
||||
print(f"Найдено {len(elements)} объектов")
|
||||
return elements
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Ошибка запроса к Overpass API: {e}")
|
||||
return []
|
||||
|
||||
def process_nominatim_result(self, item: Dict) -> Dict:
|
||||
"""Обработка результата от Nominatim"""
|
||||
address = item.get('address', {})
|
||||
|
||||
return {
|
||||
'id': f"nominatim_{item.get('place_id')}",
|
||||
'name': item.get('display_name', '').split(',')[0].strip(),
|
||||
'full_name': item.get('display_name', ''),
|
||||
'address': self._format_address(address),
|
||||
'latitude': float(item.get('lat', 0)),
|
||||
'longitude': float(item.get('lon', 0)),
|
||||
'type': item.get('type', ''),
|
||||
'category': item.get('category', ''),
|
||||
'city': address.get('city') or address.get('town') or address.get('village', ''),
|
||||
'region': address.get('state', ''),
|
||||
'source': 'OpenStreetMap Nominatim',
|
||||
'osm_id': item.get('osm_id'),
|
||||
'osm_type': item.get('osm_type'),
|
||||
'raw_data': item
|
||||
}
|
||||
|
||||
def process_overpass_result(self, item: Dict) -> Dict:
|
||||
"""Обработка результата от Overpass API"""
|
||||
tags = item.get('tags', {})
|
||||
|
||||
# Определяем координаты
|
||||
if 'center' in item:
|
||||
lat = item['center']['lat']
|
||||
lon = item['center']['lon']
|
||||
else:
|
||||
lat = item.get('lat', 0)
|
||||
lon = item.get('lon', 0)
|
||||
|
||||
# Форматируем адрес
|
||||
address_parts = []
|
||||
if tags.get('addr:street'):
|
||||
if tags.get('addr:housenumber'):
|
||||
address_parts.append(f"{tags['addr:street']}, {tags['addr:housenumber']}")
|
||||
else:
|
||||
address_parts.append(tags['addr:street'])
|
||||
if tags.get('addr:city'):
|
||||
address_parts.append(tags['addr:city'])
|
||||
|
||||
address = ', '.join(address_parts) if address_parts else ''
|
||||
|
||||
return {
|
||||
'id': f"osm_{item.get('id')}",
|
||||
'name': tags.get('name', ''),
|
||||
'official_name': tags.get('official_name', ''),
|
||||
'address': address,
|
||||
'latitude': float(lat),
|
||||
'longitude': float(lon),
|
||||
'amenity': tags.get('amenity', ''),
|
||||
'operator': tags.get('operator', ''),
|
||||
'website': tags.get('website', ''),
|
||||
'phone': tags.get('phone', ''),
|
||||
'email': tags.get('email', ''),
|
||||
'city': tags.get('addr:city', ''),
|
||||
'region': tags.get('addr:state', ''),
|
||||
'opening_hours': tags.get('opening_hours', ''),
|
||||
'source': 'OpenStreetMap Overpass',
|
||||
'osm_id': item.get('id'),
|
||||
'osm_type': item.get('type'),
|
||||
'raw_data': item
|
||||
}
|
||||
|
||||
def _format_address(self, address: Dict) -> str:
|
||||
"""Форматирование адреса"""
|
||||
parts = []
|
||||
|
||||
# Улица и номер дома
|
||||
if address.get('road'):
|
||||
if address.get('house_number'):
|
||||
parts.append(f"{address['road']}, {address['house_number']}")
|
||||
else:
|
||||
parts.append(address['road'])
|
||||
|
||||
# Город
|
||||
city = address.get('city') or address.get('town') or address.get('village')
|
||||
if city:
|
||||
parts.append(city)
|
||||
|
||||
return ', '.join(parts)
|
||||
|
||||
def collect_all_data(self) -> List[Dict]:
|
||||
"""Собираем все данные об учебных заведениях"""
|
||||
print("=" * 80)
|
||||
print("СБОР ДАННЫХ ОБ УЧЕБНЫХ ЗАВЕДЕНИЯХ ЧЕЛЯБИНСКОЙ ОБЛАСТИ")
|
||||
print("=" * 80)
|
||||
|
||||
all_institutions = []
|
||||
seen_names = set()
|
||||
|
||||
# 1. Поиск через Nominatim
|
||||
print("\n1. Поиск через Nominatim API...")
|
||||
nominatim_queries = [
|
||||
"университет",
|
||||
"институт",
|
||||
"колледж",
|
||||
"техникум",
|
||||
"училище",
|
||||
"академия",
|
||||
"ВУЗ",
|
||||
"образовательное учреждение"
|
||||
]
|
||||
|
||||
for query in nominatim_queries:
|
||||
results = self.search_nominatim(query)
|
||||
for item in results:
|
||||
processed = self.process_nominatim_result(item)
|
||||
if processed['name'] and processed['name'] not in seen_names:
|
||||
all_institutions.append(processed)
|
||||
seen_names.add(processed['name'])
|
||||
|
||||
# 2. Поиск через Overpass API
|
||||
print("\n2. Поиск через Overpass API...")
|
||||
overpass_amenities = [
|
||||
"university",
|
||||
"college",
|
||||
"school" # может включать техникумы и училища
|
||||
]
|
||||
|
||||
for amenity in overpass_amenities:
|
||||
results = self.search_overpass(amenity)
|
||||
for item in results:
|
||||
processed = self.process_overpass_result(item)
|
||||
if processed['name'] and processed['name'] not in seen_names:
|
||||
# Фильтруем школы, оставляем только ВУЗы и колледжи
|
||||
name = processed['name'].lower()
|
||||
if any(word in name for word in ['университет', 'институт', 'колледж', 'техникум', 'училище', 'академия']):
|
||||
all_institutions.append(processed)
|
||||
seen_names.add(processed['name'])
|
||||
|
||||
# 3. Добавляем известные учебные заведения
|
||||
print("\n3. Добавляем известные учебные заведения...")
|
||||
known_institutions = self._get_known_institutions()
|
||||
for institution in known_institutions:
|
||||
if institution['name'] not in seen_names:
|
||||
all_institutions.append(institution)
|
||||
seen_names.add(institution['name'])
|
||||
|
||||
print(f"\nВсего найдено уникальных учебных заведений: {len(all_institutions)}")
|
||||
return all_institutions
|
||||
|
||||
def _get_known_institutions(self) -> List[Dict]:
|
||||
"""Список известных учебных заведений для дополнения"""
|
||||
return [
|
||||
{
|
||||
'id': 'known_1',
|
||||
'name': 'Южно-Уральский государственный университет',
|
||||
'official_name': 'ФГАОУ ВО "Южно-Уральский государственный университет (национальный исследовательский университет)"',
|
||||
'address': 'проспект Ленина, 76, Челябинск',
|
||||
'latitude': 55.1549,
|
||||
'longitude': 61.4289,
|
||||
'phone': '+7 (351) 267-90-90',
|
||||
'website': 'https://www.susu.ru',
|
||||
'type': 'университет',
|
||||
'city': 'Челябинск',
|
||||
'region': 'Челябинская область',
|
||||
'source': 'Дополнительные данные'
|
||||
},
|
||||
{
|
||||
'id': 'known_2',
|
||||
'name': 'Челябинский государственный университет',
|
||||
'official_name': 'ФГБОУ ВО "Челябинский государственный университет"',
|
||||
'address': 'ул. Братьев Кашириных, 129, Челябинск',
|
||||
'latitude': 55.1605,
|
||||
'longitude': 61.4025,
|
||||
'phone': '+7 (351) 799-70-02',
|
||||
'website': 'https://www.csu.ru',
|
||||
'type': 'университет',
|
||||
'city': 'Челябинск',
|
||||
'region': 'Челябинская область',
|
||||
'source': 'Дополнительные данные'
|
||||
},
|
||||
{
|
||||
'id': 'known_3',
|
||||
'name': 'Магнитогорский государственный технический университет',
|
||||
'official_name': 'ФГБОУ ВО "Магнитогорский государственный технический университет им. Г.И. Носова"',
|
||||
'address': 'проспект Ленина, 38, Магнитогорск',
|
||||
'latitude': 53.4213,
|
||||
'longitude': 59.0457,
|
||||
'phone': '+7 (3519) 29-84-42',
|
||||
'website': 'https://www.magtu.ru',
|
||||
'type': 'университет',
|
||||
'city': 'Магнитогорск',
|
||||
'region': 'Челябинская область',
|
||||
'source': 'Дополнительные данные'
|
||||
},
|
||||
{
|
||||
'id': 'known_4',
|
||||
'name': 'Южно-Уральский государственный медицинский университет',
|
||||
'official_name': 'ФГБОУ ВО "Южно-Уральский государственный медицинский университет"',
|
||||
'address': 'ул. Воровского, 64, Челябинск',
|
||||
'latitude': 55.1634,
|
||||
'longitude': 61.4012,
|
||||
'phone': '+7 (351) 232-73-47',
|
||||
'website': 'https://www.chelsma.ru',
|
||||
'type': 'университет',
|
||||
'city': 'Челябинск',
|
||||
'region': 'Челябинская область',
|
||||
'source': 'Дополнительные данные'
|
||||
}
|
||||
]
|
||||
|
||||
def save_to_json(self, data: List[Dict], filename: str = "chelyabinsk_educational_institutions.json"):
|
||||
"""Сохранение данных в JSON"""
|
||||
try:
|
||||
output = {
|
||||
'region': 'Челябинская область',
|
||||
'collection_date': time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'total_count': len(data),
|
||||
'sources': ['OpenStreetMap Nominatim', 'OpenStreetMap Overpass', 'Дополнительные данные'],
|
||||
'data_types': {
|
||||
'universities': len([x for x in data if 'университет' in x.get('name', '').lower() or x.get('type') == 'университет']),
|
||||
'institutes': len([x for x in data if 'институт' in x.get('name', '').lower()]),
|
||||
'colleges': len([x for x in data if any(word in x.get('name', '').lower() for word in ['колледж', 'техникум', 'училище'])]),
|
||||
},
|
||||
'institutions': data
|
||||
}
|
||||
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
json.dump(output, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"\n✅ Данные сохранены в файл: {filename}")
|
||||
return filename
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка сохранения: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция"""
|
||||
collector = EducationalInstitutionsCollector()
|
||||
|
||||
# Собираем данные
|
||||
institutions = collector.collect_all_data()
|
||||
|
||||
if institutions:
|
||||
# Сохраняем в JSON
|
||||
filename = collector.save_to_json(institutions)
|
||||
|
||||
# Выводим статистику
|
||||
print("\n" + "=" * 80)
|
||||
print("СТАТИСТИКА СОБРАННЫХ ДАННЫХ")
|
||||
print("=" * 80)
|
||||
|
||||
cities = {}
|
||||
types = {}
|
||||
|
||||
for inst in institutions:
|
||||
city = inst.get('city', 'Не указан')
|
||||
cities[city] = cities.get(city, 0) + 1
|
||||
|
||||
name = inst.get('name', '').lower()
|
||||
if 'университет' in name:
|
||||
inst_type = 'Университеты'
|
||||
elif 'институт' in name:
|
||||
inst_type = 'Институты'
|
||||
elif 'колледж' in name:
|
||||
inst_type = 'Колледжи'
|
||||
elif 'техникум' in name:
|
||||
inst_type = 'Техникумы'
|
||||
elif 'училище' in name:
|
||||
inst_type = 'Училища'
|
||||
else:
|
||||
inst_type = 'Другие'
|
||||
|
||||
types[inst_type] = types.get(inst_type, 0) + 1
|
||||
|
||||
print(f"Всего учебных заведений: {len(institutions)}")
|
||||
|
||||
print(f"\nПо типам:")
|
||||
for inst_type, count in sorted(types.items(), key=lambda x: x[1], reverse=True):
|
||||
print(f" {inst_type}: {count}")
|
||||
|
||||
print(f"\nПо городам:")
|
||||
for city, count in sorted(cities.items(), key=lambda x: x[1], reverse=True)[:10]:
|
||||
print(f" {city}: {count}")
|
||||
|
||||
print(f"\nПримеры найденных учреждений:")
|
||||
for i, inst in enumerate(institutions[:5], 1):
|
||||
print(f"{i}. {inst['name']}")
|
||||
if inst.get('address'):
|
||||
print(f" 📍 {inst['address']}")
|
||||
if inst.get('phone'):
|
||||
print(f" 📞 {inst['phone']}")
|
||||
if inst.get('website'):
|
||||
print(f" 🌐 {inst['website']}")
|
||||
print()
|
||||
|
||||
else:
|
||||
print("❌ Не удалось собрать данные об учебных заведениях")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
1236
colleges_data.json
Normal file
1236
colleges_data.json
Normal file
File diff suppressed because it is too large
Load Diff
477
create_static_map.py
Normal file
477
create_static_map.py
Normal file
@@ -0,0 +1,477 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Создание статической HTML карты с использованием Leaflet и OpenStreetMap
|
||||
Отображает все найденные учебные заведения Челябинской области
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def create_static_map(json_file: str = "chelyabinsk_educational_institutions.json"):
|
||||
"""Создает статическую HTML карту с данными из JSON"""
|
||||
|
||||
# Загружаем данные
|
||||
try:
|
||||
with open(json_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Файл {json_file} не найден!")
|
||||
return None
|
||||
|
||||
institutions = data.get('institutions', [])
|
||||
total_count = len(institutions)
|
||||
|
||||
print(f"Создание карты для {total_count} учебных заведений...")
|
||||
|
||||
# Определяем центр карты (центр Челябинской области)
|
||||
center_lat = 55.1644
|
||||
center_lng = 61.4368
|
||||
|
||||
# Создаем HTML с картой
|
||||
html_content = f"""<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Карта учебных заведений Челябинской области</title>
|
||||
|
||||
<!-- Leaflet CSS -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
||||
crossorigin=""/>
|
||||
|
||||
<!-- Leaflet JavaScript -->
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||||
crossorigin=""></script>
|
||||
|
||||
<style>
|
||||
body {{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
}}
|
||||
|
||||
.header {{
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}}
|
||||
|
||||
.header h1 {{
|
||||
margin: 0;
|
||||
font-size: 2.2em;
|
||||
font-weight: 300;
|
||||
}}
|
||||
|
||||
.header p {{
|
||||
margin: 10px 0 0 0;
|
||||
opacity: 0.9;
|
||||
font-size: 1.1em;
|
||||
}}
|
||||
|
||||
.controls {{
|
||||
background: white;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}}
|
||||
|
||||
.filter-btn {{
|
||||
padding: 8px 16px;
|
||||
border: 2px solid #4CAF50;
|
||||
background: white;
|
||||
color: #4CAF50;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}}
|
||||
|
||||
.filter-btn:hover {{
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(76, 175, 80, 0.3);
|
||||
}}
|
||||
|
||||
.filter-btn.active {{
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
}}
|
||||
|
||||
.stats {{
|
||||
background: #e8f5e8;
|
||||
padding: 10px 15px;
|
||||
border-radius: 20px;
|
||||
color: #2E7D32;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}}
|
||||
|
||||
#map {{
|
||||
width: 100%;
|
||||
height: calc(100vh - 200px);
|
||||
border: 2px solid #ddd;
|
||||
}}
|
||||
|
||||
.legend {{
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
|
||||
z-index: 1000;
|
||||
max-width: 250px;
|
||||
}}
|
||||
|
||||
.legend h4 {{
|
||||
margin: 0 0 10px 0;
|
||||
color: #2E7D32;
|
||||
}}
|
||||
|
||||
.legend-item {{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 5px 0;
|
||||
font-size: 13px;
|
||||
}}
|
||||
|
||||
.legend-color {{
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
||||
}}
|
||||
|
||||
.university {{ background-color: #2196F3; }}
|
||||
.institute {{ background-color: #FF9800; }}
|
||||
.college {{ background-color: #4CAF50; }}
|
||||
.technical {{ background-color: #9C27B0; }}
|
||||
.other {{ background-color: #607D8B; }}
|
||||
|
||||
@media (max-width: 768px) {{
|
||||
.header h1 {{
|
||||
font-size: 1.8em;
|
||||
}}
|
||||
|
||||
.controls {{
|
||||
padding: 10px;
|
||||
}}
|
||||
|
||||
.filter-btn {{
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
}}
|
||||
|
||||
.legend {{
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
max-width: 200px;
|
||||
}}
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>🎓 Карта учебных заведений Челябинской области</h1>
|
||||
<p>Университеты, институты, колледжи, техникумы и училища • {total_count} учреждений</p>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="filter-btn active" onclick="filterInstitutions('all')">🏛️ Все ({total_count})</button>
|
||||
<button class="filter-btn" onclick="filterInstitutions('университет')">🎓 Университеты</button>
|
||||
<button class="filter-btn" onclick="filterInstitutions('институт')">🏛️ Институты</button>
|
||||
<button class="filter-btn" onclick="filterInstitutions('колледж')">📚 Колледжи</button>
|
||||
<button class="filter-btn" onclick="filterInstitutions('техникум')">🔧 Техникумы</button>
|
||||
<button class="filter-btn" onclick="filterInstitutions('училище')">⚙️ Училища</button>
|
||||
<div class="stats">
|
||||
<span id="showing-count">{total_count}</span> из {total_count} учреждений
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
<div class="legend">
|
||||
<h4>Легенда</h4>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color university"></div>
|
||||
<span>Университеты</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color institute"></div>
|
||||
<span>Институты</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color college"></div>
|
||||
<span>Колледжи</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color technical"></div>
|
||||
<span>Техникумы/Училища</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color other"></div>
|
||||
<span>Другие</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Данные учебных заведений
|
||||
const institutionsData = {json.dumps(institutions, ensure_ascii=False, indent=2)};
|
||||
|
||||
// Инициализация карты
|
||||
const map = L.map('map').setView([{center_lat}, {center_lng}], 8);
|
||||
|
||||
// Добавляем слой OpenStreetMap
|
||||
L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="https://openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}}).addTo(map);
|
||||
|
||||
// Массив всех маркеров
|
||||
let allMarkers = [];
|
||||
|
||||
// Функция определения цвета маркера по типу учреждения
|
||||
function getMarkerColor(institution) {{
|
||||
const name = institution.name.toLowerCase();
|
||||
|
||||
if (name.includes('университет')) return '#2196F3';
|
||||
if (name.includes('институт')) return '#FF9800';
|
||||
if (name.includes('колледж')) return '#4CAF50';
|
||||
if (name.includes('техникум') || name.includes('училище')) return '#9C27B0';
|
||||
return '#607D8B';
|
||||
}}
|
||||
|
||||
// Функция создания пользовательского маркера
|
||||
function createCustomMarker(color) {{
|
||||
return L.divIcon({{
|
||||
className: 'custom-marker',
|
||||
html: `<div style="
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: ${{color}};
|
||||
border: 3px solid white;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
|
||||
"></div>`,
|
||||
iconSize: [20, 20],
|
||||
iconAnchor: [10, 10]
|
||||
}});
|
||||
}}
|
||||
|
||||
// Добавление маркеров на карту
|
||||
function addMarkersToMap() {{
|
||||
institutionsData.forEach((institution, index) => {{
|
||||
const lat = institution.latitude;
|
||||
const lng = institution.longitude;
|
||||
|
||||
if (lat && lng && lat !== 0 && lng !== 0) {{
|
||||
const color = getMarkerColor(institution);
|
||||
const customIcon = createCustomMarker(color);
|
||||
|
||||
const marker = L.marker([lat, lng], {{ icon: customIcon }});
|
||||
|
||||
// Создаем содержимое всплывающего окна
|
||||
let popupContent = `
|
||||
<div style="max-width: 300px; font-family: Arial, sans-serif;">
|
||||
<h3 style="margin: 0 0 10px 0; color: #2E7D32; font-size: 16px;">
|
||||
${{institution.name}}
|
||||
</h3>
|
||||
`;
|
||||
|
||||
if (institution.official_name && institution.official_name !== institution.name) {{
|
||||
popupContent += `
|
||||
<p style="margin: 5px 0; font-size: 13px; color: #666; font-style: italic;">
|
||||
${{institution.official_name}}
|
||||
</p>
|
||||
`;
|
||||
}}
|
||||
|
||||
if (institution.address) {{
|
||||
popupContent += `
|
||||
<p style="margin: 5px 0; font-size: 14px;">
|
||||
<strong>📍 Адрес:</strong> ${{institution.address}}
|
||||
</p>
|
||||
`;
|
||||
}}
|
||||
|
||||
if (institution.city) {{
|
||||
popupContent += `
|
||||
<p style="margin: 5px 0; font-size: 14px;">
|
||||
<strong>🏙️ Город:</strong> ${{institution.city}}
|
||||
</p>
|
||||
`;
|
||||
}}
|
||||
|
||||
if (institution.phone) {{
|
||||
popupContent += `
|
||||
<p style="margin: 5px 0; font-size: 14px;">
|
||||
<strong>📞 Телефон:</strong>
|
||||
<a href="tel:${{institution.phone}}" style="color: #4CAF50;">
|
||||
${{institution.phone}}
|
||||
</a>
|
||||
</p>
|
||||
`;
|
||||
}}
|
||||
|
||||
if (institution.website) {{
|
||||
popupContent += `
|
||||
<p style="margin: 5px 0; font-size: 14px;">
|
||||
<strong>🌐 Сайт:</strong>
|
||||
<a href="${{institution.website}}" target="_blank" style="color: #4CAF50;">
|
||||
Перейти на сайт
|
||||
</a>
|
||||
</p>
|
||||
`;
|
||||
}}
|
||||
|
||||
if (institution.amenity) {{
|
||||
popupContent += `
|
||||
<p style="margin: 5px 0; font-size: 13px; color: #666;">
|
||||
<strong>Тип:</strong> ${{institution.amenity}}
|
||||
</p>
|
||||
`;
|
||||
}}
|
||||
|
||||
popupContent += `
|
||||
<p style="margin: 10px 0 0 0; font-size: 11px; color: #999;">
|
||||
Источник: ${{institution.source}}
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
marker.bindPopup(popupContent);
|
||||
marker.institutionData = institution;
|
||||
marker.addTo(map);
|
||||
allMarkers.push(marker);
|
||||
}}
|
||||
}});
|
||||
|
||||
console.log(`Добавлено ${{allMarkers.length}} маркеров на карту`);
|
||||
}}
|
||||
|
||||
// Функция фильтрации учреждений
|
||||
function filterInstitutions(filterType) {{
|
||||
// Обновляем активную кнопку
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => btn.classList.remove('active'));
|
||||
event.target.classList.add('active');
|
||||
|
||||
let visibleCount = 0;
|
||||
|
||||
allMarkers.forEach(marker => {{
|
||||
const institution = marker.institutionData;
|
||||
let shouldShow = false;
|
||||
|
||||
if (filterType === 'all') {{
|
||||
shouldShow = true;
|
||||
}} else {{
|
||||
const name = institution.name.toLowerCase();
|
||||
shouldShow = name.includes(filterType);
|
||||
}}
|
||||
|
||||
if (shouldShow) {{
|
||||
map.addLayer(marker);
|
||||
visibleCount++;
|
||||
}} else {{
|
||||
map.removeLayer(marker);
|
||||
}}
|
||||
}});
|
||||
|
||||
// Обновляем счетчик
|
||||
document.getElementById('showing-count').textContent = visibleCount;
|
||||
|
||||
// Подгоняем карту под видимые маркеры
|
||||
if (visibleCount > 0) {{
|
||||
const visibleMarkers = allMarkers.filter(marker => map.hasLayer(marker));
|
||||
if (visibleMarkers.length > 1) {{
|
||||
const group = new L.featureGroup(visibleMarkers);
|
||||
map.fitBounds(group.getBounds(), {{padding: [20, 20]}});
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
// Инициализация после загрузки страницы
|
||||
document.addEventListener('DOMContentLoaded', function() {{
|
||||
console.log('Загрузка карты с данными об учебных заведениях...');
|
||||
addMarkersToMap();
|
||||
|
||||
// Подгоняем карту под все маркеры
|
||||
if (allMarkers.length > 0) {{
|
||||
setTimeout(() => {{
|
||||
const group = new L.featureGroup(allMarkers);
|
||||
map.fitBounds(group.getBounds(), {{padding: [50, 50]}});
|
||||
}}, 1000);
|
||||
}}
|
||||
}});
|
||||
</script>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
# Сохраняем HTML файл
|
||||
output_file = "chelyabinsk_educational_map_static.html"
|
||||
try:
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(html_content)
|
||||
|
||||
print(f"✅ Статическая карта создана: {output_file}")
|
||||
return output_file
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка создания карты: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция"""
|
||||
print("=" * 80)
|
||||
print("СОЗДАНИЕ СТАТИЧЕСКОЙ КАРТЫ УЧЕБНЫХ ЗАВЕДЕНИЙ")
|
||||
print("=" * 80)
|
||||
|
||||
# Проверяем наличие файла с данными
|
||||
json_file = "chelyabinsk_educational_institutions.json"
|
||||
|
||||
if not os.path.exists(json_file):
|
||||
print(f"❌ Файл с данными {json_file} не найден!")
|
||||
print("Сначала запустите collect_educational_data.py для сбора данных")
|
||||
return
|
||||
|
||||
# Создаем карту
|
||||
output_file = create_static_map(json_file)
|
||||
|
||||
if output_file:
|
||||
print(f"\n🎯 Карта готова!")
|
||||
print(f"📁 Файл: {os.path.abspath(output_file)}")
|
||||
print(f"🌐 Откройте файл в браузере для просмотра")
|
||||
|
||||
# Предлагаем открыть в браузере
|
||||
open_browser = input("\nОткрыть карту в браузере? (y/N): ").strip().lower()
|
||||
if open_browser in ['y', 'yes', 'да', 'д']:
|
||||
import webbrowser
|
||||
try:
|
||||
webbrowser.open(f'file://{os.path.abspath(output_file)}')
|
||||
print("🚀 Карта открыта в браузере!")
|
||||
except Exception as e:
|
||||
print(f"❌ Не удалось открыть браузер: {e}")
|
||||
|
||||
else:
|
||||
print("❌ Не удалось создать карту")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
400
demo_institutions_chelyabinsk.py
Normal file
400
demo_institutions_chelyabinsk.py
Normal file
@@ -0,0 +1,400 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Демо-данные заведений среднего специального и высшего образования в Челябинске
|
||||
Для случая, когда API 2GIS недоступен без ключа
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
|
||||
def get_demo_institutions():
|
||||
"""Возвращает список заведений среднего специального и высшего образования в Челябинске"""
|
||||
|
||||
institutions = [
|
||||
# Университеты
|
||||
{
|
||||
"id": "demo_1",
|
||||
"name": "Южно-Уральский государственный университет",
|
||||
"full_name": "ФГАОУ ВО «Южно-Уральский государственный университет (национальный исследовательский университет)»",
|
||||
"address": "проспект Ленина, 76, Челябинск",
|
||||
"latitude": 55.1549,
|
||||
"longitude": 61.4289,
|
||||
"phones": ["+7 (351) 267-90-90"],
|
||||
"categories": ["Университет", "Высшее образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_2",
|
||||
"name": "Челябинский государственный университет",
|
||||
"full_name": "ФГБОУ ВО «Челябинский государственный университет»",
|
||||
"address": "ул. Братьев Кашириных, 129, Челябинск",
|
||||
"latitude": 55.1605,
|
||||
"longitude": 61.4025,
|
||||
"phones": ["+7 (351) 799-70-02"],
|
||||
"categories": ["Университет", "Высшее образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_3",
|
||||
"name": "Челябинский государственный педагогический университет",
|
||||
"full_name": "ФГБОУ ВО «Южно-Уральский государственный гуманитарно-педагогический университет»",
|
||||
"address": "проспект Ленина, 69, Челябинск",
|
||||
"latitude": 55.1545,
|
||||
"longitude": 61.4267,
|
||||
"phones": ["+7 (351) 216-56-01"],
|
||||
"categories": ["Университет", "Педагогическое образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_4",
|
||||
"name": "Уральский государственный университет физической культуры",
|
||||
"full_name": "ФГБОУ ВО «Уральский государственный университет физической культуры»",
|
||||
"address": "ул. Орджоникидзе, 1, Челябинск",
|
||||
"latitude": 55.1674,
|
||||
"longitude": 61.3988,
|
||||
"phones": ["+7 (351) 237-04-54"],
|
||||
"categories": ["Университет", "Физическая культура"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
|
||||
# Институты
|
||||
{
|
||||
"id": "demo_5",
|
||||
"name": "Челябинский институт экономики и права",
|
||||
"full_name": "АНО ВО «Челябинский институт экономики и права им. М.В. Ладошина»",
|
||||
"address": "ул. Свободы, 155, Челябинск",
|
||||
"latitude": 55.1890,
|
||||
"longitude": 61.3176,
|
||||
"phones": ["+7 (351) 217-18-18"],
|
||||
"categories": ["Институт", "Экономика и право"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_6",
|
||||
"name": "Челябинский филиал РАНХиГС",
|
||||
"full_name": "Челябинский филиал Российской академии народного хозяйства и государственной службы",
|
||||
"address": "ул. Комарова, 26, Челябинск",
|
||||
"latitude": 55.1823,
|
||||
"longitude": 61.3895,
|
||||
"phones": ["+7 (351) 220-11-91"],
|
||||
"categories": ["Филиал института", "Государственная служба"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
|
||||
# Колледжи и техникумы
|
||||
{
|
||||
"id": "demo_7",
|
||||
"name": "Челябинский техникум промышленности и городского хозяйства",
|
||||
"full_name": "ГБПОУ «Челябинский техникум промышленности и городского хозяйства имени Я.П. Осадчего»",
|
||||
"address": "ул. Энтузиастов, 15, Челябинск",
|
||||
"latitude": 55.1456,
|
||||
"longitude": 61.3789,
|
||||
"phones": ["+7 (351) 256-56-82"],
|
||||
"categories": ["Техникум", "Промышленность"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_8",
|
||||
"name": "Челябинский медицинский колледж",
|
||||
"full_name": "ГБПОУ «Челябинский базовый медицинский колледж»",
|
||||
"address": "ул. Свободы, 159а, Челябинск",
|
||||
"latitude": 55.1901,
|
||||
"longitude": 61.3164,
|
||||
"phones": ["+7 (351) 232-72-01"],
|
||||
"categories": ["Медицинский колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_9",
|
||||
"name": "Челябинский государственный колледж индустрии питания и торговли",
|
||||
"full_name": "ГБПОУ «Челябинский государственный колледж индустрии питания и торговли»",
|
||||
"address": "ул. Энтузиастов, 21, Челябинск",
|
||||
"latitude": 55.1444,
|
||||
"longitude": 61.3812,
|
||||
"phones": ["+7 (351) 256-71-90"],
|
||||
"categories": ["Колледж", "Торговля и питание"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_10",
|
||||
"name": "Челябинский педагогический колледж №1",
|
||||
"full_name": "ГБПОУ «Челябинский педагогический колледж № 1»",
|
||||
"address": "ул. Красная, 305, Челябинск",
|
||||
"latitude": 55.1733,
|
||||
"longitude": 61.3456,
|
||||
"phones": ["+7 (351) 263-89-50"],
|
||||
"categories": ["Педагогический колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_11",
|
||||
"name": "Челябинский технологический колледж",
|
||||
"full_name": "ГБПОУ «Челябинский технологический колледж»",
|
||||
"address": "ул. Машиностроителей, 23, Челябинск",
|
||||
"latitude": 55.1267,
|
||||
"longitude": 61.4567,
|
||||
"phones": ["+7 (351) 729-99-60"],
|
||||
"categories": ["Технологический колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_12",
|
||||
"name": "Челябинский юридический колледж",
|
||||
"full_name": "ЧОУ СПО «Челябинский юридический колледж»",
|
||||
"address": "ул. Цвиллинга, 60, Челябинск",
|
||||
"latitude": 55.1645,
|
||||
"longitude": 61.4156,
|
||||
"phones": ["+7 (351) 266-55-20"],
|
||||
"categories": ["Юридический колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_13",
|
||||
"name": "Южно-Уральский государственный технический колледж",
|
||||
"full_name": "ГБПОУ «Южно-Уральский государственный технический колледж»",
|
||||
"address": "ул. Воровского, 16, Челябинск",
|
||||
"latitude": 55.1598,
|
||||
"longitude": 61.4034,
|
||||
"phones": ["+7 (351) 263-44-36"],
|
||||
"categories": ["Технический колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_14",
|
||||
"name": "Челябинский экономический колледж",
|
||||
"full_name": "ЧОУ СПО «Челябинский экономический колледж»",
|
||||
"address": "ул. Кирова, 159, Челябинск",
|
||||
"latitude": 55.1534,
|
||||
"longitude": 61.3789,
|
||||
"phones": ["+7 (351) 263-78-05"],
|
||||
"categories": ["Экономический колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_15",
|
||||
"name": "Челябинский автотранспортный техникум",
|
||||
"full_name": "ГБПОУ «Челябинский автотранспортный техникум»",
|
||||
"address": "ул. Монакова, 33, Челябинск",
|
||||
"latitude": 55.1123,
|
||||
"longitude": 61.4234,
|
||||
"phones": ["+7 (351) 721-81-67"],
|
||||
"categories": ["Автотранспортный техникум", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
|
||||
# Дополнительные университеты и институты
|
||||
{
|
||||
"id": "demo_16",
|
||||
"name": "Челябинский государственный институт культуры",
|
||||
"full_name": "ФГБОУ ВО «Челябинский государственный институт культуры»",
|
||||
"address": "ул. Орджоникидзе, 36а, Челябинск",
|
||||
"latitude": 55.1699,
|
||||
"longitude": 61.4018,
|
||||
"phones": ["+7 (351) 263-83-18"],
|
||||
"categories": ["Институт культуры", "Высшее образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_17",
|
||||
"name": "Магнитогорский государственный технический университет (филиал)",
|
||||
"full_name": "Челябинский филиал ФГБОУ ВО «МГТУ им. Г.И. Носова»",
|
||||
"address": "ул. 250-летия Челябинска, 17, Челябинск",
|
||||
"latitude": 55.1589,
|
||||
"longitude": 61.3756,
|
||||
"phones": ["+7 (351) 799-74-45"],
|
||||
"categories": ["Филиал университета", "Техническое образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_18",
|
||||
"name": "Российский государственный профессионально-педагогический университет (филиал)",
|
||||
"full_name": "Челябинский филиал ФГАОУ ВО «РГППУ»",
|
||||
"address": "ул. Машиностроителей, 11, Челябинск",
|
||||
"latitude": 55.1298,
|
||||
"longitude": 61.4523,
|
||||
"phones": ["+7 (351) 729-99-10"],
|
||||
"categories": ["Филиал университета", "Педагогическое образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
|
||||
# Дополнительные колледжи и техникумы
|
||||
{
|
||||
"id": "demo_19",
|
||||
"name": "Челябинский государственный промышленно-гуманитарный техникум",
|
||||
"full_name": "ГБПОУ «Челябинский государственный промышленно-гуманитарный техникум им. А.В. Яковлева»",
|
||||
"address": "ул. Красноармейская, 88, Челябинск",
|
||||
"latitude": 55.1678,
|
||||
"longitude": 61.4125,
|
||||
"phones": ["+7 (351) 263-22-89"],
|
||||
"categories": ["Промышленно-гуманитарный техникум", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_20",
|
||||
"name": "Челябинский радиотехнический техникум",
|
||||
"full_name": "ГБПОУ «Челябинский радиотехнический техникум»",
|
||||
"address": "ул. Цвиллинга, 38, Челябинск",
|
||||
"latitude": 55.1634,
|
||||
"longitude": 61.4089,
|
||||
"phones": ["+7 (351) 232-08-71"],
|
||||
"categories": ["Радиотехнический техникум", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_21",
|
||||
"name": "Челябинский механико-технологический техникум",
|
||||
"full_name": "ГБПОУ «Челябинский механико-технологический техникум»",
|
||||
"address": "ул. Агалакова, 102, Челябинск",
|
||||
"latitude": 55.1445,
|
||||
"longitude": 61.3756,
|
||||
"phones": ["+7 (351) 256-78-34"],
|
||||
"categories": ["Механико-технологический техникум", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_22",
|
||||
"name": "Челябинский строительный колледж",
|
||||
"full_name": "ГБПОУ «Челябинский строительный колледж»",
|
||||
"address": "ул. Электростальская, 12, Челябинск",
|
||||
"latitude": 55.1234,
|
||||
"longitude": 61.4567,
|
||||
"phones": ["+7 (351) 729-88-90"],
|
||||
"categories": ["Строительный колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_23",
|
||||
"name": "Челябинский энергетический колледж",
|
||||
"full_name": "ГБПОУ «Челябинский энергетический колледж им. С.М. Кирова»",
|
||||
"address": "ул. Энергетиков, 44, Челябинск",
|
||||
"latitude": 55.1345,
|
||||
"longitude": 61.4234,
|
||||
"phones": ["+7 (351) 721-17-17"],
|
||||
"categories": ["Энергетический колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_24",
|
||||
"name": "Южно-Уральский многопрофильный колледж",
|
||||
"full_name": "ГБПОУ «Южно-Уральский многопрофильный колледж»",
|
||||
"address": "ул. Доватора, 16а, Челябинск",
|
||||
"latitude": 55.1456,
|
||||
"longitude": 61.3987,
|
||||
"phones": ["+7 (351) 263-44-78"],
|
||||
"categories": ["Многопрофильный колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_25",
|
||||
"name": "Челябинский колледж физической культуры",
|
||||
"full_name": "ГБПОУ «Челябинский колледж физической культуры»",
|
||||
"address": "ул. Российская, 258, Челябинск",
|
||||
"latitude": 55.1567,
|
||||
"longitude": 61.3456,
|
||||
"phones": ["+7 (351) 237-25-25"],
|
||||
"categories": ["Колледж физической культуры", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_26",
|
||||
"name": "Челябинский социально-профессиональный колледж",
|
||||
"full_name": "ГБПОУ «Челябинский социально-профессиональный колледж «Сфера»",
|
||||
"address": "ул. Чичерина, 38, Челябинск",
|
||||
"latitude": 55.1678,
|
||||
"longitude": 61.3234,
|
||||
"phones": ["+7 (351) 264-65-65"],
|
||||
"categories": ["Социально-профессиональный колледж", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
|
||||
# Частные образовательные учреждения
|
||||
{
|
||||
"id": "demo_27",
|
||||
"name": "Международный институт дизайна и сервиса",
|
||||
"full_name": "ЧОУ ВО «Международный институт дизайна и сервиса»",
|
||||
"address": "ул. Воровского, 36, Челябинск",
|
||||
"latitude": 55.1589,
|
||||
"longitude": 61.4012,
|
||||
"phones": ["+7 (351) 265-35-35"],
|
||||
"categories": ["Частный институт", "Дизайн и сервис"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_28",
|
||||
"name": "Челябинский институт путей сообщения",
|
||||
"full_name": "Челябинский институт путей сообщения (филиал УрГУПС)",
|
||||
"address": "ул. Жукова, 99, Челябинск",
|
||||
"latitude": 55.1234,
|
||||
"longitude": 61.3789,
|
||||
"phones": ["+7 (351) 263-88-88"],
|
||||
"categories": ["Институт путей сообщения", "Транспорт"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_29",
|
||||
"name": "Уральский социально-экономический institut",
|
||||
"full_name": "ЧОУ ВО «Уральский социально-экономический институт»",
|
||||
"address": "ул. Ленина, 77, Челябинск",
|
||||
"latitude": 55.1545,
|
||||
"longitude": 61.4278,
|
||||
"phones": ["+7 (351) 200-33-33"],
|
||||
"categories": ["Частный институт", "Социально-экономическое образование"],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_30",
|
||||
"name": "Челябинский колледж индустрии красоты",
|
||||
"full_name": "ЧОУ СПО «Челябинский колледж индустрии красоты»",
|
||||
"address": "ул. Труда, 179, Челябинск",
|
||||
"latitude": 55.1723,
|
||||
"longitude": 61.3567,
|
||||
"phones": ["+7 (351) 700-44-44"],
|
||||
"categories": ["Колледж красоты", "Среднее специальное образование"],
|
||||
"source": "Демо-данные"
|
||||
}
|
||||
]
|
||||
|
||||
return institutions
|
||||
|
||||
|
||||
def save_demo_data():
|
||||
"""Сохраняет демо-данные в JSON файл"""
|
||||
institutions = get_demo_institutions()
|
||||
|
||||
data = {
|
||||
'city': 'Челябинск',
|
||||
'source': 'Демо-данные (среднее специальное и высшее образование)',
|
||||
'count': len(institutions),
|
||||
'institutions': institutions
|
||||
}
|
||||
|
||||
filename = "educational_institutions_demo.json"
|
||||
|
||||
try:
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
print(f"Демо-данные сохранены в файл: {filename}")
|
||||
print(f"Всего заведений: {len(institutions)}")
|
||||
|
||||
# Статистика по типам
|
||||
categories_count = {}
|
||||
for inst in institutions:
|
||||
for category in inst.get('categories', []):
|
||||
categories_count[category] = categories_count.get(category, 0) + 1
|
||||
|
||||
print("\nРаспределение по категориям:")
|
||||
for category, count in sorted(categories_count.items(), key=lambda x: x[1], reverse=True):
|
||||
print(f" {category}: {count}")
|
||||
|
||||
return filename
|
||||
|
||||
except Exception as e:
|
||||
print(f"Ошибка при сохранении файла: {e}")
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Создание демо-данных заведений среднего специального и высшего образования")
|
||||
print("="*75)
|
||||
save_demo_data()
|
||||
487
educational_institutions_demo.json
Normal file
487
educational_institutions_demo.json
Normal file
@@ -0,0 +1,487 @@
|
||||
{
|
||||
"city": "Челябинск",
|
||||
"source": "Демо-данные (среднее специальное и высшее образование)",
|
||||
"count": 30,
|
||||
"institutions": [
|
||||
{
|
||||
"id": "demo_1",
|
||||
"name": "Южно-Уральский государственный университет",
|
||||
"full_name": "ФГАОУ ВО «Южно-Уральский государственный университет (национальный исследовательский университет)»",
|
||||
"address": "проспект Ленина, 76, Челябинск",
|
||||
"latitude": 55.1549,
|
||||
"longitude": 61.4289,
|
||||
"phones": [
|
||||
"+7 (351) 267-90-90"
|
||||
],
|
||||
"categories": [
|
||||
"Университет",
|
||||
"Высшее образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_2",
|
||||
"name": "Челябинский государственный университет",
|
||||
"full_name": "ФГБОУ ВО «Челябинский государственный университет»",
|
||||
"address": "ул. Братьев Кашириных, 129, Челябинск",
|
||||
"latitude": 55.1605,
|
||||
"longitude": 61.4025,
|
||||
"phones": [
|
||||
"+7 (351) 799-70-02"
|
||||
],
|
||||
"categories": [
|
||||
"Университет",
|
||||
"Высшее образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_3",
|
||||
"name": "Челябинский государственный педагогический университет",
|
||||
"full_name": "ФГБОУ ВО «Южно-Уральский государственный гуманитарно-педагогический университет»",
|
||||
"address": "проспект Ленина, 69, Челябинск",
|
||||
"latitude": 55.1545,
|
||||
"longitude": 61.4267,
|
||||
"phones": [
|
||||
"+7 (351) 216-56-01"
|
||||
],
|
||||
"categories": [
|
||||
"Университет",
|
||||
"Педагогическое образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_4",
|
||||
"name": "Уральский государственный университет физической культуры",
|
||||
"full_name": "ФГБОУ ВО «Уральский государственный университет физической культуры»",
|
||||
"address": "ул. Орджоникидзе, 1, Челябинск",
|
||||
"latitude": 55.1674,
|
||||
"longitude": 61.3988,
|
||||
"phones": [
|
||||
"+7 (351) 237-04-54"
|
||||
],
|
||||
"categories": [
|
||||
"Университет",
|
||||
"Физическая культура"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_5",
|
||||
"name": "Челябинский институт экономики и права",
|
||||
"full_name": "АНО ВО «Челябинский институт экономики и права им. М.В. Ладошина»",
|
||||
"address": "ул. Свободы, 155, Челябинск",
|
||||
"latitude": 55.189,
|
||||
"longitude": 61.3176,
|
||||
"phones": [
|
||||
"+7 (351) 217-18-18"
|
||||
],
|
||||
"categories": [
|
||||
"Институт",
|
||||
"Экономика и право"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_6",
|
||||
"name": "Челябинский филиал РАНХиГС",
|
||||
"full_name": "Челябинский филиал Российской академии народного хозяйства и государственной службы",
|
||||
"address": "ул. Комарова, 26, Челябинск",
|
||||
"latitude": 55.1823,
|
||||
"longitude": 61.3895,
|
||||
"phones": [
|
||||
"+7 (351) 220-11-91"
|
||||
],
|
||||
"categories": [
|
||||
"Филиал института",
|
||||
"Государственная служба"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_7",
|
||||
"name": "Челябинский техникум промышленности и городского хозяйства",
|
||||
"full_name": "ГБПОУ «Челябинский техникум промышленности и городского хозяйства имени Я.П. Осадчего»",
|
||||
"address": "ул. Энтузиастов, 15, Челябинск",
|
||||
"latitude": 55.1456,
|
||||
"longitude": 61.3789,
|
||||
"phones": [
|
||||
"+7 (351) 256-56-82"
|
||||
],
|
||||
"categories": [
|
||||
"Техникум",
|
||||
"Промышленность"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_8",
|
||||
"name": "Челябинский медицинский колледж",
|
||||
"full_name": "ГБПОУ «Челябинский базовый медицинский колледж»",
|
||||
"address": "ул. Свободы, 159а, Челябинск",
|
||||
"latitude": 55.1901,
|
||||
"longitude": 61.3164,
|
||||
"phones": [
|
||||
"+7 (351) 232-72-01"
|
||||
],
|
||||
"categories": [
|
||||
"Медицинский колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_9",
|
||||
"name": "Челябинский государственный колледж индустрии питания и торговли",
|
||||
"full_name": "ГБПОУ «Челябинский государственный колледж индустрии питания и торговли»",
|
||||
"address": "ул. Энтузиастов, 21, Челябинск",
|
||||
"latitude": 55.1444,
|
||||
"longitude": 61.3812,
|
||||
"phones": [
|
||||
"+7 (351) 256-71-90"
|
||||
],
|
||||
"categories": [
|
||||
"Колледж",
|
||||
"Торговля и питание"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_10",
|
||||
"name": "Челябинский педагогический колледж №1",
|
||||
"full_name": "ГБПОУ «Челябинский педагогический колледж № 1»",
|
||||
"address": "ул. Красная, 305, Челябинск",
|
||||
"latitude": 55.1733,
|
||||
"longitude": 61.3456,
|
||||
"phones": [
|
||||
"+7 (351) 263-89-50"
|
||||
],
|
||||
"categories": [
|
||||
"Педагогический колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_11",
|
||||
"name": "Челябинский технологический колледж",
|
||||
"full_name": "ГБПОУ «Челябинский технологический колледж»",
|
||||
"address": "ул. Машиностроителей, 23, Челябинск",
|
||||
"latitude": 55.1267,
|
||||
"longitude": 61.4567,
|
||||
"phones": [
|
||||
"+7 (351) 729-99-60"
|
||||
],
|
||||
"categories": [
|
||||
"Технологический колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_12",
|
||||
"name": "Челябинский юридический колледж",
|
||||
"full_name": "ЧОУ СПО «Челябинский юридический колледж»",
|
||||
"address": "ул. Цвиллинга, 60, Челябинск",
|
||||
"latitude": 55.1645,
|
||||
"longitude": 61.4156,
|
||||
"phones": [
|
||||
"+7 (351) 266-55-20"
|
||||
],
|
||||
"categories": [
|
||||
"Юридический колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_13",
|
||||
"name": "Южно-Уральский государственный технический колледж",
|
||||
"full_name": "ГБПОУ «Южно-Уральский государственный технический колледж»",
|
||||
"address": "ул. Воровского, 16, Челябинск",
|
||||
"latitude": 55.1598,
|
||||
"longitude": 61.4034,
|
||||
"phones": [
|
||||
"+7 (351) 263-44-36"
|
||||
],
|
||||
"categories": [
|
||||
"Технический колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_14",
|
||||
"name": "Челябинский экономический колледж",
|
||||
"full_name": "ЧОУ СПО «Челябинский экономический колледж»",
|
||||
"address": "ул. Кирова, 159, Челябинск",
|
||||
"latitude": 55.1534,
|
||||
"longitude": 61.3789,
|
||||
"phones": [
|
||||
"+7 (351) 263-78-05"
|
||||
],
|
||||
"categories": [
|
||||
"Экономический колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_15",
|
||||
"name": "Челябинский автотранспортный техникум",
|
||||
"full_name": "ГБПОУ «Челябинский автотранспортный техникум»",
|
||||
"address": "ул. Монакова, 33, Челябинск",
|
||||
"latitude": 55.1123,
|
||||
"longitude": 61.4234,
|
||||
"phones": [
|
||||
"+7 (351) 721-81-67"
|
||||
],
|
||||
"categories": [
|
||||
"Автотранспортный техникум",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_16",
|
||||
"name": "Челябинский государственный институт культуры",
|
||||
"full_name": "ФГБОУ ВО «Челябинский государственный институт культуры»",
|
||||
"address": "ул. Орджоникидзе, 36а, Челябинск",
|
||||
"latitude": 55.1699,
|
||||
"longitude": 61.4018,
|
||||
"phones": [
|
||||
"+7 (351) 263-83-18"
|
||||
],
|
||||
"categories": [
|
||||
"Институт культуры",
|
||||
"Высшее образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_17",
|
||||
"name": "Магнитогорский государственный технический университет (филиал)",
|
||||
"full_name": "Челябинский филиал ФГБОУ ВО «МГТУ им. Г.И. Носова»",
|
||||
"address": "ул. 250-летия Челябинска, 17, Челябинск",
|
||||
"latitude": 55.1589,
|
||||
"longitude": 61.3756,
|
||||
"phones": [
|
||||
"+7 (351) 799-74-45"
|
||||
],
|
||||
"categories": [
|
||||
"Филиал университета",
|
||||
"Техническое образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_18",
|
||||
"name": "Российский государственный профессионально-педагогический университет (филиал)",
|
||||
"full_name": "Челябинский филиал ФГАОУ ВО «РГППУ»",
|
||||
"address": "ул. Машиностроителей, 11, Челябинск",
|
||||
"latitude": 55.1298,
|
||||
"longitude": 61.4523,
|
||||
"phones": [
|
||||
"+7 (351) 729-99-10"
|
||||
],
|
||||
"categories": [
|
||||
"Филиал университета",
|
||||
"Педагогическое образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_19",
|
||||
"name": "Челябинский государственный промышленно-гуманитарный техникум",
|
||||
"full_name": "ГБПОУ «Челябинский государственный промышленно-гуманитарный техникум им. А.В. Яковлева»",
|
||||
"address": "ул. Красноармейская, 88, Челябинск",
|
||||
"latitude": 55.1678,
|
||||
"longitude": 61.4125,
|
||||
"phones": [
|
||||
"+7 (351) 263-22-89"
|
||||
],
|
||||
"categories": [
|
||||
"Промышленно-гуманитарный техникум",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_20",
|
||||
"name": "Челябинский радиотехнический техникум",
|
||||
"full_name": "ГБПОУ «Челябинский радиотехнический техникум»",
|
||||
"address": "ул. Цвиллинга, 38, Челябинск",
|
||||
"latitude": 55.1634,
|
||||
"longitude": 61.4089,
|
||||
"phones": [
|
||||
"+7 (351) 232-08-71"
|
||||
],
|
||||
"categories": [
|
||||
"Радиотехнический техникум",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_21",
|
||||
"name": "Челябинский механико-технологический техникум",
|
||||
"full_name": "ГБПОУ «Челябинский механико-технологический техникум»",
|
||||
"address": "ул. Агалакова, 102, Челябинск",
|
||||
"latitude": 55.1445,
|
||||
"longitude": 61.3756,
|
||||
"phones": [
|
||||
"+7 (351) 256-78-34"
|
||||
],
|
||||
"categories": [
|
||||
"Механико-технологический техникум",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_22",
|
||||
"name": "Челябинский строительный колледж",
|
||||
"full_name": "ГБПОУ «Челябинский строительный колледж»",
|
||||
"address": "ул. Электростальская, 12, Челябинск",
|
||||
"latitude": 55.1234,
|
||||
"longitude": 61.4567,
|
||||
"phones": [
|
||||
"+7 (351) 729-88-90"
|
||||
],
|
||||
"categories": [
|
||||
"Строительный колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_23",
|
||||
"name": "Челябинский энергетический колледж",
|
||||
"full_name": "ГБПОУ «Челябинский энергетический колледж им. С.М. Кирова»",
|
||||
"address": "ул. Энергетиков, 44, Челябинск",
|
||||
"latitude": 55.1345,
|
||||
"longitude": 61.4234,
|
||||
"phones": [
|
||||
"+7 (351) 721-17-17"
|
||||
],
|
||||
"categories": [
|
||||
"Энергетический колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_24",
|
||||
"name": "Южно-Уральский многопрофильный колледж",
|
||||
"full_name": "ГБПОУ «Южно-Уральский многопрофильный колледж»",
|
||||
"address": "ул. Доватора, 16а, Челябинск",
|
||||
"latitude": 55.1456,
|
||||
"longitude": 61.3987,
|
||||
"phones": [
|
||||
"+7 (351) 263-44-78"
|
||||
],
|
||||
"categories": [
|
||||
"Многопрофильный колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_25",
|
||||
"name": "Челябинский колледж физической культуры",
|
||||
"full_name": "ГБПОУ «Челябинский колледж физической культуры»",
|
||||
"address": "ул. Российская, 258, Челябинск",
|
||||
"latitude": 55.1567,
|
||||
"longitude": 61.3456,
|
||||
"phones": [
|
||||
"+7 (351) 237-25-25"
|
||||
],
|
||||
"categories": [
|
||||
"Колледж физической культуры",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_26",
|
||||
"name": "Челябинский социально-профессиональный колледж",
|
||||
"full_name": "ГБПОУ «Челябинский социально-профессиональный колледж «Сфера»",
|
||||
"address": "ул. Чичерина, 38, Челябинск",
|
||||
"latitude": 55.1678,
|
||||
"longitude": 61.3234,
|
||||
"phones": [
|
||||
"+7 (351) 264-65-65"
|
||||
],
|
||||
"categories": [
|
||||
"Социально-профессиональный колледж",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_27",
|
||||
"name": "Международный институт дизайна и сервиса",
|
||||
"full_name": "ЧОУ ВО «Международный институт дизайна и сервиса»",
|
||||
"address": "ул. Воровского, 36, Челябинск",
|
||||
"latitude": 55.1589,
|
||||
"longitude": 61.4012,
|
||||
"phones": [
|
||||
"+7 (351) 265-35-35"
|
||||
],
|
||||
"categories": [
|
||||
"Частный институт",
|
||||
"Дизайн и сервис"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_28",
|
||||
"name": "Челябинский институт путей сообщения",
|
||||
"full_name": "Челябинский институт путей сообщения (филиал УрГУПС)",
|
||||
"address": "ул. Жукова, 99, Челябинск",
|
||||
"latitude": 55.1234,
|
||||
"longitude": 61.3789,
|
||||
"phones": [
|
||||
"+7 (351) 263-88-88"
|
||||
],
|
||||
"categories": [
|
||||
"Институт путей сообщения",
|
||||
"Транспорт"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_29",
|
||||
"name": "Уральский социально-экономический institut",
|
||||
"full_name": "ЧОУ ВО «Уральский социально-экономический институт»",
|
||||
"address": "ул. Ленина, 77, Челябинск",
|
||||
"latitude": 55.1545,
|
||||
"longitude": 61.4278,
|
||||
"phones": [
|
||||
"+7 (351) 200-33-33"
|
||||
],
|
||||
"categories": [
|
||||
"Частный институт",
|
||||
"Социально-экономическое образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
},
|
||||
{
|
||||
"id": "demo_30",
|
||||
"name": "Челябинский колледж индустрии красоты",
|
||||
"full_name": "ЧОУ СПО «Челябинский колледж индустрии красоты»",
|
||||
"address": "ул. Труда, 179, Челябинск",
|
||||
"latitude": 55.1723,
|
||||
"longitude": 61.3567,
|
||||
"phones": [
|
||||
"+7 (351) 700-44-44"
|
||||
],
|
||||
"categories": [
|
||||
"Колледж красоты",
|
||||
"Среднее специальное образование"
|
||||
],
|
||||
"source": "Демо-данные"
|
||||
}
|
||||
]
|
||||
}
|
||||
158
main.py
Normal file
158
main.py
Normal file
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Интерактивная карта образовательных учреждений Челябинска
|
||||
Отображает 72+ учебных заведения: университеты, институты, колледжи, техникумы, лицеи и училища
|
||||
Использует Yandex Maps API для создания интерактивной карты с фильтрацией
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import webbrowser
|
||||
from pathlib import Path
|
||||
from jinja2 import Template
|
||||
|
||||
|
||||
def load_colleges_data():
|
||||
"""Загружает данные об образовательных учреждениях из JSON файла"""
|
||||
current_dir = Path(__file__).parent
|
||||
colleges_file = current_dir / "colleges_data.json"
|
||||
|
||||
try:
|
||||
with open(colleges_file, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"Файл с данными об образовательных учреждениях не найден: {colleges_file}")
|
||||
return []
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Ошибка при чтении JSON файла: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def generate_map_html(colleges_data, api_key=""):
|
||||
"""Генерирует HTML файл с интерактивной картой образовательных учреждений"""
|
||||
current_dir = Path(__file__).parent
|
||||
template_file = current_dir / "map_template.html"
|
||||
|
||||
try:
|
||||
with open(template_file, 'r', encoding='utf-8') as f:
|
||||
template_content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Шаблон HTML не найден: {template_file}")
|
||||
return None
|
||||
|
||||
# Заменяем placeholder API ключа
|
||||
template_content = template_content.replace("YOUR_API_KEY", api_key)
|
||||
|
||||
# Создаем шаблон Jinja2
|
||||
template = Template(template_content)
|
||||
|
||||
# Рендерим HTML с данными о колледжах
|
||||
html_content = template.render(
|
||||
colleges=colleges_data,
|
||||
colleges_json=json.dumps(colleges_data, ensure_ascii=False)
|
||||
)
|
||||
|
||||
# Сохраняем HTML файл
|
||||
output_file = current_dir / "chelyabinsk_colleges_map.html"
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(html_content)
|
||||
|
||||
return output_file
|
||||
|
||||
|
||||
def print_colleges_info(colleges_data):
|
||||
"""Выводит информацию об образовательных учреждениях в консоль"""
|
||||
print("=" * 80)
|
||||
print("ОБРАЗОВАТЕЛЬНЫЕ УЧРЕЖДЕНИЯ ЧЕЛЯБИНСКА")
|
||||
print("=" * 80)
|
||||
|
||||
# Группировка по типам
|
||||
types_stats = {}
|
||||
for institution in colleges_data:
|
||||
inst_type = institution.get('type', 'Неопределенный')
|
||||
types_stats[inst_type] = types_stats.get(inst_type, 0) + 1
|
||||
|
||||
print(f"\n📊 Статистика по типам учреждений:")
|
||||
for inst_type, count in sorted(types_stats.items()):
|
||||
print(f" {inst_type}: {count}")
|
||||
|
||||
print(f"\n🎯 Всего учреждений: {len(colleges_data)}")
|
||||
|
||||
for i, institution in enumerate(colleges_data, 1):
|
||||
print(f"\n{i}. {institution['name']}")
|
||||
print(f" Тип: {institution.get('type', 'Не указан')}")
|
||||
print(f" Адрес: {institution['address']}")
|
||||
|
||||
coords = institution.get('coordinates', {})
|
||||
if coords:
|
||||
print(f" Координаты: {coords.get('lat', 'Н/Д')}, {coords.get('lon', 'Н/Д')}")
|
||||
|
||||
specialties = institution.get('specialties', [])
|
||||
if specialties:
|
||||
print(f" Специальности: {', '.join(specialties)}")
|
||||
|
||||
contacts = institution.get('contacts', {})
|
||||
if contacts.get('website'):
|
||||
print(f" Веб-сайт: {contacts['website']}")
|
||||
if contacts.get('phone'):
|
||||
print(f" Телефон: {contacts['phone']}")
|
||||
|
||||
print(f" Описание: {institution.get('description', 'Не указано')}")
|
||||
print("-" * 80)
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция программы"""
|
||||
print("Генерация карты колледжей Челябинска...")
|
||||
|
||||
# Загружаем данные о колледжах
|
||||
colleges_data = load_colleges_data()
|
||||
|
||||
if not colleges_data:
|
||||
print("Не удалось загрузить данные о колледжах!")
|
||||
return
|
||||
|
||||
print(f"Загружено {len(colleges_data)} колледжей")
|
||||
|
||||
# Выводим информацию о колледжах
|
||||
print_colleges_info(colleges_data)
|
||||
|
||||
# Запрашиваем API ключ у пользователя
|
||||
print("\n" + "=" * 80)
|
||||
print("НАСТРОЙКА API КЛЮЧА YANDEX MAPS")
|
||||
print("=" * 80)
|
||||
print("Для работы карты необходим API ключ Yandex Maps.")
|
||||
print("Получить ключ можно на: https://developer.tech.yandex.ru/")
|
||||
print("Если у вас нет ключа, оставьте поле пустым (карта будет работать с ограничениями)")
|
||||
|
||||
api_key = input("\nВведите ваш API ключ Yandex Maps (или нажмите Enter для пропуска): ").strip()
|
||||
|
||||
# Генерируем HTML файл с картой
|
||||
output_file = generate_map_html(colleges_data, api_key)
|
||||
|
||||
if output_file:
|
||||
print(f"\n✅ Карта успешно создана: {output_file}")
|
||||
|
||||
# Предлагаем открыть карту в браузере
|
||||
open_browser = input("\nОткрыть карту в браузере? (y/N): ").strip().lower()
|
||||
if open_browser in ['y', 'yes', 'да', 'д']:
|
||||
try:
|
||||
webbrowser.open(f'file://{output_file.absolute()}')
|
||||
print("Карта открыта в браузере!")
|
||||
except Exception as e:
|
||||
print(f"Не удалось открыть браузер: {e}")
|
||||
print(f"Откройте файл вручную: {output_file.absolute()}")
|
||||
else:
|
||||
print(f"Откройте файл в браузере: {output_file.absolute()}")
|
||||
|
||||
print("\n📍 На карте отмечены следующие колледжи:")
|
||||
for college in colleges_data:
|
||||
print(f" • {college['name']}")
|
||||
|
||||
else:
|
||||
print("❌ Ошибка при создании карты!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
265
main_2gis.py
Normal file
265
main_2gis.py
Normal file
@@ -0,0 +1,265 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Интерактивная карта учебных заведений Челябинска
|
||||
Отображает заведения среднего специального и высшего образования
|
||||
Использует 2GIS Maps API для создания интерактивной карты с фильтрацией
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import webbrowser
|
||||
from pathlib import Path
|
||||
from jinja2 import Template
|
||||
|
||||
|
||||
def load_institutions_data():
|
||||
"""Загружает данные об учебных заведениях из JSON файла (демо-данные или 2GIS API)"""
|
||||
current_dir = Path(__file__).parent
|
||||
|
||||
# Сначала пробуем демо-данные
|
||||
demo_file = current_dir / "educational_institutions_demo.json"
|
||||
if demo_file.exists():
|
||||
try:
|
||||
with open(demo_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
return data.get('institutions', [])
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
print(f"Ошибка при чтении демо-данных: {e}")
|
||||
|
||||
# Пробуем данные от 2GIS API
|
||||
api_file = current_dir / "educational_institutions_2gis.json"
|
||||
if api_file.exists():
|
||||
try:
|
||||
with open(api_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
return data.get('institutions', [])
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
print(f"Ошибка при чтении данных 2GIS API: {e}")
|
||||
|
||||
# Пробуем старые данные (на случай отката)
|
||||
legacy_file = current_dir / "colleges_data.json"
|
||||
if legacy_file.exists():
|
||||
try:
|
||||
with open(legacy_file, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
print(f"Ошибка при чтении устаревших данных: {e}")
|
||||
|
||||
print("Файлы с данными об учебных заведениях не найдены!")
|
||||
return []
|
||||
|
||||
|
||||
def generate_map_html(institutions_data):
|
||||
"""Генерирует HTML файл с интерактивной картой учебных заведений (2GIS)"""
|
||||
current_dir = Path(__file__).parent
|
||||
template_file = current_dir / "map_template_2gis.html"
|
||||
|
||||
try:
|
||||
with open(template_file, 'r', encoding='utf-8') as f:
|
||||
template_content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Шаблон HTML не найден: {template_file}")
|
||||
return None
|
||||
|
||||
# Создаем шаблон Jinja2
|
||||
template = Template(template_content)
|
||||
|
||||
# Рендерим HTML с данными об учебных заведениях
|
||||
html_content = template.render(
|
||||
institutions=institutions_data,
|
||||
institutions_json=json.dumps(institutions_data, ensure_ascii=False)
|
||||
)
|
||||
|
||||
# Сохраняем HTML файл
|
||||
output_file = current_dir / "chelyabinsk_education_map_2gis.html"
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(html_content)
|
||||
|
||||
return output_file
|
||||
|
||||
|
||||
def print_institutions_info(institutions_data):
|
||||
"""Выводит информацию об учебных заведениях в консоль"""
|
||||
print("=" * 90)
|
||||
print("УЧЕБНЫЕ ЗАВЕДЕНИЯ ЧЕЛЯБИНСКА - СРЕДНЕЕ СПЕЦИАЛЬНОЕ И ВЫСШЕЕ ОБРАЗОВАНИЕ")
|
||||
print("=" * 90)
|
||||
|
||||
# Группировка по категориям
|
||||
categories_stats = {}
|
||||
type_stats = {}
|
||||
|
||||
for institution in institutions_data:
|
||||
categories = institution.get('categories', [])
|
||||
for category in categories:
|
||||
categories_stats[category] = categories_stats.get(category, 0) + 1
|
||||
|
||||
# Определяем тип по названию
|
||||
name = institution.get('name', '').lower()
|
||||
if 'университет' in name:
|
||||
inst_type = 'Университет'
|
||||
elif 'институт' in name:
|
||||
inst_type = 'Институт'
|
||||
elif 'колледж' in name:
|
||||
inst_type = 'Колледж'
|
||||
elif 'техникум' in name:
|
||||
inst_type = 'Техникум'
|
||||
elif 'академия' in name:
|
||||
inst_type = 'Академия'
|
||||
else:
|
||||
inst_type = 'Другое'
|
||||
|
||||
type_stats[inst_type] = type_stats.get(inst_type, 0) + 1
|
||||
|
||||
print(f"\n📊 Статистика по типам учреждений:")
|
||||
for inst_type, count in sorted(type_stats.items(), key=lambda x: x[1], reverse=True):
|
||||
print(f" {inst_type}: {count}")
|
||||
|
||||
print(f"\n📋 Статистика по категориям:")
|
||||
for category, count in sorted(categories_stats.items(), key=lambda x: x[1], reverse=True)[:10]:
|
||||
print(f" {category}: {count}")
|
||||
|
||||
print(f"\n🎯 Всего учреждений: {len(institutions_data)}")
|
||||
|
||||
print(f"\n📍 Список учебных заведений:")
|
||||
for i, institution in enumerate(institutions_data, 1):
|
||||
print(f"\n{i}. {institution['name']}")
|
||||
|
||||
if institution.get('full_name') and institution['full_name'] != institution['name']:
|
||||
print(f" Полное название: {institution['full_name']}")
|
||||
|
||||
print(f" Адрес: {institution.get('address', 'Не указан')}")
|
||||
|
||||
lat = institution.get('latitude', 0)
|
||||
lon = institution.get('longitude', 0)
|
||||
if lat and lon:
|
||||
print(f" Координаты: {lat}, {lon}")
|
||||
|
||||
categories = institution.get('categories', [])
|
||||
if categories:
|
||||
print(f" Категории: {', '.join(categories)}")
|
||||
|
||||
phones = institution.get('phones', [])
|
||||
if phones:
|
||||
print(f" Телефоны: {', '.join(phones)}")
|
||||
|
||||
source = institution.get('source', 'Не указан')
|
||||
print(f" Источник данных: {source}")
|
||||
|
||||
print("-" * 90)
|
||||
|
||||
|
||||
def run_search_if_needed():
|
||||
"""Запускает поиск через 2GIS API или создает демо-данные, если данных нет"""
|
||||
current_dir = Path(__file__).parent
|
||||
demo_file = current_dir / "educational_institutions_demo.json"
|
||||
api_file = current_dir / "educational_institutions_2gis.json"
|
||||
|
||||
if not demo_file.exists() and not api_file.exists():
|
||||
print("Данные об учебных заведениях не найдены.")
|
||||
print("Создание демо-данных...")
|
||||
|
||||
# Импортируем и запускаем создание демо-данных
|
||||
try:
|
||||
from demo_institutions_chelyabinsk import save_demo_data
|
||||
save_demo_data()
|
||||
except ImportError:
|
||||
print("Не удалось импортировать модуль создания демо-данных")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция программы"""
|
||||
print("Генерация карты учебных заведений Челябинска с использованием 2GIS Maps API...")
|
||||
print("Отображаются только заведения среднего специального и высшего образования")
|
||||
print("=" * 90)
|
||||
|
||||
# Проверяем наличие данных, создаем если нужно
|
||||
if not run_search_if_needed():
|
||||
print("Не удалось получить данные об учебных заведениях!")
|
||||
return
|
||||
|
||||
# Загружаем данные об учебных заведениях
|
||||
institutions_data = load_institutions_data()
|
||||
|
||||
if not institutions_data:
|
||||
print("Не удалось загрузить данные об учебных заведениях!")
|
||||
return
|
||||
|
||||
print(f"Загружено {len(institutions_data)} учебных заведений")
|
||||
|
||||
# Выводим информацию об учебных заведениях
|
||||
print_institutions_info(institutions_data)
|
||||
|
||||
# Информация о 2GIS API
|
||||
print("\n" + "=" * 90)
|
||||
print("ИНФОРМАЦИЯ О 2GIS MAPS API")
|
||||
print("=" * 90)
|
||||
print("Карта использует 2GIS Maps API для отображения.")
|
||||
print("2GIS API предоставляется бесплатно для некоммерческого использования.")
|
||||
print("Для получения API ключа для коммерческого использования:")
|
||||
print("https://platform.2gis.com/")
|
||||
|
||||
# Генерируем HTML файл с картой
|
||||
output_file = generate_map_html(institutions_data)
|
||||
|
||||
if output_file:
|
||||
print(f"\n✅ Карта успешно создана: {output_file}")
|
||||
|
||||
# Предлагаем открыть карту в браузере
|
||||
open_browser = input("\nОткрыть карту в браузере? (y/N): ").strip().lower()
|
||||
if open_browser in ['y', 'yes', 'да', 'д']:
|
||||
try:
|
||||
webbrowser.open(f'file://{output_file.absolute()}')
|
||||
print("Карта открыта в браузере!")
|
||||
except Exception as e:
|
||||
print(f"Не удалось открыть браузер: {e}")
|
||||
print(f"Откройте файл вручную: {output_file.absolute()}")
|
||||
else:
|
||||
print(f"Откройте файл в браузере: {output_file.absolute()}")
|
||||
|
||||
print("\n🎓 Особенности карты:")
|
||||
print(" • Использует 2GIS Maps API вместо Yandex Maps")
|
||||
print(" • Показывает только среднее специальное и высшее образование")
|
||||
print(" • Фильтрация по типам учреждений")
|
||||
print(" • Цветные маркеры для разных типов")
|
||||
print(" • Подробная информация в всплывающих окнах")
|
||||
|
||||
print(f"\n📍 На карте отмечены следующие учебные заведения:")
|
||||
|
||||
# Группируем по типам для удобного отображения
|
||||
by_type = {}
|
||||
for institution in institutions_data:
|
||||
name = institution.get('name', '').lower()
|
||||
if 'университет' in name:
|
||||
inst_type = 'Университеты'
|
||||
elif 'институт' in name:
|
||||
inst_type = 'Институты'
|
||||
elif 'колледж' in name:
|
||||
inst_type = 'Колледжи'
|
||||
elif 'техникум' in name:
|
||||
inst_type = 'Техникумы'
|
||||
elif 'академия' in name:
|
||||
inst_type = 'Академии'
|
||||
else:
|
||||
inst_type = 'Другие'
|
||||
|
||||
if inst_type not in by_type:
|
||||
by_type[inst_type] = []
|
||||
by_type[inst_type].append(institution['name'])
|
||||
|
||||
for inst_type, names in by_type.items():
|
||||
print(f"\n {inst_type} ({len(names)}):")
|
||||
for name in names:
|
||||
print(f" • {name}")
|
||||
|
||||
else:
|
||||
print("❌ Ошибка при создании карты!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
323
map_template.html
Normal file
323
map_template.html
Normal file
@@ -0,0 +1,323 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Карта образовательных учреждений Челябинска</title>
|
||||
<script src="https://api-maps.yandex.ru/2.1/?apikey=YOUR_API_KEY&lang=ru_RU" type="text/javascript"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
font-size: 2.2em;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 10px 0 0 0;
|
||||
opacity: 0.9;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
background: white;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 8px 16px;
|
||||
border: 2px solid #667eea;
|
||||
background: white;
|
||||
color: #667eea;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.stats {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
#map {
|
||||
width: 100%;
|
||||
height: calc(100vh - 180px);
|
||||
}
|
||||
|
||||
.balloon-content {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.balloon-title {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.balloon-address {
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.balloon-specialties {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.specialty-tag {
|
||||
display: inline-block;
|
||||
background: #e8f2ff;
|
||||
color: #0066cc;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
margin: 2px;
|
||||
border: 1px solid #cce7ff;
|
||||
}
|
||||
|
||||
.balloon-contacts {
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.balloon-contacts a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.balloon-contacts a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header h1 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>🎓 Карта образовательных учреждений Челябинска</h1>
|
||||
<p>Интерактивная карта: университеты, институты, колледжи, техникумы, лицеи и училища</p>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="filter-btn active" onclick="filterBySpecialty('all')">🏛️ Все</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Техника и технологии')">⚙️ Техника</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Экономика и управление')">💼 Экономика</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Медицина и здравоохранение')">🏥 Медицина</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Педагогика и образование')">👨<EFBFBD> Педагогика</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Строительство и архитектура')">🏗️ Строительство</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Искусство и культура')"><EFBFBD> Искусство</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Транспорт и логистика')"><EFBFBD> Транспорт</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Информационные технологии')">💻 IT</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Право и юриспруденция')">⚖️ Право</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Пищевая промышленность')">🍳 Питание</button>
|
||||
<button class="filter-btn" onclick="filterBySpecialty('Энергетика')">🏭 Энергетика</button>
|
||||
<div class="stats">
|
||||
<span id="showing-count">{{ colleges|length }}</span> из {{ colleges|length }} учреждений
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
<script>
|
||||
let myMap;
|
||||
let placemarks = [];
|
||||
let colleges = {{ colleges_json|safe }};
|
||||
|
||||
ymaps.ready(init);
|
||||
|
||||
function init() {
|
||||
myMap = new ymaps.Map("map", {
|
||||
center: [55.1644, 61.4291], // Центр Челябинска
|
||||
zoom: 11,
|
||||
controls: ['zoomControl', 'searchControl', 'trafficControl', 'fullscreenControl']
|
||||
});
|
||||
|
||||
// Добавляем метки для всех колледжей
|
||||
colleges.forEach(function(college, index) {
|
||||
addPlacemark(college, index);
|
||||
});
|
||||
|
||||
// Подгоняем границы карты под все метки
|
||||
if (placemarks.length > 0) {
|
||||
myMap.setBounds(myMap.geoObjects.getBounds(), {
|
||||
checkZoomRange: true,
|
||||
zoomMargin: 50
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addPlacemark(college, index) {
|
||||
let placemark = new ymaps.Placemark(
|
||||
[college.lat, college.lon],
|
||||
{
|
||||
balloonContentHeader: '<div class="balloon-title">' + college.name + '</div>',
|
||||
balloonContentBody: createBalloonContent(college),
|
||||
balloonContentFooter: '',
|
||||
hintContent: college.name,
|
||||
clusterCaption: college.name
|
||||
},
|
||||
{
|
||||
preset: 'islands#redEducationIcon',
|
||||
hideIconOnBalloonOpen: false,
|
||||
balloonOffset: [0, -40]
|
||||
}
|
||||
);
|
||||
|
||||
// Сохраняем данные колледжа в метке для фильтрации
|
||||
placemark.college = college;
|
||||
placemark.collegeIndex = index;
|
||||
|
||||
myMap.geoObjects.add(placemark);
|
||||
placemarks.push(placemark);
|
||||
}
|
||||
|
||||
function createBalloonContent(college) {
|
||||
let content = '<div class="balloon-content">';
|
||||
|
||||
if (college.address) {
|
||||
content += '<div class="balloon-address">📍 ' + college.address + '</div>';
|
||||
}
|
||||
|
||||
if (college.specialties && college.specialties.length > 0) {
|
||||
content += '<div class="balloon-specialties">';
|
||||
content += '<strong>🎓 Специальности:</strong><br>';
|
||||
college.specialties.slice(0, 5).forEach(function(specialty) {
|
||||
content += '<span class="specialty-tag">' + specialty + '</span>';
|
||||
});
|
||||
if (college.specialties.length > 5) {
|
||||
content += '<span class="specialty-tag">+' + (college.specialties.length - 5) + ' еще</span>';
|
||||
}
|
||||
content += '</div>';
|
||||
}
|
||||
|
||||
if (college.description) {
|
||||
content += '<div style="margin-top: 10px; font-size: 13px; color: #666;">' + college.description + '</div>';
|
||||
}
|
||||
|
||||
let contacts = [];
|
||||
if (college.phone) {
|
||||
contacts.push('📞 <a href="tel:' + college.phone + '">' + college.phone + '</a>');
|
||||
}
|
||||
if (college.website) {
|
||||
contacts.push('🌐 <a href="' + college.website + '" target="_blank">Сайт</a>');
|
||||
}
|
||||
|
||||
if (contacts.length > 0) {
|
||||
content += '<div class="balloon-contacts">' + contacts.join('<br>') + '</div>';
|
||||
}
|
||||
|
||||
content += '</div>';
|
||||
return content;
|
||||
}
|
||||
|
||||
function filterBySpecialty(specialty) {
|
||||
// Обновляем активную кнопку
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => btn.classList.remove('active'));
|
||||
event.target.classList.add('active');
|
||||
|
||||
let visibleCount = 0;
|
||||
|
||||
placemarks.forEach(function(placemark) {
|
||||
let college = placemark.college;
|
||||
let shouldShow = false;
|
||||
|
||||
if (specialty === 'all') {
|
||||
shouldShow = true;
|
||||
} else {
|
||||
// Проверяем специальности и название колледжа
|
||||
let searchText = (college.name + ' ' + college.specialties.join(' ')).toLowerCase();
|
||||
shouldShow = searchText.includes(specialty.toLowerCase());
|
||||
}
|
||||
|
||||
if (shouldShow) {
|
||||
placemark.options.set('visible', true);
|
||||
visibleCount++;
|
||||
} else {
|
||||
placemark.options.set('visible', false);
|
||||
}
|
||||
});
|
||||
|
||||
// Обновляем счетчик
|
||||
document.getElementById('showing-count').textContent = visibleCount;
|
||||
|
||||
// Подгоняем карту под видимые метки
|
||||
if (visibleCount > 0) {
|
||||
let visiblePlacemarks = placemarks.filter(p => p.options.get('visible'));
|
||||
if (visiblePlacemarks.length > 0) {
|
||||
let bounds = [];
|
||||
visiblePlacemarks.forEach(p => {
|
||||
bounds.push(p.geometry.getCoordinates());
|
||||
});
|
||||
myMap.setBounds(ymaps.util.bounds.fromPoints(bounds), {
|
||||
checkZoomRange: true,
|
||||
zoomMargin: 50
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем обработчик изменения размера окна
|
||||
window.addEventListener('resize', function() {
|
||||
if (myMap) {
|
||||
myMap.container.fitToViewport();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
443
map_template_2gis.html
Normal file
443
map_template_2gis.html
Normal file
@@ -0,0 +1,443 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Карта учебных заведений Челябинска - Среднее специальное и высшее образование</title>
|
||||
<script src="https://maps.api.2gis.ru/2.0/loader.js?pkg=full&key=f61cfc4b-55b7-4937-8625-c00932bee844"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #2c5aa0 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
font-size: 2.2em;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 10px 0 0 0;
|
||||
opacity: 0.9;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
background: white;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 8px 16px;
|
||||
border: 2px solid #2c5aa0;
|
||||
background: white;
|
||||
color: #2c5aa0;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
background: #2c5aa0;
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(44, 90, 160, 0.3);
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
background: #2c5aa0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.stats {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
#map {
|
||||
width: 100%;
|
||||
height: calc(100vh - 180px);
|
||||
}
|
||||
|
||||
.balloon-content {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.balloon-title {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.balloon-address {
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.balloon-categories {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.category-tag {
|
||||
display: inline-block;
|
||||
background: #e8f2ff;
|
||||
color: #0066cc;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
margin: 2px;
|
||||
border: 1px solid #cce7ff;
|
||||
}
|
||||
|
||||
.balloon-contacts {
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.balloon-contacts a {
|
||||
color: #2c5aa0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.balloon-contacts a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.powered-by {
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header h1 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>🎓 Карта учебных заведений Челябинска</h1>
|
||||
<p>Среднее специальное и высшее образование: университеты, института, колледжи, техникумы</p>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="filter-btn active" onclick="filterByCategory('all')">🏛️ Все</button>
|
||||
<button class="filter-btn" onclick="filterByCategory('университет')">🎓 Университеты</button>
|
||||
<button class="filter-btn" onclick="filterByCategory('институт')">🏛️ Институты</button>
|
||||
<button class="filter-btn" onclick="filterByCategory('колледж')">📚 Колледжи</button>
|
||||
<button class="filter-btn" onclick="filterByCategory('техникум')">🔧 Техникумы</button>
|
||||
<button class="filter-btn" onclick="filterByCategory('медицинский')">🏥 Медицинские</button>
|
||||
<button class="filter-btn" onclick="filterByCategory('педагогический')">👨🏫 Педагогические</button>
|
||||
<button class="filter-btn" onclick="filterByCategory('технический')">⚙️ Технические</button>
|
||||
<button class="filter-btn" onclick="filterByCategory('экономический')">💼 Экономические</button>
|
||||
<div class="stats">
|
||||
<span id="showing-count">{{ institutions|length }}</span> из {{ institutions|length }} учреждений
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map"></div>
|
||||
<div class="powered-by">Powered by 2GIS Maps API</div>
|
||||
|
||||
<script>
|
||||
let myMap;
|
||||
let markers = [];
|
||||
let institutions = {{ institutions_json|safe }};
|
||||
|
||||
DG.then(function () {
|
||||
console.log('2GIS API загружен успешно');
|
||||
init();
|
||||
}).catch(function(error) {
|
||||
console.error('Ошибка загрузки 2GIS API:', error);
|
||||
document.getElementById('map').innerHTML = '<div style="padding: 50px; text-align: center; color: red; background: #ffe6e6; border: 1px solid #ff9999; border-radius: 5px;">❌ Не удалось загрузить 2GIS Maps API<br><small>Проверьте подключение к интернету</small></div>';
|
||||
});
|
||||
|
||||
// Проверяем, что DG доступен
|
||||
if (typeof DG === 'undefined') {
|
||||
console.error('2GIS API не загружен');
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('map').innerHTML = '<div style="padding: 50px; text-align: center; color: red; background: #ffe6e6; border: 1px solid #ff9999; border-radius: 5px;">❌ 2GIS Maps API недоступен<br><small>Проверьте подключение к интернету</small></div>';
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
console.log('Инициализация карты 2GIS...');
|
||||
console.log('Всего учебных заведений:', institutions.length);
|
||||
|
||||
try {
|
||||
myMap = DG.map('map', {
|
||||
center: [55.1644, 61.4291], // Центр Челябинска
|
||||
zoom: 11
|
||||
});
|
||||
|
||||
console.log('Карта создана успешно');
|
||||
|
||||
// Добавляем маркеры для всех учебных заведений
|
||||
institutions.forEach(function(institution, index) {
|
||||
console.log('Добавляем маркер для:', institution.name);
|
||||
addMarker(institution, index);
|
||||
});
|
||||
|
||||
console.log('Добавлено маркеров:', markers.length);
|
||||
|
||||
// Подгоняем границы карты под все маркеры
|
||||
if (markers.length > 0) {
|
||||
setTimeout(function() {
|
||||
fitMapToBounds();
|
||||
}, 1000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при инициализации карты:', error);
|
||||
document.getElementById('map').innerHTML = '<div style="padding: 50px; text-align: center; color: red;">Ошибка загрузки карты 2GIS: ' + error.message + '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function addMarker(institution, index) {
|
||||
// Определяем иконку в зависимости от типа учреждения
|
||||
let iconColor = getIconColor(institution);
|
||||
|
||||
let marker = DG.marker([institution.latitude, institution.longitude], {
|
||||
icon: DG.icon({
|
||||
iconUrl: getIconUrl(institution),
|
||||
iconSize: [32, 32],
|
||||
iconAnchor: [16, 32]
|
||||
})
|
||||
}).addTo(myMap);
|
||||
|
||||
// Создаем содержимое всплывающего окна
|
||||
let popupContent = createPopupContent(institution);
|
||||
marker.bindPopup(popupContent);
|
||||
|
||||
// Сохраняем данные учреждения в маркере для фильтрации
|
||||
marker.institution = institution;
|
||||
marker.institutionIndex = index;
|
||||
|
||||
markers.push(marker);
|
||||
}
|
||||
|
||||
function getIconColor(institution) {
|
||||
let name = institution.name.toLowerCase();
|
||||
let categories = institution.categories.join(' ').toLowerCase();
|
||||
|
||||
if (name.includes('университет') || categories.includes('университет')) return 'blue';
|
||||
if (name.includes('институт') || categories.includes('институт')) return 'green';
|
||||
if (name.includes('колледж') || categories.includes('колледж')) return 'orange';
|
||||
if (name.includes('техникум') || categories.includes('техникум')) return 'red';
|
||||
return 'gray';
|
||||
}
|
||||
|
||||
function getIconUrl(institution) {
|
||||
// Используем стандартные иконки 2GIS или создаем собственные
|
||||
let name = institution.name.toLowerCase();
|
||||
let categories = institution.categories.join(' ').toLowerCase();
|
||||
|
||||
// Для демонстрации используем простые цветные маркеры
|
||||
if (name.includes('университет') || categories.includes('университет')) {
|
||||
return 'data:image/svg+xml;base64,' + btoa(`
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="12" fill="#2c5aa0" stroke="white" stroke-width="2"/>
|
||||
<text x="16" y="20" text-anchor="middle" fill="white" font-size="16">🎓</text>
|
||||
</svg>
|
||||
`);
|
||||
}
|
||||
if (name.includes('институт') || categories.includes('институт')) {
|
||||
return 'data:image/svg+xml;base64,' + btoa(`
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="12" fill="#28a745" stroke="white" stroke-width="2"/>
|
||||
<text x="16" y="20" text-anchor="middle" fill="white" font-size="16">🏛️</text>
|
||||
</svg>
|
||||
`);
|
||||
}
|
||||
if (name.includes('колледж') || categories.includes('колледж')) {
|
||||
return 'data:image/svg+xml;base64,' + btoa(`
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="12" fill="#fd7e14" stroke="white" stroke-width="2"/>
|
||||
<text x="16" y="20" text-anchor="middle" fill="white" font-size="16">📚</text>
|
||||
</svg>
|
||||
`);
|
||||
}
|
||||
if (name.includes('техникум') || categories.includes('техникум')) {
|
||||
return 'data:image/svg+xml;base64,' + btoa(`
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="12" fill="#dc3545" stroke="white" stroke-width="2"/>
|
||||
<text x="16" y="20" text-anchor="middle" fill="white" font-size="16">🔧</text>
|
||||
</svg>
|
||||
`);
|
||||
}
|
||||
|
||||
// По умолчанию
|
||||
return 'data:image/svg+xml;base64,' + btoa(`
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="12" fill="#6c757d" stroke="white" stroke-width="2"/>
|
||||
<text x="16" y="20" text-anchor="middle" fill="white" font-size="16">🏫</text>
|
||||
</svg>
|
||||
`);
|
||||
}
|
||||
|
||||
function createPopupContent(institution) {
|
||||
let content = '<div class="balloon-content">';
|
||||
|
||||
content += '<div class="balloon-title">' + institution.name + '</div>';
|
||||
|
||||
if (institution.full_name && institution.full_name !== institution.name) {
|
||||
content += '<div style="font-size: 13px; color: #888; margin-bottom: 8px;">' + institution.full_name + '</div>';
|
||||
}
|
||||
|
||||
if (institution.address) {
|
||||
content += '<div class="balloon-address">📍 ' + institution.address + '</div>';
|
||||
}
|
||||
|
||||
if (institution.categories && institution.categories.length > 0) {
|
||||
content += '<div class="balloon-categories">';
|
||||
content += '<strong>📋 Категории:</strong><br>';
|
||||
institution.categories.forEach(function(category) {
|
||||
content += '<span class="category-tag">' + category + '</span>';
|
||||
});
|
||||
content += '</div>';
|
||||
}
|
||||
|
||||
let contacts = [];
|
||||
if (institution.phones && institution.phones.length > 0) {
|
||||
institution.phones.forEach(function(phone) {
|
||||
contacts.push('📞 <a href="tel:' + phone + '">' + phone + '</a>');
|
||||
});
|
||||
}
|
||||
|
||||
if (contacts.length > 0) {
|
||||
content += '<div class="balloon-contacts">' + contacts.join('<br>') + '</div>';
|
||||
}
|
||||
|
||||
// Источник данных
|
||||
if (institution.source) {
|
||||
content += '<div style="margin-top: 10px; font-size: 11px; color: #999;">Источник: ' + institution.source + '</div>';
|
||||
}
|
||||
|
||||
content += '</div>';
|
||||
return content;
|
||||
}
|
||||
|
||||
function filterByCategory(category) {
|
||||
// Обновляем активную кнопку
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => btn.classList.remove('active'));
|
||||
event.target.classList.add('active');
|
||||
|
||||
let visibleCount = 0;
|
||||
|
||||
markers.forEach(function(marker) {
|
||||
let institution = marker.institution;
|
||||
let shouldShow = false;
|
||||
|
||||
if (category === 'all') {
|
||||
shouldShow = true;
|
||||
} else {
|
||||
// Проверяем название и категории учреждения
|
||||
let searchText = (institution.name + ' ' + institution.categories.join(' ') + ' ' + institution.full_name).toLowerCase();
|
||||
shouldShow = searchText.includes(category.toLowerCase());
|
||||
}
|
||||
|
||||
if (shouldShow) {
|
||||
if (!myMap.hasLayer(marker)) {
|
||||
myMap.addLayer(marker);
|
||||
}
|
||||
visibleCount++;
|
||||
} else {
|
||||
if (myMap.hasLayer(marker)) {
|
||||
myMap.removeLayer(marker);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Обновляем счетчик
|
||||
document.getElementById('showing-count').textContent = visibleCount;
|
||||
|
||||
// Подгоняем карту под видимые маркеры
|
||||
if (visibleCount > 0) {
|
||||
fitMapToBounds();
|
||||
}
|
||||
}
|
||||
|
||||
function fitMapToBounds() {
|
||||
let visibleMarkers = markers.filter(marker => myMap.hasLayer(marker));
|
||||
if (visibleMarkers.length === 0) return;
|
||||
|
||||
if (visibleMarkers.length === 1) {
|
||||
// Если только один маркер, центрируем на него
|
||||
let coords = visibleMarkers[0].getLatLng();
|
||||
myMap.setView([coords.lat, coords.lng], 14);
|
||||
} else {
|
||||
// Если несколько маркеров, подгоняем границы
|
||||
let bounds = [];
|
||||
visibleMarkers.forEach(marker => {
|
||||
let coords = marker.getLatLng();
|
||||
bounds.push([coords.lat, coords.lng]);
|
||||
});
|
||||
|
||||
// Вычисляем границы
|
||||
let minLat = Math.min(...bounds.map(b => b[0]));
|
||||
let maxLat = Math.max(...bounds.map(b => b[0]));
|
||||
let minLng = Math.min(...bounds.map(b => b[1]));
|
||||
let maxLng = Math.max(...bounds.map(b => b[1]));
|
||||
|
||||
// Добавляем отступы
|
||||
let padding = 0.01;
|
||||
myMap.fitBounds([
|
||||
[minLat - padding, minLng - padding],
|
||||
[maxLat + padding, maxLng + padding]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем обработчик изменения размера окна
|
||||
window.addEventListener('resize', function() {
|
||||
if (myMap) {
|
||||
myMap.invalidateSize();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
10
pyproject.toml
Normal file
10
pyproject.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[project]
|
||||
name = "map-yandex"
|
||||
version = "0.1.0"
|
||||
description = "Карта колледжей Челябинска с использованием Yandex Maps API"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"requests>=2.31.0",
|
||||
"jinja2>=3.1.2",
|
||||
]
|
||||
249
search_2gis.py
Normal file
249
search_2gis.py
Normal file
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Скрипт для поиска учебных заведений в Челябинске через 2GIS Places API
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
|
||||
class TwoGISSearcher:
|
||||
"""Клиент для работы с 2GIS Places API"""
|
||||
|
||||
def __init__(self, api_key: Optional[str] = None):
|
||||
self.base_url = "https://catalog.api.2gis.com/3.0/items"
|
||||
self.api_key = api_key or "f61cfc4b-55b7-4937-8625-c00932bee844"
|
||||
self.session = requests.Session()
|
||||
|
||||
# Челябинск (region_id получен из документации 2GIS)
|
||||
self.chelyabinsk_location = "55.1644,61.4368" # координаты центра Челябинска
|
||||
self.chelyabinsk_region_id = 1 # region_id для России
|
||||
|
||||
# Поисковые запросы для среднего специального и высшего образования
|
||||
self.search_queries = [
|
||||
"техникум",
|
||||
"колледж",
|
||||
"университет",
|
||||
"институт",
|
||||
"академия",
|
||||
"профессиональное училище",
|
||||
"высшее учебное заведение",
|
||||
"вуз",
|
||||
"факультет",
|
||||
"филиал университета",
|
||||
"медицинский колледж",
|
||||
"педагогический колледж",
|
||||
"технический колледж",
|
||||
"экономический колледж"
|
||||
]
|
||||
|
||||
def search_educational_institutions(self, query: str, limit: int = 50) -> List[Dict]:
|
||||
"""Поиск учебных заведений по запросу"""
|
||||
params = {
|
||||
'q': query,
|
||||
'location': self.chelyabinsk_location,
|
||||
'radius': 50000, # радиус поиска 50км от центра города
|
||||
'region_id': self.chelyabinsk_region_id,
|
||||
'fields': 'items.name,items.full_name,items.address,items.point,items.contact_groups,items.rubrics,items.context',
|
||||
'page_size': limit
|
||||
}
|
||||
|
||||
if self.api_key:
|
||||
params['key'] = self.api_key
|
||||
|
||||
try:
|
||||
print(f"Поиск: {query}")
|
||||
response = self.session.get(self.base_url, params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
|
||||
if 'result' in data and 'items' in data['result']:
|
||||
items = data['result']['items']
|
||||
print(f"Найдено {len(items)} заведений для запроса '{query}'")
|
||||
return items
|
||||
else:
|
||||
print(f"Нет результатов для запроса '{query}'")
|
||||
return []
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Ошибка при запросе '{query}': {e}")
|
||||
return []
|
||||
|
||||
def filter_educational_institutions(self, items: List[Dict]) -> List[Dict]:
|
||||
"""Фильтрация результатов - оставляем только учебные заведения"""
|
||||
educational_rubrics = [
|
||||
'university',
|
||||
'college',
|
||||
'technical_school',
|
||||
'higher_education',
|
||||
'vocational_school'
|
||||
]
|
||||
|
||||
filtered_items = []
|
||||
|
||||
for item in items:
|
||||
# Проверяем рубрики
|
||||
if 'rubrics' in item:
|
||||
for rubric in item['rubrics']:
|
||||
if any(edu_key in rubric.get('alias', '').lower() for edu_key in educational_rubrics):
|
||||
filtered_items.append(item)
|
||||
break
|
||||
|
||||
# Дополнительная проверка по названию (только среднее специальное и высшее)
|
||||
name = item.get('name', '').lower()
|
||||
educational_keywords = [
|
||||
'техникум', 'колледж', 'университет', 'институт', 'академия',
|
||||
'вуз', 'факультет', 'филиал', 'профессиональное училище',
|
||||
'медицинский', 'педагогический', 'технический', 'экономический'
|
||||
]
|
||||
|
||||
# Исключаем школы, лицеи, гимназии, детские сады
|
||||
excluded_keywords = [
|
||||
'школа', 'лицей', 'гимназия', 'детский сад', 'дошкольн'
|
||||
]
|
||||
|
||||
# Проверяем наличие ключевых слов и отсутствие исключаемых
|
||||
has_educational_keyword = any(keyword in name for keyword in educational_keywords)
|
||||
has_excluded_keyword = any(keyword in name for keyword in excluded_keywords)
|
||||
|
||||
if has_educational_keyword and not has_excluded_keyword:
|
||||
if item not in filtered_items:
|
||||
filtered_items.append(item)
|
||||
|
||||
return filtered_items
|
||||
|
||||
def extract_institution_data(self, item: Dict) -> Dict:
|
||||
"""Извлечение данных об учебном заведении"""
|
||||
# Получаем координаты
|
||||
point = item.get('point', {})
|
||||
lat = point.get('lat', 0)
|
||||
lon = point.get('lon', 0)
|
||||
|
||||
# Получаем адрес
|
||||
address_info = item.get('address', {})
|
||||
address = address_info.get('name', '') if address_info else ''
|
||||
|
||||
# Получаем контактную информацию
|
||||
contacts = []
|
||||
if 'contact_groups' in item:
|
||||
for group in item['contact_groups']:
|
||||
if 'contacts' in group:
|
||||
for contact in group['contacts']:
|
||||
if contact.get('type') == 'phone':
|
||||
contacts.append(contact.get('value', ''))
|
||||
|
||||
# Получаем рубрики (категории)
|
||||
rubrics = []
|
||||
if 'rubrics' in item:
|
||||
rubrics = [rubric.get('name', '') for rubric in item['rubrics']]
|
||||
|
||||
return {
|
||||
'id': item.get('id', ''),
|
||||
'name': item.get('name', ''),
|
||||
'full_name': item.get('full_name', ''),
|
||||
'address': address,
|
||||
'latitude': lat,
|
||||
'longitude': lon,
|
||||
'phones': contacts,
|
||||
'categories': rubrics,
|
||||
'source': '2GIS Places API'
|
||||
}
|
||||
|
||||
def search_all_educational_institutions(self) -> List[Dict]:
|
||||
"""Поиск всех учебных заведений в Челябинске"""
|
||||
all_institutions = []
|
||||
seen_ids = set()
|
||||
|
||||
for query in self.search_queries:
|
||||
# Задержка между запросами чтобы не превысить лимиты API
|
||||
time.sleep(0.5)
|
||||
|
||||
items = self.search_educational_institutions(query)
|
||||
filtered_items = self.filter_educational_institutions(items)
|
||||
|
||||
for item in filtered_items:
|
||||
institution_data = self.extract_institution_data(item)
|
||||
|
||||
# Избегаем дубликатов по ID
|
||||
if institution_data['id'] and institution_data['id'] not in seen_ids:
|
||||
seen_ids.add(institution_data['id'])
|
||||
all_institutions.append(institution_data)
|
||||
# Если нет ID, проверяем по названию и адресу
|
||||
elif not institution_data['id']:
|
||||
unique_key = f"{institution_data['name']}_{institution_data['address']}"
|
||||
if unique_key not in seen_ids:
|
||||
seen_ids.add(unique_key)
|
||||
all_institutions.append(institution_data)
|
||||
|
||||
print(f"\nВсего найдено уникальных учебных заведений: {len(all_institutions)}")
|
||||
return all_institutions
|
||||
|
||||
def save_to_json(self, institutions: List[Dict], filename: str = "educational_institutions_2gis.json"):
|
||||
"""Сохранение данных в JSON файл"""
|
||||
try:
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
json.dump({
|
||||
'city': 'Челябинск',
|
||||
'source': '2GIS Places API',
|
||||
'count': len(institutions),
|
||||
'institutions': institutions
|
||||
}, f, ensure_ascii=False, indent=2)
|
||||
print(f"Данные сохранены в файл: {filename}")
|
||||
except Exception as e:
|
||||
print(f"Ошибка при сохранении файла: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция"""
|
||||
print("Поиск заведений среднего специального и высшего образования в Челябинске")
|
||||
print("Используется 2GIS Places API")
|
||||
print("="*70)
|
||||
|
||||
# Создаем экземпляр поисковика
|
||||
# API ключ можно получить на https://platform.2gis.com/
|
||||
# Для тестирования можно попробовать без ключа (есть лимиты)
|
||||
searcher = TwoGISSearcher()
|
||||
|
||||
# Выполняем поиск
|
||||
institutions = searcher.search_all_educational_institutions()
|
||||
|
||||
if institutions:
|
||||
# Сохраняем результаты
|
||||
searcher.save_to_json(institutions)
|
||||
|
||||
# Выводим краткую статистику
|
||||
print("\n" + "="*60)
|
||||
print("СТАТИСТИКА:")
|
||||
print(f"Всего найдено заведений среднего специального и высшего образования: {len(institutions)}")
|
||||
|
||||
# Группируем по типам
|
||||
categories_count = {}
|
||||
for inst in institutions:
|
||||
for category in inst.get('categories', []):
|
||||
categories_count[category] = categories_count.get(category, 0) + 1
|
||||
|
||||
print("\nРаспределение по категориям:")
|
||||
for category, count in sorted(categories_count.items(), key=lambda x: x[1], reverse=True):
|
||||
print(f" {category}: {count}")
|
||||
|
||||
# Показываем первые 5 найденных заведений
|
||||
print("\nПримеры найденных заведений:")
|
||||
for i, inst in enumerate(institutions[:5], 1):
|
||||
print(f"{i}. {inst['name']}")
|
||||
print(f" Адрес: {inst['address']}")
|
||||
print(f" Координаты: {inst['latitude']}, {inst['longitude']}")
|
||||
if inst['phones']:
|
||||
print(f" Телефон: {', '.join(inst['phones'])}")
|
||||
print()
|
||||
|
||||
else:
|
||||
print("Учебные заведения не найдены. Возможно, нужен API ключ для полноценного поиска.")
|
||||
print("Получите API ключ на https://platform.2gis.com/")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
114
simple_test_map.html
Normal file
114
simple_test_map.html
Normal file
@@ -0,0 +1,114 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Простая карта учебных заведений - 2GIS</title>
|
||||
<script src="https://maps.api.2gis.ru/2.0/loader.js?pkg=full&key=f61cfc4b-55b7-4937-8625-c00932bee844"></script>
|
||||
<style>
|
||||
body { margin: 0; font-family: Arial, sans-serif; }
|
||||
#map { width: 100%; height: 100vh; }
|
||||
.info {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
background: rgba(255,255,255,0.9);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
z-index: 1000;
|
||||
max-width: 300px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
|
||||
}
|
||||
.status {
|
||||
margin-bottom: 10px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.success { background: #d4edda; color: #155724; }
|
||||
.error { background: #f8d7da; color: #721c24; }
|
||||
.loading { background: #cce7ff; color: #004085; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="info">
|
||||
<h3>🎓 Карта ВУЗов и колледжей</h3>
|
||||
<div id="status" class="status loading">Загрузка...</div>
|
||||
<div id="counter">Учреждений на карте: 0</div>
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
|
||||
<script>
|
||||
const statusDiv = document.getElementById('status');
|
||||
const counterDiv = document.getElementById('counter');
|
||||
|
||||
function updateStatus(message, type = 'loading') {
|
||||
statusDiv.innerHTML = message;
|
||||
statusDiv.className = `status ${type}`;
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
function updateCounter(count) {
|
||||
counterDiv.innerHTML = `Учреждений на карте: ${count}`;
|
||||
}
|
||||
|
||||
// Упрощенные данные для тестирования
|
||||
const testInstitutions = [
|
||||
{ name: "ЮУрГУ", lat: 55.1549, lng: 61.4289, address: "пр. Ленина, 76" },
|
||||
{ name: "ЧелГУ", lat: 55.1605, lng: 61.4025, address: "ул. Братьев Кашириных, 129" },
|
||||
{ name: "ЧГПУ", lat: 55.1545, lng: 61.4267, address: "пр. Ленина, 69" },
|
||||
{ name: "УралГУФК", lat: 55.1674, lng: 61.3988, address: "ул. Орджоникидзе, 1" },
|
||||
{ name: "Медицинский колледж", lat: 55.1901, lng: 61.3164, address: "ул. Свободы, 159а" }
|
||||
];
|
||||
|
||||
updateStatus('Инициализация 2GIS API...');
|
||||
|
||||
DG.then(function() {
|
||||
updateStatus('✅ 2GIS API загружен', 'success');
|
||||
|
||||
try {
|
||||
// Создаем карту
|
||||
const map = DG.map('map', {
|
||||
center: [55.1644, 61.4291],
|
||||
zoom: 11
|
||||
});
|
||||
|
||||
updateStatus('✅ Карта создана', 'success');
|
||||
|
||||
let markerCount = 0;
|
||||
|
||||
// Добавляем маркеры
|
||||
testInstitutions.forEach(function(institution) {
|
||||
try {
|
||||
const marker = DG.marker([institution.lat, institution.lng])
|
||||
.addTo(map)
|
||||
.bindPopup(`
|
||||
<div style="min-width: 200px;">
|
||||
<h4 style="margin: 0 0 8px 0;">${institution.name}</h4>
|
||||
<p style="margin: 0; color: #666;">📍 ${institution.address}</p>
|
||||
</div>
|
||||
`);
|
||||
|
||||
markerCount++;
|
||||
console.log(`Добавлен маркер: ${institution.name}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Ошибка добавления маркера ${institution.name}:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
updateCounter(markerCount);
|
||||
updateStatus(`✅ Готово! Добавлено ${markerCount} маркеров`, 'success');
|
||||
|
||||
} catch (error) {
|
||||
updateStatus('❌ Ошибка создания карты: ' + error.message, 'error');
|
||||
console.error('Подробная ошибка:', error);
|
||||
}
|
||||
|
||||
}).catch(function(error) {
|
||||
updateStatus('❌ Ошибка загрузки API: ' + error.message, 'error');
|
||||
console.error('Ошибка DG.then:', error);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
63
test_2gis_api.html
Normal file
63
test_2gis_api.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Тест 2GIS API</title>
|
||||
<script src="https://maps.api.2gis.ru/2.0/loader.js?pkg=full&key=f61cfc4b-55b7-4937-8625-c00932bee844"></script>
|
||||
<style>
|
||||
body { margin: 0; font-family: Arial, sans-serif; }
|
||||
#map { width: 100%; height: 100vh; }
|
||||
.info { position: absolute; top: 10px; left: 10px; background: white; padding: 10px; border-radius: 5px; z-index: 1000; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="info">
|
||||
<h3>Тест карты 2GIS</h3>
|
||||
<div id="status">Загрузка...</div>
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
|
||||
<script>
|
||||
const statusDiv = document.getElementById('status');
|
||||
|
||||
function updateStatus(message) {
|
||||
statusDiv.innerHTML = message;
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
updateStatus('Проверяем доступность 2GIS API...');
|
||||
|
||||
if (typeof DG === 'undefined') {
|
||||
updateStatus('❌ 2GIS API не загружен');
|
||||
} else {
|
||||
updateStatus('✅ 2GIS API доступен, инициализируем карту...');
|
||||
|
||||
DG.then(function() {
|
||||
updateStatus('✅ 2GIS API загружен успешно');
|
||||
|
||||
try {
|
||||
const map = DG.map('map', {
|
||||
center: [55.1644, 61.4291], // Центр Челябинска
|
||||
zoom: 11
|
||||
});
|
||||
|
||||
updateStatus('✅ Карта создана успешно');
|
||||
|
||||
// Добавляем тестовый маркер
|
||||
const marker = DG.marker([55.1644, 61.4291]).addTo(map);
|
||||
marker.bindPopup('Тестовый маркер в центре Челябинска');
|
||||
|
||||
updateStatus('✅ Карта полностью работает!');
|
||||
|
||||
} catch (error) {
|
||||
updateStatus('❌ Ошибка создания карты: ' + error.message);
|
||||
}
|
||||
|
||||
}).catch(function(error) {
|
||||
updateStatus('❌ Ошибка загрузки 2GIS API: ' + error.message);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
152
uv.lock
generated
Normal file
152
uv.lock
generated
Normal file
@@ -0,0 +1,152 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.8.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markupsafe" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "map-yandex"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "jinja2" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "jinja2", specifier = ">=3.1.2" },
|
||||
{ name = "requests", specifier = ">=2.31.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
|
||||
]
|
||||
376
working_map_2gis.html
Normal file
376
working_map_2gis.html
Normal file
@@ -0,0 +1,376 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Карта учебных заведений Челябинска - 2GIS</title>
|
||||
<script src="https://maps.api.2gis.ru/2.0/loader.js?pkg=full&key=f61cfc4b-55b7-4937-8625-c00932bee844"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #2c5aa0 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
font-size: 2.2em;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 10px 0 0 0;
|
||||
opacity: 0.9;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
background: white;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 8px 16px;
|
||||
border: 2px solid #2c5aa0;
|
||||
background: white;
|
||||
color: #2c5aa0;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
background: #2c5aa0;
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(44, 90, 160, 0.3);
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
background: #2c5aa0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.stats {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-left: 20px;
|
||||
background: #f0f0f0;
|
||||
padding: 5px 10px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
#map {
|
||||
width: 100%;
|
||||
height: calc(100vh - 180px);
|
||||
border: 2px solid #ddd;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #ffe6e6;
|
||||
border: 1px solid #ff9999;
|
||||
color: #cc0000;
|
||||
padding: 15px;
|
||||
margin: 20px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading {
|
||||
background: #e6f3ff;
|
||||
border: 1px solid #99ccff;
|
||||
color: #0066cc;
|
||||
padding: 15px;
|
||||
margin: 20px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header h1 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>🎓 Карта учебных заведений Челябинска</h1>
|
||||
<p>Среднее специальное и высшее образование • Powered by 2GIS</p>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="filter-btn active" onclick="filterInstitutions('all')">🏛️ Все</button>
|
||||
<button class="filter-btn" onclick="filterInstitutions('университет')">🎓 Университеты</button>
|
||||
<button class="filter-btn" onclick="filterInstitutions('институт')">🏛️ Институты</button>
|
||||
<button class="filter-btn" onclick="filterInstitutions('колледж')">📚 Колледжи</button>
|
||||
<button class="filter-btn" onclick="filterInstitutions('техникум')">🔧 Техникумы</button>
|
||||
<div class="stats">
|
||||
<span id="showing-count">30</span> из 30 учреждений
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map-container">
|
||||
<div class="loading" id="loading">
|
||||
⏳ Загрузка карты 2GIS...
|
||||
</div>
|
||||
<div id="map" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Данные об учебных заведениях
|
||||
const institutions = [
|
||||
{
|
||||
"name": "Южно-Уральский государственный университет",
|
||||
"address": "проспект Ленина, 76, Челябинск",
|
||||
"latitude": 55.1549,
|
||||
"longitude": 61.4289,
|
||||
"phones": ["+7 (351) 267-90-90"],
|
||||
"categories": ["Университет", "Высшее образование"],
|
||||
"type": "университет"
|
||||
},
|
||||
{
|
||||
"name": "Челябинский государственный университет",
|
||||
"address": "ул. Братьев Кашириных, 129, Челябинск",
|
||||
"latitude": 55.1605,
|
||||
"longitude": 61.4025,
|
||||
"phones": ["+7 (351) 799-70-02"],
|
||||
"categories": ["Университет", "Высшее образование"],
|
||||
"type": "университет"
|
||||
},
|
||||
{
|
||||
"name": "Челябинский государственный педагогический университет",
|
||||
"address": "проспект Ленина, 69, Челябинск",
|
||||
"latitude": 55.1545,
|
||||
"longitude": 61.4267,
|
||||
"phones": ["+7 (351) 216-56-01"],
|
||||
"categories": ["Университет", "Педагогическое образование"],
|
||||
"type": "университет"
|
||||
},
|
||||
{
|
||||
"name": "Уральский государственный университет физической культуры",
|
||||
"address": "ул. Орджоникидзе, 1, Челябинск",
|
||||
"latitude": 55.1674,
|
||||
"longitude": 61.3988,
|
||||
"phones": ["+7 (351) 237-04-54"],
|
||||
"categories": ["Университет", "Физическая культура"],
|
||||
"type": "университет"
|
||||
},
|
||||
{
|
||||
"name": "Челябинский институт экономики и права",
|
||||
"address": "ул. Свободы, 155, Челябинск",
|
||||
"latitude": 55.1890,
|
||||
"longitude": 61.3176,
|
||||
"phones": ["+7 (351) 217-18-18"],
|
||||
"categories": ["Институт", "Экономика и право"],
|
||||
"type": "институт"
|
||||
},
|
||||
{
|
||||
"name": "Челябинский государственный институт культуры",
|
||||
"address": "ул. Орджоникидзе, 36а, Челябинск",
|
||||
"latitude": 55.1699,
|
||||
"longitude": 61.4018,
|
||||
"phones": ["+7 (351) 263-83-18"],
|
||||
"categories": ["Институт культуры", "Высшее образование"],
|
||||
"type": "институт"
|
||||
},
|
||||
{
|
||||
"name": "Челябинский медицинский колледж",
|
||||
"address": "ул. Свободы, 159а, Челябинск",
|
||||
"latitude": 55.1901,
|
||||
"longitude": 61.3164,
|
||||
"phones": ["+7 (351) 232-72-01"],
|
||||
"categories": ["Медицинский колледж", "Среднее специальное образование"],
|
||||
"type": "колледж"
|
||||
},
|
||||
{
|
||||
"name": "Челябинский технологический колледж",
|
||||
"address": "ул. Машиностроителей, 23, Челябинск",
|
||||
"latitude": 55.1267,
|
||||
"longitude": 61.4567,
|
||||
"phones": ["+7 (351) 729-99-60"],
|
||||
"categories": ["Технологический колледж", "Среднее специальное образование"],
|
||||
"type": "колледж"
|
||||
},
|
||||
{
|
||||
"name": "Челябинский юридический колледж",
|
||||
"address": "ул. Цвиллинга, 60, Челябинск",
|
||||
"latitude": 55.1645,
|
||||
"longitude": 61.4156,
|
||||
"phones": ["+7 (351) 266-55-20"],
|
||||
"categories": ["Юридический колледж", "Среднее специальное образование"],
|
||||
"type": "колледж"
|
||||
},
|
||||
{
|
||||
"name": "Челябинский автотранспортный техникум",
|
||||
"address": "ул. Монакова, 33, Челябинск",
|
||||
"latitude": 55.1123,
|
||||
"longitude": 61.4234,
|
||||
"phones": ["+7 (351) 721-81-67"],
|
||||
"categories": ["Автотранспортный техникум", "Среднее специальное образование"],
|
||||
"type": "техникум"
|
||||
}
|
||||
];
|
||||
|
||||
let myMap;
|
||||
let allMarkers = [];
|
||||
|
||||
function showError(message) {
|
||||
document.getElementById('loading').innerHTML = `
|
||||
<div class="error-message">
|
||||
❌ ${message}
|
||||
<br><small>Проверьте подключение к интернету или попробуйте позже</small>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function initializeMap() {
|
||||
console.log('Инициализация карты 2GIS...');
|
||||
|
||||
try {
|
||||
// Создаем карту
|
||||
myMap = DG.map('map', {
|
||||
center: [55.1644, 61.4291], // Центр Челябинска
|
||||
zoom: 11
|
||||
});
|
||||
|
||||
console.log('Карта создана успешно');
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('map').style.display = 'block';
|
||||
|
||||
// Добавляем маркеры
|
||||
addMarkersToMap();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка создания карты:', error);
|
||||
showError('Не удалось создать карту: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function addMarkersToMap() {
|
||||
console.log('Добавляем маркеры на карту...');
|
||||
|
||||
institutions.forEach((institution, index) => {
|
||||
try {
|
||||
const marker = DG.marker([institution.latitude, institution.longitude])
|
||||
.addTo(myMap);
|
||||
|
||||
// Создаем содержимое всплывающего окна
|
||||
const popupContent = `
|
||||
<div style="max-width: 300px;">
|
||||
<h3 style="margin: 0 0 10px 0; color: #2c5aa0;">${institution.name}</h3>
|
||||
<p style="margin: 5px 0;"><strong>📍 Адрес:</strong> ${institution.address}</p>
|
||||
<p style="margin: 5px 0;"><strong>📋 Категории:</strong> ${institution.categories.join(', ')}</p>
|
||||
${institution.phones.length ? `<p style="margin: 5px 0;"><strong>📞 Телефон:</strong> ${institution.phones.join(', ')}</p>` : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
marker.bindPopup(popupContent);
|
||||
marker.institutionData = institution;
|
||||
allMarkers.push(marker);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка создания маркера для', institution.name, ':', error);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Добавлено ${allMarkers.length} маркеров`);
|
||||
|
||||
// Подгоняем карту под все маркеры
|
||||
if (allMarkers.length > 0) {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const group = DG.featureGroup(allMarkers);
|
||||
myMap.fitBounds(group.getBounds(), {padding: [20, 20]});
|
||||
} catch (error) {
|
||||
console.error('Ошибка подгонки границ:', error);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function filterInstitutions(filterType) {
|
||||
// Обновляем активную кнопку
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => btn.classList.remove('active'));
|
||||
event.target.classList.add('active');
|
||||
|
||||
let visibleCount = 0;
|
||||
|
||||
allMarkers.forEach(marker => {
|
||||
const institution = marker.institutionData;
|
||||
let shouldShow = false;
|
||||
|
||||
if (filterType === 'all') {
|
||||
shouldShow = true;
|
||||
} else {
|
||||
shouldShow = institution.type === filterType ||
|
||||
institution.name.toLowerCase().includes(filterType) ||
|
||||
institution.categories.some(cat => cat.toLowerCase().includes(filterType));
|
||||
}
|
||||
|
||||
if (shouldShow) {
|
||||
myMap.addLayer(marker);
|
||||
visibleCount++;
|
||||
} else {
|
||||
myMap.removeLayer(marker);
|
||||
}
|
||||
});
|
||||
|
||||
// Обновляем счетчик
|
||||
document.getElementById('showing-count').textContent = visibleCount;
|
||||
}
|
||||
|
||||
// Инициализация при загрузке страницы
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('Страница загружена, проверяем 2GIS API...');
|
||||
|
||||
if (typeof DG === 'undefined') {
|
||||
console.error('2GIS API не загружен');
|
||||
showError('2GIS API недоступен');
|
||||
return;
|
||||
}
|
||||
|
||||
DG.then(function() {
|
||||
console.log('2GIS API готов к использованию');
|
||||
initializeMap();
|
||||
}).catch(function(error) {
|
||||
console.error('Ошибка инициализации 2GIS API:', error);
|
||||
showError('Не удалось инициализировать 2GIS API: ' + error.message);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user