import React from 'react';
import * as ReactDOM from 'react-dom';
import '../../../css/MonitoredAreaAnnotation.css';
import { createZone } from '../../../../restmodules/ZoneRestModule';
import { API } from '../../../../../LocalConfiguration';
/**
* View annotation
* @extends Component
* @hideconstructor
*/
export class MonitoredAreaViewAnotation extends React.Component {
/**
* @constructs
* @param props
*/
constructor(props) {
super(props);
const loadUrl = API + "/screenshot/" + this.props.monitoredAreaID;
const refreshUrl = API + "/screenshot/" + this.props.monitoredAreaID + "?refresh=true";
let zones = [];
this.props.zonesData.map((zoneData) => {
zones.push(zoneData.cameraVertices);
});
this.state = {
loadUrl: loadUrl,
refreshUrl: refreshUrl,
activeUrl: loadUrl,
loading: true,
spinnerInterval: null,
loaded: false,
zones: zones,
actualZone: {
name: "",
cameraVertices: []
},
image: new Image()
};
this.actualizeScreenshot = this.actualizeScreenshot.bind(this);
this.onCanvasClick = this.onCanvasClick.bind(this);
this.saveActualZone = this.saveActualZone.bind(this);
this.clearActualZone = this.clearActualZone.bind(this);
}
/**
*
*/
actualizeScreenshot() {
if (this.state.loading === true) return;
let context = this.state.canvas.getContext("2d");
context.clearRect(0, 0, this.state.canvas.width, this.state.canvas.height);
this.setState({
loading: true,
loaded: false,
actualZone: {
name: "",
cameraVertices: []
},
activeUrl: this.state.refreshUrl + "&date=" + new Date()
});
this.showSpinner();
}
/**
*
*/
componentDidMount() {
let canvas = ReactDOM.findDOMNode(this.refs.drawCanvas);
let image = ReactDOM.findDOMNode(this.refs.image);
let context = canvas.getContext("2d");
context.lineWidth = 3;
this.setState({
image: image,
canvas: canvas
});
if (this.state.loading === false) context.drawImage(image, 0, 0, canvas.width, canvas.height);
}
/**
*
* @param prevProps
* @param prevState
* @param snapshot
*/
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.zonesData !== this.props.zonesData) {
let zones = [];
this.props.zonesData.map((zoneData) => {
zones.push(zoneData.cameraVertices);
});
this.setState({
zones: zones
});
}
let canvas = this.state.canvas;
let image = this.state.image;
let context = canvas.getContext("2d");
if (this.state.loading === true || this.state.loaded === false) {
return;
}
context.drawImage(image, 0, 0, canvas.width, canvas.height);
context.strokeStyle = 'rgb(255, 134, 20)';
context.fillStyle = 'rgba(255, 152, 56, 0.5)';
if (this.state.zones.length > 0) {
this.state.zones.map(zone => {
let start = this.displayToCanvas(this.imageToDisplay(zone[0]));
context.beginPath();
context.moveTo(start.x, start.y);
for (let i = 1; i < zone.length; ++i) {
let coord = this.displayToCanvas(this.imageToDisplay(zone[i]));
context.lineTo(coord.x, coord.y);
}
context.closePath();
context.fill();
context.stroke();
});
}
let actualZoneVertices = this.state.actualZone.cameraVertices;
context.strokeStyle = 'rgb(0, 255, 0)';
context.fillStyle = 'rgba(0, 255, 0, 0.5)';
if (actualZoneVertices.length === 1) {
let start = this.displayToCanvas(this.imageToDisplay(actualZoneVertices[0]));
context.beginPath();
context.arc(start.x, start.y, 2, 0, 2 * Math.PI);
context.fill();
} else if (actualZoneVertices.length > 1) {
let start = this.displayToCanvas(this.imageToDisplay(actualZoneVertices[0]));
context.beginPath();
context.moveTo(start.x, start.y);
for (let i = 1; i < actualZoneVertices.length; ++i) {
let coords = this.displayToCanvas(this.imageToDisplay(actualZoneVertices[i]));
context.lineTo(coords.x, coords.y);
}
context.closePath();
context.fill();
context.stroke();
}
}
/**
*
* @param event
*/
onCanvasClick(event) {
let actualZoneVertices = this.state.actualZone.cameraVertices.slice();
actualZoneVertices.push(
this.displayToImage({ x: event.nativeEvent.offsetX, y: event.nativeEvent.offsetY })
);
this.setState({
actualZone: {
cameraVertices: actualZoneVertices
}
});
}
handleNameInputChange(event) {
let data = this.state.actualZone;
data.name = event.target.value;
this.setState({
actualZone: data,
});
}
/**
*
*/
onImageLoaded() {
window.clearInterval(this.state.spinnerInterval);
let canvas = ReactDOM.findDOMNode(this.refs.drawCanvas);
let image = ReactDOM.findDOMNode(this.refs.image);
this.setState({
loading: false,
loaded: true,
image: image,
canvas: canvas
});
}
/**
*
*/
onImageError() {
this.setState({
loading: false,
loaded: false
})
}
/**
*
*/
clearActualZone() {
this.setState({
actualZone: {
name: "",
cameraVertices: []
}
});
}
/**
*
*/
saveActualZone() {
console.log(this.state.actualZone);
createZone(this.props.monitoredAreaID, this.state.actualZone)
.then(() => {
this.setState({
actualZone: {
name: "",
cameraVertices: []
}
});
this.props.onZoneAdded();
})
.catch(error => console.log(error));
}
/**
*
* @returns {*}
*/
render() {
return (
<div className="fit-content img-bordered">
{this.renderAddZoneModal()}
<div className="pull-right spacing"
style={{
position: 'relative',
display: 'inline-block'
}}>
<button
type="submit"
className="btn btn-primary"
disabled={this.state.actualZone.cameraVertices.length < 3}
data-toggle="modal"
data-target="#AddModal"
>
<span className="fa fa-plus"/>
</button>
<button
type="submit"
className="btn btn-primary"
onClick={this.actualizeScreenshot}
disabled={this.state.loading === true}
>
<span className="fa fa-repeat"/>
</button>
<button
type="submit"
className="btn btn-primary"
onClick={this.clearActualZone}
>
<span className="fa fa-remove"/>
</button>
</div>
<canvas
ref="drawCanvas"
id="drawCanvas"
onClick={event => this.onCanvasClick(event)}
height={600}
width={700}
style={{
position: 'relative',
}}
/>
<img
id="image"
ref="image"
src={this.state.activeUrl}
alt="monitored area view"
onLoad={() => this.onImageLoaded()}
onError={() => this.onImageError()}
className="hide"
style={{
position: 'relative',
}}
/>
</div>
)
}
renderAddZoneModal() {
console.log('renderAddZoneModal()');
let content =
<form role="form">
<div className="form-group">
<label>Zone Name</label>
<input
type="text"
className="form-control"
placeholder="Name"
name="name"
value={this.state.actualZone['name']}
onChange={(e) => this.handleNameInputChange(e)}
/>
</div>
</form>;
return (
<div className="modal fade"
id="AddModal"
tabIndex="-1"
role="dialog"
aria-labelledby="myModalLabel">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span></button>
<h4 className="modal-title" id="myModalLabel">Enter name of zone</h4>
</div>
<div className="modal-body">
{content}
</div>
<div className="modal-footer">
<button type="button"
className="btn btn-default"
data-dismiss="modal">
Cancel
</button>
<button type="button"
className="btn btn-primary"
data-dismiss="modal"
onClick={() => this.saveActualZone()}
disabled={this.state.actualZone['name'] === undefined || this.state.actualZone['name'].length === 0}
>Update
</button>
</div>
</div>
</div>
</div>
)
}
/**
* converts from displayed canvas coordinates to image coordinates
* @param coords
* @returns {{x: number, y: number}}
*/
displayToImage(coords) {
const widthRatio = this.state.image.width / this.state.canvas.clientWidth;
const heightRatio = this.state.image.height / this.state.canvas.clientHeight;
return { x: Math.round(coords.x * widthRatio), y: Math.round(coords.y * heightRatio) };
}
/**
* converts from image coordinates to displayed canvas coordinates
* @param coords
* @returns {{x: number, y: number}}
*/
imageToDisplay(coords) {
const widthRatio = this.state.image.width / this.state.canvas.clientWidth;
const heightRatio = this.state.image.height / this.state.canvas.clientHeight;
return { x: Math.round(coords.x / widthRatio), y: Math.round(coords.y / heightRatio) };
}
/**
* converts from displayed canvas coordinates to logical canvas coordinates
* @param coords
* @returns {{x: number, y: number}}
*/
displayToCanvas(coords) {
const widthRatio = this.state.canvas.width / this.state.canvas.clientWidth;
const heightRatio = this.state.canvas.height / this.state.canvas.clientHeight;
return { x: coords.x * widthRatio, y: coords.y * heightRatio };
}
/**
* code source: https://codepen.io/reneras/pen/HFrmC
* edited size of spinner
*/
showSpinner() {
const canvas = ReactDOM.findDOMNode(this.refs.drawCanvas);
const context = canvas.getContext('2d');
const start = new Date();
const lines = 16,
cW = context.canvas.width,
cH = context.canvas.height;
let draw = function () {
let rotation = parseInt(((new Date() - start) / 1000) * lines) / lines;
context.save();
context.clearRect(0, 0, cW, cH);
context.translate(cW / 2, cH / 2);
// context.translate(cW / 2, cH / 2);
context.rotate(Math.PI * 2 * rotation);
for (let i = 0; i < lines; i++) {
context.beginPath();
context.rotate(Math.PI * 2 / lines);
// context.moveTo(cW / 10, 0);
// context.lineTo(cW / 4, 0);
// context.lineWidth = cW / 30;
context.moveTo(cW / 30, 0);
context.lineTo(cW / 12, 0);
context.lineWidth = cW / 90;
context.strokeStyle = "rgba(0, 0, 0," + i / lines + ")";
context.stroke();
}
context.restore();
};
this.state.spinnerInterval = window.setInterval(draw, 1000 / 30);
}
}