ToDo:

  • Сделать оси по обе стороны
  • Сделать подложку с черезстрочной заливкой
  • Добавить интерактивность к дереву

Интерактивная карта участка на Hugo с SVG и JSON

Отличная идея — создать интерактивную карту вашего участка в деревне! Использование SVG для визуальной части и JSON для данных, интегрированных в ваш сайт на Hugo с темой hugo docs, — это мощное и гибкое решение. Вот подробная инструкция, как это реализовать:


АЯАЯ10110110010099999898979796969595949493939292919190908989888887878686858584848383828281818080797978787777767675757474737372727171707069696868676766666565646463636262616160605959585857575656555554545353525251515050494948484747464645454444434342424141404039393838373736363535343433333232313130302929282827272626252524242323222221212020191918181717161615151414131312121111101099887766554433221ААББВВГГДДЕЕЖЖИИККЛЛММННППРРССТТУУФФШШЭЭЮЮЯЯАААААБАБАВАВАГАГАДАДАЕАЕАЖАЖАИАИАКАКАЛАЛАМАМАНАНАПАПАРАРАСАСАТАТАУАУАФАФАШАШАЭАЭАЮАЮ1

1. Подготовьте SVG-файл с планом участка

Сначала вам понадобится векторное изображение вашего участка.

  • Нарисуйте план участка: Используйте векторный графический редактор, например, Inkscape (бесплатный), Adobe Illustrator или Affinity Designer.
    • Важно: Каждый интерактивный объект (дерево, грядка, постройка) должен быть отдельным элементом (путь, круг, прямоугольник и т.д.).
    • Присвойте уникальные ID: Каждому такому элементу присвойте уникальный id. Это критически важно для связывания элементов SVG с данными из JSON. Например: <rect id="dom" ...>, <circle id="yablonya-1" ...>, <polygon id="gryadka-pomidory" ...>.
    • Сохраните как Plain SVG: При сохранении выберите опцию “Plain SVG” (Простой SVG) или аналогичную, чтобы минимизировать избыточный код.

2. Создайте JSON-файл с данными

Этот файл будет содержать всю информацию, которая должна отображаться при наведении на элементы карты.

* **Структура JSON:** Создайте JSON-файл (например, `data.json`) в директории `static` вашего проекта Hugo (например, `static/data/map_data.json`).

        {
          "items": [
            {
              "id": "dom",
              "name": "Дом",
              "type": "Постройка",
              "description": "Основное здание, требует ежегодного осмотра кровли.",
              "tasks": [
                {"task_name": "Осмотр крыши", "deadline": "Август", "notes": "Проверить на протечки и повреждения"},
                {"task_name": "Чистка водостоков", "deadline": "Октябрь", "notes": "Перед зимой"}
              ]
            },
            {
              "id": "yablonya-1",
              "name": "Яблоня сорта 'Антоновка'",
              "type": "Дерево",
              "description": "Молодое дерево, посажено 2023 г.",
              "tasks": [
                {"task_name": "Подкормка", "deadline": "Апрель", "notes": "Азотные удобрения"},
                {"task_name": "Обрезка", "deadline": "Март", "notes": "Санитарная обрезка"}
              ]
            },
            {
              "id": "gryadka-pomidory",
              "name": "Грядка с томатами",
              "type": "Грядка",
              "description": "Используется для выращивания индетерминантных сортов томатов.",
              "tasks": [
                {"task_name": "Подвязка", "deadline": "Июнь", "notes": "По мере роста"},
                {"task_name": "Пасынкование", "deadline": "Весь сезон", "notes": "Регулярно"}
              ]
            }
            // Добавьте другие элементы по аналогии
          ]
        }

    * **`id`:** Должен точно совпадать с `id` соответствующего элемента в SVG.
    * **`name`:** Название элемента для отображения.
    * **`type`:** Категория элемента (для фильтрации или стилизации).
    * **`description`:** Общее описание.
    * **`tasks`:** Массив объектов с задачами, сроками и примечаниями.

3. Интеграция SVG и JSON в страницу Hugo

Вам нужно создать страницу Hugo, которая будет загружать SVG, JSON и управлять интерактивностью.

3.1. Создание шорткода (рекомендуется)

Шорткод позволит вам легко вставлять карту на любую страницу или запись. Создайте файл

`layouts/shortcodes/interactive_map.html`:

        <div id="interactive-map-container" style="position: relative;">
            {{ with .Get "svg_path" }}
                {{ $svgContent := readFile . }}
                {{ $svgContent | safeHTML }}
            {{ else }}
                <p>Ошибка: Укажите путь к SVG-файлу.</p>
            {{ end }}

            <div id="tooltip" style="position: absolute; background-color: rgba(0,0,0,0.8); color: white; padding: 10px; border-radius: 5px; display: none; z-index: 1000;">
                <h3></h3>
                <p></p>
                <h4>Необходимые работы:</h4>
                <ul id="tasks-list"></ul>
            </div>
        </div>

        <script>
            document.addEventListener('DOMContentLoaded', function() {
                const svgContainer = document.getElementById('interactive-map-container');
                const tooltip = document.getElementById('tooltip');
                const tooltipTitle = tooltip.querySelector('h3');
                const tooltipDescription = tooltip.querySelector('p');
                const tasksList = tooltip.querySelector('#tasks-list');

                let mapData = {}; // Здесь будут храниться данные из JSON

                // Загрузка JSON-данных
                fetch('/data/map_data.json') // Путь к вашему JSON-файлу
                    .then(response => response.json())
                    .then(data => {
                        mapData = data.items.reduce((acc, item) => {
                            acc[item.id] = item;
                            return acc;
                        }, {});
                        attachEventListeners(); // Прикрепляем слушателей после загрузки данных
                    })
                    .catch(error => console.error('Ошибка загрузки данных карты:', error));

                function attachEventListeners() {
                    const svgElement = svgContainer.querySelector('svg');
                    if (!svgElement) {
                        console.error("SVG элемент не найден внутри контейнера.");
                        return;
                    }

                    svgElement.querySelectorAll('[id]').forEach(element => {
                        const elementId = element.id;
                        if (mapData[elementId]) {
                            element.addEventListener('mouseenter', function(e) {
                                const data = mapData[elementId];
                                tooltipTitle.textContent = data.name;
                                tooltipDescription.textContent = data.description;
                                tasksList.innerHTML = '';
                                if (data.tasks && data.tasks.length > 0) {
                                    data.tasks.forEach(task => {
                                        const li = document.createElement('li');
                                        li.innerHTML = `<strong>${task.task_name}</strong> (Срок: ${task.deadline})<br>${task.notes}`;
                                        tasksList.appendChild(li);
                                    });
                                } else {
                                    tasksList.innerHTML = '<li>Нет запланированных работ.</li>';
                                }

                                // Позиционирование тултипа
                                tooltip.style.left = `${e.clientX + 15}px`;
                                tooltip.style.top = `${e.clientY + 15}px`;
                                tooltip.style.display = 'block';

                                // Добавляем класс для подсветки элемента SVG
                                element.classList.add('highlight-svg-element');
                            });

                            element.addEventListener('mouseleave', function() {
                                tooltip.style.display = 'none';
                                element.classList.remove('highlight-svg-element');
                            });
                        }
                    });
                }
            });
        </script>

        <style>
            /* Базовые стили для SVG-элементов */
            #interactive-map-container svg * {
                transition: fill 0.3s ease, stroke 0.3s ease; /* Плавный переход при наведении */
                cursor: pointer;
            }

            /* Стиль при наведении */
            #interactive-map-container svg *.highlight-svg-element {
                fill: rgba(255, 255, 0, 0.5) !important; /* Желтая полупрозрачная заливка */
                stroke: #FFD700 !important; /* Золотая обводка */
                stroke-width: 2px !important;
            }

            /* Если у вас есть элементы без ID, но вы хотите, чтобы они не реагировали */
            #interactive-map-container svg *:not([id]) {
                cursor: default; /* Обычный курсор */
            }
        </style>

3.2. Размещение SVG-файла

Поместите ваш SVG-файл, например, plot_plan.svg, в директорию static/images/.

3.3. Использование шорткода на странице

На любой странице или записи Hugo (например, content/my_plot.md):

        ---
        title: "Мой Участок: Интерактивный План"
        date: 2023-10-26T10:00:00+03:00
        draft: false
        ---
    ## Добро пожаловать на интерактивную карту моего участка!

    Наведите курсор на элементы, чтобы узнать о них больше и увидеть запланированные работы.
         interactive_map svg_path="/images/plot_plan.svg" >}}
    Это мой дом и прилегающая территория. Здесь вы найдете информацию о расположении деревьев, грядок, построек и задачах по уходу за ними.

4. Доработка и улучшение

  • Пути в SVG: Убедитесь, что ваш SVG-файл не содержит относительных путей к другим ресурсам.
  • Стилизация SVG: Можно добавить стили непосредственно в SVG-файл или использовать CSS в вашем проекте Hugo.
  • Позиционирование тултипа: Текущее позиционирование тултипа основано на координатах курсора. Для лучшего пользовательского опыта можно настроить его смещение или проверять границы экрана.
  • Сложные SVG-файлы: Если ваш SVG очень большой или сложный, рассмотрите оптимизацию, например, через SVG-спрайты или асинхронную загрузку SVG через JavaScript.
  • Фильтрация и поиск: Для большого количества объектов можно добавить элементы управления для фильтрации (например, показывать только деревья) или поиска.
  • Производительность: Для очень больших SVG-файлов или большого количества данных может потребоваться оптимизация, например, ленивая загрузка.