diff --git a/index.html b/index.html
index ffc82a8..5dfb19d 100644
--- a/index.html
+++ b/index.html
@@ -64,12 +64,11 @@ body{font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,sans-serif;
-
+
-
+
-
-
+
@@ -154,84 +153,118 @@ function init(){
}
// ====================== ВЫБОР ЛОКАЦИИ ======================
+let districtData=[]; // {name, osm_id}
+let regionOsmId=null;
+
function onRegionChange(){
const name=document.getElementById('regionSelect').value;
- const r=REGIONS.find(x=>x.name===name);
+ const region=REGIONS.find(x=>x.name===name);
const sel=document.getElementById('districtSelect');
- sel.innerHTML='';
- if(!r){document.getElementById('locStatus').textContent='';return;}
- map.setView(r.center,9);
+ const vsel=document.getElementById('villageSelect');
+ sel.innerHTML='';
+ vsel.innerHTML='';
+ districtData=[];
+ if(!region){document.getElementById('locStatus').textContent='';return;}
+ map.setView(region.center,9);
document.getElementById('locStatus').textContent='⏳ районы...';
- // Ищем область в Nominatim, получаем bounding box, ищем районы внутри
- const searchName=name+' область';
- fetch('https://nominatim.openstreetmap.org/search?format=json&q='+encodeURIComponent(searchName)+'&countrycodes=kz&limit=1')
- .then(r=>r.json()).then(reg=>{
- if(!reg.length)return fetch('https://nominatim.openstreetmap.org/search?format=json&q='+encodeURIComponent(name)+'&countrycodes=kz&limit=1').then(r=>r.json());
- return reg;
- }).then(reg=>{
- if(!reg.length)throw new Error('region not found');
- const b=reg[0].boundingbox;
- return fetch('https://nominatim.openstreetmap.org/search?format=json&q=район&countrycodes=kz&bounded=1&viewbox='+b[2]+','+b[0]+','+b[3]+','+b[1]+'&limit=40');
+ fetch('https://nominatim.openstreetmap.org/search?format=json&q='+encodeURIComponent(name+' область')+'&countrycodes=kz&limit=1')
+ .then(r=>r.json())
+ .then(list=>{
+ if(list.length)return list[0];
+ return fetch('https://nominatim.openstreetmap.org/search?format=json&q='+encodeURIComponent(name)+'&countrycodes=kz&limit=1').then(r=>r.json()).then(l=>l[0]||null);
})
- .then(r=>r.json()).then(data=>{
- const names=new Set();
- data.forEach(item=>{
- if(item.type!='administrative')return;
- const n=item.display_name.split(',')[0].trim();
- // отсекаем лишнее
- if(n.toLowerCase().includes('город')||n===name)return;
- if(n&&!names.has(n))names.add(n);
+ .then(reg=>{
+ if(!reg)throw new Error('Регион не найден');
+ regionOsmId=reg.osm_id;
+ const areaId=3600000000+reg.osm_id;
+ const q='[out:json];area('+areaId+')->.a;(rel(area.a)["boundary"="administrative"]["admin_level"~"7|8"];);out tags id;';
+ return fetch('https://overpass-api.de/api/interpreter?data='+encodeURIComponent(q));
+ })
+ .then(r=>r.json())
+ .then(data=>{
+ if(!data||!data.elements)throw new Error('Нет данных');
+ const map=new Map();
+ data.elements.forEach(el=>{
+ if(!el.tags||!el.tags.name)return;
+ let n=el.tags.name.replace(/\s*ауданы$/i,'').replace(/\s*районы$/i,'').replace(/\s*район$/i,'').trim();
+ if(n&&!map.has(n))map.set(n,el.id);
});
- const sorted=[...names].sort();
+ const sorted=[...map.keys()].sort();
+ sel.innerHTML='';
sorted.forEach(n=>{
const o=document.createElement('option');
o.value=n;o.textContent=n;
sel.appendChild(o);
+ districtData.push({name:n,osm_id:map.get(n)});
});
- document.getElementById('locStatus').textContent=sorted.length?'✅ '+sorted.length+' районов':'⚠ районы не найдены (введите село вручную)';
+ document.getElementById('locStatus').textContent=sorted.length?'✅ '+sorted.length+' районов':'⚠ районы не найдены';
})
- .catch(()=>{
- document.getElementById('locStatus').textContent='⚠ ошибка загрузки (введите село вручную)';
+ .catch(e=>{
+ sel.innerHTML='';
+ document.getElementById('locStatus').textContent='⚠ '+e.message;
});
}
+
function onDistrictSelect(){
- searchLocation();
-}
-function searchLocation(){
- const r=document.getElementById('regionSelect').value;
- const d=document.getElementById('districtSelect').value;
- const v=document.getElementById('villageInput').value.trim();
- const parts=[];
- if(r)parts.push(r+' область');
- if(d)parts.push(d+' район');
- if(v)parts.push(v);
- const q=parts.join(', ');
- if(!q||!v&&!d){showToast('Введите название села');return;}
- // Если есть район но нет села — летим в центр района
- if(!v&&d){
- document.getElementById('locStatus').textContent='⏳ поиск района...';
- fetch('https://nominatim.openstreetmap.org/search?format=json&q='+encodeURIComponent(d+' район '+r+' область')+'&countrycodes=kz&limit=5')
- .then(r=>r.json()).then(data=>{
- if(!data.length){showToast('❌ Район не найден');document.getElementById('locStatus').textContent='';return;}
- map.setView([data[0].lat,data[0].lon],11);
- document.getElementById('locStatus').textContent='📍 '+data[0].display_name.split(',')[0];
- });
+ const dname=document.getElementById('districtSelect').value;
+ const vsel=document.getElementById('villageSelect');
+ vsel.innerHTML='';
+ if(!dname||!regionOsmId){
+ vsel.innerHTML='';
return;
}
- document.getElementById('locStatus').textContent='⏳ поиск...';
- fetch('https://nominatim.openstreetmap.org/search?format=json&q='+encodeURIComponent(q)+'&countrycodes=kz&limit=8')
- .then(r=>r.json()).then(data=>{
- if(!data.length){showToast('❌ Не найдено');document.getElementById('locStatus').textContent='не найдено';return;}
- const loc=data[0];
- map.setView([loc.lat,loc.lon],16);
- const name=loc.display_name.split(',')[0];
- showToast('📍 '+name);
- document.getElementById('villageInput').value=name;
- document.getElementById('locStatus').textContent=name;
- setTimeout(autoDetect,500);
+ // Ищем район среди загруженных
+ const dist=districtData.find(d=>d.name===dname);
+ if(!dist){
+ vsel.innerHTML='';
+ return;
+ }
+ const areaId=3600000000+dist.osm_id;
+ // Ищем сёла и посёлки внутри района
+ const q='[out:json];area('+areaId+')->.a;(node(area.a)["place"~"village|hamlet|town"];);out tags;';
+ fetch('https://overpass-api.de/api/interpreter?data='+encodeURIComponent(q))
+ .then(r=>r.json())
+ .then(data=>{
+ vsel.innerHTML='';
+ if(!data||!data.elements||!data.elements.length){
+ vsel.innerHTML='';
+ return;
+ }
+ const villages=[];
+ data.elements.forEach(el=>{
+ if(!el.tags||!el.tags.name)return;
+ villages.push({name:el.tags.name,lat:el.lat,lng:el.lon});
+ });
+ villages.sort((a,b)=>a.name.localeCompare(b.name,'ru'));
+ villages.forEach(v=>{
+ const o=document.createElement('option');
+ o.value=v.name;
+ o.dataset.lat=v.lat;
+ o.dataset.lng=v.lng;
+ o.textContent=v.name;
+ vsel.appendChild(o);
+ });
+ document.getElementById('locStatus').textContent='✅ '+villages.length+' сёл';
+ })
+ .catch(()=>{
+ vsel.innerHTML='';
});
}
+function onVillageSelect(){
+ const vsel=document.getElementById('villageSelect');
+ const opt=vsel.options[vsel.selectedIndex];
+ if(!opt||!opt.dataset.lat)return;
+ const lat=parseFloat(opt.dataset.lat);
+ const lng=parseFloat(opt.dataset.lng);
+ if(!isNaN(lat)&&!isNaN(lng)){
+ map.setView([lat,lng],16);
+ showToast('📍 '+opt.value);
+ setTimeout(autoDetect,500);
+ }
+}
+
+
// ====================== АВТООПРЕДЕЛЕНИЕ УЛИЦ И ЗДАНИЙ (OSM) ======================
function autoDetect(){
document.getElementById('autoPanel').style.display='block';