Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Triangulate Vegetation #410

Merged
merged 8 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 2 additions & 14 deletions src/components/ThreeViewer/Meshes/VegetationMesh.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,15 @@
import React, { useMemo } from 'react'
import * as THREE from 'three'

const vegetationColors = [
'#27AD6B', // Light green
'#2DBE76', // mint
'#33CC80', //dull green
]

const VegetationMesh = ({ geometries }) => {
const randomColors = useMemo(() => {
return geometries.map(
() =>
vegetationColors[Math.floor(Math.random() * vegetationColors.length)],
)
}, [geometries])

return (
<>
{geometries.map((geometry, index) => (
<mesh key={index} geometry={geometry}>
<meshLambertMaterial
vertexColors={false}
color={randomColors[index]}
color='#3C9000'
shininess={0}
side={THREE.DoubleSide}
/>
</mesh>
Expand Down
103 changes: 2 additions & 101 deletions src/components/ThreeViewer/Terrain.jsx
Original file line number Diff line number Diff line change
@@ -1,106 +1,7 @@
import * as geotiff from 'geotiff'
import React, { useEffect, useState } from 'react'
import * as THREE from 'three'
import { mercator2meters } from '../../simulation/download'
import {
coordinatesWebMercator,
coordinatesXY15,
xyzBounds,
} from '../../simulation/location'

class ElevationManager {
static instancePromise = null
static state = 'uninitialized'

tiff = null
image = null
width = -1
height = -1
boundingBox = [0, 0, 0, 0]
cache = {}
requestedRegions = {}

static async toPoint3D(x, y) {
const me = await this.getInstance()
const xyscale = mercator2meters()
const [cx, cy] = coordinatesWebMercator

const px = (x - me.tiepoint[3]) / me.pixelScale[0]
const py = (y - me.tiepoint[4]) / -me.pixelScale[1]

const fx = Math.floor(px)
const fy = Math.floor(py)
const tl = await me.requestPixel(fx, fy)
const tr = await me.requestPixel(fx + 1, fy)
const bl = await me.requestPixel(fx, fy + 1)
const br = await me.requestPixel(fx + 1, fy + 1)

// bilinear interpolation
const tx = px % 1
const ty = py % 1
const qx = 1 - tx
const qy = 1 - ty
const z =
qx * qy * tl + // Top left
tx * qy * tr + // Top right
qx * ty * bl + // Bottom left
tx * ty * br // Botrom right

// Calculate normal
let dx =
-qy * tl + // Top left
qy * tr + // Top right
-ty * bl + // Bottom left
ty * tr // Botrom right
let dy =
-qx * tl + // Top left
-tx * tr + // Top right
qx * bl + // Bottom left
tx * br // Botrom right
dx /= xyscale * me.pixelScale[0]
dy /= xyscale * -me.pixelScale[1]
const len = Math.sqrt(dx * dx + dy * dy + 1.0)

return {
point: [xyscale * (x - cx), xyscale * (y - cy), z],
normal: [dx / len, dy / len, -1.0 / len],
}
}

static async getInstance() {
if (!this.instancePromise) {
this.instancePromise = this.init()
}
return this.instancePromise
}

static async init() {
this.instance = new ElevationManager()
let me = this.instance
me.tiff = await geotiff.fromUrl(
'https://maps.heidler.info/sonny_dtm_20.tif',
)
me.image = await this.instance.tiff.getImage()
me.pixelScale = me.image.fileDirectory.ModelPixelScale
me.tiepoint = me.image.fileDirectory.ModelTiepoint
return me
}

async requestPixel(px, py) {
const tile_x = Math.floor(px / 256) * 256 // Internal COG tile window start
const tile_y = Math.floor(py / 256) * 256 // Internal COG tile window start
const tile_key = [tile_x, tile_y]
if (!this.requestedRegions[tile_key]) {
// Only request each tile once
this.requestedRegions[tile_key] = this.image.readRasters({
window: [tile_x, tile_y, tile_x + 256, tile_y + 256],
interleave: true,
})
}
const region = await this.requestedRegions[tile_key]
return region[px - tile_x + (py - tile_y) * 256]
}
}
import { coordinatesXY15, xyzBounds } from '../../simulation/location'
import { ElevationManager } from '../../simulation/elevation'

/** Load an OSM map tile and return it as a THREE Mesh
*/
Expand Down
100 changes: 100 additions & 0 deletions src/simulation/elevation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as geotiff from 'geotiff'
import { mercator2meters } from './download'
import { coordinatesWebMercator } from './location'

/**
* Loads the tiles from geotiff terrain model to cache, so that requests to the geotiff are faster.
*/
export class ElevationManager {
static instancePromise = null
static state = 'uninitialized'

tiff = null
image = null
width = -1
height = -1
boundingBox = [0, 0, 0, 0]
cache = {}
requestedRegions = {}

static async toPoint3D(x, y) {
const me = await this.getInstance()
const xyscale = mercator2meters()
const [cx, cy] = coordinatesWebMercator

const px = (x - me.tiepoint[3]) / me.pixelScale[0]
const py = (y - me.tiepoint[4]) / -me.pixelScale[1]

const fx = Math.floor(px)
const fy = Math.floor(py)
const tl = await me.requestPixel(fx, fy)
const tr = await me.requestPixel(fx + 1, fy)
const bl = await me.requestPixel(fx, fy + 1)
const br = await me.requestPixel(fx + 1, fy + 1)

// bilinear interpolation
const tx = px % 1
const ty = py % 1
const qx = 1 - tx
const qy = 1 - ty
const z =
qx * qy * tl + // Top left
tx * qy * tr + // Top right
qx * ty * bl + // Bottom left
tx * ty * br // Botrom right

// Calculate normal
let dx =
-qy * tl + // Top left
qy * tr + // Top right
-ty * bl + // Bottom left
ty * tr // Botrom right
let dy =
-qx * tl + // Top left
-tx * tr + // Top right
qx * bl + // Bottom left
tx * br // Botrom right
dx /= xyscale * me.pixelScale[0]
dy /= xyscale * -me.pixelScale[1]
const len = Math.sqrt(dx * dx + dy * dy + 1.0)

return {
point: [xyscale * (x - cx), xyscale * (y - cy), z],
normal: [dx / len, dy / len, -1.0 / len],
}
}

static async getInstance() {
if (!this.instancePromise) {
this.instancePromise = this.init()
}
return this.instancePromise
}

static async init() {
this.instance = new ElevationManager()
let me = this.instance
me.tiff = await geotiff.fromUrl(
'https://maps.heidler.info/sonny_dtm_20.tif',
)
me.image = await this.instance.tiff.getImage()
me.pixelScale = me.image.fileDirectory.ModelPixelScale
me.tiepoint = me.image.fileDirectory.ModelTiepoint
return me
}

async requestPixel(px, py) {
const tile_x = Math.floor(px / 256) * 256 // Internal COG tile window start
const tile_y = Math.floor(py / 256) * 256 // Internal COG tile window start
const tile_key = [tile_x, tile_y]
if (!this.requestedRegions[tile_key]) {
// Only request each tile once
this.requestedRegions[tile_key] = this.image.readRasters({
window: [tile_x, tile_y, tile_x + 256, tile_y + 256],
interleave: true,
})
}
const region = await this.requestedRegions[tile_key]
return region[px - tile_x + (py - tile_y) * 256]
}
}
10 changes: 1 addition & 9 deletions src/simulation/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export async function mainSimulation(location) {
console.log('Vegetation Raster processed successfully')

console.log('Processing vegetation geometries...')
const vegetationGeometries = processVegetationData(
const vegetationGeometries = await processVegetationData(
vegetationRaster,
new THREE.Vector3(0, 0, 0),
30,
Expand Down Expand Up @@ -131,14 +131,6 @@ export async function mainSimulation(location) {
console.log('Vegetation processing completed')
}

//vegetationGeometries.surrounding.forEach((geom) => {
// scene.addShadingGeometry(geom)
//})
//vegetationGeometries.surrounding.forEach((geom) => {
// scene.addShadingGeometry(geom)
//})
//scene.addVegetationRaster(rasterData)

let numSimulations = window.numSimulations || 80
function loadingBarWrapperFunction(progress, total) {
return window.setSimulationProgress((progress * 100) / total)
Expand Down
Loading
Loading