survey-imagery.mjs 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. // Offline orthoimagery contact sheet with the original footprint outlines, no invented infill.
  2. import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'
  3. import { resolve } from 'node:path'
  4. import { PNG } from 'pngjs'
  5. import jpeg from 'jpeg-js'
  6. const dir = process.env.GIS_DIR || 'D:/GIS/GIS'
  7. const data = JSON.parse(readFileSync(dir + '/planet_110.059,28.258_110.744,28.625-shp/shpOutput/buildings.json'))
  8. const out = resolve('.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/imagery')
  9. mkdirSync(out, { recursive: true })
  10. const px = (lon, lat, z) => [
  11. ((lon + 180) / 360) * 2 ** z * 256,
  12. ((1 - Math.asinh(Math.tan((lat * Math.PI) / 180)) / Math.PI) / 2) * 2 ** z * 256,
  13. ]
  14. const sets = [
  15. ['south', 110.401, 28.428, 18],
  16. ['north', 110.395, 28.454, 18],
  17. ]
  18. for (const [name, lon, lat, z] of sets) {
  19. const center = px(lon, lat, z)
  20. const x0 = Math.floor(center[0] / 256) - 3,
  21. y0 = Math.floor(center[1] / 256) - 3
  22. const img = new PNG({ width: 1792, height: 1792 })
  23. img.data.fill(255)
  24. let tiles = 0
  25. for (let y = 0; y < 7; y++)
  26. for (let x = 0; x < 7; x++) {
  27. const file = dir + `/yuanlingPic3/${z}/${x0 + x}/${2 ** z - 1 - (y0 + y)}.png`
  28. if (!existsSync(file)) continue
  29. const bytes = readFileSync(file)
  30. const t = bytes[0] === 0xff ? jpeg.decode(bytes) : PNG.sync.read(bytes)
  31. PNG.bitblt(t, img, 0, 0, 256, 256, x * 256, y * 256)
  32. tiles++
  33. }
  34. writeFileSync(resolve(out, name + '-satellite.png'), PNG.sync.write(img))
  35. function line(a, b) {
  36. let steps = Math.ceil(Math.hypot(b[0] - a[0], b[1] - a[1]))
  37. for (let i = 0; i <= steps; i++) {
  38. let x = Math.round(a[0] + ((b[0] - a[0]) * i) / steps),
  39. y = Math.round(a[1] + ((b[1] - a[1]) * i) / steps)
  40. if (x < 0 || y < 0 || x >= img.width || y >= img.height) continue
  41. const k = (y * img.width + x) * 4
  42. img.data[k] = 255
  43. img.data[k + 1] = 208
  44. img.data[k + 2] = 0
  45. }
  46. }
  47. const inside = []
  48. for (const f of data.features) {
  49. let rings =
  50. f.geometry.type === 'Polygon' ? [f.geometry.coordinates[0]] : f.geometry.coordinates.map((p) => p[0])
  51. for (const r of rings) {
  52. let p = r.map((c) => {
  53. let a = px(...c, z)
  54. return [a[0] - x0 * 256, a[1] - y0 * 256]
  55. })
  56. if (p.every((c) => c[0] >= 0 && c[1] >= 0 && c[0] < img.width && c[1] < img.height)) {
  57. inside.push({ id: String(f.properties.osm_id), ring: r, px: p })
  58. for (let i = 1; i < p.length; i++) line(p[i - 1], p[i])
  59. }
  60. }
  61. }
  62. writeFileSync(resolve(out, name + '-footprints.png'), PNG.sync.write(img))
  63. writeFileSync(
  64. resolve(out, name + '-source.json'),
  65. JSON.stringify({ z, x0, y0, width: 1792, features: inside }, null, 2)
  66. )
  67. console.log({ name, tiles, footprints: inside.length, out })
  68. }