From 2dee3e51bcb829e54f48c8b80ad4ce1d39453871 Mon Sep 17 00:00:00 2001 From: Dauren777 Date: Fri, 19 Jun 2026 06:48:33 +0000 Subject: [PATCH] v11: fixed districts via Overpass directly, village dropdown --- index.html | 107 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/index.html b/index.html index 5dfb19d..3ac3e44 100644 --- a/index.html +++ b/index.html @@ -153,55 +153,81 @@ function init(){ } // ====================== ВЫБОР ЛОКАЦИИ ====================== -let districtData=[]; // {name, osm_id} -let regionOsmId=null; +let regionRelId=null; +let districtList=[]; function onRegionChange(){ const name=document.getElementById('regionSelect').value; const region=REGIONS.find(x=>x.name===name); - const sel=document.getElementById('districtSelect'); + const dsel=document.getElementById('districtSelect'); const vsel=document.getElementById('villageSelect'); - sel.innerHTML=''; + dsel.innerHTML=''; vsel.innerHTML=''; - districtData=[]; + districtList=[];regionRelId=null; if(!region){document.getElementById('locStatus').textContent='';return;} map.setView(region.center,9); - document.getElementById('locStatus').textContent='⏳ районы...'; - fetch('https://nominatim.openstreetmap.org/search?format=json&q='+encodeURIComponent(name+' область')+'&countrycodes=kz&limit=1') + const st=document.getElementById('locStatus'); + 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(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(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(data=>{ + if(!data||!data.elements||!data.elements.length)throw new Error('регион не найден в OSM'); + const rel=data.elements[0]; + regionRelId=rel.id; + st.textContent='⏳ районы...'; + // ШАГ 2: ищем районы (admin_level=6,7) внутри региона + const areaId=3600000000+rel.id; + const q2='[out:json];area('+areaId+')->.a;(rel(area.a)["boundary"="administrative"]["admin_level"~"6|7"];);out tags id;'; + return fetch('https://overpass-api.de/api/interpreter?data='+encodeURIComponent(q2)); }) .then(r=>r.json()) .then(data=>{ - if(!data||!data.elements)throw new Error('Нет данных'); - const map=new Map(); + if(!data||!data.elements||!data.elements.length)throw new Error('районы не найдены'); + const seen=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); + let clean=el.tags.name.replace(/\s*ауданы$/i,'').replace(/\s*районы$/i,'').replace(/\s*район$/i,'').trim(); + if(clean&&!seen.has(clean))seen.set(clean,el.id); }); - const sorted=[...map.keys()].sort(); - sel.innerHTML=''; + dsel.innerHTML=''; + const sorted=[...seen.keys()].sort(); 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)}); + dsel.appendChild(o); + districtList.push({name:n,osm_id:seen.get(n)}); }); - document.getElementById('locStatus').textContent=sorted.length?'✅ '+sorted.length+' районов':'⚠ районы не найдены'; + st.textContent='✅ '+sorted.length+' районов'; }) .catch(e=>{ - sel.innerHTML=''; - document.getElementById('locStatus').textContent='⚠ '+e.message; + dsel.innerHTML=''; + 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=''; + 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 vsel=document.getElementById('villageSelect'); vsel.innerHTML=''; - if(!dname||!regionOsmId){ - vsel.innerHTML=''; - return; + if(!dname||!districtList.length){ + vsel.innerHTML='';return; } - // Ищем район среди загруженных - const dist=districtData.find(d=>d.name===dname); + const dist=districtList.find(d=>d.name===dname); if(!dist){ - vsel.innerHTML=''; - return; + 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; + vsel.innerHTML='';return; } const villages=[]; data.elements.forEach(el=>{ @@ -238,16 +259,13 @@ function onDistrictSelect(){ 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; + 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=''; + vsel.innerHTML=''; }); } @@ -255,8 +273,7 @@ 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); + const lat=parseFloat(opt.dataset.lat),lng=parseFloat(opt.dataset.lng); if(!isNaN(lat)&&!isNaN(lng)){ map.setView([lat,lng],16); showToast('📍 '+opt.value);