v11: fixed districts via Overpass directly, village dropdown

This commit is contained in:
Dauren777 2026-06-19 06:48:33 +00:00
parent 0c173e048e
commit 2dee3e51bc

View File

@ -153,55 +153,81 @@ function init(){
} }
// ====================== ВЫБОР ЛОКАЦИИ ====================== // ====================== ВЫБОР ЛОКАЦИИ ======================
let districtData=[]; // {name, osm_id} let regionRelId=null;
let regionOsmId=null; let districtList=[];
function onRegionChange(){ function onRegionChange(){
const name=document.getElementById('regionSelect').value; const name=document.getElementById('regionSelect').value;
const region=REGIONS.find(x=>x.name===name); const region=REGIONS.find(x=>x.name===name);
const sel=document.getElementById('districtSelect'); const dsel=document.getElementById('districtSelect');
const vsel=document.getElementById('villageSelect'); const vsel=document.getElementById('villageSelect');
sel.innerHTML='<option value="">— загрузка —</option>'; dsel.innerHTML='<option value="">— загрузка —</option>';
vsel.innerHTML='<option value="">— выберите район —</option>'; vsel.innerHTML='<option value="">— выберите район —</option>';
districtData=[]; districtList=[];regionRelId=null;
if(!region){document.getElementById('locStatus').textContent='';return;} if(!region){document.getElementById('locStatus').textContent='';return;}
map.setView(region.center,9); map.setView(region.center,9);
document.getElementById('locStatus').textContent='⏳ районы...'; const st=document.getElementById('locStatus');
fetch('https://nominatim.openstreetmap.org/search?format=json&q='+encodeURIComponent(name+' область')+'&countrycodes=kz&limit=1') st.textContent='⏳ поиск региона...';
// ШАГ 1: через Nominatim ищем ТОЛЬКО relation административной границы
const q='[out:json];(rel["name:ru"="'+name+' область"]["boundary"="administrative"];rel["name"="'+name+' область"]["boundary"="administrative"];);out tags id;';
fetch('https://overpass-api.de/api/interpreter?data='+encodeURIComponent(q))
.then(r=>r.json()) .then(r=>r.json())
.then(list=>{ .then(data=>{
if(list.length)return list[0]; if(!data||!data.elements||!data.elements.length)throw new Error('регион не найден в OSM');
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); const rel=data.elements[0];
}) regionRelId=rel.id;
.then(reg=>{ st.textContent='⏳ районы...';
if(!reg)throw new Error('Регион не найден'); // ШАГ 2: ищем районы (admin_level=6,7) внутри региона
regionOsmId=reg.osm_id; const areaId=3600000000+rel.id;
const areaId=3600000000+reg.osm_id; const q2='[out:json];area('+areaId+')->.a;(rel(area.a)["boundary"="administrative"]["admin_level"~"6|7"];);out tags 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(q2));
return fetch('https://overpass-api.de/api/interpreter?data='+encodeURIComponent(q));
}) })
.then(r=>r.json()) .then(r=>r.json())
.then(data=>{ .then(data=>{
if(!data||!data.elements)throw new Error('Нет данных'); if(!data||!data.elements||!data.elements.length)throw new Error('районы не найдены');
const map=new Map(); const seen=new Map();
data.elements.forEach(el=>{ data.elements.forEach(el=>{
if(!el.tags||!el.tags.name)return; if(!el.tags||!el.tags.name)return;
let n=el.tags.name.replace(/\s*ауданы$/i,'').replace(/\s*районы$/i,'').replace(/\s*район$/i,'').trim(); let clean=el.tags.name.replace(/\s*ауданы$/i,'').replace(/\s*районы$/i,'').replace(/\s*район$/i,'').trim();
if(n&&!map.has(n))map.set(n,el.id); if(clean&&!seen.has(clean))seen.set(clean,el.id);
}); });
const sorted=[...map.keys()].sort(); dsel.innerHTML='<option value="">— все —</option>';
sel.innerHTML='<option value="">— все —</option>'; const sorted=[...seen.keys()].sort();
sorted.forEach(n=>{ sorted.forEach(n=>{
const o=document.createElement('option'); const o=document.createElement('option');
o.value=n;o.textContent=n; o.value=n;o.textContent=n;
sel.appendChild(o); dsel.appendChild(o);
districtData.push({name:n,osm_id:map.get(n)}); districtList.push({name:n,osm_id:seen.get(n)});
}); });
document.getElementById('locStatus').textContent=sorted.length?'✅ '+sorted.length+' районов':'⚠ районы не найдены'; st.textContent='✅ '+sorted.length+' районов';
}) })
.catch(e=>{ .catch(e=>{
sel.innerHTML='<option value="">— ошибка —</option>'; dsel.innerHTML='<option value="">— ошибка —</option>';
document.getElementById('locStatus').textContent='⚠ '+e.message; st.textContent='⚠ районы: '+e.message;
// fallback: поищем через Nominatim
fetchNominatimDistricts(name);
});
}
function fetchNominatimDistricts(regionName){
const st=document.getElementById('locStatus');
st.textContent='⏳ районы через Nominatim...';
fetch('https://nominatim.openstreetmap.org/search?format=json&q='+encodeURIComponent(regionName+' район')+'&countrycodes=kz&limit=25')
.then(r=>r.json())
.then(data=>{
const dsel=document.getElementById('districtSelect');
dsel.innerHTML='<option value="">— все —</option>';
if(!data||!data.length){st.textContent='⚠ районы не найдены';return;}
const seen=new Set();
data.forEach(item=>{
let n=item.display_name.split(',')[0].trim();
if(!n||seen.has(n))return;seen.add(n);
const o=document.createElement('option');
o.value=n;o.textContent=n;
dsel.appendChild(o);
districtList.push({name:n,osm_id:item.osm_id});
});
st.textContent='✅ через Nominatim: '+seen.size;
}); });
} }
@ -209,26 +235,21 @@ function onDistrictSelect(){
const dname=document.getElementById('districtSelect').value; const dname=document.getElementById('districtSelect').value;
const vsel=document.getElementById('villageSelect'); const vsel=document.getElementById('villageSelect');
vsel.innerHTML='<option value="">⏳ загрузка сёл...</option>'; vsel.innerHTML='<option value="">⏳ загрузка сёл...</option>';
if(!dname||!regionOsmId){ if(!dname||!districtList.length){
vsel.innerHTML='<option value="">— выберите район —</option>'; vsel.innerHTML='<option value="">— выберите район —</option>';return;
return;
} }
// Ищем район среди загруженных const dist=districtList.find(d=>d.name===dname);
const dist=districtData.find(d=>d.name===dname);
if(!dist){ if(!dist){
vsel.innerHTML='<option value="">— район не найден —</option>'; vsel.innerHTML='<option value="">— ошибка —</option>';return;
return;
} }
const areaId=3600000000+dist.osm_id; const areaId=3600000000+dist.osm_id;
// Ищем сёла и посёлки внутри района
const q='[out:json];area('+areaId+')->.a;(node(area.a)["place"~"village|hamlet|town"];);out tags;'; 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)) fetch('https://overpass-api.de/api/interpreter?data='+encodeURIComponent(q))
.then(r=>r.json()) .then(r=>r.json())
.then(data=>{ .then(data=>{
vsel.innerHTML='<option value="">— выберите село —</option>'; vsel.innerHTML='<option value="">— выберите село —</option>';
if(!data||!data.elements||!data.elements.length){ if(!data||!data.elements||!data.elements.length){
vsel.innerHTML='<option value="">— сёла не найдены —</option>'; vsel.innerHTML='<option value="">— сёла не найдены —</option>';return;
return;
} }
const villages=[]; const villages=[];
data.elements.forEach(el=>{ data.elements.forEach(el=>{
@ -238,16 +259,13 @@ function onDistrictSelect(){
villages.sort((a,b)=>a.name.localeCompare(b.name,'ru')); villages.sort((a,b)=>a.name.localeCompare(b.name,'ru'));
villages.forEach(v=>{ villages.forEach(v=>{
const o=document.createElement('option'); const o=document.createElement('option');
o.value=v.name; o.value=v.name;o.dataset.lat=v.lat;o.dataset.lng=v.lng;o.textContent=v.name;
o.dataset.lat=v.lat;
o.dataset.lng=v.lng;
o.textContent=v.name;
vsel.appendChild(o); vsel.appendChild(o);
}); });
document.getElementById('locStatus').textContent='✅ '+villages.length+' сёл'; document.getElementById('locStatus').textContent='✅ '+villages.length+' сёл';
}) })
.catch(()=>{ .catch(()=>{
vsel.innerHTML='<option value="">— ошибка загрузки </option>'; vsel.innerHTML='<option value="">— ошибка —</option>';
}); });
} }
@ -255,8 +273,7 @@ function onVillageSelect(){
const vsel=document.getElementById('villageSelect'); const vsel=document.getElementById('villageSelect');
const opt=vsel.options[vsel.selectedIndex]; const opt=vsel.options[vsel.selectedIndex];
if(!opt||!opt.dataset.lat)return; if(!opt||!opt.dataset.lat)return;
const lat=parseFloat(opt.dataset.lat); const lat=parseFloat(opt.dataset.lat),lng=parseFloat(opt.dataset.lng);
const lng=parseFloat(opt.dataset.lng);
if(!isNaN(lat)&&!isNaN(lng)){ if(!isNaN(lat)&&!isNaN(lng)){
map.setView([lat,lng],16); map.setView([lat,lng],16);
showToast('📍 '+opt.value); showToast('📍 '+opt.value);