| 12345678910111213141516 |
- import assert from 'node:assert/strict'
- import {readFileSync,writeFileSync} from 'node:fs'
- import {createHash} from 'node:crypto'
- const features=JSON.parse(readFileSync('tools/city-massing/city/cad-buildings.geojson')).features,root='tools/city-massing/city/cad-bim/',plan=JSON.parse(readFileSync(root+'plan.json')),seated=JSON.parse(readFileSync(root+'seated-plan.json')),audit=JSON.parse(readFileSync('.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/large-building-audit/audit.json'))
- const planBy=new Map(plan.buildings.map(b=>[b.id,b])),seatBy=new Map(seated.buildings.map(b=>[b.id,b])),excludeBy=new Map(seated.excluded.map(b=>[b.id,b])),auditBy=new Map(audit.candidates.map(b=>[b.id,b]))
- const roots=['pilot','batch-urban8000-dedup','batch-urban8000b','batch-urban8000c','batch-urban-final','batch-urban-tail','batch-unfitted-fallback'],active=new Set()
- for(const n of roots){const m=JSON.parse(readFileSync(`${root}lod-${n}-roofed2/manifest.json`));for(const c of m.cells)for(const id of c.buildingIds)active.add(id)}
- const targets=[['positive-fine',[110.40957689672715,28.4430329383677]],['negative-estate',[110.40570023361066,28.434788752462875]]],radius=180
- function coordinates(g){const out=[];function visit(v){if(Array.isArray(v)&&v.length>=2&&typeof v[0]==='number'&&typeof v[1]==='number'){out.push(v);return}if(Array.isArray(v))for(const x of v)visit(x)}if(g)visit(g.coordinates);return out}
- function center(points){return points.reduce((s,p)=>[s[0]+p[0]/points.length,s[1]+p[1]/points.length],[0,0])}
- function area(points){const c=center(points),cos=Math.cos(c[1]*Math.PI/180),xy=points.map(([x,y])=>[(x-c[0])*111320*cos,(y-c[1])*111320]);let a=0;for(let i=0;i<xy.length;i++){const p=xy[i],q=xy[(i+1)%xy.length];a+=p[0]*q[1]-q[0]*p[1]}return Math.abs(a/2)}
- const reports=[]
- for(const [name,target] of targets){const near=[];for(let i=0;i<features.length;i++){const f=features[i],ps=coordinates(f.geometry);if(!ps.length)continue;const c=center(ps),d=Math.hypot((c[0]-target[0])*111320*Math.cos(target[1]*Math.PI/180),(c[1]-target[1])*111320);if(d>radius)continue;const id=`cad-${i}`,b=planBy.get(id),s=seatBy.get(id),ex=excludeBy.get(id),a=auditBy.get(id);near.push({id,distanceM:+d.toFixed(1),areaSqm:+area(ps).toFixed(1),vertexCount:ps.length-1,activeSourceId:active.has(id),planned:!!b,seated:!!s,tier:b?.tier||ex?.tier||a?.tier||'unknown',kind:b?.kind||null,archetype:b?.archetype||null,rectFillRatio:b?.rectFillRatio??null,floors:b?.floors??a?.floors??null,heightM:s?.heightM??null,heightSource:b?.heightSource||s?.heightSource||a?.source||null,terrainRelief:s?.terrainRelief??ex?.relief??a?.terrainRelief??null,exclusionReason:ex?.reason||a?.status||null})}near.sort((a,b)=>a.distanceM-b.distanceM);const tally={surveyed:near.length,active:near.filter(x=>x.activeSourceId).length,seated:near.filter(x=>x.seated).length,seatedNotActive:near.filter(x=>x.seated&&!x.activeSourceId).length,unseated:near.filter(x=>!x.seated).length,assets:near.filter(x=>x.kind==='asset').length,massing:near.filter(x=>x.kind==='massing').length,tier:{}};for(const x of near)tally.tier[x.tier]=(tally.tier[x.tier]||0)+1
- reports.push({name,center:target,radiusM:radius,tally,buildings:near})}
- const result={generatedAt:'2026-10-14',sourceFootprints:'1:500 surveyed JMD source IDs cad-N; source rows mapped by array index',activeSourceManifests:roots.map(n=>`lod-${n}-roofed2/manifest.json`),activeSourceIdCount:active.size,planSha256:createHash('sha256').update(readFileSync(root+'plan.json')).digest('hex'),seatedSha256:createHash('sha256').update(readFileSync(root+'seated-plan.json')).digest('hex'),neighborhoods:reports,caveats:['ActiveSourceId is footprint metadata found in source manifests; not a browser-render confirmation.','Heights/floors are estimated from footprint area; no measured height source available.','Current source-tier mask is recorded, not reclassified from satellite imagery.','Satellite polygon/roof alignment remains subject to image-date/parallax review.'],modelChanged:false}
- assert.equal(reports.length,2);writeFileSync('.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/user-samples/neighborhood-building-audit.json',JSON.stringify(result,null,2));console.log(JSON.stringify({passed:true,activeSourceIdCount:active.size,neighborhoods:reports.map(x=>({name:x.name,tally:x.tally,unactiveSeatedTop:x.buildings.filter(b=>b.seated&&!b.activeSourceId).sort((a,b)=>b.areaSqm-a.areaSqm).slice(0,12)})),modelChanged:false}))
|