Files
Sheep-Service/web/lib/pages/constructor/script.js

909 lines
39 KiB
JavaScript
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.
let map, houseGroup, homesteadGroup, buildingGroup, pointsGroup;
let numApartments = 1;
let mode = '';
const Constructor = {
info: {},
async init() {
let html = await fetch('/lib/pages/constructor/index.html').then((response) => response.text());
app.innerHTML = html;
map = "";
houseGroup = "";
homesteadGroup = "";
buildingGroup = "";
pointsGroup = "";
numApartments = 1;
this.info = {
type: 'house',
points: [],
points_number: [],
geo: {},
osm_id: [],
zoom: 17,
title: null,
number: null,
settlement: null
}
const infoTypeInputs = document.querySelectorAll('input[name="info-type"]');
const infoForm = document.getElementById('info-form');
const infoLabels = {
points: {
title: "Назва",
number: "Номер",
settlement: "Місто",
init: () => Constructor.points.init(),
next: () => Constructor.points.next(),
save: () => Constructor.points.save()
},
homestead: {
title: "Назва району / села",
number: "Номер району",
settlement: "Місто",
init: () => Constructor.homestead.init(),
next: () => Constructor.homestead.next(),
save: () => Constructor.homestead.save()
},
house: {
title: "Назва вулиці",
number: "Номер будинку",
settlement: "Місто",
init: () => Constructor.house.init(),
next: () => Constructor.house.next(),
save: () => Constructor.house.save()
}
};
let currentInit = infoLabels['house'].init;
let currentNext = infoLabels['house'].next;
let currentSave = infoLabels['house'].save;
function renderInfoForm(type) {
const labels = infoLabels[type];
currentInit = labels.init;
currentNext = labels.next;
currentSave = labels.save;
infoForm.innerHTML = `
<div>
<label for="info-title">${labels.title}</label>
<input type="text" id="info-title" name="address" required value=""/>
</div>
<div>
<label for="info-number">${labels.number}</label>
<input type="text" id="info-number" name="number" required value=""/>
</div>
<div>
<label for="info-settlementv">${labels.settlement}</label>
<input type="text" id="info-settlement" name="settlement" required value="Тернопіль"/>
</div>
<button type="submit" id="part-1-button">Далі</button>
`;
}
// Обработчик submit формы
infoForm.addEventListener('submit', (event) => {
event.preventDefault();
document.getElementById('part-1-button').style.display = "none";
['title', 'number', 'settlement'].forEach(key => {
Constructor.info[key] = document.getElementById(`info-${key}`).value;
});
if (currentInit) currentInit();
});
// Слушатели радиокнопок
infoTypeInputs.forEach(radio => {
radio.addEventListener('change', event => {
const value = event.target.value;
const part_2 = document.getElementById('part-2');
const part_2_button = document.getElementById('part-2-button');
part_2.style.display = "none";
part_2_button.style.display = "";
const part_3 = document.getElementById('part-3');
part_3.style.display = "none";
renderInfoForm(value);
console.log(`Вибрано: ${value}`);
this.info.type = value;
this.info.osm_id = null;
this.info.geo = {};
this.info.points_number = [];
this.info.points = [];
this.house.apartments.quantity = 0;
this.house.apartments.list = [];
});
});
document.getElementById('part-2-button').addEventListener('click', (event) => {
event.preventDefault();
document.getElementById('part-2-button').style.display = "none";
if (currentNext) currentNext();
});
document.getElementById('part-3-button').addEventListener('click', (event) => {
event.preventDefault();
// document.getElementById('part-3-button').style.display = "none";
if (currentSave) currentSave();
});
},
points: {
init() {
console.log('points');
// const part_2 = document.getElementById('part-2');
// const part_2_title = document.getElementById('part-2-title');
// part_2_title.innerHTML = `<span>Крок 2.</span> Створення точок на карті`;
// part_2.style.display = "";
},
next() {
console.log('points next');
},
save() {
console.log('points next save');
}
},
homestead: {
init() {
console.log('homestead');
const part_2 = document.getElementById('part-2');
const part_2_title = document.getElementById('part-2-title');
part_2_title.innerHTML = `<span>Крок 2.</span> Створення ділянки`;
part_2.style.display = "";
Constructor.osm.init();
},
next() {
console.log('homestead next');
const part_3 = document.getElementById('part-3');
const title = part_3.querySelector('h1');
const button = part_3.querySelector('#part-3-button');
part_3.innerHTML = '';
title.innerHTML = `<span>Крок 3.</span> Створення будинків`;
part_3.appendChild(title);
part_3.innerHTML += `
<div class="info">
<p>*Натисніть кнопку нижче, а потім клацайте на карті, щоб додати будинки. Після цього натисніть "Зберегти".</p>
<p>*Щоб видалити будинок, клацніть на ньому та у спливаючому вікні оберіть "Видалити".</p>
<br />
<button onclick="Constructor.homestead.building.newHouse(this)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M 22.828125 3 C 22.316375 3 21.804562 3.1954375 21.414062 3.5859375 L 19 6 L 24 11 L 26.414062 8.5859375 C 27.195062 7.8049375 27.195062 6.5388125 26.414062 5.7578125 L 24.242188 3.5859375 C 23.851688 3.1954375 23.339875 3 22.828125 3 z M 17 8 L 5.2597656 19.740234 C 5.2597656 19.740234 6.1775313 19.658 6.5195312 20 C 6.8615312 20.342 6.58 22.58 7 23 C 7.42 23.42 9.6438906 23.124359 9.9628906 23.443359 C 10.281891 23.762359 10.259766 24.740234 10.259766 24.740234 L 22 13 L 17 8 z M 4 23 L 3.0566406 25.671875 A 1 1 0 0 0 3 26 A 1 1 0 0 0 4 27 A 1 1 0 0 0 4.328125 26.943359 A 1 1 0 0 0 4.3378906 26.939453 L 4.3632812 26.931641 A 1 1 0 0 0 4.3691406 26.927734 L 7 26 L 5.5 24.5 L 4 23 z"></path></svg>
<span>Додати будинок</span>
</button>
</div>
`;
part_3.appendChild(button);
part_3.style.display = "";
this.building.init();
},
async save() {
Constructor.info.buildings = Constructor.homestead.building.list;
console.log(Constructor.info);
Constructor.save();
},
building: {
list: [], editing: false,
async init() {
this.editing = false;
setLeafletCursor('pointer');
// Обробник кліку на карту
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 });
});
},
async addBuilding({ geo, title }) {
this.list.push({
title: title,
geo: geo
});
// Додаємо маркер на карту
const redDot = L.divIcon({
className: "leaflet_drop",
html: `<div id="redDot_${this.list.length}"></div>`,
iconSize: [16, 16],
iconAnchor: [8, 8]
});
const marker = L.marker(geo, { icon: redDot }).addTo(buildingGroup);
marker.bindPopup(`
Будинок: ${this.list.length}<br>
Координати: ${geo.lat.toFixed(5)}, ${geo.lng.toFixed(5)}<br>
<button class="map_dell" onclick="Constructor.homestead.building.delleteBuilding({id: ${this.list.length}})" type="button">Видалити</button>
`);
setLeafletCursor('crosshair');
this.editing = true;
},
async delleteBuilding({ id }) {
const el = document.getElementById(`redDot_${id}`);
if (el) el.remove();
this.list = this.list.filter(item => item.title !== id);
const block = document.getElementById(`Building_${id}`);
if (block) block.remove();
houseGroup.eachLayer(layer => {
if (layer instanceof L.Marker && layer.getPopup()?.getContent().includes(`Будинок: ${id}`)) {
houseGroup.removeLayer(layer);
}
});
},
newHouse(element) {
const btn = element;
this.editing = !this.editing;
setLeafletCursor(this.editing ? 'crosshair' : 'pointer');
btn.innerHTML = this.editing
? `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"> <path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z" ></path> </svg><span>Завершити додавання</span>`
: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"> <path d="M 22.828125 3 C 22.316375 3 21.804562 3.1954375 21.414062 3.5859375 L 19 6 L 24 11 L 26.414062 8.5859375 C 27.195062 7.8049375 27.195062 6.5388125 26.414062 5.7578125 L 24.242188 3.5859375 C 23.851688 3.1954375 23.339875 3 22.828125 3 z M 17 8 L 5.2597656 19.740234 C 5.2597656 19.740234 6.1775313 19.658 6.5195312 20 C 6.8615312 20.342 6.58 22.58 7 23 C 7.42 23.42 9.6438906 23.124359 9.9628906 23.443359 C 10.281891 23.762359 10.259766 24.740234 10.259766 24.740234 L 22 13 L 17 8 z M 4 23 L 3.0566406 25.671875 A 1 1 0 0 0 3 26 A 1 1 0 0 0 4 27 A 1 1 0 0 0 4.328125 26.943359 A 1 1 0 0 0 4.3378906 26.939453 L 4.3632812 26.931641 A 1 1 0 0 0 4.3691406 26.927734 L 7 26 L 5.5 24.5 L 4 23 z" ></path> </svg><span>Додати будинок</span>`;
if (this.editing) alert("Натискаючи на карту будуть створюватись нові точки (будинки)");
}
}
},
house: {
init() {
console.log('house');
const part_2 = document.getElementById('part-2');
const part_2_title = document.getElementById('part-2-title');
part_2_title.innerHTML = `<span>Крок 2.</span> Конструктор будинків`;
part_2.style.display = "";
Constructor.osm.init();
},
next() {
console.log('house next');
const part_3 = document.getElementById('part-3');
const title = part_3.querySelector('h1');
const button = part_3.querySelector('#part-3-button');
part_3.innerHTML = '';
title.innerHTML = `<span>Крок 3.</span> Конструктор квартир`;
part_3.appendChild(title);
part_3.innerHTML += `<input onchange="Constructor.house.apartments.editQuantity(this.value)" type="number" value="1" id="next-apartment-title" title="Авто-номер наступної квартири">`
part_3.appendChild(button);
part_3.style.display = "";
this.apartments.init();
},
async save() {
console.log('house next save');
Constructor.info.entrances = this.apartments.list.map((entrance, entranceIndex) => {
let apartments = [];
let apartmentCounter = 0;
entrance.list.forEach((floor, floorIndex) => {
floor.forEach(apartment => {
apartments.push({
title: apartment.title,
apartment_number: apartmentCounter++,
floors_number: floorIndex + 1
});
});
});
return {
title: entrance.title,
entrance_number: entranceIndex,
apartments
};
});
Constructor.save();
},
apartments: {
quantity: 0,
list: [],
init() {
const part_3 = document.getElementById("part-3");
const part_3_Button = part_3.querySelector("#part-3-button");
this.quantity++;
const newEntrance = {
title: `Під'їзд ${this.list.length + 1}`,
list: [[{ title: this.quantity }]]
};
this.list.push(newEntrance);
const eIndex = this.list.length - 1;
const floorIndex = 0;
const apartmentIndex = 0;
const houseDiv = this.createHouse(eIndex);
const entranceDiv = this.createEntrance(eIndex);
const floorDiv = this.createFloor(eIndex, floorIndex);
const apartmentDiv = this.createApartment(eIndex, floorIndex, apartmentIndex, this.quantity);
floorDiv.insertBefore(apartmentDiv, floorDiv.querySelector(".floor-info"));
entranceDiv.appendChild(floorDiv);
houseDiv.insertBefore(entranceDiv, houseDiv.querySelector(".entrance-button"));
part_3.insertBefore(houseDiv, part_3_Button);
},
createApartment(entrance, floor, apartment, value) {
const div = document.createElement("div");
div.className = "apartment";
div.id = `apartment-${entrance}-${floor}-${apartment}`;
div.innerHTML = `
<input onchange="Constructor.house.apartments.editApartment(${entrance}, ${floor}, ${apartment}, this.value)" type="text" value="${value}">
<button onclick="Constructor.house.apartments.deleteApartment(${entrance}, ${floor}, ${apartment})" title="Видалити квартиру" type="button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
`;
return div;
},
createFloor(entrance, floor) {
const div = document.createElement("div");
div.className = "floor";
div.id = `floor-${entrance}-${floor}`;
div.innerHTML = `
<div class="floor-info">
<h2>Поверх ${floor + 1}</h2>
<button onclick="Constructor.house.apartments.addApartment(${entrance}, ${floor})" title="Додати квартиру" type="button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
</div>`;
return div;
},
createEntrance(entrance) {
const div = document.createElement("div");
div.className = "entrance";
div.id = `entrance-${entrance}`;
div.innerHTML = `
<div class="entrance-info">
<input onchange="Constructor.house.apartments.editEntrance(${entrance}, this.value)" type="text" value="Під'їзд ${entrance + 1}">
<button onclick="Constructor.house.apartments.addFloors(${entrance})" title="Додати поверх" type="button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
</div>`;
return div;
},
createHouse() {
const div = document.createElement("div");
div.id = `house`;
div.innerHTML = `
<button class="entrance-button" onclick="Constructor.house.apartments.addEntrance()" title="Додати під'їзд" type="button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"> <path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>`;
return div;
},
addEntrance() {
const blockHouse = document.getElementById("house");
const houseButton = blockHouse.querySelector(".entrance-button");
this.editQuantity(this.quantity + 1);
const newEntrance = {
title: `Під'їзд ${this.list.length + 1}`,
list: [[{ title: this.quantity }]]
};
this.list.push(newEntrance);
const eIndex = this.list.length - 1;
const floorIndex = 0;
const apartmentIndex = 0;
const entranceDiv = this.createEntrance(eIndex);
const floorDiv = this.createFloor(eIndex, floorIndex);
const apartmentDiv = this.createApartment(eIndex, floorIndex, apartmentIndex, this.quantity);
floorDiv.insertBefore(apartmentDiv, floorDiv.querySelector(".floor-info"));
entranceDiv.appendChild(floorDiv);
blockHouse.insertBefore(entranceDiv, houseButton);
},
editEntrance(entrance, value) {
this.list[entrance].title = value;
},
addFloors(entrance) {
const entranceBlock = document.getElementById(`entrance-${entrance}`);
const entranceInfo = entranceBlock.querySelector(".entrance-info");
this.editQuantity(this.quantity + 1);
this.list[entrance].list.push([{ title: this.quantity }]);
const fIndex = this.list[entrance].list.length - 1;
const floorDiv = this.createFloor(entrance, fIndex);
const aptDiv = this.createApartment(entrance, fIndex, 0, this.quantity);
floorDiv.insertBefore(aptDiv, floorDiv.querySelector(".floor-info"));
entranceInfo.after(floorDiv);
},
addApartment(entrance, floor) {
const blockFloor = document.getElementById(`floor-${entrance}-${floor}`);
const floorInfo = blockFloor.querySelector(".floor-info");
this.editQuantity(this.quantity + 1);
this.list[entrance].list[floor].push({ title: this.quantity });
const aIndex = this.list[entrance].list[floor].length - 1;
const aptDiv = this.createApartment(entrance, floor, aIndex, this.quantity);
blockFloor.insertBefore(aptDiv, floorInfo);
},
editApartment(entrance, floor, apartment, value) {
this.list[entrance].list[floor][apartment].title = value;
},
deleteApartment(entrance, floor, apartment) {
this.list[entrance].list[floor].splice(apartment, 1);
document.getElementById(`apartment-${entrance}-${floor}-${apartment}`)?.remove();
this.editQuantity(this.quantity - 1);
},
editQuantity(value) {
const next_apartment_title = document.getElementById('next-apartment-title');
next_apartment_title.style.display = "";
next_apartment_title.value = value;
this.quantity = Number(value);
}
}
},
osm: {
init() {
const center = { lat: 49.5629016, lng: 25.6145625 };
const zoom = 19;
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]);
Constructor.info.points.push(LatLngs);
Constructor.info.points_number.push(this.center(layer.getLatLngs()));
let geo = this.center(layer.getLatLngs());
const house = layer; // сохраняем именно слой
if (Constructor.info.type === 'house') {
houseGroup.addLayer(house);
} else if (Constructor.info.type === 'homestead') {
homesteadGroup.addLayer(house);
}
house.bindPopup(`
Координати: ${geo.lat.toFixed(5)}, ${geo.lng.toFixed(5)}<br>
<button class="map_dell" type="button">Видалити</button>
`);
// при открытии popup вешаем обработчик удаления
house.on('popupopen', (e) => {
if (Constructor.homestead.building.editing) {
house.closePopup();
return;
}
const btn = e.popup.getElement().querySelector('.map_dell');
if (btn) {
btn.addEventListener('click', () => {
Constructor.osm.delete(house);
});
}
});
Constructor.osm.autoZoom(Constructor.info.points);
});
map.pm.setLang("ua");
}
houseGroup.clearLayers();
homesteadGroup.clearLayers();
buildingGroup.clearLayers();
pointsGroup.clearLayers();
},
newPoligon() {
if (Constructor.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 (Constructor.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(',');
Constructor.info.osm_id = ids_list;
houseGroup.clearLayers();
homesteadGroup.clearLayers();
Constructor.info.points = [];
Constructor.info.points_number = [];
Constructor.info.geo = {}
// 1006306041, 1006306065
for (let i = 0; i < ids_list.length; i++) {
const element = await Constructor.osm.getOSM(Constructor.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
Constructor.info.points.push(LatLngs);
Constructor.info.points_number.push(center);
// Создаем L.polygon
const polyOptions = Constructor.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 (Constructor.info.type === 'house') {
houseGroup.addLayer(house);
} else if (Constructor.info.type === 'homestead') {
homesteadGroup.addLayer(house);
}
// Bind popup с кнопкой удаления
house.bindPopup(`
Координати: ${center.lat.toFixed(5)}, ${center.lng.toFixed(5)}<br>
<button class="map_dell" type="button">Видалити</button>
`);
house.on('popupopen', (e) => {
if (Constructor.homestead.building.editing) {
house.closePopup();
return;
}
const btn = e.popup.getElement().querySelector('.map_dell');
if (btn) {
btn.addEventListener('click', () => {
Constructor.osm.delete(house);
});
}
});
}
Constructor.osm.autoZoom(Constructor.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(() => {
Constructor.info.zoom = map.getZoom();
Constructor.info.geo = map.getCenter();
}, 200)
},
delete(house) {
// убрать слой с карты
if (Editor.info.type === 'house') {
houseGroup.removeLayer(house);
} else if (Editor.info.type === 'homestead') {
homesteadGroup.removeLayer(house);
}
// найти индекс полигона в points
const target = house.getLatLngs()[0]; // вершины полигона
const index = Editor.info.points.findIndex((p) => {
const copy = p.slice(); // делаем копию
copy[0].pop(); // убираем последний элемент
if (isSamePolygon(p, target)) return true; // проверка как есть
if (isSamePolygon(copy, target)) return true; // проверка без последнего
return false;
});
function isSamePolygon(a, b) {
if (a.length !== b.length) return false;
return a.every((pt, i) => pt.lat === b[i].lat && pt.lng === b[i].lng);
}
if (index) {
// удалить из points и points_number по индексу
Editor.info.points.splice(index, 1);
Editor.info.points_number.splice(index, 1);
}
Editor.osm.autoZoom(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() {
const part_3_button = document.getElementById('part-3-button');
console.log(Constructor.info);
setLeafletCursor('pointer');
Constructor.homestead.building.editing = false;
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}constructor`;
await fetch(URL, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
body: JSON.stringify(Constructor.info)
})
.then(response => {
if (response.status == 200) {
console.log({ 'setPack': 'ok' });
part_3_button.innerText = "Запис додано";
return response.json()
} else {
console.log('err');
part_3_button.innerText = "Помилка запису";
return
}
})
.then(data => {
console.log(data);
Territory.house.list = [];
Territory.homestead.list = [];
Router.navigate(`/territory/manager/${Constructor.info.type}/${data.id}`);
setTimeout(() => {
part_3_button.innerText = "Зберегти";
}, 3000);
})
.catch(err => {
console.log(err);
part_3_button.innerText = "Помилка запису";
})
}
}