kopia lustrzana https://github.com/OpenDroneMap/WebODM
Save/load potree measurements
rodzic
cf554fdbfd
commit
8f760ecd7f
|
@ -1,5 +1,7 @@
|
||||||
from .tasks import TaskNestedView
|
from .tasks import TaskNestedView
|
||||||
|
from .common import get_and_check_project
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import exceptions
|
||||||
|
|
||||||
class Scene(TaskNestedView):
|
class Scene(TaskNestedView):
|
||||||
def get(self, request, pk=None, project_pk=None):
|
def get(self, request, pk=None, project_pk=None):
|
||||||
|
@ -9,3 +11,46 @@ class Scene(TaskNestedView):
|
||||||
task = self.get_and_check_task(request, pk)
|
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_nested import routers
|
||||||
from rest_framework_jwt.views import obtain_jwt_token
|
from rest_framework_jwt.views import obtain_jwt_token
|
||||||
from .tiler import TileJson, Bounds, Metadata, Tiles, Export
|
from .tiler import TileJson, Bounds, Metadata, Tiles, Export
|
||||||
from .potree import Scene
|
from .potree import Scene, CameraView
|
||||||
from .workers import CheckTask, GetTaskResult
|
from .workers import CheckTask, GetTaskResult
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
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>[^/.]+)/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/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/check/(?P<celery_task_id>.+)', CheckTask.as_view()),
|
||||||
url(r'workers/get/(?P<celery_task_id>.+)', GetTaskResult.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/MTLLoader');
|
||||||
require('./vendor/ColladaLoader');
|
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{
|
class TexturedModelMenu extends React.Component{
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
toggleTexturedModel: PropTypes.func.isRequired
|
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() {
|
componentDidMount() {
|
||||||
let container = this.container;
|
let container = this.container;
|
||||||
if (!container) return; // Enzyme tests don't have support for all WebGL methods so we just skip this
|
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").hide();
|
||||||
$("#cameras_container").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 );
|
viewer.scene.scene.add( new THREE.AmbientLight( 0x404040, 2.0 ) ); // soft white light );
|
||||||
|
@ -280,7 +346,60 @@ class ModelView extends React.Component {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: `/api/projects/${this.props.task.project}/tasks/${this.props.task.id}/3d/scene`
|
url: `/api/projects/${this.props.task.project}/tasks/${this.props.task.id}/3d/scene`
|
||||||
}).done(sceneData => {
|
}).done(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);
|
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 => {
|
}).fail(e => {
|
||||||
console.error("Cannot load 3D scene information", e);
|
console.error("Cannot load 3D scene information", e);
|
||||||
});
|
});
|
||||||
|
|
Ładowanie…
Reference in New Issue