Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/OpenDroneMap/WebODM into …
Browse files Browse the repository at this point in the history
…align_plugin
  • Loading branch information
pierotofy committed Aug 13, 2024
2 parents 67820e4 + 109adc7 commit b592d48
Show file tree
Hide file tree
Showing 62 changed files with 3,034 additions and 133 deletions.
2 changes: 1 addition & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

github: pierotofy
custom:
- https://www.opendronemap.org/webodm/download/
- https://webodm.net
- https://odmbook.com
6 changes: 3 additions & 3 deletions .github/workflows/test-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
swap-size-gb: 12
- name: Build and Test
run: |
docker-compose -f docker-compose.yml -f docker-compose.build.yml build --build-arg TEST_BUILD=ON
docker-compose -f docker-compose.yml -f docker-compose.build.yml up -d
docker compose -f docker-compose.yml -f docker-compose.build.yml build --build-arg TEST_BUILD=ON
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d
sleep 20
docker-compose exec -T webapp /webodm/webodm.sh test
docker compose exec -T webapp /webodm/webodm.sh test
11 changes: 9 additions & 2 deletions app/api/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from django.db import transaction
from django.http import FileResponse
from django.http import HttpResponse
from django.http import StreamingHttpResponse
from app.vendor import zipfly
from rest_framework import status, serializers, viewsets, filters, exceptions, permissions, parsers
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny
Expand Down Expand Up @@ -340,8 +342,13 @@ def download_file_response(request, filePath, content_disposition, download_file


def download_file_stream(request, stream, content_disposition, download_filename=None):
response = HttpResponse(FileWrapper(stream),
content_type=(mimetypes.guess_type(download_filename)[0] or "application/zip"))
if isinstance(stream, zipfly.ZipStream):
f = stream.generator()
else:
# This should never happen, but just in case..
raise exceptions.ValidationError("stream not a zipstream instance")

response = StreamingHttpResponse(f, content_type=(mimetypes.guess_type(download_filename)[0] or "application/zip"))

response['Content-Type'] = mimetypes.guess_type(download_filename)[0] or "application/zip"
response['Content-Disposition'] = "{}; filename={}".format(content_disposition, download_filename)
Expand Down
2 changes: 1 addition & 1 deletion app/models/plugin_datum.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

class PluginDatum(models.Model):
key = models.CharField(max_length=255, help_text=_("Setting key"), db_index=True, verbose_name=_("Key"))
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, default=None, on_delete=models.CASCADE, help_text=_("The user this setting belongs to. If NULL, the setting is global."), verbose_name=_("User"))
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None, on_delete=models.CASCADE, help_text=_("The user this setting belongs to. If NULL, the setting is global."), verbose_name=_("User"))
int_value = models.IntegerField(blank=True, null=True, default=None, verbose_name=_("Integer value"))
float_value = models.FloatField(blank=True, null=True, default=None, verbose_name=_("Float value"))
bool_value = models.NullBooleanField(blank=True, null=True, default=None, verbose_name=_("Bool value"))
Expand Down
Binary file added app/static/app/img/accept.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 24 additions & 5 deletions app/static/app/js/MapView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class MapView extends React.Component {
// and preference order (below)
if (props.selectedMapType === "auto"){
let preferredTypes = ['orthophoto', 'dsm', 'dtm'];
if (this.isThermalMap()) preferredTypes = ['plant'].concat(preferredTypes);

for (let i = 0; i < this.props.mapItems.length; i++){
let mapItem = this.props.mapItems[i];
Expand All @@ -57,6 +58,21 @@ class MapView extends React.Component {
this.handleMapTypeButton = this.handleMapTypeButton.bind(this);
}

isThermalMap = () => {
let thermalCount = 0;
for (let item of this.props.mapItems){
if (item.meta && item.meta.task && item.meta.task.orthophoto_bands){
if (item.meta.task.orthophoto_bands.length === 2 && item.meta.task.orthophoto_bands &&
item.meta.task.orthophoto_bands[0] && typeof(item.meta.task.orthophoto_bands[0].description) === "string" &&
item.meta.task.orthophoto_bands[0].description.toLowerCase() === "lwir"){
thermalCount++;
}
}
}

return thermalCount === this.props.mapItems.length;
}

getTilesByMapType(type){
// Go through the list of map items and return
// only those that match a particular type (in tile format)
Expand Down Expand Up @@ -85,16 +101,18 @@ class MapView extends React.Component {
}

render(){
const isThermal = this.isThermalMap();

let mapTypeButtons = [
{
label: _("Orthophoto"),
type: "orthophoto",
icon: "far fa-image"
},
{
label: _("Plant Health"),
label: isThermal ? _("Thermal") : _("Plant Health"),
type: "plant",
icon: "fa fa-seedling"
icon: isThermal ? "fa fa-thermometer-half" : "fa fa-seedling"
},
{
label: _("Surface Model"),
Expand All @@ -113,17 +131,17 @@ class MapView extends React.Component {

return (<div className="map-view">
<div className="map-view-header">
{this.props.title ?
{this.props.title ?
<h3 className="map-title" title={this.props.title}><i className="fa fa-globe"></i> {this.props.title}</h3>
: ""}

<div className="map-type-selector btn-group" role="group">
{mapTypeButtons.map(mapType =>
<button
<button
key={mapType.type}
onClick={this.handleMapTypeButton(mapType.type)}
title={mapType.label}
className={"btn btn-sm " + (mapType.type === this.state.selectedMapType ? "btn-primary" : "btn-default")}><i className={mapType.icon}></i><span className="hidden-sm hidden-xs"> {mapType.label}</span></button>
className={"btn btn-sm " + (mapType.type === this.state.selectedMapType ? "btn-primary" : "btn-default")}><i className={mapType.icon + " fa-fw"}></i><span className="hidden-sm hidden-xs"> {mapType.label}</span></button>
)}
</div>
</div>
Expand All @@ -136,6 +154,7 @@ class MapView extends React.Component {
public={this.props.public}
shareButtons={this.props.shareButtons}
permissions={this.props.permissions}
thermal={isThermal}
/>
</div>
</div>);
Expand Down
8 changes: 4 additions & 4 deletions app/static/app/js/classes/Basemaps.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export default [
},
{
attribution:
'&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 21,
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 19,
minZoom: 0,
label: _("OSM Mapnik"),
url: "//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
label: _("OpenStreetMap"),
url: "//tile.openstreetmap.org/{z}/{x}/{y}.png"
}
];
53 changes: 51 additions & 2 deletions app/static/app/js/classes/Units.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { _ } from './gettext';
const types = {
LENGTH: 1,
AREA: 2,
VOLUME: 3
VOLUME: 3,
TEMPERATURE: 4
};

const units = {
Expand Down Expand Up @@ -139,6 +140,26 @@ const units = {
round: 4,
label: _("Cubic Yards"),
type: types.VOLUME
},
celsius:{
conversion:{
forward: celsius => celsius,
backward: celsius => celsius
},
abbr: '°C',
round: 1,
label: _("Celsius"),
type: types.TEMPERATURE
},
fahrenheit:{
conversion: {
forward: celsius => (9.0 / 5.0) * celsius + 32.0,
backward: fahrenheit => (fahrenheit - 32.0) * (5.0 / 9.0)
},
abbr: '°F',
round: 1,
label: _("Fahrenheit"),
type: types.TEMPERATURE
}
};

Expand Down Expand Up @@ -175,6 +196,7 @@ class UnitSystem{
lengthUnit(meters, opts = {}){ throw new Error("Not implemented"); }
areaUnit(sqmeters, opts = {}){ throw new Error("Not implemented"); }
volumeUnit(cbmeters, opts = {}){ throw new Error("Not implemented"); }
temperatureUnit(celsius, opts = {}){ throw new Error("Not implemented"); }

getName(){ throw new Error("Not implemented"); }
getKey(){ throw new Error("Not implemented"); }
Expand Down Expand Up @@ -209,6 +231,15 @@ class UnitSystem{
const val = unit.factor * cbmeters;
return new ValueUnit(val, unit);
}

temperature(celsius, opts = {}){
celsius = parseFloat(celsius);
if (isNaN(celsius)) return NanUnit();

const unit = this.temperatureUnit(celsius, opts);
const val = unit.conversion.forward(celsius);
return new ValueUnit(val, unit);
}
};

function toMetric(valueUnit, unit){
Expand All @@ -221,13 +252,23 @@ function toMetric(valueUnit, unit){
}
if (isNaN(value)) return NanUnit();

const val = value / unit.factor;
let val;
if (unit.factor !== undefined){
val = value / unit.factor;
}else if (unit.conversion !== undefined){
val = unit.conversion.backward(value);
}else{
throw new Error(`No unit factor or conversion: ${unit.type}`);
}

if (unit.type === types.LENGTH){
return new ValueUnit(val, units.meters);
}else if (unit.type === types.AREA){
return new ValueUnit(val, unit.sqmeters);
}else if (unit.type === types.VOLUME){
return new ValueUnit(val, unit.cbmeters);
}else if (unit.type === types.TEMPERATURE){
return new ValueUnit(val, units.celsius);
}else{
throw new Error(`Unrecognized unit type: ${unit.type}`);
}
Expand Down Expand Up @@ -261,6 +302,10 @@ class MetricSystem extends UnitSystem{
volumeUnit(cbmeters, opts = {}){
return units.cbmeters;
}

temperatureUnit(celsius, opts = {}){
return units.celsius;
}
}

class ImperialSystem extends UnitSystem{
Expand Down Expand Up @@ -316,6 +361,10 @@ class ImperialSystem extends UnitSystem{
volumeUnit(cbmeters, opts = {}){
return this.cbyards();
}

temperatureUnit(celsius, opts = {}){
return units.fahrenheit;
}
}

class ImperialUSSystem extends ImperialSystem{
Expand Down
25 changes: 22 additions & 3 deletions app/static/app/js/components/EditTaskForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class EditTaskForm extends React.Component {
onFormChanged: PropTypes.func,
inReview: PropTypes.bool,
task: PropTypes.object,
suggestedTaskName: PropTypes.oneOfType([PropTypes.string, PropTypes.func])
suggestedTaskName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
getCropPolygon: PropTypes.func
};

constructor(props){
Expand Down Expand Up @@ -350,14 +351,32 @@ class EditTaskForm extends React.Component {
// from a processing node)
getAvailableOptionsOnly(options, availableOptions){
const optionNames = {};
let optsCopy = Utils.clone(options);

availableOptions.forEach(opt => optionNames[opt.name] = true);
return options.filter(opt => optionNames[opt.name]);

// Override boundary and crop options (if they are available)
if (this.props.getCropPolygon){
const poly = this.props.getCropPolygon();
if (poly && optionNames['crop'] && optionNames['boundary']){
let cropOpt = optsCopy.find(opt => opt.name === 'crop');
if (!cropOpt) optsCopy.push({name: 'crop', value: "0"});

let boundaryOpt = optsCopy.find(opt => opt.name === 'boundary');
if (!boundaryOpt) optsCopy.push({name: 'boundary', value: JSON.stringify(poly)});
else boundaryOpt.value = JSON.stringify(poly);
}
}

return optsCopy.filter(opt => optionNames[opt.name]);
}

getAvailableOptionsOnlyText(options, availableOptions){
const opts = this.getAvailableOptionsOnly(options, availableOptions);
let res = opts.map(opt => `${opt.name}:${opt.value}`).join(", ");
let res = opts.map(opt => {
if (opt.name === "boundary") return `${opt.name}:geojson`;
else return `${opt.name}:${opt.value}`;
}).join(", ");
if (!res) res = _("Default");
return res;
}
Expand Down
12 changes: 7 additions & 5 deletions app/static/app/js/components/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class Map extends React.Component {
mapType: "orthophoto",
public: false,
shareButtons: true,
permissions: ["view"]
permissions: ["view"],
thermal: false
};

static propTypes = {
Expand All @@ -45,7 +46,8 @@ class Map extends React.Component {
mapType: PropTypes.oneOf(['orthophoto', 'plant', 'dsm', 'dtm']),
public: PropTypes.bool,
shareButtons: PropTypes.bool,
permissions: PropTypes.array
permissions: PropTypes.array,
thermal: PropTypes.bool
};

constructor(props) {
Expand Down Expand Up @@ -88,7 +90,7 @@ class Map extends React.Component {
case "orthophoto":
return _("Orthophoto");
case "plant":
return _("Plant Health");
return this.props.thermal ? _("Thermal") : _("Plant Health");
case "dsm":
return _("DSM");
case "dtm":
Expand All @@ -102,13 +104,13 @@ class Map extends React.Component {
case "orthophoto":
return "far fa-image fa-fw"
case "plant":
return "fa fa-seedling fa-fw";
return this.props.thermal ? "fa fa-thermometer-half fa-fw" : "fa fa-seedling fa-fw";
case "dsm":
case "dtm":
return "fa fa-chart-area fa-fw";
}
return "";
}
}

hasBands = (bands, orthophoto_bands) => {
if (!orthophoto_bands) return false;
Expand Down
Loading

0 comments on commit b592d48

Please sign in to comment.