Finish virtual profile
This commit is contained in:
parent
46db218492
commit
913af8fba6
3 changed files with 298 additions and 132 deletions
|
@ -23,7 +23,7 @@ import { buildCoordinateGrid } from "./utils/build-coordinate-grid";
|
|||
import { DragControls } from "three/examples/jsm/Addons.js";
|
||||
import { MapTilerProvider, MapView, OpenStreetMapsProvider } from "geo-three";
|
||||
import { CustomMapHeightNodeShader } from "./CustomMapHeightNodeShader";
|
||||
import { createSVG } from "./utils/create-borehole-svg";
|
||||
import { Data, createSVG } from "./utils/create-borehole-svg";
|
||||
|
||||
export type CustomEvent = CustomEventInit<{
|
||||
element: SVGSVGElement | null;
|
||||
|
@ -41,6 +41,7 @@ export class SceneView extends EventTarget {
|
|||
private _startY: number = 0;
|
||||
private _isDragging: boolean = false;
|
||||
private static _DRAG_THRESHOLD = 5;
|
||||
private _callback: EventListenerOrEventListenerObject | null = null;
|
||||
|
||||
constructor(
|
||||
scene: Scene,
|
||||
|
@ -183,21 +184,36 @@ export class SceneView extends EventTarget {
|
|||
const meshes = this._model.children.filter((c) => c.name !== "Topography");
|
||||
const intersects = this._raycaster.intersectObjects(meshes, true);
|
||||
|
||||
// Remove existing point and add visual marker
|
||||
this._removePoint();
|
||||
this._addPoint(targetPosition);
|
||||
|
||||
// Iterate over intersections
|
||||
if (intersects.length > 0) {
|
||||
const data = [];
|
||||
const data: Data[] = [];
|
||||
for (let i = 0; i < intersects.length; i += 2) {
|
||||
const depthStart = intersects[i].point.z;
|
||||
const depthEnd = intersects[i + 1].point.z;
|
||||
let name = intersects[i].object.name;
|
||||
let color = `#${(
|
||||
const name = intersects[i].object.name;
|
||||
const color = `#${(
|
||||
(intersects[i].object as Mesh).material as MeshStandardMaterial
|
||||
).color.getHexString()}`;
|
||||
|
||||
data.push({ depthStart, depthEnd, name, color });
|
||||
// Avoid duplicate entries, just update the depth information
|
||||
const index = data.findIndex((d) => d.name === name);
|
||||
if (index > -1) {
|
||||
data[index] = {
|
||||
depthStart: data[index].depthStart,
|
||||
depthEnd,
|
||||
name,
|
||||
color,
|
||||
};
|
||||
} else {
|
||||
data.push({ depthStart, depthEnd, name, color });
|
||||
}
|
||||
}
|
||||
|
||||
const element = createSVG(data, 400, 800, this._extent);
|
||||
const element = createSVG(data, 400, 600, this._extent);
|
||||
const event = new CustomEvent("svg-created", {
|
||||
detail: { element },
|
||||
});
|
||||
|
@ -226,10 +242,14 @@ export class SceneView extends EventTarget {
|
|||
}
|
||||
};
|
||||
|
||||
enableRaycaster() {
|
||||
enableRaycaster(callback: EventListenerOrEventListenerObject) {
|
||||
this._container.addEventListener("pointerdown", this._pointerDownListener);
|
||||
this._container.addEventListener("pointermove", this._pointerMoveListener);
|
||||
this._container.addEventListener("pointerup", this._pointerUpListener);
|
||||
|
||||
// Add event listener for svg-created event
|
||||
this.addEventListener("svg-created", callback);
|
||||
this._callback = callback;
|
||||
}
|
||||
|
||||
disableRaycaster() {
|
||||
|
@ -242,16 +262,30 @@ export class SceneView extends EventTarget {
|
|||
this._pointerMoveListener
|
||||
);
|
||||
this._container.removeEventListener("pointerup", this._pointerUpListener);
|
||||
|
||||
if (this._callback) {
|
||||
this.removeEventListener("svg-created", this._callback);
|
||||
}
|
||||
}
|
||||
|
||||
// Add point marker for bore profiles
|
||||
private _addPoint(point: Vector3) {
|
||||
const geometry = new SphereGeometry(2500, 16, 16); // Small sphere
|
||||
const material = new MeshBasicMaterial({ color: 0xff0000 }); // Red color
|
||||
const geometry = new SphereGeometry(1000, 16, 16);
|
||||
const material = new MeshBasicMaterial({ color: 0xff0000 });
|
||||
const sphere = new Mesh(geometry, material);
|
||||
sphere.name = "point-marker";
|
||||
|
||||
sphere.position.set(point.x, point.y, point.z);
|
||||
this._scene.add(sphere);
|
||||
}
|
||||
|
||||
private _removePoint() {
|
||||
const o = this._scene.getObjectByName("point-marker");
|
||||
|
||||
if (o) {
|
||||
this._scene.remove(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function init(container: HTMLElement, modelId = MODEL_ID) {
|
||||
|
|
|
@ -2,9 +2,10 @@ import * as d3 from "d3";
|
|||
import { Extent } from "./build-scene";
|
||||
|
||||
// SVG dimensions
|
||||
const margin = { top: 20, right: 250, bottom: 20, left: 20 };
|
||||
const margin = { top: 20, right: 20, bottom: 20, left: 80 };
|
||||
const barWidth = 30;
|
||||
|
||||
interface Data {
|
||||
export interface Data {
|
||||
depthStart: number;
|
||||
depthEnd: number;
|
||||
name: string;
|
||||
|
@ -17,7 +18,6 @@ export function createSVG(
|
|||
height: number = 800,
|
||||
extent: Extent
|
||||
) {
|
||||
console.log(data);
|
||||
const svg = d3
|
||||
.create("svg")
|
||||
.attr("width", width)
|
||||
|
@ -27,34 +27,110 @@ export function createSVG(
|
|||
|
||||
// Scales: Invert Y-axis so depth increases downward
|
||||
const zmax = d3.max(data, (d) => d.depthStart) ?? extent.zmax;
|
||||
const zmin = d3.max(data, (d) => d.depthEnd) ?? extent.zmin;
|
||||
const zmin = d3.min(data, (d) => d.depthEnd) ?? extent.zmin;
|
||||
const zScale = d3
|
||||
.scaleLinear()
|
||||
.domain([zmax, zmin])
|
||||
.range([margin.top, height - margin.bottom]);
|
||||
|
||||
// Create logical group
|
||||
const barGroup = svg.append("g");
|
||||
|
||||
// Draw bars (formations)
|
||||
svg
|
||||
.append("g")
|
||||
.selectAll()
|
||||
barGroup
|
||||
.selectAll("rect")
|
||||
.data(data)
|
||||
.join("rect")
|
||||
.attr("x", margin.left)
|
||||
.attr("y", (d) => zScale(d.depthStart))
|
||||
.attr("height", (d) => zScale(d.depthEnd) - zScale(d.depthStart))
|
||||
.attr("width", width - margin.left - margin.right)
|
||||
.attr("width", barWidth)
|
||||
.attr("fill", (d) => d.color);
|
||||
|
||||
// Add labels (formation names)
|
||||
svg
|
||||
.selectAll(".label")
|
||||
barGroup
|
||||
.selectAll("text")
|
||||
.data(data)
|
||||
.enter()
|
||||
.append("text")
|
||||
.attr("class", "label")
|
||||
.attr("x", width - margin.right + 5) // Place text slightly outside the bar
|
||||
.attr("y", (d) => (zScale(d.depthStart) + zScale(d.depthEnd)) / 2) // Center in the bar
|
||||
.text((d) => d.name);
|
||||
.join("text")
|
||||
.attr("x", margin.left + barWidth + 5)
|
||||
.attr("y", (d) => (zScale(d.depthStart) + zScale(d.depthEnd)) / 2)
|
||||
.attr("text-anchor", "start")
|
||||
.attr("fill", "black")
|
||||
.style("font-size", "12px")
|
||||
.each(function (d) {
|
||||
const textElement = d3.select(this);
|
||||
textElement.selectAll("tspan").remove(); // Clear previous tspans
|
||||
|
||||
const groups = groupWordsByFour(d.name);
|
||||
let dy = 0;
|
||||
|
||||
for (const group of groups) {
|
||||
textElement
|
||||
.append("tspan")
|
||||
.attr("x", margin.left + barWidth + 5)
|
||||
.attr("dy", dy)
|
||||
.text(group.join(" "));
|
||||
|
||||
dy = 14;
|
||||
}
|
||||
});
|
||||
|
||||
// Add depth labels
|
||||
svg
|
||||
.append("g")
|
||||
.selectAll("text")
|
||||
.data(data)
|
||||
.join("text")
|
||||
.attr("x", margin.left - 5)
|
||||
.attr("y", (d) =>
|
||||
d.depthStart - d.depthEnd < 100
|
||||
? zScale(d.depthStart) - 10
|
||||
: zScale(d.depthStart)
|
||||
)
|
||||
.attr("dy", "0.35em")
|
||||
.attr("text-anchor", "end")
|
||||
.attr("fill", "black")
|
||||
.style("font-size", "12px")
|
||||
.text((d) => `${d.depthStart.toFixed(0)}m`);
|
||||
|
||||
// Add label for last depth
|
||||
svg
|
||||
.append("g")
|
||||
.selectAll("text")
|
||||
.data(data)
|
||||
.join("text")
|
||||
.attr("x", margin.left - 5)
|
||||
.attr("y", (d, i) => (i === data.length - 1 ? zScale(d.depthEnd) : null))
|
||||
.attr("dy", "0.35em")
|
||||
.attr("text-anchor", "end")
|
||||
.attr("fill", "black")
|
||||
.style("font-size", "12px")
|
||||
.text((d, i) => (i === data.length - 1 ? `${d.depthEnd.toFixed(0)}m` : ""));
|
||||
|
||||
return svg.node();
|
||||
}
|
||||
|
||||
// Group words to split lines if necessary
|
||||
function groupWordsByFour(inputString: string) {
|
||||
const words = inputString.split(" ");
|
||||
|
||||
// Use reduce to group the words into chunks of four
|
||||
const groups = words.reduce(
|
||||
(result: string[][], word: string, index: number) => {
|
||||
const groupIndex = Math.floor(index / 4);
|
||||
|
||||
// If the group doesn't exist yet, create an empty array for it
|
||||
if (!result[groupIndex]) {
|
||||
result[groupIndex] = [];
|
||||
}
|
||||
|
||||
// Add the current word to the correct group
|
||||
result[groupIndex].push(word);
|
||||
|
||||
return result;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue