class LayerManager { constructor() { this.layers = new Map(); } // Нормализация геометрии в мульти-формат normalizeGeometry(feature) { const geometry = feature.geometry; const properties = feature.properties || {}; if (geometry.type.startsWith('Multi')) { return feature; } switch(geometry.type) { case 'Point': return { ...feature, geometry: { type: 'MultiPoint', coordinates: [geometry.coordinates] } }; case 'LineString': return { ...feature, geometry: { type: 'MultiLineString', coordinates: [geometry.coordinates] } }; case 'Polygon': return { ...feature, geometry: { type: 'MultiPolygon', coordinates: [geometry.coordinates] } }; default: console.warn(`Неподдерживаемый тип геометрии: ${geometry.type}`); return feature; } } // Определение стиля для объекта resolveStyle(properties, geometryType) { if (properties.customStyle) { return properties.customStyle; } if (properties.layers && properties.layers.length > 0) { for (let layer of properties.layers) { const layerKey = `layer:${layer}`; if (StyleRules[layerKey]) { return StyleRules[layerKey]; } } } const styleKey = `category:${properties.category}+type:${geometryType}`; if (StyleRules[styleKey]) { return StyleRules[styleKey]; } const categoryKey = `category:${properties.category}`; if (StyleRules[categoryKey]) { return StyleRules[categoryKey]; } return this.getDefaultStyle(geometryType); } // Стили по умолчанию getDefaultStyle(geometryType) { const defaults = { multipoint: { preset: 'islands#darkGreenCircleDotIcon' }, multilinestring: { strokeColor: '#000000', strokeWidth: 4, strokeOpacity: 0.6 }, multipolygon: { fillColor: '#c4c4c4', fillOpacity: 0.4, strokeColor: '#c4c4c4', strokeWidth: 1, strokeOpacity: 0.8 } }; return defaults[geometryType] || {}; } // Стили для вершин getVerticesStyle(geometryType) { const styles = { multilinestring: { iconLayout: 'default#image', iconImageHref: './icons/vertex.svg', iconImageSize: [18, 18], iconImageOffset: [-9, -9] }, multipolygon: { iconLayout: 'default#image', iconImageHref: './icons/vertex.svg', iconImageSize: [18, 18], iconImageOffset: [-9, -9] } }; return styles[geometryType] || styles.multilinestring; } // Добавление объекта на карту // Добавление объекта на карту addObject(feature, map) { const normalizedFeature = this.normalizeGeometry(feature); const geometryType = normalizedFeature.geometry.type.toLowerCase(); let mapObject; let vertices = null; const yandexCoords = CoordUtils.geoJsonToYandex(normalizedFeature.geometry.coordinates); const style = this.resolveStyle(normalizedFeature.properties, geometryType); switch(geometryType) { case 'multipoint': mapObject = this.createMultiPoint(yandexCoords, normalizedFeature.properties, style); break; case 'multilinestring': mapObject = this.createMultiLineString(normalizedFeature.geometry.coordinates, normalizedFeature.properties, style); let lineVerticesCollection = new ymaps.GeoObjectCollection(); let pointOffset = 0; normalizedFeature.geometry.coordinates.forEach((lineCoords, index) => { const vertices = this.createVerticesFromMultiGeometry(normalizedFeature, index, pointOffset); if (vertices) { lineVerticesCollection.add(vertices); pointOffset += lineCoords.length; } }); if (lineVerticesCollection.getLength() > 0) { vertices = lineVerticesCollection; // ★★★ ЗАКОММЕНТИРОВАТЬ ЭТУ СТРОКУ ★★★ // map.geoObjects.add(vertices); } break; case 'multipolygon': mapObject = this.createMultiPolygon(normalizedFeature.geometry.coordinates, normalizedFeature.properties, style); let polyVerticesCollection = new ymaps.GeoObjectCollection(); let polyPointOffset = 0; normalizedFeature.geometry.coordinates.forEach((polygonCoords, index) => { const vertices = this.createVerticesFromMultiGeometry(normalizedFeature, index, polyPointOffset); if (vertices) { polyVerticesCollection.add(vertices); polyPointOffset += polygonCoords[0].length; } }); if (polyVerticesCollection.getLength() > 0) { vertices = polyVerticesCollection; // ★★★ ЗАКОММЕНТИРОВАТЬ ЭТУ СТРОКУ ★★★ // map.geoObjects.add(vertices); } break; default: console.error(`Необработанный тип геометрии: ${geometryType}`); } if (mapObject) { map.geoObjects.add(mapObject); } return { main: mapObject, vertices: vertices, feature: normalizedFeature }; } // Создание вершин для мультигеометрии createVerticesFromMultiGeometry(feature, contourIndex, pointIndexOffset = 0) { const geometryType = feature.geometry.type.toLowerCase(); const coordinates = feature.geometry.coordinates; const properties = feature.properties; let points = []; if (geometryType === 'multilinestring') { if (coordinates[contourIndex]) { points = coordinates[contourIndex]; } } else if (geometryType === 'multipolygon') { if (coordinates[contourIndex] && coordinates[contourIndex][0]) { points = coordinates[contourIndex][0]; } } else { return null; } if (points.length === 0) { return null; } const verticesCollection = new ymaps.GeoObjectCollection({}, { hintContent: `Вершины контура ${contourIndex + 1} (${points.length} точек)` }); const pointsData = properties.pointsData || []; points.forEach((pointCoords, index) => { const yandexCoords = CoordUtils.geoJsonToYandex(pointCoords); const pointDataIndex = pointIndexOffset + index; const pointData = pointsData[pointDataIndex] || {}; let pointName = pointData.name; if (!pointName && pointData.attributes) { pointName = pointData.attributes.Название || Object.values(pointData.attributes)[0] || `К${contourIndex + 1}-Т${index + 1}`; } else { pointName = pointName || `К${contourIndex + 1}-Т${index + 1}`; } const placemark = new ymaps.Placemark(yandexCoords, { balloonContent: this.createVertexBalloon(pointName, yandexCoords, pointData, properties), hintContent: pointName }, this.getVerticesStyle(geometryType)); verticesCollection.add(placemark); }); return verticesCollection; } // Создание мультиточек createMultiPoint(coords, properties, style) { const collection = new ymaps.GeoObjectCollection({}, { balloonContent: this.createBalloonContent(properties), hintContent: properties.name || `Группа точек (${properties.pointsCount || coords.length})` }); const pointsData = properties.pointsData || []; coords.forEach((pointCoords, index) => { const pointProps = pointsData[index] || {}; const pointName = pointProps.name || `Точка ${index + 1}`; const placemark = new ymaps.Placemark(pointCoords, { balloonContent: this.createSinglePointBalloon(pointName, pointCoords, pointProps, properties), hintContent: pointName }, style); // ★★★ ОБРАБОТЧИК ОТКРЫТИЯ БАЛУНА → ОТКРЫВАЕМ ПАНЕЛЬ ★★★ placemark.events.add('balloonopen', () => { if (window.app && window.app.sidebarManager) { window.app.sidebarManager.showObjectDetails({ mapObject: placemark, feature: { type: 'Feature', geometry: { type: 'Point', coordinates: [pointCoords[1], pointCoords[0]] }, properties: { ...properties, name: pointName } }, pointData: pointProps, coordinates: pointCoords }); } }); // ★★★ ОБРАБОТЧИК ЗАКРЫТИЯ БАЛУНА → ЗАКРЫВАЕМ ПАНЕЛЬ ★★★ placemark.events.add('balloonclose', () => { if (window.app && window.app.sidebarManager && window.app.sidebarManager.currentObject && window.app.sidebarManager.currentObject.mapObject === placemark) { window.app.sidebarManager.hideObjectDetails(); } }); collection.add(placemark); }); return collection; } // Создание мультилиний createMultiLineString(coords, properties, style) { const collection = new ymaps.GeoObjectCollection({}, { balloonContent: this.createBalloonContent(properties), hintContent: properties.name || `Группа линий (${coords.length} линий)` }); coords.forEach((lineCoords, index) => { const yandexCoords = lineCoords.map(coord => CoordUtils.geoJsonToYandex(coord)); const polyline = new ymaps.Polyline(yandexCoords, { balloonContent: `${properties.name || 'Линия'} - Контур ${index + 1}` }, style); // ★★★ ДОБАВИТЬ ЭТИ ОБРАБОТЧИКИ ДЛЯ КАЖДОЙ POLYLINE ★★★ // Обработчик открытия балуна → открываем панель polyline.events.add('balloonopen', () => { if (window.app && window.app.sidebarManager) { window.app.sidebarManager.showObjectDetails({ mapObject: polyline, feature: { type: 'Feature', geometry: { type: 'MultiLineString', coordinates: coords }, properties: properties }, contourIndex: index }); } }); // Обработчик закрытия балуна → закрываем панель polyline.events.add('balloonclose', () => { if (window.app && window.app.sidebarManager && window.app.sidebarManager.currentObject && window.app.sidebarManager.currentObject.mapObject === polyline) { window.app.sidebarManager.hideObjectDetails(); } }); collection.add(polyline); }); return collection; } // Создание мультиполигонов createMultiPolygon(coords, properties, style) { const collection = new ymaps.GeoObjectCollection({}, { balloonContent: this.createBalloonContent(properties), hintContent: properties.name || `Группа полигонов (${coords.length} полигонов)` }); coords.forEach((polygonCoords, index) => { const yandexCoords = polygonCoords[0].map(coord => CoordUtils.geoJsonToYandex(coord)); const polygon = new ymaps.Polygon([yandexCoords], { balloonContent: `${properties.name || 'Полигон'} - Контур ${index + 1}` }, style); // Обработчик открытия балуна → открываем панель polygon.events.add('balloonopen', () => { if (window.app && window.app.sidebarManager) { window.app.sidebarManager.showObjectDetails({ mapObject: polygon, feature: { type: 'Feature', geometry: { type: 'MultiPolygon', coordinates: coords }, properties: properties }, contourIndex: index }); } }); // Обработчик закрытия балуна → закрываем панель polygon.events.add('balloonclose', () => { if (window.app && window.app.sidebarManager && window.app.sidebarManager.currentObject && window.app.sidebarManager.currentObject.mapObject === polygon) { window.app.sidebarManager.hideObjectDetails(); } }); collection.add(polygon); }); return collection; } // ★★★ НОВЫЙ МЕТОД: Обработчик клика по объекту ★★★ handleObjectClick(mapObject, objectData) { // Проверяем, доступен ли SidebarManager if (window.app && window.app.sidebarManager) { // Передаем данные в сайдбар для отображения window.app.sidebarManager.showObjectDetails({ mapObject: mapObject, feature: objectData.feature, pointData: objectData.pointData || {}, coordinates: objectData.coordinates, contourIndex: objectData.contourIndex }); } } // Создание контента балуна для объектов createBalloonContent(properties) { if (!properties) return 'Нет данных'; let content = `
`; content += `

${properties.name || 'Без названия'}

`; if (properties.description) { content += `

${properties.description}

`; } if (properties.pointsCount) { content += `

Всего точек: ${properties.pointsCount}

`; } if (properties.category) { content += `

Категория: ${properties.category}

`; } if (properties.dateCreated) { content += `

Дата: ${properties.dateCreated}

`; } if (properties.album) { content += `

📷 Фотоальбом

`; } if (properties.status) { content += `

Статус: ${properties.status}

`; } content += `
`; return content; } // Создание балуна для вершины createVertexBalloon(pointName, coordinates, pointData, parentProperties) { const lat = coordinates[0]; const lon = coordinates[1]; let attributesContent = ''; if (pointData.attributes) { attributesContent = `
Атрибуты точки:
`; } return `

${pointName}

Объект: ${parentProperties.name || 'Без названия'}

Координаты:
Ш: ${lat.toFixed(6)}
Д: ${lon.toFixed(6)}

${attributesContent}
`; } // Создание балуна для одиночной точки createSinglePointBalloon(pointName, coordinates, pointData, groupProperties) { const lat = coordinates[0]; const lon = coordinates[1]; let attributesContent = ''; if (pointData.attributes) { attributesContent = `
Атрибуты точки:
`; } return `

${pointName}

Группа: ${groupProperties.name || 'Без названия'}

Координаты:
Ш: ${lat.toFixed(6)}
Д: ${lon.toFixed(6)}

${pointData.description ? `

${pointData.description}

` : ''} ${attributesContent}
`; } } // Глобальная функция для копирования координат function copyCoordinates(lat, lon) { const coordString = `${lat.toFixed(6)}, ${lon.toFixed(6)}`; navigator.clipboard.writeText(coordString).then(() => { console.log('Координаты скопированы: ' + coordString); }).catch(err => { console.error('Ошибка копирования: ', err); }); }