Merge pull request #1597 from pierotofy/multilayer

Simultaneous map items display and swipe view
pull/1604/head
Piero Toffanin 2025-02-08 15:47:46 +01:00 zatwierdzone przez GitHub
commit beb7ded028
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
19 zmienionych plików z 659 dodań i 55 usunięć

Wyświetl plik

@ -53,11 +53,12 @@ class MapView extends React.Component {
this.state = {
selectedMapType,
tiles: this.getTilesByMapType(selectedMapType)
tiles: this.tilesFromMapType(selectedMapType)
};
this.getTilesByMapType = this.getTilesByMapType.bind(this);
this.tilesFromMapType = this.tilesFromMapType.bind(this);
this.handleMapTypeButton = this.handleMapTypeButton.bind(this);
this.hasTilesOfType = this.hasTilesOfType.bind(this);
}
isThermalMap = () => {
@ -75,17 +76,18 @@ class MapView extends React.Component {
return thermalCount === this.props.mapItems.length;
}
getTilesByMapType(type){
tilesFromMapType(type){
// Go through the list of map items and return
// only those that match a particular type (in tile format)
const tiles = [];
this.props.mapItems.forEach(mapItem => {
mapItem.tiles.forEach(tile => {
if (tile.type === type) tiles.push({
tiles.push({
url: tile.url,
meta: mapItem.meta,
type: tile.type
type: tile.type,
selected: tile.type === type
});
});
});
@ -93,11 +95,22 @@ class MapView extends React.Component {
return tiles;
}
hasTilesOfType(type){
for (let i = 0; i < this.props.mapItems.length; i++){
let mapItem = this.props.mapItems[i];
for (let j = 0; j < mapItem.tiles.length; j++){
let tile = mapItem.tiles[j];
if (tile.type === type) return true;
}
}
return false;
}
handleMapTypeButton(type){
return () => {
this.setState({
selectedMapType: type,
tiles: this.getTilesByMapType(type)
tiles: this.tilesFromMapType(type)
});
};
}
@ -126,7 +139,7 @@ class MapView extends React.Component {
type: "dtm",
icon: "fa fa-chart-area"
}
].filter(mapType => this.getTilesByMapType(mapType.type).length > 0 );
].filter(mapType => this.hasTilesOfType(mapType.type));
// If we have only one button, hide it...
if (mapTypeButtons.length === 1) mapTypeButtons = [];

Wyświetl plik

@ -29,7 +29,9 @@ export default {
"deleteAnnotation",
"toggleAnnotation",
"annotationDeleted",
"downloadAnnotations"
"downloadAnnotations",
"mapTypeChanged",
"sideBySideChanged",
]
};

Wyświetl plik

@ -58,7 +58,7 @@ export default L.Control.extend({
},
update: function(layers, overlays, annotations){
ReactDOM.render(<LayersControlButton map={this.map} layers={layers} overlays={overlays} annotations={annotations} />, this.container);
ReactDOM.render(<LayersControlButton ref={r => this.layersControlButton = r} map={this.map} layers={layers} overlays={overlays} annotations={annotations} />, this.container);
}
});

Wyświetl plik

@ -115,8 +115,10 @@ export default class LayersControlAnnotations extends React.Component {
componentDidUpdate(prevProps, prevState){
if (prevState.visible !== this.state.visible){
this.annRefs.forEach(ann => {
let visible = this.state.visible ? ann.state.visible : false;
PluginsAPI.Map.toggleAnnotation(ann.props.layer, visible);
if (ann){
let visible = this.state.visible ? ann.state.visible : false;
PluginsAPI.Map.toggleAnnotation(ann.props.layer, visible);
}
});
}
}

Wyświetl plik

@ -7,6 +7,7 @@ import Utils from '../classes/Utils';
import Workers from '../classes/Workers';
import ErrorMessage from './ErrorMessage';
import ExportAssetPanel from './ExportAssetPanel';
import PluginsAPI from '../classes/plugins/API';
import $ from 'jquery';
import { _, interpolate } from '../classes/gettext';
@ -15,20 +16,22 @@ export default class LayersControlLayer extends React.Component {
layer: null,
expanded: false,
map: null,
overlay: false
overlay: false,
separator: false,
};
static propTypes = {
layer: PropTypes.object.isRequired,
expanded: PropTypes.bool,
map: PropTypes.object.isRequired,
overlay: PropTypes.bool
}
overlay: PropTypes.bool,
separator: PropTypes.bool
};
constructor(props){
super(props);
this.map = props.map;
const url = this.getLayerUrl();
const params = Utils.queryParams({search: url.slice(url.indexOf("?"))});
@ -63,6 +66,7 @@ export default class LayersControlLayer extends React.Component {
hillshade: params.hillshade || "",
histogramLoading: false,
exportLoading: false,
side: false,
error: ""
};
this.rescale = params.rescale || "";
@ -72,14 +76,46 @@ export default class LayersControlLayer extends React.Component {
return this.props.layer._url || "";
}
componentDidMount(){
PluginsAPI.Map.onMapTypeChanged(this.handleMapTypeChange);
PluginsAPI.Map.onSideBySideChanged(this.handleSideBySideChange);
}
handleMapTypeChange = (type, autoExpand) => {
if (this.meta.type !== undefined){
const visible = this.meta.type === type;
const expanded = visible && autoExpand;
this.setState({visible, expanded});
}
}
handleSideBySideChange = (layer, side) => {
// Toggle this layer's side off if it was previously sided
// when another layer is set to side
if (this.props.layer !== layer && this.state.side && side){
setTimeout(() => {
let visible = this.state.visible;
if (this.wasInvisibleOnSideClick){
visible = false;
this.wasInvisibleOnSideClick = false;
}
this.setState({ side: false, visible });
PluginsAPI.Map.sideBySideChanged(this.props.layer, false);
}, 0);
}
}
componentDidUpdate(prevProps, prevState){
const { layer } = this.props;
if (prevState.visible !== this.state.visible){
if (this.state.visible){
layer.addTo(this.map);
if (layer.show) layer.show();
else if (!this.map.hasLayer(layer)) layer.addTo(this.map);
}else{
this.map.removeLayer(layer);
if (layer.hide) layer.hide();
else this.map.removeLayer(layer);
}
}
@ -93,7 +129,7 @@ export default class LayersControlLayer extends React.Component {
}
if (prevProps.expanded !== this.props.expanded){
this.state.expanded = this.props.expanded;
this.setState({expanded: this.props.expanded});
}
}
@ -107,6 +143,9 @@ export default class LayersControlLayer extends React.Component {
this.exportReq.abort();
this.exportReq = null;
}
PluginsAPI.Map.offSideBySideChanged(this.handleSideBySideChange);
PluginsAPI.Map.offMapTypeChanged(this.handleMapTypeChange);
}
handleZoomToClick = () => {
@ -120,6 +159,32 @@ export default class LayersControlLayer extends React.Component {
if (layer.getPopup()) layer.openPopup();
}
handleSideClick = () => {
let { side, visible } = this.state;
if (!side){
side = true;
if (!visible){
visible = true;
this.wasInvisibleOnSideClick = true;
}
}else{
side = false;
if (this.wasInvisibleOnSideClick){
visible = false;
this.wasInvisibleOnSideClick = false;
}
}
this.setState({ side, visible });
PluginsAPI.Map.sideBySideChanged(this.props.layer, side);
}
sideIcon = () => {
if (!this.state.side) return "fa-divide fa-rotate-90";
else return "fa-chevron-right";
}
handleLayerClick = () => {
if (this.props.overlay){
this.setState({visible: !this.state.visible});
@ -296,8 +361,8 @@ export default class LayersControlLayer extends React.Component {
return (<div className="layers-control-layer">
<div className="layer-control-title">
{!this.props.overlay ? <ExpandButton bind={[this, 'expanded']} /> : <div className="paddingSpace"></div>}<Checkbox bind={[this, 'visible']}/>
<a title={meta.name} className="layer-label" href="javascript:void(0);" onClick={this.handleLayerClick}><i className={"layer-icon " + (meta.icon || "fa fa-vector-square fa-fw")}></i><div className="layer-title">{meta.name}</div></a> <a className="layer-action" href="javascript:void(0)" onClick={this.handleZoomToClick}><i title={_("Zoom To")} className="fa fa-expand"></i></a>
{!this.props.overlay ? <ExpandButton bind={[this, 'expanded']} className="expand-layer" /> : <div className="paddingSpace"></div>}<Checkbox bind={[this, 'visible']}/>
<a title={meta.name} className="layer-label" href="javascript:void(0);" onClick={this.handleLayerClick}><i className={"layer-icon " + (meta.icon || "fa fa-vector-square fa-fw")}></i><div className="layer-title">{meta.name}</div></a> {meta.raster ? <a className="layer-action" href="javascript:void(0)" onClick={this.handleSideClick}><i title={_("Side By Side")} className={"fa fa-fw " + this.sideIcon()}></i></a> : ""}<a className="layer-action" href="javascript:void(0)" onClick={this.handleZoomToClick}><i title={_("Zoom To")} className="fa fa-expand"></i></a>
</div>
{this.state.expanded ?
@ -370,6 +435,8 @@ export default class LayersControlLayer extends React.Component {
asset={this.asset}
exportParams={this.getLayerParams}
dropUp />
{this.props.separator ? <hr className="layer-separator" /> : ""}
</div> : ""}
</div>);

Wyświetl plik

@ -65,7 +65,7 @@ export default class LayersControlPanel extends React.Component {
this.props.overlays.forEach(scanGroup('overlays'));
this.props.layers.forEach(scanGroup('layers'));
this.props.annotations.forEach(scanGroup('annotations'));
const getGroupContent = group => {
return (<div>
@ -83,8 +83,14 @@ export default class LayersControlPanel extends React.Component {
{group.layers.sort((a, b) => {
const m_a = a[Symbol.for("meta")] || {};
const m_b = b[Symbol.for("meta")] || {};
return m_a.name > m_b.name ? -1 : 1;
}).map((layer, i) => <LayersControlLayer map={this.props.map} expanded={this.props.layers.length === 1} overlay={false} layer={layer} key={i} />)}
return m_a.type > m_b.type ? -1 : 1;
}).map((layer, i) => <LayersControlLayer map={this.props.map}
expanded={(layer[Symbol.for("meta")] || {}).autoExpand || false}
overlay={false}
layer={layer}
key={`${i}-${(layer[Symbol.for("meta")] || {}).type}`}
separator={i < group.layers.length - 1}
/>)}
</div>);
};

Wyświetl plik

@ -26,6 +26,7 @@ import Utils from '../classes/Utils';
import '../vendor/leaflet/Leaflet.Ajax';
import 'rbush';
import '../vendor/leaflet/leaflet-markers-canvas';
import '../vendor/leaflet/Leaflet.SideBySide/leaflet-side-by-side';
import { _ } from '../classes/gettext';
import UnitSelector from './UnitSelector';
import { unitSystem, toMetric } from '../classes/Units';
@ -63,12 +64,14 @@ class Map extends React.Component {
opacity: 100,
imageryLayers: [],
overlays: [],
annotations: []
annotations: [],
rightLayers: []
};
this.basemaps = {};
this.mapBounds = null;
this.autolayers = null;
this.taskCount = 1;
this.addedCameraShots = {};
this.loadImageryLayers = this.loadImageryLayers.bind(this);
@ -76,6 +79,14 @@ class Map extends React.Component {
this.handleMapMouseDown = this.handleMapMouseDown.bind(this);
}
countTasks = () => {
let tasks = {};
this.props.tiles.forEach(tile => {
tasks[tile.meta.task.id] = true;
});
return Object.keys(tasks).length;
}
updateOpacity = (evt) => {
this.setState({
opacity: parseFloat(evt.target.value),
@ -102,9 +113,9 @@ class Map extends React.Component {
case "plant":
return this.props.thermal ? _("Thermal") : _("Plant Health");
case "dsm":
return _("DSM");
return _("Surface Model");
case "dtm":
return _("DTM");
return _("Terrain Model");
}
return "";
}
@ -122,6 +133,10 @@ class Map extends React.Component {
return "";
}
typeZIndex = (type) => {
return ["dsm", "dtm", "orthophoto", "plant"].indexOf(type) + 1;
}
hasBands = (bands, orthophoto_bands) => {
if (!orthophoto_bands) return false;
@ -139,6 +154,8 @@ class Map extends React.Component {
this.tileJsonRequests = [];
}
this.taskCount = this.countTasks();
const { tiles } = this.props,
layerId = layer => {
const meta = layer[Symbol.for("meta")];
@ -153,15 +170,16 @@ class Map extends React.Component {
if (this.map.hasLayer(layer)) prevSelectedLayers.push(layerId(layer));
layer.remove();
});
this.setState({imageryLayers: []});
this.setState({imageryLayers: [], rightLayers: []});
// Request new tiles
return new Promise((resolve, reject) => {
this.tileJsonRequests = [];
async.each(tiles, (tile, done) => {
const { url, meta, type } = tile;
const { url, type } = tile;
const meta = Utils.clone(tile.meta);
let metaUrl = url + "metadata";
let unitForward = value => value;
let unitBackward = value => value;
@ -242,16 +260,20 @@ class Map extends React.Component {
tileSize: TILESIZE,
tms: scheme === 'tms',
opacity: this.state.opacity / 100,
detectRetina: true
detectRetina: true,
zIndex: this.typeZIndex(type),
});
// Associate metadata with this layer
meta.name = this.typeToHuman(type);
meta.icon = this.typeToIcon(type);
meta.type = type;
meta.raster = true;
meta.autoExpand = this.taskCount === 1 && type === this.props.mapType;
meta.metaUrl = metaUrl;
meta.unitForward = unitForward;
meta.unitBackward = unitBackward;
if (this.props.tiles.length > 1){
if (this.taskCount > 1){
// Assign to a group
meta.group = {id: meta.task.id, name: meta.task.name};
}
@ -259,17 +281,19 @@ class Map extends React.Component {
layer[Symbol.for("tile-meta")] = mres;
if (forceAddLayers || prevSelectedLayers.indexOf(layerId(layer)) !== -1){
layer.addTo(this.map);
if (type === this.props.mapType){
layer.addTo(this.map);
}
}
// Show 3D switch button only if we have a single orthophoto
if (tiles.length === 1){
if (this.taskCount === 1){
this.setState({singleTask: meta.task});
}
// For some reason, getLatLng is not defined for tileLayer?
// We need this function if other code calls layer.openPopup()
let self = this;
const self = this;
layer.getLatLng = function(){
let latlng = self.lastClickedLatLng ?
self.lastClickedLatLng :
@ -277,12 +301,38 @@ class Map extends React.Component {
return latlng;
};
// Additional layer methods
layer.show = function(){
if (!self.map.hasLayer(this)) self.map.addLayer(this);
else this.getContainer().style.display = '';
};
layer.hide = function(){
this.getContainer().style.display = 'none';
};
layer.isHidden = function(){
if (!this.getContainer()) return false;
return this.getContainer().style.display === 'none';
};
layer.setZIndex = function(z){
if (this._originalZ === undefined) this._originalZ = this.options.zIndex;
this.options.zIndex = z;
this._updateZIndex();
};
layer.restoreZIndex = function(){
if (this._originalZ !== undefined){
this.setZIndex(this._originalZ);
}
};
layer.bringToFront = function(){
this.setZIndex(this.options.zIndex + 10000);
};
var popup = L.DomUtil.create('div', 'infoWindow');
popup.innerHTML = `<div class="title">
${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 class="popup-opacity-slider">Opacity: <input id="layerOpacity" class="opacity" type="range" value="${layer.options.opacity}" min="0" max="1" step="0.01" /></div>
<div>Bounds: [${layer.options.bounds.toBBoxString().split(",").join(", ")}]</div>
<ul class="asset-links loading">
<li><i class="fa fa-spin fa-sync fa-spin fa-fw"></i></li>
@ -351,7 +401,7 @@ class Map extends React.Component {
}
});
shotsLayer[Symbol.for("meta")] = {name: _("Cameras"), icon: "fa fa-camera fa-fw"};
if (this.props.tiles.length > 1){
if (this.taskCount > 1){
// Assign to a group
shotsLayer[Symbol.for("meta")].group = {id: meta.task.id, name: meta.task.name};
}
@ -405,7 +455,7 @@ class Map extends React.Component {
}
});
gcpLayer[Symbol.for("meta")] = {name: _("Ground Control Points"), icon: "far fa-dot-circle fa-fw"};
if (this.props.tiles.length > 1){
if (this.taskCount > 1){
// Assign to a group
gcpLayer[Symbol.for("meta")].group = {id: meta.task.id, name: meta.task.name};
}
@ -443,12 +493,17 @@ class Map extends React.Component {
maxZoom: 24
});
this.map.on('viewreset', this.layerVisibilityCheck);
this.map.on('zoomstart', this.layerVisibilityCheck);
this.map.on('movestart', this.layerVisibilityCheck);
// For some reason, in production this class is not added (but we need it)
// leaflet bug?
$(this.container).addClass("leaflet-touch");
PluginsAPI.Map.onAddAnnotation(this.handleAddAnnotation);
PluginsAPI.Map.onAnnotationDeleted(this.handleDeleteAnnotation);
PluginsAPI.Map.onSideBySideChanged(this.handleSideBySideChange);
PluginsAPI.Map.triggerWillAddControls({
map: this.map,
@ -592,10 +647,11 @@ _('Example:'),
this.map.on('click', e => {
if (PluginsAPI.Map.handleClick(e)) return;
if (this.sideBySideCtrl) return;
// Find first tile layer at the selected coordinates
// Find first visible tile layer at the selected coordinates
for (let layer of this.state.imageryLayers){
if (layer._map && layer.options.bounds.contains(e.latlng)){
if (layer._map && !layer.isHidden() && layer.options.bounds.contains(e.latlng)){
this.lastClickedLatLng = this.map.mouseEventToLatLng(e.originalEvent);
this.updatePopupFor(layer);
layer.openPopup();
@ -640,7 +696,7 @@ _('Example:'),
}).catch(e => {
this.setState({showLoading: false, error: e.message});
});
PluginsAPI.Map.triggerDidAddControls({
map: this.map,
tiles: tiles,
@ -665,7 +721,7 @@ _('Example:'),
name: name || "",
icon: "fa fa-sticky-note fa-fw"
};
if (this.props.tiles.length > 1 && task){
if (this.taskCount > 1 && task){
meta.group = {id: task.id, name: task.name};
}
layer[Symbol.for("meta")] = meta;
@ -679,14 +735,54 @@ _('Example:'),
this.setState({annotations: this.state.annotations.filter(l => l !== layer)});
}
handleSideBySideChange = (layer, side) => {
let { rightLayers, imageryLayers } = this.state;
imageryLayers.forEach(l => l.restoreZIndex());
rightLayers = rightLayers.filter(l => l !== layer);
if (side){
rightLayers.push(layer);
}
rightLayers.forEach(l => l.bringToFront());
this.setState({rightLayers});
// Make sure to reset clipping
imageryLayers.forEach(l => {
let container = l.getContainer();
if (container) container.style.clip = '';
});
if (rightLayers.length > 0){
if (!this.sideBySideCtrl){
this.sideBySideCtrl = L.control.sideBySide([], rightLayers).addTo(this.map);
}else{
this.sideBySideCtrl.setRightLayers(rightLayers);
}
}else{
if (this.sideBySideCtrl){
this.sideBySideCtrl.remove();
this.sideBySideCtrl = null;
}
}
}
layerVisibilityCheck = () => {
// Check if imageryLayers are invisible and remove them to prevent tiles from loading
this.state.imageryLayers.forEach(layer => {
if (layer.isHidden()) this.map.removeLayer(layer);
});
}
componentDidUpdate(prevProps, prevState) {
this.state.imageryLayers.forEach(imageryLayer => {
imageryLayer.setOpacity(this.state.opacity / 100);
this.updatePopupFor(imageryLayer);
});
if (prevProps.tiles !== this.props.tiles){
this.loadImageryLayers(true);
if (this.layersControl && prevProps.mapType !== this.props.mapType){
PluginsAPI.Map.mapTypeChanged(this.props.mapType, this.taskCount === 1);
}
if (this.layersControl && (prevState.imageryLayers !== this.state.imageryLayers ||
@ -698,6 +794,9 @@ _('Example:'),
componentWillUnmount() {
this.map.remove();
this.map.off('viewreset', this.layerVisibilityCheck);
this.map.off('zoomstart', this.layerVisibilityCheck);
this.map.off('movestart', this.layerVisibilityCheck);
if (this.tileJsonRequests) {
this.tileJsonRequests.forEach(tileJsonRequest => tileJsonRequest.abort());
@ -706,7 +805,7 @@ _('Example:'),
PluginsAPI.Map.offAddAnnotation(this.handleAddAnnotation);
PluginsAPI.Map.offAnnotationDeleted(this.handleAddAnnotation);
PluginsAPI.Map.offSideBySideChanged(this.handleSideBySideChange);
}
handleMapMouseDown(e){
@ -719,7 +818,7 @@ _('Example:'),
<div style={{height: "100%"}} className="map">
<ErrorMessage bind={[this, 'error']} />
<div className="opacity-slider theme-secondary hidden-xs">
<div className="opacity-slider-label">{_("Opacity:")}</div> <input type="range" step="1" value={this.state.opacity} onChange={this.updateOpacity} />
<div className="opacity-slider-label">{_("Opacity:")}</div> <input type="range" className="opacity" step="1" value={this.state.opacity} onChange={this.updateOpacity} />
</div>
<Standby

Wyświetl plik

@ -35,14 +35,16 @@ class Toggle extends React.Component {
class Checkbox extends Toggle{
static defaultProps = {
trueIcon: "far fa-check-square",
falseIcon: "far fa-square"
falseIcon: "far fa-square",
className: ""
}
}
class ExpandButton extends Toggle{
static defaultProps = {
trueIcon: "fa fa-caret-down",
falseIcon: "fa fa-caret-right"
falseIcon: "fa fa-caret-right",
className: ""
}
}

Wyświetl plik

@ -21,6 +21,15 @@
padding-right: 6px;
}
.expand-layer{
margin-right: 3px;
}
hr.layer-separator{
margin-top: 6px;
margin-left: 8px;
}
select, input{
height: auto;
padding: 4px;

Wyświetl plik

@ -12,7 +12,7 @@
height: calc(100% - 20px);
position: relative;
input[type="range"]{
input.opacity[type="range"]{
margin-left: 4px;
display: inline-block;
width: 130px;

Wyświetl plik

@ -0,0 +1,19 @@
Copyright (c) 2015 Gregor MacLennan
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Wyświetl plik

@ -0,0 +1,17 @@
.leaflet-sbs-range {
position: absolute;
top: 50%;
width: 100%;
z-index: 999;
}
.leaflet-sbs-divider {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
margin-left: -2px;
width: 4px;
background-color: #fff;
pointer-events: none;
z-index: 999;
}

Wyświetl plik

@ -0,0 +1,270 @@
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
(function (global){
var L = (typeof window !== "undefined" ? window['L'] : typeof global !== "undefined" ? global['L'] : null)
require('./layout.css')
require('./range.css')
var mapWasDragEnabled
var mapWasTapEnabled
// Leaflet v0.7 backwards compatibility
function on (el, types, fn, context) {
types.split(' ').forEach(function (type) {
L.DomEvent.on(el, type, fn, context)
})
}
// Leaflet v0.7 backwards compatibility
function off (el, types, fn, context) {
types.split(' ').forEach(function (type) {
L.DomEvent.off(el, type, fn, context)
})
}
function getRangeEvent (rangeInput) {
return 'oninput' in rangeInput ? 'input' : 'change'
}
function cancelMapDrag () {
mapWasDragEnabled = this._map.dragging.enabled()
mapWasTapEnabled = this._map.tap && this._map.tap.enabled()
this._map.dragging.disable()
this._map.tap && this._map.tap.disable()
}
function uncancelMapDrag (e) {
this._refocusOnMap(e)
if (mapWasDragEnabled) {
this._map.dragging.enable()
}
if (mapWasTapEnabled) {
this._map.tap.enable()
}
}
// convert arg to an array - returns empty array if arg is undefined
function asArray (arg) {
return (arg === 'undefined') ? [] : Array.isArray(arg) ? arg : [arg]
}
function noop () {}
L.Control.SideBySide = L.Control.extend({
options: {
thumbSize: 42,
padding: 0
},
initialize: function (leftLayers, rightLayers, options) {
this.setLeftLayers(leftLayers)
this.setRightLayers(rightLayers)
L.setOptions(this, options)
},
getPosition: function () {
var rangeValue = this._range.value
var offset = (0.5 - rangeValue) * (2 * this.options.padding + this.options.thumbSize)
return this._map.getSize().x * rangeValue + offset
},
setPosition: noop,
includes: L.Evented.prototype || L.Mixin.Events,
addTo: function (map) {
this.remove()
this._map = map
var container = this._container = L.DomUtil.create('div', 'leaflet-sbs', map._controlContainer)
this._divider = L.DomUtil.create('div', 'leaflet-sbs-divider', container)
var range = this._range = L.DomUtil.create('input', 'leaflet-sbs-range', container)
range.type = 'range'
range.min = 0
range.max = 1
range.step = 'any'
range.value = 0.5
range.style.paddingLeft = range.style.paddingRight = this.options.padding + 'px'
this._addEvents()
this._updateLayers()
return this
},
remove: function () {
if (!this._map) {
return this
}
if (this._leftLayer) {
this._leftLayer.getContainer().style.clip = ''
}
if (this._rightLayer) {
this._rightLayer.getContainer().style.clip = ''
}
this._removeEvents()
L.DomUtil.remove(this._container)
this._map = null
return this
},
setLeftLayers: function (leftLayers) {
this._leftLayers = asArray(leftLayers)
this._updateLayers()
return this
},
setRightLayers: function (rightLayers) {
this._rightLayers = asArray(rightLayers)
this._updateLayers()
return this
},
_updateClip: function () {
var map = this._map
var nw = map.containerPointToLayerPoint([0, 0])
var se = map.containerPointToLayerPoint(map.getSize())
var clipX = nw.x + this.getPosition()
var dividerX = this.getPosition()
this._divider.style.left = dividerX + 'px'
this.fire('dividermove', {x: dividerX})
var clipLeft = 'rect(' + [nw.y, clipX, se.y, nw.x].join('px,') + 'px)'
var clipRight = 'rect(' + [nw.y, se.x, se.y, clipX].join('px,') + 'px)'
if (this._leftLayer) {
this._leftLayer.getContainer().style.clip = clipLeft
}
if (this._rightLayer) {
this._rightLayer.getContainer().style.clip = clipRight
}
},
_updateLayers: function () {
if (!this._map) {
return this
}
var prevLeft = this._leftLayer
var prevRight = this._rightLayer
this._leftLayer = this._rightLayer = null
this._leftLayers.forEach(function (layer) {
if (this._map.hasLayer(layer)) {
this._leftLayer = layer
}
}, this)
this._rightLayers.forEach(function (layer) {
if (this._map.hasLayer(layer)) {
this._rightLayer = layer
}
}, this)
if (prevLeft !== this._leftLayer) {
prevLeft && this.fire('leftlayerremove', {layer: prevLeft})
this._leftLayer && this.fire('leftlayeradd', {layer: this._leftLayer})
}
if (prevRight !== this._rightLayer) {
prevRight && this.fire('rightlayerremove', {layer: prevRight})
this._rightLayer && this.fire('rightlayeradd', {layer: this._rightLayer})
}
this._updateClip()
},
_addEvents: function () {
var range = this._range
var map = this._map
if (!map || !range) return
map.on('move', this._updateClip, this)
map.on('layeradd layerremove', this._updateLayers, this)
on(range, getRangeEvent(range), this._updateClip, this)
on(range, L.Browser.touch ? 'touchstart' : 'mousedown', cancelMapDrag, this)
on(range, L.Browser.touch ? 'touchend' : 'mouseup', uncancelMapDrag, this)
},
_removeEvents: function () {
var range = this._range
var map = this._map
if (range) {
off(range, getRangeEvent(range), this._updateClip, this)
off(range, L.Browser.touch ? 'touchstart' : 'mousedown', cancelMapDrag, this)
off(range, L.Browser.touch ? 'touchend' : 'mouseup', uncancelMapDrag, this)
}
if (map) {
map.off('layeradd layerremove', this._updateLayers, this)
map.off('move', this._updateClip, this)
}
}
})
L.control.sideBySide = function (leftLayers, rightLayers, options) {
return new L.Control.SideBySide(leftLayers, rightLayers, options)
}
module.exports = L.Control.SideBySide
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./layout.css":2,"./range.css":4}],2:[function(require,module,exports){
var inject = require('./node_modules/cssify');
var css = ".leaflet-sbs-range {\r\n position: absolute;\r\n top: 50%;\r\n width: 100%;\r\n z-index: 999;\r\n}\r\n.leaflet-sbs-divider {\r\n position: absolute;\r\n top: 0;\r\n bottom: 0;\r\n left: 50%;\r\n margin-left: -2px;\r\n width: 4px;\r\n background-color: #fff;\r\n pointer-events: none;\r\n z-index: 999;\r\n}\r\n";
inject(css, undefined, '_i6aomd');
module.exports = css;
},{"./node_modules/cssify":3}],3:[function(require,module,exports){
'use strict'
function injectStyleTag (document, fileName, cb) {
var style = document.getElementById(fileName)
if (style) {
cb(style)
} else {
var head = document.getElementsByTagName('head')[0]
style = document.createElement('style')
if (fileName != null) style.id = fileName
cb(style)
head.appendChild(style)
}
return style
}
module.exports = function (css, customDocument, fileName) {
var doc = customDocument || document
/* istanbul ignore if: not supported by Electron */
if (doc.createStyleSheet) {
var sheet = doc.createStyleSheet()
sheet.cssText = css
return sheet.ownerNode
} else {
return injectStyleTag(doc, fileName, function (style) {
/* istanbul ignore if: not supported by Electron */
if (style.styleSheet) {
style.styleSheet.cssText = css
} else {
style.innerHTML = css
}
})
}
}
module.exports.byUrl = function (url) {
/* istanbul ignore if: not supported by Electron */
if (document.createStyleSheet) {
return document.createStyleSheet(url).ownerNode
} else {
var head = document.getElementsByTagName('head')[0]
var link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
head.appendChild(link)
return link
}
}
},{}],4:[function(require,module,exports){
var inject = require('./node_modules/cssify');
var css = ".leaflet-sbs-range {\r\n -webkit-appearance: none;\r\n display: inline-block!important;\r\n vertical-align: middle;\r\n height: 0;\r\n padding: 0;\r\n margin: 0;\r\n border: 0;\r\n background: rgba(0, 0, 0, 0.25);\r\n min-width: 100px;\r\n cursor: pointer;\r\n pointer-events: none;\r\n z-index: 999;\r\n}\r\n.leaflet-sbs-range::-ms-fill-upper {\r\n background: transparent;\r\n}\r\n.leaflet-sbs-range::-ms-fill-lower {\r\n background: rgba(255, 255, 255, 0.25);\r\n}\r\n/* Browser thingies */\r\n\r\n.leaflet-sbs-range::-moz-range-track {\r\n opacity: 0;\r\n}\r\n.leaflet-sbs-range::-ms-track {\r\n opacity: 0;\r\n}\r\n.leaflet-sbs-range::-ms-tooltip {\r\n display: none;\r\n}\r\n/* For whatever reason, these need to be defined\r\n * on their own so dont group them */\r\n\r\n.leaflet-sbs-range::-webkit-slider-thumb {\r\n -webkit-appearance: none;\r\n margin: 0;\r\n padding: 0;\r\n background: #fff;\r\n height: 40px;\r\n width: 40px;\r\n border-radius: 20px;\r\n cursor: ew-resize;\r\n pointer-events: auto;\r\n border: 1px solid #ddd;\r\n background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAABlBMVEV9fX3///+Kct39AAAAAnRSTlP/AOW3MEoAAAA9SURBVFjD7dehDQAwDANBZ/+l2wmKoiqR7pHRcaeaCxAIBAL/g7k9JxAIBAKBQCAQCAQC14H+MhAIBE4CD3fOFvGVBzhZAAAAAElFTkSuQmCC\");\r\n background-position: 50% 50%;\r\n background-repeat: no-repeat;\r\n background-size: 40px 40px;\r\n}\r\n.leaflet-sbs-range::-ms-thumb {\r\n margin: 0;\r\n padding: 0;\r\n background: #fff;\r\n height: 40px;\r\n width: 40px;\r\n border-radius: 20px;\r\n cursor: ew-resize;\r\n pointer-events: auto;\r\n border: 1px solid #ddd;\r\n background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAABlBMVEV9fX3///+Kct39AAAAAnRSTlP/AOW3MEoAAAA9SURBVFjD7dehDQAwDANBZ/+l2wmKoiqR7pHRcaeaCxAIBAL/g7k9JxAIBAKBQCAQCAQC14H+MhAIBE4CD3fOFvGVBzhZAAAAAElFTkSuQmCC\");\r\n background-position: 50% 50%;\r\n background-repeat: no-repeat;\r\n background-size: 40px 40px;\r\n}\r\n.leaflet-sbs-range::-moz-range-thumb {\r\n padding: 0;\r\n right: 0 ;\r\n background: #fff;\r\n height: 40px;\r\n width: 40px;\r\n border-radius: 20px;\r\n cursor: ew-resize;\r\n pointer-events: auto;\r\n border: 1px solid #ddd;\r\n background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAABlBMVEV9fX3///+Kct39AAAAAnRSTlP/AOW3MEoAAAA9SURBVFjD7dehDQAwDANBZ/+l2wmKoiqR7pHRcaeaCxAIBAL/g7k9JxAIBAKBQCAQCAQC14H+MhAIBE4CD3fOFvGVBzhZAAAAAElFTkSuQmCC\");\r\n background-position: 50% 50%;\r\n background-repeat: no-repeat;\r\n background-size: 40px 40px;\r\n}\r\n.leaflet-sbs-range:disabled::-moz-range-thumb {\r\n cursor: default;\r\n}\r\n.leaflet-sbs-range:disabled::-ms-thumb {\r\n cursor: default;\r\n}\r\n.leaflet-sbs-range:disabled::-webkit-slider-thumb {\r\n cursor: default;\r\n}\r\n.leaflet-sbs-range:disabled {\r\n cursor: default;\r\n}\r\n.leaflet-sbs-range:focus {\r\n outline: none!important;\r\n}\r\n.leaflet-sbs-range::-moz-focus-outer {\r\n border: 0;\r\n}\r\n\r\n";
inject(css, undefined, '_1tlt668');
module.exports = css;
},{"./node_modules/cssify":3}]},{},[1]);

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 150 B

Wyświetl plik

@ -0,0 +1,98 @@
.leaflet-sbs-range {
-webkit-appearance: none;
display: inline-block!important;
vertical-align: middle;
height: 0;
padding: 0;
margin: 0;
border: 0;
background: rgba(0, 0, 0, 0.25);
min-width: 100px;
cursor: pointer;
pointer-events: none;
z-index: 999;
}
.leaflet-sbs-range::-ms-fill-upper {
background: transparent;
}
.leaflet-sbs-range::-ms-fill-lower {
background: rgba(255, 255, 255, 0.25);
}
/* Browser thingies */
.leaflet-sbs-range::-moz-range-track {
opacity: 0;
}
.leaflet-sbs-range::-ms-track {
opacity: 0;
}
.leaflet-sbs-range::-ms-tooltip {
display: none;
}
/* For whatever reason, these need to be defined
* on their own so dont group them */
.leaflet-sbs-range::-webkit-slider-thumb {
-webkit-appearance: none;
margin: 0;
padding: 0;
background: #fff;
height: 40px;
width: 40px;
border-radius: 20px;
cursor: ew-resize;
pointer-events: auto;
border: 1px solid #ddd;
background-image: url(range-icon.png);
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: 40px 40px;
}
.leaflet-sbs-range::-ms-thumb {
margin: 0;
padding: 0;
background: #fff;
height: 40px;
width: 40px;
border-radius: 20px;
cursor: ew-resize;
pointer-events: auto;
border: 1px solid #ddd;
background-image: url(range-icon.png);
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: 40px 40px;
}
.leaflet-sbs-range::-moz-range-thumb {
padding: 0;
right: 0 ;
background: #fff;
height: 40px;
width: 40px;
border-radius: 20px;
cursor: ew-resize;
pointer-events: auto;
border: 1px solid #ddd;
background-image: url(range-icon.png);
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: 40px 40px;
}
.leaflet-sbs-range:disabled::-moz-range-thumb {
cursor: default;
}
.leaflet-sbs-range:disabled::-ms-thumb {
cursor: default;
}
.leaflet-sbs-range:disabled::-webkit-slider-thumb {
cursor: default;
}
.leaflet-sbs-range:disabled {
cursor: default;
}
.leaflet-sbs-range:focus {
outline: none!important;
}
.leaflet-sbs-range::-moz-focus-outer {
border: 0;
}

Wyświetl plik

@ -10,7 +10,7 @@ import {
ImplicitTaskFetcher as TaskFetcher,
APIFetcher
} from "./components/Fetcher";
import {AssetConfig, AssetStyles} from "./defaults";
import {AssetConfig} from "./defaults";
import { fetchCancelable, getCookie } from "./utils";
export default class TaskView extends Component {

Wyświetl plik

@ -1,6 +1,6 @@
import React, { PureComponent, Fragment } from "react";
import IonAssetLabel from "./IonAssetLabel";
import { AssetStyles } from "../defaults";
import { AssetConfig } from "../defaults";
import "./IonAssetButton.scss";
@ -22,7 +22,7 @@ export default class IonAssetButton extends PureComponent {
} = this.props;
const menuItems = assets
.sort((a, b) => AssetStyles[a].name.localeCompare(AssetStyles[b].name))
.sort((a, b) => AssetConfig[a].name.localeCompare(AssetConfig[b].name))
.map(asset => (
<li>
<a key={asset} style={{cursor:'pointer'}} onClick={this.handleClick(asset)}>

Wyświetl plik

@ -1,11 +1,11 @@
import React, { PureComponent, Fragment } from "react";
import { AssetStyles } from "../defaults";
import { AssetConfig } from "../defaults";
const IonAssetLabel = ({ asset, showIcon = false, ...options }) => (
<Fragment>
{showIcon && <i className={`${AssetStyles[asset].icon}`} />}
{showIcon && <i className={`${AssetConfig[asset].icon}`} />}
{" "}
{AssetStyles[asset].name}
{AssetConfig[asset].name}
</Fragment>
);

Wyświetl plik

@ -1,6 +1,6 @@
{
"name": "WebODM",
"version": "2.6.0",
"version": "2.7.0",
"description": "User-friendly, extendable application and API for processing aerial imagery.",
"main": "index.js",
"scripts": {