function init() {
// Создаём карту и ставим начальный центр
var map = L.map('yr-map').setView([36.752, 28.938], 9);
// Подключаем OpenStreetMap тайлы
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '©
OpenStreetMap contributors'
}).addTo(map);
// Добавляем линии маршрута по дням
days.forEach(function(day) {
L.polyline(day.path, { color: '#1a7fb8', weight: 3 }).addTo(map)
.bindPopup('
' + day.weekday + '' + day.desc);
});
// Добавляем маркеры стоянок
stops.forEach(function(stop) {
L.marker(stop.ll).addTo(map).bindPopup(stop.name);
});
}
// Подложки по порядку: светлая CARTO → OpenStreetMap (обесцвеченная) → Esri светло-серая
var TILES = [
{ url: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', o: { subdomains: 'abcd', attribution: '© OpenStreetMap © CARTO' } },
{ url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', o: { attribution: '© OpenStreetMap' }, gray: true },
{ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}', o: { attribution: '© Esri', maxZoom: 16 } }
];
function addTiles(map, i) {
if (i >= TILES.length) return;
var t = TILES[i], el = document.getElementById('yr-map');
el.classList.toggle('yr-osm', !!t.gray);
var layer = L.tileLayer(t.url, L.extend({ maxZoom: 18 }, t.o)).addTo(map);
var ok = false, errors = 0, switched = false;
function next() { if (switched || ok) return; switched = true; map.removeLayer(layer); addTiles(map, i + 1); }
layer.on('tileload', function () { ok = true; });
layer.on('tileerror', function () { if (++errors >= 3) next(); });
setTimeout(next, 3000);
}
function init() {
var map = L.map('yr-map', { scrollWheelZoom: false, zoomControl: true });
addTiles(map, 0);
// Бледная линия всего маршрута
var all = [];
days.forEach(function (d) { all = all.concat(d.path); });
L.polyline(all, { color: '#1a7fb8', weight: 2, opacity: .25, dashArray: '4 6' }).addTo(map);
map.fitBounds(L.latLngBounds(all).pad(0.1));
// Маркеры стоянок
var pins = stops.map(function (s, i) {
var m = L.marker(s.ll, { icon: L.divIcon({ className: '', html: '
', iconSize: [14,14], iconAnchor: [7,7] }) })
.addTo(map)
.bindTooltip(s.name, { permanent: true, direction: i === 1 ? 'right' : 'top', offset: i === 1 ? [8,0] : [0,-8], className: 'yr-label' });
return m;
});
var done = L.polyline([], { color: '#1a7fb8', weight: 4, opacity: .95 }).addTo(map);
var boat = L.marker(stops[0].ll, { icon: L.divIcon({ className: '', html: '
', iconSize: [30,30], iconAnchor: [15,15] }), zIndexOffset: 1000 }).addTo(map);
// Длины отрезков для плавной интерполяции
days.forEach(function (d) {
d.cum = [0];
for (var i = 1; i < d.path.length; i++) {
d.cum.push(d.cum[i-1] + map.distance(d.path[i-1], d.path[i]));
}
d.len = d.cum[d.cum.length-1];
});
function partial(d, f) {
var target = d.len * f, pts = [d.path[0]];
for (var i = 1; i < d.path.length; i++) {
if (d.cum[i] <= target) { pts.push(d.path[i]); continue; }
var k = (target - d.cum[i-1]) / (d.cum[i] - d.cum[i-1]);
var a = d.path[i-1], b = d.path[i];
pts.push([a[0] + (b[0]-a[0])*k, a[1] + (b[1]-a[1])*k]);
break;
}
return pts;
}
var range = document.getElementById('yr-range');
var elDay = document.getElementById('yr-day'), elLeg = document.getElementById('yr-leg'), elDesc = document.getElementById('yr-desc');
var ticks = document.getElementById('yr-ticks');
// Подписи под ползунком
['Старт','1','2','3','4','5','6','7'].forEach(function (t, i) {
var s = document.createElement('span');
s.textContent = i === 0 ? t : 'День ' + t;
s.onclick = function () { range.value = i * 100; render(); };
ticks.appendChild(s);
});
var lastDay = -1;
function render() {
var v = +range.value / 100; // 0..7
var di = Math.min(Math.floor(v), 6); // текущий день (0..6)
var f = v - di;
if (v >= 7) { di = 6; f = 1; }
var pts = [];
for (var i = 0; i < di; i++) pts = pts.concat(days[i].path);
var cur = v > 0 ? partial(days[di], f) : [stops[0].ll];
pts = pts.concat(cur);
done.setLatLngs(pts);
boat.setLatLng(cur[cur.length-1]);
// Подсветка пройденных стоянок
stops.forEach(function (s, i) {
var reached = i === 0 || days.some(function (d, k) { return d.to === i && (k < di || (k === di && f >= 1)); });
pins[i].getElement() && pins[i].getElement().firstChild.classList.toggle('dim', !reached);
});
range.style.background = 'linear-gradient(to right,#1a7fb8 ' + (v/7*100) + '%,#c9d8e2 ' + (v/7*100) + '%)';
var shownDay = v === 0 ? -1 : (f === 0 && di > 0 ? di - 1 : di);
if (v >= 7) shownDay = 6;
Array.prototype.forEach.call(ticks.children, function (el, i) { el.classList.toggle('on', i === shownDay + 1); });
if (shownDay !== lastDay) {
lastDay = shownDay;
if (shownDay < 0) {
elDay.textContent = 'Старт';
elLeg.textContent = 'Гёчек, марина D-Marin';
elDesc.textContent = 'Передвиньте ползунок, чтобы пройти маршрут день за днём.';
} else {
var d = days[shownDay];
elDay.textContent = 'День ' + (shownDay + 1) + ' · ' + d.weekday + ' · ' + d.miles + ' миль';
elLeg.textContent = stops[d.from].name + ' → ' + stops[d.to].name;
elDesc.textContent = d.desc;
}
}
}
range.addEventListener('input', render);
render();
// Tilda иногда меняет размеры блока после загрузки — перерисовываем карту
function fix() { map.invalidateSize(); map.fitBounds(L.latLngBounds(all).pad(0.1)); }
setTimeout(fix, 300);
window.addEventListener('load', fix);
if (window.ResizeObserver) new ResizeObserver(function () { map.invalidateSize(); }).observe(document.getElementById('yr-map'));
}
loadLeaflet(0);
})();