kopia lustrzana https://github.com/OpenDroneMap/WebODM
Refactor crop button
rodzic
ac03ad071a
commit
48eec415e0
|
@ -0,0 +1,278 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'ReactDOM';
|
||||
import L from 'leaflet';
|
||||
import '../css/CropButton.scss';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _ } from '../classes/gettext';
|
||||
|
||||
const Colors = {
|
||||
fill: '#fff',
|
||||
stroke: '#1a1a1a'
|
||||
};
|
||||
|
||||
class CropButton extends React.Component {
|
||||
static defaultProps = {
|
||||
group: null,
|
||||
title: _("Crop"),
|
||||
onPolygonCreated: () => {},
|
||||
onPolygonChange: () => {}
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
map: PropTypes.object.isRequired,
|
||||
group: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
onPolygonCreated: PropTypes.func,
|
||||
onPolygonChange: PropTypes.func
|
||||
};
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
cropping: false
|
||||
}
|
||||
|
||||
this.map = props.map;
|
||||
this.group = props.group;
|
||||
if (!this.group){
|
||||
this.group = L.layerGroup();
|
||||
this.group.addTo(this.map);
|
||||
}
|
||||
}
|
||||
|
||||
toggleCrop = () => {
|
||||
const { cropping } = this.state;
|
||||
|
||||
let crop = !cropping;
|
||||
if (!crop) {
|
||||
if (this.captureMarker) {
|
||||
this.captureMarker.off('click', this.handleMarkerClick);
|
||||
this.captureMarker.off('dblclick', this.handleMarkerDblClick);
|
||||
this.captureMarker.off('mousemove', this.handleMarkerMove);
|
||||
this.captureMarker.off('contextmenu', this.handleMarkerContextMenu);
|
||||
|
||||
this.map.off('move', this.onMapMove);
|
||||
this.map.off('resize', this.onMapResize);
|
||||
|
||||
this.group.removeLayer(this.captureMarker);
|
||||
this.captureMarker = null;
|
||||
}
|
||||
|
||||
if (this.acceptMarker) {
|
||||
this.group.removeLayer(this.acceptMarker);
|
||||
this.acceptMarker = null;
|
||||
}
|
||||
if (this.measureBoundary) {
|
||||
this.group.removeLayer(this.measureBoundary);
|
||||
this.measureBoundary = null;
|
||||
}
|
||||
if (this.measureArea) {
|
||||
this.group.removeLayer(this.measureArea);
|
||||
this.measureArea = null;
|
||||
}
|
||||
}else{
|
||||
if (!this.captureMarker) {
|
||||
this.captureMarker = L.marker(this.map.getCenter(), {
|
||||
clickable: true,
|
||||
zIndexOffset: 10001
|
||||
}).setIcon(L.divIcon({
|
||||
iconSize: this.map.getSize().multiplyBy(2),
|
||||
className: "crop-button-marker-layer"
|
||||
})).addTo(this.group);
|
||||
|
||||
this.captureMarker.on('click', this.handleMarkerClick);
|
||||
this.captureMarker.on('dblclick', this.handleMarkerDblClick);
|
||||
this.captureMarker.on('mousemove', this.handleMarkerMove);
|
||||
this.captureMarker.on('contextmenu', this.handleMarkerContextMenu);
|
||||
|
||||
this.map.on('move', this.onMapMove);
|
||||
this.map.on('resize', this.onMapResize);
|
||||
}
|
||||
|
||||
this.deletePolygon();
|
||||
|
||||
// Reset latlngs
|
||||
this.latlngs = [];
|
||||
}
|
||||
|
||||
this.setState({cropping: !cropping});
|
||||
}
|
||||
|
||||
handleMarkerClick = e => {
|
||||
L.DomEvent.stop(e);
|
||||
|
||||
const latlng = this.map.mouseEventToLatLng(e.originalEvent);
|
||||
this.uniqueLatLonPush(latlng);
|
||||
|
||||
if (this.latlngs.length >= 1) {
|
||||
if (!this.measureBoundary) {
|
||||
this.measureBoundary = L.polyline(this.latlngs.concat(latlng), {
|
||||
clickable: false,
|
||||
color: Colors.stroke,
|
||||
weight: 2,
|
||||
opacity: 0.9,
|
||||
fill: false,
|
||||
}).addTo(this.group);
|
||||
} else {
|
||||
this.measureBoundary.setLatLngs(this.latlngs.concat(latlng));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.latlngs.length >= 2) {
|
||||
if (!this.measureArea) {
|
||||
this.measureArea = L.polygon(this.latlngs.concat(latlng), {
|
||||
clickable: false,
|
||||
stroke: false,
|
||||
fillColor: Colors.fill,
|
||||
fillOpacity: 0.2,
|
||||
}).addTo(this.group);
|
||||
} else {
|
||||
this.measureArea.setLatLngs(this.latlngs.concat(latlng));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.latlngs.length >= 3) {
|
||||
if (this.acceptMarker) {
|
||||
this.group.removeLayer(this.acceptMarker);
|
||||
this.acceptMarker = null;
|
||||
}
|
||||
|
||||
const onAccept = e => {
|
||||
L.DomEvent.stop(e);
|
||||
this.confirmPolygon();
|
||||
return false;
|
||||
};
|
||||
|
||||
let acceptLatlng = this.latlngs[0];
|
||||
|
||||
this.acceptMarker = L.marker(acceptLatlng, {
|
||||
icon: L.icon({
|
||||
iconUrl: `/static/app/img/accept.png`,
|
||||
iconSize: [20, 20],
|
||||
iconAnchor: [10, 10],
|
||||
className: "crop-button-accept-button",
|
||||
}),
|
||||
zIndexOffset: 99999
|
||||
}).addTo(this.group)
|
||||
.on("click", onAccept)
|
||||
.on("contextmenu", onAccept);
|
||||
}
|
||||
}
|
||||
|
||||
deletePolygon = () => {
|
||||
if (this.polygon){
|
||||
this.group.removeLayer(this.polygon);
|
||||
this.polygon = null;
|
||||
this.props.onPolygonChange();
|
||||
}
|
||||
}
|
||||
|
||||
confirmPolygon = () => {
|
||||
if (this.latlngs.length >= 3){
|
||||
this.polygon = L.polygon(this.latlngs, {
|
||||
clickable: true,
|
||||
weight: 3,
|
||||
opacity: 0.9,
|
||||
color: "#ffa716",
|
||||
fillColor: "#ffa716",
|
||||
fillOpacity: 0.2
|
||||
}).addTo(this.group);
|
||||
|
||||
this.props.onPolygonCreated(this.polygon);
|
||||
this.props.onPolygonChange();
|
||||
}
|
||||
|
||||
this.toggleCrop();
|
||||
}
|
||||
|
||||
getCropPolygon = () => {
|
||||
if (!this.polygon) return null;
|
||||
return this.polygon.toGeoJSON(14);
|
||||
}
|
||||
|
||||
uniqueLatLonPush = latlng => {
|
||||
if (this.latlngs.length === 0) this.latlngs.push(latlng);
|
||||
else{
|
||||
const last = this.latlngs[this.latlngs.length - 1];
|
||||
if (last.lat !== latlng.lat && last.lng !== latlng.lng) this.latlngs.push(latlng);
|
||||
}
|
||||
};
|
||||
|
||||
handleMarkerDblClick = e => {
|
||||
if (this.latlngs.length >= 2){
|
||||
const latlng = this.map.mouseEventToLatLng(e.originalEvent);
|
||||
this.uniqueLatLonPush(latlng);
|
||||
this.confirmPolygon();
|
||||
}
|
||||
}
|
||||
|
||||
handleMarkerMove = e => {
|
||||
const latlng = this.map.mouseEventToLatLng(e.originalEvent);
|
||||
let lls = this.latlngs.concat(latlng);
|
||||
lls.push(lls[0]);
|
||||
if (this.measureBoundary) {
|
||||
this.measureBoundary.setLatLngs(lls);
|
||||
}
|
||||
if (this.measureArea) {
|
||||
this.measureArea.setLatLngs(lls);
|
||||
}
|
||||
}
|
||||
|
||||
handleMarkerContextMenu = e => {
|
||||
if (this.latlngs.length >= 2){
|
||||
const latlng = this.map.mouseEventToLatLng(e.originalEvent);
|
||||
this.uniqueLatLonPush(latlng);
|
||||
this.confirmPolygon();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
onMapMove = () => {
|
||||
if (this.captureMarker) this.captureMarker.setLatLng(this.map.getCenter());
|
||||
};
|
||||
|
||||
onMapResize = () => {
|
||||
if (this.captureMarker) this.captureMarker.setIcon(L.divIcon({
|
||||
iconSize: this._map.getSize().multiplyBy(2)
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<div>
|
||||
<a href="javascript:void(0);"
|
||||
onClick={this.toggleCrop}
|
||||
title={this.props.title}
|
||||
className={"leaflet-control-crop-button leaflet-bar-part theme-secondary " + (this.state.cropping ? "selected" : "")}><i className="fa fa-crop-alt"></i></a>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
export default L.Control.extend({
|
||||
_btn: null,
|
||||
|
||||
options: {
|
||||
position: 'topright'
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
var container = L.DomUtil.create('div', 'crop-button leaflet-control-crop leaflet-bar leaflet-control');
|
||||
L.DomEvent.disableClickPropagation(container);
|
||||
ReactDOM.render(<CropButton ref={(domNode) => this._btn = domNode} map={map}
|
||||
group={this.options.group}
|
||||
title={this.options.title}
|
||||
onPolygonCreated={this.options.onPolygonCreated}
|
||||
onPolygonChange={this.options.onPolygonChange} />, container);
|
||||
|
||||
return container;
|
||||
},
|
||||
|
||||
deletePolygon: function(){
|
||||
return this._btn.deletePolygon();
|
||||
},
|
||||
|
||||
getCropPolygon: function(){
|
||||
return this._btn.getCropPolygon();
|
||||
}
|
||||
});
|
|
@ -13,14 +13,10 @@ import Standby from './Standby';
|
|||
import exifr from '../vendor/exifr';
|
||||
import '../vendor/leaflet/leaflet-markers-canvas';
|
||||
import { _, interpolate } from '../classes/gettext';
|
||||
import CropButton from './CropButton';
|
||||
import 'leaflet-fullscreen/dist/Leaflet.fullscreen';
|
||||
import 'leaflet-fullscreen/dist/leaflet.fullscreen.css';
|
||||
|
||||
const Colors = {
|
||||
fill: '#fff',
|
||||
stroke: '#1a1a1a'
|
||||
};
|
||||
|
||||
class MapPreview extends React.Component {
|
||||
static defaultProps = {
|
||||
getFiles: null,
|
||||
|
@ -39,8 +35,7 @@ class MapPreview extends React.Component {
|
|||
|
||||
this.state = {
|
||||
showLoading: true,
|
||||
error: "",
|
||||
cropping: false
|
||||
error: ""
|
||||
};
|
||||
|
||||
this.basemaps = {};
|
||||
|
@ -147,6 +142,15 @@ _('Example:'),
|
|||
});
|
||||
}
|
||||
|
||||
this.cropButton = new CropButton({
|
||||
position:'bottomleft',
|
||||
title: _("Set Reconstruction Area (optional)"),
|
||||
group: this.group,
|
||||
onPolygonCreated: this.onPolygonCreated,
|
||||
onPolygonChange: this.props.onPolygonChange
|
||||
});
|
||||
this.map.addControl(this.cropButton);
|
||||
|
||||
this.map.fitBounds([
|
||||
[13.772919746115805,
|
||||
45.664640939831735],
|
||||
|
@ -157,6 +161,21 @@ _('Example:'),
|
|||
this.loadNewFiles();
|
||||
}
|
||||
|
||||
onPolygonCreated = (polygon) => {
|
||||
const popupContainer = L.DomUtil.create('div');
|
||||
popupContainer.className = "crop-button-delete";
|
||||
const deleteLink = L.DomUtil.create('a');
|
||||
deleteLink.href = "javascript:void(0)";
|
||||
deleteLink.innerHTML = `<i class="fa fa-trash"></i> ${_("Delete")}`;
|
||||
deleteLink.onclick = (e) => {
|
||||
L.DomEvent.stop(e);
|
||||
this.cropButton.deletePolygon();
|
||||
};
|
||||
popupContainer.appendChild(deleteLink);
|
||||
|
||||
polygon.bindPopup(popupContainer);
|
||||
}
|
||||
|
||||
sampled = (arr, N) => {
|
||||
// Return a uniformly sampled array with max N elements
|
||||
if (arr.length <= N) return arr;
|
||||
|
@ -316,214 +335,7 @@ _('Example:'),
|
|||
}
|
||||
|
||||
getCropPolygon = () => {
|
||||
if (!this.polygon) return null;
|
||||
return this.polygon.toGeoJSON(14);
|
||||
}
|
||||
|
||||
toggleCrop = () => {
|
||||
const { cropping } = this.state;
|
||||
|
||||
let crop = !cropping;
|
||||
if (!crop) {
|
||||
if (this.captureMarker) {
|
||||
this.captureMarker.off('click', this.handleMarkerClick);
|
||||
this.captureMarker.off('dblclick', this.handleMarkerDblClick);
|
||||
this.captureMarker.off('mousemove', this.handleMarkerMove);
|
||||
this.captureMarker.off('contextmenu', this.handleMarkerContextMenu);
|
||||
|
||||
this.map.off('move', this.onMapMove);
|
||||
this.map.off('resize', this.onMapResize);
|
||||
|
||||
this.group.removeLayer(this.captureMarker);
|
||||
this.captureMarker = null;
|
||||
}
|
||||
|
||||
if (this.acceptMarker) {
|
||||
this.group.removeLayer(this.acceptMarker);
|
||||
this.acceptMarker = null;
|
||||
}
|
||||
if (this.measureBoundary) {
|
||||
this.group.removeLayer(this.measureBoundary);
|
||||
this.measureBoundary = null;
|
||||
}
|
||||
if (this.measureArea) {
|
||||
this.group.removeLayer(this.measureArea);
|
||||
this.measureArea = null;
|
||||
}
|
||||
this.cropButton.blur();
|
||||
}
|
||||
else{
|
||||
if (!this.captureMarker) {
|
||||
this.captureMarker = L.marker(this.map.getCenter(), {
|
||||
clickable: true,
|
||||
zIndexOffset: 10001
|
||||
}).setIcon(L.divIcon({
|
||||
iconSize: this.map.getSize().multiplyBy(2),
|
||||
className: "map-preview-marker-layer"
|
||||
})).addTo(this.group);
|
||||
|
||||
this.captureMarker.on('click', this.handleMarkerClick);
|
||||
this.captureMarker.on('dblclick', this.handleMarkerDblClick);
|
||||
this.captureMarker.on('mousemove', this.handleMarkerMove);
|
||||
this.captureMarker.on('contextmenu', this.handleMarkerContextMenu);
|
||||
|
||||
this.map.on('move', this.onMapMove);
|
||||
this.map.on('resize', this.onMapResize);
|
||||
}
|
||||
|
||||
if (this.polygon){
|
||||
this.group.removeLayer(this.polygon);
|
||||
this.polygon = null;
|
||||
this.props.onPolygonChange();
|
||||
}
|
||||
|
||||
// Reset latlngs
|
||||
this.latlngs = [];
|
||||
}
|
||||
|
||||
|
||||
this.setState({cropping: !cropping});
|
||||
}
|
||||
|
||||
handleMarkerClick = e => {
|
||||
L.DomEvent.stop(e);
|
||||
|
||||
const latlng = this.map.mouseEventToLatLng(e.originalEvent);
|
||||
this.uniqueLatLonPush(latlng);
|
||||
|
||||
if (this.latlngs.length >= 1) {
|
||||
if (!this.measureBoundary) {
|
||||
this.measureBoundary = L.polyline(this.latlngs.concat(latlng), {
|
||||
clickable: false,
|
||||
color: Colors.stroke,
|
||||
weight: 2,
|
||||
opacity: 0.9,
|
||||
fill: false,
|
||||
}).addTo(this.group);
|
||||
} else {
|
||||
this.measureBoundary.setLatLngs(this.latlngs.concat(latlng));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.latlngs.length >= 2) {
|
||||
if (!this.measureArea) {
|
||||
this.measureArea = L.polygon(this.latlngs.concat(latlng), {
|
||||
clickable: false,
|
||||
stroke: false,
|
||||
fillColor: Colors.fill,
|
||||
fillOpacity: 0.2,
|
||||
}).addTo(this.group);
|
||||
} else {
|
||||
this.measureArea.setLatLngs(this.latlngs.concat(latlng));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.latlngs.length >= 3) {
|
||||
if (this.acceptMarker) {
|
||||
this.group.removeLayer(this.acceptMarker);
|
||||
this.acceptMarker = null;
|
||||
}
|
||||
|
||||
const onAccept = e => {
|
||||
L.DomEvent.stop(e);
|
||||
this.confirmPolygon();
|
||||
return false;
|
||||
};
|
||||
|
||||
let acceptLatlng = this.latlngs[0];
|
||||
|
||||
this.acceptMarker = L.marker(acceptLatlng, {
|
||||
icon: L.icon({
|
||||
iconUrl: `/static/app/img/accept.png`,
|
||||
iconSize: [20, 20],
|
||||
iconAnchor: [10, 10],
|
||||
className: "map-preview-accept-button",
|
||||
}),
|
||||
zIndexOffset: 99999
|
||||
}).addTo(this.group)
|
||||
.on("click", onAccept)
|
||||
.on("contextmenu", onAccept);
|
||||
}
|
||||
};
|
||||
|
||||
confirmPolygon = () => {
|
||||
if (this.latlngs.length >= 3){
|
||||
const popupContainer = L.DomUtil.create('div');
|
||||
popupContainer.className = "map-preview-delete";
|
||||
const deleteLink = L.DomUtil.create('a');
|
||||
deleteLink.href = "javascript:void(0)";
|
||||
deleteLink.innerHTML = `<i class="fa fa-trash"></i> ${_("Delete")}`;
|
||||
deleteLink.onclick = (e) => {
|
||||
L.DomEvent.stop(e);
|
||||
if (this.polygon){
|
||||
this.group.removeLayer(this.polygon);
|
||||
this.polygon = null;
|
||||
this.props.onPolygonChange();
|
||||
}
|
||||
};
|
||||
popupContainer.appendChild(deleteLink);
|
||||
|
||||
this.polygon = L.polygon(this.latlngs, {
|
||||
clickable: true,
|
||||
weight: 3,
|
||||
opacity: 0.9,
|
||||
color: "#ffa716",
|
||||
fillColor: "#ffa716",
|
||||
fillOpacity: 0.2
|
||||
}).bindPopup(popupContainer).addTo(this.group);
|
||||
|
||||
this.props.onPolygonChange();
|
||||
}
|
||||
|
||||
this.toggleCrop();
|
||||
}
|
||||
|
||||
uniqueLatLonPush = latlng => {
|
||||
if (this.latlngs.length === 0) this.latlngs.push(latlng);
|
||||
else{
|
||||
const last = this.latlngs[this.latlngs.length - 1];
|
||||
if (last.lat !== latlng.lat && last.lng !== latlng.lng) this.latlngs.push(latlng);
|
||||
}
|
||||
};
|
||||
|
||||
handleMarkerDblClick = e => {
|
||||
if (this.latlngs.length >= 2){
|
||||
const latlng = this.map.mouseEventToLatLng(e.originalEvent);
|
||||
this.uniqueLatLonPush(latlng);
|
||||
this.confirmPolygon();
|
||||
}
|
||||
}
|
||||
|
||||
handleMarkerMove = e => {
|
||||
const latlng = this.map.mouseEventToLatLng(e.originalEvent);
|
||||
let lls = this.latlngs.concat(latlng);
|
||||
lls.push(lls[0]);
|
||||
if (this.measureBoundary) {
|
||||
this.measureBoundary.setLatLngs(lls);
|
||||
}
|
||||
if (this.measureArea) {
|
||||
this.measureArea.setLatLngs(lls);
|
||||
}
|
||||
}
|
||||
|
||||
handleMarkerContextMenu = e => {
|
||||
if (this.latlngs.length >= 2){
|
||||
const latlng = this.map.mouseEventToLatLng(e.originalEvent);
|
||||
this.uniqueLatLonPush(latlng);
|
||||
this.confirmPolygon();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
onMapMove = () => {
|
||||
if (this.captureMarker) this.captureMarker.setLatLng(this.map.getCenter());
|
||||
};
|
||||
|
||||
onMapResize = () => {
|
||||
if (this.captureMarker) this.captureMarker.setIcon(L.divIcon({
|
||||
iconSize: this._map.getSize().multiplyBy(2)
|
||||
}));
|
||||
return this.cropButton.getCropPolygon();
|
||||
}
|
||||
|
||||
setAlignmentPolygon = (task) => {
|
||||
|
@ -632,14 +444,6 @@ _('Example:'),
|
|||
</ul>
|
||||
</div> : ""}
|
||||
|
||||
{this.state.error === "" ?
|
||||
<div className="crop-control">
|
||||
<button ref={(domNode) => {this.cropButton = domNode; }} type="button" onClick={this.toggleCrop} className={"btn btn-sm " + (this.state.cropping ? "btn-default" : "btn-secondary")} title={_("Set Reconstruction Area (optional)")}>
|
||||
<i className="fa fa-crop-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
: ""}
|
||||
|
||||
<div
|
||||
style={{height: "100%"}}
|
||||
ref={(domNode) => (this.container = domNode)}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
.crop-control:active, .crop-control:focus{
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.crop-button-marker-layer{
|
||||
&:hover{
|
||||
cursor: crosshair;
|
||||
}
|
||||
}
|
||||
|
||||
.crop-button-accept-button{
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
|
@ -20,16 +20,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.crop-control{
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 8px;
|
||||
z-index: 999;
|
||||
.btn:active, .btn:focus{
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.leaflet-control-layers-expanded{
|
||||
.leaflet-control-layers-base{
|
||||
overflow: hidden;
|
||||
|
@ -38,18 +28,7 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.map-preview-marker-layer{
|
||||
&:hover{
|
||||
cursor: crosshair;
|
||||
}
|
||||
}
|
||||
|
||||
.map-preview-accept-button{
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.map-preview-delete{
|
||||
.crop-button-delete{
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
|
|
|
@ -293,6 +293,11 @@ pre.prettyprint:focus,
|
|||
border-color: {% scalebyiv theme_secondary 0.90 %} !important;
|
||||
}
|
||||
|
||||
.leaflet-bar a.selected{
|
||||
background-color: {{ theme_button_default }} !important;
|
||||
color: {{ theme_secondary }} !important;
|
||||
}
|
||||
|
||||
.leaflet-popup-content-wrapper{
|
||||
background-color: {{ theme_secondary }} !important;
|
||||
color: {{ theme_primary }} !important;
|
||||
|
|
Ładowanie…
Reference in New Issue