OpenDroneMap-WebODM/coreplugins/objdetect/public/ObjDetectPanel.jsx

227 wiersze
7.2 KiB
React
Czysty Zwykły widok Historia

2025-01-23 20:09:58 +00:00
import React from 'react';
import PropTypes from 'prop-types';
import Storage from 'webodm/classes/Storage';
import L from 'leaflet';
import './ObjDetectPanel.scss';
import ErrorMessage from 'webodm/components/ErrorMessage';
import Workers from 'webodm/classes/Workers';
2025-01-24 17:20:53 +00:00
import Utils from 'webodm/classes/Utils';
2025-01-23 20:09:58 +00:00
import { _ } from 'webodm/classes/gettext';
export default class ObjDetectPanel extends React.Component {
static defaultProps = {
};
static propTypes = {
onClose: PropTypes.func.isRequired,
tasks: PropTypes.object.isRequired,
isShowed: PropTypes.bool.isRequired,
map: PropTypes.object.isRequired
}
constructor(props){
super(props);
this.state = {
error: "",
permanentError: "",
model: Storage.getItem("last_objdetect_model") || "cars",
loading: true,
task: props.tasks[0] || null,
detecting: false,
2025-01-24 19:23:18 +00:00
progress: null,
2025-01-23 20:09:58 +00:00
objLayer: null,
};
}
componentDidMount(){
}
componentDidUpdate(){
if (this.props.isShowed && this.state.loading){
const {id, project} = this.state.task;
this.loadingReq = $.getJSON(`/api/projects/${project}/tasks/${id}/`)
.done(res => {
const { available_assets } = res;
if (available_assets.indexOf("orthophoto.tif") === -1){
this.setState({permanentError: _("No orthophoto is available. To use object detection you need an orthophoto.")});
}
})
.fail(() => {
this.setState({permanentError: _("Cannot retrieve information for task. Are you are connected to the internet?")})
})
.always(() => {
this.setState({loading: false});
this.loadingReq = null;
});
}
}
componentWillUnmount(){
if (this.loadingReq){
this.loadingReq.abort();
this.loadingReq = null;
}
if (this.detectReq){
this.detectReq.abort();
this.detectReq = null;
}
}
handleSelectModel = e => {
this.setState({model: e.target.value});
}
getFormValues = () => {
const { model } = this.state;
return {
model
};
}
2025-01-24 16:43:19 +00:00
addGeoJSON = (geojson, cb) => {
2025-01-23 20:09:58 +00:00
const { map } = this.props;
2025-01-24 16:43:19 +00:00
try{
this.handleRemoveObjLayer();
this.setState({objLayer: L.geoJSON(geojson, {
onEachFeature: (feature, layer) => {
2025-01-24 17:20:53 +00:00
if (feature.properties && feature.properties['class'] !== undefined) {
2025-01-24 16:43:19 +00:00
layer.bindPopup(`<div style="margin-right: 32px;">
2025-01-24 17:20:53 +00:00
<b>${_("Label:")}</b> ${feature.properties['class']}<br/>
<b>${_("Confidence:")}</b> ${feature.properties.score.toFixed(3)}<br/>
2025-01-24 16:43:19 +00:00
</div>
`);
}
},
style: feature => {
// TODO: different colors for different elevations?
return {color: "red"};
}
})});
this.state.objLayer.addTo(map);
2025-01-24 17:20:53 +00:00
this.state.objLayer.label = this.state.model;
2025-01-24 16:43:19 +00:00
cb();
}catch(e){
cb(e.message);
}
2025-01-23 20:09:58 +00:00
}
handleRemoveObjLayer = () => {
const { map } = this.props;
if (this.state.objLayer){
map.removeLayer(this.state.objLayer);
this.setState({objLayer: null});
}
}
saveInputValues = () => {
// Save settings
Storage.setItem("last_objdetect_model", this.state.model);
}
handleDetect = () => {
2025-01-30 05:33:56 +00:00
this.handleRemoveObjLayer();
2025-01-24 19:23:18 +00:00
this.setState({detecting: true, error: "", progress: null});
2025-01-23 20:09:58 +00:00
const taskId = this.state.task.id;
this.saveInputValues();
this.detectReq = $.ajax({
type: 'POST',
url: `/api/plugins/objdetect/task/${taskId}/detect`,
data: this.getFormValues()
}).done(result => {
if (result.celery_task_id){
Workers.waitForCompletion(result.celery_task_id, error => {
if (error) this.setState({detecting: false, error});
else{
2025-01-24 16:43:19 +00:00
Workers.getOutput(result.celery_task_id, (error, geojson) => {
try{
geojson = JSON.parse(geojson);
}catch(e){
error = "Invalid GeoJSON";
}
if (error) this.setState({detecting: false, error});
else{
this.addGeoJSON(geojson, e => {
if (e) this.setState({error: JSON.stringify(e)});
this.setState({detecting: false});
});
}
2025-01-23 20:09:58 +00:00
});
}
2025-01-24 19:23:18 +00:00
}, (_, progress) => {
this.setState({progress});
});
2025-01-23 20:09:58 +00:00
}else if (result.error){
this.setState({detecting: false, error: result.error});
}else{
this.setState({detecting: false, error: "Invalid response: " + result});
}
}).fail(error => {
this.setState({detecting: false, error: JSON.stringify(error)});
});
}
2025-01-24 17:20:53 +00:00
handleDownload = () => {
Utils.saveAs(JSON.stringify(this.state.objLayer.toGeoJSON(14), null, 4), `${this.state.objLayer.label || "objects"}.geojson`);
}
2025-01-23 20:09:58 +00:00
render(){
2025-01-24 19:23:18 +00:00
const { loading, permanentError, objLayer, detecting, model, progress } = this.state;
2025-01-23 20:09:58 +00:00
const models = [
{label: _('Cars'), value: 'cars'},
2025-01-31 07:34:36 +00:00
// {label: _('Trees'), value: 'trees'},
{label: _('Athletic Facilities'), value: 'athletic'},
{label: _('Boats'), value: 'boats'},
{label: _('Planes'), value: 'planes'}
2025-01-23 20:09:58 +00:00
]
2025-01-24 17:26:06 +00:00
2025-01-23 20:09:58 +00:00
let content = "";
if (loading) content = (<span><i className="fa fa-circle-notch fa-spin"></i> {_("Loading…")}</span>);
else if (permanentError) content = (<div className="alert alert-warning">{permanentError}</div>);
else{
2025-01-24 17:26:06 +00:00
const featCount = objLayer ? objLayer.getLayers().length : 0;
2025-01-23 20:09:58 +00:00
content = (<div>
<ErrorMessage bind={[this, "error"]} />
2025-01-24 17:20:53 +00:00
<div className="row model-selector">
2025-01-23 20:09:58 +00:00
<select className="form-control" value={model} onChange={this.handleSelectModel}>
{models.map(m => <option value={m.value}>{m.label}</option>)}
</select>
<button onClick={this.handleDetect}
disabled={detecting} type="button" className="btn btn-sm btn-primary btn-detect">
2025-01-24 19:23:18 +00:00
{detecting ? <i className="fa fa-spin fa-circle-notch"/> : <i className="fa fa-search fa-fw"/>} {_("Detect")} {detecting && progress !== null ? ` (${progress.toFixed(0)}%)` : ""}
2025-01-23 20:09:58 +00:00
</button>
</div>
2025-01-24 17:20:53 +00:00
{objLayer ? <div className="detect-action-buttons">
2025-01-24 17:26:06 +00:00
<span><strong>{_("Count:")}</strong> {featCount}</span>
2025-01-24 17:20:53 +00:00
<div>
2025-01-24 17:26:06 +00:00
{featCount > 0 ? <button onClick={this.handleDownload}
2025-01-24 17:20:53 +00:00
type="button" className="btn btn-sm btn-primary btn-download">
<i className="fa fa-download fa-fw"/> {_("Download")}
2025-01-24 17:26:06 +00:00
</button> : ""}
2025-01-24 17:20:53 +00:00
<button onClick={this.handleRemoveObjLayer}
type="button" className="btn btn-sm btn-default">
<i className="fa fa-trash fa-fw"/>
</button>
</div>
</div> : ""}
2025-01-23 20:09:58 +00:00
</div>);
}
return (<div className="objdetect-panel">
<span className="close-button" onClick={this.props.onClose}/>
<div className="title">{_("Object Detection")}</div>
<hr/>
{content}
</div>);
}
}