Files
map_yandex/map_template_2gis.html
2025-09-23 20:13:51 +05:00

443 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>