forked from orson/bachemap
188 lines
6.5 KiB
HTML
188 lines
6.5 KiB
HTML
{% extends 'base.html' %}
|
|
{% block head %}
|
|
<title>Take a Photo with Geolocation</title>
|
|
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
|
|
<style>
|
|
body, html {
|
|
margin: 0;
|
|
padding: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
}
|
|
#camera-container {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
#video {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
#canvas {
|
|
display: none;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
#file-fallback {
|
|
display: none;
|
|
position: fixed;
|
|
bottom: 100px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
z-index: 100;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
padding: 10px;
|
|
border-radius: 10px;
|
|
color: black;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div id="camera-container">
|
|
<video id="video" autoplay playsinline></video>
|
|
<canvas id="canvas"></canvas>
|
|
</div>
|
|
|
|
<div style="position: absolute; bottom: 0; width: 100%; display: flex; flex-direction: column; padding: 10px; box-sizing: border-box;">
|
|
<button id="capture-btn" class="control-button" style="margin: 10px;">Tomar foto</button>
|
|
<div style="display: flex; justify-content: space-between; width: 100%; box-sizing: border-box;">
|
|
<button id="retake-btn" class="control-button" style="display: none; margin: 10px 5px 10px 10px; flex: 1; left:20%; background-color: rgba(180,0,0,0.6);">Nueva foto</button>
|
|
<button id="send-btn" class="control-button" style="display: none; margin: 10px 10px 10px 5px; flex: 1;">Enviar foto</button>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<form id="camera-form" action="{{ url_for('camera') }}" method="POST" enctype="multipart/form-data" style="display:none;">
|
|
<input type="hidden" name="latitude" id="latitude" value="">
|
|
<input type="hidden" name="longitude" id="longitude" value="">
|
|
<input type="hidden" name="has_image" id="has_image" value="false">
|
|
|
|
<!-- Fallback file input for unsupported browsers -->
|
|
<div id="file-fallback">
|
|
<label for="photo">Take/Choose a Photo:</label>
|
|
<input type="file" name="photo" id="photo" accept="image/*" capture="environment" required>
|
|
<button type="submit">Upload</button>
|
|
</div>
|
|
</form>
|
|
|
|
<script>
|
|
// Get DOM elements
|
|
const video = document.getElementById('video');
|
|
const canvas = document.getElementById('canvas');
|
|
const captureBtn = document.getElementById('capture-btn');
|
|
const retakeBtn = document.getElementById('retake-btn');
|
|
const sendBtn = document.getElementById('send-btn');
|
|
const form = document.getElementById('camera-form');
|
|
const fallbackInput = document.getElementById('file-fallback');
|
|
const hasImageInput = document.getElementById('has_image');
|
|
|
|
// Set up camera stream
|
|
async function setupCamera() {
|
|
try {
|
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
video: {
|
|
facingMode: 'environment',
|
|
width: { ideal: window.innerWidth },
|
|
height: { ideal: window.innerHeight }
|
|
},
|
|
audio: false
|
|
});
|
|
video.srcObject = stream;
|
|
} catch (err) {
|
|
console.error('Error accessing camera:', err);
|
|
fallbackInput.style.display = 'block';
|
|
}
|
|
}
|
|
|
|
// Initialize camera if supported
|
|
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
setupCamera();
|
|
} else {
|
|
console.error('getUserMedia not supported');
|
|
fallbackInput.style.display = 'block';
|
|
}
|
|
|
|
// Capture photo from video feed
|
|
captureBtn.addEventListener('click', function() {
|
|
const context = canvas.getContext('2d');
|
|
canvas.width = video.videoWidth;
|
|
canvas.height = video.videoHeight;
|
|
context.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
|
|
// Stop camera stream
|
|
video.srcObject.getTracks().forEach(track => track.stop());
|
|
video.style.display = 'none';
|
|
canvas.style.display = 'block';
|
|
|
|
// Update controls
|
|
captureBtn.style.display = 'none';
|
|
retakeBtn.style.display = 'inline-block';
|
|
sendBtn.style.display = 'inline-block';
|
|
|
|
// Mark that we have an image
|
|
hasImageInput.value = 'true';
|
|
});
|
|
|
|
// Retake photo
|
|
retakeBtn.addEventListener('click', function() {
|
|
canvas.style.display = 'none';
|
|
video.style.display = 'block';
|
|
captureBtn.style.display = 'inline-block';
|
|
retakeBtn.style.display = 'none';
|
|
sendBtn.style.display = 'none';
|
|
hasImageInput.value = 'false';
|
|
setupCamera();
|
|
});
|
|
|
|
// Send photo
|
|
sendBtn.addEventListener('click', function() {
|
|
if (hasImageInput.value === 'true') {
|
|
// Convert canvas to blob and append to FormData
|
|
canvas.toBlob(function(blob) {
|
|
const formData = new FormData(form);
|
|
formData.delete('photo'); // Remove any file input value
|
|
formData.append('photo', blob, 'camera-capture.jpg');
|
|
|
|
// Submit form data via fetch
|
|
fetch(form.action, {
|
|
method: 'POST',
|
|
body: formData
|
|
}).then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
} else (response.ok) {
|
|
console.log('Success:', response);
|
|
window.location.href = '{{ url_for('dashboard') }}';
|
|
}
|
|
}).catch(error => console.error('Error:', error));
|
|
}, 'image/jpeg', 0.9);
|
|
}
|
|
});
|
|
|
|
// Request Geolocation Permission and Set Form Fields
|
|
if ('geolocation' in navigator) {
|
|
navigator.geolocation.getCurrentPosition(
|
|
(position) => {
|
|
document.getElementById('latitude').value = position.coords.latitude;
|
|
document.getElementById('longitude').value = position.coords.longitude;
|
|
},
|
|
(error) => {
|
|
console.error('Error getting location:', error);
|
|
},
|
|
{
|
|
enableHighAccuracy: true,
|
|
timeout: 5000
|
|
}
|
|
);
|
|
} else {
|
|
console.error('Geolocation not supported in this browser.');
|
|
}
|
|
</script>
|
|
{% endblock %} |