| 123456789101112131415161718192021222324 |
- // Per-building north-up ENU roof imagery for Blender UVs, local-only provenance.
- import assert from 'node:assert/strict'
- import {readFileSync,writeFileSync} from 'node:fs'
- import {resolve,join} from 'node:path'
- import {createHash} from 'node:crypto'
- import jpeg from 'jpeg-js'
- import {PNG} from 'pngjs'
- import {enuToGeodetic} from './roof-geodesy.mjs'
- import {roofRasterSize,sharpenRoofRgba} from './roof-raster-quality.mjs'
- const dir=resolve('.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/negative-estate-pilot/input-v2'),plan=JSON.parse(readFileSync(join(dir,'seated-plan.json')))
- const tiles=new Map(),zoom=19,n=2**zoom,textures={},sha=b=>createHash('sha256').update(b).digest('hex')
- function tile(x,y){const path=`D:/GIS/GIS/yuanlingPic3/${zoom}/${x}/${n-1-y}.png`;if(!tiles.has(path)){const bytes=readFileSync(path),image=bytes[0]===255?jpeg.decode(bytes):PNG.sync.read(bytes);assert.equal(image.width,256);assert.equal(image.height,256);tiles.set(path,{image,path,sha256:sha(bytes)})}return tiles.get(path)}
- for(const b of plan.buildings){
- const low=[0,1].map(a=>Math.min(...b.footprintENU.map(p=>p[a]))),high=[0,1].map(a=>Math.max(...b.footprintENU.map(p=>p[a]))),span=high.map((v,a)=>v-low[a])
- const [width,height]=roofRasterSize(span,{maxDimension:512,metresPerPixel:.263}),rgba=Buffer.alloc(width*height*4),used=new Set()
- for(let y=0;y<height;y++)for(let x=0;x<width;x++){
- const g=enuToGeodetic(plan.transform,low[0]+(x+.5)/width*span[0],high[1]-(y+.5)/height*span[1],b.positionENU[2])
- const px=(g.longitude/Math.PI+1)/2*n*256,py=(1-Math.asinh(Math.tan(g.latitude))/Math.PI)/2*n*256,tx=Math.floor(px/256),ty=Math.floor(py/256),source=tile(tx,ty)
- used.add(source.path);const offset=(Math.floor(py-ty*256)*256+Math.floor(px-tx*256))*4;source.image.data.copy(rgba,(y*width+x)*4,offset,offset+4)
- }
- const processed=sharpenRoofRgba(rgba,width,height,.25),image=jpeg.encode({data:processed,width,height},92).data,path=join(dir,b.id+'-roof.jpg');writeFileSync(path,image)
- textures[b.id]={path,boundsENU:[low,high],size:[width,height],source:'local z19 EPSG3857/TMS reprojected to original seated ENU',zoom,planeHeight:b.positionENU[2],imageSha256:sha(image),rawRgbaSha256:sha(rgba),processedRgbaSha256:sha(processed),sharpenAmount:.25,jpegQuality:92,sourceTiles:[...used].map(p=>({path:p,sha256:tiles.get(p).sha256})),caveat:'roof texture from undated satellite; facade/height estimates; no source pixels claimed as surveyed facade'}
- }
- writeFileSync(join(dir,'roof-textures.json'),JSON.stringify(textures,null,2));console.log(JSON.stringify({passed:true,textures:Object.entries(textures).map(([id,r])=>({id,size:r.size,tiles:r.sourceTiles.length})),modelConfigured:false}))
|