Public
Edited
Aug 16, 2024
Paused
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
points = {
// filter points --> delete same (x, y)-coordinates with different z-values
const rawPoints = data.attributes.POSITION.value;
const formattedPoints = []; //[[x1, y1, z1], ...]
for (let i=0; i<rawPoints.length; i+=3) {
formattedPoints.push([rawPoints[i], rawPoints[i+1], rawPoints[i+2]]);
}
if (filterData.includes("sort data (2.)")) {
console.log("noo")
const startTime = new Date();
// Sort the array based on the z-values in descending order
formattedPoints.sort((a, b) => b[2] - a[2]);
// Filter out duplicates based on x and y coordinates
let maxZValues = {};
for (let point of formattedPoints) {
// Create a string representation of the x and y coordinates
let coordString = `${point[0]},${point[1]}`;
// If the coordinates are not in the hash map or the current point has a greater z-value, update the hash map
if (!maxZValues[coordString] || point[2] > maxZValues[coordString][2]) {
maxZValues[coordString] = point;
}
}
// Convert the hash map to an array
const filteredData = Object.values(maxZValues);
const endTime = new Date()
mutable sortDataMS = endTime-startTime
return filteredData
} else {
return formattedPoints
}
}
Insert cell
grid = {
const startTime = new Date();
const grid = createGrid(data.attributes.POSITION.value)
const endTime = new Date();
mutable createGridMS = endTime-startTime;
console.log(`create grid function (without spatial indexing) ${endTime-startTime} ms`)
return grid
}
Insert cell
terrainMean = {
const startTime = new Date();
const terrain = createTerrain(grid);
const endTime = new Date();
mutable createTerrainMS = endTime-startTime;
console.log(`create terrain function ${endTime-startTime} ms`)
return terrain
}
Insert cell
terrainGridBased = {
let startTime = new Date();
const grid = gridBased(points)
let endTime = new Date();
mutable gridBasedMS = endTime-startTime;

startTime = new Date();
const terrain = createTerrain(grid);
endTime = new Date();
mutable createTerrainMS = endTime-startTime
return terrain
}
Insert cell
gridBased = (points) => {
const data = points;

const startTime = new Date();
// Create an empty grid
let grid = new Array(gridSize);
for (let i = 0; i < gridSize; i++) {
grid[i] = new Array(gridSize);
for (let j = 0; j < gridSize; j++) {
grid[i][j] = [];
}
}
// Find the min and max x and y values
const maxX = bbox.maxX
const maxY = bbox.maxY
const minX = bbox.minX
const minY = bbox.minY
// Calculate the width and height of each cell
let cellWidth = (maxX - minX) / (gridSize-1);
let cellHeight = (maxY - minY) / (gridSize-1);
// Sort the points into the grid
for (let point of data) {
let x = point[0];
let y = point[1];
let i = Math.floor((x - minX) / cellWidth);
let j = Math.floor((y - minY) / cellHeight);
grid[i][j].push(point);
}
return grid
}
Insert cell
Insert cell
Insert cell
createGrid = (points) => {
// create uniform grid
const formattedPoints = []; //[[x1, y1, z1], ...]
for (let i=0; i<points.length; i+=3*nth) {
formattedPoints.push([points[i], points[i+1], points[i+2]]);
}

// sort formattedPoints for x-coordinates
formattedPoints.sort((a, b) => {
if (a[0] < b[0]) {
return -1;
} else if (a[0] > b[0]) {
return 1;
} else {
return 0;
}
});

// split this array into gridsize-length arrays with distance between first and last element of aequidistantFactorX
const colArray = [];
let tempArray = [];
let xStart = bbox.minX;
let counterCol = 1;
for (let i=0; i<formattedPoints.length; i++) {
if (formattedPoints[i][0]-xStart >= counterCol*aequidistance[0]) {
colArray.push(tempArray);
tempArray = [];
counterCol += 1;
} else tempArray.push(formattedPoints[i])
}

// sort each x-column for y-coordinates
colArray.forEach((col) => col.sort((a, b) => {
if (a[1] < b[1]) {
return -1;
} else if (a[1] > b[1]) {
return 1;
} else {
return 0;
}
}));

// split each column into gridsize-length arrays with distance between first and last element of aequidistantFactorY
const xColumns = [];
let yStart = bbox.minY;
let xColumn = []
let counterRow = 1;
colArray.forEach((col) => {
xColumn = [];
tempArray = [];
counterRow = 1;
for (let i=0; i<col.length; i++) {
if (col[i][1]-yStart >= counterRow*aequidistance[1]) {
xColumn.push(tempArray);
tempArray = [];
counterRow += 1;
} else tempArray.push(col[i])
}
xColumns.push(xColumn);
})
return xColumns //sorted array of shape (gridsize, gridsize, number of Points in each cell)
}
Insert cell
createTerrain = (grid) => {
// TODO: before applying average, filter for same (x,y)-coordinates --> just use the one with the greatest z-value
// define each pixel in final grid as mean of z-values
let finalGrid = new Float32Array(gridSize * gridSize);
grid.forEach((col, x) => {
let latestMean = 0;
col.forEach((row, y) => {
let mean = 0;
if(row.length) {
mean = latestMean = d3.mean(row.map((elem) => elem[2]));
} else {
mean = latestMean;
}
finalGrid[x*gridSize+y] = mean;
});
});
return finalGrid
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
index = {
const start = new Date();
const points = data.attributes.POSITION.value;
const index = new Flatbush(points.length / 3);

for (let i = 0; i < points.length; i += 3) {
index.add(points[i], points[i + 1], points[i], points[i + 1]);
}
// perform the indexing
index.finish();
const end = new Date();
mutable indexingMS = end - start;
return index;
}
Insert cell
terrain = {
const start = new Date();
const arr = new Float32Array(gridSize * gridSize);
let ti = 0;
for (let x = 0; x < gridSize; ++x) {
for (let y = 0; y < gridSize; ++y) {
const xPos = bbox.minX + aequidistance[0] * x;
const yPos = bbox.minY + aequidistance[1] * y;
const i = index.neighbors(xPos, yPos, 1)[0];
arr[ti++] = data.attributes.POSITION.value[i * 3 + 2];
}
}
const end = new Date();
mutable terrainMS = end - start;
return arr;
}
Insert cell
geometryGridBased = {
const error = 0;
const martini = new Martini(gridSize);
const tile = martini.createTile(terrainGridBased);
const mesh = tile.getMesh(error);

const geometry = new THREE.BufferGeometry();

const vertices = new Float32Array((mesh.vertices.length / 2) * 3);
const terrainExaggeration = 1;

let index = 0;
for (let i = 0; i < mesh.vertices.length / 2; i++) {
let x = mesh.vertices[i * 2],
y = mesh.vertices[i * 2 + 1];
vertices[index++] = x * aequidistance[0] + bbox.minX;
vertices[index++] = y * aequidistance[1] + bbox.minY;
vertices[index++] = terrainGridBased[x * gridSize + y] * terrainExaggeration;
}

geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
geometry.setIndex(new THREE.BufferAttribute(mesh.triangles, 1));
geometry.computeVertexNormals();
geometry.computeBoundingBox();

// calc uvs to correctly position texture
let { min, max } = geometry.boundingBox;

let offset = new THREE.Vector2(0 - min.x, 0 - min.y);
let range = new THREE.Vector2(max.x - min.x, max.y - min.y);

const position = geometry.attributes.position;
return geometry;
}
Insert cell
geometryMean = {
const error = 0;
const martini = new Martini(gridSize);
const tile = martini.createTile(terrainMean);
const mesh = tile.getMesh(error);

const geometry = new THREE.BufferGeometry();

const vertices = new Float32Array((mesh.vertices.length / 2) * 3);
const terrainExaggeration = 1;

let index = 0;
for (let i = 0; i < mesh.vertices.length / 2; i++) {
let x = mesh.vertices[i * 2],
y = mesh.vertices[i * 2 + 1];
vertices[index++] = x * aequidistance[0] + bbox.minX;
vertices[index++] = y * aequidistance[1] + bbox.minY;
vertices[index++] = terrainMean[x * gridSize + y] * terrainExaggeration;
}

geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
geometry.setIndex(new THREE.BufferAttribute(mesh.triangles, 1));
geometry.computeVertexNormals();
geometry.computeBoundingBox();

// calc uvs to correctly position texture
let { min, max } = geometry.boundingBox;

let offset = new THREE.Vector2(0 - min.x, 0 - min.y);
let range = new THREE.Vector2(max.x - min.x, max.y - min.y);

const position = geometry.attributes.position;
return geometry;
}
Insert cell
geometry = {
const start = new Date();
const error = 0;
const martini = new Martini(gridSize);
const tile = martini.createTile(terrain);
const mesh = tile.getMesh(error);

const geometry = new THREE.BufferGeometry();

const vertices = new Float32Array((mesh.vertices.length / 2) * 3);
const terrainExaggeration = 1;

let index = 0;
for (let i = 0; i < mesh.vertices.length / 2; i++) {
let x = mesh.vertices[i * 2],
y = mesh.vertices[i * 2 + 1];
vertices[index++] = x * aequidistance[0] + bbox.minX;
vertices[index++] = y * aequidistance[1] + bbox.minY;
vertices[index++] = terrain[x * gridSize + y] * terrainExaggeration;
}

geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
geometry.setIndex(new THREE.BufferAttribute(mesh.triangles, 1));
geometry.computeVertexNormals();
geometry.computeBoundingBox();

// calc uvs to correctly position texture
let { min, max } = geometry.boundingBox;

let offset = new THREE.Vector2(0 - min.x, 0 - min.y);
let range = new THREE.Vector2(max.x - min.x, max.y - min.y);

const position = geometry.attributes.position;

const martiniEnd = new Date();
mutable martiniMS = martiniEnd - start;

const uvs = [];

for (let i = 0; i < position.count; i++) {
const v3 = new THREE.Vector3().fromBufferAttribute(position, i);
uvs.push((v3.x + offset.x) / range.x);
uvs.push((v3.y + offset.y) / range.y);
}

geometry.setAttribute(
"uv",
new THREE.BufferAttribute(new Float32Array(uvs), 2)
);
geometry.setAttribute(
"uv2",
new THREE.BufferAttribute(new Float32Array(uvs), 2)
);

geometry.attributes.uv.needsUpdate = true;
geometry.attributes.uv2.needsUpdate = true;
const end = new Date();
console.log("Calculating UVs", end - martiniEnd);
return geometry;
}
Insert cell
camera = {
const camera = new THREE.PerspectiveCamera(
100,
Math.min(width, 640) / height,
0.1,
100000
);
camera.up.set(0, 0, 1);

camera.position.set(center.x, center.y, center.z + 10);

return camera;
}
Insert cell
scene = {
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);

// add geometry

const material = new THREE.MeshNormalMaterial({
flatShading: true,
side: THREE.DoubleSide
});

const floormaterial1 = new THREE.MeshPhongMaterial({
color: 0x0000ff,
opacity: 1,
// wireframe: true,
side: THREE.DoubleSide
});
const floormaterial2 = new THREE.MeshPhongMaterial({
color: 0xffff00,
opacity: 1,
// wireframe: true,
side: THREE.DoubleSide
});

const meshSpatial = new THREE.Mesh(geometry, floormaterial2);
meshSpatial.name = 'terrainSpatial';
//scene.add(meshSpatial);

const meshMean = new THREE.Mesh(geometryMean, material);
meshMean.name = 'terrainMean';
if (terrains.includes("loop sorting")) scene.add(meshMean);

const meshGridBased = new THREE.Mesh(geometryGridBased, material);
meshMean.name = 'terrainGridBased';
if (terrains.includes("grid based")) scene.add(meshGridBased);

//const meshMonte = new THREE.Mesh(geometryMonteCarlo, material);
//if (terrains.includes("Monte Carlo")) scene.add(meshMonte)

const map = await loadTexture(textureWMSUrl);

let threeMesh = new THREE.Mesh(
geometry,
new THREE.MeshBasicMaterial({ map: map, side: THREE.DoubleSide })
);
if (terrains.includes("spatial indexing")) scene.add(threeMesh)

return scene;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more