add dynamic loading of point clouds

name and visible changes are also passed to potree
This commit is contained in:
Tim Wundenberg
2021-08-10 19:22:32 +02:00
parent 3c76cb6583
commit f57782587b
13 changed files with 157 additions and 215 deletions

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@ obj/
.vscode/ .vscode/
node_modules/ node_modules/
*.csproj.user *.csproj.user
.idea .idea
/temp/

View File

@@ -14,7 +14,7 @@ namespace PointCloudWeb.Server.Controllers
public PointCloudInfoController(PointCloudService pointCloudService) public PointCloudInfoController(PointCloudService pointCloudService)
{ {
this._pointCloudService = pointCloudService; _pointCloudService = pointCloudService;
} }
private PointCloudInfoDto ConvertPointCloudToDto(PointCloud pc) => new PointCloudInfoDto(pc.Id, pc.Name); private PointCloudInfoDto ConvertPointCloudToDto(PointCloud pc) => new PointCloudInfoDto(pc.Id, pc.Name);
@@ -47,7 +47,8 @@ namespace PointCloudWeb.Server.Controllers
return new NotFoundResult(); return new NotFoundResult();
_pointCloudService.RemoveById(id); _pointCloudService.RemoveById(id);
return new OkResult(); //Json Result, becaus OkResult throws in Firefox an XML-Root element not found error.
return new JsonResult("OK");
} }
[HttpPut] [HttpPut]

View File

@@ -7,7 +7,7 @@ namespace PointCloudWeb.Server
static Globals() static Globals()
{ {
var basePath = Directory.GetCurrentDirectory() + "/../.."; var basePath = Directory.GetCurrentDirectory() + "/../..";
PotreeDataPath = basePath + "/PointCloudWeb.Web/public/Potree/pointclouds"; PotreeDataPath = basePath + "/PointCloudWeb.Web/public/Potree/pointclouds/generated";
PotreeConverterExe = basePath + "/PointCloudWeb.Server/Tools/PotreeConverter/PotreeConverter.exe"; PotreeConverterExe = basePath + "/PointCloudWeb.Server/Tools/PotreeConverter/PotreeConverter.exe";
TempPath = basePath + "/temp"; TempPath = basePath + "/temp";
CloudCompareExe = "C:/Program Files/CloudCompare/CloudCompare.exe"; CloudCompareExe = "C:/Program Files/CloudCompare/CloudCompare.exe";

View File

@@ -21,23 +21,19 @@ namespace PointCloudWeb.Server.Services
private void GeneratePotreeData(Guid id) private void GeneratePotreeData(Guid id)
{ {
var pathTarget = Globals.PotreeDataPath; var pathTarget = Globals.PotreeDataPath + $"/{id.ToString()}";
var converter = Globals.PotreeConverterExe;
var tempFile = Globals.TempPath + $"/{id}.las"; var tempFile = Globals.TempPath + $"/{id}.las";
Directory.CreateDirectory(Globals.TempPath);
var pc = _pointClouds.GetById(id); var pc = _pointClouds.GetById(id);
pc.WriteToLas(tempFile); pc.WriteToLas(tempFile);
var potreeConverter = new Process(); var potreeConverter = new Process();
potreeConverter.StartInfo.FileName = Globals.PotreeConverterExe; potreeConverter.StartInfo.FileName = Globals.PotreeConverterExe;
potreeConverter.StartInfo.Arguments = $"\"{tempFile}\" -o \"{Globals.TempPath}/{id.ToString()}\""; potreeConverter.StartInfo.Arguments = $"\"{tempFile}\" -o \"{Globals.TempPath}/{id.ToString()}\"";
potreeConverter.Start(); potreeConverter.Start();
potreeConverter.WaitForExit(); potreeConverter.WaitForExit();
Directory.Move(Globals.TempPath + "/" + id, pathTarget);
} }
private void InitSampleData() private void InitSampleData()
@@ -70,7 +66,7 @@ namespace PointCloudWeb.Server.Services
public IEnumerable<PointCloud> GetAll() => _pointClouds; public IEnumerable<PointCloud> GetAll() => _pointClouds;
public PointCloud GetById(Guid id) => _pointClouds.GetById(id); public PointCloud GetById(Guid id) => _pointClouds.GetById(id);
public void RegisterPointCloud(Guid id) public void RegisterPointCloud(Guid id)
{ {

View File

@@ -13,7 +13,13 @@ namespace PointCloudWeb.Server
public Startup(IConfiguration configuration) public Startup(IConfiguration configuration)
{ {
Configuration = configuration; Configuration = configuration;
Directory.Delete(Globals.TempPath, true);
if (Directory.Exists(Globals.TempPath))
Directory.Delete(Globals.TempPath, true);
if (Directory.Exists(Globals.PotreeDataPath))
Directory.Delete(Globals.PotreeDataPath, true);
Directory.CreateDirectory(Globals.TempPath);
Directory.CreateDirectory(Globals.PotreeDataPath);
} }
public IConfiguration Configuration { get; } public IConfiguration Configuration { get; }
@@ -36,10 +42,7 @@ namespace PointCloudWeb.Server
//app.UseAuthorization(); //app.UseAuthorization();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
{
endpoints.MapControllers();
});
} }
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.

View File

@@ -21,3 +21,5 @@ pnpm-debug.log*
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
/public/Potree/pointclouds/generated/*

View File

@@ -1858,9 +1858,9 @@
} }
}, },
"vue-loader-v16": { "vue-loader-v16": {
"version": "npm:vue-loader@16.4.0", "version": "npm:vue-loader@16.5.0",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.4.0.tgz", "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.5.0.tgz",
"integrity": "sha512-oySoEgEedGw0jz2czEOdb6sycQ2PPyzUmF8Yclj1NTpoTVHGCYCQaGf+jsvxhdDVfhOnDLjkRzT2fLpGOrVPjg==", "integrity": "sha512-WXh+7AgFxGTgb5QAkQtFeUcHNIEq3PGVQ8WskY5ZiFbWBkOwcCPRs4w/2tVyTbh2q6TVRlO3xfvIukUtjsu62A==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@@ -11366,90 +11366,6 @@
} }
} }
}, },
<<<<<<< HEAD
=======
"vue-loader-v16": {
"version": "npm:vue-loader@16.4.1",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.4.1.tgz",
"integrity": "sha512-nL1bDhfMAZgTVmVkOXQaK/WJa9zFDLM9vKHbh5uGv6HeH1TmZrXMWUEVhUrACT38XPhXM4Awtjj25EvhChEgXw==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
>>>>>>> a95e821330508d8ec6a5953c53ca70219d0ff815
"vue-router": { "vue-router": {
"version": "4.0.10", "version": "4.0.10",
"resolved": "https://registry.nlark.com/vue-router/download/vue-router-4.0.10.tgz", "resolved": "https://registry.nlark.com/vue-router/download/vue-router-4.0.10.tgz",

View File

@@ -79571,8 +79571,9 @@ ENDSEC
let node = createNode(pcID, pointcloud.name, cloudIcon, pointcloud); let node = createNode(pcID, pointcloud.name, cloudIcon, pointcloud);
pointcloud.addEventListener("visibility_changed", () => { pointcloud.addEventListener("visibility_changed", () => {
tree.jstree('rename_node', node, pointcloud.name);
if(pointcloud.visible){ if(pointcloud.visible){
tree.jstree('check_node', node); tree.jstree('check_node', node);
}else { }else {
tree.jstree('uncheck_node', node); tree.jstree('uncheck_node', node);
} }

View File

@@ -19,7 +19,6 @@
<script src="../libs/spectrum/spectrum.js"></script> <script src="../libs/spectrum/spectrum.js"></script>
<script src="../libs/jquery-ui/jquery-ui.min.js"></script> <script src="../libs/jquery-ui/jquery-ui.min.js"></script>
<script src="../libs/other/BinaryHeap.js"></script> <script src="../libs/other/BinaryHeap.js"></script>
<script src="../libs/tween/tween.min.js"></script> <script src="../libs/tween/tween.min.js"></script>
<script src="../libs/d3/d3.js"></script> <script src="../libs/d3/d3.js"></script>
@@ -33,16 +32,12 @@
<!-- INCLUDE ADDITIONAL DEPENDENCIES HERE --> <!-- INCLUDE ADDITIONAL DEPENDENCIES HERE -->
<!-- INCLUDE SETTINGS HERE --> <!-- INCLUDE SETTINGS HERE -->
<div class="potree_container" style="position: absolute; width: 100%; height: 100%; left: 0px; top: 0px; "> <div class="potree_container" style="position: absolute; width: 100%; height: 100%; left: 0; top: 0; ">
<div id="potree_render_area"
style="background-image: url('../build/potree/resources/images/background.jpg');"></div>
<div id="potree_sidebar_container"></div> <div id="potree_sidebar_container"></div>
<div id="potree_render_area"></div>
</div> </div>
<script type="module"> <script>
import * as THREE from "../libs/three.js/build/three.module.js";
window.viewer = new Potree.Viewer(document.getElementById("potree_render_area")); window.viewer = new Potree.Viewer(document.getElementById("potree_render_area"));
viewer.setEDLEnabled(true); viewer.setEDLEnabled(true);
@@ -51,107 +46,61 @@
viewer.setMinNodeSize(10); viewer.setMinNodeSize(10);
viewer.loadSettingsFromURL(); viewer.loadSettingsFromURL();
viewer.setDescription(`Sorvilier point cloud courtesy of <a target='_blank' href='https://www.sigeom.ch/'>sigeom sa</a>`);
viewer.loadGUI(() => { viewer.loadGUI(() => {
viewer.setLanguage('en'); viewer.setLanguage('en');
$("#menu_scene").next().show(); $("#menu_scene").next().show();
$("#menu_tools").next().show(); $("#menu_tools").next().show();
viewer.toggleSidebar(); // viewer.toggleSidebar();
}); });
// wrap load code into an async function so that we can use "await" let loadedPointClouds = [];
async function load() {
// specify point clouds that are to be loaded and callbacks to invoke async function loadByPc(pointCloud) {
let pointclouds = [ let doLoadPc = Potree.loadPointCloud("../pointclouds/generated/" + pointCloud.id + "/metadata.json");
{
url: "../pointclouds/test.las_converted/metadata.json",
callback: (pointcloud) => {
pointcloud.name = "sorvilier";
let material = pointcloud.material; let pc = (await doLoadPc).pointcloud;
material.size = 1;
material.pointSizeType = Potree.PointSizeType.ADAPTIVE;
},
}
// , {
// url: "../pointclouds/lion_takanawa/cloud.js",
// callback: (pointcloud) => {
// pointcloud.name = "lion 1";
//
// let material = pointcloud.material;
// material.pointSizeType = Potree.PointSizeType.ADAPTIVE;
//
// pointcloud.position.set(589927.44, 231479.04, 726.87);
// pointcloud.scale.set(10, 10, 10);
// pointcloud.rotation.set(0, 0, 0.8 * Math.PI);
// },
// }, {
// url: "../pointclouds/lion_takanawa/cloud.js",
// callback: (pointcloud) => {
// pointcloud.name = "lion 2";
//
// let material = pointcloud.material;
//
// material.size = 1;
// material.pointSizeType = Potree.PointSizeType.ADAPTIVE;
// material.activeAttributeName = "elevation";
// material.heightMin = 720;
// material.heightMax = 780;
//
// pointcloud.position.set(589997.44, 231479.04, 726.87);
// pointcloud.scale.set(10, 10, 10);
// pointcloud.rotation.set(0, 0, 0.8 * Math.PI);
// },
// }, {
// url: "../pointclouds/lion_takanawa/cloud.js",
// callback: (pointcloud) => {
// pointcloud.name = "lion 3";
//
// let material = pointcloud.material;
// material.pointSizeType = Potree.PointSizeType.ADAPTIVE;
//
// material.color = new THREE.Color().setRGB(0.4, 0.6, 0.7);
//
// pointcloud.position.set(589927.44 - 70, 231479.04, 726.87);
// pointcloud.scale.set(10, 10, 10);
// pointcloud.rotation.set(0, 0, 0.8 * Math.PI);
// },
// }, {
// url: "http://5.9.65.151/mschuetz/potree/resources/pointclouds/archpro/heidentor/cloud.js",
// callback: (pointcloud) => {
// pointcloud.name = "Heidentor";
//
// pointcloud.position.set(589817.920, 231358.010, 744.865);
// pointcloud.scale.set(6, 6, 6);
// pointcloud.rotation.z = -1.9;
// },
// }
];
// start loading all point clouds asynchronously, get a promise for each one that resolves when it's loaded
let promises = pointclouds.map(p => Potree.loadPointCloud(p.url));
// now iterate over all promises in order
for (let i = 0; i < promises.length; i++) {
// wait until this point cloud is loaded before processing the next one
let pointcloud = (await promises[i]).pointcloud;
viewer.scene.addPointCloud(pointcloud);
pointclouds[i].callback(pointcloud);
}
viewer.scene.view.setView( pc.name = pointCloud.name;//(' ' + pointCloud.name).slice(1);
[589974.341, 231698.397, 986.146],
[589851.587, 231428.213, 715.634], viewer.scene.addPointCloud(pc);
);
// let material = pc.material;
// material.size = 1;
// material.pointSizeType = Potree.PointSizeType.ADAPTIVE;
loadedPointClouds.push({id: pointCloud.id, pointCloud: pc})
} }
load(); window.loadFromArray = function (pointClouds) {
for (const i in pointClouds) {
let entry = loadedPointClouds.find(pc => pc.id === pointClouds[i].id)
if (entry != null) {
entry.pointCloud.name = pointClouds[i].name;
if (entry.pointCloud.visible !== pointClouds[i].visible)
entry.pointCloud.visible = pointClouds[i].visible;
else {
entry.pointCloud.visible = !entry.pointCloud.visible;
entry.pointCloud.visible = !entry.pointCloud.visible;
}
} else {
loadByPc(pointClouds[i])
}
}
let notFound = [...loadedPointClouds.filter(pc => pointClouds.find(ipc => ipc.id === pc.id) === null)];
for (let entry in notFound) {
// entry.pointCloud
//TODO: Remove from Potree
}
}
window.setVisible = function (targetId, val) {
let entry = loadedPointClouds.find(pc => pc.id === targetId)
entry.visible = val;
}
</script> </script>

View File

@@ -0,0 +1,59 @@
<template>
<div class="full-size">
<!-- <p>{{ scans === null ? scans[0].name : "" }}</p>-->
<iframe v-on:load="onLoadIframe" name="myIframe" class="full-size" ref="viewer"
src="Potree/examples/pcw.html"></iframe>
</div>
</template>
<script>
export default {
name: "PotreeViewer",
data() {
return {
oldScans: [],
iframe: null
}
},
computed: {
scans() {
return this.$store.state.pci.pointClouds
}
},
watch: {
scans: {
handler: function () {
this.pointCloudsChanged();
},
deep: true //To receive changes in array-objects, like name
}
},
methods: {
pointCloudsChanged() {
if (this.iframe !== null) {
this.iframe.window.loadFromArray(this.scans);
}
},
onLoadIframe() {
for (let i = 0; i < window.frames.length; i++) {
if (window.frames[i].name === "myIframe") {
this.iframe = window.frames[i];
break;
}
}
this.pointCloudsChanged();
}
}
}
</script>
<style scoped>
.full-size {
width: 100%;
height: Calc(100% - 5px);
border: 0;
}
</style>

View File

@@ -46,6 +46,10 @@ export default {
methods: { methods: {
onClickVisible() { onClickVisible() {
this.isVisible = !this.isVisible; this.isVisible = !this.isVisible;
this.$store.dispatch("pci/updateVisible", {
id: this.item.id,
visible: this.isVisible,
});
}, },
onEnter() { onEnter() {
this.onClickSave(); this.onClickSave();

View File

@@ -8,12 +8,11 @@ export default {
pointClouds: [], pointClouds: [],
loading: false loading: false
}, },
// getters: {
// pointClouds: state => state.pointClouds,
// loading: state => state.loading
// },
mutations: { mutations: {
SET_POINT_CLOUDS(state, pointClouds) { SET_POINT_CLOUDS(state, pointClouds) {
for (const i in pointClouds) {
pointClouds[i].visible = true;
}
state.pointClouds = pointClouds state.pointClouds = pointClouds
}, },
SET_LOADING(state, loading) { SET_LOADING(state, loading) {
@@ -25,12 +24,17 @@ export default {
return; return;
if (pointCloud.action === "update") { if (pointCloud.action === "update") {
pointCloud.data.visible = true;
state.pointClouds[index] = pointCloud.data; state.pointClouds[index] = pointCloud.data;
} }
else if (pointCloud.action === "remove") { else if (pointCloud.action === "remove") {
state.pointClouds.splice(index, 1); state.pointClouds.splice(index, 1);
} }
}, },
SET_VISIBLE(state, visibleInfo){
let pc = state.pointClouds.find(x => x.id === visibleInfo.id);
pc.visible = visibleInfo.visible;
},
}, },
actions: { actions: {
loadPointClouds({ commit }) { loadPointClouds({ commit }) {
@@ -68,6 +72,9 @@ export default {
alert(e); alert(e);
commit('SET_LOADING', false); commit('SET_LOADING', false);
}) })
},
updateVisible({ commit }, visibleInfo) {
commit('SET_VISIBLE', visibleInfo);
} }
} }
} }

View File

@@ -5,17 +5,20 @@
<h3>Point Clouds</h3> <h3>Point Clouds</h3>
<ul v-for="item in cloudItems" :key="item"> <ul v-for="item in cloudItems" :key="item">
<li><ScanItem :item="item" /></li> <li>
<ScanItem :item="item"/>
</li>
</ul> </ul>
</div> </div>
</div> </div>
<div id="map" > <div id="map">
<iframe src="Potree/examples/pcw.html"></iframe> <PotreeViewer/>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import ScanItem from "@/components/ScanItem"; import ScanItem from "@/components/ScanItem";
import PotreeViewer from "@/components/PotreeViewer";
export default { export default {
created() { created() {
@@ -26,18 +29,17 @@ export default {
return this.$store.state.pci.pointClouds; return this.$store.state.pci.pointClouds;
}, },
}, },
components: { ScanItem }, components: {PotreeViewer, ScanItem},
}; };
</script> </script>
<style scoped> <style scoped>
iframe { PotreeViewer {
width: 100%; width: 100%;
height: Calc(100% - 5px); height: 100%;
border: 0; border: 0;
} }
.map-grid-container { .map-grid-container {
text-align: left; text-align: left;
} }
@@ -70,6 +72,7 @@ a {
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
} }
.map-settings-internal { .map-settings-internal {
padding: 20px; padding: 20px;
} }
@@ -84,7 +87,7 @@ a {
grid-template-areas: "main right"; grid-template-areas: "main right";
grid-template-columns: calc(100vw - 325px) 325px; grid-template-columns: calc(100vw - 325px) 325px;
height: calc( height: calc(
100vh - 2em - 20px - 1px 100vh - 2em - 20px - 1px
); /*viewport height - height of navbar-button - padding - borderline*/ ); /*viewport height - height of navbar-button - padding - borderline*/
grid-gap: 0; grid-gap: 0;
padding: 0; padding: 0;