1
0
forked from orson/bachemap
bachemap/templates/camera.html

258 lines
9.8 KiB
HTML
Raw Permalink Normal View History

{% 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;
}
#location-status {
position: fixed;
top: 10px;
right: 10px;
background: rgba(0, 0, 0, 0.7);
color: white;
padding: 5px 10px;
border-radius: 5px;
z-index: 100;
}
</style>
{% endblock %}
{% block content %}
<div id="camera-container">
<video id="video" autoplay playsinline></video>
<canvas id="canvas"></canvas>
</div>
<div id="location-status">Esperando ubicación...</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; right:20%"><i class="fa fas fa-camera"></i></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(231, 9, 9, 0.671);color:white; left:20%"><i class="fas fa-redo"></i></button>
<button id="send-btn" class="control-button" style="display: none; margin: 10px 10px 10px 5px; flex: 1; background-color: rgba(49, 182, 28, 0.6);color:white; right: 20%;" disabled><i class="fas fa-paper-plane"></i></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">
<div id="file-fallback">
<label for="photo">Toma o selecciona una foto:</label>
<input type="file" name="photo" id="photo" accept="image/*" capture="environment" required>
<button type="submit">Subir</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');
const locationStatus = document.getElementById('location-status');
// Location tracking variables
let hasLocation = false;
let watchId = null;
// 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';
}
// Check if we can enable the send button
function updateSendButtonState() {
if (hasImageInput.value === 'true' && hasLocation) {
sendBtn.disabled = false;
sendBtn.style.opacity = '1';
} else {
sendBtn.disabled = true;
sendBtn.style.opacity = '0.5';
}
}
// 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';
// Check if we can enable send button
updateSendButtonState();
});
// Retake photo
retakeBtn.addEventListener('click', function() {
canvas.style.display = 'none';
video.style.display = 'block';
captureBtn.style.display = 'flex';
retakeBtn.style.display = 'none';
sendBtn.style.display = 'none';
hasImageInput.value = 'false';
setupCamera();
});
// Send photo
sendBtn.addEventListener('click', function() {
if (hasImageInput.value === 'true' && hasLocation) {
// 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
// Generate a filename with timestamp and random element
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const randomId = Math.floor(Math.random() * 10000);
const filename = `photo-${timestamp}-${randomId}.jpg`;
// Add the photo blob with the dynamic filename
formData.append('photo', blob, filename);
// Submit form data via fetch
fetch(form.action, {
method: 'POST',
body: formData
}).then(response => {
if (response.redirected) {
window.location.href = response.url;
} else if(response.ok) {
2025-03-15 00:00:31 +00:00
console.log('Success:', response);
window.location.href = '{{ url_for('dashboard') }}';
}
}).catch(error => console.error('Error:', error));
}, 'image/jpeg', 0.9);
} else if (!hasLocation) {
alert('Esperando ubicación GPS. Por favor espera.');
}
});
// Request Geolocation Permission and Set Form Fields
if ('geolocation' in navigator) {
locationStatus.textContent = 'Obteniendo ubicación...';
locationStatus.style.backgroundColor = 'rgba(255, 165, 0, 0.7)'; // Orange
// Start watching position for more accurate results
watchId = navigator.geolocation.watchPosition(
(position) => {
document.getElementById('latitude').value = position.coords.latitude;
document.getElementById('longitude').value = position.coords.longitude;
hasLocation = true;
locationStatus.textContent = 'Ubicación: ✓';
locationStatus.style.backgroundColor = 'rgba(0, 128, 0, 0.7)'; // Green
updateSendButtonState();
// Once we get a good reading, we can stop watching
if (position.coords.accuracy < 100) { // If accuracy is under 100 meters
navigator.geolocation.clearWatch(watchId);
}
},
(error) => {
console.error('Error getting location:', error);
locationStatus.textContent = 'Error de ubicación';
locationStatus.style.backgroundColor = 'rgba(255, 0, 0, 0.7)'; // Red
// Try getting location once more with different options
navigator.geolocation.getCurrentPosition(
(position) => {
document.getElementById('latitude').value = position.coords.latitude;
document.getElementById('longitude').value = position.coords.longitude;
hasLocation = true;
locationStatus.textContent = 'Ubicación: ✓';
locationStatus.style.backgroundColor = 'rgba(0, 128, 0, 0.7)'; // Green
updateSendButtonState();
},
(error) => {
console.error('Final error getting location:', error);
locationStatus.textContent = 'Error de ubicación';
},
{ maximumAge: 60000, timeout: 10000 }
);
},
{
enableHighAccuracy: true,
timeout: 10000
}
);
} else {
console.error('Geolocation not supported in this browser.');
locationStatus.textContent = 'GPS no soportado';
locationStatus.style.backgroundColor = 'rgba(255, 0, 0, 0.7)'; // Red
}
</script>
{% endblock %}