kopia lustrzana https://github.com/OpenDroneMap/WebODM
Cleanup, UI polishing
rodzic
c347e02a73
commit
115d860179
|
@ -1,4 +1,5 @@
|
|||
import rasterio
|
||||
import os
|
||||
from django.http import HttpResponse
|
||||
from rio_tiler.errors import TileOutsideBounds
|
||||
from rio_tiler.mercator import get_zooms
|
||||
|
@ -109,7 +110,11 @@ class Metadata(TaskNestedView):
|
|||
|
||||
raster_path = get_raster_path(task, tile_type)
|
||||
info = main.metadata(raster_path, pmin=2.0, pmax=98.0)
|
||||
info['address'] = get_tile_url(task, tile_type)
|
||||
info['address'] = os.path.basename(info['address'])
|
||||
info['name'] = task.name
|
||||
info['scheme'] = 'xyz'
|
||||
info['tiles'] = [get_tile_url(task, tile_type)]
|
||||
|
||||
return Response(info)
|
||||
|
||||
class Tiles(TaskNestedView):
|
||||
|
|
|
@ -703,17 +703,22 @@ class Task(models.Model):
|
|||
def get_tile_path(self, tile_type, z, x, y):
|
||||
return self.assets_path("{}_tiles".format(tile_type), z, x, "{}.png".format(y))
|
||||
|
||||
def get_tile_json_url(self, tile_type):
|
||||
return "/api/projects/{}/tasks/{}/{}/tiles.json".format(self.project.id, self.id, tile_type)
|
||||
def get_tile_base_url(self, tile_type):
|
||||
# plant is just a special case of orthophoto
|
||||
if tile_type == 'plant':
|
||||
tile_type = 'orthophoto'
|
||||
|
||||
return "/api/projects/{}/tasks/{}/{}/".format(self.project.id, self.id, tile_type)
|
||||
|
||||
def get_map_items(self):
|
||||
types = []
|
||||
if 'orthophoto.tif' in self.available_assets: types.append('orthophoto')
|
||||
if 'orthophoto.tif' in self.available_assets: types.append('plant')
|
||||
if 'dsm.tif' in self.available_assets: types.append('dsm')
|
||||
if 'dtm.tif' in self.available_assets: types.append('dtm')
|
||||
|
||||
return {
|
||||
'tiles': [{'url': self.get_tile_json_url(t), 'type': t} for t in types],
|
||||
'tiles': [{'url': self.get_tile_base_url(t), 'type': t} for t in types],
|
||||
'meta': {
|
||||
'task': {
|
||||
'id': str(self.id),
|
||||
|
|
|
@ -23,12 +23,10 @@ class MapView extends React.Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
opacity: 100,
|
||||
selectedMapType: props.selectedMapType,
|
||||
tiles: this.getTilesByMapType(props.selectedMapType)
|
||||
};
|
||||
|
||||
this.updateOpacity = this.updateOpacity.bind(this);
|
||||
this.getTilesByMapType = this.getTilesByMapType.bind(this);
|
||||
this.handleMapTypeButton = this.handleMapTypeButton.bind(this);
|
||||
}
|
||||
|
@ -44,13 +42,6 @@ class MapView extends React.Component {
|
|||
url: tile.url,
|
||||
meta: mapItem.meta
|
||||
});
|
||||
|
||||
// Special case for plant
|
||||
// if (tile.type === 'orthophoto') tiles.push({
|
||||
// url: tile.url + "?expr=(b2-b1)/(b2+b1-b3)&rescale=0,1&color_map=rdylgn",
|
||||
// meta: mapItem.meta
|
||||
// });
|
||||
console.log(mapItem.meta);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -66,14 +57,7 @@ class MapView extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
updateOpacity(evt) {
|
||||
this.setState({
|
||||
opacity: parseFloat(evt.target.value),
|
||||
});
|
||||
}
|
||||
|
||||
render(){
|
||||
const { opacity } = this.state;
|
||||
let mapTypeButtons = [
|
||||
{
|
||||
label: "Orthophoto",
|
||||
|
@ -113,15 +97,14 @@ class MapView extends React.Component {
|
|||
{this.props.title ?
|
||||
<h3><i className="fa fa-globe"></i> {this.props.title}</h3>
|
||||
: ""}
|
||||
|
||||
<Map
|
||||
tiles={this.state.tiles}
|
||||
showBackground={true}
|
||||
opacity={opacity}
|
||||
mapType={this.state.selectedMapType}
|
||||
public={this.props.public} />
|
||||
<div className="opacity-slider theme-secondary hidden-xs">
|
||||
Opacity: <input type="range" step="1" value={opacity} onChange={this.updateOpacity} />
|
||||
|
||||
<div className="map-container">
|
||||
<Map
|
||||
tiles={this.state.tiles}
|
||||
showBackground={true}
|
||||
mapType={this.state.selectedMapType}
|
||||
public={this.props.public}
|
||||
/>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import update from 'immutability-helper';
|
|||
class Map extends React.Component {
|
||||
static defaultProps = {
|
||||
showBackground: false,
|
||||
opacity: 100,
|
||||
mapType: "orthophoto",
|
||||
public: false
|
||||
};
|
||||
|
@ -31,7 +30,6 @@ class Map extends React.Component {
|
|||
static propTypes = {
|
||||
showBackground: PropTypes.bool,
|
||||
tiles: PropTypes.array.isRequired,
|
||||
opacity: PropTypes.number,
|
||||
mapType: PropTypes.oneOf(['orthophoto', 'plant', 'dsm', 'dtm']),
|
||||
public: PropTypes.bool
|
||||
};
|
||||
|
@ -43,7 +41,8 @@ class Map extends React.Component {
|
|||
error: "",
|
||||
singleTask: null, // When this is set to a task, show a switch mode button to view the 3d model
|
||||
pluginActionButtons: [],
|
||||
showLoading: false
|
||||
showLoading: false, // for drag&drop of files
|
||||
opacity: 100
|
||||
};
|
||||
|
||||
this.imageryLayers = [];
|
||||
|
@ -56,6 +55,12 @@ class Map extends React.Component {
|
|||
this.handleMapMouseDown = this.handleMapMouseDown.bind(this);
|
||||
}
|
||||
|
||||
updateOpacity = (evt) => {
|
||||
this.setState({
|
||||
opacity: parseFloat(evt.target.value),
|
||||
});
|
||||
}
|
||||
|
||||
updatePopupFor(layer){
|
||||
const popup = layer.getPopup();
|
||||
$('#layerOpacity', popup.getContent()).val(layer.options.opacity);
|
||||
|
@ -86,23 +91,25 @@ class Map extends React.Component {
|
|||
async.each(tiles, (tile, done) => {
|
||||
const { url, meta } = tile;
|
||||
|
||||
this.tileJsonRequests.push($.getJSON(url)
|
||||
.done(info => {
|
||||
this.tileJsonRequests.push($.getJSON(url + "metadata")
|
||||
.done(mres => {
|
||||
const { scheme, name, maxzoom } = mres;
|
||||
|
||||
const bounds = Leaflet.latLngBounds(
|
||||
[info.bounds.slice(0, 2).reverse(), info.bounds.slice(2, 4).reverse()]
|
||||
[mres.bounds.value.slice(0, 2).reverse(), mres.bounds.value.slice(2, 4).reverse()]
|
||||
);
|
||||
const layer = Leaflet.tileLayer(info.tiles[0], {
|
||||
const layer = Leaflet.tileLayer(mres.tiles[0], {
|
||||
bounds,
|
||||
minZoom: 0,
|
||||
maxZoom: info.maxzoom + 4,
|
||||
maxNativeZoom: info.maxzoom,
|
||||
tms: info.scheme === 'tms',
|
||||
opacity: this.props.opacity / 100,
|
||||
maxZoom: maxzoom + 4,
|
||||
maxNativeZoom: maxzoom,
|
||||
tms: scheme === 'tms',
|
||||
opacity: this.state.opacity / 100,
|
||||
detectRetina: true
|
||||
});
|
||||
|
||||
// Associate metadata with this layer
|
||||
meta.name = info.name;
|
||||
meta.name = name;
|
||||
layer[Symbol.for("meta")] = meta;
|
||||
|
||||
if (forceAddLayers || prevSelectedLayers.indexOf(layerId(layer)) !== -1){
|
||||
|
@ -127,7 +134,7 @@ class Map extends React.Component {
|
|||
var popup = L.DomUtil.create('div', 'infoWindow');
|
||||
|
||||
popup.innerHTML = `<div class="title">
|
||||
${info.name}
|
||||
${name}
|
||||
</div>
|
||||
<div class="popup-opacity-slider">Opacity: <input id="layerOpacity" type="range" value="${layer.options.opacity}" min="0" max="1" step="0.01" /></div>
|
||||
<div>Bounds: [${layer.options.bounds.toBBoxString().split(",").join(", ")}]</div>
|
||||
|
@ -155,7 +162,7 @@ class Map extends React.Component {
|
|||
this.mapBounds = mapBounds;
|
||||
|
||||
// Add layer to layers control
|
||||
this.autolayers.addOverlay(layer, info.name);
|
||||
this.autolayers.addOverlay(layer, name);
|
||||
|
||||
done();
|
||||
})
|
||||
|
@ -333,7 +340,7 @@ https://a.tile.openstreetmap.org/{z}/{x}/{y}.png
|
|||
|
||||
componentDidUpdate(prevProps) {
|
||||
this.imageryLayers.forEach(imageryLayer => {
|
||||
imageryLayer.setOpacity(this.props.opacity / 100);
|
||||
imageryLayer.setOpacity(this.state.opacity / 100);
|
||||
this.updatePopupFor(imageryLayer);
|
||||
});
|
||||
|
||||
|
@ -360,6 +367,10 @@ https://a.tile.openstreetmap.org/{z}/{x}/{y}.png
|
|||
return (
|
||||
<div style={{height: "100%"}} className="map">
|
||||
<ErrorMessage bind={[this, 'error']} />
|
||||
<div className="opacity-slider theme-secondary hidden-xs">
|
||||
Opacity: <input type="range" step="1" value={this.state.opacity} onChange={this.updateOpacity} />
|
||||
</div>
|
||||
|
||||
<Standby
|
||||
message="Loading..."
|
||||
show={this.state.showLoading}
|
||||
|
|
|
@ -5,12 +5,14 @@ import PropTypes from 'prop-types';
|
|||
class Standby extends React.Component {
|
||||
static defaultProps = {
|
||||
message: "",
|
||||
show: false
|
||||
show: false,
|
||||
opacity: 0.5
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
message: PropTypes.string,
|
||||
show: PropTypes.bool
|
||||
show: PropTypes.bool,
|
||||
opacity: PropTypes.number
|
||||
};
|
||||
|
||||
constructor(props){
|
||||
|
@ -41,7 +43,7 @@ class Standby extends React.Component {
|
|||
return (
|
||||
<div className="standby"
|
||||
style={{display: this.state.show ? "block" : "none"}}>
|
||||
<div className="cover"> </div>
|
||||
<div className="cover" style={{opacity: this.props.opacity}}> </div>
|
||||
<div className="content">
|
||||
<i className="fa fa-spinner fa-spin fa-2x fa-fw"></i>
|
||||
<p>{this.state.message}</p>
|
||||
|
|
|
@ -8,7 +8,7 @@ sinon.useFakeXMLHttpRequest();
|
|||
describe('<Map />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const wrapper = mount(<Map
|
||||
tiles={['/tiles.json']} />);
|
||||
tiles={['/']} />);
|
||||
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
})
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
.map{
|
||||
position: relative;
|
||||
|
||||
.opacity-slider{
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
width: 220px;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
z-index: 999;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.leaflet-popup-content{
|
||||
.title{
|
||||
font-weight: bold;
|
||||
|
|
|
@ -14,18 +14,15 @@
|
|||
top: 6px;
|
||||
}
|
||||
|
||||
.opacity-slider{
|
||||
text-align: center;
|
||||
width: 220px;
|
||||
position: absolute;
|
||||
bottom: -32px;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
z-index: 400;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.map-type-selector{
|
||||
float: right;
|
||||
}
|
||||
|
||||
.map-container{
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.standby{
|
||||
z-index: 99999;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,8 @@
|
|||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
right: 0;
|
||||
.cover{
|
||||
opacity: 0.5;
|
||||
background-color: #111;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -23,5 +22,5 @@
|
|||
right: 0;
|
||||
padding: 0;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import React, { Component, Fragment } from "react";
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import GoToFolderButton from "./components/GoToFolderButton";
|
||||
import $ from "jquery"; // Fixes a AMD module definition error in webpack
|
||||
import $ from "jquery"; // Fixes a AMD module definition error due to webpack
|
||||
|
||||
export default class TaskView extends Component {
|
||||
static propTypes = {
|
||||
|
|
Ładowanie…
Reference in New Issue