reconcile-large-building-coverage.mjs 3.7 KB

123456789101112131415161718192021222324252627282930
  1. // Reconcile historical missing-ID audit against the actual configured regional asset graph.
  2. import assert from 'node:assert/strict'
  3. import {readFileSync,writeFileSync} from 'node:fs'
  4. import {resolve,join,dirname} from 'node:path'
  5. import {createHash} from 'node:crypto'
  6. const root=resolve('tools/city-massing/city/cad-bim'),read=p=>JSON.parse(readFileSync(p)),sha=p=>createHash('sha256').update(readFileSync(p)).digest('hex')
  7. const config=read(resolve('public/cockpit/config.json')),plan=config.map.tilesets.find(p=>p.regionManifest)
  8. assert.ok(plan&&plan.url.startsWith('/gis/cad-bim/'))
  9. const manifestPath=join(root,plan.regionManifest.replace('/gis/cad-bim/','')),manifest=read(manifestPath),sourcePath=join(root,manifest.source,'manifest.json'),source=read(sourcePath),sources=new Map(source.records.filter(r=>r.level==='fine').map(r=>[resolve(dirname(sourcePath),r.uri),r]))
  10. const active=new Set(),owners=new Map(),seen=new Set(),perRegion=[]
  11. for(const region of manifest.regions){
  12. const count=new Set()
  13. for(const file of region.fineRecords){const path=resolve(dirname(sourcePath),file.sourceUri),record=sources.get(path);assert.ok(record,'untraced fine '+path);assert.ok(!seen.has(path),'fine reused');seen.add(path)
  14. assert.ok(record.buildingIds?.length);for(const id of record.buildingIds){assert.ok(!owners.has(id)||owners.get(id)===record.sourceCell,'building owned by multiple cells '+id);owners.set(id,record.sourceCell);active.add(id);count.add(id)}
  15. }
  16. perRegion.push({region:region.id,buildings:count.size})
  17. }
  18. assert.equal(seen.size,3023);assert.equal(active.size,30172);assert.equal(active.size,manifest.buildings)
  19. const auditPath=resolve('.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/large-building-audit/audit.json'),audit=read(auditPath),nearPath=join(root,'near-estate-pilot-input/seated-plan.json'),near=read(nearPath)
  20. const accounted=audit.candidates.filter(b=>active.has(b.id)),remaining=audit.candidates.filter(b=>!active.has(b.id)),counts={}
  21. for(const b of remaining)counts[b.status]=(counts[b.status]||0)+1
  22. assert.equal(near.buildings.length,13);assert.ok(near.buildings.every(b=>active.has(b.id)));assert.equal(accounted.length,13)
  23. const report={passed:true,configuredUrl:plan.url,configuredRegionManifest:plan.regionManifest,sourceBuildingIdsReferenced:active.size,evidenceLevel:'source-cell metadata IDs referenced by configured fine graph',geometryDecodedThisAudit:false,renderedCoverageValidated:false,activeBuildings:active.size,tracedFineFiles:seen.size,regions:perRegion.length,
  24. sourceHashes:{currentManifest:sha(manifestPath),sourceManifest:sha(sourcePath),historicalGapAudit:sha(auditPath),nearEstateInput:sha(nearPath)},
  25. historicalMissingCandidates:audit.candidateCount,nowCovered:accounted.map(b=>({id:b.id,areaSqm:b.areaSqm,previousStatus:b.status})),remainingCandidateCount:remaining.length,
  26. remainingUrbanCandidates:remaining.filter(b=>b.tier==='urban').length,remainingReasonCounts:counts,
  27. urbanSeatedOutsideSelection:remaining.filter(b=>b.tier==='urban'&&b.status==='seated-outside-active-selection').map(({id,areaSqm,center,terrainRelief})=>({id,areaSqm,center,terrainRelief})),
  28. perRegion,caveat:'Remaining IDs are unverified candidates, not confirmed missing buildings. No geometry added; no rural reinstatement. Existing 13 additions traced to actual fine asset source records.'}
  29. writeFileSync(process.argv[2]||'.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/active-large-building-reconciliation-v7.json',JSON.stringify(report,null,2))
  30. console.log(JSON.stringify({passed:report.passed,active:active.size,nowCovered:accounted.length,remaining:remaining.length,urbanRemaining:report.remainingUrbanCandidates,urbanSeatedOutsideSelection:report.urbanSeatedOutsideSelection.length,remainingReasonCounts:counts}))