Save/load potree measurements

pull/999/head
Piero Toffanin 2021-06-11 14:22:53 -04:00
rodzic cf554fdbfd
commit 8f760ecd7f
3 zmienionych plików z 169 dodań i 4 usunięć

Wyświetl plik

@ -1,5 +1,7 @@
from .tasks import TaskNestedView
from .common import get_and_check_project
from rest_framework.response import Response
from rest_framework import exceptions
class Scene(TaskNestedView):
def get(self, request, pk=None, project_pk=None):
@ -8,4 +10,47 @@ class Scene(TaskNestedView):
"""
task = self.get_and_check_task(request, pk)
return Response(task.potree_scene)
return Response(task.potree_scene)
def post(self, request, pk=None, project_pk=None):
"""
Store potree scene information (except camera view)
"""
get_and_check_project(request, project_pk, perms=("change_project", ))
task = self.get_and_check_task(request, pk)
scene = request.data
# Quick type check
if scene.get('type') != 'Potree':
raise exceptions.ValidationError(detail="Invalid potree scene")
for k in scene:
if not k in ["view", "pointclouds", "settings"]:
task.potree_scene[k] = scene[k]
task.save()
return Response({'success': True})
class CameraView(TaskNestedView):
def post(self, request, pk=None, project_pk=None):
"""
Store camera view information
"""
get_and_check_project(request, project_pk, perms=("change_project", ))
task = self.get_and_check_task(request, pk)
view = request.data
if not view:
raise exceptions.ValidationError(detail="view parameter missing")
if not task.potree_scene:
init_p = {
'type': 'Potree',
'version': 1.7
}
task.potree_scene = init_p
task.potree_scene['view'] = view
task.save()
return Response({'success': True})

Wyświetl plik

@ -10,7 +10,7 @@ from .admin import UserViewSet, GroupViewSet
from rest_framework_nested import routers
from rest_framework_jwt.views import obtain_jwt_token
from .tiler import TileJson, Bounds, Metadata, Tiles, Export
from .potree import Scene
from .potree import Scene, CameraView
from .workers import CheckTask, GetTaskResult
router = routers.DefaultRouter()
@ -47,6 +47,7 @@ urlpatterns = [
url(r'projects/(?P<project_pk>[^/.]+)/tasks/(?P<pk>[^/.]+)/images/download/(?P<image_filename>.+)$', ImageDownload.as_view()),
url(r'projects/(?P<project_pk>[^/.]+)/tasks/(?P<pk>[^/.]+)/3d/scene$', Scene.as_view()),
url(r'projects/(?P<project_pk>[^/.]+)/tasks/(?P<pk>[^/.]+)/3d/cameraview$', CameraView.as_view()),
url(r'workers/check/(?P<celery_task_id>.+)', CheckTask.as_view()),
url(r'workers/get/(?P<celery_task_id>.+)', GetTaskResult.as_view()),

Wyświetl plik

@ -15,6 +15,57 @@ require('./vendor/OBJLoader');
require('./vendor/MTLLoader');
require('./vendor/ColladaLoader');
class SetCameraView extends React.Component{
static propTypes = {
viewer: PropTypes.object.isRequired,
task: PropTypes.object.isRequired
}
constructor(props){
super(props);
this.state = {
error: "",
showOk: false
}
}
handleClick = () => {
const { view } = Potree.saveProject(this.props.viewer);
const showError = () => {
this.setState({error: _("Cannot set initial camera view")});
setTimeout(() => this.setState({error: ""}), 3000);
};
const showOk = () => {
this.setState({showOk: true});
setTimeout(() => this.setState({showOk: false}), 2000);
}
$.ajax({
url: `/api/projects/${this.props.task.project}/tasks/${this.props.task.id}/3d/cameraview`,
contentType: 'application/json',
data: JSON.stringify(view),
dataType: 'json',
type: 'POST'
}).done(result => {
if (result.success) showOk();
else showError();
}).fail(() => {
showError();
});
}
render(){
return ([<input key="btn" type="button" onClick={this.handleClick}
style={{marginBottom: 12, display: 'inline-block'}} name="set_camera_view"
value={_("set initial camera view")} />,
this.state.showOk ? (<div key="ok" style={{color: 'lightgreen', display: 'inline-block', marginLeft: 12}}></div>) : "",
this.state.error ? (<div key="error" style={{color: 'red'}}>{this.state.error}</div>) : ""
]
);
}
}
class TexturedModelMenu extends React.Component{
static propTypes = {
toggleTexturedModel: PropTypes.func.isRequired
@ -221,6 +272,17 @@ class ModelView extends React.Component {
});
}
getSceneData(){
let json = Potree.saveProject(window.viewer);
// Remove view, settings since we don't want to trigger
// scene updates when these change.
delete json.view;
delete json.settings;
return json;
}
componentDidMount() {
let container = this.container;
if (!container) return; // Enzyme tests don't have support for all WebGL methods so we just skip this
@ -250,6 +312,10 @@ class ModelView extends React.Component {
$("#cameras").hide();
$("#cameras_container").hide();
}
const $scv = $("<div id='set-camera-view'></div>");
$scv.prependTo($("#scene_export").parent());
window.ReactDOM.render(<SetCameraView viewer={viewer} task={this.props.task} />, $scv.get(0));
});
viewer.scene.scene.add( new THREE.AmbientLight( 0x404040, 2.0 ) ); // soft white light );
@ -280,9 +346,62 @@ class ModelView extends React.Component {
type: "GET",
url: `/api/projects/${this.props.task.project}/tasks/${this.props.task.id}/3d/scene`
}).done(sceneData => {
Potree.loadProject(viewer, sceneData);
let localSceneData = Potree.saveProject(viewer);
// Check if we do not have a view set
// if so, just keep the current view information
if (!sceneData.view || !sceneData.view.position){
sceneData.view = localSceneData.view;
}
sceneData.pointclouds = localSceneData.pointclouds;
sceneData.settings = localSceneData.settings;
// Load
Potree.loadProject(viewer, sceneData);
// Every 3 seconds, check if the scene has changed
// if it has, save the changes server-side
// Unfortunately Potree does not have reliable events
// for trivially detecting changes in measurements
let saveSceneReq = null;
let saveSceneInterval = null;
let saveSceneErrors = 0;
let prevSceneData = JSON.stringify(this.getSceneData());
const postSceneData = (sceneData) => {
if (saveSceneReq){
saveSceneReq.abort();
saveSceneReq = null;
}
saveSceneReq = $.ajax({
url: `/api/projects/${this.props.task.project}/tasks/${this.props.task.id}/3d/scene`,
contentType: 'application/json',
data: sceneData,
dataType: 'json',
type: 'POST'
}).done(result => {
if (result.success){
saveSceneErrors = 0;
prevSceneData = sceneData;
}else{
console.warn("Cannot save Potree scene");
}
}).fail(() => {
console.error("Cannot save Potree scene");
if (++saveSceneErrors === 5) clearInterval(saveSceneInterval);
});
};
const checkScene = () => {
const sceneData = JSON.stringify(this.getSceneData());
if (sceneData !== prevSceneData) postSceneData(sceneData);
};
saveSceneInterval = setInterval(checkScene, 3000);
}).fail(e => {
console.error("Cannot load 3D scene information", e);
console.error("Cannot load 3D scene information", e);
});
});
});