diff --git a/index.html b/index.html
index 69c2012..334a71f 100644
--- a/index.html
+++ b/index.html
@@ -6,44 +6,47 @@
Планировщик опор ВЛ — РК
@@ -51,15 +54,14 @@ body{font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,sans-serif;
@@ -68,20 +70,30 @@ body{font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,sans-serif;
-
+
+
+
Режим: ручная расстановка
🎯 Кликните для установки
+
+
🔄 Автоопределение
+
Загрузка данных...
+
+
+
- Опора ЖБ
- Линия ВЛ
- В Въезд
+ Опора
+ ВЛ
+ В Въезд
+ Улица (OSM)
+ Здание
',iconSize:[26,18],iconAnchor:[13,9]});
- const marker=L.marker(latlng,{icon}).addTo(map).bindPopup('🚗 Въезд во двор');
+ const marker=L.marker(latlng,{icon}).addTo(map).bindPopup('🚗 Въезд');
entrances.push({lat:latlng.lat,lng:latlng.lng,marker});
const circle=L.circle(latlng,{radius:5,color:'#FF9800',fillColor:'#FF9800',fillOpacity:0.15,dashArray:'3,3'}).addTo(map);
entranceCircles.push(circle);
showToast('🚗 Въезд отмечен');
}
+function isNearEntrance(pt,margin){
+ for(const e of entrances)if(haversine(pt.lat,pt.lng,e.lat,e.lng)x*Math.PI/180;
const dLat=toRad(lat2-lat1),dLon=toRad(lon2-lon1);
- const a=Math.sin(dLat/2)**2+Math.cos(toRad(lat1))*Math.cos(toRad(lat2))*Math.sin(dLon/2)**2;
- return R*2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));
+ return R*2*Math.atan2(Math.sqrt(Math.sin(dLat/2)**2+Math.cos(toRad(lat1))*Math.cos(toRad(lat2))*Math.sin(dLon/2)**2),Math.sqrt(1-(Math.sin(dLat/2)**2+Math.cos(toRad(lat1))*Math.cos(toRad(lat2))*Math.sin(dLon/2)**2)));
}
// ====================== ТАБЛИЦА ======================
@@ -322,136 +454,59 @@ function clearAll(){
}
document.addEventListener('keydown',e=>{if(e.key==='Enter'&&mode==='line')finishLine();});
-// ====================== ЭКСПОРТ В SVG (Visio/draw.io) ======================
+// ====================== ЭКСПОРТ ======================
function exportSVG(){
if(!markers.length){showToast('Нет опор');return;}
const first=markers[0];
const step=parseFloat(document.getElementById('poleStep').value)||50;
let svg=`
-
`;
+ markers.forEach((p,i)=>{if(i>0){const p1=markers[i-1];svg+=``;}});
+ markers.forEach(p=>{const x=sx(p.lng),y=sy(p.lat);svg+=`${p.id}${p.name}`;});
+ entrances.forEach(e=>{const x=sx(e.lng),y=sy(e.lat);svg+=`В`;});
+ let total=0;markers.forEach((p,i)=>{if(i>0)total+=haversine(markers[i-1].lat,markers[i-1].lng,p.lat,p.lng);});
+ svg+=`Всего: ${total.toFixed(1)} м | ${markers.length} опор | ср.пролёт: ${markers.length>1?(total/(markers.length-1)).toFixed(1):0} м`;
download(svg,'opory_vl04.svg','image/svg+xml');
- showToast('📥 SVG экспортирован (открывается в Visio)');
+ showToast('📥 SVG (Visio/draw.io)');
}
-
-// ====================== ЭКСПОРТ В DRAW.IO ======================
function exportDrawIO(){
if(!markers.length){showToast('Нет опор');return;}
- const first=markers[0];
- const step=parseFloat(document.getElementById('poleStep').value)||50;
const date=new Date().toLocaleDateString('ru-RU');
- let mx='';
- mx+=``;
- mx+=``;
- // заголовок
- mx+=``;
- // масштаб
+ let mx='';
+ mx+=``;
const lats=markers.map(m=>m.lat),lngs=markers.map(m=>m.lng);
const minLat=Math.min(...lats),maxLat=Math.max(...lats);
const minLng=Math.min(...lngs),maxLng=Math.max(...lngs);
const sx=x=>40+(x-minLng)/(maxLng-minLng||1)*700;
- const sy=y=>560-(y-minLat)/(maxLat-minLat||1)*500;
- let lineId=100;
- // линии
- markers.forEach((p,i)=>{
- if(i===0)return;
- const p1=markers[i-1];
- const d=haversine(p1.lat,p1.lng,p.lat,p.lng).toFixed(1);
- lineId++;
- mx+=``;
- });
- // опоры
- markers.forEach(p=>{
- const x=sx(p.lng).toFixed(0),y=sy(p.lat).toFixed(0);
- mx+=``;
- mx+=``;
- });
- // въезды
- entrances.forEach((e,i)=>{
- const x=sx(e.lng).toFixed(0),y=sy(e.lat).toFixed(0);
- mx+=``;
- });
- // итого
- let total=0;
- markers.forEach((p,i)=>{if(i>0)total+=haversine(markers[i-1].lat,markers[i-1].lng,p.lat,p.lng);});
- mx+=``;
+ const sy=y=>560-(y-minLat)/(maxLat-minLat||1)*520;
+ let id=100;
+ markers.forEach((p,i)=>{if(i>0){const d=haversine(markers[i-1].lat,markers[i-1].lng,p.lat,p.lng).toFixed(1);id++;mx+=``;}});
+ markers.forEach(p=>{const x=sx(p.lng).toFixed(0),y=sy(p.lat).toFixed(0);mx+=``;});
+ entrances.forEach((e,i)=>{const x=sx(e.lng).toFixed(0),y=sy(e.lat).toFixed(0);mx+=``;});
+ let total=0;markers.forEach((p,i)=>{if(i>0)total+=haversine(markers[i-1].lat,markers[i-1].lng,p.lat,p.lng);});
+ mx+=``;
mx+=``;
download(mx,'opory_vl04.drawio','application/xml');
- showToast('📥 draw.io экспортирован');
+ showToast('📥 draw.io');
}
-
-// ====================== CSV / JSON ======================
function exportCSV(){
if(!markers.length){showToast('Нет опор');return;}
let csv='№,Название,Широта,Долгота,Расстояние_до_предыдущей_м\n';
- markers.forEach((p,i)=>{
- const dist=i>0?haversine(markers[i-1].lat,markers[i-1].lng,p.lat,p.lng).toFixed(1):'';
- csv+=p.id+','+p.name+','+p.lat.toFixed(6)+','+p.lng.toFixed(6)+','+dist+'\n';
- });
+ markers.forEach((p,i)=>{const d=i>0?haversine(markers[i-1].lat,markers[i-1].lng,p.lat,p.lng).toFixed(1):'';csv+=p.id+','+p.name+','+p.lat.toFixed(6)+','+p.lng.toFixed(6)+','+d+'\n';});
download(csv,'opory_vl04.csv','text/csv;charset=utf-8');
}
-function exportJSON(){
- if(!markers.length){showToast('Нет опор');return;}
- const data=markers.map((p,i)=>{
- const dist=i>0?haversine(markers[i-1].lat,markers[i-1].lng,p.lat,p.lng):0;
- return{id:p.id,name:p.name,lat:parseFloat(p.lat.toFixed(6)),lng:parseFloat(p.lng.toFixed(6)),distance_m:parseFloat(dist.toFixed(1))};
- });
- download(JSON.stringify({poles:data,total_m:data.reduce((s,d)=>s+d.distance_m,0),average_span_m:data.length>1?(data.reduce((s,d)=>s+d.distance_m,0)/(data.length-1)):0},null,2),'opory_vl04.json','application/json');
-}
function download(content,filename,type){
const blob=new Blob([content],{type:'application/octet-stream'});
const a=document.createElement('a');a.href=URL.createObjectURL(blob);