kopia lustrzana https://github.com/OpenDroneMap/WebODM
Save/load potree measurements
rodzic
cf554fdbfd
commit
8f760ecd7f
|
@ -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})
|
|
@ -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()),
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Ładowanie…
Reference in New Issue