-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This demonstrator is based on iTowns 2.42 with some preview functionnalities: - support of COPC datasets (iTowns/itowns#2110) - support of more LAS attributes (iTowns/itowns#2262) - support of workers for LAS-based format (i.e. EPT, COPC)
- Loading branch information
1 parent
7097ff5
commit 5cb3cf5
Showing
4 changed files
with
237 additions
and
37 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
// @ts-ignore: added in 2.42 | ||
import { PNTS_MODE, PNTS_SHAPE, PNTS_SIZE_MODE } from 'itowns'; | ||
import GUI from 'lil-gui'; | ||
|
||
import type { View, PointCloudLayer } from 'itowns'; | ||
|
||
// TODO: export type or define our own gradients here | ||
const GRADIENTS = [ | ||
'SPECTRAL', | ||
'PLASMA', | ||
'YELLOW_GREEN', | ||
'VIRIDIS', | ||
'INFERNO', | ||
'GRAYSCALE', | ||
'TURBO', | ||
'RAINBOW', | ||
'CONTOUR', | ||
]; | ||
|
||
interface PointCloudGUIOptions { | ||
autoPlace?: boolean; | ||
container?: HTMLElement; | ||
width?: number; | ||
title?: string; | ||
closeFolders?: boolean; | ||
injectStyles?: boolean; | ||
touchStyles?: number; | ||
parent?: GUI; | ||
} | ||
|
||
export class PointCloudGUI extends GUI { | ||
pointUI: GUI; | ||
attributeUI: GUI; | ||
|
||
constructor(view: View, layer: PointCloudLayer, options: PointCloudGUIOptions) { | ||
super(options); | ||
|
||
const material = layer.material; | ||
|
||
const update = () => view.notifyChange(layer, true); | ||
this.add(layer, 'visible') | ||
.name('Visible') | ||
.onChange(update); | ||
this.add(layer, 'opacity', 0, 1) | ||
.name('Opacity') | ||
.onChange(update); | ||
this.add(layer, 'pointBudget', 0, 12000000) | ||
.step(500000) | ||
.name('Point budget') | ||
.onChange(update); | ||
this.add(layer, 'sseThreshold') | ||
.name('SSE threshold') | ||
.onChange(update); | ||
|
||
|
||
// Point styling | ||
this.pointUI = this.addFolder('Points'); | ||
const addPointSize = (obj: object, prop: string, name: string) => | ||
this.pointUI.add(obj, prop, 0, 15) | ||
.step(0.1) | ||
.name(name) | ||
.onChange(update); | ||
|
||
this.pointUI.add(material, 'sizeMode', PNTS_SIZE_MODE) | ||
.name('Size mode') | ||
.onChange(update); | ||
this.pointUI.add(material, 'shape', PNTS_SHAPE) | ||
.name('Shape') | ||
.onChange(update); | ||
addPointSize(layer, 'pointSize', 'Size'); | ||
addPointSize(material, 'minAttenuatedSize', 'Min size'); | ||
addPointSize(material, 'maxAttenuatedSize', 'Max size'); | ||
|
||
|
||
// Attribute styling | ||
this.attributeUI = this.addFolder('Attributes'); | ||
const addUint16Property = (obj: object, prop: string) => | ||
this.attributeUI.add(obj, prop, 0, 65535) | ||
.step(1) | ||
.onChange(update); | ||
|
||
const mode = this.attributeUI.add(material, 'mode', PNTS_MODE) | ||
.name('Mode') | ||
.onChange(update); | ||
|
||
const gradient = this.attributeUI.add(material, 'gradient', GRADIENTS) | ||
.name('Gradient') | ||
.hide() | ||
.onChange(update); | ||
|
||
const minIntensity = addUint16Property(layer, 'minIntensityRange') | ||
.name('Min intensity') | ||
.hide(); | ||
|
||
const maxIntensity = addUint16Property(layer, 'maxIntensityRange') | ||
.name('Max intensity') | ||
.hide(); | ||
|
||
const minScanAngle = this.attributeUI.add(layer, 'minAngleRange', 0, 90) | ||
.name('Min scan angle') | ||
.hide(); | ||
|
||
const maxScanAngle = this.attributeUI.add(layer, 'maxAngleRange', 0, 90) | ||
.name('Max scan angle') | ||
.hide(); | ||
|
||
mode.onFinishChange((event: PNTS_MODE) => { | ||
gradient.hide(); | ||
minIntensity.hide(); | ||
maxIntensity.hide(); | ||
minScanAngle.hide(); | ||
maxScanAngle.hide(); | ||
switch (event) { | ||
case PNTS_MODE.INTENSITY: | ||
gradient.show(); | ||
minIntensity.show(); | ||
maxIntensity.show(); | ||
return; | ||
// @ts-ignore: not released yet | ||
case PNTS_MODE.ELEVATION: | ||
// TODO: minElevation/maxElevation when layer ready | ||
gradient.show(); | ||
return; | ||
// @ts-ignore: not released yet | ||
case PNTS_MODE.SCAN_ANGLE: | ||
minScanAngle.show(); | ||
maxScanAngle.show(); | ||
return; | ||
} | ||
}); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,94 @@ | ||
import * as itowns from 'itowns'; | ||
import { Vector3 } from 'three'; | ||
import { View, PlanarControls, PNTS_SHAPE } from 'itowns'; | ||
// @ts-ignore: COPCSource, COPCLayer not released yet | ||
import { CopcSource, CopcLayer } from 'itowns'; | ||
import GUI from 'lil-gui'; | ||
|
||
import { PointCloudGUI } from './debug/PointCloudGUI'; | ||
|
||
import type { PerspectiveCamera } from 'three'; | ||
import type { PointCloudLayer } from 'itowns'; | ||
|
||
// TODO: add types not officially released in this repository | ||
|
||
const uri = new URL(window.location.href); | ||
const gui = new GUI(); | ||
|
||
// Get our `<div id="viewerId">` element. When creating a `View`, a canvas will | ||
// be appended to this element. | ||
const viewerDiv = document.getElementById('viewerDiv') as HTMLDivElement; | ||
const view = new View('EPSG:4326', viewerDiv); | ||
|
||
// @ts-ignore: argument not in type definitions | ||
const controls = new PlanarControls(view); | ||
view.mainLoop.gfxEngine.renderer.setClearColor(0xdddddd); | ||
|
||
let layer: PointCloudLayer; // COPCLayer | ||
|
||
|
||
function onLayerReady(layer: PointCloudLayer) { | ||
const camera = view.camera.camera3D as PerspectiveCamera; | ||
|
||
const lookAt = new Vector3(); | ||
const size = new Vector3(); | ||
const root = layer.root; | ||
|
||
root.bbox.getSize(size); | ||
root.bbox.getCenter(lookAt); | ||
|
||
camera.far = 2.0 * size.length(); | ||
|
||
controls.groundLevel = root.bbox.min.z; | ||
const position = root.bbox.min.clone().add( | ||
size.multiply({ x: 1, y: 1, z: size.x / size.z }), | ||
); | ||
|
||
camera.position.copy(position); | ||
camera.lookAt(lookAt); | ||
camera.updateProjectionMatrix(); | ||
|
||
view.notifyChange(camera); | ||
} | ||
|
||
|
||
function setUrl(url: string) { | ||
if (!url) return; | ||
|
||
uri.searchParams.set('copc', url); | ||
history.replaceState(null, '', `?${uri.searchParams.toString()}`); | ||
|
||
load(url); | ||
} | ||
|
||
|
||
function load(url: string) { | ||
// @ts-ignore: not released yet | ||
const source = new CopcSource({ url }); | ||
|
||
if (layer) { | ||
view.removeLayer('COPC'); | ||
view.notifyChange(); | ||
layer.delete(); | ||
} | ||
|
||
// @ts-ignore: not released yet | ||
layer = new CopcLayer('COPC', { | ||
source, | ||
crs: view.referenceCrs, | ||
sseThreshold: 1, | ||
pointBudget: 3500000, | ||
material: { | ||
minAttenuatedSize: 2, | ||
maxAttenuatedSize: 5, | ||
shape: PNTS_SHAPE.SQUARE, | ||
}, | ||
}); | ||
view.addLayer(layer).then(onLayerReady); | ||
new PointCloudGUI(view, layer, { | ||
title: layer.id, | ||
parent: gui | ||
}); | ||
} | ||
|
||
// Define an initial camera position | ||
const placement = { | ||
coord: new itowns.Coordinates('EPSG:4326', 2.351323, 48.856712), | ||
range: 25000000, | ||
}; | ||
|
||
// Create an empty Globe View | ||
const view = new itowns.GlobeView(viewerDiv, placement); | ||
|
||
// Declare your data source configuration. In this context, those are the | ||
// parameters used in the WMTS requests. | ||
const orthoConfig = { | ||
'url': 'https://data.geopf.fr/wmts', | ||
'crs': 'EPSG:3857', | ||
'format': 'image/jpeg', | ||
'name': 'ORTHOIMAGERY.ORTHOPHOTOS', | ||
'tileMatrixSet': 'PM', | ||
}; | ||
|
||
// Instantiate the WMTS source of your imagery layer. | ||
const imagerySource = new itowns.WMTSSource(orthoConfig); | ||
|
||
// Create your imagery layer | ||
const imageryLayer = new itowns.ColorLayer('imagery', { | ||
source: imagerySource, | ||
}); | ||
|
||
// Add it to source view! | ||
view.addLayer(imageryLayer); | ||
const copcParams = uri.searchParams.get('copc'); | ||
if (copcParams) { | ||
setUrl(copcParams); | ||
} |