const Territory_editor = { info: {}, // Ініціалізація редактора за типом об'єкта та його ID async init(type, id) { let html = await fetch('/lib/pages/territory/editor/index.html').then((response) => response.text()); app.innerHTML = html; // Очищення груп та карти map = ""; houseGroup = ""; homesteadGroup = ""; buildingGroup = ""; pointsGroup = ""; numApartments = 1; this.info.id = Number(id); this.info.type = type; this.setHTML(type, id); }, // Отримання даних з API з авторизацією через UUID async loadAPI(URL) { let uuid = localStorage.getItem("uuid"); return await fetch(URL, { method: 'GET', headers: { "Content-Type": "application/json", "Authorization": uuid } }).then((response) => response.json()); }, // Встановлення HTML-контенту для редактора залежно від типу об'єкта async setHTML(type, id) { let list = await Territory_editor.loadAPI(`${CONFIG.api}${type}/${id}`); console.log(list); // Заповнення даних об'єкта let info_title = document.getElementById("info-title"); let info_number = document.getElementById("info-number"); let info_settlement = document.getElementById("info-settlement"); let info_osm = document.getElementById("info-osm"); this.info.points = list.points; this.info.points_number = list.points_number ?? []; this.info.geo = list.geo; this.info.osm_id = list.osm_id; this.info.zoom = list.zoom; this.info.title = list.title; this.info.number = list.number; this.info.settlement = list.settlement; this.info.description = list.description ?? null; info_title.value = this.info.title; info_number.value = this.info.number; info_settlement.value = this.info.settlement; info_osm.value = this.info.osm_id; this.osm.init(); switch (type) { case 'points': this.points.init(); break; case 'homestead': this.homestead.init(); break; case 'house': this.house.init(); break; } }, points: { init() { } }, homestead: { init() { const part_3 = document.getElementById('part-3'); const title = part_3.querySelector('h1'); part_3.innerHTML = ''; title.innerHTML = `Крок 3. Створення будинків`; part_3.appendChild(title); part_3.innerHTML += `

*Натисніть кнопку нижче, а потім клацайте на карті, щоб додати будинки. Після цього натисніть "Зберегти".

*Щоб видалити будинок, клацніть на ньому та у спливаючому вікні оберіть "Видалити".


`; part_3.style.display = ""; this.building.init(); }, building: { list: [], editing: false, async init() { this.editing = false; setLeafletCursor('pointer'); this.list = await Territory_editor.loadAPI(`${CONFIG.api}building/${Territory_editor.info.id}`); // Обробник кліку на карту homesteadGroup.on('click', e => { console.log(this.editing); if (e.layer instanceof L.Marker || !this.editing) return; const { lat, lng } = e.latlng; console.log(`Координати: ${lat.toFixed(5)}, ${lng.toFixed(5)}`); setLeafletCursor('progress'); this.editing = false; this.addBuilding({ geo: e.latlng, title: this.list.length + 1 }); }); for (const element of this.list) { // Додаємо маркер на карту const redDot = L.divIcon({ className: "leaflet_drop", html: `
`, iconSize: [16, 16], iconAnchor: [8, 8] }); L.marker(element.geo, { icon: redDot }) .addTo(buildingGroup) .bindPopup(` Точка: ${element.id}
Координати: ${element.geo.lat.toFixed(5)}, ${element.geo.lng.toFixed(5)}
`); } }, async addBuilding({ geo, title }) { this.list.push({ title: title, geo: geo }); const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}building/${Territory_editor.info.id}`; try { const response = await fetch(URL, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": uuid }, body: JSON.stringify({ title, geo }) }); const data = await response.json(); console.log(data); // Додаємо маркер на карту const redDot = L.divIcon({ className: "leaflet_drop", html: `
`, iconSize: [16, 16], iconAnchor: [8, 8] }); const marker = L.marker(geo, { icon: redDot }).addTo(buildingGroup); marker.bindPopup(` Точка: ${data.id}
Координати: ${geo.lat.toFixed(5)}, ${geo.lng.toFixed(5)}
`); setLeafletCursor('crosshair'); this.editing = true; } catch (err) { console.error("Помилка при додаванні будівлі:", err); } }, async delleteBuilding({ id }) { const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}building/${id}`; try { const response = await fetch(URL, { method: 'DELETE', headers: { "Content-Type": "application/json", "Authorization": uuid } }); const data = await response.json(); // Видаляємо елемент списку та маркер const el = document.getElementById(`redDot_${id}`); if (el) el.remove(); const block = document.getElementById(`Building_${id}`); if (block) block.remove(); buildingGroup.eachLayer(layer => { if (layer instanceof L.Marker && layer.getPopup()?.getContent().includes(`Точка: ${id}`)) { buildingGroup.removeLayer(layer); } }); } catch (err) { console.error("Помилка при видаленні будівлі:", err); } }, newHouse(element) { const btn = element; this.editing = !this.editing; setLeafletCursor(this.editing ? 'crosshair' : 'pointer'); btn.innerHTML = this.editing ? ` Завершити додавання` : ` Додати будинок`; if (this.editing) alert("Натискаючи на карту будуть створюватись нові точки (будинки)"); } } }, house: { init() { const part_3 = document.getElementById('part-3'); const title = part_3.querySelector('h1'); part_3.innerHTML = ''; title.innerHTML = `Крок 3. Конструктор квартир`; part_3.appendChild(title); part_3.innerHTML += `
`; part_3.style.display = ""; this.entrances.setHTML(Territory_editor.info.id); }, entrances: { list: [], async setHTML(id) { this.list = await Territory_editor.loadAPI(`${CONFIG.api}house/${id}/entrances`); const houseDiv = document.getElementById('house'); if (!houseDiv) return; houseDiv.innerHTML = ""; const newBtn = document.createElement('button'); newBtn.className = "entrance-button"; newBtn.type = "button"; newBtn.title = "Додати під'їзд"; newBtn.innerHTML = ``; newBtn.onclick = () => Territory_editor.house.entrances.addEntrance(); houseDiv.appendChild(newBtn); for (const element of this.list) { // Блок entrance const entranceDiv = document.createElement('div'); entranceDiv.className = "entrance"; entranceDiv.id = `entrance-${element.id}`; const input = document.createElement('input'); input.value = element.title; input.type = "text" input.onchange = () => Territory_editor.house.entrances.editEntrance(element.id, input.value); const delBtn = document.createElement('button'); delBtn.type = "button"; delBtn.title = "Видалити під'їзд"; delBtn.innerHTML = ``; delBtn.onclick = () => Territory_editor.house.entrances.deleteEntrance(element.id); const headerDiv = document.createElement('div'); headerDiv.className = "entrance-header"; headerDiv.append(input, delBtn); const addBtn = document.createElement('button'); addBtn.className = "addFloors"; addBtn.type = "button"; addBtn.title = "Додати поверх"; addBtn.innerHTML = ``; addBtn.onclick = () => Territory_editor.house.apartments.addFloors(element.id); const infoDiv = document.createElement('div'); infoDiv.className = "entrance-info"; infoDiv.append(headerDiv, addBtn); entranceDiv.append(infoDiv); houseDiv.insertBefore(entranceDiv, houseDiv.querySelector(".entrance-button")); // Завантажуємо квартири для ентрансів Territory_editor.house.apartments.setHTML(element.id); } }, async editEntrance(floor, value) { const pos = this.list.findIndex(e => e.id === Number(floor)); if (pos === -1) return; this.list[pos].title = value; const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}house/${Territory_editor.info.id}/entrances`; try { const response = await fetch(URL, { method: 'PUT', headers: { "Content-Type": "application/json", "Authorization": uuid }, body: JSON.stringify({ id: this.list[pos].id, title: this.list[pos].title, description: this.list[pos].description }) }); const data = await response.json(); console.log(data); } catch (err) { console.error("Помилка при редагуванні під'їзду:", err); } }, async addEntrance() { console.log('addEntrance'); const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}house/${Territory_editor.info.id}/entrances`; try { const response = await fetch(URL, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": uuid }, body: JSON.stringify({ house_id: Territory_editor.info.id, entrance_number: this.list.length, title: `Під'їзд ${this.list.length + 1}`, description: null }) }); const data = await response.json(); console.log(data); let element = { id: data.id, house_id: Territory_editor.info.id, entrance_number: this.list.length, title: `Під'їзд ${this.list.length + 1}`, description: null } this.list.push(element); Territory_editor.house.apartments.list[element.id] = [] console.log(this.list); const houseDiv = document.getElementById('house'); const entranceDiv = document.createElement('div'); entranceDiv.className = "entrance"; entranceDiv.id = `entrance-${element.id}`; const input = document.createElement('input'); input.value = element.title; input.type = "text" input.onchange = () => Territory_editor.house.entrances.editEntrance(element.id, input.value); const delBtn = document.createElement('button'); delBtn.type = "button"; delBtn.title = "Видалити під'їзд"; delBtn.innerHTML = ``; delBtn.onclick = () => Territory_editor.house.entrances.deleteEntrance(element.id); const headerDiv = document.createElement('div'); headerDiv.className = "entrance-header"; headerDiv.append(input, delBtn); const addBtn = document.createElement('button'); addBtn.className = "addFloors"; addBtn.type = "button"; addBtn.title = "Додати поверх"; addBtn.innerHTML = ``; addBtn.onclick = () => Territory_editor.house.apartments.addFloors(element.id); const infoDiv = document.createElement('div'); infoDiv.className = "entrance-info"; infoDiv.append(headerDiv, addBtn); entranceDiv.append(infoDiv); houseDiv.insertBefore(entranceDiv, houseDiv.querySelector(".entrance-button")); } catch (err) { console.error("Помилка при створенні під'їзду:", err); } }, async deleteEntrance(entrance) { console.log(entrance); if (Territory_editor.house.apartments.list[entrance].length == 0) { console.log("OK"); const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}house/${Territory_editor.info.id}/entrances`; try { const response = await fetch(URL, { method: 'DELETE', headers: { "Content-Type": "application/json", "Authorization": uuid }, body: JSON.stringify({ id: entrance }) }); const data = await response.json(); console.log(data); const index = this.list.findIndex(item => item.id === entrance); if (index !== -1) this.list.splice(index, 1); delete Territory_editor.house.apartments.list[entrance]; document.getElementById(`entrance-${entrance}`).remove(); } catch (err) { console.error("Помилка при видаленні під'їзду:", err); } } else { alert("Для видалення під'їзду спочатку видаліть всі квартири з нього"); } } }, apartments: { list: {}, async setHTML(id) { this.list[id] = await Territory_editor.loadAPI(`${CONFIG.api}apartments/${id}`); const entranceDiv = document.getElementById(`entrance-${id}`); if (!entranceDiv) return; // Унікальні поверхи const uniqueFloors = [...new Set(this.list[id].map(a => a.floors_number))].sort((a, b) => a - b); // Створюємо блоки поверхів for (const num of uniqueFloors) { const floorDiv = document.createElement('div'); floorDiv.className = "floor"; floorDiv.id = `floor-${id}-${num}`; const h2 = document.createElement('h2'); h2.textContent = `Поверх ${num}`; floorDiv.appendChild(h2); const addBtn = document.createElement('button'); addBtn.id = `buttonApartment-${id}-${num}`; addBtn.type = "button"; addBtn.title = "Додати квартиру"; addBtn.innerHTML = ``; addBtn.onclick = () => Territory_editor.house.apartments.addApartment(id, num); const infoDiv = document.createElement('div'); infoDiv.className = "floor-info"; infoDiv.append(h2, addBtn); floorDiv.appendChild(infoDiv); entranceDiv.insertBefore(floorDiv, entranceDiv.querySelector(".floor")); } // Сортуємо квартири за назвою this.list[id].sort((a, b) => b.title - a.title); // Створюємо блоки квартир for (const apartment of this.list[id]) { const floorDiv = document.getElementById(`floor-${id}-${apartment.floors_number}`); if (!floorDiv) continue; const apartmentDiv = document.createElement('div'); apartmentDiv.className = "apartment"; apartmentDiv.id = `apartment-${id}-${apartment.id}`; const input = document.createElement('input'); input.type = "text"; input.value = apartment.title; input.id = `apartment-input-${id}-${apartment.id}`; input.onchange = () => Territory_editor.house.apartments.editApartment(id, apartment.id); const delBtn = document.createElement('button'); delBtn.type = "button"; delBtn.title = "Видалити квартиру"; delBtn.innerHTML = ``; delBtn.onclick = () => Territory_editor.house.apartments.deleteApartment(id, apartment.id); apartmentDiv.append(input, delBtn); floorDiv.prepend(apartmentDiv); numApartments++; } const nextApartmentTitle = document.getElementById('next-apartment-title'); if (nextApartmentTitle) nextApartmentTitle.value = numApartments; }, async addFloors(entrance) { console.log(entrance); const entranceBlock = document.getElementById(`entrance-${entrance}`); const uniqueFloors = [...new Set(this.list[entrance].map(obj => obj.floors_number))]; console.log(uniqueFloors); const newFloors = uniqueFloors.length + 1; const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}/apartments/${entrance}`; try { const response = await fetch(URL, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": uuid }, body: JSON.stringify({ apartment_number: this.list[entrance].length, title: numApartments.toString(), floors_number: newFloors }) }); const data = await response.json(); console.log(data); // Створюємо блок поверху const floorDiv = document.createElement('div'); floorDiv.className = "floor"; floorDiv.id = `floor-${entrance}-${newFloors}`; // Заголовок поверху const h2 = document.createElement('h2'); h2.textContent = `Поверх ${newFloors}`; // Кнопка додати квартиру const addBtn = document.createElement('button'); addBtn.className = "addApartment"; addBtn.id = `buttonApartment-${entrance}-${newFloors}`; addBtn.title = "Додати квартиру"; addBtn.type = "button"; addBtn.onclick = () => Territory_editor.house.apartments.addApartment(entrance, newFloors); addBtn.innerHTML = ``; const infoDiv = document.createElement('div'); infoDiv.className = "floor-info"; infoDiv.append(h2, addBtn); // Блок квартири const apartmentBlock = document.createElement('div'); apartmentBlock.className = "apartment"; apartmentBlock.id = `apartment-${entrance}-${data.id}`; const input = document.createElement('input'); input.type = "text"; input.value = numApartments; input.id = `apartment-input-${entrance}-${data.id}`; input.onchange = () => Territory_editor.house.apartments.editApartment(entrance, data.id); const delBtn = document.createElement('button'); delBtn.type = "button"; delBtn.title = "Видалити квартиру"; delBtn.innerHTML = ``; delBtn.onclick = () => Territory_editor.house.apartments.deleteApartment(entrance, data.id); apartmentBlock.append(input, delBtn); floorDiv.appendChild(apartmentBlock); floorDiv.appendChild(infoDiv); entranceBlock.insertBefore(floorDiv, entranceBlock.querySelector(".floor")); // Оновлюємо список квартир this.list[entrance].push({ id: data.id, entrance_id: Number(entrance), apartment_number: this.list[entrance].length, title: numApartments.toString(), floors_number: newFloors }); numApartments++; const nextApartmentTitle = document.getElementById('next-apartment-title'); if (nextApartmentTitle) nextApartmentTitle.value = numApartments; } catch (err) { console.error("Помилка при додаванні поверху:", err); } }, async addApartment(entrance, floor) { const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}/apartments/${entrance}`; try { const response = await fetch(URL, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": uuid }, body: JSON.stringify({ apartment_number: this.list[entrance].length, title: numApartments.toString(), floors_number: Number(floor) }) }); const data = await response.json(); console.log(data); // Оновлюємо список квартир this.list[entrance].push({ id: data.id, entrance_id: Number(entrance), apartment_number: this.list[entrance].length, title: numApartments.toString(), floors_number: Number(floor) }); const floorDiv = document.getElementById(`floor-${entrance}-${floor}`); // Створюємо блок нової квартири const apartmentDiv = document.createElement('div'); apartmentDiv.className = "apartment"; apartmentDiv.id = `apartment-${entrance}-${data.id}`; const input = document.createElement('input'); input.type = "text"; input.value = numApartments; input.id = `apartment-input-${entrance}-${data.id}`; input.onchange = () => Territory_editor.house.apartments.editApartment(entrance, data.id); const delBtn = document.createElement('button'); delBtn.type = "button"; delBtn.title = "Видалити квартиру"; delBtn.innerHTML = ``; delBtn.onclick = () => Territory_editor.house.apartments.deleteApartment(entrance, data.id); apartmentDiv.append(input, delBtn); floorDiv.insertBefore(apartmentDiv, floorDiv.querySelector(".floor-info")); numApartments++; const nextApartmentTitle = document.getElementById('next-apartment-title'); if (nextApartmentTitle) nextApartmentTitle.value = numApartments; } catch (err) { console.error("Помилка при додаванні квартири:", err); } }, async editApartment(entrance, apartment) { console.log(entrance, apartment); const input = document.getElementById(`apartment-input-${entrance}-${apartment}`); if (!input) return; const newTitle = input.value; // Оновлюємо локальний список квартир const pos = this.list[entrance].findIndex(e => e.id === Number(apartment)); if (pos === -1) return; this.list[entrance][pos].title = newTitle; const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}/apartments/${entrance}`; try { const response = await fetch(URL, { method: 'PUT', headers: { "Content-Type": "application/json", "Authorization": uuid }, body: JSON.stringify({ id: this.list[entrance][pos].id, title: this.list[entrance][pos].title, status: this.list[entrance][pos].status, description: this.list[entrance][pos].description }) }); const data = await response.json(); console.log(data); } catch (err) { console.error("Помилка при редагуванні квартири:", err); } }, async deleteApartment(entrance, apartment) { const pos = this.list[entrance].findIndex(e => e.id === Number(apartment)); if (pos === -1) return; const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}/apartments/${entrance}`; try { const response = await fetch(URL, { method: 'DELETE', headers: { "Content-Type": "application/json", "Authorization": uuid }, body: JSON.stringify({ id: this.list[entrance][pos].id }) }); const data = await response.json(); console.log(data); // Видаляємо елемент з DOM const apartmentBlock = document.getElementById(`apartment-${entrance}-${apartment}`); if (apartmentBlock) apartmentBlock.remove(); // Оновлюємо локальний список this.list[entrance].splice(pos, 1); // Оновлюємо номер наступної квартири numApartments = Math.max(0, numApartments - 1); const nextApartmentTitle = document.getElementById('next-apartment-title'); if (nextApartmentTitle) nextApartmentTitle.value = numApartments; } catch (err) { console.error("Помилка при видаленні квартири:", err); } }, editNum(el) { numApartments = Number(el.value) }, }, }, osm: { init() { const center = Territory_editor.info.geo; const zoom = Territory_editor.info.zoom; const googleHybrid = L.tileLayer('http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', { subdomains: ['mt0', 'mt1', 'mt2', 'mt3'] }); const osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'); const mytile = L.tileLayer('https://sheep-service.com/map/{z}/{x}/{y}.webp', { maxZoom: 20, minZoom: 15, tms: true }); if (!map) { houseGroup = new L.FeatureGroup(); homesteadGroup = new L.FeatureGroup(); buildingGroup = new L.FeatureGroup(); pointsGroup = new L.FeatureGroup(); map = L.map('map', { renderer: L.canvas(), center, zoom, layers: [googleHybrid, osm, mytile, houseGroup, homesteadGroup, buildingGroup, pointsGroup], zoomControl: false }); L.control.layers( { "Google Hybrid": googleHybrid, "OpenStreetMap": osm, "Territory Map": mytile }, { "Багатоповерхові будинки": houseGroup, "Житлові райони": homesteadGroup, "Приватні будинки": buildingGroup, "Точки на карті": pointsGroup }, { position: 'bottomright' } ).addTo(map); map.pm.addControls({ position: 'bottomright', drawCircleMarker: false, drawPolyline: false, drawPolygon: false, drawRectangle: false, drawCircle: false, drawText: false, drawMarker: false, cutPolygon: false, tooltips: false, editMode: true, dragMode: true, }); map.pm.toggleControls() // Событие после завершения рисования map.on('pm:create', e => { const layer = e.layer; let LatLngs = layer.getLatLngs(); LatLngs[0].push(LatLngs[0][0]); Territory_editor.info.points.push(LatLngs); let geo = this.center(layer.getLatLngs()); const house = layer; // сохраняем именно слой if (Territory_editor.info.type === 'house') { houseGroup.addLayer(house); Territory_editor.info.points_number.push(this.center(layer.getLatLngs())); } else if (Territory_editor.info.type === 'homestead') { homesteadGroup.addLayer(house); } house.bindPopup(` Координати: ${geo.lat.toFixed(5)}, ${geo.lng.toFixed(5)}
`); // при открытии popup вешаем обработчик удаления house.on('popupopen', (e) => { if (Territory_editor.homestead.building.editing) { house.closePopup(); return; } const btn = e.popup.getElement().querySelector('.map_dell'); if (btn) { btn.addEventListener('click', () => { Territory_editor.osm.delete(house); }); } }); Territory_editor.osm.autoZoom(Territory_editor.info.points); }); map.pm.setLang("ua"); } houseGroup.clearLayers(); homesteadGroup.clearLayers(); buildingGroup.clearLayers(); pointsGroup.clearLayers(); for (let i = 0; i < Territory_editor.info.points.length; i++) { const LatLngs = Territory_editor.info.points[i]; // Создаем L.polygon const polyOptions = Territory_editor.info.type === 'homestead' ? { color: "#f2bd53", fillColor: "#f2bd53", fillOpacity: 0.4, dashArray: '5,10' } : { color: "#585858", fillColor: "#f2bd53", fillOpacity: 0.8 }; const house = L.polygon(LatLngs, polyOptions); // Добавляем в нужную группу if (Territory_editor.info.type === 'house') { houseGroup.addLayer(house); } else if (Territory_editor.info.type === 'homestead') { homesteadGroup.addLayer(house); } house.bindPopup(` Координати: ${Territory_editor.info.geo.lat.toFixed(5)}, ${Territory_editor.info.geo.lng.toFixed(5)}
`); // при открытии popup вешаем обработчик удаления house.on('popupopen', (e) => { if (Territory_editor.homestead.building.editing) { house.closePopup(); return; } const btn = e.popup.getElement().querySelector('.map_dell'); if (btn) { btn.addEventListener('click', () => { Territory_editor.osm.delete(house); }); } }); Territory_editor.osm.autoZoom(Territory_editor.info.points); } }, newPoligon() { if (Territory_editor.info.type === 'house') { map.pm.enableDraw('Polygon', { snappable: true, snapDistance: 20, layerGroup: houseGroup, templineStyle: { color: '#585858', radius: 500, fillOpacity: 0.4, dashArray: '5, 10', dashOffset: '20', }, hintlineStyle: { color: '#C14D4D', dashArray: '5, 10' }, pathOptions: { color: "#585858", fillColor: "#f2bd53", fillOpacity: 0.8 } }); } else if (Territory_editor.info.type === 'homestead') { map.pm.enableDraw('Polygon', { snappable: true, snapDistance: 20, layerGroup: houseGroup, templineStyle: { color: '#585858', radius: 500, fillOpacity: 0.3, dashArray: '5, 10', dashOffset: '20', }, hintlineStyle: { color: '#C14D4D', dashArray: '5, 10' }, pathOptions: { color: "#f2bd53", fillColor: "#f2bd53", radius: 500, fillOpacity: 0.3, dashArray: '5, 10' } }); } }, async autoPoligon(IDs) { if (!IDs) return; const ids_list = IDs.replace(/\s+/g, "").split(','); Territory_editor.info.osm_id = ids_list; houseGroup.clearLayers(); homesteadGroup.clearLayers(); Territory_editor.info.points = []; Territory_editor.info.points_number = []; Territory_editor.info.geo = {} // 1006306041, 1006306065 for (let i = 0; i < ids_list.length; i++) { const element = await Territory_editor.osm.getOSM(Territory_editor.info.osm_id[i]); // Преобразуем координаты в LatLng const LatLngs = [[]]; element[0].forEach(feature => LatLngs[0].push({ lat: feature.lat, lng: feature.lng })); // Замыкаем полигон // if (LatLngs[0][0] && LatLngs[0][0] !== LatLngs[0][LatLngs[0].length - 1]) { // LatLngs[0].push(LatLngs[0][0]); // } // Считаем центр const center = this.center(LatLngs); // Сохраняем в points / points_number Territory_editor.info.points.push(LatLngs); Territory_editor.info.points_number.push(center); // Создаем L.polygon const polyOptions = Territory_editor.info.type === 'homestead' ? { color: "#f2bd53", fillColor: "#f2bd53", fillOpacity: 0.4, dashArray: '5,10' } : { color: "#585858", fillColor: "#f2bd53", fillOpacity: 0.8 }; const house = L.polygon(LatLngs, polyOptions); // Добавляем в нужную группу if (Territory_editor.info.type === 'house') { houseGroup.addLayer(house); } else if (Territory_editor.info.type === 'homestead') { homesteadGroup.addLayer(house); } // Bind popup с кнопкой удаления house.bindPopup(` Координати: ${center.lat.toFixed(5)}, ${center.lng.toFixed(5)}
`); house.on('popupopen', (e) => { if (Territory_editor.homestead.building.editing) { house.closePopup(); return; } const btn = e.popup.getElement().querySelector('.map_dell'); if (btn) { btn.addEventListener('click', () => { Territory_editor.osm.delete(house); }); } }); } Territory_editor.osm.autoZoom(Territory_editor.info.points); }, center(geo) { // Получаем координаты полигона Leaflet let latlngs = geo[0]; // Преобразуем в формат GeoJSON для Turf const coordinates = latlngs.map(ll => [ll.lng, ll.lat]); const polygonGeoJSON = { type: "Feature", geometry: { type: "Polygon", coordinates: [coordinates] } }; // Находим центроид const centroid = turf.centroid(polygonGeoJSON); latlngs = { lat: centroid.geometry.coordinates[1], lng: centroid.geometry.coordinates[0] } return latlngs; }, autoZoom(polygons) { if (!polygons || !polygons.length) return; const allBounds = []; polygons.forEach(polygon => { const ring = polygon[0]; if (!ring || ring.length < 3) return; const coords = ring.map(p => [p.lng, p.lat]); if (coords[0][0] !== coords[coords.length - 1][0] || coords[0][1] !== coords[coords.length - 1][1]) { coords.push(coords[0]); } const polygonGeoJSON = turf.polygon([coords]); const bbox = turf.bbox(polygonGeoJSON); const bounds = L.latLngBounds( [bbox[1], bbox[0]], [bbox[3], bbox[2]] ); allBounds.push(bounds); }); if (!allBounds.length) return; // Если один полигон, просто fitBounds на него if (allBounds.length === 1) { map.fitBounds(allBounds[0]); } else { // Несколько полигонов → объединяем bounds let finalBounds = allBounds[0]; for (let i = 1; i < allBounds.length; i++) { finalBounds = finalBounds.extend(allBounds[i]); } map.fitBounds(finalBounds); } if (map.getZoom() > 18) map.setZoom(18); setTimeout(() => { Territory_editor.info.zoom = map.getZoom(); Territory_editor.info.geo = map.getCenter(); }, 200) }, delete(house) { // убрать слой с карты if (Territory_editor.info.type === 'house') { houseGroup.removeLayer(house); } else if (Territory_editor.info.type === 'homestead') { homesteadGroup.removeLayer(house); } // найти индекс полигона в points const target_1 = house.getLatLngs(); // вершины полигона const target_2 = house.getLatLngs(); target_1[0].push(target_1[0][0]) const index = Territory_editor.info.points.findIndex( poly => { if(JSON.stringify(poly[0]) === JSON.stringify(target_1[0])) return true else if(JSON.stringify(poly[0]) === JSON.stringify(target_2[0])) return true return false } ); console.log("index ", index); console.log(Territory_editor.info.points); if (index !== -1) { // удалить из points и points_number по индексу Territory_editor.info.points.splice(index, 1); Territory_editor.info.points_number.splice(index, 1); } Territory_editor.osm.autoZoom(Territory_editor.info.points); }, async getOSM(wayId) { const overpassUrl = `https://overpass-api.de/api/interpreter?data=[out:json];way(${wayId});(._;>;);out;`; return await fetch(overpassUrl) .then(response => response.json()) .then(data => { const nodes = new Map(); data.elements.forEach(el => { if (el.type === "node") { nodes.set(el.id, { lat: el.lat, lng: el.lon }); } }); const way = data.elements.find(el => el.type === "way"); if (way) { const coordinates = way.nodes.map(nodeId => nodes.get(nodeId)); return [coordinates]; } else { console.error("Way не найден!"); } }) .catch(error => console.error("Ошибка запроса:", error)); }, }, async save() { console.log(Territory_editor.info); setLeafletCursor('pointer'); Territory_editor.homestead.building.editing = false; const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}${Territory_editor.info.type}/${Territory_editor.info.id}`; try { const response = await fetch(URL, { method: 'PUT', headers: { "Content-Type": "application/json", "Authorization": uuid }, body: JSON.stringify(Territory_editor.info) }); const data = await response.json(); console.log(data); } catch (err) { console.error("Помилка при редагуванні під'їзду:", err); } } }